1 // Copyright (C) 2014-2019 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include <GeomAlgoAPI_SketchBuilder.h>
21 #include <GeomAPI_PlanarEdges.h>
23 #include <GeomAPI_Pln.h>
25 #include <BOPAlgo_Builder.hxx>
26 #include <BRep_Builder.hxx>
27 #include <BRepTools_WireExplorer.hxx>
28 #include <BRepTopAdaptor_FClass2d.hxx>
29 #include <Geom_Plane.hxx>
30 #include <Geom_TrimmedCurve.hxx>
31 #include <Precision.hxx>
33 #include <TopExp_Explorer.hxx>
35 #include <TopoDS_Edge.hxx>
36 #include <TopTools_ListIteratorOfListOfShape.hxx>
37 #include <GProp_GProps.hxx>
38 #include <BRepGProp.hxx>
44 static TopoDS_Vertex findStartVertex(const TopoDS_Shape& theShape)
46 static const double aTol = Precision::PConfusion();
48 TopExp_Explorer anExp(theShape, TopAbs_VERTEX);
49 TopoDS_Vertex aStart = TopoDS::Vertex(anExp.Current());
50 gp_Pnt aStartPnt(BRep_Tool::Pnt(aStart));
51 TopoDS_Vertex aCurrent;
54 for (anExp.Next(); anExp.More(); anExp.Next()) {
55 aCurrent = TopoDS::Vertex(anExp.Current());
56 aCurrentPnt = BRep_Tool::Pnt(aCurrent);
57 if ((aCurrentPnt.X() > aStartPnt.X() + aTol) ||
58 (aCurrentPnt.X() > aStartPnt.X() - aTol && aCurrentPnt.Y() > aStartPnt.Y() + aTol) ||
59 (aCurrentPnt.X() > aStartPnt.X() - aTol && aCurrentPnt.Y() > aStartPnt.Y() - aTol &&
60 aCurrentPnt.Z() > aStartPnt.Z() + aTol)) {
62 aStartPnt = aCurrentPnt;
68 static TopoDS_Vertex findStartVertex(const TopoDS_Wire& theWire, const TopoDS_Face& theFace,
69 const std::list<std::shared_ptr<GeomAPI_Shape> >& theInitialShapes)
71 // Try to find edge lying on the one of original edges.
72 // First found edge will be taken as a start edge for the result wire
73 std::list<std::shared_ptr<GeomAPI_Shape> >::const_iterator aFeatIt = theInitialShapes.begin();
74 for (; aFeatIt != theInitialShapes.end(); aFeatIt++) {
75 std::shared_ptr<GeomAPI_Shape> aShape(*aFeatIt);
76 const TopoDS_Edge& anEdge = aShape->impl<TopoDS_Edge>();
77 if (anEdge.ShapeType() != TopAbs_EDGE)
81 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
82 if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
83 aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
85 BRepTools_WireExplorer anExp(theWire, theFace);
86 for (; anExp.More(); anExp.Next()) {
87 const TopoDS_Edge& aShapeEdge = anExp.Current();
89 Handle(Geom_Curve) aShapeCurve = BRep_Tool::Curve(aShapeEdge, aF, aL);
90 if (aShapeCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
91 aShapeCurve = Handle(Geom_TrimmedCurve)::DownCast(aShapeCurve)->BasisCurve();
93 if (aCurve != aShapeCurve)
96 // the edge is found, search vertex
97 TopoDS_Vertex aV1, aV2;
98 TopExp::Vertices(aShapeEdge, aV1, aV2);
99 return fabs(aF - aFirst) <= fabs(aL - aFirst) ? aV1 : aV2;
103 // start vertex is not found, use algorithm to search vertex with the greatest coordinates
104 return findStartVertex(theWire);
107 static double averageValue(const NCollection_Array1<int>& theArray)
110 for (NCollection_Array1<int>::Iterator anIt(theArray); anIt.More(); anIt.Next())
111 aSum += (double)anIt.Value();
112 return aSum / theArray.Size();
115 // returns true if the first shape must be located earlier than the second
116 static bool isFirst(const TopoDS_Shape& theFirst, const TopoDS_Shape& theSecond,
117 NCollection_DataMap<TopoDS_Shape, NCollection_Array1<int> >& theAreaToIndex,
118 const NCollection_DataMap<Handle(Geom_Curve), int>& theCurveToIndex)
120 // fill theAreaToIndex for both shapes if needed
121 for(int aShapeNum = 1; aShapeNum <= 2; aShapeNum++) {
122 TopoDS_Shape aShape = aShapeNum == 1 ? theFirst : theSecond;
123 if (!theAreaToIndex.IsBound(aShape)) { // fill the list of curve indices
124 NCollection_List<int> aNewList;
125 TopExp_Explorer anEdgesExp(aShape, TopAbs_EDGE);
126 for (; anEdgesExp.More(); anEdgesExp.Next()) {
127 double aFirst, aLast;
128 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(
129 TopoDS::Edge(anEdgesExp.Current()), aFirst, aLast);
130 if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
131 aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
132 if (theCurveToIndex.IsBound(aCurve)) {
133 aNewList.Append(theCurveToIndex.Find(aCurve));
136 if (aNewList.Extent()) {
137 NCollection_Array1<int> aNewArray(1, aNewList.Extent());
138 NCollection_List<int>::Iterator aListIter(aNewList);
139 for (int anIndex = 1; aListIter.More(); aListIter.Next(), anIndex++) {
140 aNewArray.SetValue(anIndex, aListIter.Value());
142 std::sort(aNewArray.begin(), aNewArray.end());
143 theAreaToIndex.Bind(aShape, aNewArray);
148 bool aGeomCompare = !theAreaToIndex.IsBound(theFirst) || !theAreaToIndex.IsBound(theSecond);
150 // compare average values of the lists
151 NCollection_Array1<int>& aFirstList = theAreaToIndex.ChangeFind(theFirst);
152 NCollection_Array1<int>& aSecondList = theAreaToIndex.ChangeFind(theSecond);
153 if (aFirstList.Size() > 1 && aSecondList.Size() > 1) {
154 double aFirstMiddle = averageValue(aFirstList);
155 double aSecondMiddle = averageValue(aSecondList);
156 if ((double)aFirstList.Last() < aSecondMiddle)
158 else if (aFirstMiddle > (double)aSecondList.Last())
161 // compare lists of indices one by one to find which list indices are lower
162 NCollection_Array1<int>::Iterator aFirstListIt(aFirstList);
163 NCollection_Array1<int>::Iterator aSecondListIt(aSecondList);
164 for (; aFirstListIt.More() && aSecondListIt.More();
165 aFirstListIt.Next(), aSecondListIt.Next()) {
166 if (aFirstListIt.Value() < aSecondListIt.Value()) return true;
167 if (aFirstListIt.Value() > aSecondListIt.Value()) return false;
169 aGeomCompare = !aFirstListIt.More() && !aSecondListIt.More();
170 isFirst = !aFirstListIt.More();
172 isFirst = !theAreaToIndex.IsBound(theFirst);
174 // if faces are identical by curves names (circle splitted by line in seam-point), use parameters
176 GProp_GProps aGProps;
177 BRepGProp::SurfaceProperties(theFirst, aGProps);
178 gp_Pnt aCentre1 = aGProps.CentreOfMass();
179 BRepGProp::SurfaceProperties(theSecond, aGProps);
180 gp_Pnt aCentre2 = aGProps.CentreOfMass();
181 return aCentre1.X() + aCentre1.Y() + aCentre1.Z() < aCentre2.X() + aCentre2.Y() + aCentre2.Z();
183 // if in first list there is no elements left, it is the first
187 // sorts faces (in theAreas list) to make persistent order: by initial shapes edges
188 static void sortFaces(TopTools_ListOfShape& theAreas,
189 const std::list<std::shared_ptr<GeomAPI_Shape> >& theInitialShapes)
191 // collect indices of all edges to operate them quickly
192 NCollection_DataMap<Handle(Geom_Curve), int> aCurveToIndex; // curve -> index in initial shapes
193 std::list<std::shared_ptr<GeomAPI_Shape> >::const_iterator aFeatIt = theInitialShapes.begin();
194 for (int anIndex = 0; aFeatIt != theInitialShapes.end(); ++aFeatIt, ++anIndex) {
195 std::shared_ptr<GeomAPI_Shape> aShape(*aFeatIt);
196 const TopoDS_Edge& anEdge = aShape->impl<TopoDS_Edge>();
197 if (anEdge.ShapeType() != TopAbs_EDGE)
200 double aFirst, aLast;
201 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
202 if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
203 aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
204 if (!aCurveToIndex.IsBound(aCurve))
205 aCurveToIndex.Bind(aCurve, anIndex);
207 // map from area to the most first indices of curves (to compare) in it
208 NCollection_DataMap<TopoDS_Shape, NCollection_Array1<int> > anAreaToIndex;
210 TopTools_ListOfShape::Iterator anArea1(theAreas);
211 for(; anArea1.More(); anArea1.Next()) {
212 TopTools_ListOfShape::Iterator anArea2 = anArea1;
213 for(anArea2.Next(); anArea2.More(); anArea2.Next()) {
214 if (!isFirst(anArea1.Value(), anArea2.Value(), anAreaToIndex, aCurveToIndex)) { // exchange
215 TopoDS_Shape aTmp = anArea1.Value();
216 anArea1.ChangeValue() = anArea2.Value();
217 anArea2.ChangeValue() = aTmp;
223 void GeomAlgoAPI_SketchBuilder::build(
224 const std::shared_ptr<GeomAPI_Pnt>& theOrigin,
225 const std::shared_ptr<GeomAPI_Dir>& theDirX,
226 const std::shared_ptr<GeomAPI_Dir>& theNorm,
227 const std::list<std::shared_ptr<GeomAPI_Shape> >& theEdges)
229 myResultFaces.clear();
231 if (theEdges.empty())
234 BRep_Builder aBuilder;
235 // Planar face, where the sketch was built
236 Handle(Geom_Surface) aPlane(new Geom_Plane(theOrigin->impl<gp_Pnt>(), theNorm->impl<gp_Dir>()));
237 TopoDS_Face aPlnFace;
238 aBuilder.MakeFace(aPlnFace, aPlane, Precision::Confusion());
240 // Use General Fuse algorithm to prepare all subfaces, bounded by given list of edges
241 BOPAlgo_Builder* aBB = new BOPAlgo_Builder;
242 aBB->AddArgument(aPlnFace);
245 setBuilderType(OCCT_BOPAlgo_Builder);
247 NCollection_List<TopoDS_Shape> anEdges;
248 NCollection_List<TopoDS_Shape>::Iterator aShapeIt;
249 std::list<std::shared_ptr<GeomAPI_Shape> >::const_iterator aFeatIt = theEdges.begin();
250 for (; aFeatIt != theEdges.end(); aFeatIt++) {
251 std::shared_ptr<GeomAPI_Shape> aShape(*aFeatIt);
252 const TopoDS_Edge& anEdge = aShape->impl<TopoDS_Edge>();
253 if (anEdge.ShapeType() == TopAbs_EDGE)
254 aBB->AddArgument(anEdge);
257 if (aBB->HasErrors())
260 TopoDS_Compound aResult;
261 aBuilder.MakeCompound(aResult);
264 TopTools_ListOfShape anAreas = aBB->Modified(aPlnFace);
265 sortFaces(anAreas, theEdges); // sort faces by the edges in them
266 TopTools_ListIteratorOfListOfShape anIt(anAreas);
267 for (; anIt.More(); anIt.Next()) {
268 TopoDS_Face aFace = TopoDS::Face(anIt.Value());
269 // avoid infinite faces
270 BRepTopAdaptor_FClass2d aFClass(aFace, Precision::Confusion());
271 if (aFClass.PerformInfinitePoint() == TopAbs_IN)
275 TopoDS_Face aNewFace;
276 aBuilder.MakeFace(aNewFace, aPlane, Precision::Confusion());
279 TopExp_Explorer aWireExp(aFace, TopAbs_WIRE);
280 for (; aWireExp.More(); aWireExp.Next()) {
281 TopoDS_Wire aWire = TopoDS::Wire(aWireExp.Current());
283 // to make faces equal on different platforms, we will find
284 // a vertex lying on an edge with the lowest index in the list of initial edges
285 TopoDS_Vertex aStartVertex = findStartVertex(aWire, aFace, theEdges);
287 TopoDS_Wire aNewWire;
288 aBuilder.MakeWire(aNewWire);
289 std::list<TopoDS_Edge> aSkippedEdges;
290 bool aStartFound = false;
292 // remove internal edges from faces and make wire start from found vertex
293 BRepTools_WireExplorer anExp(aWire, aFace);
294 for (; anExp.More(); anExp.Next()) {
295 if (anExp.Current().Orientation() == TopAbs_INTERNAL)
298 const TopoDS_Edge& anEdge = anExp.Current();
299 TopoDS_Vertex aV1, aV2;
300 TopExp::Vertices(anEdge, aV1, aV2, Standard_True);
301 if (aV1.IsSame(aStartVertex) == Standard_True)
304 aSkippedEdges.push_back(anEdge);
307 aBuilder.Add(aNewWire, anExp.Current());
309 // add skipped edges to the end of wire
310 std::list<TopoDS_Edge>::const_iterator aSkIt = aSkippedEdges.begin();
311 for (; aSkIt != aSkippedEdges.end(); ++aSkIt)
312 aBuilder.Add(aNewWire, *aSkIt);
314 // check the wire is empty
315 anExp.Init(aNewWire);
317 aBuilder.Add(aNewFace, aNewWire);
321 aBuilder.Add(aResult, aNewFace);
322 std::shared_ptr<GeomAPI_Shape> aResFace(new GeomAPI_Shape);
323 aResFace->setImpl(new TopoDS_Face(aNewFace));
324 myResultFaces.push_back(aResFace);
328 GeomShapePtr aResShape(new GeomAPI_Shape);
329 aResShape->setImpl(new TopoDS_Shape(aResult));
334 GeomAlgoAPI_SketchBuilder::GeomAlgoAPI_SketchBuilder(
335 const std::shared_ptr<GeomAPI_Pln>& thePlane,
336 const std::list<std::shared_ptr<GeomAPI_Shape> >& theEdges)
338 build(thePlane->location(), thePlane->xDirection(), thePlane->direction(), theEdges);
341 GeomAlgoAPI_SketchBuilder::GeomAlgoAPI_SketchBuilder(
342 const std::shared_ptr<GeomAPI_Pnt>& theOrigin,
343 const std::shared_ptr<GeomAPI_Dir>& theDirX,
344 const std::shared_ptr<GeomAPI_Dir>& theNorm,
345 const std::shared_ptr<GeomAPI_Shape>& theWire)
347 std::shared_ptr<GeomAPI_PlanarEdges> aWire =
348 std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theWire);
350 // Filter wires, return only faces.
351 build(theOrigin, theDirX, theNorm, aWire->getEdges());
352 } else { // it may be only one circle
353 std::shared_ptr<GeomAPI_Edge> anEdge = std::dynamic_pointer_cast<GeomAPI_Edge>(theWire);
355 std::list<std::shared_ptr<GeomAPI_Shape> > aList;
356 aList.push_back(anEdge);
357 build(theOrigin, theDirX, theNorm, aList);