From fd5a80446b2abfdfb9d8951fcc03fb1b55ad707c Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Tue, 4 Feb 2014 12:39:23 -0600 Subject: [PATCH] Misc. fixes & additions Fixed handedness of Vectors, added connections for Lines, added the ability to move Dimensions (still needs user feedback though). Also, added intersection checks to Geometry class. --- src/applicationwindow.cpp | 56 ++++ src/applicationwindow.h | 2 + src/circle.h | 2 + src/dimension.cpp | 175 ++++++------- src/dimension.h | 3 + src/drawingview.cpp | 12 +- src/geometry.cpp | 171 +++++++++++- src/geometry.h | 6 + src/line.cpp | 63 +++-- src/line.h | 3 + src/object.h | 3 + src/vector.cpp | 529 +++++++++++++++++++------------------- src/vector.h | 118 ++++----- 13 files changed, 691 insertions(+), 452 deletions(-) diff --git a/src/applicationwindow.cpp b/src/applicationwindow.cpp index cb4f020..23629bc 100644 --- a/src/applicationwindow.cpp +++ b/src/applicationwindow.cpp @@ -37,6 +37,7 @@ #include "drawlineaction.h" #include "fileio.h" #include "generaltab.h" +#include "geometry.h" #include "layerwidget.h" #include "mirroraction.h" #include "painter.h" @@ -500,6 +501,59 @@ else } +void ApplicationWindow::HandleConnection(void) +{ +//double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2)); +//printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt); + int itemsSelected = drawing->document.ItemsSelected(); + + // If nothing selected, do nothing + if (itemsSelected == 0) + { + statusBar()->showMessage(tr("No objects selected to connect.")); + return; + } + + // If one thing selected, do nothing + if (itemsSelected == 1) + { + statusBar()->showMessage(tr("Nothing to connect object to.")); + return; + } + + // This is O(n^2 / 2) :-P + for(int i=0; idocument.SelectedItem(i); + + for(int j=i+1; jdocument.SelectedItem(j); + double t, u; + + if ((obj1->type != OTLine) || (obj2->type != OTLine)) + continue; + +//printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2); + int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u); +//printf(" (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u); + + if (intersects) + { +printf("Connecting objects (%X, %X)...\n", obj1, obj2); + obj1->Connect(obj2, u); + obj2->Connect(obj1, t); + } + } + } +} + + +void ApplicationWindow::HandleDisconnection(void) +{ +} + + void ApplicationWindow::HandleGridSizeInPixels(int size) { drawing->SetGridSize(size); @@ -612,8 +666,10 @@ void ApplicationWindow::CreateActions(void) connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping())); connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c")); + connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection())); disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d")); + connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection())); mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true); connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool())); diff --git a/src/applicationwindow.h b/src/applicationwindow.h index 9432d81..cf25247 100644 --- a/src/applicationwindow.h +++ b/src/applicationwindow.h @@ -43,6 +43,8 @@ class ApplicationWindow: public QMainWindow void HelpAbout(void); void Settings(void); void HandleGrouping(void); + void HandleConnection(void); + void HandleDisconnection(void); void HandleGridSizeInPixels(int); void HandleGridSizeInBaseUnits(QString); void HandleDimensionSize(QString); diff --git a/src/circle.h b/src/circle.h index 527ea0f..3cf6a44 100644 --- a/src/circle.h +++ b/src/circle.h @@ -5,6 +5,8 @@ class Circle: public Object { + friend class Geometry; + public: Circle(Vector, double, Object * p = 0); ~Circle(); diff --git a/src/dimension.cpp b/src/dimension.cpp index db2a208..63e1ba2 100644 --- a/src/dimension.cpp +++ b/src/dimension.cpp @@ -15,6 +15,7 @@ #include "dimension.h" #include +#include "geometry.h" #include "mathconstants.h" #include "painter.h" @@ -43,6 +44,21 @@ Dimension::~Dimension() } +/* +The approach used below creates a hierarchy: Dimension is subservient to Line. + +Does this solve our problem of connected objects? Maybe, partially. Let's think this +through. It only works for endpoints, not points in the middle... + +Also: this is bad, depending on the Draw() function to update the internal + position(s) of the data of the object! (is it though?) + +How to move: click once moves only the object/point clicked on, all connected +objects deform themselves accordingly. click twice selects ALL connected objects; +all objects move as a unified whole. + +*/ + /*virtual*/ void Dimension::Draw(Painter * painter) { // If there are valid Vector pointers in here, use them to update the internal @@ -93,8 +109,7 @@ I believe they are pixels. // Calculate whether or not the arrowheads are too crowded to put inside // the extension lines. 9.0 is the length of the arrowhead. // double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size)); - double t = Vector::Parameter(position, endpoint, position + (unit * 9.0 * size)); -// double t = Vector::Parameter(endpoint, position, position + (unit * 9.0 * size)); + double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size)); //printf("Dimension::Draw(): t = %lf\n", t); // On the screen, it's acting like this is actually 58%... @@ -154,101 +169,37 @@ I believe they are pixels. } -/*virtual*/ bool Dimension::Collided(Vector /*point*/) +/*virtual*/ bool Dimension::Collided(Vector point) { -#if 0 + // Someone told us to fuck off, so we'll fuck off. :-) + if (ignoreClicks) + return false; + + // We can assume this, since this is a mouse down event here. objectWasDragged = false; - Vector lineSegment = endpoint - position; - Vector v1 = point - position; - Vector v2 = point - endpoint; - double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance; - - // Geometric interpretation: - // pp is the paremeterized point on the vector ls where the perpendicular intersects ls. - // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls, - // then the perpendicular lies beyond the 2nd endpoint. - - if (parameterizedPoint < 0.0) - distance = v1.Magnitude(); - else if (parameterizedPoint > lineSegment.Magnitude()) - distance = v2.Magnitude(); - else // distance = ?Det?(ls, v1) / |ls| - distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude()); - - // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular - // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and - // {e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is - // computed by first computing the area of the triangle the three points form, then dividing by the - // length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the - // triangle formed by three points is the determinant of the following matrix: - // - // sx sy 1 - // ex ey 1 - // px py 1 - // - // By translating the start point to the origin, this can be rewritten as: - // By subtracting row 1 from all rows, you get the following: - // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted - // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!] - // - // 0 0 0 0 0 0 - // (ex - sx) (ey - sy) 0 ==> ex ey 0 - // (px - sx) (py - sy) 0 px py 0 - // - // which greatly simplifies the calculation of the determinant. - - if (state == OSInactive) + HitTest(point); + + // Now that we've done our hit testing on the non-snapped point, snap it if + // necessary... + if (snapToGrid) + point = SnapPointToGrid(point); + + if (hitPoint1) { -//printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance); -//printf(" v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude()); -//printf(" point = %lf,%lf,%lf; p1 = %lf,%lf,%lf; p2 = %lf,%lf,%lf\n", point.x, point.y, point.z, position.x, position.y, position.z, endpoint.x, endpoint.y, endpoint.z); -//printf(" \n", ); -//How to translate this into pixels from Document space??? -//Maybe we need to pass a scaling factor in here from the caller? That would make sense, as -//the caller knows about the zoom factor and all that good kinda crap - if (v1.Magnitude() < 10.0) - { - oldState = state; - state = OSSelected; - oldPoint = position; //maybe "position"? - draggingHandle1 = true; - return true; - } - else if (v2.Magnitude() < 10.0) - { - oldState = state; - state = OSSelected; - oldPoint = endpoint; //maybe "position"? - draggingHandle2 = true; - return true; - } - else if (distance < 2.0) - { - oldState = state; - state = OSSelected; - oldPoint = point; - dragging = true; - return true; - } +// oldState = state; +// state = OSSelected; + oldPoint = position; + draggingHandle1 = true; + return true; } - else if (state == OSSelected) + else if (hitPoint2) { - // Here we test for collision with handles as well! (SOON!) -/* -Like so: - if (v1.Magnitude() < 2.0) // Handle #1 - else if (v2.Magnitude() < 2.0) // Handle #2 -*/ - if (distance < 2.0) - { - oldState = state; -// state = OSInactive; - oldPoint = point; - dragging = true; - return true; - } +// oldState = state; +// state = OSSelected; + oldPoint = endpoint; + draggingHandle2 = true; + return true; } -#endif state = OSInactive; return false; @@ -262,7 +213,7 @@ Like so: // dragged... objectWasDragged = true; - if (dragging) +/* if (dragging) { // Here we need to check whether or not we're dragging a handle or the object itself... Vector delta = point - oldPoint; @@ -273,7 +224,7 @@ Like so: oldPoint = point; needUpdate = true; } - else if (draggingHandle1) + else*/ if (draggingHandle1) { Vector delta = point - oldPoint; @@ -298,7 +249,7 @@ Like so: /*virtual*/ void Dimension::PointerReleased(void) { - if (draggingHandle1 || draggingHandle2) +/* if (draggingHandle1 || draggingHandle2) { // Set the length (in case the global state was set to fixed (or not)) if (Object::fixedLength) @@ -306,23 +257,22 @@ Like so: if (draggingHandle1) // startpoint { - Vector v = Vector(position - endpoint).Unit() * length; + Vector v = Vector(endpoint, position).Unit() * length; position = endpoint + v; } else // endpoint { -// Vector v1 = endpoint - position; - Vector v = Vector(endpoint - position).Unit() * length; + Vector v = Vector(position, endpoint).Unit() * length; endpoint = position + v; } } - else + else*/ { // Otherwise, we calculate the new length, just in case on the next move // it turns out to have a fixed length. :-) length = Vector(endpoint - position).Magnitude(); } - } +/* }*/ dragging = false; draggingHandle1 = false; @@ -339,6 +289,33 @@ about keeping track of old states... } +/*virtual*/ bool Dimension::HitTest(Point point) +{ + hitPoint1 = hitPoint2 = false; +// Vector lineSegment(position, endpoint); + Vector v1(position, point); + Vector v2(endpoint, point); +// double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point); +// double distance; + +// if (t < 0.0) +// distance = v1.Magnitude(); +// else if (t > 1.0) +// distance = v2.Magnitude(); +// else + // distance = ?Det?(ls, v1) / |ls| +// distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) +// / lineSegment.Magnitude()); + + if ((v1.Magnitude() * Painter::zoom) < 8.0) + hitPoint1 = true; + else if ((v2.Magnitude() * Painter::zoom) < 8.0) + hitPoint2 = true; + + return (hitPoint1 || hitPoint2 ? true : false); +} + + /*virtual*/ void Dimension::Enumerate(FILE * file) { fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type); diff --git a/src/dimension.h b/src/dimension.h index d4c4c31..7ffca2f 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -18,6 +18,7 @@ class Dimension: public Object virtual bool Collided(Vector); virtual void PointerMoved(Vector); virtual void PointerReleased(void); + virtual bool HitTest(Point); virtual void Enumerate(FILE *); virtual Object * Copy(void); virtual Vector GetPointAtParameter(double parameter); @@ -38,6 +39,8 @@ class Dimension: public Object bool objectWasDragged; double length; DimensionType dimensionType; + bool hitPoint1; + bool hitPoint2; public: double size; // Size of arrows/text in base units diff --git a/src/drawingview.cpp b/src/drawingview.cpp index 9341a76..07d7d9f 100644 --- a/src/drawingview.cpp +++ b/src/drawingview.cpp @@ -398,7 +398,8 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event) point = Vector(event->x(), event->y()); // Since we're using Qt coords for scrolling, we have to adjust them here to // conform to Cartesian coords, since the origin is using Cartesian. :-) - Vector delta(point, oldPoint); +// Vector delta(point, oldPoint); + Vector delta(oldPoint, point); delta /= Painter::zoom; delta.y = -delta.y; Painter::origin -= delta; @@ -470,10 +471,11 @@ void DrawingView::mouseReleaseEvent(QMouseEvent * event) void DrawingView::wheelEvent(QWheelEvent * event) { double zoomFactor = 1.25; - QSize sizeWin = /*drawing->*/size(); + QSize sizeWin = size(); Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0); center = Painter::QtToCartesianCoords(center); + // This is not centering for some reason. Need to figure out why. :-/ if (event->delta() > 0) { Vector newOrigin = center - ((center - Painter::origin) / zoomFactor); @@ -487,10 +489,10 @@ void DrawingView::wheelEvent(QWheelEvent * event) Painter::zoom /= zoomFactor; } -// Object::gridSpacing = /*drawing->*/gridPixels / Painter::zoom; +// Object::gridSpacing = gridPixels / Painter::zoom; +// UpdateGridBackground(); SetGridSize(Object::gridSpacing * Painter::zoom); -// /*drawing->*/UpdateGridBackground(); - /*drawing->*/update(); + update(); // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Object::gridSpacing)); } diff --git a/src/geometry.cpp b/src/geometry.cpp index 5f9a30b..eacaec7 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -15,6 +15,9 @@ #include "geometry.h" #include +#include "line.h" +#include "circle.h" + Point Geometry::IntersectionOfLineAndLine(Point p1, Point p2, Point p3, Point p4) { @@ -41,7 +44,8 @@ Point Geometry::IntersectionOfLineAndLine(Point p1, Point p2, Point p3, Point p4 // Returns the parameter of a point in space to this vector. If the parameter // is between 0 and 1, the normal of the vector to the point is on the vector. -double Geometry::ParameterOfLineAndPoint(Point lp1, Point lp2, Point point) +// Note: lp1 is the tail, lp2 is the head of the line (vector). +double Geometry::ParameterOfLineAndPoint(Point tail, Point head, Point point) { // Geometric interpretation: // The parameterized point on the vector lineSegment is where the normal of @@ -49,9 +53,9 @@ double Geometry::ParameterOfLineAndPoint(Point lp1, Point lp2, Point point) // the perpendicular lies beyond the 1st endpoint. If pp > 1, then the // perpendicular lies beyond the 2nd endpoint. - Vector lineSegment = lp1 - lp2; + Vector lineSegment = head - tail; double magnitude = lineSegment.Magnitude(); - Vector pointSegment = point - lp2; + Vector pointSegment = point - tail; double t = lineSegment.Dot(pointSegment) / (magnitude * magnitude); return t; } @@ -86,3 +90,164 @@ Point Geometry::RotatePointAroundPoint(Point point, Point rotationPoint, double return Vector(rotationPoint.x + px, rotationPoint.y + py, 0); } + +double Geometry::Determinant(Point p1, Point p2) +{ + return (p1.x * p2.y) - (p2.x * p1.y); +} + + +/* +Intersecting line segments: +An easier way: +Segment L1 has edges A=(a1,a2), A'=(a1',a2'). +Segment L2 has edges B=(b1,b2), B'=(b1',b2'). +Segment L1 is the set of points tA'+(1-t)A, where 0<=t<=1. +Segment L2 is the set of points sB'+(1-s)B, where 0<=s<=1. +Segment L1 meet segment L2 if and only if for some t and s we have +tA'+(1-t)A=sB'+(1-s)B +The solution of this with respect to t and s is + +t=((-b?'a?+b?'b?+b?a?+a?b?'-a?b?-b?b?')/(b?'a?'-b?'a?-b?a?'+b?a?-a?'b?'+a?'b?+a?b?'-a?b?)) + +s=((-a?b?+a?'b?-a?a?'+b?a?+a?'a?-b?a?')/(b?'a?'-b?'a?-b?a?'+b?a?-a?'b??+a?'b?+a?b?'-a?b?)) + +So check if the above two numbers are both >=0 and <=1. +*/ + + +#if 0 +// Finds the intesection between two objects (if any) +bool Geometry::Intersects(Object * obj1, Object * obj2, double * t, double * s) +{ +} +#endif + +// Finds the intersection between two lines (if any) +int Geometry::Intersects(Line * l1, Line * l2, double * tp/*= 0*/, double * up/*= 0*/) +{ + Vector r(l1->position, l1->endpoint); + Vector s(l2->position, l2->endpoint); + Vector v1 = l2->position - l1->position; +// Vector v1 = l1->position - l2->position; + + double rxs = (r.x * s.y) - (s.x * r.y); + + if (rxs == 0) + return 0; + + double t = ((v1.x * s.y) - (s.x * v1.y)) / rxs; + double u = ((v1.x * r.y) - (r.x * v1.y)) / rxs; +/* +Now there are five cases: + +1. If r × s = 0 and (q − p) × r = 0, then the two lines are collinear. If in addition, either 0 ≤ (q − p) · r ≤ r · r or 0 ≤ (p − q) · s ≤ s · s, then the two lines are overlapping. + +2. If r × s = 0 and (q − p) × r = 0, but neither 0 ≤ (q − p) · r ≤ r · r nor 0 ≤ (p − q) · s ≤ s · s, then the two lines are collinear but disjoint. + +3. If r × s = 0 and (q − p) × r ≠ 0, then the two lines are parallel and non-intersecting. + +4. If r × s ≠ 0 and 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1, the two line segments meet at the point p + t r = q + u s. + +5. Otherwise, the two line segments are not parallel but do not intersect. +*/ + // Return parameter values, if user passed in valid pointers + if (tp) + *tp = t; + + if (up) + *up = u; + + // If the parameters are in range, we have overlap! + if ((t >= 0) && (t <= 1.0) && (u >= 0) && (u <= 1.0)) + return 1; + + return 0; +} + + +// Finds the intesection(s) between a line and a circle (if any) +int Geometry::Intersects(Line * l, Circle * c, double * tp/*= 0*/, double * up/*= 0*/, double * vp/*= 0*/, double * wp/*= 0*/) +{ +#if 0 + Vector center = c->position; + Vector v1 = l->position - center; + Vector v2 = l->endpoint - center; + Vector d = v2 - v1; + double dr = d.Magnitude(); + double determinant = (v1.x * v2.y) - (v1.y * v2.x); + + double discriminant = ((c->radius * c->radius) * (dr * dr)) - (determinant * determinant); + + if (discriminant < 0) + return false; + + + + return true; +#else +/* +I'm thinking a better approach to this might be as follows: + +-- Get the distance of the circle's center from the line segment. If it's + > the radius, it doesn't intersect. +-- If the parameter is off the line segment, check distance to endpoints. (Not sure + how to proceed from here, it's different than the following.) + [Actually, you can use the following for all of it. You only know if you have + an intersection at the last step, which is OK.] +-- If the radius == distance, we have a tangent line. +-- If radius > distance, use Pythagorus to find the length on either side of the + normal to the spots where the hypotenuse (== radius' length) contact the line. +-- Use those points to find the parameter on the line segment; if they're not on + the line segment, no intersection. +*/ + double t = ParameterOfLineAndPoint(l->position, l->endpoint, c->position); +//small problem here: it clamps the result to the line segment. NOT what we want +//here! !!! FIX !!! [DONE] + Vector p = l->GetPointAtParameter(t); + double distance = Vector::Magnitude(c->position, p); + + // If the center of the circle is farther from the line than the radius, fail. + if (distance > c->radius) + return 0; + + // Now we have to check for intersection points. + // Tangent case: (needs to return something) + if ((distance == c->radius) && (t >= 0.0) && (t <= 1.0)) + return 1; + + // The line intersects the circle in two points (possibly). Use Pythagorus + // to find them for testing. + double offset = sqrt((c->radius * c->radius) - (distance * distance)); +//need to convert distance to paramter value... :-/ +//t = position on line / length of line segment, so if we divide the offset by length, +//that should give us what we want. + double length = Vector::Magnitude(l->position, l->endpoint); + double t1 = t + (offset / length); + double t2 = t - (offset / length); + +//need to find angles for the circle... + Vector cp1 = l->position + (Vector(l->position, l->endpoint) * (length * t1)); + Vector cp2 = l->position + (Vector(l->position, l->endpoint) * (length * t2)); + double a1 = Vector(c->position, cp1).Angle(); + double a2 = Vector(c->position, cp2).Angle(); + +//instead of this, return a # which is the # of intersections. [DONE] + int intersections = 0; + + // Now check for if the parameters are in range + if ((t1 >= 0) && (t1 <= 1.0)) + { + intersections++; + } + + if ((t2 >= 0) && (t2 <= 1.0)) + { + intersections++; + } + + return intersections; +#endif +} + + diff --git a/src/geometry.h b/src/geometry.h index 6ff4564..8936088 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -3,6 +3,9 @@ #include "vector.h" +class Line; +class Circle; + class Geometry { public: @@ -11,6 +14,9 @@ class Geometry static double ParameterOfLineAndPoint(Point, Point, Point); static Point MirrorPointAroundLine(Point, Point, Point); static Point RotatePointAroundPoint(Point, Point, double); + static double Determinant(Point, Point); + static int Intersects(Line *, Line *, double * tp = 0, double * up = 0); + static int Intersects(Line * l, Circle * c, double * tp = 0, double * up = 0, double * vp = 0, double * wp = 0); }; #endif // __GEOMETRY_H__ diff --git a/src/line.cpp b/src/line.cpp index 90b5dc2..0608685 100644 --- a/src/line.cpp +++ b/src/line.cpp @@ -172,6 +172,13 @@ key to make it draw/show on the other side... TODO: Make Dimension preview with modifier keys for showing on other side */ +/* + +N.B.: This no longer works, as the DrawDimension object takes precedence over this code. + THIS DOES NOTHING ANYMORE!!! + +*/ +#if 0 // Is the dimension tool active? Let's use it: if (dimensionActive) { @@ -223,6 +230,7 @@ a dimension only) Draw() function... :-/ return true; } } +#endif if (state == OSInactive) { @@ -335,6 +343,18 @@ a dimension only) Draw() function... :-/ needUpdate = true; //doesn't work QMainWindow::statusBar()->setText("You are manipulating a line"); + + // Tell connected objects to move themselves... + if (draggingLine) + { + std::vector::iterator i; + + for(i=connected.begin(); i!=connected.end(); i++) + { + if ((*i).object->type == OTLine) + ((Line *)((*i).object))->MovePointAtParameter((*i).t, delta); + } + } } /* @@ -449,23 +469,13 @@ the horizontal line or vertical line that intersects from the current mouse posi /*virtual*/ bool Line::HitTest(Point point) { -// SaveHitState(); - hitPoint1 = hitPoint2 = hitLine = false; Vector lineSegment = endpoint - position; Vector v1 = point - position; Vector v2 = point - endpoint; -// double t = Vector::Parameter(position, endpoint, point); double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point); double distance; - // Geometric interpretation: - // The parameter "t" on the vector lineSegment is where the normal of - // lineSegment coincides with point. If t < 0, the normal lies beyond the - // 1st endpoint. If t > 1, then the normal lies beyond the 2nd endpoint. We - // only calculate the length of the normal between the point and the - // lineSegment when the parameter is between 0 and 1. - // Geometric interpretation of "distance = ?Det?(ls, v1) / |ls|": // If the segment endpoints are s and e, and the point is p, then the test // for the perpendicular intercepting the segment is equivalent to insisting @@ -501,7 +511,6 @@ the horizontal line or vertical line that intersects from the current mouse posi hitLine = true; return (hitPoint1 || hitPoint2 || hitLine ? true : false); -// return HitStateChanged(); } @@ -542,23 +551,29 @@ same reference number. /*virtual*/ Vector Line::GetPointAtParameter(double parameter) { +// Is there any real reason to clamp this to the endpoints? +// (hey, whaddya know? this was masking a bug!) +#if 0 if (parameter <= 0) return position; else if (parameter >= 1.0) return endpoint; +#endif - // Our parameter lies between zero and one, so calculate it! - Vector v(endpoint, position); - double length = v.Magnitude(); - // We scale the magnitude of v so that it lies between 0 and 1... - // By multiplying the parameter by the magnitude, we obtain the point we - // want. No scaling necessary as it's inherent in the approach! - double spotOnLength = length * parameter; - - // To get our point, we use the initial point of the line and add in our - // scaled point. - Vector result = position + (v * spotOnLength); - return result; + // The parameter is a percentage of the length of the vector, so all we + // have to do is scale the vector by it to find the point. + return position + (Vector(position, endpoint) * parameter); +} + + +/*virtual*/ void Line::MovePointAtParameter(double parameter, Vector v) +{ + if (parameter == 0) + position += v; + else if (parameter == 1.0) + endpoint += v; + else + {} // Not sure how to handle this case :-P } @@ -613,7 +628,7 @@ same reference number. } -void Line::SetDimensionOnLine(Dimension * dimension/*=NULL*/) +void Line::SetDimensionOnLine(Dimension * dimension/*= NULL*/) { // If they don't pass one in, create it for the caller. if (dimension == NULL) diff --git a/src/line.h b/src/line.h index 4e5d7b5..0ad264c 100644 --- a/src/line.h +++ b/src/line.h @@ -7,6 +7,8 @@ class Dimension; class Line: public Object { + friend class Geometry; + public: Line(Vector, Vector, Object * p = 0); ~Line(); @@ -21,6 +23,7 @@ class Line: public Object virtual void Enumerate(FILE *); virtual Object * Copy(void); virtual Vector GetPointAtParameter(double parameter); + virtual void MovePointAtParameter(double parameter, Vector); virtual QRectF Extents(void); virtual void Translate(Vector); virtual void Rotate(Point, double); diff --git a/src/object.h b/src/object.h index ec775d1..4f50821 100644 --- a/src/object.h +++ b/src/object.h @@ -17,6 +17,8 @@ enum ObjectType { OTNone, OTObject, OTLine, OTCircle, OTArc, OTDimension, OTElli class Object { + friend class Geometry; + public: Object(); Object(Vector, Object * passedInParent = 0); @@ -36,6 +38,7 @@ class Object virtual void Enumerate(FILE *); virtual Object * Copy(void); virtual Vector GetPointAtParameter(double parameter); +//Not yet, soon though virtual void MovePointAtParameter(double parameter, Vector); virtual void Connect(Object *, double); virtual void Disconnect(Object *, double); virtual void DisconnectAll(Object *); diff --git a/src/vector.cpp b/src/vector.cpp index ac5a205..11ddb64 100644 --- a/src/vector.cpp +++ b/src/vector.cpp @@ -1,262 +1,267 @@ -// -// vector.cpp: Various structures used for 3 dimensional imaging -// -// by James Hammons -// (C) 2006 Underground Software -// -// JLH = James L. Hammons -// -// WHO WHEN WHAT -// --- ---------- ------------------------------------------------------------ -// JLH 09/19/2006 Created this file -// JLH 03/22/2011 Moved implementation of constructor from header to here -// JLH 04/02/2011 Fixed divide-by-zero bug in Unit(), added Angle() function -// JLH 08/04/2013 Added Parameter() function -// - -#include "vector.h" - -#include // For sqrt() -#include "mathconstants.h" - -// Vector implementation - -Vector::Vector(double xx/*= 0*/, double yy/*= 0*/, double zz/*= 0*/): x(xx), y(yy), z(zz) -{ -} - - -Vector::Vector(Vector head, Vector tail): x(head.x - tail.x), y(head.y - tail.y), z(head.z - tail.z) -{ -} - - -Vector Vector::operator=(Vector const v) -{ - x = v.x, y = v.y, z = v.z; - - return *this; -} - - -Vector Vector::operator+(Vector const v) -{ - return Vector(x + v.x, y + v.y, z + v.z); -} - - -Vector Vector::operator-(Vector const v) -{ - return Vector(x - v.x, y - v.y, z - v.z); -} - - -// Unary negation - -Vector Vector::operator-(void) -{ - return Vector(-x, -y, -z); -} - - -// Vector x constant - -Vector Vector::operator*(double const v) -{ - return Vector(x * v, y * v, z * v); -} - - -// Vector x constant - -Vector Vector::operator*(float const v) -{ - return Vector(x * v, y * v, z * v); -} - - -// Vector / constant - -Vector Vector::operator/(double const v) -{ - return Vector(x / v, y / v, z / v); -} - - -// Vector / constant - -Vector Vector::operator/(float const v) -{ - return Vector(x / v, y / v, z / v); -} - - -// Vector (cross) product - -Vector Vector::operator*(Vector const v) -{ - // a x b = [a2b3 - a3b2, a3b1 - a1b3, a1b2 - a2b1] - return Vector((y * v.z) - (z * v.y), (z * v.x) - (x * v.z), (x * v.y) - (y * v.x)); -} - - -// Dot product - -double Vector::Dot(Vector const v) -{ - return (x * v.x) + (y * v.y) + (z * v.z); -} - - -// Vector x constant, self assigned - -Vector& Vector::operator*=(double const v) -{ - x *= v, y *= v, z *= v; - - return *this; -} - - -// Vector / constant, self assigned - -Vector& Vector::operator/=(double const v) -{ - x /= v, y /= v, z /= v; - - return *this; -} - -// Vector + vector, self assigned - -Vector& Vector::operator+=(Vector const v) -{ - x += v.x, y += v.y, z += v.z; - - return *this; -} - - -// Vector + constant, self assigned - -Vector& Vector::operator+=(double const v) -{ - x += v, y += v, z += v; - - return *this; -} - - -// Vector - vector, self assigned - -Vector& Vector::operator-=(Vector const v) -{ - x -= v.x, y -= v.y, z -= v.z; - - return *this; -} - - -// Vector - constant, self assigned - -Vector& Vector::operator-=(double const v) -{ - x -= v, y -= v, z -= v; - - return *this; -} - - -// Check for equality -bool Vector::operator==(Vector const v) -{ - return (x == v.x && y == v.y && z == v.z ? true : false); -} - - -// Check for inequality -bool Vector::operator!=(Vector const v) -{ - return (x != v.x || y != v.y || z != v.z ? true : false); -} - - -Vector Vector::Unit(void) -{ - double mag = Magnitude(); - - // If the magnitude of the vector is zero, then the Unit vector is undefined... - if (mag == 0) - return Vector(0, 0, 0); - - return Vector(x / mag, y / mag, z / mag); -} - - -double Vector::Magnitude(void) -{ - return sqrt(x * x + y * y + z * z); -} - - -double Vector::Angle(void) -{ - // acos returns a value between zero and PI, which means we don't know which - // quadrant the angle is in... Though, if the y-coordinate of the vector is - // negative, that means that the angle is in quadrants III - IV. - double rawAngle = acos(Unit().x); - double correctedAngle = (y < 0 ? (2.0 * PI) - rawAngle : rawAngle); - - return correctedAngle; -} - - -bool Vector::isZero(double epsilon/*= 1e-6*/) -{ - return (fabs(x) < epsilon && fabs(y) < epsilon && fabs(z) < epsilon ? true : false); -} - - -// Class methods - -double Vector::Dot(Vector v1, Vector v2) -{ - return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); -} - - -double Vector::Magnitude(Vector v1, Vector v2) -{ - double xx = v1.x - v2.x; - double yy = v1.y - v2.y; - double zz = v1.z - v2.z; - return sqrt(xx * xx + yy * yy + zz * zz); -} - - -// Returns the parameter of a point in space to this vector. If the parameter -// is between 0 and 1, the normal of the vector to the point is on the vector. -double Vector::Parameter(Vector v1, Vector v2, Vector p) -{ - // Geometric interpretation: - // The parameterized point on the vector lineSegment is where the normal of - // the lineSegment to the point intersects lineSegment. If the pp < 0, then - // the perpendicular lies beyond the 1st endpoint. If pp > 1, then the - // perpendicular lies beyond the 2nd endpoint. - - Vector lineSegment = v1 - v2; - double magnitude = lineSegment.Magnitude(); - Vector pointSegment = p - v2; - double t = lineSegment.Dot(pointSegment) / (magnitude * magnitude); - return t; -} - - -// Return the normal to the linesegment formed by the passed in points. -// (Not sure which is head or tail, or which hand the normal lies) -/*static*/ Vector Vector::Normal(Vector v1, Vector v2) -{ - Vector v = (v1 - v2).Unit(); - return Vector(-v.y, v.x); -} - +// +// vector.cpp: Various structures used for 3 dimensional imaging +// +// by James Hammons +// (C) 2006 Underground Software +// +// JLH = James L. Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// JLH 09/19/2006 Created this file +// JLH 03/22/2011 Moved implementation of constructor from header to here +// JLH 04/02/2011 Fixed divide-by-zero bug in Unit(), added Angle() function +// JLH 08/04/2013 Added Parameter() function +// + +#include "vector.h" + +#include // For sqrt() +#include "mathconstants.h" + +// Vector implementation + +Vector::Vector(double xx/*= 0*/, double yy/*= 0*/, double zz/*= 0*/): x(xx), y(yy), z(zz) +{ +} + + +Vector::Vector(Vector tail, Vector head): x(head.x - tail.x), y(head.y - tail.y), z(head.z - tail.z) +{ +} + + +Vector Vector::operator=(Vector const v) +{ + x = v.x, y = v.y, z = v.z; + + return *this; +} + + +Vector Vector::operator+(Vector const v) +{ + return Vector(x + v.x, y + v.y, z + v.z); +} + + +Vector Vector::operator-(Vector const v) +{ + return Vector(x - v.x, y - v.y, z - v.z); +} + + +// Unary negation + +Vector Vector::operator-(void) +{ + return Vector(-x, -y, -z); +} + + +// Vector x constant + +Vector Vector::operator*(double const v) +{ + return Vector(x * v, y * v, z * v); +} + + +// Vector x constant + +Vector Vector::operator*(float const v) +{ + return Vector(x * v, y * v, z * v); +} + + +// Vector / constant + +Vector Vector::operator/(double const v) +{ + return Vector(x / v, y / v, z / v); +} + + +// Vector / constant + +Vector Vector::operator/(float const v) +{ + return Vector(x / v, y / v, z / v); +} + + +// Vector (cross) product + +Vector Vector::operator*(Vector const v) +{ + // a x b = [a2b3 - a3b2, a3b1 - a1b3, a1b2 - a2b1] + return Vector((y * v.z) - (z * v.y), (z * v.x) - (x * v.z), (x * v.y) - (y * v.x)); +} + + +// Dot product + +double Vector::Dot(Vector const v) +{ + return (x * v.x) + (y * v.y) + (z * v.z); +} + + +// Vector x constant, self assigned + +Vector& Vector::operator*=(double const v) +{ + x *= v, y *= v, z *= v; + + return *this; +} + + +// Vector / constant, self assigned + +Vector& Vector::operator/=(double const v) +{ + x /= v, y /= v, z /= v; + + return *this; +} + +// Vector + vector, self assigned + +Vector& Vector::operator+=(Vector const v) +{ + x += v.x, y += v.y, z += v.z; + + return *this; +} + + +// Vector + constant, self assigned + +Vector& Vector::operator+=(double const v) +{ + x += v, y += v, z += v; + + return *this; +} + + +// Vector - vector, self assigned + +Vector& Vector::operator-=(Vector const v) +{ + x -= v.x, y -= v.y, z -= v.z; + + return *this; +} + + +// Vector - constant, self assigned + +Vector& Vector::operator-=(double const v) +{ + x -= v, y -= v, z -= v; + + return *this; +} + + +// Check for equality +bool Vector::operator==(Vector const v) +{ + return (x == v.x && y == v.y && z == v.z ? true : false); +} + + +// Check for inequality +bool Vector::operator!=(Vector const v) +{ + return (x != v.x || y != v.y || z != v.z ? true : false); +} + + +Vector Vector::Unit(void) +{ + double mag = Magnitude(); + + // If the magnitude of the vector is zero, then the Unit vector is undefined... + if (mag == 0) + return Vector(0, 0, 0); + + return Vector(x / mag, y / mag, z / mag); +} + + +double Vector::Magnitude(void) +{ + return sqrt(x * x + y * y + z * z); +} + + +double Vector::Angle(void) +{ + // acos returns a value between zero and PI, which means we don't know which + // quadrant the angle is in... Though, if the y-coordinate of the vector is + // negative, that means that the angle is in quadrants III - IV. + double rawAngle = acos(Unit().x); + double correctedAngle = (y < 0 ? (2.0 * PI) - rawAngle : rawAngle); + + return correctedAngle; +} + + +bool Vector::isZero(double epsilon/*= 1e-6*/) +{ + return (fabs(x) < epsilon && fabs(y) < epsilon && fabs(z) < epsilon ? true : false); +} + + +// Class methods + +double Vector::Dot(Vector v1, Vector v2) +{ + return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); +} + + +double Vector::Magnitude(Vector v1, Vector v2) +{ + double xx = v1.x - v2.x; + double yy = v1.y - v2.y; + double zz = v1.z - v2.z; + return sqrt((xx * xx) + (yy * yy) + (zz * zz)); +} + + +// Returns the parameter of a point in space to this vector. If the parameter +// is between 0 and 1, the normal of the vector to the point is on the vector. +// Note: v1 is the tail, v2 is the head of the line (vector). +double Vector::Parameter(Vector tail, Vector head, Vector p) +{ + // Geometric interpretation: + // The parameterized point on the vector lineSegment is where the normal of + // the lineSegment to the point intersects lineSegment. If the pp < 0, then + // the perpendicular lies beyond the 1st endpoint. If pp > 1, then the + // perpendicular lies beyond the 2nd endpoint. + + Vector lineSegment = head - tail; + double magnitude = lineSegment.Magnitude(); + Vector pointSegment = p - tail; + double t = lineSegment.Dot(pointSegment) / (magnitude * magnitude); + return t; +} + + +// Return the normal to the linesegment formed by the passed in points. +// (Not sure which is head or tail, or which hand the normal lies) +// [v1 should be the tail, v2 should be the head, in which case the normal should +// rotate anti-clockwise.] +///*static*/ Vector Vector::Normal(Vector v1, Vector v2) +/*static*/ Vector Vector::Normal(Vector tail, Vector head) +{ +// Vector v = (v1 - v2).Unit(); + Vector v = (head - tail).Unit(); + return Vector(-v.y, v.x); +} + diff --git a/src/vector.h b/src/vector.h index 6c0ad20..c7f2771 100644 --- a/src/vector.h +++ b/src/vector.h @@ -1,59 +1,59 @@ -// -// vector.h (Last modified: 6/28/2001) -// -// Various structures used for 3 dimensional imaging -// -// by James L. Hammons -// (C) 2001 Underground Software -// - -#ifndef __VECTOR_H__ -#define __VECTOR_H__ - -// What we'll do here is create the vector type and use typedef to alias Point to it. Yeah, that's it. - -class Vector -{ - public: - Vector(double xx = 0, double yy = 0, double zz = 0); - Vector(Vector head, Vector tail); // Create vector from two points - Vector operator=(Vector const v); - Vector operator+(Vector const v); - Vector operator-(Vector const v); - Vector operator-(void); // Unary negation - Vector operator*(double const v); // Vector times constant (double) - Vector operator*(float const v); // Vector times constant (float) - Vector operator/(double const v); // Vector divided by constant (double) - Vector operator/(float const v); // Vector divided by constant (float) - Vector operator*(Vector const v); // Vector product - double Dot(Vector const v); // Dot product - - Vector& operator*=(double const v); // Vector times constant self-assignment - Vector& operator/=(double const v); // Vector divided by constant self-assignment - Vector& operator+=(Vector const v); // Vector plus Vector self-assignment - Vector& operator+=(double const v); // Vector plus constant self-assignment - Vector& operator-=(Vector const v); // Vector minus Vector self-assignment - Vector& operator-=(double const v); // Vector minus constant self-assignment - - bool operator==(Vector const v); // Check for equality - bool operator!=(Vector const v); // Check for inequality - - Vector Unit(void); - double Magnitude(void); - double Angle(void); - bool isZero(double epsilon = 1e-6); - - // Class methods - - static double Dot(Vector v1, Vector v2); - static double Magnitude(Vector v1, Vector v2); - static double Parameter(Vector v1, Vector v2, Vector p); - static Vector Normal(Vector v1, Vector v2); - - public: - double x, y, z; -}; - -typedef Vector Point; - -#endif // __VECTOR_H__ +// +// vector.h (Last modified: 6/28/2001) +// +// Various structures used for 3 dimensional imaging +// +// by James L. Hammons +// (C) 2001 Underground Software +// + +#ifndef __VECTOR_H__ +#define __VECTOR_H__ + +// What we'll do here is create the vector type and use typedef to alias Point to it. Yeah, that's it. + +class Vector +{ + public: + Vector(double xx = 0, double yy = 0, double zz = 0); + Vector(Vector tail, Vector head); // Create vector from two points + Vector operator=(Vector const v); + Vector operator+(Vector const v); + Vector operator-(Vector const v); + Vector operator-(void); // Unary negation + Vector operator*(double const v); // Vector times constant (double) + Vector operator*(float const v); // Vector times constant (float) + Vector operator/(double const v); // Vector divided by constant (double) + Vector operator/(float const v); // Vector divided by constant (float) + Vector operator*(Vector const v); // Vector product + double Dot(Vector const v); // Dot product + + Vector& operator*=(double const v); // Vector times constant self-assignment + Vector& operator/=(double const v); // Vector divided by constant self-assignment + Vector& operator+=(Vector const v); // Vector plus Vector self-assignment + Vector& operator+=(double const v); // Vector plus constant self-assignment + Vector& operator-=(Vector const v); // Vector minus Vector self-assignment + Vector& operator-=(double const v); // Vector minus constant self-assignment + + bool operator==(Vector const v); // Check for equality + bool operator!=(Vector const v); // Check for inequality + + Vector Unit(void); + double Magnitude(void); + double Angle(void); + bool isZero(double epsilon = 1e-6); + + // Class methods + + static double Dot(Vector v1, Vector v2); + static double Magnitude(Vector v1, Vector v2); + static double Parameter(Vector v1, Vector v2, Vector p); + static Vector Normal(Vector v1, Vector v2); + + public: + double x, y, z; +}; + +typedef Vector Point; + +#endif // __VECTOR_H__ -- 2.37.2