]> Shamusworld >> Repos - architektonas/blob - src/dimension.cpp
Fixes to accomodate object connections.
[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 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 #include "painter.h"
19
20
21 Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/ ,Object * p/*= NULL*/):
22         Object(p1, p), endpoint(p2),
23         dragging(false), draggingHandle1(false), draggingHandle2(false),
24         length(p2.Magnitude()), type(dt), point1(NULL), point2(NULL)
25 {
26 }
27
28
29 #if 0
30 // This is bad, p1 & p2 could be NULL, causing much consternation...
31 Dimension::Dimension(Vector * p1, Vector * p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
32         Object(*p1, p), endpoint(*p2),
33         dragging(false), draggingHandle1(false), draggingHandle2(false),
34         length(p2->Magnitude()), type(dt), point1(p1), point2(p2)
35 {
36 }
37 #endif
38
39
40 // This is bad, p1 & p2 could be NULL, causing much consternation...
41 Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
42 /*      Object(p1.object->GetPointForParameter(p1.t), p),
43         endpoint(p2.object->GetPointForParameter(p2.t)),*/
44         dragging(false), draggingHandle1(false), draggingHandle2(false),
45         /*length(p2->Magnitude()),*/length(0), type(dt), point1(p1), point2(p2)
46 {
47 }
48
49
50 Dimension::~Dimension()
51 {
52 }
53
54
55 /*virtual*/ void Dimension::Draw(Painter * painter)
56 {
57         // If there are valid Vector pointers in here, use them to update the internal
58         // positions. Otherwise, we just use the internal positions by default.
59 #if 0
60         if (point1)
61                 position = *point1;
62
63         if (point2)
64                 endpoint = *point2;
65 #else
66         if (point1.object)
67                 position = point1.object->GetPointAtParameter(point1.t);
68
69         if (point2.object)
70                 endpoint = point2.object->GetPointAtParameter(point2.t);
71 #endif
72
73         if (state == OSSelected)
74                 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
75         else
76                 painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine));
77
78         // Draw an aligned dimension line
79         double angle = Vector(endpoint - position).Angle();
80         double orthoAngle = angle + (PI / 2.0);
81         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
82         Vector unit = Vector(endpoint - position).Unit();
83
84 //NOTE: SCREEN_ZOOM is our kludge factor... We need to figure out a better
85 //      way of doing this...
86         // Get our line parallel to our points
87         Point p1 = position + (orthogonal * 10.0 * SCREEN_ZOOM);
88         Point p2 = endpoint + (orthogonal * 10.0 * SCREEN_ZOOM);
89
90         // Draw main dimension line
91         painter->DrawLine(p1, p2);
92
93         Point p3 = position + (orthogonal * 16.0 * SCREEN_ZOOM);
94         Point p4 = endpoint + (orthogonal * 16.0 * SCREEN_ZOOM);
95         Point p5 = position + (orthogonal * 4.0 * SCREEN_ZOOM);
96         Point p6 = endpoint + (orthogonal * 4.0 * SCREEN_ZOOM);
97
98         // Draw extension lines
99         painter->DrawLine(p3, p5);
100         painter->DrawLine(p4, p6);
101
102         painter->SetBrush(QBrush(QColor(Qt::blue)));
103         painter->DrawArrowhead(p1, p2);
104         painter->DrawArrowhead(p2, p1);
105
106         // Draw length of dimension line...
107         painter->SetFont(QFont("Arial", 10.0 * Painter::zoom * SCREEN_ZOOM));
108         Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0);
109         Point ctr = p2 + v1;
110         // This is in pixels, which isn't even remotely correct... !!! FIX !!!
111         QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
112 //      int textWidth = QFontMetrics(painter->font()).width(dimText);
113 //      int textHeight = QFontMetrics(painter->font()).height();
114 #if 0
115 //We have to do transformation voodoo to make the text come out readable and in correct orientation...
116 //Some things to note here: if angle > 90 degrees, then we need to take the negative of the angle
117 //for our text.
118 painter->save();
119 painter->translate(ctr.x, ctr.y);
120 int yOffset = -8;
121 //16 : printf("textHeight: %d\n", textHeight);
122
123 //Fix text so it isn't upside down...
124 if ((angle > PI * 0.5) && (angle < PI * 1.5))
125 {
126         angle += PI;
127         yOffset = 18;
128 }
129
130 painter->rotate(angle * RADIANS_TO_DEGREES);
131 painter->scale(1.0, -1.0);
132 //painter->translate(-textWidth / 2, -24);
133 //      painter->drawText(0, 0, textWidth, 20, Qt::AlignCenter, dimText);
134         // This version draws the y-coord from the baseline of the font
135         painter->DrawText(-textWidth / 2, yOffset, dimText);
136 //painter->setPen(QPen(QColor(0xFF, 0x20, 0x20), 1.0, Qt::SolidLine));
137 //painter->drawLine(20, 0, -20, 0);
138 //painter->drawLine(0, 20, 0, -20);
139 painter->restore();
140 #else
141 //      painter->DrawText(QRectF(QPointF(ctr.x, ctr.y), QPointF(ctr.x + textWidth, ctr.y + textHeight)), Qt::AlignVCenter, dimText);
142 // Now that we've taken our own good advice, maybe we should have the painter class
143 // do a nice abstracted text draw routine? :-)
144         painter->DrawAngledText(ctr, angle, dimText);
145 #endif
146
147 /*
148 All of the preceeding makes me think that rather than try to compensate for Qt's unbelieveably
149 AWFUL decision to go with a wrong-handed graphics subsystem, it may be better to just stuff
150 all of that crap into some kind of subclass that handles all the nastiness behind the scenes.
151 I mean, really, all this crap just to get some proplerly rendered text on the screen? How
152 retarded is that? :-/
153 */
154 }
155
156
157 /*virtual*/ Vector Dimension::Center(void)
158 {
159         // Technically, this is the midpoint but who are we to quibble? :-)
160         Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
161         return endpoint + v;
162 }
163
164
165 /*virtual*/ bool Dimension::Collided(Vector /*point*/)
166 {
167 #if 0
168         objectWasDragged = false;
169         Vector lineSegment = endpoint - position;
170         Vector v1 = point - position;
171         Vector v2 = point - endpoint;
172         double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
173
174         // Geometric interpretation:
175         // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
176         // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
177         // then the perpendicular lies beyond the 2nd endpoint.
178
179         if (parameterizedPoint < 0.0)
180                 distance = v1.Magnitude();
181         else if (parameterizedPoint > lineSegment.Magnitude())
182                 distance = v2.Magnitude();
183         else                                    // distance = ?Det?(ls, v1) / |ls|
184                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
185
186         // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
187         // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
188         // {e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
189         // computed by first computing the area of the triangle the three points form, then dividing by the
190         // length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
191         // triangle formed by three points is the determinant of the following matrix:
192         //
193         // sx sy 1
194         // ex ey 1
195         // px py 1
196         //
197         // By translating the start point to the origin, this can be rewritten as:
198         // By subtracting row 1 from all rows, you get the following:
199         // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
200         // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
201         //
202         // 0         0         0        0  0  0
203         // (ex - sx) (ey - sy) 0   ==>  ex ey 0
204         // (px - sx) (py - sy) 0        px py 0
205         //
206         // which greatly simplifies the calculation of the determinant.
207
208         if (state == OSInactive)
209         {
210 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
211 //printf("      v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
212 //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);
213 //printf("      \n", );
214 //How to translate this into pixels from Document space???
215 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
216 //the caller knows about the zoom factor and all that good kinda crap
217                 if (v1.Magnitude() < 10.0)
218                 {
219                         oldState = state;
220                         state = OSSelected;
221                         oldPoint = position; //maybe "position"?
222                         draggingHandle1 = true;
223                         return true;
224                 }
225                 else if (v2.Magnitude() < 10.0)
226                 {
227                         oldState = state;
228                         state = OSSelected;
229                         oldPoint = endpoint; //maybe "position"?
230                         draggingHandle2 = true;
231                         return true;
232                 }
233                 else if (distance < 2.0)
234                 {
235                         oldState = state;
236                         state = OSSelected;
237                         oldPoint = point;
238                         dragging = true;
239                         return true;
240                 }
241         }
242         else if (state == OSSelected)
243         {
244                 // Here we test for collision with handles as well! (SOON!)
245 /*
246 Like so:
247                 if (v1.Magnitude() < 2.0) // Handle #1
248                 else if (v2.Magnitude() < 2.0) // Handle #2
249 */
250                 if (distance < 2.0)
251                 {
252                         oldState = state;
253 //                      state = OSInactive;
254                         oldPoint = point;
255                         dragging = true;
256                         return true;
257                 }
258         }
259 #endif
260
261         state = OSInactive;
262         return false;
263 }
264
265
266 /*virtual*/ void Dimension::PointerMoved(Vector point)
267 {
268         // We know this is true because mouse move messages don't come here unless
269         // the object was actually clicked on--therefore we *know* we're being
270         // dragged...
271         objectWasDragged = true;
272
273         if (dragging)
274         {
275                 // Here we need to check whether or not we're dragging a handle or the object itself...
276                 Vector delta = point - oldPoint;
277
278                 position += delta;
279                 endpoint += delta;
280
281                 oldPoint = point;
282                 needUpdate = true;
283         }
284         else if (draggingHandle1)
285         {
286                 Vector delta = point - oldPoint;
287
288                 position += delta;
289
290                 oldPoint = point;
291                 needUpdate = true;
292         }
293         else if (draggingHandle2)
294         {
295                 Vector delta = point - oldPoint;
296
297                 endpoint += delta;
298
299                 oldPoint = point;
300                 needUpdate = true;
301         }
302         else
303                 needUpdate = false;
304 }
305
306
307 /*virtual*/ void Dimension::PointerReleased(void)
308 {
309         if (draggingHandle1 || draggingHandle2)
310         {
311                 // Set the length (in case the global state was set to fixed (or not))
312                 if (Object::fixedLength)
313                 {
314
315                         if (draggingHandle1)    // startpoint
316                         {
317                                 Vector v = Vector(position - endpoint).Unit() * length;
318                                 position = endpoint + v;
319                         }
320                         else                                    // endpoint
321                         {
322 //                              Vector v1 = endpoint - position;
323                                 Vector v = Vector(endpoint - position).Unit() * length;
324                                 endpoint = position + v;
325                         }
326                 }
327                 else
328                 {
329                         // Otherwise, we calculate the new length, just in case on the next move
330                         // it turns out to have a fixed length. :-)
331                         length = Vector(endpoint - position).Magnitude();
332                 }
333         }
334
335         dragging = false;
336         draggingHandle1 = false;
337         draggingHandle2 = false;
338
339         // Here we check for just a click: If object was clicked and dragged, then
340         // revert to the old state (OSInactive). Otherwise, keep the new state that
341         // we set.
342 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
343 about keeping track of old states...
344 */
345         if (objectWasDragged)
346                 state = oldState;
347 }
348
349
350 #if 0
351 void Dimension::SetPoint1(Vector * v)
352 {
353         point1 = v;
354         needUpdate = true;
355 }
356
357
358 void Dimension::SetPoint2(Vector * v)
359 {
360         point2 = v;
361         needUpdate = true;
362 }
363 #endif
364
365
366 /*virtual*/ void Dimension::Enumerate(FILE * file)
367 {
368         fprintf(file, "DIMENSION (%lf,%lf) (%lf,%lf) %i\n", position.x, position.y, endpoint.x, endpoint.y, type);
369 }
370
371
372 // Dimensions are special: they contain exactly *two* points. Here, we check
373 // only for zero/non-zero in returning the correct points.
374 /*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
375 {
376         if (parameter == 0)
377                 return position;
378
379         return endpoint;
380 }
381
382
383 /*virtual*/ void Dimension::Connect(Object * obj, double param)
384 {
385         // There are four possibilities here...
386         // The param is only looking for 0 or 1 here.
387         if (point1.object == NULL && point2.object == NULL)
388         {
389                 point1.object = obj;
390                 point1.t = param;
391         }
392         else if (point1.object == NULL && point2.object != NULL)
393         {
394                 if (point2.t == param)
395                         point2.object = obj;
396                 else
397                 {
398                         point1.object = obj;
399                         point1.t = param;
400                 }
401         }
402         else if (point1.object != NULL && point2.object == NULL)
403         {
404                 if (point1.t == param)
405                         point1.object = obj;
406                 else
407                 {
408                         point2.object = obj;
409                         point2.t = param;
410                 }
411         }
412         else if (point1.object != NULL && point2.object != NULL)
413         {
414                 if (point1.t == param)
415                         point1.object = obj;
416                 else
417                         point2.object = obj;
418         }
419 }
420
421
422 /*virtual*/ void Dimension::Disconnect(Object * obj, double param)
423 {
424         if (point1.object == obj && point1.t == param)
425                 point1 = NULL;
426         else if (point2.object == obj && point2.t == param)
427                 point2 = NULL;
428 }
429
430
431 /*virtual*/ void Dimension::DisconnectAll(Object * obj)
432 {
433         if (point1.object == obj)
434                 point1 = NULL;
435
436         if (point2.object == obj)
437                 point2 = NULL;
438 }
439
440
441 #if 0
442 Vector Dimension::GetPoint1(void)
443 {
444         return position;
445 }
446
447
448 Vector Dimension::GetPoint2(void)
449 {
450         return endpoint;
451 }
452 #endif
453
454
455 void Dimension::FlipSides(void)
456 {
457 #if 0
458         Vector tmp = position;
459         position = endpoint;
460         endpoint = tmp;
461 #else
462         Connection tmp = point1;
463         point1 = point2;
464         point2 = tmp;
465 //      double tmp = point1.t;
466 //      point1.t = point2.t;
467 //      point2.t = tmp;
468 //      Object * tmp = point1.object;
469 //      point1.object = point2.object;
470 //      point2.object = tmp;
471 #endif
472         needUpdate = true;
473 }
474