Salome HOME
Issue #19056: crash when selecting a whole sketch in "build ==> wire"
[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           aResultBody->modified(anEdgeInList, anEdgeInResult);
126           break;
127         }
128       }
129     }
130     setResult(aResultBody, aResultIndex);
131     ++aResultIndex;
132   }
133
134   // create wires from sketches
135   for (std::list<std::pair<FeaturePtr, GeomShapePtr> >::iterator anIt = aSketches.begin();
136        anIt != aSketches.end(); ++anIt) {
137     ListOfShape aWires;
138     GeomMakeShapePtr aMakeShapeList;
139     if (!buildSketchWires(anIt->first, anIt->second, isIntersect,
140                           aWires, aMakeShapeList, anError)) {
141       setError(anError);
142       return;
143     }
144
145     for (ListOfShape::iterator aWIt = aWires.begin(); aWIt != aWires.end(); ++aWIt) {
146       ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
147       ListOfShape aSketches;
148       aSketches.push_back(anIt->second);
149       aResultBody->storeModified(aSketches, *aWIt, aMakeShapeList);
150       aResultBody->loadModifiedShapes(aMakeShapeList, anIt->second, GeomAPI_Shape::EDGE);
151       setResult(aResultBody, aResultIndex);
152       ++aResultIndex;
153     }
154   }
155
156   removeResults(aResultIndex);
157 }
158
159 //=================================================================================================
160 bool BuildPlugin_Wire::customAction(const std::string& theActionId)
161 {
162   if(theActionId == ADD_CONTOUR_ACTION_ID()) {
163     return addContour();
164   } else {
165     std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
166     Events_InfoMessage("BuildPlugin_Wire", aMsg).arg(getKind()).arg(theActionId).send();
167   }
168
169   return false;
170 }
171
172 //=================================================================================================
173 bool BuildPlugin_Wire::addContour()
174 {
175   // Get base objects list.
176   AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID());
177   if(aSelectionList->size() == 0) {
178     Events_InfoMessage("BuildPlugin_Wire", "Error: Empty selection list.").send();
179     return false;
180   }
181
182   // Collect attributes to check.
183   ListOfShape anAddedEdges;
184   std::list<AttributeSelectionPtr> anAttributesToCheck;
185   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
186     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
187     GeomShapePtr anEdgeInList = aSelection->value();
188     if(!anEdgeInList.get()) {
189       continue;
190     }
191
192     // Check that it is edge.
193     if(anEdgeInList->shapeType() != GeomAPI_Shape::EDGE) {
194       continue;
195     }
196
197     // Check that it is edge on sketch.
198     ResultPtr aContext = aSelection->context();
199     ResultConstructionPtr aConstruction =
200       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
201     if(!aConstruction.get()) {
202       continue;
203     }
204     GeomShapePtr aContextShape = aConstruction->shape();
205     std::shared_ptr<GeomAPI_PlanarEdges> aPlanarEdges =
206       std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aContextShape);
207     if(!aPlanarEdges.get()) {
208       continue;
209     }
210
211     // Check that sketch have faces.
212     if(aConstruction->facesNum() == 0) {
213       continue;
214     }
215
216     anAddedEdges.push_back(anEdgeInList);
217     anAttributesToCheck.push_back(aSelection);
218   }
219
220   // Check if edges have contours.
221   bool isAnyContourAdded = false;
222   for(std::list<AttributeSelectionPtr>::const_iterator aListIt = anAttributesToCheck.cbegin();
223       aListIt != anAttributesToCheck.cend();
224       ++aListIt) {
225     AttributeSelectionPtr aSelection = *aListIt;
226     std::shared_ptr<GeomAPI_Edge> anEdgeInList(new GeomAPI_Edge(aSelection->value()));
227
228     ResultConstructionPtr aConstruction =
229       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aSelection->context());
230
231     // Iterate on wires and add wire with this edge.
232     std::shared_ptr<GeomAPI_Shape> aFoundWire;
233     for(int anIndex = 0; anIndex < aConstruction->facesNum(); ++anIndex) {
234       std::shared_ptr<GeomAPI_Face> aFace = aConstruction->face(anIndex);
235       for(GeomAPI_ShapeExplorer
236           aWireExp(aFace, GeomAPI_Shape::WIRE); aWireExp.more(); aWireExp.next()) {
237         GeomShapePtr aWireOnFace = aWireExp.current();
238         for(GeomAPI_ShapeExplorer
239             anExp(aWireOnFace, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
240           std::shared_ptr<GeomAPI_Edge> anEdgeOnFace(new GeomAPI_Edge(anExp.current()));
241           if(anEdgeInList->isEqual(anEdgeOnFace)) {
242             aFoundWire = aWireOnFace;
243             break;
244           }
245         }
246       }
247       if(aFoundWire.get()) {
248         break;
249       }
250     }
251
252     // If wire with the same edge found. Add all other edges to list.
253     if(aFoundWire.get()) {
254       for(GeomAPI_ShapeExplorer
255           anExp(aFoundWire, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
256         std::shared_ptr<GeomAPI_Edge> anEdgeOnFace(new GeomAPI_Edge(anExp.current()));
257         ListOfShape::const_iterator anEdgesIt = anAddedEdges.cbegin();
258         for(; anEdgesIt != anAddedEdges.cend(); ++anEdgesIt) {
259           if(anEdgeOnFace->isEqual(*anEdgesIt)) {
260             break;
261           }
262         }
263         if(anEdgesIt == anAddedEdges.cend()) {
264           isAnyContourAdded = true;
265           anAddedEdges.push_back(anEdgeOnFace);
266           aSelectionList->append(aConstruction, anEdgeOnFace);
267         }
268       }
269     }
270   }
271
272   if(!isAnyContourAdded) {
273     Events_InfoMessage("BuildPlugin_Wire",
274       "Error: Contours already closed or no contours found for selected edges.").send();
275     return false;
276   }
277
278   return true;
279 }
280
281
282
283 // =====================     Auxiliary functions     ==============================================
284
285 bool buildWire(const ListOfShape& theEdges, GeomShapePtr& theWire, std::string& theError)
286 {
287   theWire = GeomAlgoAPI_WireBuilder::wire(theEdges);
288   if (!theWire.get()) {
289     theError = "Error: Result wire is empty. Probably it has disconnected edges or non-manifold.";
290     return false;
291   }
292   return true;
293 }
294
295 bool buildSketchWires(FeaturePtr theSketchFeature, GeomShapePtr theSketchShape, bool isIntersect,
296                       ListOfShape& theWires, GeomMakeShapePtr& theAlgo, std::string& theError)
297 {
298   ListOfShape aSketchEdges =
299       std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theSketchShape)->getEdges();
300
301   std::shared_ptr<GeomAlgoAPI_MakeShapeList> anAlgoList(new GeomAlgoAPI_MakeShapeList);
302   if (isIntersect) {
303     std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aProcessedEdges;
304     // perform sketch builder first
305     AttributePointPtr anOrigin = std::dynamic_pointer_cast<GeomDataAPI_Point>(
306         theSketchFeature->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
307     AttributeDirPtr aNormal = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
308         theSketchFeature->attribute(SketchPlugin_Sketch::NORM_ID()));
309     AttributeDirPtr aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
310         theSketchFeature->attribute(SketchPlugin_Sketch::DIRX_ID()));
311     std::shared_ptr<GeomAlgoAPI_SketchBuilder> aSketchBuilder(new GeomAlgoAPI_SketchBuilder(
312         anOrigin->pnt(), aDirX->dir(), aNormal->dir(), theSketchShape));
313
314     anAlgoList->appendAlgo(aSketchBuilder);
315
316     // collect wires from faces
317     const ListOfShape& aFaces = aSketchBuilder->faces();
318     for (ListOfShape::const_iterator anIt = aFaces.begin(); anIt != aFaces.end(); ++anIt) {
319       for (GeomAPI_ShapeExplorer aWExp(*anIt, GeomAPI_Shape::WIRE); aWExp.more(); aWExp.next()) {
320         GeomAPI_ShapeExplorer aEExp(aWExp.current(), GeomAPI_Shape::EDGE);
321         if (aProcessedEdges.find(aEExp.current()) != aProcessedEdges.end())
322           continue; // wire is already processed
323         // mark edges as processed
324         for (; aEExp.more(); aEExp.next())
325           aProcessedEdges.insert(aEExp.current());
326         // store the wire
327         theWires.push_back(aWExp.current());
328       }
329     }
330
331     // collect unused edges
332     ListOfShape aCopy;
333     for (ListOfShape::iterator anIt = aSketchEdges.begin(); anIt != aSketchEdges.end(); ++anIt) {
334       ListOfShape anImages;
335       aSketchBuilder->modified(*anIt, anImages);
336       for (ListOfShape::iterator anEdge = anImages.begin(); anEdge != anImages.end(); ++anEdge)
337         if (aProcessedEdges.find(*anEdge) == aProcessedEdges.end())
338           aCopy.push_back(*anEdge);
339     }
340
341     if (aCopy.size() > 1) {
342       // split these edges
343       std::shared_ptr<GeomAlgoAPI_PaveFiller> aGeneralFuse(new GeomAlgoAPI_PaveFiller(aCopy));
344       if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(
345               aGeneralFuse, BuildPlugin_Wire::ID(), theError))
346         return false;
347       anAlgoList->appendAlgo(aGeneralFuse);
348
349       // collect edges after the split
350       aSketchEdges.clear();
351       for (GeomAPI_ShapeExplorer anExp(aGeneralFuse->shape(), GeomAPI_Shape::EDGE);
352            anExp.more(); anExp.next())
353         aSketchEdges.push_back(anExp.current());
354     }
355     else
356       aSketchEdges = aCopy;
357   }
358
359   // connect least edges to wires
360   typedef std::list<ListOfShape> ListOfWires;
361   ListOfWires aNewWires;
362   typedef std::map<GeomVertexPtr, ListOfWires::iterator,
363                    GeomAPI_Vertex::GeometricComparator> MapVertexWire;
364   MapVertexWire aMapVW;
365   for (ListOfShape::iterator aEIt = aSketchEdges.begin(); aEIt != aSketchEdges.end(); ++aEIt) {
366     GeomEdgePtr anEdge = (*aEIt)->edge();
367     GeomVertexPtr aStartV, aEndV;
368     anEdge->vertices(aStartV, aEndV);
369     MapVertexWire::iterator aFoundStart = aMapVW.find(aStartV);
370     MapVertexWire::iterator aFoundEnd = aMapVW.find(aEndV);
371     if (aFoundStart == aMapVW.end()) {
372       if (aFoundEnd == aMapVW.end()) {
373         // new wire
374         aNewWires.push_back(ListOfShape());
375         ListOfWires::iterator aNewW = --aNewWires.end();
376         aNewW->push_back(anEdge);
377         aMapVW[aStartV] = aNewW;
378         aMapVW[aEndV] = aNewW;
379         continue;
380       }
381     }
382     else {
383       if (aFoundEnd == aMapVW.end()) {
384         // swap found vertices for correct further processing
385         aFoundEnd = aFoundStart;
386         aStartV = aEndV;
387       }
388       else {
389         // both vertices are found
390         aFoundStart->second->push_back(anEdge);
391         if (aFoundStart->second != aFoundEnd->second) {
392           // different wires => merge segments
393           aFoundStart->second->insert(aFoundStart->second->end(),
394               aFoundEnd->second->begin(), aFoundEnd->second->end());
395           for (MapVertexWire::iterator it = aMapVW.begin(); it != aMapVW.end(); ++it)
396             if (it != aFoundEnd && it->second == aFoundEnd->second) {
397               // another boundary of the wire, change link to the whole result
398               it->second = aFoundStart->second;
399               break;
400             }
401           aNewWires.erase(aFoundEnd->second);
402         }
403         aMapVW.erase(aFoundStart);
404         aMapVW.erase(aFoundEnd);
405         continue;
406       }
407     }
408     // add edge to existing wire, substitute the connection point
409     // by the other boundary point of the edge
410     aFoundEnd->second->push_back(anEdge);
411     aMapVW[aStartV] = aFoundEnd->second;
412     aMapVW.erase(aFoundEnd);
413   }
414
415   // generate new wires from the sets of edges
416   for (ListOfWires::iterator anIt = aNewWires.begin(); anIt != aNewWires.end(); ++anIt) {
417     GeomShapePtr aWire;
418     if (!buildWire(*anIt, aWire, theError))
419       return false;
420     theWires.push_back(aWire);
421   }
422
423   theAlgo = anAlgoList;
424   return true;
425 }