+ // 3. Set the result
+ theOutShape = aShapeCopy;
+
+ // 4. Collect statistics
+ {
+ ShHealOper_Tool tool;
+ ShHealOper_ModifStats& stats = tool.GetStatistics();
+
+ int nb[3] = { 0,0,0 };
+ TopTools_IndexedMapOfShape aShapes;
+ TopExp::MapShapes( theOutShape, TopAbs_VERTEX, aShapes);
+ for ( int i = 1; i <= aShapes.Extent(); ++i )
+ {
+ const TopoDS_Vertex& v = TopoDS::Vertex( aShapes( i ));
+ double tol = BRep_Tool::Tolerance( v );
+ if ( tol < aTol ) nb[0]++;
+ else if ( tol > aTol ) nb[2]++;
+ else nb[1]++;
+ }
+ if ( nb[0] > 0 )
+ stats.AddModif( "Tolerance of vertex decreased for shape validity", nb[0] );
+ if ( nb[1] > 0 )
+ stats.AddModif( "Tolerance of vertex limited as requested", nb[1] );
+ if ( nb[2] > 0 )
+ stats.AddModif( "Tolerance of vertex increased for shape validity", nb[2] );
+
+ nb[0] = nb[1] = nb[2] = 0;
+ aShapes.Clear();
+ TopExp::MapShapes( theOutShape, TopAbs_EDGE, aShapes);
+ for ( int i = 1; i <= aShapes.Extent(); ++i )
+ {
+ const TopoDS_Edge& e = TopoDS::Edge( aShapes( i ));
+ double tol = BRep_Tool::Tolerance( e );
+ if ( tol < aTol ) nb[0]++;
+ else if ( tol > aTol ) nb[2]++;
+ else nb[1]++;
+ }
+ if ( nb[0] > 0 )
+ stats.AddModif( "Tolerance of edge decreased for shape validity", nb[0] );
+ if ( nb[1] > 0 )
+ stats.AddModif( "Tolerance of edge limited as requested", nb[1] );
+ if ( nb[2] > 0 )
+ stats.AddModif( "Tolerance of edge increased for shape validity", nb[2] );
+
+ nb[0] = nb[1] = nb[2] = 0;
+ aShapes.Clear();
+ TopExp::MapShapes( theOutShape, TopAbs_FACE, aShapes);
+ for ( int i = 1; i <= aShapes.Extent(); ++i )
+ {
+ const TopoDS_Face& f = TopoDS::Face( aShapes( i ));
+ double tol = BRep_Tool::Tolerance( f );
+ if ( tol < aTol ) nb[0]++;
+ else if ( tol > aTol ) nb[2]++;
+ else nb[1]++;
+ }
+ if ( nb[0] > 0 )
+ stats.AddModif( "Tolerance of face decreased for shape validity", nb[0] );
+ if ( nb[1] > 0 )
+ stats.AddModif( "Tolerance of face limited as requested", nb[1] );
+ if ( nb[2] > 0 )
+ stats.AddModif( "Tolerance of face increased for shape validity", nb[2] );
+
+ SaveStatistics( tool );
+ }
+}
+
+//=======================================================================
+//function : FuseCollinearEdges
+//purpose :
+//=======================================================================
+void GEOMImpl_HealingDriver::FuseCollinearEdges (const TopoDS_Shape& theOriginalShape,
+ const Handle(TColStd_HSequenceOfTransient)& aVerts,
+ TopoDS_Shape& theOutShape)
+{
+ if (theOriginalShape.ShapeType() != TopAbs_WIRE)
+ Standard_TypeMismatch::Raise("Not a wire is given");
+
+ // Tolerances
+ Standard_Real AngTol = Precision::Angular();
+ Standard_Real LinTol = Precision::Confusion();
+ Standard_Real tol;
+ for (TopExp_Explorer ExV (theOriginalShape, TopAbs_VERTEX); ExV.More(); ExV.Next()) {
+ TopoDS_Vertex Vertex = TopoDS::Vertex(ExV.Current());
+ tol = BRep_Tool::Tolerance(Vertex);
+ if (tol > LinTol)
+ LinTol = tol;
+ }
+
+ // 1. Make a copy to prevent the original shape changes.
+ TopoDS_Shape aWire;
+ TColStd_IndexedDataMapOfTransientTransient aMapTShapes;
+ TNaming_CopyShape::CopyTool(theOriginalShape, aMapTShapes, aWire);
+ TopoDS_Wire theWire = TopoDS::Wire(aWire);
+
+ // 2. Sub-shapes of the wire
+ TopTools_MapOfShape aMapToRemove;
+
+ TopTools_IndexedMapOfShape anOldIndices;
+ TopExp::MapShapes(theOriginalShape, anOldIndices);
+
+ TopTools_IndexedMapOfShape aNewIndices;
+ TopExp::MapShapes(theWire, aNewIndices);
+
+ // 3. Collect vertices of the wire, same or equal to the given vertices
+ bool removeAll = false;
+ if (aVerts.IsNull() || aVerts->Length() < 1)
+ removeAll = true;
+
+ if (!removeAll) {
+ for ( int ind = 1; ind <= aVerts->Length(); ind++) {
+ Handle(GEOM_Function) aRefShape = Handle(GEOM_Function)::DownCast(aVerts->Value(ind));
+ TopoDS_Shape aShape_i = aRefShape->GetValue();
+ if (aShape_i.IsNull())
+ Standard_NullObject::Raise("Null vertex given");
+ if (aShape_i.ShapeType() != TopAbs_VERTEX)
+ Standard_TypeMismatch::Raise("Shape to suppress is not a vertex");
+
+ // find vertices shared with the initial wire
+ if (anOldIndices.Contains(aShape_i)) {
+ aMapToRemove.Add(aNewIndices.FindKey(anOldIndices.FindIndex(aShape_i)));
+ } else {
+ // try to find by coords in the new wire
+ TopoDS_Vertex aVert = TopoDS::Vertex(aShape_i);
+ gp_Pnt aP = BRep_Tool::Pnt(aVert);
+
+ bool isFound = false;
+ TopTools_MapOfShape mapShape;
+ TopExp_Explorer exp (theWire, TopAbs_VERTEX);
+ for (; exp.More() && !isFound; exp.Next()) {
+ if (mapShape.Add(exp.Current())) {
+ TopoDS_Vertex aVi = TopoDS::Vertex(exp.Current());
+ gp_Pnt aPi = BRep_Tool::Pnt(aVi);
+ if (aPi.Distance(aP) < LinTol) {
+ aMapToRemove.Add(aVi);
+ isFound = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ BRepLib::BuildCurves3d(theWire);
+ Handle(ShapeFix_Shape) Fixer = new ShapeFix_Shape(theWire);
+ Fixer->SetPrecision(LinTol);
+ Fixer->SetMaxTolerance(LinTol);
+ Fixer->Perform();
+ theWire = TopoDS::Wire(Fixer->Shape());
+ */
+
+ // Get the ordered list of edges.
+ TopTools_ListOfShape anEdges;
+ TopTools_ListOfShape aCurVertices;
+ BRepTools_WireExplorer aWExp (theWire);
+
+ for (; aWExp.More(); aWExp.Next()) {
+ anEdges.Append(aWExp.Current());
+ aCurVertices.Append(aWExp.CurrentVertex());
+ }
+
+ if (anEdges.IsEmpty()) {
+ Standard_NullObject::Raise("Empty wire given");
+ }
+
+ // Treat the case if the wire is closed and first and last edges are C1.
+ Standard_Boolean isShift = Standard_False;
+
+ if (BRep_Tool::IsClosed(theWire)) {
+ // Wire is closed. Check if there are more than 2 edges in the wire.
+ if (!anEdges.First().IsSame(anEdges.Last())) {
+ isShift = Standard_True;
+ }
+ }
+
+ if (isShift) {
+ // Put first edge to the end of the list while the chain break is reached.
+ TopoDS_Shape aFirstEdge = anEdges.First();
+
+ while (isShift) {
+ isShift = Standard_False;
+
+ // Check if the first vertex should be kept
+ if (aMapToRemove.Contains(aCurVertices.First()) || removeAll) {
+ // Check if first and last edges are C1.
+ TopoDS_Edge anEdge1 = TopoDS::Edge(anEdges.Last());
+ TopoDS_Edge anEdge2 = TopoDS::Edge(anEdges.First());
+
+ if (AreEdgesC1(anEdge1, anEdge2)) {
+ // Make the first edge last.
+ anEdges.Append(anEdge2);
+ anEdges.RemoveFirst();
+ aCurVertices.Append(aCurVertices.First());
+ aCurVertices.RemoveFirst();
+
+ // Check if we reached the first edge again.
+ // Break the loop in this case.
+ isShift = !aFirstEdge.IsSame(anEdges.First());
+ }
+ }
+ }
+ }
+
+ TopTools_ListOfShape finalList, currChain;
+ TopTools_ListIteratorOfListOfShape anEIter(anEdges);
+ TopTools_ListIteratorOfListOfShape aVIter(aCurVertices);
+ TopoDS_Edge prevEdge = TopoDS::Edge(anEIter.Value());
+
+ currChain.Append(prevEdge);
+ anEIter.Next();
+ aVIter.Next();
+
+ for (; anEIter.More(); anEIter.Next(), aVIter.Next()) {
+ TopoDS_Edge anEdge = TopoDS::Edge(anEIter.Value());
+ const TopoDS_Shape &aCurVertex = aVIter.Value();
+
+ bool continueChain = false;
+ if (aMapToRemove.Contains(aCurVertex) || removeAll) {
+ // if C1 -> continue chain
+ if (AreEdgesC1(prevEdge, anEdge)) {
+ continueChain = true;
+ }
+ }
+
+ if (!continueChain) {
+ if (currChain.Extent() == 1) {
+ // add one edge to the final list
+ finalList.Append(currChain.First());
+ }
+ else {
+ // make wire from the list of edges
+ BRep_Builder B;
+ TopoDS_Wire aCurrWire;
+ B.MakeWire(aCurrWire);
+ TopTools_ListIteratorOfListOfShape itEdges (currChain);
+ for (; itEdges.More(); itEdges.Next()) {
+ TopoDS_Shape aValue = itEdges.Value();
+ B.Add(aCurrWire, TopoDS::Edge(aValue));
+ }
+
+ // make edge from the wire
+ TopoDS_Edge anEdge = GEOMImpl_ShapeDriver::MakeEdgeFromWire(aCurrWire, LinTol, AngTol);
+
+ // add this new edge to the final list
+ finalList.Append(anEdge);
+ }
+ currChain.Clear();
+ }
+
+ // add one edge to the chain
+ currChain.Append(anEdge);
+ prevEdge = anEdge;
+ }
+
+ if (currChain.Extent() == 1) {
+ // add one edge to the final list
+ finalList.Append(currChain.First());
+ }
+ else {
+ // make wire from the list of edges
+ BRep_Builder B;
+ TopoDS_Wire aCurrWire;
+ B.MakeWire(aCurrWire);
+ TopTools_ListIteratorOfListOfShape itEdges (currChain);
+ for (; itEdges.More(); itEdges.Next()) {
+ TopoDS_Shape aValue = itEdges.Value();
+ B.Add(aCurrWire, TopoDS::Edge(aValue));
+ }
+
+ // make edge from the wire
+ TopoDS_Edge anEdge = GEOMImpl_ShapeDriver::MakeEdgeFromWire(aCurrWire, LinTol, AngTol);
+
+ // add this new edge to the final list
+ finalList.Append(anEdge);
+ }
+
+ BRep_Builder B;
+ TopoDS_Wire aFinalWire;
+ B.MakeWire(aFinalWire);
+ TopTools_ListIteratorOfListOfShape itEdges (finalList);
+ for (; itEdges.More(); itEdges.Next()) {
+ TopoDS_Shape aValue = itEdges.Value();
+ B.Add(aFinalWire, TopoDS::Edge(aValue));
+ }
+ theOutShape = aFinalWire;
+
+ if (!GEOMUtils::CheckShape(theOutShape, true))
+ StdFail_NotDone::Raise("Non valid shape result");
+}
+
+//=======================================================================
+//function : AreEdgesC1
+//purpose :
+//=======================================================================
+Standard_Boolean GEOMImpl_HealingDriver::AreEdgesC1 (const TopoDS_Edge& E1, const TopoDS_Edge& E2)
+{
+ BRepAdaptor_Curve aCurve1 (E1);
+ BRepAdaptor_Curve aCurve2 (E2);
+
+ if (aCurve1.Continuity() == GeomAbs_C0 || aCurve2.Continuity() == GeomAbs_C0)
+ return Standard_False;
+
+ Standard_Real tol, tolMax = Precision::Confusion();
+ for (TopExp_Explorer ExV1 (E1, TopAbs_VERTEX); ExV1.More(); ExV1.Next()) {
+ TopoDS_Vertex Vertex = TopoDS::Vertex(ExV1.Current());
+ tol = BRep_Tool::Tolerance(Vertex);
+ if (tol > tolMax)
+ tolMax = tol;
+ }
+ for (TopExp_Explorer ExV2 (E2, TopAbs_VERTEX); ExV2.More(); ExV2.Next()) {
+ TopoDS_Vertex Vertex = TopoDS::Vertex(ExV2.Current());
+ tol = BRep_Tool::Tolerance(Vertex);
+ if (tol > tolMax)
+ tolMax = tol;
+ }
+
+ Standard_Real f1, l1, f2, l2;
+ f1 = aCurve1.FirstParameter();
+ l1 = aCurve1.LastParameter();
+ f2 = aCurve2.FirstParameter();
+ l2 = aCurve2.LastParameter();
+
+ if (f1 > l1) {
+ Standard_Real tmp = f1;
+ f1 = l1;
+ l1 = tmp;
+ }
+
+ if (f2 > l2) {
+ Standard_Real tmp = f2;
+ f2 = l2;
+ l2 = tmp;
+ }
+
+ gp_Pnt pf1, pl1, pf2, pl2;
+ gp_Vec vf1, vl1, vf2, vl2;
+ aCurve1.D1(f1, pf1, vf1);
+ aCurve1.D1(l1, pl1, vl1);
+ aCurve2.D1(f2, pf2, vf2);
+ aCurve2.D1(l2, pl2, vl2);
+
+ // pf1--->---pl1.pf2--->---pl2
+ if (pl1.SquareDistance(pf2) < tolMax*tolMax) {
+ if (vl1.Angle(vf2) < Precision::Angular())
+ return Standard_True;
+ }
+ // pl1---<---pf1.pf2--->---pl2
+ else if (pf1.SquareDistance(pf2) < tolMax*tolMax) {
+ if (vf1.Angle(-vf2) < Precision::Angular())
+ return Standard_True;
+ }
+ // pf1--->---pl1.pl2---<---pf2
+ else if (pl1.SquareDistance(pl2) < tolMax*tolMax) {
+ if (vl1.Angle(-vl2) < Precision::Angular())
+ return Standard_True;
+ }
+ // pl1---<---pf1.pl2---<---pf2
+ else {
+ if (vf1.Angle(vl2) < Precision::Angular())
+ return Standard_True;
+ }
+
+ return Standard_False;
+}
+
+//================================================================================
+/*!
+ * \brief Returns a name of creation operation and names and values of creation parameters
+ */
+//================================================================================
+
+bool GEOMImpl_HealingDriver::
+GetCreationInformation(std::string& theOperationName,
+ std::vector<GEOM_Param>& theParams)