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