Salome HOME
Merge remote-tracking branch 'remotes/origin/EDF_2020_Lot2'
[modules/shaper.git] / src / BuildPlugin / BuildPlugin_Wire.cpp
1 // Copyright (C) 2014-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 "BuildPlugin_Wire.h"
21
22 #include <ModelAPI_AttributeBoolean.h>
23 #include <ModelAPI_AttributeSelectionList.h>
24 #include <ModelAPI_ResultBody.h>
25 #include <ModelAPI_ResultConstruction.h>
26 #include <ModelAPI_Session.h>
27 #include <ModelAPI_Validator.h>
28
29 #include <Events_InfoMessage.h>
30
31 #include <GeomAPI_PlanarEdges.h>
32 #include <GeomAPI_ShapeExplorer.h>
33 #include <GeomAPI_Vertex.h>
34
35 #include <GeomAlgoAPI_MakeShapeList.h>
36 #include <GeomAlgoAPI_PaveFiller.h>
37 #include <GeomAlgoAPI_SketchBuilder.h>
38 #include <GeomAlgoAPI_Tools.h>
39 #include <GeomAlgoAPI_WireBuilder.h>
40
41 #include <SketchPlugin_Sketch.h>
42
43 #include <algorithm>
44
45 static bool buildSketchWires(FeaturePtr theSketchFeature, GeomShapePtr theSketchShape,
46                              bool isIntersect,
47                              ListOfShape& theWires, GeomMakeShapePtr& theAlgo,
48                              std::string& theError);
49
50 static bool buildWire(const ListOfShape& theEdges, GeomShapePtr& theWire, std::string& theError);
51
52 //=================================================================================================
53 BuildPlugin_Wire::BuildPlugin_Wire()
54 {
55 }
56
57 //=================================================================================================
58 void BuildPlugin_Wire::initAttributes()
59 {
60   data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
61
62   data()->addAttribute(INTERSECT_ID(), ModelAPI_AttributeBoolean::typeId());
63   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), INTERSECT_ID());
64 }
65
66 //=================================================================================================
67 void BuildPlugin_Wire::execute()
68 {
69   // Get base objects list.
70   AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID());
71   if(!aSelectionList.get()) {
72     setError("Error: Could not get selection list.");
73     return;
74   }
75   if(aSelectionList->size() == 0) {
76     setError("Error: Empty selection list.");
77     return;
78   }
79
80   AttributeBooleanPtr anIntersectAttr = boolean(INTERSECT_ID());
81   bool isIntersect = anIntersectAttr->isInitialized() && anIntersectAttr->value();
82
83   // Collect base shapes.
84   ListOfShape anEdges;
85   std::list< std::pair<FeaturePtr, GeomShapePtr> > aSketches;
86   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
87     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
88     GeomShapePtr aShape = aSelection->value();
89     if(!aShape.get()) {
90       aShape = aSelection->context()->shape();
91
92       std::shared_ptr<GeomAPI_PlanarEdges> aSketchShape =
93           std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aShape);
94       if (aSketchShape) {
95         FeaturePtr aSketchFeature = ModelAPI_Feature::feature(aSelection->context());
96         aSketches.push_back(std::pair<FeaturePtr, GeomShapePtr>(aSketchFeature, aSketchShape));
97         continue;
98       }
99     }
100     for(GeomAPI_ShapeExplorer anExp(aShape, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
101       GeomShapePtr anEdge = anExp.current();
102       anEdges.push_back(anEdge);
103     }
104   }
105
106   int aResultIndex = 0;
107   std::string anError;
108
109   if (!anEdges.empty()) {
110     // Create wire from the list of edges.
111     GeomShapePtr aWire;
112     if (!buildWire(anEdges, aWire, anError)) {
113       setError(anError);
114       return;
115     }
116
117     // Store result.
118     ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
119     aResultBody->store(aWire);
120     for (GeomAPI_ShapeExplorer anExp(aWire, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
121       GeomShapePtr anEdgeInResult = anExp.current();
122       for (ListOfShape::const_iterator anIt = anEdges.cbegin(); anIt != anEdges.cend(); ++anIt) {
123         std::shared_ptr<GeomAPI_Edge> anEdgeInList(new GeomAPI_Edge(*anIt));
124         if (anEdgeInList->isEqual(anEdgeInResult)) {
125           if (anEdgeInList->isSame(anEdgeInResult))
126             aResultBody->generated(anEdgeInResult, "Edge");
127           else
128             aResultBody->modified(anEdgeInList, anEdgeInResult);
129           break;
130         }
131       }
132     }
133     setResult(aResultBody, aResultIndex);
134     ++aResultIndex;
135   }
136
137   // create wires from sketches
138   for (std::list<std::pair<FeaturePtr, GeomShapePtr> >::iterator anIt = aSketches.begin();
139        anIt != aSketches.end(); ++anIt) {
140     ListOfShape aWires;
141     GeomMakeShapePtr aMakeShapeList;
142     if (!buildSketchWires(anIt->first, anIt->second, isIntersect,
143                           aWires, aMakeShapeList, anError)) {
144       setError(anError);
145       return;
146     }
147
148     for (ListOfShape::iterator aWIt = aWires.begin(); aWIt != aWires.end(); ++aWIt) {
149       ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
150       ListOfShape aSketch;
151       aSketch.push_back(anIt->second);
152       aResultBody->storeModified(aSketch, *aWIt, aMakeShapeList);
153       aResultBody->loadModifiedShapes(aMakeShapeList, anIt->second, GeomAPI_Shape::EDGE);
154       setResult(aResultBody, aResultIndex);
155       ++aResultIndex;
156     }
157   }
158
159   removeResults(aResultIndex);
160 }
161
162 //=================================================================================================
163 bool BuildPlugin_Wire::customAction(const std::string& theActionId)
164 {
165   if(theActionId == ADD_CONTOUR_ACTION_ID()) {
166     return addContour();
167   } else {
168     std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
169     Events_InfoMessage("BuildPlugin_Wire", aMsg).arg(getKind()).arg(theActionId).send();
170   }
171
172   return false;
173 }
174
175 //=================================================================================================
176 bool BuildPlugin_Wire::addContour()
177 {
178   // Get base objects list.
179   AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID());
180   if(aSelectionList->size() == 0) {
181     Events_InfoMessage("BuildPlugin_Wire", "Error: Empty selection list.").send();
182     return false;
183   }
184
185   // Collect attributes to check.
186   ListOfShape anAddedEdges;
187   std::list<AttributeSelectionPtr> anAttributesToCheck;
188   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
189     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
190     GeomShapePtr anEdgeInList = aSelection->value();
191     if(!anEdgeInList.get()) {
192       continue;
193     }
194
195     // Check that it is edge.
196     if(anEdgeInList->shapeType() != GeomAPI_Shape::EDGE) {
197       continue;
198     }
199
200     // Check that it is edge on sketch.
201     ResultPtr aContext = aSelection->context();
202     ResultConstructionPtr aConstruction =
203       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
204     if(!aConstruction.get()) {
205       continue;
206     }
207     GeomShapePtr aContextShape = aConstruction->shape();
208     std::shared_ptr<GeomAPI_PlanarEdges> aPlanarEdges =
209       std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aContextShape);
210     if(!aPlanarEdges.get()) {
211       continue;
212     }
213
214     // Check that sketch have faces.
215     if(aConstruction->facesNum() == 0) {
216       continue;
217     }
218
219     anAddedEdges.push_back(anEdgeInList);
220     anAttributesToCheck.push_back(aSelection);
221   }
222
223   // Check if edges have contours.
224   bool isAnyContourAdded = false;
225   for(std::list<AttributeSelectionPtr>::const_iterator aListIt = anAttributesToCheck.cbegin();
226       aListIt != anAttributesToCheck.cend();
227       ++aListIt) {
228     AttributeSelectionPtr aSelection = *aListIt;
229     std::shared_ptr<GeomAPI_Edge> anEdgeInList(new GeomAPI_Edge(aSelection->value()));
230
231     ResultConstructionPtr aConstruction =
232       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aSelection->context());
233
234     // Iterate on wires and add wire with this edge.
235     std::shared_ptr<GeomAPI_Shape> aFoundWire;
236     for(int anIndex = 0; anIndex < aConstruction->facesNum(); ++anIndex) {
237       std::shared_ptr<GeomAPI_Face> aFace = aConstruction->face(anIndex);
238       for(GeomAPI_ShapeExplorer
239           aWireExp(aFace, GeomAPI_Shape::WIRE); aWireExp.more(); aWireExp.next()) {
240         GeomShapePtr aWireOnFace = aWireExp.current();
241         for(GeomAPI_ShapeExplorer
242             anExp(aWireOnFace, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
243           std::shared_ptr<GeomAPI_Edge> anEdgeOnFace(new GeomAPI_Edge(anExp.current()));
244           if(anEdgeInList->isEqual(anEdgeOnFace)) {
245             aFoundWire = aWireOnFace;
246             break;
247           }
248         }
249       }
250       if(aFoundWire.get()) {
251         break;
252       }
253     }
254
255     // If wire with the same edge found. Add all other edges to list.
256     if(aFoundWire.get()) {
257       for(GeomAPI_ShapeExplorer
258           anExp(aFoundWire, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
259         std::shared_ptr<GeomAPI_Edge> anEdgeOnFace(new GeomAPI_Edge(anExp.current()));
260         ListOfShape::const_iterator anEdgesIt = anAddedEdges.cbegin();
261         for(; anEdgesIt != anAddedEdges.cend(); ++anEdgesIt) {
262           if(anEdgeOnFace->isEqual(*anEdgesIt)) {
263             break;
264           }
265         }
266         if(anEdgesIt == anAddedEdges.cend()) {
267           isAnyContourAdded = true;
268           anAddedEdges.push_back(anEdgeOnFace);
269           aSelectionList->append(aConstruction, anEdgeOnFace);
270         }
271       }
272     }
273   }
274
275   if(!isAnyContourAdded) {
276     Events_InfoMessage("BuildPlugin_Wire",
277       "Error: Contours already closed or no contours found for selected edges.").send();
278     return false;
279   }
280
281   return true;
282 }
283
284
285
286 // =====================     Auxiliary functions     ==============================================
287
288 bool buildWire(const ListOfShape& theEdges, GeomShapePtr& theWire, std::string& theError)
289 {
290   theWire = GeomAlgoAPI_WireBuilder::wire(theEdges);
291   if (!theWire.get()) {
292     theError = "Error: Result wire is empty. Probably it has disconnected edges or non-manifold.";
293     return false;
294   }
295   return true;
296 }
297
298 bool buildSketchWires(FeaturePtr theSketchFeature, GeomShapePtr theSketchShape, bool isIntersect,
299                       ListOfShape& theWires, GeomMakeShapePtr& theAlgo, std::string& theError)
300 {
301   ListOfShape aSketchEdges =
302       std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theSketchShape)->getEdges();
303
304   std::shared_ptr<GeomAlgoAPI_MakeShapeList> anAlgoList(new GeomAlgoAPI_MakeShapeList);
305   if (isIntersect) {
306     std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aProcessedEdges;
307     // perform sketch builder first
308     AttributePointPtr anOrigin = std::dynamic_pointer_cast<GeomDataAPI_Point>(
309         theSketchFeature->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
310     AttributeDirPtr aNormal = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
311         theSketchFeature->attribute(SketchPlugin_Sketch::NORM_ID()));
312     AttributeDirPtr aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
313         theSketchFeature->attribute(SketchPlugin_Sketch::DIRX_ID()));
314     std::shared_ptr<GeomAlgoAPI_SketchBuilder> aSketchBuilder(new GeomAlgoAPI_SketchBuilder(
315         anOrigin->pnt(), aDirX->dir(), aNormal->dir(), theSketchShape));
316
317     anAlgoList->appendAlgo(aSketchBuilder);
318
319     // collect wires from faces
320     const ListOfShape& aFaces = aSketchBuilder->faces();
321     for (ListOfShape::const_iterator anIt = aFaces.begin(); anIt != aFaces.end(); ++anIt) {
322       for (GeomAPI_ShapeExplorer aWExp(*anIt, GeomAPI_Shape::WIRE); aWExp.more(); aWExp.next()) {
323         GeomAPI_ShapeExplorer aEExp(aWExp.current(), GeomAPI_Shape::EDGE);
324         if (aProcessedEdges.find(aEExp.current()) != aProcessedEdges.end())
325           continue; // wire is already processed
326         // mark edges as processed
327         for (; aEExp.more(); aEExp.next())
328           aProcessedEdges.insert(aEExp.current());
329         // store the wire
330         theWires.push_back(aWExp.current());
331       }
332     }
333
334     // collect unused edges
335     ListOfShape aCopy;
336     for (ListOfShape::iterator anIt = aSketchEdges.begin(); anIt != aSketchEdges.end(); ++anIt) {
337       ListOfShape anImages;
338       aSketchBuilder->modified(*anIt, anImages);
339       for (ListOfShape::iterator anEdge = anImages.begin(); anEdge != anImages.end(); ++anEdge)
340         if (aProcessedEdges.find(*anEdge) == aProcessedEdges.end())
341           aCopy.push_back(*anEdge);
342     }
343
344     if (aCopy.size() > 1) {
345       // split these edges
346       std::shared_ptr<GeomAlgoAPI_PaveFiller> aGeneralFuse(new GeomAlgoAPI_PaveFiller(aCopy));
347       if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(
348               aGeneralFuse, BuildPlugin_Wire::ID(), theError))
349         return false;
350       anAlgoList->appendAlgo(aGeneralFuse);
351
352       // collect edges after the split
353       aSketchEdges.clear();
354       for (GeomAPI_ShapeExplorer anExp(aGeneralFuse->shape(), GeomAPI_Shape::EDGE);
355            anExp.more(); anExp.next())
356         aSketchEdges.push_back(anExp.current());
357     }
358     else
359       aSketchEdges = aCopy;
360   }
361
362   // connect least edges to wires
363   typedef std::list<ListOfShape> ListOfWires;
364   ListOfWires aNewWires;
365   typedef std::map<GeomVertexPtr, ListOfWires::iterator,
366                    GeomAPI_Vertex::GeometricComparator> MapVertexWire;
367   MapVertexWire aMapVW;
368   for (ListOfShape::iterator aEIt = aSketchEdges.begin(); aEIt != aSketchEdges.end(); ++aEIt) {
369     GeomEdgePtr anEdge = (*aEIt)->edge();
370     GeomVertexPtr aStartV, aEndV;
371     anEdge->vertices(aStartV, aEndV);
372     MapVertexWire::iterator aFoundStart = aMapVW.find(aStartV);
373     MapVertexWire::iterator aFoundEnd = aMapVW.find(aEndV);
374     if (aFoundStart == aMapVW.end()) {
375       if (aFoundEnd == aMapVW.end()) {
376         // new wire
377         aNewWires.push_back(ListOfShape());
378         ListOfWires::iterator aNewW = --aNewWires.end();
379         aNewW->push_back(anEdge);
380         aMapVW[aStartV] = aNewW;
381         aMapVW[aEndV] = aNewW;
382         continue;
383       }
384     }
385     else {
386       if (aFoundEnd == aMapVW.end()) {
387         // swap found vertices for correct further processing
388         aFoundEnd = aFoundStart;
389         aStartV = aEndV;
390       }
391       else {
392         // both vertices are found
393         aFoundStart->second->push_back(anEdge);
394         if (aFoundStart->second != aFoundEnd->second) {
395           // different wires => merge segments
396           aFoundStart->second->insert(aFoundStart->second->end(),
397               aFoundEnd->second->begin(), aFoundEnd->second->end());
398           for (MapVertexWire::iterator it = aMapVW.begin(); it != aMapVW.end(); ++it)
399             if (it != aFoundEnd && it->second == aFoundEnd->second) {
400               // another boundary of the wire, change link to the whole result
401               it->second = aFoundStart->second;
402               break;
403             }
404           aNewWires.erase(aFoundEnd->second);
405         }
406         aMapVW.erase(aFoundStart);
407         aMapVW.erase(aFoundEnd);
408         continue;
409       }
410     }
411     // add edge to existing wire, substitute the connection point
412     // by the other boundary point of the edge
413     aFoundEnd->second->push_back(anEdge);
414     aMapVW[aStartV] = aFoundEnd->second;
415     aMapVW.erase(aFoundEnd);
416   }
417
418   // generate new wires from the sets of edges
419   for (ListOfWires::iterator anIt = aNewWires.begin(); anIt != aNewWires.end(); ++anIt) {
420     GeomShapePtr aWire;
421     if (!buildWire(*anIt, aWire, theError))
422       return false;
423     theWires.push_back(aWire);
424   }
425
426   theAlgo = anAlgoList;
427   return true;
428 }