Salome HOME
6d0b9ad503f419f5f1f8c299970f44998049beaa
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_Fillet1D.cpp
1 // Copyright (C) 2020-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_Fillet1D.h>
21
22 #include <GeomAlgoAPI_Copy.h>
23 #include <GeomAlgoAPI_MapShapesAndAncestors.h>
24 #include <GeomAlgoAPI_ShapeTools.h>
25 #include <GeomAlgoAPI_CompoundBuilder.h>
26
27 #include <GeomAPI_Edge.h>
28 #include <GeomAPI_Pln.h>
29 #include <GeomAPI_Pnt.h>
30 #include <GeomAPI_Wire.h>
31 #include <GeomAPI_WireExplorer.h>
32 #include <GeomAPI_ShapeIterator.h>
33
34 #include <GEOMImpl_Fillet1d.hxx>
35
36 #include <BRep_Builder.hxx>
37 #include <BRepTools_WireExplorer.hxx>
38 #include <ShapeFix_Wire.hxx>
39 #include <TopoDS.hxx>
40 #include <TopoDS_Wire.hxx>
41
42 static GeomShapePtr convert(const TopoDS_Shape& theShape)
43 {
44   GeomShapePtr aNewShape(new GeomAPI_Shape);
45   aNewShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(theShape));
46   return aNewShape;
47 }
48
49 static void substituteNewEdge(GeomEdgePtr theEdge,
50     std::map<GeomShapePtr, ListOfShape, GeomAPI_Shape::Comparator>& theMap)
51 {
52   std::map<GeomShapePtr, ListOfShape, GeomAPI_Shape::Comparator>::iterator anIt = theMap.begin();
53   for (; anIt != theMap.end(); ++anIt) {
54     for (ListOfShape::iterator aEIt = anIt->second.begin(); aEIt != anIt->second.end(); ++aEIt)
55       if (theEdge->isEqual(*aEIt)) {
56         // substitute edge and stop iteration
57         *aEIt = theEdge;
58         return;
59       }
60   }
61 }
62
63
64 GeomAlgoAPI_Fillet1D::GeomAlgoAPI_Fillet1D(const GeomShapePtr& theBaseWire,
65                                            const ListOfShape&  theFilletVertices,
66                                            const double        theFilletRadius)
67 {
68   build(theBaseWire, theFilletVertices, theFilletRadius);
69 }
70
71 void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseShape,
72                                  const ListOfShape&  theFilletVertices,
73                                  const double        theRadius)
74 {
75   if (!theBaseShape.get() || theFilletVertices.empty() || theRadius < 0.)
76     return;
77
78   myFailedVertices.clear();
79   GeomShapePtr aShape;
80
81   if (theBaseShape->isWire())
82     aShape = buildWire(theBaseShape, theFilletVertices, theRadius);
83   else if (theBaseShape->isCompound()) {
84     std::list<GeomShapePtr> aShapes;
85     for (GeomAPI_ShapeIterator it (theBaseShape); it.more(); it.next()) {
86       GeomShapePtr aSubShape = it.current();
87       if (aSubShape->isWire()) {
88         // get only fillet vertices of current wire
89         ListOfShape aFilletVertices;
90         std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aFilletVerticesSet
91           (theFilletVertices.begin(), theFilletVertices.end());
92         for (GeomAPI_WireExplorer anExp(aSubShape->wire()); anExp.more(); anExp.next()) {
93           if (aFilletVerticesSet.find(anExp.currentVertex()) != aFilletVerticesSet.end())
94             aFilletVertices.push_back(anExp.currentVertex());
95         }
96
97         GeomShapePtr aShape_i = buildWire(aSubShape, aFilletVertices, theRadius);
98         if (aShape_i.get() != NULL)
99           aShapes.push_back(aShape_i);
100         else
101           aShapes.push_back(aSubShape);
102       }
103       else {
104         setDone(false);
105         myError = "Input shape for fillet is neither a wire nor a compound of wires";
106         return;
107       }
108     }
109     aShape = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
110     myModified[theBaseShape].push_back(aShape);
111   } else {
112     setDone(false);
113     myError = "Input shape for fillet is neither a wire nor a compound";
114     return;
115   }
116
117   setShape(aShape);
118   setDone(myFailedVertices.empty());
119 }
120
121 GeomShapePtr GeomAlgoAPI_Fillet1D::buildWire(const GeomShapePtr& theBaseWire,
122                                              const ListOfShape&  theFilletVertices,
123                                              const double        theRadius)
124 {
125   std::shared_ptr<GeomAPI_Shape> aShape;
126   if (!theBaseWire.get() || theFilletVertices.empty() || theRadius < 0.)
127     return aShape;
128
129   // store all edges of a base wire as modified, because they will be rebuild by ShapeFix
130   for (GeomAPI_WireExplorer aWExp(theBaseWire->wire()); aWExp.more(); aWExp.next()) {
131     GeomShapePtr aCurrent = aWExp.current();
132     GeomAlgoAPI_Copy aCopy(aCurrent);
133     myModified[aCurrent].push_back(aCopy.shape());
134   }
135
136   GeomAlgoAPI_MapShapesAndAncestors aMapVE(theBaseWire, GeomAPI_Shape::VERTEX,
137                                                         GeomAPI_Shape::EDGE);
138
139   for (ListOfShape::const_iterator aVIt = theFilletVertices.begin();
140        aVIt != theFilletVertices.end(); ++aVIt) {
141     // get edges to perform fillet
142     MapShapeToShapes::const_iterator aVE = aMapVE.map().find(*aVIt);
143     if (aVE == aMapVE.map().end())
144       continue;
145     ListOfShape anEdges;
146     for (SetOfShapes::const_iterator aEIt = aVE->second.begin();
147          aEIt != aVE->second.end(); ++aEIt) {
148       ListOfShape aNewEdges;
149       modified(*aEIt, aNewEdges);
150       if (aNewEdges.empty())
151         anEdges.push_back(*aEIt);
152       else
153         anEdges.insert(anEdges.end(), aNewEdges.begin(), aNewEdges.end());
154     }
155
156     GeomPlanePtr aPlane = GeomAlgoAPI_ShapeTools::findPlane(anEdges);
157     if (!aPlane)
158       return aShape; // non-planar edges
159
160     TopoDS_Edge anEdge1 = TopoDS::Edge(anEdges.front()->impl<TopoDS_Shape>());
161     TopoDS_Edge anEdge2 = TopoDS::Edge(anEdges.back()->impl<TopoDS_Shape>());
162
163     // create fillet builder
164     GEOMImpl_Fillet1d aFilletBuilder(anEdge1, anEdge2, aPlane->impl<gp_Pln>());
165     bool isOk = aFilletBuilder.Perform(theRadius);
166     TopoDS_Edge aFilletEdge;
167     if (isOk) {
168       GeomPointPtr aPoint = aVE->first->vertex()->point();
169       aFilletEdge = aFilletBuilder.Result(aPoint->impl<gp_Pnt>(), anEdge1, anEdge2);
170       isOk = !aFilletEdge.IsNull();
171     }
172
173     if (!isOk) {
174       // something gone wrong and the fillet edge is not constructed,
175       // just store the failed vertex and continue
176       myFailedVertices.push_back(*aVIt);
177       continue;
178     }
179
180     // store modified shapes
181     myGenerated[aVE->first].push_back(convert(aFilletEdge));
182     SetOfShapes::const_iterator aEIt = aVE->second.begin();
183     myModified[*aEIt].clear();
184     myModified[*aEIt].push_back(convert(anEdge1));
185     myModified[*(++aEIt)].clear();
186     myModified[*aEIt].push_back(convert(anEdge2));
187   }
188
189   // compose a new wire
190   TopoDS_Wire aNewWire;
191   BRep_Builder aBuilder;
192   aBuilder.MakeWire(aNewWire);
193   GeomWirePtr aBaseWire = theBaseWire->wire();
194   GeomAPI_WireExplorer aWExp(aBaseWire);
195   GeomShapePtr aBaseFirstEdge = aWExp.current();
196   for (; aWExp.more(); aWExp.next()) {
197     ListOfShape aNewEdges;
198     modified(aWExp.current(), aNewEdges);
199     if (aNewEdges.empty())
200       aNewEdges.push_back(aWExp.current());
201     for (ListOfShape::iterator anIt = aNewEdges.begin(); anIt != aNewEdges.end(); ++anIt)
202       aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl<TopoDS_Shape>()));
203
204     ListOfShape aNewEdges1;
205     generated(aWExp.currentVertex(), aNewEdges1);
206     for (ListOfShape::iterator anIt = aNewEdges1.begin(); anIt != aNewEdges1.end(); ++anIt)
207       aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl<TopoDS_Shape>()));
208   }
209   // fix the wire connectivity
210   ShapeFix_Wire aFixWire;
211   aFixWire.Load(aNewWire);
212   aFixWire.ClosedWireMode() = aBaseWire->isClosed();
213   aFixWire.FixReorder();
214   aNewWire = aFixWire.WireAPIMake();
215   if (aNewWire.IsNull()) {
216     myFailedVertices = theFilletVertices;
217     return aShape;
218   }
219
220   // update the map of modified shapes, because the edges are changed by ShapeFix
221   for (BRepTools_WireExplorer anExp(aNewWire); anExp.More(); anExp.Next()) {
222     GeomEdgePtr aCurrent(new GeomAPI_Edge);
223     aCurrent->setImpl(new TopoDS_Edge(anExp.Current()));
224     substituteNewEdge(aCurrent, myGenerated);
225     substituteNewEdge(aCurrent, myModified);
226   }
227
228   // rebuild the wire once again to get the first edge of fillet wire correspond
229   // to the first edge of original wire
230   TopoDS_Edge aFirstEdge = TopoDS::Edge(aBaseFirstEdge->impl<TopoDS_Shape>());
231   ListOfShape aNewEdges;
232   modified(aBaseFirstEdge, aNewEdges);
233   if (!aNewEdges.empty())
234     aFirstEdge = TopoDS::Edge(aNewEdges.front()->impl<TopoDS_Shape>());
235   TopTools_ListOfShape aKeptForEnd;
236   BRepTools_WireExplorer aNewExp(aNewWire);
237   for (; aNewExp.More(); aNewExp.Next())
238     if (aNewExp.Current().IsEqual(aFirstEdge))
239       break;
240   if (aNewExp.More()) {
241     TopoDS_Wire aReorderedWire;
242     aBuilder.MakeWire(aReorderedWire);
243     for (; aNewExp.More(); aNewExp.Next())
244       aBuilder.Add(aReorderedWire, aNewExp.Current());
245     for (aNewExp.Init(aNewWire); aNewExp.More(); aNewExp.Next()) {
246       if (aNewExp.Current().IsEqual(aFirstEdge))
247         break;
248       aBuilder.Add(aReorderedWire, aNewExp.Current());
249     }
250     aNewWire = aReorderedWire;
251   }
252
253   std::shared_ptr<GeomAPI_Shape> aResShape(new GeomAPI_Shape);
254   aResShape->setImpl(new TopoDS_Shape(aNewWire));
255   myModified[theBaseWire].push_back(aResShape);
256   return aResShape;
257 }
258
259 void GeomAlgoAPI_Fillet1D::generated(const GeomShapePtr theOldShape,
260                                      ListOfShape& theNewShapes)
261 {
262   MapModified::iterator aFound = myGenerated.find(theOldShape);
263   if (aFound != myGenerated.end())
264     theNewShapes = aFound->second;
265 }
266
267 void GeomAlgoAPI_Fillet1D::modified(const GeomShapePtr theOldShape,
268                                      ListOfShape& theNewShapes)
269 {
270   MapModified::iterator aFound = myModified.find(theOldShape);
271   if (aFound != myModified.end())
272     theNewShapes = aFound->second;
273 }