From 2926fa95d972e41ef7c4ec009497edf54082030f Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Mon, 21 Apr 2008 00:16:38 -0800 Subject: [PATCH] Start to add some constraint stuff. I now have point-coincident, and point-in-plane. These work, but the equation is still stupid, solving everything at once and not substituting trivial equations. [git-p4: depot-paths = "//depot/solvespace/": change = 1677] --- constraint.cpp | 38 +++++++++++++++++++++++ drawconstraint.cpp | 75 ++++++++++++++++++++++++++++++++++++---------- dsc.h | 7 +++++ entity.cpp | 68 ++++++++++++++++++++++++++++++++++------- expr.cpp | 21 +++++++++++++ expr.h | 6 ++++ glhelper.cpp | 2 +- graphicswin.cpp | 33 ++++++++++++-------- sketch.cpp | 6 ---- sketch.h | 18 +++++++++-- solvespace.cpp | 32 +++++++++++++------- solvespace.h | 2 +- system.cpp | 45 ++++++++++++++++++---------- ui.h | 2 ++ win32/w32main.cpp | 4 ++- 15 files changed, 283 insertions(+), 76 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index 8804718..f745983 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -33,6 +33,22 @@ void Constraint::MenuConstrain(int id) { AddConstraint(&c); break; + case GraphicsWindow::MNU_ON_ENTITY: + if(gs.points == 2 && gs.n == 2) { + c.type = POINTS_COINCIDENT; + c.ptA = gs.point[0]; + c.ptB = gs.point[1]; + } else if(gs.points == 1 && gs.planes == 1 && gs.n == 2) { + c.type = PT_IN_PLANE; + c.ptA = gs.point[0]; + c.entityA = gs.entity[0]; + } else { + Error("Bad selection for on point / curve / plane constraint."); + return; + } + AddConstraint(&c); + break; + case GraphicsWindow::MNU_SOLVE_NOW: SS.Solve(); return; @@ -88,6 +104,28 @@ void Constraint::Generate(IdList *l) { AddEq(l, Distance(ptA, ptB)->Minus(exprA), 0); break; + case POINTS_COINCIDENT: { + Expr *ax, *ay, *az; + Expr *bx, *by, *bz; + SS.GetEntity(ptA)->PointGetExprs(&ax, &ay, &az); + SS.GetEntity(ptB)->PointGetExprs(&bx, &by, &bz); + AddEq(l, ax->Minus(bx), 0); + AddEq(l, ay->Minus(by), 1); + AddEq(l, az->Minus(bz), 2); + break; + } + + case PT_IN_PLANE: { + Expr *px, *py, *pz; + Expr *nx, *ny, *nz, *d; + SS.GetEntity(ptA)->PointGetExprs(&px, &py, &pz); + SS.GetEntity(entityA)->PlaneGetExprs(&nx, &ny, &nz, &d); + AddEq(l, + ((px->Times(nx))->Plus((py->Times(ny)->Plus(pz->Times(nz))))) + ->Minus(d), 0); + break; + } + default: oops(); } } diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 5c37c11..fbe9f1b 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -10,7 +10,24 @@ bool Constraint::HasLabel(void) { } } +void Constraint::LineDrawOrGetDistance(Vector a, Vector b) { + if(dogd.drawing) { + glBegin(GL_LINE_STRIP); + glxVertex3v(a); + glxVertex3v(b); + glEnd(); + } else { + Point2d ap = SS.GW.ProjectPoint(a); + Point2d bp = SS.GW.ProjectPoint(b); + + double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true); + dogd.dmin = min(dogd.dmin, d); + } +} + void Constraint::DrawOrGetDistance(void) { + if(!SS.GW.showConstraints) return; + // Unit vectors that describe our current view of the scene. Vector gr = SS.GW.projRight; Vector gu = SS.GW.projUp; @@ -24,21 +41,17 @@ void Constraint::DrawOrGetDistance(void) { Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); + Vector ab = ap.Minus(bp); + Vector ar = ap.Minus(ref); + // Normal to a plan containing the line and the label origin. + Vector n = ab.Cross(ar); + Vector out = ab.Cross(n).WithMagnitude(1); + out = out.ScaledBy(-out.Dot(ar)); + + LineDrawOrGetDistance(ap, ap.Plus(out)); + LineDrawOrGetDistance(bp, bp.Plus(out)); + if(dogd.drawing) { - Vector ab = ap.Minus(bp); - Vector ar = ap.Minus(ref); - // Normal to a plan containing the line and the label origin. - Vector n = ab.Cross(ar); - Vector out = ab.Cross(n).WithMagnitude(1); - out = out.ScaledBy(-out.Dot(ar)); - - glBegin(GL_LINES); - glxVertex3v(ap); - glxVertex3v(ap.Plus(out)); - glxVertex3v(bp); - glxVertex3v(bp.Plus(out)); - glEnd(); - glPushMatrix(); glxTranslatev(ref); glxOntoCsys(gr, gu); @@ -46,12 +59,44 @@ void Constraint::DrawOrGetDistance(void) { glPopMatrix(); } else { Point2d o = SS.GW.ProjectPoint(ref); - dogd.dmin = o.DistanceTo(dogd.mp) - 10; + dogd.dmin = min(dogd.dmin, o.DistanceTo(dogd.mp) - 10); } break; } + case POINTS_COINCIDENT: { + // It's impossible to select this constraint on the drawing; + // have to do it from the text window. + if(!dogd.drawing) break; + double s = 2; + Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); + Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); + for(int i = 0; i < 2; i++) { + Vector p = SS.GetEntity(i == 0 ? ptA : ptB)->PointGetCoords(); + glxColor(0.4, 0, 0.4); + glBegin(GL_QUADS); + glxVertex3v(p.Plus (r).Plus (d)); + glxVertex3v(p.Plus (r).Minus(d)); + glxVertex3v(p.Minus(r).Minus(d)); + glxVertex3v(p.Minus(r).Plus (d)); + glEnd(); + } + break; + } + + case PT_IN_PLANE: { + double s = 6; + Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); + Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); + Vector p = SS.GetEntity(ptA)->PointGetCoords(); + LineDrawOrGetDistance(p.Plus (r).Plus (d), p.Plus (r).Minus(d)); + LineDrawOrGetDistance(p.Plus (r).Minus(d), p.Minus(r).Minus(d)); + LineDrawOrGetDistance(p.Minus(r).Minus(d), p.Minus(r).Plus (d)); + LineDrawOrGetDistance(p.Minus(r).Plus (d), p.Plus (r).Plus (d)); + break; + } + default: oops(); } } diff --git a/dsc.h b/dsc.h index 46b856f..bee5702 100644 --- a/dsc.h +++ b/dsc.h @@ -146,6 +146,13 @@ public: elem = NULL; } + void DeepCopyInto(IdList *l) { + l->elem = (T *)MemAlloc(elemsAllocated * sizeof(elem[0])); + memcpy(l->elem, elem, elemsAllocated * sizeof(elem[0])); + l->elemsAllocated = elemsAllocated; + l->n = n; + } + void Clear(void) { elemsAllocated = n = 0; if(elem) free(elem); diff --git a/entity.cpp b/entity.cpp index 2a64628..4579c0d 100644 --- a/entity.cpp +++ b/entity.cpp @@ -43,6 +43,43 @@ void Entity::Csys2dGetBasisExprs(Expr **u, Expr **v) { v[2] = (v[2])->Plus(two->Times(c->Times(d))); } +bool Entity::HasPlane(void) { + switch(type) { + case CSYS_2D: + return true; + default: + return false; + } +} + +void Entity::PlaneGetExprs(Expr **x, Expr **y, Expr **z, Expr **dn) { + Expr *a = Expr::FromParam(param.h[0]); + Expr *b = Expr::FromParam(param.h[1]); + Expr *c = Expr::FromParam(param.h[2]); + Expr *d = Expr::FromParam(param.h[3]); + + Expr *two = Expr::FromConstant(2); + + // Convert the quaternion to our plane's normal vector. + *x = two->Times(a->Times(c)); + *x = (*x)->Plus (two->Times(b->Times(d))); + *y = two->Times(c->Times(d)); + *y = (*y)->Minus(two->Times(a->Times(b))); + *z = a->Square(); + *z = (*z)->Minus(b->Square()); + *z = (*z)->Minus(c->Square()); + *z = (*z)->Plus (d->Square()); + + Expr *x0, *y0, *z0; + SS.GetEntity(assoc[0])->PointGetExprs(&x0, &y0, &z0); + // The plane is n dot (p - p0) = 0, or + // n dot p - n dot p0 = 0 + // so dn = n dot p0 + *dn = x0->Times(*x); + *dn = (*dn)->Plus(y0->Times(*y)); + *dn = (*dn)->Plus(z0->Times(*z)); +} + bool Entity::IsPoint(void) { switch(type) { case POINT_IN_3D: @@ -53,28 +90,37 @@ bool Entity::IsPoint(void) { } } +bool Entity::PointIsKnown(void) { + switch(type) { + case POINT_IN_3D: + return SS.GetParam(param.h[0])->known && + SS.GetParam(param.h[1])->known && + SS.GetParam(param.h[2])->known; + case POINT_IN_2D: + return SS.GetParam(param.h[0])->known && + SS.GetParam(param.h[1])->known; + default: oops(); + } +} + bool Entity::PointIsFromReferences(void) { - hRequest hr = h.request(); - if(hr.v == Request::HREQUEST_REFERENCE_XY.v) return true; - if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) return true; - if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) return true; - return false; + return h.request().IsFromReferences(); } void Entity::PointForceTo(Vector p) { switch(type) { case POINT_IN_3D: - SS.GetParam(param.h[0])->ForceTo(p.x); - SS.GetParam(param.h[1])->ForceTo(p.y); - SS.GetParam(param.h[2])->ForceTo(p.z); + SS.GetParam(param.h[0])->val = p.x; + SS.GetParam(param.h[1])->val = p.y; + SS.GetParam(param.h[2])->val = p.z; break; case POINT_IN_2D: { Entity *c = SS.GetEntity(csys); Vector u, v; c->Csys2dGetBasisVectors(&u, &v); - SS.GetParam(param.h[0])->ForceTo(p.Dot(u)); - SS.GetParam(param.h[1])->ForceTo(p.Dot(v)); + SS.GetParam(param.h[0])->val = p.Dot(u); + SS.GetParam(param.h[1])->val = p.Dot(v); break; } default: oops(); @@ -166,6 +212,8 @@ void Entity::DrawOrGetDistance(void) { switch(type) { case POINT_IN_3D: case POINT_IN_2D: { + if(!SS.GW.showPoints) break; + Entity *isfor = SS.GetEntity(h.request().entity(0)); if(!SS.GW.show2dCsyss && isfor->type == Entity::CSYS_2D) break; diff --git a/expr.cpp b/expr.cpp index 40157ee..e50da46 100644 --- a/expr.cpp +++ b/expr.cpp @@ -65,6 +65,27 @@ Expr *Expr::DeepCopyKeep(void) { return n; } +Expr *Expr::DeepCopyWithParamsAsPointers(IdList *firstTry, + IdList *thenTry) +{ + Expr *n = AllocExpr(); + if(op == PARAM) { + // A param that is referenced by its hParam gets rewritten to go + // straight in to the parameter table with a pointer. + n->op = PARAM_PTR; + Param *p = firstTry->FindByIdNoOops(x.parh); + if(!p) p = thenTry->FindById(x.parh); + n->x.parp = p; + return n; + } + + *n = *this; + int c = n->Children(); + if(c > 0) n->a = a->DeepCopyWithParamsAsPointers(firstTry, thenTry); + if(c > 1) n->b = b->DeepCopyWithParamsAsPointers(firstTry, thenTry); + return n; +} + double Expr::Eval(void) { switch(op) { case PARAM: return SS.GetParam(x.parh)->val; diff --git a/expr.h b/expr.h index 24bafea..56ae471 100644 --- a/expr.h +++ b/expr.h @@ -83,6 +83,12 @@ public: // number of child nodes: 0 (e.g. constant), 1 (sqrt), or 2 (+) int Children(void); + // Make a copy, with the parameters (usually referenced by hParam) + // resolved to pointers to the actual value. This speeds things up + // considerably. + Expr *DeepCopyWithParamsAsPointers(IdList *firstTry, + IdList *thenTry); + static Expr *FromString(char *in); static void Lex(char *in); static Expr *Next(void); diff --git a/glhelper.cpp b/glhelper.cpp index 0f60b03..2f2ff44 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -7,7 +7,7 @@ static bool ColorLocked; void glxWriteText(char *str) { - double scale = 0.7/SS.GW.scale; + double scale = 0.5/SS.GW.scale; int xo = 5; int yo = 5; diff --git a/graphicswin.cpp b/graphicswin.cpp index 38af51b..851f839 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -69,7 +69,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "&Horizontal\tShift+H", 0, 'H'|S, NULL }, { 1, "&Vertical\tShift+V", 0, 'V'|S, NULL }, { 1, NULL, 0, NULL }, -{ 1, "Coincident / &On Curve\tShift+O", 0, 'O'|S, NULL }, +{ 1, "&On Point / Curve / Plane\tShift+O", MNU_ON_ENTITY, 'O'|S, mCon }, { 1, "E&qual Length / Radius\tShift+Q", 0, 'Q'|S, NULL }, { 1, "At &Midpoint\tShift+M", 0, 'M'|S, NULL }, { 1, "S&ymmetric\tShift+Y", 0, 'Y'|S, NULL }, @@ -238,16 +238,20 @@ void GraphicsWindow::MenuEdit(int id) { case MNU_DELETE: { int i; SS.request.ClearTags(); + SS.constraint.ClearTags(); for(i = 0; i < MAX_SELECTED; i++) { Selection *s = &(SS.GW.selection[i]); - hRequest r; - r.v = 0; + hRequest r; r.v = 0; if(s->entity.v) { r = s->entity.request(); } - if(r.v) SS.request.Tag(r, 1); + if(r.v && !r.IsFromReferences()) SS.request.Tag(r, 1); + if(s->constraint.v) { + SS.constraint.Tag(s->constraint, 1); + } } SS.request.RemoveTagged(); + SS.constraint.RemoveTagged(); SS.GenerateAll(); SS.GW.ClearSelection(); @@ -433,24 +437,24 @@ void GraphicsWindow::ClearSelection(void) { } void GraphicsWindow::GroupSelection(void) { - gs.points = gs.entities = 0; - gs.csyss = gs.lineSegments = 0; - gs.n = 0; + memset(&gs, 0, sizeof(gs)); int i; for(i = 0; i < MAX_SELECTED; i++) { Selection *s = &(selection[i]); if(s->entity.v) { - gs.entity[(gs.entities)++] = s->entity; (gs.n)++; Entity *e = SS.entity.FindById(s->entity); + if(e->IsPoint()) { + gs.point[(gs.points)++] = s->entity; + } else { + gs.entity[(gs.entities)++] = s->entity; + } switch(e->type) { case Entity::CSYS_2D: (gs.csyss)++; break; case Entity::LINE_SEGMENT: (gs.lineSegments)++; break; } - if(e->IsPoint()) { - gs.point[(gs.points)++] = s->entity; - } + if(e->HasPlane()) (gs.planes)++; } } } @@ -620,12 +624,15 @@ void GraphicsWindow::Paint(int w, int h) { for(i = 0; i < SS.entity.n; i++) { SS.entity.elem[i].Draw(); } + + // Want the constraints to get drawn in front, so disable depth test. + glDisable(GL_DEPTH_TEST); + // Draw the constraints for(i = 0; i < SS.constraint.n; i++) { SS.constraint.elem[i].Draw(); } - // Then redraw whatever the mouse is hovering over, highlighted. Have - // to disable the depth test, so that we can overdraw. + // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); glxLockColorTo(1, 1, 0); hover.Draw(); diff --git a/sketch.cpp b/sketch.cpp index 07ea0f4..89b7f6f 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -99,9 +99,3 @@ char *Request::DescriptionString(void) { return ret; } -void Param::ForceTo(double v) { - val = v; - known = true; -} - - diff --git a/sketch.h b/sketch.h index fb1321e..12184ae 100644 --- a/sketch.h +++ b/sketch.h @@ -27,6 +27,8 @@ public: inline hEntity entity(int i); inline hParam param(int i); + + inline bool IsFromReferences(void); }; class hEntity { public: @@ -147,6 +149,12 @@ public: Vector PointGetCoords(void); void PointForceTo(Vector v); bool PointIsFromReferences(void); + bool PointIsKnown(void); + + // Applies for anything that comes with a plane + bool HasPlane(void); + // The plane is points P such that P dot (xn, yn, zn) - d = 0 + void PlaneGetExprs(Expr **xn, Expr **yn, Expr **zn, Expr **d); // Routines to draw and hit-test the representation of the entity // on-screen. @@ -170,11 +178,15 @@ public: double val; bool known; - - void ForceTo(double v); }; +inline bool hRequest::IsFromReferences(void) { + if(v == Request::HREQUEST_REFERENCE_XY.v) return true; + if(v == Request::HREQUEST_REFERENCE_YZ.v) return true; + if(v == Request::HREQUEST_REFERENCE_ZX.v) return true; + return false; +} inline hEntity hRequest::entity(int i) { hEntity r; r.v = (v << 16) | i; return r; } inline hParam hRequest::param(int i) @@ -200,6 +212,7 @@ public: static const int POINTS_COINCIDENT = 20; static const int PT_PT_DISTANCE = 30; static const int PT_LINE_DISTANCE = 31; + static const int PT_IN_PLANE = 40; static const int HORIZONTAL = 40; static const int VERTICAL = 41; @@ -232,6 +245,7 @@ public: Point2d mp; double dmin; } dogd; // state for drawing or getting distance (for hit testing) + void LineDrawOrGetDistance(Vector a, Vector b); double GetDistance(Point2d mp); void Draw(void); void DrawOrGetDistance(void); diff --git a/solvespace.cpp b/solvespace.cpp index fc89f83..6f42821 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -88,10 +88,10 @@ void SolveSpace::ForceReferences(void) { Vector v = Vector::MakeFrom(0, 0, 0); GetEntity(hr.entity(1))->PointForceTo(v); // The quaternion that defines the rotation, from the table. - GetParam(hr.param(0))->ForceTo(Quat[i].a); - GetParam(hr.param(1))->ForceTo(Quat[i].b); - GetParam(hr.param(2))->ForceTo(Quat[i].c); - GetParam(hr.param(3))->ForceTo(Quat[i].d); + GetParam(hr.param(0))->val = Quat[i].a; + GetParam(hr.param(1))->val = Quat[i].b; + GetParam(hr.param(2))->val = Quat[i].c; + GetParam(hr.param(3))->val = Quat[i].d; } } @@ -132,10 +132,12 @@ bool SolveSpace::SolveGroup(hGroup hg) { c->Generate(&(sys.eq)); } - return sys.Solve(); + bool r = sys.Solve(); + FreeAllExprs(); + return r; } -bool SolveSpace::SolveWorker(void) { +bool SolveSpace::SolveWorker(int order) { bool allSolved = true; int i; @@ -145,17 +147,25 @@ bool SolveSpace::SolveWorker(void) { allSolved = false; dbp("try solve group %s", g->DescriptionString()); + + // Save the parameter table; a failed solve attempt will mess that + // up a little bit. + IdList savedParam; + param.DeepCopyInto(&savedParam); + if(SolveGroup(g->h)) { g->solved = true; + g->solveOrder = order; // So this one worked; let's see if we can go any further. - if(SolveWorker()) { + if(SolveWorker(order+1)) { // So everything worked; we're done. return true; - } else { - // Didn't work, so undo this choice and give up - g->solved = false; } } + // Didn't work, so undo this choice and give up + g->solved = false; + param.Clear(); + savedParam.MoveSelfInto(¶m); } // If we got here, then either everything failed, so we're stuck, or @@ -168,7 +178,7 @@ void SolveSpace::Solve(void) { for(i = 0; i < group.n; i++) { group.elem[i].solved = false; } - SolveWorker(); + SolveWorker(0); InvalidateGraphics(); } diff --git a/solvespace.h b/solvespace.h index 96cda29..87cb4bc 100644 --- a/solvespace.h +++ b/solvespace.h @@ -151,7 +151,7 @@ public: void Init(void); bool SolveGroup(hGroup hg); - bool SolveWorker(void); + bool SolveWorker(int order); void Solve(void); static void MenuFile(int id); diff --git a/system.cpp b/system.cpp index c4cc66e..1279d49 100644 --- a/system.cpp +++ b/system.cpp @@ -17,10 +17,12 @@ void System::WriteJacobian(int eqTag, int paramTag) { Equation *e = &(eq.elem[a]); if(e->tag != eqTag) continue; - mat.eq[i] = eq.elem[i].h; - mat.B.sym[i] = eq.elem[i].e; + mat.eq[i] = e->h; + mat.B.sym[i] = e->e->DeepCopyWithParamsAsPointers(¶m, &(SS.param)); for(j = 0; j < mat.n; j++) { - mat.A.sym[i][j] = e->e->PartialWrt(mat.param[j]); + Expr *pd = e->e->PartialWrt(mat.param[j]); + mat.A.sym[i][j] = + pd->DeepCopyWithParamsAsPointers(¶m, &(SS.param)); } i++; } @@ -165,14 +167,13 @@ bool System::NewtonSolve(int tag) { int iter = 0; bool converged = false; int i; + + // Evaluate the functions at our operating point. + for(i = 0; i < mat.m; i++) { + mat.B.num[i] = (mat.B.sym[i])->Eval(); + } do { - // Evaluate the functions numerically - for(i = 0; i < mat.m; i++) { - mat.B.num[i] = (mat.B.sym[i])->Eval(); - dbp("mat.B.num[%d] = %.3f", i, mat.B.num[i]); - dbp("mat.B.sym[%d] = %s", i, (mat.B.sym[i])->Print()); - } - // And likewise for the Jacobian + // And evaluate the Jacobian at our initial operating point. EvalJacobian(); if(!SolveLinearSystem()) break; @@ -184,12 +185,13 @@ bool System::NewtonSolve(int tag) { dbp("modifying param %08x, now %.3f", mat.param[i], param.FindById(mat.param[i])->val); (param.FindById(mat.param[i]))->val -= mat.X[i]; - // XXX do this properly - SS.GetParam(mat.param[i])->val = - (param.FindById(mat.param[i]))->val; } - // XXX re-evaluate functions before checking convergence + // Re-evalute the functions, since the params have just changed. + for(i = 0; i < mat.m; i++) { + mat.B.num[i] = (mat.B.sym[i])->Eval(); + } + // Check for convergence converged = true; for(i = 0; i < mat.m; i++) { if(!Tol(mat.B.num[i])) { @@ -199,7 +201,7 @@ bool System::NewtonSolve(int tag) { } } while(iter++ < 50 && !converged); - if(converged) { + if(converged) { return true; } else { return false; @@ -238,7 +240,18 @@ bool System::Solve(void) { param.FindById(mat.param[j])->tag = ASSUMED; } - NewtonSolve(0); + bool ok = NewtonSolve(0); + + if(ok) { + // System solved correctly, so write the new values back in to the + // main parameter table. + for(i = 0; i < param.n; i++) { + Param *p = &(param.elem[i]); + Param *pp = SS.GetParam(p->h); + pp->val = p->val; + pp->known = true; + } + } return true; } diff --git a/ui.h b/ui.h index 345c359..1dd0f06 100644 --- a/ui.h +++ b/ui.h @@ -96,6 +96,7 @@ public: MNU_LINE_SEGMENT, // Constrain MNU_DISTANCE_DIA, + MNU_ON_ENTITY, MNU_SOLVE_NOW, } MenuId; typedef void MenuHandler(int id); @@ -167,6 +168,7 @@ public: int points; int entities; int csyss; + int planes; int lineSegments; int n; } gs; diff --git a/win32/w32main.cpp b/win32/w32main.cpp index b29fff5..2b5fc39 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -36,10 +36,11 @@ HFONT FixedFont, LinkFont; void dbp(char *str, ...) { va_list f; - char buf[1024]; + static char buf[1024*50]; va_start(f, str); vsprintf(buf, str, f); OutputDebugString(buf); + va_end(f); } void Error(char *str, ...) @@ -48,6 +49,7 @@ void Error(char *str, ...) char buf[1024]; va_start(f, str); vsprintf(buf, str, f); + va_end(f); HWND h = GetForegroundWindow(); MessageBox(h, buf, "SolveSpace Error", MB_OK | MB_ICONERROR);