From 70bf14530d56ed36214ba1258c06d42159deb2c1 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Mon, 28 Apr 2008 01:40:02 -0800 Subject: [PATCH] Add point on line constraints, in 2d and 3d. The 3d equations do not have much motivation behind them, but they seem to work. And make sure that we don't solve multiple times without repainting in between, and tweak the text window a bit more. [git-p4: depot-paths = "//depot/solvespace/": change = 1696] --- constraint.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++ drawconstraint.cpp | 17 +++++++----- expr.cpp | 8 ++++++ expr.h | 1 + glhelper.cpp | 3 +++ graphicswin.cpp | 8 ++++-- sketch.h | 2 ++ system.cpp | 5 ++-- textwin.cpp | 25 ++++++++---------- ui.h | 7 ++++- 10 files changed, 113 insertions(+), 27 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index 528c824..26d10d0 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -68,6 +68,10 @@ void Constraint::MenuConstrain(int id) { c.type = PT_IN_PLANE; c.ptA = gs.point[0]; c.entityA = gs.entity[0]; + } else if(gs.points == 1 && gs.lineSegments == 1 && gs.n == 2) { + c.type = PT_ON_LINE; + c.ptA = gs.point[0]; + c.entityA = gs.entity[0]; } else { Error("Bad selection for on point / curve / plane constraint."); return; @@ -135,6 +139,42 @@ void Constraint::MenuConstrain(int id) { InvalidateGraphics(); } +Expr *Constraint::PointLineDistance(hEntity wrkpl, hEntity hpt, hEntity hln) { + Entity *ln = SS.GetEntity(hln); + Entity *a = SS.GetEntity(ln->point[0]); + Entity *b = SS.GetEntity(ln->point[1]); + + Entity *p = SS.GetEntity(hpt); + + if(wrkpl.v == Entity::FREE_IN_3D.v) { + ExprVector ep = p->PointGetExprs(); + + ExprVector ea = a->PointGetExprs(); + ExprVector eb = b->PointGetExprs(); + ExprVector eab = ea.Minus(eb); + Expr *m = eab.Magnitude(); + + return ((eab.Cross(ea.Minus(ep))).Magnitude())->Div(m); + } else { + Expr *ua, *va, *ub, *vb; + a->PointGetExprsInWorkplane(wrkpl, &ua, &va); + b->PointGetExprsInWorkplane(wrkpl, &ub, &vb); + + Expr *du = ua->Minus(ub); + Expr *dv = va->Minus(vb); + + Expr *u, *v; + p->PointGetExprsInWorkplane(wrkpl, &u, &v); + + Expr *m = ((du->Square())->Plus(dv->Square()))->Sqrt(); + + Expr *proj = (dv->Times(ua->Minus(u)))->Minus( + (du->Times(va->Minus(v)))); + + return proj->Div(m); + } +} + Expr *Constraint::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) { Entity *pa = SS.GetEntity(hpa); Entity *pb = SS.GetEntity(hpb); @@ -231,6 +271,30 @@ void Constraint::Generate(IdList *l) { break; } + case PT_ON_LINE: + if(workplane.v == Entity::FREE_IN_3D.v) { + Entity *ln = SS.GetEntity(entityA); + Entity *a = SS.GetEntity(ln->point[0]); + Entity *b = SS.GetEntity(ln->point[1]); + Entity *p = SS.GetEntity(ptA); + + ExprVector ep = p->PointGetExprs(); + ExprVector ea = a->PointGetExprs(); + ExprVector eb = b->PointGetExprs(); + ExprVector eab = ea.Minus(eb); + ExprVector r = eab.Cross(ea.Minus(ep)); + + // When the constraint is satisfied, our vector r is zero; + // but that's three numbers, and the constraint hits only + // two degrees of freedom. This seems to be an acceptable + // choice of equations, though it's arbitrary. + AddEq(l, (r.x)->Square()->Plus((r.y)->Square()), 0); + AddEq(l, (r.y)->Square()->Plus((r.z)->Square()), 1); + } else { + AddEq(l, PointLineDistance(workplane, ptA, entityA), 0); + } + break; + case HORIZONTAL: case VERTICAL: { hEntity ha, hb; diff --git a/drawconstraint.cpp b/drawconstraint.cpp index dfdc206..8e8b07a 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -32,10 +32,11 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { // able to be selected. if(!(g->visible)) return; - // Unit vectors that describe our current view of the scene. - Vector gr = SS.GW.projRight; - Vector gu = SS.GW.projUp; - Vector gn = gr.Cross(gu); + // Unit vectors that describe our current view of the scene. One pixel + // long, not one actual unit. + Vector gr = SS.GW.projRight.ScaledBy(1/SS.GW.scale); + Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale); + Vector gn = (gr.Cross(gu)).WithMagnitude(1/SS.GW.scale); glxColor3d(1, 0.2, 1); switch(type) { @@ -103,11 +104,12 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + case PT_ON_LINE: case PT_IN_PLANE: { - double s = 5; - Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); - Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); + double s = 7; Vector p = SS.GetEntity(ptA)->PointGetCoords(); + Vector r = gr.WithMagnitude(s); + Vector d = gu.WithMagnitude(s); LineDrawOrGetDistance(p.Plus (r).Plus (d), p.Plus (r).Minus(d)); LineDrawOrGetDistance(p.Plus (r).Minus(d), p.Minus(r).Minus(d)); LineDrawOrGetDistance(p.Minus(r).Minus(d), p.Minus(r).Plus (d)); @@ -115,6 +117,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + case EQUAL_LENGTH_LINES: { for(int i = 0; i < 2; i++) { Entity *e = SS.GetEntity(i == 0 ? entityA : entityB); diff --git a/expr.cpp b/expr.cpp index 8866cee..c034a6c 100644 --- a/expr.cpp +++ b/expr.cpp @@ -29,6 +29,14 @@ Expr *ExprVector::Dot(ExprVector b) { return r; } +ExprVector ExprVector::Cross(ExprVector b) { + ExprVector r; + r.x = (y->Times(b.z))->Minus(z->Times(b.y)); + r.y = (z->Times(b.x))->Minus(x->Times(b.z)); + r.z = (x->Times(b.y))->Minus(y->Times(b.x)); + return r; +} + ExprVector ExprVector::ScaledBy(Expr *s) { ExprVector r; r.x = x->Times(s); diff --git a/expr.h b/expr.h index 8916f44..7276e1f 100644 --- a/expr.h +++ b/expr.h @@ -123,6 +123,7 @@ public: ExprVector Plus(ExprVector b); ExprVector Minus(ExprVector b); Expr *Dot(ExprVector b); + ExprVector Cross(ExprVector b); ExprVector ScaledBy(Expr *s); Expr *Magnitude(void); }; diff --git a/glhelper.cpp b/glhelper.cpp index 2497c0b..51bc6f8 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -48,6 +48,9 @@ void glxTranslatev(Vector u) void glxOntoWorkplane(Vector u, Vector v) { + u = u.WithMagnitude(1); + v = v.WithMagnitude(1); + double mat[16]; Vector n = u.Cross(v); MakeMatrix(mat, u.x, v.x, n.x, 0, diff --git a/graphicswin.cpp b/graphicswin.cpp index eb1a272..fe00245 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -432,8 +432,11 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, Constraint *c = SS.constraint.FindById(pendingConstraint); UpdateDraggedPoint(&(c->disp.offset), x, y); } else if(leftDown && pendingOperation == DRAGGING_POINT) { - UpdateDraggedEntity(pendingPoint, x, y); - SS.GenerateAll(solving == SOLVE_ALWAYS); + if(havePainted) { + UpdateDraggedEntity(pendingPoint, x, y); + SS.GenerateAll(solving == SOLVE_ALWAYS); + havePainted = false; + } } // No buttons pressed. @@ -775,6 +778,7 @@ void GraphicsWindow::ToggleAnyDatumShown(int link, DWORD v) { } void GraphicsWindow::Paint(int w, int h) { + havePainted = true; width = w; height = h; glViewport(0, 0, w, h); diff --git a/sketch.h b/sketch.h index 40017dd..6d89c7a 100644 --- a/sketch.h +++ b/sketch.h @@ -260,6 +260,7 @@ public: static const int PT_PT_DISTANCE = 30; static const int PT_LINE_DISTANCE = 31; static const int PT_IN_PLANE = 40; + static const int PT_ON_LINE = 41; static const int EQUAL_LENGTH_LINES = 50; static const int HORIZONTAL = 80; @@ -311,6 +312,7 @@ public: void ModifyToSatisfy(void); void AddEq(IdList *l, Expr *expr, int index); static Expr *Distance(hEntity workplane, hEntity pa, hEntity pb); + static Expr *PointLineDistance(hEntity workplane, hEntity pt, hEntity ln); static void ConstrainCoincident(hEntity ptA, hEntity ptB); }; diff --git a/system.cpp b/system.cpp index 40449a5..a087f59 100644 --- a/system.cpp +++ b/system.cpp @@ -39,7 +39,7 @@ void System::EvalJacobian(void) { } bool System::Tol(double v) { - return (fabs(v) < 0.01); + return (fabs(v) < 0.001); } void System::GaussJordan(void) { @@ -207,10 +207,11 @@ bool System::NewtonSolve(int tag) { bool System::Solve(void) { int i, j; + /* dbp("%d equations", eq.n); for(i = 0; i < eq.n; i++) { - dbp(" %s = 0", eq.elem[i].e->Print()); + dbp(" %.3f = %s = 0", eq.elem[i].e->Eval(), eq.elem[i].e->Print()); } dbp("%d parameters", param.n); */ diff --git a/textwin.cpp b/textwin.cpp index ca2c3c4..90c1320 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -13,8 +13,8 @@ const TextWindow::Color TextWindow::fgColors[] = { }; const TextWindow::Color TextWindow::bgColors[] = { { 'd', RGB( 0, 0, 0) }, - { 't', RGB( 30, 10, 30) }, - { 'a', RGB( 25, 25, 25) }, + { 't', RGB( 40, 20, 40) }, + { 'a', RGB( 20, 20, 20) }, { 'r', RGB(255, 255, 255) }, { 0, 0 }, }; @@ -181,19 +181,20 @@ void TextWindow::Show(void) { InvalidateText(); } -void TextWindow::OneScreenForward(void) { +void TextWindow::OneScreenForwardTo(int screen) { SS.TW.shownIndex++; if(SS.TW.shownIndex >= HISTORY_LEN) SS.TW.shownIndex = 0; SS.TW.shown = &(SS.TW.showns[SS.TW.shownIndex]); history++; + + if(screen >= 0) shown->screen = screen; } void TextWindow::ScreenNavigation(int link, DWORD v) { switch(link) { default: case 'h': - SS.TW.OneScreenForward(); - SS.TW.shown->screen = SCREEN_LIST_OF_GROUPS; + SS.TW.OneScreenForwardTo(SCREEN_LIST_OF_GROUPS); break; case 'b': @@ -206,7 +207,7 @@ void TextWindow::ScreenNavigation(int link, DWORD v) { break; case 'f': - SS.TW.OneScreenForward(); + SS.TW.OneScreenForwardTo(-1); break; } SS.TW.Show(); @@ -262,9 +263,7 @@ hs(SS.GW.showConstraints), (DWORD)(&SS.GW.showConstraints), &(SS.GW.ToggleBool) } void TextWindow::ScreenSelectGroup(int link, DWORD v) { - SS.TW.OneScreenForward(); - - SS.TW.shown->screen = SCREEN_GROUP_INFO; + SS.TW.OneScreenForwardTo(SCREEN_GROUP_INFO); SS.TW.shown->group.v = v; SS.TW.Show(); @@ -317,17 +316,13 @@ void TextWindow::ShowListOfGroups(void) { void TextWindow::ScreenSelectConstraint(int link, DWORD v) { - SS.TW.OneScreenForward(); - - SS.TW.shown->screen = SCREEN_CONSTRAINT_INFO; + SS.TW.OneScreenForwardTo(SCREEN_CONSTRAINT_INFO); SS.TW.shown->constraint.v = v; SS.TW.Show(); } void TextWindow::ScreenSelectRequest(int link, DWORD v) { - SS.TW.OneScreenForward(); - - SS.TW.shown->screen = SCREEN_REQUEST_INFO; + SS.TW.OneScreenForwardTo(SCREEN_REQUEST_INFO); SS.TW.shown->request.v = v; SS.TW.Show(); diff --git a/ui.h b/ui.h index 1a814be..e95968f 100644 --- a/ui.h +++ b/ui.h @@ -65,7 +65,7 @@ public: void ShowEntityInfo(void); void ShowConstraintInfo(void); - void OneScreenForward(void); + void OneScreenForwardTo(int screen); static void ScreenSelectGroup(int link, DWORD v); static void ScreenActivateGroup(int link, DWORD v); static void ScreenToggleGroupShown(int link, DWORD v); @@ -145,6 +145,11 @@ public: Point2d mouse; } orig; + // When the user is dragging a point, don't solve multiple times without + // allowing a paint in between. The extra solves are wasted if they're + // not displayed. + bool havePainted; + void NormalizeProjectionVectors(void); Point2d ProjectPoint(Vector p); void AnimateOnto(Quaternion quatf, Vector offsetf);