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