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]
23 // - Layer locking (hiding works)
26 // Uncomment this for debugging...
28 //#define DEBUGFOO // Various tool debugging...
29 //#define DEBUGTP // Toolpalette debugging...
31 #include "drawingview.h"
36 #include "mathconstants.h"
42 #define BACKGROUND_MAX_SIZE 512
45 //Container DrawingView::document(Vector(0, 0));
48 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
49 // The value in the settings file will override this.
50 useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
52 gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
53 scale(1.0), offsetX(-10), offsetY(-10),
54 gridPixels(0), collided(false), hoveringIntersection(false)
56 // document.isTopLevelContainer = true;
57 //wtf? doesn't work except in c++11??? document = { 0 };
58 setBackgroundRole(QPalette::Base);
59 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
61 Global::gridSpacing = 12.0; // In base units (inch is default)
64 Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
66 document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
67 document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
68 document.Add(new Circle(Vector(100, 100), 36, &document));
69 document.Add(new Circle(Vector(50, 150), 49, &document));
70 document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
71 document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
73 Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
74 line->SetDimensionOnLine(dimension);
75 document.Add(dimension);
77 // Alternate way to do the above...
78 line->SetDimensionOnLine();
81 Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
82 line->p[0] = Vector(5, 5);
83 line->p[1] = Vector(50, 40);
85 line->thickness = 2.0;
87 line->color = 0xFF7F00;
89 document.objects.push_back(line);
90 document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
91 document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
92 document.objects.push_back(new Circle(Vector(100, 100), 36));
93 document.objects.push_back(new Circle(Vector(50, 150), 49));
94 document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
95 document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
96 document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
97 document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
101 Here we set the grid size in pixels--12 in this case. Initially, we have our
102 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
103 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
104 to be able to set the size of the background grid (which we do here at an
105 arbitrary 12 pixels) to anything we want (within reason, of course :-).
107 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
109 drawing->gridSpacing = 12.0 / Global::zoom;
111 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
112 translated to Cartesian coordinates through this. (Initially, Global::zoom is
113 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
115 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
116 convenience function than any measure of absolutes. Doing things that way we
117 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
118 shittiness that comes with it.
120 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
121 a certain way, which means we should probably create something else in those
122 objects to take its place--like some kind of scale factor. This would seem to
123 imply that certain point sizes actually *do* tie things like fonts to absolute
124 sizes on the screen, but not necessarily because you could have an inch scale
125 with text that is quite small relative to other objects on the screen, which
126 currently you have to zoom in to see (and which blows up the text). Point sizes
127 in an application like this are a bit meaningless; even though an inch is an
128 inch regardless of the zoom level a piece of text can be larger or smaller than
129 this. Maybe this is the case for having a base unit and basing point sizes off
132 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
133 base units. What that means is that if you have a 12px grid with a 6" grid size
134 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
136 Dimensions now have a "size" parameter to set their absolute size in relation
137 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
138 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
139 scaled the same way as the arrowheads.
141 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
142 need a thickness parameter similar to the "size" param for dimensions. (And now
146 SetGridSize(12); // This is in pixels
151 void DrawingView::SetToolActive(Action * action)
156 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
157 SLOT(AddNewObjectToDocument(Object *)));
158 connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
164 void DrawingView::SetGridSize(uint32_t size)
167 if (size == gridPixels)
170 // Recreate the background bitmap
172 QPainter pmp(&gridBackground);
173 pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
174 pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
176 for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
178 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
179 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
184 // Set up new BG brush & zoom level (pixels per base unit)
185 // Painter::zoom = gridPixels / gridSpacing;
186 Global::zoom = gridPixels / Global::gridSpacing;
187 UpdateGridBackground();
191 void DrawingView::UpdateGridBackground(void)
193 // Transform the origin to Qt coordinates
194 Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
195 int x = (int)pixmapOrigin.x;
196 int y = (int)pixmapOrigin.y;
197 // Use mod arithmetic to grab the correct swatch of background
199 Negative numbers still screw it up... Need to think about what we're
200 trying to do here. The fact that it worked with 72 seems to have been pure luck.
201 It seems the problem is negative numbers: We can't let that happen.
202 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
205 The bitmap looks like this:
215 @ x = 1, we want it to look like:
217 -+---+---+---+---+---
220 -+---+---+---+---+---
225 Which means we need to grab the sample from x = 3. @ x = -1:
235 Which means we need to grab the sample from x = 1. Which means we have to take
236 the mirror of the modulus of gridPixels.
238 Doing a mod of a negative number is problematic: 1st, the compiler converts the
239 negative number to an unsigned int, then it does the mod. Gets you wrong answers
240 most of the time, unless you use a power of 2. :-P So what we do here is just
241 take the modulus of the negation, which means we don't have to worry about
244 The positive case looks gruesome (and it is) but it boils down to this: We take
245 the modulus of the X coordinate, then mirror it by subtraction from the
246 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
247 gridPixels. But we need the case where the result equalling gridPixels to be
248 zero; so we do another modulus operation on the result to achieve this.
253 x = (gridPixels - (x % gridPixels)) % gridPixels;
258 y = (gridPixels - (y % gridPixels)) % gridPixels;
260 // Here we grab a section of the bigger pixmap, so that the background
261 // *looks* like it's scrolling...
262 QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
263 QPalette pal = palette();
264 pal.setBrush(backgroundRole(), QBrush(pm));
265 setAutoFillBackground(true);
270 void DrawingView::SetCurrentLayer(int /*layer*/)
272 //Not needed anymore...
273 // Global::currentLayer = layer;
274 //printf("DrawingView::CurrentLayer = %i\n", layer);
279 // Basically, we just make a single pass through the Container. If the layer #
280 // is less than the layer # being deleted, then do nothing. If the layer # is
281 // equal to the layer # being deleted, then delete the object. If the layer #
282 // is greater than the layer # being deleted, then set the layer # to its layer
285 void DrawingView::DeleteCurrentLayer(int layer)
287 //printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
288 std::vector<void *>::iterator i = document.objects.begin();
290 while (i != document.objects.end())
292 Object * obj = (Object *)(*i);
294 if (obj->layer < layer)
296 else if (obj->layer == layer)
298 document.objects.erase(i);
308 // We've just done a destructive action, so update the screen!
313 void DrawingView::HandleLayerToggle(void)
315 // A layer's visibility was toggled, so update the screen...
321 // A layer was moved up or down in the layer list, so we have to swap the
322 // document's object's layer numbers in the layers that were swapped.
324 void DrawingView::HandleLayerSwap(int layer1, int layer2)
326 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
327 std::vector<void *>::iterator i;
329 for(i=document.objects.begin(); i!=document.objects.end(); i++)
331 Object * obj = (Object *)(*i);
333 if (obj->layer == layer1)
335 else if (obj->layer == layer2)
341 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
343 // This is undoing the transform, e.g. going from client coords to local coords.
344 // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
345 // conversion of the y-axis from increasing bottom to top.
346 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
350 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
352 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
353 // No voodoo here, it's just grouped wrong to see it. It should be:
354 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
355 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
359 void DrawingView::paintEvent(QPaintEvent * /*event*/)
361 QPainter qtPainter(this);
362 Painter painter(&qtPainter);
365 qtPainter.setRenderHint(QPainter::Antialiasing);
367 Global::viewportHeight = size().height();
369 // Draw coordinate axes
370 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
371 painter.DrawLine(0, -16384, 0, 16384);
372 painter.DrawLine(-16384, 0, 16384, 0);
374 // Do object rendering...
375 for(int i=0; i<Global::numLayers; i++)
377 if (Global::layerHidden[i] == false)
378 RenderObjects(&painter, document.objects, i);
381 // Do tool rendering, if any...
384 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
385 painter.DrawCrosshair(oldPoint);
389 // Do selection rectangle rendering, if any
390 if (Global::selectionInProgress)
392 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
393 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
394 painter.DrawRect(Global::selection);
397 if (hoveringIntersection)
398 painter.DrawHandle(intersectionPoint);
400 if (!informativeText.isEmpty())
401 painter.DrawInformativeText(informativeText);
406 // Renders objects in the passed in vector
408 void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer)
410 std::vector<void *>::iterator i;
412 for(i=v.begin(); i!=v.end(); i++)
414 Object * obj = (Object *)(*i);
415 float scaledThickness = Global::scale * obj->thickness;
417 // If the object isn't on the current layer being drawn, skip it
418 if (obj->layer != layer)
421 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
423 painter->SetPen(0x00FF00, 2.0, LSSolid);
427 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
428 painter->SetBrush(obj->color);
430 if (obj->selected || obj->hitObject)
431 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
437 painter->DrawLine(obj->p[0], obj->p[1]);
439 if (obj->hitPoint[0])
440 painter->DrawHandle(obj->p[0]);
442 if (obj->hitPoint[1])
443 painter->DrawHandle(obj->p[1]);
447 painter->SetBrush(QBrush(Qt::NoBrush));
448 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
450 if (obj->hitPoint[0])
451 painter->DrawHandle(obj->p[0]);
455 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
457 if (obj->hitPoint[0])
458 painter->DrawHandle(obj->p[0]);
460 if (obj->hitPoint[1])
461 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
463 if (obj->hitPoint[2])
464 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
469 Dimension * d = (Dimension *)obj;
471 Vector v(d->p[0], d->p[1]);
472 double angle = v.Angle();
473 Vector unit = v.Unit();
474 Vector linePt1 = d->p[0], linePt2 = d->p[1];
476 double x1, y1, length;
478 if (d->subtype == DTLinearVert)
480 if ((angle < 0) || (angle > PI))
482 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
483 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
484 ortho = Vector(1.0, 0);
489 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
490 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
491 ortho = Vector(-1.0, 0);
495 linePt1.x = linePt2.x = x1;
496 length = fabs(d->p[0].y - d->p[1].y);
498 else if (d->subtype == DTLinearHorz)
500 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
502 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
503 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
504 ortho = Vector(0, 1.0);
509 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
510 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
511 ortho = Vector(0, -1.0);
515 linePt1.y = linePt2.y = y1;
516 length = fabs(d->p[0].x - d->p[1].x);
518 else if (d->subtype == DTLinear)
520 angle = Vector(linePt1, linePt2).Angle();
521 ortho = Vector::Normal(linePt1, linePt2);
522 length = v.Magnitude();
525 unit = Vector(linePt1, linePt2).Unit();
527 Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
528 Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
529 Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
530 Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
531 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
532 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
535 The numbers hardcoded into here, what are they?
536 I believe they are pixels.
538 // Draw extension lines (if certain type)
539 painter->DrawLine(p3, p5);
540 painter->DrawLine(p4, p6);
542 // Calculate whether or not the arrowheads are too crowded to put inside
543 // the extension lines. 9.0 is the length of the arrowhead.
544 double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
545 //printf("Dimension::Draw(): t = %lf\n", t);
547 // On the screen, it's acting like this is actually 58%...
548 // This is correct, we want it to happen at > 50%
551 // Draw main dimension line + arrowheads
552 painter->DrawLine(p1, p2);
553 painter->DrawArrowhead(p1, p2, scaledThickness);
554 painter->DrawArrowhead(p2, p1, scaledThickness);
558 // Draw outside arrowheads
559 Point p7 = p1 - (unit * 9.0 * scaledThickness);
560 Point p8 = p2 + (unit * 9.0 * scaledThickness);
561 painter->DrawArrowhead(p1, p7, scaledThickness);
562 painter->DrawArrowhead(p2, p8, scaledThickness);
563 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
564 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
567 // Draw length of dimension line...
568 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
569 Point ctr = p2 + (Vector(p2, p1) / 2.0);
572 QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
577 dimText = QString("%1\"").arg(length);
580 double feet = (double)((int)length / 12);
581 double inches = length - (feet * 12.0);
584 dimText = QString("%1'").arg(feet);
586 dimText = QString("%1' %2\"").arg(feet).arg(inches);
590 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
596 Text * t = (Text *)obj;
597 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
607 void DrawingView::AddHoveredToSelection(void)
609 std::vector<void *>::iterator i;
611 for(i=document.objects.begin(); i!=document.objects.end(); i++)
613 if (((Object *)(*i))->hovered)
614 ((Object *)(*i))->selected = true;
619 void DrawingView::GetSelection(std::vector<void *> & v)
622 std::vector<void *>::iterator i;
624 for(i=document.objects.begin(); i!=document.objects.end(); i++)
626 if (((Object *)(*i))->selected)
632 void DrawingView::GetHovered(std::vector<void *> & v)
635 std::vector<void *>::iterator i;
637 for(i=document.objects.begin(); i!=document.objects.end(); i++)
639 if (((Object *)(*i))->hovered)
641 //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"));
648 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
650 Global::screenSize = Vector(size().width(), size().height());
651 UpdateGridBackground();
655 void DrawingView::ToolHandler(int mode, Point p)
657 if (Global::tool == TTLine)
658 LineHandler(mode, p);
659 else if (Global::tool == TTCircle)
660 CircleHandler(mode, p);
661 else if (Global::tool == TTArc)
663 else if (Global::tool == TTRotate)
664 RotateHandler(mode, p);
665 else if (Global::tool == TTMirror)
666 MirrorHandler(mode, p);
670 void DrawingView::ToolDraw(Painter * painter)
672 if (Global::tool == TTLine)
674 if (Global::toolState == TSNone)
676 painter->DrawHandle(toolPoint[0]);
678 else if ((Global::toolState == TSPoint2) && shiftDown)
680 painter->DrawHandle(toolPoint[1]);
684 painter->DrawLine(toolPoint[0], toolPoint[1]);
685 painter->DrawHandle(toolPoint[1]);
687 Vector v(toolPoint[0], toolPoint[1]);
688 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
689 double absLength = v.Magnitude();
690 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
691 informativeText = text.arg(absLength).arg(absAngle);
694 else if (Global::tool == TTCircle)
696 if (Global::toolState == TSNone)
698 painter->DrawHandle(toolPoint[0]);
700 else if ((Global::toolState == TSPoint2) && shiftDown)
702 painter->DrawHandle(toolPoint[1]);
706 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
707 // painter->DrawLine(toolPoint[0], toolPoint[1]);
708 // painter->DrawHandle(toolPoint[1]);
709 painter->SetBrush(QBrush(Qt::NoBrush));
710 painter->DrawEllipse(toolPoint[0], length, length);
711 QString text = tr("Radius: %1 in.");//\n") + QChar(0x2221) + tr(": %2");
712 informativeText = text.arg(length);//.arg(absAngle);
715 else if (Global::tool == TTArc)
717 if (Global::toolState == TSNone)
719 painter->DrawHandle(toolPoint[0]);
721 else if (Global::toolState == TSPoint2)
723 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
724 painter->SetBrush(QBrush(Qt::NoBrush));
725 painter->DrawEllipse(toolPoint[0], length, length);
726 painter->DrawLine(toolPoint[0], toolPoint[1]);
727 painter->DrawHandle(toolPoint[1]);
728 QString text = tr("Radius: %1 in.");
729 informativeText = text.arg(length);
731 else if (Global::toolState == TSPoint3)
733 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
734 painter->DrawLine(toolPoint[0], toolPoint[2]);
735 painter->SetBrush(QBrush(Qt::NoBrush));
736 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
737 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
738 QString text = tr("Angle start: %1") + QChar(0x00B0);
739 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
743 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
744 double span = angle - toolPoint[2].x;
749 painter->DrawLine(toolPoint[0], toolPoint[3]);
750 painter->SetBrush(QBrush(Qt::NoBrush));
751 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
752 painter->SetPen(0xFF00FF, 2.0, LSSolid);
753 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
754 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
755 QString text = tr("Arc span: %1") + QChar(0x00B0);
756 informativeText = text.arg(RADIANS_TO_DEGREES * span);
759 else if (Global::tool == TTRotate)
761 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
762 painter->DrawHandle(toolPoint[0]);
763 else if ((Global::toolState == TSPoint2) && shiftDown)
764 painter->DrawHandle(toolPoint[1]);
767 if (toolPoint[0] == toolPoint[1])
770 painter->DrawLine(toolPoint[0], toolPoint[1]);
771 // Likely we need a tool container for this... (now we do!)
775 painter->SetPen(0x00FF00, 2.0, LSSolid);
776 overrideColor = true;
779 RenderObjects(painter, toolObjects);
780 overrideColor = false;
783 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
784 QString text = QChar(0x2221) + QObject::tr(": %1");
785 informativeText = text.arg(absAngle);
788 informativeText += " (Copy)";
791 else if (Global::tool == TTMirror)
793 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
794 painter->DrawHandle(toolPoint[0]);
795 else if ((Global::toolState == TSPoint2) && shiftDown)
796 painter->DrawHandle(toolPoint[1]);
799 if (toolPoint[0] == toolPoint[1])
802 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
803 // painter->DrawLine(toolPoint[0], toolPoint[1]);
804 painter->DrawLine(mirrorPoint, toolPoint[1]);
805 // Likely we need a tool container for this... (now we do!)
809 painter->SetPen(0x00FF00, 2.0, LSSolid);
810 overrideColor = true;
813 RenderObjects(painter, toolObjects);
814 overrideColor = false;
817 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
819 if (absAngle > 180.0)
822 QString text = QChar(0x2221) + QObject::tr(": %1");
823 informativeText = text.arg(absAngle);
826 informativeText += " (Copy)";
832 void DrawingView::LineHandler(int mode, Point p)
837 if (Global::toolState == TSNone)
844 if (Global::toolState == TSNone)
851 if (Global::toolState == TSNone)
853 Global::toolState = TSPoint2;
854 // Prevent spurious line from drawing...
855 toolPoint[1] = toolPoint[0];
857 else if ((Global::toolState == TSPoint2) && shiftDown)
859 // Key override is telling us to make a new line, not continue the
861 toolPoint[0] = toolPoint[1];
865 Line * l = new Line(toolPoint[0], toolPoint[1]);
866 l->layer = Global::activeLayer;
867 document.objects.push_back(l);
868 toolPoint[0] = toolPoint[1];
874 void DrawingView::CircleHandler(int mode, Point p)
879 if (Global::toolState == TSNone)
886 if (Global::toolState == TSNone)
893 if (Global::toolState == TSNone)
895 Global::toolState = TSPoint2;
896 // Prevent spurious line from drawing...
897 toolPoint[1] = toolPoint[0];
899 else if ((Global::toolState == TSPoint2) && shiftDown)
901 // Key override is telling us to make a new line, not continue the
903 toolPoint[0] = toolPoint[1];
907 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
908 Circle * c = new Circle(toolPoint[0], length);
909 c->layer = Global::activeLayer;
910 document.objects.push_back(c);
911 toolPoint[0] = toolPoint[1];
912 Global::toolState = TSNone;
918 void DrawingView::ArcHandler(int mode, Point p)
923 if (Global::toolState == TSNone)
925 else if (Global::toolState == TSPoint2)
927 else if (Global::toolState == TSPoint3)
934 if (Global::toolState == TSNone)
936 else if (Global::toolState == TSPoint2)
938 else if (Global::toolState == TSPoint3)
945 if (Global::toolState == TSNone)
947 // Prevent spurious line from drawing...
948 toolPoint[1] = toolPoint[0];
949 Global::toolState = TSPoint2;
951 else if (Global::toolState == TSPoint2)
955 // Key override is telling us to start circle at new center, not
956 // continue the current one.
957 toolPoint[0] = toolPoint[1];
961 // Set the radius in toolPoint[1].x
962 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
963 Global::toolState = TSPoint3;
965 else if (Global::toolState == TSPoint3)
967 // Set the angle in toolPoint[2].x
968 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
969 Global::toolState = TSPoint4;
973 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
974 double span = endAngle - toolPoint[2].x;
979 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
980 arc->layer = Global::activeLayer;
981 document.objects.push_back(arc);
982 Global::toolState = TSNone;
988 void DrawingView::RotateHandler(int mode, Point p)
993 if (Global::toolState == TSNone)
996 SavePointsFrom(select, toolScratch);
997 Global::toolState = TSPoint1;
999 else if (Global::toolState == TSPoint1)
1006 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1008 else if (Global::toolState == TSPoint2)
1015 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1016 std::vector<void *>::iterator j = select.begin();
1017 std::vector<Object>::iterator i = toolScratch.begin();
1019 for(; i!=toolScratch.end(); i++, j++)
1022 Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
1023 Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
1024 Object * obj2 = (Object *)(*j);
1028 if (obj.type == OTArc)
1030 obj2->angle[0] = obj.angle[0] + angle;
1032 if (obj2->angle[0] > PI_TIMES_2)
1033 obj2->angle[0] -= PI_TIMES_2;
1040 if (Global::toolState == TSPoint1)
1042 Global::toolState = TSPoint2;
1043 // Prevent spurious line from drawing...
1044 toolPoint[1] = toolPoint[0];
1046 else if ((Global::toolState == TSPoint2) && shiftDown)
1048 // Key override is telling us to make a new line, not continue the
1050 toolPoint[0] = toolPoint[1];
1054 // Either we're finished with our rotate, or we're stamping a copy.
1057 // Stamp a copy of the selection at the current rotation & bail
1058 std::vector<void *> temp;
1059 CopyObjects(select, temp);
1060 ClearSelected(temp);
1061 AddObjectsTo(document.objects, temp);
1062 RestorePointsTo(select, toolScratch);
1067 Global::toolState = TSPoint1;
1068 SavePointsFrom(select, toolScratch);
1073 // Reset the selection if shift held down...
1075 RestorePointsTo(select, toolScratch);
1079 // Reset selection when key is let up
1081 RotateHandler(ToolMouseMove, toolPoint[1]);
1085 RestorePointsTo(select, toolScratch);
1090 void DrawingView::MirrorHandler(int mode, Point p)
1095 if (Global::toolState == TSNone)
1098 SavePointsFrom(select, toolScratch);
1099 Global::toolState = TSPoint1;
1101 else if (Global::toolState == TSPoint1)
1108 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1110 else if (Global::toolState == TSPoint2)
1117 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1118 std::vector<void *>::iterator j = select.begin();
1119 std::vector<Object>::iterator i = toolScratch.begin();
1121 for(; i!=toolScratch.end(); i++, j++)
1124 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1125 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1126 Object * obj2 = (Object *)(*j);
1130 if (obj.type == OTArc)
1132 // This is 2*mirror angle - obj angle - obj span
1133 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1135 if (obj2->angle[0] > PI_TIMES_2)
1136 obj2->angle[0] -= PI_TIMES_2;
1143 if (Global::toolState == TSPoint1)
1145 Global::toolState = TSPoint2;
1146 // Prevent spurious line from drawing...
1147 toolPoint[1] = toolPoint[0];
1149 else if ((Global::toolState == TSPoint2) && shiftDown)
1151 // Key override is telling us to make a new line, not continue the
1153 toolPoint[0] = toolPoint[1];
1157 // Either we're finished with our rotate, or we're stamping a copy.
1160 // Stamp a copy of the selection at the current rotation & bail
1161 std::vector<void *> temp;
1162 CopyObjects(select, temp);
1163 ClearSelected(temp);
1164 AddObjectsTo(document.objects, temp);
1165 RestorePointsTo(select, toolScratch);
1170 Global::toolState = TSPoint1;
1171 SavePointsFrom(select, toolScratch);
1176 // Reset the selection if shift held down...
1178 RestorePointsTo(select, toolScratch);
1182 // Reset selection when key is let up
1184 MirrorHandler(ToolMouseMove, toolPoint[1]);
1188 RestorePointsTo(select, toolScratch);
1193 void DrawingView::mousePressEvent(QMouseEvent * event)
1195 if (event->button() == Qt::LeftButton)
1197 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1199 // Handle tool processing, if any
1202 if (Global::snapToGrid)
1203 point = SnapPointToGrid(point);
1205 //Also, may want to figure out if hovering over a snap point on an object,
1206 //snap to grid if not.
1207 // Snap to object point if valid...
1208 // if (Global::snapPointIsValid)
1209 // point = Global::snapPoint;
1211 ToolHandler(ToolMouseDown, point);
1215 // Clear the selection only if CTRL isn't being held on click
1217 ClearSelected(document.objects);
1218 // ClearSelection();
1220 // If any objects are being hovered on click, add them to the selection
1224 AddHoveredToSelection();
1225 update(); // needed??
1226 GetHovered(hover); // prolly needed
1228 // Needed for grab & moving objects
1229 // We do it *after*... why? (doesn't seem to confer any advantage...)
1230 if (Global::snapToGrid)
1231 oldPoint = SnapPointToGrid(point);
1236 // Didn't hit any object and not using a tool, so do a selection rectangle
1237 Global::selectionInProgress = true;
1238 Global::selection.setTopLeft(QPointF(point.x, point.y));
1239 Global::selection.setBottomRight(QPointF(point.x, point.y));
1241 else if (event->button() == Qt::MiddleButton)
1244 oldPoint = Vector(event->x(), event->y());
1245 // Should also change the mouse pointer as well...
1246 setCursor(Qt::SizeAllCursor);
1251 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1253 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1254 Global::selection.setBottomRight(QPointF(point.x, point.y));
1255 // Only needs to be done here, as mouse down is always preceded by movement
1256 Global::snapPointIsValid = false;
1257 hoveringIntersection = false;
1260 if (event->buttons() & Qt::MiddleButton)
1262 point = Vector(event->x(), event->y());
1263 // Since we're using Qt coords for scrolling, we have to adjust them here to
1264 // conform to Cartesian coords, since the origin is using Cartesian. :-)
1265 Vector delta(oldPoint, point);
1266 delta /= Global::zoom;
1268 Global::origin -= delta;
1270 UpdateGridBackground();
1276 // If we're doing a selection rect, see if any objects are engulfed by it
1277 // (implies left mouse button held down)
1278 if (Global::selectionInProgress)
1280 CheckObjectBounds();
1285 // Handle object movement (left button down & over an object)
1286 if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
1288 if (Global::snapToGrid)
1289 point = SnapPointToGrid(point);
1291 HandleObjectMovement(point);
1297 // Do object hit testing...
1298 bool needUpdate = HitTestObjects(point);
1300 // Check for multi-hover...
1306 // int numIntersecting = Geometry::Intersects((Object *)hover[0], (Object *)hover[1], &t, &u);
1307 Geometry::Intersects((Object *)hover[0], (Object *)hover[1]);
1308 int numIntersecting = Global::numIntersectParams;
1309 double t = Global::intersectParam[0];
1310 double u = Global::intersectParam[1];
1312 if (numIntersecting > 0)
1314 Vector v1 = Geometry::GetPointForParameter((Object *)hover[0], t);
1315 Vector v2 = Geometry::GetPointForParameter((Object *)hover[1], u);
1316 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1317 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1319 hoveringIntersection = true;
1320 intersectionPoint = v1;
1323 numIntersecting = Global::numIntersectPoints;
1325 if (numIntersecting > 0)
1327 Vector v1 = Global::intersectPoint[0];
1328 QString text = tr("Intersection <%1, %2>");
1329 informativeText = text.arg(v1.x).arg(v1.y);
1331 hoveringIntersection = true;
1332 intersectionPoint = v1;
1336 // Do tool handling, if any are active...
1339 if (Global::snapToGrid)
1340 point = SnapPointToGrid(point);
1342 ToolHandler(ToolMouseMove, point);
1345 // This is used to draw the tool crosshair...
1348 if (needUpdate || Global::tool)
1353 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1355 if (event->button() == Qt::LeftButton)
1357 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1358 //could set it up to use the document's update function (assumes that all object updates
1359 //are being reported correctly:
1360 // if (document.NeedsUpdate())
1361 // Do an update if collided with at least *one* object in the document
1367 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1368 ToolHandler(ToolMouseUp, point);
1372 if (Global::selectionInProgress)
1373 Global::selectionInProgress = false;
1375 informativeText.clear();
1376 // Should we be doing this automagically? Hmm...
1377 // Clear our vectors
1382 std::vector<void *>::iterator i;
1384 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1386 if (((Object *)(*i))->selected)
1387 select.push_back(*i);
1389 //hmm, this is no good, too late to do any good :-P
1390 // if ((*i)->hovered)
1391 // hover.push_back(*i);
1394 else if (event->button() == Qt::MiddleButton)
1397 setCursor(Qt::ArrowCursor);
1402 void DrawingView::wheelEvent(QWheelEvent * event)
1404 double zoomFactor = 1.25;
1405 QSize sizeWin = size();
1406 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1407 center = Painter::QtToCartesianCoords(center);
1409 // This is not centering for some reason. Need to figure out why. :-/
1410 if (event->delta() > 0)
1412 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1413 Global::origin = newOrigin;
1414 Global::zoom *= zoomFactor;
1418 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1419 Global::origin = newOrigin;
1420 Global::zoom /= zoomFactor;
1423 // Global::gridSpacing = gridPixels / Painter::zoom;
1424 // UpdateGridBackground();
1425 SetGridSize(Global::gridSpacing * Global::zoom);
1427 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1431 void DrawingView::keyPressEvent(QKeyEvent * event)
1433 bool oldShift = shiftDown;
1434 bool oldCtrl = ctrlDown;
1436 if (event->key() == Qt::Key_Shift)
1438 else if (event->key() == Qt::Key_Control)
1441 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1444 ToolHandler(ToolKeyDown, Point(0, 0));
1451 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1453 bool oldShift = shiftDown;
1454 bool oldCtrl = ctrlDown;
1456 if (event->key() == Qt::Key_Shift)
1458 else if (event->key() == Qt::Key_Control)
1461 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1464 ToolHandler(ToolKeyUp, Point(0, 0));
1472 // This looks strange, but it's really quite simple: We want a point that's
1473 // more than half-way to the next grid point to snap there while conversely we
1474 // want a point that's less than half-way to to the next grid point then snap
1475 // to the one before it. So we add half of the grid spacing to the point, then
1476 // divide by it so that we can remove the fractional part, then multiply it
1477 // back to get back to the correct answer.
1479 Point DrawingView::SnapPointToGrid(Point point)
1481 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
1482 point /= Global::gridSpacing;
1483 point.x = floor(point.x);//need to fix this for negative numbers...
1484 point.y = floor(point.y);
1485 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
1486 point *= Global::gridSpacing;
1491 void DrawingView::CheckObjectBounds(void)
1493 std::vector<void *>::iterator i;
1496 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1498 Object * obj = (Object *)(*i);
1499 obj->selected = false;
1505 Line * l = (Line *)obj;
1507 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1514 Circle * c = (Circle *)obj;
1516 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]))
1523 Arc * a = (Arc *)obj;
1525 double start = a->angle[0];
1526 double end = start + a->angle[1];
1527 QPointF p1(cos(start), sin(start));
1528 QPointF p2(cos(end), sin(end));
1529 QRectF bounds(p1, p2);
1532 // Swap X/Y coordinates if they're backwards...
1533 if (bounds.left() > bounds.right())
1535 double temp = bounds.left();
1536 bounds.setLeft(bounds.right());
1537 bounds.setRight(temp);
1540 if (bounds.bottom() > bounds.top())
1542 double temp = bounds.bottom();
1543 bounds.setBottom(bounds.top());
1544 bounds.setTop(temp);
1547 // Doesn't work as advertised! For shame!
1548 bounds = bounds.normalized();
1551 // If the end of the arc is before the beginning, add 360 degrees to it
1555 // Adjust the bounds depending on which axes are crossed
1556 if ((start < PI_OVER_2) && (end > PI_OVER_2))
1559 if ((start < PI) && (end > PI))
1560 bounds.setLeft(-1.0);
1562 if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
1563 bounds.setBottom(-1.0);
1565 if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
1566 bounds.setRight(1.0);
1568 if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
1571 if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
1572 bounds.setLeft(-1.0);
1574 if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
1575 bounds.setBottom(-1.0);
1577 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1578 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1579 bounds.translate(a->p[0].x, a->p[0].y);
1581 if (Global::selection.contains(bounds))
1596 bool DrawingView::HitTestObjects(Point point)
1598 std::vector<void *>::iterator i;
1600 bool needUpdate = false;
1602 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1604 Object * obj = (Object *)(*i);
1610 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1611 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1612 Vector lineSegment = obj->p[1] - obj->p[0];
1613 Vector v1 = point - obj->p[0];
1614 Vector v2 = point - obj->p[1];
1615 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1619 distance = v1.Magnitude();
1621 distance = v2.Magnitude();
1623 // distance = ?Det?(ls, v1) / |ls|
1624 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1625 / lineSegment.Magnitude());
1627 if ((v1.Magnitude() * Global::zoom) < 8.0)
1628 obj->hitPoint[0] = true;
1629 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1630 obj->hitPoint[1] = true;
1631 else if ((distance * Global::zoom) < 5.0)
1632 obj->hitObject = true;
1634 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1636 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1643 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1644 obj->hitPoint[0] = obj->hitObject = false;
1645 double length = Vector::Magnitude(obj->p[0], point);
1647 if ((length * Global::zoom) < 8.0)
1648 obj->hitPoint[0] = true;
1649 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1650 obj->hitObject = true;
1652 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1654 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1661 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
1662 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
1663 double length = Vector::Magnitude(obj->p[0], point);
1664 double angle = Vector::Angle(obj->p[0], point);
1666 // Make sure we get the angle in the correct spot
1667 if (angle < obj->angle[0])
1668 angle += PI_TIMES_2;
1670 // Get the span that we're pointing at...
1671 double span = angle - obj->angle[0];
1673 // N.B.: Still need to hit test the arc start & arc span handles...
1674 double spanAngle = obj->angle[0] + obj->angle[1];
1675 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
1676 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
1677 double length2 = Vector::Magnitude(point, handle1);
1678 double length3 = Vector::Magnitude(point, handle2);
1680 if ((length * Global::zoom) < 8.0)
1681 obj->hitPoint[0] = true;
1682 else if ((length2 * Global::zoom) < 8.0)
1683 obj->hitPoint[1] = true;
1684 else if ((length3 * Global::zoom) < 8.0)
1685 obj->hitPoint[2] = true;
1686 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
1687 obj->hitObject = true;
1689 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
1691 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
1703 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1711 void DrawingView::HandleObjectMovement(Point point)
1713 Point delta = point - oldPoint;
1714 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
1715 Object * obj = (Object *)hover[0];
1716 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
1717 //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"));
1722 if (obj->hitPoint[0])
1724 else if (obj->hitPoint[1])
1726 else if (obj->hitObject)
1734 if (obj->hitPoint[0])
1736 else if (obj->hitObject)
1738 //this doesn't work. we need to save this on mouse down for this to work correctly!
1739 // double oldRadius = obj->radius[0];
1740 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1742 QString text = QObject::tr("Radius: %1");//\nScale: %2%");
1743 informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
1748 if (obj->hitPoint[0])
1750 else if (obj->hitPoint[1])
1752 // Change the Arc's span (handle #1)
1755 double angle = Vector::Angle(obj->p[0], point);
1756 double delta = angle - obj->angle[0];
1759 delta += PI_TIMES_2;
1761 obj->angle[1] -= delta;
1762 obj->angle[0] = angle;
1764 if (obj->angle[1] < 0)
1765 obj->angle[1] += PI_TIMES_2;
1767 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
1768 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);
1772 double angle = Vector::Angle(obj->p[0], point);
1773 obj->angle[0] = angle;
1774 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
1775 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
1777 else if (obj->hitPoint[2])
1779 // Change the Arc's span (handle #2)
1782 double angle = Vector::Angle(obj->p[0], point);
1783 obj->angle[1] = angle - obj->angle[0];
1785 if (obj->angle[1] < 0)
1786 obj->angle[1] += PI_TIMES_2;
1788 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
1789 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);
1793 double angle = Vector::Angle(obj->p[0], point);
1794 obj->angle[0] = angle - obj->angle[1];
1796 if (obj->angle[0] < 0)
1797 obj->angle[0] += PI_TIMES_2;
1799 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
1800 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
1802 else if (obj->hitObject)
1809 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1810 QString text = QObject::tr("Radius: %1");
1811 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
1823 // This returns true if we've moved over an object...
1824 if (document.PointerMoved(point)) // <-- This
1825 // This is where the object would do automagic dragging & shit. Since we don't
1826 // do that anymore, we need a strategy to handle it.
1830 Now objects handle mouse move snapping as well. The code below mainly works only
1831 for tools; we need to fix it so that objects work as well...
1833 There's a problem with the object point snapping in that it's dependent on the
1834 order of the objects in the document. Most likely this is because it counts the
1835 selected object last and thus fucks up the algorithm. Need to fix this...
1839 // Do object snapping here. Grid snapping on mouse down is done in the
1840 // objects themselves, only because we have to hit test the raw point,
1841 // not the snapped point. There has to be a better way...!
1842 if (document.penultimateObjectHovered)
1844 // Two objects are hovered, see if we have an intersection point
1845 if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
1848 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
1852 Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
1853 Global::snapPointIsValid = true;
1856 else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
1859 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
1863 Global::snapPoint = p1;
1864 Global::snapPointIsValid = true;
1868 double d1 = Vector(point, p1).Magnitude();
1869 double d2 = Vector(point, p2).Magnitude();
1872 Global::snapPoint = p1;
1874 Global::snapPoint = p2;
1876 Global::snapPointIsValid = true;
1882 // Otherwise, it was a single object hovered...
1888 if (Global::snapToGrid)
1889 point = Global::SnapPointToGrid(point);
1891 // We always snap to object points, and they take precendence over
1893 if (Global::snapPointIsValid)
1894 point = Global::snapPoint;
1896 toolAction->MouseMoved(point);