Back to index

salome-geom  6.5.0
parts.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 #
00003 # Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
00004 #
00005 # This library is free software; you can redistribute it and/or
00006 # modify it under the terms of the GNU Lesser General Public
00007 # License as published by the Free Software Foundation; either
00008 # version 2.1 of the License.
00009 #
00010 # This library is distributed in the hope that it will be useful,
00011 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 # Lesser General Public License for more details.
00014 #
00015 # You should have received a copy of the GNU Lesser General Public
00016 # License along with this library; if not, write to the Free Software
00017 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00018 #
00019 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
00020 #
00021 """
00022 This module defines the different structural element parts. It is used to
00023 build the geometric shapes of the structural elements. It should not be used
00024 directly in the general case. Structural elements should be created by the
00025 class :class:`~salome.geom.structelem.StructuralElementManager`.
00026 """
00027 
00028 import math
00029 
00030 import salome
00031 import SALOMEDS
00032 
00033 from salome.kernel.logger import Logger
00034 from salome.kernel import termcolor
00035 logger = Logger("salome.geom.structelem.parts", color = termcolor.RED)
00036 from salome.geom.geomtools import getGeompy
00037 
00038 import orientation
00039 
00040 # Filling for the beams
00041 FULL = "FULL"
00042 HOLLOW = "HOLLOW"
00043 
00044 # Minimum dimension for the shapes to extrude
00045 MIN_DIM_FOR_EXTRUDED_SHAPE = 2e-4
00046 MIN_LENGTH_FOR_EXTRUSION = 1e-4
00047 MIN_THICKNESS = 1e-5
00048 
00049 # Colors for the structural elements
00050 GREEN = SALOMEDS.Color(0.0, 1.0, 0.0)
00051 LIGHT_GREEN = SALOMEDS.Color(0.0, 1.0, 170.0/255.0)
00052 BLUE = SALOMEDS.Color(0.0, 0.0, 1.0)
00053 LIGHT_BLUE = SALOMEDS.Color(0.0, 0.5, 1.0)
00054 RED = SALOMEDS.Color(1.0, 0.0, 0.0)
00055 LIGHT_RED = SALOMEDS.Color(1.0, 0.5, 0.5)
00056 PURPLE = SALOMEDS.Color(170.0/255.0, 85.0/255.0, 1.0)
00057 ORANGE = SALOMEDS.Color(1.0, 170.0/255.0, 0.0)
00058 
00059 
00060 class InvalidParameterError(Exception):
00061     """
00062     This exception is raised when an invalid parameter is used to build a
00063     structural element part.
00064     """
00065     
00066     def __init__(self, groupName, expression, minValue, value):
00067         self.groupName = groupName
00068         self.expression = expression
00069         self.minValue = minValue
00070         self.value = value
00071         
00072     def __str__(self):
00073         return "%s < %g (%s = %g in %s)" % (self.expression, self.minValue,
00074                                             self.expression, self.value,
00075                                             self.groupName)
00076 
00077 
00078 class SubShapeID:
00079     """
00080     This class enables the use of sub-shapes in sets or as dictionary keys.
00081     It implements __eq__ and __hash__ methods so that sub-shapes with the same
00082     CORBA object `mainShape` and the same `id` are considered equal.
00083     """
00084 
00085     def __init__(self, mainShape, id):
00086         self._mainShape = mainShape
00087         self._id = id
00088 
00089     def getObj(self, geom):
00090         """
00091         Return the sub-shape (GEOM object). `geom` is a pseudo-geompy object
00092         used to find the geometrical object.
00093         """
00094         return geom.GetSubShape(self._mainShape, [self._id])
00095     
00096     def __eq__(self, other):
00097         return self._mainShape._is_equivalent(other._mainShape) and \
00098                self._id == other._id
00099     
00100     def __hash__(self):
00101         return self._mainShape._hash(2147483647) ^ self._id
00102 
00103 
00104 class StructuralElementPart:
00105     """
00106     This class is the base class for all structural element parts. It should
00107     not be instantiated directly (consider it as an "abstract" class).
00108 
00109     :type  studyId: integer
00110     :param studyId: the ID of the study in which the part is created.
00111 
00112     :type  groupName: string
00113     :param groupName: the name of the underlying geometrical primitive in the
00114                       study.
00115 
00116     :type  groupGeomObj: GEOM object
00117     :param groupGeomObj: the underlying geometrical primitive.
00118 
00119     :type  parameters: dictionary
00120     :param parameters: parameters defining the structural element (see
00121                        subclasses for details).
00122 
00123     :type  name: string
00124     :param name: name to use for the created object in the study.
00125 
00126     """
00127     
00128     DEFAULT_NAME = "StructElemPart"
00129 
00130     def __init__(self, studyId, groupName, groupGeomObj, parameters,
00131                  name = DEFAULT_NAME, color = None):
00132         self._parameters = parameters
00133         self.groupName = groupName
00134         self._groupGeomObj = groupGeomObj
00135         self._orientation = None
00136         self._paramUserName = {}
00137         self.name = name
00138         self.geom = getGeompy(studyId)
00139         self.baseShapesSet = set()
00140         self.isMainShape = groupGeomObj.IsMainShape()
00141         if not self.isMainShape:
00142             mainShape = self.geom.GetMainShape(groupGeomObj)
00143             listIDs = self.geom.GetObjectIDs(groupGeomObj)
00144             if mainShape is not None and listIDs is not None:
00145                 for id in listIDs:
00146                     self.baseShapesSet.add(SubShapeID(mainShape, id))
00147         self.color = color
00148         if self.color is None:
00149             self.color = self._groupGeomObj.GetColor()
00150 
00151     def _getParameter(self, nameList, default = None):
00152         """
00153         This method finds the value of a parameter in the parameters
00154         dictionary. The argument is a list because some parameters can have
00155         several different names.
00156         """
00157         if len(nameList) > 0:
00158             paramName = nameList[0]
00159         for name in nameList:
00160             if self._parameters.has_key(name):
00161                 self._paramUserName[paramName] = name
00162                 return self._parameters[name]
00163         return default
00164 
00165     def _getParamUserName(self, paramName):
00166         """
00167         This method finds the user name for a parameter.
00168         """
00169         if self._paramUserName.has_key(paramName):
00170             return self._paramUserName[paramName]
00171         else:
00172             return paramName
00173 
00174     def __repr__(self):
00175         reprdict = self.__dict__.copy()
00176         del reprdict["_parameters"]
00177         del reprdict["groupName"]
00178         del reprdict["_groupGeomObj"]
00179         del reprdict["_paramUserName"]
00180         del reprdict["name"]
00181         del reprdict["geom"]
00182         del reprdict["baseShapesSet"]
00183         return '%s("%s", %s)' % (self.__class__.__name__, self.groupName,
00184                                  reprdict)
00185 
00186     def addOrientation(self, orientParams):
00187         """
00188         Add orientation information to the structural element part. See class
00189         :class:`~salome.geom.structelem.orientation.Orientation1D` for the description
00190         of the parameters.
00191         """
00192         self._orientation.addParams(orientParams)
00193 
00194     def _checkSize(self, value, mindim, expression):
00195         """
00196         This method checks that some parameters or some expressions involving
00197         those parameters are greater than a minimum value.
00198         """
00199         if value < mindim:
00200             raise InvalidParameterError(self.groupName, expression,
00201                                         mindim, value)
00202 
00203     def build(self):
00204         """
00205         Build the geometric shapes and the markers corresponding to the
00206         structural element part in the study `studyId`.
00207         """
00208         shape = self._buildPart()
00209         markers = self._buildMarkers()
00210         shape.SetColor(self.color)
00211         for marker in markers:
00212             marker.SetColor(self.color)
00213         return (shape, markers)
00214 
00215     def _buildPart(self):
00216         """
00217         This abstract method must be implemented in subclasses and should
00218         create the geometrical shape(s) of the structural element part.
00219         """
00220         raise NotImplementedError("Method _buildPart not implemented in class"
00221                                   " %s (it must be implemented in "
00222                                   "StructuralElementPart subclasses)." %
00223                                   self.__class__.__name__)
00224 
00225     def _buildMarkers(self):
00226         """
00227         This abstract method must be implemented in subclasses and should
00228         create the markers defining the orientation of the structural element
00229         part.
00230         """
00231         raise NotImplementedError("Method _buildMarker not implemented in "
00232                                   "class %s (it must be implemented in "
00233                                   "StructuralElementPart subclasses)." %
00234                                   self.__class__.__name__)
00235 
00236     def _getSubShapes(self, minDim = MIN_LENGTH_FOR_EXTRUSION):
00237         """
00238         Find and return the base sub-shapes in the structural element part.
00239         """
00240         if self.isMainShape:
00241             return [self._groupGeomObj]
00242         subShapes = []
00243         for subShapeID in self.baseShapesSet:
00244             subShape = subShapeID.getObj(self.geom)
00245             length = self.geom.BasicProperties(subShape)[0]
00246             if length < minDim:
00247                 logger.warning("Length too short (%s - ID %s, length = %g), "
00248                                "subshape will not be used in structural "
00249                                "element" % (self.groupName, subShapeID._id,
00250                                             length))
00251             else:
00252                 subShapes.append(subShape)
00253         return subShapes
00254 
00255 
00256 class Beam(StructuralElementPart):
00257     """
00258     This class is an "abstract" class for all 1D structural element parts. It
00259     should not be instantiated directly. See class
00260     :class:`StructuralElementPart` for the description of the parameters.
00261     """
00262 
00263     DEFAULT_NAME = "Beam"
00264 
00265     def __init__(self, studyId, groupName, groupGeomObj, parameters,
00266                  name = DEFAULT_NAME, color = None):
00267         StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
00268                                        parameters, name, color)
00269         self._orientation = orientation.Orientation1D()
00270 
00271     def _isReversed(self, path):
00272         """
00273         This method checks if a 1D object is "reversed", i.e. if its
00274         orientation is different than the orientation of the underlying OCC
00275         object.
00276         """
00277         length = self.geom.BasicProperties(path)[0]
00278         p1 = self.geom.MakeVertexOnCurve(path, 0.0)
00279         p2 = self.geom.GetFirstVertex(path)
00280         dist = self.geom.MinDistance(p1, p2)
00281         return dist > length / 2
00282 
00283     def _getVertexAndTangentOnOrientedWire(self, path, param):
00284         """
00285         Get a vertex and the corresponding tangent on a wire by parameter.
00286         This method takes into account the "real" orientation of the wire
00287         (i.e. the orientation of the underlying OCC object).
00288         """
00289         if self._isReversed(path):
00290             vertex = self.geom.MakeVertexOnCurve(path, 1.0 - param)
00291             invtangent = self.geom.MakeTangentOnCurve(path, 1.0 - param)
00292             tanpoint = self.geom.MakeTranslationVectorDistance(vertex,
00293                                                                invtangent,
00294                                                                -1.0)
00295             tangent = self.geom.MakeVector(vertex, tanpoint)
00296         else:
00297             vertex = self.geom.MakeVertexOnCurve(path, param)
00298             tangent = self.geom.MakeTangentOnCurve(path, param)
00299         return (vertex, tangent)
00300 
00301     def _makeSolidPipeFromWires(self, wire1, wire2, point1, point2, path):
00302         """
00303         Create a solid by the extrusion of section `wire1` to section `wire2`
00304         along `path`.
00305         """
00306         face1 = self.geom.MakeFace(wire1, True)
00307         face2 = self.geom.MakeFace(wire2, True)
00308         shell = self.geom.MakePipeWithDifferentSections([wire1, wire2],
00309                                                         [point1, point2],
00310                                                         path, False, False)
00311         closedShell = self.geom.MakeShell([face1, face2, shell])
00312         solid = self.geom.MakeSolid([closedShell])
00313         return solid
00314 
00315     def _buildPart(self):
00316         """
00317         Build the structural element part.
00318         """
00319         # Get all the sub-shapes in the group (normally only edges and wires)
00320         paths = self._getSubShapes()
00321         listPipes = []
00322         withContact = False
00323         withCorrection = False
00324     
00325         for path in paths:
00326             # Build the sections (rectangular or circular) at each end of the
00327             # beam
00328             (fPoint, fNormal) = self._getVertexAndTangentOnOrientedWire(path,
00329                                                                         0.0)
00330             (lPoint, lNormal) = self._getVertexAndTangentOnOrientedWire(path,
00331                                                                         1.0)
00332             (outerWire1, innerWire1, outerWire2, innerWire2) = \
00333                     self._makeSectionWires(fPoint, fNormal, lPoint, lNormal)
00334 
00335             # Create the resulting solid
00336             outerSolid = self._makeSolidPipeFromWires(outerWire1, outerWire2,
00337                                                       fPoint, lPoint, path)
00338             if self.filling == HOLLOW:
00339                 innerSolid = self._makeSolidPipeFromWires(innerWire1,
00340                                                           innerWire2, fPoint,
00341                                                           lPoint, path)
00342                 resultSolid = self.geom.MakeCut(outerSolid, innerSolid)
00343                 listPipes.append(resultSolid)
00344             else:
00345                 listPipes.append(outerSolid)
00346 
00347         if len(listPipes) == 0:
00348             return None
00349         elif len(listPipes) == 1:
00350             return listPipes[0]
00351         else:
00352             return self.geom.MakeCompound(listPipes)
00353 
00354     def _buildMarkers(self):
00355         """
00356         Build the markers defining the orientation of the structural element
00357         part.
00358         """
00359         param = 0.5
00360         paths = self._getSubShapes()
00361         listMarkers = []
00362         for path in paths:
00363             (center, vecX) = self._getVertexAndTangentOnOrientedWire(path,
00364                                                                      param)
00365             marker = self._orientation.buildMarker(self.geom, center, vecX)
00366             listMarkers.append(marker)
00367         return listMarkers
00368 
00369 
00370 class CircularBeam(Beam):
00371     """
00372     This class defines a beam with a circular section. It can be full or
00373     hollow, and its radius and thickness can vary from one end of the beam to
00374     the other. The valid parameters for circular beams are:
00375 
00376     * "R1" or "R": radius at the first end of the beam.
00377     * "R2" or "R": radius at the other end of the beam.
00378     * "EP1" or "EP" (optional): thickness at the first end of the beam.
00379       If not specified or equal to 0, the beam is considered full.
00380     * "EP2" or "EP" (optional): thickness at the other end of the beam.
00381       If not specified or equal to 0, the beam is considered full.
00382 
00383     See class :class:`StructuralElementPart` for the description of the
00384     other parameters.
00385 
00386     """
00387 
00388     def __init__(self, studyId, groupName, groupGeomObj, parameters,
00389                  name = Beam.DEFAULT_NAME, color = None):
00390         if color is None:
00391             if parameters.has_key("R1"): # variable section
00392                 color = LIGHT_RED
00393             else:                       # constant section
00394                 color = RED
00395 
00396         Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
00397                       name, color)
00398 
00399         self.R1 = self._getParameter(["R1", "R"])
00400         self.R2 = self._getParameter(["R2", "R"])
00401         self.EP1 = self._getParameter(["EP1", "EP"])
00402         self.EP2 = self._getParameter(["EP2", "EP"])
00403 
00404         if self.EP1 is None or self.EP2 is None or \
00405                                 self.EP1 == 0 or self.EP2 == 0:
00406             self.filling = FULL
00407         else:
00408             self.filling = HOLLOW
00409 
00410         logger.debug(repr(self))
00411 
00412         # Check parameters
00413         self._checkSize(self.R1, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
00414                         self._getParamUserName("R1"))
00415         self._checkSize(self.R2, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
00416                         self._getParamUserName("R2"))
00417         if self.filling == HOLLOW:
00418             self._checkSize(self.EP1, MIN_THICKNESS,
00419                             self._getParamUserName("EP1"))
00420             self._checkSize(self.EP2, MIN_THICKNESS,
00421                             self._getParamUserName("EP2"))
00422             self._checkSize(self.R1 - self.EP1,
00423                             MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
00424                             "%s - %s" % (self._getParamUserName("R1"),
00425                                          self._getParamUserName("EP1")))
00426             self._checkSize(self.R2 - self.EP2,
00427                             MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
00428                             "%s - %s" % (self._getParamUserName("R2"),
00429                                          self._getParamUserName("EP2")))
00430 
00431     def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
00432         """
00433         Create the circular sections used to build the pipe.
00434         """
00435         outerCircle1 = self.geom.MakeCircle(fPoint, fNormal, self.R1)
00436         outerCircle2 = self.geom.MakeCircle(lPoint, lNormal, self.R2)
00437         if self.filling == HOLLOW:
00438             innerCircle1 = self.geom.MakeCircle(fPoint, fNormal,
00439                                                 self.R1 - self.EP1)
00440             innerCircle2 = self.geom.MakeCircle(lPoint, lNormal,
00441                                                 self.R2 - self.EP2)
00442         else:
00443             innerCircle1 = None
00444             innerCircle2 = None
00445 
00446         return (outerCircle1, innerCircle1, outerCircle2, innerCircle2)
00447 
00448 
00449 class RectangularBeam(Beam):
00450     """
00451     This class defines a beam with a rectangular section. It can be full or
00452     hollow, and its dimensions can vary from one end of the beam to the other.
00453     The valid parameters for rectangular beams are:
00454 
00455     * "HY1", "HY", "H1" or "H": width at the first end of the beam.
00456     * "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
00457     * "HY2", "HY", "H2" or "H": width at the other end of the beam.
00458     * "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
00459     * "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
00460       direction at the first end of the beam. If not specified or equal to 0,
00461       the beam is considered full.
00462     * "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
00463       direction at the first end of the beam. If not specified or equal to 0,
00464       the beam is considered full.
00465     * "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
00466       direction at the other end of the beam. If not specified or equal to 0,
00467       the beam is considered full.
00468     * "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
00469       direction at the other end of the beam. If not specified or equal to 0,
00470       the beam is considered full.
00471 
00472     See class :class:`StructuralElementPart` for the description of the
00473     other parameters.
00474 
00475     """
00476 
00477     def __init__(self, studyId, groupName, groupGeomObj, parameters,
00478                  name = Beam.DEFAULT_NAME, color = None):
00479         if color is None:
00480             if parameters.has_key("HY1") or parameters.has_key("H1"):
00481                 color = LIGHT_BLUE # variable section
00482             else:                  # constant section
00483                 color = BLUE
00484 
00485         Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
00486                       name, color)
00487 
00488         self.HY1 = self._getParameter(["HY1", "HY", "H1", "H"])
00489         self.HZ1 = self._getParameter(["HZ1", "HZ", "H1", "H"])
00490         self.HY2 = self._getParameter(["HY2", "HY", "H2", "H"])
00491         self.HZ2 = self._getParameter(["HZ2", "HZ", "H2", "H"])
00492         self.EPY1 = self._getParameter(["EPY1", "EPY", "EP1", "EP"])
00493         self.EPZ1 = self._getParameter(["EPZ1", "EPZ", "EP1", "EP"])
00494         self.EPY2 = self._getParameter(["EPY2", "EPY", "EP2", "EP"])
00495         self.EPZ2 = self._getParameter(["EPZ2", "EPZ", "EP2", "EP"])
00496 
00497         if self.EPY1 is None or self.EPZ1 is None or \
00498            self.EPY2 is None or self.EPZ2 is None or \
00499            self.EPY1 == 0 or self.EPZ1 == 0 or \
00500            self.EPY2 == 0 or self.EPZ2 == 0:
00501             self.filling = FULL
00502         else:
00503             self.filling = HOLLOW
00504 
00505         logger.debug(repr(self))
00506 
00507         # Check parameters
00508         self._checkSize(self.HY1, MIN_DIM_FOR_EXTRUDED_SHAPE,
00509                         self._getParamUserName("HY1"))
00510         self._checkSize(self.HZ1, MIN_DIM_FOR_EXTRUDED_SHAPE,
00511                         self._getParamUserName("HZ1"))
00512         self._checkSize(self.HY2, MIN_DIM_FOR_EXTRUDED_SHAPE,
00513                         self._getParamUserName("HY2"))
00514         self._checkSize(self.HZ2, MIN_DIM_FOR_EXTRUDED_SHAPE,
00515                         self._getParamUserName("HZ2"))
00516         if self.filling == HOLLOW:
00517             self._checkSize(self.EPY1, MIN_THICKNESS,
00518                             self._getParamUserName("EPY1"))
00519             self._checkSize(self.EPZ1, MIN_THICKNESS,
00520                             self._getParamUserName("EPZ1"))
00521             self._checkSize(self.EPY2, MIN_THICKNESS,
00522                             self._getParamUserName("EPY2"))
00523             self._checkSize(self.EPZ2, MIN_THICKNESS,
00524                             self._getParamUserName("EPZ2"))
00525             self._checkSize(self.HY1 - 2 * self.EPY1,
00526                             MIN_DIM_FOR_EXTRUDED_SHAPE,
00527                             "%s - 2 * %s" % (self._getParamUserName("HY1"),
00528                                              self._getParamUserName("EPY1")))
00529             self._checkSize(self.HZ1 - 2 * self.EPZ1,
00530                             MIN_DIM_FOR_EXTRUDED_SHAPE,
00531                             "%s - 2 * %s" % (self._getParamUserName("HZ1"),
00532                                              self._getParamUserName("EPZ1")))
00533             self._checkSize(self.HY2 - 2 * self.EPY2,
00534                             MIN_DIM_FOR_EXTRUDED_SHAPE,
00535                             "%s - 2 * %s" % (self._getParamUserName("HY2"),
00536                                              self._getParamUserName("EPY2")))
00537             self._checkSize(self.HZ2 - 2 * self.EPZ2,
00538                             MIN_DIM_FOR_EXTRUDED_SHAPE,
00539                             "%s - 2 * %s" % (self._getParamUserName("HZ2"),
00540                                              self._getParamUserName("EPZ2")))
00541 
00542     def _makeRectangle(self, HY, HZ, lcs):
00543         """
00544         Create a rectangle in the specified plane.
00545         """
00546         halfHY = HY / 2.0
00547         halfHZ = HZ / 2.0
00548         sketchStr = "Sketcher:F %g %g:" % (-halfHY, -halfHZ)
00549         sketchStr += "TT %g %g:" % (halfHY, -halfHZ)
00550         sketchStr += "TT %g %g:" % (halfHY, halfHZ)
00551         sketchStr += "TT %g %g:WW" % (-halfHY, halfHZ)
00552         logger.debug('Drawing rectangle: "%s"' % sketchStr)
00553         sketch = self.geom.MakeSketcherOnPlane(sketchStr, lcs)
00554         return sketch
00555 
00556     def _makeSectionRectangles(self, point, vecX, HY, HZ, EPY, EPZ):
00557         """
00558         Create one side of the rectangular sections used to build the pipe.
00559         """
00560         (vecY, vecZ) = self._orientation.getVecYZ(self.geom, point, vecX)
00561         lcs = self.geom.MakeMarkerPntTwoVec(point, vecY, vecZ)
00562         outerRect = self._makeRectangle(HY, HZ, lcs)
00563         if self.filling == HOLLOW:
00564             innerRect = self._makeRectangle(HY - 2.0 * EPY,
00565                                             HZ - 2.0 * EPZ,
00566                                             lcs)
00567         else:
00568             innerRect = None
00569         return (outerRect, innerRect)
00570 
00571     def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
00572         """
00573         Create the rectangular sections used to build the pipe.
00574         """
00575         (outerRect1, innerRect1) = \
00576             self._makeSectionRectangles(fPoint, fNormal, self.HY1, self.HZ1,
00577                                         self.EPY1, self.EPZ1)
00578         (outerRect2, innerRect2) = \
00579             self._makeSectionRectangles(lPoint, lNormal, self.HY2, self.HZ2,
00580                                         self.EPY2, self.EPZ2)
00581         return (outerRect1, innerRect1, outerRect2, innerRect2)
00582 
00583 
00584 def getParameterInDict(nameList, parametersDict, default = None):
00585     """
00586     This method finds the value of a parameter in the parameters
00587     dictionary. The argument is a list because some parameters can have
00588     several different names.
00589     """
00590     for name in nameList:
00591         if parametersDict.has_key(name):
00592             return parametersDict[name]
00593     return default
00594 
00595 
00596 class GeneralBeam(RectangularBeam):
00597     """
00598     This class defines a beam with a generic section. It is represented as a
00599     full rectangular beam with the following parameters:
00600     
00601     * HY1 = sqrt(12 * IZ1 / A1)
00602     * HZ1 = sqrt(12 * IY1 / A1)
00603     * HY2 = sqrt(12 * IZ2 / A2)
00604     * HZ2 = sqrt(12 * IY2 / A2)
00605     
00606     See class :class:`StructuralElementPart` for the description of the other
00607     parameters.
00608     """
00609 
00610     def __init__(self, studyId, groupName, groupGeomObj, parameters,
00611                  name = Beam.DEFAULT_NAME, color = None):
00612         self.IY1 = getParameterInDict(["IY1", "IY"], parameters)
00613         self.IZ1 = getParameterInDict(["IZ1", "IZ"], parameters)
00614         self.IY2 = getParameterInDict(["IY2", "IY"], parameters)
00615         self.IZ2 = getParameterInDict(["IZ2", "IZ"], parameters)
00616         self.A1 = getParameterInDict(["A1", "A"], parameters)
00617         self.A2 = getParameterInDict(["A2", "A"], parameters)
00618         parameters["HY1"] = math.sqrt(12 * self.IZ1 / self.A1)
00619         parameters["HZ1"] = math.sqrt(12 * self.IY1 / self.A1)
00620         parameters["HY2"] = math.sqrt(12 * self.IZ2 / self.A2)
00621         parameters["HZ2"] = math.sqrt(12 * self.IY2 / self.A2)
00622 
00623         if color is None:
00624             if parameters.has_key("IY1"): # variable section
00625                 color = LIGHT_GREEN
00626             else:                         # constant section
00627                 color = GREEN
00628 
00629         RectangularBeam.__init__(self, studyId, groupName, groupGeomObj, parameters,
00630                                  name, color)
00631 
00632 
00633 class StructuralElementPart2D(StructuralElementPart):
00634     """
00635     This class is an "abstract" class for all 2D structural element parts. It
00636     should not be instantiated directly. See class
00637     :class:`StructuralElementPart` for the description of the parameters.
00638     """
00639 
00640     DEFAULT_NAME = "StructuralElementPart2D"
00641 
00642     def __init__(self, studyId, groupName, groupGeomObj, parameters,
00643                  name = DEFAULT_NAME):
00644         StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
00645                                        parameters, name)
00646         self._orientation = orientation.Orientation2D(
00647                                         self._getParameter(["angleAlpha"]),
00648                                         self._getParameter(["angleBeta"]),
00649                                         self._getParameter(["Vecteur"]))
00650         self.offset = self._getParameter(["Excentre"], 0.0)
00651 
00652     def _makeFaceOffset(self, face, offset, epsilon = 1e-6):
00653         """
00654         Create a copy of a face at a given offset.
00655         """
00656         if abs(offset) < epsilon:
00657             return self.geom.MakeCopy(face)
00658         else:
00659             offsetObj = self.geom.MakeOffset(face, offset)
00660             # We have to explode the resulting object into faces because it is
00661             # created as a polyhedron and not as a single face
00662             faces = self.geom.SubShapeAll(offsetObj,
00663                                           self.geom.ShapeType["FACE"])
00664             return faces[0]
00665 
00666     def _buildMarkersWithOffset(self, offset):
00667         """
00668         Build the markers for the structural element part with a given offset
00669         from the base face.
00670         """
00671         uParam = 0.5
00672         vParam = 0.5
00673         listMarkers = []
00674         subShapes = self._getSubShapes()
00675     
00676         for subShape in subShapes:
00677             faces = self.geom.SubShapeAll(subShape,
00678                                           self.geom.ShapeType["FACE"])
00679             for face in faces:
00680                 offsetFace = self._makeFaceOffset(face, offset)
00681                 # get tangent plane on surface by parameters
00682                 center = self.geom.MakeVertexOnSurface(offsetFace,
00683                                                        uParam, vParam)
00684                 tangPlane = self.geom.MakeTangentPlaneOnFace(offsetFace,
00685                                                              uParam, vParam,
00686                                                              1.0)
00687                 normal = self.geom.GetNormal(tangPlane)
00688                 marker = self._orientation.buildMarker(self.geom,
00689                                                        center, normal)
00690                 listMarkers.append(marker)
00691 
00692         return listMarkers
00693 
00694 
00695 class ThickShell(StructuralElementPart2D):
00696     """
00697     This class defines a shell with a given thickness. It can be shifted from
00698     the base face. The valid parameters for thick shells are:
00699 
00700     * "Epais": thickness of the shell.
00701     * "Excentre": offset of the shell from the base face.
00702     * "angleAlpha": angle used to build the markers (see class
00703       :class:`~salome.geom.structelem.orientation.Orientation2D`)
00704     * "angleBeta": angle used to build the markers (see class
00705       :class:`~salome.geom.structelem.orientation.Orientation2D`)
00706     * "Vecteur": vector used instead of the angles to build the markers (see
00707       class :class:`~salome.geom.structelem.orientation.Orientation2D`)
00708 
00709     See class :class:`StructuralElementPart` for the description of the
00710     other parameters.
00711     """
00712 
00713     DEFAULT_NAME = "ThickShell"
00714 
00715     def __init__(self, studyId, groupName, groupGeomObj, parameters,
00716                  name = DEFAULT_NAME):
00717         StructuralElementPart2D.__init__(self, studyId, groupName,
00718                                          groupGeomObj, parameters, name)
00719         self.thickness = self._getParameter(["Epais"])
00720         logger.debug(repr(self))
00721 
00722     def _buildPart(self):
00723         """
00724         Create the geometrical shapes corresponding to the thick shell.
00725         """
00726         subShapes = self._getSubShapes()
00727         listSolids = []
00728     
00729         for subShape in subShapes:
00730             faces = self.geom.SubShapeAll(subShape,
00731                                           self.geom.ShapeType["FACE"])
00732             for face in faces:
00733                 shape = self._buildThickShellForFace(face)
00734                 listSolids.append(shape)
00735 
00736         if len(listSolids) == 0:
00737             return None
00738         elif len(listSolids) == 1:
00739             return listSolids[0]
00740         else:
00741             return self.geom.MakeCompound(listSolids)
00742 
00743     def _buildThickShellForFace(self, face):
00744         """
00745         Create the geometrical shapes corresponding to the thick shell for a
00746         given face.
00747         """
00748         epsilon = 1e-6
00749         if self.thickness < 2 * epsilon:
00750             return self._makeFaceOffset(face, self.offset, epsilon)
00751 
00752         upperOffset = self.offset + self.thickness / 2.0
00753         lowerOffset = self.offset - self.thickness / 2.0
00754         ruledMode = True
00755         modeSolid = False
00756 
00757         upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
00758         lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
00759         listShapes = [upperFace, lowerFace]
00760         upperWires = self.geom.SubShapeAll(upperFace,
00761                                            self.geom.ShapeType["WIRE"])
00762         lowerWires = self.geom.SubShapeAll(lowerFace,
00763                                            self.geom.ShapeType["WIRE"])
00764         if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
00765             # if the face is a cylinder, we remove the extra side edge
00766             upperWires = self._removeCylinderExtraEdge(upperWires)
00767             lowerWires = self._removeCylinderExtraEdge(lowerWires)
00768         for i in range(len(upperWires)):
00769             resShape = self.geom.MakeThruSections([upperWires[i],
00770                                                    lowerWires[i]],
00771                                                   modeSolid, epsilon,
00772                                                   ruledMode)
00773             listShapes.append(resShape)
00774         resultShell = self.geom.MakeShell(listShapes)
00775         resultSolid = self.geom.MakeSolid([resultShell])
00776         return resultSolid
00777 
00778     def _removeCylinderExtraEdge(self, wires):
00779         """
00780         Remove the side edge in a cylinder.
00781         """
00782         result = []
00783         for wire in wires:
00784             edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
00785             for edge in edges:
00786                 if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
00787                     result.append(edge)
00788         return result
00789 
00790     def _buildMarkers(self):
00791         """
00792         Build the markers defining the orientation of the thick shell.
00793         """
00794         return self._buildMarkersWithOffset(self.offset +
00795                                             self.thickness / 2.0)
00796 
00797 
00798 class Grid(StructuralElementPart2D):
00799     """
00800     This class defines a grid. A grid is represented by a 2D face patterned
00801     with small lines in the main direction of the grid frame. The valid
00802     parameters for grids are:
00803 
00804     * "Excentre": offset of the grid from the base face.
00805     * "angleAlpha": angle used to build the markers (see class
00806       :class:`~salome.geom.structelem.orientation.Orientation2D`)
00807     * "angleBeta": angle used to build the markers (see class
00808       :class:`~salome.geom.structelem.orientation.Orientation2D`)
00809     * "Vecteur": vector used instead of the angles to build the markers (see
00810       class :class:`~salome.geom.structelem.orientation.Orientation2D`)
00811     * "origAxeX": X coordinate of the origin of the axis used to determine the
00812       orientation of the frame in the case of a cylindrical grid.
00813     * "origAxeY": Y coordinate of the origin of the axis used to determine the
00814       orientation of the frame in the case of a cylindrical grid.
00815     * "origAxeZ": Z coordinate of the origin of the axis used to determine the
00816       orientation of the frame in the case of a cylindrical grid.
00817     * "axeX": X coordinate of the axis used to determine the orientation of
00818       the frame in the case of a cylindrical grid.
00819     * "axeY": Y coordinate of the axis used to determine the orientation of
00820       the frame in the case of a cylindrical grid.
00821     * "axeZ": Z coordinate of the axis used to determine the orientation of
00822       the frame in the case of a cylindrical grid.
00823 
00824     See class :class:`StructuralElementPart` for the description of the
00825     other parameters.
00826     """
00827 
00828     DEFAULT_NAME = "Grid"
00829 
00830     def __init__(self, studyId, groupName, groupGeomObj, parameters,
00831                  name = DEFAULT_NAME):
00832         StructuralElementPart2D.__init__(self, studyId, groupName,
00833                                          groupGeomObj, parameters, name)
00834         self.xr = self._getParameter(["origAxeX"])
00835         self.yr = self._getParameter(["origAxeY"])
00836         self.zr = self._getParameter(["origAxeZ"])
00837         self.vx = self._getParameter(["axeX"])
00838         self.vy = self._getParameter(["axeY"])
00839         self.vz = self._getParameter(["axeZ"])
00840         logger.debug(repr(self))
00841 
00842     def _buildPart(self):
00843         """
00844         Create the geometrical shapes representing the grid.
00845         """
00846         subShapes = self._getSubShapes()
00847         listGridShapes = []
00848     
00849         for subShape in subShapes:
00850             faces = self.geom.SubShapeAll(subShape,
00851                                           self.geom.ShapeType["FACE"])
00852             for face in faces:
00853                 if self.geom.KindOfShape(face)[0] == \
00854                                         self.geom.kind.CYLINDER2D and \
00855                         self.xr is not None and self.yr is not None and \
00856                         self.zr is not None and self.vx is not None and \
00857                         self.vy is not None and self.vz is not None:
00858                     shape = self._buildGridForCylinderFace(face)
00859                 else:
00860                     shape = self._buildGridForNormalFace(face)
00861                 listGridShapes.append(shape)
00862 
00863         if len(listGridShapes) == 0:
00864             return None
00865         elif len(listGridShapes) == 1:
00866             return listGridShapes[0]
00867         else:
00868             return self.geom.MakeCompound(listGridShapes)
00869 
00870     def _buildGridForNormalFace(self, face):
00871         """
00872         Create the geometrical shapes representing the grid for a given
00873         non-cylindrical face.
00874         """
00875         baseFace = self._makeFaceOffset(face, self.offset)
00876         gridList = [baseFace]
00877         
00878         # Compute display length for grid elements
00879         p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
00880         p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
00881         length = self.geom.MinDistance(p1, p2) / 2.0
00882 
00883         for u in range(1, 10):
00884             uParam = u * 0.1
00885             for v in range(1, 10):
00886                 vParam = v * 0.1
00887                 # get tangent plane on surface by parameters
00888                 center = self.geom.MakeVertexOnSurface(baseFace,
00889                                                        uParam, vParam)
00890                 tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
00891                                                              vParam, 1.0)
00892                 
00893                 # use the marker to get the orientation of the frame
00894                 normal = self.geom.GetNormal(tangPlane)
00895                 marker = self._orientation.buildMarker(self.geom, center,
00896                                                        normal, False)
00897                 [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
00898                 xPoint = self.geom.MakeTranslation(center, Xx * length,
00899                                                    Xy * length, Xz * length)
00900                 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
00901                 gridList.append(gridLine)
00902         grid = self.geom.MakeCompound(gridList)
00903         return grid
00904 
00905     def _buildGridForCylinderFace(self, face):
00906         """
00907         Create the geometrical shapes representing the grid for a given
00908         cylindrical face.
00909         """
00910         baseFace = self._makeFaceOffset(face, self.offset)
00911         gridList = [baseFace]
00912         
00913         # Compute display length for grid elements
00914         p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
00915         p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
00916         length = self.geom.MinDistance(p1, p2) / 2.0
00917         
00918         # Create reference vector V
00919         origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
00920         vPoint = self.geom.MakeTranslation(origPoint,
00921                                            self.vx, self.vy, self.vz)
00922         refVec = self.geom.MakeVector(origPoint, vPoint)
00923 
00924         for u in range(10):
00925             uParam = u * 0.1
00926             for v in range(1, 10):
00927                 vParam = v * 0.1
00928                 
00929                 # Compute the local orientation of the frame
00930                 center = self.geom.MakeVertexOnSurface(baseFace,
00931                                                        uParam, vParam)
00932                 locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
00933                                                          vPoint, 1.0)
00934                 locOrient = self.geom.GetNormal(locPlaneYZ)
00935                 xPoint = self.geom.MakeTranslationVectorDistance(center,
00936                                                                  locOrient,
00937                                                                  length)
00938                 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
00939                 gridList.append(gridLine)
00940 
00941         grid = self.geom.MakeCompound(gridList)
00942         return grid
00943 
00944     def _buildMarkers(self):
00945         """
00946         Create the markers defining the orientation of the grid.
00947         """
00948         return self._buildMarkersWithOffset(self.offset)
00949 
00950 
00951 def VisuPoutreGenerale(studyId, groupName, groupGeomObj, parameters,
00952                        name = "POUTRE"):
00953     """
00954     Alias for class :class:`GeneralBeam`.
00955     """
00956     return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
00957 
00958 def VisuPoutreCercle(studyId, groupName, groupGeomObj, parameters,
00959                      name = "POUTRE"):
00960     """
00961     Alias for class :class:`CircularBeam`.
00962     """
00963     return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
00964   
00965 def VisuPoutreRectangle(studyId, groupName, groupGeomObj, parameters,
00966                         name = "POUTRE"):
00967     """
00968     Alias for class :class:`RectangularBeam`.
00969     """
00970     return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
00971   
00972 def VisuBarreGenerale(studyId, groupName, groupGeomObj, parameters,
00973                       name = "BARRE"):
00974     """
00975     Alias for class :class:`GeneralBeam`.
00976     """
00977     return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name,
00978                        color = ORANGE)
00979       
00980 def VisuBarreRectangle(studyId, groupName, groupGeomObj, parameters,
00981                        name = "BARRE"):
00982     """
00983     Alias for class :class:`RectangularBeam`.
00984     """
00985     return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name,
00986                            color = ORANGE)
00987 
00988 def VisuBarreCercle(studyId, groupName, groupGeomObj, parameters,
00989                     name = "BARRE"):
00990     """
00991     Alias for class :class:`CircularBeam`.
00992     """
00993     return CircularBeam(studyId, groupName, groupGeomObj, parameters, name,
00994                         color = ORANGE)
00995 
00996 def VisuCable(studyId, groupName, groupGeomObj, parameters, name = "CABLE"):
00997     """
00998     Alias for class :class:`CircularBeam`.
00999     """
01000     return CircularBeam(studyId, groupName, groupGeomObj, parameters, name,
01001                         color = PURPLE)
01002 
01003 def VisuCoque(studyId, groupName, groupGeomObj, parameters, name = "COQUE"):
01004     """
01005     Alias for class :class:`ThickShell`.
01006     """
01007     return ThickShell(studyId, groupName, groupGeomObj, parameters, name)
01008   
01009 def VisuGrille(studyId, groupName, groupGeomObj, parameters, name = "GRILLE"):
01010     """
01011     Alias for class :class:`Grid`.
01012     """
01013     return Grid(studyId, groupName, groupGeomObj, parameters, name)