How to do _SelDup followed by _Delete using RhinoCommon methods?

I currently use this:


This selects only the duplicate segments and deletes them, leaving curves that can be joined into a closed “loop” curve.

The performance of this is terrible.
Which methods do I have to use in order to get this done using RhinoCommon?

Note: SelDup not _SelDupAll

after the _SelDup I can replace the rs.Command("_Delete") with this:

selected_dups = rs.SelectedObjects()
if selected_dups is not None:

Are you disabling the redraw before running it?

Yes, always, since I learned the existance of that method. :slight_smile:

OK, just a thought. I do not know of a substitute for SelDup aside from the GeometryBase.GeometryEquals mentioned in another thread. I would have no idea how to write an efficient loop to run through a large number of objects with that - I have a feeling that is what is being done with SelDup…

(I’d be happy to learn something new though)

1 Like

is that for me? I learn new things every day :rofl:

No, for me… edited the post for clarity.

1 Like

I’m currently trying with:

OBJ_S = [obj.Geometry for obj in get_all_selectable_curves()]
for i in range(-1, len(OBJ_S)-1):
    tmp_OBJ = OBJ_S[i]
    for j in range(-1, len(OBJ_S_-1)):
        if j != i:

:frowning: It seemed to work but I’m stuck in an infinite loop again.
I fear I’ll get BSoD again if I kill the process.

Well, I don’t have time to test, but for your ‘j’ loop, i think you can start at i+1, otherwise you end up comparing objects twice - inefficient…

for i in range (0, n-1):
    for j in range (i+1,n):

(I think)

OK, you managed to get me hooked on this one: :stuck_out_tongue_closed_eyes:

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def SelDupSub(objs):
    for i in range(0,len(objs)-1):
        for j in range(i+1,len(objs)):
            if Rhino.Geometry.GeometryBase.GeometryEquals(objs[i],objs[j]):
    return dup_indices
def TestFunction():
    obj_IDs=rs.GetObjects("Select objects to check")
    if not obj_IDs: return
    objs=[rs.coercegeometry(obj_ID) for obj_ID in obj_IDs]
    if result:
        for index in result:
1 Like

Here’s what I did :slight_smile: (RhinoCommon mainly :stuck_out_tongue: )

import Rhino
import System
import rhinoscriptsyntax as rs
import scriptcontext as sc
import time

tol = sc.doc.ModelAbsoluteTolerance

def get_em_all_curves(select=False, include_lights=False, include_grips=False, include_references=False):
    modified AllObjects() method to select all selectable objects of type curve
    selection_settings = Rhino.DocObjects.ObjectEnumeratorSettings()
    selection_settings.IncludeLights = include_lights
    selection_settings.IncludeGrips = include_grips
    selection_settings.NormalObjects = True
    selection_settings.LockedObjects = False
    selection_settings.HiddenObjects = False
    selection_settings.ReferenceObjects = include_references
    #Select all curve
    selection_settings.ObjectTypeFilter = Rhino.DocObjects.ObjectType.Curve 
    # this gets object references
    #.Geometry to get the geometry
    #.Id to get the uuid
    e = sc.doc.Objects.GetObjectList(selection_settings)
    return e

def remove_duplicates(OBJS):
    OBJS_G = [obj.Geometry for obj in OBJS]
    DUPS = []
    for i in range(0,len(OBJS_G)):
        tmp_OBJ = OBJS_G[i]
        for j in range(i+1,len(OBJS_G)):
            if j != i:
                if Rhino.Geometry.GeometryBase.GeometryEquals(tmp_OBJ,OBJS_G[j]):

    selected_dups = [obj.Id for obj in DUPS]
    if selected_dups is not None:

if __name__=="__main__":
    objects = [obj for obj in get_em_all_curves()]
    ts = time.time()
    print "Elapsed time is {:.2f}".format(time.time()-ts)


is Rhino.Geometry.GeometryBase.GeometryEquals thread safe?
Can we multi-thread this loop above?

Sorry for the probably dumb question, I’m not very threading savvy.

Thanks in advance.

Well, I added timing and reporting to my script above - then I tested it on 4400 curves, 2400 of which were dupes.

Script selected 2400 duplicate objects Script took 20.43 sec.
SelDup selected 2400 duplicate objects SelDup took 0.19 sec.

Which is what I suspected, they are using something other than the above brute force method for SelDup, so I think it is unlikely you are going to win out over it… (IMO)

1 Like

How about selecting by bounding box to avoid checking every other curve in the inner loop…?

1 Like

good idea, I’ll try that

I also thought about trying to .pop() the curve that is found to be identical. This way it won’t get in the second loop.

One of my theories is that they are using an rTree to do that in SelDup

Yes I’m sure you’re right- and using the right Rhinocommon methods will presumably give access to a bit of that rtree magic !

Hi Ivelin,
Do you want to delete duplicate curves?
If so, you can use this script:

Unlike “_SelDup” it can also identify if two curves are the same, but one has a flipped direction.

1 Like

Wow, thanks for sharing this @djordje.

I’ll test that.

I didn’t know _SelDup has such a weakness, are you sure?
So far I haven’t noticed that.

Hi Ivelin,
Draw a curve. Copy paste it, and then move it somewhere else, and/or flip it (so that it is not on the top of the original curve) then run the SelDup command.
This is consistent with what command documentation states:
The SelDup command selects objects that are geometrically identical with another object, visible, and in the same location regardless of other object properties.

But in my workflow I needed inclusion of curves which are identical but not in the same location.
This is what I meant with “unlike _SelDup…”.

Oh, no, I don’t want that. See, my model is of about 1000 objects. Most 3d pipes, straight, bent, elbows, valves, flanges, etc. I make silhouette of them, and project the curves in xy plane. Then, I wish to close these curves as closed polylines, but there is a limitation of the curve boolean. One must not have duplicates (on top of each other). But these pipes have the same radii, meaning most duplicate curve segments will have the same length and scattered around the xy plane. If I remove them all I will end up with a single pipe able to close.

Subsequently, I need to create hatches or surfaces from these closed polylines.