How to get the size of an assembly instance? I do not know the size of the assembly because it has no geometry.
The assembly location is not necessarily the geometric centre of all the elements of the assembly, I wish but not the case.
Any idea without crashing dynamo to resolve this? Do I need to take the geometry of the elements in the assembly to know this or can I query just the assembly elements?
thanks for the response, I already took the bounding box of all the assembly elements and get the bounding box of each and combine and get the centroid of the combined bounding box and the point that I get is identical than the assembly instance element Location.
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import BoundingBoxXYZ, XYZ
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import BoundingBox, Point
doc = DocumentManager.Instance.CurrentDBDocument
assemblies = UnwrapElement(IN[0])
union_bounding_box = None
for assembly_instance in assemblies:
memberslist = assembly_instance.GetMemberIds()
for member_id in memberslist:
element = doc.GetElement(member_id)
bounding_box = element.get_BoundingBox(None)
# If this is the first bounding box, use it to initialize the union bounding box
if union_bounding_box is None:
union_bounding_box = bounding_box
continue
# Extend the union bounding box to include this element's bounding box
union_bounding_box.Min = XYZ(
min(union_bounding_box.Min.X, bounding_box.Min.X),
min(union_bounding_box.Min.Y, bounding_box.Min.Y),
min(union_bounding_box.Min.Z, bounding_box.Min.Z)
)
union_bounding_box.Max = XYZ(
max(union_bounding_box.Max.X, bounding_box.Max.X),
max(union_bounding_box.Max.Y, bounding_box.Max.Y),
max(union_bounding_box.Max.Z, bounding_box.Max.Z)
)
# Convert the Revit bounding box to Dynamo bounding box
min_point = union_bounding_box.Min.ToPoint()
max_point = union_bounding_box.Max.ToPoint()
# Calculate the centroid of the bounding box
centroid_x = (min_point.X + max_point.X) / 2
centroid_y = (min_point.Y + max_point.Y) / 2
centroid_z = (min_point.Z + max_point.Z) / 2
centroid_point = Point.ByCoordinates(centroid_x, centroid_y, centroid_z)
# Output the centroid point
OUT = centroid_point
displaying the geometry in Revit 3D view you can see what is the bounding box of the combined bounding boxes of the assembly elements, the centroid is highlighted with a sphere, although you can imagine the geometry of the elements within the assembly because they are the blue points and lines, so for me that does not make sense, the centroid should be more in the centre to the left of the image instead.
centroids = [] #empty list to hold the assemblies
for assembly in assemblies: #for each assembly
memberIds = assembly.GetMemberIds() #get the member ids
members = [doc.GetElement(i) for i in memberIds] #get the members
geo = [i.get_Geometry(Options()) for i in members] #get the goemetry
solids = [i for geoset in geo for i in geoset if i.__class__ == Autodesk.Revit.DB.Solid] #filter out non-solids and returna flat list
solid = solids.pop() #get one solid from the list
for s in solids: #for each remaining solid in the list of solids
solid = BooleanOperationsUtils.ExecuteBooleanOperation (solid,s,BooleanOperationsType.Union) #merge each solid in sequence
centroid = solid.ComputeCentroid() #compute the centroid of the unioned solid
centroids.append(centroid.ToPoint()) #append the centroid to the list of centroids
bloody Revit why so complicated, I modified the python script, now makes sense you can see the difference of centroid point between using bounding boxes of assembly elements and solid geometry of assembly elements, but the geometry of some pipe element is wrong therefore the combined bounding box size or combined solid size is not correct.
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import BooleanOperationsUtils, BooleanOperationsType, Options, Solid
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
assemblies = UnwrapElement(IN[0])
centroids = [] # Empty list to hold the centroids
for assembly in assemblies: # For each assembly
memberIds = assembly.GetMemberIds() # Get the member ids
members = [doc.GetElement(i) for i in memberIds] # Get the members
geo = [i.get_Geometry(Options()) for i in members] # Get the geometry
solids = [i for geoset in geo for i in geoset if i.__class__ == Solid] # Filter out non-solids and return a flat list
solid = solids.pop() # Get one solid from the list
for s in solids: # For each remaining solid in the list of solids
solid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, s, BooleanOperationsType.Union) # Merge each solid in sequence
centroid = solid.ComputeCentroid() # Compute the centroid of the unioned solid
centroids.append(centroid.ToPoint()) # Append the centroid to the list of centroids
OUT = centroids
I modified the script again to also show me the dynamo geometry of the elements; the mark-up of the screenshot belongs to a pipe that was originally split by a pipe accessory but it seems Revit is considering the geometry of the pipe is full and not split, because the right split of the pipe which has other Revit element ID is not inside of the assembly but it appears like if the geometry of it was part of the original pipe without accessories.
So if there is a one pipe modelled and it is split by a revit family of pipe accessory, revit tells me there are 2 pipe elements, but what I see is that one of them has geometry like the full pipe and the other part split has the geometry that you actually can see and measure which is correct, but the other side is not correct.
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import BooleanOperationsUtils, BooleanOperationsType, Options, Solid
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
assemblies = UnwrapElement(IN[0])
result = [] # Empty list to hold the combined solids and centroids
for assembly in assemblies: # For each assembly
memberIds = assembly.GetMemberIds() # Get the member ids
members = [doc.GetElement(i) for i in memberIds] # Get the members
geo = [i.get_Geometry(Options()) for i in members] # Get the geometry
solids = [i for geoset in geo for i in geoset if i.__class__ == Solid] # Filter out non-solids and return a flat list
solid = solids.pop() # Get one solid from the list
for s in solids: # For each remaining solid in the list of solids
solid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, s, BooleanOperationsType.Union) # Merge each solid in sequence
centroid = solid.ComputeCentroid().ToPoint() # Compute the centroid of the unioned solid and convert to Dynamo point
result.append((solid.ToProtoType(), centroid)) # Append the combined solid and centroid to the result list
OUT = result
Might be resolvable via the geometry options, or another bit. Hard to say without a model and I don’t have time to dig into it even if I did.
My recommendation is to focus on getting the geometry as you’d expect it to be returned first, instead of solving for all the things at once. You know how to get the size of an assembly, it’s now a matter of getting the right stuff into the assembly. Note that part of this may be due to an error in how the assembly was originally created, so that edge case may have to be handled independently via an error handling method of your choice.
I opted to ignore pipes and pipe insulations so result that I get with bounding boxes is acceptable, the pipe gives trouble with bounding boxes because if the original pipe is split in piece, one of the part always remembers the original length size even it is split in small piece. I took the first option script but ignoring elements of categories Pipes and Pipe Insulations.