bspy 2.2.2__py3-none-any.whl → 3.0.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 +1 -1
- bspy/_spline_domain.py +17 -16
- bspy/_spline_evaluation.py +14 -6
- bspy/_spline_fitting.py +166 -137
- bspy/_spline_intersection.py +3 -3
- bspy/_spline_operations.py +60 -46
- bspy/bspyApp.py +36 -39
- bspy/drawableSpline.py +11 -13
- bspy/spline.py +202 -69
- bspy/splineOpenGLFrame.py +24 -16
- {bspy-2.2.2.dist-info → bspy-3.0.1.dist-info}/METADATA +14 -8
- bspy-3.0.1.dist-info/RECORD +15 -0
- bspy-2.2.2.dist-info/RECORD +0 -15
- {bspy-2.2.2.dist-info → bspy-3.0.1.dist-info}/LICENSE +0 -0
- {bspy-2.2.2.dist-info → bspy-3.0.1.dist-info}/WHEEL +0 -0
- {bspy-2.2.2.dist-info → bspy-3.0.1.dist-info}/top_level.txt +0 -0
bspy/_spline_operations.py
CHANGED
|
@@ -17,7 +17,7 @@ def add(self, other, indMap = None):
|
|
|
17
17
|
otherMapped = []
|
|
18
18
|
otherToSelf = {}
|
|
19
19
|
if indMap is not None:
|
|
20
|
-
(self, other) =
|
|
20
|
+
(self, other) = bspy.Spline.common_basis((self, other), indMap)
|
|
21
21
|
for map in indMap:
|
|
22
22
|
selfMapped.append(map[0])
|
|
23
23
|
otherMapped.append(map[1])
|
|
@@ -57,7 +57,7 @@ def add(self, other, indMap = None):
|
|
|
57
57
|
# Reverse the permutation.
|
|
58
58
|
coefs = coefs.transpose(np.argsort(permutation))
|
|
59
59
|
|
|
60
|
-
return type(self)(nInd, self.nDep, order, nCoef, knots, coefs, self.
|
|
60
|
+
return type(self)(nInd, self.nDep, order, nCoef, knots, coefs, self.metadata)
|
|
61
61
|
|
|
62
62
|
def confine(self, range_bounds):
|
|
63
63
|
if self.nInd != 1: raise ValueError("Confine only works on curves (nInd == 1)")
|
|
@@ -100,6 +100,15 @@ def confine(self, range_bounds):
|
|
|
100
100
|
for i in range(spline.nDep):
|
|
101
101
|
intersectBoundary(i, 0)
|
|
102
102
|
intersectBoundary(i, 1)
|
|
103
|
+
|
|
104
|
+
# Put the intersection points in order.
|
|
105
|
+
intersections.sort(key=lambda intersection: intersection[0])
|
|
106
|
+
|
|
107
|
+
# Remove repeat points at start and end.
|
|
108
|
+
if intersections[1][0] - intersections[0][0] < epsilon:
|
|
109
|
+
del intersections[1]
|
|
110
|
+
if intersections[-1][0] - intersections[-2][0] < epsilon:
|
|
111
|
+
del intersections[-2]
|
|
103
112
|
|
|
104
113
|
# Insert order-1 knots at each intersection point.
|
|
105
114
|
for (knot, boundaryPoint, headedOutside) in intersections:
|
|
@@ -110,9 +119,6 @@ def confine(self, range_bounds):
|
|
|
110
119
|
spline = spline.insert_knots(([knot] * count,))
|
|
111
120
|
else:
|
|
112
121
|
spline = spline.insert_knots(([knot] * (order - 1),))
|
|
113
|
-
|
|
114
|
-
# Put the intersection points in order.
|
|
115
|
-
intersections.sort(key=lambda intersection: intersection[0])
|
|
116
122
|
|
|
117
123
|
# Go through the boundary points, assigning boundary coefficients, interpolating between boundary points,
|
|
118
124
|
# and removing knots and coefficients where the curve stalls.
|
|
@@ -194,7 +200,7 @@ def contract(self, uvw):
|
|
|
194
200
|
else:
|
|
195
201
|
ix += 1
|
|
196
202
|
|
|
197
|
-
return type(self)(nInd, self.nDep, order, nCoef, knots, coefs, self.
|
|
203
|
+
return type(self)(nInd, self.nDep, order, nCoef, knots, coefs, self.metadata)
|
|
198
204
|
|
|
199
205
|
def cross(self, vector):
|
|
200
206
|
if isinstance(vector, bspy.Spline):
|
|
@@ -206,14 +212,14 @@ def cross(self, vector):
|
|
|
206
212
|
coefs[0] = vector[2] * self.coefs[1] - vector[1] * self.coefs[2]
|
|
207
213
|
coefs[1] = vector[0] * self.coefs[2] - vector[2] * self.coefs[0]
|
|
208
214
|
coefs[2] = vector[1] * self.coefs[0] - vector[0] * self.coefs[1]
|
|
209
|
-
return type(self)(self.nInd, 3, self.order, self.nCoef, self.knots, coefs, self.
|
|
215
|
+
return type(self)(self.nInd, 3, self.order, self.nCoef, self.knots, coefs, self.metadata)
|
|
210
216
|
else:
|
|
211
217
|
if not(self.nDep == 2): raise ValueError("Invalid nDep")
|
|
212
218
|
if not(len(vector) == self.nDep): raise ValueError("Invalid vector")
|
|
213
219
|
|
|
214
220
|
coefs = np.empty((1, *self.coefs.shape[1:]), self.coefs.dtype)
|
|
215
221
|
coefs[0] = vector[1] * self.coefs[0] - vector[0] * self.coefs[1]
|
|
216
|
-
return type(self)(self.nInd, 3, self.order, self.nCoef, self.knots, coefs, self.
|
|
222
|
+
return type(self)(self.nInd, 3, self.order, self.nCoef, self.knots, coefs, self.metadata)
|
|
217
223
|
|
|
218
224
|
def differentiate(self, with_respect_to = 0):
|
|
219
225
|
if not(0 <= with_respect_to < self.nInd): raise ValueError("Invalid with_respect_to")
|
|
@@ -237,7 +243,7 @@ def differentiate(self, with_respect_to = 0):
|
|
|
237
243
|
alpha = degree / (dKnots[i+degree] - dKnots[i])
|
|
238
244
|
newCoefs[i] = alpha * (newCoefs[i] - oldCoefs[i])
|
|
239
245
|
|
|
240
|
-
return type(self)(self.nInd, self.nDep, order, nCoef, knots, newCoefs.swapaxes(0, with_respect_to + 1), self.
|
|
246
|
+
return type(self)(self.nInd, self.nDep, order, nCoef, knots, newCoefs.swapaxes(0, with_respect_to + 1), self.metadata)
|
|
241
247
|
|
|
242
248
|
def dot(self, vector):
|
|
243
249
|
if isinstance(vector, bspy.Spline):
|
|
@@ -250,24 +256,24 @@ def dot(self, vector):
|
|
|
250
256
|
coefs += vector[i] * self.coefs[i]
|
|
251
257
|
if len(coefs.shape) == len(self.coefs.shape) - 1:
|
|
252
258
|
coefs = coefs.reshape(1, *coefs.shape)
|
|
253
|
-
return type(self)(self.nInd, 1, self.order, self.nCoef, self.knots, coefs, self.
|
|
259
|
+
return type(self)(self.nInd, 1, self.order, self.nCoef, self.knots, coefs, self.metadata)
|
|
254
260
|
|
|
255
261
|
def graph(self):
|
|
256
262
|
self = self.clamp(range(self.nInd), range(self.nInd))
|
|
257
263
|
coefs = np.insert(self.coefs, self.nInd * (0,), 0.0, axis = 0)
|
|
258
264
|
for nInd in range(self.nInd):
|
|
259
265
|
dep = np.swapaxes(coefs, nInd + 1, 1)[nInd] # Returns a view, so changes to dep make changes to coefs
|
|
260
|
-
for i,
|
|
261
|
-
dep[i] =
|
|
262
|
-
return type(self)(self.nInd, self.nInd + self.nDep, self.order, self.nCoef, self.knots, coefs, self.
|
|
266
|
+
for i, knotAverage in enumerate(self.greville(nInd)):
|
|
267
|
+
dep[i] = knotAverage
|
|
268
|
+
return type(self)(self.nInd, self.nInd + self.nDep, self.order, self.nCoef, self.knots, coefs, self.metadata)
|
|
263
269
|
|
|
264
270
|
def greville(self, ind = 0):
|
|
265
271
|
if ind < 0 or ind >= self.nInd: raise ValueError("Invalid independent variable")
|
|
266
272
|
myKnots = self.knots[ind]
|
|
267
|
-
|
|
273
|
+
knotAverages = 0
|
|
268
274
|
for ix in range(1, self.order[ind]):
|
|
269
|
-
|
|
270
|
-
return
|
|
275
|
+
knotAverages = knotAverages + myKnots[ix : ix + self.nCoef[ind]]
|
|
276
|
+
return knotAverages / (self.order[ind] - 1)
|
|
271
277
|
|
|
272
278
|
def integrate(self, with_respect_to = 0):
|
|
273
279
|
if not(0 <= with_respect_to < self.nInd): raise ValueError("Invalid with_respect_to")
|
|
@@ -295,7 +301,7 @@ def integrate(self, with_respect_to = 0):
|
|
|
295
301
|
for i in range(1, nCoef[with_respect_to]):
|
|
296
302
|
newCoefs[i] = newCoefs[i - 1] + ((iKnots[degree + i] - iKnots[i]) / degree) * oldCoefs[i - 1]
|
|
297
303
|
|
|
298
|
-
return type(self)(self.nInd, self.nDep, order, nCoef, knots, newCoefs.swapaxes(0, with_respect_to + 1), self.
|
|
304
|
+
return type(self)(self.nInd, self.nDep, order, nCoef, knots, newCoefs.swapaxes(0, with_respect_to + 1), self.metadata)
|
|
299
305
|
|
|
300
306
|
def multiplyAndConvolve(self, other, indMap = None, productType = 'S'):
|
|
301
307
|
if not(productType == 'C' or productType == 'D' or productType == 'S'): raise ValueError("productType must be 'C', 'D' or 'S'")
|
|
@@ -631,7 +637,7 @@ def multiplyAndConvolve(self, other, indMap = None, productType = 'S'):
|
|
|
631
637
|
# Now move combined independent variable back to its original axis.
|
|
632
638
|
coefs = np.moveaxis(newCoefs[:nCoef[ind1]], 0, ind1 + 1)
|
|
633
639
|
|
|
634
|
-
return type(self)(nInd, nDep, order, nCoef, knots, coefs, self.
|
|
640
|
+
return type(self)(nInd, nDep, order, nCoef, knots, coefs, self.metadata)
|
|
635
641
|
|
|
636
642
|
# Return a matrix of booleans whose [i,j] value indicates if self's partial wrt variable i depends on variable j.
|
|
637
643
|
def _cross_correlation_matrix(self):
|
|
@@ -653,23 +659,20 @@ def _cross_correlation_matrix(self):
|
|
|
653
659
|
if not match:
|
|
654
660
|
break
|
|
655
661
|
ccm[i, j] = ccm[j, i] = not match
|
|
656
|
-
|
|
657
662
|
ccm[-1, -1] = True
|
|
658
663
|
return ccm
|
|
659
664
|
|
|
660
665
|
def normal_spline(self, indices=None):
|
|
661
|
-
if abs(self.nInd - self.nDep) != 1: raise ValueError("The number of independent variables must be one
|
|
666
|
+
if abs(self.nInd - self.nDep) != 1: raise ValueError("The number of independent variables must be one less than the number of dependent variables.")
|
|
662
667
|
|
|
663
668
|
# Construct order and knots for generalized cross product of the tangent space.
|
|
664
669
|
newOrder = []
|
|
665
670
|
newKnots = []
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
deltaUvw = []
|
|
671
|
+
uvwValues = []
|
|
672
|
+
nCoefs = []
|
|
669
673
|
totalCoefs = [1]
|
|
670
|
-
rank = min(self.nInd, self.nDep)
|
|
671
674
|
ccm = _cross_correlation_matrix(self)
|
|
672
|
-
for i, order, knots in zip(
|
|
675
|
+
for i, (order, knots) in enumerate(zip(self.order, self.knots)):
|
|
673
676
|
# First, calculate the order of the normal for this independent variable.
|
|
674
677
|
# Note that the total order will be one less than usual, because one of
|
|
675
678
|
# the tangents is the derivative with respect to that independent variable.
|
|
@@ -700,56 +703,67 @@ def normal_spline(self, indices=None):
|
|
|
700
703
|
# using that calculation to span uvw from the starting knot to the end for each variable.
|
|
701
704
|
nCoef = len(newKnots[-1]) - newOrder[-1]
|
|
702
705
|
totalCoefs.append(totalCoefs[-1] * nCoef)
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
deltaUvw.append((uniqueKnots[-1] - uniqueKnots[0]) / (nCoef - 1))
|
|
706
|
-
|
|
706
|
+
uvwValues.append(bspy.Spline(1, 0, [newOrd], [nCoef], [newKnots[-1]], []).greville())
|
|
707
|
+
nCoefs.append(nCoef)
|
|
707
708
|
points = []
|
|
708
|
-
|
|
709
|
+
ijk = [0 for order in self.order]
|
|
709
710
|
for i in range(totalCoefs[-1]):
|
|
710
|
-
|
|
711
|
-
|
|
711
|
+
uvw = [uvwValues[j][k] for j, k in enumerate(ijk)]
|
|
712
|
+
points.append(self.normal(uvw, False, indices))
|
|
713
|
+
for j, nCoef in enumerate(totalCoefs[:-1]):
|
|
712
714
|
if (i + 1) % nCoef == 0:
|
|
713
|
-
|
|
715
|
+
ijk[j] += 1
|
|
714
716
|
if j > 0:
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
717
|
+
ijk[j - 1] = 0
|
|
718
|
+
points = np.array(points).T
|
|
718
719
|
nDep = max(self.nInd, self.nDep) if indices is None else len(indices)
|
|
719
|
-
|
|
720
|
+
nCoefs.reverse()
|
|
721
|
+
points = np.reshape(points, [nDep] + nCoefs)
|
|
722
|
+
points = np.transpose(points, [0] + list(range(self.nInd, 0, -1)))
|
|
723
|
+
return bspy.Spline.least_squares(uvwValues, points, order = newOrder, knots = newKnots, metadata = self.metadata)
|
|
724
|
+
# return bspy.Spline.least_squares(self.nInd, nDep, newOrder, points, newKnots, 0, self.metadata)
|
|
725
|
+
|
|
726
|
+
def rotate(self, vector, angle):
|
|
727
|
+
vector = np.atleast_1d(vector)
|
|
728
|
+
vector = vector / np.linalg.norm(vector)
|
|
729
|
+
if len(vector) != 3: raise ValueError("Rotation vector must have 3 components")
|
|
730
|
+
if self.nDep != 3: raise ValueError("Spline must have exactly 3 dependent variables")
|
|
731
|
+
radians = np.pi * angle / 180.0
|
|
732
|
+
cost = np.cos(radians)
|
|
733
|
+
sint = np.sin(radians)
|
|
734
|
+
kMat = np.array([[0.0, -vector[2], vector[1]],
|
|
735
|
+
[vector[2], 0.0, -vector[0]],
|
|
736
|
+
[-vector[1], vector[0], 0.0]])
|
|
737
|
+
rotMat = np.identity(3) + sint * kMat + (1.0 - cost) * kMat @ kMat
|
|
738
|
+
return list(rotMat) @ self
|
|
720
739
|
|
|
721
740
|
def scale(self, multiplier):
|
|
722
741
|
if isinstance(multiplier, bspy.Spline):
|
|
723
742
|
return self.multiply(multiplier, [(ix, ix) for ix in range(min(self.nInd, multiplier.nInd))], 'S')
|
|
724
743
|
else:
|
|
725
744
|
if np.isscalar(multiplier):
|
|
726
|
-
accuracy = abs(multiplier) * self.accuracy
|
|
727
745
|
nDep = self.nDep
|
|
728
746
|
coefs = multiplier * self.coefs
|
|
729
747
|
elif len(multiplier) == self.nDep:
|
|
730
|
-
accuracy = np.linalg.norm(multiplier) * self.accuracy
|
|
731
748
|
nDep = self.nDep
|
|
732
749
|
coefs = np.array(self.coefs)
|
|
733
750
|
for i in range(nDep):
|
|
734
751
|
coefs[i] *= multiplier[i]
|
|
735
752
|
elif self.nDep == 1:
|
|
736
|
-
accuracy = np.linalg.norm(multiplier) * self.accuracy
|
|
737
753
|
nDep = len(multiplier)
|
|
738
754
|
coefs = np.empty((nDep, *self.coefs.shape[1:]), self.coefs.dtype)
|
|
739
755
|
for i in range(nDep):
|
|
740
756
|
coefs[i] = multiplier[i] * self.coefs[0]
|
|
741
757
|
else:
|
|
742
758
|
raise ValueError("Invalid multiplier")
|
|
743
|
-
return type(self)(self.nInd, nDep, self.order, self.nCoef, self.knots, coefs,
|
|
759
|
+
return type(self)(self.nInd, nDep, self.order, self.nCoef, self.knots, coefs, self.metadata)
|
|
744
760
|
|
|
745
|
-
def transform(self, matrix
|
|
761
|
+
def transform(self, matrix):
|
|
746
762
|
if not(matrix.ndim == 2 and matrix.shape[1] == self.nDep): raise ValueError("Invalid matrix")
|
|
747
763
|
|
|
748
|
-
if maxSingularValue is None:
|
|
749
|
-
maxSingularValue = np.linalg.svd(matrix, compute_uv=False)[0]
|
|
750
764
|
swapped = np.swapaxes(self.coefs, 0, -2)
|
|
751
765
|
newCoefs = np.swapaxes(matrix @ swapped, 0, -2)
|
|
752
|
-
return type(self)(self.nInd, matrix.shape[0], self.order, self.nCoef, self.knots, newCoefs,
|
|
766
|
+
return type(self)(self.nInd, matrix.shape[0], self.order, self.nCoef, self.knots, newCoefs, self.metadata)
|
|
753
767
|
|
|
754
768
|
def translate(self, translationVector):
|
|
755
769
|
translationVector = np.atleast_1d(translationVector)
|
|
@@ -758,4 +772,4 @@ def translate(self, translationVector):
|
|
|
758
772
|
coefs = np.array(self.coefs)
|
|
759
773
|
for i in range(self.nDep):
|
|
760
774
|
coefs[i] += translationVector[i]
|
|
761
|
-
return type(self)(self.nInd, self.nDep, self.order, self.nCoef, self.knots, coefs, self.
|
|
775
|
+
return type(self)(self.nInd, self.nDep, self.order, self.nCoef, self.knots, coefs, self.metadata)
|
bspy/bspyApp.py
CHANGED
|
@@ -82,7 +82,7 @@ class bspyApp(tk.Tk):
|
|
|
82
82
|
controls = tk.Frame(self)
|
|
83
83
|
controls.pack(side=tk.RIGHT, fill=tk.BOTH, expand=tk.YES)
|
|
84
84
|
|
|
85
|
-
self.frame = SplineOpenGLFrame(controls)
|
|
85
|
+
self.frame = SplineOpenGLFrame(controls, draw_func=self._DrawSplines)
|
|
86
86
|
self.frame.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES)
|
|
87
87
|
|
|
88
88
|
buttons = tk.Frame(controls)
|
|
@@ -98,6 +98,7 @@ class bspyApp(tk.Tk):
|
|
|
98
98
|
self.scale.set(0.5)
|
|
99
99
|
|
|
100
100
|
self.splineList = []
|
|
101
|
+
self.splineDrawList = []
|
|
101
102
|
self.splineRadius = 0.0
|
|
102
103
|
self.adjust = None
|
|
103
104
|
self.workQueue = workQueue
|
|
@@ -138,32 +139,25 @@ class bspyApp(tk.Tk):
|
|
|
138
139
|
|
|
139
140
|
def draw(self, spline, name = None):
|
|
140
141
|
"""Add a `Spline` to the listbox and draw it. Can be called before the app is running."""
|
|
141
|
-
|
|
142
|
-
if name is not None:
|
|
143
|
-
spline.metadata["Name"] = name
|
|
144
|
-
self.splineList.append(spline)
|
|
145
|
-
self.listBox.insert(tk.END, spline)
|
|
142
|
+
self.list(spline, name)
|
|
146
143
|
self.listBox.selection_set(self.listBox.size() - 1)
|
|
147
144
|
self.update()
|
|
148
145
|
|
|
149
146
|
def save_splines(self):
|
|
150
|
-
if self.
|
|
151
|
-
initialName = self.
|
|
147
|
+
if self.splineDrawList:
|
|
148
|
+
initialName = self.splineDrawList[0].metadata.get("Name", "spline") + ".json"
|
|
152
149
|
fileName = filedialog.asksaveasfilename(title="Save splines", initialfile=initialName,
|
|
153
150
|
defaultextension=".json", filetypes=(('Json files', '*.json'),('All files', '*.*')))
|
|
154
151
|
if fileName:
|
|
155
|
-
self.
|
|
152
|
+
self.splineDrawList[0].save(fileName, *self.splineDrawList[1:])
|
|
156
153
|
|
|
157
154
|
def load_splines(self):
|
|
158
155
|
fileName = filedialog.askopenfilename(title="Load splines",
|
|
159
156
|
defaultextension=".json", filetypes=(('Json files', '*.json'),('All files', '*.*')))
|
|
160
157
|
if fileName:
|
|
161
158
|
splines = DrawableSpline.load(fileName)
|
|
162
|
-
|
|
163
|
-
self.list(
|
|
164
|
-
else:
|
|
165
|
-
for spline in splines:
|
|
166
|
-
self.list(spline)
|
|
159
|
+
for spline in splines:
|
|
160
|
+
self.list(spline)
|
|
167
161
|
|
|
168
162
|
def erase_all(self):
|
|
169
163
|
"""Stop drawing all splines. Splines remain in the listbox."""
|
|
@@ -203,7 +197,7 @@ class bspyApp(tk.Tk):
|
|
|
203
197
|
|
|
204
198
|
def update(self):
|
|
205
199
|
"""Update the spline draw list, set the default view, reset the bounds, and refresh the frame."""
|
|
206
|
-
self.
|
|
200
|
+
self.splineDrawList = []
|
|
207
201
|
gotOne = False
|
|
208
202
|
for item in self.listBox.curselection():
|
|
209
203
|
spline = self.splineList[item]
|
|
@@ -215,25 +209,24 @@ class bspyApp(tk.Tk):
|
|
|
215
209
|
splineMin = spline.coefs[:3].min(axis=coefsAxis)
|
|
216
210
|
splineMax = spline.coefs[:3].max(axis=coefsAxis)
|
|
217
211
|
gotOne = True
|
|
218
|
-
self.
|
|
212
|
+
self.splineDrawList.append(spline)
|
|
219
213
|
|
|
220
214
|
if gotOne:
|
|
221
215
|
newRadius = 0.5 * np.max(splineMax - splineMin)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
self.frame.ResetView()
|
|
216
|
+
self.splineRadius = newRadius
|
|
217
|
+
atDefaultEye = np.allclose(self.frame.eye, self.frame.defaultEye)
|
|
218
|
+
center = 0.5 * (splineMax + splineMin)
|
|
219
|
+
self.frame.SetDefaultView(center + (0.0, 0.0, 3.0 * newRadius), center, (0.0, 1.0, 0.0))
|
|
220
|
+
self.frame.ResetBounds()
|
|
221
|
+
if atDefaultEye:
|
|
222
|
+
self.frame.ResetView()
|
|
230
223
|
else:
|
|
231
224
|
self.splineRadius = 0.0
|
|
232
225
|
|
|
233
226
|
if self.adjust is not None:
|
|
234
|
-
if self.
|
|
235
|
-
self.bits.set(self.
|
|
236
|
-
animate = self.
|
|
227
|
+
if self.splineDrawList:
|
|
228
|
+
self.bits.set(self.splineDrawList[0].get_options())
|
|
229
|
+
animate = self.splineDrawList[0].get_animate()
|
|
237
230
|
else:
|
|
238
231
|
self.bits.set(0)
|
|
239
232
|
animate = None
|
|
@@ -243,6 +236,10 @@ class bspyApp(tk.Tk):
|
|
|
243
236
|
|
|
244
237
|
self.frame.Update()
|
|
245
238
|
|
|
239
|
+
def _DrawSplines(self, frame, transform):
|
|
240
|
+
for spline in self.splineDrawList:
|
|
241
|
+
spline._Draw(frame, transform)
|
|
242
|
+
|
|
246
243
|
def _ListSelectionChanged(self, event):
|
|
247
244
|
"""Handle when the listbox selection has changed."""
|
|
248
245
|
self.update()
|
|
@@ -262,8 +259,8 @@ class bspyApp(tk.Tk):
|
|
|
262
259
|
self.checkButtons.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES)
|
|
263
260
|
|
|
264
261
|
self.bits = tk.IntVar()
|
|
265
|
-
if self.
|
|
266
|
-
self.bits.set(self.
|
|
262
|
+
if self.splineDrawList:
|
|
263
|
+
self.bits.set(self.splineDrawList[0].get_options())
|
|
267
264
|
else:
|
|
268
265
|
self.bits.set(0)
|
|
269
266
|
_BitCheckbutton(self.checkButtons, DrawableSpline.SHADED, text="Shaded", anchor=tk.W, variable=self.bits, command=self._ChangeOptions).pack(side=tk.TOP, fill=tk.X)
|
|
@@ -277,8 +274,8 @@ class bspyApp(tk.Tk):
|
|
|
277
274
|
tk.Button(buttons, text='Line color', command=self._LineColorChange).pack(side=tk.TOP, fill=tk.X)
|
|
278
275
|
self.animate = tk.StringVar()
|
|
279
276
|
self.animateOptions = {"Animate: Off" : None, "Animate: u(0)" : 0, "Animate: v(1)" : 1, "Animate: w(2)" : 2}
|
|
280
|
-
if self.
|
|
281
|
-
animate = self.
|
|
277
|
+
if self.splineDrawList:
|
|
278
|
+
animate = self.splineDrawList[0].get_animate()
|
|
282
279
|
else:
|
|
283
280
|
animate = None
|
|
284
281
|
self.animate.set(next(key for key, value in self.animateOptions.items() if value == animate))
|
|
@@ -302,7 +299,7 @@ class bspyApp(tk.Tk):
|
|
|
302
299
|
|
|
303
300
|
def _ChangeOptions(self, options):
|
|
304
301
|
"""Handle when the spline options are changed."""
|
|
305
|
-
for spline in self.
|
|
302
|
+
for spline in self.splineDrawList:
|
|
306
303
|
spline.set_options(options)
|
|
307
304
|
self.frame.Update()
|
|
308
305
|
|
|
@@ -310,7 +307,7 @@ class bspyApp(tk.Tk):
|
|
|
310
307
|
"""Handle when the spline animation is changed."""
|
|
311
308
|
nInd = self.animateOptions[value]
|
|
312
309
|
animating = False
|
|
313
|
-
for spline in self.
|
|
310
|
+
for spline in self.splineDrawList:
|
|
314
311
|
if nInd is None or nInd < spline.nInd:
|
|
315
312
|
spline.set_animate(nInd)
|
|
316
313
|
animating = True
|
|
@@ -319,21 +316,21 @@ class bspyApp(tk.Tk):
|
|
|
319
316
|
|
|
320
317
|
def _FillColorChange(self):
|
|
321
318
|
"""Handle when the fill color changed."""
|
|
322
|
-
if self.
|
|
323
|
-
oldColor = 255.0 * self.
|
|
319
|
+
if self.splineDrawList:
|
|
320
|
+
oldColor = 255.0 * self.splineDrawList[0].get_fill_color()
|
|
324
321
|
newColor = askcolor(title="Set spline fill color", color="#%02x%02x%02x" % (int(oldColor[0]), int(oldColor[1]), int(oldColor[2])))
|
|
325
322
|
if newColor[0] is not None:
|
|
326
|
-
for spline in self.
|
|
323
|
+
for spline in self.splineDrawList:
|
|
327
324
|
spline.set_fill_color(newColor[0])
|
|
328
325
|
self.frame.Update()
|
|
329
326
|
|
|
330
327
|
def _LineColorChange(self):
|
|
331
328
|
"""Handle when the line color changed."""
|
|
332
|
-
if self.
|
|
333
|
-
oldColor = 255.0 * self.
|
|
329
|
+
if self.splineDrawList:
|
|
330
|
+
oldColor = 255.0 * self.splineDrawList[0].get_line_color()
|
|
334
331
|
newColor = askcolor(title="Set spline line color", color="#%02x%02x%02x" % (int(oldColor[0]), int(oldColor[1]), int(oldColor[2])))
|
|
335
332
|
if newColor[0] is not None:
|
|
336
|
-
for spline in self.
|
|
333
|
+
for spline in self.splineDrawList:
|
|
337
334
|
spline.set_line_color(newColor[0])
|
|
338
335
|
self.frame.Update()
|
|
339
336
|
|
bspy/drawableSpline.py
CHANGED
|
@@ -91,11 +91,6 @@ class DrawableSpline(Spline):
|
|
|
91
91
|
coefs : array-like
|
|
92
92
|
A list of the B-spline coefficients of the spline.
|
|
93
93
|
|
|
94
|
-
accuracy : `float`
|
|
95
|
-
Each spline function is presumed to be an approximation of something else.
|
|
96
|
-
The `accuracy` stores the infinity norm error of the difference between
|
|
97
|
-
the given spline function and that something else.
|
|
98
|
-
|
|
99
94
|
metadata : `dict`
|
|
100
95
|
A dictionary of ancillary data to store with the spline
|
|
101
96
|
|
|
@@ -135,7 +130,6 @@ class DrawableSpline(Spline):
|
|
|
135
130
|
self.nCoef = spline.nCoef
|
|
136
131
|
self.knots = spline.knots
|
|
137
132
|
self.coefs = spline.coefs
|
|
138
|
-
self.accuracy = spline.accuracy
|
|
139
133
|
self.metadata = spline.metadata
|
|
140
134
|
else:
|
|
141
135
|
Spline.__init__(self, *args, **kwargs)
|
|
@@ -151,10 +145,14 @@ class DrawableSpline(Spline):
|
|
|
151
145
|
floatCount += 2 + self.order[i] + self.nCoef[i]
|
|
152
146
|
coefficientCount *= self.nCoef[i]
|
|
153
147
|
if not(floatCount + self.nDep * coefficientCount <= self._maxFloats): raise ValueError("Spline to large to draw")
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
148
|
+
if not "fillColor" in self.metadata:
|
|
149
|
+
self.metadata["fillColor"] = np.array((0.0, 1.0, 0.0, 1.0), np.float32)
|
|
150
|
+
if not "lineColor" in self.metadata:
|
|
151
|
+
self.metadata["lineColor"] = np.array((0.0, 0.0, 0.0, 1.0) if self.nInd > 1 else (1.0, 1.0, 1.0, 1.0), np.float32)
|
|
152
|
+
if not "options" in self.metadata:
|
|
153
|
+
self.metadata["options"] = self.SHADED | self.BOUNDARY
|
|
154
|
+
if not "animate" in self.metadata:
|
|
155
|
+
self.metadata["animate"] = None
|
|
158
156
|
|
|
159
157
|
def __str__(self):
|
|
160
158
|
return self.metadata.get("Name", "[{0}, {1}]".format(self.coefs[0], self.coefs[1]))
|
|
@@ -204,7 +202,7 @@ class DrawableSpline(Spline):
|
|
|
204
202
|
if rangeCoef > 1.0e-8:
|
|
205
203
|
coefs[i] = (coefs[i] - minCoef) / rangeCoef
|
|
206
204
|
else:
|
|
207
|
-
coefs[i] = 1.0
|
|
205
|
+
coefs[i] = max(0.0, min(1.0, minCoef))
|
|
208
206
|
elif spline.nInd == 3:
|
|
209
207
|
if spline.nDep == 1:
|
|
210
208
|
spline = spline.graph()
|
|
@@ -221,11 +219,11 @@ class DrawableSpline(Spline):
|
|
|
221
219
|
if rangeCoef > 1.0e-8:
|
|
222
220
|
coefs[i] = (coefs[i] - minCoef) / rangeCoef
|
|
223
221
|
else:
|
|
224
|
-
coefs[i] = 1.0
|
|
222
|
+
coefs[i] = max(0.0, min(1.0, minCoef))
|
|
225
223
|
else:
|
|
226
224
|
raise ValueError("Can't convert to drawable spline.")
|
|
227
225
|
|
|
228
|
-
drawable = DrawableSpline(spline.nInd, nDep, spline.order, spline.nCoef, knotList, coefs
|
|
226
|
+
drawable = DrawableSpline(spline.nInd, nDep, spline.order, spline.nCoef, knotList, coefs)
|
|
229
227
|
drawable.metadata = spline.metadata # Make the original spline share its metadata with its drawable spline
|
|
230
228
|
if not "fillColor" in drawable.metadata:
|
|
231
229
|
drawable.metadata["fillColor"] = np.array((0.0, 1.0, 0.0, 1.0), np.float32)
|