Salome HOME
Boolean Smash feature
authordbv <dbv@opencascade.com>
Wed, 9 Mar 2016 12:06:25 +0000 (15:06 +0300)
committerdbv <dbv@opencascade.com>
Fri, 25 Mar 2016 13:19:16 +0000 (16:19 +0300)
Test for Smash operation

src/FeaturesPlugin/CMakeLists.txt
src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp
src/FeaturesPlugin/FeaturesPlugin_Boolean.h
src/FeaturesPlugin/Test/TestBooleanSmash.py [new file with mode: 0644]
src/FeaturesPlugin/boolean_widget.xml
src/ModuleBase/ModuleBase_WidgetChoice.h
src/PartSet/PartSet_icons.qrc
src/PartSet/icons/bool_smash.png [new file with mode: 0644]

index 8dea5b736abaaa879b8d6bbe78974a0e2764fe50..bb7ff7c59640e21da486f9d471d85b1f6b7a809f 100644 (file)
@@ -107,6 +107,7 @@ ADD_UNIT_TESTS(TestExtrusion.py
                TestRotation.py
                TestBoolean.py
                TestBooleanCompSolids.py
+               TestBooleanSmash.py
                TestMultiBoolean.py
                TestSerialBoolean.py
                TestGroup.py
index 7360f443cda6aeaedf3b9b533bdff020195fb76c..701017b5238726c29f328ebcb12dd56dc9021d33 100644 (file)
@@ -71,7 +71,7 @@ void FeaturesPlugin_Boolean::execute()
       ModelAPI_AttributeInteger>(data()->attribute(FeaturesPlugin_Boolean::TYPE_ID()));
   if (!aTypeAttr)
     return;
-  GeomAlgoAPI_Boolean::OperationType aType = (GeomAlgoAPI_Boolean::OperationType)aTypeAttr->value();
+  OperationType aType = (FeaturesPlugin_Boolean::OperationType)aTypeAttr->value();
 
   ListOfShape anObjects, aTools, anEdgesAndFaces;
   std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape> aCompSolidsObjects;
@@ -127,8 +127,8 @@ void FeaturesPlugin_Boolean::execute()
   int aResultIndex = 0;
 
   switch(aType) {
-    case GeomAlgoAPI_Boolean::BOOL_CUT:
-    case GeomAlgoAPI_Boolean::BOOL_COMMON:{
+    case BOOL_CUT:
+    case BOOL_COMMON:{
       if((anObjects.empty() && aCompSolidsObjects.empty()) || aTools.empty()) {
         std::string aFeatureError = "Error: Not enough objects for boolean operation.";
         setError(aFeatureError);
@@ -140,7 +140,7 @@ void FeaturesPlugin_Boolean::execute()
         std::shared_ptr<GeomAPI_Shape> anObject = *anObjectsIt;
         ListOfShape aListWithObject;
         aListWithObject.push_back(anObject);
-        GeomAlgoAPI_Boolean aBoolAlgo(aListWithObject, aTools, aType);
+        GeomAlgoAPI_Boolean aBoolAlgo(aListWithObject, aTools, (GeomAlgoAPI_Boolean::OperationType)aType);
 
         // Checking that the algorithm worked properly.
         if(!aBoolAlgo.isDone()) {
@@ -188,7 +188,9 @@ void FeaturesPlugin_Boolean::execute()
           }
         }
 
-        std::shared_ptr<GeomAlgoAPI_Boolean> aBoolAlgo(new GeomAlgoAPI_Boolean(aUsedInOperationSolids, aTools, aType));
+        std::shared_ptr<GeomAlgoAPI_Boolean> aBoolAlgo(new GeomAlgoAPI_Boolean(aUsedInOperationSolids,
+                                                                               aTools,
+                                                                               (GeomAlgoAPI_Boolean::OperationType)aType));
 
         // Checking that the algorithm worked properly.
         if(!aBoolAlgo->isDone()) {
@@ -234,7 +236,7 @@ void FeaturesPlugin_Boolean::execute()
       }
       break;
     }
-    case GeomAlgoAPI_Boolean::BOOL_FUSE: {
+    case BOOL_FUSE: {
       if((anObjects.size() + aTools.size() + aCompSolidsObjects.size() + anEdgesAndFaces.size()) < 2) {
         std::string aFeatureError = "Error: Not enough objects for boolean operation.";
         setError(aFeatureError);
@@ -316,7 +318,9 @@ void FeaturesPlugin_Boolean::execute()
       } else if(anObjects.empty() && aTools.size() == 1) {
         aShape = aTools.front();
       } else if((anObjects.size() + aTools.size()) > 1){
-        std::shared_ptr<GeomAlgoAPI_Boolean> aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects, aTools, aType));
+        std::shared_ptr<GeomAlgoAPI_Boolean> aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects,
+                                                                               aTools,
+                                                                               (GeomAlgoAPI_Boolean::OperationType)aType));
 
         // Checking that the algorithm worked properly.
         if(!aFuseAlgo->isDone()) {
@@ -380,6 +384,132 @@ void FeaturesPlugin_Boolean::execute()
       aResultIndex++;
       break;
     }
+    case BOOL_SMASH: {
+      if((anObjects.empty() && aCompSolidsObjects.empty()) || aTools.empty()) {
+        std::string aFeatureError = "Error: Not enough objects for boolean operation.";
+        setError(aFeatureError);
+        return;
+      }
+
+      // List of original solids for naming.
+      ListOfShape anOriginalShapes;
+      anOriginalShapes.insert(anOriginalShapes.end(), anObjects.begin(), anObjects.end());
+      anOriginalShapes.insert(anOriginalShapes.end(), aTools.begin(), aTools.end());
+
+      // Collecting all solids which will be smashed.
+      ListOfShape aShapesToSmash;
+      aShapesToSmash.insert(aShapesToSmash.end(), anObjects.begin(), anObjects.end());
+
+      // Collecting solids from compsolids which will not be modified in boolean operation and will be added to result.
+      ListOfShape aShapesToAdd;
+      for(std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
+        anIt != aCompSolidsObjects.end(); anIt++) {
+        std::shared_ptr<GeomAPI_Shape> aCompSolid = anIt->first;
+        ListOfShape& aUsedInOperationSolids = anIt->second;
+        anOriginalShapes.push_back(aCompSolid);
+        aShapesToSmash.insert(aShapesToSmash.end(), aUsedInOperationSolids.begin(), aUsedInOperationSolids.end());
+
+        // Collect solids from compsolid which will not be modified in boolean operation.
+        for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
+          std::shared_ptr<GeomAPI_Shape> aSolidInCompSolid = anExp.current();
+          ListOfShape::iterator anIt = aUsedInOperationSolids.begin();
+          for(; anIt != aUsedInOperationSolids.end(); anIt++) {
+            if(aSolidInCompSolid->isEqual(*anIt)) {
+              break;
+            }
+          }
+          if(anIt == aUsedInOperationSolids.end()) {
+            aShapesToAdd.push_back(aSolidInCompSolid);
+          }
+        }
+      }
+
+      GeomAlgoAPI_MakeShapeList aMakeShapeList;
+      GeomAPI_DataMapOfShapeShape aMapOfShapes;
+      if(!aShapesToAdd.empty()) {
+        // Cut objects with not used solids.
+        std::shared_ptr<GeomAlgoAPI_Boolean> anObjectsCutAlgo(new GeomAlgoAPI_Boolean(aShapesToSmash,
+                                                                                      aShapesToAdd,
+                                                                                      GeomAlgoAPI_Boolean::BOOL_CUT));
+
+        if(GeomAlgoAPI_ShapeTools::volume(anObjectsCutAlgo->shape()) > 1.e-7) {
+          aShapesToSmash.clear();
+          aShapesToSmash.push_back(anObjectsCutAlgo->shape());
+          aMakeShapeList.appendAlgo(anObjectsCutAlgo);
+          aMapOfShapes.merge(anObjectsCutAlgo->mapOfSubShapes());
+        }
+
+        // Cut tools with not used solids.
+        std::shared_ptr<GeomAlgoAPI_Boolean> aToolsCutAlgo(new GeomAlgoAPI_Boolean(aTools,
+                                                                                   aShapesToAdd,
+                                                                                   GeomAlgoAPI_Boolean::BOOL_CUT));
+
+        if(GeomAlgoAPI_ShapeTools::volume(aToolsCutAlgo->shape()) > 1.e-7) {
+          aTools.clear();
+          aTools.push_back(aToolsCutAlgo->shape());
+          aMakeShapeList.appendAlgo(aToolsCutAlgo);
+          aMapOfShapes.merge(aToolsCutAlgo->mapOfSubShapes());
+        }
+      }
+
+      // Cut objects with tools.
+      std::shared_ptr<GeomAlgoAPI_Boolean> aBoolAlgo(new GeomAlgoAPI_Boolean(aShapesToSmash,
+                                                                             aTools,
+                                                                             GeomAlgoAPI_Boolean::BOOL_CUT));
+
+      // Checking that the algorithm worked properly.
+      if(!aBoolAlgo->isDone()) {
+        static const std::string aFeatureError = "Error: Boolean algorithm failed.";
+        setError(aFeatureError);
+        return;
+      }
+      if(aBoolAlgo->shape()->isNull()) {
+        static const std::string aShapeError = "Error: Resulting shape is Null.";
+        setError(aShapeError);
+        return;
+      }
+      if(!aBoolAlgo->isValid()) {
+        std::string aFeatureError = "Error: Resulting shape is not valid.";
+        setError(aFeatureError);
+        return;
+      }
+      aMakeShapeList.appendAlgo(aBoolAlgo);
+      aMapOfShapes.merge(aBoolAlgo->mapOfSubShapes());
+
+      // Put all (cut result, tools and not used solids) to PaveFiller.
+      aShapesToAdd.push_back(aBoolAlgo->shape());
+      aShapesToAdd.insert(aShapesToAdd.end(), aTools.begin(), aTools.end());
+
+      std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
+      if(!aFillerAlgo->isDone()) {
+        std::string aFeatureError = "Error: PaveFiller algorithm failed.";
+        setError(aFeatureError);
+        return;
+      }
+      if(aFillerAlgo->shape()->isNull()) {
+        static const std::string aShapeError = "Error: Resulting shape is Null.";
+        setError(aShapeError);
+        return;
+      }
+      if(!aFillerAlgo->isValid()) {
+        std::string aFeatureError = "Error: Resulting shape is not valid.";
+        setError(aFeatureError);
+        return;
+      }
+
+      std::shared_ptr<GeomAPI_Shape> aShape = aFillerAlgo->shape();
+      aMakeShapeList.appendAlgo(aFillerAlgo);
+      aMapOfShapes.merge(aFillerAlgo->mapOfSubShapes());
+
+      std::shared_ptr<GeomAPI_Shape> aFrontShape = anOriginalShapes.front();
+      anOriginalShapes.pop_front();
+      std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
+      loadNamingDS(aResultBody, aFrontShape, anOriginalShapes, aShape, aMakeShapeList, aMapOfShapes);
+      setResult(aResultBody, aResultIndex);
+      aResultIndex++;
+
+      break;
+    }
     default: {
       std::string anOperationError = "Error: Wrong type of operation";
       setError(anOperationError);
index 0a072e8229cf9a0f1a2a7849129286e05f0df949..fd25cff88e439979e86532865f70d4be7c321e08 100644 (file)
@@ -8,79 +8,67 @@
 #define FeaturesPlugin_Cut_H_
 
 #include "FeaturesPlugin.h"
-#include <ModelAPI_Feature.h>
 
-#include <GeomAlgoAPI_Boolean.h>
+#include <GeomAlgoAPI_MakeShape.h>
 
-class GeomAlgoAPI_MakeShapeList;
+#include <ModelAPI_Feature.h>
 
-/**\class FeaturesPlugin_Boolean
- * \ingroup Plugins
- * \brief Feature for applying of Boolean operations on Solids.
- *
- * Supports three kinds of Boolean operations: Cut, Fuse and Common.
- * For all of them requires two Solids: object and tool.
- */
+/// \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
 {
 public:
-  /// Extrusion kind
+  enum OperationType {
+    BOOL_CUT,
+    BOOL_FUSE,
+    BOOL_COMMON,
+    BOOL_SMASH
+  };
+
+  /// Feature kind.
   inline static const std::string& ID()
   {
     static const std::string MY_ID("Boolean");
     return MY_ID;
   }
-  /// attribute name of referenced object
+
+  /// Attribute name of main objects.
   inline static const std::string& OBJECT_LIST_ID()
   {
     static const std::string MY_OBJECT_LIST_ID("main_objects");
     return MY_OBJECT_LIST_ID;
   }
-  /// attribute name of referenced object
-  inline static const std::string& OBJECT_ID()
-  {
-    static const std::string MY_OBJECT_ID("main_object");
-    return MY_OBJECT_ID;
-  }
-  /// attribute name of tool object
-  inline static const std::string& TOOL_ID()
-  {
-    static const std::string MY_TOOL_ID("tool_object");
-    return MY_TOOL_ID;
-  }
-  /// attribute name of tool object
+
+  /// Attribute name of tool objects.
   inline static const std::string& TOOL_LIST_ID()
   {
     static const std::string MY_TOOL_LIST_ID("tool_objects");
     return MY_TOOL_LIST_ID;
   }
-  /// attribute name of operation type
+
+  /// Attribute name of operation type.
   inline static const std::string& TYPE_ID()
   {
     static const std::string MY_TYPE_ID("bool_type");
     return MY_TYPE_ID;
   }
 
-  enum {
-    BOOL_CUT,
-    BOOL_FUSE,
-    BOOL_COMMON
-  };
-
-  /// Returns the kind of a feature
+  /// \return the kind of a feature.
   FEATURESPLUGIN_EXPORT virtual const std::string& getKind()
   {
     static std::string MY_KIND = FeaturesPlugin_Boolean::ID();
     return MY_KIND;
   }
 
-  /// Creates a new part document if needed
+  /// Creates a new part document if needed.
   FEATURESPLUGIN_EXPORT virtual void execute();
 
-  /// Request for initialization of data model of the feature: adding all attributes
+  /// Request for initialization of data model of the feature: adding all attributes.
   FEATURESPLUGIN_EXPORT virtual void initAttributes();
 
-  /// Use plugin manager for features creation
+  /// Use plugin manager for features creation.
   FeaturesPlugin_Boolean();
 
 private:
diff --git a/src/FeaturesPlugin/Test/TestBooleanSmash.py b/src/FeaturesPlugin/Test/TestBooleanSmash.py
new file mode 100644 (file)
index 0000000..8591f52
--- /dev/null
@@ -0,0 +1,104 @@
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+from ModelAPI import *
+from GeomDataAPI import *
+from GeomAlgoAPI import *
+from GeomAPI import *
+
+aSession = ModelAPI_Session.get()
+# Create a part for extrusions & boolean
+aSession.startOperation()
+aPartFeature = aSession.moduleDocument().addFeature("Part")
+aSession.finishOperation()
+aPart = aSession.activeDocument()
+#=========================================================================
+# Create a sketch with circle to extrude
+#=========================================================================
+aSession.startOperation()
+aCircleSketchFeature = featureToCompositeFeature(aPart.addFeature("Sketch"))
+origin = geomDataAPI_Point(aCircleSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aCircleSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aCircleSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSketchCircle = aCircleSketchFeature.addFeature("SketchCircle")
+anCircleCentr = geomDataAPI_Point2D(aSketchCircle.attribute("CircleCenter"))
+aCircleRadius = aSketchCircle.real("CircleRadius")
+anCircleCentr.setValue(10., 10.)
+aCircleRadius.setValue(50.)
+aSession.finishOperation()
+#=========================================================================
+# Create a sketch with triangle to extrude
+#=========================================================================
+aSession.startOperation()
+aTriangleSketchFeature = featureToCompositeFeature(aPart.addFeature("Sketch"))
+origin = geomDataAPI_Point(aTriangleSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aTriangleSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aTriangleSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSketchLineA = aTriangleSketchFeature.addFeature("SketchLine")
+aSketchLineB = aTriangleSketchFeature.addFeature("SketchLine")
+aSketchLineC = aTriangleSketchFeature.addFeature("SketchLine")
+aLineAStartPoint = geomDataAPI_Point2D(aSketchLineA.attribute("StartPoint"))
+aLineAEndPoint = geomDataAPI_Point2D(aSketchLineA.attribute("EndPoint"))
+aLineBStartPoint = geomDataAPI_Point2D(aSketchLineB.attribute("StartPoint"))
+aLineBEndPoint = geomDataAPI_Point2D(aSketchLineB.attribute("EndPoint"))
+aLineCStartPoint = geomDataAPI_Point2D(aSketchLineC.attribute("StartPoint"))
+aLineCEndPoint = geomDataAPI_Point2D(aSketchLineC.attribute("EndPoint"))
+aLineAStartPoint.setValue(25., 25.)
+aLineAEndPoint.setValue(100., 25.)
+aLineBStartPoint.setValue(100., 25.)
+aLineBEndPoint.setValue(60., 75.)
+aLineCStartPoint.setValue(60., 75.)
+aLineCEndPoint.setValue(25., 25.)
+aSession.finishOperation()
+#=========================================================================
+# Make extrusion on circle (cylinder) and triangle (prism)
+#=========================================================================
+# Build shape from sketcher results
+aSession.startOperation()
+extrudedObjects = []
+for eachSketchFeature in [aCircleSketchFeature, aTriangleSketchFeature]:
+    # Build sketch faces
+    aSketchResult = eachSketchFeature.firstResult()
+    aSketchEdges = modelAPI_ResultConstruction(aSketchResult).shape()
+    origin = geomDataAPI_Point(eachSketchFeature.attribute("Origin")).pnt()
+    dirX = geomDataAPI_Dir(eachSketchFeature.attribute("DirX")).dir()
+    norm = geomDataAPI_Dir(eachSketchFeature.attribute("Norm")).dir()
+    aSketchFaces = ShapeList()
+    GeomAlgoAPI_SketchBuilder.createFaces(
+        origin, dirX, norm, aSketchEdges, aSketchFaces)
+    # Create extrusion on them
+    anExtrusionFt = aPart.addFeature("Extrusion")
+    anExtrusionFt.selectionList("base").append(
+        aSketchResult, aSketchFaces[0])
+    anExtrusionFt.string("CreationMethod").setValue("BySizes")
+    anExtrusionFt.real("from_size").setValue(0)
+    anExtrusionFt.real("to_size").setValue(50)
+    anExtrusionFt.real("to_offset").setValue(0) #TODO: remove
+    anExtrusionFt.real("from_offset").setValue(0) #TODO: remove
+    anExtrusionFt.execute()
+    extrudedObjects.append(modelAPI_ResultBody(anExtrusionFt.firstResult()))
+aSession.finishOperation()
+#=========================================================================
+# Smash prism into the cylinder
+#=========================================================================
+aSession.startOperation()
+aBooleanFt = aPart.addFeature("Boolean")
+aBooleanFt.selectionList("main_objects").append(extrudedObjects[0], extrudedObjects[0].shape())
+aBooleanFt.selectionList("tool_objects").append(extrudedObjects[1], extrudedObjects[1].shape())
+kBooleanTypeSmash = 3
+aBooleanFt.integer("bool_type").setValue(kBooleanTypeSmash)
+aBooleanFt.execute()
+aSession.finishOperation()
+
+assert (len(aBooleanFt.results()) > 0)
+aBooleanResult = modelAPI_ResultBody(aBooleanFt.firstResult())
+assert (aBooleanResult is not None)
+#=========================================================================
+# End of test
+#=========================================================================
index e3b9b2dbbad9b56ae193527f9f4edcb82f1d39b9..75be156a40885fce5e11cdd60af036ed86cc28a1 100644 (file)
@@ -6,9 +6,9 @@
     buttons_dir="horizontal"
     label="Operation type" 
     tooltip="Type of boolean operation"
-    string_list="Cut Fuse Common"
+    string_list="Cut Fuse Common Smash"
     use_in_title="true"
-    icons_list=":icons/bool_cut.png :icons/bool_fuse.png :icons/bool_common.png"
+    icons_list=":icons/bool_cut.png :icons/bool_fuse.png :icons/bool_common.png :icons/bool_smash.png"
     default="0"
   />
   <multi_selector id="main_objects"
index e5e7474c3bb864e69f794bd923baf9670a89bf80..0cdf589ee398c9c1adf1a441bc76ba130ff5cee4 100644 (file)
@@ -23,7 +23,7 @@ class QButtonGroup;
 *   <choice id="bool_type" 
 *     label="Type" 
 *     tooltip="Type of boolean operation"
-*     string_list="Cut Fuse Common"
+*     string_list="Cut Fuse Common Smash"
 *   />
 * \endcode
 * Aditionally can be used: 
index e725a6a82eb6ce193cd02b27d814ca2d7a4003be..cd7a245704901335e12d486384599f693f0f7955 100644 (file)
@@ -89,6 +89,7 @@
      <file>icons/bool_cut.png</file>
      <file>icons/bool_fuse.png</file>
      <file>icons/bool_common.png</file>
+     <file>icons/bool_smash.png</file>
      <file>icons/plane_view.png</file>
      <file>icons/collinear.png</file>
      <file>icons/middlepoint.png</file>
diff --git a/src/PartSet/icons/bool_smash.png b/src/PartSet/icons/bool_smash.png
new file mode 100644 (file)
index 0000000..bf82ac2
Binary files /dev/null and b/src/PartSet/icons/bool_smash.png differ