diff --git a/entity.cpp b/entity.cpp index 884cf576..030c0e88 100644 --- a/entity.cpp +++ b/entity.cpp @@ -36,16 +36,15 @@ void Entity::DrawOrGetDistance(void) { if(!SS.GW.show2dCsyss) break; Vector p; - double a, b, c, d; - p = SS.point.FindById(point(16))->GetCoords(); - a = SS.param.FindById(param(0))->val; - b = SS.param.FindById(param(1))->val; - c = SS.param.FindById(param(2))->val; - d = SS.param.FindById(param(3))->val; - Vector u = Vector::RotationU(a, b, c, d); - Vector v = Vector::RotationV(a, b, c, d); + double q[4]; + for(int i = 0; i < 4; i++) { + q[i] = SS.param.FindById(param(i))->val; + } + + Vector u = Vector::RotationU(q[0], q[1], q[2], q[3]); + Vector v = Vector::RotationV(q[0], q[1], q[2], q[3]); double s = (min(SS.GW.width, SS.GW.height))*0.4; @@ -77,3 +76,4 @@ void Entity::DrawOrGetDistance(void) { oops(); } } + diff --git a/graphicswin.cpp b/graphicswin.cpp index 6ca1bb74..96afb21b 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -2,36 +2,43 @@ #include "solvespace.h" +#define mView (&GraphicsWindow::MenuView) +#define mEdit (&GraphicsWindow::MenuEdit) 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 }, +{ 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 }, - { 0, "&Edit", 0, NULL }, - { 1, "&Undo\tCtrl+Z", 0, NULL }, - { 1, "&Redo\tCtrl+Y", 0, NULL }, - { 0, "&View", 0, NULL }, - { 1, "Zoom &In\t+", 0, NULL }, - { 1, "Zoom &Out\t-", 0, NULL }, - { 1, "Zoom To &Fit\tF", 0, NULL }, - { 1, NULL, 0, NULL }, - { 1, "Dimensions in &Inches", 0, NULL }, - { 1, "Dimensions in &Millimeters", 0, NULL }, +{ 0, "&Edit", 0, NULL }, +{ 1, "&Undo\tCtrl+Z", 0, NULL }, +{ 1, "&Redo\tCtrl+Y", 0, NULL }, +{ 1, NULL, 0, NULL }, +{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit }, - { 0, "&Sketch", 0, NULL }, - { 1, NULL, 0, NULL }, - { 1, "To&ggle Construction\tG", 0, NULL }, +{ 0, "&View", 0, NULL }, +{ 1, "Zoom &In\t+", MNU_ZOOM_IN, '+', mView }, +{ 1, "Zoom &Out\t-", MNU_ZOOM_OUT, '-', mView }, +{ 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView }, +{ 1, NULL, 0, NULL }, +{ 1, "Onto Plane/Csys\tO", MNU_ORIENT_ONTO, 'O', mView }, +{ 1, NULL, 0, NULL }, +{ 1, "Dimensions in &Inches", 0, NULL }, +{ 1, "Dimensions in &Millimeters", 0, NULL }, - { 0, "&Constrain", 0, NULL }, - { 1, "S&ymmetric\tY", 0, NULL }, +{ 0, "&Sketch", 0, NULL }, +{ 1, NULL, 0, NULL }, +{ 1, "To&ggle Construction\tG", 0, NULL }, - { 0, "&Help", 0, NULL }, - { 1, "&About\t", 0, NULL }, - { -1 }, +{ 0, "&Constrain", 0, NULL }, +{ 1, "S&ymmetric\tY", 0, NULL }, + +{ 0, "&Help", 0, NULL }, +{ 1, "&About\t", 0, NULL }, +{ -1 }, }; void GraphicsWindow::Init(void) { @@ -40,7 +47,7 @@ void GraphicsWindow::Init(void) { offset.x = offset.y = offset.z = 0; scale = 1; projRight.x = 1; projRight.y = projRight.z = 0; - projDown.y = 1; projDown.z = projDown.x = 0; + projUp.y = 1; projUp.z = projUp.x = 0; show2dCsyss = true; showAxes = true; @@ -50,10 +57,10 @@ void GraphicsWindow::Init(void) { } void GraphicsWindow::NormalizeProjectionVectors(void) { - Vector norm = projRight.Cross(projDown); - projDown = norm.Cross(projRight); + Vector norm = projRight.Cross(projUp); + projUp = norm.Cross(projRight); - projDown = projDown.ScaledBy(1/projDown.Magnitude()); + projUp = projUp.ScaledBy(1/projUp.Magnitude()); projRight = projRight.ScaledBy(1/projRight.Magnitude()); } @@ -62,10 +69,56 @@ Point2d GraphicsWindow::ProjectPoint(Vector p) { Point2d r; r.x = p.Dot(projRight) * scale; - r.y = p.Dot(projDown) * scale; + r.y = p.Dot(projUp) * scale; return r; } +void GraphicsWindow::MenuView(MenuId id) { + switch(id) { + case MNU_ZOOM_IN: + SS.GW.scale *= 1.2; + break; + + case MNU_ZOOM_OUT: + SS.GW.scale /= 1.2; + break; + + case MNU_ZOOM_TO_FIT: + break; + + case MNU_ORIENT_ONTO: + SS.GW.GroupSelection(); + if(SS.GW.gs.n == 1 && SS.GW.gs.csyss == 1) { + Entity *e = SS.entity.FindById(SS.GW.gs.entity[0]); + double q[4]; + for(int i = 0; i < 4; i++) { + q[i] = SS.param.FindById(e->param(i))->val; + } + SS.GW.projRight = Vector::RotationU(q[0], q[1], q[2], q[3]); + SS.GW.projUp = Vector::RotationV(q[0], q[1], q[2], q[3]); + SS.GW.offset = SS.point.FindById(e->point(16))->GetCoords(); + SS.GW.ClearSelection(); + InvalidateGraphics(); + } else { + Error("Select plane or coordinate system before orienting."); + } + break; + + default: oops(); + } + InvalidateGraphics(); +} + +void GraphicsWindow::MenuEdit(MenuId id) { + switch(id) { + case MNU_UNSELECT_ALL: + SS.GW.ClearSelection(); + break; + + default: oops(); + } +} + void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown) { @@ -78,28 +131,28 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, double dy = (y - orig.mouse.y) / scale; if(shiftDown) { - offset.x = orig.offset.x + dx*projRight.x + dy*projDown.x; - offset.y = orig.offset.y + dx*projRight.y + dy*projDown.y; - offset.z = orig.offset.z + dx*projRight.z + dy*projDown.z; + 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) { double theta = atan2(orig.mouse.y, orig.mouse.x); theta -= atan2(y, x); - Vector normal = orig.projRight.Cross(orig.projDown); + Vector normal = orig.projRight.Cross(orig.projUp); projRight = orig.projRight.RotatedAbout(normal, theta); - projDown = orig.projDown.RotatedAbout(normal, theta); + projUp = orig.projUp.RotatedAbout(normal, theta); NormalizeProjectionVectors(); } else { double s = 0.3*(PI/180); // degrees per pixel - projRight = orig.projRight.RotatedAbout(orig.projDown, -s*dx); - projDown = orig.projDown.RotatedAbout(orig.projRight, s*dy); + projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx); + projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy); NormalizeProjectionVectors(); } orig.projRight = projRight; - orig.projDown = projDown; + orig.projUp = projUp; orig.offset = offset; orig.mouse.x = x; orig.mouse.y = y; @@ -142,6 +195,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { dest->point.v = 0; dest->entity.v = 0; + // Do the points for(i = 0; i < SS.entity.elems; i++) { d = SS.entity.elem[i].t.GetDistance(mp); if(d < 10 && d < dmin) { @@ -150,6 +204,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { } } + // Entities for(i = 0; i < SS.point.elems; i++) { d = SS.point.elem[i].t.GetDistance(mp); if(d < 10 && d < dmin) { @@ -159,9 +214,39 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { } } +void GraphicsWindow::ClearSelection(void) { + for(int i = 0; i < MAX_SELECTED; i++) { + selection[i].Clear(); + } + InvalidateGraphics(); +} + +void GraphicsWindow::GroupSelection(void) { + gs.points = gs.entities = 0; + gs.csyss = 0; + gs.n = 0; + int i; + for(i = 0; i < MAX_SELECTED; i++) { + Selection *s = &(selection[i]); + if(s->point.v) { + gs.point[(gs.points)++] = s->point; + (gs.n)++; + } + if(s->entity.v) { + gs.entity[(gs.entities)++] = s->entity; + (gs.n)++; + + Entity *e = SS.entity.FindById(s->entity); + if(e->type == Entity::CSYS_2D) { + (gs.csyss)++; + } + } + } +} + void GraphicsWindow::MouseMiddleDown(double x, double y) { orig.offset = offset; - orig.projDown = projDown; + orig.projUp = projUp; orig.projRight = projRight; orig.mouse.x = x; orig.mouse.y = y; @@ -193,22 +278,22 @@ done: void GraphicsWindow::MouseScroll(double x, double y, int delta) { double offsetRight = offset.Dot(projRight); - double offsetDown = offset.Dot(projDown); + double offsetUp = offset.Dot(projUp); double righti = x/scale - offsetRight; - double downi = y/scale - offsetDown; + double upi = y/scale - offsetUp; if(delta > 0) { - scale *= 1.3; + scale *= 1.2; } else { - scale /= 1.3; + scale /= 1.2; } double rightf = x/scale - offsetRight; - double downf = y/scale - offsetDown; + double upf = y/scale - offsetUp; offset = offset.Plus(projRight.ScaledBy(rightf - righti)); - offset = offset.Plus(projDown.ScaledBy(downf - downi)); + offset = offset.Plus(projUp.ScaledBy(upf - upi)); InvalidateGraphics(); } @@ -245,10 +330,10 @@ void GraphicsWindow::Paint(int w, int h) { glScaled(scale*2.0/w, scale*2.0/h, 0); double tx = projRight.Dot(offset); - double ty = projDown.Dot(offset); + double ty = projUp.Dot(offset); double mat[16]; MakeMatrix(mat, projRight.x, projRight.y, projRight.z, tx, - projDown.x, projDown.y, projDown.z, ty, + projUp.x, projUp.y, projUp.z, ty, 0, 0, 0, 0, 0, 0, 0, 1); glMultMatrixd(mat); diff --git a/sketch.cpp b/sketch.cpp index 29312caf..1ef1d0cb 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -126,7 +126,7 @@ void Point::Draw(void) { double s = 4; Vector r = SS.GW.projRight.ScaledBy(4/SS.GW.scale); - Vector d = SS.GW.projDown.ScaledBy(4/SS.GW.scale); + Vector d = SS.GW.projUp.ScaledBy(4/SS.GW.scale); glBegin(GL_QUADS); glxVertex3v(v.Plus (r).Plus (d)); diff --git a/solvespace.h b/solvespace.h index 7fa847fc..3bc3fd46 100644 --- a/solvespace.h +++ b/solvespace.h @@ -13,6 +13,7 @@ #endif void dbp(char *str, ...); +void Error(char *str, ...); #include #include #include diff --git a/ui.h b/ui.h index cde9272e..56a56690 100644 --- a/ui.h +++ b/ui.h @@ -69,17 +69,27 @@ public: class GraphicsWindow { public: + void Init(void); + // This table describes the top-level menus in the graphics winodw. - typedef void MenuHandler(int id); + typedef enum { + MNU_ZOOM_IN = 100, + MNU_ZOOM_OUT, + MNU_ZOOM_TO_FIT, + MNU_ORIENT_ONTO, + MNU_UNSELECT_ALL, + } MenuId; + typedef void MenuHandler(MenuId id); typedef struct { int level; // 0 == on menu bar, 1 == one level down char *label; // or NULL for a separator int id; // unique ID + int accel; // keyboard accelerator MenuHandler *fn; } MenuEntry; static const MenuEntry menu[]; - - void Init(void); + static void MenuView(MenuId id); + static void MenuEdit(MenuId id); // The width and height (in pixels) of the window. double width, height; @@ -88,12 +98,12 @@ public: // projection. Vector offset; Vector projRight; - Vector projDown; + Vector projUp; double scale; struct { Vector offset; Vector projRight; - Vector projDown; + Vector projUp; Point2d mouse; } orig; @@ -116,6 +126,16 @@ public: static const int MAX_SELECTED = 32; Selection selection[MAX_SELECTED]; void HitTestMakeSelection(Point2d mp, Selection *dest); + void ClearSelection(void); + struct { + hPoint point[MAX_SELECTED]; + hEntity entity[MAX_SELECTED]; + int points; + int entities; + int csyss; + int n; + } gs; + void GroupSelection(void); // This sets what gets displayed. bool show2dCsyss; diff --git a/win32/w32main.cpp b/win32/w32main.cpp index 4487ea89..cc22a193 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -42,6 +42,17 @@ void dbp(char *str, ...) OutputDebugString("\n"); } +void Error(char *str, ...) +{ + va_list f; + char buf[1024]; + va_start(f, str); + vsprintf(buf, str, f); + + HWND h = GetForegroundWindow(); + MessageBox(h, buf, "SolveSpace Error", MB_OK | MB_ICONERROR); +} + static void PaintTextWnd(HDC hdc) { RECT rect; @@ -241,6 +252,37 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return 1; } +static BOOL ProcessKeyDown(WPARAM wParam) +{ + int c; + switch(wParam) { + case VK_OEM_PLUS: c = '+'; break; + case VK_OEM_MINUS: c = '-'; break; + case VK_ESCAPE: c = 27; break; + case VK_OEM_4: c = '['; break; + case VK_OEM_6: c = ']'; break; + case VK_OEM_5: c = '\\'; break; + case VK_SPACE: c = ' '; break; + case VK_DELETE: c = 127; break; + + default: + c = wParam; + break; + } + if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= 0x100; + if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= 0x200; + + for(int i = 0; SS.GW.menu[i].level >= 0; i++) { + if(c == SS.GW.menu[i].accel) { + (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)SS.GW.menu[i].id); + break; + } + } + + // No accelerator; process the key as normal. + return FALSE; +} + static HGLRC CreateGlContext(HDC hdc) { PIXELFORMATDESCRIPTOR pfd; @@ -347,6 +389,16 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, SS.GW.MouseScroll(LastMousePos.x, LastMousePos.y, delta); break; } + case WM_COMMAND: { + int id = LOWORD(wParam); + for(int i = 0; SS.GW.menu[i].level >= 0; i++) { + if(id == SS.GW.menu[i].id) { + (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)id); + break; + } + } + break; + } case WM_CLOSE: case WM_DESTROY: @@ -485,6 +537,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, MSG msg; DWORD ret; while(ret = GetMessage(&msg, NULL, 0, 0)) { + if(msg.message == WM_KEYDOWN) { + if(ProcessKeyDown(msg.wParam)) continue; + } TranslateMessage(&msg); DispatchMessage(&msg); }