Salome HOME
0f1ac4f724384ff62e4321a7e50b8bbd1071be19
[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;
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->shapeType() == GeomAPI_Shape::EDGE
115           || aTool->shapeType() == GeomAPI_Shape::FACE)
116       {
117         anEdgesAndFaces.push_back(aTool);
118       } else {
119         aTools.push_back(aTool);
120       }
121     }
122   }
123
124   if ((anObjects.size() + aTools.size() +
125     aCompSolidsObjects.size() + anEdgesAndFaces.size()) < 2) {
126     std::string aFeatureError = "Error: Not enough objects for boolean operation.";
127     setError(aFeatureError);
128     return;
129   }
130
131   // Collecting all solids which will be fused.
132   ListOfShape aSolidsToFuse;
133   aSolidsToFuse.insert(aSolidsToFuse.end(), anObjects.begin(), anObjects.end());
134   aSolidsToFuse.insert(aSolidsToFuse.end(), aTools.begin(), aTools.end());
135
136   // Collecting solids from compsolids which will not be modified
137   // in boolean operation and will be added to result.
138   ListOfShape aShapesToAdd;
139   for (std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator
140        anIt = aCompSolidsObjects.begin();
141        anIt != aCompSolidsObjects.end(); anIt++) {
142     std::shared_ptr<GeomAPI_Shape> aCompSolid = anIt->first;
143     ListOfShape& aUsedInOperationSolids = anIt->second;
144     aSolidsToFuse.insert(aSolidsToFuse.end(), aUsedInOperationSolids.begin(),
145                          aUsedInOperationSolids.end());
146
147     // Collect solids from compsolid which will not be modified in boolean operation.
148     for (GeomAPI_ShapeExplorer
149          anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
150       std::shared_ptr<GeomAPI_Shape> aSolidInCompSolid = anExp.current();
151       ListOfShape::iterator anIt = aUsedInOperationSolids.begin();
152       for (; anIt != aUsedInOperationSolids.end(); anIt++) {
153         if (aSolidInCompSolid->isEqual(*anIt)) {
154           break;
155         }
156       }
157       if (anIt == aUsedInOperationSolids.end()) {
158         aShapesToAdd.push_back(aSolidInCompSolid);
159       }
160     }
161   }
162
163   ListOfShape anOriginalShapes = aSolidsToFuse;
164   anOriginalShapes.insert(anOriginalShapes.end(), aShapesToAdd.begin(), aShapesToAdd.end());
165
166   // Cut edges and faces(if we have any) with solids.
167   GeomAlgoAPI_MakeShapeList aMakeShapeList;
168   GeomAPI_DataMapOfShapeShape aMapOfShapes;
169   std::shared_ptr<GeomAPI_Shape> aCuttedEdgesAndFaces;
170   if (!anEdgesAndFaces.empty()) {
171     std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(new GeomAlgoAPI_Boolean(anEdgesAndFaces,
172       anOriginalShapes, GeomAlgoAPI_Boolean::BOOL_CUT));
173     if (aCutAlgo->isDone()) {
174       aCuttedEdgesAndFaces = aCutAlgo->shape();
175       aMakeShapeList.appendAlgo(aCutAlgo);
176       aMapOfShapes.merge(aCutAlgo->mapOfSubShapes());
177     }
178   }
179   anOriginalShapes.insert(anOriginalShapes.end(), anEdgesAndFaces.begin(),
180                           anEdgesAndFaces.end());
181
182   // If we have compsolids then cut with not used solids all others.
183   if (!aShapesToAdd.empty()) {
184     aSolidsToFuse.clear();
185     for (ListOfShape::iterator
186          anIt = anOriginalShapes.begin(); anIt != anOriginalShapes.end(); anIt++) {
187       ListOfShape aOneObjectList;
188       aOneObjectList.push_back(*anIt);
189       std::shared_ptr<GeomAlgoAPI_Boolean> aCutAlgo(
190         new GeomAlgoAPI_Boolean(aOneObjectList, aShapesToAdd, GeomAlgoAPI_Boolean::BOOL_CUT));
191
192       if (GeomAlgoAPI_ShapeTools::volume(aCutAlgo->shape()) > 1.e-27) {
193         aSolidsToFuse.push_back(aCutAlgo->shape());
194         aMakeShapeList.appendAlgo(aCutAlgo);
195         aMapOfShapes.merge(aCutAlgo->mapOfSubShapes());
196       }
197     }
198   }
199
200   if (!aSolidsToFuse.empty()) {
201     anObjects.clear();
202     anObjects.push_back(aSolidsToFuse.back());
203     aSolidsToFuse.pop_back();
204     aTools = aSolidsToFuse;
205   }
206
207   // Fuse all objects and all tools.
208   std::shared_ptr<GeomAPI_Shape> aShape;
209   if (anObjects.size() == 1 && aTools.empty()) {
210     aShape = anObjects.front();
211   } else if (anObjects.empty() && aTools.size() == 1) {
212     aShape = aTools.front();
213   } else if ((anObjects.size() + aTools.size()) > 1) {
214     std::shared_ptr<GeomAlgoAPI_Boolean> aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects,
215       aTools,
216       GeomAlgoAPI_Boolean::BOOL_FUSE));
217
218     // Checking that the algorithm worked properly.
219     if (!aFuseAlgo->isDone()) {
220       static const std::string aFeatureError = "Error: Boolean algorithm failed.";
221       setError(aFeatureError);
222       return;
223     }
224     if (aFuseAlgo->shape()->isNull()) {
225       static const std::string aShapeError = "Error: Resulting shape is Null.";
226       setError(aShapeError);
227       return;
228     }
229     if (!aFuseAlgo->isValid()) {
230       std::string aFeatureError = "Error: Resulting shape is not valid.";
231       setError(aFeatureError);
232       return;
233     }
234
235     aShape = aFuseAlgo->shape();
236     aMakeShapeList.appendAlgo(aFuseAlgo);
237     aMapOfShapes.merge(aFuseAlgo->mapOfSubShapes());
238   }
239
240   // Combine result with not used solids from compsolid and edges and faces (if we have any).
241   if (aCuttedEdgesAndFaces.get() && !aCuttedEdgesAndFaces->isNull()) {
242     aShapesToAdd.push_back(aCuttedEdgesAndFaces);
243   } else {
244     aShapesToAdd.insert(aShapesToAdd.end(), anEdgesAndFaces.begin(), anEdgesAndFaces.end());
245   }
246   if (!aShapesToAdd.empty()) {
247     if (aShape.get()) {
248       aShapesToAdd.push_back(aShape);
249     }
250     std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(
251       new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
252     if (!aFillerAlgo->isDone()) {
253       std::string aFeatureError = "Error: PaveFiller algorithm failed.";
254       setError(aFeatureError);
255       return;
256     }
257     if (aFillerAlgo->shape()->isNull()) {
258       static const std::string aShapeError = "Error: Resulting shape is Null.";
259       setError(aShapeError);
260       return;
261     }
262     if (!aFillerAlgo->isValid()) {
263       std::string aFeatureError = "Error: Resulting shape is not valid.";
264       setError(aFeatureError);
265       return;
266     }
267
268     aShape = aFillerAlgo->shape();
269     aMakeShapeList.appendAlgo(aFillerAlgo);
270     aMapOfShapes.merge(aFillerAlgo->mapOfSubShapes());
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     aMapOfShapes.merge(aUnifyAlgo->mapOfSubShapes());
302   }
303
304   int aResultIndex = 0;
305
306   std::shared_ptr<GeomAPI_Shape> aBackShape = anOriginalShapes.back();
307   anOriginalShapes.pop_back();
308   std::shared_ptr<ModelAPI_ResultBody> aResultBody =
309     document()->createBody(data(), aResultIndex);
310   loadNamingDS(aResultBody, aBackShape, anOriginalShapes,
311                aShape, aMakeShapeList, aMapOfShapes);
312   setResult(aResultBody, aResultIndex);
313   aResultIndex++;
314
315   // remove the rest results if there were produced in the previous pass
316   removeResults(aResultIndex);
317 }
318
319 //==================================================================================================
320 void FeaturesPlugin_BooleanFuse::loadNamingDS(ResultBodyPtr theResultBody,
321                                               const GeomShapePtr theBaseShape,
322                                               const ListOfShape& theTools,
323                                               const GeomShapePtr theResultShape,
324                                               GeomAlgoAPI_MakeShape& theMakeShape,
325                                               GeomAPI_DataMapOfShapeShape& theMapOfShapes)
326 {
327   //load result
328   if (theBaseShape->isEqual(theResultShape)) {
329     theResultBody->store(theResultShape, false);
330   } else {
331     const int aModifyVTag = 1;
332     const int aModifyETag = 2;
333     const int aModifyFTag = 3;
334     const int aDeletedTag = 4;
335     /// sub solids will be placed at labels 5, 6, etc. if result is compound of solids
336     const int aSubsolidsTag = 5;
337
338     theResultBody->storeModified(theBaseShape, theResultShape, aSubsolidsTag);
339
340     const std::string aModVName = "Modified_Vertex";
341     const std::string aModEName = "Modified_Edge";
342     const std::string aModFName = "Modified_Face";
343
344     theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::VERTEX,
345                                                aModifyVTag, aModVName, theMapOfShapes, false,
346                                                false, true);
347     theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::EDGE,
348                                                aModifyETag, aModEName, theMapOfShapes, false,
349                                                false, true);
350     theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE,
351                                                aModifyFTag, aModFName, theMapOfShapes, false,
352                                                false, true);
353
354     theResultBody->loadDeletedShapes(&theMakeShape, theBaseShape,
355                                      GeomAPI_Shape::VERTEX, aDeletedTag);
356     theResultBody->loadDeletedShapes(&theMakeShape, theBaseShape,
357                                      GeomAPI_Shape::EDGE, aDeletedTag);
358     theResultBody->loadDeletedShapes(&theMakeShape, theBaseShape,
359                                      GeomAPI_Shape::FACE, aDeletedTag);
360
361     for (ListOfShape::const_iterator anIter = theTools.begin(); anIter != theTools.end(); anIter++)
362     {
363       theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, GeomAPI_Shape::VERTEX,
364                                                  aModifyVTag, aModVName, theMapOfShapes, false,
365                                                  false, true);
366
367       theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, GeomAPI_Shape::EDGE,
368                                                  aModifyETag, aModEName, theMapOfShapes, false,
369                                                  false, true);
370
371       theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE,
372                                                  aModifyFTag, aModFName, theMapOfShapes, false,
373                                                  false, true);
374
375       theResultBody->loadDeletedShapes(&theMakeShape, *anIter, GeomAPI_Shape::VERTEX, aDeletedTag);
376       theResultBody->loadDeletedShapes(&theMakeShape, *anIter, GeomAPI_Shape::EDGE, aDeletedTag);
377       theResultBody->loadDeletedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE, aDeletedTag);
378     }
379   }
380 }