1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
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.
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
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 ## \defgroup parts parts
25 # This module defines the different structural element parts. It is used to
26 # build the geometric shapes of the structural elements. It should not be used
27 # directly in the general case. Structural elements should be created by the
28 # \ref structelem.StructuralElementManager "salome.geom.structelem.StructuralElementManager".
32 This module defines the different structural element parts. It is used to
33 build the geometric shapes of the structural elements. It should not be used
34 directly in the general case. Structural elements should be created by the
35 class :class:`~salome.geom.structelem.StructuralElementManager`.
43 from salome.kernel.logger import Logger
44 from salome.kernel import termcolor
45 logger = Logger("salome.geom.structelem.parts", color = termcolor.RED)
46 from salome.geom.geomtools import getGeompy
50 # Filling for the beams
54 # Minimum dimension for the shapes to extrude
55 MIN_DIM_FOR_EXTRUDED_SHAPE = 2e-4
56 MIN_LENGTH_FOR_EXTRUSION = 1e-4
59 # Colors for the structural elements
60 GREEN = SALOMEDS.Color(0.0, 1.0, 0.0)
61 LIGHT_GREEN = SALOMEDS.Color(0.0, 1.0, 170.0/255.0)
62 BLUE = SALOMEDS.Color(0.0, 0.0, 1.0)
63 LIGHT_BLUE = SALOMEDS.Color(0.0, 0.5, 1.0)
64 RED = SALOMEDS.Color(1.0, 0.0, 0.0)
65 LIGHT_RED = SALOMEDS.Color(1.0, 0.5, 0.5)
66 PURPLE = SALOMEDS.Color(170.0/255.0, 85.0/255.0, 1.0)
67 ORANGE = SALOMEDS.Color(1.0, 170.0/255.0, 0.0)
69 ## This exception is raised when an invalid parameter is used to build a
70 # structural element part.
72 class InvalidParameterError(Exception):
74 This exception is raised when an invalid parameter is used to build a
75 structural element part.
78 def __init__(self, groupName, expression, minValue, value):
79 self.groupName = groupName
80 self.expression = expression
81 self.minValue = minValue
85 return "%s < %g (%s = %g in %s)" % (self.expression, self.minValue,
86 self.expression, self.value,
89 ## This class enables the use of sub-shapes in sets or as dictionary keys.
90 # It implements __eq__ and __hash__ methods so that sub-shapes with the same
91 # CORBA object \em mainShape and the same \em id are considered equal.
95 This class enables the use of sub-shapes in sets or as dictionary keys.
96 It implements __eq__ and __hash__ methods so that sub-shapes with the same
97 CORBA object `mainShape` and the same `id` are considered equal.
100 def __init__(self, mainShape, id):
101 self._mainShape = mainShape
104 ## Return the sub-shape (GEOM object). \em geom is a pseudo-geompy object
105 # used to find the geometrical object.
106 def getObj(self, geom):
108 Return the sub-shape (GEOM object). `geom` is a pseudo-geompy object
109 used to find the geometrical object.
111 return geom.GetSubShape(self._mainShape, [self._id])
113 def __eq__(self, other):
114 return self._mainShape._is_equivalent(other._mainShape) and \
115 self._id == other._id
118 return self._mainShape._hash(2147483647) ^ self._id
120 ## This class is the base class for all structural element parts. It should
121 # not be instantiated directly (consider it as an "abstract" class).
122 # \param studyId (integer) the ID of the study in which the part is created.
123 # \param groupName (string) the name of the underlying geometrical primitive
125 # \param groupGeomObj (GEOM object) the underlying geometrical primitive.
126 # \param parameters (dictionary) parameters defining the structural element (see
127 # subclasses for details).
128 # \param name (string) name to use for the created object in the study.
130 class StructuralElementPart:
132 This class is the base class for all structural element parts. It should
133 not be instantiated directly (consider it as an "abstract" class).
135 :type studyId: integer
136 :param studyId: the ID of the study in which the part is created.
138 :type groupName: string
139 :param groupName: the name of the underlying geometrical primitive in the
142 :type groupGeomObj: GEOM object
143 :param groupGeomObj: the underlying geometrical primitive.
145 :type parameters: dictionary
146 :param parameters: parameters defining the structural element (see
147 subclasses for details).
150 :param name: name to use for the created object in the study.
154 DEFAULT_NAME = "StructElemPart"
156 def __init__(self, studyId, groupName, groupGeomObj, parameters,
157 name = DEFAULT_NAME, color = None):
158 self._parameters = parameters
159 self.groupName = groupName
160 self._groupGeomObj = groupGeomObj
161 self._orientation = None
162 self._paramUserName = {}
164 self.geom = getGeompy(studyId)
165 self.baseShapesSet = set()
166 self.isMainShape = (groupGeomObj.GetType() != 37) # See geompyDC.ShapeIdToType for type codes
167 if not self.isMainShape:
168 mainShape = self.geom.GetMainShape(groupGeomObj)
169 listIDs = self.geom.GetObjectIDs(groupGeomObj)
170 if mainShape is not None and listIDs is not None:
172 self.baseShapesSet.add(SubShapeID(mainShape, id))
174 if self.color is None:
175 self.color = self._groupGeomObj.GetColor()
177 ## This method finds the value of a parameter in the parameters
178 # dictionary. The argument is a list because some parameters can have
179 # several different names.
180 def _getParameter(self, nameList, default = None):
182 This method finds the value of a parameter in the parameters
183 dictionary. The argument is a list because some parameters can have
184 several different names.
186 if len(nameList) > 0:
187 paramName = nameList[0]
188 for name in nameList:
189 if self._parameters.has_key(name):
190 self._paramUserName[paramName] = name
191 return self._parameters[name]
194 ## This method finds the user name for a parameter.
195 def _getParamUserName(self, paramName):
197 This method finds the user name for a parameter.
199 if self._paramUserName.has_key(paramName):
200 return self._paramUserName[paramName]
205 reprdict = self.__dict__.copy()
206 del reprdict["_parameters"]
207 del reprdict["groupName"]
208 del reprdict["_groupGeomObj"]
209 del reprdict["_paramUserName"]
212 del reprdict["baseShapesSet"]
213 return '%s("%s", %s)' % (self.__class__.__name__, self.groupName,
216 ## Add orientation information to the structural element part. See class
217 # \ref Orientation1D "salome.geom.structelem.orientation.Orientation1D"
218 # for the description of the parameters.
219 def addOrientation(self, orientParams):
221 Add orientation information to the structural element part. See class
222 :class:`~salome.geom.structelem.orientation.Orientation1D` for the description
225 self._orientation.addParams(orientParams)
227 ## This method checks that some parameters or some expressions involving
228 # those parameters are greater than a minimum value.
229 def _checkSize(self, value, mindim, expression):
231 This method checks that some parameters or some expressions involving
232 those parameters are greater than a minimum value.
235 raise InvalidParameterError(self.groupName, expression,
238 ## Build the geometric shapes and the markers corresponding to the
239 # structural element part in the study \em studyId.
242 Build the geometric shapes and the markers corresponding to the
243 structural element part in the study `studyId`.
245 shape = self._buildPart()
246 markers = self._buildMarkers()
247 shape.SetColor(self.color)
248 for marker in markers:
249 marker.SetColor(self.color)
250 return (shape, markers)
252 ## This abstract method must be implemented in subclasses and should
253 # create the geometrical shape(s) of the structural element part.
254 def _buildPart(self):
256 This abstract method must be implemented in subclasses and should
257 create the geometrical shape(s) of the structural element part.
259 raise NotImplementedError("Method _buildPart not implemented in class"
260 " %s (it must be implemented in "
261 "StructuralElementPart subclasses)." %
262 self.__class__.__name__)
264 ## This abstract method must be implemented in subclasses and should
265 # create the markers defining the orientation of the structural element
267 def _buildMarkers(self):
269 This abstract method must be implemented in subclasses and should
270 create the markers defining the orientation of the structural element
273 raise NotImplementedError("Method _buildMarker not implemented in "
274 "class %s (it must be implemented in "
275 "StructuralElementPart subclasses)." %
276 self.__class__.__name__)
278 ## Find and return the base sub-shapes in the structural element part.
279 def _getSubShapes(self, minDim = MIN_LENGTH_FOR_EXTRUSION):
281 Find and return the base sub-shapes in the structural element part.
284 return [self._groupGeomObj]
286 for subShapeID in self.baseShapesSet:
287 subShape = subShapeID.getObj(self.geom)
288 length = self.geom.BasicProperties(subShape)[0]
290 logger.warning("Length too short (%s - ID %s, length = %g), "
291 "subshape will not be used in structural "
292 "element" % (self.groupName, subShapeID._id,
295 subShapes.append(subShape)
298 ## This class is an "abstract" class for all 1D structural element parts. It
299 # should not be instantiated directly. See class StructuralElementPart
300 # for the description of the parameters.
302 class Beam(StructuralElementPart):
304 This class is an "abstract" class for all 1D structural element parts. It
305 should not be instantiated directly. See class
306 :class:`StructuralElementPart` for the description of the parameters.
309 DEFAULT_NAME = "Beam"
311 def __init__(self, studyId, groupName, groupGeomObj, parameters,
312 name = DEFAULT_NAME, color = None):
313 StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
314 parameters, name, color)
315 self._orientation = orientation.Orientation1D()
317 ## This method checks if a 1D object is "reversed", i.e. if its
318 # orientation is different than the orientation of the underlying OCC
320 def _isReversed(self, path):
322 This method checks if a 1D object is "reversed", i.e. if its
323 orientation is different than the orientation of the underlying OCC
326 length = self.geom.BasicProperties(path)[0]
327 p1 = self.geom.MakeVertexOnCurve(path, 0.0)
328 p2 = self.geom.GetFirstVertex(path)
329 dist = self.geom.MinDistance(p1, p2)
330 return dist > length / 2
332 ## Get a vertex and the corresponding tangent on a wire by parameter.
333 # This method takes into account the "real" orientation of the wire
334 # (i.e. the orientation of the underlying OCC object).
335 def _getVertexAndTangentOnOrientedWire(self, path, param):
337 Get a vertex and the corresponding tangent on a wire by parameter.
338 This method takes into account the "real" orientation of the wire
339 (i.e. the orientation of the underlying OCC object).
341 if self._isReversed(path):
342 vertex = self.geom.MakeVertexOnCurve(path, 1.0 - param)
343 invtangent = self.geom.MakeTangentOnCurve(path, 1.0 - param)
344 tanpoint = self.geom.MakeTranslationVectorDistance(vertex,
347 tangent = self.geom.MakeVector(vertex, tanpoint)
349 vertex = self.geom.MakeVertexOnCurve(path, param)
350 tangent = self.geom.MakeTangentOnCurve(path, param)
351 return (vertex, tangent)
353 ## Create a solid by the extrusion of section \em wire1 to section \em wire2
355 def _makeSolidPipeFromWires(self, wire1, wire2, point1, point2, path):
357 Create a solid by the extrusion of section `wire1` to section `wire2`
360 face1 = self.geom.MakeFace(wire1, True)
361 face2 = self.geom.MakeFace(wire2, True)
362 shell = self.geom.MakePipeWithDifferentSections([wire1, wire2],
365 closedShell = self.geom.MakeShell([face1, face2, shell])
366 solid = self.geom.MakeSolid([closedShell])
369 ## Build the structural element part.
370 def _buildPart(self):
372 Build the structural element part.
374 # Get all the sub-shapes in the group (normally only edges and wires)
375 paths = self._getSubShapes()
378 withCorrection = False
381 # Build the sections (rectangular or circular) at each end of the
383 (fPoint, fNormal) = self._getVertexAndTangentOnOrientedWire(path,
385 (lPoint, lNormal) = self._getVertexAndTangentOnOrientedWire(path,
387 (outerWire1, innerWire1, outerWire2, innerWire2) = \
388 self._makeSectionWires(fPoint, fNormal, lPoint, lNormal)
390 # Create the resulting solid
391 outerSolid = self._makeSolidPipeFromWires(outerWire1, outerWire2,
392 fPoint, lPoint, path)
393 if self.filling == HOLLOW:
394 innerSolid = self._makeSolidPipeFromWires(innerWire1,
397 resultSolid = self.geom.MakeCut(outerSolid, innerSolid)
398 listPipes.append(resultSolid)
400 listPipes.append(outerSolid)
402 if len(listPipes) == 0:
404 elif len(listPipes) == 1:
407 return self.geom.MakeCompound(listPipes)
409 ## Build the markers defining the orientation of the structural element part.
410 def _buildMarkers(self):
412 Build the markers defining the orientation of the structural element
416 paths = self._getSubShapes()
419 (center, vecX) = self._getVertexAndTangentOnOrientedWire(path,
421 marker = self._orientation.buildMarker(self.geom, center, vecX)
422 listMarkers.append(marker)
426 ## This class defines a beam with a circular section. It can be full or
427 # hollow, and its radius and thickness can vary from one end of the beam to
428 # the other. The valid parameters for circular beams are:
429 # - "R1" or "R": radius at the first end of the beam.
430 # - "R2" or "R": radius at the other end of the beam.
431 # - "EP1" or "EP" (optional): thickness at the first end of the beam.
432 # If not specified or equal to 0, the beam is considered full.
433 # - "EP2" or "EP" (optional): thickness at the other end of the beam.
434 # If not specified or equal to 0, the beam is considered full.
436 # See class StructuralElementPart for the description of the other parameters.
438 class CircularBeam(Beam):
440 This class defines a beam with a circular section. It can be full or
441 hollow, and its radius and thickness can vary from one end of the beam to
442 the other. The valid parameters for circular beams are:
444 * "R1" or "R": radius at the first end of the beam.
445 * "R2" or "R": radius at the other end of the beam.
446 * "EP1" or "EP" (optional): thickness at the first end of the beam.
447 If not specified or equal to 0, the beam is considered full.
448 * "EP2" or "EP" (optional): thickness at the other end of the beam.
449 If not specified or equal to 0, the beam is considered full.
451 See class :class:`StructuralElementPart` for the description of the
456 def __init__(self, studyId, groupName, groupGeomObj, parameters,
457 name = Beam.DEFAULT_NAME, color = None):
459 if parameters.has_key("R1"): # variable section
461 else: # constant section
464 Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
467 self.R1 = self._getParameter(["R1", "R"])
468 self.R2 = self._getParameter(["R2", "R"])
469 self.EP1 = self._getParameter(["EP1", "EP"])
470 self.EP2 = self._getParameter(["EP2", "EP"])
472 if self.EP1 is None or self.EP2 is None or \
473 self.EP1 == 0 or self.EP2 == 0:
476 self.filling = HOLLOW
478 logger.debug(repr(self))
481 self._checkSize(self.R1, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
482 self._getParamUserName("R1"))
483 self._checkSize(self.R2, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
484 self._getParamUserName("R2"))
485 if self.filling == HOLLOW:
486 self._checkSize(self.EP1, MIN_THICKNESS,
487 self._getParamUserName("EP1"))
488 self._checkSize(self.EP2, MIN_THICKNESS,
489 self._getParamUserName("EP2"))
490 self._checkSize(self.R1 - self.EP1,
491 MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
492 "%s - %s" % (self._getParamUserName("R1"),
493 self._getParamUserName("EP1")))
494 self._checkSize(self.R2 - self.EP2,
495 MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
496 "%s - %s" % (self._getParamUserName("R2"),
497 self._getParamUserName("EP2")))
499 ## Create the circular sections used to build the pipe.
500 def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
502 Create the circular sections used to build the pipe.
504 outerCircle1 = self.geom.MakeCircle(fPoint, fNormal, self.R1)
505 outerCircle2 = self.geom.MakeCircle(lPoint, lNormal, self.R2)
506 if self.filling == HOLLOW:
507 innerCircle1 = self.geom.MakeCircle(fPoint, fNormal,
509 innerCircle2 = self.geom.MakeCircle(lPoint, lNormal,
515 return (outerCircle1, innerCircle1, outerCircle2, innerCircle2)
518 ## This class defines a beam with a rectangular section. It can be full or
519 # hollow, and its dimensions can vary from one end of the beam to the other.
520 # The valid parameters for rectangular beams are:
521 # - "HY1", "HY", "H1" or "H": width at the first end of the beam.
522 # - "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
523 # - "HY2", "HY", "H2" or "H": width at the other end of the beam.
524 # - "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
525 # - "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
526 # direction at the first end of the beam. If not specified or equal to 0,
527 # the beam is considered full.
528 # - "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
529 # direction at the first end of the beam. If not specified or equal to 0,
530 # the beam is considered full.
531 # - "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
532 # direction at the other end of the beam. If not specified or equal to 0,
533 # the beam is considered full.
534 # - "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
535 # direction at the other end of the beam. If not specified or equal to 0,
536 # the beam is considered full.
538 # See class StructuralElementPart for the description of the other parameters.
540 class RectangularBeam(Beam):
542 This class defines a beam with a rectangular section. It can be full or
543 hollow, and its dimensions can vary from one end of the beam to the other.
544 The valid parameters for rectangular beams are:
546 * "HY1", "HY", "H1" or "H": width at the first end of the beam.
547 * "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
548 * "HY2", "HY", "H2" or "H": width at the other end of the beam.
549 * "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
550 * "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
551 direction at the first end of the beam. If not specified or equal to 0,
552 the beam is considered full.
553 * "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
554 direction at the first end of the beam. If not specified or equal to 0,
555 the beam is considered full.
556 * "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
557 direction at the other end of the beam. If not specified or equal to 0,
558 the beam is considered full.
559 * "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
560 direction at the other end of the beam. If not specified or equal to 0,
561 the beam is considered full.
563 See class :class:`StructuralElementPart` for the description of the
568 def __init__(self, studyId, groupName, groupGeomObj, parameters,
569 name = Beam.DEFAULT_NAME, color = None):
571 if parameters.has_key("HY1") or parameters.has_key("H1"):
572 color = LIGHT_BLUE # variable section
573 else: # constant section
576 Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
579 self.HY1 = self._getParameter(["HY1", "HY", "H1", "H"])
580 self.HZ1 = self._getParameter(["HZ1", "HZ", "H1", "H"])
581 self.HY2 = self._getParameter(["HY2", "HY", "H2", "H"])
582 self.HZ2 = self._getParameter(["HZ2", "HZ", "H2", "H"])
583 self.EPY1 = self._getParameter(["EPY1", "EPY", "EP1", "EP"])
584 self.EPZ1 = self._getParameter(["EPZ1", "EPZ", "EP1", "EP"])
585 self.EPY2 = self._getParameter(["EPY2", "EPY", "EP2", "EP"])
586 self.EPZ2 = self._getParameter(["EPZ2", "EPZ", "EP2", "EP"])
588 if self.EPY1 is None or self.EPZ1 is None or \
589 self.EPY2 is None or self.EPZ2 is None or \
590 self.EPY1 == 0 or self.EPZ1 == 0 or \
591 self.EPY2 == 0 or self.EPZ2 == 0:
594 self.filling = HOLLOW
596 logger.debug(repr(self))
599 self._checkSize(self.HY1, MIN_DIM_FOR_EXTRUDED_SHAPE,
600 self._getParamUserName("HY1"))
601 self._checkSize(self.HZ1, MIN_DIM_FOR_EXTRUDED_SHAPE,
602 self._getParamUserName("HZ1"))
603 self._checkSize(self.HY2, MIN_DIM_FOR_EXTRUDED_SHAPE,
604 self._getParamUserName("HY2"))
605 self._checkSize(self.HZ2, MIN_DIM_FOR_EXTRUDED_SHAPE,
606 self._getParamUserName("HZ2"))
607 if self.filling == HOLLOW:
608 self._checkSize(self.EPY1, MIN_THICKNESS,
609 self._getParamUserName("EPY1"))
610 self._checkSize(self.EPZ1, MIN_THICKNESS,
611 self._getParamUserName("EPZ1"))
612 self._checkSize(self.EPY2, MIN_THICKNESS,
613 self._getParamUserName("EPY2"))
614 self._checkSize(self.EPZ2, MIN_THICKNESS,
615 self._getParamUserName("EPZ2"))
616 self._checkSize(self.HY1 - 2 * self.EPY1,
617 MIN_DIM_FOR_EXTRUDED_SHAPE,
618 "%s - 2 * %s" % (self._getParamUserName("HY1"),
619 self._getParamUserName("EPY1")))
620 self._checkSize(self.HZ1 - 2 * self.EPZ1,
621 MIN_DIM_FOR_EXTRUDED_SHAPE,
622 "%s - 2 * %s" % (self._getParamUserName("HZ1"),
623 self._getParamUserName("EPZ1")))
624 self._checkSize(self.HY2 - 2 * self.EPY2,
625 MIN_DIM_FOR_EXTRUDED_SHAPE,
626 "%s - 2 * %s" % (self._getParamUserName("HY2"),
627 self._getParamUserName("EPY2")))
628 self._checkSize(self.HZ2 - 2 * self.EPZ2,
629 MIN_DIM_FOR_EXTRUDED_SHAPE,
630 "%s - 2 * %s" % (self._getParamUserName("HZ2"),
631 self._getParamUserName("EPZ2")))
633 ## Create a rectangle in the specified plane.
634 def _makeRectangle(self, HY, HZ, lcs):
636 Create a rectangle in the specified plane.
640 sketchStr = "Sketcher:F %g %g:" % (-halfHY, -halfHZ)
641 sketchStr += "TT %g %g:" % (halfHY, -halfHZ)
642 sketchStr += "TT %g %g:" % (halfHY, halfHZ)
643 sketchStr += "TT %g %g:WW" % (-halfHY, halfHZ)
644 logger.debug('Drawing rectangle: "%s"' % sketchStr)
645 sketch = self.geom.MakeSketcherOnPlane(sketchStr, lcs)
648 ## Create one side of the rectangular sections used to build the pipe.
649 def _makeSectionRectangles(self, point, vecX, HY, HZ, EPY, EPZ):
651 Create one side of the rectangular sections used to build the pipe.
653 (vecY, vecZ) = self._orientation.getVecYZ(self.geom, point, vecX)
654 lcs = self.geom.MakeMarkerPntTwoVec(point, vecY, vecZ)
655 outerRect = self._makeRectangle(HY, HZ, lcs)
656 if self.filling == HOLLOW:
657 innerRect = self._makeRectangle(HY - 2.0 * EPY,
662 return (outerRect, innerRect)
664 ## Create the rectangular sections used to build the pipe.
665 def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
667 Create the rectangular sections used to build the pipe.
669 (outerRect1, innerRect1) = \
670 self._makeSectionRectangles(fPoint, fNormal, self.HY1, self.HZ1,
671 self.EPY1, self.EPZ1)
672 (outerRect2, innerRect2) = \
673 self._makeSectionRectangles(lPoint, lNormal, self.HY2, self.HZ2,
674 self.EPY2, self.EPZ2)
675 return (outerRect1, innerRect1, outerRect2, innerRect2)
678 ## This method finds the value of a parameter in the parameters
679 # dictionary. The argument is a list because some parameters can have
680 # several different names.
682 def getParameterInDict(nameList, parametersDict, default = None):
684 This method finds the value of a parameter in the parameters
685 dictionary. The argument is a list because some parameters can have
686 several different names.
688 for name in nameList:
689 if parametersDict.has_key(name):
690 return parametersDict[name]
693 ## This class defines a beam with a generic section. It is represented as a
694 # full rectangular beam with the following parameters:
695 # - HY1 = sqrt(12 * IZ1 / A1)
696 # - HZ1 = sqrt(12 * IY1 / A1)
697 # - HY2 = sqrt(12 * IZ2 / A2)
698 # - HZ2 = sqrt(12 * IY2 / A2)
700 # See StructuralElementPart for the description of the other parameters.
702 class GeneralBeam(RectangularBeam):
704 This class defines a beam with a generic section. It is represented as a
705 full rectangular beam with the following parameters:
707 * HY1 = sqrt(12 * IZ1 / A1)
708 * HZ1 = sqrt(12 * IY1 / A1)
709 * HY2 = sqrt(12 * IZ2 / A2)
710 * HZ2 = sqrt(12 * IY2 / A2)
712 See class :class:`StructuralElementPart` for the description of the other
716 def __init__(self, studyId, groupName, groupGeomObj, parameters,
717 name = Beam.DEFAULT_NAME, color = None):
718 self.IY1 = getParameterInDict(["IY1", "IY"], parameters)
719 self.IZ1 = getParameterInDict(["IZ1", "IZ"], parameters)
720 self.IY2 = getParameterInDict(["IY2", "IY"], parameters)
721 self.IZ2 = getParameterInDict(["IZ2", "IZ"], parameters)
722 self.A1 = getParameterInDict(["A1", "A"], parameters)
723 self.A2 = getParameterInDict(["A2", "A"], parameters)
724 parameters["HY1"] = math.sqrt(12 * self.IZ1 / self.A1)
725 parameters["HZ1"] = math.sqrt(12 * self.IY1 / self.A1)
726 parameters["HY2"] = math.sqrt(12 * self.IZ2 / self.A2)
727 parameters["HZ2"] = math.sqrt(12 * self.IY2 / self.A2)
730 if parameters.has_key("IY1"): # variable section
732 else: # constant section
735 RectangularBeam.__init__(self, studyId, groupName, groupGeomObj, parameters,
738 ## This class is an "abstract" class for all 2D structural element parts. It
739 # should not be instantiated directly.
740 # See class StructuralElementPart for the description of the parameters.
742 class StructuralElementPart2D(StructuralElementPart):
744 This class is an "abstract" class for all 2D structural element parts. It
745 should not be instantiated directly. See class
746 :class:`StructuralElementPart` for the description of the parameters.
749 DEFAULT_NAME = "StructuralElementPart2D"
751 def __init__(self, studyId, groupName, groupGeomObj, parameters,
752 name = DEFAULT_NAME):
753 StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
755 self._orientation = orientation.Orientation2D(
756 self._getParameter(["angleAlpha"]),
757 self._getParameter(["angleBeta"]),
758 self._getParameter(["Vecteur"]))
759 self.offset = self._getParameter(["Excentre"], 0.0)
761 ## Create a copy of a face at a given offset.
762 def _makeFaceOffset(self, face, offset, epsilon = 1e-6):
764 Create a copy of a face at a given offset.
766 if abs(offset) < epsilon:
767 return self.geom.MakeCopy(face)
769 offsetObj = self.geom.MakeOffset(face, offset)
770 # We have to explode the resulting object into faces because it is
771 # created as a polyhedron and not as a single face
772 faces = self.geom.SubShapeAll(offsetObj,
773 self.geom.ShapeType["FACE"])
776 ## Build the markers for the structural element part with a given offset
777 # from the base face.
778 def _buildMarkersWithOffset(self, offset):
780 Build the markers for the structural element part with a given offset
786 subShapes = self._getSubShapes()
788 for subShape in subShapes:
789 faces = self.geom.SubShapeAll(subShape,
790 self.geom.ShapeType["FACE"])
792 offsetFace = self._makeFaceOffset(face, offset)
793 # get tangent plane on surface by parameters
794 center = self.geom.MakeVertexOnSurface(offsetFace,
796 tangPlane = self.geom.MakeTangentPlaneOnFace(offsetFace,
799 normal = self.geom.GetNormal(tangPlane)
800 marker = self._orientation.buildMarker(self.geom,
802 listMarkers.append(marker)
806 ## This class defines a shell with a given thickness. It can be shifted from
807 # the base face. The valid parameters for thick shells are:
808 # - "Epais": thickness of the shell.
809 # - "Excentre": offset of the shell from the base face.
810 # - "angleAlpha": angle used to build the markers (see class
811 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
812 # - "angleBeta": angle used to build the markers (see class
813 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
814 # - "Vecteur": vector used instead of the angles to build the markers (see
815 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
817 # See class StructuralElementPart for the description of the other parameters.
819 class ThickShell(StructuralElementPart2D):
821 This class defines a shell with a given thickness. It can be shifted from
822 the base face. The valid parameters for thick shells are:
824 * "Epais": thickness of the shell.
825 * "Excentre": offset of the shell from the base face.
826 * "angleAlpha": angle used to build the markers (see class
827 :class:`~salome.geom.structelem.orientation.Orientation2D`)
828 * "angleBeta": angle used to build the markers (see class
829 :class:`~salome.geom.structelem.orientation.Orientation2D`)
830 * "Vecteur": vector used instead of the angles to build the markers (see
831 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
833 See class :class:`StructuralElementPart` for the description of the
837 DEFAULT_NAME = "ThickShell"
839 def __init__(self, studyId, groupName, groupGeomObj, parameters,
840 name = DEFAULT_NAME):
841 StructuralElementPart2D.__init__(self, studyId, groupName,
842 groupGeomObj, parameters, name)
843 self.thickness = self._getParameter(["Epais"])
844 logger.debug(repr(self))
846 ## Create the geometrical shapes corresponding to the thick shell.
847 def _buildPart(self):
849 Create the geometrical shapes corresponding to the thick shell.
851 subShapes = self._getSubShapes()
854 for subShape in subShapes:
855 faces = self.geom.SubShapeAll(subShape,
856 self.geom.ShapeType["FACE"])
858 shape = self._buildThickShellForFace(face)
859 listSolids.append(shape)
861 if len(listSolids) == 0:
863 elif len(listSolids) == 1:
866 return self.geom.MakeCompound(listSolids)
868 ## Create the geometrical shapes corresponding to the thick shell for a
870 def _buildThickShellForFace(self, face):
872 Create the geometrical shapes corresponding to the thick shell for a
876 if self.thickness < 2 * epsilon:
877 return self._makeFaceOffset(face, self.offset, epsilon)
879 upperOffset = self.offset + self.thickness / 2.0
880 lowerOffset = self.offset - self.thickness / 2.0
884 upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
885 lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
886 listShapes = [upperFace, lowerFace]
887 upperWires = self.geom.SubShapeAll(upperFace,
888 self.geom.ShapeType["WIRE"])
889 lowerWires = self.geom.SubShapeAll(lowerFace,
890 self.geom.ShapeType["WIRE"])
891 if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
892 # if the face is a cylinder, we remove the extra side edge
893 upperWires = self._removeCylinderExtraEdge(upperWires)
894 lowerWires = self._removeCylinderExtraEdge(lowerWires)
895 for i in range(len(upperWires)):
896 resShape = self.geom.MakeThruSections([upperWires[i],
900 listShapes.append(resShape)
901 resultShell = self.geom.MakeShell(listShapes)
902 resultSolid = self.geom.MakeSolid([resultShell])
905 ## Remove the side edge in a cylinder.
906 def _removeCylinderExtraEdge(self, wires):
908 Remove the side edge in a cylinder.
912 edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
914 if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
918 ## Build the markers defining the orientation of the thick shell.
919 def _buildMarkers(self):
921 Build the markers defining the orientation of the thick shell.
923 return self._buildMarkersWithOffset(self.offset +
924 self.thickness / 2.0)
926 ## This class defines a grid. A grid is represented by a 2D face patterned
927 # with small lines in the main direction of the grid frame. The valid
928 # parameters for grids are:
929 # - "Excentre": offset of the grid from the base face.
930 # - "angleAlpha": angle used to build the markers (see class
931 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
932 # - "angleBeta": angle used to build the markers (see class
933 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
934 # - "Vecteur": vector used instead of the angles to build the markers (see
935 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
936 # - "origAxeX": X coordinate of the origin of the axis used to determine the
937 # orientation of the frame in the case of a cylindrical grid.
938 # - "origAxeY": Y coordinate of the origin of the axis used to determine the
939 # orientation of the frame in the case of a cylindrical grid.
940 # - "origAxeZ": Z coordinate of the origin of the axis used to determine the
941 # orientation of the frame in the case of a cylindrical grid.
942 # - "axeX": X coordinate of the axis used to determine the orientation of
943 # the frame in the case of a cylindrical grid.
944 # - "axeY": Y coordinate of the axis used to determine the orientation of
945 # the frame in the case of a cylindrical grid.
946 # - "axeZ": Z coordinate of the axis used to determine the orientation of
947 # the frame in the case of a cylindrical grid.
949 # See class StructuralElementPart for the description of the other parameters.
951 class Grid(StructuralElementPart2D):
953 This class defines a grid. A grid is represented by a 2D face patterned
954 with small lines in the main direction of the grid frame. The valid
955 parameters for grids are:
957 * "Excentre": offset of the grid from the base face.
958 * "angleAlpha": angle used to build the markers (see class
959 :class:`~salome.geom.structelem.orientation.Orientation2D`)
960 * "angleBeta": angle used to build the markers (see class
961 :class:`~salome.geom.structelem.orientation.Orientation2D`)
962 * "Vecteur": vector used instead of the angles to build the markers (see
963 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
964 * "origAxeX": X coordinate of the origin of the axis used to determine the
965 orientation of the frame in the case of a cylindrical grid.
966 * "origAxeY": Y coordinate of the origin of the axis used to determine the
967 orientation of the frame in the case of a cylindrical grid.
968 * "origAxeZ": Z coordinate of the origin of the axis used to determine the
969 orientation of the frame in the case of a cylindrical grid.
970 * "axeX": X coordinate of the axis used to determine the orientation of
971 the frame in the case of a cylindrical grid.
972 * "axeY": Y coordinate of the axis used to determine the orientation of
973 the frame in the case of a cylindrical grid.
974 * "axeZ": Z coordinate of the axis used to determine the orientation of
975 the frame in the case of a cylindrical grid.
977 See class :class:`StructuralElementPart` for the description of the
981 DEFAULT_NAME = "Grid"
983 def __init__(self, studyId, groupName, groupGeomObj, parameters,
984 name = DEFAULT_NAME):
985 StructuralElementPart2D.__init__(self, studyId, groupName,
986 groupGeomObj, parameters, name)
987 self.xr = self._getParameter(["origAxeX"])
988 self.yr = self._getParameter(["origAxeY"])
989 self.zr = self._getParameter(["origAxeZ"])
990 self.vx = self._getParameter(["axeX"])
991 self.vy = self._getParameter(["axeY"])
992 self.vz = self._getParameter(["axeZ"])
993 logger.debug(repr(self))
995 ## Create the geometrical shapes representing the grid.
996 def _buildPart(self):
998 Create the geometrical shapes representing the grid.
1000 subShapes = self._getSubShapes()
1003 for subShape in subShapes:
1004 faces = self.geom.SubShapeAll(subShape,
1005 self.geom.ShapeType["FACE"])
1007 if self.geom.KindOfShape(face)[0] == \
1008 self.geom.kind.CYLINDER2D and \
1009 self.xr is not None and self.yr is not None and \
1010 self.zr is not None and self.vx is not None and \
1011 self.vy is not None and self.vz is not None:
1012 shape = self._buildGridForCylinderFace(face)
1014 shape = self._buildGridForNormalFace(face)
1015 listGridShapes.append(shape)
1017 if len(listGridShapes) == 0:
1019 elif len(listGridShapes) == 1:
1020 return listGridShapes[0]
1022 return self.geom.MakeCompound(listGridShapes)
1024 ## Create the geometrical shapes representing the grid for a given
1025 # non-cylindrical face.
1026 def _buildGridForNormalFace(self, face):
1028 Create the geometrical shapes representing the grid for a given
1029 non-cylindrical face.
1031 baseFace = self._makeFaceOffset(face, self.offset)
1032 gridList = [baseFace]
1034 # Compute display length for grid elements
1035 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1036 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1037 length = self.geom.MinDistance(p1, p2) / 2.0
1039 for u in range(1, 10):
1041 for v in range(1, 10):
1043 # get tangent plane on surface by parameters
1044 center = self.geom.MakeVertexOnSurface(baseFace,
1046 tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
1049 # use the marker to get the orientation of the frame
1050 normal = self.geom.GetNormal(tangPlane)
1051 marker = self._orientation.buildMarker(self.geom, center,
1053 [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
1054 xPoint = self.geom.MakeTranslation(center, Xx * length,
1055 Xy * length, Xz * length)
1056 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1057 gridList.append(gridLine)
1058 grid = self.geom.MakeCompound(gridList)
1061 ## Create the geometrical shapes representing the grid for a given
1063 def _buildGridForCylinderFace(self, face):
1065 Create the geometrical shapes representing the grid for a given
1068 baseFace = self._makeFaceOffset(face, self.offset)
1069 gridList = [baseFace]
1071 # Compute display length for grid elements
1072 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1073 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1074 length = self.geom.MinDistance(p1, p2) / 2.0
1076 # Create reference vector V
1077 origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
1078 vPoint = self.geom.MakeTranslation(origPoint,
1079 self.vx, self.vy, self.vz)
1080 refVec = self.geom.MakeVector(origPoint, vPoint)
1084 for v in range(1, 10):
1087 # Compute the local orientation of the frame
1088 center = self.geom.MakeVertexOnSurface(baseFace,
1090 locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
1092 locOrient = self.geom.GetNormal(locPlaneYZ)
1093 xPoint = self.geom.MakeTranslationVectorDistance(center,
1096 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1097 gridList.append(gridLine)
1099 grid = self.geom.MakeCompound(gridList)
1102 ## Create the markers defining the orientation of the grid.
1103 def _buildMarkers(self):
1105 Create the markers defining the orientation of the grid.
1107 return self._buildMarkersWithOffset(self.offset)
1109 ## Alias for class GeneralBeam.
1111 def VisuPoutreGenerale(studyId, groupName, groupGeomObj, parameters,
1114 Alias for class :class:`GeneralBeam`.
1116 return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
1118 ## Alias for class CircularBeam.
1120 def VisuPoutreCercle(studyId, groupName, groupGeomObj, parameters,
1123 Alias for class :class:`CircularBeam`.
1125 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
1127 ## Alias for class RectangularBeam.
1129 def VisuPoutreRectangle(studyId, groupName, groupGeomObj, parameters,
1132 Alias for class :class:`RectangularBeam`.
1134 return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
1136 ## Alias for class GeneralBeam.
1138 def VisuBarreGenerale(studyId, groupName, groupGeomObj, parameters,
1141 Alias for class :class:`GeneralBeam`.
1143 return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name,
1146 ## Alias for class RectangularBeam.
1148 def VisuBarreRectangle(studyId, groupName, groupGeomObj, parameters,
1151 Alias for class :class:`RectangularBeam`.
1153 return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name,
1156 ## Alias for class CircularBeam.
1158 def VisuBarreCercle(studyId, groupName, groupGeomObj, parameters,
1161 Alias for class :class:`CircularBeam`.
1163 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name,
1166 ## Alias for class CircularBeam.
1168 def VisuCable(studyId, groupName, groupGeomObj, parameters, name = "CABLE"):
1170 Alias for class :class:`CircularBeam`.
1172 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name,
1175 ## Alias for class ThickShell.
1177 def VisuCoque(studyId, groupName, groupGeomObj, parameters, name = "COQUE"):
1179 Alias for class :class:`ThickShell`.
1181 return ThickShell(studyId, groupName, groupGeomObj, parameters, name)
1183 ## Alias for class Grid.
1185 def VisuGrille(studyId, groupName, groupGeomObj, parameters, name = "GRILLE"):
1187 Alias for class :class:`Grid`.
1189 return Grid(studyId, groupName, groupGeomObj, parameters, name)