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.
This commit is contained in:
parent
ced42440e7
commit
97e71856b3
|
@ -67,8 +67,10 @@ Bugs fixed:
|
||||||
causes the line length to collapse.
|
causes the line length to collapse.
|
||||||
* Curve-line constraints (in 3d), parallel constraints (in 3d), and
|
* Curve-line constraints (in 3d), parallel constraints (in 3d), and
|
||||||
same orientation constraints are more robust.
|
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.
|
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
|
* Points highlighted with "Analyze → Show Degrees of Freedom" are drawn
|
||||||
on top of all other geometry.
|
on top of all other geometry.
|
||||||
|
|
||||||
|
|
|
@ -82,8 +82,8 @@ hConstraint Constraint::AddConstraint(Constraint *c) {
|
||||||
hConstraint Constraint::AddConstraint(Constraint *c, bool rememberForUndo) {
|
hConstraint Constraint::AddConstraint(Constraint *c, bool rememberForUndo) {
|
||||||
if(rememberForUndo) SS.UndoRemember();
|
if(rememberForUndo) SS.UndoRemember();
|
||||||
|
|
||||||
SK.constraint.AddAndAssignId(c);
|
hConstraint hc = SK.constraint.AddAndAssignId(c);
|
||||||
c->Generate(&SK.param);
|
SK.GetConstraint(hc)->Generate(&SK.param);
|
||||||
|
|
||||||
SS.MarkGroupDirty(c->group);
|
SS.MarkGroupDirty(c->group);
|
||||||
SK.GetGroup(c->group)->dofCheckOk = false;
|
SK.GetGroup(c->group)->dofCheckOk = false;
|
||||||
|
|
|
@ -894,22 +894,52 @@ hRequest GraphicsWindow::AddRequest(Request::Type type, bool rememberForUndo) {
|
||||||
return r.h;
|
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;
|
if(!hover.entity.v) return false;
|
||||||
|
|
||||||
|
Entity *point = SK.GetEntity(pt);
|
||||||
Entity *e = SK.GetEntity(hover.entity);
|
Entity *e = SK.GetEntity(hover.entity);
|
||||||
if(e->IsPoint()) {
|
if(e->IsPoint()) {
|
||||||
Entity *point = SK.GetEntity(pt);
|
|
||||||
point->PointForceTo(e->PointGetNum());
|
point->PointForceTo(e->PointGetNum());
|
||||||
Constraint::ConstrainCoincident(e->h, pt);
|
Constraint::ConstrainCoincident(e->h, pt);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(e->IsCircle()) {
|
if(e->IsCircle()) {
|
||||||
|
if(projected != NULL) {
|
||||||
|
Vector snapPos = SnapToEntityByScreenPoint(*projected, e->h);
|
||||||
|
point->PointForceTo(snapPos);
|
||||||
|
}
|
||||||
Constraint::Constrain(Constraint::Type::PT_ON_CIRCLE,
|
Constraint::Constrain(Constraint::Type::PT_ON_CIRCLE,
|
||||||
pt, Entity::NO_ENTITY, e->h);
|
pt, Entity::NO_ENTITY, e->h);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(e->type == Entity::Type::LINE_SEGMENT) {
|
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,
|
Constraint::Constrain(Constraint::Type::PT_ON_LINE,
|
||||||
pt, Entity::NO_ENTITY, e->h);
|
pt, Entity::NO_ENTITY, e->h);
|
||||||
return true;
|
return true;
|
||||||
|
@ -942,6 +972,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
orig.mouse.x = mx;
|
orig.mouse.x = mx;
|
||||||
orig.mouse.y = my;
|
orig.mouse.y = my;
|
||||||
orig.mouseOnButtonDown = orig.mouse;
|
orig.mouseOnButtonDown = orig.mouse;
|
||||||
|
Point2d mouse = Point2d::From(mx, my);
|
||||||
|
|
||||||
// The current mouse location
|
// The current mouse location
|
||||||
Vector v = offset.ScaledBy(-1);
|
Vector v = offset.ScaledBy(-1);
|
||||||
|
@ -956,7 +987,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
case Command::DATUM_POINT:
|
case Command::DATUM_POINT:
|
||||||
hr = AddRequest(Request::Type::DATUM_POINT);
|
hr = AddRequest(Request::Type::DATUM_POINT);
|
||||||
SK.GetEntity(hr.entity(0))->PointForceTo(v);
|
SK.GetEntity(hr.entity(0))->PointForceTo(v);
|
||||||
ConstrainPointByHovered(hr.entity(0));
|
ConstrainPointByHovered(hr.entity(0), &mouse);
|
||||||
|
|
||||||
ClearSuper();
|
ClearSuper();
|
||||||
break;
|
break;
|
||||||
|
@ -966,7 +997,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
hr = AddRequest(Request::Type::LINE_SEGMENT);
|
hr = AddRequest(Request::Type::LINE_SEGMENT);
|
||||||
SK.GetRequest(hr)->construction = (pending.command == Command::CONSTR_SEGMENT);
|
SK.GetRequest(hr)->construction = (pending.command == Command::CONSTR_SEGMENT);
|
||||||
SK.GetEntity(hr.entity(1))->PointForceTo(v);
|
SK.GetEntity(hr.entity(1))->PointForceTo(v);
|
||||||
ConstrainPointByHovered(hr.entity(1));
|
ConstrainPointByHovered(hr.entity(1), &mouse);
|
||||||
|
|
||||||
ClearSuper();
|
ClearSuper();
|
||||||
AddToPending(hr);
|
AddToPending(hr);
|
||||||
|
@ -1004,7 +1035,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
||||||
lns[i].entity(0));
|
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();
|
Vector pos = SK.GetEntity(lns[2].entity(1))->PointGetNum();
|
||||||
for(i = 0; i < 4; i++) {
|
for(i = 0; i < 4; i++) {
|
||||||
SK.GetEntity(lns[i].entity(1))->PointForceTo(pos);
|
SK.GetEntity(lns[i].entity(1))->PointForceTo(pos);
|
||||||
|
@ -1028,7 +1059,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
// Initial radius zero
|
// Initial radius zero
|
||||||
SK.GetEntity(hr.entity(64))->DistanceForceTo(0);
|
SK.GetEntity(hr.entity(64))->DistanceForceTo(0);
|
||||||
|
|
||||||
ConstrainPointByHovered(hr.entity(1));
|
ConstrainPointByHovered(hr.entity(1), &mouse);
|
||||||
|
|
||||||
ClearSuper();
|
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(1))->PointForceTo(v.Minus(adj));
|
||||||
SK.GetEntity(hr.entity(2))->PointForceTo(v);
|
SK.GetEntity(hr.entity(2))->PointForceTo(v);
|
||||||
SK.GetEntity(hr.entity(3))->PointForceTo(v);
|
SK.GetEntity(hr.entity(3))->PointForceTo(v);
|
||||||
ConstrainPointByHovered(hr.entity(2));
|
ConstrainPointByHovered(hr.entity(2), &mouse);
|
||||||
|
|
||||||
ClearSuper();
|
ClearSuper();
|
||||||
AddToPending(hr);
|
AddToPending(hr);
|
||||||
|
@ -1067,7 +1098,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
SK.GetEntity(hr.entity(2))->PointForceTo(v);
|
SK.GetEntity(hr.entity(2))->PointForceTo(v);
|
||||||
SK.GetEntity(hr.entity(3))->PointForceTo(v);
|
SK.GetEntity(hr.entity(3))->PointForceTo(v);
|
||||||
SK.GetEntity(hr.entity(4))->PointForceTo(v);
|
SK.GetEntity(hr.entity(4))->PointForceTo(v);
|
||||||
ConstrainPointByHovered(hr.entity(1));
|
ConstrainPointByHovered(hr.entity(1), &mouse);
|
||||||
|
|
||||||
ClearSuper();
|
ClearSuper();
|
||||||
AddToPending(hr);
|
AddToPending(hr);
|
||||||
|
@ -1088,7 +1119,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
SK.GetEntity(hr.entity(1))->PointForceTo(v);
|
SK.GetEntity(hr.entity(1))->PointForceTo(v);
|
||||||
SK.GetEntity(hr.entity(32))->NormalForceTo(
|
SK.GetEntity(hr.entity(32))->NormalForceTo(
|
||||||
Quaternion::From(SS.GW.projRight, SS.GW.projUp));
|
Quaternion::From(SS.GW.projRight, SS.GW.projUp));
|
||||||
ConstrainPointByHovered(hr.entity(1));
|
ConstrainPointByHovered(hr.entity(1), &mouse);
|
||||||
|
|
||||||
ClearSuper();
|
ClearSuper();
|
||||||
break;
|
break;
|
||||||
|
@ -1136,7 +1167,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
|
|
||||||
case Pending::DRAGGING_NEW_POINT:
|
case Pending::DRAGGING_NEW_POINT:
|
||||||
case Pending::DRAGGING_NEW_ARC_POINT:
|
case Pending::DRAGGING_NEW_ARC_POINT:
|
||||||
ConstrainPointByHovered(pending.point);
|
ConstrainPointByHovered(pending.point, &mouse);
|
||||||
ClearPending();
|
ClearPending();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1165,7 +1196,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ConstrainPointByHovered(pending.point)) {
|
if(ConstrainPointByHovered(pending.point, &mouse)) {
|
||||||
ClearPending();
|
ClearPending();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1213,7 +1244,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ConstrainPointByHovered(pending.point)) {
|
if(ConstrainPointByHovered(pending.point, &mouse)) {
|
||||||
ClearPending();
|
ClearPending();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1222,7 +1253,6 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
hRequest hr = AddRequest(Request::Type::LINE_SEGMENT);
|
hRequest hr = AddRequest(Request::Type::LINE_SEGMENT);
|
||||||
ReplacePending(pending.request, hr);
|
ReplacePending(pending.request, hr);
|
||||||
SK.GetRequest(hr)->construction = SK.GetRequest(pending.request)->construction;
|
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,
|
// Displace the second point of the new line segment slightly,
|
||||||
// to avoid creating zero-length edge warnings.
|
// to avoid creating zero-length edge warnings.
|
||||||
SK.GetEntity(hr.entity(2))->PointForceTo(
|
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
|
// Constrain the line segments to share an endpoint
|
||||||
Constraint::ConstrainCoincident(pending.point, hr.entity(1));
|
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
|
// And drag an endpoint of the new line segment
|
||||||
pending.operation = Pending::DRAGGING_NEW_LINE_POINT;
|
pending.operation = Pending::DRAGGING_NEW_LINE_POINT;
|
||||||
|
|
3
src/ui.h
3
src/ui.h
|
@ -728,7 +728,8 @@ public:
|
||||||
bool SuggestLineConstraint(hRequest lineSegment, ConstraintBase::Type *type);
|
bool SuggestLineConstraint(hRequest lineSegment, ConstraintBase::Type *type);
|
||||||
|
|
||||||
Vector SnapToGrid(Vector p);
|
Vector SnapToGrid(Vector p);
|
||||||
bool ConstrainPointByHovered(hEntity pt);
|
Vector SnapToEntityByScreenPoint(Point2d pp, hEntity he);
|
||||||
|
bool ConstrainPointByHovered(hEntity pt, const Point2d *projected = NULL);
|
||||||
void DeleteTaggedRequests();
|
void DeleteTaggedRequests();
|
||||||
hRequest AddRequest(Request::Type type, bool rememberForUndo);
|
hRequest AddRequest(Request::Type type, bool rememberForUndo);
|
||||||
hRequest AddRequest(Request::Type type);
|
hRequest AddRequest(Request::Type type);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user