Hi,
I am using Revit API 2015 and Visual Studio 2013.
I want to get the total width (along x-axis) and the total depth/length (along y-axis) of the given design. Does anyone know what would be the best way to achieve it using Revit API.
Any suggestions will be much appreciated.
Many Thanks and Kind Regards
Bilal
Dear Bilal,
The number of possible approaches is infinite.
I would probably start off by computing the convex hull of the entire design, and then determine the width and length of that:
http://thebuildingcoder.typepad.com/blog/2009/06/convex-hull-and-volume-computation.html
Another approach, which may or may not be faster, depending on many factors:
Skip the convex hull part and just iterate over all building elements, enlarging a bounding box containing all their vertices as you go along:
bounding box B = (+infinity, -infinity) for all elements for all element geometry vertices P if P < B.min: B.min = P if P > B.max: B.max = P
Another approach, definitely faster than the last:
Use the element bounding box vertices instead of all the element geometry vertices.
All of these approaches would make very nice little samples.
There are certainly more ways to approach this task.
I have a strong personal tendency towards the pure geometric.
Implement them and provide the code and a couple of examples designs to test them on, including extreme cases, and I'll clean it up and publish it for you on The Building Coder.
Cheers,
Jeremy
Funnily enough, a very similar question was raised shortly after by another developer:
Question: Is there a built-in method to get a bounding box of the entire model, similar to the method Element.get_BoundingBox?
The only way I see right now is to use IExportContext, go through all the visible elements and get the minimum and maximum coordinates among the all points. But on the large models this method may take a while.
Is there some faster method?
Answer: Yes, maybe.
As I already suggested above, there are a number of possibilities:
http://thebuildingcoder.typepad.com/blog/2009/06/convex-hull-and-volume-computation.html
bounding box B = (+infinity, -infinity) for all elements for all element geometry vertices P if P < B.min: B.min = P if P > B.max: B.max = P
One aspect that I did not mention that makes a huge difference for large models:
The bounding box of all elements is immediately available from the element header information, and thus corresponds to a quick filtering method, whereas all the approaches that require the full geometry correspond to a slow filter.
Using a custom exporter, like you suggest, is also a good idea, and corresponds to a slow filter.
If you are interested in good performance in large models, I would aim for a quick filter method, e.g., like this:
public static class JtBoundingBoxXyzExtensionMethods { /// <summary> /// Expand the given bounding box to include /// and contain the given point. /// </summary> public static void ExpandToContain( this BoundingBoxXYZ bb, XYZ p ) { bb.Min = new XYZ( Math.Min( bb.Min.X, p.X ), Math.Min( bb.Min.Y, p.Y ), Math.Min( bb.Min.Z, p.Z ) ); bb.Max = new XYZ( Math.Max( bb.Max.X, p.X ), Math.Max( bb.Max.Y, p.Y ), Math.Max( bb.Max.Z, p.Z ) ); } /// <summary> /// Expand the given bounding box to include /// and contain the given other one. /// </summary> public static void ExpandToContain( this BoundingBoxXYZ bb, BoundingBoxXYZ other ) { bb.ExpandToContain( other.Min ); bb.ExpandToContain( other.Max ); } } #region Get Model Extents /// <summary> /// Return a bounding box enclosing all model /// elements using only quick filters. /// </summary> BoundingBoxXYZ GetModelExtents( Document doc ) { FilteredElementCollector quick_model_elements = new FilteredElementCollector( doc ) .WhereElementIsNotElementType() .WhereElementIsViewIndependent(); IEnumerable<BoundingBoxXYZ> bbs = quick_model_elements .Where<Element>( e => null != e.Category ) .Select<Element,BoundingBoxXYZ>( e => e.get_BoundingBox( null ) ); return bbs.Aggregate<BoundingBoxXYZ>( ( a, b ) => { a.ExpandToContain( b ); return a; } ); } #endregion // Get Model Extents
I implemented that for you and included it in The Building Coder samples release 2017.0.127.6:
https://github.com/jeremytammik/the_building_coder_samples/releases/tag/2017.0.127.6
Response: So, in fact I was right and the only method is to get this information from the geometry.
I’ll benchmark with CustomExport (as I suggested) and FilteredElementCollector (as you did) with large models and let you know.
I think your way should be faster, but not correct in some cases:
Case 1) Linked models. It doesn’t consider linked models if they are.
Case 2) Even if we extend this method and get the elements geometry from the linked models, we need to transform the coordinates from linked model to the hosted model, as BoundingBox returns the coordinates in a source model.
Case 3) Your method returns all the elements. In my case I need only the view specific Model BoundingBox. Yes, we can use FilteredElementCollector with ActiveView, but it can be a section view or 3d view bounded by section box.
Therefore, FilteredElementCollector should be faster, but needs to consider all the cases (maybe there are more than I mentioned). As CustomExporter iterate only the visible geometry, exactly like it presented on the view, this way is more reliable.
Anyway, the benchmark will show the results ☺
Cheers,
Jeremy
Dear Bilal,
Thank you for your query.
I summarised, cleaned up and published our conversation on The Building Coder:
http://thebuildingcoder.typepad.com/blog/2016/08/vacation-end-forge-news-and-bounding-boxes.html#8
Cheers,
Jeremy