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]
This commit is contained in:
Jonathan Westhues 2008-05-26 01:56:50 -08:00
parent 727ac126fb
commit b484c26493
12 changed files with 271 additions and 67 deletions

View File

@ -1,5 +1,7 @@
#include "solvespace.h" #include "solvespace.h"
const hConstraint Constraint::NO_CONSTRAINT = { 0 };
char *Constraint::DescriptionString(void) { char *Constraint::DescriptionString(void) {
static char ret[1024]; static char ret[1024];
sprintf(ret, "c%03x", h.v); sprintf(ret, "c%03x", h.v);

View File

@ -28,6 +28,7 @@ void Constraint::LineDrawOrGetDistance(Vector a, Vector b) {
double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true); double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true);
dogd.dmin = min(dogd.dmin, d); dogd.dmin = min(dogd.dmin, d);
} }
dogd.refp = (a.Plus(b)).ScaledBy(0.5);
} }
double Constraint::EllipticalInterpolation(double rx, double ry, double theta) { 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 { } else {
Point2d o = SS.GW.ProjectPoint(ref); Point2d o = SS.GW.ProjectPoint(ref);
dogd.dmin = min(dogd.dmin, o.DistanceTo(dogd.mp) - 10); 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 // same center; so if the point is visible, then this
// constraint cannot be selected. But that's okay. // constraint cannot be selected. But that's okay.
dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 3); dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 3);
dogd.refp = p;
} }
break; break;
} }
@ -449,7 +452,8 @@ s:
(type == AT_MIDPOINT) ? "M" : NULL))); (type == AT_MIDPOINT) ? "M" : NULL)));
glPopMatrix(); glPopMatrix();
} else { } 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); dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10);
} }
} else { } else {
@ -518,3 +522,12 @@ Vector Constraint::GetLabelPos(void) {
return p; return p;
} }
Vector Constraint::GetReferencePos(void) {
dogd.drawing = false;
dogd.refp = SS.GW.offset.ScaledBy(-1);
DrawOrGetDistance(NULL);
return dogd.refp;
}

View File

@ -522,6 +522,7 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) {
double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true); double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true);
dogd.dmin = min(dogd.dmin, d); dogd.dmin = min(dogd.dmin, d);
} }
dogd.refp = (a.Plus(b)).ScaledBy(0.5);
} }
void Entity::LineDrawOrGetDistanceOrEdge(Vector a, Vector b) { void Entity::LineDrawOrGetDistanceOrEdge(Vector a, Vector b) {
@ -555,6 +556,16 @@ double Entity::GetDistance(Point2d mp) {
return dogd.dmin; 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) { void Entity::DrawOrGetDistance(int order) {
Group *g = SS.GetGroup(group); Group *g = SS.GetGroup(group);
// If an entity is invisible, then it doesn't get shown, and it doesn't // 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_3D:
case NORMAL_IN_2D: { case NORMAL_IN_2D: {
if(order >= 0 && order != 2) break; if(order >= 0 && order != 2) break;
if(!SS.GW.showNormals) break;
int i; int i;
for(i = 0; i < 2; i++) { for(i = 0; i < 2; i++) {
@ -630,6 +640,7 @@ void Entity::DrawOrGetDistance(int order) {
} else { } else {
glxColor3d(0, 0.4, 0.4); glxColor3d(0, 0.4, 0.4);
if(i > 0) break; if(i > 0) break;
if(!SS.GW.showNormals) break;
} }
Quaternion q = NormalGetNum(); Quaternion q = NormalGetNum();

View File

@ -320,7 +320,7 @@ void GraphicsWindow::MenuEdit(int id) {
SS.GW.ClearSuper(); SS.GW.ClearSuper();
// And regenerate to get rid of what it generates, plus anything // And regenerate to get rid of what it generates, plus anything
// that references it (since the regen code checks for that). // 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.GW.EnsureValidActives();
SS.TW.Show(); SS.TW.Show();
break; break;
@ -624,7 +624,9 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
default: oops(); default: oops();
} }
SS.GW.GeneratePerSolving(); if(pending.operation != 0 && pending.operation != DRAGGING_CONSTRAINT) {
SS.GW.GeneratePerSolving();
}
havePainted = false; havePainted = false;
} }
@ -640,10 +642,34 @@ bool GraphicsWindow::Selection::IsEmpty(void) {
} }
void GraphicsWindow::Selection::Clear(void) { void GraphicsWindow::Selection::Clear(void) {
entity.v = constraint.v = 0; entity.v = constraint.v = 0;
emphasized = false;
} }
void GraphicsWindow::Selection::Draw(void) { void GraphicsWindow::Selection::Draw(void) {
if(entity.v) SS.GetEntity (entity )->Draw(-1); Vector refp;
if(constraint.v) SS.GetConstraint(constraint)->Draw(); 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) { void GraphicsWindow::ClearSuper(void) {
@ -998,7 +1024,9 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
break; break;
} }
} }
SS.GW.GeneratePerSolving(); if(pending.operation != 0 && pending.operation != DRAGGING_CONSTRAINT) {
SS.GW.GeneratePerSolving();
}
SS.TW.Show(); SS.TW.Show();
InvalidateGraphics(); InvalidateGraphics();

View File

@ -98,6 +98,7 @@ void Group::MenuGroup(int id) {
Vector offsetf = (e->WorkplaneGetOffset()).ScaledBy(-1); Vector offsetf = (e->WorkplaneGetOffset()).ScaledBy(-1);
SS.GW.AnimateOnto(quatf, offsetf); SS.GW.AnimateOnto(quatf, offsetf);
} }
TextWindow::ScreenSelectGroup(0, g.h.v);
SS.TW.Show(); SS.TW.Show();
} }
@ -517,7 +518,7 @@ void Group::Draw(void) {
glxFillMesh(&mesh); glxFillMesh(&mesh);
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
// glxDebugMesh(&mesh); glxDebugMesh(&mesh);
if(polyError.yes) { if(polyError.yes) {
glxColor4d(1, 0, 0, 0.2); glxColor4d(1, 0, 0, 0.2);
@ -534,7 +535,7 @@ void Group::Draw(void) {
glxWriteText("not closed contour!"); glxWriteText("not closed contour!");
glPopMatrix(); glPopMatrix();
} else { } else {
glxColor4d(0, 1.0, 1.0, 0.1); glxColor4d(0, 1.0, 1.0, 0.05);
glPolygonOffset(-1, -1); glPolygonOffset(-1, -1);
glxFillPolygon(&poly); glxFillPolygon(&poly);
glPolygonOffset(0, 0); glPolygonOffset(0, 0);

View File

@ -10,6 +10,7 @@ class hParam;
class Entity; class Entity;
class Param; class Param;
class hConstraint;
class hEquation; class hEquation;
class Equation; class Equation;
@ -88,6 +89,14 @@ public:
hGroup opB; hGroup opB;
bool visible; bool visible;
static const int SOLVED_OKAY = 0;
static const int DIDNT_CONVERGE = 10;
static const int SINGULAR_JACOBIAN = 11;
struct {
int how;
SList<hConstraint> remove;
} solved;
static const int WORKPLANE_BY_POINT_ORTHO = 6000; static const int WORKPLANE_BY_POINT_ORTHO = 6000;
static const int WORKPLANE_BY_LINE_SEGMENTS = 6001; static const int WORKPLANE_BY_LINE_SEGMENTS = 6001;
static const int EXTRUDE_ONE_SIDED = 7000; static const int EXTRUDE_ONE_SIDED = 7000;
@ -288,6 +297,7 @@ public:
Point2d mp; Point2d mp;
double dmin; double dmin;
SEdgeList *edges; SEdgeList *edges;
Vector refp;
} dogd; // state for drawing or getting distance (for hit testing) } dogd; // state for drawing or getting distance (for hit testing)
void LineDrawOrGetDistance(Vector a, Vector b); void LineDrawOrGetDistance(Vector a, Vector b);
void LineDrawOrGetDistanceOrEdge(Vector a, Vector b); void LineDrawOrGetDistanceOrEdge(Vector a, Vector b);
@ -296,6 +306,7 @@ public:
void Draw(int order); void Draw(int order);
double GetDistance(Point2d mp); double GetDistance(Point2d mp);
void GenerateEdges(SEdgeList *el); void GenerateEdges(SEdgeList *el);
Vector GetReferencePos(void);
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index); void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
void GenerateEquations(IdList<Equation,hEquation> *l); void GenerateEquations(IdList<Equation,hEquation> *l);
@ -328,6 +339,8 @@ public:
class Constraint { class Constraint {
public: public:
static const hConstraint NO_CONSTRAINT;
static const int USER_EQUATION = 10; static const int USER_EQUATION = 10;
static const int POINTS_COINCIDENT = 20; static const int POINTS_COINCIDENT = 20;
static const int PT_PT_DISTANCE = 30; static const int PT_PT_DISTANCE = 30;
@ -382,6 +395,7 @@ public:
bool drawing; bool drawing;
Point2d mp; Point2d mp;
double dmin; double dmin;
Vector refp;
} dogd; // state for drawing or getting distance (for hit testing) } dogd; // state for drawing or getting distance (for hit testing)
void LineDrawOrGetDistance(Vector a, Vector b); void LineDrawOrGetDistance(Vector a, Vector b);
void DrawOrGetDistance(Vector *labelPos); void DrawOrGetDistance(Vector *labelPos);
@ -391,6 +405,7 @@ public:
double GetDistance(Point2d mp); double GetDistance(Point2d mp);
Vector GetLabelPos(void); Vector GetLabelPos(void);
Vector GetReferencePos(void);
void Draw(void); void Draw(void);
bool HasLabel(void); bool HasLabel(void);

View File

@ -14,8 +14,8 @@ void SolveSpace::Init(char *cmdLine) {
TW.Init(); TW.Init();
GW.Init(); GW.Init();
GenerateAll(true, 0, INT_MAX);
TW.Show(); TW.Show();
GenerateAll(false, 0, INT_MAX);
} }
bool SolveSpace::PruneOrphans(void) { bool SolveSpace::PruneOrphans(void) {
@ -127,7 +127,8 @@ void SolveSpace::GenerateAll(bool andSolve) {
int firstShown = INT_MAX, lastShown = 0; int firstShown = INT_MAX, lastShown = 0;
// The references don't count, so start from group 1 // The references don't count, so start from group 1
for(i = 1; i < group.n; i++) { 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); firstShown = min(firstShown, i);
lastShown = max(lastShown, 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) { if(g->h.v == Group::HGROUP_REFERENCES.v) {
ForceReferences(); ForceReferences();
g->solved.how = Group::SOLVED_OKAY;
} else { } else {
if(i >= first && i <= last) { if(i >= first && i <= last) {
// The group falls inside the range, so really solve it, // 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; int i;
// Clear out the system to be solved. // Clear out the system to be solved.
sys.entity.Clear(); sys.entity.Clear();
@ -293,26 +295,9 @@ bool SolveSpace::SolveGroup(hGroup hg) {
p->known = false; p->known = false;
p->val = GetParam(p->h)->val; 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)); sys.Solve(g);
}
// 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();
FreeAllTemporary(); FreeAllTemporary();
return r;
} }
void SolveSpace::MenuFile(int id) { void SolveSpace::MenuFile(int id) {

View File

@ -162,12 +162,14 @@ public:
void WriteJacobian(int eqTag, int paramTag); void WriteJacobian(int eqTag, int paramTag);
void EvalJacobian(void); void EvalJacobian(void);
void WriteEquationsExceptFor(hConstraint hc, hGroup hg);
void FindWhichToRemoveToFixJacobian(Group *g);
void SolveBySubstitution(void); void SolveBySubstitution(void);
static bool IsDragged(hParam p); static bool IsDragged(hParam p);
bool NewtonSolve(int tag); bool NewtonSolve(int tag);
bool Solve(void); void Solve(Group *g);
}; };
@ -236,7 +238,7 @@ public:
void GenerateAll(bool andSolve); void GenerateAll(bool andSolve);
void GenerateAll(bool andSolve, int first, int last); void GenerateAll(bool andSolve, int first, int last);
bool SolveGroup(hGroup hg); void SolveGroup(hGroup hg);
void ForceReferences(void); void ForceReferences(void);
// The system to be solved. // The system to be solved.

View File

@ -349,17 +349,60 @@ bool System::NewtonSolve(int tag) {
} }
} while(iter++ < 50 && !converged); } while(iter++ < 50 && !converged);
if(converged) { return converged;
return true; }
} else {
dbp("no convergence"); void System::WriteEquationsExceptFor(hConstraint hc, hGroup hg) {
return false; 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) {
int i, j = 0; g->solved.remove.Clear();
WriteEquationsExceptFor(Constraint::NO_CONSTRAINT, g->h);
int i, j = 0;
/* /*
dbp("%d equations", eq.n); dbp("%d equations", eq.n);
for(i = 0; i < eq.n; i++) { for(i = 0; i < eq.n; i++) {
@ -391,6 +434,12 @@ bool System::Solve(void) {
} */ } */
int rank = GaussJordan(); int rank = GaussJordan();
if(rank != mat.m) {
FindWhichToRemoveToFixJacobian(g);
g->solved.how = Group::SINGULAR_JACOBIAN;
TextWindow::ReportHowGroupSolved(g->h);
return;
}
/* dbp("bound states:"); /* dbp("bound states:");
for(j = 0; j < mat.n; j++) { 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. // The main param table keeps track of what was assumed.
pp->assumed = (p->tag == VAR_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;
} }

View File

@ -5,15 +5,16 @@ const TextWindow::Color TextWindow::fgColors[] = {
{ 'd', RGB(255, 255, 255) }, { 'd', RGB(255, 255, 255) },
{ 'l', RGB(100, 100, 255) }, { 'l', RGB(100, 100, 255) },
{ 't', RGB(255, 200, 0) }, { 't', RGB(255, 200, 0) },
{ 'h', RGB(170, 0, 0) }, { 'h', RGB( 90, 90, 90) },
{ 's', RGB( 40, 255, 40) }, { 's', RGB( 40, 255, 40) },
{ 'm', RGB(200, 200, 0) }, { 'm', RGB(200, 200, 0) },
{ 'r', RGB( 0, 0, 0) }, { 'r', RGB( 0, 0, 0) },
{ 'x', RGB(255, 20, 20) },
{ 0, 0 }, { 0, 0 },
}; };
const TextWindow::Color TextWindow::bgColors[] = { const TextWindow::Color TextWindow::bgColors[] = {
{ 'd', RGB( 0, 0, 0) }, { 'd', RGB( 0, 0, 0) },
{ 't', RGB( 40, 20, 40) }, { 't', RGB( 34, 15, 15) },
{ 'a', RGB( 20, 20, 20) }, { 'a', RGB( 20, 20, 20) },
{ 'r', RGB(255, 255, 255) }, { 'r', RGB(255, 255, 255) },
{ 0, 0 }, { 0, 0 },
@ -59,7 +60,7 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) {
int fg = 'd', bg = 'd'; int fg = 'd', bg = 'd';
int link = NOT_A_LINK; int link = NOT_A_LINK;
DWORD data = 0; DWORD data = 0;
LinkFunction *f = NULL; LinkFunction *f = NULL, *h = NULL;
c = 0; c = 0;
while(*fmt) { while(*fmt) {
@ -91,6 +92,7 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) {
link = NOT_A_LINK; link = NOT_A_LINK;
data = 0; data = 0;
f = NULL; f = NULL;
h = NULL;
break; break;
case 'F': case 'F':
@ -121,6 +123,10 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) {
f = va_arg(vl, LinkFunction *); f = va_arg(vl, LinkFunction *);
break; break;
case 'h':
h = va_arg(vl, LinkFunction *);
break;
case 'D': case 'D':
data = va_arg(vl, DWORD); data = va_arg(vl, DWORD);
break; break;
@ -142,6 +148,7 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) {
meta[r][c].link = link; meta[r][c].link = link;
meta[r][c].data = data; meta[r][c].data = data;
meta[r][c].f = f; meta[r][c].f = f;
meta[r][c].h = h;
c++; c++;
} }
@ -160,6 +167,9 @@ done:
void TextWindow::Show(void) { void TextWindow::Show(void) {
if(!(SS.GW.pending.operation)) SS.GW.ClearPending(); if(!(SS.GW.pending.operation)) SS.GW.ClearPending();
SS.GW.GroupSelection();
#define gs (SS.GW.gs)
ShowHeader(); ShowHeader();
if(SS.GW.pending.description) { if(SS.GW.pending.description) {
@ -174,8 +184,7 @@ void TextWindow::Show(void) {
// fall through // fall through
case SCREEN_LIST_OF_GROUPS: ShowListOfGroups(); break; case SCREEN_LIST_OF_GROUPS: ShowListOfGroups(); break;
case SCREEN_GROUP_INFO: ShowGroupInfo(); break; case SCREEN_GROUP_INFO: ShowGroupInfo(); break;
case SCREEN_REQUEST_INFO: ShowRequestInfo(); break; case SCREEN_GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break;
case SCREEN_CONSTRAINT_INFO: ShowConstraintInfo(); break;
} }
} }
InvalidateText(); InvalidateText();
@ -297,18 +306,32 @@ void TextWindow::ScreenActivateGroup(int link, DWORD v) {
} }
SS.GW.ClearSuper(); 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) { void TextWindow::ShowListOfGroups(void) {
Printf(true, "%Ftactive show group-name%E"); Printf(true, "%Ftactv show ok group-name%E");
int i; int i;
for(i = 0; i < SS.group.n; i++) { for(i = 0; i < SS.group.n; i++) {
Group *g = &(SS.group.elem[i]); Group *g = &(SS.group.elem[i]);
char *s = g->DescriptionString(); char *s = g->DescriptionString();
bool active = (g->h.v == SS.GW.activeGroup.v); bool active = (g->h.v == SS.GW.activeGroup.v);
bool shown = g->visible; bool shown = g->visible;
bool ok = (g->solved.how == Group::SOLVED_OKAY);
bool ref = (g->h.v == Group::HGROUP_REFERENCES.v); bool ref = (g->h.v == Group::HGROUP_REFERENCES.v);
Printf(false, "%Bp%Fd " Printf(false, "%Bp%Fd "
"%Fp%D%f%s%Ll%s%E%s " "%Fp%D%f%s%Ll%s%E%s "
"%Fp%D%f%Ll%s%E%s " "%Fp%D%f%Ll%s%E%s "
"%Fp%D%f%s%Ll%s%E "
"%Fl%Ll%D%f%s", "%Fl%Ll%D%f%s",
// Alternate between light and dark backgrounds, for readability // Alternate between light and dark backgrounds, for readability
(i & 1) ? 'd' : 'a', (i & 1) ? 'd' : 'a',
@ -321,6 +344,10 @@ void TextWindow::ShowListOfGroups(void) {
shown ? 's' : 'h', g->h.v, (&TextWindow::ScreenToggleGroupShown), shown ? 's' : 'h', g->h.v, (&TextWindow::ScreenToggleGroupShown),
shown ? "yes" : "no", shown ? "yes" : "no",
shown ? "" : " ", 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 // Link to a screen that gives more details on the group
g->h.v, (&TextWindow::ScreenSelectGroup), s); 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) { void TextWindow::ScreenSelectConstraint(int link, DWORD v) {
SS.TW.OneScreenForwardTo(SCREEN_CONSTRAINT_INFO); SS.GW.ClearSelection();
SS.TW.shown->constraint.v = v; SS.GW.selection[0].constraint.v = v;
} }
void TextWindow::ScreenSelectRequest(int link, DWORD v) { void TextWindow::ScreenSelectRequest(int link, DWORD v) {
SS.TW.OneScreenForwardTo(SCREEN_REQUEST_INFO); hRequest hr = { v };
SS.TW.shown->request.v = v; SS.GW.ClearSelection();
SS.GW.selection[0].entity = hr.entity(0);
} }
void TextWindow::ScreenChangeExtrudeSides(int link, DWORD v) { void TextWindow::ScreenChangeExtrudeSides(int link, DWORD v) {
Group *g = SS.GetGroup(SS.TW.shown->group); Group *g = SS.GetGroup(SS.TW.shown->group);
@ -396,9 +435,10 @@ void TextWindow::ShowGroupInfo(void) {
if(r->group.v == shown->group.v) { if(r->group.v == shown->group.v) {
char *s = r->DescriptionString(); 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', (a & 1) ? 'd' : 'a',
r->h.v, (&TextWindow::ScreenSelectRequest), s); r->h.v, (&TextWindow::ScreenSelectRequest),
&(TextWindow::ScreenHoverRequest), s);
a++; a++;
} }
} }
@ -411,15 +451,46 @@ void TextWindow::ShowGroupInfo(void) {
if(c->group.v == shown->group.v) { if(c->group.v == shown->group.v) {
char *s = c->DescriptionString(); 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', (a & 1) ? 'd' : 'a',
c->h.v, (&TextWindow::ScreenSelectConstraint), s); c->h.v, (&TextWindow::ScreenSelectConstraint),
(&TextWindow::ScreenHoverConstraint), s);
a++; a++;
} }
} }
if(a == 0) Printf(false, "%Ba (none)"); 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) { void TextWindow::ShowRequestInfo(void) {
Request *r = SS.GetRequest(shown->request); Request *r = SS.GetRequest(shown->request);

14
ui.h
View File

@ -27,6 +27,7 @@ public:
int link; int link;
DWORD data; DWORD data;
LinkFunction *f; LinkFunction *f;
LinkFunction *h;
} meta[MAX_ROWS][MAX_COLS]; } meta[MAX_ROWS][MAX_COLS];
int top[MAX_ROWS]; // in half-line units, or -1 for unused 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. // State for the screen that we are showing in the text window.
static const int SCREEN_LIST_OF_GROUPS = 0; static const int SCREEN_LIST_OF_GROUPS = 0;
static const int SCREEN_GROUP_INFO = 1; static const int SCREEN_GROUP_INFO = 1;
static const int SCREEN_REQUEST_INFO = 2; static const int SCREEN_GROUP_SOLVE_INFO = 2;
static const int SCREEN_ENTIY_INFO = 3;
static const int SCREEN_CONSTRAINT_INFO = 4;
typedef struct { typedef struct {
int screen; int screen;
hGroup group; hGroup group;
@ -56,6 +55,8 @@ public:
int history; int history;
ShownState *shown; ShownState *shown;
static void ReportHowGroupSolved(hGroup hg);
void ShowHeader(void); void ShowHeader(void);
// These are self-contained screens, that show some information about // These are self-contained screens, that show some information about
// the sketch. // the sketch.
@ -64,15 +65,20 @@ public:
void ShowRequestInfo(void); void ShowRequestInfo(void);
void ShowEntityInfo(void); void ShowEntityInfo(void);
void ShowConstraintInfo(void); void ShowConstraintInfo(void);
void ShowGroupSolveInfo(void);
void OneScreenForwardTo(int screen); void OneScreenForwardTo(int screen);
static void ScreenSelectGroup(int link, DWORD v); static void ScreenSelectGroup(int link, DWORD v);
static void ScreenActivateGroup(int link, DWORD v); static void ScreenActivateGroup(int link, DWORD v);
static void ScreenToggleGroupShown(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 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 ScreenSelectRequest(int link, DWORD v);
static void ScreenSelectConstraint(int link, DWORD v); static void ScreenSelectConstraint(int link, DWORD v);
static void ScreenChangeExtrudeSides(int link, DWORD v); static void ScreenChangeExtrudeSides(int link, DWORD v);
static void ScreenChangeMeshCombine(int link, DWORD v); static void ScreenChangeMeshCombine(int link, DWORD v);
@ -219,6 +225,8 @@ public:
hEntity entity; hEntity entity;
hConstraint constraint; hConstraint constraint;
bool emphasized;
void Draw(void); void Draw(void);
void Clear(void); void Clear(void);

View File

@ -288,6 +288,9 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
case WM_MOUSEMOVE: { case WM_MOUSEMOVE: {
GraphicsWindow::Selection ps = SS.GW.hover;
SS.GW.hover.Clear();
int x = LOWORD(lParam); int x = LOWORD(lParam);
int y = HIWORD(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) { if(r >= SS.TW.rows) {
SetCursor(LoadCursor(NULL, IDC_ARROW)); SetCursor(LoadCursor(NULL, IDC_ARROW));
break; goto done;
} }
#define META (SS.TW.meta[r][c])
if(msg == WM_MOUSEMOVE) { if(msg == WM_MOUSEMOVE) {
if(SS.TW.meta[r][c].link) { if(META.link) {
SetCursor(LoadCursor(NULL, IDC_HAND)); SetCursor(LoadCursor(NULL, IDC_HAND));
if(META.h) {
(META.h)(META.link, META.data);
}
} else { } else {
SetCursor(LoadCursor(NULL, IDC_ARROW)); SetCursor(LoadCursor(NULL, IDC_ARROW));
} }
} else { } else {
if(SS.TW.meta[r][c].link && SS.TW.meta[r][c].f) { if(META.link && META.f) {
(SS.TW.meta[r][c].f)( (META.f)(META.link, META.data);
SS.TW.meta[r][c].link,
SS.TW.meta[r][c].data
);
SS.TW.Show(); SS.TW.Show();
InvalidateGraphics(); InvalidateGraphics();
} }
} }
done:
if(!ps.Equals(&(SS.GW.hover))) {
InvalidateGraphics();
}
break; 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; int c;
switch(wParam) { switch(wParam) {
case VK_OEM_PLUS: c = '+'; break; case VK_OEM_PLUS: c = '+'; break;