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
12 // JLH 03/14/2013 Updated to new connection system
15 #include "dimension.h"
18 #include "mathconstants.h"
22 Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/ ,Object * p/*= NULL*/):
23 Object(p1, p), endpoint(p2),
24 dragging(false), draggingHandle1(false), draggingHandle2(false),
25 length(p2.Magnitude()), dimensionType(dt), point1(NULL), point2(NULL)
31 // This is bad, p1 & p2 could be NULL, causing much consternation...
32 Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
33 dragging(false), draggingHandle1(false), draggingHandle2(false),
34 length(0), dimensionType(dt), point1(p1), point2(p2)
40 Dimension::~Dimension()
45 /*virtual*/ void Dimension::Draw(Painter * painter)
47 // If there are valid Vector pointers in here, use them to update the internal
48 // positions. Otherwise, we just use the internal positions by default.
50 position = point1.object->GetPointAtParameter(point1.t);
53 endpoint = point2.object->GetPointAtParameter(point2.t);
55 if (state == OSSelected)
56 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
58 painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
60 // Draw an aligned dimension line
61 double angle = Vector(endpoint - position).Angle();
62 double orthoAngle = angle + (PI / 2.0);
63 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
64 Vector unit = Vector(endpoint - position).Unit();
66 //NOTE: SCREEN_ZOOM is our kludge factor... We need to figure out a better
67 // way of doing this...
68 // Get our line parallel to our points
69 Point p1 = position + (orthogonal * 10.0 * SCREEN_ZOOM);
70 Point p2 = endpoint + (orthogonal * 10.0 * SCREEN_ZOOM);
72 // Draw main dimension line
73 painter->DrawLine(p1, p2);
75 Point p3 = position + (orthogonal * 16.0 * SCREEN_ZOOM);
76 Point p4 = endpoint + (orthogonal * 16.0 * SCREEN_ZOOM);
77 Point p5 = position + (orthogonal * 4.0 * SCREEN_ZOOM);
78 Point p6 = endpoint + (orthogonal * 4.0 * SCREEN_ZOOM);
80 // Draw extension lines
81 painter->DrawLine(p3, p5);
82 painter->DrawLine(p4, p6);
84 painter->SetBrush(QBrush(QColor(Qt::blue)));
85 painter->DrawArrowhead(p1, p2);
86 painter->DrawArrowhead(p2, p1);
88 // Draw length of dimension line...
89 painter->SetFont(QFont("Arial", 10.0 * Painter::zoom * SCREEN_ZOOM));
90 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
92 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
93 painter->DrawAngledText(ctr, angle, dimText);
97 /*virtual*/ Vector Dimension::Center(void)
99 // Technically, this is the midpoint but who are we to quibble? :-)
100 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
105 /*virtual*/ bool Dimension::Collided(Vector /*point*/)
108 objectWasDragged = false;
109 Vector lineSegment = endpoint - position;
110 Vector v1 = point - position;
111 Vector v2 = point - endpoint;
112 double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
114 // Geometric interpretation:
115 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
116 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
117 // then the perpendicular lies beyond the 2nd endpoint.
119 if (parameterizedPoint < 0.0)
120 distance = v1.Magnitude();
121 else if (parameterizedPoint > lineSegment.Magnitude())
122 distance = v2.Magnitude();
123 else // distance = ?Det?(ls, v1) / |ls|
124 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
126 // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
127 // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
128 // {e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is
129 // computed by first computing the area of the triangle the three points form, then dividing by the
130 // length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the
131 // triangle formed by three points is the determinant of the following matrix:
137 // By translating the start point to the origin, this can be rewritten as:
138 // By subtracting row 1 from all rows, you get the following:
139 // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
140 // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
143 // (ex - sx) (ey - sy) 0 ==> ex ey 0
144 // (px - sx) (py - sy) 0 px py 0
146 // which greatly simplifies the calculation of the determinant.
148 if (state == OSInactive)
150 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
151 //printf(" v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
152 //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);
154 //How to translate this into pixels from Document space???
155 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
156 //the caller knows about the zoom factor and all that good kinda crap
157 if (v1.Magnitude() < 10.0)
161 oldPoint = position; //maybe "position"?
162 draggingHandle1 = true;
165 else if (v2.Magnitude() < 10.0)
169 oldPoint = endpoint; //maybe "position"?
170 draggingHandle2 = true;
173 else if (distance < 2.0)
182 else if (state == OSSelected)
184 // Here we test for collision with handles as well! (SOON!)
187 if (v1.Magnitude() < 2.0) // Handle #1
188 else if (v2.Magnitude() < 2.0) // Handle #2
193 // state = OSInactive;
206 /*virtual*/ void Dimension::PointerMoved(Vector point)
208 // We know this is true because mouse move messages don't come here unless
209 // the object was actually clicked on--therefore we *know* we're being
211 objectWasDragged = true;
215 // Here we need to check whether or not we're dragging a handle or the object itself...
216 Vector delta = point - oldPoint;
224 else if (draggingHandle1)
226 Vector delta = point - oldPoint;
233 else if (draggingHandle2)
235 Vector delta = point - oldPoint;
247 /*virtual*/ void Dimension::PointerReleased(void)
249 if (draggingHandle1 || draggingHandle2)
251 // Set the length (in case the global state was set to fixed (or not))
252 if (Object::fixedLength)
255 if (draggingHandle1) // startpoint
257 Vector v = Vector(position - endpoint).Unit() * length;
258 position = endpoint + v;
262 // Vector v1 = endpoint - position;
263 Vector v = Vector(endpoint - position).Unit() * length;
264 endpoint = position + v;
269 // Otherwise, we calculate the new length, just in case on the next move
270 // it turns out to have a fixed length. :-)
271 length = Vector(endpoint - position).Magnitude();
276 draggingHandle1 = false;
277 draggingHandle2 = false;
279 // Here we check for just a click: If object was clicked and dragged, then
280 // revert to the old state (OSInactive). Otherwise, keep the new state that
282 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
283 about keeping track of old states...
285 if (objectWasDragged)
290 /*virtual*/ void Dimension::Enumerate(FILE * file)
292 fprintf(file, "DIMENSION (%lf,%lf) (%lf,%lf) %i\n", position.x, position.y, endpoint.x, endpoint.y, type);
296 // Dimensions are special: they contain exactly *two* points. Here, we check
297 // only for zero/non-zero in returning the correct points.
298 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
307 /*virtual*/ void Dimension::Connect(Object * obj, double param)
309 // There are four possibilities here...
310 // The param is only looking for 0 or 1 here.
311 if (point1.object == NULL && point2.object == NULL)
316 else if (point1.object == NULL && point2.object != NULL)
318 if (point2.t == param)
326 else if (point1.object != NULL && point2.object == NULL)
328 if (point1.t == param)
336 else if (point1.object != NULL && point2.object != NULL)
338 if (point1.t == param)
346 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
348 if (point1.object == obj && point1.t == param)
349 point1.object = NULL;
350 else if (point2.object == obj && point2.t == param)
351 point2.object = NULL;
355 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
357 if (point1.object == obj)
358 point1.object = NULL;
360 if (point2.object == obj)
361 point2.object = NULL;
365 /*virtual*/ QRectF Dimension::Extents(void)
371 p1 = point1.object->GetPointAtParameter(point1.t);
374 p2 = point2.object->GetPointAtParameter(point2.t);
376 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
381 /*virtual*/ ObjectType Dimension::Type(void)
388 void Dimension::FlipSides(void)
391 Vector tmp = position;
395 Connection tmp = point1;
398 // double tmp = point1.t;
399 // point1.t = point2.t;
401 // Object * tmp = point1.object;
402 // point1.object = point2.object;
403 // point2.object = tmp;