Coverage for src/beamme/mesh_creation_functions/nurbs_geometries.py: 99%
205 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-28 15:20 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-28 15:20 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2018-2026 BeamMe Authors
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in
13# all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21# THE SOFTWARE.
22"""This file has functions to create NURBS geometries using Geomdl."""
24import numpy as _np
25from geomdl import NURBS as _NURBS
26from geomdl import compatibility as _compat
27from geomdl import operations as _operations
30def create_nurbs_hollow_cylinder_segment_2d(
31 radius_in, radius_out, angle, *, n_ele_u=1, n_ele_v=1
32):
33 """Creates a patch of a 2 dimensional segment of a hollow cylinder.
35 Args
36 ----
37 radius_in: double
38 inner cylinder radius
39 radius_out: double
40 outer cylinder radius
41 angle: double
42 angle of the hollow cylinder section (radians)
43 n_ele_u: int
44 number of elements in the parametric u-direction
45 n_ele_v: int
46 number of elements in the parametric v-direction
49 Return
50 ----
51 surf: geomdl object
52 geomdl object that contains the surface information
53 """
54 # Check the validity of the input values:
55 if radius_in >= radius_out:
56 raise ValueError(
57 "The external radius should be larger than the internal radius of a hollow cylinder."
58 )
59 if (angle > _np.pi) or (angle < 0):
60 raise ValueError(
61 "The following algorithm for creating a hollow cylinder section is only valid for 0 < angle <= pi."
62 )
64 # Create a NURBS surface instance
65 surf = _NURBS.Surface()
67 # Set degrees
68 surf.degree_u = 2
69 surf.degree_v = 2
71 # Control points and set them to the surface
72 p_size_u = 3
73 p_size_v = 3
75 # To calculate the internal control point cp_2 we have to calculate
76 # the intersection of the tangents of the points cp_1 and cp_3.
77 # As point cp_1 is always defined over the x axis, its tangent is simply x = radius
78 # The equation of the tangent to the circle at the cp_3 point is:
79 # y - cp_3y = -(cp_3x/cp_3y)*(x - cp_3x).
81 # Obtaining the control points that define the external arc of the hollow cylinder
82 cp_ext1 = [radius_out, 0.0, 0.0]
83 cp_ext3 = [radius_out * _np.cos(angle), radius_out * _np.sin(angle), 0.0]
84 cp_ext2 = [
85 radius_out,
86 -(cp_ext3[0] / cp_ext3[1]) * (radius_out - cp_ext3[0]) + cp_ext3[1],
87 0.0,
88 ]
90 # Obtaining the control points that define the internal arc of the hollow cylinder
91 cp_int1 = [radius_in, 0.0, 0.0]
92 cp_int3 = [radius_in * _np.cos(angle), radius_in * _np.sin(angle), 0.0]
93 cp_int2 = [
94 radius_in,
95 -(cp_int3[0] / cp_int3[1]) * (radius_in - cp_int3[0]) + cp_int3[1],
96 0.0,
97 ]
99 # Obtaining the control points positioned in the middle of the internal and external arches
100 cp_middle1 = [(cp_ext1[0] + cp_int1[0]) / 2, (cp_ext1[1] + cp_int1[1]) / 2, 0.0]
101 cp_middle2 = [(cp_ext2[0] + cp_int2[0]) / 2, (cp_ext2[1] + cp_int2[1]) / 2, 0.0]
102 cp_middle3 = [(cp_ext3[0] + cp_int3[0]) / 2, (cp_ext3[1] + cp_int3[1]) / 2, 0.0]
104 ctrlpts = [
105 cp_ext1,
106 cp_ext2,
107 cp_ext3,
108 cp_middle1,
109 cp_middle2,
110 cp_middle3,
111 cp_int1,
112 cp_int2,
113 cp_int3,
114 ]
116 weights = [
117 1.0,
118 _np.cos(angle / 2),
119 1.0,
120 1.0,
121 _np.cos(angle / 2),
122 1.0,
123 1.0,
124 _np.cos(angle / 2),
125 1.0,
126 ]
128 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights)
129 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v)
131 surf.ctrlpts_size_u = p_size_u
132 surf.ctrlpts_size_v = p_size_v
133 surf.ctrlptsw = n_ctrlptsw
135 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
136 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
138 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v)
140 return surf
143def create_nurbs_cylindrical_shell_sector(
144 radius: float, angle: float, length: float, *, n_ele_u: int = 1, n_ele_v: int = 1
145) -> _NURBS.Surface:
146 """Creates a NURBS surface representing a 3D sector of a cylindrical shell.
148 The center of the cylindrical shell sector is located at [0, 0, 0].
150 Args:
151 radius: Radius of the cylindrical shell.
152 angle: Angle of the cylindrical shell sector in radians.
153 The angle is only valid for 0 < angle <= pi according
154 to "Isogeometric Analysis: Toward Integration of CAD and FEA" by
155 J.Austin Cottrell, 2009
156 length: Length of the cylindrical shell.
157 n_ele_u: Number of elements in the parametric u-direction. Defaults to 1.
158 n_ele_v: Number of elements in the parametric v-direction. Defaults to 1.
160 Returns:
161 geomdl.NURBS.Surface: A geomdl object that contains the surface information.
162 """
163 # Check the validity of the input values:
164 if (angle >= _np.pi) or (angle < 0):
165 raise ValueError(
166 "The following algorithm for creating a cylindrical shell sector is only valid for 0 < angle <= pi."
167 )
169 # Create a NURBS surface instance
170 surf = _NURBS.Surface()
172 # Set degrees
173 surf.degree_u = 2
174 surf.degree_v = 2
176 # Control points and set them to the surface
177 p_size_u = 3
178 p_size_v = 3
180 # Obtaining the control points
181 cp_1 = [-radius * _np.sin(angle / 2), -length / 2, -radius * _np.cos(angle / 2)]
182 cp_3 = [radius * _np.sin(angle / 2), -length / 2, -radius * _np.cos(angle / 2)]
184 # Calculating position of middle points. This is done by
185 # obtaining the tangents on the points cp_1 and cp_3 and
186 # calculating the intersection point between the tangents.
187 m_1 = -_np.tan(-angle / 2)
188 m_3 = -_np.tan(angle / 2)
189 b_1 = cp_1[2] + m_1 * cp_1[0]
190 b_3 = cp_3[2] + m_3 * cp_3[0]
192 inter_point_x = (b_3 - b_1) / (m_3 - m_1)
193 inter_point_z = m_1 * inter_point_x + b_1
195 # The intersection point is assigned to the middle points cp_2, cp_5 and cp_8
196 cp_2 = [inter_point_x, -length / 2, inter_point_z]
198 cp_4 = [-radius * _np.sin(angle / 2), 0.0, -radius * _np.cos(angle / 2)]
199 cp_5 = [inter_point_x, 0.0, inter_point_z]
200 cp_6 = [radius * _np.sin(angle / 2), 0.0, -radius * _np.cos(angle / 2)]
202 cp_7 = [
203 -radius * _np.sin(angle / 2),
204 length / 2,
205 -radius * _np.cos(angle / 2),
206 ]
207 cp_8 = [inter_point_x, length / 2, inter_point_z]
208 cp_9 = [radius * _np.sin(angle / 2), length / 2, -radius * _np.cos(angle / 2)]
210 ctrlpts = [cp_1, cp_4, cp_7, cp_2, cp_5, cp_8, cp_3, cp_6, cp_9]
212 weights = [
213 1.0,
214 1.0,
215 1.0,
216 _np.cos(angle / 2),
217 _np.cos(angle / 2),
218 _np.cos(angle / 2),
219 1.0,
220 1.0,
221 1.0,
222 ]
224 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights)
226 surf.ctrlpts_size_u = p_size_u
227 surf.ctrlpts_size_v = p_size_v
228 surf.ctrlptsw = t_ctrlptsw
230 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
231 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
233 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v)
235 return surf
238def create_nurbs_flat_plate_2d(width, length, *, n_ele_u=1, n_ele_v=1):
239 """Creates a patch of a 2 dimensional flat plate.
241 Args
242 ----
243 width: double
244 dimension of the plate in the x-direction
245 length: double
246 dimension of the plate in the y-direction
247 n_ele_u: int
248 number of elements in the parametric u-direction
249 n_ele_v: int
250 number of elements in the parametric v-direction
253 Return
254 ----
255 surf: geomdl object
256 geomdl object that contains the surface information
257 """
258 # Create a NURBS surface instance
259 surf = _NURBS.Surface()
261 # Set degrees
262 surf.degree_u = 2
263 surf.degree_v = 2
265 # Control points and set them to the surface
266 p_size_u = 3
267 p_size_v = 3
269 ctrlpts = [
270 [-width / 2, -length / 2, 0.0],
271 [0.0, -length / 2, 0.0],
272 [width / 2, -length / 2, 0.0],
273 [-width / 2, 0.0, 0.0],
274 [0.0, 0.0, 0.0],
275 [width / 2, 0.0, 0.0],
276 [-width / 2, length / 2, 0.0],
277 [0.0, length / 2, 0.0],
278 [width / 2, length / 2, 0.0],
279 ]
281 weights = [1.0] * 9
283 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights)
284 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v)
286 surf.ctrlpts_size_u = p_size_u
287 surf.ctrlpts_size_v = p_size_v
288 surf.ctrlptsw = n_ctrlptsw
290 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
291 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
293 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v)
295 return surf
298def create_nurbs_sphere_surface(radius, n_ele_u=1, n_ele_v=1):
299 """Generates a patch of a sphere as a NURBS surface.
301 This function constructs a segment of a spherical surface using Non-Uniform Rational
302 B-Splines (NURBS) based on the specified radius and the number of elements
303 in the parametric u and v directions.
305 Args
306 ---
307 radius: double
308 radius of the sphere
309 n_ele_u: int
310 number of elements in the parametric u-direction
311 n_ele_v: int
312 number of elements in the parametric v-direction
314 Return
315 ----
316 surf: geomdl object
317 geomdl object that contains the surface information
318 """
319 # Create a NURBS surface instance
320 surf = _NURBS.Surface()
322 # Set degrees
323 surf.degree_u = 2
324 surf.degree_v = 2
326 # Control points and set them to the surface
327 p_size_u = 3
328 p_size_v = 3
330 dummy = 6.0 * (
331 5.0 / 12.0
332 + 0.5 * _np.sqrt(2.0 / 3.0)
333 - 0.25 / _np.sqrt(3.0)
334 - 0.5 * _np.sqrt(2.0 / 3.0) * _np.sqrt(3.0) / 2.0
335 )
337 ctrlpts = [
338 [
339 2.0 * radius / _np.sqrt(6.0) * -_np.sin(1.0 / 4.0 * _np.pi),
340 -radius / _np.sqrt(3.0),
341 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi),
342 ],
343 [
344 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi)
345 + radius
346 * _np.sqrt(3.0)
347 / (_np.sqrt(2.0) * 2.0)
348 * -_np.sin(1.0 / 4.0 * _np.pi),
349 -_np.sqrt(3.0) / 2 * radius,
350 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi)
351 + radius
352 * _np.sqrt(3.0)
353 / (_np.sqrt(2.0) * 2.0)
354 * _np.sin(1.0 / 4.0 * _np.pi),
355 ],
356 [
357 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi),
358 -radius / _np.sqrt(3.0),
359 2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi),
360 ],
361 [
362 radius * _np.sqrt(6.0) / 2.0 * -_np.sin(1.0 / 4.0 * _np.pi),
363 0.0,
364 radius * _np.sqrt(6.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi),
365 ],
366 [
367 radius * dummy * _np.sqrt(2.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi)
368 + radius * dummy * _np.sqrt(2.0) / 2.0 * -_np.sin(1.0 / 4.0 * _np.pi),
369 0.0,
370 radius * dummy * _np.sqrt(2.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi)
371 + radius * dummy * _np.sqrt(2.0) / 2.0 * _np.sin(1.0 / 4.0 * _np.pi),
372 ],
373 [
374 radius * _np.sqrt(6.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi),
375 0.0,
376 radius * _np.sqrt(6.0) / 2.0 * _np.sin(1.0 / 4.0 * _np.pi),
377 ],
378 [
379 2.0 * radius / _np.sqrt(6.0) * -_np.sin(1.0 / 4.0 * _np.pi),
380 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi),
381 radius / _np.sqrt(3.0),
382 ],
383 [
384 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi)
385 + radius
386 * _np.sqrt(3.0)
387 / (_np.sqrt(2.0) * 2.0)
388 * -_np.sin(1.0 / 4.0 * _np.pi),
389 _np.sqrt(3.0) / 2 * radius,
390 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi)
391 + radius
392 * _np.sqrt(3.0)
393 / (_np.sqrt(2.0) * 2.0)
394 * _np.sin(1.0 / 4.0 * _np.pi),
395 ],
396 [
397 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi),
398 radius / _np.sqrt(3.0),
399 2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi),
400 ],
401 ]
403 weights = [
404 1.0,
405 2.0 / _np.sqrt(6.0),
406 1.0,
407 2.0 / _np.sqrt(6.0),
408 2.0 / 3.0,
409 2.0 / _np.sqrt(6.0),
410 1.0,
411 2.0 / _np.sqrt(6.0),
412 1.0,
413 ]
415 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights)
416 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v)
418 surf.ctrlpts_size_u = p_size_u
419 surf.ctrlpts_size_v = p_size_v
420 surf.ctrlptsw = n_ctrlptsw
422 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
423 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
425 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v)
427 return surf
430def create_nurbs_hemisphere_surface(radius, n_ele_uv=1):
431 """Generates a hemisphere as a NURBS surface.
433 This function constructs five segments that represent the surface of a
434 hemisphere using Non-Uniform Rational B-Splines (NURBS) based on the specified
435 radius and the number of elements in the parametric u and v directions.
436 To secure the connectivity between surfaces, all surfaces must have the same
437 parametric representation in any parametric direction. Therefore, the number
438 of elements in u- and v-directions must be the same.
440 This function generates a list of five NURBS geomdl objects.
442 Args
443 ---
444 radius: double
445 Radius of the hemisphere
446 n_ele_uv: int
447 Number of elements in the parametric u- and v- directions
449 Return
450 ----
451 list: list(geomdl object)
452 A list of geomdl objects that contains the surface information
453 """
454 # Create the first section of the hemisphere
455 hemisphere_1 = create_nurbs_sphere_surface(radius, n_ele_u=1, n_ele_v=1)
457 # Create a temporary section by rotating and translating the
458 # first section of the hemisphere
459 temp_hemisphere = _operations.rotate(hemisphere_1, 90, axis=1)
460 temp_hemisphere = _operations.translate(
461 temp_hemisphere,
462 (0, 0, -2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi) * 2),
463 )
465 # To create the hemisphere it is necessary to split the temporary
466 # sphere section in two pieces. This split is done in u = 0.5. After
467 # the split, the second surface is taken as it will be
468 # adjacent to the first section of the sphere
469 cut_section_sphere = _operations.split_surface_u(temp_hemisphere, param=0.5)
470 hemisphere_2 = cut_section_sphere[1]
472 translation_component = radius * _np.sin(1.0 / 4.0 * _np.pi) * 2
473 # Create the third section. Rotate and translate it accordingly
474 hemisphere_3 = _operations.rotate(hemisphere_2, 90, axis=2)
475 hemisphere_3 = _operations.translate(hemisphere_3, (translation_component, 0, 0))
477 # Create the forth section. Rotate and translate it accordingly
478 hemisphere_4 = _operations.rotate(hemisphere_3, 90, axis=2)
479 hemisphere_4 = _operations.translate(hemisphere_4, (0, translation_component, 0))
481 # Create the fifth section. Rotate and translate it accordingly
482 hemisphere_5 = _operations.rotate(hemisphere_4, 90, axis=2)
483 hemisphere_5 = _operations.translate(hemisphere_5, (-translation_component, 0, 0))
485 patches = [hemisphere_1, hemisphere_2, hemisphere_3, hemisphere_4, hemisphere_5]
486 for patch in patches:
487 do_uniform_knot_refinement_surface(patch, n_ele_uv, n_ele_uv)
488 return patches
491def create_nurbs_torus_surface(radius_torus, radius_circle, *, n_ele_u=1, n_ele_v=1):
492 """Creates a NURBS patch for the construction of a ring torus (outer
493 surface). A ring torus is a surface of revolution generated by revolving a
494 circle in a 3-dimensional space around an axis of revolution (axis z). The
495 center of the torus is located at the coordinates [0, 0, 0].
497 This function constructs the surface of a ring torus in the following manner:
498 - The quarter of the torus is made by 4 patches
499 - These patches are rotated three times around the z-axis by 90°, e.g. theta = 90°, 180°, 270°,
500 leading to a total of 16 NURBS patches.
502 The number of elements in this function are given for a single patch, where:
503 - n_ele_u is the number of elements in the "radius_torus" direction
504 - n_ele_v is the number of elements in the "radius_circle" direction
506 This function generates a list of 16 NURBS geomdl objects.
508 Args
509 ----
510 radius_torus: double
511 distance from the axis of revolution (axis z) to the center of the revolving circle
512 radius_circle: double
513 radius of the circle that revolves around the axis of revolution
514 n_ele_u: int
515 number of elements in the parametric u-direction
516 n_ele_v: int
517 number of elements in the parametric v-direction
518 """
520 # Create four NURBS surface instances. These are the base patches of the torus.
521 surf_1 = _NURBS.Surface()
522 surf_2 = _NURBS.Surface()
523 surf_3 = _NURBS.Surface()
524 surf_4 = _NURBS.Surface()
525 base_surfs = [surf_1, surf_2, surf_3, surf_4]
527 # Define control points and set them to the surfaces
528 p_size_u = 3
529 p_size_v = 3
531 dummy_surf1 = radius_torus + radius_circle
532 dummy_surf2 = radius_torus - radius_circle
534 ctrlpts_surf1 = [
535 [dummy_surf1, 0.0, 0.0],
536 [dummy_surf1, 0.0, radius_circle],
537 [radius_torus, 0.0, radius_circle],
538 [dummy_surf1, dummy_surf1, 0.0],
539 [dummy_surf1, dummy_surf1, radius_circle],
540 [radius_torus, radius_torus, radius_circle],
541 [0.0, dummy_surf1, 0.0],
542 [0.0, dummy_surf1, radius_circle],
543 [0.0, radius_torus, radius_circle],
544 ]
546 ctrlpts_surf2 = [
547 [dummy_surf1, 0.0, 0.0],
548 [dummy_surf1, 0.0, -radius_circle],
549 [radius_torus, 0.0, -radius_circle],
550 [dummy_surf1, dummy_surf1, 0.0],
551 [dummy_surf1, dummy_surf1, -radius_circle],
552 [radius_torus, radius_torus, -radius_circle],
553 [0.0, dummy_surf1, 0.0],
554 [0.0, dummy_surf1, -radius_circle],
555 [0.0, radius_torus, -radius_circle],
556 ]
558 ctrlpts_surf3 = [
559 [radius_torus, 0.0, -radius_circle],
560 [dummy_surf2, 0.0, -radius_circle],
561 [dummy_surf2, 0.0, 0.0],
562 [radius_torus, radius_torus, -radius_circle],
563 [dummy_surf2, dummy_surf2, -radius_circle],
564 [dummy_surf2, dummy_surf2, 0.0],
565 [0.0, radius_torus, -radius_circle],
566 [0.0, dummy_surf2, -radius_circle],
567 [0.0, dummy_surf2, 0.0],
568 ]
570 ctrlpts_surf4 = [
571 [0.0, dummy_surf2, 0.0],
572 [0.0, dummy_surf2, radius_circle],
573 [0.0, radius_torus, radius_circle],
574 [dummy_surf2, dummy_surf2, 0.0],
575 [dummy_surf2, dummy_surf2, radius_circle],
576 [radius_torus, radius_torus, radius_circle],
577 [dummy_surf2, 0.0, 0.0],
578 [dummy_surf2, 0.0, radius_circle],
579 [radius_torus, 0.0, radius_circle],
580 ]
582 weights = [
583 1.0,
584 _np.sqrt(2) / 2,
585 1.0,
586 _np.sqrt(2) / 2,
587 0.5,
588 _np.sqrt(2) / 2,
589 1.0,
590 _np.sqrt(2) / 2,
591 1.0,
592 ]
594 t_ctrlptsw1 = _compat.combine_ctrlpts_weights(ctrlpts_surf1, weights)
595 t_ctrlptsw2 = _compat.combine_ctrlpts_weights(ctrlpts_surf2, weights)
596 t_ctrlptsw3 = _compat.combine_ctrlpts_weights(ctrlpts_surf3, weights)
597 t_ctrlptsw4 = _compat.combine_ctrlpts_weights(ctrlpts_surf4, weights)
599 t_ctrlpts_surfs = [t_ctrlptsw1, t_ctrlptsw2, t_ctrlptsw3, t_ctrlptsw4]
601 for surf, t_ctrlpts in zip(base_surfs, t_ctrlpts_surfs):
602 # Set degrees
603 surf.degree_u = 2
604 surf.degree_v = 2
606 surf.ctrlpts_size_u = p_size_u
607 surf.ctrlpts_size_v = p_size_v
609 # Set control points
610 surf.ctrlptsw = t_ctrlpts
612 # Set knot vectors
613 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
614 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
616 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v)
618 # Define the rotations and translations to rotate the base patches and form a complete torus
619 tmp_trans = [
620 radius_torus + radius_circle,
621 radius_torus + radius_circle,
622 radius_torus,
623 radius_torus - radius_circle,
624 ]
626 transform_surf1 = [
627 [(-tmp_trans[0], tmp_trans[0], 0), 90, 2],
628 [(-2 * tmp_trans[0], 0, 0), 180, 2],
629 [(-tmp_trans[0], -tmp_trans[0], 0), 270, 2],
630 ]
632 transform_surf2 = [
633 [(-tmp_trans[1], tmp_trans[1], 0), 90, 2],
634 [(-2 * tmp_trans[1], 0, 0), 180, 2],
635 [(-tmp_trans[1], -tmp_trans[1], 0), 270, 2],
636 ]
638 transform_surf3 = [
639 [(-tmp_trans[2], tmp_trans[2], 0), 90, 2],
640 [(-2 * tmp_trans[2], 0, 0), 180, 2],
641 [(-tmp_trans[2], -tmp_trans[2], 0), 270, 2],
642 ]
644 transform_surf4 = [
645 [(-tmp_trans[3], -tmp_trans[3], 0), 90, 2],
646 [(0, -2 * tmp_trans[3], 0), 180, 2],
647 [(tmp_trans[3], -tmp_trans[3], 0), 270, 2],
648 ]
650 # Rotate base patches and store them
651 surfaces_torus = [surf_1, surf_2, surf_3, surf_4]
652 for transform1, transform2, transform3, transform4 in zip(
653 transform_surf1, transform_surf2, transform_surf3, transform_surf4
654 ):
655 new_surf1 = _operations.translate(surf_1, transform1[0])
656 new_surf1 = _operations.rotate(new_surf1, transform1[1], axis=transform1[2])
657 surfaces_torus.append(new_surf1)
659 new_surf2 = _operations.translate(surf_2, transform2[0])
660 new_surf2 = _operations.rotate(new_surf2, transform2[1], axis=transform2[2])
661 surfaces_torus.append(new_surf2)
663 new_surf3 = _operations.translate(surf_3, transform3[0])
664 new_surf3 = _operations.rotate(new_surf3, transform3[1], axis=transform3[2])
665 surfaces_torus.append(new_surf3)
667 new_surf4 = _operations.translate(surf_4, transform4[0])
668 new_surf4 = _operations.rotate(new_surf4, transform4[1], axis=transform4[2])
669 surfaces_torus.append(new_surf4)
671 return surfaces_torus
674def create_nurbs_brick(width, length, height, *, n_ele_u=1, n_ele_v=1, n_ele_w=1):
675 """Creates a patch of a 3 dimensional brick.
677 Args
678 ----
679 width: double
680 dimension of the plate in the x-direction
681 length: double
682 dimension of the plate in the y-direction
683 height: double
684 dimension of the plate in the z-direction
685 n_ele_u: int
686 number of elements in the parametric u-direction
687 n_ele_v: int
688 number of elements in the parametric v-direction
689 n_ele_w: int
690 number of elements in the parametric w-direction
693 Return
694 ----
695 vol: geomdl object
696 geomdl object that contains the volume information
697 """
698 # Create a NURBS volume instance
699 vol = _NURBS.Volume()
701 # Set degrees
702 vol.degree_u = 2
703 vol.degree_v = 2
704 vol.degree_w = 2
706 # Create control points and set them to the volume
707 cp_size_u = 3
708 cp_size_v = 3
709 cp_size_w = 3
711 ctrlpts = [
712 [-width / 2, -length / 2, -height / 2],
713 [-width / 2, 0.0, -height / 2],
714 [-width / 2, length / 2, -height / 2],
715 [0.0, -length / 2, -height / 2],
716 [0.0, 0.0, -height / 2],
717 [0.0, length / 2, -height / 2],
718 [width / 2, -length / 2, -height / 2],
719 [width / 2, 0.0, -height / 2],
720 [width / 2, length / 2, -height / 2],
721 [-width / 2, -length / 2, 0.0],
722 [-width / 2, 0.0, 0.0],
723 [-width / 2, length / 2, 0.0],
724 [0.0, -length / 2, 0.0],
725 [0.0, 0.0, 0.0],
726 [0.0, length / 2, 0.0],
727 [width / 2, -length / 2, 0.0],
728 [width / 2, 0.0, 0.0],
729 [width / 2, length / 2, 0.0],
730 [-width / 2, -length / 2, height / 2],
731 [-width / 2, 0.0, height / 2],
732 [-width / 2, length / 2, height / 2],
733 [0.0, -length / 2, height / 2],
734 [0.0, 0.0, height / 2],
735 [0.0, length / 2, height / 2],
736 [width / 2, -length / 2, height / 2],
737 [width / 2, 0.0, height / 2],
738 [width / 2, length / 2, height / 2],
739 ]
741 weights = [1.0] * 27
743 vol.ctrlpts_size_u = cp_size_u
744 vol.ctrlpts_size_v = cp_size_v
745 vol.ctrlpts_size_w = cp_size_w
747 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights)
749 vol.set_ctrlpts(t_ctrlptsw, cp_size_u, cp_size_v, cp_size_w)
751 vol.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
752 vol.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
753 vol.knotvector_w = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
755 do_uniform_knot_refinement_volume(vol, n_ele_u, n_ele_v, n_ele_w)
757 return vol
760def do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v):
761 """This function does an uniform knot refinement in the u- and v- direction.
763 Args
764 ----
765 surf: geomdl object
766 geomdl object that contains the surface information
767 n_ele_u: int
768 number of elements in the parametric u-direction
769 n_ele_v: int
770 number of elements in the parametric v-direction
772 Return
773 ----
774 surf: geomdl object
775 """
776 size_of_knotvector_u = 1 / n_ele_u
777 size_of_knotvector_v = 1 / n_ele_v
779 for i in range(1, n_ele_u):
780 _operations.insert_knot(surf, [size_of_knotvector_u * i, None], [1, 0])
781 for j in range(1, n_ele_v):
782 _operations.insert_knot(surf, [None, size_of_knotvector_v * j], [0, 1])
785def do_uniform_knot_refinement_volume(vol, n_ele_u, n_ele_v, n_ele_w):
786 """This function does an uniform knot refinement in the u-, v- and w- direction.
788 Args
789 ----
790 vol: geomdl object
791 geomdl object that contains the volume information
792 n_ele_u: int
793 number of elements in the parametric u-direction
794 n_ele_v: int
795 number of elements in the parametric v-direction
796 n_ele_w: int
797 number of elements in the parametric w-direction
799 Return
800 ----
801 vol: geomdl object
802 """
803 size_of_knotvector_u = 1 / n_ele_u
804 size_of_knotvector_v = 1 / n_ele_v
805 size_of_knotvector_w = 1 / n_ele_w
807 for i in range(1, n_ele_u):
808 _operations.insert_knot(vol, [size_of_knotvector_u * i, None, None], [1, 0, 0])
809 for j in range(1, n_ele_v):
810 _operations.insert_knot(vol, [None, size_of_knotvector_v * j, None], [0, 1, 0])
811 for k in range(1, n_ele_w):
812 _operations.insert_knot(vol, [None, None, size_of_knotvector_w * k], [0, 0, 1])