1 // dimension.cpp: Dimension 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 Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 04/04/2011 Created this file, basic rendering
14 #include "dimension.h"
17 #include "mathconstants.h"
21 Dimension::Dimension(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint(p2),
22 dragging(false), draggingHandle1(false), draggingHandle2(false),
23 length(p2.Magnitude()), point1(NULL), point2(NULL)
27 // This is bad, p1 & p2 could be NULL, causing much consternation...
28 Dimension::Dimension(Vector * p1, Vector * p2, Object * p/*= NULL*/): Object(*p1, p), endpoint(*p2),
29 dragging(false), draggingHandle1(false), draggingHandle2(false),
30 length(p2->Magnitude()), point1(p1), point2(p2)
34 Dimension::~Dimension()
38 /*virtual*/ void Dimension::Draw(Painter * painter)
40 // If there are valid Vector pointers in here, use them to update the internal
41 // positions. Otherwise, we just use the internal positions by default.
48 if (state == OSSelected)
49 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
51 painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
53 // Draw an aligned dimension line
54 double angle = Vector(endpoint - position).Angle();
55 double orthoAngle = angle + (PI / 2.0);
56 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
57 Vector unit = Vector(endpoint - position).Unit();
59 // Get our line parallel to our points
60 Point p1 = position + (orthogonal * 10.0);
61 Point p2 = endpoint + (orthogonal * 10.0);
63 // Draw main dimension line
64 painter->DrawLine(p1, p2);
66 Point p3 = position + (orthogonal * 16.0);
67 Point p4 = endpoint + (orthogonal * 16.0);
68 Point p5 = position + (orthogonal * 4.0);
69 Point p6 = endpoint + (orthogonal * 4.0);
71 // Draw extension lines
72 painter->DrawLine(p3, p5);
73 painter->DrawLine(p4, p6);
75 painter->SetBrush(QBrush(QColor(Qt::blue)));
76 painter->DrawArrowhead(p1, p2);
77 painter->DrawArrowhead(p2, p1);
79 // Draw arrowheads, dots, etc :-P
80 // NOTE: These need some kind of enum in the header so that they can
81 // be created with some kind of user control...
83 Point p7 = position + (unit * 9.0);
84 Point p8 = endpoint - (unit * 9.0);
85 Point p9 = p7 + (orthogonal * 7.0);
86 Point p10 = p7 + (orthogonal * 13.0);
87 Point p11 = p8 + (orthogonal * 7.0);
88 Point p12 = p8 + (orthogonal * 13.0);
90 painter->DrawLine(p1, p9);
91 painter->DrawLine(p9, p10);
92 painter->DrawLine(p10, p1);
93 painter->DrawLine(p2, p11);
94 painter->DrawLine(p11, p12);
95 painter->DrawLine(p12, p2);
98 // Draw length of dimension line...
99 painter->SetFont(QFont("Arial", 10 * Painter::zoom));
100 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
102 // This is in pixels, which isn't even remotely correct... !!! FIX !!!
103 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
104 // int textWidth = QFontMetrics(painter->font()).width(dimText);
105 // int textHeight = QFontMetrics(painter->font()).height();
107 //We have to do transformation voodoo to make the text come out readable and in correct orientation...
108 //Some things to note here: if angle > 90 degrees, then we need to take the negative of the angle
111 painter->translate(ctr.x, ctr.y);
113 //16 : printf("textHeight: %d\n", textHeight);
115 //Fix text so it isn't upside down...
116 if ((angle > PI * 0.5) && (angle < PI * 1.5))
122 painter->rotate(angle * RADIANS_TO_DEGREES);
123 painter->scale(1.0, -1.0);
124 //painter->translate(-textWidth / 2, -24);
125 // painter->drawText(0, 0, textWidth, 20, Qt::AlignCenter, dimText);
126 // This version draws the y-coord from the baseline of the font
127 painter->DrawText(-textWidth / 2, yOffset, dimText);
128 //painter->setPen(QPen(QColor(0xFF, 0x20, 0x20), 1.0, Qt::SolidLine));
129 //painter->drawLine(20, 0, -20, 0);
130 //painter->drawLine(0, 20, 0, -20);
133 // painter->DrawText(QRectF(QPointF(ctr.x, ctr.y), QPointF(ctr.x + textWidth, ctr.y + textHeight)), Qt::AlignVCenter, dimText);
134 // Now that we've taken our own good advice, maybe we should have the painter class
135 // do a nice abstracted text draw routine? :-)
136 painter->DrawAngledText(ctr, angle, dimText);
140 All of the preceeding makes me think that rather than try to compensate for Qt's unbelieveably
141 AWFUL decision to go with a wrong-handed graphics subsystem, it may be better to just stuff
142 all of that crap into some kind of subclass that handles all the nastiness behind the scenes.
143 I mean, really, all this crap just to get some proplerly rendered text on the screen? How
144 retarded is that? :-/
148 /*virtual*/ Vector Dimension::Center(void)
150 // Technically, this is the midpoint but who are we to quibble? :-)
151 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
155 /*virtual*/ bool Dimension::Collided(Vector /*point*/)
158 objectWasDragged = false;
159 Vector lineSegment = endpoint - position;
160 Vector v1 = point - position;
161 Vector v2 = point - endpoint;
162 double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
164 // Geometric interpretation:
165 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
166 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
167 // then the perpendicular lies beyond the 2nd endpoint.
169 if (parameterizedPoint < 0.0)
170 distance = v1.Magnitude();
171 else if (parameterizedPoint > lineSegment.Magnitude())
172 distance = v2.Magnitude();
173 else // distance = ?Det?(ls, v1) / |ls|
174 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
176 // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
177 // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
178 // {e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is
179 // computed by first computing the area of the triangle the three points form, then dividing by the
180 // length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the
181 // triangle formed by three points is the determinant of the following matrix:
187 // By translating the start point to the origin, this can be rewritten as:
188 // By subtracting row 1 from all rows, you get the following:
189 // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
190 // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
193 // (ex - sx) (ey - sy) 0 ==> ex ey 0
194 // (px - sx) (py - sy) 0 px py 0
196 // which greatly simplifies the calculation of the determinant.
198 if (state == OSInactive)
200 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
201 //printf(" v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
202 //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);
204 //How to translate this into pixels from Document space???
205 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
206 //the caller knows about the zoom factor and all that good kinda crap
207 if (v1.Magnitude() < 10.0)
211 oldPoint = position; //maybe "position"?
212 draggingHandle1 = true;
215 else if (v2.Magnitude() < 10.0)
219 oldPoint = endpoint; //maybe "position"?
220 draggingHandle2 = true;
223 else if (distance < 2.0)
232 else if (state == OSSelected)
234 // Here we test for collision with handles as well! (SOON!)
237 if (v1.Magnitude() < 2.0) // Handle #1
238 else if (v2.Magnitude() < 2.0) // Handle #2
243 // state = OSInactive;
255 /*virtual*/ void Dimension::PointerMoved(Vector point)
257 // We know this is true because mouse move messages don't come here unless
258 // the object was actually clicked on--therefore we *know* we're being
260 objectWasDragged = true;
264 // Here we need to check whether or not we're dragging a handle or the object itself...
265 Vector delta = point - oldPoint;
273 else if (draggingHandle1)
275 Vector delta = point - oldPoint;
282 else if (draggingHandle2)
284 Vector delta = point - oldPoint;
295 /*virtual*/ void Dimension::PointerReleased(void)
297 if (draggingHandle1 || draggingHandle2)
299 // Set the length (in case the global state was set to fixed (or not))
300 if (Object::fixedLength)
303 if (draggingHandle1) // startpoint
305 Vector v = Vector(position - endpoint).Unit() * length;
306 position = endpoint + v;
310 // Vector v1 = endpoint - position;
311 Vector v = Vector(endpoint - position).Unit() * length;
312 endpoint = position + v;
317 // Otherwise, we calculate the new length, just in case on the next move
318 // it turns out to have a fixed length. :-)
319 length = Vector(endpoint - position).Magnitude();
324 draggingHandle1 = false;
325 draggingHandle2 = false;
327 // Here we check for just a click: If object was clicked and dragged, then
328 // revert to the old state (OSInactive). Otherwise, keep the new state that
330 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
331 about keeping track of old states...
333 if (objectWasDragged)
337 void Dimension::SetPoint1(Vector * v)
343 void Dimension::SetPoint2(Vector * v)
349 void Dimension::FlipSides(void)
352 Vector tmp = position;
356 Vector * tmp = point1;