diff --git a/Makefile b/Makefile index 30faac5f..92929347 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ SSOBJS = $(OBJDIR)\solvespace.obj \ $(OBJDIR)\expr.obj \ $(OBJDIR)\constraint.obj \ $(OBJDIR)\drawconstraint.obj \ + $(OBJDIR)\file.obj \ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib diff --git a/dsc.h b/dsc.h index 6dfddc5d..77512996 100644 --- a/dsc.h +++ b/dsc.h @@ -5,6 +5,28 @@ typedef unsigned long DWORD; typedef unsigned char BYTE; +class Vector; + +class Quaternion { +public: + // a + bi + cj + dk + double a, b, c, d; + + static Quaternion MakeFrom(double a, double b, double c, double d); + static Quaternion MakeFrom(Vector u, Vector v); + + Quaternion Plus(Quaternion y); + Quaternion Minus(Quaternion y); + Quaternion ScaledBy(double s); + double Magnitude(void); + Quaternion WithMagnitude(double s); + + // Call a rotation matrix [ u' v' n' ]'; this returns the first and + // second rows, where that matrix is generated by this quaternion + Vector RotationU(void); + Vector RotationV(void); +}; + class Vector { public: double x, y, z; @@ -21,12 +43,6 @@ public: double Magnitude(void); Vector WithMagnitude(double s); Vector ScaledBy(double s); - - // Call a rotation matrix [ u' v' n' ]'; this returns the first and - // second rows, where that matrix is generated by the given quaternion - // a + bi + cj + dk - static Vector RotationU(double a, double b, double c, double d); - static Vector RotationV(double a, double b, double c, double d); }; class Point2d { @@ -44,14 +60,14 @@ template class IdList { public: T *elem; - int elems; + int n; int elemsAllocated; H AddAndAssignId(T *t) { int i; DWORD id = 0; - for(i = 0; i < elems; i++) { + for(i = 0; i < n; i++) { id = max(id, elem[i].h.v); } @@ -62,20 +78,20 @@ public: } void Add(T *t) { - if(elems >= elemsAllocated) { + if(n >= elemsAllocated) { elemsAllocated = (elemsAllocated + 32)*2; elem = (T *)MemRealloc(elem, elemsAllocated*sizeof(elem[0])); if(!elem) oops(); } - elem[elems] = *t; - elems++; + elem[n] = *t; + n++; } T *FindById(H h) { T *t = FindByIdNoOops(h); if(!t) { - dbp("failed to look up item %16lx, searched %d items", h.v, elems); + dbp("failed to look up item %16lx, searched %d items", h.v, n); oops(); } return t; @@ -83,7 +99,7 @@ public: T *FindByIdNoOops(H h) { int i; - for(i = 0; i < elems; i++) { + for(i = 0; i < n; i++) { if(elem[i].h.v == h.v) { return &(elem[i]); } @@ -93,14 +109,14 @@ public: void ClearTags(void) { int i; - for(i = 0; i < elems; i++) { + for(i = 0; i < n; i++) { elem[i].tag = 0; } } void Tag(H h, int tag) { int i; - for(i = 0; i < elems; i++) { + for(i = 0; i < n; i++) { if(elem[i].h.v == h.v) { elem[i].tag = tag; } @@ -110,7 +126,7 @@ public: void RemoveTagged(void) { int src, dest; dest = 0; - for(src = 0; src < elems; src++) { + for(src = 0; src < n; src++) { if(elem[src].tag) { // this item should be deleted } else { @@ -120,18 +136,18 @@ public: dest++; } } - elems = dest; + n = dest; // and elemsAllocated is untouched, because we didn't resize } void MoveSelfInto(IdList *l) { memcpy(l, this, sizeof(*this)); - elemsAllocated = elems = 0; + elemsAllocated = n = 0; elem = NULL; } void Clear(void) { - elemsAllocated = elems = 0; + elemsAllocated = n = 0; if(elem) free(elem); elem = NULL; } diff --git a/entity.cpp b/entity.cpp index 00b700c4..ade16e89 100644 --- a/entity.cpp +++ b/entity.cpp @@ -10,9 +10,10 @@ void Entity::Get2dCsysBasisVectors(Vector *u, Vector *v) { for(int i = 0; i < 4; i++) { q[i] = SS.param.FindById(param(i))->val; } + Quaternion quat = Quaternion::MakeFrom(q[0], q[1], q[2], q[3]); - *u = Vector::RotationU(q[0], q[1], q[2], q[3]); - *v = Vector::RotationV(q[0], q[1], q[2], q[3]); + *u = quat.RotationU(); + *v = quat.RotationV(); } void Entity::LineDrawOrGetDistance(Vector a, Vector b) { diff --git a/expr.cpp b/expr.cpp index 967bedc5..40157ee2 100644 --- a/expr.cpp +++ b/expr.cpp @@ -130,6 +130,8 @@ void Expr::App(char *s, ...) { vsprintf(StringBuffer+strlen(StringBuffer), s, f); } char *Expr::Print(void) { + if(!this) return "0"; + StringBuffer[0] = '\0'; PrintW(); return StringBuffer; @@ -356,6 +358,7 @@ void Expr::Lex(char *in) { Expr *Expr::FromString(char *in) { UnparsedCnt = 0; + UnparsedP = 0; OperandsP = 0; OperatorsP = 0; diff --git a/file.cpp b/file.cpp new file mode 100644 index 00000000..d09dbdc2 --- /dev/null +++ b/file.cpp @@ -0,0 +1,84 @@ +#include "solvespace.h" + +bool SolveSpace::SaveToFile(char *filename) { + fh = fopen(filename, "w"); + if(!fh) { + Error("Couldn't write to file '%s'", fh); + return false; + } + + fprintf(fh, "!!SolveSpaceREVa\n\n\n"); + + int i; + for(i = 0; i < group.n; i++) { + Group *g = &(group.elem[i]); + fprintf(fh, "Group.h.v=%08x\n", g->h.v); + fprintf(fh, "Group.name=%s\n", g->name.str); + fprintf(fh, "AddGroup\n\n"); + } + + for(i = 0; i < param.n; i++) { + Param *p = &(param.elem[i]); + fprintf(fh, "Param.h.v=%08x\n", p->h.v); + fprintf(fh, "Param.val=%.20f\n", p->val); + fprintf(fh, "AddParam\n\n"); + } + + for(i = 0; i < request.n; i++) { + Request *r = &(request.elem[i]); + fprintf(fh, "Request.h.v=%08x\n", r->h.v); + fprintf(fh, "Request.type=%d\n", r->type); + fprintf(fh, "Request.csys.v=%08x\n", r->csys.v); + fprintf(fh, "Request.group.v=%08x\n", r->group.v); + fprintf(fh, "Request.name=%s\n", r->name.str); + fprintf(fh, "AddRequest\n\n"); + } + + for(i = 0; i < entity.n; i++) { + Entity *e = &(entity.elem[i]); + fprintf(fh, "Entity.h.v=%08x\n", e->h.v); + fprintf(fh, "Entity.type=%d\n", e->type); + fprintf(fh, "AddEntity\n\n"); + } + + for(i = 0; i < point.n; i++) { + Point *p = &(point.elem[i]); + fprintf(fh, "Point.h.v=%08x\n", p->h.v); + fprintf(fh, "Point.type=%d\n", p->type); + fprintf(fh, "Point.csys.v=%08x\n", p->csys.v); + fprintf(fh, "AddPoint\n\n"); + } + + for(i = 0; i < constraint.n; i++) { + Constraint *c = &(constraint.elem[i]); + fprintf(fh, "Constraint.h.v=%08x\n", c->h.v); + fprintf(fh, "Constraint.type=%d\n", c->type); + fprintf(fh, "Constraint.group.v=%08x\n", c->group); + fprintf(fh, "Constraint.exprA=%s\n", c->exprA->Print()); + fprintf(fh, "Constraint.exprB=%s\n", c->exprB->Print()); + fprintf(fh, "Constraint.ptA.v=%08x\n", c->ptA.v); + fprintf(fh, "Constraint.ptB.v=%08x\n", c->ptB.v); + fprintf(fh, "Constraint.ptC.v=%08x\n", c->ptC.v); + fprintf(fh, "Constraint.entityA.v=%08x\n", c->entityA.v); + fprintf(fh, "Constraint.entityB.v=%08x\n", c->entityB.v); + fprintf(fh, "Constraint.disp.offset.x=%.20f\n", c->disp.offset.x); + fprintf(fh, "Constraint.disp.offset.y=%.20f\n", c->disp.offset.y); + fprintf(fh, "Constraint.disp.offset.z=%.20f\n", c->disp.offset.z); + fprintf(fh, "AddConstraint\n\n"); + } + + fclose(fh); + + return true; +} + +bool SolveSpace::LoadFromFile(char *filename) { + fh = fopen(filename, "r"); + if(!fh) { + Error("Couldn't read from file '%s'", fh); + return false; + } + + return true; +} + diff --git a/graphicswin.cpp b/graphicswin.cpp index 4390f46b..1ea72938 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -6,15 +6,17 @@ #define mEdit (&GraphicsWindow::MenuEdit) #define mReq (&GraphicsWindow::MenuRequest) #define mCon (&Constraint::MenuConstrain) +#define mFile (&SolveSpace::MenuFile) #define S 0x100 +#define C 0x200 const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 0, "&File", 0, NULL }, -{ 1, "&New\tCtrl+N", 0, NULL }, -{ 1, "&Open...\tCtrl+O", 0, NULL }, -{ 1, "&Save\tCtrl+S", 0, NULL }, -{ 1, "Save &As...", 0, NULL }, -{ 1, NULL, 0, NULL }, -{ 1, "E&xit", 0, NULL }, +{ 1, "&New\tCtrl+N", MNU_NEW, 'N'|C, mFile }, +{ 1, "&Open...\tCtrl+O", MNU_OPEN, 'O'|C, mFile }, +{ 1, "&Save\tCtrl+S", MNU_SAVE, 'S'|C, mFile }, +{ 1, "Save &As...", MNU_SAVE_AS, 0, mFile }, +{ 1, NULL, 0, 0, NULL }, +{ 1, "E&xit", MNU_EXIT, 0, mFile }, { 0, "&Edit", 0, NULL }, { 1, "&Undo\tCtrl+Z", 0, NULL }, @@ -37,7 +39,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 0, "&Request", 0, NULL }, { 1, "Dra&w in 2d Coordinate System\tW", MNU_SEL_CSYS, 'W', mReq }, -{ 1, "Draw Anywhere in 3d\tF", MNU_NO_CSYS, 'Q', mReq }, +{ 1, "Draw Anywhere in 3d\tQ", MNU_NO_CSYS, 'Q', mReq }, { 1, NULL, 0, NULL }, { 1, "Datum &Point\tP", MNU_DATUM_POINT, 'P', mReq }, { 1, "Datum A&xis\tX", 0, 'X', mReq }, @@ -132,18 +134,63 @@ void GraphicsWindow::MenuView(int id) { CheckMenuById(MNU_LOCK_VIEW, SS.GW.viewLocked); break; - case MNU_ORIENT_ONTO: + case MNU_ORIENT_ONTO: { SS.GW.GroupSelection(); + Entity *e = NULL; if(SS.GW.gs.n == 1 && SS.GW.gs.csyss == 1) { - Entity *e = SS.entity.FindById(SS.GW.gs.entity[0]); - e->Get2dCsysBasisVectors( &(SS.GW.projRight), &(SS.GW.projUp)); - SS.GW.offset = SS.point.FindById(e->point(16))->GetCoords(); + e = SS.entity.FindById(SS.GW.gs.entity[0]); + } else if(SS.GW.activeCsys.v != Entity::NO_CSYS.v) { + e = SS.entity.FindById(SS.GW.activeCsys); + } + if(e) { + // A quaternion with our original rotation + Quaternion quat0 = Quaternion::MakeFrom( + SS.GW.projRight, SS.GW.projUp); + // And with our final rotation + Vector pr, pu; + e->Get2dCsysBasisVectors(&pr, &pu); + Quaternion quatf = Quaternion::MakeFrom(pr, pu); + // Make sure we take the shorter of the two possible paths. + double mp = (quatf.Minus(quat0)).Magnitude(); + double mm = (quatf.Plus(quat0)).Magnitude(); + if(mp > mm) { + quatf = quatf.ScaledBy(-1); + mp = mm; + } + + // And also get the offsets. + Vector offset0 = SS.GW.offset; + Vector offsetf = SS.point.FindById(e->point(16))->GetCoords(); + + // Animate transition, unless it's a tiny move. + SDWORD dt = (mp < 0.01) ? (-20) : (SDWORD)(100 + 1000*mp); + SDWORD tn, t0 = GetMilliseconds(); + double s = 0; + do { + SS.GW.offset = + (offset0.ScaledBy(1 - s)).Plus(offsetf.ScaledBy(s)); + Quaternion quat = + (quat0.ScaledBy(1 - s)).Plus(quatf.ScaledBy(s)); + quat = quat.WithMagnitude(1); + SS.GW.projRight = quat.RotationU(); + SS.GW.projUp = quat.RotationV(); + PaintGraphics(); + + tn = GetMilliseconds(); + s = (tn - t0)/((double)dt); + } while((tn - t0) < dt); + SS.GW.projRight = pr; + SS.GW.projUp = pu; + SS.GW.offset = offsetf; + + SS.GW.hover.Clear(); SS.GW.ClearSelection(); InvalidateGraphics(); } else { Error("Select plane or coordinate system before orienting."); } break; + } default: oops(); } @@ -156,12 +203,12 @@ void GraphicsWindow::EnsureValidActives(void) { Group *g = SS.group.FindByIdNoOops(activeGroup); if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) { int i; - for(i = 0; i < SS.group.elems; i++) { + for(i = 0; i < SS.group.n; i++) { if(SS.group.elem[i].h.v != Group::HGROUP_REFERENCES.v) { break; } } - if(i >= SS.group.elems) oops(); + if(i >= SS.group.n) oops(); activeGroup = SS.group.elem[i].h; change = true; } @@ -174,6 +221,8 @@ void GraphicsWindow::EnsureValidActives(void) { change = true; } if(change) SS.TW.Show(); + + EnableMenuById(MNU_NO_CSYS, (activeCsys.v != Entity::NO_CSYS.v)); } void GraphicsWindow::MenuEdit(int id) { @@ -362,7 +411,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { memset(dest, 0, sizeof(*dest)); // Do the points - for(i = 0; i < SS.entity.elems; i++) { + for(i = 0; i < SS.entity.n; i++) { d = SS.entity.elem[i].GetDistance(mp); if(d < 10 && d < dmin) { memset(dest, 0, sizeof(*dest)); @@ -371,7 +420,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { } // Entities - for(i = 0; i < SS.point.elems; i++) { + for(i = 0; i < SS.point.n; i++) { d = SS.point.elem[i].GetDistance(mp); if(d < 10 && d < dmin) { memset(dest, 0, sizeof(*dest)); @@ -380,7 +429,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { } // Constraints - for(i = 0; i < SS.constraint.elems; i++) { + for(i = 0; i < SS.constraint.n; i++) { d = SS.constraint.elem[i].GetDistance(mp); if(d < 10 && d < dmin) { memset(dest, 0, sizeof(*dest)); @@ -581,15 +630,15 @@ void GraphicsWindow::Paint(int w, int h) { // First, draw the entire scene. glColor3f(1, 1, 1); - for(i = 0; i < SS.entity.elems; i++) { + for(i = 0; i < SS.entity.n; i++) { SS.entity.elem[i].Draw(); } glColor3f(0, 0.8f, 0); - for(i = 0; i < SS.point.elems; i++) { + for(i = 0; i < SS.point.n; i++) { SS.point.elem[i].Draw(); } glColor3f(1.0f, 0, 1.0f); - for(i = 0; i < SS.constraint.elems; i++) { + for(i = 0; i < SS.constraint.n; i++) { SS.constraint.elem[i].Draw(); } diff --git a/sketch.h b/sketch.h index f69836cd..58678728 100644 --- a/sketch.h +++ b/sketch.h @@ -82,9 +82,9 @@ public: hRequest h; // Types of requests - static const int CSYS_2D = 10; - static const int DATUM_POINT = 11; - static const int LINE_SEGMENT = 20; + static const int CSYS_2D = 100; + static const int DATUM_POINT = 101; + static const int LINE_SEGMENT = 200; int type; @@ -107,6 +107,9 @@ public: class Entity { public: + int tag; + hEntity h; + static const hEntity NO_CSYS; static const int CSYS_2D = 1000; @@ -114,11 +117,6 @@ public: static const int LINE_SEGMENT = 1010; int type; - int tag; - hEntity h; - - Expr *expr[16]; - inline hRequest request(void) { hRequest r; r.v = (this->h.v >> 10); return r; } inline hParam param(int i) @@ -161,7 +159,6 @@ public: int type; static const int IN_FREE_SPACE = 0; // three params, x y z static const int IN_2D_CSYS = 1; // two params, u v, plus csys - static const int BY_EXPR = 2; // three Expr *, could be anything hEntity csys; @@ -234,9 +231,7 @@ public: // These define how the constraint is drawn on-screen. struct { - hEntity csys; Vector offset; - Vector u, v; } disp; static hConstraint AddConstraint(Constraint *c); diff --git a/solvespace.cpp b/solvespace.cpp index 908107f8..c392450c 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -58,11 +58,11 @@ void SolveSpace::GenerateAll(void) { entity.Clear(); point.Clear(); - for(i = 0; i < request.elems; i++) { + for(i = 0; i < request.n; i++) { request.elem[i].Generate(&entity, &point, ¶m); } - for(i = 0; i < param.elems; i++) { + for(i = 0; i < param.n; i++) { Param *p = prev.FindByIdNoOops(param.elem[i].h); if(p) { param.elem[i].val = p->val; @@ -80,8 +80,8 @@ void SolveSpace::ForceReferences(void) { double a, b, c, d; } Quat[] = { { Request::HREQUEST_REFERENCE_XY, 1, 0, 0, 0, }, - { Request::HREQUEST_REFERENCE_YZ, 0.5, -0.5, -0.5, -0.5, }, - { Request::HREQUEST_REFERENCE_ZX, 0.5, 0.5, 0.5, 0.5, }, + { Request::HREQUEST_REFERENCE_YZ, 0.5, 0.5, 0.5, 0.5, }, + { Request::HREQUEST_REFERENCE_ZX, 0.5, -0.5, -0.5, -0.5, }, }; for(int i = 0; i < 3; i++) { hEntity he; @@ -101,3 +101,21 @@ void SolveSpace::ForceReferences(void) { void SolveSpace::Solve(void) { } +void SolveSpace::MenuFile(int id) { + switch(id) { + case GraphicsWindow::MNU_NEW: + case GraphicsWindow::MNU_OPEN: + + case GraphicsWindow::MNU_SAVE: + SS.SaveToFile("t.slv"); + break; + + case GraphicsWindow::MNU_SAVE_AS: + break; + + case GraphicsWindow::MNU_EXIT: + break; + + default: oops(); + } +} diff --git a/solvespace.h b/solvespace.h index fa5f5124..d1a31a61 100644 --- a/solvespace.h +++ b/solvespace.h @@ -14,6 +14,8 @@ #define isforname(c) (isalnum(c) || (c) == '_' || (c) == '-' || (c) == '#') +typedef signed long SDWORD; + #include #include #include @@ -26,12 +28,21 @@ class Expr; // From the platform-specific code. +int SaveFileYesNoCancel(void); +BOOL GetSaveFile(char *file, char *defExtension, char *selPattern); +BOOL GetOpenFile(char *file, char *defExtension, char *selPattern); + void CheckMenuById(int id, BOOL checked); void EnableMenuById(int id, BOOL checked); + void InvalidateGraphics(void); void InvalidateText(void); +SDWORD GetMilliseconds(void); +void PaintGraphics(void); + void dbp(char *str, ...); void Error(char *str, ...); + Expr *AllocExpr(void); void FreeAllExprs(void); void *MemRealloc(void *p, int n); @@ -82,13 +93,19 @@ public: inline Param *GetParam (hParam h) { return param. FindById(h); } inline Point *GetPoint (hPoint h) { return point. FindById(h); } - hGroup activeGroup; + hGroup activeGroup; + + FILE *fh; void GenerateAll(void); void ForceReferences(void); void Init(void); void Solve(void); + + static void MenuFile(int id); + bool SaveToFile(char *filename); + bool LoadFromFile(char *filename); }; extern SolveSpace SS; diff --git a/textwin.cpp b/textwin.cpp index 8fd68e3e..01a8b2e9 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -254,10 +254,10 @@ void TextWindow::ShowHeader(void) { void TextWindow::ShowAllGroups(void) { Printf("%C8[[all groups in sketch follow]]%E"); int i; - for(i = 0; i <= SS.group.elems; i++) { + for(i = 0; i <= SS.group.n; i++) { DWORD v; char *s; - if(i == SS.group.elems) { + if(i == SS.group.n) { s = "all requests from all groups"; v = 0; } else { @@ -293,7 +293,7 @@ void TextWindow::ShowRequestsInGroup(void) { } int i; - for(i = 0; i < SS.request.elems; i++) { + for(i = 0; i < SS.request.n; i++) { Request *r = &(SS.request.elem[i]); if(r->group.v == shown->group.v || shown->group.v == 0) { diff --git a/ui.h b/ui.h index 596b4759..fc6d34d4 100644 --- a/ui.h +++ b/ui.h @@ -74,8 +74,14 @@ public: // This table describes the top-level menus in the graphics winodw. typedef enum { + // File + MNU_NEW = 100, + MNU_OPEN, + MNU_SAVE, + MNU_SAVE_AS, + MNU_EXIT, // View - MNU_ZOOM_IN = 100, + MNU_ZOOM_IN, MNU_ZOOM_OUT, MNU_ZOOM_TO_FIT, MNU_ORIENT_ONTO, diff --git a/util.cpp b/util.cpp index 766a0840..b336a41e 100644 --- a/util.cpp +++ b/util.cpp @@ -23,27 +23,85 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, mat[15] = a44; } +Quaternion Quaternion::MakeFrom(double a, double b, double c, double d) { + Quaternion q; + q.a = a; + q.b = b; + q.c = c; + q.d = d; + return q; +} + +Quaternion Quaternion::MakeFrom(Vector u, Vector v) +{ + Vector n = u.Cross(v); + + Quaternion q; + q.a = 0.5*sqrt(1 + u.x + v.y + n.z); + q.b = (1/(4*(q.a)))*(v.z - n.y); + q.c = (1/(4*(q.a)))*(n.x - u.z); + q.d = (1/(4*(q.a)))*(u.y - v.x); + return q; +} + +Quaternion Quaternion::Plus(Quaternion y) { + Quaternion q; + q.a = a + y.a; + q.b = b + y.b; + q.c = c + y.c; + q.d = d + y.d; + return q; +} + +Quaternion Quaternion::Minus(Quaternion y) { + Quaternion q; + q.a = a - y.a; + q.b = b - y.b; + q.c = c - y.c; + q.d = d - y.d; + return q; +} + +Quaternion Quaternion::ScaledBy(double s) { + Quaternion q; + q.a = a*s; + q.b = b*s; + q.c = c*s; + q.d = d*s; + return q; +} + +double Quaternion::Magnitude(void) { + return sqrt(a*a + b*b + c*c + d*d); +} + +Quaternion Quaternion::WithMagnitude(double s) { + return ScaledBy(s/Magnitude()); +} + +Vector Quaternion::RotationU(void) { + Vector v; + v.x = a*a + b*b - c*c - d*d; + v.y = 2*a*d + 2*b*c; + v.z = 2*b*d - 2*a*c; + return v; +} + +Vector Quaternion::RotationV(void) { + Vector v; + v.x = 2*b*c - 2*a*d; + v.y = a*a - b*b + c*c - d*d; + v.z = 2*a*b + 2*c*d; + return v; +} + + Vector Vector::MakeFrom(double x, double y, double z) { Vector v; v.x = x; v.y = y; v.z = z; return v; } -Vector Vector::RotationU(double a, double b, double c, double d) { - Vector v; - v.x = a*a + b*b - c*c - d*d; - v.y = 2*b*c - 2*a*d; - v.z = 2*a*c + 2*b*d; - return v; -} - -Vector Vector::RotationV(double a, double b, double c, double d) { - Vector v; - v.x = 2*a*d + 2*b*c; - v.y = a*a - b*b + c*c - d*d; - v.z = 2*c*d - 2*a*b; - return v; -} Vector Vector::Plus(Vector b) { Vector r; diff --git a/win32/w32main.cpp b/win32/w32main.cpp index e68d7e49..b29fff52 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -349,6 +350,32 @@ void InvalidateGraphics(void) { InvalidateRect(GraphicsWnd, NULL, FALSE); } +static void PaintGraphicsWithHdc(HDC hdc) +{ + HGLRC hgrc = CreateGlContext(hdc); + + RECT r; + GetClientRect(GraphicsWnd, &r); + int w = r.right - r.left; + int h = r.bottom - r.top; + + SS.GW.Paint(w, h); + + SwapBuffers(hdc); + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(hgrc); +} +void PaintGraphics(void) +{ + HDC hdc = GetDC(GraphicsWnd); + PaintGraphicsWithHdc(hdc); +} +SDWORD GetMilliseconds(void) +{ + return (SDWORD)GetTickCount(); +} + void InvalidateText(void) { InvalidateRect(TextWnd, NULL, FALSE); @@ -370,19 +397,7 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); - HGLRC hgrc = CreateGlContext(hdc); - - RECT r; - GetClientRect(GraphicsWnd, &r); - int w = r.right - r.left; - int h = r.bottom - r.top; - - SS.GW.Paint(w, h); - - SwapBuffers(hdc); - - wglMakeCurrent(NULL, NULL); - wglDeleteContext(hgrc); + PaintGraphicsWithHdc(hdc); EndPaint(hwnd, &ps); break; @@ -446,6 +461,58 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, return 1; } +//----------------------------------------------------------------------------- +// Common dialog routines, to open or save a file. +//----------------------------------------------------------------------------- +BOOL GetOpenFile(char *file, char *defExtension, char *selPattern) +{ + OPENFILENAME ofn; + + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hInstance = Instance; + ofn.hwndOwner = GraphicsWnd; + ofn.lpstrFilter = selPattern; + ofn.lpstrDefExt = defExtension; + ofn.lpstrFile = file; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + + EnableWindow(GraphicsWnd, FALSE); + BOOL r = GetOpenFileName(&ofn); + EnableWindow(GraphicsWnd, TRUE); + SetForegroundWindow(GraphicsWnd); + return r; +} +BOOL GetSaveFile(char *file, char *defExtension, char *selPattern) +{ + OPENFILENAME ofn; + + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hInstance = Instance; + ofn.hwndOwner = GraphicsWnd; + ofn.lpstrFilter = selPattern; + ofn.lpstrDefExt = defExtension; + ofn.lpstrFile = file; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + + EnableWindow(GraphicsWnd, FALSE); + BOOL r = GetSaveFileName(&ofn); + EnableWindow(GraphicsWnd, TRUE); + SetForegroundWindow(GraphicsWnd); + return r; +} +int SaveFileYesNoCancel(void) +{ + return MessageBox(GraphicsWnd, + "The program has changed since it was last saved.\r\n\r\n" + "Do you want to save the changes?", "SolveSpace", + MB_YESNOCANCEL | MB_ICONWARNING); +} + + static void MenuById(int id, BOOL yes, BOOL check) { int i;