]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Further progress on Polylines: Polylines can be selected and moved.
[architektonas] / src / drawingview.cpp
index 3baa2d77eed71712dd168fa79ff6ddb73e016faa..e65bbe92de99f12d47f6f2e12596c25d33485c1d 100644 (file)
@@ -41,6 +41,7 @@
 #include "painter.h"
 #include "penwidget.h"
 #include "structs.h"
+#include "units.h"
 #include "utils.h"
 
 #define BACKGROUND_MAX_SIZE    512
@@ -52,8 +53,9 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
        scale(1.0), offsetX(-10), offsetY(-10), supressSelected(false),
        document(true),
-       gridPixels(0), collided(false), scrollDrag(false), hoverPointValid(false),
-       hoveringIntersection(false), dragged(NULL), draggingObject(false),
+       gridPixels(0), collided(false), scrollDrag(false),
+       hoverPointValid(false), hoveringIntersection(false),
+       dragged(NULL), draggingObject(false),
        angleSnap(false), dirty(false)
 {
 //wtf? doesn't work except in c++11??? document = { 0 };
@@ -131,7 +133,7 @@ void DrawingView::DrawBackground(Painter * painter)
 
        painter->SetBrush(0xF0F0F0);
        painter->SetPen(0xF0F0F0, 1, 1);
-       painter->DrawRect(QRectF(QPointF(ul.x, ul.y), QPointF(br.x, br.y)));
+       painter->DrawRect(Rect(ul, br));
 
        double spacing = Global::gridSpacing;
 
@@ -453,6 +455,28 @@ void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool
 
                        break;
 
+               case OTPolyline:
+               {
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       Polyline * pl = (Polyline *)obj;
+
+                       for(long unsigned int i=0; i<(pl->points.size()-1); i++)
+                       {
+                               Point p1 = pl->points[i];
+                               Point p2 = pl->points[i + 1];
+
+                               if (p1.b == 0)
+                                       painter->DrawLine(p1, p2);
+                               else
+                               {
+                                       Arc a = Geometry::Unpack(p1, p2, p1.b);
+                                       painter->DrawArc(a.p[0], a.radius[0], a.angle[0], a.angle[1]);
+                               }
+                       }
+
+                       break;
+               }
+
                case OTDimension:
                {
                        Dimension * d = (Dimension *)obj;
@@ -556,20 +580,7 @@ void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool
                        painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
                        Point ctr = p2 + (Vector(p2, p1) / 2.0);
 
-                       QString dimText;
-
-                       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);
-                       }
+                       QString dimText = GetDimensionText(&document, length);
 
 /*
 Where is the text offset?  It looks like it's drawing in the center, but obviously it isn't.  It isn't here, it's in Painter::DrawAngledText().
@@ -643,15 +654,12 @@ Where is the text offset?  It looks like it's drawing in the center, but obvious
                        break;
                }
 
+#if 0
                case OTPolygon:
                {
                        break;
                }
-
-               case OTPolyline:
-               {
-                       break;
-               }
+#endif
 
                case OTContainer:
                {
@@ -1986,8 +1994,8 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                // Didn't hit any object and not using a tool, so do a selection
                // rectangle
                Global::selectionInProgress = true;
-               Global::selection.setTopLeft(QPointF(point.x, point.y));
-               Global::selection.setBottomRight(QPointF(point.x, point.y));
+               Global::selection.l = Global::selection.r = point.x;
+               Global::selection.t = Global::selection.b = point.y;
                select = GetSelection();
        }
        else if (event->button() == Qt::MiddleButton)
@@ -2009,7 +2017,8 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        }
 
        Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
-       Global::selection.setBottomRight(QPointF(point.x, point.y));
+       Global::selection.r = point.x;
+       Global::selection.b = point.y;
        // Only needs to be done here, as mouse down is always preceded by movement
        Global::snapPointIsValid = false;
        hoveringIntersection = false;
@@ -2375,6 +2384,7 @@ Point DrawingView::SnapPointToGrid(Point point)
        point.y = floor(point.y);
        point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
        point *= Global::gridSpacing;
+
        return point;
 }
 
@@ -2419,39 +2429,14 @@ Rect DrawingView::GetObjectExtents(Object * obj)
        case OTArc:
        {
                Arc * a = (Arc *)obj;
+               rect = a->Bounds();
+               break;
+       }
 
-               double start = a->angle[0];
-               double end = start + a->angle[1];
-               rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
-
-               // If the end of the arc is before the beginning, add 360 degrees to it
-               if (end < start)
-                       end += TAU;
-
-               // Adjust the bounds depending on which axes are crossed
-               if ((start < QTR_TAU) && (end > QTR_TAU))
-                       rect.t = 1.0;
-
-               if ((start < HALF_TAU) && (end > HALF_TAU))
-                       rect.l = -1.0;
-
-               if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
-                       rect.b = -1.0;
-
-               if ((start < TAU) && (end > TAU))
-                       rect.r = 1.0;
-
-               if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
-                       rect.t = 1.0;
-
-               if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
-                       rect.l = -1.0;
-
-               if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
-                       rect.b = -1.0;
-
-               rect *= a->radius[0];
-               rect.Translate(a->p[0]);
+       case OTPolyline:
+       {
+               Polyline * p = (Polyline *)obj;
+               rect = p->Bounds();
                break;
        }
 
@@ -2488,6 +2473,8 @@ void DrawingView::CheckObjectBounds(void)
        {
                Object * obj = (Object *)(*i);
                obj->selected = false;
+               Rect selection = Global::selection;
+               selection.Normalize();
 
                switch (obj->type)
                {
@@ -2496,7 +2483,7 @@ void DrawingView::CheckObjectBounds(void)
                {
                        Line * l = (Line *)obj;
 
-                       if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
+                       if (selection.Contains(l->p[0]) && selection.Contains(l->p[1]))
                                l->selected = true;
 
                        break;
@@ -2505,8 +2492,9 @@ void DrawingView::CheckObjectBounds(void)
                case OTCircle:
                {
                        Circle * c = (Circle *)obj;
+                       Vector radVec(c->radius[0], c->radius[0]);
 
-                       if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0]))
+                       if (selection.Contains(c->p[0] - radVec) && selection.Contains(c->p[0] + radVec))
                                c->selected = true;
 
                        break;
@@ -2515,66 +2503,21 @@ void DrawingView::CheckObjectBounds(void)
                case OTArc:
                {
                        Arc * a = (Arc *)obj;
+                       Rect bounds = a->Bounds();
 
-                       double start = a->angle[0];
-                       double end = start + a->angle[1];
-                       QPointF p1(cos(start), sin(start));
-                       QPointF p2(cos(end), sin(end));
-                       QRectF bounds(p1, p2);
-
-#if 1
-                       // Swap X/Y coordinates if they're backwards...
-                       if (bounds.left() > bounds.right())
-                       {
-                               double temp = bounds.left();
-                               bounds.setLeft(bounds.right());
-                               bounds.setRight(temp);
-                       }
-
-                       if (bounds.bottom() > bounds.top())
-                       {
-                               double temp = bounds.bottom();
-                               bounds.setBottom(bounds.top());
-                               bounds.setTop(temp);
-                       }
-#else
-                       // Doesn't work as advertised! For shame!
-                       bounds = bounds.normalized();
-#endif
-
-                       // If the end of the arc is before the beginning, add 360 degrees
-                       // to it
-                       if (end < start)
-                               end += TAU;
-
-                       // Adjust the bounds depending on which axes are crossed
-                       if ((start < QTR_TAU) && (end > QTR_TAU))
-                               bounds.setTop(1.0);
-
-                       if ((start < HALF_TAU) && (end > HALF_TAU))
-                               bounds.setLeft(-1.0);
-
-                       if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
-                               bounds.setBottom(-1.0);
-
-                       if ((start < TAU) && (end > TAU))
-                               bounds.setRight(1.0);
-
-                       if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
-                               bounds.setTop(1.0);
-
-                       if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
-                               bounds.setLeft(-1.0);
+                       if (selection.Contains(bounds))
+                               a->selected = true;
 
-                       if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
-                               bounds.setBottom(-1.0);
+                       break;
+               }
 
-                       bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
-                       bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
-                       bounds.translate(a->p[0].x, a->p[0].y);
+               case OTPolyline:
+               {
+                       Polyline * pl = (Polyline *)obj;
+                       Rect bounds = pl->Bounds();
 
-                       if (Global::selection.contains(bounds))
-                               a->selected = true;
+                       if (selection.Contains(bounds))
+                               pl->selected = true;
 
                        break;
                }
@@ -2584,7 +2527,7 @@ void DrawingView::CheckObjectBounds(void)
                        Text * t = (Text *)obj;
                        Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
 
-                       if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
+                       if (selection.Contains(r))
                                t->selected = true;
 
                        break;
@@ -2594,7 +2537,7 @@ void DrawingView::CheckObjectBounds(void)
                {
                        Container * c = (Container *)obj;
 
-                       if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
+                       if (selection.Contains(c->p[0]) && selection.Contains(c->p[1]))
                                c->selected = true;
 
                        break;
@@ -2679,7 +2622,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if ((distance * Global::zoom) < 5.0)
                        obj->hitObject = true;
 
-               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject);
 
                if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
                        needUpdate = true;
@@ -2702,7 +2645,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
                        obj->hitObject = true;
 
-               obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitObject);
 
                if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
                        needUpdate = true;
@@ -2724,7 +2667,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                // Get the span that we're pointing at...
                double span = angle - obj->angle[0];
 
-               // N.B.: Still need to hit test the arc start & arc span handles...
+               // N.B.: Still need to hit test the arc start & arc span handles... [looks like it's DONE?]
                double spanAngle = obj->angle[0] + obj->angle[1];
                Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
                Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
@@ -2752,7 +2695,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
                        obj->hitObject = true;
 
-               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject);
 
                if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
                        needUpdate = true;
@@ -2760,6 +2703,86 @@ bool DrawingView::HitTest(Object * obj, Point point)
                break;
        }
 
+       case OTPolyline:
+       {
+               Polyline * pl = (Polyline *)obj;
+               bool oldHP0 = pl->hitPoint[0], oldHO = pl->hitObject;
+               pl->hitPoint[0] = pl->hitObject = false;
+
+               for(long unsigned int i=0; i<(pl->points.size()-1); i++)
+               {
+                       Point p1 = pl->points[i];
+                       Point p2 = pl->points[i + 1];
+
+                       double dist1 = Vector::Magnitude(p1, point) * Global::zoom;
+                       double dist2 = Vector::Magnitude(p2, point) * Global::zoom;
+
+                       // Check for endpoints of lines and/or arcs first
+                       if (dist1 < 8.0)
+                       {
+                               pl->hitPoint[0] = true;
+                               hoverPoint = p1;
+                               hoverPointValid = true;
+                               pl->ptNum = i;
+                       }
+                       else if (dist2 < 8.0)
+                       {
+                               pl->hitPoint[0] = true;
+                               hoverPoint = p2;
+                               hoverPointValid = true;
+                               pl->ptNum = i + 1;
+                       }
+                       // Check for object (line/arc) last
+                       else if (p1.b == 0)
+                       {
+                               double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
+                               double objDist;
+
+                               // No bump == check for line proximity
+                               if (t < 0.0)
+                                       objDist = dist1;
+                               else if (t > 1.0)
+                                       objDist = dist2;
+                               else
+                               {
+                                       Line l(p1, p2);
+                                       Vector v1 = l.Vect();
+                                       Vector v2(p1, point);
+                                       objDist = fabs((v1.x * v2.y - v2.x * v1.y) / l.Length()) * Global::zoom;
+                               }
+
+                               if (objDist < 5.0)
+                                       pl->hitObject = true;
+                       }
+                       else
+                       {
+                               // We have a bump == check for arc proximity
+                               Arc a = Geometry::Unpack(p1, p2, p1.b);
+                               double length = Vector::Magnitude(a.p[0], point);
+                               double angle = Vector::Angle(a.p[0], point);
+                               double span = angle - a.angle[0];
+
+                               // Ensure point span is positive if we have a positive arc span
+                               if (span < 0 && a.angle[1] > 0)
+                                       span += TAU;
+
+                               // Ensure point span is negative if we have a negative arc span
+                               if (span > 0 && a.angle[1] < 0)
+                                       span -= TAU;
+
+                               if (((fabs(length - a.radius[0]) * Global::zoom) < 2.5) && (fabs(span) < fabs(a.angle[1])))
+                                       pl->hitObject = true;
+                       }
+               }
+
+               pl->hovered = (pl->hitPoint[0] || pl->hitObject);
+
+               if ((oldHP0 != pl->hitPoint[0]) || (oldHO != pl->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+
        case OTDimension:
        {
                bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
@@ -2812,7 +2835,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
                        obj->hitPoint[4] = true;
 
-               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject);
 
                if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
                        needUpdate = true;
@@ -2832,7 +2855,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                if (r.Contains(point))
                        obj->hitObject = true;
 
-               obj->hovered = (obj->hitObject ? true : false);
+               obj->hovered = obj->hitObject;
 
                if (oldHO != obj->hitObject)
                        needUpdate = true;
@@ -3030,11 +3053,16 @@ N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not*
                        obj->p[0] = point;
                else if (obj->hitObject)
                {
-                       double oldRadius = obj->length;
-                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+                       if (shiftDown)
+                       {
+                               double oldRadius = obj->length;
+                               obj->radius[0] = Vector::Magnitude(obj->p[0], point);
 
-                       QString text = QObject::tr("Radius: %1\nScale: %2%");
-                       informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
+                               QString text = QObject::tr("Radius: %1\nScale: %2%");
+                               informativeText = text.arg(obj->radius[0], 0, 'f', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'f', 0);
+                       }
+                       else
+                               obj->p[0] += delta;
                }
 
                break;
@@ -3060,14 +3088,14 @@ N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not*
                                        obj->angle[1] += TAU;
 
                                QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
-                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'f', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'f', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'f', 2);
                                return;
                        }
 
                        double angle = Vector::Angle(obj->p[0], point);
                        obj->angle[0] = angle;
                        QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
-                       informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
+                       informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'f', 4);
                }
                else if (obj->hitPoint[2])
                {
@@ -3081,7 +3109,7 @@ N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not*
                                        obj->angle[1] += TAU;
 
                                QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
-                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'f', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'f', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'f', 2);
                                return;
                        }
 
@@ -3091,23 +3119,46 @@ N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not*
                        if (obj->angle[0] < 0)
                                obj->angle[0] += TAU;
 
+                       double endAngle = obj->angle[0] + obj->angle[1];
+
+                       if (endAngle > TAU)
+                               endAngle -= TAU;
+
                        QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
-                       informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
+                       informativeText = text.arg(endAngle * RADIANS_TO_DEGREES, 0, 'f', 4);
                }
                else if (obj->hitObject)
                {
                        if (shiftDown)
                        {
-                               return;
+                               obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+                               QString text = QObject::tr("Radius: %1");
+                               informativeText = text.arg(obj->radius[0], 0, 'f', 4);
                        }
-
-                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
-                       QString text = QObject::tr("Radius: %1");
-                       informativeText = text.arg(obj->radius[0], 0, 'd', 4);
+                       else
+                               obj->p[0] += delta;
                }
 
                break;
 
+       case OTPolyline:
+       {
+#if 1
+               // Do this for now...
+               ((Polyline *)obj)->Translate(delta);
+//             Polyline * pl = (Polyline *)obj;
+
+//             for(long unsigned int i=0; i<pl->points.size(); i++)
+//                     pl->points[i] += delta;
+#else
+               Polyline * pl = (Polyline *)obj;
+
+               for(long unsigned int i=0; i<(pl->points.size()-1); i++)
+#endif
+
+               break;
+       }
+
        case OTDimension:
                if (obj->hitPoint[0])
                        obj->p[0] = point;