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/__init__.py +16 -9
- bspy/_spline_domain.py +83 -47
- bspy/_spline_evaluation.py +44 -62
- bspy/_spline_fitting.py +353 -75
- bspy/_spline_intersection.py +332 -59
- bspy/_spline_operations.py +33 -38
- bspy/hyperplane.py +540 -0
- bspy/manifold.py +391 -0
- bspy/solid.py +839 -0
- bspy/spline.py +310 -77
- bspy/splineOpenGLFrame.py +683 -19
- bspy/viewer.py +795 -0
- {bspy-3.0.1.dist-info → bspy-4.1.dist-info}/METADATA +25 -13
- bspy-4.1.dist-info/RECORD +17 -0
- {bspy-3.0.1.dist-info → bspy-4.1.dist-info}/WHEEL +1 -1
- bspy/bspyApp.py +0 -426
- bspy/drawableSpline.py +0 -585
- bspy-3.0.1.dist-info/RECORD +0 -15
- {bspy-3.0.1.dist-info → bspy-4.1.dist-info}/LICENSE +0 -0
- {bspy-3.0.1.dist-info → bspy-4.1.dist-info}/top_level.txt +0 -0
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
1198
|
+
Intersect a spline or hyperplane.
|
|
1096
1199
|
|
|
1097
1200
|
Parameters
|
|
1098
1201
|
----------
|
|
1099
|
-
other : `Spline`
|
|
1100
|
-
The
|
|
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
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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.
|
|
1121
|
-
|
|
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
|
|
1328
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|