bspy 1.2.3__tar.gz → 1.2.4__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-1.2.3 → bspy-1.2.4}/PKG-INFO +1 -1
- {bspy-1.2.3 → bspy-1.2.4}/bspy/_spline_intersection.py +48 -23
- {bspy-1.2.3 → bspy-1.2.4}/bspy.egg-info/PKG-INFO +1 -1
- {bspy-1.2.3 → bspy-1.2.4}/setup.cfg +1 -1
- {bspy-1.2.3 → bspy-1.2.4}/tests/test_bspy.py +6 -1
- {bspy-1.2.3 → bspy-1.2.4}/LICENSE +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/README.md +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/__init__.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/_spline_domain.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/_spline_evaluation.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/_spline_fitting.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/_spline_operations.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/bspyApp.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/drawableSpline.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/spline.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy/splineOpenGLFrame.py +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy.egg-info/SOURCES.txt +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy.egg-info/dependency_links.txt +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy.egg-info/requires.txt +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/bspy.egg-info/top_level.txt +0 -0
- {bspy-1.2.3 → bspy-1.2.4}/pyproject.toml +0 -0
|
@@ -218,8 +218,9 @@ def _refine_projected_polyhedron(interval):
|
|
|
218
218
|
# No roots in this interval.
|
|
219
219
|
return root, intervals
|
|
220
220
|
scale = max(scale, abs(coefsMin), abs(coefsMax))
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
newScale = scale * interval.scale
|
|
222
|
+
|
|
223
|
+
if newScale < evaluationEpsilon:
|
|
223
224
|
# Return the bounds of the interval within which the spline is zero.
|
|
224
225
|
root = (interval.intercept, interval.slope + interval.intercept)
|
|
225
226
|
else:
|
|
@@ -265,7 +266,6 @@ def _refine_projected_polyhedron(interval):
|
|
|
265
266
|
domain.append(xInterval)
|
|
266
267
|
|
|
267
268
|
if domain is not None:
|
|
268
|
-
newScale = scale * interval.scale
|
|
269
269
|
domain = np.array(domain).T
|
|
270
270
|
width = domain[1] - domain[0]
|
|
271
271
|
newSlope = np.multiply(width, interval.slope)
|
|
@@ -273,10 +273,9 @@ def _refine_projected_polyhedron(interval):
|
|
|
273
273
|
# one iteration past being less than sqrt(machineEpsilon) or simply less than epsilon.
|
|
274
274
|
if interval.atMachineEpsilon or newSlope.max() < epsilon:
|
|
275
275
|
newIntercept = np.multiply(domain[0], interval.slope) + interval.intercept
|
|
276
|
-
root = newIntercept + 0.5 * newSlope
|
|
277
276
|
# Double-check that we're at an actual zero (avoids boundary case).
|
|
278
|
-
if newScale * np.linalg.norm(spline(0.5 * (domain[0] + domain[1])))
|
|
279
|
-
root =
|
|
277
|
+
if newScale * np.linalg.norm(spline(0.5 * (domain[0] + domain[1]))) < evaluationEpsilon:
|
|
278
|
+
root = (newIntercept, newIntercept + newSlope)
|
|
280
279
|
else:
|
|
281
280
|
# Split domain in dimensions that aren't decreasing in width sufficiently.
|
|
282
281
|
domains = [domain]
|
|
@@ -299,7 +298,10 @@ def _refine_projected_polyhedron(interval):
|
|
|
299
298
|
newIntercept = np.multiply(domain[0], interval.slope) + interval.intercept
|
|
300
299
|
for i, w in zip(range(spline.nInd), width):
|
|
301
300
|
if w < machineEpsilon:
|
|
302
|
-
|
|
301
|
+
if domain[0, i] > machineEpsilon:
|
|
302
|
+
domain[0, i] = domain[1, i] - machineEpsilon
|
|
303
|
+
else:
|
|
304
|
+
domain[1, i] = domain[0, i] + machineEpsilon
|
|
303
305
|
newDomain = [None if s < epsilon else (0.0, 1.0) for s in newSlope]
|
|
304
306
|
intervals.append(Interval(spline.trim(domain.T).reparametrize(newDomain), newScale, newSlope, newIntercept, epsilon, np.dot(newSlope, newSlope) < machineEpsilon))
|
|
305
307
|
|
|
@@ -333,25 +335,48 @@ def zeros_using_projected_polyhedron(self, epsilon=None):
|
|
|
333
335
|
nextIntervals += newIntervals
|
|
334
336
|
intervals = nextIntervals
|
|
335
337
|
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
while
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
338
|
+
# Connect intervals of zeros that overlap.
|
|
339
|
+
gotOverlap = True
|
|
340
|
+
while gotOverlap:
|
|
341
|
+
gotOverlap = False
|
|
342
|
+
i = 0
|
|
343
|
+
rootCount = len(roots)
|
|
344
|
+
while i < rootCount:
|
|
345
|
+
iRoot = roots[i]
|
|
346
|
+
root = (iRoot[0].copy(), iRoot[1].copy()) # Temporary storage for expanded interval
|
|
347
|
+
j = i + 1
|
|
348
|
+
# Check for overlap with other intervals.
|
|
349
|
+
while j < rootCount:
|
|
350
|
+
jRoot = roots[j]
|
|
351
|
+
overlapped = True
|
|
352
|
+
for d in range(self.nInd):
|
|
353
|
+
if iRoot[0][d] < jRoot[1][d] + epsilon and jRoot[0][d] < iRoot[1][d] + epsilon:
|
|
354
|
+
root[0][d] = min(iRoot[0][d], jRoot[0][d])
|
|
355
|
+
root[1][d] = max(iRoot[1][d], jRoot[1][d])
|
|
356
|
+
else:
|
|
357
|
+
overlapped = False
|
|
358
|
+
break
|
|
359
|
+
if overlapped:
|
|
360
|
+
# For an overlapped interval, expand original interval and toss overlapping interval.
|
|
361
|
+
iRoot[0][:] = root[0]
|
|
362
|
+
iRoot[1][:] = root[1]
|
|
363
|
+
roots.pop(j)
|
|
364
|
+
rootCount -= 1
|
|
365
|
+
gotOverlap = True
|
|
366
|
+
else:
|
|
367
|
+
j += 1
|
|
368
|
+
i += 1
|
|
369
|
+
|
|
370
|
+
# Collapse intervals to points as appropriate.
|
|
371
|
+
for i in range(len(roots)):
|
|
372
|
+
iRoot = roots[i]
|
|
373
|
+
# If interval is small, just return a single value (not an interval).
|
|
374
|
+
if True: # Skip small interval test, since it's typically a shallow point, not a flat section. np.linalg.norm(iRoot[1] - iRoot[0]) < 2.0 * epsilon:
|
|
375
|
+
roots[i] = 0.5 * (iRoot[0] + iRoot[1])
|
|
351
376
|
|
|
352
377
|
# Sort roots if there's only one dimension.
|
|
353
378
|
if self.nInd == 1:
|
|
354
|
-
roots.sort(key=lambda root: root[0] if
|
|
379
|
+
roots.sort(key=lambda root: root[0] if isinstance(root, tuple) else root)
|
|
355
380
|
|
|
356
381
|
return roots
|
|
357
382
|
|
|
@@ -842,6 +842,11 @@ def test_intersect():
|
|
|
842
842
|
|
|
843
843
|
return # Comment this line to run the following additional really lengthy test
|
|
844
844
|
|
|
845
|
+
maxError = 0.0
|
|
846
|
+
F = lambda u , v : (u ** 2 + (v - 3/4) ** 2 - 1/25) * \
|
|
847
|
+
((u - 2/5) ** 2 + (v - 3/5) ** 2 - 1/25) * \
|
|
848
|
+
(u ** 2 + (v - 3/2) ** 2 - 25/16) * \
|
|
849
|
+
((u - 1) ** 2 + (v - 3/10) ** 2 - 1/25)
|
|
845
850
|
order = 9
|
|
846
851
|
knots = [0.0] * order + [1.0] * order
|
|
847
852
|
nCoef = order
|
|
@@ -1072,7 +1077,7 @@ def test_zeros():
|
|
|
1072
1077
|
assert expectedRootCount == len(roots)
|
|
1073
1078
|
for root in roots:
|
|
1074
1079
|
value = spline(root)
|
|
1075
|
-
assert np.dot(value, value) < tolerance
|
|
1080
|
+
assert np.dot(value, value) < np.sqrt(tolerance)
|
|
1076
1081
|
|
|
1077
1082
|
tolerance = 1.0e-14
|
|
1078
1083
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|