]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
Lines respond to mouse movement, added Text rendering.
[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),
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), "Tyegxt!"));
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
515                         for(float angle=0; angle<(PI*3.0/2.0); angle+=PI/8.0)
516                                 painter->DrawTextObject(t->p1, t->s.c_str(), scaledThickness, angle);
517
518                         break;
519                 }
520                 default:
521                         break;
522                 }
523         }
524 }
525
526
527 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
528 {
529         Global::screenSize = Vector(size().width(), size().height());
530         UpdateGridBackground();
531 }
532
533
534 void DrawingView::mousePressEvent(QMouseEvent * event)
535 {
536         if (event->button() == Qt::LeftButton)
537         {
538                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
539 //              collided = document.Collided(point);
540                 collided = false;
541
542                 // Do an update if collided with at least *one* object in the document
543 //              if (collided)
544 //                      update();
545
546 #if 0
547                 if (toolAction)
548                 {
549                         if (Global::snapToGrid)
550                                 point = Global::SnapPointToGrid(point);
551
552                         // We always snap to object points, and they take precendence over
553                         // grid points...
554                         if (Global::snapPointIsValid)
555                                 point = Global::snapPoint;
556
557                         toolAction->MouseDown(point);
558                 }
559 #endif
560
561 #if 1
562                 // Didn't hit any object and not using a tool, so do a selection rectangle
563                 if (!(collided))// || toolAction))
564                 {
565                         Global::selectionInProgress = true;
566                         Global::selection.setTopLeft(QPointF(point.x, point.y));
567                         Global::selection.setBottomRight(QPointF(point.x, point.y));
568                 }
569 #endif
570         }
571         else if (event->button() == Qt::MiddleButton)
572         {
573                 scrollDrag = true;
574                 oldPoint = Vector(event->x(), event->y());
575                 // Should also change the mouse pointer as well...
576                 setCursor(Qt::SizeAllCursor);
577         }
578 }
579
580
581 void DrawingView::mouseMoveEvent(QMouseEvent * event)
582 {
583         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
584         Global::selection.setBottomRight(QPointF(point.x, point.y));
585         // Only needs to be done here, as mouse down is always preceded by movement
586         Global::snapPointIsValid = false;
587
588         // Scrolling...
589         if (event->buttons() & Qt::MiddleButton)
590         {
591                 point = Vector(event->x(), event->y());
592                 // Since we're using Qt coords for scrolling, we have to adjust them here to
593                 // conform to Cartesian coords, since the origin is using Cartesian. :-)
594                 Vector delta(oldPoint, point);
595                 delta /= Global::zoom;
596                 delta.y = -delta.y;
597                 Global::origin -= delta;
598
599                 UpdateGridBackground();
600                 update();
601                 oldPoint = point;
602                 return;
603         }
604
605 #if 1
606         // Grid processing... (only snap here is left button is down)
607         if ((event->buttons() & Qt::LeftButton) && Global::snapToGrid)
608         {
609                 point = SnapPointToGrid(point);
610         }
611
612         // Snap points on objects always take precedence over the grid, whether
613         // dragging an object or not...
614 //thisnowok
615         if (Global::snapPointIsValid)
616         {
617 // Uncommenting this causes the cursor to become unresponsive after the first
618 // object is added.
619 //              point = Global::snapPoint;
620         }
621 #endif
622
623         // Do checking here to see if object can be selected or not
624         if (Global::selectionInProgress)
625         {
626                 std::vector<void *>::iterator i;
627 //              QRectF bounds;
628
629                 for(i=document.objects.begin(); i!=document.objects.end(); i++)
630                 {
631                         Object * obj = (Object *)(*i);
632                         obj->selected = false;
633 //                      QRectF extents;
634
635                         switch (obj->type)
636                         {
637                         case OTLine:
638                         {
639                                 Line * l = (Line *)obj;
640
641                                 if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
642                                         l->selected = true;
643
644                                 break;
645                         }
646                         case OTCircle:
647                         {
648                                 Circle * c = (Circle *)obj;
649
650                                 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))
651                                         c->selected = true;
652
653                                 break;
654                         }
655                         case OTArc:
656                         {
657                                 Arc * a = (Arc *)obj;
658
659         double start = a->angle1;
660         double end = start + a->angle2;
661         QPointF p1(cos(start), sin(start));
662         QPointF p2(cos(end), sin(end));
663         QRectF bounds(p1, p2);
664
665 #if 1
666         // Swap X/Y coordinates if they're backwards...
667         if (bounds.left() > bounds.right())
668         {
669                 double temp = bounds.left();
670                 bounds.setLeft(bounds.right());
671                 bounds.setRight(temp);
672         }
673
674         if (bounds.bottom() > bounds.top())
675         {
676                 double temp = bounds.bottom();
677                 bounds.setBottom(bounds.top());
678                 bounds.setTop(temp);
679         }
680 #else
681         // Doesn't work as advertised! For shame!
682         bounds = bounds.normalized();
683 #endif
684
685         // If the end of the arc is before the beginning, add 360 degrees to it
686         if (end < start)
687                 end += 2.0 * PI;
688
689         // Adjust the bounds depending on which axes are crossed
690         if ((start < PI_OVER_2) && (end > PI_OVER_2))
691                 bounds.setTop(1.0);
692
693         if ((start < PI) && (end > PI))
694                 bounds.setLeft(-1.0);
695
696         if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
697                 bounds.setBottom(-1.0);
698
699         if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
700                 bounds.setRight(1.0);
701
702         if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
703                 bounds.setTop(1.0);
704
705         if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
706                 bounds.setLeft(-1.0);
707
708         if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
709                 bounds.setBottom(-1.0);
710
711         bounds.setTopLeft(QPointF(bounds.left() * a->radius, bounds.top() * a->radius));
712         bounds.setBottomRight(QPointF(bounds.right() * a->radius, bounds.bottom() * a->radius));
713         bounds.translate(a->p1.x, a->p1.y);
714
715                                 if (Global::selection.contains(bounds))
716                                         a->selected = true;
717
718                                 break;
719                         }
720                         default:
721                                 break;
722                         }
723                 }
724         }
725
726 //      oldPoint = point;
727 //we should keep track of the last point here and only pass this down *if* the point
728 //changed...
729
730 #if 0
731         // This returns true if we've moved over an object...
732         if (document.PointerMoved(point))
733         {
734 /*
735 Now objects handle mouse move snapping as well. The code below mainly works only
736 for tools; we need to fix it so that objects work as well...
737
738 There's a problem with the object point snapping in that it's dependent on the
739 order of the objects in the document. Most likely this is because it counts the
740 selected object last and thus fucks up the algorithm. Need to fix this...
741
742
743 */
744                 // Do object snapping here. Grid snapping on mouse down is done in the
745                 // objects themselves, only because we have to hit test the raw point,
746                 // not the snapped point. There has to be a better way...!
747                 if (document.penultimateObjectHovered)
748                 {
749                         // Two objects are hovered, see if we have an intersection point
750                         if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
751                         {
752                                 double t;
753                                 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
754
755                                 if (n == 1)
756                                 {
757                                         Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
758                                         Global::snapPointIsValid = true;
759                                 }
760                         }
761                         else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
762                         {
763                                 Point p1, p2;
764                                 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
765
766                                 if (n == 1)
767                                 {
768                                         Global::snapPoint = p1;
769                                         Global::snapPointIsValid = true;
770                                 }
771                                 else if (n == 2)
772                                 {
773                                         double d1 = Vector(point, p1).Magnitude();
774                                         double d2 = Vector(point, p2).Magnitude();
775
776                                         if (d1 < d2)
777                                                 Global::snapPoint = p1;
778                                         else
779                                                 Global::snapPoint = p2;
780
781                                         Global::snapPointIsValid = true;
782                                 }
783                         }
784                 }
785 //              else
786 //              {
787                         // Otherwise, it was a single object hovered...
788 //              }
789         }
790
791         if (toolAction)
792         {
793                 if (Global::snapToGrid)
794                         point = Global::SnapPointToGrid(point);
795
796                 // We always snap to object points, and they take precendence over
797                 // grid points...
798                 if (Global::snapPointIsValid)
799                         point = Global::snapPoint;
800
801                 toolAction->MouseMoved(point);
802         }
803 #endif
804         bool needUpdate = false;
805
806         // Don't do this kind of checking unless we're not doing a selection rectangle!
807         // Hmm, lines don't stay selected if globally selected... !!! FIX !!!
808         // it's because there were extra state variables, the hit* vars...
809         if (!Global::selectionInProgress)
810         {
811         std::vector<void *>::iterator i;
812         int numHovered = 0;
813
814         for(i=document.objects.begin(); i!=document.objects.end(); i++)
815         {
816                 Object * obj = (Object *)(*i);
817 //              obj->selected = false;
818
819                 switch (obj->type)
820                 {
821                 case OTLine:
822                 {
823                         Line * l = (Line *)obj;
824
825 //      bool hitPoint1, hitPoint2, hitLine;
826         l->hitPoint[0] = l->hitPoint[1] = l->hitObject = false;
827         Vector lineSegment = l->p2 - l->p1;
828         Vector v1 = point - l->p1;
829         Vector v2 = point - l->p2;
830         double t = Geometry::ParameterOfLineAndPoint(l->p1, l->p2, point);
831         double distance;
832
833         if (t < 0.0)
834                 distance = v1.Magnitude();
835         else if (t > 1.0)
836                 distance = v2.Magnitude();
837         else
838                 // distance = ?Det?(ls, v1) / |ls|
839                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
840                         / lineSegment.Magnitude());
841
842         if ((v1.Magnitude() * Global::zoom) < 8.0)
843         {
844                 l->hitPoint[0] = true;
845 //              snapPoint = l->p1;
846 //              snapPointIsValid = true;
847         }
848         else if ((v2.Magnitude() * Global::zoom) < 8.0)
849         {
850                 l->hitPoint[1] = true;
851 //              snapPoint = l->p2;
852 //              snapPointIsValid = true;
853         }
854         else if ((distance * Global::zoom) < 5.0)
855                 l->hitObject = true;
856
857         bool oldHovered = l->hovered;
858         l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
859
860         if (oldHovered != l->hovered)
861                 needUpdate = true;
862
863                         break;
864                 }
865                 case OTCircle:
866                 {
867                         Circle * c = (Circle *)obj;
868
869
870                         break;
871                 }
872                 default:
873                         break;
874                 }
875         }
876         }
877
878         // This is used to draw the tool crosshair...
879         oldPoint = point;
880
881 //      if (/*document.NeedsUpdate() ||*/ Global::selectionInProgress /*|| toolAction*/)
882         if (needUpdate || Global::selectionInProgress)
883                 update();
884 }
885
886
887 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
888 {
889         if (event->button() == Qt::LeftButton)
890         {
891 #if 0
892                 document.PointerReleased();
893 #endif
894
895 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
896 //could set it up to use the document's update function (assumes that all object updates
897 //are being reported correctly:
898 //              if (document.NeedsUpdate())
899 //              if (collided)
900                         update();       // Do an update if collided with at least *one* object in the document
901
902 #if 0
903                 if (toolAction)
904                         toolAction->MouseReleased();
905 #endif
906
907                 if (Global::selectionInProgress)
908                 {
909                         // Select all the stuff inside of selection
910                         Global::selectionInProgress = false;
911                 }
912         }
913         else if (event->button() == Qt::MiddleButton)
914         {
915                 scrollDrag = false;
916                 setCursor(Qt::ArrowCursor);
917         }
918 }
919
920
921 void DrawingView::wheelEvent(QWheelEvent * event)
922 {
923         double zoomFactor = 1.25;
924         QSize sizeWin = size();
925         Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
926         center = Painter::QtToCartesianCoords(center);
927
928         // This is not centering for some reason. Need to figure out why. :-/
929         if (event->delta() > 0)
930         {
931                 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
932                 Global::origin = newOrigin;
933                 Global::zoom *= zoomFactor;
934         }
935         else
936         {
937                 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
938                 Global::origin = newOrigin;
939                 Global::zoom /= zoomFactor;
940         }
941
942 #if 1
943 //      Global::gridSpacing = gridPixels / Painter::zoom;
944 //      UpdateGridBackground();
945         SetGridSize(Global::gridSpacing * Global::zoom);
946         update();
947 //      zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
948 #endif
949 }
950
951
952 void DrawingView::keyPressEvent(QKeyEvent * event)
953 {
954 #if 0
955         if (toolAction)
956                 toolAction->KeyDown(event->key());
957 #endif
958 }
959
960
961 void DrawingView::keyReleaseEvent(QKeyEvent * event)
962 {
963 #if 0
964         if (toolAction)
965                 toolAction->KeyReleased(event->key());
966 #endif
967 }
968
969 //
970 // This looks strange, but it's really quite simple: We want a point that's
971 // more than half-way to the next grid point to snap there while conversely we
972 // want a point that's less than half-way to to the next grid point then snap
973 // to the one before it. So we add half of the grid spacing to the point, then
974 // divide by it so that we can remove the fractional part, then multiply it
975 // back to get back to the correct answer.
976 //
977 Point DrawingView::SnapPointToGrid(Point point)
978 {
979         point += Global::gridSpacing / 2.0;             // *This* adds to Z!!!
980         point /= Global::gridSpacing;
981         point.x = floor(point.x);//need to fix this for negative numbers...
982         point.y = floor(point.y);
983         point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
984         point *= Global::gridSpacing;
985         return point;
986 }
987