7bd071ed |
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 | } |