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"
19 #include "mathconstants.h"
23 Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/, Object * p/*= NULL*/):
24 Object(p1, p), endpoint(p2),
25 dragging(false), draggingHandle1(false), draggingHandle2(false),
26 length(p2.Magnitude()), dimensionType(dt), size(0.25), point1(NULL), point2(NULL)
28 // We set the size to 1/4 base unit. Could be anything.
33 // This is bad, p1 & p2 could be NULL, causing much consternation...
34 Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/, Object * p/*= NULL*/):
35 dragging(false), draggingHandle1(false), draggingHandle2(false),
36 length(0), dimensionType(dt), size(0.25), point1(p1), point2(p2)
42 Dimension::~Dimension()
48 The approach used below creates a hierarchy: Dimension is subservient to Line.
50 Does this solve our problem of connected objects? Maybe, partially. Let's think this
51 through. It only works for endpoints, not points in the middle...
53 Also: this is bad, depending on the Draw() function to update the internal
54 position(s) of the data of the object! (is it though?)
56 How to move: click once moves only the object/point clicked on, all connected
57 objects deform themselves accordingly. click twice selects ALL connected objects;
58 all objects move as a unified whole.
62 /*virtual*/ void Dimension::Draw(Painter * painter)
65 // If there are valid Vector pointers in here, use them to update the internal
66 // positions. Otherwise, we just use the internal positions by default.
68 position = point1.object->GetPointAtParameter(point1.t);
71 endpoint = point2.object->GetPointAtParameter(point2.t);
74 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
76 if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
77 painter->DrawHandle(position);
79 if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
80 painter->DrawHandle(endpoint);
82 if (state == OSSelected)
83 painter->SetPen(QPen(Qt::cyan, 1.0 * Painter::zoom * size, Qt::SolidLine));
85 // painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
87 painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine));
89 painter->SetBrush(QBrush(QColor(Qt::blue)));
91 // Draw an aligned dimension line
92 double angle = Vector(endpoint - position).Angle();
93 double orthoAngle = angle + (PI / 2.0);
94 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
95 Vector unit = Vector(endpoint - position).Unit();
98 // Point p1 = head - (unit * 9.0 * size);
99 // Point p2 = p1 + (orthogonal * 3.0 * size);
100 // Point p3 = p1 - (orthogonal * 3.0 * size);
103 The numbers hardcoded into here, what are they?
104 I believe they are pixels.
107 // Get our line parallel to our points
108 Point p1 = position + (orthogonal * 10.0 * size);
109 Point p2 = endpoint + (orthogonal * 10.0 * size);
111 Point p3 = position + (orthogonal * 16.0 * size);
112 Point p4 = endpoint + (orthogonal * 16.0 * size);
113 Point p5 = position + (orthogonal * 4.0 * size);
114 Point p6 = endpoint + (orthogonal * 4.0 * size);
116 // Draw extension lines
117 painter->DrawLine(p3, p5);
118 painter->DrawLine(p4, p6);
120 // Calculate whether or not the arrowheads are too crowded to put inside
121 // the extension lines. 9.0 is the length of the arrowhead.
122 // double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size));
123 double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size));
124 //printf("Dimension::Draw(): t = %lf\n", t);
126 // On the screen, it's acting like this is actually 58%...
127 // This is correct, we want it to happen at > 50%
130 // Draw main dimension line + arrowheads
131 painter->DrawLine(p1, p2);
132 painter->DrawArrowhead(p1, p2, size);
133 painter->DrawArrowhead(p2, p1, size);
137 // Draw outside arrowheads
138 Point p7 = p1 - (unit * 9.0 * size);
139 Point p8 = p2 + (unit * 9.0 * size);
140 painter->DrawArrowhead(p1, p7, size);
141 painter->DrawArrowhead(p2, p8, size);
142 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
143 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
146 // Draw length of dimension line...
147 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
148 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
152 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
155 double length = Vector(endpoint - position).Magnitude();
158 dimText = QString("%1\"").arg(length);
161 double feet = (double)((int)length / 12);
162 double inches = length - (feet * 12.0);
165 dimText = QString("%1'").arg(feet);
167 dimText = QString("%1' %2\"").arg(feet).arg(inches);
171 painter->DrawAngledText(ctr, angle, dimText, size);
175 /*virtual*/ Vector Dimension::Center(void)
177 // Technically, this is the midpoint but who are we to quibble? :-)
178 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
183 /*virtual*/ bool Dimension::Collided(Vector point)
185 // Someone told us to fuck off, so we'll fuck off. :-)
189 // We can assume this, since this is a mouse down event here.
190 objectWasDragged = false;
193 // Now that we've done our hit testing on the non-snapped point, snap it if
196 point = SnapPointToGrid(point);
203 draggingHandle1 = true;
211 draggingHandle2 = true;
220 /*virtual*/ void Dimension::PointerMoved(Vector point)
222 if (selectionInProgress)
224 // Check for whether or not the rect contains this line
225 if (selection.contains(position.x, position.y)
226 && selection.contains(endpoint.x, endpoint.y))
234 // Hit test tells us what we hit (if anything) through boolean variables. (It
235 // also tells us whether or not the state changed. --not any more)
238 needUpdate = HitStateChanged();
240 objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
242 if (objectWasDragged)
244 Vector delta = point - oldPoint;
246 if (draggingHandle1)// || draggingLine)
249 if (draggingHandle2)// || draggingLine)
258 /*virtual*/ void Dimension::PointerReleased(void)
260 /* if (draggingHandle1 || draggingHandle2)
262 // Set the length (in case the global state was set to fixed (or not))
263 if (Object::fixedLength)
266 if (draggingHandle1) // startpoint
268 Vector v = Vector(endpoint, position).Unit() * length;
269 position = endpoint + v;
273 Vector v = Vector(position, endpoint).Unit() * length;
274 endpoint = position + v;
279 // Otherwise, we calculate the new length, just in case on the next move
280 // it turns out to have a fixed length. :-)
281 length = Vector(endpoint - position).Magnitude();
286 draggingHandle1 = false;
287 draggingHandle2 = false;
289 // Here we check for just a click: If object was clicked and dragged, then
290 // revert to the old state (OSInactive). Otherwise, keep the new state that
292 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
293 about keeping track of old states...
295 if (objectWasDragged)
300 /*virtual*/ bool Dimension::HitTest(Point point)
302 hitPoint1 = hitPoint2 = false;
303 // Vector lineSegment(position, endpoint);
304 Vector v1(position, point);
305 Vector v2(endpoint, point);
306 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
310 // distance = v1.Magnitude();
312 // distance = v2.Magnitude();
314 // distance = ?Det?(ls, v1) / |ls|
315 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
316 // / lineSegment.Magnitude());
318 if ((v1.Magnitude() * Painter::zoom) < 8.0)
320 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
323 return (hitPoint1 || hitPoint2 ? true : false);
327 void Dimension::SaveHitState(void)
329 oldHitPoint1 = hitPoint1;
330 oldHitPoint2 = hitPoint2;
331 // oldHitLine = hitLine;
335 bool Dimension::HitStateChanged(void)
337 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2))
344 /*virtual*/ void Dimension::Enumerate(FILE * file)
346 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
350 /*virtual*/ Object * Dimension::Copy(void)
352 #warning "!!! This doesn't take care of attached Dimensions !!!"
354 This is a real problem. While having a pointer in the Dimension to this line's points
355 is fast & easy, it creates a huge problem when trying to replicate an object like this.
357 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
358 way, if you copy them, ... you might still have problems. Because you can't be sure if
359 a copy will be persistant or not, you then *definitely* do not want them to have the
360 same reference number.
363 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
369 // Dimensions are special: they contain exactly *two* points. Here, we check
370 // only for zero/non-zero in returning the correct points.
371 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
380 /*virtual*/ void Dimension::MovePointAtParameter(double parameter, Vector v)
384 else if (parameter == 1.0)
387 {} // Not sure how to handle this case :-P
391 /*virtual*/ void Dimension::Connect(Object * obj, double param)
393 // There are four possibilities here...
394 // The param is only looking for 0 or 1 here.
395 if (point1.object == NULL && point2.object == NULL)
400 else if (point1.object == NULL && point2.object != NULL)
402 if (point2.t == param)
410 else if (point1.object != NULL && point2.object == NULL)
412 if (point1.t == param)
420 else if (point1.object != NULL && point2.object != NULL)
422 if (point1.t == param)
430 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
432 if (point1.object == obj && point1.t == param)
433 point1.object = NULL;
434 else if (point2.object == obj && point2.t == param)
435 point2.object = NULL;
439 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
441 if (point1.object == obj)
442 point1.object = NULL;
444 if (point2.object == obj)
445 point2.object = NULL;
449 /*virtual*/ QRectF Dimension::Extents(void)
455 p1 = point1.object->GetPointAtParameter(point1.t);
458 p2 = point2.object->GetPointAtParameter(point2.t);
460 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
465 /*virtual*/ ObjectType Dimension::Type(void)
472 void Dimension::FlipSides(void)
475 Vector tmp = position;
479 Connection tmp = point1;
482 // double tmp = point1.t;
483 // point1.t = point2.t;
485 // Object * tmp = point1.object;
486 // point1.object = point2.object;
487 // point2.object = tmp;