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);