]> Shamusworld >> Repos - architektonas/blob - src/circle.cpp
909d30416bac3676beaf8d6d8cbe905184f04eb7
[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 "painter.h"
20
21
22 Circle::Circle(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r),
23         draggingEdge(false), draggingCenter(false), hitCenter(false), hitCircle(false)
24 {
25         type = OTCircle;
26 }
27
28
29 Circle::~Circle()
30 {
31 }
32
33
34 /*virtual*/ void Circle::Draw(Painter * painter)
35 {
36         if (state == OSSelected || hitCircle || hitCenter)
37                 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
38         else
39                 painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
40
41         // Hatch/Fill...
42 //      QBrush brush(Qt::DiagCrossPattern);
43 //      brush.setColor(QColor(255, 255, 0));
44 //      painter->SetBrush(brush);
45         painter->SetBrush(QBrush(Qt::NoBrush));
46
47         // Draw the object...
48         painter->DrawEllipse(position, radius, radius);
49
50         // & draw handles (if needed)
51         if (state == OSSelected || hitCenter)
52                 painter->DrawHandle(position);
53
54         if (state == OSSelected && draggingEdge && objectWasDragged)
55                 painter->DrawHandle(dragPoint);
56
57         // If resizing the circle, draw an information panel showing the new radius.
58         if (draggingEdge)
59         {
60                 QString text = QObject::tr("Radius: %1\nScale: %2%");
61                 text = text.arg(radius, 0, 'd', 4).arg(radius / oldRadius * 100.0, 0, 'd', 0);
62
63                 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
64                 painter->SetPen(pen);
65                 painter->SetBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
66                 QRectF textRect(10.0, 10.0, 270.0, 70.0);       // x, y, w, h
67                 painter->DrawRoundedRect(textRect, 7.0, 7.0);
68
69                 textRect.setLeft(textRect.left() + 14);
70                 painter->SetFont(*Object::font);
71                 pen = QPen(QColor(0x00, 0x5F, 0xDF));
72                 painter->SetPen(pen);
73                 painter->DrawText(textRect, Qt::AlignVCenter, text);
74         }
75 }
76
77
78 /*virtual*/ Vector Circle::Center(void)
79 {
80         return position;
81 }
82
83
84 /*virtual*/ bool Circle::Collided(Vector point)
85 {
86         // We can assume this, since this is a mouse down event here.
87         objectWasDragged = false;
88         HitTest(point);
89
90         draggingCenter = hitCenter;
91         draggingEdge = hitCircle;
92
93         if (hitCenter || hitCircle)
94         {
95                 dragPoint = point;
96                 oldState = state;
97                 state = OSSelected;
98                 oldRadius = radius;
99                 return true;
100         }
101
102         // We didn't hit anything, so deselect this object and report failure to hit
103         state = OSInactive;
104         return false;
105 }
106
107
108 /*virtual*/ void Circle::PointerMoved(Vector point)
109 {
110         if (selectionInProgress)
111         {
112                 // Check for whether or not the rect contains this circle
113 //              if (selection.normalized().contains(Extents()))
114                 if (selection.contains(Extents()))
115                         state = OSSelected;
116                 else
117                         state = OSInactive;
118
119                 return;
120         }
121
122         // Hit test tells us what we hit (if anything) through boolean variables. It
123         // also tells us whether or not the state changed.
124         SaveHitState();
125         HitTest(point);
126         needUpdate = HitStateChanged();
127         objectWasDragged = (draggingEdge | draggingCenter);
128
129         if (objectWasDragged)
130                 needUpdate = true;
131
132         if (draggingEdge)
133                 radius = Vector::Magnitude(point, position);
134         else if (draggingCenter)
135                 position = point;
136
137         // Save this point so the rendering code knows where to draw the handle...
138         dragPoint = point;
139 }
140
141
142 /*virtual*/ void Circle::PointerReleased(void)
143 {
144         // Mouse went up, so our dragging is done (if any *was* done, that is)
145         draggingEdge = draggingCenter = false;
146         hitCenter = hitCircle = false;
147
148         // If the object was dragged, then revert to the old state.
149         // Otherwise, we were probably just clicked, and want to stay in the selected state.
150         if (objectWasDragged)
151                 state = oldState;
152 }
153
154
155 /*virtual*/ bool Circle::HitTest(Point point)
156 {
157 //      SaveHitState();
158         hitCenter = hitCircle = false;
159         double length = Vector::Magnitude(position, point);
160 //printf("Circle::length = %lf, radius = %lf\n", length, radius);
161 //How to translate this into pixels from Document space???
162 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
163 //the caller knows about the zoom factor and all that good kinda crap
164 /*
165 Document passes in the correct Cartesian coordinates being pointed to by the mouse.
166 So all we have to be concerned with is properly scaling our hot zones/handle sizes,
167 since we generally *don't* want those to scale with the zoom level. ;-)
168
169 What is going on here?
170 If we're zoomed out to, say, 50%, & our radius is 10.0 (absolute), then on screen
171 the radius will be 5.0. By multiplying the length by the zoom factor, we align our
172 pointed at length with our on screen length.
173 */
174         if ((length * Painter::zoom) < 8.0)
175                 hitCenter = true;
176 //wrong:        else if ((length < (radius + 2.0)) && (length > (radius - 2.0)))
177 /*NB: The following should be identical to what we have down below, but it doesn't work out that way... :-P */
178 //close, but no else if (((length * Painter::zoom) < ((radius * Painter::zoom) + 2.0)) && ((length * Painter::zoom) > ((radius * Painter::zoom) - 2.0)))
179 //really wrong! else if (((length * Painter::zoom) < (radius + 2.0)) && ((length * Painter::zoom) > (radius - 2.0)))
180 // close again, but sill no     else if (((length * Painter::zoom) < ((radius + 2.0) * Painter::zoom)) && ((length * Painter::zoom) > ((radius - 2.0) * Painter::zoom)))
181         else if ((fabs(length - radius) * Painter::zoom) < 2.0)
182                 hitCircle = true;
183
184 //      return HitStateChanged();
185         return (hitCenter || hitCircle ? true : false);
186 }
187
188
189 /*virtual*/ QRectF Circle::Extents(void)
190 {
191         return QRectF(QPointF(position.x - radius, position.y - radius), QPointF(position.x + radius, position.y + radius));
192 }
193
194
195 void Circle::SaveHitState(void)
196 {
197         oldHitCenter = hitCenter;
198         oldHitCircle = hitCircle;
199 }
200
201
202 bool Circle::HitStateChanged(void)
203 {
204         if ((hitCenter != oldHitCenter) || (hitCircle != oldHitCircle))
205                 return true;
206
207         return false;
208 }
209
210
211 /*virtual*/ void Circle::Enumerate(FILE * file)
212 {
213         fprintf(file, "CIRCLE (%lf,%lf) %lf\n", position.x, position.y, radius);
214 }
215
216
217 /*virtual*/ Object * Circle::Copy(void)
218 {
219 #warning "!!! This doesn't take care of attached Dimensions !!!"
220 /*
221 This is a real problem. While having a pointer in the Dimension to this line's points
222 is fast & easy, it creates a huge problem when trying to replicate an object like this.
223
224 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
225 way, if you copy them, ... you might still have problems. Because you can't be sure if
226 a copy will be persistant or not, you then *definitely* do not want them to have the
227 same reference number.
228 */
229         return new Circle(position, radius, parent);
230 }
231