4 // Part of the Architektonas Project
5 // (C) 2011-2020 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
8 // JLH = James Hammons <jlhamm@acm.org>
11 // --- ---------- ------------------------------------------------------------
12 // JLH 03/22/2011 Created this file
13 // JLH 09/29/2011 Added middle mouse button panning
18 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
19 // to a left-handed system and we need a right-handed one. [DONE]
20 // - Fixed length tool doesn't work on lines [DONE]
25 // - Layer locking (hiding works)
26 // - Fixed angle tool doesn't work on lines
27 // - Make it so "dirty" flag reflects drawing state
30 // Uncomment this for debugging...
32 //#define DEBUGFOO // Various tool debugging...
33 //#define DEBUGTP // Toolpalette debugging...
35 #include "drawingview.h"
40 #include "mathconstants.h"
46 #define BACKGROUND_MAX_SIZE 512
49 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
50 // The value in the settings file will override this.
51 useAntialiasing(true), numHovered(0), shiftDown(false),
52 ctrlDown(false), altDown(false),
53 gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
54 scale(1.0), offsetX(-10), offsetY(-10), document(true),
55 gridPixels(0), collided(false), scrollDrag(false), hoverPointValid(false),
56 hoveringIntersection(false), dragged(NULL), draggingObject(false),
57 angleSnap(false), dirty(false)
59 //wtf? doesn't work except in c++11??? document = { 0 };
60 setBackgroundRole(QPalette::Base);
61 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
63 Global::gridSpacing = 12.0; // In base units (inch is default)
65 Line * line = new Line(Vector(5, 5), Vector(50, 40), 2.0, 0xFF7F00, LSDash);
67 document.Add(new Line(Vector(50, 40), Vector(10, 83)));
68 document.Add(new Line(Vector(10, 83), Vector(17, 2)));
69 document.Add(new Circle(Vector(100, 100), 36));
70 document.Add(new Circle(Vector(50, 150), 49));
71 document.Add(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
72 document.Add(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
73 document.Add(new Text(Vector(10, 83), "Here is some awesome text!"));
78 Here we set the grid size in pixels--12 in this case. Initially, we have our
79 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
80 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
81 to be able to set the size of the background grid (which we do here at an
82 arbitrary 12 pixels) to anything we want (within reason, of course :-).
84 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
86 drawing->gridSpacing = 12.0 / Global::zoom;
88 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
89 translated to Cartesian coordinates through this. (Initially, Global::zoom is
90 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
92 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
93 convenience function than any measure of absolutes. Doing things that way we
94 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
95 shittiness that comes with it.
97 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
98 a certain way, which means we should probably create something else in those
99 objects to take its place--like some kind of scale factor. This would seem to
100 imply that certain point sizes actually *do* tie things like fonts to absolute
101 sizes on the screen, but not necessarily because you could have an inch scale
102 with text that is quite small relative to other objects on the screen, which
103 currently you have to zoom in to see (and which blows up the text). Point sizes
104 in an application like this are a bit meaningless; even though an inch is an
105 inch regardless of the zoom level a piece of text can be larger or smaller than
106 this. Maybe this is the case for having a base unit and basing point sizes off
109 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
110 base units. What that means is that if you have a 12px grid with a 6" grid size
111 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
113 Dimensions now have a "size" parameter to set their absolute size in relation
114 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
115 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
116 scaled the same way as the arrowheads.
118 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
119 need a thickness parameter similar to the "size" param for dimensions. (And now
123 SetGridSize(12); // This is in pixels
127 void DrawingView::SetGridSize(uint32_t size)
130 if (size == gridPixels)
133 // Recreate the background bitmap
135 QPainter pmp(&gridBackground);
136 pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
137 pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
139 for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
141 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
142 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
147 // Set up new BG brush & zoom level (pixels per base unit)
148 Global::zoom = gridPixels / Global::gridSpacing;
149 UpdateGridBackground();
153 void DrawingView::UpdateGridBackground(void)
155 // Transform the origin to Qt coordinates
156 Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
157 int x = (int)pixmapOrigin.x;
158 int y = (int)pixmapOrigin.y;
159 // Use mod arithmetic to grab the correct swatch of background
161 Negative numbers still screw it up... Need to think about what we're
162 trying to do here. The fact that it worked with 72 seems to have been pure luck.
163 It seems the problem is negative numbers: We can't let that happen.
164 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
167 The bitmap looks like this:
177 @ x = 1, we want it to look like:
179 -+---+---+---+---+---
182 -+---+---+---+---+---
187 Which means we need to grab the sample from x = 3. @ x = -1:
197 Which means we need to grab the sample from x = 1. Which means we have to take
198 the mirror of the modulus of gridPixels.
200 Doing a mod of a negative number is problematic: 1st, the compiler converts the
201 negative number to an unsigned int, then it does the mod. Gets you wrong answers
202 most of the time, unless you use a power of 2. :-P So what we do here is just
203 take the modulus of the negation, which means we don't have to worry about
206 The positive case looks gruesome (and it is) but it boils down to this: We take
207 the modulus of the X coordinate, then mirror it by subtraction from the
208 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
209 gridPixels. But we need the case where the result equalling gridPixels to be
210 zero; so we do another modulus operation on the result to achieve this.
215 x = (gridPixels - (x % gridPixels)) % gridPixels;
220 y = (gridPixels - (y % gridPixels)) % gridPixels;
222 // Here we grab a section of the bigger pixmap, so that the background
223 // *looks* like it's scrolling...
224 QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
225 QPalette pal = palette();
226 pal.setBrush(backgroundRole(), QBrush(pm));
227 setAutoFillBackground(true);
233 // Basically, we just make a single pass through the Container. If the layer #
234 // is less than the layer # being deleted, then do nothing. If the layer # is
235 // equal to the layer # being deleted, then delete the object. If the layer #
236 // is greater than the layer # being deleted, then set the layer # to its layer
239 void DrawingView::DeleteCurrentLayer(int layer)
241 //printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
242 VPVectorIter i = document.objects.begin();
244 while (i != document.objects.end())
246 Object * obj = (Object *)(*i);
248 if (obj->layer < layer)
250 else if (obj->layer == layer)
252 document.objects.erase(i);
262 // We've just done a destructive action, so update the screen!
267 void DrawingView::HandleLayerToggle(void)
269 // A layer's visibility was toggled, so update the screen...
275 // A layer was moved up or down in the layer list, so we have to swap the
276 // document's object's layer numbers in the layers that were swapped.
278 void DrawingView::HandleLayerSwap(int layer1, int layer2)
280 // !!! FIX !!! This doesn't properly handle container contents...
281 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
282 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
284 Object * obj = (Object *)(*i);
286 if (obj->layer == layer1)
288 else if (obj->layer == layer2)
294 void DrawingView::HandlePenWidth(float width)
296 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
298 Object * obj = (Object *)(*i);
299 obj->thickness = width;
304 void DrawingView::HandlePenStyle(int style)
306 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
308 Object * obj = (Object *)(*i);
314 void DrawingView::HandlePenColor(uint32_t color)
316 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
318 Object * obj = (Object *)(*i);
324 void DrawingView::HandlePenStamp(void)
326 VPVector flat = Flatten(select);
328 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
330 Object * obj = (Object *)(*i);
332 if (obj->type != OTText)
333 obj->thickness = Global::penWidth;
335 obj->style = Global::penStyle;
336 obj->color = Global::penColor;
343 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
345 // This is undoing the transform, e.g. going from client coords to local
346 // coords. In essence, the height - y is height + (y * -1), the (y * -1)
347 // term doing the conversion of the y-axis from increasing bottom to top.
348 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
352 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
354 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
355 // No voodoo here, it's just grouped wrong to see it. It should be:
356 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
357 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
361 void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
363 // printf("DrawingView::focusOutEvent()...\n");
364 // Make sure all modkeys being held are marked as released when the app
365 // loses focus (N.B.: This only works because the app sets the focus policy
366 // of this object to something other than Qt::NoFocus)
367 shiftDown = ctrlDown = altDown = false;
369 setCursor(Qt::ArrowCursor);
373 void DrawingView::paintEvent(QPaintEvent * /*event*/)
375 QPainter qtPainter(this);
376 Painter painter(&qtPainter);
379 qtPainter.setRenderHint(QPainter::Antialiasing);
381 Global::viewportHeight = size().height();
383 // Draw coordinate axes
384 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
385 painter.DrawLine(0, -16384, 0, 16384);
386 painter.DrawLine(-16384, 0, 16384, 0);
388 // Do object rendering...
389 for(int i=0; i<Global::numLayers; i++)
391 if (Global::layerHidden[i] == false)
392 RenderObjects(&painter, document.objects, i);
395 // Do tool rendering, if any...
398 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
399 painter.DrawCrosshair(oldPoint);
403 // Do selection rectangle rendering, if any
404 if (Global::selectionInProgress)
406 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
407 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
408 painter.DrawRect(Global::selection);
411 if (hoveringIntersection)
412 painter.DrawHandle(intersectionPoint);
415 painter.DrawHandle(hoverPoint);
417 if (!informativeText.isEmpty())
418 painter.DrawInformativeText(informativeText);
423 // Renders objects in the passed in vector
426 N.B.: Since we have "hoverPointValid" drawing regular object handles above,
427 we can probably do away with a lot of them that are being done down below.
429 [Well, it seems to work OK *except* when you move one of the points, then you get to see nothing. Is it worth fixing there to get rid of problems here? Have to investigate...]
431 void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool ignoreLayer/*= false*/)
433 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
435 Object * obj = (Object *)(*i);
436 float scaledThickness = Global::scale * obj->thickness;
438 // If the object isn't on the current layer being drawn, skip it
439 if (!ignoreLayer && (obj->layer != layer))
442 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
444 painter->SetPen(0x00FF00, 2.0, LSSolid);
448 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
449 painter->SetBrush(obj->color);
451 if (obj->selected || obj->hitObject)
452 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
458 painter->DrawLine(obj->p[0], obj->p[1]);
460 if (obj->hitPoint[0])
461 painter->DrawHandle(obj->p[0]);
463 if (obj->hitPoint[1])
464 painter->DrawHandle(obj->p[1]);
467 painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
472 painter->SetBrush(QBrush(Qt::NoBrush));
473 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
475 if (obj->hitPoint[0])
476 painter->DrawHandle(obj->p[0]);
481 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
483 if (obj->hitPoint[0])
484 painter->DrawHandle(obj->p[0]);
486 if (obj->hitPoint[1])
487 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
489 if (obj->hitPoint[2])
490 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
496 Dimension * d = (Dimension *)obj;
498 Vector v(d->p[0], d->p[1]);
499 double angle = v.Angle();
500 Vector unit = v.Unit();
501 d->lp[0] = d->p[0], d->lp[1] = d->p[1];
503 double x1, y1, length;
505 if (d->subtype == DTLinearVert)
507 if ((angle < 0) || (angle > HALF_TAU))
509 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
510 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
511 ortho = Vector(1.0, 0);
512 angle = THREE_QTR_TAU;
516 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
517 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
518 ortho = Vector(-1.0, 0);
522 d->lp[0].x = d->lp[1].x = x1;
523 length = fabs(d->p[0].y - d->p[1].y);
525 else if (d->subtype == DTLinearHorz)
527 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
529 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
530 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
531 ortho = Vector(0, 1.0);
536 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
537 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
538 ortho = Vector(0, -1.0);
542 d->lp[0].y = d->lp[1].y = y1;
543 length = fabs(d->p[0].x - d->p[1].x);
545 else if (d->subtype == DTLinear)
547 angle = Vector(d->lp[0], d->lp[1]).Angle();
548 ortho = Vector::Normal(d->lp[0], d->lp[1]);
549 length = v.Magnitude();
552 unit = Vector(d->lp[0], d->lp[1]).Unit();
554 Point p1 = d->lp[0] + (ortho * (d->offset + (10.0 * scaledThickness)));
555 Point p2 = d->lp[1] + (ortho * (d->offset + (10.0 * scaledThickness)));
556 Point p3 = d->lp[0] + (ortho * (d->offset + (16.0 * scaledThickness)));
557 Point p4 = d->lp[1] + (ortho * (d->offset + (16.0 * scaledThickness)));
558 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
559 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
562 The numbers hardcoded into here, what are they?
563 I believe they are pixels.
565 // Draw extension lines (if certain type)
566 painter->DrawLine(p3, p5);
567 painter->DrawLine(p4, p6);
569 // Calculate whether or not the arrowheads are too crowded to put
570 // inside the extension lines. 9.0 is the length of the arrowhead.
571 double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
573 // On the screen, it's acting like this is actually 58%...
574 // This is correct, we want it to happen at > 50%
577 // Draw main dimension line + arrowheads
578 painter->DrawLine(p1, p2);
579 painter->DrawArrowhead(p1, p2, scaledThickness);
580 painter->DrawArrowhead(p2, p1, scaledThickness);
584 // Draw outside arrowheads
585 Point p7 = p1 - (unit * 9.0 * scaledThickness);
586 Point p8 = p2 + (unit * 9.0 * scaledThickness);
587 painter->DrawArrowhead(p1, p7, scaledThickness);
588 painter->DrawArrowhead(p2, p8, scaledThickness);
589 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
590 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
593 // Draw length of dimension line...
594 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
595 Point ctr = p2 + (Vector(p2, p1) / 2.0);
600 dimText = QString("%1\"").arg(length);
603 double feet = (double)((int)length / 12);
604 double inches = length - (feet * 12.0);
607 dimText = QString("%1'").arg(feet);
609 dimText = QString("%1' %2\"").arg(feet).arg(inches);
613 Where is the text offset? It looks like it's drawing in the center, but obviously it isn't. It isn't here, it's in Painter::DrawAngledText().
615 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
619 Point hp1 = (p1 + p2) / 2.0;
620 Point hp2 = (p1 + hp1) / 2.0;
621 Point hp3 = (hp1 + p2) / 2.0;
625 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
626 painter->SetBrush(QBrush(QColor(Qt::magenta)));
627 painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
628 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
631 painter->DrawHandle(hp1);
632 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
636 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
637 painter->SetBrush(QBrush(QColor(Qt::magenta)));
638 painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
639 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
642 painter->DrawHandle(hp2);
643 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
647 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
648 painter->SetBrush(QBrush(QColor(Qt::magenta)));
649 painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
650 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
653 painter->DrawHandle(hp3);
656 if (obj->hitPoint[0])
657 painter->DrawHandle(obj->p[0]);
659 if (obj->hitPoint[1])
660 painter->DrawHandle(obj->p[1]);
667 Text * t = (Text *)obj;
669 if (t->measured == false)
671 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
675 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
691 // Containers require recursive rendering...
692 Container * c = (Container *)obj;
693 printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
694 RenderObjects(painter, (*c).objects, layer, ignoreLayer);
696 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
697 // Containers also have special indicators showing they are selected
698 if (c->selected || c->hitObject)
700 // Rect r = GetObjectExtents(obj);
701 // painter->DrawRectCorners(r);
702 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
716 // This toggles the selection being hovered (typically, only 1 object)
718 void DrawingView::AddHoveredToSelection(void)
720 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
722 if (((Object *)(*i))->hovered)
723 // ((Object *)(*i))->selected = true;
724 ((Object *)(*i))->selected = !((Object *)(*i))->selected;
729 void DrawingView::GetSelection(VPVector & v)
733 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
735 if (((Object *)(*i))->selected)
741 VPVector DrawingView::GetSelection(void)
745 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
747 if (((Object *)(*i))->selected)
755 VPVector DrawingView::GetHovered(void)
759 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
761 if (((Object *)(*i))->hovered)
769 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
771 Global::screenSize = Vector(size().width(), size().height());
772 UpdateGridBackground();
776 void DrawingView::ToolHandler(int mode, Point p)
778 // Drop angle snap until it's needed
781 if (Global::tool == TTLine)
782 LineHandler(mode, p);
783 else if (Global::tool == TTCircle)
784 CircleHandler(mode, p);
785 else if (Global::tool == TTArc)
787 else if (Global::tool == TTRotate)
788 RotateHandler(mode, p);
789 else if (Global::tool == TTMirror)
790 MirrorHandler(mode, p);
791 else if (Global::tool == TTDimension)
792 DimensionHandler(mode, p);
793 else if (Global::tool == TTTriangulate)
794 TriangulateHandler(mode, p);
795 else if (Global::tool == TTTrim)
796 TrimHandler(mode, p);
797 else if (Global::tool == TTParallel)
798 ParallelHandler(mode, p);
802 void DrawingView::ToolDraw(Painter * painter)
804 if (Global::tool == TTLine)
806 if (Global::toolState == TSNone)
808 painter->DrawHandle(toolPoint[0]);
810 else if ((Global::toolState == TSPoint2) && shiftDown)
812 painter->DrawHandle(toolPoint[1]);
816 painter->DrawLine(toolPoint[0], toolPoint[1]);
817 painter->DrawHandle(toolPoint[1]);
819 Vector v(toolPoint[0], toolPoint[1]);
820 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
821 double absLength = v.Magnitude();
822 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
823 informativeText = text.arg(absLength).arg(absAngle);
826 else if (Global::tool == TTCircle)
828 if (Global::toolState == TSNone)
830 painter->DrawHandle(toolPoint[0]);
832 else if ((Global::toolState == TSPoint2) && shiftDown)
834 painter->DrawHandle(toolPoint[1]);
838 painter->DrawCross(toolPoint[0]);
839 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
840 painter->SetBrush(QBrush(Qt::NoBrush));
841 painter->DrawEllipse(toolPoint[0], length, length);
842 QString text = tr("Radius: %1 in.");
843 informativeText = text.arg(length);
846 else if (Global::tool == TTArc)
848 if (Global::toolState == TSNone)
850 painter->DrawHandle(toolPoint[0]);
852 else if (Global::toolState == TSPoint2)
854 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
855 painter->SetBrush(QBrush(Qt::NoBrush));
856 painter->DrawEllipse(toolPoint[0], length, length);
857 painter->DrawLine(toolPoint[0], toolPoint[1]);
858 painter->DrawHandle(toolPoint[1]);
859 QString text = tr("Radius: %1 in.");
860 informativeText = text.arg(length);
862 else if (Global::toolState == TSPoint3)
864 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
865 painter->DrawLine(toolPoint[0], toolPoint[2]);
866 painter->SetBrush(QBrush(Qt::NoBrush));
867 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
868 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
869 QString text = tr("Angle start: %1") + QChar(0x00B0);
870 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
874 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
875 double span = angle - toolPoint[2].x;
880 painter->DrawLine(toolPoint[0], toolPoint[3]);
881 painter->SetBrush(QBrush(Qt::NoBrush));
882 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
883 painter->SetPen(0xFF00FF, 2.0, LSSolid);
884 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
885 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
886 QString text = tr("Arc span: %1") + QChar(0x00B0);
887 informativeText = text.arg(RADIANS_TO_DEGREES * span);
890 else if (Global::tool == TTRotate)
892 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
893 painter->DrawHandle(toolPoint[0]);
894 else if ((Global::toolState == TSPoint2) && shiftDown)
895 painter->DrawHandle(toolPoint[1]);
898 if (toolPoint[0] == toolPoint[1])
901 painter->DrawLine(toolPoint[0], toolPoint[1]);
903 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
904 QString text = QChar(0x2221) + QObject::tr(": %1");
905 informativeText = text.arg(absAngle);
908 informativeText += " (Copy)";
911 else if (Global::tool == TTMirror)
913 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
914 painter->DrawHandle(toolPoint[0]);
915 else if ((Global::toolState == TSPoint2) && shiftDown)
916 painter->DrawHandle(toolPoint[1]);
919 if (toolPoint[0] == toolPoint[1])
922 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
923 painter->DrawLine(mirrorPoint, toolPoint[1]);
925 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
927 if (absAngle > 180.0)
930 QString text = QChar(0x2221) + QObject::tr(": %1");
931 informativeText = text.arg(absAngle);
934 informativeText += " (Copy)";
937 if (Global::tool == TTDimension)
939 if (Global::toolState == TSNone)
941 painter->DrawHandle(toolPoint[0]);
943 else if ((Global::toolState == TSPoint2) && shiftDown)
945 painter->DrawHandle(toolPoint[1]);
949 painter->DrawLine(toolPoint[0], toolPoint[1]);
950 painter->DrawHandle(toolPoint[1]);
952 Vector v(toolPoint[0], toolPoint[1]);
953 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
954 double absLength = v.Magnitude();
955 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
956 informativeText = text.arg(absLength).arg(absAngle);
962 void DrawingView::LineHandler(int mode, Point p)
967 if (Global::toolState == TSNone)
975 if (Global::toolState == TSNone)
983 if (Global::toolState == TSNone)
985 Global::toolState = TSPoint2;
986 // Prevent spurious line from drawing...
987 toolPoint[1] = toolPoint[0];
989 else if ((Global::toolState == TSPoint2) && shiftDown)
991 // Key override is telling us to make a new line, not continue the
993 toolPoint[0] = toolPoint[1];
997 Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
998 l->layer = Global::activeLayer;
999 document.objects.push_back(l);
1000 toolPoint[0] = toolPoint[1];
1006 void DrawingView::CircleHandler(int mode, Point p)
1011 if (Global::toolState == TSNone)
1019 if (Global::toolState == TSNone)
1027 if (Global::toolState == TSNone)
1029 Global::toolState = TSPoint2;
1030 // Prevent spurious line from drawing...
1031 toolPoint[1] = toolPoint[0];
1033 else if ((Global::toolState == TSPoint2) && shiftDown)
1035 // Key override is telling us to make a new line, not continue the
1037 toolPoint[0] = toolPoint[1];
1041 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1042 Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1043 c->layer = Global::activeLayer;
1044 document.objects.push_back(c);
1045 toolPoint[0] = toolPoint[1];
1046 Global::toolState = TSNone;
1052 void DrawingView::ArcHandler(int mode, Point p)
1057 if (Global::toolState == TSNone)
1059 else if (Global::toolState == TSPoint2)
1061 else if (Global::toolState == TSPoint3)
1069 if (Global::toolState == TSNone)
1071 else if (Global::toolState == TSPoint2)
1073 else if (Global::toolState == TSPoint3)
1087 if (Global::toolState == TSNone)
1089 // Prevent spurious line from drawing...
1090 toolPoint[1] = toolPoint[0];
1091 Global::toolState = TSPoint2;
1093 else if (Global::toolState == TSPoint2)
1097 // Key override is telling us to start arc at new center, not
1098 // continue the current one.
1099 toolPoint[0] = toolPoint[1];
1103 // Set the radius in toolPoint[1].x
1104 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1105 Global::toolState = TSPoint3;
1107 else if (Global::toolState == TSPoint3)
1109 // Set the angle in toolPoint[2].x
1110 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1111 Global::toolState = TSPoint4;
1115 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1116 double span = endAngle - toolPoint[2].x;
1121 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1122 arc->layer = Global::activeLayer;
1123 document.objects.push_back(arc);
1124 Global::toolState = TSNone;
1130 void DrawingView::RotateHandler(int mode, Point p)
1135 if (Global::toolState == TSNone)
1138 // SavePointsFrom(select, toolScratch);
1139 CopyObjects(select, toolScratch2);
1140 Global::toolState = TSPoint1;
1142 else if (Global::toolState == TSPoint1)
1150 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1152 else if (Global::toolState == TSPoint2)
1160 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1161 VPVectorIter j = select.begin();
1162 // std::vector<Object>::iterator i = toolScratch.begin();
1163 VPVectorIter i = toolScratch2.begin();
1165 // for(; i!=toolScratch.end(); i++, j++)
1166 for(; i!=toolScratch2.end(); i++, j++)
1168 // Object objT = *i;
1169 // Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1170 // Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1171 Object * objT = (Object *)(*i);
1172 Object * objS = (Object *)(*j);
1174 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1175 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1180 // if (objT.type == OTArc || objT.type == OTText)
1181 if (objT->type == OTArc || objT->type == OTText)
1183 // objS->angle[0] = objT.angle[0] + angle;
1184 objS->angle[0] = objT->angle[0] + angle;
1186 if (objS->angle[0] > TAU)
1187 objS->angle[0] -= TAU;
1189 // else if (objT.type == OTContainer)
1190 else if (objT->type == OTContainer)
1192 // 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...]
1193 // Container * c = (Container *)&objT;
1194 Container * c = (Container *)objT;
1195 Container * c2 = (Container *)objS;
1196 VPVectorIter l = c->objects.begin();
1197 // TODO: Rotate items in the container
1198 // TODO: Make this recursive
1199 for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1201 Object * obj3 = (Object *)(*k);
1202 Object * obj4 = (Object *)(*l);
1204 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1205 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1209 // obj3->angle[0] = objT.angle[0] + angle;
1210 obj3->angle[0] = obj4->angle[0] + angle;
1212 if (obj3->angle[0] > TAU)
1213 obj3->angle[0] -= TAU;
1216 Rect r = GetObjectExtents(objS);
1217 c2->p[0] = r.TopLeft();
1218 c2->p[1] = r.BottomRight();
1226 if (Global::toolState == TSPoint1)
1228 Global::toolState = TSPoint2;
1229 // Prevent spurious line from drawing...
1230 toolPoint[1] = toolPoint[0];
1232 else if ((Global::toolState == TSPoint2) && shiftDown)
1234 // Key override is telling us to make a new line, not continue the
1236 toolPoint[0] = toolPoint[1];
1240 // Either we're finished with our rotate, or we're stamping a copy.
1243 // Stamp a copy of the selection at the current rotation & bail
1245 CopyObjects(select, temp);
1246 ClearSelected(temp);
1247 AddObjectsTo(document.objects, temp);
1248 // RestorePointsTo(select, toolScratch);
1249 RestorePointsTo(select, toolScratch2);
1254 Global::toolState = TSPoint1;
1255 // SavePointsFrom(select, toolScratch);
1256 DeleteContents(toolScratch2);
1257 CopyObjects(select, toolScratch2);
1263 // Reset the selection if shift held down...
1265 // RestorePointsTo(select, toolScratch);
1266 RestorePointsTo(select, toolScratch2);
1271 // Reset selection when key is let up
1273 RotateHandler(ToolMouseMove, toolPoint[1]);
1278 // RestorePointsTo(select, toolScratch);
1279 RestorePointsTo(select, toolScratch2);
1280 DeleteContents(toolScratch2);
1285 void DrawingView::MirrorHandler(int mode, Point p)
1290 if (Global::toolState == TSNone)
1293 SavePointsFrom(select, toolScratch);
1294 Global::toolState = TSPoint1;
1296 else if (Global::toolState == TSPoint1)
1304 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1306 else if (Global::toolState == TSPoint2)
1314 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1315 VPVectorIter j = select.begin();
1316 std::vector<Object>::iterator i = toolScratch.begin();
1318 for(; i!=toolScratch.end(); i++, j++)
1321 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1322 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1323 Object * obj2 = (Object *)(*j);
1328 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1329 a negative start angle which makes it impossible to interact with.
1332 if (obj.type == OTArc)
1334 // This is 2*mirror angle - obj angle - obj span
1335 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1337 if (obj2->angle[0] > TAU)
1338 obj2->angle[0] -= TAU;
1346 if (Global::toolState == TSPoint1)
1348 Global::toolState = TSPoint2;
1349 // Prevent spurious line from drawing...
1350 toolPoint[1] = toolPoint[0];
1352 else if ((Global::toolState == TSPoint2) && shiftDown)
1354 // Key override is telling us to make a new line, not continue the
1356 toolPoint[0] = toolPoint[1];
1360 // Either we're finished with our rotate, or we're stamping a copy.
1363 // Stamp a copy of the selection at the current rotation & bail
1365 CopyObjects(select, temp);
1366 ClearSelected(temp);
1367 AddObjectsTo(document.objects, temp);
1368 RestorePointsTo(select, toolScratch);
1373 Global::toolState = TSPoint1;
1374 SavePointsFrom(select, toolScratch);
1380 // Reset the selection if shift held down...
1382 RestorePointsTo(select, toolScratch);
1387 // Reset selection when key is let up
1389 MirrorHandler(ToolMouseMove, toolPoint[1]);
1394 RestorePointsTo(select, toolScratch);
1399 void DrawingView::DimensionHandler(int mode, Point p)
1404 if (Global::toolState == TSNone)
1412 if (Global::toolState == TSNone)
1420 if (Global::toolState == TSNone)
1422 Global::toolState = TSPoint2;
1423 // Prevent spurious line from drawing...
1424 toolPoint[1] = toolPoint[0];
1426 else if ((Global::toolState == TSPoint2) && shiftDown)
1428 // Key override is telling us to make a new line, not continue the
1430 toolPoint[0] = toolPoint[1];
1434 Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear);
1435 d->layer = Global::activeLayer;
1436 document.objects.push_back(d);
1437 Global::toolState = TSNone;
1443 void DrawingView::TriangulateHandler(int mode, Point/*p*/)
1449 // Skip if nothing hovered...
1450 if (numHovered != 1)
1453 VPVector hover = GetHovered();
1454 Object * obj = (Object *)hover[0];
1456 // Skip if it's not a line...
1457 if (obj->type != OTLine)
1460 if (Global::toolState == TSNone)
1462 else if (Global::toolState == TSPoint2)
1471 if (Global::toolState == TSNone)
1473 else if (Global::toolState == TSPoint2)
1475 else if (Global::toolState == TSPoint3)
1489 if (Global::toolState == TSNone)
1491 Global::toolState = TSPoint2;
1493 else if (Global::toolState == TSPoint2)
1497 // Key override is telling us to start arc at new center, not
1498 // continue the current one.
1499 toolPoint[0] = toolPoint[1];
1503 Global::toolState = TSPoint3;
1507 double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1508 double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1510 Circle c1(toolObj[0]->p[0], len2);
1511 Circle c2(toolObj[0]->p[1], len3);
1513 Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1515 // Only move lines if the triangle formed by them is not degenerate
1516 if (Global::numIntersectPoints > 0)
1518 toolObj[1]->p[0] = toolObj[0]->p[0];
1519 toolObj[1]->p[1] = Global::intersectPoint[0];
1521 toolObj[2]->p[0] = Global::intersectPoint[0];
1522 toolObj[2]->p[1] = toolObj[0]->p[1];
1525 Global::toolState = TSNone;
1531 void DrawingView::TrimHandler(int mode, Point p)
1534 n.b.: this code is lifted straight out of the old oo code. needs to be updated.
1541 Object * toTrim = doc->lastObjectHovered;
1546 Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
1548 // Check to see which case we have...
1549 // We're trimming point #1...
1552 ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
1556 ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
1560 Point p1 = ((Line *)toTrim)->position + (v * t);
1561 Point p2 = ((Line *)toTrim)->position + (v * u);
1562 Point p3 = ((Line *)toTrim)->endpoint;
1563 ((Line *)toTrim)->endpoint = p1;
1564 Line * line = new Line(p2, p3);
1565 emit ObjectReady(line);
1568 doc->lastObjectHovered = NULL;
1576 Object * toTrim = doc->lastObjectHovered;
1582 if (toTrim->type != OTLine)
1585 double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
1587 std::vector<Object *>::iterator i;
1589 for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
1591 // Can't trim against yourself... :-P
1595 Object * trimAgainst = *i;
1598 if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
1601 int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
1605 // Now what? We don't know which side to trim!
1606 // ... now we do, we know which side of the Line we're on!
1607 if ((t1 > t) && (t1 < pointHoveredT))
1610 if ((t1 < u) && (t1 > pointHoveredT))
1615 // Bail out if nothing hovered...
1616 if (numHovered != 1)
1622 VPVector hover = GetHovered();
1623 Object * obj = (Object *)hover[0];
1625 // Skip if it's not a line...
1626 if (obj->type != OTLine)
1633 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1635 // Currently only deal with line against line trimming, can expand to
1636 // others as well (line/circle, circle/circle, line/arc, etc)
1638 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1640 obj = (Object *)(*i);
1642 if (obj == toolObj[0])
1644 else if (obj->type != OTLine)
1647 Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1649 if (Global::numIntersectParams > 0)
1651 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1672 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1697 void DrawingView::mousePressEvent(QMouseEvent * event)
1699 if (event->button() == Qt::LeftButton)
1701 //printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
1702 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1704 // Handle tool processing, if any
1707 if (hoveringIntersection)
1708 point = intersectionPoint;
1709 else if (hoverPointValid)
1711 else if (Global::snapToGrid)
1712 point = SnapPointToGrid(point);
1714 //Also, may want to figure out if hovering over a snap point on an
1715 //object, snap to grid if not.
1716 // Snap to object point if valid...
1717 // if (Global::snapPointIsValid)
1718 // point = Global::snapPoint;
1720 ToolHandler(ToolMouseDown, point);
1724 // Clear the selection only if CTRL isn't being held on click
1726 ClearSelected(document.objects);
1727 // ClearSelection();
1729 // If any objects are being hovered on click, add them to the selection
1733 AddHoveredToSelection();
1734 update(); // needed??
1735 // GetHovered(hover); // prolly needed
1736 VPVector hover2 = GetHovered();
1737 dragged = (Object *)hover2[0];
1738 draggingObject = true;
1739 //printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
1741 // Alert the pen widget
1742 emit ObjectSelected(dragged);
1744 // See if anything is using just a straight click on a handle
1745 if (HandleObjectClicked())
1747 draggingObject = false;
1752 // Needed for grab & moving objects
1753 // We do it *after*... why? (doesn't seem to confer any advantage...)
1754 if (hoveringIntersection)
1755 oldPoint = intersectionPoint;
1756 else if (hoverPointValid)
1757 oldPoint = hoverPoint;
1758 else if (Global::snapToGrid)
1759 oldPoint = SnapPointToGrid(point);
1761 // Needed for fixed length handling
1762 if (Global::fixedLength)
1764 if (dragged->type == OTLine)
1766 dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1770 if (dragged->type == OTCircle)
1772 // Save for informative text, uh, er, informing
1773 dragged->length = dragged->radius[0];
1779 // Didn't hit any object and not using a tool, so do a selection
1781 Global::selectionInProgress = true;
1782 Global::selection.setTopLeft(QPointF(point.x, point.y));
1783 Global::selection.setBottomRight(QPointF(point.x, point.y));
1784 select = GetSelection();
1786 else if (event->button() == Qt::MiddleButton)
1789 oldPoint = Vector(event->x(), event->y());
1790 // Should also change the mouse pointer as well...
1791 setCursor(Qt::SizeAllCursor);
1796 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1798 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1799 Global::selection.setBottomRight(QPointF(point.x, point.y));
1800 // Only needs to be done here, as mouse down is always preceded by movement
1801 Global::snapPointIsValid = false;
1802 hoveringIntersection = false;
1803 oldScrollPoint = Vector(event->x(), event->y());
1806 if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1808 point = Vector(event->x(), event->y());
1809 // Since we're using Qt coords for scrolling, we have to adjust them
1810 // here to conform to Cartesian coords, since the origin is using
1812 Vector delta(oldPoint, point);
1813 delta /= Global::zoom;
1815 Global::origin -= delta;
1817 UpdateGridBackground();
1823 // If we're doing a selection rect, see if any objects are engulfed by it
1824 // (implies left mouse button held down)
1825 if (Global::selectionInProgress)
1827 CheckObjectBounds();
1829 // Make sure previously selected objects stay selected (CTRL held)
1830 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
1831 ((Object *)(*i))->selected = true;
1837 // Do object hit testing...
1838 bool needUpdate = HitTestObjects(point);
1839 VPVector hover2 = GetHovered();
1844 printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1846 if (hover2.size() > 0)
1847 printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1852 // Check for multi-hover...
1855 //need to check for case where hover is over 2 circles and a 3rd's center...
1856 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1858 Geometry::Intersects(obj1, obj2);
1859 int numIntersecting = Global::numIntersectParams;
1860 double t = Global::intersectParam[0];
1861 double u = Global::intersectParam[1];
1863 if (numIntersecting > 0)
1865 Vector v1 = Geometry::GetPointForParameter(obj1, t);
1866 Vector v2 = Geometry::GetPointForParameter(obj2, u);
1867 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1868 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1870 hoveringIntersection = true;
1871 intersectionPoint = v1;
1874 numIntersecting = Global::numIntersectPoints;
1876 if (numIntersecting > 0)
1878 Vector v1 = Global::intersectPoint[0];
1880 if (numIntersecting == 2)
1882 Vector v2 = Global::intersectPoint[1];
1884 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1888 QString text = tr("Intersection <%1, %2>");
1889 informativeText = text.arg(v1.x).arg(v1.y);
1890 hoveringIntersection = true;
1891 intersectionPoint = v1;
1894 else if (numHovered == 1)
1896 Object * obj = (Object *)hover2[0];
1898 if (obj->type == OTLine)
1901 Not sure that this is the best way to handle this, but it works(TM)...
1902 Except when lines are overlapping, then it doesn't work... !!! FIX !!!
1904 Point midpoint = Geometry::Midpoint((Line *)obj);
1905 Vector v1 = Vector::Magnitude(midpoint, point);
1907 if ((v1.Magnitude() * Global::zoom) < 8.0)
1909 QString text = tr("Midpoint <%1, %2>");
1910 informativeText = text.arg(midpoint.x).arg(midpoint.y);
1911 hoverPointValid = true;
1912 hoverPoint = midpoint;
1918 // Handle object movement (left button down & over an object)
1919 if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
1921 if (hoveringIntersection)
1922 point = intersectionPoint;
1923 else if (hoverPointValid)
1925 else if (Global::snapToGrid)
1926 point = SnapPointToGrid(point);
1928 HandleObjectMovement(point);
1934 // Do tool handling, if any are active...
1937 if (hoveringIntersection)
1938 point = intersectionPoint;
1939 else if (hoverPointValid)
1941 else if (Global::snapToGrid)
1944 point = SnapPointToAngle(point);
1946 point = SnapPointToGrid(point);
1949 ToolHandler(ToolMouseMove, point);
1952 // This is used to draw the tool crosshair...
1955 if (needUpdate || Global::tool)
1960 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1962 if (event->button() == Qt::LeftButton)
1964 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1965 //could set it up to use the document's update function (assumes that all object
1966 //updates are being reported correctly:
1967 // if (document.NeedsUpdate())
1968 // Do an update if collided with at least *one* object in the document
1974 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1975 ToolHandler(ToolMouseUp, point);
1979 // if (Global::selectionInProgress)
1980 Global::selectionInProgress = false;
1982 informativeText.clear();
1983 // Should we be doing this automagically? Hmm...
1984 // Clear our vectors
1988 // 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)
1989 select = GetSelection();
1991 draggingObject = false;
1993 else if (event->button() == Qt::MiddleButton)
1996 setCursor(Qt::ArrowCursor);
2001 void DrawingView::wheelEvent(QWheelEvent * event)
2003 double zoomFactor = 1.25;
2004 QSize sizeWin = size();
2005 Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
2006 center = Painter::QtToCartesianCoords(center);
2008 // This is not centering for some reason. Need to figure out why. :-/
2009 if (event->delta() > 0)
2011 if (Global::zoom > 20.0)
2014 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
2015 Global::origin = newOrigin;
2016 Global::zoom *= zoomFactor;
2020 if (Global::zoom < 0.25)
2023 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
2024 Global::origin = newOrigin;
2025 Global::zoom /= zoomFactor;
2028 // Global::gridSpacing = gridPixels / Painter::zoom;
2029 // UpdateGridBackground();
2030 SetGridSize(Global::gridSpacing * Global::zoom);
2032 // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
2036 void DrawingView::keyPressEvent(QKeyEvent * event)
2038 bool oldShift = shiftDown;
2039 bool oldCtrl = ctrlDown;
2040 bool oldAlt = altDown;
2042 if (event->key() == Qt::Key_Shift)
2044 else if (event->key() == Qt::Key_Control)
2046 else if (event->key() == Qt::Key_Alt)
2049 // If there's a change in any of the modifier key states, pass it on to
2050 // the current tool's handler
2051 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2054 ToolHandler(ToolKeyDown, Point(0, 0));
2059 if (oldAlt != altDown)
2062 setCursor(Qt::SizeAllCursor);
2063 oldPoint = oldScrollPoint;
2066 if (select.size() > 0)
2068 if (event->key() == Qt::Key_Up)
2070 TranslateObjects(select, Point(0, +1.0));
2073 else if (event->key() == Qt::Key_Down)
2075 TranslateObjects(select, Point(0, -1.0));
2078 else if (event->key() == Qt::Key_Right)
2080 TranslateObjects(select, Point(+1.0, 0));
2083 else if (event->key() == Qt::Key_Left)
2085 TranslateObjects(select, Point(-1.0, 0));
2092 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2094 bool oldShift = shiftDown;
2095 bool oldCtrl = ctrlDown;
2096 bool oldAlt = altDown;
2098 if (event->key() == Qt::Key_Shift)
2100 else if (event->key() == Qt::Key_Control)
2102 else if (event->key() == Qt::Key_Alt)
2105 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2108 ToolHandler(ToolKeyUp, Point(0, 0));
2113 if (oldAlt != altDown)
2116 setCursor(Qt::ArrowCursor);
2122 // This looks strange, but it's really quite simple: We want a point that's
2123 // more than half-way to the next grid point to snap there while conversely we
2124 // want a point that's less than half-way to to the next grid point then snap
2125 // to the one before it. So we add half of the grid spacing to the point, then
2126 // divide by it so that we can remove the fractional part, then multiply it
2127 // back to get back to the correct answer.
2129 Point DrawingView::SnapPointToGrid(Point point)
2131 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
2132 point /= Global::gridSpacing;
2133 point.x = floor(point.x);//need to fix this for negative numbers...
2134 point.y = floor(point.y);
2135 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
2136 point *= Global::gridSpacing;
2141 Point DrawingView::SnapPointToAngle(Point point)
2143 // Snap to a single digit angle (using toolpoint #1 as the center)
2144 double angle = Vector::Angle(toolPoint[0], point);
2145 double length = Vector::Magnitude(toolPoint[0], point);
2147 // Convert from radians to degrees
2148 double degAngle = angle * RADIANS_TO_DEGREES;
2149 double snapAngle = (double)((int)(degAngle + 0.5));
2152 v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2153 point = toolPoint[0] + v;
2159 Rect DrawingView::GetObjectExtents(Object * obj)
2161 // Default to empty rect, if object checks below fail for some reason
2169 rect = Rect(obj->p[0], obj->p[1]);
2175 rect = Rect(obj->p[0], obj->p[0]);
2176 rect.Expand(obj->radius[0]);
2182 Arc * a = (Arc *)obj;
2184 double start = a->angle[0];
2185 double end = start + a->angle[1];
2186 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2188 // If the end of the arc is before the beginning, add 360 degrees to it
2192 // Adjust the bounds depending on which axes are crossed
2193 if ((start < QTR_TAU) && (end > QTR_TAU))
2196 if ((start < HALF_TAU) && (end > HALF_TAU))
2199 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2202 if ((start < TAU) && (end > TAU))
2205 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2208 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2211 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2214 rect *= a->radius[0];
2215 rect.Translate(a->p[0]);
2221 Text * t = (Text *)obj;
2222 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2228 Container * c = (Container *)obj;
2229 VPVectorIter i = c->objects.begin();
2230 rect = GetObjectExtents((Object *)*i);
2233 for(; i!=c->objects.end(); i++)
2234 rect |= GetObjectExtents((Object *)*i);
2245 void DrawingView::CheckObjectBounds(void)
2249 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2251 Object * obj = (Object *)(*i);
2252 obj->selected = false;
2259 Line * l = (Line *)obj;
2261 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2269 Circle * c = (Circle *)obj;
2271 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]))
2279 Arc * a = (Arc *)obj;
2281 double start = a->angle[0];
2282 double end = start + a->angle[1];
2283 QPointF p1(cos(start), sin(start));
2284 QPointF p2(cos(end), sin(end));
2285 QRectF bounds(p1, p2);
2288 // Swap X/Y coordinates if they're backwards...
2289 if (bounds.left() > bounds.right())
2291 double temp = bounds.left();
2292 bounds.setLeft(bounds.right());
2293 bounds.setRight(temp);
2296 if (bounds.bottom() > bounds.top())
2298 double temp = bounds.bottom();
2299 bounds.setBottom(bounds.top());
2300 bounds.setTop(temp);
2303 // Doesn't work as advertised! For shame!
2304 bounds = bounds.normalized();
2307 // If the end of the arc is before the beginning, add 360 degrees
2312 // Adjust the bounds depending on which axes are crossed
2313 if ((start < QTR_TAU) && (end > QTR_TAU))
2316 if ((start < HALF_TAU) && (end > HALF_TAU))
2317 bounds.setLeft(-1.0);
2319 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2320 bounds.setBottom(-1.0);
2322 if ((start < TAU) && (end > TAU))
2323 bounds.setRight(1.0);
2325 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2328 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2329 bounds.setLeft(-1.0);
2331 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2332 bounds.setBottom(-1.0);
2334 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2335 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2336 bounds.translate(a->p[0].x, a->p[0].y);
2338 if (Global::selection.contains(bounds))
2346 Text * t = (Text *)obj;
2347 Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2349 if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2357 Container * c = (Container *)obj;
2359 if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2372 bool DrawingView::HitTestObjects(Point point)
2376 bool needUpdate = false;
2377 hoverPointValid = false;
2379 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2381 Object * obj = (Object *)(*i);
2383 // If we're seeing the object we're dragging, skip it
2384 if (draggingObject && (obj == dragged))
2387 if (HitTest(obj, point) == true)
2393 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2394 emit ObjectHovered(obj);
2402 bool DrawingView::HitTest(Object * obj, Point point)
2404 bool needUpdate = false;
2410 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2411 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2412 Vector lineSegment = obj->p[1] - obj->p[0];
2413 Vector v1 = point - obj->p[0];
2414 Vector v2 = point - obj->p[1];
2415 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2419 distance = v1.Magnitude();
2421 distance = v2.Magnitude();
2423 // distance = ?Det?(ls, v1) / |ls|
2424 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2425 / lineSegment.Magnitude());
2427 if ((v1.Magnitude() * Global::zoom) < 8.0)
2429 obj->hitPoint[0] = true;
2430 hoverPoint = obj->p[0];
2431 hoverPointValid = true;
2433 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2435 obj->hitPoint[1] = true;
2436 hoverPoint = obj->p[1];
2437 hoverPointValid = true;
2439 else if ((distance * Global::zoom) < 5.0)
2440 obj->hitObject = true;
2442 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2444 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2452 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2453 obj->hitPoint[0] = obj->hitObject = false;
2454 double length = Vector::Magnitude(obj->p[0], point);
2456 if ((length * Global::zoom) < 8.0)
2458 obj->hitPoint[0] = true;
2459 hoverPoint = obj->p[0];
2460 hoverPointValid = true;
2462 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2463 obj->hitObject = true;
2465 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2467 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2475 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2476 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2477 double length = Vector::Magnitude(obj->p[0], point);
2478 double angle = Vector::Angle(obj->p[0], point);
2480 // Make sure we get the angle in the correct spot
2481 if (angle < obj->angle[0])
2484 // Get the span that we're pointing at...
2485 double span = angle - obj->angle[0];
2487 // N.B.: Still need to hit test the arc start & arc span handles...
2488 double spanAngle = obj->angle[0] + obj->angle[1];
2489 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2490 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2491 double length2 = Vector::Magnitude(point, handle1);
2492 double length3 = Vector::Magnitude(point, handle2);
2494 if ((length * Global::zoom) < 8.0)
2496 obj->hitPoint[0] = true;
2497 hoverPoint = obj->p[0];
2498 hoverPointValid = true;
2500 else if ((length2 * Global::zoom) < 8.0)
2502 obj->hitPoint[1] = true;
2503 hoverPoint = handle1;
2504 hoverPointValid = true;
2506 else if ((length3 * Global::zoom) < 8.0)
2508 obj->hitPoint[2] = true;
2509 hoverPoint = handle2;
2510 hoverPointValid = true;
2512 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2513 obj->hitObject = true;
2515 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2517 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2525 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2526 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2528 Dimension * d = (Dimension *)obj;
2530 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2531 // Get our line parallel to our points
2532 float scaledThickness = Global::scale * obj->thickness;
2534 Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2535 Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2537 Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2538 Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2540 Point p3(p1, point);
2542 Vector v1(d->p[0], point);
2543 Vector v2(d->p[1], point);
2544 Vector lineSegment(p1, p2);
2545 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2547 Point midpoint = (p1 + p2) / 2.0;
2548 Point hFSPoint = Point(midpoint, point);
2549 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2550 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2553 distance = v1.Magnitude();
2555 distance = v2.Magnitude();
2557 // distance = ?Det?(ls, v1) / |ls|
2558 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2559 / lineSegment.Magnitude());
2561 if ((v1.Magnitude() * Global::zoom) < 8.0)
2562 obj->hitPoint[0] = true;
2563 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2564 obj->hitPoint[1] = true;
2565 else if ((distance * Global::zoom) < 5.0)
2566 obj->hitObject = true;
2568 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2569 obj->hitPoint[2] = true;
2570 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2571 obj->hitPoint[3] = true;
2572 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2573 obj->hitPoint[4] = true;
2575 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2577 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2585 Text * t = (Text *)obj;
2586 bool oldHO = obj->hitObject;
2587 obj->hitObject = false;
2589 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2590 //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);
2592 if (r.Contains(point))
2593 obj->hitObject = true;
2595 obj->hovered = (obj->hitObject ? true : false);
2597 if (oldHO != obj->hitObject)
2605 // Containers must be recursively tested... Or do they???
2607 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.
2609 // bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2610 // Object * oldClicked = c->clicked;
2612 still need to compare old state to new state, and set things up based upon that...
2613 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);
2614 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.
2616 Container * c = (Container *)obj;
2617 c->hitObject = false;
2621 VPVector flat = Flatten(c);
2623 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2624 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
2626 Object * cObj = (Object *)(*i);
2628 // Skip the flattened containers (if any)...
2629 if (cObj->type == OTContainer)
2632 // We do it this way instead of needUpdate = HitTest() because we
2633 // are checking more than one object, and that way of doing will
2634 // not return consistent results.
2635 if (HitTest(cObj, point) == true)
2637 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2639 // c->hitObject = true;
2640 // c->clicked = cObj;
2641 // c->hovered = true;
2644 // Same reasons for doing it this way here apply.
2645 if (cObj->hitObject == true)
2647 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2648 c->hitObject = true;
2652 if (cObj->hitPoint[0] == true)
2654 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2655 c->hitPoint[0] = true;
2659 if (cObj->hitPoint[1] == true)
2661 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2662 c->hitPoint[1] = true;
2666 if (cObj->hitPoint[2] == true)
2668 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2669 c->hitPoint[2] = true;
2673 if (cObj->hovered == true)
2674 c->hovered = true;//*/
2688 bool DrawingView::HandleObjectClicked(void)
2690 if (dragged->type == OTDimension)
2692 Dimension * d = (Dimension *)dragged;
2696 // Hit the "flip sides" switch, so flip 'em
2697 Point temp = d->p[0];
2702 else if (d->hitPoint[3])
2704 // There are three cases here: aligned, horizontal, & vertical.
2705 // Aligned and horizontal do the same thing, vertical goes back to
2707 if (d->subtype == DTLinearVert)
2708 d->subtype = DTLinear;
2710 d->subtype = DTLinearVert;
2714 else if (d->hitPoint[4])
2716 // There are three cases here: aligned, horizontal, & vertical.
2717 // Aligned and vertical do the same thing, horizontal goes back to
2719 if (d->subtype == DTLinearHorz)
2720 d->subtype = DTLinear;
2722 d->subtype = DTLinearHorz;
2732 void DrawingView::HandleObjectMovement(Point point)
2734 Point delta = point - oldPoint;
2735 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2736 // Object * obj = (Object *)hover[0];
2737 Object * obj = dragged;
2738 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2739 //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"));
2744 if (obj->hitPoint[0])
2746 if (Global::fixedLength)
2748 Vector line = point - obj->p[1];
2749 Vector unit = line.Unit();
2750 point = obj->p[1] + (unit * obj->length);
2755 else if (obj->hitPoint[1])
2757 if (Global::fixedLength)
2759 Vector line = point - obj->p[0];
2760 Vector unit = line.Unit();
2761 point = obj->p[0] + (unit * obj->length);
2766 else if (obj->hitObject)
2775 if (obj->hitPoint[0])
2777 else if (obj->hitObject)
2779 double oldRadius = obj->length;
2780 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2782 QString text = QObject::tr("Radius: %1\nScale: %2%");
2783 informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2789 if (obj->hitPoint[0])
2791 else if (obj->hitPoint[1])
2793 // Change the Arc's span (handle #1)
2796 double angle = Vector::Angle(obj->p[0], point);
2797 double delta = angle - obj->angle[0];
2802 obj->angle[1] -= delta;
2803 obj->angle[0] = angle;
2805 if (obj->angle[1] < 0)
2806 obj->angle[1] += TAU;
2808 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2809 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);
2813 double angle = Vector::Angle(obj->p[0], point);
2814 obj->angle[0] = angle;
2815 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2816 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2818 else if (obj->hitPoint[2])
2820 // Change the Arc's span (handle #2)
2823 double angle = Vector::Angle(obj->p[0], point);
2824 obj->angle[1] = angle - obj->angle[0];
2826 if (obj->angle[1] < 0)
2827 obj->angle[1] += TAU;
2829 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2830 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);
2834 double angle = Vector::Angle(obj->p[0], point);
2835 obj->angle[0] = angle - obj->angle[1];
2837 if (obj->angle[0] < 0)
2838 obj->angle[0] += TAU;
2840 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2841 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2843 else if (obj->hitObject)
2850 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2851 QString text = QObject::tr("Radius: %1");
2852 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2858 if (obj->hitPoint[0])
2860 else if (obj->hitPoint[1])
2862 else if (obj->hitObject)
2864 // Move measurement lines in/out
2867 Dimension * d = (Dimension *)obj;
2868 double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
2869 float scaledThickness = Global::scale * obj->thickness;
2870 // Looks like offset is 0 to +MAX, but line is at 10.0. So
2871 // anything less than 10.0 should set the offset to 0.
2874 if (dist > (10.0 * scaledThickness))
2875 d->offset = dist - (10.0 * scaledThickness);
2893 // This is shitty, but works for now until I can code up something
2896 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.
2898 // TranslateObject(obj, delta);
2899 TranslateContainer((Container *)obj, point, delta);
2908 void DrawingView::AddDimensionTo(void * o)
2910 Object * obj = (Object *)o;
2915 document.Add(new Dimension(obj->p[0], obj->p[1]));