RhinoCommon Split Brep with multiple Breps

Having a bit of a brain fart here…

In RhinoCommon, I want to be able to split a Brep with multiple Brep cutters and get all the split parts. The thing is when I split the Brep with the first cutter, it can produce two or more parts (if there is an intersection) or none (if not). If the split does happen, the original Brep needs to be replaced by the list of split parts for the next cutter, and then I need to iterate through that list and do the same for each item in the list… etc. So the function needs to be recursive.

But I can’t seem to get my head around how to feed the split parts back into the original list and continue…

At this point I need to get on with other stuff, but if anyone can toss out a coupe of clues, it would be greatly appreciated…

Thx, --Mitch

Hmmm… How about first calculating all the intersection curves (Intersection.BrepBrep); then use all resulting curves to split all the BRep faces (BrepFace.Split(curves)). No need for recursion.

1 Like

Hey Menno,
Thanks! That was my first thought as well, but I didn’t go down that road because it seems that you can’t split multiface Breps with curves, only individual faces… So then, as everything is exploded, I would have to figure out how to join the right faces back together to reconstitute the split bits correctly…

–Mitch

I suppose you can do the second step brep-per-brep and just give it all the curves. That should give you the split parts of each brep separately:

in pseudo-code:

curves = []
foreach brep1 in breps:
  foreach brep2 in breps:
    if brep1 == brep 2: continue
    curves.add( intersect(brep1, brep2) )

all_parts = []
foreach brep in breps:
  parts = []
  foreach face in brep:
    parts.add( split(face, curves) )

  all_parts.add(parts)

now all_parts is a list of list of splitted parts, each list belonging to one brep.

Hi @Helvetosaur,

Does something like this help?

# Create a disjoint brep to use as a cutter
cutter = Rhino.Geometry.Brep()
for b in cutting_breps:
    cutter.Append(b)
    
pieces = brep.Split(cutter, tol)

– Dale

HI @dale,
Wow, I’m amazed…! So you can just create a single empty Brep and then throw a bunch of disjoint breps into it and that will pass into Brep.Split()… I’d never have thought of that approach. I’ll have a go at testing tomorrow, thanks!

Edit - This is indeed working and very cool, thanks again @dale !

Cheers, --Mitch

@dale - I did however discover a limitation of this method… The disjoint breps that make up the cutter may not intersect each other, otherwise the operation fails…

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def testDJBrepSplit():
    cutter_IDs=rs.GetObjects("Select cutters",8)
    to_cut_ID=rs.GetObject("Select object to cut",8+16)
    cutters=[rs.coercebrep(ID) for ID in cutter_IDs]
    to_cut=rs.coercebrep(to_cut_ID)
    
    #make disjoint Brep
    dj_brep=Rhino.Geometry.Brep()
    for cutter in cutters: dj_brep.Append(cutter)
    
    #try to cut brep object with disjoint cutter
    if isinstance(to_cut,Rhino.Geometry.Extrusion): to_cut=to_cut.ToBrep()
    split_result=to_cut.Split(dj_brep,sc.doc.ModelAbsoluteTolerance)
    
    if not split_result: print "Failed!"
    else:
        for brep in split_result: sc.doc.Objects.AddBrep(brep)
        rs.DeleteObject(to_cut_ID)
    
    sc.doc.Views.Redraw()
testDJBrepSplit()

In the file below, you can split the blue box with all three green cutters or just the one red cutter, but not with all four at the same time, or any combination of the red plus a green (which cross). I suppose there isn’t a real workaround to this… The native Rhino command does work, so I imagine there is a different method used.

MultisplitBox.3dm (255.6 KB)

–Mitch

@GregArden, any suggestions?

You can also keep track of pieces and cut them with the remainder of the cutters. Here is an example for your case:

split.gh (12.4 KB)

Forgot to add the code:

import scriptcontext as sc
tol = sc.doc.ModelAbsoluteTolerance

def split(input_geo, cutters):
    collector = []
    for g in input_geo:
        res = g.Split(cutter, tol)
        if res:
            collector.extend(res)
        else:
            collector.append(g)
            
    return collector

geos = [geo]
for count, cutter in enumerate(cutters):
    # split with the first cutter
    # and collect the new splitted geos
    geos = split(geos, cutter)
    print('round %d: input geometry is splitted into %d pieces.' % (count + 1, len(geos)))
1 Like

@Helvetosaur Call NonmanifoldMerge on the splitters. Then you’ll have a single BREP that has been split up at the intersections.

Hi Chuck,
Thanks, that doesn’t seem to be available via RhinoCommon - at least I didn’t find it. I’m mainly trying to avoid scripting a Rhino command - otherwise this would be easy, I could just use Split…

Cheers, --Mitch

That’s exactly what I was trying to do at first… I’ll have to look at your code to see how it differs from mine, as I couldn’t get mine working for some reason… Thanks!

–Mitch

@Helvetosaur In RhinoCommon it’s Brep.MergeBreps

1 Like

Thanks @chuck and @Mostapha, I got it to work using both methods… :smile:

–Mitch

Hi @Helvetosaur,

You shouldn’t have to go thru this much pain - I’ve created an issue to fix this.

https://mcneel.myjetbrains.com/youtrack/issue/RH-42917

– Dale

1 Like

Hi @Dale, has this issue been addressed in Rhino6? I have the same problem

Hi @Matthieu_from_NAVINN,

You can view the status of the issue using the above link. This particular issue has not yet been worked on.

– Dale

ok thanks

It looks like this was implemented in 6.13. Today, running a python script I had created previous to 6.13, I received:

Split() got an unexpected keyword argument ‘splitter’

https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Geometry_Brep_Split.htm
states that ‘splitter’ is still the parameter name for when splitting with a single Brep, but ‘cutter’ is in 6.13’s RhinoCommon.dll:

Split(self: Brep, cutters: IEnumerable[Curve], intersectionTolerance: float) -> Array[Brep]
Split(self: Brep, cutters: IEnumerable[GeometryBase], normal: Vector3d, planView: bool, intersectionTolerance: float) -> Array[Brep]
Split(self: Brep, cutters: IEnumerable[Brep], intersectionTolerance: float) -> Array[Brep]
Split(self: Brep, cutter: Brep, intersectionTolerance: float) -> Array[Brep]

I modified the script so it works in both V5 and V6, still calling Split with named arguments, but I’m letting you know in case this change wasn’t intentional.

Hi @spb,

New Brep.Split overrides added to SR13

IronPython’s support for overloaded methods can be somewhat manual. In the case of Brep.Split, you will need to use the .Overloads property to access a method’s overloads:

More on overloads…

For example:

import System
import System.Collections.Generic.IEnumerable as IEnumerable
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def test():

   id = rs.GetObject( filter = 8+16, preselect = True)
   if not id: return
   brep = rs.coercebrep(id)

   ids = rs.GetObjects(filter = 8+16,)
   if not ids: return

   cutters = [rs.coercebrep(item) for item in ids]

   pass
   out = brep.Split.Overloads[IEnumerable[Rhino.Geometry.Brep], System.Double]( cutters, .001)
   rs.DeleteObject(id)
   for item in out:
       sc.doc.Objects.AddBrep(item)
   sc.doc.Views.Redraw()

test()

– Dale