I got curious and decided to implement it in Python after all - This works on your test cases but will probably crash and burn for even slightly ill-conditioned inputs, so be warned!

Sort Curves (1).gh (25.1 KB)

```
from Queue import Queue
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path
from collections import namedtuple
tol = 0.01 # tolerance when comparing end points of a curve
class Node(object):
def __init__(self, point):
self.location = point
self.neighbors = [] # list of tuples (node, edge to that node)
def add_neighbor(self, neighbor_node, edge):
self.neighbors.append((neighbor_node, edge))
def get_minimal_cycle(self, filter_edges):
# breadth first search to find shortest (number of edges) cycle
visiting = Queue()
# keep track of current node, visited nodes and the edges leading up to it
visiting.put((self, [self], []))
while not visiting.empty():
curr_node, visited, path = visiting.get()
for next_node, next_edge in curr_node.neighbors:
if not next_edge in filter_edges:
continue
next_path = path + [next_edge]
if next_node in visited:
if next_node == self and len(visited) > 2:
# a single edge (2 visited nodes) doesn't count as a cycle
return next_path
continue
next_visited = visited + [curr_node]
visiting.put((next_node, next_visited, next_path))
raise RuntimeError('Could not find closed cycle')
Edge = namedtuple("Edge", "curve, start_node, end_node")
class Graph(object):
def __init__(self, curves):
self.nodes = []
self.edges = []
# construct graph
for crv in curves:
self.edges.append
start_pt, end_pt = crv.PointAtStart, crv.PointAtEnd
start_node, end_node = None, None
for node in self.nodes:
# test if end points of curve are already existing nodes
if start_node is None and node.location.EpsilonEquals(start_pt, tol):
start_node = node
if end_node is None and node.location.EpsilonEquals(end_pt, tol):
end_node = node
if start_node and end_node:
break
if start_node is None: # create new node
start_node = Node(start_pt)
self.nodes.append(start_node)
if end_node is None:
end_node = Node(end_pt)
self.nodes.append(end_node)
edge = Edge(crv, start_node, end_node)
start_node.add_neighbor(end_node, edge)
end_node.add_neighbor(start_node, edge)
self.edges.append(edge)
def get_cycles(self):
result = []
remaining_edges = set(self.edges)
while remaining_edges:
for start_edge in remaining_edges:
break # choose random edge
node = start_edge.start_node # choose random node
cycle_edges = node.get_minimal_cycle(remaining_edges)
remaining_edges.difference_update(cycle_edges)
result.append([edge.curve for edge in cycle_edges])
return result
result = Graph(curves).get_cycles() # input 'curves', type hint: Curve, List Access
tree = DataTree[object]() # output
for i, cycle in enumerate(result): # convert nested list to datatree
tree.AddRange(cycle, GH_Path(i))
```