Salome HOME
Bug #1170: wrong object names in Main objects field Partition
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_Boolean.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        FeaturesPlugin_Boolean.cpp
4 // Created:     02 Sept 2014
5 // Author:      Vitaly SMETANNIKOV
6
7 #include "FeaturesPlugin_Boolean.h"
8
9 #include <ModelAPI_Data.h>
10 #include <ModelAPI_Document.h>
11 #include <ModelAPI_AttributeReference.h>
12 #include <ModelAPI_AttributeInteger.h>
13 #include <ModelAPI_ResultBody.h>
14 #include <ModelAPI_AttributeSelectionList.h>
15 #include <ModelAPI_Session.h>
16 #include <ModelAPI_Validator.h>
17 #include <ModelAPI_Tools.h>
18
19 #include <GeomAlgoAPI_Boolean.h>
20 #include <GeomAlgoAPI_MakeShapeList.h>
21 #include <GeomAlgoAPI_PaveFiller.h>
22 #include <GeomAlgoAPI_ShapeTools.h>
23 #include <GeomAPI_ShapeExplorer.h>
24
25 #include <algorithm>
26
27 //=================================================================================================
28 FeaturesPlugin_Boolean::FeaturesPlugin_Boolean()
29 {
30 }
31
32 //=================================================================================================
33 void FeaturesPlugin_Boolean::initAttributes()
34 {
35   data()->addAttribute(FeaturesPlugin_Boolean::TYPE_ID(), ModelAPI_AttributeInteger::typeId());
36
37   AttributeSelectionListPtr aSelection = 
38     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(data()->addAttribute(
39     FeaturesPlugin_Boolean::OBJECT_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()));
40   // extrusion works with faces always
41   aSelection->setSelectionType("SOLID");
42
43   aSelection = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(data()->addAttribute(
44     FeaturesPlugin_Boolean::TOOL_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()));
45   // extrusion works with faces always
46   aSelection->setSelectionType("SOLID");
47
48   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), OBJECT_LIST_ID());
49   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), TOOL_LIST_ID());
50 }
51
52 //=================================================================================================
53 std::shared_ptr<GeomAPI_Shape> FeaturesPlugin_Boolean::getShape(const std::string& theAttrName)
54 {
55   std::shared_ptr<ModelAPI_AttributeReference> aObjRef = std::dynamic_pointer_cast<
56       ModelAPI_AttributeReference>(data()->attribute(theAttrName));
57   if (aObjRef) {
58     std::shared_ptr<ModelAPI_ResultBody> aConstr = std::dynamic_pointer_cast<
59         ModelAPI_ResultBody>(aObjRef->value());
60     if (aConstr)
61       return aConstr->shape();
62   }
63   return std::shared_ptr<GeomAPI_Shape>();
64 }
65
66 //=================================================================================================
67 void FeaturesPlugin_Boolean::execute()
68 {
69   // Getting operation type.
70   std::shared_ptr<ModelAPI_AttributeInteger> aTypeAttr = std::dynamic_pointer_cast<
71       ModelAPI_AttributeInteger>(data()->attribute(FeaturesPlugin_Boolean::TYPE_ID()));
72   if (!aTypeAttr)
73     return;
74   GeomAlgoAPI_Boolean::OperationType aType = (GeomAlgoAPI_Boolean::OperationType)aTypeAttr->value();
75
76   ListOfShape anObjects, aTools;
77   std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape> aCompSolidsObjects;
78
79   // Getting objects.
80   AttributeSelectionListPtr anObjectsSelList = selectionList(FeaturesPlugin_Boolean::OBJECT_LIST_ID());
81   for(int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
82     AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex);
83     std::shared_ptr<GeomAPI_Shape> anObject = anObjectAttr->value();
84     if(!anObject.get()) {
85       return;
86     }
87     ResultPtr aContext = anObjectAttr->context();
88     ResultCompSolidPtr aResCompSolidPtr = ModelAPI_Tools::compSolidOwner(aContext);
89     if(aResCompSolidPtr.get()) {
90       std::shared_ptr<GeomAPI_Shape> aContextShape = aResCompSolidPtr->shape();
91       std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
92       for(; anIt != aCompSolidsObjects.end(); anIt++) {
93         if(anIt->first->isEqual(aContextShape)) {
94           aCompSolidsObjects[anIt->first].push_back(anObject);
95           break;
96         }
97       }
98       if(anIt == aCompSolidsObjects.end()) {
99         aCompSolidsObjects[aContextShape].push_back(anObject);
100       }
101     } else {
102       anObjects.push_back(anObject);
103     }
104   }
105
106   // Getting tools.
107   AttributeSelectionListPtr aToolsSelList = selectionList(FeaturesPlugin_Boolean::TOOL_LIST_ID());
108   for(int aToolsIndex = 0; aToolsIndex < aToolsSelList->size(); aToolsIndex++) {
109     AttributeSelectionPtr aToolAttr = aToolsSelList->value(aToolsIndex);
110     std::shared_ptr<GeomAPI_Shape> aTool = aToolAttr->value();
111     if(!aTool.get()) {
112       return;
113     }
114     aTools.push_back(aTool);
115   }
116
117   int aResultIndex = 0;
118
119   switch(aType) {
120     case GeomAlgoAPI_Boolean::BOOL_CUT:
121     case GeomAlgoAPI_Boolean::BOOL_COMMON:{
122       if((anObjects.empty() && aCompSolidsObjects.empty()) || aTools.empty()) {
123         std::string aFeatureError = "Not enough objects for boolean operation";
124         setError(aFeatureError);
125         return;
126       }
127
128       // For solids cut each object with all tools.
129       for(ListOfShape::iterator anObjectsIt = anObjects.begin(); anObjectsIt != anObjects.end(); anObjectsIt++) {
130         std::shared_ptr<GeomAPI_Shape> anObject = *anObjectsIt;
131         ListOfShape aListWithObject;
132         aListWithObject.push_back(anObject);
133         GeomAlgoAPI_Boolean aBoolAlgo(aListWithObject, aTools, aType);
134
135         // Checking that the algorithm worked properly.
136         if(!aBoolAlgo.isDone()) {
137           static const std::string aFeatureError = "Boolean algorithm failed";
138           setError(aFeatureError);
139           return;
140         }
141         if(aBoolAlgo.shape()->isNull()) {
142           static const std::string aShapeError = "Resulting shape is Null";
143           setError(aShapeError);
144           return;
145         }
146         if(!aBoolAlgo.isValid()) {
147           std::string aFeatureError = "Warning: resulting shape is not valid";
148           setError(aFeatureError);
149           return;
150         }
151
152         if(GeomAlgoAPI_ShapeTools::volume(aBoolAlgo.shape()) > 1.e-7) {
153           std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
154           loadNamingDS(aResultBody, anObject, aTools, aBoolAlgo.shape(), aBoolAlgo, *aBoolAlgo.mapOfSubShapes().get());
155           setResult(aResultBody, aResultIndex);
156           aResultIndex++;
157         }
158       }
159
160       // Compsolids handling
161       for(std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
162         anIt != aCompSolidsObjects.end(); anIt++) {
163         std::shared_ptr<GeomAPI_Shape> aCompSolid = anIt->first;
164         ListOfShape& aUsedInOperationSolids = anIt->second;
165
166         // Collecting solids from compsolids which will not be modified in boolean operation.
167         ListOfShape aNotUsedSolids;
168         for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
169           std::shared_ptr<GeomAPI_Shape> aSolidInCompSolid = anExp.current();
170           ListOfShape::iterator anIt = aUsedInOperationSolids.begin();
171           for(; anIt != aUsedInOperationSolids.end(); anIt++) {
172             if(aSolidInCompSolid->isEqual(*anIt)) {
173               break;
174             }
175           }
176           if(anIt == aUsedInOperationSolids.end()) {
177             aNotUsedSolids.push_back(aSolidInCompSolid);
178           }
179         }
180
181         std::shared_ptr<GeomAlgoAPI_Boolean> aBoolAlgo(new GeomAlgoAPI_Boolean(aUsedInOperationSolids, aTools, aType));
182
183         // Checking that the algorithm worked properly.
184         if(!aBoolAlgo->isDone()) {
185           static const std::string aFeatureError = "Boolean algorithm failed";
186           setError(aFeatureError);
187           return;
188         }
189         if(aBoolAlgo->shape()->isNull()) {
190           static const std::string aShapeError = "Resulting shape is Null";
191           setError(aShapeError);
192           return;
193         }
194         if(!aBoolAlgo->isValid()) {
195           std::string aFeatureError = "Warning: resulting shape is not valid";
196           setError(aFeatureError);
197           return;
198         }
199
200         GeomAlgoAPI_MakeShapeList aMakeShapeList;
201         aMakeShapeList.appendAlgo(aBoolAlgo);
202         GeomAPI_DataMapOfShapeShape aMapOfShapes;
203         aMapOfShapes.merge(aBoolAlgo->mapOfSubShapes());
204
205         // Add result to not used solids from compsolid.
206         ListOfShape aShapesToAdd = aNotUsedSolids;
207         aShapesToAdd.push_back(aBoolAlgo->shape());
208         std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
209         if(!aFillerAlgo->isDone()) {
210           std::string aFeatureError = "PaveFiller algorithm failed";
211           setError(aFeatureError);
212           return;
213         }
214
215         aMakeShapeList.appendAlgo(aFillerAlgo);
216         aMapOfShapes.merge(aFillerAlgo->mapOfSubShapes());
217
218         if(GeomAlgoAPI_ShapeTools::volume(aFillerAlgo->shape()) > 1.e-7) {
219           std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
220           loadNamingDS(aResultBody, aCompSolid, aTools, aFillerAlgo->shape(), aMakeShapeList, aMapOfShapes);
221           setResult(aResultBody, aResultIndex);
222           aResultIndex++;
223         }
224       }
225       break;
226     }
227     case GeomAlgoAPI_Boolean::BOOL_FUSE: {
228       if((anObjects.size() + aTools.size() + aCompSolidsObjects.size()) < 2) {
229         std::string aFeatureError = "Not enough objects for boolean operation";
230         setError(aFeatureError);
231         return;
232       }
233
234       // Collecting all solids which will be fused.
235       ListOfShape aSolidsToFuse;
236       aSolidsToFuse.insert(aSolidsToFuse.end(), anObjects.begin(), anObjects.end());
237       aSolidsToFuse.insert(aSolidsToFuse.end(), aTools.begin(), aTools.end());
238
239       // Collecting solids from compsolids which will not be modified in boolean operation.
240       ListOfShape aNotUsedSolids;
241       for(std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
242         anIt != aCompSolidsObjects.end(); anIt++) {
243         std::shared_ptr<GeomAPI_Shape> aCompSolid = anIt->first;
244         ListOfShape& aUsedInOperationSolids = anIt->second;
245         aSolidsToFuse.insert(aSolidsToFuse.end(), aUsedInOperationSolids.begin(), aUsedInOperationSolids.end());
246
247         // Collect solids from compsolid which will not be modified in boolean operation.
248         for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
249           std::shared_ptr<GeomAPI_Shape> aSolidInCompSolid = anExp.current();
250           ListOfShape::iterator anIt = aUsedInOperationSolids.begin();
251           for(; anIt != aUsedInOperationSolids.end(); anIt++) {
252             if(aSolidInCompSolid->isEqual(*anIt)) {
253               break;
254             }
255           }
256           if(anIt == aUsedInOperationSolids.end()) {
257             aNotUsedSolids.push_back(aSolidInCompSolid);
258           }
259         }
260       }
261
262       ListOfShape anOriginalSolids = aSolidsToFuse;
263       anOriginalSolids.insert(anOriginalSolids.end(), aNotUsedSolids.begin(), aNotUsedSolids.end());
264       GeomAlgoAPI_MakeShapeList aMakeShapeList;
265       GeomAPI_DataMapOfShapeShape aMapOfShapes;
266
267       // If we have compsolids then cut with not used solids all others.
268       if(!aNotUsedSolids.empty()) {
269         aSolidsToFuse.clear();
270         for(ListOfShape::iterator anIt = anOriginalSolids.begin(); anIt != anOriginalSolids.end(); anIt++) {
271           ListOfShape aOneObjectList;
272           aOneObjectList.push_back(*anIt);
273           std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(new GeomAlgoAPI_Boolean(aOneObjectList, aNotUsedSolids, GeomAlgoAPI_Boolean::BOOL_CUT));
274
275           if(GeomAlgoAPI_ShapeTools::volume(aCutAlgo->shape()) > 1.e-7) {
276             aSolidsToFuse.push_back(aCutAlgo->shape());
277             aMakeShapeList.appendAlgo(aCutAlgo);
278             aMapOfShapes.merge(aCutAlgo->mapOfSubShapes());
279           }
280         }
281       }
282
283       anObjects.clear();
284       anObjects.push_back(aSolidsToFuse.back());
285       aSolidsToFuse.pop_back();
286       aTools = aSolidsToFuse;
287
288       // Fuse all objects and all tools.
289       std::shared_ptr<GeomAlgoAPI_Boolean> aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects, aTools, aType));
290
291       // Checking that the algorithm worked properly.
292       if(!aFuseAlgo->isDone()) {
293         static const std::string aFeatureError = "Boolean algorithm failed";
294         setError(aFeatureError);
295         return;
296       }
297       if(aFuseAlgo->shape()->isNull()) {
298         static const std::string aShapeError = "Resulting shape is Null";
299         setError(aShapeError);
300         return;
301       }
302       if(!aFuseAlgo->isValid()) {
303         std::string aFeatureError = "Warning: resulting shape is not valid";
304         setError(aFeatureError);
305         return;
306       }
307
308       std::shared_ptr<GeomAPI_Shape> aShape = aFuseAlgo->shape();
309       aMakeShapeList.appendAlgo(aFuseAlgo);
310       aMapOfShapes.merge(aFuseAlgo->mapOfSubShapes());
311
312       // Add result to not used solids from compsolid (if we have any).
313       if(!aNotUsedSolids.empty()) {
314         aNotUsedSolids.push_back(aShape);
315         std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(new GeomAlgoAPI_PaveFiller(aNotUsedSolids, true));
316         if(!aFillerAlgo->isDone()) {
317           std::string aFeatureError = "PaveFiller algorithm failed";
318           setError(aFeatureError);
319           return;
320         }
321         if(aFillerAlgo->shape()->isNull()) {
322           static const std::string aShapeError = "Resulting shape is Null";
323           setError(aShapeError);
324           return;
325         }
326         if(!aFillerAlgo->isValid()) {
327           std::string aFeatureError = "Warning: resulting shape is not valid";
328           setError(aFeatureError);
329           return;
330         }
331
332         aShape = aFillerAlgo->shape();
333         aMakeShapeList.appendAlgo(aFillerAlgo);
334         aMapOfShapes.merge(aFillerAlgo->mapOfSubShapes());
335       }
336
337       std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
338       loadNamingDS(aResultBody, anOriginalSolids.front(), anOriginalSolids, aShape, aMakeShapeList, aMapOfShapes);
339       setResult(aResultBody, aResultIndex);
340       aResultIndex++;
341       break;
342     }
343     default: {
344       std::string anOperationError = "Error: wrong type of operation";
345       setError(anOperationError);
346       return;
347     }
348   }
349   // remove the rest results if there were produced in the previous pass
350   removeResults(aResultIndex);
351 }
352
353 //=================================================================================================
354 void FeaturesPlugin_Boolean::loadNamingDS(std::shared_ptr<ModelAPI_ResultBody> theResultBody,
355                                           const std::shared_ptr<GeomAPI_Shape> theBaseShape,
356                                           const ListOfShape& theTools,
357                                           const std::shared_ptr<GeomAPI_Shape> theResultShape,
358                                           GeomAlgoAPI_MakeShape& theMakeShape,
359                                           GeomAPI_DataMapOfShapeShape& theMapOfShapes)
360 {
361   //load result
362   if(theBaseShape->isEqual(theResultShape)) {
363     theResultBody->store(theResultShape);
364   } else {
365     const int aModifyTag = 1;
366     const int aDeletedTag = 2;
367     const int aSubsolidsTag = 3; /// sub solids will be placed at labels 3, 4, etc. if result is compound of solids
368
369     theResultBody->storeModified(theBaseShape, theResultShape, aSubsolidsTag);
370
371     std::string aModName = "Modified";
372     theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE,
373                                                aModifyTag, aModName, theMapOfShapes);
374     theResultBody->loadDeletedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE, aDeletedTag);
375
376     for(ListOfShape::const_iterator anIter = theTools.begin(); anIter != theTools.end(); anIter++) {
377       theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE,
378                                                  aModifyTag, aModName, theMapOfShapes);
379       theResultBody->loadDeletedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE, aDeletedTag);
380     }
381   }
382 }