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),
53 gridPixels(0), collided(false), hoveringIntersection(false)
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 (hoveringIntersection)
328 painter.DrawHandle(intersectionPoint);
330 if (!informativeText.isEmpty())
331 painter.DrawInformativeText(informativeText);
336 // Renders objects in the passed in vector
338 void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
340 std::vector<void *>::iterator i;
342 for(i=v.begin(); i!=v.end(); i++)
344 Object * obj = (Object *)(*i);
345 float scaledThickness = Global::scale * obj->thickness;
347 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
349 painter->SetPen(0x00FF00, 2.0, LSSolid);
353 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
354 painter->SetBrush(obj->color);
356 if (obj->selected || obj->hitObject)
357 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
363 painter->DrawLine(obj->p[0], obj->p[1]);
365 if (obj->hitPoint[0])
366 painter->DrawHandle(obj->p[0]);
368 if (obj->hitPoint[1])
369 painter->DrawHandle(obj->p[1]);
373 painter->SetBrush(QBrush(Qt::NoBrush));
374 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
376 if (obj->hitPoint[0])
377 painter->DrawHandle(obj->p[0]);
381 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
383 if (obj->hitPoint[0])
384 painter->DrawHandle(obj->p[0]);
386 if (obj->hitPoint[1])
387 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
389 if (obj->hitPoint[2])
390 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
395 Dimension * d = (Dimension *)obj;
397 Vector v(d->p[0], d->p[1]);
398 double angle = v.Angle();
399 Vector unit = v.Unit();
400 Vector linePt1 = d->p[0], linePt2 = d->p[1];
402 double x1, y1, length;
404 if (d->subtype == DTLinearVert)
406 if ((angle < 0) || (angle > PI))
408 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
409 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
410 ortho = Vector(1.0, 0);
415 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
416 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
417 ortho = Vector(-1.0, 0);
421 linePt1.x = linePt2.x = x1;
422 length = fabs(d->p[0].y - d->p[1].y);
424 else if (d->subtype == DTLinearHorz)
426 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
428 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
429 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
430 ortho = Vector(0, 1.0);
435 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
436 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
437 ortho = Vector(0, -1.0);
441 linePt1.y = linePt2.y = y1;
442 length = fabs(d->p[0].x - d->p[1].x);
444 else if (d->subtype == DTLinear)
446 angle = Vector(linePt1, linePt2).Angle();
447 ortho = Vector::Normal(linePt1, linePt2);
448 length = v.Magnitude();
451 unit = Vector(linePt1, linePt2).Unit();
453 Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
454 Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
455 Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
456 Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
457 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
458 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
461 The numbers hardcoded into here, what are they?
462 I believe they are pixels.
464 // Draw extension lines (if certain type)
465 painter->DrawLine(p3, p5);
466 painter->DrawLine(p4, p6);
468 // Calculate whether or not the arrowheads are too crowded to put inside
469 // the extension lines. 9.0 is the length of the arrowhead.
470 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
471 //printf("Dimension::Draw(): t = %lf\n", t);
473 // On the screen, it's acting like this is actually 58%...
474 // This is correct, we want it to happen at > 50%
477 // Draw main dimension line + arrowheads
478 painter->DrawLine(p1, p2);
479 painter->DrawArrowhead(p1, p2, scaledThickness);
480 painter->DrawArrowhead(p2, p1, scaledThickness);
484 // Draw outside arrowheads
485 Point p7 = p1 - (unit * 9.0 * scaledThickness);
486 Point p8 = p2 + (unit * 9.0 * scaledThickness);
487 painter->DrawArrowhead(p1, p7, scaledThickness);
488 painter->DrawArrowhead(p2, p8, scaledThickness);
489 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
490 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
493 // Draw length of dimension line...
494 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
495 Point ctr = p2 + (Vector(p2, p1) / 2.0);
498 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
503 dimText = QString("%1\"").arg(length);
506 double feet = (double)((int)length / 12);
507 double inches = length - (feet * 12.0);
510 dimText = QString("%1'").arg(feet);
512 dimText = QString("%1' %2\"").arg(feet).arg(inches);
516 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
522 Text * t = (Text *)obj;
523 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
533 void DrawingView::AddHoveredToSelection(void)
535 std::vector<void *>::iterator i;
537 for(i=document.objects.begin(); i!=document.objects.end(); i++)
539 if (((Object *)(*i))->hovered)
540 ((Object *)(*i))->selected = true;
545 void DrawingView::GetSelection(std::vector<void *> & v)
548 std::vector<void *>::iterator i;
550 for(i=document.objects.begin(); i!=document.objects.end(); i++)
552 if (((Object *)(*i))->selected)
558 void DrawingView::GetHovered(std::vector<void *> & v)
561 std::vector<void *>::iterator i;
563 for(i=document.objects.begin(); i!=document.objects.end(); i++)
565 if (((Object *)(*i))->hovered)
567 //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"));
574 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
576 Global::screenSize = Vector(size().width(), size().height());
577 UpdateGridBackground();
581 void DrawingView::ToolHandler(int mode, Point p)
583 if (Global::tool == TTLine)
584 LineHandler(mode, p);
585 else if (Global::tool == TTCircle)
586 CircleHandler(mode, p);
587 else if (Global::tool == TTArc)
589 else if (Global::tool == TTRotate)
590 RotateHandler(mode, p);
591 else if (Global::tool == TTMirror)
592 MirrorHandler(mode, p);
596 void DrawingView::ToolDraw(Painter * painter)
598 if (Global::tool == TTLine)
600 if (Global::toolState == TSNone)
602 painter->DrawHandle(toolPoint[0]);
604 else if ((Global::toolState == TSPoint2) && shiftDown)
606 painter->DrawHandle(toolPoint[1]);
610 painter->DrawLine(toolPoint[0], toolPoint[1]);
611 painter->DrawHandle(toolPoint[1]);
613 Vector v(toolPoint[0], toolPoint[1]);
614 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
615 double absLength = v.Magnitude();
616 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
617 informativeText = text.arg(absLength).arg(absAngle);
620 else if (Global::tool == TTCircle)
622 if (Global::toolState == TSNone)
624 painter->DrawHandle(toolPoint[0]);
626 else if ((Global::toolState == TSPoint2) && shiftDown)
628 painter->DrawHandle(toolPoint[1]);
632 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
633 // painter->DrawLine(toolPoint[0], toolPoint[1]);
634 // painter->DrawHandle(toolPoint[1]);
635 painter->SetBrush(QBrush(Qt::NoBrush));
636 painter->DrawEllipse(toolPoint[0], length, length);
637 QString text = tr("Radius: %1 in.");//\n") + QChar(0x2221) + tr(": %2");
638 informativeText = text.arg(length);//.arg(absAngle);
641 else if (Global::tool == TTArc)
643 if (Global::toolState == TSNone)
645 painter->DrawHandle(toolPoint[0]);
647 else if (Global::toolState == TSPoint2)
649 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
650 painter->SetBrush(QBrush(Qt::NoBrush));
651 painter->DrawEllipse(toolPoint[0], length, length);
652 painter->DrawLine(toolPoint[0], toolPoint[1]);
653 painter->DrawHandle(toolPoint[1]);
654 QString text = tr("Radius: %1 in.");
655 informativeText = text.arg(length);
657 else if (Global::toolState == TSPoint3)
659 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
660 painter->DrawLine(toolPoint[0], toolPoint[2]);
661 painter->SetBrush(QBrush(Qt::NoBrush));
662 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
663 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
664 QString text = tr("Angle start: %1") + QChar(0x00B0);
665 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
669 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
670 double span = angle - toolPoint[2].x;
675 painter->DrawLine(toolPoint[0], toolPoint[3]);
676 painter->SetBrush(QBrush(Qt::NoBrush));
677 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
678 painter->SetPen(0xFF00FF, 2.0, LSSolid);
679 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
680 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
681 QString text = tr("Arc span: %1") + QChar(0x00B0);
682 informativeText = text.arg(RADIANS_TO_DEGREES * span);
685 else if (Global::tool == TTRotate)
687 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
688 painter->DrawHandle(toolPoint[0]);
689 else if ((Global::toolState == TSPoint2) && shiftDown)
690 painter->DrawHandle(toolPoint[1]);
693 if (toolPoint[0] == toolPoint[1])
696 painter->DrawLine(toolPoint[0], toolPoint[1]);
697 // Likely we need a tool container for this... (now we do!)
701 painter->SetPen(0x00FF00, 2.0, LSSolid);
702 overrideColor = true;
705 RenderObjects(painter, toolObjects);
706 overrideColor = false;
709 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
710 QString text = QChar(0x2221) + QObject::tr(": %1");
711 informativeText = text.arg(absAngle);
714 informativeText += " (Copy)";
717 else if (Global::tool == TTMirror)
719 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
720 painter->DrawHandle(toolPoint[0]);
721 else if ((Global::toolState == TSPoint2) && shiftDown)
722 painter->DrawHandle(toolPoint[1]);
725 if (toolPoint[0] == toolPoint[1])
728 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
729 // painter->DrawLine(toolPoint[0], toolPoint[1]);
730 painter->DrawLine(mirrorPoint, toolPoint[1]);
731 // Likely we need a tool container for this... (now we do!)
735 painter->SetPen(0x00FF00, 2.0, LSSolid);
736 overrideColor = true;
739 RenderObjects(painter, toolObjects);
740 overrideColor = false;
743 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
745 if (absAngle > 180.0)
748 QString text = QChar(0x2221) + QObject::tr(": %1");
749 informativeText = text.arg(absAngle);
752 informativeText += " (Copy)";
758 void DrawingView::LineHandler(int mode, Point p)
763 if (Global::toolState == TSNone)
770 if (Global::toolState == TSNone)
777 if (Global::toolState == TSNone)
779 Global::toolState = TSPoint2;
780 // Prevent spurious line from drawing...
781 toolPoint[1] = toolPoint[0];
783 else if ((Global::toolState == TSPoint2) && shiftDown)
785 // Key override is telling us to make a new line, not continue the
787 toolPoint[0] = toolPoint[1];
791 Line * l = new Line(toolPoint[0], toolPoint[1]);
792 document.objects.push_back(l);
793 toolPoint[0] = toolPoint[1];
799 void DrawingView::CircleHandler(int mode, Point p)
804 if (Global::toolState == TSNone)
811 if (Global::toolState == TSNone)
818 if (Global::toolState == TSNone)
820 Global::toolState = TSPoint2;
821 // Prevent spurious line from drawing...
822 toolPoint[1] = toolPoint[0];
824 else if ((Global::toolState == TSPoint2) && shiftDown)
826 // Key override is telling us to make a new line, not continue the
828 toolPoint[0] = toolPoint[1];
832 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
833 Circle * c = new Circle(toolPoint[0], length);
834 document.objects.push_back(c);
835 toolPoint[0] = toolPoint[1];
836 Global::toolState = TSNone;
842 void DrawingView::ArcHandler(int mode, Point p)
847 if (Global::toolState == TSNone)
849 else if (Global::toolState == TSPoint2)
851 else if (Global::toolState == TSPoint3)
858 if (Global::toolState == TSNone)
860 else if (Global::toolState == TSPoint2)
862 else if (Global::toolState == TSPoint3)
869 if (Global::toolState == TSNone)
871 // Prevent spurious line from drawing...
872 toolPoint[1] = toolPoint[0];
873 Global::toolState = TSPoint2;
875 else if (Global::toolState == TSPoint2)
879 // Key override is telling us to start circle at new center, not
880 // continue the current one.
881 toolPoint[0] = toolPoint[1];
885 // Set the radius in toolPoint[1].x
886 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
887 Global::toolState = TSPoint3;
889 else if (Global::toolState == TSPoint3)
891 // Set the angle in toolPoint[2].x
892 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
893 Global::toolState = TSPoint4;
897 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
898 double span = endAngle - toolPoint[2].x;
903 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
904 document.objects.push_back(arc);
905 Global::toolState = TSNone;
911 void DrawingView::RotateHandler(int mode, Point p)
916 if (Global::toolState == TSNone)
919 SavePointsFrom(select, toolScratch);
920 Global::toolState = TSPoint1;
922 else if (Global::toolState == TSPoint1)
929 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
931 else if (Global::toolState == TSPoint2)
938 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
939 std::vector<void *>::iterator j = select.begin();
940 std::vector<Object>::iterator i = toolScratch.begin();
942 for(; i!=toolScratch.end(); i++, j++)
945 Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
946 Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
947 Object * obj2 = (Object *)(*j);
951 if (obj.type == OTArc)
953 obj2->angle[0] = obj.angle[0] + angle;
955 if (obj2->angle[0] > PI_TIMES_2)
956 obj2->angle[0] -= PI_TIMES_2;
963 if (Global::toolState == TSPoint1)
965 Global::toolState = TSPoint2;
966 // Prevent spurious line from drawing...
967 toolPoint[1] = toolPoint[0];
969 else if ((Global::toolState == TSPoint2) && shiftDown)
971 // Key override is telling us to make a new line, not continue the
973 toolPoint[0] = toolPoint[1];
977 // Either we're finished with our rotate, or we're stamping a copy.
980 // Stamp a copy of the selection at the current rotation & bail
981 std::vector<void *> temp;
982 CopyObjects(select, temp);
984 AddObjectsTo(document.objects, temp);
985 RestorePointsTo(select, toolScratch);
990 Global::toolState = TSPoint1;
991 SavePointsFrom(select, toolScratch);
996 // Reset the selection if shift held down...
998 RestorePointsTo(select, toolScratch);
1002 // Reset selection when key is let up
1004 RotateHandler(ToolMouseMove, toolPoint[1]);
1008 RestorePointsTo(select, toolScratch);
1013 void DrawingView::MirrorHandler(int mode, Point p)
1018 if (Global::toolState == TSNone)
1021 SavePointsFrom(select, toolScratch);
1022 Global::toolState = TSPoint1;
1024 else if (Global::toolState == TSPoint1)
1031 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1033 else if (Global::toolState == TSPoint2)
1040 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1041 std::vector<void *>::iterator j = select.begin();
1042 std::vector<Object>::iterator i = toolScratch.begin();
1044 for(; i!=toolScratch.end(); i++, j++)
1047 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1048 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1049 Object * obj2 = (Object *)(*j);
1053 if (obj.type == OTArc)
1055 // This is 2*mirror angle - obj angle - obj span
1056 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1058 if (obj2->angle[0] > PI_TIMES_2)
1059 obj2->angle[0] -= PI_TIMES_2;
1066 if (Global::toolState == TSPoint1)
1068 Global::toolState = TSPoint2;
1069 // Prevent spurious line from drawing...
1070 toolPoint[1] = toolPoint[0];
1072 else if ((Global::toolState == TSPoint2) && shiftDown)
1074 // Key override is telling us to make a new line, not continue the
1076 toolPoint[0] = toolPoint[1];
1080 // Either we're finished with our rotate, or we're stamping a copy.
1083 // Stamp a copy of the selection at the current rotation & bail
1084 std::vector<void *> temp;
1085 CopyObjects(select, temp);
1086 ClearSelected(temp);
1087 AddObjectsTo(document.objects, temp);
1088 RestorePointsTo(select, toolScratch);
1093 Global::toolState = TSPoint1;
1094 SavePointsFrom(select, toolScratch);
1099 // Reset the selection if shift held down...
1101 RestorePointsTo(select, toolScratch);
1105 // Reset selection when key is let up
1107 MirrorHandler(ToolMouseMove, toolPoint[1]);
1111 RestorePointsTo(select, toolScratch);
1116 void DrawingView::mousePressEvent(QMouseEvent * event)
1118 if (event->button() == Qt::LeftButton)
1120 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1122 // Handle tool processing, if any
1125 if (Global::snapToGrid)
1126 point = SnapPointToGrid(point);
1128 //Also, may want to figure out if hovering over a snap point on an object,
1129 //snap to grid if not.
1130 // Snap to object point if valid...
1131 // if (Global::snapPointIsValid)
1132 // point = Global::snapPoint;
1134 ToolHandler(ToolMouseDown, point);
1138 // Clear the selection only if CTRL isn't being held on click
1140 ClearSelected(document.objects);
1141 // ClearSelection();
1143 // If any objects are being hovered on click, add them to the selection
1147 AddHoveredToSelection();
1148 update(); // needed??
1149 GetHovered(hover); // prolly needed
1151 // Needed for grab & moving objects
1152 // We do it *after*... why? (doesn't seem to confer any advantage...)
1153 if (Global::snapToGrid)
1154 oldPoint = SnapPointToGrid(point);
1159 // Didn't hit any object and not using a tool, so do a selection rectangle
1160 Global::selectionInProgress = true;
1161 Global::selection.setTopLeft(QPointF(point.x, point.y));
1162 Global::selection.setBottomRight(QPointF(point.x, point.y));
1164 else if (event->button() == Qt::MiddleButton)
1167 oldPoint = Vector(event->x(), event->y());
1168 // Should also change the mouse pointer as well...
1169 setCursor(Qt::SizeAllCursor);
1174 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1176 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1177 Global::selection.setBottomRight(QPointF(point.x, point.y));
1178 // Only needs to be done here, as mouse down is always preceded by movement
1179 Global::snapPointIsValid = false;
1180 hoveringIntersection = false;
1183 if (event->buttons() & Qt::MiddleButton)
1185 point = Vector(event->x(), event->y());
1186 // Since we're using Qt coords for scrolling, we have to adjust them here to
1187 // conform to Cartesian coords, since the origin is using Cartesian. :-)
1188 Vector delta(oldPoint, point);
1189 delta /= Global::zoom;
1191 Global::origin -= delta;
1193 UpdateGridBackground();
1199 // If we're doing a selection rect, see if any objects are engulfed by it
1200 // (implies left mouse button held down)
1201 if (Global::selectionInProgress)
1203 CheckObjectBounds();
1208 // Handle object movement (left button down & over an object)
1209 if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
1211 if (Global::snapToGrid)
1212 point = SnapPointToGrid(point);
1214 HandleObjectMovement(point);
1220 // Do object hit testing...
1221 bool needUpdate = HitTestObjects(point);
1223 // Check for multi-hover...
1229 int numIntersecting = Geometry::Intersects((Object *)hover[0], (Object *)hover[1], &t, &u);
1231 if (numIntersecting > 0)
1233 Vector v1 = Geometry::GetPointForParameter((Object *)hover[0], t);
1234 Vector v2 = Geometry::GetPointForParameter((Object *)hover[1], u);
1235 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1236 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1238 hoveringIntersection = true;
1239 intersectionPoint = v1;
1243 // Do tool handling, if any are active...
1246 if (Global::snapToGrid)
1247 point = SnapPointToGrid(point);
1249 ToolHandler(ToolMouseMove, point);
1252 // This is used to draw the tool crosshair...
1255 if (needUpdate || Global::tool)
1260 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1262 if (event->button() == Qt::LeftButton)
1264 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1265 //could set it up to use the document's update function (assumes that all object updates
1266 //are being reported correctly:
1267 // if (document.NeedsUpdate())
1268 // Do an update if collided with at least *one* object in the document
1274 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1275 ToolHandler(ToolMouseUp, point);
1279 if (Global::selectionInProgress)
1280 Global::selectionInProgress = false;
1282 informativeText.clear();
1283 // Should we be doing this automagically? Hmm...
1284 // Clear our vectors
1289 std::vector<void *>::iterator i;
1291 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1293 if (((Object *)(*i))->selected)
1294 select.push_back(*i);
1296 //hmm, this is no good, too late to do any good :-P
1297 // if ((*i)->hovered)
1298 // hover.push_back(*i);
1301 else if (event->button() == Qt::MiddleButton)
1304 setCursor(Qt::ArrowCursor);
1309 void DrawingView::wheelEvent(QWheelEvent * event)
1311 double zoomFactor = 1.25;
1312 QSize sizeWin = size();
1313 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1314 center = Painter::QtToCartesianCoords(center);
1316 // This is not centering for some reason. Need to figure out why. :-/
1317 if (event->delta() > 0)
1319 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1320 Global::origin = newOrigin;
1321 Global::zoom *= zoomFactor;
1325 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1326 Global::origin = newOrigin;
1327 Global::zoom /= zoomFactor;
1330 // Global::gridSpacing = gridPixels / Painter::zoom;
1331 // UpdateGridBackground();
1332 SetGridSize(Global::gridSpacing * Global::zoom);
1334 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1338 void DrawingView::keyPressEvent(QKeyEvent * event)
1340 bool oldShift = shiftDown;
1341 bool oldCtrl = ctrlDown;
1343 if (event->key() == Qt::Key_Shift)
1345 else if (event->key() == Qt::Key_Control)
1348 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1351 ToolHandler(ToolKeyDown, Point(0, 0));
1358 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1360 bool oldShift = shiftDown;
1361 bool oldCtrl = ctrlDown;
1363 if (event->key() == Qt::Key_Shift)
1365 else if (event->key() == Qt::Key_Control)
1368 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1371 ToolHandler(ToolKeyUp, Point(0, 0));
1379 // This looks strange, but it's really quite simple: We want a point that's
1380 // more than half-way to the next grid point to snap there while conversely we
1381 // want a point that's less than half-way to to the next grid point then snap
1382 // to the one before it. So we add half of the grid spacing to the point, then
1383 // divide by it so that we can remove the fractional part, then multiply it
1384 // back to get back to the correct answer.
1386 Point DrawingView::SnapPointToGrid(Point point)
1388 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
1389 point /= Global::gridSpacing;
1390 point.x = floor(point.x);//need to fix this for negative numbers...
1391 point.y = floor(point.y);
1392 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
1393 point *= Global::gridSpacing;
1398 void DrawingView::CheckObjectBounds(void)
1400 std::vector<void *>::iterator i;
1403 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1405 Object * obj = (Object *)(*i);
1406 obj->selected = false;
1412 Line * l = (Line *)obj;
1414 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1421 Circle * c = (Circle *)obj;
1423 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]))
1430 Arc * a = (Arc *)obj;
1432 double start = a->angle[0];
1433 double end = start + a->angle[1];
1434 QPointF p1(cos(start), sin(start));
1435 QPointF p2(cos(end), sin(end));
1436 QRectF bounds(p1, p2);
1439 // Swap X/Y coordinates if they're backwards...
1440 if (bounds.left() > bounds.right())
1442 double temp = bounds.left();
1443 bounds.setLeft(bounds.right());
1444 bounds.setRight(temp);
1447 if (bounds.bottom() > bounds.top())
1449 double temp = bounds.bottom();
1450 bounds.setBottom(bounds.top());
1451 bounds.setTop(temp);
1454 // Doesn't work as advertised! For shame!
1455 bounds = bounds.normalized();
1458 // If the end of the arc is before the beginning, add 360 degrees to it
1462 // Adjust the bounds depending on which axes are crossed
1463 if ((start < PI_OVER_2) && (end > PI_OVER_2))
1466 if ((start < PI) && (end > PI))
1467 bounds.setLeft(-1.0);
1469 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
1470 bounds.setBottom(-1.0);
1472 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
1473 bounds.setRight(1.0);
1475 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
1478 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
1479 bounds.setLeft(-1.0);
1481 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
1482 bounds.setBottom(-1.0);
1484 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1485 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1486 bounds.translate(a->p[0].x, a->p[0].y);
1488 if (Global::selection.contains(bounds))
1503 bool DrawingView::HitTestObjects(Point point)
1505 std::vector<void *>::iterator i;
1507 bool needUpdate = false;
1509 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1511 Object * obj = (Object *)(*i);
1517 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1518 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1519 Vector lineSegment = obj->p[1] - obj->p[0];
1520 Vector v1 = point - obj->p[0];
1521 Vector v2 = point - obj->p[1];
1522 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1526 distance = v1.Magnitude();
1528 distance = v2.Magnitude();
1530 // distance = ?Det?(ls, v1) / |ls|
1531 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1532 / lineSegment.Magnitude());
1534 if ((v1.Magnitude() * Global::zoom) < 8.0)
1535 obj->hitPoint[0] = true;
1536 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1537 obj->hitPoint[1] = true;
1538 else if ((distance * Global::zoom) < 5.0)
1539 obj->hitObject = true;
1541 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1543 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1550 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1551 obj->hitPoint[0] = obj->hitObject = false;
1552 double length = Vector::Magnitude(obj->p[0], point);
1554 if ((length * Global::zoom) < 8.0)
1555 obj->hitPoint[0] = true;
1556 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1557 obj->hitObject = true;
1559 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1561 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1568 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
1569 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
1570 double length = Vector::Magnitude(obj->p[0], point);
1571 double angle = Vector::Angle(obj->p[0], point);
1573 // Make sure we get the angle in the correct spot
1574 if (angle < obj->angle[0])
1575 angle += PI_TIMES_2;
1577 // Get the span that we're pointing at...
1578 double span = angle - obj->angle[0];
1580 // N.B.: Still need to hit test the arc start & arc span handles...
1581 double spanAngle = obj->angle[0] + obj->angle[1];
1582 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
1583 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
1584 double length2 = Vector::Magnitude(point, handle1);
1585 double length3 = Vector::Magnitude(point, handle2);
1587 if ((length * Global::zoom) < 8.0)
1588 obj->hitPoint[0] = true;
1589 else if ((length2 * Global::zoom) < 8.0)
1590 obj->hitPoint[1] = true;
1591 else if ((length3 * Global::zoom) < 8.0)
1592 obj->hitPoint[2] = true;
1593 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
1594 obj->hitObject = true;
1596 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
1598 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
1610 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1618 void DrawingView::HandleObjectMovement(Point point)
1620 Point delta = point - oldPoint;
1621 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
1622 Object * obj = (Object *)hover[0];
1623 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
1624 //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"));
1629 if (obj->hitPoint[0])
1631 else if (obj->hitPoint[1])
1633 else if (obj->hitObject)
1641 if (obj->hitPoint[0])
1643 else if (obj->hitObject)
1645 //this doesn't work. we need to save this on mouse down for this to work correctly!
1646 // double oldRadius = obj->radius[0];
1647 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1649 QString text = QObject::tr("Radius: %1");//\nScale: %2%");
1650 informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
1655 if (obj->hitPoint[0])
1657 else if (obj->hitPoint[1])
1659 // Change the Arc's span (handle #1)
1662 double angle = Vector::Angle(obj->p[0], point);
1663 double delta = angle - obj->angle[0];
1666 delta += PI_TIMES_2;
1668 obj->angle[1] -= delta;
1669 obj->angle[0] = angle;
1671 if (obj->angle[1] < 0)
1672 obj->angle[1] += PI_TIMES_2;
1674 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
1675 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);
1679 double angle = Vector::Angle(obj->p[0], point);
1680 obj->angle[0] = angle;
1681 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
1682 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
1684 else if (obj->hitPoint[2])
1686 // Change the Arc's span (handle #2)
1689 double angle = Vector::Angle(obj->p[0], point);
1690 obj->angle[1] = angle - obj->angle[0];
1692 if (obj->angle[1] < 0)
1693 obj->angle[1] += PI_TIMES_2;
1695 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
1696 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);
1700 double angle = Vector::Angle(obj->p[0], point);
1701 obj->angle[0] = angle - obj->angle[1];
1703 if (obj->angle[0] < 0)
1704 obj->angle[0] += PI_TIMES_2;
1706 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
1707 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
1709 else if (obj->hitObject)
1716 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1717 QString text = QObject::tr("Radius: %1");
1718 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
1730 // This returns true if we've moved over an object...
1731 if (document.PointerMoved(point)) // <-- This
1732 // This is where the object would do automagic dragging & shit. Since we don't
1733 // do that anymore, we need a strategy to handle it.
1737 Now objects handle mouse move snapping as well. The code below mainly works only
1738 for tools; we need to fix it so that objects work as well...
1740 There's a problem with the object point snapping in that it's dependent on the
1741 order of the objects in the document. Most likely this is because it counts the
1742 selected object last and thus fucks up the algorithm. Need to fix this...
1746 // Do object snapping here. Grid snapping on mouse down is done in the
1747 // objects themselves, only because we have to hit test the raw point,
1748 // not the snapped point. There has to be a better way...!
1749 if (document.penultimateObjectHovered)
1751 // Two objects are hovered, see if we have an intersection point
1752 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
1755 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
1759 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
1760 Global::snapPointIsValid = true;
1763 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
1766 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
1770 Global::snapPoint = p1;
1771 Global::snapPointIsValid = true;
1775 double d1 = Vector(point, p1).Magnitude();
1776 double d2 = Vector(point, p2).Magnitude();
1779 Global::snapPoint = p1;
1781 Global::snapPoint = p2;
1783 Global::snapPointIsValid = true;
1789 // Otherwise, it was a single object hovered...
1795 if (Global::snapToGrid)
1796 point = Global::SnapPointToGrid(point);
1798 // We always snap to object points, and they take precendence over
1800 if (Global::snapPointIsValid)
1801 point = Global::snapPoint;
1803 toolAction->MouseMoved(point);