From b484c2649393041add20e71e010ff60a0a5f6fb8 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Mon, 26 May 2008 01:56:50 -0800 Subject: [PATCH] Many user interface additions. Now I report when the solver fails, and in the case of a singular Jacobian, report which constraints can be removed to fix it. Also a mechanism to hover and select entities and constraints from the text window. [git-p4: depot-paths = "//depot/solvespace/": change = 1746] --- constraint.cpp | 2 + drawconstraint.cpp | 15 ++++++- entity.cpp | 13 +++++- graphicswin.cpp | 38 ++++++++++++++--- sketch.cpp | 5 ++- sketch.h | 15 +++++++ solvespace.cpp | 27 +++--------- solvespace.h | 6 ++- system.cpp | 72 +++++++++++++++++++++++++++---- textwin.cpp | 103 ++++++++++++++++++++++++++++++++++++++------- ui.h | 14 ++++-- win32/w32main.cpp | 28 +++++++++--- 12 files changed, 271 insertions(+), 67 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index f073d02..ef55576 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -1,5 +1,7 @@ #include "solvespace.h" +const hConstraint Constraint::NO_CONSTRAINT = { 0 }; + char *Constraint::DescriptionString(void) { static char ret[1024]; sprintf(ret, "c%03x", h.v); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 62ab02c..e0d917c 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -28,6 +28,7 @@ void Constraint::LineDrawOrGetDistance(Vector a, Vector b) { double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true); dogd.dmin = min(dogd.dmin, d); } + dogd.refp = (a.Plus(b)).ScaledBy(0.5); } double Constraint::EllipticalInterpolation(double rx, double ry, double theta) { @@ -56,6 +57,7 @@ void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { } else { Point2d o = SS.GW.ProjectPoint(ref); dogd.dmin = min(dogd.dmin, o.DistanceTo(dogd.mp) - 10); + dogd.refp = ref; } } @@ -197,6 +199,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { // same center; so if the point is visible, then this // constraint cannot be selected. But that's okay. dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 3); + dogd.refp = p; } break; } @@ -449,7 +452,8 @@ s: (type == AT_MIDPOINT) ? "M" : NULL))); glPopMatrix(); } else { - Point2d ref = SS.GW.ProjectPoint(m.Plus(offset)); + dogd.refp = m.Plus(offset); + Point2d ref = SS.GW.ProjectPoint(dogd.refp); dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10); } } else { @@ -518,3 +522,12 @@ Vector Constraint::GetLabelPos(void) { return p; } +Vector Constraint::GetReferencePos(void) { + dogd.drawing = false; + + dogd.refp = SS.GW.offset.ScaledBy(-1); + DrawOrGetDistance(NULL); + + return dogd.refp; +} + diff --git a/entity.cpp b/entity.cpp index e8c3f0e..6547d6a 100644 --- a/entity.cpp +++ b/entity.cpp @@ -522,6 +522,7 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) { double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true); dogd.dmin = min(dogd.dmin, d); } + dogd.refp = (a.Plus(b)).ScaledBy(0.5); } void Entity::LineDrawOrGetDistanceOrEdge(Vector a, Vector b) { @@ -555,6 +556,16 @@ double Entity::GetDistance(Point2d mp) { return dogd.dmin; } +Vector Entity::GetReferencePos(void) { + dogd.drawing = false; + dogd.edges = NULL; + + dogd.refp = SS.GW.offset.ScaledBy(-1); + DrawOrGetDistance(-1); + + return dogd.refp; +} + void Entity::DrawOrGetDistance(int order) { Group *g = SS.GetGroup(group); // If an entity is invisible, then it doesn't get shown, and it doesn't @@ -615,7 +626,6 @@ void Entity::DrawOrGetDistance(int order) { case NORMAL_IN_3D: case NORMAL_IN_2D: { if(order >= 0 && order != 2) break; - if(!SS.GW.showNormals) break; int i; for(i = 0; i < 2; i++) { @@ -630,6 +640,7 @@ void Entity::DrawOrGetDistance(int order) { } else { glxColor3d(0, 0.4, 0.4); if(i > 0) break; + if(!SS.GW.showNormals) break; } Quaternion q = NormalGetNum(); diff --git a/graphicswin.cpp b/graphicswin.cpp index 58c865a..02de59c 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -320,7 +320,7 @@ void GraphicsWindow::MenuEdit(int id) { SS.GW.ClearSuper(); // And regenerate to get rid of what it generates, plus anything // that references it (since the regen code checks for that). - SS.GW.GeneratePerSolving(); + SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS, 0, INT_MAX); SS.GW.EnsureValidActives(); SS.TW.Show(); break; @@ -624,7 +624,9 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, default: oops(); } - SS.GW.GeneratePerSolving(); + if(pending.operation != 0 && pending.operation != DRAGGING_CONSTRAINT) { + SS.GW.GeneratePerSolving(); + } havePainted = false; } @@ -640,10 +642,34 @@ bool GraphicsWindow::Selection::IsEmpty(void) { } void GraphicsWindow::Selection::Clear(void) { entity.v = constraint.v = 0; + emphasized = false; } void GraphicsWindow::Selection::Draw(void) { - if(entity.v) SS.GetEntity (entity )->Draw(-1); - if(constraint.v) SS.GetConstraint(constraint)->Draw(); + Vector refp; + if(entity.v) { + Entity *e = SS.GetEntity(entity); + e->Draw(-1); + if(emphasized) refp = e->GetReferencePos(); + } + if(constraint.v) { + Constraint *c = SS.GetConstraint(constraint); + c->Draw(); + if(emphasized) refp = c->GetReferencePos(); + } + if(emphasized && (constraint.v || entity.v)) { + double s = 0.501/SS.GW.scale; + Vector topLeft = SS.GW.projRight.ScaledBy(-SS.GW.width*s); + topLeft = topLeft.Plus(SS.GW.projUp.ScaledBy(SS.GW.height*s)); + topLeft = topLeft.Minus(SS.GW.offset); + + glLineWidth(40); + glColor4d(1.0, 1.0, 0, 0.2); + glBegin(GL_LINES); + glxVertex3v(topLeft); + glxVertex3v(refp); + glEnd(); + glLineWidth(1); + } } void GraphicsWindow::ClearSuper(void) { @@ -998,7 +1024,9 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { break; } } - SS.GW.GeneratePerSolving(); + if(pending.operation != 0 && pending.operation != DRAGGING_CONSTRAINT) { + SS.GW.GeneratePerSolving(); + } SS.TW.Show(); InvalidateGraphics(); diff --git a/sketch.cpp b/sketch.cpp index ade6162..f95598a 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -98,6 +98,7 @@ void Group::MenuGroup(int id) { Vector offsetf = (e->WorkplaneGetOffset()).ScaledBy(-1); SS.GW.AnimateOnto(quatf, offsetf); } + TextWindow::ScreenSelectGroup(0, g.h.v); SS.TW.Show(); } @@ -517,7 +518,7 @@ void Group::Draw(void) { glxFillMesh(&mesh); glDisable(GL_LIGHTING); -// glxDebugMesh(&mesh); + glxDebugMesh(&mesh); if(polyError.yes) { glxColor4d(1, 0, 0, 0.2); @@ -534,7 +535,7 @@ void Group::Draw(void) { glxWriteText("not closed contour!"); glPopMatrix(); } else { - glxColor4d(0, 1.0, 1.0, 0.1); + glxColor4d(0, 1.0, 1.0, 0.05); glPolygonOffset(-1, -1); glxFillPolygon(&poly); glPolygonOffset(0, 0); diff --git a/sketch.h b/sketch.h index f1d894c..867f452 100644 --- a/sketch.h +++ b/sketch.h @@ -10,6 +10,7 @@ class hParam; class Entity; class Param; +class hConstraint; class hEquation; class Equation; @@ -88,6 +89,14 @@ public: hGroup opB; bool visible; + static const int SOLVED_OKAY = 0; + static const int DIDNT_CONVERGE = 10; + static const int SINGULAR_JACOBIAN = 11; + struct { + int how; + SList remove; + } solved; + static const int WORKPLANE_BY_POINT_ORTHO = 6000; static const int WORKPLANE_BY_LINE_SEGMENTS = 6001; static const int EXTRUDE_ONE_SIDED = 7000; @@ -288,6 +297,7 @@ public: Point2d mp; double dmin; SEdgeList *edges; + Vector refp; } dogd; // state for drawing or getting distance (for hit testing) void LineDrawOrGetDistance(Vector a, Vector b); void LineDrawOrGetDistanceOrEdge(Vector a, Vector b); @@ -296,6 +306,7 @@ public: void Draw(int order); double GetDistance(Point2d mp); void GenerateEdges(SEdgeList *el); + Vector GetReferencePos(void); void AddEq(IdList *l, Expr *expr, int index); void GenerateEquations(IdList *l); @@ -328,6 +339,8 @@ public: class Constraint { public: + static const hConstraint NO_CONSTRAINT; + static const int USER_EQUATION = 10; static const int POINTS_COINCIDENT = 20; static const int PT_PT_DISTANCE = 30; @@ -382,6 +395,7 @@ public: bool drawing; Point2d mp; double dmin; + Vector refp; } dogd; // state for drawing or getting distance (for hit testing) void LineDrawOrGetDistance(Vector a, Vector b); void DrawOrGetDistance(Vector *labelPos); @@ -391,6 +405,7 @@ public: double GetDistance(Point2d mp); Vector GetLabelPos(void); + Vector GetReferencePos(void); void Draw(void); bool HasLabel(void); diff --git a/solvespace.cpp b/solvespace.cpp index d65b8a1..c68ca15 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -14,8 +14,8 @@ void SolveSpace::Init(char *cmdLine) { TW.Init(); GW.Init(); + GenerateAll(true, 0, INT_MAX); TW.Show(); - GenerateAll(false, 0, INT_MAX); } bool SolveSpace::PruneOrphans(void) { @@ -127,7 +127,8 @@ void SolveSpace::GenerateAll(bool andSolve) { int firstShown = INT_MAX, lastShown = 0; // The references don't count, so start from group 1 for(i = 1; i < group.n; i++) { - if(group.elem[i].visible) { + Group *g = &(group.elem[i]); + if(g->visible || (g->solved.how != Group::SOLVED_OKAY)) { firstShown = min(firstShown, i); lastShown = max(lastShown, i); } @@ -182,6 +183,7 @@ void SolveSpace::GenerateAll(bool andSolve, int first, int last) { if(g->h.v == Group::HGROUP_REFERENCES.v) { ForceReferences(); + g->solved.how = Group::SOLVED_OKAY; } else { if(i >= first && i <= last) { // The group falls inside the range, so really solve it, @@ -271,7 +273,7 @@ void SolveSpace::ForceReferences(void) { } } -bool SolveSpace::SolveGroup(hGroup hg) { +void SolveSpace::SolveGroup(hGroup hg) { int i; // Clear out the system to be solved. sys.entity.Clear(); @@ -293,26 +295,9 @@ bool SolveSpace::SolveGroup(hGroup hg) { p->known = false; p->val = GetParam(p->h)->val; } - // And generate all the equations from constraints in this group - for(i = 0; i < constraint.n; i++) { - Constraint *c = &(constraint.elem[i]); - if(c->group.v != hg.v) continue; - c->Generate(&(sys.eq)); - } - // And the equations from entities - for(i = 0; i < entity.n; i++) { - Entity *e = &(entity.elem[i]); - if(e->group.v != hg.v) continue; - - e->GenerateEquations(&(sys.eq)); - } - // And from the groups themselves - g->GenerateEquations(&(sys.eq)); - - bool r = sys.Solve(); + sys.Solve(g); FreeAllTemporary(); - return r; } void SolveSpace::MenuFile(int id) { diff --git a/solvespace.h b/solvespace.h index 650689b..0d721ee 100644 --- a/solvespace.h +++ b/solvespace.h @@ -162,12 +162,14 @@ public: void WriteJacobian(int eqTag, int paramTag); void EvalJacobian(void); + void WriteEquationsExceptFor(hConstraint hc, hGroup hg); + void FindWhichToRemoveToFixJacobian(Group *g); void SolveBySubstitution(void); static bool IsDragged(hParam p); bool NewtonSolve(int tag); - bool Solve(void); + void Solve(Group *g); }; @@ -236,7 +238,7 @@ public: void GenerateAll(bool andSolve); void GenerateAll(bool andSolve, int first, int last); - bool SolveGroup(hGroup hg); + void SolveGroup(hGroup hg); void ForceReferences(void); // The system to be solved. diff --git a/system.cpp b/system.cpp index df50b00..b7a6254 100644 --- a/system.cpp +++ b/system.cpp @@ -349,17 +349,60 @@ bool System::NewtonSolve(int tag) { } } while(iter++ < 50 && !converged); - if(converged) { - return true; - } else { - dbp("no convergence"); - return false; + return converged; +} + +void System::WriteEquationsExceptFor(hConstraint hc, hGroup hg) { + int i; + // Generate all the equations from constraints in this group + for(i = 0; i < SS.constraint.n; i++) { + Constraint *c = &(SS.constraint.elem[i]); + if(c->group.v != hg.v) continue; + if(c->h.v == hc.v) continue; + + c->Generate(&eq); + } + // And the equations from entities + for(i = 0; i < SS.entity.n; i++) { + Entity *e = &(SS.entity.elem[i]); + if(e->group.v != hg.v) continue; + + e->GenerateEquations(&eq); + } + // And from the groups themselves + (SS.GetGroup(hg))->GenerateEquations(&eq); +} + +void System::FindWhichToRemoveToFixJacobian(Group *g) { + int i; + (g->solved.remove).Clear(); + + for(i = 0; i < SS.constraint.n; i++) { + Constraint *c = &(SS.constraint.elem[i]); + if(c->group.v != g->h.v) continue; + + param.ClearTags(); + eq.Clear(); + WriteEquationsExceptFor(c->h, g->h); + eq.ClearTags(); + + WriteJacobian(0, 0); + EvalJacobian(); + + int rank = GaussJordan(); + if(rank == mat.m) { + // We fixed it by removing this constraint + (g->solved.remove).Add(&(c->h)); + } } } -bool System::Solve(void) { +void System::Solve(Group *g) { + g->solved.remove.Clear(); + + WriteEquationsExceptFor(Constraint::NO_CONSTRAINT, g->h); + int i, j = 0; - /* dbp("%d equations", eq.n); for(i = 0; i < eq.n; i++) { @@ -391,6 +434,12 @@ bool System::Solve(void) { } */ int rank = GaussJordan(); + if(rank != mat.m) { + FindWhichToRemoveToFixJacobian(g); + g->solved.how = Group::SINGULAR_JACOBIAN; + TextWindow::ReportHowGroupSolved(g->h); + return; + } /* dbp("bound states:"); for(j = 0; j < mat.n; j++) { @@ -417,8 +466,13 @@ bool System::Solve(void) { // The main param table keeps track of what was assumed. pp->assumed = (p->tag == VAR_ASSUMED); } + if(g->solved.how != Group::SOLVED_OKAY) { + g->solved.how = Group::SOLVED_OKAY; + TextWindow::ReportHowGroupSolved(g->h); + } + } else { + g->solved.how = Group::DIDNT_CONVERGE; + TextWindow::ReportHowGroupSolved(g->h); } - - return true; } diff --git a/textwin.cpp b/textwin.cpp index 11e6181..f160f8c 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -5,15 +5,16 @@ const TextWindow::Color TextWindow::fgColors[] = { { 'd', RGB(255, 255, 255) }, { 'l', RGB(100, 100, 255) }, { 't', RGB(255, 200, 0) }, - { 'h', RGB(170, 0, 0) }, + { 'h', RGB( 90, 90, 90) }, { 's', RGB( 40, 255, 40) }, { 'm', RGB(200, 200, 0) }, { 'r', RGB( 0, 0, 0) }, + { 'x', RGB(255, 20, 20) }, { 0, 0 }, }; const TextWindow::Color TextWindow::bgColors[] = { { 'd', RGB( 0, 0, 0) }, - { 't', RGB( 40, 20, 40) }, + { 't', RGB( 34, 15, 15) }, { 'a', RGB( 20, 20, 20) }, { 'r', RGB(255, 255, 255) }, { 0, 0 }, @@ -59,7 +60,7 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) { int fg = 'd', bg = 'd'; int link = NOT_A_LINK; DWORD data = 0; - LinkFunction *f = NULL; + LinkFunction *f = NULL, *h = NULL; c = 0; while(*fmt) { @@ -91,6 +92,7 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) { link = NOT_A_LINK; data = 0; f = NULL; + h = NULL; break; case 'F': @@ -121,6 +123,10 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) { f = va_arg(vl, LinkFunction *); break; + case 'h': + h = va_arg(vl, LinkFunction *); + break; + case 'D': data = va_arg(vl, DWORD); break; @@ -142,6 +148,7 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) { meta[r][c].link = link; meta[r][c].data = data; meta[r][c].f = f; + meta[r][c].h = h; c++; } @@ -160,6 +167,9 @@ done: void TextWindow::Show(void) { if(!(SS.GW.pending.operation)) SS.GW.ClearPending(); + SS.GW.GroupSelection(); +#define gs (SS.GW.gs) + ShowHeader(); if(SS.GW.pending.description) { @@ -174,8 +184,7 @@ void TextWindow::Show(void) { // fall through case SCREEN_LIST_OF_GROUPS: ShowListOfGroups(); break; case SCREEN_GROUP_INFO: ShowGroupInfo(); break; - case SCREEN_REQUEST_INFO: ShowRequestInfo(); break; - case SCREEN_CONSTRAINT_INFO: ShowConstraintInfo(); break; + case SCREEN_GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break; } } InvalidateText(); @@ -297,18 +306,32 @@ void TextWindow::ScreenActivateGroup(int link, DWORD v) { } SS.GW.ClearSuper(); } +void TextWindow::ReportHowGroupSolved(hGroup hg) { + SS.TW.OneScreenForwardTo(SCREEN_GROUP_SOLVE_INFO); + SS.TW.shown->group.v = hg.v; + SS.TW.Show(); +} +void TextWindow::ScreenHowGroupSolved(int link, DWORD v) { + if(SS.GW.activeGroup.v != v) { + ScreenActivateGroup(link, v); + } + SS.TW.OneScreenForwardTo(SCREEN_GROUP_SOLVE_INFO); + SS.TW.shown->group.v = v; +} void TextWindow::ShowListOfGroups(void) { - Printf(true, "%Ftactive show group-name%E"); + Printf(true, "%Ftactv show ok group-name%E"); int i; for(i = 0; i < SS.group.n; i++) { Group *g = &(SS.group.elem[i]); char *s = g->DescriptionString(); bool active = (g->h.v == SS.GW.activeGroup.v); bool shown = g->visible; + bool ok = (g->solved.how == Group::SOLVED_OKAY); bool ref = (g->h.v == Group::HGROUP_REFERENCES.v); - Printf(false, "%Bp%Fd " - "%Fp%D%f%s%Ll%s%E%s " + Printf(false, "%Bp%Fd " + "%Fp%D%f%s%Ll%s%E%s " "%Fp%D%f%Ll%s%E%s " + "%Fp%D%f%s%Ll%s%E " "%Fl%Ll%D%f%s", // Alternate between light and dark backgrounds, for readability (i & 1) ? 'd' : 'a', @@ -321,6 +344,10 @@ void TextWindow::ShowListOfGroups(void) { shown ? 's' : 'h', g->h.v, (&TextWindow::ScreenToggleGroupShown), shown ? "yes" : "no", shown ? "" : " ", + // Link to the errors, if a problem occured while solving + ok ? 's' : 'x', g->h.v, (&TextWindow::ScreenHowGroupSolved), + ok ? "ok" : "", + ok ? "" : "NO", // Link to a screen that gives more details on the group g->h.v, (&TextWindow::ScreenSelectGroup), s); } @@ -332,13 +359,25 @@ void TextWindow::ShowListOfGroups(void) { } +void TextWindow::ScreenHoverConstraint(int link, DWORD v) { + SS.GW.hover.Clear(); + SS.GW.hover.constraint.v = v; + SS.GW.hover.emphasized = true; +} +void TextWindow::ScreenHoverRequest(int link, DWORD v) { + SS.GW.hover.Clear(); + hRequest hr = { v }; + SS.GW.hover.entity = hr.entity(0); + SS.GW.hover.emphasized = true; +} void TextWindow::ScreenSelectConstraint(int link, DWORD v) { - SS.TW.OneScreenForwardTo(SCREEN_CONSTRAINT_INFO); - SS.TW.shown->constraint.v = v; + SS.GW.ClearSelection(); + SS.GW.selection[0].constraint.v = v; } void TextWindow::ScreenSelectRequest(int link, DWORD v) { - SS.TW.OneScreenForwardTo(SCREEN_REQUEST_INFO); - SS.TW.shown->request.v = v; + hRequest hr = { v }; + SS.GW.ClearSelection(); + SS.GW.selection[0].entity = hr.entity(0); } void TextWindow::ScreenChangeExtrudeSides(int link, DWORD v) { Group *g = SS.GetGroup(SS.TW.shown->group); @@ -396,9 +435,10 @@ void TextWindow::ShowGroupInfo(void) { if(r->group.v == shown->group.v) { char *s = r->DescriptionString(); - Printf(false, "%Bp %Fl%Ll%D%f%s%E", + Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", (a & 1) ? 'd' : 'a', - r->h.v, (&TextWindow::ScreenSelectRequest), s); + r->h.v, (&TextWindow::ScreenSelectRequest), + &(TextWindow::ScreenHoverRequest), s); a++; } } @@ -411,15 +451,46 @@ void TextWindow::ShowGroupInfo(void) { if(c->group.v == shown->group.v) { char *s = c->DescriptionString(); - Printf(false, "%Bp %Fl%Ll%D%f%s%E", + Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", (a & 1) ? 'd' : 'a', - c->h.v, (&TextWindow::ScreenSelectConstraint), s); + c->h.v, (&TextWindow::ScreenSelectConstraint), + (&TextWindow::ScreenHoverConstraint), s); a++; } } if(a == 0) Printf(false, "%Ba (none)"); } +void TextWindow::ShowGroupSolveInfo(void) { + Group *g = SS.group.FindById(shown->group); + Printf(true, "%FtGROUP %E%s", g->DescriptionString()); + + switch(g->solved.how) { + case Group::SOLVED_OKAY: + Printf(true, " %Fsgroup solved okay%E"); + break; + + case Group::DIDNT_CONVERGE: + Printf(true, " %FxSOLVE FAILED!%Fd no convergence"); + break; + + case Group::SINGULAR_JACOBIAN: { + Printf(true, "%FxSOLVE FAILED!%Fd inconsistent system"); + Printf(true, "remove any one of these to fix it"); + for(int i = 0; i < g->solved.remove.n; i++) { + hConstraint hc = g->solved.remove.elem[i]; + Constraint *c = SS.GetConstraint(hc); + Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", + (i & 1) ? 'd' : 'a', + c->h.v, (&TextWindow::ScreenSelectConstraint), + (&TextWindow::ScreenHoverConstraint), + c->DescriptionString()); + } + break; + } + } +} + void TextWindow::ShowRequestInfo(void) { Request *r = SS.GetRequest(shown->request); diff --git a/ui.h b/ui.h index d1aeae6..b3da56e 100644 --- a/ui.h +++ b/ui.h @@ -27,6 +27,7 @@ public: int link; DWORD data; LinkFunction *f; + LinkFunction *h; } meta[MAX_ROWS][MAX_COLS]; int top[MAX_ROWS]; // in half-line units, or -1 for unused @@ -41,9 +42,7 @@ public: // State for the screen that we are showing in the text window. static const int SCREEN_LIST_OF_GROUPS = 0; static const int SCREEN_GROUP_INFO = 1; - static const int SCREEN_REQUEST_INFO = 2; - static const int SCREEN_ENTIY_INFO = 3; - static const int SCREEN_CONSTRAINT_INFO = 4; + static const int SCREEN_GROUP_SOLVE_INFO = 2; typedef struct { int screen; hGroup group; @@ -56,6 +55,8 @@ public: int history; ShownState *shown; + static void ReportHowGroupSolved(hGroup hg); + void ShowHeader(void); // These are self-contained screens, that show some information about // the sketch. @@ -64,15 +65,20 @@ public: void ShowRequestInfo(void); void ShowEntityInfo(void); void ShowConstraintInfo(void); + void ShowGroupSolveInfo(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); + static void ScreenHowGroupSolved(int link, DWORD v); static void ScreenShowGroupsSpecial(int link, DWORD v); + static void ScreenHoverConstraint(int link, DWORD v); + static void ScreenHoverRequest(int link, DWORD v); static void ScreenSelectRequest(int link, DWORD v); static void ScreenSelectConstraint(int link, DWORD v); + static void ScreenChangeExtrudeSides(int link, DWORD v); static void ScreenChangeMeshCombine(int link, DWORD v); @@ -218,6 +224,8 @@ public: public: hEntity entity; hConstraint constraint; + + bool emphasized; void Draw(void); diff --git a/win32/w32main.cpp b/win32/w32main.cpp index a48cee2..82675f9 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -288,6 +288,9 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_LBUTTONDOWN: case WM_MOUSEMOVE: { + GraphicsWindow::Selection ps = SS.GW.hover; + SS.GW.hover.Clear(); + int x = LOWORD(lParam); int y = HIWORD(lParam); @@ -303,25 +306,30 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } if(r >= SS.TW.rows) { SetCursor(LoadCursor(NULL, IDC_ARROW)); - break; + goto done; } +#define META (SS.TW.meta[r][c]) if(msg == WM_MOUSEMOVE) { - if(SS.TW.meta[r][c].link) { + if(META.link) { SetCursor(LoadCursor(NULL, IDC_HAND)); + if(META.h) { + (META.h)(META.link, META.data); + } } else { SetCursor(LoadCursor(NULL, IDC_ARROW)); } } else { - if(SS.TW.meta[r][c].link && SS.TW.meta[r][c].f) { - (SS.TW.meta[r][c].f)( - SS.TW.meta[r][c].link, - SS.TW.meta[r][c].data - ); + if(META.link && META.f) { + (META.f)(META.link, META.data); SS.TW.Show(); InvalidateGraphics(); } } +done: + if(!ps.Equals(&(SS.GW.hover))) { + InvalidateGraphics(); + } break; } @@ -361,6 +369,12 @@ static BOOL ProcessKeyDown(WPARAM wParam) } } + if(wParam == VK_BACK && !GraphicsEditControlIsVisible()) { + TextWindow::ScreenNavigation('b', 0); + SS.TW.Show(); + return TRUE; + } + int c; switch(wParam) { case VK_OEM_PLUS: c = '+'; break;