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