3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 03/30/2011 Created this file
12 // JLH 04/03/2011 Added information panel (angles) rendering
18 #include "mathconstants.h"
22 Arc::Arc(Vector p1, double r, double a1, double a2, Object * p/*= NULL*/):
23 Object(p1, p), /*type(OTArc),*/ radius(r), startAngle(a1), angleSpan(a2)
25 // This is in the base class, why can't we use the contructor to fill it???
35 /*virtual*/ void Arc::Draw(Painter * painter)
39 if (state == OSSelected)
41 Point p1(cos(startAngle), sin(startAngle));
42 Point p2(cos(startAngle + angleSpan), sin(startAngle + angleSpan));
43 Vector handle2 = (p1 * radius) + position;
44 Vector handle3 = (p2 * radius) + position;
46 if ((hitHandle2 || hitHandle3) && objectWasDragged)
50 // If we rotating, we draw a guideline showing the angle we're
52 Point p3(cos(oldAngle), sin(oldAngle));
53 Vector oldLine = (p3 * (radius * 1.25)) + position;
54 pen = QPen(QColor(0x80, 0x80, 0x80), 1.0, Qt::DashLine);
56 painter->DrawLine((int)position.x, (int)position.y, (int)oldLine.x, (int)oldLine.y);
59 // In rotating and setting the span, we draw a line showing where
60 // we angle/span is that we're setting.
61 pen = QPen(QColor(0x00, 0xC0, 0x80), 1.0, Qt::DashLine);
63 painter->DrawLine((int)position.x, (int)position.y, (int)oldPoint.x, (int)oldPoint.y);
66 // Draw the center point of the arc
67 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
68 painter->DrawHandle(position);
70 // Draw the rotation & span setting handles
71 painter->DrawHandle(handle2);
72 painter->DrawHandle(handle3);
74 // If we're rotating or setting the span, draw an information panel
75 // showing both absolute and relative angles being set.
76 if ((hitHandle2 || hitHandle3 || hitHandle4) && objectWasDragged)
78 double absAngle = (Vector(oldPoint - position).Angle()) * RADIANS_TO_DEGREES;
79 double relAngle = (startAngle >= oldAngle ? startAngle - oldAngle :
80 startAngle - oldAngle + (2.0 * PI)) * RADIANS_TO_DEGREES;
86 text = QObject::tr("Abs ") + QChar(0x2221) + ": %1" + QChar(0x00B0)
87 + QObject::tr("\nRel ") + QChar(0x2221) + ": %2" + QChar(0x00B0);
88 text = text.arg(absAngle, 0, 'd', 4).arg(relAngle, 0, 'd', 4);
92 text = QObject::tr("Abs ") + QChar(0x2221) + ": %1" + QChar(0x00B0)
93 + QObject::tr("\nSpan: %2") + QChar(0x00B0);
94 text = text.arg(absAngle, 0, 'd', 4).arg(angleSpan * RADIANS_TO_DEGREES, 0, 'd', 4);
98 text = QObject::tr("Radius: %1\nScale: %2%");
99 text = text.arg(radius, 0, 'd', 4).arg(radius / oldRadius * 100.0, 0, 'd', 0);
102 pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
103 painter->SetPen(pen);
104 painter->SetBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
105 QRectF textRect(10.0, 10.0, 270.0, 70.0); // x, y, w, h
106 painter->DrawRoundedRect(textRect, 7.0, 7.0);
108 textRect.setLeft(textRect.left() + 14);
109 painter->SetFont(*Object::font);
110 // pen = QPen(QColor(0xDF, 0x5F, 0x00), 1.0, Qt::SolidLine);
111 pen = QPen(QColor(0x00, 0x5F, 0xDF));
112 painter->SetPen(pen);
113 painter->DrawText(textRect, Qt::AlignVCenter, text);
114 painter->SetPen(QPen(QColor(0xDF, 0x5F, 0x00)));
119 pen = QPen(Qt::black, 1.0, Qt::SolidLine);
120 painter->SetPen(pen);
123 painter->DrawArc(position, radius, startAngle, angleSpan);
127 /*virtual*/ Vector Arc::Center(void)
134 We need at least *four* handles for this object:
138 - one for setting the span of the arc
140 We need to think about the intuitive way (if there is any) to grab and
141 manipulate a complex object like this... Need to think, "What should happen when
142 I click here and drag there?"
144 Also: should put the snap logic into the Object base class (as a static method)...
147 /*virtual*/ bool Arc::Collided(Vector point)
149 objectWasDragged = false;
150 Vector v1 = point - position; // Head minus tail (vector points at "point")
152 // Check for collision with various things...
153 hitHandle1 = false; // Moving
154 hitHandle2 = false; // Rotation
155 hitHandle3 = false; // Setting span of the arc
156 hitHandle4 = false; // Resizing
159 the center of the arc
162 The point on a unit circle given an angle a is x = cos(a), y = sin(a)
163 This vector is already unitized, so all we need to do to get our point is to multiply it by
164 radius (to get the length correct) and add it to the center point (to get the correct position).
166 Point p1(cos(startAngle), sin(startAngle));
167 Point p2(cos(startAngle + angleSpan), sin(startAngle + angleSpan));
168 Vector handle2 = (p1 * radius) + position;
169 Vector handle3 = (p2 * radius) + position;
170 double pointerAngle = v1.Angle();
174 if (v1.Magnitude() < 10.0)
177 else if (Vector(handle3 - point).Magnitude() < 10.0)
180 else if (Vector(handle2 - point).Magnitude() < 10.0)
182 // Resize handle (the arc itself)
183 else if ((v1.Magnitude() < radius + 3.0) && (v1.Magnitude() > radius - 3.0)
184 && AngleInArcSpan(pointerAngle))
190 We want the arc to go into OSSelected mode if we click on it but don't drag.
191 If we don't click anywhere on the arc, then we want it to go into OSInactive mode.
192 Otherwise, we hit a handle and we want to modify the object whether it's in
193 OSSelected mode or not.
195 If I, go to S. Can drag, but only handles 2, 3, & 4 (since the center is not highlighted
197 However, if dragging, revert to I once finished.
199 If S, stay in S. Can drag all.
201 So, we know this. If wasDragged is true, then there's a chance we need to revert to I.
204 So. We have a matrix that looks like this:
218 so let's do like this:
220 if (hitHandle1 || hitHandle2 || hitHandle3 || hitHandle4)
226 oldAngle = startAngle;
237 /*virtual*/ void Arc::PointerMoved(Vector point)
239 // The TLC will send these messages if the object is selected but not clicked on.
240 // So we have to be careful with our assumptions here.
241 // This is actually untrue in that case, we need to come up with something better
243 objectWasDragged = true;
246 if (!(hitHandle1 || hitHandle2 || hitHandle3 || hitHandle4))
249 Vector delta = point - oldPoint;
251 if (hitHandle1) // Move arc
255 else if (hitHandle2) // Rotate arc
257 startAngle = Vector(point - position).Angle();
259 else if (hitHandle3) // Set arc span
261 double angle = Vector(point - position).Angle();
263 if (angle < startAngle)
266 angleSpan = angle - startAngle;
268 else if (hitHandle4) // Resize the radius of the arc
270 radius = Vector(point - position).Magnitude();
278 /*virtual*/ void Arc::PointerReleased(void)
280 hitHandle1 = hitHandle2 = hitHandle3 = hitHandle4 = false;
281 // Here we check for just a click: If object was clicked and dragged, then
282 // revert to the old state (OSInactive). Otherwise, keep the new state that
284 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
285 about keeping track of old states...
286 Well, we can't know if it was dragged from Inactive or not, that's the problem. We could make
287 a variable called "needToRevertToInactive" instead
289 I mean, we could write like:
290 if (objectWasDragged && oldState == OSInactive)
293 but this is actually more compact and cleaner.
295 if (objectWasDragged)
300 /*virtual*/ QRectF Arc::Extents(void)
302 #warning "!!! Arc extents not calculated !!!"
308 /*virtual*/ bool Arc::NeedsUpdate(void)
316 /*virtual*/ ObjectType Arc::Type(void)
324 start = 350, span = 20, end = 10, angle = 5
325 angle < start, so angle = 365
327 bool Arc::AngleInArcSpan(double angle)
329 // This should be simple except for a small complication: The start angle plus
330 // the angle span can end up being less than the start angle! So, to check
331 // for this possibility, we check to see if the angle passed in is less than
332 // the start angle and if so, add 2PI to the angle passed in. Then we take the
333 // difference between our start angle and this adjusted angle and see if it
334 // falls within our span.
335 if (angle < startAngle)
338 double passedInSpan = angle - startAngle;
340 return (passedInSpan <= angleSpan ? true : false);
344 /*virtual*/ void Arc::Enumerate(FILE * file)
346 fprintf(file, "ARC (%lf,%lf) %lf, %lf, %lf\n", position.x, position.y, radius, startAngle, angleSpan);