]> Shamusworld >> Repos - architektonas/blob - src/circle.cpp
093f53395834bd3472a05a3242ad148bd1cc8759
[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         // Hit test tells us what we hit (if anything) through boolean variables. It
139         // also tells us whether or not the state changed.
140         SaveHitState();
141         bool hovered = HitTest(point);
142         needUpdate = HitStateChanged();
143
144         if (snapToGrid)
145                 point = SnapPointToGrid(point);
146
147         if (snapPointIsValid)
148                 point = snapPoint;
149
150         objectWasDragged = (draggingEdge | draggingCenter);
151
152         if (objectWasDragged)
153                 needUpdate = true;
154
155         if (draggingEdge)
156                 radius = Vector::Magnitude(point, position);
157         else if (draggingCenter)
158                 position = point;
159
160         // Save this point so the rendering code knows where to draw the handle...
161         dragPoint = point;
162         return hovered;
163 }
164
165
166 /*virtual*/ void Circle::PointerReleased(void)
167 {
168         // Mouse went up, so our dragging is done (if any *was* done, that is)
169         draggingEdge = draggingCenter = false;
170         hitCenter = hitCircle = false;
171
172         // If the object was dragged, then revert to the old state.
173         // Otherwise, we were probably just clicked, and want to stay in the selected state.
174         if (objectWasDragged)
175                 state = oldState;
176 }
177
178
179 /*virtual*/ bool Circle::HitTest(Point point)
180 {
181 //      SaveHitState();
182         hitCenter = hitCircle = false;
183         double length = Vector::Magnitude(position, point);
184 //printf("Circle::length = %lf, radius = %lf\n", length, radius);
185 //How to translate this into pixels from Document space???
186 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
187 //the caller knows about the zoom factor and all that good kinda crap
188 /*
189 Document passes in the correct Cartesian coordinates being pointed to by the mouse.
190 So all we have to be concerned with is properly scaling our hot zones/handle sizes,
191 since we generally *don't* want those to scale with the zoom level. ;-)
192
193 What is going on here?
194 If we're zoomed out to, say, 50%, & our radius is 10.0 (absolute), then on screen
195 the radius will be 5.0. By multiplying the length by the zoom factor, we align our
196 pointed at length with our on screen length.
197 */
198         if ((length * Painter::zoom) < 8.0)
199         {
200                 hitCenter = true;
201                 snapPoint = position;
202                 snapPointIsValid = true;
203         }
204 //wrong:        else if ((length < (radius + 2.0)) && (length > (radius - 2.0)))
205 /*NB: The following should be identical to what we have down below, but it doesn't work out that way... :-P */
206 //close, but no else if (((length * Painter::zoom) < ((radius * Painter::zoom) + 2.0)) && ((length * Painter::zoom) > ((radius * Painter::zoom) - 2.0)))
207 //really wrong! else if (((length * Painter::zoom) < (radius + 2.0)) && ((length * Painter::zoom) > (radius - 2.0)))
208 // close again, but sill no     else if (((length * Painter::zoom) < ((radius + 2.0) * Painter::zoom)) && ((length * Painter::zoom) > ((radius - 2.0) * Painter::zoom)))
209         else if ((fabs(length - radius) * Painter::zoom) < 2.0)
210                 hitCircle = true;
211
212 //      return HitStateChanged();
213         return (hitCenter || hitCircle ? true : false);
214 }
215
216
217 /*virtual*/ QRectF Circle::Extents(void)
218 {
219         return QRectF(QPointF(position.x - radius, position.y - radius), QPointF(position.x + radius, position.y + radius));
220 }
221
222
223 void Circle::SaveHitState(void)
224 {
225         oldHitCenter = hitCenter;
226         oldHitCircle = hitCircle;
227 }
228
229
230 bool Circle::HitStateChanged(void)
231 {
232         if ((hitCenter != oldHitCenter) || (hitCircle != oldHitCircle))
233                 return true;
234
235         return false;
236 }
237
238
239 /*virtual*/ void Circle::Enumerate(FILE * file)
240 {
241         fprintf(file, "CIRCLE %i (%lf,%lf) %lf\n", layer, position.x, position.y, radius);
242 }
243
244
245 /*virtual*/ Object * Circle::Copy(void)
246 {
247 #warning "!!! This doesn't take care of attached Dimensions !!!"
248 /*
249 This is a real problem. While having a pointer in the Dimension to this line's points
250 is fast & easy, it creates a huge problem when trying to replicate an object like this.
251
252 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
253 way, if you copy them, ... you might still have problems. Because you can't be sure if
254 a copy will be persistant or not, you then *definitely* do not want them to have the
255 same reference number.
256 */
257         return new Circle(position, radius, parent);
258 }
259
260
261 /*virtual*/ void Circle::Rotate(Point point, double angle)
262 {
263         Point c1 = Geometry::RotatePointAroundPoint(position, point, angle);
264         position = c1;
265 }
266
267
268 /*virtual*/ void Circle::Mirror(Point p1, Point p2)
269 {
270         Point c1 = Geometry::MirrorPointAroundLine(position, p1, p2);
271         position = c1;
272 }
273
274
275 /*virtual*/ void Circle::Save(void)
276 {
277         Object::Save();
278         oldRadius2 = radius;
279 }
280
281
282 /*virtual*/ void Circle::Restore(void)
283 {
284         Object::Restore();
285         radius = oldRadius2;
286 }
287