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 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
341 painter.DrawCrosshair(oldPoint);
347 if (Global::selectionInProgress)
349 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
350 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
351 painter.DrawRect(Global::selection);
357 void DrawingView::RenderObjects(Painter * painter, Container * c)
359 std::vector<void *>::iterator i;
361 for(i=c->objects.begin(); i!=c->objects.end(); i++)
363 Object * obj = (Object *)(*i);
364 float scaledThickness = Global::scale * obj->thickness;
365 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
366 painter->SetBrush(obj->color);
368 if (obj->selected || obj->hovered)
369 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
375 Line * l = (Line *)obj;
376 painter->DrawLine(l->p1, l->p2);
381 Circle * ci = (Circle *)obj;
382 painter->SetBrush(QBrush(Qt::NoBrush));
383 painter->DrawEllipse(ci->p1, ci->radius, ci->radius);
388 Arc * a = (Arc *)obj;
389 painter->DrawArc(a->p1, a->radius, a->angle1, a->angle2);
394 Dimension * d = (Dimension *)obj;
396 Vector v(d->p1, d->p2);
397 double angle = v.Angle();
398 Vector unit = v.Unit();
399 Vector linePt1 = d->p1, linePt2 = d->p2;
401 double x1, y1, length;
403 if (d->subtype == DTLinearVert)
405 if ((angle < 0) || (angle > PI))
407 x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
408 y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
409 ortho = Vector(1.0, 0);
414 x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
415 y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
416 ortho = Vector(-1.0, 0);
420 linePt1.x = linePt2.x = x1;
421 length = fabs(d->p1.y - d->p2.y);
423 else if (d->subtype == DTLinearHorz)
425 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
427 x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
428 y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
429 ortho = Vector(0, 1.0);
434 x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
435 y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
436 ortho = Vector(0, -1.0);
440 linePt1.y = linePt2.y = y1;
441 length = fabs(d->p1.x - d->p2.x);
443 else if (d->subtype == DTLinear)
445 angle = Vector(linePt1, linePt2).Angle();
446 ortho = Vector::Normal(linePt1, linePt2);
447 length = v.Magnitude();
450 unit = Vector(linePt1, linePt2).Unit();
452 Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
453 Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
454 Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
455 Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
456 Point p5 = d->p1 + (ortho * 4.0 * scaledThickness);
457 Point p6 = d->p2 + (ortho * 4.0 * scaledThickness);
460 The numbers hardcoded into here, what are they?
461 I believe they are pixels.
463 // Draw extension lines (if certain type)
464 painter->DrawLine(p3, p5);
465 painter->DrawLine(p4, p6);
467 // Calculate whether or not the arrowheads are too crowded to put inside
468 // the extension lines. 9.0 is the length of the arrowhead.
469 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
470 //printf("Dimension::Draw(): t = %lf\n", t);
472 // On the screen, it's acting like this is actually 58%...
473 // This is correct, we want it to happen at > 50%
476 // Draw main dimension line + arrowheads
477 painter->DrawLine(p1, p2);
478 painter->DrawArrowhead(p1, p2, scaledThickness);
479 painter->DrawArrowhead(p2, p1, scaledThickness);
483 // Draw outside arrowheads
484 Point p7 = p1 - (unit * 9.0 * scaledThickness);
485 Point p8 = p2 + (unit * 9.0 * scaledThickness);
486 painter->DrawArrowhead(p1, p7, scaledThickness);
487 painter->DrawArrowhead(p2, p8, scaledThickness);
488 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
489 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
492 // Draw length of dimension line...
493 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
494 Point ctr = p2 + (Vector(p2, p1) / 2.0);
497 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
502 dimText = QString("%1\"").arg(length);
505 double feet = (double)((int)length / 12);
506 double inches = length - (feet * 12.0);
509 dimText = QString("%1'").arg(feet);
511 dimText = QString("%1' %2\"").arg(feet).arg(inches);
515 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
521 Text * t = (Text *)obj;
522 painter->DrawTextObject(t->p1, t->s.c_str(), scaledThickness);
532 void DrawingView::DeleteSelectedItems(void)
534 std::vector<void *>::iterator i = document.objects.begin();
536 while (i != document.objects.end())
538 Object * obj = (Object *)(*i);
543 document.objects.erase(i);
551 void DrawingView::ClearSelection(void)
553 std::vector<void *>::iterator i;
555 for(i=document.objects.begin(); i!=document.objects.end(); i++)
556 ((Object *)(*i))->selected = false;
560 void DrawingView::AddHoveredToSelection(void)
562 std::vector<void *>::iterator i;
564 for(i=document.objects.begin(); i!=document.objects.end(); i++)
566 if (((Object *)(*i))->hovered)
567 ((Object *)(*i))->selected = true;
572 void DrawingView::GetSelection(std::vector<void *> & v)
575 std::vector<void *>::iterator i;
577 for(i=v.begin(); i!=v.end(); i++)
579 if (((Object *)(*i))->selected)
585 void DrawingView::GetHovered(std::vector<void *> & v)
588 std::vector<void *>::iterator i;
590 for(i=v.begin(); i!=v.end(); i++)
592 if (((Object *)(*i))->hovered)
598 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
600 Global::screenSize = Vector(size().width(), size().height());
601 UpdateGridBackground();
605 void DrawingView::ToolMouseDown(Point p)
607 if (Global::tool == TTLine)
609 if (Global::toolState == TSNone)
612 // toolState = TSPoint1;
613 // toolObject = (Object *)(new Line(p, Vector(0, 0)));
615 else //if (Global::toolState == TSPoint1)
623 void DrawingView::ToolMouseMove(Point p)
625 if (Global::tool == TTLine)
627 if (Global::toolState == TSNone)
635 void DrawingView::ToolMouseUp(Point p)
637 if (Global::tool == TTLine)
639 if (Global::toolState == TSNone)
641 Global::toolState = TSPoint2;
642 // Prevent spurious line from drawing...
643 toolPoint[1] = toolPoint[0];
647 Line * l = new Line(toolPoint[0], toolPoint[1]);
648 document.objects.push_back(l);
649 toolPoint[0] = toolPoint[1];
655 void DrawingView::ToolDraw(Painter * painter)
657 if (Global::tool == TTLine)
659 if (Global::toolState == TSNone)
661 painter->DrawHandle(toolPoint[0]);
665 painter->DrawLine(toolPoint[0], toolPoint[1]);
666 painter->DrawHandle(toolPoint[1]);
672 void DrawingView::mousePressEvent(QMouseEvent * event)
674 if (event->button() == Qt::LeftButton)
676 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
677 // collided = document.Collided(point);
680 // Do an update if collided with at least *one* object in the document
683 // Actually, we already know what we're going to click on as all the collision
684 // detection already happened in the mouse move function...!
685 //printf("MouseDown: ctrl=%s, numHovered=%i\n", (ctrlDown ? "DOWN" : "up"), numHovered);
690 if (Global::snapToGrid)
691 point = Global::SnapPointToGrid(point);
693 // We always snap to object points, and they take precendence over
695 if (Global::snapPointIsValid)
696 point = Global::snapPoint;
698 toolAction->MouseDown(point);
703 if (Global::snapToGrid)
704 point = SnapPointToGrid(point);
706 // Snap to object point if valid...
709 ToolMouseDown(point);
710 //Also, may want to figure out if hovering over a snap point on an object,
711 //snap to grid if not.
720 AddHoveredToSelection();
723 // Didn't hit any object and not using a tool, so do a selection rectangle
724 if (!(numHovered || Global::tool))
726 Global::selectionInProgress = true;
727 Global::selection.setTopLeft(QPointF(point.x, point.y));
728 Global::selection.setBottomRight(QPointF(point.x, point.y));
732 else if (event->button() == Qt::MiddleButton)
735 oldPoint = Vector(event->x(), event->y());
736 // Should also change the mouse pointer as well...
737 setCursor(Qt::SizeAllCursor);
742 void DrawingView::mouseMoveEvent(QMouseEvent * event)
744 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
745 Global::selection.setBottomRight(QPointF(point.x, point.y));
746 // Only needs to be done here, as mouse down is always preceded by movement
747 Global::snapPointIsValid = false;
750 if (event->buttons() & Qt::MiddleButton)
752 point = Vector(event->x(), event->y());
753 // Since we're using Qt coords for scrolling, we have to adjust them here to
754 // conform to Cartesian coords, since the origin is using Cartesian. :-)
755 Vector delta(oldPoint, point);
756 delta /= Global::zoom;
758 Global::origin -= delta;
760 UpdateGridBackground();
767 // Grid processing... (only snap here is left button is down)
769 if (/*(event->buttons() & Qt::LeftButton) &&*/ Global::snapToGrid)
771 point = SnapPointToGrid(point);
774 // Snap points on objects always take precedence over the grid, whether
775 // dragging an object or not...
777 if (Global::snapPointIsValid)
779 // Uncommenting this causes the cursor to become unresponsive after the first
781 // point = Global::snapPoint;
785 // Do checking here to see if object can be selected or not
786 if (Global::selectionInProgress)
788 std::vector<void *>::iterator i;
792 for(i=document.objects.begin(); i!=document.objects.end(); i++)
794 Object * obj = (Object *)(*i);
795 obj->selected = false;
802 Line * l = (Line *)obj;
804 if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
811 Circle * c = (Circle *)obj;
813 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))
820 Arc * a = (Arc *)obj;
822 double start = a->angle1;
823 double end = start + a->angle2;
824 QPointF p1(cos(start), sin(start));
825 QPointF p2(cos(end), sin(end));
826 QRectF bounds(p1, p2);
829 // Swap X/Y coordinates if they're backwards...
830 if (bounds.left() > bounds.right())
832 double temp = bounds.left();
833 bounds.setLeft(bounds.right());
834 bounds.setRight(temp);
837 if (bounds.bottom() > bounds.top())
839 double temp = bounds.bottom();
840 bounds.setBottom(bounds.top());
844 // Doesn't work as advertised! For shame!
845 bounds = bounds.normalized();
848 // If the end of the arc is before the beginning, add 360 degrees to it
852 // Adjust the bounds depending on which axes are crossed
853 if ((start < PI_OVER_2) && (end > PI_OVER_2))
856 if ((start < PI) && (end > PI))
857 bounds.setLeft(-1.0);
859 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
860 bounds.setBottom(-1.0);
862 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
863 bounds.setRight(1.0);
865 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
868 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
869 bounds.setLeft(-1.0);
871 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
872 bounds.setBottom(-1.0);
874 bounds.setTopLeft(QPointF(bounds.left() * a->radius, bounds.top() * a->radius));
875 bounds.setBottomRight(QPointF(bounds.right() * a->radius, bounds.bottom() * a->radius));
876 bounds.translate(a->p1.x, a->p1.y);
878 if (Global::selection.contains(bounds))
893 //we should keep track of the last point here and only pass this down *if* the point
897 // This returns true if we've moved over an object...
898 if (document.PointerMoved(point)) // <-- This
899 // This is where the object would do automagic dragging & shit. Since we don't
900 // do that anymore, we need a strategy to handle it.
904 Now objects handle mouse move snapping as well. The code below mainly works only
905 for tools; we need to fix it so that objects work as well...
907 There's a problem with the object point snapping in that it's dependent on the
908 order of the objects in the document. Most likely this is because it counts the
909 selected object last and thus fucks up the algorithm. Need to fix this...
913 // Do object snapping here. Grid snapping on mouse down is done in the
914 // objects themselves, only because we have to hit test the raw point,
915 // not the snapped point. There has to be a better way...!
916 if (document.penultimateObjectHovered)
918 // Two objects are hovered, see if we have an intersection point
919 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
922 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
926 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
927 Global::snapPointIsValid = true;
930 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
933 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
937 Global::snapPoint = p1;
938 Global::snapPointIsValid = true;
942 double d1 = Vector(point, p1).Magnitude();
943 double d2 = Vector(point, p2).Magnitude();
946 Global::snapPoint = p1;
948 Global::snapPoint = p2;
950 Global::snapPointIsValid = true;
956 // Otherwise, it was a single object hovered...
962 if (Global::snapToGrid)
963 point = Global::SnapPointToGrid(point);
965 // We always snap to object points, and they take precendence over
967 if (Global::snapPointIsValid)
968 point = Global::snapPoint;
970 toolAction->MouseMoved(point);
975 bool needUpdate = false;
977 // Don't do this kind of checking unless we're not doing a selection rectangle!
978 // Hmm, lines don't stay selected if globally selected... !!! FIX !!! [DONE]
979 // it's because there were extra state variables, the hit* vars...
980 if (!Global::selectionInProgress)
982 std::vector<void *>::iterator i;
985 for(i=document.objects.begin(); i!=document.objects.end(); i++)
987 Object * obj = (Object *)(*i);
988 // obj->selected = false;
994 Line * l = (Line *)obj;
996 // bool hitPoint1, hitPoint2, hitLine;
997 l->hitPoint[0] = l->hitPoint[1] = l->hitObject = false;
998 Vector lineSegment = l->p2 - l->p1;
999 Vector v1 = point - l->p1;
1000 Vector v2 = point - l->p2;
1001 double t = Geometry::ParameterOfLineAndPoint(l->p1, l->p2, point);
1005 distance = v1.Magnitude();
1007 distance = v2.Magnitude();
1009 // distance = ?Det?(ls, v1) / |ls|
1010 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1011 / lineSegment.Magnitude());
1013 if ((v1.Magnitude() * Global::zoom) < 8.0)
1015 l->hitPoint[0] = true;
1016 // snapPoint = l->p1;
1017 // snapPointIsValid = true;
1019 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1021 l->hitPoint[1] = true;
1022 // snapPoint = l->p2;
1023 // snapPointIsValid = true;
1025 else if ((distance * Global::zoom) < 5.0)
1026 l->hitObject = true;
1028 bool oldHovered = l->hovered;
1029 l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
1031 if (oldHovered != l->hovered)
1038 Circle * c = (Circle *)obj;
1050 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1054 //printf("MouseMove: numHovered = %i\n", numHovered);
1059 // Need to do snapping, etc. as well
1060 ToolMouseMove(point);
1063 // This is used to draw the tool crosshair...
1066 // if (/*document.NeedsUpdate() ||*/ Global::selectionInProgress /*|| toolAction*/)
1067 if (needUpdate || Global::selectionInProgress || Global::tool)
1072 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1074 if (event->button() == Qt::LeftButton)
1077 document.PointerReleased();
1080 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1081 //could set it up to use the document's update function (assumes that all object updates
1082 //are being reported correctly:
1083 // if (document.NeedsUpdate())
1085 update(); // Do an update if collided with at least *one* object in the document
1089 toolAction->MouseReleased();
1093 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1099 if (Global::selectionInProgress)
1101 // Select all the stuff inside of selection
1102 Global::selectionInProgress = false;
1104 // Clear our vectors
1109 std::vector<void *>::iterator i;
1111 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1113 if (((Object *)(*i))->selected)
1114 select.push_back(*i);
1116 //hmm, this is no good, too late to do any good :-P
1117 // if ((*i)->hovered)
1118 // hover.push_back(*i);
1122 else if (event->button() == Qt::MiddleButton)
1125 setCursor(Qt::ArrowCursor);
1130 void DrawingView::wheelEvent(QWheelEvent * event)
1132 double zoomFactor = 1.25;
1133 QSize sizeWin = size();
1134 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1135 center = Painter::QtToCartesianCoords(center);
1137 // This is not centering for some reason. Need to figure out why. :-/
1138 if (event->delta() > 0)
1140 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1141 Global::origin = newOrigin;
1142 Global::zoom *= zoomFactor;
1146 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1147 Global::origin = newOrigin;
1148 Global::zoom /= zoomFactor;
1152 // Global::gridSpacing = gridPixels / Painter::zoom;
1153 // UpdateGridBackground();
1154 SetGridSize(Global::gridSpacing * Global::zoom);
1156 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1161 void DrawingView::keyPressEvent(QKeyEvent * event)
1165 toolAction->KeyDown(event->key());
1167 if (event->key() == Qt::Key_Shift)
1169 else if (event->key() == Qt::Key_Control)
1174 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1178 toolAction->KeyReleased(event->key());
1180 if (event->key() == Qt::Key_Shift)
1182 else if (event->key() == Qt::Key_Control)
1187 // This looks strange, but it's really quite simple: We want a point that's
1188 // more than half-way to the next grid point to snap there while conversely we
1189 // want a point that's less than half-way to to the next grid point then snap
1190 // to the one before it. So we add half of the grid spacing to the point, then
1191 // divide by it so that we can remove the fractional part, then multiply it
1192 // back to get back to the correct answer.
1194 Point DrawingView::SnapPointToGrid(Point point)
1196 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
1197 point /= Global::gridSpacing;
1198 point.x = floor(point.x);//need to fix this for negative numbers...
1199 point.y = floor(point.y);
1200 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
1201 point *= Global::gridSpacing;