Salome HOME
Merge from V6_main_20120808 08Aug12
[modules/geom.git] / src / GEOM_PY / structelem / __init__.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 #
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21 """
22 This package is used to create and visualize structural elements. It contains
23 three modules:
24
25 * This module :mod:`salome.geom.structelem` defines the main classes
26   :class:`StructuralElement` and :class:`StructuralElementManager` that can be
27   directly used to build structural elements.
28 * The module :mod:`salome.geom.structelem.parts` defines the classes corresponding to
29   the different parts (beams, grids, etc.) that make up a structural element.
30   It is used to build the geometric shapes in the structural element.
31 * The module :mod:`salome.geom.structelem.orientation` defines the classes that are
32   used to compute the orientation of the structural element parts and to build
33   the corresponding markers.
34
35 A structural element is a set of geometric shapes (beams, grids, etc.) that
36 are built semi-automatically along a set of geometric primitives (edges for
37 instance). They are visualized with the same color as their base primitives in
38 the geom viewer.
39
40 Structural elements are generally created by the
41 :class:`StructuralElementManager` class, from a list of commands describing
42 the element to create.
43
44 Example::
45
46     commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
47                    ('VisuBarreCercle',
48                     {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
49                   ]
50
51     structElemManager = StructuralElementManager()
52     elem = structElemManager.createElement(commandList)
53     elem.display()
54     salome.sg.updateObjBrowser(True)
55
56 """
57
58 import types
59
60 import salome
61
62 from salome.kernel.logger import Logger
63 from salome.kernel import termcolor
64 logger = Logger("salome.geom.structelem", color = termcolor.RED)
65 from salome.kernel.studyedit import getStudyEditor
66
67 __all__ = ["parts", "orientation"]
68
69 from salome.geom.structelem import parts
70 from salome.geom.structelem.parts import InvalidParameterError
71
72 class StructuralElementManager:
73     """
74     This class manages the structural elements in the study. It is used to
75     create a new structural element from a list of commands. The parameter
76     `studyId` defines the ID of the study in which the manager will create
77     structural elements. If it is :const:`None` or not specified, it will use
78     the ID of the current study as defined by
79     :func:`salome.kernel.studyedit.getActiveStudyId` function.
80     """
81     def __init__(self, studyId = None):
82         self._studyEditor = getStudyEditor(studyId)
83
84     def createElement(self, commandList):
85         """
86         Create a structural element from the list of commands `commandList`.
87         Each command in this list represent a part of the structural element,
88         that is a specific kind of shape (circular beam, grid, etc.)
89         associated with one or several geometrical primitives. A command must
90         be a tuple. The first element is the structural element part class
91         name or alias name. The second element is a dictionary containing the
92         parameters describing the part. Valid class names are all the classes
93         defined in the module :mod:`~salome.geom.structelem.parts` and inheriting
94         class :class:`~parts.StructuralElementPart`. There are also several
95         aliases for backward compatibility. Here is the complete list:
96         
97         * :class:`~parts.GeneralBeam`
98         * :class:`~parts.CircularBeam`
99         * :class:`~parts.RectangularBeam`
100         * :class:`~parts.ThickShell`
101         * :class:`~parts.Grid`
102
103         * :func:`~parts.VisuPoutreGenerale` (alias for
104           :class:`~parts.GeneralBeam`)
105         * :func:`~parts.VisuPoutreCercle` (alias for
106           :class:`~parts.CircularBeam`)
107         * :func:`~parts.VisuPoutreRectangle` (alias for
108           :class:`~parts.RectangularBeam`)
109         * :func:`~parts.VisuBarreGenerale` (alias for
110           :class:`~parts.GeneralBeam`)
111         * :func:`~parts.VisuBarreRectangle` (alias for
112           :class:`~parts.RectangularBeam`)
113         * :func:`~parts.VisuBarreCercle` (alias for
114           :class:`~parts.CircularBeam`)
115         * :func:`~parts.VisuCable` (alias for :class:`~parts.CircularBeam`)
116         * :func:`~parts.VisuCoque` (alias for :class:`~parts.ThickShell`)
117         * :func:`~parts.VisuGrille` (alias for :class:`~parts.Grid`)
118         
119         * ``Orientation``: This identifier is used to specify the orientation
120           of one or several 1D structural element parts (i.e. beams). The
121           parameters are described in class
122           :class:`~orientation.Orientation1D`.
123
124         The valid parameters in the dictionary depend on the type of the
125         structural element part, and are detailed in the documentation of
126         the corresponding class. The only parameter that is common to all the
127         classes is "MeshGroups" (that can also be named "Group_Maille"). It
128         defines the name of the geometrical object(s) in the study that will
129         be used as primitives to build the structural element part. This
130         parameter can be either a list of strings or a single string with
131         comma separated names.
132         """
133         logger.debug("StructuralElementManager.createElement: START")
134         logger.debug("Command list: %s" % commandList)
135
136         element = StructuralElement(self._studyEditor.studyId)
137         orientationCmdList = []
138         for command in commandList:
139             (parttype, parameters) = command
140             if parttype == "Orientation":
141                 orientationCmdList += [command]
142             elif parttype not in dir(parts):
143                 logger.warning('Invalid structural element part name "%s"'
144                                ' in command %s, this command will be '
145                                'ignored.' % (parttype, command))
146             else:
147                 (meshGroupList, newparams) = self._extractMeshGroups(command)
148                 for meshGroup in meshGroupList:
149                     # Get the geometrical primitive object
150                     groupSObj = self._studyEditor.study.FindObject(meshGroup)
151                     groupGeomObj = None
152                     if groupSObj is not None:
153                         groupGeomObj = \
154                                 self._studyEditor.getOrLoadObject(groupSObj)
155                     if groupGeomObj is None:
156                         logger.error("Can't get geom object corresponding to "
157                                      'mesh group "%s", structural element '
158                                      "part %s will not be built." %
159                                      (groupName, part))
160                         continue
161                     
162                     # Create the part
163                     try:
164                         part = parts.__dict__[parttype](
165                                         self._studyEditor.studyId, meshGroup,
166                                         groupGeomObj, newparams)
167                         element.addPart(part)
168                     except InvalidParameterError, e:
169                         logger.error("Invalid parameter error: %s" % e)
170                         raise
171                     except:
172                         logger.exception("Can't create structural element"
173                                          " part with command %s." %
174                                          str(command))
175
176         # Orientations are parsed after the parts because they must be
177         # associated with existing parts.
178         for command in orientationCmdList:
179             (parttype, parameters) = command
180             (meshGroupList, orientParams) = self._extractMeshGroups(command)
181             for meshGroup in meshGroupList:
182                 element.addOrientation(meshGroup, orientParams)
183
184         element.build()
185         logger.debug("StructuralElementManager.createElement: END")
186         return element
187     
188     def _extractMeshGroups(self, command):
189         """
190         This method extracts the names of the mesh groups (i.e. the
191         geometrical objects used to build the structural element part) in the
192         command in parameter. It returns a tuple containing the mesh groups as
193         a list of strings and the other parameters of the command as a new
194         dictionary.
195         """
196         (parttype, parameters) = command
197         newparams = parameters.copy()
198         groupMailleParam = newparams.pop("Group_Maille", None)
199         meshGroupParam = newparams.pop("MeshGroups", None)
200         if groupMailleParam is None and meshGroupParam is None:
201             logger.warning("No mesh group specified in command %s, this "
202                            "command will be ignored." % command)
203             return ([], newparams)
204         elif groupMailleParam is not None and meshGroupParam is not None:
205             logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
206                            ' command %s, only "MeshGroups" will be used.' %
207                            command)
208         elif groupMailleParam is not None and meshGroupParam is None:
209             meshGroupParam = groupMailleParam
210         
211         meshGroupList = []
212         if type(meshGroupParam) == types.StringType:
213             meshGroupList = self._getMeshGroupListFromString(meshGroupParam)
214         else:
215             for item in meshGroupParam:
216                 meshGroupList += self._getMeshGroupListFromString(item)
217         
218         if len(meshGroupList) == 0:
219             logger.warning("Mesh group list is empty in command %s, this "
220                            "command will be ignored." % command)
221
222         return (meshGroupList, newparams)
223     
224     def _getMeshGroupListFromString(self, meshString):
225         """
226         This method splits the string in parameter to extract comma separated
227         names. Those names are returned as a list of strings.
228         """
229         meshGroupList = []
230         list = meshString.split(",")
231         for item in list:
232             strippedItem = item.strip()
233             if len(strippedItem) > 0:
234                 meshGroupList.append(strippedItem)
235         return meshGroupList
236
237
238 class StructuralElement:
239     """
240     This class represents a structural element, i.e. a set of geometrical
241     objects built along geometrical primitives. The parameter `studyId`
242     defines the ID of the study that will contain the structural element. If
243     it is :const:`None` or not specified, the constructor will use the ID of
244     the active study as defined by :func:`salome.kernel.studyedit.getActiveStudyId`
245     function. Structural elements are normally created by the class
246     :class:`StructuralElementManager`, so this class should not be
247     instantiated directly in the general case.
248     """
249     _counter = 1
250     _mainFolderTag = 14725
251
252     def __init__(self, studyId = None):
253         # _parts is the dictionary mapping group name to structural element
254         # part. _shapeDict is the dictionary mapping SubShapeID objects to
255         # structural element parts. Both are used to avoid duplicate shapes
256         # in structural elements.
257         self._parts = {}
258         self._shapeDict = {}
259         self._id = StructuralElement._counter
260         StructuralElement._counter += 1
261         self._studyEditor = getStudyEditor(studyId)
262         logger.debug("Creating structural element in study %s" %
263                      self._studyEditor.studyId)
264         self._SObject = None
265
266     def _getSObject(self):
267         """
268         Find or create the study object corresponding to the structural
269         element. This object is named "SE_N" where N is a numerical ID. 
270         """
271         if self._SObject is None:
272             geomComponent = self._studyEditor.study.FindComponent("GEOM")
273             mainFolder = self._studyEditor.setItemAtTag(geomComponent,
274                                             StructuralElement._mainFolderTag,
275                                             name = "Structural Elements")
276             self._SObject = self._studyEditor.findOrCreateItem(mainFolder,
277                                             name = "SE_" + str(self._id))
278         return self._SObject
279
280     def addPart(self, newpart):
281         """
282         Add a part to the structural element.
283
284         :type  newpart: :class:`~parts.StructuralElementPart`
285         :param newpart: the part to add to the structural element.
286
287         """
288         newshapes = newpart.baseShapesSet
289
290         # Check duplicate groups
291         if self._parts.has_key(newpart.groupName):
292             logger.warning('Mesh group "%s" is used several times in the '
293                            'structural element. Only the last definition '
294                            'will be used.' % newpart.groupName)
295         else:
296             # Check duplicate shapes
297             intersect = newshapes.intersection(self._shapeDict.keys())
298             while len(intersect) > 0:
299                 shape, = intersect
300                 oldpartwithshape = self._shapeDict[shape]
301                 oldpartshapes = oldpartwithshape.baseShapesSet
302                 intersectwitholdpart = intersect.intersection(oldpartshapes)
303                 logger.warning('Some shapes are common to groups "%s" and '
304                                '"%s". For those, the parameters defined for '
305                                '"%s" will be used.' %
306                                (oldpartwithshape.groupName, newpart.groupName,
307                                 newpart.groupName))
308                 oldpartwithshape.baseShapesSet = \
309                                 oldpartshapes.difference(intersectwitholdpart)
310                 intersect = intersect.difference(intersectwitholdpart)
311
312         # Finally add the new part in the structural element
313         self._parts[newpart.groupName] = newpart
314         for shape in newshapes:
315             self._shapeDict[shape] = newpart
316
317     def addOrientation(self, meshGroup, orientParams):
318         """
319         Add orientation information to a part in the structural element. This
320         information will be used to build the corresponding markers.
321
322         :type  meshGroup: string
323         :param meshGroup: the name of a geometrical primitive. The orientation
324                           information will apply to the structural element
325                           part built along this primitive.
326
327         :type  orientParams: dictionary
328         :param orientParams: parameters defining the orientation of the
329                              structural element part. Those parameters are
330                              detailed in class
331                              :class:`~orientation.Orientation1D`.
332
333         """
334         if self._parts.has_key(meshGroup):
335             self._parts[meshGroup].addOrientation(orientParams)
336         else:
337             logger.warning('Mesh group "%s" not found in structural element, '
338                            'cannot set orientation.' % meshGroup)
339
340     def build(self):
341         """
342         Build the geometric shapes and the markers corresponding to the
343         different parts of the structural element, and add them to the study.
344         """
345         gg = salome.ImportComponentGUI("GEOM")
346         for part in self._parts.itervalues():
347             # Build the structural element part
348             logger.debug("Building %s" % part)
349             try:
350                 (shape, markers) = part.build()
351                 if shape is None:
352                     logger.error("Part %s has not been built" % part)
353                     continue
354             except:
355                 logger.exception("Couldn't build part %s" % part)
356                 continue
357             
358             # Add the new objects to the study
359             IOR = self._studyEditor.study.ConvertObjectToIOR(shape)
360             shapeSObjName = part.name + "_" + part.groupName
361             icon = None
362             if salome.hasDesktop():
363                 icon = gg.getShapeTypeIcon(IOR)
364             shapeSObj = self._studyEditor.createItem(self._getSObject(),
365                                             name = shapeSObjName, IOR = IOR,
366                                             icon = icon)
367             if markers is not None and len(markers) > 0:
368                 i = 1
369                 for marker in markers:
370                     markerIOR = \
371                             self._studyEditor.study.ConvertObjectToIOR(marker)
372                     markerSObjName = "Orient_" + shapeSObjName
373                     if len(markers) > 1:
374                         markerSObjName += "_%d" % i
375                     markerSObj = self._studyEditor.createItem(
376                                                 self._getSObject(),
377                                                 name = markerSObjName,
378                                                 IOR = markerIOR,
379                                                 icon = "ICON_OBJBROWSER_LCS")
380                     i += 1
381
382     def display(self):
383         """
384         Display the structural element in the geom view.
385         """
386         StructuralElement.showElement(self._SObject)
387
388     @staticmethod
389     def showElement(theSObject):
390         """
391         Display the structural element corresponding to the study object
392         `theSObject`
393         """
394         if theSObject is not None:
395             gg = salome.ImportComponentGUI("GEOM")
396             aStudy = theSObject.GetStudy()
397             editor = getStudyEditor(aStudy._get_StudyId())
398             aIterator = aStudy.NewChildIterator(theSObject)
399             aIterator.Init()
400             while aIterator.More():
401                 sobj = aIterator.Value()
402                 icon = editor.getIcon(sobj)
403                 if icon != "ICON_OBJBROWSER_LCS":
404                     entry = aIterator.Value().GetID()
405                     gg.createAndDisplayGO(entry)
406                     gg.setDisplayMode(entry, 1)
407                 aIterator.Next()
408
409
410 def TEST_CreateGeometry():
411     import geompy
412     import SALOMEDS
413     geompy.init_geom(salome.myStudy)
414     Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
415     edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
416     geompy.addToStudy(Box_1, "Box_1")
417     for i in range(len(edges)):
418         geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
419     faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
420     faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
421     faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
422     for i in range(len(faces)):
423         geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
424     Cylinder_1 = geompy.MakeCylinderRH(50, 200)
425     geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
426     cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
427     geompy.addToStudy(Cylinder_1, "Cylinder_1")
428     for i in range(len(cyl_faces)):
429         geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
430     Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
431     cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
432                                           geompy.ShapeType["FACE"])
433     geompy.addToStudy(Cylinder_2, "Cylinder_2")
434     for i in range(len(cyl_faces2)):
435         geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
436                                   "CylFace2_%d" % i)
437
438
439 def TEST_StructuralElement():
440     salome.salome_init()
441     TEST_CreateGeometry()
442     liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
443                                         'VECT_Y': (1.0, 0.0, 1.0)}),
444                        ('Orientation', {'MeshGroups': 'Edge_5',
445                                         'ANGL_VRIL': 45.0}),
446                        ('GeneralBeam', {'MeshGroups': 'Edge_1, Edge_7',
447                                         'A': 1, 'IY1': 20, 'IY2': 40,
448                                         'IZ1': 60, 'IZ2': 30}),
449                        ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
450                                              'R1': 30, 'R2': 20}),
451                        ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
452                                          'R': 40, 'EP': 20}),
453                        ('RectangularBeam', {'MeshGroups': 'Edge_4, Edge_5',
454                                             'HZ1': 60, 'HY1': 40,
455                                             'EPZ1': 15, 'EPY1': 10,
456                                             'HZ2': 40, 'HY2': 60,
457                                             'EPZ2': 10, 'EPY2': 15}),
458                        ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
459                        ('VisuCoque', {'MeshGroups': 'Face_4',
460                                       'Epais': 10, 'Excentre': 5,
461                                       'angleAlpha': 45, 'angleBeta': 60}),
462                        ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
463                        ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
464                                        'angleAlpha': 45, 'angleBeta': 60}),
465                        ('VisuGrille', {'MeshGroups': 'CylFace2_2',
466                                        'Excentre': 5, 'origAxeX': 400,
467                                        'origAxeY': 400, 'origAxeZ': 0,
468                                        'axeX': 0, 'axeY': 0, 'axeZ': 100}),
469                       ]
470
471     structElemManager = StructuralElementManager()
472     elem = structElemManager.createElement(liste_commandes)
473     if salome.hasDesktop():
474         elem.display()
475         salome.sg.updateObjBrowser(True)
476
477
478 # Main function only used to test the module
479 if __name__ == "__main__":
480     TEST_StructuralElement()