Salome HOME
updated copyright message
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_WireBuilder.cpp
1 // Copyright (C) 2014-2023  CEA, EDF
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_WireBuilder.h"
21
22 #include <GeomAPI_Edge.h>
23 #include <GeomAPI_Pnt.h>
24 #include <GeomAPI_Vertex.h>
25 #include <GeomAPI_ShapeExplorer.h>
26
27 #include <BRep_Builder.hxx>
28 #include <BRep_Tool.hxx>
29 #include <BRepBuilderAPI_MakeWire.hxx>
30 #include <BRepTools_ReShape.hxx>
31 #include <Geom_Curve.hxx>
32 #include <Precision.hxx>
33 #include <TopoDS.hxx>
34 #include <TopoDS_Wire.hxx>
35 #include <TopExp.hxx>
36 #include <TopExp_Explorer.hxx>
37
38 #include <cmath>
39 #include <map>
40 #include <set>
41
42 class SetOfEdges
43 {
44   class DoubleCompare {
45   public:
46     bool operator() (const double d1, const double d2) const {
47       return d1 + Precision::Confusion() < d2;
48     }
49   };
50
51   typedef std::map<double, std::set<double, DoubleCompare>, DoubleCompare> ParamMap;
52   std::map<Handle(Geom_Curve), ParamMap> myShapes;
53
54 public:
55   bool add(const TopoDS_Shape& theEdge)
56   {
57     const TopoDS_Edge& anEdge = TopoDS::Edge(theEdge);
58     if (anEdge.IsNull())
59       return true;
60
61     double aFirst, aLast;
62     Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
63
64     bool isAdded = true;
65     std::map<Handle(Geom_Curve), ParamMap>::iterator
66         aFound = myShapes.find(aCurve);
67     if (aFound == myShapes.end())
68       myShapes[aCurve][aFirst].insert(aLast);
69     else {
70       ParamMap::iterator aFoundPar = aFound->second.find(aFirst);
71       if (aFoundPar == aFound->second.end())
72         aFound->second[aFirst].insert(aLast);
73       else if (aFoundPar->second.find(aLast) == aFoundPar->second.end())
74         aFoundPar->second.insert(aLast);
75       else
76         isAdded = false;
77     }
78     return isAdded;
79   }
80
81   static bool isEqual(const TopoDS_Shape& theShape1, const TopoDS_Shape& theShape2)
82   {
83     const TopoDS_Edge& anEdge1 = TopoDS::Edge(theShape1);
84     const TopoDS_Edge& anEdge2 = TopoDS::Edge(theShape2);
85     if (anEdge1.IsNull() || anEdge2.IsNull())
86       return false;
87
88     double aFirst1, aLast1;
89     Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(anEdge1, aFirst1, aLast1);
90     double aFirst2, aLast2;
91     Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(anEdge2, aFirst2, aLast2);
92     return aCurve1 == aCurve2 && fabs(aFirst1 - aFirst2) < Precision::Confusion() &&
93                                  fabs(aLast1 - aLast2) < Precision::Confusion();
94   }
95 };
96
97 static GeomShapePtr fromTopoDS(const TopoDS_Shape& theShape)
98 {
99   GeomShapePtr aResultShape(new GeomAPI_Shape());
100   aResultShape->setImpl(new TopoDS_Shape(theShape));
101   return aResultShape;
102 }
103
104 GeomAlgoAPI_WireBuilder::GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes,
105                                                  const bool theForceOpenWire)
106 {
107   TopTools_ListOfShape aListOfEdges;
108   SetOfEdges aProcessedEdges;
109
110   ListOfShape::const_iterator anIt = theShapes.cbegin();
111   for (; anIt != theShapes.cend(); ++anIt) {
112     TopoDS_Shape aShape = (*anIt)->impl<TopoDS_Shape>();
113     switch(aShape.ShapeType()) {
114       case TopAbs_EDGE: {
115         aShape.Orientation(TopAbs_FORWARD);
116         if (aProcessedEdges.add(aShape))
117           aListOfEdges.Append(aShape);
118         break;
119       }
120       case TopAbs_WIRE: {
121         for (TopExp_Explorer anExp(aShape, TopAbs_EDGE); anExp.More(); anExp.Next()) {
122           TopoDS_Shape anEdge = anExp.Current();
123           anEdge.Orientation(TopAbs_FORWARD);
124           // if the edge was already processed, remove it to keep original order of the current wire
125           if (!aProcessedEdges.add(anEdge)) {
126             for (TopTools_ListIteratorOfListOfShape aEIt(aListOfEdges); aEIt.More(); aEIt.Next())
127               if (SetOfEdges::isEqual(anEdge, aEIt.Value())) {
128                 aListOfEdges.Remove(aEIt);
129                 break;
130               }
131           }
132           aListOfEdges.Append(anEdge);
133         }
134         break;
135       }
136     default:
137       break;
138     }
139   }
140
141   bool isSplitWire = false;
142   gp_Pnt aSplitPoint;
143   if (theForceOpenWire && aListOfEdges.Size() > 1) {
144     // find a vertex to split the wire
145     TopoDS_Vertex V1[2];
146     TopExp::Vertices(TopoDS::Edge(aListOfEdges.First()), V1[0], V1[1]);
147     TopoDS_Vertex V2[2];
148     TopExp::Vertices(TopoDS::Edge(aListOfEdges.Last()), V2[0], V2[1]);
149     gp_Pnt P1[2] = { BRep_Tool::Pnt(V1[0]), BRep_Tool::Pnt(V1[1]) };
150     gp_Pnt P2[2] = { BRep_Tool::Pnt(V2[0]), BRep_Tool::Pnt(V2[1]) };
151     double Tol1[2] = { BRep_Tool::Tolerance(V1[0]), BRep_Tool::Tolerance(V1[1]) };
152     double Tol2[2] = { BRep_Tool::Tolerance(V2[0]), BRep_Tool::Tolerance(V2[1]) };
153     for (int i = 0; i < 2 && !isSplitWire; ++i)
154       for (int j = 0; j < 2 && !isSplitWire; ++j)
155         if (P1[i].Distance(P2[j]) < Max(Tol1[i], Tol2[j])) {
156           aSplitPoint = P1[i];
157           isSplitWire = true;
158         }
159   }
160
161   BRepBuilderAPI_MakeWire* aWireBuilder = new BRepBuilderAPI_MakeWire;
162   aWireBuilder->Add(aListOfEdges);
163   if (aWireBuilder->Error() == BRepBuilderAPI_WireDone) {
164     setImpl(aWireBuilder);
165     setBuilderType(OCCT_BRepBuilderAPI_MakeShape);
166
167     // split the result wire
168     TopoDS_Wire aWire = aWireBuilder->Wire();
169     if (isSplitWire && BRep_Tool::IsClosed(aWire)) {
170       TopoDS_Wire aNewWire;
171       BRep_Builder aBuilder;
172       aBuilder.MakeWire(aNewWire);
173       for (TopExp_Explorer anExp(aWire, TopAbs_EDGE); anExp.More(); anExp.Next()) {
174         TopoDS_Edge aNewCurrent = TopoDS::Edge(anExp.Current());
175         if (isSplitWire) {
176           bool isToReshape = false;
177           BRepTools_ReShape aReshape;
178           TopoDS_Vertex aVF, aVL;
179           TopExp::Vertices(aNewCurrent, aVF, aVL);
180           gp_Pnt aPF = BRep_Tool::Pnt(aVF);
181           double aTolF = BRep_Tool::Tolerance(aVF);
182           gp_Pnt aPL = BRep_Tool::Pnt(aVL);
183           double aTolL = BRep_Tool::Tolerance(aVL);
184           if (aSplitPoint.SquareDistance(aPF) < aTolF * aTolF) {
185             aReshape.Replace(aVF, aReshape.CopyVertex(aVF));
186             isToReshape = true;
187           }
188           else if (aSplitPoint.SquareDistance(aPL) < aTolL * aTolL) {
189             aReshape.Replace(aVL, aReshape.CopyVertex(aVL));
190             isToReshape = true;
191           }
192           if (isToReshape) {
193             aNewCurrent = TopoDS::Edge(aReshape.Apply(aNewCurrent));
194             isSplitWire = false; // no need to continue splitting
195           }
196         }
197         aBuilder.Add(aNewWire, aNewCurrent);
198       }
199       aWire = aNewWire;
200     }
201
202     // store generated/modified shapes
203     for (TopTools_ListOfShape::Iterator aBaseIt(aListOfEdges); aBaseIt.More(); aBaseIt.Next()) {
204       TopoDS_Edge aBaseCurrent = TopoDS::Edge(aBaseIt.Value());
205       Standard_Real aFirst, aLast;
206       Handle(Geom_Curve) aBaseCurve = BRep_Tool::Curve(aBaseCurrent, aFirst, aLast);
207
208       for (TopExp_Explorer anExp(aWire, TopAbs_EDGE); anExp.More(); anExp.Next()) {
209         TopoDS_Edge aNewCurrent = TopoDS::Edge(anExp.Current());
210         Handle(Geom_Curve) aNewCurve = BRep_Tool::Curve(aNewCurrent, aFirst, aLast);
211         if (aBaseCurve == aNewCurve) {
212           GeomShapePtr aBaseShape = fromTopoDS(aBaseCurrent);
213           GeomShapePtr aNewShape = fromTopoDS(aNewCurrent);
214           addGenerated(aBaseShape, aNewShape);
215           addModified(aBaseShape, aNewShape);
216         }
217       }
218     }
219
220     setShape(fromTopoDS(aWire));
221     setDone(true);
222   }
223 }
224
225 //=================================================================================================
226 GeomShapePtr GeomAlgoAPI_WireBuilder::wire(const ListOfShape& theShapes)
227 {
228   return GeomAlgoAPI_WireBuilder(theShapes).shape();
229 }
230
231 //=================================================================================================
232 bool GeomAlgoAPI_WireBuilder::isSelfIntersected(const GeomShapePtr& theWire)
233 {
234   // Collect edges.
235   ListOfShape anEdges;
236
237   GeomAPI_ShapeExplorer anExp(theWire, GeomAPI_Shape::EDGE);
238   for (; anExp.more(); anExp.next()) {
239     GeomShapePtr anEdge = anExp.current();
240     anEdges.push_back(anEdge);
241   }
242
243   // Check intersections between edges pair-wise
244   std::list<GeomShapePtr>::const_iterator anEdgesIt = anEdges.begin();
245   for (int i = 0; anEdgesIt != anEdges.end(); ++anEdgesIt, i++) {
246     GeomEdgePtr anEdge1(new GeomAPI_Edge(*anEdgesIt));
247
248     std::list<GeomShapePtr>::const_iterator anOtherEdgesIt = std::next(anEdgesIt);
249     for (int j = i + 1; anOtherEdgesIt != anEdges.end(); ++anOtherEdgesIt, j++) {
250       GeomEdgePtr anEdge2(new GeomAPI_Edge(*anOtherEdgesIt));
251       GeomShapePtr anInter = anEdge1->intersect(anEdge2);
252       if (!anInter.get()) {
253         continue;
254       }
255
256       bool isOk = false;
257
258       if (anInter->isVertex()) {
259         GeomVertexPtr aVertex(new GeomAPI_Vertex(anInter));
260         GeomPointPtr aPnt = aVertex->point();
261
262         GeomPointPtr aFirstPnt1 = anEdge1->orientation() == GeomAPI_Shape::FORWARD ?
263                                   anEdge1->firstPoint() : anEdge1->lastPoint();
264         GeomPointPtr aLastPnt1 = anEdge1->orientation() == GeomAPI_Shape::FORWARD ?
265                                  anEdge1->lastPoint() : anEdge1->firstPoint();
266         GeomPointPtr aFirstPnt2 = anEdge2->orientation() == GeomAPI_Shape::FORWARD ?
267                                   anEdge2->firstPoint() : anEdge2->lastPoint();
268         GeomPointPtr aLastPnt2 = anEdge2->orientation() == GeomAPI_Shape::FORWARD ?
269                                  anEdge2->lastPoint() : anEdge2->firstPoint();
270
271         GeomPointPtr aCommonEndPnt;
272         if (aFirstPnt1->isEqual(aLastPnt2)) {
273           aCommonEndPnt = aFirstPnt1;
274         } else if(aLastPnt1->isEqual(aFirstPnt2)) {
275           aCommonEndPnt = aLastPnt1;
276         }
277
278         isOk = aCommonEndPnt && aPnt->isEqual(aCommonEndPnt);
279       }
280
281       if (!isOk) {
282         return true;
283       }
284     }
285   }
286
287   return false;
288 }