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