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)
65 // If there are valid Vector pointers in here, use them to update the internal
66 // positions. Otherwise, we just use the internal positions by default.
68 position = point1.object->GetPointAtParameter(point1.t);
71 endpoint = point2.object->GetPointAtParameter(point2.t);
74 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
76 if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
77 painter->DrawHandle(position);
79 if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
80 painter->DrawHandle(endpoint);
82 if (state == OSSelected)
83 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
85 // painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
87 painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine));
89 painter->SetBrush(QBrush(QColor(Qt::blue)));
91 // Draw an aligned dimension line
92 double angle = Vector(endpoint - position).Angle();
93 double orthoAngle = angle + (PI / 2.0);
94 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
95 Vector unit = Vector(endpoint - position).Unit();
98 // Point p1 = head - (unit * 9.0 * size);
99 // Point p2 = p1 + (orthogonal * 3.0 * size);
100 // Point p3 = p1 - (orthogonal * 3.0 * size);
103 The numbers hardcoded into here, what are they?
104 I believe they are pixels.
107 // Get our line parallel to our points
108 Point p1 = position + (orthogonal * 10.0 * size);
109 Point p2 = endpoint + (orthogonal * 10.0 * size);
111 Point p3 = position + (orthogonal * 16.0 * size);
112 Point p4 = endpoint + (orthogonal * 16.0 * size);
113 Point p5 = position + (orthogonal * 4.0 * size);
114 Point p6 = endpoint + (orthogonal * 4.0 * size);
116 // Draw extension lines
117 painter->DrawLine(p3, p5);
118 painter->DrawLine(p4, p6);
120 // Calculate whether or not the arrowheads are too crowded to put inside
121 // the extension lines. 9.0 is the length of the arrowhead.
122 // double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size));
123 double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size));
124 //printf("Dimension::Draw(): t = %lf\n", t);
126 // On the screen, it's acting like this is actually 58%...
127 // This is correct, we want it to happen at > 50%
130 // Draw main dimension line + arrowheads
131 painter->DrawLine(p1, p2);
132 painter->DrawArrowhead(p1, p2, size);
133 painter->DrawArrowhead(p2, p1, size);
137 // Draw outside arrowheads
138 Point p7 = p1 - (unit * 9.0 * size);
139 Point p8 = p2 + (unit * 9.0 * size);
140 painter->DrawArrowhead(p1, p7, size);
141 painter->DrawArrowhead(p2, p8, size);
142 painter->DrawLine(p1, p1 - (unit * 14.0 * size));
143 painter->DrawLine(p2, p2 + (unit * 14.0 * size));
146 // Draw length of dimension line...
147 painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size));
148 Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
152 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
155 double length = Vector(endpoint - position).Magnitude();
158 dimText = QString("%1\"").arg(length);
161 double feet = (double)((int)length / 12);
162 double inches = length - (feet * 12.0);
165 dimText = QString("%1'").arg(feet);
167 dimText = QString("%1' %2\"").arg(feet).arg(inches);
171 painter->DrawAngledText(ctr, angle, dimText, size);
175 /*virtual*/ Vector Dimension::Center(void)
177 // Technically, this is the midpoint but who are we to quibble? :-)
178 Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
183 /*virtual*/ bool Dimension::Collided(Vector point)
185 // Someone told us to fuck off, so we'll fuck off. :-)
189 // We can assume this, since this is a mouse down event here.
190 objectWasDragged = false;
193 // Now that we've done our hit testing on the non-snapped point, snap it if
196 point = SnapPointToGrid(point);
203 draggingHandle1 = true;
211 draggingHandle2 = true;
220 /*virtual*/ void Dimension::PointerMoved(Vector point)
223 // We know this is true because mouse move messages don't come here unless
224 // the object was actually clicked on--therefore we *know* we're being
226 objectWasDragged = true;
230 // Here we need to check whether or not we're dragging a handle or the object itself...
231 Vector delta = point - oldPoint;
239 else*/ if (draggingHandle1)
241 Vector delta = point - oldPoint;
248 else if (draggingHandle2)
250 Vector delta = point - oldPoint;
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)
285 /*virtual*/ void Dimension::PointerReleased(void)
287 /* if (draggingHandle1 || draggingHandle2)
289 // Set the length (in case the global state was set to fixed (or not))
290 if (Object::fixedLength)
293 if (draggingHandle1) // startpoint
295 Vector v = Vector(endpoint, position).Unit() * length;
296 position = endpoint + v;
300 Vector v = Vector(position, endpoint).Unit() * length;
301 endpoint = position + v;
306 // Otherwise, we calculate the new length, just in case on the next move
307 // it turns out to have a fixed length. :-)
308 length = Vector(endpoint - position).Magnitude();
313 draggingHandle1 = false;
314 draggingHandle2 = false;
316 // Here we check for just a click: If object was clicked and dragged, then
317 // revert to the old state (OSInactive). Otherwise, keep the new state that
319 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
320 about keeping track of old states...
322 if (objectWasDragged)
327 /*virtual*/ bool Dimension::HitTest(Point point)
329 hitPoint1 = hitPoint2 = false;
330 // Vector lineSegment(position, endpoint);
331 Vector v1(position, point);
332 Vector v2(endpoint, point);
333 // double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
337 // distance = v1.Magnitude();
339 // distance = v2.Magnitude();
341 // distance = ?Det?(ls, v1) / |ls|
342 // distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
343 // / lineSegment.Magnitude());
345 if ((v1.Magnitude() * Painter::zoom) < 8.0)
347 else if ((v2.Magnitude() * Painter::zoom) < 8.0)
350 return (hitPoint1 || hitPoint2 ? true : false);
354 void Dimension::SaveHitState(void)
356 oldHitPoint1 = hitPoint1;
357 oldHitPoint2 = hitPoint2;
358 // oldHitLine = hitLine;
362 bool Dimension::HitStateChanged(void)
364 if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2))
371 /*virtual*/ void Dimension::Enumerate(FILE * file)
373 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
377 /*virtual*/ Object * Dimension::Copy(void)
379 #warning "!!! This doesn't take care of attached Dimensions !!!"
381 This is a real problem. While having a pointer in the Dimension to this line's points
382 is fast & easy, it creates a huge problem when trying to replicate an object like this.
384 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
385 way, if you copy them, ... you might still have problems. Because you can't be sure if
386 a copy will be persistant or not, you then *definitely* do not want them to have the
387 same reference number.
390 Dimension * d = new Dimension(position, endpoint, dimensionType, parent);
396 // Dimensions are special: they contain exactly *two* points. Here, we check
397 // only for zero/non-zero in returning the correct points.
398 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
407 /*virtual*/ void Dimension::MovePointAtParameter(double parameter, Vector v)
411 else if (parameter == 1.0)
414 {} // Not sure how to handle this case :-P
418 /*virtual*/ void Dimension::Connect(Object * obj, double param)
420 // There are four possibilities here...
421 // The param is only looking for 0 or 1 here.
422 if (point1.object == NULL && point2.object == NULL)
427 else if (point1.object == NULL && point2.object != NULL)
429 if (point2.t == param)
437 else if (point1.object != NULL && point2.object == NULL)
439 if (point1.t == param)
447 else if (point1.object != NULL && point2.object != NULL)
449 if (point1.t == param)
457 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
459 if (point1.object == obj && point1.t == param)
460 point1.object = NULL;
461 else if (point2.object == obj && point2.t == param)
462 point2.object = NULL;
466 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
468 if (point1.object == obj)
469 point1.object = NULL;
471 if (point2.object == obj)
472 point2.object = NULL;
476 /*virtual*/ QRectF Dimension::Extents(void)
482 p1 = point1.object->GetPointAtParameter(point1.t);
485 p2 = point2.object->GetPointAtParameter(point2.t);
487 return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
492 /*virtual*/ ObjectType Dimension::Type(void)
499 void Dimension::FlipSides(void)
502 Vector tmp = position;
506 Connection tmp = point1;
509 // double tmp = point1.t;
510 // point1.t = point2.t;
512 // Object * tmp = point1.object;
513 // point1.object = point2.object;
514 // point2.object = tmp;