]> Shamusworld >> Repos - architektonas/commitdiff
Added object pane, grouping, load/save functionality.
authorShamus Hammons <jlhamm@acm.org>
Sun, 5 Feb 2017 02:07:58 +0000 (20:07 -0600)
committerShamus Hammons <jlhamm@acm.org>
Sun, 5 Feb 2017 02:07:58 +0000 (20:07 -0600)
There's probably a bunch more things that I'm forgetting, but that's
what happens when you code like a sumbitch and then forget to commit it.
:-P

16 files changed:
architektonas.pro
src/applicationwindow.cpp
src/drawingview.cpp
src/drawingview.h
src/fileio.cpp
src/fileio.h
src/geometry.cpp
src/objectwidget.cpp [new file with mode: 0644]
src/objectwidget.h [new file with mode: 0644]
src/painter.cpp
src/painter.h
src/rect.cpp [new file with mode: 0644]
src/rect.h [new file with mode: 0644]
src/structs.h
src/utils.cpp
src/utils.h

index a82f9a045b1e2672035fec0a7097b982b1918007..5feda569c159da471b70bb5a537ec7eac0907e78 100644 (file)
@@ -61,7 +61,9 @@ HEADERS = \
        src/layeritemwidget.h \
        src/main.h \
        src/mathconstants.h \
+       src/objectwidget.h \
        src/painter.h \
+       src/rect.h \
        src/settingsdialog.h \
        src/structs.h \
        src/utils.h \
@@ -81,7 +83,9 @@ SOURCES = \
        src/layerwidget.cpp \
        src/layeritemwidget.cpp \
        src/main.cpp \
+       src/objectwidget.cpp \
        src/painter.cpp \
+       src/rect.cpp \
        src/settingsdialog.cpp \
        src/utils.cpp \
        src/vector.cpp
index ad832720743d286da3284823bfbaf6d12a668694..a4c39b74fe96eace7ee7376055641138c6c49d1e 100644 (file)
@@ -35,6 +35,7 @@
 #include "generaltab.h"
 #include "global.h"
 #include "layerwidget.h"
+#include "objectwidget.h"
 #include "painter.h"
 #include "settingsdialog.h"
 #include "structs.h"
@@ -75,11 +76,16 @@ ApplicationWindow::ApplicationWindow():
        BlockWidget * bw = new BlockWidget;
        dock2->setWidget(bw);
        addDockWidget(Qt::RightDockWidgetArea, dock2);
+       QDockWidget * dock3 = new QDockWidget(tr("Object"), this);
+       ObjectWidget * ow = new ObjectWidget;
+       dock3->setWidget(ow);
+       addDockWidget(Qt::RightDockWidgetArea, dock3);
        // Needed for saveState()
        dock1->setObjectName("Layers");
        dock2->setObjectName("Blocks");
+       dock3->setObjectName("Object");
 
-       //      Create status bar
+       // Create status bar
        zoomIndicator = new QLabel("Grid: 12.0\" BU: Inch");
        statusBar()->addPermanentWidget(zoomIndicator);
        statusBar()->showMessage(tr("Ready"));
@@ -91,6 +97,8 @@ ApplicationWindow::ApplicationWindow():
        connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
        connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
        connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
+
+       connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
 }
 
 
@@ -104,8 +112,9 @@ void ApplicationWindow::closeEvent(QCloseEvent * event)
 
 void ApplicationWindow::FileNew(void)
 {
-       // Should warn the user if drawing hasn't been saved...
-       drawing->document.objects.empty();
+       // Should warn the user if drawing hasn't been saved... !!! FIX !!!
+       DeleteContents(drawing->document.objects);
+       drawing->document.objects.clear();
        drawing->update();
        documentName.clear();
        setWindowTitle("Architektonas - Untitled");
@@ -133,7 +142,7 @@ void ApplicationWindow::FileOpen(void)
                return;
        }
 
-       Container container;//(Vector(0, 0));
+       Container container;
        bool successful = FileIO::LoadAtnsFile(file, &container);
        fclose(file);
 
@@ -143,10 +152,16 @@ void ApplicationWindow::FileOpen(void)
                msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
                msg.setIcon(QMessageBox::Critical);
                msg.exec();
+               // Make sure to delete any hanging objects in the container...
+               DeleteContents(container.objects);
                return;
        }
 
-printf("FileOpen: container size = %li\n", container.objects.size());
+//printf("FileOpen: container size = %li\n", container.objects.size());
+       // Keep memory leaks from happening by getting rid of the old document
+       DeleteContents(drawing->document.objects);
+       // We can do this because the vector is just a bunch of pointers to our
+       // Objects, and the Containers (non-empty) can be moved way with no problem.
        drawing->document = container;
        drawing->update();
        documentName = filename;
@@ -181,7 +196,8 @@ void ApplicationWindow::FileSave(void)
                msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
                msg.setIcon(QMessageBox::Critical);
                msg.exec();
-               // In this case, we should unlink the created file, since it's not right...
+               // In this case, we should unlink the created file, since it's not
+               // right...
 //             unlink(documentName.toUtf8().data());
                QFile::remove(documentName);
                return;
@@ -227,9 +243,9 @@ void ApplicationWindow::DeleteTool(void)
 {
        // For this tool, we check first to see if anything is selected. If so, we
        // delete those and *don't* select the delete tool.
-       if (drawing->numSelected > 0)
+//     if (drawing->numSelected > 0)
+       if (drawing->select.size() > 0)
        {
-//             drawing->DeleteSelectedItems();
                DeleteSelectedObjects(drawing->document.objects);
                drawing->update();
                deleteAct->setChecked(false);
@@ -507,8 +523,9 @@ else
 
                // Need the parent of the group, we're assuming here that the parent is
                // the drawing's document. Does it matter? Maybe...
-               // Could just stipulate that grouping like this only takes place where the
-               // parent of the group is the drawing's document. Makes life much simpler.
+               // Could just stipulate that grouping like this only takes place where
+               // the parent of the group is the drawing's document. Makes life much
+               // simpler.
                ((Container *)object)->SelectAll();
                ((Container *)object)->MoveContentsTo(&(drawing->document));
                drawing->document.Delete(object);
@@ -526,6 +543,70 @@ else
                statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
        }
 #else
+       int numSelected = drawing->select.size();
+
+       // If nothing selected, do nothing
+       if (numSelected == 0)
+       {
+               statusBar()->showMessage(tr("No objects selected to make a group from."));
+               return;
+       }
+
+       // If it's a group that's selected, ungroup it and leave the objects in a
+       // selected state
+       if (numSelected == 1)
+       {
+               Object * obj = (Object *)drawing->select[0];
+
+               if (obj->type != OTContainer)
+               {
+                       statusBar()->showMessage(tr("A group requires two or more selected objects."));
+                       return;
+               }
+
+               // Need the parent of the group, we're assuming here that the parent is
+               // the drawing's document. Does it matter? Maybe...
+               // Could just stipulate that grouping like this only takes place where
+               // the parent of the group is the drawing's document. Makes life much
+               // simpler.
+//             ((Container *)object)->SelectAll();
+//             ((Container *)object)->MoveContentsTo(&(drawing->document));
+//             drawing->document.Delete(object);
+               Container * c = (Container *)obj;
+//printf("Ungroup: container size = %li\n", c->objects.size());
+               SelectAll(c->objects);
+//printf("Ungroup: document size = %li (pre-AddObjectsTo)\n", drawing->document.objects.size());
+               RemoveSelectedObjects(drawing->document.objects);
+               AddObjectsTo(drawing->document.objects, c->objects);
+               drawing->select.clear();
+               AddObjectsTo(drawing->select, c->objects);
+               delete c;
+               statusBar()->showMessage(tr("Objects ungrouped."));
+//printf("Ungroup: document size = %li\n", drawing->document.objects.size());
+       }
+       // Otherwise, if it's a group of 2 or more objects (which can be groups too)
+       // group them and select the group
+       else if (numSelected > 1)
+       {
+//             Container * container = new Container(Vector(), &(drawing->document));
+//             drawing->document.MoveSelectedContentsTo(container);
+//             drawing->document.Add(container);
+//             container->DeselectAll();
+//             container->state = OSSelected;
+
+               Container * c = new Container();
+//             AddObjectsTo(c->objects, drawing->select);
+//             RemoveSelectedObjects(drawing->document.objects);
+               MoveSelectedObjectsTo(c->objects, drawing->document.objects);
+               drawing->document.objects.push_back(c);
+               ClearSelected(c->objects);
+               c->selected = true;
+               c->layer = Global::currentLayer;
+               drawing->select.clear();
+               drawing->select.push_back(c);
+               statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
+//printf("Group: document size = %li\n", drawing->document.objects.size());
+       }
 #endif
 
        drawing->update();
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
-
index cf70e400967a247ca8fbb73ad2d14da3cb686600..780c81214bf81c360bf53901478fb1421d390d6d 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <QtWidgets>
 #include <stdint.h>
+#include "rect.h"
 #include "structs.h"
 
 enum { ToolMouseDown, ToolMouseMove, ToolMouseUp, ToolKeyDown, ToolKeyUp, ToolCleanup };
@@ -20,7 +21,7 @@ class DrawingView: public QWidget
                void SetGridSize(uint32_t);
                void UpdateGridBackground(void);
                Point SnapPointToGrid(Point);
-               void RenderObjects(Painter *, std::vector<void *> &, int);
+               void RenderObjects(Painter *, std::vector<void *> &, int, bool ignoreLayer = false);
                void AddHoveredToSelection(void);
                void GetSelection(std::vector<void *> &);
                void GetHovered(std::vector<void *> &);
@@ -31,8 +32,10 @@ class DrawingView: public QWidget
                void ArcHandler(int, Point);
                void RotateHandler(int, Point);
                void MirrorHandler(int, Point);
+               Rect GetObjectExtents(Object *);
                void CheckObjectBounds(void);
                bool HitTestObjects(Point);
+               bool HitTest(Object *, Point);
                void HandleObjectMovement(Point);
 
        public slots:
@@ -40,6 +43,9 @@ class DrawingView: public QWidget
                void HandleLayerToggle(void);
                void HandleLayerSwap(int, int);
 
+       signals:
+               void ObjectHovered(Object *);
+
        protected:
                void paintEvent(QPaintEvent * event);
                void resizeEvent(QResizeEvent * event);
@@ -56,7 +62,7 @@ class DrawingView: public QWidget
 
        public:
                bool useAntialiasing;
-               uint32_t numSelected;
+//             uint32_t numSelected;
                uint32_t numHovered;
                bool shiftDown;
                bool ctrlDown;
index e0028cd90228c9d96a6ecd0fddea57f11382183f..1002dca87fecf22b79cbb2883253e9660fe6c34b 100644 (file)
@@ -108,42 +108,53 @@ OTHER CONSIDERATIONS:
 
 */
 
-enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc,
-       OTFDimension, OTFPolygon, OTFText, OTFImage, OTFBlock, OTFEndOfFile };
+//enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc,
+//     OTFDimension, OTFPolygon, OTFText, OTFImage, OTFBlock, OTFEndOfFile };
+enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFObject, OTFEndOfFile };
 
 
-/*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * object)
+// Instantiate class variables
+/*static*/ int FileIO::objectFileType = OTFObject;
+
+
+/*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * c)
 {
-       /* Approach: loop through the container, doing a depth-first traversal. Any extra
-          containers found are looped through until there aren't any more down, then
-          ordinary objects are described. This can be handled by a virtual Object function
-          that reports the object by itself if it's a non-Container, otherwise it
-          enumerates all objects within itself. */
+       /* Approach: loop through the container, doing a depth-first traversal. Any
+          extra containers found are looped through until there aren't any more
+          down, then ordinary objects are described. This can be handled by a
+          virtual Object function that reports the object by itself if it's a non-
+          Container, otherwise it enumerates all objects within itself. */
 
-       fprintf(file, "ARCHITEKTONAS DRAWING V1.0\n");
+       fprintf(file, "ARCHITEKTONAS DRAWING V1.1\n");
 #if 0
        object->Enumerate(file);
-       fprintf(file, "END\n");
-       return true;
 #else
-       return false;
+       WriteObjectToFile(file, (Object *)c);
 #endif
+       fprintf(file, "END\n");
+       return true;
 }
 
 
 /*static*/ bool FileIO::LoadAtnsFile(FILE * file, Container * drawing)
 {
-//     char buffer[256];
        float version;
 
        fscanf(file, "ARCHITEKTONAS DRAWING V%f", &version);
 
 //printf("Load: version = %f\n", version);
-       if (version != 1.0)
-               return false;
-       /* Approach: read each object in the file, one by one. If the object is a Container,
-          add objects to it until an "endContainer" marker is found. This will require a
-          stack to maintain the current Container. */
+//     if (version != 1.0)
+//             return false;
+
+       if (version == 1.0)
+               return LoadVersion1_0(file, drawing);
+       else if (version == 1.1)
+               return LoadVersion1_1(file, drawing);
+
+       return false;
+       /* Approach: read each object in the file, one by one. If the object is a
+          Container, add objects to it until an "endContainer" marker is found.
+          This will require a stack to maintain the current Container. */
 
 #if 0
        std::vector<Container *> containerStack;
@@ -158,9 +169,10 @@ enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc,
                        return false;
 
                // object->type down below can be replaced with objType.
-               // Above could be: bool FileIO::GetObjectFromFile(FILE *, Object *, ObjectType *);
-               // where the return value tells if it's a valid object, Object * returns the
-               // reconstructed object and ObjectType * returns the object type.
+               // Above could be: bool FileIO::GetObjectFromFile(FILE *, Object *,
+               // ObjectType *); where the return value tells if it's a valid object,
+               // Object * returns the reconstructed object and ObjectType * returns
+               // the object type.
 
                if (objectType == OTFEndOfFile)
                {
@@ -193,21 +205,114 @@ enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc,
 }
 
 
-/*static*/ bool FileIO::GetObjectFromFile(FILE * file, Object * parent, Object ** object, int * objectType)
+/*static*/ bool FileIO::LoadVersion1_0(FILE * file, Container * drawing)
+{
+       // Approach: read each object in the file, one by one. If the object is a
+       // Container, add objects to it until an "endContainer" marker is found.
+       // This will require a stack to maintain the current Container.
+       std::vector<Container *> containerStack;
+       Container * currentTopContainer = drawing;
+
+       while (!feof(file))
+       {
+               // Reconstruct the object
+               Object * obj = GetObjectFromFile(file);
+
+               // objectFileType is set in GetObjectFromFile()...
+               if (objectFileType == OTFObject)
+               {
+                       if (obj == NULL)
+                               return false;
+
+                       currentTopContainer->objects.push_back(obj);
+//printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
+
+                       // If the object is a container, push current TLC on the stack and
+                       // set it as the new TLC
+                       if (obj->type == OTContainer)
+                       {
+                               containerStack.push_back(currentTopContainer);
+                               currentTopContainer = (Container *)obj;
+                       }
+               }
+               else if (objectFileType == OTFContainerEnd)
+               {
+                       // Container is done, so pop the stack to get back the previous TLC
+                       currentTopContainer = containerStack.back();
+                       containerStack.pop_back();
+               }
+               else if (objectFileType == OTFEndOfFile)
+               {
+//printf("Load: container size = %li\n", drawing->objects.size());
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+
+/*static*/ bool FileIO::LoadVersion1_1(FILE * file, Container * drawing)
+{
+       // Approach: read each object in the file, one by one. If the object is a
+       // Container, add objects to it until an "endContainer" marker is found.
+       // This will require a stack to maintain the current Container.
+       std::vector<Container *> containerStack;
+       Container * currentTopContainer = drawing;
+
+       while (!feof(file))
+       {
+               // Reconstruct the object (extended format!)
+               Object * obj = GetObjectFromFile(file, true);
+
+               // objectFileType is set in GetObjectFromFile()...
+               if (objectFileType == OTFObject)
+               {
+                       if (obj == NULL)
+                               return false;
+
+                       currentTopContainer->objects.push_back(obj);
+//printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
+
+                       // If the object is a container, push current TLC on the stack and
+                       // set it as the new TLC
+                       if (obj->type == OTContainer)
+                       {
+                               containerStack.push_back(currentTopContainer);
+                               currentTopContainer = (Container *)obj;
+                       }
+               }
+               else if (objectFileType == OTFContainerEnd)
+               {
+                       // Container is done, so pop the stack to get back the previous TLC
+                       currentTopContainer = containerStack.back();
+                       containerStack.pop_back();
+               }
+               else if (objectFileType == OTFEndOfFile)
+               {
+//printf("Load: container size = %li\n", drawing->objects.size());
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+
+/*static*/ Object * FileIO::GetObjectFromFile(FILE * file, bool extended/*= false*/)
 {
-#if 0
        char buffer[256];
        int foundLayer = 0;
-       int num = fscanf(file, "%s", buffer);
-       bool recognized = false;
-       *object = NULL;
+       /*int num =*/ fscanf(file, "%s", buffer);
+       Object * obj = NULL;
+       objectFileType = OTFObject;
 //printf("FileIO: fscanf returned %i, buffer = \"%s\"\n", num, buffer);
 
 // The following fugliness is for troubleshooting. Can remove later.
        if ((strcmp(buffer, "END") != 0) && (strcmp(buffer, "ENDCONTAINER") != 0))
 {
 errno = 0;
-               num = fscanf(file, " %i ", &foundLayer);
+               /*num =*/ fscanf(file, " %i ", &foundLayer);
 //printf("FileIO: fscanf returned %i, foundLayer = %i\n", num, foundLayer);
 if (errno)
 {
@@ -230,65 +335,116 @@ if (errno)
 }
 }
 
+       // Need to add pen attributes as well... do that for v1.1 :-P
        if (strcmp(buffer, "LINE") == 0)
        {
-//printf("      Found LINE.\n");
-               recognized = true;
-               Vector v1, v2;
-               fscanf(file, "(%lf,%lf) (%lf,%lf)", &v1.x, &v1.y, &v2.x, &v2.y);
-//printf("      Number of params recognized: %i\n", n);
-               *object = new Line(v1, v2, parent);
-               *objectType = OTFLine;
+               Point p1, p2;
+               fscanf(file, "(%lf,%lf) (%lf,%lf)", &p1.x, &p1.y, &p2.x, &p2.y);
+               obj = (Object *)new Line(p1, p2);
        }
        else if (strcmp(buffer, "CIRCLE") == 0)
        {
-               recognized = true;
-               Vector v;
+               Point p;
                double r;
-               fscanf(file, "(%lf,%lf) %lf", &v.x, &v.y, &r);
-               *object = new Circle(v, r, parent);
-               *objectType = OTFCircle;
+               fscanf(file, "(%lf,%lf) %lf", &p.x, &p.y, &r);
+               obj = (Object *)new Circle(p, r);
        }
        else if (strcmp(buffer, "ARC") == 0)
        {
-               recognized = true;
-               Vector v;
+               Point p;
                double r, a1, a2;
-               fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &v.x, &v.y, &r, &a1, &a2);
-               *object = new Arc(v, r, a1, a2, parent);
-               *objectType = OTFArc;
+               fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &p.x, &p.y, &r, &a1, &a2);
+               obj = (Object *)new Arc(p, r, a1, a2);
        }
        else if (strcmp(buffer, "DIMENSION") == 0)
        {
-               recognized = true;
-               Vector v1, v2;
+               Point p1, p2;
                DimensionType type;
-               fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &v1.x, &v1.y, &v2.x, &v2.y, &type);
-               *object = new Dimension(v1, v2, type, parent);
-               *objectType = OTFDimension;
+               fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &p1.x, &p1.y, &p2.x, &p2.y, (int *)&type);
+               obj = (Object *)new Dimension(p1, p2, type);
        }
        else if (strcmp(buffer, "CONTAINER") == 0)
        {
-               recognized = true;
-               *objectType = OTFContainer;
+               obj = (Object *)new Container();
+//             objectFileType = OTFContainer;
        }
        else if (strcmp(buffer, "ENDCONTAINER") == 0)
        {
-               recognized = true;
-               *objectType = OTFContainerEnd;
+               objectFileType = OTFContainerEnd;
        }
        else if (strcmp(buffer, "END") == 0)
        {
-               recognized = true;
-               *objectType = OTFEndOfFile;
+               objectFileType = OTFEndOfFile;
        }
 
-       if (*object)
-               (*object)->layer = foundLayer;
+       if (obj != NULL)
+       {
+               obj->layer = foundLayer;
 
-       return recognized;
-#else
-       return false;
-#endif
+               if (extended && (obj->type != OTContainer))
+               {
+                       fscanf(file, " (%i, %f, %i)", &obj->color, &obj->thickness, &obj->style);
+               }
+       }
+
+       return obj;
+}
+
+
+/*static*/ bool FileIO::WriteObjectToFile(FILE * file, Object * obj)
+{
+       // Sanity check
+       if (obj == NULL)
+               return false;
+
+       switch (obj->type)
+       {
+       case OTLine:
+               fprintf(file, "LINE %i (%lf,%lf) (%lf,%lf)", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y);
+               break;
+       case OTCircle:
+               fprintf(file, "CIRCLE %i (%lf,%lf) %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0]);
+               break;
+       case OTEllipse:
+               break;
+       case OTArc:
+               fprintf(file, "ARC %i (%lf,%lf) %lf, %lf, %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0], obj->angle[0], obj->angle[1]);
+               break;
+       case OTPolygon:
+               break;
+       case OTDimension:
+//             fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, dimensionType);
+               fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y, ((Dimension *)obj)->subtype);
+               break;
+       case OTSpline:
+               break;
+       case OTText:
+               fprintf(file, "TEXT %i (%lf,%lf) \"%s\"", obj->layer, obj->p[0].x, obj->p[0].y, ((Text *)obj)->s.c_str());
+               break;
+       case OTContainer:
+       {
+               Container * c = (Container *)obj;
+
+               if (c->topLevel == false)
+                       fprintf(file, "CONTAINER %i\n", obj->layer);
+
+               std::vector<void *>::iterator i;
+
+               for(i=c->objects.begin(); i!=c->objects.end(); i++)
+                       WriteObjectToFile(file, (Object *)*i);
+
+               if (c->topLevel == false)
+                       fprintf(file, "ENDCONTAINER\n");
+
+               break;
+       }
+       default:
+               break;
+       }
+
+       if (obj->type != OTContainer)
+               fprintf(file, " (%i, %f, %i)\n", obj->color, obj->thickness, obj->style);
+
+       return true;
 }
 
index d4fc9d585efc2fe776bc08470989a782a1852c6e..8aa7511f26366df767fa648a3db159733de428a6 100644 (file)
@@ -3,8 +3,6 @@
 
 #include <stdio.h>
 #include "structs.h"
-//class Container;
-//class Object;
 
 // NB: The methods in this class are all static, so there's no need to
 //     instantiate an object of this type to use its functions.
@@ -16,7 +14,13 @@ class FileIO
                static bool LoadAtnsFile(FILE *, Container *);
 
        private:
-               static bool GetObjectFromFile(FILE *, Object *, Object **, int *);
+               static bool LoadVersion1_0(FILE *, Container *);
+               static bool LoadVersion1_1(FILE *, Container *);
+//             static bool GetObjectFromFile(FILE *, Object *, Object **, int *);
+               static Object * GetObjectFromFile(FILE *, bool extended = false);
+               static bool WriteObjectToFile(FILE *, Object *);
+
+               static int objectFileType;
 };
 
 #endif // __FILEIO_H__
index b9158974ec97d902d4ff2e936a2cc4b43e469082..e5a6af2cd991b5c5f74ee5e198073e18f43053fe 100644 (file)
@@ -255,14 +255,12 @@ void Geometry::CheckLineToCircleIntersection(Object * l, Object * c)
        // Radius is the hypotenuse, so we have to use c² = a² + b² => a² = c² - b²
        double perpendicularLength = sqrt((c->radius[0] * c->radius[0]) - (distance * distance));
 
-       // Now, find the points using the length, then check to see if they are on
-       // the line segment
+       // Now, find the intersection points using the length...
        Vector lineUnit = Vector(l->p[0], l->p[1]).Unit();
        Point i1 = p + (lineUnit * perpendicularLength);
        Point i2 = p - (lineUnit * perpendicularLength);
 
-       // Now we have our intersection points, next we need to see if they are on
-       // the line segment...
+       // Next we need to see if they are on the line segment...
        double u = ParameterOfLineAndPoint(l->p[0], l->p[1], i1);
        double v = ParameterOfLineAndPoint(l->p[0], l->p[1], i2);
 
diff --git a/src/objectwidget.cpp b/src/objectwidget.cpp
new file mode 100644 (file)
index 0000000..c7e0cf6
--- /dev/null
@@ -0,0 +1,117 @@
+// objectwidget.cpp: Object tweaking widget
+//
+// Part of the Architektonas Project
+// (C) 2016 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO  WHEN        WHAT
+// ---  ----------  ------------------------------------------------------------
+// JLH  11/07/2016  Created this file
+//
+
+#include "objectwidget.h"
+#include "mathconstants.h"
+
+
+ObjectWidget::ObjectWidget(void): QWidget()
+{
+#if 0
+       QListWidget * qlw = new QListWidget;
+       QListWidgetItem * qli1 = new QListWidgetItem(qlw);
+       QListWidgetItem * qli2 = new QListWidgetItem(qlw);
+       QListWidgetItem * qli3 = new QListWidgetItem(qlw);
+       QListWidgetItem * qli4 = new QListWidgetItem(qlw);
+       QListWidgetItem * qli5 = new QListWidgetItem(qlw);
+#endif
+       label = new QLabel;
+
+#if 0
+       QPushButton * pb1 = new QPushButton("+");
+       QPushButton * pb2 = new QPushButton("-");
+       QPushButton * pb3 = new QPushButton("Edit");
+       QPushButton * pb4 = new QPushButton("Import");
+#else
+       QToolButton * pb1 = new QToolButton;
+       QToolButton * pb2 = new QToolButton;
+       QToolButton * pb3 = new QToolButton;
+       QToolButton * pb4 = new QToolButton;
+
+       pb1->setIcon(QIcon(":/res/layer-add.png"));
+       pb2->setIcon(QIcon(":/res/layer-delete.png"));
+       pb3->setIcon(QIcon(":/res/layer-edit.png"));
+       pb4->setIcon(QIcon(":/res/block-import.png"));
+
+       pb1->setToolTip(tr("Add block"));
+       pb2->setToolTip(tr("Remove block"));
+       pb3->setToolTip(tr("Edit block"));
+       pb4->setToolTip(tr("Import block"));
+#endif
+
+       QHBoxLayout * hbox1 = new QHBoxLayout;
+       hbox1->addWidget(pb1);
+       hbox1->addWidget(pb2);
+       hbox1->addWidget(pb3);
+       hbox1->addWidget(pb4);
+       hbox1->addStretch();
+
+       QVBoxLayout * mainLayout = new QVBoxLayout;
+       mainLayout->addWidget(label);
+       mainLayout->addLayout(hbox1);
+
+       setLayout(mainLayout);
+}
+
+
+ObjectWidget::~ObjectWidget()
+{
+}
+
+
+void ObjectWidget::ShowInfo(Object * obj)
+{
+       const char objName[OTCount][16] = {
+               "None", "Line", "Circle", "Ellipse", "Arc", "Polygon", "Dimension", "Spline", "Text", "Container"
+       };
+
+       // Sanity check
+       if (obj == NULL)
+               return;
+
+       QString s = QString("%1<br><br>").arg(QString(objName[obj->type]));
+
+       switch (obj->type)
+       {
+       case OTLine:
+       {
+               Vector line(obj->p[0], obj->p[1]);
+               s += QString("&lt;%1, %2&gt; to &lt;%3, %4&gt;<br>Length: %5<br>Angle: %6&#x00B0;<br>").arg(obj->p[0].x).arg(obj->p[0].y).arg(obj->p[1].x).arg(obj->p[1].y).arg(line.Magnitude()).arg(line.Angle() * RADIANS_TO_DEGREES);
+               break;
+       }
+       case OTCircle:
+               s += QString("Center: &lt;%1, %2&gt;<br>Radius: %3<br>").arg(obj->p[0].x).arg(obj->p[0].y).arg(obj->radius[0]);
+               break;
+       case OTEllipse:
+               break;
+       case OTArc:
+               s += QString("Center: &lt;%1, %2&gt;<br>Radius: %3<br>Start: %4&#x00B0;<br>End: %5&#x00B0;<br>").arg(obj->p[0].x).arg(obj->p[0].y).arg(obj->radius[0]).arg(obj->angle[0] * RADIANS_TO_DEGREES).arg(obj->angle[1] * RADIANS_TO_DEGREES);
+               break;
+               break;
+       case OTPolygon:
+               break;
+       case OTDimension:
+               break;
+       case OTSpline:
+               break;
+       case OTText:
+               break;
+       case OTContainer:
+               break;
+       default:
+               break;
+       }
+
+       label->setText(s);
+}
+
diff --git a/src/objectwidget.h b/src/objectwidget.h
new file mode 100644 (file)
index 0000000..31b7be8
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __OBJECTWIDGET_H__
+#define __OBJECTWIDGET_H__
+
+#include <QtWidgets>
+#include "structs.h"
+
+
+class ObjectWidget: public QWidget
+{
+       Q_OBJECT
+
+       public:
+               ObjectWidget(void);
+               ~ObjectWidget();
+
+       public slots:
+               void ShowInfo(Object *);
+
+       private:
+               QLabel * label;
+};
+
+#endif // __OBJECTWIDGET_H__
+
index e8aaba1cf04cc8f7602a9d587884c0309f0e7222..08068119afde63c7ccd3b5b0eb263b3ab774663a 100644 (file)
 #include "mathconstants.h"
 
 
-// Set class variable defaults
-//Vector Painter::origin(-10.0, -10.0);
-//double Painter::zoom = 1.0;
-//Vector Painter::screenSize(200.0, 200.0);
-
-
 Painter::Painter(QPainter * p/*= NULL*/): painter(p)
 {
 }
@@ -148,19 +142,11 @@ void Painter::DrawAngledText(Vector center, double angle, QString text, double s
        float yOffset = -12.0 * Global::zoom * size;
 
        // Fix text so it isn't upside down...
-#if 0
-       if ((angle > PI * 0.5) && (angle < PI * 1.5))
-       {
-               angle += PI;
-               yOffset = 12.0 * Global::zoom * size;
-       }
-#else
        if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
        {
                angle += HALF_TAU;
                yOffset = 12.0 * Global::zoom * size;
        }
-#endif
 
        textBox.translate(0, yOffset);
        painter->save();
@@ -199,6 +185,9 @@ void Painter::DrawTextObject(Point p, QString text, double size, double angle/*=
 
 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
 {
+       if (!painter)
+               return;
+
        center = CartesianToQtCoords(center);
        // Need to multiply scalar quantities by the zoom factor as well...
        radius *= Global::zoom;
@@ -212,6 +201,9 @@ void Painter::DrawArc(Vector center, double radius, double startAngle, double sp
 
 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
 {
+       if (!painter)
+               return;
+
        // Need to multiply scalar quantities by the zoom factor as well...
        center = CartesianToQtCoords(center);
        painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
@@ -222,6 +214,9 @@ void Painter::DrawEllipse(Vector center, double axis1, double axis2)
 // we don't want our object handle size to depend on the zoom level!
 void Painter::DrawHandle(Vector center)
 {
+       if (!painter)
+               return;
+
        center = CartesianToQtCoords(center);
        painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
        painter->setBrush(Qt::NoBrush);
@@ -229,16 +224,62 @@ void Painter::DrawHandle(Vector center)
 }
 
 
+// This function is for drawing feedback points without regard for zoom level;
+// we don't want our feedback point size to depend on the zoom level!
+void Painter::DrawCross(Vector point)
+{
+       if (!painter)
+               return;
+
+       point = CartesianToQtCoords(point);
+       painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
+       painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
+       painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.0);
+}
+
+
+// This function is for drawing feedback points without regard for zoom level;
+// we don't want our feedback point size to depend on the zoom level!
+void Painter::DrawRectCorners(Rect rect)
+{
+       if (!painter)
+               return;
+
+//     QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
+       
+       Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
+       Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
+//     QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
+//     screenRect.adjust(-8, 8, 8, -8);        // Left/top, right/bottom
+//     painter->drawRect(screenRect);
+       v1 += Vector(-8.0, -8.0);
+       v2 += Vector(+8.0, +8.0);
+       painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
+       painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
+       painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
+       painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
+       painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
+       painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
+       painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
+       painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
+       painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
+       
+}
+
+
 // This function is for drawing object handles without regard for zoom level;
 // we don't want our object handle size to depend on the zoom level!
 void Painter::DrawArrowHandle(Vector center, double angle)
 {
+       if (!painter)
+               return;
+
        center = CartesianToQtCoords(center);
        QPolygonF arrow;
 
        // Since we're drawing directly on the screen, the Y is inverted. So we use
        // the mirror of the angle.
-       double orthoAngle = -angle + QTR_TAU;//(PI / 2.0);
+       double orthoAngle = -angle + QTR_TAU;
        Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
        Vector unit = Vector(cos(-angle), sin(-angle));
 
@@ -259,12 +300,15 @@ void Painter::DrawArrowHandle(Vector center, double angle)
 // we don't want our object handle size to depend on the zoom level!
 void Painter::DrawArrowToLineHandle(Vector center, double angle)
 {
+       if (!painter)
+               return;
+
        DrawArrowHandle(center, angle);
        center = CartesianToQtCoords(center);
 
        // Since we're drawing directly on the screen, the Y is inverted. So we use
        // the mirror of the angle.
-       double orthoAngle = -angle + QTR_TAU;//(PI / 2.0);
+       double orthoAngle = -angle + QTR_TAU;
        Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
        Vector unit = Vector(cos(-angle), sin(-angle));
 
@@ -363,7 +407,7 @@ void Painter::DrawArrowhead(Vector head, Vector tail, double size)
 
        // We draw the arrowhead aligned along the line from tail to head
        double angle = Vector(head - tail).Angle();
-       double orthoAngle = angle + QTR_TAU;//(PI / 2.0);
+       double orthoAngle = angle + QTR_TAU;
        Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
        Vector unit = Vector(head - tail).Unit();
 
@@ -395,6 +439,9 @@ void Painter::DrawCrosshair(Vector point)
 
 void Painter::DrawInformativeText(QString text)
 {
+       if (!painter)
+               return;
+
        painter->setFont(*Global::font);
        QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
        bounds.moveTo(17.0, 17.0);
index 5e868d75764250e457d161f118f2ad8fd7ad4b36..4ff50b2298887145ee587bc29e11550a666ac529 100644 (file)
@@ -3,10 +3,9 @@
 
 #include <stdint.h>
 #include <QtWidgets>
+#include "rect.h"
 #include "vector.h"
 
-//#define SCREEN_ZOOM  (1.0 / 4.0)
-
 // Forward declarations
 
 class Painter
@@ -26,6 +25,8 @@ class Painter
                void DrawArc(Vector, double, double, double);
                void DrawEllipse(Vector, double, double);
                void DrawHandle(Vector);
+               void DrawCross(Vector);
+               void DrawRectCorners(Rect);
                void DrawArrowHandle(Vector, double);
                void DrawArrowToLineHandle(Vector, double);
                void DrawLine(int, int, int, int);
@@ -43,14 +44,9 @@ class Painter
                static Vector CartesianToQtCoords(Vector);
                static Vector QtToCartesianCoords(Vector);
 
-       public:
-               // Class variables
-//             static Vector origin;           // The window origin, not location of the origin
-//             static double zoom;                     // Window zoom factor
-//             static Vector screenSize;       // Width & height of the window we're drawing on
-
        private:
                QPainter * painter;
 };
 
 #endif // __PAINTER_H__
+
diff --git a/src/rect.cpp b/src/rect.cpp
new file mode 100644 (file)
index 0000000..8207f23
--- /dev/null
@@ -0,0 +1,100 @@
+//
+// rect.cpp: Rectangle object implementation
+//
+// Part of the Architektonas Project
+// (C) 2016 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO  WHEN        WHAT
+// ---  ----------  ------------------------------------------------------------
+// JLH  11/10/2016  Created this file
+//
+
+#include "rect.h"
+//#include <stdio.h>
+
+
+Rect::Rect(): l(0), r(0), t(0), b(0)
+{
+}
+
+
+Rect::Rect(double ll, double rr, double tt, double bb):
+       l(ll), r(rr), t(tt), b(bb)
+{
+       Normalize();
+}
+
+
+Rect::Rect(Point tl, Point br): l(tl.x), r(br.x), t(tl.y), b(br.y)
+{
+       Normalize();
+}
+
+
+Rect & Rect::operator*=(double scale)
+{
+       l *= scale;
+       r *= scale;
+       t *= scale;
+       b *= scale;
+       return *this;
+}
+
+
+Rect & Rect::operator|=(Rect r2)
+{
+//printf("operatore|=\nthis = (%lf, %lf, %lf, %lf), r = (%lf, %lf, %lf, %lf)\n", l, t, r, b, r2.l, r2.t, r2.r, r2.b);
+       if (r2.l < l)
+               l = r2.l;
+
+       if (r2.r > r)
+               r = r2.r;
+
+       if (r2.t > t)
+               t = r2.t;
+
+       if (r2.b < b)
+               b = r2.b;
+
+       return *this;
+}
+
+
+void Rect::Normalize(void)
+{
+       if (l > r)
+       {
+               double x = l;
+               l = r;
+               r = x;
+       }
+
+       if (b > t)
+       {
+               double x = b;
+               b = t;
+               t = x;
+       }
+}
+
+
+void Rect::Translate(Point p)
+{
+       l += p.x;
+       r += p.x;
+       t += p.y;
+       b += p.y;
+}
+
+
+void Rect::Expand(double amt)
+{
+       l -= amt;
+       r += amt;
+       t += amt;
+       b -= amt;
+}
+
diff --git a/src/rect.h b/src/rect.h
new file mode 100644 (file)
index 0000000..0a8478a
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __RECT_H__
+#define __RECT_H__
+
+//
+// We're doing this because the Qt implementation is non-Cartesian compliant.
+// Also, it auto-normalizes rects constructed using the constructors. :-)
+//
+
+#include "vector.h"
+
+struct Rect
+{
+       double l, r, t, b;
+
+       Rect();
+       Rect(double ll, double rr, double tt, double bb);
+       Rect(Point tl, Point br);
+       Rect & operator*=(double scale);
+       Rect & operator|=(Rect x);
+       void Normalize(void);
+       void Translate(Point p);
+       void Expand(double amt);
+};
+
+#endif // __RECT_H__
+
index 6920e491cb92203f7555a57efc1c1268b209d22c..bc68adf76801316d1b790aa3d72695b879b0a3c8 100644 (file)
@@ -7,7 +7,7 @@
 #include "global.h"
 #include "vector.h"
 
-enum ObjectType { OTNone, OTLine, OTCircle, OTEllipse, OTArc, OTPolygon, OTDimension, OTSpline, OTText, OTContainer };
+enum ObjectType { OTNone = 0, OTLine, OTCircle, OTEllipse, OTArc, OTPolygon, OTDimension, OTSpline, OTText, OTContainer, OTCount };
 
 enum DimensionType { DTLinear, DTLinearVert, DTLinearHorz, DTRadial, DTDiametric, DTCircumferential, DTAngular, DTLeader };
 
@@ -106,14 +106,29 @@ struct Container {
        OBJECT_COMMON;
        std::vector<void *> objects;
        double scale;
+       bool topLevel;
 
-       Container(): type(OTContainer), id(Global::objectID++), selected(false), hovered(false), hitObject(false) {}
+       Container(bool tl = false): type(OTContainer), id(Global::objectID++), selected(false), hovered(false), hitObject(false), topLevel(tl) {}
+//     void DeleteContents(void) {}
+/*     void DeleteContents(Container * c)
+       {
+               std::vector<void *>::iterator i;
+
+               for(i=c->objects.begin(); i!=c->objects.end(); i++)
+               {
+                       Object * obj = (Object *)(*i);
+
+                       if (obj->type == OTContainer)
+                               DeleteContainer((Container *)obj);
+
+                       delete *i;
+               }
+       }*/
 };
 
 struct Object {
        OBJECT_COMMON;
 };
 
-
 #endif // __STRUCTS_H__
 
index 12f7146ad4418ea74d145074847981b562c81651..02d6b20ef9c6fe977aa9efe5de0a4f5e6e70baa7 100644 (file)
@@ -90,6 +90,25 @@ Object * CopyObject(Object * obj)
 }
 
 
+void MoveSelectedObjectsTo(std::vector<void *> & dest, std::vector<void *> & from)
+{
+       std::vector<void *>::iterator i = from.begin();
+
+       while (i != from.end())
+       {
+               Object * obj = (Object *)(*i);
+
+               if (obj->selected)
+               {
+                       dest.push_back(*i);
+                       from.erase(i);
+               }
+               else
+                       i++;
+       }
+}
+
+
 void AddObjectsTo(std::vector<void *> & dest, std::vector<void *> & from)
 {
        for(std::vector<void *>::iterator i=from.begin(); i!=from.end(); i++)
@@ -106,6 +125,38 @@ void ClearSelected(std::vector<void *> & v)
 }
 
 
+void SelectAll(std::vector<void *> & v)
+{
+       std::vector<void *>::iterator i;
+
+       for(i=v.begin(); i!=v.end(); i++)
+               ((Object *)(*i))->selected = true;
+}
+
+
+//
+// Recursively go down thru the Container's vectors, deleting all the objects
+// contained therein. Once that is done, the main Container can be deleted. We
+// don't have to worry about the underlying std::vectors, as they have their
+// own destructors--plus they don't take ownership of objects, which is why we
+// have to keep track of that stuff ourselves. :-P Believe it or not, this is a
+// Good Thing(TM). ;-)
+//
+void DeleteContents(std::vector<void *> & v)
+{
+       std::vector<void *>::iterator i;
+
+       for(i=v.begin(); i!=v.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+
+               if (obj->type == OTContainer)
+                       DeleteContents(((Container *)obj)->objects);
+
+               delete obj;
+       }
+}
+
 void DeleteSelectedObjects(std::vector<void *> & v)
 {
        std::vector<void *>::iterator i = v.begin();
@@ -125,6 +176,26 @@ void DeleteSelectedObjects(std::vector<void *> & v)
 }
 
 
+//
+// This is used to remove selected objects from one container in order to move
+// them to a different container.
+//
+void RemoveSelectedObjects(std::vector<void *> & v)
+{
+       std::vector<void *>::iterator i = v.begin();
+
+       while (i != v.end())
+       {
+               Object * obj = (Object *)(*i);
+
+               if (obj->selected)
+                       v.erase(i);
+               else
+                       i++;
+       }
+}
+
+
 void SavePointsFrom(std::vector<void *> & v, std::vector<Object> & save)
 {
        save.clear();
@@ -156,3 +227,21 @@ void RestorePointsTo(std::vector<void *> & v, std::vector<Object> & s)
 }
 
 
+void TranslateObject(Object * obj, Point delta)
+{
+       if (obj->type == OTContainer)
+       {
+               Container * c = (Container *)obj;
+               std::vector<void *>::iterator i;
+
+               for(i=c->objects.begin(); i!=c->objects.end(); i++)
+                       TranslateObject((Object *)*i, delta);
+       }
+       else
+       {
+               obj->p[0] += delta;
+               obj->p[1] += delta;
+       }
+}
+
+
index 23b6ad3d3c1a467e5794fc53d24ff483fb70aec8..24c0414634bd6bdd1f234f5caa853e54c9ea95cf 100644 (file)
@@ -6,11 +6,16 @@
 
 void CopyObjects(std::vector<void *> & from, std::vector<void *> & to);
 Object * CopyObject(Object * obj);
+void MoveSelectedObjectsTo(std::vector<void *> & dest, std::vector<void *> & from);
 void AddObjectsTo(std::vector<void *> & dest, std::vector<void *> & from);
 void ClearSelected(std::vector<void *> & v);
+void SelectAll(std::vector<void *> & v);
+void DeleteContents(std::vector<void *> & v);
 void DeleteSelectedObjects(std::vector<void *> & v);
+void RemoveSelectedObjects(std::vector<void *> & v);
 void SavePointsFrom(std::vector<void *> & v, std::vector<Object> & s);
 void RestorePointsTo(std::vector<void *> & v, std::vector<Object> & s);
+void TranslateObject(Object * obj, Point delta);
 
 #endif // __UTILS_H__