4 // Part of the Architektonas Project
5 // (C) 2011-2020 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
8 // JLH = James Hammons <jlhamm@acm.org>
11 // --- ---------- ------------------------------------------------------------
12 // JLH 03/22/2011 Created this file
13 // JLH 09/29/2011 Added middle mouse button panning
18 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
19 // to a left-handed system and we need a right-handed one. [DONE]
20 // - Fixed length tool doesn't work on lines [DONE]
25 // - Layer locking (hiding works)
26 // - Fixed angle tool doesn't work on lines
27 // - Make it so "dirty" flag reflects drawing state
30 // Uncomment this for debugging...
32 //#define DEBUGFOO // Various tool debugging...
33 //#define DEBUGTP // Toolpalette debugging...
35 #include "drawingview.h"
40 #include "mathconstants.h"
46 #define BACKGROUND_MAX_SIZE 512
49 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
50 // The value in the settings file will override this.
51 useAntialiasing(true), numHovered(0), shiftDown(false),
52 ctrlDown(false), altDown(false),
53 gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
54 scale(1.0), offsetX(-10), offsetY(-10), document(true),
55 gridPixels(0), collided(false), scrollDrag(false), hoverPointValid(false),
56 hoveringIntersection(false), dragged(NULL), draggingObject(false),
57 angleSnap(false), dirty(false)
59 //wtf? doesn't work except in c++11??? document = { 0 };
60 setBackgroundRole(QPalette::Base);
61 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
63 Global::gridSpacing = 12.0; // In base units (inch is default)
65 Line * line = new Line(Vector(5, 5), Vector(50, 40), 2.0, 0xFF7F00, LSDash);
67 document.Add(new Line(Vector(50, 40), Vector(10, 83)));
68 document.Add(new Line(Vector(10, 83), Vector(17, 2)));
69 document.Add(new Circle(Vector(100, 100), 36));
70 document.Add(new Circle(Vector(50, 150), 49));
71 document.Add(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
72 document.Add(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
73 document.Add(new Text(Vector(10, 83), "Here is some awesome text!"));
78 Here we set the grid size in pixels--12 in this case. Initially, we have our
79 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
80 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
81 to be able to set the size of the background grid (which we do here at an
82 arbitrary 12 pixels) to anything we want (within reason, of course :-).
84 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
86 drawing->gridSpacing = 12.0 / Global::zoom;
88 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
89 translated to Cartesian coordinates through this. (Initially, Global::zoom is
90 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
92 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
93 convenience function than any measure of absolutes. Doing things that way we
94 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
95 shittiness that comes with it.
97 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
98 a certain way, which means we should probably create something else in those
99 objects to take its place--like some kind of scale factor. This would seem to
100 imply that certain point sizes actually *do* tie things like fonts to absolute
101 sizes on the screen, but not necessarily because you could have an inch scale
102 with text that is quite small relative to other objects on the screen, which
103 currently you have to zoom in to see (and which blows up the text). Point sizes
104 in an application like this are a bit meaningless; even though an inch is an
105 inch regardless of the zoom level a piece of text can be larger or smaller than
106 this. Maybe this is the case for having a base unit and basing point sizes off
109 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
110 base units. What that means is that if you have a 12px grid with a 6" grid size
111 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
113 Dimensions now have a "size" parameter to set their absolute size in relation
114 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
115 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
116 scaled the same way as the arrowheads.
118 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
119 need a thickness parameter similar to the "size" param for dimensions. (And now
123 SetGridSize(12); // This is in pixels
127 void DrawingView::SetGridSize(uint32_t size)
130 if (size == gridPixels)
133 // Recreate the background bitmap
135 QPainter pmp(&gridBackground);
136 pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
137 pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
139 for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
141 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
142 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
147 // Set up new BG brush & zoom level (pixels per base unit)
148 Global::zoom = gridPixels / Global::gridSpacing;
149 UpdateGridBackground();
153 void DrawingView::UpdateGridBackground(void)
155 // Transform the origin to Qt coordinates
156 Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
157 int x = (int)pixmapOrigin.x;
158 int y = (int)pixmapOrigin.y;
159 // Use mod arithmetic to grab the correct swatch of background
161 Negative numbers still screw it up... Need to think about what we're
162 trying to do here. The fact that it worked with 72 seems to have been pure luck.
163 It seems the problem is negative numbers: We can't let that happen.
164 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
167 The bitmap looks like this:
177 @ x = 1, we want it to look like:
179 -+---+---+---+---+---
182 -+---+---+---+---+---
187 Which means we need to grab the sample from x = 3. @ x = -1:
197 Which means we need to grab the sample from x = 1. Which means we have to take
198 the mirror of the modulus of gridPixels.
200 Doing a mod of a negative number is problematic: 1st, the compiler converts the
201 negative number to an unsigned int, then it does the mod. Gets you wrong answers
202 most of the time, unless you use a power of 2. :-P So what we do here is just
203 take the modulus of the negation, which means we don't have to worry about
206 The positive case looks gruesome (and it is) but it boils down to this: We take
207 the modulus of the X coordinate, then mirror it by subtraction from the
208 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
209 gridPixels. But we need the case where the result equalling gridPixels to be
210 zero; so we do another modulus operation on the result to achieve this.
215 x = (gridPixels - (x % gridPixels)) % gridPixels;
220 y = (gridPixels - (y % gridPixels)) % gridPixels;
222 // Here we grab a section of the bigger pixmap, so that the background
223 // *looks* like it's scrolling...
224 QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
225 QPalette pal = palette();
226 pal.setBrush(backgroundRole(), QBrush(pm));
227 setAutoFillBackground(true);
233 // Basically, we just make a single pass through the Container. If the layer #
234 // is less than the layer # being deleted, then do nothing. If the layer # is
235 // equal to the layer # being deleted, then delete the object. If the layer #
236 // is greater than the layer # being deleted, then set the layer # to its layer
239 void DrawingView::DeleteCurrentLayer(int layer)
241 //printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
242 VPVectorIter i = document.objects.begin();
244 while (i != document.objects.end())
246 Object * obj = (Object *)(*i);
248 if (obj->layer < layer)
250 else if (obj->layer == layer)
252 document.objects.erase(i);
262 // We've just done a destructive action, so update the screen!
267 void DrawingView::HandleLayerToggle(void)
269 // A layer's visibility was toggled, so update the screen...
275 // A layer was moved up or down in the layer list, so we have to swap the
276 // document's object's layer numbers in the layers that were swapped.
278 void DrawingView::HandleLayerSwap(int layer1, int layer2)
280 // !!! FIX !!! This doesn't properly handle container contents...
281 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
282 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
284 Object * obj = (Object *)(*i);
286 if (obj->layer == layer1)
288 else if (obj->layer == layer2)
294 void DrawingView::HandlePenWidth(float width)
296 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
298 Object * obj = (Object *)(*i);
299 obj->thickness = width;
304 void DrawingView::HandlePenStyle(int style)
306 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
308 Object * obj = (Object *)(*i);
314 void DrawingView::HandlePenColor(uint32_t color)
316 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
318 Object * obj = (Object *)(*i);
324 void DrawingView::HandlePenStamp(void)
326 VPVector flat = Flatten(select);
328 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
330 Object * obj = (Object *)(*i);
332 if (obj->type != OTText)
333 obj->thickness = Global::penWidth;
335 obj->style = Global::penStyle;
336 obj->color = Global::penColor;
343 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
345 // This is undoing the transform, e.g. going from client coords to local
346 // coords. In essence, the height - y is height + (y * -1), the (y * -1)
347 // term doing the conversion of the y-axis from increasing bottom to top.
348 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
352 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
354 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
355 // No voodoo here, it's just grouped wrong to see it. It should be:
356 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
357 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
361 void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
363 // printf("DrawingView::focusOutEvent()...\n");
364 // Make sure all modkeys being held are marked as released when the app
365 // loses focus (N.B.: This only works because the app sets the focus policy
366 // of this object to something other than Qt::NoFocus)
367 shiftDown = ctrlDown = altDown = false;
369 setCursor(Qt::ArrowCursor);
373 void DrawingView::paintEvent(QPaintEvent * /*event*/)
375 QPainter qtPainter(this);
376 Painter painter(&qtPainter);
379 qtPainter.setRenderHint(QPainter::Antialiasing);
381 Global::viewportHeight = size().height();
383 // Draw coordinate axes
384 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
385 painter.DrawLine(0, -16384, 0, 16384);
386 painter.DrawLine(-16384, 0, 16384, 0);
388 // Do object rendering...
389 for(int i=0; i<Global::numLayers; i++)
391 if (Global::layerHidden[i] == false)
392 RenderObjects(&painter, document.objects, i);
395 // Do tool rendering, if any...
398 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
399 painter.DrawCrosshair(oldPoint);
403 // Do selection rectangle rendering, if any
404 if (Global::selectionInProgress)
406 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
407 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
408 painter.DrawRect(Global::selection);
411 if (hoveringIntersection)
412 painter.DrawHandle(intersectionPoint);
415 painter.DrawHandle(hoverPoint);
417 if (!informativeText.isEmpty())
418 painter.DrawInformativeText(informativeText);
423 // Renders objects in the passed in vector
426 N.B.: Since we have "hoverPointValid" drawing regular object handles above,
427 we can probably do away with a lot of them that are being done down below.
429 [Well, it seems to work OK *except* when you move one of the points, then you get to see nothing. Is it worth fixing there to get rid of problems here? Have to investigate...]
431 void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool ignoreLayer/*= false*/)
433 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
435 Object * obj = (Object *)(*i);
436 float scaledThickness = Global::scale * obj->thickness;
438 // If the object isn't on the current layer being drawn, skip it
439 if (!ignoreLayer && (obj->layer != layer))
442 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
444 painter->SetPen(0x00FF00, 2.0, LSSolid);
448 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
449 painter->SetBrush(obj->color);
451 if (obj->selected || obj->hitObject)
452 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
458 painter->DrawLine(obj->p[0], obj->p[1]);
460 if (obj->hitPoint[0])
461 painter->DrawHandle(obj->p[0]);
463 if (obj->hitPoint[1])
464 painter->DrawHandle(obj->p[1]);
467 painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
472 painter->SetBrush(QBrush(Qt::NoBrush));
473 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
475 if (obj->hitPoint[0])
476 painter->DrawHandle(obj->p[0]);
481 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
483 if (obj->hitPoint[0])
484 painter->DrawHandle(obj->p[0]);
486 if (obj->hitPoint[1])
487 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
489 if (obj->hitPoint[2])
490 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
496 Dimension * d = (Dimension *)obj;
498 Vector v(d->p[0], d->p[1]);
499 double angle = v.Angle();
500 Vector unit = v.Unit();
501 d->lp[0] = d->p[0], d->lp[1] = d->p[1];
503 double x1, y1, length;
505 if (d->subtype == DTLinearVert)
507 if ((angle < 0) || (angle > HALF_TAU))
509 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
510 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
511 ortho = Vector(1.0, 0);
512 angle = THREE_QTR_TAU;
516 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
517 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
518 ortho = Vector(-1.0, 0);
522 d->lp[0].x = d->lp[1].x = x1;
523 length = fabs(d->p[0].y - d->p[1].y);
525 else if (d->subtype == DTLinearHorz)
527 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
529 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
530 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
531 ortho = Vector(0, 1.0);
536 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
537 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
538 ortho = Vector(0, -1.0);
542 d->lp[0].y = d->lp[1].y = y1;
543 length = fabs(d->p[0].x - d->p[1].x);
545 else if (d->subtype == DTLinear)
547 angle = Vector(d->lp[0], d->lp[1]).Angle();
548 ortho = Vector::Normal(d->lp[0], d->lp[1]);
549 length = v.Magnitude();
552 unit = Vector(d->lp[0], d->lp[1]).Unit();
554 Point p1 = d->lp[0] + (ortho * (d->offset + (10.0 * scaledThickness)));
555 Point p2 = d->lp[1] + (ortho * (d->offset + (10.0 * scaledThickness)));
556 Point p3 = d->lp[0] + (ortho * (d->offset + (16.0 * scaledThickness)));
557 Point p4 = d->lp[1] + (ortho * (d->offset + (16.0 * scaledThickness)));
558 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
559 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
562 The numbers hardcoded into here, what are they?
563 I believe they are pixels.
565 // Draw extension lines (if certain type)
566 painter->DrawLine(p3, p5);
567 painter->DrawLine(p4, p6);
569 // Calculate whether or not the arrowheads are too crowded to put
570 // inside the extension lines. 9.0 is the length of the arrowhead.
571 double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
573 // On the screen, it's acting like this is actually 58%...
574 // This is correct, we want it to happen at > 50%
577 // Draw main dimension line + arrowheads
578 painter->DrawLine(p1, p2);
579 painter->DrawArrowhead(p1, p2, scaledThickness);
580 painter->DrawArrowhead(p2, p1, scaledThickness);
584 // Draw outside arrowheads
585 Point p7 = p1 - (unit * 9.0 * scaledThickness);
586 Point p8 = p2 + (unit * 9.0 * scaledThickness);
587 painter->DrawArrowhead(p1, p7, scaledThickness);
588 painter->DrawArrowhead(p2, p8, scaledThickness);
589 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
590 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
593 // Draw length of dimension line...
594 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
595 Point ctr = p2 + (Vector(p2, p1) / 2.0);
600 dimText = QString("%1\"").arg(length);
603 double feet = (double)((int)length / 12);
604 double inches = length - (feet * 12.0);
607 dimText = QString("%1'").arg(feet);
609 dimText = QString("%1' %2\"").arg(feet).arg(inches);
613 Where is the text offset? It looks like it's drawing in the center, but obviously it isn't. It isn't here, it's in Painter::DrawAngledText().
615 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
619 Point hp1 = (p1 + p2) / 2.0;
620 Point hp2 = (p1 + hp1) / 2.0;
621 Point hp3 = (hp1 + p2) / 2.0;
625 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
626 painter->SetBrush(QBrush(QColor(Qt::magenta)));
627 painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
628 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
631 painter->DrawHandle(hp1);
632 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
636 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
637 painter->SetBrush(QBrush(QColor(Qt::magenta)));
638 painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
639 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
642 painter->DrawHandle(hp2);
643 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
647 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
648 painter->SetBrush(QBrush(QColor(Qt::magenta)));
649 painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
650 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
653 painter->DrawHandle(hp3);
656 if (obj->hitPoint[0])
657 painter->DrawHandle(obj->p[0]);
659 if (obj->hitPoint[1])
660 painter->DrawHandle(obj->p[1]);
667 Text * t = (Text *)obj;
669 if (t->measured == false)
671 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
675 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
691 // Containers require recursive rendering...
692 Container * c = (Container *)obj;
693 printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
694 RenderObjects(painter, (*c).objects, layer, ignoreLayer);
696 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
697 // Containers also have special indicators showing they are selected
698 if (c->selected || c->hitObject)
700 // Rect r = GetObjectExtents(obj);
701 // painter->DrawRectCorners(r);
702 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
716 // This toggles the selection being hovered (typically, only 1 object)
718 void DrawingView::AddHoveredToSelection(void)
720 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
722 if (((Object *)(*i))->hovered)
723 // ((Object *)(*i))->selected = true;
724 ((Object *)(*i))->selected = !((Object *)(*i))->selected;
729 VPVector DrawingView::GetSelection(void)
733 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
735 if (((Object *)(*i))->selected)
743 VPVector DrawingView::GetHovered(void)
747 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
749 if (((Object *)(*i))->hovered)
757 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
759 Global::screenSize = Vector(size().width(), size().height());
760 UpdateGridBackground();
764 void DrawingView::ToolHandler(int mode, Point p)
766 // Drop angle snap until it's needed
769 if (Global::tool == TTLine)
770 LineHandler(mode, p);
771 else if (Global::tool == TTCircle)
772 CircleHandler(mode, p);
773 else if (Global::tool == TTArc)
775 else if (Global::tool == TTRotate)
776 RotateHandler(mode, p);
777 else if (Global::tool == TTMirror)
778 MirrorHandler(mode, p);
779 else if (Global::tool == TTDimension)
780 DimensionHandler(mode, p);
781 else if (Global::tool == TTTriangulate)
782 TriangulateHandler(mode, p);
783 else if (Global::tool == TTTrim)
784 TrimHandler(mode, p);
785 else if (Global::tool == TTParallel)
786 ParallelHandler(mode, p);
790 void DrawingView::ToolDraw(Painter * painter)
792 if (Global::tool == TTLine)
794 if (Global::toolState == TSNone)
796 painter->DrawHandle(toolPoint[0]);
798 else if ((Global::toolState == TSPoint2) && shiftDown)
800 painter->DrawHandle(toolPoint[1]);
804 painter->DrawLine(toolPoint[0], toolPoint[1]);
805 painter->DrawHandle(toolPoint[1]);
807 Vector v(toolPoint[0], toolPoint[1]);
808 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
809 double absLength = v.Magnitude();
810 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
811 informativeText = text.arg(absLength).arg(absAngle);
814 else if (Global::tool == TTCircle)
816 if (Global::toolState == TSNone)
818 painter->DrawHandle(toolPoint[0]);
820 else if ((Global::toolState == TSPoint2) && shiftDown)
822 painter->DrawHandle(toolPoint[1]);
826 painter->DrawCross(toolPoint[0]);
827 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
828 painter->SetBrush(QBrush(Qt::NoBrush));
829 painter->DrawEllipse(toolPoint[0], length, length);
830 QString text = tr("Radius: %1 in.");
831 informativeText = text.arg(length);
834 else if (Global::tool == TTArc)
836 if (Global::toolState == TSNone)
838 painter->DrawHandle(toolPoint[0]);
840 else if (Global::toolState == TSPoint2)
842 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
843 painter->SetBrush(QBrush(Qt::NoBrush));
844 painter->DrawEllipse(toolPoint[0], length, length);
845 painter->DrawLine(toolPoint[0], toolPoint[1]);
846 painter->DrawHandle(toolPoint[1]);
847 QString text = tr("Radius: %1 in.");
848 informativeText = text.arg(length);
850 else if (Global::toolState == TSPoint3)
852 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
853 painter->DrawLine(toolPoint[0], toolPoint[2]);
854 painter->SetBrush(QBrush(Qt::NoBrush));
855 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
856 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
857 QString text = tr("Angle start: %1") + QChar(0x00B0);
858 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
862 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
863 double span = angle - toolPoint[2].x;
868 painter->DrawLine(toolPoint[0], toolPoint[3]);
869 painter->SetBrush(QBrush(Qt::NoBrush));
870 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
871 painter->SetPen(0xFF00FF, 2.0, LSSolid);
872 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
873 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
874 QString text = tr("Arc span: %1") + QChar(0x00B0);
875 informativeText = text.arg(RADIANS_TO_DEGREES * span);
878 else if (Global::tool == TTRotate)
880 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
881 painter->DrawHandle(toolPoint[0]);
882 else if ((Global::toolState == TSPoint2) && shiftDown)
883 painter->DrawHandle(toolPoint[1]);
886 if (toolPoint[0] == toolPoint[1])
889 painter->DrawLine(toolPoint[0], toolPoint[1]);
891 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
892 QString text = QChar(0x2221) + QObject::tr(": %1");
893 informativeText = text.arg(absAngle);
896 informativeText += " (Copy)";
899 else if (Global::tool == TTMirror)
901 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
902 painter->DrawHandle(toolPoint[0]);
903 else if ((Global::toolState == TSPoint2) && shiftDown)
904 painter->DrawHandle(toolPoint[1]);
907 if (toolPoint[0] == toolPoint[1])
910 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
911 painter->DrawLine(mirrorPoint, toolPoint[1]);
913 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
915 if (absAngle > 180.0)
918 QString text = QChar(0x2221) + QObject::tr(": %1");
919 informativeText = text.arg(absAngle);
922 informativeText += " (Copy)";
925 if (Global::tool == TTDimension)
927 if (Global::toolState == TSNone)
929 painter->DrawHandle(toolPoint[0]);
931 else if ((Global::toolState == TSPoint2) && shiftDown)
933 painter->DrawHandle(toolPoint[1]);
937 painter->DrawLine(toolPoint[0], toolPoint[1]);
938 painter->DrawHandle(toolPoint[1]);
940 Vector v(toolPoint[0], toolPoint[1]);
941 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
942 double absLength = v.Magnitude();
943 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
944 informativeText = text.arg(absLength).arg(absAngle);
950 void DrawingView::LineHandler(int mode, Point p)
955 if (Global::toolState == TSNone)
963 if (Global::toolState == TSNone)
971 if (Global::toolState == TSNone)
973 Global::toolState = TSPoint2;
974 // Prevent spurious line from drawing...
975 toolPoint[1] = toolPoint[0];
977 else if ((Global::toolState == TSPoint2) && shiftDown)
979 // Key override is telling us to make a new line, not continue the
981 toolPoint[0] = toolPoint[1];
985 Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
986 l->layer = Global::activeLayer;
987 document.objects.push_back(l);
988 toolPoint[0] = toolPoint[1];
994 void DrawingView::CircleHandler(int mode, Point p)
999 if (Global::toolState == TSNone)
1007 if (Global::toolState == TSNone)
1015 if (Global::toolState == TSNone)
1017 Global::toolState = TSPoint2;
1018 // Prevent spurious line from drawing...
1019 toolPoint[1] = toolPoint[0];
1021 else if ((Global::toolState == TSPoint2) && shiftDown)
1023 // Key override is telling us to make a new line, not continue the
1025 toolPoint[0] = toolPoint[1];
1029 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1030 Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1031 c->layer = Global::activeLayer;
1032 document.objects.push_back(c);
1033 toolPoint[0] = toolPoint[1];
1034 Global::toolState = TSNone;
1040 void DrawingView::ArcHandler(int mode, Point p)
1045 if (Global::toolState == TSNone)
1047 else if (Global::toolState == TSPoint2)
1049 else if (Global::toolState == TSPoint3)
1057 if (Global::toolState == TSNone)
1059 else if (Global::toolState == TSPoint2)
1061 else if (Global::toolState == TSPoint3)
1075 if (Global::toolState == TSNone)
1077 // Prevent spurious line from drawing...
1078 toolPoint[1] = toolPoint[0];
1079 Global::toolState = TSPoint2;
1081 else if (Global::toolState == TSPoint2)
1085 // Key override is telling us to start arc at new center, not
1086 // continue the current one.
1087 toolPoint[0] = toolPoint[1];
1091 // Set the radius in toolPoint[1].x
1092 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1093 Global::toolState = TSPoint3;
1095 else if (Global::toolState == TSPoint3)
1097 // Set the angle in toolPoint[2].x
1098 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1099 Global::toolState = TSPoint4;
1103 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1104 double span = endAngle - toolPoint[2].x;
1109 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1110 arc->layer = Global::activeLayer;
1111 document.objects.push_back(arc);
1112 Global::toolState = TSNone;
1118 void DrawingView::RotateHandler(int mode, Point p)
1123 if (Global::toolState == TSNone)
1126 // SavePointsFrom(select, toolScratch);
1127 CopyObjects(select, toolScratch2);
1128 Global::toolState = TSPoint1;
1130 else if (Global::toolState == TSPoint1)
1138 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1140 else if (Global::toolState == TSPoint2)
1148 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1149 VPVectorIter j = select.begin();
1150 // std::vector<Object>::iterator i = toolScratch.begin();
1151 VPVectorIter i = toolScratch2.begin();
1153 // for(; i!=toolScratch.end(); i++, j++)
1154 for(; i!=toolScratch2.end(); i++, j++)
1156 // Object objT = *i;
1157 // Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1158 // Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1159 Object * objT = (Object *)(*i);
1160 Object * objS = (Object *)(*j);
1162 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1163 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1168 // if (objT.type == OTArc || objT.type == OTText)
1169 if (objT->type == OTArc || objT->type == OTText)
1171 // objS->angle[0] = objT.angle[0] + angle;
1172 objS->angle[0] = objT->angle[0] + angle;
1174 if (objS->angle[0] > TAU)
1175 objS->angle[0] -= TAU;
1177 // else if (objT.type == OTContainer)
1178 else if (objT->type == OTContainer)
1180 // OK, this doesn't work because toolScratch only has points and nothing in the containers... [ACTUALLY... toolScratch is is a vector of type Object... which DOESN'T have an objects vector in it...]
1181 // Container * c = (Container *)&objT;
1182 Container * c = (Container *)objT;
1183 Container * c2 = (Container *)objS;
1184 VPVectorIter l = c->objects.begin();
1185 // TODO: Rotate items in the container
1186 // TODO: Make this recursive
1187 for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1189 Object * obj3 = (Object *)(*k);
1190 Object * obj4 = (Object *)(*l);
1192 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1193 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1197 // obj3->angle[0] = objT.angle[0] + angle;
1198 obj3->angle[0] = obj4->angle[0] + angle;
1200 if (obj3->angle[0] > TAU)
1201 obj3->angle[0] -= TAU;
1204 Rect r = GetObjectExtents(objS);
1205 c2->p[0] = r.TopLeft();
1206 c2->p[1] = r.BottomRight();
1214 if (Global::toolState == TSPoint1)
1216 Global::toolState = TSPoint2;
1217 // Prevent spurious line from drawing...
1218 toolPoint[1] = toolPoint[0];
1220 else if ((Global::toolState == TSPoint2) && shiftDown)
1222 // Key override is telling us to make a new line, not continue the
1224 toolPoint[0] = toolPoint[1];
1228 // Either we're finished with our rotate, or we're stamping a copy.
1231 // Stamp a copy of the selection at the current rotation & bail
1233 CopyObjects(select, temp);
1234 ClearSelected(temp);
1235 AddObjectsTo(document.objects, temp);
1236 // RestorePointsTo(select, toolScratch);
1237 RestorePointsTo(select, toolScratch2);
1242 Global::toolState = TSPoint1;
1243 // SavePointsFrom(select, toolScratch);
1244 DeleteContents(toolScratch2);
1245 CopyObjects(select, toolScratch2);
1251 // Reset the selection if shift held down...
1253 // RestorePointsTo(select, toolScratch);
1254 RestorePointsTo(select, toolScratch2);
1259 // Reset selection when key is let up
1261 RotateHandler(ToolMouseMove, toolPoint[1]);
1266 // RestorePointsTo(select, toolScratch);
1267 RestorePointsTo(select, toolScratch2);
1268 DeleteContents(toolScratch2);
1273 void DrawingView::MirrorHandler(int mode, Point p)
1278 if (Global::toolState == TSNone)
1281 SavePointsFrom(select, toolScratch);
1282 Global::toolState = TSPoint1;
1284 else if (Global::toolState == TSPoint1)
1292 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1294 else if (Global::toolState == TSPoint2)
1302 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1303 VPVectorIter j = select.begin();
1304 std::vector<Object>::iterator i = toolScratch.begin();
1306 for(; i!=toolScratch.end(); i++, j++)
1309 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1310 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1311 Object * obj2 = (Object *)(*j);
1316 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1317 a negative start angle which makes it impossible to interact with.
1320 if (obj.type == OTArc)
1322 // This is 2*mirror angle - obj angle - obj span
1323 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1325 if (obj2->angle[0] > TAU)
1326 obj2->angle[0] -= TAU;
1334 if (Global::toolState == TSPoint1)
1336 Global::toolState = TSPoint2;
1337 // Prevent spurious line from drawing...
1338 toolPoint[1] = toolPoint[0];
1340 else if ((Global::toolState == TSPoint2) && shiftDown)
1342 // Key override is telling us to make a new line, not continue the
1344 toolPoint[0] = toolPoint[1];
1348 // Either we're finished with our rotate, or we're stamping a copy.
1351 // Stamp a copy of the selection at the current rotation & bail
1353 CopyObjects(select, temp);
1354 ClearSelected(temp);
1355 AddObjectsTo(document.objects, temp);
1356 RestorePointsTo(select, toolScratch);
1361 Global::toolState = TSPoint1;
1362 SavePointsFrom(select, toolScratch);
1368 // Reset the selection if shift held down...
1370 RestorePointsTo(select, toolScratch);
1375 // Reset selection when key is let up
1377 MirrorHandler(ToolMouseMove, toolPoint[1]);
1382 RestorePointsTo(select, toolScratch);
1387 void DrawingView::DimensionHandler(int mode, Point p)
1392 if (Global::toolState == TSNone)
1400 if (Global::toolState == TSNone)
1408 if (Global::toolState == TSNone)
1410 Global::toolState = TSPoint2;
1411 // Prevent spurious line from drawing...
1412 toolPoint[1] = toolPoint[0];
1414 else if ((Global::toolState == TSPoint2) && shiftDown)
1416 // Key override is telling us to make a new line, not continue the
1418 toolPoint[0] = toolPoint[1];
1422 Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear);
1423 d->layer = Global::activeLayer;
1424 document.objects.push_back(d);
1425 Global::toolState = TSNone;
1431 void DrawingView::TriangulateHandler(int mode, Point/*p*/)
1437 // Skip if nothing hovered...
1438 if (numHovered != 1)
1441 VPVector hover = GetHovered();
1442 Object * obj = (Object *)hover[0];
1444 // Skip if it's not a line...
1445 if (obj->type != OTLine)
1448 if (Global::toolState == TSNone)
1450 else if (Global::toolState == TSPoint2)
1459 if (Global::toolState == TSNone)
1461 else if (Global::toolState == TSPoint2)
1463 else if (Global::toolState == TSPoint3)
1477 if (Global::toolState == TSNone)
1479 Global::toolState = TSPoint2;
1481 else if (Global::toolState == TSPoint2)
1485 // Key override is telling us to start arc at new center, not
1486 // continue the current one.
1487 toolPoint[0] = toolPoint[1];
1491 Global::toolState = TSPoint3;
1495 double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1496 double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1498 Circle c1(toolObj[0]->p[0], len2);
1499 Circle c2(toolObj[0]->p[1], len3);
1501 Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1503 // Only move lines if the triangle formed by them is not degenerate
1504 if (Global::numIntersectPoints > 0)
1506 toolObj[1]->p[0] = toolObj[0]->p[0];
1507 toolObj[1]->p[1] = Global::intersectPoint[0];
1509 toolObj[2]->p[0] = Global::intersectPoint[0];
1510 toolObj[2]->p[1] = toolObj[0]->p[1];
1513 Global::toolState = TSNone;
1519 void DrawingView::TrimHandler(int mode, Point p)
1522 n.b.: this code is lifted straight out of the old oo code. needs to be updated.
1529 Object * toTrim = doc->lastObjectHovered;
1534 Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
1536 // Check to see which case we have...
1537 // We're trimming point #1...
1540 ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
1544 ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
1548 Point p1 = ((Line *)toTrim)->position + (v * t);
1549 Point p2 = ((Line *)toTrim)->position + (v * u);
1550 Point p3 = ((Line *)toTrim)->endpoint;
1551 ((Line *)toTrim)->endpoint = p1;
1552 Line * line = new Line(p2, p3);
1553 emit ObjectReady(line);
1556 doc->lastObjectHovered = NULL;
1564 Object * toTrim = doc->lastObjectHovered;
1570 if (toTrim->type != OTLine)
1573 double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
1575 std::vector<Object *>::iterator i;
1577 for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
1579 // Can't trim against yourself... :-P
1583 Object * trimAgainst = *i;
1586 if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
1589 int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
1593 // Now what? We don't know which side to trim!
1594 // ... now we do, we know which side of the Line we're on!
1595 if ((t1 > t) && (t1 < pointHoveredT))
1598 if ((t1 < u) && (t1 > pointHoveredT))
1603 // Bail out if nothing hovered...
1604 if (numHovered != 1)
1610 VPVector hover = GetHovered();
1611 Object * obj = (Object *)hover[0];
1613 // Skip if it's not a line...
1614 if (obj->type != OTLine)
1621 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1623 // Currently only deal with line against line trimming, can expand to
1624 // others as well (line/circle, circle/circle, line/arc, etc)
1626 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1628 obj = (Object *)(*i);
1630 if (obj == toolObj[0])
1632 else if (obj->type != OTLine)
1635 Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1637 if (Global::numIntersectParams > 0)
1639 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1660 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1685 void DrawingView::mousePressEvent(QMouseEvent * event)
1687 if (event->button() == Qt::LeftButton)
1689 //printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
1690 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1692 // Handle tool processing, if any
1695 if (hoveringIntersection)
1696 point = intersectionPoint;
1697 else if (hoverPointValid)
1699 else if (Global::snapToGrid)
1700 point = SnapPointToGrid(point);
1702 //Also, may want to figure out if hovering over a snap point on an
1703 //object, snap to grid if not.
1704 // Snap to object point if valid...
1705 // if (Global::snapPointIsValid)
1706 // point = Global::snapPoint;
1708 ToolHandler(ToolMouseDown, point);
1712 // Clear the selection only if CTRL isn't being held on click
1714 ClearSelected(document.objects);
1715 // ClearSelection();
1717 // If any objects are being hovered on click, add them to the selection
1721 AddHoveredToSelection();
1722 update(); // needed??
1723 // GetHovered(hover); // prolly needed
1724 VPVector hover2 = GetHovered();
1725 dragged = (Object *)hover2[0];
1726 draggingObject = true;
1727 //printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
1729 // Alert the pen widget
1730 emit ObjectSelected(dragged);
1732 // See if anything is using just a straight click on a handle
1733 if (HandleObjectClicked())
1735 draggingObject = false;
1740 // Needed for grab & moving objects
1741 // We do it *after*... why? (doesn't seem to confer any advantage...)
1742 if (hoveringIntersection)
1743 oldPoint = intersectionPoint;
1744 else if (hoverPointValid)
1745 oldPoint = hoverPoint;
1746 else if (Global::snapToGrid)
1747 oldPoint = SnapPointToGrid(point);
1749 // Needed for fixed length handling
1750 if (Global::fixedLength)
1752 if (dragged->type == OTLine)
1754 dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1758 if (dragged->type == OTCircle)
1760 // Save for informative text, uh, er, informing
1761 dragged->length = dragged->radius[0];
1767 // Didn't hit any object and not using a tool, so do a selection
1769 Global::selectionInProgress = true;
1770 Global::selection.setTopLeft(QPointF(point.x, point.y));
1771 Global::selection.setBottomRight(QPointF(point.x, point.y));
1772 select = GetSelection();
1774 else if (event->button() == Qt::MiddleButton)
1777 oldPoint = Vector(event->x(), event->y());
1778 // Should also change the mouse pointer as well...
1779 setCursor(Qt::SizeAllCursor);
1784 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1786 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1787 Global::selection.setBottomRight(QPointF(point.x, point.y));
1788 // Only needs to be done here, as mouse down is always preceded by movement
1789 Global::snapPointIsValid = false;
1790 hoveringIntersection = false;
1791 oldScrollPoint = Vector(event->x(), event->y());
1794 if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1796 point = Vector(event->x(), event->y());
1797 // Since we're using Qt coords for scrolling, we have to adjust them
1798 // here to conform to Cartesian coords, since the origin is using
1800 Vector delta(oldPoint, point);
1801 delta /= Global::zoom;
1803 Global::origin -= delta;
1805 UpdateGridBackground();
1811 // If we're doing a selection rect, see if any objects are engulfed by it
1812 // (implies left mouse button held down)
1813 if (Global::selectionInProgress)
1815 CheckObjectBounds();
1817 // Make sure previously selected objects stay selected (CTRL held)
1818 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
1819 ((Object *)(*i))->selected = true;
1825 // Do object hit testing...
1826 bool needUpdate = HitTestObjects(point);
1827 VPVector hover2 = GetHovered();
1832 printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1834 if (hover2.size() > 0)
1835 printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1840 // Check for multi-hover...
1843 //need to check for case where hover is over 2 circles and a 3rd's center...
1844 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1846 Geometry::Intersects(obj1, obj2);
1847 int numIntersecting = Global::numIntersectParams;
1848 double t = Global::intersectParam[0];
1849 double u = Global::intersectParam[1];
1851 if (numIntersecting > 0)
1853 Vector v1 = Geometry::GetPointForParameter(obj1, t);
1854 Vector v2 = Geometry::GetPointForParameter(obj2, u);
1855 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1856 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1858 hoveringIntersection = true;
1859 intersectionPoint = v1;
1862 numIntersecting = Global::numIntersectPoints;
1864 if (numIntersecting > 0)
1866 Vector v1 = Global::intersectPoint[0];
1868 if (numIntersecting == 2)
1870 Vector v2 = Global::intersectPoint[1];
1872 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1876 QString text = tr("Intersection <%1, %2>");
1877 informativeText = text.arg(v1.x).arg(v1.y);
1878 hoveringIntersection = true;
1879 intersectionPoint = v1;
1882 else if (numHovered == 1)
1884 Object * obj = (Object *)hover2[0];
1886 if (obj->type == OTLine)
1889 Not sure that this is the best way to handle this, but it works(TM)...
1890 Except when lines are overlapping, then it doesn't work... !!! FIX !!!
1892 Point midpoint = Geometry::Midpoint((Line *)obj);
1893 Vector v1 = Vector::Magnitude(midpoint, point);
1895 if ((v1.Magnitude() * Global::zoom) < 8.0)
1897 QString text = tr("Midpoint <%1, %2>");
1898 informativeText = text.arg(midpoint.x).arg(midpoint.y);
1899 hoverPointValid = true;
1900 hoverPoint = midpoint;
1906 // Handle object movement (left button down & over an object)
1907 if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
1909 if (hoveringIntersection)
1910 point = intersectionPoint;
1911 else if (hoverPointValid)
1913 else if (Global::snapToGrid)
1914 point = SnapPointToGrid(point);
1916 HandleObjectMovement(point);
1922 // Do tool handling, if any are active...
1925 if (hoveringIntersection)
1926 point = intersectionPoint;
1927 else if (hoverPointValid)
1929 else if (Global::snapToGrid)
1932 point = SnapPointToAngle(point);
1934 point = SnapPointToGrid(point);
1937 ToolHandler(ToolMouseMove, point);
1940 // This is used to draw the tool crosshair...
1943 if (needUpdate || Global::tool)
1948 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1950 if (event->button() == Qt::LeftButton)
1952 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1953 //could set it up to use the document's update function (assumes that all object
1954 //updates are being reported correctly:
1955 // if (document.NeedsUpdate())
1956 // Do an update if collided with at least *one* object in the document
1962 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1963 ToolHandler(ToolMouseUp, point);
1967 // if (Global::selectionInProgress)
1968 Global::selectionInProgress = false;
1970 informativeText.clear();
1971 // Should we be doing this automagically? Hmm...
1972 // Clear our vectors
1976 // Scoop 'em up (do we need to??? Seems we do, because keyboard movement uses it. Also, tools use it too. But we can move it out of here)
1977 select = GetSelection();
1979 draggingObject = false;
1981 else if (event->button() == Qt::MiddleButton)
1984 setCursor(Qt::ArrowCursor);
1989 void DrawingView::wheelEvent(QWheelEvent * event)
1991 double zoomFactor = 1.25;
1992 QSize sizeWin = size();
1993 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1994 center = Painter::QtToCartesianCoords(center);
1996 // This is not centering for some reason. Need to figure out why. :-/
1997 if (event->delta() > 0)
1999 if (Global::zoom > 20.0)
2002 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
2003 Global::origin = newOrigin;
2004 Global::zoom *= zoomFactor;
2008 if (Global::zoom < 0.25)
2011 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
2012 Global::origin = newOrigin;
2013 Global::zoom /= zoomFactor;
2016 // Global::gridSpacing = gridPixels / Painter::zoom;
2017 // UpdateGridBackground();
2018 SetGridSize(Global::gridSpacing * Global::zoom);
2020 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
2024 void DrawingView::keyPressEvent(QKeyEvent * event)
2026 bool oldShift = shiftDown;
2027 bool oldCtrl = ctrlDown;
2028 bool oldAlt = altDown;
2030 if (event->key() == Qt::Key_Shift)
2032 else if (event->key() == Qt::Key_Control)
2034 else if (event->key() == Qt::Key_Alt)
2037 // If there's a change in any of the modifier key states, pass it on to
2038 // the current tool's handler
2039 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2042 ToolHandler(ToolKeyDown, Point(0, 0));
2047 if (oldAlt != altDown)
2050 setCursor(Qt::SizeAllCursor);
2051 oldPoint = oldScrollPoint;
2054 if (select.size() > 0)
2056 if (event->key() == Qt::Key_Up)
2058 TranslateObjects(select, Point(0, +1.0));
2061 else if (event->key() == Qt::Key_Down)
2063 TranslateObjects(select, Point(0, -1.0));
2066 else if (event->key() == Qt::Key_Right)
2068 TranslateObjects(select, Point(+1.0, 0));
2071 else if (event->key() == Qt::Key_Left)
2073 TranslateObjects(select, Point(-1.0, 0));
2080 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2082 bool oldShift = shiftDown;
2083 bool oldCtrl = ctrlDown;
2084 bool oldAlt = altDown;
2086 if (event->key() == Qt::Key_Shift)
2088 else if (event->key() == Qt::Key_Control)
2090 else if (event->key() == Qt::Key_Alt)
2093 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2096 ToolHandler(ToolKeyUp, Point(0, 0));
2101 if (oldAlt != altDown)
2104 setCursor(Qt::ArrowCursor);
2110 // This looks strange, but it's really quite simple: We want a point that's
2111 // more than half-way to the next grid point to snap there while conversely we
2112 // want a point that's less than half-way to to the next grid point then snap
2113 // to the one before it. So we add half of the grid spacing to the point, then
2114 // divide by it so that we can remove the fractional part, then multiply it
2115 // back to get back to the correct answer.
2117 Point DrawingView::SnapPointToGrid(Point point)
2119 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
2120 point /= Global::gridSpacing;
2121 point.x = floor(point.x);//need to fix this for negative numbers...
2122 point.y = floor(point.y);
2123 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
2124 point *= Global::gridSpacing;
2129 Point DrawingView::SnapPointToAngle(Point point)
2131 // Snap to a single digit angle (using toolpoint #1 as the center)
2132 double angle = Vector::Angle(toolPoint[0], point);
2133 double length = Vector::Magnitude(toolPoint[0], point);
2135 // Convert from radians to degrees
2136 double degAngle = angle * RADIANS_TO_DEGREES;
2137 double snapAngle = (double)((int)(degAngle + 0.5));
2140 v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2141 point = toolPoint[0] + v;
2147 Rect DrawingView::GetObjectExtents(Object * obj)
2149 // Default to empty rect, if object checks below fail for some reason
2157 rect = Rect(obj->p[0], obj->p[1]);
2163 rect = Rect(obj->p[0], obj->p[0]);
2164 rect.Expand(obj->radius[0]);
2170 Arc * a = (Arc *)obj;
2172 double start = a->angle[0];
2173 double end = start + a->angle[1];
2174 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2176 // If the end of the arc is before the beginning, add 360 degrees to it
2180 // Adjust the bounds depending on which axes are crossed
2181 if ((start < QTR_TAU) && (end > QTR_TAU))
2184 if ((start < HALF_TAU) && (end > HALF_TAU))
2187 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2190 if ((start < TAU) && (end > TAU))
2193 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2196 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2199 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2202 rect *= a->radius[0];
2203 rect.Translate(a->p[0]);
2209 Text * t = (Text *)obj;
2210 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2216 Container * c = (Container *)obj;
2217 VPVectorIter i = c->objects.begin();
2218 rect = GetObjectExtents((Object *)*i);
2221 for(; i!=c->objects.end(); i++)
2222 rect |= GetObjectExtents((Object *)*i);
2233 void DrawingView::CheckObjectBounds(void)
2237 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2239 Object * obj = (Object *)(*i);
2240 obj->selected = false;
2247 Line * l = (Line *)obj;
2249 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2257 Circle * c = (Circle *)obj;
2259 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]))
2267 Arc * a = (Arc *)obj;
2269 double start = a->angle[0];
2270 double end = start + a->angle[1];
2271 QPointF p1(cos(start), sin(start));
2272 QPointF p2(cos(end), sin(end));
2273 QRectF bounds(p1, p2);
2276 // Swap X/Y coordinates if they're backwards...
2277 if (bounds.left() > bounds.right())
2279 double temp = bounds.left();
2280 bounds.setLeft(bounds.right());
2281 bounds.setRight(temp);
2284 if (bounds.bottom() > bounds.top())
2286 double temp = bounds.bottom();
2287 bounds.setBottom(bounds.top());
2288 bounds.setTop(temp);
2291 // Doesn't work as advertised! For shame!
2292 bounds = bounds.normalized();
2295 // If the end of the arc is before the beginning, add 360 degrees
2300 // Adjust the bounds depending on which axes are crossed
2301 if ((start < QTR_TAU) && (end > QTR_TAU))
2304 if ((start < HALF_TAU) && (end > HALF_TAU))
2305 bounds.setLeft(-1.0);
2307 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2308 bounds.setBottom(-1.0);
2310 if ((start < TAU) && (end > TAU))
2311 bounds.setRight(1.0);
2313 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2316 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2317 bounds.setLeft(-1.0);
2319 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2320 bounds.setBottom(-1.0);
2322 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2323 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2324 bounds.translate(a->p[0].x, a->p[0].y);
2326 if (Global::selection.contains(bounds))
2334 Text * t = (Text *)obj;
2335 Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2337 if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2345 Container * c = (Container *)obj;
2347 if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2360 bool DrawingView::HitTestObjects(Point point)
2364 bool needUpdate = false;
2365 hoverPointValid = false;
2367 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2369 Object * obj = (Object *)(*i);
2371 // If we're seeing the object we're dragging, skip it
2372 if (draggingObject && (obj == dragged))
2375 if (HitTest(obj, point) == true)
2381 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2382 emit ObjectHovered(obj);
2390 bool DrawingView::HitTest(Object * obj, Point point)
2392 bool needUpdate = false;
2398 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2399 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2400 Vector lineSegment = obj->p[1] - obj->p[0];
2401 Vector v1 = point - obj->p[0];
2402 Vector v2 = point - obj->p[1];
2403 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2407 distance = v1.Magnitude();
2409 distance = v2.Magnitude();
2411 // distance = ?Det?(ls, v1) / |ls|
2412 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2413 / lineSegment.Magnitude());
2415 if ((v1.Magnitude() * Global::zoom) < 8.0)
2417 obj->hitPoint[0] = true;
2418 hoverPoint = obj->p[0];
2419 hoverPointValid = true;
2421 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2423 obj->hitPoint[1] = true;
2424 hoverPoint = obj->p[1];
2425 hoverPointValid = true;
2427 else if ((distance * Global::zoom) < 5.0)
2428 obj->hitObject = true;
2430 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2432 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2440 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2441 obj->hitPoint[0] = obj->hitObject = false;
2442 double length = Vector::Magnitude(obj->p[0], point);
2444 if ((length * Global::zoom) < 8.0)
2446 obj->hitPoint[0] = true;
2447 hoverPoint = obj->p[0];
2448 hoverPointValid = true;
2450 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2451 obj->hitObject = true;
2453 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2455 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2463 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2464 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2465 double length = Vector::Magnitude(obj->p[0], point);
2466 double angle = Vector::Angle(obj->p[0], point);
2468 // Make sure we get the angle in the correct spot
2469 if (angle < obj->angle[0])
2472 // Get the span that we're pointing at...
2473 double span = angle - obj->angle[0];
2475 // N.B.: Still need to hit test the arc start & arc span handles...
2476 double spanAngle = obj->angle[0] + obj->angle[1];
2477 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2478 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2479 double length2 = Vector::Magnitude(point, handle1);
2480 double length3 = Vector::Magnitude(point, handle2);
2482 if ((length * Global::zoom) < 8.0)
2484 obj->hitPoint[0] = true;
2485 hoverPoint = obj->p[0];
2486 hoverPointValid = true;
2488 else if ((length2 * Global::zoom) < 8.0)
2490 obj->hitPoint[1] = true;
2491 hoverPoint = handle1;
2492 hoverPointValid = true;
2494 else if ((length3 * Global::zoom) < 8.0)
2496 obj->hitPoint[2] = true;
2497 hoverPoint = handle2;
2498 hoverPointValid = true;
2500 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2501 obj->hitObject = true;
2503 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2505 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2513 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2514 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2516 Dimension * d = (Dimension *)obj;
2518 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2519 // Get our line parallel to our points
2520 float scaledThickness = Global::scale * obj->thickness;
2522 Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2523 Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2525 Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2526 Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2528 Point p3(p1, point);
2530 Vector v1(d->p[0], point);
2531 Vector v2(d->p[1], point);
2532 Vector lineSegment(p1, p2);
2533 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2535 Point midpoint = (p1 + p2) / 2.0;
2536 Point hFSPoint = Point(midpoint, point);
2537 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2538 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2541 distance = v1.Magnitude();
2543 distance = v2.Magnitude();
2545 // distance = ?Det?(ls, v1) / |ls|
2546 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2547 / lineSegment.Magnitude());
2549 if ((v1.Magnitude() * Global::zoom) < 8.0)
2550 obj->hitPoint[0] = true;
2551 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2552 obj->hitPoint[1] = true;
2553 else if ((distance * Global::zoom) < 5.0)
2554 obj->hitObject = true;
2556 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2557 obj->hitPoint[2] = true;
2558 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2559 obj->hitPoint[3] = true;
2560 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2561 obj->hitPoint[4] = true;
2563 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2565 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2573 Text * t = (Text *)obj;
2574 bool oldHO = obj->hitObject;
2575 obj->hitObject = false;
2577 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2578 //printf("Text: p=<%lf, %lf>, w/h=%lf, %lf [lrtb=%lf, %lf, %lf, %lf]\n", obj->p[0].x, obj->p[0].y, t->extents.Width(), t->extents.Height(), t->extents.l, t->extents.r, t->extents.t, t->extents.b);
2580 if (r.Contains(point))
2581 obj->hitObject = true;
2583 obj->hovered = (obj->hitObject ? true : false);
2585 if (oldHO != obj->hitObject)
2593 // Containers must be recursively tested... Or do they???
2595 So the idea here is to flatten the structure, *then* test the objects within. Flattening includes the Containers as well; we can do this because it's pointers all the way down.
2597 // bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2598 // Object * oldClicked = c->clicked;
2600 still need to compare old state to new state, and set things up based upon that...
2601 likely we can just rely on the object itself and steal its state like we have in the commented out portion below; can prolly rewrite the HitTest() portion to be one line: needUpdate = HitTest(cObj, point);
2602 Well, you could if there was only one object in the Container. But since there isn't, we have to keep the if HitTest() == true then needUpdate = true bit. Because otherwise, a false result anywhere will kill the needed update elsewhere.
2604 Container * c = (Container *)obj;
2605 c->hitObject = false;
2609 VPVector flat = Flatten(c);
2611 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2612 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
2614 Object * cObj = (Object *)(*i);
2616 // Skip the flattened containers (if any)...
2617 if (cObj->type == OTContainer)
2620 // We do it this way instead of needUpdate = HitTest() because we
2621 // are checking more than one object, and that way of doing will
2622 // not return consistent results.
2623 if (HitTest(cObj, point) == true)
2625 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2627 // c->hitObject = true;
2628 // c->clicked = cObj;
2629 // c->hovered = true;
2632 // Same reasons for doing it this way here apply.
2633 if (cObj->hitObject == true)
2635 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2636 c->hitObject = true;
2640 if (cObj->hitPoint[0] == true)
2642 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2643 c->hitPoint[0] = true;
2647 if (cObj->hitPoint[1] == true)
2649 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2650 c->hitPoint[1] = true;
2654 if (cObj->hitPoint[2] == true)
2656 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2657 c->hitPoint[2] = true;
2661 if (cObj->hovered == true)
2662 c->hovered = true;//*/
2676 bool DrawingView::HandleObjectClicked(void)
2678 if (dragged->type == OTDimension)
2680 Dimension * d = (Dimension *)dragged;
2684 // Hit the "flip sides" switch, so flip 'em
2685 Point temp = d->p[0];
2690 else if (d->hitPoint[3])
2692 // There are three cases here: aligned, horizontal, & vertical.
2693 // Aligned and horizontal do the same thing, vertical goes back to
2695 if (d->subtype == DTLinearVert)
2696 d->subtype = DTLinear;
2698 d->subtype = DTLinearVert;
2702 else if (d->hitPoint[4])
2704 // There are three cases here: aligned, horizontal, & vertical.
2705 // Aligned and vertical do the same thing, horizontal goes back to
2707 if (d->subtype == DTLinearHorz)
2708 d->subtype = DTLinear;
2710 d->subtype = DTLinearHorz;
2720 void DrawingView::HandleObjectMovement(Point point)
2722 Point delta = point - oldPoint;
2723 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2724 // Object * obj = (Object *)hover[0];
2725 Object * obj = dragged;
2726 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2727 //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"));
2732 if (obj->hitPoint[0])
2734 if (Global::fixedLength)
2736 Vector line = point - obj->p[1];
2737 Vector unit = line.Unit();
2738 point = obj->p[1] + (unit * obj->length);
2743 else if (obj->hitPoint[1])
2745 if (Global::fixedLength)
2747 Vector line = point - obj->p[0];
2748 Vector unit = line.Unit();
2749 point = obj->p[0] + (unit * obj->length);
2754 else if (obj->hitObject)
2763 if (obj->hitPoint[0])
2765 else if (obj->hitObject)
2767 double oldRadius = obj->length;
2768 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2770 QString text = QObject::tr("Radius: %1\nScale: %2%");
2771 informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2777 if (obj->hitPoint[0])
2779 else if (obj->hitPoint[1])
2781 // Change the Arc's span (handle #1)
2784 double angle = Vector::Angle(obj->p[0], point);
2785 double delta = angle - obj->angle[0];
2790 obj->angle[1] -= delta;
2791 obj->angle[0] = angle;
2793 if (obj->angle[1] < 0)
2794 obj->angle[1] += TAU;
2796 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2797 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);
2801 double angle = Vector::Angle(obj->p[0], point);
2802 obj->angle[0] = angle;
2803 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2804 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2806 else if (obj->hitPoint[2])
2808 // Change the Arc's span (handle #2)
2811 double angle = Vector::Angle(obj->p[0], point);
2812 obj->angle[1] = angle - obj->angle[0];
2814 if (obj->angle[1] < 0)
2815 obj->angle[1] += TAU;
2817 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2818 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);
2822 double angle = Vector::Angle(obj->p[0], point);
2823 obj->angle[0] = angle - obj->angle[1];
2825 if (obj->angle[0] < 0)
2826 obj->angle[0] += TAU;
2828 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2829 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2831 else if (obj->hitObject)
2838 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2839 QString text = QObject::tr("Radius: %1");
2840 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2846 if (obj->hitPoint[0])
2848 else if (obj->hitPoint[1])
2850 else if (obj->hitObject)
2852 // Move measurement lines in/out
2855 Dimension * d = (Dimension *)obj;
2856 double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
2857 float scaledThickness = Global::scale * obj->thickness;
2858 // Looks like offset is 0 to +MAX, but line is at 10.0. So
2859 // anything less than 10.0 should set the offset to 0.
2862 if (dist > (10.0 * scaledThickness))
2863 d->offset = dist - (10.0 * scaledThickness);
2881 // This is shitty, but works for now until I can code up something
2884 The idea is to make it so whichever point on the object in question is being dragged becomes the snap point for the container; shouldn't be too difficult to figure out how to do this.
2886 // TranslateObject(obj, delta);
2887 TranslateContainer((Container *)obj, point, delta);
2896 void DrawingView::AddDimensionTo(void * o)
2898 Object * obj = (Object *)o;
2903 document.Add(new Dimension(obj->p[0], obj->p[1]));