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 How to move: click once moves only the object/point clicked on, all connected
49 objects deform themselves accordingly. click twice selects ALL connected objects;
50 all objects move as a unified whole.
54 /*virtual*/ void Dimension::Draw(Painter * painter)
56 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
58 if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
59 painter->DrawHandle(position);
61 if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
62 painter->DrawHandle(endpoint);
64 if (state == OSSelected)
65 painter->SetPen(QPen(Qt::cyan, 1.0 * Painter::zoom * size, Qt::SolidLine));
67 painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine));
69 painter->SetBrush(QBrush(QColor(Qt::blue)));
71 // Draw an aligned dimension line
72 Vector v(position, endpoint);
73 double angle = v.Angle();
74 Vector orthogonal = Vector::Normal(position, endpoint);
75 Vector unit = v.Unit();
77 // Horizontally aligned display
79 Vector pos = position, endp = endpoint, ortho;
82 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
84 y1 = (pos.y > endp.y ? pos.y : endp.y);
85 y2 = (pos.y > endp.y ? endp.y : pos.y);
86 ortho = Vector(0, 1.0);
91 y1 = (pos.y > endp.y ? endp.y : pos.y);
92 y2 = (pos.y > endp.y ? pos.y : endp.y);
93 ortho = Vector(0, -1.0);
98 unit = Vector(pos, endp).Unit();
99 Point p1 = pos + (ortho * 10.0 * size);
100 Point p2 = endp + (ortho * 10.0 * size);
101 Point p3 = pos + (ortho * 16.0 * size);
102 Point p4 = endp + (ortho * 16.0 * size);
103 Point p5 = position + (ortho * 4.0 * size);
104 Point p6 = endpoint + (ortho * 4.0 * size);
107 The numbers hardcoded into here, what are they?
108 I believe they are pixels.
111 // Get our line parallel to our points
112 Point p1 = position + (orthogonal * 10.0 * size);
113 Point p2 = endpoint + (orthogonal * 10.0 * size);
115 Point p3 = position + (orthogonal * 16.0 * size);
116 Point p4 = endpoint + (orthogonal * 16.0 * size);
117 Point p5 = position + (orthogonal * 4.0 * size);
118 Point p6 = endpoint + (orthogonal * 4.0 * size);
120 // Draw extension lines (if certain type)
121 painter->DrawLine(p3, p5);
122 painter->DrawLine(p4, p6);
124 // Calculate whether or not the arrowheads are too crowded to put inside
125 // the extension lines. 9.0 is the length of the arrowhead.
126 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size));
127 double t = Geometry::ParameterOfLineAndPoint(pos, endp, endp - (unit * 9.0 * size));
128 //printf("Dimension::Draw(): t = %lf\n", t);
130 // On the screen, it's acting like this is actually 58%...
131 // This is correct, we want it to happen at > 50%
134 // Draw main dimension line + arrowheads
135 painter->DrawLine(p1, p2);
136 painter->DrawArrowhead(p1, p2, size);
137 painter->DrawArrowhead(p2, p1, size);
141 // Draw outside arrowheads
142 Point p7 = p1 - (unit * 9.0 * size);
143 Point p8 = p2 + (unit * 9.0 * size);
144 painter->DrawArrowhead(p1, p7, size);
145 painter->DrawArrowhead(p2, p8, size);
146 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
147 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
150 // Draw length of dimension line...
151 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
153 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
156 Point ctr = p2 + (Vector(p2, p1) / 2.0);
160 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
163 // double length = v.Magnitude();
164 double length = fabs(position.x - endpoint.x);
167 dimText = QString("%1\"").arg(length);
170 double feet = (double)((int)length / 12);
171 double inches = length - (feet * 12.0);
174 dimText = QString("%1'").arg(feet);
176 dimText = QString("%1' %2\"").arg(feet).arg(inches);
180 painter->DrawAngledText(ctr, angle, dimText, size);
182 // need to make another handle drawing function in Painter, instead of this
185 Point p9 = ((position + endpoint) / 2.0) + (orthogonal * 14.0)
188 Point p10 = p9 + (orthogonal * -7.0);
189 Point p11 = p10 + (unit * 7.0);
190 Point p12 = p11 + (orthogonal * 7.0);
191 Point p13 = p12 + (unit * -7.0);
192 painter->DrawLine(p10, p11);
193 painter->DrawLine(p11, p12);
194 painter->DrawLine(p12, p13);
195 painter->DrawLine(p13, p10);
200 /*virtual*/ Vector Dimension::Center(void)
202 // Technically, this is the midpoint but who are we to quibble? :-)
203 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
208 /*virtual*/ bool Dimension::Collided(Vector point)
210 // Someone told us to fuck off, so we'll fuck off. :-)
214 // We can assume this, since this is a mouse down event here.
215 objectWasDragged = false;
218 // Now that we've done our hit testing on the non-snapped point, snap it if
221 point = SnapPointToGrid(point);
228 draggingHandle1 = true;
236 draggingHandle2 = true;
245 /*virtual*/ void Dimension::PointerMoved(Vector point)
247 if (selectionInProgress)
249 // Check for whether or not the rect contains this line
250 if (selection.contains(position.x, position.y)
251 && selection.contains(endpoint.x, endpoint.y))
259 // Hit test tells us what we hit (if anything) through boolean variables. (It
260 // also tells us whether or not the state changed. --not any more)
263 needUpdate = HitStateChanged();
265 objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
267 if (objectWasDragged)
269 Vector delta = point - oldPoint;
271 if (draggingHandle1)// || draggingLine)
274 if (draggingHandle2)// || draggingLine)
283 /*virtual*/ void Dimension::PointerReleased(void)
285 /* if (draggingHandle1 || draggingHandle2)
287 // Set the length (in case the global state was set to fixed (or not))
288 if (Object::fixedLength)
291 if (draggingHandle1) // startpoint
293 Vector v = Vector(endpoint, position).Unit() * length;
294 position = endpoint + v;
298 Vector v = Vector(position, endpoint).Unit() * length;
299 endpoint = position + v;
304 // Otherwise, we calculate the new length, just in case on the next move
305 // it turns out to have a fixed length. :-)
306 length = Vector(endpoint - position).Magnitude();
311 draggingHandle1 = false;
312 draggingHandle2 = false;
314 // Here we check for just a click: If object was clicked and dragged, then
315 // revert to the old state (OSInactive). Otherwise, keep the new state that
317 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
318 about keeping track of old states...
320 if (objectWasDragged)
325 /*virtual*/ bool Dimension::HitTest(Point point)
327 Vector orthogonal = Vector::Normal(position, endpoint);
328 // Get our line parallel to our points
329 Point p1 = position + (orthogonal * 10.0 * size);
330 Point p2 = endpoint + (orthogonal * 10.0 * size);
333 hitPoint1 = hitPoint2 = hitLine = hitFlipSwitch = false;
334 Vector v1(position, point);
335 Vector v2(endpoint, point);
336 // Vector lineSegment(position, endpoint);
337 Vector lineSegment(p1, p2);
338 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
339 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
343 distance = v1.Magnitude();
345 distance = v2.Magnitude();
347 // distance = ?Det?(ls, v1) / |ls|
348 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
349 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
350 / lineSegment.Magnitude());
352 if ((v1.Magnitude() * Painter::zoom) < 8.0)
354 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
356 else if ((distance * Painter::zoom) < 5.0)
359 return (hitPoint1 || hitPoint2 || hitLine ? true : false);
363 void Dimension::SaveHitState(void)
365 oldHitPoint1 = hitPoint1;
366 oldHitPoint2 = hitPoint2;
367 oldHitLine = hitLine;
371 bool Dimension::HitStateChanged(void)
373 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
380 /*virtual*/ void Dimension::Enumerate(FILE * file)
382 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
386 /*virtual*/ Object * Dimension::Copy(void)
388 #warning "!!! This doesn't take care of attached Dimensions !!!"
390 This is a real problem. While having a pointer in the Dimension to this line's points
391 is fast & easy, it creates a huge problem when trying to replicate an object like this.
393 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
394 way, if you copy them, ... you might still have problems. Because you can't be sure if
395 a copy will be persistant or not, you then *definitely* do not want them to have the
396 same reference number.
399 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
405 // Dimensions are special: they contain exactly *two* points. Here, we check
406 // only for zero/non-zero in returning the correct points.
407 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
416 /*virtual*/ void Dimension::MovePointAtParameter(double parameter, Vector v)
420 else if (parameter == 1.0)
423 {} // Not sure how to handle this case :-P
427 /*virtual*/ void Dimension::Connect(Object * obj, double param)
429 // There are four possibilities here...
430 // The param is only looking for 0 or 1 here.
431 if (point1.object == NULL && point2.object == NULL)
436 else if (point1.object == NULL && point2.object != NULL)
438 if (point2.t == param)
446 else if (point1.object != NULL && point2.object == NULL)
448 if (point1.t == param)
456 else if (point1.object != NULL && point2.object != NULL)
458 if (point1.t == param)
466 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
468 if (point1.object == obj && point1.t == param)
469 point1.object = NULL;
470 else if (point2.object == obj && point2.t == param)
471 point2.object = NULL;
475 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
477 if (point1.object == obj)
478 point1.object = NULL;
480 if (point2.object == obj)
481 point2.object = NULL;
485 /*virtual*/ QRectF Dimension::Extents(void)
490 // if (point1.object)
491 // p1 = point1.object->GetPointAtParameter(point1.t);
493 // if (point2.object)
494 // p2 = point2.object->GetPointAtParameter(point2.t);
496 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
501 /*virtual*/ ObjectType Dimension::Type(void)
508 void Dimension::FlipSides(void)
511 Vector tmp = position;
514 //Not sure this matters...
515 //#warning "!!! May need to swap parameter values on connected objects !!!"
517 Connection tmp = point1;
520 // double tmp = point1.t;
521 // point1.t = point2.t;
523 // Object * tmp = point1.object;
524 // point1.object = point2.object;
525 // point2.object = tmp;