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
42 enum { ToolMouseDown, ToolMouseMove, ToolMouseUp };
45 //Container DrawingView::document(Vector(0, 0));
48 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
49 // The value in the settings file will override this.
50 useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
52 gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
53 scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)),
54 gridPixels(0), collided(false)//, toolAction(NULL)
56 // document.isTopLevelContainer = true;
57 //wtf? doesn't work except in c++11??? document = { 0 };
58 setBackgroundRole(QPalette::Base);
59 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
61 Global::gridSpacing = 12.0; // In base units (inch is default)
64 Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
66 document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
67 document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
68 document.Add(new Circle(Vector(100, 100), 36, &document));
69 document.Add(new Circle(Vector(50, 150), 49, &document));
70 document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
71 document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
73 Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
74 line->SetDimensionOnLine(dimension);
75 document.Add(dimension);
77 // Alternate way to do the above...
78 line->SetDimensionOnLine();
81 Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
82 line->p1 = Vector(5, 5);
83 line->p2 = Vector(50, 40);
85 line->thickness = 2.0;
87 line->color = 0xFF7F00;
88 document.objects.push_back(line);
89 document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
90 document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
91 document.objects.push_back(new Circle(Vector(100, 100), 36));
92 document.objects.push_back(new Circle(Vector(50, 150), 49));
93 document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
94 document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
95 document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
96 document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
100 Here we set the grid size in pixels--12 in this case. Initially, we have our
101 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
102 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
103 to be able to set the size of the background grid (which we do here at an
104 arbitrary 12 pixels) to anything we want (within reason, of course :-).
106 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
108 drawing->gridSpacing = 12.0 / Global::zoom;
110 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
111 translated to Cartesian coordinates through this. (Initially, Global::zoom is
112 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
114 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
115 convenience function than any measure of absolutes. Doing things that way we
116 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
117 shittiness that comes with it.
119 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
120 a certain way, which means we should probably create something else in those
121 objects to take its place--like some kind of scale factor. This would seem to
122 imply that certain point sizes actually *do* tie things like fonts to absolute
123 sizes on the screen, but not necessarily because you could have an inch scale
124 with text that is quite small relative to other objects on the screen, which
125 currently you have to zoom in to see (and which blows up the text). Point sizes
126 in an application like this are a bit meaningless; even though an inch is an
127 inch regardless of the zoom level a piece of text can be larger or smaller than
128 this. Maybe this is the case for having a base unit and basing point sizes off
131 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
132 base units. What that means is that if you have a 12px grid with a 6" grid size
133 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
135 Dimensions now have a "size" parameter to set their absolute size in relation
136 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
137 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
138 scaled the same way as the arrowheads.
140 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
141 need a thickness parameter similar to the "size" param for dimensions. (And now
145 SetGridSize(12); // This is in pixels
150 void DrawingView::SetToolActive(Action * action)
155 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
156 SLOT(AddNewObjectToDocument(Object *)));
157 connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
163 void DrawingView::SetGridSize(uint32_t size)
166 if (size == gridPixels)
169 // Recreate the background bitmap
171 QPainter pmp(&gridBackground);
172 pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
173 pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
175 for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
177 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
178 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
183 // Set up new BG brush & zoom level (pixels per base unit)
184 // Painter::zoom = gridPixels / gridSpacing;
185 Global::zoom = gridPixels / Global::gridSpacing;
186 UpdateGridBackground();
190 void DrawingView::UpdateGridBackground(void)
192 // Transform the origin to Qt coordinates
193 Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
194 int x = (int)pixmapOrigin.x;
195 int y = (int)pixmapOrigin.y;
196 // Use mod arithmetic to grab the correct swatch of background
198 Negative numbers still screw it up... Need to think about what we're
199 trying to do here. The fact that it worked with 72 seems to have been pure luck.
200 It seems the problem is negative numbers: We can't let that happen.
201 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
204 The bitmap looks like this:
214 @ x = 1, we want it to look like:
216 -+---+---+---+---+---
219 -+---+---+---+---+---
224 Which means we need to grab the sample from x = 3. @ x = -1:
234 Which means we need to grab the sample from x = 1. Which means we have to take
235 the mirror of the modulus of gridPixels.
237 Doing a mod of a negative number is problematic: 1st, the compiler converts the
238 negative number to an unsigned int, then it does the mod. Gets you wrong answers
239 most of the time, unless you use a power of 2. :-P So what we do here is just
240 take the modulus of the negation, which means we don't have to worry about
243 The positive case looks gruesome (and it is) but it boils down to this: We take
244 the modulus of the X coordinate, then mirror it by subtraction from the
245 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
246 gridPixels. But we need the case where the result equalling gridPixels to be
247 zero; so we do another modulus operation on the result to achieve this.
252 x = (gridPixels - (x % gridPixels)) % gridPixels;
257 y = (gridPixels - (y % gridPixels)) % gridPixels;
259 // Here we grab a section of the bigger pixmap, so that the background
260 // *looks* like it's scrolling...
261 QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
262 QPalette pal = palette();
263 pal.setBrush(backgroundRole(), QBrush(pm));
264 setAutoFillBackground(true);
269 void DrawingView::AddNewObjectToDocument(Object * object)
273 // object->Reparent(&document);
274 // document.Add(object);
277 //printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
281 void DrawingView::HandleActionUpdate(void)
287 void DrawingView::SetCurrentLayer(int layer)
289 Global::currentLayer = layer;
290 //printf("DrawingView::CurrentLayer = %i\n", layer);
294 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
296 // This is undoing the transform, e.g. going from client coords to local coords.
297 // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
298 // conversion of the y-axis from increasing bottom to top.
299 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
303 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
305 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
306 // No voodoo here, it's just grouped wrong to see it. It should be:
307 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
308 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
312 void DrawingView::paintEvent(QPaintEvent * /*event*/)
314 QPainter qtPainter(this);
315 Painter painter(&qtPainter);
318 qtPainter.setRenderHint(QPainter::Antialiasing);
320 Global::viewportHeight = size().height();
322 // Draw coordinate axes
323 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
324 painter.DrawLine(0, -16384, 0, 16384);
325 painter.DrawLine(-16384, 0, 16384, 0);
327 // The top level document takes care of rendering for us...
328 // document.Draw(&painter);
329 // Not any more it doesn't...
330 RenderObjects(&painter, &document);
335 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
336 painter.DrawCrosshair(oldPoint);
337 toolAction->Draw(&painter);
342 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
343 painter.DrawCrosshair(oldPoint);
349 if (Global::selectionInProgress)
351 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
352 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
353 painter.DrawRect(Global::selection);
359 void DrawingView::RenderObjects(Painter * painter, Container * c)
361 std::vector<void *>::iterator i;
363 for(i=c->objects.begin(); i!=c->objects.end(); i++)
365 Object * obj = (Object *)(*i);
366 float scaledThickness = Global::scale * obj->thickness;
367 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
368 painter->SetBrush(obj->color);
370 if (obj->selected || obj->hovered)
371 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
377 Line * l = (Line *)obj;
378 painter->DrawLine(l->p1, l->p2);
383 Circle * ci = (Circle *)obj;
384 painter->SetBrush(QBrush(Qt::NoBrush));
385 painter->DrawEllipse(ci->p1, ci->radius, ci->radius);
390 Arc * a = (Arc *)obj;
391 painter->DrawArc(a->p1, a->radius, a->angle1, a->angle2);
396 Dimension * d = (Dimension *)obj;
398 Vector v(d->p1, d->p2);
399 double angle = v.Angle();
400 Vector unit = v.Unit();
401 Vector linePt1 = d->p1, linePt2 = d->p2;
403 double x1, y1, length;
405 if (d->subtype == DTLinearVert)
407 if ((angle < 0) || (angle > PI))
409 x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
410 y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
411 ortho = Vector(1.0, 0);
416 x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
417 y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
418 ortho = Vector(-1.0, 0);
422 linePt1.x = linePt2.x = x1;
423 length = fabs(d->p1.y - d->p2.y);
425 else if (d->subtype == DTLinearHorz)
427 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
429 x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
430 y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
431 ortho = Vector(0, 1.0);
436 x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
437 y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
438 ortho = Vector(0, -1.0);
442 linePt1.y = linePt2.y = y1;
443 length = fabs(d->p1.x - d->p2.x);
445 else if (d->subtype == DTLinear)
447 angle = Vector(linePt1, linePt2).Angle();
448 ortho = Vector::Normal(linePt1, linePt2);
449 length = v.Magnitude();
452 unit = Vector(linePt1, linePt2).Unit();
454 Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
455 Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
456 Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
457 Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
458 Point p5 = d->p1 + (ortho * 4.0 * scaledThickness);
459 Point p6 = d->p2 + (ortho * 4.0 * scaledThickness);
462 The numbers hardcoded into here, what are they?
463 I believe they are pixels.
465 // Draw extension lines (if certain type)
466 painter->DrawLine(p3, p5);
467 painter->DrawLine(p4, p6);
469 // Calculate whether or not the arrowheads are too crowded to put inside
470 // the extension lines. 9.0 is the length of the arrowhead.
471 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
472 //printf("Dimension::Draw(): t = %lf\n", t);
474 // On the screen, it's acting like this is actually 58%...
475 // This is correct, we want it to happen at > 50%
478 // Draw main dimension line + arrowheads
479 painter->DrawLine(p1, p2);
480 painter->DrawArrowhead(p1, p2, scaledThickness);
481 painter->DrawArrowhead(p2, p1, scaledThickness);
485 // Draw outside arrowheads
486 Point p7 = p1 - (unit * 9.0 * scaledThickness);
487 Point p8 = p2 + (unit * 9.0 * scaledThickness);
488 painter->DrawArrowhead(p1, p7, scaledThickness);
489 painter->DrawArrowhead(p2, p8, scaledThickness);
490 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
491 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
494 // Draw length of dimension line...
495 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
496 Point ctr = p2 + (Vector(p2, p1) / 2.0);
499 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
504 dimText = QString("%1\"").arg(length);
507 double feet = (double)((int)length / 12);
508 double inches = length - (feet * 12.0);
511 dimText = QString("%1'").arg(feet);
513 dimText = QString("%1' %2\"").arg(feet).arg(inches);
517 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
523 Text * t = (Text *)obj;
524 painter->DrawTextObject(t->p1, t->s.c_str(), scaledThickness);
534 void DrawingView::DeleteSelectedItems(void)
536 std::vector<void *>::iterator i = document.objects.begin();
538 while (i != document.objects.end())
540 Object * obj = (Object *)(*i);
545 document.objects.erase(i);
553 void DrawingView::ClearSelection(void)
555 std::vector<void *>::iterator i;
557 for(i=document.objects.begin(); i!=document.objects.end(); i++)
558 ((Object *)(*i))->selected = false;
562 void DrawingView::AddHoveredToSelection(void)
564 std::vector<void *>::iterator i;
566 for(i=document.objects.begin(); i!=document.objects.end(); i++)
568 if (((Object *)(*i))->hovered)
569 ((Object *)(*i))->selected = true;
574 void DrawingView::GetSelection(std::vector<void *> & v)
577 std::vector<void *>::iterator i;
579 for(i=v.begin(); i!=v.end(); i++)
581 if (((Object *)(*i))->selected)
587 void DrawingView::GetHovered(std::vector<void *> & v)
590 std::vector<void *>::iterator i;
592 for(i=v.begin(); i!=v.end(); i++)
594 if (((Object *)(*i))->hovered)
600 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
602 Global::screenSize = Vector(size().width(), size().height());
603 UpdateGridBackground();
607 void DrawingView::ToolMouse(int mode, Point p)
609 if (Global::tool == TTLine)
610 LineHandler(mode, p);
614 void DrawingView::ToolDraw(Painter * painter)
616 if (Global::tool == TTLine)
618 if (Global::toolState == TSNone)
620 painter->DrawHandle(toolPoint[0]);
622 else if ((Global::toolState == TSPoint2) && shiftDown)
624 painter->DrawHandle(toolPoint[1]);
628 painter->DrawLine(toolPoint[0], toolPoint[1]);
629 painter->DrawHandle(toolPoint[1]);
635 void DrawingView::LineHandler(int mode, Point p)
640 if (Global::toolState == TSNone)
647 if (Global::toolState == TSNone)
654 if (Global::toolState == TSNone)
656 Global::toolState = TSPoint2;
657 // Prevent spurious line from drawing...
658 toolPoint[1] = toolPoint[0];
660 else if ((Global::toolState == TSPoint2) && shiftDown)
662 toolPoint[0] = toolPoint[1];
666 Line * l = new Line(toolPoint[0], toolPoint[1]);
667 document.objects.push_back(l);
668 toolPoint[0] = toolPoint[1];
674 void DrawingView::mousePressEvent(QMouseEvent * event)
676 if (event->button() == Qt::LeftButton)
678 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
679 // collided = document.Collided(point);
682 // Do an update if collided with at least *one* object in the document
685 // Actually, we already know what we're going to click on as all the collision
686 // detection already happened in the mouse move function...!
687 //printf("MouseDown: ctrl=%s, numHovered=%i\n", (ctrlDown ? "DOWN" : "up"), numHovered);
692 if (Global::snapToGrid)
693 point = Global::SnapPointToGrid(point);
695 // We always snap to object points, and they take precendence over
697 if (Global::snapPointIsValid)
698 point = Global::snapPoint;
700 toolAction->MouseDown(point);
705 if (Global::snapToGrid)
706 point = SnapPointToGrid(point);
708 // Snap to object point if valid...
711 // ToolMouseDown(point);
712 ToolMouse(ToolMouseDown, point);
713 //Also, may want to figure out if hovering over a snap point on an object,
714 //snap to grid if not.
723 AddHoveredToSelection();
726 // Didn't hit any object and not using a tool, so do a selection rectangle
727 if (!(numHovered || Global::tool))
729 Global::selectionInProgress = true;
730 Global::selection.setTopLeft(QPointF(point.x, point.y));
731 Global::selection.setBottomRight(QPointF(point.x, point.y));
735 else if (event->button() == Qt::MiddleButton)
738 oldPoint = Vector(event->x(), event->y());
739 // Should also change the mouse pointer as well...
740 setCursor(Qt::SizeAllCursor);
745 void DrawingView::mouseMoveEvent(QMouseEvent * event)
747 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
748 Global::selection.setBottomRight(QPointF(point.x, point.y));
749 // Only needs to be done here, as mouse down is always preceded by movement
750 Global::snapPointIsValid = false;
753 if (event->buttons() & Qt::MiddleButton)
755 point = Vector(event->x(), event->y());
756 // Since we're using Qt coords for scrolling, we have to adjust them here to
757 // conform to Cartesian coords, since the origin is using Cartesian. :-)
758 Vector delta(oldPoint, point);
759 delta /= Global::zoom;
761 Global::origin -= delta;
763 UpdateGridBackground();
770 // Grid processing... (only snap here is left button is down)
772 // well, it causes problems with selecting lines that aren't close to a grid line!
774 // But even still, this is a bad approach, we need to not just do this for every
775 // case because it's WRONG to do it that way! !!! FIX !!!
776 if (/*(event->buttons() & Qt::LeftButton) &&*/ Global::snapToGrid)
778 point = SnapPointToGrid(point);
781 // Snap points on objects always take precedence over the grid, whether
782 // dragging an object or not...
784 if (Global::snapPointIsValid)
786 // Uncommenting this causes the cursor to become unresponsive after the first
788 // point = Global::snapPoint;
792 // Do checking here to see if object can be selected or not
793 if (Global::selectionInProgress)
795 std::vector<void *>::iterator i;
799 for(i=document.objects.begin(); i!=document.objects.end(); i++)
801 Object * obj = (Object *)(*i);
802 obj->selected = false;
809 Line * l = (Line *)obj;
811 if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
818 Circle * c = (Circle *)obj;
820 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))
827 Arc * a = (Arc *)obj;
829 double start = a->angle1;
830 double end = start + a->angle2;
831 QPointF p1(cos(start), sin(start));
832 QPointF p2(cos(end), sin(end));
833 QRectF bounds(p1, p2);
836 // Swap X/Y coordinates if they're backwards...
837 if (bounds.left() > bounds.right())
839 double temp = bounds.left();
840 bounds.setLeft(bounds.right());
841 bounds.setRight(temp);
844 if (bounds.bottom() > bounds.top())
846 double temp = bounds.bottom();
847 bounds.setBottom(bounds.top());
851 // Doesn't work as advertised! For shame!
852 bounds = bounds.normalized();
855 // If the end of the arc is before the beginning, add 360 degrees to it
859 // Adjust the bounds depending on which axes are crossed
860 if ((start < PI_OVER_2) && (end > PI_OVER_2))
863 if ((start < PI) && (end > PI))
864 bounds.setLeft(-1.0);
866 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
867 bounds.setBottom(-1.0);
869 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
870 bounds.setRight(1.0);
872 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
875 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
876 bounds.setLeft(-1.0);
878 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
879 bounds.setBottom(-1.0);
881 bounds.setTopLeft(QPointF(bounds.left() * a->radius, bounds.top() * a->radius));
882 bounds.setBottomRight(QPointF(bounds.right() * a->radius, bounds.bottom() * a->radius));
883 bounds.translate(a->p1.x, a->p1.y);
885 if (Global::selection.contains(bounds))
900 //we should keep track of the last point here and only pass this down *if* the point
904 // This returns true if we've moved over an object...
905 if (document.PointerMoved(point)) // <-- This
906 // This is where the object would do automagic dragging & shit. Since we don't
907 // do that anymore, we need a strategy to handle it.
911 Now objects handle mouse move snapping as well. The code below mainly works only
912 for tools; we need to fix it so that objects work as well...
914 There's a problem with the object point snapping in that it's dependent on the
915 order of the objects in the document. Most likely this is because it counts the
916 selected object last and thus fucks up the algorithm. Need to fix this...
920 // Do object snapping here. Grid snapping on mouse down is done in the
921 // objects themselves, only because we have to hit test the raw point,
922 // not the snapped point. There has to be a better way...!
923 if (document.penultimateObjectHovered)
925 // Two objects are hovered, see if we have an intersection point
926 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
929 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
933 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
934 Global::snapPointIsValid = true;
937 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
940 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
944 Global::snapPoint = p1;
945 Global::snapPointIsValid = true;
949 double d1 = Vector(point, p1).Magnitude();
950 double d2 = Vector(point, p2).Magnitude();
953 Global::snapPoint = p1;
955 Global::snapPoint = p2;
957 Global::snapPointIsValid = true;
963 // Otherwise, it was a single object hovered...
969 if (Global::snapToGrid)
970 point = Global::SnapPointToGrid(point);
972 // We always snap to object points, and they take precendence over
974 if (Global::snapPointIsValid)
975 point = Global::snapPoint;
977 toolAction->MouseMoved(point);
982 bool needUpdate = false;
984 // Don't do this kind of checking unless we're not doing a selection rectangle!
985 // Hmm, lines don't stay selected if globally selected... !!! FIX !!! [DONE]
986 // it's because there were extra state variables, the hit* vars...
987 if (!Global::selectionInProgress)
989 std::vector<void *>::iterator i;
992 for(i=document.objects.begin(); i!=document.objects.end(); i++)
994 Object * obj = (Object *)(*i);
995 // obj->selected = false;
1001 Line * l = (Line *)obj;
1003 // bool hitPoint1, hitPoint2, hitLine;
1004 l->hitPoint[0] = l->hitPoint[1] = l->hitObject = false;
1005 Vector lineSegment = l->p2 - l->p1;
1006 Vector v1 = point - l->p1;
1007 Vector v2 = point - l->p2;
1008 double t = Geometry::ParameterOfLineAndPoint(l->p1, l->p2, point);
1012 distance = v1.Magnitude();
1014 distance = v2.Magnitude();
1016 // distance = ?Det?(ls, v1) / |ls|
1017 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1018 / lineSegment.Magnitude());
1020 if ((v1.Magnitude() * Global::zoom) < 8.0)
1022 l->hitPoint[0] = true;
1023 // snapPoint = l->p1;
1024 // snapPointIsValid = true;
1026 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1028 l->hitPoint[1] = true;
1029 // snapPoint = l->p2;
1030 // snapPointIsValid = true;
1032 else if ((distance * Global::zoom) < 5.0)
1033 l->hitObject = true;
1035 bool oldHovered = l->hovered;
1036 l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
1038 if (oldHovered != l->hovered)
1045 Circle * c = (Circle *)obj;
1057 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1061 //printf("MouseMove: numHovered = %i\n", numHovered);
1066 // Need to do snapping, etc. as well
1067 // ToolMouseMove(point);
1068 ToolMouse(ToolMouseMove, point);
1071 // This is used to draw the tool crosshair...
1074 // if (/*document.NeedsUpdate() ||*/ Global::selectionInProgress /*|| toolAction*/)
1075 if (needUpdate || Global::selectionInProgress || Global::tool)
1080 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1082 if (event->button() == Qt::LeftButton)
1085 document.PointerReleased();
1088 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1089 //could set it up to use the document's update function (assumes that all object updates
1090 //are being reported correctly:
1091 // if (document.NeedsUpdate())
1093 update(); // Do an update if collided with at least *one* object in the document
1097 toolAction->MouseReleased();
1101 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1102 // ToolMouseUp(point);
1103 ToolMouse(ToolMouseUp, point);
1108 if (Global::selectionInProgress)
1110 // Select all the stuff inside of selection
1111 Global::selectionInProgress = false;
1113 // Clear our vectors
1118 std::vector<void *>::iterator i;
1120 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1122 if (((Object *)(*i))->selected)
1123 select.push_back(*i);
1125 //hmm, this is no good, too late to do any good :-P
1126 // if ((*i)->hovered)
1127 // hover.push_back(*i);
1131 else if (event->button() == Qt::MiddleButton)
1134 setCursor(Qt::ArrowCursor);
1139 void DrawingView::wheelEvent(QWheelEvent * event)
1141 double zoomFactor = 1.25;
1142 QSize sizeWin = size();
1143 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1144 center = Painter::QtToCartesianCoords(center);
1146 // This is not centering for some reason. Need to figure out why. :-/
1147 if (event->delta() > 0)
1149 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1150 Global::origin = newOrigin;
1151 Global::zoom *= zoomFactor;
1155 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1156 Global::origin = newOrigin;
1157 Global::zoom /= zoomFactor;
1161 // Global::gridSpacing = gridPixels / Painter::zoom;
1162 // UpdateGridBackground();
1163 SetGridSize(Global::gridSpacing * Global::zoom);
1165 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1170 void DrawingView::keyPressEvent(QKeyEvent * event)
1174 toolAction->KeyDown(event->key());
1176 bool oldShift = shiftDown;
1177 bool oldCtrl = ctrlDown;
1179 if (event->key() == Qt::Key_Shift)
1181 else if (event->key() == Qt::Key_Control)
1184 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1189 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1193 toolAction->KeyReleased(event->key());
1195 bool oldShift = shiftDown;
1196 bool oldCtrl = ctrlDown;
1198 if (event->key() == Qt::Key_Shift)
1200 else if (event->key() == Qt::Key_Control)
1203 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1208 // This looks strange, but it's really quite simple: We want a point that's
1209 // more than half-way to the next grid point to snap there while conversely we
1210 // want a point that's less than half-way to to the next grid point then snap
1211 // to the one before it. So we add half of the grid spacing to the point, then
1212 // divide by it so that we can remove the fractional part, then multiply it
1213 // back to get back to the correct answer.
1215 Point DrawingView::SnapPointToGrid(Point point)
1217 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
1218 point /= Global::gridSpacing;
1219 point.x = floor(point.x);//need to fix this for negative numbers...
1220 point.y = floor(point.y);
1221 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
1222 point *= Global::gridSpacing;