Salome HOME
c4e2f8fe725dc7c4a561f58b74677d4e626fcbac
[modules/geom.git] / src / GEOM_PY / structelem / parts.py
1 # -*- coding: utf-8 -*-
2 #
3 #  Copyright (C) 2007-2009       EDF R&D
4
5 #    This file is part of PAL_SRC.
6 #
7 #    PAL_SRC is free software; you can redistribute it and/or modify
8 #    it under the terms of the GNU General Public License as published by
9 #    the Free Software Foundation; either version 2 of the License, or
10 #    (at your option) any later version.
11 #
12 #    PAL_SRC is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU General Public License for more details.
16 #
17 #    You should have received a copy of the GNU General Public License
18 #    along with PAL_SRC; if not, write to the Free Software
19 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
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, planeSect):
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" % (-halfHZ) + " %g" % (-halfHY) + ":"
540         sketchStr += "TT %g" % (halfHZ) + " %g" % (-halfHY) + ":"
541         sketchStr += "TT %g" % (halfHZ) + " %g" % (halfHY) + ":" 
542         sketchStr += "TT %g" % (-halfHZ) + " %g" % (halfHY) + ":WW"
543         logger.debug('Drawing rectangle: "%s"' % sketchStr)
544         sketch = self.geom.MakeSketcherOnPlane(sketchStr, planeSect)
545         return sketch
546
547     def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
548         """
549         Create the rectangular sections used to build the pipe.
550         """
551         planeSect1 = self.geom.MakePlane(fPoint, fNormal, 1.0)
552         outerRect1 = self._makeRectangle(self.HY1, self.HZ1, planeSect1)
553         planeSect2 = self.geom.MakePlane(lPoint, lNormal, 1.0)
554         outerRect2 = self._makeRectangle(self.HY2, self.HZ2, planeSect2)
555         if self.filling == HOLLOW:
556             innerRect1 = self._makeRectangle(self.HY1 - 2 * self.EPY1,
557                                              self.HZ1 - 2 * self.EPZ1,
558                                              planeSect1)
559             innerRect2 = self._makeRectangle(self.HY2 - 2 * self.EPY2,
560                                              self.HZ2 - 2 * self.EPZ2,
561                                              planeSect2)
562         else:
563             innerRect1 = None
564             innerRect2 = None
565
566         return (outerRect1, innerRect1, outerRect2, innerRect2)
567
568
569 class StructuralElementPart2D(StructuralElementPart):
570     """
571     This class is an "abstract" class for all 2D structural element parts. It
572     should not be instantiated directly. See class
573     :class:`StructuralElementPart` for the description of the parameters.
574     """
575
576     DEFAULT_NAME = "StructuralElementPart2D"
577
578     def __init__(self, studyId, groupName, groupGeomObj, parameters,
579                  name = DEFAULT_NAME):
580         StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
581                                        parameters, name)
582         self._orientation = orientation.Orientation2D(
583                                         self._getParameter(["angleAlpha"]),
584                                         self._getParameter(["angleBeta"]),
585                                         self._getParameter(["Vecteur"]))
586         self.offset = self._getParameter(["Excentre"], 0.0)
587
588     def _makeFaceOffset(self, face, offset, epsilon = 1e-6):
589         """
590         Create a copy of a face at a given offset.
591         """
592         if abs(offset) < epsilon:
593             return self.geom.MakeCopy(face)
594         else:
595             offsetObj = self.geom.MakeOffset(face, offset)
596             # We have to explode the resulting object into faces because it is
597             # created as a polyhedron and not as a single face
598             faces = self.geom.SubShapeAll(offsetObj,
599                                           self.geom.ShapeType["FACE"])
600             return faces[0]
601
602     def _buildMarkersWithOffset(self, offset):
603         """
604         Build the markers for the structural element part with a given offset
605         from the base face.
606         """
607         uParam = 0.5
608         vParam = 0.5
609         listMarkers = []
610         subShapes = self._getSubShapes()
611     
612         for subShape in subShapes:
613             faces = self.geom.SubShapeAll(subShape,
614                                           self.geom.ShapeType["FACE"])
615             for face in faces:
616                 offsetFace = self._makeFaceOffset(face, offset)
617                 # get tangent plane on surface by parameters
618                 center = self.geom.MakeVertexOnSurface(offsetFace,
619                                                        uParam, vParam)
620                 tangPlane = self.geom.MakeTangentPlaneOnFace(offsetFace,
621                                                              uParam, vParam,
622                                                              1.0)
623                 normal = self.geom.GetNormal(tangPlane)
624                 marker = self._orientation.buildMarker(self.geom,
625                                                        center, normal)
626                 listMarkers.append(marker)
627
628         return listMarkers
629
630
631 class ThickShell(StructuralElementPart2D):
632     """
633     This class defines a shell with a given thickness. It can be shifted from
634     the base face. The valid parameters for thick shells are:
635
636     * "Epais": thickness of the shell.
637     * "Excentre": offset of the shell from the base face.
638     * "angleAlpha": angle used to build the markers (see class
639       :class:`~salome.geom.structelem.orientation.Orientation2D`)
640     * "angleBeta": angle used to build the markers (see class
641       :class:`~salome.geom.structelem.orientation.Orientation2D`)
642     * "Vecteur": vector used instead of the angles to build the markers (see
643       class :class:`~salome.geom.structelem.orientation.Orientation2D`)
644
645     See class :class:`StructuralElementPart` for the description of the
646     other parameters.
647     """
648
649     DEFAULT_NAME = "ThickShell"
650
651     def __init__(self, studyId, groupName, groupGeomObj, parameters,
652                  name = DEFAULT_NAME):
653         StructuralElementPart2D.__init__(self, studyId, groupName,
654                                          groupGeomObj, parameters, name)
655         self.thickness = self._getParameter(["Epais"])
656         logger.debug(repr(self))
657
658     def _buildPart(self):
659         """
660         Create the geometrical shapes corresponding to the thick shell.
661         """
662         subShapes = self._getSubShapes()
663         listSolids = []
664     
665         for subShape in subShapes:
666             faces = self.geom.SubShapeAll(subShape,
667                                           self.geom.ShapeType["FACE"])
668             for face in faces:
669                 shape = self._buildThickShellForFace(face)
670                 listSolids.append(shape)
671
672         if len(listSolids) == 0:
673             return None
674         elif len(listSolids) == 1:
675             return listSolids[0]
676         else:
677             return self.geom.MakeCompound(listSolids)
678
679     def _buildThickShellForFace(self, face):
680         """
681         Create the geometrical shapes corresponding to the thick shell for a
682         given face.
683         """
684         epsilon = 1e-6
685         if self.thickness < 2 * epsilon:
686             return self._makeFaceOffset(face, self.offset, epsilon)
687
688         upperOffset = self.offset + self.thickness / 2.0
689         lowerOffset = self.offset - self.thickness / 2.0
690         ruledMode = True
691         modeSolid = False
692
693         upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
694         lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
695         listShapes = [upperFace, lowerFace]
696         upperWires = self.geom.SubShapeAll(upperFace,
697                                            self.geom.ShapeType["WIRE"])
698         lowerWires = self.geom.SubShapeAll(lowerFace,
699                                            self.geom.ShapeType["WIRE"])
700         if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
701             # if the face is a cylinder, we remove the extra side edge
702             upperWires = self._removeCylinderExtraEdge(upperWires)
703             lowerWires = self._removeCylinderExtraEdge(lowerWires)
704         for i in range(len(upperWires)):
705             resShape = self.geom.MakeThruSections([upperWires[i],
706                                                    lowerWires[i]],
707                                                   modeSolid, epsilon,
708                                                   ruledMode)
709             listShapes.append(resShape)
710         resultShell = self.geom.MakeShell(listShapes)
711         resultSolid = self.geom.MakeSolid([resultShell])
712         return resultSolid
713
714     def _removeCylinderExtraEdge(self, wires):
715         """
716         Remove the side edge in a cylinder.
717         """
718         result = []
719         for wire in wires:
720             edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
721             for edge in edges:
722                 if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
723                     result.append(edge)
724         return result
725
726     def _buildMarkers(self):
727         """
728         Build the markers defining the orientation of the thick shell.
729         """
730         return self._buildMarkersWithOffset(self.offset +
731                                             self.thickness / 2.0)
732
733
734 class Grid(StructuralElementPart2D):
735     """
736     This class defines a grid. A grid is represented by a 2D face patterned
737     with small lines in the main direction of the grid frame. The valid
738     parameters for grids are:
739
740     * "Excentre": offset of the grid from the base face.
741     * "angleAlpha": angle used to build the markers (see class
742       :class:`~salome.geom.structelem.orientation.Orientation2D`)
743     * "angleBeta": angle used to build the markers (see class
744       :class:`~salome.geom.structelem.orientation.Orientation2D`)
745     * "Vecteur": vector used instead of the angles to build the markers (see
746       class :class:`~salome.geom.structelem.orientation.Orientation2D`)
747     * "origAxeX": X coordinate of the origin of the axis used to determine the
748       orientation of the frame in the case of a cylindrical grid.
749     * "origAxeY": Y coordinate of the origin of the axis used to determine the
750       orientation of the frame in the case of a cylindrical grid.
751     * "origAxeZ": Z coordinate of the origin of the axis used to determine the
752       orientation of the frame in the case of a cylindrical grid.
753     * "axeX": X coordinate of the axis used to determine the orientation of
754       the frame in the case of a cylindrical grid.
755     * "axeY": Y coordinate of the axis used to determine the orientation of
756       the frame in the case of a cylindrical grid.
757     * "axeZ": Z coordinate of the axis used to determine the orientation of
758       the frame in the case of a cylindrical grid.
759
760     See class :class:`StructuralElementPart` for the description of the
761     other parameters.
762     """
763
764     DEFAULT_NAME = "Grid"
765
766     def __init__(self, studyId, groupName, groupGeomObj, parameters,
767                  name = DEFAULT_NAME):
768         StructuralElementPart2D.__init__(self, studyId, groupName,
769                                          groupGeomObj, parameters, name)
770         self.xr = self._getParameter(["origAxeX"])
771         self.yr = self._getParameter(["origAxeY"])
772         self.zr = self._getParameter(["origAxeZ"])
773         self.vx = self._getParameter(["axeX"])
774         self.vy = self._getParameter(["axeY"])
775         self.vz = self._getParameter(["axeZ"])
776         logger.debug(repr(self))
777
778     def _buildPart(self):
779         """
780         Create the geometrical shapes representing the grid.
781         """
782         subShapes = self._getSubShapes()
783         listGridShapes = []
784     
785         for subShape in subShapes:
786             faces = self.geom.SubShapeAll(subShape,
787                                           self.geom.ShapeType["FACE"])
788             for face in faces:
789                 if self.geom.KindOfShape(face)[0] == \
790                                         self.geom.kind.CYLINDER2D and \
791                         self.xr is not None and self.yr is not None and \
792                         self.zr is not None and self.vx is not None and \
793                         self.vy is not None and self.vz is not None:
794                     shape = self._buildGridForCylinderFace(face)
795                 else:
796                     shape = self._buildGridForNormalFace(face)
797                 listGridShapes.append(shape)
798
799         if len(listGridShapes) == 0:
800             return None
801         elif len(listGridShapes) == 1:
802             return listGridShapes[0]
803         else:
804             return self.geom.MakeCompound(listGridShapes)
805
806     def _buildGridForNormalFace(self, face):
807         """
808         Create the geometrical shapes representing the grid for a given
809         non-cylindrical face.
810         """
811         baseFace = self._makeFaceOffset(face, self.offset)
812         gridList = [baseFace]
813         
814         # Compute display length for grid elements
815         p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
816         p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
817         length = self.geom.MinDistance(p1, p2) / 2.0
818
819         for u in range(1, 10):
820             uParam = u * 0.1
821             for v in range(1, 10):
822                 vParam = v * 0.1
823                 # get tangent plane on surface by parameters
824                 center = self.geom.MakeVertexOnSurface(baseFace,
825                                                        uParam, vParam)
826                 tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
827                                                              vParam, 1.0)
828                 
829                 # use the marker to get the orientation of the frame
830                 normal = self.geom.GetNormal(tangPlane)
831                 marker = self._orientation.buildMarker(self.geom, center,
832                                                        normal, False)
833                 [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
834                 xPoint = self.geom.MakeTranslation(center, Xx * length,
835                                                    Xy * length, Xz * length)
836                 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
837                 gridList.append(gridLine)
838         grid = self.geom.MakeCompound(gridList)
839         return grid
840
841     def _buildGridForCylinderFace(self, face):
842         """
843         Create the geometrical shapes representing the grid for a given
844         cylindrical face.
845         """
846         baseFace = self._makeFaceOffset(face, self.offset)
847         gridList = [baseFace]
848         
849         # Compute display length for grid elements
850         p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
851         p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
852         length = self.geom.MinDistance(p1, p2) / 2.0
853         
854         # Create reference vector V
855         origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
856         vPoint = self.geom.MakeTranslation(origPoint,
857                                            self.vx, self.vy, self.vz)
858         refVec = self.geom.MakeVector(origPoint, vPoint)
859
860         for u in range(10):
861             uParam = u * 0.1
862             for v in range(1, 10):
863                 vParam = v * 0.1
864                 
865                 # Compute the local orientation of the frame
866                 center = self.geom.MakeVertexOnSurface(baseFace,
867                                                        uParam, vParam)
868                 locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
869                                                          vPoint, 1.0)
870                 locOrient = self.geom.GetNormal(locPlaneYZ)
871                 xPoint = self.geom.MakeTranslationVectorDistance(center,
872                                                                  locOrient,
873                                                                  length)
874                 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
875                 gridList.append(gridLine)
876
877         grid = self.geom.MakeCompound(gridList)
878         return grid
879
880     def _buildMarkers(self):
881         """
882         Create the markers defining the orientation of the grid.
883         """
884         return self._buildMarkersWithOffset(self.offset)
885
886
887 def VisuPoutreGenerale(studyId, groupName, groupGeomObj, parameters,
888                        name = "POUTRE"):
889     """
890     Alias for class :class:`GeneralBeam`.
891     """
892     return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
893
894 def VisuPoutreCercle(studyId, groupName, groupGeomObj, parameters,
895                      name = "POUTRE"):
896     """
897     Alias for class :class:`CircularBeam`.
898     """
899     return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
900   
901 def VisuPoutreRectangle(studyId, groupName, groupGeomObj, parameters,
902                         name = "POUTRE"):
903     """
904     Alias for class :class:`RectangularBeam`.
905     """
906     return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
907   
908 def VisuBarreGenerale(studyId, groupName, groupGeomObj, parameters,
909                       name = "BARRE"):
910     """
911     Alias for class :class:`GeneralBeam`.
912     """
913     return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
914       
915 def VisuBarreRectangle(studyId, groupName, groupGeomObj, parameters,
916                        name = "BARRE"):
917     """
918     Alias for class :class:`RectangularBeam`.
919     """
920     return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
921
922 def VisuBarreCercle(studyId, groupName, groupGeomObj, parameters,
923                     name = "BARRE"):
924     """
925     Alias for class :class:`CircularBeam`.
926     """
927     return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
928
929 def VisuCable(studyId, groupName, groupGeomObj, parameters, name = "CABLE"):
930     """
931     Alias for class :class:`CircularBeam`.
932     """
933     return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
934
935 def VisuCoque(studyId, groupName, groupGeomObj, parameters, name = "COQUE"):
936     """
937     Alias for class :class:`ThickShell`.
938     """
939     return ThickShell(studyId, groupName, groupGeomObj, parameters, name)
940   
941 def VisuGrille(studyId, groupName, groupGeomObj, parameters, name = "GRILLE"):
942     """
943     Alias for class :class:`Grid`.
944     """
945     return Grid(studyId, groupName, groupGeomObj, parameters, name)