]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Added object pane, grouping, load/save functionality.
[architektonas] / src / drawingview.cpp
index 0e563e7e892e3ecba6e563d8af1b7798ee8e05ba..1fd0482a83d266c0e313081f878ded884655b649 100644 (file)
 
 #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);
@@ -382,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;
 
@@ -392,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)
@@ -569,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;
                }
@@ -675,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)
@@ -740,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");
@@ -772,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;
 
@@ -1171,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,
@@ -1199,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;
@@ -1232,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;
@@ -1257,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);
@@ -1273,9 +1276,6 @@ 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];
@@ -1313,10 +1313,30 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                }
        }
 
+//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);
@@ -1335,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)
@@ -1468,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++)
        {
@@ -1566,9 +1660,6 @@ void DrawingView::CheckObjectBounds(void)
                default:
                        break;
                }
-
-               if (obj->selected)
-                       numSelected++;
        }
 }
 
@@ -1583,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:
@@ -1673,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;
@@ -1791,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
-