From 4e318f7539c90724e703e14e4999e35d7c2941bc Mon Sep 17 00:00:00 2001 From: Artem Zhidkov Date: Thu, 3 Dec 2020 11:16:59 +0300 Subject: [PATCH] Issue #20274: No intersection point from python dump Improve dumping of the following entities to provide correct update of the sketch elements they produce: * Linear Copy * Angular Copy * Mirror --- src/ModelHighAPI/ModelHighAPI_Dumper.cpp | 17 ++- src/ModelHighAPI/ModelHighAPI_Dumper.h | 4 + src/ModelHighAPI/ModelHighAPI_Tools.cpp | 14 ++- src/SketchAPI/SketchAPI_Mirror.cpp | 74 ++++++++---- src/SketchAPI/SketchAPI_Mirror.h | 4 + src/SketchAPI/SketchAPI_Rotation.cpp | 100 ++++++++++------ src/SketchAPI/SketchAPI_Rotation.h | 4 + src/SketchAPI/SketchAPI_Translation.cpp | 94 ++++++++++----- src/SketchAPI/SketchAPI_Translation.h | 4 + src/SketchPlugin/CMakeLists.txt | 4 + .../SketchPlugin_ConstraintMirror.cpp | 7 ++ src/SketchPlugin/SketchPlugin_Offset.cpp | 7 ++ src/SketchPlugin/Test/Test20274_1.py | 112 ++++++++++++++++++ src/SketchPlugin/Test/Test20274_2.py | 112 ++++++++++++++++++ src/SketchPlugin/Test/Test20274_3.py | 112 ++++++++++++++++++ 15 files changed, 580 insertions(+), 89 deletions(-) create mode 100644 src/SketchPlugin/Test/Test20274_1.py create mode 100644 src/SketchPlugin/Test/Test20274_2.py create mode 100644 src/SketchPlugin/Test/Test20274_3.py diff --git a/src/ModelHighAPI/ModelHighAPI_Dumper.cpp b/src/ModelHighAPI/ModelHighAPI_Dumper.cpp index 9d2c5e5f0..324b827d0 100644 --- a/src/ModelHighAPI/ModelHighAPI_Dumper.cpp +++ b/src/ModelHighAPI/ModelHighAPI_Dumper.cpp @@ -992,6 +992,18 @@ bool ModelHighAPI_Dumper::isDumped(const AttributeRefListPtr& theRefList) const return true; } +size_t ModelHighAPI_Dumper::indexOfFirstNotDumped( + const std::shared_ptr& theRefList) const +{ + size_t anIndex = 0; + std::list anObjects = theRefList->list(); + for (std::list::const_iterator anIt = anObjects.begin(); + anIt != anObjects.end(); ++anIt, ++anIndex) + if (!isDumped(ModelAPI_Feature::feature(*anIt))) + break; + return anIndex; +} + static bool isSketchSub(const FeaturePtr& theFeature) { static const std::string SKETCH("Sketch"); @@ -1071,7 +1083,7 @@ bool ModelHighAPI_Dumper::dumpCommentBeforeFeature(const FeaturePtr& theFeature) if (aFilters) return false; // all other features should be commented before the dump - return true; + return !isDumped(theFeature); } ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar) @@ -1445,6 +1457,9 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<( bool isAdded = false; std::list::const_iterator anIt = aList.begin(); for (; anIt != aList.end(); ++anIt) { + if (!isDumped(ModelAPI_Feature::feature(*anIt))) + break; // stop if the object is not dumped yet (parent feature should be postponed) + if (isAdded) *myDumpStorage << ", "; else diff --git a/src/ModelHighAPI/ModelHighAPI_Dumper.h b/src/ModelHighAPI/ModelHighAPI_Dumper.h index 1fa21e9da..8e12c05bb 100644 --- a/src/ModelHighAPI/ModelHighAPI_Dumper.h +++ b/src/ModelHighAPI/ModelHighAPI_Dumper.h @@ -354,6 +354,10 @@ public: MODELHIGHAPI_EXPORT bool isDumped(const std::shared_ptr& theRefList) const; + /// Returns the index of the first object in the list which is not dumped yet. + MODELHIGHAPI_EXPORT + size_t indexOfFirstNotDumped(const std::shared_ptr& theRefList) const; + /// Export variables names to another module (calls exportVariable implemented in python) MODELHIGHAPI_EXPORT virtual void exportVariables() const; diff --git a/src/ModelHighAPI/ModelHighAPI_Tools.cpp b/src/ModelHighAPI/ModelHighAPI_Tools.cpp index 0b72c418f..7f959291c 100644 --- a/src/ModelHighAPI/ModelHighAPI_Tools.cpp +++ b/src/ModelHighAPI/ModelHighAPI_Tools.cpp @@ -168,8 +168,18 @@ void fillAttribute(const std::shared_ptr & theValue, void fillAttribute(const std::list > & theValue, const std::shared_ptr & theAttribute) { - theAttribute->clear(); - for (auto it = theValue.begin(); it != theValue.end(); ++it) + int aSize = theAttribute->size(); + // keep objects at the beginning of the list if they the same + auto it = theValue.begin(); + for (int anIndex = 0; it != theValue.end() && anIndex < aSize; ++it, ++anIndex) + if (theAttribute->object(anIndex) != *it) { + // remove the tail of the list + while (++anIndex <= aSize) + theAttribute->removeLast(); + break; + } + // append the rest of elements + for (; it != theValue.end(); ++it) theAttribute->append(*it); } diff --git a/src/SketchAPI/SketchAPI_Mirror.cpp b/src/SketchAPI/SketchAPI_Mirror.cpp index 1013e3cf5..534f50576 100644 --- a/src/SketchAPI/SketchAPI_Mirror.cpp +++ b/src/SketchAPI/SketchAPI_Mirror.cpp @@ -39,15 +39,18 @@ SketchAPI_Mirror::SketchAPI_Mirror( { if (initialize()) { fillAttribute(theMirrorLine, mirrorLine()); - fillAttribute(theObjects, mirrorList()); - - execute(); + setMirrorList(theObjects); } } SketchAPI_Mirror::~SketchAPI_Mirror() { +} +void SketchAPI_Mirror::setMirrorList(const std::list >& theObjects) +{ + fillAttribute(theObjects, mirrorList()); + execute(); } std::list > SketchAPI_Mirror::mirrored() const @@ -69,44 +72,71 @@ void SketchAPI_Mirror::dump(ModelHighAPI_Dumper& theDumper) const FeaturePtr aBase = feature(); const std::string& aSketchName = theDumper.parentName(aBase); - AttributeRefAttrPtr aMirrorLine = mirrorLine(); AttributeRefListPtr aMirrorObjects = mirrorList(); // Check all attributes are already dumped. If not, store the constraint as postponed. - if (!theDumper.isDumped(aMirrorLine) || !theDumper.isDumped(aMirrorObjects)) { + size_t aFirstNotDumped = theDumper.indexOfFirstNotDumped(aMirrorObjects); + if (!theDumper.isDumped(aMirrorLine) || aFirstNotDumped == 0) { + theDumper.postpone(aBase); + return; + } + + + // the number of dumped aMirrorObjects is not changed, no need to dump anything + static std::map aNbDumpedArguments; + std::map::iterator aFound = aNbDumpedArguments.find(aBase); + if (aFound != aNbDumpedArguments.end() && aFound->second == aFirstNotDumped) { theDumper.postpone(aBase); return; } + else + aNbDumpedArguments[aBase] = aFirstNotDumped; - theDumper << aBase << " = " << aSketchName << ".addMirror(" << aMirrorLine << ", " - << aMirrorObjects << ")" << std::endl; + if (theDumper.isDumped(aBase)) { + // the feature is already dumped, but it was postponed, because of some arguments + // were not dumped yet, thus, it is necessary to update the list of rotated objects + theDumper << "\n### Update " << aBase->getKind() << std::endl; + theDumper << aBase << ".setMirrorList(" << aMirrorObjects << ")" << std::endl; + } + else { + // the feature is not dumped yet, make the full dump + theDumper << aBase << " = " << aSketchName << ".addMirror(" << aMirrorLine << ", " + << aMirrorObjects << ")" << std::endl; + } // Dump variables for a list of mirrored features theDumper << "["; std::list > aList = mirrored(); std::list >::const_iterator anIt = aList.begin(); - for (; anIt != aList.end(); ++anIt) { + for (size_t anIndex = 0; anIndex < aFirstNotDumped; ++anIndex, ++anIt) { if (anIt != aList.begin()) theDumper << ", "; theDumper << (*anIt)->feature(); } theDumper << "] = " << theDumper.name(aBase) << ".mirrored()" << std::endl; - // Set necessary "auxiliary" flag for mirrored features - // (flag is set if it differs to base entity) - std::list aMirList = aMirrorObjects->list(); - std::list::const_iterator aMIt = aMirList.begin(); - for (anIt = aList.begin(); aMIt != aMirList.end(); ++aMIt, ++anIt) { - FeaturePtr aFeature = ModelAPI_Feature::feature(*aMIt); - if (!aFeature) - continue; - bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); + if (theDumper.isDumped(aMirrorObjects)) { + aNbDumpedArguments.erase(aBase); + // Set necessary "auxiliary" flag for mirrored features + // (flag is set if it differs to base entity) + std::list aMirList = aMirrorObjects->list(); + std::list::const_iterator aMIt = aMirList.begin(); + for (anIt = aList.begin(); aMIt != aMirList.end(); ++aMIt, ++anIt) { + FeaturePtr aFeature = ModelAPI_Feature::feature(*aMIt); + if (!aFeature) + continue; + bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); - aFeature = (*anIt)->feature(); - bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); - if (aFeatAux != aBaseAux) - theDumper << theDumper.name((*anIt)->feature(), false) - << ".setAuxiliary(" << aFeatAux << ")" <feature(); + bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); + if (aFeatAux != aBaseAux) + theDumper << theDumper.name((*anIt)->feature(), false) + << ".setAuxiliary(" << aFeatAux << ")" < >& theObjects); + /// List of mirrored objects SKETCHAPI_EXPORT std::list > mirrored() const; diff --git a/src/SketchAPI/SketchAPI_Rotation.cpp b/src/SketchAPI/SketchAPI_Rotation.cpp index eb81b0d70..07e426a71 100644 --- a/src/SketchAPI/SketchAPI_Rotation.cpp +++ b/src/SketchAPI/SketchAPI_Rotation.cpp @@ -43,14 +43,13 @@ SketchAPI_Rotation::SketchAPI_Rotation( : ModelHighAPI_Interface(theFeature) { if (initialize()) { - fillAttribute(theObjects, rotationList()); fillAttribute(theCenter, center()); fillAttribute(theFullValue ? "FullAngle" : "SingleAngle", valueType()); fillAttribute(theAngle, angle()); fillAttribute(theReversed, reversed()); fillAttribute(theNumberOfObjects, numberOfObjects()); - execute(true); + setRotationList(theObjects); } } @@ -59,6 +58,13 @@ SketchAPI_Rotation::~SketchAPI_Rotation() } +void SketchAPI_Rotation::setRotationList( + const std::list >& theObjects) +{ + fillAttribute(theObjects, rotationList()); + execute(true); +} + std::list > SketchAPI_Rotation::rotated() const { std::list aList = rotatedObjects()->list(); @@ -89,49 +95,77 @@ void SketchAPI_Rotation::dump(ModelHighAPI_Dumper& theDumper) const bool isReversed = reversed()->value(); // Check all attributes are already dumped. If not, store the constraint as postponed. - if (!theDumper.isDumped(aCenter) || !theDumper.isDumped(aRotObjects)) { + size_t aFirstNotDumped = theDumper.indexOfFirstNotDumped(aRotObjects); + if (!theDumper.isDumped(aCenter) || aFirstNotDumped == 0) { theDumper.postpone(aBase); return; } - theDumper << aBase << " = " << aSketchName << ".addRotation(" - << aRotObjects << ", " << aCenter << ", " << anAngle << ", " << aNbCopies; - if (isFullValue || isReversed) - { - theDumper << ", " << isFullValue; - if (isReversed) - theDumper << ", " << isReversed; + // the number of dumped aRotObjects is not changed, no need to dump anything + static std::map aNbDumpedArguments; + std::map::iterator aFound = aNbDumpedArguments.find(aBase); + if (aFound != aNbDumpedArguments.end() && aFound->second == aFirstNotDumped) { + theDumper.postpone(aBase); + return; + } + else + aNbDumpedArguments[aBase] = aFirstNotDumped; + + if (theDumper.isDumped(aBase)) { + // the feature is already dumped, but it was postponed, because of some arguments + // were not dumped yet, thus, it is necessary to update the list of rotated objects + theDumper << "\n### Update " << aBase->getKind() << std::endl; + theDumper << aBase << ".setRotationList(" << aRotObjects << ")" << std::endl; + } + else { + // the feature is not dumped yet, make the full dump + theDumper << aBase << " = " << aSketchName << ".addRotation(" + << aRotObjects << ", " << aCenter << ", " << anAngle << ", " << aNbCopies; + if (isFullValue || isReversed) + { + theDumper << ", " << isFullValue; + if (isReversed) + theDumper << ", " << isReversed; + } + theDumper << ")" << std::endl; } - theDumper << ")" << std::endl; // Dump variables for a list of rotated features theDumper << "["; std::list > aList = rotated(); std::list >::const_iterator anIt = aList.begin(); - for (; anIt != aList.end(); ++anIt) { - if (anIt != aList.begin()) - theDumper << ", "; - theDumper << (*anIt)->feature(); - } + for (size_t anIndex = 0; anIndex < aFirstNotDumped; ++anIndex) + for (int i = 1; i < aNbCopies->value() && anIt != aList.end(); ++i, ++anIt) { + if (anIt != aList.begin()) + theDumper << ", "; + theDumper << (*anIt)->feature(); + } theDumper << "] = " << theDumper.name(aBase) << ".rotated()" << std::endl; - // Set necessary "auxiliary" flag for rotated features - // (flag is set if it differs to base entity) - std::list aRotList = aRotObjects->list(); - std::list::const_iterator aRIt = aRotList.begin(); - anIt = aList.begin(); - for (; aRIt != aRotList.end(); ++aRIt) { - FeaturePtr aFeature = ModelAPI_Feature::feature(*aRIt); - if (!aFeature) - continue; - bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); - - for (int i = 1; i < aNbCopies->value(); ++i, ++anIt) { - aFeature = (*anIt)->feature(); - bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); - if (aFeatAux != aBaseAux) - theDumper << theDumper.name((*anIt)->feature(), false) - << ".setAuxiliary(" << aFeatAux << ")" < aRotList = aRotObjects->list(); + std::list::const_iterator aRIt = aRotList.begin(); + anIt = aList.begin(); + for (; aRIt != aRotList.end(); ++aRIt) { + FeaturePtr aFeature = ModelAPI_Feature::feature(*aRIt); + if (!aFeature) + continue; + bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); + + for (int i = 1; i < aNbCopies->value(); ++i, ++anIt) { + aFeature = (*anIt)->feature(); + bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); + if (aFeatAux != aBaseAux) + theDumper << theDumper.name((*anIt)->feature(), false) + << ".setAuxiliary(" << aFeatAux << ")" << std::endl; + } } } + else { + // If all refereced objects are not dumped yet, mark the feature as postponed. + theDumper.postpone(aBase); + } } diff --git a/src/SketchAPI/SketchAPI_Rotation.h b/src/SketchAPI/SketchAPI_Rotation.h index ac3392f92..e9899d001 100644 --- a/src/SketchAPI/SketchAPI_Rotation.h +++ b/src/SketchAPI/SketchAPI_Rotation.h @@ -78,6 +78,10 @@ public: ModelAPI_AttributeRefList, /** Rotated objects */ ) + /// Set list of objects to be rotated + SKETCHAPI_EXPORT + void setRotationList(const std::list >& theObjects); + /// List of rotated objects SKETCHAPI_EXPORT std::list > rotated() const; diff --git a/src/SketchAPI/SketchAPI_Translation.cpp b/src/SketchAPI/SketchAPI_Translation.cpp index 660286d45..3c95ce76b 100644 --- a/src/SketchAPI/SketchAPI_Translation.cpp +++ b/src/SketchAPI/SketchAPI_Translation.cpp @@ -42,19 +42,24 @@ SketchAPI_Translation::SketchAPI_Translation( : ModelHighAPI_Interface(theFeature) { if (initialize()) { - fillAttribute(theObjects, translationList()); fillAttribute(thePoint1, startPoint()); fillAttribute(thePoint2, endPoint()); fillAttribute(theNumberOfObjects, numberOfObjects()); fillAttribute(theFullValue ? "FullValue" : "SingleValue", valueType()); - execute(true); + setTranslationList(theObjects); } } SketchAPI_Translation::~SketchAPI_Translation() { +} +void SketchAPI_Translation::setTranslationList( + const std::list >& theObjects) +{ + fillAttribute(theObjects, translationList()); + execute(true); } std::list > SketchAPI_Translation::translated() const @@ -86,46 +91,73 @@ void SketchAPI_Translation::dump(ModelHighAPI_Dumper& theDumper) const bool isFullValue = valueType()->value() != "SingleValue"; // Check all attributes are already dumped. If not, store the constraint as postponed. - if (!theDumper.isDumped(aStart) || !theDumper.isDumped(aEnd) || - !theDumper.isDumped(aTransObjects)) { + size_t aFirstNotDumped = theDumper.indexOfFirstNotDumped(aTransObjects); + if (!theDumper.isDumped(aStart) || !theDumper.isDumped(aEnd) || aFirstNotDumped == 0) { theDumper.postpone(aBase); return; } - theDumper << aBase << " = " << aSketchName << ".addTranslation(" - << aTransObjects << ", " << aStart << ", " << aEnd << ", " << aNbCopies; - if (isFullValue) - theDumper << ", " << isFullValue; - theDumper << ")" << std::endl; + // the number of dumped aTransObjects is not changed, no need to dump anything + static std::map aNbDumpedArguments; + std::map::iterator aFound = aNbDumpedArguments.find(aBase); + if (aFound != aNbDumpedArguments.end() && aFound->second == aFirstNotDumped) { + theDumper.postpone(aBase); + return; + } + else + aNbDumpedArguments[aBase] = aFirstNotDumped; + + if (theDumper.isDumped(aBase)) { + // the feature is already dumped, but it was postponed, because of some arguments + // were not dumped yet, thus, it is necessary to update the list of rotated objects + theDumper << "\n### Update " << aBase->getKind() << std::endl; + theDumper << aBase << ".setTranslationList(" << aTransObjects << ")" << std::endl; + } + else { + // the feature is not dumped yet, make the full dump + theDumper << aBase << " = " << aSketchName << ".addTranslation(" + << aTransObjects << ", " << aStart << ", " << aEnd << ", " << aNbCopies; + if (isFullValue) + theDumper << ", " << isFullValue; + theDumper << ")" << std::endl; + } // Dump variables for a list of translated features theDumper << "["; std::list > aList = translated(); std::list >::const_iterator anIt = aList.begin(); - for (; anIt != aList.end(); ++anIt) { - if (anIt != aList.begin()) - theDumper << ", "; - theDumper << (*anIt)->feature(); - } + for (size_t anIndex = 0; anIndex < aFirstNotDumped; ++anIndex) + for (int i = 1; i < aNbCopies->value() && anIt != aList.end(); ++i, ++anIt) { + if (anIt != aList.begin()) + theDumper << ", "; + theDumper << (*anIt)->feature(); + } theDumper << "] = " << theDumper.name(aBase) << ".translated()" << std::endl; - // Set necessary "auxiliary" flag for translated features - // (flag is set if it differs to base entity) - std::list aTransList = aTransObjects->list(); - std::list::const_iterator aTrIt = aTransList.begin(); - anIt = aList.begin(); - for (; aTrIt != aTransList.end(); ++aTrIt) { - FeaturePtr aFeature = ModelAPI_Feature::feature(*aTrIt); - if (!aFeature) - continue; - bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); - - for (int i = 1; i < aNbCopies->value(); ++i, ++anIt) { - aFeature = (*anIt)->feature(); - bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); - if (aFeatAux != aBaseAux) - theDumper << theDumper.name((*anIt)->feature(), false) - << ".setAuxiliary(" << aFeatAux << ")" < aTransList = aTransObjects->list(); + std::list::const_iterator aTrIt = aTransList.begin(); + anIt = aList.begin(); + for (; aTrIt != aTransList.end(); ++aTrIt) { + FeaturePtr aFeature = ModelAPI_Feature::feature(*aTrIt); + if (!aFeature) + continue; + bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); + + for (int i = 1; i < aNbCopies->value(); ++i, ++anIt) { + aFeature = (*anIt)->feature(); + bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value(); + if (aFeatAux != aBaseAux) + theDumper << theDumper.name((*anIt)->feature(), false) + << ".setAuxiliary(" << aFeatAux << ")" << std::endl; + } } } + else { + // If all refereced objects are not dumped yet, mark the feature as postponed. + theDumper.postpone(aBase); + } } diff --git a/src/SketchAPI/SketchAPI_Translation.h b/src/SketchAPI/SketchAPI_Translation.h index 558fa5a50..32119218e 100644 --- a/src/SketchAPI/SketchAPI_Translation.h +++ b/src/SketchAPI/SketchAPI_Translation.h @@ -74,6 +74,10 @@ public: ModelAPI_AttributeRefList, /** Translationed objects */ ) + /// Set list of objects to be translated + SKETCHAPI_EXPORT + void setTranslationList(const std::list >& theObjects); + /// List of translated objects SKETCHAPI_EXPORT std::list > translated() const; diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 3fcc4e389..b5cbdd3d3 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -233,6 +233,10 @@ ADD_UNIT_TESTS( Test3240.py Test19089.py Test19101.py + Test20274_1.py + Test20274_2.py + Test20274_3.py + TestArcBehavior.py TestBSplineAddPole.py TestChangeSketchPlane1.py diff --git a/src/SketchPlugin/SketchPlugin_ConstraintMirror.cpp b/src/SketchPlugin/SketchPlugin_ConstraintMirror.cpp index a8eb3d6c4..009e9ab44 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintMirror.cpp +++ b/src/SketchPlugin/SketchPlugin_ConstraintMirror.cpp @@ -62,6 +62,11 @@ void SketchPlugin_ConstraintMirror::execute() if (isUpdateFlushed) Events_Loop::loop()->setFlushed(anUpdateEvent, false); + // Save the current feature of the document, because new features may appear while executing. + // In this case, they will become current. But if the number of copies is updated from outside + // of sketch (e.g. by parameter change), the history line should not hold in sketch. + keepCurrentFeature(); + std::shared_ptr aData = data(); AttributeRefListPtr aRefListOfShapes = std::dynamic_pointer_cast( aData->attribute(SketchPlugin_Constraint::ENTITY_B())); @@ -198,6 +203,8 @@ void SketchPlugin_ConstraintMirror::execute() } } + restoreCurrentFeature(); + // send events to update the sub-features by the solver if (isUpdateFlushed) Events_Loop::loop()->setFlushed(anUpdateEvent, true); diff --git a/src/SketchPlugin/SketchPlugin_Offset.cpp b/src/SketchPlugin/SketchPlugin_Offset.cpp index f3f8d2b89..4400e8e61 100644 --- a/src/SketchPlugin/SketchPlugin_Offset.cpp +++ b/src/SketchPlugin/SketchPlugin_Offset.cpp @@ -127,6 +127,11 @@ void SketchPlugin_Offset::execute() if (isUpdateFlushed) Events_Loop::loop()->setFlushed(anUpdateEvent, false); + // Save the current feature of the document, because new features may appear while executing. + // In this case, they will become current. But if the number of copies is updated from outside + // of sketch (e.g. by parameter change), the history line should not hold in sketch. + keepCurrentFeature(); + // 5. Gather wires and make offset for each wire ListOfMakeShape anOffsetAlgos; std::set aProcessedEdgesSet; @@ -199,6 +204,8 @@ void SketchPlugin_Offset::execute() // created features in CREATED_ID() to remove them on next execute() addToSketch(anOffsetAlgos); + restoreCurrentFeature(); + // send events to update the sub-features by the solver if (isUpdateFlushed) Events_Loop::loop()->setFlushed(anUpdateEvent, true); diff --git a/src/SketchPlugin/Test/Test20274_1.py b/src/SketchPlugin/Test/Test20274_1.py new file mode 100644 index 000000000..353241aa7 --- /dev/null +++ b/src/SketchPlugin/Test/Test20274_1.py @@ -0,0 +1,112 @@ +# Copyright (C) 2020 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 GeomAlgoAPI import * +from SketchAPI import * + +from salome.shaper import model + +import math + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(50, 0, 41.0188620508502, 41.01886205085074) + +### Create SketchProjection +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_2 = SketchProjection_1.createdFeature() +Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_2.result()) + +### Create SketchLine +SketchLine_3 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 0, 50) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_3.startPoint()) + +### Create SketchProjection +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_4 = SketchProjection_2.createdFeature() +Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.result()) + +### Create SketchLine +SketchLine_5 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 70.71067811865088, 70.71067811866516) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_5.startPoint()) + +### Create SketchCircle +SketchCircle_1 = Sketch_1.addCircle(0, 0, 100) +SketchCircle_1.setAuxiliary(True) +Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).startPoint(), SketchCircle_1.center()) +Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).endPoint(), SketchCircle_1.results()[1]) +Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchCircle_1.results()[1]) +Sketch_1.setMiddlePoint(SketchLine_1.startPoint(), SketchLine_2.result()) +Sketch_1.setMiddlePoint(SketchLine_3.endPoint(), SketchLine_4.result()) +Sketch_1.setEqual(SketchLine_1.result(), SketchLine_3.result()) +Sketch_1.setEqual(SketchLine_1.result(), SketchLine_5.result()) +Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_5.result()) + +### Create SketchMultiRotation +SketchMultiRotation_1_objects = [SketchLine_1.result(), SketchLine_3.result(), SketchLine_5.result()] +SketchMultiRotation_1 = Sketch_1.addRotation(SketchMultiRotation_1_objects, SketchCircle_1.center(), 360, 4, True) +[SketchLine_6, SketchLine_7, SketchLine_8, SketchLine_9, SketchLine_10, SketchLine_11, SketchLine_12, SketchLine_13, SketchLine_14] = SketchMultiRotation_1.rotated() +model.do() + +### Create Extrusion +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 100, 0, "Edges") +model.end() + +### Store volumes of sub-shapes as a reference +TOLERANCE = 1.e-7 +REFERENCE = [] +resultExtrusion_1 = Extrusion_1.result() +for ind in range(resultExtrusion_1.numberOfSubs()): + REFERENCE.append(GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())) + +### Add new edges to Sketch_1 then check the Extrusion and update reference data +model.begin() +SketchLine_6 = Sketch_1.addLine(70.71067811865088, 70.71067811866516, 77.78174593052023, 77.78174593052023) +Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +Sketch_1.setCollinear(SketchLine_5.result(), SketchLine_6.result()) +Sketch_1.setLength(SketchLine_6.result(), 10) + +SketchMultiRotation_1.rotationList().append(SketchLine_6.defaultResult()) +model.end() + +resultExtrusion_1 = Extrusion_1.result() +for ind in range(resultExtrusion_1.numberOfSubs()): + area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()) + if (ind < len(REFERENCE)): + assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE) + else: + REFERENCE.append(area) + +assert(model.checkPythonDump(model.CHECK_NAMING)) + +### Check results after dump +resultExtrusion_1 = Extrusion_1.result() +assert(len(REFERENCE) == resultExtrusion_1.numberOfSubs()) +for ind in range(resultExtrusion_1.numberOfSubs()): + area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()) + assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE) diff --git a/src/SketchPlugin/Test/Test20274_2.py b/src/SketchPlugin/Test/Test20274_2.py new file mode 100644 index 000000000..e239d3b76 --- /dev/null +++ b/src/SketchPlugin/Test/Test20274_2.py @@ -0,0 +1,112 @@ +# Copyright (C) 2020 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 GeomAlgoAPI import * +from SketchAPI import * + +from salome.shaper import model + +import math + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(50, 0, 41.0188620508502, 41.01886205085074) + +### Create SketchProjection +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_2 = SketchProjection_1.createdFeature() +Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_2.result()) + +### Create SketchLine +SketchLine_3 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 0, 50) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_3.startPoint()) + +### Create SketchProjection +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_4 = SketchProjection_2.createdFeature() +Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.result()) + +### Create SketchLine +SketchLine_5 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 70.71067811865088, 70.71067811866516) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_5.startPoint()) + +### Create SketchCircle +SketchCircle_1 = Sketch_1.addCircle(0, 0, 100) +SketchCircle_1.setAuxiliary(True) +Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).startPoint(), SketchCircle_1.center()) +Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).endPoint(), SketchCircle_1.results()[1]) +Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchCircle_1.results()[1]) +Sketch_1.setMiddlePoint(SketchLine_1.startPoint(), SketchLine_2.result()) +Sketch_1.setMiddlePoint(SketchLine_3.endPoint(), SketchLine_4.result()) +Sketch_1.setEqual(SketchLine_1.result(), SketchLine_3.result()) +Sketch_1.setEqual(SketchLine_1.result(), SketchLine_5.result()) +Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_5.result()) + +### Create SketchMultiTranslation +SketchMultiTranslation_1_objects = [SketchLine_1.result(), SketchLine_3.result(), SketchLine_5.result()] +SketchMultiTranslation_1 = Sketch_1.addTranslation(SketchMultiTranslation_1_objects, SketchAPI_Line(SketchLine_4).startPoint(), SketchLine_5.endPoint(), 2) +[SketchLine_7, SketchLine_8, SketchLine_9] = SketchMultiTranslation_1.translated() +model.do() + +### Create Extrusion +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 100, 0, "Edges") +model.end() + +### Store volumes of sub-shapes as a reference +TOLERANCE = 1.e-7 +REFERENCE = [] +resultExtrusion_1 = Extrusion_1.result() +for ind in range(resultExtrusion_1.numberOfSubs()): + REFERENCE.append(GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())) + +### Add new edges to Sketch_1 then check the Extrusion and update reference data +model.begin() +SketchLine_6 = Sketch_1.addLine(70.71067811865088, 70.71067811866516, 77.78174593052023, 77.78174593052023) +Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +Sketch_1.setCollinear(SketchLine_5.result(), SketchLine_6.result()) +Sketch_1.setLength(SketchLine_6.result(), 10) + +SketchMultiTranslation_1.translationList().append(SketchLine_6.defaultResult()) +model.end() + +resultExtrusion_1 = Extrusion_1.result() +for ind in range(resultExtrusion_1.numberOfSubs()): + area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()) + if (ind < len(REFERENCE)): + assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE) + else: + REFERENCE.append(area) + +assert(model.checkPythonDump(model.CHECK_NAMING)) + +### Check results after dump +resultExtrusion_1 = Extrusion_1.result() +assert(len(REFERENCE) == resultExtrusion_1.numberOfSubs()) +for ind in range(resultExtrusion_1.numberOfSubs()): + area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()) + assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE) diff --git a/src/SketchPlugin/Test/Test20274_3.py b/src/SketchPlugin/Test/Test20274_3.py new file mode 100644 index 000000000..de30b85cc --- /dev/null +++ b/src/SketchPlugin/Test/Test20274_3.py @@ -0,0 +1,112 @@ +# Copyright (C) 2020 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 GeomAlgoAPI import * +from SketchAPI import * + +from salome.shaper import model + +import math + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(50, 0, 41.0188620508502, 41.01886205085074) + +### Create SketchProjection +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_2 = SketchProjection_1.createdFeature() +Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_2.result()) + +### Create SketchLine +SketchLine_3 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 0, 50) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_3.startPoint()) + +### Create SketchProjection +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_4 = SketchProjection_2.createdFeature() +Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.result()) + +### Create SketchLine +SketchLine_5 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 70.71067811865088, 70.71067811866516) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_5.startPoint()) + +### Create SketchCircle +SketchCircle_1 = Sketch_1.addCircle(0, 0, 100) +SketchCircle_1.setAuxiliary(True) +Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).startPoint(), SketchCircle_1.center()) +Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).endPoint(), SketchCircle_1.results()[1]) +Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchCircle_1.results()[1]) +Sketch_1.setMiddlePoint(SketchLine_1.startPoint(), SketchLine_2.result()) +Sketch_1.setMiddlePoint(SketchLine_3.endPoint(), SketchLine_4.result()) +Sketch_1.setEqual(SketchLine_1.result(), SketchLine_3.result()) +Sketch_1.setEqual(SketchLine_1.result(), SketchLine_5.result()) +Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_5.result()) + +### Create SketchConstraintMirror +SketchConstraintMirror_1_objects = [SketchLine_1.result(), SketchLine_3.result(), SketchLine_5.result()] +SketchConstraintMirror_1 = Sketch_1.addMirror(SketchLine_2.result(), SketchConstraintMirror_1_objects) +[SketchLine_7, SketchLine_8, SketchLine_9] = SketchConstraintMirror_1.mirrored() +model.do() + +### Create Extrusion +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 100, 0, "Edges") +model.end() + +### Store volumes of sub-shapes as a reference +TOLERANCE = 1.e-7 +REFERENCE = [] +resultExtrusion_1 = Extrusion_1.result() +for ind in range(resultExtrusion_1.numberOfSubs()): + REFERENCE.append(GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())) + +### Add new edges to Sketch_1 then check the Extrusion and update reference data +model.begin() +SketchLine_6 = Sketch_1.addLine(70.71067811865088, 70.71067811866516, 77.78174593052023, 77.78174593052023) +Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +Sketch_1.setCollinear(SketchLine_5.result(), SketchLine_6.result()) +Sketch_1.setLength(SketchLine_6.result(), 10) + +SketchConstraintMirror_1.mirrorList().append(SketchLine_6.defaultResult()) +model.end() + +resultExtrusion_1 = Extrusion_1.result() +for ind in range(resultExtrusion_1.numberOfSubs()): + area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()) + if (ind < len(REFERENCE)): + assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE) + else: + REFERENCE.append(area) + +assert(model.checkPythonDump(model.CHECK_NAMING)) + +### Check results after dump +resultExtrusion_1 = Extrusion_1.result() +assert(len(REFERENCE) == resultExtrusion_1.numberOfSubs()) +for ind in range(resultExtrusion_1.numberOfSubs()): + area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()) + assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE) -- 2.39.2