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