3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -------------------------------------------------------------
11 // JLH 03/22/2011 Created this file
12 // JLH 09/29/2011 Added middle mouse button panning
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]
25 // Uncomment this for debugging...
27 //#define DEBUGFOO // Various tool debugging...
28 //#define DEBUGTP // Toolpalette debugging...
30 #include "drawingview.h"
35 #include "mathconstants.h"
40 #define BACKGROUND_MAX_SIZE 512
43 //Container DrawingView::document(Vector(0, 0));
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)
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);
58 Global::gridSpacing = 12.0; // In base units (inch is default)
61 Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
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));
70 Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
71 line->SetDimensionOnLine(dimension);
72 document.Add(dimension);
74 // Alternate way to do the above...
75 line->SetDimensionOnLine();
78 Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
79 line->p1 = Vector(5, 5);
80 line->p2 = Vector(50, 40);
82 line->thickness = 2.0;
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!"));
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 :-).
103 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
105 drawing->gridSpacing = 12.0 / Global::zoom;
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.)
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.
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
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.
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.
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
142 SetGridSize(12); // This is in pixels
147 void DrawingView::SetToolActive(Action * action)
152 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
153 SLOT(AddNewObjectToDocument(Object *)));
154 connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
160 void DrawingView::SetGridSize(uint32_t size)
163 if (size == gridPixels)
166 // Recreate the background bitmap
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));
172 for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
174 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
175 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
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();
187 void DrawingView::UpdateGridBackground(void)
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
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
201 The bitmap looks like this:
211 @ x = 1, we want it to look like:
213 -+---+---+---+---+---
216 -+---+---+---+---+---
221 Which means we need to grab the sample from x = 3. @ x = -1:
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.
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
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.
249 x = (gridPixels - (x % gridPixels)) % gridPixels;
254 y = (gridPixels - (y % gridPixels)) % gridPixels;
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);
266 void DrawingView::AddNewObjectToDocument(Object * object)
270 // object->Reparent(&document);
271 // document.Add(object);
274 //printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
278 void DrawingView::HandleActionUpdate(void)
284 void DrawingView::SetCurrentLayer(int layer)
286 Global::currentLayer = layer;
287 //printf("DrawingView::CurrentLayer = %i\n", layer);
291 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
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()));
300 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
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);
309 void DrawingView::paintEvent(QPaintEvent * /*event*/)
311 QPainter qtPainter(this);
312 Painter painter(&qtPainter);
315 qtPainter.setRenderHint(QPainter::Antialiasing);
317 Global::viewportHeight = size().height();
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);
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);
332 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
333 painter.DrawCrosshair(oldPoint);
334 toolAction->Draw(&painter);
339 if (Global::selectionInProgress)
341 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
342 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
343 painter.DrawRect(Global::selection);
349 void DrawingView::RenderObjects(Painter * painter, Container * c)
351 std::vector<void *>::iterator i;
353 for(i=c->objects.begin(); i!=c->objects.end(); i++)
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);
360 if (obj->selected || obj->hovered)
361 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
367 Line * l = (Line *)obj;
368 painter->DrawLine(l->p1, l->p2);
373 Circle * ci = (Circle *)obj;
374 painter->SetBrush(QBrush(Qt::NoBrush));
375 painter->DrawEllipse(ci->p1, ci->radius, ci->radius);
380 Arc * a = (Arc *)obj;
381 painter->DrawArc(a->p1, a->radius, a->angle1, a->angle2);
386 Dimension * d = (Dimension *)obj;
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;
393 double x1, y1, length;
395 if (d->subtype == DTLinearVert)
397 if ((angle < 0) || (angle > PI))
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);
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);
412 linePt1.x = linePt2.x = x1;
413 length = fabs(d->p1.y - d->p2.y);
415 else if (d->subtype == DTLinearHorz)
417 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
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);
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);
432 linePt1.y = linePt2.y = y1;
433 length = fabs(d->p1.x - d->p2.x);
435 else if (d->subtype == DTLinear)
437 angle = Vector(linePt1, linePt2).Angle();
438 ortho = Vector::Normal(linePt1, linePt2);
439 length = v.Magnitude();
442 unit = Vector(linePt1, linePt2).Unit();
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);
452 The numbers hardcoded into here, what are they?
453 I believe they are pixels.
455 // Draw extension lines (if certain type)
456 painter->DrawLine(p3, p5);
457 painter->DrawLine(p4, p6);
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);
464 // On the screen, it's acting like this is actually 58%...
465 // This is correct, we want it to happen at > 50%
468 // Draw main dimension line + arrowheads
469 painter->DrawLine(p1, p2);
470 painter->DrawArrowhead(p1, p2, scaledThickness);
471 painter->DrawArrowhead(p2, p1, scaledThickness);
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));
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);
489 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
494 dimText = QString("%1\"").arg(length);
497 double feet = (double)((int)length / 12);
498 double inches = length - (feet * 12.0);
501 dimText = QString("%1'").arg(feet);
503 dimText = QString("%1' %2\"").arg(feet).arg(inches);
507 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
513 Text * t = (Text *)obj;
514 painter->DrawTextObject(t->p1, t->s.c_str(), scaledThickness);
524 void DrawingView::DeleteSelectedItems(void)
526 std::vector<void *>::iterator i = document.objects.begin();
528 while (i != document.objects.end())
530 Object * obj = (Object *)(*i);
535 document.objects.erase(i);
543 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
545 Global::screenSize = Vector(size().width(), size().height());
546 UpdateGridBackground();
550 void DrawingView::mousePressEvent(QMouseEvent * event)
552 if (event->button() == Qt::LeftButton)
554 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
555 // collided = document.Collided(point);
558 // Do an update if collided with at least *one* object in the document
565 if (Global::snapToGrid)
566 point = Global::SnapPointToGrid(point);
568 // We always snap to object points, and they take precendence over
570 if (Global::snapPointIsValid)
571 point = Global::snapPoint;
573 toolAction->MouseDown(point);
578 // Didn't hit any object and not using a tool, so do a selection rectangle
579 if (!(collided))// || toolAction))
581 Global::selectionInProgress = true;
582 Global::selection.setTopLeft(QPointF(point.x, point.y));
583 Global::selection.setBottomRight(QPointF(point.x, point.y));
587 else if (event->button() == Qt::MiddleButton)
590 oldPoint = Vector(event->x(), event->y());
591 // Should also change the mouse pointer as well...
592 setCursor(Qt::SizeAllCursor);
597 void DrawingView::mouseMoveEvent(QMouseEvent * event)
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;
605 if (event->buttons() & Qt::MiddleButton)
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;
613 Global::origin -= delta;
615 UpdateGridBackground();
622 // Grid processing... (only snap here is left button is down)
623 if ((event->buttons() & Qt::LeftButton) && Global::snapToGrid)
625 point = SnapPointToGrid(point);
628 // Snap points on objects always take precedence over the grid, whether
629 // dragging an object or not...
631 if (Global::snapPointIsValid)
633 // Uncommenting this causes the cursor to become unresponsive after the first
635 // point = Global::snapPoint;
639 // Do checking here to see if object can be selected or not
640 if (Global::selectionInProgress)
642 std::vector<void *>::iterator i;
646 for(i=document.objects.begin(); i!=document.objects.end(); i++)
648 Object * obj = (Object *)(*i);
649 obj->selected = false;
656 Line * l = (Line *)obj;
658 if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
665 Circle * c = (Circle *)obj;
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))
674 Arc * a = (Arc *)obj;
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);
683 // Swap X/Y coordinates if they're backwards...
684 if (bounds.left() > bounds.right())
686 double temp = bounds.left();
687 bounds.setLeft(bounds.right());
688 bounds.setRight(temp);
691 if (bounds.bottom() > bounds.top())
693 double temp = bounds.bottom();
694 bounds.setBottom(bounds.top());
698 // Doesn't work as advertised! For shame!
699 bounds = bounds.normalized();
702 // If the end of the arc is before the beginning, add 360 degrees to it
706 // Adjust the bounds depending on which axes are crossed
707 if ((start < PI_OVER_2) && (end > PI_OVER_2))
710 if ((start < PI) && (end > PI))
711 bounds.setLeft(-1.0);
713 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
714 bounds.setBottom(-1.0);
716 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
717 bounds.setRight(1.0);
719 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
722 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
723 bounds.setLeft(-1.0);
725 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
726 bounds.setBottom(-1.0);
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);
732 if (Global::selection.contains(bounds))
747 //we should keep track of the last point here and only pass this down *if* the point
751 // This returns true if we've moved over an object...
752 if (document.PointerMoved(point))
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...
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...
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)
769 // Two objects are hovered, see if we have an intersection point
770 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
773 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
777 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
778 Global::snapPointIsValid = true;
781 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
784 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
788 Global::snapPoint = p1;
789 Global::snapPointIsValid = true;
793 double d1 = Vector(point, p1).Magnitude();
794 double d2 = Vector(point, p2).Magnitude();
797 Global::snapPoint = p1;
799 Global::snapPoint = p2;
801 Global::snapPointIsValid = true;
807 // Otherwise, it was a single object hovered...
813 if (Global::snapToGrid)
814 point = Global::SnapPointToGrid(point);
816 // We always snap to object points, and they take precendence over
818 if (Global::snapPointIsValid)
819 point = Global::snapPoint;
821 toolAction->MouseMoved(point);
824 bool needUpdate = false;
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)
831 std::vector<void *>::iterator i;
834 for(i=document.objects.begin(); i!=document.objects.end(); i++)
836 Object * obj = (Object *)(*i);
837 // obj->selected = false;
843 Line * l = (Line *)obj;
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);
854 distance = v1.Magnitude();
856 distance = v2.Magnitude();
858 // distance = ?Det?(ls, v1) / |ls|
859 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
860 / lineSegment.Magnitude());
862 if ((v1.Magnitude() * Global::zoom) < 8.0)
864 l->hitPoint[0] = true;
865 // snapPoint = l->p1;
866 // snapPointIsValid = true;
868 else if ((v2.Magnitude() * Global::zoom) < 8.0)
870 l->hitPoint[1] = true;
871 // snapPoint = l->p2;
872 // snapPointIsValid = true;
874 else if ((distance * Global::zoom) < 5.0)
877 bool oldHovered = l->hovered;
878 l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
880 if (oldHovered != l->hovered)
887 Circle * c = (Circle *)obj;
898 // This is used to draw the tool crosshair...
901 // if (/*document.NeedsUpdate() ||*/ Global::selectionInProgress /*|| toolAction*/)
902 if (needUpdate || Global::selectionInProgress)
907 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
909 if (event->button() == Qt::LeftButton)
912 document.PointerReleased();
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())
920 update(); // Do an update if collided with at least *one* object in the document
924 toolAction->MouseReleased();
927 if (Global::selectionInProgress)
929 // Select all the stuff inside of selection
930 Global::selectionInProgress = false;
933 else if (event->button() == Qt::MiddleButton)
936 setCursor(Qt::ArrowCursor);
941 void DrawingView::wheelEvent(QWheelEvent * event)
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);
948 // This is not centering for some reason. Need to figure out why. :-/
949 if (event->delta() > 0)
951 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
952 Global::origin = newOrigin;
953 Global::zoom *= zoomFactor;
957 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
958 Global::origin = newOrigin;
959 Global::zoom /= zoomFactor;
963 // Global::gridSpacing = gridPixels / Painter::zoom;
964 // UpdateGridBackground();
965 SetGridSize(Global::gridSpacing * Global::zoom);
967 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
972 void DrawingView::keyPressEvent(QKeyEvent * event)
976 toolAction->KeyDown(event->key());
981 void DrawingView::keyReleaseEvent(QKeyEvent * event)
985 toolAction->KeyReleased(event->key());
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.
997 Point DrawingView::SnapPointToGrid(Point point)
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;