]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Added line-to-circle intersection code.
[architektonas] / src / drawingview.cpp
index 9e1b56c784e02698733c85cd205c1bc913db372f..0e563e7e892e3ecba6e563d8af1b7798ee8e05ba 100644 (file)
@@ -7,7 +7,7 @@
 // JLH = James Hammons <jlhamm@acm.org>
 //
 // Who  When        What
-// ---  ----------  -------------------------------------------------------------
+// ---  ----------  ------------------------------------------------------------
 // JLH  03/22/2011  Created this file
 // JLH  09/29/2011  Added middle mouse button panning
 //
@@ -20,6 +20,7 @@
 // STILL TO BE DONE:
 //
 // - Lots of stuff
+// - Layer locking (hiding works)
 //
 
 // Uncomment this for debugging...
@@ -49,8 +50,8 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
        ctrlDown(false),
        gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
-       scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)),
-       gridPixels(0), collided(false)//, toolAction(NULL)
+       scale(1.0), offsetX(-10), offsetY(-10),
+       gridPixels(0), collided(false), hoveringIntersection(false)
 {
 //     document.isTopLevelContainer = true;
 //wtf? doesn't work except in c++11??? document = { 0 };
@@ -84,13 +85,14 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        line->thickness = 2.0;
        line->style = LSDash;
        line->color = 0xFF7F00;
+       line->layer = 0;
        document.objects.push_back(line);
        document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
        document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
        document.objects.push_back(new Circle(Vector(100, 100), 36));
        document.objects.push_back(new Circle(Vector(50, 150), 49));
-       document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
-       document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
+       document.objects.push_back(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
+       document.objects.push_back(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
        document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
        document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
 #endif
@@ -145,20 +147,6 @@ we do! :-)
 }
 
 
-#if 0
-void DrawingView::SetToolActive(Action * action)
-{
-       if (action != NULL)
-       {
-               toolAction = action;
-               connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
-                       SLOT(AddNewObjectToDocument(Object *)));
-               connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
-       }
-}
-#endif
-
-
 void DrawingView::SetGridSize(uint32_t size)
 {
        // Sanity check
@@ -180,7 +168,6 @@ void DrawingView::SetGridSize(uint32_t size)
        pmp.end();
 
        // Set up new BG brush & zoom level (pixels per base unit)
-//     Painter::zoom = gridPixels / gridSpacing;
        Global::zoom = gridPixels / Global::gridSpacing;
        UpdateGridBackground();
 }
@@ -265,18 +252,74 @@ zero; so we do another modulus operation on the result to achieve this.
 }
 
 
-void DrawingView::SetCurrentLayer(int layer)
+//
+// Basically, we just make a single pass through the Container. If the layer #
+// is less than the layer # being deleted, then do nothing. If the layer # is
+// equal to the layer # being deleted, then delete the object. If the layer #
+// is greater than the layer # being deleted, then set the layer # to its layer
+// # - 1.
+//
+void DrawingView::DeleteCurrentLayer(int layer)
 {
-       Global::currentLayer = layer;
-//printf("DrawingView::CurrentLayer = %i\n", layer);
+//printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
+       std::vector<void *>::iterator i = document.objects.begin();
+
+       while (i != document.objects.end())
+       {
+               Object * obj = (Object *)(*i);
+
+               if (obj->layer < layer)
+                       i++;
+               else if (obj->layer == layer)
+               {
+                       document.objects.erase(i);
+                       delete obj;
+               }
+               else
+               {
+                       obj->layer--;
+                       i++;
+               }
+       }
+
+       // We've just done a destructive action, so update the screen!
+       update();
+}
+
+
+void DrawingView::HandleLayerToggle(void)
+{
+       // A layer's visibility was toggled, so update the screen...
+       update();
+}
+
+
+//
+// A layer was moved up or down in the layer list, so we have to swap the
+// document's object's layer numbers in the layers that were swapped.
+//
+void DrawingView::HandleLayerSwap(int layer1, int layer2)
+{
+//printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
+       std::vector<void *>::iterator i;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+
+               if (obj->layer == layer1)
+                       obj->layer = layer2;
+               else if (obj->layer == layer2)
+                       obj->layer = layer1;
+       }
 }
 
 
 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
 {
-       // This is undoing the transform, e.g. going from client coords to local coords.
-       // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
-       // conversion of the y-axis from increasing bottom to top.
+       // This is undoing the transform, e.g. going from client coords to local
+       // coords. In essence, the height - y is height + (y * -1), the (y * -1)
+       // term doing the conversion of the y-axis from increasing bottom to top.
        return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
 }
 
@@ -285,7 +328,7 @@ QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
 {
        // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
        // No voodoo here, it's just grouped wrong to see it. It should be:
-       // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
+       // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
        return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
 }
 
@@ -306,7 +349,11 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
        painter.DrawLine(-16384, 0, 16384, 0);
 
        // Do object rendering...
-       RenderObjects(&painter, document.objects);
+       for(int i=0; i<Global::numLayers; i++)
+       {
+               if (Global::layerHidden[i] == false)
+                       RenderObjects(&painter, document.objects, i);
+       }
 
        // Do tool rendering, if any...
        if (Global::tool)
@@ -324,6 +371,9 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
                painter.DrawRect(Global::selection);
        }
 
+       if (hoveringIntersection)
+               painter.DrawHandle(intersectionPoint);
+
        if (!informativeText.isEmpty())
                painter.DrawInformativeText(informativeText);
 }
@@ -332,7 +382,7 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
 //
 // Renders objects in the passed in vector
 //
-void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
+void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer)
 {
        std::vector<void *>::iterator i;
 
@@ -341,6 +391,10 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
                Object * obj = (Object *)(*i);
                float scaledThickness = Global::scale * obj->thickness;
 
+               // If the object isn't on the current layer being drawn, skip it
+               if (obj->layer != layer)
+                       continue;
+
                if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
                {
                        painter->SetPen(0x00FF00, 2.0, LSSolid);
@@ -376,6 +430,16 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
                        break;
                case OTArc:
                        painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
+                       if (obj->hitPoint[1])
+                               painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
+
+                       if (obj->hitPoint[2])
+                               painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
+
                        break;
                case OTDimension:
                {
@@ -390,19 +454,19 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
 
                        if (d->subtype == DTLinearVert)
                        {
-                               if ((angle < 0) || (angle > PI))
+                               if ((angle < 0) || (angle > HALF_TAU))
                                {
                                        x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
                                        y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
                                        ortho = Vector(1.0, 0);
-                                       angle = PI3_OVER_2;
+                                       angle = THREE_QTR_TAU;
                                }
                                else
                                {
                                        x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
                                        y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
                                        ortho = Vector(-1.0, 0);
-                                       angle = PI_OVER_2;
+                                       angle = QTR_TAU;
                                }
 
                                linePt1.x = linePt2.x = x1;
@@ -410,7 +474,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
                        }
                        else if (d->subtype == DTLinearHorz)
                        {
-                               if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
+                               if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
                                {
                                        x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
                                        y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
@@ -422,7 +486,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
                                        x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
                                        y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
                                        ortho = Vector(0, -1.0);
-                                       angle = PI;
+                                       angle = HALF_TAU;
                                }
 
                                linePt1.y = linePt2.y = y1;
@@ -452,10 +516,9 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
                        painter->DrawLine(p3, p5);
                        painter->DrawLine(p4, p6);
 
-                       // Calculate whether or not the arrowheads are too crowded to put inside
-                       // the extension lines. 9.0 is the length of the arrowhead.
+                       // Calculate whether or not the arrowheads are too crowded to put
+                       // inside the extension lines. 9.0 is the length of the arrowhead.
                        double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
-               //printf("Dimension::Draw(): t = %lf\n", t);
 
                        // On the screen, it's acting like this is actually 58%...
                        // This is correct, we want it to happen at > 50%
@@ -481,9 +544,6 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
                        painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
                        Point ctr = p2 + (Vector(p2, p1) / 2.0);
 
-               #if 0
-                       QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
-               #else
                        QString dimText;
 
                        if (length < 12.0)
@@ -498,7 +558,6 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
                                else
                                        dimText = QString("%1' %2\"").arg(feet).arg(inches);
                        }
-               #endif
 
                        painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
 
@@ -575,6 +634,8 @@ void DrawingView::ToolHandler(int mode, Point p)
                ArcHandler(mode, p);
        else if (Global::tool == TTRotate)
                RotateHandler(mode, p);
+       else if (Global::tool == TTMirror)
+               MirrorHandler(mode, p);
 }
 
 
@@ -655,7 +716,7 @@ void DrawingView::ToolDraw(Painter * painter)
                        double span = angle - toolPoint[2].x;
 
                        if (span < 0)
-                               span += PI_TIMES_2;
+                               span += TAU;
 
                        painter->DrawLine(toolPoint[0], toolPoint[3]);
                        painter->SetBrush(QBrush(Qt::NoBrush));
@@ -677,7 +738,7 @@ void DrawingView::ToolDraw(Painter * painter)
                {
                        if (toolPoint[0] == toolPoint[1])
                                return;
-                       
+
                        painter->DrawLine(toolPoint[0], toolPoint[1]);
                        // Likely we need a tool container for this... (now we do!)
 #if 0
@@ -692,14 +753,49 @@ void DrawingView::ToolDraw(Painter * painter)
 #endif
 
                        double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
-
                        QString text = QChar(0x2221) + QObject::tr(": %1");
                        informativeText = text.arg(absAngle);
 
                        if (ctrlDown)
                                informativeText += " (Copy)";
+               }
+       }
+       else if (Global::tool == TTMirror)
+       {
+               if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
+                       painter->DrawHandle(toolPoint[0]);
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+                       painter->DrawHandle(toolPoint[1]);
+               else
+               {
+                       if (toolPoint[0] == toolPoint[1])
+                               return;
+                       
+                       Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
+//                     painter->DrawLine(toolPoint[0], toolPoint[1]);
+                       painter->DrawLine(mirrorPoint, toolPoint[1]);
+                       // Likely we need a tool container for this... (now we do!)
+#if 0
+                       if (ctrlDown)
+                       {
+                               painter->SetPen(0x00FF00, 2.0, LSSolid);
+                               overrideColor = true;
+                       }
+
+                       RenderObjects(painter, toolObjects);
+                       overrideColor = false;
+#endif
+
+                       double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
+
+                       if (absAngle > 180.0)
+                               absAngle -= 180.0;
+
+                       QString text = QChar(0x2221) + QObject::tr(": %1");
+                       informativeText = text.arg(absAngle);
 
-//                     painter->DrawInformativeText(text);
+                       if (ctrlDown)
+                               informativeText += " (Copy)";
                }
        }
 }
@@ -739,6 +835,7 @@ void DrawingView::LineHandler(int mode, Point p)
                else
                {
                        Line * l = new Line(toolPoint[0], toolPoint[1]);
+                       l->layer = Global::activeLayer;
                        document.objects.push_back(l);
                        toolPoint[0] = toolPoint[1];
                }
@@ -781,6 +878,7 @@ void DrawingView::CircleHandler(int mode, Point p)
                {
                        double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
                        Circle * c = new Circle(toolPoint[0], length);
+                       c->layer = Global::activeLayer;
                        document.objects.push_back(c);
                        toolPoint[0] = toolPoint[1];
                        Global::toolState = TSNone;
@@ -848,9 +946,10 @@ void DrawingView::ArcHandler(int mode, Point p)
                        double span = endAngle - toolPoint[2].x;
 
                        if (span < 0)
-                               span += PI_TIMES_2;
+                               span += TAU;
 
                        Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
+                       arc->layer = Global::activeLayer;
                        document.objects.push_back(arc);
                        Global::toolState = TSNone;
                }
@@ -902,8 +1001,8 @@ void DrawingView::RotateHandler(int mode, Point p)
                                {
                                        obj2->angle[0] = obj.angle[0] + angle;
 
-                                       if (obj2->angle[0] > PI_TIMES_2)
-                                               obj2->angle[0] -= PI_TIMES_2;
+                                       if (obj2->angle[0] > TAU)
+                                               obj2->angle[0] -= TAU;
                                }
                        }
                }
@@ -960,6 +1059,109 @@ void DrawingView::RotateHandler(int mode, Point p)
 }
 
 
+void DrawingView::MirrorHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+               {
+                       toolPoint[0] = p;
+                       SavePointsFrom(select, toolScratch);
+                       Global::toolState = TSPoint1;
+               }
+               else if (Global::toolState == TSPoint1)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+       case ToolMouseMove:
+               if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+               {
+                       toolPoint[1] = p;
+
+                       if (shiftDown)
+                               return;
+
+                       double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
+                       std::vector<void *>::iterator j = select.begin();
+                       std::vector<Object>::iterator i = toolScratch.begin();
+
+                       for(; i!=toolScratch.end(); i++, j++)
+                       {
+                               Object obj = *i;
+                               Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
+                               Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
+                               Object * obj2 = (Object *)(*j);
+                               obj2->p[0] = p1;
+                               obj2->p[1] = p2;
+
+                               if (obj.type == OTArc)
+                               {
+                                       // This is 2*mirror angle - obj angle - obj span
+                                       obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
+
+                                       if (obj2->angle[0] > TAU)
+                                               obj2->angle[0] -= TAU;
+                               }
+                       }
+               }
+
+               break;
+       case ToolMouseUp:
+               if (Global::toolState == TSPoint1)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       // Either we're finished with our rotate, or we're stamping a copy.
+                       if (ctrlDown)
+                       {
+                               // Stamp a copy of the selection at the current rotation & bail
+                               std::vector<void *> temp;
+                               CopyObjects(select, temp);
+                               ClearSelected(temp);
+                               AddObjectsTo(document.objects, temp);
+                               RestorePointsTo(select, toolScratch);
+                               return;
+                       }
+
+                       toolPoint[0] = p;
+                       Global::toolState = TSPoint1;
+                       SavePointsFrom(select, toolScratch);
+               }
+
+               break;
+       case ToolKeyDown:
+               // Reset the selection if shift held down...
+               if (shiftDown)
+                       RestorePointsTo(select, toolScratch);
+
+               break;
+       case ToolKeyUp:
+               // Reset selection when key is let up
+               if (!shiftDown)
+                       MirrorHandler(ToolMouseMove, toolPoint[1]);
+
+               break;
+       case ToolCleanup:
+               RestorePointsTo(select, toolScratch);
+       }
+}
+
+
 void DrawingView::mousePressEvent(QMouseEvent * event)
 {
        if (event->button() == Qt::LeftButton)
@@ -1024,6 +1226,7 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        Global::selection.setBottomRight(QPointF(point.x, point.y));
        // Only needs to be done here, as mouse down is always preceded by movement
        Global::snapPointIsValid = false;
+       hoveringIntersection = false;
 
        // Scrolling...
        if (event->buttons() & Qt::MiddleButton)
@@ -1066,6 +1269,50 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        // Do object hit testing...
        bool needUpdate = HitTestObjects(point);
 
+       // Check for multi-hover...
+       if (numHovered > 1)
+       {
+               GetHovered(hover);
+
+//             double t, u;
+//             int numIntersecting = Geometry::Intersects((Object *)hover[0], (Object *)hover[1], &t, &u);
+               Geometry::Intersects((Object *)hover[0], (Object *)hover[1]);
+               int numIntersecting = Global::numIntersectParams;
+               double t = Global::intersectParam[0];
+               double u = Global::intersectParam[1];
+
+               if (numIntersecting > 0)
+               {
+                       Vector v1 = Geometry::GetPointForParameter((Object *)hover[0], t);
+                       Vector v2 = Geometry::GetPointForParameter((Object *)hover[1], u);
+                       QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
+                       informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
+
+                       hoveringIntersection = true;
+                       intersectionPoint = v1;
+               }
+
+               numIntersecting = Global::numIntersectPoints;
+
+               if (numIntersecting > 0)
+               {
+                       Vector v1 = Global::intersectPoint[0];
+
+                       if (numIntersecting == 2)
+                       {
+                               Vector v2 = Global::intersectPoint[1];
+
+                               if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
+                                       v1 = v2;
+                       }
+
+                       QString text = tr("Intersection <%1, %2>");
+                       informativeText = text.arg(v1.x).arg(v1.y);
+                       hoveringIntersection = true;
+                       intersectionPoint = v1;
+               }
+       }
+
        // Do tool handling, if any are active...
        if (Global::tool)
        {
@@ -1283,28 +1530,28 @@ void DrawingView::CheckObjectBounds(void)
 
                        // If the end of the arc is before the beginning, add 360 degrees to it
                        if (end < start)
-                               end += 2.0 * PI;
+                               end += TAU;
 
                        // Adjust the bounds depending on which axes are crossed
-                       if ((start < PI_OVER_2) && (end > PI_OVER_2))
+                       if ((start < QTR_TAU) && (end > QTR_TAU))
                                bounds.setTop(1.0);
 
-                       if ((start < PI) && (end > PI))
+                       if ((start < HALF_TAU) && (end > HALF_TAU))
                                bounds.setLeft(-1.0);
 
-                       if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
+                       if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
                                bounds.setBottom(-1.0);
 
-                       if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
+                       if ((start < TAU) && (end > TAU))
                                bounds.setRight(1.0);
 
-                       if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
+                       if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
                                bounds.setTop(1.0);
 
-                       if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
+                       if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
                                bounds.setLeft(-1.0);
 
-                       if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
+                       if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
                                bounds.setBottom(-1.0);
 
                        bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
@@ -1391,28 +1638,37 @@ bool DrawingView::HitTestObjects(Point point)
                }
                case OTArc:
                {
-                       bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
-                       obj->hitPoint[0] = obj->hitObject = false;
+                       bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
+                       obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
                        double length = Vector::Magnitude(obj->p[0], point);
                        double angle = Vector::Angle(obj->p[0], point);
 
                        // Make sure we get the angle in the correct spot
                        if (angle < obj->angle[0])
-                               angle += PI_TIMES_2;
+                               angle += TAU;
 
                        // 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...
+                       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]);
+                       double length2 = Vector::Magnitude(point, handle1);
+                       double length3 = Vector::Magnitude(point, handle2);
 
                        if ((length * Global::zoom) < 8.0)
                                obj->hitPoint[0] = true;
+                       else if ((length2 * Global::zoom) < 8.0)
+                               obj->hitPoint[1] = true;
+                       else if ((length3 * Global::zoom) < 8.0)
+                               obj->hitPoint[2] = true;
                        else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
                                obj->hitObject = true;
 
-                       obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+                       obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
 
-                       if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
+                       if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
                                needUpdate = true;
 
                        break;
@@ -1467,6 +1723,74 @@ void DrawingView::HandleObjectMovement(Point point)
                        informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
                }
 
+               break;
+       case OTArc:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitPoint[1])
+               {
+                       // Change the Arc's span (handle #1)
+                       if (shiftDown)
+                       {
+                               double angle = Vector::Angle(obj->p[0], point);
+                               double delta = angle - obj->angle[0];
+
+                               if (delta < 0)
+                                       delta += TAU;
+
+                               obj->angle[1] -= delta;
+                               obj->angle[0] = angle;
+
+                               if (obj->angle[1] < 0)
+                                       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);
+                               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);
+               }
+               else if (obj->hitPoint[2])
+               {
+                       // Change the Arc's span (handle #2)
+                       if (shiftDown)
+                       {
+                               double angle = Vector::Angle(obj->p[0], point);
+                               obj->angle[1] = angle - obj->angle[0];
+
+                               if (obj->angle[1] < 0)
+                                       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);
+                               return;
+                       }
+
+                       double angle = Vector::Angle(obj->p[0], point);
+                       obj->angle[0] = angle - obj->angle[1];
+
+                       if (obj->angle[0] < 0)
+                               obj->angle[0] += 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);
+               }
+               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, 'd', 4);
+               }
+
                break;
        default:
                break;