+// 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;
+}
+