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),
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), "Tyegxt!"));
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;
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);
527 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
529 Global::screenSize = Vector(size().width(), size().height());
530 UpdateGridBackground();
534 void DrawingView::mousePressEvent(QMouseEvent * event)
536 if (event->button() == Qt::LeftButton)
538 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
539 // collided = document.Collided(point);
542 // Do an update if collided with at least *one* object in the document
549 if (Global::snapToGrid)
550 point = Global::SnapPointToGrid(point);
552 // We always snap to object points, and they take precendence over
554 if (Global::snapPointIsValid)
555 point = Global::snapPoint;
557 toolAction->MouseDown(point);
562 // Didn't hit any object and not using a tool, so do a selection rectangle
563 if (!(collided))// || toolAction))
565 Global::selectionInProgress = true;
566 Global::selection.setTopLeft(QPointF(point.x, point.y));
567 Global::selection.setBottomRight(QPointF(point.x, point.y));
571 else if (event->button() == Qt::MiddleButton)
574 oldPoint = Vector(event->x(), event->y());
575 // Should also change the mouse pointer as well...
576 setCursor(Qt::SizeAllCursor);
581 void DrawingView::mouseMoveEvent(QMouseEvent * event)
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;
589 if (event->buttons() & Qt::MiddleButton)
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;
597 Global::origin -= delta;
599 UpdateGridBackground();
606 // Grid processing... (only snap here is left button is down)
607 if ((event->buttons() & Qt::LeftButton) && Global::snapToGrid)
609 point = SnapPointToGrid(point);
612 // Snap points on objects always take precedence over the grid, whether
613 // dragging an object or not...
615 if (Global::snapPointIsValid)
617 // Uncommenting this causes the cursor to become unresponsive after the first
619 // point = Global::snapPoint;
623 // Do checking here to see if object can be selected or not
624 if (Global::selectionInProgress)
626 std::vector<void *>::iterator i;
629 for(i=document.objects.begin(); i!=document.objects.end(); i++)
631 Object * obj = (Object *)(*i);
632 obj->selected = false;
639 Line * l = (Line *)obj;
641 if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
648 Circle * c = (Circle *)obj;
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))
657 Arc * a = (Arc *)obj;
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);
666 // Swap X/Y coordinates if they're backwards...
667 if (bounds.left() > bounds.right())
669 double temp = bounds.left();
670 bounds.setLeft(bounds.right());
671 bounds.setRight(temp);
674 if (bounds.bottom() > bounds.top())
676 double temp = bounds.bottom();
677 bounds.setBottom(bounds.top());
681 // Doesn't work as advertised! For shame!
682 bounds = bounds.normalized();
685 // If the end of the arc is before the beginning, add 360 degrees to it
689 // Adjust the bounds depending on which axes are crossed
690 if ((start < PI_OVER_2) && (end > PI_OVER_2))
693 if ((start < PI) && (end > PI))
694 bounds.setLeft(-1.0);
696 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
697 bounds.setBottom(-1.0);
699 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
700 bounds.setRight(1.0);
702 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
705 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
706 bounds.setLeft(-1.0);
708 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
709 bounds.setBottom(-1.0);
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);
715 if (Global::selection.contains(bounds))
727 //we should keep track of the last point here and only pass this down *if* the point
731 // This returns true if we've moved over an object...
732 if (document.PointerMoved(point))
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...
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...
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)
749 // Two objects are hovered, see if we have an intersection point
750 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
753 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
757 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
758 Global::snapPointIsValid = true;
761 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
764 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
768 Global::snapPoint = p1;
769 Global::snapPointIsValid = true;
773 double d1 = Vector(point, p1).Magnitude();
774 double d2 = Vector(point, p2).Magnitude();
777 Global::snapPoint = p1;
779 Global::snapPoint = p2;
781 Global::snapPointIsValid = true;
787 // Otherwise, it was a single object hovered...
793 if (Global::snapToGrid)
794 point = Global::SnapPointToGrid(point);
796 // We always snap to object points, and they take precendence over
798 if (Global::snapPointIsValid)
799 point = Global::snapPoint;
801 toolAction->MouseMoved(point);
804 bool needUpdate = false;
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)
811 std::vector<void *>::iterator i;
814 for(i=document.objects.begin(); i!=document.objects.end(); i++)
816 Object * obj = (Object *)(*i);
817 // obj->selected = false;
823 Line * l = (Line *)obj;
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);
834 distance = v1.Magnitude();
836 distance = v2.Magnitude();
838 // distance = ?Det?(ls, v1) / |ls|
839 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
840 / lineSegment.Magnitude());
842 if ((v1.Magnitude() * Global::zoom) < 8.0)
844 l->hitPoint[0] = true;
845 // snapPoint = l->p1;
846 // snapPointIsValid = true;
848 else if ((v2.Magnitude() * Global::zoom) < 8.0)
850 l->hitPoint[1] = true;
851 // snapPoint = l->p2;
852 // snapPointIsValid = true;
854 else if ((distance * Global::zoom) < 5.0)
857 bool oldHovered = l->hovered;
858 l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
860 if (oldHovered != l->hovered)
867 Circle * c = (Circle *)obj;
878 // This is used to draw the tool crosshair...
881 // if (/*document.NeedsUpdate() ||*/ Global::selectionInProgress /*|| toolAction*/)
882 if (needUpdate || Global::selectionInProgress)
887 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
889 if (event->button() == Qt::LeftButton)
892 document.PointerReleased();
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())
900 update(); // Do an update if collided with at least *one* object in the document
904 toolAction->MouseReleased();
907 if (Global::selectionInProgress)
909 // Select all the stuff inside of selection
910 Global::selectionInProgress = false;
913 else if (event->button() == Qt::MiddleButton)
916 setCursor(Qt::ArrowCursor);
921 void DrawingView::wheelEvent(QWheelEvent * event)
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);
928 // This is not centering for some reason. Need to figure out why. :-/
929 if (event->delta() > 0)
931 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
932 Global::origin = newOrigin;
933 Global::zoom *= zoomFactor;
937 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
938 Global::origin = newOrigin;
939 Global::zoom /= zoomFactor;
943 // Global::gridSpacing = gridPixels / Painter::zoom;
944 // UpdateGridBackground();
945 SetGridSize(Global::gridSpacing * Global::zoom);
947 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
952 void DrawingView::keyPressEvent(QKeyEvent * event)
956 toolAction->KeyDown(event->key());
961 void DrawingView::keyReleaseEvent(QKeyEvent * event)
965 toolAction->KeyReleased(event->key());
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.
977 Point DrawingView::SnapPointToGrid(Point point)
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;