1
1
"""
2
2
Utilities for reading and writing parameters files to perform the desired geometrical morphing.
3
3
"""
4
- import os
5
4
import ConfigParser
5
+ import os
6
+
6
7
import numpy as np
8
+ from OCC .BRepBndLib import brepbndlib_Add
9
+ from OCC .BRepMesh import BRepMesh_IncrementalMesh
10
+ from OCC .Bnd import Bnd_Box
11
+
7
12
import pygem .affine as at
8
13
9
14
@@ -51,19 +56,28 @@ class FFDParameters(object):
51
56
:cvar numpy.ndarray position_vertex_2: position of the third vertex of the FFD bounding box.
52
57
:cvar numpy.ndarray position_vertex_3: position of the fourth vertex of the FFD bounding box.
53
58
54
- :Example:
59
+ :Example: from file
55
60
56
61
>>> import pygem.params as ffdp
57
62
58
63
>>> # Reading an existing file
59
64
>>> params1 = ffdp.FFDParameters()
60
65
>>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm')
61
66
62
- >>> # Creating a defaul paramters file with the right dimensions (if the file does not exists
67
+ >>> # Creating a default parameters file with the right dimensions (if the file does not exists
63
68
>>> # it is created with that name). So it is possible to manually edit it and read it again.
64
69
>>> params2 = ffdp.FFDParameters(n_control_points=[2, 3, 2])
65
70
>>> params2.read_parameters(filename='parameters_test.prm')
66
-
71
+
72
+ >>> # Creating bounding box of the given shape
73
+ >>> from OCC.IGESControl import IGESControl_Reader
74
+ >>> params3 = ffdp.FFDParameters()
75
+ >>> reader = IGESControl_Reader()
76
+ >>> reader.ReadFile('tests/test_datasets/test_pipe.igs')
77
+ >>> reader.TransferRoots()
78
+ >>> shape = reader.Shape()
79
+ >>> params3.build_bounding_box(shape)
80
+
67
81
.. note::
68
82
Four vertex (non coplanar) are sufficient to uniquely identify a parallelepiped.
69
83
If the four vertex are coplanar, an assert is thrown when affine_points_fit is used.
@@ -116,7 +130,7 @@ def read_parameters(self, filename='parameters.prm'):
116
130
if not os .path .isfile (filename ):
117
131
self .write_parameters (filename )
118
132
return
119
-
133
+
120
134
config = ConfigParser .RawConfigParser ()
121
135
config .read (filename )
122
136
@@ -175,12 +189,12 @@ def read_parameters(self, filename='parameters.prm'):
175
189
self .psi_mapping = np .diag ([1. / self .lenght_box_x , 1. / self .lenght_box_y , 1. / self .lenght_box_z ])
176
190
self .inv_psi_mapping = np .diag ([self .lenght_box_x , self .lenght_box_y , self .lenght_box_z ])
177
191
178
-
192
+
179
193
def write_parameters (self , filename = 'parameters.prm' ):
180
194
"""
181
195
This method writes a parameters file (.prm) called `filename` and fills it with all
182
196
the parameters class members.
183
-
197
+
184
198
:param string filename: parameters file to be written out.
185
199
"""
186
200
if not isinstance (filename , basestring ):
@@ -230,7 +244,7 @@ def write_parameters(self, filename='parameters.prm'):
230
244
output_file .write ('# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n ' )
231
245
output_file .write ('# | 0 | 1 | 0 | -2.1 |\n ' )
232
246
output_file .write ('# | 0 | 0 | 1 | 3.4 |\n ' )
233
-
247
+
234
248
output_file .write ('\n # parameter x collects the displacements along x, normalized with the box lenght x.' )
235
249
output_file .write ('\n parameter x:' )
236
250
offset = 1
@@ -294,6 +308,104 @@ def print_info(self):
294
308
print '\n position_vertex_3 ='
295
309
print self .position_vertex_3
296
310
311
+ def build_bounding_box (self , shape , tol = 1e-6 , triangulate = False , triangulate_tol = 1e-1 ):
312
+ """
313
+ Builds a bounding box around the given shape. ALl parameters (with the exception of array_mu_x/y/z)
314
+ are set to match the computed box.
315
+
316
+ :param TopoDS_Shape shape: or a subclass such as TopoDS_Face
317
+ the shape to compute the bounding box from
318
+ :param float tol: tolerance of the computed bounding box
319
+ :param bool triangulate: Should shape be triangulated before the boudning box is created.
320
+
321
+ If ``True`` only the dimensions of the bb will take into account every part of the shape (also not *visible*)
322
+
323
+ If ``False`` only the *visible* part is taken into account
324
+
325
+ *Explanation:* every UV-Surface has to be rectangular. When a solid is created surfaces are trimmed.
326
+ the trimmed part, however, is still saved inside a file. It is just *invisible* when drawn in a program
327
+
328
+ :param float triangulate_tol: tolerance of triangulation (size of created triangles)
329
+ """
330
+ min_xyz , max_xyz = self ._calculate_bb_dimension (shape , tol , triangulate , triangulate_tol )
331
+ self .origin_box = min_xyz
332
+ self ._set_box_dimensions (min_xyz , max_xyz )
333
+ self ._set_position_of_vertices ()
334
+ self ._set_mapping ()
335
+ self ._set_transformation_params_to_zero ()
336
+
337
+ def _set_box_dimensions (self , min_xyz , max_xyz ):
338
+ """
339
+ Dimensions of the cage are set as distance from the origin (minimum) of the cage to
340
+ the maximal point in each dimension.
341
+
342
+ :param iterable min_xyz: three values representing the minimal values of the bounding box in XYZ respectively
343
+ :param iterable max_xyz: three values representing the maximal values of the bounding box in XYZ respectively
344
+ """
345
+ dims = [max_xyz [i ] - min_xyz [i ] for i in range (3 )]
346
+ self .lenght_box_x = dims [0 ]
347
+ self .lenght_box_y = dims [1 ]
348
+ self .lenght_box_z = dims [2 ]
349
+
350
+ def _set_position_of_vertices (self ):
351
+ """
352
+ Vertices of the control box around the object are set in this method.
353
+ Four vertices (non coplanar) are sufficient to uniquely identify a parallelepiped -- the
354
+ second half of the box is created as a mirror reflection of the first four vertices.
355
+ """
356
+ origin_array = np .array (self .origin_box )
357
+ dim = [self .lenght_box_x , self .lenght_box_y , self .lenght_box_z ]
358
+ self .position_vertex_0 = origin_array
359
+ self .position_vertex_1 = origin_array + np .array ([dim [0 ], .0 , .0 ])
360
+ self .position_vertex_2 = origin_array + np .array ([.0 , dim [1 ], .0 ])
361
+ self .position_vertex_3 = origin_array + np .array ([.0 , .0 , dim [2 ]])
362
+
363
+ def _set_mapping (self ):
364
+ """
365
+ This method sets mapping from physcial domain to the reference domain (``psi_mapping``)
366
+ as well as inverse mapping (``inv_psi_mapping``).
367
+ """
368
+ dim = [self .lenght_box_x , self .lenght_box_y , self .lenght_box_z ]
369
+ self .psi_mapping = np .diag ([1. / dim [i ] for i in range (3 )])
370
+ self .inv_psi_mapping = np .diag (dim )
371
+
372
+ def _set_transformation_params_to_zero (self ):
373
+ """
374
+ Sets transfomration parameters (``array_mu_x, array_mu_y, array_mu_z``) to arrays of zeros
375
+ (``numpy.zeros``). The shape of arrays corresponds to the number of control points in each dimension.
376
+ """
377
+ ctrl_pnts = self .n_control_points
378
+ self .array_mu_x = np .zeros (ctrl_pnts )
379
+ self .array_mu_y = np .zeros (ctrl_pnts )
380
+ self .array_mu_z = np .zeros (ctrl_pnts )
381
+
382
+ @staticmethod
383
+ def _calculate_bb_dimension (shape , tol = 1e-6 , triangulate = False , triangulate_tol = 1e-1 ):
384
+ """ Calculate dimensions (minima and maxima) of a box bounding the
385
+
386
+ :param TopoDS_Shape shape: or a subclass such as TopoDS_Face
387
+ the shape to compute the bounding box from
388
+ :param float tol: tolerance of the computed bounding box
389
+ :param bool triangulate: Should shape be triangulated before the boudning box is created.
390
+
391
+ If ``True`` only the dimensions of the bb will take into account every part of the shape (also not *visible*)
392
+
393
+ If ``False`` only the *visible* part is taken into account
394
+
395
+ \*See :meth:`~params.FFDParameters.build_bounding_box`
396
+ :param float triangulate_tol: tolerance of triangulation (size of created triangles)
397
+ :return: coordinates of minima and maxima along XYZ
398
+ :rtype: tuple
399
+ """
400
+ bbox = Bnd_Box ()
401
+ bbox .SetGap (tol )
402
+ if triangulate :
403
+ BRepMesh_IncrementalMesh (shape , triangulate_tol )
404
+ brepbndlib_Add (shape , bbox , triangulate )
405
+ xmin , ymin , zmin , xmax , ymax , zmax = bbox .Get ()
406
+ xyz_min = np .array ([xmin , ymin , zmin ])
407
+ xyz_max = np .array ([xmax , ymax , zmax ])
408
+ return xyz_min , xyz_max
297
409
298
410
299
411
class RBFParameters (object ):
@@ -346,7 +458,7 @@ def read_parameters(self, filename='parameters_rbf.prm'):
346
458
0. , 1. , 1. , 1. , 0. , 1. , 1. , 1. , 0. , 1. , 1. , 1. ]).reshape ((8 , 3 ))
347
459
self .write_parameters (filename )
348
460
return
349
-
461
+
350
462
config = ConfigParser .RawConfigParser ()
351
463
config .read (filename )
352
464
@@ -378,7 +490,7 @@ def write_parameters(self, filename='parameters_rbf.prm'):
378
490
"""
379
491
This method writes a parameters file (.prm) called `filename` and fills it with all
380
492
the parameters class members. Default value is parameters_rbf.prm.
381
-
493
+
382
494
:param string filename: parameters file to be written out.
383
495
"""
384
496
if not isinstance (filename , basestring ):
@@ -402,7 +514,7 @@ def write_parameters(self, filename='parameters_rbf.prm'):
402
514
403
515
output_file .write ('\n \n [Control points]\n ' )
404
516
output_file .write ('# This section describes the RBF control points.\n ' )
405
-
517
+
406
518
output_file .write ('\n # original control points collects the coordinates of the interpolation ' + \
407
519
'control points before the deformation.\n ' )
408
520
output_file .write ('original control points:' )
@@ -435,4 +547,3 @@ def print_info(self):
435
547
print self .original_control_points
436
548
print '\n deformed_control_points ='
437
549
print self .deformed_control_points
438
-
0 commit comments