Salome HOME
updated copyright message
[modules/shaper.git] / src / BuildPlugin / BuildPlugin_Wire.cpp
1 // Copyright (C) 2014-2023  CEA, EDF
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         // skip the wire if at least one its edge was already processed
324         GeomAPI_ShapeExplorer aEExp(aWExp.current(), GeomAPI_Shape::EDGE);
325         for (; aEExp.more(); aEExp.next()) {
326           if (aProcessedEdges.find(aEExp.current()) != aProcessedEdges.end())
327             break; // wire is already processed
328         }
329         if (aEExp.more())
330           continue;
331         // mark edges as processed
332         for (aEExp.init(aWExp.current(), GeomAPI_Shape::EDGE); aEExp.more(); aEExp.next())
333           aProcessedEdges.insert(aEExp.current());
334         // store the wire
335         theWires.push_back(aWExp.current());
336       }
337     }
338
339     // collect unused edges
340     ListOfShape aCopy;
341     for (ListOfShape::iterator anIt = aSketchEdges.begin(); anIt != aSketchEdges.end(); ++anIt) {
342       ListOfShape anImages;
343       aSketchBuilder->modified(*anIt, anImages);
344       for (ListOfShape::iterator anEdge = anImages.begin(); anEdge != anImages.end(); ++anEdge)
345         if (aProcessedEdges.find(*anEdge) == aProcessedEdges.end())
346           aCopy.push_back(*anEdge);
347     }
348
349     if (aCopy.size() > 1) {
350       // split these edges
351       std::shared_ptr<GeomAlgoAPI_PaveFiller> aGeneralFuse(new GeomAlgoAPI_PaveFiller(aCopy));
352       if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(
353               aGeneralFuse, BuildPlugin_Wire::ID(), theError))
354         return false;
355       anAlgoList->appendAlgo(aGeneralFuse);
356
357       // collect edges after the split
358       aSketchEdges.clear();
359       for (GeomAPI_ShapeExplorer anExp(aGeneralFuse->shape(), GeomAPI_Shape::EDGE);
360            anExp.more(); anExp.next())
361         aSketchEdges.push_back(anExp.current());
362     }
363     else
364       aSketchEdges = aCopy;
365   }
366
367   // connect least edges to wires
368   typedef std::list<ListOfShape> ListOfWires;
369   ListOfWires aNewWires;
370   typedef std::map<GeomVertexPtr, ListOfWires::iterator,
371                    GeomAPI_Vertex::GeometricComparator> MapVertexWire;
372   MapVertexWire aMapVW;
373   for (ListOfShape::iterator aEIt = aSketchEdges.begin(); aEIt != aSketchEdges.end(); ++aEIt) {
374     GeomEdgePtr anEdge = (*aEIt)->edge();
375     GeomVertexPtr aStartV, aEndV;
376     anEdge->vertices(aStartV, aEndV);
377     MapVertexWire::iterator aFoundStart = aMapVW.find(aStartV);
378     MapVertexWire::iterator aFoundEnd = aMapVW.find(aEndV);
379     if (aFoundStart == aMapVW.end()) {
380       if (aFoundEnd == aMapVW.end()) {
381         // new wire
382         aNewWires.push_back(ListOfShape());
383         ListOfWires::iterator aNewW = --aNewWires.end();
384         aNewW->push_back(anEdge);
385         aMapVW[aStartV] = aNewW;
386         aMapVW[aEndV] = aNewW;
387         continue;
388       }
389     }
390     else {
391       if (aFoundEnd == aMapVW.end()) {
392         // swap found vertices for correct further processing
393         aFoundEnd = aFoundStart;
394         aStartV = aEndV;
395       }
396       else {
397         // both vertices are found
398         aFoundStart->second->push_back(anEdge);
399         if (aFoundStart->second == aFoundEnd->second)
400           aMapVW.erase(aFoundStart);
401         else {
402           // different wires => merge segments
403           aFoundStart->second->insert(aFoundStart->second->end(),
404               aFoundEnd->second->begin(), aFoundEnd->second->end());
405           for (MapVertexWire::iterator it = aMapVW.begin(); it != aMapVW.end(); ++it)
406             if (it != aFoundEnd && it->second == aFoundEnd->second) {
407               // another boundary of the wire, change link to the whole result
408               it->second = aFoundStart->second;
409               break;
410             }
411           aNewWires.erase(aFoundEnd->second);
412           aMapVW.erase(aFoundStart);
413           aMapVW.erase(aFoundEnd);
414         }
415         continue;
416       }
417     }
418     // add edge to existing wire, substitute the connection point
419     // by the other boundary point of the edge
420     aFoundEnd->second->push_back(anEdge);
421     aMapVW[aStartV] = aFoundEnd->second;
422     aMapVW.erase(aFoundEnd);
423   }
424
425   // generate new wires from the sets of edges
426   for (ListOfWires::iterator anIt = aNewWires.begin(); anIt != aNewWires.end(); ++anIt) {
427     GeomShapePtr aWire;
428     if (!buildWire(*anIt, aWire, theError))
429       return false;
430     theWires.push_back(aWire);
431   }
432
433   theAlgo = anAlgoList;
434   return true;
435 }