From: azv Date: Thu, 3 Oct 2019 11:53:38 +0000 (+0300) Subject: Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003) X-Git-Tag: V9_4_0a2~4^2~22 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=96b1f1a82347863f2940db75e39c6ed11fc75654;p=modules%2Fshaper.git Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003) Constraint Perpendicular for elliptic arc and line --- diff --git a/src/SketchSolver/CMakeLists.txt b/src/SketchSolver/CMakeLists.txt index a543a7014..fafa0372a 100644 --- a/src/SketchSolver/CMakeLists.txt +++ b/src/SketchSolver/CMakeLists.txt @@ -50,6 +50,7 @@ SET(SKETCHSOLVER_CONSTRAINT_HEADERS SketchSolver_ConstraintMiddle.h SketchSolver_ConstraintMirror.h SketchSolver_ConstraintFixed.h + SketchSolver_ConstraintPerpendicular.h SketchSolver_ConstraintTangent.h SketchSolver_ConstraintMulti.h SketchSolver_ConstraintMultiRotation.h @@ -74,6 +75,7 @@ SET(SKETCHSOLVER_CONSTRAINT_SOURCES SketchSolver_ConstraintMiddle.cpp SketchSolver_ConstraintMirror.cpp SketchSolver_ConstraintFixed.cpp + SketchSolver_ConstraintPerpendicular.cpp SketchSolver_ConstraintTangent.cpp SketchSolver_ConstraintMulti.cpp SketchSolver_ConstraintMultiRotation.cpp diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h index fc543f5b0..0f46e7544 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h @@ -74,6 +74,7 @@ enum SketchSolver_ConstraintType { CONSTRAINT_VERTICAL, CONSTRAINT_PARALLEL, CONSTRAINT_PERPENDICULAR, + CONSTRAINT_PERPENDICULAR_CURVES, CONSTRAINT_SYMMETRIC, CONSTRAINT_EQUAL, // base equality if we don't know the measured objects yet CONSTRAINT_EQUAL_LINES, diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp index ccc07b05b..a13a7b891 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -119,6 +121,11 @@ static ConstraintWrapperPtr createConstraintMiddlePoint(std::shared_ptr thePoint, std::shared_ptr theEntity, std::shared_ptr theAuxParameters); +static ConstraintWrapperPtr + createConstraintAngleBetweenCurves(std::shared_ptr theValue, + std::shared_ptr thePoint, + std::shared_ptr theEntity1, + std::shared_ptr theEntity2); static GCS::SET_pD scalarParameters(const ScalarWrapperPtr& theScalar); static GCS::SET_pD pointParameters(const PointWrapperPtr& thePoint); @@ -163,6 +170,8 @@ SolverConstraintPtr PlaneGCSSolver_Tools::createConstraint(ConstraintPtr theCons return SolverConstraintPtr(new SketchSolver_ConstraintMultiRotation(theConstraint)); } else if (theConstraint->getKind() == SketchPlugin_ConstraintAngle::ID()) { return SolverConstraintPtr(new SketchSolver_ConstraintAngle(theConstraint)); + } else if (theConstraint->getKind() == SketchPlugin_ConstraintPerpendicular::ID()) { + return SolverConstraintPtr(new SketchSolver_ConstraintPerpendicular(theConstraint)); } // All other types of constraints return SolverConstraintPtr(new SketchSolver_Constraint(theConstraint)); @@ -243,6 +252,10 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint( case CONSTRAINT_PERPENDICULAR: aResult = createConstraintPerpendicular(anEntity1, GCS_EDGE_WRAPPER(theEntity2)); break; + case CONSTRAINT_PERPENDICULAR_CURVES: + aResult = createConstraintAngleBetweenCurves(GCS_SCALAR_WRAPPER(theValue), + aPoint1, anEntity1, GCS_EDGE_WRAPPER(theEntity2)); + break; case CONSTRAINT_EQUAL_LINES: case CONSTRAINT_EQUAL_ELLIPSES: anIntermediate = GCS_SCALAR_WRAPPER(theValue); // parameter is used to store length of lines @@ -663,6 +676,18 @@ ConstraintWrapperPtr createConstraintPerpendicular( new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_PERPENDICULAR)); } +ConstraintWrapperPtr createConstraintAngleBetweenCurves( + std::shared_ptr theValue, + std::shared_ptr thePoint, + std::shared_ptr theEntity1, + std::shared_ptr theEntity2) +{ + GCSConstraintPtr aNewConstr(new GCS::ConstraintAngleViaPoint( + *theEntity1->entity(), *theEntity2->entity(), *thePoint->point(), theValue->scalar())); + return ConstraintWrapperPtr( + new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_PERPENDICULAR_CURVES)); +} + ConstraintWrapperPtr createConstraintEqual( const SketchSolver_ConstraintType& theType, std::shared_ptr theEntity1, diff --git a/src/SketchSolver/SketchSolver_ConstraintPerpendicular.cpp b/src/SketchSolver/SketchSolver_ConstraintPerpendicular.cpp new file mode 100644 index 000000000..4109bb555 --- /dev/null +++ b/src/SketchSolver/SketchSolver_ConstraintPerpendicular.cpp @@ -0,0 +1,411 @@ +// Copyright (C) 2014-2019 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 +// + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast(x) +#define GCS_POINT_WRAPPER(x) std::dynamic_pointer_cast(x) + +/// \brief Obtain constrained features +static void getFeatures(const ConstraintPtr& theConstraint, + FeaturePtr& theFeature1, + FeaturePtr& theFeature2); + +/// \brief Check whether the entities has only one shared point or less. +/// Return list of coincident points. +static std::set coincidentBoundaryPoints(FeaturePtr theFeature1, + FeaturePtr theFeature2); + +/// \brief Collect points coincident with each of two features +static std::set coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2); + +static void calculateIntersectionPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2, + GCSPointPtr& theIntersectionPoint); + +/// sets angle to 90 or -90 degrees +static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1, + const GCSCurvePtr& theCurve2, + const GCSPointPtr& thePoint, + double* theAngle); + + +void SketchSolver_ConstraintPerpendicular::process() +{ + cleanErrorMsg(); + if (!myBaseConstraint || !myStorage) { + // Not enough parameters are assigned + return; + } + + EntityWrapperPtr aValue; + std::vector anAttributes; + SketchSolver_Constraint::getAttributes(aValue, anAttributes); + if (!myErrorMsg.empty()) + return; + + rebuild(); + if (!myErrorMsg.empty()) + return; + + myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP()); +} + +void SketchSolver_ConstraintPerpendicular::rebuild() +{ + if (mySolverConstraint) + myStorage->removeConstraint(myBaseConstraint); + + std::shared_ptr aStorage = + std::dynamic_pointer_cast(myStorage); + + mySolverConstraint = ConstraintWrapperPtr(); + mySharedPoint = AttributePtr(); + if (myAuxPoint) { + GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint); + aStorage->removeParameters(aParams); + myAuxPoint = EntityWrapperPtr(); + } + + // Check the quantity of entities of each type and their order (arcs first) + int aNbLines = 0; + int aNbCircles = 0; + int aNbOther = 0; + std::list::iterator anEntIt = myAttributes.begin(); + for (; anEntIt != myAttributes.end(); ++anEntIt) { + if (!(*anEntIt).get()) + continue; + if ((*anEntIt)->type() == ENTITY_LINE) + ++aNbLines; + else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE) + ++aNbCircles; + else if ((*anEntIt)->type() != ENTITY_POINT) + ++aNbOther; + } + + if (aNbLines + aNbCircles + aNbOther == 2) { + if (aNbOther > 0) + myType = CONSTRAINT_PERPENDICULAR_CURVES; + } + else { + myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE(); + return; + } + + FeaturePtr aFeature1, aFeature2; + getFeatures(myBaseConstraint, aFeature1, aFeature2); + + // check number of coincident points + std::set aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2); + // Try to find non-boundary points coincident with both features. + // It is necesasry to create perpendicularity with ellipse + if (aCoincidentPoints.empty() && aNbOther > 0) + aCoincidentPoints = coincidentPoints(aFeature1, aFeature2); + + EntityWrapperPtr aSharedPointEntity; + std::list anAuxConstraints; + if (!aCoincidentPoints.empty()) { + mySharedPoint = *aCoincidentPoints.begin(); + aSharedPointEntity = myStorage->entity(mySharedPoint); + } + else if (aNbOther > 0) { + // create auxiliary point + GCSPointPtr aPoint(new GCS::Point); + aPoint->x = aStorage->createParameter(); + aPoint->y = aStorage->createParameter(); + calculateIntersectionPoint(myAttributes.front(), myAttributes.back(), aPoint); + + myAuxPoint.reset(new PlaneGCSSolver_PointWrapper(aPoint)); + aSharedPointEntity = myAuxPoint; + + // create auxiliary coincident constraints for tangency with ellipse + EntityWrapperPtr aDummy; + ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(), + CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.front(), aDummy); + anAuxConstraints = aCoincidence->constraints(); + aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(), + CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.back(), aDummy); + anAuxConstraints.insert(anAuxConstraints.end(), + aCoincidence->constraints().begin(), aCoincidence->constraints().end()); + } + + ScalarWrapperPtr anAngleWrapper; + if (aSharedPointEntity) { + adjustAngleBetweenCurves(GCS_EDGE_WRAPPER(myAttributes.front())->entity(), + GCS_EDGE_WRAPPER(myAttributes.back())->entity(), + GCS_POINT_WRAPPER(aSharedPointEntity)->point(), &myCurveCurveAngle); + anAngleWrapper.reset(new PlaneGCSSolver_ScalarWrapper(&myCurveCurveAngle)); + } + + mySolverConstraint = PlaneGCSSolver_Tools::createConstraint(myBaseConstraint, myType, + anAngleWrapper, aSharedPointEntity, EntityWrapperPtr(), + myAttributes.front(), myAttributes.back()); + + if (!anAuxConstraints.empty()) { + anAuxConstraints.insert(anAuxConstraints.end(), mySolverConstraint->constraints().begin(), + mySolverConstraint->constraints().end()); + mySolverConstraint->setConstraints(anAuxConstraints); + } + + myStorage->addConstraint(myBaseConstraint, mySolverConstraint); +} + +void SketchSolver_ConstraintPerpendicular::notify(const FeaturePtr& theFeature, + PlaneGCSSolver_Update* theUpdater) +{ + if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID()) + return; + + // base constraint may become invalid (for example, during undo) + if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid()) + return; + + FeaturePtr aTgFeat1, aTgFeat2; + getFeatures(myBaseConstraint, aTgFeat1, aTgFeat2); + + bool isRebuild = false; + if (theFeature->data() && theFeature->data()->isValid()) { + // the constraint has been created + if (!mySharedPoint) { + // features has no shared point, check whether coincidence constraint binds these features) + int aNbCoincidentFeatures = 0; + for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) { + AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i)); + if (!aRefAttr) + continue; + + ObjectPtr anObj; + if (aRefAttr->isObject()) + anObj = aRefAttr->object(); + else + anObj = aRefAttr->attr()->owner(); + + FeaturePtr aFeature = ModelAPI_Feature::feature(anObj); + if (aFeature == aTgFeat1 || aFeature == aTgFeat2) + ++aNbCoincidentFeatures; + } + + if (aNbCoincidentFeatures == 2) + isRebuild = true; + } + } + + if (!isRebuild) { + if (mySharedPoint) { + // The features are tangent in the shared point, but the coincidence has been removed/updated. + // Check if the coincidence is the same. + std::set aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2); + isRebuild = true; + std::set::iterator anIt = aCoincidentPoints.begin(); + for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt) + if (*anIt == mySharedPoint) + isRebuild = false; // the coincidence is still exists => nothing to change + } + else { + // check both features have a coincident point + std::set aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2); + isRebuild = (bool)(myAuxPoint.get()) == (!aCoincidentPoints.empty()); + } + } + + if (isRebuild) + rebuild(); +} + + + + +// ================== Auxiliary functions ================================= +void getFeatures(const ConstraintPtr& theConstraint, + FeaturePtr& theFeature1, + FeaturePtr& theFeature2) +{ + AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A()); + theFeature1 = ModelAPI_Feature::feature(aRefAttr->object()); + aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B()); + theFeature2 = ModelAPI_Feature::feature(aRefAttr->object()); +} + +static std::set collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2) +{ + const std::set& aRefs1 = theFeature1->data()->refsToMe(); + const std::set& aRefs2 = theFeature2->data()->refsToMe(); + + std::set aCoincidences; + std::set::const_iterator anIt; + + // collect coincidences referred to the first feature + for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) { + FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner()); + if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID()) + aCoincidences.insert(aRef); + } + + // leave only coincidences referred to the second feature + std::set aCoincidencesBetweenFeatures; + for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) { + FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner()); + if (aCoincidences.find(aRef) != aCoincidences.end()) + aCoincidencesBetweenFeatures.insert(aRef); + } + + return aCoincidencesBetweenFeatures; +} + +std::set coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2) +{ + std::set aCoincidences = collectCoincidences(theFeature1, theFeature2); + // collect points only + std::set aCoincidentPoints; + std::set::const_iterator aCIt = aCoincidences.begin(); + for (; aCIt != aCoincidences.end(); ++ aCIt) { + AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A()); + AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B()); + if (!aRefAttrA || aRefAttrA->isObject() || + !aRefAttrB || aRefAttrB->isObject()) + continue; + + AttributePtr anAttrA = aRefAttrA->attr(); + AttributePtr anAttrB = aRefAttrB->attr(); + if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() && + anAttrA->id() != SketchPlugin_Circle::CENTER_ID() && + anAttrB->id() != SketchPlugin_Arc::CENTER_ID() && + anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) { + aCoincidentPoints.insert(anAttrA); + aCoincidentPoints.insert(anAttrB); + } + } + return aCoincidentPoints; +} + +static std::set refsToFeatureAndResults(FeaturePtr theFeature) +{ + std::set aRefs = theFeature->data()->refsToMe(); + const std::list& aResults = theFeature->results(); + for (std::list::const_iterator anIt = aResults.begin(); + anIt != aResults.end(); ++anIt) { + const std::set& aResRefs = (*anIt)->data()->refsToMe(); + aRefs.insert(aResRefs.begin(), aResRefs.end()); + } + return aRefs; +} + +// collect all points coincident with the feature +static std::set pointsOnFeature(FeaturePtr theFeature) +{ + std::set aPoints; + + std::set aRefs = refsToFeatureAndResults(theFeature); + for (std::set::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) { + FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner()); + if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() || + aRef->getKind() == SketchPlugin_ConstraintMiddle::ID())) { + for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) { + AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i)); + if (aRefAttr) { + AttributePtr anAttr = aRefAttr->attr(); + if (anAttr && anAttr->id() != SketchPlugin_Arc::CENTER_ID() && + anAttr->id() != SketchPlugin_Circle::CENTER_ID()) + aPoints.insert(anAttr); + } + } + } + } + return aPoints; +} + +std::set coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2) +{ + std::set aPointsOnF1 = pointsOnFeature(theFeature1); + std::set aPointsOnF2 = pointsOnFeature(theFeature2); + + std::set aCommonPoints; + for (std::set::iterator anIt = aPointsOnF1.begin(); + anIt != aPointsOnF1.end(); ++anIt) + if (aPointsOnF2.find(*anIt) != aPointsOnF2.end()) + aCommonPoints.insert(*anIt); + return aCommonPoints; +} + +void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1, + const GCSCurvePtr& theCurve2, + const GCSPointPtr& thePoint, + double* theAngle) +{ + double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint); + // bring angle to [-pi..pi] + if (anAngle > PI) anAngle -= 2.0 * PI; + if (anAngle < -PI) anAngle += 2.0 * PI; + // set angle value according to the current angle between curves + if (anAngle >= 0) + *theAngle = PI / 2.; + else + *theAngle = -PI / 2.; +} + +void calculateIntersectionPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2, + GCSPointPtr& theIntersectionPoint) +{ + std::shared_ptr anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve1); + EntityWrapperPtr aCurve2 = theCurve2; + if (!anEllipse) { + // try converting to ellipse the second curve + anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve2); + if (!anEllipse) + return; // no one curve is ellipse + aCurve2 = theCurve1; + } + + GeomPnt2dPtr aP1, aP2; + if (aCurve2->type() == ENTITY_LINE) { + std::shared_ptr aLine = PlaneGCSSolver_Tools::line(aCurve2); + anEllipse->distance(aLine, aP1, aP2); + } + else if (aCurve2->type() == ENTITY_ARC || aCurve2->type() == ENTITY_CIRCLE) { + std::shared_ptr aCircle = PlaneGCSSolver_Tools::circle(aCurve2); + anEllipse->distance(aCircle, aP1, aP2); + } + else if (aCurve2->type() == ENTITY_ELLIPSE || aCurve2->type() == ENTITY_ELLIPTIC_ARC) { + std::shared_ptr anEl2 = PlaneGCSSolver_Tools::ellipse(aCurve2); + anEllipse->distance(anEl2, aP1, aP2); + } + + if (aP1 && aP2) { + *theIntersectionPoint->x = 0.5 * (aP1->x() + aP2->x()); + *theIntersectionPoint->y = 0.5 * (aP1->y() + aP2->y()); + } +} diff --git a/src/SketchSolver/SketchSolver_ConstraintPerpendicular.h b/src/SketchSolver/SketchSolver_ConstraintPerpendicular.h new file mode 100644 index 000000000..822c7020f --- /dev/null +++ b/src/SketchSolver/SketchSolver_ConstraintPerpendicular.h @@ -0,0 +1,55 @@ +// Copyright (C) 2014-2019 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 +// + +#ifndef SketchSolver_ConstraintPerpendicular_H_ +#define SketchSolver_ConstraintPerpendicular_H_ + +#include + +/** \class SketchSolver_ConstraintPerpendicular + * \ingroup Plugins + * \brief Convert perpendicularity constraint to SolveSpace structure + */ +class SketchSolver_ConstraintPerpendicular : public SketchSolver_Constraint +{ +public: + /// Constructor based on SketchPlugin constraint + SketchSolver_ConstraintPerpendicular(ConstraintPtr theConstraint) : + SketchSolver_Constraint(theConstraint), + myCurveCurveAngle(0.0) + {} + + /// \brief Notify this object about the feature is changed somewhere + virtual void notify(const FeaturePtr& theFeature, + PlaneGCSSolver_Update* theUpdater); + +protected: + /// \brief Converts SketchPlugin constraint to a list of solver constraints + virtual void process(); + + /// \brief Remove current constraint from the storage and build is again + void rebuild(); + +private: + double myCurveCurveAngle; + AttributePtr mySharedPoint; + EntityWrapperPtr myAuxPoint; +}; + +#endif