Salome HOME
Small refactoring: separate storing a compound of sub-results.
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_Boolean.cpp
index d466d1a1348e046c44d7350dc6027645309ebfd9..066bf02af3c038979b3b535fa332e8c266f9da08 100644 (file)
@@ -35,6 +35,7 @@
 #include <GeomAlgoAPI_MakeShapeList.h>
 #include <GeomAlgoAPI_Partition.h>
 #include <GeomAlgoAPI_PaveFiller.h>
+#include <GeomAlgoAPI_ShapeBuilder.h>
 #include <GeomAlgoAPI_ShapeTools.h>
 #include <GeomAlgoAPI_Tools.h>
 #include <GeomAPI_Face.h>
@@ -64,6 +65,21 @@ 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()
 {
@@ -153,7 +169,8 @@ bool FeaturesPlugin_Boolean::processObject(
     const ListOfShape& thePlanes,
     int& theResultIndex,
     std::vector<FeaturesPlugin_Tools::ResultBaseAlgo>& theResultBaseAlgoList,
-    ListOfShape& theResultShapesList)
+    ListOfShape& theResultShapesList,
+    GeomShapePtr theResultCompound)
 {
   ListOfShape aListWithObject;
   aListWithObject.push_back(theObject);
@@ -208,19 +225,31 @@ bool FeaturesPlugin_Boolean::processObject(
 
   GeomAPI_ShapeIterator aShapeIt(aResShape);
   if (aShapeIt.more() || aResShape->shapeType() == GeomAPI_Shape::VERTEX) {
-    std::shared_ptr<ModelAPI_ResultBody> aResultBody =
-        document()->createBody(data(), theResultIndex);
+    std::shared_ptr<ModelAPI_ResultBody> aResultBody;
 
-    ListOfShape aUsedTools = theTools;
-    aUsedTools.insert(aUsedTools.end(), thePlanes.begin(), thePlanes.end());
+    if (theResultCompound) { // store BOP result to the compound
+      std::shared_ptr<GeomAlgoAPI_ShapeBuilder> 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::loadModifiedShapes(aResultBody,
-                                             aListWithObject,
-                                             aUsedTools,
-                                             aMakeShapeList,
-                                             aResShape);
-    setResult(aResultBody, theResultIndex);
-    ++theResultIndex;
 
     FeaturesPlugin_Tools::ResultBaseAlgo aRBA;
     aRBA.resultBody = aResultBody;
@@ -241,7 +270,8 @@ bool FeaturesPlugin_Boolean::processCompsolid(
     const ListOfShape& thePlanes,
     int& theResultIndex,
     std::vector<FeaturesPlugin_Tools::ResultBaseAlgo>& theResultBaseAlgoList,
-    ListOfShape& theResultShapesList)
+    ListOfShape& theResultShapesList,
+    GeomShapePtr theResultCompound)
 {
   ListOfShape aUsedInOperationSolids;
   ListOfShape aNotUsedSolids;
@@ -302,22 +332,32 @@ bool FeaturesPlugin_Boolean::processCompsolid(
   GeomAPI_ShapeIterator aShapeIt(aResultShape);
   if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX)
   {
-    std::shared_ptr<ModelAPI_ResultBody> aResultBody =
-        document()->createBody(data(), theResultIndex);
-
-    ListOfShape aCompSolidList;
-    aCompSolidList.push_back(theCompsolid);
-
-    ListOfShape aUsedTools = theTools;
-    aUsedTools.insert(aUsedTools.end(), thePlanes.begin(), thePlanes.end());
+    std::shared_ptr<ModelAPI_ResultBody> aResultBody;
 
-    FeaturesPlugin_Tools::loadModifiedShapes(aResultBody,
-                                             aCompSolidList,
-                                             aUsedTools,
-                                             aMakeShapeList,
-                                             aResultShape);
-    setResult(aResultBody, theResultIndex);
-    ++theResultIndex;
+    if (theResultCompound) { // store BOP result to the compound
+      std::shared_ptr<GeomAlgoAPI_ShapeBuilder> 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;
@@ -337,11 +377,17 @@ bool FeaturesPlugin_Boolean::processCompound(
     const ListOfShape& theTools,
     int& theResultIndex,
     std::vector<FeaturesPlugin_Tools::ResultBaseAlgo>& theResultBaseAlgoList,
-    ListOfShape& theResultShapesList)
+    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<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
   std::shared_ptr<GeomAlgoAPI_Boolean> aBoolAlgo(
@@ -381,18 +427,26 @@ bool FeaturesPlugin_Boolean::processCompound(
 
   GeomAPI_ShapeIterator aShapeIt(aResultShape);
   if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX) {
-    std::shared_ptr<ModelAPI_ResultBody> aResultBody =
-        document()->createBody(data(), theResultIndex);
-
-    ListOfShape aCompoundList;
-    aCompoundList.push_back(theCompound);
-    FeaturesPlugin_Tools::loadModifiedShapes(aResultBody,
-                                             aCompoundList,
-                                             theTools,
-                                             aMakeShapeList,
-                                             aResultShape);
-    setResult(aResultBody, theResultIndex);
-    ++theResultIndex;
+    std::shared_ptr<ModelAPI_ResultBody> aResultBody;
+
+    if (theResultCompound) { // store BOP result to the compound
+      std::shared_ptr<GeomAlgoAPI_ShapeBuilder> 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;
@@ -404,6 +458,82 @@ bool FeaturesPlugin_Boolean::processCompound(
   return true;
 }
 
+//==================================================================================================
+GeomShapePtr FeaturesPlugin_Boolean::keepUnusedSubsOfCompound(
+    const GeomShapePtr& theResult,
+    const ObjectHierarchy& theObjectsHierarchy,
+    const ObjectHierarchy& theToolsHierarchy,
+    std::shared_ptr<GeomAlgoAPI_MakeShapeList> theMakeShapeList)
+{
+  ListOfShape aCompounds;
+  theObjectsHierarchy.CompoundsOfUnusedObjects(aCompounds);
+  theToolsHierarchy.CompoundsOfUnusedObjects(aCompounds);
+
+  GeomShapePtr aResultShape = theResult;
+  if (!aCompounds.empty()) {
+    aResultShape = aCompounds.front();
+    aCompounds.pop_front();
+
+    std::shared_ptr<GeomAlgoAPI_ShapeBuilder> 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,
+    const ListOfShape& theTools,
+    const GeomShapePtr theResultShape,
+    int& theResultIndex,
+    std::shared_ptr<GeomAlgoAPI_MakeShapeList> theMakeShapeList,
+    std::vector<FeaturesPlugin_Tools::ResultBaseAlgo>& theResultBaseAlgoList)
+{
+  if (!theResultShape)
+    return;
+
+  std::shared_ptr<ModelAPI_ResultBody> aResultBody =
+      document()->createBody(data(), theResultIndex);
+
+  FeaturesPlugin_Tools::loadModifiedShapes(aResultBody,
+                                           theObjects,
+                                           theTools,
+                                           theMakeShapeList,
+                                           theResultShape);
+  setResult(aResultBody, theResultIndex++);
+
+  // merge algorithms
+  FeaturesPlugin_Tools::ResultBaseAlgo aRBA;
+  aRBA.resultBody = aResultBody;
+  aRBA.baseShape = theObjects.front();
+  for (std::vector<FeaturesPlugin_Tools::ResultBaseAlgo>::iterator
+       aRBAIt = theResultBaseAlgoList.begin();
+       aRBAIt != theResultBaseAlgoList.end(); ++aRBAIt) {
+    theMakeShapeList->appendAlgo(aRBAIt->makeShape);
+  }
+  aRBA.makeShape = theMakeShapeList;
+  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)
@@ -415,7 +545,16 @@ void FeaturesPlugin_Boolean::ObjectHierarchy::AddParent(const GeomShapePtr& theS
                                                         const GeomShapePtr& theParent)
 {
   myParent[theShape] = theParent;
-  mySubshapes[theParent].push_back(theShape);
+
+  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,
@@ -428,7 +567,7 @@ GeomShapePtr FeaturesPlugin_Boolean::ObjectHierarchy::Parent(const GeomShapePtr&
     if (theMarkProcessed) {
       // mark the parent and all its subs as processed by Boolean algorithm
       myProcessedObjects.insert(aParent);
-      const ListOfShape& aSubs = mySubshapes[aParent];
+      const ListOfShape& aSubs = mySubshapes[myParentIndices[aParent]].second;
       for (ListOfShape::const_iterator anIt = aSubs.begin(); anIt != aSubs.end(); ++anIt)
         myProcessedObjects.insert(*anIt);
     }
@@ -436,6 +575,31 @@ GeomShapePtr FeaturesPlugin_Boolean::ObjectHierarchy::Parent(const GeomShapePtr&
   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
@@ -443,17 +607,18 @@ void FeaturesPlugin_Boolean::ObjectHierarchy::SplitCompound(const GeomShapePtr&
   theUsed.clear();
   theNotUsed.clear();
 
-  const ListOfShape& aSubs = mySubshapes.find(theCompShape)->second;
+  MapShapeToIndex::const_iterator aFoundIndex = myParentIndices.find(theCompShape);
+  if (aFoundIndex == myParentIndices.end())
+    return; // no such shape
+
+  theUsed = mySubshapes[aFoundIndex->second].second;
   SetOfShape aSubsSet;
-  aSubsSet.insert(aSubs.begin(), aSubs.end());
+  aSubsSet.insert(theUsed.begin(), theUsed.end());
 
-  for (GeomAPI_ShapeExplorer anExp(theCompShape, GeomAPI_Shape::SOLID);
-       anExp.more(); anExp.next()) {
+  for (GeomAPI_ShapeIterator anExp(theCompShape); anExp.more(); anExp.next()) {
     GeomShapePtr aCurrent = anExp.current();
     if (aSubsSet.find(aCurrent) == aSubsSet.end())
       theNotUsed.push_back(aCurrent);
-    else
-      theUsed.push_back(aCurrent);
   }
 }
 
@@ -462,6 +627,64 @@ 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<ShapeAndSubshapes>::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);