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 ` :pray:

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 :slight_smile:

2 Likes