bspy 4.0__py3-none-any.whl → 4.2__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.
@@ -105,9 +105,9 @@ def confine(self, range_bounds):
105
105
  intersections.sort(key=lambda intersection: intersection[0])
106
106
 
107
107
  # Remove repeat points at start and end.
108
- if intersections[1][0] - intersections[0][0] < epsilon:
108
+ while intersections[1][0] - intersections[0][0] < epsilon:
109
109
  del intersections[1]
110
- if intersections[-1][0] - intersections[-2][0] < epsilon:
110
+ while intersections[-1][0] - intersections[-2][0] < epsilon:
111
111
  del intersections[-2]
112
112
 
113
113
  # Insert order-1 knots at each intersection point.
@@ -308,6 +308,8 @@ def multiplyAndConvolve(self, other, indMap = None, productType = 'S'):
308
308
  # Construct new spline parameters.
309
309
  nInd = self.nInd + other.nInd
310
310
  nDep = other.nDep
311
+ if productType == 'C' and nDep == 2:
312
+ nDep = 1
311
313
  order = [*self.order, *other.order]
312
314
  nCoef = [*self.nCoef, *other.nCoef]
313
315
  knots = [*self.knots, *other.knots]
@@ -628,74 +630,94 @@ def multiplyAndConvolve(self, other, indMap = None, productType = 'S'):
628
630
 
629
631
  return type(self)(nInd, nDep, order, nCoef, knots, coefs, self.metadata)
630
632
 
631
- # Return a matrix of booleans whose [i,j] value indicates if self's partial wrt variable i depends on variable j.
632
- def _cross_correlation_matrix(self):
633
- ccm = np.empty((self.nInd, self.nInd), bool)
634
- for i in range(self.nInd - 1):
635
- tangent = self.differentiate(i)
636
- totalCoefs = tangent.coefs.size // tangent.nDep
637
- ccm[i, i] = True
638
- for j in range(i + 1, self.nInd):
639
- coefs = np.moveaxis(tangent.coefs, (0, j + 1), (-1, -2))
640
- coefs = coefs.reshape(totalCoefs // tangent.nCoef[j], tangent.nCoef[j], tangent.nDep)
641
- match = True
642
- for row in coefs:
643
- first = row[0]
644
- for point in row[1:]:
645
- match = np.allclose(point, first)
646
- if not match:
647
- break
648
- if not match:
649
- break
650
- ccm[i, j] = ccm[j, i] = not match
651
- ccm[-1, -1] = True
652
- return ccm
653
-
654
633
  def normal_spline(self, indices=None):
655
- if abs(self.nInd - self.nDep) != 1: raise ValueError("The number of independent variables must be one less than the number of dependent variables.")
634
+ if abs(self.nInd - self.nDep) != 1: raise ValueError("The number of independent variables must be different than the number of dependent variables.")
656
635
 
657
- # Construct order and knots for generalized cross product of the tangent space.
636
+ # Construct order, nCoef, knots, and sample values for generalized cross product of the tangent space.
658
637
  newOrder = []
659
638
  newKnots = []
660
639
  uvwValues = []
661
640
  nCoefs = []
662
641
  totalCoefs = [1]
663
- ccm = _cross_correlation_matrix(self)
664
- for i, (order, knots) in enumerate(zip(self.order, self.knots)):
665
- # First, calculate the order of the normal for this independent variable.
642
+ for nInd in range(self.nInd):
643
+ knots = None
644
+ counts = None
645
+ maxOrder = 0
646
+ startInd = 0
647
+ endInd = 0
648
+ # First, collect the order, knots, and number of relevant columns for this independent variable.
649
+ for row in self.block:
650
+ rowInd = 0
651
+ for spline in row:
652
+ if rowInd <= nInd < rowInd + spline.nInd:
653
+ ind = nInd - rowInd
654
+ order = spline.order[ind]
655
+ k, c = np.unique(spline.knots[ind][order-1:spline.nCoef[ind]+1], return_counts=True)
656
+ if knots:
657
+ if maxOrder < order:
658
+ counts += order - maxOrder
659
+ maxOrder = order
660
+ endInd = max(endInd, rowInd + spline.nInd)
661
+ for knot, count in zip(k[1:-1], c[1:-1]):
662
+ ix = np.searchsorted(knots, knot)
663
+ if knots[ix] == knot:
664
+ counts[ix] = max(counts[ix], count + maxOrder - order)
665
+ else:
666
+ knots = np.insert(knots, ix, knot)
667
+ counts = np.insert(counts, ix, count + maxOrder - order)
668
+ else:
669
+ knots = k
670
+ counts = c
671
+ maxOrder = order
672
+ startInd = rowInd
673
+ endInd = rowInd + spline.nInd
674
+
675
+ break
676
+
677
+ rowInd += spline.nInd
678
+
679
+ # Next, calculate the order of the normal for this independent variable.
666
680
  # Note that the total order will be one less than usual, because one of
667
681
  # the tangents is the derivative with respect to that independent variable.
668
- newOrd = 0
669
682
  if self.nInd < self.nDep:
670
683
  # If this normal involves all tangents, simply add the degree of each,
671
- # so long as that tangent contains the independent variable.
672
- for j in range(self.nInd):
673
- newOrd += order - 1 if ccm[i, j] else 0
684
+ # so long as that tangent contains the independent variable.
685
+ order = (maxOrder - 1) * (endInd - startInd)
674
686
  else:
675
687
  # If this normal doesn't involve all tangents, find the max order of
676
688
  # each returned combination (as defined by the indices).
677
- for index in range(self.nInd) if indices is None else indices:
689
+ order = 0
690
+ for index in range(startInd, endInd) if indices is None else indices:
678
691
  # The order will be one larger if this independent variable's tangent is excluded by the index.
679
- ord = 0 if index != i else 1
692
+ ord = 0 if index != nInd else 1
680
693
  # Add the degree of each tangent, so long as that tangent contains the
681
694
  # independent variable and is not excluded by the index.
682
- for j in range(self.nInd):
683
- ord += order - 1 if ccm[i, j] and index != j else 0
684
- newOrd = max(newOrd, ord)
685
- newOrder.append(newOrd)
686
- uniqueKnots, counts = np.unique(knots[order - 1:self.nCoef[i] + 1], return_counts=True)
687
- counts += newOrd - order + 1 # Because we're multiplying all the tangents, the knot elevation is one more
688
- counts[0] = newOrd # But not at the endpoints, which are full order as usual
689
- counts[-1] = newOrd # But not at the endpoints, which are full order as usual
690
- newKnots.append(np.repeat(uniqueKnots, counts))
695
+ for ind in range(startInd, endInd):
696
+ ord += maxOrder - 1 if index != ind else 0
697
+ order = max(order, ord)
698
+
699
+ # Now, record the order of this independent variable and adjust the knot counts.
700
+ newOrder.append(order)
701
+ counts += order - maxOrder + 1 # Because we're multiplying all the tangents, the knot elevation is one more
702
+ counts[0] = order # But not at the endpoints, which are full order as usual
703
+ counts[-1] = order # But not at the endpoints, which are full order as usual
704
+ newKnots.append(np.repeat(knots, counts))
705
+
691
706
  # Also calculate the total number of coefficients, capturing how it progressively increases, and
692
707
  # using that calculation to span uvw from the starting knot to the end for each variable.
693
708
  nCoef = len(newKnots[-1]) - newOrder[-1]
694
709
  totalCoefs.append(totalCoefs[-1] * nCoef)
695
- uvwValues.append(bspy.Spline(1, 0, [newOrd], [nCoef], [newKnots[-1]], []).greville())
710
+ knotAverages = bspy.Spline(1, 0, [order], [nCoef], [newKnots[-1]], []).greville()
711
+ for iKnot in range(1, len(knotAverages) - 1):
712
+ if knotAverages[iKnot] == knotAverages[iKnot + 1]:
713
+ knotAverages[iKnot] = 0.5 * (knotAverages[iKnot - 1] + knotAverages[iKnot])
714
+ knotAverages[iKnot + 1] = 0.5 * (knotAverages[iKnot + 1] + knotAverages[iKnot + 2])
715
+ uvwValues.append(knotAverages)
696
716
  nCoefs.append(nCoef)
717
+
718
+ # Construct data points for normal.
697
719
  points = []
698
- ijk = [0 for order in self.order]
720
+ ijk = [0] * self.nInd
699
721
  for i in range(totalCoefs[-1]):
700
722
  uvw = [uvwValues[j][k] for j, k in enumerate(ijk)]
701
723
  points.append(self.normal(uvw, False, indices))
@@ -709,8 +731,7 @@ def normal_spline(self, indices=None):
709
731
  nCoefs.reverse()
710
732
  points = np.reshape(points, [nDep] + nCoefs)
711
733
  points = np.transpose(points, [0] + list(range(self.nInd, 0, -1)))
712
- return bspy.Spline.least_squares(uvwValues, points, order = newOrder, knots = newKnots, metadata = self.metadata)
713
- # return bspy.Spline.least_squares(self.nInd, nDep, newOrder, points, newKnots, 0, self.metadata)
734
+ return bspy.Spline.least_squares(uvwValues, points, order = newOrder, knots = newKnots)
714
735
 
715
736
  def rotate(self, vector, angle):
716
737
  vector = np.atleast_1d(vector)