Coverage for src/meshpy/mesh_creation_functions/nurbs_geometries.py: 99%

205 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-09 12:53 +0000

1# The MIT License (MIT) 

2# 

3# Copyright (c) 2018-2025 MeshPy 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.""" 

23 

24import numpy as _np 

25from geomdl import NURBS as _NURBS 

26from geomdl import compatibility as _compat 

27from geomdl import operations as _operations 

28 

29 

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. 

34 

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 

47 

48 

49 Return 

50 ---- 

51 surf: geomdl object 

52 geomdl object that contains the surface information 

53 """ 

54 

55 # Check the validity of the input values: 

56 if radius_in >= radius_out: 

57 raise ValueError( 

58 "The external radius should be larger than the internal radius of a hollow cylinder." 

59 ) 

60 if (angle > _np.pi) or (angle < 0): 

61 raise ValueError( 

62 "The following algorithm for creating a hollow cylinder section is only valid for 0 < angle <= pi." 

63 ) 

64 

65 # Create a NURBS surface instance 

66 surf = _NURBS.Surface() 

67 

68 # Set degrees 

69 surf.degree_u = 2 

70 surf.degree_v = 2 

71 

72 # Control points and set them to the surface 

73 p_size_u = 3 

74 p_size_v = 3 

75 

76 # To calculate the internal control point cp_2 we have to calculate 

77 # the intersection of the tangents of the points cp_1 and cp_3. 

78 # As point cp_1 is always defined over the x axis, its tangent is simply x = radius 

79 # The equation of the tangent to the circle at the cp_3 point is: 

80 # y - cp_3y = -(cp_3x/cp_3y)*(x - cp_3x). 

81 

82 # Obtaining the control points that define the external arc of the hollow cylinder 

83 cp_ext1 = [radius_out, 0.0, 0.0] 

84 cp_ext3 = [radius_out * _np.cos(angle), radius_out * _np.sin(angle), 0.0] 

85 cp_ext2 = [ 

86 radius_out, 

87 -(cp_ext3[0] / cp_ext3[1]) * (radius_out - cp_ext3[0]) + cp_ext3[1], 

88 0.0, 

89 ] 

90 

91 # Obtaining the control points that define the internal arc of the hollow cylinder 

92 cp_int1 = [radius_in, 0.0, 0.0] 

93 cp_int3 = [radius_in * _np.cos(angle), radius_in * _np.sin(angle), 0.0] 

94 cp_int2 = [ 

95 radius_in, 

96 -(cp_int3[0] / cp_int3[1]) * (radius_in - cp_int3[0]) + cp_int3[1], 

97 0.0, 

98 ] 

99 

100 # Obtaining the control points positioned in the middle of the internal and external arches 

101 cp_middle1 = [(cp_ext1[0] + cp_int1[0]) / 2, (cp_ext1[1] + cp_int1[1]) / 2, 0.0] 

102 cp_middle2 = [(cp_ext2[0] + cp_int2[0]) / 2, (cp_ext2[1] + cp_int2[1]) / 2, 0.0] 

103 cp_middle3 = [(cp_ext3[0] + cp_int3[0]) / 2, (cp_ext3[1] + cp_int3[1]) / 2, 0.0] 

104 

105 ctrlpts = [ 

106 cp_ext1, 

107 cp_ext2, 

108 cp_ext3, 

109 cp_middle1, 

110 cp_middle2, 

111 cp_middle3, 

112 cp_int1, 

113 cp_int2, 

114 cp_int3, 

115 ] 

116 

117 weights = [ 

118 1.0, 

119 _np.cos(angle / 2), 

120 1.0, 

121 1.0, 

122 _np.cos(angle / 2), 

123 1.0, 

124 1.0, 

125 _np.cos(angle / 2), 

126 1.0, 

127 ] 

128 

129 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

130 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v) 

131 

132 surf.ctrlpts_size_u = p_size_u 

133 surf.ctrlpts_size_v = p_size_v 

134 surf.ctrlptsw = n_ctrlptsw 

135 

136 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

137 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

138 

139 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

140 

141 return surf 

142 

143 

144def create_nurbs_cylindrical_shell_sector( 

145 radius: float, angle: float, length: float, *, n_ele_u: int = 1, n_ele_v: int = 1 

146) -> _NURBS.Surface: 

147 """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]. 

149 

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. 

159 

160 Returns: 

161 geomdl.NURBS.Surface: A geomdl object that contains the surface information. 

162 """ 

163 

164 # Check the validity of the input values: 

165 if (angle >= _np.pi) or (angle < 0): 

166 raise ValueError( 

167 "The following algorithm for creating a cylindrical shell sector is only valid for 0 < angle <= pi." 

168 ) 

169 

170 # Create a NURBS surface instance 

171 surf = _NURBS.Surface() 

172 

173 # Set degrees 

174 surf.degree_u = 2 

175 surf.degree_v = 2 

176 

177 # Control points and set them to the surface 

178 p_size_u = 3 

179 p_size_v = 3 

180 

181 # Obtaining the control points 

182 cp_1 = [-radius * _np.sin(angle / 2), -length / 2, -radius * _np.cos(angle / 2)] 

183 cp_3 = [radius * _np.sin(angle / 2), -length / 2, -radius * _np.cos(angle / 2)] 

184 

185 # Calculating position of middle points. This is done by 

186 # obtaining the tangents on the points cp_1 and cp_3 and 

187 # calculating the intersection point between the tangents. 

188 m_1 = -_np.tan(-angle / 2) 

189 m_3 = -_np.tan(angle / 2) 

190 b_1 = cp_1[2] + m_1 * cp_1[0] 

191 b_3 = cp_3[2] + m_3 * cp_3[0] 

192 

193 inter_point_x = (b_3 - b_1) / (m_3 - m_1) 

194 inter_point_z = m_1 * inter_point_x + b_1 

195 

196 # The intersection point is assigned to the middle points cp_2, cp_5 and cp_8 

197 cp_2 = [inter_point_x, -length / 2, inter_point_z] 

198 

199 cp_4 = [-radius * _np.sin(angle / 2), 0.0, -radius * _np.cos(angle / 2)] 

200 cp_5 = [inter_point_x, 0.0, inter_point_z] 

201 cp_6 = [radius * _np.sin(angle / 2), 0.0, -radius * _np.cos(angle / 2)] 

202 

203 cp_7 = [ 

204 -radius * _np.sin(angle / 2), 

205 length / 2, 

206 -radius * _np.cos(angle / 2), 

207 ] 

208 cp_8 = [inter_point_x, length / 2, inter_point_z] 

209 cp_9 = [radius * _np.sin(angle / 2), length / 2, -radius * _np.cos(angle / 2)] 

210 

211 ctrlpts = [cp_1, cp_4, cp_7, cp_2, cp_5, cp_8, cp_3, cp_6, cp_9] 

212 

213 weights = [ 

214 1.0, 

215 1.0, 

216 1.0, 

217 _np.cos(angle / 2), 

218 _np.cos(angle / 2), 

219 _np.cos(angle / 2), 

220 1.0, 

221 1.0, 

222 1.0, 

223 ] 

224 

225 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

226 

227 surf.ctrlpts_size_u = p_size_u 

228 surf.ctrlpts_size_v = p_size_v 

229 surf.ctrlptsw = t_ctrlptsw 

230 

231 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

232 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

233 

234 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

235 

236 return surf 

237 

238 

239def create_nurbs_flat_plate_2d(width, length, *, n_ele_u=1, n_ele_v=1): 

240 """Creates a patch of a 2 dimensional flat plate. 

241 

242 Args 

243 ---- 

244 width: double 

245 dimension of the plate in the x-direction 

246 length: double 

247 dimension of the plate in the y-direction 

248 n_ele_u: int 

249 number of elements in the parametric u-direction 

250 n_ele_v: int 

251 number of elements in the parametric v-direction 

252 

253 

254 Return 

255 ---- 

256 surf: geomdl object 

257 geomdl object that contains the surface information 

258 """ 

259 

260 # Create a NURBS surface instance 

261 surf = _NURBS.Surface() 

262 

263 # Set degrees 

264 surf.degree_u = 2 

265 surf.degree_v = 2 

266 

267 # Control points and set them to the surface 

268 p_size_u = 3 

269 p_size_v = 3 

270 

271 ctrlpts = [ 

272 [-width / 2, -length / 2, 0.0], 

273 [0.0, -length / 2, 0.0], 

274 [width / 2, -length / 2, 0.0], 

275 [-width / 2, 0.0, 0.0], 

276 [0.0, 0.0, 0.0], 

277 [width / 2, 0.0, 0.0], 

278 [-width / 2, length / 2, 0.0], 

279 [0.0, length / 2, 0.0], 

280 [width / 2, length / 2, 0.0], 

281 ] 

282 

283 weights = [1.0] * 9 

284 

285 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

286 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v) 

287 

288 surf.ctrlpts_size_u = p_size_u 

289 surf.ctrlpts_size_v = p_size_v 

290 surf.ctrlptsw = n_ctrlptsw 

291 

292 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

293 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

294 

295 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

296 

297 return surf 

298 

299 

300def create_nurbs_sphere_surface(radius, n_ele_u=1, n_ele_v=1): 

301 """Generates a patch of a sphere as a NURBS surface. This function 

302 constructs a segment of a spherical surface using Non-Uniform Rational 

303 B-Splines (NURBS) based on the specified radius and the number of elements 

304 in the parametric u and v directions. 

305 

306 Args 

307 --- 

308 radius: double 

309 radius of the sphere 

310 n_ele_u: int 

311 number of elements in the parametric u-direction 

312 n_ele_v: int 

313 number of elements in the parametric v-direction 

314 

315 Return 

316 ---- 

317 surf: geomdl object 

318 geomdl object that contains the surface information 

319 """ 

320 

321 # Create a NURBS surface instance 

322 surf = _NURBS.Surface() 

323 

324 # Set degrees 

325 surf.degree_u = 2 

326 surf.degree_v = 2 

327 

328 # Control points and set them to the surface 

329 p_size_u = 3 

330 p_size_v = 3 

331 

332 dummy = 6.0 * ( 

333 5.0 / 12.0 

334 + 0.5 * _np.sqrt(2.0 / 3.0) 

335 - 0.25 / _np.sqrt(3.0) 

336 - 0.5 * _np.sqrt(2.0 / 3.0) * _np.sqrt(3.0) / 2.0 

337 ) 

338 

339 ctrlpts = [ 

340 [ 

341 2.0 * radius / _np.sqrt(6.0) * -_np.sin(1.0 / 4.0 * _np.pi), 

342 -radius / _np.sqrt(3.0), 

343 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi), 

344 ], 

345 [ 

346 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi) 

347 + radius 

348 * _np.sqrt(3.0) 

349 / (_np.sqrt(2.0) * 2.0) 

350 * -_np.sin(1.0 / 4.0 * _np.pi), 

351 -_np.sqrt(3.0) / 2 * radius, 

352 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi) 

353 + radius 

354 * _np.sqrt(3.0) 

355 / (_np.sqrt(2.0) * 2.0) 

356 * _np.sin(1.0 / 4.0 * _np.pi), 

357 ], 

358 [ 

359 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi), 

360 -radius / _np.sqrt(3.0), 

361 2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi), 

362 ], 

363 [ 

364 radius * _np.sqrt(6.0) / 2.0 * -_np.sin(1.0 / 4.0 * _np.pi), 

365 0.0, 

366 radius * _np.sqrt(6.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi), 

367 ], 

368 [ 

369 radius * dummy * _np.sqrt(2.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi) 

370 + radius * dummy * _np.sqrt(2.0) / 2.0 * -_np.sin(1.0 / 4.0 * _np.pi), 

371 0.0, 

372 radius * dummy * _np.sqrt(2.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi) 

373 + radius * dummy * _np.sqrt(2.0) / 2.0 * _np.sin(1.0 / 4.0 * _np.pi), 

374 ], 

375 [ 

376 radius * _np.sqrt(6.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi), 

377 0.0, 

378 radius * _np.sqrt(6.0) / 2.0 * _np.sin(1.0 / 4.0 * _np.pi), 

379 ], 

380 [ 

381 2.0 * radius / _np.sqrt(6.0) * -_np.sin(1.0 / 4.0 * _np.pi), 

382 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi), 

383 radius / _np.sqrt(3.0), 

384 ], 

385 [ 

386 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi) 

387 + radius 

388 * _np.sqrt(3.0) 

389 / (_np.sqrt(2.0) * 2.0) 

390 * -_np.sin(1.0 / 4.0 * _np.pi), 

391 _np.sqrt(3.0) / 2 * radius, 

392 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi) 

393 + radius 

394 * _np.sqrt(3.0) 

395 / (_np.sqrt(2.0) * 2.0) 

396 * _np.sin(1.0 / 4.0 * _np.pi), 

397 ], 

398 [ 

399 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi), 

400 radius / _np.sqrt(3.0), 

401 2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi), 

402 ], 

403 ] 

404 

405 weights = [ 

406 1.0, 

407 2.0 / _np.sqrt(6.0), 

408 1.0, 

409 2.0 / _np.sqrt(6.0), 

410 2.0 / 3.0, 

411 2.0 / _np.sqrt(6.0), 

412 1.0, 

413 2.0 / _np.sqrt(6.0), 

414 1.0, 

415 ] 

416 

417 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

418 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v) 

419 

420 surf.ctrlpts_size_u = p_size_u 

421 surf.ctrlpts_size_v = p_size_v 

422 surf.ctrlptsw = n_ctrlptsw 

423 

424 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

425 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

426 

427 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

428 

429 return surf 

430 

431 

432def create_nurbs_hemisphere_surface(radius, n_ele_uv=1): 

433 """Generates a hemisphere as a NURBS surface. This function constructs five 

434 segments that represent the surface of a hemisphere using Non-Uniform 

435 Rational B-Splines (NURBS) based on the specified radius and the number of 

436 elements in the parametric u and v directions. To secure the connectivity 

437 between surfaces, all surfaces must have the same parametric representation 

438 in any parametric direction. Therefore, the number of elements in u- and v- 

439 directions must be the same. 

440 

441 This function generates a list of five NURBS geomdl objects. 

442 

443 Args 

444 --- 

445 radius: double 

446 Radius of the hemisphere 

447 n_ele_uv: int 

448 Number of elements in the parametric u- and v- directions 

449 

450 Return 

451 ---- 

452 list: list(geomdl object) 

453 A list of geomdl objects that contains the surface information 

454 """ 

455 

456 # Create the first section of the hemisphere 

457 hemisphere_1 = create_nurbs_sphere_surface(radius, n_ele_u=1, n_ele_v=1) 

458 

459 # Create a temporary section by rotating and translating the 

460 # first section of the hemisphere 

461 temp_hemisphere = _operations.rotate(hemisphere_1, 90, axis=1) 

462 temp_hemisphere = _operations.translate( 

463 temp_hemisphere, 

464 (0, 0, -2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi) * 2), 

465 ) 

466 

467 # To create the hemisphere it is necessary to split the temporary 

468 # sphere section in two pieces. This split is done in u = 0.5. After 

469 # the split, the second surface is taken as it will be 

470 # adjacent to the first section of the sphere 

471 cut_section_sphere = _operations.split_surface_u(temp_hemisphere, param=0.5) 

472 hemisphere_2 = cut_section_sphere[1] 

473 

474 translation_component = radius * _np.sin(1.0 / 4.0 * _np.pi) * 2 

475 # Create the third section. Rotate and translate it accordingly 

476 hemisphere_3 = _operations.rotate(hemisphere_2, 90, axis=2) 

477 hemisphere_3 = _operations.translate(hemisphere_3, (translation_component, 0, 0)) 

478 

479 # Create the forth section. Rotate and translate it accordingly 

480 hemisphere_4 = _operations.rotate(hemisphere_3, 90, axis=2) 

481 hemisphere_4 = _operations.translate(hemisphere_4, (0, translation_component, 0)) 

482 

483 # Create the fifth section. Rotate and translate it accordingly 

484 hemisphere_5 = _operations.rotate(hemisphere_4, 90, axis=2) 

485 hemisphere_5 = _operations.translate(hemisphere_5, (-translation_component, 0, 0)) 

486 

487 patches = [hemisphere_1, hemisphere_2, hemisphere_3, hemisphere_4, hemisphere_5] 

488 for patch in patches: 

489 do_uniform_knot_refinement_surface(patch, n_ele_uv, n_ele_uv) 

490 return patches 

491 

492 

493def create_nurbs_torus_surface(radius_torus, radius_circle, *, n_ele_u=1, n_ele_v=1): 

494 """Creates a NURBS patch for the construction of a ring torus (outer 

495 surface). A ring torus is a surface of revolution generated by revolving a 

496 circle in a 3-dimensional space around an axis of revolution (axis z). The 

497 center of the torus is located at the coordinates [0, 0, 0]. 

498 

499 This function constructs the surface of a ring torus in the following manner: 

500 - The quarter of the torus is made by 4 patches 

501 - These patches are rotated three times around the z-axis by 90°, e.g. theta = 90°, 180°, 270°, 

502 leading to a total of 16 NURBS patches. 

503 

504 The number of elements in this function are given for a single patch, where: 

505 - n_ele_u is the number of elements in the "radius_torus" direction 

506 - n_ele_v is the number of elements in the "radius_circle" direction 

507 

508 This function generates a list of 16 NURBS geomdl objects. 

509 

510 Args 

511 ---- 

512 radius_torus: double 

513 distance from the axis of revolution (axis z) to the center of the revolving circle 

514 radius_circle: double 

515 radius of the circle that revolves around the axis of revolution 

516 n_ele_u: int 

517 number of elements in the parametric u-direction 

518 n_ele_v: int 

519 number of elements in the parametric v-direction 

520 """ 

521 

522 # Create four NURBS surface instances. These are the base patches of the torus. 

523 surf_1 = _NURBS.Surface() 

524 surf_2 = _NURBS.Surface() 

525 surf_3 = _NURBS.Surface() 

526 surf_4 = _NURBS.Surface() 

527 base_surfs = [surf_1, surf_2, surf_3, surf_4] 

528 

529 # Define control points and set them to the surfaces 

530 p_size_u = 3 

531 p_size_v = 3 

532 

533 dummy_surf1 = radius_torus + radius_circle 

534 dummy_surf2 = radius_torus - radius_circle 

535 

536 ctrlpts_surf1 = [ 

537 [dummy_surf1, 0.0, 0.0], 

538 [dummy_surf1, 0.0, radius_circle], 

539 [radius_torus, 0.0, radius_circle], 

540 [dummy_surf1, dummy_surf1, 0.0], 

541 [dummy_surf1, dummy_surf1, radius_circle], 

542 [radius_torus, radius_torus, radius_circle], 

543 [0.0, dummy_surf1, 0.0], 

544 [0.0, dummy_surf1, radius_circle], 

545 [0.0, radius_torus, radius_circle], 

546 ] 

547 

548 ctrlpts_surf2 = [ 

549 [dummy_surf1, 0.0, 0.0], 

550 [dummy_surf1, 0.0, -radius_circle], 

551 [radius_torus, 0.0, -radius_circle], 

552 [dummy_surf1, dummy_surf1, 0.0], 

553 [dummy_surf1, dummy_surf1, -radius_circle], 

554 [radius_torus, radius_torus, -radius_circle], 

555 [0.0, dummy_surf1, 0.0], 

556 [0.0, dummy_surf1, -radius_circle], 

557 [0.0, radius_torus, -radius_circle], 

558 ] 

559 

560 ctrlpts_surf3 = [ 

561 [radius_torus, 0.0, -radius_circle], 

562 [dummy_surf2, 0.0, -radius_circle], 

563 [dummy_surf2, 0.0, 0.0], 

564 [radius_torus, radius_torus, -radius_circle], 

565 [dummy_surf2, dummy_surf2, -radius_circle], 

566 [dummy_surf2, dummy_surf2, 0.0], 

567 [0.0, radius_torus, -radius_circle], 

568 [0.0, dummy_surf2, -radius_circle], 

569 [0.0, dummy_surf2, 0.0], 

570 ] 

571 

572 ctrlpts_surf4 = [ 

573 [0.0, dummy_surf2, 0.0], 

574 [0.0, dummy_surf2, radius_circle], 

575 [0.0, radius_torus, radius_circle], 

576 [dummy_surf2, dummy_surf2, 0.0], 

577 [dummy_surf2, dummy_surf2, radius_circle], 

578 [radius_torus, radius_torus, radius_circle], 

579 [dummy_surf2, 0.0, 0.0], 

580 [dummy_surf2, 0.0, radius_circle], 

581 [radius_torus, 0.0, radius_circle], 

582 ] 

583 

584 weights = [ 

585 1.0, 

586 _np.sqrt(2) / 2, 

587 1.0, 

588 _np.sqrt(2) / 2, 

589 0.5, 

590 _np.sqrt(2) / 2, 

591 1.0, 

592 _np.sqrt(2) / 2, 

593 1.0, 

594 ] 

595 

596 t_ctrlptsw1 = _compat.combine_ctrlpts_weights(ctrlpts_surf1, weights) 

597 t_ctrlptsw2 = _compat.combine_ctrlpts_weights(ctrlpts_surf2, weights) 

598 t_ctrlptsw3 = _compat.combine_ctrlpts_weights(ctrlpts_surf3, weights) 

599 t_ctrlptsw4 = _compat.combine_ctrlpts_weights(ctrlpts_surf4, weights) 

600 

601 t_ctrlpts_surfs = [t_ctrlptsw1, t_ctrlptsw2, t_ctrlptsw3, t_ctrlptsw4] 

602 

603 for surf, t_ctrlpts in zip(base_surfs, t_ctrlpts_surfs): 

604 # Set degrees 

605 surf.degree_u = 2 

606 surf.degree_v = 2 

607 

608 surf.ctrlpts_size_u = p_size_u 

609 surf.ctrlpts_size_v = p_size_v 

610 

611 # Set control points 

612 surf.ctrlptsw = t_ctrlpts 

613 

614 # Set knot vectors 

615 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

616 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

617 

618 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

619 

620 # Define the rotations and translations to rotate the base patches and form a complete torus 

621 tmp_trans = [ 

622 radius_torus + radius_circle, 

623 radius_torus + radius_circle, 

624 radius_torus, 

625 radius_torus - radius_circle, 

626 ] 

627 

628 transform_surf1 = [ 

629 [(-tmp_trans[0], tmp_trans[0], 0), 90, 2], 

630 [(-2 * tmp_trans[0], 0, 0), 180, 2], 

631 [(-tmp_trans[0], -tmp_trans[0], 0), 270, 2], 

632 ] 

633 

634 transform_surf2 = [ 

635 [(-tmp_trans[1], tmp_trans[1], 0), 90, 2], 

636 [(-2 * tmp_trans[1], 0, 0), 180, 2], 

637 [(-tmp_trans[1], -tmp_trans[1], 0), 270, 2], 

638 ] 

639 

640 transform_surf3 = [ 

641 [(-tmp_trans[2], tmp_trans[2], 0), 90, 2], 

642 [(-2 * tmp_trans[2], 0, 0), 180, 2], 

643 [(-tmp_trans[2], -tmp_trans[2], 0), 270, 2], 

644 ] 

645 

646 transform_surf4 = [ 

647 [(-tmp_trans[3], -tmp_trans[3], 0), 90, 2], 

648 [(0, -2 * tmp_trans[3], 0), 180, 2], 

649 [(tmp_trans[3], -tmp_trans[3], 0), 270, 2], 

650 ] 

651 

652 # Rotate base patches and store them 

653 surfaces_torus = [surf_1, surf_2, surf_3, surf_4] 

654 for transform1, transform2, transform3, transform4 in zip( 

655 transform_surf1, transform_surf2, transform_surf3, transform_surf4 

656 ): 

657 new_surf1 = _operations.translate(surf_1, transform1[0]) 

658 new_surf1 = _operations.rotate(new_surf1, transform1[1], axis=transform1[2]) 

659 surfaces_torus.append(new_surf1) 

660 

661 new_surf2 = _operations.translate(surf_2, transform2[0]) 

662 new_surf2 = _operations.rotate(new_surf2, transform2[1], axis=transform2[2]) 

663 surfaces_torus.append(new_surf2) 

664 

665 new_surf3 = _operations.translate(surf_3, transform3[0]) 

666 new_surf3 = _operations.rotate(new_surf3, transform3[1], axis=transform3[2]) 

667 surfaces_torus.append(new_surf3) 

668 

669 new_surf4 = _operations.translate(surf_4, transform4[0]) 

670 new_surf4 = _operations.rotate(new_surf4, transform4[1], axis=transform4[2]) 

671 surfaces_torus.append(new_surf4) 

672 

673 return surfaces_torus 

674 

675 

676def create_nurbs_brick(width, length, height, *, n_ele_u=1, n_ele_v=1, n_ele_w=1): 

677 """Creates a patch of a 3 dimensional brick. 

678 

679 Args 

680 ---- 

681 width: double 

682 dimension of the plate in the x-direction 

683 length: double 

684 dimension of the plate in the y-direction 

685 height: double 

686 dimension of the plate in the z-direction 

687 n_ele_u: int 

688 number of elements in the parametric u-direction 

689 n_ele_v: int 

690 number of elements in the parametric v-direction 

691 n_ele_w: int 

692 number of elements in the parametric w-direction 

693 

694 

695 Return 

696 ---- 

697 vol: geomdl object 

698 geomdl object that contains the volume information 

699 """ 

700 

701 # Create a NURBS volume instance 

702 vol = _NURBS.Volume() 

703 

704 # Set degrees 

705 vol.degree_u = 2 

706 vol.degree_v = 2 

707 vol.degree_w = 2 

708 

709 # Create control points and set them to the volume 

710 cp_size_u = 3 

711 cp_size_v = 3 

712 cp_size_w = 3 

713 

714 ctrlpts = [ 

715 [-width / 2, -length / 2, -height / 2], 

716 [-width / 2, 0.0, -height / 2], 

717 [-width / 2, length / 2, -height / 2], 

718 [0.0, -length / 2, -height / 2], 

719 [0.0, 0.0, -height / 2], 

720 [0.0, length / 2, -height / 2], 

721 [width / 2, -length / 2, -height / 2], 

722 [width / 2, 0.0, -height / 2], 

723 [width / 2, length / 2, -height / 2], 

724 [-width / 2, -length / 2, 0.0], 

725 [-width / 2, 0.0, 0.0], 

726 [-width / 2, length / 2, 0.0], 

727 [0.0, -length / 2, 0.0], 

728 [0.0, 0.0, 0.0], 

729 [0.0, length / 2, 0.0], 

730 [width / 2, -length / 2, 0.0], 

731 [width / 2, 0.0, 0.0], 

732 [width / 2, length / 2, 0.0], 

733 [-width / 2, -length / 2, height / 2], 

734 [-width / 2, 0.0, height / 2], 

735 [-width / 2, length / 2, height / 2], 

736 [0.0, -length / 2, height / 2], 

737 [0.0, 0.0, height / 2], 

738 [0.0, length / 2, height / 2], 

739 [width / 2, -length / 2, height / 2], 

740 [width / 2, 0.0, height / 2], 

741 [width / 2, length / 2, height / 2], 

742 ] 

743 

744 weights = [1.0] * 27 

745 

746 vol.ctrlpts_size_u = cp_size_u 

747 vol.ctrlpts_size_v = cp_size_v 

748 vol.ctrlpts_size_w = cp_size_w 

749 

750 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

751 

752 vol.set_ctrlpts(t_ctrlptsw, cp_size_u, cp_size_v, cp_size_w) 

753 

754 vol.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

755 vol.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

756 vol.knotvector_w = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

757 

758 do_uniform_knot_refinement_volume(vol, n_ele_u, n_ele_v, n_ele_w) 

759 

760 return vol 

761 

762 

763def do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v): 

764 """This function does an uniform knot refinement in the u- and v- 

765 direction. 

766 

767 Args 

768 ---- 

769 surf: geomdl object 

770 geomdl object that contains the surface information 

771 n_ele_u: int 

772 number of elements in the parametric u-direction 

773 n_ele_v: int 

774 number of elements in the parametric v-direction 

775 

776 Return 

777 ---- 

778 surf: geomdl object 

779 """ 

780 

781 size_of_knotvector_u = 1 / n_ele_u 

782 size_of_knotvector_v = 1 / n_ele_v 

783 

784 for i in range(1, n_ele_u): 

785 _operations.insert_knot(surf, [size_of_knotvector_u * i, None], [1, 0]) 

786 for j in range(1, n_ele_v): 

787 _operations.insert_knot(surf, [None, size_of_knotvector_v * j], [0, 1]) 

788 

789 

790def do_uniform_knot_refinement_volume(vol, n_ele_u, n_ele_v, n_ele_w): 

791 """This function does an uniform knot refinement in the u-, v- and w- 

792 direction. 

793 

794 Args 

795 ---- 

796 vol: geomdl object 

797 geomdl object that contains the volume information 

798 n_ele_u: int 

799 number of elements in the parametric u-direction 

800 n_ele_v: int 

801 number of elements in the parametric v-direction 

802 n_ele_w: int 

803 number of elements in the parametric w-direction 

804 

805 Return 

806 ---- 

807 vol: geomdl object 

808 """ 

809 

810 size_of_knotvector_u = 1 / n_ele_u 

811 size_of_knotvector_v = 1 / n_ele_v 

812 size_of_knotvector_w = 1 / n_ele_w 

813 

814 for i in range(1, n_ele_u): 

815 _operations.insert_knot(vol, [size_of_knotvector_u * i, None, None], [1, 0, 0]) 

816 for j in range(1, n_ele_v): 

817 _operations.insert_knot(vol, [None, size_of_knotvector_v * j, None], [0, 1, 0]) 

818 for k in range(1, n_ele_w): 

819 _operations.insert_knot(vol, [None, None, size_of_knotvector_w * k], [0, 0, 1])