Campbell Barton
e8da6131fd
Move copyright text to SPDX-FileCopyrightText or set to the Blender Foundation so "make check_licenses" now runs without warnings.
599 lines
20 KiB
Python
599 lines
20 KiB
Python
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
from . import mathematics
|
|
|
|
import bpy
|
|
|
|
|
|
class BezierPoint:
|
|
@staticmethod
|
|
def FromBlenderBezierPoint(blenderBezierPoint):
|
|
return BezierPoint(blenderBezierPoint.handle_left, blenderBezierPoint.co, blenderBezierPoint.handle_right)
|
|
|
|
|
|
def __init__(self, handle_left, co, handle_right):
|
|
self.handle_left = handle_left
|
|
self.co = co
|
|
self.handle_right = handle_right
|
|
|
|
|
|
def Copy(self):
|
|
return BezierPoint(self.handle_left.copy(), self.co.copy(), self.handle_right.copy())
|
|
|
|
def Reversed(self):
|
|
return BezierPoint(self.handle_right, self.co, self.handle_left)
|
|
|
|
def Reverse(self):
|
|
tmp = self.handle_left
|
|
self.handle_left = self.handle_right
|
|
self.handle_right = tmp
|
|
|
|
|
|
class BezierSegment:
|
|
@staticmethod
|
|
def FromBlenderBezierPoints(blenderBezierPoint1, blenderBezierPoint2):
|
|
bp1 = BezierPoint.FromBlenderBezierPoint(blenderBezierPoint1)
|
|
bp2 = BezierPoint.FromBlenderBezierPoint(blenderBezierPoint2)
|
|
|
|
return BezierSegment(bp1, bp2)
|
|
|
|
|
|
def Copy(self):
|
|
return BezierSegment(self.bezierPoint1.Copy(), self.bezierPoint2.Copy())
|
|
|
|
def Reversed(self):
|
|
return BezierSegment(self.bezierPoint2.Reversed(), self.bezierPoint1.Reversed())
|
|
|
|
def Reverse(self):
|
|
# make a copy, otherwise neighboring segment may be affected
|
|
tmp = self.bezierPoint1.Copy()
|
|
self.bezierPoint1 = self.bezierPoint2.Copy()
|
|
self.bezierPoint2 = tmp
|
|
self.bezierPoint1.Reverse()
|
|
self.bezierPoint2.Reverse()
|
|
|
|
|
|
def __init__(self, bezierPoint1, bezierPoint2):
|
|
# bpy.types.BezierSplinePoint
|
|
# ## NOTE/TIP: copy() helps with repeated (intersection) action -- ??
|
|
self.bezierPoint1 = bezierPoint1.Copy()
|
|
self.bezierPoint2 = bezierPoint2.Copy()
|
|
|
|
self.ctrlPnt0 = self.bezierPoint1.co
|
|
self.ctrlPnt1 = self.bezierPoint1.handle_right
|
|
self.ctrlPnt2 = self.bezierPoint2.handle_left
|
|
self.ctrlPnt3 = self.bezierPoint2.co
|
|
|
|
self.coeff0 = self.ctrlPnt0
|
|
self.coeff1 = self.ctrlPnt0 * (-3.0) + self.ctrlPnt1 * (+3.0)
|
|
self.coeff2 = self.ctrlPnt0 * (+3.0) + self.ctrlPnt1 * (-6.0) + self.ctrlPnt2 * (+3.0)
|
|
self.coeff3 = self.ctrlPnt0 * (-1.0) + self.ctrlPnt1 * (+3.0) + self.ctrlPnt2 * (-3.0) + self.ctrlPnt3
|
|
|
|
|
|
def CalcPoint(self, parameter = 0.5):
|
|
parameter2 = parameter * parameter
|
|
parameter3 = parameter * parameter2
|
|
|
|
rvPoint = self.coeff0 + self.coeff1 * parameter + self.coeff2 * parameter2 + self.coeff3 * parameter3
|
|
|
|
return rvPoint
|
|
|
|
|
|
def CalcDerivative(self, parameter = 0.5):
|
|
parameter2 = parameter * parameter
|
|
|
|
rvPoint = self.coeff1 + self.coeff2 * parameter * 2.0 + self.coeff3 * parameter2 * 3.0
|
|
|
|
return rvPoint
|
|
|
|
|
|
def CalcLength(self, nrSamples = 2):
|
|
nrSamplesFloat = float(nrSamples)
|
|
rvLength = 0.0
|
|
for iSample in range(nrSamples):
|
|
par1 = float(iSample) / nrSamplesFloat
|
|
par2 = float(iSample + 1) / nrSamplesFloat
|
|
|
|
point1 = self.CalcPoint(parameter = par1)
|
|
point2 = self.CalcPoint(parameter = par2)
|
|
diff12 = point1 - point2
|
|
|
|
rvLength += diff12.magnitude
|
|
|
|
return rvLength
|
|
|
|
|
|
#http://en.wikipedia.org/wiki/De_Casteljau's_algorithm
|
|
def CalcSplitPoint(self, parameter = 0.5):
|
|
par1min = 1.0 - parameter
|
|
|
|
bez00 = self.ctrlPnt0
|
|
bez01 = self.ctrlPnt1
|
|
bez02 = self.ctrlPnt2
|
|
bez03 = self.ctrlPnt3
|
|
|
|
bez10 = bez00 * par1min + bez01 * parameter
|
|
bez11 = bez01 * par1min + bez02 * parameter
|
|
bez12 = bez02 * par1min + bez03 * parameter
|
|
|
|
bez20 = bez10 * par1min + bez11 * parameter
|
|
bez21 = bez11 * par1min + bez12 * parameter
|
|
|
|
bez30 = bez20 * par1min + bez21 * parameter
|
|
|
|
bezPoint1 = BezierPoint(self.bezierPoint1.handle_left, bez00, bez10)
|
|
bezPointNew = BezierPoint(bez20, bez30, bez21)
|
|
bezPoint2 = BezierPoint(bez12, bez03, self.bezierPoint2.handle_right)
|
|
|
|
return [bezPoint1, bezPointNew, bezPoint2]
|
|
|
|
|
|
class BezierSpline:
|
|
@staticmethod
|
|
def FromSegments(listSegments):
|
|
rvSpline = BezierSpline(None)
|
|
|
|
rvSpline.segments = listSegments
|
|
|
|
return rvSpline
|
|
|
|
|
|
def __init__(self, blenderBezierSpline):
|
|
if not blenderBezierSpline is None:
|
|
if blenderBezierSpline.type != 'BEZIER':
|
|
print("## ERROR:", "blenderBezierSpline.type != 'BEZIER'")
|
|
raise Exception("blenderBezierSpline.type != 'BEZIER'")
|
|
if len(blenderBezierSpline.bezier_points) < 1:
|
|
if not blenderBezierSpline.use_cyclic_u:
|
|
print("## ERROR:", "len(blenderBezierSpline.bezier_points) < 1")
|
|
raise Exception("len(blenderBezierSpline.bezier_points) < 1")
|
|
|
|
self.bezierSpline = blenderBezierSpline
|
|
|
|
self.resolution = 12
|
|
self.isCyclic = False
|
|
if not self.bezierSpline is None:
|
|
self.resolution = self.bezierSpline.resolution_u
|
|
self.isCyclic = self.bezierSpline.use_cyclic_u
|
|
|
|
self.segments = self.SetupSegments()
|
|
|
|
|
|
def __getattr__(self, attrName):
|
|
if attrName == "nrSegments":
|
|
return len(self.segments)
|
|
|
|
if attrName == "bezierPoints":
|
|
rvList = []
|
|
|
|
for seg in self.segments: rvList.append(seg.bezierPoint1)
|
|
if not self.isCyclic: rvList.append(self.segments[-1].bezierPoint2)
|
|
|
|
return rvList
|
|
|
|
if attrName == "resolutionPerSegment":
|
|
try: rvResPS = int(self.resolution / self.nrSegments)
|
|
except: rvResPS = 2
|
|
if rvResPS < 2: rvResPS = 2
|
|
|
|
return rvResPS
|
|
|
|
if attrName == "length":
|
|
return self.CalcLength()
|
|
|
|
return None
|
|
|
|
|
|
def SetupSegments(self):
|
|
rvSegments = []
|
|
if self.bezierSpline is None: return rvSegments
|
|
|
|
nrBezierPoints = len(self.bezierSpline.bezier_points)
|
|
for iBezierPoint in range(nrBezierPoints - 1):
|
|
bezierPoint1 = self.bezierSpline.bezier_points[iBezierPoint]
|
|
bezierPoint2 = self.bezierSpline.bezier_points[iBezierPoint + 1]
|
|
rvSegments.append(BezierSegment.FromBlenderBezierPoints(bezierPoint1, bezierPoint2))
|
|
if self.isCyclic:
|
|
bezierPoint1 = self.bezierSpline.bezier_points[-1]
|
|
bezierPoint2 = self.bezierSpline.bezier_points[0]
|
|
rvSegments.append(BezierSegment.FromBlenderBezierPoints(bezierPoint1, bezierPoint2))
|
|
|
|
return rvSegments
|
|
|
|
|
|
def UpdateSegments(self, newSegments):
|
|
prevNrSegments = len(self.segments)
|
|
diffNrSegments = len(newSegments) - prevNrSegments
|
|
if diffNrSegments > 0:
|
|
newBezierPoints = []
|
|
for segment in newSegments: newBezierPoints.append(segment.bezierPoint1)
|
|
if not self.isCyclic: newBezierPoints.append(newSegments[-1].bezierPoint2)
|
|
|
|
self.bezierSpline.bezier_points.add(diffNrSegments)
|
|
|
|
for i, bezPoint in enumerate(newBezierPoints):
|
|
blBezPoint = self.bezierSpline.bezier_points[i]
|
|
|
|
blBezPoint.tilt = 0
|
|
blBezPoint.radius = 1.0
|
|
|
|
blBezPoint.handle_left_type = 'FREE'
|
|
blBezPoint.handle_left = bezPoint.handle_left
|
|
blBezPoint.co = bezPoint.co
|
|
blBezPoint.handle_right_type = 'FREE'
|
|
blBezPoint.handle_right = bezPoint.handle_right
|
|
|
|
self.segments = newSegments
|
|
else:
|
|
print("### WARNING: UpdateSegments(): not diffNrSegments > 0")
|
|
|
|
|
|
def Reversed(self):
|
|
revSegments = []
|
|
|
|
for iSeg in reversed(range(self.nrSegments)): revSegments.append(self.segments[iSeg].Reversed())
|
|
|
|
rvSpline = BezierSpline.FromSegments(revSegments)
|
|
rvSpline.resolution = self.resolution
|
|
rvSpline.isCyclic = self.isCyclic
|
|
|
|
return rvSpline
|
|
|
|
|
|
def Reverse(self):
|
|
revSegments = []
|
|
|
|
for iSeg in reversed(range(self.nrSegments)):
|
|
self.segments[iSeg].Reverse()
|
|
revSegments.append(self.segments[iSeg])
|
|
|
|
self.segments = revSegments
|
|
|
|
|
|
def CalcDivideResolution(self, segment, parameter):
|
|
if not segment in self.segments:
|
|
print("### WARNING: InsertPoint(): not segment in self.segments")
|
|
return None
|
|
|
|
iSeg = self.segments.index(segment)
|
|
dPar = 1.0 / self.nrSegments
|
|
splinePar = dPar * (parameter + float(iSeg))
|
|
|
|
res1 = int(splinePar * self.resolution)
|
|
if res1 < 2:
|
|
print("### WARNING: CalcDivideResolution(): res1 < 2 -- res1: %d" % res1, "-- setting it to 2")
|
|
res1 = 2
|
|
|
|
res2 = int((1.0 - splinePar) * self.resolution)
|
|
if res2 < 2:
|
|
print("### WARNING: CalcDivideResolution(): res2 < 2 -- res2: %d" % res2, "-- setting it to 2")
|
|
res2 = 2
|
|
|
|
return [res1, res2]
|
|
# return [self.resolution, self.resolution]
|
|
|
|
|
|
def CalcPoint(self, parameter):
|
|
nrSegs = self.nrSegments
|
|
|
|
segmentIndex = int(nrSegs * parameter)
|
|
if segmentIndex < 0: segmentIndex = 0
|
|
if segmentIndex > (nrSegs - 1): segmentIndex = nrSegs - 1
|
|
|
|
segmentParameter = nrSegs * parameter - segmentIndex
|
|
if segmentParameter < 0.0: segmentParameter = 0.0
|
|
if segmentParameter > 1.0: segmentParameter = 1.0
|
|
|
|
return self.segments[segmentIndex].CalcPoint(parameter = segmentParameter)
|
|
|
|
|
|
def CalcDerivative(self, parameter):
|
|
nrSegs = self.nrSegments
|
|
|
|
segmentIndex = int(nrSegs * parameter)
|
|
if segmentIndex < 0: segmentIndex = 0
|
|
if segmentIndex > (nrSegs - 1): segmentIndex = nrSegs - 1
|
|
|
|
segmentParameter = nrSegs * parameter - segmentIndex
|
|
if segmentParameter < 0.0: segmentParameter = 0.0
|
|
if segmentParameter > 1.0: segmentParameter = 1.0
|
|
|
|
return self.segments[segmentIndex].CalcDerivative(parameter = segmentParameter)
|
|
|
|
|
|
def InsertPoint(self, segment, parameter):
|
|
if not segment in self.segments:
|
|
print("### WARNING: InsertPoint(): not segment in self.segments")
|
|
return
|
|
iSeg = self.segments.index(segment)
|
|
nrSegments = len(self.segments)
|
|
|
|
splitPoints = segment.CalcSplitPoint(parameter = parameter)
|
|
bezPoint1 = splitPoints[0]
|
|
bezPointNew = splitPoints[1]
|
|
bezPoint2 = splitPoints[2]
|
|
|
|
segment.bezierPoint1.handle_right = bezPoint1.handle_right
|
|
segment.bezierPoint2 = bezPointNew
|
|
|
|
if iSeg < (nrSegments - 1):
|
|
nextSeg = self.segments[iSeg + 1]
|
|
nextSeg.bezierPoint1.handle_left = bezPoint2.handle_left
|
|
else:
|
|
if self.isCyclic:
|
|
nextSeg = self.segments[0]
|
|
nextSeg.bezierPoint1.handle_left = bezPoint2.handle_left
|
|
|
|
|
|
newSeg = BezierSegment(bezPointNew, bezPoint2)
|
|
self.segments.insert(iSeg + 1, newSeg)
|
|
|
|
|
|
def Split(self, segment, parameter):
|
|
if not segment in self.segments:
|
|
print("### WARNING: InsertPoint(): not segment in self.segments")
|
|
return None
|
|
iSeg = self.segments.index(segment)
|
|
nrSegments = len(self.segments)
|
|
|
|
splitPoints = segment.CalcSplitPoint(parameter = parameter)
|
|
bezPoint1 = splitPoints[0]
|
|
bezPointNew = splitPoints[1]
|
|
bezPoint2 = splitPoints[2]
|
|
|
|
|
|
newSpline1Segments = []
|
|
for iSeg1 in range(iSeg): newSpline1Segments.append(self.segments[iSeg1])
|
|
if len(newSpline1Segments) > 0: newSpline1Segments[-1].bezierPoint2.handle_right = bezPoint1.handle_right
|
|
newSpline1Segments.append(BezierSegment(bezPoint1, bezPointNew))
|
|
|
|
newSpline2Segments = []
|
|
newSpline2Segments.append(BezierSegment(bezPointNew, bezPoint2))
|
|
for iSeg2 in range(iSeg + 1, nrSegments): newSpline2Segments.append(self.segments[iSeg2])
|
|
if len(newSpline2Segments) > 1: newSpline2Segments[1].bezierPoint1.handle_left = newSpline2Segments[0].bezierPoint2.handle_left
|
|
|
|
|
|
newSpline1 = BezierSpline.FromSegments(newSpline1Segments)
|
|
newSpline2 = BezierSpline.FromSegments(newSpline2Segments)
|
|
|
|
return [newSpline1, newSpline2]
|
|
|
|
|
|
def Join(self, spline2, mode = 'At_midpoint'):
|
|
if mode == 'At_midpoint':
|
|
self.JoinAtMidpoint(spline2)
|
|
return
|
|
|
|
if mode == 'Insert_segment':
|
|
self.JoinInsertSegment(spline2)
|
|
return
|
|
|
|
print("### ERROR: Join(): unknown mode:", mode)
|
|
|
|
|
|
def JoinAtMidpoint(self, spline2):
|
|
bezPoint1 = self.segments[-1].bezierPoint2
|
|
bezPoint2 = spline2.segments[0].bezierPoint1
|
|
|
|
mpHandleLeft = bezPoint1.handle_left.copy()
|
|
mpCo = (bezPoint1.co + bezPoint2.co) * 0.5
|
|
mpHandleRight = bezPoint2.handle_right.copy()
|
|
mpBezPoint = BezierPoint(mpHandleLeft, mpCo, mpHandleRight)
|
|
|
|
self.segments[-1].bezierPoint2 = mpBezPoint
|
|
spline2.segments[0].bezierPoint1 = mpBezPoint
|
|
for seg2 in spline2.segments: self.segments.append(seg2)
|
|
|
|
self.resolution += spline2.resolution
|
|
self.isCyclic = False # is this ok?
|
|
|
|
|
|
def JoinInsertSegment(self, spline2):
|
|
self.segments.append(BezierSegment(self.segments[-1].bezierPoint2, spline2.segments[0].bezierPoint1))
|
|
for seg2 in spline2.segments: self.segments.append(seg2)
|
|
|
|
self.resolution += spline2.resolution # extra segment will usually be short -- impact on resolution negligible
|
|
|
|
self.isCyclic = False # is this ok?
|
|
|
|
|
|
def RefreshInScene(self):
|
|
bezierPoints = self.bezierPoints
|
|
|
|
currNrBezierPoints = len(self.bezierSpline.bezier_points)
|
|
diffNrBezierPoints = len(bezierPoints) - currNrBezierPoints
|
|
if diffNrBezierPoints > 0: self.bezierSpline.bezier_points.add(diffNrBezierPoints)
|
|
|
|
for i, bezPoint in enumerate(bezierPoints):
|
|
blBezPoint = self.bezierSpline.bezier_points[i]
|
|
|
|
blBezPoint.tilt = 0
|
|
blBezPoint.radius = 1.0
|
|
|
|
blBezPoint.handle_left_type = 'FREE'
|
|
blBezPoint.handle_left = bezPoint.handle_left
|
|
blBezPoint.co = bezPoint.co
|
|
blBezPoint.handle_right_type = 'FREE'
|
|
blBezPoint.handle_right = bezPoint.handle_right
|
|
|
|
self.bezierSpline.use_cyclic_u = self.isCyclic
|
|
self.bezierSpline.resolution_u = self.resolution
|
|
|
|
|
|
def CalcLength(self):
|
|
try: nrSamplesPerSegment = int(self.resolution / self.nrSegments)
|
|
except: nrSamplesPerSegment = 2
|
|
if nrSamplesPerSegment < 2: nrSamplesPerSegment = 2
|
|
|
|
rvLength = 0.0
|
|
for segment in self.segments:
|
|
rvLength += segment.CalcLength(nrSamples = nrSamplesPerSegment)
|
|
|
|
return rvLength
|
|
|
|
|
|
def GetLengthIsSmallerThan(self, threshold):
|
|
try: nrSamplesPerSegment = int(self.resolution / self.nrSegments)
|
|
except: nrSamplesPerSegment = 2
|
|
if nrSamplesPerSegment < 2: nrSamplesPerSegment = 2
|
|
|
|
length = 0.0
|
|
for segment in self.segments:
|
|
length += segment.CalcLength(nrSamples = nrSamplesPerSegment)
|
|
if not length < threshold: return False
|
|
|
|
return True
|
|
|
|
|
|
class Curve:
|
|
def __init__(self, blenderCurve):
|
|
self.curve = blenderCurve
|
|
self.curveData = blenderCurve.data
|
|
|
|
self.splines = self.SetupSplines()
|
|
|
|
|
|
def __getattr__(self, attrName):
|
|
if attrName == "nrSplines":
|
|
return len(self.splines)
|
|
|
|
if attrName == "length":
|
|
return self.CalcLength()
|
|
|
|
if attrName == "worldMatrix":
|
|
return self.curve.matrix_world
|
|
|
|
if attrName == "location":
|
|
return self.curve.location
|
|
|
|
return None
|
|
|
|
|
|
def SetupSplines(self):
|
|
rvSplines = []
|
|
for spline in self.curveData.splines:
|
|
if spline.type != 'BEZIER':
|
|
print("## WARNING: only bezier splines are supported, atm; other types are ignored")
|
|
continue
|
|
|
|
try: newSpline = BezierSpline(spline)
|
|
except:
|
|
print("## EXCEPTION: newSpline = BezierSpline(spline)")
|
|
continue
|
|
|
|
rvSplines.append(newSpline)
|
|
|
|
return rvSplines
|
|
|
|
|
|
def RebuildInScene(self):
|
|
self.curveData.splines.clear()
|
|
|
|
for spline in self.splines:
|
|
blSpline = self.curveData.splines.new('BEZIER')
|
|
blSpline.use_cyclic_u = spline.isCyclic
|
|
blSpline.resolution_u = spline.resolution
|
|
|
|
bezierPoints = []
|
|
for segment in spline.segments: bezierPoints.append(segment.bezierPoint1)
|
|
if not spline.isCyclic: bezierPoints.append(spline.segments[-1].bezierPoint2)
|
|
#else: print("????", "spline.isCyclic")
|
|
|
|
nrBezierPoints = len(bezierPoints)
|
|
blSpline.bezier_points.add(nrBezierPoints - 1)
|
|
|
|
for i, blBezPoint in enumerate(blSpline.bezier_points):
|
|
bezPoint = bezierPoints[i]
|
|
|
|
blBezPoint.tilt = 0
|
|
blBezPoint.radius = 1.0
|
|
|
|
blBezPoint.handle_left_type = 'FREE'
|
|
blBezPoint.handle_left = bezPoint.handle_left
|
|
blBezPoint.co = bezPoint.co
|
|
blBezPoint.handle_right_type = 'FREE'
|
|
blBezPoint.handle_right = bezPoint.handle_right
|
|
|
|
|
|
def CalcLength(self):
|
|
rvLength = 0.0
|
|
for spline in self.splines:
|
|
rvLength += spline.length
|
|
|
|
return rvLength
|
|
|
|
|
|
def RemoveShortSplines(self, threshold):
|
|
splinesToRemove = []
|
|
|
|
for spline in self.splines:
|
|
if spline.GetLengthIsSmallerThan(threshold): splinesToRemove.append(spline)
|
|
|
|
for spline in splinesToRemove: self.splines.remove(spline)
|
|
|
|
return len(splinesToRemove)
|
|
|
|
|
|
def JoinNeighbouringSplines(self, startEnd, threshold, mode):
|
|
nrJoins = 0
|
|
|
|
while True:
|
|
firstPair = self.JoinGetFirstPair(startEnd, threshold)
|
|
if firstPair is None: break
|
|
|
|
firstPair[0].Join(firstPair[1], mode)
|
|
self.splines.remove(firstPair[1])
|
|
|
|
nrJoins += 1
|
|
|
|
return nrJoins
|
|
|
|
|
|
def JoinGetFirstPair(self, startEnd, threshold):
|
|
nrSplines = len(self.splines)
|
|
|
|
if startEnd:
|
|
for iCurrentSpline in range(nrSplines):
|
|
currentSpline = self.splines[iCurrentSpline]
|
|
|
|
for iNextSpline in range(iCurrentSpline + 1, nrSplines):
|
|
nextSpline = self.splines[iNextSpline]
|
|
|
|
currEndPoint = currentSpline.segments[-1].bezierPoint2.co
|
|
nextStartPoint = nextSpline.segments[0].bezierPoint1.co
|
|
if mathematics.IsSamePoint(currEndPoint, nextStartPoint, threshold): return [currentSpline, nextSpline]
|
|
|
|
nextEndPoint = nextSpline.segments[-1].bezierPoint2.co
|
|
currStartPoint = currentSpline.segments[0].bezierPoint1.co
|
|
if mathematics.IsSamePoint(nextEndPoint, currStartPoint, threshold): return [nextSpline, currentSpline]
|
|
|
|
return None
|
|
else:
|
|
for iCurrentSpline in range(nrSplines):
|
|
currentSpline = self.splines[iCurrentSpline]
|
|
|
|
for iNextSpline in range(iCurrentSpline + 1, nrSplines):
|
|
nextSpline = self.splines[iNextSpline]
|
|
|
|
currEndPoint = currentSpline.segments[-1].bezierPoint2.co
|
|
nextStartPoint = nextSpline.segments[0].bezierPoint1.co
|
|
if mathematics.IsSamePoint(currEndPoint, nextStartPoint, threshold): return [currentSpline, nextSpline]
|
|
|
|
nextEndPoint = nextSpline.segments[-1].bezierPoint2.co
|
|
currStartPoint = currentSpline.segments[0].bezierPoint1.co
|
|
if mathematics.IsSamePoint(nextEndPoint, currStartPoint, threshold): return [nextSpline, currentSpline]
|
|
|
|
if mathematics.IsSamePoint(currEndPoint, nextEndPoint, threshold):
|
|
nextSpline.Reverse()
|
|
#print("## ", "nextSpline.Reverse()")
|
|
return [currentSpline, nextSpline]
|
|
|
|
if mathematics.IsSamePoint(currStartPoint, nextStartPoint, threshold):
|
|
currentSpline.Reverse()
|
|
#print("## ", "currentSpline.Reverse()")
|
|
return [currentSpline, nextSpline]
|
|
|
|
return None
|