bspy 1.2.2__tar.gz → 1.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bspy-1.2.2 → bspy-1.2.3}/PKG-INFO +1 -1
- {bspy-1.2.2 → bspy-1.2.3}/bspy/_spline_intersection.py +62 -32
- {bspy-1.2.2 → bspy-1.2.3}/bspy/_spline_operations.py +3 -3
- {bspy-1.2.2 → bspy-1.2.3}/bspy.egg-info/PKG-INFO +1 -1
- {bspy-1.2.2 → bspy-1.2.3}/setup.cfg +1 -1
- {bspy-1.2.2 → bspy-1.2.3}/tests/test_bspy.py +41 -24
- {bspy-1.2.2 → bspy-1.2.3}/LICENSE +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/README.md +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy/__init__.py +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy/_spline_domain.py +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy/_spline_evaluation.py +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy/_spline_fitting.py +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy/bspyApp.py +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy/drawableSpline.py +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy/spline.py +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy/splineOpenGLFrame.py +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy.egg-info/SOURCES.txt +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy.egg-info/dependency_links.txt +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy.egg-info/requires.txt +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/bspy.egg-info/top_level.txt +0 -0
- {bspy-1.2.2 → bspy-1.2.3}/pyproject.toml +0 -0
|
@@ -67,12 +67,15 @@ def zeros_using_interval_newton(self):
|
|
|
67
67
|
adjustedLeftStep = min(leftNewtonStep, rightNewtonStep) - 0.5 * epsilon
|
|
68
68
|
adjustedRightStep = max(leftNewtonStep, rightNewtonStep) + 0.5 * epsilon
|
|
69
69
|
if derivativeBounds[0] * derivativeBounds[1] >= 0.0: # Refine interval
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
projectedLeftStep = max(0.0, adjustedLeftStep)
|
|
71
|
+
projectedRightStep = min(1.0, adjustedRightStep)
|
|
72
|
+
if projectedLeftStep <= projectedRightStep:
|
|
73
|
+
if projectedRightStep - projectedLeftStep <= epsilon:
|
|
74
|
+
myZeros = [0.5 * (projectedLeftStep + projectedRightStep)]
|
|
75
|
+
else:
|
|
76
|
+
trimmedSpline = mySpline.trim(((projectedLeftStep, projectedRightStep),))
|
|
77
|
+
myZeros = refine(trimmedSpline, intervalSize, functionMax)
|
|
78
|
+
else:
|
|
76
79
|
return []
|
|
77
80
|
else: # . . . or split as needed
|
|
78
81
|
myZeros = []
|
|
@@ -367,9 +370,15 @@ def contours(self):
|
|
|
367
370
|
# No contours for this spline.
|
|
368
371
|
return []
|
|
369
372
|
|
|
373
|
+
# Record self's original domain and then reparametrize self's domain to [0, 1]^nInd.
|
|
374
|
+
domain = self.domain().T
|
|
375
|
+
self = self.reparametrize(((0.0, 1.0),) * self.nInd)
|
|
376
|
+
|
|
377
|
+
# Construct self's tangents and normal.
|
|
370
378
|
tangents = []
|
|
371
379
|
for nInd in range(self.nInd):
|
|
372
380
|
tangents.append(self.differentiate(nInd))
|
|
381
|
+
normal = self.normal_spline((0, 1)) # We only need the first two indices
|
|
373
382
|
|
|
374
383
|
theta = np.sqrt(2) # Arbitrary starting value for theta (picked one in [0, pi/2] unlikely to be a stationary point)
|
|
375
384
|
# Try different theta values until no border or turning points are degenerate.
|
|
@@ -381,7 +390,6 @@ def contours(self):
|
|
|
381
390
|
abort = False
|
|
382
391
|
|
|
383
392
|
# Construct the turning point determinant.
|
|
384
|
-
normal = self.normal_spline((0, 1)) # We only need the first two indices
|
|
385
393
|
turningPointDeterminant = normal.dot((cosTheta, sinTheta))
|
|
386
394
|
|
|
387
395
|
# Find intersections with u and v boundaries.
|
|
@@ -496,6 +504,20 @@ def contours(self):
|
|
|
496
504
|
# (3) Take all the points found in Step (1) and Step (2) and order them by distance in the theta direction from the origin.
|
|
497
505
|
points.sort()
|
|
498
506
|
|
|
507
|
+
# Extra step not in the paper: Add a panel between two consecutive turning points to uniquely determine contours between them.
|
|
508
|
+
if len(points) > 1:
|
|
509
|
+
i = 0
|
|
510
|
+
previousPoint = points[i]
|
|
511
|
+
while i < len(points) - 1:
|
|
512
|
+
i += 1
|
|
513
|
+
point = points[i]
|
|
514
|
+
if not previousPoint.onBoundary and not point.onBoundary and point.d - previousPoint.d > epsilon:
|
|
515
|
+
# We have two consecutive turning points on separate panels.
|
|
516
|
+
# Insert a panel in between them, with the uvw value of None, since there is no zero associated.
|
|
517
|
+
points.insert(i, Point(0.5 * (previousPoint.d + point.d), 0.0, False, None))
|
|
518
|
+
i += 1
|
|
519
|
+
previousPoint = point
|
|
520
|
+
|
|
499
521
|
# (4) Initialize an ordered list of contours. No contours will be on the list at first.
|
|
500
522
|
currentContourPoints = [] # Holds contours currently being identified
|
|
501
523
|
contourPoints = [] # Hold contours already identified
|
|
@@ -541,29 +563,34 @@ def contours(self):
|
|
|
541
563
|
# the list. Go back to Step (5).
|
|
542
564
|
# First, construct panel, whose zeros lie along the panel boundary, u * cosTheta + v * sinTheta - d = 0.
|
|
543
565
|
panel.coefs[self.nDep] -= point.d
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
566
|
+
|
|
567
|
+
if point.uvw is None:
|
|
568
|
+
# For an inserted panel between two consecutive turning points, just find zeros along the panel.
|
|
569
|
+
panelPoints = panel.zeros()
|
|
570
|
+
else:
|
|
571
|
+
# Split panel below and above the known zero point.
|
|
572
|
+
# This avoids extra computation and the high-zero at the known zero point.
|
|
573
|
+
panelPoints = [point.uvw]
|
|
574
|
+
# To split the panel, we need to determine the offset from the point.
|
|
575
|
+
# Since the objective function (self) is zero and its derivative is zero at the point,
|
|
576
|
+
# we use second derivatives to determine when the objective function will likely grow
|
|
577
|
+
# evaluationEpsilon above zero again.
|
|
578
|
+
wrt = [0] * self.nInd
|
|
579
|
+
wrt[0] = 2
|
|
580
|
+
selfUU = self.derivative(wrt, point.uvw)
|
|
581
|
+
wrt[0] = 1
|
|
582
|
+
wrt[1] = 1
|
|
583
|
+
selfUV = self.derivative(wrt, point.uvw)
|
|
584
|
+
wrt[0] = 0
|
|
585
|
+
wrt[1] = 2
|
|
586
|
+
selfVV = self.derivative(wrt, point.uvw)
|
|
587
|
+
offset = np.sqrt(2.0 * evaluationEpsilon / \
|
|
588
|
+
np.linalg.norm(selfUU * sinTheta * sinTheta - 2.0 * selfUV * sinTheta * cosTheta + selfVV * cosTheta * cosTheta))
|
|
589
|
+
# Now, we can find the zeros of the split panel, checking to ensure each panel is within bounds first.
|
|
590
|
+
if point.uvw[0] + sinTheta * offset < 1.0 - epsilon and epsilon < point.uvw[1] - cosTheta * offset:
|
|
591
|
+
panelPoints += panel.trim(((point.uvw[0] + sinTheta * offset, 1.0), (0.0, point.uvw[1] - cosTheta * offset)) + ((None, None),) * (self.nInd - 2)).zeros()
|
|
592
|
+
if epsilon < point.uvw[0] - sinTheta * offset and point.uvw[1] + cosTheta * offset < 1.0 - epsilon:
|
|
593
|
+
panelPoints += panel.trim(((0.0, point.uvw[0] - sinTheta * offset), (point.uvw[1] + cosTheta * offset, 1.0)) + ((None, None),) * (self.nInd - 2)).zeros()
|
|
567
594
|
# Sort zero points by their position along the panel boundary (using vector orthogonal to its normal).
|
|
568
595
|
panelPoints.sort(key=lambda uvw: uvw[1] * cosTheta - uvw[0] * sinTheta)
|
|
569
596
|
# Add d back to prepare for next turning point.
|
|
@@ -571,7 +598,7 @@ def contours(self):
|
|
|
571
598
|
# Go through panel points, adding them to existing contours, creating new ones, or closing old ones.
|
|
572
599
|
adjustment = 0 # Adjust index after a contour point is added or removed.
|
|
573
600
|
for i, uvw in zip(range(len(panelPoints)), panelPoints):
|
|
574
|
-
if np.allclose(point.uvw, uvw):
|
|
601
|
+
if point.uvw is not None and np.allclose(point.uvw, uvw):
|
|
575
602
|
if point.det > 0.0:
|
|
576
603
|
# Insert the turning point twice (second one appears before the first one in the points list).
|
|
577
604
|
currentContourPoints.insert(i, [True, point.uvw]) # True indicates end point
|
|
@@ -594,6 +621,9 @@ def contours(self):
|
|
|
594
621
|
# Now we just need to create splines for those contours using the Spline.contour method.
|
|
595
622
|
splineContours = []
|
|
596
623
|
for points in contourPoints:
|
|
597
|
-
|
|
624
|
+
contour = bspy.spline.Spline.contour(self, points[1:]) # Skip endPoint boolean at start of points list
|
|
625
|
+
# Transform the contour to self's original domain.
|
|
626
|
+
contour.coefs = (contour.coefs.T * (domain[1] - domain[0]) + domain[0]).T
|
|
627
|
+
splineContours.append(contour)
|
|
598
628
|
|
|
599
629
|
return splineContours
|
|
@@ -573,10 +573,10 @@ def normal_spline(self, indices=None):
|
|
|
573
573
|
ord += order - 1 if ccm[i, j] and index != j else 0
|
|
574
574
|
newOrd = max(newOrd, ord)
|
|
575
575
|
newOrder.append(newOrd)
|
|
576
|
-
uniqueKnots, counts = np.unique(knots, return_counts=True)
|
|
576
|
+
uniqueKnots, counts = np.unique(knots[order - 1:self.nCoef[i] + 1], return_counts=True)
|
|
577
577
|
counts += newOrd - order + 1 # Because we're multiplying all the tangents, the knot elevation is one more
|
|
578
|
-
counts[0]
|
|
579
|
-
counts[-1]
|
|
578
|
+
counts[0] = newOrd # But not at the endpoints, which are full order as usual
|
|
579
|
+
counts[-1] = newOrd # But not at the endpoints, which are full order as usual
|
|
580
580
|
newKnots.append(np.repeat(uniqueKnots, counts))
|
|
581
581
|
# Also calculate the total number of coefficients, capturing how it progressively increases, and
|
|
582
582
|
# using that calculation to span uvw from the starting knot to the end for each variable.
|
|
@@ -592,7 +592,7 @@ def test_blossom():
|
|
|
592
592
|
maxError = max(maxError, np.linalg.norm(myCurve.coefs[:,i] - coef))
|
|
593
593
|
assert maxError <= np.finfo(float).eps
|
|
594
594
|
|
|
595
|
-
def
|
|
595
|
+
def test_contours():
|
|
596
596
|
maxError = 0.0
|
|
597
597
|
F = lambda x: (x[0] - x[2], x[1] - x[3], x[0]*x[0] + x[1]*x[1]/9.0 - 1.0 + x[2]*x[2] + x[3]*x[3]/9.0 - 1.0)
|
|
598
598
|
spline = bspy.Spline.contour(F, ((1.0, 0.0, 1.0, 0.0), (0.0, 3.0, 0.0, 3.0)))
|
|
@@ -601,6 +601,41 @@ def test_contour():
|
|
|
601
601
|
maxError = max(maxError, np.linalg.norm(F(x)))
|
|
602
602
|
assert maxError <= 0.05
|
|
603
603
|
|
|
604
|
+
maxError = 0.0
|
|
605
|
+
order = 3
|
|
606
|
+
knots = [0.0] * order + [1.0] * order
|
|
607
|
+
nCoef = len(knots) - order
|
|
608
|
+
spline = bspy.Spline(2, 1, (order, order), (nCoef, nCoef), (knots, knots), \
|
|
609
|
+
(((1.0, 0.0, 1.0), (0.0, -5.0, 0.0), (1.0, 0.0, 1.0)),))
|
|
610
|
+
contours = spline.contours()
|
|
611
|
+
for contour in contours:
|
|
612
|
+
for t in np.linspace(0.0, 1.0, 11):
|
|
613
|
+
uvw = contour((t,))
|
|
614
|
+
maxError = max(maxError, np.linalg.norm(spline(uvw)))
|
|
615
|
+
assert maxError <= 0.05
|
|
616
|
+
|
|
617
|
+
return # Comment this line to run the following lengthy test
|
|
618
|
+
|
|
619
|
+
maxError = 0.0
|
|
620
|
+
F = lambda u , v : (u ** 2 + (v - 3/4) ** 2 - 1/25) * \
|
|
621
|
+
((u - 2/5) ** 2 + (v - 3/5) ** 2 - 1/25) * \
|
|
622
|
+
(u ** 2 + (v - 3/2) ** 2 - 25/16) * \
|
|
623
|
+
((u - 1) ** 2 + (v - 3/10) ** 2 - 1/25)
|
|
624
|
+
order = 9
|
|
625
|
+
knots = [0.0] * order + [1.0] * order
|
|
626
|
+
nCoef = order
|
|
627
|
+
points = []
|
|
628
|
+
for u in np.linspace(0.0, 1.0, nCoef):
|
|
629
|
+
for v in np.linspace(0.0, 1.0, nCoef):
|
|
630
|
+
points.append((u, v, F(u, v)))
|
|
631
|
+
spline = bspy.Spline.least_squares(2, 1, (order, order), points, (knots, knots))
|
|
632
|
+
contours = spline.contours()
|
|
633
|
+
for contour in contours:
|
|
634
|
+
for t in np.linspace(0.0, 1.0, 11):
|
|
635
|
+
uvw = contour((t,))
|
|
636
|
+
maxError = max(maxError, np.linalg.norm(spline(uvw)))
|
|
637
|
+
assert maxError <= 0.05
|
|
638
|
+
|
|
604
639
|
def test_contract():
|
|
605
640
|
maxError = 0.0
|
|
606
641
|
contracted = mySurface.contract([.25, None])
|
|
@@ -804,29 +839,6 @@ def test_integral():
|
|
|
804
839
|
assert maxError <= np.finfo(float).eps
|
|
805
840
|
|
|
806
841
|
def test_intersect():
|
|
807
|
-
maxError = 0.0
|
|
808
|
-
F = lambda u , v : (u ** 2 + (v - 3/4) ** 2 - 1/25) * \
|
|
809
|
-
((u - 2/5) ** 2 + (v - 3/5) ** 2 - 1/25) * \
|
|
810
|
-
(u ** 2 + (v - 3/2) ** 2 - 25/16) * \
|
|
811
|
-
((u - 1) ** 2 + (v - 3/10) ** 2 - 1/25)
|
|
812
|
-
|
|
813
|
-
order = 9
|
|
814
|
-
knots = [0.0] * order + [1.0] * order
|
|
815
|
-
nCoef = order
|
|
816
|
-
points = []
|
|
817
|
-
for u in np.linspace(0.0, 1.0, nCoef):
|
|
818
|
-
for v in np.linspace(0.0, 1.0, nCoef):
|
|
819
|
-
points.append((u, v, F(u, v)))
|
|
820
|
-
spline = bspy.Spline.least_squares(2, 1, (order, order), points, (knots, knots))
|
|
821
|
-
|
|
822
|
-
return # Comment this line to run the following lengthy test
|
|
823
|
-
|
|
824
|
-
contours = spline.contours()
|
|
825
|
-
for contour in contours:
|
|
826
|
-
for t in np.linspace(0.0, 1.0, 11):
|
|
827
|
-
uvw = contour((t,))
|
|
828
|
-
maxError = max(maxError, np.linalg.norm(spline(uvw)))
|
|
829
|
-
assert maxError <= np.finfo(float).eps ** 0.2
|
|
830
842
|
|
|
831
843
|
return # Comment this line to run the following additional really lengthy test
|
|
832
844
|
|
|
@@ -1064,6 +1076,11 @@ def test_zeros():
|
|
|
1064
1076
|
|
|
1065
1077
|
tolerance = 1.0e-14
|
|
1066
1078
|
|
|
1079
|
+
spline = bspy.Spline(1, 1, (3,), (3,), [[0., 0., 0., 1., 1., 1.]], [[-1.2, -0.2, 0.8]], 0.0, {})
|
|
1080
|
+
expectedRoots = (0.6,)
|
|
1081
|
+
roots = spline.zeros()
|
|
1082
|
+
check_1D_roots(expectedRoots, roots, tolerance)
|
|
1083
|
+
|
|
1067
1084
|
spline = bspy.Spline(1, 1, (4,), (4,), ((0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0),), ((1.0, -2.0, -2.0, 1.0),))
|
|
1068
1085
|
expectedRoots = (0.12732200375003502, 0.8726779962499649)
|
|
1069
1086
|
roots = spline.zeros()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|