1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2011 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.
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 This module defines the different structural element parts. It is used to
23 build the geometric shapes of the structural elements. It should not be used
24 directly in the general case. Structural elements should be created by the
25 class :class:`~salome.geom.structelem.StructuralElementManager`.
32 from salome.kernel.logger import Logger
33 from salome.kernel import termcolor
34 logger = Logger("salome.geom.structelem.parts", color = termcolor.RED)
35 from salome.geom.geomtools import getGeompy
39 # Filling for the beams
43 # Minimum dimension for the shapes to extrude
44 MIN_DIM_FOR_EXTRUDED_SHAPE = 2e-4
45 MIN_LENGTH_FOR_EXTRUSION = 1e-4
49 class InvalidParameterError(Exception):
51 This exception is raised when an invalid parameter is used to build a
52 structural element part.
55 def __init__(self, groupName, expression, minValue, value):
56 self.groupName = groupName
57 self.expression = expression
58 self.minValue = minValue
62 return "%s < %g (%s = %g in %s)" % (self.expression, self.minValue,
63 self.expression, self.value,
69 This class enables the use of sub-shapes in sets or as dictionary keys.
70 It implements __eq__ and __hash__ methods so that sub-shapes with the same
71 CORBA object `mainShape` and the same `id` are considered equal.
74 def __init__(self, mainShape, id):
75 self._mainShape = mainShape
78 def getObj(self, geom):
80 Return the sub-shape (GEOM object). `geom` is a pseudo-geompy object
81 used to find the geometrical object.
83 return geom.GetSubShape(self._mainShape, [self._id])
85 def __eq__(self, other):
86 return self._mainShape._is_equivalent(other._mainShape) and \
90 return self._mainShape._hash(2147483647) ^ self._id
93 class StructuralElementPart:
95 This class is the base class for all structural element parts. It should
96 not be instantiated directly (consider it as an "abstract" class).
98 :type studyId: integer
99 :param studyId: the ID of the study in which the part is created.
101 :type groupName: string
102 :param groupName: the name of the underlying geometrical primitive in the
105 :type groupGeomObj: GEOM object
106 :param groupGeomObj: the underlying geometrical primitive.
108 :type parameters: dictionary
109 :param parameters: parameters defining the structural element (see
110 subclasses for details).
113 :param name: name to use for the created object in the study.
117 DEFAULT_NAME = "StructElemPart"
119 def __init__(self, studyId, groupName, groupGeomObj, parameters,
120 name = DEFAULT_NAME):
121 self._parameters = parameters
122 self.groupName = groupName
123 self._groupGeomObj = groupGeomObj
124 self._orientation = None
125 self._paramUserName = {}
127 self.geom = getGeompy(studyId)
128 self.baseShapesSet = set()
129 mainShape = self.geom.GetMainShape(groupGeomObj)
130 listIDs = self.geom.GetObjectIDs(groupGeomObj)
131 if mainShape is not None and listIDs is not None:
133 self.baseShapesSet.add(SubShapeID(mainShape, id))
135 def _getParameter(self, nameList, default = None):
137 This method finds the value of a parameter in the parameters
138 dictionary. The argument is a list because some parameters can have
139 several different names.
141 if len(nameList) > 0:
142 paramName = nameList[0]
143 for name in nameList:
144 if self._parameters.has_key(name):
145 self._paramUserName[paramName] = name
146 return self._parameters[name]
149 def _getParamUserName(self, paramName):
151 This method finds the user name for a parameter.
153 if self._paramUserName.has_key(paramName):
154 return self._paramUserName[paramName]
159 reprdict = self.__dict__.copy()
160 del reprdict["_parameters"]
161 del reprdict["groupName"]
162 del reprdict["_groupGeomObj"]
163 del reprdict["_paramUserName"]
166 del reprdict["baseShapesSet"]
167 return '%s("%s", %s)' % (self.__class__.__name__, self.groupName,
170 def addOrientation(self, orientParams):
172 Add orientation information to the structural element part. See class
173 :class:`~salome.geom.structelem.orientation.Orientation1D` for the description
176 self._orientation.addParams(orientParams)
178 def _checkSize(self, value, mindim, expression):
180 This method checks that some parameters or some expressions involving
181 those parameters are greater than a minimum value.
184 raise InvalidParameterError(self.groupName, expression,
189 Build the geometric shapes and the markers corresponding to the
190 structural element part in the study `studyId`.
192 shape = self._buildPart()
193 markers = self._buildMarkers()
194 shape.SetColor(self._groupGeomObj.GetColor())
195 for marker in markers:
196 marker.SetColor(self._groupGeomObj.GetColor())
197 return (shape, markers)
199 def _buildPart(self):
201 This abstract method must be implemented in subclasses and should
202 create the geometrical shape(s) of the structural element part.
204 raise NotImplementedError("Method _buildPart not implemented in class"
205 " %s (it must be implemented in "
206 "StructuralElementPart subclasses)." %
207 self.__class__.__name__)
209 def _buildMarkers(self):
211 This abstract method must be implemented in subclasses and should
212 create the markers defining the orientation of the structural element
215 raise NotImplementedError("Method _buildMarker not implemented in "
216 "class %s (it must be implemented in "
217 "StructuralElementPart subclasses)." %
218 self.__class__.__name__)
220 def _getSubShapes(self, minDim = MIN_LENGTH_FOR_EXTRUSION):
222 Find and return the base sub-shapes in the structural element part.
225 for subShapeID in self.baseShapesSet:
226 subShape = subShapeID.getObj(self.geom)
227 length = self.geom.BasicProperties(subShape)[0]
229 logger.warning("Length too short (%s - ID %s, length = %g), "
230 "subshape will not be used in structural "
231 "element" % (self.groupName, subShapeID._id,
234 subShapes.append(subShape)
238 class Beam(StructuralElementPart):
240 This class is an "abstract" class for all 1D structural element parts. It
241 should not be instantiated directly. See class
242 :class:`StructuralElementPart` for the description of the parameters.
245 DEFAULT_NAME = "Beam"
247 def __init__(self, studyId, groupName, groupGeomObj, parameters,
248 name = DEFAULT_NAME):
249 StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
251 self._orientation = orientation.Orientation1D()
253 def _isReversed(self, path):
255 This method checks if a 1D object is "reversed", i.e. if its
256 orientation is different than the orientation of the underlying OCC
259 length = self.geom.BasicProperties(path)[0]
260 p1 = self.geom.MakeVertexOnCurve(path, 0.0)
261 p2 = self.geom.GetFirstVertex(path)
262 dist = self.geom.MinDistance(p1, p2)
263 return dist > length / 2
265 def _getVertexAndTangentOnOrientedWire(self, path, param):
267 Get a vertex and the corresponding tangent on a wire by parameter.
268 This method takes into account the "real" orientation of the wire
269 (i.e. the orientation of the underlying OCC object).
271 if self._isReversed(path):
272 vertex = self.geom.MakeVertexOnCurve(path, 1.0 - param)
273 invtangent = self.geom.MakeTangentOnCurve(path, 1.0 - param)
274 tanpoint = self.geom.MakeTranslationVectorDistance(vertex,
277 tangent = self.geom.MakeVector(vertex, tanpoint)
279 vertex = self.geom.MakeVertexOnCurve(path, param)
280 tangent = self.geom.MakeTangentOnCurve(path, param)
281 return (vertex, tangent)
283 def _makeSolidPipeFromWires(self, wire1, wire2, point1, point2, path):
285 Create a solid by the extrusion of section `wire1` to section `wire2`
288 face1 = self.geom.MakeFace(wire1, True)
289 face2 = self.geom.MakeFace(wire2, True)
290 shell = self.geom.MakePipeWithDifferentSections([wire1, wire2],
293 closedShell = self.geom.MakeShell([face1, face2, shell])
294 solid = self.geom.MakeSolid([closedShell])
297 def _buildPart(self):
299 Build the structural element part.
301 # Get all the sub-shapes in the group (normally only edges and wires)
302 paths = self._getSubShapes()
305 withCorrection = False
308 # Build the sections (rectangular or circular) at each end of the
310 (fPoint, fNormal) = self._getVertexAndTangentOnOrientedWire(path,
312 (lPoint, lNormal) = self._getVertexAndTangentOnOrientedWire(path,
314 (outerWire1, innerWire1, outerWire2, innerWire2) = \
315 self._makeSectionWires(fPoint, fNormal, lPoint, lNormal)
317 # Create the resulting solid
318 outerSolid = self._makeSolidPipeFromWires(outerWire1, outerWire2,
319 fPoint, lPoint, path)
320 if self.filling == HOLLOW:
321 innerSolid = self._makeSolidPipeFromWires(innerWire1,
324 resultSolid = self.geom.MakeCut(outerSolid, innerSolid)
325 listPipes.append(resultSolid)
327 listPipes.append(outerSolid)
329 if len(listPipes) == 0:
331 elif len(listPipes) == 1:
334 return self.geom.MakeCompound(listPipes)
336 def _buildMarkers(self):
338 Build the markers defining the orientation of the structural element
342 paths = self._getSubShapes()
345 (center, vecX) = self._getVertexAndTangentOnOrientedWire(path,
347 marker = self._orientation.buildMarker(self.geom, center, vecX)
348 listMarkers.append(marker)
352 class CircularBeam(Beam):
354 This class defines a beam with a circular section. It can be full or
355 hollow, and its radius and thickness can vary from one end of the beam to
356 the other. The valid parameters for circular beams are:
358 * "R1" or "R": radius at the first end of the beam.
359 * "R2" or "R": radius at the other end of the beam.
360 * "EP1" or "EP" (optional): thickness at the first end of the beam.
361 If not specified or equal to 0, the beam is considered full.
362 * "EP2" or "EP" (optional): thickness at the other end of the beam.
363 If not specified or equal to 0, the beam is considered full.
365 See class :class:`StructuralElementPart` for the description of the
370 def __init__(self, studyId, groupName, groupGeomObj, parameters,
371 name = Beam.DEFAULT_NAME):
372 Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
375 self.R1 = self._getParameter(["R1", "R"])
376 self.R2 = self._getParameter(["R2", "R"])
377 self.EP1 = self._getParameter(["EP1", "EP"])
378 self.EP2 = self._getParameter(["EP2", "EP"])
380 if self.EP1 is None or self.EP2 is None or \
381 self.EP1 == 0 or self.EP2 == 0:
384 self.filling = HOLLOW
386 logger.debug(repr(self))
389 self._checkSize(self.R1, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
390 self._getParamUserName("R1"))
391 self._checkSize(self.R2, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
392 self._getParamUserName("R2"))
393 if self.filling == HOLLOW:
394 self._checkSize(self.EP1, MIN_THICKNESS,
395 self._getParamUserName("EP1"))
396 self._checkSize(self.EP2, MIN_THICKNESS,
397 self._getParamUserName("EP2"))
398 self._checkSize(self.R1 - self.EP1,
399 MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
400 "%s - %s" % (self._getParamUserName("R1"),
401 self._getParamUserName("EP1")))
402 self._checkSize(self.R2 - self.EP2,
403 MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
404 "%s - %s" % (self._getParamUserName("R2"),
405 self._getParamUserName("EP2")))
407 def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
409 Create the circular sections used to build the pipe.
411 outerCircle1 = self.geom.MakeCircle(fPoint, fNormal, self.R1)
412 outerCircle2 = self.geom.MakeCircle(lPoint, lNormal, self.R2)
413 if self.filling == HOLLOW:
414 innerCircle1 = self.geom.MakeCircle(fPoint, fNormal,
416 innerCircle2 = self.geom.MakeCircle(lPoint, lNormal,
422 return (outerCircle1, innerCircle1, outerCircle2, innerCircle2)
425 class RectangularBeam(Beam):
427 This class defines a beam with a rectangular section. It can be full or
428 hollow, and its dimensions can vary from one end of the beam to the other.
429 The valid parameters for rectangular beams are:
431 * "HY1", "HY", "H1" or "H": width at the first end of the beam.
432 * "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
433 * "HY2", "HY", "H2" or "H": width at the other end of the beam.
434 * "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
435 * "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
436 direction at the first end of the beam. If not specified or equal to 0,
437 the beam is considered full.
438 * "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
439 direction at the first end of the beam. If not specified or equal to 0,
440 the beam is considered full.
441 * "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
442 direction at the other end of the beam. If not specified or equal to 0,
443 the beam is considered full.
444 * "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
445 direction at the other end of the beam. If not specified or equal to 0,
446 the beam is considered full.
448 See class :class:`StructuralElementPart` for the description of the
453 def __init__(self, studyId, groupName, groupGeomObj, parameters,
454 name = Beam.DEFAULT_NAME):
455 Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
458 self.HY1 = self._getParameter(["HY1", "HY", "H1", "H"])
459 self.HZ1 = self._getParameter(["HZ1", "HZ", "H1", "H"])
460 self.HY2 = self._getParameter(["HY2", "HY", "H2", "H"])
461 self.HZ2 = self._getParameter(["HZ2", "HZ", "H2", "H"])
462 self.EPY1 = self._getParameter(["EPY1", "EPY", "EP1", "EP"])
463 self.EPZ1 = self._getParameter(["EPZ1", "EPZ", "EP1", "EP"])
464 self.EPY2 = self._getParameter(["EPY2", "EPY", "EP2", "EP"])
465 self.EPZ2 = self._getParameter(["EPZ2", "EPZ", "EP2", "EP"])
467 if self.EPY1 is None or self.EPZ1 is None or \
468 self.EPY2 is None or self.EPZ2 is None or \
469 self.EPY1 == 0 or self.EPZ1 == 0 or \
470 self.EPY2 == 0 or self.EPZ2 == 0:
473 self.filling = HOLLOW
475 logger.debug(repr(self))
478 self._checkSize(self.HY1, MIN_DIM_FOR_EXTRUDED_SHAPE,
479 self._getParamUserName("HY1"))
480 self._checkSize(self.HZ1, MIN_DIM_FOR_EXTRUDED_SHAPE,
481 self._getParamUserName("HZ1"))
482 self._checkSize(self.HY2, MIN_DIM_FOR_EXTRUDED_SHAPE,
483 self._getParamUserName("HY2"))
484 self._checkSize(self.HZ2, MIN_DIM_FOR_EXTRUDED_SHAPE,
485 self._getParamUserName("HZ2"))
486 if self.filling == HOLLOW:
487 self._checkSize(self.EPY1, MIN_THICKNESS,
488 self._getParamUserName("EPY1"))
489 self._checkSize(self.EPZ1, MIN_THICKNESS,
490 self._getParamUserName("EPZ1"))
491 self._checkSize(self.EPY2, MIN_THICKNESS,
492 self._getParamUserName("EPY2"))
493 self._checkSize(self.EPZ2, MIN_THICKNESS,
494 self._getParamUserName("EPZ2"))
495 self._checkSize(self.HY1 - 2 * self.EPY1,
496 MIN_DIM_FOR_EXTRUDED_SHAPE,
497 "%s - 2 * %s" % (self._getParamUserName("HY1"),
498 self._getParamUserName("EPY1")))
499 self._checkSize(self.HZ1 - 2 * self.EPZ1,
500 MIN_DIM_FOR_EXTRUDED_SHAPE,
501 "%s - 2 * %s" % (self._getParamUserName("HZ1"),
502 self._getParamUserName("EPZ1")))
503 self._checkSize(self.HY2 - 2 * self.EPY2,
504 MIN_DIM_FOR_EXTRUDED_SHAPE,
505 "%s - 2 * %s" % (self._getParamUserName("HY2"),
506 self._getParamUserName("EPY2")))
507 self._checkSize(self.HZ2 - 2 * self.EPZ2,
508 MIN_DIM_FOR_EXTRUDED_SHAPE,
509 "%s - 2 * %s" % (self._getParamUserName("HZ2"),
510 self._getParamUserName("EPZ2")))
512 def _makeRectangle(self, HY, HZ, lcs):
514 Create a rectangle in the specified plane.
518 sketchStr = "Sketcher:F %g %g:" % (-halfHY, -halfHZ)
519 sketchStr += "TT %g %g:" % (halfHY, -halfHZ)
520 sketchStr += "TT %g %g:" % (halfHY, halfHZ)
521 sketchStr += "TT %g %g:WW" % (-halfHY, halfHZ)
522 logger.debug('Drawing rectangle: "%s"' % sketchStr)
523 sketch = self.geom.MakeSketcherOnPlane(sketchStr, lcs)
526 def _makeSectionRectangles(self, point, vecX, HY, HZ, EPY, EPZ):
528 Create one side of the rectangular sections used to build the pipe.
530 (vecY, vecZ) = self._orientation.getVecYZ(self.geom, point, vecX)
531 lcs = self.geom.MakeMarkerPntTwoVec(point, vecY, vecZ)
532 outerRect = self._makeRectangle(HY, HZ, lcs)
533 if self.filling == HOLLOW:
534 innerRect = self._makeRectangle(HY - 2.0 * EPY,
539 return (outerRect, innerRect)
541 def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
543 Create the rectangular sections used to build the pipe.
545 (outerRect1, innerRect1) = \
546 self._makeSectionRectangles(fPoint, fNormal, self.HY1, self.HZ1,
547 self.EPY1, self.EPZ1)
548 (outerRect2, innerRect2) = \
549 self._makeSectionRectangles(lPoint, lNormal, self.HY2, self.HZ2,
550 self.EPY2, self.EPZ2)
551 return (outerRect1, innerRect1, outerRect2, innerRect2)
554 def getParameterInDict(nameList, parametersDict, default = None):
556 This method finds the value of a parameter in the parameters
557 dictionary. The argument is a list because some parameters can have
558 several different names.
560 for name in nameList:
561 if parametersDict.has_key(name):
562 return parametersDict[name]
566 class GeneralBeam(RectangularBeam):
568 This class defines a beam with a generic section. It is represented as a
569 full rectangular beam with the following parameters:
571 * HY1 = sqrt(12 * IZ1 / A1)
572 * HZ1 = sqrt(12 * IY1 / A1)
573 * HY2 = sqrt(12 * IZ2 / A2)
574 * HZ2 = sqrt(12 * IY2 / A2)
576 See class :class:`StructuralElementPart` for the description of the other
580 def __init__(self, studyId, groupName, groupGeomObj, parameters,
581 name = Beam.DEFAULT_NAME):
582 self.IY1 = getParameterInDict(["IY1", "IY"], parameters)
583 self.IZ1 = getParameterInDict(["IZ1", "IZ"], parameters)
584 self.IY2 = getParameterInDict(["IY2", "IY"], parameters)
585 self.IZ2 = getParameterInDict(["IZ2", "IZ"], parameters)
586 self.A1 = getParameterInDict(["A1", "A"], parameters)
587 self.A2 = getParameterInDict(["A2", "A"], parameters)
588 parameters["HY1"] = math.sqrt(12 * self.IZ1 / self.A1)
589 parameters["HZ1"] = math.sqrt(12 * self.IY1 / self.A1)
590 parameters["HY2"] = math.sqrt(12 * self.IZ2 / self.A2)
591 parameters["HZ2"] = math.sqrt(12 * self.IY2 / self.A2)
592 RectangularBeam.__init__(self, studyId, groupName, groupGeomObj, parameters,
596 class StructuralElementPart2D(StructuralElementPart):
598 This class is an "abstract" class for all 2D structural element parts. It
599 should not be instantiated directly. See class
600 :class:`StructuralElementPart` for the description of the parameters.
603 DEFAULT_NAME = "StructuralElementPart2D"
605 def __init__(self, studyId, groupName, groupGeomObj, parameters,
606 name = DEFAULT_NAME):
607 StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
609 self._orientation = orientation.Orientation2D(
610 self._getParameter(["angleAlpha"]),
611 self._getParameter(["angleBeta"]),
612 self._getParameter(["Vecteur"]))
613 self.offset = self._getParameter(["Excentre"], 0.0)
615 def _makeFaceOffset(self, face, offset, epsilon = 1e-6):
617 Create a copy of a face at a given offset.
619 if abs(offset) < epsilon:
620 return self.geom.MakeCopy(face)
622 offsetObj = self.geom.MakeOffset(face, offset)
623 # We have to explode the resulting object into faces because it is
624 # created as a polyhedron and not as a single face
625 faces = self.geom.SubShapeAll(offsetObj,
626 self.geom.ShapeType["FACE"])
629 def _buildMarkersWithOffset(self, offset):
631 Build the markers for the structural element part with a given offset
637 subShapes = self._getSubShapes()
639 for subShape in subShapes:
640 faces = self.geom.SubShapeAll(subShape,
641 self.geom.ShapeType["FACE"])
643 offsetFace = self._makeFaceOffset(face, offset)
644 # get tangent plane on surface by parameters
645 center = self.geom.MakeVertexOnSurface(offsetFace,
647 tangPlane = self.geom.MakeTangentPlaneOnFace(offsetFace,
650 normal = self.geom.GetNormal(tangPlane)
651 marker = self._orientation.buildMarker(self.geom,
653 listMarkers.append(marker)
658 class ThickShell(StructuralElementPart2D):
660 This class defines a shell with a given thickness. It can be shifted from
661 the base face. The valid parameters for thick shells are:
663 * "Epais": thickness of the shell.
664 * "Excentre": offset of the shell from the base face.
665 * "angleAlpha": angle used to build the markers (see class
666 :class:`~salome.geom.structelem.orientation.Orientation2D`)
667 * "angleBeta": angle used to build the markers (see class
668 :class:`~salome.geom.structelem.orientation.Orientation2D`)
669 * "Vecteur": vector used instead of the angles to build the markers (see
670 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
672 See class :class:`StructuralElementPart` for the description of the
676 DEFAULT_NAME = "ThickShell"
678 def __init__(self, studyId, groupName, groupGeomObj, parameters,
679 name = DEFAULT_NAME):
680 StructuralElementPart2D.__init__(self, studyId, groupName,
681 groupGeomObj, parameters, name)
682 self.thickness = self._getParameter(["Epais"])
683 logger.debug(repr(self))
685 def _buildPart(self):
687 Create the geometrical shapes corresponding to the thick shell.
689 subShapes = self._getSubShapes()
692 for subShape in subShapes:
693 faces = self.geom.SubShapeAll(subShape,
694 self.geom.ShapeType["FACE"])
696 shape = self._buildThickShellForFace(face)
697 listSolids.append(shape)
699 if len(listSolids) == 0:
701 elif len(listSolids) == 1:
704 return self.geom.MakeCompound(listSolids)
706 def _buildThickShellForFace(self, face):
708 Create the geometrical shapes corresponding to the thick shell for a
712 if self.thickness < 2 * epsilon:
713 return self._makeFaceOffset(face, self.offset, epsilon)
715 upperOffset = self.offset + self.thickness / 2.0
716 lowerOffset = self.offset - self.thickness / 2.0
720 upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
721 lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
722 listShapes = [upperFace, lowerFace]
723 upperWires = self.geom.SubShapeAll(upperFace,
724 self.geom.ShapeType["WIRE"])
725 lowerWires = self.geom.SubShapeAll(lowerFace,
726 self.geom.ShapeType["WIRE"])
727 if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
728 # if the face is a cylinder, we remove the extra side edge
729 upperWires = self._removeCylinderExtraEdge(upperWires)
730 lowerWires = self._removeCylinderExtraEdge(lowerWires)
731 for i in range(len(upperWires)):
732 resShape = self.geom.MakeThruSections([upperWires[i],
736 listShapes.append(resShape)
737 resultShell = self.geom.MakeShell(listShapes)
738 resultSolid = self.geom.MakeSolid([resultShell])
741 def _removeCylinderExtraEdge(self, wires):
743 Remove the side edge in a cylinder.
747 edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
749 if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
753 def _buildMarkers(self):
755 Build the markers defining the orientation of the thick shell.
757 return self._buildMarkersWithOffset(self.offset +
758 self.thickness / 2.0)
761 class Grid(StructuralElementPart2D):
763 This class defines a grid. A grid is represented by a 2D face patterned
764 with small lines in the main direction of the grid frame. The valid
765 parameters for grids are:
767 * "Excentre": offset of the grid from the base face.
768 * "angleAlpha": angle used to build the markers (see class
769 :class:`~salome.geom.structelem.orientation.Orientation2D`)
770 * "angleBeta": angle used to build the markers (see class
771 :class:`~salome.geom.structelem.orientation.Orientation2D`)
772 * "Vecteur": vector used instead of the angles to build the markers (see
773 class :class:`~salome.geom.structelem.orientation.Orientation2D`)
774 * "origAxeX": X coordinate of the origin of the axis used to determine the
775 orientation of the frame in the case of a cylindrical grid.
776 * "origAxeY": Y coordinate of the origin of the axis used to determine the
777 orientation of the frame in the case of a cylindrical grid.
778 * "origAxeZ": Z coordinate of the origin of the axis used to determine the
779 orientation of the frame in the case of a cylindrical grid.
780 * "axeX": X coordinate of the axis used to determine the orientation of
781 the frame in the case of a cylindrical grid.
782 * "axeY": Y coordinate of the axis used to determine the orientation of
783 the frame in the case of a cylindrical grid.
784 * "axeZ": Z coordinate of the axis used to determine the orientation of
785 the frame in the case of a cylindrical grid.
787 See class :class:`StructuralElementPart` for the description of the
791 DEFAULT_NAME = "Grid"
793 def __init__(self, studyId, groupName, groupGeomObj, parameters,
794 name = DEFAULT_NAME):
795 StructuralElementPart2D.__init__(self, studyId, groupName,
796 groupGeomObj, parameters, name)
797 self.xr = self._getParameter(["origAxeX"])
798 self.yr = self._getParameter(["origAxeY"])
799 self.zr = self._getParameter(["origAxeZ"])
800 self.vx = self._getParameter(["axeX"])
801 self.vy = self._getParameter(["axeY"])
802 self.vz = self._getParameter(["axeZ"])
803 logger.debug(repr(self))
805 def _buildPart(self):
807 Create the geometrical shapes representing the grid.
809 subShapes = self._getSubShapes()
812 for subShape in subShapes:
813 faces = self.geom.SubShapeAll(subShape,
814 self.geom.ShapeType["FACE"])
816 if self.geom.KindOfShape(face)[0] == \
817 self.geom.kind.CYLINDER2D and \
818 self.xr is not None and self.yr is not None and \
819 self.zr is not None and self.vx is not None and \
820 self.vy is not None and self.vz is not None:
821 shape = self._buildGridForCylinderFace(face)
823 shape = self._buildGridForNormalFace(face)
824 listGridShapes.append(shape)
826 if len(listGridShapes) == 0:
828 elif len(listGridShapes) == 1:
829 return listGridShapes[0]
831 return self.geom.MakeCompound(listGridShapes)
833 def _buildGridForNormalFace(self, face):
835 Create the geometrical shapes representing the grid for a given
836 non-cylindrical face.
838 baseFace = self._makeFaceOffset(face, self.offset)
839 gridList = [baseFace]
841 # Compute display length for grid elements
842 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
843 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
844 length = self.geom.MinDistance(p1, p2) / 2.0
846 for u in range(1, 10):
848 for v in range(1, 10):
850 # get tangent plane on surface by parameters
851 center = self.geom.MakeVertexOnSurface(baseFace,
853 tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
856 # use the marker to get the orientation of the frame
857 normal = self.geom.GetNormal(tangPlane)
858 marker = self._orientation.buildMarker(self.geom, center,
860 [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
861 xPoint = self.geom.MakeTranslation(center, Xx * length,
862 Xy * length, Xz * length)
863 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
864 gridList.append(gridLine)
865 grid = self.geom.MakeCompound(gridList)
868 def _buildGridForCylinderFace(self, face):
870 Create the geometrical shapes representing the grid for a given
873 baseFace = self._makeFaceOffset(face, self.offset)
874 gridList = [baseFace]
876 # Compute display length for grid elements
877 p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
878 p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
879 length = self.geom.MinDistance(p1, p2) / 2.0
881 # Create reference vector V
882 origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
883 vPoint = self.geom.MakeTranslation(origPoint,
884 self.vx, self.vy, self.vz)
885 refVec = self.geom.MakeVector(origPoint, vPoint)
889 for v in range(1, 10):
892 # Compute the local orientation of the frame
893 center = self.geom.MakeVertexOnSurface(baseFace,
895 locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
897 locOrient = self.geom.GetNormal(locPlaneYZ)
898 xPoint = self.geom.MakeTranslationVectorDistance(center,
901 gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
902 gridList.append(gridLine)
904 grid = self.geom.MakeCompound(gridList)
907 def _buildMarkers(self):
909 Create the markers defining the orientation of the grid.
911 return self._buildMarkersWithOffset(self.offset)
914 def VisuPoutreGenerale(studyId, groupName, groupGeomObj, parameters,
917 Alias for class :class:`GeneralBeam`.
919 return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
921 def VisuPoutreCercle(studyId, groupName, groupGeomObj, parameters,
924 Alias for class :class:`CircularBeam`.
926 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
928 def VisuPoutreRectangle(studyId, groupName, groupGeomObj, parameters,
931 Alias for class :class:`RectangularBeam`.
933 return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
935 def VisuBarreGenerale(studyId, groupName, groupGeomObj, parameters,
938 Alias for class :class:`GeneralBeam`.
940 return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
942 def VisuBarreRectangle(studyId, groupName, groupGeomObj, parameters,
945 Alias for class :class:`RectangularBeam`.
947 return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
949 def VisuBarreCercle(studyId, groupName, groupGeomObj, parameters,
952 Alias for class :class:`CircularBeam`.
954 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
956 def VisuCable(studyId, groupName, groupGeomObj, parameters, name = "CABLE"):
958 Alias for class :class:`CircularBeam`.
960 return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
962 def VisuCoque(studyId, groupName, groupGeomObj, parameters, name = "COQUE"):
964 Alias for class :class:`ThickShell`.
966 return ThickShell(studyId, groupName, groupGeomObj, parameters, name)
968 def VisuGrille(studyId, groupName, groupGeomObj, parameters, name = "GRILLE"):
970 Alias for class :class:`Grid`.
972 return Grid(studyId, groupName, groupGeomObj, parameters, name)