From 5bc3738ec404bc12079e5429e1055d453bd5b890 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Fri, 25 Apr 2008 04:07:17 -0800 Subject: [PATCH] Add cubics, and tweak mouse handling code. [git-p4: depot-paths = "//depot/solvespace/": change = 1689] --- entity.cpp | 20 ++++++++ graphicswin.cpp | 127 ++++++++++++++++++++++++++++++------------------ sketch.cpp | 3 ++ sketch.h | 2 + ui.h | 10 ++-- 5 files changed, 110 insertions(+), 52 deletions(-) diff --git a/entity.cpp b/entity.cpp index 4df3d3e..5333985 100644 --- a/entity.cpp +++ b/entity.cpp @@ -321,6 +321,26 @@ void Entity::DrawOrGetDistance(int order) { break; } + case CUBIC: { + Vector p0 = SS.GetEntity(assoc[0])->PointGetCoords(); + Vector p1 = SS.GetEntity(assoc[1])->PointGetCoords(); + Vector p2 = SS.GetEntity(assoc[2])->PointGetCoords(); + Vector p3 = SS.GetEntity(assoc[3])->PointGetCoords(); + int i, n = 20; + Vector prev = p0; + for(i = 1; i <= n; i++) { + double t = ((double)i)/n; + Vector p = + (p0.ScaledBy((1 - t)*(1 - t)*(1 - t))).Plus( + (p1.ScaledBy(3*t*(1 - t)*(1 - t))).Plus( + (p2.ScaledBy(3*t*t*(1 - t))).Plus( + (p3.ScaledBy(t*t*t))))); + LineDrawOrGetDistanceOrEdge(prev, p); + prev = p; + } + break; + } + default: oops(); } diff --git a/graphicswin.cpp b/graphicswin.cpp index e7d6a1e..2c1d01a 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -58,7 +58,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 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, "&Cubic Segment\t3", MNU_CUBIC, '3', mReq }, { 1, NULL, 0, NULL }, { 1, "Sym&bolic Variable\tB", 0, 'B', mReq }, { 1, "&Import From File...\tI", 0, 'I', mReq }, @@ -320,6 +320,7 @@ void GraphicsWindow::MenuRequest(int id) { case MNU_DATUM_POINT: s = "click to place datum point"; goto c; case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c; + case MNU_CUBIC: s = "click first point of cubic segment"; goto c; c: SS.GW.pendingOperation = id; SS.GW.pendingDescription = s; @@ -388,53 +389,55 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, orig.mouse.y = y; InvalidateGraphics(); - } else if(leftDown) { - // We are left-dragging. This is often used to drag points, or - // constraint labels. - 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 = DRAGGING_POINT; - } - } else if(hover.constraint.v && - SS.GetConstraint(hover.constraint)->HasLabel()) - { - if(dm > dmt) { - ClearSelection(); - pendingConstraint = hover.constraint; - pendingOperation = DRAGGING_CONSTRAINT; - } - } - } else if(pendingOperation == DRAGGING_POINT || - pendingOperation == DRAGGING_NEW_POINT || - pendingOperation == DRAGGING_NEW_LINE_POINT) + return; + } + + // Enforce a bit of static friction before we start dragging. + double dm = orig.mouse.DistanceTo(mp); + if(leftDown && dm > 3 && pendingOperation == 0) { + if(hover.entity.v && + SS.GetEntity(hover.entity)->IsPoint() && + !SS.GetEntity(hover.entity)->PointIsFromReferences()) { - UpdateDraggedEntity(pendingPoint, x, y); - } else if(pendingOperation == DRAGGING_CONSTRAINT) { - Constraint *c = SS.constraint.FindById(pendingConstraint); - UpdateDraggedPoint(&(c->disp.offset), x, y); - } - } else { - // No buttons pressed. - if(pendingOperation == DRAGGING_NEW_POINT || - pendingOperation == DRAGGING_NEW_LINE_POINT) + // Start dragging this point. + ClearSelection(); + pendingPoint = hover.entity; + pendingOperation = DRAGGING_POINT; + } else if(hover.constraint.v && + SS.GetConstraint(hover.constraint)->HasLabel()) { - UpdateDraggedEntity(pendingPoint, x, y); - HitTestMakeSelection(mp); - } else { - // Do our usual hit testing, for the selection. - HitTestMakeSelection(mp); + ClearSelection(); + pendingConstraint = hover.constraint; + pendingOperation = DRAGGING_CONSTRAINT; } + } else if(leftDown && pendingOperation == DRAGGING_CONSTRAINT) { + Constraint *c = SS.constraint.FindById(pendingConstraint); + UpdateDraggedPoint(&(c->disp.offset), x, y); + } else if(leftDown && pendingOperation == DRAGGING_POINT) { + UpdateDraggedEntity(pendingPoint, x, y); + HitTestMakeSelection(mp); + } + + // No buttons pressed. + if(pendingOperation == DRAGGING_NEW_POINT || + pendingOperation == DRAGGING_NEW_LINE_POINT) + { + UpdateDraggedEntity(pendingPoint, x, y); + HitTestMakeSelection(mp); + } else if(pendingOperation == DRAGGING_NEW_CUBIC_POINT) { + UpdateDraggedEntity(pendingPoint, x, y); + HitTestMakeSelection(mp); + + hRequest hr = pendingPoint.request(); + Vector p0 = SS.GetEntity(hr.entity(1))->PointGetCoords(); + Vector p3 = SS.GetEntity(hr.entity(4))->PointGetCoords(); + Vector p1 = p0.ScaledBy(2.0/3).Plus(p3.ScaledBy(1.0/3)); + SS.GetEntity(hr.entity(2))->PointForceTo(p1); + Vector p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3)); + SS.GetEntity(hr.entity(3))->PointForceTo(p2); + } else if(!leftDown) { + // Do our usual hit testing, for the selection. + HitTestMakeSelection(mp); } } @@ -556,6 +559,10 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { v = v.Plus(projRight.ScaledBy(mx/scale)); v = v.Plus(projUp.ScaledBy(my/scale)); +#define MAYBE_PLACE(p) \ + if(hover.entity.v && SS.GetEntity((p))->IsPoint()) { \ + Constraint::ConstrainCoincident(hover.entity, (p)); \ + } hRequest hr; switch(pendingOperation) { case MNU_DATUM_POINT: @@ -570,9 +577,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { case MNU_LINE_SEGMENT: hr = AddRequest(Request::LINE_SEGMENT); SS.GetEntity(hr.entity(1))->PointForceTo(v); - if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { - Constraint::ConstrainCoincident(hover.entity, hr.entity(1)); - } + MAYBE_PLACE(hr.entity(1)); ClearSelection(); hover.Clear(); @@ -582,12 +587,35 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SS.GetEntity(pendingPoint)->PointForceTo(v); break; + case MNU_CUBIC: + hr = AddRequest(Request::CUBIC); + SS.GetEntity(hr.entity(1))->PointForceTo(v); + SS.GetEntity(hr.entity(2))->PointForceTo(v); + SS.GetEntity(hr.entity(3))->PointForceTo(v); + SS.GetEntity(hr.entity(4))->PointForceTo(v); + MAYBE_PLACE(hr.entity(1)); + + ClearSelection(); hover.Clear(); + + pendingOperation = DRAGGING_NEW_CUBIC_POINT; + pendingPoint = hr.entity(4); + pendingDescription = "click to place next point of cubic"; + break; + case DRAGGING_NEW_POINT: // The MouseMoved event has already dragged it under the cursor. pendingOperation = 0; pendingPoint.v = 0; break; + case DRAGGING_NEW_CUBIC_POINT: + if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { + Constraint::ConstrainCoincident(pendingPoint, hover.entity); + } + pendingOperation = 0; + pendingPoint.v = 0; + break; + case DRAGGING_NEW_LINE_POINT: { if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { Constraint::ConstrainCoincident(pendingPoint, hover.entity); @@ -614,6 +642,9 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { case 0: default: { pendingOperation = 0; + pendingPoint.v = 0; + pendingConstraint.v = 0; + pendingDescription = NULL; if(hover.IsEmpty()) break; diff --git a/sketch.cpp b/sketch.cpp index cc1335c..3516c70 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -80,6 +80,9 @@ void Request::Generate(IdList *entity, case Request::LINE_SEGMENT: et = Entity::LINE_SEGMENT; points = 2; params = 0; goto c; + + case Request::CUBIC: + et = Entity::CUBIC; points = 4; params = 0; goto c; c: { // Generate the entity that's specific to this request. e.type = et; diff --git a/sketch.h b/sketch.h index a474e3c..0e7d9d6 100644 --- a/sketch.h +++ b/sketch.h @@ -106,6 +106,7 @@ public: static const int CSYS_2D = 100; static const int DATUM_POINT = 101; static const int LINE_SEGMENT = 200; + static const int CUBIC = 300; int type; @@ -138,6 +139,7 @@ public: static const int POINT_IN_3D = 2000; static const int POINT_IN_2D = 2001; static const int LINE_SEGMENT = 3000; + static const int CUBIC = 4000; int type; bool symbolic; diff --git a/ui.h b/ui.h index bc74738..75b6f3f 100644 --- a/ui.h +++ b/ui.h @@ -98,6 +98,7 @@ public: MNU_DATUM_POINT, MNU_LINE_SEGMENT, MNU_RECTANGLE, + MNU_CUBIC, // Constrain MNU_DISTANCE_DIA, MNU_EQUAL, @@ -151,10 +152,11 @@ public: // Operations that must be completed by doing something with the mouse // are noted here. - static const int DRAGGING_POINT = 0x0f000000; - static const int DRAGGING_NEW_POINT = 0x0f000001; - static const int DRAGGING_NEW_LINE_POINT = 0x0f000002; - static const int DRAGGING_CONSTRAINT = 0x0f000003; + static const int DRAGGING_POINT = 0x0f000000; + static const int DRAGGING_NEW_POINT = 0x0f000001; + static const int DRAGGING_NEW_LINE_POINT = 0x0f000002; + static const int DRAGGING_NEW_CUBIC_POINT = 0x0f000003; + static const int DRAGGING_CONSTRAINT = 0x0f000004; hEntity pendingPoint; hConstraint pendingConstraint; int pendingOperation;