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;
707 QString text = QChar(0x2221) + QObject::tr(": %1");
708 informativeText = text.arg(absAngle);
711 informativeText += " (Copy)";
714 else if (Global::tool == TTMirror)
716 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
717 painter->DrawHandle(toolPoint[0]);
718 else if ((Global::toolState == TSPoint2) && shiftDown)
719 painter->DrawHandle(toolPoint[1]);
722 if (toolPoint[0] == toolPoint[1])
725 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
726 // painter->DrawLine(toolPoint[0], toolPoint[1]);
727 painter->DrawLine(mirrorPoint, toolPoint[1]);
728 // Likely we need a tool container for this... (now we do!)
732 painter->SetPen(0x00FF00, 2.0, LSSolid);
733 overrideColor = true;
736 RenderObjects(painter, toolObjects);
737 overrideColor = false;
740 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
742 if (absAngle > 180.0)
745 QString text = QChar(0x2221) + QObject::tr(": %1");
746 informativeText = text.arg(absAngle);
749 informativeText += " (Copy)";
755 void DrawingView::LineHandler(int mode, Point p)
760 if (Global::toolState == TSNone)
767 if (Global::toolState == TSNone)
774 if (Global::toolState == TSNone)
776 Global::toolState = TSPoint2;
777 // Prevent spurious line from drawing...
778 toolPoint[1] = toolPoint[0];
780 else if ((Global::toolState == TSPoint2) && shiftDown)
782 // Key override is telling us to make a new line, not continue the
784 toolPoint[0] = toolPoint[1];
788 Line * l = new Line(toolPoint[0], toolPoint[1]);
789 document.objects.push_back(l);
790 toolPoint[0] = toolPoint[1];
796 void DrawingView::CircleHandler(int mode, Point p)
801 if (Global::toolState == TSNone)
808 if (Global::toolState == TSNone)
815 if (Global::toolState == TSNone)
817 Global::toolState = TSPoint2;
818 // Prevent spurious line from drawing...
819 toolPoint[1] = toolPoint[0];
821 else if ((Global::toolState == TSPoint2) && shiftDown)
823 // Key override is telling us to make a new line, not continue the
825 toolPoint[0] = toolPoint[1];
829 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
830 Circle * c = new Circle(toolPoint[0], length);
831 document.objects.push_back(c);
832 toolPoint[0] = toolPoint[1];
833 Global::toolState = TSNone;
839 void DrawingView::ArcHandler(int mode, Point p)
844 if (Global::toolState == TSNone)
846 else if (Global::toolState == TSPoint2)
848 else if (Global::toolState == TSPoint3)
855 if (Global::toolState == TSNone)
857 else if (Global::toolState == TSPoint2)
859 else if (Global::toolState == TSPoint3)
866 if (Global::toolState == TSNone)
868 // Prevent spurious line from drawing...
869 toolPoint[1] = toolPoint[0];
870 Global::toolState = TSPoint2;
872 else if (Global::toolState == TSPoint2)
876 // Key override is telling us to start circle at new center, not
877 // continue the current one.
878 toolPoint[0] = toolPoint[1];
882 // Set the radius in toolPoint[1].x
883 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
884 Global::toolState = TSPoint3;
886 else if (Global::toolState == TSPoint3)
888 // Set the angle in toolPoint[2].x
889 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
890 Global::toolState = TSPoint4;
894 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
895 double span = endAngle - toolPoint[2].x;
900 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
901 document.objects.push_back(arc);
902 Global::toolState = TSNone;
908 void DrawingView::RotateHandler(int mode, Point p)
913 if (Global::toolState == TSNone)
916 SavePointsFrom(select, toolScratch);
917 Global::toolState = TSPoint1;
919 else if (Global::toolState == TSPoint1)
926 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
928 else if (Global::toolState == TSPoint2)
935 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
936 std::vector<void *>::iterator j = select.begin();
937 std::vector<Object>::iterator i = toolScratch.begin();
939 for(; i!=toolScratch.end(); i++, j++)
942 Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
943 Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
944 Object * obj2 = (Object *)(*j);
948 if (obj.type == OTArc)
950 obj2->angle[0] = obj.angle[0] + angle;
952 if (obj2->angle[0] > PI_TIMES_2)
953 obj2->angle[0] -= PI_TIMES_2;
960 if (Global::toolState == TSPoint1)
962 Global::toolState = TSPoint2;
963 // Prevent spurious line from drawing...
964 toolPoint[1] = toolPoint[0];
966 else if ((Global::toolState == TSPoint2) && shiftDown)
968 // Key override is telling us to make a new line, not continue the
970 toolPoint[0] = toolPoint[1];
974 // Either we're finished with our rotate, or we're stamping a copy.
977 // Stamp a copy of the selection at the current rotation & bail
978 std::vector<void *> temp;
979 CopyObjects(select, temp);
981 AddObjectsTo(document.objects, temp);
982 RestorePointsTo(select, toolScratch);
987 Global::toolState = TSPoint1;
988 SavePointsFrom(select, toolScratch);
993 // Reset the selection if shift held down...
995 RestorePointsTo(select, toolScratch);
999 // Reset selection when key is let up
1001 RotateHandler(ToolMouseMove, toolPoint[1]);
1005 RestorePointsTo(select, toolScratch);
1010 void DrawingView::MirrorHandler(int mode, Point p)
1015 if (Global::toolState == TSNone)
1018 SavePointsFrom(select, toolScratch);
1019 Global::toolState = TSPoint1;
1021 else if (Global::toolState == TSPoint1)
1028 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1030 else if (Global::toolState == TSPoint2)
1037 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1038 std::vector<void *>::iterator j = select.begin();
1039 std::vector<Object>::iterator i = toolScratch.begin();
1041 for(; i!=toolScratch.end(); i++, j++)
1044 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1045 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1046 Object * obj2 = (Object *)(*j);
1050 if (obj.type == OTArc)
1052 // This is 2*mirror angle - obj angle - obj span
1053 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1055 if (obj2->angle[0] > PI_TIMES_2)
1056 obj2->angle[0] -= PI_TIMES_2;
1063 if (Global::toolState == TSPoint1)
1065 Global::toolState = TSPoint2;
1066 // Prevent spurious line from drawing...
1067 toolPoint[1] = toolPoint[0];
1069 else if ((Global::toolState == TSPoint2) && shiftDown)
1071 // Key override is telling us to make a new line, not continue the
1073 toolPoint[0] = toolPoint[1];
1077 // Either we're finished with our rotate, or we're stamping a copy.
1080 // Stamp a copy of the selection at the current rotation & bail
1081 std::vector<void *> temp;
1082 CopyObjects(select, temp);
1083 ClearSelected(temp);
1084 AddObjectsTo(document.objects, temp);
1085 RestorePointsTo(select, toolScratch);
1090 Global::toolState = TSPoint1;
1091 SavePointsFrom(select, toolScratch);
1096 // Reset the selection if shift held down...
1098 RestorePointsTo(select, toolScratch);
1102 // Reset selection when key is let up
1104 MirrorHandler(ToolMouseMove, toolPoint[1]);
1108 RestorePointsTo(select, toolScratch);
1113 void DrawingView::mousePressEvent(QMouseEvent * event)
1115 if (event->button() == Qt::LeftButton)
1117 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1119 // Handle tool processing, if any
1122 if (Global::snapToGrid)
1123 point = SnapPointToGrid(point);
1125 //Also, may want to figure out if hovering over a snap point on an object,
1126 //snap to grid if not.
1127 // Snap to object point if valid...
1128 // if (Global::snapPointIsValid)
1129 // point = Global::snapPoint;
1131 ToolHandler(ToolMouseDown, point);
1135 // Clear the selection only if CTRL isn't being held on click
1137 ClearSelected(document.objects);
1138 // ClearSelection();
1140 // If any objects are being hovered on click, add them to the selection
1144 AddHoveredToSelection();
1145 update(); // needed??
1146 GetHovered(hover); // prolly needed
1148 // Needed for grab & moving objects
1149 // We do it *after*... why? (doesn't seem to confer any advantage...)
1150 if (Global::snapToGrid)
1151 oldPoint = SnapPointToGrid(point);
1156 // Didn't hit any object and not using a tool, so do a selection rectangle
1157 Global::selectionInProgress = true;
1158 Global::selection.setTopLeft(QPointF(point.x, point.y));
1159 Global::selection.setBottomRight(QPointF(point.x, point.y));
1161 else if (event->button() == Qt::MiddleButton)
1164 oldPoint = Vector(event->x(), event->y());
1165 // Should also change the mouse pointer as well...
1166 setCursor(Qt::SizeAllCursor);
1171 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1173 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1174 Global::selection.setBottomRight(QPointF(point.x, point.y));
1175 // Only needs to be done here, as mouse down is always preceded by movement
1176 Global::snapPointIsValid = false;
1179 if (event->buttons() & Qt::MiddleButton)
1181 point = Vector(event->x(), event->y());
1182 // Since we're using Qt coords for scrolling, we have to adjust them here to
1183 // conform to Cartesian coords, since the origin is using Cartesian. :-)
1184 Vector delta(oldPoint, point);
1185 delta /= Global::zoom;
1187 Global::origin -= delta;
1189 UpdateGridBackground();
1195 // If we're doing a selection rect, see if any objects are engulfed by it
1196 // (implies left mouse button held down)
1197 if (Global::selectionInProgress)
1199 CheckObjectBounds();
1204 // Handle object movement (left button down & over an object)
1205 if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
1207 if (Global::snapToGrid)
1208 point = SnapPointToGrid(point);
1210 HandleObjectMovement(point);
1216 // Do object hit testing...
1217 bool needUpdate = HitTestObjects(point);
1219 // Check for multi-hover...
1225 int numIntersecting = Geometry::Intersects((Object *)hover[0], (Object *)hover[1], &t, &u);
1227 if (numIntersecting > 0)
1229 QString text = tr("Intersection t=%1, u=%2");
1230 informativeText = text.arg(t).arg(u);
1234 // Do tool handling, if any are active...
1237 if (Global::snapToGrid)
1238 point = SnapPointToGrid(point);
1240 ToolHandler(ToolMouseMove, point);
1243 // This is used to draw the tool crosshair...
1246 if (needUpdate || Global::tool)
1251 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1253 if (event->button() == Qt::LeftButton)
1255 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1256 //could set it up to use the document's update function (assumes that all object updates
1257 //are being reported correctly:
1258 // if (document.NeedsUpdate())
1259 // Do an update if collided with at least *one* object in the document
1265 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1266 ToolHandler(ToolMouseUp, point);
1270 if (Global::selectionInProgress)
1271 Global::selectionInProgress = false;
1273 informativeText.clear();
1274 // Should we be doing this automagically? Hmm...
1275 // Clear our vectors
1280 std::vector<void *>::iterator i;
1282 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1284 if (((Object *)(*i))->selected)
1285 select.push_back(*i);
1287 //hmm, this is no good, too late to do any good :-P
1288 // if ((*i)->hovered)
1289 // hover.push_back(*i);
1292 else if (event->button() == Qt::MiddleButton)
1295 setCursor(Qt::ArrowCursor);
1300 void DrawingView::wheelEvent(QWheelEvent * event)
1302 double zoomFactor = 1.25;
1303 QSize sizeWin = size();
1304 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1305 center = Painter::QtToCartesianCoords(center);
1307 // This is not centering for some reason. Need to figure out why. :-/
1308 if (event->delta() > 0)
1310 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1311 Global::origin = newOrigin;
1312 Global::zoom *= zoomFactor;
1316 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1317 Global::origin = newOrigin;
1318 Global::zoom /= zoomFactor;
1321 // Global::gridSpacing = gridPixels / Painter::zoom;
1322 // UpdateGridBackground();
1323 SetGridSize(Global::gridSpacing * Global::zoom);
1325 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1329 void DrawingView::keyPressEvent(QKeyEvent * event)
1331 bool oldShift = shiftDown;
1332 bool oldCtrl = ctrlDown;
1334 if (event->key() == Qt::Key_Shift)
1336 else if (event->key() == Qt::Key_Control)
1339 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1342 ToolHandler(ToolKeyDown, Point(0, 0));
1349 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1351 bool oldShift = shiftDown;
1352 bool oldCtrl = ctrlDown;
1354 if (event->key() == Qt::Key_Shift)
1356 else if (event->key() == Qt::Key_Control)
1359 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1362 ToolHandler(ToolKeyUp, Point(0, 0));
1370 // This looks strange, but it's really quite simple: We want a point that's
1371 // more than half-way to the next grid point to snap there while conversely we
1372 // want a point that's less than half-way to to the next grid point then snap
1373 // to the one before it. So we add half of the grid spacing to the point, then
1374 // divide by it so that we can remove the fractional part, then multiply it
1375 // back to get back to the correct answer.
1377 Point DrawingView::SnapPointToGrid(Point point)
1379 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
1380 point /= Global::gridSpacing;
1381 point.x = floor(point.x);//need to fix this for negative numbers...
1382 point.y = floor(point.y);
1383 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
1384 point *= Global::gridSpacing;
1389 void DrawingView::CheckObjectBounds(void)
1391 std::vector<void *>::iterator i;
1394 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1396 Object * obj = (Object *)(*i);
1397 obj->selected = false;
1403 Line * l = (Line *)obj;
1405 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1412 Circle * c = (Circle *)obj;
1414 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]))
1421 Arc * a = (Arc *)obj;
1423 double start = a->angle[0];
1424 double end = start + a->angle[1];
1425 QPointF p1(cos(start), sin(start));
1426 QPointF p2(cos(end), sin(end));
1427 QRectF bounds(p1, p2);
1430 // Swap X/Y coordinates if they're backwards...
1431 if (bounds.left() > bounds.right())
1433 double temp = bounds.left();
1434 bounds.setLeft(bounds.right());
1435 bounds.setRight(temp);
1438 if (bounds.bottom() > bounds.top())
1440 double temp = bounds.bottom();
1441 bounds.setBottom(bounds.top());
1442 bounds.setTop(temp);
1445 // Doesn't work as advertised! For shame!
1446 bounds = bounds.normalized();
1449 // If the end of the arc is before the beginning, add 360 degrees to it
1453 // Adjust the bounds depending on which axes are crossed
1454 if ((start < PI_OVER_2) && (end > PI_OVER_2))
1457 if ((start < PI) && (end > PI))
1458 bounds.setLeft(-1.0);
1460 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
1461 bounds.setBottom(-1.0);
1463 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
1464 bounds.setRight(1.0);
1466 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
1469 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
1470 bounds.setLeft(-1.0);
1472 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
1473 bounds.setBottom(-1.0);
1475 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1476 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1477 bounds.translate(a->p[0].x, a->p[0].y);
1479 if (Global::selection.contains(bounds))
1494 bool DrawingView::HitTestObjects(Point point)
1496 std::vector<void *>::iterator i;
1498 bool needUpdate = false;
1500 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1502 Object * obj = (Object *)(*i);
1508 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1509 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1510 Vector lineSegment = obj->p[1] - obj->p[0];
1511 Vector v1 = point - obj->p[0];
1512 Vector v2 = point - obj->p[1];
1513 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1517 distance = v1.Magnitude();
1519 distance = v2.Magnitude();
1521 // distance = ?Det?(ls, v1) / |ls|
1522 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1523 / lineSegment.Magnitude());
1525 if ((v1.Magnitude() * Global::zoom) < 8.0)
1526 obj->hitPoint[0] = true;
1527 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1528 obj->hitPoint[1] = true;
1529 else if ((distance * Global::zoom) < 5.0)
1530 obj->hitObject = true;
1532 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1534 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1541 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1542 obj->hitPoint[0] = obj->hitObject = false;
1543 double length = Vector::Magnitude(obj->p[0], point);
1545 if ((length * Global::zoom) < 8.0)
1546 obj->hitPoint[0] = true;
1547 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1548 obj->hitObject = true;
1550 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1552 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1559 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
1560 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
1561 double length = Vector::Magnitude(obj->p[0], point);
1562 double angle = Vector::Angle(obj->p[0], point);
1564 // Make sure we get the angle in the correct spot
1565 if (angle < obj->angle[0])
1566 angle += PI_TIMES_2;
1568 // Get the span that we're pointing at...
1569 double span = angle - obj->angle[0];
1571 // N.B.: Still need to hit test the arc start & arc span handles...
1572 double spanAngle = obj->angle[0] + obj->angle[1];
1573 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
1574 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
1575 double length2 = Vector::Magnitude(point, handle1);
1576 double length3 = Vector::Magnitude(point, handle2);
1578 if ((length * Global::zoom) < 8.0)
1579 obj->hitPoint[0] = true;
1580 else if ((length2 * Global::zoom) < 8.0)
1581 obj->hitPoint[1] = true;
1582 else if ((length3 * Global::zoom) < 8.0)
1583 obj->hitPoint[2] = true;
1584 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
1585 obj->hitObject = true;
1587 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
1589 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
1601 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1609 void DrawingView::HandleObjectMovement(Point point)
1611 Point delta = point - oldPoint;
1612 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
1613 Object * obj = (Object *)hover[0];
1614 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
1615 //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"));
1620 if (obj->hitPoint[0])
1622 else if (obj->hitPoint[1])
1624 else if (obj->hitObject)
1632 if (obj->hitPoint[0])
1634 else if (obj->hitObject)
1636 //this doesn't work. we need to save this on mouse down for this to work correctly!
1637 // double oldRadius = obj->radius[0];
1638 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1640 QString text = QObject::tr("Radius: %1");//\nScale: %2%");
1641 informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
1646 if (obj->hitPoint[0])
1648 else if (obj->hitPoint[1])
1650 // Change the Arc's span (handle #1)
1653 double angle = Vector::Angle(obj->p[0], point);
1654 double delta = angle - obj->angle[0];
1657 delta += PI_TIMES_2;
1659 obj->angle[1] -= delta;
1660 obj->angle[0] = angle;
1662 if (obj->angle[1] < 0)
1663 obj->angle[1] += PI_TIMES_2;
1665 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
1666 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);
1670 double angle = Vector::Angle(obj->p[0], point);
1671 obj->angle[0] = angle;
1672 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
1673 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
1675 else if (obj->hitPoint[2])
1677 // Change the Arc's span (handle #2)
1680 double angle = Vector::Angle(obj->p[0], point);
1681 obj->angle[1] = angle - obj->angle[0];
1683 if (obj->angle[1] < 0)
1684 obj->angle[1] += PI_TIMES_2;
1686 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
1687 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);
1691 double angle = Vector::Angle(obj->p[0], point);
1692 obj->angle[0] = angle - obj->angle[1];
1694 if (obj->angle[0] < 0)
1695 obj->angle[0] += PI_TIMES_2;
1697 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
1698 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
1700 else if (obj->hitObject)
1707 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1708 QString text = QObject::tr("Radius: %1");
1709 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
1721 // This returns true if we've moved over an object...
1722 if (document.PointerMoved(point)) // <-- This
1723 // This is where the object would do automagic dragging & shit. Since we don't
1724 // do that anymore, we need a strategy to handle it.
1728 Now objects handle mouse move snapping as well. The code below mainly works only
1729 for tools; we need to fix it so that objects work as well...
1731 There's a problem with the object point snapping in that it's dependent on the
1732 order of the objects in the document. Most likely this is because it counts the
1733 selected object last and thus fucks up the algorithm. Need to fix this...
1737 // Do object snapping here. Grid snapping on mouse down is done in the
1738 // objects themselves, only because we have to hit test the raw point,
1739 // not the snapped point. There has to be a better way...!
1740 if (document.penultimateObjectHovered)
1742 // Two objects are hovered, see if we have an intersection point
1743 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
1746 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
1750 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
1751 Global::snapPointIsValid = true;
1754 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
1757 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
1761 Global::snapPoint = p1;
1762 Global::snapPointIsValid = true;
1766 double d1 = Vector(point, p1).Magnitude();
1767 double d2 = Vector(point, p2).Magnitude();
1770 Global::snapPoint = p1;
1772 Global::snapPoint = p2;
1774 Global::snapPointIsValid = true;
1780 // Otherwise, it was a single object hovered...
1786 if (Global::snapToGrid)
1787 point = Global::SnapPointToGrid(point);
1789 // We always snap to object points, and they take precendence over
1791 if (Global::snapPointIsValid)
1792 point = Global::snapPoint;
1794 toolAction->MouseMoved(point);