From bf9b7eb42772aae24906f7cc2d926ff15cf6604a Mon Sep 17 00:00:00 2001 From: Alexey Kondratyev Date: Fri, 8 Oct 2021 16:54:33 +0300 Subject: [PATCH] bos #26448 Construction elements: create extra planes Add attribute "number of copies" for creating plane by rotating and by distance from other plane. Added a corresponding field in the creation window. Change documentation to reference about copies when creating a planes. Change "dump to script" to set number of copies(or without it). Add Python test. Add translate to French. --- src/ConstructionAPI/ConstructionAPI_Plane.cpp | 34 ++++-- src/ConstructionAPI/ConstructionAPI_Plane.h | 25 +++-- .../ConstructionPlugin_Plane.cpp | 104 ++++++++++++------ .../ConstructionPlugin_Plane.h | 10 +- .../ConstructionPlugin_msg_en.ts | 7 ++ .../ConstructionPlugin_msg_fr.ts | 16 ++- .../Test/TestPlane_Copies.py | 65 +++++++++++ src/ConstructionPlugin/doc/images/Plane3.png | Bin 14655 -> 14100 bytes src/ConstructionPlugin/doc/planeFeature.rst | 7 +- src/ConstructionPlugin/plane_widget.xml | 12 ++ src/ConstructionPlugin/tests.set | 1 + src/Model/Model_Objects.cpp | 10 +- src/Model/Model_Update.cpp | 3 +- 13 files changed, 228 insertions(+), 66 deletions(-) create mode 100644 src/ConstructionPlugin/Test/TestPlane_Copies.py diff --git a/src/ConstructionAPI/ConstructionAPI_Plane.cpp b/src/ConstructionAPI/ConstructionAPI_Plane.cpp index cd0bbb424..3933b1623 100644 --- a/src/ConstructionAPI/ConstructionAPI_Plane.cpp +++ b/src/ConstructionAPI/ConstructionAPI_Plane.cpp @@ -33,11 +33,12 @@ ConstructionAPI_Plane::ConstructionAPI_Plane(const std::shared_ptr& theFeature, const ModelHighAPI_Selection& theFace, const ModelHighAPI_Double& theDistance, - const bool theIsReverse) + const bool theIsReverse, + const ModelHighAPI_Integer& theNbCopy) : ModelHighAPI_Interface(theFeature) { if(initialize()) { - setByFaceAndDistance(theFace, theDistance, theIsReverse); + setByFaceAndDistance(theFace, theDistance, theIsReverse, theNbCopy); } } @@ -99,11 +100,12 @@ ConstructionAPI_Plane::ConstructionAPI_Plane(const std::shared_ptr& theFeature, const ModelHighAPI_Selection& thePlane, const ModelHighAPI_Selection& theAxis, - const ModelHighAPI_Double& theAngle) + const ModelHighAPI_Double& theAngle, + const ModelHighAPI_Integer& theNbCopy) : ModelHighAPI_Interface(theFeature) { if(initialize()) { - setByRotation(thePlane, theAxis, theAngle); + setByRotation(thePlane, theAxis, theAngle, theNbCopy); } } @@ -115,7 +117,8 @@ ConstructionAPI_Plane::~ConstructionAPI_Plane() //================================================================================================== void ConstructionAPI_Plane::setByFaceAndDistance(const ModelHighAPI_Selection& theFace, const ModelHighAPI_Double& theDistance, - const bool theIsReverse) + const bool theIsReverse, + const ModelHighAPI_Integer& theNbCopy) { fillAttribute(ConstructionPlugin_Plane::CREATION_METHOD_BY_OTHER_PLANE(), mycreationMethod); fillAttribute(theFace, myplane); @@ -123,6 +126,7 @@ void ConstructionAPI_Plane::setByFaceAndDistance(const ModelHighAPI_Selection& t mycreationMethodByOtherPlane); fillAttribute(theDistance, mydistance); fillAttribute(theIsReverse, myreverse); + fillAttribute(theNbCopy, mynbcopy); execute(); } @@ -196,7 +200,8 @@ void ConstructionAPI_Plane::setByCoincidentToPoint(const ModelHighAPI_Selection& //================================================================================================== void ConstructionAPI_Plane::setByRotation(const ModelHighAPI_Selection& thePlane, const ModelHighAPI_Selection& theAxis, - const ModelHighAPI_Double& theAngle) + const ModelHighAPI_Double& theAngle, + const ModelHighAPI_Integer& theNbCopy) { fillAttribute(ConstructionPlugin_Plane::CREATION_METHOD_BY_OTHER_PLANE(), mycreationMethod); fillAttribute(thePlane, myplane); @@ -204,6 +209,7 @@ void ConstructionAPI_Plane::setByRotation(const ModelHighAPI_Selection& thePlane mycreationMethodByOtherPlane); fillAttribute(theAxis, myaxis); fillAttribute(theAngle, myangle); + fillAttribute(theNbCopy, mynbcopy); execute(); } @@ -248,8 +254,11 @@ void ConstructionAPI_Plane::dump(ModelHighAPI_Dumper& theDumper) const ConstructionPlugin_Plane::CREATION_METHOD_BY_DISTANCE_FROM_OTHER()) { AttributeDoublePtr anAttrDistance = aBase->real(ConstructionPlugin_Plane::DISTANCE()); AttributeBooleanPtr anAttrReverse = aBase->boolean(ConstructionPlugin_Plane::REVERSE()); + AttributeIntegerPtr anAttrNbCopy = aBase->integer(ConstructionPlugin_Plane::NB_COPIES()); theDumper << ", " << anAttrPlane << ", " << anAttrDistance << ", " << anAttrReverse; + if(anAttrNbCopy.get() && anAttrNbCopy->value() > 1) + theDumper << ", " << anAttrNbCopy; } else if(aCreationMethodOption == ConstructionPlugin_Plane::CREATION_METHOD_BY_COINCIDENT_TO_POINT()) { AttributeSelectionPtr anAttrPoint = @@ -259,8 +268,11 @@ void ConstructionAPI_Plane::dump(ModelHighAPI_Dumper& theDumper) const } else if(aCreationMethodOption == ConstructionPlugin_Plane::CREATION_METHOD_BY_ROTATION()) { AttributeSelectionPtr anAttrAxis = aBase->selection(ConstructionPlugin_Plane::AXIS()); AttributeDoublePtr anAttrAngle = aBase->real(ConstructionPlugin_Plane::ANGLE()); + AttributeIntegerPtr anAttrNbCopy = aBase->integer(ConstructionPlugin_Plane::NB_COPIES()); theDumper << ", " << anAttrPlane << ", " << anAttrAxis << ", " << anAttrAngle; + if (anAttrNbCopy.get() && anAttrNbCopy->value() > 1) + theDumper << ", " << anAttrNbCopy; } } else if(aCreationMethod == ConstructionPlugin_Plane::CREATION_METHOD_BY_TWO_PARALLEL_PLANES()) { @@ -277,11 +289,12 @@ void ConstructionAPI_Plane::dump(ModelHighAPI_Dumper& theDumper) const PlanePtr addPlane(const std::shared_ptr& thePart, const ModelHighAPI_Selection& theFace, const ModelHighAPI_Double& theDistance, - const bool theIsReverse) + const bool theIsReverse, + const ModelHighAPI_Integer& theNbCopies) { // TODO(spo): check that thePart is not empty std::shared_ptr aFeature = thePart->addFeature(ConstructionAPI_Plane::ID()); - return PlanePtr(new ConstructionAPI_Plane(aFeature, theFace, theDistance, theIsReverse)); + return PlanePtr(new ConstructionAPI_Plane(aFeature, theFace, theDistance, theIsReverse, theNbCopies)); } //================================================================================================== @@ -332,9 +345,10 @@ PlanePtr addPlane(const std::shared_ptr& thePart, PlanePtr addPlane(const std::shared_ptr& thePart, const ModelHighAPI_Selection& thePlane, const ModelHighAPI_Selection& theAxis, - const ModelHighAPI_Double& theAngle) + const ModelHighAPI_Double& theAngle, + const ModelHighAPI_Integer& theNbCopies) { // TODO(spo): check that thePart is not empty std::shared_ptr aFeature = thePart->addFeature(ConstructionAPI_Plane::ID()); - return PlanePtr(new ConstructionAPI_Plane(aFeature, thePlane, theAxis, theAngle)); + return PlanePtr(new ConstructionAPI_Plane(aFeature, thePlane, theAxis, theAngle, theNbCopies)); } diff --git a/src/ConstructionAPI/ConstructionAPI_Plane.h b/src/ConstructionAPI/ConstructionAPI_Plane.h index b8b28d3f4..b99940b09 100644 --- a/src/ConstructionAPI/ConstructionAPI_Plane.h +++ b/src/ConstructionAPI/ConstructionAPI_Plane.h @@ -26,6 +26,7 @@ #include #include +#include class ModelHighAPI_Double; class ModelHighAPI_Selection; @@ -45,7 +46,8 @@ public: ConstructionAPI_Plane(const std::shared_ptr& theFeature, const ModelHighAPI_Selection& theFace, const ModelHighAPI_Double& theDistance, - const bool theIsReverse); + const bool theIsReverse, + const ModelHighAPI_Integer& theNbCopy = ModelHighAPI_Integer(1)); /// Constructor with values CONSTRUCTIONAPI_EXPORT @@ -80,13 +82,14 @@ public: ConstructionAPI_Plane(const std::shared_ptr& theFeature, const ModelHighAPI_Selection& thePlane, const ModelHighAPI_Selection& theAxis, - const ModelHighAPI_Double& theAngle); + const ModelHighAPI_Double& theAngle, + const ModelHighAPI_Integer& theNbCopy = ModelHighAPI_Integer(1)); /// Destructor CONSTRUCTIONAPI_EXPORT virtual ~ConstructionAPI_Plane(); - INTERFACE_20(ConstructionPlugin_Plane::ID(), + INTERFACE_21(ConstructionPlugin_Plane::ID(), creationMethod, ConstructionPlugin_Plane::CREATION_METHOD(), ModelAPI_AttributeString, /** Creation method */, A, ConstructionPlugin_Plane::A(), @@ -127,13 +130,16 @@ public: plane1, ConstructionPlugin_Plane::PLANE1(), ModelAPI_AttributeSelection, /** Plane 1 */, plane2, ConstructionPlugin_Plane::PLANE2(), - ModelAPI_AttributeSelection, /** Plane 2 */) + ModelAPI_AttributeSelection, /** Plane 2 */, + nbcopy, ConstructionPlugin_Plane::NB_COPIES(), + ModelAPI_AttributeInteger, /** Number of copies */) /// Set face and distance CONSTRUCTIONAPI_EXPORT void setByFaceAndDistance(const ModelHighAPI_Selection& theFace, const ModelHighAPI_Double& theDistance, - const bool theIsReverse); + const bool theIsReverse, + const ModelHighAPI_Integer& theNbCopy = ModelHighAPI_Integer(1)); /// Set GeneralEquation parameters of the feature CONSTRUCTIONAPI_EXPORT @@ -168,7 +174,8 @@ public: CONSTRUCTIONAPI_EXPORT void setByRotation(const ModelHighAPI_Selection& thePlane, const ModelHighAPI_Selection& theAxis, - const ModelHighAPI_Double& theAngle); + const ModelHighAPI_Double& theAngle, + const ModelHighAPI_Integer& theNbCopy = ModelHighAPI_Integer(1)); /// Dump wrapped feature CONSTRUCTIONAPI_EXPORT @@ -184,7 +191,8 @@ CONSTRUCTIONAPI_EXPORT PlanePtr addPlane(const std::shared_ptr& thePart, const ModelHighAPI_Selection& theFace, const ModelHighAPI_Double& theDistance, - const bool theIsReverse); + const bool theIsReverse, + const ModelHighAPI_Integer& theNbCopy = ModelHighAPI_Integer(1)); /// \ingroup CPPHighAPI /// \brief Create Plane feature @@ -224,6 +232,7 @@ CONSTRUCTIONAPI_EXPORT PlanePtr addPlane(const std::shared_ptr& thePart, const ModelHighAPI_Selection& thePlane, const ModelHighAPI_Selection& theAxis, - const ModelHighAPI_Double& theAngle); + const ModelHighAPI_Double& theAngle, + const ModelHighAPI_Integer& theNbCopy = ModelHighAPI_Integer(1)); #endif /* SRC_CONSTRUCTIONAPI_CONSTRUCTIONAPI_PLANE_H_ */ diff --git a/src/ConstructionPlugin/ConstructionPlugin_Plane.cpp b/src/ConstructionPlugin/ConstructionPlugin_Plane.cpp index c4c384ba5..6a6d7fc6b 100644 --- a/src/ConstructionPlugin/ConstructionPlugin_Plane.cpp +++ b/src/ConstructionPlugin/ConstructionPlugin_Plane.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -99,46 +100,63 @@ void ConstructionPlugin_Plane::initAttributes() // By two parallel planes. data()->addAttribute(PLANE1(), ModelAPI_AttributeSelection::typeId()); data()->addAttribute(PLANE2(), ModelAPI_AttributeSelection::typeId()); + + // By other plane. + AttributeIntegerPtr aNbCopies = std::dynamic_pointer_cast( + data()->addAttribute(NB_COPIES(), ModelAPI_AttributeInteger::typeId())); + + if (!aNbCopies->isInitialized()) + aNbCopies->setValue(1); } //================================================================================================== void ConstructionPlugin_Plane::execute() { - GeomShapePtr aShape; + ListOfShape aShapes; std::string aCreationMethod = string(CREATION_METHOD())->value(); if(aCreationMethod == CREATION_METHOD_BY_GENERAL_EQUATION() || aCreationMethod == "PlaneByGeneralEquation") { - aShape = createByGeneralEquation(); + aShapes.push_back(createByGeneralEquation()); } else if(aCreationMethod == CREATION_METHOD_BY_THREE_POINTS()) { - aShape = createByThreePoints(); + aShapes.push_back(createByThreePoints()); } else if(aCreationMethod == CREATION_METHOD_BY_LINE_AND_POINT()) { - aShape = createByLineAndPoint(); + aShapes.push_back(createByLineAndPoint()); } else if(aCreationMethod == CREATION_METHOD_BY_OTHER_PLANE()) { std::string aCreationMethodOption = string(CREATION_METHOD_BY_OTHER_PLANE_OPTION())->value(); if(aCreationMethodOption == CREATION_METHOD_BY_DISTANCE_FROM_OTHER()) { - aShape = createByDistanceFromOther(); + createByDistanceFromOther(aShapes); } else if(aCreationMethodOption == CREATION_METHOD_BY_COINCIDENT_TO_POINT()) { - aShape = createByCoincidentPoint(); + aShapes.push_back(createByCoincidentPoint()); } else if(aCreationMethodOption == CREATION_METHOD_BY_ROTATION()) { - aShape = createByRotation(); + createByRotation(aShapes); } } else if(aCreationMethod == CREATION_METHOD_BY_TWO_PARALLEL_PLANES()) { - aShape = createByTwoParallelPlanes(); + aShapes.push_back(createByTwoParallelPlanes()); } else { setError("Error: Plane creation method \"" + aCreationMethod + "\" not supported."); return; } - if(!aShape.get()) { + if(aShapes.size() == 0) { setError("Error: Could not create a plane."); return; } - ResultConstructionPtr aConstr = document()->createConstruction(data()); - aConstr->setInfinite(true); - aConstr->setShape(aShape); - setResult(aConstr); + int anIndex = 0; + for (auto aShapeIter = aShapes.begin(); aShapeIter != aShapes.end(); ++aShapeIter, ++anIndex) + { + if (!aShapeIter->get()) + { + setError("Error: Could not create a plane."); + continue; + } + ResultConstructionPtr aConstr = document()->createConstruction(data(), anIndex); + aConstr->setInfinite(true); + aConstr->setShape(*aShapeIter); + setResult(aConstr, anIndex); + } + removeResults(anIndex); } //================================================================================================== @@ -191,7 +209,6 @@ std::shared_ptr ConstructionPlugin_Plane::createByGeneralEquation } return aPlaneFace; } - //================================================================================================== std::shared_ptr ConstructionPlugin_Plane::createByThreePoints() { @@ -275,17 +292,18 @@ std::shared_ptr ConstructionPlugin_Plane::createByLineAndPoint() } //================================================================================================== -std::shared_ptr ConstructionPlugin_Plane::createByDistanceFromOther() +void ConstructionPlugin_Plane::createByDistanceFromOther(ListOfShape& theShapes) { AttributeSelectionPtr aFaceAttr = data()->selection(ConstructionPlugin_Plane::PLANE()); AttributeDoublePtr aDistAttr = data()->real(ConstructionPlugin_Plane::DISTANCE()); - std::shared_ptr aPlane; + AttributeIntegerPtr aNbCopyAttr = data()->integer(ConstructionPlugin_Plane::NB_COPIES()); if ((aFaceAttr.get() != NULL) && - (aDistAttr.get() != NULL) && + (aDistAttr.get() != NULL) && (aNbCopyAttr.get() != NULL) && aFaceAttr->isInitialized() && aDistAttr->isInitialized()) { double aDist = aDistAttr->value(); bool anIsReverse = boolean(REVERSE())->value(); + int aNumOfCopies = aNbCopyAttr->value(); if(anIsReverse) aDist = -aDist; GeomShapePtr aShape = aFaceAttr->value(); if (!aShape.get() && aFaceAttr->context()) { @@ -293,7 +311,7 @@ std::shared_ptr ConstructionPlugin_Plane::createByDistanceFromOth } if(!aShape.get()) { - return aPlane; + return; } std::shared_ptr aFace; @@ -305,18 +323,22 @@ std::shared_ptr ConstructionPlugin_Plane::createByDistanceFromOth aFace = anIt.current()->face(); } if (!aFace) - return GeomShapePtr(); + return; std::shared_ptr aPln = aFace->getPlane(); std::shared_ptr aOrig = aPln->location(); std::shared_ptr aDir = aPln->direction(); - aOrig->translate(aDir, aDist); - std::shared_ptr aNewPln(new GeomAPI_Pln(aOrig, aDir)); + for (int aNbCopy = 0; aNbCopy < aNumOfCopies; ++aNbCopy) + { + std::shared_ptr aPlane; + aOrig->translate(aDir, aDist); + std::shared_ptr aNewPln(new GeomAPI_Pln(aOrig, aDir)); - aPlane = makeRectangularFace(aFace, aNewPln); + aPlane = makeRectangularFace(aFace, aNewPln); + theShapes.push_back(aPlane); + } } - return aPlane; } //================================================================================================== @@ -357,7 +379,7 @@ std::shared_ptr ConstructionPlugin_Plane::createByCoincidentPoint } //================================================================================================== -std::shared_ptr ConstructionPlugin_Plane::createByRotation() +void ConstructionPlugin_Plane::createByRotation(ListOfShape& theShapes) { // Get face. AttributeSelectionPtr aFaceSelection = selection(PLANE()); @@ -374,7 +396,7 @@ std::shared_ptr ConstructionPlugin_Plane::createByRotation() aFace = anIt.current()->face(); } if (!aFace) - return GeomShapePtr(); + return; aFace = makeRectangularFace(aFace, aFace->getPlane()); // Get axis. @@ -392,7 +414,14 @@ std::shared_ptr ConstructionPlugin_Plane::createByRotation() anEdge = anIt.current()->edge(); } if (!anEdge) - return GeomShapePtr(); + return; + + AttributeIntegerPtr aNbCopyAttr = data()->integer(ConstructionPlugin_Plane::NB_COPIES()); + int aNBCopy; + if (!aNbCopyAttr.get()) + return; + + aNBCopy = aNbCopyAttr->value(); std::shared_ptr anAxis = std::shared_ptr(new GeomAPI_Ax1(anEdge->line()->location(), @@ -401,17 +430,20 @@ std::shared_ptr ConstructionPlugin_Plane::createByRotation() // Getting angle. double anAngle = real(ANGLE())->value(); - std::shared_ptr aRotationAlgo( - new GeomAlgoAPI_Rotation(aFace, anAxis, anAngle)); - // Checking that the algorithm worked properly. - std::string anError; - if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aRotationAlgo, getKind(), anError)) { - setError("Error: Failed to rotate plane"); - return GeomShapePtr(); - } + for (int anIndex = 1; anIndex <= aNBCopy; ++anIndex) + { + std::shared_ptr aRotationAlgo( + new GeomAlgoAPI_Rotation(aFace, anAxis, anAngle * anIndex)); + // Checking that the algorithm worked properly. + std::string anError; + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aRotationAlgo, getKind(), anError)) { + setError("Error: Failed to rotate plane"); + return; + } - std::shared_ptr aRes(new GeomAPI_Face(aRotationAlgo->shape())); - return aRes; + std::shared_ptr aRes(new GeomAPI_Face(aRotationAlgo->shape())); + theShapes.push_back(aRes); + } } //================================================================================================== diff --git a/src/ConstructionPlugin/ConstructionPlugin_Plane.h b/src/ConstructionPlugin/ConstructionPlugin_Plane.h index 214f4e6db..ff6b14adf 100644 --- a/src/ConstructionPlugin/ConstructionPlugin_Plane.h +++ b/src/ConstructionPlugin/ConstructionPlugin_Plane.h @@ -25,6 +25,7 @@ #include #include #include +#include /// \class ConstructionPlugin_Plane /// \ingroup Plugins @@ -226,6 +227,11 @@ public: return ATTR_ID; } + inline static const std::string& NB_COPIES() + { + static const std::string ATTR_ID("nb_copies"); + return ATTR_ID; + } /// Attribute name for a parameter for the general equation of a plane (ax+by+cz+d=0) inline static const std::string& A() @@ -270,11 +276,11 @@ protected: std::shared_ptr createByThreePoints(); std::shared_ptr createByLineAndPoint(); std::shared_ptr createByCoincidentPoint(); - std::shared_ptr createByRotation(); + void createByRotation(ListOfShape& theShapes); std::shared_ptr createByTwoParallelPlanes(); /// Creates a new plane by copy of face plane with translation along the normal /// to the specified distance. - std::shared_ptr createByDistanceFromOther(); + void createByDistanceFromOther(ListOfShape& theShapes); }; #endif diff --git a/src/ConstructionPlugin/ConstructionPlugin_msg_en.ts b/src/ConstructionPlugin/ConstructionPlugin_msg_en.ts index ecb0de35d..2de6c5fea 100644 --- a/src/ConstructionPlugin/ConstructionPlugin_msg_en.ts +++ b/src/ConstructionPlugin/ConstructionPlugin_msg_en.ts @@ -300,6 +300,13 @@ Select the third point. + + Plane:nb_copies + + Attribute "%1" is not initialized. + Select the number of copies. + + Point:edge diff --git a/src/ConstructionPlugin/ConstructionPlugin_msg_fr.ts b/src/ConstructionPlugin/ConstructionPlugin_msg_fr.ts index 21fa8315f..240f138ed 100644 --- a/src/ConstructionPlugin/ConstructionPlugin_msg_fr.ts +++ b/src/ConstructionPlugin/ConstructionPlugin_msg_fr.ts @@ -759,7 +759,21 @@ Sens inverse - + + Plane:nb_copies + + Nb copies + Nb d'exemplaires + + + Number of copies of the plane + Nombre de plans + + + Attribute "%1" is not initialized. + Sélectionnez le nombre d'exemplaires. + + Point diff --git a/src/ConstructionPlugin/Test/TestPlane_Copies.py b/src/ConstructionPlugin/Test/TestPlane_Copies.py new file mode 100644 index 000000000..ddc46213d --- /dev/null +++ b/src/ConstructionPlugin/Test/TestPlane_Copies.py @@ -0,0 +1,65 @@ +# Copyright (C) 2014-2021 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 +# + +""" +Test case for Construction Plane feature. Written on High API. +""" +from ModelAPI import * +from GeomAPI import * + +from salome.shaper import model + +# Get session +aSession = ModelAPI_Session.get() + +# Create a part +aDocument = aSession.activeDocument() +aSession.startOperation() +model.addPart(aDocument) +aDocument = aSession.activeDocument() +aSession.finishOperation() + +# Test a plane by general equation +aSession.startOperation() +aPlane = model.addPlane(aDocument, 1, 1, 1, 0) +aSession.finishOperation() +assert (len(aPlane.results()) > 0) + +# Create an axis +aSession.startOperation() +anAxis = model.addAxis(aDocument, 100, 0, 0) +aSession.finishOperation() + +# Test a plane by rotation +aSession.startOperation() +aRotatedPlane = model.addPlane(aDocument, aPlane.result(), anAxis.result(), 45) +aRotatedPlanes = model.addPlane(aDocument, aPlane.result(), anAxis.result(), 45, 5) +aSession.finishOperation() +assert (len(aRotatedPlane.results()) > 0) +assert (len(aRotatedPlanes.results()) == 5) + +# Test a plane by distance from other +aSession.startOperation() +anOnlyPlane = model.addPlane(aDocument, aPlane.result(), 50, False) +assert (len(anOnlyPlane.results()) > 0) +aPlane = model.addPlane(aDocument, aPlane.result(), 50, False, 10) +aSession.finishOperation() +assert (len(aPlane.results()) == 10) + +assert(model.checkPythonDump()) diff --git a/src/ConstructionPlugin/doc/images/Plane3.png b/src/ConstructionPlugin/doc/images/Plane3.png index be4eddd0dad1d363bb9482b52c3b489003c06e91..a0512dd0a3c6d630bc4a2dbe42d587d81743ea5e 100644 GIT binary patch literal 14100 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 literal 14655 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%0AQ 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 + + + + diff --git a/src/ConstructionPlugin/tests.set b/src/ConstructionPlugin/tests.set index 09e2211bc..1bfdfa7fd 100644 --- a/src/ConstructionPlugin/tests.set +++ b/src/ConstructionPlugin/tests.set @@ -37,6 +37,7 @@ SET(TEST_NAMES TestPlane.py TestPlane_ErrorMsg.py TestPlane_FaceValidator.py + TestPlane_Copies.py Test19207.py Test19471.py ) diff --git a/src/Model/Model_Objects.cpp b/src/Model/Model_Objects.cpp index ebb3d6be9..1265360d6 100644 --- a/src/Model/Model_Objects.cpp +++ b/src/Model/Model_Objects.cpp @@ -1905,10 +1905,12 @@ void Model_Objects::updateResults(FeaturePtr theFeature, std::set& t } } else if (aGroup->Get() == ModelAPI_ResultConstruction::group().c_str()) { ResultConstructionPtr aConstr = createConstruction(theFeature->data(), aResIndex); - if (!aConstr->updateShape()) - theFeature->execute(); // not stored shape in the data structure, execute to have it - else - theFeature->setResult(aConstr, aResIndex); // result is ready without execution + if (!aConstr->data()->isDeleted()) { + if (!aConstr->updateShape()) + theFeature->execute(); // not stored shape in the data structure, execute to have it + else + theFeature->setResult(aConstr, aResIndex); // result is ready without execution + } } else if (aGroup->Get() == ModelAPI_ResultGroup::group().c_str()) { aNewBody = createGroup(theFeature->data(), aResIndex); } else if (aGroup->Get() == ModelAPI_ResultField::group().c_str()) { diff --git a/src/Model/Model_Update.cpp b/src/Model/Model_Update.cpp index 25c0cd978..dc5da8ad1 100644 --- a/src/Model/Model_Update.cpp +++ b/src/Model/Model_Update.cpp @@ -905,8 +905,7 @@ void Model_Update::updateArguments(FeaturePtr theFeature) { bool isObligatory = aFactory->isCase(theFeature, theFeature->data()->id(aSel)); if (isObligatory) aState = ModelAPI_StateInvalidArgument; - } else if (theFeature->getKind() == "Sketch" && aSel->id() == "External" && - aSel->isInitialized()) { + } else if (aSel->isInitialized()) { // #19703 : if sketch plane was selected, but after context disappears, it must become invalid aSel->update(); if (aSel->isInvalid()) { -- 2.39.2