Duplicate model geometry removal

I am working on the removal of duplicate model geometry using the Rhino3dm functionality in .NET. During this, a few things have come up:

  1. As a suggestion for Rhino3dm in .NET, it would be a nice convenience if Rhino.Geometry.NurbsSurface.EpsilonEquals() also compared control points. I suspect this may have accidentally been left out given that Rhino.Geometry.NurbsCurve.EpsilonEquals() does compare control points.

  2. Out of curiosity, why don’t the EpsilonEquals() functions check the vector distance between points, rather than the difference in vector components, as is currently done? I see that Rhino.Geometry.Point3d.CullDuplicates() checks whether the vector distance between two points is within tolerance, whilst all EpsilonEquals() functions check whether the differences in vector components of points are within tolerance. My interpretation is that Rhino’s model absolute tolerance is in relation to vector distance. Therefore, to me, it would make more sense for the EpsilonEquals() functions to do this also.

  3. Is the following the best approach for detecting duplicates? To detect duplicate points, I call Rhino.Geometry.Point3d.CullDuplicates. To detect duplicate curves, I convert all curves to NurbsCurve (splitting polycurves into each segment first), then call Rhino.Geometry.NurbsCurve.EpsilonEquals() for forward and reverse. To detect duplicate surfaces, I convert all surfaces to NurbsSurface, then call Rhino.Geometry.NurbsSurface.EpsilonEquals() and NurbsSurfacePointList.EpsilonEquals() for each possible reverse. I am using Rhino.FileIO.File3dmSettings.ModelAbsoluteTolerance as my tolerance.

Many thanks.

Hi @stephen.banks,

I’m guessing this is just a simple oversight. I’ve logged the issue.


Are you talking about this?

if (point0.DistanceTo(point1) < epsilon)


if ((point0 - point1).Length < epsilon)

In the end, both use the same underlying calculation.

Seems like a reasonable approach.


– Dale

Thanks, Dale.

In regards to my point (2), to clarify, the Rhino.Geometry.Point3d.CullDuplicates() function tests whether point_list[i].DistanceTo(point_list[j]) <= tolerance, where the function DistanceTo() computes the Euclidean distance between point_list[i] and point_list[j]. In contrast, the Rhino.Geometry.Point3d.EpsilonEquals() and Rhino.Geometry.Point4d.EpsilonEquals() functions test whether the difference in coordinate components are all less than epsilon (each coordinate component individually tested using the function RhinoMath.EpsilonEquals). Control points for NurbsCurve and NurbsSurface using the Rhino.Geometry.NurbsCurve.EpsilonEquals() and NurbsSurfacePointList.EpsilonEquals(), respectively, are compared using Rhino.Geometry.Point4d.EpsilonEquals(). So, it appears CullDuplicates and EpsilonEquals use different test conditions. As such, EpsilonEquals allows two points to be an inconsistent vector distance apart (i.e. from tolerance to tolerance * sqrt(3) for Point3d, and from tolerance to tolerance * 2 for Point4d, depending on where the two points are relative to each other in 3d or 4d space, respectively), whereas CullDuplicates, whilst only applying to 3d space, does not.

I am curious why there is a different approach used?

An additional difference is CullDuplicates uses less than or equal to tolerance, whereas EpsilonEquals requires less than tolerance. I would probably think they should be inclusive of the tolerance, but that’s just me, and I might need to give that one further thought. I guess it is really how Rhino.FileIO.File3dmSettings.ModelAbsoluteTolerance controls accuracy, i.e. does it impose that accuracy is better than the tolerance, or better than or equal to it?

As a correction to my original post, I actually use twice Rhino.FileIO.File3dmSettings.ModelAbsoluteTolerance considering that is the maximum difference, and indeed according to your Wiki In joining operations, 2X the absolute tolerance is allowed, so be careful.