From 97e71856b3eddc2780d72cd02d3e0f37a4174d07 Mon Sep 17 00:00:00 2001 From: EvilSpirit Date: Thu, 2 Feb 2017 10:48:54 +0700 Subject: [PATCH] Snap point to entity when constraining to a hovered entity. Before this commit, when a point is constrained to an entity (point, circle, arc of circle or line segment) by clicking on it, the resulting constraint is not necessarily satisfied, and the next regeneration may place the newly constrained point somewhere other than the intended position. After this commit, the parameters are modified to satisfy the constraint. --- CHANGELOG.md | 4 +++- src/constraint.cpp | 4 ++-- src/mouse.cpp | 58 +++++++++++++++++++++++++++++++++++----------- src/ui.h | 3 ++- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd7c456..4cfd461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,8 +67,10 @@ Bugs fixed: causes the line length to collapse. * Curve-line constraints (in 3d), parallel constraints (in 3d), and same orientation constraints are more robust. - * Adding some constraints (vertical, midpoint, etc) twice will now error out + * Adding some constraints (vertical, midpoint, etc) twice errors out immediately, instead of later and in a confusing way. + * Constraining a newly placed point to a hovered entity does not cause + spurious changes in the sketch. * Points highlighted with "Analyze → Show Degrees of Freedom" are drawn on top of all other geometry. diff --git a/src/constraint.cpp b/src/constraint.cpp index bf34d08..b51cbaf 100644 --- a/src/constraint.cpp +++ b/src/constraint.cpp @@ -82,8 +82,8 @@ hConstraint Constraint::AddConstraint(Constraint *c) { hConstraint Constraint::AddConstraint(Constraint *c, bool rememberForUndo) { if(rememberForUndo) SS.UndoRemember(); - SK.constraint.AddAndAssignId(c); - c->Generate(&SK.param); + hConstraint hc = SK.constraint.AddAndAssignId(c); + SK.GetConstraint(hc)->Generate(&SK.param); SS.MarkGroupDirty(c->group); SK.GetGroup(c->group)->dofCheckOk = false; diff --git a/src/mouse.cpp b/src/mouse.cpp index db11b4b..3247cc5 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -894,22 +894,52 @@ hRequest GraphicsWindow::AddRequest(Request::Type type, bool rememberForUndo) { return r.h; } -bool GraphicsWindow::ConstrainPointByHovered(hEntity pt) { +Vector GraphicsWindow::SnapToEntityByScreenPoint(Point2d pp, hEntity he) { + Entity *e = SK.GetEntity(he); + if(e->IsPoint()) return e->PointGetNum(); + SEdgeList *edges = e->GetOrGenerateEdges(); + + double minD = -1.0f; + double k; + const SEdge *edge = NULL; + for(const auto &e : edges->l) { + Point2d p0 = ProjectPoint(e.a); + Point2d p1 = ProjectPoint(e.b); + Point2d dir = p1.Minus(p0); + double d = pp.DistanceToLine(p0, dir, /*asSegment=*/true); + if(minD > 0.0 && d > minD) continue; + minD = d; + k = pp.Minus(p0).Dot(dir) / dir.Dot(dir); + edge = &e; + } + if(edge == NULL) return UnProjectPoint(pp); + return edge->a.Plus(edge->b.Minus(edge->a).ScaledBy(k)); +} + +bool GraphicsWindow::ConstrainPointByHovered(hEntity pt, const Point2d *projected) { if(!hover.entity.v) return false; + Entity *point = SK.GetEntity(pt); Entity *e = SK.GetEntity(hover.entity); if(e->IsPoint()) { - Entity *point = SK.GetEntity(pt); point->PointForceTo(e->PointGetNum()); Constraint::ConstrainCoincident(e->h, pt); return true; } if(e->IsCircle()) { + if(projected != NULL) { + Vector snapPos = SnapToEntityByScreenPoint(*projected, e->h); + point->PointForceTo(snapPos); + } Constraint::Constrain(Constraint::Type::PT_ON_CIRCLE, pt, Entity::NO_ENTITY, e->h); return true; } if(e->type == Entity::Type::LINE_SEGMENT) { + if(projected != NULL) { + Vector snapPos = SnapToEntityByScreenPoint(*projected, e->h); + point->PointForceTo(snapPos); + } Constraint::Constrain(Constraint::Type::PT_ON_LINE, pt, Entity::NO_ENTITY, e->h); return true; @@ -942,6 +972,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { orig.mouse.x = mx; orig.mouse.y = my; orig.mouseOnButtonDown = orig.mouse; + Point2d mouse = Point2d::From(mx, my); // The current mouse location Vector v = offset.ScaledBy(-1); @@ -956,7 +987,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { case Command::DATUM_POINT: hr = AddRequest(Request::Type::DATUM_POINT); SK.GetEntity(hr.entity(0))->PointForceTo(v); - ConstrainPointByHovered(hr.entity(0)); + ConstrainPointByHovered(hr.entity(0), &mouse); ClearSuper(); break; @@ -966,7 +997,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { hr = AddRequest(Request::Type::LINE_SEGMENT); SK.GetRequest(hr)->construction = (pending.command == Command::CONSTR_SEGMENT); SK.GetEntity(hr.entity(1))->PointForceTo(v); - ConstrainPointByHovered(hr.entity(1)); + ConstrainPointByHovered(hr.entity(1), &mouse); ClearSuper(); AddToPending(hr); @@ -1004,7 +1035,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { Entity::NO_ENTITY, Entity::NO_ENTITY, lns[i].entity(0)); } - if(ConstrainPointByHovered(lns[2].entity(1))) { + if(ConstrainPointByHovered(lns[2].entity(1), &mouse)) { Vector pos = SK.GetEntity(lns[2].entity(1))->PointGetNum(); for(i = 0; i < 4; i++) { SK.GetEntity(lns[i].entity(1))->PointForceTo(pos); @@ -1028,7 +1059,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { // Initial radius zero SK.GetEntity(hr.entity(64))->DistanceForceTo(0); - ConstrainPointByHovered(hr.entity(1)); + ConstrainPointByHovered(hr.entity(1), &mouse); ClearSuper(); @@ -1051,7 +1082,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SK.GetEntity(hr.entity(1))->PointForceTo(v.Minus(adj)); SK.GetEntity(hr.entity(2))->PointForceTo(v); SK.GetEntity(hr.entity(3))->PointForceTo(v); - ConstrainPointByHovered(hr.entity(2)); + ConstrainPointByHovered(hr.entity(2), &mouse); ClearSuper(); AddToPending(hr); @@ -1067,7 +1098,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SK.GetEntity(hr.entity(2))->PointForceTo(v); SK.GetEntity(hr.entity(3))->PointForceTo(v); SK.GetEntity(hr.entity(4))->PointForceTo(v); - ConstrainPointByHovered(hr.entity(1)); + ConstrainPointByHovered(hr.entity(1), &mouse); ClearSuper(); AddToPending(hr); @@ -1088,7 +1119,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SK.GetEntity(hr.entity(1))->PointForceTo(v); SK.GetEntity(hr.entity(32))->NormalForceTo( Quaternion::From(SS.GW.projRight, SS.GW.projUp)); - ConstrainPointByHovered(hr.entity(1)); + ConstrainPointByHovered(hr.entity(1), &mouse); ClearSuper(); break; @@ -1136,7 +1167,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { case Pending::DRAGGING_NEW_POINT: case Pending::DRAGGING_NEW_ARC_POINT: - ConstrainPointByHovered(pending.point); + ConstrainPointByHovered(pending.point, &mouse); ClearPending(); break; @@ -1165,7 +1196,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { break; } - if(ConstrainPointByHovered(pending.point)) { + if(ConstrainPointByHovered(pending.point, &mouse)) { ClearPending(); break; } @@ -1213,7 +1244,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { } } - if(ConstrainPointByHovered(pending.point)) { + if(ConstrainPointByHovered(pending.point, &mouse)) { ClearPending(); break; } @@ -1222,7 +1253,6 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { hRequest hr = AddRequest(Request::Type::LINE_SEGMENT); ReplacePending(pending.request, hr); SK.GetRequest(hr)->construction = SK.GetRequest(pending.request)->construction; - SK.GetEntity(hr.entity(1))->PointForceTo(v); // Displace the second point of the new line segment slightly, // to avoid creating zero-length edge warnings. SK.GetEntity(hr.entity(2))->PointForceTo( @@ -1230,6 +1260,8 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { // Constrain the line segments to share an endpoint Constraint::ConstrainCoincident(pending.point, hr.entity(1)); + Vector pendingPos = SK.GetEntity(pending.point)->PointGetNum(); + SK.GetEntity(hr.entity(1))->PointForceTo(pendingPos); // And drag an endpoint of the new line segment pending.operation = Pending::DRAGGING_NEW_LINE_POINT; diff --git a/src/ui.h b/src/ui.h index cee4a1f..93dd7a9 100644 --- a/src/ui.h +++ b/src/ui.h @@ -728,7 +728,8 @@ public: bool SuggestLineConstraint(hRequest lineSegment, ConstraintBase::Type *type); Vector SnapToGrid(Vector p); - bool ConstrainPointByHovered(hEntity pt); + Vector SnapToEntityByScreenPoint(Point2d pp, hEntity he); + bool ConstrainPointByHovered(hEntity pt, const Point2d *projected = NULL); void DeleteTaggedRequests(); hRequest AddRequest(Request::Type type, bool rememberForUndo); hRequest AddRequest(Request::Type type);