]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Added object pane, grouping, load/save functionality.
[architektonas] / src / drawingview.cpp
index 62f240a1c931a32c5b49f4cf77e79f872c89136c..1fd0482a83d266c0e313081f878ded884655b649 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
 //
 
 #define BACKGROUND_MAX_SIZE    512
 
-// Class variable
-//Container DrawingView::document(Vector(0, 0));
-
 
 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        // The value in the settings file will override this.
-       useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
+       useAntialiasing(true), /*numSelected(0),*/ numHovered(0), shiftDown(false),
        ctrlDown(false),
        gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
-       scale(1.0), offsetX(-10), offsetY(-10),
+       scale(1.0), offsetX(-10), offsetY(-10), document(true),
        gridPixels(0), collided(false), hoveringIntersection(false)
 {
-//     document.isTopLevelContainer = true;
 //wtf? doesn't work except in c++11??? document = { 0 };
        setBackgroundRole(QPalette::Base);
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@@ -91,8 +87,8 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        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
@@ -147,20 +143,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
@@ -182,7 +164,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();
 }
@@ -267,14 +248,6 @@ zero; so we do another modulus operation on the result to achieve this.
 }
 
 
-void DrawingView::SetCurrentLayer(int /*layer*/)
-{
-//Not needed anymore...
-//     Global::currentLayer = layer;
-//printf("DrawingView::CurrentLayer = %i\n", 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
@@ -340,9 +313,9 @@ void DrawingView::HandleLayerSwap(int layer1, int layer2)
 
 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()));
 }
 
@@ -351,7 +324,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);
 }
 
@@ -405,7 +378,7 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
 //
 // Renders objects in the passed in vector
 //
-void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer)
+void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer, bool ignoreLayer/*= false*/)
 {
        std::vector<void *>::iterator i;
 
@@ -415,7 +388,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                float scaledThickness = Global::scale * obj->thickness;
 
                // If the object isn't on the current layer being drawn, skip it
-               if (obj->layer != layer)
+               if (!ignoreLayer && (obj->layer != layer))
                        continue;
 
                if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
@@ -477,19 +450,19 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
 
                        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;
@@ -497,7 +470,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                        }
                        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);
@@ -509,7 +482,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                        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;
@@ -539,10 +512,9 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                        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%
@@ -568,9 +540,6 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                        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)
@@ -585,7 +554,6 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                else
                                        dimText = QString("%1' %2\"").arg(feet).arg(inches);
                        }
-               #endif
 
                        painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
 
@@ -597,6 +565,30 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                        painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
                        break;
                }
+               case OTSpline:
+               {
+                       break;
+               }
+               case OTPolygon:
+               {
+                       break;
+               }
+               case OTContainer:
+               {
+                       // Containers require recursive rendering...
+                       Container * c = (Container *)obj;
+                       RenderObjects(painter, (*c).objects, layer);
+
+//printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
+                       // Containers also have special indicators showing they are selected
+                       if (c->selected || c->hitObject)
+                       {
+                               Rect r = GetObjectExtents(obj);
+                               painter->DrawRectCorners(r);
+                       }
+
+                       break;
+               }
                default:
                        break;
                }
@@ -703,13 +695,12 @@ void DrawingView::ToolDraw(Painter * painter)
                }
                else
                {
+                       painter->DrawCross(toolPoint[0]);
                        double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
-//                     painter->DrawLine(toolPoint[0], toolPoint[1]);
-//                     painter->DrawHandle(toolPoint[1]);
                        painter->SetBrush(QBrush(Qt::NoBrush));
                        painter->DrawEllipse(toolPoint[0], length, length);
-                       QString text = tr("Radius: %1 in.");//\n") + QChar(0x2221) + tr(": %2");
-                       informativeText = text.arg(length);//.arg(absAngle);
+                       QString text = tr("Radius: %1 in.");
+                       informativeText = text.arg(length);
                }
        }
        else if (Global::tool == TTArc)
@@ -744,7 +735,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));
@@ -768,17 +759,6 @@ void DrawingView::ToolDraw(Painter * painter)
                                return;
 
                        painter->DrawLine(toolPoint[0], 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;
                        QString text = QChar(0x2221) + QObject::tr(": %1");
@@ -800,19 +780,7 @@ void DrawingView::ToolDraw(Painter * painter)
                                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;
 
@@ -974,7 +942,7 @@ 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;
@@ -1029,8 +997,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;
                                }
                        }
                }
@@ -1132,8 +1100,8 @@ void DrawingView::MirrorHandler(int mode, Point p)
                                        // 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] > PI_TIMES_2)
-                                               obj2->angle[0] -= PI_TIMES_2;
+                                       if (obj2->angle[0] > TAU)
+                                               obj2->angle[0] -= TAU;
                                }
                        }
                }
@@ -1199,7 +1167,9 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                // Handle tool processing, if any
                if (Global::tool)
                {
-                       if (Global::snapToGrid)
+                       if (hoveringIntersection)
+                               point = intersectionPoint;
+                       else if (Global::snapToGrid)
                                point = SnapPointToGrid(point);
 
                        //Also, may want to figure out if hovering over a snap point on an object,
@@ -1227,7 +1197,9 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
 
                        // Needed for grab & moving objects
                        // We do it *after*... why? (doesn't seem to confer any advantage...)
-                       if (Global::snapToGrid)
+                       if (hoveringIntersection)
+                               oldPoint = intersectionPoint;
+                       else if (Global::snapToGrid)
                                oldPoint = SnapPointToGrid(point);
 
                        return;
@@ -1260,8 +1232,9 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        if (event->buttons() & Qt::MiddleButton)
        {
                point = Vector(event->x(), event->y());
-               // Since we're using Qt coords for scrolling, we have to adjust them here to
-               // conform to Cartesian coords, since the origin is using Cartesian. :-)
+               // Since we're using Qt coords for scrolling, we have to adjust them
+               // here to conform to Cartesian coords, since the origin is using
+               // Cartesian. :-)
                Vector delta(oldPoint, point);
                delta /= Global::zoom;
                delta.y = -delta.y;
@@ -1285,7 +1258,9 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        // Handle object movement (left button down & over an object)
        if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
        {
-               if (Global::snapToGrid)
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
                        point = SnapPointToGrid(point);
 
                HandleObjectMovement(point);
@@ -1301,9 +1276,10 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        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)
                {
@@ -1315,12 +1291,52 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                        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;
+               }
        }
 
+//this doesn't work down here for some reason... :-P
+//could be because the object being moved is part of the intersection, and this is screwing things up. In which case, we need to exclude the moving object somehow from the hit test function...
+#if 0
+       // Handle object movement (left button down & over an object)
+       if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
+       {
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
+                       point = SnapPointToGrid(point);
+
+               HandleObjectMovement(point);
+               update();
+               oldPoint = point;
+               return;
+       }
+#endif
+
        // Do tool handling, if any are active...
        if (Global::tool)
        {
-               if (Global::snapToGrid)
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
                        point = SnapPointToGrid(point);
 
                ToolHandler(ToolMouseMove, point);
@@ -1339,8 +1355,8 @@ void DrawingView::mouseReleaseEvent(QMouseEvent * event)
        if (event->button() == Qt::LeftButton)
        {
 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
-//could set it up to use the document's update function (assumes that all object updates
-//are being reported correctly:
+//could set it up to use the document's update function (assumes that all object
+//updates are being reported correctly:
 //             if (document.NeedsUpdate())
                // Do an update if collided with at least *one* object in the document
 //             if (collided)
@@ -1472,10 +1488,84 @@ Point DrawingView::SnapPointToGrid(Point point)
 }
 
 
+Rect DrawingView::GetObjectExtents(Object * obj)
+{
+       // Default to empty rect, if object checks below fail for some reason
+       Rect rect;
+
+       switch (obj->type)
+       {
+       case OTLine:
+       {
+               rect = Rect(obj->p[0], obj->p[1]);
+               break;
+       }
+       case OTCircle:
+       {
+               rect = Rect(obj->p[0], obj->p[0]);
+               rect.Expand(obj->radius[0]);
+               break;
+       }
+       case OTArc:
+       {
+               Arc * a = (Arc *)obj;
+
+               double start = a->angle[0];
+               double end = start + a->angle[1];
+               rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
+
+               // If the end of the arc is before the beginning, add 360 degrees to it
+               if (end < start)
+                       end += TAU;
+
+               // Adjust the bounds depending on which axes are crossed
+               if ((start < QTR_TAU) && (end > QTR_TAU))
+                       rect.t = 1.0;
+
+               if ((start < HALF_TAU) && (end > HALF_TAU))
+                       rect.l = -1.0;
+
+               if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
+                       rect.b = -1.0;
+
+               if ((start < TAU) && (end > TAU))
+                       rect.r = 1.0;
+
+               if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
+                       rect.t = 1.0;
+
+               if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
+                       rect.l = -1.0;
+
+               if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
+                       rect.b = -1.0;
+
+               rect *= a->radius[0];
+               rect.Translate(a->p[0]);
+
+               break;
+       }
+       case OTContainer:
+       {
+               Container * c = (Container *)obj;
+               std::vector<void *>::iterator i = c->objects.begin();
+               rect = GetObjectExtents((Object *)*i);
+               i++;
+
+               for(; i!=c->objects.end(); i++)
+                       rect |= GetObjectExtents((Object *)*i);
+       }
+       default:
+               break;
+       }
+
+       return rect;
+}
+
+
 void DrawingView::CheckObjectBounds(void)
 {
        std::vector<void *>::iterator i;
-       numSelected = 0;
 
        for(i=document.objects.begin(); i!=document.objects.end(); i++)
        {
@@ -1534,28 +1624,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]));
@@ -1570,9 +1660,6 @@ void DrawingView::CheckObjectBounds(void)
                default:
                        break;
                }
-
-               if (obj->selected)
-                       numSelected++;
        }
 }
 
@@ -1587,6 +1674,9 @@ bool DrawingView::HitTestObjects(Point point)
        {
                Object * obj = (Object *)(*i);
 
+               if (HitTest(obj, point))
+                       needUpdate = true;
+#if 0
                switch (obj->type)
                {
                case OTLine:
@@ -1649,7 +1739,7 @@ bool DrawingView::HitTestObjects(Point 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];
@@ -1677,15 +1767,154 @@ bool DrawingView::HitTestObjects(Point point)
 
                        break;
                }
+               case OTContainer:
+               {
+                       // Containers must be recursively tested...
+                       Container * c = (Container *)obj;
+                       std::vector<void *>::iterator i;
+
+                       for(i=c->objects.begin(); i!=c->objects.end(); i++)
+                       {
+                               
+                       }
+               }
                default:
                        break;
                }
+#endif
 
                if (obj->hovered)
-//             {
+               {
                        numHovered++;
 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
-//             }
+                       emit(ObjectHovered(obj));
+               }
+       }
+
+       return needUpdate;
+}
+
+
+bool DrawingView::HitTest(Object * obj, Point point)
+{
+       bool needUpdate = false;
+
+       switch (obj->type)
+       {
+       case OTLine:
+       {
+               bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
+               Vector lineSegment = obj->p[1] - obj->p[0];
+               Vector v1 = point - obj->p[0];
+               Vector v2 = point - obj->p[1];
+               double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
+               double distance;
+
+               if (t < 0.0)
+                       distance = v1.Magnitude();
+               else if (t > 1.0)
+                       distance = v2.Magnitude();
+               else
+                       // distance = ?Det?(ls, v1) / |ls|
+                       distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
+                               / lineSegment.Magnitude());
+
+               if ((v1.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((v2.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[1] = true;
+               else if ((distance * Global::zoom) < 5.0)
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+
+               if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTCircle:
+       {
+               bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitObject = false;
+               double length = Vector::Magnitude(obj->p[0], point);
+
+               if ((length * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+
+               if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTArc:
+       {
+               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 += 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->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+
+               if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTContainer:
+       {
+               // Containers must be recursively tested...
+               Container * c = (Container *)obj;
+               c->hitObject = false;
+               c->hovered = false;
+               std::vector<void *>::iterator i;
+
+               for(i=c->objects.begin(); i!=c->objects.end(); i++)
+               {
+                       Object * cObj = (Object *)*i;
+
+                       if (HitTest(cObj, point))
+                               needUpdate = true;
+
+                       if (cObj->hitObject == true)
+                               c->hitObject = true;
+
+                       if (cObj->hovered == true)
+                               c->hovered = true;
+               }
+
+               break;
+       }
+       default:
+               break;
        }
 
        return needUpdate;
@@ -1740,13 +1969,13 @@ void DrawingView::HandleObjectMovement(Point point)
                                double delta = angle - obj->angle[0];
 
                                if (delta < 0)
-                                       delta += PI_TIMES_2;
+                                       delta += TAU;
 
                                obj->angle[1] -= delta;
                                obj->angle[0] = angle;
 
                                if (obj->angle[1] < 0)
-                                       obj->angle[1] += PI_TIMES_2;
+                                       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);
@@ -1767,7 +1996,7 @@ void DrawingView::HandleObjectMovement(Point point)
                                obj->angle[1] = angle - obj->angle[0];
 
                                if (obj->angle[1] < 0)
-                                       obj->angle[1] += PI_TIMES_2;
+                                       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);
@@ -1778,7 +2007,7 @@ void DrawingView::HandleObjectMovement(Point point)
                        obj->angle[0] = angle - obj->angle[1];
 
                        if (obj->angle[0] < 0)
-                               obj->angle[0] += PI_TIMES_2;
+                               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);
@@ -1795,90 +2024,15 @@ void DrawingView::HandleObjectMovement(Point point)
                        informativeText = text.arg(obj->radius[0], 0, 'd', 4);
                }
 
+               break;
+       case OTContainer:
+               // This is shitty, but works for now until I can code up something
+               // nicer :-)
+               TranslateObject(obj, delta);
+
                break;
        default:
                break;
        }
 }
 
-
-
-#if 0
-       // This returns true if we've moved over an object...
-       if (document.PointerMoved(point)) // <-- This
-       // This is where the object would do automagic dragging & shit. Since we don't
-       // do that anymore, we need a strategy to handle it.
-       {
-
-/*
-Now objects handle mouse move snapping as well. The code below mainly works only
-for tools; we need to fix it so that objects work as well...
-
-There's a problem with the object point snapping in that it's dependent on the
-order of the objects in the document. Most likely this is because it counts the
-selected object last and thus fucks up the algorithm. Need to fix this...
-
-
-*/
-               // Do object snapping here. Grid snapping on mouse down is done in the
-               // objects themselves, only because we have to hit test the raw point,
-               // not the snapped point. There has to be a better way...!
-               if (document.penultimateObjectHovered)
-               {
-                       // Two objects are hovered, see if we have an intersection point
-                       if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
-                       {
-                               double t;
-                               int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
-
-                               if (n == 1)
-                               {
-                                       Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
-                                       Global::snapPointIsValid = true;
-                               }
-                       }
-                       else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
-                       {
-                               Point p1, p2;
-                               int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
-
-                               if (n == 1)
-                               {
-                                       Global::snapPoint = p1;
-                                       Global::snapPointIsValid = true;
-                               }
-                               else if (n == 2)
-                               {
-                                       double d1 = Vector(point, p1).Magnitude();
-                                       double d2 = Vector(point, p2).Magnitude();
-
-                                       if (d1 < d2)
-                                               Global::snapPoint = p1;
-                                       else
-                                               Global::snapPoint = p2;
-
-                                       Global::snapPointIsValid = true;
-                               }
-                       }
-               }
-//             else
-//             {
-                       // Otherwise, it was a single object hovered...
-//             }
-       }
-
-       if (toolAction)
-       {
-               if (Global::snapToGrid)
-                       point = Global::SnapPointToGrid(point);
-
-               // We always snap to object points, and they take precendence over
-               // grid points...
-               if (Global::snapPointIsValid)
-                       point = Global::snapPoint;
-
-               toolAction->MouseMoved(point);
-       }
-#else
-#endif
-