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), size(0.25), point1(NULL), point2(NULL)
27 // We set the size to 1/4 base unit. Could be anything.
32 // This is bad, p1 & p2 could be NULL, causing much consternation...
33 Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
34 dragging(false), draggingHandle1(false), draggingHandle2(false),
35 length(0), dimensionType(dt), size(0.25), point1(p1), point2(p2)
41 Dimension::~Dimension()
46 /*virtual*/ void Dimension::Draw(Painter * painter)
48 // If there are valid Vector pointers in here, use them to update the internal
49 // positions. Otherwise, we just use the internal positions by default.
51 position = point1.object->GetPointAtParameter(point1.t);
54 endpoint = point2.object->GetPointAtParameter(point2.t);
56 if (state == OSSelected)
57 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
59 painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
61 // Draw an aligned dimension line
62 double angle = Vector(endpoint - position).Angle();
63 double orthoAngle = angle + (PI / 2.0);
64 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
65 Vector unit = Vector(endpoint - position).Unit();
68 //NOTE: SCREEN_ZOOM is our kludge factor... We need to figure out a better
69 // way of doing this...
70 // Get our line parallel to our points
71 Point p1 = position + (orthogonal * 10.0 * SCREEN_ZOOM);
72 Point p2 = endpoint + (orthogonal * 10.0 * SCREEN_ZOOM);
74 // Draw main dimension line
75 painter->DrawLine(p1, p2);
77 Point p3 = position + (orthogonal * 16.0 * SCREEN_ZOOM);
78 Point p4 = endpoint + (orthogonal * 16.0 * SCREEN_ZOOM);
79 Point p5 = position + (orthogonal * 4.0 * SCREEN_ZOOM);
80 Point p6 = endpoint + (orthogonal * 4.0 * SCREEN_ZOOM);
83 The numbers hardcoded into here, what are they?
84 I believe they are pixels.
87 // Get our line parallel to our points
88 Point p1 = position + (orthogonal * 10.0 * size);
89 Point p2 = endpoint + (orthogonal * 10.0 * size);
91 // Draw main dimension line
92 painter->DrawLine(p1, p2);
94 Point p3 = position + (orthogonal * 16.0 * size);
95 Point p4 = endpoint + (orthogonal * 16.0 * size);
96 Point p5 = position + (orthogonal * 4.0 * size);
97 Point p6 = endpoint + (orthogonal * 4.0 * size);
100 // Draw extension lines
101 painter->DrawLine(p3, p5);
102 painter->DrawLine(p4, p6);
104 painter->SetBrush(QBrush(QColor(Qt::blue)));
105 // painter->DrawArrowhead(p1, p2);
106 // painter->DrawArrowhead(p2, p1);
107 painter->DrawArrowhead(p1, p2, size);
108 painter->DrawArrowhead(p2, p1, size);
110 // Draw length of dimension line...
111 // painter->SetFont(QFont("Arial", 10.0 * Painter::zoom * SCREEN_ZOOM));
112 painter->SetFont(QFont("Arial", 10.0 * Painter::zoom * size));
113 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
115 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
116 // painter->DrawAngledText(ctr, angle, dimText);
117 painter->DrawAngledText(ctr, angle, dimText, size);
121 /*virtual*/ Vector Dimension::Center(void)
123 // Technically, this is the midpoint but who are we to quibble? :-)
124 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
129 /*virtual*/ bool Dimension::Collided(Vector /*point*/)
132 objectWasDragged = false;
133 Vector lineSegment = endpoint - position;
134 Vector v1 = point - position;
135 Vector v2 = point - endpoint;
136 double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
138 // Geometric interpretation:
139 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
140 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
141 // then the perpendicular lies beyond the 2nd endpoint.
143 if (parameterizedPoint < 0.0)
144 distance = v1.Magnitude();
145 else if (parameterizedPoint > lineSegment.Magnitude())
146 distance = v2.Magnitude();
147 else // distance = ?Det?(ls, v1) / |ls|
148 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
150 // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
151 // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
152 // {e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is
153 // computed by first computing the area of the triangle the three points form, then dividing by the
154 // length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the
155 // triangle formed by three points is the determinant of the following matrix:
161 // By translating the start point to the origin, this can be rewritten as:
162 // By subtracting row 1 from all rows, you get the following:
163 // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
164 // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
167 // (ex - sx) (ey - sy) 0 ==> ex ey 0
168 // (px - sx) (py - sy) 0 px py 0
170 // which greatly simplifies the calculation of the determinant.
172 if (state == OSInactive)
174 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
175 //printf(" v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
176 //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);
178 //How to translate this into pixels from Document space???
179 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
180 //the caller knows about the zoom factor and all that good kinda crap
181 if (v1.Magnitude() < 10.0)
185 oldPoint = position; //maybe "position"?
186 draggingHandle1 = true;
189 else if (v2.Magnitude() < 10.0)
193 oldPoint = endpoint; //maybe "position"?
194 draggingHandle2 = true;
197 else if (distance < 2.0)
206 else if (state == OSSelected)
208 // Here we test for collision with handles as well! (SOON!)
211 if (v1.Magnitude() < 2.0) // Handle #1
212 else if (v2.Magnitude() < 2.0) // Handle #2
217 // state = OSInactive;
230 /*virtual*/ void Dimension::PointerMoved(Vector point)
232 // We know this is true because mouse move messages don't come here unless
233 // the object was actually clicked on--therefore we *know* we're being
235 objectWasDragged = true;
239 // Here we need to check whether or not we're dragging a handle or the object itself...
240 Vector delta = point - oldPoint;
248 else if (draggingHandle1)
250 Vector delta = point - oldPoint;
257 else if (draggingHandle2)
259 Vector delta = point - oldPoint;
271 /*virtual*/ void Dimension::PointerReleased(void)
273 if (draggingHandle1 || draggingHandle2)
275 // Set the length (in case the global state was set to fixed (or not))
276 if (Object::fixedLength)
279 if (draggingHandle1) // startpoint
281 Vector v = Vector(position - endpoint).Unit() * length;
282 position = endpoint + v;
286 // Vector v1 = endpoint - position;
287 Vector v = Vector(endpoint - position).Unit() * length;
288 endpoint = position + v;
293 // Otherwise, we calculate the new length, just in case on the next move
294 // it turns out to have a fixed length. :-)
295 length = Vector(endpoint - position).Magnitude();
300 draggingHandle1 = false;
301 draggingHandle2 = false;
303 // Here we check for just a click: If object was clicked and dragged, then
304 // revert to the old state (OSInactive). Otherwise, keep the new state that
306 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
307 about keeping track of old states...
309 if (objectWasDragged)
314 /*virtual*/ void Dimension::Enumerate(FILE * file)
316 fprintf(file, "DIMENSION (%lf,%lf) (%lf,%lf) %i\n", position.x, position.y, endpoint.x, endpoint.y, type);
320 // Dimensions are special: they contain exactly *two* points. Here, we check
321 // only for zero/non-zero in returning the correct points.
322 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
331 /*virtual*/ void Dimension::Connect(Object * obj, double param)
333 // There are four possibilities here...
334 // The param is only looking for 0 or 1 here.
335 if (point1.object == NULL && point2.object == NULL)
340 else if (point1.object == NULL && point2.object != NULL)
342 if (point2.t == param)
350 else if (point1.object != NULL && point2.object == NULL)
352 if (point1.t == param)
360 else if (point1.object != NULL && point2.object != NULL)
362 if (point1.t == param)
370 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
372 if (point1.object == obj && point1.t == param)
373 point1.object = NULL;
374 else if (point2.object == obj && point2.t == param)
375 point2.object = NULL;
379 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
381 if (point1.object == obj)
382 point1.object = NULL;
384 if (point2.object == obj)
385 point2.object = NULL;
389 /*virtual*/ QRectF Dimension::Extents(void)
395 p1 = point1.object->GetPointAtParameter(point1.t);
398 p2 = point2.object->GetPointAtParameter(point2.t);
400 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
405 /*virtual*/ ObjectType Dimension::Type(void)
412 void Dimension::FlipSides(void)
415 Vector tmp = position;
419 Connection tmp = point1;
422 // double tmp = point1.t;
423 // point1.t = point2.t;
425 // Object * tmp = point1.object;
426 // point1.object = point2.object;
427 // point2.object = tmp;