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