1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: FeaturesPlugin_CompositeBoolean.cpp
4 // Created: 11 June 2015
5 // Author: Dmitry Bobylev
7 #include "FeaturesPlugin_CompositeBoolean.h"
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>
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>
25 //=================================================================================================
26 void FeaturesPlugin_CompositeBoolean::initAttributes()
28 data()->addAttribute(SKETCH_OBJECT_ID(), ModelAPI_AttributeReference::typeId());
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");
35 initMakeSolidsAttributes();
37 data()->addAttribute(SKETCH_SELECTION_ID(), ModelAPI_AttributeSelection::typeId());
38 ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SKETCH_SELECTION_ID());
41 //=================================================================================================
42 std::shared_ptr<ModelAPI_Feature> FeaturesPlugin_CompositeBoolean::addFeature(std::string theID)
44 std::shared_ptr<ModelAPI_Feature> aNew = document()->addFeature(theID, false);
46 data()->reference(SKETCH_OBJECT_ID())->setValue(aNew);
48 // set as current also after it becomes sub to set correctly enabled for other sketch subs
49 document()->setCurrentFeature(aNew, false);
53 //=================================================================================================
54 int FeaturesPlugin_CompositeBoolean::numberOfSubs(bool forTree) const
56 ObjectPtr aObj = data()->reference(SKETCH_OBJECT_ID())->value();
57 return aObj.get()? 1 : 0;
60 //=================================================================================================
61 std::shared_ptr<ModelAPI_Feature> FeaturesPlugin_CompositeBoolean::subFeature(const int theIndex, bool forTree)
64 return std::dynamic_pointer_cast<ModelAPI_Feature>(data()->reference(SKETCH_OBJECT_ID())->value());
65 return std::shared_ptr<ModelAPI_Feature>();
68 //=================================================================================================
69 int FeaturesPlugin_CompositeBoolean::subFeatureId(const int theIndex) const
73 std::dynamic_pointer_cast<ModelAPI_Feature>(data()->reference(SKETCH_OBJECT_ID())->value());
75 return aFeature->data()->featureId();
80 //=================================================================================================
81 bool FeaturesPlugin_CompositeBoolean::isSub(ObjectPtr theObject) const
83 // check is this feature of result
84 FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
88 ObjectPtr aSub = data()->reference(SKETCH_OBJECT_ID())->value();
89 return aSub == theObject;
92 //=================================================================================================
93 void FeaturesPlugin_CompositeBoolean::removeFeature(std::shared_ptr<ModelAPI_Feature> theFeature)
97 //=================================================================================================
98 void FeaturesPlugin_CompositeBoolean::erase()
100 if (data().get() && data()->isValid()) { // on abort of sketch of this composite it may be invalid
102 std::dynamic_pointer_cast<ModelAPI_Feature>(data()->reference(SKETCH_OBJECT_ID())->value());
103 if (aSketch.get() && aSketch->data()->isValid()) {
104 document()->removeFeature(aSketch);
107 ModelAPI_CompositeFeature::erase();
111 //=================================================================================================
112 void FeaturesPlugin_CompositeBoolean::execute()
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()) {
120 ResultPtr aSketchRes = aSketchFeature->results().front();
121 ResultConstructionPtr aConstruction = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aSketchRes);
122 if(!aConstruction.get()) {
125 selection(SKETCH_SELECTION_ID())->setValue(aSketchRes, std::shared_ptr<GeomAPI_Shape>());
126 int aSketchFacesNum = aConstruction->facesNum();
127 if(aSketchFacesNum == 0) {
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);
136 // Searching faces with common edges.
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());
143 // Pass shells/faces to solids creation function.
145 std::list<std::shared_ptr<GeomAPI_Interface>> aSolidsAlgos;
146 makeSolids(aShells, aTools, aSolidsAlgos);
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) {
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()) {
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);
175 if(anIt == aCompSolidsObjects.end()) {
176 aCompSolidsObjects[aContextShape].push_back(anObject);
179 anObjects.push_back(anObject);
183 // Cut from each object solids.
184 int aResultIndex = 0;
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);
196 // Checking that the algorithm worked properly.
197 if(!aBoolAlgo.isDone() || aBoolAlgo.shape()->isNull() || !aBoolAlgo.isValid()) {
198 setError("Boolean algorithm failed");
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);
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;
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)) {
227 if(anIt == aUsedInOperationSolids.end()) {
228 aNotUsedSolids.push_back(aSolidInCompSolid);
232 GeomAlgoAPI_Boolean aBoolAlgo(aUsedInOperationSolids, aTools, myBooleanOperationType);
234 // Checking that the algorithm worked properly.
235 if(!aBoolAlgo.isDone() || aBoolAlgo.shape()->isNull() || !aBoolAlgo.isValid()) {
236 setError("Boolean algorithm failed");
240 GeomAlgoAPI_MakeShapeList aMakeShapeList;
241 aMakeShapeList.appendAlgo(aBoolAlgo.makeShape());
242 GeomAPI_DataMapOfShapeShape aMapOfShapes;
243 aMapOfShapes.merge(aBoolAlgo.mapOfShapes());
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);
255 aMakeShapeList.appendAlgo(aFillerAlgo.makeShape());
256 aMapOfShapes.merge(aFillerAlgo.mapOfShapes());
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);
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());
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());
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)) {
290 if(anIt == aUsedInOperationSolids.end()) {
291 aNotUsedSolids.push_back(aSolidInCompSolid);
296 ListOfShape anOriginalSolids = aSolidsToFuse;
297 anOriginalSolids.insert(anOriginalSolids.end(), aNotUsedSolids.begin(), aNotUsedSolids.end());
298 GeomAlgoAPI_MakeShapeList aMakeShapeList;
299 GeomAPI_DataMapOfShapeShape aMapOfShapes;
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);
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());
318 anObjects.push_back(aSolidsToFuse.back());
319 aSolidsToFuse.pop_back();
320 aTools = aSolidsToFuse;
322 // Fuse all objects and all tools.
323 GeomAlgoAPI_Boolean aFuseAlgo(anObjects, aTools, myBooleanOperationType);
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);
332 std::shared_ptr<GeomAPI_Shape> aShape = aFuseAlgo.shape();
333 aMakeShapeList.appendAlgo(aFuseAlgo.makeShape());
334 aMapOfShapes.merge(aFuseAlgo.mapOfShapes());
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);
345 if(aFillerAlgo.shape()->isNull()) {
346 static const std::string aShapeError = "Resulting shape is Null";
347 setError(aShapeError);
350 if(!aFillerAlgo.isValid()) {
351 std::string aFeatureError = "Warning: resulting shape is not valid";
352 setError(aFeatureError);
356 aShape = aFillerAlgo.shape();
357 aMakeShapeList.appendAlgo(aFillerAlgo.makeShape());
358 aMapOfShapes.merge(aFillerAlgo.mapOfShapes());
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);
368 setError("Error: wrong type of boolean operation");
373 // Remove the rest results if there were produced in the previous pass.
374 removeResults(aResultIndex);
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)
388 if(theBaseShape->isEqual(theResultShape)) {
389 theResultBody->store(theResultShape);
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";
403 theResultBody->storeModified(theBaseShape, theResultShape, aSubsolidsTag);
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;
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());
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->mapOfSubShapes();
424 theResultBody->loadAndOrientGeneratedShapes(aRevolAlgo.get(), *aShellsIter, GeomAPI_Shape::EDGE, aGenTag,
425 aLatName, *aSubShapes.get());
426 aFromFaces = aRevolAlgo->fromFaces();
427 aToFaces = aRevolAlgo->toFaces();
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);
436 theResultBody->generated(aToFace, aToName, aToTag++);
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);
446 theResultBody->generated(aFromFace, aFromName, aFromTag++);
450 theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE,
451 aModTag, aModName, theMapOfShapes);
452 theResultBody->loadDeletedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE, aDelTag);
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);