]> Shamusworld >> Repos - architektonas/blob - src/dimension.cpp
d049fc32da0316336c4ba0414bce1cd2e8de5e9a
[architektonas] / src / dimension.cpp
1 // dimension.cpp: Dimension object
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  04/04/2011  Created this file, basic rendering
12 //
13
14 #include "dimension.h"
15
16 #include <QtGui>
17 #include "mathconstants.h"
18
19
20 Dimension::Dimension(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint(p2),
21         dragging(false), draggingHandle1(false), draggingHandle2(false),
22         length(p2.Magnitude())
23 {
24 }
25
26 Dimension::~Dimension()
27 {
28 }
29
30 /*virtual*/ void Dimension::Draw(QPainter * painter)
31 {
32         if (state == OSSelected)
33                 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
34         else
35                 painter->setPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
36
37 #if 0
38 //      if (draggingHandle1)
39         if (state == OSSelected)
40                 painter->drawEllipse(QPointF(position.x, position.y), 4.0, 4.0);
41
42 //      if (draggingHandle2)
43         if (state == OSSelected)
44                 painter->drawEllipse(QPointF(endpoint.x, endpoint.y), 4.0, 4.0);
45
46         if (Object::fixedLength && (draggingHandle1 || draggingHandle2))
47         {
48                 Vector point1 = (draggingHandle1 ? endpoint : position);
49                 Vector point2 = (draggingHandle1 ? position : endpoint);
50
51                 Vector current(point2 - point1);
52                 Vector v = current.Unit() * length;
53                 Vector v2 = point1 + v;
54                 painter->drawLine((int)point1.x, (int)point1.y, (int)v2.x, (int)v2.y);
55
56                 if (current.Magnitude() > length)
57                 {
58                         painter->setPen(QPen(QColor(128, 0, 0), 1.0, Qt::DashLine));
59                         painter->drawLine((int)v2.x, (int)v2.y, (int)point2.x, (int)point2.y);
60                 }
61         }
62         else
63                 painter->drawLine((int)position.x, (int)position.y, (int)endpoint.x, (int)endpoint.y);
64 #endif
65         // Draw an aligned dimension line
66         double angle = Vector(endpoint - position).Angle();
67         double orthoAngle = angle + (PI / 2.0);
68         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
69         Vector unit = Vector(endpoint - position).Unit();
70
71         // Get our line parallel to our points
72         Point p1 = position + (orthogonal * 10.0);
73         Point p2 = endpoint + (orthogonal * 10.0);
74
75         // Draw main dimension line
76         painter->drawLine(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
77
78         Point p3 = position + (orthogonal * 16.0);
79         Point p4 = endpoint + (orthogonal * 16.0);
80         Point p5 = position + (orthogonal * 4.0);
81         Point p6 = endpoint + (orthogonal * 4.0);
82
83         // Draw extension lines
84         painter->drawLine(QPointF(p3.x, p3.y), QPointF(p5.x, p5.y));
85         painter->drawLine(QPointF(p4.x, p4.y), QPointF(p6.x, p6.y));
86
87         // Draw length of dimension line...
88         painter->setFont(QFont("Arial", 10));
89         Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
90         Point ctr = p2 + v1;
91         QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
92         int textWidth = QFontMetrics(painter->font()).width(dimText);
93 //We have to do transformation voodoo to make the text come out readable and in correct orientation...
94 //Some things to note here: if angle > 90 degrees, then we need to take the negative of the angle
95 //for our text.
96 painter->save();
97 painter->translate(ctr.x, ctr.y);
98 painter->rotate(angle * RADIANS_TO_DEGREES);
99 painter->scale(1.0, -1.0);
100 //painter->translate(-textWidth / 2, -24);
101 //      painter->drawText(0, 0, textWidth, 20, Qt::AlignCenter, dimText);
102         // This version draws the y-coord from the baseline of the font
103         painter->drawText(-textWidth / 2, -8, dimText);
104 //painter->setPen(QPen(QColor(0xFF, 0x20, 0x20), 1.0, Qt::SolidLine));
105 //painter->drawLine(20, 0, -20, 0);
106 //painter->drawLine(0, 20, 0, -20);
107 painter->restore();
108 }
109
110 /*virtual*/ Vector Dimension::Center(void)
111 {
112         // Technically, this is the midpoint but who are we to quibble? :-)
113         Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
114         return endpoint + v;
115 }
116
117 /*virtual*/ bool Dimension::Collided(Vector /*point*/)
118 {
119 #if 0
120         objectWasDragged = false;
121         Vector lineSegment = endpoint - position;
122         Vector v1 = point - position;
123         Vector v2 = point - endpoint;
124         double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
125
126         // Geometric interpretation:
127         // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
128         // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
129         // then the perpendicular lies beyond the 2nd endpoint.
130
131         if (parameterizedPoint < 0.0)
132                 distance = v1.Magnitude();
133         else if (parameterizedPoint > lineSegment.Magnitude())
134                 distance = v2.Magnitude();
135         else                                    // distance = ?Det?(ls, v1) / |ls|
136                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
137
138         // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
139         // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
140         // {e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
141         // computed by first computing the area of the triangle the three points form, then dividing by the
142         // length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
143         // triangle formed by three points is the determinant of the following matrix:
144         //
145         // sx sy 1
146         // ex ey 1
147         // px py 1
148         //
149         // By translating the start point to the origin, this can be rewritten as:
150         // By subtracting row 1 from all rows, you get the following:
151         // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
152         // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
153         //
154         // 0         0         0        0  0  0
155         // (ex - sx) (ey - sy) 0   ==>  ex ey 0
156         // (px - sx) (py - sy) 0        px py 0
157         //
158         // which greatly simplifies the calculation of the determinant.
159
160         if (state == OSInactive)
161         {
162 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
163 //printf("      v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
164 //printf("      point = %lf,%lf,%lf; p1 = %lf,%lf,%lf; p2 = %lf,%lf,%lf\n", point.x, point.y, point.z, position.x, position.y, position.z, endpoint.x, endpoint.y, endpoint.z);
165 //printf("      \n", );
166 //How to translate this into pixels from Document space???
167 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
168 //the caller knows about the zoom factor and all that good kinda crap
169                 if (v1.Magnitude() < 10.0)
170                 {
171                         oldState = state;
172                         state = OSSelected;
173                         oldPoint = position; //maybe "position"?
174                         draggingHandle1 = true;
175                         return true;
176                 }
177                 else if (v2.Magnitude() < 10.0)
178                 {
179                         oldState = state;
180                         state = OSSelected;
181                         oldPoint = endpoint; //maybe "position"?
182                         draggingHandle2 = true;
183                         return true;
184                 }
185                 else if (distance < 2.0)
186                 {
187                         oldState = state;
188                         state = OSSelected;
189                         oldPoint = point;
190                         dragging = true;
191                         return true;
192                 }
193         }
194         else if (state == OSSelected)
195         {
196                 // Here we test for collision with handles as well! (SOON!)
197 /*
198 Like so:
199                 if (v1.Magnitude() < 2.0) // Handle #1
200                 else if (v2.Magnitude() < 2.0) // Handle #2
201 */
202                 if (distance < 2.0)
203                 {
204                         oldState = state;
205 //                      state = OSInactive;
206                         oldPoint = point;
207                         dragging = true;
208                         return true;
209                 }
210         }
211 #endif
212
213         state = OSInactive;
214         return false;
215 }
216
217 /*virtual*/ void Dimension::PointerMoved(Vector point)
218 {
219         // We know this is true because mouse move messages don't come here unless
220         // the object was actually clicked on--therefore we *know* we're being
221         // dragged...
222         objectWasDragged = true;
223
224         if (dragging)
225         {
226                 // Here we need to check whether or not we're dragging a handle or the object itself...
227                 Vector delta = point - oldPoint;
228
229                 position += delta;
230                 endpoint += delta;
231
232                 oldPoint = point;
233                 needUpdate = true;
234         }
235         else if (draggingHandle1)
236         {
237                 Vector delta = point - oldPoint;
238
239                 position += delta;
240
241                 oldPoint = point;
242                 needUpdate = true;
243         }
244         else if (draggingHandle2)
245         {
246                 Vector delta = point - oldPoint;
247
248                 endpoint += delta;
249
250                 oldPoint = point;
251                 needUpdate = true;
252         }
253         else
254                 needUpdate = false;
255 }
256
257 /*virtual*/ void Dimension::PointerReleased(void)
258 {
259         if (draggingHandle1 || draggingHandle2)
260         {
261                 // Set the length (in case the global state was set to fixed (or not))
262                 if (Object::fixedLength)
263                 {
264
265                         if (draggingHandle1)    // startpoint
266                         {
267                                 Vector v = Vector(position - endpoint).Unit() * length;
268                                 position = endpoint + v;
269                         }
270                         else                                    // endpoint
271                         {
272 //                              Vector v1 = endpoint - position;
273                                 Vector v = Vector(endpoint - position).Unit() * length;
274                                 endpoint = position + v;
275                         }
276                 }
277                 else
278                 {
279                         // Otherwise, we calculate the new length, just in case on the next move
280                         // it turns out to have a fixed length. :-)
281                         length = Vector(endpoint - position).Magnitude();
282                 }
283         }
284
285         dragging = false;
286         draggingHandle1 = false;
287         draggingHandle2 = false;
288
289         // Here we check for just a click: If object was clicked and dragged, then
290         // revert to the old state (OSInactive). Otherwise, keep the new state that
291         // we set.
292 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
293 about keeping track of old states...
294 */
295         if (objectWasDragged)
296                 state = oldState;
297 }