]> Shamusworld >> Repos - architektonas/blobdiff - src/line.cpp
Misc. fixes & additions
[architektonas] / src / line.cpp
index b78a9dc6cd4f578a4e8d02cfe82adfe588797168..060868562a8299c3d335bb849f3657d4a0d7417c 100644 (file)
@@ -4,41 +4,69 @@
 // (C) 2011 Underground Software
 // See the README and GPLv3 files for licensing and warranty information
 //
-// JLH = James L. Hammons <jlhamm@acm.org>
+// JLH = James Hammons <jlhamm@acm.org>
 //
 // WHO  WHEN        WHAT
 // ---  ----------  ------------------------------------------------------------
 // JLH  03/22/2011  Created this file
+// JLH  04/11/2011  Fixed attached dimensions to stay at correct length when
+//                  "Fixed Length" button is down
+// JLH  04/27/2011  Fixed attached dimension to stay a correct length when
+//                  "Fixed Length" button is *not* down ;-)
+// JLH  05/29/2011  Added (some) mouseover hints
 //
 
 #include "line.h"
 
 #include <QtGui>
+#include "container.h"
+#include "dimension.h"
+#include "geometry.h"
+#include "mathconstants.h"
+#include "painter.h"
 
-Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint(p2),
-       dragging(false), draggingHandle1(false), draggingHandle2(false), //needUpdate(false),
-       length(p2.Magnitude())
+
+Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p),
+       /*type(OTLine),*/ endpoint(p2),
+       draggingLine(false), draggingHandle1(false), draggingHandle2(false), //needUpdate(false),
+       length(Vector::Magnitude(p2, p1)), angle(Vector(endpoint - position).Unit()),
+       hitPoint1(false), hitPoint2(false), hitLine(false)
 {
+       type = OTLine;
 }
 
+
 Line::~Line()
 {
+// Taking care of connections should be done by the Container, as we don't know
+// anything about any other object connected to this one.
+#if 0
+       // If there are any attached Dimensions, we must set the attachment points
+       // to NULL since they will no longer be valid.
+       if (attachedDimension)
+       {
+               attachedDimension->SetPoint1(NULL);
+               attachedDimension->SetPoint2(NULL);
+       }
+       // IT WOULD BE NICE to have any object points attached to this line automagically
+       // connect to this dimension object at this point, instead of just becoming
+       // detached.
+#endif
 }
 
-/*virtual*/ void Line::Draw(QPainter * painter)
+
+/*virtual*/ void Line::Draw(Painter * painter)
 {
-       if (state == OSSelected)
-               painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
-       else
-               painter->setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+       painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
+
+       if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
+               painter->DrawHandle(position);
 
-//     if (draggingHandle1)
-       if (state == OSSelected)
-               painter->drawEllipse(QPointF(position.x, position.y), 4.0, 4.0);
+       if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
+               painter->DrawHandle(endpoint);
 
-//     if (draggingHandle2)
-       if (state == OSSelected)
-               painter->drawEllipse(QPointF(endpoint.x, endpoint.y), 4.0, 4.0);
+       if ((state == OSInactive) && !hitLine)
+               painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
 
        if (Object::fixedLength && (draggingHandle1 || draggingHandle2))
        {
@@ -48,16 +76,42 @@ Line::~Line()
                Vector current(point2 - point1);
                Vector v = current.Unit() * length;
                Vector v2 = point1 + v;
-               painter->drawLine((int)point1.x, (int)point1.y, (int)v2.x, (int)v2.y);
+               painter->DrawLine(point1, v2);
 
                if (current.Magnitude() > length)
                {
-                       painter->setPen(QPen(QColor(128, 0, 0), 1.0, Qt::DashLine));
-                       painter->drawLine((int)v2.x, (int)v2.y, (int)point2.x, (int)point2.y);
+                       painter->SetPen(QPen(QColor(128, 0, 0), 1.0, Qt::DashLine));
+                       painter->DrawLine(v2, point2);
                }
        }
        else
-               painter->drawLine((int)position.x, (int)position.y, (int)endpoint.x, (int)endpoint.y);
+               painter->DrawLine(position, endpoint);
+
+       // If we're dragging an endpoint, draw an information panel showing both
+       // the length and angle being set.
+       if (draggingHandle1 || draggingHandle2)
+       {
+               double absAngle = (Vector(endpoint - position).Angle()) * RADIANS_TO_DEGREES;
+               double absLength = Vector(position - endpoint).Magnitude();
+
+               QString text = QObject::tr("Length: %1 in.\n") + QChar(0x2221) + QObject::tr(": %2");
+               text = text.arg(absLength).arg(absAngle);
+#if 0
+               QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
+               painter->SetPen(pen);
+               painter->SetBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
+               QRectF textRect(10.0, 10.0, 270.0, 70.0);       // x, y, w, h (in Qt coords)
+               painter->DrawRoundedRect(textRect, 7.0, 7.0);
+
+               textRect.setLeft(textRect.left() + 14);
+               painter->SetFont(*Object::font);
+               pen = QPen(QColor(0x00, 0x5F, 0xDF));
+               painter->SetPen(pen);
+               painter->DrawText(textRect, Qt::AlignVCenter, text);
+#else
+               painter->DrawInformativeText(text);
+#endif
+       }
 }
 
 /*virtual*/ Vector Line::Center(void)
@@ -69,142 +123,308 @@ Line::~Line()
 
 /*virtual*/ bool Line::Collided(Vector point)
 {
+/*
+what we can do here is set ignoreClicks to true to keep other objects that are
+selected from deselecting themselves. Will that fuck up something else? Not sure
+yet... :-/
+*/
+       // 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;
+       HitTest(point);
 
-       // 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.
+       // Now that we've done our hit testing on the non-snapped point, snap it if
+       // necessary...
+       if (snapToGrid)
+               point = SnapPointToGrid(point);
 
-       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.
+// this is shite. this should be checked for in the Container, not here!
+#warning "!!! This should be checked for in Container, not here !!!"
+       // If we're part of a non-top-level container, send this signal to it
+       if (parent->type == OTContainer && !((Container *)parent)->isTopLevelContainer
+               && (hitLine || hitPoint1 || hitPoint2))
+       {
+               parent->state = OSSelected;
+               return true;
+       }
+
+/*
+There's a small problem here with the implementation: You can have a dimension tied
+to only one point while at the same time you can have a dimension sitting on this line.
+Since there's only *one* dimPoint for each point, this can be problematic...
+
+We solve this by allowing only *one* Dimension object to be attached to the Line,
+Arc, etc. and by giving the Dimension object a pointer to our endpoints.
+
+Problem still arises when we delete this object; The attached Dimension object will
+then have bad pointers! What it *should* do is delete the object if and only if this
+line is not attached to any other object. If it is, then one of those attachment
+points should be sent to the dimension object (done for position & endpoint).
+
+NOTE: The STL vector<T> *does not* take ownership of pointers, therefore is suitable
+      for our purposes
+
+Also: It would be nice to have a preview of the dimension being drawn, with a modifier
+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)
+       {
+               // User clicked on the line itself (endpoint checks should preceed this one):
+               // (Priorities are taken care of in HitTest()...)
+               if (hitLine)
+               {
+#if 0
+                       if (attachedDimension == NULL)
+                       {
+                               // How to get this object into the top level container???
+/*
+The real question is do we care. I think so, because if this isn't in the top
+level container, it won't get drawn...
+But we can fix that by making this object call any attached object's (like
+a dimension only) Draw() function... :-/
+*/
+                               attachedDimension = new Dimension(&position, &endpoint, DTLinear, this);
+
+                               if (parent != NULL)
+                                       parent->Add(attachedDimension);
+                       }
+                       else
+                       {
+                               // If there's one already there, tell it to flip sides...
+                               attachedDimension->FlipSides();
+                       }
+#else
+                       // New approach here: We look for connected objects.
+                       Object * attachedDimension = FindAttachedDimension();
+
+                       if (attachedDimension)
+                       {
+                               // If there's an attached Dimension, tell it to switch sides...
+                               ((Dimension *)attachedDimension)->FlipSides();
+                       }
+                       else
+                       {
+                               // Otherwise, we make a new one and attach it here.
+                               attachedDimension = new Dimension(Connection(this, 0), Connection(this, 1.0), DTLinear, this);
+                               connected.push_back(Connection(attachedDimension, 0));
+                               connected.push_back(Connection(attachedDimension, 1.0));
+
+                               if (parent != NULL)
+                                       parent->Add(attachedDimension);
+                       }
+#endif
+
+                       return true;
+               }
+       }
+#endif
 
        if (state == OSInactive)
        {
-//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)
+//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
+//I think what's needed is an Object class variable/method that can be changed
+//by the TLC and called in derived classes to properly scale the location to
+//the current zoom level. That *should* work.
+
+// ALSO: Need to code a global (read: Object class) variable that tells use
+//       whether a modifier key was pressed in addition to the mouse click, so
+//       we can do stuff like, say, hold down CTRL and be able to do multiple
+//       selecting of objects (in that case, we would keep the Object state
+//       from changing).
+               if (hitPoint1)
                {
                        oldState = state;
                        state = OSSelected;
-                       oldPoint = position; //maybe "position"?
+                       oldPoint = position;
                        draggingHandle1 = true;
                        return true;
                }
-               else if (v2.Magnitude() < 10.0)
+               else if (hitPoint2)
                {
                        oldState = state;
                        state = OSSelected;
-                       oldPoint = endpoint; //maybe "position"?
+                       oldPoint = endpoint;
                        draggingHandle2 = true;
                        return true;
                }
-               else if (distance < 2.0)
+               else if (hitLine)
                {
                        oldState = state;
                        state = OSSelected;
                        oldPoint = point;
-                       dragging = true;
+                       draggingLine = true;
                        return true;
                }
        }
        else if (state == OSSelected)
        {
-               // 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)
+               if (hitLine)
                {
                        oldState = state;
 //                     state = OSInactive;
                        oldPoint = point;
-                       dragging = true;
+                       draggingLine = true;
+
+                       // Toggle selected state if CTRL held
+                       if (qApp->keyboardModifiers() == Qt::ControlModifier)
+                               state = OSInactive;
+
                        return true;
                }
        }
 
+       // If CTRL is held, then we bypass the "turn off" code. Still didn't hit
+       // *this* object though. :-)
+       if (qApp->keyboardModifiers() == Qt::ControlModifier)
+               return false;
+
+       // If we got here, we clicked on nothing, so set the object to inactive.
+       // (Once we can read key modifiers, we can override this to allow multiple selection.)
        state = OSInactive;
        return false;
 }
 
+
 /*virtual*/ void Line::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 0
+               if (selection.normalized().contains(Extents()))
+#else
+//             if (selection.normalized().contains(position.x, position.y)
+//                     && selection.normalized().contains(endpoint.x, endpoint.y))
+               if (selection.contains(position.x, position.y)
+                       && selection.contains(endpoint.x, endpoint.y))
+#endif
+                       state = OSSelected;
+               else
+                       state = OSInactive;
 
-               oldPoint = point;
-               needUpdate = true;
+               return;
        }
-       else if (draggingHandle1)
+
+       // 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();
+
+       objectWasDragged = (draggingLine | draggingHandle1 | draggingHandle2);
+
+       if (objectWasDragged)
        {
                Vector delta = point - oldPoint;
 
-               position += delta;
+               if (draggingHandle1 || draggingLine)
+                       position += delta;
+
+               if (draggingHandle2 || draggingLine)
+                       endpoint += delta;
 
                oldPoint = point;
                needUpdate = true;
+
+//doesn't work         QMainWindow::statusBar()->setText("You are manipulating a line");
+
+               // Tell connected objects to move themselves...
+               if (draggingLine)
+               {
+                       std::vector<Connection>::iterator i;
+
+                       for(i=connected.begin(); i!=connected.end(); i++)
+                       {
+                               if ((*i).object->type == OTLine)
+                                       ((Line *)((*i).object))->MovePointAtParameter((*i).t, delta);
+                       }
+               }
        }
-       else if (draggingHandle2)
+
+/*
+We can't count on any coupling between the dimension object and us, so how do we do this???
+Also, there may be more than one Dimension object connected to a single endpoint!
+
+Ugly ways to do it:
+ - Keep track of the state of the connected dimension
+ - Pass the Dimension the point that's being changed and the delta
+
+More elegant ways:
+ - Pass the point in a notification function (how?)
+ - Pass the point as a reference to the class instance object (&endpoint). This
+   way, the line doesn't have to care about keeping track of Dimensions
+   connected to it. But still have to care about other connected entities
+   (other Lines, Circles, Arcs, Splines, Texts, etc). I think I'd be OK with
+   this. Since the Dimension has a pointer to our object, all we have to do is
+   update our coordinates and the Dimension object will adjust itself on the
+   next repaint. Problem solved, and we don't have to know anything about how
+   many Dimensions are connected to us, or where! \o/
+   The question then becomes, how do we do this kind of coupling???
+
+We need to know about connected entities so that we can have them either move
+in expected ways or constrain the movement of this Line object. This is how we
+will be a cut above all other CAD software currently out there: the GUI will
+try to do the right thing, most of the time. :-)
+*/
+       if (needUpdate)
        {
-               Vector delta = point - oldPoint;
+// should only do this if "Fixed Length" is set... !!! FIX !!! [DONE]
+               Vector point1 = (draggingHandle1 ? endpoint : position);
+               Vector point2 = (draggingHandle1 ? position : endpoint);
 
-               endpoint += delta;
+               if (Object::fixedAngle)
+               {
+                       // Here we calculate the component of the current vector along the fixed angle.
+                       // A_compB = (A . Bu) * Bu
+                       double magnitudeAlongB = Vector::Dot(Vector(point2 - point1), angle);
+/*
+Actually, this isn't quite right. What we want to do is look for the intersection along either
+the horizontal line or vertical line that intersects from the current mouse position.
+*/
 
-               oldPoint = point;
-               needUpdate = true;
+                       if (draggingHandle1)
+                               position = endpoint + (angle * magnitudeAlongB);
+
+                       if (draggingHandle2)
+                               endpoint = position + (angle * magnitudeAlongB);
+               }
+//             else
+//                     v2 = point2;
+
+//If we tell the dimension to flip sides, this is no longer a valid
+//assumption. !!! FIX !!!
+//Ideally, we should just send the point that's changing to the Dimension object
+//and have it figure out which point needs to move... Or is it???
+// Ideally, we shouldn't have to fuck around with this shit. We need to fix the rendering code
+// so that we don't have to wait until the dragging is done to correct the position of the
+// point in question, but we'd need another variable tho.
+#if 0
+               if (dimPoint1)
+                       dimPoint1->SetPoint1(draggingHandle1 ? v2 : position);
+               
+               if (dimPoint2)
+                       dimPoint2->SetPoint2(draggingHandle2 ? v2 : endpoint);
+#endif
        }
-       else
-               needUpdate = false;
 }
 
+
 /*virtual*/ void Line::PointerReleased(void)
 {
        if (draggingHandle1 || draggingHandle2)
@@ -212,7 +432,6 @@ Like so:
                // Set the length (in case the global state was set to fixed (or not))
                if (Object::fixedLength)
                {
-
                        if (draggingHandle1)    // startpoint
                        {
                                Vector v = Vector(position - endpoint).Unit() * length;
@@ -220,36 +439,362 @@ Like so:
                        }
                        else                                    // endpoint
                        {
-//                             Vector v1 = endpoint - position;
                                Vector v = Vector(endpoint - position).Unit() * length;
                                endpoint = position + v;
                        }
                }
                else
                {
-                       // Otherwise, we calculate the new length, just in case on the next move
-                       // it turns out to have a fixed length. :-)
+                       // 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();
                }
+
+               if (!Object::fixedAngle)
+               {
+                       // Calculate the new angle, just in case on the next move it turns
+                       // out to be fixed. :-)
+                       angle = Vector(endpoint - position).Unit();
+               }
        }
 
-       dragging = false;
+       draggingLine = false;
        draggingHandle1 = false;
        draggingHandle2 = false;
 
-       // Here we check for just a click: If object was clicked and dragged, then
-       // revert to the old state (OSInactive). Otherwise, keep the new state that
-       // we set.
-/*Maybe it would be better to just check for "object was dragged" state and not have to worry
-about keeping track of old states...
-*/
        if (objectWasDragged)
                state = oldState;
 }
 
-#if 0
-/*virtual*/ bool Line::NeedsUpdate(void)
+
+/*virtual*/ bool Line::HitTest(Point point)
+{
+       hitPoint1 = hitPoint2 = hitLine = false;
+       Vector lineSegment = endpoint - position;
+       Vector v1 = point - position;
+       Vector v2 = point - endpoint;
+       double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
+       double distance;
+
+       // 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
+       // 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       0  0  1       0  0  0
+       // ex ey 1  ==>  ex ey 1  ==>  ex ey 0
+       // px py 1       px py 1       px py 0
+       //
+       // By translating the start point to the origin, and subtracting row 1 from
+       // all other rows, we end up with the matrix on the right which greatly
+       // simplifies the calculation of the determinant.
+
+       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;
+       else if ((distance * Painter::zoom) < 5.0)
+               hitLine = true;
+
+       return (hitPoint1 || hitPoint2 || hitLine ? true : false);
+}
+
+
+// Check to see if the point passed in coincides with any we have. If so, return a
+// pointer to it; otherwise, return NULL.
+/*virtual*/ Vector * Line::GetPointAt(Vector v)
 {
-       return needUpdate;
+       if (v == position)
+               return &position;
+       else if (v == endpoint)
+               return &endpoint;
+
+       return 0;
 }
+
+
+/*virtual*/ void Line::Enumerate(FILE * file)
+{
+       fprintf(file, "LINE %i (%lf,%lf) (%lf,%lf)\n", layer, position.x, position.y, endpoint.x, endpoint.y);
+}
+
+
+/*virtual*/ Object * Line::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.
+*/
+       return new Line(position, endpoint, parent);
+}
+
+
+/*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
+
+       // 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
+}
+
+
+/*virtual*/ QRectF Line::Extents(void)
+{
+       QRectF rect(QPointF(position.x, position.y), QPointF(endpoint.x, endpoint.y));
+       return rect.normalized();
+}
+
+
+/*virtual*/ void Line::Translate(Vector amount)
+{
+       position += amount;
+       endpoint += amount;
+}
+
+
+/*virtual*/ void Line::Rotate(Point point, double angle)
+{
+       Point l1 = Geometry::RotatePointAroundPoint(position, point, angle);
+       Point l2 = Geometry::RotatePointAroundPoint(endpoint, point, angle);
+       position = l1;
+       endpoint = l2;
+}
+
+
+/*virtual*/ void Line::Scale(Point point, double amount)
+{
+}
+
+
+/*virtual*/ void Line::Mirror(Point p1, Point p2)
+{
+       Point l1 = Geometry::MirrorPointAroundLine(position, p1, p2);
+       Point l2 = Geometry::MirrorPointAroundLine(endpoint, p1, p2);
+       position = l1;
+       endpoint = l2;
+}
+
+
+/*virtual*/ void Line::Save(void)
+{
+       Object::Save();
+       oldEndpoint = endpoint;
+}
+
+
+/*virtual*/ void Line::Restore(void)
+{
+       Object::Restore();
+       endpoint = oldEndpoint;
+}
+
+
+void Line::SetDimensionOnLine(Dimension * dimension/*= NULL*/)
+{
+       // If they don't pass one in, create it for the caller.
+       if (dimension == NULL)
+       {
+//printf("Line::SetDimensionOnLine(): Creating new dimension...\n");
+//             dimension = new Dimension(position, endpoint, DTLinear, this);
+               dimension = new Dimension(Connection(this, 0), Connection(this, 1.0), DTLinear, this);
+
+               if (parent)
+//{
+//printf("Line::SetDimensionOnLine(): Adding to parent...\n");
+                       parent->Add(dimension);
+//}
+       }
+       else
+       {
+               dimension->Connect(this, 0);
+               dimension->Connect(this, 1.0);
+       }
+
+       // Make sure the Dimension is connected to us...
+       Connect(dimension, 0);
+       Connect(dimension, 1.0);
+}
+
+
+Object * Line::FindAttachedDimension(void)
+{
+       // Is there anything connected to this line? If not, return NULL
+       if (connected.size() < 2)
+               return NULL;
+
+       // Otherwise, we have to search our objects to see if there's a likely
+       // candidate. In this case, we're looking for a pointer to the same object
+       // with a parameter of 0 and 1 respectively. This is O((n^2)/2).
+       for(uint i=0; i<connected.size(); i++)
+       {
+               for(uint j=i+1; j<connected.size(); j++)
+               {
+//printf("Line: connected[i]=%X, connected[j]=%X, connected[i].t=%lf, connected[j].t=%lf\n", connected[i].object, connected[j].object, connected[i].t, connected[j].t);
+                       if ((connected[i].object == connected[j].object)
+                               && ((connected[i].t == 0 && connected[j].t == 1.0)
+                               || (connected[i].t == 1.0 && connected[j].t == 0)))
+                               return connected[i].object;
+               }
+       }
+
+       // Didn't find anything, so return NULL
+       return NULL;
+}
+
+
+void Line::SaveHitState(void)
+{
+       oldHitPoint1 = hitPoint1;
+       oldHitPoint2 = hitPoint2;
+       oldHitLine = hitLine;
+}
+
+
+bool Line::HitStateChanged(void)
+{
+       if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
+               return true;
+
+       return false;
+}
+
+
+/*
+Intersection of two lines:
+
+Find where the lines with equations r = i + j + t (3i - j) and r = -i + s (j) intersect.
+
+When they intersect, we can set the equations equal to one another:
+
+i + j + t (3i - j) = -i + s (j)
+
+Equating coefficients:
+1 + 3t = -1 and 1 - t = s
+So t = -2/3 and s = 5/3
+
+The position vector of the intersection point is therefore given by putting t = -2/3 or s = 5/3 into one of the above equations. This gives -i +5j/3 .
+
+
+so, let's say we have two lines, l1 and l2. Points are v0(p0x, p0y), v1(p1x, p1y) for l1
+and v2(p2x, p2y), v3(p3x, p3y) for l2.
+
+d1 = v1 - v0, d2 = v3 - v2
+
+Our parametric equations for the line then are:
+
+r1 = v0 + t(d1)
+r2 = v2 + s(d2)
+
+Set r1 = r2, thus we have:
+
+v0 + t(d1) = v2 + s(d2)
+
+Taking coefficients, we have:
+
+p0x + t(d1x) = p2x + s(d2x)
+p0y + t(d1y) = p2y + s(d2y)
+
+rearranging we get:
+
+t(d1x) - s(d2x) = p2x - p0x
+t(d1y) - s(d2y) = p2y - p0y
+
+Determinant D is ad - bc where the matrix looks like:
+
+a b
+c d
+
+so D = (d1x)(d2y) - (d2x)(d1y)
+if D = 0, the lines are parallel.
+Dx = (p2x - p0x)(d2y) - (d2x)(p2y - p0y)
+Dy = (d1x)(p2y - p0y) - (p2x - p0x)(d1y)
+t = Dx/D, s = Dy/D
+
+We only need to calculate t, as we can then multiply it by d1 to get the intersection point.
+
+---------------------------------------------------------------------------------------------------
+
+The first and most preferred method for intersection calculation is the perp-product calculation. There are two vectors, v1 and v2. Create a third vector vector between the starting points of these vectors, and calculate the perp product of v2 and the two other vectors. These two scalars have to be divided to get the mulitplication ratio of v1 to reach intersection point. So:
+
+v1 ( bx1 , by1 );
+v2 ( bx2 , by2 );
+v3 ( bx3 , by3 );
+
+Perp product is equal with dot product of normal of first vector and the second vector, so we need normals:
+
+n1 ( -by1 , bx1 );
+n3 ( -by3 , bx3 );
+
+Dot products:
+
+dp1 = n3 . v2 = -by3 * bx2 + bx3 * by2;
+dp2 = n1 . v2 = -by1 * bx2 + bx1 * by2;
+
+ratio = dp1 / dp2;
+crossing vector = v1 * ratio;
+
+And that's it.
+
+-----------------------------------
+
+So... to code this, let's say we have two Lines: l1 & l2.
+
+Vector v1 = l1.endpoint - l1.position;
+Vector v2 = l2.endpoint - l2.position;
+Vector v3 = v2 - v1;
+
+Vector normal1(-v1.y, v1.x);
+Vector normal3(-v3.y, v3.x);
+
+double dotProduct1 = v2.Dot(normal1);
+double dotProduct2 = v2.Dot(normal3);
+
+if (dotProduct2 == 0)
+       return ParallelLines;
+else
+{
+       // I think we'd still have to add the intersection to the position point to get the intersection...
+       Point intersection = v1 * (dotProduct1 / dotProduct2);
+       return intersection;
+}
+*/
+