// (C) 2011 Underground Software
// See the README and GPLv3 files for licensing and warranty information
//
-// JLH = James L. Hammons <jlhamm@acm.org>
+// JLH = James Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
// --- ---------- ------------------------------------------------------------
// JLH 03/28/2011 Created this file
+// JLH 09/26/2011 Added hover effects
+// JLH 09/26/2011 Major cleanup of this class
//
#include "circle.h"
#include <QtGui>
+#include "painter.h"
+
Circle::Circle(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r),
- dragging(false), draggingHandle1(false), draggingHandle2(false)//, needUpdate(false)
+ draggingEdge(false), draggingCenter(false), hitCenter(false), hitCircle(false)
{
+ type = OTCircle;
}
+
Circle::~Circle()
{
}
-/*virtual*/ void Circle::Draw(QPainter * painter)
+
+/*virtual*/ void Circle::Draw(Painter * painter)
{
- if (state == OSSelected)
- painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
+ if (state == OSSelected || hitCircle || hitCenter)
+ painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
else
- painter->setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+ 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);
-// if (draggingHandle1)
- if (state == OSSelected)
- painter->drawEllipse(QPointF(position.x, position.y), 4.0, 4.0);
+ // & draw handles (if needed)
+ if (state == OSSelected || hitCenter)
+ painter->DrawHandle(position);
-// if (draggingHandle2)
-// if (state == OSSelected)
-// painter->drawEllipse(QPointF(endpoint.x, endpoint.y), 4.0, 4.0);
- if (state == OSSelected && dragging)
- painter->drawEllipse(QPointF(oldPoint.x, oldPoint.y), 4.0, 4.0);
+ if (state == OSSelected && draggingEdge && objectWasDragged)
+ painter->DrawHandle(dragPoint);
- painter->drawEllipse(QPointF(position.x, position.y), radius, radius);
+ // 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 Circle::Center(void)
{
return position;
}
+
/*virtual*/ bool Circle::Collided(Vector point)
{
+ // We can assume this, since this is a mouse down event here.
objectWasDragged = false;
- Vector v1 = position - point;
+ HitTest(point);
- if (state == OSInactive)
- {
-//printf("Circle: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
-//printf(" v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
-//printf(" point = %lf,%lf,%lf; p1 = %lf,%lf,%lf; p2 = %lf,%lf,%lf\n", point.x, point.y, point.z, position.x, position.y, position.z, endpoint.x, endpoint.y, endpoint.z);
-//printf(" \n", );
-//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
- if (v1.Magnitude() < 10.0)
- {
- oldState = state;
- state = OSSelected;
- oldPoint = position; //maybe "position"?
- draggingHandle1 = true;
- return true;
- }
- else if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
- {
- oldState = state;
- state = OSSelected;
- oldPoint = point;
- dragging = true;
- return true;
- }
- }
- else if (state == OSSelected)
+ // 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 = hitCircle;
+
+ if (hitCenter || hitCircle)
{
- // Here we test for collision with handles as well! (SOON!)
-/*
-Like so:
- if (v1.Magnitude() < 2.0) // Handle #1
- else if (v2.Magnitude() < 2.0) // Handle #2
-*/
- if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
- {
- oldState = state;
-// state = OSInactive;
- oldPoint = point;
- dragging = true;
- return true;
- }
+ 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*/ void Circle::PointerMoved(Vector point)
{
- objectWasDragged = true;
-
- if (dragging)
+ if (selectionInProgress)
{
- // Here we need to check whether or not we're dragging a handle or the object itself...
-// Vector delta = point - oldPoint;
-
-// position += delta;
-// endpoint += delta;
- radius = Vector(point - position).Magnitude();
+ // Check for whether or not the rect contains this circle
+// if (selection.normalized().contains(Extents()))
+ if (selection.contains(Extents()))
+ state = OSSelected;
+ else
+ state = OSInactive;
- oldPoint = point;
- needUpdate = true;
+ return;
}
- else if (draggingHandle1)
- {
- Vector delta = point - oldPoint;
- position += delta;
- oldPoint = point;
- needUpdate = true;
- }
-/* else if (draggingHandle2)
- {
- Vector delta = point - oldPoint;
- endpoint += delta;
+ // Hit test tells us what we hit (if anything) through boolean variables. It
+ // also tells us whether or not the state changed.
+ SaveHitState();
+ HitTest(point);
+ needUpdate = HitStateChanged();
+ objectWasDragged = (draggingEdge | draggingCenter);
- oldPoint = point;
+ if (objectWasDragged)
needUpdate = true;
- }*/
- else
- needUpdate = false;
+
+ 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;
}
+
/*virtual*/ void Circle::PointerReleased(void)
{
- dragging = false;
- draggingHandle1 = false;
- draggingHandle2 = false;
-
- // Here we check for just a click: If object was clicked and dragged, then
- // revert to the old state (OSInactive). Otherwise, keep the new state that
- // we set.
-/*Maybe it would be better to just check for "object was dragged" state and not have to worry
-about keeping track of old states...
-*/
+ // Mouse went up, so our dragging is done (if any *was* done, that is)
+ draggingEdge = draggingCenter = false;
+ hitCenter = hitCircle = 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;
}
-#if 0
-/*virtual*/ bool Circle::NeedsUpdate(void)
+
+/*virtual*/ bool Circle::HitTest(Point point)
+{
+// SaveHitState();
+ hitCenter = hitCircle = false;
+ double length = Vector::Magnitude(position, point);
+//printf("Circle::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)
+ hitCircle = true;
+
+// return HitStateChanged();
+ return (hitCenter || hitCircle ? true : false);
+}
+
+
+/*virtual*/ QRectF Circle::Extents(void)
{
- return needUpdate;
+ return QRectF(QPointF(position.x - radius, position.y - radius), QPointF(position.x + radius, position.y + radius));
}
-#endif
+
+
+void Circle::SaveHitState(void)
+{
+ oldHitCenter = hitCenter;
+ oldHitCircle = hitCircle;
+}
+
+
+bool Circle::HitStateChanged(void)
+{
+ if ((hitCenter != oldHitCenter) || (hitCircle != oldHitCircle))
+ return true;
+
+ return false;
+}
+
+
+/*virtual*/ void Circle::Enumerate(FILE * file)
+{
+ fprintf(file, "CIRCLE %i (%lf,%lf) %lf\n", layer, position.x, position.y, radius);
+}
+
+
+/*virtual*/ Object * Circle::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 Circle(position, radius, parent);
+}
+