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), numHovered(0), shiftDown(false),
50 gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
51 scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)),
52 gridPixels(0), collided(false)//, toolAction(NULL)
54 // document.isTopLevelContainer = true;
55 //wtf? doesn't work except in c++11??? document = { 0 };
56 setBackgroundRole(QPalette::Base);
57 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
59 Global::gridSpacing = 12.0; // In base units (inch is default)
62 Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
64 document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
65 document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
66 document.Add(new Circle(Vector(100, 100), 36, &document));
67 document.Add(new Circle(Vector(50, 150), 49, &document));
68 document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
69 document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
71 Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
72 line->SetDimensionOnLine(dimension);
73 document.Add(dimension);
75 // Alternate way to do the above...
76 line->SetDimensionOnLine();
79 Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
80 line->p1 = Vector(5, 5);
81 line->p2 = Vector(50, 40);
83 line->thickness = 2.0;
85 line->color = 0xFF7F00;
86 document.objects.push_back(line);
87 document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
88 document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
89 document.objects.push_back(new Circle(Vector(100, 100), 36));
90 document.objects.push_back(new Circle(Vector(50, 150), 49));
91 document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
92 document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
93 document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
94 document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
98 Here we set the grid size in pixels--12 in this case. Initially, we have our
99 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
100 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
101 to be able to set the size of the background grid (which we do here at an
102 arbitrary 12 pixels) to anything we want (within reason, of course :-).
104 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
106 drawing->gridSpacing = 12.0 / Global::zoom;
108 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
109 translated to Cartesian coordinates through this. (Initially, Global::zoom is
110 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
112 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
113 convenience function than any measure of absolutes. Doing things that way we
114 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
115 shittiness that comes with it.
117 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
118 a certain way, which means we should probably create something else in those
119 objects to take its place--like some kind of scale factor. This would seem to
120 imply that certain point sizes actually *do* tie things like fonts to absolute
121 sizes on the screen, but not necessarily because you could have an inch scale
122 with text that is quite small relative to other objects on the screen, which
123 currently you have to zoom in to see (and which blows up the text). Point sizes
124 in an application like this are a bit meaningless; even though an inch is an
125 inch regardless of the zoom level a piece of text can be larger or smaller than
126 this. Maybe this is the case for having a base unit and basing point sizes off
129 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
130 base units. What that means is that if you have a 12px grid with a 6" grid size
131 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
133 Dimensions now have a "size" parameter to set their absolute size in relation
134 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
135 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
136 scaled the same way as the arrowheads.
138 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
139 need a thickness parameter similar to the "size" param for dimensions. (And now
143 SetGridSize(12); // This is in pixels
148 void DrawingView::SetToolActive(Action * action)
153 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
154 SLOT(AddNewObjectToDocument(Object *)));
155 connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
161 void DrawingView::SetGridSize(uint32_t size)
164 if (size == gridPixels)
167 // Recreate the background bitmap
169 QPainter pmp(&gridBackground);
170 pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
171 pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
173 for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
175 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
176 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
181 // Set up new BG brush & zoom level (pixels per base unit)
182 // Painter::zoom = gridPixels / gridSpacing;
183 Global::zoom = gridPixels / Global::gridSpacing;
184 UpdateGridBackground();
188 void DrawingView::UpdateGridBackground(void)
190 // Transform the origin to Qt coordinates
191 Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
192 int x = (int)pixmapOrigin.x;
193 int y = (int)pixmapOrigin.y;
194 // Use mod arithmetic to grab the correct swatch of background
196 Negative numbers still screw it up... Need to think about what we're
197 trying to do here. The fact that it worked with 72 seems to have been pure luck.
198 It seems the problem is negative numbers: We can't let that happen.
199 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
202 The bitmap looks like this:
212 @ x = 1, we want it to look like:
214 -+---+---+---+---+---
217 -+---+---+---+---+---
222 Which means we need to grab the sample from x = 3. @ x = -1:
232 Which means we need to grab the sample from x = 1. Which means we have to take
233 the mirror of the modulus of gridPixels.
235 Doing a mod of a negative number is problematic: 1st, the compiler converts the
236 negative number to an unsigned int, then it does the mod. Gets you wrong answers
237 most of the time, unless you use a power of 2. :-P So what we do here is just
238 take the modulus of the negation, which means we don't have to worry about
241 The positive case looks gruesome (and it is) but it boils down to this: We take
242 the modulus of the X coordinate, then mirror it by subtraction from the
243 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
244 gridPixels. But we need the case where the result equalling gridPixels to be
245 zero; so we do another modulus operation on the result to achieve this.
250 x = (gridPixels - (x % gridPixels)) % gridPixels;
255 y = (gridPixels - (y % gridPixels)) % gridPixels;
257 // Here we grab a section of the bigger pixmap, so that the background
258 // *looks* like it's scrolling...
259 QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
260 QPalette pal = palette();
261 pal.setBrush(backgroundRole(), QBrush(pm));
262 setAutoFillBackground(true);
267 void DrawingView::AddNewObjectToDocument(Object * object)
271 // object->Reparent(&document);
272 // document.Add(object);
275 //printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
279 void DrawingView::HandleActionUpdate(void)
285 void DrawingView::SetCurrentLayer(int layer)
287 Global::currentLayer = layer;
288 //printf("DrawingView::CurrentLayer = %i\n", layer);
292 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
294 // This is undoing the transform, e.g. going from client coords to local coords.
295 // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
296 // conversion of the y-axis from increasing bottom to top.
297 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
301 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
303 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
304 // No voodoo here, it's just grouped wrong to see it. It should be:
305 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
306 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
310 void DrawingView::paintEvent(QPaintEvent * /*event*/)
312 QPainter qtPainter(this);
313 Painter painter(&qtPainter);
316 qtPainter.setRenderHint(QPainter::Antialiasing);
318 Global::viewportHeight = size().height();
320 // Draw coordinate axes
321 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
322 painter.DrawLine(0, -16384, 0, 16384);
323 painter.DrawLine(-16384, 0, 16384, 0);
325 // The top level document takes care of rendering for us...
326 // document.Draw(&painter);
327 // Not any more it doesn't...
328 RenderObjects(&painter, &document);
333 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
334 painter.DrawCrosshair(oldPoint);
335 toolAction->Draw(&painter);
340 if (Global::selectionInProgress)
342 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
343 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
344 painter.DrawRect(Global::selection);
350 void DrawingView::RenderObjects(Painter * painter, Container * c)
352 std::vector<void *>::iterator i;
354 for(i=c->objects.begin(); i!=c->objects.end(); i++)
356 Object * obj = (Object *)(*i);
357 float scaledThickness = Global::scale * obj->thickness;
358 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
359 painter->SetBrush(obj->color);
361 if (obj->selected || obj->hovered)
362 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
368 Line * l = (Line *)obj;
369 painter->DrawLine(l->p1, l->p2);
374 Circle * ci = (Circle *)obj;
375 painter->SetBrush(QBrush(Qt::NoBrush));
376 painter->DrawEllipse(ci->p1, ci->radius, ci->radius);
381 Arc * a = (Arc *)obj;
382 painter->DrawArc(a->p1, a->radius, a->angle1, a->angle2);
387 Dimension * d = (Dimension *)obj;
389 Vector v(d->p1, d->p2);
390 double angle = v.Angle();
391 Vector unit = v.Unit();
392 Vector linePt1 = d->p1, linePt2 = d->p2;
394 double x1, y1, length;
396 if (d->subtype == DTLinearVert)
398 if ((angle < 0) || (angle > PI))
400 x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
401 y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
402 ortho = Vector(1.0, 0);
407 x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
408 y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
409 ortho = Vector(-1.0, 0);
413 linePt1.x = linePt2.x = x1;
414 length = fabs(d->p1.y - d->p2.y);
416 else if (d->subtype == DTLinearHorz)
418 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
420 x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
421 y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
422 ortho = Vector(0, 1.0);
427 x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
428 y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
429 ortho = Vector(0, -1.0);
433 linePt1.y = linePt2.y = y1;
434 length = fabs(d->p1.x - d->p2.x);
436 else if (d->subtype == DTLinear)
438 angle = Vector(linePt1, linePt2).Angle();
439 ortho = Vector::Normal(linePt1, linePt2);
440 length = v.Magnitude();
443 unit = Vector(linePt1, linePt2).Unit();
445 Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
446 Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
447 Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
448 Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
449 Point p5 = d->p1 + (ortho * 4.0 * scaledThickness);
450 Point p6 = d->p2 + (ortho * 4.0 * scaledThickness);
453 The numbers hardcoded into here, what are they?
454 I believe they are pixels.
456 // Draw extension lines (if certain type)
457 painter->DrawLine(p3, p5);
458 painter->DrawLine(p4, p6);
460 // Calculate whether or not the arrowheads are too crowded to put inside
461 // the extension lines. 9.0 is the length of the arrowhead.
462 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
463 //printf("Dimension::Draw(): t = %lf\n", t);
465 // On the screen, it's acting like this is actually 58%...
466 // This is correct, we want it to happen at > 50%
469 // Draw main dimension line + arrowheads
470 painter->DrawLine(p1, p2);
471 painter->DrawArrowhead(p1, p2, scaledThickness);
472 painter->DrawArrowhead(p2, p1, scaledThickness);
476 // Draw outside arrowheads
477 Point p7 = p1 - (unit * 9.0 * scaledThickness);
478 Point p8 = p2 + (unit * 9.0 * scaledThickness);
479 painter->DrawArrowhead(p1, p7, scaledThickness);
480 painter->DrawArrowhead(p2, p8, scaledThickness);
481 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
482 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
485 // Draw length of dimension line...
486 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
487 Point ctr = p2 + (Vector(p2, p1) / 2.0);
490 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
495 dimText = QString("%1\"").arg(length);
498 double feet = (double)((int)length / 12);
499 double inches = length - (feet * 12.0);
502 dimText = QString("%1'").arg(feet);
504 dimText = QString("%1' %2\"").arg(feet).arg(inches);
508 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
514 Text * t = (Text *)obj;
515 painter->DrawTextObject(t->p1, t->s.c_str(), scaledThickness);
525 void DrawingView::DeleteSelectedItems(void)
527 std::vector<void *>::iterator i = document.objects.begin();
529 while (i != document.objects.end())
531 Object * obj = (Object *)(*i);
536 document.objects.erase(i);
544 void DrawingView::ClearSelection(void)
546 std::vector<void *>::iterator i;
548 for(i=document.objects.begin(); i!=document.objects.end(); i++)
549 ((Object *)(*i))->selected = false;
553 void DrawingView::AddHoveredToSelection(void)
555 std::vector<void *>::iterator i;
557 for(i=document.objects.begin(); i!=document.objects.end(); i++)
559 if (((Object *)(*i))->hovered)
560 ((Object *)(*i))->selected = true;
565 void DrawingView::GetSelection(std::vector<void *> & v)
568 std::vector<void *>::iterator i;
570 for(i=v.begin(); i!=v.end(); i++)
572 if (((Object *)(*i))->selected)
578 void DrawingView::GetHovered(std::vector<void *> & v)
581 std::vector<void *>::iterator i;
583 for(i=v.begin(); i!=v.end(); i++)
585 if (((Object *)(*i))->hovered)
591 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
593 Global::screenSize = Vector(size().width(), size().height());
594 UpdateGridBackground();
598 void DrawingView::mousePressEvent(QMouseEvent * event)
600 if (event->button() == Qt::LeftButton)
602 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
603 // collided = document.Collided(point);
606 // Do an update if collided with at least *one* object in the document
609 // Actually, we already know what we're going to click on as all the collision
610 // detection already happened in the mouse move function...!
611 //printf("MouseDown: ctrl=%s, numHovered=%i\n", (ctrlDown ? "DOWN" : "up"), numHovered);
616 if (Global::snapToGrid)
617 point = Global::SnapPointToGrid(point);
619 // We always snap to object points, and they take precendence over
621 if (Global::snapPointIsValid)
622 point = Global::snapPoint;
624 toolAction->MouseDown(point);
637 AddHoveredToSelection();
640 // Didn't hit any object and not using a tool, so do a selection rectangle
641 if (!(numHovered || Global::tool))
643 Global::selectionInProgress = true;
644 Global::selection.setTopLeft(QPointF(point.x, point.y));
645 Global::selection.setBottomRight(QPointF(point.x, point.y));
649 else if (event->button() == Qt::MiddleButton)
652 oldPoint = Vector(event->x(), event->y());
653 // Should also change the mouse pointer as well...
654 setCursor(Qt::SizeAllCursor);
659 void DrawingView::mouseMoveEvent(QMouseEvent * event)
661 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
662 Global::selection.setBottomRight(QPointF(point.x, point.y));
663 // Only needs to be done here, as mouse down is always preceded by movement
664 Global::snapPointIsValid = false;
667 if (event->buttons() & Qt::MiddleButton)
669 point = Vector(event->x(), event->y());
670 // Since we're using Qt coords for scrolling, we have to adjust them here to
671 // conform to Cartesian coords, since the origin is using Cartesian. :-)
672 Vector delta(oldPoint, point);
673 delta /= Global::zoom;
675 Global::origin -= delta;
677 UpdateGridBackground();
684 // Grid processing... (only snap here is left button is down)
685 if ((event->buttons() & Qt::LeftButton) && Global::snapToGrid)
687 point = SnapPointToGrid(point);
690 // Snap points on objects always take precedence over the grid, whether
691 // dragging an object or not...
693 if (Global::snapPointIsValid)
695 // Uncommenting this causes the cursor to become unresponsive after the first
697 // point = Global::snapPoint;
701 // Do checking here to see if object can be selected or not
702 if (Global::selectionInProgress)
704 std::vector<void *>::iterator i;
708 for(i=document.objects.begin(); i!=document.objects.end(); i++)
710 Object * obj = (Object *)(*i);
711 obj->selected = false;
718 Line * l = (Line *)obj;
720 if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
727 Circle * c = (Circle *)obj;
729 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))
736 Arc * a = (Arc *)obj;
738 double start = a->angle1;
739 double end = start + a->angle2;
740 QPointF p1(cos(start), sin(start));
741 QPointF p2(cos(end), sin(end));
742 QRectF bounds(p1, p2);
745 // Swap X/Y coordinates if they're backwards...
746 if (bounds.left() > bounds.right())
748 double temp = bounds.left();
749 bounds.setLeft(bounds.right());
750 bounds.setRight(temp);
753 if (bounds.bottom() > bounds.top())
755 double temp = bounds.bottom();
756 bounds.setBottom(bounds.top());
760 // Doesn't work as advertised! For shame!
761 bounds = bounds.normalized();
764 // If the end of the arc is before the beginning, add 360 degrees to it
768 // Adjust the bounds depending on which axes are crossed
769 if ((start < PI_OVER_2) && (end > PI_OVER_2))
772 if ((start < PI) && (end > PI))
773 bounds.setLeft(-1.0);
775 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
776 bounds.setBottom(-1.0);
778 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
779 bounds.setRight(1.0);
781 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
784 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
785 bounds.setLeft(-1.0);
787 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
788 bounds.setBottom(-1.0);
790 bounds.setTopLeft(QPointF(bounds.left() * a->radius, bounds.top() * a->radius));
791 bounds.setBottomRight(QPointF(bounds.right() * a->radius, bounds.bottom() * a->radius));
792 bounds.translate(a->p1.x, a->p1.y);
794 if (Global::selection.contains(bounds))
809 //we should keep track of the last point here and only pass this down *if* the point
813 // This returns true if we've moved over an object...
814 if (document.PointerMoved(point)) // <-- This
815 // This is where the object would do automagic dragging & shit. Since we don't
816 // do that anymore, we need a strategy to handle it.
820 Now objects handle mouse move snapping as well. The code below mainly works only
821 for tools; we need to fix it so that objects work as well...
823 There's a problem with the object point snapping in that it's dependent on the
824 order of the objects in the document. Most likely this is because it counts the
825 selected object last and thus fucks up the algorithm. Need to fix this...
829 // Do object snapping here. Grid snapping on mouse down is done in the
830 // objects themselves, only because we have to hit test the raw point,
831 // not the snapped point. There has to be a better way...!
832 if (document.penultimateObjectHovered)
834 // Two objects are hovered, see if we have an intersection point
835 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
838 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
842 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
843 Global::snapPointIsValid = true;
846 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
849 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
853 Global::snapPoint = p1;
854 Global::snapPointIsValid = true;
858 double d1 = Vector(point, p1).Magnitude();
859 double d2 = Vector(point, p2).Magnitude();
862 Global::snapPoint = p1;
864 Global::snapPoint = p2;
866 Global::snapPointIsValid = true;
872 // Otherwise, it was a single object hovered...
878 if (Global::snapToGrid)
879 point = Global::SnapPointToGrid(point);
881 // We always snap to object points, and they take precendence over
883 if (Global::snapPointIsValid)
884 point = Global::snapPoint;
886 toolAction->MouseMoved(point);
889 bool needUpdate = false;
891 // Don't do this kind of checking unless we're not doing a selection rectangle!
892 // Hmm, lines don't stay selected if globally selected... !!! FIX !!! [DONE]
893 // it's because there were extra state variables, the hit* vars...
894 if (!Global::selectionInProgress)
896 std::vector<void *>::iterator i;
899 for(i=document.objects.begin(); i!=document.objects.end(); i++)
901 Object * obj = (Object *)(*i);
902 // obj->selected = false;
908 Line * l = (Line *)obj;
910 // bool hitPoint1, hitPoint2, hitLine;
911 l->hitPoint[0] = l->hitPoint[1] = l->hitObject = false;
912 Vector lineSegment = l->p2 - l->p1;
913 Vector v1 = point - l->p1;
914 Vector v2 = point - l->p2;
915 double t = Geometry::ParameterOfLineAndPoint(l->p1, l->p2, point);
919 distance = v1.Magnitude();
921 distance = v2.Magnitude();
923 // distance = ?Det?(ls, v1) / |ls|
924 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
925 / lineSegment.Magnitude());
927 if ((v1.Magnitude() * Global::zoom) < 8.0)
929 l->hitPoint[0] = true;
930 // snapPoint = l->p1;
931 // snapPointIsValid = true;
933 else if ((v2.Magnitude() * Global::zoom) < 8.0)
935 l->hitPoint[1] = true;
936 // snapPoint = l->p2;
937 // snapPointIsValid = true;
939 else if ((distance * Global::zoom) < 5.0)
942 bool oldHovered = l->hovered;
943 l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
945 if (oldHovered != l->hovered)
952 Circle * c = (Circle *)obj;
964 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
968 //printf("MouseMove: numHovered = %i\n", numHovered);
970 // This is used to draw the tool crosshair...
973 // if (/*document.NeedsUpdate() ||*/ Global::selectionInProgress /*|| toolAction*/)
974 if (needUpdate || Global::selectionInProgress || Global::tool)
979 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
981 if (event->button() == Qt::LeftButton)
984 document.PointerReleased();
987 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
988 //could set it up to use the document's update function (assumes that all object updates
989 //are being reported correctly:
990 // if (document.NeedsUpdate())
992 update(); // Do an update if collided with at least *one* object in the document
996 toolAction->MouseReleased();
999 if (Global::selectionInProgress)
1001 // Select all the stuff inside of selection
1002 Global::selectionInProgress = false;
1004 // Clear our vectors
1009 std::vector<void *>::iterator i;
1011 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1013 if (((Object *)(*i))->selected)
1014 select.push_back(*i);
1016 //hmm, this is no good, too late to do any good :-P
1017 // if ((*i)->hovered)
1018 // hover.push_back(*i);
1022 else if (event->button() == Qt::MiddleButton)
1025 setCursor(Qt::ArrowCursor);
1030 void DrawingView::wheelEvent(QWheelEvent * event)
1032 double zoomFactor = 1.25;
1033 QSize sizeWin = size();
1034 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1035 center = Painter::QtToCartesianCoords(center);
1037 // This is not centering for some reason. Need to figure out why. :-/
1038 if (event->delta() > 0)
1040 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1041 Global::origin = newOrigin;
1042 Global::zoom *= zoomFactor;
1046 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1047 Global::origin = newOrigin;
1048 Global::zoom /= zoomFactor;
1052 // Global::gridSpacing = gridPixels / Painter::zoom;
1053 // UpdateGridBackground();
1054 SetGridSize(Global::gridSpacing * Global::zoom);
1056 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1061 void DrawingView::keyPressEvent(QKeyEvent * event)
1065 toolAction->KeyDown(event->key());
1067 if (event->key() == Qt::Key_Shift)
1069 else if (event->key() == Qt::Key_Control)
1074 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1078 toolAction->KeyReleased(event->key());
1080 if (event->key() == Qt::Key_Shift)
1082 else if (event->key() == Qt::Key_Control)
1087 // This looks strange, but it's really quite simple: We want a point that's
1088 // more than half-way to the next grid point to snap there while conversely we
1089 // want a point that's less than half-way to to the next grid point then snap
1090 // to the one before it. So we add half of the grid spacing to the point, then
1091 // divide by it so that we can remove the fractional part, then multiply it
1092 // back to get back to the correct answer.
1094 Point DrawingView::SnapPointToGrid(Point point)
1096 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
1097 point /= Global::gridSpacing;
1098 point.x = floor(point.x);//need to fix this for negative numbers...
1099 point.y = floor(point.y);
1100 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
1101 point *= Global::gridSpacing;