1 // Created on: 2016-06-23
2 // Copyright (c) 2016 OPEN CASCADE SAS
3 // Created by: Oleg AGASHIN
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
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>
26 #include <TopoDS_Vertex.hxx>
29 #include <BRepBuilderAPI_MakePolygon.hxx>
30 #include <BRepTools.hxx>
31 #include <BRep_Builder.hxx>
32 #include <TopoDS_Compound.hxx>
35 IMPLEMENT_STANDARD_RTTIEXT(BRepMesh_ModelHealer, IMeshTools_ModelAlgo)
39 //! Decreases deflection of the given edge and tries to update discretization.
44 EdgeAmplifier(const IMeshTools_Parameters& theParameters)
45 : myParameters(theParameters)
50 void operator()(const IMeshData::IEdgePtr& theDEdge) const
52 const IMeshData::IEdgeHandle aDEdge = theDEdge;
54 Standard_Integer aPointsNb = aDEdge->GetCurve()->ParametersNb();
56 aDEdge->Clear(Standard_True);
57 aDEdge->SetDeflection(Max(aDEdge->GetDeflection() / 3., Precision::Confusion()));
59 for (Standard_Integer aPCurveIt = 0; aPCurveIt < aDEdge->PCurvesNb(); ++aPCurveIt)
61 const IMeshData::IPCurveHandle& aPCurve = aDEdge->GetPCurve(aPCurveIt);
62 const IMeshData::IFaceHandle aDFace = aPCurve->GetFace();
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)
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);
80 BRepMesh_EdgeDiscret::Tessellate3d(aDEdge, aTessellator, Standard_False);
81 BRepMesh_EdgeDiscret::Tessellate2d(aDEdge, Standard_False);
86 EdgeAmplifier (const EdgeAmplifier& theOther);
88 void operator=(const EdgeAmplifier& theOther);
91 const IMeshTools_Parameters& myParameters;
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)
100 return (theRefVertex.IsSame(theVertex1) ||
101 theRefVertex.IsSame(theVertex2));
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)
111 const Standard_Real aSqTol = theTol * theTol;
112 return (theRefPoint.SquareDistance(thePoint1) < aSqTol ||
113 theRefPoint.SquareDistance(thePoint2) < aSqTol);
117 //=======================================================================
118 // Function: Constructor
120 //=======================================================================
121 BRepMesh_ModelHealer::BRepMesh_ModelHealer()
125 //=======================================================================
126 // Function: Destructor
128 //=======================================================================
129 BRepMesh_ModelHealer::~BRepMesh_ModelHealer()
133 //=======================================================================
136 //=======================================================================
137 Standard_Boolean BRepMesh_ModelHealer::performInternal(
138 const Handle(IMeshData_Model)& theModel,
139 const IMeshTools_Parameters& theParameters,
140 const Message_ProgressRange& theRange)
144 myParameters = theParameters;
145 if (myModel.IsNull())
147 return Standard_False;
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();
157 myFaceIntersectingEdges = new IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs;
158 for (Standard_Integer aFaceIt = 0; aFaceIt < myModel->FacesNb(); ++aFaceIt)
160 myFaceIntersectingEdges->Bind(myModel->GetFace(aFaceIt).get(), Handle(IMeshData::MapOfIEdgePtr)());
163 // TODO: Here we can process edges in order to remove close discrete points.
164 OSD_Parallel::For(0, myModel->FacesNb(), *this, !isParallel());
167 IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs::Iterator aFaceIt(*myFaceIntersectingEdges);
168 for (; aFaceIt.More(); aFaceIt.Next())
170 if (!aFaceIt.Value().IsNull())
172 const IMeshData::IFaceHandle aDFace = aFaceIt.Key();
173 aDFace->SetStatus(IMeshData_SelfIntersectingWire);
174 aDFace->SetStatus(IMeshData_Failure);
178 myFaceIntersectingEdges.Nullify();
179 myModel.Nullify(); // Do not hold link to model.
180 return Standard_True;
183 //=======================================================================
184 // Function: amplifyEdges
186 //=======================================================================
187 void BRepMesh_ModelHealer::amplifyEdges()
189 Handle(NCollection_IncAllocator) aTmpAlloc =
190 new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE);
192 Standard_Integer aAmpIt = 0;
193 const Standard_Real aIterNb = 5;
194 IMeshData::MapOfIEdgePtr aEdgesToUpdate(1, aTmpAlloc);
195 EdgeAmplifier anEdgeAmplifier (myParameters);
197 while (aAmpIt++ < aIterNb && popEdgesToUpdate(aEdgesToUpdate))
199 // Try to update discretization by decreasing deflection of problematic edges.
200 OSD_Parallel::ForEach(aEdgesToUpdate.cbegin(), aEdgesToUpdate.cend(),
202 !(myParameters.InParallel && aEdgesToUpdate.Size() > 1),
203 aEdgesToUpdate.Size());
205 IMeshData::MapOfIFacePtr aFacesToCheck(1, aTmpAlloc);
206 IMeshData::MapOfIEdgePtr::Iterator aEdgeIt(aEdgesToUpdate);
207 for (; aEdgeIt.More(); aEdgeIt.Next())
209 const IMeshData::IEdgeHandle aDEdge = aEdgeIt.Value();
210 for (Standard_Integer aPCurveIt = 0; aPCurveIt < aDEdge->PCurvesNb(); ++aPCurveIt)
212 aFacesToCheck.Add(aDEdge->GetPCurve(aPCurveIt)->GetFace());
216 OSD_Parallel::ForEach(aFacesToCheck.cbegin(), aFacesToCheck.cend(),
217 *this, !(myParameters.InParallel && aFacesToCheck.Size() > 1),
218 aFacesToCheck.Size());
220 aEdgesToUpdate.Clear();
221 aTmpAlloc->Reset(Standard_False);
225 //=======================================================================
226 // Function: popEdgesToUpdate
228 //=======================================================================
229 Standard_Boolean BRepMesh_ModelHealer::popEdgesToUpdate(
230 IMeshData::MapOfIEdgePtr& theEdgesToUpdate)
232 IMeshData::DMapOfIFacePtrsMapOfIEdgePtrs::Iterator aFaceIt(*myFaceIntersectingEdges);
233 for (; aFaceIt.More(); aFaceIt.Next())
235 Handle(IMeshData::MapOfIEdgePtr)& aIntersections = aFaceIt.ChangeValue();
236 if (!aIntersections.IsNull())
238 theEdgesToUpdate.Unite(*aIntersections);
239 aIntersections.Nullify();
243 return !theEdgesToUpdate.IsEmpty();
246 //=======================================================================
249 //=======================================================================
250 void BRepMesh_ModelHealer::process(const IMeshData::IFaceHandle& theDFace) const
256 Handle(IMeshData::MapOfIEdgePtr)& aIntersections = myFaceIntersectingEdges->ChangeFind(theDFace.get());
257 aIntersections.Nullify();
259 fixFaceBoundaries(theDFace);
261 if (!theDFace->IsSet(IMeshData_Failure))
263 BRepMesh_FaceChecker aChecker(theDFace, myParameters);
264 if (!aChecker.Perform())
267 std::cout << "Failed : #" << aChecker.GetIntersectingEdges()->Size() << std::endl;
269 aIntersections = aChecker.GetIntersectingEdges();
273 if (theDFace->WiresNb () == 1)
275 const IMeshData::IWireHandle& aDWire = theDFace->GetWire (0);
277 if (aDWire->EdgesNb () == 2)
279 const IMeshData::IEdgePtr& aDEdge0 = aDWire->GetEdge (0);
280 const IMeshData::IEdgePtr& aDEdge1 = aDWire->GetEdge (1);
282 const IMeshData::IPCurveHandle& aPCurve0 = aDEdge0->GetPCurve (theDFace.get (), aDWire->GetEdgeOrientation (0));
283 const IMeshData::IPCurveHandle& aPCurve1 = aDEdge1->GetPCurve (theDFace.get (), aDWire->GetEdgeOrientation (1));
285 if (aPCurve0->ParametersNb () == 2 && aPCurve1->ParametersNb () == 2)
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);
297 catch (Standard_Failure const&)
299 theDFace->SetStatus (IMeshData_Failure);
303 //=======================================================================
304 // Function: fixFaceBoundaries
306 //=======================================================================
307 void BRepMesh_ModelHealer::fixFaceBoundaries(const IMeshData::IFaceHandle& theDFace) const
310 TopoDS_Compound aComp;
311 BRep_Builder aBuilder;
312 aBuilder.MakeCompound(aComp);
315 for (int aWireIt = 0; aWireIt < theDFace->WiresNb(); ++aWireIt)
317 const IMeshData::IWireHandle& aDWire = theDFace->GetWire(aWireIt);
318 BRepMesh_Deflection::ComputeDeflection(aDWire, myParameters);
319 for (int aEdgeIt = 0; aEdgeIt < aDWire->EdgesNb(); ++aEdgeIt)
321 const int aPrevEdgeIt = (aEdgeIt + aDWire->EdgesNb() - 1) % aDWire->EdgesNb();
322 const int aNextEdgeIt = (aEdgeIt + 1) % aDWire->EdgesNb();
324 const IMeshData::IEdgeHandle aPrevEdge = aDWire->GetEdge(aPrevEdgeIt);
325 const IMeshData::IEdgeHandle aCurrEdge = aDWire->GetEdge(aEdgeIt);
326 const IMeshData::IEdgeHandle aNextEdge = aDWire->GetEdge(aNextEdgeIt);
328 Standard_Boolean isConnected = !getCommonVertex(aCurrEdge, aNextEdge).IsNull() &&
329 !getCommonVertex(aPrevEdge, aCurrEdge).IsNull();
333 const IMeshData::IPCurveHandle& aPrevPCurve =
334 aPrevEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aPrevEdgeIt));
336 const IMeshData::IPCurveHandle& aCurrPCurve =
337 aCurrEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aEdgeIt));
339 const IMeshData::IPCurveHandle& aNextPCurve =
340 aNextEdge->GetPCurve(theDFace.get(), aDWire->GetEdgeOrientation(aNextEdgeIt));
342 isConnected = connectClosestPoints(aPrevPCurve, aCurrPCurve, aNextPCurve);
345 BRepBuilderAPI_MakePolygon aPoly;
346 for (int i = 0; i < aCurrPCurve->ParametersNb(); ++i)
348 const gp_Pnt2d& aPnt = aCurrPCurve->GetPoint(i);
349 aPoly.Add(gp_Pnt(aPnt.X(), aPnt.Y(), 0.));
354 aBuilder.Add(aComp, aPoly.Shape());
356 TCollection_AsciiString aName("face_discr.brep");
357 BRepTools::Write(aComp, aName.ToCString());
361 if (!isConnected || aCurrEdge->IsSet(IMeshData_Outdated))
363 // We have to clean face from triangulation.
364 theDFace->SetStatus(IMeshData_Outdated);
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);
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());
383 BRepMesh_Deflection::ComputeDeflection(theDFace, myParameters);
386 //=======================================================================
387 // Function: hasCommonVertex
389 //=======================================================================
390 TopoDS_Vertex BRepMesh_ModelHealer::getCommonVertex(
391 const IMeshData::IEdgeHandle& theEdge1,
392 const IMeshData::IEdgeHandle& theEdge2) const
394 TopoDS_Vertex aVertex1_1, aVertex1_2;
395 TopExp::Vertices(theEdge1->GetEdge(), aVertex1_1, aVertex1_2);
397 //Test bugs moddata_2 bug428.
398 // restore [locate_data_file OCC428.brep] rr
402 // nbshapes rr_91_2_2
403 // # 0 vertices; 1 edge
405 //This shape is invalid and can lead to exception in this code.
407 if (aVertex1_1.IsNull() || aVertex1_2.IsNull())
408 return TopoDS_Vertex();
410 if (theEdge1->GetEdge().IsSame(theEdge2->GetEdge()))
412 return aVertex1_1.IsSame(aVertex1_2) ? aVertex1_1 : TopoDS_Vertex();
415 TopoDS_Vertex aVertex2_1, aVertex2_2;
416 TopExp::Vertices(theEdge2->GetEdge(), aVertex2_1, aVertex2_2);
418 if (aVertex2_1.IsNull() || aVertex2_2.IsNull())
419 return TopoDS_Vertex();
421 if (isSameWithSomeOf(aVertex1_1, aVertex2_1, aVertex2_2))
425 else if (isSameWithSomeOf(aVertex1_2, aVertex2_1, aVertex2_2))
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);
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);
440 if (isInToleranceWithSomeOf(aPnt1_1, aPnt2_1, aPnt2_2, aTol1_1 + Max(aTol2_1, aTol2_2)))
444 else if (isInToleranceWithSomeOf(aPnt1_2, aPnt2_1, aPnt2_2, aTol1_2 + Max(aTol2_1, aTol2_2)))
449 return TopoDS_Vertex();
452 //=======================================================================
453 // Function: connectClosestPoints
455 //=======================================================================
456 Standard_Boolean BRepMesh_ModelHealer::connectClosestPoints(
457 const IMeshData::IPCurveHandle& thePrevDEdge,
458 const IMeshData::IPCurveHandle& theCurrDEdge,
459 const IMeshData::IPCurveHandle& theNextDEdge) const
461 if (thePrevDEdge->IsInternal() ||
462 theCurrDEdge->IsInternal() ||
463 theNextDEdge->IsInternal())
465 return Standard_True;
468 gp_Pnt2d& aPrevFirstUV = thePrevDEdge->GetPoint(0);
469 gp_Pnt2d& aPrevLastUV = thePrevDEdge->GetPoint(thePrevDEdge->ParametersNb() - 1);
471 if (thePrevDEdge == theCurrDEdge)
473 // Wire consists of a single edge.
474 aPrevFirstUV = aPrevLastUV;
475 return Standard_True;
478 gp_Pnt2d& aCurrFirstUV = theCurrDEdge->GetPoint(0);
479 gp_Pnt2d& aCurrLastUV = theCurrDEdge->GetPoint(theCurrDEdge->ParametersNb() - 1);
481 gp_Pnt2d *aPrevUV = NULL, *aCurrPrevUV = NULL;
482 const Standard_Real aPrevSqDist = closestPoints(aPrevFirstUV, aPrevLastUV,
483 aCurrFirstUV, aCurrLastUV,
484 aPrevUV, aCurrPrevUV);
486 gp_Pnt2d *aNextUV = NULL, *aCurrNextUV = NULL;
487 if (thePrevDEdge == theNextDEdge)
489 // Wire consists of two edges. Connect both ends.
490 aNextUV = (aPrevUV == &aPrevFirstUV) ? &aPrevLastUV : &aPrevFirstUV;
491 aCurrNextUV = (aCurrPrevUV == &aCurrFirstUV) ? &aCurrLastUV : &aCurrFirstUV;
493 *aNextUV = *aCurrNextUV;
494 *aPrevUV = *aCurrPrevUV;
495 return Standard_True;
498 gp_Pnt2d& aNextFirstUV = theNextDEdge->GetPoint(0);
499 gp_Pnt2d& aNextLastUV = theNextDEdge->GetPoint(theNextDEdge->ParametersNb() - 1);
501 const Standard_Real aNextSqDist = closestPoints(aNextFirstUV, aNextLastUV,
502 aCurrFirstUV, aCurrLastUV,
503 aNextUV, aCurrNextUV);
506 std::cout << "PrevSqDist = " << aPrevSqDist << std::endl;
507 std::cout << "NextSqDist = " << aNextSqDist << std::endl;
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())
514 adjustSamePoints(aCurrNextUV, aNextUV, aCurrPrevUV, aPrevUV, aCurrFirstUV, aCurrLastUV, aPrevFirstUV, aPrevLastUV);
518 adjustSamePoints(aCurrPrevUV, aPrevUV, aCurrNextUV, aNextUV, aCurrFirstUV, aCurrLastUV, aNextFirstUV, aNextLastUV);
521 return Standard_True;