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:
parent
5bbb27fd8e
commit
7d4a4fbb76
@ -38,6 +38,7 @@ char *Constraint::DescriptionString(void) {
|
||||
void Constraint::AddConstraint(Constraint *c) {
|
||||
SS.constraint.AddAndAssignId(c);
|
||||
|
||||
SS.MarkGroupDirty(c->group);
|
||||
SS.GenerateAll();
|
||||
}
|
||||
|
||||
|
29
file.cpp
29
file.cpp
@ -2,6 +2,23 @@
|
||||
|
||||
#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) {
|
||||
constraint.Clear();
|
||||
request.Clear();
|
||||
@ -22,7 +39,7 @@ void SolveSpace::NewFile(void) {
|
||||
// Let's create three two-d coordinate systems, for the coordinate
|
||||
// planes; these are our references, present in every sketch.
|
||||
Request r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
ZERO(&r);
|
||||
r.type = Request::WORKPLANE;
|
||||
r.group = Group::HGROUP_REFERENCES;
|
||||
r.workplane = Entity::FREE_IN_3D;
|
||||
@ -36,15 +53,7 @@ void SolveSpace::NewFile(void) {
|
||||
r.h = Request::HREQUEST_REFERENCE_ZX;
|
||||
request.Add(&r);
|
||||
|
||||
// And an empty group, for the first stuff the user draws.
|
||||
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);
|
||||
CreateDefaultDrawingGroup();
|
||||
}
|
||||
|
||||
const SolveSpace::SaveTable SolveSpace::SAVED[] = {
|
||||
|
@ -245,8 +245,17 @@ void GraphicsWindow::EnsureValidActives(void) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i >= SS.group.n) oops();
|
||||
activeGroup = SS.group.elem[i].h;
|
||||
if(i >= SS.group.n) {
|
||||
// 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();
|
||||
change = true;
|
||||
}
|
||||
@ -309,7 +318,9 @@ void GraphicsWindow::MenuEdit(int id) {
|
||||
case MNU_UNSELECT_ALL:
|
||||
SS.GW.GroupSelection();
|
||||
if(SS.GW.gs.n == 0 && SS.GW.pending.operation == 0) {
|
||||
SS.TW.ClearSuper();
|
||||
if(!TextEditControlIsVisible()) {
|
||||
SS.TW.ClearSuper();
|
||||
}
|
||||
}
|
||||
SS.GW.ClearSuper();
|
||||
HideTextEditControl();
|
||||
@ -401,6 +412,7 @@ c:
|
||||
if(!he.isFromRequest()) continue;
|
||||
Request *r = SS.GetRequest(he.request());
|
||||
r->construction = !(r->construction);
|
||||
SS.MarkGroupDirty(r->group);
|
||||
}
|
||||
SS.GW.ClearSelection();
|
||||
SS.GenerateAll();
|
||||
@ -581,6 +593,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
UpdateDraggedPoint(pending.point, x, y);
|
||||
HitTestMakeSelection(mp);
|
||||
}
|
||||
SS.MarkGroupDirtyByEntity(pending.point);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
Vector p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3));
|
||||
SS.GetEntity(hr.entity(3))->PointForceTo(p2);
|
||||
|
||||
SS.MarkGroupDirtyByEntity(pending.point);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
|
||||
SS.GetEntity(hr.entity(1))->PointForceTo(center);
|
||||
|
||||
SS.MarkGroupDirtyByEntity(pending.point);
|
||||
break;
|
||||
}
|
||||
case DRAGGING_NEW_RADIUS:
|
||||
@ -615,6 +632,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
Point2d c2 = ProjectPoint(center);
|
||||
double r = c2.DistanceTo(mp)/scale;
|
||||
SS.GetEntity(circle->distance)->DistanceForceTo(r);
|
||||
|
||||
SS.MarkGroupDirtyByEntity(pending.circle);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -644,6 +663,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
}
|
||||
orig.mouse = mp;
|
||||
normal->NormalForceTo(Quaternion::From(u, v));
|
||||
|
||||
SS.MarkGroupDirtyByEntity(pending.normal);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -856,7 +877,7 @@ hRequest GraphicsWindow::AddRequest(int type) {
|
||||
// we mustn't try to solve until reasonable values have been supplied
|
||||
// for these new parameters, or else we'll get a numerical blowup.
|
||||
SS.GenerateAll(-1, -1);
|
||||
|
||||
SS.MarkGroupDirty(r.group);
|
||||
return r.h;
|
||||
}
|
||||
|
||||
@ -1118,7 +1139,9 @@ void GraphicsWindow::EditControlDone(char *s) {
|
||||
Constraint *c = SS.GetConstraint(constraintBeingEdited);
|
||||
Expr::FreeKeep(&(c->exprA));
|
||||
c->exprA = e->DeepCopyKeep();
|
||||
|
||||
HideGraphicsEditControl();
|
||||
SS.MarkGroupDirty(c->group);
|
||||
SS.GenerateAll();
|
||||
} else {
|
||||
Error("Not a valid number or expression: '%s'", s);
|
||||
|
@ -23,7 +23,7 @@ void Group::AddParam(IdList<Param,hParam> *param, hParam hp, double v) {
|
||||
|
||||
void Group::MenuGroup(int id) {
|
||||
Group g;
|
||||
memset(&g, 0, sizeof(g));
|
||||
ZERO(&g);
|
||||
g.visible = true;
|
||||
|
||||
if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
|
||||
@ -163,6 +163,8 @@ void Group::Activate(void) {
|
||||
} else {
|
||||
SS.GW.showFaces = false;
|
||||
}
|
||||
SS.MarkGroupDirty(h); // for good measure; shouldn't be needed
|
||||
SS.GenerateAll();
|
||||
SS.TW.Show();
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,26 @@ void SolveSpace::AfterNewFile(void) {
|
||||
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) {
|
||||
int i;
|
||||
for(i = 0; i < request.n; i++) {
|
||||
@ -133,18 +153,24 @@ bool SolveSpace::PruneConstraints(hGroup hg) {
|
||||
|
||||
void SolveSpace::GenerateAll(void) {
|
||||
int i;
|
||||
int firstShown = INT_MAX, lastShown = 0;
|
||||
// The references don't count, so start from group 1
|
||||
for(i = 1; i < group.n; i++) {
|
||||
int firstDirty = INT_MAX, lastVisible = 0;
|
||||
// Start from the first dirty group, and solve until the active group,
|
||||
// since all groups after the active group are hidden.
|
||||
for(i = 0; i < group.n; i++) {
|
||||
Group *g = &(group.elem[i]);
|
||||
if(g->visible || (g->solved.how != Group::SOLVED_OKAY)) {
|
||||
firstShown = min(firstShown, i);
|
||||
lastShown = max(lastShown, i);
|
||||
if((!g->clean) || (g->solved.how != Group::SOLVED_OKAY)) {
|
||||
firstDirty = min(firstDirty, i);
|
||||
}
|
||||
if(g->h.v == SS.GW.activeGroup.v) {
|
||||
lastVisible = i;
|
||||
}
|
||||
}
|
||||
// Even if nothing is shown, we have to keep going; the entities get
|
||||
// generated for hidden groups, even though they're not solved.
|
||||
GenerateAll(firstShown, lastShown);
|
||||
if(firstDirty == INT_MAX || lastVisible == 0) {
|
||||
// All clean; so just regenerate the entities, and don't solve anything.
|
||||
GenerateAll(-1, -1);
|
||||
} else {
|
||||
GenerateAll(firstDirty, lastVisible);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
ForceReferences();
|
||||
g->solved.how = Group::SOLVED_OKAY;
|
||||
g->clean = true;
|
||||
} else {
|
||||
if(i >= first && i <= last) {
|
||||
// The group falls inside the range, so really solve it,
|
||||
@ -200,6 +227,7 @@ void SolveSpace::GenerateAll(int first, int last) {
|
||||
SolveGroup(g->h);
|
||||
g->GeneratePolygon();
|
||||
g->GenerateMesh();
|
||||
g->clean = true;
|
||||
} else {
|
||||
// The group falls outside the range, so just assume that
|
||||
// it's good wherever we left it. The mesh is unchanged,
|
||||
|
@ -243,6 +243,7 @@ public:
|
||||
Constraint c;
|
||||
} sv;
|
||||
static void MenuFile(int id);
|
||||
hGroup CreateDefaultDrawingGroup(void);
|
||||
void NewFile(void);
|
||||
bool SaveToFile(char *filename);
|
||||
bool LoadFromFile(char *filename);
|
||||
@ -250,6 +251,7 @@ public:
|
||||
void ReloadAllImported(void);
|
||||
|
||||
void MarkGroupDirty(hGroup hg);
|
||||
void MarkGroupDirtyByEntity(hEntity he);
|
||||
|
||||
struct {
|
||||
int requests;
|
||||
|
32
textwin.cpp
32
textwin.cpp
@ -535,6 +535,7 @@ void TextWindow::ScreenChangeOneOrTwoSides(int link, DWORD v) {
|
||||
} else if(g->subtype == Group::TWO_SIDED) {
|
||||
g->subtype = Group::ONE_SIDED;
|
||||
} else oops();
|
||||
SS.MarkGroupDirty(g->h);
|
||||
SS.GenerateAll();
|
||||
SS.GW.ClearSuper();
|
||||
}
|
||||
@ -545,6 +546,7 @@ void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) {
|
||||
} else if(g->meshCombine == Group::COMBINE_AS_UNION) {
|
||||
g->meshCombine = Group::COMBINE_AS_DIFFERENCE;
|
||||
} else oops();
|
||||
SS.MarkGroupDirty(g->h);
|
||||
SS.GenerateAll();
|
||||
SS.GW.ClearSuper();
|
||||
}
|
||||
@ -552,6 +554,7 @@ void TextWindow::ScreenColor(int link, DWORD v) {
|
||||
Group *g = SS.GetGroup(SS.TW.shown->group);
|
||||
if(v < 0 || v >= MODEL_COLORS) return;
|
||||
g->color = SS.TW.modelColor[v];
|
||||
SS.MarkGroupDirty(g->h);
|
||||
SS.GenerateAll();
|
||||
SS.GW.ClearSuper();
|
||||
}
|
||||
@ -567,15 +570,32 @@ void TextWindow::ScreenChangeGroupName(int link, DWORD v) {
|
||||
SS.TW.edit.meaning = EDIT_GROUP_NAME;
|
||||
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) {
|
||||
Group *g = SS.group.FindById(shown->group);
|
||||
char *s, *s2;
|
||||
|
||||
s = (shown->group.v == Group::HGROUP_REFERENCES.v) ? "" : "(rename)";
|
||||
|
||||
Printf(true, "%FtGROUP %E%s %Fl%Ll%D%f%s%E",
|
||||
g->DescriptionString(),
|
||||
g->h.v, &TextWindow::ScreenChangeGroupName, s);
|
||||
if(shown->group.v == Group::HGROUP_REFERENCES.v) {
|
||||
Printf(true, "%FtGROUP %E%s", g->DescriptionString());
|
||||
} else {
|
||||
Printf(true, "%FtGROUP %E%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) {
|
||||
Printf(true, "%FtIMPORT %E '%s'", g->impFile);
|
||||
@ -722,6 +742,8 @@ void TextWindow::EditControlDone(char *s) {
|
||||
Group *g = SS.GetGroup(edit.group);
|
||||
Expr::FreeKeep(&(g->exprA));
|
||||
g->exprA = e->DeepCopyKeep();
|
||||
|
||||
SS.MarkGroupDirty(g->h);
|
||||
SS.GenerateAll();
|
||||
SS.TW.Show();
|
||||
} else {
|
||||
|
1
ui.h
1
ui.h
@ -89,6 +89,7 @@ public:
|
||||
static void ScreenToggleGroupShown(int link, DWORD v);
|
||||
static void ScreenHowGroupSolved(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 ScreenHoverRequest(int link, DWORD v);
|
||||
|
Loading…
Reference in New Issue
Block a user