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