bspy 1.2.2__py3-none-any.whl → 1.2.3__py3-none-any.whl

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.
@@ -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
- projectedLeftStep = max(0.0, adjustedLeftStep)
71
- projectedRightStep = min(1.0, adjustedRightStep)
72
- if projectedLeftStep <= projectedRightStep:
73
- trimmedSpline = mySpline.trim(((projectedLeftStep, projectedRightStep),))
74
- myZeros = refine(trimmedSpline, intervalSize, functionMax)
75
- else:
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
- # Split panel below and above the known zero point.
545
- # This avoids extra computation and the high-zero at the known zero point.
546
- panelPoints = [point.uvw]
547
- # To split the panel, we need to determine the offset from the point.
548
- # Since the objective function (self) is zero and its derivative is zero at the point,
549
- # we use second derivatives to determine when the objective function will likely grow
550
- # evaluationEpsilon above zero again.
551
- wrt = [0] * self.nInd
552
- wrt[0] = 2
553
- selfUU = self.derivative(wrt, point.uvw)
554
- wrt[0] = 1
555
- wrt[1] = 1
556
- selfUV = self.derivative(wrt, point.uvw)
557
- wrt[0] = 0
558
- wrt[1] = 2
559
- selfVV = self.derivative(wrt, point.uvw)
560
- offset = np.sqrt(2.0 * evaluationEpsilon / \
561
- np.linalg.norm(selfUU * sinTheta * sinTheta - 2.0 * selfUV * sinTheta * cosTheta + selfVV * cosTheta * cosTheta))
562
- # Now, we can find the zeros of the split panel, checking to ensure each panel is within bounds first.
563
- if point.uvw[0] + sinTheta * offset < 1.0 - epsilon and epsilon < point.uvw[1] - cosTheta * offset:
564
- panelPoints += panel.trim(((point.uvw[0] + sinTheta * offset, 1.0), (0.0, point.uvw[1] - cosTheta * offset)) + ((None, None),) * (self.nInd - 2)).zeros()
565
- if epsilon < point.uvw[0] - sinTheta * offset and point.uvw[1] + cosTheta * offset < 1.0 - epsilon:
566
- panelPoints += panel.trim(((0.0, point.uvw[0] - sinTheta * offset), (point.uvw[1] + cosTheta * offset, 1.0)) + ((None, None),) * (self.nInd - 2)).zeros()
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
- splineContours.append(bspy.spline.Spline.contour(self, points[1:])) # Skip endPoint boolean at start of points list
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] -= 1 # But not at the endpoints, which get reduced by one when taking the derivative
579
- counts[-1] -= 1 # But not at the endpoints, which get reduced by one when taking the derivative
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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bspy
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: Library for manipulating and rendering non-uniform b-splines
5
5
  Home-page: http://github.com/ericbrec/bspy
6
6
  Author: Eric Brechner
@@ -2,14 +2,14 @@ bspy/__init__.py,sha256=XRBelbYtbhB6AWKh8DTsWVMDIteVxUm7Cf_S1fkqeEs,851
2
2
  bspy/_spline_domain.py,sha256=29_Mp0bn9_8z4ltu__DG9_NZuCiSuol0tRgAeIuzXZI,27826
3
3
  bspy/_spline_evaluation.py,sha256=i0qPy6UNe1MeoCOxF5LjTtloP6HUnxtxPHseAUAlmJU,6376
4
4
  bspy/_spline_fitting.py,sha256=NKNUfHfHYkQLlK_4i5oF7MIS59wrCqVKkPXHDcDGwIc,20552
5
- bspy/_spline_intersection.py,sha256=J6OO3-pDz4ozklT8Mn-TXf0AaH_07C6DN9co_9xur4w,31442
6
- bspy/_spline_operations.py,sha256=mkp1FeFNRDRyWln6-9Z0GBHZUB9FnXYwjpJWYHccs5U,35722
5
+ bspy/_spline_intersection.py,sha256=olDQpLohlF6DhNshMY0kriitlypXHNVXPcMcY3NUnGs,33110
6
+ bspy/_spline_operations.py,sha256=8VIVLGgYln7lLplgVBpnMVHMWbjigvir-LHE8tusZ-A,35715
7
7
  bspy/bspyApp.py,sha256=UxmNp_IGaG7gMVexUmA7YfuwX5iuMBr7ibW3DRan8R8,14524
8
8
  bspy/drawableSpline.py,sha256=Ha7wh__aRTpGTr3cEDG49SeAyRrKtfMUQYkEZ3w9OJw,17004
9
9
  bspy/spline.py,sha256=WIPhSQu09laxQdtjpp4XDO8RscYRin4hF8ERVHLeE1o,59087
10
10
  bspy/splineOpenGLFrame.py,sha256=77rikAbAood76cf5w1Idj7wciTwPV-Bn-yGSuRFvlJM,60616
11
- bspy-1.2.2.dist-info/LICENSE,sha256=nLfJULN68Jw6GfCJp4xeMksGuRdyWNdgEsZGjw2twig,1091
12
- bspy-1.2.2.dist-info/METADATA,sha256=ZMkhzIDDgtW6glsa2CrQ1nruhy0-AAkdtHmliK6iwcE,3777
13
- bspy-1.2.2.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92
14
- bspy-1.2.2.dist-info/top_level.txt,sha256=fotZnJn6aCwgUbBEV3hslIko7Nw-eqtHLq2eyJLlFsY,5
15
- bspy-1.2.2.dist-info/RECORD,,
11
+ bspy-1.2.3.dist-info/LICENSE,sha256=nLfJULN68Jw6GfCJp4xeMksGuRdyWNdgEsZGjw2twig,1091
12
+ bspy-1.2.3.dist-info/METADATA,sha256=uEuJAnmqnP85Zs8qInKp4JzRYPo_GTp7OJ08bisn9No,3777
13
+ bspy-1.2.3.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92
14
+ bspy-1.2.3.dist-info/top_level.txt,sha256=fotZnJn6aCwgUbBEV3hslIko7Nw-eqtHLq2eyJLlFsY,5
15
+ bspy-1.2.3.dist-info/RECORD,,
File without changes
File without changes