Join curves in similar direction

I’ve written a Rhinoscript that given two lines, the script will join them if (a) they share an end point and (b) the angle of the tangent vector at their shared end point is less than a specified value. I’d like to expand this to do it for a large number of curves all at once.

Back up a bit to the bigger picture: people in my (architecture) firm like to design a surface/mass then subdivide the surface into a triangulated grid. Everybody uses different ways of doing this. I’ve been asked a number of times to take the resulting grid of lines and turn it into a set of triangular surfaces and sweep a profile along each curve/line to build an architectural model of glass/metal panels and steel structure. Sometimes the grid is a bunch of continuous degree-3 curves, sometimes degree-3 curves that go from node-to-node, sometimes straight lines that go from node-to-node, and sometimes polylines in each direction.

In any of these cases, I can create the sweeps for the structural members, that’s not a problem.

For panels, it’s another story. Given polylines or continous degree-3 curves along each diagonal (or in U and V directions), I have no problem creating panels. However, if they’ve split the curves/lines at the node intersections and haven’t sorted them previously by layer for each direction, it’s a tedious process. (I’d like to encourage/teach people how to create grids in a more useful way, but partly for practical reasons and partly for self-educational reasons, I think this is an interesting problem to solve.)

Any suggestions for working through a whole list of curves would be greatly appreciated. Here’s what I have so far that works for the simple case of testing two curves:

Call JoinCurvesToPath()
Sub JoinCurvesToPath()

	Dim arrCrv : arrCrv = Rhino.GetObjects("Select two curves to join into a polycurve", 4)
	If IsNull(arrCrv) Then Exit Sub
	
	Call Rhino.EnableRedraw(False)
	
	Dim crvA : crvA = arrCrv(0)
	Dim crvB : crvB = arrCrv(1)
	Dim arrParams
	Dim angle : angle = 15

	'Find joining ends
	arrParams = JoiningEnds(crvA, crvB)

	If Not (IsNull(arrParams)) Then 

		'Join curves if angle is met.
		Call JoinCurves(crvA, crvB, arrParams, angle)

	End If
			
	Call Rhino.EnableRedraw(True)
	
End Sub

Function JoiningEnds(crvA, crvB)
	
	Call Rhino.Print("In JoiningEnds function")
	
	Dim startA, endA, startB, endB
	Dim paramA, paramB
	
	startA = Rhino.CurveStartPoint(crvA)
	endA = Rhino.CurveEndPoint(crvA)
	startB = Rhino.CurveStartPoint(crvB)
	endB = Rhino.CurveEndPoint(crvB)
	
	If (Rhino.Distance(startA, startB) < 0.1) Then
		Call Rhino.Print("starts")
		paramA = Rhino.CurveClosestPoint(crvA, startA)
		paramB = Rhino.CurveClosestPoint(crvB, startB)
		JoiningEnds = Array(paramA, paramB)
		
	ElseIf (Rhino.Distance(startA, endB) < 0.1) Then
		Call Rhino.Print("start, end")
		paramA = Rhino.CurveClosestPoint(crvA, startA)
		paramB = Rhino.CurveClosestPoint(crvB, endB)
		JoiningEnds = Array(paramA, paramB)
		
	ElseIf (Rhino.Distance(endA, endB) < 0.1) Then
		Call Rhino.Print("ends")
		paramA = Rhino.CurveClosestPoint(crvA, endA)
		paramB = Rhino.CurveClosestPoint(crvB, endB)
		JoiningEnds = Array(paramA, paramB)
		
	ElseIf (Rhino.Distance(endA, startB) < 0.1) Then
		Call Rhino.Print("end, start")
		paramA = Rhino.CurveClosestPoint(crvA, endA)
		paramB = Rhino.CurveClosestPoint(crvB, startB)
		JoiningEnds = Array(paramA, paramB)
		
	End If
	
End Function

Function JoinCurves(crvA, crvB, arrParams, minAngle)
	
	Dim vecA, vecB
	Dim joined
	
	vecA = Rhino.CurveTangent(crvA, arrParams(0))
	If IsNull(vecA) Then
		Call Rhino.Print("CurveTangent at A failed")
		Exit Function
	End If
	vecB = Rhino.CurveTangent(crvB, arrParams(1))
	If IsNull(vecB) Then
		Call Rhino.Print("CurveTangent at B failed")
		Exit Function
	End If
	
	Dim angle: angle = Rhino.VectorAngle(vecA, vecB)
	
	Call Rhino.Print("Angle = " & angle)
	
	If (angle < minAngle) Then
		
		joined = Rhino.JoinCurves(Array(crvA, crvB))
		Call Rhino.Print("joined has " & Ubound(joined) + 1 & "objects")
		
		JoinCurves = joined(0)
		Call Rhino.DeleteObjects(Array(crvA, crvB))
		
	Else
		
		JoinCurves = Null
		
	End If
	
		
End Function
1 Like

I have not looked over your script, but you might want to look into PanelingTools.

http://wiki.mcneel.com/labs/panelingtools

I know It’s a very old topic but I had a similar problem and found no scripts so I wanted to share it.
There you go for anyone having this issue in the future

import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs

def join_curves_to_path():
    arrCrv = rs.GetObjects("Select curves to join into a polycurve", 4)
    if arrCrv is None:
        return

    sc.doc.Views.RedrawEnabled = False

    angle = 15
    joined_something = True

    while joined_something:
        joined_something = False
        for i, crvA in enumerate(arrCrv[:-1]):
            for crvB in arrCrv[i+1:]:
                arrParams = joining_ends(crvA, crvB)
                if arrParams is not None:
                    joined_crv = join_curves(crvA, crvB, arrParams, angle)
                    if joined_crv is not None:
                        arrCrv.remove(crvA)
                        arrCrv.remove(crvB)
                        arrCrv.append(joined_crv)
                        joined_something = True
                        break
            if joined_something:
                break
    return arrCrv
    sc.doc.Views.RedrawEnabled = True

def joining_ends(crvA, crvB):
    print("In JoiningEnds function")

    startA = rs.CurveStartPoint(crvA)
    endA = rs.CurveEndPoint(crvA)
    startB = rs.CurveStartPoint(crvB)
    endB = rs.CurveEndPoint(crvB)

    if rs.Distance(startA, startB) < 0.1:
        print("starts")
        paramA = rs.CurveClosestPoint(crvA, startA)
        paramB = rs.CurveClosestPoint(crvB, startB)
        return [paramA, paramB]

    elif rs.Distance(startA, endB) < 0.1:
        print("start, end")
        paramA = rs.CurveClosestPoint(crvA, startA)
        paramB = rs.CurveClosestPoint(crvB, endB)
        return [paramA, paramB]

    elif rs.Distance(endA, endB) < 0.1:
        print("ends")
        paramA = rs.CurveClosestPoint(crvA, endA)
        paramB = rs.CurveClosestPoint(crvB, endB)
        return [paramA, paramB]

    elif rs.Distance(endA, startB) < 0.1:
        print("end, start")
        paramA = rs.CurveClosestPoint(crvA, endA)
        paramB = rs.CurveClosestPoint(crvB, startB)
        return [paramA, paramB]

def join_curves(crvA, crvB, arrParams, minAngle):
    vecA = rs.CurveTangent(crvA, arrParams[0])
    if vecA is None:
        print("CurveTangent at A failed")
        return

    vecB = rs.CurveTangent(crvB, arrParams[1])
    if vecB is None:
        print("CurveTangent at B failed")
        return

    angle = rs.VectorAngle(vecA, vecB)
    print("Angle = {}".format(angle))

    if angle < minAngle:
        joined = rs.JoinCurves([crvA, crvB])
        print("joined has {} objects".format(len(joined)))
        return joined[0]

    else:
        return None

join_curves_to_path()