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:
parent
727ac126fb
commit
b484c26493
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
13
entity.cpp
13
entity.cpp
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
15
sketch.h
15
sketch.h
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
72
system.cpp
72
system.cpp
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
103
textwin.cpp
103
textwin.cpp
|
@ -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
14
ui.h
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user