]> SALOME platform Git repositories - modules/shaper.git/blob - src/GeomAlgoAPI/GeomAlgoAPI_SketchBuilder.cpp
Salome HOME
f7f3b937515721eef1db84d9ad2c6667359b09c4
[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 static double averageValue(const NCollection_Array1<int>& theArray)
108 {
109   double aSum = 0.0;
110   for (NCollection_Array1<int>::Iterator anIt(theArray); anIt.More(); anIt.Next())
111     aSum += (double)anIt.Value();
112   return aSum / theArray.Size();
113 }
114
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)
119 {
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));
134         }
135       }
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());
141         }
142         std::sort(aNewArray.begin(), aNewArray.end());
143         theAreaToIndex.Bind(aShape, aNewArray);
144       }
145     }
146   }
147   bool isFirst;
148   bool aGeomCompare = !theAreaToIndex.IsBound(theFirst) || !theAreaToIndex.IsBound(theSecond);
149   if (!aGeomCompare) {
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)
157         return true;
158       else if (aFirstMiddle > (double)aSecondList.Last())
159         return false;
160     }
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;
168     }
169     aGeomCompare = !aFirstListIt.More() && !aSecondListIt.More();
170     isFirst = !aFirstListIt.More();
171   } else {
172     isFirst = !theAreaToIndex.IsBound(theFirst);
173   }
174   // if faces are identical by curves names (circle splitted by line in seam-point), use parameters
175   if (aGeomCompare) {
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();
182   }
183   // if in first list there is no elements left, it is the first
184   return isFirst;
185 }
186
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)
190 {
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)
198       continue;
199
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);
206   }
207   // map from area to the most first indices of curves (to compare) in it
208   NCollection_DataMap<TopoDS_Shape, NCollection_Array1<int> > anAreaToIndex;
209   // sort areas
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;
218       }
219     }
220   }
221 }
222
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)
228 {
229   myResultFaces.clear();
230   setDone(false);
231   if (theEdges.empty())
232     return;
233
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());
239
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);
243
244   setImpl(aBB);
245   setBuilderType(OCCT_BOPAlgo_Builder);
246
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);
255   }
256   aBB->Perform();
257   if (aBB->HasErrors())
258     return;
259
260   TopoDS_Compound aResult;
261   aBuilder.MakeCompound(aResult);
262
263   // Collect faces
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)
272       continue;
273
274     // rebuild face
275     TopoDS_Face aNewFace;
276     aBuilder.MakeFace(aNewFace, aPlane, Precision::Confusion());
277
278     // iterate on wires
279     TopExp_Explorer aWireExp(aFace, TopAbs_WIRE);
280     for (; aWireExp.More(); aWireExp.Next()) {
281       TopoDS_Wire aWire = TopoDS::Wire(aWireExp.Current());
282
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);
286
287       TopoDS_Wire aNewWire;
288       aBuilder.MakeWire(aNewWire);
289       std::list<TopoDS_Edge> aSkippedEdges;
290       bool aStartFound = false;
291
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)
296           continue;
297         if (!aStartFound) {
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)
302             aStartFound = true;
303           else
304             aSkippedEdges.push_back(anEdge);
305         }
306         if (aStartFound)
307           aBuilder.Add(aNewWire, anExp.Current());
308       }
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);
313
314       // check the wire is empty
315       anExp.Init(aNewWire);
316       if (anExp.More())
317         aBuilder.Add(aNewFace, aNewWire);
318     }
319
320     // store face
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);
325   }
326
327   // update results
328   GeomShapePtr aResShape(new GeomAPI_Shape);
329   aResShape->setImpl(new TopoDS_Shape(aResult));
330   setShape(aResShape);
331   setDone(true);
332 }
333
334 GeomAlgoAPI_SketchBuilder::GeomAlgoAPI_SketchBuilder(
335   const std::shared_ptr<GeomAPI_Pln>& thePlane,
336   const std::list<std::shared_ptr<GeomAPI_Shape> >& theEdges)
337 {
338   build(thePlane->location(), thePlane->xDirection(), thePlane->direction(), theEdges);
339 }
340
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)
346 {
347   std::shared_ptr<GeomAPI_PlanarEdges> aWire =
348     std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theWire);
349   if(aWire) {
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);
354     if (anEdge) {
355       std::list<std::shared_ptr<GeomAPI_Shape> > aList;
356       aList.push_back(anEdge);
357       build(theOrigin, theDirX, theNorm, aList);
358     }
359   }
360 }