diff --git a/Makefile b/Makefile index 10a07acc..2c4a0a77 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ DEFINES = /D_WIN32_WINNT=0x500 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN32_LEAN_AND_MEAN /DWIN32 -CFLAGS = /W3 /nologo -I..\common\win32 /O2 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /Zi /I. /EHs +CFLAGS = /W3 /nologo -I..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /Zi /I. /EHs HEADERS = ..\common\win32\freeze.h ui.h solvespace.h dsc.h sketch.h expr.h polygon.h diff --git a/dsc.h b/dsc.h index 2acb1aa9..61f47e86 100644 --- a/dsc.h +++ b/dsc.h @@ -154,6 +154,11 @@ public: n = dest; // and elemsAllocated is untouched, because we didn't resize } + void RemoveById(H h) { + ClearTags(); + FindById(h)->tag = 1; + RemoveTagged(); + } void MoveSelfInto(IdList *l) { memcpy(l, this, sizeof(*this)); diff --git a/graphicswin.cpp b/graphicswin.cpp index 631dd307..59ee12dc 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -263,6 +263,8 @@ void GraphicsWindow::EnsureValidActives(void) { CheckMenuById(MNU_SHOW_TEXT_WND, SS.GW.showTextWindow); CheckMenuById(MNU_SOLVE_AUTO, (SS.GW.solving == SOLVE_ALWAYS)); + + if(change) SS.TW.Show(); } void GraphicsWindow::MenuEdit(int id) { @@ -287,18 +289,6 @@ void GraphicsWindow::MenuEdit(int id) { } if(r.v && !r.IsFromReferences()) { SS.request.Tag(r, 1); - int j; - for(j = 0; j < SS.constraint.n; j++) { - Constraint *c = &(SS.constraint.elem[j]); - if(((c->ptA).request().v == r.v) || - ((c->ptB).request().v == r.v) || - ((c->ptC).request().v == r.v) || - ((c->entityA).request().v == r.v) || - ((c->entityB).request().v == r.v)) - { - SS.constraint.Tag(c->h, 1); - } - } } if(s->constraint.v) { SS.constraint.Tag(s->constraint, 1); @@ -307,9 +297,14 @@ void GraphicsWindow::MenuEdit(int id) { SS.request.RemoveTagged(); SS.constraint.RemoveTagged(); - SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS); + // Forget any mention of the just-deleted entity SS.GW.ClearSelection(); SS.GW.hover.Clear(); + // And regenerate to get rid of what it generates, plus anything + // that references it (since the regen code checks for that). + SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS); + SS.GW.EnsureValidActives(); + SS.TW.Show(); break; } @@ -814,6 +809,10 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { break; case MNU_RECTANGLE: { + if(SS.GW.activeWorkplane.v == Entity::FREE_IN_3D.v) { + Error("Can't draw rectangle in 3d; select a workplane first."); + break; + } hRequest lns[4]; int i; for(i = 0; i < 4; i++) { diff --git a/polygon.cpp b/polygon.cpp index 54bba79a..86227f52 100644 --- a/polygon.cpp +++ b/polygon.cpp @@ -137,7 +137,10 @@ Vector SContour::ComputeNormal(void) { } bool SContour::IsClockwiseProjdToNormal(Vector n) { - if(n.Magnitude() < 0.01) oops(); + // Degenerate things might happen as we draw; doesn't really matter + // what we do then. + if(n.Magnitude() < 0.01) return true; + // An arbitrary 2d coordinate system that has n as its normal Vector u = n.Normal(0); Vector v = n.Normal(1); diff --git a/solvespace.cpp b/solvespace.cpp index bfadcb56..9f435afb 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -17,9 +17,116 @@ void SolveSpace::Init(char *cmdLine) { TW.Show(); } +bool SolveSpace::PruneOrphans(void) { + int i; + for(i = 0; i < request.n; i++) { + Request *r = &(request.elem[i]); + if(GroupExists(r->group)) continue; + + (deleted.requests)++; + request.RemoveById(r->h); + return true; + } + + for(i = 0; i < constraint.n; i++) { + Constraint *c = &(constraint.elem[i]); + if(GroupExists(c->group)) continue; + + (deleted.constraints)++; + constraint.RemoveById(c->h); + return true; + } + return false; +} + +bool SolveSpace::GroupsInOrder(hGroup before, hGroup after) { + if(before.v == 0) return true; + if(after.v == 0) return true; + + int beforep = -1, afterp = -1; + int i; + for(i = 0; i < group.n; i++) { + Group *g = &(group.elem[i]); + if(g->h.v == before.v) beforep = i; + if(g->h.v == after.v) afterp = i; + } + if(beforep < 0 || afterp < 0) return false; + if(beforep >= afterp) return false; + return true; +} + +bool SolveSpace::GroupExists(hGroup hg) { + // A nonexistent group is not acceptable + return group.FindByIdNoOops(hg) ? true : false; +} +bool SolveSpace::EntityExists(hEntity he) { + // A nonexstient entity is acceptable, though, usually just means it + // doesn't apply. + if(he.v == Entity::NO_ENTITY.v) return true; + return entity.FindByIdNoOops(he) ? true : false; +} + +bool SolveSpace::PruneGroups(hGroup hg) { + Group *g = GetGroup(hg); + if(GroupsInOrder(g->opA, hg) && + GroupsInOrder(g->opB, hg) && + EntityExists(g->wrkpl.origin) && + EntityExists(g->wrkpl.entityB) && + EntityExists(g->wrkpl.entityC)) + { + return false; + } + (deleted.groups)++; + group.RemoveById(g->h); + return true; +} + +bool SolveSpace::PruneRequests(hGroup hg) { + int i; + for(i = 0; i < entity.n; i++) { + Entity *e = &(entity.elem[i]); + if(e->group.v != hg.v) continue; + + if(EntityExists(e->workplane)) continue; + + if(!e->h.isFromRequest()) oops(); + + (deleted.requests)++; + request.RemoveById(e->h.request()); + return true; + } + return false; +} + +bool SolveSpace::PruneConstraints(hGroup hg) { + int i; + for(i = 0; i < constraint.n; i++) { + Constraint *c = &(constraint.elem[i]); + if(c->group.v != hg.v) continue; + + if(EntityExists(c->workplane) && + EntityExists(c->ptA) && + EntityExists(c->ptB) && + EntityExists(c->ptC) && + EntityExists(c->entityA) && + EntityExists(c->entityB)) + { + continue; + } + + (deleted.constraints)++; + constraint.RemoveById(c->h); + return true; + } + return false; +} + void SolveSpace::GenerateAll(bool andSolve) { int i, j; + while(PruneOrphans()) + ; + // Don't lose our numerical guesses when we regenerate. IdList prev; param.MoveSelfInto(&prev); @@ -34,15 +141,25 @@ void SolveSpace::GenerateAll(bool andSolve) { for(i = 0; i < group.n; i++) { Group *g = &(group.elem[i]); + // The group may depend on entities or other groups, to define its + // workplane geometry or for its operands. Those must already exist + // in a previous group, so check them before generating. + if(PruneGroups(g->h)) + goto pruned; + for(j = 0; j < request.n; j++) { Request *r = &(request.elem[j]); if(r->group.v != g->h.v) continue; r->Generate(&entity, ¶m); } - g->Generate(&entity, ¶m); + // The requests and constraints depend on stuff in this or the + // previous group, so check them after generating. + if(PruneRequests(g->h) || PruneConstraints(g->h)) + goto pruned; + // Use the previous values for params that we've seen before, as // initial guesses for the solver. for(j = 0; j < param.n; j++) { @@ -66,6 +183,33 @@ void SolveSpace::GenerateAll(bool andSolve) { prev.Clear(); InvalidateGraphics(); + + if(deleted.requests > 0 || deleted.constraints > 0 || deleted.groups > 0) { + // Don't display any errors until we've regenerated fully. The + // sketch is not necessarily in a consistent state until we've + // pruned any orphaned etc. objects, and the message loop for the + // messagebox could allow us to repaint and crash. But now we must + // be fine. + Error("Additional sketch elements were deleted, because they depend " + "on the element that was just deleted explicitly. These " + "include: \r\n" + " %d request%s\r\n" + " %d constraint%s\r\n" + " %d group%s\r\n\r\n" + "Choose Edit -> Undo to undelete all elements.", + deleted.requests, deleted.requests == 1 ? "" : "s", + deleted.constraints, deleted.constraints == 1 ? "" : "s", + deleted.groups, deleted.groups == 1 ? "" : "s"); + memset(&deleted, 0, sizeof(deleted)); + } + return; + +pruned: + // Restore the numerical guesses + param.Clear(); + prev.MoveSelfInto(¶m); + // Try again + GenerateAll(andSolve); } void SolveSpace::ForceReferences(void) { diff --git a/solvespace.h b/solvespace.h index c2e5267b..84888c99 100644 --- a/solvespace.h +++ b/solvespace.h @@ -204,6 +204,18 @@ public: bool SaveToFile(char *filename); bool LoadFromFile(char *filename); + struct { + int requests; + int groups; + int constraints; + } deleted; + bool GroupExists(hGroup hg); + bool PruneOrphans(void); + bool EntityExists(hEntity he); + bool GroupsInOrder(hGroup before, hGroup after); + bool PruneGroups(hGroup hg); + bool PruneRequests(hGroup hg); + bool PruneConstraints(hGroup hg); void GenerateAll(bool andSolve); bool SolveGroup(hGroup hg); diff --git a/textwin.cpp b/textwin.cpp index 4efebb8b..e7efcd60 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -215,8 +215,6 @@ void TextWindow::ScreenNavigation(int link, DWORD v) { void TextWindow::ShowHeader(void) { ClearScreen(); - SS.GW.EnsureValidActives(); - char *cd = (SS.GW.activeWorkplane.v == Entity::FREE_IN_3D.v) ? "free in 3d" : SS.GetEntity(SS.GW.activeWorkplane)->DescriptionString();