Define center of Gravity for complicated element

Hello guys,
I’m new to Dynamo. I have an issue need you guys help.
I try to use these nodes to define centroid point of this element.
But it’s not working. It works with simple element.
Do you guys have any solution?
Thanks a lot in advance!

.

Hello @bauconvietnam1 …you could try with a bounding box…

Hello @sovitek … could you please explain more clearly?

Bounding box isn’t going to find the center of mass.
And I don’t think that is something inherent to anything in Revit. You’re going to need some external libraries. Maybe this will help:
https://trimsh.org/trimesh.html

Or dump the shape out to Fusion 360 and use that to find the center of mass if this is a one off sort of thing.

Instead of Element.Solids - use Element.Geometry. Then filter out any non-solid geometry. Once you have a list of just solids - then you have to make some decisions.

  1. Is the element only one material (meaning it all has same material density). This is key because if not - the centroids wont provide you the real C.G. They will only provide you the volume based centroid - 10 cu ft of steel beside 10 cu ft of styrofoam might provide you a centroid dead center between the two - but the center of gravity would basically be the centroid of the steel because the styrofoam weighs nearly nothing

  2. Does the element have overlapping solids? If so - then you must do the Solid.Union (again - assuming it’s all one material). If it is multi-material and has overlapping solids - then you need to divide up the list of the solids first into separate material-based lists and union each of those separately. Sometimes Solid.Union will fail for no apparent reason - so the best suggestion is to avoid using it and build the element without overlaps. I personally always avoid Unions because of this.

  3. You can use design script or python or vanilla dynamo to find the centroid and volume (if single material) or weight (if multi-material) of each indv solid then get a weighted sum of all the indv centroids based on weight or volume to get the real C.G.

This process works (but it can be complicated) - I use it all the time on 1 million pound + trusses and heavy precast concrete pieces to design rigging and lifts.

1 Like

Hey Ben, can you provide additional information on your method #3? My company also does very complex models, with a lot of returns and voided areas and trying to get the centroid of those is a pain. Below is what I ultimately came up with that has handled a lot of our cases, but there have been some that it has failed on. As shown I incorporated two methods and return whichever method works. I would like to get a more full proof way of getting the centroid, because I use this method internally on another script that places handling in panels. Thanks for any input you can provide!

1 Like

A lot of how you handle it depends on your geometry source - i see you are dealing with conversions from Mesh to Solid and that is always going to be a “Hope and Pray” because I have used a very similar method when dealing with meshes and it works 75% of the time (because sometimes the meshes aren’t watertight and cant make a solid.)

At the end of your method you are probably ending up with some list of solids and potentially other geometry primitives (Element.Geometry may return Solids, Meshes, Points, Curves, Surfaces). What i’d suggest is testing the data out of the Element.Geometry (String.FromObject - then test if the Strings = “Solid” and Filter By Bool Mask to get only the solids) that way when you get to Solid.Centroid you are only dealing with Solids coming in.

Once you have only a list of Solids (assuming Single Material) then along with Solid .Centroid you also need to do Solid.Volume to get the contribution of each indv centroid to the overall centroid/C.G. Then what i do is get the X,Y,Z coordinates of each centroid point. then to get the overall Centroid X coordinate - its just a matter of taking each X coordinate and multiplying it by it’s corresponding Volume. Take the sum of all the Coordinates x Volumes and divide by the Total Volume of Everything (the sum of all the Volumes) - that would be the overall centroid X coordinate. Repeat for Y and Z. Then feed the resultant X, Y, Z into a Point.ByCoordinates and then feed that point into your Family Placement node

Haven’t had a chance to look at it too closely, but this looks promising
https://pypi.org/project/numpy-stl/

Seems promising if you are dealing with .stl files and working in a newer version of Revit and can import numpy…if in Revit you are likely dealing in Solids and Meshes so seems counterintuitive to work to convert it all over to stl just to get a CG.

The main reason why I have two methods, as I have had instances where the Element.Geometry node failed and didn’t return any data. I don’t remember the exact wording of the error at the moment. I am sure the failure was due to how the elements were modeled, but I could never pinpoint what caused it, so it was easier to add a second check in the script. If you interested and wanting to know, I can try to recreate the error. Thank you for the feedback! I will investigate that method more.

Ahh - understand. Been there done that.

I actually don’t use Element.Geometry per se at this point unless i’m dealing with multi-material elements - I have the whole thing coded up in Python so I never actually get Dynamo Solids when dealing with single material elements (i.e. steel trusses). I get the Revit API solids directly and then get the centroid using the Revit API Solid.Centroid method.
https://www.revitapidocs.com/2020/42d79808-231b-f802-f574-6b799c95b871.htm

Hello
an example of solid fusion with ExecuteBooleanOperation then apply the ComputeCentroid method

import clr
import sys
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

def mergeSolidOperation(lstSolid, mergeSolid = None, i = 0):
	lstfail = []
	if len(lstSolid) > 0 and i < 1000:
		mergeSolid = lstSolid.pop(0) if mergeSolid is None else mergeSolid
		for s in lstSolid:
			try:
				mergeSolid = DB.BooleanOperationsUtils.ExecuteBooleanOperation(mergeSolid, s, BooleanOperationsType.Union)
			except Autodesk.Revit.Exceptions.InvalidOperationException :
				lstfail.append(s)
	if len(lstfail) == len(lstSolid):
		return mergeSolid
	else:
		return mergeSolidOperation(lstfail, mergeSolid, i + 1)

toList = lambda x : x if hasattr(x, '__iter__') else [x]
lstelems = toList(UnwrapElement(IN[0]))

lstSolid = [g for e in lstelems for g in e.GetGeometryObjectFromReference(Reference(e)) if isinstance(g, Solid)]
mergeSolid = mergeSolidOperation(lstSolid)
if isinstance(mergeSolid, Solid):
	OUT = mergeSolid.ComputeCentroid().ToPoint(), mergeSolid.ToProtoType()
3 Likes

Hi @Ben_Osborne
Thanks for advice.
All elements are same material. I tried to deleted some objects inside and now it’s only one element with lots of voids inside.
But it’s still not working if I just pick only that element. The new warning error happens.


I have no idea about code block.
I can not upload Revit file because I’m newbie on this forum.
Could I send you my file via email to help me check? That would be helpful for me.
Thanks a lot.

Hi @c.poupin
Thanks for your opinion.
I have no idea about code block. I try to copy your code (all you wrote) into “Code Block” node. But it seems like I made wrong. Can you please guide me to try it?
Thank you!

That’s a nice piece of code there.

Provided I trusted the geometry to not have overlap, I would avoid the merge solids because that occasionally fails (hence the try/except). In my experience it’s more reliable (albeit potentially not as efficient - but what’s a few extra milliseconds of computer processing?) to loop thru all the solids getting every single centroid and volume - and then calculate an overall centroid based on the weighted average of the points.

The most accurate way would likely be to do both the merge and the looping of individual centroids then compare the results (if both points same - 100% trust, if merge fails - note the failure and use the calculated CG, if the values differ - use the merged CG).

1 Like

Heres my method:

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

def convert_geometry_instance(geo, elementlist):
	for g in geo:
		if str(g.GetType()) == 'Autodesk.Revit.DB.GeometryInstance':
			elementlist = convert_geometry_instance(g.GetInstanceGeometry(), elementlist)
		else:
			try: 
				if g.Volume != 0:
					elementlist.append(g)
			except:
				pass
	return elementlist

doc = DocumentManager.Instance.CurrentDBDocument
items = UnwrapElement(IN[0])
detail_lvl = ViewDetailLevel.Fine
inc_invis = False
view = None
inserts = True
remove_inserts = False
revitlist = list()
dynlist = list()

TransactionManager.Instance.EnsureInTransaction(doc)

i = 0
for item in items:
	geo_options = Options()
	geo_options.DetailLevel = detail_lvl
	geo_options.IncludeNonVisibleObjects = inc_invis
	revitGeo = item.Geometry[geo_options]
	try:		
		revit_geos = convert_geometry_instance(revitGeo, list())
		revitlist.append(revit_geos)
	except:
		revitlist.append(list())
	i += 1

TransactionManager.Instance.TransactionTaskDone()

volz=[]
cent=[]
for sol in revitlist:
	for so in sol:
		volz.append(so.Volume)
		cent.append(so.ComputeCentroid())
totvol=sum(volz)
xs=[]
ys=[]
zs=[]
for i in range(0,len(volz)):
	vol=volz[i]
	cen=cent[i]
	xs.append(cen.X*vol/totvol)
	ys.append(cen.Y*vol/totvol)
	zs.append(cen.Z*vol/totvol)

centroidpoint=Point.Create(XYZ(sum(xs),sum(ys),sum(zs))).ToProtoType()

OUT = (totvol,centroidpoint)
1 Like

@bauconvietnam1
you need copy and paste the code inside the python Node

@Ben_Osborne
interesting process :grinning:

Thanks Ben and c.poupin for providing your methods along with the code! As stated, I use this as part of another script, so it will be great if I can rely on this getting the COG every time.

Just curious @staylor - being that you are working with precast - I would assume that single material center of gravity would not be sufficient since precast elements could consist of concrete, steel, brick (as in precast walls with finishes) and insulation.

Is your typical practice to just assume 150 pcf and run with single material assumptions or actually get the real C.G. wherein a more concentrated zone of rebar on one side of an otherwise uniform wall might result in a non-symmetric C.G.