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 the center of the face and the normal at the center
794 center = self.geom.MakeVertexOnSurface(offsetFace,
796 normal = self.geom.GetNormal(offsetFace, center)
797 marker = self._orientation.buildMarker(self.geom,
799 listMarkers.append(marker)
803 ## This class defines a shell with a given thickness. It can be shifted from
804 # the base face. The valid parameters for thick shells are:
805 # - "Epais": thickness of the shell.
806 # - "Excentre": offset of the shell from the base face.
807 # - "angleAlpha": angle used to build the markers (see class
808 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
809 # - "angleBeta": angle used to build the markers (see class
810 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
811 # - "Vecteur": vector used instead of the angles to build the markers (see
812 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
814 # See class StructuralElementPart for the description of the other parameters.
816 class ThickShell(StructuralElementPart2D):
818 This class defines a shell with a given thickness. It can be shifted from
819 the base face. The valid parameters for thick shells are:
821 * "Epais": thickness of the shell.
822 * "Excentre": offset of the shell from the base face.
823 * "angleAlpha": angle used to build the markers (see class
824 :class:`~salome.geom.structelem.orientation.Orientation2D`)
825 * "angleBeta": angle used to build the markers (see class
826 :class:`~salome.geom.structelem.orientation.Orientation2D`)
827 * "Vecteur": vector used instead of the angles to build the markers (see
828 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
830 See class :class:`StructuralElementPart` for the description of the
834 DEFAULT_NAME = "ThickShell"
836 def __init__(self, studyId, groupName, groupGeomObj, parameters,
837 name = DEFAULT_NAME):
838 StructuralElementPart2D.__init__(self, studyId, groupName,
839 groupGeomObj, parameters, name)
840 self.thickness = self._getParameter(["Epais"])
841 logger.debug(repr(self))
843 ## Create the geometrical shapes corresponding to the thick shell.
844 def _buildPart(self):
846 Create the geometrical shapes corresponding to the thick shell.
848 subShapes = self._getSubShapes()
851 for subShape in subShapes:
852 faces = self.geom.SubShapeAll(subShape,
853 self.geom.ShapeType["FACE"])
855 shape = self._buildThickShellForFace(face)
856 listSolids.append(shape)
858 if len(listSolids) == 0:
860 elif len(listSolids) == 1:
863 return self.geom.MakeCompound(listSolids)
865 ## Create the geometrical shapes corresponding to the thick shell for a
867 def _buildThickShellForFace(self, face):
869 Create the geometrical shapes corresponding to the thick shell for a
873 if self.thickness < 2 * epsilon:
874 return self._makeFaceOffset(face, self.offset, epsilon)
876 upperOffset = self.offset + self.thickness / 2.0
877 lowerOffset = self.offset - self.thickness / 2.0
881 upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
882 lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
883 listShapes = [upperFace, lowerFace]
884 upperWires = self.geom.SubShapeAll(upperFace,
885 self.geom.ShapeType["WIRE"])
886 lowerWires = self.geom.SubShapeAll(lowerFace,
887 self.geom.ShapeType["WIRE"])
888 if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
889 # if the face is a cylinder, we remove the extra side edge
890 upperWires = self._removeCylinderExtraEdge(upperWires)
891 lowerWires = self._removeCylinderExtraEdge(lowerWires)
892 for i in range(len(upperWires)):
893 resShape = self.geom.MakeThruSections([upperWires[i],
897 listShapes.append(resShape)
898 resultShell = self.geom.MakeShell(listShapes)
899 resultSolid = self.geom.MakeSolid([resultShell])
902 ## Remove the side edge in a cylinder.
903 def _removeCylinderExtraEdge(self, wires):
905 Remove the side edge in a cylinder.
909 edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
911 if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
915 ## Build the markers defining the orientation of the thick shell.
916 def _buildMarkers(self):
918 Build the markers defining the orientation of the thick shell.
920 return self._buildMarkersWithOffset(self.offset +
921 self.thickness / 2.0)
923 ## This class defines a grid. A grid is represented by a 2D face patterned
924 # with small lines in the main direction of the grid frame. The valid
925 # parameters for grids are:
926 # - "Excentre": offset of the grid from the base face.
927 # - "angleAlpha": angle used to build the markers (see class
928 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
929 # - "angleBeta": angle used to build the markers (see class
930 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
931 # - "Vecteur": vector used instead of the angles to build the markers (see
932 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
933 # - "origAxeX": X coordinate of the origin of the axis used to determine the
934 # orientation of the frame in the case of a cylindrical grid.
935 # - "origAxeY": Y coordinate of the origin of the axis used to determine the
936 # orientation of the frame in the case of a cylindrical grid.
937 # - "origAxeZ": Z coordinate of the origin of the axis used to determine the
938 # orientation of the frame in the case of a cylindrical grid.
939 # - "axeX": X coordinate of the axis used to determine the orientation of
940 # the frame in the case of a cylindrical grid.
941 # - "axeY": Y coordinate of the axis used to determine the orientation of
942 # the frame in the case of a cylindrical grid.
943 # - "axeZ": Z coordinate of the axis used to determine the orientation of
944 # the frame in the case of a cylindrical grid.
946 # See class StructuralElementPart for the description of the other parameters.
948 class Grid(StructuralElementPart2D):
950 This class defines a grid. A grid is represented by a 2D face patterned
951 with small lines in the main direction of the grid frame. The valid
952 parameters for grids are:
954 * "Excentre": offset of the grid from the base face.
955 * "angleAlpha": angle used to build the markers (see class
956 :class:`~salome.geom.structelem.orientation.Orientation2D`)
957 * "angleBeta": angle used to build the markers (see class
958 :class:`~salome.geom.structelem.orientation.Orientation2D`)
959 * "Vecteur": vector used instead of the angles to build the markers (see
960 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
961 * "origAxeX": X coordinate of the origin of the axis used to determine the
962 orientation of the frame in the case of a cylindrical grid.
963 * "origAxeY": Y coordinate of the origin of the axis used to determine the
964 orientation of the frame in the case of a cylindrical grid.
965 * "origAxeZ": Z coordinate of the origin of the axis used to determine the
966 orientation of the frame in the case of a cylindrical grid.
967 * "axeX": X coordinate of the axis used to determine the orientation of
968 the frame in the case of a cylindrical grid.
969 * "axeY": Y coordinate of the axis used to determine the orientation of
970 the frame in the case of a cylindrical grid.
971 * "axeZ": Z coordinate of the axis used to determine the orientation of
972 the frame in the case of a cylindrical grid.
974 See class :class:`StructuralElementPart` for the description of the
978 DEFAULT_NAME = "Grid"
980 def __init__(self, studyId, groupName, groupGeomObj, parameters,
981 name = DEFAULT_NAME):
982 StructuralElementPart2D.__init__(self, studyId, groupName,
983 groupGeomObj, parameters, name)
984 self.xr = self._getParameter(["origAxeX"])
985 self.yr = self._getParameter(["origAxeY"])
986 self.zr = self._getParameter(["origAxeZ"])
987 self.vx = self._getParameter(["axeX"])
988 self.vy = self._getParameter(["axeY"])
989 self.vz = self._getParameter(["axeZ"])
990 logger.debug(repr(self))
992 ## Create the geometrical shapes representing the grid.
993 def _buildPart(self):
995 Create the geometrical shapes representing the grid.
997 subShapes = self._getSubShapes()
1000 for subShape in subShapes:
1001 faces = self.geom.SubShapeAll(subShape,
1002 self.geom.ShapeType["FACE"])
1004 if self.geom.KindOfShape(face)[0] == \
1005 self.geom.kind.CYLINDER2D and \
1006 self.xr is not None and self.yr is not None and \
1007 self.zr is not None and self.vx is not None and \
1008 self.vy is not None and self.vz is not None:
1009 shape = self._buildGridForCylinderFace(face)
1011 shape = self._buildGridForNormalFace(face)
1012 listGridShapes.append(shape)
1014 if len(listGridShapes) == 0:
1016 elif len(listGridShapes) == 1:
1017 return listGridShapes[0]
1019 return self.geom.MakeCompound(listGridShapes)
1021 ## Create the geometrical shapes representing the grid for a given
1022 # non-cylindrical face.
1023 def _buildGridForNormalFace(self, face):
1025 Create the geometrical shapes representing the grid for a given
1026 non-cylindrical face.
1028 baseFace = self._makeFaceOffset(face, self.offset)
1029 gridList = [baseFace]
1031 # Compute display length for grid elements
1032 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1033 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1034 length = self.geom.MinDistance(p1, p2) / 2.0
1036 for u in range(1, 10):
1038 for v in range(1, 10):
1040 # get tangent plane on surface by parameters
1041 center = self.geom.MakeVertexOnSurface(baseFace,
1043 tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
1046 # use the marker to get the orientation of the frame
1047 normal = self.geom.GetNormal(tangPlane)
1048 marker = self._orientation.buildMarker(self.geom, center,
1050 [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
1051 xPoint = self.geom.MakeTranslation(center, Xx * length,
1052 Xy * length, Xz * length)
1053 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1054 gridList.append(gridLine)
1055 grid = self.geom.MakeCompound(gridList)
1058 ## Create the geometrical shapes representing the grid for a given
1060 def _buildGridForCylinderFace(self, face):
1062 Create the geometrical shapes representing the grid for a given
1065 baseFace = self._makeFaceOffset(face, self.offset)
1066 gridList = [baseFace]
1068 # Compute display length for grid elements
1069 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1070 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1071 length = self.geom.MinDistance(p1, p2) / 2.0
1073 # Create reference vector V
1074 origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
1075 vPoint = self.geom.MakeTranslation(origPoint,
1076 self.vx, self.vy, self.vz)
1077 refVec = self.geom.MakeVector(origPoint, vPoint)
1081 for v in range(1, 10):
1084 # Compute the local orientation of the frame
1085 center = self.geom.MakeVertexOnSurface(baseFace,
1087 locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
1089 locOrient = self.geom.GetNormal(locPlaneYZ)
1090 xPoint = self.geom.MakeTranslationVectorDistance(center,
1093 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1094 gridList.append(gridLine)
1096 grid = self.geom.MakeCompound(gridList)
1099 ## Create the markers defining the orientation of the grid.
1100 def _buildMarkers(self):
1102 Create the markers defining the orientation of the grid.
1104 return self._buildMarkersWithOffset(self.offset)
1106 ## Alias for class GeneralBeam.
1108 def VisuPoutreGenerale(studyId, groupName, groupGeomObj, parameters,
1111 Alias for class :class:`GeneralBeam`.
1113 return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
1115 ## Alias for class CircularBeam.
1117 def VisuPoutreCercle(studyId, groupName, groupGeomObj, parameters,
1120 Alias for class :class:`CircularBeam`.
1122 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
1124 ## Alias for class RectangularBeam.
1126 def VisuPoutreRectangle(studyId, groupName, groupGeomObj, parameters,
1129 Alias for class :class:`RectangularBeam`.
1131 return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
1133 ## Alias for class GeneralBeam.
1135 def VisuBarreGenerale(studyId, groupName, groupGeomObj, parameters,
1138 Alias for class :class:`GeneralBeam`.
1140 return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name,
1143 ## Alias for class RectangularBeam.
1145 def VisuBarreRectangle(studyId, groupName, groupGeomObj, parameters,
1148 Alias for class :class:`RectangularBeam`.
1150 return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name,
1153 ## Alias for class CircularBeam.
1155 def VisuBarreCercle(studyId, groupName, groupGeomObj, parameters,
1158 Alias for class :class:`CircularBeam`.
1160 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name,
1163 ## Alias for class CircularBeam.
1165 def VisuCable(studyId, groupName, groupGeomObj, parameters, name = "CABLE"):
1167 Alias for class :class:`CircularBeam`.
1169 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name,
1172 ## Alias for class ThickShell.
1174 def VisuCoque(studyId, groupName, groupGeomObj, parameters, name = "COQUE"):
1176 Alias for class :class:`ThickShell`.
1178 return ThickShell(studyId, groupName, groupGeomObj, parameters, name)
1180 ## Alias for class Grid.
1182 def VisuGrille(studyId, groupName, groupGeomObj, parameters, name = "GRILLE"):
1184 Alias for class :class:`Grid`.
1186 return Grid(studyId, groupName, groupGeomObj, parameters, name)