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 // This shouldn't be done here, because it fucks up the scrollwheel zooming...
149 // Global::zoom = gridPixels / Global::gridSpacing;
150 UpdateGridBackground();
154 void DrawingView::UpdateGridBackground(void)
156 // Transform the origin to Qt coordinates
157 Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
158 int x = (int)pixmapOrigin.x;
159 int y = (int)pixmapOrigin.y;
160 // Use mod arithmetic to grab the correct swatch of background
162 Negative numbers still screw it up... Need to think about what we're
163 trying to do here. The fact that it worked with 72 seems to have been pure luck.
164 It seems the problem is negative numbers: We can't let that happen.
165 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
168 The bitmap looks like this:
178 @ x = 1, we want it to look like:
180 -+---+---+---+---+---
183 -+---+---+---+---+---
188 Which means we need to grab the sample from x = 3. @ x = -1:
198 Which means we need to grab the sample from x = 1. Which means we have to take
199 the mirror of the modulus of gridPixels.
201 Doing a mod of a negative number is problematic: 1st, the compiler converts the
202 negative number to an unsigned int, then it does the mod. Gets you wrong answers
203 most of the time, unless you use a power of 2. :-P So what we do here is just
204 take the modulus of the negation, which means we don't have to worry about
207 The positive case looks gruesome (and it is) but it boils down to this: We take
208 the modulus of the X coordinate, then mirror it by subtraction from the
209 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
210 gridPixels. But we need the case where the result equalling gridPixels to be
211 zero; so we do another modulus operation on the result to achieve this.
216 x = (gridPixels - (x % gridPixels)) % gridPixels;
221 y = (gridPixels - (y % gridPixels)) % gridPixels;
223 // Here we grab a section of the bigger pixmap, so that the background
224 // *looks* like it's scrolling...
225 QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
226 QPalette pal = palette();
227 pal.setBrush(backgroundRole(), QBrush(pm));
228 setAutoFillBackground(true);
234 // Basically, we just make a single pass through the Container. If the layer #
235 // is less than the layer # being deleted, then do nothing. If the layer # is
236 // equal to the layer # being deleted, then delete the object. If the layer #
237 // is greater than the layer # being deleted, then set the layer # to its layer
240 void DrawingView::DeleteCurrentLayer(int layer)
242 //printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
243 VPVectorIter i = document.objects.begin();
245 while (i != document.objects.end())
247 Object * obj = (Object *)(*i);
249 if (obj->layer < layer)
251 else if (obj->layer == layer)
253 document.objects.erase(i);
263 // We've just done a destructive action, so update the screen!
268 void DrawingView::HandleLayerToggle(void)
270 // A layer's visibility was toggled, so update the screen...
276 // A layer was moved up or down in the layer list, so we have to swap the
277 // document's object's layer numbers in the layers that were swapped.
279 void DrawingView::HandleLayerSwap(int layer1, int layer2)
281 // !!! FIX !!! This doesn't properly handle container contents...
282 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
283 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
285 Object * obj = (Object *)(*i);
287 if (obj->layer == layer1)
289 else if (obj->layer == layer2)
295 void DrawingView::HandlePenWidth(float width)
297 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
299 Object * obj = (Object *)(*i);
300 obj->thickness = width;
305 void DrawingView::HandlePenStyle(int style)
307 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
309 Object * obj = (Object *)(*i);
315 void DrawingView::HandlePenColor(uint32_t color)
317 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
319 Object * obj = (Object *)(*i);
325 void DrawingView::HandlePenStamp(void)
327 VPVector flat = Flatten(select);
329 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
331 Object * obj = (Object *)(*i);
333 if (obj->type != OTText)
334 obj->thickness = Global::penWidth;
336 obj->style = Global::penStyle;
337 obj->color = Global::penColor;
344 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
346 // This is undoing the transform, e.g. going from client coords to local
347 // coords. In essence, the height - y is height + (y * -1), the (y * -1)
348 // term doing the conversion of the y-axis from increasing bottom to top.
349 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
353 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
355 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
356 // No voodoo here, it's just grouped wrong to see it. It should be:
357 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
358 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
362 void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
364 // printf("DrawingView::focusOutEvent()...\n");
365 // Make sure all modkeys being held are marked as released when the app
366 // loses focus (N.B.: This only works because the app sets the focus policy
367 // of this object to something other than Qt::NoFocus)
368 shiftDown = ctrlDown = altDown = false;
370 setCursor(Qt::ArrowCursor);
374 void DrawingView::paintEvent(QPaintEvent * /*event*/)
376 QPainter qtPainter(this);
377 Painter painter(&qtPainter);
380 qtPainter.setRenderHint(QPainter::Antialiasing);
382 Global::viewportHeight = size().height();
384 // Draw coordinate axes
385 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
386 painter.DrawLine(0, -16384, 0, 16384);
387 painter.DrawLine(-16384, 0, 16384, 0);
389 // Do object rendering...
390 for(int i=0; i<Global::numLayers; i++)
392 if (Global::layerHidden[i] == false)
393 RenderObjects(&painter, document.objects, i);
396 // Do tool rendering, if any...
399 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
400 painter.DrawCrosshair(oldPoint);
404 // Do selection rectangle rendering, if any
405 if (Global::selectionInProgress)
407 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
408 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
409 painter.DrawRect(Global::selection);
412 if (hoveringIntersection)
413 painter.DrawHandle(intersectionPoint);
416 painter.DrawHandle(hoverPoint);
418 if (!informativeText.isEmpty())
419 painter.DrawInformativeText(informativeText);
424 // Renders objects in the passed in vector
427 N.B.: Since we have "hoverPointValid" drawing regular object handles above,
428 we can probably do away with a lot of them that are being done down below.
430 [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...]
432 void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool ignoreLayer/*= false*/)
434 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
436 Object * obj = (Object *)(*i);
437 float scaledThickness = Global::scale * obj->thickness;
439 // If the object isn't on the current layer being drawn, skip it
440 if (!ignoreLayer && (obj->layer != layer))
443 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
445 painter->SetPen(0x00FF00, 2.0, LSSolid);
449 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
450 painter->SetBrush(obj->color);
452 if (obj->selected || obj->hitObject)
453 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
459 painter->DrawLine(obj->p[0], obj->p[1]);
461 if (obj->hitPoint[0])
462 painter->DrawHandle(obj->p[0]);
464 if (obj->hitPoint[1])
465 painter->DrawHandle(obj->p[1]);
468 painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
473 painter->SetBrush(QBrush(Qt::NoBrush));
474 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
476 if (obj->hitPoint[0])
477 painter->DrawHandle(obj->p[0]);
482 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
484 if (obj->hitPoint[0])
485 painter->DrawHandle(obj->p[0]);
487 if (obj->hitPoint[1])
488 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
490 if (obj->hitPoint[2])
491 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
497 Dimension * d = (Dimension *)obj;
499 Vector v(d->p[0], d->p[1]);
500 double angle = v.Angle();
501 Vector unit = v.Unit();
502 d->lp[0] = d->p[0], d->lp[1] = d->p[1];
504 double x1, y1, length;
506 if (d->subtype == DTLinearVert)
508 if ((angle < 0) || (angle > HALF_TAU))
510 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
511 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
512 ortho = Vector(1.0, 0);
513 angle = THREE_QTR_TAU;
517 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
518 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
519 ortho = Vector(-1.0, 0);
523 d->lp[0].x = d->lp[1].x = x1;
524 length = fabs(d->p[0].y - d->p[1].y);
526 else if (d->subtype == DTLinearHorz)
528 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
530 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
531 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
532 ortho = Vector(0, 1.0);
537 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
538 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
539 ortho = Vector(0, -1.0);
543 d->lp[0].y = d->lp[1].y = y1;
544 length = fabs(d->p[0].x - d->p[1].x);
546 else if (d->subtype == DTLinear)
548 angle = Vector(d->lp[0], d->lp[1]).Angle();
549 ortho = Vector::Normal(d->lp[0], d->lp[1]);
550 length = v.Magnitude();
553 unit = Vector(d->lp[0], d->lp[1]).Unit();
555 Point p1 = d->lp[0] + (ortho * (d->offset + (10.0 * scaledThickness)));
556 Point p2 = d->lp[1] + (ortho * (d->offset + (10.0 * scaledThickness)));
557 Point p3 = d->lp[0] + (ortho * (d->offset + (16.0 * scaledThickness)));
558 Point p4 = d->lp[1] + (ortho * (d->offset + (16.0 * scaledThickness)));
559 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
560 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
563 The numbers hardcoded into here, what are they?
564 I believe they are pixels.
566 // Draw extension lines (if certain type)
567 painter->DrawLine(p3, p5);
568 painter->DrawLine(p4, p6);
570 // Calculate whether or not the arrowheads are too crowded to put
571 // inside the extension lines. 9.0 is the length of the arrowhead.
572 double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
574 // On the screen, it's acting like this is actually 58%...
575 // This is correct, we want it to happen at > 50%
578 // Draw main dimension line + arrowheads
579 painter->DrawLine(p1, p2);
580 painter->DrawArrowhead(p1, p2, scaledThickness);
581 painter->DrawArrowhead(p2, p1, scaledThickness);
585 // Draw outside arrowheads
586 Point p7 = p1 - (unit * 9.0 * scaledThickness);
587 Point p8 = p2 + (unit * 9.0 * scaledThickness);
588 painter->DrawArrowhead(p1, p7, scaledThickness);
589 painter->DrawArrowhead(p2, p8, scaledThickness);
590 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
591 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
594 // Draw length of dimension line...
595 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
596 Point ctr = p2 + (Vector(p2, p1) / 2.0);
601 dimText = QString("%1\"").arg(length);
604 double feet = (double)((int)length / 12);
605 double inches = length - (feet * 12.0);
608 dimText = QString("%1'").arg(feet);
610 dimText = QString("%1' %2\"").arg(feet).arg(inches);
614 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().
616 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
620 Point hp1 = (p1 + p2) / 2.0;
621 Point hp2 = (p1 + hp1) / 2.0;
622 Point hp3 = (hp1 + p2) / 2.0;
626 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
627 painter->SetBrush(QBrush(QColor(Qt::magenta)));
628 painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
629 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
632 painter->DrawHandle(hp1);
633 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
637 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
638 painter->SetBrush(QBrush(QColor(Qt::magenta)));
639 painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
640 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
643 painter->DrawHandle(hp2);
644 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
648 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
649 painter->SetBrush(QBrush(QColor(Qt::magenta)));
650 painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
651 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
654 painter->DrawHandle(hp3);
657 if (obj->hitPoint[0])
658 painter->DrawHandle(obj->p[0]);
660 if (obj->hitPoint[1])
661 painter->DrawHandle(obj->p[1]);
668 Text * t = (Text *)obj;
670 if (t->measured == false)
672 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
676 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
692 // Containers require recursive rendering...
693 Container * c = (Container *)obj;
694 printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
695 RenderObjects(painter, (*c).objects, layer, ignoreLayer);
697 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
698 // Containers also have special indicators showing they are selected
699 if (c->selected || c->hitObject)
701 // Rect r = GetObjectExtents(obj);
702 // painter->DrawRectCorners(r);
703 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
717 // This toggles the selection being hovered (typically, only 1 object)
719 void DrawingView::AddHoveredToSelection(void)
721 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
723 if (((Object *)(*i))->hovered)
724 // ((Object *)(*i))->selected = true;
725 ((Object *)(*i))->selected = !((Object *)(*i))->selected;
730 VPVector DrawingView::GetSelection(void)
734 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
736 if (((Object *)(*i))->selected)
744 VPVector DrawingView::GetHovered(void)
748 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
750 if (((Object *)(*i))->hovered)
758 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
760 Global::screenSize = Vector(size().width(), size().height());
761 UpdateGridBackground();
765 void DrawingView::ToolHandler(int mode, Point p)
767 // Drop angle snap until it's needed
770 if (Global::tool == TTLine)
771 LineHandler(mode, p);
772 else if (Global::tool == TTCircle)
773 CircleHandler(mode, p);
774 else if (Global::tool == TTArc)
776 else if (Global::tool == TTRotate)
777 RotateHandler(mode, p);
778 else if (Global::tool == TTMirror)
779 MirrorHandler(mode, p);
780 else if (Global::tool == TTDimension)
781 DimensionHandler(mode, p);
782 else if (Global::tool == TTTriangulate)
783 TriangulateHandler(mode, p);
784 else if (Global::tool == TTTrim)
785 TrimHandler(mode, p);
786 else if (Global::tool == TTParallel)
787 ParallelHandler(mode, p);
791 void DrawingView::ToolDraw(Painter * painter)
793 if (Global::tool == TTLine)
795 if (Global::toolState == TSNone)
797 painter->DrawHandle(toolPoint[0]);
799 else if ((Global::toolState == TSPoint2) && shiftDown)
801 painter->DrawHandle(toolPoint[1]);
805 painter->DrawLine(toolPoint[0], toolPoint[1]);
806 painter->DrawHandle(toolPoint[1]);
808 Vector v(toolPoint[0], toolPoint[1]);
809 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
810 double absLength = v.Magnitude();
811 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
812 informativeText = text.arg(absLength).arg(absAngle);
815 else if (Global::tool == TTCircle)
817 if (Global::toolState == TSNone)
819 painter->DrawHandle(toolPoint[0]);
821 else if ((Global::toolState == TSPoint2) && shiftDown)
823 painter->DrawHandle(toolPoint[1]);
827 painter->DrawCross(toolPoint[0]);
828 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
829 painter->SetBrush(QBrush(Qt::NoBrush));
830 painter->DrawEllipse(toolPoint[0], length, length);
831 QString text = tr("Radius: %1 in.");
832 informativeText = text.arg(length);
835 else if (Global::tool == TTArc)
837 if (Global::toolState == TSNone)
839 painter->DrawHandle(toolPoint[0]);
841 else if (Global::toolState == TSPoint2)
843 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
844 painter->SetBrush(QBrush(Qt::NoBrush));
845 painter->DrawEllipse(toolPoint[0], length, length);
846 painter->DrawLine(toolPoint[0], toolPoint[1]);
847 painter->DrawHandle(toolPoint[1]);
848 QString text = tr("Radius: %1 in.");
849 informativeText = text.arg(length);
851 else if (Global::toolState == TSPoint3)
853 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
854 painter->DrawLine(toolPoint[0], toolPoint[2]);
855 painter->SetBrush(QBrush(Qt::NoBrush));
856 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
857 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
858 QString text = tr("Angle start: %1") + QChar(0x00B0);
859 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
863 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
864 double span = angle - toolPoint[2].x;
869 painter->DrawLine(toolPoint[0], toolPoint[3]);
870 painter->SetBrush(QBrush(Qt::NoBrush));
871 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
872 painter->SetPen(0xFF00FF, 2.0, LSSolid);
873 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
874 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
875 QString text = tr("Arc span: %1") + QChar(0x00B0);
876 informativeText = text.arg(RADIANS_TO_DEGREES * span);
879 else if (Global::tool == TTRotate)
881 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
882 painter->DrawHandle(toolPoint[0]);
883 else if ((Global::toolState == TSPoint2) && shiftDown)
884 painter->DrawHandle(toolPoint[1]);
887 if (toolPoint[0] == toolPoint[1])
890 painter->DrawLine(toolPoint[0], toolPoint[1]);
892 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
893 QString text = QChar(0x2221) + QObject::tr(": %1");
894 informativeText = text.arg(absAngle);
897 informativeText += " (Copy)";
900 else if (Global::tool == TTMirror)
902 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
903 painter->DrawHandle(toolPoint[0]);
904 else if ((Global::toolState == TSPoint2) && shiftDown)
905 painter->DrawHandle(toolPoint[1]);
908 if (toolPoint[0] == toolPoint[1])
911 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
912 painter->DrawLine(mirrorPoint, toolPoint[1]);
914 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
916 if (absAngle > 180.0)
919 QString text = QChar(0x2221) + QObject::tr(": %1");
920 informativeText = text.arg(absAngle);
923 informativeText += " (Copy)";
926 if (Global::tool == TTDimension)
928 if (Global::toolState == TSNone)
930 painter->DrawHandle(toolPoint[0]);
932 else if ((Global::toolState == TSPoint2) && shiftDown)
934 painter->DrawHandle(toolPoint[1]);
938 painter->DrawLine(toolPoint[0], toolPoint[1]);
939 painter->DrawHandle(toolPoint[1]);
941 Vector v(toolPoint[0], toolPoint[1]);
942 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
943 double absLength = v.Magnitude();
944 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
945 informativeText = text.arg(absLength).arg(absAngle);
951 void DrawingView::LineHandler(int mode, Point p)
956 if (Global::toolState == TSNone)
964 if (Global::toolState == TSNone)
972 if (Global::toolState == TSNone)
974 Global::toolState = TSPoint2;
975 // Prevent spurious line from drawing...
976 toolPoint[1] = toolPoint[0];
978 else if ((Global::toolState == TSPoint2) && shiftDown)
980 // Key override is telling us to make a new line, not continue the
982 toolPoint[0] = toolPoint[1];
986 Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
987 l->layer = Global::activeLayer;
988 document.objects.push_back(l);
989 toolPoint[0] = toolPoint[1];
995 void DrawingView::CircleHandler(int mode, Point p)
1000 if (Global::toolState == TSNone)
1008 if (Global::toolState == TSNone)
1016 if (Global::toolState == TSNone)
1018 Global::toolState = TSPoint2;
1019 // Prevent spurious line from drawing...
1020 toolPoint[1] = toolPoint[0];
1022 else if ((Global::toolState == TSPoint2) && shiftDown)
1024 // Key override is telling us to make a new line, not continue the
1026 toolPoint[0] = toolPoint[1];
1030 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1031 Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1032 c->layer = Global::activeLayer;
1033 document.objects.push_back(c);
1034 toolPoint[0] = toolPoint[1];
1035 Global::toolState = TSNone;
1041 void DrawingView::ArcHandler(int mode, Point p)
1046 if (Global::toolState == TSNone)
1048 else if (Global::toolState == TSPoint2)
1050 else if (Global::toolState == TSPoint3)
1058 if (Global::toolState == TSNone)
1060 else if (Global::toolState == TSPoint2)
1062 else if (Global::toolState == TSPoint3)
1076 if (Global::toolState == TSNone)
1078 // Prevent spurious line from drawing...
1079 toolPoint[1] = toolPoint[0];
1080 Global::toolState = TSPoint2;
1082 else if (Global::toolState == TSPoint2)
1086 // Key override is telling us to start arc at new center, not
1087 // continue the current one.
1088 toolPoint[0] = toolPoint[1];
1092 // Set the radius in toolPoint[1].x
1093 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1094 Global::toolState = TSPoint3;
1096 else if (Global::toolState == TSPoint3)
1098 // Set the angle in toolPoint[2].x
1099 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1100 Global::toolState = TSPoint4;
1104 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1105 double span = endAngle - toolPoint[2].x;
1110 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1111 arc->layer = Global::activeLayer;
1112 document.objects.push_back(arc);
1113 Global::toolState = TSNone;
1119 void DrawingView::RotateHandler(int mode, Point p)
1124 if (Global::toolState == TSNone)
1127 // SavePointsFrom(select, toolScratch);
1128 CopyObjects(select, toolScratch2);
1129 Global::toolState = TSPoint1;
1131 else if (Global::toolState == TSPoint1)
1139 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1141 else if (Global::toolState == TSPoint2)
1149 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1150 VPVectorIter j = select.begin();
1151 // std::vector<Object>::iterator i = toolScratch.begin();
1152 VPVectorIter i = toolScratch2.begin();
1154 // for(; i!=toolScratch.end(); i++, j++)
1155 for(; i!=toolScratch2.end(); i++, j++)
1157 // Object objT = *i;
1158 // Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1159 // Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1160 Object * objT = (Object *)(*i);
1161 Object * objS = (Object *)(*j);
1163 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1164 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1169 // if (objT.type == OTArc || objT.type == OTText)
1170 if (objT->type == OTArc || objT->type == OTText)
1172 // objS->angle[0] = objT.angle[0] + angle;
1173 objS->angle[0] = objT->angle[0] + angle;
1175 if (objS->angle[0] > TAU)
1176 objS->angle[0] -= TAU;
1178 // else if (objT.type == OTContainer)
1179 else if (objT->type == OTContainer)
1181 // 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...]
1182 // Container * c = (Container *)&objT;
1183 Container * c = (Container *)objT;
1184 Container * c2 = (Container *)objS;
1185 VPVectorIter l = c->objects.begin();
1186 // TODO: Rotate items in the container
1187 // TODO: Make this recursive
1188 for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1190 Object * obj3 = (Object *)(*k);
1191 Object * obj4 = (Object *)(*l);
1193 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1194 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1198 // obj3->angle[0] = objT.angle[0] + angle;
1199 obj3->angle[0] = obj4->angle[0] + angle;
1201 if (obj3->angle[0] > TAU)
1202 obj3->angle[0] -= TAU;
1205 Rect r = GetObjectExtents(objS);
1206 c2->p[0] = r.TopLeft();
1207 c2->p[1] = r.BottomRight();
1215 if (Global::toolState == TSPoint1)
1217 Global::toolState = TSPoint2;
1218 // Prevent spurious line from drawing...
1219 toolPoint[1] = toolPoint[0];
1221 else if ((Global::toolState == TSPoint2) && shiftDown)
1223 // Key override is telling us to make a new line, not continue the
1225 toolPoint[0] = toolPoint[1];
1229 // Either we're finished with our rotate, or we're stamping a copy.
1232 // Stamp a copy of the selection at the current rotation & bail
1234 CopyObjects(select, temp);
1235 ClearSelected(temp);
1236 AddObjectsTo(document.objects, temp);
1237 // RestorePointsTo(select, toolScratch);
1238 RestorePointsTo(select, toolScratch2);
1243 Global::toolState = TSPoint1;
1244 // SavePointsFrom(select, toolScratch);
1245 DeleteContents(toolScratch2);
1246 CopyObjects(select, toolScratch2);
1252 // Reset the selection if shift held down...
1254 // RestorePointsTo(select, toolScratch);
1255 RestorePointsTo(select, toolScratch2);
1260 // Reset selection when key is let up
1262 RotateHandler(ToolMouseMove, toolPoint[1]);
1267 // RestorePointsTo(select, toolScratch);
1268 RestorePointsTo(select, toolScratch2);
1269 DeleteContents(toolScratch2);
1274 void DrawingView::MirrorHandler(int mode, Point p)
1279 if (Global::toolState == TSNone)
1282 SavePointsFrom(select, toolScratch);
1283 Global::toolState = TSPoint1;
1285 else if (Global::toolState == TSPoint1)
1293 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1295 else if (Global::toolState == TSPoint2)
1303 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1304 VPVectorIter j = select.begin();
1305 std::vector<Object>::iterator i = toolScratch.begin();
1307 for(; i!=toolScratch.end(); i++, j++)
1310 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1311 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1312 Object * obj2 = (Object *)(*j);
1317 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1318 a negative start angle which makes it impossible to interact with.
1321 if (obj.type == OTArc)
1323 // This is 2*mirror angle - obj angle - obj span
1324 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1326 if (obj2->angle[0] > TAU)
1327 obj2->angle[0] -= TAU;
1335 if (Global::toolState == TSPoint1)
1337 Global::toolState = TSPoint2;
1338 // Prevent spurious line from drawing...
1339 toolPoint[1] = toolPoint[0];
1341 else if ((Global::toolState == TSPoint2) && shiftDown)
1343 // Key override is telling us to make a new line, not continue the
1345 toolPoint[0] = toolPoint[1];
1349 // Either we're finished with our rotate, or we're stamping a copy.
1352 // Stamp a copy of the selection at the current rotation & bail
1354 CopyObjects(select, temp);
1355 ClearSelected(temp);
1356 AddObjectsTo(document.objects, temp);
1357 RestorePointsTo(select, toolScratch);
1362 Global::toolState = TSPoint1;
1363 SavePointsFrom(select, toolScratch);
1369 // Reset the selection if shift held down...
1371 RestorePointsTo(select, toolScratch);
1376 // Reset selection when key is let up
1378 MirrorHandler(ToolMouseMove, toolPoint[1]);
1383 RestorePointsTo(select, toolScratch);
1388 void DrawingView::DimensionHandler(int mode, Point p)
1393 if (Global::toolState == TSNone)
1401 if (Global::toolState == TSNone)
1409 if (Global::toolState == TSNone)
1411 Global::toolState = TSPoint2;
1412 // Prevent spurious line from drawing...
1413 toolPoint[1] = toolPoint[0];
1415 else if ((Global::toolState == TSPoint2) && shiftDown)
1417 // Key override is telling us to make a new line, not continue the
1419 toolPoint[0] = toolPoint[1];
1423 Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear);
1424 d->layer = Global::activeLayer;
1425 document.objects.push_back(d);
1426 Global::toolState = TSNone;
1432 void DrawingView::TriangulateHandler(int mode, Point/*p*/)
1438 // Skip if nothing hovered...
1439 if (numHovered != 1)
1442 VPVector hover = GetHovered();
1443 Object * obj = (Object *)hover[0];
1445 // Skip if it's not a line...
1446 if (obj->type != OTLine)
1449 if (Global::toolState == TSNone)
1451 else if (Global::toolState == TSPoint2)
1460 if (Global::toolState == TSNone)
1462 else if (Global::toolState == TSPoint2)
1464 else if (Global::toolState == TSPoint3)
1478 if (Global::toolState == TSNone)
1480 Global::toolState = TSPoint2;
1482 else if (Global::toolState == TSPoint2)
1486 // Key override is telling us to start arc at new center, not
1487 // continue the current one.
1488 toolPoint[0] = toolPoint[1];
1492 Global::toolState = TSPoint3;
1496 double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1497 double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1499 Circle c1(toolObj[0]->p[0], len2);
1500 Circle c2(toolObj[0]->p[1], len3);
1502 Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1504 // Only move lines if the triangle formed by them is not degenerate
1505 if (Global::numIntersectPoints > 0)
1507 toolObj[1]->p[0] = toolObj[0]->p[0];
1508 toolObj[1]->p[1] = Global::intersectPoint[0];
1510 toolObj[2]->p[0] = Global::intersectPoint[0];
1511 toolObj[2]->p[1] = toolObj[0]->p[1];
1514 Global::toolState = TSNone;
1520 void DrawingView::TrimHandler(int mode, Point p)
1523 n.b.: this code is lifted straight out of the old oo code. needs to be updated.
1530 Object * toTrim = doc->lastObjectHovered;
1535 Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
1537 // Check to see which case we have...
1538 // We're trimming point #1...
1541 ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
1545 ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
1549 Point p1 = ((Line *)toTrim)->position + (v * t);
1550 Point p2 = ((Line *)toTrim)->position + (v * u);
1551 Point p3 = ((Line *)toTrim)->endpoint;
1552 ((Line *)toTrim)->endpoint = p1;
1553 Line * line = new Line(p2, p3);
1554 emit ObjectReady(line);
1557 doc->lastObjectHovered = NULL;
1565 Object * toTrim = doc->lastObjectHovered;
1571 if (toTrim->type != OTLine)
1574 double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
1576 std::vector<Object *>::iterator i;
1578 for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
1580 // Can't trim against yourself... :-P
1584 Object * trimAgainst = *i;
1587 if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
1590 int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
1594 // Now what? We don't know which side to trim!
1595 // ... now we do, we know which side of the Line we're on!
1596 if ((t1 > t) && (t1 < pointHoveredT))
1599 if ((t1 < u) && (t1 > pointHoveredT))
1604 // Bail out if nothing hovered...
1605 if (numHovered != 1)
1611 VPVector hover = GetHovered();
1612 Object * obj = (Object *)hover[0];
1614 // Skip if it's not a line...
1615 if (obj->type != OTLine)
1622 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1624 // Currently only deal with line against line trimming, can expand to
1625 // others as well (line/circle, circle/circle, line/arc, etc)
1627 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1629 obj = (Object *)(*i);
1631 if (obj == toolObj[0])
1633 else if (obj->type != OTLine)
1636 Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1638 if (Global::numIntersectParams > 0)
1640 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1661 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1686 void DrawingView::mousePressEvent(QMouseEvent * event)
1688 if (event->button() == Qt::LeftButton)
1690 //printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
1691 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1693 // Handle tool processing, if any
1696 if (hoveringIntersection)
1697 point = intersectionPoint;
1698 else if (hoverPointValid)
1700 else if (Global::snapToGrid)
1701 point = SnapPointToGrid(point);
1703 //Also, may want to figure out if hovering over a snap point on an
1704 //object, snap to grid if not.
1705 // Snap to object point if valid...
1706 // if (Global::snapPointIsValid)
1707 // point = Global::snapPoint;
1709 ToolHandler(ToolMouseDown, point);
1713 // Clear the selection only if CTRL isn't being held on click
1715 ClearSelected(document.objects);
1716 // ClearSelection();
1718 // If any objects are being hovered on click, add them to the selection
1722 AddHoveredToSelection();
1723 update(); // needed??
1724 // GetHovered(hover); // prolly needed
1725 VPVector hover2 = GetHovered();
1726 dragged = (Object *)hover2[0];
1727 draggingObject = true;
1728 //printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
1730 // Alert the pen widget
1731 emit ObjectSelected(dragged);
1733 // See if anything is using just a straight click on a handle
1734 if (HandleObjectClicked())
1736 draggingObject = false;
1741 // Needed for grab & moving objects
1742 // We do it *after*... why? (doesn't seem to confer any advantage...)
1743 if (hoveringIntersection)
1744 oldPoint = intersectionPoint;
1745 else if (hoverPointValid)
1746 oldPoint = hoverPoint;
1747 else if (Global::snapToGrid)
1748 oldPoint = SnapPointToGrid(point);
1750 // Needed for fixed length handling
1751 if (Global::fixedLength)
1753 if (dragged->type == OTLine)
1755 dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1759 if (dragged->type == OTCircle)
1761 // Save for informative text, uh, er, informing
1762 dragged->length = dragged->radius[0];
1768 // Didn't hit any object and not using a tool, so do a selection
1770 Global::selectionInProgress = true;
1771 Global::selection.setTopLeft(QPointF(point.x, point.y));
1772 Global::selection.setBottomRight(QPointF(point.x, point.y));
1773 select = GetSelection();
1775 else if (event->button() == Qt::MiddleButton)
1778 oldPoint = Vector(event->x(), event->y());
1779 // Should also change the mouse pointer as well...
1780 setCursor(Qt::SizeAllCursor);
1785 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1787 // It seems that wheelEvent() triggers this for some reason...
1788 if (scrollWheelSeen)
1790 scrollWheelSeen = false;
1794 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1795 Global::selection.setBottomRight(QPointF(point.x, point.y));
1796 // Only needs to be done here, as mouse down is always preceded by movement
1797 Global::snapPointIsValid = false;
1798 hoveringIntersection = false;
1799 oldScrollPoint = Vector(event->x(), event->y());
1802 if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1804 point = Vector(event->x(), event->y());
1805 // Since we're using Qt coords for scrolling, we have to adjust them
1806 // here to conform to Cartesian coords, since the origin is using
1808 Vector delta(oldPoint, point);
1809 delta /= Global::zoom;
1811 Global::origin -= delta;
1813 UpdateGridBackground();
1819 // If we're doing a selection rect, see if any objects are engulfed by it
1820 // (implies left mouse button held down)
1821 if (Global::selectionInProgress)
1823 CheckObjectBounds();
1825 // Make sure previously selected objects stay selected (CTRL held)
1826 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
1827 ((Object *)(*i))->selected = true;
1833 // Do object hit testing...
1834 bool needUpdate = HitTestObjects(point);
1835 VPVector hover2 = GetHovered();
1840 printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1842 if (hover2.size() > 0)
1843 printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1848 // Check for multi-hover...
1851 //need to check for case where hover is over 2 circles and a 3rd's center...
1852 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1854 Geometry::Intersects(obj1, obj2);
1855 int numIntersecting = Global::numIntersectParams;
1856 double t = Global::intersectParam[0];
1857 double u = Global::intersectParam[1];
1859 if (numIntersecting > 0)
1861 Vector v1 = Geometry::GetPointForParameter(obj1, t);
1862 Vector v2 = Geometry::GetPointForParameter(obj2, u);
1863 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1864 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1866 hoveringIntersection = true;
1867 intersectionPoint = v1;
1870 numIntersecting = Global::numIntersectPoints;
1872 if (numIntersecting > 0)
1874 Vector v1 = Global::intersectPoint[0];
1876 if (numIntersecting == 2)
1878 Vector v2 = Global::intersectPoint[1];
1880 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1884 QString text = tr("Intersection <%1, %2>");
1885 informativeText = text.arg(v1.x).arg(v1.y);
1886 hoveringIntersection = true;
1887 intersectionPoint = v1;
1890 else if (numHovered == 1)
1892 Object * obj = (Object *)hover2[0];
1894 if (obj->type == OTLine)
1897 Not sure that this is the best way to handle this, but it works(TM)...
1898 Except when lines are overlapping, then it doesn't work... !!! FIX !!!
1900 Point midpoint = Geometry::Midpoint((Line *)obj);
1901 Vector v1 = Vector::Magnitude(midpoint, point);
1903 if ((v1.Magnitude() * Global::zoom) < 8.0)
1905 QString text = tr("Midpoint <%1, %2>");
1906 informativeText = text.arg(midpoint.x).arg(midpoint.y);
1907 hoverPointValid = true;
1908 hoverPoint = midpoint;
1914 // Handle object movement (left button down & over an object)
1915 if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
1917 if (hoveringIntersection)
1918 point = intersectionPoint;
1919 else if (hoverPointValid)
1921 else if (Global::snapToGrid)
1922 point = SnapPointToGrid(point);
1924 HandleObjectMovement(point);
1930 // Do tool handling, if any are active...
1933 if (hoveringIntersection)
1934 point = intersectionPoint;
1935 else if (hoverPointValid)
1937 else if (Global::snapToGrid)
1940 point = SnapPointToAngle(point);
1942 point = SnapPointToGrid(point);
1945 ToolHandler(ToolMouseMove, point);
1948 // This is used to draw the tool crosshair...
1951 if (needUpdate || Global::tool)
1956 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1958 if (event->button() == Qt::LeftButton)
1960 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1961 //could set it up to use the document's update function (assumes that all object
1962 //updates are being reported correctly:
1963 // if (document.NeedsUpdate())
1964 // Do an update if collided with at least *one* object in the document
1970 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1971 ToolHandler(ToolMouseUp, point);
1975 // if (Global::selectionInProgress)
1976 Global::selectionInProgress = false;
1978 informativeText.clear();
1979 // Should we be doing this automagically? Hmm...
1980 // Clear our vectors
1984 // 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)
1985 select = GetSelection();
1987 draggingObject = false;
1989 else if (event->button() == Qt::MiddleButton)
1992 setCursor(Qt::ArrowCursor);
1997 void DrawingView::wheelEvent(QWheelEvent * event)
1999 double zoomFactor = 1.25;
2000 scrollWheelSeen = true;
2002 if (event->delta() < 0)
2004 if (Global::zoom > 20.0)
2007 Global::zoom *= zoomFactor;
2008 // Point np = Painter::QtToCartesianCoords(oldScrollPoint);
2009 // Global::origin += (oldPoint - np);
2013 if (Global::zoom < 0.25)
2016 Global::zoom /= zoomFactor;
2017 // Point np = Painter::QtToCartesianCoords(oldScrollPoint);
2018 // Global::origin += (oldPoint - np);
2021 Point np = Painter::QtToCartesianCoords(oldScrollPoint);
2022 Global::origin += (oldPoint - np);
2024 // Global::gridSpacing = gridPixels / Painter::zoom;
2025 SetGridSize(Global::gridSpacing * Global::zoom);
2027 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
2031 void DrawingView::keyPressEvent(QKeyEvent * event)
2033 bool oldShift = shiftDown;
2034 bool oldCtrl = ctrlDown;
2035 bool oldAlt = altDown;
2037 if (event->key() == Qt::Key_Shift)
2039 else if (event->key() == Qt::Key_Control)
2041 else if (event->key() == Qt::Key_Alt)
2044 // If there's a change in any of the modifier key states, pass it on to
2045 // the current tool's handler
2046 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2049 ToolHandler(ToolKeyDown, Point(0, 0));
2054 if (oldAlt != altDown)
2057 setCursor(Qt::SizeAllCursor);
2058 oldPoint = oldScrollPoint;
2061 if (select.size() > 0)
2063 if (event->key() == Qt::Key_Up)
2065 TranslateObjects(select, Point(0, +1.0));
2068 else if (event->key() == Qt::Key_Down)
2070 TranslateObjects(select, Point(0, -1.0));
2073 else if (event->key() == Qt::Key_Right)
2075 TranslateObjects(select, Point(+1.0, 0));
2078 else if (event->key() == Qt::Key_Left)
2080 TranslateObjects(select, Point(-1.0, 0));
2087 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2089 bool oldShift = shiftDown;
2090 bool oldCtrl = ctrlDown;
2091 bool oldAlt = altDown;
2093 if (event->key() == Qt::Key_Shift)
2095 else if (event->key() == Qt::Key_Control)
2097 else if (event->key() == Qt::Key_Alt)
2100 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2103 ToolHandler(ToolKeyUp, Point(0, 0));
2108 if (oldAlt != altDown)
2111 setCursor(Qt::ArrowCursor);
2117 // This looks strange, but it's really quite simple: We want a point that's
2118 // more than half-way to the next grid point to snap there while conversely we
2119 // want a point that's less than half-way to to the next grid point then snap
2120 // to the one before it. So we add half of the grid spacing to the point, then
2121 // divide by it so that we can remove the fractional part, then multiply it
2122 // back to get back to the correct answer.
2124 Point DrawingView::SnapPointToGrid(Point point)
2126 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
2127 point /= Global::gridSpacing;
2128 point.x = floor(point.x);//need to fix this for negative numbers...
2129 point.y = floor(point.y);
2130 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
2131 point *= Global::gridSpacing;
2136 Point DrawingView::SnapPointToAngle(Point point)
2138 // Snap to a single digit angle (using toolpoint #1 as the center)
2139 double angle = Vector::Angle(toolPoint[0], point);
2140 double length = Vector::Magnitude(toolPoint[0], point);
2142 // Convert from radians to degrees
2143 double degAngle = angle * RADIANS_TO_DEGREES;
2144 double snapAngle = (double)((int)(degAngle + 0.5));
2147 v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2148 point = toolPoint[0] + v;
2154 Rect DrawingView::GetObjectExtents(Object * obj)
2156 // Default to empty rect, if object checks below fail for some reason
2164 rect = Rect(obj->p[0], obj->p[1]);
2170 rect = Rect(obj->p[0], obj->p[0]);
2171 rect.Expand(obj->radius[0]);
2177 Arc * a = (Arc *)obj;
2179 double start = a->angle[0];
2180 double end = start + a->angle[1];
2181 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2183 // If the end of the arc is before the beginning, add 360 degrees to it
2187 // Adjust the bounds depending on which axes are crossed
2188 if ((start < QTR_TAU) && (end > QTR_TAU))
2191 if ((start < HALF_TAU) && (end > HALF_TAU))
2194 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2197 if ((start < TAU) && (end > TAU))
2200 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2203 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2206 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2209 rect *= a->radius[0];
2210 rect.Translate(a->p[0]);
2216 Text * t = (Text *)obj;
2217 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2223 Container * c = (Container *)obj;
2224 VPVectorIter i = c->objects.begin();
2225 rect = GetObjectExtents((Object *)*i);
2228 for(; i!=c->objects.end(); i++)
2229 rect |= GetObjectExtents((Object *)*i);
2240 void DrawingView::CheckObjectBounds(void)
2244 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2246 Object * obj = (Object *)(*i);
2247 obj->selected = false;
2254 Line * l = (Line *)obj;
2256 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2264 Circle * c = (Circle *)obj;
2266 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]))
2274 Arc * a = (Arc *)obj;
2276 double start = a->angle[0];
2277 double end = start + a->angle[1];
2278 QPointF p1(cos(start), sin(start));
2279 QPointF p2(cos(end), sin(end));
2280 QRectF bounds(p1, p2);
2283 // Swap X/Y coordinates if they're backwards...
2284 if (bounds.left() > bounds.right())
2286 double temp = bounds.left();
2287 bounds.setLeft(bounds.right());
2288 bounds.setRight(temp);
2291 if (bounds.bottom() > bounds.top())
2293 double temp = bounds.bottom();
2294 bounds.setBottom(bounds.top());
2295 bounds.setTop(temp);
2298 // Doesn't work as advertised! For shame!
2299 bounds = bounds.normalized();
2302 // If the end of the arc is before the beginning, add 360 degrees
2307 // Adjust the bounds depending on which axes are crossed
2308 if ((start < QTR_TAU) && (end > QTR_TAU))
2311 if ((start < HALF_TAU) && (end > HALF_TAU))
2312 bounds.setLeft(-1.0);
2314 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2315 bounds.setBottom(-1.0);
2317 if ((start < TAU) && (end > TAU))
2318 bounds.setRight(1.0);
2320 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2323 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2324 bounds.setLeft(-1.0);
2326 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2327 bounds.setBottom(-1.0);
2329 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2330 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2331 bounds.translate(a->p[0].x, a->p[0].y);
2333 if (Global::selection.contains(bounds))
2341 Text * t = (Text *)obj;
2342 Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2344 if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2352 Container * c = (Container *)obj;
2354 if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2367 bool DrawingView::HitTestObjects(Point point)
2371 bool needUpdate = false;
2372 hoverPointValid = false;
2374 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2376 Object * obj = (Object *)(*i);
2378 // If we're seeing the object we're dragging, skip it
2379 if (draggingObject && (obj == dragged))
2382 if (HitTest(obj, point) == true)
2388 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2389 emit ObjectHovered(obj);
2397 bool DrawingView::HitTest(Object * obj, Point point)
2399 bool needUpdate = false;
2405 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2406 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2407 Vector lineSegment = obj->p[1] - obj->p[0];
2408 Vector v1 = point - obj->p[0];
2409 Vector v2 = point - obj->p[1];
2410 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2414 distance = v1.Magnitude();
2416 distance = v2.Magnitude();
2418 // distance = ?Det?(ls, v1) / |ls|
2419 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2420 / lineSegment.Magnitude());
2422 if ((v1.Magnitude() * Global::zoom) < 8.0)
2424 obj->hitPoint[0] = true;
2425 hoverPoint = obj->p[0];
2426 hoverPointValid = true;
2428 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2430 obj->hitPoint[1] = true;
2431 hoverPoint = obj->p[1];
2432 hoverPointValid = true;
2434 else if ((distance * Global::zoom) < 5.0)
2435 obj->hitObject = true;
2437 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2439 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2447 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2448 obj->hitPoint[0] = obj->hitObject = false;
2449 double length = Vector::Magnitude(obj->p[0], point);
2451 if ((length * Global::zoom) < 8.0)
2453 obj->hitPoint[0] = true;
2454 hoverPoint = obj->p[0];
2455 hoverPointValid = true;
2457 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2458 obj->hitObject = true;
2460 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2462 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2470 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2471 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2472 double length = Vector::Magnitude(obj->p[0], point);
2473 double angle = Vector::Angle(obj->p[0], point);
2475 // Make sure we get the angle in the correct spot
2476 if (angle < obj->angle[0])
2479 // Get the span that we're pointing at...
2480 double span = angle - obj->angle[0];
2482 // N.B.: Still need to hit test the arc start & arc span handles...
2483 double spanAngle = obj->angle[0] + obj->angle[1];
2484 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2485 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2486 double length2 = Vector::Magnitude(point, handle1);
2487 double length3 = Vector::Magnitude(point, handle2);
2489 if ((length * Global::zoom) < 8.0)
2491 obj->hitPoint[0] = true;
2492 hoverPoint = obj->p[0];
2493 hoverPointValid = true;
2495 else if ((length2 * Global::zoom) < 8.0)
2497 obj->hitPoint[1] = true;
2498 hoverPoint = handle1;
2499 hoverPointValid = true;
2501 else if ((length3 * Global::zoom) < 8.0)
2503 obj->hitPoint[2] = true;
2504 hoverPoint = handle2;
2505 hoverPointValid = true;
2507 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2508 obj->hitObject = true;
2510 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2512 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2520 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2521 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2523 Dimension * d = (Dimension *)obj;
2525 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2526 // Get our line parallel to our points
2527 float scaledThickness = Global::scale * obj->thickness;
2529 Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2530 Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2532 Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2533 Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2535 Point p3(p1, point);
2537 Vector v1(d->p[0], point);
2538 Vector v2(d->p[1], point);
2539 Vector lineSegment(p1, p2);
2540 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2542 Point midpoint = (p1 + p2) / 2.0;
2543 Point hFSPoint = Point(midpoint, point);
2544 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2545 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2548 distance = v1.Magnitude();
2550 distance = v2.Magnitude();
2552 // distance = ?Det?(ls, v1) / |ls|
2553 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2554 / lineSegment.Magnitude());
2556 if ((v1.Magnitude() * Global::zoom) < 8.0)
2557 obj->hitPoint[0] = true;
2558 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2559 obj->hitPoint[1] = true;
2560 else if ((distance * Global::zoom) < 5.0)
2561 obj->hitObject = true;
2563 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2564 obj->hitPoint[2] = true;
2565 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2566 obj->hitPoint[3] = true;
2567 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2568 obj->hitPoint[4] = true;
2570 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2572 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2580 Text * t = (Text *)obj;
2581 bool oldHO = obj->hitObject;
2582 obj->hitObject = false;
2584 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2585 //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);
2587 if (r.Contains(point))
2588 obj->hitObject = true;
2590 obj->hovered = (obj->hitObject ? true : false);
2592 if (oldHO != obj->hitObject)
2600 // Containers must be recursively tested... Or do they???
2602 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.
2604 // bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2605 // Object * oldClicked = c->clicked;
2607 still need to compare old state to new state, and set things up based upon that...
2608 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);
2609 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.
2611 Container * c = (Container *)obj;
2612 c->hitObject = false;
2616 VPVector flat = Flatten(c);
2618 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2619 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
2621 Object * cObj = (Object *)(*i);
2623 // Skip the flattened containers (if any)...
2624 if (cObj->type == OTContainer)
2627 // We do it this way instead of needUpdate = HitTest() because we
2628 // are checking more than one object, and that way of doing will
2629 // not return consistent results.
2630 if (HitTest(cObj, point) == true)
2632 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2634 // c->hitObject = true;
2635 // c->clicked = cObj;
2636 // c->hovered = true;
2639 // Same reasons for doing it this way here apply.
2640 if (cObj->hitObject == true)
2642 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2643 c->hitObject = true;
2647 if (cObj->hitPoint[0] == true)
2649 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2650 c->hitPoint[0] = true;
2654 if (cObj->hitPoint[1] == true)
2656 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2657 c->hitPoint[1] = true;
2661 if (cObj->hitPoint[2] == true)
2663 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2664 c->hitPoint[2] = true;
2668 if (cObj->hovered == true)
2669 c->hovered = true;//*/
2683 bool DrawingView::HandleObjectClicked(void)
2685 if (dragged->type == OTDimension)
2687 Dimension * d = (Dimension *)dragged;
2691 // Hit the "flip sides" switch, so flip 'em
2692 Point temp = d->p[0];
2697 else if (d->hitPoint[3])
2699 // There are three cases here: aligned, horizontal, & vertical.
2700 // Aligned and horizontal do the same thing, vertical goes back to
2702 if (d->subtype == DTLinearVert)
2703 d->subtype = DTLinear;
2705 d->subtype = DTLinearVert;
2709 else if (d->hitPoint[4])
2711 // There are three cases here: aligned, horizontal, & vertical.
2712 // Aligned and vertical do the same thing, horizontal goes back to
2714 if (d->subtype == DTLinearHorz)
2715 d->subtype = DTLinear;
2717 d->subtype = DTLinearHorz;
2727 void DrawingView::HandleObjectMovement(Point point)
2729 Point delta = point - oldPoint;
2730 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2731 // Object * obj = (Object *)hover[0];
2732 Object * obj = dragged;
2733 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2734 //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"));
2739 if (obj->hitPoint[0])
2741 if (Global::fixedLength)
2743 Vector line = point - obj->p[1];
2744 Vector unit = line.Unit();
2745 point = obj->p[1] + (unit * obj->length);
2750 else if (obj->hitPoint[1])
2752 if (Global::fixedLength)
2754 Vector line = point - obj->p[0];
2755 Vector unit = line.Unit();
2756 point = obj->p[0] + (unit * obj->length);
2761 else if (obj->hitObject)
2770 if (obj->hitPoint[0])
2772 else if (obj->hitObject)
2774 double oldRadius = obj->length;
2775 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2777 QString text = QObject::tr("Radius: %1\nScale: %2%");
2778 informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2784 if (obj->hitPoint[0])
2786 else if (obj->hitPoint[1])
2788 // Change the Arc's span (handle #1)
2791 double angle = Vector::Angle(obj->p[0], point);
2792 double delta = angle - obj->angle[0];
2797 obj->angle[1] -= delta;
2798 obj->angle[0] = angle;
2800 if (obj->angle[1] < 0)
2801 obj->angle[1] += TAU;
2803 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2804 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);
2808 double angle = Vector::Angle(obj->p[0], point);
2809 obj->angle[0] = angle;
2810 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2811 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2813 else if (obj->hitPoint[2])
2815 // Change the Arc's span (handle #2)
2818 double angle = Vector::Angle(obj->p[0], point);
2819 obj->angle[1] = angle - obj->angle[0];
2821 if (obj->angle[1] < 0)
2822 obj->angle[1] += TAU;
2824 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2825 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);
2829 double angle = Vector::Angle(obj->p[0], point);
2830 obj->angle[0] = angle - obj->angle[1];
2832 if (obj->angle[0] < 0)
2833 obj->angle[0] += TAU;
2835 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2836 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2838 else if (obj->hitObject)
2845 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2846 QString text = QObject::tr("Radius: %1");
2847 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2853 if (obj->hitPoint[0])
2855 else if (obj->hitPoint[1])
2857 else if (obj->hitObject)
2859 // Move measurement lines in/out
2862 Dimension * d = (Dimension *)obj;
2863 double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
2864 float scaledThickness = Global::scale * obj->thickness;
2865 // Looks like offset is 0 to +MAX, but line is at 10.0. So
2866 // anything less than 10.0 should set the offset to 0.
2869 if (dist > (10.0 * scaledThickness))
2870 d->offset = dist - (10.0 * scaledThickness);
2888 // This is shitty, but works for now until I can code up something
2891 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.
2893 // TranslateObject(obj, delta);
2894 TranslateContainer((Container *)obj, point, delta);
2903 void DrawingView::AddDimensionTo(void * o)
2905 Object * obj = (Object *)o;
2910 document.Add(new Dimension(obj->p[0], obj->p[1]));