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 Dimension::~Dimension()
39 How to move: click once moves only the object/point clicked on, all connected
40 objects deform themselves accordingly. click twice selects ALL connected objects;
41 all objects move as a unified whole.
45 /*virtual*/ void Dimension::Draw(Painter * painter)
47 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
49 if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
50 painter->DrawHandle(position);
52 if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
53 painter->DrawHandle(endpoint);
55 if (state == OSSelected)
56 painter->SetPen(QPen(Qt::cyan, 1.0 * Painter::zoom * size, Qt::SolidLine));
58 painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine));
60 painter->SetBrush(QBrush(QColor(Qt::blue)));
62 // Draw an aligned dimension line
63 Vector v(position, endpoint);
64 double angle = v.Angle();
65 Vector orthogonal = Vector::Normal(position, endpoint);
66 Vector unit = v.Unit();
67 linePt1 = position, linePt2 = endpoint;
69 // Horizontally aligned display
71 Vector /*pos = position, endp = endpoint,*/ ortho;
74 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
76 y1 = (position.y > endpoint.y ? position.y : endpoint.y);
77 ortho = Vector(0, 1.0);
82 y1 = (position.y > endpoint.y ? endpoint.y : position.y);
83 ortho = Vector(0, -1.0);
87 // pos.y = endp.y = y1;
88 linePt1.y = linePt2.y = y1;
89 unit = Vector(linePt1, linePt2).Unit();
90 Point p1 = linePt1 + (ortho * 10.0 * size);
91 Point p2 = linePt2 + (ortho * 10.0 * size);
92 Point p3 = linePt1 + (ortho * 16.0 * size);
93 Point p4 = linePt2 + (ortho * 16.0 * size);
94 Point p5 = position + (ortho * 4.0 * size);
95 Point p6 = endpoint + (ortho * 4.0 * size);
98 The numbers hardcoded into here, what are they?
99 I believe they are pixels.
102 // Get our line parallel to our points
103 Point p1 = position + (orthogonal * 10.0 * size);
104 Point p2 = endpoint + (orthogonal * 10.0 * size);
106 Point p3 = position + (orthogonal * 16.0 * size);
107 Point p4 = endpoint + (orthogonal * 16.0 * size);
108 Point p5 = position + (orthogonal * 4.0 * size);
109 Point p6 = endpoint + (orthogonal * 4.0 * size);
111 // Draw extension lines (if certain type)
112 painter->DrawLine(p3, p5);
113 painter->DrawLine(p4, p6);
115 // Calculate whether or not the arrowheads are too crowded to put inside
116 // the extension lines. 9.0 is the length of the arrowhead.
117 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size));
118 // double t = Geometry::ParameterOfLineAndPoint(pos, endp, endp - (unit * 9.0 * size));
119 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * size));
120 //printf("Dimension::Draw(): t = %lf\n", t);
122 // On the screen, it's acting like this is actually 58%...
123 // This is correct, we want it to happen at > 50%
126 // Draw main dimension line + arrowheads
127 painter->DrawLine(p1, p2);
128 painter->DrawArrowhead(p1, p2, size);
129 painter->DrawArrowhead(p2, p1, size);
133 // Draw outside arrowheads
134 Point p7 = p1 - (unit * 9.0 * size);
135 Point p8 = p2 + (unit * 9.0 * size);
136 painter->DrawArrowhead(p1, p7, size);
137 painter->DrawArrowhead(p2, p8, size);
138 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
139 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
142 // Draw length of dimension line...
143 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
145 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
148 Point ctr = p2 + (Vector(p2, p1) / 2.0);
152 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
155 // double length = v.Magnitude();
156 double length = fabs(position.x - endpoint.x);
159 dimText = QString("%1\"").arg(length);
162 double feet = (double)((int)length / 12);
163 double inches = length - (feet * 12.0);
166 dimText = QString("%1'").arg(feet);
168 dimText = QString("%1' %2\"").arg(feet).arg(inches);
172 painter->DrawAngledText(ctr, angle, dimText, size);
174 // need to make another handle drawing function in Painter, instead of this
178 Point p9 = ((position + endpoint) / 2.0) + (orthogonal * 14.0)
181 Point p10 = p9 + (orthogonal * -7.0);
182 Point p11 = p10 + (unit * 7.0);
183 Point p12 = p11 + (orthogonal * 7.0);
184 Point p13 = p12 + (unit * -7.0);
185 painter->DrawLine(p10, p11);
186 painter->DrawLine(p11, p12);
187 painter->DrawLine(p12, p13);
188 painter->DrawLine(p13, p10);
191 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
193 Point hp1 = (p1 + p2) / 2.0;
194 painter->DrawHandle(hp1);
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;
239 else if (hitFlipSwitch)
242 hitFlipSwitch = hitLine = false;
243 // state = OSInactive;
252 /*virtual*/ bool Dimension::PointerMoved(Vector point)
254 if (selectionInProgress)
256 // Check for whether or not the rect contains this line
257 if (selection.contains(position.x, position.y)
258 && selection.contains(endpoint.x, endpoint.y))
266 // Hit test tells us what we hit (if anything) through boolean variables. (It
267 // also tells us whether or not the state changed. --not any more)
269 bool hovered = HitTest(point);
270 needUpdate = HitStateChanged();
272 objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
274 if (objectWasDragged)
276 Vector delta = point - oldPoint;
278 if (draggingHandle1)// || draggingLine)
281 if (draggingHandle2)// || draggingLine)
292 /*virtual*/ void Dimension::PointerReleased(void)
294 /* if (draggingHandle1 || draggingHandle2)
296 // Set the length (in case the global state was set to fixed (or not))
297 if (Object::fixedLength)
300 if (draggingHandle1) // startpoint
302 Vector v = Vector(endpoint, position).Unit() * length;
303 position = endpoint + v;
307 Vector v = Vector(position, endpoint).Unit() * length;
308 endpoint = position + v;
313 // Otherwise, we calculate the new length, just in case on the next move
314 // it turns out to have a fixed length. :-)
315 length = Vector(endpoint - position).Magnitude();
320 draggingHandle1 = false;
321 draggingHandle2 = false;
323 // Here we check for just a click: If object was clicked and dragged, then
324 // revert to the old state (OSInactive). Otherwise, keep the new state that
326 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
327 about keeping track of old states...
329 if (objectWasDragged)
334 /*virtual*/ bool Dimension::HitTest(Point point)
336 // Vector orthogonal = Vector::Normal(position, endpoint);
337 Vector orthogonal = Vector::Normal(linePt1, linePt2);
338 // Get our line parallel to our points
340 Point p1 = position + (orthogonal * 10.0 * size);
341 Point p2 = endpoint + (orthogonal * 10.0 * size);
343 Point p1 = linePt1 + (orthogonal * 10.0 * size);
344 Point p2 = linePt2 + (orthogonal * 10.0 * size);
348 hitPoint1 = hitPoint2 = hitLine = hitFlipSwitch = false;
349 Vector v1(position, point);
350 Vector v2(endpoint, point);
351 // Vector lineSegment(position, endpoint);
352 Vector lineSegment(p1, p2);
353 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
354 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
356 Point hFSPoint = Point((p1 + p2) / 2.0, point);
359 distance = v1.Magnitude();
361 distance = v2.Magnitude();
363 // distance = ?Det?(ls, v1) / |ls|
364 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
365 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
366 / lineSegment.Magnitude());
368 if ((v1.Magnitude() * Painter::zoom) < 8.0)
370 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
372 else if ((distance * Painter::zoom) < 5.0)
375 if ((hFSPoint.Magnitude() * Painter::zoom) < 8.0)
376 hitFlipSwitch = true;
378 return (hitPoint1 || hitPoint2 || hitLine || hitFlipSwitch ? true : false);
382 void Dimension::SaveHitState(void)
384 oldHitPoint1 = hitPoint1;
385 oldHitPoint2 = hitPoint2;
386 oldHitLine = hitLine;
387 oldHitFlipSwitch = hitFlipSwitch;
391 bool Dimension::HitStateChanged(void)
393 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine) || (hitFlipSwitch != oldHitFlipSwitch))
400 /*virtual*/ void Dimension::Enumerate(FILE * file)
402 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
406 /*virtual*/ Object * Dimension::Copy(void)
408 #warning "!!! This doesn't take care of attached Dimensions !!!"
410 This is a real problem. While having a pointer in the Dimension to this line's points
411 is fast & easy, it creates a huge problem when trying to replicate an object like this.
413 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
414 way, if you copy them, ... you might still have problems. Because you can't be sure if
415 a copy will be persistant or not, you then *definitely* do not want them to have the
416 same reference number.
419 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
425 // Dimensions are special: they contain exactly *two* points. Here, we check
426 // only for zero/non-zero in returning the correct points.
427 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
436 /*virtual*/ void Dimension::MovePointAtParameter(double parameter, Vector v)
440 else if (parameter == 1.0)
443 {} // Not sure how to handle this case :-P
447 /*virtual*/ void Dimension::Connect(Object * obj, double param)
449 // There are four possibilities here...
450 // The param is only looking for 0 or 1 here.
451 if (point1.object == NULL && point2.object == NULL)
456 else if (point1.object == NULL && point2.object != NULL)
458 if (point2.t == param)
466 else if (point1.object != NULL && point2.object == NULL)
468 if (point1.t == param)
476 else if (point1.object != NULL && point2.object != NULL)
478 if (point1.t == param)
486 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
488 if (point1.object == obj && point1.t == param)
489 point1.object = NULL;
490 else if (point2.object == obj && point2.t == param)
491 point2.object = NULL;
495 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
497 if (point1.object == obj)
498 point1.object = NULL;
500 if (point2.object == obj)
501 point2.object = NULL;
505 /*virtual*/ QRectF Dimension::Extents(void)
510 // if (point1.object)
511 // p1 = point1.object->GetPointAtParameter(point1.t);
513 // if (point2.object)
514 // p2 = point2.object->GetPointAtParameter(point2.t);
516 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
521 /*virtual*/ ObjectType Dimension::Type(void)
528 void Dimension::FlipSides(void)
531 Vector tmp = position;
534 //Not sure this matters...
535 //#warning "!!! May need to swap parameter values on connected objects !!!"
537 Connection tmp = point1;
540 // double tmp = point1.t;
541 // point1.t = point2.t;
543 // Object * tmp = point1.object;
544 // point1.object = point2.object;
545 // point2.object = tmp;