Salome HOME
Issue #3222: 1d fillet
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_Fillet1D.cpp
1 // Copyright (C) 2020  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_Fillet1D.h>
21 #include <GeomAlgoAPI_MapShapesAndAncestors.h>
22 #include <GeomAlgoAPI_ShapeTools.h>
23
24 #include <GeomAPI_Edge.h>
25 #include <GeomAPI_Pln.h>
26 #include <GeomAPI_Pnt.h>
27 #include <GeomAPI_Wire.h>
28 #include <GeomAPI_WireExplorer.h>
29
30 #include <GEOMImpl_Fillet1d.hxx>
31
32 #include <BRep_Builder.hxx>
33 #include <BRepTools_WireExplorer.hxx>
34 #include <ShapeFix_Wire.hxx>
35 #include <TopoDS.hxx>
36 #include <TopoDS_Wire.hxx>
37
38 static GeomShapePtr convert(const TopoDS_Shape& theShape)
39 {
40   GeomShapePtr aNewShape(new GeomAPI_Shape);
41   aNewShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(theShape));
42   return aNewShape;
43 }
44
45 static void substituteNewEdge(GeomEdgePtr theEdge,
46     std::map<GeomShapePtr, ListOfShape, GeomAPI_Shape::Comparator>& theMap)
47 {
48   std::map<GeomShapePtr, ListOfShape, GeomAPI_Shape::Comparator>::iterator anIt = theMap.begin();
49   for (; anIt != theMap.end(); ++anIt) {
50     for (ListOfShape::iterator aEIt = anIt->second.begin(); aEIt != anIt->second.end(); ++aEIt)
51       if (theEdge->isEqual(*aEIt)) {
52         // substitute edge and stop iteration
53         *aEIt = theEdge;
54         return;
55       }
56   }
57 }
58
59
60 GeomAlgoAPI_Fillet1D::GeomAlgoAPI_Fillet1D(const GeomShapePtr& theBaseWire,
61                                            const ListOfShape&  theFilletVertices,
62                                            const double        theFilletRadius)
63 {
64   build(theBaseWire, theFilletVertices, theFilletRadius);
65 }
66
67 void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseWire,
68                                  const ListOfShape&  theFilletVertices,
69                                  const double        theRadius)
70 {
71   if (!theBaseWire || theFilletVertices.empty() || theRadius < 0.)
72     return;
73
74   GeomAlgoAPI_MapShapesAndAncestors aMapVE(theBaseWire, GeomAPI_Shape::VERTEX,
75                                                         GeomAPI_Shape::EDGE);
76
77   for (ListOfShape::const_iterator aVIt = theFilletVertices.begin();
78        aVIt != theFilletVertices.end(); ++aVIt) {
79     // get edges to perform fillet
80     MapShapeToShapes::const_iterator aVE = aMapVE.map().find(*aVIt);
81     if (aVE == aMapVE.map().end())
82       continue;
83     ListOfShape anEdges;
84     for (SetOfShapes::const_iterator aEIt = aVE->second.begin();
85          aEIt != aVE->second.end(); ++aEIt) {
86       ListOfShape aNewEdges;
87       modified(*aEIt, aNewEdges);
88       if (aNewEdges.empty())
89         anEdges.push_back(*aEIt);
90       else
91         anEdges.insert(anEdges.end(), aNewEdges.begin(), aNewEdges.end());
92     }
93
94     GeomPlanePtr aPlane = GeomAlgoAPI_ShapeTools::findPlane(anEdges);
95     if (!aPlane)
96       return; // non-planar edges
97
98     TopoDS_Edge anEdge1 = TopoDS::Edge(anEdges.front()->impl<TopoDS_Shape>());
99     TopoDS_Edge anEdge2 = TopoDS::Edge(anEdges.back()->impl<TopoDS_Shape>());
100
101     // create fillet builder
102     GEOMImpl_Fillet1d aFilletBuilder(anEdge1, anEdge2, aPlane->impl<gp_Pln>());
103     if (!aFilletBuilder.Perform(theRadius))
104       return; // fillet is failed, no way to continue
105
106     GeomPointPtr aPoint = aVE->first->vertex()->point();
107     TopoDS_Edge aFilletEdge = aFilletBuilder.Result(aPoint->impl<gp_Pnt>(), anEdge1, anEdge2);
108
109     // store modified shapes
110     myGenerated[aVE->first].push_back(convert(aFilletEdge));
111     SetOfShapes::const_iterator aEIt = aVE->second.begin();
112     myModified[*aEIt].clear();
113     myModified[*aEIt].push_back(convert(anEdge1));
114     myModified[*(++aEIt)].clear();
115     myModified[*aEIt].push_back(convert(anEdge2));
116   }
117
118   // compose a new wire
119   TopoDS_Wire aNewWire;
120   BRep_Builder aBuilder;
121   aBuilder.MakeWire(aNewWire);
122   GeomWirePtr aBaseWire = theBaseWire->wire();
123   GeomAPI_WireExplorer aWExp(aBaseWire);
124   GeomShapePtr aBaseFirstEdge = aWExp.current();
125   for (; aWExp.more(); aWExp.next()) {
126     ListOfShape aNewEdges;
127     modified(aWExp.current(), aNewEdges);
128     if (aNewEdges.empty())
129       aNewEdges.push_back(aWExp.current());
130     for (ListOfShape::iterator anIt = aNewEdges.begin(); anIt != aNewEdges.end(); ++anIt)
131       aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl<TopoDS_Shape>()));
132   }
133   for (MapModified::iterator aGenIt = myGenerated.begin(); aGenIt != myGenerated.end(); ++aGenIt) {
134     for (ListOfShape::iterator anIt = aGenIt->second.begin(); anIt != aGenIt->second.end(); ++anIt)
135       aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl<TopoDS_Shape>()));
136   }
137   // fix the wire connectivity
138   ShapeFix_Wire aFixWire;
139   aFixWire.Load(aNewWire);
140   aFixWire.ClosedWireMode() = aBaseWire->isClosed();
141   aFixWire.FixReorder();
142   aNewWire = aFixWire.WireAPIMake();
143   if (aNewWire.IsNull())
144     return;
145
146   // update the map of modified shapes, because the edges are changed by ShapeFix
147   for (BRepTools_WireExplorer anExp(aNewWire); anExp.More(); anExp.Next()) {
148     GeomEdgePtr aCurrent(new GeomAPI_Edge);
149     aCurrent->setImpl(new TopoDS_Edge(anExp.Current()));
150     substituteNewEdge(aCurrent, myGenerated);
151     substituteNewEdge(aCurrent, myModified);
152   }
153
154   // rebuild the wire once again to get the first edge of fillet wire correspond
155   // to the first edge of original wire
156   TopoDS_Edge aFirstEdge = TopoDS::Edge(aBaseFirstEdge->impl<TopoDS_Shape>());
157   ListOfShape aNewEdges;
158   modified(aBaseFirstEdge, aNewEdges);
159   if (!aNewEdges.empty())
160     aFirstEdge = TopoDS::Edge(aNewEdges.front()->impl<TopoDS_Shape>());
161   TopTools_ListOfShape aKeptForEnd;
162   BRepTools_WireExplorer aNewExp(aNewWire);
163   for (; aNewExp.More(); aNewExp.Next())
164     if (aNewExp.Current().IsEqual(aFirstEdge))
165       break;
166   if (aNewExp.More()) {
167     TopoDS_Wire aReorderedWire;
168     aBuilder.MakeWire(aReorderedWire);
169     for (; aNewExp.More(); aNewExp.Next())
170       aBuilder.Add(aReorderedWire, aNewExp.Current());
171     for (aNewExp.Init(aNewWire); aNewExp.More(); aNewExp.Next()) {
172       if (aNewExp.Current().IsEqual(aFirstEdge))
173         break;
174       aBuilder.Add(aReorderedWire, aNewExp.Current());
175     }
176     aNewWire = aReorderedWire;
177   }
178
179   std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape());
180   aShape->setImpl(new TopoDS_Shape(aNewWire));
181   myModified[theBaseWire].push_back(aShape);
182
183   setShape(aShape);
184   setDone(true);
185 }
186
187 void GeomAlgoAPI_Fillet1D::generated(const GeomShapePtr theOldShape,
188                                      ListOfShape& theNewShapes)
189 {
190   MapModified::iterator aFound = myGenerated.find(theOldShape);
191   if (aFound != myGenerated.end())
192     theNewShapes = aFound->second;
193 }
194
195 void GeomAlgoAPI_Fillet1D::modified(const GeomShapePtr theOldShape,
196                                      ListOfShape& theNewShapes)
197 {
198   MapModified::iterator aFound = myModified.find(theOldShape);
199   if (aFound != myModified.end())
200     theNewShapes = aFound->second;
201 }