0026106: BRepMesh - revision of data model
[occt.git] / src / BRepMesh / BRepMesh_ModelHealer.cxx
1 // Created on: 2016-06-23
2 // Copyright (c) 2016 OPEN CASCADE SAS
3 // Created by: Oleg AGASHIN
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <BRepMesh_ModelHealer.hxx>
17 #include <BRepMesh_Deflection.hxx>
18 #include <BRepMesh_ShapeTool.hxx>
19 #include <BRepMesh_FaceChecker.hxx>
20 #include <BRepMesh_EdgeDiscret.hxx>
21 #include <IMeshData_Face.hxx>
22 #include <IMeshData_Wire.hxx>
23 #include <IMeshData_Edge.hxx>
24 #include <IMeshData_PCurve.hxx>
25 #include <OSD_Parallel.hxx>
26 #include <TopExp.hxx>
27 #include <TopoDS_Vertex.hxx>
28
29 #ifdef DEBUG_HEALER
30 #include <BRepBuilderAPI_MakePolygon.hxx>
31 #include <BRepTools.hxx>
32 #include <BRep_Builder.hxx>
33 #include <TopoDS_Compound.hxx>
34 #endif
35
36 namespace
37 {
38   //! Decreases deflection of the given edge and tries to update discretization.
39   class EdgeAmplifier
40   {
41   public:
42     //! Constructor.
43     EdgeAmplifier(const IMeshTools_Parameters& theParameters)
44       : myParameters(theParameters)
45     {
46     }
47
48     //! Main operator.
49     void operator()(const IMeshData::IEdgePtr& theDEdge) const
50     {
51       const IMeshData::IEdgeHandle aDEdge = theDEdge;
52       aDEdge->Clear(Standard_True);
53       aDEdge->SetDeflection(Max(aDEdge->GetDeflection() / 3., Precision::Confusion()));
54
55       const IMeshData::IPCurveHandle& aPCurve = aDEdge->GetPCurve(0);
56       const IMeshData::IFaceHandle    aDFace = aPCurve->GetFace();
57       Handle(IMeshTools_CurveTessellator) aTessellator =
58         BRepMesh_EdgeDiscret::CreateEdgeTessellator(
59           aDEdge, aPCurve->GetOrientation(), aDFace, myParameters);
60
61       BRepMesh_EdgeDiscret::Tessellate3d(aDEdge, aTessellator, Standard_False);
62       BRepMesh_EdgeDiscret::Tessellate2d(aDEdge, Standard_False);
63     }
64
65   private:
66
67     EdgeAmplifier (const EdgeAmplifier& theOther);
68
69     void operator=(const EdgeAmplifier& theOther);
70
71   private:
72     const IMeshTools_Parameters& myParameters;
73   };
74
75   //! Returns True if some of two vertcies is same with reference one.
76   inline Standard_Boolean isSameWithSomeOf(
77     const TopoDS_Vertex& theRefVertex,
78     const TopoDS_Vertex& theVertex1,
79     const TopoDS_Vertex& theVertex2)
80   {
81     return (theRefVertex.IsSame(theVertex1) ||
82             theRefVertex.IsSame(theVertex2));
83   }
84
85   //! Returns True if some of two vertcies is within tolerance of reference one.
86   inline Standard_Boolean isInToleranceWithSomeOf(
87     const gp_Pnt& theRefPoint,
88     const gp_Pnt& thePoint1,
89     const gp_Pnt& thePoint2,
90     const Standard_Real theTol)
91   {
92     const Standard_Real aSqTol = theTol * theTol;
93     return (theRefPoint.SquareDistance(thePoint1) < aSqTol ||
94             theRefPoint.SquareDistance(thePoint2) < aSqTol);
95   }
96 }
97
98 //=======================================================================
99 // Function: Constructor
100 // Purpose : 
101 //=======================================================================
102 BRepMesh_ModelHealer::BRepMesh_ModelHealer()
103 {
104 }
105
106 //=======================================================================
107 // Function: Destructor
108 // Purpose : 
109 //=======================================================================
110 BRepMesh_ModelHealer::~BRepMesh_ModelHealer()
111 {
112 }
113
114 //=======================================================================
115 // Function: Perform
116 // Purpose : 
117 //=======================================================================
118 Standard_Boolean BRepMesh_ModelHealer::Perform(
119   const Handle(IMeshData_Model)& theModel,
120   const IMeshTools_Parameters&   theParameters)
121 {
122   myModel      = theModel;
123   myParameters = theParameters;
124   if (myModel.IsNull())
125   {
126     return Standard_False;
127   }
128
129   // MinSize is made as a constant. It is connected with
130   // the fact that too rude discretisation can lead to 
131   // self-intersecting polygon, which cannot be fixed.
132   // As result the face will not be triangulated at all.
133   // E.g. see "Test mesh standard_mesh C7", the face #17.
134   myParameters.MinSize = Precision::Confusion();
135
136   myFaceIntersectingEdges = new IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs;
137   for (Standard_Integer aFaceIt = 0; aFaceIt < myModel->FacesNb(); ++aFaceIt)
138   {
139     myFaceIntersectingEdges->Bind(myModel->GetFace(aFaceIt).get(), Handle(IMeshData::MapOfIEdgePtr)());
140   }
141
142   // TODO: Here we can process edges in order to remove close discrete points.
143   OSD_Parallel::For(0, myModel->FacesNb(), *this, !isParallel());
144   amplifyEdges();
145
146   IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs::Iterator aFaceIt(*myFaceIntersectingEdges);
147   for (; aFaceIt.More(); aFaceIt.Next())
148   {
149     if (!aFaceIt.Value().IsNull())
150     {
151       const IMeshData::IFaceHandle aDFace = aFaceIt.Key();
152       aDFace->SetStatus(IMeshData_SelfIntersectingWire);
153       aDFace->SetStatus(IMeshData_Failure);
154     }
155   }
156
157   myFaceIntersectingEdges.Nullify();
158   myModel.Nullify(); // Do not hold link to model.
159   return Standard_True;
160 }
161
162 //=======================================================================
163 // Function: amplifyEdges
164 // Purpose : 
165 //=======================================================================
166 void BRepMesh_ModelHealer::amplifyEdges()
167 {
168   Handle(NCollection_IncAllocator) aTmpAlloc =
169     new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE);
170
171   Standard_Integer aAmpIt = 0;
172   const Standard_Real aIterNb = 5;
173   IMeshData::MapOfIEdgePtr aEdgesToUpdate(1, aTmpAlloc);
174   while (aAmpIt++ < aIterNb && popEdgesToUpdate(aEdgesToUpdate))
175   {
176     // Try to update discretization by decreasing deflection of problematic edges.
177     OSD_Parallel::ForEach(aEdgesToUpdate.cbegin(), aEdgesToUpdate.cend(),
178                           EdgeAmplifier(myParameters),
179                           !(myParameters.InParallel && aEdgesToUpdate.Size() > 1),
180                           aEdgesToUpdate.Size());
181
182     IMeshData::MapOfIFacePtr aFacesToCheck(1, aTmpAlloc);
183     IMeshData::MapOfIEdgePtr::Iterator aEdgeIt(aEdgesToUpdate);
184     for (; aEdgeIt.More(); aEdgeIt.Next())
185     {
186       const IMeshData::IEdgeHandle aDEdge = aEdgeIt.Value();
187       for (Standard_Integer aPCurveIt = 0; aPCurveIt < aDEdge->PCurvesNb(); ++aPCurveIt)
188       {
189         aFacesToCheck.Add(aDEdge->GetPCurve(aPCurveIt)->GetFace());
190       }
191     }
192
193     OSD_Parallel::ForEach(aFacesToCheck.cbegin(), aFacesToCheck.cend(),
194                           *this, !(myParameters.InParallel && aFacesToCheck.Size() > 1),
195                           aFacesToCheck.Size());
196
197     aEdgesToUpdate.Clear();
198     aTmpAlloc->Reset(Standard_False);
199   }
200 }
201
202 //=======================================================================
203 // Function: popEdgesToUpdate
204 // Purpose : 
205 //=======================================================================
206 Standard_Boolean BRepMesh_ModelHealer::popEdgesToUpdate(
207   IMeshData::MapOfIEdgePtr& theEdgesToUpdate)
208 {
209   IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs::Iterator aFaceIt(*myFaceIntersectingEdges);
210   for (; aFaceIt.More(); aFaceIt.Next())
211   {
212     Handle(IMeshData::MapOfIEdgePtr)& aIntersections = aFaceIt.ChangeValue();
213     if (!aIntersections.IsNull())
214     {
215       theEdgesToUpdate.Unite(*aIntersections);
216       aIntersections.Nullify();
217     }
218   }
219
220   return !theEdgesToUpdate.IsEmpty();
221 }
222
223 //=======================================================================
224 // Function: process
225 // Purpose : 
226 //=======================================================================
227 void BRepMesh_ModelHealer::process(const IMeshData::IFaceHandle& theDFace) const
228 {
229   Handle(IMeshData::MapOfIEdgePtr)& aIntersections = myFaceIntersectingEdges->ChangeFind(theDFace.get());
230   aIntersections.Nullify();
231
232   fixFaceBoundaries(theDFace);
233
234   if (!theDFace->IsSet(IMeshData_Failure))
235   {
236     BRepMesh_FaceChecker aChecker(theDFace, myParameters);
237     if (!aChecker.Perform())
238     {
239 #ifdef DEBUG_HEALER
240       std::cout << "Failed : #" << aChecker.GetIntersectingEdges()->Size() << std::endl;
241 #endif
242       aIntersections = aChecker.GetIntersectingEdges();
243     }
244   }
245 }
246
247 //=======================================================================
248 // Function: fixFaceBoundaries
249 // Purpose : 
250 //=======================================================================
251 void BRepMesh_ModelHealer::fixFaceBoundaries(const IMeshData::IFaceHandle& theDFace) const
252 {
253 #ifdef DEBUG_HEALER
254   TopoDS_Compound aComp;
255   BRep_Builder aBuilder;
256   aBuilder.MakeCompound(aComp);
257 #endif
258
259   for (int aWireIt = 0; aWireIt < theDFace->WiresNb(); ++aWireIt)
260   {
261     const IMeshData::IWireHandle& aDWire = theDFace->GetWire(aWireIt);
262     BRepMesh_Deflection::ComputeDeflection(aDWire, myParameters);
263     for (int aEdgeIt = 0; aEdgeIt < aDWire->EdgesNb(); ++aEdgeIt)
264     {
265       const int aPrevEdgeIt = (aEdgeIt + aDWire->EdgesNb() - 1) % aDWire->EdgesNb();
266       const int aNextEdgeIt = (aEdgeIt + 1) % aDWire->EdgesNb();
267
268       const IMeshData::IEdgeHandle aPrevEdge = aDWire->GetEdge(aPrevEdgeIt);
269       const IMeshData::IEdgeHandle aCurrEdge = aDWire->GetEdge(aEdgeIt);
270       const IMeshData::IEdgeHandle aNextEdge = aDWire->GetEdge(aNextEdgeIt);
271
272       Standard_Boolean isConnected = !getCommonVertex(aCurrEdge, aNextEdge).IsNull() &&
273                                      !getCommonVertex(aPrevEdge, aCurrEdge).IsNull();
274
275       if (isConnected)
276       {
277         const IMeshData::IPCurveHandle& aPrevPCurve =
278           aPrevEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aPrevEdgeIt));
279
280         const IMeshData::IPCurveHandle& aCurrPCurve =
281           aCurrEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aEdgeIt));
282
283         const IMeshData::IPCurveHandle& aNextPCurve =
284           aNextEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aNextEdgeIt));
285
286         isConnected = connectClosestPoints(aPrevPCurve, aCurrPCurve, aNextPCurve);
287
288 #ifdef DEBUG_HEALER
289         BRepBuilderAPI_MakePolygon aPoly;
290         for (int i = 0; i < aCurrPCurve->ParametersNb(); ++i)
291         {
292           const gp_Pnt2d& aPnt = aCurrPCurve->GetPoint(i);
293           aPoly.Add(gp_Pnt(aPnt.X(), aPnt.Y(), 0.));
294         }
295
296         if (aPoly.IsDone())
297         {
298           aBuilder.Add(aComp, aPoly.Shape());
299         }
300         TCollection_AsciiString aName("face_discr.brep");
301         BRepTools::Write(aComp, aName.ToCString());
302 #endif
303       }
304
305       if (!isConnected || aCurrEdge->IsSet(IMeshData_Outdated))
306       {
307         // We have to clean face from triangulation.
308         theDFace->SetStatus(IMeshData_Outdated);
309
310         if (!isConnected)
311         {
312           // Just mark wire as open, but continue fixing other inconsistencies
313           // in hope that this data could be suitable to build mesh somehow.
314           aDWire->SetStatus(IMeshData_OpenWire);
315         }
316       }
317     }
318   }
319
320 #ifdef DEBUG_HEALER
321   TCollection_AsciiString aName    ("face_discr.brep");
322   TCollection_AsciiString aFaceName("face_geom.brep");
323   BRepTools::Write(aComp, aName.ToCString());
324   BRepTools::Write(theDFace->GetFace(), aFaceName.ToCString());
325 #endif
326
327   BRepMesh_Deflection::ComputeDeflection(theDFace, myParameters);
328 }
329
330 //=======================================================================
331 // Function: hasCommonVertex
332 // Purpose : 
333 //=======================================================================
334 TopoDS_Vertex BRepMesh_ModelHealer::getCommonVertex(
335   const IMeshData::IEdgeHandle& theEdge1,
336   const IMeshData::IEdgeHandle& theEdge2) const
337 {
338   TopoDS_Vertex aVertex1_1, aVertex1_2;
339   TopExp::Vertices(theEdge1->GetEdge(), aVertex1_1, aVertex1_2);
340
341   //Test bugs moddata_2 bug428.
342   //  restore [locate_data_file OCC428.brep] rr
343   //  explode rr f
344   //  explode rr_91 w
345   //  explode rr_91_2 e
346   //  nbshapes rr_91_2_2
347   //  # 0 vertices; 1 edge
348
349   //This shape is invalid and can lead to exception in this code.
350
351   if (aVertex1_1.IsNull() || aVertex1_2.IsNull())
352     return TopoDS_Vertex();
353
354   if (theEdge1->GetEdge().IsSame(theEdge2->GetEdge()))
355   {
356     return aVertex1_1.IsSame(aVertex1_2) ? aVertex1_1 : TopoDS_Vertex();
357   }
358
359   TopoDS_Vertex aVertex2_1, aVertex2_2;
360   TopExp::Vertices(theEdge2->GetEdge(), aVertex2_1, aVertex2_2);
361
362   if (aVertex2_1.IsNull() || aVertex2_2.IsNull())
363     return TopoDS_Vertex();
364
365   if (isSameWithSomeOf(aVertex1_1, aVertex2_1, aVertex2_2))
366   {
367     return aVertex1_1;
368   }
369   else if (isSameWithSomeOf(aVertex1_2, aVertex2_1, aVertex2_2))
370   {
371     return aVertex1_2;
372   }
373
374   const gp_Pnt        aPnt1_1 = BRep_Tool::Pnt(aVertex1_1);
375   const gp_Pnt        aPnt1_2 = BRep_Tool::Pnt(aVertex1_2);
376   const Standard_Real aTol1_1 = BRep_Tool::Tolerance(aVertex1_1);
377   const Standard_Real aTol1_2 = BRep_Tool::Tolerance(aVertex1_2);
378
379   const gp_Pnt        aPnt2_1 = BRep_Tool::Pnt(aVertex2_1);
380   const gp_Pnt        aPnt2_2 = BRep_Tool::Pnt(aVertex2_2);
381   const Standard_Real aTol2_1 = BRep_Tool::Tolerance(aVertex2_1);
382   const Standard_Real aTol2_2 = BRep_Tool::Tolerance(aVertex2_2);
383
384   if (isInToleranceWithSomeOf(aPnt1_1, aPnt2_1, aPnt2_2, aTol1_1 + Max(aTol2_1, aTol2_2)))
385   {
386     return aVertex1_1;
387   }
388   else if (isInToleranceWithSomeOf(aPnt1_2, aPnt2_1, aPnt2_2, aTol1_2 + Max(aTol2_1, aTol2_2)))
389   {
390     return aVertex1_2;
391   }
392
393   return TopoDS_Vertex();
394 }
395
396 //=======================================================================
397 // Function: connectClosestPoints
398 // Purpose : 
399 //=======================================================================
400 Standard_Boolean BRepMesh_ModelHealer::connectClosestPoints(
401   const IMeshData::IPCurveHandle& thePrevDEdge,
402   const IMeshData::IPCurveHandle& theCurrDEdge,
403   const IMeshData::IPCurveHandle& theNextDEdge) const
404 {
405   if (thePrevDEdge->IsInternal() ||
406       theCurrDEdge->IsInternal() ||
407       theNextDEdge->IsInternal())
408   {
409     return Standard_True;
410   }
411
412   gp_Pnt2d& aPrevFirstUV = thePrevDEdge->GetPoint(0);
413   gp_Pnt2d& aPrevLastUV  = thePrevDEdge->GetPoint(thePrevDEdge->ParametersNb() - 1);
414
415   if (thePrevDEdge == theCurrDEdge)
416   {
417     // Wire consists of a single edge.
418     aPrevFirstUV = aPrevLastUV;
419     return Standard_True;
420   }
421
422   gp_Pnt2d& aCurrFirstUV = theCurrDEdge->GetPoint(0);
423   gp_Pnt2d& aCurrLastUV  = theCurrDEdge->GetPoint(theCurrDEdge->ParametersNb() - 1);
424
425   gp_Pnt2d *aPrevUV = NULL, *aCurrPrevUV = NULL;
426   const Standard_Real aPrevSqDist = closestPoints(aPrevFirstUV, aPrevLastUV,
427                                                   aCurrFirstUV, aCurrLastUV,
428                                                   aPrevUV, aCurrPrevUV);
429
430   gp_Pnt2d *aNextUV = NULL, *aCurrNextUV = NULL;
431   if (thePrevDEdge == theNextDEdge)
432   {
433     // Wire consists of two edges. Connect both ends.
434     aNextUV     = (aPrevUV     == &aPrevFirstUV) ? &aPrevLastUV : &aPrevFirstUV;
435     aCurrNextUV = (aCurrPrevUV == &aCurrFirstUV) ? &aCurrLastUV : &aCurrFirstUV;
436
437     *aNextUV = *aCurrNextUV;
438     *aPrevUV = *aCurrPrevUV;
439     return Standard_True;
440   }
441
442   gp_Pnt2d& aNextFirstUV = theNextDEdge->GetPoint(0);
443   gp_Pnt2d& aNextLastUV  = theNextDEdge->GetPoint(theNextDEdge->ParametersNb() - 1);
444
445   const Standard_Real aNextSqDist = closestPoints(aNextFirstUV, aNextLastUV,
446                                                   aCurrFirstUV, aCurrLastUV,
447                                                   aNextUV, aCurrNextUV);
448
449 #ifdef DEBUG_HEALER
450   std::cout << "PrevSqDist = " << aPrevSqDist << std::endl;
451   std::cout << "NextSqDist = " << aNextSqDist << std::endl;
452 #endif
453
454   // Connect closest points first. This can help to identify 
455   // which ends should be connected in case of gap.
456   if (aPrevSqDist - aNextSqDist > gp::Resolution())
457   {
458     adjustSamePoints(aCurrNextUV, aNextUV, aCurrPrevUV, aPrevUV, aCurrFirstUV, aCurrLastUV, aPrevFirstUV, aPrevLastUV);
459   }
460   else
461   {
462     adjustSamePoints(aCurrPrevUV, aPrevUV, aCurrNextUV, aNextUV, aCurrFirstUV, aCurrLastUV, aNextFirstUV, aNextLastUV);
463   }
464
465   return Standard_True;
466 }