// --- ---------- ------------------------------------------------------------
// JLH 03/30/2011 Created this file
// JLH 04/03/2011 Added information panel (angles) rendering
+// JLH 08/16/2013 Added continuous user feedack like for Line and Circle
//
#include "arc.h"
Arc::Arc(Vector p1, double r, double a1, double a2, Object * p/*= NULL*/):
- Object(p1, p), /*type(OTArc),*/ radius(r), startAngle(a1), angleSpan(a2)
+ Object(p1, p), /*type(OTArc),*/ radius(r), startAngle(a1), angleSpan(a2),
+ draggingCenter(false), draggingEdge(false), draggingRotate(false),
+ draggingSpan(false),
+ hitCenter(false), hitArc(false), hitRotate(false), hitSpan(false)
{
// This is in the base class, why can't we use the contructor to fill it???
type = OTArc;
+ state = OSInactive;
}
/*virtual*/ void Arc::Draw(Painter * painter)
{
QPen pen;
+ painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
- if (state == OSSelected)
- {
- Point p1(cos(startAngle), sin(startAngle));
- Point p2(cos(startAngle + angleSpan), sin(startAngle + angleSpan));
- Vector handle2 = (p1 * radius) + position;
- Vector handle3 = (p2 * radius) + position;
+ Point p1(cos(startAngle), sin(startAngle));
+ Point p2(cos(startAngle + angleSpan), sin(startAngle + angleSpan));
+ Vector handle2 = (p1 * radius) + position;
+ Vector handle3 = (p2 * radius) + position;
- if ((hitHandle2 || hitHandle3) && objectWasDragged)
- {
- if (hitHandle2)
- {
- // If we rotating, we draw a guideline showing the angle we're
- // moving it from.
- Point p3(cos(oldAngle), sin(oldAngle));
- Vector oldLine = (p3 * (radius * 1.25)) + position;
- pen = QPen(QColor(0x80, 0x80, 0x80), 1.0, Qt::DashLine);
- painter->SetPen(pen);
- painter->DrawLine((int)position.x, (int)position.y, (int)oldLine.x, (int)oldLine.y);
- }
-
- // In rotating and setting the span, we draw a line showing where
- // we angle/span is that we're setting.
- pen = QPen(QColor(0x00, 0xC0, 0x80), 1.0, Qt::DashLine);
- painter->SetPen(pen);
- painter->DrawLine((int)position.x, (int)position.y, (int)oldPoint.x, (int)oldPoint.y);
- }
+ if ((state == OSSelected) || ((state == OSInactive) && hitRotate))
+ painter->DrawHandle(handle2);
+
+ if ((state == OSSelected) || ((state == OSInactive) && hitSpan))
+ painter->DrawHandle(handle3);
- // Draw the center point of the arc
- painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
+ if ((state == OSSelected) || ((state == OSInactive) && hitCenter))
painter->DrawHandle(position);
- // Draw the rotation & span setting handles
- painter->DrawHandle(handle2);
- painter->DrawHandle(handle3);
+ if ((state == OSInactive) && !hitArc)
+ painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+
+ painter->DrawArc(position, radius, startAngle, angleSpan);
- // If we're rotating or setting the span, draw an information panel
- // showing both absolute and relative angles being set.
- if ((hitHandle2 || hitHandle3 || hitHandle4) && objectWasDragged)
+ if (draggingRotate || draggingSpan)
+ {
+ if (draggingRotate)
{
- double absAngle = (Vector(oldPoint - position).Angle()) * RADIANS_TO_DEGREES;
- double relAngle = (startAngle >= oldAngle ? startAngle - oldAngle :
- startAngle - oldAngle + (2.0 * PI)) * RADIANS_TO_DEGREES;
-
- QString text;
-
- if (hitHandle2)
- {
- text = QObject::tr("Abs ") + QChar(0x2221) + ": %1" + QChar(0x00B0)
- + QObject::tr("\nRel ") + QChar(0x2221) + ": %2" + QChar(0x00B0);
- text = text.arg(absAngle, 0, 'd', 4).arg(relAngle, 0, 'd', 4);
- }
- else if (hitHandle3)
- {
- text = QObject::tr("Abs ") + QChar(0x2221) + ": %1" + QChar(0x00B0)
- + QObject::tr("\nSpan: %2") + QChar(0x00B0);
- text = text.arg(absAngle, 0, 'd', 4).arg(angleSpan * RADIANS_TO_DEGREES, 0, 'd', 4);
- }
- else if (hitHandle4)
- {
- text = QObject::tr("Radius: %1\nScale: %2%");
- text = text.arg(radius, 0, 'd', 4).arg(radius / oldRadius * 100.0, 0, 'd', 0);
- }
-
- 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(0xDF, 0x5F, 0x00), 1.0, Qt::SolidLine);
- pen = QPen(QColor(0x00, 0x5F, 0xDF));
+ // If we rotating, we draw a guideline showing the angle we're
+ // moving it from.
+ Point p3(cos(oldAngle), sin(oldAngle));
+ Vector oldLine = (p3 * (radius * 1.25)) + position;
+ pen = QPen(QColor(0x80, 0x80, 0x80), 1.0, Qt::DashLine);
painter->SetPen(pen);
- painter->DrawText(textRect, Qt::AlignVCenter, text);
- painter->SetPen(QPen(QColor(0xDF, 0x5F, 0x00)));
+ painter->DrawLine((int)position.x, (int)position.y, (int)oldLine.x, (int)oldLine.y);
}
+
+ // In rotating and setting the span, we draw a line showing where
+ // we angle/span is that we're setting.
+ pen = QPen(QColor(0x00, 0xC0, 0x80), 1.0, Qt::DashLine);
+ painter->SetPen(pen);
+ painter->DrawLine((int)position.x, (int)position.y, (int)oldPoint.x, (int)oldPoint.y);
}
- else
+
+ // If we're rotating or setting the span, draw an information panel
+ // showing both absolute and relative angles being set.
+ if (draggingRotate || draggingSpan || draggingEdge)
{
- pen = QPen(Qt::black, 1.0, Qt::SolidLine);
+ double absAngle = (Vector(oldPoint - position).Angle()) * RADIANS_TO_DEGREES;
+ double relAngle = (startAngle >= oldAngle ? startAngle - oldAngle :
+ startAngle - oldAngle + (2.0 * PI)) * RADIANS_TO_DEGREES;
+
+ QString text;
+
+ if (draggingRotate)
+ {
+ text = QObject::tr("Abs ") + QChar(0x2221) + ": %1" + QChar(0x00B0)
+ + QObject::tr("\nRel ") + QChar(0x2221) + ": %2" + QChar(0x00B0);
+ text = text.arg(absAngle, 0, 'd', 4).arg(relAngle, 0, 'd', 4);
+ }
+ else if (draggingSpan)
+ {
+ text = QObject::tr("Abs ") + QChar(0x2221) + ": %1" + QChar(0x00B0)
+ + QObject::tr("\nSpan: %2") + QChar(0x00B0);
+ text = text.arg(absAngle, 0, 'd', 4).arg(angleSpan * RADIANS_TO_DEGREES, 0, 'd', 4);
+ }
+ else if (draggingEdge)
+ {
+ text = QObject::tr("Radius: %1\nScale: %2%");
+ text = text.arg(radius, 0, 'd', 4).arg(radius / oldRadius * 100.0, 0, 'd', 0);
+ }
+
+ 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);
- painter->DrawArc(position, radius, startAngle, angleSpan);
-// painter->DrawRect(Extents());
+ textRect.setLeft(textRect.left() + 14);
+ painter->SetFont(*Object::font);
+ pen = QPen(QColor(0x00, 0x5F, 0xDF));
+ painter->SetPen(pen);
+ painter->DrawText(textRect, Qt::AlignVCenter, text);
+ }
}
/*virtual*/ bool Arc::Collided(Vector point)
{
objectWasDragged = false;
- Vector v1 = point - position; // Head minus tail (vector points at "point")
+// Vector v1 = point - position; // Head minus tail (vector points at "point")
+#if 1
+ bool hitSomething = HitTest(point);
+ draggingCenter = hitCenter;
+ draggingEdge = hitArc;
+ draggingRotate = hitRotate;
+ draggingSpan = hitSpan;
+#else
// Check for collision with various things...
hitHandle1 = false; // Moving
hitHandle2 = false; // Rotation
This vector is already unitized, so all we need to do to get our point is to multiply it by
radius (to get the length correct) and add it to the center point (to get the correct position).
*/
+ Vector v1 = point - position; // Head minus tail (vector points at "point")
Point p1(cos(startAngle), sin(startAngle));
Point p2(cos(startAngle + angleSpan), sin(startAngle + angleSpan));
Vector handle2 = (p1 * radius) + position;
&& AngleInArcSpan(pointerAngle))
hitHandle4 = true;
#endif
+#endif
/*
State Management:
so let's do like this:
*/
- if (hitHandle1 || hitHandle2 || hitHandle3 || hitHandle4)
+// if (hitCenter || hitArc || hitRotate || hitSpan)
+ if (hitSomething)
{
oldState = state;
state = OSSelected;
oldPoint = position;
oldAngle = startAngle;
oldRadius = radius;
-
return true;
}
/*virtual*/ void Arc::PointerMoved(Vector point)
{
+// one other thing to check here for is if a modifier key is being held as well,
+// to allow for multi-selection
if (selectionInProgress)
{
// Check for whether or not the rect contains this circle
- if (selection.normalized().contains(Extents()))
+// if (selection.normalized().contains(Extents()))
+ if (selection.contains(Extents()))
state = OSSelected;
else
state = OSInactive;
// So we have to be careful with our assumptions here.
// This is actually untrue in that case, we need to come up with something better
// here...
- objectWasDragged = true;
- needUpdate = false;
+// objectWasDragged = true;
+// needUpdate = false;
+ SaveHitState();
+ HitTest(point);
+ needUpdate = HitStateChanged();
+ objectWasDragged = (draggingCenter | draggingEdge | draggingRotate | draggingSpan);
- if (!(hitHandle1 || hitHandle2 || hitHandle3 || hitHandle4))
- return;
+ if (objectWasDragged)
+ needUpdate = true;
+// if (!(hitHandle1 || hitHandle2 || hitHandle3 || hitHandle4))
+// return;
- Vector delta = point - oldPoint;
+// Vector delta = point - oldPoint;
- if (hitHandle1) // Move arc
- {
- position += delta;
- }
- else if (hitHandle2) // Rotate arc
+ if (draggingCenter)
+ position = point;
+ else if (draggingEdge)
+ radius = Vector::Magnitude(point, position);
+ else if (draggingRotate)
{
startAngle = Vector(point - position).Angle();
}
- else if (hitHandle3) // Set arc span
+ else if (draggingSpan)
{
double angle = Vector(point - position).Angle();
angleSpan = angle - startAngle;
}
- else if (hitHandle4) // Resize the radius of the arc
- {
- radius = Vector(point - position).Magnitude();
- }
+ // Why save this? For rendering code?
oldPoint = point;
- needUpdate = true;
+// needUpdate = true;
}
/*virtual*/ void Arc::PointerReleased(void)
{
- hitHandle1 = hitHandle2 = hitHandle3 = hitHandle4 = 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...
-Well, we can't know if it was dragged from Inactive or not, that's the problem.
-We could make a variable called "needToRevertToInactive" instead
+ // Mouse went up, so our dragging is done (if any *was* done, that is)
+// hitHandle1 = hitHandle2 = hitHandle3 = hitHandle4 = false;
+ draggingCenter = draggingEdge = draggingRotate = draggingSpan = false;
+ hitCenter = hitArc = hitRotate = hitSpan = false;
-I mean, we could write like:
- if (objectWasDragged && oldState == OSInactive)
- state = OSInactive;
-
-but this is actually more compact and cleaner.
-*/
+ // 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 Arc::HitTest(Point point)
+{
+ hitCenter = hitArc = hitRotate = hitSpan = false;
+
+/*
+What we have:
+the center of the arc
+the starting angle
+the span of the arc
+The point on a unit circle given an angle a is x = cos(a), y = sin(a)
+This vector is already unitized, so all we need to do to get our point is to
+multiply it by radius (to get the length correct) and add it to the center
+point (to get the correct position).
+*/
+ Vector v1(point, position); // Head minus tail (vector points at "point")
+ Point p1(cos(startAngle), sin(startAngle));
+ Point p2(cos(startAngle + angleSpan), sin(startAngle + angleSpan));
+ Vector handle2 = (p1 * radius) + position;
+ Vector handle3 = (p2 * radius) + position;
+ double pointerAngle = v1.Angle();
+ double length = v1.Magnitude();
+
+#if 0
+ if (v1.Magnitude() < 10.0)
+ hitCenter = true;
+ else if (Vector(handle3 - point).Magnitude() < 10.0)
+ hitSpan = true;
+ else if (Vector(handle2 - point).Magnitude() < 10.0)
+ hitRotate = true;
+ else if ((v1.Magnitude() < radius + 3.0) && (v1.Magnitude() > radius - 3.0)
+ && AngleInArcSpan(pointerAngle))
+ hitArc = true;
+#else
+ if ((length * Painter::zoom) < 8.0)
+ hitCenter = true;
+ else if (((fabs(length - radius) * Painter::zoom) < 2.0)
+ && AngleInArcSpan(pointerAngle))
+ hitArc = true;
+ else if ((Vector::Magnitude(handle2, point) * Painter::zoom) < 8.0)
+ hitRotate = true;
+ else if ((Vector::Magnitude(handle3, point) * Painter::zoom) < 8.0)
+ hitSpan = true;
+#endif
+
+ return (hitCenter || hitArc || hitRotate || hitSpan ? true : false);
+}
+
+
/*virtual*/ QRectF Arc::Extents(void)
{
double start = startAngle;
}
-#if 0
-/*virtual*/ bool Arc::NeedsUpdate(void)
-{
- return needUpdate;
-}
-#endif
-
-
/*
start = 350, span = 20, end = 10, angle = 5
angle < start, so angle = 365
}
+void Arc::SaveHitState(void)
+{
+ oldHitCenter = hitCenter;
+ oldHitArc = hitArc;
+ oldHitRotate = hitRotate;
+ oldHitSpan = hitSpan;
+}
+
+
+bool Arc::HitStateChanged(void)
+{
+ if ((hitCenter != oldHitCenter)
+ || (hitArc != oldHitArc)
+ || (hitRotate != oldHitRotate)
+ || (hitSpan != oldHitSpan))
+ return true;
+
+ return false;
+}
+
+
/*virtual*/ void Arc::Enumerate(FILE * file)
{
fprintf(file, "ARC (%lf,%lf) %lf, %lf, %lf\n", position.x, position.y, radius, startAngle, angleSpan);
Vector current(point2 - point1);
Vector v = current.Unit() * length;
Vector v2 = point1 + v;
-// painter->DrawLine((int)point1.x, (int)point1.y, (int)v2.x, (int)v2.y);
painter->DrawLine(point1, v2);
if (current.Magnitude() > length)
{
painter->SetPen(QPen(QColor(128, 0, 0), 1.0, Qt::DashLine));
-// painter->DrawLine((int)v2.x, (int)v2.y, (int)point2.x, (int)point2.y);
painter->DrawLine(v2, point2);
}
}
-// Problem: when drawing at large zoom levels, this throws away precision thus
-// causing the line to rendered too short. !!! FIX !!! [DONE]
else
-// painter->DrawLine((int)position.x, (int)position.y, (int)endpoint.x, (int)endpoint.y);
painter->DrawLine(position, endpoint);
- // If we're rotating or setting the span, draw an information panel
- // showing both absolute and relative angles being set.
+ // If we're dragging an endpoint, draw an information panel showing both
+ // the length and angle being set.
if (draggingHandle1 || draggingHandle2)
{
double absAngle = (Vector(endpoint - position).Angle()) * RADIANS_TO_DEGREES;
-// double relAngle = (startAngle >= oldAngle ? startAngle - oldAngle :
-// startAngle - oldAngle + (2.0 * PI)) * RADIANS_TO_DEGREES;
double absLength = Vector(position - endpoint).Magnitude();
QString text;
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
+ QRectF textRect(10.0, 10.0, 270.0, 70.0); // x, y, w, h (in Qt coords)
painter->DrawRoundedRect(textRect, 7.0, 7.0);
textRect.setLeft(textRect.left() + 14);
painter->SetFont(*Object::font);
-// pen = QPen(QColor(0xDF, 0x5F, 0x00), 1.0, Qt::SolidLine);
pen = QPen(QColor(0x00, 0x5F, 0xDF));
painter->SetPen(pen);
painter->DrawText(textRect, Qt::AlignVCenter, text);
-// painter->SetPen(QPen(QColor(0xDF, 0x5F, 0x00)));
}
}
{
// We can assume this, since this is a mouse down event here.
objectWasDragged = false;
- SaveState();
+// SaveHitState();
HitTest(point);
-// return StateChanged();
+// return HitStateChanged();
// this is shite. this should be checked for in the Container, not here!
// If we're part of a non-top-level container, send this signal to it
// Hit test tells us what we hit (if anything) through boolean variables. (It
// also tells us whether or not the state changed. --not any more)
- SaveState();
+ SaveHitState();
HitTest(point);
- needUpdate = StateChanged();
+ needUpdate = HitStateChanged();
objectWasDragged = (draggingLine | draggingHandle1 | draggingHandle2);
/*virtual*/ bool Line::HitTest(Point point)
{
-// SaveState();
+// SaveHitState();
hitPoint1 = hitPoint2 = hitLine = false;
Vector lineSegment = endpoint - position;
else if ((distance * Painter::zoom) < 5.0)
hitLine = true;
- return (hitPoint1 | hitPoint2 | hitLine ? true : false);
-// return StateChanged();
+ return (hitPoint1 || hitPoint2 || hitLine ? true : false);
+// return HitStateChanged();
}
}
-void Line::SaveState(void)
+void Line::SaveHitState(void)
{
oldHitPoint1 = hitPoint1;
oldHitPoint2 = hitPoint2;
}
-bool Line::StateChanged(void)
+bool Line::HitStateChanged(void)
{
if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
return true;