]> Shamusworld >> Repos - architektonas/blob - src/line.cpp
Major refactor of Architektonas: Jettisoning old cruft.
[architektonas] / src / line.cpp
1 // line.cpp: Line 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/22/2011  Created this file
12 //
13
14 #include "line.h"
15
16 #include <QtGui>
17
18 Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint(p2),
19         dragging(false), draggingHandle1(false), draggingHandle2(false), //needUpdate(false),
20         length(p2.Magnitude())
21 {
22 }
23
24 Line::~Line()
25 {
26 }
27
28 /*virtual*/ void Line::Draw(QPainter * painter)
29 {
30         if (state == OSSelected)
31                 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
32         else
33                 painter->setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
34
35 //      if (draggingHandle1)
36         if (state == OSSelected)
37                 painter->drawEllipse(QPointF(position.x, position.y), 4.0, 4.0);
38
39 //      if (draggingHandle2)
40         if (state == OSSelected)
41                 painter->drawEllipse(QPointF(endpoint.x, endpoint.y), 4.0, 4.0);
42
43         if (Object::fixedLength && (draggingHandle1 || draggingHandle2))
44         {
45                 Vector point1 = (draggingHandle1 ? endpoint : position);
46                 Vector point2 = (draggingHandle1 ? position : endpoint);
47
48                 Vector current(point2 - point1);
49                 Vector v = current.Unit() * length;
50                 Vector v2 = point1 + v;
51                 painter->drawLine((int)point1.x, (int)point1.y, (int)v2.x, (int)v2.y);
52
53                 if (current.Magnitude() > length)
54                 {
55                         painter->setPen(QPen(QColor(128, 0, 0), 1.0, Qt::DashLine));
56                         painter->drawLine((int)v2.x, (int)v2.y, (int)point2.x, (int)point2.y);
57                 }
58         }
59         else
60                 painter->drawLine((int)position.x, (int)position.y, (int)endpoint.x, (int)endpoint.y);
61 }
62
63 /*virtual*/ Vector Line::Center(void)
64 {
65         // Technically, this is the midpoint but who are we to quibble? :-)
66         Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
67         return endpoint + v;
68 }
69
70 /*virtual*/ bool Line::Collided(Vector point)
71 {
72         objectWasDragged = false;
73         Vector lineSegment = endpoint - position;
74         Vector v1 = point - position;
75         Vector v2 = point - endpoint;
76         double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
77
78         // Geometric interpretation:
79         // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
80         // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
81         // then the perpendicular lies beyond the 2nd endpoint.
82
83         if (parameterizedPoint < 0.0)
84                 distance = v1.Magnitude();
85         else if (parameterizedPoint > lineSegment.Magnitude())
86                 distance = v2.Magnitude();
87         else                                    // distance = ?Det?(ls, v1) / |ls|
88                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
89
90         // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
91         // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
92         // {e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
93         // computed by first computing the area of the triangle the three points form, then dividing by the
94         // length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
95         // triangle formed by three points is the determinant of the following matrix:
96         //
97         // sx sy 1
98         // ex ey 1
99         // px py 1
100         //
101         // By translating the start point to the origin, this can be rewritten as:
102         // By subtracting row 1 from all rows, you get the following:
103         // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
104         // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
105         //
106         // 0         0         0        0  0  0
107         // (ex - sx) (ey - sy) 0   ==>  ex ey 0
108         // (px - sx) (py - sy) 0        px py 0
109         //
110         // which greatly simplifies the calculation of the determinant.
111
112         if (state == OSInactive)
113         {
114 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
115 //printf("      v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
116 //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);
117 //printf("      \n", );
118 //How to translate this into pixels from Document space???
119 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
120 //the caller knows about the zoom factor and all that good kinda crap
121                 if (v1.Magnitude() < 10.0)
122                 {
123                         oldState = state;
124                         state = OSSelected;
125                         oldPoint = position; //maybe "position"?
126                         draggingHandle1 = true;
127                         return true;
128                 }
129                 else if (v2.Magnitude() < 10.0)
130                 {
131                         oldState = state;
132                         state = OSSelected;
133                         oldPoint = endpoint; //maybe "position"?
134                         draggingHandle2 = true;
135                         return true;
136                 }
137                 else if (distance < 2.0)
138                 {
139                         oldState = state;
140                         state = OSSelected;
141                         oldPoint = point;
142                         dragging = true;
143                         return true;
144                 }
145         }
146         else if (state == OSSelected)
147         {
148                 // Here we test for collision with handles as well! (SOON!)
149 /*
150 Like so:
151                 if (v1.Magnitude() < 2.0) // Handle #1
152                 else if (v2.Magnitude() < 2.0) // Handle #2
153 */
154                 if (distance < 2.0)
155                 {
156                         oldState = state;
157 //                      state = OSInactive;
158                         oldPoint = point;
159                         dragging = true;
160                         return true;
161                 }
162         }
163
164         state = OSInactive;
165         return false;
166 }
167
168 /*virtual*/ void Line::PointerMoved(Vector point)
169 {
170         // We know this is true because mouse move messages don't come here unless
171         // the object was actually clicked on--therefore we *know* we're being
172         // dragged...
173         objectWasDragged = true;
174
175         if (dragging)
176         {
177                 // Here we need to check whether or not we're dragging a handle or the object itself...
178                 Vector delta = point - oldPoint;
179
180                 position += delta;
181                 endpoint += delta;
182
183                 oldPoint = point;
184                 needUpdate = true;
185         }
186         else if (draggingHandle1)
187         {
188                 Vector delta = point - oldPoint;
189
190                 position += delta;
191
192                 oldPoint = point;
193                 needUpdate = true;
194         }
195         else if (draggingHandle2)
196         {
197                 Vector delta = point - oldPoint;
198
199                 endpoint += delta;
200
201                 oldPoint = point;
202                 needUpdate = true;
203         }
204         else
205                 needUpdate = false;
206 }
207
208 /*virtual*/ void Line::PointerReleased(void)
209 {
210         if (draggingHandle1 || draggingHandle2)
211         {
212                 // Set the length (in case the global state was set to fixed (or not))
213                 if (Object::fixedLength)
214                 {
215
216                         if (draggingHandle1)    // startpoint
217                         {
218                                 Vector v = Vector(position - endpoint).Unit() * length;
219                                 position = endpoint + v;
220                         }
221                         else                                    // endpoint
222                         {
223 //                              Vector v1 = endpoint - position;
224                                 Vector v = Vector(endpoint - position).Unit() * length;
225                                 endpoint = position + v;
226                         }
227                 }
228                 else
229                 {
230                         // Otherwise, we calculate the new length, just in case on the next move
231                         // it turns out to have a fixed length. :-)
232                         length = Vector(endpoint - position).Magnitude();
233                 }
234         }
235
236         dragging = false;
237         draggingHandle1 = false;
238         draggingHandle2 = false;
239
240         // Here we check for just a click: If object was clicked and dragged, then
241         // revert to the old state (OSInactive). Otherwise, keep the new state that
242         // we set.
243 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
244 about keeping track of old states...
245 */
246         if (objectWasDragged)
247                 state = oldState;
248 }
249
250 #if 0
251 /*virtual*/ bool Line::NeedsUpdate(void)
252 {
253         return needUpdate;
254 }
255 #endif