Rhino Python - Extract multi SubSurf and delete the originals

Hello everyone,

I’ve been looking for a long time, but I can’t find the solution.
I really hope you can help me.

I want to delete some subsurfaces from a polysurface. But I want to be able to make a selection of several subsurfaces at the same time.

From what I understand, Rhinocommon is the best/only tool for the job.
But rhinocommon’s behavior means that I can’t delete the selected subsurfaces without deleting the whole polysurface.

I thought I’d try extracting the sub-surfaces in order to remove them from the polysurface.
But same thing, the surfaces are extracted, but the originals always remain.

Whereas when I use rs.GetObject to select a single subsurface, it is extracted and the original is removed.

So rs.GetObject allows you to select a subsurface, but only one at a time.
And I can’t delete the originals subsurfaces, with Rhinocommon.

Thanks for your help and explanations, I really need them.

import rhinoscriptsyntax as rs
import Rhino


def get_brep_faces():
    sub_surfaces = []

    # Create a GetObject object to prompt the user to select surfaces
    go = Rhino.Input.Custom.GetObject()
    go.SetCommandPrompt("Select surfaces")
    go.GeometryFilter = Rhino.DocObjects.ObjectType.Surface
    go.SubObjectSelect = True
    go.GetMultiple(1, 0)

    # Check if the user selection was successful
    if go.CommandResult() != Rhino.Commands.Result.Success:
        return

    # Iterate through the selected objects
    for objref in go.Objects():
        # Get the face of the object reference
        face = objref.Face()
        # Convert the face to a Brep
        sub_srf = face.ToBrep()
        # Append the Brep to the list of sub-surfaces
        sub_surfaces.append(sub_srf)
        # Print information about the selected object
        print(objref.ObjectId)
        print(objref.GeometryComponentIndex.ComponentIndexType)
        print(objref.GeometryComponentIndex.Index)

    # Check if any sub-surfaces were selected
    if sub_surfaces is None:
        print("No sub-surfaces selected.")
    else:
        print("Sub-surfaces have been selected successfully.")
        return sub_surfaces


# Call the get_brep_face function to get the sub-surfaces
subsurfaces = get_brep_faces()

# If there are sub-surfaces, extract them from their parent surfaces
if subsurfaces:
    for obj in subsurfaces:
        rs.ExtractSurface(obj, 0, False)

Use rs.GetObjects with an s

From what I know, GetObjects does not allow selecting subsurfaces like GetObject

It’s a little more complicated than this (in my understanding).

Subobjects (i.e. faces of a brep, etc.) do not have an ID, so you cannot delete them with rhinoscriptsyntax methods. With RhinoCommon as you did above you can get the parent id’s and the face indices of the faces you want to delete. Basically after that you can reconstruct the brep(s) using the faces that are not in the list of faces to delete, then replace the original brep with the reconstructed brep.

(maybe there is a better way to do this, but this is what I know)

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def SelectSubobjects(prompt,filt):
    go = Rhino.Input.Custom.GetObject()
    go.SetCommandPrompt(prompt)
    go.GeometryFilter=filt
    go.SubObjectSelect=True
    go.ChooseOneQuestion = True
    go.GetMultiple(1,0)
    if go.CommandResult()!=Rhino.Commands.Result.Success:
        return go.CommandResult()
    objrefs = go.Objects()
    if not objrefs: return Rhino.Commands.Result.Nothing
    rc=[]
    for objref in objrefs:
        indices=[]
        for item in objref.Object().GetSelectedSubObjects():
            #select subobject
            objref.Object().SelectSubObject(item, True, True, True)
            #append index to return
            indices.append(item.Index)
        rc.append([objref.ObjectId,indices])
    return rc

def DeleteBrepFaces():
    prompt="Select brep faces to delete"
    #result is a nested list of parent object, subobject index
    result=SelectSubobjects(prompt,filt=Rhino.DocObjects.ObjectType.Surface)
    if not result: return
    
    tol=sc.doc.ModelAbsoluteTolerance
    main_brep_ids=set()
    for item in result:
        #get the object id's of the parent objects
        main_brep_ids.add(item[0])
    #main_brep_ids contains one or more GUID's
    
    for main_brep_id in main_brep_ids:
        to_delete=[]
        for item in result:
            #add the indices of the selected faces
            if item[0]==main_brep_id: to_delete.extend(item[1])
        to_delete=set(to_delete) #remove duplicates
        #to_delete now has the indices of the faces to delete
        
        to_keep=[]
        brep=rs.coercebrep(main_brep_id)
        for i,face in enumerate(brep.Faces):
            if i not in to_delete:
                #keep this face
                to_keep.append(face.DuplicateFace(True))
        #to_keep has a list of faces to be kept
        
        #join the faces back into a new brep
        joined=Rhino.Geometry.Brep.JoinBreps(to_keep,tol)
        #joined returns a list of breps
        if joined:
            if len(joined)==1:
                #can replace the original with the brep with faces deleted
                sc.doc.Objects.Replace(main_brep_id,joined[0])
            else:
                #somehow the original brep has been split into multiple pieces
                #get original object attributes
                rhobj=rs.coercerhinoobject(main_brep_id)
                attrs=rhobj.Attributes.Duplicate()
                #add new breps with original attributes
                for new_brep in joined:
                    sc.doc.Objects.AddBrep(new_brep,attrs)
                sc.doc.Objects.Delete(rhobj)
    sc.doc.Views.Redraw()
DeleteBrepFaces()

Note it is possible to directly delete a face from a brep via the brep’s facelist and the face index, but if there are multiple faces to delete in the same brep, it fails after the first one because the indicies are rearranged after each delete so they are no longer what you expect. Maybe there is a multiple index delete somewhere but I didn’t find it.

Edit: I realized that it is possible to delete enough faces in a brep to end up with two or more separate objects - therefore in this case one canot replace the original object with the result directly. I modified the code to reflect this, it replaces if there is a one to one correspondence, otherwise it makes new objects and deletes the original.

@Helvetosaur
Thank you so much !

It seems to work perfectly. I was drowning in the RhinoCommon library.

I had thought of this idea, to sew a new polysurface without the subsurfaces I wanted to remove, but I couldn’t figure out how to do it. And I’d gone on to other ideas.

Your answer is really appropriate, fast, and with great comments.

Thanks again.