Salome HOME
Continue fixing regression with incorrect order of inner wires (sort inner wires...
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_SketchBuilder.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <GeomAlgoAPI_SketchBuilder.h>
21 #include <GeomAPI_PlanarEdges.h>
22
23 #include <GeomAPI_Pln.h>
24
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>
32 #include <TopExp.hxx>
33 #include <TopExp_Explorer.hxx>
34 #include <TopoDS.hxx>
35 #include <TopoDS_Edge.hxx>
36 #include <TopTools_ListIteratorOfListOfShape.hxx>
37 #include <GProp_GProps.hxx>
38 #include <BRepGProp.hxx>
39
40 #include <list>
41 #include <cmath>
42 #include <algorithm>
43
44 static TopoDS_Vertex findStartVertex(const TopoDS_Shape& theShape)
45 {
46   static const double aTol = Precision::PConfusion();
47
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;
52   gp_Pnt aCurrentPnt;
53
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)) {
61       aStart = aCurrent;
62       aStartPnt = aCurrentPnt;
63     }
64   }
65   return aStart;
66 }
67
68 static TopoDS_Vertex findStartVertex(const TopoDS_Wire& theWire, const TopoDS_Face& theFace,
69     const std::list<std::shared_ptr<GeomAPI_Shape> >& theInitialShapes)
70 {
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)
78       continue;
79
80     double aFirst, aLast;
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();
84
85     BRepTools_WireExplorer anExp(theWire, theFace);
86     for (; anExp.More(); anExp.Next()) {
87       const TopoDS_Edge& aShapeEdge = anExp.Current();
88       double aF, aL;
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();
92
93       if (aCurve != aShapeCurve)
94         continue;
95
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;
100     }
101   }
102
103   // start vertex is not found, use algorithm to search vertex with the greatest coordinates
104   return findStartVertex(theWire);
105 }
106
107 // returns true if the first shape must be located earlier than the second
108 bool isFirst(const TopoDS_Shape& theFirst, const TopoDS_Shape& theSecond,
109   NCollection_DataMap<TopoDS_Shape, NCollection_Array1<int> >& theAreaToIndex,
110   const NCollection_DataMap<Handle(Geom_Curve), int>& theCurveToIndex)
111 {
112   // fill theAreaToIndex for both shapes if needed
113   for(int aShapeNum = 1; aShapeNum <= 2; aShapeNum++) {
114     TopoDS_Shape aShape = aShapeNum == 1 ? theFirst : theSecond;
115     if (!theAreaToIndex.IsBound(aShape)) { // fill the list of curve indices
116       NCollection_List<int> aNewList;
117       TopExp_Explorer anEdgesExp(aShape, TopAbs_EDGE);
118       for (; anEdgesExp.More(); anEdgesExp.Next()) {
119         double aFirst, aLast;
120         Handle(Geom_Curve) aCurve = BRep_Tool::Curve(
121           TopoDS::Edge(anEdgesExp.Current()), aFirst, aLast);
122         if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
123           aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
124         if (theCurveToIndex.IsBound(aCurve)) {
125           aNewList.Append(theCurveToIndex.Find(aCurve));
126         }
127       }
128       if (aNewList.Extent()) {
129         NCollection_Array1<int> aNewArray(1, aNewList.Extent());
130         NCollection_List<int>::Iterator aListIter(aNewList);
131         for (int anIndex = 1; aListIter.More(); aListIter.Next(), anIndex++) {
132           aNewArray.SetValue(anIndex, aListIter.Value());
133         }
134         std::sort(aNewArray.begin(), aNewArray.end());
135         theAreaToIndex.Bind(aShape, aNewArray);
136       }
137     }
138   }
139   bool isFirst;
140   bool aGeomCompare = !theAreaToIndex.IsBound(theFirst) || !theAreaToIndex.IsBound(theSecond);
141   if (!aGeomCompare) {
142     // compare lists of indices one by one to find chich list indices are lower
143     NCollection_Array1<int>::Iterator aFirstList(theAreaToIndex.ChangeFind(theFirst));
144     NCollection_Array1<int>::Iterator aSecondList(theAreaToIndex.ChangeFind(theSecond));
145     for (; aFirstList.More() && aSecondList.More(); aFirstList.Next(), aSecondList.Next()) {
146       if (aFirstList.Value() < aSecondList.Value()) return true;
147       if (aFirstList.Value() > aSecondList.Value()) return false;
148     }
149     aGeomCompare = !aFirstList.More() && !aSecondList.More();
150     isFirst = !aFirstList.More();
151   } else {
152     isFirst = !theAreaToIndex.IsBound(theFirst);
153   }
154   // if faces are identical by curves names (circle splitted by line in seam-point), use parameters
155   if (aGeomCompare) {
156     GProp_GProps aGProps;
157     BRepGProp::SurfaceProperties(theFirst, aGProps);
158     gp_Pnt aCentre1 = aGProps.CentreOfMass();
159     BRepGProp::SurfaceProperties(theSecond, aGProps);
160     gp_Pnt aCentre2 = aGProps.CentreOfMass();
161     return aCentre1.X() + aCentre1.Y() + aCentre1.Z() < aCentre2.X() + aCentre2.Y() + aCentre2.Z();
162   }
163   // if in first list there is no elements left, it is the first
164   return isFirst;
165 }
166
167 // sorts faces (in theAreas list) to make persistent order: by initial shapes edges
168 static void sortAreas(TopTools_ListOfShape& theAreas,
169   const std::list<std::shared_ptr<GeomAPI_Shape> >& theInitialShapes)
170 {
171   // collect indices of all edges to operate them quickly
172   NCollection_DataMap<Handle(Geom_Curve), int> aCurveToIndex; // curve -> index in initial shapes
173   std::list<std::shared_ptr<GeomAPI_Shape> >::const_iterator aFeatIt = theInitialShapes.begin();
174   for (int anIndex = 0; aFeatIt != theInitialShapes.end(); aFeatIt++) {
175     std::shared_ptr<GeomAPI_Shape> aShape(*aFeatIt);
176     const TopoDS_Edge& anEdge = aShape->impl<TopoDS_Edge>();
177     if (anEdge.ShapeType() != TopAbs_EDGE)
178       continue;
179
180     double aFirst, aLast;
181     Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
182     if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
183       aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
184     if (!aCurveToIndex.IsBound(aCurve))
185       aCurveToIndex.Bind(aCurve, anIndex++);
186   }
187   // map from area to the most first indices of curves (to compare) in it
188   NCollection_DataMap<TopoDS_Shape, NCollection_Array1<int> > anAreaToIndex;
189   // sort areas
190   TopTools_ListOfShape::Iterator anArea1(theAreas);
191   for(; anArea1.More(); anArea1.Next()) {
192     TopTools_ListOfShape::Iterator anArea2 = anArea1;
193     for(anArea2.Next(); anArea2.More(); anArea2.Next()) {
194       if (!isFirst(anArea1.Value(), anArea2.Value(), anAreaToIndex, aCurveToIndex)) { // exchange
195         TopoDS_Shape aTmp = anArea1.Value();
196         anArea1.ChangeValue() = anArea2.Value();
197         anArea2.ChangeValue() = aTmp;
198       }
199     }
200   }
201 }
202
203 void GeomAlgoAPI_SketchBuilder::build(
204     const std::shared_ptr<GeomAPI_Pnt>& theOrigin,
205     const std::shared_ptr<GeomAPI_Dir>& theDirX,
206     const std::shared_ptr<GeomAPI_Dir>& theNorm,
207     const std::list<std::shared_ptr<GeomAPI_Shape> >& theEdges)
208 {
209   myResultFaces.clear();
210   setDone(false);
211   if (theEdges.empty())
212     return;
213
214   BRep_Builder aBuilder;
215   // Planar face, where the sketch was built
216   Handle(Geom_Surface) aPlane(new Geom_Plane(theOrigin->impl<gp_Pnt>(), theNorm->impl<gp_Dir>()));
217   TopoDS_Face aPlnFace;
218   aBuilder.MakeFace(aPlnFace, aPlane, Precision::Confusion());
219
220   // Use General Fuse algorithm to prepare all subfaces, bounded by given list of edges
221   BOPAlgo_Builder* aBB = new BOPAlgo_Builder;
222   aBB->AddArgument(aPlnFace);
223
224   setImpl(aBB);
225   setBuilderType(OCCT_BOPAlgo_Builder);
226
227   NCollection_List<TopoDS_Shape> anEdges;
228   NCollection_List<TopoDS_Shape>::Iterator aShapeIt;
229   std::list<std::shared_ptr<GeomAPI_Shape> >::const_iterator aFeatIt = theEdges.begin();
230   for (; aFeatIt != theEdges.end(); aFeatIt++) {
231     std::shared_ptr<GeomAPI_Shape> aShape(*aFeatIt);
232     const TopoDS_Edge& anEdge = aShape->impl<TopoDS_Edge>();
233     if (anEdge.ShapeType() == TopAbs_EDGE)
234       aBB->AddArgument(anEdge);
235   }
236   aBB->Perform();
237   if (aBB->HasErrors())
238     return;
239
240   TopoDS_Compound aResult;
241   aBuilder.MakeCompound(aResult);
242
243   // Collect faces
244   TopTools_ListOfShape anAreas = aBB->Modified(aPlnFace);
245   sortAreas(anAreas, theEdges); // sort faces by the edges in them
246   TopTools_ListIteratorOfListOfShape anIt(anAreas);
247   for (; anIt.More(); anIt.Next()) {
248     TopoDS_Face aFace = TopoDS::Face(anIt.Value());
249     // avoid infinite faces
250     BRepTopAdaptor_FClass2d aFClass(aFace, Precision::Confusion());
251     if (aFClass.PerformInfinitePoint() == TopAbs_IN)
252       continue;
253
254     // rebuild face
255     TopoDS_Face aNewFace;
256     aBuilder.MakeFace(aNewFace, aPlane, Precision::Confusion());
257
258     // sort inner wires according to the original edges as well as faces
259     TopTools_ListOfShape aWires;
260     TopExp_Explorer aWireExp(aFace, TopAbs_WIRE);
261     for (; aWireExp.More(); aWireExp.Next())
262       aWires.Append(aWireExp.Current());
263     if (aWires.Size() > 2) {
264       TopoDS_Shape anOuterWire = aWires.First();
265       aWires.RemoveFirst();
266       sortAreas(aWires, theEdges);
267       aWires.Prepend(anOuterWire);
268     }
269
270     // iterate on wires
271     for (TopTools_ListIteratorOfListOfShape aWIt(aWires); aWIt.More(); aWIt.Next()) {
272       TopoDS_Wire aWire = TopoDS::Wire(aWIt.Value());
273
274       // to make faces equal on different platforms, we will find
275       // a vertex lying on an edge with the lowest index in the list of initial edges
276       TopoDS_Vertex aStartVertex = findStartVertex(aWire, aFace, theEdges);
277
278       TopoDS_Wire aNewWire;
279       aBuilder.MakeWire(aNewWire);
280       std::list<TopoDS_Edge> aSkippedEdges;
281       bool aStartFound = false;
282
283       // remove internal edges from faces and make wire start from found vertex
284       BRepTools_WireExplorer anExp(aWire, aFace);
285       for (; anExp.More(); anExp.Next()) {
286         if (anExp.Current().Orientation() == TopAbs_INTERNAL)
287           continue;
288         if (!aStartFound) {
289           const TopoDS_Edge& anEdge = anExp.Current();
290           TopoDS_Vertex aV1, aV2;
291           TopExp::Vertices(anEdge, aV1, aV2, Standard_True);
292           if (aV1.IsSame(aStartVertex) == Standard_True)
293             aStartFound = true;
294           else
295             aSkippedEdges.push_back(anEdge);
296         }
297         if (aStartFound)
298           aBuilder.Add(aNewWire, anExp.Current());
299       }
300       // add skipped edges to the end of wire
301       std::list<TopoDS_Edge>::const_iterator aSkIt = aSkippedEdges.begin();
302       for (; aSkIt != aSkippedEdges.end(); ++aSkIt)
303         aBuilder.Add(aNewWire, *aSkIt);
304
305       // check the wire is empty
306       anExp.Init(aNewWire);
307       if (anExp.More())
308         aBuilder.Add(aNewFace, aNewWire);
309     }
310
311     // store face
312     aBuilder.Add(aResult, aNewFace);
313     std::shared_ptr<GeomAPI_Shape> aResFace(new GeomAPI_Shape);
314     aResFace->setImpl(new TopoDS_Face(aNewFace));
315     myResultFaces.push_back(aResFace);
316   }
317
318   // update results
319   GeomShapePtr aResShape(new GeomAPI_Shape);
320   aResShape->setImpl(new TopoDS_Shape(aResult));
321   setShape(aResShape);
322   setDone(true);
323 }
324
325 GeomAlgoAPI_SketchBuilder::GeomAlgoAPI_SketchBuilder(
326   const std::shared_ptr<GeomAPI_Pln>& thePlane,
327   const std::list<std::shared_ptr<GeomAPI_Shape> >& theEdges)
328 {
329   build(thePlane->location(), thePlane->xDirection(), thePlane->direction(), theEdges);
330 }
331
332 GeomAlgoAPI_SketchBuilder::GeomAlgoAPI_SketchBuilder(
333     const std::shared_ptr<GeomAPI_Pnt>& theOrigin,
334     const std::shared_ptr<GeomAPI_Dir>& theDirX,
335     const std::shared_ptr<GeomAPI_Dir>& theNorm,
336     const std::shared_ptr<GeomAPI_Shape>& theWire)
337 {
338   std::shared_ptr<GeomAPI_PlanarEdges> aWire =
339     std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theWire);
340   if(aWire) {
341     // Filter wires, return only faces.
342     build(theOrigin, theDirX, theNorm, aWire->getEdges());
343   } else { // it may be only one circle
344     std::shared_ptr<GeomAPI_Edge> anEdge = std::dynamic_pointer_cast<GeomAPI_Edge>(theWire);
345     if (anEdge) {
346       std::list<std::shared_ptr<GeomAPI_Shape> > aList;
347       aList.push_back(anEdge);
348       build(theOrigin, theDirX, theNorm, aList);
349     }
350   }
351 }