Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Family BoundingBox in Family Document

16 REPLIES 16
Reply
Message 1 of 17
FrankHolidaytoiling
2520 Views, 16 Replies

Family BoundingBox in Family Document

How can I acquire the bounding box or equivalent in a family doc, just to calrify I am not after the element bounding box in the revit doc! How does one get the bounding box of types?

16 REPLIES 16
Message 2 of 17

I just answered your identical question on StackOverflow:

 

http://stackoverflow.com/questions/38012345/revit-bounding-box-in-familydoc/38017623#38017623

 

 

Of course you cannot get the geometry of a family type. A family type is an abstract concept, basically just a collection of dimension values. The only concrete object owning geometry is the family instance inserted into a project.

 

Within the family document, you can determine the bounding box by iterating over all the elements inside the family definition, which might be solids of various kinds, cylinders, extrusions, etc., and summing up all their respective bounding boxes.

 

For an example of how that can easily be done, take a look at the ExpandToContain method in

 

http://thebuildingcoder.typepad.com/blog/2013/04/geosnoop-net-boundary-curve-loop-visualisation.html

  

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 3 of 17
kelau1993
in reply to: jeremytammik

The ExpandToContain seems to not work for void elements? How do I go about finding the bounding box for a family that includes void and non-void elements?

 

The problem I am trying to solve is to programmatically create two reference planes that defines the origin. I want to put these planes on the lower left corner of the family as seen from the Ref. Level. Creating the reference planes and setting them to define origin is easy enough.

 

The problem is with finding where to place the reference planes. Is there a way to merge all of the elements into one single non-void element that represents the whole object? Revit UI is able to do this easily when it snaps the reference plane placer to points that exist on the final object (taking into account void and non-void elements). Is the only way to do this to look through all elements and find some algorithm to see if a vertex/edge exists on the final object and then find the leftmost/bottommost one?

Message 4 of 17
jeremytammik
in reply to: kelau1993

Look at this recent thread:

 

 

http://forums.autodesk.com/t5/revit-api-forum/how-to-get-a-solids-location-in-the-project/m-p/685189...

 

It refers to the SetoutPoints sample:

 

https://github.com/jeremytammik/SetoutPoints

 

What does RevitLookup say when you explore the geometry of a void?



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 5 of 17
kelau1993
in reply to: jeremytammik

Thanks for replying, Jeremy.

 

Just some background information - I've been programming for a couple years but I am new to Revit.

 

To clarify, I am talking about the void and non-void "forms" under the Create tab of Revit. https://snag.gy/c6gfvs.jpg

 

The void forms cut away from the non-void forms.

 

When using RevitLookup on a void, I think it's the same as a solid except that it has "Is solid" marked to false. This is also in the api docs http://www.revitapidocs.com/2016/8d62241f-2efe-6581-4a70-83a0be66413c.htm

 

I looked at some of the SetoutPoints code but it seems like it just finds all vertices of all solids and places a marker on them? I don't need the corners of each individual solid but I need the corners of the final solid, taking into account all void and solid (non-void) geometries. If I have the corners of the final solid, I can easily find out what is the one that is the lowest and most left. So in the RevitLookup picture above, I don't need the vertices of each individual form, but I the corners of the merged solid (as if it was only one solid and not a combination of solids and voids).

 

There are two ways I can think of doing this. One way is if there is a simple way in Revit to merge all forms (void or solid) into a single solid form, I can then find the vertex that is the lowest and most left by looping through all vertices.

If that is not possible, I would need some algorithm that looks through all forms and finds out the vertices of the merged solid and then find the one that is lowest and most left. This would be easy if all forms were solid, because all vertices actually exist. But it's harder with void forms because they cut away at solids and can create new vertices where an edge exists, and a vertex may or may not exist depending on what gets voided out.

 

Here is a basic example I drew in microsoft paint (haha):

The red box is void and the black one is solid. I want to select the green vertex as lower left. The black box with the red face on the bottom is the final, merged solid. Notice how the green vertex is not actually any vertex on the individual forms. Notice how the vertex gets formed on the edge of the black solid.

This example does not cover all cases. Imagine a cookie cutter type example, where a void form with a hole in it shapes a solid by cutting out the excess solid. In this case, the vertex would be formed on the void form edge (instead of the solid form edge like above).

 

I tried a little at thinking of an algorithm but I'm not sure if there is a simpler way with the API. So far, I only thought of a 2D algorithm (not 3D), but it's still not complete and I would have to be able to tell if a point is inside a polygon - http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/. In 3D, I would have to be able to tell if a point is inside a polyhedron (a geometry or void/solid form). Not sure if Revit API can help me with this.

Message 6 of 17
kelau1993
in reply to: kelau1993

I actually made a minor mistake though this doesn't affect objects such as boxes that have a lower left corner. And this shouldn't affect having to find the final merged solid. I actually want the lowest vertex and the most left vertex from the ref. level view.

 

An example is this star (imagine it is a star prism in a ref. level (top down) view):

I want the origin where the blue planes (uses 2 vertices - lowest one and most left one) line up and not the red ones (uses one vertex - one that is lowest and most left).

Message 7 of 17
FAIR59
in reply to: kelau1993

combinations of solids and voids are a new Element in the revit database:  GeomCombination

 

the boundingbox of the GeomCombination is the boundingbox of the merged solids ( the truncated beam in your drawing).

 

GeomCombination.AllMembers  is an array of the merged Elements (solids and voids) that are part of the GeomCombination.

 

workflow:

 

find all geomcombinations,

     List<Element> _combinations = new FilteredElementCollector(doc)

             .OfClass(typeof(GeomCombination))

             .ToList();

make list of the solids in the geomcombination.

find all solids, excluding those in the geomcombinations.

 

Message 8 of 17

I really have absolutely no certainty, but I can imagine that just scanning all the vertices and determining the max and min from those might be 1000 times more efficient than actually combining all the solids first and then asking the Boolean union for its bounding box.



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 9 of 17
kelau1993
in reply to: FAIR59

Thank you. I looked at GeomCombinations in the DB and it looks like what I need.

 

Like you said, I will have to look for the lowest vertex and the leftmost vertex in the Bounding box of the GeomCombinations (if the bounding box has a volume >0). I can then look for solids that aren't part of a GeomCombination and get their bottommost and leftmost vertices of their bounding boxes. I can ignore void forms that are not part of a GeomCombination because they do not exist in the final family object. With the bottommost/leftmost for GeomCombinations and individual solids, I can find the bottommost/leftmost for the entire family.

 

I will dig into this deeper and post a solution when I find one. Or come back and ask more questions if I get stuck. Thanks again!

Message 10 of 17
kelau1993
in reply to: jeremytammik

I can't scan through all vertices and determine the max and min because some vertices may not actually exist in the family if they are voided out, some new vertices are created on an existing edge. I would need some way to determine if that vertex will be part of the final family object and some way to determine if a new vertex is formed on an edge. For example, in this image I posted earlier, the green vertex doesn't exist on any individual solid or void, but it exists in the combination of the solid and the void (and in the family as a whole too). Also, the purple vertex is gone from the combination (and the family).

 

Though I don't think I have to worry about this. The family files I am dealing with already combine solids and voids as needed into GeomCombinations like FAIR59 said, so my code will take no performance penalty from that. I was unaware that the DB had this information and thought I had to somehow combine them myself using the Revit API. This doesn't cover solids that aren't part of a combination but that should be easy to handle as explained in my previous post.

Thanks for the help!

Message 11 of 17
kelau1993
in reply to: kelau1993

 

Here is my solution to compute the family bounding box. Reference planes can be placed along the edges of the bounding box.

/// <summary>
/// Computes the effective 'BoundingBoxXYZ' of the whole family,
/// considering all combined and individual forms.
/// </summary>
/// <param name="document">The Revit document that contains the
/// family.</param>
/// <returns>The effective 'BoundingBoxXYZ' of the whole family.</returns>
private static BoundingBoxXYZ ComputeFamilyBoundingBoxXyz(Document document)
{
    BoundingBoxXYZ familyBoundingBoxXyz = null;

    HashSet<ElementId> genericFormExclusionSet = new HashSet<ElementId>();

    familyBoundingBoxXyz =
	MergeGeomCombinationBoundingBoxXyz(document,
					   familyBoundingBoxXyz,
					   genericFormExclusionSet);

    familyBoundingBoxXyz = MergeSolidBoundingBoxXyz(document,
						    familyBoundingBoxXyz,
						    genericFormExclusionSet);

    return familyBoundingBoxXyz;
}

/// <summary>
/// Merge <paramref name="boundingBoxXyz"/> with the 'BoundingBoxXYZ's of
/// all 'GeomCombination's in <paramref name="document"/> into a new
/// 'BoundingBoxXYZ'. Collect all members of the 'GeomCombination's
/// found into <paramref name="geomCombinationMembers"/>.
/// </summary>
/// <param name="document">The Revit 'Document' to search for all
/// 'GeomCombination's.</param>
/// <param name="boundingBoxXyz">The 'BoundingBoxXYZ' to merge with.</param>
/// <param name="geomCombinationMembers">A 'HashSet' to collect all of the
/// 'GeomCombination' members that form the 'GeomCombination'.</param>
/// <returns>The new merged 'BoundingBoxXYZ' of
/// <paramref name="boundingBoxXyz"/> and all 'GeomCombination's
/// in <paramref name="document"/></returns>
private static BoundingBoxXYZ MergeGeomCombinationBoundingBoxXyz(Document document,
								 BoundingBoxXYZ boundingBoxXyz,
								 HashSet<ElementId> geomCombinationMembers)
{
    BoundingBoxXYZ mergedResult = boundingBoxXyz;

    FilteredElementCollector geomCombinationCollector =
	new FilteredElementCollector(document);
    geomCombinationCollector.OfClass(typeof(GeomCombination));
    foreach (GeomCombination geomCombination in geomCombinationCollector)
    {
	if (geomCombinationMembers != null)
	{
	    foreach (GenericForm genericForm in geomCombination.AllMembers)
	    {
		geomCombinationMembers.Add(genericForm.Id);
	    }
	}

	BoundingBoxXYZ geomCombinationBoundingBox = geomCombination.get_BoundingBox(null);

	if (mergedResult == null)
	{
	    mergedResult = new BoundingBoxXYZ();
	    mergedResult.Min = geomCombinationBoundingBox.Min;
	    mergedResult.Max = geomCombinationBoundingBox.Max;
	    continue;
	}

	mergedResult = MergeBoundingBoxXyz(mergedResult,
					   geomCombinationBoundingBox);
    }

    return mergedResult;
}

/// <summary>
/// Merge <paramref name="boundingBoxXyz"/> with the 'BoundingBoxXYZ's of
/// all 'GenericForm's in <paramref name="document"/> that are solid into
/// a new 'BoundingBoxXYZ'.
/// Exclude all 'GenericForm's in
/// <paramref name="genericFormExclusionSet"/> from being found in
/// <paramref name="document"/>.
/// </summary>
/// <param name="document">The Revit 'Document' to search for all
/// 'GenericForm's excluding the ones in
/// <paramref name="genericFormExclusionSet"/>.</param>
/// <param name="boundingBoxXyz">The 'BoundingBoxXYZ' to merge with.</param>
/// <param name="genericFormExclusionSet">A 'HashSet' of all the
/// 'GenericForm's to exclude from being merged with in
/// <paramref name="document"/>.</param>
/// <returns>The new merged 'BoundingBoxXYZ' of
/// <paramref name="boundingBoxXyz"/> and all 'GenericForm's excluding
/// the ones in <paramref name="genericFormExclusionSet"/>
/// in <paramref name="document"/></returns>
private static BoundingBoxXYZ MergeSolidBoundingBoxXyz(Document document,
						       BoundingBoxXYZ boundingBoxXyz,
						       HashSet<ElementId> genericFormExclusionSet)
{
    BoundingBoxXYZ mergedResult = boundingBoxXyz;

    FilteredElementCollector genericFormCollector =
	new FilteredElementCollector(document);
    genericFormCollector.OfClass(typeof(GenericForm));
    if (genericFormExclusionSet != null &&
	genericFormExclusionSet.Any())
    {
	genericFormCollector.Excluding(genericFormExclusionSet);
    }
    foreach (GenericForm solid in genericFormCollector
				  .Cast<GenericForm>()
				  .Where(genericForm => genericForm.IsSolid))
    {
	BoundingBoxXYZ solidBoundingBox = solid.get_BoundingBox(null);

	if (mergedResult == null)
	{
	    mergedResult = new BoundingBoxXYZ();
	    mergedResult.Min = solidBoundingBox.Min;
	    mergedResult.Max = solidBoundingBox.Max;
	    continue;
	}

	mergedResult = MergeBoundingBoxXyz(mergedResult,
					   solidBoundingBox);
    }

    return mergedResult;
}

/// <summary>
/// Merge <paramref name="boundingBoxXyz0"/> and
/// <paramref name="boundingBoxXyz1"/> into a new 'BoundingBoxXYZ'.
/// </summary>
/// <param name="boundingBoxXyz0">A 'BoundingBoxXYZ' to merge</param>
/// <param name="boundingBoxXyz1">A 'BoundingBoxXYZ' to merge</param>
/// <returns>The new merged 'BoundingBoxXYZ'.</returns>
private static BoundingBoxXYZ MergeBoundingBoxXyz(BoundingBoxXYZ boundingBoxXyz0,
						  BoundingBoxXYZ boundingBoxXyz1)
{
    BoundingBoxXYZ mergedResult = new BoundingBoxXYZ();

    mergedResult.Min = new XYZ(Math.Min(boundingBoxXyz0.Min.X, boundingBoxXyz1.Min.X),
			       Math.Min(boundingBoxXyz0.Min.Y, boundingBoxXyz1.Min.Y),
			       Math.Min(boundingBoxXyz0.Min.Z, boundingBoxXyz1.Min.Z));

    mergedResult.Max = new XYZ(Math.Max(boundingBoxXyz0.Max.X, boundingBoxXyz1.Max.X),
			       Math.Max(boundingBoxXyz0.Max.Y, boundingBoxXyz1.Max.Y),
			       Math.Max(boundingBoxXyz0.Max.Z, boundingBoxXyz1.Max.Z));

    return mergedResult;
}

 

Message 12 of 17
jeremytammik
in reply to: kelau1993

Dear Kevin,

 

Thank you very much for sharing this solution.

 

Looks very cool indeed to me.

 

In fact, I went ahead and created a complete Visual Studio project and GitHub repository for it to make it more accessible for people:

 

https://github.com/jeremytammik/FamilyBoundingBox

 

Here is the direct link to your code:

 

https://github.com/jeremytammik/FamilyBoundingBox/blob/master/FamilyBoundingBox/Command.cs

 

I hope that suits your intentions.

 

I imagine that some enhancements can be added, but the main principles are absolutely clear and solid.

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 13 of 17
jeremytammik
in reply to: jeremytammik

Added this to today's blog post as well:

 

http://thebuildingcoder.typepad.com/blog/2017/03/family-bounding-box-and-aec-hackathon-munich.html#3

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 14 of 17
kelau1993
in reply to: jeremytammik

Hey Jeremy,

 

You're welcome. Thanks for creating the repo and posting it on your blog. Sharing the solution definitely suits my intentions.

Message 15 of 17
benporter
in reply to: kelau1993

Hi Kelau/ Jeremy,

 

Firstly thank you for providing your solution and workings for what is quite a tricky problem. I'd be interested to hear your thoughts on how the process might be developed to take into account types within a family document, perhaps nested families also.

 

Thanks,

Ben

Message 16 of 17
jeremytammik
in reply to: benporter

You can easily loop through all the family types, make them current, one after the other, and increase the bounding box successively while doing that, or calculate a new bounding box for each type.

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 17 of 17
kelau1993
in reply to: benporter

@benporter, I haven't worked on Revit development for years now.

 

Your questions:

1. How to calculate bbox for family types within a family document?

2. How to calculate bbox for nested families?

 

I don't remember exactly, but aren't nested families and family types within a family document both still just a nested family?

A while back I added support for family instances to be added in the bbox calculation for a family doc. See:

https://github.com/jeremytammik/FamilyBoundingBox/pull/1

 

family instances are families placed within a family doc. I think nested families, whether they are a family type or not, count as family instances.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community