- 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
- 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.
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]
src/drawcircleaction.h \
src/drawdimensionaction.h \
src/drawlineaction.h \
+ src/drawsplineaction.h \
src/drawtextaction.h \
src/ellipse.h \
src/fileio.h \
src/painter.h \
src/rotateaction.h \
src/settingsdialog.h \
+ src/spline.h \
src/text.h \
src/trimaction.h \
src/vector.h
src/drawcircleaction.cpp \
src/drawdimensionaction.cpp \
src/drawlineaction.cpp \
+ src/drawsplineaction.cpp \
src/drawtextaction.cpp \
src/ellipse.cpp \
src/fileio.cpp \
src/painter.cpp \
src/rotateaction.cpp \
src/settingsdialog.cpp \
+ src/spline.cpp \
src/text.cpp \
src/trimaction.cpp \
src/vector.cpp
<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>
#include "drawcircleaction.h"
#include "drawdimensionaction.h"
#include "drawlineaction.h"
+#include "drawsplineaction.h"
#include "fileio.h"
#include "generaltab.h"
#include "geometry.h"
}
+void ApplicationWindow::AddSplineTool(void)
+{
+ ClearUIToolStatesExcept(addSplineAct);
+ SetInternalToolStates();
+}
+
+
void ApplicationWindow::ZoomInTool(void)
{
double zoomFactor = 2.0;
if (exception != addPolygonAct)
addPolygonAct->setChecked(false);
+ if (exception != addSplineAct)
+ addSplineAct->setChecked(false);
+
if (exception != deleteAct)
deleteAct->setChecked(false);
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);
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()));
menu->addAction(addCircleAct);
menu->addAction(addArcAct);
menu->addAction(addPolygonAct);
+ menu->addAction(addSplineAct);
menu->addAction(addDimensionAct);
menu->addSeparator();
menu->addAction(settingsAct);
toolbar->addAction(addCircleAct);
toolbar->addAction(addArcAct);
toolbar->addAction(addPolygonAct);
+ toolbar->addAction(addSplineAct);
toolbar->addAction(addDimensionAct);
spinbox->setRange(4, 256);
void AddCircleTool(void);
void AddArcTool(void);
void AddPolygonTool(void);
+ void AddSplineTool(void);
void ZoomInTool(void);
void ZoomOutTool(void);
void HelpAbout(void);
QAction * addCircleAct;
QAction * addArcAct;
QAction * addPolygonAct;
+ QAction * addSplineAct;
QAction * aboutAct;
QAction * rotateAct;
QAction * zoomInAct;
--- /dev/null
+// 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());
+ }
+}
+
--- /dev/null
+#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__
+
//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
{
--- /dev/null
+// 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;
+}
+
--- /dev/null
+#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__
+