Join disjoint curves into closed curve

Hello everyone,

I am working on a function that will join a collection of curves into a single closed curve, based upon the closest endpoints.

You can read more detailed comments in the .gh file, but the basic algorithm is as follows:

  1. Collect up all the endpoints of the curves
  2. If it’s not already present, then add each point and the point that is closest to it to a separate list
  3. Draw line segments between every pair of points in this separate list.
  4. Convert everything to nurbs curves, and join the new segments with the original curves.

Here you can see an example, with the segments below, and the intended closed polyline (highlighted in yellow) above.

It works great 50% of the time, but as I test the input curves to see how robust the algorithm is, I start getting invalid polycurves and I can’t figure out exactly why.

My guess is that it has something to do with point tolerances, and I am creating microsopic lines… but I can’t quite figure out what’s up.

Here is the invalid curve segment

Any ideas?
201111_CloseDisjointCurves.3dm (29.0 KB) 201111_CloseDisjointCurves.gh (10.6 KB)

Here is the code:

import rhinoscriptsyntax as rs
import Rhino as rc
import scriptcontext as sc



def checkIfListIncludesPoint(listOfPoints,pointToTest):
    sc.doc = rc.RhinoDoc.ActiveDoc
    functionTolerance = sc.doc.ModelAbsoluteTolerance
    sc.doc = ghdoc
    counter = 0
    for pt in listOfPoints:
        if pt.EpsilonEquals(pointToTest, 1) == True:
            counter = counter + 1
    if counter > 0:
        return True
    if counter == 0:
        return False
        
def sortListOfPointsByDistanceToPoint(listOfPoints,pointToTest):
    listOfDistances = []
    for testPt in listOfPoints:
        listOfDistances.append(pointToTest.DistanceTo(testPt))
    sortedPts = [x for distance,x in sorted(zip(listOfDistances,listOfPoints))]
    return sortedPts


#Create a function that will attemp to close several curves by connecting them to
#the closest endpoints of the curves nearby

#Create an empty list for all the end points
allEndPoints = []
#Create a list where you will place the points as they are sorted
sortedEndPoints = []
#Create a pointcloud for fast processing of point proximity
pointCloud = rc.Geometry.PointCloud()
#Create a list of all nurbsCurves for joining at the end
allNurbsCurves = []
#Define the tolerance as the rhino doc tolerance
sc.doc = rc.RhinoDoc.ActiveDoc
tolerance = sc.doc.ModelAbsoluteTolerance
sc.doc = ghdoc

#Load up the list of all endpoints with the start and end points of all the curves.
for curve in Curves:
    allEndPoints.append(curve.PointAtStart)
    allEndPoints.append(curve.PointAtEnd)
    nurbsCrv = rc.Geometry.NurbsCurve.ToNurbsCurve(curve)
    allNurbsCurves.append(nurbsCrv)

#Load up the point cloud with points you just collected
pointCloud.AddRange(allEndPoints)

#Iterate over all the points in allEndPoints
for i, pt3D in enumerate(allEndPoints):
    #For each point, sort all the points in order of their distance
    pointsAescendingDistances  = sortListOfPointsByDistanceToPoint(allEndPoints,pt3D)
    #If the point itself is not already in the list, 
    #then add both the test point and the test point's closest neighbor to a new list
    if checkIfListIncludesPoint(sortedEndPoints,pt3D) == False:
        sortedEndPoints.append(pointsAescendingDistances[0])
        sortedEndPoints.append(pointsAescendingDistances[1])

#Iterate over every other item in this list to make the lines:
for i in range(0, len(sortedEndPoints), 2):
    line = rc.Geometry.Line(sortedEndPoints[i],sortedEndPoints[i+1])
    nurbsCurve = line.ToNurbsCurve()
    allNurbsCurves.append(nurbsCurve)


joinedCurves = rc.Geometry.Curve.JoinCurves(allNurbsCurves, tolerance, True)[0]
print joinedCurves
#Return the new list
a = sortedEndPoints

image
that line segment appeared twice; you were joining overlapping/identical lines…
maybe when you searched for closest point, the closest to blue was the start point of that line itself rather than the end point on its neighbor.