]> Shamusworld >> Repos - architektonas/blob - src/circle.cpp
Preliminary start on zooming/circle hover feedback.
[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 L. Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  03/28/2011  Created this file
12 //
13
14 #include "circle.h"
15
16 #include <QtGui>
17 #include "painter.h"
18
19
20 Circle::Circle(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r),
21         dragging(false), draggingHandle1(false), draggingHandle2(false)//, needUpdate(false)
22 {
23 }
24
25 Circle::~Circle()
26 {
27 }
28
29 /*virtual*/ void Circle::Draw(Painter * painter)
30 {
31         if (state == OSSelected || hitCircle || hitCenter)
32                 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
33         else
34                 painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
35
36         if (state == OSSelected || hitCenter)
37                 painter->DrawEllipse(position, 4.0, 4.0);
38
39         if (state == OSSelected && dragging)
40                 painter->DrawEllipse(oldPoint, 4.0, 4.0);
41
42         painter->DrawEllipse(position, radius, radius);
43 }
44
45 /*virtual*/ Vector Circle::Center(void)
46 {
47         return position;
48 }
49
50 /*virtual*/ bool Circle::Collided(Vector point)
51 {
52         // We can assume this, since this is a mouse down event here.
53         objectWasDragged = false;
54         HitTest(point);
55
56 //      objectWasDragged = false;
57         Vector v1 = position - point;
58
59         if (state == OSInactive)
60         {
61 //printf("Circle: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
62 //printf("      v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
63 //printf("      point = %lf,%lf,%lf; p1 = %lf,%lf,%lf; p2 = %lf,%lf,%lf\n", point.x, point.y, point.z, position.x, position.y, position.z, endpoint.x, endpoint.y, endpoint.z);
64 //printf("      \n", );
65 //How to translate this into pixels from Document space???
66 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
67 //the caller knows about the zoom factor and all that good kinda crap
68                 if (v1.Magnitude() < 10.0)
69                 {
70                         oldState = state;
71                         state = OSSelected;
72                         oldPoint = position; //maybe "position"?
73                         draggingHandle1 = true;
74                         return true;
75                 }
76                 else if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
77                 {
78                         oldState = state;
79                         state = OSSelected;
80                         oldPoint = point;
81                         dragging = true;
82                         return true;
83                 }
84         }
85         else if (state == OSSelected)
86         {
87                 // Here we test for collision with handles as well! (SOON!)
88 /*
89 Like so:
90                 if (v1.Magnitude() < 2.0) // Handle #1
91                 else if (v2.Magnitude() < 2.0) // Handle #2
92 */
93                 if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
94                 {
95                         oldState = state;
96 //                      state = OSInactive;
97                         oldPoint = point;
98                         dragging = true;
99                         return true;
100                 }
101         }
102
103         state = OSInactive;
104         return false;
105 }
106
107 /*virtual*/ void Circle::PointerMoved(Vector point)
108 {
109         // Hit test tells us what we hit (if anything) through boolean variables. It
110         // also tells us whether or not the state changed.
111         needUpdate = HitTest(point);
112
113         objectWasDragged = true;
114
115         if (dragging)
116         {
117                 // Here we need to check whether or not we're dragging a handle or the object itself...
118 //              Vector delta = point - oldPoint;
119
120 //              position += delta;
121 //              endpoint += delta;
122                 radius = Vector(point - position).Magnitude();
123
124                 oldPoint = point;
125                 needUpdate = true;
126         }
127         else if (draggingHandle1)
128         {
129                 Vector delta = point - oldPoint;
130                 position += delta;
131                 oldPoint = point;
132                 needUpdate = true;
133         }
134 /*      else if (draggingHandle2)
135         {
136                 Vector delta = point - oldPoint;
137
138                 endpoint += delta;
139
140                 oldPoint = point;
141                 needUpdate = true;
142         }*/
143 //      else
144 //              needUpdate = false;
145 }
146
147 /*virtual*/ void Circle::PointerReleased(void)
148 {
149         dragging = false;
150         draggingHandle1 = false;
151         draggingHandle2 = false;
152
153         // Here we check for just a click: If object was clicked and dragged, then
154         // revert to the old state (OSInactive). Otherwise, keep the new state that
155         // we set.
156 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
157 about keeping track of old states...
158 */
159         if (objectWasDragged)
160                 state = oldState;
161 }
162
163 #if 0
164 /*virtual*/ bool Circle::NeedsUpdate(void)
165 {
166         return needUpdate;
167 }
168 #endif
169
170 bool Circle::HitTest(Point point)
171 {
172         SaveState();
173
174         hitCenter = hitCircle = false;
175
176 #if 0
177         Vector lineSegment = endpoint - position;
178         Vector v1 = point - position;
179         Vector v2 = point - endpoint;
180         double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
181
182         // Geometric interpretation:
183         // The parameterized point on the vector lineSegment is where the perpendicular
184         // intersects lineSegment. If pp < 0, then the perpendicular lies beyond the 1st
185         // endpoint. If pp > length of ls, then the perpendicular lies beyond the 2nd endpoint.
186
187         if (parameterizedPoint < 0.0)
188                 distance = v1.Magnitude();
189         else if (parameterizedPoint > lineSegment.Magnitude())
190                 distance = v2.Magnitude();
191         else
192                 // distance = ?Det?(ls, v1) / |ls|
193                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
194
195         // Geometric interpretation of the above:
196         // If the segment endpoints are s and e, and the point is p, then the test
197         // for the perpendicular intercepting the segment is equivalent to insisting
198         // that the two dot products {s-e}.{s-p} and {e-s}.{e-p} are both non-negative.
199         // Perpendicular distance from the point to the segment is computed by first
200         // computing the area of the triangle the three points form, then dividing by
201         // the length of the segment.  Distances are done just by the Pythagorean
202         // theorem. Twice the area of the triangle formed by three points is the
203         // determinant of the following matrix:
204         //
205         // sx sy 1       0  0  1       0  0  0
206         // ex ey 1  ==>  ex ey 1  ==>  ex ey 0
207         // px py 1       px py 1       px py 0
208         //
209         // By translating the start point to the origin, and subtracting row 1 from
210         // all other rows, we end up with the matrix on the right which greatly
211         // simplifies the calculation of the determinant.
212
213 //How do we determine distance here? Especially if zoomed in or out???
214 #warning "!!! Distances tested for may not be valid if zoomed in or out !!!"
215         if (v1.Magnitude() < 8.0)
216                 hitPoint1 = true;
217         else if (v2.Magnitude() < 8.0)
218                 hitPoint2 = true;
219         else if (distance < 5.0)
220                 hitLine = true;
221 #else
222         Vector v1 = position - point;
223 //How to translate this into pixels from Document space???
224 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
225 //the caller knows about the zoom factor and all that good kinda crap
226         if (v1.Magnitude() < 10.0)
227                 hitCenter = true;
228         else if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
229                 hitCircle = true;
230 #endif
231
232         return StateChanged();
233 }
234
235 void Circle::SaveState(void)
236 {
237         oldHitCenter = hitCenter;
238         oldHitCircle = hitCircle;
239 }
240
241 bool Circle::StateChanged(void)
242 {
243         if ((hitCenter != oldHitCenter) || (hitCircle != oldHitCircle))
244                 return true;
245
246         return false;
247 }