1 // line.cpp: Line 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/22/2011 Created this file
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())
28 /*virtual*/ void Line::Draw(QPainter * painter)
30 if (state == OSSelected)
31 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
33 painter->setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
35 // if (draggingHandle1)
36 if (state == OSSelected)
37 painter->drawEllipse(QPointF(position.x, position.y), 4.0, 4.0);
39 // if (draggingHandle2)
40 if (state == OSSelected)
41 painter->drawEllipse(QPointF(endpoint.x, endpoint.y), 4.0, 4.0);
43 if (Object::fixedLength && (draggingHandle1 || draggingHandle2))
45 Vector point1 = (draggingHandle1 ? endpoint : position);
46 Vector point2 = (draggingHandle1 ? position : endpoint);
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);
53 if (current.Magnitude() > length)
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);
60 painter->drawLine((int)position.x, (int)position.y, (int)endpoint.x, (int)endpoint.y);
63 /*virtual*/ Vector Line::Center(void)
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);
70 /*virtual*/ bool Line::Collided(Vector point)
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;
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.
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());
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:
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!]
107 // (ex - sx) (ey - sy) 0 ==> ex ey 0
108 // (px - sx) (py - sy) 0 px py 0
110 // which greatly simplifies the calculation of the determinant.
112 if (state == OSInactive)
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);
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)
125 oldPoint = position; //maybe "position"?
126 draggingHandle1 = true;
129 else if (v2.Magnitude() < 10.0)
133 oldPoint = endpoint; //maybe "position"?
134 draggingHandle2 = true;
137 else if (distance < 2.0)
146 else if (state == OSSelected)
148 // Here we test for collision with handles as well! (SOON!)
151 if (v1.Magnitude() < 2.0) // Handle #1
152 else if (v2.Magnitude() < 2.0) // Handle #2
157 // state = OSInactive;
168 /*virtual*/ void Line::PointerMoved(Vector point)
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
173 objectWasDragged = true;
177 // Here we need to check whether or not we're dragging a handle or the object itself...
178 Vector delta = point - oldPoint;
186 else if (draggingHandle1)
188 Vector delta = point - oldPoint;
195 else if (draggingHandle2)
197 Vector delta = point - oldPoint;
208 /*virtual*/ void Line::PointerReleased(void)
210 if (draggingHandle1 || draggingHandle2)
212 // Set the length (in case the global state was set to fixed (or not))
213 if (Object::fixedLength)
216 if (draggingHandle1) // startpoint
218 Vector v = Vector(position - endpoint).Unit() * length;
219 position = endpoint + v;
223 // Vector v1 = endpoint - position;
224 Vector v = Vector(endpoint - position).Unit() * length;
225 endpoint = position + v;
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();
237 draggingHandle1 = false;
238 draggingHandle2 = false;
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
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...
246 if (objectWasDragged)
251 /*virtual*/ bool Line::NeedsUpdate(void)