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*/ void 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)
264 needUpdate = HitStateChanged();
266 objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
268 if (objectWasDragged)
270 Vector delta = point - oldPoint;
272 if (draggingHandle1)// || draggingLine)
275 if (draggingHandle2)// || draggingLine)
284 /*virtual*/ void Dimension::PointerReleased(void)
286 /* if (draggingHandle1 || draggingHandle2)
288 // Set the length (in case the global state was set to fixed (or not))
289 if (Object::fixedLength)
292 if (draggingHandle1) // startpoint
294 Vector v = Vector(endpoint, position).Unit() * length;
295 position = endpoint + v;
299 Vector v = Vector(position, endpoint).Unit() * length;
300 endpoint = position + v;
305 // Otherwise, we calculate the new length, just in case on the next move
306 // it turns out to have a fixed length. :-)
307 length = Vector(endpoint - position).Magnitude();
312 draggingHandle1 = false;
313 draggingHandle2 = false;
315 // Here we check for just a click: If object was clicked and dragged, then
316 // revert to the old state (OSInactive). Otherwise, keep the new state that
318 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
319 about keeping track of old states...
321 if (objectWasDragged)
326 /*virtual*/ bool Dimension::HitTest(Point point)
328 Vector orthogonal = Vector::Normal(position, endpoint);
329 // Get our line parallel to our points
331 Point p1 = position + (orthogonal * 10.0 * size);
332 Point p2 = endpoint + (orthogonal * 10.0 * size);
334 Point p1 = linePt1 + (orthogonal * 10.0 * size);
335 Point p2 = linePt2 + (orthogonal * 10.0 * size);
339 hitPoint1 = hitPoint2 = hitLine = hitFlipSwitch = false;
340 Vector v1(position, point);
341 Vector v2(endpoint, point);
342 // Vector lineSegment(position, endpoint);
343 Vector lineSegment(p1, p2);
344 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
345 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
349 distance = v1.Magnitude();
351 distance = v2.Magnitude();
353 // distance = ?Det?(ls, v1) / |ls|
354 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
355 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
356 / lineSegment.Magnitude());
358 if ((v1.Magnitude() * Painter::zoom) < 8.0)
360 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
362 else if ((distance * Painter::zoom) < 5.0)
365 return (hitPoint1 || hitPoint2 || hitLine ? true : false);
369 void Dimension::SaveHitState(void)
371 oldHitPoint1 = hitPoint1;
372 oldHitPoint2 = hitPoint2;
373 oldHitLine = hitLine;
377 bool Dimension::HitStateChanged(void)
379 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
386 /*virtual*/ void Dimension::Enumerate(FILE * file)
388 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
392 /*virtual*/ Object * Dimension::Copy(void)
394 #warning "!!! This doesn't take care of attached Dimensions !!!"
396 This is a real problem. While having a pointer in the Dimension to this line's points
397 is fast & easy, it creates a huge problem when trying to replicate an object like this.
399 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
400 way, if you copy them, ... you might still have problems. Because you can't be sure if
401 a copy will be persistant or not, you then *definitely* do not want them to have the
402 same reference number.
405 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
411 // Dimensions are special: they contain exactly *two* points. Here, we check
412 // only for zero/non-zero in returning the correct points.
413 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
422 /*virtual*/ void Dimension::MovePointAtParameter(double parameter, Vector v)
426 else if (parameter == 1.0)
429 {} // Not sure how to handle this case :-P
433 /*virtual*/ void Dimension::Connect(Object * obj, double param)
435 // There are four possibilities here...
436 // The param is only looking for 0 or 1 here.
437 if (point1.object == NULL && point2.object == NULL)
442 else if (point1.object == NULL && point2.object != NULL)
444 if (point2.t == param)
452 else if (point1.object != NULL && point2.object == NULL)
454 if (point1.t == param)
462 else if (point1.object != NULL && point2.object != NULL)
464 if (point1.t == param)
472 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
474 if (point1.object == obj && point1.t == param)
475 point1.object = NULL;
476 else if (point2.object == obj && point2.t == param)
477 point2.object = NULL;
481 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
483 if (point1.object == obj)
484 point1.object = NULL;
486 if (point2.object == obj)
487 point2.object = NULL;
491 /*virtual*/ QRectF Dimension::Extents(void)
496 // if (point1.object)
497 // p1 = point1.object->GetPointAtParameter(point1.t);
499 // if (point2.object)
500 // p2 = point2.object->GetPointAtParameter(point2.t);
502 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
507 /*virtual*/ ObjectType Dimension::Type(void)
514 void Dimension::FlipSides(void)
517 Vector tmp = position;
520 //Not sure this matters...
521 //#warning "!!! May need to swap parameter values on connected objects !!!"
523 Connection tmp = point1;
526 // double tmp = point1.t;
527 // point1.t = point2.t;
529 // Object * tmp = point1.object;
530 // point1.object = point2.object;
531 // point2.object = tmp;