Salome HOME
507cd2da4f7479b556e98867c088088828bd098d
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_CompositeBoolean.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        FeaturesPlugin_CompositeBoolean.cpp
4 // Created:     11 June 2015
5 // Author:      Dmitry Bobylev
6
7 #include "FeaturesPlugin_CompositeBoolean.h"
8
9 #include <ModelAPI_AttributeSelectionList.h>
10 #include <ModelAPI_Tools.h>
11
12 #include <GeomAlgoAPI_Boolean.h>
13 #include <GeomAlgoAPI_MakeShapeList.h>
14 #include <GeomAlgoAPI_PaveFiller.h>
15 #include <GeomAlgoAPI_ShapeTools.h>
16
17 #include <GeomAPI_ShapeExplorer.h>
18
19 #include <map>
20
21 //=================================================================================================
22 void FeaturesPlugin_CompositeBoolean::initBooleanAttributes()
23 {
24   myFeature->data()->addAttribute(OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
25 }
26
27 //=================================================================================================
28 void FeaturesPlugin_CompositeBoolean::executeCompositeBoolean()
29 {
30   // Make generation.
31   ListOfShape aGenBaseShapes;
32   ListOfMakeShape aGenMakeShapes;
33   if(!makeGeneration(aGenBaseShapes, aGenMakeShapes)) {
34     return;
35   }
36
37   // Getting tools.
38   ListOfShape aTools;
39   for(ListOfMakeShape::const_iterator anIt = aGenMakeShapes.cbegin(); anIt != aGenMakeShapes.cend(); ++anIt) {
40     aTools.push_back((*anIt)->shape());
41   }
42
43   // Make boolean.
44   ListOfShape aBooleanObjects;
45   ListOfMakeShape aBooleanMakeShapes;
46   if(!makeBoolean(aTools, aBooleanObjects, aBooleanMakeShapes)) {
47     return;
48   }
49
50   if(myOperationType == BOOL_FUSE) {
51     aTools.splice(aTools.begin(), aBooleanObjects);
52     aBooleanObjects.splice(aBooleanObjects.begin(), aTools, aTools.begin());
53   }
54
55   // Store result.
56   int aResultIndex = 0;
57   ListOfShape::const_iterator aBoolObjIt = aBooleanObjects.cbegin();
58   ListOfMakeShape::const_iterator aBoolMSIt = aBooleanMakeShapes.cbegin();
59   for(; aBoolObjIt != aBooleanObjects.cend() && aBoolMSIt != aBooleanMakeShapes.cend();
60       ++aBoolObjIt, ++aBoolMSIt) {
61
62     int aTag = 1;
63
64     ResultBodyPtr aResultBody = myFeature->document()->createBody(myFeature->data(), aResultIndex);
65     aResultBody->storeModified(*aBoolObjIt, (*aBoolMSIt)->shape(), aTag);
66
67     aTag += 5000;
68
69     // Store generation history.
70     ListOfShape::const_iterator aGenBaseIt = aGenBaseShapes.cbegin();
71     ListOfMakeShape::const_iterator aGenMSIt = aGenMakeShapes.cbegin();
72     for(; aGenBaseIt != aGenBaseShapes.cend() && aGenMSIt != aGenMakeShapes.cend();
73         ++aGenBaseIt, ++aGenMSIt) {
74       storeGenerationHistory(aResultBody, *aGenBaseIt, *aGenMSIt, aTag);
75     }
76
77     int aModTag = aTag;
78     storeModificationHistory(aResultBody, *aBoolObjIt, aTools, *aBoolMSIt, aModTag);
79
80     myFeature->setResult(aResultBody, aResultIndex++);
81   }
82
83   myFeature->removeResults(aResultIndex);
84 }
85
86 //=================================================================================================
87 bool FeaturesPlugin_CompositeBoolean::makeBoolean(const ListOfShape& theTools,
88                                                   ListOfShape& theObjects,
89                                                   ListOfMakeShape& theMakeShapes)
90 {
91   // Getting objects.
92   ListOfShape anObjects, anEdgesAndFaces, aCompSolids;
93   std::map<GeomShapePtr, ListOfShape> aCompSolidsObjects;
94   AttributeSelectionListPtr anObjectsSelList = myFeature->selectionList(OBJECTS_ID());
95   for(int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
96     AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex);
97     GeomShapePtr anObject = anObjectAttr->value();
98     if(!anObject.get()) {
99       myFeature->setError("Error: Could not get object.");
100       return false;
101     }
102     ResultPtr aContext = anObjectAttr->context();
103     ResultCompSolidPtr aResCompSolidPtr = ModelAPI_Tools::compSolidOwner(aContext);
104     if(aResCompSolidPtr.get()) {
105       GeomShapePtr aContextShape = aResCompSolidPtr->shape();
106       std::map<GeomShapePtr, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
107       for(; anIt != aCompSolidsObjects.end(); anIt++) {
108         if(anIt->first->isEqual(aContextShape)) {
109           aCompSolidsObjects[anIt->first].push_back(anObject);
110           break;
111         }
112       }
113       if(anIt == aCompSolidsObjects.end()) {
114         aCompSolidsObjects[aContextShape].push_back(anObject);
115         aCompSolids.push_back(aContextShape);
116       }
117     } else {
118       if(anObject->shapeType() == GeomAPI_Shape::EDGE ||
119          anObject->shapeType() == GeomAPI_Shape::FACE) {
120         anEdgesAndFaces.push_back(anObject);
121       } else {
122         anObjects.push_back(anObject);
123       }
124     }
125   }
126
127   switch(myOperationType) {
128     case BOOL_CUT: {
129       if((anObjects.empty() && aCompSolidsObjects.empty()) || theTools.empty()) {
130         myFeature->setError("Error: Not enough objects for boolean operation.");
131         return false;
132       }
133
134       // For solids cut each object with all tools.
135       for(ListOfShape::const_iterator anIt = anObjects.cbegin(); anIt != anObjects.cend(); ++anIt) {
136         GeomShapePtr anObject = *anIt;
137         ListOfShape aListWithObject;
138         aListWithObject.push_back(anObject);
139         std::shared_ptr<GeomAlgoAPI_Boolean> aBoolAlgo(new GeomAlgoAPI_Boolean(aListWithObject,
140                                                                                theTools,
141                                                                                GeomAlgoAPI_Boolean::BOOL_CUT));
142
143         // Checking that the algorithm worked properly.
144         if(!aBoolAlgo->isDone() || aBoolAlgo->shape()->isNull() || !aBoolAlgo->isValid()) {
145           myFeature->setError("Error: Boolean algorithm failed.");
146           return false;
147         }
148
149         if(GeomAlgoAPI_ShapeTools::volume(aBoolAlgo->shape()) > 1.e-27) {
150           theObjects.push_back(anObject);
151           theMakeShapes.push_back(aBoolAlgo);
152         }
153       }
154
155       // Compsolids handling
156       for(std::map<GeomShapePtr, ListOfShape>::const_iterator anIt = aCompSolidsObjects.cbegin();
157           anIt != aCompSolidsObjects.cend(); ++anIt) {
158         GeomShapePtr aCompSolid = anIt->first;
159         const ListOfShape& aUsedShapes = anIt->second;
160
161         // Collecting solids from compsolids which will not be modified in boolean operation.
162         ListOfShape aShapesToAdd;
163         for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
164           GeomShapePtr aSolidInCompSolid = anExp.current();
165           ListOfShape::const_iterator aUsedShapesIt = aUsedShapes.cbegin();
166           for(; aUsedShapesIt != aUsedShapes.cend(); ++aUsedShapesIt) {
167             if(aSolidInCompSolid->isEqual(*aUsedShapesIt)) {
168               break;
169             }
170           }
171           if(aUsedShapesIt == aUsedShapes.end()) {
172             aShapesToAdd.push_back(aSolidInCompSolid);
173           }
174         }
175
176         std::shared_ptr<GeomAlgoAPI_Boolean> aBoolAlgo(new GeomAlgoAPI_Boolean(aUsedShapes,
177                                                                                theTools,
178                                                                                GeomAlgoAPI_Boolean::BOOL_CUT));
179
180         // Checking that the algorithm worked properly.
181         if(!aBoolAlgo->isDone() || aBoolAlgo->shape()->isNull() || !aBoolAlgo->isValid()) {
182           myFeature->setError("Error: Boolean algorithm failed.");
183           return false;
184         }
185
186         std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
187         aMakeShapeList->appendAlgo(aBoolAlgo);
188
189         // Add result to not used solids from compsolid.
190         aShapesToAdd.push_back(aBoolAlgo->shape());
191         std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
192         if(!aFillerAlgo->isDone() || aFillerAlgo->shape()->isNull() || !aFillerAlgo->isValid()) {
193           myFeature->setError("Error: PaveFiller algorithm failed.");
194           return false;
195         }
196
197         aMakeShapeList->appendAlgo(aFillerAlgo);
198
199         if(GeomAlgoAPI_ShapeTools::volume(aFillerAlgo->shape()) > 1.e-27) {
200           theObjects.push_back(aCompSolid);
201           theMakeShapes.push_back(aMakeShapeList);
202         }
203       }
204       break;
205     }
206     case BOOL_FUSE: {
207       // Set objects.
208       theObjects.insert(theObjects.end(), anEdgesAndFaces.begin(), anEdgesAndFaces.end());
209       theObjects.insert(theObjects.end(), anObjects.begin(), anObjects.end());
210       theObjects.insert(theObjects.end(), aCompSolids.begin(), aCompSolids.end());
211
212       // Filter edges and faces in tools.
213       ListOfShape aTools;
214       for(ListOfShape::const_iterator anIt = theTools.cbegin(); anIt != theTools.cend(); ++anIt) {
215         if((*anIt)->shapeType() == GeomAPI_Shape::EDGE ||
216            (*anIt)->shapeType() == GeomAPI_Shape::FACE) {
217           anEdgesAndFaces.push_back(*anIt);
218         } else {
219           aTools.push_back(*anIt);
220         }
221       }
222
223       if((anObjects.size() + aTools.size() + aCompSolidsObjects.size() + anEdgesAndFaces.size()) < 2) {
224         myFeature->setError("Error: Not enough objects for boolean operation.");
225         return false;
226       }
227
228       // Collecting all solids which will be fused.
229       ListOfShape aSolidsToFuse;
230       aSolidsToFuse.insert(aSolidsToFuse.end(), anObjects.begin(), anObjects.end());
231       aSolidsToFuse.insert(aSolidsToFuse.end(), aTools.begin(), aTools.end());
232
233       // Collecting solids from compsolids which will not be modified in boolean operation and will be added to result.
234       ListOfShape aShapesToAdd;
235       for(std::map<GeomShapePtr, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
236           anIt != aCompSolidsObjects.end(); anIt++) {
237         GeomShapePtr aCompSolid = anIt->first;
238         ListOfShape& aUsedShapes = anIt->second;
239         aSolidsToFuse.insert(aSolidsToFuse.end(), aUsedShapes.begin(), aUsedShapes.end());
240
241         // Collect solids from compsolid which will not be modified in boolean operation.
242         for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
243           GeomShapePtr aSolidInCompSolid = anExp.current();
244           ListOfShape::iterator anIt = aUsedShapes.begin();
245           for(; anIt != aUsedShapes.end(); anIt++) {
246             if(aSolidInCompSolid->isEqual(*anIt)) {
247               break;
248             }
249           }
250           if(anIt == aUsedShapes.end()) {
251             aShapesToAdd.push_back(aSolidInCompSolid);
252           }
253         }
254       }
255
256       // Cut edges and faces(if we have any) with solids.
257       ListOfShape aCutTools;
258       aCutTools.insert(aCutTools.end(), anObjects.begin(), anObjects.end());
259       aCutTools.insert(aCutTools.end(), aCompSolids.begin(), aCompSolids.end());
260       aCutTools.insert(aCutTools.end(), aTools.begin(), aTools.end());
261
262       std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
263       if(!anEdgesAndFaces.empty() && !aCutTools.empty()) {
264         std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(new GeomAlgoAPI_Boolean(anEdgesAndFaces,
265                                                                               aCutTools,
266                                                                               GeomAlgoAPI_Boolean::BOOL_CUT));
267         if(aCutAlgo->isDone() && !aCutAlgo->shape()->isNull() && aCutAlgo->isValid()) {
268           anEdgesAndFaces.clear();
269           anEdgesAndFaces.push_back(aCutAlgo->shape());
270           aMakeShapeList->appendAlgo(aCutAlgo);
271         }
272       }
273
274       // If we have compsolids then cut with not used solids all others.
275       if(!aShapesToAdd.empty()) {
276         std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(new GeomAlgoAPI_Boolean(aSolidsToFuse,
277                                                                               aShapesToAdd,
278                                                                               GeomAlgoAPI_Boolean::BOOL_CUT));
279         if(aCutAlgo->isDone() && GeomAlgoAPI_ShapeTools::volume(aCutAlgo->shape()) > 1.e-27) {
280           aSolidsToFuse.clear();
281           aSolidsToFuse.push_back(aCutAlgo->shape());
282           aMakeShapeList->appendAlgo(aCutAlgo);
283         }
284       }
285
286       // Fuse all objects and all tools.
287       GeomShapePtr aFusedShape;
288       if(aSolidsToFuse.size() == 1) {
289         aFusedShape = aSolidsToFuse.front();
290       } else if(aSolidsToFuse.size() > 1){
291         anObjects.clear();
292         anObjects.push_back(aSolidsToFuse.front());
293         aSolidsToFuse.pop_front();
294         aTools = aSolidsToFuse;
295
296         std::shared_ptr<GeomAlgoAPI_Boolean> aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects,
297                                                                                aTools,
298                                                                                GeomAlgoAPI_Boolean::BOOL_FUSE));
299
300         // Checking that the algorithm worked properly.
301         if(!aFuseAlgo->isDone() || aFuseAlgo->shape()->isNull() || !aFuseAlgo->isValid()) {
302           myFeature->setError("Error: Boolean algorithm failed.");
303           return false;
304         }
305
306         aFusedShape = aFuseAlgo->shape();
307         aMakeShapeList->appendAlgo(aFuseAlgo);
308       }
309
310       // Combine result with not used solids from compsolid and edges and faces (if we have any).
311       aShapesToAdd.insert(aShapesToAdd.end(), anEdgesAndFaces.begin(), anEdgesAndFaces.end());
312       if(!aShapesToAdd.empty()) {
313         if(aFusedShape.get()) {
314           aShapesToAdd.push_back(aFusedShape);
315         }
316
317         std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
318         if(!aFillerAlgo->isDone() || aFillerAlgo->shape()->isNull() || !aFillerAlgo->isValid()) {
319           myFeature->setError("Error: PaveFiller algorithm failed.");
320           return false;
321         }
322
323         aMakeShapeList->appendAlgo(aFillerAlgo);
324       }
325
326       theMakeShapes.push_back(aMakeShapeList);
327       break;
328     }
329   }
330
331   return true;
332 }
333
334 //=================================================================================================
335 void FeaturesPlugin_CompositeBoolean::storeModificationHistory(ResultBodyPtr theResultBody,
336                                 const GeomShapePtr theObject,
337                                 const ListOfShape& theTools,
338                                 const std::shared_ptr<GeomAlgoAPI_MakeShape> theMakeShape,
339                                 int& theTag)
340 {
341   int aModTag = theTag;
342   int anEdgesAndFacesTag = ++aModTag;
343   int aDelTag = ++anEdgesAndFacesTag;
344   theTag = aDelTag;
345
346   const std::string aModName = "Modfied";
347
348   ListOfShape aTools = theTools;
349   aTools.push_back(theObject);
350
351   std::shared_ptr<GeomAPI_DataMapOfShapeShape> aMap = theMakeShape->mapOfSubShapes();
352
353   int aTag;
354   std::string aName;
355   for(ListOfShape::const_iterator anIt = aTools.begin(); anIt != aTools.end(); anIt++) {
356     if((*anIt)->shapeType() == GeomAPI_Shape::EDGE) {
357       aTag = anEdgesAndFacesTag;
358       aName = aModName + "_Edge";
359     }
360     else if((*anIt)->shapeType() == GeomAPI_Shape::FACE) {
361       aTag = anEdgesAndFacesTag;
362       aName = aModName + "_Face";
363     } else {
364       aTag = aModTag;
365       aName = aModName;
366     }
367     theResultBody->loadAndOrientModifiedShapes(theMakeShape.get(), *anIt, (*anIt)->shapeType() == GeomAPI_Shape::EDGE ?
368                                                GeomAPI_Shape::EDGE : GeomAPI_Shape::FACE, aTag, aName, *aMap.get());
369     theResultBody->loadDeletedShapes(theMakeShape.get(), *anIt, GeomAPI_Shape::FACE, aDelTag);
370   }
371 }