RhinoCommon equivalent of "Explode"

I seem to be missing something here. In the file attached, there is one polycurve consisting of two segments, the split points are indicated with points. Each of the two subcurves is “kinked”, meaning it will explode into more segments.

Running the Rhino command “Explode” on the original curve produces 25 segments. I would like to be able to do this with RhinoCommon. I guess I am missing something, because I can’t find an exact way.

I ran the following script on the curve to test:

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def TestExplode():
    crvID=rs.GetObject("Pick polycurve",4,True)
    p_crv=sc.doc.Objects.Find(crvID).Geometry
    msg=""
    msg+="Original has {} segments\n".format(p_crv.SegmentCount)
    if p_crv.IsNested:
        msg+="Polycurve is nested\n"
        p_crv.RemoveNesting()
    else:
        msg+="Polycurve is NOT nested\n" 
    exploded=p_crv.Explode()
    msg+="Number of segs after crv.Explode()={}\n".format(len(exploded))
    dup_segs=p_crv.DuplicateSegments()
    msg+="Number of segs found with crv.DuplicateSegments={}\n".format(len(dup_segs))
    ex_crvs=rs.ExplodeCurves(crvID,False)
    msg+="Number of segs found after rs.ExplodeCurves()={}\n".format(len(ex_crvs))
    rs.SelectObject(crvID)
    rs.Command("Explode",False)
    lco=rs.LastCreatedObjects()
    msg+="Number of segs found after Rhino Explode command={}\n".format(len(lco))
    print msg
TestExplode()

The results are as follows:

Original has 2 segments
Polycurve is NOT nested
Number of segs after crv.Explode()=2
Number of segs found with crv.DuplicateSegments=19
Number of segs found after rs.ExplodeCurves()=19
Number of segs found after Rhino Explode command=25

I did not find any method that would completely explode the curve as the Rhino explode command does. Am I missing something? Or do I have to write my own function with FindNextDiscontinuity (painful) and split the curve, something like the following:

import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc

def SplitAtAllDiscontinuities(crv):
    dom=crv.Domain
    params=[]
    if crv.IsClosed:
        cd=Rhino.Geometry.Continuity.C1_locus_continuous
    else:
        cd=Rhino.Geometry.Continuity.C1_continuous
    param=dom[0]
    while True:
        chk,next_param=crv.GetNextDiscontinuity(cd,param,dom[1])
        if chk:
            params.append(next_param)
            param=next_param
        else:
            break
    return params
    
crvID=rs.GetObject("Select polycurve",4,preselect=True)
crv=sc.doc.Objects.Find(crvID).Geometry
parameters=SplitAtAllDiscontinuities(crv)
if len(parameters)>0:
    pts=[rs.EvaluateCurve(crvID,p) for p in parameters]
    rs.AddPoints(pts)
    rs.SplitCurve(crvID,parameters,False)

However, although this does give me 26 split points (not 25), they are not exactly the same as the results from the Rhino command “Explode”… Some curves with kinks remain, and some without kinks have been split… :confused:

Edit: and, a corrolary to this question: Is there a RhinoCommon tool to detect a kinked single NURBS curve (as Properties>Details reports) as opposed to a polycurve?

–Mitch

ExplodeQuestion.3dm (26.0 KB)

ExplodeQuestion.zip (10.8 KB)

1 Like

One simple approach might be to cast to polyline and then get its segments. If I understood the problem correctly.

Except it’s not a polyline, it’s a multi-segment degree 3 NURBS curve…

Ah sorry, guess I didn’t get the problem then. Long day…

No problem. The curve in question is posted above, but not sure you can download it anyway - here I can’t re-download my attachments, something is broken. --Mitch

Mitch,

If you have a curve that is in the document, the easiest way to “explode” it is to call RhinoObject.GetSubObjects.

1 Like

OK, thanks Dale. Unfortunately, I am working with “virtual” geometry in RhinoCommon for this one, nothing has been added to the document.

Cheers, --Mitch

The explode command searches for ON::Gsmooth_continuous discontinuity, which is what we call aesthetic discontinuity. It has a enum value of 12. I don’t believe this is defined in RhinoCommon. Try using this value with Curve.GetNextDiscontinuity.

Hey Dale,
How do you work with the subobjects after? If try to extract the geometry of the subobject, all I get is that I can’t because it’s “document controlled”:

“This object cannot be modified because it is controlled by a document.”

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

objID=rs.GetObject("Select polycurve",4,True)
crvObj=sc.doc.Objects.Find(objID)
su=crvObj.GetSubObjects()
for i,obj in enumerate(su):
    geo=obj.Geometry
    #errors out here

–Mitch

Hi Mitch,

How about this; Duplicate the geometry before ‘addressing’ it.

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

objID=rs.GetObject("Select polycurve",4,True)
crvObj=sc.doc.Objects.Find(objID)

crv_dup = crvObj.DuplicateGeometry() # It's here you get a copy that is not bound to the doc

su=crv_dup.DuplicateSegments() #This was my explode means for the polycurve I tested
for i,obj in enumerate(su):
    sc.doc.Objects.AddCurve(obj)

-Willem

Well I was just trying out Dale’s advice to GetSubObjects - from above, DuplicateSegments does not seem to get all the segments that Explode does… But I really don’t know how it’s supposed to work, as those sub-objects can’t seem to be duplicated to get unbound geometry. But if you try it with my example above and a breakpoint, you will see the length of the subobject list is indeed 25 and not the 19 that DuplicateSegments gets… --Mitch

This is my version of explode in C#:

private List<Curve> ExplodeCurve(Curve curve) {
    var curves = new List<Curve>();
    var t1 = curve.Domain.T1;
    var lastT = 0.0;
    var t = 0.0;
    while (curve.GetNextDiscontinuity(Continuity.Gsmooth_continuous, lastT, t1, out t)) {
        var subCurve = curve.Trim(lastT, t);
        if (subCurve != null) {
            curves.Add(subCurve);
        }
        lastT = t;
    }
    if (lastT != t1) {
        var subCurve = curve.Trim(lastT, t1);
        if (subCurve != null) {
            curves.Add(subCurve);
        }
    }
    return curves;
}
5 Likes

Thanks for this, its super helpful, though I think there is a bug in the code @public
Not all curves have domain’s starting at zero - so i think it should read
var lastT = curve.Domain.T0;
so that the Discontinuity search starts at the beginning of the domain

1 Like