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();
76 linePt1 = position, linePt2 = endpoint;
78 // Horizontally aligned display
80 Vector /*pos = position, endp = endpoint,*/ ortho;
83 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
85 y1 = (position.y > endpoint.y ? position.y : endpoint.y);
86 ortho = Vector(0, 1.0);
91 y1 = (position.y > endpoint.y ? endpoint.y : position.y);
92 ortho = Vector(0, -1.0);
96 // pos.y = endp.y = y1;
97 linePt1.y = linePt2.y = y1;
98 unit = Vector(linePt1, linePt2).Unit();
99 Point p1 = linePt1 + (ortho * 10.0 * size);
100 Point p2 = linePt2 + (ortho * 10.0 * size);
101 Point p3 = linePt1 + (ortho * 16.0 * size);
102 Point p4 = linePt2 + (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 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * size));
129 //printf("Dimension::Draw(): t = %lf\n", t);
131 // On the screen, it's acting like this is actually 58%...
132 // This is correct, we want it to happen at > 50%
135 // Draw main dimension line + arrowheads
136 painter->DrawLine(p1, p2);
137 painter->DrawArrowhead(p1, p2, size);
138 painter->DrawArrowhead(p2, p1, size);
142 // Draw outside arrowheads
143 Point p7 = p1 - (unit * 9.0 * size);
144 Point p8 = p2 + (unit * 9.0 * size);
145 painter->DrawArrowhead(p1, p7, size);
146 painter->DrawArrowhead(p2, p8, size);
147 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
148 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
151 // Draw length of dimension line...
152 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
154 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
157 Point ctr = p2 + (Vector(p2, p1) / 2.0);
161 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
164 // double length = v.Magnitude();
165 double length = fabs(position.x - endpoint.x);
168 dimText = QString("%1\"").arg(length);
171 double feet = (double)((int)length / 12);
172 double inches = length - (feet * 12.0);
175 dimText = QString("%1'").arg(feet);
177 dimText = QString("%1' %2\"").arg(feet).arg(inches);
181 painter->DrawAngledText(ctr, angle, dimText, size);
183 // need to make another handle drawing function in Painter, instead of this
186 Point p9 = ((position + endpoint) / 2.0) + (orthogonal * 14.0)
189 Point p10 = p9 + (orthogonal * -7.0);
190 Point p11 = p10 + (unit * 7.0);
191 Point p12 = p11 + (orthogonal * 7.0);
192 Point p13 = p12 + (unit * -7.0);
193 painter->DrawLine(p10, p11);
194 painter->DrawLine(p11, p12);
195 painter->DrawLine(p12, p13);
196 painter->DrawLine(p13, p10);
201 /*virtual*/ Vector Dimension::Center(void)
203 // Technically, this is the midpoint but who are we to quibble? :-)
204 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
209 /*virtual*/ bool Dimension::Collided(Vector point)
211 // Someone told us to fuck off, so we'll fuck off. :-)
215 // We can assume this, since this is a mouse down event here.
216 objectWasDragged = false;
219 // Now that we've done our hit testing on the non-snapped point, snap it if
222 point = SnapPointToGrid(point);
229 draggingHandle1 = true;
237 draggingHandle2 = true;
246 /*virtual*/ bool Dimension::PointerMoved(Vector point)
248 if (selectionInProgress)
250 // Check for whether or not the rect contains this line
251 if (selection.contains(position.x, position.y)
252 && selection.contains(endpoint.x, endpoint.y))
260 // Hit test tells us what we hit (if anything) through boolean variables. (It
261 // also tells us whether or not the state changed. --not any more)
263 bool hovered = HitTest(point);
264 needUpdate = HitStateChanged();
266 objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
268 if (objectWasDragged)
270 Vector delta = point - oldPoint;
272 if (draggingHandle1)// || draggingLine)
275 if (draggingHandle2)// || draggingLine)
286 /*virtual*/ void Dimension::PointerReleased(void)
288 /* if (draggingHandle1 || draggingHandle2)
290 // Set the length (in case the global state was set to fixed (or not))
291 if (Object::fixedLength)
294 if (draggingHandle1) // startpoint
296 Vector v = Vector(endpoint, position).Unit() * length;
297 position = endpoint + v;
301 Vector v = Vector(position, endpoint).Unit() * length;
302 endpoint = position + v;
307 // Otherwise, we calculate the new length, just in case on the next move
308 // it turns out to have a fixed length. :-)
309 length = Vector(endpoint - position).Magnitude();
314 draggingHandle1 = false;
315 draggingHandle2 = false;
317 // Here we check for just a click: If object was clicked and dragged, then
318 // revert to the old state (OSInactive). Otherwise, keep the new state that
320 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
321 about keeping track of old states...
323 if (objectWasDragged)
328 /*virtual*/ bool Dimension::HitTest(Point point)
330 Vector orthogonal = Vector::Normal(position, endpoint);
331 // Get our line parallel to our points
333 Point p1 = position + (orthogonal * 10.0 * size);
334 Point p2 = endpoint + (orthogonal * 10.0 * size);
336 Point p1 = linePt1 + (orthogonal * 10.0 * size);
337 Point p2 = linePt2 + (orthogonal * 10.0 * size);
341 hitPoint1 = hitPoint2 = hitLine = hitFlipSwitch = false;
342 Vector v1(position, point);
343 Vector v2(endpoint, point);
344 // Vector lineSegment(position, endpoint);
345 Vector lineSegment(p1, p2);
346 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
347 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
351 distance = v1.Magnitude();
353 distance = v2.Magnitude();
355 // distance = ?Det?(ls, v1) / |ls|
356 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
357 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
358 / lineSegment.Magnitude());
360 if ((v1.Magnitude() * Painter::zoom) < 8.0)
362 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
364 else if ((distance * Painter::zoom) < 5.0)
367 return (hitPoint1 || hitPoint2 || hitLine ? true : false);
371 void Dimension::SaveHitState(void)
373 oldHitPoint1 = hitPoint1;
374 oldHitPoint2 = hitPoint2;
375 oldHitLine = hitLine;
379 bool Dimension::HitStateChanged(void)
381 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
388 /*virtual*/ void Dimension::Enumerate(FILE * file)
390 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
394 /*virtual*/ Object * Dimension::Copy(void)
396 #warning "!!! This doesn't take care of attached Dimensions !!!"
398 This is a real problem. While having a pointer in the Dimension to this line's points
399 is fast & easy, it creates a huge problem when trying to replicate an object like this.
401 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
402 way, if you copy them, ... you might still have problems. Because you can't be sure if
403 a copy will be persistant or not, you then *definitely* do not want them to have the
404 same reference number.
407 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
413 // Dimensions are special: they contain exactly *two* points. Here, we check
414 // only for zero/non-zero in returning the correct points.
415 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
424 /*virtual*/ void Dimension::MovePointAtParameter(double parameter, Vector v)
428 else if (parameter == 1.0)
431 {} // Not sure how to handle this case :-P
435 /*virtual*/ void Dimension::Connect(Object * obj, double param)
437 // There are four possibilities here...
438 // The param is only looking for 0 or 1 here.
439 if (point1.object == NULL && point2.object == NULL)
444 else if (point1.object == NULL && point2.object != NULL)
446 if (point2.t == param)
454 else if (point1.object != NULL && point2.object == NULL)
456 if (point1.t == param)
464 else if (point1.object != NULL && point2.object != NULL)
466 if (point1.t == param)
474 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
476 if (point1.object == obj && point1.t == param)
477 point1.object = NULL;
478 else if (point2.object == obj && point2.t == param)
479 point2.object = NULL;
483 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
485 if (point1.object == obj)
486 point1.object = NULL;
488 if (point2.object == obj)
489 point2.object = NULL;
493 /*virtual*/ QRectF Dimension::Extents(void)
498 // if (point1.object)
499 // p1 = point1.object->GetPointAtParameter(point1.t);
501 // if (point2.object)
502 // p2 = point2.object->GetPointAtParameter(point2.t);
504 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
509 /*virtual*/ ObjectType Dimension::Type(void)
516 void Dimension::FlipSides(void)
519 Vector tmp = position;
522 //Not sure this matters...
523 //#warning "!!! May need to swap parameter values on connected objects !!!"
525 Connection tmp = point1;
528 // double tmp = point1.t;
529 // point1.t = point2.t;
531 // Object * tmp = point1.object;
532 // point1.object = point2.object;
533 // point2.object = tmp;