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"
41 #define BACKGROUND_MAX_SIZE 512
44 //Container DrawingView::document(Vector(0, 0));
47 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
48 // The value in the settings file will override this.
49 useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
51 gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
52 scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)),
53 gridPixels(0), collided(false)//, toolAction(NULL)
55 // document.isTopLevelContainer = true;
56 //wtf? doesn't work except in c++11??? document = { 0 };
57 setBackgroundRole(QPalette::Base);
58 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
60 Global::gridSpacing = 12.0; // In base units (inch is default)
63 Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
65 document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
66 document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
67 document.Add(new Circle(Vector(100, 100), 36, &document));
68 document.Add(new Circle(Vector(50, 150), 49, &document));
69 document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
70 document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
72 Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
73 line->SetDimensionOnLine(dimension);
74 document.Add(dimension);
76 // Alternate way to do the above...
77 line->SetDimensionOnLine();
80 Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
81 line->p[0] = Vector(5, 5);
82 line->p[1] = Vector(50, 40);
84 line->thickness = 2.0;
86 line->color = 0xFF7F00;
87 document.objects.push_back(line);
88 document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
89 document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
90 document.objects.push_back(new Circle(Vector(100, 100), 36));
91 document.objects.push_back(new Circle(Vector(50, 150), 49));
92 document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
93 document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
94 document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
95 document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
99 Here we set the grid size in pixels--12 in this case. Initially, we have our
100 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
101 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
102 to be able to set the size of the background grid (which we do here at an
103 arbitrary 12 pixels) to anything we want (within reason, of course :-).
105 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
107 drawing->gridSpacing = 12.0 / Global::zoom;
109 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
110 translated to Cartesian coordinates through this. (Initially, Global::zoom is
111 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
113 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
114 convenience function than any measure of absolutes. Doing things that way we
115 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
116 shittiness that comes with it.
118 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
119 a certain way, which means we should probably create something else in those
120 objects to take its place--like some kind of scale factor. This would seem to
121 imply that certain point sizes actually *do* tie things like fonts to absolute
122 sizes on the screen, but not necessarily because you could have an inch scale
123 with text that is quite small relative to other objects on the screen, which
124 currently you have to zoom in to see (and which blows up the text). Point sizes
125 in an application like this are a bit meaningless; even though an inch is an
126 inch regardless of the zoom level a piece of text can be larger or smaller than
127 this. Maybe this is the case for having a base unit and basing point sizes off
130 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
131 base units. What that means is that if you have a 12px grid with a 6" grid size
132 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
134 Dimensions now have a "size" parameter to set their absolute size in relation
135 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
136 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
137 scaled the same way as the arrowheads.
139 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
140 need a thickness parameter similar to the "size" param for dimensions. (And now
144 SetGridSize(12); // This is in pixels
149 void DrawingView::SetToolActive(Action * action)
154 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
155 SLOT(AddNewObjectToDocument(Object *)));
156 connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
162 void DrawingView::SetGridSize(uint32_t size)
165 if (size == gridPixels)
168 // Recreate the background bitmap
170 QPainter pmp(&gridBackground);
171 pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
172 pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
174 for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
176 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
177 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
182 // Set up new BG brush & zoom level (pixels per base unit)
183 // Painter::zoom = gridPixels / gridSpacing;
184 Global::zoom = gridPixels / Global::gridSpacing;
185 UpdateGridBackground();
189 void DrawingView::UpdateGridBackground(void)
191 // Transform the origin to Qt coordinates
192 Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
193 int x = (int)pixmapOrigin.x;
194 int y = (int)pixmapOrigin.y;
195 // Use mod arithmetic to grab the correct swatch of background
197 Negative numbers still screw it up... Need to think about what we're
198 trying to do here. The fact that it worked with 72 seems to have been pure luck.
199 It seems the problem is negative numbers: We can't let that happen.
200 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
203 The bitmap looks like this:
213 @ x = 1, we want it to look like:
215 -+---+---+---+---+---
218 -+---+---+---+---+---
223 Which means we need to grab the sample from x = 3. @ x = -1:
233 Which means we need to grab the sample from x = 1. Which means we have to take
234 the mirror of the modulus of gridPixels.
236 Doing a mod of a negative number is problematic: 1st, the compiler converts the
237 negative number to an unsigned int, then it does the mod. Gets you wrong answers
238 most of the time, unless you use a power of 2. :-P So what we do here is just
239 take the modulus of the negation, which means we don't have to worry about
242 The positive case looks gruesome (and it is) but it boils down to this: We take
243 the modulus of the X coordinate, then mirror it by subtraction from the
244 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
245 gridPixels. But we need the case where the result equalling gridPixels to be
246 zero; so we do another modulus operation on the result to achieve this.
251 x = (gridPixels - (x % gridPixels)) % gridPixels;
256 y = (gridPixels - (y % gridPixels)) % gridPixels;
258 // Here we grab a section of the bigger pixmap, so that the background
259 // *looks* like it's scrolling...
260 QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
261 QPalette pal = palette();
262 pal.setBrush(backgroundRole(), QBrush(pm));
263 setAutoFillBackground(true);
268 void DrawingView::SetCurrentLayer(int layer)
270 Global::currentLayer = layer;
271 //printf("DrawingView::CurrentLayer = %i\n", layer);
275 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
277 // This is undoing the transform, e.g. going from client coords to local coords.
278 // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
279 // conversion of the y-axis from increasing bottom to top.
280 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
284 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
286 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
287 // No voodoo here, it's just grouped wrong to see it. It should be:
288 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
289 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
293 void DrawingView::paintEvent(QPaintEvent * /*event*/)
295 QPainter qtPainter(this);
296 Painter painter(&qtPainter);
299 qtPainter.setRenderHint(QPainter::Antialiasing);
301 Global::viewportHeight = size().height();
303 // Draw coordinate axes
304 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
305 painter.DrawLine(0, -16384, 0, 16384);
306 painter.DrawLine(-16384, 0, 16384, 0);
308 // Do object rendering...
309 RenderObjects(&painter, document.objects);
311 // Do tool rendering, if any...
314 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
315 painter.DrawCrosshair(oldPoint);
319 // Do selection rectangle rendering, if any
320 if (Global::selectionInProgress)
322 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
323 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
324 painter.DrawRect(Global::selection);
327 if (!informativeText.isEmpty())
328 painter.DrawInformativeText(informativeText);
333 // Renders objects in the passed in vector
335 void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
337 std::vector<void *>::iterator i;
339 for(i=v.begin(); i!=v.end(); i++)
341 Object * obj = (Object *)(*i);
342 float scaledThickness = Global::scale * obj->thickness;
344 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
346 painter->SetPen(0x00FF00, 2.0, LSSolid);
350 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
351 painter->SetBrush(obj->color);
353 if (obj->selected || obj->hitObject)
354 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
360 painter->DrawLine(obj->p[0], obj->p[1]);
362 if (obj->hitPoint[0])
363 painter->DrawHandle(obj->p[0]);
365 if (obj->hitPoint[1])
366 painter->DrawHandle(obj->p[1]);
370 painter->SetBrush(QBrush(Qt::NoBrush));
371 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
373 if (obj->hitPoint[0])
374 painter->DrawHandle(obj->p[0]);
378 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
380 if (obj->hitPoint[0])
381 painter->DrawHandle(obj->p[0]);
383 if (obj->hitPoint[1])
384 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
386 if (obj->hitPoint[2])
387 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
392 Dimension * d = (Dimension *)obj;
394 Vector v(d->p[0], d->p[1]);
395 double angle = v.Angle();
396 Vector unit = v.Unit();
397 Vector linePt1 = d->p[0], linePt2 = d->p[1];
399 double x1, y1, length;
401 if (d->subtype == DTLinearVert)
403 if ((angle < 0) || (angle > PI))
405 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
406 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
407 ortho = Vector(1.0, 0);
412 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
413 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
414 ortho = Vector(-1.0, 0);
418 linePt1.x = linePt2.x = x1;
419 length = fabs(d->p[0].y - d->p[1].y);
421 else if (d->subtype == DTLinearHorz)
423 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
425 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
426 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
427 ortho = Vector(0, 1.0);
432 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
433 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
434 ortho = Vector(0, -1.0);
438 linePt1.y = linePt2.y = y1;
439 length = fabs(d->p[0].x - d->p[1].x);
441 else if (d->subtype == DTLinear)
443 angle = Vector(linePt1, linePt2).Angle();
444 ortho = Vector::Normal(linePt1, linePt2);
445 length = v.Magnitude();
448 unit = Vector(linePt1, linePt2).Unit();
450 Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
451 Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
452 Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
453 Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
454 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
455 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
458 The numbers hardcoded into here, what are they?
459 I believe they are pixels.
461 // Draw extension lines (if certain type)
462 painter->DrawLine(p3, p5);
463 painter->DrawLine(p4, p6);
465 // Calculate whether or not the arrowheads are too crowded to put inside
466 // the extension lines. 9.0 is the length of the arrowhead.
467 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
468 //printf("Dimension::Draw(): t = %lf\n", t);
470 // On the screen, it's acting like this is actually 58%...
471 // This is correct, we want it to happen at > 50%
474 // Draw main dimension line + arrowheads
475 painter->DrawLine(p1, p2);
476 painter->DrawArrowhead(p1, p2, scaledThickness);
477 painter->DrawArrowhead(p2, p1, scaledThickness);
481 // Draw outside arrowheads
482 Point p7 = p1 - (unit * 9.0 * scaledThickness);
483 Point p8 = p2 + (unit * 9.0 * scaledThickness);
484 painter->DrawArrowhead(p1, p7, scaledThickness);
485 painter->DrawArrowhead(p2, p8, scaledThickness);
486 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
487 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
490 // Draw length of dimension line...
491 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
492 Point ctr = p2 + (Vector(p2, p1) / 2.0);
495 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
500 dimText = QString("%1\"").arg(length);
503 double feet = (double)((int)length / 12);
504 double inches = length - (feet * 12.0);
507 dimText = QString("%1'").arg(feet);
509 dimText = QString("%1' %2\"").arg(feet).arg(inches);
513 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
519 Text * t = (Text *)obj;
520 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
530 void DrawingView::AddHoveredToSelection(void)
532 std::vector<void *>::iterator i;
534 for(i=document.objects.begin(); i!=document.objects.end(); i++)
536 if (((Object *)(*i))->hovered)
537 ((Object *)(*i))->selected = true;
542 void DrawingView::GetSelection(std::vector<void *> & v)
545 std::vector<void *>::iterator i;
547 for(i=document.objects.begin(); i!=document.objects.end(); i++)
549 if (((Object *)(*i))->selected)
555 void DrawingView::GetHovered(std::vector<void *> & v)
558 std::vector<void *>::iterator i;
560 for(i=document.objects.begin(); i!=document.objects.end(); i++)
562 if (((Object *)(*i))->hovered)
564 //printf("GetHovered: adding object (%X) to hover... hp1=%s, hp2=%s, hl=%s\n", (*i), (((Line *)(*i))->hitPoint[0] ? "true" : "false"), (((Line *)(*i))->hitPoint[1] ? "true" : "false"), (((Line *)(*i))->hitObject ? "true" : "false"));
571 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
573 Global::screenSize = Vector(size().width(), size().height());
574 UpdateGridBackground();
578 void DrawingView::ToolHandler(int mode, Point p)
580 if (Global::tool == TTLine)
581 LineHandler(mode, p);
582 else if (Global::tool == TTCircle)
583 CircleHandler(mode, p);
584 else if (Global::tool == TTArc)
586 else if (Global::tool == TTRotate)
587 RotateHandler(mode, p);
588 else if (Global::tool == TTMirror)
589 MirrorHandler(mode, p);
593 void DrawingView::ToolDraw(Painter * painter)
595 if (Global::tool == TTLine)
597 if (Global::toolState == TSNone)
599 painter->DrawHandle(toolPoint[0]);
601 else if ((Global::toolState == TSPoint2) && shiftDown)
603 painter->DrawHandle(toolPoint[1]);
607 painter->DrawLine(toolPoint[0], toolPoint[1]);
608 painter->DrawHandle(toolPoint[1]);
610 Vector v(toolPoint[0], toolPoint[1]);
611 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
612 double absLength = v.Magnitude();
613 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
614 informativeText = text.arg(absLength).arg(absAngle);
617 else if (Global::tool == TTCircle)
619 if (Global::toolState == TSNone)
621 painter->DrawHandle(toolPoint[0]);
623 else if ((Global::toolState == TSPoint2) && shiftDown)
625 painter->DrawHandle(toolPoint[1]);
629 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
630 // painter->DrawLine(toolPoint[0], toolPoint[1]);
631 // painter->DrawHandle(toolPoint[1]);
632 painter->SetBrush(QBrush(Qt::NoBrush));
633 painter->DrawEllipse(toolPoint[0], length, length);
634 QString text = tr("Radius: %1 in.");//\n") + QChar(0x2221) + tr(": %2");
635 informativeText = text.arg(length);//.arg(absAngle);
638 else if (Global::tool == TTArc)
640 if (Global::toolState == TSNone)
642 painter->DrawHandle(toolPoint[0]);
644 else if (Global::toolState == TSPoint2)
646 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
647 painter->SetBrush(QBrush(Qt::NoBrush));
648 painter->DrawEllipse(toolPoint[0], length, length);
649 painter->DrawLine(toolPoint[0], toolPoint[1]);
650 painter->DrawHandle(toolPoint[1]);
651 QString text = tr("Radius: %1 in.");
652 informativeText = text.arg(length);
654 else if (Global::toolState == TSPoint3)
656 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
657 painter->DrawLine(toolPoint[0], toolPoint[2]);
658 painter->SetBrush(QBrush(Qt::NoBrush));
659 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
660 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
661 QString text = tr("Angle start: %1") + QChar(0x00B0);
662 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
666 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
667 double span = angle - toolPoint[2].x;
672 painter->DrawLine(toolPoint[0], toolPoint[3]);
673 painter->SetBrush(QBrush(Qt::NoBrush));
674 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
675 painter->SetPen(0xFF00FF, 2.0, LSSolid);
676 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
677 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
678 QString text = tr("Arc span: %1") + QChar(0x00B0);
679 informativeText = text.arg(RADIANS_TO_DEGREES * span);
682 else if (Global::tool == TTRotate)
684 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
685 painter->DrawHandle(toolPoint[0]);
686 else if ((Global::toolState == TSPoint2) && shiftDown)
687 painter->DrawHandle(toolPoint[1]);
690 if (toolPoint[0] == toolPoint[1])
693 painter->DrawLine(toolPoint[0], toolPoint[1]);
694 // Likely we need a tool container for this... (now we do!)
698 painter->SetPen(0x00FF00, 2.0, LSSolid);
699 overrideColor = true;
702 RenderObjects(painter, toolObjects);
703 overrideColor = false;
706 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
708 QString text = QChar(0x2221) + QObject::tr(": %1");
709 informativeText = text.arg(absAngle);
712 informativeText += " (Copy)";
715 else if (Global::tool == TTMirror)
717 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
718 painter->DrawHandle(toolPoint[0]);
719 else if ((Global::toolState == TSPoint2) && shiftDown)
720 painter->DrawHandle(toolPoint[1]);
723 if (toolPoint[0] == toolPoint[1])
726 painter->DrawLine(toolPoint[0], toolPoint[1]);
727 // Likely we need a tool container for this... (now we do!)
731 painter->SetPen(0x00FF00, 2.0, LSSolid);
732 overrideColor = true;
735 RenderObjects(painter, toolObjects);
736 overrideColor = false;
739 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
741 QString text = QChar(0x2221) + QObject::tr(": %1");
742 informativeText = text.arg(absAngle);
745 informativeText += " (Copy)";
751 void DrawingView::LineHandler(int mode, Point p)
756 if (Global::toolState == TSNone)
763 if (Global::toolState == TSNone)
770 if (Global::toolState == TSNone)
772 Global::toolState = TSPoint2;
773 // Prevent spurious line from drawing...
774 toolPoint[1] = toolPoint[0];
776 else if ((Global::toolState == TSPoint2) && shiftDown)
778 // Key override is telling us to make a new line, not continue the
780 toolPoint[0] = toolPoint[1];
784 Line * l = new Line(toolPoint[0], toolPoint[1]);
785 document.objects.push_back(l);
786 toolPoint[0] = toolPoint[1];
792 void DrawingView::CircleHandler(int mode, Point p)
797 if (Global::toolState == TSNone)
804 if (Global::toolState == TSNone)
811 if (Global::toolState == TSNone)
813 Global::toolState = TSPoint2;
814 // Prevent spurious line from drawing...
815 toolPoint[1] = toolPoint[0];
817 else if ((Global::toolState == TSPoint2) && shiftDown)
819 // Key override is telling us to make a new line, not continue the
821 toolPoint[0] = toolPoint[1];
825 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
826 Circle * c = new Circle(toolPoint[0], length);
827 document.objects.push_back(c);
828 toolPoint[0] = toolPoint[1];
829 Global::toolState = TSNone;
835 void DrawingView::ArcHandler(int mode, Point p)
840 if (Global::toolState == TSNone)
842 else if (Global::toolState == TSPoint2)
844 else if (Global::toolState == TSPoint3)
851 if (Global::toolState == TSNone)
853 else if (Global::toolState == TSPoint2)
855 else if (Global::toolState == TSPoint3)
862 if (Global::toolState == TSNone)
864 // Prevent spurious line from drawing...
865 toolPoint[1] = toolPoint[0];
866 Global::toolState = TSPoint2;
868 else if (Global::toolState == TSPoint2)
872 // Key override is telling us to start circle at new center, not
873 // continue the current one.
874 toolPoint[0] = toolPoint[1];
878 // Set the radius in toolPoint[1].x
879 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
880 Global::toolState = TSPoint3;
882 else if (Global::toolState == TSPoint3)
884 // Set the angle in toolPoint[2].x
885 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
886 Global::toolState = TSPoint4;
890 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
891 double span = endAngle - toolPoint[2].x;
896 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
897 document.objects.push_back(arc);
898 Global::toolState = TSNone;
904 void DrawingView::RotateHandler(int mode, Point p)
909 if (Global::toolState == TSNone)
912 SavePointsFrom(select, toolScratch);
913 Global::toolState = TSPoint1;
915 else if (Global::toolState == TSPoint1)
922 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
924 else if (Global::toolState == TSPoint2)
931 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
932 std::vector<void *>::iterator j = select.begin();
933 std::vector<Object>::iterator i = toolScratch.begin();
935 for(; i!=toolScratch.end(); i++, j++)
938 Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
939 Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
940 Object * obj2 = (Object *)(*j);
944 if (obj.type == OTArc)
946 obj2->angle[0] = obj.angle[0] + angle;
948 if (obj2->angle[0] > PI_TIMES_2)
949 obj2->angle[0] -= PI_TIMES_2;
956 if (Global::toolState == TSPoint1)
958 Global::toolState = TSPoint2;
959 // Prevent spurious line from drawing...
960 toolPoint[1] = toolPoint[0];
962 else if ((Global::toolState == TSPoint2) && shiftDown)
964 // Key override is telling us to make a new line, not continue the
966 toolPoint[0] = toolPoint[1];
970 // Either we're finished with our rotate, or we're stamping a copy.
973 // Stamp a copy of the selection at the current rotation & bail
974 std::vector<void *> temp;
975 CopyObjects(select, temp);
977 AddObjectsTo(document.objects, temp);
978 RestorePointsTo(select, toolScratch);
983 Global::toolState = TSPoint1;
984 SavePointsFrom(select, toolScratch);
989 // Reset the selection if shift held down...
991 RestorePointsTo(select, toolScratch);
995 // Reset selection when key is let up
997 RotateHandler(ToolMouseMove, toolPoint[1]);
1001 RestorePointsTo(select, toolScratch);
1006 void DrawingView::MirrorHandler(int mode, Point p)
1011 if (Global::toolState == TSNone)
1014 SavePointsFrom(select, toolScratch);
1015 Global::toolState = TSPoint1;
1017 else if (Global::toolState == TSPoint1)
1024 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1026 else if (Global::toolState == TSPoint2)
1033 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1034 std::vector<void *>::iterator j = select.begin();
1035 std::vector<Object>::iterator i = toolScratch.begin();
1037 for(; i!=toolScratch.end(); i++, j++)
1040 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1041 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1042 Object * obj2 = (Object *)(*j);
1046 if (obj.type == OTArc)
1049 // This is WRONG for mirroring...
1050 obj2->angle[0] = obj.angle[0] + angle;
1052 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1054 if (obj2->angle[0] > PI_TIMES_2)
1055 obj2->angle[0] -= PI_TIMES_2;
1062 if (Global::toolState == TSPoint1)
1064 Global::toolState = TSPoint2;
1065 // Prevent spurious line from drawing...
1066 toolPoint[1] = toolPoint[0];
1068 else if ((Global::toolState == TSPoint2) && shiftDown)
1070 // Key override is telling us to make a new line, not continue the
1072 toolPoint[0] = toolPoint[1];
1076 // Either we're finished with our rotate, or we're stamping a copy.
1079 // Stamp a copy of the selection at the current rotation & bail
1080 std::vector<void *> temp;
1081 CopyObjects(select, temp);
1082 ClearSelected(temp);
1083 AddObjectsTo(document.objects, temp);
1084 RestorePointsTo(select, toolScratch);
1089 Global::toolState = TSPoint1;
1090 SavePointsFrom(select, toolScratch);
1095 // Reset the selection if shift held down...
1097 RestorePointsTo(select, toolScratch);
1101 // Reset selection when key is let up
1103 MirrorHandler(ToolMouseMove, toolPoint[1]);
1107 RestorePointsTo(select, toolScratch);
1112 void DrawingView::mousePressEvent(QMouseEvent * event)
1114 if (event->button() == Qt::LeftButton)
1116 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1118 // Handle tool processing, if any
1121 if (Global::snapToGrid)
1122 point = SnapPointToGrid(point);
1124 //Also, may want to figure out if hovering over a snap point on an object,
1125 //snap to grid if not.
1126 // Snap to object point if valid...
1127 // if (Global::snapPointIsValid)
1128 // point = Global::snapPoint;
1130 ToolHandler(ToolMouseDown, point);
1134 // Clear the selection only if CTRL isn't being held on click
1136 ClearSelected(document.objects);
1137 // ClearSelection();
1139 // If any objects are being hovered on click, add them to the selection
1143 AddHoveredToSelection();
1144 update(); // needed??
1145 GetHovered(hover); // prolly needed
1147 // Needed for grab & moving objects
1148 // We do it *after*... why? (doesn't seem to confer any advantage...)
1149 if (Global::snapToGrid)
1150 oldPoint = SnapPointToGrid(point);
1155 // Didn't hit any object and not using a tool, so do a selection rectangle
1156 Global::selectionInProgress = true;
1157 Global::selection.setTopLeft(QPointF(point.x, point.y));
1158 Global::selection.setBottomRight(QPointF(point.x, point.y));
1160 else if (event->button() == Qt::MiddleButton)
1163 oldPoint = Vector(event->x(), event->y());
1164 // Should also change the mouse pointer as well...
1165 setCursor(Qt::SizeAllCursor);
1170 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1172 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1173 Global::selection.setBottomRight(QPointF(point.x, point.y));
1174 // Only needs to be done here, as mouse down is always preceded by movement
1175 Global::snapPointIsValid = false;
1178 if (event->buttons() & Qt::MiddleButton)
1180 point = Vector(event->x(), event->y());
1181 // Since we're using Qt coords for scrolling, we have to adjust them here to
1182 // conform to Cartesian coords, since the origin is using Cartesian. :-)
1183 Vector delta(oldPoint, point);
1184 delta /= Global::zoom;
1186 Global::origin -= delta;
1188 UpdateGridBackground();
1194 // If we're doing a selection rect, see if any objects are engulfed by it
1195 // (implies left mouse button held down)
1196 if (Global::selectionInProgress)
1198 CheckObjectBounds();
1203 // Handle object movement (left button down & over an object)
1204 if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
1206 if (Global::snapToGrid)
1207 point = SnapPointToGrid(point);
1209 HandleObjectMovement(point);
1215 // Do object hit testing...
1216 bool needUpdate = HitTestObjects(point);
1218 // Do tool handling, if any are active...
1221 if (Global::snapToGrid)
1222 point = SnapPointToGrid(point);
1224 ToolHandler(ToolMouseMove, point);
1227 // This is used to draw the tool crosshair...
1230 if (needUpdate || Global::tool)
1235 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1237 if (event->button() == Qt::LeftButton)
1239 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1240 //could set it up to use the document's update function (assumes that all object updates
1241 //are being reported correctly:
1242 // if (document.NeedsUpdate())
1243 // Do an update if collided with at least *one* object in the document
1249 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1250 ToolHandler(ToolMouseUp, point);
1254 if (Global::selectionInProgress)
1255 Global::selectionInProgress = false;
1257 informativeText.clear();
1258 // Should we be doing this automagically? Hmm...
1259 // Clear our vectors
1264 std::vector<void *>::iterator i;
1266 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1268 if (((Object *)(*i))->selected)
1269 select.push_back(*i);
1271 //hmm, this is no good, too late to do any good :-P
1272 // if ((*i)->hovered)
1273 // hover.push_back(*i);
1276 else if (event->button() == Qt::MiddleButton)
1279 setCursor(Qt::ArrowCursor);
1284 void DrawingView::wheelEvent(QWheelEvent * event)
1286 double zoomFactor = 1.25;
1287 QSize sizeWin = size();
1288 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1289 center = Painter::QtToCartesianCoords(center);
1291 // This is not centering for some reason. Need to figure out why. :-/
1292 if (event->delta() > 0)
1294 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1295 Global::origin = newOrigin;
1296 Global::zoom *= zoomFactor;
1300 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1301 Global::origin = newOrigin;
1302 Global::zoom /= zoomFactor;
1305 // Global::gridSpacing = gridPixels / Painter::zoom;
1306 // UpdateGridBackground();
1307 SetGridSize(Global::gridSpacing * Global::zoom);
1309 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1313 void DrawingView::keyPressEvent(QKeyEvent * event)
1315 bool oldShift = shiftDown;
1316 bool oldCtrl = ctrlDown;
1318 if (event->key() == Qt::Key_Shift)
1320 else if (event->key() == Qt::Key_Control)
1323 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1326 ToolHandler(ToolKeyDown, Point(0, 0));
1333 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1335 bool oldShift = shiftDown;
1336 bool oldCtrl = ctrlDown;
1338 if (event->key() == Qt::Key_Shift)
1340 else if (event->key() == Qt::Key_Control)
1343 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1346 ToolHandler(ToolKeyUp, Point(0, 0));
1354 // This looks strange, but it's really quite simple: We want a point that's
1355 // more than half-way to the next grid point to snap there while conversely we
1356 // want a point that's less than half-way to to the next grid point then snap
1357 // to the one before it. So we add half of the grid spacing to the point, then
1358 // divide by it so that we can remove the fractional part, then multiply it
1359 // back to get back to the correct answer.
1361 Point DrawingView::SnapPointToGrid(Point point)
1363 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
1364 point /= Global::gridSpacing;
1365 point.x = floor(point.x);//need to fix this for negative numbers...
1366 point.y = floor(point.y);
1367 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
1368 point *= Global::gridSpacing;
1373 void DrawingView::CheckObjectBounds(void)
1375 std::vector<void *>::iterator i;
1378 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1380 Object * obj = (Object *)(*i);
1381 obj->selected = false;
1387 Line * l = (Line *)obj;
1389 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1396 Circle * c = (Circle *)obj;
1398 if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0]))
1405 Arc * a = (Arc *)obj;
1407 double start = a->angle[0];
1408 double end = start + a->angle[1];
1409 QPointF p1(cos(start), sin(start));
1410 QPointF p2(cos(end), sin(end));
1411 QRectF bounds(p1, p2);
1414 // Swap X/Y coordinates if they're backwards...
1415 if (bounds.left() > bounds.right())
1417 double temp = bounds.left();
1418 bounds.setLeft(bounds.right());
1419 bounds.setRight(temp);
1422 if (bounds.bottom() > bounds.top())
1424 double temp = bounds.bottom();
1425 bounds.setBottom(bounds.top());
1426 bounds.setTop(temp);
1429 // Doesn't work as advertised! For shame!
1430 bounds = bounds.normalized();
1433 // If the end of the arc is before the beginning, add 360 degrees to it
1437 // Adjust the bounds depending on which axes are crossed
1438 if ((start < PI_OVER_2) && (end > PI_OVER_2))
1441 if ((start < PI) && (end > PI))
1442 bounds.setLeft(-1.0);
1444 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
1445 bounds.setBottom(-1.0);
1447 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
1448 bounds.setRight(1.0);
1450 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
1453 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
1454 bounds.setLeft(-1.0);
1456 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
1457 bounds.setBottom(-1.0);
1459 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1460 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1461 bounds.translate(a->p[0].x, a->p[0].y);
1463 if (Global::selection.contains(bounds))
1478 bool DrawingView::HitTestObjects(Point point)
1480 std::vector<void *>::iterator i;
1482 bool needUpdate = false;
1484 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1486 Object * obj = (Object *)(*i);
1492 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1493 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1494 Vector lineSegment = obj->p[1] - obj->p[0];
1495 Vector v1 = point - obj->p[0];
1496 Vector v2 = point - obj->p[1];
1497 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1501 distance = v1.Magnitude();
1503 distance = v2.Magnitude();
1505 // distance = ?Det?(ls, v1) / |ls|
1506 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1507 / lineSegment.Magnitude());
1509 if ((v1.Magnitude() * Global::zoom) < 8.0)
1510 obj->hitPoint[0] = true;
1511 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1512 obj->hitPoint[1] = true;
1513 else if ((distance * Global::zoom) < 5.0)
1514 obj->hitObject = true;
1516 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1518 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1525 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1526 obj->hitPoint[0] = obj->hitObject = false;
1527 double length = Vector::Magnitude(obj->p[0], point);
1529 if ((length * Global::zoom) < 8.0)
1530 obj->hitPoint[0] = true;
1531 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1532 obj->hitObject = true;
1534 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1536 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1543 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
1544 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
1545 double length = Vector::Magnitude(obj->p[0], point);
1546 double angle = Vector::Angle(obj->p[0], point);
1548 // Make sure we get the angle in the correct spot
1549 if (angle < obj->angle[0])
1550 angle += PI_TIMES_2;
1552 // Get the span that we're pointing at...
1553 double span = angle - obj->angle[0];
1555 // N.B.: Still need to hit test the arc start & arc span handles...
1556 double spanAngle = obj->angle[0] + obj->angle[1];
1557 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
1558 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
1559 double length2 = Vector::Magnitude(point, handle1);
1560 double length3 = Vector::Magnitude(point, handle2);
1562 if ((length * Global::zoom) < 8.0)
1563 obj->hitPoint[0] = true;
1564 else if ((length2 * Global::zoom) < 8.0)
1565 obj->hitPoint[1] = true;
1566 else if ((length3 * Global::zoom) < 8.0)
1567 obj->hitPoint[2] = true;
1568 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
1569 obj->hitObject = true;
1571 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
1573 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
1585 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1593 void DrawingView::HandleObjectMovement(Point point)
1595 Point delta = point - oldPoint;
1596 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
1597 Object * obj = (Object *)hover[0];
1598 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
1599 //printf("Object (%X) move: hp1=%s, hp2=%s, hl=%s\n", obj, (obj->hitPoint[0] ? "true" : "false"), (obj->hitPoint[1] ? "true" : "false"), (obj->hitObject ? "true" : "false"));
1604 if (obj->hitPoint[0])
1606 else if (obj->hitPoint[1])
1608 else if (obj->hitObject)
1616 if (obj->hitPoint[0])
1618 else if (obj->hitObject)
1620 //this doesn't work. we need to save this on mouse down for this to work correctly!
1621 // double oldRadius = obj->radius[0];
1622 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1624 QString text = QObject::tr("Radius: %1");//\nScale: %2%");
1625 informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
1630 if (obj->hitPoint[0])
1632 else if (obj->hitPoint[1])
1634 // Change the Arc's span (handle #1)
1637 double angle = Vector::Angle(obj->p[0], point);
1638 double delta = angle - obj->angle[0];
1641 delta += PI_TIMES_2;
1643 obj->angle[1] -= delta;
1644 obj->angle[0] = angle;
1646 if (obj->angle[1] < 0)
1647 obj->angle[1] += PI_TIMES_2;
1649 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
1650 informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
1654 double angle = Vector::Angle(obj->p[0], point);
1655 obj->angle[0] = angle;
1656 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
1657 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
1659 else if (obj->hitPoint[2])
1661 // Change the Arc's span (handle #2)
1664 double angle = Vector::Angle(obj->p[0], point);
1665 obj->angle[1] = angle - obj->angle[0];
1667 if (obj->angle[1] < 0)
1668 obj->angle[1] += PI_TIMES_2;
1670 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
1671 informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
1675 double angle = Vector::Angle(obj->p[0], point);
1676 obj->angle[0] = angle - obj->angle[1];
1678 if (obj->angle[0] < 0)
1679 obj->angle[0] += PI_TIMES_2;
1681 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
1682 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
1684 else if (obj->hitObject)
1691 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1692 QString text = QObject::tr("Radius: %1");
1693 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
1705 // This returns true if we've moved over an object...
1706 if (document.PointerMoved(point)) // <-- This
1707 // This is where the object would do automagic dragging & shit. Since we don't
1708 // do that anymore, we need a strategy to handle it.
1712 Now objects handle mouse move snapping as well. The code below mainly works only
1713 for tools; we need to fix it so that objects work as well...
1715 There's a problem with the object point snapping in that it's dependent on the
1716 order of the objects in the document. Most likely this is because it counts the
1717 selected object last and thus fucks up the algorithm. Need to fix this...
1721 // Do object snapping here. Grid snapping on mouse down is done in the
1722 // objects themselves, only because we have to hit test the raw point,
1723 // not the snapped point. There has to be a better way...!
1724 if (document.penultimateObjectHovered)
1726 // Two objects are hovered, see if we have an intersection point
1727 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
1730 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
1734 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
1735 Global::snapPointIsValid = true;
1738 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
1741 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
1745 Global::snapPoint = p1;
1746 Global::snapPointIsValid = true;
1750 double d1 = Vector(point, p1).Magnitude();
1751 double d2 = Vector(point, p2).Magnitude();
1754 Global::snapPoint = p1;
1756 Global::snapPoint = p2;
1758 Global::snapPointIsValid = true;
1764 // Otherwise, it was a single object hovered...
1770 if (Global::snapToGrid)
1771 point = Global::SnapPointToGrid(point);
1773 // We always snap to object points, and they take precendence over
1775 if (Global::snapPointIsValid)
1776 point = Global::snapPoint;
1778 toolAction->MouseMoved(point);