geomdl 5.4.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.
- geomdl/BSpline.py +1017 -0
- geomdl/CPGen.py +330 -0
- geomdl/NURBS.py +565 -0
- geomdl/VERSION.txt +1 -0
- geomdl/__init__.py +43 -0
- geomdl/_convert.py +46 -0
- geomdl/_exchange.py +558 -0
- geomdl/_linalg.py +42 -0
- geomdl/_operations.py +271 -0
- geomdl/_tessellate.py +410 -0
- geomdl/_utilities.py +69 -0
- geomdl/_voxelize.py +170 -0
- geomdl/abstract.py +3129 -0
- geomdl/compatibility.py +357 -0
- geomdl/construct.py +394 -0
- geomdl/control_points.py +406 -0
- geomdl/convert.py +70 -0
- geomdl/elements.py +625 -0
- geomdl/evaluators.py +753 -0
- geomdl/exceptions.py +41 -0
- geomdl/exchange.py +935 -0
- geomdl/exchange_vtk.py +142 -0
- geomdl/fitting.py +558 -0
- geomdl/freeform.py +37 -0
- geomdl/functools_lru_cache.py +202 -0
- geomdl/helpers.py +1182 -0
- geomdl/knotvector.py +134 -0
- geomdl/linalg.py +835 -0
- geomdl/multi.py +1306 -0
- geomdl/operations.py +1753 -0
- geomdl/ray.py +204 -0
- geomdl/shortcuts.py +51 -0
- geomdl/sweeping.py +49 -0
- geomdl/tessellate.py +190 -0
- geomdl/trimming.py +444 -0
- geomdl/utilities.py +235 -0
- geomdl/vis.py +183 -0
- geomdl/visualization/VisMPL.py +1018 -0
- geomdl/visualization/VisPlotly.py +601 -0
- geomdl/visualization/VisVTK.py +392 -0
- geomdl/visualization/__init__.py +13 -0
- geomdl/visualization/vtk_helpers.py +395 -0
- geomdl/voxelize.py +107 -0
- geomdl-5.4.0.dist-info/METADATA +111 -0
- geomdl-5.4.0.dist-info/RECORD +48 -0
- geomdl-5.4.0.dist-info/WHEEL +5 -0
- geomdl-5.4.0.dist-info/licenses/LICENSE +19 -0
- geomdl-5.4.0.dist-info/top_level.txt +1 -0
geomdl/BSpline.py
ADDED
|
@@ -0,0 +1,1017 @@
|
|
|
1
|
+
"""
|
|
2
|
+
.. module:: BSpline
|
|
3
|
+
:platform: Unix, Windows
|
|
4
|
+
:synopsis: Provides data storage and evaluation functionality for non-rational spline geometries
|
|
5
|
+
|
|
6
|
+
.. moduleauthor:: Onur R. Bingol <contact@onurbingol.net>
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import pickle
|
|
11
|
+
from . import abstract, evaluators, operations, tessellate, utilities
|
|
12
|
+
from . import _utilities as utl
|
|
13
|
+
from .exceptions import GeomdlException
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@utl.export
|
|
17
|
+
class Curve(abstract.Curve):
|
|
18
|
+
"""Data storage and evaluation class for n-variate B-spline (non-rational) curves.
|
|
19
|
+
|
|
20
|
+
This class provides the following properties:
|
|
21
|
+
|
|
22
|
+
* :py:attr:`type` = spline
|
|
23
|
+
* :py:attr:`id`
|
|
24
|
+
* :py:attr:`order`
|
|
25
|
+
* :py:attr:`degree`
|
|
26
|
+
* :py:attr:`knotvector`
|
|
27
|
+
* :py:attr:`ctrlpts`
|
|
28
|
+
* :py:attr:`delta`
|
|
29
|
+
* :py:attr:`sample_size`
|
|
30
|
+
* :py:attr:`bbox`
|
|
31
|
+
* :py:attr:`vis`
|
|
32
|
+
* :py:attr:`name`
|
|
33
|
+
* :py:attr:`dimension`
|
|
34
|
+
* :py:attr:`evaluator`
|
|
35
|
+
* :py:attr:`rational`
|
|
36
|
+
|
|
37
|
+
The following code segment illustrates the usage of Curve class:
|
|
38
|
+
|
|
39
|
+
.. code-block:: python
|
|
40
|
+
|
|
41
|
+
from geomdl import BSpline
|
|
42
|
+
|
|
43
|
+
# Create a 3-dimensional B-spline Curve
|
|
44
|
+
curve = BSpline.Curve()
|
|
45
|
+
|
|
46
|
+
# Set degree
|
|
47
|
+
curve.degree = 3
|
|
48
|
+
|
|
49
|
+
# Set control points
|
|
50
|
+
curve.ctrlpts = [[10, 5, 10], [10, 20, -30], [40, 10, 25], [-10, 5, 0]]
|
|
51
|
+
|
|
52
|
+
# Set knot vector
|
|
53
|
+
curve.knotvector = [0, 0, 0, 0, 1, 1, 1, 1]
|
|
54
|
+
|
|
55
|
+
# Set evaluation delta (controls the number of curve points)
|
|
56
|
+
curve.delta = 0.05
|
|
57
|
+
|
|
58
|
+
# Get curve points (the curve will be automatically evaluated)
|
|
59
|
+
curve_points = curve.evalpts
|
|
60
|
+
|
|
61
|
+
**Keyword Arguments:**
|
|
62
|
+
|
|
63
|
+
* ``precision``: number of decimal places to round to. *Default: 18*
|
|
64
|
+
* ``normalize_kv``: activates knot vector normalization. *Default: True*
|
|
65
|
+
* ``find_span_func``: sets knot span search implementation. *Default:* :func:`.helpers.find_span_linear`
|
|
66
|
+
* ``insert_knot_func``: sets knot insertion implementation. *Default:* :func:`.operations.insert_knot`
|
|
67
|
+
* ``remove_knot_func``: sets knot removal implementation. *Default:* :func:`.operations.remove_knot`
|
|
68
|
+
|
|
69
|
+
Please refer to the :py:class:`.abstract.Curve()` documentation for more details.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
# __slots__ = ('_insert_knot_func', '_remove_knot_func')
|
|
73
|
+
|
|
74
|
+
def __init__(self, **kwargs):
|
|
75
|
+
super(Curve, self).__init__(**kwargs)
|
|
76
|
+
self._evaluator = evaluators.CurveEvaluator(find_span_func=self._span_func)
|
|
77
|
+
self._insert_knot_func = kwargs.get("insert_knot_func", operations.insert_knot)
|
|
78
|
+
self._remove_knot_func = kwargs.get("remove_knot_func", operations.remove_knot)
|
|
79
|
+
|
|
80
|
+
def save(self, file_name):
|
|
81
|
+
"""Saves the curve as a pickled file.
|
|
82
|
+
|
|
83
|
+
.. deprecated:: 5.2.4
|
|
84
|
+
|
|
85
|
+
Use :func:`.exchange.export_json()` instead.
|
|
86
|
+
|
|
87
|
+
:param file_name: name of the file to be saved
|
|
88
|
+
:type file_name: str
|
|
89
|
+
"""
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
def load(self, file_name):
|
|
93
|
+
"""Loads the curve from a pickled file.
|
|
94
|
+
|
|
95
|
+
.. deprecated:: 5.2.4
|
|
96
|
+
|
|
97
|
+
Use :func:`.exchange.import_json()` instead.
|
|
98
|
+
|
|
99
|
+
:param file_name: name of the file to be loaded
|
|
100
|
+
:type file_name: str
|
|
101
|
+
"""
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
def evaluate(self, **kwargs):
|
|
105
|
+
"""Evaluates the curve.
|
|
106
|
+
|
|
107
|
+
The evaluated points are stored in :py:attr:`evalpts` property.
|
|
108
|
+
|
|
109
|
+
Keyword arguments:
|
|
110
|
+
* ``start``: start parameter
|
|
111
|
+
* ``stop``: stop parameter
|
|
112
|
+
|
|
113
|
+
The ``start`` and ``stop`` parameters allow evaluation of a curve segment in the range *[start, stop]*, i.e.
|
|
114
|
+
the curve will also be evaluated at the ``stop`` parameter value.
|
|
115
|
+
|
|
116
|
+
The following examples illustrate the usage of the keyword arguments.
|
|
117
|
+
|
|
118
|
+
.. code-block:: python
|
|
119
|
+
|
|
120
|
+
# Start evaluating from u=0.2 to u=1.0
|
|
121
|
+
curve.evaluate(start=0.2)
|
|
122
|
+
|
|
123
|
+
# Start evaluating from u=0.0 to u=0.7
|
|
124
|
+
curve.evaluate(stop=0.7)
|
|
125
|
+
|
|
126
|
+
# Start evaluating from u=0.1 to u=0.5
|
|
127
|
+
curve.evaluate(start=0.1, stop=0.5)
|
|
128
|
+
|
|
129
|
+
# Get the evaluated points
|
|
130
|
+
curve_points = curve.evalpts
|
|
131
|
+
"""
|
|
132
|
+
# Call parent method
|
|
133
|
+
super(Curve, self).evaluate(**kwargs)
|
|
134
|
+
|
|
135
|
+
# Find evaluation start and stop parameter values
|
|
136
|
+
start = kwargs.get("start", self.knotvector[self.degree])
|
|
137
|
+
stop = kwargs.get("stop", self.knotvector[-(self.degree + 1)])
|
|
138
|
+
|
|
139
|
+
# Check parameters
|
|
140
|
+
if self._kv_normalize:
|
|
141
|
+
if not utilities.check_params([start, stop]):
|
|
142
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
143
|
+
|
|
144
|
+
# Clean up the curve points
|
|
145
|
+
self.reset(evalpts=True)
|
|
146
|
+
|
|
147
|
+
# Evaluate and cache
|
|
148
|
+
self._eval_points = self._evaluator.evaluate(self.data, start=start, stop=stop)
|
|
149
|
+
|
|
150
|
+
def evaluate_single(self, param):
|
|
151
|
+
"""Evaluates the curve at the input parameter.
|
|
152
|
+
|
|
153
|
+
:param param: parameter
|
|
154
|
+
:type param: float
|
|
155
|
+
:return: evaluated surface point at the given parameter
|
|
156
|
+
:rtype: list
|
|
157
|
+
"""
|
|
158
|
+
# Call parent method
|
|
159
|
+
super(Curve, self).evaluate_single(param)
|
|
160
|
+
|
|
161
|
+
# Check parameters
|
|
162
|
+
if self._kv_normalize:
|
|
163
|
+
if not utilities.check_params([param]):
|
|
164
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
165
|
+
|
|
166
|
+
# Evaluate the curve point
|
|
167
|
+
pt = self._evaluator.evaluate(self.data, start=param, stop=param)
|
|
168
|
+
|
|
169
|
+
return pt[0]
|
|
170
|
+
|
|
171
|
+
def evaluate_list(self, param_list):
|
|
172
|
+
"""Evaluates the curve for an input range of parameters.
|
|
173
|
+
|
|
174
|
+
:param param_list: list of parameters
|
|
175
|
+
:type param_list: list, tuple
|
|
176
|
+
:return: evaluated surface points at the input parameters
|
|
177
|
+
:rtype: list
|
|
178
|
+
"""
|
|
179
|
+
# Call parent method
|
|
180
|
+
super(Curve, self).evaluate_list(param_list)
|
|
181
|
+
|
|
182
|
+
# Evaluate parameter list
|
|
183
|
+
res = []
|
|
184
|
+
for prm in param_list:
|
|
185
|
+
if self._kv_normalize:
|
|
186
|
+
if utilities.check_params([prm]):
|
|
187
|
+
res.append(self.evaluate_single(prm))
|
|
188
|
+
else:
|
|
189
|
+
res.append(self.evaluate_single(prm))
|
|
190
|
+
return res
|
|
191
|
+
|
|
192
|
+
def derivatives(self, u, order=0, **kwargs):
|
|
193
|
+
"""Evaluates n-th order curve derivatives at the given parameter value.
|
|
194
|
+
|
|
195
|
+
The output of this method is list of n-th order derivatives. If ``order`` is ``0``, then it will only output
|
|
196
|
+
the evaluated point. Similarly, if ``order`` is ``2``, then it will output the evaluated point, 1st derivative
|
|
197
|
+
and the 2nd derivative. For instance;
|
|
198
|
+
|
|
199
|
+
.. code-block:: python
|
|
200
|
+
|
|
201
|
+
# Assuming a curve (crv) is defined on a parametric domain [0.0, 1.0]
|
|
202
|
+
# Let's take the curve derivative at the parametric position u = 0.35
|
|
203
|
+
ders = crv.derivatives(u=0.35, order=2)
|
|
204
|
+
ders[0] # evaluated point, equal to crv.evaluate_single(0.35)
|
|
205
|
+
ders[1] # 1st derivative at u = 0.35
|
|
206
|
+
ders[2] @ 2nd derivative at u = 0.35
|
|
207
|
+
|
|
208
|
+
:param u: parameter value
|
|
209
|
+
:type u: float
|
|
210
|
+
:param order: derivative order
|
|
211
|
+
:type order: int
|
|
212
|
+
:return: a list containing up to {order}-th derivative of the curve
|
|
213
|
+
:rtype: list
|
|
214
|
+
"""
|
|
215
|
+
# Call parent method
|
|
216
|
+
super(Curve, self).derivatives(u=u, order=order, **kwargs)
|
|
217
|
+
|
|
218
|
+
# Evaluate and return the derivative at knot u
|
|
219
|
+
return self._evaluator.derivatives(self.data, parpos=u, deriv_order=order)
|
|
220
|
+
|
|
221
|
+
def insert_knot(self, param, **kwargs):
|
|
222
|
+
"""Inserts the knot and updates the control points array and the knot vector.
|
|
223
|
+
|
|
224
|
+
Keyword Arguments:
|
|
225
|
+
* ``num``: Number of knot insertions. *Default: 1*
|
|
226
|
+
|
|
227
|
+
:param param: knot to be inserted
|
|
228
|
+
:type param: float
|
|
229
|
+
"""
|
|
230
|
+
# Check if all required parameters are set before the evaluation
|
|
231
|
+
self._check_variables()
|
|
232
|
+
|
|
233
|
+
# Check parameters are correct
|
|
234
|
+
if self._kv_normalize:
|
|
235
|
+
if not utilities.check_params([param]):
|
|
236
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
237
|
+
|
|
238
|
+
# Get keyword arguments
|
|
239
|
+
num = kwargs.get("num", 1) # number of knot insertions
|
|
240
|
+
check_num = kwargs.get("check_r", True) # can be set to False when the caller checks number of insertions
|
|
241
|
+
|
|
242
|
+
# Insert knot
|
|
243
|
+
try:
|
|
244
|
+
self._insert_knot_func(self, [param], [num], check_num=check_num)
|
|
245
|
+
except GeomdlException as e:
|
|
246
|
+
print(e)
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
# Evaluate curve again if it has already been evaluated before knot insertion
|
|
250
|
+
if check_num and self._eval_points:
|
|
251
|
+
self.evaluate()
|
|
252
|
+
|
|
253
|
+
def remove_knot(self, param, **kwargs):
|
|
254
|
+
"""Removes the knot and updates the control points array and the knot vector.
|
|
255
|
+
|
|
256
|
+
Keyword Arguments:
|
|
257
|
+
* ``num``: Number of knot removals. *Default: 1*
|
|
258
|
+
|
|
259
|
+
:param param: knot to be removed
|
|
260
|
+
:type param: float
|
|
261
|
+
"""
|
|
262
|
+
# Check if all required parameters are set before the evaluation
|
|
263
|
+
self._check_variables()
|
|
264
|
+
|
|
265
|
+
# Check param parameters are correct
|
|
266
|
+
if self._kv_normalize:
|
|
267
|
+
if not utilities.check_params([param]):
|
|
268
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
269
|
+
|
|
270
|
+
# Get keyword arguments
|
|
271
|
+
num = kwargs.get("num", 1) # number of knot removals
|
|
272
|
+
check_num = kwargs.get("check_r", True) # can be set to False when the caller checks number of removals
|
|
273
|
+
|
|
274
|
+
# Remove knot
|
|
275
|
+
try:
|
|
276
|
+
self._remove_knot_func(self, [param], [num], check_num=check_num)
|
|
277
|
+
except GeomdlException as e:
|
|
278
|
+
print(e)
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
# Evaluate curve again if it has already been evaluated before knot removal
|
|
282
|
+
if check_num and self._eval_points:
|
|
283
|
+
self.evaluate()
|
|
284
|
+
|
|
285
|
+
def tangent(self, parpos, **kwargs):
|
|
286
|
+
"""Evaluates the tangent vector of the curve at the given parametric position(s).
|
|
287
|
+
|
|
288
|
+
.. deprecated: 5.3.0
|
|
289
|
+
|
|
290
|
+
Please use :func:`operations.tangent` instead.
|
|
291
|
+
|
|
292
|
+
:param parpos: parametric position(s) where the evaluation will be executed
|
|
293
|
+
:type parpos: float, list or tuple
|
|
294
|
+
:return: tangent vector as a tuple of the origin point and the vector components
|
|
295
|
+
:rtype: tuple
|
|
296
|
+
"""
|
|
297
|
+
return tuple()
|
|
298
|
+
|
|
299
|
+
def normal(self, parpos, **kwargs):
|
|
300
|
+
"""Evaluates the normal to the tangent vector of the curve at the given parametric position(s).
|
|
301
|
+
|
|
302
|
+
.. deprecated: 5.3.0
|
|
303
|
+
|
|
304
|
+
:param parpos: parametric position(s) where the evaluation will be executed
|
|
305
|
+
:type parpos: float, list or tuple
|
|
306
|
+
:return: normal vector as a tuple of the origin point and the vector components
|
|
307
|
+
:rtype: tuple
|
|
308
|
+
"""
|
|
309
|
+
return tuple()
|
|
310
|
+
|
|
311
|
+
def binormal(self, parpos, **kwargs):
|
|
312
|
+
"""Evaluates the binormal vector of the curve at the given parametric position(s).
|
|
313
|
+
|
|
314
|
+
.. deprecated: 5.3.0
|
|
315
|
+
|
|
316
|
+
:param parpos: parametric position(s) where the evaluation will be executed
|
|
317
|
+
:type parpos: float, list or tuple
|
|
318
|
+
:return: binormal vector as a tuple of the origin point and the vector components
|
|
319
|
+
:rtype: tuple
|
|
320
|
+
"""
|
|
321
|
+
return tuple()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
@utl.export
|
|
325
|
+
class Surface(abstract.Surface):
|
|
326
|
+
"""Data storage and evaluation class for B-spline (non-rational) surfaces.
|
|
327
|
+
|
|
328
|
+
This class provides the following properties:
|
|
329
|
+
|
|
330
|
+
* :py:attr:`type` = spline
|
|
331
|
+
* :py:attr:`id`
|
|
332
|
+
* :py:attr:`order_u`
|
|
333
|
+
* :py:attr:`order_v`
|
|
334
|
+
* :py:attr:`degree_u`
|
|
335
|
+
* :py:attr:`degree_v`
|
|
336
|
+
* :py:attr:`knotvector_u`
|
|
337
|
+
* :py:attr:`knotvector_v`
|
|
338
|
+
* :py:attr:`ctrlpts`
|
|
339
|
+
* :py:attr:`ctrlpts_size_u`
|
|
340
|
+
* :py:attr:`ctrlpts_size_v`
|
|
341
|
+
* :py:attr:`ctrlpts2d`
|
|
342
|
+
* :py:attr:`delta`
|
|
343
|
+
* :py:attr:`delta_u`
|
|
344
|
+
* :py:attr:`delta_v`
|
|
345
|
+
* :py:attr:`sample_size`
|
|
346
|
+
* :py:attr:`sample_size_u`
|
|
347
|
+
* :py:attr:`sample_size_v`
|
|
348
|
+
* :py:attr:`bbox`
|
|
349
|
+
* :py:attr:`name`
|
|
350
|
+
* :py:attr:`dimension`
|
|
351
|
+
* :py:attr:`vis`
|
|
352
|
+
* :py:attr:`evaluator`
|
|
353
|
+
* :py:attr:`tessellator`
|
|
354
|
+
* :py:attr:`rational`
|
|
355
|
+
* :py:attr:`trims`
|
|
356
|
+
|
|
357
|
+
The following code segment illustrates the usage of Surface class:
|
|
358
|
+
|
|
359
|
+
.. code-block:: python
|
|
360
|
+
:linenos:
|
|
361
|
+
|
|
362
|
+
from geomdl import BSpline
|
|
363
|
+
|
|
364
|
+
# Create a BSpline surface instance (Bezier surface)
|
|
365
|
+
surf = BSpline.Surface()
|
|
366
|
+
|
|
367
|
+
# Set degrees
|
|
368
|
+
surf.degree_u = 3
|
|
369
|
+
surf.degree_v = 2
|
|
370
|
+
|
|
371
|
+
# Set control points
|
|
372
|
+
control_points = [[0, 0, 0], [0, 4, 0], [0, 8, -3],
|
|
373
|
+
[2, 0, 6], [2, 4, 0], [2, 8, 0],
|
|
374
|
+
[4, 0, 0], [4, 4, 0], [4, 8, 3],
|
|
375
|
+
[6, 0, 0], [6, 4, -3], [6, 8, 0]]
|
|
376
|
+
surf.set_ctrlpts(control_points, 4, 3)
|
|
377
|
+
|
|
378
|
+
# Set knot vectors
|
|
379
|
+
surf.knotvector_u = [0, 0, 0, 0, 1, 1, 1, 1]
|
|
380
|
+
surf.knotvector_v = [0, 0, 0, 1, 1, 1]
|
|
381
|
+
|
|
382
|
+
# Set evaluation delta (control the number of surface points)
|
|
383
|
+
surf.delta = 0.05
|
|
384
|
+
|
|
385
|
+
# Get surface points (the surface will be automatically evaluated)
|
|
386
|
+
surface_points = surf.evalpts
|
|
387
|
+
|
|
388
|
+
**Keyword Arguments:**
|
|
389
|
+
|
|
390
|
+
* ``precision``: number of decimal places to round to. *Default: 18*
|
|
391
|
+
* ``normalize_kv``: activates knot vector normalization. *Default: True*
|
|
392
|
+
* ``find_span_func``: sets knot span search implementation. *Default:* :func:`.helpers.find_span_linear`
|
|
393
|
+
* ``insert_knot_func``: sets knot insertion implementation. *Default:* :func:`.operations.insert_knot`
|
|
394
|
+
* ``remove_knot_func``: sets knot removal implementation. *Default:* :func:`.operations.remove_knot`
|
|
395
|
+
|
|
396
|
+
Please refer to the :py:class:`.abstract.Surface()` documentation for more details.
|
|
397
|
+
"""
|
|
398
|
+
|
|
399
|
+
# __slots__ = ('_insert_knot_func', '_remove_knot_func', '_control_points2D')
|
|
400
|
+
|
|
401
|
+
def __init__(self, **kwargs):
|
|
402
|
+
super(Surface, self).__init__(**kwargs)
|
|
403
|
+
self._evaluator = evaluators.SurfaceEvaluator(find_span_func=self._span_func)
|
|
404
|
+
self._tsl_component = tessellate.TriangularTessellate()
|
|
405
|
+
self._control_points2D = self._init_array() # control points, 2-D array [u][v]
|
|
406
|
+
self._insert_knot_func = kwargs.get("insert_knot_func", operations.insert_knot)
|
|
407
|
+
self._remove_knot_func = kwargs.get("remove_knot_func", operations.remove_knot)
|
|
408
|
+
|
|
409
|
+
@property
|
|
410
|
+
def ctrlpts2d(self):
|
|
411
|
+
"""2-dimensional array of control points.
|
|
412
|
+
|
|
413
|
+
The getter returns a tuple of 2D control points (weighted control points + weights if NURBS) in *[u][v]* format.
|
|
414
|
+
The rows of the returned tuple correspond to v-direction and the columns correspond to u-direction.
|
|
415
|
+
|
|
416
|
+
The following example can be used to traverse 2D control points:
|
|
417
|
+
|
|
418
|
+
.. code-block:: python
|
|
419
|
+
:linenos:
|
|
420
|
+
|
|
421
|
+
# Create a BSpline surface
|
|
422
|
+
surf_bs = BSpline.Surface()
|
|
423
|
+
|
|
424
|
+
# Do degree, control points and knot vector assignments here
|
|
425
|
+
|
|
426
|
+
# Each u includes a row of v values
|
|
427
|
+
for u in surf_bs.ctrlpts2d:
|
|
428
|
+
# Each row contains the coordinates of the control points
|
|
429
|
+
for v in u:
|
|
430
|
+
print(str(v)) # will be something like (1.0, 2.0, 3.0)
|
|
431
|
+
|
|
432
|
+
# Create a NURBS surface
|
|
433
|
+
surf_nb = NURBS.Surface()
|
|
434
|
+
|
|
435
|
+
# Do degree, weighted control points and knot vector assignments here
|
|
436
|
+
|
|
437
|
+
# Each u includes a row of v values
|
|
438
|
+
for u in surf_nb.ctrlpts2d:
|
|
439
|
+
# Each row contains the coordinates of the weighted control points
|
|
440
|
+
for v in u:
|
|
441
|
+
print(str(v)) # will be something like (0.5, 1.0, 1.5, 0.5)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
When using **NURBS.Surface** class, the output of :py:attr:`~ctrlpts2d` property could be confusing since,
|
|
445
|
+
:py:attr:`~ctrlpts` always returns the unweighted control points, i.e. :py:attr:`~ctrlpts` property returns 3D
|
|
446
|
+
control points all divided by the weights and you can use :py:attr:`~weights` property to access the weights
|
|
447
|
+
vector, but :py:attr:`~ctrlpts2d` returns the weighted ones plus weights as the last element.
|
|
448
|
+
This difference is intentionally added for compatibility and interoperability purposes.
|
|
449
|
+
|
|
450
|
+
To explain this situation in a simple way;
|
|
451
|
+
|
|
452
|
+
* If you need the weighted control points directly, use :py:attr:`~ctrlpts2d`
|
|
453
|
+
* If you need the control points and the weights separately, use :py:attr:`~ctrlpts` and :py:attr:`~weights`
|
|
454
|
+
|
|
455
|
+
.. note::
|
|
456
|
+
|
|
457
|
+
Please note that the setter doesn't check for inconsistencies and using the setter is not recommended.
|
|
458
|
+
Instead of the setter property, please use :func:`.set_ctrlpts()` function.
|
|
459
|
+
|
|
460
|
+
Please refer to the `wiki <https://github.com/orbingol/NURBS-Python/wiki/Using-Python-Properties>`_ for details
|
|
461
|
+
on using this class member.
|
|
462
|
+
|
|
463
|
+
:getter: Gets the control points as a 2-dimensional array in [u][v] format
|
|
464
|
+
:setter: Sets the control points as a 2-dimensional array in [u][v] format
|
|
465
|
+
:type: list
|
|
466
|
+
"""
|
|
467
|
+
return self._control_points2D
|
|
468
|
+
|
|
469
|
+
@ctrlpts2d.setter
|
|
470
|
+
def ctrlpts2d(self, value):
|
|
471
|
+
if not isinstance(value, (list, tuple)):
|
|
472
|
+
raise ValueError("The input must be a list or tuple")
|
|
473
|
+
|
|
474
|
+
# Clean up the surface and control points
|
|
475
|
+
self.reset(evalpts=True, ctrlpts=True)
|
|
476
|
+
|
|
477
|
+
# Assume that the user has prepared the lists correctly
|
|
478
|
+
size_u = len(value)
|
|
479
|
+
size_v = len(value[0])
|
|
480
|
+
|
|
481
|
+
# Estimate dimension by checking the size of the first element
|
|
482
|
+
self._dimension = len(value[0][0])
|
|
483
|
+
|
|
484
|
+
# Make sure that all numbers are float type
|
|
485
|
+
ctrlpts = [[] for _ in range(size_u * size_v)]
|
|
486
|
+
for u in range(size_u):
|
|
487
|
+
for v in range(size_v):
|
|
488
|
+
idx = v + (size_v * u)
|
|
489
|
+
ctrlpts[idx] = [float(coord) for coord in value[u][v]]
|
|
490
|
+
|
|
491
|
+
# Set control points
|
|
492
|
+
self.set_ctrlpts(ctrlpts, size_u, size_v)
|
|
493
|
+
|
|
494
|
+
def set_ctrlpts(self, ctrlpts, *args, **kwargs):
|
|
495
|
+
"""Sets the control points and checks if the data is consistent.
|
|
496
|
+
|
|
497
|
+
This method is designed to provide a consistent way to set control points whether they are weighted or not.
|
|
498
|
+
It directly sets the control points member of the class, and therefore it doesn't return any values.
|
|
499
|
+
The input will be an array of coordinates. If you are working in the 3-dimensional space, then your coordinates
|
|
500
|
+
will be an array of 3 elements representing *(x, y, z)* coordinates.
|
|
501
|
+
|
|
502
|
+
This method also generates 2D control points in *[u][v]* format which can be accessed via :py:attr:`~ctrlpts2d`.
|
|
503
|
+
|
|
504
|
+
.. note::
|
|
505
|
+
|
|
506
|
+
The v index varies first. That is, a row of v control points for the first u value is found first.
|
|
507
|
+
Then, the row of v control points for the next u value.
|
|
508
|
+
|
|
509
|
+
:param ctrlpts: input control points as a list of coordinates
|
|
510
|
+
:type ctrlpts: list
|
|
511
|
+
"""
|
|
512
|
+
# Call parent function
|
|
513
|
+
super(Surface, self).set_ctrlpts(ctrlpts, *args, **kwargs)
|
|
514
|
+
|
|
515
|
+
# Generate a 2-dimensional list of control points
|
|
516
|
+
array_init2d = kwargs.get("array_init2d", [[[] for _ in range(args[1])] for _ in range(args[0])])
|
|
517
|
+
ctrlpts_float2d = array_init2d
|
|
518
|
+
for i in range(0, self.ctrlpts_size_u):
|
|
519
|
+
for j in range(0, self.ctrlpts_size_v):
|
|
520
|
+
ctrlpts_float2d[i][j] = self._control_points[j + (i * self.ctrlpts_size_v)]
|
|
521
|
+
|
|
522
|
+
# Set the new 2-dimension control points
|
|
523
|
+
self._control_points2D = ctrlpts_float2d
|
|
524
|
+
|
|
525
|
+
def reset(self, **kwargs):
|
|
526
|
+
"""Resets control points and/or evaluated points.
|
|
527
|
+
|
|
528
|
+
Keyword Arguments:
|
|
529
|
+
* ``evalpts``: if True, then resets evaluated points
|
|
530
|
+
* ``ctrlpts`` if True, then resets control points
|
|
531
|
+
|
|
532
|
+
"""
|
|
533
|
+
# Call parent function
|
|
534
|
+
super(Surface, self).reset(**kwargs)
|
|
535
|
+
|
|
536
|
+
# Reset ctrlpts2d
|
|
537
|
+
reset_ctrlpts = kwargs.get("ctrlpts", False)
|
|
538
|
+
if reset_ctrlpts:
|
|
539
|
+
self._control_points2D = self._init_array()
|
|
540
|
+
|
|
541
|
+
def save(self, file_name):
|
|
542
|
+
"""Saves the surface as a pickled file.
|
|
543
|
+
|
|
544
|
+
.. deprecated:: 5.2.4
|
|
545
|
+
|
|
546
|
+
Use :func:`.exchange.export_json()` instead.
|
|
547
|
+
|
|
548
|
+
:param file_name: name of the file to be saved
|
|
549
|
+
:type file_name: str
|
|
550
|
+
"""
|
|
551
|
+
return None
|
|
552
|
+
|
|
553
|
+
def load(self, file_name):
|
|
554
|
+
"""Loads the surface from a pickled file.
|
|
555
|
+
|
|
556
|
+
.. deprecated:: 5.2.4
|
|
557
|
+
|
|
558
|
+
Use :func:`.exchange.import_json()` instead.
|
|
559
|
+
|
|
560
|
+
:param file_name: name of the file to be loaded
|
|
561
|
+
:type file_name: str
|
|
562
|
+
"""
|
|
563
|
+
return None
|
|
564
|
+
|
|
565
|
+
def transpose(self):
|
|
566
|
+
"""Transposes the surface by swapping u and v parametric directions."""
|
|
567
|
+
operations.transpose(self, inplace=True)
|
|
568
|
+
self.reset(evalpts=True)
|
|
569
|
+
|
|
570
|
+
def evaluate(self, **kwargs):
|
|
571
|
+
"""Evaluates the surface.
|
|
572
|
+
|
|
573
|
+
The evaluated points are stored in :py:attr:`evalpts` property.
|
|
574
|
+
|
|
575
|
+
Keyword arguments:
|
|
576
|
+
* ``start_u``: start parameter on the u-direction
|
|
577
|
+
* ``stop_u``: stop parameter on the u-direction
|
|
578
|
+
* ``start_v``: start parameter on the v-direction
|
|
579
|
+
* ``stop_v``: stop parameter on the v-direction
|
|
580
|
+
|
|
581
|
+
The ``start_u``, ``start_v`` and ``stop_u`` and ``stop_v`` parameters allow evaluation of a surface segment
|
|
582
|
+
in the range *[start_u, stop_u][start_v, stop_v]* i.e. the surface will also be evaluated at the ``stop_u``
|
|
583
|
+
and ``stop_v`` parameter values.
|
|
584
|
+
|
|
585
|
+
The following examples illustrate the usage of the keyword arguments.
|
|
586
|
+
|
|
587
|
+
.. code-block:: python
|
|
588
|
+
:linenos:
|
|
589
|
+
|
|
590
|
+
# Start evaluating in range u=[0, 0.7] and v=[0.1, 1]
|
|
591
|
+
surf.evaluate(stop_u=0.7, start_v=0.1)
|
|
592
|
+
|
|
593
|
+
# Start evaluating in range u=[0, 1] and v=[0.1, 0.3]
|
|
594
|
+
surf.evaluate(start_v=0.1, stop_v=0.3)
|
|
595
|
+
|
|
596
|
+
# Get the evaluated points
|
|
597
|
+
surface_points = surf.evalpts
|
|
598
|
+
|
|
599
|
+
"""
|
|
600
|
+
# Call parent method
|
|
601
|
+
super(Surface, self).evaluate(**kwargs)
|
|
602
|
+
|
|
603
|
+
# Find evaluation start and stop parameter values
|
|
604
|
+
start_u = kwargs.get("start_u", self.knotvector_u[self.degree_u])
|
|
605
|
+
stop_u = kwargs.get("stop_u", self.knotvector_u[-(self.degree_u + 1)])
|
|
606
|
+
start_v = kwargs.get("start_v", self.knotvector_v[self.degree_v])
|
|
607
|
+
stop_v = kwargs.get("stop_v", self.knotvector_v[-(self.degree_v + 1)])
|
|
608
|
+
|
|
609
|
+
# Check parameters
|
|
610
|
+
if self._kv_normalize:
|
|
611
|
+
if not utilities.check_params([start_u, stop_u, start_v, stop_v]):
|
|
612
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
613
|
+
|
|
614
|
+
# Clean up the surface points
|
|
615
|
+
self.reset(evalpts=True)
|
|
616
|
+
|
|
617
|
+
# Evaluate and cache
|
|
618
|
+
self._eval_points = self._evaluator.evaluate(self.data, start=(start_u, start_v), stop=(stop_u, stop_v))
|
|
619
|
+
|
|
620
|
+
def evaluate_single(self, param):
|
|
621
|
+
"""Evaluates the surface at the input (u, v) parameter pair.
|
|
622
|
+
|
|
623
|
+
:param param: parameter pair (u, v)
|
|
624
|
+
:type param: list, tuple
|
|
625
|
+
:return: evaluated surface point at the given parameter pair
|
|
626
|
+
:rtype: list
|
|
627
|
+
"""
|
|
628
|
+
# Call parent method
|
|
629
|
+
super(Surface, self).evaluate_single(param)
|
|
630
|
+
|
|
631
|
+
# Evaluate the surface point
|
|
632
|
+
pt = self._evaluator.evaluate(self.data, start=param, stop=param)
|
|
633
|
+
|
|
634
|
+
return pt[0]
|
|
635
|
+
|
|
636
|
+
def evaluate_list(self, param_list):
|
|
637
|
+
"""Evaluates the surface for a given list of (u, v) parameters.
|
|
638
|
+
|
|
639
|
+
:param param_list: list of parameter pairs (u, v)
|
|
640
|
+
:type param_list: list, tuple
|
|
641
|
+
:return: evaluated surface point at the input parameter pairs
|
|
642
|
+
:rtype: tuple
|
|
643
|
+
"""
|
|
644
|
+
# Call parent method
|
|
645
|
+
super(Surface, self).evaluate_list(param_list)
|
|
646
|
+
|
|
647
|
+
# Evaluate (u,v) list
|
|
648
|
+
res = []
|
|
649
|
+
for prm in param_list:
|
|
650
|
+
if self._kv_normalize:
|
|
651
|
+
if utilities.check_params(prm):
|
|
652
|
+
res.append(self.evaluate_single(prm))
|
|
653
|
+
else:
|
|
654
|
+
res.append(self.evaluate_single(prm))
|
|
655
|
+
return res
|
|
656
|
+
|
|
657
|
+
# Evaluates n-th order surface derivatives at the given (u,v) parameter
|
|
658
|
+
def derivatives(self, u, v, order=0, **kwargs):
|
|
659
|
+
"""Evaluates n-th order surface derivatives at the given (u, v) parameter pair.
|
|
660
|
+
|
|
661
|
+
* SKL[0][0] will be the surface point itself
|
|
662
|
+
* SKL[0][1] will be the 1st derivative w.r.t. v
|
|
663
|
+
* SKL[2][1] will be the 2nd derivative w.r.t. u and 1st derivative w.r.t. v
|
|
664
|
+
|
|
665
|
+
:param u: parameter on the u-direction
|
|
666
|
+
:type u: float
|
|
667
|
+
:param v: parameter on the v-direction
|
|
668
|
+
:type v: float
|
|
669
|
+
:param order: derivative order
|
|
670
|
+
:type order: integer
|
|
671
|
+
:return: A list SKL, where SKL[k][l] is the derivative of the surface S(u,v) w.r.t. u k times and v l times
|
|
672
|
+
:rtype: list
|
|
673
|
+
"""
|
|
674
|
+
# Call parent method
|
|
675
|
+
super(Surface, self).derivatives(u=u, v=v, order=order, **kwargs)
|
|
676
|
+
|
|
677
|
+
# Evaluate and return the derivatives
|
|
678
|
+
return self._evaluator.derivatives(self.data, parpos=(u, v), deriv_order=order)
|
|
679
|
+
|
|
680
|
+
def insert_knot(self, u=None, v=None, **kwargs):
|
|
681
|
+
"""Inserts knot(s) on the u- or v-directions
|
|
682
|
+
|
|
683
|
+
Keyword Arguments:
|
|
684
|
+
* ``num_u``: Number of knot insertions on the u-direction. *Default: 1*
|
|
685
|
+
* ``num_v``: Number of knot insertions on the v-direction. *Default: 1*
|
|
686
|
+
|
|
687
|
+
:param u: knot to be inserted on the u-direction
|
|
688
|
+
:type u: float
|
|
689
|
+
:param v: knot to be inserted on the v-direction
|
|
690
|
+
:type v: float
|
|
691
|
+
"""
|
|
692
|
+
# Check all parameters are set before the evaluation
|
|
693
|
+
self._check_variables()
|
|
694
|
+
|
|
695
|
+
# Check if the parameter values are correctly defined
|
|
696
|
+
if self._kv_normalize:
|
|
697
|
+
if not utilities.check_params([u, v]):
|
|
698
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
699
|
+
|
|
700
|
+
# Get keyword arguments
|
|
701
|
+
num_u = kwargs.get("num_u", 1) # number of knot insertions on the u-direction
|
|
702
|
+
num_v = kwargs.get("num_v", 1) # number of knot insertions on the v-direction
|
|
703
|
+
check_num = kwargs.get("check_r", True) # Enables/disables number of knot insertions checking
|
|
704
|
+
|
|
705
|
+
# Insert knots
|
|
706
|
+
try:
|
|
707
|
+
self._insert_knot_func(self, [u, v], [num_u, num_v], check_num=check_num)
|
|
708
|
+
except GeomdlException as e:
|
|
709
|
+
print(e)
|
|
710
|
+
return
|
|
711
|
+
|
|
712
|
+
# Evaluate surface again if it has already been evaluated before knot insertion
|
|
713
|
+
if check_num and self._eval_points:
|
|
714
|
+
self.evaluate()
|
|
715
|
+
|
|
716
|
+
def remove_knot(self, u=None, v=None, **kwargs):
|
|
717
|
+
"""Inserts knot(s) on the u- or v-directions
|
|
718
|
+
|
|
719
|
+
Keyword Arguments:
|
|
720
|
+
* ``num_u``: Number of knot removals on the u-direction. *Default: 1*
|
|
721
|
+
* ``num_v``: Number of knot removals on the v-direction. *Default: 1*
|
|
722
|
+
|
|
723
|
+
:param u: knot to be removed on the u-direction
|
|
724
|
+
:type u: float
|
|
725
|
+
:param v: knot to be removed on the v-direction
|
|
726
|
+
:type v: float
|
|
727
|
+
"""
|
|
728
|
+
# Check all parameters are set before the evaluation
|
|
729
|
+
self._check_variables()
|
|
730
|
+
|
|
731
|
+
# Check if the parameter values are correctly defined
|
|
732
|
+
if self._kv_normalize:
|
|
733
|
+
if not utilities.check_params([u, v]):
|
|
734
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
735
|
+
|
|
736
|
+
# Get keyword arguments
|
|
737
|
+
num_u = kwargs.get("num_u", 1) # number of knot removals on the u-direction
|
|
738
|
+
num_v = kwargs.get("num_v", 1) # number of knot removals on the v-direction
|
|
739
|
+
check_num = kwargs.get("check_r", True) # can be set to False when the caller checks number of removals
|
|
740
|
+
|
|
741
|
+
# Remove knots
|
|
742
|
+
try:
|
|
743
|
+
self._remove_knot_func(self, [u, v], [num_u, num_v], check_num=check_num)
|
|
744
|
+
except GeomdlException as e:
|
|
745
|
+
print(e)
|
|
746
|
+
return
|
|
747
|
+
|
|
748
|
+
# Evaluate curve again if it has already been evaluated before knot removal
|
|
749
|
+
if check_num and self._eval_points:
|
|
750
|
+
self.evaluate()
|
|
751
|
+
|
|
752
|
+
def tangent(self, parpos, **kwargs):
|
|
753
|
+
"""Evaluates the tangent vectors of the surface at the given parametric position(s).
|
|
754
|
+
|
|
755
|
+
.. deprecated: 5.3.0
|
|
756
|
+
|
|
757
|
+
Please use :func:`operations.tangent` instead.
|
|
758
|
+
|
|
759
|
+
:param parpos: parametric position(s) where the evaluation will be executed
|
|
760
|
+
:type parpos: list or tuple
|
|
761
|
+
:return: an array containing "point" and "vector"s on u- and v-directions, respectively
|
|
762
|
+
:rtype: tuple
|
|
763
|
+
"""
|
|
764
|
+
return tuple()
|
|
765
|
+
|
|
766
|
+
def normal(self, parpos, **kwargs):
|
|
767
|
+
"""Evaluates the normal vector of the surface at the given parametric position(s).
|
|
768
|
+
|
|
769
|
+
.. deprecated: 5.3.0
|
|
770
|
+
|
|
771
|
+
Please use :func:`operations.normal` instead.
|
|
772
|
+
|
|
773
|
+
:param parpos: parametric position(s) where the evaluation will be executed
|
|
774
|
+
:type parpos: list or tuple
|
|
775
|
+
:return: an array containing "point" and "vector" pairs
|
|
776
|
+
:rtype: tuple
|
|
777
|
+
"""
|
|
778
|
+
return tuple()
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
@utl.export
|
|
782
|
+
class Volume(abstract.Volume):
|
|
783
|
+
"""Data storage and evaluation class for B-spline (non-rational) volumes.
|
|
784
|
+
|
|
785
|
+
This class provides the following properties:
|
|
786
|
+
|
|
787
|
+
* :py:attr:`type` = spline
|
|
788
|
+
* :py:attr:`id`
|
|
789
|
+
* :py:attr:`order_u`
|
|
790
|
+
* :py:attr:`order_v`
|
|
791
|
+
* :py:attr:`order_w`
|
|
792
|
+
* :py:attr:`degree_u`
|
|
793
|
+
* :py:attr:`degree_v`
|
|
794
|
+
* :py:attr:`degree_w`
|
|
795
|
+
* :py:attr:`knotvector_u`
|
|
796
|
+
* :py:attr:`knotvector_v`
|
|
797
|
+
* :py:attr:`knotvector_w`
|
|
798
|
+
* :py:attr:`ctrlpts`
|
|
799
|
+
* :py:attr:`ctrlpts_size_u`
|
|
800
|
+
* :py:attr:`ctrlpts_size_v`
|
|
801
|
+
* :py:attr:`ctrlpts_size_w`
|
|
802
|
+
* :py:attr:`delta`
|
|
803
|
+
* :py:attr:`delta_u`
|
|
804
|
+
* :py:attr:`delta_v`
|
|
805
|
+
* :py:attr:`delta_w`
|
|
806
|
+
* :py:attr:`sample_size`
|
|
807
|
+
* :py:attr:`sample_size_u`
|
|
808
|
+
* :py:attr:`sample_size_v`
|
|
809
|
+
* :py:attr:`sample_size_w`
|
|
810
|
+
* :py:attr:`bbox`
|
|
811
|
+
* :py:attr:`name`
|
|
812
|
+
* :py:attr:`dimension`
|
|
813
|
+
* :py:attr:`vis`
|
|
814
|
+
* :py:attr:`evaluator`
|
|
815
|
+
* :py:attr:`rational`
|
|
816
|
+
|
|
817
|
+
**Keyword Arguments:**
|
|
818
|
+
|
|
819
|
+
* ``precision``: number of decimal places to round to. *Default: 18*
|
|
820
|
+
* ``normalize_kv``: activates knot vector normalization. *Default: True*
|
|
821
|
+
* ``find_span_func``: sets knot span search implementation. *Default:* :func:`.helpers.find_span_linear`
|
|
822
|
+
* ``insert_knot_func``: sets knot insertion implementation. *Default:* :func:`.operations.insert_knot`
|
|
823
|
+
* ``remove_knot_func``: sets knot removal implementation. *Default:* :func:`.operations.remove_knot`
|
|
824
|
+
|
|
825
|
+
Please refer to the :py:class:`.abstract.Volume()` documentation for more details.
|
|
826
|
+
"""
|
|
827
|
+
|
|
828
|
+
# __slots__ = ('_insert_knot_func', '_remove_knot_func')
|
|
829
|
+
|
|
830
|
+
def __init__(self, **kwargs):
|
|
831
|
+
super(Volume, self).__init__(**kwargs)
|
|
832
|
+
self._evaluator = evaluators.VolumeEvaluator(find_span_func=self._span_func)
|
|
833
|
+
self._insert_knot_func = kwargs.get("insert_knot_func", operations.insert_knot)
|
|
834
|
+
self._remove_knot_func = kwargs.get("remove_knot_func", operations.remove_knot)
|
|
835
|
+
|
|
836
|
+
def save(self, file_name):
|
|
837
|
+
"""Saves the volume as a pickled file.
|
|
838
|
+
|
|
839
|
+
.. deprecated:: 5.2.4
|
|
840
|
+
|
|
841
|
+
Use :func:`.exchange.export_json()` instead.
|
|
842
|
+
|
|
843
|
+
:param file_name: name of the file to be saved
|
|
844
|
+
:type file_name: str
|
|
845
|
+
"""
|
|
846
|
+
return None
|
|
847
|
+
|
|
848
|
+
def load(self, file_name):
|
|
849
|
+
"""Loads the volume from a pickled file.
|
|
850
|
+
|
|
851
|
+
.. deprecated:: 5.2.4
|
|
852
|
+
|
|
853
|
+
Use :func:`.exchange.import_json()` instead.
|
|
854
|
+
|
|
855
|
+
:param file_name: name of the file to be loaded
|
|
856
|
+
:type file_name: str
|
|
857
|
+
"""
|
|
858
|
+
return None
|
|
859
|
+
|
|
860
|
+
def evaluate(self, **kwargs):
|
|
861
|
+
"""Evaluates the volume.
|
|
862
|
+
|
|
863
|
+
The evaluated points are stored in :py:attr:`evalpts` property.
|
|
864
|
+
|
|
865
|
+
Keyword arguments:
|
|
866
|
+
* ``start_u``: start parameter on the u-direction
|
|
867
|
+
* ``stop_u``: stop parameter on the u-direction
|
|
868
|
+
* ``start_v``: start parameter on the v-direction
|
|
869
|
+
* ``stop_v``: stop parameter on the v-direction
|
|
870
|
+
* ``start_w``: start parameter on the w-direction
|
|
871
|
+
* ``stop_w``: stop parameter on the w-direction
|
|
872
|
+
|
|
873
|
+
"""
|
|
874
|
+
# Call parent method
|
|
875
|
+
super(Volume, self).evaluate(**kwargs)
|
|
876
|
+
|
|
877
|
+
# Find evaluation start and stop parameter values
|
|
878
|
+
start_u = kwargs.get("start_u", self.knotvector_u[self.degree_u])
|
|
879
|
+
stop_u = kwargs.get("stop_u", self.knotvector_u[-(self.degree_u + 1)])
|
|
880
|
+
start_v = kwargs.get("start_v", self.knotvector_v[self.degree_v])
|
|
881
|
+
stop_v = kwargs.get("stop_v", self.knotvector_v[-(self.degree_v + 1)])
|
|
882
|
+
start_w = kwargs.get("start_w", self.knotvector_w[self.degree_w])
|
|
883
|
+
stop_w = kwargs.get("stop_w", self.knotvector_w[-(self.degree_w + 1)])
|
|
884
|
+
|
|
885
|
+
# Check if all the input parameters are in the range
|
|
886
|
+
if self._kv_normalize:
|
|
887
|
+
if not utilities.check_params([start_u, stop_u, start_v, stop_v, start_w, stop_w]):
|
|
888
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
889
|
+
|
|
890
|
+
# Clean up the evaluated points
|
|
891
|
+
self.reset(evalpts=True)
|
|
892
|
+
|
|
893
|
+
# Evaluate and cache
|
|
894
|
+
self._eval_points = self._evaluator.evaluate(
|
|
895
|
+
self.data, start=(start_u, start_v, start_w), stop=(stop_u, stop_v, stop_w)
|
|
896
|
+
)
|
|
897
|
+
|
|
898
|
+
def evaluate_single(self, param):
|
|
899
|
+
"""Evaluates the volume at the input (u, v, w) parameter.
|
|
900
|
+
|
|
901
|
+
:param param: parameter (u, v, w)
|
|
902
|
+
:type param: list, tuple
|
|
903
|
+
:return: evaluated surface point at the given parameter pair
|
|
904
|
+
:rtype: list
|
|
905
|
+
"""
|
|
906
|
+
# Call parent method
|
|
907
|
+
super(Volume, self).evaluate_single(param)
|
|
908
|
+
|
|
909
|
+
# Check if all parameters are in the range
|
|
910
|
+
if self._kv_normalize:
|
|
911
|
+
if not utilities.check_params(param):
|
|
912
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
913
|
+
|
|
914
|
+
# Evaluate the volume point
|
|
915
|
+
pt = self._evaluator.evaluate(self.data, start=param, stop=param)
|
|
916
|
+
return pt[0]
|
|
917
|
+
|
|
918
|
+
def evaluate_list(self, param_list):
|
|
919
|
+
"""Evaluates the volume for a given list of (u, v, w) parameters.
|
|
920
|
+
|
|
921
|
+
:param param_list: list of parameters in format (u, v, w)
|
|
922
|
+
:type param_list: list, tuple
|
|
923
|
+
:return: evaluated surface point at the input parameter pairs
|
|
924
|
+
:rtype: tuple
|
|
925
|
+
"""
|
|
926
|
+
# Call parent method
|
|
927
|
+
super(Volume, self).evaluate_list(param_list)
|
|
928
|
+
|
|
929
|
+
# Evaluate (u, v, w) list
|
|
930
|
+
res = []
|
|
931
|
+
for prm in param_list:
|
|
932
|
+
if self._kv_normalize:
|
|
933
|
+
if utilities.check_params(prm):
|
|
934
|
+
res.append(self.evaluate_single(prm))
|
|
935
|
+
else:
|
|
936
|
+
res.append(self.evaluate_single(prm))
|
|
937
|
+
return res
|
|
938
|
+
|
|
939
|
+
def insert_knot(self, u=None, v=None, w=None, **kwargs):
|
|
940
|
+
"""Inserts knot(s) on the u-, v- and w-directions
|
|
941
|
+
|
|
942
|
+
Keyword Arguments:
|
|
943
|
+
* ``num_u``: Number of knot insertions on the u-direction. *Default: 1*
|
|
944
|
+
* ``num_v``: Number of knot insertions on the v-direction. *Default: 1*
|
|
945
|
+
* ``num_w``: Number of knot insertions on the w-direction. *Default: 1*
|
|
946
|
+
|
|
947
|
+
:param u: knot to be inserted on the u-direction
|
|
948
|
+
:type u: float
|
|
949
|
+
:param v: knot to be inserted on the v-direction
|
|
950
|
+
:type v: float
|
|
951
|
+
:param w: knot to be inserted on the w-direction
|
|
952
|
+
:type w: float
|
|
953
|
+
"""
|
|
954
|
+
# Check all parameters are set before the evaluation
|
|
955
|
+
self._check_variables()
|
|
956
|
+
|
|
957
|
+
# Check if the parameter values are correctly defined
|
|
958
|
+
if self._kv_normalize:
|
|
959
|
+
if not utilities.check_params([u, v, w]):
|
|
960
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
961
|
+
|
|
962
|
+
# Get keyword arguments
|
|
963
|
+
num_u = kwargs.get("num_u", 1) # number of knot insertions on the u-direction
|
|
964
|
+
num_v = kwargs.get("num_v", 1) # number of knot insertions on the v-direction
|
|
965
|
+
num_w = kwargs.get("num_w", 1) # number of knot insertions on the w-direction
|
|
966
|
+
check_num = kwargs.get("check_r", True) # Enables/disables number of knot insertions checking
|
|
967
|
+
|
|
968
|
+
# Insert knots
|
|
969
|
+
try:
|
|
970
|
+
self._insert_knot_func(self, [u, v, w], [num_u, num_v, num_w], check_num=check_num)
|
|
971
|
+
except GeomdlException as e:
|
|
972
|
+
print(e)
|
|
973
|
+
return
|
|
974
|
+
|
|
975
|
+
# Evaluate surface again if it has already been evaluated before knot insertion
|
|
976
|
+
if check_num and self._eval_points:
|
|
977
|
+
self.evaluate()
|
|
978
|
+
|
|
979
|
+
def remove_knot(self, u=None, v=None, w=None, **kwargs):
|
|
980
|
+
"""Inserts knot(s) on the u-, v- and w-directions
|
|
981
|
+
|
|
982
|
+
Keyword Arguments:
|
|
983
|
+
* ``num_u``: Number of knot removals on the u-direction. *Default: 1*
|
|
984
|
+
* ``num_v``: Number of knot removals on the v-direction. *Default: 1*
|
|
985
|
+
* ``num_w``: Number of knot removals on the w-direction. *Default: 1*
|
|
986
|
+
|
|
987
|
+
:param u: knot to be removed on the u-direction
|
|
988
|
+
:type u: float
|
|
989
|
+
:param v: knot to be removed on the v-direction
|
|
990
|
+
:type v: float
|
|
991
|
+
:param w: knot to be removed on the w-direction
|
|
992
|
+
:type w: float
|
|
993
|
+
"""
|
|
994
|
+
# Check all parameters are set before the evaluation
|
|
995
|
+
self._check_variables()
|
|
996
|
+
|
|
997
|
+
# Check if the parameter values are correctly defined
|
|
998
|
+
if self._kv_normalize:
|
|
999
|
+
if not utilities.check_params([u, v, w]):
|
|
1000
|
+
raise GeomdlException("Parameters should be between 0 and 1")
|
|
1001
|
+
|
|
1002
|
+
# Get keyword arguments
|
|
1003
|
+
num_u = kwargs.get("num_u", 1) # number of knot removals on the u-direction
|
|
1004
|
+
num_v = kwargs.get("num_v", 1) # number of knot removals on the v-direction
|
|
1005
|
+
num_w = kwargs.get("num_w", 1) # number of knot insertions on the w-direction
|
|
1006
|
+
check_num = kwargs.get("check_r", True) # can be set to False when the caller checks number of removals
|
|
1007
|
+
|
|
1008
|
+
# Remove knots
|
|
1009
|
+
try:
|
|
1010
|
+
self._remove_knot_func(self, [u, v, w], [num_u, num_v, num_w], check_num=check_num)
|
|
1011
|
+
except GeomdlException as e:
|
|
1012
|
+
print(e)
|
|
1013
|
+
return
|
|
1014
|
+
|
|
1015
|
+
# Evaluate curve again if it has already been evaluated before knot removal
|
|
1016
|
+
if check_num and self._eval_points:
|
|
1017
|
+
self.evaluate()
|