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