We now have selective solve; a group (and all the groups afterward)

becomes dirty when the user makes a change, and only the dirty
groups get solved. That's a huge effective speedup.

Also add delete function for groups. That has an interesting issue;
it actually ends up recursing on GenerateAll(), since GenerateAll()
calls ClearSuper(), ClearSuper() might need to recreate a group (if
all the groups were deleted), and that would activate the group,
which calls GenerateAll. The right solution is probably a deferred
execution mechanism, where you can schedule something to happen
before we go idle, but not do it right now.

[git-p4: depot-paths = "//depot/solvespace/": change = 1771]
This commit is contained in:
Jonathan Westhues 2008-06-02 03:43:27 -08:00
parent 5bbb27fd8e
commit 7d4a4fbb76
8 changed files with 117 additions and 29 deletions

View File

@ -38,6 +38,7 @@ char *Constraint::DescriptionString(void) {
void Constraint::AddConstraint(Constraint *c) { void Constraint::AddConstraint(Constraint *c) {
SS.constraint.AddAndAssignId(c); SS.constraint.AddAndAssignId(c);
SS.MarkGroupDirty(c->group);
SS.GenerateAll(); SS.GenerateAll();
} }

View File

@ -2,6 +2,23 @@
#define VERSION_STRING "±²³SolveSpaceREVa" #define VERSION_STRING "±²³SolveSpaceREVa"
hGroup SolveSpace::CreateDefaultDrawingGroup(void) {
Group g;
ZERO(&g);
// And an empty group, for the first stuff the user draws.
g.visible = true;
g.type = Group::DRAWING_WORKPLANE;
g.subtype = Group::WORKPLANE_BY_POINT_ORTHO;
g.predef.q = Quaternion::From(1, 0, 0, 0);
hRequest hr = Request::HREQUEST_REFERENCE_XY;
g.predef.origin = hr.entity(1);
g.name.strcpy("draw-in-plane");
group.AddAndAssignId(&g);
SS.GetGroup(g.h)->activeWorkplane = g.h.entity(0);
return g.h;
}
void SolveSpace::NewFile(void) { void SolveSpace::NewFile(void) {
constraint.Clear(); constraint.Clear();
request.Clear(); request.Clear();
@ -22,7 +39,7 @@ void SolveSpace::NewFile(void) {
// Let's create three two-d coordinate systems, for the coordinate // Let's create three two-d coordinate systems, for the coordinate
// planes; these are our references, present in every sketch. // planes; these are our references, present in every sketch.
Request r; Request r;
memset(&r, 0, sizeof(r)); ZERO(&r);
r.type = Request::WORKPLANE; r.type = Request::WORKPLANE;
r.group = Group::HGROUP_REFERENCES; r.group = Group::HGROUP_REFERENCES;
r.workplane = Entity::FREE_IN_3D; r.workplane = Entity::FREE_IN_3D;
@ -36,15 +53,7 @@ void SolveSpace::NewFile(void) {
r.h = Request::HREQUEST_REFERENCE_ZX; r.h = Request::HREQUEST_REFERENCE_ZX;
request.Add(&r); request.Add(&r);
// And an empty group, for the first stuff the user draws. CreateDefaultDrawingGroup();
g.type = Group::DRAWING_WORKPLANE;
g.subtype = Group::WORKPLANE_BY_POINT_ORTHO;
g.predef.q = Quaternion::From(1, 0, 0, 0);
hRequest hr = Request::HREQUEST_REFERENCE_XY;
g.predef.origin = hr.entity(1);
g.name.strcpy("draw-in-plane");
group.AddAndAssignId(&g);
SS.GetGroup(g.h)->activeWorkplane = g.h.entity(0);
} }
const SolveSpace::SaveTable SolveSpace::SAVED[] = { const SolveSpace::SaveTable SolveSpace::SAVED[] = {

View File

@ -245,8 +245,17 @@ void GraphicsWindow::EnsureValidActives(void) {
break; break;
} }
} }
if(i >= SS.group.n) oops(); if(i >= SS.group.n) {
activeGroup = SS.group.elem[i].h; // This can happen if the user deletes all the groups in the
// sketch. It's difficult to prevent that, because the last
// group might have been deleted automatically, because it failed
// a dependency. There needs to be something, so create a plane
// drawing group and activate that. They should never be able
// to delete the references, though.
activeGroup = SS.CreateDefaultDrawingGroup();
} else {
activeGroup = SS.group.elem[i].h;
}
SS.GetGroup(activeGroup)->Activate(); SS.GetGroup(activeGroup)->Activate();
change = true; change = true;
} }
@ -309,7 +318,9 @@ void GraphicsWindow::MenuEdit(int id) {
case MNU_UNSELECT_ALL: case MNU_UNSELECT_ALL:
SS.GW.GroupSelection(); SS.GW.GroupSelection();
if(SS.GW.gs.n == 0 && SS.GW.pending.operation == 0) { if(SS.GW.gs.n == 0 && SS.GW.pending.operation == 0) {
SS.TW.ClearSuper(); if(!TextEditControlIsVisible()) {
SS.TW.ClearSuper();
}
} }
SS.GW.ClearSuper(); SS.GW.ClearSuper();
HideTextEditControl(); HideTextEditControl();
@ -401,6 +412,7 @@ c:
if(!he.isFromRequest()) continue; if(!he.isFromRequest()) continue;
Request *r = SS.GetRequest(he.request()); Request *r = SS.GetRequest(he.request());
r->construction = !(r->construction); r->construction = !(r->construction);
SS.MarkGroupDirty(r->group);
} }
SS.GW.ClearSelection(); SS.GW.ClearSelection();
SS.GenerateAll(); SS.GenerateAll();
@ -581,6 +593,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
UpdateDraggedPoint(pending.point, x, y); UpdateDraggedPoint(pending.point, x, y);
HitTestMakeSelection(mp); HitTestMakeSelection(mp);
} }
SS.MarkGroupDirtyByEntity(pending.point);
break; break;
} }
case DRAGGING_NEW_CUBIC_POINT: { case DRAGGING_NEW_CUBIC_POINT: {
@ -594,6 +607,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
SS.GetEntity(hr.entity(2))->PointForceTo(p1); SS.GetEntity(hr.entity(2))->PointForceTo(p1);
Vector p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3)); Vector p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3));
SS.GetEntity(hr.entity(3))->PointForceTo(p2); SS.GetEntity(hr.entity(3))->PointForceTo(p2);
SS.MarkGroupDirtyByEntity(pending.point);
break; break;
} }
case DRAGGING_NEW_ARC_POINT: { case DRAGGING_NEW_ARC_POINT: {
@ -606,6 +621,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
Vector center = (ona.Plus(onb)).ScaledBy(0.5); Vector center = (ona.Plus(onb)).ScaledBy(0.5);
SS.GetEntity(hr.entity(1))->PointForceTo(center); SS.GetEntity(hr.entity(1))->PointForceTo(center);
SS.MarkGroupDirtyByEntity(pending.point);
break; break;
} }
case DRAGGING_NEW_RADIUS: case DRAGGING_NEW_RADIUS:
@ -615,6 +632,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
Point2d c2 = ProjectPoint(center); Point2d c2 = ProjectPoint(center);
double r = c2.DistanceTo(mp)/scale; double r = c2.DistanceTo(mp)/scale;
SS.GetEntity(circle->distance)->DistanceForceTo(r); SS.GetEntity(circle->distance)->DistanceForceTo(r);
SS.MarkGroupDirtyByEntity(pending.circle);
break; break;
} }
@ -644,6 +663,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
} }
orig.mouse = mp; orig.mouse = mp;
normal->NormalForceTo(Quaternion::From(u, v)); normal->NormalForceTo(Quaternion::From(u, v));
SS.MarkGroupDirtyByEntity(pending.normal);
break; break;
} }
@ -856,7 +877,7 @@ hRequest GraphicsWindow::AddRequest(int type) {
// we mustn't try to solve until reasonable values have been supplied // we mustn't try to solve until reasonable values have been supplied
// for these new parameters, or else we'll get a numerical blowup. // for these new parameters, or else we'll get a numerical blowup.
SS.GenerateAll(-1, -1); SS.GenerateAll(-1, -1);
SS.MarkGroupDirty(r.group);
return r.h; return r.h;
} }
@ -1118,7 +1139,9 @@ void GraphicsWindow::EditControlDone(char *s) {
Constraint *c = SS.GetConstraint(constraintBeingEdited); Constraint *c = SS.GetConstraint(constraintBeingEdited);
Expr::FreeKeep(&(c->exprA)); Expr::FreeKeep(&(c->exprA));
c->exprA = e->DeepCopyKeep(); c->exprA = e->DeepCopyKeep();
HideGraphicsEditControl(); HideGraphicsEditControl();
SS.MarkGroupDirty(c->group);
SS.GenerateAll(); SS.GenerateAll();
} else { } else {
Error("Not a valid number or expression: '%s'", s); Error("Not a valid number or expression: '%s'", s);

View File

@ -23,7 +23,7 @@ void Group::AddParam(IdList<Param,hParam> *param, hParam hp, double v) {
void Group::MenuGroup(int id) { void Group::MenuGroup(int id) {
Group g; Group g;
memset(&g, 0, sizeof(g)); ZERO(&g);
g.visible = true; g.visible = true;
if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) { if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
@ -163,6 +163,8 @@ void Group::Activate(void) {
} else { } else {
SS.GW.showFaces = false; SS.GW.showFaces = false;
} }
SS.MarkGroupDirty(h); // for good measure; shouldn't be needed
SS.GenerateAll();
SS.TW.Show(); SS.TW.Show();
} }

View File

@ -28,6 +28,26 @@ void SolveSpace::AfterNewFile(void) {
TW.Show(); TW.Show();
} }
void SolveSpace::MarkGroupDirtyByEntity(hEntity he) {
Entity *e = SS.GetEntity(he);
MarkGroupDirty(e->group);
}
void SolveSpace::MarkGroupDirty(hGroup hg) {
int i;
bool go = false;
for(i = 0; i < group.n; i++) {
Group *g = &(group.elem[i]);
if(g->h.v == hg.v) {
go = true;
}
if(go) {
g->clean = false;
}
}
unsaved = true;
}
bool SolveSpace::PruneOrphans(void) { bool SolveSpace::PruneOrphans(void) {
int i; int i;
for(i = 0; i < request.n; i++) { for(i = 0; i < request.n; i++) {
@ -133,18 +153,24 @@ bool SolveSpace::PruneConstraints(hGroup hg) {
void SolveSpace::GenerateAll(void) { void SolveSpace::GenerateAll(void) {
int i; int i;
int firstShown = INT_MAX, lastShown = 0; int firstDirty = INT_MAX, lastVisible = 0;
// The references don't count, so start from group 1 // Start from the first dirty group, and solve until the active group,
for(i = 1; i < group.n; i++) { // since all groups after the active group are hidden.
for(i = 0; i < group.n; i++) {
Group *g = &(group.elem[i]); Group *g = &(group.elem[i]);
if(g->visible || (g->solved.how != Group::SOLVED_OKAY)) { if((!g->clean) || (g->solved.how != Group::SOLVED_OKAY)) {
firstShown = min(firstShown, i); firstDirty = min(firstDirty, i);
lastShown = max(lastShown, i); }
if(g->h.v == SS.GW.activeGroup.v) {
lastVisible = i;
} }
} }
// Even if nothing is shown, we have to keep going; the entities get if(firstDirty == INT_MAX || lastVisible == 0) {
// generated for hidden groups, even though they're not solved. // All clean; so just regenerate the entities, and don't solve anything.
GenerateAll(firstShown, lastShown); GenerateAll(-1, -1);
} else {
GenerateAll(firstDirty, lastVisible);
}
} }
void SolveSpace::GenerateAll(int first, int last) { void SolveSpace::GenerateAll(int first, int last) {
@ -193,6 +219,7 @@ void SolveSpace::GenerateAll(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; g->solved.how = Group::SOLVED_OKAY;
g->clean = true;
} 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,
@ -200,6 +227,7 @@ void SolveSpace::GenerateAll(int first, int last) {
SolveGroup(g->h); SolveGroup(g->h);
g->GeneratePolygon(); g->GeneratePolygon();
g->GenerateMesh(); g->GenerateMesh();
g->clean = true;
} else { } else {
// The group falls outside the range, so just assume that // The group falls outside the range, so just assume that
// it's good wherever we left it. The mesh is unchanged, // it's good wherever we left it. The mesh is unchanged,

View File

@ -243,6 +243,7 @@ public:
Constraint c; Constraint c;
} sv; } sv;
static void MenuFile(int id); static void MenuFile(int id);
hGroup CreateDefaultDrawingGroup(void);
void NewFile(void); void NewFile(void);
bool SaveToFile(char *filename); bool SaveToFile(char *filename);
bool LoadFromFile(char *filename); bool LoadFromFile(char *filename);
@ -250,6 +251,7 @@ public:
void ReloadAllImported(void); void ReloadAllImported(void);
void MarkGroupDirty(hGroup hg); void MarkGroupDirty(hGroup hg);
void MarkGroupDirtyByEntity(hEntity he);
struct { struct {
int requests; int requests;

View File

@ -535,6 +535,7 @@ void TextWindow::ScreenChangeOneOrTwoSides(int link, DWORD v) {
} else if(g->subtype == Group::TWO_SIDED) { } else if(g->subtype == Group::TWO_SIDED) {
g->subtype = Group::ONE_SIDED; g->subtype = Group::ONE_SIDED;
} else oops(); } else oops();
SS.MarkGroupDirty(g->h);
SS.GenerateAll(); SS.GenerateAll();
SS.GW.ClearSuper(); SS.GW.ClearSuper();
} }
@ -545,6 +546,7 @@ void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) {
} else if(g->meshCombine == Group::COMBINE_AS_UNION) { } else if(g->meshCombine == Group::COMBINE_AS_UNION) {
g->meshCombine = Group::COMBINE_AS_DIFFERENCE; g->meshCombine = Group::COMBINE_AS_DIFFERENCE;
} else oops(); } else oops();
SS.MarkGroupDirty(g->h);
SS.GenerateAll(); SS.GenerateAll();
SS.GW.ClearSuper(); SS.GW.ClearSuper();
} }
@ -552,6 +554,7 @@ void TextWindow::ScreenColor(int link, DWORD v) {
Group *g = SS.GetGroup(SS.TW.shown->group); Group *g = SS.GetGroup(SS.TW.shown->group);
if(v < 0 || v >= MODEL_COLORS) return; if(v < 0 || v >= MODEL_COLORS) return;
g->color = SS.TW.modelColor[v]; g->color = SS.TW.modelColor[v];
SS.MarkGroupDirty(g->h);
SS.GenerateAll(); SS.GenerateAll();
SS.GW.ClearSuper(); SS.GW.ClearSuper();
} }
@ -567,15 +570,32 @@ void TextWindow::ScreenChangeGroupName(int link, DWORD v) {
SS.TW.edit.meaning = EDIT_GROUP_NAME; SS.TW.edit.meaning = EDIT_GROUP_NAME;
SS.TW.edit.group.v = v; SS.TW.edit.group.v = v;
} }
void TextWindow::ScreenDeleteGroup(int link, DWORD v) {
hGroup hg = SS.TW.shown->group;
if(hg.v == SS.GW.activeGroup.v) {
Error("This group is currently active; activate a different group "
"before proceeding.");
return;
}
SS.group.RemoveById(SS.TW.shown->group);
// This is a major change, so let's re-solve everything.
SS.TW.ClearSuper();
SS.GW.ClearSuper();
SS.GenerateAll(0, INT_MAX);
}
void TextWindow::ShowGroupInfo(void) { void TextWindow::ShowGroupInfo(void) {
Group *g = SS.group.FindById(shown->group); Group *g = SS.group.FindById(shown->group);
char *s, *s2; char *s, *s2;
s = (shown->group.v == Group::HGROUP_REFERENCES.v) ? "" : "(rename)"; if(shown->group.v == Group::HGROUP_REFERENCES.v) {
Printf(true, "%FtGROUP %E%s", g->DescriptionString());
Printf(true, "%FtGROUP %E%s %Fl%Ll%D%f%s%E", } else {
g->DescriptionString(), Printf(true, "%FtGROUP %E%s "
g->h.v, &TextWindow::ScreenChangeGroupName, s); "(%Fl%Ll%D%frename%E / %Fl%Ll%D%fdel%E)",
g->DescriptionString(),
g->h.v, &TextWindow::ScreenChangeGroupName,
g->h.v, &TextWindow::ScreenDeleteGroup);
}
if(g->type == Group::IMPORTED) { if(g->type == Group::IMPORTED) {
Printf(true, "%FtIMPORT %E '%s'", g->impFile); Printf(true, "%FtIMPORT %E '%s'", g->impFile);
@ -722,6 +742,8 @@ void TextWindow::EditControlDone(char *s) {
Group *g = SS.GetGroup(edit.group); Group *g = SS.GetGroup(edit.group);
Expr::FreeKeep(&(g->exprA)); Expr::FreeKeep(&(g->exprA));
g->exprA = e->DeepCopyKeep(); g->exprA = e->DeepCopyKeep();
SS.MarkGroupDirty(g->h);
SS.GenerateAll(); SS.GenerateAll();
SS.TW.Show(); SS.TW.Show();
} else { } else {

1
ui.h
View File

@ -89,6 +89,7 @@ public:
static void ScreenToggleGroupShown(int link, DWORD v); static void ScreenToggleGroupShown(int link, DWORD v);
static void ScreenHowGroupSolved(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 ScreenDeleteGroup(int link, DWORD v);
static void ScreenHoverConstraint(int link, DWORD v); static void ScreenHoverConstraint(int link, DWORD v);
static void ScreenHoverRequest(int link, DWORD v); static void ScreenHoverRequest(int link, DWORD v);