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