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 The approach used below creates a hierarchy: Dimension is subservient to Line.
50 Does this solve our problem of connected objects? Maybe, partially. Let's think this
51 through. It only works for endpoints, not points in the middle...
53 Also: this is bad, depending on the Draw() function to update the internal
54 position(s) of the data of the object! (is it though?)
56 How to move: click once moves only the object/point clicked on, all connected
57 objects deform themselves accordingly. click twice selects ALL connected objects;
58 all objects move as a unified whole.
62 /*virtual*/ void Dimension::Draw(Painter * painter)
64 // If there are valid Vector pointers in here, use them to update the internal
65 // positions. Otherwise, we just use the internal positions by default.
67 position = point1.object->GetPointAtParameter(point1.t);
70 endpoint = point2.object->GetPointAtParameter(point2.t);
72 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
74 if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
75 painter->DrawHandle(position);
77 if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
78 painter->DrawHandle(endpoint);
80 if (state == OSSelected)
81 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
83 // painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
85 painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine));
87 painter->SetBrush(QBrush(QColor(Qt::blue)));
89 // Draw an aligned dimension line
90 double angle = Vector(endpoint - position).Angle();
91 double orthoAngle = angle + (PI / 2.0);
92 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
93 Vector unit = Vector(endpoint - position).Unit();
96 // Point p1 = head - (unit * 9.0 * size);
97 // Point p2 = p1 + (orthogonal * 3.0 * size);
98 // Point p3 = p1 - (orthogonal * 3.0 * size);
101 The numbers hardcoded into here, what are they?
102 I believe they are pixels.
105 // Get our line parallel to our points
106 Point p1 = position + (orthogonal * 10.0 * size);
107 Point p2 = endpoint + (orthogonal * 10.0 * size);
109 Point p3 = position + (orthogonal * 16.0 * size);
110 Point p4 = endpoint + (orthogonal * 16.0 * size);
111 Point p5 = position + (orthogonal * 4.0 * size);
112 Point p6 = endpoint + (orthogonal * 4.0 * size);
114 // Draw extension lines
115 painter->DrawLine(p3, p5);
116 painter->DrawLine(p4, p6);
118 // Calculate whether or not the arrowheads are too crowded to put inside
119 // the extension lines. 9.0 is the length of the arrowhead.
120 // double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size));
121 double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size));
122 //printf("Dimension::Draw(): t = %lf\n", t);
124 // On the screen, it's acting like this is actually 58%...
125 // This is correct, we want it to happen at > 50%
128 // Draw main dimension line + arrowheads
129 painter->DrawLine(p1, p2);
130 painter->DrawArrowhead(p1, p2, size);
131 painter->DrawArrowhead(p2, p1, size);
135 // Draw outside arrowheads
136 Point p7 = p1 - (unit * 9.0 * size);
137 Point p8 = p2 + (unit * 9.0 * size);
138 painter->DrawArrowhead(p1, p7, size);
139 painter->DrawArrowhead(p2, p8, size);
140 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
141 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
144 // Draw length of dimension line...
145 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
146 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
150 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
153 double length = Vector(endpoint - position).Magnitude();
156 dimText = QString("%1\"").arg(length);
159 double feet = (double)((int)length / 12);
160 double inches = length - (feet * 12.0);
163 dimText = QString("%1'").arg(feet);
165 dimText = QString("%1' %2\"").arg(feet).arg(inches);
169 painter->DrawAngledText(ctr, angle, dimText, size);
173 /*virtual*/ Vector Dimension::Center(void)
175 // Technically, this is the midpoint but who are we to quibble? :-)
176 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
181 /*virtual*/ bool Dimension::Collided(Vector point)
183 // Someone told us to fuck off, so we'll fuck off. :-)
187 // We can assume this, since this is a mouse down event here.
188 objectWasDragged = false;
191 // Now that we've done our hit testing on the non-snapped point, snap it if
194 point = SnapPointToGrid(point);
201 draggingHandle1 = true;
209 draggingHandle2 = true;
218 /*virtual*/ void Dimension::PointerMoved(Vector point)
221 // We know this is true because mouse move messages don't come here unless
222 // the object was actually clicked on--therefore we *know* we're being
224 objectWasDragged = true;
228 // Here we need to check whether or not we're dragging a handle or the object itself...
229 Vector delta = point - oldPoint;
237 else*/ if (draggingHandle1)
239 Vector delta = point - oldPoint;
246 else if (draggingHandle2)
248 Vector delta = point - oldPoint;
258 // Hit test tells us what we hit (if anything) through boolean variables. (It
259 // also tells us whether or not the state changed. --not any more)
262 needUpdate = HitStateChanged();
264 objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
266 if (objectWasDragged)
268 Vector delta = point - oldPoint;
270 if (draggingHandle1)// || draggingLine)
273 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 hitPoint1 = hitPoint2 = false;
328 // Vector lineSegment(position, endpoint);
329 Vector v1(position, point);
330 Vector v2(endpoint, point);
331 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
335 // distance = v1.Magnitude();
337 // distance = v2.Magnitude();
339 // distance = ?Det?(ls, v1) / |ls|
340 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
341 // / lineSegment.Magnitude());
343 if ((v1.Magnitude() * Painter::zoom) < 8.0)
345 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
348 return (hitPoint1 || hitPoint2 ? true : false);
352 void Dimension::SaveHitState(void)
354 oldHitPoint1 = hitPoint1;
355 oldHitPoint2 = hitPoint2;
356 // oldHitLine = hitLine;
360 bool Dimension::HitStateChanged(void)
362 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2))
369 /*virtual*/ void Dimension::Enumerate(FILE * file)
371 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
375 /*virtual*/ Object * Dimension::Copy(void)
377 #warning "!!! This doesn't take care of attached Dimensions !!!"
379 This is a real problem. While having a pointer in the Dimension to this line's points
380 is fast & easy, it creates a huge problem when trying to replicate an object like this.
382 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
383 way, if you copy them, ... you might still have problems. Because you can't be sure if
384 a copy will be persistant or not, you then *definitely* do not want them to have the
385 same reference number.
388 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
394 // Dimensions are special: they contain exactly *two* points. Here, we check
395 // only for zero/non-zero in returning the correct points.
396 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
405 /*virtual*/ void Dimension::Connect(Object * obj, double param)
407 // There are four possibilities here...
408 // The param is only looking for 0 or 1 here.
409 if (point1.object == NULL && point2.object == NULL)
414 else if (point1.object == NULL && point2.object != NULL)
416 if (point2.t == param)
424 else if (point1.object != NULL && point2.object == NULL)
426 if (point1.t == param)
434 else if (point1.object != NULL && point2.object != NULL)
436 if (point1.t == param)
444 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
446 if (point1.object == obj && point1.t == param)
447 point1.object = NULL;
448 else if (point2.object == obj && point2.t == param)
449 point2.object = NULL;
453 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
455 if (point1.object == obj)
456 point1.object = NULL;
458 if (point2.object == obj)
459 point2.object = NULL;
463 /*virtual*/ QRectF Dimension::Extents(void)
469 p1 = point1.object->GetPointAtParameter(point1.t);
472 p2 = point2.object->GetPointAtParameter(point2.t);
474 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
479 /*virtual*/ ObjectType Dimension::Type(void)
486 void Dimension::FlipSides(void)
489 Vector tmp = position;
493 Connection tmp = point1;
496 // double tmp = point1.t;
497 // point1.t = point2.t;
499 // Object * tmp = point1.object;
500 // point1.object = point2.object;
501 // point2.object = tmp;