Salome HOME
Basing on the issue #1757 make extrusion-cut that
[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 "FeaturesPlugin_Tools.h"
24
25 #include <ModelAPI_ResultBody.h>
26 #include <ModelAPI_AttributeBoolean.h>
27 #include <ModelAPI_AttributeSelectionList.h>
28 #include <ModelAPI_AttributeString.h>
29 #include <ModelAPI_Session.h>
30 #include <ModelAPI_Tools.h>
31 #include <ModelAPI_Validator.h>
32
33 #include <GeomAlgoAPI_Boolean.h>
34 #include <GeomAlgoAPI_MakeShapeList.h>
35 #include <GeomAlgoAPI_PaveFiller.h>
36 #include <GeomAlgoAPI_ShapeTools.h>
37 #include <GeomAlgoAPI_UnifySameDomain.h>
38 #include <GeomAPI_ShapeExplorer.h>
39
40 //==================================================================================================
41 FeaturesPlugin_BooleanFuse::FeaturesPlugin_BooleanFuse()
42 : FeaturesPlugin_Boolean(FeaturesPlugin_Boolean::BOOL_FUSE)
43 {
44 }
45
46 //==================================================================================================
47 void FeaturesPlugin_BooleanFuse::initAttributes()
48 {
49   data()->addAttribute(CREATION_METHOD(), ModelAPI_AttributeString::typeId());
50
51   data()->addAttribute(OBJECT_LIST_ID(), ModelAPI_AttributeSelectionList::typeId());
52   data()->addAttribute(TOOL_LIST_ID(), ModelAPI_AttributeSelectionList::typeId());
53
54   data()->addAttribute(REMOVE_INTERSECTION_EDGES_ID(), ModelAPI_AttributeBoolean::typeId());
55
56   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), OBJECT_LIST_ID());
57   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), TOOL_LIST_ID());
58 }
59
60 //==================================================================================================
61 void FeaturesPlugin_BooleanFuse::execute()
62 {
63   ListOfShape anObjects, aTools, anEdgesAndFaces;
64   std::map<GeomShapePtr, ListOfShape> aCompSolidsObjects;
65
66   bool isSimpleCreation = false;
67
68   AttributeStringPtr aCreationMethodAttr = string(CREATION_METHOD());
69   if (aCreationMethodAttr.get()
70       && aCreationMethodAttr->value() == CREATION_METHOD_SIMPLE())
71   {
72     isSimpleCreation = true;
73   }
74
75   // Getting objects.
76   AttributeSelectionListPtr anObjectsSelList =
77     selectionList(FeaturesPlugin_Boolean::OBJECT_LIST_ID());
78   for (int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
79     AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex);
80     GeomShapePtr anObject = anObjectAttr->value();
81     if (!anObject.get()) {
82       return;
83     }
84     ResultPtr aContext = anObjectAttr->context();
85     ResultBodyPtr aResCompSolidPtr = ModelAPI_Tools::bodyOwner(aContext);
86     if (!isSimpleCreation
87         && aResCompSolidPtr.get()
88         && aResCompSolidPtr->shape()->shapeType() == GeomAPI_Shape::COMPSOLID)
89     {
90       GeomShapePtr aContextShape = aResCompSolidPtr->shape();
91       std::map<GeomShapePtr, ListOfShape>::iterator
92         anIt = aCompSolidsObjects.begin();
93       for (; anIt != aCompSolidsObjects.end(); anIt++) {
94         if (anIt->first->isEqual(aContextShape)) {
95           aCompSolidsObjects[anIt->first].push_back(anObject);
96           break;
97         }
98       }
99       if (anIt == aCompSolidsObjects.end()) {
100         aCompSolidsObjects[aContextShape].push_back(anObject);
101       }
102     } else {
103       if (anObject->shapeType() == GeomAPI_Shape::EDGE
104           || anObject->shapeType() == GeomAPI_Shape::FACE) {
105         anEdgesAndFaces.push_back(anObject);
106       } else {
107         anObjects.push_back(anObject);
108       }
109     }
110   }
111
112   // Getting tools.
113   if (!isSimpleCreation) {
114     AttributeSelectionListPtr aToolsSelList = selectionList(FeaturesPlugin_Boolean::TOOL_LIST_ID());
115     for (int aToolsIndex = 0; aToolsIndex < aToolsSelList->size(); aToolsIndex++) {
116       AttributeSelectionPtr aToolAttr = aToolsSelList->value(aToolsIndex);
117       GeomShapePtr aTool = aToolAttr->value();
118       if (aTool->shapeType() == GeomAPI_Shape::EDGE
119           || aTool->shapeType() == GeomAPI_Shape::FACE)
120       {
121         anEdgesAndFaces.push_back(aTool);
122       } else {
123         aTools.push_back(aTool);
124       }
125     }
126   }
127
128   if ((anObjects.size() + aTools.size() +
129     aCompSolidsObjects.size() + anEdgesAndFaces.size()) < 2) {
130     std::string aFeatureError = "Error: Not enough objects for boolean operation.";
131     setError(aFeatureError);
132     return;
133   }
134
135   // Collecting all solids which will be fused.
136   ListOfShape aSolidsToFuse;
137   aSolidsToFuse.insert(aSolidsToFuse.end(), anObjects.begin(), anObjects.end());
138   aSolidsToFuse.insert(aSolidsToFuse.end(), aTools.begin(), aTools.end());
139
140   // Collecting solids from compsolids which will not be modified
141   // in boolean operation and will be added to result.
142   ListOfShape aShapesToAdd;
143   for (std::map<GeomShapePtr, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
144        anIt != aCompSolidsObjects.end();
145        ++anIt)
146   {
147     GeomShapePtr aCompSolid = anIt->first;
148     ListOfShape& aUsedInOperationSolids = anIt->second;
149     aSolidsToFuse.insert(aSolidsToFuse.end(), aUsedInOperationSolids.begin(),
150                          aUsedInOperationSolids.end());
151
152     // Collect solids from compsolid which will not be modified in boolean operation.
153     for (GeomAPI_ShapeExplorer
154          anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
155       GeomShapePtr aSolidInCompSolid = anExp.current();
156       ListOfShape::iterator anIt = aUsedInOperationSolids.begin();
157       for (; anIt != aUsedInOperationSolids.end(); anIt++) {
158         if (aSolidInCompSolid->isEqual(*anIt)) {
159           break;
160         }
161       }
162       if (anIt == aUsedInOperationSolids.end()) {
163         aShapesToAdd.push_back(aSolidInCompSolid);
164       }
165     }
166   }
167
168   ListOfShape anOriginalShapes = aSolidsToFuse;
169   anOriginalShapes.insert(anOriginalShapes.end(), aShapesToAdd.begin(), aShapesToAdd.end());
170
171   // Cut edges and faces(if we have any) with solids.
172   std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
173   GeomShapePtr aCuttedEdgesAndFaces;
174   if (!anEdgesAndFaces.empty()) {
175     std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(new GeomAlgoAPI_Boolean(anEdgesAndFaces,
176       anOriginalShapes, GeomAlgoAPI_Boolean::BOOL_CUT));
177     if (aCutAlgo->isDone()) {
178       aCuttedEdgesAndFaces = aCutAlgo->shape();
179       aMakeShapeList->appendAlgo(aCutAlgo);
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       }
199     }
200   }
201
202   if (!aSolidsToFuse.empty()) {
203     anObjects.clear();
204     anObjects.push_back(aSolidsToFuse.back());
205     aSolidsToFuse.pop_back();
206     aTools = aSolidsToFuse;
207   }
208
209   // Fuse all objects and all tools.
210   GeomShapePtr aShape;
211   if (anObjects.size() == 1 && aTools.empty()) {
212     aShape = anObjects.front();
213   } else if (anObjects.empty() && aTools.size() == 1) {
214     aShape = aTools.front();
215   } else if ((anObjects.size() + aTools.size()) > 1) {
216     std::shared_ptr<GeomAlgoAPI_Boolean> aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects,
217       aTools,
218       GeomAlgoAPI_Boolean::BOOL_FUSE));
219
220     // Checking that the algorithm worked properly.
221     if (!aFuseAlgo->isDone()) {
222       static const std::string aFeatureError = "Error: Boolean algorithm failed.";
223       setError(aFeatureError);
224       return;
225     }
226     if (aFuseAlgo->shape()->isNull()) {
227       static const std::string aShapeError = "Error: Resulting shape is Null.";
228       setError(aShapeError);
229       return;
230     }
231     if (!aFuseAlgo->isValid()) {
232       std::string aFeatureError = "Error: Resulting shape is not valid.";
233       setError(aFeatureError);
234       return;
235     }
236
237     aShape = aFuseAlgo->shape();
238     aMakeShapeList->appendAlgo(aFuseAlgo);
239   }
240
241   // Combine result with not used solids from compsolid and edges and faces (if we have any).
242   if (aCuttedEdgesAndFaces.get() && !aCuttedEdgesAndFaces->isNull()) {
243     aShapesToAdd.push_back(aCuttedEdgesAndFaces);
244   } else {
245     aShapesToAdd.insert(aShapesToAdd.end(), anEdgesAndFaces.begin(), anEdgesAndFaces.end());
246   }
247   if (!aShapesToAdd.empty()) {
248     if (aShape.get()) {
249       aShapesToAdd.push_back(aShape);
250     }
251     std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(
252       new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
253     if (!aFillerAlgo->isDone()) {
254       std::string aFeatureError = "Error: PaveFiller algorithm failed.";
255       setError(aFeatureError);
256       return;
257     }
258     if (aFillerAlgo->shape()->isNull()) {
259       static const std::string aShapeError = "Error: Resulting shape is Null.";
260       setError(aShapeError);
261       return;
262     }
263     if (!aFillerAlgo->isValid()) {
264       std::string aFeatureError = "Error: Resulting shape is not valid.";
265       setError(aFeatureError);
266       return;
267     }
268
269     aShape = aFillerAlgo->shape();
270     aMakeShapeList->appendAlgo(aFillerAlgo);
271   }
272
273   bool isRemoveEdges = false;
274   AttributeBooleanPtr removeEdgesAttr = boolean(REMOVE_INTERSECTION_EDGES_ID());
275   if (removeEdgesAttr.get()) {
276     isRemoveEdges = removeEdgesAttr->value();
277   }
278
279   if (isRemoveEdges) {
280     std::shared_ptr<GeomAlgoAPI_UnifySameDomain> aUnifyAlgo(
281       new GeomAlgoAPI_UnifySameDomain(aShape));
282
283     if (!aUnifyAlgo->isDone()) {
284       std::string aFeatureError = "Error: PaveFiller algorithm failed.";
285       setError(aFeatureError);
286       return;
287     }
288     if (aUnifyAlgo->shape()->isNull()) {
289       static const std::string aShapeError = "Error: Resulting shape is Null.";
290       setError(aShapeError);
291       return;
292     }
293     if (!aUnifyAlgo->isValid()) {
294       std::string aFeatureError = "Error: Resulting shape is not valid.";
295       setError(aFeatureError);
296       return;
297     }
298
299     aShape = aUnifyAlgo->shape();
300     aMakeShapeList->appendAlgo(aUnifyAlgo);
301   }
302
303   int aResultIndex = 0;
304
305   GeomShapePtr aBackShape = anOriginalShapes.back();
306   anOriginalShapes.pop_back();
307   ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
308
309   FeaturesPlugin_Tools::loadModifiedShapes(aResultBody,
310                                            aBackShape,
311                                            anOriginalShapes,
312                                            aMakeShapeList,
313                                            aShape);
314   setResult(aResultBody, aResultIndex);
315   aResultIndex++;
316
317   FeaturesPlugin_Tools::loadDeletedShapes(aResultBody,
318                                           aBackShape,
319                                           anOriginalShapes,
320                                           aMakeShapeList,
321                                           aShape);
322
323   // remove the rest results if there were produced in the previous pass
324   removeResults(aResultIndex);
325 }