bspy 4.1__py3-none-any.whl → 4.3__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 +3 -0
- bspy/_spline_domain.py +94 -30
- bspy/_spline_evaluation.py +80 -21
- bspy/_spline_fitting.py +205 -49
- bspy/_spline_intersection.py +442 -283
- bspy/_spline_operations.py +93 -74
- bspy/hyperplane.py +13 -9
- bspy/manifold.py +10 -5
- bspy/solid.py +22 -15
- bspy/spline.py +195 -53
- bspy/splineOpenGLFrame.py +346 -303
- bspy/spline_block.py +460 -0
- bspy/viewer.py +26 -16
- {bspy-4.1.dist-info → bspy-4.3.dist-info}/METADATA +14 -6
- bspy-4.3.dist-info/RECORD +18 -0
- {bspy-4.1.dist-info → bspy-4.3.dist-info}/WHEEL +1 -1
- bspy-4.1.dist-info/RECORD +0 -17
- {bspy-4.1.dist-info → bspy-4.3.dist-info}/LICENSE +0 -0
- {bspy-4.1.dist-info → bspy-4.3.dist-info}/top_level.txt +0 -0
bspy/_spline_operations.py
CHANGED
|
@@ -13,47 +13,55 @@ def _shiftPolynomial(polynomial, delta):
|
|
|
13
13
|
|
|
14
14
|
def add(self, other, indMap = None):
|
|
15
15
|
if not(self.nDep == other.nDep): raise ValueError("self and other must have same nDep")
|
|
16
|
-
selfMapped =
|
|
17
|
-
otherMapped = []
|
|
16
|
+
selfMapped = set()
|
|
18
17
|
otherToSelf = {}
|
|
19
18
|
if indMap is not None:
|
|
20
19
|
(self, other) = bspy.Spline.common_basis((self, other), indMap)
|
|
21
20
|
for map in indMap:
|
|
22
|
-
selfMapped.
|
|
23
|
-
otherMapped.append(map[1])
|
|
21
|
+
selfMapped.add(map[0])
|
|
24
22
|
otherToSelf[map[1]] = map[0]
|
|
25
23
|
|
|
26
24
|
# Construct new spline parameters.
|
|
27
|
-
# We index backwards because we're adding transposed coefficients (see below).
|
|
28
25
|
nInd = self.nInd
|
|
29
26
|
order = [*self.order]
|
|
30
27
|
nCoef = [*self.nCoef]
|
|
31
28
|
knots = list(self.knots)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if i not in otherMapped:
|
|
38
|
-
order.append(other.order[other.nInd - 1 - i])
|
|
39
|
-
nCoef.append(other.nCoef[other.nInd - 1 - i])
|
|
40
|
-
knots.append(other.knots[other.nInd - 1 - i])
|
|
41
|
-
permutation.append(self.nInd + i + 1) # Add 1 to account for dependent variables.
|
|
29
|
+
for i in range(other.nInd):
|
|
30
|
+
if i not in otherToSelf:
|
|
31
|
+
order.append(other.order[i])
|
|
32
|
+
nCoef.append(other.nCoef[i])
|
|
33
|
+
knots.append(other.knots[i])
|
|
42
34
|
nInd += 1
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
permutation.append(0) # Account for dependent variables.
|
|
46
|
-
permutation = np.array(permutation)
|
|
35
|
+
|
|
36
|
+
# Build coefs array.
|
|
47
37
|
coefs = np.zeros((self.nDep, *nCoef), self.coefs.dtype)
|
|
48
38
|
|
|
49
|
-
#
|
|
50
|
-
# First, add in self.coefs.
|
|
39
|
+
# Add in self.coefs (you need to transpose coefs for the addition to work properly).
|
|
51
40
|
coefs = coefs.T
|
|
52
41
|
coefs += self.coefs.T
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
#
|
|
42
|
+
coefs = coefs.T
|
|
43
|
+
|
|
44
|
+
# Construct permutation of coefs to transpose coefs to match other.coefs.
|
|
45
|
+
otherUnmappedCount = 0
|
|
46
|
+
permutation = [0] # Account for dependent variables
|
|
47
|
+
for i in range(other.nInd):
|
|
48
|
+
if i in otherToSelf:
|
|
49
|
+
permutation.append(otherToSelf[i] + 1) # Add 1 to account for dependent variables.
|
|
50
|
+
else:
|
|
51
|
+
permutation.append(self.nInd + otherUnmappedCount + 1) # Add 1 to account for dependent variables.
|
|
52
|
+
otherUnmappedCount += 1
|
|
53
|
+
for i in range(self.nInd):
|
|
54
|
+
if i not in selfMapped:
|
|
55
|
+
permutation.append(i + 1) # Add 1 to account for dependent variables.
|
|
56
|
+
|
|
57
|
+
# Permute coefs to match other.coefs
|
|
58
|
+
coefs = coefs.transpose(permutation)
|
|
59
|
+
|
|
60
|
+
# Add in other.coefs (you need to transpose coefs for the addition to work properly).
|
|
61
|
+
coefs = coefs.T
|
|
56
62
|
coefs += other.coefs.T
|
|
63
|
+
coefs = coefs.T
|
|
64
|
+
|
|
57
65
|
# Reverse the permutation.
|
|
58
66
|
coefs = coefs.transpose(np.argsort(permutation))
|
|
59
67
|
|
|
@@ -105,9 +113,9 @@ def confine(self, range_bounds):
|
|
|
105
113
|
intersections.sort(key=lambda intersection: intersection[0])
|
|
106
114
|
|
|
107
115
|
# Remove repeat points at start and end.
|
|
108
|
-
|
|
116
|
+
while intersections[1][0] - intersections[0][0] < epsilon:
|
|
109
117
|
del intersections[1]
|
|
110
|
-
|
|
118
|
+
while intersections[-1][0] - intersections[-2][0] < epsilon:
|
|
111
119
|
del intersections[-2]
|
|
112
120
|
|
|
113
121
|
# Insert order-1 knots at each intersection point.
|
|
@@ -116,9 +124,9 @@ def confine(self, range_bounds):
|
|
|
116
124
|
if unique[ix] == knot:
|
|
117
125
|
count = (order - 1) - counts[ix]
|
|
118
126
|
if count > 0:
|
|
119
|
-
spline = spline.insert_knots((
|
|
127
|
+
spline = spline.insert_knots(((knot, count),))
|
|
120
128
|
else:
|
|
121
|
-
spline = spline.insert_knots((
|
|
129
|
+
spline = spline.insert_knots(((knot, order - 1),))
|
|
122
130
|
|
|
123
131
|
# Go through the boundary points, assigning boundary coefficients, interpolating between boundary points,
|
|
124
132
|
# and removing knots and coefficients where the curve stalls.
|
|
@@ -630,79 +638,90 @@ def multiplyAndConvolve(self, other, indMap = None, productType = 'S'):
|
|
|
630
638
|
|
|
631
639
|
return type(self)(nInd, nDep, order, nCoef, knots, coefs, self.metadata)
|
|
632
640
|
|
|
633
|
-
# Return a matrix of booleans whose [i,j] value indicates if self's partial wrt variable i depends on variable j.
|
|
634
|
-
def _cross_correlation_matrix(self):
|
|
635
|
-
ccm = np.empty((self.nInd, self.nInd), bool)
|
|
636
|
-
for i in range(self.nInd - 1):
|
|
637
|
-
tangent = self.differentiate(i)
|
|
638
|
-
totalCoefs = tangent.coefs.size // tangent.nDep
|
|
639
|
-
ccm[i, i] = True
|
|
640
|
-
for j in range(i + 1, self.nInd):
|
|
641
|
-
coefs = np.moveaxis(tangent.coefs, (0, j + 1), (-1, -2))
|
|
642
|
-
coefs = coefs.reshape(totalCoefs // tangent.nCoef[j], tangent.nCoef[j], tangent.nDep)
|
|
643
|
-
match = True
|
|
644
|
-
for row in coefs:
|
|
645
|
-
first = row[0]
|
|
646
|
-
for point in row[1:]:
|
|
647
|
-
match = np.allclose(point, first)
|
|
648
|
-
if not match:
|
|
649
|
-
break
|
|
650
|
-
if not match:
|
|
651
|
-
break
|
|
652
|
-
ccm[i, j] = ccm[j, i] = not match
|
|
653
|
-
ccm[-1, -1] = True
|
|
654
|
-
return ccm
|
|
655
|
-
|
|
656
641
|
def normal_spline(self, indices=None):
|
|
657
|
-
if abs(self.nInd - self.nDep) != 1: raise ValueError("The number of independent variables must be
|
|
642
|
+
if abs(self.nInd - self.nDep) != 1: raise ValueError("The number of independent variables must be different than the number of dependent variables.")
|
|
658
643
|
|
|
659
|
-
# Construct order
|
|
644
|
+
# Construct order, nCoef, knots, and sample values for generalized cross product of the tangent space.
|
|
660
645
|
newOrder = []
|
|
661
646
|
newKnots = []
|
|
662
647
|
uvwValues = []
|
|
663
648
|
nCoefs = []
|
|
664
649
|
totalCoefs = [1]
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
650
|
+
for nInd in range(self.nInd):
|
|
651
|
+
knots = None
|
|
652
|
+
counts = None
|
|
653
|
+
maxOrder = 0
|
|
654
|
+
maxMap = []
|
|
655
|
+
# First, collect the order, knots, and number of relevant columns for this independent variable.
|
|
656
|
+
for row in self.block:
|
|
657
|
+
for map, spline in row:
|
|
658
|
+
if nInd in map:
|
|
659
|
+
ind = map.index(nInd)
|
|
660
|
+
order = spline.order[ind]
|
|
661
|
+
k, c = np.unique(spline.knots[ind][order-1:spline.nCoef[ind]+1], return_counts=True)
|
|
662
|
+
if knots:
|
|
663
|
+
if maxOrder < order:
|
|
664
|
+
counts += order - maxOrder
|
|
665
|
+
maxOrder = order
|
|
666
|
+
if len(maxMap) < len(map):
|
|
667
|
+
maxMap = map
|
|
668
|
+
for knot, count in zip(k[1:-1], c[1:-1]):
|
|
669
|
+
ix = np.searchsorted(knots, knot)
|
|
670
|
+
if knots[ix] == knot:
|
|
671
|
+
counts[ix] = max(counts[ix], count + maxOrder - order)
|
|
672
|
+
else:
|
|
673
|
+
knots = np.insert(knots, ix, knot)
|
|
674
|
+
counts = np.insert(counts, ix, count + maxOrder - order)
|
|
675
|
+
else:
|
|
676
|
+
knots = k
|
|
677
|
+
counts = c
|
|
678
|
+
maxOrder = order
|
|
679
|
+
maxMap = map
|
|
680
|
+
|
|
681
|
+
break
|
|
682
|
+
|
|
683
|
+
# Next, calculate the order of the normal for this independent variable.
|
|
668
684
|
# Note that the total order will be one less than usual, because one of
|
|
669
685
|
# the tangents is the derivative with respect to that independent variable.
|
|
670
|
-
newOrd = 0
|
|
671
686
|
if self.nInd < self.nDep:
|
|
672
687
|
# If this normal involves all tangents, simply add the degree of each,
|
|
673
|
-
# so long as that tangent contains the independent variable.
|
|
674
|
-
|
|
675
|
-
newOrd += order - 1 if ccm[i, j] else 0
|
|
688
|
+
# so long as that tangent contains the independent variable.
|
|
689
|
+
order = (maxOrder - 1) * len(maxMap)
|
|
676
690
|
else:
|
|
677
691
|
# If this normal doesn't involve all tangents, find the max order of
|
|
678
692
|
# each returned combination (as defined by the indices).
|
|
679
|
-
|
|
693
|
+
order = 0
|
|
694
|
+
for index in maxMap if indices is None else indices:
|
|
680
695
|
# The order will be one larger if this independent variable's tangent is excluded by the index.
|
|
681
|
-
ord = 0 if index !=
|
|
696
|
+
ord = 0 if index != nInd else 1
|
|
682
697
|
# Add the degree of each tangent, so long as that tangent contains the
|
|
683
698
|
# independent variable and is not excluded by the index.
|
|
684
|
-
for
|
|
685
|
-
ord +=
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
counts
|
|
691
|
-
counts[
|
|
692
|
-
|
|
699
|
+
for ind in maxMap:
|
|
700
|
+
ord += maxOrder - 1 if index != ind else 0
|
|
701
|
+
order = max(order, ord)
|
|
702
|
+
|
|
703
|
+
# Now, record the order of this independent variable and adjust the knot counts.
|
|
704
|
+
newOrder.append(order)
|
|
705
|
+
counts += order - maxOrder + 1 # Because we're multiplying all the tangents, the knot elevation is one more
|
|
706
|
+
counts[0] = order # But not at the endpoints, which are full order as usual
|
|
707
|
+
counts[-1] = order # But not at the endpoints, which are full order as usual
|
|
708
|
+
newKnots.append(np.repeat(knots, counts))
|
|
709
|
+
|
|
693
710
|
# Also calculate the total number of coefficients, capturing how it progressively increases, and
|
|
694
711
|
# using that calculation to span uvw from the starting knot to the end for each variable.
|
|
695
712
|
nCoef = len(newKnots[-1]) - newOrder[-1]
|
|
696
713
|
totalCoefs.append(totalCoefs[-1] * nCoef)
|
|
697
|
-
knotAverages = bspy.Spline(1, 0, [
|
|
714
|
+
knotAverages = bspy.Spline(1, 0, [order], [nCoef], [newKnots[-1]], []).greville()
|
|
698
715
|
for iKnot in range(1, len(knotAverages) - 1):
|
|
699
716
|
if knotAverages[iKnot] == knotAverages[iKnot + 1]:
|
|
700
717
|
knotAverages[iKnot] = 0.5 * (knotAverages[iKnot - 1] + knotAverages[iKnot])
|
|
701
718
|
knotAverages[iKnot + 1] = 0.5 * (knotAverages[iKnot + 1] + knotAverages[iKnot + 2])
|
|
702
719
|
uvwValues.append(knotAverages)
|
|
703
720
|
nCoefs.append(nCoef)
|
|
721
|
+
|
|
722
|
+
# Construct data points for normal.
|
|
704
723
|
points = []
|
|
705
|
-
ijk = [0
|
|
724
|
+
ijk = [0] * self.nInd
|
|
706
725
|
for i in range(totalCoefs[-1]):
|
|
707
726
|
uvw = [uvwValues[j][k] for j, k in enumerate(ijk)]
|
|
708
727
|
points.append(self.normal(uvw, False, indices))
|
|
@@ -716,7 +735,7 @@ def normal_spline(self, indices=None):
|
|
|
716
735
|
nCoefs.reverse()
|
|
717
736
|
points = np.reshape(points, [nDep] + nCoefs)
|
|
718
737
|
points = np.transpose(points, [0] + list(range(self.nInd, 0, -1)))
|
|
719
|
-
return bspy.Spline.least_squares(uvwValues, points, order = newOrder, knots = newKnots
|
|
738
|
+
return bspy.Spline.least_squares(uvwValues, points, order = newOrder, knots = newKnots)
|
|
720
739
|
|
|
721
740
|
def rotate(self, vector, angle):
|
|
722
741
|
vector = np.atleast_1d(vector)
|
bspy/hyperplane.py
CHANGED
|
@@ -18,6 +18,9 @@ class Hyperplane(Manifold):
|
|
|
18
18
|
|
|
19
19
|
tangentSpace : array-like
|
|
20
20
|
A array of tangents that are linearly independent and orthogonal to the normal.
|
|
21
|
+
|
|
22
|
+
metadata : `dict`, optional
|
|
23
|
+
A dictionary of ancillary data to store with the hyperplane. Default is {}.
|
|
21
24
|
|
|
22
25
|
Notes
|
|
23
26
|
-----
|
|
@@ -25,14 +28,15 @@ class Hyperplane(Manifold):
|
|
|
25
28
|
Thus the dimension of the domain is one less than that of the range.
|
|
26
29
|
"""
|
|
27
30
|
|
|
28
|
-
maxAlignment = 0.
|
|
29
|
-
""" If
|
|
31
|
+
maxAlignment = 0.9999 # 1 - 1/10^4
|
|
32
|
+
""" If the absolute value of the dot product of two unit normals is greater than maxAlignment, the manifolds are parallel."""
|
|
30
33
|
|
|
31
|
-
def __init__(self, normal, point, tangentSpace):
|
|
32
|
-
self._normal = np.atleast_1d(
|
|
33
|
-
self._point = np.atleast_1d(
|
|
34
|
-
self._tangentSpace = np.atleast_1d(
|
|
34
|
+
def __init__(self, normal, point, tangentSpace, metadata = {}):
|
|
35
|
+
self._normal = np.atleast_1d(normal)
|
|
36
|
+
self._point = np.atleast_1d(point)
|
|
37
|
+
self._tangentSpace = np.atleast_1d(tangentSpace)
|
|
35
38
|
if not np.allclose(self._tangentSpace.T @ self._normal, 0.0): raise ValueError("normal must be orthogonal to tangent space")
|
|
39
|
+
self.metadata = dict(metadata)
|
|
36
40
|
|
|
37
41
|
def __repr__(self):
|
|
38
42
|
return "Hyperplane({0}, {1}, {2})".format(self._normal, self._point, self._tangentSpace)
|
|
@@ -172,7 +176,7 @@ class Hyperplane(Manifold):
|
|
|
172
176
|
-------
|
|
173
177
|
point : `numpy.array`
|
|
174
178
|
"""
|
|
175
|
-
return np.dot(self._tangentSpace, domainPoint) + self._point
|
|
179
|
+
return np.dot(self._tangentSpace, np.atleast_1d(domainPoint)) + self._point
|
|
176
180
|
|
|
177
181
|
def flip_normal(self):
|
|
178
182
|
"""
|
|
@@ -207,7 +211,7 @@ class Hyperplane(Manifold):
|
|
|
207
211
|
--------
|
|
208
212
|
`to_dict` : Return a `dict` with `Hyperplane` data.
|
|
209
213
|
"""
|
|
210
|
-
return Hyperplane(dictionary["normal"], dictionary["point"], dictionary["tangentSpace"])
|
|
214
|
+
return Hyperplane(dictionary["normal"], dictionary["point"], dictionary["tangentSpace"], dictionary.get("metadata", {}))
|
|
211
215
|
|
|
212
216
|
def full_domain(self):
|
|
213
217
|
"""
|
|
@@ -455,7 +459,7 @@ class Hyperplane(Manifold):
|
|
|
455
459
|
--------
|
|
456
460
|
`from_dict` : Create a `Hyperplane` from a data in a `dict`.
|
|
457
461
|
"""
|
|
458
|
-
return {"type" : "Hyperplane", "normal" : self._normal, "point" : self._point, "tangentSpace" : self._tangentSpace}
|
|
462
|
+
return {"type" : "Hyperplane", "normal" : self._normal, "point" : self._point, "tangentSpace" : self._tangentSpace, "metadata" : self.metadata}
|
|
459
463
|
|
|
460
464
|
def transform(self, matrix, matrixInverseTranspose = None):
|
|
461
465
|
"""
|
bspy/manifold.py
CHANGED
|
@@ -5,10 +5,15 @@ class Manifold:
|
|
|
5
5
|
"""
|
|
6
6
|
A manifold is an abstract base class for differentiable functions with
|
|
7
7
|
normals and tangent spaces whose range is one dimension higher than their domain.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
metadata : `dict`, optional
|
|
12
|
+
A dictionary of ancillary data to store with the manifold. Default is {}.
|
|
8
13
|
"""
|
|
9
14
|
|
|
10
|
-
minSeparation = 0.
|
|
11
|
-
"""If two points are within
|
|
15
|
+
minSeparation = 0.0001
|
|
16
|
+
"""If two points are within minSeparation of each each other, they are coincident."""
|
|
12
17
|
|
|
13
18
|
Crossing = namedtuple('Crossing', ('left','right'))
|
|
14
19
|
Coincidence = namedtuple('Coincidence', ('left', 'right', 'alignment', 'transform', 'inverse', 'translation'))
|
|
@@ -17,8 +22,8 @@ class Manifold:
|
|
|
17
22
|
factory = {}
|
|
18
23
|
"""Factory dictionary for creating manifolds."""
|
|
19
24
|
|
|
20
|
-
def __init__(self):
|
|
21
|
-
|
|
25
|
+
def __init__(self, metadata = {}):
|
|
26
|
+
self.metadata = dict(metadata)
|
|
22
27
|
|
|
23
28
|
def cached_intersect(self, other, cache = None):
|
|
24
29
|
"""
|
|
@@ -322,7 +327,7 @@ class Manifold:
|
|
|
322
327
|
--------
|
|
323
328
|
`from_dict` : Create a `Manifold` from a data in a `dict`.
|
|
324
329
|
"""
|
|
325
|
-
return
|
|
330
|
+
return {"metadata" : self.metadata}
|
|
326
331
|
|
|
327
332
|
def transform(self, matrix, matrixInverseTranspose = None):
|
|
328
333
|
"""
|
bspy/solid.py
CHANGED
|
@@ -59,6 +59,9 @@ class Solid:
|
|
|
59
59
|
|
|
60
60
|
containsInfinity : `bool`
|
|
61
61
|
Indicates whether or not the solid contains infinity.
|
|
62
|
+
|
|
63
|
+
metadata : `dict`, optional
|
|
64
|
+
A dictionary of ancillary data to store with the solid. Default is {}.
|
|
62
65
|
|
|
63
66
|
See also
|
|
64
67
|
--------
|
|
@@ -70,10 +73,11 @@ class Solid:
|
|
|
70
73
|
|
|
71
74
|
Solids can be of zero dimension, typically acting as the domain of boundary endpoints. Zero-dimension solids have no boundaries, they only contain infinity or not.
|
|
72
75
|
"""
|
|
73
|
-
def __init__(self, dimension, containsInfinity):
|
|
76
|
+
def __init__(self, dimension, containsInfinity, metadata = {}):
|
|
74
77
|
assert dimension >= 0
|
|
75
78
|
self.dimension = dimension
|
|
76
79
|
self.containsInfinity = containsInfinity
|
|
80
|
+
self.metadata = dict(metadata)
|
|
77
81
|
self.boundaries = []
|
|
78
82
|
self.bounds = None
|
|
79
83
|
|
|
@@ -113,7 +117,7 @@ class Solid:
|
|
|
113
117
|
if boundary.manifold.range_dimension() != self.dimension: raise ValueError("Dimensions don't match")
|
|
114
118
|
self.boundaries.append(boundary)
|
|
115
119
|
if self.bounds is None:
|
|
116
|
-
self.bounds = boundary.bounds
|
|
120
|
+
self.bounds = boundary.bounds.copy()
|
|
117
121
|
elif boundary.bounds is None:
|
|
118
122
|
raise ValueError("Mix of infinite and bounded boundaries")
|
|
119
123
|
else:
|
|
@@ -356,7 +360,7 @@ class Solid:
|
|
|
356
360
|
`save` : Save a solids and/or manifolds in json format to the specified filename (full path).
|
|
357
361
|
"""
|
|
358
362
|
def from_dict(dictionary):
|
|
359
|
-
solid = Solid(dictionary["dimension"], dictionary["containsInfinity"])
|
|
363
|
+
solid = Solid(dictionary["dimension"], dictionary["containsInfinity"], dictionary.get("metadata", {}))
|
|
360
364
|
for boundary in dictionary["boundaries"]:
|
|
361
365
|
manifold = boundary["manifold"]
|
|
362
366
|
solid.add_boundary(Boundary(Manifold.factory[manifold.get("type", "Spline")].from_dict(manifold), from_dict(boundary["domain"])))
|
|
@@ -428,7 +432,7 @@ class Solid:
|
|
|
428
432
|
if isinstance(obj, Boundary):
|
|
429
433
|
return {"type" : "Boundary", "manifold" : obj.manifold, "domain" : obj.domain}
|
|
430
434
|
if isinstance(obj, Solid):
|
|
431
|
-
return {"type" : "Solid", "dimension" : obj.dimension, "containsInfinity" : obj.containsInfinity, "boundaries" : obj.boundaries}
|
|
435
|
+
return {"type" : "Solid", "dimension" : obj.dimension, "containsInfinity" : obj.containsInfinity, "boundaries" : obj.boundaries, "metadata" : obj.metadata}
|
|
432
436
|
return super().default(obj)
|
|
433
437
|
|
|
434
438
|
with open(fileName, 'w', encoding='utf-8') as file:
|
|
@@ -509,25 +513,28 @@ class Solid:
|
|
|
509
513
|
slice.add_boundary(Boundary(right, domainSlice))
|
|
510
514
|
|
|
511
515
|
elif isinstance(intersection, Manifold.Coincidence):
|
|
512
|
-
#
|
|
513
|
-
|
|
514
|
-
#
|
|
516
|
+
# Intersect domain coincidence with the boundary's domain.
|
|
517
|
+
left = left.intersection(boundary.domain)
|
|
518
|
+
# Invert the domain coincidence (which will remove it) if this is a twin or if the normals point in opposite directions.
|
|
519
|
+
#invertCoincidence = trimTwin and (isTwin or intersection.alignment < 0.0)
|
|
515
520
|
invertCoincidence = (trimTwin and isTwin) or intersection.alignment < 0.0
|
|
521
|
+
# Create the coincidence to hold the trimmed and transformed domain coincidence (left).
|
|
522
|
+
coincidence = Solid(left.dimension, left.containsInfinity)
|
|
516
523
|
if invertCoincidence:
|
|
517
524
|
coincidence.containsInfinity = not coincidence.containsInfinity
|
|
518
525
|
# Next, transform the domain coincidence from the boundary to the given manifold.
|
|
519
526
|
# Create copies of the manifolds and boundaries, since we are changing them.
|
|
520
|
-
for
|
|
521
|
-
|
|
527
|
+
for coincidenceBoundary in left.boundaries:
|
|
528
|
+
coincidenceManifold = coincidenceBoundary.manifold
|
|
522
529
|
if invertCoincidence:
|
|
523
|
-
|
|
530
|
+
coincidenceManifold = coincidenceManifold.flip_normal()
|
|
524
531
|
if isTwin:
|
|
525
|
-
|
|
526
|
-
|
|
532
|
+
coincidenceManifold = coincidenceManifold.translate(-intersection.translation)
|
|
533
|
+
coincidenceManifold = coincidenceManifold.transform(intersection.inverse, intersection.transform.T)
|
|
527
534
|
else:
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
coincidence.
|
|
535
|
+
coincidenceManifold = coincidenceManifold.transform(intersection.transform, intersection.inverse.T)
|
|
536
|
+
coincidenceManifold = coincidenceManifold.translate(intersection.translation)
|
|
537
|
+
coincidence.add_boundary(Boundary(coincidenceManifold, coincidenceBoundary.domain))
|
|
531
538
|
# Finally, add the domain coincidence to the list of coincidences.
|
|
532
539
|
coincidences.append((invertCoincidence, coincidence))
|
|
533
540
|
|