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 Vector v(position, endpoint);
93 double angle = v.Angle();
94 // double orthoAngle = angle + (PI / 2.0);
95 // Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
96 Vector orthogonal = Vector::Normal(position, endpoint);
97 Vector unit = v.Unit();
100 The numbers hardcoded into here, what are they?
101 I believe they are pixels.
104 // Get our line parallel to our points
105 Point p1 = position + (orthogonal * 10.0 * size);
106 Point p2 = endpoint + (orthogonal * 10.0 * size);
108 Point p3 = position + (orthogonal * 16.0 * size);
109 Point p4 = endpoint + (orthogonal * 16.0 * size);
110 Point p5 = position + (orthogonal * 4.0 * size);
111 Point p6 = endpoint + (orthogonal * 4.0 * size);
113 // Draw extension lines
114 painter->DrawLine(p3, p5);
115 painter->DrawLine(p4, p6);
117 // Calculate whether or not the arrowheads are too crowded to put inside
118 // the extension lines. 9.0 is the length of the arrowhead.
119 // double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size));
120 double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size));
121 //printf("Dimension::Draw(): t = %lf\n", t);
123 // On the screen, it's acting like this is actually 58%...
124 // This is correct, we want it to happen at > 50%
127 // Draw main dimension line + arrowheads
128 painter->DrawLine(p1, p2);
129 painter->DrawArrowhead(p1, p2, size);
130 painter->DrawArrowhead(p2, p1, size);
134 // Draw outside arrowheads
135 Point p7 = p1 - (unit * 9.0 * size);
136 Point p8 = p2 + (unit * 9.0 * size);
137 painter->DrawArrowhead(p1, p7, size);
138 painter->DrawArrowhead(p2, p8, size);
139 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
140 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
143 // Draw length of dimension line...
144 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
145 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
149 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
152 // double length = Vector(endpoint - position).Magnitude();
153 double length = v.Magnitude();
156 dimText = QString("%1\"").arg(length);
159 double feet = (double)((int)length / 12);
160 double inches = length - (feet * 12.0);
163 dimText = QString("%1'").arg(feet);
165 dimText = QString("%1' %2\"").arg(feet).arg(inches);
169 painter->DrawAngledText(ctr, angle, dimText, size);
173 Point p9 = ((position + endpoint) / 2.0) + (orthogonal * 14.0)
176 Point p10 = p9 + (orthogonal * -7.0);
177 Point p11 = p10 + (unit * 7.0);
178 Point p12 = p11 + (orthogonal * 7.0);
179 Point p13 = p12 + (unit * -7.0);
180 painter->DrawLine(p10, p11);
181 painter->DrawLine(p11, p12);
182 painter->DrawLine(p12, p13);
183 painter->DrawLine(p13, p10);
188 /*virtual*/ Vector Dimension::Center(void)
190 // Technically, this is the midpoint but who are we to quibble? :-)
191 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
196 /*virtual*/ bool Dimension::Collided(Vector point)
198 // Someone told us to fuck off, so we'll fuck off. :-)
202 // We can assume this, since this is a mouse down event here.
203 objectWasDragged = false;
206 // Now that we've done our hit testing on the non-snapped point, snap it if
209 point = SnapPointToGrid(point);
216 draggingHandle1 = true;
224 draggingHandle2 = true;
233 /*virtual*/ void Dimension::PointerMoved(Vector point)
235 if (selectionInProgress)
237 // Check for whether or not the rect contains this line
238 if (selection.contains(position.x, position.y)
239 && selection.contains(endpoint.x, endpoint.y))
247 // Hit test tells us what we hit (if anything) through boolean variables. (It
248 // also tells us whether or not the state changed. --not any more)
251 needUpdate = HitStateChanged();
253 objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
255 if (objectWasDragged)
257 Vector delta = point - oldPoint;
259 if (draggingHandle1)// || draggingLine)
262 if (draggingHandle2)// || draggingLine)
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(endpoint, position).Unit() * length;
282 position = endpoint + v;
286 Vector v = Vector(position, endpoint).Unit() * length;
287 endpoint = position + v;
292 // Otherwise, we calculate the new length, just in case on the next move
293 // it turns out to have a fixed length. :-)
294 length = Vector(endpoint - position).Magnitude();
299 draggingHandle1 = false;
300 draggingHandle2 = false;
302 // Here we check for just a click: If object was clicked and dragged, then
303 // revert to the old state (OSInactive). Otherwise, keep the new state that
305 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
306 about keeping track of old states...
308 if (objectWasDragged)
313 /*virtual*/ bool Dimension::HitTest(Point point)
315 Vector orthogonal = Vector::Normal(position, endpoint);
316 // Get our line parallel to our points
317 Point p1 = position + (orthogonal * 10.0 * size);
318 Point p2 = endpoint + (orthogonal * 10.0 * size);
321 hitPoint1 = hitPoint2 = hitLine = hitFlipSwitch = false;
322 Vector v1(position, point);
323 Vector v2(endpoint, point);
324 // Vector lineSegment(position, endpoint);
325 Vector lineSegment(p1, p2);
326 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
327 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
331 distance = v1.Magnitude();
333 distance = v2.Magnitude();
335 // distance = ?Det?(ls, v1) / |ls|
336 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
337 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
338 / lineSegment.Magnitude());
340 if ((v1.Magnitude() * Painter::zoom) < 8.0)
342 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
344 else if ((distance * Painter::zoom) < 5.0)
347 return (hitPoint1 || hitPoint2 || hitLine ? true : false);
351 void Dimension::SaveHitState(void)
353 oldHitPoint1 = hitPoint1;
354 oldHitPoint2 = hitPoint2;
355 oldHitLine = hitLine;
359 bool Dimension::HitStateChanged(void)
361 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
368 /*virtual*/ void Dimension::Enumerate(FILE * file)
370 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
374 /*virtual*/ Object * Dimension::Copy(void)
376 #warning "!!! This doesn't take care of attached Dimensions !!!"
378 This is a real problem. While having a pointer in the Dimension to this line's points
379 is fast & easy, it creates a huge problem when trying to replicate an object like this.
381 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
382 way, if you copy them, ... you might still have problems. Because you can't be sure if
383 a copy will be persistant or not, you then *definitely* do not want them to have the
384 same reference number.
387 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
393 // Dimensions are special: they contain exactly *two* points. Here, we check
394 // only for zero/non-zero in returning the correct points.
395 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
404 /*virtual*/ void Dimension::MovePointAtParameter(double parameter, Vector v)
408 else if (parameter == 1.0)
411 {} // Not sure how to handle this case :-P
415 /*virtual*/ void Dimension::Connect(Object * obj, double param)
417 // There are four possibilities here...
418 // The param is only looking for 0 or 1 here.
419 if (point1.object == NULL && point2.object == NULL)
424 else if (point1.object == NULL && point2.object != NULL)
426 if (point2.t == param)
434 else if (point1.object != NULL && point2.object == NULL)
436 if (point1.t == param)
444 else if (point1.object != NULL && point2.object != NULL)
446 if (point1.t == param)
454 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
456 if (point1.object == obj && point1.t == param)
457 point1.object = NULL;
458 else if (point2.object == obj && point2.t == param)
459 point2.object = NULL;
463 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
465 if (point1.object == obj)
466 point1.object = NULL;
468 if (point2.object == obj)
469 point2.object = NULL;
473 /*virtual*/ QRectF Dimension::Extents(void)
478 // if (point1.object)
479 // p1 = point1.object->GetPointAtParameter(point1.t);
481 // if (point2.object)
482 // p2 = point2.object->GetPointAtParameter(point2.t);
484 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
489 /*virtual*/ ObjectType Dimension::Type(void)
496 void Dimension::FlipSides(void)
499 Vector tmp = position;
502 //Not sure this matters...
503 //#warning "!!! May need to swap parameter values on connected objects !!!"
505 Connection tmp = point1;
508 // double tmp = point1.t;
509 // point1.t = point2.t;
511 // Object * tmp = point1.object;
512 // point1.object = point2.object;
513 // point2.object = tmp;