From 22302dca7a5be1c221ba0e90545801597ae4c041 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Mon, 14 Apr 2008 02:28:32 -0800 Subject: [PATCH] Now I can add a constraint (a length), and it's displayed on-screen, and I can drag the label. That's progress. Also implement a bunch of untested expression stuff, since I'll need that for the values of the dimensions, for example. [git-p4: depot-paths = "//depot/solvespace/": change = 1668] --- Makefile | 2 + constraint.cpp | 39 +++++++++++++++ drawconstraint.cpp | 72 +++++++++++++++++++++++++++ dsc.h | 10 ++++ expr.cpp | 70 ++++++++++++++++++++++++++ expr.h | 5 +- graphicswin.cpp | 119 ++++++++++++++++++++++++++++++++++----------- sketch.h | 72 +++++++++++++++++++++++++-- solvespace.cpp | 8 ++- solvespace.h | 44 ++++++++++------- ui.h | 19 ++++++-- util.cpp | 11 ++++- win32/w32main.cpp | 25 ++++++++-- 13 files changed, 433 insertions(+), 63 deletions(-) create mode 100644 constraint.cpp create mode 100644 drawconstraint.cpp diff --git a/Makefile b/Makefile index 7de4a1e..7398de5 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,8 @@ SSOBJS = $(OBJDIR)\solvespace.obj \ $(OBJDIR)\sketch.obj \ $(OBJDIR)\glhelper.obj \ $(OBJDIR)\expr.obj \ + $(OBJDIR)\constraint.obj \ + $(OBJDIR)\drawconstraint.obj \ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib diff --git a/constraint.cpp b/constraint.cpp new file mode 100644 index 0000000..40fb4e1 --- /dev/null +++ b/constraint.cpp @@ -0,0 +1,39 @@ +#include "solvespace.h" + +hConstraint Constraint::AddConstraint(Constraint *c) { + SS.constraint.AddAndAssignId(c); + return c->h; +} + +void Constraint::MenuConstrain(int id) { + Constraint c; + memset(&c, 0, sizeof(c)); + c.group = SS.GW.activeGroup; + + SS.GW.GroupSelection(); +#define gs (SS.GW.gs) + + switch(id) { + case GraphicsWindow::MNU_DISTANCE_DIA: + if(gs.points == 2 && gs.n == 2) { + c.type = PT_PT_DISTANCE; + c.ptA = gs.point[0]; + c.ptB = gs.point[1]; + } else if(gs.lineSegments == 1 && gs.n == 1) { + c.type = PT_PT_DISTANCE; + c.ptA = gs.entity[0].point(16); + c.ptB = gs.entity[0].point(16+3); + } else { + Error("Bad selection for distance / diameter constraint."); + return; + } + c.disp.offset = Vector::MakeFrom(50, 50, 50); + AddConstraint(&c); + break; + + default: oops(); + } + + SS.GW.ClearSelection(); + InvalidateGraphics(); +} diff --git a/drawconstraint.cpp b/drawconstraint.cpp new file mode 100644 index 0000000..2074da8 --- /dev/null +++ b/drawconstraint.cpp @@ -0,0 +1,72 @@ +#include "solvespace.h" + +bool Constraint::HasLabel(void) { + switch(type) { + case PT_PT_DISTANCE: + return true; + + default: + return false; + } +} + +void Constraint::DrawOrGetDistance(void) { + // Unit vectors that describe our current view of the scene. + Vector gr = SS.GW.projRight; + Vector gu = SS.GW.projUp; + Vector gn = gr.Cross(gu); + + switch(type) { + case PT_PT_DISTANCE: { + Vector ap = SS.GetPoint(ptA)->GetCoords(); + Vector bp = SS.GetPoint(ptB)->GetCoords(); + + Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); + + 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); + glxWriteText("ABCDEFG"); + glPopMatrix(); + } else { + Point2d o = SS.GW.ProjectPoint(ref); + dogd.dmin = o.DistanceTo(dogd.mp) - 10; + } + + break; + } + + default: oops(); + } +} + +void Constraint::Draw(void) { + dogd.drawing = true; + DrawOrGetDistance(); +} + +double Constraint::GetDistance(Point2d mp) { + dogd.drawing = false; + dogd.mp = mp; + dogd.dmin = 1e12; + + DrawOrGetDistance(); + + return dogd.dmin; +} + diff --git a/dsc.h b/dsc.h index ea77cc3..63cdaad 100644 --- a/dsc.h +++ b/dsc.h @@ -19,6 +19,7 @@ public: Vector Normal(int which); Vector RotatedAbout(Vector axis, double theta); double Magnitude(void); + Vector WithMagnitude(double s); Vector ScaledBy(double s); // Call a rotation matrix [ u' v' n' ]'; this returns the first and @@ -103,6 +104,15 @@ public: } } + void Tag(H h, int tag) { + int i; + for(i = 0; i < elems; i++) { + if(elem[i].t.h.v == h.v) { + elem[i].tag = tag; + } + } + } + void RemoveTagged(void) { int src, dest; dest = 0; diff --git a/expr.cpp b/expr.cpp index c009504..a8245ac 100644 --- a/expr.cpp +++ b/expr.cpp @@ -44,3 +44,73 @@ double Expr::Eval(void) { } } +Expr *Expr::PartialWrt(hParam p) { + Expr *da, *db; + + switch(op) { + case PARAM_PTR: oops(); + case PARAM: return FromConstant(p.v == x.parh.v ? 1 : 0); + + case CONSTANT: return FromConstant(0); + + case PLUS: return (a->PartialWrt(p))->Plus(b->PartialWrt(p)); + case MINUS: return (a->PartialWrt(p))->Minus(b->PartialWrt(p)); + + case TIMES: + da = a->PartialWrt(p); + db = b->PartialWrt(p); + return (a->Times(db))->Plus(b->Times(da)); + + case DIV: + da = a->PartialWrt(p); + db = b->PartialWrt(p); + return ((da->Times(b))->Minus(a->Times(db)))->Div(b->Square()); + + case SQRT: + return (FromConstant(0.5)->Div(a->Sqrt()))->Times(a->PartialWrt(p)); + + case SQUARE: + return (FromConstant(2.0)->Times(a))->Times(a->PartialWrt(p)); + + case NEGATE: return (a->PartialWrt(p))->Negate(); + case SIN: return (a->Cos())->Times(a->PartialWrt(p)); + case COS: return ((a->Sin())->Times(a->PartialWrt(p)))->Negate(); + + default: oops(); + } +} + +static char StringBuffer[4096]; +void Expr::App(char *s, ...) { + va_list f; + va_start(f, s); + vsprintf(StringBuffer+strlen(StringBuffer), s, f); +} +char *Expr::Print(void) { + StringBuffer[0] = '\0'; + PrintW(); + return StringBuffer; +} + +void Expr::PrintW(void) { + switch(op) { + case PARAM: App("(param %08x)", x.parh.v); break; + case PARAM_PTR: App("(paramp %08x)", x.parp->h.v); break; + + case CONSTANT: App("%.3f", x.v); + + case PLUS: App("(+ "); a->PrintW(); b->PrintW(); App(")"); break; + case MINUS: App("(- "); a->PrintW(); b->PrintW(); App(")"); break; + case TIMES: App("(* "); a->PrintW(); b->PrintW(); App(")"); break; + case DIV: App("(/ "); a->PrintW(); b->PrintW(); App(")"); break; + + case NEGATE: App("(- "); a->PrintW(); App(")"); break; + case SQRT: App("(sqrt "); a->PrintW(); App(")"); break; + case SQUARE: App("(square "); a->PrintW(); App(")"); break; + case SIN: App("(sin "); a->PrintW(); App(")"); break; + case COS: App("(cos "); a->PrintW(); App(")"); break; + + default: oops(); + } +} + diff --git a/expr.h b/expr.h index aea9891..d26a969 100644 --- a/expr.h +++ b/expr.h @@ -58,7 +58,10 @@ public: double Eval(void); void ParamsToPointers(void); - void Print(void); + + void App(char *str, ...); + char *Print(void); + void PrintW(void); // worker }; #endif diff --git a/graphicswin.cpp b/graphicswin.cpp index b2da9a5..a229300 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -5,6 +5,7 @@ #define mView (&GraphicsWindow::MenuView) #define mEdit (&GraphicsWindow::MenuEdit) #define mReq (&GraphicsWindow::MenuRequest) +#define mCon (&Constraint::MenuConstrain) #define S 0x100 const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 0, "&File", 0, NULL }, @@ -19,6 +20,8 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "&Undo\tCtrl+Z", 0, NULL }, { 1, "&Redo\tCtrl+Y", 0, NULL }, { 1, NULL, 0, NULL }, +{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit }, +{ 1, NULL, 0, NULL }, { 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit }, { 0, "&View", 0, NULL }, @@ -27,7 +30,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView }, { 1, NULL, 0, NULL }, { 1, "&Onto Plane / Coordinate System\tO", MNU_ORIENT_ONTO, 'O', mView }, -{ 1, "&Lock Orientation\tL", 0, 'L', mView }, +{ 1, "&Lock Orientation\tL", MNU_LOCK_VIEW, 'L', mView }, { 1, NULL, 0, NULL }, { 1, "Dimensions in &Inches", 0, NULL }, { 1, "Dimensions in &Millimeters", 0, NULL }, @@ -54,7 +57,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "To&ggle Construction\tG", 0, 'G', NULL }, { 0, "&Constrain", 0, NULL }, -{ 1, "&Distance / Diameter\tShift+D", 0, 'D'|S, NULL }, +{ 1, "&Distance / Diameter\tShift+D", MNU_DISTANCE_DIA, 'D'|S, mCon }, { 1, "A&ngle\tShift+N", 0, 'N'|S, NULL }, { 1, "Other S&upplementary Angle\tShift+U", 0, 'U'|S, NULL }, { 1, NULL, 0, NULL }, @@ -108,7 +111,7 @@ Point2d GraphicsWindow::ProjectPoint(Vector p) { return r; } -void GraphicsWindow::MenuView(MenuId id) { +void GraphicsWindow::MenuView(int id) { switch(id) { case MNU_ZOOM_IN: SS.GW.scale *= 1.2; @@ -121,6 +124,11 @@ void GraphicsWindow::MenuView(MenuId id) { case MNU_ZOOM_TO_FIT: break; + case MNU_LOCK_VIEW: + SS.GW.viewLocked = !SS.GW.viewLocked; + CheckMenuById(MNU_LOCK_VIEW, SS.GW.viewLocked); + break; + case MNU_ORIENT_ONTO: SS.GW.GroupSelection(); if(SS.GW.gs.n == 1 && SS.GW.gs.csyss == 1) { @@ -155,7 +163,7 @@ void GraphicsWindow::EnsureValidActiveGroup(void) { activeGroup = SS.group.elem[i].t.h; } -void GraphicsWindow::MenuEdit(MenuId id) { +void GraphicsWindow::MenuEdit(int id) { switch(id) { case MNU_UNSELECT_ALL: SS.GW.ClearSelection(); @@ -164,11 +172,38 @@ void GraphicsWindow::MenuEdit(MenuId id) { SS.TW.Show(); break; + case MNU_DELETE: { + int i; + SS.request.ClearTags(); + for(i = 0; i < MAX_SELECTED; i++) { + Selection *s = &(SS.GW.selection[i]); + hRequest r; + r.v = 0; + if(s->point.v) { + Point *pt = SS.GetPoint(s->point); + Entity *e = SS.GetEntity(pt->entity()); + if(e->type == Entity::DATUM_POINT) { + r = e->request(); + } + } else if(s->entity.v) { + Entity *e = SS.GetEntity(s->entity); + r = e->request(); + } + if(r.v) SS.request.Tag(r, 1); + } + SS.request.RemoveTagged(); + + SS.GenerateAll(); + SS.GW.ClearSelection(); + SS.GW.hover.Clear(); + break; + } + default: oops(); } } -void GraphicsWindow::MenuRequest(MenuId id) { +void GraphicsWindow::MenuRequest(int id) { char *s; switch(id) { case MNU_DATUM_POINT: s = "click to place datum point"; goto c; @@ -183,12 +218,16 @@ c: } } -void GraphicsWindow::UpdateDraggedPoint(hPoint hp, double mx, double my) { +void GraphicsWindow::UpdateDraggedHPoint(hPoint hp, double mx, double my) { Point *p = SS.point.FindById(hp); Vector pos = p->GetCoords(); - pos = pos.Plus(projRight.ScaledBy((mx - orig.mouse.x)/scale)); - pos = pos.Plus(projUp.ScaledBy((my - orig.mouse.y)/scale)); + UpdateDraggedPoint(&pos, mx, my); p->ForceTo(pos); +} + +void GraphicsWindow::UpdateDraggedPoint(Vector *pos, double mx, double my) { + *pos = pos->Plus(projRight.ScaledBy((mx - orig.mouse.x)/scale)); + *pos = pos->Plus(projUp.ScaledBy((my - orig.mouse.y)/scale)); orig.mouse.x = mx; orig.mouse.y = my; @@ -206,11 +245,12 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, double dx = (x - orig.mouse.x) / scale; double dy = (y - orig.mouse.y) / scale; - if(shiftDown) { + // When the view is locked, permit only translation (pan). + if(shiftDown || viewLocked) { offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x; offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y; offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z; - } else if(ctrlDown) { + } else if(ctrlDown && !viewLocked) { double theta = atan2(orig.mouse.y, orig.mouse.x); theta -= atan2(y, x); @@ -219,7 +259,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, projUp = orig.projUp.RotatedAbout(normal, theta); NormalizeProjectionVectors(); - } else { + } else if(!viewLocked) { double s = 0.3*(PI/180); // degrees per pixel projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx); projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy); @@ -235,14 +275,21 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, InvalidateGraphics(); } else if(leftDown) { - // We are left-dragging. This is often used to drag points. + // We are left-dragging. This is often used to drag points, or + // constraint labels. if(hover.point.v && !hover.point.isFromReferences()) { ClearSelection(); - UpdateDraggedPoint(hover.point, x, y); + UpdateDraggedHPoint(hover.point, x, y); + } else if(hover.constraint.v && + SS.GetConstraint(hover.constraint)->HasLabel()) + { + ClearSelection(); + Constraint *c = SS.constraint.FindById(hover.constraint); + UpdateDraggedPoint(&(c->disp.offset), x, y); } } else { if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT) { - UpdateDraggedPoint(pendingPoint, x, y); + UpdateDraggedHPoint(pendingPoint, x, y); } else { // Do our usual hit testing, for the selection. Selection s; @@ -256,35 +303,37 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, } bool GraphicsWindow::Selection::Equals(Selection *b) { - if(point.v != b->point.v) return false; - if(entity.v != b->entity.v) return false; + if(point.v != b->point.v) return false; + if(entity.v != b->entity.v) return false; + if(constraint.v != b->constraint.v) return false; return true; } bool GraphicsWindow::Selection::IsEmpty(void) { - if(point.v) return false; - if(entity.v) return false; + if(point.v) return false; + if(entity.v) return false; + if(constraint.v) return false; return true; } void GraphicsWindow::Selection::Clear(void) { - point.v = entity.v = 0; + point.v = entity.v = constraint.v = 0; } void GraphicsWindow::Selection::Draw(void) { - if(point.v) SS.point. FindById(point )->Draw(); - if(entity.v) SS.entity.FindById(entity)->Draw(); + if(point.v) SS.point. FindById(point )->Draw(); + if(entity.v) SS.entity. FindById(entity )->Draw(); + if(constraint.v) SS.constraint.FindById(constraint)->Draw(); } void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { int i; double d, dmin = 1e12; - dest->point.v = 0; - dest->entity.v = 0; + memset(dest, 0, sizeof(*dest)); // Do the points for(i = 0; i < SS.entity.elems; i++) { d = SS.entity.elem[i].t.GetDistance(mp); if(d < 10 && d < dmin) { - dest->point.v = 0; + memset(dest, 0, sizeof(*dest)); dest->entity = SS.entity.elem[i].t.h; } } @@ -293,10 +342,19 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { for(i = 0; i < SS.point.elems; i++) { d = SS.point.elem[i].t.GetDistance(mp); if(d < 10 && d < dmin) { - dest->entity.v = 0; + memset(dest, 0, sizeof(*dest)); dest->point = SS.point.elem[i].t.h; } } + + // Constraints + for(i = 0; i < SS.constraint.elems; i++) { + d = SS.constraint.elem[i].t.GetDistance(mp); + if(d < 10 && d < dmin) { + memset(dest, 0, sizeof(*dest)); + dest->constraint = SS.constraint.elem[i].t.h; + } + } } void GraphicsWindow::ClearSelection(void) { @@ -308,7 +366,7 @@ void GraphicsWindow::ClearSelection(void) { void GraphicsWindow::GroupSelection(void) { gs.points = gs.entities = 0; - gs.csyss = 0; + gs.csyss = gs.lineSegments = 0; gs.n = 0; int i; for(i = 0; i < MAX_SELECTED; i++) { @@ -322,8 +380,9 @@ void GraphicsWindow::GroupSelection(void) { (gs.n)++; Entity *e = SS.entity.FindById(s->entity); - if(e->type == Entity::CSYS_2D) { - (gs.csyss)++; + switch(e->type) { + case Entity::CSYS_2D: (gs.csyss)++; break; + case Entity::LINE_SEGMENT: (gs.lineSegments)++; break; } } } @@ -496,6 +555,10 @@ void GraphicsWindow::Paint(int w, int h) { for(i = 0; i < SS.point.elems; i++) { SS.point.elem[i].t.Draw(); } + glColor3f(1.0f, 0, 1.0f); + for(i = 0; i < SS.constraint.elems; i++) { + SS.constraint.elem[i].t.Draw(); + } // Then redraw whatever the mouse is hovering over, highlighted. Have // to disable the depth test, so that we can overdraw. diff --git a/sketch.h b/sketch.h index ad8cbdf..8903e52 100644 --- a/sketch.h +++ b/sketch.h @@ -12,6 +12,9 @@ class Entity; class Param; class Point; +class hEquation; +class Equation; + // All of the hWhatever handles are a 32-bit ID, that is used to represent // some data structure in the sketch. class hGroup { @@ -79,9 +82,9 @@ public: static const hRequest HREQUEST_REFERENCE_ZX; // Types of requests - static const int CSYS_2D = 0; - static const int DATUM_POINT = 1; - static const int LINE_SEGMENT = 10; + static const int CSYS_2D = 10; + static const int DATUM_POINT = 11; + static const int LINE_SEGMENT = 20; int type; @@ -173,6 +176,69 @@ public: double GetDistance(Point2d mp); }; +class hConstraint { +public: + DWORD v; +}; + +class Constraint { +public: + static const int USER_EQUATION = 10; + static const int POINTS_COINCIDENT = 20; + static const int PT_PT_DISTANCE = 30; + static const int PT_LINE_DISTANCE = 31; + + static const int HORIZONTAL = 40; + static const int VERTICAL = 41; + + hConstraint h; + int type; + hGroup group; + + // These are the parameters for the constraint. + Expr *exprA; + Expr *exprB; + hPoint ptA; + hPoint ptB; + hPoint ptC; + hEntity entityA; + hEntity entityB; + + // These define how the constraint is drawn on-screen. + struct { + hEntity csys; + Vector offset; + Vector u, v; + } disp; + + static hConstraint AddConstraint(Constraint *c); + static void MenuConstrain(int id); + + struct { + bool drawing; + Point2d mp; + double dmin; + } dogd; // state for drawing or getting distance (for hit testing) + double GetDistance(Point2d mp); + void Draw(void); + void DrawOrGetDistance(void); + + bool HasLabel(void); + + void Generate(IdList *l); +}; + +class hEquation { +public: + DWORD v; +}; + +class Equation { +public: + hEquation h; + Expr *e; +}; + inline hEntity hRequest::entity(int i) { hEntity r; r.v = (v << 10) | i; return r; } diff --git a/solvespace.cpp b/solvespace.cpp index d3f14f6..e9da016 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -1,17 +1,15 @@ #include "solvespace.h" -template IdList; -template IdList; -template IdList; - SolveSpace SS; void SolveSpace::Init(void) { + constraint.Clear(); request.Clear(); + group.Clear(); + entity.Clear(); point.Clear(); param.Clear(); - group.Clear(); // Our initial group, that contains the references. Group g; diff --git a/solvespace.h b/solvespace.h index dad8c4e..ade285b 100644 --- a/solvespace.h +++ b/solvespace.h @@ -12,13 +12,6 @@ #define max(x, y) ((x) > (y) ? (x) : (y)) #endif -class Expr; - -void dbp(char *str, ...); -void Error(char *str, ...); -Expr *AllocExpr(void); -void FreeAllExprs(void); - #include #include #include @@ -27,14 +20,23 @@ void FreeAllExprs(void); #include #include +class Expr; + +// From the platform-specific code. +void CheckMenuById(int id, BOOL checked); +void InvalidateGraphics(void); +void InvalidateText(void); +void dbp(char *str, ...); +void Error(char *str, ...); +Expr *AllocExpr(void); +void FreeAllExprs(void); + + #include "dsc.h" #include "sketch.h" #include "ui.h" #include "expr.h" -// From the platform-specific code. -void InvalidateGraphics(void); -void InvalidateText(void); // Utility functions that are provided in the platform-independent code. void glxVertex3v(Vector u); @@ -56,14 +58,22 @@ public: TextWindow TW; GraphicsWindow GW; - IdList group; - IdList request; - IdList entity; - IdList point; - IdList param; + // These lists define the sketch, and are edited by the user. + IdList group; + IdList request; + IdList constraint; - inline Entity *GetEntity(hEntity h) { return entity.FindById(h); } - inline Param *GetParam (hParam h) { return param. FindById(h); } + // These lists are generated automatically when we solve the sketch. + IdList entity; + IdList point; + IdList param; + + inline Constraint *GetConstraint(hConstraint h) + { return constraint.FindById(h); } + inline Request *GetRequest(hRequest h) { return request.FindById(h); } + inline Entity *GetEntity (hEntity h) { return entity. FindById(h); } + inline Param *GetParam (hParam h) { return param. FindById(h); } + inline Point *GetPoint (hPoint h) { return point. FindById(h); } hGroup activeGroup; diff --git a/ui.h b/ui.h index 5370e6e..1ea0b01 100644 --- a/ui.h +++ b/ui.h @@ -78,12 +78,17 @@ public: MNU_ZOOM_OUT, MNU_ZOOM_TO_FIT, MNU_ORIENT_ONTO, + MNU_LOCK_VIEW, MNU_UNSELECT_ALL, + // Edit + MNU_DELETE, // Request MNU_DATUM_POINT, MNU_LINE_SEGMENT, + // Constrain + MNU_DISTANCE_DIA, } MenuId; - typedef void MenuHandler(MenuId id); + typedef void MenuHandler(int id); typedef struct { int level; // 0 == on menu bar, 1 == one level down char *label; // or NULL for a separator @@ -92,9 +97,9 @@ public: MenuHandler *fn; } MenuEntry; static const MenuEntry menu[]; - static void MenuView(MenuId id); - static void MenuEdit(MenuId id); - static void MenuRequest(MenuId id); + static void MenuView(int id); + static void MenuEdit(int id); + static void MenuRequest(int id); // The width and height (in pixels) of the window. double width, height; @@ -111,6 +116,7 @@ public: Vector projUp; Point2d mouse; } orig; + bool viewLocked; void NormalizeProjectionVectors(void); Point2d ProjectPoint(Vector p); @@ -132,6 +138,7 @@ public: public: hPoint point; hEntity entity; + hConstraint constraint; void Draw(void); @@ -150,6 +157,7 @@ public: int points; int entities; int csyss; + int lineSegments; int n; } gs; void GroupSelection(void); @@ -163,7 +171,8 @@ public: static void ToggleBool(int link, DWORD v); static void ToggleAnyDatumShown(int link, DWORD v); - void UpdateDraggedPoint(hPoint hp, double mx, double my); + void UpdateDraggedPoint(Vector *pos, double mx, double my); + void UpdateDraggedHPoint(hPoint hp, double mx, double my); // These are called by the platform-specific code. void Paint(int w, int h); diff --git a/util.cpp b/util.cpp index 2138e5e..766a084 100644 --- a/util.cpp +++ b/util.cpp @@ -120,7 +120,7 @@ Vector Vector::Normal(int which) { oops(); } - n = n.ScaledBy(1/n.Magnitude()); + n = n.WithMagnitude(1); return n; } @@ -160,6 +160,15 @@ Vector Vector::ScaledBy(double v) { return r; } +Vector Vector::WithMagnitude(double v) { + double m = Magnitude(); + if(m < 0.001) { + return MakeFrom(v, 0, 0); + } else { + return ScaledBy(v/Magnitude()); + } +} + Point2d Point2d::Plus(Point2d b) { Point2d r; r.x = x + b.x; diff --git a/win32/w32main.cpp b/win32/w32main.cpp index febedd8..546560e 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -39,7 +39,6 @@ void dbp(char *str, ...) va_start(f, str); vsprintf(buf, str, f); OutputDebugString(buf); - OutputDebugString("\n"); } void Error(char *str, ...) @@ -300,8 +299,8 @@ static BOOL ProcessKeyDown(WPARAM wParam) c = wParam; break; } - if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= 0x100; - if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= 0x200; + if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= 0x100; + if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= 0x200; for(int i = 0; SS.GW.menu[i].level >= 0; i++) { if(c == SS.GW.menu[i].accel) { @@ -443,6 +442,26 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, return 1; } +void CheckMenuById(int id, BOOL checked) +{ + int i; + int subMenu = -1; + + for(i = 0; SS.GW.menu[i].level >= 0; i++) { + if(SS.GW.menu[i].level == 0) subMenu++; + + if(SS.GW.menu[i].id == id) { + if(subMenu < 0) oops(); + if(subMenu >= (sizeof(SubMenus)/sizeof(SubMenus[0]))) oops(); + + CheckMenuItem(SubMenus[subMenu], id, + checked ? MF_CHECKED : MF_UNCHECKED); + return; + } + } + oops(); +} + HMENU CreateGraphicsWindowMenus(void) { HMENU top = CreateMenu();