1 // circle.cpp: Circle object
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
7 // JLH = James L. Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 03/28/2011 Created this file
20 Circle::Circle(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r),
21 dragging(false), draggingHandle1(false), draggingHandle2(false)//, needUpdate(false)
29 /*virtual*/ void Circle::Draw(Painter * painter)
31 if (state == OSSelected || hitCircle || hitCenter)
32 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
34 painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
36 if (state == OSSelected || hitCenter)
37 painter->DrawEllipse(position, 4.0, 4.0);
39 if (state == OSSelected && dragging)
40 painter->DrawEllipse(oldPoint, 4.0, 4.0);
42 painter->DrawEllipse(position, radius, radius);
45 /*virtual*/ Vector Circle::Center(void)
50 /*virtual*/ bool Circle::Collided(Vector point)
52 // We can assume this, since this is a mouse down event here.
53 objectWasDragged = false;
56 // objectWasDragged = false;
57 Vector v1 = position - point;
59 if (state == OSInactive)
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);
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)
72 oldPoint = position; //maybe "position"?
73 draggingHandle1 = true;
76 else if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
85 else if (state == OSSelected)
87 // Here we test for collision with handles as well! (SOON!)
90 if (v1.Magnitude() < 2.0) // Handle #1
91 else if (v2.Magnitude() < 2.0) // Handle #2
93 if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
96 // state = OSInactive;
107 /*virtual*/ void Circle::PointerMoved(Vector point)
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);
113 objectWasDragged = true;
117 // Here we need to check whether or not we're dragging a handle or the object itself...
118 // Vector delta = point - oldPoint;
120 // position += delta;
121 // endpoint += delta;
122 radius = Vector(point - position).Magnitude();
127 else if (draggingHandle1)
129 Vector delta = point - oldPoint;
134 /* else if (draggingHandle2)
136 Vector delta = point - oldPoint;
144 // needUpdate = false;
147 /*virtual*/ void Circle::PointerReleased(void)
150 draggingHandle1 = false;
151 draggingHandle2 = false;
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
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...
159 if (objectWasDragged)
164 /*virtual*/ bool Circle::NeedsUpdate(void)
170 bool Circle::HitTest(Point point)
174 hitCenter = hitCircle = false;
177 Vector lineSegment = endpoint - position;
178 Vector v1 = point - position;
179 Vector v2 = point - endpoint;
180 double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
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.
187 if (parameterizedPoint < 0.0)
188 distance = v1.Magnitude();
189 else if (parameterizedPoint > lineSegment.Magnitude())
190 distance = v2.Magnitude();
192 // distance = ?Det?(ls, v1) / |ls|
193 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
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:
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
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.
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)
217 else if (v2.Magnitude() < 8.0)
219 else if (distance < 5.0)
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)
228 else if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
232 return StateChanged();
235 void Circle::SaveState(void)
237 oldHitCenter = hitCenter;
238 oldHitCircle = hitCircle;
241 bool Circle::StateChanged(void)
243 if ((hitCenter != oldHitCenter) || (hitCircle != oldHitCircle))