]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Preliminary support for Polylines.
[architektonas] / src / drawingview.cpp
index 8ad55628ced17fd993c289c2c58e524a490ea8b4..39196a753646a9eb7929e8d699b9e21f89c8fce3 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,41 @@ void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool
 
                        break;
 
+               case OTPolyline:
+               {
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       Polyline * pl = (Polyline *)obj;
+                       Point lastp;
+                       double lastbump;
+
+                       for(VPVectorIter i=pl->points.begin(); i!=pl->points.end(); i++)
+                       {
+                               if (i != pl->points.begin())
+                               {
+                                       Point p = ((Object *)(*i))->p[0];
+                                       double bump = ((Object *)(*i))->length;
+
+                                       if (lastbump == 0)
+                                               painter->DrawLine(lastp, p);
+                                       else
+                                       {
+                                               Arc a = Geometry::Unpack(lastp, p, lastbump);
+                                               painter->DrawArc(a.p[0], a.radius[0], a.angle[0], a.angle[1]);
+                                       }
+
+                                       lastp = p;
+                                       lastbump = bump;
+                               }
+                               else
+                               {
+                                       lastp = ((Object *)(*i))->p[0];
+                                       lastbump = ((Object *)(*i))->length;
+                               }
+                       }
+
+                       break;
+               }
+
                case OTDimension:
                {
                        Dimension * d = (Dimension *)obj;
@@ -556,20 +593,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 +667,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:
                {
@@ -809,8 +830,9 @@ void DrawingView::ToolHandler(int mode, Point p)
 
 void DrawingView::ToolDraw(Painter * painter)
 {
-       if (Global::tool == TTLine)
+       switch (Global::tool)
        {
+       case TTLine:
                if (Global::toolState == TSNone)
                {
                        painter->DrawHandle(toolPoint[0]);
@@ -830,9 +852,10 @@ void DrawingView::ToolDraw(Painter * painter)
                        QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
                        informativeText = text.arg(absLength).arg(absAngle);
                }
-       }
-       else if (Global::tool == TTCircle)
-       {
+
+               break;
+
+       case TTCircle:
                if (Global::toolState == TSNone)
                {
                        painter->DrawHandle(toolPoint[0]);
@@ -850,9 +873,10 @@ void DrawingView::ToolDraw(Painter * painter)
                        QString text = tr("Radius: %1 in.");
                        informativeText = text.arg(length);
                }
-       }
-       else if (Global::tool == TTArc)
-       {
+
+               break;
+
+       case TTArc:
                if (Global::toolState == TSNone)
                {
                        painter->DrawHandle(toolPoint[0]);
@@ -894,9 +918,10 @@ void DrawingView::ToolDraw(Painter * painter)
                        QString text = tr("Arc span: %1") + QChar(0x00B0);
                        informativeText = text.arg(RADIANS_TO_DEGREES * span);
                }
-       }
-       else if (Global::tool == TTRotate)
-       {
+
+               break;
+
+       case TTRotate:
                if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
                        painter->DrawHandle(toolPoint[0]);
                else if ((Global::toolState == TSPoint2) && shiftDown)
@@ -915,9 +940,10 @@ void DrawingView::ToolDraw(Painter * painter)
                        if (ctrlDown)
                                informativeText += " (Copy)";
                }
-       }
-       else if (Global::tool == TTMirror)
-       {
+
+               break;
+
+       case TTMirror:
                if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
                        painter->DrawHandle(toolPoint[0]);
                else if ((Global::toolState == TSPoint2) && shiftDown)
@@ -941,9 +967,10 @@ void DrawingView::ToolDraw(Painter * painter)
                        if (ctrlDown)
                                informativeText += " (Copy)";
                }
-       }
-       else if (Global::tool == TTDimension)
-       {
+
+               break;
+
+       case TTDimension:
                if (Global::toolState == TSNone)
                {
                        painter->DrawHandle(toolPoint[0]);
@@ -963,9 +990,10 @@ void DrawingView::ToolDraw(Painter * painter)
                        QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
                        informativeText = text.arg(absLength).arg(absAngle);
                }
-       }
-       else if (Global::tool == TTTrim)
-       {
+
+               break;
+
+       case TTTrim:
                if (toolObj[0] != NULL)
                {
                        // We're assuming ATM it's just a line...
@@ -974,9 +1002,10 @@ void DrawingView::ToolDraw(Painter * painter)
 //                     QString text = tr("Arc span: %1") + QChar(0x00B0);
 //                     informativeText = text.arg(RADIANS_TO_DEGREES * span);
                }
-       }
-       else if (Global::tool == TTParallel)
-       {
+
+               break;
+
+       case TTParallel:
                if (Global::toolState == TSPoint1)
                {
                        painter->SetPen(0xFF00FF, 2.0, LSSolid);
@@ -1005,6 +1034,11 @@ void DrawingView::ToolDraw(Painter * painter)
                                }
                        }
                }
+
+               break;
+
+       default:
+               break;
        }
 }
 
@@ -1951,9 +1985,14 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                        if (Global::fixedLength)
                        {
                                if (dragged->type == OTLine)
-                               {
-                                       dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
-                               }
+                                       dragged->length = ((Line *)dragged)->Length();
+                       }
+
+                       // Needed for fixed angle handling
+                       if (Global::fixedAngle)
+                       {
+                               if (dragged->type == OTLine)
+                                       dragged->p[2] = ((Line *)dragged)->Unit();
                        }
 
                        if (dragged->type == OTCircle)
@@ -1968,8 +2007,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)
@@ -1991,7 +2030,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;
@@ -2404,12 +2444,40 @@ Rect DrawingView::GetObjectExtents(Object * obj)
 
                double start = a->angle[0];
                double end = start + a->angle[1];
+
+               // Swap 'em if the span is negative...
+               if (a->angle[1] < 0)
+               {
+                       end = a->angle[0];
+                       start = end + 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;
 
+/*
+Find which quadrant the start angle is in (consider the beginning of the 90° angle to be in the quadrant, the end to be in the next quadrant).  Then, divide the span into 90° segments.  The integer portion is the definite axis crossings; the remainder needs more scrutiny.  There will be an additional axis crossing if the the sum of the start angle and the remainder is > 90°.
+*/
+#if 1
+               int quadStart = (int)(a->angle[0] / QTR_TAU);
+               double qsRemain = a->angle[0] - ((double)quadStart * QTR_TAU);
+               int numAxes = (int)((a->angle[1] + qsRemain) / QTR_TAU);
+
+               double axis[4] = { 0, 0, 0, 0 };
+               axis[0] = rect.t, axis[1] = rect.l, axis[2] = rect.b, axis[3] = rect.r;
+               double box[4] = { 1.0, -1.0, -1.0, 1.0 };
+
+               for(int i=0; i<numAxes; i++)
+                       axis[(quadStart + i) % 4] = box[(quadStart + i) % 4];
+
+               // The rect is constructed the same way we traverse the axes: TLBR
+               Rect r2(axis[0], axis[1], axis[2], axis[3]);
+
+               rect |= r2;
+#else
                // Adjust the bounds depending on which axes are crossed
                if ((start < QTR_TAU) && (end > QTR_TAU))
                        rect.t = 1.0;
@@ -2431,6 +2499,7 @@ Rect DrawingView::GetObjectExtents(Object * obj)
 
                if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
                        rect.b = -1.0;
+#endif
 
                rect *= a->radius[0];
                rect.Translate(a->p[0]);
@@ -2470,6 +2539,8 @@ void DrawingView::CheckObjectBounds(void)
        {
                Object * obj = (Object *)(*i);
                obj->selected = false;
+               Rect selection = Global::selection;
+               selection.Normalize();
 
                switch (obj->type)
                {
@@ -2478,7 +2549,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;
@@ -2487,8 +2558,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;
@@ -2500,73 +2572,85 @@ void DrawingView::CheckObjectBounds(void)
 
                        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())
+                       // Swap 'em if the span is negative...
+                       if (a->angle[1] < 0)
                        {
-                               double temp = bounds.bottom();
-                               bounds.setBottom(bounds.top());
-                               bounds.setTop(temp);
+                               end = a->angle[0];
+                               start = end + a->angle[1];
                        }
-#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;
 
+#if 1
+                       int quadStart = (int)(a->angle[0] / QTR_TAU);
+                       double qsRemain = a->angle[0] - ((double)quadStart * QTR_TAU);
+                       int numAxes = (int)((a->angle[1] + qsRemain) / QTR_TAU);
+
+                       Rect bounds(sin(start), cos(start), sin(end), cos(end));
+                       const double box[4] = { 1.0, -1.0, -1.0, 1.0 };
+
+                       for(int i=0; i<numAxes; i++)
+                               bounds[(quadStart + i) % 4] = box[(quadStart + i) % 4];
+#else
                        // Adjust the bounds depending on which axes are crossed
                        if ((start < QTR_TAU) && (end > QTR_TAU))
-                               bounds.setTop(1.0);
+                               bounds.t = 1.0;
 
                        if ((start < HALF_TAU) && (end > HALF_TAU))
-                               bounds.setLeft(-1.0);
+                               bounds.l = -1.0;
 
                        if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
-                               bounds.setBottom(-1.0);
+                               bounds.b = -1.0;
 
                        if ((start < TAU) && (end > TAU))
-                               bounds.setRight(1.0);
+                               bounds.r = 1.0;
 
                        if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
-                               bounds.setTop(1.0);
+                               bounds.t = 1.0;
 
                        if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
-                               bounds.setLeft(-1.0);
+                               bounds.l = -1.0;
 
                        if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
-                               bounds.setBottom(-1.0);
+                               bounds.b = -1.0;
+#endif
 
-                       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);
+                       bounds *= a->radius[0];
+                       bounds.Translate(a->p[0]);
 
-                       if (Global::selection.contains(bounds))
+                       if (selection.Contains(bounds))
                                a->selected = true;
 
                        break;
                }
 
+               case OTPolyline:
+               {
+                       Polyline * pl = (Polyline *)obj;
+                       Rect r(((Object *)(pl->points[0]))->p[0]);
+
+                       for(int i=0; i<(pl->points.size()-1); i++)
+                       {
+                               r += ((Object *)(pl->points[i]))->p[0];
+                               r += ((Object *)(pl->points[i + 1]))->p[0];
+                       }
+
+                       if (selection.Contains(r))
+                               pl->selected = true;
+
+                       break;
+               }
+
                case OTText:
                {
                        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;
@@ -2576,7 +2660,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;
@@ -2621,6 +2705,10 @@ bool DrawingView::HitTest(Object * obj, Point point)
 {
        bool needUpdate = false;
 
+       // Make sure we don't hit test stuff on an invisible layer...
+       if (Global::layerHidden[obj->layer] == true)
+               return false;
+
        switch (obj->type)
        {
        case OTLine:
@@ -2959,24 +3047,40 @@ void DrawingView::HandleObjectMovement(Point point)
        case OTLine:
                if (obj->hitPoint[0])
                {
+/*
+N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not* going to work out in any meaningful way, and we should probably make the GUI force these to be mutually exclusive.  Besides, this combined effect already works by dragging the line segment by clicking on it.  :-P
+*/
                        if (Global::fixedLength)
                        {
-                               Vector line = point - obj->p[1];
-                               Vector unit = line.Unit();
+                               Vector unit = Vector::Unit(obj->p[1], point);
                                point = obj->p[1] + (unit * obj->length);
                        }
 
+                       if (Global::fixedAngle)
+                       {
+                               // Calculate the component of the current vector along the
+                               // fixed angle: A_compB = (A • Bu) * Bu  (p[2] has the unit
+                               // vector "Bu".)
+                               double magnitudeAlongB = Vector::Dot(Vector(point - obj->p[1]), obj->p[2]);
+                               point = obj->p[1] + (obj->p[2] * magnitudeAlongB);
+                       }
+
                        obj->p[0] = point;
                }
                else if (obj->hitPoint[1])
                {
                        if (Global::fixedLength)
                        {
-                               Vector line = point - obj->p[0];
-                               Vector unit = line.Unit();
+                               Vector unit = Vector::Unit(obj->p[0], point);
                                point = obj->p[0] + (unit * obj->length);
                        }
 
+                       if (Global::fixedAngle)
+                       {
+                               double magnitudeAlongB = Vector::Dot(Vector(point - obj->p[0]), obj->p[2]);
+                               point = obj->p[0] + (obj->p[2] * magnitudeAlongB);
+                       }
+
                        obj->p[1] = point;
                }
                else if (obj->hitObject)
@@ -2996,7 +3100,7 @@ void DrawingView::HandleObjectMovement(Point point)
                        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);
+                       informativeText = text.arg(obj->radius[0], 0, 'f', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'f', 0);
                }
 
                break;
@@ -3022,14 +3126,14 @@ void DrawingView::HandleObjectMovement(Point point)
                                        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])
                {
@@ -3043,7 +3147,7 @@ void DrawingView::HandleObjectMovement(Point point)
                                        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;
                        }
 
@@ -3053,8 +3157,13 @@ void DrawingView::HandleObjectMovement(Point point)
                        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)
                {
@@ -3065,7 +3174,7 @@ void DrawingView::HandleObjectMovement(Point point)
 
                        obj->radius[0] = Vector::Magnitude(obj->p[0], point);
                        QString text = QObject::tr("Radius: %1");
-                       informativeText = text.arg(obj->radius[0], 0, 'd', 4);
+                       informativeText = text.arg(obj->radius[0], 0, 'f', 4);
                }
 
                break;