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, DimensionType dt/*= DTLinear*/ ,Object * p/*= NULL*/):
22 Object(p1, p), endpoint(p2),
23 dragging(false), draggingHandle1(false), draggingHandle2(false),
24 length(p2.Magnitude()), type(dt), point1(NULL), point2(NULL)
30 // This is bad, p1 & p2 could be NULL, causing much consternation...
31 Dimension::Dimension(Vector * p1, Vector * p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
32 Object(*p1, p), endpoint(*p2),
33 dragging(false), draggingHandle1(false), draggingHandle2(false),
34 length(p2->Magnitude()), type(dt), point1(p1), point2(p2)
40 // This is bad, p1 & p2 could be NULL, causing much consternation...
41 Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
42 /* Object(p1.object->GetPointForParameter(p1.t), p),
43 endpoint(p2.object->GetPointForParameter(p2.t)),*/
44 dragging(false), draggingHandle1(false), draggingHandle2(false),
45 /*length(p2->Magnitude()),*/length(0), type(dt), point1(p1), point2(p2)
50 Dimension::~Dimension()
55 /*virtual*/ void Dimension::Draw(Painter * painter)
57 // If there are valid Vector pointers in here, use them to update the internal
58 // positions. Otherwise, we just use the internal positions by default.
67 position = point1.object->GetPointAtParameter(point1.t);
70 endpoint = point2.object->GetPointAtParameter(point2.t);
73 if (state == OSSelected)
74 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
76 painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
78 // Draw an aligned dimension line
79 double angle = Vector(endpoint - position).Angle();
80 double orthoAngle = angle + (PI / 2.0);
81 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
82 Vector unit = Vector(endpoint - position).Unit();
84 //NOTE: SCREEN_ZOOM is our kludge factor... We need to figure out a better
85 // way of doing this...
86 // Get our line parallel to our points
87 Point p1 = position + (orthogonal * 10.0 * SCREEN_ZOOM);
88 Point p2 = endpoint + (orthogonal * 10.0 * SCREEN_ZOOM);
90 // Draw main dimension line
91 painter->DrawLine(p1, p2);
93 Point p3 = position + (orthogonal * 16.0 * SCREEN_ZOOM);
94 Point p4 = endpoint + (orthogonal * 16.0 * SCREEN_ZOOM);
95 Point p5 = position + (orthogonal * 4.0 * SCREEN_ZOOM);
96 Point p6 = endpoint + (orthogonal * 4.0 * SCREEN_ZOOM);
98 // Draw extension lines
99 painter->DrawLine(p3, p5);
100 painter->DrawLine(p4, p6);
102 painter->SetBrush(QBrush(QColor(Qt::blue)));
103 painter->DrawArrowhead(p1, p2);
104 painter->DrawArrowhead(p2, p1);
106 // Draw length of dimension line...
107 painter->SetFont(QFont("Arial", 10.0 * Painter::zoom * SCREEN_ZOOM));
108 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
110 // This is in pixels, which isn't even remotely correct... !!! FIX !!!
111 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
112 // int textWidth = QFontMetrics(painter->font()).width(dimText);
113 // int textHeight = QFontMetrics(painter->font()).height();
115 //We have to do transformation voodoo to make the text come out readable and in correct orientation...
116 //Some things to note here: if angle > 90 degrees, then we need to take the negative of the angle
119 painter->translate(ctr.x, ctr.y);
121 //16 : printf("textHeight: %d\n", textHeight);
123 //Fix text so it isn't upside down...
124 if ((angle > PI * 0.5) && (angle < PI * 1.5))
130 painter->rotate(angle * RADIANS_TO_DEGREES);
131 painter->scale(1.0, -1.0);
132 //painter->translate(-textWidth / 2, -24);
133 // painter->drawText(0, 0, textWidth, 20, Qt::AlignCenter, dimText);
134 // This version draws the y-coord from the baseline of the font
135 painter->DrawText(-textWidth / 2, yOffset, dimText);
136 //painter->setPen(QPen(QColor(0xFF, 0x20, 0x20), 1.0, Qt::SolidLine));
137 //painter->drawLine(20, 0, -20, 0);
138 //painter->drawLine(0, 20, 0, -20);
141 // painter->DrawText(QRectF(QPointF(ctr.x, ctr.y), QPointF(ctr.x + textWidth, ctr.y + textHeight)), Qt::AlignVCenter, dimText);
142 // Now that we've taken our own good advice, maybe we should have the painter class
143 // do a nice abstracted text draw routine? :-)
144 painter->DrawAngledText(ctr, angle, dimText);
148 All of the preceeding makes me think that rather than try to compensate for Qt's unbelieveably
149 AWFUL decision to go with a wrong-handed graphics subsystem, it may be better to just stuff
150 all of that crap into some kind of subclass that handles all the nastiness behind the scenes.
151 I mean, really, all this crap just to get some proplerly rendered text on the screen? How
152 retarded is that? :-/
157 /*virtual*/ Vector Dimension::Center(void)
159 // Technically, this is the midpoint but who are we to quibble? :-)
160 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
165 /*virtual*/ bool Dimension::Collided(Vector /*point*/)
168 objectWasDragged = false;
169 Vector lineSegment = endpoint - position;
170 Vector v1 = point - position;
171 Vector v2 = point - endpoint;
172 double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
174 // Geometric interpretation:
175 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
176 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
177 // then the perpendicular lies beyond the 2nd endpoint.
179 if (parameterizedPoint < 0.0)
180 distance = v1.Magnitude();
181 else if (parameterizedPoint > lineSegment.Magnitude())
182 distance = v2.Magnitude();
183 else // distance = ?Det?(ls, v1) / |ls|
184 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
186 // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
187 // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
188 // {e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is
189 // computed by first computing the area of the triangle the three points form, then dividing by the
190 // length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the
191 // triangle formed by three points is the determinant of the following matrix:
197 // By translating the start point to the origin, this can be rewritten as:
198 // By subtracting row 1 from all rows, you get the following:
199 // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
200 // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
203 // (ex - sx) (ey - sy) 0 ==> ex ey 0
204 // (px - sx) (py - sy) 0 px py 0
206 // which greatly simplifies the calculation of the determinant.
208 if (state == OSInactive)
210 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
211 //printf(" v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
212 //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);
214 //How to translate this into pixels from Document space???
215 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
216 //the caller knows about the zoom factor and all that good kinda crap
217 if (v1.Magnitude() < 10.0)
221 oldPoint = position; //maybe "position"?
222 draggingHandle1 = true;
225 else if (v2.Magnitude() < 10.0)
229 oldPoint = endpoint; //maybe "position"?
230 draggingHandle2 = true;
233 else if (distance < 2.0)
242 else if (state == OSSelected)
244 // Here we test for collision with handles as well! (SOON!)
247 if (v1.Magnitude() < 2.0) // Handle #1
248 else if (v2.Magnitude() < 2.0) // Handle #2
253 // state = OSInactive;
266 /*virtual*/ void Dimension::PointerMoved(Vector point)
268 // We know this is true because mouse move messages don't come here unless
269 // the object was actually clicked on--therefore we *know* we're being
271 objectWasDragged = true;
275 // Here we need to check whether or not we're dragging a handle or the object itself...
276 Vector delta = point - oldPoint;
284 else if (draggingHandle1)
286 Vector delta = point - oldPoint;
293 else if (draggingHandle2)
295 Vector delta = point - oldPoint;
307 /*virtual*/ void Dimension::PointerReleased(void)
309 if (draggingHandle1 || draggingHandle2)
311 // Set the length (in case the global state was set to fixed (or not))
312 if (Object::fixedLength)
315 if (draggingHandle1) // startpoint
317 Vector v = Vector(position - endpoint).Unit() * length;
318 position = endpoint + v;
322 // Vector v1 = endpoint - position;
323 Vector v = Vector(endpoint - position).Unit() * length;
324 endpoint = position + v;
329 // Otherwise, we calculate the new length, just in case on the next move
330 // it turns out to have a fixed length. :-)
331 length = Vector(endpoint - position).Magnitude();
336 draggingHandle1 = false;
337 draggingHandle2 = false;
339 // Here we check for just a click: If object was clicked and dragged, then
340 // revert to the old state (OSInactive). Otherwise, keep the new state that
342 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
343 about keeping track of old states...
345 if (objectWasDragged)
351 void Dimension::SetPoint1(Vector * v)
358 void Dimension::SetPoint2(Vector * v)
366 /*virtual*/ void Dimension::Enumerate(FILE * file)
368 fprintf(file, "DIMENSION (%lf,%lf) (%lf,%lf) %i\n", position.x, position.y, endpoint.x, endpoint.y, type);
372 // Dimensions are special: they contain exactly *two* points. Here, we check
373 // only for zero/non-zero in returning the correct points.
374 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
383 /*virtual*/ void Dimension::Connect(Object * obj, double param)
385 // There are four possibilities here...
386 // The param is only looking for 0 or 1 here.
387 if (point1.object == NULL && point2.object == NULL)
392 else if (point1.object == NULL && point2.object != NULL)
394 if (point2.t == param)
402 else if (point1.object != NULL && point2.object == NULL)
404 if (point1.t == param)
412 else if (point1.object != NULL && point2.object != NULL)
414 if (point1.t == param)
422 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
424 if (point1.object == obj && point1.t == param)
426 else if (point2.object == obj && point2.t == param)
431 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
433 if (point1.object == obj)
436 if (point2.object == obj)
442 Vector Dimension::GetPoint1(void)
448 Vector Dimension::GetPoint2(void)
455 void Dimension::FlipSides(void)
458 Vector tmp = position;
462 Connection tmp = point1;
465 // double tmp = point1.t;
466 // point1.t = point2.t;
468 // Object * tmp = point1.object;
469 // point1.object = point2.object;
470 // point2.object = tmp;