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