4 // Part of the Architektonas Project
5 // (C) 2011 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), /*numSelected(0),*/ 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 std::vector<void *>::iterator 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 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
281 std::vector<void *>::iterator i;
283 for(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 std::vector<void *>::iterator i = select.begin();
299 for(; i!=select.end(); i++)
301 Object * obj = (Object *)(*i);
302 obj->thickness = width;
307 void DrawingView::HandlePenStyle(int style)
309 std::vector<void *>::iterator i = select.begin();
311 for(; i!=select.end(); i++)
313 Object * obj = (Object *)(*i);
319 void DrawingView::HandlePenColor(uint32_t color)
321 std::vector<void *>::iterator i = select.begin();
323 for(; i!=select.end(); i++)
325 Object * obj = (Object *)(*i);
331 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
333 // This is undoing the transform, e.g. going from client coords to local
334 // coords. In essence, the height - y is height + (y * -1), the (y * -1)
335 // term doing the conversion of the y-axis from increasing bottom to top.
336 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
340 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
342 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
343 // No voodoo here, it's just grouped wrong to see it. It should be:
344 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
345 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
349 void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
351 // printf("DrawingView::focusOutEvent()...\n");
352 // Make sure all modkeys being held are marked as released when the app
353 // loses focus (N.B.: This only works because the app sets the focus policy
354 // of this object to something other than Qt::NoFocus)
355 shiftDown = ctrlDown = altDown = false;
357 setCursor(Qt::ArrowCursor);
361 void DrawingView::paintEvent(QPaintEvent * /*event*/)
363 QPainter qtPainter(this);
364 Painter painter(&qtPainter);
367 qtPainter.setRenderHint(QPainter::Antialiasing);
369 Global::viewportHeight = size().height();
371 // Draw coordinate axes
372 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
373 painter.DrawLine(0, -16384, 0, 16384);
374 painter.DrawLine(-16384, 0, 16384, 0);
376 // Do object rendering...
377 for(int i=0; i<Global::numLayers; i++)
379 if (Global::layerHidden[i] == false)
380 RenderObjects(&painter, document.objects, i);
383 // Do tool rendering, if any...
386 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
387 painter.DrawCrosshair(oldPoint);
391 // Do selection rectangle rendering, if any
392 if (Global::selectionInProgress)
394 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
395 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
396 painter.DrawRect(Global::selection);
399 if (hoveringIntersection)
400 painter.DrawHandle(intersectionPoint);
403 painter.DrawHandle(hoverPoint);
405 if (!informativeText.isEmpty())
406 painter.DrawInformativeText(informativeText);
411 // Renders objects in the passed in vector
414 N.B.: Since we have "hoverPointValid" drawing regular object handles above,
415 we can probably do away with a lot of them that are being done down below.
417 [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...]
419 void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer, bool ignoreLayer/*= false*/)
421 std::vector<void *>::iterator i;
423 for(i=v.begin(); i!=v.end(); i++)
425 Object * obj = (Object *)(*i);
426 float scaledThickness = Global::scale * obj->thickness;
428 // If the object isn't on the current layer being drawn, skip it
429 if (!ignoreLayer && (obj->layer != layer))
432 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
434 painter->SetPen(0x00FF00, 2.0, LSSolid);
438 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
439 painter->SetBrush(obj->color);
441 if (obj->selected || obj->hitObject)
442 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
448 painter->DrawLine(obj->p[0], obj->p[1]);
450 if (obj->hitPoint[0])
451 painter->DrawHandle(obj->p[0]);
453 if (obj->hitPoint[1])
454 painter->DrawHandle(obj->p[1]);
457 painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
462 painter->SetBrush(QBrush(Qt::NoBrush));
463 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
465 if (obj->hitPoint[0])
466 painter->DrawHandle(obj->p[0]);
471 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
473 if (obj->hitPoint[0])
474 painter->DrawHandle(obj->p[0]);
476 if (obj->hitPoint[1])
477 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
479 if (obj->hitPoint[2])
480 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
486 Dimension * d = (Dimension *)obj;
488 Vector v(d->p[0], d->p[1]);
489 double angle = v.Angle();
490 Vector unit = v.Unit();
491 d->lp[0] = d->p[0], d->lp[1] = d->p[1];
493 double x1, y1, length;
495 if (d->subtype == DTLinearVert)
497 if ((angle < 0) || (angle > HALF_TAU))
499 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
500 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
501 ortho = Vector(1.0, 0);
502 angle = THREE_QTR_TAU;
506 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
507 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
508 ortho = Vector(-1.0, 0);
512 d->lp[0].x = d->lp[1].x = x1;
513 length = fabs(d->p[0].y - d->p[1].y);
515 else if (d->subtype == DTLinearHorz)
517 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
519 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
520 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
521 ortho = Vector(0, 1.0);
526 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
527 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
528 ortho = Vector(0, -1.0);
532 d->lp[0].y = d->lp[1].y = y1;
533 length = fabs(d->p[0].x - d->p[1].x);
535 else if (d->subtype == DTLinear)
537 angle = Vector(d->lp[0], d->lp[1]).Angle();
538 ortho = Vector::Normal(d->lp[0], d->lp[1]);
539 length = v.Magnitude();
542 unit = Vector(d->lp[0], d->lp[1]).Unit();
544 Point p1 = d->lp[0] + (ortho * 10.0 * scaledThickness);
545 Point p2 = d->lp[1] + (ortho * 10.0 * scaledThickness);
546 Point p3 = d->lp[0] + (ortho * 16.0 * scaledThickness);
547 Point p4 = d->lp[1] + (ortho * 16.0 * scaledThickness);
548 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
549 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
552 The numbers hardcoded into here, what are they?
553 I believe they are pixels.
555 // Draw extension lines (if certain type)
556 painter->DrawLine(p3, p5);
557 painter->DrawLine(p4, p6);
559 // Calculate whether or not the arrowheads are too crowded to put
560 // inside the extension lines. 9.0 is the length of the arrowhead.
561 double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
563 // On the screen, it's acting like this is actually 58%...
564 // This is correct, we want it to happen at > 50%
567 // Draw main dimension line + arrowheads
568 painter->DrawLine(p1, p2);
569 painter->DrawArrowhead(p1, p2, scaledThickness);
570 painter->DrawArrowhead(p2, p1, scaledThickness);
574 // Draw outside arrowheads
575 Point p7 = p1 - (unit * 9.0 * scaledThickness);
576 Point p8 = p2 + (unit * 9.0 * scaledThickness);
577 painter->DrawArrowhead(p1, p7, scaledThickness);
578 painter->DrawArrowhead(p2, p8, scaledThickness);
579 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
580 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
583 // Draw length of dimension line...
584 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
585 Point ctr = p2 + (Vector(p2, p1) / 2.0);
590 dimText = QString("%1\"").arg(length);
593 double feet = (double)((int)length / 12);
594 double inches = length - (feet * 12.0);
597 dimText = QString("%1'").arg(feet);
599 dimText = QString("%1' %2\"").arg(feet).arg(inches);
602 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
606 Point hp1 = (p1 + p2) / 2.0;
607 Point hp2 = (p1 + hp1) / 2.0;
608 Point hp3 = (hp1 + p2) / 2.0;
612 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
613 painter->SetBrush(QBrush(QColor(Qt::magenta)));
614 painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
615 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
618 painter->DrawHandle(hp1);
619 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
623 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
624 painter->SetBrush(QBrush(QColor(Qt::magenta)));
625 painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
626 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
629 painter->DrawHandle(hp2);
630 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
634 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
635 painter->SetBrush(QBrush(QColor(Qt::magenta)));
636 painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
637 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
640 painter->DrawHandle(hp3);
643 if (obj->hitPoint[0])
644 painter->DrawHandle(obj->p[0]);
646 if (obj->hitPoint[1])
647 painter->DrawHandle(obj->p[1]);
654 Text * t = (Text *)obj;
656 if (t->measured == false)
658 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
662 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
678 // Containers require recursive rendering...
679 Container * c = (Container *)obj;
680 printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
681 RenderObjects(painter, (*c).objects, layer, ignoreLayer);
683 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
684 // Containers also have special indicators showing they are selected
685 if (c->selected || c->hitObject)
687 // Rect r = GetObjectExtents(obj);
688 // painter->DrawRectCorners(r);
689 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
702 void DrawingView::AddHoveredToSelection(void)
704 std::vector<void *>::iterator i;
706 for(i=document.objects.begin(); i!=document.objects.end(); i++)
708 if (((Object *)(*i))->hovered)
709 ((Object *)(*i))->selected = true;
714 void DrawingView::GetSelection(std::vector<void *> & v)
717 std::vector<void *>::iterator i;
719 for(i=document.objects.begin(); i!=document.objects.end(); i++)
721 if (((Object *)(*i))->selected)
728 void DrawingView::GetHovered(std::vector<void *> & v)
731 std::vector<void *>::iterator i;
733 for(i=document.objects.begin(); i!=document.objects.end(); i++)
735 if (((Object *)(*i))->hovered)
737 //printf("GetHovered: adding object (%X) to hover... hp1=%s, hp2=%s, hl=%s\n", (*i), (((Line *)(*i))->hitPoint[0] ? "true" : "false"), (((Line *)(*i))->hitPoint[1] ? "true" : "false"), (((Line *)(*i))->hitObject ? "true" : "false"));
745 std::vector<void *> DrawingView::GetHovered(void)
747 std::vector<void *> v;
748 std::vector<void *>::iterator i;
750 for(i=document.objects.begin(); i!=document.objects.end(); i++)
752 if (((Object *)(*i))->hovered)
754 //printf("GetHovered: adding object (%X) to hover... hp1=%s, hp2=%s, hl=%s\n", (*i), (((Line *)(*i))->hitPoint[0] ? "true" : "false"), (((Line *)(*i))->hitPoint[1] ? "true" : "false"), (((Line *)(*i))->hitObject ? "true" : "false"));
763 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
765 Global::screenSize = Vector(size().width(), size().height());
766 UpdateGridBackground();
770 void DrawingView::ToolHandler(int mode, Point p)
772 // Drop angle snap until it's needed
775 if (Global::tool == TTLine)
776 LineHandler(mode, p);
777 else if (Global::tool == TTCircle)
778 CircleHandler(mode, p);
779 else if (Global::tool == TTArc)
781 else if (Global::tool == TTRotate)
782 RotateHandler(mode, p);
783 else if (Global::tool == TTMirror)
784 MirrorHandler(mode, p);
785 else if (Global::tool == TTDimension)
786 DimensionHandler(mode, p);
787 else if (Global::tool == TTTriangulate)
788 TriangulateHandler(mode, p);
789 else if (Global::tool == TTTrim)
790 TrimHandler(mode, p);
791 else if (Global::tool == TTParallel)
792 ParallelHandler(mode, p);
796 void DrawingView::ToolDraw(Painter * painter)
798 if (Global::tool == TTLine)
800 if (Global::toolState == TSNone)
802 painter->DrawHandle(toolPoint[0]);
804 else if ((Global::toolState == TSPoint2) && shiftDown)
806 painter->DrawHandle(toolPoint[1]);
810 painter->DrawLine(toolPoint[0], toolPoint[1]);
811 painter->DrawHandle(toolPoint[1]);
813 Vector v(toolPoint[0], toolPoint[1]);
814 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
815 double absLength = v.Magnitude();
816 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
817 informativeText = text.arg(absLength).arg(absAngle);
820 else if (Global::tool == TTCircle)
822 if (Global::toolState == TSNone)
824 painter->DrawHandle(toolPoint[0]);
826 else if ((Global::toolState == TSPoint2) && shiftDown)
828 painter->DrawHandle(toolPoint[1]);
832 painter->DrawCross(toolPoint[0]);
833 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
834 painter->SetBrush(QBrush(Qt::NoBrush));
835 painter->DrawEllipse(toolPoint[0], length, length);
836 QString text = tr("Radius: %1 in.");
837 informativeText = text.arg(length);
840 else if (Global::tool == TTArc)
842 if (Global::toolState == TSNone)
844 painter->DrawHandle(toolPoint[0]);
846 else if (Global::toolState == TSPoint2)
848 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
849 painter->SetBrush(QBrush(Qt::NoBrush));
850 painter->DrawEllipse(toolPoint[0], length, length);
851 painter->DrawLine(toolPoint[0], toolPoint[1]);
852 painter->DrawHandle(toolPoint[1]);
853 QString text = tr("Radius: %1 in.");
854 informativeText = text.arg(length);
856 else if (Global::toolState == TSPoint3)
858 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
859 painter->DrawLine(toolPoint[0], toolPoint[2]);
860 painter->SetBrush(QBrush(Qt::NoBrush));
861 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
862 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
863 QString text = tr("Angle start: %1") + QChar(0x00B0);
864 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
868 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
869 double span = angle - toolPoint[2].x;
874 painter->DrawLine(toolPoint[0], toolPoint[3]);
875 painter->SetBrush(QBrush(Qt::NoBrush));
876 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
877 painter->SetPen(0xFF00FF, 2.0, LSSolid);
878 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
879 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
880 QString text = tr("Arc span: %1") + QChar(0x00B0);
881 informativeText = text.arg(RADIANS_TO_DEGREES * span);
884 else if (Global::tool == TTRotate)
886 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
887 painter->DrawHandle(toolPoint[0]);
888 else if ((Global::toolState == TSPoint2) && shiftDown)
889 painter->DrawHandle(toolPoint[1]);
892 if (toolPoint[0] == toolPoint[1])
895 painter->DrawLine(toolPoint[0], toolPoint[1]);
897 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
898 QString text = QChar(0x2221) + QObject::tr(": %1");
899 informativeText = text.arg(absAngle);
902 informativeText += " (Copy)";
905 else if (Global::tool == TTMirror)
907 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
908 painter->DrawHandle(toolPoint[0]);
909 else if ((Global::toolState == TSPoint2) && shiftDown)
910 painter->DrawHandle(toolPoint[1]);
913 if (toolPoint[0] == toolPoint[1])
916 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
917 painter->DrawLine(mirrorPoint, toolPoint[1]);
919 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
921 if (absAngle > 180.0)
924 QString text = QChar(0x2221) + QObject::tr(": %1");
925 informativeText = text.arg(absAngle);
928 informativeText += " (Copy)";
931 if (Global::tool == TTDimension)
933 if (Global::toolState == TSNone)
935 painter->DrawHandle(toolPoint[0]);
937 else if ((Global::toolState == TSPoint2) && shiftDown)
939 painter->DrawHandle(toolPoint[1]);
943 painter->DrawLine(toolPoint[0], toolPoint[1]);
944 painter->DrawHandle(toolPoint[1]);
946 Vector v(toolPoint[0], toolPoint[1]);
947 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
948 double absLength = v.Magnitude();
949 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
950 informativeText = text.arg(absLength).arg(absAngle);
956 void DrawingView::LineHandler(int mode, Point p)
961 if (Global::toolState == TSNone)
969 if (Global::toolState == TSNone)
977 if (Global::toolState == TSNone)
979 Global::toolState = TSPoint2;
980 // Prevent spurious line from drawing...
981 toolPoint[1] = toolPoint[0];
983 else if ((Global::toolState == TSPoint2) && shiftDown)
985 // Key override is telling us to make a new line, not continue the
987 toolPoint[0] = toolPoint[1];
991 Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
992 l->layer = Global::activeLayer;
993 document.objects.push_back(l);
994 toolPoint[0] = toolPoint[1];
1000 void DrawingView::CircleHandler(int mode, Point p)
1005 if (Global::toolState == TSNone)
1013 if (Global::toolState == TSNone)
1021 if (Global::toolState == TSNone)
1023 Global::toolState = TSPoint2;
1024 // Prevent spurious line from drawing...
1025 toolPoint[1] = toolPoint[0];
1027 else if ((Global::toolState == TSPoint2) && shiftDown)
1029 // Key override is telling us to make a new line, not continue the
1031 toolPoint[0] = toolPoint[1];
1035 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1036 Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1037 c->layer = Global::activeLayer;
1038 document.objects.push_back(c);
1039 toolPoint[0] = toolPoint[1];
1040 Global::toolState = TSNone;
1046 void DrawingView::ArcHandler(int mode, Point p)
1051 if (Global::toolState == TSNone)
1053 else if (Global::toolState == TSPoint2)
1055 else if (Global::toolState == TSPoint3)
1063 if (Global::toolState == TSNone)
1065 else if (Global::toolState == TSPoint2)
1067 else if (Global::toolState == TSPoint3)
1081 if (Global::toolState == TSNone)
1083 // Prevent spurious line from drawing...
1084 toolPoint[1] = toolPoint[0];
1085 Global::toolState = TSPoint2;
1087 else if (Global::toolState == TSPoint2)
1091 // Key override is telling us to start arc at new center, not
1092 // continue the current one.
1093 toolPoint[0] = toolPoint[1];
1097 // Set the radius in toolPoint[1].x
1098 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1099 Global::toolState = TSPoint3;
1101 else if (Global::toolState == TSPoint3)
1103 // Set the angle in toolPoint[2].x
1104 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1105 Global::toolState = TSPoint4;
1109 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1110 double span = endAngle - toolPoint[2].x;
1115 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1116 arc->layer = Global::activeLayer;
1117 document.objects.push_back(arc);
1118 Global::toolState = TSNone;
1124 void DrawingView::RotateHandler(int mode, Point p)
1129 if (Global::toolState == TSNone)
1132 // SavePointsFrom(select, toolScratch);
1133 CopyObjects(select, toolScratch2);
1134 Global::toolState = TSPoint1;
1136 else if (Global::toolState == TSPoint1)
1144 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1146 else if (Global::toolState == TSPoint2)
1154 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1155 std::vector<void *>::iterator j = select.begin();
1156 // std::vector<Object>::iterator i = toolScratch.begin();
1157 std::vector<void *>::iterator i = toolScratch2.begin();
1159 // for(; i!=toolScratch.end(); i++, j++)
1160 for(; i!=toolScratch2.end(); i++, j++)
1162 // Object objT = *i;
1163 // Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1164 // Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1165 Object * objT = (Object *)(*i);
1166 Object * objS = (Object *)(*j);
1168 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1169 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1174 // if (objT.type == OTArc || objT.type == OTText)
1175 if (objT->type == OTArc || objT->type == OTText)
1177 // objS->angle[0] = objT.angle[0] + angle;
1178 objS->angle[0] = objT->angle[0] + angle;
1180 if (objS->angle[0] > TAU)
1181 objS->angle[0] -= TAU;
1183 // else if (objT.type == OTContainer)
1184 else if (objT->type == OTContainer)
1186 // 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...]
1187 // Container * c = (Container *)&objT;
1188 Container * c = (Container *)objT;
1189 Container * c2 = (Container *)objS;
1190 std::vector<void *>::iterator l = c->objects.begin();
1191 // TODO: Rotate items in the container
1192 // TODO: Make this recursive
1193 for(std::vector<void *>::iterator k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1195 Object * obj3 = (Object *)(*k);
1196 Object * obj4 = (Object *)(*l);
1198 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1199 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1203 // obj3->angle[0] = objT.angle[0] + angle;
1204 obj3->angle[0] = obj4->angle[0] + angle;
1206 if (obj3->angle[0] > TAU)
1207 obj3->angle[0] -= TAU;
1210 Rect r = GetObjectExtents(objS);
1211 c2->p[0] = r.TopLeft();
1212 c2->p[1] = r.BottomRight();
1220 if (Global::toolState == TSPoint1)
1222 Global::toolState = TSPoint2;
1223 // Prevent spurious line from drawing...
1224 toolPoint[1] = toolPoint[0];
1226 else if ((Global::toolState == TSPoint2) && shiftDown)
1228 // Key override is telling us to make a new line, not continue the
1230 toolPoint[0] = toolPoint[1];
1234 // Either we're finished with our rotate, or we're stamping a copy.
1237 // Stamp a copy of the selection at the current rotation & bail
1238 std::vector<void *> temp;
1239 CopyObjects(select, temp);
1240 ClearSelected(temp);
1241 AddObjectsTo(document.objects, temp);
1242 // RestorePointsTo(select, toolScratch);
1243 RestorePointsTo(select, toolScratch2);
1248 Global::toolState = TSPoint1;
1249 // SavePointsFrom(select, toolScratch);
1250 DeleteContents(toolScratch2);
1251 CopyObjects(select, toolScratch2);
1257 // Reset the selection if shift held down...
1259 // RestorePointsTo(select, toolScratch);
1260 RestorePointsTo(select, toolScratch2);
1265 // Reset selection when key is let up
1267 RotateHandler(ToolMouseMove, toolPoint[1]);
1272 // RestorePointsTo(select, toolScratch);
1273 RestorePointsTo(select, toolScratch2);
1274 DeleteContents(toolScratch2);
1279 void DrawingView::MirrorHandler(int mode, Point p)
1284 if (Global::toolState == TSNone)
1287 SavePointsFrom(select, toolScratch);
1288 Global::toolState = TSPoint1;
1290 else if (Global::toolState == TSPoint1)
1298 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1300 else if (Global::toolState == TSPoint2)
1308 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1309 std::vector<void *>::iterator j = select.begin();
1310 std::vector<Object>::iterator i = toolScratch.begin();
1312 for(; i!=toolScratch.end(); i++, j++)
1315 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1316 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1317 Object * obj2 = (Object *)(*j);
1322 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1323 a negative start angle which makes it impossible to interact with.
1326 if (obj.type == OTArc)
1328 // This is 2*mirror angle - obj angle - obj span
1329 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1331 if (obj2->angle[0] > TAU)
1332 obj2->angle[0] -= TAU;
1340 if (Global::toolState == TSPoint1)
1342 Global::toolState = TSPoint2;
1343 // Prevent spurious line from drawing...
1344 toolPoint[1] = toolPoint[0];
1346 else if ((Global::toolState == TSPoint2) && shiftDown)
1348 // Key override is telling us to make a new line, not continue the
1350 toolPoint[0] = toolPoint[1];
1354 // Either we're finished with our rotate, or we're stamping a copy.
1357 // Stamp a copy of the selection at the current rotation & bail
1358 std::vector<void *> temp;
1359 CopyObjects(select, temp);
1360 ClearSelected(temp);
1361 AddObjectsTo(document.objects, temp);
1362 RestorePointsTo(select, toolScratch);
1367 Global::toolState = TSPoint1;
1368 SavePointsFrom(select, toolScratch);
1374 // Reset the selection if shift held down...
1376 RestorePointsTo(select, toolScratch);
1381 // Reset selection when key is let up
1383 MirrorHandler(ToolMouseMove, toolPoint[1]);
1388 RestorePointsTo(select, toolScratch);
1393 void DrawingView::DimensionHandler(int mode, Point p)
1398 if (Global::toolState == TSNone)
1406 if (Global::toolState == TSNone)
1414 if (Global::toolState == TSNone)
1416 Global::toolState = TSPoint2;
1417 // Prevent spurious line from drawing...
1418 toolPoint[1] = toolPoint[0];
1420 else if ((Global::toolState == TSPoint2) && shiftDown)
1422 // Key override is telling us to make a new line, not continue the
1424 toolPoint[0] = toolPoint[1];
1428 Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear);
1429 d->layer = Global::activeLayer;
1430 document.objects.push_back(d);
1431 Global::toolState = TSNone;
1437 void DrawingView::TriangulateHandler(int mode, Point/*p*/)
1443 // Skip if nothing hovered...
1444 if (numHovered != 1)
1447 std::vector<void *> hover = GetHovered();
1448 Object * obj = (Object *)hover[0];
1450 // Skip if it's not a line...
1451 if (obj->type != OTLine)
1454 if (Global::toolState == TSNone)
1456 else if (Global::toolState == TSPoint2)
1465 if (Global::toolState == TSNone)
1467 else if (Global::toolState == TSPoint2)
1469 else if (Global::toolState == TSPoint3)
1483 if (Global::toolState == TSNone)
1485 Global::toolState = TSPoint2;
1487 else if (Global::toolState == TSPoint2)
1491 // Key override is telling us to start arc at new center, not
1492 // continue the current one.
1493 toolPoint[0] = toolPoint[1];
1497 Global::toolState = TSPoint3;
1501 double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1502 double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1504 Circle c1(toolObj[0]->p[0], len2);
1505 Circle c2(toolObj[0]->p[1], len3);
1507 Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1509 // Only move lines if the triangle formed by them is not degenerate
1510 if (Global::numIntersectPoints > 0)
1512 toolObj[1]->p[0] = toolObj[0]->p[0];
1513 toolObj[1]->p[1] = Global::intersectPoint[0];
1515 toolObj[2]->p[0] = Global::intersectPoint[0];
1516 toolObj[2]->p[1] = toolObj[0]->p[1];
1519 Global::toolState = TSNone;
1525 void DrawingView::TrimHandler(int mode, Point p)
1528 n.b.: this code is lifted straight out of the old oo code. needs to be updated.
1535 Object * toTrim = doc->lastObjectHovered;
1540 Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
1542 // Check to see which case we have...
1543 // We're trimming point #1...
1546 ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
1550 ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
1554 Point p1 = ((Line *)toTrim)->position + (v * t);
1555 Point p2 = ((Line *)toTrim)->position + (v * u);
1556 Point p3 = ((Line *)toTrim)->endpoint;
1557 ((Line *)toTrim)->endpoint = p1;
1558 Line * line = new Line(p2, p3);
1559 emit ObjectReady(line);
1562 doc->lastObjectHovered = NULL;
1570 Object * toTrim = doc->lastObjectHovered;
1576 if (toTrim->type != OTLine)
1579 double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
1581 std::vector<Object *>::iterator i;
1583 for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
1585 // Can't trim against yourself... :-P
1589 Object * trimAgainst = *i;
1592 if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
1595 int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
1599 // Now what? We don't know which side to trim!
1600 // ... now we do, we know which side of the Line we're on!
1601 if ((t1 > t) && (t1 < pointHoveredT))
1604 if ((t1 < u) && (t1 > pointHoveredT))
1609 // Bail out if nothing hovered...
1610 if (numHovered != 1)
1616 std::vector<void *> hover = GetHovered();
1617 Object * obj = (Object *)hover[0];
1619 // Skip if it's not a line...
1620 if (obj->type != OTLine)
1627 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1629 // Currently only deal with line against line trimming, can expand to
1630 // others as well (line/circle, circle/circle, line/arc, etc)
1631 std::vector<void *>::iterator i;
1632 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1634 obj = (Object *)(*i);
1636 if (obj == toolObj[0])
1638 else if (obj->type != OTLine)
1641 Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1643 if (Global::numIntersectParams > 0)
1645 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1666 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1691 void DrawingView::mousePressEvent(QMouseEvent * event)
1693 if (event->button() == Qt::LeftButton)
1695 printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
1696 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1698 // Handle tool processing, if any
1701 if (hoveringIntersection)
1702 point = intersectionPoint;
1703 else if (hoverPointValid)
1705 else if (Global::snapToGrid)
1706 point = SnapPointToGrid(point);
1708 //Also, may want to figure out if hovering over a snap point on an
1709 //object, snap to grid if not.
1710 // Snap to object point if valid...
1711 // if (Global::snapPointIsValid)
1712 // point = Global::snapPoint;
1714 ToolHandler(ToolMouseDown, point);
1718 // Clear the selection only if CTRL isn't being held on click
1720 ClearSelected(document.objects);
1721 // ClearSelection();
1723 // If any objects are being hovered on click, add them to the selection
1727 AddHoveredToSelection();
1728 update(); // needed??
1729 // GetHovered(hover); // prolly needed
1730 std::vector<void *> hover2 = GetHovered();
1731 dragged = (Object *)hover2[0];
1732 draggingObject = true;
1733 printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
1735 // Alert the pen widget
1736 emit ObjectSelected(dragged);
1738 // See if anything is using just a straight click on a handle
1739 if (HandleObjectClicked())
1741 draggingObject = false;
1746 // Needed for grab & moving objects
1747 // We do it *after*... why? (doesn't seem to confer any advantage...)
1748 if (hoveringIntersection)
1749 oldPoint = intersectionPoint;
1750 else if (hoverPointValid)
1751 oldPoint = hoverPoint;
1752 else if (Global::snapToGrid)
1753 oldPoint = SnapPointToGrid(point);
1755 // Needed for fixed length handling
1756 if (Global::fixedLength)
1758 if (dragged->type == OTLine)
1760 dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1764 if (dragged->type == OTCircle)
1766 // Save for informative text, uh, er, informing
1767 dragged->length = dragged->radius[0];
1773 // Didn't hit any object and not using a tool, so do a selection rectangle
1774 Global::selectionInProgress = true;
1775 Global::selection.setTopLeft(QPointF(point.x, point.y));
1776 Global::selection.setBottomRight(QPointF(point.x, point.y));
1778 else if (event->button() == Qt::MiddleButton)
1781 oldPoint = Vector(event->x(), event->y());
1782 // Should also change the mouse pointer as well...
1783 setCursor(Qt::SizeAllCursor);
1788 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1790 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1791 Global::selection.setBottomRight(QPointF(point.x, point.y));
1792 // Only needs to be done here, as mouse down is always preceded by movement
1793 Global::snapPointIsValid = false;
1794 hoveringIntersection = false;
1795 oldScrollPoint = Vector(event->x(), event->y());
1798 if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1800 point = Vector(event->x(), event->y());
1801 // Since we're using Qt coords for scrolling, we have to adjust them
1802 // here to conform to Cartesian coords, since the origin is using
1804 Vector delta(oldPoint, point);
1805 delta /= Global::zoom;
1807 Global::origin -= delta;
1809 UpdateGridBackground();
1815 // If we're doing a selection rect, see if any objects are engulfed by it
1816 // (implies left mouse button held down)
1817 if (Global::selectionInProgress)
1819 CheckObjectBounds();
1824 // Do object hit testing...
1825 bool needUpdate = HitTestObjects(point);
1826 // GetHovered(hover);
1827 std::vector<void *> hover2 = GetHovered();
1831 printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1833 if (hover2.size() > 0)
1834 printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1838 // Check for multi-hover...
1841 //need to check for case where hover is over 2 circles and a 3rd's center...
1842 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1844 Geometry::Intersects(obj1, obj2);
1845 int numIntersecting = Global::numIntersectParams;
1846 double t = Global::intersectParam[0];
1847 double u = Global::intersectParam[1];
1849 if (numIntersecting > 0)
1851 Vector v1 = Geometry::GetPointForParameter(obj1, t);
1852 Vector v2 = Geometry::GetPointForParameter(obj2, u);
1853 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1854 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1856 hoveringIntersection = true;
1857 intersectionPoint = v1;
1860 numIntersecting = Global::numIntersectPoints;
1862 if (numIntersecting > 0)
1864 Vector v1 = Global::intersectPoint[0];
1866 if (numIntersecting == 2)
1868 Vector v2 = Global::intersectPoint[1];
1870 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1874 QString text = tr("Intersection <%1, %2>");
1875 informativeText = text.arg(v1.x).arg(v1.y);
1876 hoveringIntersection = true;
1877 intersectionPoint = v1;
1880 else if (numHovered == 1)
1882 Object * obj = (Object *)hover2[0];
1884 if (obj->type == OTLine)
1887 Not sure that this is the best way to handle this, but it works(TM)...
1888 Except when lines are overlapping, then it doesn't work... !!! FIX !!!
1890 Point midpoint = Geometry::Midpoint((Line *)obj);
1891 Vector v1 = Vector::Magnitude(midpoint, point);
1893 if ((v1.Magnitude() * Global::zoom) < 8.0)
1895 QString text = tr("Midpoint <%1, %2>");
1896 informativeText = text.arg(midpoint.x).arg(midpoint.y);
1897 hoverPointValid = true;
1898 hoverPoint = midpoint;
1904 // Handle object movement (left button down & over an object)
1905 if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
1907 if (hoveringIntersection)
1908 point = intersectionPoint;
1909 else if (hoverPointValid)
1911 else if (Global::snapToGrid)
1912 point = SnapPointToGrid(point);
1914 HandleObjectMovement(point);
1920 // Do tool handling, if any are active...
1923 if (hoveringIntersection)
1924 point = intersectionPoint;
1925 else if (hoverPointValid)
1927 else if (Global::snapToGrid)
1930 point = SnapPointToAngle(point);
1932 point = SnapPointToGrid(point);
1935 ToolHandler(ToolMouseMove, point);
1938 // This is used to draw the tool crosshair...
1941 if (needUpdate || Global::tool)
1946 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1948 if (event->button() == Qt::LeftButton)
1950 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1951 //could set it up to use the document's update function (assumes that all object
1952 //updates are being reported correctly:
1953 // if (document.NeedsUpdate())
1954 // Do an update if collided with at least *one* object in the document
1960 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1961 ToolHandler(ToolMouseUp, point);
1965 if (Global::selectionInProgress)
1966 Global::selectionInProgress = false;
1968 informativeText.clear();
1969 // Should we be doing this automagically? Hmm...
1970 // Clear our vectors
1975 std::vector<void *>::iterator i;
1977 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1979 if (((Object *)(*i))->selected)
1980 select.push_back(*i);
1983 draggingObject = false;
1985 else if (event->button() == Qt::MiddleButton)
1988 setCursor(Qt::ArrowCursor);
1993 void DrawingView::wheelEvent(QWheelEvent * event)
1995 double zoomFactor = 1.25;
1996 QSize sizeWin = size();
1997 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1998 center = Painter::QtToCartesianCoords(center);
2000 // This is not centering for some reason. Need to figure out why. :-/
2001 if (event->delta() > 0)
2003 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
2004 Global::origin = newOrigin;
2005 Global::zoom *= zoomFactor;
2009 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
2010 Global::origin = newOrigin;
2011 Global::zoom /= zoomFactor;
2014 // Global::gridSpacing = gridPixels / Painter::zoom;
2015 // UpdateGridBackground();
2016 SetGridSize(Global::gridSpacing * Global::zoom);
2018 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
2022 void DrawingView::keyPressEvent(QKeyEvent * event)
2024 bool oldShift = shiftDown;
2025 bool oldCtrl = ctrlDown;
2026 bool oldAlt = altDown;
2028 if (event->key() == Qt::Key_Shift)
2030 else if (event->key() == Qt::Key_Control)
2032 else if (event->key() == Qt::Key_Alt)
2035 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2038 ToolHandler(ToolKeyDown, Point(0, 0));
2043 if (oldAlt != altDown)
2046 setCursor(Qt::SizeAllCursor);
2047 oldPoint = oldScrollPoint;
2050 if (select.size() > 0)
2052 if (event->key() == Qt::Key_Up)
2054 TranslateObjects(select, Point(0, +1.0));
2057 else if (event->key() == Qt::Key_Down)
2059 TranslateObjects(select, Point(0, -1.0));
2062 else if (event->key() == Qt::Key_Right)
2064 TranslateObjects(select, Point(+1.0, 0));
2067 else if (event->key() == Qt::Key_Left)
2069 TranslateObjects(select, Point(-1.0, 0));
2076 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2078 bool oldShift = shiftDown;
2079 bool oldCtrl = ctrlDown;
2080 bool oldAlt = altDown;
2082 if (event->key() == Qt::Key_Shift)
2084 else if (event->key() == Qt::Key_Control)
2086 else if (event->key() == Qt::Key_Alt)
2089 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2092 ToolHandler(ToolKeyUp, Point(0, 0));
2097 if (oldAlt != altDown)
2100 setCursor(Qt::ArrowCursor);
2106 // This looks strange, but it's really quite simple: We want a point that's
2107 // more than half-way to the next grid point to snap there while conversely we
2108 // want a point that's less than half-way to to the next grid point then snap
2109 // to the one before it. So we add half of the grid spacing to the point, then
2110 // divide by it so that we can remove the fractional part, then multiply it
2111 // back to get back to the correct answer.
2113 Point DrawingView::SnapPointToGrid(Point point)
2115 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
2116 point /= Global::gridSpacing;
2117 point.x = floor(point.x);//need to fix this for negative numbers...
2118 point.y = floor(point.y);
2119 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
2120 point *= Global::gridSpacing;
2125 Point DrawingView::SnapPointToAngle(Point point)
2127 // Snap to a single digit angle (using toolpoint #1 as the center)
2128 double angle = Vector::Angle(toolPoint[0], point);
2129 double length = Vector::Magnitude(toolPoint[0], point);
2131 // Convert from radians to degrees
2132 double degAngle = angle * RADIANS_TO_DEGREES;
2133 double snapAngle = (double)((int)(degAngle + 0.5));
2136 v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2137 point = toolPoint[0] + v;
2143 Rect DrawingView::GetObjectExtents(Object * obj)
2145 // Default to empty rect, if object checks below fail for some reason
2153 rect = Rect(obj->p[0], obj->p[1]);
2159 rect = Rect(obj->p[0], obj->p[0]);
2160 rect.Expand(obj->radius[0]);
2166 Arc * a = (Arc *)obj;
2168 double start = a->angle[0];
2169 double end = start + a->angle[1];
2170 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2172 // If the end of the arc is before the beginning, add 360 degrees to it
2176 // Adjust the bounds depending on which axes are crossed
2177 if ((start < QTR_TAU) && (end > QTR_TAU))
2180 if ((start < HALF_TAU) && (end > HALF_TAU))
2183 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2186 if ((start < TAU) && (end > TAU))
2189 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2192 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2195 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2198 rect *= a->radius[0];
2199 rect.Translate(a->p[0]);
2205 Text * t = (Text *)obj;
2206 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2212 Container * c = (Container *)obj;
2213 std::vector<void *>::iterator i = c->objects.begin();
2214 rect = GetObjectExtents((Object *)*i);
2217 for(; i!=c->objects.end(); i++)
2218 rect |= GetObjectExtents((Object *)*i);
2229 void DrawingView::CheckObjectBounds(void)
2231 std::vector<void *>::iterator i;
2233 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2235 Object * obj = (Object *)(*i);
2236 obj->selected = false;
2243 Line * l = (Line *)obj;
2245 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2253 Circle * c = (Circle *)obj;
2255 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]))
2263 Arc * a = (Arc *)obj;
2265 double start = a->angle[0];
2266 double end = start + a->angle[1];
2267 QPointF p1(cos(start), sin(start));
2268 QPointF p2(cos(end), sin(end));
2269 QRectF bounds(p1, p2);
2272 // Swap X/Y coordinates if they're backwards...
2273 if (bounds.left() > bounds.right())
2275 double temp = bounds.left();
2276 bounds.setLeft(bounds.right());
2277 bounds.setRight(temp);
2280 if (bounds.bottom() > bounds.top())
2282 double temp = bounds.bottom();
2283 bounds.setBottom(bounds.top());
2284 bounds.setTop(temp);
2287 // Doesn't work as advertised! For shame!
2288 bounds = bounds.normalized();
2291 // If the end of the arc is before the beginning, add 360 degrees
2296 // Adjust the bounds depending on which axes are crossed
2297 if ((start < QTR_TAU) && (end > QTR_TAU))
2300 if ((start < HALF_TAU) && (end > HALF_TAU))
2301 bounds.setLeft(-1.0);
2303 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2304 bounds.setBottom(-1.0);
2306 if ((start < TAU) && (end > TAU))
2307 bounds.setRight(1.0);
2309 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2312 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2313 bounds.setLeft(-1.0);
2315 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2316 bounds.setBottom(-1.0);
2318 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2319 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2320 bounds.translate(a->p[0].x, a->p[0].y);
2322 if (Global::selection.contains(bounds))
2330 Text * t = (Text *)obj;
2331 Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2333 if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2341 Container * c = (Container *)obj;
2343 if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2356 bool DrawingView::HitTestObjects(Point point)
2358 std::vector<void *>::iterator i;
2360 bool needUpdate = false;
2361 hoverPointValid = false;
2363 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2365 Object * obj = (Object *)(*i);
2367 // If we're seeing the object we're dragging, skip it
2368 if (draggingObject && (obj == dragged))
2371 if (HitTest(obj, point) == true)
2377 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2378 emit ObjectHovered(obj);
2386 bool DrawingView::HitTest(Object * obj, Point point)
2388 bool needUpdate = false;
2394 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2395 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2396 Vector lineSegment = obj->p[1] - obj->p[0];
2397 Vector v1 = point - obj->p[0];
2398 Vector v2 = point - obj->p[1];
2399 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2403 distance = v1.Magnitude();
2405 distance = v2.Magnitude();
2407 // distance = ?Det?(ls, v1) / |ls|
2408 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2409 / lineSegment.Magnitude());
2411 if ((v1.Magnitude() * Global::zoom) < 8.0)
2413 obj->hitPoint[0] = true;
2414 hoverPoint = obj->p[0];
2415 hoverPointValid = true;
2417 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2419 obj->hitPoint[1] = true;
2420 hoverPoint = obj->p[1];
2421 hoverPointValid = true;
2423 else if ((distance * Global::zoom) < 5.0)
2424 obj->hitObject = true;
2426 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2428 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2436 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2437 obj->hitPoint[0] = obj->hitObject = false;
2438 double length = Vector::Magnitude(obj->p[0], point);
2440 if ((length * Global::zoom) < 8.0)
2442 obj->hitPoint[0] = true;
2443 hoverPoint = obj->p[0];
2444 hoverPointValid = true;
2446 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2447 obj->hitObject = true;
2449 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2451 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2459 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2460 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2461 double length = Vector::Magnitude(obj->p[0], point);
2462 double angle = Vector::Angle(obj->p[0], point);
2464 // Make sure we get the angle in the correct spot
2465 if (angle < obj->angle[0])
2468 // Get the span that we're pointing at...
2469 double span = angle - obj->angle[0];
2471 // N.B.: Still need to hit test the arc start & arc span handles...
2472 double spanAngle = obj->angle[0] + obj->angle[1];
2473 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2474 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2475 double length2 = Vector::Magnitude(point, handle1);
2476 double length3 = Vector::Magnitude(point, handle2);
2478 if ((length * Global::zoom) < 8.0)
2480 obj->hitPoint[0] = true;
2481 hoverPoint = obj->p[0];
2482 hoverPointValid = true;
2484 else if ((length2 * Global::zoom) < 8.0)
2486 obj->hitPoint[1] = true;
2487 hoverPoint = handle1;
2488 hoverPointValid = true;
2490 else if ((length3 * Global::zoom) < 8.0)
2492 obj->hitPoint[2] = true;
2493 hoverPoint = handle2;
2494 hoverPointValid = true;
2496 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2497 obj->hitObject = true;
2499 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2501 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2509 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2510 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2512 Dimension * d = (Dimension *)obj;
2514 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2515 // Get our line parallel to our points
2516 float scaledThickness = Global::scale * obj->thickness;
2517 Point p1 = d->lp[0] + (orthogonal * 10.0 * scaledThickness);
2518 Point p2 = d->lp[1] + (orthogonal * 10.0 * scaledThickness);
2519 Point p3(p1, point);
2521 Vector v1(d->p[0], point);
2522 Vector v2(d->p[1], point);
2523 Vector lineSegment(p1, p2);
2524 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2526 Point midpoint = (p1 + p2) / 2.0;
2527 Point hFSPoint = Point(midpoint, point);
2528 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2529 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2532 distance = v1.Magnitude();
2534 distance = v2.Magnitude();
2536 // distance = ?Det?(ls, v1) / |ls|
2537 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2538 / lineSegment.Magnitude());
2540 if ((v1.Magnitude() * Global::zoom) < 8.0)
2541 obj->hitPoint[0] = true;
2542 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2543 obj->hitPoint[1] = true;
2544 else if ((distance * Global::zoom) < 5.0)
2545 obj->hitObject = true;
2547 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2548 obj->hitPoint[2] = true;
2549 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2550 obj->hitPoint[3] = true;
2551 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2552 obj->hitPoint[4] = true;
2554 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2556 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2564 Text * t = (Text *)obj;
2565 bool oldHO = obj->hitObject;
2566 obj->hitObject = false;
2568 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2569 //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);
2571 if (r.Contains(point))
2572 obj->hitObject = true;
2574 obj->hovered = (obj->hitObject ? true : false);
2576 if (oldHO != obj->hitObject)
2584 // Containers must be recursively tested... Or do they???
2586 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.
2588 // bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2589 // Object * oldClicked = c->clicked;
2591 still need to compare old state to new state, and set things up based upon that...
2592 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);
2593 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.
2596 Container * c = (Container *)obj;
2597 c->hitObject = false;
2601 std::vector<void *> flat = Flatten(c);
2603 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2604 for(std::vector<void *>::iterator i=flat.begin(); i!=flat.end(); i++)
2606 Object * cObj = (Object *)(*i);
2608 // Skip the flattened containers (if any)...
2609 if (cObj->type == OTContainer)
2612 // We do it this way instead of needUpdate = HitTest() because we
2613 // are checking more than one object, and that way of doing will
2614 // not return consistent results.
2615 if (HitTest(cObj, point) == true)
2617 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2619 // c->hitObject = true;
2620 // c->clicked = cObj;
2621 // c->hovered = true;
2624 // Same reasons for doing it this way here apply.
2625 if (cObj->hitObject == true)
2627 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2628 c->hitObject = true;
2632 if (cObj->hitPoint[0] == true)
2634 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2635 c->hitPoint[0] = true;
2639 if (cObj->hitPoint[1] == true)
2641 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2642 c->hitPoint[1] = true;
2646 if (cObj->hovered == true)
2647 c->hovered = true;//*/
2661 bool DrawingView::HandleObjectClicked(void)
2663 if (dragged->type == OTDimension)
2665 Dimension * d = (Dimension *)dragged;
2669 // Hit the "flip sides" switch, so flip 'em
2670 Point temp = d->p[0];
2675 else if (d->hitPoint[3])
2677 // There are three cases here: aligned, horizontal, & vertical.
2678 // Aligned and horizontal do the same thing, vertical goes back to
2680 if (d->subtype == DTLinearVert)
2681 d->subtype = DTLinear;
2683 d->subtype = DTLinearVert;
2687 else if (d->hitPoint[4])
2689 // There are three cases here: aligned, horizontal, & vertical.
2690 // Aligned and vertical do the same thing, horizontal goes back to
2692 if (d->subtype == DTLinearHorz)
2693 d->subtype = DTLinear;
2695 d->subtype = DTLinearHorz;
2705 void DrawingView::HandleObjectMovement(Point point)
2707 Point delta = point - oldPoint;
2708 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2709 // Object * obj = (Object *)hover[0];
2710 Object * obj = dragged;
2711 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2712 //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"));
2717 if (obj->hitPoint[0])
2719 if (Global::fixedLength)
2721 Vector line = point - obj->p[1];
2722 Vector unit = line.Unit();
2723 point = obj->p[1] + (unit * obj->length);
2728 else if (obj->hitPoint[1])
2730 if (Global::fixedLength)
2732 Vector line = point - obj->p[0];
2733 Vector unit = line.Unit();
2734 point = obj->p[0] + (unit * obj->length);
2739 else if (obj->hitObject)
2748 if (obj->hitPoint[0])
2750 else if (obj->hitObject)
2752 double oldRadius = obj->length;
2753 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2755 QString text = QObject::tr("Radius: %1\nScale: %2%");
2756 informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2762 if (obj->hitPoint[0])
2764 else if (obj->hitPoint[1])
2766 // Change the Arc's span (handle #1)
2769 double angle = Vector::Angle(obj->p[0], point);
2770 double delta = angle - obj->angle[0];
2775 obj->angle[1] -= delta;
2776 obj->angle[0] = angle;
2778 if (obj->angle[1] < 0)
2779 obj->angle[1] += TAU;
2781 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2782 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);
2786 double angle = Vector::Angle(obj->p[0], point);
2787 obj->angle[0] = angle;
2788 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2789 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2791 else if (obj->hitPoint[2])
2793 // Change the Arc's span (handle #2)
2796 double angle = Vector::Angle(obj->p[0], point);
2797 obj->angle[1] = angle - obj->angle[0];
2799 if (obj->angle[1] < 0)
2800 obj->angle[1] += TAU;
2802 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2803 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);
2807 double angle = Vector::Angle(obj->p[0], point);
2808 obj->angle[0] = angle - obj->angle[1];
2810 if (obj->angle[0] < 0)
2811 obj->angle[0] += TAU;
2813 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2814 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2816 else if (obj->hitObject)
2823 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2824 QString text = QObject::tr("Radius: %1");
2825 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2831 if (obj->hitPoint[0])
2833 else if (obj->hitPoint[1])
2835 else if (obj->hitObject)
2850 // This is shitty, but works for now until I can code up something
2853 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.
2855 // TranslateObject(obj, delta);
2856 TranslateContainer((Container *)obj, point, delta);
2865 void DrawingView::AddDimensionTo(void * o)
2867 Object * obj = (Object *)o;
2872 document.Add(new Dimension(obj->p[0], obj->p[1]));