bspy 3.0.1__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
@@ -1,13 +1,15 @@
1
1
  import numpy as np
2
2
  from os import path
3
3
  import json
4
+ from bspy.manifold import Manifold
4
5
  import bspy._spline_domain
5
6
  import bspy._spline_evaluation
6
7
  import bspy._spline_intersection
7
8
  import bspy._spline_fitting
8
9
  import bspy._spline_operations
9
10
 
10
- class Spline:
11
+ @Manifold.register
12
+ class Spline(Manifold):
11
13
  """
12
14
  A class to model, represent, and process piecewise polynomial tensor product
13
15
  functions (splines) as linear combinations of B-splines.
@@ -178,31 +180,6 @@ class Spline:
178
180
  indMap = [(mapping, mapping) if np.isscalar(mapping) else mapping for mapping in indMap]
179
181
  return bspy._spline_operations.add(self, other, indMap)
180
182
 
181
- def blossom(self, uvw):
182
- """
183
- Compute the blossom of the spline at given parameter values.
184
-
185
- Parameters
186
- ----------
187
- uvwValues : `iterable`
188
- An iterable of length `nInd` that specifies the degree-sized vectors of blossom parameters for each independent variable.
189
-
190
- Returns
191
- -------
192
- value : `numpy.array`
193
- The value of the spline's blossom at the given blossom parameters.
194
-
195
- See Also
196
- --------
197
- `evaluate` : Compute the value of the spline at a given parameter value.
198
-
199
- Notes
200
- -----
201
- Evaluates the blossom based on blossoming algorithm 1 found in Goldman, Ronald N. "Blossoming and knot insertion algorithms for B-spline curves."
202
- Computer Aided Geometric Design 7, no. 1-4 (1990): 69-81.
203
- """
204
- return bspy._spline_evaluation.blossom(self, uvw)
205
-
206
183
  @staticmethod
207
184
  def bspline_values(knot, knots, splineOrder, u, derivativeOrder = 0, taylorCoefs = False):
208
185
  """
@@ -211,7 +188,8 @@ class Spline:
211
188
  Parameters
212
189
  ----------
213
190
  knot : `int`
214
- The rightmost knot in the bspline segment.
191
+ The rightmost knot in the bspline segment. If knot is None, then this value is
192
+ determined by binary search
215
193
 
216
194
  knots : array-like
217
195
  The array of knots for the bspline.
@@ -231,6 +209,10 @@ class Spline:
231
209
 
232
210
  Returns
233
211
  -------
212
+ knot : `int`
213
+ The rightmost knot in the bspline segment. If this is specified on input, then this
214
+ valued is returned. Otherwise, it is computed and then returned.
215
+
234
216
  value : `numpy.array`
235
217
  The value of the bspline or its derivative at the given parameter.
236
218
 
@@ -332,6 +314,35 @@ class Spline:
332
314
  """
333
315
  return bspy._spline_domain.common_basis(splines, indMap)
334
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
+
335
346
  @staticmethod
336
347
  def cone(radius1, radius2, height, tolerance = None):
337
348
  """
@@ -537,21 +548,16 @@ class Spline:
537
548
  indMap = [(mapping, mapping, True) if np.isscalar(mapping) else (*mapping, True) for mapping in indMap]
538
549
  return bspy._spline_operations.multiplyAndConvolve(self, other, indMap, productType)
539
550
 
540
- def copy(self, metadata={}):
551
+ def copy(self):
541
552
  """
542
553
  Create a copy of a spline.
543
-
544
- Parameters
545
- ----------
546
- metadata : `dict`, optional
547
- A dictionary of ancillary data to store with the spline. Default is {}.
548
554
 
549
555
  Returns
550
556
  -------
551
557
  spline : `Spline`
552
558
  The spline copy.
553
559
  """
554
- 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)
555
561
 
556
562
  def cross(self, vector):
557
563
  """
@@ -712,7 +718,7 @@ class Spline:
712
718
  Returns
713
719
  -------
714
720
  bounds : `numpy.array`
715
- 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.
716
722
 
717
723
  See Also
718
724
  --------
@@ -721,6 +727,17 @@ class Spline:
721
727
  """
722
728
  return bspy._spline_evaluation.domain(self)
723
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
+
724
741
  def dot(self, vector):
725
742
  """
726
743
  Dot product a spline by the given vector.
@@ -858,7 +875,7 @@ class Spline:
858
875
  Parameters
859
876
  ----------
860
877
  newDomain : array-like
861
- 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
862
879
  returned from `domain`). If a bound is None or nan then the original bound (and knots) are left unchanged.
863
880
 
864
881
  continuityOrder : `int`
@@ -878,6 +895,23 @@ class Spline:
878
895
  """
879
896
  return bspy._spline_domain.extrapolate(self, newDomain, continuityOrder)
880
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
+
881
915
  def fold(self, foldedInd):
882
916
  """
883
917
  Fold the coefficients of a spline's indicated independent variables into the coefficients of the remaining independent variables, retaining the
@@ -956,13 +990,82 @@ class Spline:
956
990
  """
957
991
  return bspy._spline_fitting.four_sided_patch(bottom, right, top, left, surfParam)
958
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
+
1029
+ def geodesic(self, uvStart, uvEnd, tolerance = 1.0e-6):
1030
+ """
1031
+ Determine a geodesic between two points on a surface
1032
+
1033
+ Parameters
1034
+ ----------
1035
+ uvStart : `array-like`
1036
+ The parameter values for the surface at one end of the desired geodesic.
1037
+
1038
+ uvEnd : `array-like`
1039
+ The parameter values for the surface at the other end of the desired geodesic.
1040
+
1041
+ tolerance : scalar
1042
+ The maximum error in parameter space to which the geodesic should get computed.
1043
+ Defaults to 1.0e-6.
1044
+
1045
+ Returns
1046
+ -------
1047
+ geodesic : `Spline`
1048
+ A spline curve whose range is in the domain of the given surface. The range of the
1049
+ curve is the locus of points whose image under the surface map form the curve of
1050
+ minimum distance between the two given points.
1051
+
1052
+ See Also
1053
+ --------
1054
+ `solve_ode` : Solve an ordinary differential equation using spline collocation.
1055
+
1056
+ Notes
1057
+ -----
1058
+ Solves the second order ODE which are the geodesic equations over the surface.
1059
+ """
1060
+ return bspy._spline_fitting.geodesic(self, uvStart, uvEnd, tolerance)
1061
+
959
1062
  def graph(self):
960
1063
  """
961
1064
  Generate the spline which is the graph of the given spline.
962
1065
 
963
1066
  Returns
964
1067
  -------
965
- spline: `Spline`
1068
+ spline : `Spline`
966
1069
  A spline with nInd independent variables and nInd + nDep dependent variables, the first nInd of
967
1070
  which are just the independent variables themselves. For example, given a scalar
968
1071
  valued function f of two variables u and v, returns the spline of two independent variables whose
@@ -998,7 +1101,7 @@ class Spline:
998
1101
  The Greville abscissae always satisfy the interlacing conditions, so can be used as
999
1102
  valid collocation points, interpolation points, or quadrature points.
1000
1103
  """
1001
- return bspy._spline_operations.greville(self, ind)
1104
+ return bspy._spline_evaluation.greville(self, ind)
1002
1105
 
1003
1106
  def insert_knots(self, newKnots):
1004
1107
  """
@@ -1092,39 +1195,45 @@ class Spline:
1092
1195
 
1093
1196
  def intersect(self, other):
1094
1197
  """
1095
- Intersect two splines.
1198
+ Intersect a spline or hyperplane.
1096
1199
 
1097
1200
  Parameters
1098
1201
  ----------
1099
- other : `Spline`
1100
- The spline to intersect with self (`other.nDep` match match `self.nDep`).
1202
+ other : `Spline` or `Hyperplane`
1203
+ The `Manifold` to intersect with self (must have same range dimension as self).
1101
1204
 
1102
1205
  Returns
1103
1206
  -------
1104
- intersection : `iterable` or `NotImplemented`
1105
- If `self.nInd + other.nInd - self.nDep` is 0, returns an iterable of intersection points in the
1106
- parameter space of the two splines (a vector of size `self.nInd + other.nInd`).
1107
- If `self.nInd + other.nInd - self.nDep` is 1, returns an iterable of `Spline` curves, each of whose domain is [0, 1]
1108
- and each of whose range is in the parameter space of the two splines (a vector of size `self.nInd + other.nInd`).
1109
- If `self.nInd + other.nInd - self.nDep` is < 0 or > 1, `NotImplemented` is returned.
1207
+ intersections : `iterable` (or `NotImplemented` if other is an unknown type of Manifold)
1208
+ A list of intersections between the two manifolds.
1209
+ Each intersection records either a crossing or a coincident region.
1210
+
1211
+ For a crossing, intersection is a `Manifold.Crossing`: (left, right)
1212
+ * left : `Manifold` in the manifold's domain where the manifold and the other cross.
1213
+ * right : `Manifold` in the other's domain where the manifold and the other cross.
1214
+ * Both intersection manifolds have the same domain and range (the crossing between the manifold and the other).
1215
+
1216
+ For a coincident region, intersection is a `Manifold.Coincidence`: (left, right, alignment, transform, inverse, translation)
1217
+ * left : `Solid` in the manifold's domain within which the manifold and the other are coincident.
1218
+ * right : `Solid` in the other's domain within which the manifold and the other are coincident.
1219
+ * alignment : scalar value holding the normal alignment between the manifold and the other (the dot product of their unit normals).
1220
+ * transform : `numpy.array` holding the transform matrix from the manifold's domain to the other's domain.
1221
+ * inverse : `numpy.array` holding the inverse transform matrix from the other's domain to the boundary's domain.
1222
+ * translation : `numpy.array` holding the translation vector from the manifold's domain to the other's domain.
1223
+ * Together transform, inverse, and translation form the mapping from the manifold's domain to the other's domain and vice-versa.
1110
1224
 
1111
1225
  See Also
1112
1226
  --------
1113
1227
  `zeros` : Find the roots of a spline (nInd must match nDep).
1114
1228
  `contours` : Find all the contour curves of a spline.
1229
+ `solid.Solid.slice` : slice the solid by a manifold.
1115
1230
 
1116
1231
  Notes
1117
1232
  -----
1118
1233
  Uses `zeros` to find all intersection points and `contours` to find all the intersection curves.
1119
1234
  """
1120
- if not(self.nDep == other.nDep): raise ValueError("The number of dependent variables for both splines much match.")
1121
- freeParameters = self.nInd + other.nInd - self.nDep
1122
- if freeParameters == 0:
1123
- return self.subtract(other).zeros()
1124
- elif freeParameters == 1:
1125
- return self.subtract(other).contours()
1126
- else:
1127
- return NotImplemented
1235
+ if not(self.range_dimension() == other.range_dimension()): raise ValueError("The number of dependent variables for both splines much match.")
1236
+ return bspy._spline_intersection.intersect(self, other)
1128
1237
 
1129
1238
  def jacobian(self, uvw):
1130
1239
  """
@@ -1324,9 +1433,8 @@ class Spline:
1324
1433
  if isinstance(splineData, dict):
1325
1434
  splineData = [splineData]
1326
1435
  for splineDict in splineData:
1327
- splines.append(Spline(splineDict["nInd"], splineDict["nDep"], splineDict["order"], splineDict["nCoef"],
1328
- [np.array(knots) for knots in splineDict["knots"]], np.array(splineDict["coefs"]), splineDict["metadata"]))
1329
- return splines
1436
+ splines.append(Spline.from_dict(splineDict))
1437
+ return splines
1330
1438
 
1331
1439
  def multiply(self, other, indMap = None, productType = 'S'):
1332
1440
  """
@@ -1468,18 +1576,39 @@ class Spline:
1468
1576
  def range_bounds(self):
1469
1577
  """
1470
1578
  Return the range of a spline as lower and upper bounds on each of the
1471
- dependent variables
1579
+ dependent variables.
1472
1580
  """
1473
1581
  return bspy._spline_evaluation.range_bounds(self)
1474
1582
 
1475
- def remove_knot(self, iKnot):
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
+
1594
+ def remove_knot(self, iKnot, nLeft = 0, nRight = 0):
1476
1595
  """
1477
1596
  Remove a single knot from a univariate spline.
1478
1597
 
1479
1598
  Parameters
1480
1599
  ----------
1481
1600
  iKnot : integer
1482
- index of the knot to remove from the spline
1601
+ Index of the knot to remove from the spline
1602
+
1603
+ nLeft : integer
1604
+ Number of continuity conditions to enforce on the left end. For example, if nLeft = 2,
1605
+ then remove_knot will leave the function value and first derivative unchanged on the
1606
+ left end of the domain.
1607
+
1608
+ nRight : integer
1609
+ Number of continuity conditions to enforce on the right end. For example, if
1610
+ nRight = 3, then remove_knot will leave the function value and first and second
1611
+ derivative values unchanged on the right end of the domain.
1483
1612
 
1484
1613
  Returns
1485
1614
  -------
@@ -1496,9 +1625,9 @@ class Spline:
1496
1625
  -----
1497
1626
  Solves a simple least squares problem
1498
1627
  """
1499
- return bspy._spline_domain.remove_knot(self, iKnot)
1628
+ return bspy._spline_domain.remove_knot(self, iKnot, nLeft, nRight)
1500
1629
 
1501
- def remove_knots(self, tolerance = 1.0e-14):
1630
+ def remove_knots(self, tolerance = 1.0e-14, nLeft = 0, nRight = 0):
1502
1631
  """
1503
1632
  Remove interior knots from a spline.
1504
1633
 
@@ -1509,6 +1638,17 @@ class Spline:
1509
1638
  removed until removal of the next knot would put the pointwise error above the threshold.
1510
1639
  The default is 1.0e-14 which is relative to the size of each of the dependent variables.
1511
1640
 
1641
+ nLeft : integer
1642
+ Number of continuity conditions to enforce on the left end. For example, if nLeft = 2,
1643
+ then remove_knot will leave the function value and first derivative unchanged on the
1644
+ left end of the domain.
1645
+
1646
+ nRight : integer
1647
+ Number of continuity conditions to enforce on the right end. For example, if
1648
+ nRight = 3, then remove_knot will leave the function value and first and second
1649
+ derivative values unchanged on the right end of the domain.
1650
+
1651
+
1512
1652
  Returns
1513
1653
  -------
1514
1654
  spline : `Spline`
@@ -1524,7 +1664,7 @@ class Spline:
1524
1664
  -----
1525
1665
  Calls `remove_knot` repeatedly until no longer possible.
1526
1666
  """
1527
- return bspy._spline_domain.remove_knots(self, tolerance)
1667
+ return bspy._spline_domain.remove_knots(self, tolerance, nLeft, nRight)
1528
1668
 
1529
1669
  def reparametrize(self, newDomain):
1530
1670
  """
@@ -1533,7 +1673,7 @@ class Spline:
1533
1673
  Parameters
1534
1674
  ----------
1535
1675
  newDomain : array-like
1536
- 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
1537
1677
  returned from `domain`). If a bound pair is `None` then the original bound (and knots) are left unchanged.
1538
1678
  For example, `[[0.0, 1.0], None]` will reparametrize the first independent variable and leave the second unchanged)
1539
1679
 
@@ -1570,7 +1710,7 @@ class Spline:
1570
1710
 
1571
1711
  def revolve(self, angle):
1572
1712
  """
1573
- 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,
1574
1714
  first dimension provides the radius for x and y, second dimension provides the z).
1575
1715
 
1576
1716
  Parameters
@@ -1658,15 +1798,7 @@ class Spline:
1658
1798
  if isinstance(obj, np.ndarray):
1659
1799
  return obj.tolist()
1660
1800
  if isinstance(obj, Spline):
1661
- return {
1662
- "nInd" : obj.nInd,
1663
- "nDep" : obj.nDep,
1664
- "order" : obj.order,
1665
- "nCoef" : obj.nCoef,
1666
- "knots" : obj.knots,
1667
- "coefs" : obj.coefs,
1668
- "metadata" : obj.metadata
1669
- }
1801
+ return obj.to_dict()
1670
1802
  return super().default(obj)
1671
1803
 
1672
1804
  with open(fileName, 'w', encoding='utf-8') as file:
@@ -1733,6 +1865,50 @@ class Spline:
1733
1865
  """
1734
1866
  return bspy._spline_fitting.section(xytk)
1735
1867
 
1868
+ def solve_ode(self, nLeft, nRight, FAndF_u, tolerance = 1.0e-6, args = ()):
1869
+ """
1870
+ Numerically solve an ordinary differential equation with boundary conditions.
1871
+
1872
+ Parameters
1873
+ ==========
1874
+ self : `Spline`
1875
+ The initial guess for the solution to the spline. All of the spline parameters, e.g.
1876
+ order, boundary conditions, domain, etc. are all established by the initial guess. The
1877
+ ODE itself must be of the form u^(nOrder)(t) = F(t, u, u', ... , u^(nOrder-1)). self.nDep should
1878
+ be the same as the number of state variables in the ODE.
1879
+
1880
+ nLeft : integer
1881
+ The number of boundary conditions to be imposed on the left side of the domain. The
1882
+ order of the differential equation is assumed to be the sum of nLeft and nRight.
1883
+
1884
+ nRight : integer
1885
+ The number of boundary conditions to be imposed on the right sie of the domain. The
1886
+ order of the differential equation is assumed to be the sum of nLeft and nRight.
1887
+
1888
+ FAndF_u : Python function
1889
+ FAndF_u must have exactly this calling sequence: FAndF_u(t, uData, *args). t is a scalar set
1890
+ to the desired value of the independent variable of the ODE. uData will be a numpy matrix of shape
1891
+ (self.nDep, nOrder) whose columns are (u, ... , u^(nOrder - 1). It must return a numpy
1892
+ vector of length self.nDep and a numpy array whose shape is (self.nDep, self.nDep, nOrder).
1893
+ The first output vector is the value of the forcing function F at (t, uData). The numpy
1894
+ array is the array of partial derivatives with respect to all the numbers in uData. Thus, if
1895
+ this array is called jacobian, then jacobian[:, i, j] is the gradient of the forcing function with
1896
+ respect to uData[i, j].
1897
+
1898
+ tolerance : scalar
1899
+ The relative error to which the ODE should get solved.
1900
+
1901
+ args : tuple
1902
+ Additional arguments to pass to the user-defined function FAndF_u. For example, if FAndF_u has the
1903
+ FAndF_u(t, uData, a, b, c), then args must be a tuple of length 3.
1904
+
1905
+ Notes
1906
+ =====
1907
+ This method uses B-splines as finite elements. The ODE itself is discretized using
1908
+ collocation.
1909
+ """
1910
+ return bspy._spline_fitting.solve_ode(self, nLeft, nRight, FAndF_u, tolerance, args)
1911
+
1736
1912
  @staticmethod
1737
1913
  def sphere(radius, tolerance = None):
1738
1914
  """
@@ -1807,6 +1983,37 @@ class Spline:
1807
1983
  indMap = [(mapping, mapping) if np.isscalar(mapping) else mapping for mapping in indMap]
1808
1984
  return self.add(other.scale(-1.0), indMap)
1809
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
+
1810
2017
  @staticmethod
1811
2018
  def torus(innerRadius, outerRadius, tolerance = None):
1812
2019
  """
@@ -1845,7 +2052,7 @@ class Spline:
1845
2052
  """
1846
2053
  return bspy._spline_fitting.torus(innerRadius, outerRadius, tolerance)
1847
2054
 
1848
- def transform(self, matrix):
2055
+ def transform(self, matrix, matrixInverseTranspose = None):
1849
2056
  """
1850
2057
  Transform a spline by the given matrix.
1851
2058
 
@@ -1854,6 +2061,9 @@ class Spline:
1854
2061
  matrix : array-like
1855
2062
  An array of size `newNDep`x`nDep` that specifies the transform matrix.
1856
2063
 
2064
+ matrixInverseTranspose : `numpy.array`, optional
2065
+ The inverse transpose of matrix (not used for splines).
2066
+
1857
2067
  Returns
1858
2068
  -------
1859
2069
  spline : `Spline`
@@ -1928,7 +2138,7 @@ class Spline:
1928
2138
  Parameters
1929
2139
  ----------
1930
2140
  newDomain : array-like
1931
- 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
1932
2142
  returned from `domain`). If a bound is None or nan then the original bound (and knots) are left unchanged.
1933
2143
 
1934
2144
  Returns
@@ -1943,6 +2153,29 @@ class Spline:
1943
2153
  """
1944
2154
  return bspy._spline_domain.trim(self, newDomain)
1945
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
+
1946
2179
  def unfold(self, foldedInd, coefficientlessSpline):
1947
2180
  """
1948
2181
  Unfold the coefficients of an original spline's indicated independent variables back into the spline, using the