Select Similar?


#1

Good morning Rhino friends,

Recently I have found that everything I’ve asked for already exists in Rhino, but I will try this anyway:

A command called something like “Selectsimilar” that would find all the instances of a particular object, (curve, surface, polysurface, extrusion, mesh, etc.)

Seldup seems to be limited to similar objects that overlap – but this would find duplicate objects that don’t necessarily overlap.

This, in combination with Replaceobject would be really powerful.

Thank you.


#2

sounds like a very useful command / idea!
i guess one of the devs will hav a much better answer than me, but i give it a shot for fun:

eg. your object is a mesh:

  • filter out all the objects with the same vertex count
  • select any three vertices from your source mesh, make a plane from them.
  • do the same with all the possible similars (ones with same vertex count)
  • get a xform with Transform.ChangeBasis(plane,plane) - the two planes to compare
  • transform the object in question to the source object
  • compare the rest of the vertices
  • if 100% or any other threshold is overlapping they are similar / the same

then you have a xform you can use to replace it with any other object, while keeping the rotation, translation and scale

this should work with almost anything as long as you can idenify three points in the given object. (a straight line could be a problem - as i doesnt give you a defined tranform?)


#3

Here is a thread with a script I wrote a while back for finding similar closed volume objects. I wanted to refine it and extend the types of similar objects it can find, but just haven’t had time.

–Mitch


#4

Hi Mitch,

Thanks for your script but I am still wondering why it seems to be so difficult to create a script that can find similar objects that are not closed volumes? This, of course, applies to similar objects that do not overlap.

What about simple two point curves, or four sided loops?

Thank you.


#5

The simpler the objects are, the easier it is… Just that the original script was for someone who was working with closed volumes.

Lines are easy - you only have to check the length. Circles, the radius. However, past that point, it gets more complex… To find the same object you either need to check a sufficient number of criteria (length, area, volume; number, type, dimensions/locations of sub-objects; etc.) that a match is more or less assured - or, try to find a transformation such that you can lay one object on top of another. The second method is not trivial at all and can require a lot of computation - you need to find an orientation in space - so it’s much easier to work with the first method and try and eliminate the obvious non-matches first.

Some of it is certainly feasible - especially with simple curves - but as soon as you get into more complex objects like polysurfaces, it gets more complicated and potentially computationally long.

–Mitch


(Menno Deij - van Rijswijk) #6

Note that in RhinoCommon most geometry primitives have an EpsilonEquals function:


interface Rhino.IEpsilonComparable<T> 
{
    bool EpsilonEquals(T other, double epsilon);
}

// These structs and classes all implement the interface:
Point4d, Point4f, Point3d, Point3f, Point2d, Point2f, Vector3d, Vector3f, Vector2d, Vector2f, Interval, 
Quaternion, Ray3d;

Sphere, Rectangle3d, Torus, Plane, Line, Arc, Ellipse, Box, Cylinder, Cone, Circle;

NurbsCurve, NurbsSurface, ControlPoint

NurbsSurfacePointList, NurbsCurvePointList, NurbsSurfaceKnotList, NurbsCurveKnotList,

This means that all these geometry objects can be compared for “similarity” by choosing an epsilon value within which they should be similar.


#7

Yes, but I assume this means they are at the same location in space - as in SelDup - whereas the poster is talking about finding the a geometrically identical object but located elsewhere if I understood correctly.

–Mitch


(Menno Deij - van Rijswijk) #8

The difference between EpsilonEquals and SelDup is that SelDup only selects objects that are bitwise the same. If there is a small deviation in one control point location at the 12th decimal, SelDup will not select that object. EpsilonEquals with a reasonable tolerance will flag those objects as similar.

Note that for curves and surfaces the number of control points and knots must be the same between the two objects being compared.


#9

Correct, Mitch. I am hoping to find similar objects that are located anywhere in the model.


(Menno Deij - van Rijswijk) #10

Ok, that was not clear to me. Good luck with that :smile:


#11

Hi Mitch

Im a complete novice with python and im struggling to select duplicate curves do you happen to have the python script you created available for duplicate curves?

Thanks

Joe


#12

Are you looking for curves that (in theory) should select with SelDup but don’t - i.e. in the same location in space, but just slightly out - or are you looking to find “same object but somewhere else in model” ?

–Mitch


#13

Hi Mitch

Im looking for ones that were extracted from the faces of a tesselated surface so there are multiple line sin one place, they are in the same location but must be slightly out of the tolerance of SelDup so SelDup doesnt pick it up.

Thanks


#14

Looks like coupling EpsilonEquals with enormous tolerance, and checking whether the curves are of the same length, does the trick for copied curves - for both cases: curves on the top of each other (the ones SelDup would find), and curves scattered around.

import rhinoscriptsyntax as rs
import scriptcontext as sc

def cullDuplicateCurves(_ids, _tol):
    if len(_ids) > 1:
        objs = [sc.doc.Objects.Find(id) for id in _ids]
        nc_objs = [crv.Geometry.ToNurbsCurve() for crv in objs]
        indx = []
        for i in range(len(nc_objs)):
            for k in range(len(nc_objs)):
                if (i != k) and (i not in indx) and (k not in indx) and (round(nc_objs[i].GetLength(), 3) == round(nc_objs[k].GetLength(), 3)):
                        if nc_objs[i].EpsilonEquals(nc_objs[k], _tol):
                            indx.append(i)
        for index in indx:
            rs.SelectObject(ids[index])
        if len(indx) > 0:
            print "Found %s duplicate curves" % len(indx)
        else:
            print "No duplicate curves found"

tol = 100000000
ids = rs.GetObjects("Pick up the curves you would like to clean from duplicates", 4, preselect=True)
cullDuplicateCurves(ids, tol)

cullDuplicateCurves.py (974 Bytes)


(Menno Deij - van Rijswijk) #15

In addition to checking length, you may also want to check curvature along the curve at a number of points at the same length along the curves to see if the shape of the curve is similar. Otherwise any curve with the same number of control points and similar length will match (think mirrored, rotated curves).


#16

Thanks Menno
Would checking the curvature vectors at start, mid and end domain of the curve be sufficient enough ?
cullDuplicateCurves2.py (3.0 KB)


(Menno Deij - van Rijswijk) #17

More points gives more certainty. But it is important to check at a given length along the curve, not at given parameters.


#18

How would these two differ? Checking the curvature information (in this case curvature vectors) at a given Parameter, and at a given LengthParameter?


(Menno Deij - van Rijswijk) #19

A curve can have the same (or similar) shape, but a different parametrization (knot vector values and spacing). Looking at the same parameter value (e.g. Domain.Mid) can give a very different point on the curve.


#20

Sorry for the lateness, I’ve been (and still am) chained to my lasers for the last few days…

Here is an old script I have… FWIW…

–Mitch

SelNearDupCrvs1.py (2.7 KB)