# SubDObject Volume Calculation

Hello fellow rhino users,

I have the following python code to calculate volume of certain objects. I can get the volume of meshes, polysurfaces and surfaces but cannot get the volume of the subds. Does anyone know how to do it (without converting them into meshes of course)? (If you know how to display python code properly in the forum, could you please tell me how to do it?)

``````  def cumulativeVolume():
obj = rs.GetObjects("Objects for weight calculation", 8+16+32+262144+1073741824, True, True)
if not obj: return

meshVol = []
brepVol = []

for object in obj:
print "Object type: ", rs.ObjectType(object)

if rs.ObjectType(object) == 32:

Vmesh = rs.MeshVolume(object)[1]
meshVol.append(Vmesh)

# object is SubD
elif rs.ObjectType(object) == 262144:
Vmesh = rs.SurfaceVolume(object)[0]
meshVol.append(Vmesh)
else:
i = rs.coercebrep(object)
blockVolume = i.GetVolume()
brepVol.append(blockVolume)

Vmm3 = sum(meshVol) + sum(brepVol)
return Vmm3

V = cumulativeVolume() / 1000
``````

I am not sure you can currently - the `VolumeMassProperties.Compute()` method (which underlies the rhinoscriptsyntax methods) does not appear to support SubDâ€™s for the moment - this from the RhinoCommon 8.0 API:

Computes the VolumeMassProperties for a collection of geometric objects. At present only Breps, Surfaces, Meshes and Planar Closed Curves are supported.

So you might currently still need to mesh the SubD. Better actually to just convert to a Brep with `ToBrep()` and get the volume of the brep. It should be pretty accurate.
(someone who knows more please correct me if Iâ€™m wrongâ€¦)

Type 3 `backticks` and Python
Paste the entire code below it
Type 3 more backticks after

Like this:

Oh Rhino 8 API. Okay. I think Iâ€™ll create dummy brep objects, calculate the volume and then delete them. Is this what you were thinking?

Thank you for the backtick `

Well, that basically means that itâ€™s not supported in Rhino 7 eitherâ€¦ Otherwise I would have said that itâ€™s possible in the WIP but not in Rhino 7.

Well, thatâ€™s one way. I prefer to dive down into RhinoCommon and do all the calcs there, because the geometry is virtual and so you can convert a subd to a brep just for the volume calculation without having to add it to the document and delete it later. However, itâ€™s another level of abstractionâ€¦

Something like this perhaps:

``````import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def CumulativeVolume(obj_ids):
breps=[]
mesh_vol_total=0
brep_vol_total=0
count=0

#get all rhinocommon geometry from object ids
objs=[rs.coercegeometry(obj_id) for obj_id in obj_ids]

for obj in objs:
#Sum volumes directly for valid meshes, or skip if not valid
if isinstance(obj,Rhino.Geometry.Mesh):
if obj.IsClosed:
mv=obj.Volume()
if mv is not None:
mesh_vol_total+=mv
count+=1

#Convert other objects into breps before calculating volume
elif isinstance(obj,Rhino.Geometry.Brep):
if obj.IsSolid:
breps.append(obj)
elif isinstance(obj,Rhino.Geometry.Extrusion) or \
isinstance(obj,Rhino.Geometry.SubD):
obj=obj.ToBrep()
if obj.IsSolid:
breps.append(obj)

#Do brep volume calculation/sum
if breps:
for brep_obj in breps:
bv=brep_obj.GetVolume()
if bv is not None:
brep_vol_total+=bv
count+=1

#return the sum of mesh and brep volumes
return mesh_vol_total+brep_vol_total, count

otype_filtr=8+16+32+262144+1073741824
obj_ids= rs.GetObjects("Objects for weight calculation", otype_filtr,True,True)
if obj_ids:
cv=CumulativeVolume(obj_ids)
if cv:
msg="Cumulative volume of {} objects is {}".format(cv[1],cv[0]*0.001)
skipped =len(obj_ids)-cv[1]
if skipped>0: msg+= " | Skipped {} objects".format(skipped)
else:
msg="Unable to calculate any volumes"
print msg
``````
1 Like

You are amazing! Thank you.

Hi @Helvetosaur
Since the Volume command in Rhino, does work on SubDs, [and apology for the Python novice question] is there a way to call that command in the script to check the volume for the SubDâ€¦?

• I have the same limitation in a script [shared on the forum years ago] that I slightly modified to calculate gems weight from a library of gemstoneâ€™s SG

thanks a lot
Akash

Sure. I canâ€™t create an example right now, but it should be possible. I canâ€™t remember, but the Volume command returns a number and an error margin, Iâ€™d have to see how to parse the returned command line text. The advantage of scripted methods is that this step is not necessary.

1 Like

Thank you
Itâ€™s not too urgent in my case, it would be lovely to have a workaround, even if it is more clumsy then the proper scripting methodâ€¦
in my work, there are gemstones that do not have a regular [or symmetrical ] shape and I use SubD to model these.

with best regards
Akash

I had thought about too in the past but I didnâ€™t have enough coding knowledge to do that. Now I think I made it and here is the code for you Akash and for whom might need it:

``````            object = rs.GetObject()
rs.SelectObject(object)
rs.Command("_volume")
history = rs.CommandHistory()
history_lines = history.split("\n")
volume_info = history_lines[-2]
split_operation = volume_info.split("=")[-1].split("(")[0].strip()
volume = float(split_operation)
``````

Here what we do is;
First, to call the volume command and have the code return its value into the command line.
Then we use the `rs.CommandHistory()` function in rhinoscriptsyntax to get all the lines of the history.
Next, we select the last line only.
Then parse this line to get the number value, which is still a string so cannot be used in calculations.
And finally convert it into a number using the pythonâ€™s `float()` function

Hope this helps

2 Likes