1 # Copyright (C) 2014-2022 EDF R&D
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # Lesser General Public License for more details.
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
24 This represents a python class definition which contains
25 all necessary information about the macro object being created
29 def __init__( self, ObjectType, GeoParameters, MeshParameters, **args ):
31 Initializes the macro object to be created, saves parameters inside of it, checks for neighboring objects,
32 determines meshing parameters if necessary and finally launches the generation process.
34 import Config,GenFunctions
35 if Config.debug : print("Initializing object No. " + str(len(Config.ListObj)+1))
37 if 'publish' in args :
38 if args['publish']==0 : Config.publish = 0
39 else : Config.publish = 1
40 else : Config.publish = 1
43 self.GroupNames = args['groups']
44 for group in args['groups'] :
45 if not(group in Config.Groups) and group : Config.Groups.append(group)
46 else : self.GroupNames = [None, None, None, None]
48 if ObjectType == 'NonOrtho':
49 if not(len(GeoParameters)==4): print("Error: trying to construct a non-ortho object but the 4 constitutive vertices are not given!")
51 Xmin = min([GeoParameters[i][0] for i in range(4)])
52 Xmax = max([GeoParameters[i][0] for i in range(4)])
53 Ymin = min([GeoParameters[i][1] for i in range(4)])
54 Ymax = max([GeoParameters[i][1] for i in range(4)])
55 self.GeoPar = [(0.5*(Xmin+Xmax),0.5*(Ymin+Ymax)),(Xmax-Xmin,Ymax-Ymin)]
56 self.PtCoor = GenFunctions.SortPoints(GeoParameters)
58 self.GeoPar = GeoParameters
59 [Xmin,Ymin,Xmax,Ymax] = [ self.GeoPar[0][0]-0.5*self.GeoPar[1][0], self.GeoPar[0][1]-0.5*self.GeoPar[1][1] ] + [ self.GeoPar[0][0]+0.5*self.GeoPar[1][0], self.GeoPar[0][1]+0.5*self.GeoPar[1][1] ]
60 self.PtCoor = [(Xmin,Ymin),(Xmax,Ymin),(Xmax,Ymax),(Xmin,Ymax)]
62 self.Type = ObjectType
63 self.LowBound = [ self.GeoPar[0][0]-0.5*self.GeoPar[1][0], self.GeoPar[0][1]-0.5*self.GeoPar[1][1] ]
64 self.UpperBound = [ self.GeoPar[0][0]+0.5*self.GeoPar[1][0], self.GeoPar[0][1]+0.5*self.GeoPar[1][1] ]
65 self.MeshPar = MeshParameters
67 self.GeoChildrenNames = []
70 self.CheckInterfaces()
71 if 'auto' in MeshParameters : self.AutoParam()
72 if isinstance(self.MeshPar[0], list) or not(self.MeshPar[0]<0): self.Generate()
74 Config.ListObj.append(self)
75 print("Aborting object creation\n ")
79 This method generates the geometrical object with the corresponding mesh once all verifications (CheckInterfaces and AutoParam)
80 have been accomplished
82 import GenFunctions, Alarms, Config
83 self = {'Box11' : lambda : GenFunctions.Box11(self),
84 'Box42' : lambda : GenFunctions.Box42(self),
85 'BoxAng32' : lambda : GenFunctions.BoxAng32(self),
86 'CompBox' : lambda : GenFunctions.CompBox(self),
87 'CompBoxF' : lambda : GenFunctions.CompBoxF(self),
88 'NonOrtho' : lambda : GenFunctions.NonOrtho(self),
89 'QuartCyl' : lambda : GenFunctions.QuartCyl(self) }[self.Type]()
91 if Config.debug : Alarms.Message(self.status) # notification on the result of the generation algorithm
94 def CheckInterfaces(self):
96 This method searches for neighbours for the object being created and saves them inside the Config.Connections
97 array. This array contains 4 entries per object corresponding to West, East, South, and North neighbours.
98 Note that an object may have more than one neighbour for a given direction.
100 import Alarms, Config
101 from GenFunctions import AddIfDifferent
102 from CompositeBox import FindCommonSide
104 Config.Connections.append([(-1,),(-1,),(-1,),(-1,)])
105 itemID = len(Config.ListObj)
106 # In all cases except non ortho, PrincipleBoxes is unitary and contains the box in question
107 # In the non-ortho case it contains all possible combinations of boxes with 3 vertices
108 PrincipleBoxes = self.PrincipleBoxes()
109 for i, TestObj in enumerate(Config.ListObj):
110 SecondaryBoxes = TestObj.PrincipleBoxes()
113 for Box0 in PrincipleBoxes:
114 for Box1 in SecondaryBoxes:
116 CenterDis = abs(Box1[0][0]-Box0[0][0])
117 Extension = 0.5*(Box1[1][0]+Box0[1][0])
118 if CenterDis - Extension < -1e-7 :
120 elif CenterDis - Extension < 1e-7 :
121 if not(FindCommonSide(self.DirBoundaries(2),TestObj.DirBoundaries(3))==[0,0]) and Box1[0][0] < Box0[0][0] : ConnX = 1
122 elif not(FindCommonSide(self.DirBoundaries(3),TestObj.DirBoundaries(2))==[0,0]) and Box1[0][0] >= Box0[0][0]: ConnX = 2
126 CenterDis = abs(Box1[0][1]-Box0[0][1])
127 Extension = 0.5*(Box1[1][1]+Box0[1][1])
128 if CenterDis - Extension < -1e-7 :
130 elif CenterDis - Extension < 1e-7 :
131 if not(FindCommonSide(self.DirBoundaries(0),TestObj.DirBoundaries(1))==[0,0]) and Box1[0][1] < Box0[0][1] : ConnY = 1
132 elif not(FindCommonSide(self.DirBoundaries(1),TestObj.DirBoundaries(0))==[0,0]) and Box1[0][1] >= Box0[0][1]: ConnY = 2
135 if not (ConnX*ConnY == 0) :
136 if max(ConnX,ConnY) == -1 and not('NonOrtho' in [self.Type,TestObj.Type]) : Alarms.Message(3)
138 if ConnX == 1 and ConnY == -1:
139 if Config.Connections[i][1] == (-1,) : Config.Connections[i][1] = (itemID,)
140 else : Config.Connections[i][1] = AddIfDifferent(Config.Connections[i][1],itemID)
141 if Config.Connections[itemID][0] == (-1,) : Config.Connections[itemID][0] = (i,)
142 else : Config.Connections[itemID][0] = AddIfDifferent(Config.Connections[itemID][0],i)
143 elif ConnX == 2 and ConnY == -1:
144 if Config.Connections[i][0] == (-1,) : Config.Connections[i][0] = (itemID,)
145 else : Config.Connections[i][0] = AddIfDifferent(Config.Connections[i][0],itemID)
146 if Config.Connections[itemID][1] == (-1,) : Config.Connections[itemID][1] = (i,)
147 else : Config.Connections[itemID][1] = AddIfDifferent(Config.Connections[itemID][1],i)
148 elif ConnY == 1 and ConnX == -1:
149 if Config.Connections[i][3] == (-1,) : Config.Connections[i][3] = (itemID,)
150 else : Config.Connections[i][3] = AddIfDifferent(Config.Connections[i][3],itemID)
151 if Config.Connections[itemID][2] == (-1,) : Config.Connections[itemID][2] = (i,)
152 else : Config.Connections[itemID][2] = AddIfDifferent(Config.Connections[itemID][2],i)
153 elif ConnY ==2 and ConnX == -1:
154 if Config.Connections[i][2] == (-1,) : Config.Connections[i][2] = (itemID,)
155 else : Config.Connections[i][2] = AddIfDifferent(Config.Connections[i][2],itemID)
156 if Config.Connections[itemID][3] == (-1,) : Config.Connections[itemID][3] = (i,)
157 else : Config.Connections[itemID][3] = AddIfDifferent(Config.Connections[itemID][3],i)
159 def AutoParam (self):
161 This method is called only if the 'auto' keyword is used inside the meshing algorithm. It is based on the
162 connection results per object and tries to find the correct parameters for obtaining a final compatible mesh
163 between the objects already present and the one being created. If this is not possible, the method gives an error
166 import Alarms, Config, GenFunctions, CompositeBox
167 MeshPar = [0,0,0,0] # initialize the mesh parameter value to be used to -1
168 [(X0,Y0),(DX,DY)] = self.GeoPar
170 for i, Conn in enumerate(Config.Connections[-1]):
171 if not ( Conn == (-1,) ): # Meaning that there is one or more neighbors on this direction
173 ToLook0 = [2,3,0,1][i]
174 ToLook1 = [3,2,1,0][i]
175 CommonSide = CompositeBox.FindCommonSide(Config.ListObj[ObjID].DirBoundaries(ToLook1),self.DirBoundaries(ToLook0))
176 #print "Common Side is:", CommonSide
177 ToLook2 = [1,0,3,2][i]
178 #print "Full Side is:", CompositeBox.IntLen(Config.ListObj[ObjID].DirBoundaries(ToLook1))
179 #print "Full Segments on this direction are:", Config.ListObj[ObjID].DirectionalMeshParams[ToLook2]
180 RealSegments = round(Config.ListObj[ObjID].DirectionalMeshParams[ToLook2]*CompositeBox.IntLen(CommonSide)/CompositeBox.IntLen(Config.ListObj[ObjID].DirBoundaries(ToLook1)))
181 #print "RealSegments :", RealSegments
183 MeshPar[i] = MeshPar[i] + RealSegments
184 ObjectsInvolved.append(ObjID+1)
185 self.DirectionalMeshParams = MeshPar
186 self.MeshPar[0] = GenFunctions.CompatibilityTest(self)
188 if isinstance( self.MeshPar[0], list ):
190 if self.MeshPar[0] < 0 :
192 if self.MeshPar[0] == -1 : print(("Problem encountered with object(s) no. "+str(ObjectsInvolved)))
193 elif self.MeshPar[0] == -2 : print ("This object has no neighbours !!!")
195 def Boundaries (self):
197 This method returns the global boundaries of the MacObject. [Xmin,Xmax,Ymin,Ymax]
199 Xmin = min([self.DirBoundaries(i)[0] for i in [0,1]])
200 Xmax = max([self.DirBoundaries(i)[1] for i in [0,1]])
201 Ymin = min([self.DirBoundaries(i)[0] for i in [2,3]])
202 Ymax = max([self.DirBoundaries(i)[1] for i in [2,3]])
204 return [Xmin,Xmax,Ymin,Ymax]
206 def DirBoundaries (self, Direction):
208 This method returns a single interval giving [Xmin,Xmax] or [Ymin,Ymax] according to the required direction.
209 This works particularly well for nonorthogonal objects.
210 Direction : [0,1,2,3] <=> [South, North, West, East]
213 PtCoor.append(self.PtCoor[0])
214 if isinstance(Direction, str) :
215 Dir = { 'South' : lambda : 0,
216 'North' : lambda : 1,
218 'East' : lambda : 3,}[Direction]()
219 else : Dir = int(Direction)
221 PtIndex = [0,2,3,1][Dir]
222 DirIndex = [0,0,1,1][Dir]
224 return sorted([PtCoor[PtIndex][DirIndex],PtCoor[PtIndex+1][DirIndex]])
225 def DirVectors (self, Direction):
227 This method returns for a given object, the real vectors which define a given direction
228 The interest in using this method is for non-orthogonal objects where the sides can be
229 deviated from the orthogonal basis vectors
231 if isinstance(Direction, str) :
232 Dir = { 'South' : lambda : 0,
233 'North' : lambda : 1,
235 'East' : lambda : 3,}[Direction]()
236 else : Dir = int(Direction)
238 PtCoor.append(self.PtCoor[0])
239 PtIndex = [0,2,3,1][Dir]
240 return [PtCoor[PtIndex+1][0]-PtCoor[PtIndex][0],PtCoor[PtIndex+1][1]-PtCoor[PtIndex][1],0.]
242 def GetBorder (self, Criterion):
243 import GenFunctions, Config
245 from salome.geom import geomBuilder
246 geompy = geomBuilder.New()
248 if isinstance(Criterion, str) :
249 Crit = {'South' : lambda : 0,
250 'North' : lambda : 1,
252 'East' : lambda : 3,}[Criterion]()
253 else : Crit = int(Criterion)
257 Boundaries = self.Boundaries()
258 Research = {0 : lambda : [self.DirVectors(0),1,Boundaries[2]],
259 1 : lambda : [self.DirVectors(1),1,Boundaries[3]],
260 2 : lambda : [self.DirVectors(2),0,Boundaries[0]],
261 3 : lambda : [self.DirVectors(3),0,Boundaries[1]], }[Crit]()
263 for i,ElemObj in enumerate(self.GeoChildren):
264 EdgeIDs = geompy.ExtractShapes(ElemObj,6)# List of Edge IDs belonging to ElemObj
266 if GenFunctions.IsParallel(Edge,Research[0]):
267 if abs( geompy.PointCoordinates(geompy.GetVertexByIndex(Edge,0))[Research[1]] - Research[2] )< 1e-6 or abs( geompy.PointCoordinates(geompy.GetVertexByIndex(Edge,1))[Research[1]] - Research[2] )< 1e-6 :
268 AcceptedObj.append(Edge)
270 CenterSrchPar = {'NE' : lambda : [-1., -1.],
271 'NW' : lambda : [ 1., -1.],
272 'SW' : lambda : [ 1., 1.],
273 'SE' : lambda : [-1., 1.], }[self.MeshPar[1]]()
274 Radius = self.GeoPar[1][1]*float(self.MeshPar[2])/(self.MeshPar[2]+1)
275 Center = (self.GeoPar[0][0]+CenterSrchPar[0]*self.GeoPar[1][0]/2.,self.GeoPar[0][1]+CenterSrchPar[1]*self.GeoPar[1][1]/2.,0.)
276 for i,ElemObj in enumerate(self.GeoChildren):
277 EdgeIDs = geompy.ExtractShapes(ElemObj,6)# List of Edge IDs belonging to ElemObj
279 if GenFunctions.IsOnCircle(Edge,Center,Radius):
280 AcceptedObj.append(Edge)
283 def PrincipleBoxes (self):
285 This function returns all possible combination rectangular shape objects that can contain at least 3 of the principle vertices
286 constituting the MacObject. This is indispensable for the Non-ortho types and shall return a number of 24 possible combinations
288 from itertools import combinations
290 if self.Type == 'NonOrtho':
291 for combi in combinations(list(range(4)),3):
292 Xmin = min([self.PtCoor[i][0] for i in combi])
293 Xmax = max([self.PtCoor[i][0] for i in combi])
294 Ymin = min([self.PtCoor[i][1] for i in combi])
295 Ymax = max([self.PtCoor[i][1] for i in combi])
296 Boxes.append([(0.5*(Xmin+Xmax),0.5*(Ymin+Ymax)),(Xmax-Xmin,Ymax-Ymin)])
298 Boxes = [self.GeoPar]