bspy 4.4__py3-none-any.whl → 5.0.0__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/_spline_domain.py +36 -5
- bspy/_spline_evaluation.py +1 -1
- bspy/_spline_fitting.py +6 -2
- bspy/_spline_intersection.py +85 -84
- bspy/_spline_milling.py +111 -56
- bspy/hyperplane.py +35 -35
- bspy/manifold.py +40 -40
- bspy/solid.py +162 -159
- bspy/spline.py +49 -30
- bspy/splineOpenGLFrame.py +7 -7
- bspy/viewer.py +3 -3
- {bspy-4.4.dist-info → bspy-5.0.0.dist-info}/METADATA +8 -2
- bspy-5.0.0.dist-info/RECORD +19 -0
- {bspy-4.4.dist-info → bspy-5.0.0.dist-info}/WHEEL +1 -1
- bspy-4.4.dist-info/RECORD +0 -19
- {bspy-4.4.dist-info → bspy-5.0.0.dist-info}/licenses/LICENSE +0 -0
- {bspy-4.4.dist-info → bspy-5.0.0.dist-info}/top_level.txt +0 -0
bspy/_spline_milling.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import bspy.spline
|
|
3
3
|
import bspy.spline_block
|
|
4
|
+
from collections import namedtuple
|
|
4
5
|
|
|
5
6
|
def line_of_curvature(self, uvStart, is_max, tolerance = 1.0e-3):
|
|
6
7
|
if self.nInd != 2: raise ValueError("Surface must have two independent variables")
|
|
@@ -115,6 +116,60 @@ def offset(self, edgeRadius, bitRadius=None, angle=np.pi / 2.2, path=None, subtr
|
|
|
115
116
|
if path is not None and (path.nInd != 1 or path.nDep != 2 or self.nInd != 2):
|
|
116
117
|
raise ValueError("path must be a 2D curve and self must be a 3D surface")
|
|
117
118
|
|
|
119
|
+
# Compute new order, knots, and fillets for offset (ensure order is at least 4).
|
|
120
|
+
Fillet = namedtuple('Fillet', ('adjustment', 'isFillet'))
|
|
121
|
+
newOrder = []
|
|
122
|
+
newKnotList = []
|
|
123
|
+
newUniqueList = []
|
|
124
|
+
filletList = []
|
|
125
|
+
for order, knots in zip(self.order, self.knots):
|
|
126
|
+
min4Order = max(order, 4)
|
|
127
|
+
unique, counts = np.unique(knots, return_counts=True)
|
|
128
|
+
counts += min4Order - order # Ensure order is at least 4
|
|
129
|
+
newOrder.append(min4Order)
|
|
130
|
+
adjustment = 0
|
|
131
|
+
epsilon = np.finfo(unique.dtype).eps
|
|
132
|
+
|
|
133
|
+
# Add first knot.
|
|
134
|
+
newKnots = [unique[0]] * counts[0]
|
|
135
|
+
newUnique = [unique[0]]
|
|
136
|
+
fillets = [Fillet(adjustment, False)]
|
|
137
|
+
|
|
138
|
+
# Add internal knots, checking for C1 discontinuities needing fillets.
|
|
139
|
+
for knot, count in zip(unique[1:-1], counts[1:-1]):
|
|
140
|
+
knot += adjustment
|
|
141
|
+
newKnots += [knot] * count
|
|
142
|
+
newUnique.append(knot)
|
|
143
|
+
# Check for lack of C1 continuity (need for a fillet)
|
|
144
|
+
if count >= min4Order - 1:
|
|
145
|
+
fillets.append(Fillet(adjustment, True))
|
|
146
|
+
# Create parametric space for fillet.
|
|
147
|
+
adjustment += 1
|
|
148
|
+
knot += 1 + epsilon # Add additional adjustment and step slightly past discontinuity
|
|
149
|
+
newKnots += [knot] * (min4Order - 1)
|
|
150
|
+
newUnique.append(knot)
|
|
151
|
+
fillets.append(Fillet(adjustment, False))
|
|
152
|
+
|
|
153
|
+
# Add last knot.
|
|
154
|
+
newKnots += [unique[-1] + adjustment] * counts[-1]
|
|
155
|
+
newUnique.append(unique[-1] + adjustment)
|
|
156
|
+
fillets.append(Fillet(adjustment, False))
|
|
157
|
+
|
|
158
|
+
# Build fillet and knot lists.
|
|
159
|
+
newKnotList.append(np.array(newKnots, knots.dtype))
|
|
160
|
+
newUniqueList.append(np.array(newUnique, knots.dtype))
|
|
161
|
+
filletList.append(fillets)
|
|
162
|
+
|
|
163
|
+
if path is not None:
|
|
164
|
+
min4Order = max(path.order[0], 4)
|
|
165
|
+
newOrder = [min4Order]
|
|
166
|
+
unique, counts = np.unique(path.knots[0], return_counts=True)
|
|
167
|
+
counts += min4Order - path.order[0] # Ensure order is at least 4
|
|
168
|
+
newKnotList = [np.repeat(unique, counts)]
|
|
169
|
+
domain = path.domain()
|
|
170
|
+
else:
|
|
171
|
+
domain = [(unique[0], unique[-1]) for unique in newUniqueList]
|
|
172
|
+
|
|
118
173
|
# Determine geometry of drill bit.
|
|
119
174
|
if subtract:
|
|
120
175
|
edgeRadius *= -1
|
|
@@ -126,83 +181,83 @@ def offset(self, edgeRadius, bitRadius=None, angle=np.pi / 2.2, path=None, subtr
|
|
|
126
181
|
|
|
127
182
|
# Define drill bit function.
|
|
128
183
|
if abs(w) < tolerance and path is None: # Simple offset curve or surface
|
|
129
|
-
def drillBit(
|
|
130
|
-
return
|
|
184
|
+
def drillBit(normal):
|
|
185
|
+
return edgeRadius * normal
|
|
131
186
|
elif self.nDep == 2: # General offset curve
|
|
132
|
-
def drillBit(
|
|
133
|
-
xy = self(u)
|
|
134
|
-
normal = self.normal(u)
|
|
187
|
+
def drillBit(normal):
|
|
135
188
|
upward = np.sign(normal[1])
|
|
136
189
|
if upward * normal[1] <= bottom:
|
|
137
|
-
|
|
138
|
-
xy[1] += edgeRadius * normal[1]
|
|
190
|
+
return np.array((edgeRadius * normal[0] + w * np.sign(normal[0]), edgeRadius * normal[1]))
|
|
139
191
|
else:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
elif self.nDep == 3 and path is None: # General offset surface
|
|
144
|
-
def drillBit(uv):
|
|
145
|
-
xyz = self(uv)
|
|
146
|
-
normal = self.normal(uv)
|
|
192
|
+
return np.array((bottomRadius * normal[0], bottomRadius * normal[1] - upward * h))
|
|
193
|
+
elif self.nDep == 3: # General offset surface
|
|
194
|
+
def drillBit(normal):
|
|
147
195
|
upward = np.sign(normal[1])
|
|
148
196
|
if upward * normal[1] <= bottom:
|
|
149
197
|
norm = np.sqrt(normal[0] * normal[0] + normal[2] * normal[2])
|
|
150
|
-
|
|
151
|
-
xyz[1] += edgeRadius * normal[1]
|
|
152
|
-
xyz[2] += edgeRadius * normal[2] + w * normal[2] / norm
|
|
198
|
+
return np.array((edgeRadius * normal[0] + w * normal[0] / norm, edgeRadius * normal[1], edgeRadius * normal[2] + w * normal[2] / norm))
|
|
153
199
|
else:
|
|
154
|
-
|
|
155
|
-
xyz[1] += bottomRadius * normal[1] - upward * h
|
|
156
|
-
xyz[2] += bottomRadius * normal[2]
|
|
157
|
-
return xyz
|
|
158
|
-
elif self.nDep == 3: # General offset of a given path along a surface
|
|
159
|
-
surface = self
|
|
160
|
-
self = path # Redefine self to be the path (used below for fitting)
|
|
161
|
-
def drillBit(u):
|
|
162
|
-
uv = self(u)
|
|
163
|
-
xyz = surface(uv)
|
|
164
|
-
normal = surface.normal(uv)
|
|
165
|
-
upward = np.sign(normal[1])
|
|
166
|
-
if upward * normal[1] <= bottom:
|
|
167
|
-
norm = np.sqrt(normal[0] * normal[0] + normal[2] * normal[2])
|
|
168
|
-
xyz[0] += edgeRadius * normal[0] + w * normal[0] / norm
|
|
169
|
-
xyz[1] += edgeRadius * normal[1]
|
|
170
|
-
xyz[2] += edgeRadius * normal[2] + w * normal[2] / norm
|
|
171
|
-
else:
|
|
172
|
-
xyz[0] += bottomRadius * normal[0]
|
|
173
|
-
xyz[1] += bottomRadius * normal[1] - upward * h
|
|
174
|
-
xyz[2] += bottomRadius * normal[2]
|
|
175
|
-
return xyz
|
|
200
|
+
return np.array((bottomRadius * normal[0], bottomRadius * normal[1] - upward * h, bottomRadius * normal[2]))
|
|
176
201
|
else: # Should never get here (exception raised earlier)
|
|
177
202
|
raise ValueError("The offset is only defined for 2D curves and 3D surfaces with well-defined normals.")
|
|
178
203
|
|
|
179
|
-
#
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
204
|
+
# Define function to pass to fit.
|
|
205
|
+
def fitFunction(uv):
|
|
206
|
+
if path is not None:
|
|
207
|
+
uv = path(uv)
|
|
208
|
+
|
|
209
|
+
# Compute adjusted spline uv values, accounting for fillets.
|
|
210
|
+
hasFillet = False
|
|
211
|
+
adjustedUV = uv.copy()
|
|
212
|
+
for (i, u), unique, fillets in zip(enumerate(uv), newUniqueList, filletList):
|
|
213
|
+
ix = np.searchsorted(unique, u, 'right') - 1
|
|
214
|
+
fillet = fillets[ix]
|
|
215
|
+
if fillet.isFillet:
|
|
216
|
+
hasFillet = True
|
|
217
|
+
adjustedUV[i] = unique[ix] - fillet.adjustment
|
|
218
|
+
else:
|
|
219
|
+
adjustedUV[i] -= fillet.adjustment
|
|
220
|
+
|
|
221
|
+
# If we have fillets, compute the normal from their normal fan.
|
|
222
|
+
if hasFillet:
|
|
223
|
+
normal = np.zeros(self.nDep, self.coefs.dtype)
|
|
224
|
+
nudged = adjustedUV.copy()
|
|
225
|
+
for (i, u), unique, fillets in zip(enumerate(uv), newUniqueList, filletList):
|
|
226
|
+
ix = np.searchsorted(unique, u, 'right') - 1
|
|
227
|
+
fillet = fillets[ix]
|
|
228
|
+
if fillet.isFillet:
|
|
229
|
+
epsilon = np.finfo(unique.dtype).eps
|
|
230
|
+
alpha = u - unique[ix]
|
|
231
|
+
np.copyto(nudged, adjustedUV)
|
|
232
|
+
nudged[i] -= epsilon
|
|
233
|
+
normal += (1 - alpha) * self.normal(nudged)
|
|
234
|
+
nudged[i] += 2 * epsilon
|
|
235
|
+
normal += alpha * self.normal(nudged)
|
|
236
|
+
normal = normal / np.linalg.norm(normal)
|
|
237
|
+
else:
|
|
238
|
+
normal = self.normal(adjustedUV)
|
|
239
|
+
|
|
240
|
+
# Return the offset based on the normal.
|
|
241
|
+
return self(adjustedUV) + drillBit(normal)
|
|
188
242
|
|
|
189
243
|
# Fit new spline to offset by drill bit.
|
|
190
|
-
offset = bspy.spline.Spline.fit(
|
|
244
|
+
offset = bspy.spline.Spline.fit(domain, fitFunction, newOrder, newKnotList, tolerance)
|
|
191
245
|
|
|
192
246
|
# Remove cusps as required (only applies to offset curves).
|
|
193
|
-
if removeCusps and self.nInd == 1:
|
|
247
|
+
if removeCusps and (self.nInd == 1 or path is not None):
|
|
194
248
|
# Find the cusps by checking for tangent direction reversal between the spline and offset.
|
|
195
249
|
cusps = []
|
|
196
250
|
previousKnot = None
|
|
197
251
|
start = None
|
|
198
252
|
for knot in np.unique(offset.knots[0][offset.order[0]:offset.nCoef[0]]):
|
|
199
|
-
tangent = self.derivative((1,), knot)
|
|
200
253
|
if path is not None:
|
|
201
|
-
tangent =
|
|
202
|
-
|
|
203
|
-
|
|
254
|
+
tangent = self.jacobian(path(knot)) @ path.derivative((1,), knot)
|
|
255
|
+
else:
|
|
256
|
+
tangent = self.derivative((1,), knot)
|
|
257
|
+
negated = np.dot(tangent, offset.derivative((1,), knot)) < 0
|
|
258
|
+
if negated and start is None:
|
|
204
259
|
start = knot
|
|
205
|
-
if not
|
|
260
|
+
if not negated and start is not None:
|
|
206
261
|
cusps.append((start, previousKnot))
|
|
207
262
|
start = None
|
|
208
263
|
previousKnot = knot
|
|
@@ -219,7 +274,7 @@ def offset(self, edgeRadius, bitRadius=None, angle=np.pi / 2.2, path=None, subtr
|
|
|
219
274
|
# This is necessary to find the intersection point (2 equations, 2 unknowns).
|
|
220
275
|
tangent = offset.derivative((1,), cusp[0])
|
|
221
276
|
projection = np.concatenate((tangent / np.linalg.norm(tangent),
|
|
222
|
-
|
|
277
|
+
self.normal(path(cusp[0])))).reshape((2,3))
|
|
223
278
|
before = before.transform(projection)
|
|
224
279
|
after = after.transform(projection)
|
|
225
280
|
block = bspy.spline_block.SplineBlock([[before, after]])
|
bspy/hyperplane.py
CHANGED
|
@@ -41,19 +41,19 @@ class Hyperplane(Manifold):
|
|
|
41
41
|
def __repr__(self):
|
|
42
42
|
return "Hyperplane({0}, {1}, {2})".format(self._normal, self._point, self._tangentSpace)
|
|
43
43
|
|
|
44
|
-
def
|
|
44
|
+
def complete_cutout(self, cutout, solid):
|
|
45
45
|
"""
|
|
46
|
-
Add any missing inherent (implicit) boundaries of this manifold's domain to the given
|
|
47
|
-
given solid that are needed to make the
|
|
46
|
+
Add any missing inherent (implicit) boundaries of this manifold's domain to the given cutout of the
|
|
47
|
+
given solid that are needed to make the cutout valid and complete.
|
|
48
48
|
|
|
49
49
|
Parameters
|
|
50
50
|
----------
|
|
51
|
-
|
|
52
|
-
The
|
|
51
|
+
cutout : `Solid`
|
|
52
|
+
The cutout of the given solid formed by the manifold. The cutout may be incomplete, missing some of the
|
|
53
53
|
manifold's inherent domain boundaries. Its dimension must match `self.domain_dimension()`.
|
|
54
54
|
|
|
55
55
|
solid : `Solid`
|
|
56
|
-
The solid
|
|
56
|
+
The solid determining the cutout of the manifold. Its dimension must match `self.range_dimension()`.
|
|
57
57
|
|
|
58
58
|
Parameters
|
|
59
59
|
----------
|
|
@@ -63,17 +63,17 @@ class Hyperplane(Manifold):
|
|
|
63
63
|
|
|
64
64
|
See Also
|
|
65
65
|
--------
|
|
66
|
-
`Solid.
|
|
66
|
+
`Solid.compute_cutout` : Compute the cutout portion of the manifold within the solid.
|
|
67
67
|
|
|
68
68
|
Notes
|
|
69
69
|
-----
|
|
70
70
|
Since hyperplanes have no inherent domain boundaries, this operation only tests for
|
|
71
71
|
point containment for zero-dimension hyperplanes (points).
|
|
72
72
|
"""
|
|
73
|
-
assert self.domain_dimension() ==
|
|
73
|
+
assert self.domain_dimension() == cutout.dimension
|
|
74
74
|
assert self.range_dimension() == solid.dimension
|
|
75
|
-
if
|
|
76
|
-
|
|
75
|
+
if cutout.dimension == 0:
|
|
76
|
+
cutout.containsInfinity = solid.contains_point(self._point)
|
|
77
77
|
|
|
78
78
|
def copy(self):
|
|
79
79
|
"""
|
|
@@ -86,7 +86,7 @@ class Hyperplane(Manifold):
|
|
|
86
86
|
return Hyperplane(self._normal, self._point, self._tangentSpace)
|
|
87
87
|
|
|
88
88
|
@staticmethod
|
|
89
|
-
def create_axis_aligned(dimension, axis, offset,
|
|
89
|
+
def create_axis_aligned(dimension, axis, offset, negateNormal=False):
|
|
90
90
|
"""
|
|
91
91
|
Create an axis-aligned hyperplane.
|
|
92
92
|
|
|
@@ -101,7 +101,7 @@ class Hyperplane(Manifold):
|
|
|
101
101
|
offset : `float`
|
|
102
102
|
The offset from zero along the axis of a point on the hyperplane.
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
negateNormal : `bool`, optional
|
|
105
105
|
A Boolean indicating that the normal should point toward in the negative direction along the axis.
|
|
106
106
|
Default is False, meaning the normal points in the positive direction along the axis.
|
|
107
107
|
|
|
@@ -112,7 +112,7 @@ class Hyperplane(Manifold):
|
|
|
112
112
|
"""
|
|
113
113
|
assert dimension > 0
|
|
114
114
|
diagonal = np.identity(dimension)
|
|
115
|
-
sign = -1.0 if
|
|
115
|
+
sign = -1.0 if negateNormal else 1.0
|
|
116
116
|
normal = sign * diagonal[:,axis]
|
|
117
117
|
point = offset * normal
|
|
118
118
|
if dimension > 1:
|
|
@@ -178,21 +178,6 @@ class Hyperplane(Manifold):
|
|
|
178
178
|
"""
|
|
179
179
|
return np.dot(self._tangentSpace, np.atleast_1d(domainPoint)) + self._point
|
|
180
180
|
|
|
181
|
-
def flip_normal(self):
|
|
182
|
-
"""
|
|
183
|
-
Flip the direction of the normal.
|
|
184
|
-
|
|
185
|
-
Returns
|
|
186
|
-
-------
|
|
187
|
-
hyperplane : `Hyperplane`
|
|
188
|
-
The hyperplane with flipped normal. The hyperplane retains the same tangent space.
|
|
189
|
-
|
|
190
|
-
See Also
|
|
191
|
-
--------
|
|
192
|
-
`Solid.complement` : Return the complement of the solid: whatever was inside is outside and vice-versa.
|
|
193
|
-
"""
|
|
194
|
-
return Hyperplane(-self._normal, self._point, self._tangentSpace)
|
|
195
|
-
|
|
196
181
|
@staticmethod
|
|
197
182
|
def from_dict(dictionary):
|
|
198
183
|
"""
|
|
@@ -244,14 +229,14 @@ class Hyperplane(Manifold):
|
|
|
244
229
|
(Hyperplanes will have at most one intersection, but other types of manifolds can have several.)
|
|
245
230
|
Each intersection records either a crossing or a coincident region.
|
|
246
231
|
|
|
247
|
-
For a crossing, intersection is a `Manifold.Crossing`: (
|
|
248
|
-
*
|
|
249
|
-
*
|
|
232
|
+
For a crossing, intersection is a `Manifold.Crossing`: (firstPart, secondPart)
|
|
233
|
+
* firstPart : `Manifold` in the manifold's domain where the manifold and the other cross.
|
|
234
|
+
* secondPart : `Manifold` in the other's domain where the manifold and the other cross.
|
|
250
235
|
* Both intersection manifolds have the same domain and range (the crossing between the manifold and the other).
|
|
251
236
|
|
|
252
|
-
For a coincident region, intersection is a `Manifold.Coincidence`: (
|
|
253
|
-
*
|
|
254
|
-
*
|
|
237
|
+
For a coincident region, intersection is a `Manifold.Coincidence`: (firstPart, secondPart, alignment, transform, inverse, translation)
|
|
238
|
+
* firstPart : `Solid` in the manifold's domain within which the manifold and the other are coincident.
|
|
239
|
+
* secondPart : `Solid` in the other's domain within which the manifold and the other are coincident.
|
|
255
240
|
* alignment : scalar value holding the normal alignment between the manifold and the other (the dot product of their unit normals).
|
|
256
241
|
* transform : `numpy.array` holding the transform matrix from the manifold's domain to the other's domain.
|
|
257
242
|
* inverse : `numpy.array` holding the inverse transform matrix from the other's domain to the boundary's domain.
|
|
@@ -260,7 +245,7 @@ class Hyperplane(Manifold):
|
|
|
260
245
|
|
|
261
246
|
See Also
|
|
262
247
|
--------
|
|
263
|
-
`Solid.
|
|
248
|
+
`Solid.compute_cutout` : Compute the cutout portion of the manifold within the solid.
|
|
264
249
|
`numpy.linalg.svd` : Compute the singular value decomposition of a matrix array.
|
|
265
250
|
|
|
266
251
|
Notes
|
|
@@ -361,6 +346,21 @@ class Hyperplane(Manifold):
|
|
|
361
346
|
|
|
362
347
|
return intersections
|
|
363
348
|
|
|
349
|
+
def negate_normal(self):
|
|
350
|
+
"""
|
|
351
|
+
Negate the direction of the normal.
|
|
352
|
+
|
|
353
|
+
Returns
|
|
354
|
+
-------
|
|
355
|
+
hyperplane : `Hyperplane`
|
|
356
|
+
The hyperplane with negated normal. The hyperplane retains the same tangent space.
|
|
357
|
+
|
|
358
|
+
See Also
|
|
359
|
+
--------
|
|
360
|
+
`Solid.complement` : Return the complement of the solid: whatever was inside is outside and vice-versa.
|
|
361
|
+
"""
|
|
362
|
+
return Hyperplane(-self._normal, self._point, self._tangentSpace)
|
|
363
|
+
|
|
364
364
|
def normal(self, domainPoint, normalize=True, indices=None):
|
|
365
365
|
"""
|
|
366
366
|
Return the normal.
|
bspy/manifold.py
CHANGED
|
@@ -15,8 +15,8 @@ class Manifold:
|
|
|
15
15
|
minSeparation = 0.0001
|
|
16
16
|
"""If two points are within minSeparation of each each other, they are coincident."""
|
|
17
17
|
|
|
18
|
-
Crossing = namedtuple('Crossing', ('
|
|
19
|
-
Coincidence = namedtuple('Coincidence', ('
|
|
18
|
+
Crossing = namedtuple('Crossing', ('firstPart','secondPart'))
|
|
19
|
+
Coincidence = namedtuple('Coincidence', ('firstPart', 'secondPart', 'alignment', 'transform', 'inverse', 'translation'))
|
|
20
20
|
"""Return type for intersect."""
|
|
21
21
|
|
|
22
22
|
factory = {}
|
|
@@ -43,14 +43,14 @@ class Manifold:
|
|
|
43
43
|
A list of intersections between the two manifolds.
|
|
44
44
|
Each intersection records either a crossing or a coincident region.
|
|
45
45
|
|
|
46
|
-
For a crossing, intersection is a Manifold.Crossing: (
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
For a crossing, intersection is a Manifold.Crossing: (firstPart, secondPart)
|
|
47
|
+
* firstPart : `Manifold` in the manifold's domain where the manifold and the other cross.
|
|
48
|
+
* secondPart : `Manifold` in the other's domain where the manifold and the other cross.
|
|
49
49
|
* Both intersection manifolds have the same domain and range (the crossing between the manifold and the other).
|
|
50
50
|
|
|
51
|
-
For a coincident region, intersection is Manifold.Coincidence: (
|
|
52
|
-
*
|
|
53
|
-
*
|
|
51
|
+
For a coincident region, intersection is Manifold.Coincidence: (firstPart, secondPart, alignment, transform, inverse, translation)
|
|
52
|
+
* firstPart : `Solid` in the manifold's domain within which the manifold and the other are coincident.
|
|
53
|
+
* secondPart : `Solid` in the other's domain within which the manifold and the other are coincident.
|
|
54
54
|
* alignment : scalar value holding the normal alignment between the manifold and the other (the dot product of their unit normals).
|
|
55
55
|
* transform : `numpy.array` holding the matrix transform from the boundary's domain to the other's domain.
|
|
56
56
|
* inverse : `numpy.array` holding the matrix inverse transform from the other's domain to the boundary's domain.
|
|
@@ -63,7 +63,7 @@ class Manifold:
|
|
|
63
63
|
See Also
|
|
64
64
|
--------
|
|
65
65
|
`intersect` : Intersect two manifolds.
|
|
66
|
-
`Solid.
|
|
66
|
+
`Solid.compute_cutout` : Compute the cutout portion of the manifold within the solid.
|
|
67
67
|
|
|
68
68
|
Notes
|
|
69
69
|
-----
|
|
@@ -97,31 +97,31 @@ class Manifold:
|
|
|
97
97
|
|
|
98
98
|
return intersections, isTwin
|
|
99
99
|
|
|
100
|
-
def
|
|
100
|
+
def complete_cutout(self, cutout, solid):
|
|
101
101
|
"""
|
|
102
|
-
Add any missing inherent (implicit) boundaries of this manifold's domain to the given
|
|
103
|
-
given solid that are needed to make the
|
|
102
|
+
Add any missing inherent (implicit) boundaries of this manifold's domain to the given cutout of the
|
|
103
|
+
given solid that are needed to make the cutout valid and complete.
|
|
104
104
|
|
|
105
105
|
Parameters
|
|
106
106
|
----------
|
|
107
|
-
|
|
108
|
-
The
|
|
107
|
+
cutout : `Solid`
|
|
108
|
+
The cutout of the given solid formed by the manifold. The cutout may be incomplete, missing some of the
|
|
109
109
|
manifold's inherent domain boundaries. Its dimension must match `self.domain_dimension()`.
|
|
110
110
|
|
|
111
111
|
solid : `Solid`
|
|
112
|
-
The solid
|
|
112
|
+
The solid determining the cutout of the manifold. Its dimension must match `self.range_dimension()`.
|
|
113
113
|
|
|
114
114
|
See Also
|
|
115
115
|
--------
|
|
116
|
-
`Solid.
|
|
116
|
+
`Solid.compute_cutout` : Compute the cutout portion of the manifold within the solid.
|
|
117
117
|
|
|
118
118
|
Notes
|
|
119
119
|
-----
|
|
120
120
|
For manifolds without inherent domain boundaries (like hyperplanes), the operation does nothing.
|
|
121
121
|
"""
|
|
122
|
-
assert self.domain_dimension() ==
|
|
122
|
+
assert self.domain_dimension() == cutout.dimension
|
|
123
123
|
assert self.range_dimension() == solid.dimension
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
def copy(self):
|
|
126
126
|
"""
|
|
127
127
|
Copy the manifold.
|
|
@@ -157,21 +157,6 @@ class Manifold:
|
|
|
157
157
|
"""
|
|
158
158
|
return None
|
|
159
159
|
|
|
160
|
-
def flip_normal(self):
|
|
161
|
-
"""
|
|
162
|
-
Flip the direction of the normal.
|
|
163
|
-
|
|
164
|
-
Returns
|
|
165
|
-
-------
|
|
166
|
-
manifold : `Manifold`
|
|
167
|
-
The manifold with flipped normal. The manifold retains the same tangent space.
|
|
168
|
-
|
|
169
|
-
See Also
|
|
170
|
-
--------
|
|
171
|
-
`Solid.complement` : Return the complement of the solid: whatever was inside is outside and vice-versa.
|
|
172
|
-
"""
|
|
173
|
-
return None
|
|
174
|
-
|
|
175
160
|
@staticmethod
|
|
176
161
|
def from_dict(dictionary):
|
|
177
162
|
"""
|
|
@@ -222,14 +207,14 @@ class Manifold:
|
|
|
222
207
|
A list of intersections between the two manifolds.
|
|
223
208
|
Each intersection records either a crossing or a coincident region.
|
|
224
209
|
|
|
225
|
-
For a crossing, intersection is a `Manifold.Crossing`: (
|
|
226
|
-
*
|
|
227
|
-
*
|
|
210
|
+
For a crossing, intersection is a `Manifold.Crossing`: (firstPart, secondPart)
|
|
211
|
+
* firstPart : `Manifold` in the manifold's domain where the manifold and the other cross.
|
|
212
|
+
* secondPart : `Manifold` in the other's domain where the manifold and the other cross.
|
|
228
213
|
* Both intersection manifolds have the same domain and range (the crossing between the manifold and the other).
|
|
229
214
|
|
|
230
|
-
For a coincident region, intersection is a `Manifold.Coincidence`: (
|
|
231
|
-
*
|
|
232
|
-
*
|
|
215
|
+
For a coincident region, intersection is a `Manifold.Coincidence`: (firstPart, secondPart, alignment, transform, inverse, translation)
|
|
216
|
+
* firstPart : `Solid` in the manifold's domain within which the manifold and the other are coincident.
|
|
217
|
+
* secondPart : `Solid` in the other's domain within which the manifold and the other are coincident.
|
|
233
218
|
* alignment : scalar value holding the normal alignment between the manifold and the other (the dot product of their unit normals).
|
|
234
219
|
* transform : `numpy.array` holding the transform matrix from the manifold's domain to the other's domain.
|
|
235
220
|
* inverse : `numpy.array` holding the inverse transform matrix from the other's domain to the boundary's domain.
|
|
@@ -239,7 +224,7 @@ class Manifold:
|
|
|
239
224
|
See Also
|
|
240
225
|
--------
|
|
241
226
|
`cached_intersect` : Intersect two manifolds, caching the result for twins (same intersection but swapping self and other).
|
|
242
|
-
`Solid.
|
|
227
|
+
`Solid.compute_cutout` : Compute the cutout portion of the manifold within the solid.
|
|
243
228
|
|
|
244
229
|
Notes
|
|
245
230
|
-----
|
|
@@ -247,6 +232,21 @@ class Manifold:
|
|
|
247
232
|
"""
|
|
248
233
|
return NotImplemented
|
|
249
234
|
|
|
235
|
+
def negate_normal(self):
|
|
236
|
+
"""
|
|
237
|
+
Negate the direction of the normal.
|
|
238
|
+
|
|
239
|
+
Returns
|
|
240
|
+
-------
|
|
241
|
+
manifold : `Manifold`
|
|
242
|
+
The manifold with negated normal. The manifold retains the same tangent space.
|
|
243
|
+
|
|
244
|
+
See Also
|
|
245
|
+
--------
|
|
246
|
+
`Solid.complement` : Return the complement of the solid: whatever was inside is outside and vice-versa.
|
|
247
|
+
"""
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
250
|
def normal(self, domainPoint, normalize=True, indices=None):
|
|
251
251
|
"""
|
|
252
252
|
Return the normal.
|