assert ((aLineBStartPoint.x(), aLineBStartPoint.y()) == (aLineAEndPoint.x(), aLineAEndPoint.y()))
assert ((aLineCStartPoint.x(), aLineCStartPoint.y()) == (aLineBEndPoint.x(), aLineBEndPoint.y()))
assert (model.dof(aSketchFeature) == 6)
+
+#=========================================================================
+# Create circle, fix it and check the circle is not moved
+#=========================================================================
+aCenter = [10., 10.]
+aRadius = 5.
+aSession.startOperation()
+aCircle = aSketchFeature.addFeature("SketchCircle")
+aCircleCenter = geomDataAPI_Point2D(aCircle.attribute("circle_center"))
+aCircleRadius = aCircle.real("circle_radius")
+aCircleCenter.setValue(aCenter[0], aCenter[1])
+aCircleRadius.setValue(aRadius)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 9)
+# fixed constraints
+aSession.startOperation()
+aRigidConstraint = aSketchFeature.addFeature("SketchConstraintRigid")
+aRigidConstraint.refattr("ConstraintEntityA").setObject(aCircle.lastResult())
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 6)
+# move center of circle
+aSession.startOperation()
+aCircleCenter.setValue(aCenter[0] + 1., aCenter[1] - 1.)
+aSession.finishOperation()
+assert (aCircleCenter.x() == aCenter[0] and aCircleCenter.y() == aCenter[1])
+assert (aCircleRadius.value() == aRadius)
+assert (model.dof(aSketchFeature) == 6)
+# change radius of circle
+aSession.startOperation()
+aCircleRadius.setValue(aRadius + 3.)
+aSession.finishOperation()
+assert (aCircleCenter.x() == aCenter[0] and aCircleCenter.y() == aCenter[1])
+assert (aCircleRadius.value() == aRadius)
+assert (model.dof(aSketchFeature) == 6)
+
+#=========================================================================
+# Remove Fixed constraint and check the circle can be moved
+#=========================================================================
+aSession.startOperation()
+aDocument.removeFeature(aRigidConstraint)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 9)
+# move center of circle
+aCenter = [aCenter[0] + 1., aCenter[1] - 1.]
+aSession.startOperation()
+aCircleCenter.setValue(aCenter[0], aCenter[1])
+aSession.finishOperation()
+assert (aCircleCenter.x() == aCenter[0] and aCircleCenter.y() == aCenter[1])
+assert (aCircleRadius.value() == aRadius)
+assert (model.dof(aSketchFeature) == 9)
+# change radius of circle
+aRadius = aRadius + 3.
+aSession.startOperation()
+aCircleRadius.setValue(aRadius)
+aSession.finishOperation()
+assert (aCircleCenter.x() == aCenter[0] and aCircleCenter.y() == aCenter[1])
+assert (aCircleRadius.value() == aRadius)
+assert (model.dof(aSketchFeature) == 9)
+
+#=========================================================================
+# Create arc, fix it and check it is not moved
+#=========================================================================
+aCenter = [10., 10.]
+aStart = [5., 10.]
+aEnd = [10., 15.]
+aSession.startOperation()
+anArc = aSketchFeature.addFeature("SketchArc")
+anArcCenter = geomDataAPI_Point2D(anArc.attribute("center_point"))
+anArcStart = geomDataAPI_Point2D(anArc.attribute("start_point"))
+anArcEnd = geomDataAPI_Point2D(anArc.attribute("end_point"))
+anArcCenter.setValue(aCenter[0], aCenter[1])
+anArcStart.setValue(aStart[0], aStart[1])
+anArcEnd.setValue(aEnd[0], aEnd[1])
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 14)
+# fixed constraints
+aSession.startOperation()
+aRigidConstraint = aSketchFeature.addFeature("SketchConstraintRigid")
+aRigidConstraint.refattr("ConstraintEntityA").setObject(anArc.lastResult())
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 9)
+# move center of arc
+aSession.startOperation()
+anArcCenter.setValue(aCenter[0] + 1., aCenter[1] - 1.)
+aSession.finishOperation()
+assert (anArcCenter.x() == aCenter[0] and anArcCenter.y() == aCenter[1])
+assert (anArcStart.x() == aStart[0] and anArcStart.y() == aStart[1])
+assert (anArcEnd.x() == aEnd[0] and anArcEnd.y() == aEnd[1])
+assert (model.dof(aSketchFeature) == 9)
+# move start point of arc
+aSession.startOperation()
+anArcStart.setValue(aStart[0] + 1., aStart[1] - 1.)
+aSession.finishOperation()
+assert (anArcCenter.x() == aCenter[0] and anArcCenter.y() == aCenter[1])
+assert (anArcStart.x() == aStart[0] and anArcStart.y() == aStart[1])
+assert (anArcEnd.x() == aEnd[0] and anArcEnd.y() == aEnd[1])
+assert (model.dof(aSketchFeature) == 9)
+# move end point of arc
+aSession.startOperation()
+anArcEnd.setValue(aEnd[0] + 1., aEnd[1] - 1.)
+aSession.finishOperation()
+assert (anArcCenter.x() == aCenter[0] and anArcCenter.y() == aCenter[1])
+assert (anArcStart.x() == aStart[0] and anArcStart.y() == aStart[1])
+assert (anArcEnd.x() == aEnd[0] and anArcEnd.y() == aEnd[1])
+assert (model.dof(aSketchFeature) == 9)
+
+#=========================================================================
+# Remove Fixed constraint and check the arc can be moved
+#=========================================================================
+aSession.startOperation()
+aDocument.removeFeature(aRigidConstraint)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 14)
+# move center of arc
+aCenter = [anArcCenter.x(), anArcCenter.y()]
+aSession.startOperation()
+anArcCenter.setValue(aCenter[0] + 1., aCenter[1] - 1.)
+aSession.finishOperation()
+assert (anArcCenter.x() != aCenter[0] or anArcCenter.y() != aCenter[1])
+assert (model.dof(aSketchFeature) == 14)
+# move start point of arc
+aStart = [anArcStart.x(), anArcStart.y()]
+aSession.startOperation()
+anArcStart.setValue(aStart[0] + 1., aStart[1] - 1.)
+aSession.finishOperation()
+assert (anArcStart.x() != aStart[0] or anArcStart.y() != aStart[1])
+assert (model.dof(aSketchFeature) == 14)
+# move end point of arc
+aEnd = [anArcEnd.x(), anArcEnd.y()]
+aSession.startOperation()
+anArcEnd.setValue(aEnd[0] + 1., aEnd[1] - 1.)
+aSession.finishOperation()
+assert (anArcEnd.x() != aEnd[0] or anArcEnd.y() != aEnd[1])
+assert (model.dof(aSketchFeature) == 14)
+
#=========================================================================
# End of test
#=========================================================================
// Predefined values for identifiers
const ConstraintID CID_UNKNOWN = 0;
const ConstraintID CID_MOVEMENT = -1;
+const ConstraintID CID_FICTIVE = 1024;
/// Types of entities
enum SketchSolver_EntityType {
myDiagnoseBeforeSolve(false),
myInitilized(false),
myConfCollected(false),
- myDOF(0)
+ myDOF(0),
+ myFictiveConstraint(0)
{
}
myConstraints.clear();
myConflictingIDs.clear();
myDOF = 0;
+
+ removeFictiveConstraint();
}
void PlaneGCSSolver_Solver::addConstraint(GCSConstraintPtr theConstraint)
void PlaneGCSSolver_Solver::initialize()
{
Events_LongOp::start(this);
+ addFictiveConstraintIfNecessary();
if (myDiagnoseBeforeSolve)
diagnose();
myEquationSystem->declareUnknowns(myParameters);
if (myInitilized) {
aResult = (GCS::SolveStatus)myEquationSystem->solve();
} else {
+ addFictiveConstraintIfNecessary();
+
if (myDiagnoseBeforeSolve)
diagnose();
aResult = (GCS::SolveStatus)myEquationSystem->solve(myParameters);
aStatus = STATUS_OK;
}
+ removeFictiveConstraint();
myInitilized = false;
return aStatus;
}
myDOF = myEquationSystem->diagnose();
myDiagnoseBeforeSolve = false;
}
+
+void PlaneGCSSolver_Solver::addFictiveConstraintIfNecessary()
+{
+ if (!myConstraints.empty() &&
+ myConstraints.find(CID_MOVEMENT) == myConstraints.end())
+ return;
+
+ if (myFictiveConstraint)
+ return; // no need several fictive constraints
+
+ double* aParam = createParameter();
+ double* aFictiveParameter = new double(0.0);
+
+ myFictiveConstraint = new GCS::ConstraintEqual(aFictiveParameter, aParam);
+ myFictiveConstraint->setTag(CID_FICTIVE);
+ myEquationSystem->addConstraint(myFictiveConstraint);
+}
+
+void PlaneGCSSolver_Solver::removeFictiveConstraint()
+{
+ if (myFictiveConstraint) {
+ myEquationSystem->removeConstraint(myFictiveConstraint);
+ myParameters.pop_back();
+
+ GCS::VEC_pD aParams = myFictiveConstraint->params();
+ for (GCS::VEC_pD::iterator anIt = aParams.begin(); anIt != aParams.end(); ++ anIt)
+ delete *anIt;
+ delete myFictiveConstraint;
+ myFictiveConstraint = 0;
+ }
+}
private:
void collectConflicting();
+ /// \brief Add fictive constraint if the sketch contains temporary constraints only
+ void addFictiveConstraintIfNecessary();
+ /// \brief Remove previously added fictive constraint
+ void removeFictiveConstraint();
+
private:
typedef std::map<ConstraintID, std::set<GCSConstraintPtr> > ConstraintMap;
bool myConfCollected;
int myDOF; ///< degrees of freedom
+
+ GCS::Constraint* myFictiveConstraint;
};
typedef std::shared_ptr<PlaneGCSSolver_Solver> SolverPtr;
void PlaneGCSSolver_Storage::addMovementConstraint(
const ConstraintWrapperPtr& theSolverConstraint)
{
- if (myConstraintMap.empty())
- return; // no need to process temporary constraints if there is no active constraint
-
// before adding movement constraint to solver, re-check its DOF
if (mySketchSolver->dof() == 0)
mySketchSolver->diagnose();
#include <GeomDataAPI_Point2D.h>
#include <SketchPlugin_Feature.h>
-#include <cmath>
-
-// Verify the entities are equal
-static bool isEqual(const EntityWrapperPtr& theEntity1, const EntityWrapperPtr& theEntity2);
+/// \brief Get list of parameters of current entity
+static GCS::VEC_pD toParameters(const EntityWrapperPtr& theEntity);
SketchSolver_ConstraintFixed::SketchSolver_ConstraintFixed(ConstraintPtr theConstraint)
return EntityWrapperPtr();
}
-GCS::VEC_pD SketchSolver_ConstraintFixed::toParameters(const EntityWrapperPtr& theEntity)
+
+
+
+// ================== Auxiliary functions ==================
+GCS::VEC_pD toParameters(const EntityWrapperPtr& theEntity)
{
GCS::VEC_pD aParameters;
if (!theEntity)
std::dynamic_pointer_cast<GCS::Circle>(anEntity->entity());
aParameters.push_back(aCircle->center.x);
aParameters.push_back(aCircle->center.y);
+ aParameters.push_back(aCircle->rad);
break;
}
case ENTITY_ARC: {
std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEntity->entity());
aParameters.push_back(anArc->center.x);
aParameters.push_back(anArc->center.y);
+ aParameters.push_back(anArc->rad);
+ aParameters.push_back(anArc->startAngle);
+ aParameters.push_back(anArc->endAngle);
break;
}
default:
{}
/// \brief Obtain entity to be fixed
- virtual EntityWrapperPtr entityToFix();
+ EntityWrapperPtr entityToFix();
/// \brief Create Fixed constraint for the feature basing on its type
/// \param theFeature [in] feature, converted to solver specific format
/// \return Fixed constraint
- virtual ConstraintWrapperPtr fixFeature(EntityWrapperPtr theFeature);
-
- /// \brief Get list of parameters of current entity
- static GCS::VEC_pD toParameters(const EntityWrapperPtr& theEntity);
+ ConstraintWrapperPtr fixFeature(EntityWrapperPtr theFeature);
protected:
std::vector<double> myFixedValues;
#include <SketchSolver_Error.h>
#include <SketchSolver_Manager.h>
+#include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_PointWrapper.h>
+
#include <SketchPlugin_Arc.h>
#include <SketchPlugin_Circle.h>
#include <SketchPlugin_Line.h>
SketchSolver_ConstraintMovement::SketchSolver_ConstraintMovement(FeaturePtr theFeature)
: SketchSolver_ConstraintFixed(ConstraintPtr()),
- myMovedFeature(theFeature)
+ myMovedFeature(theFeature),
+ mySimpleMove(true)
{
}
SketchSolver_ConstraintMovement::SketchSolver_ConstraintMovement(AttributePtr thePoint)
: SketchSolver_ConstraintFixed(ConstraintPtr()),
- myDraggedPoint(thePoint)
+ myDraggedPoint(thePoint),
+ mySimpleMove(true)
{
myMovedFeature = ModelAPI_Feature::feature(thePoint->owner());
}
return;
}
- EntityWrapperPtr aMovedEntity = entityToFix();
- if (!myErrorMsg.empty() || !aMovedEntity) {
+ mySolverConstraint = initMovement();
+ if (!myErrorMsg.empty() || !mySolverConstraint) {
// Nothing to move, clear the feature to avoid changing its group
// after removing the Movement constraint.
myMovedFeature = FeaturePtr();
return;
}
-
- mySolverConstraint = fixFeature(aMovedEntity);
myStorage->addMovementConstraint(mySolverConstraint);
}
-EntityWrapperPtr SketchSolver_ConstraintMovement::entityToFix()
+static bool isSimpleMove(FeaturePtr theMovedFeature, AttributePtr theDraggedPoint)
+{
+ bool isSimple = true;
+ if (theMovedFeature->getKind() == SketchPlugin_Circle::ID())
+ isSimple = (theDraggedPoint.get() != 0);
+ else if (theMovedFeature->getKind() == SketchPlugin_Arc::ID()) {
+ isSimple = (theDraggedPoint.get() != 0 &&
+ theDraggedPoint->id() == SketchPlugin_Arc::CENTER_ID());
+ }
+ return isSimple;
+}
+
+ConstraintWrapperPtr SketchSolver_ConstraintMovement::initMovement()
{
+ ConstraintWrapperPtr aConstraint;
+
// if the feature is copy, do not move it
std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
std::dynamic_pointer_cast<SketchPlugin_Feature>(myMovedFeature);
if (!aSketchFeature || aSketchFeature->isCopy()) {
myStorage->setNeedToResolve(true);
- return EntityWrapperPtr();
+ return aConstraint;
}
EntityWrapperPtr anEntity =
myDraggedPoint ? myStorage->entity(myDraggedPoint) : myStorage->entity(myMovedFeature);
if (!anEntity) {
myStorage->update(myMovedFeature, true);
- anEntity = myStorage->entity(myMovedFeature);
+ anEntity =
+ myDraggedPoint ? myStorage->entity(myDraggedPoint) : myStorage->entity(myMovedFeature);
+ if (!anEntity)
+ return aConstraint;
+ }
+
+ mySimpleMove = isSimpleMove(myMovedFeature, myDraggedPoint);
+
+ if (mySimpleMove)
+ aConstraint = fixFeature(anEntity);
+ else {
+ if (myDraggedPoint) // start or end point of arc has been moved
+ aConstraint = fixArcExtremity(anEntity);
+ else // arc or circle has been moved
+ aConstraint = fixPointOnCircle(anEntity);
+ }
+
+ return aConstraint;
+}
+
+ConstraintWrapperPtr SketchSolver_ConstraintMovement::fixArcExtremity(
+ const EntityWrapperPtr& theArcExtremity)
+{
+ static const int nbParams = 4;
+ myFixedValues.reserve(nbParams); // moved point and center of arc
+
+ EdgeWrapperPtr aCircularEntity = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(
+ myStorage->entity(myMovedFeature));
+ std::shared_ptr<GCS::Arc> anArc =
+ std::dynamic_pointer_cast<GCS::Arc>(aCircularEntity->entity());
+
+ PointWrapperPtr aPoint =
+ std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theArcExtremity);
+
+ double* aParams[nbParams] = { aPoint->point()->x, aPoint->point()->y,
+ anArc->center.x, anArc->center.y };
+
+ std::list<GCSConstraintPtr> aConstraints;
+ for (int i = 0; i < nbParams; ++i) {
+ myFixedValues.push_back(*aParams[i]);
+ GCSConstraintPtr aNewConstraint(new GCS::ConstraintEqual(&myFixedValues[i], aParams[i]));
+ aNewConstraint->rescale(0.01);
+ aConstraints.push_back(aNewConstraint);
+ }
+
+ return ConstraintWrapperPtr(
+ new PlaneGCSSolver_ConstraintWrapper(aConstraints, getType()));
+}
+
+ConstraintWrapperPtr SketchSolver_ConstraintMovement::fixPointOnCircle(
+ const EntityWrapperPtr& theCircular)
+{
+ static const double scale = 0.01;
+ static const int nbParams = 4;
+ myFixedValues.reserve(nbParams); // moved point and center of arc/circle
+
+ EdgeWrapperPtr aCircularEntity =
+ std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(theCircular);
+ std::shared_ptr<GCS::Circle> aCircular =
+ std::dynamic_pointer_cast<GCS::Circle>(aCircularEntity->entity());
+
+ // initialize fixed values
+ myFixedValues.push_back(*aCircular->center.x + *aCircular->rad);
+ myFixedValues.push_back(*aCircular->center.y);
+ myFixedValues.push_back(*aCircular->center.x);
+ myFixedValues.push_back(*aCircular->center.y);
+
+ // create a moved point
+ GCS::Point aPointOnCircle;
+ aPointOnCircle.x = &myFixedValues[0];
+ aPointOnCircle.y = &myFixedValues[1];
+
+ std::list<GCSConstraintPtr> aConstraints;
+ // point-on-circle
+ GCSConstraintPtr aNewConstraint(
+ new GCS::ConstraintP2PDistance(aPointOnCircle, aCircular->center, aCircular->rad));
+ aNewConstraint->rescale(scale);
+ aConstraints.push_back(aNewConstraint);
+ // fixed center (x)
+ aNewConstraint = GCSConstraintPtr(
+ new GCS::ConstraintEqual(&myFixedValues[2], aCircular->center.x));
+ aNewConstraint->rescale(scale);
+ aConstraints.push_back(aNewConstraint);
+ // fixed center (y)
+ aNewConstraint = GCSConstraintPtr(
+ new GCS::ConstraintEqual(&myFixedValues[3], aCircular->center.y));
+ aNewConstraint->rescale(scale);
+ aConstraints.push_back(aNewConstraint);
+
+ return ConstraintWrapperPtr(
+ new PlaneGCSSolver_ConstraintWrapper(aConstraints, getType()));
+}
+
+
+void SketchSolver_ConstraintMovement::startPoint(
+ const std::shared_ptr<GeomAPI_Pnt2d>& theStartPoint)
+{
+ myStartPoint = theStartPoint;
+ if (!mySimpleMove) {
+ myFixedValues[0] = myStartPoint->x();
+ myFixedValues[1] = myStartPoint->y();
}
- return anEntity;
}
void SketchSolver_ConstraintMovement::moveTo(
const std::shared_ptr<GeomAPI_Pnt2d>& theDestinationPoint)
{
- EntityWrapperPtr aMovedEntity =
- myDraggedPoint ? myStorage->entity(myDraggedPoint) : myStorage->entity(myMovedFeature);
- if (!aMovedEntity)
- return;
-
double aDelta[2] = { theDestinationPoint->x() - myStartPoint->x(),
theDestinationPoint->y() - myStartPoint->y() };
- GCS::VEC_pD aFixedParams = toParameters(aMovedEntity);
- for (int i = 0; i < aFixedParams.size() && i < myFixedValues.size(); ++i)
- myFixedValues[i] = *(aFixedParams[i]) + aDelta[i % 2];
-
- // no persistent constraints in the storage, thus store values directly to the feature
- if (myStorage->isEmpty()) {
- for (int i = 0; i < aFixedParams.size() && i < myFixedValues.size(); ++i)
- *(aFixedParams[i]) = myFixedValues[i];
- myStorage->setNeedToResolve(true);
- }
+ int aMaxSize = mySimpleMove ? (int)myFixedValues.size() : 2;
+ for (int i = 0; i < aMaxSize; ++i)
+ myFixedValues[i] += aDelta[i % 2];
}
SketchSolver_ConstraintMovement(AttributePtr thePoint);
/// \brief Set coordinates of the start point of the movement
- void startPoint(const std::shared_ptr<GeomAPI_Pnt2d>& theStartPoint)
- { myStartPoint = theStartPoint; }
+ void startPoint(const std::shared_ptr<GeomAPI_Pnt2d>& theStartPoint);
/// \brief Set coordinates of fixed feature to the values where it has been dragged.
/// Useful when the feature is being moved.
/// \brief Converts SketchPlugin constraint to a list of SolveSpace constraints
virtual void process();
- /// \brief Obtain entity to be fixed
- virtual EntityWrapperPtr entityToFix();
+ /// \brief Create Fixed constraint for the feature basing on its type and moved point
+ /// \return Fixed constraint
+ ConstraintWrapperPtr initMovement();
+
+ /// \brief Create constraint to fix moved arc extremity
+ ConstraintWrapperPtr fixArcExtremity(const EntityWrapperPtr& theArcExtremity);
+
+ /// \brief Creat constraint to fix moved point on circle/arc
+ ConstraintWrapperPtr fixPointOnCircle(const EntityWrapperPtr& theCircular);
private:
FeaturePtr myMovedFeature; ///< fixed feature (if set, myBaseConstraint should be NULL)
AttributePtr myDraggedPoint; ///< one of the feature points which has been moved
std::shared_ptr<GeomAPI_Pnt2d> myStartPoint; ///< start point of the movement
+
+ bool mySimpleMove; ///< simple move, thus all parameters should be increased by movement delta
};
#endif
if (aConstraint) {
SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
if (aConstraint->error().empty()) {
- if (!theStorage->isEmpty())
- theStorage->setNeedToResolve(true);
-
- theSketchSolver->initialize();
aConstraint->startPoint(theFrom);
+ theSketchSolver->initialize();
aConstraint->moveTo(theTo);
+ theStorage->setNeedToResolve(true);
} else
theStorage->notify(aConstraint->movedFeature());
}