Salome HOME
Merge branch 'master' into V9_dev
[modules/smesh.git] / src / Tools / MacMesh / MacMesh / MacObject.py
1 # Copyright (C) 2014-2016  EDF R&D
2 #
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.
7 #
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.
12 #
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
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20
21
22 class MacObject:
23     """
24     This represents a python class definition which contains
25     all necessary information about the macro object being created
26     in Salome
27     """
28
29     def __init__( self, ObjectType, GeoParameters, MeshParameters, **args ):
30         """
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.
33         """
34         import Config,GenFunctions
35         if Config.debug : print("Initializing object No. " + str(len(Config.ListObj)+1))
36
37         if 'publish' in args :
38             if args['publish']==0 : Config.publish = 0
39             else : Config.publish = 1
40         else : Config.publish = 1
41
42         if 'groups' in args :
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]
47
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!")
50             else :
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)
57         else:
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)]
61
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
66         self.GeoChildren = []
67         self.GeoChildrenNames = []
68         self.Mesh = []
69         self.MeshGroups = []
70         self.CheckInterfaces()
71         if 'auto' in MeshParameters : self.AutoParam()
72         if not(self.MeshPar[0]<0): self.Generate()
73         else :
74             Config.ListObj.append(self)
75             print("Aborting object creation\n ")
76
77     def Generate(self) :
78         """
79         This method generates the geometrical object with the corresponding mesh once all verifications (CheckInterfaces and AutoParam)
80         have been accomplished
81         """
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]()
90
91         if Config.debug : Alarms.Message(self.status)   # notification on the result of the generation algorithm
92
93
94     def CheckInterfaces(self):
95         """
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.
99         """
100         import Alarms, Config
101         from GenFunctions import AddIfDifferent
102         from CompositeBox import FindCommonSide
103
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()
111             ConnX = 0
112             ConnY = 0
113             for Box0 in PrincipleBoxes:
114                 for Box1 in SecondaryBoxes:
115                     # Along X
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 :
119                         ConnX = -1
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
123                         else : ConnX = 0
124
125                     # Along Y
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 :
129                         ConnY = -1
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
133                         else : ConnY = 0
134
135                     if not (ConnX*ConnY == 0) :
136                         if max(ConnX,ConnY) == -1 and not('NonOrtho' in [self.Type,TestObj.Type]) : Alarms.Message(3)
137                         else:
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)
158
159     def AutoParam (self):
160         """
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
164         message.
165         """
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
169         ObjectsInvolved = []
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
172                 for ObjID in Conn :
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
182
183                     MeshPar[i] = MeshPar[i] + RealSegments
184                     ObjectsInvolved.append(ObjID+1)
185         self.DirectionalMeshParams =  MeshPar
186         self.MeshPar[0] = GenFunctions.CompatibilityTest(self)
187
188         if self.MeshPar[0] < 0 :
189             Alarms.Message(4)
190             if self.MeshPar[0] == -1 : print(("Problem encountered with object(s) no. "+str(ObjectsInvolved)))
191             elif self.MeshPar[0] == -2 : print ("This object has no neighbours !!!")
192
193     def Boundaries (self):
194         """
195         This method returns the global boundaries of the MacObject. [Xmin,Xmax,Ymin,Ymax]
196         """
197         Xmin = min([self.DirBoundaries(i)[0] for i in [0,1]])
198         Xmax = max([self.DirBoundaries(i)[1] for i in [0,1]])
199         Ymin = min([self.DirBoundaries(i)[0] for i in [2,3]])
200         Ymax = max([self.DirBoundaries(i)[1] for i in [2,3]])
201
202         return [Xmin,Xmax,Ymin,Ymax]
203
204     def DirBoundaries (self, Direction):
205         """
206         This method returns a single interval giving [Xmin,Xmax] or [Ymin,Ymax] according to the required direction.
207         This works particularly well for nonorthogonal objects.
208         Direction : [0,1,2,3] <=> [South, North, West, East]
209         """
210         PtCoor = self.PtCoor
211         PtCoor.append(self.PtCoor[0])
212         if isinstance(Direction, str) :
213             Dir = { 'South'  : lambda : 0,
214                     'North'  : lambda : 1,
215                     'West'   : lambda : 2,
216                     'East'   : lambda : 3,}[Direction]()
217         else : Dir = int(Direction)
218
219         PtIndex  = [0,2,3,1][Dir]
220         DirIndex = [0,0,1,1][Dir]
221
222         return sorted([PtCoor[PtIndex][DirIndex],PtCoor[PtIndex+1][DirIndex]])
223     def DirVectors (self, Direction):
224         """
225         This method returns for a given object, the real vectors which define a given direction
226         The interest in using this method is for non-orthogonal objects where the sides can be
227         deviated from the orthogonal basis vectors
228         """
229         if isinstance(Direction, str) :
230             Dir = { 'South'  : lambda : 0,
231                     'North'  : lambda : 1,
232                     'West'   : lambda : 2,
233                     'East'   : lambda : 3,}[Direction]()
234         else : Dir = int(Direction)
235         PtCoor = self.PtCoor
236         PtCoor.append(self.PtCoor[0])
237         PtIndex  = [0,2,3,1][Dir]
238         return [PtCoor[PtIndex+1][0]-PtCoor[PtIndex][0],PtCoor[PtIndex+1][1]-PtCoor[PtIndex][1],0.]
239
240     def GetBorder (self, Criterion):
241         import GenFunctions, Config
242
243         from salome.geom import geomBuilder
244         geompy = geomBuilder.New( Config.theStudy )
245
246         if isinstance(Criterion, str) :
247             Crit = {'South'  : lambda : 0,
248                     'North'  : lambda : 1,
249                     'West'   : lambda : 2,
250                     'East'   : lambda : 3,}[Criterion]()
251         else : Crit = int(Criterion)
252
253         AcceptedObj = []
254         if Crit < 4 :
255             Boundaries = self.Boundaries()
256             Research = {0 : lambda : [self.DirVectors(0),1,Boundaries[2]],
257                         1 : lambda : [self.DirVectors(1),1,Boundaries[3]],
258                         2 : lambda : [self.DirVectors(2),0,Boundaries[0]],
259                         3 : lambda : [self.DirVectors(3),0,Boundaries[1]], }[Crit]()
260
261             for i,ElemObj in enumerate(self.GeoChildren):
262                 EdgeIDs = geompy.ExtractShapes(ElemObj,6)# List of Edge IDs belonging to ElemObj
263                 for Edge in EdgeIDs:
264                     if GenFunctions.IsParallel(Edge,Research[0]):
265                         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 :
266                             AcceptedObj.append(Edge)
267         else :
268             CenterSrchPar = {'NE' : lambda : [-1., -1.],
269                              'NW' : lambda : [ 1., -1.],
270                              'SW' : lambda : [ 1.,  1.],
271                              'SE' : lambda : [-1.,  1.], }[self.MeshPar[1]]()
272             Radius = self.GeoPar[1][1]*float(self.MeshPar[2])/(self.MeshPar[2]+1)
273             Center = (self.GeoPar[0][0]+CenterSrchPar[0]*self.GeoPar[1][0]/2.,self.GeoPar[0][1]+CenterSrchPar[1]*self.GeoPar[1][1]/2.,0.)
274             for i,ElemObj in enumerate(self.GeoChildren):
275                 EdgeIDs = geompy.ExtractShapes(ElemObj,6)# List of Edge IDs belonging to ElemObj
276                 for Edge in EdgeIDs:
277                     if GenFunctions.IsOnCircle(Edge,Center,Radius):
278                         AcceptedObj.append(Edge)
279         return AcceptedObj
280
281     def PrincipleBoxes (self):
282         """
283         This function returns all possible combination rectangular shape objects that can contain at least 3 of the principle vertices
284         constituting the MacObject. This is indispensable for the Non-ortho types and shall return a number of 24 possible combinations
285         """
286         from itertools import combinations
287         Boxes = []
288         if self.Type == 'NonOrtho':
289             for combi in combinations(list(range(4)),3):
290                 Xmin = min([self.PtCoor[i][0] for i in combi])
291                 Xmax = max([self.PtCoor[i][0] for i in combi])
292                 Ymin = min([self.PtCoor[i][1] for i in combi])
293                 Ymax = max([self.PtCoor[i][1] for i in combi])
294                 Boxes.append([(0.5*(Xmin+Xmax),0.5*(Ymin+Ymax)),(Xmax-Xmin,Ymax-Ymin)])
295         else :
296             Boxes = [self.GeoPar]
297
298         return Boxes