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