You may consider a multi-step verification.
Once the lengths match, for example divide the shape in 3 and check the lengths of AB and AC vectors…
Or even use the resulting 3 points to orient-copy 2 shapes to the worldXY origins and compare these point locations…
The original problem isn’t very well defined - is this limited to planar 2D curves? Will curves translated or rotated out-of-plane be considered ‘same’ as each other? What about 2 lines of the same length but different number of segments?
(Note that a mirrored 2D curve corresponds to 180º 3D rotation around the mirror axis)
The objective is, find on drawing how many shapes is equal, can be a square, can be round or other shape, the shapes is always close and 2d in top view.
You could maybe look at extracting the control points, checking to make sure that the count, degree and overall knot multiplicity is the same, and then doing rigid point set registration https://en.wikipedia.org/wiki/Point_set_registration and seeing if the error is below a certain threshold.
Edit: I thought a little more about this , maybe the above is a bit overkill for your situation (it was the first thing that came to mind).
For a less robust but simpler algorithm you could try:
Calculate the curve centroid/ control pt average
sort curve control points by distance to the centroid
construct plane aligned to world Z axis and the nearest / farthest point from center
orient one set of sorted points to the other
check if distance between point sets below threshold
Maybe you could post what you have so far? Are you having trouble with the understanding of the algorithm or with the scripting syntax itself? ( i.e. are you familiar with using rhinoscriptsyntax / RhinoCommon )
Ok, this is the script that I am using, so, I compare the length and the area, if is the identical the geometry is true, if not the gemotry is false, (I also put a small tolerance, sometimes even being equal geometries, the script returns faulty)
Hmm, so this turned out to be more tricky than I thought…
My algorithm outlined above works for comparing irregular shapes, but not for regular ones like the squares and circles, because there isn’t a consistent way of sorting their vertices due to their symmetry.
I’ll just leave what I’ve got so far, hopefully you can get some ideas from it -
When testing your solution in two cases i got wrong results.
Case A:
-two different curves (but the same length, the same area, and the same num.of ctrl points)
-result gives Registration error = 0
Yeah, just realised my basic idea is flawed if your shapes have any sort of symmetry to them… I guess that’s the part of the reason for the complexity behind the actual robust algorithms I linked to in the wikipedia article.
For a small enough dataset though, a “dumb” brute-force approach might suffice… test every control point in the curve as a candidate seam point in both directions, to see if it matches the original one Worst case O(n*m) complexity, but probably closer to O(n) with early exit conditions (n = number of curves, m = number of control points)
from Rhino.Geometry import Vector3d
def get_signature(crv):
"""
Gets the signature of a closed 2d curve on the XY plane.
=> Cross product of the relative positions of each control point w.r.t the starting seam point with the Z Axis
then projected onto the vector of the first segment.
"""
control_pts = rs.CurvePoints(crv)
start_pt = control_pts[0]
reference_vec = control_pts[1] - start_pt
signature = [rs.VectorCrossProduct((pt - start_pt), Vector3d.ZAxis) * reference_vec for pt in control_pts]
return signature
def compare_curve_to_sig(crv, sig, tol=0.01):
"""
returns True if curve matches signature of another curve under specified tolerance
"""
control_pts = rs.CurvePoints(crv)
num_pts = len(control_pts)
if num_pts != len(sig):
return False
for start_index in range(num_pts):
# brute force check for every possible starting seam point
start_pt = control_pts[start_index]
# forward direction
reference_vec = control_pts[(start_index + 1) % num_pts] - start_pt
for i in range(num_pts):
val = rs.VectorCrossProduct(control_pts[(start_index + i) % num_pts] - start_pt, Vector3d.ZAxis) * reference_vec
if abs(val - sig[i]) > tol:
break # early exit
if i == num_pts - 1:
# completed inner loop without breaking -> all vectors match
return True # another early exit
# backward direction
reference_vec = control_pts[(start_index - 1) % num_pts] - start_pt
for i in range(num_pts):
val = rs.VectorCrossProduct(control_pts[(start_index - i) % num_pts] - start_pt, Vector3d.ZAxis) * reference_vec
if abs(val - sig[i]) > tol:
break # early exit
if i == num_pts - 1:
# completed inner loop without breaking -> all vectors match
return True # another early exit
# exhausted all possibilities
return False
Actually, one thing you should take care of is double precision error.
For example
you have circle with center at point cA=(0,0,0)
and you create 10 points on that circle (lets say by dividing circle), pointsA
and sort these points by distance to cA, orderedPointsA
If you copy and move that circle, center point (cB), and points( pointsB ) by some vector (vx,vy,0) and again sort these points by distance to center point, orderedPointsB YOU MAY GET DIFFERENT order … 2D_shape_comparison_DoublePrecisionIssue.gh (16.7 KB)