From fa71238def181f2920bab78e231d7264e9c9e644 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Tue, 22 Apr 2008 02:53:42 -0800 Subject: [PATCH] Some graphics tweaks, to the order in which stuff gets drawn, to determine what goes in front (e.g. put a drawn line in front of the reference plane, even if the z order would want the opposite), and some tweaks to the mouse behaviour, and a function to modify constraints like dimensions so that the are initially satisfied. [git-p4: depot-paths = "//depot/solvespace/": change = 1681] --- constraint.cpp | 21 ++++++++- drawconstraint.cpp | 45 ++++++++++++------- entity.cpp | 17 ++++--- graphicswin.cpp | 107 ++++++++++++++++++++++++++++++++++----------- sketch.h | 5 ++- ui.h | 13 ++++-- win32/w32main.cpp | 7 ++- 7 files changed, 158 insertions(+), 57 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index 57d86095..94f2bf20 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -29,7 +29,8 @@ void Constraint::MenuConstrain(int id) { return; } c.disp.offset = Vector::MakeFrom(50, 50, 50); - c.exprA = Expr::FromString("300")->DeepCopyKeep(); + c.exprA = Expr::FromString("0")->DeepCopyKeep(); + c.ModifyToSatisfy(); AddConstraint(&c); break; @@ -103,6 +104,24 @@ Expr *Constraint::Distance(hEntity hpa, hEntity hpb) { return (dx2->Plus(dy2->Plus(dz2)))->Sqrt(); } +void Constraint::ModifyToSatisfy(void) { + IdList l; + // An uninit IdList could lead us to free some random address, bad. + memset(&l, 0, sizeof(l)); + + Generate(&l); + if(l.n != 1) oops(); + + // These equations are written in the form f(...) - d = 0, where + // d is the value of the exprA. + double v = (l.elem[0].e)->Eval(); + double nd = exprA->Eval() + v; + Expr::FreeKeep(&exprA); + exprA = Expr::FromConstant(nd)->DeepCopyKeep(); + + l.Clear(); +} + void Constraint::AddEq(IdList *l, Expr *expr, int index) { Equation eq; eq.e = expr; diff --git a/drawconstraint.cpp b/drawconstraint.cpp index d0885f8b..bb6c0649 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -67,27 +67,40 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { } 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(); + if(!dogd.drawing) { + for(int i = 0; i < 2; i++) { + Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> + PointGetCoords(); + Point2d pp = SS.GW.ProjectPoint(p); + // The point is selected within a radius of 7, from the + // same center; so if the point is visible, then this + // constraint cannot be selected. But that's okay. + dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 3); + } + break; + } + + for(int a = 0; a < 2; a++) { + Vector r = SS.GW.projRight.ScaledBy((a+1)/SS.GW.scale); + Vector d = SS.GW.projUp.ScaledBy((2-a)/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; + double s = 5; 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(); diff --git a/entity.cpp b/entity.cpp index 4579c0d1..1a3aeb14 100644 --- a/entity.cpp +++ b/entity.cpp @@ -191,9 +191,9 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) { } } -void Entity::Draw(void) { +void Entity::Draw(int order) { dogd.drawing = true; - DrawOrGetDistance(); + DrawOrGetDistance(order); } double Entity::GetDistance(Point2d mp) { @@ -201,17 +201,18 @@ double Entity::GetDistance(Point2d mp) { dogd.mp = mp; dogd.dmin = 1e12; - DrawOrGetDistance(); + DrawOrGetDistance(-1); return dogd.dmin; } -void Entity::DrawOrGetDistance(void) { +void Entity::DrawOrGetDistance(int order) { glxColor(1, 1, 1); switch(type) { case POINT_IN_3D: case POINT_IN_2D: { + if(order >= 0 && order != 2) break; if(!SS.GW.showPoints) break; Entity *isfor = SS.GetEntity(h.request().entity(0)); @@ -220,25 +221,28 @@ void Entity::DrawOrGetDistance(void) { Vector v = PointGetCoords(); if(dogd.drawing) { - double s = 4; + double s = 3; Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); glxColor(0, 0.8, 0); + glDisable(GL_LINE_SMOOTH); glBegin(GL_QUADS); glxVertex3v(v.Plus (r).Plus (d)); glxVertex3v(v.Plus (r).Minus(d)); glxVertex3v(v.Minus(r).Minus(d)); glxVertex3v(v.Minus(r).Plus (d)); glEnd(); + glEnable(GL_LINE_SMOOTH); } else { Point2d pp = SS.GW.ProjectPoint(v); - dogd.dmin = pp.DistanceTo(dogd.mp) - 8; + dogd.dmin = pp.DistanceTo(dogd.mp) - 7; } break; } case CSYS_2D: { + if(order >= 0 && order != 0) break; if(!SS.GW.show2dCsyss) break; Vector p; @@ -274,6 +278,7 @@ void Entity::DrawOrGetDistance(void) { } case LINE_SEGMENT: { + if(order >= 0 && order != 1) break; Vector a = SS.GetEntity(assoc[0])->PointGetCoords(); Vector b = SS.GetEntity(assoc[1])->PointGetCoords(); LineDrawOrGetDistance(a, b); diff --git a/graphicswin.cpp b/graphicswin.cpp index fb12b7cf..ade8c034 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -37,6 +37,17 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "Dimensions in &Inches", 0, NULL }, { 1, "Dimensions in &Millimeters", 0, NULL }, +{ 0, "&Group", 0, 0, NULL }, +{ 1, "New &Drawing Group", 0, 0, NULL }, +{ 1, NULL, 0, NULL }, +{ 1, "New Step and Repeat &Translating", 0, 0, NULL }, +{ 1, "New Step and Repeat &Rotating", 0, 0, NULL }, +{ 1, NULL, 0, 0, NULL }, +{ 1, "New Extrusion", 0, 0, NULL }, +{ 1, NULL, 0, 0, NULL }, +{ 1, "New Boolean Difference", 0, 0, NULL }, +{ 1, "New Boolean Union", 0, 0, NULL }, + { 0, "&Request", 0, NULL }, { 1, "Dra&w in 2d Coordinate System\tW", MNU_SEL_CSYS, 'W', mReq }, { 1, "Draw Anywhere in 3d\tQ", MNU_NO_CSYS, 'Q', mReq }, @@ -47,15 +58,11 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "2d Coordinate S&ystem\tY", 0, 'Y', mReq }, { 1, NULL, 0, NULL }, { 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq }, +{ 1, "&Rectangle\tR", MNU_RECTANGLE, 'R', mReq }, { 1, "&Circle\tC", 0, 'C', mReq }, { 1, "&Arc of a Circle\tA", 0, 'A', mReq }, { 1, "&Cubic Segment\t3", 0, '3', mReq }, { 1, NULL, 0, NULL }, -{ 1, "Boolean &Union\tU", 0, 'U', mReq }, -{ 1, "Boolean &Difference\tD", 0, 'D', mReq }, -{ 1, "Step and Repeat &Translate\tT", 0, 'T', mReq }, -{ 1, "Step and Repeat &Rotate\tR", 0, 'R', mReq }, -{ 1, NULL, 0, NULL }, { 1, "Sym&bolic Variable\tB", 0, 'B', mReq }, { 1, "&Import From File...\tI", 0, 'I', mReq }, { 1, NULL, 0, NULL }, @@ -223,7 +230,9 @@ void GraphicsWindow::EnsureValidActives(void) { } if(change) SS.TW.Show(); - EnableMenuById(MNU_NO_CSYS, (activeCsys.v != Entity::NO_CSYS.v)); + bool in3d = (activeCsys.v == Entity::NO_CSYS.v); + CheckMenuById(MNU_NO_CSYS, in3d); + CheckMenuById(MNU_SEL_CSYS, !in3d); } void GraphicsWindow::MenuEdit(int id) { @@ -359,21 +368,41 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, } else if(leftDown) { // We are left-dragging. This is often used to drag points, or // constraint labels. - if(hover.entity.v && - SS.GetEntity(hover.entity)->IsPoint() && - !SS.GetEntity(hover.entity)->PointIsFromReferences()) + double dm = orig.mouse.DistanceTo(mp); + // Don't start a drag until we've moved some threshold distance from + // the mouse-down point, to avoid accidental drags. + double dmt = 3; + if(pendingOperation == 0) { + if(hover.entity.v && + SS.GetEntity(hover.entity)->IsPoint() && + !SS.GetEntity(hover.entity)->PointIsFromReferences()) + { + if(dm > dmt) { + // Start dragging this point. + ClearSelection(); + pendingPoint = hover.entity; + pendingOperation = PENDING_OPERATION_DRAGGING_POINT; + } + } else if(hover.constraint.v && + SS.GetConstraint(hover.constraint)->HasLabel()) + { + if(dm > dmt) { + ClearSelection(); + pendingConstraint = hover.constraint; + pendingOperation = PENDING_OPERATION_DRAGGING_CONSTRAINT; + } + } + } else if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT || + pendingOperation == PENDING_OPERATION_DRAGGING_NEW_POINT) { - ClearSelection(); - UpdateDraggedEntity(hover.entity, x, y); - } else if(hover.constraint.v && - SS.GetConstraint(hover.constraint)->HasLabel()) - { - ClearSelection(); - Constraint *c = SS.constraint.FindById(hover.constraint); + UpdateDraggedEntity(pendingPoint, x, y); + } else if(pendingOperation == PENDING_OPERATION_DRAGGING_CONSTRAINT) { + Constraint *c = SS.constraint.FindById(pendingConstraint); UpdateDraggedPoint(&(c->disp.offset), x, y); } } else { - if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT) { + // No buttons pressed. + if(pendingOperation == PENDING_OPERATION_DRAGGING_NEW_POINT) { UpdateDraggedEntity(pendingPoint, x, y); } else { // Do our usual hit testing, for the selection. @@ -401,7 +430,7 @@ void GraphicsWindow::Selection::Clear(void) { entity.v = constraint.v = 0; } void GraphicsWindow::Selection::Draw(void) { - if(entity.v) SS.GetEntity (entity )->Draw(); + if(entity.v) SS.GetEntity (entity )->Draw(-1); if(constraint.v) SS.GetConstraint(constraint)->Draw(); } @@ -499,21 +528,27 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { hRequest hr; switch(pendingOperation) { case MNU_DATUM_POINT: + ClearSelection(); hover.Clear(); + hr = AddRequest(Request::DATUM_POINT); SS.GetEntity(hr.entity(0))->PointForceTo(v); + pendingOperation = 0; break; case MNU_LINE_SEGMENT: + ClearSelection(); hover.Clear(); + hr = AddRequest(Request::LINE_SEGMENT); SS.GetEntity(hr.entity(1))->PointForceTo(v); - pendingOperation = PENDING_OPERATION_DRAGGING_POINT; + + pendingOperation = PENDING_OPERATION_DRAGGING_NEW_POINT; pendingPoint = hr.entity(2); pendingDescription = "click to place next point of line"; SS.GetEntity(pendingPoint)->PointForceTo(v); break; - case PENDING_OPERATION_DRAGGING_POINT: + case PENDING_OPERATION_DRAGGING_NEW_POINT: // The MouseMoved event has already dragged it under the cursor. pendingOperation = 0; break; @@ -549,6 +584,20 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { InvalidateGraphics(); } +void GraphicsWindow::MouseLeftUp(double mx, double my) { + switch(pendingOperation) { + case PENDING_OPERATION_DRAGGING_POINT: + case PENDING_OPERATION_DRAGGING_CONSTRAINT: + pendingOperation = 0; + pendingPoint.v = 0; + pendingConstraint.v = 0; + break; + + default: + break; // do nothing + } +} + void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { if(GraphicsEditControlIsVisible()) return; @@ -649,16 +698,22 @@ void GraphicsWindow::Paint(int w, int h) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - int i; + int i, a; - // First, draw the entire scene. + // First, draw the entire scene. We don't necessarily want to draw + // things with normal z-buffering behaviour; e.g. we always want to + // draw a line segment in front of a reference. So we have three draw + // levels, and only the first gets normal depth testing. glxUnlockColor(); - for(i = 0; i < SS.entity.n; i++) { - SS.entity.elem[i].Draw(); + for(a = 0; a <= 2; a++) { + // Three levels: 0 least prominent (e.g. a reference csys), 1 is + // middle (e.g. line segment), 2 is always in front (e.g. point). + if(a == 1) glDisable(GL_DEPTH_TEST); + for(i = 0; i < SS.entity.n; i++) { + SS.entity.elem[i].Draw(a); + } } - // 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(); diff --git a/sketch.h b/sketch.h index 981d64eb..f781b54d 100644 --- a/sketch.h +++ b/sketch.h @@ -164,8 +164,8 @@ public: double dmin; } dogd; void LineDrawOrGetDistance(Vector a, Vector b); - void DrawOrGetDistance(void); - void Draw(void); + void DrawOrGetDistance(int order); + void Draw(int order); double GetDistance(Point2d mp); char *DescriptionString(void); @@ -257,6 +257,7 @@ public: void Generate(IdList *l); // Some helpers when generating symbolic constraint equations + void ModifyToSatisfy(void); void AddEq(IdList *l, Expr *expr, int index); static Expr *Distance(hEntity pa, hEntity pb); }; diff --git a/ui.h b/ui.h index c3aaaead..3a08bb92 100644 --- a/ui.h +++ b/ui.h @@ -94,6 +94,7 @@ public: MNU_NO_CSYS, MNU_DATUM_POINT, MNU_LINE_SEGMENT, + MNU_RECTANGLE, // Constrain MNU_DISTANCE_DIA, MNU_EQUAL, @@ -139,10 +140,13 @@ public: // Operations that must be completed by doing something with the mouse // are noted here. - static const int PENDING_OPERATION_DRAGGING_POINT = 0x0f000000; - hEntity pendingPoint; - int pendingOperation; - char *pendingDescription; + static const int PENDING_OPERATION_DRAGGING_POINT = 0x0f000000; + static const int PENDING_OPERATION_DRAGGING_NEW_POINT = 0x0f000001; + static const int PENDING_OPERATION_DRAGGING_CONSTRAINT = 0x0f000002; + hEntity pendingPoint; + hConstraint pendingConstraint; + int pendingOperation; + char *pendingDescription; hRequest AddRequest(int type); // The constraint that is being edited with the on-screen textbox. @@ -194,6 +198,7 @@ public: void MouseMoved(double x, double y, bool leftDown, bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown); void MouseLeftDown(double x, double y); + void MouseLeftUp(double x, double y); void MouseLeftDoubleClick(double x, double y); void MouseMiddleDown(double x, double y); void MouseScroll(double x, double y, int delta); diff --git a/win32/w32main.cpp b/win32/w32main.cpp index 8b5b90cd..0a8c5625 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -13,8 +13,8 @@ #include "freeze.h" #define MIN_COLS 42 -#define TEXT_HEIGHT 19 -#define TEXT_WIDTH 10 +#define TEXT_HEIGHT 18 +#define TEXT_WIDTH 9 HINSTANCE Instance; @@ -446,6 +446,7 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_MOUSEMOVE: case WM_LBUTTONDOWN: + case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: case WM_MBUTTONDOWN: { int x = LOWORD(lParam); @@ -461,6 +462,8 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, if(msg == WM_LBUTTONDOWN) { SS.GW.MouseLeftDown(x, y); + } else if(msg == WM_LBUTTONUP) { + SS.GW.MouseLeftUp(x, y); } else if(msg == WM_LBUTTONDBLCLK) { SS.GW.MouseLeftDoubleClick(x, y); } else if(msg == WM_MBUTTONDOWN) {