From: azv Date: Tue, 14 May 2019 04:24:43 +0000 (+0300) Subject: Refactoring: split base class of Boolean operation (separate methods related to versi... X-Git-Tag: VEDF2019Lot4~101^2~76 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=8920aca3656deae99bde28b57576777b1230cefc;p=modules%2Fshaper.git Refactoring: split base class of Boolean operation (separate methods related to versioning and keeping the hierarchy of compounds) --- diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index d70e298ea..0e8117970 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -28,6 +28,7 @@ SET(PROJECT_HEADERS FeaturesPlugin_Revolution.h FeaturesPlugin_Rotation.h FeaturesPlugin_Translation.h + FeaturesPlugin_VersionedBoolean.h FeaturesPlugin_Boolean.h FeaturesPlugin_BooleanCut.h FeaturesPlugin_BooleanFuse.h @@ -67,6 +68,7 @@ SET(PROJECT_SOURCES FeaturesPlugin_Revolution.cpp FeaturesPlugin_Rotation.cpp FeaturesPlugin_Translation.cpp + FeaturesPlugin_VersionedBoolean.cpp FeaturesPlugin_Boolean.cpp FeaturesPlugin_BooleanCut.cpp FeaturesPlugin_BooleanFuse.cpp diff --git a/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp b/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp index 066bf02af..d3fcd0ba5 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp @@ -65,69 +65,12 @@ void FeaturesPlugin_Boolean::initAttributes() ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), TOOL_LIST_ID()); } -//================================================================================================= -void FeaturesPlugin_Boolean::initVersion(const int theVersion) -{ - AttributePtr aVerAttr = data()->addAttribute(VERSION_ID(), ModelAPI_AttributeInteger::typeId()); - aVerAttr->setIsArgument(false); - ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), VERSION_ID()); - if (!integer(VERSION_ID())->isInitialized() && - !selectionList(OBJECT_LIST_ID())->isInitialized() && - !selectionList(TOOL_LIST_ID())->isInitialized()) { - // this is a newly created feature (not read from file), - // so, initialize the latest version - integer(VERSION_ID())->setValue(theVersion); - } -} - //================================================================================================= FeaturesPlugin_Boolean::OperationType FeaturesPlugin_Boolean::operationType() { return myOperationType; } -//================================================================================================= -void FeaturesPlugin_Boolean::parentForShape(const GeomShapePtr& theShape, - const ResultPtr& theContext, - ObjectHierarchy& theShapesHierarchy) -{ - ResultBodyPtr aResCompSolidPtr = ModelAPI_Tools::bodyOwner(theContext); - if (aResCompSolidPtr.get()) { - std::shared_ptr aContextShape = aResCompSolidPtr->shape(); - if (aContextShape->shapeType() <= GeomAPI_Shape::COMPSOLID) { - theShapesHierarchy.AddParent(theShape, aContextShape); - parentForShape(aContextShape, aResCompSolidPtr, theShapesHierarchy); - } - } -} - -bool FeaturesPlugin_Boolean::processAttribute(const std::string& theAttributeName, - ObjectHierarchy& theObjects, - ListOfShape& thePlanesList) -{ - AttributeSelectionListPtr anObjectsSelList = selectionList(theAttributeName); - for (int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) { - AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex); - GeomShapePtr anObject = anObjectAttr->value(); - if (!anObject.get()) { - // It could be a construction plane. - ResultPtr aContext = anObjectAttr->context(); - anObject = anObjectAttr->context()->shape(); - if (anObject.get()) { - thePlanesList.push_back(anObject); - continue; - } else - return false; - } - - theObjects.AddObject(anObject); - - ResultPtr aContext = anObjectAttr->context(); - parentForShape(anObject, aContext, theObjects); - } - return true; -} - //================================================================================================= void FeaturesPlugin_Boolean::loadNamingDS(std::shared_ptr theResultBody, const std::shared_ptr theBaseShape, @@ -161,333 +104,6 @@ void FeaturesPlugin_Boolean::loadNamingDS(std::shared_ptr t } } -//================================================================================================= -bool FeaturesPlugin_Boolean::processObject( - const GeomAlgoAPI_Tools::BOPType theBooleanType, - const GeomShapePtr& theObject, - const ListOfShape& theTools, - const ListOfShape& thePlanes, - int& theResultIndex, - std::vector& theResultBaseAlgoList, - ListOfShape& theResultShapesList, - GeomShapePtr theResultCompound) -{ - ListOfShape aListWithObject; - aListWithObject.push_back(theObject); - std::shared_ptr aMakeShapeList(new GeomAlgoAPI_MakeShapeList()); - std::shared_ptr aBoolAlgo; - GeomShapePtr aResShape; - - std::list > aBoundingPoints = - GeomAlgoAPI_ShapeTools::getBoundingBox(aListWithObject, 1.0); - - // Resize planes. - ListOfShape aToolsWithPlanes = theTools; - for (ListOfShape::const_iterator anIt = thePlanes.begin(); anIt != thePlanes.end(); ++anIt) { - GeomShapePtr aPlane = *anIt; - GeomShapePtr aTool = GeomAlgoAPI_ShapeTools::fitPlaneToBox(aPlane, aBoundingPoints); - std::shared_ptr aMkShCustom( - new GeomAlgoAPI_MakeShapeCustom); - aMkShCustom->addModified(aPlane, aTool); - aMakeShapeList->appendAlgo(aMkShCustom); - aToolsWithPlanes.push_back(aTool); - } - - if (theBooleanType == GeomAlgoAPI_Tools::BOOL_PARTITION) - aBoolAlgo.reset(new GeomAlgoAPI_Partition(aListWithObject, aToolsWithPlanes)); - else - aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aListWithObject, - aToolsWithPlanes, - theBooleanType)); - - // Checking that the algorithm worked properly. - std::string anError; - if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) { - setError(anError); - return false; - } - - aResShape = aBoolAlgo->shape(); - if (aResShape.get() && aResShape->shapeType() == GeomAPI_Shape::COMPOUND) { - int aSubResultsNb = 0; - GeomAPI_ShapeIterator anIt(aResShape); - for (; anIt.more(); anIt.next()) - ++aSubResultsNb; - - if (aSubResultsNb == 1) { - anIt.init(aResShape); - if (anIt.more()) - aResShape = anIt.current(); - } - } - - aMakeShapeList->appendAlgo(aBoolAlgo); - - GeomAPI_ShapeIterator aShapeIt(aResShape); - if (aShapeIt.more() || aResShape->shapeType() == GeomAPI_Shape::VERTEX) { - std::shared_ptr aResultBody; - - if (theResultCompound) { // store BOP result to the compound - std::shared_ptr aBuilder(new GeomAlgoAPI_ShapeBuilder); - aBuilder->add(theResultCompound, aResShape); - aMakeShapeList->appendAlgo(aBuilder); - } - else { // create a separate ResultBody - aResultBody = document()->createBody(data(), theResultIndex); - - // tools should be added to the list to fulfill the correct history of modification - aListWithObject.insert(aListWithObject.end(), theTools.begin(), theTools.end()); - - ListOfShape aUsedTools = theTools; - aUsedTools.insert(aUsedTools.end(), thePlanes.begin(), thePlanes.end()); - - FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, - aListWithObject, - aUsedTools, - aMakeShapeList, - aResShape); - setResult(aResultBody, theResultIndex); - ++theResultIndex; - } - - - FeaturesPlugin_Tools::ResultBaseAlgo aRBA; - aRBA.resultBody = aResultBody; - aRBA.baseShape = theObject; - aRBA.makeShape = aMakeShapeList; - theResultBaseAlgoList.push_back(aRBA); - theResultShapesList.push_back(aResShape); - } - return true; -} - -//================================================================================================= -bool FeaturesPlugin_Boolean::processCompsolid( - const GeomAlgoAPI_Tools::BOPType theBooleanType, - const ObjectHierarchy& theCompsolidHierarchy, - const GeomShapePtr& theCompsolid, - const ListOfShape& theTools, - const ListOfShape& thePlanes, - int& theResultIndex, - std::vector& theResultBaseAlgoList, - ListOfShape& theResultShapesList, - GeomShapePtr theResultCompound) -{ - ListOfShape aUsedInOperationSolids; - ListOfShape aNotUsedSolids; - theCompsolidHierarchy.SplitCompound(theCompsolid, aUsedInOperationSolids, aNotUsedSolids); - - std::shared_ptr aMakeShapeList(new GeomAlgoAPI_MakeShapeList()); - - std::list > aBoundingPoints = - GeomAlgoAPI_ShapeTools::getBoundingBox(aUsedInOperationSolids, 1.0); - - // Resize planes. - ListOfShape aToolsWithPlanes = theTools; - for (ListOfShape::const_iterator anIt = thePlanes.begin(); anIt != thePlanes.end(); ++anIt) - { - GeomShapePtr aPlane = *anIt; - GeomShapePtr aTool = GeomAlgoAPI_ShapeTools::fitPlaneToBox(aPlane, aBoundingPoints); - std::shared_ptr aMkShCustom( - new GeomAlgoAPI_MakeShapeCustom); - aMkShCustom->addModified(aPlane, aTool); - aMakeShapeList->appendAlgo(aMkShCustom); - aToolsWithPlanes.push_back(aTool); - } - - std::shared_ptr aBoolAlgo; - if (theBooleanType == GeomAlgoAPI_Tools::BOOL_PARTITION) - aBoolAlgo.reset(new GeomAlgoAPI_Partition(aUsedInOperationSolids, aToolsWithPlanes)); - else - aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aUsedInOperationSolids, - aToolsWithPlanes, - theBooleanType)); - - // Checking that the algorithm worked properly. - std::string anError; - if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) { - setError(anError); - return false; - } - - aMakeShapeList->appendAlgo(aBoolAlgo); - GeomShapePtr aResultShape = aBoolAlgo->shape(); - - // Add result to not used solids from compsolid. - if (!aNotUsedSolids.empty()) { - ListOfShape aShapesToAdd = aNotUsedSolids; - aShapesToAdd.push_back(aBoolAlgo->shape()); - std::shared_ptr aFillerAlgo( - new GeomAlgoAPI_PaveFiller(aShapesToAdd, true)); - if (!aFillerAlgo->isDone()) { - std::string aFeatureError = "Error: PaveFiller algorithm failed."; - setError(aFeatureError); - return false; - } - - aMakeShapeList->appendAlgo(aFillerAlgo); - aResultShape = aFillerAlgo->shape(); - } - - GeomAPI_ShapeIterator aShapeIt(aResultShape); - if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX) - { - std::shared_ptr aResultBody; - - if (theResultCompound) { // store BOP result to the compound - std::shared_ptr aBuilder(new GeomAlgoAPI_ShapeBuilder); - aBuilder->add(theResultCompound, aResultShape); - aMakeShapeList->appendAlgo(aBuilder); - } - else { // create a separate ResultBody - aResultBody = document()->createBody(data(), theResultIndex); - - ListOfShape aCompSolidList; - aCompSolidList.push_back(theCompsolid); - // tools should be added to the list to fulfill the correct history of modification - aCompSolidList.insert(aCompSolidList.end(), theTools.begin(), theTools.end()); - - ListOfShape aUsedTools = theTools; - aUsedTools.insert(aUsedTools.end(), thePlanes.begin(), thePlanes.end()); - - FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, - aCompSolidList, - aUsedTools, - aMakeShapeList, - aResultShape); - setResult(aResultBody, theResultIndex); - ++theResultIndex; - } - - FeaturesPlugin_Tools::ResultBaseAlgo aRBA; - aRBA.resultBody = aResultBody; - aRBA.baseShape = theCompsolid; - aRBA.makeShape = aMakeShapeList; - theResultBaseAlgoList.push_back(aRBA); - theResultShapesList.push_back(aResultShape); - } - return true; -} - -//================================================================================================= -bool FeaturesPlugin_Boolean::processCompound( - const GeomAlgoAPI_Tools::BOPType theBooleanType, - const ObjectHierarchy& theCompoundHierarchy, - const GeomShapePtr& theCompound, - const ListOfShape& theTools, - int& theResultIndex, - std::vector& theResultBaseAlgoList, - ListOfShape& theResultShapesList, - GeomShapePtr theResultCompound) -{ - ListOfShape aUsedInOperationShapes; - ListOfShape aNotUsedShapes; - theCompoundHierarchy.SplitCompound(theCompound, aUsedInOperationShapes, aNotUsedShapes); - if (theResultCompound) { - // Not necessary to keep all subs of the current compound, - // all unused solids are already stored in the result compound. - aNotUsedShapes.clear(); - } - - std::shared_ptr aMakeShapeList(new GeomAlgoAPI_MakeShapeList()); - std::shared_ptr aBoolAlgo( - new GeomAlgoAPI_Boolean(aUsedInOperationShapes, - theTools, - theBooleanType)); - - // Checking that the algorithm worked properly. - std::string anError; - if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) { - setError(anError); - return false; - } - - aMakeShapeList->appendAlgo(aBoolAlgo); - GeomShapePtr aResultShape = aBoolAlgo->shape(); - - // Add result to not used shape from compound. - if (!aNotUsedShapes.empty()) { - ListOfShape aShapesForResult = aNotUsedShapes; - if (aResultShape->shapeType() == GeomAPI_Shape::COMPOUND) { - for (GeomAPI_ShapeIterator aResultIt(aResultShape); aResultIt.more(); aResultIt.next()) { - aShapesForResult.push_back(aResultIt.current()); - } - } - else { - aShapesForResult.push_back(aResultShape); - } - - if (aShapesForResult.size() == 1) { - aResultShape = aShapesForResult.front(); - } - else { - aResultShape = GeomAlgoAPI_CompoundBuilder::compound(aShapesForResult); - } - } - - GeomAPI_ShapeIterator aShapeIt(aResultShape); - if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX) { - std::shared_ptr aResultBody; - - if (theResultCompound) { // store BOP result to the compound - std::shared_ptr aBuilder(new GeomAlgoAPI_ShapeBuilder); - aBuilder->add(theResultCompound, aResultShape); - aMakeShapeList->appendAlgo(aBuilder); - } - else { // create a separate ResultBody - aResultBody = document()->createBody(data(), theResultIndex); - - ListOfShape aCompoundList; - aCompoundList.push_back(theCompound); - FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, - aCompoundList, - theTools, - aMakeShapeList, - aResultShape); - setResult(aResultBody, theResultIndex); - ++theResultIndex; - } - - FeaturesPlugin_Tools::ResultBaseAlgo aRBA; - aRBA.resultBody = aResultBody; - aRBA.baseShape = theCompound; - aRBA.makeShape = aMakeShapeList; - theResultBaseAlgoList.push_back(aRBA); - theResultShapesList.push_back(aResultShape); - } - return true; -} - -//================================================================================================== -GeomShapePtr FeaturesPlugin_Boolean::keepUnusedSubsOfCompound( - const GeomShapePtr& theResult, - const ObjectHierarchy& theObjectsHierarchy, - const ObjectHierarchy& theToolsHierarchy, - std::shared_ptr theMakeShapeList) -{ - ListOfShape aCompounds; - theObjectsHierarchy.CompoundsOfUnusedObjects(aCompounds); - theToolsHierarchy.CompoundsOfUnusedObjects(aCompounds); - - GeomShapePtr aResultShape = theResult; - if (!aCompounds.empty()) { - aResultShape = aCompounds.front(); - aCompounds.pop_front(); - - std::shared_ptr aBuilder(new GeomAlgoAPI_ShapeBuilder); - for (ListOfShape::iterator anIt = aCompounds.begin(); anIt != aCompounds.end(); ++anIt) { - for (GeomAPI_ShapeIterator aSub(*anIt); aSub.more(); aSub.next()) - aBuilder->add(aResultShape, aSub.current()); - } - - if (theResult) - aBuilder->add(aResultShape, theResult); - - theMakeShapeList->appendAlgo(aBuilder); - } - return aResultShape; -} - //================================================================================================= void FeaturesPlugin_Boolean::storeResult( const ListOfShape& theObjects, @@ -523,229 +139,3 @@ void FeaturesPlugin_Boolean::storeResult( theResultBaseAlgoList.clear(); theResultBaseAlgoList.push_back(aRBA); } - -//================================================================================================= -int FeaturesPlugin_Boolean::version() -{ - AttributeIntegerPtr aVersionAttr = integer(VERSION_ID()); - int aVersion = 0; - if (aVersionAttr && aVersionAttr->isInitialized()) - aVersion = aVersionAttr->value(); - return aVersion; -} - -//================================================================================================= - -void FeaturesPlugin_Boolean::ObjectHierarchy::AddObject(const GeomShapePtr& theObject) -{ - myObjects.push_back(theObject); -} - -void FeaturesPlugin_Boolean::ObjectHierarchy::AddParent(const GeomShapePtr& theShape, - const GeomShapePtr& theParent) -{ - myParent[theShape] = theParent; - - MapShapeToIndex::iterator aFound = myParentIndices.find(theParent); - size_t anIndex = myParentIndices.size(); - if (aFound == myParentIndices.end()) { - myParentIndices[theParent] = anIndex; - mySubshapes.push_back(ShapeAndSubshapes(theParent, ListOfShape())); - } else - anIndex = aFound->second; - - mySubshapes[anIndex].second.push_back(theShape); -} - -GeomShapePtr FeaturesPlugin_Boolean::ObjectHierarchy::Parent(const GeomShapePtr& theShape, - bool theMarkProcessed) -{ - MapShapeToParent::const_iterator aFound = myParent.find(theShape); - GeomShapePtr aParent; - if (aFound != myParent.end()) { - aParent = aFound->second; - if (theMarkProcessed) { - // mark the parent and all its subs as processed by Boolean algorithm - myProcessedObjects.insert(aParent); - const ListOfShape& aSubs = mySubshapes[myParentIndices[aParent]].second; - for (ListOfShape::const_iterator anIt = aSubs.begin(); anIt != aSubs.end(); ++anIt) - myProcessedObjects.insert(*anIt); - } - } - return aParent; -} - -void FeaturesPlugin_Boolean::ObjectHierarchy::ObjectsByType( - ListOfShape& theShapesByType, - ListOfShape& theOtherShapes, - const GeomAPI_Shape::ShapeType theMinType, - const GeomAPI_Shape::ShapeType theMaxType) const -{ - if (theMinType > theMaxType) - return ObjectsByType(theShapesByType, theOtherShapes, theMaxType, theMinType); - - // no need to select objects if whole range is specified - if (theMinType == GeomAPI_Shape::COMPOUND && theMaxType == GeomAPI_Shape::SHAPE) { - theShapesByType.insert(theShapesByType.end(), myObjects.begin(), myObjects.end()); - return; - } - - for (ListOfShape::const_iterator anIt = myObjects.begin(); anIt != myObjects.end(); ++anIt) { - GeomAPI_Shape::ShapeType aType = (*anIt)->shapeType(); - if (aType >= theMinType && aType <= theMaxType) - theShapesByType.push_back(*anIt); - else - theOtherShapes.push_back(*anIt); - } -} - - -void FeaturesPlugin_Boolean::ObjectHierarchy::SplitCompound(const GeomShapePtr& theCompShape, - ListOfShape& theUsed, - ListOfShape& theNotUsed) const -{ - theUsed.clear(); - theNotUsed.clear(); - - MapShapeToIndex::const_iterator aFoundIndex = myParentIndices.find(theCompShape); - if (aFoundIndex == myParentIndices.end()) - return; // no such shape - - theUsed = mySubshapes[aFoundIndex->second].second; - SetOfShape aSubsSet; - aSubsSet.insert(theUsed.begin(), theUsed.end()); - - for (GeomAPI_ShapeIterator anExp(theCompShape); anExp.more(); anExp.next()) { - GeomShapePtr aCurrent = anExp.current(); - if (aSubsSet.find(aCurrent) == aSubsSet.end()) - theNotUsed.push_back(aCurrent); - } -} - -bool FeaturesPlugin_Boolean::ObjectHierarchy::IsEmpty() const -{ - return myObjects.empty(); -} - -void FeaturesPlugin_Boolean::ObjectHierarchy::CompoundsOfUnusedObjects( - ListOfShape& theDestination) const -{ - SetOfShape aUsedObjects; - aUsedObjects.insert(myObjects.begin(), myObjects.end()); - - for (std::vector::const_iterator anIt = mySubshapes.begin(); - anIt != mySubshapes.end(); ++anIt) { - MapShapeToParent::const_iterator aParent = myParent.find(anIt->first); - if ((aParent == myParent.end() || !aParent->second) && - anIt->first->shapeType() == GeomAPI_Shape::COMPOUND) { - // this is a top-level compound - GeomShapePtr aCompound = collectUnusedSubs(anIt->first, aUsedObjects); - // add to destination non-empty compounds only - if (aCompound) - theDestination.push_back(aCompound); - } - } -} - -GeomShapePtr FeaturesPlugin_Boolean::ObjectHierarchy::collectUnusedSubs( - GeomShapePtr theTopLevelCompound, - const SetOfShape& theUsed) const -{ - GeomShapePtr aResult = theTopLevelCompound->emptyCopied(); - bool isResultEmpty = true; - - for (GeomAPI_ShapeIterator aSub(theTopLevelCompound); aSub.more(); aSub.next()) { - GeomShapePtr aCurrent = aSub.current(); - if (theUsed.find(aCurrent) != theUsed.end()) - continue; // already used - - MapShapeToIndex::const_iterator aFoundIndex = myParentIndices.find(aCurrent); - if (aCurrent->shapeType() > GeomAPI_Shape::COMPOUND || - aFoundIndex == myParentIndices.end()) { - bool isAddShape = true; - // check compsolid is fully unused in the Boolean operation - if (aCurrent->shapeType() == GeomAPI_Shape::COMPSOLID) { - for (GeomAPI_ShapeIterator anIt(aCurrent); isAddShape && anIt.more(); anIt.next()) - isAddShape = theUsed.find(anIt.current()) == theUsed.end(); - } - - if (isAddShape) { // low-level shape, add it - GeomAlgoAPI_ShapeBuilder::add(aResult, aCurrent); - isResultEmpty = false; - } - } else { - GeomShapePtr aCompound = collectUnusedSubs(aCurrent, theUsed); - if (aCompound) { - GeomAlgoAPI_ShapeBuilder::add(theTopLevelCompound, aCompound); - isResultEmpty = false; - } - } - } - return isResultEmpty ? GeomShapePtr() : aResult; -} - - -FeaturesPlugin_Boolean::ObjectHierarchy::Iterator FeaturesPlugin_Boolean::ObjectHierarchy::Begin() -{ - return Iterator(this); -} - -FeaturesPlugin_Boolean::ObjectHierarchy::Iterator FeaturesPlugin_Boolean::ObjectHierarchy::End() -{ - return Iterator(this, false); -} - -FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::Iterator( - FeaturesPlugin_Boolean::ObjectHierarchy* theHierarchy, bool isBegin) - : myHierarchy(theHierarchy) -{ - if (isBegin) { - myObject = myHierarchy->myObjects.begin(); - SkipAlreadyProcessed(); - } else - myObject = myHierarchy->myObjects.end(); -} - -void FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::SkipAlreadyProcessed() -{ - while (myObject != myHierarchy->myObjects.end() && - myHierarchy->myProcessedObjects.find(*myObject) != myHierarchy->myProcessedObjects.end()) - ++myObject; -} - -bool FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator==(const Iterator& theOther) const -{ - return myObject == theOther.myObject; -} - -bool FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator!=(const Iterator& theOther) const -{ - return !operator==(theOther); -} - -FeaturesPlugin_Boolean::ObjectHierarchy::Iterator& -FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator++() -{ - ++myObject; - SkipAlreadyProcessed(); - return *this; -} - -FeaturesPlugin_Boolean::ObjectHierarchy::Iterator -FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator++(int) -{ - Iterator aCurrent; - aCurrent.myHierarchy = myHierarchy; - aCurrent.myObject = myObject; - - // increase iterator - operator++(); - - return aCurrent; -} - -GeomShapePtr FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator*() const -{ - myHierarchy->myProcessedObjects.insert(*myObject); - return *myObject; -} diff --git a/src/FeaturesPlugin/FeaturesPlugin_Boolean.h b/src/FeaturesPlugin/FeaturesPlugin_Boolean.h index c18d2eb67..3bb43570d 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Boolean.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Boolean.h @@ -20,22 +20,13 @@ #ifndef FeaturesPlugin_Boolean_H_ #define FeaturesPlugin_Boolean_H_ -#include "FeaturesPlugin.h" -#include "FeaturesPlugin_Tools.h" - -#include -#include - -#include - -class ModelAPI_Result; -class GeomAlgoAPI_MakeShapeList; +#include "FeaturesPlugin_VersionedBoolean.h" /// \class FeaturesPlugin_Boolean /// \ingroup Plugins /// \brief Feature for applying of Boolean operations on Solids. /// Supports four kinds of Boolean operations: Cut, Fuse, Common and Smash. -class FeaturesPlugin_Boolean : public ModelAPI_Feature +class FeaturesPlugin_Boolean : public FeaturesPlugin_VersionedBoolean { public: enum OperationType { @@ -60,13 +51,6 @@ public: return MY_TOOL_LIST_ID; } - /// Attribute name of the version of Boolean feature - inline static const std::string& VERSION_ID() - { - static const std::string MY_VERSION_ID("version"); - return MY_VERSION_ID; - } - /// \return boolean operation type. FEATURESPLUGIN_EXPORT OperationType operationType(); @@ -78,11 +62,6 @@ protected: /// Use plugin manager for features creation. FeaturesPlugin_Boolean(const OperationType theOperationType); - /// Initialize version field of the Boolean feature. - /// The version is initialized for newly created features, - /// not read from previously stored document. - void initVersion(const int theVersion); - /// Load Naming data structure of the feature to the document void loadNamingDS(std::shared_ptr theResultBody, const std::shared_ptr theBaseShape, @@ -90,139 +69,6 @@ protected: const std::shared_ptr theResultShape, const GeomMakeShapePtr& theMakeShape); - - /// Auxiliary class to store hierarchy of Boolean operation objects/tools - /// and their parent shapes (compounds or compsolids) - class ObjectHierarchy { - typedef std::pair ShapeAndSubshapes; - typedef std::map MapShapeToParent; - typedef std::map MapShapeToIndex; - typedef std::set SetOfShape; - - ListOfShape myObjects; ///< list of objects/tools of Boolean operation - MapShapeToParent myParent; ///< refer a shape to compound/compsolid containing it - /// indices of compounds/compsolids to keep the order of parent shapes - /// corresponding to the order of objects - MapShapeToIndex myParentIndices; - /// list of shape and its subshapes stored according to the index of parent shape - std::vector mySubshapes; - - SetOfShape myProcessedObjects; - - public: - /// Add object of Boolean opration - void AddObject(const GeomShapePtr& theObject); - - /// Maps shape and its parent - void AddParent(const GeomShapePtr& theShape, const GeomShapePtr& theParent); - - /// Return parent shape for the given, or empty if it is a high-level shape. - /// By default, the parent and all its subshapes are marked as processed for further skip. - GeomShapePtr Parent(const GeomShapePtr& theShape, bool theMarkProcessed = true); - - /// Split compound/compsolid shape for subshapes selected for Boolean operation and the other. - void SplitCompound(const GeomShapePtr& theCompShape, - ListOfShape& theUsed, - ListOfShape& theNotUsed) const; - - /// Generates the list of top-level compounds, which contain the objects of Boolean operation. - /// The generated list will contain only shapes unused during the Boolean operation. - void CompoundsOfUnusedObjects(ListOfShape& theDestination) const; - - /// Return \c true if there is no object in hierarchy - bool IsEmpty() const; - - /// Return list of objects - const ListOfShape& Objects() const { return myObjects; } - /// Separate objects of the given range of types and all other objects - void ObjectsByType(ListOfShape& theShapesByType, ListOfShape& theOtherShapes, - const GeomAPI_Shape::ShapeType theMinType = GeomAPI_Shape::COMPOUND, - const GeomAPI_Shape::ShapeType theMaxType = GeomAPI_Shape::SHAPE) const; - - private: - GeomShapePtr collectUnusedSubs(const GeomShapePtr theTopLevelCompound, - const SetOfShape& theUsed) const; - - public: - class Iterator { - friend class ObjectHierarchy; - - ObjectHierarchy* myHierarchy; - ListOfShape::iterator myObject; - - Iterator() {} - Iterator(ObjectHierarchy* theHierarchy, bool isBegin = true); - - void SkipAlreadyProcessed(); - - public: - bool operator==(const Iterator&) const; - bool operator!=(const Iterator&) const; - - Iterator& operator++(); - Iterator operator++(int); - - GeomShapePtr operator*() const; - }; - - Iterator Begin(); - Iterator End(); - }; - - /// Process SelectionList attribute and fill the objects hierarchy. - bool processAttribute(const std::string& theAttributeName, - ObjectHierarchy& theObjects, - ListOfShape& thePlanesList); - - /// Perform Boolean operation of the object with the tools. - /// In case of theResultCompound is not empty, the result of Boolean operation - /// is added to this compound, and corresponding ResultBody is not generated. - /// \return \c false if something went wrong - bool processObject(const GeomAlgoAPI_Tools::BOPType theBooleanType, - const GeomShapePtr& theObject, - const ListOfShape& theTools, - const ListOfShape& thePlanes, - int& theResultIndex, - std::vector& theResultBaseAlgoList, - ListOfShape& theResultShapesList, - GeomShapePtr theResulCompound = GeomShapePtr()); - - /// Perform Boolean operation of the Compsolid with the tools - /// In case of theResultCompound is not empty, the result of Boolean operation - /// is added to this compound, and corresponding ResultBody is not generated. - /// \return \c false if something went wrong - bool processCompsolid(const GeomAlgoAPI_Tools::BOPType theBooleanType, - const ObjectHierarchy& theCompsolidHierarchy, - const GeomShapePtr& theCompsolid, - const ListOfShape& theTools, - const ListOfShape& thePlanes, - int& theResultIndex, - std::vector& theResultBaseAlgoList, - ListOfShape& theResultShapesList, - GeomShapePtr theResulCompound = GeomShapePtr()); - - /// Perform Boolean operation of the Compound with the tools - /// In case of theResultCompound is not empty, the result of Boolean operation - /// is added to this compound, and corresponding ResultBody is not generated. - /// \return \c false if something went wrong - bool processCompound(const GeomAlgoAPI_Tools::BOPType theBooleanType, - const ObjectHierarchy& theCompoundHierarchy, - const GeomShapePtr& theCompound, - const ListOfShape& theTools, - int& theResultIndex, - std::vector& theResultBaseAlgoList, - ListOfShape& theResultShapesList, - GeomShapePtr theResulCompound = GeomShapePtr()); - - /// Process unused sub-shapes of compounds. - /// Keep the compound hierarchy, but merge top-level compounds - /// into a single compound and add the result of the FUSE operation. - GeomShapePtr keepUnusedSubsOfCompound( - const GeomShapePtr& theResult, - const ObjectHierarchy& theObjectsHierarchy, - const ObjectHierarchy& theToolsHierarchy, - std::shared_ptr theMakeShapeList); - /// Store result shape if it is not empty and increase results counter void storeResult(const ListOfShape& theObjects, const ListOfShape& theTools, @@ -231,14 +77,6 @@ protected: std::shared_ptr theMakeShapeList, std::vector& theResultBaseAlgoList); - /// Return version of the feature - int version(); - -private: - void parentForShape(const GeomShapePtr& theShape, - const std::shared_ptr& theContext, - ObjectHierarchy& theShapesHierarchy); - private: OperationType myOperationType; }; diff --git a/src/FeaturesPlugin/FeaturesPlugin_BooleanCommon.cpp b/src/FeaturesPlugin/FeaturesPlugin_BooleanCommon.cpp index 9911d4283..99c3da987 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_BooleanCommon.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_BooleanCommon.cpp @@ -54,7 +54,7 @@ void FeaturesPlugin_BooleanCommon::initAttributes() data()->addAttribute(OBJECT_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); data()->addAttribute(TOOL_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); - initVersion(THE_COMMON_VERSION_1); + initVersion(THE_COMMON_VERSION_1, selectionList(OBJECT_LIST_ID()), selectionList(TOOL_LIST_ID())); } //================================================================================================== diff --git a/src/FeaturesPlugin/FeaturesPlugin_BooleanCut.cpp b/src/FeaturesPlugin/FeaturesPlugin_BooleanCut.cpp index 07ba068f7..383ffaa12 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_BooleanCut.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_BooleanCut.cpp @@ -47,7 +47,7 @@ FeaturesPlugin_BooleanCut::FeaturesPlugin_BooleanCut() void FeaturesPlugin_BooleanCut::initAttributes() { FeaturesPlugin_Boolean::initAttributes(); - initVersion(THE_CUT_VERSION_1); + initVersion(THE_CUT_VERSION_1, selectionList(OBJECT_LIST_ID()), selectionList(TOOL_LIST_ID())); } //================================================================================================== diff --git a/src/FeaturesPlugin/FeaturesPlugin_BooleanFill.cpp b/src/FeaturesPlugin/FeaturesPlugin_BooleanFill.cpp index 08d50f6f6..7d8d3f471 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_BooleanFill.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_BooleanFill.cpp @@ -48,11 +48,11 @@ FeaturesPlugin_BooleanFill::FeaturesPlugin_BooleanFill() { } -//================================================================================================== +//================================================================================================= void FeaturesPlugin_BooleanFill::initAttributes() { FeaturesPlugin_Boolean::initAttributes(); - initVersion(THE_SPLIT_VERSION_1); + initVersion(THE_SPLIT_VERSION_1, selectionList(OBJECT_LIST_ID()), selectionList(TOOL_LIST_ID())); } //================================================================================================= diff --git a/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp b/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp index a68ba8f50..9fccd0f08 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp @@ -62,7 +62,7 @@ void FeaturesPlugin_BooleanFuse::initAttributes() ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), OBJECT_LIST_ID()); ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), TOOL_LIST_ID()); - initVersion(THE_FUSE_VERSION_1); + initVersion(THE_FUSE_VERSION_1, selectionList(OBJECT_LIST_ID()), selectionList(TOOL_LIST_ID())); } //================================================================================================== diff --git a/src/FeaturesPlugin/FeaturesPlugin_BooleanSmash.cpp b/src/FeaturesPlugin/FeaturesPlugin_BooleanSmash.cpp index a46ed7409..f94009aa1 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_BooleanSmash.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_BooleanSmash.cpp @@ -49,7 +49,7 @@ void FeaturesPlugin_BooleanSmash::initAttributes() data()->addAttribute(OBJECT_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); data()->addAttribute(TOOL_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); - initVersion(THE_SMASH_VERSION_1); + initVersion(THE_SMASH_VERSION_1, selectionList(OBJECT_LIST_ID()), selectionList(TOOL_LIST_ID())); } //================================================================================================== diff --git a/src/FeaturesPlugin/FeaturesPlugin_VersionedBoolean.cpp b/src/FeaturesPlugin/FeaturesPlugin_VersionedBoolean.cpp new file mode 100644 index 000000000..ba7d81ba1 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_VersionedBoolean.cpp @@ -0,0 +1,658 @@ +// Copyright (C) 2014-2019 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include "FeaturesPlugin_VersionedBoolean.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//================================================================================================= +void FeaturesPlugin_VersionedBoolean::initVersion(const int theVersion, + const AttributePtr theObjectsAttr, + const AttributePtr theToolsAttr) +{ + AttributePtr aVerAttr = data()->addAttribute(VERSION_ID(), ModelAPI_AttributeInteger::typeId()); + aVerAttr->setIsArgument(false); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), VERSION_ID()); + if (!integer(VERSION_ID())->isInitialized() && + (!theObjectsAttr || !theObjectsAttr->isInitialized()) && + (!theToolsAttr || !theToolsAttr->isInitialized())) { + // this is a newly created feature (not read from file), + // so, initialize the latest version + integer(VERSION_ID())->setValue(theVersion); + } +} + +//================================================================================================= +void FeaturesPlugin_VersionedBoolean::parentForShape(const GeomShapePtr& theShape, + const ResultPtr& theContext, + ObjectHierarchy& theShapesHierarchy) +{ + ResultBodyPtr aResCompSolidPtr = ModelAPI_Tools::bodyOwner(theContext); + if (aResCompSolidPtr.get()) { + std::shared_ptr aContextShape = aResCompSolidPtr->shape(); + if (aContextShape->shapeType() <= GeomAPI_Shape::COMPSOLID) { + theShapesHierarchy.AddParent(theShape, aContextShape); + parentForShape(aContextShape, aResCompSolidPtr, theShapesHierarchy); + } + } +} + +bool FeaturesPlugin_VersionedBoolean::processAttribute(const std::string& theAttributeName, + ObjectHierarchy& theObjects, + ListOfShape& thePlanesList) +{ + AttributeSelectionListPtr anObjectsSelList = selectionList(theAttributeName); + for (int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) { + AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex); + GeomShapePtr anObject = anObjectAttr->value(); + if (!anObject.get()) { + // It could be a construction plane. + ResultPtr aContext = anObjectAttr->context(); + anObject = anObjectAttr->context()->shape(); + if (anObject.get()) { + thePlanesList.push_back(anObject); + continue; + } else + return false; + } + + theObjects.AddObject(anObject); + + ResultPtr aContext = anObjectAttr->context(); + parentForShape(anObject, aContext, theObjects); + } + return true; +} + +//================================================================================================= +bool FeaturesPlugin_VersionedBoolean::processObject( + const GeomAlgoAPI_Tools::BOPType theBooleanType, + const GeomShapePtr& theObject, + const ListOfShape& theTools, + const ListOfShape& thePlanes, + int& theResultIndex, + std::vector& theResultBaseAlgoList, + ListOfShape& theResultShapesList, + GeomShapePtr theResultCompound) +{ + ListOfShape aListWithObject; + aListWithObject.push_back(theObject); + std::shared_ptr aMakeShapeList(new GeomAlgoAPI_MakeShapeList()); + std::shared_ptr aBoolAlgo; + GeomShapePtr aResShape; + + std::list > aBoundingPoints = + GeomAlgoAPI_ShapeTools::getBoundingBox(aListWithObject, 1.0); + + // Resize planes. + ListOfShape aToolsWithPlanes = theTools; + for (ListOfShape::const_iterator anIt = thePlanes.begin(); anIt != thePlanes.end(); ++anIt) { + GeomShapePtr aPlane = *anIt; + GeomShapePtr aTool = GeomAlgoAPI_ShapeTools::fitPlaneToBox(aPlane, aBoundingPoints); + std::shared_ptr aMkShCustom( + new GeomAlgoAPI_MakeShapeCustom); + aMkShCustom->addModified(aPlane, aTool); + aMakeShapeList->appendAlgo(aMkShCustom); + aToolsWithPlanes.push_back(aTool); + } + + if (theBooleanType == GeomAlgoAPI_Tools::BOOL_PARTITION) + aBoolAlgo.reset(new GeomAlgoAPI_Partition(aListWithObject, aToolsWithPlanes)); + else + aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aListWithObject, + aToolsWithPlanes, + theBooleanType)); + + // Checking that the algorithm worked properly. + std::string anError; + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) { + setError(anError); + return false; + } + + aResShape = aBoolAlgo->shape(); + if (aResShape.get() && aResShape->shapeType() == GeomAPI_Shape::COMPOUND) { + int aSubResultsNb = 0; + GeomAPI_ShapeIterator anIt(aResShape); + for (; anIt.more(); anIt.next()) + ++aSubResultsNb; + + if (aSubResultsNb == 1) { + anIt.init(aResShape); + if (anIt.more()) + aResShape = anIt.current(); + } + } + + aMakeShapeList->appendAlgo(aBoolAlgo); + + GeomAPI_ShapeIterator aShapeIt(aResShape); + if (aShapeIt.more() || aResShape->shapeType() == GeomAPI_Shape::VERTEX) { + std::shared_ptr aResultBody; + + if (theResultCompound) { // store BOP result to the compound + std::shared_ptr aBuilder(new GeomAlgoAPI_ShapeBuilder); + aBuilder->add(theResultCompound, aResShape); + aMakeShapeList->appendAlgo(aBuilder); + } + else { // create a separate ResultBody + aResultBody = document()->createBody(data(), theResultIndex); + + // tools should be added to the list to fulfill the correct history of modification + aListWithObject.insert(aListWithObject.end(), theTools.begin(), theTools.end()); + + ListOfShape aUsedTools = theTools; + aUsedTools.insert(aUsedTools.end(), thePlanes.begin(), thePlanes.end()); + + FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, + aListWithObject, + aUsedTools, + aMakeShapeList, + aResShape); + setResult(aResultBody, theResultIndex); + ++theResultIndex; + } + + + FeaturesPlugin_Tools::ResultBaseAlgo aRBA; + aRBA.resultBody = aResultBody; + aRBA.baseShape = theObject; + aRBA.makeShape = aMakeShapeList; + theResultBaseAlgoList.push_back(aRBA); + theResultShapesList.push_back(aResShape); + } + return true; +} + +//================================================================================================= +bool FeaturesPlugin_VersionedBoolean::processCompsolid( + const GeomAlgoAPI_Tools::BOPType theBooleanType, + const ObjectHierarchy& theCompsolidHierarchy, + const GeomShapePtr& theCompsolid, + const ListOfShape& theTools, + const ListOfShape& thePlanes, + int& theResultIndex, + std::vector& theResultBaseAlgoList, + ListOfShape& theResultShapesList, + GeomShapePtr theResultCompound) +{ + ListOfShape aUsedInOperationSolids; + ListOfShape aNotUsedSolids; + theCompsolidHierarchy.SplitCompound(theCompsolid, aUsedInOperationSolids, aNotUsedSolids); + + std::shared_ptr aMakeShapeList(new GeomAlgoAPI_MakeShapeList()); + + std::list > aBoundingPoints = + GeomAlgoAPI_ShapeTools::getBoundingBox(aUsedInOperationSolids, 1.0); + + // Resize planes. + ListOfShape aToolsWithPlanes = theTools; + for (ListOfShape::const_iterator anIt = thePlanes.begin(); anIt != thePlanes.end(); ++anIt) + { + GeomShapePtr aPlane = *anIt; + GeomShapePtr aTool = GeomAlgoAPI_ShapeTools::fitPlaneToBox(aPlane, aBoundingPoints); + std::shared_ptr aMkShCustom( + new GeomAlgoAPI_MakeShapeCustom); + aMkShCustom->addModified(aPlane, aTool); + aMakeShapeList->appendAlgo(aMkShCustom); + aToolsWithPlanes.push_back(aTool); + } + + std::shared_ptr aBoolAlgo; + if (theBooleanType == GeomAlgoAPI_Tools::BOOL_PARTITION) + aBoolAlgo.reset(new GeomAlgoAPI_Partition(aUsedInOperationSolids, aToolsWithPlanes)); + else + aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aUsedInOperationSolids, + aToolsWithPlanes, + theBooleanType)); + + // Checking that the algorithm worked properly. + std::string anError; + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) { + setError(anError); + return false; + } + + aMakeShapeList->appendAlgo(aBoolAlgo); + GeomShapePtr aResultShape = aBoolAlgo->shape(); + + // Add result to not used solids from compsolid. + if (!aNotUsedSolids.empty()) { + ListOfShape aShapesToAdd = aNotUsedSolids; + aShapesToAdd.push_back(aBoolAlgo->shape()); + std::shared_ptr aFillerAlgo( + new GeomAlgoAPI_PaveFiller(aShapesToAdd, true)); + if (!aFillerAlgo->isDone()) { + std::string aFeatureError = "Error: PaveFiller algorithm failed."; + setError(aFeatureError); + return false; + } + + aMakeShapeList->appendAlgo(aFillerAlgo); + aResultShape = aFillerAlgo->shape(); + } + + GeomAPI_ShapeIterator aShapeIt(aResultShape); + if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX) + { + std::shared_ptr aResultBody; + + if (theResultCompound) { // store BOP result to the compound + std::shared_ptr aBuilder(new GeomAlgoAPI_ShapeBuilder); + aBuilder->add(theResultCompound, aResultShape); + aMakeShapeList->appendAlgo(aBuilder); + } + else { // create a separate ResultBody + aResultBody = document()->createBody(data(), theResultIndex); + + ListOfShape aCompSolidList; + aCompSolidList.push_back(theCompsolid); + // tools should be added to the list to fulfill the correct history of modification + aCompSolidList.insert(aCompSolidList.end(), theTools.begin(), theTools.end()); + + ListOfShape aUsedTools = theTools; + aUsedTools.insert(aUsedTools.end(), thePlanes.begin(), thePlanes.end()); + + FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, + aCompSolidList, + aUsedTools, + aMakeShapeList, + aResultShape); + setResult(aResultBody, theResultIndex); + ++theResultIndex; + } + + FeaturesPlugin_Tools::ResultBaseAlgo aRBA; + aRBA.resultBody = aResultBody; + aRBA.baseShape = theCompsolid; + aRBA.makeShape = aMakeShapeList; + theResultBaseAlgoList.push_back(aRBA); + theResultShapesList.push_back(aResultShape); + } + return true; +} + +//================================================================================================= +bool FeaturesPlugin_VersionedBoolean::processCompound( + const GeomAlgoAPI_Tools::BOPType theBooleanType, + const ObjectHierarchy& theCompoundHierarchy, + const GeomShapePtr& theCompound, + const ListOfShape& theTools, + int& theResultIndex, + std::vector& theResultBaseAlgoList, + ListOfShape& theResultShapesList, + GeomShapePtr theResultCompound) +{ + ListOfShape aUsedInOperationShapes; + ListOfShape aNotUsedShapes; + theCompoundHierarchy.SplitCompound(theCompound, aUsedInOperationShapes, aNotUsedShapes); + if (theResultCompound) { + // Not necessary to keep all subs of the current compound, + // all unused solids are already stored in the result compound. + aNotUsedShapes.clear(); + } + + std::shared_ptr aMakeShapeList(new GeomAlgoAPI_MakeShapeList()); + std::shared_ptr aBoolAlgo( + new GeomAlgoAPI_Boolean(aUsedInOperationShapes, + theTools, + theBooleanType)); + + // Checking that the algorithm worked properly. + std::string anError; + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) { + setError(anError); + return false; + } + + aMakeShapeList->appendAlgo(aBoolAlgo); + GeomShapePtr aResultShape = aBoolAlgo->shape(); + + // Add result to not used shape from compound. + if (!aNotUsedShapes.empty()) { + ListOfShape aShapesForResult = aNotUsedShapes; + if (aResultShape->shapeType() == GeomAPI_Shape::COMPOUND) { + for (GeomAPI_ShapeIterator aResultIt(aResultShape); aResultIt.more(); aResultIt.next()) { + aShapesForResult.push_back(aResultIt.current()); + } + } + else { + aShapesForResult.push_back(aResultShape); + } + + if (aShapesForResult.size() == 1) { + aResultShape = aShapesForResult.front(); + } + else { + aResultShape = GeomAlgoAPI_CompoundBuilder::compound(aShapesForResult); + } + } + + GeomAPI_ShapeIterator aShapeIt(aResultShape); + if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX) { + std::shared_ptr aResultBody; + + if (theResultCompound) { // store BOP result to the compound + std::shared_ptr aBuilder(new GeomAlgoAPI_ShapeBuilder); + aBuilder->add(theResultCompound, aResultShape); + aMakeShapeList->appendAlgo(aBuilder); + } + else { // create a separate ResultBody + aResultBody = document()->createBody(data(), theResultIndex); + + ListOfShape aCompoundList; + aCompoundList.push_back(theCompound); + FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, + aCompoundList, + theTools, + aMakeShapeList, + aResultShape); + setResult(aResultBody, theResultIndex); + ++theResultIndex; + } + + FeaturesPlugin_Tools::ResultBaseAlgo aRBA; + aRBA.resultBody = aResultBody; + aRBA.baseShape = theCompound; + aRBA.makeShape = aMakeShapeList; + theResultBaseAlgoList.push_back(aRBA); + theResultShapesList.push_back(aResultShape); + } + return true; +} + +//================================================================================================== +GeomShapePtr FeaturesPlugin_VersionedBoolean::keepUnusedSubsOfCompound( + const GeomShapePtr& theResult, + const ObjectHierarchy& theObjectsHierarchy, + const ObjectHierarchy& theToolsHierarchy, + std::shared_ptr theMakeShapeList) +{ + ListOfShape aCompounds; + theObjectsHierarchy.CompoundsOfUnusedObjects(aCompounds); + theToolsHierarchy.CompoundsOfUnusedObjects(aCompounds); + + GeomShapePtr aResultShape = theResult; + if (!aCompounds.empty()) { + aResultShape = aCompounds.front(); + aCompounds.pop_front(); + + std::shared_ptr aBuilder(new GeomAlgoAPI_ShapeBuilder); + for (ListOfShape::iterator anIt = aCompounds.begin(); anIt != aCompounds.end(); ++anIt) { + for (GeomAPI_ShapeIterator aSub(*anIt); aSub.more(); aSub.next()) + aBuilder->add(aResultShape, aSub.current()); + } + + if (theResult) + aBuilder->add(aResultShape, theResult); + + theMakeShapeList->appendAlgo(aBuilder); + } + return aResultShape; +} + +//================================================================================================= +int FeaturesPlugin_VersionedBoolean::version() +{ + AttributeIntegerPtr aVersionAttr = integer(VERSION_ID()); + int aVersion = 0; + if (aVersionAttr && aVersionAttr->isInitialized()) + aVersion = aVersionAttr->value(); + return aVersion; +} + +//================================================================================================= + +void FeaturesPlugin_VersionedBoolean::ObjectHierarchy::AddObject(const GeomShapePtr& theObject) +{ + myObjects.push_back(theObject); +} + +void FeaturesPlugin_VersionedBoolean::ObjectHierarchy::AddParent(const GeomShapePtr& theShape, + const GeomShapePtr& theParent) +{ + myParent[theShape] = theParent; + + MapShapeToIndex::iterator aFound = myParentIndices.find(theParent); + size_t anIndex = myParentIndices.size(); + if (aFound == myParentIndices.end()) { + myParentIndices[theParent] = anIndex; + mySubshapes.push_back(ShapeAndSubshapes(theParent, ListOfShape())); + } else + anIndex = aFound->second; + + mySubshapes[anIndex].second.push_back(theShape); +} + +GeomShapePtr FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Parent(const GeomShapePtr& theShape, + bool theMarkProcessed) +{ + MapShapeToParent::const_iterator aFound = myParent.find(theShape); + GeomShapePtr aParent; + if (aFound != myParent.end()) { + aParent = aFound->second; + if (theMarkProcessed) { + // mark the parent and all its subs as processed by Boolean algorithm + myProcessedObjects.insert(aParent); + const ListOfShape& aSubs = mySubshapes[myParentIndices[aParent]].second; + for (ListOfShape::const_iterator anIt = aSubs.begin(); anIt != aSubs.end(); ++anIt) + myProcessedObjects.insert(*anIt); + } + } + return aParent; +} + +void FeaturesPlugin_VersionedBoolean::ObjectHierarchy::ObjectsByType( + ListOfShape& theShapesByType, + ListOfShape& theOtherShapes, + const GeomAPI_Shape::ShapeType theMinType, + const GeomAPI_Shape::ShapeType theMaxType) const +{ + if (theMinType > theMaxType) + return ObjectsByType(theShapesByType, theOtherShapes, theMaxType, theMinType); + + // no need to select objects if whole range is specified + if (theMinType == GeomAPI_Shape::COMPOUND && theMaxType == GeomAPI_Shape::SHAPE) { + theShapesByType.insert(theShapesByType.end(), myObjects.begin(), myObjects.end()); + return; + } + + for (ListOfShape::const_iterator anIt = myObjects.begin(); anIt != myObjects.end(); ++anIt) { + GeomAPI_Shape::ShapeType aType = (*anIt)->shapeType(); + if (aType >= theMinType && aType <= theMaxType) + theShapesByType.push_back(*anIt); + else + theOtherShapes.push_back(*anIt); + } +} + + +void FeaturesPlugin_VersionedBoolean::ObjectHierarchy::SplitCompound(const GeomShapePtr& theCompShape, + ListOfShape& theUsed, + ListOfShape& theNotUsed) const +{ + theUsed.clear(); + theNotUsed.clear(); + + MapShapeToIndex::const_iterator aFoundIndex = myParentIndices.find(theCompShape); + if (aFoundIndex == myParentIndices.end()) + return; // no such shape + + theUsed = mySubshapes[aFoundIndex->second].second; + SetOfShape aSubsSet; + aSubsSet.insert(theUsed.begin(), theUsed.end()); + + for (GeomAPI_ShapeIterator anExp(theCompShape); anExp.more(); anExp.next()) { + GeomShapePtr aCurrent = anExp.current(); + if (aSubsSet.find(aCurrent) == aSubsSet.end()) + theNotUsed.push_back(aCurrent); + } +} + +bool FeaturesPlugin_VersionedBoolean::ObjectHierarchy::IsEmpty() const +{ + return myObjects.empty(); +} + +void FeaturesPlugin_VersionedBoolean::ObjectHierarchy::CompoundsOfUnusedObjects( + ListOfShape& theDestination) const +{ + SetOfShape aUsedObjects; + aUsedObjects.insert(myObjects.begin(), myObjects.end()); + + for (std::vector::const_iterator anIt = mySubshapes.begin(); + anIt != mySubshapes.end(); ++anIt) { + MapShapeToParent::const_iterator aParent = myParent.find(anIt->first); + if ((aParent == myParent.end() || !aParent->second) && + anIt->first->shapeType() == GeomAPI_Shape::COMPOUND) { + // this is a top-level compound + GeomShapePtr aCompound = collectUnusedSubs(anIt->first, aUsedObjects); + // add to destination non-empty compounds only + if (aCompound) + theDestination.push_back(aCompound); + } + } +} + +GeomShapePtr FeaturesPlugin_VersionedBoolean::ObjectHierarchy::collectUnusedSubs( + GeomShapePtr theTopLevelCompound, + const SetOfShape& theUsed) const +{ + GeomShapePtr aResult = theTopLevelCompound->emptyCopied(); + bool isResultEmpty = true; + + for (GeomAPI_ShapeIterator aSub(theTopLevelCompound); aSub.more(); aSub.next()) { + GeomShapePtr aCurrent = aSub.current(); + if (theUsed.find(aCurrent) != theUsed.end()) + continue; // already used + + MapShapeToIndex::const_iterator aFoundIndex = myParentIndices.find(aCurrent); + if (aCurrent->shapeType() > GeomAPI_Shape::COMPOUND || + aFoundIndex == myParentIndices.end()) { + bool isAddShape = true; + // check compsolid is fully unused in the Boolean operation + if (aCurrent->shapeType() == GeomAPI_Shape::COMPSOLID) { + for (GeomAPI_ShapeIterator anIt(aCurrent); isAddShape && anIt.more(); anIt.next()) + isAddShape = theUsed.find(anIt.current()) == theUsed.end(); + } + + if (isAddShape) { // low-level shape, add it + GeomAlgoAPI_ShapeBuilder::add(aResult, aCurrent); + isResultEmpty = false; + } + } else { + GeomShapePtr aCompound = collectUnusedSubs(aCurrent, theUsed); + if (aCompound) { + GeomAlgoAPI_ShapeBuilder::add(theTopLevelCompound, aCompound); + isResultEmpty = false; + } + } + } + return isResultEmpty ? GeomShapePtr() : aResult; +} + + +FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Begin() +{ + return Iterator(this); +} + +FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator FeaturesPlugin_VersionedBoolean::ObjectHierarchy::End() +{ + return Iterator(this, false); +} + +FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator::Iterator( + FeaturesPlugin_VersionedBoolean::ObjectHierarchy* theHierarchy, bool isBegin) + : myHierarchy(theHierarchy) +{ + if (isBegin) { + myObject = myHierarchy->myObjects.begin(); + SkipAlreadyProcessed(); + } else + myObject = myHierarchy->myObjects.end(); +} + +void FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator::SkipAlreadyProcessed() +{ + while (myObject != myHierarchy->myObjects.end() && + myHierarchy->myProcessedObjects.find(*myObject) != myHierarchy->myProcessedObjects.end()) + ++myObject; +} + +bool FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator::operator==(const Iterator& theOther) const +{ + return myObject == theOther.myObject; +} + +bool FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator::operator!=(const Iterator& theOther) const +{ + return !operator==(theOther); +} + +FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator& +FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator::operator++() +{ + ++myObject; + SkipAlreadyProcessed(); + return *this; +} + +FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator +FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator::operator++(int) +{ + Iterator aCurrent; + aCurrent.myHierarchy = myHierarchy; + aCurrent.myObject = myObject; + + // increase iterator + operator++(); + + return aCurrent; +} + +GeomShapePtr FeaturesPlugin_VersionedBoolean::ObjectHierarchy::Iterator::operator*() const +{ + myHierarchy->myProcessedObjects.insert(*myObject); + return *myObject; +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_VersionedBoolean.h b/src/FeaturesPlugin/FeaturesPlugin_VersionedBoolean.h new file mode 100644 index 000000000..81e662649 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_VersionedBoolean.h @@ -0,0 +1,200 @@ +// Copyright (C) 2014-2019 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef FeaturesPlugin_VersionedBoolean_H_ +#define FeaturesPlugin_VersionedBoolean_H_ + +#include "FeaturesPlugin.h" +#include "FeaturesPlugin_Tools.h" + +#include + +#include + +class ModelAPI_Attribute; +class ModelAPI_Result; +class GeomAlgoAPI_MakeShapeList; + +/// \class FeaturesPlugin_VersionedBoolean +/// \ingroup Plugins +/// \brief Feature controls a version of Boolean operations. +class FeaturesPlugin_VersionedBoolean : public ModelAPI_Feature +{ +public: + /// Attribute name of the version of Boolean feature + inline static const std::string& VERSION_ID() + { + static const std::string MY_VERSION_ID("version"); + return MY_VERSION_ID; + } + +protected: + + /// Use plugin manager for features creation. + FeaturesPlugin_VersionedBoolean() {} + + /// Initialize version field of the Boolean feature. + /// The version is initialized for newly created features, + /// not read from previously stored document. + void initVersion(const int theVersion, + const std::shared_ptr theObjectsAttr, + const std::shared_ptr theToolsAttr); + + /// Auxiliary class to store hierarchy of Boolean operation objects/tools + /// and their parent shapes (compounds or compsolids) + class ObjectHierarchy { + typedef std::pair ShapeAndSubshapes; + typedef std::map MapShapeToParent; + typedef std::map MapShapeToIndex; + typedef std::set SetOfShape; + + ListOfShape myObjects; ///< list of objects/tools of Boolean operation + MapShapeToParent myParent; ///< refer a shape to compound/compsolid containing it + /// indices of compounds/compsolids to keep the order of parent shapes + /// corresponding to the order of objects + MapShapeToIndex myParentIndices; + /// list of shape and its subshapes stored according to the index of parent shape + std::vector mySubshapes; + + SetOfShape myProcessedObjects; + + public: + /// Add object of Boolean opration + void AddObject(const GeomShapePtr& theObject); + + /// Maps shape and its parent + void AddParent(const GeomShapePtr& theShape, const GeomShapePtr& theParent); + + /// Return parent shape for the given, or empty if it is a high-level shape. + /// By default, the parent and all its subshapes are marked as processed for further skip. + GeomShapePtr Parent(const GeomShapePtr& theShape, bool theMarkProcessed = true); + + /// Split compound/compsolid shape for subshapes selected for Boolean operation and the other. + void SplitCompound(const GeomShapePtr& theCompShape, + ListOfShape& theUsed, + ListOfShape& theNotUsed) const; + + /// Generates the list of top-level compounds, which contain the objects of Boolean operation. + /// The generated list will contain only shapes unused during the Boolean operation. + void CompoundsOfUnusedObjects(ListOfShape& theDestination) const; + + /// Return \c true if there is no object in hierarchy + bool IsEmpty() const; + + /// Return list of objects + const ListOfShape& Objects() const { return myObjects; } + /// Separate objects of the given range of types and all other objects + void ObjectsByType(ListOfShape& theShapesByType, ListOfShape& theOtherShapes, + const GeomAPI_Shape::ShapeType theMinType = GeomAPI_Shape::COMPOUND, + const GeomAPI_Shape::ShapeType theMaxType = GeomAPI_Shape::SHAPE) const; + + private: + GeomShapePtr collectUnusedSubs(const GeomShapePtr theTopLevelCompound, + const SetOfShape& theUsed) const; + + public: + class Iterator { + friend class ObjectHierarchy; + + ObjectHierarchy* myHierarchy; + ListOfShape::iterator myObject; + + Iterator() {} + Iterator(ObjectHierarchy* theHierarchy, bool isBegin = true); + + void SkipAlreadyProcessed(); + + public: + bool operator==(const Iterator&) const; + bool operator!=(const Iterator&) const; + + Iterator& operator++(); + Iterator operator++(int); + + GeomShapePtr operator*() const; + }; + + Iterator Begin(); + Iterator End(); + }; + + /// Process SelectionList attribute and fill the objects hierarchy. + bool processAttribute(const std::string& theAttributeName, + ObjectHierarchy& theObjects, + ListOfShape& thePlanesList); + + /// Perform Boolean operation of the object with the tools. + /// In case of theResultCompound is not empty, the result of Boolean operation + /// is added to this compound, and corresponding ResultBody is not generated. + /// \return \c false if something went wrong + bool processObject(const GeomAlgoAPI_Tools::BOPType theBooleanType, + const GeomShapePtr& theObject, + const ListOfShape& theTools, + const ListOfShape& thePlanes, + int& theResultIndex, + std::vector& theResultBaseAlgoList, + ListOfShape& theResultShapesList, + GeomShapePtr theResulCompound = GeomShapePtr()); + + /// Perform Boolean operation of the Compsolid with the tools + /// In case of theResultCompound is not empty, the result of Boolean operation + /// is added to this compound, and corresponding ResultBody is not generated. + /// \return \c false if something went wrong + bool processCompsolid(const GeomAlgoAPI_Tools::BOPType theBooleanType, + const ObjectHierarchy& theCompsolidHierarchy, + const GeomShapePtr& theCompsolid, + const ListOfShape& theTools, + const ListOfShape& thePlanes, + int& theResultIndex, + std::vector& theResultBaseAlgoList, + ListOfShape& theResultShapesList, + GeomShapePtr theResulCompound = GeomShapePtr()); + + /// Perform Boolean operation of the Compound with the tools + /// In case of theResultCompound is not empty, the result of Boolean operation + /// is added to this compound, and corresponding ResultBody is not generated. + /// \return \c false if something went wrong + bool processCompound(const GeomAlgoAPI_Tools::BOPType theBooleanType, + const ObjectHierarchy& theCompoundHierarchy, + const GeomShapePtr& theCompound, + const ListOfShape& theTools, + int& theResultIndex, + std::vector& theResultBaseAlgoList, + ListOfShape& theResultShapesList, + GeomShapePtr theResulCompound = GeomShapePtr()); + + /// Process unused sub-shapes of compounds. + /// Keep the compound hierarchy, but merge top-level compounds + /// into a single compound and add the result of the FUSE operation. + GeomShapePtr keepUnusedSubsOfCompound( + const GeomShapePtr& theResult, + const ObjectHierarchy& theObjectsHierarchy, + const ObjectHierarchy& theToolsHierarchy, + std::shared_ptr theMakeShapeList); + + /// Return version of the feature + int version(); + +private: + void parentForShape(const GeomShapePtr& theShape, + const std::shared_ptr& theContext, + ObjectHierarchy& theShapesHierarchy); +}; + +#endif