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)
64 // If there are valid Vector pointers in here, use them to update the internal
65 // positions. Otherwise, we just use the internal positions by default.
67 position = point1.object->GetPointAtParameter(point1.t);
70 endpoint = point2.object->GetPointAtParameter(point2.t);
72 if (state == OSSelected)
73 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
75 // painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
76 painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine));
78 painter->SetBrush(QBrush(QColor(Qt::blue)));
80 // Draw an aligned dimension line
81 double angle = Vector(endpoint - position).Angle();
82 double orthoAngle = angle + (PI / 2.0);
83 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
84 Vector unit = Vector(endpoint - position).Unit();
87 // Point p1 = head - (unit * 9.0 * size);
88 // Point p2 = p1 + (orthogonal * 3.0 * size);
89 // Point p3 = p1 - (orthogonal * 3.0 * size);
92 The numbers hardcoded into here, what are they?
93 I believe they are pixels.
96 // Get our line parallel to our points
97 Point p1 = position + (orthogonal * 10.0 * size);
98 Point p2 = endpoint + (orthogonal * 10.0 * size);
100 Point p3 = position + (orthogonal * 16.0 * size);
101 Point p4 = endpoint + (orthogonal * 16.0 * size);
102 Point p5 = position + (orthogonal * 4.0 * size);
103 Point p6 = endpoint + (orthogonal * 4.0 * size);
105 // Draw extension lines
106 painter->DrawLine(p3, p5);
107 painter->DrawLine(p4, p6);
109 // Calculate whether or not the arrowheads are too crowded to put inside
110 // the extension lines. 9.0 is the length of the arrowhead.
111 // double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size));
112 double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size));
113 //printf("Dimension::Draw(): t = %lf\n", t);
115 // On the screen, it's acting like this is actually 58%...
116 // This is correct, we want it to happen at > 50%
119 // Draw main dimension line + arrowheads
120 painter->DrawLine(p1, p2);
121 painter->DrawArrowhead(p1, p2, size);
122 painter->DrawArrowhead(p2, p1, size);
126 // Draw outside arrowheads
127 Point p7 = p1 - (unit * 9.0 * size);
128 Point p8 = p2 + (unit * 9.0 * size);
129 painter->DrawArrowhead(p1, p7, size);
130 painter->DrawArrowhead(p2, p8, size);
131 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
132 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
135 // Draw length of dimension line...
136 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
137 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
141 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
144 double length = Vector(endpoint - position).Magnitude();
147 dimText = QString("%1\"").arg(length);
150 double feet = (double)((int)length / 12);
151 double inches = length - (feet * 12.0);
154 dimText = QString("%1'").arg(feet);
156 dimText = QString("%1' %2\"").arg(feet).arg(inches);
160 painter->DrawAngledText(ctr, angle, dimText, size);
164 /*virtual*/ Vector Dimension::Center(void)
166 // Technically, this is the midpoint but who are we to quibble? :-)
167 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
172 /*virtual*/ bool Dimension::Collided(Vector point)
174 // Someone told us to fuck off, so we'll fuck off. :-)
178 // We can assume this, since this is a mouse down event here.
179 objectWasDragged = false;
182 // Now that we've done our hit testing on the non-snapped point, snap it if
185 point = SnapPointToGrid(point);
190 // state = OSSelected;
192 draggingHandle1 = true;
198 // state = OSSelected;
200 draggingHandle2 = true;
209 /*virtual*/ void Dimension::PointerMoved(Vector point)
211 // We know this is true because mouse move messages don't come here unless
212 // the object was actually clicked on--therefore we *know* we're being
214 objectWasDragged = true;
218 // Here we need to check whether or not we're dragging a handle or the object itself...
219 Vector delta = point - oldPoint;
227 else*/ if (draggingHandle1)
229 Vector delta = point - oldPoint;
236 else if (draggingHandle2)
238 Vector delta = point - oldPoint;
250 /*virtual*/ void Dimension::PointerReleased(void)
252 /* if (draggingHandle1 || draggingHandle2)
254 // Set the length (in case the global state was set to fixed (or not))
255 if (Object::fixedLength)
258 if (draggingHandle1) // startpoint
260 Vector v = Vector(endpoint, position).Unit() * length;
261 position = endpoint + v;
265 Vector v = Vector(position, endpoint).Unit() * length;
266 endpoint = position + v;
271 // Otherwise, we calculate the new length, just in case on the next move
272 // it turns out to have a fixed length. :-)
273 length = Vector(endpoint - position).Magnitude();
278 draggingHandle1 = false;
279 draggingHandle2 = false;
281 // Here we check for just a click: If object was clicked and dragged, then
282 // revert to the old state (OSInactive). Otherwise, keep the new state that
284 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
285 about keeping track of old states...
287 if (objectWasDragged)
292 /*virtual*/ bool Dimension::HitTest(Point point)
294 hitPoint1 = hitPoint2 = false;
295 // Vector lineSegment(position, endpoint);
296 Vector v1(position, point);
297 Vector v2(endpoint, point);
298 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
302 // distance = v1.Magnitude();
304 // distance = v2.Magnitude();
306 // distance = ?Det?(ls, v1) / |ls|
307 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
308 // / lineSegment.Magnitude());
310 if ((v1.Magnitude() * Painter::zoom) < 8.0)
312 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
315 return (hitPoint1 || hitPoint2 ? true : false);
319 /*virtual*/ void Dimension::Enumerate(FILE * file)
321 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
325 /*virtual*/ Object * Dimension::Copy(void)
327 #warning "!!! This doesn't take care of attached Dimensions !!!"
329 This is a real problem. While having a pointer in the Dimension to this line's points
330 is fast & easy, it creates a huge problem when trying to replicate an object like this.
332 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
333 way, if you copy them, ... you might still have problems. Because you can't be sure if
334 a copy will be persistant or not, you then *definitely* do not want them to have the
335 same reference number.
338 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
344 // Dimensions are special: they contain exactly *two* points. Here, we check
345 // only for zero/non-zero in returning the correct points.
346 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
355 /*virtual*/ void Dimension::Connect(Object * obj, double param)
357 // There are four possibilities here...
358 // The param is only looking for 0 or 1 here.
359 if (point1.object == NULL && point2.object == NULL)
364 else if (point1.object == NULL && point2.object != NULL)
366 if (point2.t == param)
374 else if (point1.object != NULL && point2.object == NULL)
376 if (point1.t == param)
384 else if (point1.object != NULL && point2.object != NULL)
386 if (point1.t == param)
394 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
396 if (point1.object == obj && point1.t == param)
397 point1.object = NULL;
398 else if (point2.object == obj && point2.t == param)
399 point2.object = NULL;
403 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
405 if (point1.object == obj)
406 point1.object = NULL;
408 if (point2.object == obj)
409 point2.object = NULL;
413 /*virtual*/ QRectF Dimension::Extents(void)
419 p1 = point1.object->GetPointAtParameter(point1.t);
422 p2 = point2.object->GetPointAtParameter(point2.t);
424 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
429 /*virtual*/ ObjectType Dimension::Type(void)
436 void Dimension::FlipSides(void)
439 Vector tmp = position;
443 Connection tmp = point1;
446 // double tmp = point1.t;
447 // point1.t = point2.t;
449 // Object * tmp = point1.object;
450 // point1.object = point2.object;
451 // point2.object = tmp;