From cdd17f8b9923e1283e0a8bd239cd857ac4bd502a Mon Sep 17 00:00:00 2001 From: Artem Zhidkov Date: Thu, 2 Jul 2020 14:06:46 +0300 Subject: [PATCH] Task #3235: Projection without link to source shape * Improve the Projection feature by an option to break the link with the original shape. * Update unit-tests for ellipse's projection, because of solved OCCT issue #31016. --- src/GeomAlgoAPI/GeomAlgoAPI_Projection.cpp | 3 +- src/ModuleBase/ModuleBase_PagedContainer.cpp | 3 +- src/ModuleBase/ModuleBase_WidgetRadiobox.cpp | 11 +- src/ModuleBase/ModuleBase_WidgetRadiobox.h | 1 + src/PartSet/PartSet_SketcherMgr.cpp | 2 +- src/SketchAPI/SketchAPI.i | 5 +- src/SketchAPI/SketchAPI_Projection.cpp | 22 +-- src/SketchAPI/SketchAPI_Projection.h | 12 +- src/SketchAPI/SketchAPI_Sketch.cpp | 20 +-- src/SketchAPI/SketchAPI_Sketch.h | 8 +- src/SketchAPI/Test/TestSketch.py | 2 +- src/SketchPlugin/CMakeLists.txt | 1 + src/SketchPlugin/SketchPlugin_Ellipse.cpp | 8 +- src/SketchPlugin/SketchPlugin_Projection.cpp | 83 +++++++-- src/SketchPlugin/SketchPlugin_Projection.h | 26 +++ .../SketchPlugin_SketchEntity.cpp | 3 + src/SketchPlugin/SketchPlugin_SketchEntity.h | 3 + src/SketchPlugin/SketchPlugin_Validators.cpp | 2 +- src/SketchPlugin/SketchPlugin_msg_en.ts | 68 +++++++- src/SketchPlugin/SketchPlugin_msg_fr.ts | 161 ++++++++++-------- .../Test/TestProjectionEllipse.py | 5 +- .../Test/TestProjectionWithoutReference.py | 146 ++++++++++++++++ src/SketchPlugin/plugin-Sketch.xml | 22 ++- .../PlaneGCSSolver/PlaneGCSSolver_Storage.cpp | 2 +- 24 files changed, 470 insertions(+), 149 deletions(-) create mode 100644 src/SketchPlugin/Test/TestProjectionWithoutReference.py diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Projection.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_Projection.cpp index e85b9254f..3c93967ac 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_Projection.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Projection.cpp @@ -42,7 +42,8 @@ GeomCurvePtr GeomAlgoAPI_Projection::project(const GeomCurvePtr& theCurve) Handle(Geom_Curve) aCurve = theCurve->impl(); Handle(Geom_Plane) aPlane = new Geom_Plane(myPlane->impl()); - Handle(Geom_Curve) aProj = GeomProjLib::Project(aCurve, aPlane); + Handle(Geom_Curve) aProj = + GeomProjLib::ProjectOnPlane(aCurve, aPlane, aPlane->Axis().Direction(), false); GeomCurvePtr aProjCurve(new GeomAPI_Curve); aProjCurve->setImpl(new Handle_Geom_Curve(aProj)); diff --git a/src/ModuleBase/ModuleBase_PagedContainer.cpp b/src/ModuleBase/ModuleBase_PagedContainer.cpp index e9461b7e7..bdd184274 100644 --- a/src/ModuleBase/ModuleBase_PagedContainer.cpp +++ b/src/ModuleBase/ModuleBase_PagedContainer.cpp @@ -106,7 +106,8 @@ bool ModuleBase_PagedContainer::restoreValueCustom() aCaseId = QString::fromStdString(aStringAttr->value()); else aCaseId = QString::fromStdString(aDefVal.empty() ? aStringAttr->value() : aDefVal); - myIsFirst = false; + if (myIsFirst) + storeValueCustom(); int idx = myCaseIds.indexOf(aCaseId); if (idx == -1) idx = currentPageIndex(); diff --git a/src/ModuleBase/ModuleBase_WidgetRadiobox.cpp b/src/ModuleBase/ModuleBase_WidgetRadiobox.cpp index f98ddc5c0..dd71c1241 100644 --- a/src/ModuleBase/ModuleBase_WidgetRadiobox.cpp +++ b/src/ModuleBase/ModuleBase_WidgetRadiobox.cpp @@ -20,12 +20,13 @@ #include #include +#include + #include #include #include #include - ModuleBase_WidgetRadiobox::ModuleBase_WidgetRadiobox(QWidget* theParent, const Config_WidgetAPI* theData) : ModuleBase_PagedContainer(theParent, theData) @@ -35,6 +36,8 @@ ModuleBase_WidgetRadiobox::ModuleBase_WidgetRadiobox(QWidget* theParent, myGroup = new QButtonGroup(this); myGroup->setExclusive(true); + myVerticalAlignment = theData->getProperty("align_subs").find("vert") == 0; + connect(myGroup, SIGNAL(buttonToggled(int, bool)), SLOT(onPageChanged())); } @@ -68,7 +71,11 @@ int ModuleBase_WidgetRadiobox::addPage(ModuleBase_PageBase* thePage, //QFrame* aFrame = dynamic_cast(thePage); QWidget* aPage = thePage->pageWidget(); - myLayout->addRow(aWgt, aPage); + if (myVerticalAlignment) { + myLayout->addRow(aWgt); + myLayout->addRow(aPage); + } else + myLayout->addRow(aWgt, aPage); myGroup->addButton(aButton, myGroup->buttons().count()); bool isDefault = theCaseId.toStdString() == getDefaultValue(); diff --git a/src/ModuleBase/ModuleBase_WidgetRadiobox.h b/src/ModuleBase/ModuleBase_WidgetRadiobox.h index 6f69513c6..f5bb5ac0b 100644 --- a/src/ModuleBase/ModuleBase_WidgetRadiobox.h +++ b/src/ModuleBase/ModuleBase_WidgetRadiobox.h @@ -59,6 +59,7 @@ protected: private: QFormLayout* myLayout; QButtonGroup* myGroup; + bool myVerticalAlignment; }; #endif \ No newline at end of file diff --git a/src/PartSet/PartSet_SketcherMgr.cpp b/src/PartSet/PartSet_SketcherMgr.cpp index 48ba4156b..47f03b7d4 100644 --- a/src/PartSet/PartSet_SketcherMgr.cpp +++ b/src/PartSet/PartSet_SketcherMgr.cpp @@ -2269,7 +2269,7 @@ bool isIncludeToResult(const ObjectPtr& theObject) for (aIt = aRefsToMe.cbegin(); aIt != aRefsToMe.cend(); ++aIt) { if ((*aIt)->id() == SketchPlugin_Projection::PROJECTED_FEATURE_ID()) { FeaturePtr aFeature = std::dynamic_pointer_cast((*aIt)->owner()); - if (aFeature.get()) { + if (aFeature.get() && !aFeature->isMacro()) { anAttr = aFeature->data()->boolean(SketchPlugin_Projection::INCLUDE_INTO_RESULT()); if (anAttr.get()) return anAttr->value(); diff --git a/src/SketchAPI/SketchAPI.i b/src/SketchAPI/SketchAPI.i index e022d8f2d..91f9b55e2 100644 --- a/src/SketchAPI/SketchAPI.i +++ b/src/SketchAPI/SketchAPI.i @@ -49,9 +49,10 @@ %feature("kwargs") SketchAPI_BSpline::controlPolygon; %feature("kwargs") SketchAPI_Ellipse::construction; %feature("kwargs") SketchAPI_EllipticArc::construction; -%feature("kwargs") SketchAPI_Sketch::addSpline; -%feature("kwargs") SketchAPI_Sketch::addInterpolation; %feature("kwargs") SketchAPI_Sketch::addApproximation; +%feature("kwargs") SketchAPI_Sketch::addInterpolation; +%feature("kwargs") SketchAPI_Sketch::addProjection; +%feature("kwargs") SketchAPI_Sketch::addSpline; %feature("kwargs") SketchAPI_Sketch::setAngle; // shared pointers diff --git a/src/SketchAPI/SketchAPI_Projection.cpp b/src/SketchAPI/SketchAPI_Projection.cpp index 06c36fa66..8c833606b 100644 --- a/src/SketchAPI/SketchAPI_Projection.cpp +++ b/src/SketchAPI/SketchAPI_Projection.cpp @@ -56,16 +56,6 @@ SketchAPI_Projection::SketchAPI_Projection( } } -SketchAPI_Projection::SketchAPI_Projection( - const std::shared_ptr & theFeature, - const std::wstring & theExternalName) -: SketchAPI_SketchEntity(theFeature) -{ - if (initialize()) { - setByExternalName(theExternalName); - } -} - SketchAPI_Projection::~SketchAPI_Projection() { @@ -79,14 +69,18 @@ void SketchAPI_Projection::setExternalFeature(const ModelHighAPI_Selection & the execute(true); } -void SketchAPI_Projection::setByExternalName(const std::wstring& theExternalName) +void SketchAPI_Projection::setIncludeToResult(bool theKeepResult) { - setExternalFeature(ModelHighAPI_Selection("EDGE", theExternalName)); + fillAttribute(theKeepResult, includeToResult()); + execute(true); } -void SketchAPI_Projection::setIncludeToResult(bool theKeepResult) +void SketchAPI_Projection::setKeepReferenceToOriginal(bool theKeepRefToOriginal) { - fillAttribute(theKeepResult, includeToResult()); + // the Fixed constraint should be assigned explicitly + fillAttribute(false, feature()->boolean(SketchPlugin_Projection::MAKE_FIXED())); + fillAttribute(theKeepRefToOriginal ? "true" : "false", + feature()->string(SketchPlugin_Projection::KEEP_REFERENCE_ID())); execute(true); } diff --git a/src/SketchAPI/SketchAPI_Projection.h b/src/SketchAPI/SketchAPI_Projection.h index 3a16dbc4a..6ceb48501 100644 --- a/src/SketchAPI/SketchAPI_Projection.h +++ b/src/SketchAPI/SketchAPI_Projection.h @@ -43,10 +43,6 @@ public: SKETCHAPI_EXPORT SketchAPI_Projection(const std::shared_ptr & theFeature, const ModelHighAPI_Selection & theExternalFeature); - /// Constructor with values - SKETCHAPI_EXPORT - SketchAPI_Projection(const std::shared_ptr & theFeature, - const std::wstring & theExternalName); /// Destructor SKETCHAPI_EXPORT virtual ~SketchAPI_Projection(); @@ -66,14 +62,14 @@ public: SKETCHAPI_EXPORT void setExternalFeature(const ModelHighAPI_Selection & theExternalLine); - /// Set by external name - SKETCHAPI_EXPORT - void setByExternalName(const std::wstring & theExternalName); - /// Set flag to include projection to result or not SKETCHAPI_EXPORT void setIncludeToResult(bool theKeepResult); + /// Set flag to keep the reference to the original shape + SKETCHAPI_EXPORT + void setKeepReferenceToOriginal(bool theKeepRefToOriginal); + /// Returns created feature SKETCHAPI_EXPORT std::shared_ptr createdFeature() const; diff --git a/src/SketchAPI/SketchAPI_Sketch.cpp b/src/SketchAPI/SketchAPI_Sketch.cpp index 69462b8cc..8d47286ef 100644 --- a/src/SketchAPI/SketchAPI_Sketch.cpp +++ b/src/SketchAPI/SketchAPI_Sketch.cpp @@ -861,23 +861,15 @@ std::shared_ptr SketchAPI_Sketch::addApproximation( //-------------------------------------------------------------------------------------- std::shared_ptr SketchAPI_Sketch::addProjection( const ModelHighAPI_Selection & theExternalFeature, - bool theKeepResult) -{ - std::shared_ptr aFeature = - compositeFeature()->addFeature(SketchPlugin_Projection::ID()); - ProjectionPtr aProjection(new SketchAPI_Projection(aFeature, theExternalFeature)); - aProjection->setIncludeToResult(theKeepResult); - return aProjection; -} - -std::shared_ptr SketchAPI_Sketch::addProjection( - const std::wstring & theExternalName, - bool theKeepResult) + bool keepResult, + bool keepRefToOriginal) { std::shared_ptr aFeature = compositeFeature()->addFeature(SketchPlugin_Projection::ID()); - ProjectionPtr aProjection(new SketchAPI_Projection(aFeature, theExternalName)); - aProjection->setIncludeToResult(theKeepResult); + ProjectionPtr aProjection(new SketchAPI_Projection(aFeature)); + aProjection->setIncludeToResult(keepResult); + aProjection->setKeepReferenceToOriginal(keepRefToOriginal); + aProjection->setExternalFeature(theExternalFeature); return aProjection; } diff --git a/src/SketchAPI/SketchAPI_Sketch.h b/src/SketchAPI/SketchAPI_Sketch.h index 9e6a2dc20..899a2aab7 100644 --- a/src/SketchAPI/SketchAPI_Sketch.h +++ b/src/SketchAPI/SketchAPI_Sketch.h @@ -358,12 +358,8 @@ public: SKETCHAPI_EXPORT std::shared_ptr addProjection( const ModelHighAPI_Selection & theExternalFeature, - bool theKeepResult = false); - - /// Add projection - SKETCHAPI_EXPORT - std::shared_ptr addProjection(const std::wstring & theExternalName, - bool theKeepResult = false); + bool keepResult = false, + bool keepRefToOriginal = true); /// Add mirror SKETCHAPI_EXPORT diff --git a/src/SketchAPI/Test/TestSketch.py b/src/SketchAPI/Test/TestSketch.py index d1531e2bb..9718c80b2 100644 --- a/src/SketchAPI/Test/TestSketch.py +++ b/src/SketchAPI/Test/TestSketch.py @@ -173,7 +173,7 @@ class SketchTestCase(unittest.TestCase): def test_arc_by_projection(self): """ Test 10. Create arc by projection of external feature """ - self.sketch.addProjection("[Cylinder_2_1/Face_1][Cylinder_2_1/Face_3]") + self.sketch.addProjection(model.selection("EDGE", "[Cylinder_2_1/Face_1][Cylinder_2_1/Face_3]")) model.do() anArc = SketchAPI.SketchAPI_Arc(model.lastSubFeature(self.sketch, "SketchArc")) diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 401570245..835e53341 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -338,6 +338,7 @@ ADD_UNIT_TESTS( TestProjectionEllipticArc.py TestProjectionIntoResult.py TestProjectionUpdate.py + TestProjectionWithoutReference.py TestRectangle.py TestRemainingDoF.py TestRemoveBSpline.py diff --git a/src/SketchPlugin/SketchPlugin_Ellipse.cpp b/src/SketchPlugin/SketchPlugin_Ellipse.cpp index 098951a85..c35582c25 100644 --- a/src/SketchPlugin/SketchPlugin_Ellipse.cpp +++ b/src/SketchPlugin/SketchPlugin_Ellipse.cpp @@ -134,7 +134,7 @@ bool SketchPlugin_Ellipse::fillCharacteristicPoints() return false; } - data()->blockSendAttributeUpdated(true); + bool aWasBlocked = data()->blockSendAttributeUpdated(true); GeomPnt2dPtr aCenter2d = aCenterAttr->pnt(); GeomPnt2dPtr aFocus2d = aFocusAttr->pnt(); GeomDir2dPtr aMajorDir2d(new GeomAPI_Dir2d(aFocus2d->x() - aCenter2d->x(), @@ -144,7 +144,9 @@ bool SketchPlugin_Ellipse::fillCharacteristicPoints() AttributeDoublePtr aMajorRadiusAttr = real(MAJOR_RADIUS_ID()); double aFocalDist = aCenter2d->distance(aFocus2d); double aMajorRadius = sqrt(aFocalDist * aFocalDist + aMinorRadius * aMinorRadius); - aMajorRadiusAttr->setValue(aMajorRadius); + if (!aMajorRadiusAttr->isInitialized() || + fabs(aMajorRadiusAttr->value() - aMajorRadius) > tolerance) + aMajorRadiusAttr->setValue(aMajorRadius); std::dynamic_pointer_cast(attribute(SECOND_FOCUS_ID())) ->setValue(2.0 * aCenter2d->x() - aFocus2d->x(), 2.0 * aCenter2d->y() - aFocus2d->y()); @@ -160,7 +162,7 @@ bool SketchPlugin_Ellipse::fillCharacteristicPoints() std::dynamic_pointer_cast(attribute(MINOR_AXIS_END_ID())) ->setValue(aCenter2d->x() + aMinorDir2d->x() * aMinorRadius, aCenter2d->y() + aMinorDir2d->y() * aMinorRadius); - data()->blockSendAttributeUpdated(false); + data()->blockSendAttributeUpdated(aWasBlocked); return true; } diff --git a/src/SketchPlugin/SketchPlugin_Projection.cpp b/src/SketchPlugin/SketchPlugin_Projection.cpp index 5cb9740c1..c8b002248 100644 --- a/src/SketchPlugin/SketchPlugin_Projection.cpp +++ b/src/SketchPlugin/SketchPlugin_Projection.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,12 @@ #include static const double tolerance = 1.e-7; +static const std::string THE_KEEP_REF("true"); + +static bool isKeepReference(AttributeStringPtr theAttr) +{ + return !theAttr || !theAttr->isInitialized() || theAttr->value() == THE_KEEP_REF; +} SketchPlugin_Projection::SketchPlugin_Projection() @@ -71,6 +78,7 @@ void SketchPlugin_Projection::initDerivedClassAttributes() { data()->addAttribute(EXTERNAL_FEATURE_ID(), ModelAPI_AttributeSelection::typeId()); data()->addAttribute(PROJECTED_FEATURE_ID(), ModelAPI_AttributeRefAttr::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PROJECTED_FEATURE_ID()); data()->attribute(PROJECTED_FEATURE_ID())->setIsArgument(false); data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId()); @@ -81,6 +89,22 @@ void SketchPlugin_Projection::initDerivedClassAttributes() ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), AUXILIARY_ID()); } +void SketchPlugin_Projection::initDerivedClassAttributes2() +{ + AttributePtr aKeepRefAttr = + data()->addAttribute(KEEP_REFERENCE_ID(), ModelAPI_AttributeString::typeId()); + if (!aKeepRefAttr->isInitialized()) { + std::dynamic_pointer_cast(aKeepRefAttr)->setValue(THE_KEEP_REF); + } + + data()->addAttribute(MAKE_FIXED(), ModelAPI_AttributeBoolean::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), MAKE_FIXED()); + + data()->addAttribute(FIXED_CONSTRAINT_ID(), ModelAPI_AttributeReference::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), FIXED_CONSTRAINT_ID()); + data()->attribute(FIXED_CONSTRAINT_ID())->setIsArgument(false); +} + void SketchPlugin_Projection::execute() { AttributeRefAttrPtr aRefAttr = data()->refattr(PROJECTED_FEATURE_ID()); @@ -88,7 +112,7 @@ void SketchPlugin_Projection::execute() return; FeaturePtr aProjection = ModelAPI_Feature::feature(aRefAttr->object()); - if (!lastResult().get()) { + if (isKeepReference(string(KEEP_REFERENCE_ID())) && !lastResult().get()) { bool hasProjResult = aProjection->lastResult().get() != NULL; ResultConstructionPtr aConstr = document()->createConstruction(data()); if (hasProjResult) @@ -106,6 +130,16 @@ void SketchPlugin_Projection::execute() computeProjection(EXTERNAL_FEATURE_ID()); } +bool SketchPlugin_Projection::isMacro() const +{ + if (!data() || !data()->isValid()) + return false; + + AttributeStringPtr aKeepRefAttr = + const_cast(this)->string(KEEP_REFERENCE_ID()); + return !isKeepReference(aKeepRefAttr); +} + void SketchPlugin_Projection::attributeChanged(const std::string& theID) { if ((theID == EXTERNAL_FEATURE_ID() || theID == EXTERNAL_ID()) && !myIsComputing) { @@ -233,25 +267,52 @@ void SketchPlugin_Projection::computeProjection(const std::string& theID) if (!isProjected) return; // projection is not computed, stop processing - aProjection->boolean(COPY_ID())->setValue(true); + bool keepBackRef = isKeepReference(string(KEEP_REFERENCE_ID())); + + aProjection->boolean(COPY_ID())->setValue(keepBackRef); aProjection->execute(); aRefAttr->setObject(aProjection); restoreCurrentFeature(); - if (theID == EXTERNAL_FEATURE_ID()) { - selection(EXTERNAL_ID())->selectValue(aExtFeature); + AttributeBooleanPtr aMakeFixedAttr = boolean(MAKE_FIXED()); + bool isMakeFixed = aMakeFixedAttr && aMakeFixedAttr->isInitialized() && aMakeFixedAttr->value(); + + AttributeReferencePtr aFixedConstrAttr = reference(FIXED_CONSTRAINT_ID()); + FeaturePtr aFixedConstraint; + if (aFixedConstrAttr && aFixedConstrAttr->isInitialized()) + aFixedConstraint = ModelAPI_Feature::feature(aFixedConstrAttr->value()); - if (aResult) { - aResult->setShape(aProjection->lastResult()->shape()); - setResult(aResult); - GeomShapePtr anEmptyVal; - aProjection->selection(EXTERNAL_ID())->setValue(lastResult(), anEmptyVal); + if (keepBackRef) { + if (theID == EXTERNAL_FEATURE_ID()) { + selection(EXTERNAL_ID())->selectValue(aExtFeature); - static const Events_ID anEvent = Events_Loop::eventByName(EVENT_VISUAL_ATTRIBUTES); - ModelAPI_EventCreator::get()->sendUpdated(aProjection, anEvent, false); + if (aResult) { + aResult->setShape(aProjection->lastResult()->shape()); + setResult(aResult); + GeomShapePtr anEmptyVal; + aProjection->selection(EXTERNAL_ID())->setValue(lastResult(), anEmptyVal); + } } } + else if (isMakeFixed) { + // fix the projected entity with the Fixed constraint + if (!aFixedConstraint) + aFixedConstraint = sketch()->addFeature(SketchPlugin_ConstraintRigid::ID()); + aFixedConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setObject( + aProjection->lastResult()); + } + + + // remove Fixed constraint in case of redundance + if (aFixedConstraint && (keepBackRef || !isMakeFixed)) { + document()->removeFeature(aFixedConstraint); + aFixedConstraint = FeaturePtr(); + } + aFixedConstrAttr->setValue(aFixedConstraint); + + static const Events_ID anEvent = Events_Loop::eventByName(EVENT_VISUAL_ATTRIBUTES); + ModelAPI_EventCreator::get()->sendUpdated(aProjection, anEvent, false); } bool SketchPlugin_Projection::rebuildProjectedFeature( diff --git a/src/SketchPlugin/SketchPlugin_Projection.h b/src/SketchPlugin/SketchPlugin_Projection.h index b820daf99..4c41efb6c 100644 --- a/src/SketchPlugin/SketchPlugin_Projection.h +++ b/src/SketchPlugin/SketchPlugin_Projection.h @@ -62,6 +62,24 @@ public: return MY_INCLUDE; } + static const std::string& KEEP_REFERENCE_ID() + { + static std::string ID("keep_reference"); + return ID; + } + + static const std::string& MAKE_FIXED() + { + static std::string ID("make_fixed"); + return ID; + } + + static const std::string& FIXED_CONSTRAINT_ID() + { + static std::string ID("fixed_constraint"); + return ID; + } + /// Returns true because projected feature is always external virtual bool isFixed() { return true; } @@ -79,6 +97,11 @@ public: /// Called on change of any argument-attribute of this object: for external point SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + /// Returns true if this feature is used as macro: creates other features and then removed. + /// This feature may change its macro-state according to selected item. + /// \returns false by default + SKETCHPLUGIN_EXPORT virtual bool isMacro() const; + /// Use plugin manager for features creation SketchPlugin_Projection(); @@ -86,6 +109,9 @@ protected: /// \brief Initializes attributes of derived class. virtual void initDerivedClassAttributes(); + /// \brief Initializes attributes of keeping the reference to the original shape. + virtual void initDerivedClassAttributes2(); + private: /// \brief Find projection of a feature onto sketch plane void computeProjection(const std::string& theID); diff --git a/src/SketchPlugin/SketchPlugin_SketchEntity.cpp b/src/SketchPlugin/SketchPlugin_SketchEntity.cpp index 89d70db0d..3677bb174 100644 --- a/src/SketchPlugin/SketchPlugin_SketchEntity.cpp +++ b/src/SketchPlugin/SketchPlugin_SketchEntity.cpp @@ -43,4 +43,7 @@ void SketchPlugin_SketchEntity::initAttributes() anAttr = data()->addAttribute(PARENT_ID(), ModelAPI_AttributeReference::typeId()); anAttr->setIsArgument(false); ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PARENT_ID()); + + // initialize the rest of attributes + initDerivedClassAttributes2(); } diff --git a/src/SketchPlugin/SketchPlugin_SketchEntity.h b/src/SketchPlugin/SketchPlugin_SketchEntity.h index 4d85894e5..a705f2608 100644 --- a/src/SketchPlugin/SketchPlugin_SketchEntity.h +++ b/src/SketchPlugin/SketchPlugin_SketchEntity.h @@ -227,6 +227,9 @@ protected: /// \brief Initializes attributes of derived class. virtual void initDerivedClassAttributes(){}; + /// \brief Initializes attributes of derived class which were added recently. + virtual void initDerivedClassAttributes2(){}; + }; #endif diff --git a/src/SketchPlugin/SketchPlugin_Validators.cpp b/src/SketchPlugin/SketchPlugin_Validators.cpp index 4ddedd519..1727756d6 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.cpp +++ b/src/SketchPlugin/SketchPlugin_Validators.cpp @@ -1263,7 +1263,7 @@ bool SketchPlugin_ProjectionValidator::isValid(const AttributePtr& theAttribute, std::shared_ptr anEllipse = anEdge->ellipse(); std::shared_ptr anEllipseNormal = anEllipse->normal(); double aDot = fabs(aNormal->dot(anEllipseNormal)); - aValid = fabs(aDot - 1.0) <= tolerance * tolerance; + aValid = aDot >= tolerance * tolerance; if (!aValid) theError.arg(anEdge->isClosed() ? "Error: Ellipse is orthogonal to the sketch plane." : "Error: Elliptic Arc is orthogonal to the sketch plane."); diff --git a/src/SketchPlugin/SketchPlugin_msg_en.ts b/src/SketchPlugin/SketchPlugin_msg_en.ts index aad453f54..6ee4690a4 100644 --- a/src/SketchPlugin/SketchPlugin_msg_en.ts +++ b/src/SketchPlugin/SketchPlugin_msg_en.ts @@ -2220,7 +2220,66 @@ Edges in selected point has tangent constraint - + + + SketchProjection + + Project feature onto sketch plane + Project feature onto sketch plane + + + Projection + Projection + + + + SketchProjection:ExternalFeature + + Attribute "%1" is not initialized. + Select external edge or vertex. + + + Object + Object + + + Select external edge or vertex. + Select external edge or vertex. + + + + SketchProjection:IncludeToResult + + Include into the sketch result + Include into the sketch result + + + Include projected feature into the sketch result + Include projected feature into the sketch result + + + + SketchProjection:keep_reference + + A sketch entity will be created without connection to the selected shape. + A sketch entity will be created without connection to the selected shape. + + + The reference to the original curve is stored. So it can be changed later. + The reference to the original curve is stored. So it can be changed later. + + + + SketchProjection:make_fixed + + Assign the Fixed constraint to the result of projection + Assign the Fixed constraint to the result of projection + + + Make projected curve fixed + Make projected curve fixed + + SketchProjection:ExternalFeature:SketchPlugin_ProjectionValidator @@ -2264,13 +2323,6 @@ Error: Selected object is not supported for projection. - - SketchProjection:Model_FeatureValidator - - Attribute "%1" is not initialized. - - - diff --git a/src/SketchPlugin/SketchPlugin_msg_fr.ts b/src/SketchPlugin/SketchPlugin_msg_fr.ts index d509191c4..74e559170 100644 --- a/src/SketchPlugin/SketchPlugin_msg_fr.ts +++ b/src/SketchPlugin/SketchPlugin_msg_fr.ts @@ -2118,76 +2118,6 @@ - - SketchProjection:ExternalFeature:SketchPlugin_ProjectionValidator - - The attribute with the %1 type is not processed - Un argument de type %1 de la fonctionnalité de projection n'est pas pris en charge - - - The attribute %1 should be an edge - L'attribut %1 doit être une arête - - - There is no sketch referring to the current feature - La fonction de projection n'a pas d'esquisse - - - The attribute %1 should be an edge or vertex - L'élément projeté doit être une arête ou un sommet - - - Unable to project feature from the same sketch - Les fonctions de l'esquisse en cours ne peuvent pas être projetées - - - Error: Line is orthogonal to the sketch plane. - Erreur: La ligne est orthogonale au plan d'esquisse. - - - Error: Circle is orthogonal to the sketch plane. - Erreur: Le cercle est orthogonal au plan d'esquisse. - - - Error: Arc is orthogonal to the sketch plane. - Erreur: L'arc est orthogonal au plan d'esquisse. - - - Error: Ellipse is orthogonal to the sketch plane. - Erreur: L'ellipse est orthogonale au plan d'esquisse. - - - Error: Elliptic Arc is orthogonal to the sketch plane. - Erreur: L'arc d'ellipse est orthogonal au plan d'esquisse. - - - Error: Selected object is not supported for projection. - Erreur: L'objet sélectionné n'est pas pris en charge pour la projection. - - - - SketchProjection:Model_FeatureValidator - - Attribute "%1" is not initialized. - L'attribut "%1" n'est pas initialisé. - - - - SketchProjection:ExternalFeature - - Attribute "%1" is not initialized. - L'attribut "%1" n'est pas initialisé. - - - Object - Objet - - - Select external edge or vertex. - Sélectionnez une arête externe ou un sommet. - - - Sketch @@ -3989,6 +3919,97 @@ Inclure la fonctionnalité projetée dans le résultat de l'esquisse + + SketchProjection:keep_reference + + A sketch entity will be created without connection to the selected shape. + Une entité d'esquisse sera créée sans connexion à la forme sélectionnée. + + + The reference to the original curve is stored. So it can be changed later. + La référence à la courbe d'origine est stockée. Elle peut donc être modifiée ultérieurement. + + + + SketchProjection:make_fixed + + Assign the Fixed constraint to the result of projection + Affectez la contrainte Fixe au résultat de la projection + + + Make projected curve fixed + Fixer la courbe projetée + + + + SketchProjection:ExternalFeature:SketchPlugin_ProjectionValidator + + The attribute with the %1 type is not processed + Un argument de type %1 de la fonctionnalité de projection n'est pas pris en charge + + + The attribute %1 should be an edge + L'attribut %1 doit être une arête + + + There is no sketch referring to the current feature + La fonction de projection n'a pas d'esquisse + + + The attribute %1 should be an edge or vertex + L'élément projeté doit être une arête ou un sommet + + + Unable to project feature from the same sketch + Les fonctions de l'esquisse en cours ne peuvent pas être projetées + + + Error: Line is orthogonal to the sketch plane. + Erreur: La ligne est orthogonale au plan d'esquisse. + + + Error: Circle is orthogonal to the sketch plane. + Erreur: Le cercle est orthogonal au plan d'esquisse. + + + Error: Arc is orthogonal to the sketch plane. + Erreur: L'arc est orthogonal au plan d'esquisse. + + + Error: Ellipse is orthogonal to the sketch plane. + Erreur: L'ellipse est orthogonale au plan d'esquisse. + + + Error: Elliptic Arc is orthogonal to the sketch plane. + Erreur: L'arc d'ellipse est orthogonal au plan d'esquisse. + + + Error: Selected object is not supported for projection. + Erreur: L'objet sélectionné n'est pas pris en charge pour la projection. + + + + SketchProjection:Model_FeatureValidator + + Attribute "%1" is not initialized. + L'attribut "%1" n'est pas initialisé. + + + + SketchProjection:ExternalFeature + + Attribute "%1" is not initialized. + Sélectionnez une arête externe ou un sommet. + + + Object + Objet + + + Select external edge or vertex. + Sélectionnez une arête externe ou un sommet. + + SketchRectangle diff --git a/src/SketchPlugin/Test/TestProjectionEllipse.py b/src/SketchPlugin/Test/TestProjectionEllipse.py index c6a97d98d..2ee5b71ff 100644 --- a/src/SketchPlugin/Test/TestProjectionEllipse.py +++ b/src/SketchPlugin/Test/TestProjectionEllipse.py @@ -49,12 +49,11 @@ model.end() from GeomAPI import * +circle = SketchCircle_2.results()[-1].resultSubShapePair()[0].shape() +assert(circle.isEdge() and circle.edge().isCircle()) ellipse1 = SketchEllipse_1.results()[-1].resultSubShapePair()[0].shape() assert(ellipse1.isEdge() and ellipse1.edge().isEllipse()) ellipse2 = SketchEllipse_2.results()[-1].resultSubShapePair()[0].shape() assert(ellipse2.isEdge() and ellipse2.edge().isEllipse()) -# TODO [limitation]: projection of an ellipse to non-parallel plane is forbiden (OCCT issue #31016) -assert(Sketch_2.feature().error() != "") - assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestProjectionWithoutReference.py b/src/SketchPlugin/Test/TestProjectionWithoutReference.py new file mode 100644 index 000000000..82e5f5424 --- /dev/null +++ b/src/SketchPlugin/Test/TestProjectionWithoutReference.py @@ -0,0 +1,146 @@ +# 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 +# + +""" + Test projection without the reference to the source geometry. +""" + +import unittest + +from salome.shaper import model +from GeomAPI import * +from ModelAPI import * + +__updated__ = "2020-07-07" + +CURVES = [] +PLANE = None + +class TestProjectionWithoutRef(unittest.TestCase): + def setUp(self): + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(partSet, model.selection("FACE", PLANE.name())) + self.myDOF = 0 + self.myNbPoints = 1 + self.myNbLines = 1 + self.myNbCircles = 0 + self.myNbArcs = 0 + self.myNbEllipses = 2 + self.myNbEllipticArcs = 2 + self.myNbSplines = 1 + self.myNbPeriodicSplines = 1 + self.myNbProjections = 0 + self.myNbFixedConstraints = 0 + self.myNbEdgesInSketch = 0 + + def tearDown(self): + self.checkDOF() + model.end() + model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints) + model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines) + model.testNbSubFeatures(self.mySketch, "SketchCircle", self.myNbCircles) + model.testNbSubFeatures(self.mySketch, "SketchArc", self.myNbArcs) + model.testNbSubFeatures(self.mySketch, "SketchEllipse", self.myNbEllipses) + model.testNbSubFeatures(self.mySketch, "SketchEllipticArc", self.myNbEllipticArcs) + model.testNbSubFeatures(self.mySketch, "SketchBSpline", self.myNbSplines) + model.testNbSubFeatures(self.mySketch, "SketchBSplinePeriodic", self.myNbPeriodicSplines) + model.testNbSubFeatures(self.mySketch, "SketchProjection", self.myNbProjections) + model.testNbSubFeatures(self.mySketch, "SketchConstraintRigid", self.myNbFixedConstraints) + nbEdges = 0 + exp = GeomAPI_ShapeExplorer(self.mySketch.defaultResult().shape(), GeomAPI_Shape.EDGE) + while exp.more(): nbEdges += 1; exp.next() + self.assertEqual(self.myNbEdgesInSketch, nbEdges) + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + + def test_projection_withref_includeintoresult(self): + """ Test 1. Projection with the reference to the original shapes. Projected curves are composed into the sketch result. + """ + for c in CURVES: + self.mySketch.addProjection(c, keepResult = True) + self.myNbProjections = len(CURVES) + self.myNbEdgesInSketch = len(CURVES) - 1 + + def test_projection_withref_notincludeintoresult(self): + """ Test 2. Projection with the reference to the original shapes. Projected curves are NOT included into the sketch result. + """ + for c in CURVES: + self.mySketch.addProjection(c, keepResult = False) + self.myNbProjections = len(CURVES) + + def test_projection_withoutref_noconstraints(self): + """ Test 3. Projection without the reference to the original shapes. No additional constraints applied. + """ + for c in CURVES: + self.mySketch.addProjection(c, keepRefToOriginal = False) + model.do() + self.myNbEdgesInSketch = len(CURVES) - 1 + self.myDOF += 2 + 4 + 5 + 7 + 5 + 7 + 6 * 2 + 6 * 2 + + def test_projection_withoutref_fixed(self): + """ Test 4. Projection without the reference to the original shapes. Additionally, Fixed constraints applied. + """ + model.end() + # use the low-level API to access the necessary attributes + session = ModelAPI_Session.get() + for c in CURVES: + session.startOperation() + proj = featureToCompositeFeature(self.mySketch.feature()).addFeature("SketchProjection") + proj.boolean("IncludeToResult").setValue(False) + proj.string("keep_reference").setValue("False") + proj.boolean("make_fixed").setValue(True) + c.fillAttribute(proj.selection("ExternalFeature")) + session.finishOperation() + self.myNbEdgesInSketch = len(CURVES) - 1 + self.myNbFixedConstraints = len(CURVES) + model.begin() + + +if __name__ == "__main__": + model.begin() + partSet = model.moduleDocument() + Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY")) + SketchPoint_1 = Sketch_1.addPoint(35, -40) + CURVES.append(model.selection("VERTEX", Sketch_1.name() + "/" + SketchPoint_1.name())) + SketchLine_1 = Sketch_1.addLine(20, -15, 40, 15) + CURVES.append(model.selection("EDGE", Sketch_1.name() + "/" + SketchLine_1.name())) + SketchCircle_1 = Sketch_1.addCircle(65, -30, 20) + CURVES.append(model.selection("EDGE", Sketch_1.name() + "/" + SketchCircle_1.defaultResult().data().name())) + SketchArc_1 = Sketch_1.addArc(60, 15, 80, 0, 50, 33, False) + CURVES.append(model.selection("EDGE", Sketch_1.name() + "/" + SketchArc_1.defaultResult().data().name())) + SketchEllipse_1 = Sketch_1.addEllipse(25, 30, 40, 30, 10) + CURVES.append(model.selection("EDGE", Sketch_1.name() + "/" + SketchEllipse_1.defaultResult().data().name())) + SketchEllipticArc_1 = Sketch_1.addEllipticArc(40, 70, 55, 70, 45, 50, 25, 56, False) + CURVES.append(model.selection("EDGE", Sketch_1.name() + "/" + SketchEllipticArc_1.defaultResult().data().name())) + SketchBSpline_1_poles = [(95, -50), (130, -10), (100, 10), (125, 45), (90, 70), (55, 45)] + SketchBSpline_1 = Sketch_1.addSpline(poles = SketchBSpline_1_poles) + CURVES.append(model.selection("EDGE", Sketch_1.name() + "/" + SketchBSpline_1.name())) + SketchBSplinePeriodic_1_poles = [(95, 80), (135, 90), (145, 55), (130, 30), (125, 70), (105, 60)] + SketchBSplinePeriodic_1 = Sketch_1.addSpline(poles = SketchBSplinePeriodic_1_poles, periodic = True) + CURVES.append(model.selection("EDGE", Sketch_1.name() + "/" + SketchBSplinePeriodic_1.name())) + model.do() + PLANE = model.addPlane(partSet, model.selection("FACE", "XOY"), model.selection("EDGE", "OY"), 45) + model.end() + + test_program = unittest.main(exit=False) + assert test_program.result.wasSuccessful(), "Test failed" + assert model.checkPythonDump() diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index ef62acfca..f5f6f3f31 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -813,8 +813,26 @@ use_sketch_plane="false"> - + + + + + + + + diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp index 7f936524c..1835e475c 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp @@ -114,7 +114,7 @@ static bool hasReference(std::shared_ptr theFeature, for (std::set::const_iterator aRefIt = aRefs.begin(); aRefIt != aRefs.end(); ++aRefIt) { FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner()); - if (anOwner && anOwner->getKind() == theFeatureKind) + if (anOwner && !anOwner->isMacro() && anOwner->getKind() == theFeatureKind) return true; } return false; -- 2.39.2