]> Shamusworld >> Repos - architektonas/blobdiff - src/line.cpp
Added new triangulation tool, ability to snap to endpoints/intersections.
[architektonas] / src / line.cpp
index b7abc9ad6bb62939cb9ddd5102a65d25d7bcadf3..7841fa8a823e43720d327985e16bf44370eb0ce2 100644 (file)
@@ -52,6 +52,16 @@ Line::~Line()
        // connect to this dimension object at this point, instead of just becoming
        // detached.
 #endif
+//actually not true, we know the object pointer and parameter!
+//actuall, the Object base class does this for us...!
+#if 0
+       std::vector<Connection>::iterator i;
+
+       for(i=connected.begin(); i!=connected.end(); i++)
+       {
+               (*i).object->Disconnect(this, (*i).t);
+       }
+#endif
 }
 
 
@@ -96,21 +106,7 @@ Line::~Line()
 
                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
        }
 }
 
@@ -123,19 +119,28 @@ Line::~Line()
 
 /*virtual*/ bool Line::Collided(Vector point)
 {
-       // Someone told us to fuck off, so we'll fuck off. :-)
-       if (ignoreClicks)
-               return false;
-
+/*
+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... :-/
+Actually, this is done here to keep tools from selecting stuff inadvertantly...
+*/
        // We can assume this, since this is a mouse down event here.
        objectWasDragged = false;
-       HitTest(point);
+       bool hit = HitTest(point);
+
+       // Someone told us to fuck off, so we'll fuck off. :-)
+       if (ignoreClicks)
+               return hit;
 
        // Now that we've done our hit testing on the non-snapped point, snap it if
        // necessary...
        if (snapToGrid)
                point = SnapPointToGrid(point);
 
+       if (snapPointIsValid)
+               point = snapPoint;
+
 // 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
@@ -146,79 +151,6 @@ Line::~Line()
                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
-*/
-       // 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;
-               }
-       }
-
        if (state == OSInactive)
        {
 //How to translate this into pixels from Document space???
@@ -288,32 +220,31 @@ a dimension only) Draw() function... :-/
 }
 
 
-/*virtual*/ void Line::PointerMoved(Vector point)
+/*virtual*/ bool Line::PointerMoved(Vector point)
 {
        if (selectionInProgress)
        {
                // 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;
 
-               return;
+               return false;
        }
 
-       // 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)
+       // Hit test tells us what we hit (if anything) through boolean variables.
        SaveHitState();
-       HitTest(point);
+       bool hovered = HitTest(point);
        needUpdate = HitStateChanged();
 
+       if (snapToGrid)
+               point = SnapPointToGrid(point);
+
+       if (snapPointIsValid)
+               point = snapPoint;
+
        objectWasDragged = (draggingLine | draggingHandle1 | draggingHandle2);
 
        if (objectWasDragged)
@@ -330,6 +261,20 @@ 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<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 ((*i).object->type == OTDimension)
+                                       ((Dimension *)((*i).object))->MovePointAtParameter((*i).t, delta);
+                       }
+               }
        }
 
 /*
@@ -397,6 +342,8 @@ the horizontal line or vertical line that intersects from the current mouse posi
                        dimPoint2->SetPoint2(draggingHandle2 ? v2 : endpoint);
 #endif
        }
+
+       return hovered;
 }
 
 
@@ -444,23 +391,14 @@ 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);
+       Vector v3 = point - Center();   // Midpoint, for snapping...
        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
@@ -489,14 +427,28 @@ the horizontal line or vertical line that intersects from the current mouse posi
                        / lineSegment.Magnitude());
 
        if ((v1.Magnitude() * Painter::zoom) < 8.0)
+       {
                hitPoint1 = true;
+               snapPoint = position;
+               snapPointIsValid = true;
+       }
        else if ((v2.Magnitude() * Painter::zoom) < 8.0)
+       {
                hitPoint2 = true;
+               snapPoint = endpoint;
+               snapPointIsValid = true;
+       }
        else if ((distance * Painter::zoom) < 5.0)
                hitLine = true;
 
+       // Not a manipulation point, but a snapping point:
+       if ((v3.Magnitude() * Painter::zoom) < 8.0)
+       {
+               snapPoint = Center();
+               snapPointIsValid = true;
+       }
+
        return (hitPoint1 || hitPoint2 || hitLine ? true : false);
-//     return HitStateChanged();
 }
 
 
@@ -537,23 +489,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
 }
 
 
@@ -571,12 +529,16 @@ same reference number.
 }
 
 
-/*virtual*/ void Line::Rotate(Vector point, double angle)
+/*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(Vector point, double amount)
+/*virtual*/ void Line::Scale(Point point, double amount)
 {
 }
 
@@ -604,30 +566,35 @@ 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)
+       // But ONLY if this line has a parent container!
+       // This is really bad to do here, it should be done in the parent container, always!
+#warning "!!! Parent container should be creating Dimension object !!!"
+       if ((dimension == NULL) && (parent != 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);
+               dimension = new Dimension(position, endpoint, DTLinear, parent);
+//             dimension = new Dimension(Connection(this, 0), Connection(this, 1.0), DTLinear, this);
 
-               if (parent)
+               // THIS IS SERIOUS!!! WITHOUT A PARENT, THIS OBJECT IS IN LIMBO!!!
+//             if (parent)
 //{
 //printf("Line::SetDimensionOnLine(): Adding to parent...\n");
-                       parent->Add(dimension);
+               parent->Add(dimension);
 //}
        }
-       else
-       {
-               dimension->Connect(this, 0);
-               dimension->Connect(this, 1.0);
-       }
+
+       dimension->Connect(this, 0);
+       dimension->Connect(this, 1.0);
 
        // Make sure the Dimension is connected to us...
        Connect(dimension, 0);
        Connect(dimension, 1.0);
+
+       dimension->position = position;
+       dimension->endpoint = endpoint;
 }