Add undo/redo. This saves the param guesses, constraints, groups,
and requests to a separate list. It's messy, because I have to make a deep copy (e.g. of the remap list for the groups, or Expr * stuff) of some things. Others (e.g. the polygon or mesh) will be regenerated, so they should be discarded, but they must not get double-freed. In any case, works superficially. And fix a few memory leaks unrelated to this, and remove some dead code. [git-p4: depot-paths = "//depot/solvespace/": change = 1775]solver
parent
71391e6a55
commit
48612bde3d
1
Makefile
1
Makefile
|
@ -20,6 +20,7 @@ SSOBJS = $(OBJDIR)\solvespace.obj \
|
||||||
$(OBJDIR)\constraint.obj \
|
$(OBJDIR)\constraint.obj \
|
||||||
$(OBJDIR)\drawconstraint.obj \
|
$(OBJDIR)\drawconstraint.obj \
|
||||||
$(OBJDIR)\file.obj \
|
$(OBJDIR)\file.obj \
|
||||||
|
$(OBJDIR)\undoredo.obj \
|
||||||
$(OBJDIR)\system.obj \
|
$(OBJDIR)\system.obj \
|
||||||
$(OBJDIR)\polygon.obj \
|
$(OBJDIR)\polygon.obj \
|
||||||
$(OBJDIR)\mesh.obj \
|
$(OBJDIR)\mesh.obj \
|
||||||
|
|
|
@ -36,6 +36,11 @@ char *Constraint::DescriptionString(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Constraint::AddConstraint(Constraint *c) {
|
void Constraint::AddConstraint(Constraint *c) {
|
||||||
|
AddConstraint(c, true);
|
||||||
|
}
|
||||||
|
void Constraint::AddConstraint(Constraint *c, bool rememberForUndo) {
|
||||||
|
if(rememberForUndo) SS.UndoRemember();
|
||||||
|
|
||||||
SS.constraint.AddAndAssignId(c);
|
SS.constraint.AddAndAssignId(c);
|
||||||
|
|
||||||
SS.MarkGroupDirty(c->group);
|
SS.MarkGroupDirty(c->group);
|
||||||
|
@ -52,7 +57,7 @@ void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA)
|
||||||
c.ptA = ptA;
|
c.ptA = ptA;
|
||||||
c.ptB = ptB;
|
c.ptB = ptB;
|
||||||
c.entityA = entityA;
|
c.entityA = entityA;
|
||||||
AddConstraint(&c);
|
AddConstraint(&c, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
|
void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
|
||||||
|
@ -310,6 +315,7 @@ void Constraint::MenuConstrain(int id) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GraphicsWindow::MNU_SOLVE_NOW:
|
case GraphicsWindow::MNU_SOLVE_NOW:
|
||||||
|
SS.ReloadAllImported();
|
||||||
SS.GenerateAll(0, INT_MAX);
|
SS.GenerateAll(0, INT_MAX);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
11
file.cpp
11
file.cpp
|
@ -20,6 +20,9 @@ hGroup SolveSpace::CreateDefaultDrawingGroup(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SolveSpace::NewFile(void) {
|
void SolveSpace::NewFile(void) {
|
||||||
|
UndoClearStack(&redo);
|
||||||
|
UndoClearStack(&undo);
|
||||||
|
|
||||||
constraint.Clear();
|
constraint.Clear();
|
||||||
request.Clear();
|
request.Clear();
|
||||||
group.Clear();
|
group.Clear();
|
||||||
|
@ -131,7 +134,6 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
|
||||||
{ 'c', "Constraint.group.v", 'x', &(SS.sv.c.group.v) },
|
{ 'c', "Constraint.group.v", 'x', &(SS.sv.c.group.v) },
|
||||||
{ 'c', "Constraint.workplane.v", 'x', &(SS.sv.c.workplane.v) },
|
{ 'c', "Constraint.workplane.v", 'x', &(SS.sv.c.workplane.v) },
|
||||||
{ 'c', "Constraint.exprA", 'E', &(SS.sv.c.exprA) },
|
{ 'c', "Constraint.exprA", 'E', &(SS.sv.c.exprA) },
|
||||||
{ 'c', "Constraint.exprB", 'E', &(SS.sv.c.exprB) },
|
|
||||||
{ 'c', "Constraint.ptA.v", 'x', &(SS.sv.c.ptA.v) },
|
{ 'c', "Constraint.ptA.v", 'x', &(SS.sv.c.ptA.v) },
|
||||||
{ 'c', "Constraint.ptB.v", 'x', &(SS.sv.c.ptB.v) },
|
{ 'c', "Constraint.ptB.v", 'x', &(SS.sv.c.ptB.v) },
|
||||||
{ 'c', "Constraint.ptC.v", 'x', &(SS.sv.c.ptC.v) },
|
{ 'c', "Constraint.ptC.v", 'x', &(SS.sv.c.ptC.v) },
|
||||||
|
@ -373,7 +375,10 @@ bool SolveSpace::LoadEntitiesFromFile(char *file, EntityList *le, SMesh *m) {
|
||||||
char *key = line, *val = e+1;
|
char *key = line, *val = e+1;
|
||||||
LoadUsingTable(key, val);
|
LoadUsingTable(key, val);
|
||||||
} else if(strcmp(line, "AddGroup")==0) {
|
} else if(strcmp(line, "AddGroup")==0) {
|
||||||
|
// Don't leak memory; these get allocated whether we want them
|
||||||
|
// or not.
|
||||||
|
if(sv.g.exprA) Expr::FreeKeep(&(sv.g.exprA));
|
||||||
|
sv.g.remap.Clear();
|
||||||
} else if(strcmp(line, "AddParam")==0) {
|
} else if(strcmp(line, "AddParam")==0) {
|
||||||
|
|
||||||
} else if(strcmp(line, "AddEntity")==0) {
|
} else if(strcmp(line, "AddEntity")==0) {
|
||||||
|
@ -382,7 +387,7 @@ bool SolveSpace::LoadEntitiesFromFile(char *file, EntityList *le, SMesh *m) {
|
||||||
} else if(strcmp(line, "AddRequest")==0) {
|
} else if(strcmp(line, "AddRequest")==0) {
|
||||||
|
|
||||||
} else if(strcmp(line, "AddConstraint")==0) {
|
} else if(strcmp(line, "AddConstraint")==0) {
|
||||||
|
if(sv.c.exprA) Expr::FreeKeep(&(sv.c.exprA));
|
||||||
} else if(strcmp(line, VERSION_STRING)==0) {
|
} else if(strcmp(line, VERSION_STRING)==0) {
|
||||||
|
|
||||||
} else if(memcmp(line, "Triangle", 8)==0) {
|
} else if(memcmp(line, "Triangle", 8)==0) {
|
||||||
|
|
|
@ -21,8 +21,8 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||||
{ 1, "E&xit", MNU_EXIT, 0, mFile },
|
{ 1, "E&xit", MNU_EXIT, 0, mFile },
|
||||||
|
|
||||||
{ 0, "&Edit", 0, NULL },
|
{ 0, "&Edit", 0, NULL },
|
||||||
{ 1, "&Undo\tCtrl+Z", 0, NULL },
|
{ 1, "&Undo\tCtrl+Z", MNU_UNDO, 'Z'|C, mEdit },
|
||||||
{ 1, "&Redo\tCtrl+Y", 0, NULL },
|
{ 1, "&Redo\tCtrl+Y", MNU_REDO, 'Y'|C, mEdit },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
|
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
|
@ -277,11 +277,13 @@ void GraphicsWindow::EnsureValidActives(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// And update the checked state for various menus
|
||||||
bool locked = LockedInWorkplane();
|
bool locked = LockedInWorkplane();
|
||||||
CheckMenuById(MNU_FREE_IN_3D, !locked);
|
CheckMenuById(MNU_FREE_IN_3D, !locked);
|
||||||
CheckMenuById(MNU_SEL_WORKPLANE, locked);
|
CheckMenuById(MNU_SEL_WORKPLANE, locked);
|
||||||
|
|
||||||
// And update the checked state for various menus
|
SS.UndoEnableMenus();
|
||||||
|
|
||||||
switch(viewUnits) {
|
switch(viewUnits) {
|
||||||
case UNIT_MM:
|
case UNIT_MM:
|
||||||
case UNIT_INCHES:
|
case UNIT_INCHES:
|
||||||
|
@ -360,6 +362,14 @@ void GraphicsWindow::MenuEdit(int id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MNU_UNDO:
|
||||||
|
SS.UndoUndo();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MNU_REDO:
|
||||||
|
SS.UndoRedo();
|
||||||
|
break;
|
||||||
|
|
||||||
default: oops();
|
default: oops();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,6 +525,11 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
pending.constraint = hover.constraint;
|
pending.constraint = hover.constraint;
|
||||||
pending.operation = DRAGGING_CONSTRAINT;
|
pending.operation = DRAGGING_CONSTRAINT;
|
||||||
}
|
}
|
||||||
|
if(pending.operation != 0) {
|
||||||
|
// We just started a drag, so remember for the undo before
|
||||||
|
// the drag changes anything.
|
||||||
|
SS.UndoRemember();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, just hit test and give up; but don't hit test
|
// Otherwise, just hit test and give up; but don't hit test
|
||||||
// if the mouse is down, because then the user could hover
|
// if the mouse is down, because then the user could hover
|
||||||
|
@ -859,6 +874,11 @@ void GraphicsWindow::MouseMiddleDown(double x, double y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hRequest GraphicsWindow::AddRequest(int type) {
|
hRequest GraphicsWindow::AddRequest(int type) {
|
||||||
|
return AddRequest(type, true);
|
||||||
|
}
|
||||||
|
hRequest GraphicsWindow::AddRequest(int type, bool rememberForUndo) {
|
||||||
|
if(rememberForUndo) SS.UndoRemember();
|
||||||
|
|
||||||
Request r;
|
Request r;
|
||||||
memset(&r, 0, sizeof(r));
|
memset(&r, 0, sizeof(r));
|
||||||
r.group = activeGroup;
|
r.group = activeGroup;
|
||||||
|
@ -921,6 +941,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
case MNU_DATUM_POINT:
|
case MNU_DATUM_POINT:
|
||||||
hr = AddRequest(Request::DATUM_POINT);
|
hr = AddRequest(Request::DATUM_POINT);
|
||||||
SS.GetEntity(hr.entity(0))->PointForceTo(v);
|
SS.GetEntity(hr.entity(0))->PointForceTo(v);
|
||||||
|
ConstrainPointByHovered(hr.entity(0));
|
||||||
|
|
||||||
ClearSuper();
|
ClearSuper();
|
||||||
|
|
||||||
|
@ -947,8 +968,9 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
}
|
}
|
||||||
hRequest lns[4];
|
hRequest lns[4];
|
||||||
int i;
|
int i;
|
||||||
|
SS.UndoRemember();
|
||||||
for(i = 0; i < 4; i++) {
|
for(i = 0; i < 4; i++) {
|
||||||
lns[i] = AddRequest(Request::LINE_SEGMENT);
|
lns[i] = AddRequest(Request::LINE_SEGMENT, false);
|
||||||
}
|
}
|
||||||
for(i = 0; i < 4; i++) {
|
for(i = 0; i < 4; i++) {
|
||||||
Constraint::ConstrainCoincident(
|
Constraint::ConstrainCoincident(
|
||||||
|
@ -1136,6 +1158,8 @@ void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
|
||||||
void GraphicsWindow::EditControlDone(char *s) {
|
void GraphicsWindow::EditControlDone(char *s) {
|
||||||
Expr *e = Expr::From(s);
|
Expr *e = Expr::From(s);
|
||||||
if(e) {
|
if(e) {
|
||||||
|
SS.UndoRemember();
|
||||||
|
|
||||||
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();
|
||||||
|
@ -1193,6 +1217,8 @@ Vector GraphicsWindow::VectorFromProjs(double right, double up, double fwd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::Paint(int w, int h) {
|
void GraphicsWindow::Paint(int w, int h) {
|
||||||
|
SDWORD in = GetMilliseconds();
|
||||||
|
|
||||||
havePainted = true;
|
havePainted = true;
|
||||||
width = w; height = h;
|
width = w; height = h;
|
||||||
|
|
||||||
|
@ -1250,6 +1276,7 @@ void GraphicsWindow::Paint(int w, int h) {
|
||||||
// Draw the groups; this fills the polygons in a drawing group, and
|
// Draw the groups; this fills the polygons in a drawing group, and
|
||||||
// draws the solid mesh.
|
// draws the solid mesh.
|
||||||
(SS.GetGroup(activeGroup))->Draw();
|
(SS.GetGroup(activeGroup))->Draw();
|
||||||
|
dbp("done group: %d ms", GetMilliseconds() - in);
|
||||||
|
|
||||||
// First, draw the entire scene. We don't necessarily want to draw
|
// First, draw the entire scene. We don't necessarily want to draw
|
||||||
// things with normal z-buffering behaviour; e.g. we always want to
|
// things with normal z-buffering behaviour; e.g. we always want to
|
||||||
|
@ -1263,6 +1290,7 @@ void GraphicsWindow::Paint(int w, int h) {
|
||||||
SS.entity.elem[i].Draw(a);
|
SS.entity.elem[i].Draw(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dbp("done entity: %d ms", GetMilliseconds() - in);
|
||||||
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
// Draw the constraints
|
// Draw the constraints
|
||||||
|
@ -1280,5 +1308,9 @@ void GraphicsWindow::Paint(int w, int h) {
|
||||||
for(i = 0; i < MAX_SELECTED; i++) {
|
for(i = 0; i < MAX_SELECTED; i++) {
|
||||||
selection[i].Draw();
|
selection[i].Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dbp("till end: %d ms", GetMilliseconds() - in);
|
||||||
|
dbp("entity.n: %d", SS.entity.n);
|
||||||
|
dbp("param.n: %d", SS.param.n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ void Group::MenuGroup(int id) {
|
||||||
|
|
||||||
default: oops();
|
default: oops();
|
||||||
}
|
}
|
||||||
|
SS.UndoRemember();
|
||||||
SS.group.AddAndAssignId(&g);
|
SS.group.AddAndAssignId(&g);
|
||||||
if(g.type == IMPORTED) {
|
if(g.type == IMPORTED) {
|
||||||
SS.ReloadAllImported();
|
SS.ReloadAllImported();
|
||||||
|
@ -624,6 +624,7 @@ void Group::GeneratePolygon(void) {
|
||||||
polyError.notClosedAt = error;
|
polyError.notClosedAt = error;
|
||||||
poly.Clear();
|
poly.Clear();
|
||||||
}
|
}
|
||||||
|
edges.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
sketch.h
3
sketch.h
|
@ -375,7 +375,6 @@ public:
|
||||||
|
|
||||||
double val;
|
double val;
|
||||||
bool known;
|
bool known;
|
||||||
bool assumed;
|
|
||||||
|
|
||||||
// Used only in the solver
|
// Used only in the solver
|
||||||
hParam substd;
|
hParam substd;
|
||||||
|
@ -428,7 +427,6 @@ public:
|
||||||
|
|
||||||
// These are the parameters for the constraint.
|
// These are the parameters for the constraint.
|
||||||
Expr *exprA;
|
Expr *exprA;
|
||||||
Expr *exprB;
|
|
||||||
hEntity ptA;
|
hEntity ptA;
|
||||||
hEntity ptB;
|
hEntity ptB;
|
||||||
hEntity ptC;
|
hEntity ptC;
|
||||||
|
@ -443,6 +441,7 @@ public:
|
||||||
|
|
||||||
char *DescriptionString(void);
|
char *DescriptionString(void);
|
||||||
|
|
||||||
|
static void AddConstraint(Constraint *c, bool rememberForUndo);
|
||||||
static void AddConstraint(Constraint *c);
|
static void AddConstraint(Constraint *c);
|
||||||
static void MenuConstrain(int id);
|
static void MenuConstrain(int id);
|
||||||
|
|
||||||
|
|
34
solvespace.h
34
solvespace.h
|
@ -141,8 +141,7 @@ public:
|
||||||
|
|
||||||
// In general, the tag indicates the subsys that a variable/equation
|
// In general, the tag indicates the subsys that a variable/equation
|
||||||
// has been assigned to; these are exceptions for variables:
|
// has been assigned to; these are exceptions for variables:
|
||||||
static const int VAR_ASSUMED = 10000;
|
static const int VAR_SUBSTITUTED = 10000;
|
||||||
static const int VAR_SUBSTITUTED = 10001;
|
|
||||||
// and for equations:
|
// and for equations:
|
||||||
static const int EQ_SUBSTITUTED = 20000;
|
static const int EQ_SUBSTITUTED = 20000;
|
||||||
|
|
||||||
|
@ -218,13 +217,38 @@ public:
|
||||||
inline Param *GetParam (hParam h) { return param. FindById(h); }
|
inline Param *GetParam (hParam h) { return param. FindById(h); }
|
||||||
inline Group *GetGroup (hGroup h) { return group. FindById(h); }
|
inline Group *GetGroup (hGroup h) { return group. FindById(h); }
|
||||||
|
|
||||||
FILE *fh;
|
// The state for undo/redo
|
||||||
|
typedef struct {
|
||||||
|
IdList<Group,hGroup> group;
|
||||||
|
IdList<Request,hRequest> request;
|
||||||
|
IdList<Constraint,hConstraint> constraint;
|
||||||
|
IdList<Param,hParam> param;
|
||||||
|
hGroup activeGroup;
|
||||||
|
} UndoState;
|
||||||
|
static const int MAX_UNDO = 16;
|
||||||
|
typedef struct {
|
||||||
|
UndoState d[MAX_UNDO];
|
||||||
|
int cnt;
|
||||||
|
int write;
|
||||||
|
} UndoStack;
|
||||||
|
UndoStack undo;
|
||||||
|
UndoStack redo;
|
||||||
|
void UndoEnableMenus(void);
|
||||||
|
void UndoRemember(void);
|
||||||
|
void UndoUndo(void);
|
||||||
|
void UndoRedo(void);
|
||||||
|
void PushFromCurrentOnto(UndoStack *uk);
|
||||||
|
void PopOntoCurrentFrom(UndoStack *uk);
|
||||||
|
void UndoClearState(UndoState *ut);
|
||||||
|
void UndoClearStack(UndoStack *uk);
|
||||||
|
|
||||||
|
// File load/save routines, including the additional files that get
|
||||||
|
// loaded when we have import groups.
|
||||||
|
FILE *fh;
|
||||||
void Init(char *cmdLine);
|
void Init(char *cmdLine);
|
||||||
void AfterNewFile(void);
|
void AfterNewFile(void);
|
||||||
static void RemoveFromRecentList(char *file);
|
static void RemoveFromRecentList(char *file);
|
||||||
static void AddToRecentList(char *file);
|
static void AddToRecentList(char *file);
|
||||||
|
|
||||||
char saveFile[MAX_PATH];
|
char saveFile[MAX_PATH];
|
||||||
bool unsaved;
|
bool unsaved;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -256,6 +280,8 @@ public:
|
||||||
void MarkGroupDirty(hGroup hg);
|
void MarkGroupDirty(hGroup hg);
|
||||||
void MarkGroupDirtyByEntity(hEntity he);
|
void MarkGroupDirtyByEntity(hEntity he);
|
||||||
|
|
||||||
|
// Consistency checking on the sketch: stuff with missing dependencies
|
||||||
|
// will get deleted automatically.
|
||||||
struct {
|
struct {
|
||||||
int requests;
|
int requests;
|
||||||
int groups;
|
int groups;
|
||||||
|
|
|
@ -470,8 +470,6 @@ void System::Solve(Group *g) {
|
||||||
Param *pp = SS.GetParam(p->h);
|
Param *pp = SS.GetParam(p->h);
|
||||||
pp->val = val;
|
pp->val = val;
|
||||||
pp->known = true;
|
pp->known = true;
|
||||||
// The main param table keeps track of what was assumed.
|
|
||||||
pp->assumed = (p->tag == VAR_ASSUMED);
|
|
||||||
}
|
}
|
||||||
if(g->solved.how != Group::SOLVED_OKAY) {
|
if(g->solved.how != Group::SOLVED_OKAY) {
|
||||||
g->solved.how = Group::SOLVED_OKAY;
|
g->solved.how = Group::SOLVED_OKAY;
|
||||||
|
|
12
textwin.cpp
12
textwin.cpp
|
@ -540,6 +540,8 @@ void TextWindow::ScreenSelectRequest(int link, DWORD v) {
|
||||||
SS.GW.selection[0].entity = hr.entity(0);
|
SS.GW.selection[0].entity = hr.entity(0);
|
||||||
}
|
}
|
||||||
void TextWindow::ScreenChangeOneOrTwoSides(int link, DWORD v) {
|
void TextWindow::ScreenChangeOneOrTwoSides(int link, DWORD v) {
|
||||||
|
SS.UndoRemember();
|
||||||
|
|
||||||
Group *g = SS.GetGroup(SS.TW.shown->group);
|
Group *g = SS.GetGroup(SS.TW.shown->group);
|
||||||
if(g->subtype == Group::ONE_SIDED) {
|
if(g->subtype == Group::ONE_SIDED) {
|
||||||
g->subtype = Group::TWO_SIDED;
|
g->subtype = Group::TWO_SIDED;
|
||||||
|
@ -551,6 +553,8 @@ void TextWindow::ScreenChangeOneOrTwoSides(int link, DWORD v) {
|
||||||
SS.GW.ClearSuper();
|
SS.GW.ClearSuper();
|
||||||
}
|
}
|
||||||
void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) {
|
void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) {
|
||||||
|
SS.UndoRemember();
|
||||||
|
|
||||||
Group *g = SS.GetGroup(SS.TW.shown->group);
|
Group *g = SS.GetGroup(SS.TW.shown->group);
|
||||||
g->meshCombine = v;
|
g->meshCombine = v;
|
||||||
SS.MarkGroupDirty(g->h);
|
SS.MarkGroupDirty(g->h);
|
||||||
|
@ -558,6 +562,8 @@ void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) {
|
||||||
SS.GW.ClearSuper();
|
SS.GW.ClearSuper();
|
||||||
}
|
}
|
||||||
void TextWindow::ScreenColor(int link, DWORD v) {
|
void TextWindow::ScreenColor(int link, DWORD v) {
|
||||||
|
SS.UndoRemember();
|
||||||
|
|
||||||
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];
|
||||||
|
@ -578,6 +584,8 @@ void TextWindow::ScreenChangeGroupName(int link, DWORD v) {
|
||||||
SS.TW.edit.group.v = v;
|
SS.TW.edit.group.v = v;
|
||||||
}
|
}
|
||||||
void TextWindow::ScreenDeleteGroup(int link, DWORD v) {
|
void TextWindow::ScreenDeleteGroup(int link, DWORD v) {
|
||||||
|
SS.UndoRemember();
|
||||||
|
|
||||||
hGroup hg = SS.TW.shown->group;
|
hGroup hg = SS.TW.shown->group;
|
||||||
if(hg.v == SS.GW.activeGroup.v) {
|
if(hg.v == SS.GW.activeGroup.v) {
|
||||||
Error("This group is currently active; activate a different group "
|
Error("This group is currently active; activate a different group "
|
||||||
|
@ -763,6 +771,8 @@ void TextWindow::EditControlDone(char *s) {
|
||||||
case EDIT_TIMES_REPEATED: {
|
case EDIT_TIMES_REPEATED: {
|
||||||
Expr *e = Expr::From(s);
|
Expr *e = Expr::From(s);
|
||||||
if(e) {
|
if(e) {
|
||||||
|
SS.UndoRemember();
|
||||||
|
|
||||||
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();
|
||||||
|
@ -786,6 +796,8 @@ void TextWindow::EditControlDone(char *s) {
|
||||||
if(invalid || !*s) {
|
if(invalid || !*s) {
|
||||||
Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -");
|
Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -");
|
||||||
} else {
|
} else {
|
||||||
|
SS.UndoRemember();
|
||||||
|
|
||||||
Group *g = SS.GetGroup(edit.group);
|
Group *g = SS.GetGroup(edit.group);
|
||||||
g->name.strcpy(s);
|
g->name.strcpy(s);
|
||||||
}
|
}
|
||||||
|
|
3
ui.h
3
ui.h
|
@ -132,6 +132,8 @@ public:
|
||||||
MNU_UNITS_INCHES,
|
MNU_UNITS_INCHES,
|
||||||
MNU_UNITS_MM,
|
MNU_UNITS_MM,
|
||||||
// Edit
|
// Edit
|
||||||
|
MNU_UNDO,
|
||||||
|
MNU_REDO,
|
||||||
MNU_DELETE,
|
MNU_DELETE,
|
||||||
MNU_UNSELECT_ALL,
|
MNU_UNSELECT_ALL,
|
||||||
// Request
|
// Request
|
||||||
|
@ -248,6 +250,7 @@ public:
|
||||||
hConstraint constraintBeingEdited;
|
hConstraint constraintBeingEdited;
|
||||||
|
|
||||||
bool ConstrainPointByHovered(hEntity pt);
|
bool ConstrainPointByHovered(hEntity pt);
|
||||||
|
hRequest AddRequest(int type, bool rememberForUndo);
|
||||||
hRequest AddRequest(int type);
|
hRequest AddRequest(int type);
|
||||||
|
|
||||||
// The current selection.
|
// The current selection.
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
void SolveSpace::UndoRemember(void) {
|
||||||
|
PushFromCurrentOnto(&undo);
|
||||||
|
UndoClearStack(&redo);
|
||||||
|
UndoEnableMenus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SolveSpace::UndoUndo(void) {
|
||||||
|
if(undo.cnt <= 0) return;
|
||||||
|
|
||||||
|
PushFromCurrentOnto(&redo);
|
||||||
|
PopOntoCurrentFrom(&undo);
|
||||||
|
UndoEnableMenus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SolveSpace::UndoRedo(void) {
|
||||||
|
if(redo.cnt <= 0) return;
|
||||||
|
|
||||||
|
PushFromCurrentOnto(&undo);
|
||||||
|
PopOntoCurrentFrom(&redo);
|
||||||
|
UndoEnableMenus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SolveSpace::UndoEnableMenus(void) {
|
||||||
|
EnableMenuById(GraphicsWindow::MNU_UNDO, undo.cnt > 0);
|
||||||
|
EnableMenuById(GraphicsWindow::MNU_REDO, redo.cnt > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SolveSpace::PushFromCurrentOnto(UndoStack *uk) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(uk->cnt == MAX_UNDO) {
|
||||||
|
UndoClearState(&(uk->d[uk->write]));
|
||||||
|
// And then write in to this one again
|
||||||
|
} else {
|
||||||
|
(uk->cnt)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
UndoState *ut = &(uk->d[uk->write]);
|
||||||
|
ZERO(ut);
|
||||||
|
for(i = 0; i < group.n; i++) {
|
||||||
|
Group *src = &(group.elem[i]);
|
||||||
|
Group dest = *src;
|
||||||
|
// And then clean up all the stuff that needs to be a deep copy,
|
||||||
|
// and zero out all the dynamic stuff that will get regenerated.
|
||||||
|
dest.clean = false;
|
||||||
|
if(src->exprA) dest.exprA = src->exprA->DeepCopyKeep();
|
||||||
|
ZERO(&(dest.solved));
|
||||||
|
ZERO(&(dest.poly));
|
||||||
|
ZERO(&(dest.polyError));
|
||||||
|
ZERO(&(dest.mesh));
|
||||||
|
ZERO(&(dest.meshError));
|
||||||
|
|
||||||
|
ZERO(&(dest.remap));
|
||||||
|
src->remap.DeepCopyInto(&(dest.remap));
|
||||||
|
|
||||||
|
ZERO(&(dest.impMesh));
|
||||||
|
ZERO(&(dest.impEntity));
|
||||||
|
ut->group.Add(&dest);
|
||||||
|
}
|
||||||
|
for(i = 0; i < request.n; i++) {
|
||||||
|
ut->request.Add(&(request.elem[i]));
|
||||||
|
}
|
||||||
|
for(i = 0; i < constraint.n; i++) {
|
||||||
|
Constraint *src = &(constraint.elem[i]);
|
||||||
|
Constraint dest = *src;
|
||||||
|
if(src->exprA) dest.exprA = src->exprA->DeepCopyKeep();
|
||||||
|
ZERO(&(dest.dogd));
|
||||||
|
ut->constraint.Add(&dest);
|
||||||
|
}
|
||||||
|
for(i = 0; i < param.n; i++) {
|
||||||
|
ut->param.Add(&(param.elem[i]));
|
||||||
|
}
|
||||||
|
ut->activeGroup = SS.GW.activeGroup;
|
||||||
|
|
||||||
|
uk->write = WRAP(uk->write + 1, MAX_UNDO);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) {
|
||||||
|
if(uk->cnt <= 0) oops();
|
||||||
|
(uk->cnt)--;
|
||||||
|
uk->write = WRAP(uk->write - 1, MAX_UNDO);
|
||||||
|
|
||||||
|
UndoState *ut = &(uk->d[uk->write]);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
// Free everything in the main copy of the program before replacing it
|
||||||
|
for(i = 0; i < group.n; i++) {
|
||||||
|
Group *g = &(group.elem[i]);
|
||||||
|
if(g->exprA) Expr::FreeKeep(&(g->exprA));
|
||||||
|
g->poly.Clear();
|
||||||
|
g->mesh.Clear();
|
||||||
|
g->meshError.interferesAt.Clear();
|
||||||
|
g->remap.Clear();
|
||||||
|
g->impMesh.Clear();
|
||||||
|
g->impEntity.Clear();
|
||||||
|
}
|
||||||
|
for(i = 0; i < constraint.n; i++) {
|
||||||
|
Constraint *c = &(constraint.elem[i]);
|
||||||
|
if(c->exprA) Expr::FreeKeep(&(c->exprA));
|
||||||
|
}
|
||||||
|
group.Clear();
|
||||||
|
request.Clear();
|
||||||
|
constraint.Clear();
|
||||||
|
param.Clear();
|
||||||
|
|
||||||
|
// And then do a shallow copy of the state from the undo list
|
||||||
|
ut->group.MoveSelfInto(&group);
|
||||||
|
ut->request.MoveSelfInto(&request);
|
||||||
|
ut->constraint.MoveSelfInto(&constraint);
|
||||||
|
ut->param.MoveSelfInto(¶m);
|
||||||
|
SS.GW.activeGroup = ut->activeGroup;
|
||||||
|
|
||||||
|
// No need to free it, since a shallow copy was made above
|
||||||
|
ZERO(ut);
|
||||||
|
|
||||||
|
// And reset the state everywhere else in the program, since the
|
||||||
|
// sketch just changed a lot.
|
||||||
|
SS.GW.ClearSuper();
|
||||||
|
SS.TW.ClearSuper();
|
||||||
|
SS.ReloadAllImported();
|
||||||
|
SS.GenerateAll(0, INT_MAX);
|
||||||
|
later.showTW = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SolveSpace::UndoClearStack(UndoStack *uk) {
|
||||||
|
while(uk->cnt > 0) {
|
||||||
|
uk->write = WRAP(uk->write - 1, MAX_UNDO);
|
||||||
|
(uk->cnt)--;
|
||||||
|
UndoClearState(&(uk->d[uk->write]));
|
||||||
|
}
|
||||||
|
ZERO(uk); // for good measure
|
||||||
|
}
|
||||||
|
|
||||||
|
void SolveSpace::UndoClearState(UndoState *ut) {
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < ut->group.n; i++) {
|
||||||
|
Group *g = &(ut->group.elem[i]);
|
||||||
|
|
||||||
|
if(g->exprA) Expr::FreeKeep(&(g->exprA));
|
||||||
|
g->remap.Clear();
|
||||||
|
}
|
||||||
|
ut->group.Clear();
|
||||||
|
ut->request.Clear();
|
||||||
|
for(i = 0; i < ut->constraint.n; i++) {
|
||||||
|
Constraint *c = &(ut->constraint.elem[i]);
|
||||||
|
if(c->exprA) Expr::FreeKeep(&(c->exprA));
|
||||||
|
}
|
||||||
|
ut->constraint.Clear();
|
||||||
|
ut->param.Clear();
|
||||||
|
ZERO(ut);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
|
|
||||||
|
stupidity where stuff gets double-added to entity list
|
||||||
|
replace linear search through IdLists with faster (binary search?)
|
||||||
|
point face distance constraint
|
||||||
|
|
||||||
STL check for meshes, and T intersection removal
|
STL check for meshes, and T intersection removal
|
||||||
STL export
|
STL export
|
||||||
better triangle combining (Simplify()) for meshes
|
better triangle combining (Simplify()) for meshes
|
||||||
|
@ -8,10 +12,8 @@ DXF export
|
||||||
compress file format (binary?)
|
compress file format (binary?)
|
||||||
partitioned subsystems in the solver
|
partitioned subsystems in the solver
|
||||||
arbitrary color specification
|
arbitrary color specification
|
||||||
union/difference/interference check option for imports
|
|
||||||
undo/redo
|
|
||||||
TTF font text
|
TTF font text
|
||||||
display with proper formatting/units
|
display with proper formatting/units
|
||||||
more measurements
|
more measurements
|
||||||
|
reference dimensions (just to look at, no equations)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue