]> Shamusworld >> Repos - architektonas/blobdiff - src/line.cpp
Initial work on BlockWidget.
[architektonas] / src / line.cpp
index d37106e5a7846df0f9fe8fabed88aee129ea42c5..aa5054bab3da9ea9bb79ca65b8cf695093ccb687 100644 (file)
 #include "line.h"
 
 #include <QtGui>
+#include "container.h"
 #include "dimension.h"
+#include "mathconstants.h"
 #include "painter.h"
 
 
-Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint(p2),
+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;
 }
 
 
@@ -86,6 +90,35 @@ Line::~Line()
        else
 //             painter->DrawLine((int)position.x, (int)position.y, (int)endpoint.x, (int)endpoint.y);
                painter->DrawLine(position, endpoint);
+
+       // If we're rotating or setting the span, draw an information panel
+       // showing both absolute and relative angles being set.
+       if (draggingHandle1 || draggingHandle2)
+       {
+               double absAngle = (Vector(endpoint - position).Angle()) * RADIANS_TO_DEGREES;
+//             double relAngle = (startAngle >= oldAngle ? startAngle - oldAngle :
+//                     startAngle - oldAngle + (2.0 * PI)) * RADIANS_TO_DEGREES;
+               double absLength = Vector(position - endpoint).Magnitude();
+
+               QString text;
+
+               text = QObject::tr("Length: %1 in.\n") + QChar(0x2221) + QObject::tr(": %2");
+               text = text.arg(absLength).arg(absAngle);
+
+               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
+               painter->DrawRoundedRect(textRect, 7.0, 7.0);
+
+               textRect.setLeft(textRect.left() + 14);
+               painter->SetFont(*Object::font);
+//                     pen = QPen(QColor(0xDF, 0x5F, 0x00), 1.0, Qt::SolidLine);
+               pen = QPen(QColor(0x00, 0x5F, 0xDF));
+               painter->SetPen(pen);
+               painter->DrawText(textRect, Qt::AlignVCenter, text);
+//             painter->SetPen(QPen(QColor(0xDF, 0x5F, 0x00)));
+       }
 }
 
 /*virtual*/ Vector Line::Center(void)
@@ -101,6 +134,14 @@ Line::~Line()
        objectWasDragged = false;
        HitTest(point);
 
+       // 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.
@@ -230,10 +271,20 @@ Like so:
 //                     state = OSInactive;
                        oldPoint = point;
                        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;
@@ -243,6 +294,22 @@ Like so:
 
 /*virtual*/ void 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))
+#endif
+                       state = OSSelected;
+               else
+                       state = OSInactive;
+
+               return;
+       }
+
        // Hit test tells us what we hit (if anything) through boolean variables. It
        // also tells us whether or not the state changed.
        needUpdate = HitTest(point);
@@ -261,6 +328,8 @@ Like so:
 
                oldPoint = point;
                needUpdate = true;
+
+//doesn't work         QMainWindow::statusBar()->setText("You are manipulating a line");
        }
 
 /*
@@ -273,18 +342,20 @@ Ugly ways to do it:
 
 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/
+ - 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. :-)
+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)
        {
@@ -390,6 +461,61 @@ about keeping track of old states...
 }
 
 
+/*virtual*/ bool Line::HitTest(Point point)
+{
+       SaveState();
+
+       hitPoint1 = hitPoint2 = hitLine = false;
+       Vector lineSegment = endpoint - position;
+       Vector v1 = point - position;
+       Vector v2 = point - endpoint;
+       double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
+
+       // Geometric interpretation:
+       // The parameterized point on the vector lineSegment is where the perpendicular
+       // intersects lineSegment. 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());
+
+       // Geometric interpretation of the above:
+       // 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.
+
+//How do we determine distance here? Especially if zoomed in or out???
+//#warning "!!! Distances tested for may not be valid if zoomed in or out !!!"
+// [FIXED]
+       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 StateChanged();
+}
+
+
 // 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)
@@ -446,6 +572,13 @@ you then *definitely* do not want them to have the same reference number.
 }
 
 
+/*virtual*/ QRectF Line::Extents(void)
+{
+       QRectF rect(QPointF(position.x, position.y), QPointF(endpoint.x, endpoint.y));
+       return rect.normalized();
+}
+
+
 void Line::SetDimensionOnLine(Dimension * dimension/*=NULL*/)
 {
        // If they don't pass one in, create it for the caller.
@@ -499,60 +632,6 @@ Object * Line::FindAttachedDimension(void)
 }
 
 
-bool Line::HitTest(Point point)
-{
-       SaveState();
-
-       hitPoint1 = hitPoint2 = hitLine = false;
-       Vector lineSegment = endpoint - position;
-       Vector v1 = point - position;
-       Vector v2 = point - endpoint;
-       double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
-
-       // Geometric interpretation:
-       // The parameterized point on the vector lineSegment is where the perpendicular
-       // intersects lineSegment. 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());
-
-       // Geometric interpretation of the above:
-       // 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.
-
-//How do we determine distance here? Especially if zoomed in or out???
-//#warning "!!! Distances tested for may not be valid if zoomed in or out !!!"
-// [FIXED]
-       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 StateChanged();
-}
-
 void Line::SaveState(void)
 {
        oldHitPoint1 = hitPoint1;
@@ -560,6 +639,7 @@ void Line::SaveState(void)
        oldHitLine = hitLine;
 }
 
+
 bool Line::StateChanged(void)
 {
        if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
@@ -568,6 +648,7 @@ bool Line::StateChanged(void)
        return false;
 }
 
+
 /*
 Intersection of two lines: