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