]> Shamusworld >> Repos - architektonas/blobdiff - src/dimension.cpp
Fixed missing edge cases in line to line intersection function.
[architektonas] / src / dimension.cpp
index 6f561be7da4896efd7b5226277bde827bd3c91f8..27823b39135fb8f7f7ed87f4d482476672a42b19 100644 (file)
 #include "dimension.h"
 
 #include <QtGui>
+#include "geometry.h"
 #include "mathconstants.h"
 #include "painter.h"
 
 
-Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/ ,Object * p/*= NULL*/):
+Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/Object * p/*= NULL*/):
        Object(p1, p), endpoint(p2),
        dragging(false), draggingHandle1(false), draggingHandle2(false),
        length(p2.Magnitude()), dimensionType(dt), size(0.25), point1(NULL), point2(NULL)
@@ -30,7 +31,7 @@ Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/ ,Objec
 
 
 // This is bad, p1 & p2 could be NULL, causing much consternation...
-Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
+Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/, Object * p/*= NULL*/):
        dragging(false), draggingHandle1(false), draggingHandle2(false),
        length(0), dimensionType(dt), size(0.25), point1(p1), point2(p2)
 {
@@ -43,8 +44,24 @@ 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 0
        // If there are valid Vector pointers in here, use them to update the internal
        // positions. Otherwise, we just use the internal positions by default.
        if (point1.object)
@@ -52,11 +69,21 @@ Dimension::~Dimension()
 
        if (point2.object)
                endpoint = point2.object->GetPointAtParameter(point2.t);
+#endif
+
+       painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
+
+       if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
+               painter->DrawHandle(position);
 
+       if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
+               painter->DrawHandle(endpoint);
+#if 1
        if (state == OSSelected)
-               painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
+               painter->SetPen(QPen(Qt::cyan, 1.0 * Painter::zoom * size, Qt::SolidLine));
        else
 //             painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
+#endif
                painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine));
 
        painter->SetBrush(QBrush(QColor(Qt::blue)));
@@ -93,8 +120,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%...
@@ -108,6 +134,7 @@ I believe they are pixels.
        }
        else
        {
+               // Draw outside arrowheads
                Point p7 = p1 - (unit * 9.0 * size);
                Point p8 = p2 + (unit * 9.0 * size);
                painter->DrawArrowhead(p1, p7, size);
@@ -120,7 +147,27 @@ I believe they are pixels.
        painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
        Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
        Point ctr = p2 + v1;
+
+#if 0
        QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
+#else
+       QString dimText;
+       double length = Vector(endpoint - position).Magnitude();
+
+       if (length < 12.0)
+               dimText = QString("%1\"").arg(length);
+       else
+       {
+               double feet = (double)((int)length / 12);
+               double inches = length - (feet * 12.0);
+
+               if (inches == 0)
+                       dimText = QString("%1'").arg(feet);
+               else
+                       dimText = QString("%1' %2\"").arg(feet).arg(inches);
+       }
+#endif
+
        painter->DrawAngledText(ctr, angle, dimText, size);
 }
 
@@ -133,101 +180,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;
@@ -236,48 +219,45 @@ Like so:
 
 /*virtual*/ void Dimension::PointerMoved(Vector point)
 {
-       // We know this is true because mouse move messages don't come here unless
-       // the object was actually clicked on--therefore we *know* we're being
-       // dragged...
-       objectWasDragged = true;
-
-       if (dragging)
+       if (selectionInProgress)
        {
-               // Here we need to check whether or not we're dragging a handle or the object itself...
-               Vector delta = point - oldPoint;
-
-               position += delta;
-               endpoint += delta;
+               // Check for whether or not the rect contains this line
+               if (selection.contains(position.x, position.y)
+                       && selection.contains(endpoint.x, endpoint.y))
+                       state = OSSelected;
+               else
+                       state = OSInactive;
 
-               oldPoint = point;
-               needUpdate = true;
+               return;
        }
-       else if (draggingHandle1)
-       {
-               Vector delta = point - oldPoint;
 
-               position += delta;
+       // Hit test tells us what we hit (if anything) through boolean variables. (It
+       // also tells us whether or not the state changed. --not any more)
+       SaveHitState();
+       HitTest(point);
+       needUpdate = HitStateChanged();
 
-               oldPoint = point;
-               needUpdate = true;
-       }
-       else if (draggingHandle2)
+       objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
+
+       if (objectWasDragged)
        {
                Vector delta = point - oldPoint;
 
-               endpoint += delta;
+               if (draggingHandle1)// || draggingLine)
+                       position += delta;
+
+               if (draggingHandle2)// || draggingLine)
+                       endpoint += delta;
 
                oldPoint = point;
                needUpdate = true;
        }
-       else
-               needUpdate = false;
 }
 
 
 /*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)
@@ -285,23 +265,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;
@@ -318,9 +297,72 @@ 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);
+}
+
+
+void Dimension::SaveHitState(void)
+{
+       oldHitPoint1 = hitPoint1;
+       oldHitPoint2 = hitPoint2;
+//     oldHitLine = hitLine;
+}
+
+
+bool Dimension::HitStateChanged(void)
+{
+       if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2))
+               return true;
+
+       return false;
+}
+
+
 /*virtual*/ void Dimension::Enumerate(FILE * file)
 {
-       fprintf(file, "DIMENSION (%lf,%lf) (%lf,%lf) %i\n", position.x, position.y, endpoint.x, endpoint.y, type);
+       fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
+}
+
+
+/*virtual*/ Object * Dimension::Copy(void)
+{
+#warning "!!! This doesn't take care of attached Dimensions !!!"
+/*
+This is a real problem. While having a pointer in the Dimension to this line's points
+is fast & easy, it creates a huge problem when trying to replicate an object like this.
+
+Maybe a way to fix that then, is to have reference numbers instead of pointers. That
+way, if you copy them, ... you might still have problems. Because you can't be sure if
+a copy will be persistant or not, you then *definitely* do not want them to have the
+same reference number.
+*/
+
+       Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
+       d->size = size;
+       return d;
 }
 
 
@@ -335,6 +377,17 @@ about keeping track of old states...
 }
 
 
+/*virtual*/ void Dimension::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
+}
+
+
 /*virtual*/ void Dimension::Connect(Object * obj, double param)
 {
        // There are four possibilities here...