Salome HOME
GeomAlgoAPI_Prism now derived from GeomAlgoAPI_MakeSweep
[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_AttributeReference.h>
11 #include <ModelAPI_ResultBody.h>
12 #include <ModelAPI_ResultConstruction.h>
13 #include <ModelAPI_Session.h>
14 #include <ModelAPI_Tools.h>
15 #include <ModelAPI_Validator.h>
16
17 #include <GeomAlgoAPI_CompoundBuilder.h>
18 #include <GeomAlgoAPI_MakeShapeList.h>
19 #include <GeomAlgoAPI_PaveFiller.h>
20 #include <GeomAlgoAPI_Prism.h>
21 #include <GeomAlgoAPI_Revolution.h>
22 #include <GeomAlgoAPI_ShapeTools.h>
23 #include <GeomAPI_ShapeExplorer.h>
24
25 //=================================================================================================
26 void FeaturesPlugin_CompositeBoolean::initAttributes()
27 {
28   data()->addAttribute(SKETCH_OBJECT_ID(), ModelAPI_AttributeReference::typeId());
29
30   // Boolean works with solids always.
31   data()->addAttribute(BOOLEAN_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
32   AttributeSelectionListPtr aSelection = data()->selectionList(BOOLEAN_OBJECTS_ID());
33   aSelection->setSelectionType("SOLID");
34
35   initMakeSolidsAttributes();
36
37   data()->addAttribute(SKETCH_SELECTION_ID(), ModelAPI_AttributeSelection::typeId());
38   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SKETCH_SELECTION_ID());
39 }
40
41 //=================================================================================================
42 std::shared_ptr<ModelAPI_Feature> FeaturesPlugin_CompositeBoolean::addFeature(std::string theID)
43 {
44   std::shared_ptr<ModelAPI_Feature> aNew = document()->addFeature(theID, false);
45   if (aNew) {
46     data()->reference(SKETCH_OBJECT_ID())->setValue(aNew);
47   }
48   // set as current also after it becomes sub to set correctly enabled for other sketch subs
49   document()->setCurrentFeature(aNew, false);
50   return aNew;
51 }
52
53 //=================================================================================================
54 int FeaturesPlugin_CompositeBoolean::numberOfSubs(bool forTree) const
55 {
56   ObjectPtr aObj = data()->reference(SKETCH_OBJECT_ID())->value();
57   return aObj.get()? 1 : 0;
58 }
59
60 //=================================================================================================
61 std::shared_ptr<ModelAPI_Feature> FeaturesPlugin_CompositeBoolean::subFeature(const int theIndex, bool forTree)
62 {
63   if (theIndex == 0)
64     return std::dynamic_pointer_cast<ModelAPI_Feature>(data()->reference(SKETCH_OBJECT_ID())->value());
65   return std::shared_ptr<ModelAPI_Feature>();
66 }
67
68 //=================================================================================================
69 int FeaturesPlugin_CompositeBoolean::subFeatureId(const int theIndex) const
70 {
71   if (theIndex == 0) {
72     FeaturePtr aFeature = 
73       std::dynamic_pointer_cast<ModelAPI_Feature>(data()->reference(SKETCH_OBJECT_ID())->value());
74     if (aFeature.get())
75       return aFeature->data()->featureId();
76   }
77   return -1;
78 }
79
80 //=================================================================================================
81 bool FeaturesPlugin_CompositeBoolean::isSub(ObjectPtr theObject) const
82 {
83   // check is this feature of result
84   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
85   if (!aFeature)
86     return false;
87  
88   ObjectPtr aSub = data()->reference(SKETCH_OBJECT_ID())->value();
89   return aSub == theObject;
90 }
91
92 //=================================================================================================
93 void FeaturesPlugin_CompositeBoolean::removeFeature(std::shared_ptr<ModelAPI_Feature> theFeature)
94 {
95 }
96
97 //=================================================================================================
98 void FeaturesPlugin_CompositeBoolean::erase()
99 {
100   if (data().get() && data()->isValid()) { // on abort of sketch of this composite it may be invalid
101     FeaturePtr aSketch =
102       std::dynamic_pointer_cast<ModelAPI_Feature>(data()->reference(SKETCH_OBJECT_ID())->value());
103     if (aSketch.get() && aSketch->data()->isValid()) {
104       document()->removeFeature(aSketch);
105     }
106   }
107   ModelAPI_CompositeFeature::erase();
108 }
109
110
111 //=================================================================================================
112 void FeaturesPlugin_CompositeBoolean::execute()
113 {
114   // Getting faces to create solids.
115   std::shared_ptr<ModelAPI_Feature> aSketchFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(
116                                                      reference(SKETCH_OBJECT_ID())->value());
117   if(!aSketchFeature || aSketchFeature->results().empty()) {
118     return;
119   }
120   ResultPtr aSketchRes = aSketchFeature->results().front();
121   ResultConstructionPtr aConstruction = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aSketchRes);
122   if(!aConstruction.get()) {
123     return;
124   }
125   selection(SKETCH_SELECTION_ID())->setValue(aSketchRes, std::shared_ptr<GeomAPI_Shape>());
126   int aSketchFacesNum = aConstruction->facesNum();
127   if(aSketchFacesNum == 0) {
128     return;
129   }
130   ListOfShape aFacesList;
131   for(int aFaceIndex = 0; aFaceIndex < aSketchFacesNum; aFaceIndex++) {
132     std::shared_ptr<GeomAPI_Shape> aFace = std::dynamic_pointer_cast<GeomAPI_Shape>(aConstruction->face(aFaceIndex));
133     aFacesList.push_back(aFace);
134   }
135
136   // Searching faces with common edges.
137   ListOfShape aShells;
138   ListOfShape aFreeFaces;
139   std::shared_ptr<GeomAPI_Shape> aFacesCompound = GeomAlgoAPI_CompoundBuilder::compound(aFacesList);
140   GeomAlgoAPI_ShapeTools::combineShapes(aFacesCompound, GeomAPI_Shape::SHELL, aShells, aFreeFaces);
141   aShells.insert(aShells.end(), aFreeFaces.begin(), aFreeFaces.end());
142
143   // Pass shells/faces to solids creation function.
144   ListOfShape aTools;
145   std::list<std::shared_ptr<GeomAPI_Interface>> aSolidsAlgos;
146   makeSolids(aShells, aTools, aSolidsAlgos);
147   if(aTools.empty()) {
148     return;
149   }
150
151   // Getting objects for boolean operation.
152   ListOfShape anObjects;
153   std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape> aCompSolidsObjects;
154   AttributeSelectionListPtr anObjectsSelList = selectionList(BOOLEAN_OBJECTS_ID());
155   if(anObjectsSelList->size() == 0) {
156     return;
157   }
158   for(int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
159     AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex);
160     std::shared_ptr<GeomAPI_Shape> anObject = anObjectAttr->value();
161     if(!anObject.get()) {
162       return;
163     }
164     ResultPtr aContext = anObjectAttr->context();
165     ResultCompSolidPtr aResCompSolidPtr = ModelAPI_Tools::compSolidOwner(aContext);
166     if(aResCompSolidPtr.get()) {
167       std::shared_ptr<GeomAPI_Shape> aContextShape = aResCompSolidPtr->shape();
168       std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
169       for(; anIt != aCompSolidsObjects.end(); anIt++) {
170         if(anIt->first->isEqual(aContextShape)) {
171           aCompSolidsObjects[anIt->first].push_back(anObject);
172           break;
173         }
174       }
175       if(anIt == aCompSolidsObjects.end()) {
176         aCompSolidsObjects[aContextShape].push_back(anObject);
177       }
178     } else {
179       anObjects.push_back(anObject);
180     }
181   }
182
183   // Cut from each object solids.
184   int aResultIndex = 0;
185
186   switch(myBooleanOperationType) {
187     case GeomAlgoAPI_Boolean::BOOL_CUT:
188     case GeomAlgoAPI_Boolean::BOOL_COMMON:{
189       // Cut each object with all tools
190       for(ListOfShape::iterator anObjectsIt = anObjects.begin(); anObjectsIt != anObjects.end(); anObjectsIt++) {
191         std::shared_ptr<GeomAPI_Shape> anObject = *anObjectsIt;
192         ListOfShape aListWithObject;
193         aListWithObject.push_back(anObject);
194         GeomAlgoAPI_Boolean aBoolAlgo(aListWithObject, aTools, myBooleanOperationType);
195
196         // Checking that the algorithm worked properly.
197         if(!aBoolAlgo.isDone() || aBoolAlgo.shape()->isNull() || !aBoolAlgo.isValid()) {
198           setError("Boolean algorithm failed");
199           return;
200         }
201
202         if(GeomAlgoAPI_ShapeTools::volume(aBoolAlgo.shape()) > 1.e-7) {
203           std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
204           loadNamingDS(aResultBody, aShells, aSolidsAlgos, anObject, aTools, aBoolAlgo.shape(),
205                        *aBoolAlgo.makeShape(), *aBoolAlgo.mapOfShapes());
206           setResult(aResultBody, aResultIndex);
207           aResultIndex++;
208         }
209       }
210
211       // Compsolids handling
212       for(std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
213         anIt != aCompSolidsObjects.end(); anIt++) {
214         std::shared_ptr<GeomAPI_Shape> aCompSolid = anIt->first;
215         ListOfShape& aUsedInOperationSolids = anIt->second;
216
217         // Collecting solids from compsolids which will not be modified in boolean operation.
218         ListOfShape aNotUsedSolids;
219         for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
220           std::shared_ptr<GeomAPI_Shape> aSolidInCompSolid = anExp.current();
221           ListOfShape::iterator anIt = aUsedInOperationSolids.begin();
222           for(; anIt != aUsedInOperationSolids.end(); anIt++) {
223             if(aSolidInCompSolid->isEqual(*anIt)) {
224               break;
225             }
226           }
227           if(anIt == aUsedInOperationSolids.end()) {
228             aNotUsedSolids.push_back(aSolidInCompSolid);
229           }
230         }
231
232         GeomAlgoAPI_Boolean aBoolAlgo(aUsedInOperationSolids, aTools, myBooleanOperationType);
233
234         // Checking that the algorithm worked properly.
235         if(!aBoolAlgo.isDone() || aBoolAlgo.shape()->isNull() || !aBoolAlgo.isValid()) {
236           setError("Boolean algorithm failed");
237           return;
238         }
239
240         GeomAlgoAPI_MakeShapeList aMakeShapeList;
241         aMakeShapeList.appendAlgo(aBoolAlgo.makeShape());
242         GeomAPI_DataMapOfShapeShape aMapOfShapes;
243         aMapOfShapes.merge(aBoolAlgo.mapOfShapes());
244
245         // Add result to not used solids from compsolid.
246         ListOfShape aShapesToAdd = aNotUsedSolids;
247         aShapesToAdd.push_back(aBoolAlgo.shape());
248         GeomAlgoAPI_PaveFiller aFillerAlgo(aShapesToAdd, true);
249         if(!aFillerAlgo.isDone()) {
250           std::string aFeatureError = "PaveFiller algorithm failed";
251           setError(aFeatureError);
252           return;
253         }
254
255         aMakeShapeList.appendAlgo(aFillerAlgo.makeShape());
256         aMapOfShapes.merge(aFillerAlgo.mapOfShapes());
257
258         if(GeomAlgoAPI_ShapeTools::volume(aFillerAlgo.shape()) > 1.e-7) {
259           std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
260           loadNamingDS(aResultBody, aShells, aSolidsAlgos, aCompSolid, aTools, aFillerAlgo.shape(), aMakeShapeList, aMapOfShapes);
261           setResult(aResultBody, aResultIndex);
262           aResultIndex++;
263         }
264       }
265       break;
266     }
267     case GeomAlgoAPI_Boolean::BOOL_FUSE: {
268       // Collecting all solids which will be fused.
269       ListOfShape aSolidsToFuse;
270       aSolidsToFuse.insert(aSolidsToFuse.end(), anObjects.begin(), anObjects.end());
271       aSolidsToFuse.insert(aSolidsToFuse.end(), aTools.begin(), aTools.end());
272
273       // Collecting solids from compsolids which will not be modified in boolean operation.
274       ListOfShape aNotUsedSolids;
275       for(std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
276         anIt != aCompSolidsObjects.end(); anIt++) {
277         std::shared_ptr<GeomAPI_Shape> aCompSolid = anIt->first;
278         ListOfShape& aUsedInOperationSolids = anIt->second;
279         aSolidsToFuse.insert(aSolidsToFuse.end(), aUsedInOperationSolids.begin(), aUsedInOperationSolids.end());
280
281         // Collect solids from compsolid which will not be modified in boolean operation.
282         for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
283           std::shared_ptr<GeomAPI_Shape> aSolidInCompSolid = anExp.current();
284           ListOfShape::iterator anIt = aUsedInOperationSolids.begin();
285           for(; anIt != aUsedInOperationSolids.end(); anIt++) {
286             if(aSolidInCompSolid->isEqual(*anIt)) {
287               break;
288             }
289           }
290           if(anIt == aUsedInOperationSolids.end()) {
291             aNotUsedSolids.push_back(aSolidInCompSolid);
292           }
293         }
294       }
295
296       ListOfShape anOriginalSolids = aSolidsToFuse;
297       anOriginalSolids.insert(anOriginalSolids.end(), aNotUsedSolids.begin(), aNotUsedSolids.end());
298       GeomAlgoAPI_MakeShapeList aMakeShapeList;
299       GeomAPI_DataMapOfShapeShape aMapOfShapes;
300
301       // If we have compsolids then cut with not used solids all others.
302       if(!aNotUsedSolids.empty()) {
303         aSolidsToFuse.clear();
304         for(ListOfShape::iterator anIt = anOriginalSolids.begin(); anIt != anOriginalSolids.end(); anIt++) {
305           ListOfShape aOneObjectList;
306           aOneObjectList.push_back(*anIt);
307           GeomAlgoAPI_Boolean aCutAlgo(aOneObjectList, aNotUsedSolids, GeomAlgoAPI_Boolean::BOOL_CUT);
308
309           if(GeomAlgoAPI_ShapeTools::volume(aCutAlgo.shape()) > 1.e-7) {
310             aSolidsToFuse.push_back(aCutAlgo.shape());
311             aMakeShapeList.appendAlgo(aCutAlgo.makeShape());
312             aMapOfShapes.merge(aCutAlgo.mapOfShapes());
313           }
314         }
315       }
316
317       anObjects.clear();
318       anObjects.push_back(aSolidsToFuse.back());
319       aSolidsToFuse.pop_back();
320       aTools = aSolidsToFuse;
321
322       // Fuse all objects and all tools.
323       GeomAlgoAPI_Boolean aFuseAlgo(anObjects, aTools, myBooleanOperationType);
324
325       // Checking that the algorithm worked properly.
326       if(!aFuseAlgo.isDone() || aFuseAlgo.shape()->isNull() || !aFuseAlgo.isValid()) {
327         static const std::string aFeatureError = "Boolean algorithm failed";
328         setError(aFeatureError);
329         return;
330       }
331
332       std::shared_ptr<GeomAPI_Shape> aShape = aFuseAlgo.shape();
333       aMakeShapeList.appendAlgo(aFuseAlgo.makeShape());
334       aMapOfShapes.merge(aFuseAlgo.mapOfShapes());
335
336       // Add result to not used solids from compsolid (if we have any).
337       if(!aNotUsedSolids.empty()) {
338         aNotUsedSolids.push_back(aShape);
339         GeomAlgoAPI_PaveFiller aFillerAlgo(aNotUsedSolids, true);
340         if(!aFillerAlgo.isDone()) {
341           std::string aFeatureError = "PaveFiller algorithm failed";
342           setError(aFeatureError);
343           return;
344         }
345         if(aFillerAlgo.shape()->isNull()) {
346           static const std::string aShapeError = "Resulting shape is Null";
347           setError(aShapeError);
348           return;
349         }
350         if(!aFillerAlgo.isValid()) {
351           std::string aFeatureError = "Warning: resulting shape is not valid";
352           setError(aFeatureError);
353           return;
354         }
355
356         aShape = aFillerAlgo.shape();
357         aMakeShapeList.appendAlgo(aFillerAlgo.makeShape());
358         aMapOfShapes.merge(aFillerAlgo.mapOfShapes());
359       }
360
361       std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
362       loadNamingDS(aResultBody, aShells, aSolidsAlgos, anOriginalSolids.front(), anOriginalSolids, aShape, aMakeShapeList, aMapOfShapes);
363       setResult(aResultBody, aResultIndex);
364       aResultIndex++;
365       break;
366     }
367     default: {
368       setError("Error: wrong type of boolean operation");
369       return;
370     }
371   }
372
373   // Remove the rest results if there were produced in the previous pass.
374   removeResults(aResultIndex);
375 }
376
377 //=================================================================================================
378 void FeaturesPlugin_CompositeBoolean::loadNamingDS(std::shared_ptr<ModelAPI_ResultBody> theResultBody,
379                                                    const ListOfShape& theShells,
380                                                    const std::list<std::shared_ptr<GeomAPI_Interface>>& theSolidsAlgos,
381                                                    const std::shared_ptr<GeomAPI_Shape> theBaseShape,
382                                                    const ListOfShape& theTools,
383                                                    const std::shared_ptr<GeomAPI_Shape> theResultShape,
384                                                    GeomAlgoAPI_MakeShape& theMakeShape,
385                                                    GeomAPI_DataMapOfShapeShape& theMapOfShapes)
386 {
387   //load result
388   if(theBaseShape->isEqual(theResultShape)) {
389     theResultBody->store(theResultShape);
390   } else {
391     const int aGenTag = 1;
392     const int aModTag = 2;
393     const int aDelTag = 3;
394     const int aSubsolidsTag=4; /// sub solids will be placed at labels 6, 7, etc. if result is compound of solids
395     int aToTag = 5; // may be many labels, starting from this index
396     int aFromTag = 10000; // may be many labels, starting from this index or last aToTag index
397     const std::string aGenName = "Generated";
398     const std::string aModName = "Modified";
399     const std::string aLatName = "LateralFace";
400     const std::string aFromName = "FromFace";
401     const std::string aToName = "ToFace";
402
403     theResultBody->storeModified(theBaseShape, theResultShape, aSubsolidsTag);
404
405     ListOfShape::const_iterator aShellsIter = theShells.begin();
406     std::list<std::shared_ptr<GeomAPI_Interface>>::const_iterator aSolidsAlgosIter = theSolidsAlgos.begin();
407     for(; aShellsIter != theShells.end() && aSolidsAlgosIter != theSolidsAlgos.end(); aShellsIter++, aSolidsAlgosIter++) {
408       ListOfShape aFromFaces;
409       ListOfShape aToFaces;
410       std::shared_ptr<GeomAPI_DataMapOfShapeShape> aSubShapes;
411
412       //Insert lateral face : Face from Edge
413       if(std::dynamic_pointer_cast<GeomAlgoAPI_Prism>(*aSolidsAlgosIter)) {
414         std::shared_ptr<GeomAlgoAPI_Prism> aPrismAlgo = std::dynamic_pointer_cast<GeomAlgoAPI_Prism>(*aSolidsAlgosIter);
415         aSubShapes = aPrismAlgo->mapOfSubShapes();
416         theResultBody->loadAndOrientGeneratedShapes(aPrismAlgo.get(), *aShellsIter, GeomAPI_Shape::EDGE, aGenTag,
417                                                     aLatName, *aSubShapes.get());
418
419         aFromFaces = aPrismAlgo->fromFaces();
420         aToFaces = aPrismAlgo->toFaces();
421       } else if(std::dynamic_pointer_cast<GeomAlgoAPI_Revolution>(*aSolidsAlgosIter)) {
422         std::shared_ptr<GeomAlgoAPI_Revolution> aRevolAlgo = std::dynamic_pointer_cast<GeomAlgoAPI_Revolution>(*aSolidsAlgosIter);
423         aSubShapes = aRevolAlgo->mapOfShapes();
424         theResultBody->loadAndOrientGeneratedShapes(aRevolAlgo->makeShape().get(), *aShellsIter, GeomAPI_Shape::EDGE, aGenTag,
425                                                     aLatName, *aSubShapes.get());
426         aFromFaces = aRevolAlgo->fromFaces();
427         aToFaces = aRevolAlgo->toFaces();
428       }
429
430       //Insert to faces
431       for(ListOfShape::const_iterator anIt = aToFaces.cbegin(); anIt != aToFaces.cend(); anIt++) {
432         std::shared_ptr<GeomAPI_Shape> aToFace = *anIt;
433         if(aSubShapes->isBound(aToFace)) {
434           aToFace = aSubShapes->find(aToFace);
435         }
436         theResultBody->generated(aToFace, aToName, aToTag++);
437       }
438
439       //Insert from faces
440       if (aFromTag < aToTag) aFromTag = aToTag;
441       for(ListOfShape::const_iterator anIt = aFromFaces.cbegin(); anIt != aFromFaces.cend(); anIt++) {
442         std::shared_ptr<GeomAPI_Shape> aFromFace = *anIt;
443         if(aSubShapes->isBound(aFromFace)) {
444           aFromFace = aSubShapes->find(aFromFace);
445         }
446         theResultBody->generated(aFromFace, aFromName, aFromTag++);
447       }
448     }
449
450     theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE,
451                                                aModTag, aModName, theMapOfShapes);
452     theResultBody->loadDeletedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE, aDelTag);
453
454     for(ListOfShape::const_iterator anIter = theTools.begin(); anIter != theTools.end(); anIter++) {
455       theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE,
456                                                  aModTag, aModName, theMapOfShapes);
457       theResultBody->loadDeletedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE, aDelTag);
458     }
459   }
460 }