]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
Added ability to create Circles.
[architektonas] / src / drawingview.cpp
1 // drawingview.cpp
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  03/22/2011  Created this file
12 // JLH  09/29/2011  Added middle mouse button panning
13 //
14
15 // FIXED:
16 //
17 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
18 //   to a left-handed system and we need a right-handed one. [DONE]
19 //
20 // STILL TO BE DONE:
21 //
22 // - Lots of stuff
23 //
24
25 // Uncomment this for debugging...
26 //#define DEBUG
27 //#define DEBUGFOO                              // Various tool debugging...
28 //#define DEBUGTP                               // Toolpalette debugging...
29
30 #include "drawingview.h"
31
32 #include <stdint.h>
33 #include "geometry.h"
34 #include "global.h"
35 #include "mathconstants.h"
36 #include "painter.h"
37 #include "structs.h"
38 #include "utils.h"
39
40
41 #define BACKGROUND_MAX_SIZE     512
42
43 // Class variable
44 //Container DrawingView::document(Vector(0, 0));
45
46
47 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
48         // The value in the settings file will override this.
49         useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
50         ctrlDown(false),
51         gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
52         scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)),
53         gridPixels(0), collided(false)//, toolAction(NULL)
54 {
55 //      document.isTopLevelContainer = true;
56 //wtf? doesn't work except in c++11???  document = { 0 };
57         setBackgroundRole(QPalette::Base);
58         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
59
60         Global::gridSpacing = 12.0;             // In base units (inch is default)
61
62 #if 0
63         Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
64         document.Add(line);
65         document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
66         document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
67         document.Add(new Circle(Vector(100, 100), 36, &document));
68         document.Add(new Circle(Vector(50, 150), 49, &document));
69         document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
70         document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
71 #if 1
72         Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
73         line->SetDimensionOnLine(dimension);
74         document.Add(dimension);
75 #else
76         // Alternate way to do the above...
77         line->SetDimensionOnLine();
78 #endif
79 #else
80         Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
81         line->p[0] = Vector(5, 5);
82         line->p[1] = Vector(50, 40);
83         line->type = OTLine;
84         line->thickness = 2.0;
85         line->style = LSDash;
86         line->color = 0xFF7F00;
87         document.objects.push_back(line);
88         document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
89         document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
90         document.objects.push_back(new Circle(Vector(100, 100), 36));
91         document.objects.push_back(new Circle(Vector(50, 150), 49));
92         document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
93         document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
94         document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
95         document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
96 #endif
97
98 /*
99 Here we set the grid size in pixels--12 in this case. Initially, we have our
100 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
101 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
102 to be able to set the size of the background grid (which we do here at an
103 arbitrary 12 pixels) to anything we want (within reason, of course :-).
104
105 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
106
107         drawing->gridSpacing = 12.0 / Global::zoom;
108
109 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
110 translated to Cartesian coordinates through this. (Initially, Global::zoom is
111 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
112
113 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
114 convenience function than any measure of absolutes. Doing things that way we
115 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
116 shittiness that comes with it.
117
118 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
119 a certain way, which means we should probably create something else in those
120 objects to take its place--like some kind of scale factor. This would seem to
121 imply that certain point sizes actually *do* tie things like fonts to absolute
122 sizes on the screen, but not necessarily because you could have an inch scale
123 with text that is quite small relative to other objects on the screen, which
124 currently you have to zoom in to see (and which blows up the text). Point sizes
125 in an application like this are a bit meaningless; even though an inch is an
126 inch regardless of the zoom level a piece of text can be larger or smaller than
127 this. Maybe this is the case for having a base unit and basing point sizes off
128 of that.
129
130 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
131 base units. What that means is that if you have a 12px grid with a 6" grid size
132 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
133
134 Dimensions now have a "size" parameter to set their absolute size in relation
135 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
136 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
137 scaled the same way as the arrowheads.
138
139 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
140 need a thickness parameter similar to the "size" param for dimensions. (And now
141 we do! :-)
142
143 */
144         SetGridSize(12);        // This is in pixels
145 }
146
147
148 #if 0
149 void DrawingView::SetToolActive(Action * action)
150 {
151         if (action != NULL)
152         {
153                 toolAction = action;
154                 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
155                         SLOT(AddNewObjectToDocument(Object *)));
156                 connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
157         }
158 }
159 #endif
160
161
162 void DrawingView::SetGridSize(uint32_t size)
163 {
164         // Sanity check
165         if (size == gridPixels)
166                 return;
167
168         // Recreate the background bitmap
169         gridPixels = size;
170         QPainter pmp(&gridBackground);
171         pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
172         pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
173
174         for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
175         {
176                 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
177                 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
178         }
179
180         pmp.end();
181
182         // Set up new BG brush & zoom level (pixels per base unit)
183 //      Painter::zoom = gridPixels / gridSpacing;
184         Global::zoom = gridPixels / Global::gridSpacing;
185         UpdateGridBackground();
186 }
187
188
189 void DrawingView::UpdateGridBackground(void)
190 {
191         // Transform the origin to Qt coordinates
192         Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
193         int x = (int)pixmapOrigin.x;
194         int y = (int)pixmapOrigin.y;
195         // Use mod arithmetic to grab the correct swatch of background
196 /*
197 Negative numbers still screw it up... Need to think about what we're
198 trying to do here. The fact that it worked with 72 seems to have been pure luck.
199 It seems the problem is negative numbers: We can't let that happen.
200 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
201 grid at x<0.
202
203 The bitmap looks like this:
204
205 +---+---+---+---+---
206 |   |   |   |   |
207 |   |   |   |   |
208 +---+---+---+---+---
209 |   |   |   |   |
210 |   |   |   |   |
211 |   |   |   |   |
212
213 @ x = 1, we want it to look like:
214
215 -+---+---+---+---+---
216  |   |   |   |   |
217  |   |   |   |   |
218 -+---+---+---+---+---
219  |   |   |   |   |
220  |   |   |   |   |
221  |   |   |   |   |
222
223 Which means we need to grab the sample from x = 3. @ x = -1:
224
225 ---+---+---+---+---
226    |   |   |   |
227    |   |   |   |
228 ---+---+---+---+---
229    |   |   |   |
230    |   |   |   |
231    |   |   |   |
232
233 Which means we need to grab the sample from x = 1. Which means we have to take
234 the mirror of the modulus of gridPixels.
235
236 Doing a mod of a negative number is problematic: 1st, the compiler converts the
237 negative number to an unsigned int, then it does the mod. Gets you wrong answers
238 most of the time, unless you use a power of 2. :-P So what we do here is just
239 take the modulus of the negation, which means we don't have to worry about
240 mirroring it later.
241
242 The positive case looks gruesome (and it is) but it boils down to this: We take
243 the modulus of the X coordinate, then mirror it by subtraction from the
244 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
245 gridPixels. But we need the case where the result equalling gridPixels to be
246 zero; so we do another modulus operation on the result to achieve this.
247 */
248         if (x < 0)
249                 x = -x % gridPixels;
250         else
251                 x = (gridPixels - (x % gridPixels)) % gridPixels;
252
253         if (y < 0)
254                 y = -y % gridPixels;
255         else
256                 y = (gridPixels - (y % gridPixels)) % gridPixels;
257
258         // Here we grab a section of the bigger pixmap, so that the background
259         // *looks* like it's scrolling...
260         QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
261         QPalette pal = palette();
262         pal.setBrush(backgroundRole(), QBrush(pm));
263         setAutoFillBackground(true);
264         setPalette(pal);
265 }
266
267
268 void DrawingView::SetCurrentLayer(int layer)
269 {
270         Global::currentLayer = layer;
271 //printf("DrawingView::CurrentLayer = %i\n", layer);
272 }
273
274
275 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
276 {
277         // This is undoing the transform, e.g. going from client coords to local coords.
278         // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
279         // conversion of the y-axis from increasing bottom to top.
280         return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
281 }
282
283
284 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
285 {
286         // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
287         // No voodoo here, it's just grouped wrong to see it. It should be:
288         // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
289         return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
290 }
291
292
293 void DrawingView::paintEvent(QPaintEvent * /*event*/)
294 {
295         QPainter qtPainter(this);
296         Painter painter(&qtPainter);
297
298         if (useAntialiasing)
299                 qtPainter.setRenderHint(QPainter::Antialiasing);
300
301         Global::viewportHeight = size().height();
302
303         // Draw coordinate axes
304         painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
305         painter.DrawLine(0, -16384, 0, 16384);
306         painter.DrawLine(-16384, 0, 16384, 0);
307
308         // Do object rendering...
309         RenderObjects(&painter, document.objects);
310
311         // Do tool rendering, if any...
312         if (Global::tool)
313         {
314                 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
315                 painter.DrawCrosshair(oldPoint);
316                 ToolDraw(&painter);
317         }
318
319         // Do selection rectangle rendering, if any
320         if (Global::selectionInProgress)
321         {
322                 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
323                 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
324                 painter.DrawRect(Global::selection);
325         }
326
327         if (!informativeText.isEmpty())
328                 painter.DrawInformativeText(informativeText);
329 }
330
331
332 //
333 // Renders objects in the passed in vector
334 //
335 void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
336 {
337         std::vector<void *>::iterator i;
338
339         for(i=v.begin(); i!=v.end(); i++)
340         {
341                 Object * obj = (Object *)(*i);
342                 float scaledThickness = Global::scale * obj->thickness;
343
344                 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
345                 {
346                         painter->SetPen(0x00FF00, 2.0, LSSolid);
347                 }
348                 else
349                 {
350                         painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
351                         painter->SetBrush(obj->color);
352
353                         if (obj->selected || obj->hitObject)
354                                 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
355                 }
356
357                 switch (obj->type)
358                 {
359                 case OTLine:
360                         painter->DrawLine(obj->p[0], obj->p[1]);
361
362                         if (obj->hitPoint[0])
363                                 painter->DrawHandle(obj->p[0]);
364
365                         if (obj->hitPoint[1])
366                                 painter->DrawHandle(obj->p[1]);
367
368                         break;
369                 case OTCircle:
370                         painter->SetBrush(QBrush(Qt::NoBrush));
371                         painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
372
373                         if (obj->hitPoint[0])
374                                 painter->DrawHandle(obj->p[0]);
375
376                         break;
377                 case OTArc:
378                         painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
379                         break;
380                 case OTDimension:
381                 {
382                         Dimension * d = (Dimension *)obj;
383
384                         Vector v(d->p[0], d->p[1]);
385                         double angle = v.Angle();
386                         Vector unit = v.Unit();
387                         Vector linePt1 = d->p[0], linePt2 = d->p[1];
388                         Vector ortho;
389                         double x1, y1, length;
390
391                         if (d->subtype == DTLinearVert)
392                         {
393                                 if ((angle < 0) || (angle > PI))
394                                 {
395                                         x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
396                                         y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
397                                         ortho = Vector(1.0, 0);
398                                         angle = PI3_OVER_2;
399                                 }
400                                 else
401                                 {
402                                         x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
403                                         y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
404                                         ortho = Vector(-1.0, 0);
405                                         angle = PI_OVER_2;
406                                 }
407
408                                 linePt1.x = linePt2.x = x1;
409                                 length = fabs(d->p[0].y - d->p[1].y);
410                         }
411                         else if (d->subtype == DTLinearHorz)
412                         {
413                                 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
414                                 {
415                                         x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
416                                         y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
417                                         ortho = Vector(0, 1.0);
418                                         angle = 0;
419                                 }
420                                 else
421                                 {
422                                         x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
423                                         y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
424                                         ortho = Vector(0, -1.0);
425                                         angle = PI;
426                                 }
427
428                                 linePt1.y = linePt2.y = y1;
429                                 length = fabs(d->p[0].x - d->p[1].x);
430                         }
431                         else if (d->subtype == DTLinear)
432                         {
433                                 angle = Vector(linePt1, linePt2).Angle();
434                                 ortho = Vector::Normal(linePt1, linePt2);
435                                 length = v.Magnitude();
436                         }
437
438                         unit = Vector(linePt1, linePt2).Unit();
439
440                         Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
441                         Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
442                         Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
443                         Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
444                         Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
445                         Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
446
447                 /*
448                 The numbers hardcoded into here, what are they?
449                 I believe they are pixels.
450                 */
451                         // Draw extension lines (if certain type)
452                         painter->DrawLine(p3, p5);
453                         painter->DrawLine(p4, p6);
454
455                         // Calculate whether or not the arrowheads are too crowded to put inside
456                         // the extension lines. 9.0 is the length of the arrowhead.
457                         double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
458                 //printf("Dimension::Draw(): t = %lf\n", t);
459
460                         // On the screen, it's acting like this is actually 58%...
461                         // This is correct, we want it to happen at > 50%
462                         if (t > 0.58)
463                         {
464                                 // Draw main dimension line + arrowheads
465                                 painter->DrawLine(p1, p2);
466                                 painter->DrawArrowhead(p1, p2, scaledThickness);
467                                 painter->DrawArrowhead(p2, p1, scaledThickness);
468                         }
469                         else
470                         {
471                                 // Draw outside arrowheads
472                                 Point p7 = p1 - (unit * 9.0 * scaledThickness);
473                                 Point p8 = p2 + (unit * 9.0 * scaledThickness);
474                                 painter->DrawArrowhead(p1, p7, scaledThickness);
475                                 painter->DrawArrowhead(p2, p8, scaledThickness);
476                                 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
477                                 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
478                         }
479
480                         // Draw length of dimension line...
481                         painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
482                         Point ctr = p2 + (Vector(p2, p1) / 2.0);
483
484                 #if 0
485                         QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
486                 #else
487                         QString dimText;
488
489                         if (length < 12.0)
490                                 dimText = QString("%1\"").arg(length);
491                         else
492                         {
493                                 double feet = (double)((int)length / 12);
494                                 double inches = length - (feet * 12.0);
495
496                                 if (inches == 0)
497                                         dimText = QString("%1'").arg(feet);
498                                 else
499                                         dimText = QString("%1' %2\"").arg(feet).arg(inches);
500                         }
501                 #endif
502
503                         painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
504
505                         break;
506                 }
507                 case OTText:
508                 {
509                         Text * t = (Text *)obj;
510                         painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
511                         break;
512                 }
513                 default:
514                         break;
515                 }
516         }
517 }
518
519
520 void DrawingView::AddHoveredToSelection(void)
521 {
522         std::vector<void *>::iterator i;
523
524         for(i=document.objects.begin(); i!=document.objects.end(); i++)
525         {
526                 if (((Object *)(*i))->hovered)
527                         ((Object *)(*i))->selected = true;
528         }
529 }
530
531
532 void DrawingView::GetSelection(std::vector<void *> & v)
533 {
534         v.clear();
535         std::vector<void *>::iterator i;
536
537         for(i=document.objects.begin(); i!=document.objects.end(); i++)
538         {
539                 if (((Object *)(*i))->selected)
540                         v.push_back(*i);
541         }
542 }
543
544
545 void DrawingView::GetHovered(std::vector<void *> & v)
546 {
547         v.clear();
548         std::vector<void *>::iterator i;
549
550         for(i=document.objects.begin(); i!=document.objects.end(); i++)
551         {
552                 if (((Object *)(*i))->hovered)
553 //              {
554 //printf("GetHovered: adding object (%X) to hover... hp1=%s, hp2=%s, hl=%s\n", (*i), (((Line *)(*i))->hitPoint[0] ? "true" : "false"), (((Line *)(*i))->hitPoint[1] ? "true" : "false"), (((Line *)(*i))->hitObject ? "true" : "false"));
555                         v.push_back(*i);
556 //              }
557         }
558 }
559
560
561 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
562 {
563         Global::screenSize = Vector(size().width(), size().height());
564         UpdateGridBackground();
565 }
566
567
568 void DrawingView::ToolHandler(int mode, Point p)
569 {
570         if (Global::tool == TTLine)
571                 LineHandler(mode, p);
572         else if (Global::tool == TTCircle)
573                 CircleHandler(mode, p);
574         else if (Global::tool == TTRotate)
575                 RotateHandler(mode, p);
576 }
577
578
579 void DrawingView::ToolDraw(Painter * painter)
580 {
581         if (Global::tool == TTLine)
582         {
583                 if (Global::toolState == TSNone)
584                 {
585                         painter->DrawHandle(toolPoint[0]);
586                 }
587                 else if ((Global::toolState == TSPoint2) && shiftDown)
588                 {
589                         painter->DrawHandle(toolPoint[1]);
590                 }
591                 else
592                 {
593                         painter->DrawLine(toolPoint[0], toolPoint[1]);
594                         painter->DrawHandle(toolPoint[1]);
595
596                         Vector v(toolPoint[0], toolPoint[1]);
597                         double absAngle = v.Angle() * RADIANS_TO_DEGREES;
598                         double absLength = v.Magnitude();
599                         QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
600                         informativeText = text.arg(absLength).arg(absAngle);
601                 }
602         }
603         else if (Global::tool == TTCircle)
604         {
605                 if (Global::toolState == TSNone)
606                 {
607                         painter->DrawHandle(toolPoint[0]);
608                 }
609                 else if ((Global::toolState == TSPoint2) && shiftDown)
610                 {
611                         painter->DrawHandle(toolPoint[1]);
612                 }
613                 else
614                 {
615                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
616 //                      painter->DrawLine(toolPoint[0], toolPoint[1]);
617 //                      painter->DrawHandle(toolPoint[1]);
618                         painter->SetBrush(QBrush(Qt::NoBrush));
619                         painter->DrawEllipse(toolPoint[0], length, length);
620                         QString text = tr("Radius: %1 in.");//\n") + QChar(0x2221) + tr(": %2");
621                         informativeText = text.arg(length);//.arg(absAngle);
622                 }
623         }
624         else if (Global::tool == TTRotate)
625         {
626                 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
627                         painter->DrawHandle(toolPoint[0]);
628                 else if ((Global::toolState == TSPoint2) && shiftDown)
629                         painter->DrawHandle(toolPoint[1]);
630                 else
631                 {
632                         if (toolPoint[0] == toolPoint[1])
633                                 return;
634                         
635                         painter->DrawLine(toolPoint[0], toolPoint[1]);
636                         // Likely we need a tool container for this... (now we do!)
637 #if 0
638                         if (ctrlDown)
639                         {
640                                 painter->SetPen(0x00FF00, 2.0, LSSolid);
641                                 overrideColor = true;
642                         }
643
644                         RenderObjects(painter, toolObjects);
645                         overrideColor = false;
646 #endif
647
648                         double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
649
650                         QString text = QChar(0x2221) + QObject::tr(": %1");
651                         informativeText = text.arg(absAngle);
652
653                         if (ctrlDown)
654                                 informativeText += " (Copy)";
655
656 //                      painter->DrawInformativeText(text);
657                 }
658         }
659 }
660
661
662 void DrawingView::LineHandler(int mode, Point p)
663 {
664         switch (mode)
665         {
666         case ToolMouseDown:
667                 if (Global::toolState == TSNone)
668                         toolPoint[0] = p;
669                 else
670                         toolPoint[1] = p;
671
672                 break;
673         case ToolMouseMove:
674                 if (Global::toolState == TSNone)
675                         toolPoint[0] = p;
676                 else
677                         toolPoint[1] = p;
678
679                 break;
680         case ToolMouseUp:
681                 if (Global::toolState == TSNone)
682                 {
683                         Global::toolState = TSPoint2;
684                         // Prevent spurious line from drawing...
685                         toolPoint[1] = toolPoint[0];
686                 }
687                 else if ((Global::toolState == TSPoint2) && shiftDown)
688                 {
689                         // Key override is telling us to make a new line, not continue the
690                         // previous one.
691                         toolPoint[0] = toolPoint[1];
692                 }
693                 else
694                 {
695                         Line * l = new Line(toolPoint[0], toolPoint[1]);
696                         document.objects.push_back(l);
697                         toolPoint[0] = toolPoint[1];
698                 }
699         }
700 }
701
702
703 void DrawingView::CircleHandler(int mode, Point p)
704 {
705         switch (mode)
706         {
707         case ToolMouseDown:
708                 if (Global::toolState == TSNone)
709                         toolPoint[0] = p;
710                 else
711                         toolPoint[1] = p;
712
713                 break;
714         case ToolMouseMove:
715                 if (Global::toolState == TSNone)
716                         toolPoint[0] = p;
717                 else
718                         toolPoint[1] = p;
719
720                 break;
721         case ToolMouseUp:
722                 if (Global::toolState == TSNone)
723                 {
724                         Global::toolState = TSPoint2;
725                         // Prevent spurious line from drawing...
726                         toolPoint[1] = toolPoint[0];
727                 }
728                 else if ((Global::toolState == TSPoint2) && shiftDown)
729                 {
730                         // Key override is telling us to make a new line, not continue the
731                         // previous one.
732                         toolPoint[0] = toolPoint[1];
733                 }
734                 else
735                 {
736                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
737                         Circle * c = new Circle(toolPoint[0], length);
738                         document.objects.push_back(c);
739                         toolPoint[0] = toolPoint[1];
740                         Global::toolState = TSNone;
741                 }
742         }
743 }
744
745
746 void DrawingView::RotateHandler(int mode, Point p)
747 {
748         switch (mode)
749         {
750         case ToolMouseDown:
751                 if (Global::toolState == TSNone)
752                 {
753                         toolPoint[0] = p;
754                         SavePointsFrom(select, toolScratch);
755                         Global::toolState = TSPoint1;
756                 }
757                 else if (Global::toolState == TSPoint1)
758                         toolPoint[0] = p;
759                 else
760                         toolPoint[1] = p;
761
762                 break;
763         case ToolMouseMove:
764                 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
765                         toolPoint[0] = p;
766                 else if (Global::toolState == TSPoint2)
767                 {
768                         toolPoint[1] = p;
769
770                         if (shiftDown)
771                                 return;
772
773                         double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
774                         std::vector<void *>::iterator j = select.begin();
775                         std::vector<Object>::iterator i = toolScratch.begin();
776
777                         for(; i!=toolScratch.end(); i++, j++)
778                         {
779                                 Object obj = *i;
780                                 Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
781                                 Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
782                                 Object * obj2 = (Object *)(*j);
783                                 obj2->p[0] = p1;
784                                 obj2->p[1] = p2;
785
786                                 if (obj.type == OTArc)
787                                 {
788                                         obj2->angle[0] = obj.angle[0] + angle;
789
790                                         if (obj2->angle[0] > PI_TIMES_2)
791                                                 obj2->angle[0] -= PI_TIMES_2;
792                                 }
793                         }
794                 }
795
796                 break;
797         case ToolMouseUp:
798                 if (Global::toolState == TSPoint1)
799                 {
800                         Global::toolState = TSPoint2;
801                         // Prevent spurious line from drawing...
802                         toolPoint[1] = toolPoint[0];
803                 }
804                 else if ((Global::toolState == TSPoint2) && shiftDown)
805                 {
806                         // Key override is telling us to make a new line, not continue the
807                         // previous one.
808                         toolPoint[0] = toolPoint[1];
809                 }
810                 else
811                 {
812                         // Either we're finished with our rotate, or we're stamping a copy.
813                         if (ctrlDown)
814                         {
815                                 // Stamp a copy of the selection at the current rotation & bail
816                                 std::vector<void *> temp;
817                                 CopyObjects(select, temp);
818                                 ClearSelected(temp);
819                                 AddObjectsTo(document.objects, temp);
820                                 RestorePointsTo(select, toolScratch);
821                                 return;
822                         }
823
824                         toolPoint[0] = p;
825                         Global::toolState = TSPoint1;
826                         SavePointsFrom(select, toolScratch);
827                 }
828
829                 break;
830         case ToolKeyDown:
831                 // Reset the selection if shift held down...
832                 if (shiftDown)
833                         RestorePointsTo(select, toolScratch);
834
835                 break;
836         case ToolKeyUp:
837                 // Reset selection when key is let up
838                 if (!shiftDown)
839                         RotateHandler(ToolMouseMove, toolPoint[1]);
840
841                 break;
842         case ToolCleanup:
843                 RestorePointsTo(select, toolScratch);
844         }
845 }
846
847
848 void DrawingView::mousePressEvent(QMouseEvent * event)
849 {
850         if (event->button() == Qt::LeftButton)
851         {
852                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
853
854                 // Handle tool processing, if any
855                 if (Global::tool)
856                 {
857                         if (Global::snapToGrid)
858                                 point = SnapPointToGrid(point);
859
860                         //Also, may want to figure out if hovering over a snap point on an object,
861                         //snap to grid if not.
862                         // Snap to object point if valid...
863 //                      if (Global::snapPointIsValid)
864 //                              point = Global::snapPoint;
865                         
866                         ToolHandler(ToolMouseDown, point);
867                         return;
868                 }
869
870                 // Clear the selection only if CTRL isn't being held on click
871                 if (!ctrlDown)
872                         ClearSelected(document.objects);
873 //                      ClearSelection();
874
875                 // If any objects are being hovered on click, add them to the selection
876                 // & return
877                 if (numHovered > 0)
878                 {
879                         AddHoveredToSelection();
880                         update();       // needed??
881                         GetHovered(hover);      // prolly needed
882
883                         // Needed for grab & moving objects
884                         // We do it *after*... why? (doesn't seem to confer any advantage...)
885                         if (Global::snapToGrid)
886                                 oldPoint = SnapPointToGrid(point);
887
888                         return;
889                 }
890
891                 // Didn't hit any object and not using a tool, so do a selection rectangle
892                 Global::selectionInProgress = true;
893                 Global::selection.setTopLeft(QPointF(point.x, point.y));
894                 Global::selection.setBottomRight(QPointF(point.x, point.y));
895         }
896         else if (event->button() == Qt::MiddleButton)
897         {
898                 scrollDrag = true;
899                 oldPoint = Vector(event->x(), event->y());
900                 // Should also change the mouse pointer as well...
901                 setCursor(Qt::SizeAllCursor);
902         }
903 }
904
905
906 void DrawingView::mouseMoveEvent(QMouseEvent * event)
907 {
908         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
909         Global::selection.setBottomRight(QPointF(point.x, point.y));
910         // Only needs to be done here, as mouse down is always preceded by movement
911         Global::snapPointIsValid = false;
912
913         // Scrolling...
914         if (event->buttons() & Qt::MiddleButton)
915         {
916                 point = Vector(event->x(), event->y());
917                 // Since we're using Qt coords for scrolling, we have to adjust them here to
918                 // conform to Cartesian coords, since the origin is using Cartesian. :-)
919                 Vector delta(oldPoint, point);
920                 delta /= Global::zoom;
921                 delta.y = -delta.y;
922                 Global::origin -= delta;
923
924                 UpdateGridBackground();
925                 update();
926                 oldPoint = point;
927                 return;
928         }
929
930         // If we're doing a selection rect, see if any objects are engulfed by it
931         // (implies left mouse button held down)
932         if (Global::selectionInProgress)
933         {
934                 CheckObjectBounds();
935                 update();
936                 return;
937         }
938
939         // Handle object movement (left button down & over an object)
940         if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
941         {
942                 if (Global::snapToGrid)
943                         point = SnapPointToGrid(point);
944
945                 HandleObjectMovement(point);
946                 update();
947                 oldPoint = point;
948                 return;
949         }
950
951         // Do object hit testing...
952         bool needUpdate = HitTestObjects(point);
953
954         // Do tool handling, if any are active...
955         if (Global::tool)
956         {
957                 if (Global::snapToGrid)
958                         point = SnapPointToGrid(point);
959
960                 ToolHandler(ToolMouseMove, point);
961         }
962
963         // This is used to draw the tool crosshair...
964         oldPoint = point;
965
966         if (needUpdate || Global::tool)
967                 update();
968 }
969
970
971 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
972 {
973         if (event->button() == Qt::LeftButton)
974         {
975 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
976 //could set it up to use the document's update function (assumes that all object updates
977 //are being reported correctly:
978 //              if (document.NeedsUpdate())
979                 // Do an update if collided with at least *one* object in the document
980 //              if (collided)
981                         update();
982
983                 if (Global::tool)
984                 {
985                         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
986                         ToolHandler(ToolMouseUp, point);
987                         return;
988                 }
989
990                 if (Global::selectionInProgress)
991                         Global::selectionInProgress = false;
992
993                 informativeText.clear();
994 // Should we be doing this automagically? Hmm...
995                 // Clear our vectors
996                 select.clear();
997                 hover.clear();
998
999                 // Scoop 'em up
1000                 std::vector<void *>::iterator i;
1001
1002                 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1003                 {
1004                         if (((Object *)(*i))->selected)
1005                                 select.push_back(*i);
1006
1007 //hmm, this is no good, too late to do any good :-P
1008 //                      if ((*i)->hovered)
1009 //                              hover.push_back(*i);
1010                 }
1011         }
1012         else if (event->button() == Qt::MiddleButton)
1013         {
1014                 scrollDrag = false;
1015                 setCursor(Qt::ArrowCursor);
1016         }
1017 }
1018
1019
1020 void DrawingView::wheelEvent(QWheelEvent * event)
1021 {
1022         double zoomFactor = 1.25;
1023         QSize sizeWin = size();
1024         Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1025         center = Painter::QtToCartesianCoords(center);
1026
1027         // This is not centering for some reason. Need to figure out why. :-/
1028         if (event->delta() > 0)
1029         {
1030                 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1031                 Global::origin = newOrigin;
1032                 Global::zoom *= zoomFactor;
1033         }
1034         else
1035         {
1036                 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1037                 Global::origin = newOrigin;
1038                 Global::zoom /= zoomFactor;
1039         }
1040
1041 //      Global::gridSpacing = gridPixels / Painter::zoom;
1042 //      UpdateGridBackground();
1043         SetGridSize(Global::gridSpacing * Global::zoom);
1044         update();
1045 //      zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1046 }
1047
1048
1049 void DrawingView::keyPressEvent(QKeyEvent * event)
1050 {
1051         bool oldShift = shiftDown;
1052         bool oldCtrl = ctrlDown;
1053
1054         if (event->key() == Qt::Key_Shift)
1055                 shiftDown = true;
1056         else if (event->key() == Qt::Key_Control)
1057                 ctrlDown = true;
1058
1059         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1060         {
1061                 if (Global::tool)
1062                         ToolHandler(ToolKeyDown, Point(0, 0));
1063
1064                 update();
1065         }
1066 }
1067
1068
1069 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1070 {
1071         bool oldShift = shiftDown;
1072         bool oldCtrl = ctrlDown;
1073
1074         if (event->key() == Qt::Key_Shift)
1075                 shiftDown = false;
1076         else if (event->key() == Qt::Key_Control)
1077                 ctrlDown = false;
1078
1079         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1080         {
1081                 if (Global::tool)
1082                         ToolHandler(ToolKeyUp, Point(0, 0));
1083
1084                 update();
1085         }
1086 }
1087
1088
1089 //
1090 // This looks strange, but it's really quite simple: We want a point that's
1091 // more than half-way to the next grid point to snap there while conversely we
1092 // want a point that's less than half-way to to the next grid point then snap
1093 // to the one before it. So we add half of the grid spacing to the point, then
1094 // divide by it so that we can remove the fractional part, then multiply it
1095 // back to get back to the correct answer.
1096 //
1097 Point DrawingView::SnapPointToGrid(Point point)
1098 {
1099         point += Global::gridSpacing / 2.0;             // *This* adds to Z!!!
1100         point /= Global::gridSpacing;
1101         point.x = floor(point.x);//need to fix this for negative numbers...
1102         point.y = floor(point.y);
1103         point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
1104         point *= Global::gridSpacing;
1105         return point;
1106 }
1107
1108
1109 void DrawingView::CheckObjectBounds(void)
1110 {
1111         std::vector<void *>::iterator i;
1112         numSelected = 0;
1113
1114         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1115         {
1116                 Object * obj = (Object *)(*i);
1117                 obj->selected = false;
1118
1119                 switch (obj->type)
1120                 {
1121                 case OTLine:
1122                 {
1123                         Line * l = (Line *)obj;
1124
1125                         if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1126                                 l->selected = true;
1127
1128                         break;
1129                 }
1130                 case OTCircle:
1131                 {
1132                         Circle * c = (Circle *)obj;
1133
1134                         if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0]))
1135                                 c->selected = true;
1136
1137                         break;
1138                 }
1139                 case OTArc:
1140                 {
1141                         Arc * a = (Arc *)obj;
1142
1143                         double start = a->angle[0];
1144                         double end = start + a->angle[1];
1145                         QPointF p1(cos(start), sin(start));
1146                         QPointF p2(cos(end), sin(end));
1147                         QRectF bounds(p1, p2);
1148
1149 #if 1
1150                         // Swap X/Y coordinates if they're backwards...
1151                         if (bounds.left() > bounds.right())
1152                         {
1153                                 double temp = bounds.left();
1154                                 bounds.setLeft(bounds.right());
1155                                 bounds.setRight(temp);
1156                         }
1157
1158                         if (bounds.bottom() > bounds.top())
1159                         {
1160                                 double temp = bounds.bottom();
1161                                 bounds.setBottom(bounds.top());
1162                                 bounds.setTop(temp);
1163                         }
1164 #else
1165                         // Doesn't work as advertised! For shame!
1166                         bounds = bounds.normalized();
1167 #endif
1168
1169                         // If the end of the arc is before the beginning, add 360 degrees to it
1170                         if (end < start)
1171                                 end += 2.0 * PI;
1172
1173                         // Adjust the bounds depending on which axes are crossed
1174                         if ((start < PI_OVER_2) && (end > PI_OVER_2))
1175                                 bounds.setTop(1.0);
1176
1177                         if ((start < PI) && (end > PI))
1178                                 bounds.setLeft(-1.0);
1179
1180                         if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
1181                                 bounds.setBottom(-1.0);
1182
1183                         if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
1184                                 bounds.setRight(1.0);
1185
1186                         if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
1187                                 bounds.setTop(1.0);
1188
1189                         if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
1190                                 bounds.setLeft(-1.0);
1191
1192                         if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
1193                                 bounds.setBottom(-1.0);
1194
1195                         bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1196                         bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1197                         bounds.translate(a->p[0].x, a->p[0].y);
1198
1199                         if (Global::selection.contains(bounds))
1200                                 a->selected = true;
1201
1202                         break;
1203                 }
1204                 default:
1205                         break;
1206                 }
1207
1208                 if (obj->selected)
1209                         numSelected++;
1210         }
1211 }
1212
1213
1214 bool DrawingView::HitTestObjects(Point point)
1215 {
1216         std::vector<void *>::iterator i;
1217         numHovered = 0;
1218         bool needUpdate = false;
1219
1220         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1221         {
1222                 Object * obj = (Object *)(*i);
1223
1224                 switch (obj->type)
1225                 {
1226                 case OTLine:
1227                 {
1228                         bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1229                         obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1230                         Vector lineSegment = obj->p[1] - obj->p[0];
1231                         Vector v1 = point - obj->p[0];
1232                         Vector v2 = point - obj->p[1];
1233                         double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1234                         double distance;
1235
1236                         if (t < 0.0)
1237                                 distance = v1.Magnitude();
1238                         else if (t > 1.0)
1239                                 distance = v2.Magnitude();
1240                         else
1241                                 // distance = ?Det?(ls, v1) / |ls|
1242                                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1243                                         / lineSegment.Magnitude());
1244
1245                         if ((v1.Magnitude() * Global::zoom) < 8.0)
1246                                 obj->hitPoint[0] = true;
1247                         else if ((v2.Magnitude() * Global::zoom) < 8.0)
1248                                 obj->hitPoint[1] = true;
1249                         else if ((distance * Global::zoom) < 5.0)
1250                                 obj->hitObject = true;
1251
1252                         obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1253
1254                         if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1255                                 needUpdate = true;
1256
1257                         break;
1258                 }
1259                 case OTCircle:
1260                 {
1261                         bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1262                         obj->hitPoint[0] = obj->hitObject = false;
1263                         double length = Vector::Magnitude(obj->p[0], point);
1264
1265                         if ((length * Global::zoom) < 8.0)
1266                                 obj->hitPoint[0] = true;
1267                         else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1268                                 obj->hitObject = true;
1269
1270                         obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1271
1272                         if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1273                                 needUpdate = true;
1274
1275                         break;
1276                 }
1277                 default:
1278                         break;
1279                 }
1280
1281                 if (obj->hovered)
1282 //              {
1283                         numHovered++;
1284 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1285 //              }
1286         }
1287
1288         return needUpdate;
1289 }
1290
1291
1292 void DrawingView::HandleObjectMovement(Point point)
1293 {
1294         Point delta = point - oldPoint;
1295 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
1296         Object * obj = (Object *)hover[0];
1297 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
1298 //printf("Object (%X) move: hp1=%s, hp2=%s, hl=%s\n", obj, (obj->hitPoint[0] ? "true" : "false"), (obj->hitPoint[1] ? "true" : "false"), (obj->hitObject ? "true" : "false"));
1299
1300         switch (obj->type)
1301         {
1302         case OTLine:
1303                 if (obj->hitPoint[0])
1304                         obj->p[0] = point;
1305                 else if (obj->hitPoint[1])
1306                         obj->p[1] = point;
1307                 else if (obj->hitObject)
1308                 {
1309                         obj->p[0] += delta;
1310                         obj->p[1] += delta;
1311                 }
1312
1313                 break;
1314         case OTCircle:
1315                 if (obj->hitPoint[0])
1316                         obj->p[0] = point;
1317                 else if (obj->hitObject)
1318                 {
1319 //this doesn't work. we need to save this on mouse down for this to work correctly!
1320 //                      double oldRadius = obj->radius[0];
1321                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1322
1323                         QString text = QObject::tr("Radius: %1");//\nScale: %2%");
1324                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
1325                 }
1326
1327                 break;
1328         default:
1329                 break;
1330         }
1331 }
1332
1333
1334
1335 #if 0
1336         // This returns true if we've moved over an object...
1337         if (document.PointerMoved(point)) // <-- This
1338         // This is where the object would do automagic dragging & shit. Since we don't
1339         // do that anymore, we need a strategy to handle it.
1340         {
1341
1342 /*
1343 Now objects handle mouse move snapping as well. The code below mainly works only
1344 for tools; we need to fix it so that objects work as well...
1345
1346 There's a problem with the object point snapping in that it's dependent on the
1347 order of the objects in the document. Most likely this is because it counts the
1348 selected object last and thus fucks up the algorithm. Need to fix this...
1349
1350
1351 */
1352                 // Do object snapping here. Grid snapping on mouse down is done in the
1353                 // objects themselves, only because we have to hit test the raw point,
1354                 // not the snapped point. There has to be a better way...!
1355                 if (document.penultimateObjectHovered)
1356                 {
1357                         // Two objects are hovered, see if we have an intersection point
1358                         if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
1359                         {
1360                                 double t;
1361                                 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
1362
1363                                 if (n == 1)
1364                                 {
1365                                         Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
1366                                         Global::snapPointIsValid = true;
1367                                 }
1368                         }
1369                         else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
1370                         {
1371                                 Point p1, p2;
1372                                 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
1373
1374                                 if (n == 1)
1375                                 {
1376                                         Global::snapPoint = p1;
1377                                         Global::snapPointIsValid = true;
1378                                 }
1379                                 else if (n == 2)
1380                                 {
1381                                         double d1 = Vector(point, p1).Magnitude();
1382                                         double d2 = Vector(point, p2).Magnitude();
1383
1384                                         if (d1 < d2)
1385                                                 Global::snapPoint = p1;
1386                                         else
1387                                                 Global::snapPoint = p2;
1388
1389                                         Global::snapPointIsValid = true;
1390                                 }
1391                         }
1392                 }
1393 //              else
1394 //              {
1395                         // Otherwise, it was a single object hovered...
1396 //              }
1397         }
1398
1399         if (toolAction)
1400         {
1401                 if (Global::snapToGrid)
1402                         point = Global::SnapPointToGrid(point);
1403
1404                 // We always snap to object points, and they take precendence over
1405                 // grid points...
1406                 if (Global::snapPointIsValid)
1407                         point = Global::snapPoint;
1408
1409                 toolAction->MouseMoved(point);
1410         }
1411 #else
1412 #endif
1413