X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FGeomAlgoAPI%2FGeomAlgoAPI_SketchBuilder.cpp;h=5dd1a6a901eb69975047a41d8ba7a1ee46ac1cd7;hb=4c86b629d1bf8daa737f90b64e934c7bd22f6525;hp=edff6f1a43832d9d90b3ac0832c0493eb6f13298;hpb=6bceb42a25ddb76cc7e8f551824fe77cf689b55f;p=modules%2Fshaper.git diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp index edff6f1a4..5dd1a6a90 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp @@ -1,604 +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 - 3.0 * tolerance); - -/// \brief Search first vertex - the vertex with lowest x coordinate, which is used in 2 edges at least -static const TopoDS_Vertex& 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_Vertex& theStartVertex, - const BOPCol_IndexedDataMapOfShapeListOfShape& theVertexEdgeMap, - const gp_Dir& theStartDir, const gp_Dir& theNormal, - TopoDS_Vertex& theNextVertex, TopoDS_Edge& theNextEdge, - gp_Dir& theNextDir); - -/// \brief Create planar face using the edges surrounding it -static void createFace(const TopoDS_Vertex& 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_Vertex& 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_Edge& theEdge, const TopoDS_Vertex& 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 >& theFeatures, - std::list >& theResultFaces, - std::list >& 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; - - // Obtain only edges from the features list - std::list > anEdges; - std::list >::const_iterator aFeatIt = theFeatures.begin(); - for (; aFeatIt != theFeatures.end(); aFeatIt++) { - boost::shared_ptr aShape(*aFeatIt); - const TopoDS_Edge& anEdge = aShape->impl(); - if (anEdge.ShapeType() == TopAbs_EDGE) - anEdges.push_back(aShape); - } - - if (anEdges.size() == 1) { // If there is only one feature, BOPAlgo_Builder will decline to work. Need to process it anyway - aFeaturesCompound = anEdges.front()->impl(); - } else { - std::list >::const_iterator anIt = anEdges.begin(); - for (; anIt != anEdges.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_Vertex aStartVertex = findStartVertex(aMapVE, aDirX, aDirY); - aProcVertexes.push_back(aStartVertex); - - TopoDS_Vertex aCurVertex = aStartVertex; - gp_Dir aCurDir = aDirY.Reversed(); - gp_Dir aCurNorm = aNorm.Reversed(); - - // Go through the edges and find loops - TopoDS_Vertex aNextVertex; - TopoDS_Edge 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 - bool isLoopFound = false; - std::list::iterator aVertIter = aProcVertexes.begin(); - std::list::iterator anEdgeIter = aProcEdges.begin(); - for (; aVertIter != aProcVertexes.end(); aVertIter++) { - if (aVertIter->IsSame(aNextVertex)) { - isLoopFound = true; - 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; - bool isCircleFound = (isLoopFound && anEdgeIter == aProcEdges.end()); - aProcVertexes.push_back(aNextVertex); - aProcEdges.push_back(aBindingEdge); + 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 (isLoopFound) { - // If the binding edge is a full circle, then it may be added recently. Need to update edge iterator - if (isCircleFound) { - anEdgeIter = aProcEdges.end(); - anEdgeIter--; - } - else 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 once again with another initial direction. - aCurVertex = *aVertIter; - do { - aProcVertexes.pop_back(); - aProcEdges.pop_back(); - } while (aCurVertex != aProcVertexes.back()); - aCurDir = aCN.Reversed(); - //aCurNorm = aNorm.Reversed(); - continue; - } - } + 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 (!isCircleFound && anEdgeIter != aProcEdges.end() && - anEdgeIter->IsSame(aProcEdges.back())) { // The loop on the same edge taken twice - aProcVertexes.pop_back(); - aProcEdges.pop_back(); - aCurVertex = aProcVertexes.back(); - aCurDir = getOuterEdgeDirection(aProcEdges.back(), aCurVertex); - aCurNorm = aNorm.Reversed(); + if (aCurve != aShapeCurve) continue; - } - // 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)); - // remove unnecessary edges - std::list::iterator aCopyVLoop = aVertIter; - std::list::iterator aCopyELoop = anEdgeIter; - removeWasteEdges(aVertIter, anEdgeIter, aProcVertexes.end(), aProcEdges.end(), aMapVE); - - // Recalculate current vertex and current direction - aProcEdges.clear(); - aProcVertexes.clear(); - if (aMapVE.Extent() > 0) - { - aNextVertex = findStartVertex(aMapVE, aDirX, aDirY); - aProcVertexes.push_back(aNextVertex); - } - 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; } - - // 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_Edge 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); - } - } - - // 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(); - - aStartVertex = findStartVertex(aMapVE, aDirX, aDirY); - aProcVertexes.push_back(aStartVertex); - aNextVertex = aStartVertex; - aNextDir = aDirY.Reversed(); - aCurNorm = aNorm.Reversed(); - } - - 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 >& 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 >::iterator anIter1 = theFaces.begin(); - std::list >::iterator anIter2; - for (; anIter1 != theFaces.end(); anIter1++) { - anIter2 = anIter1; - for (++anIter2; anIter2 != theFaces.end(); anIter2++) { - const TopoDS_Face& aF1 = (*anIter1)->impl(); - assert(aF1.ShapeType() == TopAbs_FACE); // all items in result list should be faces - TopExp_Explorer aVert2((*anIter2)->impl(), TopAbs_VERTEX); - for (; aVert2.More(); aVert2.Next()) { - const TopoDS_Vertex& aV = (const TopoDS_Vertex&)aVert2.Current(); - aClassifier.Perform(aF1, BRep_Tool::Pnt(aV), tolerance); - TopAbs_State aState = aClassifier.State(); - if (aState != TopAbs_IN && aState != TopAbs_ON) - break; - } - if (aVert2.More()) { // second shape is not inside first, change the shapes order and repeat comparision - const TopoDS_Face& aF2 = (*anIter2)->impl(); - assert(aF2.ShapeType() == TopAbs_FACE); // all items in result list should be faces - TopExp_Explorer aVert1((*anIter1)->impl(), TopAbs_VERTEX); - for (; aVert1.More(); aVert1.Next()) { - const TopoDS_Vertex& aV = (const TopoDS_Vertex&)aVert2.Current(); - aClassifier.Perform(aF2, BRep_Tool::Pnt(aV), tolerance); - TopAbs_State aState = aClassifier.State(); - if (aState != TopAbs_IN && aState != TopAbs_ON) - break; - } - if (!aVert1.More()) { // first shape should be cut from the second - BRepAlgoAPI_Cut aCut((*anIter2)->impl(), (*anIter1)->impl()); - aCut.Build(); - TopExp_Explorer anExp(aCut.Shape(), TopAbs_FACE); - bool isFirstFace = true; - for (anExp.Next(); anExp.More(); anExp.Next()) { - if (anExp.Current().ShapeType() != TopAbs_FACE) continue; - if (isFirstFace) { - (*anIter2)->setImpl(new TopoDS_Shape(anExp.Current())); - isFirstFace = false; - } else { - boost::shared_ptr aShape(new GeomAPI_Shape); - aShape->setImpl(new TopoDS_Shape(anExp.Current())); - theFaces.push_back(aShape); - } - } + // 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()); - aCut.Build(); - TopExp_Explorer anExp(aCut.Shape(), TopAbs_FACE); - bool isFirstFace = true; - for (anExp.Next(); anExp.More(); anExp.Next()) { - if (anExp.Current().ShapeType() != TopAbs_FACE) continue; - if (isFirstFace) { - (*anIter1)->setImpl(new TopoDS_Shape(anExp.Current())); - isFirstFace = false; - } else { - boost::shared_ptr aShape(new GeomAPI_Shape); - aShape->setImpl(new TopoDS_Shape(anExp.Current())); - theFaces.push_back(aShape); - } + } + 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_Vertex& 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 gp_Pnt& aVertPnt = BRep_Tool::Pnt(aV); - - 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); } - return static_cast(theMapVE.FindKey(aStartVertexInd)); + // 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(); + } + // if in first list there is no elements left, it is the first + return isFirst; } -void findNextVertex(const TopoDS_Vertex& theStartVertex, - const BOPCol_IndexedDataMapOfShapeListOfShape& theVertexEdgeMap, - const gp_Dir& theStartDir, const gp_Dir& theNormal, TopoDS_Vertex& theNextVertex, - TopoDS_Edge& 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()) { - const TopoDS_Edge& anEdge = static_cast(aEdIter.Value()); - gp_Dir aTang = getOuterEdgeDirection(anEdge, 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 = anEdge; - TopExp_Explorer aVertExp(theNextEdge, TopAbs_VERTEX); - for (; aVertExp.More(); aVertExp.Next()) - if (!aVertExp.Current().IsSame(theStartVertex)) { - theNextVertex = static_cast(aVertExp.Current()); - theNextDir = getOuterEdgeDirection(anEdge, theNextVertex); - break; - } - if (!aVertExp.More()) { // This edge is a full circle - TopoDS_Vertex aV1, aV2; - TopExp::Vertices(theNextEdge, aV1, aV2); - if (aV1.Orientation() == theStartVertex.Orientation()) - theNextVertex = aV2; - else - theNextVertex = aV1; - theNextDir = getOuterEdgeDirection(anEdge, 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.IsSame(theSpliceVertex) - && 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.IsSame(theSpliceVertex)) { - aCurVertChanged = aVertex; - isCurVertChanged = true; - } + myResultFaces.clear(); + setDone(false); + if (theEdges.empty()) + return; + + BRep_Builder aBuilder; + // 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); } - theSpliceVertex = isCurVertChanged ? aCurVertChanged : aVertExp.Current(); + aBB->Perform(); + if (aBB->HasErrors()) + return; - theBuilder.Add(theWire, anEdge); -} + 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 createFace(const TopoDS_Vertex& 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); + // 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); + } - TopoDS_Vertex aCurVertex = theStartVertex; - std::list::const_iterator anEdgeIter = theStartEdge; - for (; anEdgeIter != theEndOfEdges; anEdgeIter++) { - if (!anEdgeIter->IsNull()) - addEdgeToWire(*anEdgeIter, aBuilder, aCurVertex, aResWire); - } + // iterate on wires + for (TopTools_ListIteratorOfListOfShape aWIt(aWires); aWIt.More(); aWIt.Next()) { + TopoDS_Wire aWire = TopoDS::Wire(aWIt.Value()); - BRepBuilderAPI_MakeFace aFaceBuilder(thePlane, aResWire); - if (aFaceBuilder.Error() == BRepBuilderAPI_FaceDone) - theResFace = aFaceBuilder.Face(); -} + // 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); -void createWireList(const TopoDS_Vertex& 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_Vertex aCurVertex = theStartVertex; - - std::list::iterator anIter = theStartEdge; - while (anIter != theEndOfEdges) { - while (anIter != theEndOfEdges && needNewWire && theEdgesInLoops.count(&(*anIter)) != 0) { - TopExp_Explorer aVertExp(*anIter, TopAbs_VERTEX); - for (; aVertExp.More(); aVertExp.Next()) - if (!aVertExp.Current().IsSame(aCurVertex)) { - aCurVertex = static_cast(aVertExp.Current()); - break; + 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); } - anIter++; - } - 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)) != 0) { // There was found the edge already used in loop. - // Current wire should be released and new one should started - needNewWire = true; - continue; + 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); } - addEdgeToWire(*anIter, 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_Edge& theEdge, const TopoDS_Vertex& theVertex) +GeomAlgoAPI_SketchBuilder::GeomAlgoAPI_SketchBuilder( + const std::shared_ptr& thePlane, + const std::list >& theEdges) { - gp_Pnt aVertexPnt = BRep_Tool::Pnt(theVertex); - - // Convert the edge to the curve to calculate the tangency. - // There should be only one curve in the edge. - double aFirst, aLast; - Handle(Geom_Curve) aCurve = BRep_Tool::Curve(theEdge, aFirst, aLast); - - gp_Pnt aPnt; - gp_Vec aTang; - // A direction is determined not in the boundary points but in the points with small shift. - // It was done to avoid tangency between circle and other edge in the shared vertex. - aCurve->D1(aFirst + shift > aLast ? aFirst : aFirst + shift, aPnt, aTang); - aCurve->D0(aFirst, aPnt); - if (aVertexPnt.IsEqual(aPnt, tolerance)) - return gp_Dir(aTang.Reversed()); - - aCurve->D1(aLast - shift < aFirst ? aLast : 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().IsSame(*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); } -