Salome HOME
Copyright update 2022
[modules/shaper.git] / src / BuildPlugin / BuildPlugin_Filling.cpp
1 // Copyright (C) 2017-2022  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 "BuildPlugin_Filling.h"
21
22 #include <ModelAPI_AttributeBoolean.h>
23 #include <ModelAPI_AttributeDouble.h>
24 #include <ModelAPI_AttributeInteger.h>
25 #include <ModelAPI_AttributeSelectionList.h>
26 #include <ModelAPI_AttributeString.h>
27 #include <ModelAPI_ResultBody.h>
28
29 #include <GeomAlgoAPI_Copy.h>
30 #include <GeomAlgoAPI_Filling.h>
31 #include <GeomAlgoAPI_ShapeTools.h>
32 #include <GeomAlgoAPI_Tools.h>
33 #include <GeomAlgoAPI_WireBuilder.h>
34
35 #include <GeomAPI_Curve.h>
36 #include <GeomAPI_Pnt.h>
37 #include <GeomAPI_ShapeExplorer.h>
38 #include <GeomAPI_Wire.h>
39 #include <GeomAPI_WireExplorer.h>
40
41 #include <cmath>
42
43 struct FillingParameters
44 {
45   std::string method;
46   int minDegree;
47   int maxDegree;
48   int nbIter;
49   double tol2D;
50   double tol3D;
51   bool isApprox;
52 };
53
54 static bool isReverseClosedCurve(const GeomEdgePtr& theEdge1,
55                                  const GeomEdgePtr& theEdge2);
56 static bool isReverseOpenedCurve(const GeomEdgePtr& theEdge1,
57                                  const GeomEdgePtr& theEdge2,
58                                  const double theTolerance);
59 static void shiftStartPoint(GeomWirePtr& theWire,
60                             const GeomEdgePtr& theRefEdge,
61                             const double theTolerance);
62
63
64 //=================================================================================================
65 BuildPlugin_Filling::BuildPlugin_Filling()
66 {
67 }
68
69 //=================================================================================================
70 void BuildPlugin_Filling::initAttributes()
71 {
72   data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
73   data()->addAttribute(ADVANCED_OPTIONS_ID(), ModelAPI_AttributeString::typeId());
74   data()->addAttribute(METHOD_ID(), ModelAPI_AttributeString::typeId());
75   data()->addAttribute(MINIMAL_DEGREE_ID(), ModelAPI_AttributeInteger::typeId());
76   data()->addAttribute(MAXIMAL_DEGREE_ID(), ModelAPI_AttributeInteger::typeId());
77   data()->addAttribute(NUMBER_OF_ITERATIONS_ID(), ModelAPI_AttributeInteger::typeId());
78   data()->addAttribute(TOLERANCE_2D_ID(), ModelAPI_AttributeDouble::typeId());
79   data()->addAttribute(TOLERANCE_3D_ID(), ModelAPI_AttributeDouble::typeId());
80   data()->addAttribute(APPROXIMATION_ID(), ModelAPI_AttributeBoolean::typeId());
81
82   if (string(ADVANCED_OPTIONS_ID())->value().empty())
83     restoreDefaultParameters();
84 }
85
86 //=================================================================================================
87 void BuildPlugin_Filling::execute()
88 {
89   // get parameters of algorithm
90   FillingParameters aParameters;
91   aParameters.method = string(METHOD_ID())->value();
92   aParameters.minDegree = integer(MINIMAL_DEGREE_ID())->value();
93   aParameters.maxDegree = integer(MAXIMAL_DEGREE_ID())->value();
94   aParameters.nbIter = integer(NUMBER_OF_ITERATIONS_ID())->value();
95   aParameters.tol2D = real(TOLERANCE_2D_ID())->value();
96   aParameters.tol3D = real(TOLERANCE_3D_ID())->value();
97   aParameters.isApprox = boolean(APPROXIMATION_ID())->value();
98
99   if (aParameters.minDegree > aParameters.maxDegree) {
100     setError("Error: " + getKind() + " algorithm failed (max deg < min deg).");
101     return;
102   }
103
104   std::shared_ptr<GeomAlgoAPI_Filling> aFilling(
105       new GeomAlgoAPI_Filling(aParameters.minDegree, aParameters.maxDegree, aParameters.nbIter,
106                               aParameters.tol2D, aParameters.tol3D));
107
108   // get base objects list
109   AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID());
110   if (aSelectionList->size() <= 1) {
111     setError("Not enough objects is selected");
112     return;
113   }
114
115   // collect base shapes
116   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
117     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
118     GeomEdgePtr anEdge = toEdge(aSelection->value(), aParameters.method);
119     if (!anEdge)
120       return;
121     aFilling->add(anEdge);
122   }
123   myLastEdge = GeomEdgePtr();
124
125   // build result
126   aFilling->build(aParameters.isApprox);
127   std::string anError;
128   if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aFilling, getKind(), anError)) {
129     setError(anError);
130     removeResults(0);
131     return;
132   }
133
134   /// store result
135   GeomShapePtr aCreatedFace = aFilling->shape();
136   ResultBodyPtr aResultBody = document()->createBody(data());
137   aResultBody->store(aCreatedFace);
138   // store edges
139   int anEdgeInd = 0;
140   for(GeomAPI_ShapeExplorer anExp(aCreatedFace, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
141     GeomShapePtr anEdge = anExp.current();
142     aResultBody->generated(anEdge, "Edge_" + std::to_string((long long)anEdgeInd));
143   }
144   setResult(aResultBody, 0);
145 }
146
147 //=================================================================================================
148 void BuildPlugin_Filling::attributeChanged(const std::string& theID)
149 {
150   if (theID == ADVANCED_OPTIONS_ID() && string(ADVANCED_OPTIONS_ID())->value().empty()) {
151     // Advanced options flag just unchecked => restore default state of all parameters
152     restoreDefaultParameters();
153   }
154 }
155
156 //=================================================================================================
157 GeomEdgePtr BuildPlugin_Filling::toEdge(const GeomShapePtr& theShape, const std::string& theMethod)
158 {
159   static const double TOLERANCE = 1.e-7;
160
161   GeomEdgePtr anEdge;
162   switch (theShape->shapeType()) {
163   case GeomAPI_Shape::EDGE:
164     anEdge = GeomEdgePtr(new GeomAPI_Edge(GeomAlgoAPI_Copy(theShape).shape()));
165     break;
166   case GeomAPI_Shape::WIRE: {
167     GeomWirePtr aWire(new GeomAPI_Wire(theShape));
168     if (myLastEdge && theMethod == Method::AUTO_CORRECT_ORIENTATION())
169       shiftStartPoint(aWire, myLastEdge, TOLERANCE);
170     anEdge = GeomAlgoAPI_ShapeTools::wireToEdge(aWire);
171     break;
172   }
173   default:
174     break;
175   }
176
177   if (!anEdge || anEdge->empty()) {
178     static const std::string aFeatureError =
179         "Error: incorrect type of input feature (edges/wire are supported only).";
180     setError(aFeatureError);
181     return anEdge;
182   }
183
184   // correct edge orientation according to filling method
185   if (theMethod == Method::AUTO_CORRECT_ORIENTATION()) {
186     // check the distance to previous edge boundaries, reverse edge if necessary
187     bool isReverse = false;
188     if (myLastEdge) {
189       if (myLastEdge->firstPoint()->distance(myLastEdge->lastPoint()) < TOLERANCE &&
190           anEdge->firstPoint()->distance(anEdge->lastPoint()) < TOLERANCE)
191         isReverse = isReverseClosedCurve(myLastEdge, anEdge);
192       else
193         isReverse = isReverseOpenedCurve(myLastEdge, anEdge, TOLERANCE);
194     }
195
196     myLastEdge = anEdge;
197     if (isReverse)
198       anEdge->reverse();
199   }
200   else if (theMethod == Method::USE_CURVE_INFORMATION()) {
201     // make all edges FORWARD to avoid reversing the curves by GeomAlgoAPI_Filling algorithm
202     anEdge->setOrientation(GeomAPI_Shape::FORWARD);
203   }
204   return anEdge;
205 }
206
207 //=================================================================================================
208 void BuildPlugin_Filling::restoreDefaultParameters()
209 {
210   string(METHOD_ID())->setValue(METHOD_DEFAULT());
211   integer(MINIMAL_DEGREE_ID())->setValue(MINIMAL_DEGREE_DEFAULT());
212   integer(MAXIMAL_DEGREE_ID())->setValue(MAXIMAL_DEGREE_DEFAULT());
213   integer(NUMBER_OF_ITERATIONS_ID())->setValue(NUMBER_OF_ITERATIONS_DEFAULT());
214   real(TOLERANCE_2D_ID())->setValue(TOLERANCE_2D_DEFAULT());
215   real(TOLERANCE_3D_ID())->setValue(TOLERANCE_3D_DEFAULT());
216   boolean(APPROXIMATION_ID())->setValue(APPROXIMATION_DEFAULT());
217 }
218
219
220 //============     Auxiliary functions     ========================================================
221
222 static std::pair<GeomPointPtr, GeomPointPtr> edgeBoundaries(const GeomEdgePtr& theEdge)
223 {
224   GeomPointPtr aStart = theEdge->firstPoint();
225   GeomPointPtr anEnd = theEdge->lastPoint();
226   if (theEdge->orientation() == GeomAPI_Shape::REVERSED)
227     std::swap(aStart, anEnd);
228   return std::pair<GeomPointPtr, GeomPointPtr>(aStart, anEnd);
229 }
230
231 static void edgePoints(const GeomEdgePtr& theEdge, std::list<GeomPointPtr>& thePoints)
232 {
233   GeomAPI_Curve aCurve(theEdge);
234   static const int aNbSegments = 10;
235   double aStart = aCurve.startParam();
236   double aEnd = aCurve.endParam();
237   for (int i = 0; i <= aNbSegments; ++i)
238     thePoints.push_back(aCurve.getPoint(aStart * (1.0 - (double)i / aNbSegments) +
239                                         aEnd * (double)i / aNbSegments ));
240   if (theEdge->orientation() == GeomAPI_Shape::REVERSED)
241     thePoints.reverse();
242 }
243
244 bool isReverseClosedCurve(const GeomEdgePtr& theEdge1,
245                           const GeomEdgePtr& theEdge2)
246 {
247   std::list<GeomPointPtr> anEdge1Points, anEdge2Points;
248   edgePoints(theEdge1, anEdge1Points);
249   edgePoints(theEdge2, anEdge2Points);
250
251   double d1 = 0.0;
252   double d2 = 0.0;
253   std::list<GeomPointPtr>::const_iterator anIt1 = anEdge1Points.begin();
254   std::list<GeomPointPtr>::const_iterator anIt2 = anEdge2Points.begin();
255   std::list<GeomPointPtr>::const_reverse_iterator anIt2Rev = anEdge2Points.rbegin();
256   for (; anIt1 != anEdge1Points.end(); ++anIt1, ++anIt2, ++anIt2Rev) {
257     d1 += (*anIt1)->distance(*anIt2);
258     d2 += (*anIt1)->distance(*anIt2Rev);
259   }
260   return d2 < d1;
261 }
262
263 bool isReverseOpenedCurve(const GeomEdgePtr& theEdge1,
264                           const GeomEdgePtr& theEdge2,
265                           const double theTolerance)
266 {
267   std::pair<GeomPointPtr, GeomPointPtr> anEdge1Points = edgeBoundaries(theEdge1);
268   std::pair<GeomPointPtr, GeomPointPtr> anEdge2Points = edgeBoundaries(theEdge2);
269   double d1 = anEdge1Points.first->distance(anEdge2Points.first)
270             + anEdge1Points.second->distance(anEdge2Points.second);
271   double d2 = anEdge1Points.first->distance(anEdge2Points.second)
272             + anEdge1Points.second->distance(anEdge2Points.first);
273   if (fabs(d1 - d2) < theTolerance) {
274     // undefined case => check distance to start point only
275     d1 = anEdge1Points.first->distance(anEdge2Points.first);
276     d2 = anEdge1Points.first->distance(anEdge2Points.second);
277   }
278   return d2 < d1;
279 }
280
281 void shiftStartPoint(GeomWirePtr& theWire, const GeomEdgePtr& theRefEdge, const double theTolerance)
282 {
283   if (!theWire->isClosed()) {
284     GeomVertexPtr aV1, aV2;
285     GeomAlgoAPI_ShapeTools::findBounds(theWire, aV1, aV2);
286     if (aV1->point()->distance(aV2->point()) > theTolerance)
287       return;
288   }
289
290   // find closest vertex on the wire to the start point on the edge
291   GeomPointPtr aFirstRefPnt = theRefEdge->firstPoint();
292   ListOfShape aBegin, aEnd;
293   double aMinDist = 1.e100;
294   for (GeomAPI_WireExplorer anExp(theWire); anExp.more(); anExp.next()) {
295     double aDist = anExp.currentVertex()->point()->distance(aFirstRefPnt);
296     if (aDist < aMinDist) {
297       aMinDist = aDist;
298       aEnd.insert(aEnd.end(), aBegin.begin(), aBegin.end());
299       aBegin.clear();
300     }
301     aBegin.push_back(anExp.current());
302   }
303   aBegin.insert(aBegin.end(), aEnd.begin(), aEnd.end());
304
305   GeomShapePtr aShape = GeomAlgoAPI_WireBuilder::wire(aBegin);
306   theWire.reset(new GeomAPI_Wire(aShape));
307 }