Salome HOME
22491: EDF 2249 SMESH: Integration of a small python library for quadrangle meshing
[modules/smesh.git] / src / Tools / MacMesh / MacMesh / MacObject.py
1 # Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2
3 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
10
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # Lesser General Public License for more details.
15
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19
20 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21
22
23
24 class MacObject:
25         """ 
26         This represents a python class definition which contains 
27         all necessary information about the macro object being created
28         in Salome 
29         """
30
31         def __init__( self, ObjectType, GeoParameters, MeshParameters, **args ):
32                 """
33                 Initializes the macro object to be created, saves parameters inside of it, checks for neighboring objects,
34                 determines meshing parameters if necessary and finally launches the generation process.
35                 """
36                 import Config,GenFunctions
37                 if Config.debug : print "Initializing object No. " + str(len(Config.ListObj)+1)
38
39                 if 'publish' in args :
40                         if args['publish']==0 : Config.publish = 0
41                         else : Config.publish = 1
42                 else : Config.publish = 1
43                 
44                 if 'groups' in args :
45                         self.GroupNames = args['groups']
46                         for group in args['groups'] :
47                                 if not(group in Config.Groups) and group : Config.Groups.append(group)
48                 else : self.GroupNames = [None, None, None, None]
49                 
50                 if ObjectType == 'NonOrtho':
51                         if not(len(GeoParameters)==4): print "Error: trying to construct a non-ortho object but the 4 constitutive vertices are not given!"
52                         else :
53                                 Xmin = min([GeoParameters[i][0] for i in range(4)])
54                                 Xmax = max([GeoParameters[i][0] for i in range(4)])
55                                 Ymin = min([GeoParameters[i][1] for i in range(4)])
56                                 Ymax = max([GeoParameters[i][1] for i in range(4)])                        
57                                 self.GeoPar = [(0.5*(Xmin+Xmax),0.5*(Ymin+Ymax)),(Xmax-Xmin,Ymax-Ymin)]
58                                 self.PtCoor = GenFunctions.SortPoints(GeoParameters)
59                 else:
60                         self.GeoPar = GeoParameters
61                         [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] ]
62                         self.PtCoor = [(Xmin,Ymin),(Xmax,Ymin),(Xmax,Ymax),(Xmin,Ymax)]      
63                 
64                 self.Type = ObjectType
65                 self.LowBound = [ self.GeoPar[0][0]-0.5*self.GeoPar[1][0], self.GeoPar[0][1]-0.5*self.GeoPar[1][1] ]
66                 self.UpperBound = [ self.GeoPar[0][0]+0.5*self.GeoPar[1][0], self.GeoPar[0][1]+0.5*self.GeoPar[1][1] ]
67                 self.MeshPar = MeshParameters
68                 self.GeoChildren = []
69                 self.GeoChildrenNames = []
70                 self.Mesh = []
71                 self.MeshGroups = []
72                 self.CheckInterfaces()
73                 if 'auto' in MeshParameters : self.AutoParam()
74                 if not(self.MeshPar[0]<0): self.Generate()
75                 else : 
76                         Config.ListObj.append(self)
77                         print("Aborting object creation\n ")
78
79         def Generate(self) :
80                 """
81                 This method generates the geometrical object with the corresponding mesh once all verifications (CheckInterfaces and AutoParam) 
82                 have been accomplished
83                 """
84                 import GenFunctions, Alarms, Config
85                 self = {'Box11'    : lambda : GenFunctions.Box11(self),
86                         'Box42'    : lambda : GenFunctions.Box42(self),
87                         'BoxAng32' : lambda : GenFunctions.BoxAng32(self),
88                         'CompBox'  : lambda : GenFunctions.CompBox(self),
89                         'CompBoxF' : lambda : GenFunctions.CompBoxF(self),
90                         'NonOrtho' : lambda : GenFunctions.NonOrtho(self),
91                         'QuartCyl' : lambda : GenFunctions.QuartCyl(self) }[self.Type]()
92
93                 if Config.debug : Alarms.Message(self.status)   # notification on the result of the generation algorithm
94                 
95
96         def CheckInterfaces(self):
97                 """
98                 This method searches for neighbours for the object being created and saves them inside the Config.Connections
99                 array. This array contains 4 entries per object corresponding to West, East, South, and North neighbours.
100                 Note that an object may have more than one neighbour for a given direction. 
101                 """
102                 import Alarms, Config
103                 from GenFunctions import AddIfDifferent
104                 from CompositeBox import FindCommonSide
105                 
106                 Config.Connections.append([(-1,),(-1,),(-1,),(-1,)])
107                 itemID = len(Config.ListObj)
108                 # In all cases except non ortho, PrincipleBoxes is unitary and contains the box in question
109                 # In the non-ortho case it contains all possible combinations of boxes with 3 vertices 
110                 PrincipleBoxes = self.PrincipleBoxes()
111                 for i, TestObj in enumerate(Config.ListObj): 
112                         SecondaryBoxes = TestObj.PrincipleBoxes()                               
113                         ConnX = 0
114                         ConnY = 0
115                         for Box0 in PrincipleBoxes:
116                                 for Box1 in SecondaryBoxes:
117                                         # Along X
118                                         CenterDis = abs(Box1[0][0]-Box0[0][0])
119                                         Extension = 0.5*(Box1[1][0]+Box0[1][0])
120                                         if CenterDis - Extension < -1e-7 : 
121                                                 ConnX = -1
122                                         elif CenterDis - Extension < 1e-7 :
123                                                 if not(FindCommonSide(self.DirBoundaries(2),TestObj.DirBoundaries(3))==[0,0]) and Box1[0][0] < Box0[0][0] : ConnX = 1
124                                                 elif not(FindCommonSide(self.DirBoundaries(3),TestObj.DirBoundaries(2))==[0,0]) and Box1[0][0] >= Box0[0][0]: ConnX = 2
125                                                 else : ConnX = 0
126                                                 
127                                         # Along Y
128                                         CenterDis = abs(Box1[0][1]-Box0[0][1])
129                                         Extension = 0.5*(Box1[1][1]+Box0[1][1])
130                                         if CenterDis - Extension < -1e-7 : 
131                                                 ConnY = -1
132                                         elif CenterDis - Extension < 1e-7 :
133                                                 if not(FindCommonSide(self.DirBoundaries(0),TestObj.DirBoundaries(1))==[0,0]) and Box1[0][1] < Box0[0][1] : ConnY = 1
134                                                 elif not(FindCommonSide(self.DirBoundaries(1),TestObj.DirBoundaries(0))==[0,0]) and Box1[0][1] >= Box0[0][1]: ConnY = 2
135                                                 else : ConnY = 0
136
137                                         if not (ConnX*ConnY == 0) :
138                                                 if max(ConnX,ConnY) == -1 and not('NonOrtho' in [self.Type,TestObj.Type]) : Alarms.Message(3)
139                                                 else:
140                                                         if ConnX == 1 and ConnY == -1:
141                                                                 if Config.Connections[i][1] == (-1,) : Config.Connections[i][1] = (itemID,)
142                                                                 else : Config.Connections[i][1] = AddIfDifferent(Config.Connections[i][1],itemID)
143                                                                 if Config.Connections[itemID][0] == (-1,) : Config.Connections[itemID][0] = (i,)
144                                                                 else : Config.Connections[itemID][0] = AddIfDifferent(Config.Connections[itemID][0],i)
145                                                         elif ConnX == 2 and ConnY == -1:
146                                                                 if Config.Connections[i][0] == (-1,) : Config.Connections[i][0] = (itemID,)
147                                                                 else : Config.Connections[i][0] = AddIfDifferent(Config.Connections[i][0],itemID)
148                                                                 if Config.Connections[itemID][1] == (-1,) : Config.Connections[itemID][1] = (i,)
149                                                                 else : Config.Connections[itemID][1] = AddIfDifferent(Config.Connections[itemID][1],i)
150                                                         elif ConnY == 1 and ConnX == -1:
151                                                                 if Config.Connections[i][3] == (-1,) : Config.Connections[i][3] = (itemID,)
152                                                                 else : Config.Connections[i][3] = AddIfDifferent(Config.Connections[i][3],itemID)
153                                                                 if Config.Connections[itemID][2] == (-1,) : Config.Connections[itemID][2] = (i,)
154                                                                 else : Config.Connections[itemID][2] = AddIfDifferent(Config.Connections[itemID][2],i)
155                                                         elif ConnY ==2 and ConnX == -1:
156                                                                 if Config.Connections[i][2] == (-1,) : Config.Connections[i][2] = (itemID,)
157                                                                 else : Config.Connections[i][2] = AddIfDifferent(Config.Connections[i][2],itemID)
158                                                                 if Config.Connections[itemID][3] == (-1,) : Config.Connections[itemID][3] = (i,)
159                                                                 else : Config.Connections[itemID][3] = AddIfDifferent(Config.Connections[itemID][3],i)
160
161         def AutoParam (self):
162                 """
163                 This method is called only if the 'auto' keyword is used inside the meshing algorithm. It is based on the 
164                 connection results per object and tries to find the correct parameters for obtaining a final compatible mesh
165                 between the objects already present and the one being created. If this is not possible, the method gives an error
166                 message.
167                 """
168                 import Alarms, Config, GenFunctions, CompositeBox
169                 MeshPar = [0,0,0,0]     # initialize the mesh parameter value to be used to -1
170                 [(X0,Y0),(DX,DY)] = self.GeoPar
171                 ObjectsInvolved = []
172                 for i, Conn in enumerate(Config.Connections[-1]):
173                         if not ( Conn == (-1,) ):   # Meaning that there is one or more neighbors on this direction
174                                 for ObjID in Conn : 
175                                         ToLook0 = [2,3,0,1][i]
176                                         ToLook1 = [3,2,1,0][i]
177                                         CommonSide =  CompositeBox.FindCommonSide(Config.ListObj[ObjID].DirBoundaries(ToLook1),self.DirBoundaries(ToLook0))
178                                         #print "Common Side is:", CommonSide
179                                         ToLook2 = [1,0,3,2][i]
180                                         #print "Full Side is:", CompositeBox.IntLen(Config.ListObj[ObjID].DirBoundaries(ToLook1))
181                                         #print "Full Segments on this direction are:", Config.ListObj[ObjID].DirectionalMeshParams[ToLook2]
182                                         RealSegments = round(Config.ListObj[ObjID].DirectionalMeshParams[ToLook2]*CompositeBox.IntLen(CommonSide)/CompositeBox.IntLen(Config.ListObj[ObjID].DirBoundaries(ToLook1)))
183                                         #print "RealSegments :", RealSegments
184                                         
185                                         MeshPar[i] = MeshPar[i] + RealSegments
186                                         ObjectsInvolved.append(ObjID+1)
187                 self.DirectionalMeshParams =  MeshPar
188                 self.MeshPar[0] = GenFunctions.CompatibilityTest(self)
189
190                 if self.MeshPar[0] < 0 : 
191                         Alarms.Message(4)
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 !!!")
194
195         def Boundaries (self):
196                 """
197                 This method returns the global boundaries of the MacObject. [Xmin,Xmax,Ymin,Ymax]
198                 """
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]])
203                 
204                 return [Xmin,Xmax,Ymin,Ymax]
205                 
206         def DirBoundaries (self, Direction):
207                 """
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]
211                 """
212                 PtCoor = self.PtCoor
213                 PtCoor.append(self.PtCoor[0])
214                 if type(Direction) is str :
215                         Dir = { 'South'  : lambda : 0,
216                                 'North'  : lambda : 1,
217                                 'West'   : lambda : 2,
218                                 'East'   : lambda : 3,}[Direction]()
219                 else : Dir = int(Direction)
220                          
221                 PtIndex  = [0,2,3,1][Dir]
222                 DirIndex = [0,0,1,1][Dir]
223                              
224                 return sorted([PtCoor[PtIndex][DirIndex],PtCoor[PtIndex+1][DirIndex]])
225         def DirVectors (self, Direction):
226                 """
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
230                 """
231                 if type(Direction) is str :
232                         Dir = { 'South'  : lambda : 0,
233                                 'North'  : lambda : 1,
234                                 'West'   : lambda : 2,
235                                 'East'   : lambda : 3,}[Direction]()
236                 else : Dir = int(Direction)
237                 PtCoor = self.PtCoor
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.]
241                                 
242         def GetBorder (self, Criterion):
243                 import GenFunctions, Config
244
245                 from salome.geom import geomBuilder
246                 geompy = geomBuilder.New( Config.theStudy )
247                 
248                 if type(Criterion) is str :
249                         Crit = {'South'  : lambda : 0,
250                                 'North'  : lambda : 1,
251                                 'West'   : lambda : 2,
252                                 'East'   : lambda : 3,}[Criterion]()
253                 else : Crit = int(Criterion)
254                 
255                 AcceptedObj = []
256                 if Crit < 4 :
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]()
262                                                             
263                         for i,ElemObj in enumerate(self.GeoChildren):
264                                 EdgeIDs = geompy.ExtractShapes(ElemObj,6)# List of Edge IDs belonging to ElemObj
265                                 for Edge in EdgeIDs:
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)
269                 else :
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
278                                 for Edge in EdgeIDs:
279                                         if GenFunctions.IsOnCircle(Edge,Center,Radius):
280                                                 AcceptedObj.append(Edge)
281                 return AcceptedObj
282
283         def PrincipleBoxes (self):
284                 """
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 indispensible for the Non-ortho types and shall return a number of 24 possible combinations
287                 """
288                 from itertools import combinations
289                 Boxes = []
290                 if self.Type == 'NonOrtho':
291                         for combi in combinations(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)])
297                 else :
298                         Boxes = [self.GeoPar]
299                 
300                 return Boxes     
301                         
302