X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FGeomAlgoAPI%2FGeomAlgoAPI_SketchBuilder.cpp;h=5dd1a6a901eb69975047a41d8ba7a1ee46ac1cd7;hb=HEAD;hp=cba0974a5f18101631d2027e54ba359119fcc153;hpb=e72ec8c7799926fd0ffe7cd2baf471a6026e1dad;p=modules%2Fshaper.git diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp index cba0974a5..5dd1a6a90 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp @@ -1,638 +1,356 @@ -// File: GeomAlgoAPI_SketchBuilder.cpp -// Created: 02 Jun 2014 -// Author: Artem ZHIDKOV +// Copyright (C) 2014-2024 CEA, EDF +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// #include +#include -#include - -#include -#include +#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include +#include +#include #include #include +#include #include -#include -#include -#include +#include +#include +#include -#include +#include +#include +#include -#ifndef DBL_MAX -#define DBL_MAX 1.7976931348623158e+308 -#endif - - -const double tolerance = Precision::Confusion(); -// This value helps to find direction on the boundaries of curve. -// It is not significant for lines, but is used for circles to avoid -// wrong directions of movement (when two edges are tangent on the certain vertex) -const double shift = acos(1.0 - 2.0 * tolerance); - -/// \brief Search first vertex - the vertex with lowest x coordinate, which is used in 2 edges at least -static const TopoDS_Shape& findStartVertex( - const BOPCol_IndexedDataMapOfShapeListOfShape& theMapVE, - const gp_Dir& theDirX, const gp_Dir& theDirY); - -/// \brief Search the vertex on the sketch candidate to be the next one in the loop -static void findNextVertex( - const TopoDS_Shape& theStartVertex, - const BOPCol_IndexedDataMapOfShapeListOfShape& theVertexEdgeMap, - const gp_Dir& theStartDir, - const gp_Dir& theNormal, - TopoDS_Shape& theNextVertex, - TopoDS_Shape& theNextEdge, - gp_Dir& theNextDir); - -/// \brief Create planar face using the edges surrounding it -static void createFace(const TopoDS_Shape& theStartVertex, - const std::list::iterator& theStartEdge, - const std::list::iterator& theEndOfEdges, - const gp_Pln& thePlane, - TopoDS_Face& theResFace); - -/// \bief Create planar wire -static void createWireList(const TopoDS_Shape& theStartVertex, - const std::list::iterator& theStartEdge, - const std::list::iterator& theEndOfEdges, - const std::set& theEdgesInLoops, - std::list& theResWires); - -/// \brief Calculate outer tengency on the edge in specified vertex -static gp_Dir getOuterEdgeDirection(const TopoDS_Shape& theEdge, - const TopoDS_Shape& theVertex); - -/// \brief Unnecessary edges will be removed from the map. -/// Positions of iterator will be updated -static void removeWasteEdges( - std::list::iterator& theStartVertex, - std::list::iterator& theStartEdge, - const std::list::iterator& theEndOfVertexes, - const std::list::iterator& theEndOfEdges, - BOPCol_IndexedDataMapOfShapeListOfShape& theMapVE); - - - - -void GeomAlgoAPI_SketchBuilder::createFaces( - const boost::shared_ptr& theOrigin, - const boost::shared_ptr& theDirX, - const boost::shared_ptr& theDirY, - const boost::shared_ptr& theNorm, - const std::list< boost::shared_ptr >& theFeatures, - std::list< boost::shared_ptr >& theResultFaces, - std::list< boost::shared_ptr >& theResultWires) +static TopoDS_Vertex findStartVertex(const TopoDS_Shape& theShape) { - if (theFeatures.empty()) - return ; - - // Create the list of edges with shared vertexes - BOPAlgo_Builder aBuilder; - BOPAlgo_PaveFiller aPF; - TopoDS_Shape aFeaturesCompound; - - if (theFeatures.size() == 1) - { // If there is only one feature, BOPAlgo_Builder will decline to work. Need to process it anyway - aFeaturesCompound = theFeatures.front()->impl(); - } - else - { - std::list< boost::shared_ptr >::const_iterator anIt = theFeatures.begin(); - for (; anIt != theFeatures.end(); anIt++) - { - boost::shared_ptr aPreview(*anIt); - aBuilder.AddArgument(aPreview->impl()); + static const double aTol = Precision::PConfusion(); + + TopExp_Explorer anExp(theShape, TopAbs_VERTEX); + TopoDS_Vertex aStart = TopoDS::Vertex(anExp.Current()); + gp_Pnt aStartPnt(BRep_Tool::Pnt(aStart)); + TopoDS_Vertex aCurrent; + gp_Pnt aCurrentPnt; + + for (anExp.Next(); anExp.More(); anExp.Next()) { + aCurrent = TopoDS::Vertex(anExp.Current()); + aCurrentPnt = BRep_Tool::Pnt(aCurrent); + if ((aCurrentPnt.X() > aStartPnt.X() + aTol) || + (aCurrentPnt.X() > aStartPnt.X() - aTol && aCurrentPnt.Y() > aStartPnt.Y() + aTol) || + (aCurrentPnt.X() > aStartPnt.X() - aTol && aCurrentPnt.Y() > aStartPnt.Y() - aTol && + aCurrentPnt.Z() > aStartPnt.Z() + aTol)) { + aStart = aCurrent; + aStartPnt = aCurrentPnt; } - aPF.SetArguments(aBuilder.Arguments()); - aPF.Perform(); - int aErr = aPF.ErrorStatus(); - if (aErr) return ; - aBuilder.PerformWithFiller(aPF); - aErr = aBuilder.ErrorStatus(); - if (aErr) return ; - aFeaturesCompound = aBuilder.Shape(); } + return aStart; +} - BOPCol_IndexedDataMapOfShapeListOfShape aMapVE; // map between vertexes and edges - BOPTools::MapShapesAndAncestors(aFeaturesCompound, TopAbs_VERTEX, TopAbs_EDGE, aMapVE); - if (aMapVE.IsEmpty()) // in case of not-initialized circle - return; - - gp_Dir aDirX = theDirX->impl(); - gp_Dir aDirY = theDirY->impl(); - gp_Dir aNorm = theNorm->impl(); - - gp_Pln aPlane(theOrigin->impl(), aNorm); - - // Set of edges used in loops - std::set anEdgesInLoops; - // Lists for processed vertexes and edges - std::list aProcVertexes; - std::list aProcEdges; - - // Search the start vertex - TopoDS_Shape aStartVertex = findStartVertex(aMapVE, aDirX, aDirY); - aProcVertexes.push_back(aStartVertex); - - TopoDS_Shape aCurVertex = aStartVertex; - gp_Dir aCurDir = aDirY.Reversed(); - gp_Dir aCurNorm = aNorm.Reversed(); - - // Go through the edges and find loops - TopoDS_Shape aNextVertex; - TopoDS_Shape aBindingEdge; - gp_Dir aNextDir; - while (aMapVE.Extent() > 0) - { - if (aCurVertex.IsNull()) - return; - findNextVertex(aCurVertex, aMapVE, aCurDir, aCurNorm, aNextVertex, aBindingEdge, aNextDir); - aCurNorm = aNorm; - - // Try to find next vertex in the list of already processed - std::list::iterator aVertIter = aProcVertexes.begin(); - std::list::iterator anEdgeIter = aProcEdges.begin(); - for ( ; aVertIter != aProcVertexes.end(); aVertIter++) - { - if (aVertIter->TShape() == aNextVertex.TShape()) - break; - if (anEdgeIter != aProcEdges.end()) - anEdgeIter++; - } +static TopoDS_Vertex findStartVertex(const TopoDS_Wire& theWire, const TopoDS_Face& theFace, + const std::list >& theInitialShapes) +{ + // Try to find edge lying on the one of original edges. + // First found edge will be taken as a start edge for the result wire + std::list >::const_iterator aFeatIt = theInitialShapes.begin(); + for (; aFeatIt != theInitialShapes.end(); aFeatIt++) { + std::shared_ptr aShape(*aFeatIt); + const TopoDS_Edge& anEdge = aShape->impl(); + if (anEdge.ShapeType() != TopAbs_EDGE) + continue; - aProcVertexes.push_back(aNextVertex); - aProcEdges.push_back(aBindingEdge); - - // The loop was found - if (aVertIter != aProcVertexes.end()) - { - // If the binding edge is a full circle, then the list may be empty before addition. Need to update edge iterator - if (aProcEdges.size() == 1) - anEdgeIter = aProcEdges.begin(); - - if (aVertIter != aProcVertexes.begin()) - { - // Check the orientation of the loop - gp_Dir aCN = getOuterEdgeDirection(*anEdgeIter, *aVertIter); - gp_Dir aCP = getOuterEdgeDirection(aProcEdges.back(), *aVertIter); - aCN.Reverse(); - aCP.Reverse(); - if (aCN.DotCross(aCP, aNorm) < -tolerance) - { - // The found loop has wrong orientation and may contain sub-loops. - // Need to check it onle again with another initial direction. - aCurVertex = *aVertIter; - do { - aProcVertexes.pop_back(); - aProcEdges.pop_back(); - } while (aCurVertex != aProcVertexes.back()); - aCurDir = getOuterEdgeDirection(aProcEdges.back(), aCurVertex); - aCurNorm = aNorm.Reversed(); - continue; - } - } + double aFirst, aLast; + Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); + if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve)) + aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve(); - // When the orientation is correct or the edges looped through - // the first element, create new face and remove unnecessary edges. - TopoDS_Face aPatch; - createFace(*aVertIter, anEdgeIter, aProcEdges.end(), aPlane, aPatch); - if (!aPatch.IsNull()) - { - boost::shared_ptr aFace(new GeomAPI_Shape); - aFace->setImpl(new TopoDS_Face(aPatch)); - theResultFaces.push_back(aFace); - } - // push the edges used in the loop to the map - std::list::iterator anIter; - for (anIter = anEdgeIter; anIter != aProcEdges.end(); anIter++) - anEdgesInLoops.insert(anIter->TShape()); - // remove unnecessary edges - std::list::iterator aCopyVLoop = aVertIter; - std::list::iterator aCopyELoop = anEdgeIter; - removeWasteEdges(aVertIter, anEdgeIter, aProcVertexes.end(), aProcEdges.end(), aMapVE); - - // revert the list of remaining edges - std::list aRemainVertexes; - for ( ; aVertIter != aProcVertexes.end(); aVertIter++) - aRemainVertexes.push_front(*aVertIter); - std::list aRemainEdges; - for ( ; anEdgeIter != aProcEdges.end(); anEdgeIter++) - aRemainEdges.push_front(*anEdgeIter); - // remove edges and vertexes used in the loop and add remaining ones - aProcVertexes.erase(aCopyVLoop, aProcVertexes.end()); - aProcVertexes.insert(aProcVertexes.end(), aRemainVertexes.begin(), aRemainVertexes.end()); - aProcEdges.erase(aCopyELoop, aProcEdges.end()); - aProcEdges.insert(aProcEdges.end(), aRemainEdges.begin(), aRemainEdges.end()); - - // Recalculate current vertex and current direction - if (!aProcVertexes.empty()) - { - aNextVertex = aProcVertexes.back(); - if (!aProcEdges.empty()) - aNextDir = getOuterEdgeDirection(aProcEdges.back(), aNextVertex); - else aNextDir = aDirY; - } - } + BRepTools_WireExplorer anExp(theWire, theFace); + for (; anExp.More(); anExp.Next()) { + const TopoDS_Edge& aShapeEdge = anExp.Current(); + double aF, aL; + Handle(Geom_Curve) aShapeCurve = BRep_Tool::Curve(aShapeEdge, aF, aL); + if (aShapeCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve)) + aShapeCurve = Handle(Geom_TrimmedCurve)::DownCast(aShapeCurve)->BasisCurve(); - // if next vertex connected only to alone edge, this is a part of wire (not a closed loop), - // we need to go back through the list of already checked edges to find a branching vertex - if (!aMapVE.IsEmpty() && aMapVE.Contains(aNextVertex) && - aMapVE.FindFromKey(aNextVertex).Size() == 1) - { - std::list::reverse_iterator aVRIter = aProcVertexes.rbegin(); - std::list::reverse_iterator aERIter = aProcEdges.rbegin(); - if (aVRIter != aProcVertexes.rend()) - aVRIter++; - if (aERIter != aProcEdges.rend()) - aERIter++; - - for ( ; aERIter != aProcEdges.rend(); aERIter++, aVRIter++) - if (aMapVE.FindFromKey(*aVRIter).Size() > 2) - break; - if (aERIter != aProcEdges.rend() || - (aVRIter != aProcVertexes.rend() && aMapVE.FindFromKey(*aVRIter).Size() == 1)) - { // the branching vertex was found or current list of edges is a wire without branches - std::list::iterator aEIter; - TopoDS_Shape aCurEdge; - if (aERIter != aProcEdges.rend()) - { - aEIter = aERIter.base(); - aCurEdge = *aERIter; - } - else - aEIter = aProcEdges.begin(); - std::list aTail; - createWireList(*aVRIter, aEIter, aProcEdges.end(), anEdgesInLoops, aTail); - std::list::const_iterator aTailIter = aTail.begin(); - for ( ; aTailIter != aTail.end(); aTailIter++) - if (!aTailIter->IsNull()) - { - boost::shared_ptr aWire(new GeomAPI_Shape); - aWire->setImpl(new TopoDS_Shape(*aTailIter)); - theResultWires.push_back(aWire); - } - std::list::iterator aVIter = aVRIter.base(); - std::list::iterator aEItCopy = aEIter; - removeWasteEdges(--aVIter, aEItCopy, aProcVertexes.end(), aProcEdges.end(), aMapVE); - - aProcEdges.erase(aEIter, aProcEdges.end()); - aVIter = aVRIter.base(); - aProcVertexes.erase(aVIter, aProcVertexes.end()); - - if (!aProcVertexes.empty()) - { - aNextVertex = aProcVertexes.back(); - if (!aCurEdge.IsNull()) - aNextDir = getOuterEdgeDirection(aCurEdge, aNextVertex); - } - } - else - { // there is no branching vertex in the list of proceeded, - // so we should revert the list and go the opposite way - aProcVertexes.reverse(); - aProcEdges.reverse(); - aNextVertex = aProcVertexes.back(); - aNextDir = aProcEdges.empty() ? aDirY : - getOuterEdgeDirection(aProcEdges.back(), aNextVertex); - } - } + if (aCurve != aShapeCurve) + continue; - // When all edges of current component of connectivity are processed, - // we should repeat procedure for finding initial vertex in the remaining - if (!aMapVE.IsEmpty() && !aMapVE.Contains(aNextVertex)) - { - aProcVertexes.clear(); - aProcEdges.clear(); - - TopoDS_Shape aStartEdge; - aStartVertex = findStartVertex(aMapVE, aDirX, aDirY); - aProcVertexes.push_back(aStartVertex); - aNextVertex = aStartVertex; - aNextDir = aDirY.Reversed(); - aCurNorm = aNorm.Reversed(); + // the edge is found, search vertex + TopoDS_Vertex aV1, aV2; + TopExp::Vertices(aShapeEdge, aV1, aV2); + return fabs(aF - aFirst) <= fabs(aL - aFirst) ? aV1 : aV2; } - - aCurVertex = aNextVertex; - aCurDir = aNextDir; } - if (theResultFaces.size() > 1) - fixIntersections(theResultFaces); + // start vertex is not found, use algorithm to search vertex with the greatest coordinates + return findStartVertex(theWire); } -void GeomAlgoAPI_SketchBuilder::fixIntersections( - std::list< boost::shared_ptr >& theFaces) +// returns true if the first shape must be located earlier than the second +bool isFirst(const TopoDS_Shape& theFirst, const TopoDS_Shape& theSecond, + NCollection_DataMap >& theAreaToIndex, + const NCollection_DataMap& theCurveToIndex) { - BRepClass_FaceClassifier aClassifier; - - std::list< boost::shared_ptr >::iterator anIter1 = theFaces.begin(); - std::list< boost::shared_ptr >::iterator anIter2; - for ( ; anIter1 != theFaces.end(); anIter1++) - { - anIter2 = anIter1; - for (++anIter2; anIter2 != theFaces.end(); anIter2++) - { - const TopoDS_Face& aF1 = (*anIter1)->impl(); - if (aF1.ShapeType() != TopAbs_FACE) // TODO: MPV - this workaround must be fixed later by AZV, now it just removes crash - continue; - TopExp_Explorer aVert2((*anIter2)->impl(), TopAbs_VERTEX); - for ( ; aVert2.More(); aVert2.Next()) - { - Handle(BRep_TVertex) aV = Handle(BRep_TVertex)::DownCast(aVert2.Current().TShape()); - aClassifier.Perform(aF1, aV->Pnt(), tolerance); - if (aClassifier.State() != TopAbs_IN) - break; - } - if (aVert2.More()) - { // second shape is not inside first, change the shapes order and repeat comparision - const TopoDS_Face& aF2 = (*anIter2)->impl(); - TopExp_Explorer aVert1((*anIter1)->impl(), TopAbs_VERTEX); - for ( ; aVert1.More(); aVert1.Next()) - { - Handle(BRep_TVertex) aV = Handle(BRep_TVertex)::DownCast(aVert1.Current().TShape()); - aClassifier.Perform(aF2, aV->Pnt(), tolerance); - if (aClassifier.State() != TopAbs_IN) - break; - } - if (!aVert1.More()) - { // first shape should be cut from the second - BRepAlgoAPI_Cut aCut((*anIter2)->impl(), - (*anIter1)->impl()); - (*anIter2)->setImpl(new TopoDS_Shape(aCut.Shape())); + // fill theAreaToIndex for both shapes if needed + for(int aShapeNum = 1; aShapeNum <= 2; aShapeNum++) { + TopoDS_Shape aShape = aShapeNum == 1 ? theFirst : theSecond; + if (!theAreaToIndex.IsBound(aShape)) { // fill the list of curve indices + NCollection_List aNewList; + TopExp_Explorer anEdgesExp(aShape, TopAbs_EDGE); + for (; anEdgesExp.More(); anEdgesExp.Next()) { + double aFirst, aLast; + Handle(Geom_Curve) aCurve = BRep_Tool::Curve( + TopoDS::Edge(anEdgesExp.Current()), aFirst, aLast); + if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve)) + aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve(); + if (theCurveToIndex.IsBound(aCurve)) { + aNewList.Append(theCurveToIndex.Find(aCurve)); } } - else - { // second shape should be cut from the first - BRepAlgoAPI_Cut aCut((*anIter1)->impl(), - (*anIter2)->impl()); - (*anIter1)->setImpl(new TopoDS_Shape(aCut.Shape())); + if (aNewList.Extent()) { + NCollection_Array1 aNewArray(1, aNewList.Extent()); + NCollection_List::Iterator aListIter(aNewList); + for (int anIndex = 1; aListIter.More(); aListIter.Next(), anIndex++) { + aNewArray.SetValue(anIndex, aListIter.Value()); + } + std::sort(aNewArray.begin(), aNewArray.end()); + theAreaToIndex.Bind(aShape, aNewArray); } } } -} - - -// =================== Auxiliary functions ==================================== -const TopoDS_Shape& findStartVertex( - const BOPCol_IndexedDataMapOfShapeListOfShape& theMapVE, - const gp_Dir& theDirX, const gp_Dir& theDirY) -{ - int aStartVertexInd = 1; - double aMaxX = -DBL_MAX; - double aMaxY = -DBL_MAX; - int aNbVert = theMapVE.Extent(); - for (int i = 1; i <= aNbVert; i++) - { - const TopoDS_Vertex& aV = (const TopoDS_Vertex&)theMapVE.FindKey(i); - const Handle(BRep_TVertex)& aVert = (const Handle(BRep_TVertex)&)aV.TShape(); - const gp_Pnt& aVertPnt = aVert->Pnt(); - - double aX = aVertPnt.XYZ().Dot(theDirX.XYZ()); - double aY = aVertPnt.XYZ().Dot(theDirY.XYZ()); - if ((aX > aMaxX || (fabs(aX - aMaxX) < tolerance && aY > aMaxY)) && - theMapVE.FindFromIndex(i).Extent() > 1) - { - aMaxX = aX; - aMaxY = aY; - aStartVertexInd = i; + bool isFirst; + bool aGeomCompare = !theAreaToIndex.IsBound(theFirst) || !theAreaToIndex.IsBound(theSecond); + if (!aGeomCompare) { + // compare lists of indices one by one to find chich list indices are lower + NCollection_Array1::Iterator aFirstList(theAreaToIndex.ChangeFind(theFirst)); + NCollection_Array1::Iterator aSecondList(theAreaToIndex.ChangeFind(theSecond)); + for (; aFirstList.More() && aSecondList.More(); aFirstList.Next(), aSecondList.Next()) { + if (aFirstList.Value() < aSecondList.Value()) return true; + if (aFirstList.Value() > aSecondList.Value()) return false; } + aGeomCompare = !aFirstList.More() && !aSecondList.More(); + isFirst = !aFirstList.More(); + } else { + isFirst = !theAreaToIndex.IsBound(theFirst); + } + // if faces are identical by curves names (circle splitted by line in seam-point), use parameters + if (aGeomCompare) { + GProp_GProps aGProps; + BRepGProp::SurfaceProperties(theFirst, aGProps); + gp_Pnt aCentre1 = aGProps.CentreOfMass(); + BRepGProp::SurfaceProperties(theSecond, aGProps); + gp_Pnt aCentre2 = aGProps.CentreOfMass(); + return aCentre1.X() + aCentre1.Y() + aCentre1.Z() < aCentre2.X() + aCentre2.Y() + aCentre2.Z(); } - return theMapVE.FindKey(aStartVertexInd); + // if in first list there is no elements left, it is the first + return isFirst; } -void findNextVertex( - const TopoDS_Shape& theStartVertex, - const BOPCol_IndexedDataMapOfShapeListOfShape& theVertexEdgeMap, - const gp_Dir& theStartDir, - const gp_Dir& theNormal, - TopoDS_Shape& theNextVertex, - TopoDS_Shape& theNextEdge, - gp_Dir& theNextDir) +// sorts faces (in theAreas list) to make persistent order: by initial shapes edges +static void sortAreas(TopTools_ListOfShape& theAreas, + const std::list >& theInitialShapes) { - const BOPCol_ListOfShape& anEdgesList = theVertexEdgeMap.FindFromKey(theStartVertex); - BOPCol_ListOfShape::Iterator aEdIter(anEdgesList); - double aBestEdgeProj = DBL_MAX; - for ( ; aEdIter.More(); aEdIter.Next()) - { - gp_Dir aTang = getOuterEdgeDirection(aEdIter.Value(), theStartVertex); - aTang.Reverse(); - - // The projection is normalized in segment (-1, 1), - // where (-1, 0] corresponds to the angles (pi/2, 0] between theStartDir and aTang - // and [0, 1) corresponds to the angles [0, -pi/2) - double aProj = (aTang.Dot(theStartDir) - 1.0) * 0.5; - if (fabs(fabs(aProj) - 1) < tolerance) + // collect indices of all edges to operate them quickly + NCollection_DataMap aCurveToIndex; // curve -> index in initial shapes + std::list >::const_iterator aFeatIt = theInitialShapes.begin(); + for (int anIndex = 0; aFeatIt != theInitialShapes.end(); aFeatIt++) { + std::shared_ptr aShape(*aFeatIt); + const TopoDS_Edge& anEdge = aShape->impl(); + if (anEdge.ShapeType() != TopAbs_EDGE) continue; - if (theStartDir.DotCross(aTang, theNormal) < tolerance) - aProj *= -1.0; - - if (aProj < aBestEdgeProj) - { - aBestEdgeProj = aProj; - theNextEdge = aEdIter.Value(); - TopExp_Explorer aVertExp(theNextEdge, TopAbs_VERTEX); - for ( ; aVertExp.More(); aVertExp.Next()) - if (aVertExp.Current().TShape() != theStartVertex.TShape()) - { - theNextVertex = aVertExp.Current(); - theNextDir = getOuterEdgeDirection(aEdIter.Value(), theNextVertex); - break; - } - if (!aVertExp.More()) - { // This edge is a full circle - TopoDS_Vertex aV1, aV2; - TopExp::Vertices(*(const TopoDS_Edge*)(&theNextEdge), aV1, aV2); - if (aV1.Orientation() == theStartVertex.Orientation()) - theNextVertex = aV2; - else - theNextVertex = aV1; - theNextDir = getOuterEdgeDirection(aEdIter.Value(), theNextVertex); + + double aFirst, aLast; + Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); + if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve)) + aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve(); + if (!aCurveToIndex.IsBound(aCurve)) + aCurveToIndex.Bind(aCurve, anIndex++); + } + // map from area to the most first indices of curves (to compare) in it + NCollection_DataMap > anAreaToIndex; + // sort areas + TopTools_ListOfShape::Iterator anArea1(theAreas); + for(; anArea1.More(); anArea1.Next()) { + TopTools_ListOfShape::Iterator anArea2 = anArea1; + for(anArea2.Next(); anArea2.More(); anArea2.Next()) { + if (!isFirst(anArea1.Value(), anArea2.Value(), anAreaToIndex, aCurveToIndex)) { // exchange + TopoDS_Shape aTmp = anArea1.Value(); + anArea1.ChangeValue() = anArea2.Value(); + anArea2.ChangeValue() = aTmp; } } } } -static void addEdgeToWire(const TopoDS_Edge& theEdge, - const BRep_Builder& theBuilder, - TopoDS_Shape& theSpliceVertex, - TopoDS_Wire& theWire) +void GeomAlgoAPI_SketchBuilder::build( + const std::shared_ptr& theOrigin, + const std::shared_ptr& theDirX, + const std::shared_ptr& theNorm, + const std::list >& theEdges) { - TopoDS_Edge anEdge = theEdge; - bool isCurVertChanged = false; - TopoDS_Shape aCurVertChanged; - - TopExp_Explorer aVertExp(theEdge, TopAbs_VERTEX); - for ( ; aVertExp.More(); aVertExp.Next()) - { - const TopoDS_Shape& aVertex = aVertExp.Current(); - if (aVertex.TShape() == theSpliceVertex.TShape() && - aVertex.Orientation() != theEdge.Orientation()) - { // Current vertex is the last for the edge, so its orientation is wrong, need to revert the edge - anEdge.Reverse(); - break; - } - if (aVertex.TShape() != theSpliceVertex.TShape()) - { - aCurVertChanged = aVertex; - isCurVertChanged = true; - } - } - theSpliceVertex = isCurVertChanged ? aCurVertChanged : aVertExp.Current(); - - theBuilder.Add(theWire, anEdge); -} + myResultFaces.clear(); + setDone(false); + if (theEdges.empty()) + return; -void createFace(const TopoDS_Shape& theStartVertex, - const std::list::iterator& theStartEdge, - const std::list::iterator& theEndOfEdges, - const gp_Pln& thePlane, - TopoDS_Face& theResFace) -{ - TopoDS_Wire aResWire; BRep_Builder aBuilder; - aBuilder.MakeWire(aResWire); - - TopoDS_Shape aCurVertex = theStartVertex; - std::list::const_iterator anEdgeIter = theStartEdge; - for ( ; anEdgeIter != theEndOfEdges; anEdgeIter++) - { - TopoDS_Edge anEdge = *((TopoDS_Edge*)(&(*anEdgeIter))); - if (!anEdge.IsNull()) - addEdgeToWire(anEdge, aBuilder, aCurVertex, aResWire); + // Planar face, where the sketch was built + gp_Ax3 aPlnAxes(theOrigin->impl(), theNorm->impl(), theDirX->impl()); + Handle(Geom_Surface) aPlane(new Geom_Plane(aPlnAxes)); + TopoDS_Face aPlnFace; + aBuilder.MakeFace(aPlnFace, aPlane, Precision::Confusion()); + + // Use General Fuse algorithm to prepare all subfaces, bounded by given list of edges + BOPAlgo_Builder* aBB = new BOPAlgo_Builder; + aBB->AddArgument(aPlnFace); + // Set fuzzy value for BOP, because PlaneGCS can solve the set of constraints with + // the precision up to 5.e-5 if the sketch contains arcs. + static const double THE_FUZZY_TOL = 5.e-5; + aBB->SetFuzzyValue(THE_FUZZY_TOL); + + setImpl(aBB); + setBuilderType(OCCT_BOPAlgo_Builder); + + NCollection_List anEdges; + NCollection_List::Iterator aShapeIt; + std::list >::const_iterator aFeatIt = theEdges.begin(); + for (; aFeatIt != theEdges.end(); aFeatIt++) { + std::shared_ptr aShape(*aFeatIt); + const TopoDS_Edge& anEdge = aShape->impl(); + if (anEdge.ShapeType() == TopAbs_EDGE) + aBB->AddArgument(anEdge); } + aBB->Perform(); + if (aBB->HasErrors()) + return; - BRepBuilderAPI_MakeFace aFaceBuilder(thePlane, aResWire); - if (aFaceBuilder.Error() == BRepBuilderAPI_FaceDone) - theResFace = aFaceBuilder.Face(); -} + TopoDS_Compound aResult; + aBuilder.MakeCompound(aResult); + + // Collect faces + TopTools_ListOfShape anAreas = aBB->Modified(aPlnFace); + sortAreas(anAreas, theEdges); // sort faces by the edges in them + TopTools_ListIteratorOfListOfShape anIt(anAreas); + for (; anIt.More(); anIt.Next()) { + TopoDS_Face aFace = TopoDS::Face(anIt.Value()); + // avoid infinite faces + BRepTopAdaptor_FClass2d aFClass(aFace, Precision::Confusion()); + if (aFClass.PerformInfinitePoint() == TopAbs_IN) + continue; -void createWireList(const TopoDS_Shape& theStartVertex, - const std::list::iterator& theStartEdge, - const std::list::iterator& theEndOfEdges, - const std::set& theEdgesInLoops, - std::list& theResWires) -{ - BRep_Builder aBuilder; - bool needNewWire = true; - TopoDS_Shape aCurVertex = theStartVertex; - - std::list::iterator anIter = theStartEdge; - while (anIter != theEndOfEdges) - { - while (anIter != theEndOfEdges && needNewWire && - theEdgesInLoops.count(anIter->TShape()) != 0) - { - TopExp_Explorer aVertExp(*anIter, TopAbs_VERTEX); - for ( ; aVertExp.More(); aVertExp.Next()) - if (aVertExp.Current().TShape() != aCurVertex.TShape()) - { - aCurVertex = aVertExp.Current(); - break; - } - anIter++; + // rebuild face + TopoDS_Face aNewFace; + aBuilder.MakeFace(aNewFace, aPlane, Precision::Confusion()); + + // sort inner wires according to the original edges as well as faces + TopTools_ListOfShape aWires; + TopExp_Explorer aWireExp(aFace, TopAbs_WIRE); + for (; aWireExp.More(); aWireExp.Next()) + aWires.Append(aWireExp.Current()); + if (aWires.Size() > 2) { + TopoDS_Shape anOuterWire = aWires.First(); + aWires.RemoveFirst(); + sortAreas(aWires, theEdges); + aWires.Prepend(anOuterWire); } - if (anIter == theEndOfEdges) - break; - - if (needNewWire) - { // The new wire should be created - TopoDS_Wire aWire; - aBuilder.MakeWire(aWire); - theResWires.push_back(aWire); - needNewWire = false; - } - else if (theEdgesInLoops.count(anIter->TShape()) != 0) - { // There was found the edge already used in loop. - // Current wire should be released and new one should started - needNewWire = true; - continue; + + // iterate on wires + for (TopTools_ListIteratorOfListOfShape aWIt(aWires); aWIt.More(); aWIt.Next()) { + TopoDS_Wire aWire = TopoDS::Wire(aWIt.Value()); + + // to make faces equal on different platforms, we will find + // a vertex lying on an edge with the lowest index in the list of initial edges + TopoDS_Vertex aStartVertex = findStartVertex(aWire, aFace, theEdges); + + TopoDS_Wire aNewWire; + aBuilder.MakeWire(aNewWire); + std::list aSkippedEdges; + bool aStartFound = false; + + // remove internal edges from faces and make wire start from found vertex + BRepTools_WireExplorer anExp(aWire, aFace); + for (; anExp.More(); anExp.Next()) { + if (anExp.Current().Orientation() == TopAbs_INTERNAL) + continue; + if (!aStartFound) { + const TopoDS_Edge& anEdge = anExp.Current(); + TopoDS_Vertex aV1, aV2; + TopExp::Vertices(anEdge, aV1, aV2, Standard_True); + if (aV1.IsSame(aStartVertex) == Standard_True) + aStartFound = true; + else + aSkippedEdges.push_back(anEdge); + } + if (aStartFound) + aBuilder.Add(aNewWire, anExp.Current()); + } + // add skipped edges to the end of wire + std::list::const_iterator aSkIt = aSkippedEdges.begin(); + for (; aSkIt != aSkippedEdges.end(); ++aSkIt) + aBuilder.Add(aNewWire, *aSkIt); + + // check the wire is empty + anExp.Init(aNewWire); + if (anExp.More()) + aBuilder.Add(aNewFace, aNewWire); } - TopoDS_Edge anEdge = *((TopoDS_Edge*)(&(*anIter))); - addEdgeToWire(anEdge, aBuilder, aCurVertex, theResWires.back()); - anIter++; + // store face + aBuilder.Add(aResult, aNewFace); + std::shared_ptr aResFace(new GeomAPI_Shape); + aResFace->setImpl(new TopoDS_Face(aNewFace)); + myResultFaces.push_back(aResFace); } -} + // update results + GeomShapePtr aResShape(new GeomAPI_Shape); + aResShape->setImpl(new TopoDS_Shape(aResult)); + setShape(aResShape); + setDone(true); +} -gp_Dir getOuterEdgeDirection(const TopoDS_Shape& theEdge, - const TopoDS_Shape& theVertex) +GeomAlgoAPI_SketchBuilder::GeomAlgoAPI_SketchBuilder( + const std::shared_ptr& thePlane, + const std::list >& theEdges) { - const Handle(BRep_TVertex)& aVertex = (const Handle(BRep_TVertex)&)theVertex.TShape(); - gp_Pnt aVertexPnt = aVertex->Pnt(); - - const Handle(BRep_TEdge)& anEdge = (const Handle(BRep_TEdge)&)theEdge.TShape(); - - // Convert the edge to the curve to calculate the tangency. - // There should be only one curve in the edge. - Handle(BRep_Curve3D) aEdCurve = - Handle(BRep_Curve3D)::DownCast(anEdge->Curves().First()); - double aFirst, aLast; - aEdCurve->Range(aFirst, aLast); - Handle(Geom_Curve) aCurve = aEdCurve->Curve3D(); - - gp_Pnt aPnt; - gp_Vec aTang; - aCurve->D1(aFirst + shift, aPnt, aTang); - aCurve->D0(aFirst, aPnt); - if (aVertexPnt.IsEqual(aPnt, tolerance)) - return gp_Dir(aTang.Reversed()); - - aCurve->D1(aLast - shift, aPnt, aTang); - return gp_Dir(aTang); + build(thePlane->location(), thePlane->xDirection(), thePlane->direction(), theEdges); } -void removeWasteEdges( - std::list::iterator& theStartVertex, - std::list::iterator& theStartEdge, - const std::list::iterator& theEndOfVertexes, - const std::list::iterator& theEndOfEdges, - BOPCol_IndexedDataMapOfShapeListOfShape& theMapVE) +GeomAlgoAPI_SketchBuilder::GeomAlgoAPI_SketchBuilder( + const std::shared_ptr& theOrigin, + const std::shared_ptr& theDirX, + const std::shared_ptr& theNorm, + const std::shared_ptr& theWire) { - bool isVertStep = true; - while (theStartVertex != theEndOfVertexes && theStartEdge != theEndOfEdges) - { - BOPCol_ListOfShape& aBunch = theMapVE.ChangeFromKey(*theStartVertex); - BOPCol_ListOfShape::Iterator anApprEdge(aBunch); - for ( ; anApprEdge.More(); anApprEdge.Next()) - if (anApprEdge.Value() == *theStartEdge) - break; - if (anApprEdge.More()) - aBunch.Remove(anApprEdge); - - if (isVertStep) - theStartVertex++; - else - { - theStartEdge++; - // check current vertex to be a branching point - // if so, it will be a new starting point to find a loop - if (aBunch.Size() > 1) - break; + std::shared_ptr aWire = + std::dynamic_pointer_cast(theWire); + if(aWire) { + // Filter wires, return only faces. + build(theOrigin, theDirX, theNorm, aWire->getEdges()); + } else { // it may be only one circle + std::shared_ptr anEdge = std::dynamic_pointer_cast(theWire); + if (anEdge) { + std::list > aList; + aList.push_back(anEdge); + build(theOrigin, theDirX, theNorm, aList); } - isVertStep = !isVertStep; } - - // The map of vertex-edges may be changed - BOPCol_IndexedDataMapOfShapeListOfShape aMapVECopy; - BOPCol_IndexedDataMapOfShapeListOfShape::Iterator aMapIter(theMapVE); - for (int ind = 1; aMapIter.More(); aMapIter.Next(), ind++) - if (!aMapIter.Value().IsEmpty()) - aMapVECopy.Add(theMapVE.FindKey(ind), aMapIter.Value()); - theMapVE.Clear(); - theMapVE.Exchange(aMapVECopy); } -