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.
bspy/spline.py CHANGED
@@ -2,12 +2,14 @@ import numpy as np
2
2
  from os import path
3
3
  import json
4
4
  from bspy.manifold import Manifold
5
+ import bspy.spline_block
5
6
  import bspy._spline_domain
6
7
  import bspy._spline_evaluation
7
8
  import bspy._spline_intersection
8
9
  import bspy._spline_fitting
9
10
  import bspy._spline_operations
10
11
 
12
+ @Manifold.register
11
13
  class Spline(Manifold):
12
14
  """
13
15
  A class to model, represent, and process piecewise polynomial tensor product
@@ -123,6 +125,9 @@ class Spline(Manifold):
123
125
  else:
124
126
  return self.scale(other)
125
127
 
128
+ def __neg__(self):
129
+ return self.scale(-1.0)
130
+
126
131
  def __sub__(self, other):
127
132
  if isinstance(other, Spline):
128
133
  return self.subtract(other, [(ix, ix) for ix in range(min(self.nInd, other.nInd))])
@@ -313,6 +318,71 @@ class Spline(Manifold):
313
318
  """
314
319
  return bspy._spline_domain.common_basis(splines, indMap)
315
320
 
321
+ def complete_slice(self, slice, solid):
322
+ """
323
+ Add any missing inherent (implicit) boundaries of this spline's domain to the given slice of the
324
+ given solid that are needed to make the slice valid and complete.
325
+
326
+ Parameters
327
+ ----------
328
+ slice : `solid.Solid`
329
+ The slice of the given solid formed by the spline. The slice may be incomplete, missing some of the
330
+ spline's inherent domain boundaries. Its dimension must match `self.domain_dimension()`.
331
+
332
+ solid : `solid.Solid`
333
+ The solid being sliced by the manifold. Its dimension must match `self.range_dimension()`.
334
+
335
+ See Also
336
+ --------
337
+ `solid.Solid.slice` : slice the solid by a manifold.
338
+ `domain` : Return the domain of a spline.
339
+
340
+ Notes
341
+ -----
342
+ A spline's inherent domain is determined by its knot array for each dimension. This method only works for
343
+ nInd of 1 or 2.
344
+ """
345
+ if self.domain_dimension() != slice.dimension: raise ValueError("Spline domain dimension must match slice dimension")
346
+ if self.range_dimension() != solid.dimension: raise ValueError("Spline range dimension must match solid dimension")
347
+ if slice.dimension != 1 and slice.dimension != 2: raise ValueError("Only works for nInd = 1 or 2")
348
+ return bspy._spline_intersection.complete_slice(self, slice, solid)
349
+
350
+ @staticmethod
351
+ def composition(splines, tolerance = 1.0e-6):
352
+ """
353
+ Construct a spline approximation to a composition of splines sequence.
354
+
355
+ Parameters
356
+ ----------
357
+ splines : `array-like`
358
+ An array of splines. The splines should have the property that the number
359
+ of independent variables of the ith spline should be the same as the number
360
+ of dependent variables of the (i+1)st spline. The number of dependent
361
+ variables of the first spline is arbitrary, as is the number of independent
362
+ variables of the last one. Moreover, the range of the ith spline should be
363
+ a subset of the domain of the (i-1)st spline. The interpretation of the
364
+ sequence is s_0(s_1(... s_(n-1)(u)))).
365
+
366
+ tolerance : `scalar`
367
+ The maximum 2-norm of the difference between the given function and the
368
+ spline fit. Defaults to 1.0e-6.
369
+
370
+ Returns
371
+ -------
372
+ spline : `Spline`
373
+ The spline which approximates the given composition.
374
+
375
+ Notes
376
+ -----
377
+ This currently defaults to a cubic spline. Depending on user experience, this
378
+ may change in the future.
379
+
380
+ See also
381
+ --------
382
+ `fit` : Fit a given function to a specified tolerance.
383
+ """
384
+ return bspy._spline_fitting.composition(splines, tolerance)
385
+
316
386
  @staticmethod
317
387
  def cone(radius1, radius2, height, tolerance = None):
318
388
  """
@@ -379,6 +449,24 @@ class Spline(Manifold):
379
449
  """
380
450
  return bspy._spline_operations.confine(self, range_bounds)
381
451
 
452
+ def continuity(self):
453
+ """
454
+ Return the smoothness of the spline in each of its independent variables.
455
+
456
+ Returns
457
+ -------
458
+ `smoothness` : `iterable`
459
+ An array of length nInd containing the number of times the function is continuously
460
+ in each of the independent variables of the input spline.
461
+
462
+ Notes
463
+ -----
464
+ The value -1 is returned if the spline is discontinuous in that variable. The degree of the spline
465
+ is returned if the spline contains no interior knots even though the spline is an analytic function
466
+ of that variable.
467
+ """
468
+ return bspy._spline_evaluation.continuity(self)
469
+
382
470
  @staticmethod
383
471
  def contour(F, knownXValues, dF = None, epsilon = None, metadata = {}):
384
472
  """
@@ -388,8 +476,8 @@ class Spline(Manifold):
388
476
 
389
477
  Parameters
390
478
  ----------
391
- F : function or `Spline`
392
- A function or spline that takes an array-like argument of length `n` and returns an
479
+ F : function, `Spline`, or `SplineBlock`
480
+ A function, spline, or spline block that takes an array-like argument of length `n` and returns an
393
481
  array-like result of length `n - 1`.
394
482
 
395
483
  knownXValues : `iterable` of array-like
@@ -398,11 +486,12 @@ class Spline(Manifold):
398
486
  All x values must be length `n` and be listed in the order they appear on the contour.
399
487
  `F(x)` for all known x values must be a zero vector of length `n-1`.
400
488
 
401
- dF : `iterable` or `None`, optional
402
- An `iterable` of the `n` functions representing the `n` first derivatives of `F`.
489
+ dF : function, `iterable`, or `None`, optional
490
+ A function that returns the Jacobian of F as an array with shape (n - 1, n).
491
+ Can also be an `iterable` of `n` functions that return the `n` first derivatives of `F`.
403
492
  If `dF` is `None` (the default), the first derivatives will be computed for you.
404
- If `F` is not a spline, computing the first derivatives involves multiple calls to `F`
405
- and can be numerically unstable.
493
+ If `F` is not a spline or spline block, computing the first derivatives involves
494
+ multiple calls to `F` and can be numerically unstable.
406
495
 
407
496
  epsilon : `float`, optional
408
497
  Tolerance for contour precision. Evaluating `F` with contour values will be within epsilon
@@ -425,7 +514,7 @@ class Spline(Manifold):
425
514
  Notes
426
515
  -----
427
516
  The returned spline has constant parametric speed (the length of its derivative is constant).
428
- If `F` is a `Spline`, then the range of the returned contour is confined to the domain of `F`.
517
+ If `F` is a `Spline` or a `SplineBlock`, then the range of the returned contour is confined to the domain of `F`.
429
518
  Implements the algorithm described in section 7 of Grandine, Thomas A.
430
519
  "Applications of contouring." Siam Review 42, no. 2 (2000): 297-316.
431
520
  """
@@ -453,7 +542,7 @@ class Spline(Manifold):
453
542
  The algorithm used to to find all intersection curves is from Grandine, Thomas A., and Frederick W. Klein IV.
454
543
  "A new approach to the surface intersection problem." Computer Aided Geometric Design 14, no. 2 (1997): 111-134.
455
544
  """
456
- return bspy._spline_intersection.contours(self)
545
+ return bspy._spline_intersection.contours(bspy.spline_block.SplineBlock(self))
457
546
 
458
547
  def contract(self, uvw):
459
548
  """
@@ -518,21 +607,16 @@ class Spline(Manifold):
518
607
  indMap = [(mapping, mapping, True) if np.isscalar(mapping) else (*mapping, True) for mapping in indMap]
519
608
  return bspy._spline_operations.multiplyAndConvolve(self, other, indMap, productType)
520
609
 
521
- def copy(self, metadata={}):
610
+ def copy(self):
522
611
  """
523
612
  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
613
 
530
614
  Returns
531
615
  -------
532
616
  spline : `Spline`
533
617
  The spline copy.
534
618
  """
535
- return type(self)(self.nInd, self.nDep, self.order, self.nCoef, self.knots, self.coefs, metadata)
619
+ return type(self)(self.nInd, self.nDep, self.order, self.nCoef, self.knots, self.coefs, self.metadata)
536
620
 
537
621
  def cross(self, vector):
538
622
  """
@@ -556,7 +640,7 @@ class Spline(Manifold):
556
640
 
557
641
  def curvature(self, uvw):
558
642
  """
559
- Compute the curvature of a univariate spline.
643
+ Compute the curvature of a univariate or bivariate spline.
560
644
 
561
645
  Parameters
562
646
  ----------
@@ -566,7 +650,8 @@ class Spline(Manifold):
566
650
  Returns
567
651
  -------
568
652
  value : scalar
569
- The value of the curvature at the given point on the curve.
653
+ The value of the curvature at the given point on the curve or surface. If called on a surface,
654
+ the value will represent the Gaussian curvature of the surface at the given point.
570
655
 
571
656
  Notes
572
657
  -----
@@ -693,7 +778,7 @@ class Spline(Manifold):
693
778
  Returns
694
779
  -------
695
780
  bounds : `numpy.array`
696
- nInd x 2 array of the upper and lower bounds on each of the independent variables.
781
+ nInd x 2 array of the lower and upper bounds on each of the independent variables.
697
782
 
698
783
  See Also
699
784
  --------
@@ -702,6 +787,17 @@ class Spline(Manifold):
702
787
  """
703
788
  return bspy._spline_evaluation.domain(self)
704
789
 
790
+ def domain_dimension(self):
791
+ """
792
+ Return the domain dimension of a spline (nInd).
793
+
794
+ Returns
795
+ -------
796
+ dimension : `int`
797
+ The dimension of the spline's domain (nInd)
798
+ """
799
+ return self.nInd
800
+
705
801
  def dot(self, vector):
706
802
  """
707
803
  Dot product a spline by the given vector.
@@ -839,7 +935,7 @@ class Spline(Manifold):
839
935
  Parameters
840
936
  ----------
841
937
  newDomain : array-like
842
- nInd x 2 array of the new upper and lower bounds on each of the independent variables (same form as
938
+ nInd x 2 array of the new lower and upper bounds on each of the independent variables (same form as
843
939
  returned from `domain`). If a bound is None or nan then the original bound (and knots) are left unchanged.
844
940
 
845
941
  continuityOrder : `int`
@@ -859,6 +955,63 @@ class Spline(Manifold):
859
955
  """
860
956
  return bspy._spline_domain.extrapolate(self, newDomain, continuityOrder)
861
957
 
958
+ @staticmethod
959
+ def fit(domain, f, order = None, knots = None, tolerance = 1.0e-4):
960
+ """
961
+ Fit the function f with a spline to a given tolerance.
962
+
963
+ Parameters
964
+ ----------
965
+ domain - `array-like`
966
+ An nInd x 2 array which specifies the rectangular domain (in nInd dimensions)
967
+ over which the function f is defined. The approximating spline which is the
968
+ output will be defined over the same rectangular domain
969
+
970
+ f : Python function
971
+ The function to approximate. It is a vector-valued function of nDep
972
+ components in nInd variables.
973
+
974
+ order : `array-like`
975
+ An optional integer array of length nInd which specifies the polynomial
976
+ order to use in each of the independent variables. It will default to order
977
+ 4 (degree 3) if None is specified (the default)
978
+
979
+ knots : `array-like`
980
+ The initial knot sequence to use, if given
981
+
982
+ tolerance : `scalar`
983
+ The maximum 2-norm of the difference between the given function and the
984
+ spline fit. Defaults to 1.0e-4.
985
+
986
+ Returns
987
+ -------
988
+ spline : `Spline`
989
+ A spline which approximates the given function to within the specified
990
+ tolerance.
991
+
992
+ See Also
993
+ --------
994
+ `least_squares` : Fit a least squares approximation to given data.
995
+ """
996
+ return bspy._spline_fitting.fit(domain, f, order, knots, tolerance)
997
+
998
+ def flip_normal(self):
999
+ """
1000
+ Flip the direction of the normal.
1001
+
1002
+ Returns
1003
+ -------
1004
+ spline : `Spline`
1005
+ The spline with flipped normal. The spline retains the same tangent space.
1006
+
1007
+ See Also
1008
+ --------
1009
+ `solid.Solid.complement` : Return the complement of the solid: whatever was inside is outside and vice-versa.
1010
+ """
1011
+ spline = self.copy()
1012
+ spline.metadata["flipNormal"] = not self.metadata.get("flipNormal", False)
1013
+ return spline
1014
+
862
1015
  def fold(self, foldedInd):
863
1016
  """
864
1017
  Fold the coefficients of a spline's indicated independent variables into the coefficients of the remaining independent variables, retaining the
@@ -937,6 +1090,42 @@ class Spline(Manifold):
937
1090
  """
938
1091
  return bspy._spline_fitting.four_sided_patch(bottom, right, top, left, surfParam)
939
1092
 
1093
+ @staticmethod
1094
+ def from_dict(dictionary):
1095
+ """
1096
+ Create a `Spline` from data in a `dict`.
1097
+
1098
+ Parameters
1099
+ ----------
1100
+ dictionary : `dict`
1101
+ The `dict` containing `Spline` data.
1102
+
1103
+ Returns
1104
+ -------
1105
+ spline : `Spline`
1106
+
1107
+ See Also
1108
+ --------
1109
+ `to_dict` : Return a `dict` with `Spline` data.
1110
+ """
1111
+ return Spline(dictionary["nInd"], dictionary["nDep"], dictionary["order"], dictionary["nCoef"],
1112
+ [np.array(knots) for knots in dictionary["knots"]], np.array(dictionary["coefs"]), dictionary["metadata"])
1113
+
1114
+ def full_domain(self):
1115
+ """
1116
+ Return a solid that represents the full domain of the spline.
1117
+
1118
+ Returns
1119
+ -------
1120
+ domain : `Solid`
1121
+ The full (untrimmed) domain of the spline.
1122
+
1123
+ See Also
1124
+ --------
1125
+ `Boundary` : A portion of the boundary of a solid.
1126
+ """
1127
+ return bspy._spline_intersection.full_domain(self)
1128
+
940
1129
  def geodesic(self, uvStart, uvEnd, tolerance = 1.0e-6):
941
1130
  """
942
1131
  Determine a geodesic between two points on a surface
@@ -1039,48 +1228,46 @@ class Spline(Manifold):
1039
1228
  """
1040
1229
  return bspy._spline_domain.insert_knots(self, newKnots)
1041
1230
 
1042
- def integral(self, with_respect_to, uvw1, uvw2, returnSpline = False):
1231
+ def integral(self, integrand = None, domain = None):
1043
1232
  """
1044
- Compute the derivative of the spline at given parameter values.
1233
+ Compute the integral of a function composed with a spline. In particular, compute the
1234
+ nInd-dimensional integral over the specified domain of the quantity f(x_0, x_1, ..., x_{nDep - 1})dA,
1235
+ where x_i is the (i+1)-th dependent variable, and dA is the multivariate measure of the spline mapping.
1045
1236
 
1046
1237
  Parameters
1047
1238
  ----------
1048
- with_respect_to : `iterable`
1049
- An iterable of length `nInd` that specifies the integer order of integral for each independent variable.
1050
- A zero-order integral just evaluates the spline normally.
1051
-
1052
- uvw1 : `iterable`
1053
- An iterable of length `nInd` that specifies the lower limit of each independent variable (the parameter values).
1239
+ integrand : Python function, optional
1240
+ A Python function which takes an array-like object of length nDep as input and returns a
1241
+ scalar. If None is specified for integrand, then the function which returns a constant value
1242
+ of one is used. This computes the volume measure of the spline itself (arc length, surface area,
1243
+ volume, etc.) depending on the dimensionality of the spline itself.
1244
+
1245
+ domain : array-like
1246
+ nInd x 2 array of the lower and upper limits of integration for the spline on each of the
1247
+ independent variables. If domain is None, then the actual domain of the spline is used.
1054
1248
 
1055
- uvw2 : `iterable`
1056
- An iterable of length `nInd` that specifies the upper limit of each independent variable (the parameter values).
1057
-
1058
- returnSpline : `boolean`, optional
1059
- A boolean flag that if true returns the integrated spline along with the value of its integral. Default is false.
1060
-
1061
1249
  Returns
1062
1250
  -------
1063
- value : `numpy.ndarray`
1064
- The value of the integral of the spline at the given parameter limits.
1065
-
1066
- spline : `Spline`
1067
- The integrated spline, which is only returned if `returnSpline` is `True`.
1068
-
1251
+ integral_value : `float`
1252
+ The computed value of the specified integral
1253
+
1069
1254
  See Also
1070
1255
  --------
1071
- `integrate` : Integrate a spline with respect to one of its independent variables, returning the resulting spline.
1072
- `evaluate` : Compute the value of the spline at a given parameter value.
1073
- `differentiate` : Differentiate a spline with respect to one of its independent variables, returning the resulting spline.
1074
- `derivative` : Compute the derivative of the spline at a given parameter value.
1256
+ `integrate` : Integrate a spline with respect to one of its independent variables, returning
1257
+ the resulting spline.
1075
1258
 
1076
1259
  Notes
1077
1260
  -----
1078
- The integral method uses the integrate method to integrate the spline `with_respect_to` times for each independent variable.
1079
- Then the method returns that integrated spline's value at `uw2` minus its value at `uw1` (optionally along with the spline).
1080
- The method doesn't calculate the integral directly because the number of operations required is nearly the same as constructing
1081
- the integrated spline.
1261
+ This function is very useful for computing mass properties of splines. If the integrand function
1262
+ returns one, then the volume measure of the spline itself is computed (arc length, surface area,
1263
+ volume, etc.). If the integrand function returns one of the dependent variable values, then the
1264
+ integral will be the first moment of the spline with respect to that variable, making it possible
1265
+ to compute centroids and center of mass. The integrand function should be smooth, but is otherwise
1266
+ unrestricted.
1267
+
1268
+ Attempts to compute the integral to two digits less than machine precision.
1082
1269
  """
1083
- return bspy._spline_evaluation.integral(self, with_respect_to, uvw1, uvw2, returnSpline)
1270
+ return bspy._spline_evaluation.composed_integral(self, integrand, domain)
1084
1271
 
1085
1272
  def integrate(self, with_respect_to = 0):
1086
1273
  """
@@ -1089,7 +1276,7 @@ class Spline(Manifold):
1089
1276
  Parameters
1090
1277
  ----------
1091
1278
  with_respect_to : integer, optional
1092
- The number of the independent variable to integrate. Default is zero.
1279
+ The index of the independent variable to integrate. Default is zero.
1093
1280
 
1094
1281
  Returns
1095
1282
  -------
@@ -1137,12 +1324,13 @@ class Spline(Manifold):
1137
1324
  --------
1138
1325
  `zeros` : Find the roots of a spline (nInd must match nDep).
1139
1326
  `contours` : Find all the contour curves of a spline.
1327
+ `solid.Solid.slice` : slice the solid by a manifold.
1140
1328
 
1141
1329
  Notes
1142
1330
  -----
1143
1331
  Uses `zeros` to find all intersection points and `contours` to find all the intersection curves.
1144
1332
  """
1145
- if not(self.nDep == other.nDep): raise ValueError("The number of dependent variables for both splines much match.")
1333
+ if not(self.range_dimension() == other.range_dimension()): raise ValueError("The number of dependent variables for both splines much match.")
1146
1334
  return bspy._spline_intersection.intersect(self, other)
1147
1335
 
1148
1336
  def jacobian(self, uvw):
@@ -1343,9 +1531,8 @@ class Spline(Manifold):
1343
1531
  if isinstance(splineData, dict):
1344
1532
  splineData = [splineData]
1345
1533
  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
1534
+ splines.append(Spline.from_dict(splineDict))
1535
+ return splines
1349
1536
 
1350
1537
  def multiply(self, other, indMap = None, productType = 'S'):
1351
1538
  """
@@ -1457,7 +1644,7 @@ class Spline(Manifold):
1457
1644
  dependent variables (instead of one less, as is typical). In that case, the normal represents the null space of
1458
1645
  the matrix formed by the tangents of the spline. If the null space is greater than one dimension, the normal will be zero.
1459
1646
  """
1460
- return bspy._spline_operations.normal_spline(self, indices)
1647
+ return bspy._spline_operations.normal_spline(bspy.spline_block.SplineBlock(self), indices)
1461
1648
 
1462
1649
  @staticmethod
1463
1650
  def point(point):
@@ -1487,10 +1674,21 @@ class Spline(Manifold):
1487
1674
  def range_bounds(self):
1488
1675
  """
1489
1676
  Return the range of a spline as lower and upper bounds on each of the
1490
- dependent variables
1677
+ dependent variables.
1491
1678
  """
1492
1679
  return bspy._spline_evaluation.range_bounds(self)
1493
1680
 
1681
+ def range_dimension(self):
1682
+ """
1683
+ Return the range dimension of a spline (nDep).
1684
+
1685
+ Returns
1686
+ -------
1687
+ dimension : `int`
1688
+ The dimension of the spline's range (nDep)
1689
+ """
1690
+ return self.nDep
1691
+
1494
1692
  def remove_knot(self, iKnot, nLeft = 0, nRight = 0):
1495
1693
  """
1496
1694
  Remove a single knot from a univariate spline.
@@ -1573,7 +1771,7 @@ class Spline(Manifold):
1573
1771
  Parameters
1574
1772
  ----------
1575
1773
  newDomain : array-like
1576
- nInd x 2 array of the new upper and lower bounds on each of the independent variables (same form as
1774
+ nInd x 2 array of the new lower and upper bounds on each of the independent variables (same form as
1577
1775
  returned from `domain`). If a bound pair is `None` then the original bound (and knots) are left unchanged.
1578
1776
  For example, `[[0.0, 1.0], None]` will reparametrize the first independent variable and leave the second unchanged)
1579
1777
 
@@ -1610,7 +1808,7 @@ class Spline(Manifold):
1610
1808
 
1611
1809
  def revolve(self, angle):
1612
1810
  """
1613
- Rotate the spline to create a surface of revolution (nDep must equal 2,
1811
+ Revolve the spline to create a surface of revolution (nDep must equal 2,
1614
1812
  first dimension provides the radius for x and y, second dimension provides the z).
1615
1813
 
1616
1814
  Parameters
@@ -1698,15 +1896,7 @@ class Spline(Manifold):
1698
1896
  if isinstance(obj, np.ndarray):
1699
1897
  return obj.tolist()
1700
1898
  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
- }
1899
+ return obj.to_dict()
1710
1900
  return super().default(obj)
1711
1901
 
1712
1902
  with open(fileName, 'w', encoding='utf-8') as file:
@@ -1851,6 +2041,31 @@ class Spline(Manifold):
1851
2041
  `multiply` : Multiply two splines together.
1852
2042
  """
1853
2043
  return bspy._spline_fitting.sphere(radius, tolerance)
2044
+
2045
+ def split(self, minContinuity = 0, breaks = None):
2046
+ """
2047
+ Split a spline into separate spline pieces.
2048
+
2049
+ Parameters
2050
+ ----------
2051
+ minContinuity : `int`, optional
2052
+ The minimum expected continuity of each spline piece. The default is zero, for C0 continuity.
2053
+
2054
+ breaks : `iterable` of length `nInd` or `None`, optional
2055
+ An iterable that specifies the breaks at which to separate the spline.
2056
+ len(breaks[ind]) == 0 if there the spline isn't separated for the `ind` independent variable.
2057
+ If breaks is `None` (the default), the spline will only be separated at discontinuities.
2058
+
2059
+ Returns
2060
+ -------
2061
+ splineArray : array of `Spline`
2062
+ A array of splines with nInd dimensions containing the spline pieces.
2063
+
2064
+ See Also
2065
+ --------
2066
+ `join` : Join a list of splines together into a single spline.
2067
+ """
2068
+ return bspy._spline_domain.split(self, minContinuity, breaks)
1854
2069
 
1855
2070
  def subtract(self, other, indMap = None):
1856
2071
  """
@@ -1891,6 +2106,37 @@ class Spline(Manifold):
1891
2106
  indMap = [(mapping, mapping) if np.isscalar(mapping) else mapping for mapping in indMap]
1892
2107
  return self.add(other.scale(-1.0), indMap)
1893
2108
 
2109
+ def tangent_space(self, uvw):
2110
+ """
2111
+ Return the tangent space of the spline. (Same as Jacobian.)
2112
+
2113
+ Parameters
2114
+ ----------
2115
+ uvw : array-like
2116
+ The value at which to evaluate the tangent space.
2117
+
2118
+ Returns
2119
+ -------
2120
+ tangentSpace : `numpy.array`
2121
+ The nDep x nInd matrix of tangent vectors (tangents are the columns).
2122
+ """
2123
+ return bspy._spline_evaluation.jacobian(self, uvw)
2124
+
2125
+ def to_dict(self):
2126
+ """
2127
+ Return a `dict` with `Spline` data.
2128
+
2129
+ Returns
2130
+ -------
2131
+ dictionary : `dict`
2132
+
2133
+ See Also
2134
+ --------
2135
+ `from_dict` : Create a `Spline` from a data in a `dict`.
2136
+ """
2137
+ return {"type" : "Spline", "nInd" : self.nInd, "nDep" : self.nDep, "order" : self.order, "nCoef" : self.nCoef,
2138
+ "knots" : self.knots, "coefs" : self.coefs, "metadata" : self.metadata}
2139
+
1894
2140
  @staticmethod
1895
2141
  def torus(innerRadius, outerRadius, tolerance = None):
1896
2142
  """
@@ -1929,7 +2175,7 @@ class Spline(Manifold):
1929
2175
  """
1930
2176
  return bspy._spline_fitting.torus(innerRadius, outerRadius, tolerance)
1931
2177
 
1932
- def transform(self, matrix):
2178
+ def transform(self, matrix, matrixInverseTranspose = None):
1933
2179
  """
1934
2180
  Transform a spline by the given matrix.
1935
2181
 
@@ -1938,6 +2184,9 @@ class Spline(Manifold):
1938
2184
  matrix : array-like
1939
2185
  An array of size `newNDep`x`nDep` that specifies the transform matrix.
1940
2186
 
2187
+ matrixInverseTranspose : `numpy.array`, optional
2188
+ The inverse transpose of matrix (not used for splines).
2189
+
1941
2190
  Returns
1942
2191
  -------
1943
2192
  spline : `Spline`
@@ -2012,7 +2261,7 @@ class Spline(Manifold):
2012
2261
  Parameters
2013
2262
  ----------
2014
2263
  newDomain : array-like
2015
- nInd x 2 array of the new upper and lower bounds on each of the independent variables (same form as
2264
+ nInd x 2 array of the new lower and upper bounds on each of the independent variables (same form as
2016
2265
  returned from `domain`). If a bound is None or nan then the original bound (and knots) are left unchanged.
2017
2266
 
2018
2267
  Returns
@@ -2027,6 +2276,31 @@ class Spline(Manifold):
2027
2276
  """
2028
2277
  return bspy._spline_domain.trim(self, newDomain)
2029
2278
 
2279
+ def trimmed_range_bounds(self, domainBounds):
2280
+ """
2281
+ Return the trimmed range bounds for the spline.
2282
+
2283
+ Parameters
2284
+ ----------
2285
+ domainBounds : array-like
2286
+ An array with shape (nInd, 2) of lower and upper and lower bounds on each independent variable.
2287
+
2288
+ Returns
2289
+ -------
2290
+ trimmedSpline : `Spline`
2291
+ A spline trimmed to the given domain bounds.
2292
+
2293
+ rangeBounds : `np.array`
2294
+ The range of the trimmed spline given as lower and upper bounds on each dependent variable.
2295
+
2296
+ See Also
2297
+ --------
2298
+ `trim` : Trim the domain of a spline.
2299
+ `range_bounds` : Return the range of a spline as lower and upper bounds on each of the
2300
+ dependent variables.
2301
+ """
2302
+ return bspy._spline_domain.trimmed_range_bounds(self, domainBounds)
2303
+
2030
2304
  def unfold(self, foldedInd, coefficientlessSpline):
2031
2305
  """
2032
2306
  Unfold the coefficients of an original spline's indicated independent variables back into the spline, using the
@@ -2064,7 +2338,7 @@ class Spline(Manifold):
2064
2338
  """
2065
2339
  return bspy._spline_domain.unfold(self, foldedInd, coefficientlessSpline)
2066
2340
 
2067
- def zeros(self, epsilon=None):
2341
+ def zeros(self, epsilon=None, initialScale=None):
2068
2342
  """
2069
2343
  Find the roots of a spline (nInd must match nDep).
2070
2344
 
@@ -2074,6 +2348,10 @@ class Spline(Manifold):
2074
2348
  Tolerance for root precision. The root will be within epsilon of the actual root.
2075
2349
  The default is the machine epsilon.
2076
2350
 
2351
+ initialScale : array-like, optional
2352
+ The initial scale of each dependent variable (as opposed to the current scale of
2353
+ the spline, which may have been normalized). The default is an array of ones (size nDep).
2354
+
2077
2355
  Returns
2078
2356
  -------
2079
2357
  roots : `iterable`
@@ -2084,7 +2362,7 @@ class Spline(Manifold):
2084
2362
  See Also
2085
2363
  --------
2086
2364
  `intersect` : Intersect two splines.
2087
- `contour` : Fit a spline to the contour defined by `F(x) = 0`.
2365
+ `contours` : Find all the contour curves of a spline whose `nInd` is one larger than its `nDep`.
2088
2366
 
2089
2367
  Notes
2090
2368
  -----
@@ -2097,4 +2375,5 @@ class Spline(Manifold):
2097
2375
  if self.nInd <= 1:
2098
2376
  return bspy._spline_intersection.zeros_using_interval_newton(self)
2099
2377
  else:
2100
- return bspy._spline_intersection.zeros_using_projected_polyhedron(self, epsilon)
2378
+ return bspy._spline_intersection.zeros_using_projected_polyhedron(bspy.spline_block.SplineBlock(self), epsilon, initialScale)
2379
+