bspy 2.0.0__tar.gz → 2.1.0__tar.gz
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-2.0.0 → bspy-2.1.0}/PKG-INFO +2 -2
- {bspy-2.0.0 → bspy-2.1.0}/README.md +1 -1
- {bspy-2.0.0 → bspy-2.1.0}/bspy/_spline_domain.py +1 -1
- {bspy-2.0.0 → bspy-2.1.0}/bspy/_spline_evaluation.py +15 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy/_spline_fitting.py +4 -2
- {bspy-2.0.0 → bspy-2.1.0}/bspy/_spline_operations.py +18 -2
- {bspy-2.0.0 → bspy-2.1.0}/bspy/spline.py +101 -57
- {bspy-2.0.0 → bspy-2.1.0}/bspy.egg-info/PKG-INFO +2 -2
- {bspy-2.0.0 → bspy-2.1.0}/setup.cfg +1 -1
- {bspy-2.0.0 → bspy-2.1.0}/tests/test_bspy.py +26 -0
- {bspy-2.0.0 → bspy-2.1.0}/LICENSE +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy/__init__.py +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy/_spline_intersection.py +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy/bspyApp.py +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy/drawableSpline.py +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy/splineOpenGLFrame.py +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy.egg-info/SOURCES.txt +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy.egg-info/dependency_links.txt +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy.egg-info/requires.txt +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/bspy.egg-info/top_level.txt +0 -0
- {bspy-2.0.0 → bspy-2.1.0}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bspy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Library for manipulating and rendering non-uniform b-splines
|
|
5
5
|
Home-page: http://github.com/ericbrec/bspy
|
|
6
6
|
Author: Eric Brechner
|
|
@@ -35,7 +35,7 @@ Library for manipulating and rendering b-spline curves, surfaces, and multidimen
|
|
|
35
35
|
The [Spline](https://ericbrec.github.io/bspy/bspy/spline.html) class has a method to fit multidimensional data for
|
|
36
36
|
scalar and vector functions of single and multiple variables. It also has methods to create circular arcs, ruled surfaces, and surfaces of revolution.
|
|
37
37
|
Other methods add, subtract, multiply, and linearly transform splines, as well as confine spline curves to a given range.
|
|
38
|
-
There are methods to evaluate spline values, derivatives, integrals, normals, and the Jacobian, as well as methods that return spline representations of derivatives, normals, integrals, and convolutions. In addition, there are methods to manipulate the domain of splines, including trim, join, reparametrize, reverse, add and remove knots, elevate and extrapolate, and fold and unfold. Finally, there are methods to compute the zeros and contours of a spline and to intersect two splines.
|
|
38
|
+
There are methods to evaluate spline values, derivatives, integrals, normals, curvature, and the Jacobian, as well as methods that return spline representations of derivatives, normals, integrals, graphs, and convolutions. In addition, there are methods to manipulate the domain of splines, including trim, join, reparametrize, reverse, add and remove knots, elevate and extrapolate, and fold and unfold. Finally, there are methods to compute the zeros and contours of a spline and to intersect two splines.
|
|
39
39
|
|
|
40
40
|
The [SplineOpenGLFrame](https://ericbrec.github.io/bspy/bspy/splineOpenGLFrame.html) class is an
|
|
41
41
|
[OpenGLFrame](https://pypi.org/project/pyopengltk/) with custom shaders to render spline curves and surfaces.
|
|
@@ -4,7 +4,7 @@ Library for manipulating and rendering b-spline curves, surfaces, and multidimen
|
|
|
4
4
|
The [Spline](https://ericbrec.github.io/bspy/bspy/spline.html) class has a method to fit multidimensional data for
|
|
5
5
|
scalar and vector functions of single and multiple variables. It also has methods to create circular arcs, ruled surfaces, and surfaces of revolution.
|
|
6
6
|
Other methods add, subtract, multiply, and linearly transform splines, as well as confine spline curves to a given range.
|
|
7
|
-
There are methods to evaluate spline values, derivatives, integrals, normals, and the Jacobian, as well as methods that return spline representations of derivatives, normals, integrals, and convolutions. In addition, there are methods to manipulate the domain of splines, including trim, join, reparametrize, reverse, add and remove knots, elevate and extrapolate, and fold and unfold. Finally, there are methods to compute the zeros and contours of a spline and to intersect two splines.
|
|
7
|
+
There are methods to evaluate spline values, derivatives, integrals, normals, curvature, and the Jacobian, as well as methods that return spline representations of derivatives, normals, integrals, graphs, and convolutions. In addition, there are methods to manipulate the domain of splines, including trim, join, reparametrize, reverse, add and remove knots, elevate and extrapolate, and fold and unfold. Finally, there are methods to compute the zeros and contours of a spline and to intersect two splines.
|
|
8
8
|
|
|
9
9
|
The [SplineOpenGLFrame](https://ericbrec.github.io/bspy/bspy/splineOpenGLFrame.html) class is an
|
|
10
10
|
[OpenGLFrame](https://pypi.org/project/pyopengltk/) with custom shaders to render spline curves and surfaces.
|
|
@@ -51,7 +51,7 @@ def common_basis(self, splines, indMap):
|
|
|
51
51
|
alignedSplines = []
|
|
52
52
|
for i, spline in enumerate(splines):
|
|
53
53
|
m = spline.nInd * [0]
|
|
54
|
-
newKnots = [[] for ix in
|
|
54
|
+
newKnots = [[] for ix in spline.order]
|
|
55
55
|
for (map, order, multiplicities) in zip(indMap, orders, knots):
|
|
56
56
|
ind = map[i]
|
|
57
57
|
m[ind] = order - spline.order[ind]
|
|
@@ -56,6 +56,21 @@ def bspline_values(knot, knots, splineOrder, u, derivativeOrder = 0, taylorCoefs
|
|
|
56
56
|
b += 1
|
|
57
57
|
return basis
|
|
58
58
|
|
|
59
|
+
def curvature(self, uv):
|
|
60
|
+
if self.nInd == 1:
|
|
61
|
+
if self.nDep == 1:
|
|
62
|
+
self = self.graph()
|
|
63
|
+
fp = self.derivative([1], uv)
|
|
64
|
+
fpp = self.derivative([2], uv)
|
|
65
|
+
fpdotfp = fp @ fp
|
|
66
|
+
fpdotfpp = fp @ fpp
|
|
67
|
+
denom = fpdotfp ** 1.5
|
|
68
|
+
if self.nDep == 2:
|
|
69
|
+
numer = fp[0] * fpp[1] - fp[1] * fpp[0]
|
|
70
|
+
else:
|
|
71
|
+
numer = np.sqrt((fpp @ fpp) * fpdotfp - fpdotfpp ** 2)
|
|
72
|
+
return numer / denom
|
|
73
|
+
|
|
59
74
|
def derivative(self, with_respect_to, uvw):
|
|
60
75
|
# Make work for scalar valued functions
|
|
61
76
|
uvw = np.atleast_1d(uvw)
|
|
@@ -2,7 +2,9 @@ import numpy as np
|
|
|
2
2
|
import bspy.spline
|
|
3
3
|
import math
|
|
4
4
|
|
|
5
|
-
def circular_arc(radius, angle, tolerance):
|
|
5
|
+
def circular_arc(radius, angle, tolerance = None):
|
|
6
|
+
if tolerance is None:
|
|
7
|
+
tolerance = np.finfo(float).eps
|
|
6
8
|
if radius < 0.0 or angle < 0.0 or tolerance < 0.0: raise ValueError("The radius, angle, and tolerance must be positive.")
|
|
7
9
|
|
|
8
10
|
samples = int(max(np.ceil(((1.1536e-5 * radius / tolerance)**(1/8)) * angle / 90), 2.0)) + 1
|
|
@@ -418,7 +420,7 @@ def revolve(self, angle):
|
|
|
418
420
|
maxRadius = max(abs(self.coefs[0].min()), self.coefs[0].max())
|
|
419
421
|
arc = ((1.0 / maxRadius, 0.0),
|
|
420
422
|
(0.0, 1.0 / maxRadius),
|
|
421
|
-
(0.0, 0.0)) @ bspy.Spline.circular_arc(maxRadius, angle
|
|
423
|
+
(0.0, 0.0)) @ bspy.Spline.circular_arc(maxRadius, angle) + (0.0, 0.0, 1.0)
|
|
422
424
|
radiusHeight = ((1.0, 0.0),
|
|
423
425
|
(1.0, 0.0),
|
|
424
426
|
(0.0, 1.0)) @ self
|
|
@@ -252,6 +252,20 @@ def dot(self, vector):
|
|
|
252
252
|
coefs = coefs.reshape(1, *coefs.shape)
|
|
253
253
|
return type(self)(self.nInd, 1, self.order, self.nCoef, self.knots, coefs, self.accuracy, self.metadata)
|
|
254
254
|
|
|
255
|
+
def graph(self):
|
|
256
|
+
splineDomain = self.domain()
|
|
257
|
+
uvwSplines = [bspy.Spline(1, 1, [2], [2], [[uLow, uLow, uHigh, uHigh]],
|
|
258
|
+
[[uLow, uHigh]]) for uLow, uHigh in splineDomain]
|
|
259
|
+
graphSpline = uvwSplines[0]
|
|
260
|
+
for nextSpline in uvwSplines[1:]:
|
|
261
|
+
graphMat = list(np.block([[np.identity(graphSpline.nInd)], [0.0]]))
|
|
262
|
+
nextMat = list(np.block([[np.zeros((graphSpline.nInd, 1))], [1.0]]))
|
|
263
|
+
graphSpline = (graphMat @ graphSpline).add(nextMat @ nextSpline)
|
|
264
|
+
graphMat = list(np.block([[np.identity(graphSpline.nInd)], [np.zeros((self.nDep, graphSpline.nInd))]]))
|
|
265
|
+
selfMat = list(np.block([[np.zeros((graphSpline.nInd, self.nDep))], [np.identity(self.nDep)]]))
|
|
266
|
+
finalGraph = graphMat @ graphSpline + selfMat @ self
|
|
267
|
+
return finalGraph
|
|
268
|
+
|
|
255
269
|
def integrate(self, with_respect_to = 0):
|
|
256
270
|
if not(0 <= with_respect_to < self.nInd): raise ValueError("Invalid with_respect_to")
|
|
257
271
|
|
|
@@ -732,10 +746,12 @@ def transform(self, matrix, maxSingularValue=None):
|
|
|
732
746
|
|
|
733
747
|
if maxSingularValue is None:
|
|
734
748
|
maxSingularValue = np.linalg.svd(matrix, compute_uv=False)[0]
|
|
735
|
-
|
|
736
|
-
|
|
749
|
+
swapped = np.swapaxes(self.coefs, 0, -2)
|
|
750
|
+
newCoefs = np.swapaxes(matrix @ swapped, 0, -2)
|
|
751
|
+
return type(self)(self.nInd, matrix.shape[0], self.order, self.nCoef, self.knots, newCoefs, maxSingularValue * self.accuracy, self.metadata)
|
|
737
752
|
|
|
738
753
|
def translate(self, translationVector):
|
|
754
|
+
translationVector = np.atleast_1d(translationVector)
|
|
739
755
|
if not(len(translationVector) == self.nDep): raise ValueError("Invalid translationVector")
|
|
740
756
|
|
|
741
757
|
coefs = np.array(self.coefs)
|
|
@@ -6,14 +6,6 @@ import bspy._spline_intersection
|
|
|
6
6
|
import bspy._spline_fitting
|
|
7
7
|
import bspy._spline_operations
|
|
8
8
|
|
|
9
|
-
def _isIterable(object):
|
|
10
|
-
result = True
|
|
11
|
-
try:
|
|
12
|
-
iterator = iter(object)
|
|
13
|
-
except TypeError:
|
|
14
|
-
result = False
|
|
15
|
-
return result
|
|
16
|
-
|
|
17
9
|
class Spline:
|
|
18
10
|
"""
|
|
19
11
|
A class to model, represent, and process piecewise polynomial tensor product
|
|
@@ -84,8 +76,8 @@ class Spline:
|
|
|
84
76
|
self.accuracy = accuracy
|
|
85
77
|
self.metadata = dict(metadata)
|
|
86
78
|
|
|
87
|
-
def __call__(self, uvw):
|
|
88
|
-
return self.evaluate(uvw)
|
|
79
|
+
def __call__(self, *uvw, **kwargs):
|
|
80
|
+
return self.evaluate(*uvw, **kwargs)
|
|
89
81
|
|
|
90
82
|
def __repr__(self):
|
|
91
83
|
return f"Spline({self.nInd}, {self.nDep}, {self.order}, " + \
|
|
@@ -95,77 +87,59 @@ class Spline:
|
|
|
95
87
|
def __add__(self, other):
|
|
96
88
|
if isinstance(other, Spline):
|
|
97
89
|
return self.add(other, [(ix, ix) for ix in range(min(self.nInd, other.nInd))])
|
|
98
|
-
elif _isIterable(other):
|
|
99
|
-
return self.translate(other)
|
|
100
90
|
else:
|
|
101
|
-
return
|
|
91
|
+
return self.translate(other)
|
|
102
92
|
|
|
103
93
|
def __radd__(self, other):
|
|
104
94
|
if isinstance(other, Spline):
|
|
105
95
|
return other.add(self, [(ix, ix) for ix in range(min(self.nInd, other.nInd))])
|
|
106
|
-
elif _isIterable(other):
|
|
107
|
-
return self.translate(other)
|
|
108
96
|
else:
|
|
109
|
-
return
|
|
97
|
+
return self.translate(other)
|
|
110
98
|
|
|
111
99
|
def __matmul__(self, other):
|
|
112
100
|
if isinstance(other, Spline):
|
|
113
101
|
return self.multiply(other, [(ix, ix) for ix in range(min(self.nInd, other.nInd))], 'D')
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if len(other.shape) == 2:
|
|
102
|
+
else:
|
|
103
|
+
other = np.atleast_1d(other)
|
|
104
|
+
if len(other.shape) > 1:
|
|
118
105
|
return self.transform(other.T)
|
|
119
106
|
else:
|
|
120
107
|
return self.dot(other)
|
|
121
|
-
else:
|
|
122
|
-
return NotImplemented
|
|
123
108
|
|
|
124
109
|
def __rmatmul__(self, other):
|
|
125
110
|
if isinstance(other, Spline):
|
|
126
111
|
return other.multiply(self, [(ix, ix) for ix in range(min(self.nInd, other.nInd))], 'D')
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if len(other.shape) == 2:
|
|
112
|
+
else:
|
|
113
|
+
other = np.atleast_1d(other)
|
|
114
|
+
if len(other.shape) > 1:
|
|
131
115
|
return self.transform(other)
|
|
132
116
|
else:
|
|
133
117
|
return self.dot(other)
|
|
134
|
-
else:
|
|
135
|
-
return NotImplemented
|
|
136
118
|
|
|
137
119
|
def __mul__(self, other):
|
|
138
120
|
if isinstance(other, Spline):
|
|
139
121
|
return self.multiply(other, [(ix, ix) for ix in range(min(self.nInd, other.nInd))], 'S')
|
|
140
|
-
elif np.isscalar(other) or _isIterable(other):
|
|
141
|
-
return self.scale(other)
|
|
142
122
|
else:
|
|
143
|
-
return
|
|
123
|
+
return self.scale(other)
|
|
144
124
|
|
|
145
125
|
def __rmul__(self, other):
|
|
146
126
|
if isinstance(other, Spline):
|
|
147
127
|
return other.multiply(self, [(ix, ix) for ix in range(min(self.nInd, other.nInd))], 'S')
|
|
148
|
-
elif np.isscalar(other) or _isIterable(other):
|
|
149
|
-
return self.scale(other)
|
|
150
128
|
else:
|
|
151
|
-
return
|
|
129
|
+
return self.scale(other)
|
|
152
130
|
|
|
153
131
|
def __sub__(self, other):
|
|
154
132
|
if isinstance(other, Spline):
|
|
155
133
|
return self.subtract(other, [(ix, ix) for ix in range(min(self.nInd, other.nInd))])
|
|
156
|
-
elif _isIterable(other):
|
|
157
|
-
return self.translate(-np.array(other))
|
|
158
134
|
else:
|
|
159
|
-
return
|
|
135
|
+
return self.translate(-np.atleast_1d(other))
|
|
160
136
|
|
|
161
137
|
def __rsub__(self, other):
|
|
162
138
|
if isinstance(other, Spline):
|
|
163
139
|
return other.subtract(self, [(ix, ix) for ix in range(min(self.nInd, other.nInd))])
|
|
164
|
-
|
|
140
|
+
else:
|
|
165
141
|
spline = self.scale(-1.0)
|
|
166
142
|
return spline.translate(other)
|
|
167
|
-
else:
|
|
168
|
-
return NotImplemented
|
|
169
143
|
|
|
170
144
|
def add(self, other, indMap = None):
|
|
171
145
|
"""
|
|
@@ -203,7 +177,7 @@ class Spline:
|
|
|
203
177
|
Uses `common_basis` to ensure mapped variables share the same order and knots.
|
|
204
178
|
"""
|
|
205
179
|
if indMap is not None:
|
|
206
|
-
indMap = [mapping if
|
|
180
|
+
indMap = [(mapping, mapping) if np.isscalar(mapping) else mapping for mapping in indMap]
|
|
207
181
|
return bspy._spline_operations.add(self, other, indMap)
|
|
208
182
|
|
|
209
183
|
def blossom(self, uvw):
|
|
@@ -274,7 +248,7 @@ class Spline:
|
|
|
274
248
|
return bspy._spline_evaluation.bspline_values(knot, knots, splineOrder, u, derivativeOrder, taylorCoefs)
|
|
275
249
|
|
|
276
250
|
@staticmethod
|
|
277
|
-
def circular_arc(radius, angle, tolerance):
|
|
251
|
+
def circular_arc(radius, angle, tolerance = None):
|
|
278
252
|
"""
|
|
279
253
|
Create a 2D circular arc for a given radius and angle accurate to within a given tolerance.
|
|
280
254
|
|
|
@@ -286,8 +260,8 @@ class Spline:
|
|
|
286
260
|
angle : `float`
|
|
287
261
|
The angle of the circular arc measured in degrees starting at the x-axis rotating counterclockwise.
|
|
288
262
|
|
|
289
|
-
tolerance : `float
|
|
290
|
-
The maximum allowed error in the circular arc.
|
|
263
|
+
tolerance : `float`, optional
|
|
264
|
+
The maximum allowed error in the circular arc (default is machine epsilon).
|
|
291
265
|
|
|
292
266
|
Returns
|
|
293
267
|
-------
|
|
@@ -521,7 +495,7 @@ class Spline:
|
|
|
521
495
|
Computer Aided Geometric Design 11, no. 6 (1994): 597-620.
|
|
522
496
|
"""
|
|
523
497
|
if indMap is not None:
|
|
524
|
-
indMap = [(
|
|
498
|
+
indMap = [(mapping, mapping, True) if np.isscalar(mapping) else (*mapping, True) for mapping in indMap]
|
|
525
499
|
return bspy._spline_operations.multiplyAndConvolve(self, other, indMap, productType)
|
|
526
500
|
|
|
527
501
|
def copy(self, metadata={}):
|
|
@@ -560,7 +534,28 @@ class Spline:
|
|
|
560
534
|
"""
|
|
561
535
|
return bspy._spline_operations.cross(self, vector)
|
|
562
536
|
|
|
563
|
-
def
|
|
537
|
+
def curvature(self, uvw):
|
|
538
|
+
"""
|
|
539
|
+
Compute the curvature of a univariate spline.
|
|
540
|
+
|
|
541
|
+
Parameters
|
|
542
|
+
----------
|
|
543
|
+
uvw : `iterable`
|
|
544
|
+
An iterable of length `nInd` that specifies the values of each independent variable (the parameter values).
|
|
545
|
+
|
|
546
|
+
Returns
|
|
547
|
+
-------
|
|
548
|
+
value : scalar
|
|
549
|
+
The value of the curvature at the given point on the curve.
|
|
550
|
+
|
|
551
|
+
Notes
|
|
552
|
+
-----
|
|
553
|
+
Computes the curvature of the graph of the function if nDep == 1. If nDep == 1 or 2,
|
|
554
|
+
then the curvature is computed as a signed quantity.
|
|
555
|
+
"""
|
|
556
|
+
return bspy._spline_evaluation.curvature(self, uvw)
|
|
557
|
+
|
|
558
|
+
def derivative(self, with_respect_to, *uvw, **kwargs):
|
|
564
559
|
"""
|
|
565
560
|
Compute the derivative of the spline at given parameter values.
|
|
566
561
|
|
|
@@ -570,13 +565,18 @@ class Spline:
|
|
|
570
565
|
An iterable of length `nInd` that specifies the integer order of derivative for each independent variable.
|
|
571
566
|
A zero-order derivative just evaluates the spline normally.
|
|
572
567
|
|
|
573
|
-
uvw : `iterable`
|
|
574
|
-
An iterable of length `nInd` that specifies the values of each independent variable (the parameter values)
|
|
568
|
+
*uvw : `iterable` or iterable of iterables
|
|
569
|
+
An iterable of length `nInd` that specifies the values of each independent variable (the parameter values), or
|
|
570
|
+
an iterable of iterables of length `nInd` that specifies values for each independent variable (numpy ufunc style).
|
|
571
|
+
|
|
572
|
+
**kwargs:
|
|
573
|
+
For other keyword-only arguments, see the `ufunc docs <https://numpy.org/doc/stable/reference/ufuncs.html#ufuncs-kwargs>`_.
|
|
575
574
|
|
|
576
575
|
Returns
|
|
577
576
|
-------
|
|
578
577
|
value : `numpy.array`
|
|
579
|
-
The value of the derivative of the spline at the given parameter values.
|
|
578
|
+
The value of the derivative of the spline at the given parameter values (array of size nDep). If multiple points are
|
|
579
|
+
passed (numpy ufunc style), then multiple arrays are returned, one for each dependent variable.
|
|
580
580
|
|
|
581
581
|
See Also
|
|
582
582
|
--------
|
|
@@ -592,7 +592,13 @@ class Spline:
|
|
|
592
592
|
evaluated, then the dot product of those B-splines with the vector of
|
|
593
593
|
B-spline coefficients is computed.
|
|
594
594
|
"""
|
|
595
|
-
|
|
595
|
+
if len(uvw) > 1 or (not np.isscalar(uvw[0]) and len(uvw[0]) > self.nInd):
|
|
596
|
+
def vectorized(*uvwInstance):
|
|
597
|
+
return tuple(bspy._spline_evaluation.derivative(self, with_respect_to, uvwInstance))
|
|
598
|
+
uFunc = np.frompyfunc(vectorized, self.nInd, self.nDep)
|
|
599
|
+
return uFunc(*uvw, **kwargs)
|
|
600
|
+
else:
|
|
601
|
+
return bspy._spline_evaluation.derivative(self, with_respect_to, *uvw)
|
|
596
602
|
|
|
597
603
|
def differentiate(self, with_respect_to = 0):
|
|
598
604
|
"""
|
|
@@ -715,19 +721,24 @@ class Spline:
|
|
|
715
721
|
"""
|
|
716
722
|
return bspy._spline_domain.elevate_and_insert_knots(self, m, newKnots)
|
|
717
723
|
|
|
718
|
-
def evaluate(self, uvw):
|
|
724
|
+
def evaluate(self, *uvw, **kwargs):
|
|
719
725
|
"""
|
|
720
726
|
Compute the value of the spline at given parameter values.
|
|
721
727
|
|
|
722
728
|
Parameters
|
|
723
729
|
----------
|
|
724
|
-
uvw : `iterable`
|
|
725
|
-
An iterable of length `nInd` that specifies the values of each independent variable (the parameter values)
|
|
730
|
+
*uvw : `iterable` or iterable of iterables
|
|
731
|
+
An iterable of length `nInd` that specifies the values of each independent variable (the parameter values), or
|
|
732
|
+
an iterable of iterables of length `nInd` that specifies values for each independent variable (numpy ufunc style).
|
|
733
|
+
|
|
734
|
+
**kwargs:
|
|
735
|
+
For other keyword-only arguments, see the `ufunc docs <https://numpy.org/doc/stable/reference/ufuncs.html#ufuncs-kwargs>`_.
|
|
726
736
|
|
|
727
737
|
Returns
|
|
728
738
|
-------
|
|
729
739
|
value : `numpy.array`
|
|
730
|
-
The value of the spline at the given parameter values.
|
|
740
|
+
The value of the spline at the given parameter values (array of size nDep). If multiple points are
|
|
741
|
+
passed (numpy ufunc style), then multiple arrays are returned, one for each dependent variable.
|
|
731
742
|
|
|
732
743
|
See Also
|
|
733
744
|
--------
|
|
@@ -735,12 +746,20 @@ class Spline:
|
|
|
735
746
|
|
|
736
747
|
Notes
|
|
737
748
|
-----
|
|
749
|
+
Equivalent to spline(*uvw, **kwargs).
|
|
750
|
+
|
|
738
751
|
The evaluate method uses the de Boor recurrence relations for a B-spline
|
|
739
752
|
series to evaluate a spline. The non-zero B-splines are
|
|
740
753
|
evaluated, then the dot product of those B-splines with the vector of
|
|
741
754
|
B-spline coefficients is computed.
|
|
742
755
|
"""
|
|
743
|
-
|
|
756
|
+
if len(uvw) > 1 or (not np.isscalar(uvw[0]) and len(uvw[0]) > self.nInd):
|
|
757
|
+
def vectorized(*uvwInstance):
|
|
758
|
+
return tuple(bspy._spline_evaluation.evaluate(self, uvwInstance))
|
|
759
|
+
uFunc = np.frompyfunc(vectorized, self.nInd, self.nDep)
|
|
760
|
+
return uFunc(*uvw, **kwargs)
|
|
761
|
+
else:
|
|
762
|
+
return bspy._spline_evaluation.evaluate(self, *uvw)
|
|
744
763
|
|
|
745
764
|
def extrapolate(self, newDomain, continuityOrder):
|
|
746
765
|
"""
|
|
@@ -803,6 +822,31 @@ class Spline:
|
|
|
803
822
|
"""
|
|
804
823
|
return bspy._spline_domain.fold(self, foldedInd)
|
|
805
824
|
|
|
825
|
+
def graph(self):
|
|
826
|
+
"""
|
|
827
|
+
Generate the spline which is the graph of the given spline.
|
|
828
|
+
|
|
829
|
+
Parameters
|
|
830
|
+
----------
|
|
831
|
+
|
|
832
|
+
Returns
|
|
833
|
+
-------
|
|
834
|
+
Given a spline with n independent variables and m dependent variables, generate a new
|
|
835
|
+
spline with n independent variables which has n + m dependent variables, the first n of
|
|
836
|
+
which are just the independent variables themselves. For example, given a scalar
|
|
837
|
+
valued function f of two variables u and v, return the spline of two variables whose
|
|
838
|
+
three dependent variables are (u, v, f(u,v)).
|
|
839
|
+
|
|
840
|
+
See Also
|
|
841
|
+
--------
|
|
842
|
+
`add` : Add two splines together
|
|
843
|
+
|
|
844
|
+
Notes
|
|
845
|
+
-----
|
|
846
|
+
Makes use of matrix spline multiply and tensor product addition for splines.
|
|
847
|
+
"""
|
|
848
|
+
return bspy._spline_operations.graph(self)
|
|
849
|
+
|
|
806
850
|
def insert_knots(self, newKnots):
|
|
807
851
|
"""
|
|
808
852
|
Insert new knots into a spline.
|
|
@@ -1112,7 +1156,7 @@ class Spline:
|
|
|
1112
1156
|
Computer Aided Geometric Design 11, no. 6 (1994): 597-620.
|
|
1113
1157
|
"""
|
|
1114
1158
|
if indMap is not None:
|
|
1115
|
-
indMap = [(
|
|
1159
|
+
indMap = [(mapping, mapping, False) if np.isscalar(mapping) else (*mapping, False) for mapping in indMap]
|
|
1116
1160
|
return bspy._spline_operations.multiplyAndConvolve(self, other, indMap, productType)
|
|
1117
1161
|
|
|
1118
1162
|
def normal(self, uvw, normalize=True, indices=None):
|
|
@@ -1443,7 +1487,7 @@ class Spline:
|
|
|
1443
1487
|
Uses `common_basis` to ensure mapped variables share the same order and knots.
|
|
1444
1488
|
"""
|
|
1445
1489
|
if indMap is not None:
|
|
1446
|
-
indMap = [mapping if
|
|
1490
|
+
indMap = [(mapping, mapping) if np.isscalar(mapping) else mapping for mapping in indMap]
|
|
1447
1491
|
return self.add(other.scale(-1.0), indMap)
|
|
1448
1492
|
|
|
1449
1493
|
def transform(self, matrix, maxSingularValue=None):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bspy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Library for manipulating and rendering non-uniform b-splines
|
|
5
5
|
Home-page: http://github.com/ericbrec/bspy
|
|
6
6
|
Author: Eric Brechner
|
|
@@ -35,7 +35,7 @@ Library for manipulating and rendering b-spline curves, surfaces, and multidimen
|
|
|
35
35
|
The [Spline](https://ericbrec.github.io/bspy/bspy/spline.html) class has a method to fit multidimensional data for
|
|
36
36
|
scalar and vector functions of single and multiple variables. It also has methods to create circular arcs, ruled surfaces, and surfaces of revolution.
|
|
37
37
|
Other methods add, subtract, multiply, and linearly transform splines, as well as confine spline curves to a given range.
|
|
38
|
-
There are methods to evaluate spline values, derivatives, integrals, normals, and the Jacobian, as well as methods that return spline representations of derivatives, normals, integrals, and convolutions. In addition, there are methods to manipulate the domain of splines, including trim, join, reparametrize, reverse, add and remove knots, elevate and extrapolate, and fold and unfold. Finally, there are methods to compute the zeros and contours of a spline and to intersect two splines.
|
|
38
|
+
There are methods to evaluate spline values, derivatives, integrals, normals, curvature, and the Jacobian, as well as methods that return spline representations of derivatives, normals, integrals, graphs, and convolutions. In addition, there are methods to manipulate the domain of splines, including trim, join, reparametrize, reverse, add and remove knots, elevate and extrapolate, and fold and unfold. Finally, there are methods to compute the zeros and contours of a spline and to intersect two splines.
|
|
39
39
|
|
|
40
40
|
The [SplineOpenGLFrame](https://ericbrec.github.io/bspy/bspy/splineOpenGLFrame.html) class is an
|
|
41
41
|
[OpenGLFrame](https://pypi.org/project/pyopengltk/) with custom shaders to render spline curves and surfaces.
|
|
@@ -700,6 +700,14 @@ def test_cross():
|
|
|
700
700
|
maxError = max(maxError, np.sqrt(xyz @ xyz))
|
|
701
701
|
assert maxError <= np.sqrt(np.finfo(float).eps)
|
|
702
702
|
|
|
703
|
+
def test_curvature():
|
|
704
|
+
testCurve = bspy.Spline.section([[1.0, 0.0, 90.0, 1.0], [0.0, 1.0, 180.0, 2.0]])
|
|
705
|
+
assert abs(testCurve.curvature(0.0) - 1.0) < 2.0e-15
|
|
706
|
+
assert abs(testCurve.curvature(1.0) - 2.0) < 2.0e-15
|
|
707
|
+
testCurve = testCurve @ [0, 1]
|
|
708
|
+
testVals = [testCurve.curvature(u) for u in np.linspace(0.0, 1.0, 101)]
|
|
709
|
+
return
|
|
710
|
+
|
|
703
711
|
def test_derivative():
|
|
704
712
|
maxError = 0.0
|
|
705
713
|
myDerivative = myCurve.differentiate()
|
|
@@ -821,6 +829,16 @@ def test_fold_unfold():
|
|
|
821
829
|
maxError = max(maxError, abs(unfolded.coefs[i, j, k, l] - spline.coefs[i, j, k, l]))
|
|
822
830
|
assert maxError <= np.finfo(float).eps
|
|
823
831
|
|
|
832
|
+
def test_graph():
|
|
833
|
+
simpleFunc = bspy.Spline(2, 1, [3, 4], [4, 5], [[0.0, 0, 0, 0.4, 1, 1, 1],
|
|
834
|
+
[0.0, 0, 0, 0, 0.6, 1, 1, 1, 1]], [[1.0, 2, 3, 4, 2, 3, 4, 5,
|
|
835
|
+
3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8]])
|
|
836
|
+
graphFunc = simpleFunc.graph()
|
|
837
|
+
uvfPoint = graphFunc([0.27, 0.83])
|
|
838
|
+
assert abs(uvfPoint[0] - 0.27) <= 4.0 * np.finfo(float).eps
|
|
839
|
+
assert abs(uvfPoint[1] - 0.83) <= 4.0 * np.finfo(float).eps
|
|
840
|
+
assert abs(uvfPoint[2] - simpleFunc([0.27, 0.83])[0]) <= 4.0 * np.finfo(float).eps
|
|
841
|
+
|
|
824
842
|
def test_insert_knots():
|
|
825
843
|
maxError = 0.0
|
|
826
844
|
newCurve = myCurve.insert_knots([[.2, .3]])
|
|
@@ -1167,6 +1185,14 @@ def test_transform():
|
|
|
1167
1185
|
[xTest, yTest] = transformedCurve.evaluate([u])
|
|
1168
1186
|
maxError = max(maxError, (xTest - (2.0 * x + 3.0 * y)) ** 2 + (yTest - (-1.0 * x - 4.0 * y)) ** 2)
|
|
1169
1187
|
assert maxError <= np.finfo(float).eps
|
|
1188
|
+
shape234 = bspy.Spline(2, 2, [3, 4], [3, 4], [[0.0, 0, 0, 1, 1, 1], [0.0, 0, 0, 0, 1, 1, 1, 1]],
|
|
1189
|
+
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
|
|
1190
|
+
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]])
|
|
1191
|
+
shape334 = [[2, 0], [0, 0.5], [1, 1]] @ shape234
|
|
1192
|
+
assert shape334.coefs[0,1,0] == 8
|
|
1193
|
+
assert shape334.coefs[0,2,1] == 18
|
|
1194
|
+
assert shape334.coefs[1,0,2] == 11
|
|
1195
|
+
assert shape334.coefs[2,2,3] == 42
|
|
1170
1196
|
|
|
1171
1197
|
def test_translate():
|
|
1172
1198
|
maxError = 0.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|