From 4e8c96ad04b88912d3a03676b87f4b0949842b18 Mon Sep 17 00:00:00 2001 From: dbv Date: Wed, 1 Nov 2017 14:53:13 +0300 Subject: [PATCH] Issue #2306: 1.1.2.3 Fill construction planes Now Boolean Fill allows to select construction planes as Tools. --- src/FeaturesPlugin/CMakeLists.txt | 1 + src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp | 114 ++++++++++++++---- src/FeaturesPlugin/FeaturesPlugin_Boolean.h | 3 +- .../FeaturesPlugin_Validators.cpp | 18 ++- .../Test/TestBooleanFillWithPlane.py | 69 +++++++++++ 5 files changed, 176 insertions(+), 29 deletions(-) create mode 100644 src/FeaturesPlugin/Test/TestBooleanFillWithPlane.py diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index 0ebd6b158..a828b1a94 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -156,6 +156,7 @@ ADD_UNIT_TESTS(TestExtrusion.py TestBooleanCompSolids.py TestBooleanSmash.py TestBooleanFill.py + TestBooleanFillWithPlane.py TestMultiBoolean.py TestSerialBoolean.py TestIntersection.py diff --git a/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp b/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp index b51d76b60..08af88445 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp @@ -32,10 +32,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -87,7 +89,7 @@ void FeaturesPlugin_Boolean::execute() return; OperationType aType = (FeaturesPlugin_Boolean::OperationType)aTypeAttr->value(); - ListOfShape anObjects, aTools, anEdgesAndFaces; + ListOfShape anObjects, aTools, anEdgesAndFaces, aPlanes; std::map, ListOfShape> aCompSolidsObjects; // Getting objects. @@ -129,11 +131,12 @@ void FeaturesPlugin_Boolean::execute() AttributeSelectionListPtr aToolsSelList = selectionList(FeaturesPlugin_Boolean::TOOL_LIST_ID()); for(int aToolsIndex = 0; aToolsIndex < aToolsSelList->size(); aToolsIndex++) { AttributeSelectionPtr aToolAttr = aToolsSelList->value(aToolsIndex); - std::shared_ptr aTool = aToolAttr->value(); + GeomShapePtr aTool = aToolAttr->value(); if(!aTool.get()) { - return; - } - if(aTool->shapeType() == GeomAPI_Shape::EDGE || + // It could be a construction plane. + ResultPtr aContext = aToolAttr->context(); + aPlanes.push_back(aToolAttr->context()->shape()); + } else if(aTool->shapeType() == GeomAPI_Shape::EDGE || aTool->shapeType() == GeomAPI_Shape::FACE) { anEdgesAndFaces.push_back(aTool); } else { @@ -147,7 +150,8 @@ void FeaturesPlugin_Boolean::execute() case BOOL_CUT: case BOOL_COMMON: case BOOL_FILL: { - if((anObjects.empty() && aCompSolidsObjects.empty()) || aTools.empty()) { + if((anObjects.empty() && aCompSolidsObjects.empty()) + || (aTools.empty() && aPlanes.empty())) { std::string aFeatureError = "Error: Not enough objects for boolean operation."; setError(aFeatureError); return; @@ -159,25 +163,46 @@ void FeaturesPlugin_Boolean::execute() std::shared_ptr anObject = *anObjectsIt; ListOfShape aListWithObject; aListWithObject.push_back(anObject); - GeomAlgoAPI_MakeShape aBoolAlgo; + GeomAlgoAPI_MakeShapeList aMakeShapeList; + std::shared_ptr aBoolAlgo; GeomShapePtr aResShape; switch(aType) { case BOOL_CUT: { - aBoolAlgo = - GeomAlgoAPI_Boolean(aListWithObject, aTools, GeomAlgoAPI_Boolean::BOOL_CUT); - aResShape = aBoolAlgo.shape(); + aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aListWithObject, + aTools, + GeomAlgoAPI_Boolean::BOOL_CUT)); + aResShape = aBoolAlgo->shape(); break; } case BOOL_COMMON: { - aBoolAlgo = - GeomAlgoAPI_Boolean(aListWithObject, aTools, GeomAlgoAPI_Boolean::BOOL_COMMON); - aResShape = aBoolAlgo.shape(); + aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aListWithObject, + aTools, + GeomAlgoAPI_Boolean::BOOL_COMMON)); + aResShape = aBoolAlgo->shape(); break; } case BOOL_FILL: { - aBoolAlgo = GeomAlgoAPI_Partition(aListWithObject, aTools); - aResShape = aBoolAlgo.shape(); + std::list > aBoundingPoints = + GeomAlgoAPI_ShapeTools::getBoundingBox(aListWithObject, 1.0); + + // Resize planes. + ListOfShape aToolsWithPlanes = aTools; + for(ListOfShape::const_iterator anIt = aPlanes.cbegin(); + anIt != aPlanes.cend(); + ++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); + } + + aBoolAlgo.reset(new GeomAlgoAPI_Partition(aListWithObject, aToolsWithPlanes)); + aResShape = aBoolAlgo->shape(); if(aResShape->shapeType() == GeomAPI_Shape::COMPOUND) { int aSubResultsNb = 0; GeomAPI_ShapeIterator anIt(aResShape); @@ -196,7 +221,7 @@ void FeaturesPlugin_Boolean::execute() } // Checking that the algorithm worked properly. - if(!aBoolAlgo.isDone()) { + if(!aBoolAlgo->isDone()) { static const std::string aFeatureError = "Error: Boolean algorithm failed."; setError(aFeatureError); return; @@ -206,17 +231,25 @@ void FeaturesPlugin_Boolean::execute() setError(aShapeError); return; } - if(!aBoolAlgo.isValid()) { + if(!aBoolAlgo->isValid()) { std::string aFeatureError = "Error: Resulting shape is not valid."; setError(aFeatureError); return; } + aMakeShapeList.appendAlgo(aBoolAlgo); + if(GeomAlgoAPI_ShapeTools::volume(aResShape) > 1.e-27) { std::shared_ptr aResultBody = document()->createBody(data(), aResultIndex); - loadNamingDS(aResultBody, anObject, aTools, aResShape, - aBoolAlgo, *aBoolAlgo.mapOfSubShapes().get()); + + ListOfShape aUsedTools = aTools; + if (aType == BOOL_FILL) { + aUsedTools.insert(aUsedTools.end(), aPlanes.begin(), aPlanes.end()); + } + + loadNamingDS(aResultBody, anObject, aUsedTools, aResShape, + aMakeShapeList, *(aBoolAlgo->mapOfSubShapes()), aType == BOOL_FILL); setResult(aResultBody, aResultIndex); aResultIndex++; } @@ -245,6 +278,7 @@ void FeaturesPlugin_Boolean::execute() } } + GeomAlgoAPI_MakeShapeList aMakeShapeList; std::shared_ptr aBoolAlgo; switch(aType) { @@ -261,7 +295,25 @@ void FeaturesPlugin_Boolean::execute() break; } case BOOL_FILL: { - aBoolAlgo.reset(new GeomAlgoAPI_Partition(aUsedInOperationSolids, aTools)); + std::list > aBoundingPoints = + GeomAlgoAPI_ShapeTools::getBoundingBox(aUsedInOperationSolids, 1.0); + + // Resize planes. + ListOfShape aToolsWithPlanes = aTools; + for(ListOfShape::const_iterator anIt = aPlanes.cbegin(); + anIt != aPlanes.cend(); + ++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); + } + + aBoolAlgo.reset(new GeomAlgoAPI_Partition(aUsedInOperationSolids, aToolsWithPlanes)); break; } } @@ -283,7 +335,6 @@ void FeaturesPlugin_Boolean::execute() return; } - GeomAlgoAPI_MakeShapeList aMakeShapeList; aMakeShapeList.appendAlgo(aBoolAlgo); GeomAPI_DataMapOfShapeShape aMapOfShapes; aMapOfShapes.merge(aBoolAlgo->mapOfSubShapes()); @@ -309,7 +360,19 @@ void FeaturesPlugin_Boolean::execute() if(GeomAlgoAPI_ShapeTools::volume(aResultShape) > 1.e-27) { std::shared_ptr aResultBody = document()->createBody(data(), aResultIndex); - loadNamingDS(aResultBody, aCompSolid, aTools, aResultShape, aMakeShapeList, aMapOfShapes); + + ListOfShape aUsedTools = aTools; + if (aType == BOOL_FILL) { + aUsedTools.insert(aUsedTools.end(), aPlanes.begin(), aPlanes.end()); + } + + loadNamingDS(aResultBody, + aCompSolid, + aUsedTools, + aResultShape, + aMakeShapeList, + aMapOfShapes, + aType == BOOL_FILL); setResult(aResultBody, aResultIndex); aResultIndex++; } @@ -626,7 +689,8 @@ void FeaturesPlugin_Boolean::loadNamingDS(std::shared_ptr t const ListOfShape& theTools, const std::shared_ptr theResultShape, GeomAlgoAPI_MakeShape& theMakeShape, - GeomAPI_DataMapOfShapeShape& theMapOfShapes) + GeomAPI_DataMapOfShapeShape& theMapOfShapes, + const bool theIsStoreAsGenerated) { //load result if(theBaseShape->isEqual(theResultShape)) { @@ -645,7 +709,7 @@ void FeaturesPlugin_Boolean::loadNamingDS(std::shared_ptr t const std::string aModFName = "Modified_Face"; theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE, - aModifyTag, aModName, theMapOfShapes, false, false, true); + aModifyTag, aModName, theMapOfShapes, false, theIsStoreAsGenerated, true); theResultBody->loadDeletedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE, aDeletedTag); @@ -666,7 +730,7 @@ void FeaturesPlugin_Boolean::loadNamingDS(std::shared_ptr t } theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, aName == aModEName ? GeomAPI_Shape::EDGE : GeomAPI_Shape::FACE, - aTag, aName, theMapOfShapes, false, false, true); + aTag, aName, theMapOfShapes, false, theIsStoreAsGenerated, true); theResultBody->loadDeletedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE, aDeletedTag); } } diff --git a/src/FeaturesPlugin/FeaturesPlugin_Boolean.h b/src/FeaturesPlugin/FeaturesPlugin_Boolean.h index 726b33846..d49820686 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Boolean.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Boolean.h @@ -95,7 +95,8 @@ private: const ListOfShape& theTools, const std::shared_ptr theResultShape, GeomAlgoAPI_MakeShape& theMakeShape, - GeomAPI_DataMapOfShapeShape& theMapOfShapes); + GeomAPI_DataMapOfShapeShape& theMapOfShapes, + const bool theIsStoreAsGenerated = false); }; #endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp index acc2b8bec..fa2ab0695 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp @@ -20,6 +20,7 @@ #include "FeaturesPlugin_Validators.h" +#include "FeaturesPlugin_Boolean.h" #include "FeaturesPlugin_Union.h" #include @@ -608,8 +609,11 @@ bool FeaturesPlugin_ValidatorBooleanSelection::isValid(const AttributePtr& theAt ResultConstructionPtr aResultConstruction = std::dynamic_pointer_cast(aContext); if(aResultConstruction.get()) { - theError = "Error: Result construction not allowed for selection."; - return false; + if (anOperationType != FeaturesPlugin_Boolean::BOOL_FILL + || theAttribute->id() != FeaturesPlugin_Boolean::TOOL_LIST_ID()) { + theError = "Error: Result construction not allowed for selection."; + return false; + } } std::shared_ptr aShape = anAttrSelection->value(); GeomShapePtr aContextShape = aContext->shape(); @@ -626,7 +630,7 @@ bool FeaturesPlugin_ValidatorBooleanSelection::isValid(const AttributePtr& theAt } int aShapeType = aShape->shapeType(); - if(anOperationType == 1) { + if(anOperationType == FeaturesPlugin_Boolean::BOOL_FUSE) { // Fuse operation. Allow to select edges, faces and solids. if(aShapeType != GeomAPI_Shape::EDGE && aShapeType != GeomAPI_Shape::FACE && @@ -636,6 +640,14 @@ bool FeaturesPlugin_ValidatorBooleanSelection::isValid(const AttributePtr& theAt theError = "Error: Selected shape has the wrong type."; return false; } + } else if (anOperationType == FeaturesPlugin_Boolean::BOOL_FILL) { + if(aShapeType != GeomAPI_Shape::FACE && + aShapeType != GeomAPI_Shape::SOLID && + aShapeType != GeomAPI_Shape::COMPSOLID && + aShapeType != GeomAPI_Shape::COMPOUND) { + theError = "Error: Selected shape has the wrong type."; + return false; + } } else { if(aShapeType != GeomAPI_Shape::SOLID && aShapeType != GeomAPI_Shape::COMPSOLID && diff --git a/src/FeaturesPlugin/Test/TestBooleanFillWithPlane.py b/src/FeaturesPlugin/Test/TestBooleanFillWithPlane.py new file mode 100644 index 000000000..923b64d90 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestBooleanFillWithPlane.py @@ -0,0 +1,69 @@ +## Copyright (C) 2014-2017 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 +## + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(50.45430931454487, 141.3050678403047, -211.8625790259634, 141.3050678403047) +SketchLine_2 = Sketch_1.addLine(-211.8625790259634, 141.3050678403047, -211.8625790259634, -163.3288837233227) +SketchLine_3 = Sketch_1.addLine(-211.8625790259634, -163.3288837233227, 50.45430931454487, -163.3288837233227) +SketchLine_4 = Sketch_1.addLine(50.45430931454487, -163.3288837233227, 50.45430931454487, 141.3050678403047) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result()) +SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result()) +SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result()) +SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result()) +SketchLine_5 = Sketch_1.addLine(246.5736143115653, 48.78197815871301, -51.09047583637312, 48.78197815871301) +SketchLine_6 = Sketch_1.addLine(-51.09047583637312, 48.78197815871301, -51.09047583637312, -104.8260668928192) +SketchLine_7 = Sketch_1.addLine(-51.09047583637312, -104.8260668928192, 246.5736143115653, -104.8260668928192) +SketchLine_8 = Sketch_1.addLine(246.5736143115653, -104.8260668928192, 246.5736143115653, 48.78197815871301) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_5.startPoint()) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint()) +SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint()) +SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_5.result()) +SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_6.result()) +SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_7.result()) +SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_8.result()) +model.do() +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 100, 0) +Boolean_1 = model.addFill(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1_2")], [model.selection("FACE", "PartSet/YOZ")]) +Group_1 = model.addGroup(Part_1_doc, [model.selection("FACE", "Boolean_1_1_4/Modified_3"), model.selection("FACE", "Boolean_1_1_4/Modified_Face_1"), model.selection("FACE", "Extrusion_1_1/Generated_Face_9"), model.selection("FACE", "Boolean_1_1_4/Modified_2"), model.selection("FACE", "Boolean_1_1_4/Modified_4"), model.selection("FACE", "Boolean_1_1_4/Modified_1"), model.selection("FACE", "Boolean_1_1_4/Modified_3"), model.selection("FACE", "Extrusion_1_1/Generated_Face_6"), model.selection("FACE", "Boolean_1_1_3/Modified_3"), model.selection("FACE", "Extrusion_1_1/Generated_Face_7"), model.selection("FACE", "Boolean_1_1_1/Modified_2"), model.selection("FACE", "Extrusion_1_1/Generated_Face_5"), model.selection("FACE", "Boolean_1_1_4/Modified_4"), model.selection("FACE", "Boolean_1_1_3/Modified_4"), model.selection("FACE", "Extrusion_1_1/Generated_Face_9"), model.selection("FACE", "Boolean_1_1_1/Modified_1"), model.selection("FACE", "Extrusion_1_1/Generated_Face_12"), model.selection("FACE", "Extrusion_1_1/Generated_Face_11"), model.selection("FACE", "Boolean_1_1_3/Modified_3"), model.selection("FACE", "Boolean_1_1_3/Modified_2"), model.selection("FACE", "Boolean_1_1_3/Modified_1"), model.selection("FACE", "Extrusion_1_1/Generated_Face_4"), model.selection("FACE", "Boolean_1_1_3/Modified_4"), model.selection("FACE", "Boolean_1_1_4/Modified_Face_1"), model.selection("FACE", "Extrusion_1_1/Generated_Face_4"), model.selection("FACE", "Extrusion_1_1/To_Face_3"), model.selection("FACE", "Extrusion_1_1/Generated_Face_3"), model.selection("FACE", "Extrusion_1_1/Generated_Face_2"), model.selection("FACE", "Extrusion_1_1/From_Face_3"), model.selection("FACE", "Extrusion_1_1/Generated_Face_1")]) +model.do() +model.end() + +from GeomAPI import GeomAPI_Shape + +model.testNbResults(Boolean_1, 1) +model.testNbSubResults(Boolean_1, [4]) +model.testNbSubShapes(Boolean_1, GeomAPI_Shape.SOLID, [4]) +model.testNbSubShapes(Boolean_1, GeomAPI_Shape.FACE, [30]) +model.testNbSubShapes(Boolean_1, GeomAPI_Shape.EDGE, [132]) +model.testNbSubShapes(Boolean_1, GeomAPI_Shape.VERTEX, [264]) +model.testResultsVolumes(Boolean_1, [11003613.329450136050581932067871094]) + +assert(model.checkPythonDump()) -- 2.39.2