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