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 L. Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 04/04/2011 Created this file, basic rendering
14 #include "dimension.h"
17 #include "mathconstants.h"
20 Dimension::Dimension(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint(p2),
21 dragging(false), draggingHandle1(false), draggingHandle2(false),
22 length(p2.Magnitude())
26 Dimension::~Dimension()
30 /*virtual*/ void Dimension::Draw(QPainter * painter)
32 if (state == OSSelected)
33 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
35 painter->setPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
37 // Draw an aligned dimension line
38 double angle = Vector(endpoint - position).Angle();
39 double orthoAngle = angle + (PI / 2.0);
40 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
41 Vector unit = Vector(endpoint - position).Unit();
43 // Get our line parallel to our points
44 Point p1 = position + (orthogonal * 10.0);
45 Point p2 = endpoint + (orthogonal * 10.0);
47 // Draw main dimension line
48 painter->drawLine(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
50 Point p3 = position + (orthogonal * 16.0);
51 Point p4 = endpoint + (orthogonal * 16.0);
52 Point p5 = position + (orthogonal * 4.0);
53 Point p6 = endpoint + (orthogonal * 4.0);
55 // Draw extension lines
56 painter->drawLine(QPointF(p3.x, p3.y), QPointF(p5.x, p5.y));
57 painter->drawLine(QPointF(p4.x, p4.y), QPointF(p6.x, p6.y));
59 // Draw length of dimension line...
60 painter->setFont(QFont("Arial", 10));
61 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
63 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
64 int textWidth = QFontMetrics(painter->font()).width(dimText);
65 int textHeight = QFontMetrics(painter->font()).height();
66 //We have to do transformation voodoo to make the text come out readable and in correct orientation...
67 //Some things to note here: if angle > 90 degrees, then we need to take the negative of the angle
70 painter->translate(ctr.x, ctr.y);
72 //16 : printf("textHeight: %d\n", textHeight);
74 //Fix text so it isn't upside down...
75 if ((angle > PI * 0.5) && (angle < PI * 1.5))
81 painter->rotate(angle * RADIANS_TO_DEGREES);
82 painter->scale(1.0, -1.0);
83 //painter->translate(-textWidth / 2, -24);
84 // painter->drawText(0, 0, textWidth, 20, Qt::AlignCenter, dimText);
85 // This version draws the y-coord from the baseline of the font
86 painter->drawText(-textWidth / 2, yOffset, dimText);
87 //painter->setPen(QPen(QColor(0xFF, 0x20, 0x20), 1.0, Qt::SolidLine));
88 //painter->drawLine(20, 0, -20, 0);
89 //painter->drawLine(0, 20, 0, -20);
93 /*virtual*/ Vector Dimension::Center(void)
95 // Technically, this is the midpoint but who are we to quibble? :-)
96 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
100 /*virtual*/ bool Dimension::Collided(Vector /*point*/)
103 objectWasDragged = false;
104 Vector lineSegment = endpoint - position;
105 Vector v1 = point - position;
106 Vector v2 = point - endpoint;
107 double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
109 // Geometric interpretation:
110 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
111 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
112 // then the perpendicular lies beyond the 2nd endpoint.
114 if (parameterizedPoint < 0.0)
115 distance = v1.Magnitude();
116 else if (parameterizedPoint > lineSegment.Magnitude())
117 distance = v2.Magnitude();
118 else // distance = ?Det?(ls, v1) / |ls|
119 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
121 // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
122 // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
123 // {e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is
124 // computed by first computing the area of the triangle the three points form, then dividing by the
125 // length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the
126 // triangle formed by three points is the determinant of the following matrix:
132 // By translating the start point to the origin, this can be rewritten as:
133 // By subtracting row 1 from all rows, you get the following:
134 // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
135 // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
138 // (ex - sx) (ey - sy) 0 ==> ex ey 0
139 // (px - sx) (py - sy) 0 px py 0
141 // which greatly simplifies the calculation of the determinant.
143 if (state == OSInactive)
145 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
146 //printf(" v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
147 //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);
149 //How to translate this into pixels from Document space???
150 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
151 //the caller knows about the zoom factor and all that good kinda crap
152 if (v1.Magnitude() < 10.0)
156 oldPoint = position; //maybe "position"?
157 draggingHandle1 = true;
160 else if (v2.Magnitude() < 10.0)
164 oldPoint = endpoint; //maybe "position"?
165 draggingHandle2 = true;
168 else if (distance < 2.0)
177 else if (state == OSSelected)
179 // Here we test for collision with handles as well! (SOON!)
182 if (v1.Magnitude() < 2.0) // Handle #1
183 else if (v2.Magnitude() < 2.0) // Handle #2
188 // state = OSInactive;
200 /*virtual*/ void Dimension::PointerMoved(Vector point)
202 // We know this is true because mouse move messages don't come here unless
203 // the object was actually clicked on--therefore we *know* we're being
205 objectWasDragged = true;
209 // Here we need to check whether or not we're dragging a handle or the object itself...
210 Vector delta = point - oldPoint;
218 else if (draggingHandle1)
220 Vector delta = point - oldPoint;
227 else if (draggingHandle2)
229 Vector delta = point - oldPoint;
240 /*virtual*/ void Dimension::PointerReleased(void)
242 if (draggingHandle1 || draggingHandle2)
244 // Set the length (in case the global state was set to fixed (or not))
245 if (Object::fixedLength)
248 if (draggingHandle1) // startpoint
250 Vector v = Vector(position - endpoint).Unit() * length;
251 position = endpoint + v;
255 // Vector v1 = endpoint - position;
256 Vector v = Vector(endpoint - position).Unit() * length;
257 endpoint = position + v;
262 // Otherwise, we calculate the new length, just in case on the next move
263 // it turns out to have a fixed length. :-)
264 length = Vector(endpoint - position).Magnitude();
269 draggingHandle1 = false;
270 draggingHandle2 = false;
272 // Here we check for just a click: If object was clicked and dragged, then
273 // revert to the old state (OSInactive). Otherwise, keep the new state that
275 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
276 about keeping track of old states...
278 if (objectWasDragged)
282 void Dimension::SetPoint1(Vector v)
288 void Dimension::SetPoint2(Vector v)