1 // Copyright (C) 2017-2021 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "BuildPlugin_Filling.h"
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>
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>
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>
43 struct FillingParameters
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);
64 //=================================================================================================
65 BuildPlugin_Filling::BuildPlugin_Filling()
69 //=================================================================================================
70 void BuildPlugin_Filling::initAttributes()
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());
82 if (string(ADVANCED_OPTIONS_ID())->value().empty())
83 restoreDefaultParameters();
86 //=================================================================================================
87 void BuildPlugin_Filling::execute()
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();
99 if (aParameters.minDegree > aParameters.maxDegree) {
100 setError("Error: " + getKind() + " algorithm failed (max deg < min deg).");
104 std::shared_ptr<GeomAlgoAPI_Filling> aFilling(
105 new GeomAlgoAPI_Filling(aParameters.minDegree, aParameters.maxDegree, aParameters.nbIter,
106 aParameters.tol2D, aParameters.tol3D));
108 // get base objects list
109 AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID());
110 if (aSelectionList->size() <= 1) {
111 setError("Not enough objects is selected");
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);
121 aFilling->add(anEdge);
123 myLastEdge = GeomEdgePtr();
126 aFilling->build(aParameters.isApprox);
128 if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aFilling, getKind(), anError)) {
135 GeomShapePtr aCreatedFace = aFilling->shape();
136 ResultBodyPtr aResultBody = document()->createBody(data());
137 aResultBody->store(aCreatedFace);
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));
144 setResult(aResultBody, 0);
147 //=================================================================================================
148 void BuildPlugin_Filling::attributeChanged(const std::string& theID)
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();
156 //=================================================================================================
157 GeomEdgePtr BuildPlugin_Filling::toEdge(const GeomShapePtr& theShape, const std::string& theMethod)
159 static const double TOLERANCE = 1.e-7;
162 switch (theShape->shapeType()) {
163 case GeomAPI_Shape::EDGE:
164 anEdge = GeomEdgePtr(new GeomAPI_Edge(GeomAlgoAPI_Copy(theShape).shape()));
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);
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);
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;
189 if (myLastEdge->firstPoint()->distance(myLastEdge->lastPoint()) < TOLERANCE &&
190 anEdge->firstPoint()->distance(anEdge->lastPoint()) < TOLERANCE)
191 isReverse = isReverseClosedCurve(myLastEdge, anEdge);
193 isReverse = isReverseOpenedCurve(myLastEdge, anEdge, TOLERANCE);
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);
207 //=================================================================================================
208 void BuildPlugin_Filling::restoreDefaultParameters()
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());
220 //============ Auxiliary functions ========================================================
222 static std::pair<GeomPointPtr, GeomPointPtr> edgeBoundaries(const GeomEdgePtr& theEdge)
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);
231 static void edgePoints(const GeomEdgePtr& theEdge, std::list<GeomPointPtr>& thePoints)
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)
244 bool isReverseClosedCurve(const GeomEdgePtr& theEdge1,
245 const GeomEdgePtr& theEdge2)
247 std::list<GeomPointPtr> anEdge1Points, anEdge2Points;
248 edgePoints(theEdge1, anEdge1Points);
249 edgePoints(theEdge2, anEdge2Points);
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);
263 bool isReverseOpenedCurve(const GeomEdgePtr& theEdge1,
264 const GeomEdgePtr& theEdge2,
265 const double theTolerance)
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);
281 void shiftStartPoint(GeomWirePtr& theWire, const GeomEdgePtr& theRefEdge, const double theTolerance)
283 if (!theWire->isClosed()) {
284 GeomVertexPtr aV1, aV2;
285 GeomAlgoAPI_ShapeTools::findBounds(theWire, aV1, aV2);
286 if (aV1->point()->distance(aV2->point()) > theTolerance)
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) {
298 aEnd.insert(aEnd.end(), aBegin.begin(), aBegin.end());
301 aBegin.push_back(anExp.current());
303 aBegin.insert(aBegin.end(), aEnd.begin(), aEnd.end());
305 GeomShapePtr aShape = GeomAlgoAPI_WireBuilder::wire(aBegin);
306 theWire.reset(new GeomAPI_Wire(aShape));