]> Shamusworld >> Repos - architektonas/blob - src/circle.cpp
ed37be3fcd7268faf59cfcb3906316f69a447045
[architektonas] / src / circle.cpp
1 // circle.cpp: Circle object
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  03/28/2011  Created this file
12 // JLH  09/26/2011  Added hover effects
13 // JLH  09/26/2011  Major cleanup of this class
14 //
15
16 #include "circle.h"
17
18 #include <QtGui>
19 #include "geometry.h"
20 #include "painter.h"
21
22
23 Circle::Circle(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r),
24         draggingEdge(false), draggingCenter(false), hitCenter(false), hitCircle(false)
25 {
26         type = OTCircle;
27 }
28
29
30 Circle::~Circle()
31 {
32 }
33
34
35 /*virtual*/ void Circle::Draw(Painter * painter)
36 {
37         if (state == OSSelected || hitCircle || hitCenter)
38                 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
39         else
40                 painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
41
42         // Hatch/Fill...
43 //      QBrush brush(Qt::DiagCrossPattern);
44 //      brush.setColor(QColor(255, 255, 0));
45 //      painter->SetBrush(brush);
46         painter->SetBrush(QBrush(Qt::NoBrush));
47
48         // Draw the object...
49         painter->DrawEllipse(position, radius, radius);
50
51         // & draw handles (if needed)
52         if (state == OSSelected || hitCenter)
53                 painter->DrawHandle(position);
54
55         if (state == OSSelected && draggingEdge && objectWasDragged)
56                 painter->DrawHandle(dragPoint);
57
58         // If resizing the circle, draw an information panel showing the new radius.
59         if (draggingEdge)
60         {
61                 QString text = QObject::tr("Radius: %1\nScale: %2%");
62                 text = text.arg(radius, 0, 'd', 4).arg(radius / oldRadius * 100.0, 0, 'd', 0);
63 #if 0
64                 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
65                 painter->SetPen(pen);
66                 painter->SetBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
67                 QRectF textRect(10.0, 10.0, 270.0, 70.0);       // x, y, w, h
68                 painter->DrawRoundedRect(textRect, 7.0, 7.0);
69
70                 textRect.setLeft(textRect.left() + 14);
71                 painter->SetFont(*Object::font);
72                 pen = QPen(QColor(0x00, 0x5F, 0xDF));
73                 painter->SetPen(pen);
74                 painter->DrawText(textRect, Qt::AlignVCenter, text);
75 #else
76                 painter->DrawInformativeText(text);
77 #endif
78         }
79 }
80
81
82 /*virtual*/ Vector Circle::Center(void)
83 {
84         return position;
85 }
86
87
88 /*virtual*/ bool Circle::Collided(Vector point)
89 {
90         // Someone told us to fuck off, so we'll fuck off. :-)
91         if (ignoreClicks)
92                 return false;
93
94         // We can assume this, since this is a mouse down event here.
95         objectWasDragged = false;
96         HitTest(point);
97
98         // Now that we've done our hit testing on the non-snapped point, snap it if
99         // necessary...
100         if (snapToGrid)
101                 point = SnapPointToGrid(point);
102
103         if (snapPointIsValid)
104                 point = snapPoint;
105
106         draggingCenter = hitCenter;
107         draggingEdge = hitCircle;
108
109         if (hitCenter || hitCircle)
110         {
111                 dragPoint = point;
112                 oldState = state;
113                 state = OSSelected;
114                 oldRadius = radius;
115                 return true;
116         }
117
118         // We didn't hit anything, so deselect this object and report failure to hit
119         state = OSInactive;
120         return false;
121 }
122
123
124 /*virtual*/ bool Circle::PointerMoved(Vector point)
125 {
126         if (selectionInProgress)
127         {
128                 // Check for whether or not the rect contains this circle
129 //              if (selection.normalized().contains(Extents()))
130                 if (selection.contains(Extents()))
131                         state = OSSelected;
132                 else
133                         state = OSInactive;
134
135                 return false;
136         }
137
138 /*
139 Note that we can separate this out in the TLC, and it would probably make more sense
140 to do it there as then we can be assured that all hit testing is done before we do
141 any snapping. !!! FIX !!!
142 */
143         // Hit test tells us what we hit (if anything) through boolean variables. It
144         // also tells us whether or not the state changed.
145         SaveHitState();
146         bool hovered = HitTest(point);
147         needUpdate = HitStateChanged();
148
149         if (snapToGrid)
150                 point = SnapPointToGrid(point);
151
152         if (snapPointIsValid)
153                 point = snapPoint;
154
155         objectWasDragged = (draggingEdge | draggingCenter);
156
157         if (objectWasDragged)
158                 needUpdate = true;
159
160         if (draggingEdge)
161                 radius = Vector::Magnitude(point, position);
162         else if (draggingCenter)
163                 position = point;
164
165         // Save this point so the rendering code knows where to draw the handle...
166         dragPoint = point;
167         return hovered;
168 }
169
170
171 /*virtual*/ void Circle::PointerReleased(void)
172 {
173         // Mouse went up, so our dragging is done (if any *was* done, that is)
174         draggingEdge = draggingCenter = false;
175         hitCenter = hitCircle = false;
176
177         // If the object was dragged, then revert to the old state.
178         // Otherwise, we were probably just clicked, and want to stay in the selected state.
179         if (objectWasDragged)
180                 state = oldState;
181 }
182
183
184 /*virtual*/ bool Circle::HitTest(Point point)
185 {
186 //      SaveHitState();
187         hitCenter = hitCircle = false;
188         double length = Vector::Magnitude(position, point);
189 //printf("Circle::length = %lf, radius = %lf\n", length, radius);
190 //How to translate this into pixels from Document space???
191 //Maybe we need to pass a scaling factor in here from the caller? That would make
192 //sense, as the caller knows about the zoom factor and all that good kinda crap
193 /*
194 Document passes in the correct Cartesian coordinates being pointed to by the mouse.
195 So all we have to be concerned with is properly scaling our hot zones/handle sizes,
196 since we generally *don't* want those to scale with the zoom level. ;-)
197
198 What is going on here?
199 If we're zoomed out to, say, 50%, & our radius is 10.0 (absolute), then on screen
200 the radius will be 5.0. By multiplying the length by the zoom factor, we align our
201 pointed at length with our on screen length.
202 */
203         if ((length * Painter::zoom) < 8.0)
204         {
205                 hitCenter = true;
206
207                 // Make sure we don't try to snap to ourselves...!
208                 if (!draggingCenter)
209                 {
210                         snapPoint = position;
211                         snapPointIsValid = true;
212                 }
213         }
214 //wrong:        else if ((length < (radius + 2.0)) && (length > (radius - 2.0)))
215 /*NB: The following should be identical to what we have down below, but it doesn't work out that way... :-P */
216 //close, but no else if (((length * Painter::zoom) < ((radius * Painter::zoom) + 2.0)) && ((length * Painter::zoom) > ((radius * Painter::zoom) - 2.0)))
217 //really wrong! else if (((length * Painter::zoom) < (radius + 2.0)) && ((length * Painter::zoom) > (radius - 2.0)))
218 // close again, but sill no     else if (((length * Painter::zoom) < ((radius + 2.0) * Painter::zoom)) && ((length * Painter::zoom) > ((radius - 2.0) * Painter::zoom)))
219         else if ((fabs(length - radius) * Painter::zoom) < 2.0)
220                 hitCircle = true;
221
222 //      return HitStateChanged();
223         return (hitCenter || hitCircle ? true : false);
224 }
225
226
227 /*virtual*/ QRectF Circle::Extents(void)
228 {
229         return QRectF(QPointF(position.x - radius, position.y - radius), QPointF(position.x + radius, position.y + radius));
230 }
231
232
233 void Circle::SaveHitState(void)
234 {
235         oldHitCenter = hitCenter;
236         oldHitCircle = hitCircle;
237 }
238
239
240 bool Circle::HitStateChanged(void)
241 {
242         if ((hitCenter != oldHitCenter) || (hitCircle != oldHitCircle))
243                 return true;
244
245         return false;
246 }
247
248
249 /*virtual*/ void Circle::Enumerate(FILE * file)
250 {
251         fprintf(file, "CIRCLE %i (%lf,%lf) %lf\n", layer, position.x, position.y, radius);
252 }
253
254
255 /*virtual*/ Object * Circle::Copy(void)
256 {
257 #warning "!!! This doesn't take care of attached Dimensions !!!"
258 /*
259 This is a real problem. While having a pointer in the Dimension to this line's points
260 is fast & easy, it creates a huge problem when trying to replicate an object like this.
261
262 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
263 way, if you copy them, ... you might still have problems. Because you can't be sure if
264 a copy will be persistant or not, you then *definitely* do not want them to have the
265 same reference number.
266 */
267         return new Circle(position, radius, parent);
268 }
269
270
271 /*virtual*/ void Circle::Rotate(Point point, double angle)
272 {
273         Point c1 = Geometry::RotatePointAroundPoint(position, point, angle);
274         position = c1;
275 }
276
277
278 /*virtual*/ void Circle::Mirror(Point p1, Point p2)
279 {
280         Point c1 = Geometry::MirrorPointAroundLine(position, p1, p2);
281         position = c1;
282 }
283
284
285 /*virtual*/ void Circle::Save(void)
286 {
287         Object::Save();
288         oldRadius2 = radius;
289 }
290
291
292 /*virtual*/ void Circle::Restore(void)
293 {
294         Object::Restore();
295         radius = oldRadius2;
296 }
297