]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Preliminary support for Polylines.
[architektonas] / src / drawingview.cpp
index 4ae021c6da555d0091cda947e9296638efb3e9c4..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;
 
@@ -193,7 +195,6 @@ void DrawingView::DrawSubGrid(Painter * painter, uint32_t color, double step, Ve
 //
 void DrawingView::DeleteCurrentLayer(int layer)
 {
-//printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
        VPVectorIter i = document.objects.begin();
 
        while (i != document.objects.end())
@@ -230,7 +231,6 @@ void DrawingView::HandleLayerToggle(void)
 //
 void DrawingView::HandleLayerSwap(int layer1, int layer2)
 {
-//printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
        HandleLayerSwap(layer1, layer2, document.objects);
 }
 
@@ -253,42 +253,6 @@ void DrawingView::HandleLayerSwap(int layer1, int layer2, VPVector & v)
        }
 }
 
-void DrawingView::HandlePenWidth(float width)
-{
-       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
-       {
-               Object * obj = (Object *)(*i);
-               obj->thickness = width;
-       }
-
-       supressSelected = true;
-       update();
-}
-
-void DrawingView::HandlePenStyle(int style)
-{
-       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
-       {
-               Object * obj = (Object *)(*i);
-               obj->style = style;
-       }
-
-       supressSelected = true;
-       update();
-}
-
-void DrawingView::HandlePenColor(uint32_t color)
-{
-       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
-       {
-               Object * obj = (Object *)(*i);
-               obj->color = color;
-       }
-
-       supressSelected = true;
-       update();
-}
-
 void DrawingView::HandlePenStamp(QAction * action)
 {
        PenWidget * pw = (PenWidget *)action->parentWidget();
@@ -340,7 +304,6 @@ QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
 
 void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
 {
-//     printf("DrawingView::focusOutEvent()...\n");
        // Make sure all modkeys being held are marked as released when the app
        // loses focus (N.B.: This only works because the app sets the focus policy
        // of this object to something other than Qt::NoFocus)
@@ -355,9 +318,6 @@ void DrawingView::focusInEvent(QFocusEvent * /*event*/)
                setCursor(curMarker);
        else if (Global::penDropper)
                setCursor(curDropper);
-//FocusOut already set this...
-//     else
-//             setCursor(Qt::ArrowCursor);
 }
 
 void DrawingView::paintEvent(QPaintEvent * /*event*/)
@@ -399,8 +359,8 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
        // Do selection rectangle rendering, if any
        if (Global::selectionInProgress)
        {
-               painter.SetPen(QPen(QColor(255, 127, 0, 255)));
-               painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
+               painter.SetPen(QPen(QColor(0xFF, 0x7F, 0x00, 0xFF)));
+               painter.SetBrush(QBrush(QColor(0xFF, 0x7F, 0x00, 0x64)));
                painter.DrawRect(Global::selection);
        }
 
@@ -495,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;
@@ -598,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().
@@ -685,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:
                {
@@ -723,16 +702,45 @@ Where is the text offset?  It looks like it's drawing in the center, but obvious
 }
 
 //
-// This toggles the selection being hovered (typically, only 1 object)
+// This toggles the selection being hovered (typically, only 1 object).  We
+// toggle because the CTRL key might be held, in which case, we want to
+// deselect a selected object.
 //
-void DrawingView::AddHoveredToSelection(void)
+void DrawingView::HandleSelectionClick(VPVector & v)
 {
-       for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
+       if (ctrlDown)
+       {
+               for(VPVectorIter i=v.begin(); i!=v.end(); i++)
+               {
+                       Object * obj = (Object *)(*i);
+
+                       if (obj->hovered)
+                               obj->selected = !obj->selected;
+               }
+
+               return;
+       }
+
+       for(VPVectorIter i=v.begin(); i!=v.end(); i++)
+               ((Object *)(*i))->selected = false;
+
+       // Check if the hover changed, and if so, reset the selection stack
+       if (oldHover.size() != v.size())
        {
-               if (((Object *)(*i))->hovered)
-//                     ((Object *)(*i))->selected = true;
-                       ((Object *)(*i))->selected = !((Object *)(*i))->selected;
+               oldHover = v;
+               currentSelect = 0;
        }
+       else
+       {
+               // Select next object in the stack under the cursor
+               currentSelect++;
+
+               if (currentSelect >= v.size())
+                       currentSelect = 0;
+       }
+
+       dragged = (Object *)v[currentSelect];
+       dragged->selected = true;
 }
 
 VPVector DrawingView::GetSelection(void)
@@ -822,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]);
@@ -843,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]);
@@ -863,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]);
@@ -907,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)
@@ -928,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)
@@ -954,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]);
@@ -976,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...
@@ -987,9 +1002,43 @@ 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);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+
+                       double length = Vector::Magnitude(toolObj[0]->p[0], toolPoint[0]);
+                       bool inside = (length >= toolObj[0]->radius[0] ? false : true);
+
+                       for(int i=1; i<=Global::parallelNum; i++)
+                       {
+                               if (toolObj[0]->type == OTLine)
+                               {
+                                       painter->DrawLine(toolObj[0]->p[0] + (toolPoint[0] * Global::parallelDist * (double)i), toolObj[0]->p[1] + (toolPoint[0] * Global::parallelDist * (double)i));
+                               }
+                               else if ((toolObj[0]->type == OTCircle) || (toolObj[0]->type == OTArc))
+                               {
+                                       double radius = toolObj[0]->radius[0] + ((double)i * Global::parallelDist * (inside ? -1.0 : 1.0));
+
+                                       if (radius > 0)
+                                       {
+                                               if (toolObj[0]->type == OTCircle)
+                                                       painter->DrawEllipse(toolObj[0]->p[0], radius, radius);
+                                               else
+                                                       painter->DrawArc(toolObj[0]->p[0], radius, toolObj[0]->angle[0], toolObj[0]->angle[1]);
+                                       }
+                               }
+                       }
+               }
+
+               break;
+
+       default:
+               break;
        }
 }
 
@@ -1770,14 +1819,85 @@ void DrawingView::TrimHandler(int mode, Point p)
        }
 }
 
-void DrawingView::ParallelHandler(int mode, Point /*p*/)
+void DrawingView::ParallelHandler(int mode, Point p)
 {
        switch (mode)
        {
        case ToolMouseDown:
+               if (numHovered == 1)
+               {
+                       // New selection made...
+                       VPVector hover = GetHovered();
+                       toolObj[0] = (Object *)hover[0];
+                       Global::toolState = TSNone;
+               }
+               else if ((numHovered == 0) && (toolObj[0] != NULL))
+               {
+                       double length = Vector::Magnitude(toolObj[0]->p[0], toolPoint[0]);
+                       bool inside = (length >= toolObj[0]->radius[0] ? false : true);
+
+                       // Stamp out new parallel object(s)...
+                       for(int i=1; i<=Global::parallelNum; i++)
+                       {
+                               if (toolObj[0]->type == OTLine)
+                               {
+                                       Line * l = new Line(toolObj[0]->p[0] + (toolPoint[0] * Global::parallelDist * (double)i), toolObj[0]->p[1] + (toolPoint[0] * Global::parallelDist * (double)i), Global::penWidth, Global::penColor, Global::penStyle);
+                                       // Should probably have a user selection for this whether it goes into the selected objects layer or the global layer...
+                                       l->layer = toolObj[0]->layer;
+                                       document.objects.push_back(l);
+                               }
+                               else if (toolObj[0]->type == OTCircle)
+                               {
+                                       double radius = toolObj[0]->radius[0] + ((double)i * Global::parallelDist * (inside ?  -1.0 : 1.0));
+
+                                       if (radius > 0)
+                                       {
+                                               Circle * c = new Circle(toolObj[0]->p[0], radius, Global::penWidth, Global::penColor, Global::penStyle);
+                                               c->layer = toolObj[0]->layer;
+                                               document.objects.push_back(c);
+                                       }
+                               }
+                               else if (toolObj[0]->type == OTArc)
+                               {
+                                       double radius = toolObj[0]->radius[0] + ((double)i * Global::parallelDist * (inside ?  -1.0 : 1.0));
+
+                                       if (radius > 0)
+                                       {
+                                               Arc * a = new Arc(toolObj[0]->p[0], radius, toolObj[0]->angle[0], toolObj[0]->angle[1], Global::penWidth, Global::penColor, Global::penStyle);
+                                               a->layer = toolObj[0]->layer;
+                                               document.objects.push_back(a);
+                                       }
+                               }
+                       }
+
+                       // Then reset the state
+                       toolObj[0]->selected = false;
+                       toolObj[0] = NULL;
+                       Global::toolState = TSNone;
+               }
+
                break;
 
        case ToolMouseMove:
+               if ((numHovered == 0) && toolObj[0] != NULL)
+                       Global::toolState = TSPoint1;
+               else
+                       Global::toolState = TSNone;
+
+               if (Global::toolState == TSPoint1)
+               {
+                       // Figure out which side of the object we're on, and draw the preview on that side...
+                       if (toolObj[0]->type == OTLine)
+                       {
+                               Vector normal = Geometry::GetNormalOfPointAndLine(p, (Line *)toolObj[0]);
+                               toolPoint[0] = normal;
+                       }
+                       else if ((toolObj[0]->type == OTCircle) || (toolObj[0]->type == OTArc))
+                       {
+                               toolPoint[0] = p;
+                       }
+               }
+
                break;
 
        case ToolMouseUp:
@@ -1798,7 +1918,6 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
 {
        if (event->button() == Qt::LeftButton)
        {
-//printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
                Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
 
                // Handle tool processing, if any
@@ -1819,17 +1938,11 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                if (!ctrlDown)
                        ClearSelected(document.objects);
 
-               // If any objects are being hovered on click, add them to the selection
-               // & return
+               // If any objects are being hovered on click, deal with 'em
                if (numHovered > 0)
                {
-                       AddHoveredToSelection();
-                       update();       // needed??
-//                     GetHovered(hover);      // prolly needed
                        VPVector hover2 = GetHovered();
                        dragged = (Object *)hover2[0];
-                       draggingObject = true;
-//printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
 
                        // Alert the pen widget
                        if (Global::penDropper)
@@ -1838,26 +1951,27 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                                Global::penWidth = dragged->thickness;
                                Global::penStyle = dragged->style;
                                emit ObjectSelected(dragged);
-                               ClearSelected(document.objects);
                                return;
                        }
-
-                       if (Global::penStamp)
+                       else if (Global::penStamp)
                        {
                                dragged->color = Global::penColor;
                                dragged->thickness = Global::penWidth;
                                dragged->style = Global::penStyle;
                                return;
                        }
-
-                       // See if anything is using just a straight click on a handle
-                       if (HandleObjectClicked())
+                       // See if anything is using just a straight click on a custom
+                       // object handle (like Dimension objects)
+                       else if (HandleObjectClicked())
                        {
-                               draggingObject = false;
                                update();
                                return;
                        }
 
+                       draggingObject = true;
+                       HandleSelectionClick(hover2);
+                       update();       // needed??
+
                        // Needed for grab & moving objects
                        // We do it *after*... why? (doesn't seem to confer any advantage...)
                        if (hoveringIntersection)
@@ -1871,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)
@@ -1888,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)
@@ -1911,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;
@@ -2324,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;
@@ -2351,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]);
@@ -2390,6 +2539,8 @@ void DrawingView::CheckObjectBounds(void)
        {
                Object * obj = (Object *)(*i);
                obj->selected = false;
+               Rect selection = Global::selection;
+               selection.Normalize();
 
                switch (obj->type)
                {
@@ -2398,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;
@@ -2407,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;
@@ -2420,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;
@@ -2496,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;
@@ -2541,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:
@@ -2879,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)
@@ -2916,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;
@@ -2942,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])
                {
@@ -2963,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;
                        }
 
@@ -2973,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)
                {
@@ -2985,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;