Salome HOME
5731b8d826ca54cae93896212b7dc18bd75d8497
[modules/geom.git] / src / NMTAlgo / NMTAlgo_Loop3d.cxx
1
2 //using namespace std;
3
4 #include <NMTAlgo_Loop3d.ixx>
5
6 #include <TopExp_Explorer.hxx>
7 #include <TopExp.hxx>
8 #include <BRep_Builder.hxx>
9 #include <TopTools_MapOfShape.hxx>
10 #include <TopTools_ListIteratorOfListOfShape.hxx>
11 #include <TopoDS_Shell.hxx>
12 #include <TopoDS_Iterator.hxx>
13 #include <TopoDS.hxx>
14 #include <TopTools_MapIteratorOfMapOfShape.hxx>
15 #include <gp_Vec.hxx>
16 #include <gp_Pnt.hxx>
17 #include <Geom2d_Curve.hxx>
18 #include <BRep_Tool.hxx>
19 #include <Geom_Surface.hxx>
20 #include <gp_Pnt2d.hxx>
21 #include <gp_Vec2d.hxx>
22 #include <gp_Dir2d.hxx>
23 #include <Geom_Curve.hxx>
24
25 static 
26   TopoDS_Edge FindEinF(const TopoDS_Edge&, const TopoDS_Face&);
27 static 
28   gp_Vec NextNormal(const TopoDS_Edge&, const TopoDS_Face&);
29
30 //=======================================================================
31 //function : 
32 //purpose  : 
33 //=======================================================================
34   NMTAlgo_Loop3d::NMTAlgo_Loop3d()
35 {
36 }
37
38 //=======================================================================
39 //function : AddConstFaces
40 //purpose  : Add faces of <S> as unique faces in the result.
41 //=======================================================================
42   void NMTAlgo_Loop3d::AddConstFaces(const TopoDS_Shape& S) 
43 {
44   TopExp_Explorer FaceExp(S, TopAbs_FACE);
45   for (; FaceExp.More(); FaceExp.Next()){
46     myFaces.Append( FaceExp.Current() );
47   }
48   TopExp::MapShapesAndAncestors(S, TopAbs_EDGE, TopAbs_FACE, myEFMap);
49 }
50
51 //=======================================================================
52 //function : AddSectionFaces
53 //purpose  : Add faces of <S> as double faces in the result.
54 //=======================================================================
55   void NMTAlgo_Loop3d::AddSectionFaces(const TopoDS_Shape& S) 
56 {
57   AddConstFaces( S );
58   AddConstFaces( S.Reversed() );
59 }
60
61 //=======================================================================
62 //function : MakeShells
63 //purpose  : Make and return shells. 
64 //           <AvoidFacesMap> can contain faces that must not be
65 //           added to result shells.
66 //=======================================================================
67   const TopTools_ListOfShape& NMTAlgo_Loop3d::MakeShells 
68           (const TopTools_MapOfOrientedShape& AvoidFacesMap)
69 {
70   myNewShells.Clear();
71   
72   BRep_Builder Builder;
73   TopTools_MapOfShape CheckedEdgesMap;
74   TopTools_MapOfOrientedShape AddedFacesMap;
75   
76   TopTools_ListIteratorOfListOfShape itF (myFaces);
77   for (; itF.More(); itF.Next()) {
78     const TopoDS_Shape& FF = itF.Value();
79     if (AvoidFacesMap.Contains( FF ) ||
80         ! AddedFacesMap.Add( FF ) )
81       continue;
82
83     // make a new shell
84     TopoDS_Shell Shell;
85     Builder.MakeShell(Shell);
86     Builder.Add(Shell,FF);
87
88     // clear the maps from shapes added to previous Shell
89     TopTools_MapIteratorOfMapOfShape itEM (CheckedEdgesMap);
90     for (; itEM.More(); itEM.Next()) {
91       TopTools_ListOfShape& FL = myEFMap.ChangeFromKey( itEM.Key());
92       TopTools_ListIteratorOfListOfShape it (FL);
93       while ( it.More()) {
94         if (AddedFacesMap.Contains( it.Value()))
95           FL.Remove( it );
96         else
97           it.Next();
98       }
99     }
100     CheckedEdgesMap.Clear();
101
102     
103     // loop on faces added to Shell; add their neighbor faces to Shell and so on
104     TopoDS_Iterator itAddedF (Shell);
105     for (; itAddedF.More(); itAddedF.Next()){
106       const TopoDS_Face& F = TopoDS::Face (itAddedF.Value());
107       
108       // loop on edges of F; find a good neighbor face of F by E
109       TopExp_Explorer EdgeExp(F, TopAbs_EDGE);
110       for (; EdgeExp.More(); EdgeExp.Next()){
111         const TopoDS_Edge& E = TopoDS::Edge( EdgeExp.Current());
112         if (! CheckedEdgesMap.Add( E ))
113           continue;
114         
115         // candidate faces list
116         const TopTools_ListOfShape& FL = myEFMap.ChangeFromKey(E);
117         if (FL.IsEmpty())
118           continue;
119         // select one of neighbors
120         TopoDS_Face SelF;
121         if (FL.Extent() == 2) {
122           if (! F.IsSame( FL.First() ))
123             SelF = TopoDS::Face( FL.First() );
124           else if (!F.IsSame( FL.Last() ))
125             SelF = TopoDS::Face( FL.Last() );
126         }
127         else {
128           // check if a face already added to Shell shares E
129           TopTools_ListIteratorOfListOfShape it (FL);
130           Standard_Boolean found = Standard_False;
131           for (; !found && it.More(); it.Next())
132             if (F != it.Value())
133               found = AddedFacesMap.Contains( it.Value() );
134           if (found)
135             continue;
136           // select basing on geometrical check
137           Standard_Boolean GoodOri, inside;
138           Standard_Real dot, MaxDot = -100;
139           TopTools_ListOfShape TangFL; // tangent faces
140           for ( it.Initialize( FL ) ; it.More(); it.Next()) {
141             const TopoDS_Face& NeighborF = TopoDS::Face( it.Value());
142             if (NeighborF.IsSame( F ))
143               continue;
144             inside = NMTAlgo_Loop3d::IsInside( E, F, NeighborF, 1, dot, GoodOri);
145             if (!GoodOri)
146               continue;
147             if (!inside)
148               dot = -dot - 3;
149             if (dot < MaxDot)
150               continue;
151             if ( IsEqual( dot, MaxDot))
152               TangFL.Append(SelF);
153             else
154               TangFL.Clear();
155             MaxDot = dot;
156             SelF = NeighborF;
157           }
158           if (!TangFL.IsEmpty()) {
159             for (it.Initialize( TangFL ); it.More(); it.Next()) {
160               const TopoDS_Face& NeighborF = TopoDS::Face( it.Value());
161               if (NMTAlgo_Loop3d:: IsInside( E, SelF , NeighborF, 0, dot, GoodOri))
162                 SelF = NeighborF;
163             }
164           }
165         }
166         if (!SelF.IsNull() &&
167             AddedFacesMap.Add( SelF ) &&
168             !AvoidFacesMap.Contains( SelF )) 
169           Builder.Add( Shell, SelF);
170
171       } // loop on edges of F
172       
173     } // loop on the faces added to Shell
174
175     // Shell is complete
176     myNewShells.Append( Shell );
177
178   } // loop on myFaces
179
180
181   // prepare to the next call
182   myFaces.Clear();
183   myEFMap.Clear();
184
185   return myNewShells;
186 }
187 //=======================================================================
188 //function : Normal
189 //purpose  : 
190 //=======================================================================
191   gp_Vec NMTAlgo_Loop3d::Normal(const TopoDS_Edge& E,
192                                   const TopoDS_Face& F)
193 {
194   gp_Vec Norm, V1, V2;
195   Standard_Real First, Last;
196   gp_Pnt Ps;
197
198   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface (E, F, First, Last);
199   Handle(Geom_Surface) Sf = BRep_Tool::Surface(F);
200
201   gp_Pnt2d p = C2d->Value( 0.5*(First+Last) );
202   Sf->D1(p.X(), p.Y(), Ps, V1, V2);
203   Norm = V1.Crossed(V2);
204
205   if (F.Orientation() == TopAbs_REVERSED ) 
206     Norm.Reverse();
207
208   return Norm;
209 }
210 //=======================================================================
211 //function : IsInside
212 //purpose  : check if <F2> is inside <F1> by edge <E>.
213 //           if <CountDot>, compute <Dot>: scalar production of
214 //           normalized  vectors  pointing  inside  faces,  and
215 //           check if faces are oriented well for sewing
216 //=======================================================================
217   Standard_Boolean NMTAlgo_Loop3d::IsInside(const TopoDS_Edge& E,
218                                             const TopoDS_Face& F1,
219                                             const TopoDS_Face& F2,
220                                             const Standard_Boolean CountDot,
221                                             Standard_Real& Dot,
222                                             Standard_Boolean& GoodOri) 
223 {
224   Standard_Real f, l;
225   gp_Pnt P;
226   gp_Vec Vc1, Vc2, Vin1, Vin2, Nf1, Nf2;
227   Handle(Geom_Curve) Curve = BRep_Tool::Curve(E,f,l);
228   Curve->D1( 0.5*(f + l), P, Vc2);
229   TopoDS_Edge E1, E2 = FindEinF (E, F2);
230   if (E2.Orientation() == TopAbs_REVERSED ) Vc2.Reverse();
231
232   Nf1 = Normal(E,F1);
233   Nf2 = Normal(E,F2);
234
235   Standard_Real sin =
236     Nf1.CrossSquareMagnitude(Nf2) / Nf1.SquareMagnitude() / Nf2.SquareMagnitude();
237   Standard_Boolean tangent = sin < 0.001;
238
239   Standard_Boolean inside = 0;
240   if (tangent) {
241     E1 = FindEinF (E, F1);
242     gp_Vec NNf1 = NextNormal(E1,F1);
243     gp_Vec NNf2 = NextNormal(E2,F2);
244     Vin2 = NNf2.Crossed(Vc2);
245     inside = Vin2 * NNf1 < 0;
246   }
247   else {
248     Vin2 = Nf2.Crossed(Vc2);
249     inside = Vin2 * Nf1 < 0;
250   }
251   
252   if (!CountDot) return inside;
253
254   if (tangent)
255     Vin2 = Nf2.Crossed(Vc2);
256   else
257     E1 = FindEinF (E, F1);
258     
259   Vc1 = Vc2;
260   if (E1.Orientation() != E2.Orientation()) 
261     Vc1.Reverse();
262   Vin1 = Nf1.Crossed(Vc1);
263
264   if (tangent) {
265     Standard_Real N1N2 = Nf1 * Nf2;
266     GoodOri = (Vin2 * Vin1 < 0) ? N1N2 > 0 : N1N2 < 0;
267   }
268   else {
269     Standard_Real V1N2 = Vin1 * Nf2;
270     GoodOri = ( inside ? V1N2 <= 0 : V1N2 >= 0);
271   }
272
273   Vin1.Normalize();
274   Vin2.Normalize();
275   
276   Dot = Vin2 * Vin1;
277   
278   return inside;
279 }
280 //=======================================================================
281 //function : NextNormal
282 //purpose  : find normal to F at point a little inside F near the middle of E
283 //warning  : E must be properly oriented in F.
284 //=======================================================================
285 gp_Vec NextNormal(const TopoDS_Edge& E,
286                          const TopoDS_Face& F)
287 {
288   Standard_Real First, Last;
289
290   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface (E, F, First, Last);
291   Handle(Geom_Surface) Sf = BRep_Tool::Surface(F);
292
293   gp_Pnt2d p;
294   gp_Vec2d v;
295   C2d->D1( 0.5*(First+Last), p, v);
296   if (E.Orientation() != F.Orientation())
297     v.Reverse();
298   gp_Dir2d dir( -v.Y(), v.X() ); // dir inside F
299   
300   Standard_Real duv = 1e-6; // this is not Ok and may give incorrect result if
301   // resolutionUV of compared faces is very different. To have a good result,
302   //it is necessary to get normal to faces at points equidistant from E in 3D
303   
304   p.SetX( p.X() + dir.X()*duv );
305   p.SetY( p.Y() + dir.Y()*duv );
306   
307   gp_Pnt Ps;
308   gp_Vec Norm, V1, V2;
309   Sf->D1( p.X(), p.Y(), Ps, V1, V2);
310   Norm = V1.Crossed(V2);
311
312   if (F.Orientation() == TopAbs_REVERSED ) 
313     Norm.Reverse();
314
315   return Norm;
316 }
317 //=======================================================================
318 //function : FindEinF
319 //purpose  : find E in F
320 //=======================================================================
321 TopoDS_Edge FindEinF(const TopoDS_Edge& E,
322                       const TopoDS_Face& F)
323 {
324   TopExp_Explorer expl (F, TopAbs_EDGE);
325   for (; expl.More(); expl.Next()) 
326     if( E.IsSame( expl.Current() ))
327       return TopoDS::Edge(expl.Current());
328   TopoDS_Edge nullE;
329   return nullE;
330 }