Salome HOME
be2c1a9663bb3df68b64a36dc54f6f5fe7f0af45
[modules/geom.git] / src / GEOM_PY / structelem / parts.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2007-2023  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, or (at your option) any later version.
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 ## \defgroup parts parts
23 #  \{ 
24 #  \details
25 #  This module defines the different structural element parts. It is used to
26 #  build the geometric shapes of the structural elements. It should not be used
27 #  directly in the general case. Structural elements should be created by the
28 #  \ref structelem.StructuralElementManager "salome.geom.structelem.StructuralElementManager".
29 #  \}
30
31 """
32 This module defines the different structural element parts. It is used to
33 build the geometric shapes of the structural elements. It should not be used
34 directly in the general case. Structural elements should be created by the
35 class :class:`~salome.geom.structelem.StructuralElementManager`.
36 """
37
38 import math
39
40 import salome
41 import SALOMEDS
42
43 from salome.kernel.logger import Logger
44 from salome.kernel import termcolor
45 logger = Logger("salome.geom.structelem.parts", color = termcolor.RED)
46 from salome.geom.geomtools import getGeompy
47
48 from . import orientation
49
50 # Filling for the beams
51 FULL = "FULL"
52 HOLLOW = "HOLLOW"
53
54 # Minimum dimension for the shapes to extrude
55 MIN_DIM_FOR_EXTRUDED_SHAPE = 2e-4
56 MIN_LENGTH_FOR_EXTRUSION = 1e-4
57 MIN_THICKNESS = 1e-5
58
59 # Colors for the structural elements
60 GREEN = SALOMEDS.Color(0.0, 1.0, 0.0)
61 LIGHT_GREEN = SALOMEDS.Color(0.0, 1.0, 170.0/255.0)
62 BLUE = SALOMEDS.Color(0.0, 0.0, 1.0)
63 LIGHT_BLUE = SALOMEDS.Color(0.0, 0.5, 1.0)
64 RED = SALOMEDS.Color(1.0, 0.0, 0.0)
65 LIGHT_RED = SALOMEDS.Color(1.0, 0.5, 0.5)
66 PURPLE = SALOMEDS.Color(170.0/255.0, 85.0/255.0, 1.0)
67 ORANGE = SALOMEDS.Color(1.0, 170.0/255.0, 0.0)
68
69 ## This exception is raised when an invalid parameter is used to build a
70 #  structural element part.
71 #  \ingroup parts
72 class InvalidParameterError(Exception):
73     """
74     This exception is raised when an invalid parameter is used to build a
75     structural element part.
76     """
77     
78     def __init__(self, groupName, expression, minValue, value):
79         self.groupName = groupName
80         self.expression = expression
81         self.minValue = minValue
82         self.value = value
83         
84     def __str__(self):
85         return "%s < %g (%s = %g in %s)" % (self.expression, self.minValue,
86                                             self.expression, self.value,
87                                             self.groupName)
88
89 ## This class enables the use of sub-shapes in sets or as dictionary keys.
90 #  It implements __eq__ and __hash__ methods so that sub-shapes with the same
91 #  CORBA object \em mainShape and the same \em id are considered equal.
92 #  \ingroup parts
93 class SubShapeID:
94     """
95     This class enables the use of sub-shapes in sets or as dictionary keys.
96     It implements __eq__ and __hash__ methods so that sub-shapes with the same
97     CORBA object `mainShape` and the same `id` are considered equal.
98     """
99
100     def __init__(self, mainShape, id):
101         self._mainShape = mainShape
102         self._id = id
103
104     ## Return the sub-shape (GEOM object). \em geom is a pseudo-geompy object
105     #  used to find the geometrical object.
106     def getObj(self, geom):
107         """
108         Return the sub-shape (GEOM object). `geom` is a pseudo-geompy object
109         used to find the geometrical object.
110         """
111         return geom.GetSubShape(self._mainShape, [self._id])
112     
113     def __eq__(self, other):
114         return self._mainShape._is_equivalent(other._mainShape) and \
115                self._id == other._id
116     
117     def __hash__(self):
118         return self._mainShape._hash(2147483647) ^ self._id
119
120 ## This class is the base class for all structural element parts. It should
121 #  not be instantiated directly (consider it as an "abstract" class).
122 #  \param groupName (string) the name of the underlying geometrical primitive 
123 #  in the study.
124 #  \param groupGeomObj (GEOM object) the underlying geometrical primitive.
125 #  \param parameters (dictionary) parameters defining the structural element (see
126 #  subclasses for details).
127 #  \param name (string) name to use for the created object in the study.
128 #  \ingroup parts
129 class StructuralElementPart:
130     """
131     This class is the base class for all structural element parts. It should
132     not be instantiated directly (consider it as an "abstract" class).
133
134     :type  groupName: string
135     :param groupName: the name of the underlying geometrical primitive in the
136                       study.
137
138     :type  groupGeomObj: GEOM object
139     :param groupGeomObj: the underlying geometrical primitive.
140
141     :type  parameters: dictionary
142     :param parameters: parameters defining the structural element (see
143                        subclasses for details).
144
145     :type  name: string
146     :param name: name to use for the created object in the study.
147
148     """
149     
150     DEFAULT_NAME = "StructElemPart"
151
152     def __init__(self, groupName, groupGeomObj, parameters,
153                  name = DEFAULT_NAME, color = None):
154         self._parameters = parameters
155         self.groupName = groupName
156         self._groupGeomObj = groupGeomObj
157         self._orientation = None
158         self._paramUserName = {}
159         self.name = name
160         self.geom = getGeompy()
161         self.baseShapesSet = set()
162         self.isMainShape = (groupGeomObj.GetType() != 37) # See geompyDC.ShapeIdToType for type codes
163         if not self.isMainShape:
164             mainShape = self.geom.GetMainShape(groupGeomObj)
165             listIDs = self.geom.GetObjectIDs(groupGeomObj)
166             if mainShape is not None and listIDs is not None:
167                 for id in listIDs:
168                     self.baseShapesSet.add(SubShapeID(mainShape, id))
169         self.color = color
170         if self.color is None:
171             self.color = self._groupGeomObj.GetColor()
172
173     ## This method finds the value of a parameter in the parameters
174     #  dictionary. The argument is a list because some parameters can have
175     #  several different names.
176     def _getParameter(self, nameList, default = None):
177         """
178         This method finds the value of a parameter in the parameters
179         dictionary. The argument is a list because some parameters can have
180         several different names.
181         """
182         if len(nameList) > 0:
183             paramName = nameList[0]
184         for name in nameList:
185             if name in self._parameters:
186                 self._paramUserName[paramName] = name
187                 return self._parameters[name]
188         return default
189
190     ## This method finds the user name for a parameter.
191     def _getParamUserName(self, paramName):
192         """
193         This method finds the user name for a parameter.
194         """
195         if paramName in self._paramUserName:
196             return self._paramUserName[paramName]
197         else:
198             return paramName
199
200     def __repr__(self):
201         reprdict = self.__dict__.copy()
202         del reprdict["_parameters"]
203         del reprdict["groupName"]
204         del reprdict["_groupGeomObj"]
205         del reprdict["_paramUserName"]
206         del reprdict["name"]
207         del reprdict["geom"]
208         del reprdict["baseShapesSet"]
209         return '%s("%s", %s)' % (self.__class__.__name__, self.groupName,
210                                  reprdict)
211
212     ## Add orientation information to the structural element part. See class
213     #  \ref Orientation1D "salome.geom.structelem.orientation.Orientation1D" 
214     #  for the description of the parameters.
215     def addOrientation(self, orientParams):
216         """
217         Add orientation information to the structural element part. See class
218         :class:`~salome.geom.structelem.orientation.Orientation1D` for the description
219         of the parameters.
220         """
221         self._orientation.addParams(orientParams)
222
223     ## This method checks that some parameters or some expressions involving
224     #  those parameters are greater than a minimum value.
225     def _checkSize(self, value, mindim, expression):
226         """
227         This method checks that some parameters or some expressions involving
228         those parameters are greater than a minimum value.
229         """
230         if value < mindim:
231             raise InvalidParameterError(self.groupName, expression,
232                                         mindim, value)
233
234     ## Build the geometric shapes and the markers corresponding to the
235     #  structural element part in the study.
236     def build(self):
237         """
238         Build the geometric shapes and the markers corresponding to the
239         structural element part in the study.
240         """
241         shape = self._buildPart()
242         markers = self._buildMarkers()
243         shape.SetColor(self.color)
244         for marker in markers:
245             marker.SetColor(self.color)
246         return (shape, markers)
247
248     ## This abstract method must be implemented in subclasses and should
249     #  create the geometrical shape(s) of the structural element part.
250     def _buildPart(self):
251         """
252         This abstract method must be implemented in subclasses and should
253         create the geometrical shape(s) of the structural element part.
254         """
255         raise NotImplementedError("Method _buildPart not implemented in class"
256                                   " %s (it must be implemented in "
257                                   "StructuralElementPart subclasses)." %
258                                   self.__class__.__name__)
259
260     ## This abstract method must be implemented in subclasses and should
261     #  create the markers defining the orientation of the structural element
262     #  part.
263     def _buildMarkers(self):
264         """
265         This abstract method must be implemented in subclasses and should
266         create the markers defining the orientation of the structural element
267         part.
268         """
269         raise NotImplementedError("Method _buildMarker not implemented in "
270                                   "class %s (it must be implemented in "
271                                   "StructuralElementPart subclasses)." %
272                                   self.__class__.__name__)
273
274     ## Find and return the base sub-shapes in the structural element part.
275     def _getSubShapes(self, minDim = MIN_LENGTH_FOR_EXTRUSION):
276         """
277         Find and return the base sub-shapes in the structural element part.
278         """
279         if self.isMainShape:
280             return [self._groupGeomObj]
281         subShapes = []
282         for subShapeID in self.baseShapesSet:
283             subShape = subShapeID.getObj(self.geom)
284             length = self.geom.BasicProperties(subShape)[0]
285             if length < minDim:
286                 logger.warning("Length too short (%s - ID %s, length = %g), "
287                                "subshape will not be used in structural "
288                                "element" % (self.groupName, subShapeID._id,
289                                             length))
290             else:
291                 subShapes.append(subShape)
292         return subShapes
293
294 ## This class is an "abstract" class for all 1D structural element parts. It
295 #  should not be instantiated directly. See class StructuralElementPart 
296 #  for the description of the parameters.
297 #  \ingroup parts
298 class Beam(StructuralElementPart):
299     """
300     This class is an "abstract" class for all 1D structural element parts. It
301     should not be instantiated directly. See class
302     :class:`StructuralElementPart` for the description of the parameters.
303     """
304
305     DEFAULT_NAME = "Beam"
306
307     def __init__(self, groupName, groupGeomObj, parameters,
308                  name = DEFAULT_NAME, color = None):
309         StructuralElementPart.__init__(self, groupName, groupGeomObj,
310                                        parameters, name, color)
311         self._orientation = orientation.Orientation1D()
312
313     ## This method checks if a 1D object is "reversed", i.e. if its
314     #  orientation is different than the orientation of the underlying OCC
315     #  object.
316     def _isReversed(self, path):
317         """
318         This method checks if a 1D object is "reversed", i.e. if its
319         orientation is different than the orientation of the underlying OCC
320         object.
321         """
322         length = self.geom.BasicProperties(path)[0]
323         p1 = self.geom.MakeVertexOnCurve(path, 0.0)
324         p2 = self.geom.GetFirstVertex(path)
325         dist = self.geom.MinDistance(p1, p2)
326         return dist > length / 2
327
328     ## Get a vertex and the corresponding tangent on a wire by parameter.
329     #  This method takes into account the "real" orientation of the wire
330     #  (i.e. the orientation of the underlying OCC object).
331     def _getVertexAndTangentOnOrientedWire(self, path, param):
332         """
333         Get a vertex and the corresponding tangent on a wire by parameter.
334         This method takes into account the "real" orientation of the wire
335         (i.e. the orientation of the underlying OCC object).
336         """
337         if self._isReversed(path):
338             vertex = self.geom.MakeVertexOnCurve(path, 1.0 - param)
339             invtangent = self.geom.MakeTangentOnCurve(path, 1.0 - param)
340             tanpoint = self.geom.MakeTranslationVectorDistance(vertex,
341                                                                invtangent,
342                                                                -1.0)
343             tangent = self.geom.MakeVector(vertex, tanpoint)
344         else:
345             vertex = self.geom.MakeVertexOnCurve(path, param)
346             tangent = self.geom.MakeTangentOnCurve(path, param)
347         return (vertex, tangent)
348
349     ## Create a solid by the extrusion of section \em wire1 to section \em wire2
350     #  along \em path.
351     def _makeSolidPipeFromWires(self, wire1, wire2, point1, point2, path):
352         """
353         Create a solid by the extrusion of section `wire1` to section `wire2`
354         along `path`.
355         """
356         face1 = self.geom.MakeFace(wire1, True)
357         face2 = self.geom.MakeFace(wire2, True)
358         shell = self.geom.MakePipeWithDifferentSections([wire1, wire2],
359                                                         [point1, point2],
360                                                         path, False, False,
361                                                         False)
362         closedShell = self.geom.MakeShell([face1, face2, shell])
363         solid = self.geom.MakeSolid([closedShell])
364         return solid
365
366     ## Build the structural element part.
367     def _buildPart(self):
368         """
369         Build the structural element part.
370         """
371         # Get all the sub-shapes in the group (normally only edges and wires)
372         paths = self._getSubShapes()
373         listPipes = []
374         withContact = False
375         withCorrection = False
376     
377         for path in paths:
378             # Build the sections (rectangular or circular) at each end of the
379             # beam
380             (fPoint, fNormal) = self._getVertexAndTangentOnOrientedWire(path,
381                                                                         0.0)
382             (lPoint, lNormal) = self._getVertexAndTangentOnOrientedWire(path,
383                                                                         1.0)
384             (outerWire1, innerWire1, outerWire2, innerWire2) = \
385                     self._makeSectionWires(fPoint, fNormal, lPoint, lNormal)
386
387             # Create the resulting solid
388             outerSolid = self._makeSolidPipeFromWires(outerWire1, outerWire2,
389                                                       fPoint, lPoint, path)
390             if self.filling == HOLLOW:
391                 innerSolid = self._makeSolidPipeFromWires(innerWire1,
392                                                           innerWire2, fPoint,
393                                                           lPoint, path)
394                 resultSolid = self.geom.MakeCut(outerSolid, innerSolid)
395                 listPipes.append(resultSolid)
396             else:
397                 listPipes.append(outerSolid)
398
399         if len(listPipes) == 0:
400             return None
401         elif len(listPipes) == 1:
402             return listPipes[0]
403         else:
404             return self.geom.MakeCompound(listPipes)
405
406     ## Build the markers defining the orientation of the structural element part.
407     def _buildMarkers(self):
408         """
409         Build the markers defining the orientation of the structural element
410         part.
411         """
412         param = 0.5
413         paths = self._getSubShapes()
414         listMarkers = []
415         for path in paths:
416             (center, vecX) = self._getVertexAndTangentOnOrientedWire(path,
417                                                                      param)
418             marker = self._orientation.buildMarker(self.geom, center, vecX)
419             listMarkers.append(marker)
420         return listMarkers
421
422
423 ## This class defines a beam with a circular section. It can be full or
424 #  hollow, and its radius and thickness can vary from one end of the beam to
425 #  the other. The valid parameters for circular beams are:
426 #  - "R1" or "R": radius at the first end of the beam.
427 #  - "R2" or "R": radius at the other end of the beam.
428 #  - "EP1" or "EP" (optional): thickness at the first end of the beam.
429 #    If not specified or equal to 0, the beam is considered full.
430 #  - "EP2" or "EP" (optional): thickness at the other end of the beam.
431 #  If not specified or equal to 0, the beam is considered full.
432 #
433 #  See class StructuralElementPart for the description of the other parameters.
434 #  \ingroup parts
435 class CircularBeam(Beam):
436     """
437     This class defines a beam with a circular section. It can be full or
438     hollow, and its radius and thickness can vary from one end of the beam to
439     the other. The valid parameters for circular beams are:
440
441     * "R1" or "R": radius at the first end of the beam.
442     * "R2" or "R": radius at the other end of the beam.
443     * "EP1" or "EP" (optional): thickness at the first end of the beam.
444       If not specified or equal to 0, the beam is considered full.
445     * "EP2" or "EP" (optional): thickness at the other end of the beam.
446       If not specified or equal to 0, the beam is considered full.
447
448     See class :class:`StructuralElementPart` for the description of the
449     other parameters.
450
451     """
452
453     def __init__(self, groupName, groupGeomObj, parameters,
454                  name = Beam.DEFAULT_NAME, color = None):
455         if color is None:
456             if "R1" in parameters: # variable section
457                 color = LIGHT_RED
458             else:                       # constant section
459                 color = RED
460
461         Beam.__init__(self, groupName, groupGeomObj, parameters,
462                       name, color)
463
464         self.R1 = self._getParameter(["R1", "R"])
465         self.R2 = self._getParameter(["R2", "R"])
466         self.EP1 = self._getParameter(["EP1", "EP"])
467         self.EP2 = self._getParameter(["EP2", "EP"])
468
469         if self.EP1 is None or self.EP2 is None or \
470                                 self.EP1 == 0 or self.EP2 == 0:
471             self.filling = FULL
472         else:
473             self.filling = HOLLOW
474
475         logger.debug(repr(self))
476
477         # Check parameters
478         self._checkSize(self.R1, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
479                         self._getParamUserName("R1"))
480         self._checkSize(self.R2, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
481                         self._getParamUserName("R2"))
482         if self.filling == HOLLOW:
483             self._checkSize(self.EP1, MIN_THICKNESS,
484                             self._getParamUserName("EP1"))
485             self._checkSize(self.EP2, MIN_THICKNESS,
486                             self._getParamUserName("EP2"))
487             self._checkSize(self.R1 - self.EP1,
488                             MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
489                             "%s - %s" % (self._getParamUserName("R1"),
490                                          self._getParamUserName("EP1")))
491             self._checkSize(self.R2 - self.EP2,
492                             MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
493                             "%s - %s" % (self._getParamUserName("R2"),
494                                          self._getParamUserName("EP2")))
495
496     ## Create the circular sections used to build the pipe.
497     def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
498         """
499         Create the circular sections used to build the pipe.
500         """
501         outerCircle1 = self.geom.MakeCircle(fPoint, fNormal, self.R1)
502         outerCircle2 = self.geom.MakeCircle(lPoint, lNormal, self.R2)
503         if self.filling == HOLLOW:
504             innerCircle1 = self.geom.MakeCircle(fPoint, fNormal,
505                                                 self.R1 - self.EP1)
506             innerCircle2 = self.geom.MakeCircle(lPoint, lNormal,
507                                                 self.R2 - self.EP2)
508         else:
509             innerCircle1 = None
510             innerCircle2 = None
511
512         return (outerCircle1, innerCircle1, outerCircle2, innerCircle2)
513
514
515 ## This class defines a beam with a rectangular section. It can be full or
516 #  hollow, and its dimensions can vary from one end of the beam to the other.
517 #  The valid parameters for rectangular beams are:
518 #  - "HY1", "HY", "H1" or "H": width at the first end of the beam.
519 #  - "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
520 #  - "HY2", "HY", "H2" or "H": width at the other end of the beam.
521 #  - "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
522 #  - "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
523 #    direction at the first end of the beam. If not specified or equal to 0,
524 #    the beam is considered full.
525 #  - "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
526 #    direction at the first end of the beam. If not specified or equal to 0,
527 #    the beam is considered full.
528 #  - "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
529 #    direction at the other end of the beam. If not specified or equal to 0,
530 #    the beam is considered full.
531 #  - "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
532 #    direction at the other end of the beam. If not specified or equal to 0,
533 #    the beam is considered full.
534 #
535 #   See class StructuralElementPart for the description of the other parameters.
536 #  \ingroup parts
537 class RectangularBeam(Beam):
538     """
539     This class defines a beam with a rectangular section. It can be full or
540     hollow, and its dimensions can vary from one end of the beam to the other.
541     The valid parameters for rectangular beams are:
542
543     * "HY1", "HY", "H1" or "H": width at the first end of the beam.
544     * "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
545     * "HY2", "HY", "H2" or "H": width at the other end of the beam.
546     * "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
547     * "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
548       direction at the first end of the beam. If not specified or equal to 0,
549       the beam is considered full.
550     * "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
551       direction at the first end of the beam. If not specified or equal to 0,
552       the beam is considered full.
553     * "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
554       direction at the other end of the beam. If not specified or equal to 0,
555       the beam is considered full.
556     * "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
557       direction at the other end of the beam. If not specified or equal to 0,
558       the beam is considered full.
559
560     See class :class:`StructuralElementPart` for the description of the
561     other parameters.
562
563     """
564
565     def __init__(self, groupName, groupGeomObj, parameters,
566                  name = Beam.DEFAULT_NAME, color = None):
567         if color is None:
568             if "HY1" in parameters or "H1" in parameters:
569                 color = LIGHT_BLUE # variable section
570             else:                  # constant section
571                 color = BLUE
572
573         Beam.__init__(self, groupName, groupGeomObj, parameters,
574                       name, color)
575
576         self.HY1 = self._getParameter(["HY1", "HY", "H1", "H"])
577         self.HZ1 = self._getParameter(["HZ1", "HZ", "H1", "H"])
578         self.HY2 = self._getParameter(["HY2", "HY", "H2", "H"])
579         self.HZ2 = self._getParameter(["HZ2", "HZ", "H2", "H"])
580         self.EPY1 = self._getParameter(["EPY1", "EPY", "EP1", "EP"])
581         self.EPZ1 = self._getParameter(["EPZ1", "EPZ", "EP1", "EP"])
582         self.EPY2 = self._getParameter(["EPY2", "EPY", "EP2", "EP"])
583         self.EPZ2 = self._getParameter(["EPZ2", "EPZ", "EP2", "EP"])
584
585         if self.EPY1 is None or self.EPZ1 is None or \
586            self.EPY2 is None or self.EPZ2 is None or \
587            self.EPY1 == 0 or self.EPZ1 == 0 or \
588            self.EPY2 == 0 or self.EPZ2 == 0:
589             self.filling = FULL
590         else:
591             self.filling = HOLLOW
592
593         logger.debug(repr(self))
594
595         # Check parameters
596         self._checkSize(self.HY1, MIN_DIM_FOR_EXTRUDED_SHAPE,
597                         self._getParamUserName("HY1"))
598         self._checkSize(self.HZ1, MIN_DIM_FOR_EXTRUDED_SHAPE,
599                         self._getParamUserName("HZ1"))
600         self._checkSize(self.HY2, MIN_DIM_FOR_EXTRUDED_SHAPE,
601                         self._getParamUserName("HY2"))
602         self._checkSize(self.HZ2, MIN_DIM_FOR_EXTRUDED_SHAPE,
603                         self._getParamUserName("HZ2"))
604         if self.filling == HOLLOW:
605             self._checkSize(self.EPY1, MIN_THICKNESS,
606                             self._getParamUserName("EPY1"))
607             self._checkSize(self.EPZ1, MIN_THICKNESS,
608                             self._getParamUserName("EPZ1"))
609             self._checkSize(self.EPY2, MIN_THICKNESS,
610                             self._getParamUserName("EPY2"))
611             self._checkSize(self.EPZ2, MIN_THICKNESS,
612                             self._getParamUserName("EPZ2"))
613             self._checkSize(self.HY1 - 2 * self.EPY1,
614                             MIN_DIM_FOR_EXTRUDED_SHAPE,
615                             "%s - 2 * %s" % (self._getParamUserName("HY1"),
616                                              self._getParamUserName("EPY1")))
617             self._checkSize(self.HZ1 - 2 * self.EPZ1,
618                             MIN_DIM_FOR_EXTRUDED_SHAPE,
619                             "%s - 2 * %s" % (self._getParamUserName("HZ1"),
620                                              self._getParamUserName("EPZ1")))
621             self._checkSize(self.HY2 - 2 * self.EPY2,
622                             MIN_DIM_FOR_EXTRUDED_SHAPE,
623                             "%s - 2 * %s" % (self._getParamUserName("HY2"),
624                                              self._getParamUserName("EPY2")))
625             self._checkSize(self.HZ2 - 2 * self.EPZ2,
626                             MIN_DIM_FOR_EXTRUDED_SHAPE,
627                             "%s - 2 * %s" % (self._getParamUserName("HZ2"),
628                                              self._getParamUserName("EPZ2")))
629
630     ## Create a rectangle in the specified plane.
631     def _makeRectangle(self, HY, HZ, lcs):
632         """
633         Create a rectangle in the specified plane.
634         """
635         halfHY = HY / 2.0
636         halfHZ = HZ / 2.0
637         sketchStr = "Sketcher:F %g %g:" % (-halfHY, -halfHZ)
638         sketchStr += "TT %g %g:" % (halfHY, -halfHZ)
639         sketchStr += "TT %g %g:" % (halfHY, halfHZ)
640         sketchStr += "TT %g %g:WW" % (-halfHY, halfHZ)
641         logger.debug('Drawing rectangle: "%s"' % sketchStr)
642         sketch = self.geom.MakeSketcherOnPlane(sketchStr, lcs)
643         return sketch
644
645     ## Create one side of the rectangular sections used to build the pipe.
646     def _makeSectionRectangles(self, point, vecX, HY, HZ, EPY, EPZ):
647         """
648         Create one side of the rectangular sections used to build the pipe.
649         """
650         (vecY, vecZ) = self._orientation.getVecYZ(self.geom, point, vecX)
651         lcs = self.geom.MakeMarkerPntTwoVec(point, vecY, vecZ)
652         outerRect = self._makeRectangle(HY, HZ, lcs)
653         if self.filling == HOLLOW:
654             innerRect = self._makeRectangle(HY - 2.0 * EPY,
655                                             HZ - 2.0 * EPZ,
656                                             lcs)
657         else:
658             innerRect = None
659         return (outerRect, innerRect)
660
661     ## Create the rectangular sections used to build the pipe.
662     def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
663         """
664         Create the rectangular sections used to build the pipe.
665         """
666         (outerRect1, innerRect1) = \
667             self._makeSectionRectangles(fPoint, fNormal, self.HY1, self.HZ1,
668                                         self.EPY1, self.EPZ1)
669         (outerRect2, innerRect2) = \
670             self._makeSectionRectangles(lPoint, lNormal, self.HY2, self.HZ2,
671                                         self.EPY2, self.EPZ2)
672         return (outerRect1, innerRect1, outerRect2, innerRect2)
673
674
675 ## This method finds the value of a parameter in the parameters
676 #  dictionary. The argument is a list because some parameters can have
677 #  several different names.
678 #  \ingroup parts
679 def getParameterInDict(nameList, parametersDict, default = None):
680     """
681     This method finds the value of a parameter in the parameters
682     dictionary. The argument is a list because some parameters can have
683     several different names.
684     """
685     for name in nameList:
686         if name in parametersDict:
687             return parametersDict[name]
688     return default
689
690 ## This class defines a beam with a generic section. It is represented as a
691 #  full rectangular beam with the following parameters:
692 #  - HY1 = sqrt(12 * IZ1 / A1)
693 #  - HZ1 = sqrt(12 * IY1 / A1)
694 #  - HY2 = sqrt(12 * IZ2 / A2)
695 #  - HZ2 = sqrt(12 * IY2 / A2)
696 #    
697 #  See StructuralElementPart for the description of the other parameters.
698 #  \ingroup parts
699 class GeneralBeam(RectangularBeam):
700     """
701     This class defines a beam with a generic section. It is represented as a
702     full rectangular beam with the following parameters:
703     
704     * HY1 = sqrt(12 * IZ1 / A1)
705     * HZ1 = sqrt(12 * IY1 / A1)
706     * HY2 = sqrt(12 * IZ2 / A2)
707     * HZ2 = sqrt(12 * IY2 / A2)
708     
709     See class :class:`StructuralElementPart` for the description of the other
710     parameters.
711     """
712
713     def __init__(self, groupName, groupGeomObj, parameters,
714                  name = Beam.DEFAULT_NAME, color = None):
715         self.IY1 = getParameterInDict(["IY1", "IY"], parameters)
716         self.IZ1 = getParameterInDict(["IZ1", "IZ"], parameters)
717         self.IY2 = getParameterInDict(["IY2", "IY"], parameters)
718         self.IZ2 = getParameterInDict(["IZ2", "IZ"], parameters)
719         self.A1 = getParameterInDict(["A1", "A"], parameters)
720         self.A2 = getParameterInDict(["A2", "A"], parameters)
721         parameters["HY1"] = math.sqrt(12 * self.IZ1 / self.A1)
722         parameters["HZ1"] = math.sqrt(12 * self.IY1 / self.A1)
723         parameters["HY2"] = math.sqrt(12 * self.IZ2 / self.A2)
724         parameters["HZ2"] = math.sqrt(12 * self.IY2 / self.A2)
725
726         if color is None:
727             if "IY1" in parameters: # variable section
728                 color = LIGHT_GREEN
729             else:                         # constant section
730                 color = GREEN
731
732         RectangularBeam.__init__(self, groupName, groupGeomObj, parameters,
733                                  name, color)
734
735 ## This class is an "abstract" class for all 2D structural element parts. It
736 #  should not be instantiated directly. 
737 #  See class StructuralElementPart for the description of the parameters.
738 #  \ingroup parts
739 class StructuralElementPart2D(StructuralElementPart):
740     """
741     This class is an "abstract" class for all 2D structural element parts. It
742     should not be instantiated directly. See class
743     :class:`StructuralElementPart` for the description of the parameters.
744     """
745
746     DEFAULT_NAME = "StructuralElementPart2D"
747
748     def __init__(self, groupName, groupGeomObj, parameters,
749                  name = DEFAULT_NAME):
750         StructuralElementPart.__init__(self, groupName, groupGeomObj,
751                                        parameters, name)
752         self._orientation = orientation.Orientation2D(
753                                         self._getParameter(["angleAlpha"]),
754                                         self._getParameter(["angleBeta"]),
755                                         self._getParameter(["Vecteur"]))
756         self.offset = self._getParameter(["Excentre"], 0.0)
757
758     ## Create a copy of a face at a given offset.
759     def _makeFaceOffset(self, face, offset, epsilon = 1e-6):
760         """
761         Create a copy of a face at a given offset.
762         """
763         if abs(offset) < epsilon:
764             return self.geom.MakeCopy(face)
765         else:
766             offsetObj = self.geom.MakeOffset(face, offset)
767             # We have to explode the resulting object into faces because it is
768             # created as a polyhedron and not as a single face
769             faces = self.geom.SubShapeAll(offsetObj,
770                                           self.geom.ShapeType["FACE"])
771             return faces[0]
772
773     ## Build the markers for the structural element part with a given offset
774     #  from the base face.
775     def _buildMarkersWithOffset(self, offset):
776         """
777         Build the markers for the structural element part with a given offset
778         from the base face.
779         """
780         uParam = 0.5
781         vParam = 0.5
782         listMarkers = []
783         subShapes = self._getSubShapes()
784     
785         for subShape in subShapes:
786             faces = self.geom.SubShapeAll(subShape,
787                                           self.geom.ShapeType["FACE"])
788             for face in faces:
789                 offsetFace = self._makeFaceOffset(face, offset)
790                 # get the center of the face and the normal at the center
791                 center = self.geom.MakeVertexOnSurface(offsetFace,
792                                                        uParam, vParam)
793                 normal = self.geom.GetNormal(offsetFace, center)
794                 marker = self._orientation.buildMarker(self.geom,
795                                                        center, normal)
796                 listMarkers.append(marker)
797
798         return listMarkers
799
800 ## This class defines a shell with a given thickness. It can be shifted from
801 #  the base face. The valid parameters for thick shells are:
802 #  - "Epais": thickness of the shell.
803 #  - "Excentre": offset of the shell from the base face.
804 #  - "angleAlpha": angle used to build the markers (see class
805 #    \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
806 #  - "angleBeta": angle used to build the markers (see class
807 #    \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
808 #  - "Vecteur": vector used instead of the angles to build the markers (see
809 #    \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
810 #
811 #    See class StructuralElementPart for the description of the other parameters.
812 #  \ingroup parts
813 class ThickShell(StructuralElementPart2D):
814     """
815     This class defines a shell with a given thickness. It can be shifted from
816     the base face. The valid parameters for thick shells are:
817
818     * "Epais": thickness of the shell.
819     * "Excentre": offset of the shell from the base face.
820     * "angleAlpha": angle used to build the markers (see class
821       :class:`~salome.geom.structelem.orientation.Orientation2D`)
822     * "angleBeta": angle used to build the markers (see class
823       :class:`~salome.geom.structelem.orientation.Orientation2D`)
824     * "Vecteur": vector used instead of the angles to build the markers (see
825       class :class:`~salome.geom.structelem.orientation.Orientation2D`)
826
827     See class :class:`StructuralElementPart` for the description of the
828     other parameters.
829     """
830
831     DEFAULT_NAME = "ThickShell"
832
833     def __init__(self, groupName, groupGeomObj, parameters,
834                  name = DEFAULT_NAME):
835         StructuralElementPart2D.__init__(self, groupName,
836                                          groupGeomObj, parameters, name)
837         self.thickness = self._getParameter(["Epais"])
838         logger.debug(repr(self))
839
840     ## Create the geometrical shapes corresponding to the thick shell.
841     def _buildPart(self):
842         """
843         Create the geometrical shapes corresponding to the thick shell.
844         """
845         subShapes = self._getSubShapes()
846         listSolids = []
847     
848         for subShape in subShapes:
849             faces = self.geom.SubShapeAll(subShape,
850                                           self.geom.ShapeType["FACE"])
851             for face in faces:
852                 shape = self._buildThickShellForFace(face)
853                 listSolids.append(shape)
854
855         if len(listSolids) == 0:
856             return None
857         elif len(listSolids) == 1:
858             return listSolids[0]
859         else:
860             return self.geom.MakeCompound(listSolids)
861
862     ## Create the geometrical shapes corresponding to the thick shell for a
863     #  given face.
864     def _buildThickShellForFace(self, face):
865         """
866         Create the geometrical shapes corresponding to the thick shell for a
867         given face.
868         """
869         epsilon = 1e-6
870         if self.thickness < 2 * epsilon:
871             return self._makeFaceOffset(face, self.offset, epsilon)
872
873         upperOffset = self.offset + self.thickness / 2.0
874         lowerOffset = self.offset - self.thickness / 2.0
875         ruledMode = True
876         modeSolid = False
877
878         upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
879         lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
880         listShapes = [upperFace, lowerFace]
881         upperWires = self.geom.SubShapeAll(upperFace,
882                                            self.geom.ShapeType["WIRE"])
883         lowerWires = self.geom.SubShapeAll(lowerFace,
884                                            self.geom.ShapeType["WIRE"])
885         if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
886             # if the face is a cylinder, we remove the extra side edge
887             upperWires = self._removeCylinderExtraEdge(upperWires)
888             lowerWires = self._removeCylinderExtraEdge(lowerWires)
889         for i in range(len(upperWires)):
890             resShape = self.geom.MakeThruSections([upperWires[i],
891                                                    lowerWires[i]],
892                                                   modeSolid, epsilon,
893                                                   ruledMode)
894             listShapes.append(resShape)
895         resultShell = self.geom.MakeShell(listShapes)
896         resultSolid = self.geom.MakeSolid([resultShell])
897         return resultSolid
898
899     ## Remove the side edge in a cylinder.
900     def _removeCylinderExtraEdge(self, wires):
901         """
902         Remove the side edge in a cylinder.
903         """
904         result = []
905         for wire in wires:
906             edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
907             for edge in edges:
908                 if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
909                     result.append(edge)
910         return result
911
912     ## Build the markers defining the orientation of the thick shell.
913     def _buildMarkers(self):
914         """
915         Build the markers defining the orientation of the thick shell.
916         """
917         return self._buildMarkersWithOffset(self.offset +
918                                             self.thickness / 2.0)
919
920 ## This class defines a grid. A grid is represented by a 2D face patterned
921 #  with small lines in the main direction of the grid frame. The valid
922 #  parameters for grids are:
923 #  - "Excentre": offset of the grid from the base face.
924 #  - "angleAlpha": angle used to build the markers (see class
925 #    \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
926 #  - "angleBeta": angle used to build the markers (see class
927 #    \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
928 #  - "Vecteur": vector used instead of the angles to build the markers (see
929 #    \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
930 #  - "origAxeX": X coordinate of the origin of the axis used to determine the
931 #    orientation of the frame in the case of a cylindrical grid.
932 #  - "origAxeY": Y coordinate of the origin of the axis used to determine the
933 #    orientation of the frame in the case of a cylindrical grid.
934 #  - "origAxeZ": Z coordinate of the origin of the axis used to determine the
935 #    orientation of the frame in the case of a cylindrical grid.
936 #  - "axeX": X coordinate of the axis used to determine the orientation of
937 #    the frame in the case of a cylindrical grid.
938 #  - "axeY": Y coordinate of the axis used to determine the orientation of
939 #    the frame in the case of a cylindrical grid.
940 #  - "axeZ": Z coordinate of the axis used to determine the orientation of
941 #    the frame in the case of a cylindrical grid.
942 #
943 #    See class StructuralElementPart for the description of the other parameters.
944 #  \ingroup parts
945 class Grid(StructuralElementPart2D):
946     """
947     This class defines a grid. A grid is represented by a 2D face patterned
948     with small lines in the main direction of the grid frame. The valid
949     parameters for grids are:
950
951     * "Excentre": offset of the grid from the base face.
952     * "angleAlpha": angle used to build the markers (see class
953       :class:`~salome.geom.structelem.orientation.Orientation2D`)
954     * "angleBeta": angle used to build the markers (see class
955       :class:`~salome.geom.structelem.orientation.Orientation2D`)
956     * "Vecteur": vector used instead of the angles to build the markers (see
957       class :class:`~salome.geom.structelem.orientation.Orientation2D`)
958     * "origAxeX": X coordinate of the origin of the axis used to determine the
959       orientation of the frame in the case of a cylindrical grid.
960     * "origAxeY": Y coordinate of the origin of the axis used to determine the
961       orientation of the frame in the case of a cylindrical grid.
962     * "origAxeZ": Z coordinate of the origin of the axis used to determine the
963       orientation of the frame in the case of a cylindrical grid.
964     * "axeX": X coordinate of the axis used to determine the orientation of
965       the frame in the case of a cylindrical grid.
966     * "axeY": Y coordinate of the axis used to determine the orientation of
967       the frame in the case of a cylindrical grid.
968     * "axeZ": Z coordinate of the axis used to determine the orientation of
969       the frame in the case of a cylindrical grid.
970
971     See class :class:`StructuralElementPart` for the description of the
972     other parameters.
973     """
974
975     DEFAULT_NAME = "Grid"
976
977     def __init__(self, groupName, groupGeomObj, parameters,
978                  name = DEFAULT_NAME):
979         StructuralElementPart2D.__init__(self, groupName,
980                                          groupGeomObj, parameters, name)
981         self.xr = self._getParameter(["origAxeX"])
982         self.yr = self._getParameter(["origAxeY"])
983         self.zr = self._getParameter(["origAxeZ"])
984         self.vx = self._getParameter(["axeX"])
985         self.vy = self._getParameter(["axeY"])
986         self.vz = self._getParameter(["axeZ"])
987         logger.debug(repr(self))
988
989     ## Create the geometrical shapes representing the grid.
990     def _buildPart(self):
991         """
992         Create the geometrical shapes representing the grid.
993         """
994         subShapes = self._getSubShapes()
995         listGridShapes = []
996     
997         for subShape in subShapes:
998             faces = self.geom.SubShapeAll(subShape,
999                                           self.geom.ShapeType["FACE"])
1000             for face in faces:
1001                 if self.geom.KindOfShape(face)[0] == \
1002                                         self.geom.kind.CYLINDER2D and \
1003                         self.xr is not None and self.yr is not None and \
1004                         self.zr is not None and self.vx is not None and \
1005                         self.vy is not None and self.vz is not None:
1006                     shape = self._buildGridForCylinderFace(face)
1007                 else:
1008                     shape = self._buildGridForNormalFace(face)
1009                 listGridShapes.append(shape)
1010
1011         if len(listGridShapes) == 0:
1012             return None
1013         elif len(listGridShapes) == 1:
1014             return listGridShapes[0]
1015         else:
1016             return self.geom.MakeCompound(listGridShapes)
1017
1018     ## Create the geometrical shapes representing the grid for a given
1019     #  non-cylindrical face.
1020     def _buildGridForNormalFace(self, face):
1021         """
1022         Create the geometrical shapes representing the grid for a given
1023         non-cylindrical face.
1024         """
1025         baseFace = self._makeFaceOffset(face, self.offset)
1026         gridList = [baseFace]
1027         
1028         # Compute display length for grid elements
1029         p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1030         p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1031         length = self.geom.MinDistance(p1, p2) / 2.0
1032
1033         for u in range(1, 10):
1034             uParam = u * 0.1
1035             for v in range(1, 10):
1036                 vParam = v * 0.1
1037                 # get tangent plane on surface by parameters
1038                 center = self.geom.MakeVertexOnSurface(baseFace,
1039                                                        uParam, vParam)
1040                 tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
1041                                                              vParam, 1.0)
1042                 
1043                 # use the marker to get the orientation of the frame
1044                 normal = self.geom.GetNormal(tangPlane)
1045                 marker = self._orientation.buildMarker(self.geom, center,
1046                                                        normal, False)
1047                 [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
1048                 xPoint = self.geom.MakeTranslation(center, Xx * length,
1049                                                    Xy * length, Xz * length)
1050                 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1051                 gridList.append(gridLine)
1052         grid = self.geom.MakeCompound(gridList)
1053         return grid
1054
1055     ## Create the geometrical shapes representing the grid for a given
1056     #  cylindrical face.
1057     def _buildGridForCylinderFace(self, face):
1058         """
1059         Create the geometrical shapes representing the grid for a given
1060         cylindrical face.
1061         """
1062         baseFace = self._makeFaceOffset(face, self.offset)
1063         gridList = [baseFace]
1064         
1065         # Compute display length for grid elements
1066         p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1067         p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1068         length = self.geom.MinDistance(p1, p2) / 2.0
1069         
1070         # Create reference vector V
1071         origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
1072         vPoint = self.geom.MakeTranslation(origPoint,
1073                                            self.vx, self.vy, self.vz)
1074         refVec = self.geom.MakeVector(origPoint, vPoint)
1075
1076         for u in range(10):
1077             uParam = u * 0.1
1078             for v in range(1, 10):
1079                 vParam = v * 0.1
1080                 
1081                 # Compute the local orientation of the frame
1082                 center = self.geom.MakeVertexOnSurface(baseFace,
1083                                                        uParam, vParam)
1084                 locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
1085                                                          vPoint, 1.0)
1086                 locOrient = self.geom.GetNormal(locPlaneYZ)
1087                 xPoint = self.geom.MakeTranslationVectorDistance(center,
1088                                                                  locOrient,
1089                                                                  length)
1090                 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1091                 gridList.append(gridLine)
1092
1093         grid = self.geom.MakeCompound(gridList)
1094         return grid
1095
1096     ## Create the markers defining the orientation of the grid.
1097     def _buildMarkers(self):
1098         """
1099         Create the markers defining the orientation of the grid.
1100         """
1101         return self._buildMarkersWithOffset(self.offset)
1102
1103 ## Alias for class GeneralBeam.
1104 #  \ingroup parts
1105 def VisuPoutreGenerale(groupName, groupGeomObj, parameters,
1106                        name = "POUTRE"):
1107     """
1108     Alias for class :class:`GeneralBeam`.
1109     """
1110     return GeneralBeam(groupName, groupGeomObj, parameters, name)
1111
1112 ## Alias for class CircularBeam.
1113 #  \ingroup parts
1114 def VisuPoutreCercle(groupName, groupGeomObj, parameters,
1115                      name = "POUTRE"):
1116     """
1117     Alias for class :class:`CircularBeam`.
1118     """
1119     return CircularBeam(groupName, groupGeomObj, parameters, name)
1120
1121 ## Alias for class RectangularBeam. 
1122 #  \ingroup parts
1123 def VisuPoutreRectangle(groupName, groupGeomObj, parameters,
1124                         name = "POUTRE"):
1125     """
1126     Alias for class :class:`RectangularBeam`.
1127     """
1128     return RectangularBeam(groupName, groupGeomObj, parameters, name)
1129
1130 ## Alias for class GeneralBeam.  
1131 #  \ingroup parts
1132 def VisuBarreGenerale(groupName, groupGeomObj, parameters,
1133                       name = "BARRE"):
1134     """
1135     Alias for class :class:`GeneralBeam`.
1136     """
1137     return GeneralBeam(groupName, groupGeomObj, parameters, name,
1138                        color = ORANGE)
1139
1140 ## Alias for class RectangularBeam.      
1141 #  \ingroup parts
1142 def VisuBarreRectangle(groupName, groupGeomObj, parameters,
1143                        name = "BARRE"):
1144     """
1145     Alias for class :class:`RectangularBeam`.
1146     """
1147     return RectangularBeam(groupName, groupGeomObj, parameters, name,
1148                            color = ORANGE)
1149
1150 ## Alias for class CircularBeam.
1151 #  \ingroup parts
1152 def VisuBarreCercle(groupName, groupGeomObj, parameters,
1153                     name = "BARRE"):
1154     """
1155     Alias for class :class:`CircularBeam`.
1156     """
1157     return CircularBeam(groupName, groupGeomObj, parameters, name,
1158                         color = ORANGE)
1159
1160 ## Alias for class CircularBeam.
1161 #  \ingroup parts
1162 def VisuCable(groupName, groupGeomObj, parameters, name = "CABLE"):
1163     """
1164     Alias for class :class:`CircularBeam`.
1165     """
1166     return CircularBeam(groupName, groupGeomObj, parameters, name,
1167                         color = PURPLE)
1168
1169 ## Alias for class ThickShell.
1170 #  \ingroup parts
1171 def VisuCoque(groupName, groupGeomObj, parameters, name = "COQUE"):
1172     """
1173     Alias for class :class:`ThickShell`.
1174     """
1175     return ThickShell(groupName, groupGeomObj, parameters, name)
1176
1177 ## Alias for class Grid.
1178 #  \ingroup parts
1179 def VisuGrille(groupName, groupGeomObj, parameters, name = "GRILLE"):
1180     """
1181     Alias for class :class:`Grid`.
1182     """
1183     return Grid(groupName, groupGeomObj, parameters, name)