From 69ff7918e98c21a8a54a2345079661c294af2fc8 Mon Sep 17 00:00:00 2001 From: dbv Date: Wed, 9 Mar 2016 15:06:25 +0300 Subject: [PATCH] Boolean Smash feature Test for Smash operation --- src/FeaturesPlugin/CMakeLists.txt | 1 + src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp | 144 +++++++++++++++++- src/FeaturesPlugin/FeaturesPlugin_Boolean.h | 60 +++----- src/FeaturesPlugin/Test/TestBooleanSmash.py | 104 +++++++++++++ src/FeaturesPlugin/boolean_widget.xml | 4 +- src/ModuleBase/ModuleBase_WidgetChoice.h | 2 +- src/PartSet/PartSet_icons.qrc | 1 + src/PartSet/icons/bool_smash.png | Bin 0 -> 263 bytes 8 files changed, 270 insertions(+), 46 deletions(-) create mode 100644 src/FeaturesPlugin/Test/TestBooleanSmash.py create mode 100644 src/PartSet/icons/bool_smash.png diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index 8dea5b736..bb7ff7c59 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -107,6 +107,7 @@ ADD_UNIT_TESTS(TestExtrusion.py TestRotation.py TestBoolean.py TestBooleanCompSolids.py + TestBooleanSmash.py TestMultiBoolean.py TestSerialBoolean.py TestGroup.py diff --git a/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp b/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp index 7360f443c..701017b52 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp @@ -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, 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 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 aBoolAlgo(new GeomAlgoAPI_Boolean(aUsedInOperationSolids, aTools, aType)); + std::shared_ptr 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 aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects, aTools, aType)); + std::shared_ptr 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, ListOfShape>::iterator anIt = aCompSolidsObjects.begin(); + anIt != aCompSolidsObjects.end(); anIt++) { + std::shared_ptr 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 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 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 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 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 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 aShape = aFillerAlgo->shape(); + aMakeShapeList.appendAlgo(aFillerAlgo); + aMapOfShapes.merge(aFillerAlgo->mapOfSubShapes()); + + std::shared_ptr aFrontShape = anOriginalShapes.front(); + anOriginalShapes.pop_front(); + std::shared_ptr 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); diff --git a/src/FeaturesPlugin/FeaturesPlugin_Boolean.h b/src/FeaturesPlugin/FeaturesPlugin_Boolean.h index 0a072e822..fd25cff88 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Boolean.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Boolean.h @@ -8,79 +8,67 @@ #define FeaturesPlugin_Cut_H_ #include "FeaturesPlugin.h" -#include -#include +#include -class GeomAlgoAPI_MakeShapeList; +#include -/**\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 index 000000000..8591f52d0 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestBooleanSmash.py @@ -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 +#========================================================================= diff --git a/src/FeaturesPlugin/boolean_widget.xml b/src/FeaturesPlugin/boolean_widget.xml index e3b9b2dbb..75be156a4 100644 --- a/src/FeaturesPlugin/boolean_widget.xml +++ b/src/FeaturesPlugin/boolean_widget.xml @@ -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" /> * \endcode * Aditionally can be used: diff --git a/src/PartSet/PartSet_icons.qrc b/src/PartSet/PartSet_icons.qrc index e725a6a82..cd7a24570 100644 --- a/src/PartSet/PartSet_icons.qrc +++ b/src/PartSet/PartSet_icons.qrc @@ -89,6 +89,7 @@ icons/bool_cut.png icons/bool_fuse.png icons/bool_common.png + icons/bool_smash.png icons/plane_view.png icons/collinear.png icons/middlepoint.png diff --git a/src/PartSet/icons/bool_smash.png b/src/PartSet/icons/bool_smash.png new file mode 100644 index 0000000000000000000000000000000000000000..bf82ac2721784eeac71e0a10de1f69de36252a50 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}c0*}aI z1_r)EAj~ML;ne^XlqhkHC<)F_D=AMbN@XZW%*-p%%S$a$Fwry6v&=EB^8>1B^>lFz ziEw{=^&n@1fq=`!kVIFp1nF%F;$;(r-W+PuNw|98nZo8Bij2x<9yTuU`{&u*vEyv^ zo#x#P@93(@RYS`9KFTc)I$ztaD0e0sy4G BSs(xa literal 0 HcmV?d00001 -- 2.30.2