1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2022 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
48 from . import orientation
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 groupName (string) the name of the underlying geometrical primitive
124 # \param groupGeomObj (GEOM object) the underlying geometrical primitive.
125 # \param parameters (dictionary) parameters defining the structural element (see
126 # subclasses for details).
127 # \param name (string) name to use for the created object in the study.
129 class StructuralElementPart:
131 This class is the base class for all structural element parts. It should
132 not be instantiated directly (consider it as an "abstract" class).
134 :type groupName: string
135 :param groupName: the name of the underlying geometrical primitive in the
138 :type groupGeomObj: GEOM object
139 :param groupGeomObj: the underlying geometrical primitive.
141 :type parameters: dictionary
142 :param parameters: parameters defining the structural element (see
143 subclasses for details).
146 :param name: name to use for the created object in the study.
150 DEFAULT_NAME = "StructElemPart"
152 def __init__(self, groupName, groupGeomObj, parameters,
153 name = DEFAULT_NAME, color = None):
154 self._parameters = parameters
155 self.groupName = groupName
156 self._groupGeomObj = groupGeomObj
157 self._orientation = None
158 self._paramUserName = {}
160 self.geom = getGeompy()
161 self.baseShapesSet = set()
162 self.isMainShape = (groupGeomObj.GetType() != 37) # See geompyDC.ShapeIdToType for type codes
163 if not self.isMainShape:
164 mainShape = self.geom.GetMainShape(groupGeomObj)
165 listIDs = self.geom.GetObjectIDs(groupGeomObj)
166 if mainShape is not None and listIDs is not None:
168 self.baseShapesSet.add(SubShapeID(mainShape, id))
170 if self.color is None:
171 self.color = self._groupGeomObj.GetColor()
173 ## This method finds the value of a parameter in the parameters
174 # dictionary. The argument is a list because some parameters can have
175 # several different names.
176 def _getParameter(self, nameList, default = None):
178 This method finds the value of a parameter in the parameters
179 dictionary. The argument is a list because some parameters can have
180 several different names.
182 if len(nameList) > 0:
183 paramName = nameList[0]
184 for name in nameList:
185 if name in self._parameters:
186 self._paramUserName[paramName] = name
187 return self._parameters[name]
190 ## This method finds the user name for a parameter.
191 def _getParamUserName(self, paramName):
193 This method finds the user name for a parameter.
195 if paramName in self._paramUserName:
196 return self._paramUserName[paramName]
201 reprdict = self.__dict__.copy()
202 del reprdict["_parameters"]
203 del reprdict["groupName"]
204 del reprdict["_groupGeomObj"]
205 del reprdict["_paramUserName"]
208 del reprdict["baseShapesSet"]
209 return '%s("%s", %s)' % (self.__class__.__name__, self.groupName,
212 ## Add orientation information to the structural element part. See class
213 # \ref Orientation1D "salome.geom.structelem.orientation.Orientation1D"
214 # for the description of the parameters.
215 def addOrientation(self, orientParams):
217 Add orientation information to the structural element part. See class
218 :class:`~salome.geom.structelem.orientation.Orientation1D` for the description
221 self._orientation.addParams(orientParams)
223 ## This method checks that some parameters or some expressions involving
224 # those parameters are greater than a minimum value.
225 def _checkSize(self, value, mindim, expression):
227 This method checks that some parameters or some expressions involving
228 those parameters are greater than a minimum value.
231 raise InvalidParameterError(self.groupName, expression,
234 ## Build the geometric shapes and the markers corresponding to the
235 # structural element part in the study.
238 Build the geometric shapes and the markers corresponding to the
239 structural element part in the study.
241 shape = self._buildPart()
242 markers = self._buildMarkers()
243 shape.SetColor(self.color)
244 for marker in markers:
245 marker.SetColor(self.color)
246 return (shape, markers)
248 ## This abstract method must be implemented in subclasses and should
249 # create the geometrical shape(s) of the structural element part.
250 def _buildPart(self):
252 This abstract method must be implemented in subclasses and should
253 create the geometrical shape(s) of the structural element part.
255 raise NotImplementedError("Method _buildPart not implemented in class"
256 " %s (it must be implemented in "
257 "StructuralElementPart subclasses)." %
258 self.__class__.__name__)
260 ## This abstract method must be implemented in subclasses and should
261 # create the markers defining the orientation of the structural element
263 def _buildMarkers(self):
265 This abstract method must be implemented in subclasses and should
266 create the markers defining the orientation of the structural element
269 raise NotImplementedError("Method _buildMarker not implemented in "
270 "class %s (it must be implemented in "
271 "StructuralElementPart subclasses)." %
272 self.__class__.__name__)
274 ## Find and return the base sub-shapes in the structural element part.
275 def _getSubShapes(self, minDim = MIN_LENGTH_FOR_EXTRUSION):
277 Find and return the base sub-shapes in the structural element part.
280 return [self._groupGeomObj]
282 for subShapeID in self.baseShapesSet:
283 subShape = subShapeID.getObj(self.geom)
284 length = self.geom.BasicProperties(subShape)[0]
286 logger.warning("Length too short (%s - ID %s, length = %g), "
287 "subshape will not be used in structural "
288 "element" % (self.groupName, subShapeID._id,
291 subShapes.append(subShape)
294 ## This class is an "abstract" class for all 1D structural element parts. It
295 # should not be instantiated directly. See class StructuralElementPart
296 # for the description of the parameters.
298 class Beam(StructuralElementPart):
300 This class is an "abstract" class for all 1D structural element parts. It
301 should not be instantiated directly. See class
302 :class:`StructuralElementPart` for the description of the parameters.
305 DEFAULT_NAME = "Beam"
307 def __init__(self, groupName, groupGeomObj, parameters,
308 name = DEFAULT_NAME, color = None):
309 StructuralElementPart.__init__(self, groupName, groupGeomObj,
310 parameters, name, color)
311 self._orientation = orientation.Orientation1D()
313 ## This method checks if a 1D object is "reversed", i.e. if its
314 # orientation is different than the orientation of the underlying OCC
316 def _isReversed(self, path):
318 This method checks if a 1D object is "reversed", i.e. if its
319 orientation is different than the orientation of the underlying OCC
322 length = self.geom.BasicProperties(path)[0]
323 p1 = self.geom.MakeVertexOnCurve(path, 0.0)
324 p2 = self.geom.GetFirstVertex(path)
325 dist = self.geom.MinDistance(p1, p2)
326 return dist > length / 2
328 ## Get a vertex and the corresponding tangent on a wire by parameter.
329 # This method takes into account the "real" orientation of the wire
330 # (i.e. the orientation of the underlying OCC object).
331 def _getVertexAndTangentOnOrientedWire(self, path, param):
333 Get a vertex and the corresponding tangent on a wire by parameter.
334 This method takes into account the "real" orientation of the wire
335 (i.e. the orientation of the underlying OCC object).
337 if self._isReversed(path):
338 vertex = self.geom.MakeVertexOnCurve(path, 1.0 - param)
339 invtangent = self.geom.MakeTangentOnCurve(path, 1.0 - param)
340 tanpoint = self.geom.MakeTranslationVectorDistance(vertex,
343 tangent = self.geom.MakeVector(vertex, tanpoint)
345 vertex = self.geom.MakeVertexOnCurve(path, param)
346 tangent = self.geom.MakeTangentOnCurve(path, param)
347 return (vertex, tangent)
349 ## Create a solid by the extrusion of section \em wire1 to section \em wire2
351 def _makeSolidPipeFromWires(self, wire1, wire2, point1, point2, path):
353 Create a solid by the extrusion of section `wire1` to section `wire2`
356 face1 = self.geom.MakeFace(wire1, True)
357 face2 = self.geom.MakeFace(wire2, True)
358 shell = self.geom.MakePipeWithDifferentSections([wire1, wire2],
362 closedShell = self.geom.MakeShell([face1, face2, shell])
363 solid = self.geom.MakeSolid([closedShell])
366 ## Build the structural element part.
367 def _buildPart(self):
369 Build the structural element part.
371 # Get all the sub-shapes in the group (normally only edges and wires)
372 paths = self._getSubShapes()
375 withCorrection = False
378 # Build the sections (rectangular or circular) at each end of the
380 (fPoint, fNormal) = self._getVertexAndTangentOnOrientedWire(path,
382 (lPoint, lNormal) = self._getVertexAndTangentOnOrientedWire(path,
384 (outerWire1, innerWire1, outerWire2, innerWire2) = \
385 self._makeSectionWires(fPoint, fNormal, lPoint, lNormal)
387 # Create the resulting solid
388 outerSolid = self._makeSolidPipeFromWires(outerWire1, outerWire2,
389 fPoint, lPoint, path)
390 if self.filling == HOLLOW:
391 innerSolid = self._makeSolidPipeFromWires(innerWire1,
394 resultSolid = self.geom.MakeCut(outerSolid, innerSolid)
395 listPipes.append(resultSolid)
397 listPipes.append(outerSolid)
399 if len(listPipes) == 0:
401 elif len(listPipes) == 1:
404 return self.geom.MakeCompound(listPipes)
406 ## Build the markers defining the orientation of the structural element part.
407 def _buildMarkers(self):
409 Build the markers defining the orientation of the structural element
413 paths = self._getSubShapes()
416 (center, vecX) = self._getVertexAndTangentOnOrientedWire(path,
418 marker = self._orientation.buildMarker(self.geom, center, vecX)
419 listMarkers.append(marker)
423 ## This class defines a beam with a circular section. It can be full or
424 # hollow, and its radius and thickness can vary from one end of the beam to
425 # the other. The valid parameters for circular beams are:
426 # - "R1" or "R": radius at the first end of the beam.
427 # - "R2" or "R": radius at the other end of the beam.
428 # - "EP1" or "EP" (optional): thickness at the first end of the beam.
429 # If not specified or equal to 0, the beam is considered full.
430 # - "EP2" or "EP" (optional): thickness at the other end of the beam.
431 # If not specified or equal to 0, the beam is considered full.
433 # See class StructuralElementPart for the description of the other parameters.
435 class CircularBeam(Beam):
437 This class defines a beam with a circular section. It can be full or
438 hollow, and its radius and thickness can vary from one end of the beam to
439 the other. The valid parameters for circular beams are:
441 * "R1" or "R": radius at the first end of the beam.
442 * "R2" or "R": radius at the other end of the beam.
443 * "EP1" or "EP" (optional): thickness at the first end of the beam.
444 If not specified or equal to 0, the beam is considered full.
445 * "EP2" or "EP" (optional): thickness at the other end of the beam.
446 If not specified or equal to 0, the beam is considered full.
448 See class :class:`StructuralElementPart` for the description of the
453 def __init__(self, groupName, groupGeomObj, parameters,
454 name = Beam.DEFAULT_NAME, color = None):
456 if "R1" in parameters: # variable section
458 else: # constant section
461 Beam.__init__(self, groupName, groupGeomObj, parameters,
464 self.R1 = self._getParameter(["R1", "R"])
465 self.R2 = self._getParameter(["R2", "R"])
466 self.EP1 = self._getParameter(["EP1", "EP"])
467 self.EP2 = self._getParameter(["EP2", "EP"])
469 if self.EP1 is None or self.EP2 is None or \
470 self.EP1 == 0 or self.EP2 == 0:
473 self.filling = HOLLOW
475 logger.debug(repr(self))
478 self._checkSize(self.R1, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
479 self._getParamUserName("R1"))
480 self._checkSize(self.R2, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
481 self._getParamUserName("R2"))
482 if self.filling == HOLLOW:
483 self._checkSize(self.EP1, MIN_THICKNESS,
484 self._getParamUserName("EP1"))
485 self._checkSize(self.EP2, MIN_THICKNESS,
486 self._getParamUserName("EP2"))
487 self._checkSize(self.R1 - self.EP1,
488 MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
489 "%s - %s" % (self._getParamUserName("R1"),
490 self._getParamUserName("EP1")))
491 self._checkSize(self.R2 - self.EP2,
492 MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
493 "%s - %s" % (self._getParamUserName("R2"),
494 self._getParamUserName("EP2")))
496 ## Create the circular sections used to build the pipe.
497 def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
499 Create the circular sections used to build the pipe.
501 outerCircle1 = self.geom.MakeCircle(fPoint, fNormal, self.R1)
502 outerCircle2 = self.geom.MakeCircle(lPoint, lNormal, self.R2)
503 if self.filling == HOLLOW:
504 innerCircle1 = self.geom.MakeCircle(fPoint, fNormal,
506 innerCircle2 = self.geom.MakeCircle(lPoint, lNormal,
512 return (outerCircle1, innerCircle1, outerCircle2, innerCircle2)
515 ## This class defines a beam with a rectangular section. It can be full or
516 # hollow, and its dimensions can vary from one end of the beam to the other.
517 # The valid parameters for rectangular beams are:
518 # - "HY1", "HY", "H1" or "H": width at the first end of the beam.
519 # - "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
520 # - "HY2", "HY", "H2" or "H": width at the other end of the beam.
521 # - "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
522 # - "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
523 # direction at the first end of the beam. If not specified or equal to 0,
524 # the beam is considered full.
525 # - "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
526 # direction at the first end of the beam. If not specified or equal to 0,
527 # the beam is considered full.
528 # - "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
529 # direction at the other end of the beam. If not specified or equal to 0,
530 # the beam is considered full.
531 # - "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
532 # direction at the other end of the beam. If not specified or equal to 0,
533 # the beam is considered full.
535 # See class StructuralElementPart for the description of the other parameters.
537 class RectangularBeam(Beam):
539 This class defines a beam with a rectangular section. It can be full or
540 hollow, and its dimensions can vary from one end of the beam to the other.
541 The valid parameters for rectangular beams are:
543 * "HY1", "HY", "H1" or "H": width at the first end of the beam.
544 * "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
545 * "HY2", "HY", "H2" or "H": width at the other end of the beam.
546 * "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
547 * "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
548 direction at the first end of the beam. If not specified or equal to 0,
549 the beam is considered full.
550 * "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
551 direction at the first end of the beam. If not specified or equal to 0,
552 the beam is considered full.
553 * "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
554 direction at the other end of the beam. If not specified or equal to 0,
555 the beam is considered full.
556 * "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
557 direction at the other end of the beam. If not specified or equal to 0,
558 the beam is considered full.
560 See class :class:`StructuralElementPart` for the description of the
565 def __init__(self, groupName, groupGeomObj, parameters,
566 name = Beam.DEFAULT_NAME, color = None):
568 if "HY1" in parameters or "H1" in parameters:
569 color = LIGHT_BLUE # variable section
570 else: # constant section
573 Beam.__init__(self, groupName, groupGeomObj, parameters,
576 self.HY1 = self._getParameter(["HY1", "HY", "H1", "H"])
577 self.HZ1 = self._getParameter(["HZ1", "HZ", "H1", "H"])
578 self.HY2 = self._getParameter(["HY2", "HY", "H2", "H"])
579 self.HZ2 = self._getParameter(["HZ2", "HZ", "H2", "H"])
580 self.EPY1 = self._getParameter(["EPY1", "EPY", "EP1", "EP"])
581 self.EPZ1 = self._getParameter(["EPZ1", "EPZ", "EP1", "EP"])
582 self.EPY2 = self._getParameter(["EPY2", "EPY", "EP2", "EP"])
583 self.EPZ2 = self._getParameter(["EPZ2", "EPZ", "EP2", "EP"])
585 if self.EPY1 is None or self.EPZ1 is None or \
586 self.EPY2 is None or self.EPZ2 is None or \
587 self.EPY1 == 0 or self.EPZ1 == 0 or \
588 self.EPY2 == 0 or self.EPZ2 == 0:
591 self.filling = HOLLOW
593 logger.debug(repr(self))
596 self._checkSize(self.HY1, MIN_DIM_FOR_EXTRUDED_SHAPE,
597 self._getParamUserName("HY1"))
598 self._checkSize(self.HZ1, MIN_DIM_FOR_EXTRUDED_SHAPE,
599 self._getParamUserName("HZ1"))
600 self._checkSize(self.HY2, MIN_DIM_FOR_EXTRUDED_SHAPE,
601 self._getParamUserName("HY2"))
602 self._checkSize(self.HZ2, MIN_DIM_FOR_EXTRUDED_SHAPE,
603 self._getParamUserName("HZ2"))
604 if self.filling == HOLLOW:
605 self._checkSize(self.EPY1, MIN_THICKNESS,
606 self._getParamUserName("EPY1"))
607 self._checkSize(self.EPZ1, MIN_THICKNESS,
608 self._getParamUserName("EPZ1"))
609 self._checkSize(self.EPY2, MIN_THICKNESS,
610 self._getParamUserName("EPY2"))
611 self._checkSize(self.EPZ2, MIN_THICKNESS,
612 self._getParamUserName("EPZ2"))
613 self._checkSize(self.HY1 - 2 * self.EPY1,
614 MIN_DIM_FOR_EXTRUDED_SHAPE,
615 "%s - 2 * %s" % (self._getParamUserName("HY1"),
616 self._getParamUserName("EPY1")))
617 self._checkSize(self.HZ1 - 2 * self.EPZ1,
618 MIN_DIM_FOR_EXTRUDED_SHAPE,
619 "%s - 2 * %s" % (self._getParamUserName("HZ1"),
620 self._getParamUserName("EPZ1")))
621 self._checkSize(self.HY2 - 2 * self.EPY2,
622 MIN_DIM_FOR_EXTRUDED_SHAPE,
623 "%s - 2 * %s" % (self._getParamUserName("HY2"),
624 self._getParamUserName("EPY2")))
625 self._checkSize(self.HZ2 - 2 * self.EPZ2,
626 MIN_DIM_FOR_EXTRUDED_SHAPE,
627 "%s - 2 * %s" % (self._getParamUserName("HZ2"),
628 self._getParamUserName("EPZ2")))
630 ## Create a rectangle in the specified plane.
631 def _makeRectangle(self, HY, HZ, lcs):
633 Create a rectangle in the specified plane.
637 sketchStr = "Sketcher:F %g %g:" % (-halfHY, -halfHZ)
638 sketchStr += "TT %g %g:" % (halfHY, -halfHZ)
639 sketchStr += "TT %g %g:" % (halfHY, halfHZ)
640 sketchStr += "TT %g %g:WW" % (-halfHY, halfHZ)
641 logger.debug('Drawing rectangle: "%s"' % sketchStr)
642 sketch = self.geom.MakeSketcherOnPlane(sketchStr, lcs)
645 ## Create one side of the rectangular sections used to build the pipe.
646 def _makeSectionRectangles(self, point, vecX, HY, HZ, EPY, EPZ):
648 Create one side of the rectangular sections used to build the pipe.
650 (vecY, vecZ) = self._orientation.getVecYZ(self.geom, point, vecX)
651 lcs = self.geom.MakeMarkerPntTwoVec(point, vecY, vecZ)
652 outerRect = self._makeRectangle(HY, HZ, lcs)
653 if self.filling == HOLLOW:
654 innerRect = self._makeRectangle(HY - 2.0 * EPY,
659 return (outerRect, innerRect)
661 ## Create the rectangular sections used to build the pipe.
662 def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
664 Create the rectangular sections used to build the pipe.
666 (outerRect1, innerRect1) = \
667 self._makeSectionRectangles(fPoint, fNormal, self.HY1, self.HZ1,
668 self.EPY1, self.EPZ1)
669 (outerRect2, innerRect2) = \
670 self._makeSectionRectangles(lPoint, lNormal, self.HY2, self.HZ2,
671 self.EPY2, self.EPZ2)
672 return (outerRect1, innerRect1, outerRect2, innerRect2)
675 ## This method finds the value of a parameter in the parameters
676 # dictionary. The argument is a list because some parameters can have
677 # several different names.
679 def getParameterInDict(nameList, parametersDict, default = None):
681 This method finds the value of a parameter in the parameters
682 dictionary. The argument is a list because some parameters can have
683 several different names.
685 for name in nameList:
686 if name in parametersDict:
687 return parametersDict[name]
690 ## This class defines a beam with a generic section. It is represented as a
691 # full rectangular beam with the following parameters:
692 # - HY1 = sqrt(12 * IZ1 / A1)
693 # - HZ1 = sqrt(12 * IY1 / A1)
694 # - HY2 = sqrt(12 * IZ2 / A2)
695 # - HZ2 = sqrt(12 * IY2 / A2)
697 # See StructuralElementPart for the description of the other parameters.
699 class GeneralBeam(RectangularBeam):
701 This class defines a beam with a generic section. It is represented as a
702 full rectangular beam with the following parameters:
704 * HY1 = sqrt(12 * IZ1 / A1)
705 * HZ1 = sqrt(12 * IY1 / A1)
706 * HY2 = sqrt(12 * IZ2 / A2)
707 * HZ2 = sqrt(12 * IY2 / A2)
709 See class :class:`StructuralElementPart` for the description of the other
713 def __init__(self, groupName, groupGeomObj, parameters,
714 name = Beam.DEFAULT_NAME, color = None):
715 self.IY1 = getParameterInDict(["IY1", "IY"], parameters)
716 self.IZ1 = getParameterInDict(["IZ1", "IZ"], parameters)
717 self.IY2 = getParameterInDict(["IY2", "IY"], parameters)
718 self.IZ2 = getParameterInDict(["IZ2", "IZ"], parameters)
719 self.A1 = getParameterInDict(["A1", "A"], parameters)
720 self.A2 = getParameterInDict(["A2", "A"], parameters)
721 parameters["HY1"] = math.sqrt(12 * self.IZ1 / self.A1)
722 parameters["HZ1"] = math.sqrt(12 * self.IY1 / self.A1)
723 parameters["HY2"] = math.sqrt(12 * self.IZ2 / self.A2)
724 parameters["HZ2"] = math.sqrt(12 * self.IY2 / self.A2)
727 if "IY1" in parameters: # variable section
729 else: # constant section
732 RectangularBeam.__init__(self, groupName, groupGeomObj, parameters,
735 ## This class is an "abstract" class for all 2D structural element parts. It
736 # should not be instantiated directly.
737 # See class StructuralElementPart for the description of the parameters.
739 class StructuralElementPart2D(StructuralElementPart):
741 This class is an "abstract" class for all 2D structural element parts. It
742 should not be instantiated directly. See class
743 :class:`StructuralElementPart` for the description of the parameters.
746 DEFAULT_NAME = "StructuralElementPart2D"
748 def __init__(self, groupName, groupGeomObj, parameters,
749 name = DEFAULT_NAME):
750 StructuralElementPart.__init__(self, groupName, groupGeomObj,
752 self._orientation = orientation.Orientation2D(
753 self._getParameter(["angleAlpha"]),
754 self._getParameter(["angleBeta"]),
755 self._getParameter(["Vecteur"]))
756 self.offset = self._getParameter(["Excentre"], 0.0)
758 ## Create a copy of a face at a given offset.
759 def _makeFaceOffset(self, face, offset, epsilon = 1e-6):
761 Create a copy of a face at a given offset.
763 if abs(offset) < epsilon:
764 return self.geom.MakeCopy(face)
766 offsetObj = self.geom.MakeOffset(face, offset)
767 # We have to explode the resulting object into faces because it is
768 # created as a polyhedron and not as a single face
769 faces = self.geom.SubShapeAll(offsetObj,
770 self.geom.ShapeType["FACE"])
773 ## Build the markers for the structural element part with a given offset
774 # from the base face.
775 def _buildMarkersWithOffset(self, offset):
777 Build the markers for the structural element part with a given offset
783 subShapes = self._getSubShapes()
785 for subShape in subShapes:
786 faces = self.geom.SubShapeAll(subShape,
787 self.geom.ShapeType["FACE"])
789 offsetFace = self._makeFaceOffset(face, offset)
790 # get the center of the face and the normal at the center
791 center = self.geom.MakeVertexOnSurface(offsetFace,
793 normal = self.geom.GetNormal(offsetFace, center)
794 marker = self._orientation.buildMarker(self.geom,
796 listMarkers.append(marker)
800 ## This class defines a shell with a given thickness. It can be shifted from
801 # the base face. The valid parameters for thick shells are:
802 # - "Epais": thickness of the shell.
803 # - "Excentre": offset of the shell from the base face.
804 # - "angleAlpha": angle used to build the markers (see class
805 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
806 # - "angleBeta": angle used to build the markers (see class
807 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
808 # - "Vecteur": vector used instead of the angles to build the markers (see
809 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
811 # See class StructuralElementPart for the description of the other parameters.
813 class ThickShell(StructuralElementPart2D):
815 This class defines a shell with a given thickness. It can be shifted from
816 the base face. The valid parameters for thick shells are:
818 * "Epais": thickness of the shell.
819 * "Excentre": offset of the shell from the base face.
820 * "angleAlpha": angle used to build the markers (see class
821 :class:`~salome.geom.structelem.orientation.Orientation2D`)
822 * "angleBeta": angle used to build the markers (see class
823 :class:`~salome.geom.structelem.orientation.Orientation2D`)
824 * "Vecteur": vector used instead of the angles to build the markers (see
825 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
827 See class :class:`StructuralElementPart` for the description of the
831 DEFAULT_NAME = "ThickShell"
833 def __init__(self, groupName, groupGeomObj, parameters,
834 name = DEFAULT_NAME):
835 StructuralElementPart2D.__init__(self, groupName,
836 groupGeomObj, parameters, name)
837 self.thickness = self._getParameter(["Epais"])
838 logger.debug(repr(self))
840 ## Create the geometrical shapes corresponding to the thick shell.
841 def _buildPart(self):
843 Create the geometrical shapes corresponding to the thick shell.
845 subShapes = self._getSubShapes()
848 for subShape in subShapes:
849 faces = self.geom.SubShapeAll(subShape,
850 self.geom.ShapeType["FACE"])
852 shape = self._buildThickShellForFace(face)
853 listSolids.append(shape)
855 if len(listSolids) == 0:
857 elif len(listSolids) == 1:
860 return self.geom.MakeCompound(listSolids)
862 ## Create the geometrical shapes corresponding to the thick shell for a
864 def _buildThickShellForFace(self, face):
866 Create the geometrical shapes corresponding to the thick shell for a
870 if self.thickness < 2 * epsilon:
871 return self._makeFaceOffset(face, self.offset, epsilon)
873 upperOffset = self.offset + self.thickness / 2.0
874 lowerOffset = self.offset - self.thickness / 2.0
878 upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
879 lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
880 listShapes = [upperFace, lowerFace]
881 upperWires = self.geom.SubShapeAll(upperFace,
882 self.geom.ShapeType["WIRE"])
883 lowerWires = self.geom.SubShapeAll(lowerFace,
884 self.geom.ShapeType["WIRE"])
885 if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
886 # if the face is a cylinder, we remove the extra side edge
887 upperWires = self._removeCylinderExtraEdge(upperWires)
888 lowerWires = self._removeCylinderExtraEdge(lowerWires)
889 for i in range(len(upperWires)):
890 resShape = self.geom.MakeThruSections([upperWires[i],
894 listShapes.append(resShape)
895 resultShell = self.geom.MakeShell(listShapes)
896 resultSolid = self.geom.MakeSolid([resultShell])
899 ## Remove the side edge in a cylinder.
900 def _removeCylinderExtraEdge(self, wires):
902 Remove the side edge in a cylinder.
906 edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
908 if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
912 ## Build the markers defining the orientation of the thick shell.
913 def _buildMarkers(self):
915 Build the markers defining the orientation of the thick shell.
917 return self._buildMarkersWithOffset(self.offset +
918 self.thickness / 2.0)
920 ## This class defines a grid. A grid is represented by a 2D face patterned
921 # with small lines in the main direction of the grid frame. The valid
922 # parameters for grids are:
923 # - "Excentre": offset of the grid from the base face.
924 # - "angleAlpha": angle used to build the markers (see class
925 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
926 # - "angleBeta": angle used to build the markers (see class
927 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
928 # - "Vecteur": vector used instead of the angles to build the markers (see
929 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
930 # - "origAxeX": X coordinate of the origin of the axis used to determine the
931 # orientation of the frame in the case of a cylindrical grid.
932 # - "origAxeY": Y coordinate of the origin of the axis used to determine the
933 # orientation of the frame in the case of a cylindrical grid.
934 # - "origAxeZ": Z coordinate of the origin of the axis used to determine the
935 # orientation of the frame in the case of a cylindrical grid.
936 # - "axeX": X coordinate of the axis used to determine the orientation of
937 # the frame in the case of a cylindrical grid.
938 # - "axeY": Y coordinate of the axis used to determine the orientation of
939 # the frame in the case of a cylindrical grid.
940 # - "axeZ": Z coordinate of the axis used to determine the orientation of
941 # the frame in the case of a cylindrical grid.
943 # See class StructuralElementPart for the description of the other parameters.
945 class Grid(StructuralElementPart2D):
947 This class defines a grid. A grid is represented by a 2D face patterned
948 with small lines in the main direction of the grid frame. The valid
949 parameters for grids are:
951 * "Excentre": offset of the grid from the base face.
952 * "angleAlpha": angle used to build the markers (see class
953 :class:`~salome.geom.structelem.orientation.Orientation2D`)
954 * "angleBeta": angle used to build the markers (see class
955 :class:`~salome.geom.structelem.orientation.Orientation2D`)
956 * "Vecteur": vector used instead of the angles to build the markers (see
957 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
958 * "origAxeX": X coordinate of the origin of the axis used to determine the
959 orientation of the frame in the case of a cylindrical grid.
960 * "origAxeY": Y coordinate of the origin of the axis used to determine the
961 orientation of the frame in the case of a cylindrical grid.
962 * "origAxeZ": Z coordinate of the origin of the axis used to determine the
963 orientation of the frame in the case of a cylindrical grid.
964 * "axeX": X coordinate of the axis used to determine the orientation of
965 the frame in the case of a cylindrical grid.
966 * "axeY": Y coordinate of the axis used to determine the orientation of
967 the frame in the case of a cylindrical grid.
968 * "axeZ": Z coordinate of the axis used to determine the orientation of
969 the frame in the case of a cylindrical grid.
971 See class :class:`StructuralElementPart` for the description of the
975 DEFAULT_NAME = "Grid"
977 def __init__(self, groupName, groupGeomObj, parameters,
978 name = DEFAULT_NAME):
979 StructuralElementPart2D.__init__(self, groupName,
980 groupGeomObj, parameters, name)
981 self.xr = self._getParameter(["origAxeX"])
982 self.yr = self._getParameter(["origAxeY"])
983 self.zr = self._getParameter(["origAxeZ"])
984 self.vx = self._getParameter(["axeX"])
985 self.vy = self._getParameter(["axeY"])
986 self.vz = self._getParameter(["axeZ"])
987 logger.debug(repr(self))
989 ## Create the geometrical shapes representing the grid.
990 def _buildPart(self):
992 Create the geometrical shapes representing the grid.
994 subShapes = self._getSubShapes()
997 for subShape in subShapes:
998 faces = self.geom.SubShapeAll(subShape,
999 self.geom.ShapeType["FACE"])
1001 if self.geom.KindOfShape(face)[0] == \
1002 self.geom.kind.CYLINDER2D and \
1003 self.xr is not None and self.yr is not None and \
1004 self.zr is not None and self.vx is not None and \
1005 self.vy is not None and self.vz is not None:
1006 shape = self._buildGridForCylinderFace(face)
1008 shape = self._buildGridForNormalFace(face)
1009 listGridShapes.append(shape)
1011 if len(listGridShapes) == 0:
1013 elif len(listGridShapes) == 1:
1014 return listGridShapes[0]
1016 return self.geom.MakeCompound(listGridShapes)
1018 ## Create the geometrical shapes representing the grid for a given
1019 # non-cylindrical face.
1020 def _buildGridForNormalFace(self, face):
1022 Create the geometrical shapes representing the grid for a given
1023 non-cylindrical face.
1025 baseFace = self._makeFaceOffset(face, self.offset)
1026 gridList = [baseFace]
1028 # Compute display length for grid elements
1029 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1030 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1031 length = self.geom.MinDistance(p1, p2) / 2.0
1033 for u in range(1, 10):
1035 for v in range(1, 10):
1037 # get tangent plane on surface by parameters
1038 center = self.geom.MakeVertexOnSurface(baseFace,
1040 tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
1043 # use the marker to get the orientation of the frame
1044 normal = self.geom.GetNormal(tangPlane)
1045 marker = self._orientation.buildMarker(self.geom, center,
1047 [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
1048 xPoint = self.geom.MakeTranslation(center, Xx * length,
1049 Xy * length, Xz * length)
1050 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1051 gridList.append(gridLine)
1052 grid = self.geom.MakeCompound(gridList)
1055 ## Create the geometrical shapes representing the grid for a given
1057 def _buildGridForCylinderFace(self, face):
1059 Create the geometrical shapes representing the grid for a given
1062 baseFace = self._makeFaceOffset(face, self.offset)
1063 gridList = [baseFace]
1065 # Compute display length for grid elements
1066 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1067 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1068 length = self.geom.MinDistance(p1, p2) / 2.0
1070 # Create reference vector V
1071 origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
1072 vPoint = self.geom.MakeTranslation(origPoint,
1073 self.vx, self.vy, self.vz)
1074 refVec = self.geom.MakeVector(origPoint, vPoint)
1078 for v in range(1, 10):
1081 # Compute the local orientation of the frame
1082 center = self.geom.MakeVertexOnSurface(baseFace,
1084 locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
1086 locOrient = self.geom.GetNormal(locPlaneYZ)
1087 xPoint = self.geom.MakeTranslationVectorDistance(center,
1090 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1091 gridList.append(gridLine)
1093 grid = self.geom.MakeCompound(gridList)
1096 ## Create the markers defining the orientation of the grid.
1097 def _buildMarkers(self):
1099 Create the markers defining the orientation of the grid.
1101 return self._buildMarkersWithOffset(self.offset)
1103 ## Alias for class GeneralBeam.
1105 def VisuPoutreGenerale(groupName, groupGeomObj, parameters,
1108 Alias for class :class:`GeneralBeam`.
1110 return GeneralBeam(groupName, groupGeomObj, parameters, name)
1112 ## Alias for class CircularBeam.
1114 def VisuPoutreCercle(groupName, groupGeomObj, parameters,
1117 Alias for class :class:`CircularBeam`.
1119 return CircularBeam(groupName, groupGeomObj, parameters, name)
1121 ## Alias for class RectangularBeam.
1123 def VisuPoutreRectangle(groupName, groupGeomObj, parameters,
1126 Alias for class :class:`RectangularBeam`.
1128 return RectangularBeam(groupName, groupGeomObj, parameters, name)
1130 ## Alias for class GeneralBeam.
1132 def VisuBarreGenerale(groupName, groupGeomObj, parameters,
1135 Alias for class :class:`GeneralBeam`.
1137 return GeneralBeam(groupName, groupGeomObj, parameters, name,
1140 ## Alias for class RectangularBeam.
1142 def VisuBarreRectangle(groupName, groupGeomObj, parameters,
1145 Alias for class :class:`RectangularBeam`.
1147 return RectangularBeam(groupName, groupGeomObj, parameters, name,
1150 ## Alias for class CircularBeam.
1152 def VisuBarreCercle(groupName, groupGeomObj, parameters,
1155 Alias for class :class:`CircularBeam`.
1157 return CircularBeam(groupName, groupGeomObj, parameters, name,
1160 ## Alias for class CircularBeam.
1162 def VisuCable(groupName, groupGeomObj, parameters, name = "CABLE"):
1164 Alias for class :class:`CircularBeam`.
1166 return CircularBeam(groupName, groupGeomObj, parameters, name,
1169 ## Alias for class ThickShell.
1171 def VisuCoque(groupName, groupGeomObj, parameters, name = "COQUE"):
1173 Alias for class :class:`ThickShell`.
1175 return ThickShell(groupName, groupGeomObj, parameters, name)
1177 ## Alias for class Grid.
1179 def VisuGrille(groupName, groupGeomObj, parameters, name = "GRILLE"):
1181 Alias for class :class:`Grid`.
1183 return Grid(groupName, groupGeomObj, parameters, name)