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],
366 closedShell = self.geom.MakeShell([face1, face2, shell])
367 solid = self.geom.MakeSolid([closedShell])
370 ## Build the structural element part.
371 def _buildPart(self):
373 Build the structural element part.
375 # Get all the sub-shapes in the group (normally only edges and wires)
376 paths = self._getSubShapes()
379 withCorrection = False
382 # Build the sections (rectangular or circular) at each end of the
384 (fPoint, fNormal) = self._getVertexAndTangentOnOrientedWire(path,
386 (lPoint, lNormal) = self._getVertexAndTangentOnOrientedWire(path,
388 (outerWire1, innerWire1, outerWire2, innerWire2) = \
389 self._makeSectionWires(fPoint, fNormal, lPoint, lNormal)
391 # Create the resulting solid
392 outerSolid = self._makeSolidPipeFromWires(outerWire1, outerWire2,
393 fPoint, lPoint, path)
394 if self.filling == HOLLOW:
395 innerSolid = self._makeSolidPipeFromWires(innerWire1,
398 resultSolid = self.geom.MakeCut(outerSolid, innerSolid)
399 listPipes.append(resultSolid)
401 listPipes.append(outerSolid)
403 if len(listPipes) == 0:
405 elif len(listPipes) == 1:
408 return self.geom.MakeCompound(listPipes)
410 ## Build the markers defining the orientation of the structural element part.
411 def _buildMarkers(self):
413 Build the markers defining the orientation of the structural element
417 paths = self._getSubShapes()
420 (center, vecX) = self._getVertexAndTangentOnOrientedWire(path,
422 marker = self._orientation.buildMarker(self.geom, center, vecX)
423 listMarkers.append(marker)
427 ## This class defines a beam with a circular section. It can be full or
428 # hollow, and its radius and thickness can vary from one end of the beam to
429 # the other. The valid parameters for circular beams are:
430 # - "R1" or "R": radius at the first end of the beam.
431 # - "R2" or "R": radius at the other end of the beam.
432 # - "EP1" or "EP" (optional): thickness at the first end of the beam.
433 # If not specified or equal to 0, the beam is considered full.
434 # - "EP2" or "EP" (optional): thickness at the other end of the beam.
435 # If not specified or equal to 0, the beam is considered full.
437 # See class StructuralElementPart for the description of the other parameters.
439 class CircularBeam(Beam):
441 This class defines a beam with a circular section. It can be full or
442 hollow, and its radius and thickness can vary from one end of the beam to
443 the other. The valid parameters for circular beams are:
445 * "R1" or "R": radius at the first end of the beam.
446 * "R2" or "R": radius at the other end of the beam.
447 * "EP1" or "EP" (optional): thickness at the first end of the beam.
448 If not specified or equal to 0, the beam is considered full.
449 * "EP2" or "EP" (optional): thickness at the other end of the beam.
450 If not specified or equal to 0, the beam is considered full.
452 See class :class:`StructuralElementPart` for the description of the
457 def __init__(self, studyId, groupName, groupGeomObj, parameters,
458 name = Beam.DEFAULT_NAME, color = None):
460 if parameters.has_key("R1"): # variable section
462 else: # constant section
465 Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
468 self.R1 = self._getParameter(["R1", "R"])
469 self.R2 = self._getParameter(["R2", "R"])
470 self.EP1 = self._getParameter(["EP1", "EP"])
471 self.EP2 = self._getParameter(["EP2", "EP"])
473 if self.EP1 is None or self.EP2 is None or \
474 self.EP1 == 0 or self.EP2 == 0:
477 self.filling = HOLLOW
479 logger.debug(repr(self))
482 self._checkSize(self.R1, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
483 self._getParamUserName("R1"))
484 self._checkSize(self.R2, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
485 self._getParamUserName("R2"))
486 if self.filling == HOLLOW:
487 self._checkSize(self.EP1, MIN_THICKNESS,
488 self._getParamUserName("EP1"))
489 self._checkSize(self.EP2, MIN_THICKNESS,
490 self._getParamUserName("EP2"))
491 self._checkSize(self.R1 - self.EP1,
492 MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
493 "%s - %s" % (self._getParamUserName("R1"),
494 self._getParamUserName("EP1")))
495 self._checkSize(self.R2 - self.EP2,
496 MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
497 "%s - %s" % (self._getParamUserName("R2"),
498 self._getParamUserName("EP2")))
500 ## Create the circular sections used to build the pipe.
501 def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
503 Create the circular sections used to build the pipe.
505 outerCircle1 = self.geom.MakeCircle(fPoint, fNormal, self.R1)
506 outerCircle2 = self.geom.MakeCircle(lPoint, lNormal, self.R2)
507 if self.filling == HOLLOW:
508 innerCircle1 = self.geom.MakeCircle(fPoint, fNormal,
510 innerCircle2 = self.geom.MakeCircle(lPoint, lNormal,
516 return (outerCircle1, innerCircle1, outerCircle2, innerCircle2)
519 ## This class defines a beam with a rectangular section. It can be full or
520 # hollow, and its dimensions can vary from one end of the beam to the other.
521 # The valid parameters for rectangular beams are:
522 # - "HY1", "HY", "H1" or "H": width at the first end of the beam.
523 # - "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
524 # - "HY2", "HY", "H2" or "H": width at the other end of the beam.
525 # - "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
526 # - "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
527 # direction at the first end of the beam. If not specified or equal to 0,
528 # the beam is considered full.
529 # - "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
530 # direction at the first end of the beam. If not specified or equal to 0,
531 # the beam is considered full.
532 # - "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
533 # direction at the other end of the beam. If not specified or equal to 0,
534 # the beam is considered full.
535 # - "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
536 # direction at the other end of the beam. If not specified or equal to 0,
537 # the beam is considered full.
539 # See class StructuralElementPart for the description of the other parameters.
541 class RectangularBeam(Beam):
543 This class defines a beam with a rectangular section. It can be full or
544 hollow, and its dimensions can vary from one end of the beam to the other.
545 The valid parameters for rectangular beams are:
547 * "HY1", "HY", "H1" or "H": width at the first end of the beam.
548 * "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
549 * "HY2", "HY", "H2" or "H": width at the other end of the beam.
550 * "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
551 * "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
552 direction at the first end of the beam. If not specified or equal to 0,
553 the beam is considered full.
554 * "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
555 direction at the first end of the beam. If not specified or equal to 0,
556 the beam is considered full.
557 * "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
558 direction at the other end of the beam. If not specified or equal to 0,
559 the beam is considered full.
560 * "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
561 direction at the other end of the beam. If not specified or equal to 0,
562 the beam is considered full.
564 See class :class:`StructuralElementPart` for the description of the
569 def __init__(self, studyId, groupName, groupGeomObj, parameters,
570 name = Beam.DEFAULT_NAME, color = None):
572 if parameters.has_key("HY1") or parameters.has_key("H1"):
573 color = LIGHT_BLUE # variable section
574 else: # constant section
577 Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
580 self.HY1 = self._getParameter(["HY1", "HY", "H1", "H"])
581 self.HZ1 = self._getParameter(["HZ1", "HZ", "H1", "H"])
582 self.HY2 = self._getParameter(["HY2", "HY", "H2", "H"])
583 self.HZ2 = self._getParameter(["HZ2", "HZ", "H2", "H"])
584 self.EPY1 = self._getParameter(["EPY1", "EPY", "EP1", "EP"])
585 self.EPZ1 = self._getParameter(["EPZ1", "EPZ", "EP1", "EP"])
586 self.EPY2 = self._getParameter(["EPY2", "EPY", "EP2", "EP"])
587 self.EPZ2 = self._getParameter(["EPZ2", "EPZ", "EP2", "EP"])
589 if self.EPY1 is None or self.EPZ1 is None or \
590 self.EPY2 is None or self.EPZ2 is None or \
591 self.EPY1 == 0 or self.EPZ1 == 0 or \
592 self.EPY2 == 0 or self.EPZ2 == 0:
595 self.filling = HOLLOW
597 logger.debug(repr(self))
600 self._checkSize(self.HY1, MIN_DIM_FOR_EXTRUDED_SHAPE,
601 self._getParamUserName("HY1"))
602 self._checkSize(self.HZ1, MIN_DIM_FOR_EXTRUDED_SHAPE,
603 self._getParamUserName("HZ1"))
604 self._checkSize(self.HY2, MIN_DIM_FOR_EXTRUDED_SHAPE,
605 self._getParamUserName("HY2"))
606 self._checkSize(self.HZ2, MIN_DIM_FOR_EXTRUDED_SHAPE,
607 self._getParamUserName("HZ2"))
608 if self.filling == HOLLOW:
609 self._checkSize(self.EPY1, MIN_THICKNESS,
610 self._getParamUserName("EPY1"))
611 self._checkSize(self.EPZ1, MIN_THICKNESS,
612 self._getParamUserName("EPZ1"))
613 self._checkSize(self.EPY2, MIN_THICKNESS,
614 self._getParamUserName("EPY2"))
615 self._checkSize(self.EPZ2, MIN_THICKNESS,
616 self._getParamUserName("EPZ2"))
617 self._checkSize(self.HY1 - 2 * self.EPY1,
618 MIN_DIM_FOR_EXTRUDED_SHAPE,
619 "%s - 2 * %s" % (self._getParamUserName("HY1"),
620 self._getParamUserName("EPY1")))
621 self._checkSize(self.HZ1 - 2 * self.EPZ1,
622 MIN_DIM_FOR_EXTRUDED_SHAPE,
623 "%s - 2 * %s" % (self._getParamUserName("HZ1"),
624 self._getParamUserName("EPZ1")))
625 self._checkSize(self.HY2 - 2 * self.EPY2,
626 MIN_DIM_FOR_EXTRUDED_SHAPE,
627 "%s - 2 * %s" % (self._getParamUserName("HY2"),
628 self._getParamUserName("EPY2")))
629 self._checkSize(self.HZ2 - 2 * self.EPZ2,
630 MIN_DIM_FOR_EXTRUDED_SHAPE,
631 "%s - 2 * %s" % (self._getParamUserName("HZ2"),
632 self._getParamUserName("EPZ2")))
634 ## Create a rectangle in the specified plane.
635 def _makeRectangle(self, HY, HZ, lcs):
637 Create a rectangle in the specified plane.
641 sketchStr = "Sketcher:F %g %g:" % (-halfHY, -halfHZ)
642 sketchStr += "TT %g %g:" % (halfHY, -halfHZ)
643 sketchStr += "TT %g %g:" % (halfHY, halfHZ)
644 sketchStr += "TT %g %g:WW" % (-halfHY, halfHZ)
645 logger.debug('Drawing rectangle: "%s"' % sketchStr)
646 sketch = self.geom.MakeSketcherOnPlane(sketchStr, lcs)
649 ## Create one side of the rectangular sections used to build the pipe.
650 def _makeSectionRectangles(self, point, vecX, HY, HZ, EPY, EPZ):
652 Create one side of the rectangular sections used to build the pipe.
654 (vecY, vecZ) = self._orientation.getVecYZ(self.geom, point, vecX)
655 lcs = self.geom.MakeMarkerPntTwoVec(point, vecY, vecZ)
656 outerRect = self._makeRectangle(HY, HZ, lcs)
657 if self.filling == HOLLOW:
658 innerRect = self._makeRectangle(HY - 2.0 * EPY,
663 return (outerRect, innerRect)
665 ## Create the rectangular sections used to build the pipe.
666 def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
668 Create the rectangular sections used to build the pipe.
670 (outerRect1, innerRect1) = \
671 self._makeSectionRectangles(fPoint, fNormal, self.HY1, self.HZ1,
672 self.EPY1, self.EPZ1)
673 (outerRect2, innerRect2) = \
674 self._makeSectionRectangles(lPoint, lNormal, self.HY2, self.HZ2,
675 self.EPY2, self.EPZ2)
676 return (outerRect1, innerRect1, outerRect2, innerRect2)
679 ## This method finds the value of a parameter in the parameters
680 # dictionary. The argument is a list because some parameters can have
681 # several different names.
683 def getParameterInDict(nameList, parametersDict, default = None):
685 This method finds the value of a parameter in the parameters
686 dictionary. The argument is a list because some parameters can have
687 several different names.
689 for name in nameList:
690 if parametersDict.has_key(name):
691 return parametersDict[name]
694 ## This class defines a beam with a generic section. It is represented as a
695 # full rectangular beam with the following parameters:
696 # - HY1 = sqrt(12 * IZ1 / A1)
697 # - HZ1 = sqrt(12 * IY1 / A1)
698 # - HY2 = sqrt(12 * IZ2 / A2)
699 # - HZ2 = sqrt(12 * IY2 / A2)
701 # See StructuralElementPart for the description of the other parameters.
703 class GeneralBeam(RectangularBeam):
705 This class defines a beam with a generic section. It is represented as a
706 full rectangular beam with the following parameters:
708 * HY1 = sqrt(12 * IZ1 / A1)
709 * HZ1 = sqrt(12 * IY1 / A1)
710 * HY2 = sqrt(12 * IZ2 / A2)
711 * HZ2 = sqrt(12 * IY2 / A2)
713 See class :class:`StructuralElementPart` for the description of the other
717 def __init__(self, studyId, groupName, groupGeomObj, parameters,
718 name = Beam.DEFAULT_NAME, color = None):
719 self.IY1 = getParameterInDict(["IY1", "IY"], parameters)
720 self.IZ1 = getParameterInDict(["IZ1", "IZ"], parameters)
721 self.IY2 = getParameterInDict(["IY2", "IY"], parameters)
722 self.IZ2 = getParameterInDict(["IZ2", "IZ"], parameters)
723 self.A1 = getParameterInDict(["A1", "A"], parameters)
724 self.A2 = getParameterInDict(["A2", "A"], parameters)
725 parameters["HY1"] = math.sqrt(12 * self.IZ1 / self.A1)
726 parameters["HZ1"] = math.sqrt(12 * self.IY1 / self.A1)
727 parameters["HY2"] = math.sqrt(12 * self.IZ2 / self.A2)
728 parameters["HZ2"] = math.sqrt(12 * self.IY2 / self.A2)
731 if parameters.has_key("IY1"): # variable section
733 else: # constant section
736 RectangularBeam.__init__(self, studyId, groupName, groupGeomObj, parameters,
739 ## This class is an "abstract" class for all 2D structural element parts. It
740 # should not be instantiated directly.
741 # See class StructuralElementPart for the description of the parameters.
743 class StructuralElementPart2D(StructuralElementPart):
745 This class is an "abstract" class for all 2D structural element parts. It
746 should not be instantiated directly. See class
747 :class:`StructuralElementPart` for the description of the parameters.
750 DEFAULT_NAME = "StructuralElementPart2D"
752 def __init__(self, studyId, groupName, groupGeomObj, parameters,
753 name = DEFAULT_NAME):
754 StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
756 self._orientation = orientation.Orientation2D(
757 self._getParameter(["angleAlpha"]),
758 self._getParameter(["angleBeta"]),
759 self._getParameter(["Vecteur"]))
760 self.offset = self._getParameter(["Excentre"], 0.0)
762 ## Create a copy of a face at a given offset.
763 def _makeFaceOffset(self, face, offset, epsilon = 1e-6):
765 Create a copy of a face at a given offset.
767 if abs(offset) < epsilon:
768 return self.geom.MakeCopy(face)
770 offsetObj = self.geom.MakeOffset(face, offset)
771 # We have to explode the resulting object into faces because it is
772 # created as a polyhedron and not as a single face
773 faces = self.geom.SubShapeAll(offsetObj,
774 self.geom.ShapeType["FACE"])
777 ## Build the markers for the structural element part with a given offset
778 # from the base face.
779 def _buildMarkersWithOffset(self, offset):
781 Build the markers for the structural element part with a given offset
787 subShapes = self._getSubShapes()
789 for subShape in subShapes:
790 faces = self.geom.SubShapeAll(subShape,
791 self.geom.ShapeType["FACE"])
793 offsetFace = self._makeFaceOffset(face, offset)
794 # get the center of the face and the normal at the center
795 center = self.geom.MakeVertexOnSurface(offsetFace,
797 normal = self.geom.GetNormal(offsetFace, center)
798 marker = self._orientation.buildMarker(self.geom,
800 listMarkers.append(marker)
804 ## This class defines a shell with a given thickness. It can be shifted from
805 # the base face. The valid parameters for thick shells are:
806 # - "Epais": thickness of the shell.
807 # - "Excentre": offset of the shell from the base face.
808 # - "angleAlpha": angle used to build the markers (see class
809 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
810 # - "angleBeta": angle used to build the markers (see class
811 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
812 # - "Vecteur": vector used instead of the angles to build the markers (see
813 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
815 # See class StructuralElementPart for the description of the other parameters.
817 class ThickShell(StructuralElementPart2D):
819 This class defines a shell with a given thickness. It can be shifted from
820 the base face. The valid parameters for thick shells are:
822 * "Epais": thickness of the shell.
823 * "Excentre": offset of the shell from the base face.
824 * "angleAlpha": angle used to build the markers (see class
825 :class:`~salome.geom.structelem.orientation.Orientation2D`)
826 * "angleBeta": angle used to build the markers (see class
827 :class:`~salome.geom.structelem.orientation.Orientation2D`)
828 * "Vecteur": vector used instead of the angles to build the markers (see
829 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
831 See class :class:`StructuralElementPart` for the description of the
835 DEFAULT_NAME = "ThickShell"
837 def __init__(self, studyId, groupName, groupGeomObj, parameters,
838 name = DEFAULT_NAME):
839 StructuralElementPart2D.__init__(self, studyId, groupName,
840 groupGeomObj, parameters, name)
841 self.thickness = self._getParameter(["Epais"])
842 logger.debug(repr(self))
844 ## Create the geometrical shapes corresponding to the thick shell.
845 def _buildPart(self):
847 Create the geometrical shapes corresponding to the thick shell.
849 subShapes = self._getSubShapes()
852 for subShape in subShapes:
853 faces = self.geom.SubShapeAll(subShape,
854 self.geom.ShapeType["FACE"])
856 shape = self._buildThickShellForFace(face)
857 listSolids.append(shape)
859 if len(listSolids) == 0:
861 elif len(listSolids) == 1:
864 return self.geom.MakeCompound(listSolids)
866 ## Create the geometrical shapes corresponding to the thick shell for a
868 def _buildThickShellForFace(self, face):
870 Create the geometrical shapes corresponding to the thick shell for a
874 if self.thickness < 2 * epsilon:
875 return self._makeFaceOffset(face, self.offset, epsilon)
877 upperOffset = self.offset + self.thickness / 2.0
878 lowerOffset = self.offset - self.thickness / 2.0
882 upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
883 lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
884 listShapes = [upperFace, lowerFace]
885 upperWires = self.geom.SubShapeAll(upperFace,
886 self.geom.ShapeType["WIRE"])
887 lowerWires = self.geom.SubShapeAll(lowerFace,
888 self.geom.ShapeType["WIRE"])
889 if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
890 # if the face is a cylinder, we remove the extra side edge
891 upperWires = self._removeCylinderExtraEdge(upperWires)
892 lowerWires = self._removeCylinderExtraEdge(lowerWires)
893 for i in range(len(upperWires)):
894 resShape = self.geom.MakeThruSections([upperWires[i],
898 listShapes.append(resShape)
899 resultShell = self.geom.MakeShell(listShapes)
900 resultSolid = self.geom.MakeSolid([resultShell])
903 ## Remove the side edge in a cylinder.
904 def _removeCylinderExtraEdge(self, wires):
906 Remove the side edge in a cylinder.
910 edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
912 if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
916 ## Build the markers defining the orientation of the thick shell.
917 def _buildMarkers(self):
919 Build the markers defining the orientation of the thick shell.
921 return self._buildMarkersWithOffset(self.offset +
922 self.thickness / 2.0)
924 ## This class defines a grid. A grid is represented by a 2D face patterned
925 # with small lines in the main direction of the grid frame. The valid
926 # parameters for grids are:
927 # - "Excentre": offset of the grid from the base face.
928 # - "angleAlpha": angle used to build the markers (see class
929 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
930 # - "angleBeta": angle used to build the markers (see class
931 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
932 # - "Vecteur": vector used instead of the angles to build the markers (see
933 # \ref orientation.Orientation2D "salome.geom.structelem.orientation.Orientation2D")
934 # - "origAxeX": X coordinate of the origin of the axis used to determine the
935 # orientation of the frame in the case of a cylindrical grid.
936 # - "origAxeY": Y coordinate of the origin of the axis used to determine the
937 # orientation of the frame in the case of a cylindrical grid.
938 # - "origAxeZ": Z coordinate of the origin of the axis used to determine the
939 # orientation of the frame in the case of a cylindrical grid.
940 # - "axeX": X coordinate of the axis used to determine the orientation of
941 # the frame in the case of a cylindrical grid.
942 # - "axeY": Y coordinate of the axis used to determine the orientation of
943 # the frame in the case of a cylindrical grid.
944 # - "axeZ": Z coordinate of the axis used to determine the orientation of
945 # the frame in the case of a cylindrical grid.
947 # See class StructuralElementPart for the description of the other parameters.
949 class Grid(StructuralElementPart2D):
951 This class defines a grid. A grid is represented by a 2D face patterned
952 with small lines in the main direction of the grid frame. The valid
953 parameters for grids are:
955 * "Excentre": offset of the grid from the base face.
956 * "angleAlpha": angle used to build the markers (see class
957 :class:`~salome.geom.structelem.orientation.Orientation2D`)
958 * "angleBeta": angle used to build the markers (see class
959 :class:`~salome.geom.structelem.orientation.Orientation2D`)
960 * "Vecteur": vector used instead of the angles to build the markers (see
961 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
962 * "origAxeX": X coordinate of the origin of the axis used to determine the
963 orientation of the frame in the case of a cylindrical grid.
964 * "origAxeY": Y coordinate of the origin of the axis used to determine the
965 orientation of the frame in the case of a cylindrical grid.
966 * "origAxeZ": Z coordinate of the origin of the axis used to determine the
967 orientation of the frame in the case of a cylindrical grid.
968 * "axeX": X coordinate of the axis used to determine the orientation of
969 the frame in the case of a cylindrical grid.
970 * "axeY": Y coordinate of the axis used to determine the orientation of
971 the frame in the case of a cylindrical grid.
972 * "axeZ": Z coordinate of the axis used to determine the orientation of
973 the frame in the case of a cylindrical grid.
975 See class :class:`StructuralElementPart` for the description of the
979 DEFAULT_NAME = "Grid"
981 def __init__(self, studyId, groupName, groupGeomObj, parameters,
982 name = DEFAULT_NAME):
983 StructuralElementPart2D.__init__(self, studyId, groupName,
984 groupGeomObj, parameters, name)
985 self.xr = self._getParameter(["origAxeX"])
986 self.yr = self._getParameter(["origAxeY"])
987 self.zr = self._getParameter(["origAxeZ"])
988 self.vx = self._getParameter(["axeX"])
989 self.vy = self._getParameter(["axeY"])
990 self.vz = self._getParameter(["axeZ"])
991 logger.debug(repr(self))
993 ## Create the geometrical shapes representing the grid.
994 def _buildPart(self):
996 Create the geometrical shapes representing the grid.
998 subShapes = self._getSubShapes()
1001 for subShape in subShapes:
1002 faces = self.geom.SubShapeAll(subShape,
1003 self.geom.ShapeType["FACE"])
1005 if self.geom.KindOfShape(face)[0] == \
1006 self.geom.kind.CYLINDER2D and \
1007 self.xr is not None and self.yr is not None and \
1008 self.zr is not None and self.vx is not None and \
1009 self.vy is not None and self.vz is not None:
1010 shape = self._buildGridForCylinderFace(face)
1012 shape = self._buildGridForNormalFace(face)
1013 listGridShapes.append(shape)
1015 if len(listGridShapes) == 0:
1017 elif len(listGridShapes) == 1:
1018 return listGridShapes[0]
1020 return self.geom.MakeCompound(listGridShapes)
1022 ## Create the geometrical shapes representing the grid for a given
1023 # non-cylindrical face.
1024 def _buildGridForNormalFace(self, face):
1026 Create the geometrical shapes representing the grid for a given
1027 non-cylindrical face.
1029 baseFace = self._makeFaceOffset(face, self.offset)
1030 gridList = [baseFace]
1032 # Compute display length for grid elements
1033 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1034 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1035 length = self.geom.MinDistance(p1, p2) / 2.0
1037 for u in range(1, 10):
1039 for v in range(1, 10):
1041 # get tangent plane on surface by parameters
1042 center = self.geom.MakeVertexOnSurface(baseFace,
1044 tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
1047 # use the marker to get the orientation of the frame
1048 normal = self.geom.GetNormal(tangPlane)
1049 marker = self._orientation.buildMarker(self.geom, center,
1051 [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
1052 xPoint = self.geom.MakeTranslation(center, Xx * length,
1053 Xy * length, Xz * length)
1054 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1055 gridList.append(gridLine)
1056 grid = self.geom.MakeCompound(gridList)
1059 ## Create the geometrical shapes representing the grid for a given
1061 def _buildGridForCylinderFace(self, face):
1063 Create the geometrical shapes representing the grid for a given
1066 baseFace = self._makeFaceOffset(face, self.offset)
1067 gridList = [baseFace]
1069 # Compute display length for grid elements
1070 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
1071 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
1072 length = self.geom.MinDistance(p1, p2) / 2.0
1074 # Create reference vector V
1075 origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
1076 vPoint = self.geom.MakeTranslation(origPoint,
1077 self.vx, self.vy, self.vz)
1078 refVec = self.geom.MakeVector(origPoint, vPoint)
1082 for v in range(1, 10):
1085 # Compute the local orientation of the frame
1086 center = self.geom.MakeVertexOnSurface(baseFace,
1088 locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
1090 locOrient = self.geom.GetNormal(locPlaneYZ)
1091 xPoint = self.geom.MakeTranslationVectorDistance(center,
1094 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
1095 gridList.append(gridLine)
1097 grid = self.geom.MakeCompound(gridList)
1100 ## Create the markers defining the orientation of the grid.
1101 def _buildMarkers(self):
1103 Create the markers defining the orientation of the grid.
1105 return self._buildMarkersWithOffset(self.offset)
1107 ## Alias for class GeneralBeam.
1109 def VisuPoutreGenerale(studyId, groupName, groupGeomObj, parameters,
1112 Alias for class :class:`GeneralBeam`.
1114 return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
1116 ## Alias for class CircularBeam.
1118 def VisuPoutreCercle(studyId, groupName, groupGeomObj, parameters,
1121 Alias for class :class:`CircularBeam`.
1123 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
1125 ## Alias for class RectangularBeam.
1127 def VisuPoutreRectangle(studyId, groupName, groupGeomObj, parameters,
1130 Alias for class :class:`RectangularBeam`.
1132 return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
1134 ## Alias for class GeneralBeam.
1136 def VisuBarreGenerale(studyId, groupName, groupGeomObj, parameters,
1139 Alias for class :class:`GeneralBeam`.
1141 return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name,
1144 ## Alias for class RectangularBeam.
1146 def VisuBarreRectangle(studyId, groupName, groupGeomObj, parameters,
1149 Alias for class :class:`RectangularBeam`.
1151 return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name,
1154 ## Alias for class CircularBeam.
1156 def VisuBarreCercle(studyId, groupName, groupGeomObj, parameters,
1159 Alias for class :class:`CircularBeam`.
1161 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name,
1164 ## Alias for class CircularBeam.
1166 def VisuCable(studyId, groupName, groupGeomObj, parameters, name = "CABLE"):
1168 Alias for class :class:`CircularBeam`.
1170 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name,
1173 ## Alias for class ThickShell.
1175 def VisuCoque(studyId, groupName, groupGeomObj, parameters, name = "COQUE"):
1177 Alias for class :class:`ThickShell`.
1179 return ThickShell(studyId, groupName, groupGeomObj, parameters, name)
1181 ## Alias for class Grid.
1183 def VisuGrille(studyId, groupName, groupGeomObj, parameters, name = "GRILLE"):
1185 Alias for class :class:`Grid`.
1187 return Grid(studyId, groupName, groupGeomObj, parameters, name)