Salome HOME
79b959e9b0531737345e9344fe49d12dc539ae44
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_BooleanCut.cpp
1 // Copyright (C) 2014-2017  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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include "FeaturesPlugin_BooleanCut.h"
22
23 #include <ModelAPI_ResultBody.h>
24 #include <ModelAPI_AttributeSelectionList.h>
25 #include <ModelAPI_Tools.h>
26
27 #include <GeomAlgoAPI_Boolean.h>
28 #include <GeomAlgoAPI_CompoundBuilder.h>
29 #include <GeomAlgoAPI_MakeShapeList.h>
30 #include <GeomAlgoAPI_PaveFiller.h>
31 #include <GeomAlgoAPI_ShapeTools.h>
32 #include <GeomAPI_ShapeExplorer.h>
33 #include <GeomAPI_ShapeIterator.h>
34
35 //==================================================================================================
36 const int ModifyVTag = 1;
37 const int ModifyETag = 2;
38 const int ModifyFTag = 3;
39 const int DeletedTag = 4;
40 /// sub solids will be placed at labels 5, 6, etc. if result is compound of solids
41 const int SubsolidsTag = 5;
42
43
44 //==================================================================================================
45 FeaturesPlugin_BooleanCut::FeaturesPlugin_BooleanCut()
46 : FeaturesPlugin_Boolean(FeaturesPlugin_Boolean::BOOL_CUT)
47 {
48 }
49
50 //==================================================================================================
51 void FeaturesPlugin_BooleanCut::execute()
52 {
53   ListOfShape anObjects, aTools;
54   std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape> aCompSolidsObjects;
55   std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape> aCompoundObjects;
56
57   // Getting objects.
58   AttributeSelectionListPtr anObjectsSelList = selectionList(OBJECT_LIST_ID());
59   for(int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
60     AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex);
61     std::shared_ptr<GeomAPI_Shape> anObject = anObjectAttr->value();
62     if(!anObject.get()) {
63       return;
64     }
65     ResultPtr aContext = anObjectAttr->context();
66     ResultBodyPtr aResCompSolidPtr = ModelAPI_Tools::bodyOwner(aContext);
67     if (aResCompSolidPtr.get())
68     {
69       std::shared_ptr<GeomAPI_Shape> aContextShape = aResCompSolidPtr->shape();
70       GeomAPI_Shape::ShapeType aShapeType = aResCompSolidPtr->shape()->shapeType();
71       std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>& aMap =
72         aShapeType == GeomAPI_Shape::COMPSOLID ? aCompSolidsObjects : aCompoundObjects;
73
74         std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator
75           anIt = aMap.begin();
76         for (; anIt != aMap.end(); anIt++) {
77           if (anIt->first->isEqual(aContextShape)) {
78             aMap[anIt->first].push_back(anObject);
79             break;
80           }
81         }
82         if (anIt == aMap.end()) {
83           aMap[aContextShape].push_back(anObject);
84         }
85
86     } else {
87       anObjects.push_back(anObject);
88     }
89   }
90
91   // Getting tools.
92   AttributeSelectionListPtr aToolsSelList = selectionList(TOOL_LIST_ID());
93   for(int aToolsIndex = 0; aToolsIndex < aToolsSelList->size(); aToolsIndex++) {
94     AttributeSelectionPtr aToolAttr = aToolsSelList->value(aToolsIndex);
95     GeomShapePtr aTool = aToolAttr->value();
96     if(!aTool.get()) {
97       return;
98     }
99     aTools.push_back(aTool);
100   }
101
102   int aResultIndex = 0;
103
104   if((anObjects.empty() && aCompSolidsObjects.empty() && aCompoundObjects.empty())
105      || aTools.empty()) {
106     std::string aFeatureError = "Error: Not enough objects for boolean operation.";
107     setError(aFeatureError);
108     return;
109   }
110
111   std::vector<ResultBaseAlgo> aResultBaseAlgoList;
112   ListOfShape aResultShapesList;
113
114   // For solids cut each object with all tools.
115   for(ListOfShape::iterator anObjectsIt = anObjects.begin();
116       anObjectsIt != anObjects.end();
117       ++anObjectsIt) {
118     std::shared_ptr<GeomAPI_Shape> anObject = *anObjectsIt;
119     std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
120     std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(
121       new GeomAlgoAPI_Boolean(anObject,
122                               aTools,
123                               GeomAlgoAPI_Boolean::BOOL_CUT));
124     GeomShapePtr aResShape = aCutAlgo->shape();
125
126     // Checking that the algorithm worked properly.
127     if (!aCutAlgo->isDone()) {
128       static const std::string aFeatureError = "Error: Boolean algorithm failed.";
129       setError(aFeatureError);
130       return;
131     }
132     if(aResShape->isNull()) {
133       static const std::string aShapeError = "Error: Resulting shape is Null.";
134       setError(aShapeError);
135       return;
136     }
137     if (!aCutAlgo->isValid()) {
138       std::string aFeatureError = "Error: Resulting shape is not valid.";
139       setError(aFeatureError);
140       return;
141     }
142
143     aMakeShapeList->appendAlgo(aCutAlgo);
144
145     GeomAPI_ShapeIterator aShapeIt(aResShape);
146     if (aShapeIt.more() || aResShape->shapeType() == GeomAPI_Shape::VERTEX)
147     {
148       std::shared_ptr<ModelAPI_ResultBody> aResultBody =
149         document()->createBody(data(), aResultIndex);
150
151       loadNamingDS(aResultBody, anObject, aTools, aResShape,
152                    *aMakeShapeList, *(aCutAlgo->mapOfSubShapes()),
153                    false);
154       setResult(aResultBody, aResultIndex);
155       aResultIndex++;
156
157       ResultBaseAlgo aRBA;
158       aRBA.resultBody = aResultBody;
159       aRBA.baseShape = anObject;
160       aRBA.makeShape = aMakeShapeList;
161       aResultBaseAlgoList.push_back(aRBA);
162       aResultShapesList.push_back(aResShape);
163     }
164   }
165
166   // Compsolids handling
167   for (std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator
168        anIt = aCompSolidsObjects.begin();
169        anIt != aCompSolidsObjects.end();
170        ++anIt)
171   {
172     std::shared_ptr<GeomAPI_Shape> aCompSolid = anIt->first;
173     ListOfShape& aUsedInOperationSolids = anIt->second;
174
175     // Collecting solids from compsolids which will not be modified in boolean operation.
176     ListOfShape aNotUsedSolids;
177     for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID);
178         anExp.more();
179         anExp.next())
180     {
181       std::shared_ptr<GeomAPI_Shape> aSolidInCompSolid = anExp.current();
182       ListOfShape::iterator aUsedIt = aUsedInOperationSolids.begin();
183       for (; aUsedIt != aUsedInOperationSolids.end(); aUsedIt++) {
184         if (aSolidInCompSolid->isEqual(*aUsedIt)) {
185           break;
186         }
187       }
188       if (aUsedIt == aUsedInOperationSolids.end()) {
189         aNotUsedSolids.push_back(aSolidInCompSolid);
190       }
191     }
192
193     std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
194     std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(
195       new GeomAlgoAPI_Boolean(aUsedInOperationSolids,
196                               aTools,
197                               GeomAlgoAPI_Boolean::BOOL_CUT));
198
199     // Checking that the algorithm worked properly.
200     if (!aCutAlgo->isDone()) {
201       static const std::string aFeatureError = "Error: Boolean algorithm failed.";
202       setError(aFeatureError);
203       return;
204     }
205     if (aCutAlgo->shape()->isNull()) {
206       static const std::string aShapeError = "Error: Resulting shape is Null.";
207       setError(aShapeError);
208       return;
209     }
210     if (!aCutAlgo->isValid()) {
211       std::string aFeatureError = "Error: Resulting shape is not valid.";
212       setError(aFeatureError);
213       return;
214     }
215
216     aMakeShapeList->appendAlgo(aCutAlgo);
217     GeomAPI_DataMapOfShapeShape aMapOfShapes;
218     aMapOfShapes.merge(aCutAlgo->mapOfSubShapes());
219     GeomShapePtr aResultShape = aCutAlgo->shape();
220
221     // Add result to not used solids from compsolid.
222     if(!aNotUsedSolids.empty()) {
223       ListOfShape aShapesToAdd = aNotUsedSolids;
224       aShapesToAdd.push_back(aCutAlgo->shape());
225       std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(
226         new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
227       if(!aFillerAlgo->isDone()) {
228         std::string aFeatureError = "Error: PaveFiller algorithm failed.";
229         setError(aFeatureError);
230         return;
231       }
232
233       aMakeShapeList->appendAlgo(aFillerAlgo);
234       aMapOfShapes.merge(aFillerAlgo->mapOfSubShapes());
235       aResultShape = aFillerAlgo->shape();
236     }
237
238     GeomAPI_ShapeIterator aShapeIt(aResultShape);
239     if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX)
240     {
241       std::shared_ptr<ModelAPI_ResultBody> aResultBody =
242         document()->createBody(data(), aResultIndex);
243
244       loadNamingDS(aResultBody,
245                    aCompSolid,
246                    aTools,
247                    aResultShape,
248                    *aMakeShapeList,
249                    aMapOfShapes,
250                    false);
251       setResult(aResultBody, aResultIndex);
252       aResultIndex++;
253
254       ResultBaseAlgo aRBA;
255       aRBA.resultBody = aResultBody;
256       aRBA.baseShape = aCompSolid;
257       aRBA.makeShape = aMakeShapeList;
258       aResultBaseAlgoList.push_back(aRBA);
259       aResultShapesList.push_back(aResultShape);
260     }
261   }
262
263   // Compounds handling
264   for (std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator
265        anIt = aCompoundObjects.begin();
266        anIt != aCompoundObjects.end();
267        ++anIt)
268   {
269     std::shared_ptr<GeomAPI_Shape> aCompound = anIt->first;
270     ListOfShape& aUsedInOperationShapes = anIt->second;
271
272     // Collecting shapes from compound which will not be modified in boolean operation.
273     ListOfShape aNotUsedShapes;
274     for (GeomAPI_ShapeIterator aCompIt(aCompound);
275          aCompIt.more();
276          aCompIt.next())
277     {
278       std::shared_ptr<GeomAPI_Shape> aShapeInCompound = aCompIt.current();
279       ListOfShape::iterator aUsedIt = aUsedInOperationShapes.begin();
280       for (; aUsedIt != aUsedInOperationShapes.end(); aUsedIt++) {
281         if (aShapeInCompound->isEqual(*aUsedIt)) {
282           break;
283         }
284       }
285       if (aUsedIt == aUsedInOperationShapes.end()) {
286         aNotUsedShapes.push_back(aShapeInCompound);
287       }
288     }
289
290     std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
291     std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(
292       new GeomAlgoAPI_Boolean(aUsedInOperationShapes,
293                               aTools,
294                               GeomAlgoAPI_Boolean::BOOL_CUT));
295
296     // Checking that the algorithm worked properly.
297     if (!aCutAlgo->isDone()) {
298       static const std::string aFeatureError = "Error: Boolean algorithm failed.";
299       setError(aFeatureError);
300       return;
301     }
302     if (aCutAlgo->shape()->isNull()) {
303       static const std::string aShapeError = "Error: Resulting shape is Null.";
304       setError(aShapeError);
305       return;
306     }
307     if (!aCutAlgo->isValid()) {
308       std::string aFeatureError = "Error: Resulting shape is not valid.";
309       setError(aFeatureError);
310       return;
311     }
312
313     aMakeShapeList->appendAlgo(aCutAlgo);
314     GeomAPI_DataMapOfShapeShape aMapOfShapes;
315     aMapOfShapes.merge(aCutAlgo->mapOfSubShapes());
316     GeomShapePtr aResultShape = aCutAlgo->shape();
317
318     // Add result to not used shape from compound.
319     if (!aNotUsedShapes.empty()) {
320       ListOfShape aShapesForResult = aNotUsedShapes;
321       if (aResultShape->shapeType() == GeomAPI_Shape::COMPOUND) {
322         for (GeomAPI_ShapeIterator aResultIt(aResultShape); aResultIt.more(); aResultIt.next()) {
323           aShapesForResult.push_back(aResultIt.current());
324         }
325       } else {
326         aShapesForResult.push_back(aResultShape);
327       }
328
329       if (aShapesForResult.size() == 1) {
330         aResultShape = aShapesForResult.front();
331       } else {
332         aResultShape = GeomAlgoAPI_CompoundBuilder::compound(aShapesForResult);
333       }
334     }
335
336     GeomAPI_ShapeIterator aShapeIt(aResultShape);
337     if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX) {
338       std::shared_ptr<ModelAPI_ResultBody> aResultBody =
339         document()->createBody(data(), aResultIndex);
340
341       loadNamingDS(aResultBody,
342                    aCompound,
343                    aTools,
344                    aResultShape,
345                    *aMakeShapeList,
346                    aMapOfShapes,
347                    false);
348       setResult(aResultBody, aResultIndex);
349       aResultIndex++;
350
351       ResultBaseAlgo aRBA;
352       aRBA.resultBody = aResultBody;
353       aRBA.baseShape = aCompound;
354       aRBA.makeShape = aMakeShapeList;
355       aResultBaseAlgoList.push_back(aRBA);
356       aResultShapesList.push_back(aResultShape);
357     }
358   }
359
360   // Store deleted shapes after all results has been proceeded. This is to avoid issue when in one
361   // result shape has been deleted, but in another it was modified or stayed.
362   GeomShapePtr aResultShapesCompound = GeomAlgoAPI_CompoundBuilder::compound(aResultShapesList);
363   storeDeletedShapes(aResultBaseAlgoList, aTools, aResultShapesCompound);
364
365   // remove the rest results if there were produced in the previous pass
366   removeResults(aResultIndex);
367 }
368
369 //==================================================================================================
370 void FeaturesPlugin_BooleanCut::loadNamingDS(ResultBodyPtr theResultBody,
371                                              const GeomShapePtr theBaseShape,
372                                              const ListOfShape& theTools,
373                                              const GeomShapePtr theResultShape,
374                                              GeomAlgoAPI_MakeShape& theMakeShape,
375                                              GeomAPI_DataMapOfShapeShape& theMapOfShapes,
376                                              const bool theIsStoreAsGenerated)
377 {
378   //load result
379   if(theBaseShape->isEqual(theResultShape)) {
380     theResultBody->store(theResultShape, false);
381   } else {
382     theResultBody->storeModified(theBaseShape, theResultShape, SubsolidsTag);
383
384     const std::string aModVName = "Modified_Vertex";
385     const std::string aModEName = "Modified_Edge";
386     const std::string aModFName = "Modified_Face";
387
388     theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::VERTEX,
389                                                ModifyVTag, aModVName, theMapOfShapes, false,
390                                                theIsStoreAsGenerated, true);
391     theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::EDGE,
392                                                ModifyETag, aModEName, theMapOfShapes, false,
393                                                theIsStoreAsGenerated, true);
394     theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE,
395                                                ModifyFTag, aModFName, theMapOfShapes, false,
396                                                theIsStoreAsGenerated, true);
397
398     for (ListOfShape::const_iterator anIter = theTools.begin(); anIter != theTools.end(); anIter++)
399     {
400       theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, GeomAPI_Shape::VERTEX,
401                                                  ModifyVTag, aModVName, theMapOfShapes, false,
402                                                  theIsStoreAsGenerated, true);
403
404       theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, GeomAPI_Shape::EDGE,
405                                                  ModifyETag, aModEName, theMapOfShapes, false,
406                                                  theIsStoreAsGenerated, true);
407
408       theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE,
409                                                  ModifyFTag, aModFName, theMapOfShapes, false,
410                                                  theIsStoreAsGenerated, true);
411     }
412   }
413 }
414
415 //==================================================================================================
416 void FeaturesPlugin_BooleanCut::storeDeletedShapes(
417   std::vector<ResultBaseAlgo>& theResultBaseAlgoList,
418   const ListOfShape& theTools,
419   const GeomShapePtr theResultShapesCompound)
420 {
421   for (std::vector<ResultBaseAlgo>::iterator anIt = theResultBaseAlgoList.begin();
422        anIt != theResultBaseAlgoList.end();
423        ++anIt)
424   {
425     ResultBaseAlgo& aRCA = *anIt;
426     aRCA.resultBody->loadDeletedShapes(aRCA.makeShape.get(),
427                                        aRCA.baseShape,
428                                        GeomAPI_Shape::VERTEX,
429                                        DeletedTag,
430                                        theResultShapesCompound);
431     aRCA.resultBody->loadDeletedShapes(aRCA.makeShape.get(),
432                                        aRCA.baseShape,
433                                        GeomAPI_Shape::EDGE,
434                                        DeletedTag,
435                                        theResultShapesCompound);
436     aRCA.resultBody->loadDeletedShapes(aRCA.makeShape.get(),
437                                        aRCA.baseShape,
438                                        GeomAPI_Shape::FACE,
439                                        DeletedTag,
440                                        theResultShapesCompound);
441
442     for (ListOfShape::const_iterator anIter = theTools.begin(); anIter != theTools.end(); anIter++)
443     {
444       aRCA.resultBody->loadDeletedShapes(aRCA.makeShape.get(),
445                                          *anIter,
446                                          GeomAPI_Shape::VERTEX,
447                                          DeletedTag,
448                                          theResultShapesCompound);
449       aRCA.resultBody->loadDeletedShapes(aRCA.makeShape.get(),
450                                          *anIter,
451                                          GeomAPI_Shape::EDGE,
452                                          DeletedTag,
453                                          theResultShapesCompound);
454       aRCA.resultBody->loadDeletedShapes(aRCA.makeShape.get(),
455                                          *anIter,
456                                          GeomAPI_Shape::FACE,
457                                          DeletedTag,
458                                          theResultShapesCompound);
459     }
460   }
461 }