8e1421cdb53cd4e73b383147b593caa1fdff84a3
[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::performInternal(
119   const Handle(IMeshData_Model)& theModel,
120   const IMeshTools_Parameters&   theParameters,
121   const Message_ProgressRange&   theRange)
122 {
123   (void )theRange;
124   myModel      = theModel;
125   myParameters = theParameters;
126   if (myModel.IsNull())
127   {
128     return Standard_False;
129   }
130
131   // MinSize is made as a constant. It is connected with
132   // the fact that too rude discretisation can lead to 
133   // self-intersecting polygon, which cannot be fixed.
134   // As result the face will not be triangulated at all.
135   // E.g. see "Test mesh standard_mesh C7", the face #17.
136   myParameters.MinSize = Precision::Confusion();
137
138   myFaceIntersectingEdges = new IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs;
139   for (Standard_Integer aFaceIt = 0; aFaceIt < myModel->FacesNb(); ++aFaceIt)
140   {
141     myFaceIntersectingEdges->Bind(myModel->GetFace(aFaceIt).get(), Handle(IMeshData::MapOfIEdgePtr)());
142   }
143
144   // TODO: Here we can process edges in order to remove close discrete points.
145   OSD_Parallel::For(0, myModel->FacesNb(), *this, !isParallel());
146   amplifyEdges();
147
148   IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs::Iterator aFaceIt(*myFaceIntersectingEdges);
149   for (; aFaceIt.More(); aFaceIt.Next())
150   {
151     if (!aFaceIt.Value().IsNull())
152     {
153       const IMeshData::IFaceHandle aDFace = aFaceIt.Key();
154       aDFace->SetStatus(IMeshData_SelfIntersectingWire);
155       aDFace->SetStatus(IMeshData_Failure);
156     }
157   }
158
159   myFaceIntersectingEdges.Nullify();
160   myModel.Nullify(); // Do not hold link to model.
161   return Standard_True;
162 }
163
164 //=======================================================================
165 // Function: amplifyEdges
166 // Purpose : 
167 //=======================================================================
168 void BRepMesh_ModelHealer::amplifyEdges()
169 {
170   Handle(NCollection_IncAllocator) aTmpAlloc =
171     new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE);
172
173   Standard_Integer aAmpIt = 0;
174   const Standard_Real aIterNb = 5;
175   IMeshData::MapOfIEdgePtr aEdgesToUpdate(1, aTmpAlloc);
176   while (aAmpIt++ < aIterNb && popEdgesToUpdate(aEdgesToUpdate))
177   {
178     // Try to update discretization by decreasing deflection of problematic edges.
179     OSD_Parallel::ForEach(aEdgesToUpdate.cbegin(), aEdgesToUpdate.cend(),
180                           EdgeAmplifier(myParameters),
181                           !(myParameters.InParallel && aEdgesToUpdate.Size() > 1),
182                           aEdgesToUpdate.Size());
183
184     IMeshData::MapOfIFacePtr aFacesToCheck(1, aTmpAlloc);
185     IMeshData::MapOfIEdgePtr::Iterator aEdgeIt(aEdgesToUpdate);
186     for (; aEdgeIt.More(); aEdgeIt.Next())
187     {
188       const IMeshData::IEdgeHandle aDEdge = aEdgeIt.Value();
189       for (Standard_Integer aPCurveIt = 0; aPCurveIt < aDEdge->PCurvesNb(); ++aPCurveIt)
190       {
191         aFacesToCheck.Add(aDEdge->GetPCurve(aPCurveIt)->GetFace());
192       }
193     }
194
195     OSD_Parallel::ForEach(aFacesToCheck.cbegin(), aFacesToCheck.cend(),
196                           *this, !(myParameters.InParallel && aFacesToCheck.Size() > 1),
197                           aFacesToCheck.Size());
198
199     aEdgesToUpdate.Clear();
200     aTmpAlloc->Reset(Standard_False);
201   }
202 }
203
204 //=======================================================================
205 // Function: popEdgesToUpdate
206 // Purpose : 
207 //=======================================================================
208 Standard_Boolean BRepMesh_ModelHealer::popEdgesToUpdate(
209   IMeshData::MapOfIEdgePtr& theEdgesToUpdate)
210 {
211   IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs::Iterator aFaceIt(*myFaceIntersectingEdges);
212   for (; aFaceIt.More(); aFaceIt.Next())
213   {
214     Handle(IMeshData::MapOfIEdgePtr)& aIntersections = aFaceIt.ChangeValue();
215     if (!aIntersections.IsNull())
216     {
217       theEdgesToUpdate.Unite(*aIntersections);
218       aIntersections.Nullify();
219     }
220   }
221
222   return !theEdgesToUpdate.IsEmpty();
223 }
224
225 //=======================================================================
226 // Function: process
227 // Purpose : 
228 //=======================================================================
229 void BRepMesh_ModelHealer::process(const IMeshData::IFaceHandle& theDFace) const
230 {
231   try
232   {
233     OCC_CATCH_SIGNALS
234
235     Handle(IMeshData::MapOfIEdgePtr)& aIntersections = myFaceIntersectingEdges->ChangeFind(theDFace.get());
236     aIntersections.Nullify();
237   
238     fixFaceBoundaries(theDFace);
239   
240     if (!theDFace->IsSet(IMeshData_Failure))
241     {
242       BRepMesh_FaceChecker aChecker(theDFace, myParameters);
243       if (!aChecker.Perform())
244       {
245 #ifdef DEBUG_HEALER
246         std::cout << "Failed : #" << aChecker.GetIntersectingEdges()->Size() << std::endl;
247 #endif
248         aIntersections = aChecker.GetIntersectingEdges();
249       }
250       else
251       {
252         if (theDFace->WiresNb () == 1)
253         {
254           const IMeshData::IWireHandle& aDWire = theDFace->GetWire (0);
255
256           if (aDWire->EdgesNb () == 2)
257           {
258             const IMeshData::IEdgePtr& aDEdge0 = aDWire->GetEdge (0);
259             const IMeshData::IEdgePtr& aDEdge1 = aDWire->GetEdge (1);
260
261             const IMeshData::IPCurveHandle& aPCurve0 = aDEdge0->GetPCurve (theDFace.get (), aDWire->GetEdgeOrientation (0));
262             const IMeshData::IPCurveHandle& aPCurve1 = aDEdge1->GetPCurve (theDFace.get (), aDWire->GetEdgeOrientation (1));
263
264             if (aPCurve0->ParametersNb () == 2 && aPCurve1->ParametersNb () == 2)
265             {
266               aIntersections = new IMeshData::MapOfIEdgePtr;
267               // a kind of degenerated face - 1 wire, 2 edges and both edges are very small
268               aIntersections->Add (aDEdge0);
269               aIntersections->Add (aDEdge1);
270             }
271           }
272         }
273       }
274     }
275   }
276   catch (Standard_Failure const&)
277   {
278     theDFace->SetStatus (IMeshData_Failure);
279   }
280 }
281
282 //=======================================================================
283 // Function: fixFaceBoundaries
284 // Purpose : 
285 //=======================================================================
286 void BRepMesh_ModelHealer::fixFaceBoundaries(const IMeshData::IFaceHandle& theDFace) const
287 {
288 #ifdef DEBUG_HEALER
289   TopoDS_Compound aComp;
290   BRep_Builder aBuilder;
291   aBuilder.MakeCompound(aComp);
292 #endif
293
294   for (int aWireIt = 0; aWireIt < theDFace->WiresNb(); ++aWireIt)
295   {
296     const IMeshData::IWireHandle& aDWire = theDFace->GetWire(aWireIt);
297     BRepMesh_Deflection::ComputeDeflection(aDWire, myParameters);
298     for (int aEdgeIt = 0; aEdgeIt < aDWire->EdgesNb(); ++aEdgeIt)
299     {
300       const int aPrevEdgeIt = (aEdgeIt + aDWire->EdgesNb() - 1) % aDWire->EdgesNb();
301       const int aNextEdgeIt = (aEdgeIt + 1) % aDWire->EdgesNb();
302
303       const IMeshData::IEdgeHandle aPrevEdge = aDWire->GetEdge(aPrevEdgeIt);
304       const IMeshData::IEdgeHandle aCurrEdge = aDWire->GetEdge(aEdgeIt);
305       const IMeshData::IEdgeHandle aNextEdge = aDWire->GetEdge(aNextEdgeIt);
306
307       Standard_Boolean isConnected = !getCommonVertex(aCurrEdge, aNextEdge).IsNull() &&
308                                      !getCommonVertex(aPrevEdge, aCurrEdge).IsNull();
309
310       if (isConnected)
311       {
312         const IMeshData::IPCurveHandle& aPrevPCurve =
313           aPrevEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aPrevEdgeIt));
314
315         const IMeshData::IPCurveHandle& aCurrPCurve =
316           aCurrEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aEdgeIt));
317
318         const IMeshData::IPCurveHandle& aNextPCurve =
319           aNextEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aNextEdgeIt));
320
321         isConnected = connectClosestPoints(aPrevPCurve, aCurrPCurve, aNextPCurve);
322
323 #ifdef DEBUG_HEALER
324         BRepBuilderAPI_MakePolygon aPoly;
325         for (int i = 0; i < aCurrPCurve->ParametersNb(); ++i)
326         {
327           const gp_Pnt2d& aPnt = aCurrPCurve->GetPoint(i);
328           aPoly.Add(gp_Pnt(aPnt.X(), aPnt.Y(), 0.));
329         }
330
331         if (aPoly.IsDone())
332         {
333           aBuilder.Add(aComp, aPoly.Shape());
334         }
335         TCollection_AsciiString aName("face_discr.brep");
336         BRepTools::Write(aComp, aName.ToCString());
337 #endif
338       }
339
340       if (!isConnected || aCurrEdge->IsSet(IMeshData_Outdated))
341       {
342         // We have to clean face from triangulation.
343         theDFace->SetStatus(IMeshData_Outdated);
344
345         if (!isConnected)
346         {
347           // Just mark wire as open, but continue fixing other inconsistencies
348           // in hope that this data could be suitable to build mesh somehow.
349           aDWire->SetStatus(IMeshData_OpenWire);
350         }
351       }
352     }
353   }
354
355 #ifdef DEBUG_HEALER
356   TCollection_AsciiString aName    ("face_discr.brep");
357   TCollection_AsciiString aFaceName("face_geom.brep");
358   BRepTools::Write(aComp, aName.ToCString());
359   BRepTools::Write(theDFace->GetFace(), aFaceName.ToCString());
360 #endif
361
362   BRepMesh_Deflection::ComputeDeflection(theDFace, myParameters);
363 }
364
365 //=======================================================================
366 // Function: hasCommonVertex
367 // Purpose : 
368 //=======================================================================
369 TopoDS_Vertex BRepMesh_ModelHealer::getCommonVertex(
370   const IMeshData::IEdgeHandle& theEdge1,
371   const IMeshData::IEdgeHandle& theEdge2) const
372 {
373   TopoDS_Vertex aVertex1_1, aVertex1_2;
374   TopExp::Vertices(theEdge1->GetEdge(), aVertex1_1, aVertex1_2);
375
376   //Test bugs moddata_2 bug428.
377   //  restore [locate_data_file OCC428.brep] rr
378   //  explode rr f
379   //  explode rr_91 w
380   //  explode rr_91_2 e
381   //  nbshapes rr_91_2_2
382   //  # 0 vertices; 1 edge
383
384   //This shape is invalid and can lead to exception in this code.
385
386   if (aVertex1_1.IsNull() || aVertex1_2.IsNull())
387     return TopoDS_Vertex();
388
389   if (theEdge1->GetEdge().IsSame(theEdge2->GetEdge()))
390   {
391     return aVertex1_1.IsSame(aVertex1_2) ? aVertex1_1 : TopoDS_Vertex();
392   }
393
394   TopoDS_Vertex aVertex2_1, aVertex2_2;
395   TopExp::Vertices(theEdge2->GetEdge(), aVertex2_1, aVertex2_2);
396
397   if (aVertex2_1.IsNull() || aVertex2_2.IsNull())
398     return TopoDS_Vertex();
399
400   if (isSameWithSomeOf(aVertex1_1, aVertex2_1, aVertex2_2))
401   {
402     return aVertex1_1;
403   }
404   else if (isSameWithSomeOf(aVertex1_2, aVertex2_1, aVertex2_2))
405   {
406     return aVertex1_2;
407   }
408
409   const gp_Pnt        aPnt1_1 = BRep_Tool::Pnt(aVertex1_1);
410   const gp_Pnt        aPnt1_2 = BRep_Tool::Pnt(aVertex1_2);
411   const Standard_Real aTol1_1 = BRep_Tool::Tolerance(aVertex1_1);
412   const Standard_Real aTol1_2 = BRep_Tool::Tolerance(aVertex1_2);
413
414   const gp_Pnt        aPnt2_1 = BRep_Tool::Pnt(aVertex2_1);
415   const gp_Pnt        aPnt2_2 = BRep_Tool::Pnt(aVertex2_2);
416   const Standard_Real aTol2_1 = BRep_Tool::Tolerance(aVertex2_1);
417   const Standard_Real aTol2_2 = BRep_Tool::Tolerance(aVertex2_2);
418
419   if (isInToleranceWithSomeOf(aPnt1_1, aPnt2_1, aPnt2_2, aTol1_1 + Max(aTol2_1, aTol2_2)))
420   {
421     return aVertex1_1;
422   }
423   else if (isInToleranceWithSomeOf(aPnt1_2, aPnt2_1, aPnt2_2, aTol1_2 + Max(aTol2_1, aTol2_2)))
424   {
425     return aVertex1_2;
426   }
427
428   return TopoDS_Vertex();
429 }
430
431 //=======================================================================
432 // Function: connectClosestPoints
433 // Purpose : 
434 //=======================================================================
435 Standard_Boolean BRepMesh_ModelHealer::connectClosestPoints(
436   const IMeshData::IPCurveHandle& thePrevDEdge,
437   const IMeshData::IPCurveHandle& theCurrDEdge,
438   const IMeshData::IPCurveHandle& theNextDEdge) const
439 {
440   if (thePrevDEdge->IsInternal() ||
441       theCurrDEdge->IsInternal() ||
442       theNextDEdge->IsInternal())
443   {
444     return Standard_True;
445   }
446
447   gp_Pnt2d& aPrevFirstUV = thePrevDEdge->GetPoint(0);
448   gp_Pnt2d& aPrevLastUV  = thePrevDEdge->GetPoint(thePrevDEdge->ParametersNb() - 1);
449
450   if (thePrevDEdge == theCurrDEdge)
451   {
452     // Wire consists of a single edge.
453     aPrevFirstUV = aPrevLastUV;
454     return Standard_True;
455   }
456
457   gp_Pnt2d& aCurrFirstUV = theCurrDEdge->GetPoint(0);
458   gp_Pnt2d& aCurrLastUV  = theCurrDEdge->GetPoint(theCurrDEdge->ParametersNb() - 1);
459
460   gp_Pnt2d *aPrevUV = NULL, *aCurrPrevUV = NULL;
461   const Standard_Real aPrevSqDist = closestPoints(aPrevFirstUV, aPrevLastUV,
462                                                   aCurrFirstUV, aCurrLastUV,
463                                                   aPrevUV, aCurrPrevUV);
464
465   gp_Pnt2d *aNextUV = NULL, *aCurrNextUV = NULL;
466   if (thePrevDEdge == theNextDEdge)
467   {
468     // Wire consists of two edges. Connect both ends.
469     aNextUV     = (aPrevUV     == &aPrevFirstUV) ? &aPrevLastUV : &aPrevFirstUV;
470     aCurrNextUV = (aCurrPrevUV == &aCurrFirstUV) ? &aCurrLastUV : &aCurrFirstUV;
471
472     *aNextUV = *aCurrNextUV;
473     *aPrevUV = *aCurrPrevUV;
474     return Standard_True;
475   }
476
477   gp_Pnt2d& aNextFirstUV = theNextDEdge->GetPoint(0);
478   gp_Pnt2d& aNextLastUV  = theNextDEdge->GetPoint(theNextDEdge->ParametersNb() - 1);
479
480   const Standard_Real aNextSqDist = closestPoints(aNextFirstUV, aNextLastUV,
481                                                   aCurrFirstUV, aCurrLastUV,
482                                                   aNextUV, aCurrNextUV);
483
484 #ifdef DEBUG_HEALER
485   std::cout << "PrevSqDist = " << aPrevSqDist << std::endl;
486   std::cout << "NextSqDist = " << aNextSqDist << std::endl;
487 #endif
488
489   // Connect closest points first. This can help to identify 
490   // which ends should be connected in case of gap.
491   if (aPrevSqDist - aNextSqDist > gp::Resolution())
492   {
493     adjustSamePoints(aCurrNextUV, aNextUV, aCurrPrevUV, aPrevUV, aCurrFirstUV, aCurrLastUV, aPrevFirstUV, aPrevLastUV);
494   }
495   else
496   {
497     adjustSamePoints(aCurrPrevUV, aPrevUV, aCurrNextUV, aNextUV, aCurrFirstUV, aCurrLastUV, aNextFirstUV, aNextLastUV);
498   }
499
500   return Standard_True;
501 }