]> Shamusworld >> Repos - architektonas/commitdiff
Added new Spline object and Add Spline tool to GUI.
authorShamus Hammons <jlhamm@acm.org>
Fri, 21 Mar 2014 02:18:56 +0000 (21:18 -0500)
committerShamus Hammons <jlhamm@acm.org>
Fri, 21 Mar 2014 02:18:56 +0000 (21:18 -0500)
TODO
architektonas.pro
res/add-spline-tool.png [new file with mode: 0644]
res/architektonas.qrc
src/applicationwindow.cpp
src/applicationwindow.h
src/drawsplineaction.cpp [new file with mode: 0644]
src/drawsplineaction.h [new file with mode: 0644]
src/object.h
src/spline.cpp [new file with mode: 0644]
src/spline.h [new file with mode: 0644]

diff --git a/TODO b/TODO
index 9251e2bb82e357fb60b8b79eefca93c2513bab32..608c08117c7d3f71ddae4d594858b1cc2668a20c 100644 (file)
--- a/TODO
+++ b/TODO
@@ -5,11 +5,11 @@ Stuff To Be Implemented/Fixed
  - Add Ellipse
  - Add Spline
  - Add Text
- - Manipulate Dimension
  - Object connections (two types: flexible and rigid)
- - Group selection (kind done, needs more work though)
- - Take movement code out of Objects and put it into top level Container (actually
-   I think this should be more of the state code handling. Have to see.)
+ - Group selection (kinda done, needs more work though)
+ - Take movement code out of Objects and put it into top level Container
+   (actually I think this should be more of the state code handling. Have to
+   see.)
  - Add OSD to Object creation
  - Add layers
  - Add blocks
@@ -18,7 +18,7 @@ Stuff To Be Implemented/Fixed
  - Add fill/hatch to Objects
  - Fix zooming to be more intuitive
  - Add other Dimension types, like radial, diametric, leader
- - Restrict movement horizontal/vertical tool
+ - Restrict movement horizontal/vertical tool (keyboard shortcut?)
  - Fix Arc manipulator. Idea: split edge handles so that the inner half controls
    arc sizing, outer half controls rotation. That way you can grab either handle
    and know what it's supposed to do.
@@ -33,6 +33,7 @@ Stuff To Be Implemented/Fixed
 Stuff That's Done
 -----------------
 
+ - Manipulate Dimension [Shamus 2014-03-20]
  - Fix snap to grid to honor both states (right now, it's a weird mix of states)
    [Shamus 2013-08-11]
  - Add Arc [Shamus 2013-08-14]
index eea6798fbcaa90ced5f0b118a11c9803d86dffb4..ce6ce577d30253b8953dc8ffcbd9aa8949b8edcb 100644 (file)
@@ -62,6 +62,7 @@ HEADERS = \
        src/drawcircleaction.h \
        src/drawdimensionaction.h \
        src/drawlineaction.h \
+       src/drawsplineaction.h \
        src/drawtextaction.h \
        src/ellipse.h \
        src/fileio.h \
@@ -77,6 +78,7 @@ HEADERS = \
        src/painter.h \
        src/rotateaction.h \
        src/settingsdialog.h \
+       src/spline.h \
        src/text.h \
        src/trimaction.h \
        src/vector.h
@@ -98,6 +100,7 @@ SOURCES = \
        src/drawcircleaction.cpp \
        src/drawdimensionaction.cpp \
        src/drawlineaction.cpp \
+       src/drawsplineaction.cpp \
        src/drawtextaction.cpp \
        src/ellipse.cpp \
        src/fileio.cpp \
@@ -112,6 +115,7 @@ SOURCES = \
        src/painter.cpp \
        src/rotateaction.cpp \
        src/settingsdialog.cpp \
+       src/spline.cpp \
        src/text.cpp \
        src/trimaction.cpp \
        src/vector.cpp
diff --git a/res/add-spline-tool.png b/res/add-spline-tool.png
new file mode 100644 (file)
index 0000000..23e6b24
Binary files /dev/null and b/res/add-spline-tool.png differ
index 602ddae4d8e7bdcce916516cedf101c4f396c359..c8e8f908ce1855fede0bb84fe11948110e028ad0 100644 (file)
@@ -5,6 +5,7 @@
                <file>add-circle-tool.png</file>
                <file>add-line-tool.png</file>
                <file>add-polygon-tool.png</file>
+               <file>add-spline-tool.png</file>
                <file>atns-icon.png</file>
                <file>delete-tool.png</file>
                <file>dimension-tool.png</file>
index 4f1be35d14a453acfdf3d6b082cf6b0360873d65..23c865427f64ecc2971695db553567908cb870ff 100644 (file)
@@ -36,6 +36,7 @@
 #include "drawcircleaction.h"
 #include "drawdimensionaction.h"
 #include "drawlineaction.h"
+#include "drawsplineaction.h"
 #include "fileio.h"
 #include "generaltab.h"
 #include "geometry.h"
@@ -303,6 +304,13 @@ void ApplicationWindow::AddPolygonTool(void)
 }
 
 
+void ApplicationWindow::AddSplineTool(void)
+{
+       ClearUIToolStatesExcept(addSplineAct);
+       SetInternalToolStates();
+}
+
+
 void ApplicationWindow::ZoomInTool(void)
 {
        double zoomFactor = 2.0;
@@ -397,6 +405,9 @@ void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
        if (exception != addPolygonAct)
                addPolygonAct->setChecked(false);
 
+       if (exception != addSplineAct)
+               addSplineAct->setChecked(false);
+
        if (exception != deleteAct)
                deleteAct->setChecked(false);
 
@@ -430,6 +441,7 @@ void ApplicationWindow::SetInternalToolStates(void)
        drawing->SetToolActive(addCircleAct->isChecked() ? new DrawCircleAction() : NULL);
        drawing->SetToolActive(addArcAct->isChecked() ? new DrawArcAction() : NULL);
        drawing->SetToolActive(addDimensionAct->isChecked() ? new DrawDimensionAction() : NULL);
+       drawing->SetToolActive(addSplineAct->isChecked() ? new DrawSplineAction() : NULL);
        drawing->SetToolActive(mirrorAct->isChecked() ? new MirrorAction() : NULL);
        drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL);
        drawing->SetToolActive(trimAct->isChecked() ? new TrimAction() : NULL);
@@ -671,6 +683,9 @@ void ApplicationWindow::CreateActions(void)
        addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
        connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
 
+       addSplineAct = CreateAction(tr("Add &Spline"), tr("Add Spline"), tr("Add a NURB spline to the drawing."), QIcon(":/res/add-spline-tool.png"), QKeySequence("A,S"), true);
+       connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
+
        aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
        connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
 
@@ -795,6 +810,7 @@ void ApplicationWindow::CreateMenus(void)
        menu->addAction(addCircleAct);
        menu->addAction(addArcAct);
        menu->addAction(addPolygonAct);
+       menu->addAction(addSplineAct);
        menu->addAction(addDimensionAct);
        menu->addSeparator();
        menu->addAction(settingsAct);
@@ -843,6 +859,7 @@ void ApplicationWindow::CreateToolbars(void)
        toolbar->addAction(addCircleAct);
        toolbar->addAction(addArcAct);
        toolbar->addAction(addPolygonAct);
+       toolbar->addAction(addSplineAct);
        toolbar->addAction(addDimensionAct);
 
        spinbox->setRange(4, 256);
index 089ab8f28010983f7174b5fbaa134c50dd0b488f..b695c1ed06059ac72c6cc618b946e03d4ec466a0 100644 (file)
@@ -39,6 +39,7 @@ class ApplicationWindow: public QMainWindow
                void AddCircleTool(void);
                void AddArcTool(void);
                void AddPolygonTool(void);
+               void AddSplineTool(void);
                void ZoomInTool(void);
                void ZoomOutTool(void);
                void HelpAbout(void);
@@ -86,6 +87,7 @@ class ApplicationWindow: public QMainWindow
                QAction * addCircleAct;
                QAction * addArcAct;
                QAction * addPolygonAct;
+               QAction * addSplineAct;
                QAction * aboutAct;
                QAction * rotateAct;
                QAction * zoomInAct;
diff --git a/src/drawsplineaction.cpp b/src/drawsplineaction.cpp
new file mode 100644 (file)
index 0000000..644cd50
--- /dev/null
@@ -0,0 +1,135 @@
+// drawsplineaction.cpp: Action class for drawing NURBS
+//
+// Part of the Architektonas Project
+// (C) 2014 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO  WHEN        WHAT
+// ---  ----------  ------------------------------------------------------------
+// JLH  03/20/2014  Created this file
+//
+
+#include "drawsplineaction.h"
+#include "spline.h"
+#include "mathconstants.h"
+#include "painter.h"
+
+
+enum { FIRST_POINT, NEXT_POINT };
+
+
+DrawSplineAction::DrawSplineAction(): state(FIRST_POINT), spline(NULL),
+       shiftWasPressedOnNextPoint(false)
+{
+}
+
+
+DrawSplineAction::~DrawSplineAction()
+{
+}
+
+
+/*virtual*/ void DrawSplineAction::Draw(Painter * painter)
+{
+       painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
+
+       // I think stuff like crosshairs should be done in the DrawingView, tho
+       // (and it's done there now...)
+       if (state == FIRST_POINT)
+       {
+               painter->DrawHandle(p1);
+       }
+       else
+       {
+               painter->DrawLine(p1, p2);
+               painter->DrawHandle(p2);
+
+               Vector v(p1, p2);
+               double absAngle = v.Angle() * RADIANS_TO_DEGREES;
+               double absLength = v.Magnitude();
+               QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
+               text = text.arg(absLength).arg(absAngle);
+               painter->DrawInformativeText(text);
+       }
+}
+
+
+/*virtual*/ void DrawSplineAction::MouseDown(Vector point)
+{
+       // Clear our override...
+       shiftWasPressedOnNextPoint = false;
+
+       if (state == FIRST_POINT)
+               p1 = point;
+       else
+               p2 = point;
+}
+
+
+/*virtual*/ void DrawSplineAction::MouseMoved(Vector point)
+{
+       if (state == FIRST_POINT)
+               p1 = point;
+       else
+               p2 = point;
+}
+
+
+/*virtual*/ void DrawSplineAction::MouseReleased(void)
+{
+       if (state == FIRST_POINT)
+       {
+               p2 = p1;
+               state = NEXT_POINT;
+               spline = NULL;
+       }
+       else if (state == NEXT_POINT)
+       {
+               Spline * oldSpline = spline;
+               // We create the new object here, and then pass it off to the
+               // DrawingView which stuffs it into the document.
+               spline = new Spline(p1, p2.Angle());
+
+               // Connect Lines by default
+               if (oldSpline)
+               {
+                       oldSpline->Connect(spline, 0);
+                       spline->Connect(oldSpline, 1.0);
+               }
+
+               // We don't need no stinkin' sentinels, when we have signals & slots!
+               emit ObjectReady(spline);
+
+               p1 = p2;
+               state = NEXT_POINT;
+       }
+}
+
+
+/*virtual*/ void DrawSplineAction::KeyDown(int key)
+{
+       if ((key == Qt::Key_Shift) && (state == NEXT_POINT))
+       {
+               shiftWasPressedOnNextPoint = true;
+               p1Save = p1;
+               p1 = p2;
+               state = FIRST_POINT;
+               emit NeedRefresh();
+       }
+}
+
+
+/*virtual*/ void DrawSplineAction::KeyReleased(int key)
+{
+       if ((key == Qt::Key_Shift) && shiftWasPressedOnNextPoint)
+       {
+               shiftWasPressedOnNextPoint = false;
+               p2 = p1;
+               p1 = p1Save;
+               state = NEXT_POINT;
+               emit(NeedRefresh());
+       }
+}
+
diff --git a/src/drawsplineaction.h b/src/drawsplineaction.h
new file mode 100644 (file)
index 0000000..a5c7483
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __DRAWSPLINEACTION_H__
+#define __DRAWSPLINEACTION_H__
+
+#include "action.h"
+
+class Spline;
+
+class DrawSplineAction: public Action
+{
+       public:
+               DrawSplineAction();
+               ~DrawSplineAction();
+
+               virtual void Draw(Painter *);
+               virtual void MouseDown(Vector);
+               virtual void MouseMoved(Vector);
+               virtual void MouseReleased(void);
+               virtual void KeyDown(int);
+               virtual void KeyReleased(int);
+
+       private:
+               int state;
+               Spline * spline;
+               Vector p1, p2, p1Save;
+               bool shiftWasPressedOnNextPoint;
+};
+
+#endif // __DRAWSPLINEACTION_H__
+
index f7d5d7c0a493b02c31d07070ef7bcdcc511e0baf..b83adaf02399fcb9e3f89db4dfaea9ebb3b2809f 100644 (file)
@@ -13,7 +13,7 @@ class Dimension;
 //class FILE;
 
 enum ObjectState { OSInactive, OSSelected };
-enum ObjectType { OTNone, OTObject, OTLine, OTCircle, OTArc, OTDimension, OTEllipse, OTContainer };
+enum ObjectType { OTNone, OTObject, OTLine, OTCircle, OTArc, OTDimension, OTEllipse, OTContainer, OTSpline };
 
 class Object
 {
diff --git a/src/spline.cpp b/src/spline.cpp
new file mode 100644 (file)
index 0000000..dd3edc6
--- /dev/null
@@ -0,0 +1,271 @@
+// spline.cpp: NURBS object
+//
+// Part of the Architektonas Project
+// (C) 2014 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO  WHEN        WHAT
+// ---  ----------  ------------------------------------------------------------
+// JLH  03/20/2014  Created this file
+//
+
+#include "spline.h"
+
+#include <QtGui>
+#include "geometry.h"
+#include "painter.h"
+
+#warning "!!! This class is NOT CORRECT !!! FIX !!!"
+Spline::Spline(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r),
+       draggingEdge(false), draggingCenter(false), hitCenter(false), hitSpline(false)
+{
+       type = OTSpline;
+}
+
+
+Spline::~Spline()
+{
+}
+
+
+/*virtual*/ void Spline::Draw(Painter * painter)
+{
+       if (state == OSSelected || hitSpline || hitCenter)
+               painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
+       else
+               painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+
+       // Hatch/Fill...
+//     QBrush brush(Qt::DiagCrossPattern);
+//     brush.setColor(QColor(255, 255, 0));
+//     painter->SetBrush(brush);
+       painter->SetBrush(QBrush(Qt::NoBrush));
+
+       // Draw the object...
+       painter->DrawEllipse(position, radius, radius);
+
+       // & draw handles (if needed)
+       if (state == OSSelected || hitCenter)
+               painter->DrawHandle(position);
+
+       if (state == OSSelected && draggingEdge && objectWasDragged)
+               painter->DrawHandle(dragPoint);
+
+       // If resizing the circle, draw an information panel showing the new radius.
+       if (draggingEdge)
+       {
+               QString text = QObject::tr("Radius: %1\nScale: %2%");
+               text = text.arg(radius, 0, 'd', 4).arg(radius / oldRadius * 100.0, 0, 'd', 0);
+#if 0
+               QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
+               painter->SetPen(pen);
+               painter->SetBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
+               QRectF textRect(10.0, 10.0, 270.0, 70.0);       // x, y, w, h
+               painter->DrawRoundedRect(textRect, 7.0, 7.0);
+
+               textRect.setLeft(textRect.left() + 14);
+               painter->SetFont(*Object::font);
+               pen = QPen(QColor(0x00, 0x5F, 0xDF));
+               painter->SetPen(pen);
+               painter->DrawText(textRect, Qt::AlignVCenter, text);
+#else
+               painter->DrawInformativeText(text);
+#endif
+       }
+}
+
+
+/*virtual*/ Vector Spline::Center(void)
+{
+       return position;
+}
+
+
+/*virtual*/ bool Spline::Collided(Vector point)
+{
+       // Someone told us to fuck off, so we'll fuck off. :-)
+       if (ignoreClicks)
+               return false;
+
+       // We can assume this, since this is a mouse down event here.
+       objectWasDragged = false;
+       HitTest(point);
+
+       // Now that we've done our hit testing on the non-snapped point, snap it if
+       // necessary...
+       if (snapToGrid)
+               point = SnapPointToGrid(point);
+
+       draggingCenter = hitCenter;
+       draggingEdge = hitSpline;
+
+       if (hitCenter || hitSpline)
+       {
+               dragPoint = point;
+               oldState = state;
+               state = OSSelected;
+               oldRadius = radius;
+               return true;
+       }
+
+       // We didn't hit anything, so deselect this object and report failure to hit
+       state = OSInactive;
+       return false;
+}
+
+
+/*virtual*/ bool Spline::PointerMoved(Vector point)
+{
+       if (selectionInProgress)
+       {
+               // Check for whether or not the rect contains this circle
+//             if (selection.normalized().contains(Extents()))
+               if (selection.contains(Extents()))
+                       state = OSSelected;
+               else
+                       state = OSInactive;
+
+               return false;
+       }
+
+       // Hit test tells us what we hit (if anything) through boolean variables. It
+       // also tells us whether or not the state changed.
+       SaveHitState();
+       bool hovered = HitTest(point);
+       needUpdate = HitStateChanged();
+       objectWasDragged = (draggingEdge | draggingCenter);
+
+       if (objectWasDragged)
+               needUpdate = true;
+
+       if (draggingEdge)
+               radius = Vector::Magnitude(point, position);
+       else if (draggingCenter)
+               position = point;
+
+       // Save this point so the rendering code knows where to draw the handle...
+       dragPoint = point;
+       return hovered;
+}
+
+
+/*virtual*/ void Spline::PointerReleased(void)
+{
+       // Mouse went up, so our dragging is done (if any *was* done, that is)
+       draggingEdge = draggingCenter = false;
+       hitCenter = hitSpline = false;
+
+       // If the object was dragged, then revert to the old state.
+       // Otherwise, we were probably just clicked, and want to stay in the selected state.
+       if (objectWasDragged)
+               state = oldState;
+}
+
+
+/*virtual*/ bool Spline::HitTest(Point point)
+{
+//     SaveHitState();
+       hitCenter = hitSpline = false;
+       double length = Vector::Magnitude(position, point);
+//printf("Spline::length = %lf, radius = %lf\n", length, radius);
+//How to translate this into pixels from Document space???
+//Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
+//the caller knows about the zoom factor and all that good kinda crap
+/*
+Document passes in the correct Cartesian coordinates being pointed to by the mouse.
+So all we have to be concerned with is properly scaling our hot zones/handle sizes,
+since we generally *don't* want those to scale with the zoom level. ;-)
+
+What is going on here?
+If we're zoomed out to, say, 50%, & our radius is 10.0 (absolute), then on screen
+the radius will be 5.0. By multiplying the length by the zoom factor, we align our
+pointed at length with our on screen length.
+*/
+       if ((length * Painter::zoom) < 8.0)
+               hitCenter = true;
+//wrong:       else if ((length < (radius + 2.0)) && (length > (radius - 2.0)))
+/*NB: The following should be identical to what we have down below, but it doesn't work out that way... :-P */
+//close, but no        else if (((length * Painter::zoom) < ((radius * Painter::zoom) + 2.0)) && ((length * Painter::zoom) > ((radius * Painter::zoom) - 2.0)))
+//really wrong!        else if (((length * Painter::zoom) < (radius + 2.0)) && ((length * Painter::zoom) > (radius - 2.0)))
+// close again, but sill no    else if (((length * Painter::zoom) < ((radius + 2.0) * Painter::zoom)) && ((length * Painter::zoom) > ((radius - 2.0) * Painter::zoom)))
+       else if ((fabs(length - radius) * Painter::zoom) < 2.0)
+               hitSpline = true;
+
+//     return HitStateChanged();
+       return (hitCenter || hitSpline ? true : false);
+}
+
+
+/*virtual*/ QRectF Spline::Extents(void)
+{
+       return QRectF(QPointF(position.x - radius, position.y - radius), QPointF(position.x + radius, position.y + radius));
+}
+
+
+void Spline::SaveHitState(void)
+{
+       oldHitCenter = hitCenter;
+       oldHitSpline = hitSpline;
+}
+
+
+bool Spline::HitStateChanged(void)
+{
+       if ((hitCenter != oldHitCenter) || (hitSpline != oldHitSpline))
+               return true;
+
+       return false;
+}
+
+
+/*virtual*/ void Spline::Enumerate(FILE * file)
+{
+       fprintf(file, "SPLINE %i (%lf,%lf) %lf\n", layer, position.x, position.y, radius);
+}
+
+
+/*virtual*/ Object * Spline::Copy(void)
+{
+#warning "!!! This doesn't take care of attached Dimensions !!!"
+/*
+This is a real problem. While having a pointer in the Dimension to this line's points
+is fast & easy, it creates a huge problem when trying to replicate an object like this.
+
+Maybe a way to fix that then, is to have reference numbers instead of pointers. That
+way, if you copy them, ... you might still have problems. Because you can't be sure if
+a copy will be persistant or not, you then *definitely* do not want them to have the
+same reference number.
+*/
+       return new Spline(position, radius, parent);
+}
+
+
+/*virtual*/ void Spline::Rotate(Point point, double angle)
+{
+       Point c1 = Geometry::RotatePointAroundPoint(position, point, angle);
+       position = c1;
+}
+
+
+/*virtual*/ void Spline::Mirror(Point p1, Point p2)
+{
+       Point c1 = Geometry::MirrorPointAroundLine(position, p1, p2);
+       position = c1;
+}
+
+
+/*virtual*/ void Spline::Save(void)
+{
+       Object::Save();
+       oldRadius2 = radius;
+}
+
+
+/*virtual*/ void Spline::Restore(void)
+{
+       Object::Restore();
+       radius = oldRadius2;
+}
+
diff --git a/src/spline.h b/src/spline.h
new file mode 100644 (file)
index 0000000..7bfa8e2
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef __SPLINE_H__
+#define __SPLINE_H__
+
+#include "object.h"
+
+class Spline: public Object
+{
+       friend class Geometry;
+
+       public:
+               Spline(Vector, double, Object * p = 0);
+               ~Spline();
+
+               virtual void Draw(Painter *);
+               virtual Vector Center(void);
+               virtual bool Collided(Vector);
+               virtual bool PointerMoved(Vector);
+               virtual void PointerReleased(void);
+               virtual bool HitTest(Point);
+               virtual void Enumerate(FILE *);
+               virtual Object * Copy(void);
+               virtual QRectF Extents(void);
+               virtual void Rotate(Point, double);
+               virtual void Mirror(Point, Point);
+               virtual void Save(void);
+               virtual void Restore(void);
+
+       protected:
+               void SaveHitState(void);
+               bool HitStateChanged(void);
+
+       protected:
+               double radius;                                          // Center is Object::position
+               Vector dragPoint;                                       // Used for rendering edge dragging
+               double oldRadius2;
+
+       private:
+               bool draggingEdge;
+               bool draggingCenter;
+               bool objectWasDragged;
+               bool hitCenter, hitSpline;
+               bool oldHitCenter, oldHitSpline;
+               double oldRadius;
+};
+
+#endif // __SPLINE_H__
+