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.
30 // dimensionType = DTLinearHorz;
34 Dimension::~Dimension()
40 How to move: click once moves only the object/point clicked on, all connected
41 objects deform themselves accordingly. click twice selects ALL connected objects;
42 all objects move as a unified whole.
46 /*virtual*/ void Dimension::Draw(Painter * painter)
48 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
50 if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
51 painter->DrawHandle(position);
53 if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
54 painter->DrawHandle(endpoint);
56 if (state == OSSelected)
57 painter->SetPen(QPen(Qt::cyan, 1.0 * Painter::zoom * size, Qt::SolidLine));
59 painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine));
61 painter->SetBrush(QBrush(QColor(Qt::blue)));
63 // Draw an aligned dimension line
64 Vector v(position, endpoint);
65 double angle = v.Angle();
66 // Vector orthogonal = Vector::Normal(position, endpoint);
67 Vector unit = v.Unit();
68 linePt1 = position, linePt2 = endpoint;
70 // Horizontally aligned display
73 double x1, y1, length;
75 if (dimensionType == DTLinearVert)
77 if ((angle < 0) || (angle > PI))
78 // if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
80 x1 = (position.x > endpoint.x ? position.x : endpoint.x);
81 y1 = (position.y > endpoint.y ? position.y : endpoint.y);
82 ortho = Vector(1.0, 0);
83 // ortho = Vector(0, 1.0);
89 x1 = (position.x > endpoint.x ? endpoint.x : position.x);
90 y1 = (position.y > endpoint.y ? endpoint.y : position.y);
91 ortho = Vector(-1.0, 0);
92 // ortho = Vector(0, -1.0);
97 linePt1.x = linePt2.x = x1;
98 length = fabs(position.y - endpoint.y);
100 else if (dimensionType == DTLinearHorz)
102 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
104 x1 = (position.x > endpoint.x ? position.x : endpoint.x);
105 y1 = (position.y > endpoint.y ? position.y : endpoint.y);
106 ortho = Vector(0, 1.0);
111 x1 = (position.x > endpoint.x ? endpoint.x : position.x);
112 y1 = (position.y > endpoint.y ? endpoint.y : position.y);
113 ortho = Vector(0, -1.0);
117 linePt1.y = linePt2.y = y1;
118 length = fabs(position.x - endpoint.x);
120 else if (dimensionType == DTLinear)
122 angle = Vector(linePt1, linePt2).Angle();
123 ortho = Vector::Normal(linePt1, linePt2);
124 length = v.Magnitude();
127 unit = Vector(linePt1, linePt2).Unit();
128 // angle = Vector(linePt1, linePt2).Angle();
129 // ortho = Vector::Normal(linePt1, linePt2);
131 Point p1 = linePt1 + (ortho * 10.0 * size);
132 Point p2 = linePt2 + (ortho * 10.0 * size);
133 Point p3 = linePt1 + (ortho * 16.0 * size);
134 Point p4 = linePt2 + (ortho * 16.0 * size);
135 Point p5 = position + (ortho * 4.0 * size);
136 Point p6 = endpoint + (ortho * 4.0 * size);
139 The numbers hardcoded into here, what are they?
140 I believe they are pixels.
143 // Get our line parallel to our points
144 Point p1 = position + (orthogonal * 10.0 * size);
145 Point p2 = endpoint + (orthogonal * 10.0 * size);
147 Point p3 = position + (orthogonal * 16.0 * size);
148 Point p4 = endpoint + (orthogonal * 16.0 * size);
149 Point p5 = position + (orthogonal * 4.0 * size);
150 Point p6 = endpoint + (orthogonal * 4.0 * size);
152 // Draw extension lines (if certain type)
153 painter->DrawLine(p3, p5);
154 painter->DrawLine(p4, p6);
156 // Calculate whether or not the arrowheads are too crowded to put inside
157 // the extension lines. 9.0 is the length of the arrowhead.
158 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size));
159 // double t = Geometry::ParameterOfLineAndPoint(pos, endp, endp - (unit * 9.0 * size));
160 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * size));
161 //printf("Dimension::Draw(): t = %lf\n", t);
163 // On the screen, it's acting like this is actually 58%...
164 // This is correct, we want it to happen at > 50%
167 // Draw main dimension line + arrowheads
168 painter->DrawLine(p1, p2);
169 painter->DrawArrowhead(p1, p2, size);
170 painter->DrawArrowhead(p2, p1, size);
174 // Draw outside arrowheads
175 Point p7 = p1 - (unit * 9.0 * size);
176 Point p8 = p2 + (unit * 9.0 * size);
177 painter->DrawArrowhead(p1, p7, size);
178 painter->DrawArrowhead(p2, p8, size);
179 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
180 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
183 // Draw length of dimension line...
184 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
185 Point ctr = p2 + (Vector(p2, p1) / 2.0);
188 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
193 dimText = QString("%1\"").arg(length);
196 double feet = (double)((int)length / 12);
197 double inches = length - (feet * 12.0);
200 dimText = QString("%1'").arg(feet);
202 dimText = QString("%1' %2\"").arg(feet).arg(inches);
206 painter->DrawAngledText(ctr, angle, dimText, size);
210 Point hp1 = (p1 + p2) / 2.0;
214 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
215 painter->SetBrush(QBrush(QColor(Qt::magenta)));
216 painter->DrawArrowHandle(hp1, ortho.Angle() + PI);
217 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
220 painter->DrawHandle(hp1);
225 /*virtual*/ Vector Dimension::Center(void)
227 // Technically, this is the midpoint but who are we to quibble? :-)
228 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
233 /*virtual*/ bool Dimension::Collided(Vector point)
235 // Someone told us to fuck off, so we'll fuck off. :-)
239 // We can assume this, since this is a mouse down event here.
240 objectWasDragged = false;
243 // Now that we've done our hit testing on the non-snapped point, snap it if
246 point = SnapPointToGrid(point);
253 draggingHandle1 = true;
261 draggingHandle2 = true;
264 else if (hitFlipSwitch)
267 hitFlipSwitch = hitLine = false;
268 // state = OSInactive;
277 /*virtual*/ bool Dimension::PointerMoved(Vector point)
279 if (selectionInProgress)
281 // Check for whether or not the rect contains this line
282 if (selection.contains(position.x, position.y)
283 && selection.contains(endpoint.x, endpoint.y))
291 // Hit test tells us what we hit (if anything) through boolean variables. (It
292 // also tells us whether or not the state changed. --not any more)
294 bool hovered = HitTest(point);
295 needUpdate = HitStateChanged();
297 objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
299 if (objectWasDragged)
301 Vector delta = point - oldPoint;
303 if (draggingHandle1)// || draggingLine)
306 if (draggingHandle2)// || draggingLine)
317 /*virtual*/ void Dimension::PointerReleased(void)
319 /* if (draggingHandle1 || draggingHandle2)
321 // Set the length (in case the global state was set to fixed (or not))
322 if (Object::fixedLength)
325 if (draggingHandle1) // startpoint
327 Vector v = Vector(endpoint, position).Unit() * length;
328 position = endpoint + v;
332 Vector v = Vector(position, endpoint).Unit() * length;
333 endpoint = position + v;
338 // Otherwise, we calculate the new length, just in case on the next move
339 // it turns out to have a fixed length. :-)
340 length = Vector(endpoint - position).Magnitude();
345 draggingHandle1 = false;
346 draggingHandle2 = false;
348 // Here we check for just a click: If object was clicked and dragged, then
349 // revert to the old state (OSInactive). Otherwise, keep the new state that
351 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
352 about keeping track of old states...
354 if (objectWasDragged)
359 /*virtual*/ bool Dimension::HitTest(Point point)
361 // Vector orthogonal = Vector::Normal(position, endpoint);
362 Vector orthogonal = Vector::Normal(linePt1, linePt2);
363 // Get our line parallel to our points
365 Point p1 = position + (orthogonal * 10.0 * size);
366 Point p2 = endpoint + (orthogonal * 10.0 * size);
368 Point p1 = linePt1 + (orthogonal * 10.0 * size);
369 Point p2 = linePt2 + (orthogonal * 10.0 * size);
373 hitPoint1 = hitPoint2 = hitLine = hitFlipSwitch = false;
374 Vector v1(position, point);
375 Vector v2(endpoint, point);
376 // Vector lineSegment(position, endpoint);
377 Vector lineSegment(p1, p2);
378 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
379 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
381 Point hFSPoint = Point((p1 + p2) / 2.0, point);
384 distance = v1.Magnitude();
386 distance = v2.Magnitude();
388 // distance = ?Det?(ls, v1) / |ls|
389 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
390 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
391 / lineSegment.Magnitude());
393 if ((v1.Magnitude() * Painter::zoom) < 8.0)
395 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
397 else if ((distance * Painter::zoom) < 5.0)
400 if ((hFSPoint.Magnitude() * Painter::zoom) < 8.0)
401 hitFlipSwitch = true;
403 return (hitPoint1 || hitPoint2 || hitLine || hitFlipSwitch ? true : false);
407 void Dimension::SaveHitState(void)
409 oldHitPoint1 = hitPoint1;
410 oldHitPoint2 = hitPoint2;
411 oldHitLine = hitLine;
412 oldHitFlipSwitch = hitFlipSwitch;
416 bool Dimension::HitStateChanged(void)
418 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine) || (hitFlipSwitch != oldHitFlipSwitch))
425 /*virtual*/ void Dimension::Enumerate(FILE * file)
427 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
431 /*virtual*/ Object * Dimension::Copy(void)
433 #warning "!!! This doesn't take care of attached Dimensions !!!"
435 This is a real problem. While having a pointer in the Dimension to this line's points
436 is fast & easy, it creates a huge problem when trying to replicate an object like this.
438 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
439 way, if you copy them, ... you might still have problems. Because you can't be sure if
440 a copy will be persistant or not, you then *definitely* do not want them to have the
441 same reference number.
444 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
450 // Dimensions are special: they contain exactly *two* points. Here, we check
451 // only for zero/non-zero in returning the correct points.
452 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
461 /*virtual*/ void Dimension::MovePointAtParameter(double parameter, Vector v)
465 else if (parameter == 1.0)
468 {} // Not sure how to handle this case :-P
472 /*virtual*/ void Dimension::Connect(Object * obj, double param)
474 // There are four possibilities here...
475 // The param is only looking for 0 or 1 here.
476 if (point1.object == NULL && point2.object == NULL)
481 else if (point1.object == NULL && point2.object != NULL)
483 if (point2.t == param)
491 else if (point1.object != NULL && point2.object == NULL)
493 if (point1.t == param)
501 else if (point1.object != NULL && point2.object != NULL)
503 if (point1.t == param)
511 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
513 if (point1.object == obj && point1.t == param)
514 point1.object = NULL;
515 else if (point2.object == obj && point2.t == param)
516 point2.object = NULL;
520 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
522 if (point1.object == obj)
523 point1.object = NULL;
525 if (point2.object == obj)
526 point2.object = NULL;
530 /*virtual*/ QRectF Dimension::Extents(void)
535 // if (point1.object)
536 // p1 = point1.object->GetPointAtParameter(point1.t);
538 // if (point2.object)
539 // p2 = point2.object->GetPointAtParameter(point2.t);
541 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
546 /*virtual*/ ObjectType Dimension::Type(void)
553 void Dimension::FlipSides(void)
556 Vector tmp = position;
559 //Not sure this matters...
560 //#warning "!!! May need to swap parameter values on connected objects !!!"
562 Connection tmp = point1;
565 // double tmp = point1.t;
566 // point1.t = point2.t;
568 // Object * tmp = point1.object;
569 // point1.object = point2.object;
570 // point2.object = tmp;