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