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