bspy 4.0__py3-none-any.whl → 4.1__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.
bspy/spline.py CHANGED
@@ -8,6 +8,7 @@ import bspy._spline_intersection
8
8
  import bspy._spline_fitting
9
9
  import bspy._spline_operations
10
10
 
11
+ @Manifold.register
11
12
  class Spline(Manifold):
12
13
  """
13
14
  A class to model, represent, and process piecewise polynomial tensor product
@@ -313,6 +314,35 @@ class Spline(Manifold):
313
314
  """
314
315
  return bspy._spline_domain.common_basis(splines, indMap)
315
316
 
317
+ def complete_slice(self, slice, solid):
318
+ """
319
+ Add any missing inherent (implicit) boundaries of this spline's domain to the given slice of the
320
+ given solid that are needed to make the slice valid and complete.
321
+
322
+ Parameters
323
+ ----------
324
+ slice : `solid.Solid`
325
+ The slice of the given solid formed by the spline. The slice may be incomplete, missing some of the
326
+ spline's inherent domain boundaries. Its dimension must match `self.domain_dimension()`.
327
+
328
+ solid : `solid.Solid`
329
+ The solid being sliced by the manifold. Its dimension must match `self.range_dimension()`.
330
+
331
+ See Also
332
+ --------
333
+ `solid.Solid.slice` : slice the solid by a manifold.
334
+ `domain` : Return the domain of a spline.
335
+
336
+ Notes
337
+ -----
338
+ A spline's inherent domain is determined by its knot array for each dimension. This method only works for
339
+ nInd of 1 or 2.
340
+ """
341
+ if self.domain_dimension() != slice.dimension: raise ValueError("Spline domain dimension must match slice dimension")
342
+ if self.range_dimension() != solid.dimension: raise ValueError("Spline range dimension must match solid dimension")
343
+ if slice.dimension != 1 and slice.dimension != 2: raise ValueError("Only works for nInd = 1 or 2")
344
+ return bspy._spline_intersection.complete_slice(self, slice, solid)
345
+
316
346
  @staticmethod
317
347
  def cone(radius1, radius2, height, tolerance = None):
318
348
  """
@@ -518,21 +548,16 @@ class Spline(Manifold):
518
548
  indMap = [(mapping, mapping, True) if np.isscalar(mapping) else (*mapping, True) for mapping in indMap]
519
549
  return bspy._spline_operations.multiplyAndConvolve(self, other, indMap, productType)
520
550
 
521
- def copy(self, metadata={}):
551
+ def copy(self):
522
552
  """
523
553
  Create a copy of a spline.
524
-
525
- Parameters
526
- ----------
527
- metadata : `dict`, optional
528
- A dictionary of ancillary data to store with the spline. Default is {}.
529
554
 
530
555
  Returns
531
556
  -------
532
557
  spline : `Spline`
533
558
  The spline copy.
534
559
  """
535
- return type(self)(self.nInd, self.nDep, self.order, self.nCoef, self.knots, self.coefs, metadata)
560
+ return type(self)(self.nInd, self.nDep, self.order, self.nCoef, self.knots, self.coefs, self.metadata)
536
561
 
537
562
  def cross(self, vector):
538
563
  """
@@ -693,7 +718,7 @@ class Spline(Manifold):
693
718
  Returns
694
719
  -------
695
720
  bounds : `numpy.array`
696
- nInd x 2 array of the upper and lower bounds on each of the independent variables.
721
+ nInd x 2 array of the lower and upper bounds on each of the independent variables.
697
722
 
698
723
  See Also
699
724
  --------
@@ -702,6 +727,17 @@ class Spline(Manifold):
702
727
  """
703
728
  return bspy._spline_evaluation.domain(self)
704
729
 
730
+ def domain_dimension(self):
731
+ """
732
+ Return the domain dimension of a spline (nInd).
733
+
734
+ Returns
735
+ -------
736
+ dimension : `int`
737
+ The dimension of the spline's domain (nInd)
738
+ """
739
+ return self.nInd
740
+
705
741
  def dot(self, vector):
706
742
  """
707
743
  Dot product a spline by the given vector.
@@ -839,7 +875,7 @@ class Spline(Manifold):
839
875
  Parameters
840
876
  ----------
841
877
  newDomain : array-like
842
- nInd x 2 array of the new upper and lower bounds on each of the independent variables (same form as
878
+ nInd x 2 array of the new lower and upper bounds on each of the independent variables (same form as
843
879
  returned from `domain`). If a bound is None or nan then the original bound (and knots) are left unchanged.
844
880
 
845
881
  continuityOrder : `int`
@@ -859,6 +895,23 @@ class Spline(Manifold):
859
895
  """
860
896
  return bspy._spline_domain.extrapolate(self, newDomain, continuityOrder)
861
897
 
898
+ def flip_normal(self):
899
+ """
900
+ Flip the direction of the normal.
901
+
902
+ Returns
903
+ -------
904
+ spline : `Spline`
905
+ The spline with flipped normal. The spline retains the same tangent space.
906
+
907
+ See Also
908
+ --------
909
+ `solid.Solid.complement` : Return the complement of the solid: whatever was inside is outside and vice-versa.
910
+ """
911
+ spline = self.copy()
912
+ spline.metadata["flipNormal"] = not self.metadata.get("flipNormal", False)
913
+ return spline
914
+
862
915
  def fold(self, foldedInd):
863
916
  """
864
917
  Fold the coefficients of a spline's indicated independent variables into the coefficients of the remaining independent variables, retaining the
@@ -937,6 +990,42 @@ class Spline(Manifold):
937
990
  """
938
991
  return bspy._spline_fitting.four_sided_patch(bottom, right, top, left, surfParam)
939
992
 
993
+ @staticmethod
994
+ def from_dict(dictionary):
995
+ """
996
+ Create a `Spline` from a data in a `dict`.
997
+
998
+ Parameters
999
+ ----------
1000
+ dictionary : `dict`
1001
+ The `dict` containing `Spline` data.
1002
+
1003
+ Returns
1004
+ -------
1005
+ spline : `Spline`
1006
+
1007
+ See Also
1008
+ --------
1009
+ `to_dict` : Return a `dict` with `Spline` data.
1010
+ """
1011
+ return Spline(dictionary["nInd"], dictionary["nDep"], dictionary["order"], dictionary["nCoef"],
1012
+ [np.array(knots) for knots in dictionary["knots"]], np.array(dictionary["coefs"]), dictionary["metadata"])
1013
+
1014
+ def full_domain(self):
1015
+ """
1016
+ Return a solid that represents the full domain of the spline.
1017
+
1018
+ Returns
1019
+ -------
1020
+ domain : `Solid`
1021
+ The full (untrimmed) domain of the spline.
1022
+
1023
+ See Also
1024
+ --------
1025
+ `Boundary` : A portion of the boundary of a solid.
1026
+ """
1027
+ return bspy._spline_intersection.full_domain(self)
1028
+
940
1029
  def geodesic(self, uvStart, uvEnd, tolerance = 1.0e-6):
941
1030
  """
942
1031
  Determine a geodesic between two points on a surface
@@ -1137,12 +1226,13 @@ class Spline(Manifold):
1137
1226
  --------
1138
1227
  `zeros` : Find the roots of a spline (nInd must match nDep).
1139
1228
  `contours` : Find all the contour curves of a spline.
1229
+ `solid.Solid.slice` : slice the solid by a manifold.
1140
1230
 
1141
1231
  Notes
1142
1232
  -----
1143
1233
  Uses `zeros` to find all intersection points and `contours` to find all the intersection curves.
1144
1234
  """
1145
- if not(self.nDep == other.nDep): raise ValueError("The number of dependent variables for both splines much match.")
1235
+ if not(self.range_dimension() == other.range_dimension()): raise ValueError("The number of dependent variables for both splines much match.")
1146
1236
  return bspy._spline_intersection.intersect(self, other)
1147
1237
 
1148
1238
  def jacobian(self, uvw):
@@ -1343,9 +1433,8 @@ class Spline(Manifold):
1343
1433
  if isinstance(splineData, dict):
1344
1434
  splineData = [splineData]
1345
1435
  for splineDict in splineData:
1346
- splines.append(Spline(splineDict["nInd"], splineDict["nDep"], splineDict["order"], splineDict["nCoef"],
1347
- [np.array(knots) for knots in splineDict["knots"]], np.array(splineDict["coefs"]), splineDict["metadata"]))
1348
- return splines
1436
+ splines.append(Spline.from_dict(splineDict))
1437
+ return splines
1349
1438
 
1350
1439
  def multiply(self, other, indMap = None, productType = 'S'):
1351
1440
  """
@@ -1487,10 +1576,21 @@ class Spline(Manifold):
1487
1576
  def range_bounds(self):
1488
1577
  """
1489
1578
  Return the range of a spline as lower and upper bounds on each of the
1490
- dependent variables
1579
+ dependent variables.
1491
1580
  """
1492
1581
  return bspy._spline_evaluation.range_bounds(self)
1493
1582
 
1583
+ def range_dimension(self):
1584
+ """
1585
+ Return the range dimension of a spline (nDep).
1586
+
1587
+ Returns
1588
+ -------
1589
+ dimension : `int`
1590
+ The dimension of the spline's range (nDep)
1591
+ """
1592
+ return self.nDep
1593
+
1494
1594
  def remove_knot(self, iKnot, nLeft = 0, nRight = 0):
1495
1595
  """
1496
1596
  Remove a single knot from a univariate spline.
@@ -1573,7 +1673,7 @@ class Spline(Manifold):
1573
1673
  Parameters
1574
1674
  ----------
1575
1675
  newDomain : array-like
1576
- nInd x 2 array of the new upper and lower bounds on each of the independent variables (same form as
1676
+ nInd x 2 array of the new lower and upper bounds on each of the independent variables (same form as
1577
1677
  returned from `domain`). If a bound pair is `None` then the original bound (and knots) are left unchanged.
1578
1678
  For example, `[[0.0, 1.0], None]` will reparametrize the first independent variable and leave the second unchanged)
1579
1679
 
@@ -1610,7 +1710,7 @@ class Spline(Manifold):
1610
1710
 
1611
1711
  def revolve(self, angle):
1612
1712
  """
1613
- Rotate the spline to create a surface of revolution (nDep must equal 2,
1713
+ Revolve the spline to create a surface of revolution (nDep must equal 2,
1614
1714
  first dimension provides the radius for x and y, second dimension provides the z).
1615
1715
 
1616
1716
  Parameters
@@ -1698,15 +1798,7 @@ class Spline(Manifold):
1698
1798
  if isinstance(obj, np.ndarray):
1699
1799
  return obj.tolist()
1700
1800
  if isinstance(obj, Spline):
1701
- return {
1702
- "nInd" : obj.nInd,
1703
- "nDep" : obj.nDep,
1704
- "order" : obj.order,
1705
- "nCoef" : obj.nCoef,
1706
- "knots" : obj.knots,
1707
- "coefs" : obj.coefs,
1708
- "metadata" : obj.metadata
1709
- }
1801
+ return obj.to_dict()
1710
1802
  return super().default(obj)
1711
1803
 
1712
1804
  with open(fileName, 'w', encoding='utf-8') as file:
@@ -1891,6 +1983,37 @@ class Spline(Manifold):
1891
1983
  indMap = [(mapping, mapping) if np.isscalar(mapping) else mapping for mapping in indMap]
1892
1984
  return self.add(other.scale(-1.0), indMap)
1893
1985
 
1986
+ def tangent_space(self, uvw):
1987
+ """
1988
+ Return the tangent space of the spline.
1989
+
1990
+ Parameters
1991
+ ----------
1992
+ uvw : array-like
1993
+ The value at which to evaluate the tangent space.
1994
+
1995
+ Returns
1996
+ -------
1997
+ tangentSpace : `numpy.array`
1998
+ The nDep x nInd matrix of tangent vectors (tangents are the columns).
1999
+ """
2000
+ return bspy._spline_evaluation.tangent_space(self, uvw)
2001
+
2002
+ def to_dict(self):
2003
+ """
2004
+ Return a `dict` with `Spline` data.
2005
+
2006
+ Returns
2007
+ -------
2008
+ dictionary : `dict`
2009
+
2010
+ See Also
2011
+ --------
2012
+ `from_dict` : Create a `Spline` from a data in a `dict`.
2013
+ """
2014
+ return {"type" : "Spline", "nInd" : self.nInd, "nDep" : self.nDep, "order" : self.order, "nCoef" : self.nCoef,
2015
+ "knots" : self.knots, "coefs" : self.coefs, "metadata" : self.metadata}
2016
+
1894
2017
  @staticmethod
1895
2018
  def torus(innerRadius, outerRadius, tolerance = None):
1896
2019
  """
@@ -1929,7 +2052,7 @@ class Spline(Manifold):
1929
2052
  """
1930
2053
  return bspy._spline_fitting.torus(innerRadius, outerRadius, tolerance)
1931
2054
 
1932
- def transform(self, matrix):
2055
+ def transform(self, matrix, matrixInverseTranspose = None):
1933
2056
  """
1934
2057
  Transform a spline by the given matrix.
1935
2058
 
@@ -1938,6 +2061,9 @@ class Spline(Manifold):
1938
2061
  matrix : array-like
1939
2062
  An array of size `newNDep`x`nDep` that specifies the transform matrix.
1940
2063
 
2064
+ matrixInverseTranspose : `numpy.array`, optional
2065
+ The inverse transpose of matrix (not used for splines).
2066
+
1941
2067
  Returns
1942
2068
  -------
1943
2069
  spline : `Spline`
@@ -2012,7 +2138,7 @@ class Spline(Manifold):
2012
2138
  Parameters
2013
2139
  ----------
2014
2140
  newDomain : array-like
2015
- nInd x 2 array of the new upper and lower bounds on each of the independent variables (same form as
2141
+ nInd x 2 array of the new lower and upper bounds on each of the independent variables (same form as
2016
2142
  returned from `domain`). If a bound is None or nan then the original bound (and knots) are left unchanged.
2017
2143
 
2018
2144
  Returns
@@ -2027,6 +2153,29 @@ class Spline(Manifold):
2027
2153
  """
2028
2154
  return bspy._spline_domain.trim(self, newDomain)
2029
2155
 
2156
+ def trimmed_range_bounds(self, domainBounds):
2157
+ """
2158
+ Return the trimmed range bounds for the spline.
2159
+
2160
+ Parameters
2161
+ ----------
2162
+ domainBounds : array-like
2163
+ An array with shape (nInd, 2) of lower and upper and lower bounds on each independent variable.
2164
+
2165
+ Returns
2166
+ -------
2167
+ trimmedSpline, rangeBounds : `Spline`, `np.array`
2168
+ A spline trimmed to the given domain bounds, and the range of the trimmed spline given as
2169
+ lower and upper bounds on each dependent variable.
2170
+
2171
+ See Also
2172
+ --------
2173
+ `trim` : Trim the domain of a spline.
2174
+ `range_bounds` : Return the range of a spline as lower and upper bounds on each of the
2175
+ dependent variables.
2176
+ """
2177
+ return bspy._spline_domain.trimmed_range_bounds(self, domainBounds)
2178
+
2030
2179
  def unfold(self, foldedInd, coefficientlessSpline):
2031
2180
  """
2032
2181
  Unfold the coefficients of an original spline's indicated independent variables back into the spline, using the