From 4375c01c51be9506de1f215c5159185240ac4797 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Mon, 26 May 2008 22:36:59 -0800 Subject: [PATCH] Add step and repeat translate, with multiple copies; that all works nicely. And to do that, I've added the user interface to show an edit control in the text window. [git-p4: depot-paths = "//depot/solvespace/": change = 1749] --- drawconstraint.cpp | 2 +- entity.cpp | 1 + file.cpp | 1 + graphicswin.cpp | 3 ++ mesh.cpp | 2 +- sketch.cpp | 52 +++++++++++++++++++++++++++------- sketch.h | 5 ++-- solvespace.cpp | 2 ++ solvespace.h | 3 ++ textwin.cpp | 67 ++++++++++++++++++++++++++++++++++++-------- ui.h | 14 +++++++++- win32/w32main.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++---- 12 files changed, 189 insertions(+), 32 deletions(-) diff --git a/drawconstraint.cpp b/drawconstraint.cpp index e0d917c..7a6edbb 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -460,7 +460,7 @@ s: Vector a = SS.GetEntity(ptA)->PointGetNum(); Vector b = SS.GetEntity(ptB)->PointGetNum(); - Entity *w = SS.GetEntity(SS.GetEntity(ptA)->workplane); + Entity *w = SS.GetEntity(workplane); Vector cu = w->Normal()->NormalU(); Vector cv = w->Normal()->NormalV(); Vector cn = w->Normal()->NormalN(); diff --git a/entity.cpp b/entity.cpp index 449aeec..f056831 100644 --- a/entity.cpp +++ b/entity.cpp @@ -332,6 +332,7 @@ void Entity::PointForceTo(Vector p) { } case POINT_N_TRANS: { + if(timesApplied == 0) break; Vector trans = (p.Minus(numPoint)).ScaledBy(1.0/timesApplied); SS.GetParam(param[0])->val = trans.x; SS.GetParam(param[1])->val = trans.y; diff --git a/file.cpp b/file.cpp index 1828a1a..b3dcc18 100644 --- a/file.cpp +++ b/file.cpp @@ -55,6 +55,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'g', "Group.activeWorkplane.v", 'x', &(SS.sv.g.activeWorkplane.v) }, { 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) }, { 'g', "Group.opB.v", 'x', &(SS.sv.g.opB.v) }, + { 'g', "Group.exprA", 'E', &(SS.sv.g.exprA) }, { 'g', "Group.subtype", 'd', &(SS.sv.g.subtype) }, { 'g', "Group.meshCombine", 'd', &(SS.sv.g.meshCombine) }, { 'g', "Group.wrkpl.q.w", 'f', &(SS.sv.g.wrkpl.q.w) }, diff --git a/graphicswin.cpp b/graphicswin.cpp index 8e803f8..c956495 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -292,6 +292,7 @@ void GraphicsWindow::MenuEdit(int id) { switch(id) { case MNU_UNSELECT_ALL: HideGraphicsEditControl(); + HideTextEditControl(); SS.GW.ClearSelection(); SS.GW.ClearPending(); SS.TW.ScreenNavigation('h', 0); @@ -320,6 +321,8 @@ void GraphicsWindow::MenuEdit(int id) { // Forget any mention of the just-deleted entity SS.GW.ClearSuper(); + HideGraphicsEditControl(); + HideTextEditControl(); // 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, 0, INT_MAX); diff --git a/mesh.cpp b/mesh.cpp index ae8cc4e..38ab4fc 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -50,7 +50,7 @@ void SMesh::GetBounding(Vector *vmax, Vector *vmin) { } void SMesh::Simplify(int start) { -#define MAX_TRIANGLES 500 +#define MAX_TRIANGLES 2000 if(l.n - start > MAX_TRIANGLES) oops(); STriangle tout[MAX_TRIANGLES]; diff --git a/sketch.cpp b/sketch.cpp index aee8e0c..dfdc064 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -75,16 +75,26 @@ void Group::MenuGroup(int id) { g.type = EXTRUDE; g.opA = SS.GW.activeGroup; g.wrkpl.entityB = SS.GW.ActiveWorkplane(); - g.subtype = EXTRUDE_ONE_SIDED; + g.subtype = ONE_SIDED; g.name.strcpy("extrude"); break; case GraphicsWindow::MNU_GROUP_ROT: g.type = ROTATE; g.opA = SS.GW.activeGroup; + g.exprA = Expr::FromConstant(7)->DeepCopyKeep(); + g.subtype = ONE_SIDED; g.name.strcpy("rotate"); break; + case GraphicsWindow::MNU_GROUP_TRANS: + g.type = TRANSLATE; + g.opA = SS.GW.activeGroup; + g.exprA = Expr::FromConstant(7)->DeepCopyKeep(); + g.subtype = ONE_SIDED; + g.name.strcpy("translate"); + break; + default: oops(); } @@ -113,8 +123,10 @@ void Group::Generate(IdList *entity, IdList *param) { Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp); + Vector gp = SS.GW.projRight.Plus(SS.GW.projUp); gn = gn.WithMagnitude(200/SS.GW.scale); - int i; + gp = gp.WithMagnitude(200/SS.GW.scale); + int a, i; switch(type) { case DRAWING_3D: break; @@ -170,9 +182,9 @@ void Group::Generate(IdList *entity, AddParam(param, h.param(1), gn.y); AddParam(param, h.param(2), gn.z); int ai, af; - if(subtype == EXTRUDE_ONE_SIDED) { - ai = 0; af = 1; - } else if(subtype == EXTRUDE_TWO_SIDED) { + if(subtype == ONE_SIDED) { + ai = 0; af = 2; + } else if(subtype == TWO_SIDED) { ai = -1; af = 1; } else oops(); for(i = 0; i < entity->n; i++) { @@ -194,11 +206,31 @@ void Group::Generate(IdList *entity, } break; + case TRANSLATE: { + // The translation vector + AddParam(param, h.param(0), gp.x); + AddParam(param, h.param(1), gp.y); + AddParam(param, h.param(2), gp.z); + + int n = (int)(exprA->Eval()); + for(a = 0; a < n; a++) { + for(i = 0; i < entity->n; i++) { + Entity *e = &(entity->elem[i]); + if(e->group.v != opA.v) continue; + + CopyEntity(e->h, a*2 - (subtype == ONE_SIDED ? 0 : (n-1)), + h.param(0), h.param(1), h.param(2), + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, + true); + } + } + break; + } case ROTATE: // The translation vector - AddParam(param, h.param(0), 100); - AddParam(param, h.param(1), 100); - AddParam(param, h.param(2), 100); + AddParam(param, h.param(0), gp.x); + AddParam(param, h.param(1), gp.y); + AddParam(param, h.param(2), gp.z); // The rotation quaternion AddParam(param, h.param(3), 1); AddParam(param, h.param(4), 0); @@ -427,8 +459,8 @@ void Group::MakePolygons(void) { SS.GetParam(h.param(2))->val ); Vector tbot, ttop; - if(subtype == EXTRUDE_ONE_SIDED) { - tbot = Vector::MakeFrom(0, 0, 0); ttop = translate; + if(subtype == ONE_SIDED) { + tbot = Vector::MakeFrom(0, 0, 0); ttop = translate.ScaledBy(2); } else { tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1); } diff --git a/sketch.h b/sketch.h index 507fdd1..cef3dc6 100644 --- a/sketch.h +++ b/sketch.h @@ -89,6 +89,7 @@ public: hGroup opB; bool visible; hEntity activeWorkplane; + Expr *exprA; static const int SOLVED_OKAY = 0; static const int DIDNT_CONVERGE = 10; @@ -100,8 +101,8 @@ public: static const int WORKPLANE_BY_POINT_ORTHO = 6000; static const int WORKPLANE_BY_LINE_SEGMENTS = 6001; - static const int EXTRUDE_ONE_SIDED = 7000; - static const int EXTRUDE_TWO_SIDED = 7001; + static const int ONE_SIDED = 7000; + static const int TWO_SIDED = 7001; int subtype; struct { diff --git a/solvespace.cpp b/solvespace.cpp index c68ca15..94623d5 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -232,6 +232,8 @@ void SolveSpace::GenerateAll(bool andSolve, int first, int last) { // the active group or active workplane could have been deleted. So // clear all that out. GW.ClearSuper(); + HideGraphicsEditControl(); + HideTextEditControl(); } return; diff --git a/solvespace.h b/solvespace.h index 0d721ee..8ba10fe 100644 --- a/solvespace.h +++ b/solvespace.h @@ -52,6 +52,9 @@ void EnableMenuById(int id, BOOL checked); void ShowGraphicsEditControl(int x, int y, char *s); void HideGraphicsEditControl(void); BOOL GraphicsEditControlIsVisible(void); +void ShowTextEditControl(int hr, int c, char *s); +void HideTextEditControl(void); +BOOL TextEditControlIsVisible(void); void ShowTextWindow(BOOL visible); void InvalidateText(void); diff --git a/textwin.cpp b/textwin.cpp index 0040991..a8b0f50 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -374,12 +374,12 @@ void TextWindow::ScreenSelectRequest(int link, DWORD v) { SS.GW.ClearSelection(); SS.GW.selection[0].entity = hr.entity(0); } -void TextWindow::ScreenChangeExtrudeSides(int link, DWORD v) { +void TextWindow::ScreenChangeOneOrTwoSides(int link, DWORD v) { Group *g = SS.GetGroup(SS.TW.shown->group); - if(g->subtype == Group::EXTRUDE_ONE_SIDED) { - g->subtype = Group::EXTRUDE_TWO_SIDED; - } else if(g->subtype == Group::EXTRUDE_TWO_SIDED) { - g->subtype = Group::EXTRUDE_ONE_SIDED; + if(g->subtype == Group::ONE_SIDED) { + g->subtype = Group::TWO_SIDED; + } else if(g->subtype == Group::TWO_SIDED) { + g->subtype = Group::ONE_SIDED; } else oops(); SS.GW.GeneratePerSolving(); SS.GW.ClearSuper(); @@ -394,9 +394,15 @@ void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) { SS.GW.GeneratePerSolving(); SS.GW.ClearSuper(); } +void TextWindow::ScreenChangeExprA(int link, DWORD v) { + Group *g = SS.GetGroup(SS.TW.shown->group); + ShowTextEditControl(13, 10, g->exprA->Print()); + SS.TW.edit.meaning = EDIT_TIMES_REPEATED; + SS.TW.edit.group.v = v; +} void TextWindow::ShowGroupInfo(void) { Group *g = SS.group.FindById(shown->group); - char *s; + char *s, *s2; if(SS.GW.activeGroup.v == shown->group.v) { s = "active "; } else if(shown->group.v == Group::HGROUP_REFERENCES.v) { @@ -407,13 +413,33 @@ void TextWindow::ShowGroupInfo(void) { Printf(true, "%Ft%sGROUP %E%s", s, g->DescriptionString()); if(g->type == Group::EXTRUDE) { - bool one = (g->subtype == Group::EXTRUDE_ONE_SIDED); - Printf(true, "%FtEXTRUDE%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", - &TextWindow::ScreenChangeExtrudeSides, - (one ? "" : "one side"), (one ? "one-side" : ""), - &TextWindow::ScreenChangeExtrudeSides, + s = "EXTRUDE"; + } else if(g->type == Group::TRANSLATE) { + s = "TRANSLATE"; + s2 ="REPEAT "; + } else if(g->type == Group::ROTATE) { + s = "ROTATE"; + s2 ="REPEAT "; + } + + if(g->type == Group::EXTRUDE || g->type == Group::ROTATE || + g->type == Group::TRANSLATE) + { + bool one = (g->subtype == Group::ONE_SIDED); + Printf(true, "%Ft%s%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", s, + &TextWindow::ScreenChangeOneOrTwoSides, + (one ? "" : "one side"), (one ? "one side" : ""), + &TextWindow::ScreenChangeOneOrTwoSides, (!one ? "" : "two sides"), (!one ? "two sides" : "")); + } + if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) { + int times = (int)(g->exprA->Eval()); + Printf(true, "%Ft%s%E %d time%s %Fl%Ll%D%f(change)%E", + s2, times, times == 1 ? "" : "s", + g->h, &TextWindow::ScreenChangeExprA); + } + if(g->type == Group::EXTRUDE) { bool diff = (g->meshCombine == Group::COMBINE_AS_DIFFERENCE); Printf(false, "%FtCOMBINE%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", &TextWindow::ScreenChangeMeshCombine, @@ -505,4 +531,23 @@ void TextWindow::ShowConstraintInfo(void) { Printf(false, "[[constraint]]"); } +void TextWindow::EditControlDone(char *s) { + HideTextEditControl(); + switch(edit.meaning) { + case EDIT_TIMES_REPEATED: { + Expr *e = Expr::FromString(s); + if(e) { + Group *g = SS.GetGroup(edit.group); + Expr::FreeKeep(&(g->exprA)); + g->exprA = e->DeepCopyKeep(); + SS.GW.GeneratePerSolving(); + SS.TW.Show(); + } else { + Error("Not a valid number or expression: '%s'", s); + } + break; + } + } + edit.meaning = EDIT_NOTHING; +} diff --git a/ui.h b/ui.h index 1293c16..d5319d5 100644 --- a/ui.h +++ b/ui.h @@ -55,6 +55,13 @@ public: int history; ShownState *shown; + static const int EDIT_NOTHING = 0; + static const int EDIT_TIMES_REPEATED = 1; + struct { + int meaning; + hGroup group; + } edit; + static void ReportHowGroupSolved(hGroup hg); void ShowHeader(void); @@ -68,6 +75,8 @@ public: void ShowGroupSolveInfo(void); void OneScreenForwardTo(int screen); + + // All of these are callbacks from the GUI code. static void ScreenSelectGroup(int link, DWORD v); static void ScreenActivateGroup(int link, DWORD v); static void ScreenToggleGroupShown(int link, DWORD v); @@ -79,10 +88,13 @@ public: static void ScreenSelectRequest(int link, DWORD v); static void ScreenSelectConstraint(int link, DWORD v); - static void ScreenChangeExtrudeSides(int link, DWORD v); + static void ScreenChangeOneOrTwoSides(int link, DWORD v); static void ScreenChangeMeshCombine(int link, DWORD v); + static void ScreenChangeExprA(int link, DWORD v); static void ScreenNavigation(int link, DWORD v); + + void EditControlDone(char *s); }; class GraphicsWindow { diff --git a/win32/w32main.cpp b/win32/w32main.cpp index 82675f9..c518a97 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -15,11 +15,18 @@ #define MIN_COLS 45 #define TEXT_HEIGHT 20 #define TEXT_WIDTH 9 +#define TEXT_LEFT_MARGIN 4 + +// For the edit controls +#define EDIT_WIDTH 220 +#define EDIT_HEIGHT 21 HINSTANCE Instance; HWND TextWnd; HWND TextWndScrollBar; +HWND TextEditControl; +int TextEditControlCol, TextEditControlHalfRow; int TextWndScrollPos; // The scrollbar position, in half-row units int TextWndHalfRows; // The height of our window, in half-row units @@ -182,7 +189,7 @@ static void PaintTextWnd(HDC hdc) SelectObject(backDc, FixedFont); } - int x = 4 + c*TEXT_WIDTH; + int x = TEXT_LEFT_MARGIN + c*TEXT_WIDTH; int y = (top-TextWndScrollPos)*(TEXT_HEIGHT/2); RECT a; @@ -227,6 +234,11 @@ void HandleTextWindowScrollBar(WPARAM wParam, LPARAM lParam) si.nPos = TextWndScrollPos; SetScrollInfo(TextWndScrollBar, SB_CTL, &si, TRUE); + if(TextEditControlIsVisible()) { + int x = TEXT_LEFT_MARGIN + TEXT_WIDTH*TextEditControlCol; + int y = (TextEditControlHalfRow - TextWndScrollPos)*(TEXT_HEIGHT/2); + MoveWindow(TextEditControl, x, y, EDIT_WIDTH, EDIT_HEIGHT, TRUE); + } InvalidateRect(TextWnd, NULL, FALSE); } } @@ -288,6 +300,10 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_LBUTTONDOWN: case WM_MOUSEMOVE: { + if(TextEditControlIsVisible() || GraphicsEditControlIsVisible()) { + SetCursor(LoadCursor(NULL, IDC_ARROW)); + break; + } GraphicsWindow::Selection ps = SS.GW.hover; SS.GW.hover.Clear(); @@ -340,6 +356,9 @@ done: GetClientRect(hwnd, &r); MoveWindow(TextWndScrollBar, r.right - sw, r.top, sw, (r.bottom - r.top), TRUE); + // If the window is growing, then the scrollbar position may + // be moving, so it's as if we're dragging the scrollbar. + HandleTextWindowScrollBar(0, 0); InvalidateRect(TextWnd, NULL, FALSE); break; } @@ -368,6 +387,16 @@ static BOOL ProcessKeyDown(WPARAM wParam) return FALSE; } } + if(TextEditControlIsVisible() && wParam != VK_ESCAPE) { + if(wParam == VK_RETURN) { + char s[1024]; + memset(s, 0, sizeof(s)); + SendMessage(TextEditControl, WM_GETTEXT, 900, (LPARAM)s); + SS.TW.EditControlDone(s); + } else { + return FALSE; + } + } if(wParam == VK_BACK && !GraphicsEditControlIsVisible()) { TextWindow::ScreenNavigation('b', 0); @@ -463,8 +492,35 @@ void InvalidateText(void) InvalidateRect(TextWnd, NULL, FALSE); } +static void ShowEditControl(HWND h, int x, int y, char *s) { + MoveWindow(h, x, y, EDIT_WIDTH, EDIT_HEIGHT, TRUE); + ShowWindow(h, SW_SHOW); + SendMessage(h, WM_SETTEXT, 0, (LPARAM)s); + SendMessage(h, EM_SETSEL, 0, strlen(s)); + SetFocus(h); +} +void ShowTextEditControl(int hr, int c, char *s) +{ + if(TextEditControlIsVisible() || GraphicsEditControlIsVisible()) return; + + int x = TEXT_LEFT_MARGIN + TEXT_WIDTH*c; + int y = (hr - TextWndScrollPos)*(TEXT_HEIGHT/2); + TextEditControlCol = c; + TextEditControlHalfRow = hr; + ShowEditControl(TextEditControl, x, y, s); +} +void HideTextEditControl(void) +{ + ShowWindow(TextEditControl, SW_HIDE); +} +BOOL TextEditControlIsVisible(void) +{ + return IsWindowVisible(TextEditControl); +} void ShowGraphicsEditControl(int x, int y, char *s) { + if(TextEditControlIsVisible() || GraphicsEditControlIsVisible()) return; + RECT r; GetClientRect(GraphicsWnd, &r); x = x + (r.right - r.left)/2; @@ -474,11 +530,7 @@ void ShowGraphicsEditControl(int x, int y, char *s) // top left corner y -= 20; - MoveWindow(GraphicsEditControl, x, y, 220, 21, TRUE); - ShowWindow(GraphicsEditControl, SW_SHOW); - SendMessage(GraphicsEditControl, WM_SETTEXT, 0, (LPARAM)s); - SendMessage(GraphicsEditControl, EM_SETSEL, 0, strlen(s)); - SetFocus(GraphicsEditControl); + ShowEditControl(GraphicsEditControl, x, y, s); } void HideGraphicsEditControl(void) { @@ -745,6 +797,11 @@ static void CreateMainWindows(void) // Force the scrollbar to get resized to the window, TextWndProc(TextWnd, WM_SIZE, 0, 0); + TextEditControl = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "", + WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS, + 50, 50, 100, 21, TextWnd, NULL, Instance, NULL); + SendMessage(TextEditControl, WM_SETFONT, (WPARAM)FixedFont, TRUE); + RECT r, rc; GetWindowRect(TextWnd, &r);