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"
42 #include "penwidget.h"
46 #define BACKGROUND_MAX_SIZE 512
48 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
49 // The value in the settings file will override this.
50 useAntialiasing(true), numHovered(0), shiftDown(false),
51 ctrlDown(false), altDown(false),
52 gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
53 scale(1.0), offsetX(-10), offsetY(-10), supressSelected(false),
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 curMarker = QCursor(QPixmap(":/res/cursor-marker.png"), 1, 18);
64 curDropper = QCursor(QPixmap(":/res/cursor-dropper.png"), 1, 20);
66 Global::gridSpacing = 12.0; // In base units (inch is default)
68 Line * line = new Line(Vector(5, 5), Vector(50, 40), 2.0, 0xFF7F00, LSDash);
70 document.Add(new Line(Vector(50, 40), Vector(10, 83)));
71 document.Add(new Line(Vector(10, 83), Vector(17, 2)));
72 document.Add(new Circle(Vector(100, 100), 36));
73 document.Add(new Circle(Vector(50, 150), 49));
74 document.Add(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
75 document.Add(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
76 document.Add(new Text(Vector(10, 83), "Here is some awesome text!"));
81 Here we set the grid size in pixels--12 in this case. Initially, we have our
82 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
83 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
84 to be able to set the size of the background grid (which we do here at an
85 arbitrary 12 pixels) to anything we want (within reason, of course :-).
87 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
89 drawing->gridSpacing = 12.0 / Global::zoom;
91 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
92 translated to Cartesian coordinates through this. (Initially, Global::zoom is
93 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
95 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
96 convenience function than any measure of absolutes. Doing things that way we
97 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
98 shittiness that comes with it.
100 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
101 a certain way, which means we should probably create something else in those
102 objects to take its place--like some kind of scale factor. This would seem to
103 imply that certain point sizes actually *do* tie things like fonts to absolute
104 sizes on the screen, but not necessarily because you could have an inch scale
105 with text that is quite small relative to other objects on the screen, which
106 currently you have to zoom in to see (and which blows up the text). Point sizes
107 in an application like this are a bit meaningless; even though an inch is an
108 inch regardless of the zoom level a piece of text can be larger or smaller than
109 this. Maybe this is the case for having a base unit and basing point sizes off
112 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
113 base units. What that means is that if you have a 12px grid with a 6" grid size
114 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
116 Dimensions now have a "size" parameter to set their absolute size in relation
117 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
118 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
119 scaled the same way as the arrowheads.
121 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
122 need a thickness parameter similar to the "size" param for dimensions. (And now
127 void DrawingView::DrawBackground(Painter * painter)
129 Point ul = Painter::QtToCartesianCoords(Vector(0, 0));
130 Point br = Painter::QtToCartesianCoords(Vector(Global::screenSize.x, Global::screenSize.y));
132 painter->SetBrush(0xF0F0F0);
133 painter->SetPen(0xF0F0F0, 1, 1);
134 painter->DrawRect(QRectF(QPointF(ul.x, ul.y), QPointF(br.x, br.y)));
136 double spacing = Global::gridSpacing;
141 double leftx = floor(ul.x / spacing) * spacing;
142 double bottomy = floor(br.y / spacing) * spacing;
144 double w = (br.x - ul.x) + Global::gridSpacing + 1.0;
145 double h = (ul.y - br.y) + Global::gridSpacing + 1.0;
147 Vector start(leftx, bottomy), size(w, h);
149 if (Global::gridSpacing <= 0.015625)
150 DrawSubGrid(painter, 0xFFD2D2, 0.015625, start, size);
152 if (Global::gridSpacing <= 0.03125)
153 DrawSubGrid(painter, 0xFFD2D2, 0.03125, start, size);
155 if (Global::gridSpacing <= 0.0625)
156 DrawSubGrid(painter, 0xB8ECFF, 0.0625, start, size);
158 if (Global::gridSpacing <= 0.125)
159 DrawSubGrid(painter, 0xB8ECFF, 0.125, start, size);
161 if (Global::gridSpacing <= 0.25)
162 DrawSubGrid(painter, 0xE6E6FF, 0.25, start, size);
164 if (Global::gridSpacing <= 0.5)
165 DrawSubGrid(painter, 0xE6E6FF, 0.5, start, size);
167 painter->SetPen(QPen(QColor(0xE0, 0xE0, 0xFF), 2.0, Qt::SolidLine));
169 for(double i=0; i<=w; i+=spacing)
170 painter->DrawVLine(leftx + i);
172 for(double i=0; i<=h; i+=spacing)
173 painter->DrawHLine(bottomy + i);
176 void DrawingView::DrawSubGrid(Painter * painter, uint32_t color, double step, Vector start, Vector size)
178 painter->SetPen(color, 1, 1);
180 for(double i=-step; i<=size.x; i+=step*2.0)
181 painter->DrawVLine(start.x + i);
183 for(double i=-step; i<=size.y; i+=step*2.0)
184 painter->DrawHLine(start.y + i);
188 // Basically, we just make a single pass through the Container. If the layer #
189 // is less than the layer # being deleted, then do nothing. If the layer # is
190 // equal to the layer # being deleted, then delete the object. If the layer #
191 // is greater than the layer # being deleted, then set the layer # to its layer
194 void DrawingView::DeleteCurrentLayer(int layer)
196 VPVectorIter i = document.objects.begin();
198 while (i != document.objects.end())
200 Object * obj = (Object *)(*i);
202 if (obj->layer < layer)
204 else if (obj->layer == layer)
206 document.objects.erase(i);
216 // We've just done a destructive action, so update the screen!
220 void DrawingView::HandleLayerToggle(void)
222 // A layer's visibility was toggled, so update the screen...
227 // A layer was moved up or down in the layer list, so we have to swap the
228 // document's object's layer numbers in the layers that were swapped.
230 void DrawingView::HandleLayerSwap(int layer1, int layer2)
232 HandleLayerSwap(layer1, layer2, document.objects);
236 We can roll this into the main one above, by having the LayerWidget's emit() call sending NULL for the VPVector, which we can test for and set to document.objects to grab the top layer. Or, keep it a top level call and a recursive call. Which is worse? :-P
238 void DrawingView::HandleLayerSwap(int layer1, int layer2, VPVector & v)
240 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
242 Object * obj = (Object *)(*i);
244 if (obj->layer == layer1)
246 else if (obj->layer == layer2)
249 if (obj->type == OTContainer)
250 HandleLayerSwap(layer1, layer2, ((Container *)obj)->objects);
254 void DrawingView::HandlePenStamp(QAction * action)
256 PenWidget * pw = (PenWidget *)action->parentWidget();
257 pw->dropperAction->setChecked(false);
258 Global::penDropper = false;
259 Global::penStamp = action->isChecked();
261 if (Global::penStamp)
262 setCursor(curMarker);
264 setCursor(Qt::ArrowCursor);
266 if (Global::penStamp == false)
267 ClearSelected(document.objects);
272 void DrawingView::HandlePenDropper(QAction * action)
274 PenWidget * pw = (PenWidget *)action->parentWidget();
275 pw->stampAction->setChecked(false);
276 Global::penStamp = false;
277 Global::penDropper = action->isChecked();
279 if (Global::penDropper)
280 setCursor(curDropper);
282 setCursor(Qt::ArrowCursor);
287 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
289 // This is undoing the transform, e.g. going from client coords to local
290 // coords. In essence, the height - y is height + (y * -1), the (y * -1)
291 // term doing the conversion of the y-axis from increasing bottom to top.
292 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
295 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
297 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
298 // No voodoo here, it's just grouped wrong to see it. It should be:
299 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
300 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
303 void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
305 // Make sure all modkeys being held are marked as released when the app
306 // loses focus (N.B.: This only works because the app sets the focus policy
307 // of this object to something other than Qt::NoFocus)
308 shiftDown = ctrlDown = altDown = false;
310 setCursor(Qt::ArrowCursor);
313 void DrawingView::focusInEvent(QFocusEvent * /*event*/)
315 if (Global::penStamp)
316 setCursor(curMarker);
317 else if (Global::penDropper)
318 setCursor(curDropper);
321 void DrawingView::paintEvent(QPaintEvent * /*event*/)
323 QPainter qtPainter(this);
324 Painter painter(&qtPainter);
327 qtPainter.setRenderHint(QPainter::Antialiasing);
329 Global::viewportHeight = size().height();
331 DrawBackground(&painter);
333 // Draw coordinate axes
334 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
335 painter.DrawLine(0, -16384, 0, 16384);
336 painter.DrawLine(-16384, 0, 16384, 0);
338 // Do object rendering...
339 for(int i=0; i<Global::numLayers; i++)
341 if (Global::layerHidden[i] == false)
342 RenderObjects(&painter, document.objects, i);
345 // Do tool rendering, if any...
348 if (Global::toolSuppressCrosshair == false)
350 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
351 painter.DrawCrosshair(oldPoint);
357 // Do selection rectangle rendering, if any
358 if (Global::selectionInProgress)
360 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
361 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
362 painter.DrawRect(Global::selection);
365 if (hoveringIntersection)
366 painter.DrawHandle(intersectionPoint);
369 painter.DrawHandle(hoverPoint);
371 if (!informativeText.isEmpty())
372 painter.DrawInformativeText(informativeText);
376 // Renders objects in the passed in vector
379 N.B.: Since we have "hoverPointValid" drawing regular object handles above,
380 we can probably do away with a lot of them that are being done down below.
382 [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...]
384 void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool ignoreLayer/*= false*/)
386 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
388 Object * obj = (Object *)(*i);
389 float scaledThickness = Global::scale * obj->thickness;
391 // If the object isn't on the current layer being drawn, skip it
392 if (!ignoreLayer && (obj->layer != layer))
395 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
397 painter->SetPen(0x00FF00, 2.0, LSSolid);
401 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
402 painter->SetBrush(obj->color);
404 // penStamp supresses object highlighting, so that changes can be seen.
405 if (supressSelected || Global::penStamp)
409 painter->SetPen(Global::penColor, Global::zoom * Global::scale * Global::penWidth, Global::penStyle);
410 painter->SetBrush(Global::penColor);
413 else if (obj->selected || obj->hitObject)
414 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
420 painter->DrawLine(obj->p[0], obj->p[1]);
422 if (obj->hitPoint[0])
423 painter->DrawHandle(obj->p[0]);
425 if (obj->hitPoint[1])
426 painter->DrawHandle(obj->p[1]);
429 painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
434 painter->SetBrush(QBrush(Qt::NoBrush));
435 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
437 if (obj->hitPoint[0])
438 painter->DrawHandle(obj->p[0]);
443 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
445 if (obj->hitPoint[0])
446 painter->DrawHandle(obj->p[0]);
448 if (obj->hitPoint[1])
449 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
451 if (obj->hitPoint[2])
452 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
458 Dimension * d = (Dimension *)obj;
460 Vector v(d->p[0], d->p[1]);
461 double angle = v.Angle();
462 Vector unit = v.Unit();
463 d->lp[0] = d->p[0], d->lp[1] = d->p[1];
465 double x1, y1, length;
467 if (d->subtype == DTLinearVert)
469 if ((angle < 0) || (angle > HALF_TAU))
471 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
472 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
473 ortho = Vector(1.0, 0);
474 angle = THREE_QTR_TAU;
478 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
479 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
480 ortho = Vector(-1.0, 0);
484 d->lp[0].x = d->lp[1].x = x1;
485 length = fabs(d->p[0].y - d->p[1].y);
487 else if (d->subtype == DTLinearHorz)
489 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
491 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
492 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
493 ortho = Vector(0, 1.0);
498 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
499 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
500 ortho = Vector(0, -1.0);
504 d->lp[0].y = d->lp[1].y = y1;
505 length = fabs(d->p[0].x - d->p[1].x);
507 else if (d->subtype == DTLinear)
509 angle = Vector(d->lp[0], d->lp[1]).Angle();
510 ortho = Vector::Normal(d->lp[0], d->lp[1]);
511 length = v.Magnitude();
514 unit = Vector(d->lp[0], d->lp[1]).Unit();
516 Point p1 = d->lp[0] + (ortho * (d->offset + (10.0 * scaledThickness)));
517 Point p2 = d->lp[1] + (ortho * (d->offset + (10.0 * scaledThickness)));
518 Point p3 = d->lp[0] + (ortho * (d->offset + (16.0 * scaledThickness)));
519 Point p4 = d->lp[1] + (ortho * (d->offset + (16.0 * scaledThickness)));
520 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
521 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
524 The numbers hardcoded into here, what are they?
525 I believe they are pixels.
527 // Draw extension lines (if certain type)
528 painter->DrawLine(p3, p5);
529 painter->DrawLine(p4, p6);
531 // Calculate whether or not the arrowheads are too crowded to put
532 // inside the extension lines. 9.0 is the length of the arrowhead.
533 double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
535 // On the screen, it's acting like this is actually 58%...
536 // This is correct, we want it to happen at > 50%
539 // Draw main dimension line + arrowheads
540 painter->DrawLine(p1, p2);
541 painter->DrawArrowhead(p1, p2, scaledThickness);
542 painter->DrawArrowhead(p2, p1, scaledThickness);
546 // Draw outside arrowheads
547 Point p7 = p1 - (unit * 9.0 * scaledThickness);
548 Point p8 = p2 + (unit * 9.0 * scaledThickness);
549 painter->DrawArrowhead(p1, p7, scaledThickness);
550 painter->DrawArrowhead(p2, p8, scaledThickness);
551 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
552 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
555 // Draw length of dimension line...
556 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
557 Point ctr = p2 + (Vector(p2, p1) / 2.0);
562 dimText = QString("%1\"").arg(length);
565 double feet = (double)((int)length / 12);
566 double inches = length - (feet * 12.0);
569 dimText = QString("%1'").arg(feet);
571 dimText = QString("%1' %2\"").arg(feet).arg(inches);
575 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().
577 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
581 Point hp1 = (p1 + p2) / 2.0;
582 Point hp2 = (p1 + hp1) / 2.0;
583 Point hp3 = (hp1 + p2) / 2.0;
587 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
588 painter->SetBrush(QBrush(QColor(Qt::magenta)));
589 painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
590 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
593 painter->DrawHandle(hp1);
594 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
598 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
599 painter->SetBrush(QBrush(QColor(Qt::magenta)));
600 painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
601 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
604 painter->DrawHandle(hp2);
605 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
609 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
610 painter->SetBrush(QBrush(QColor(Qt::magenta)));
611 painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
612 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
615 painter->DrawHandle(hp3);
618 if (obj->hitPoint[0])
619 painter->DrawHandle(obj->p[0]);
621 if (obj->hitPoint[1])
622 painter->DrawHandle(obj->p[1]);
629 Text * t = (Text *)obj;
631 if (t->measured == false)
633 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
637 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
658 // Containers require recursive rendering...
659 Container * c = (Container *)obj;
660 //printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
661 RenderObjects(painter, (*c).objects, layer, ignoreLayer);
663 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
664 // Containers also have special indicators showing they are selected
665 if (c->selected || c->hitObject)
667 // Rect r = GetObjectExtents(obj);
668 // painter->DrawRectCorners(r);
669 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
680 supressSelected = false;
684 // This toggles the selection being hovered (typically, only 1 object). We
685 // toggle because the CTRL key might be held, in which case, we want to
686 // deselect a selected object.
688 void DrawingView::HandleSelectionClick(VPVector & v)
692 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
694 Object * obj = (Object *)(*i);
697 obj->selected = !obj->selected;
703 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
704 ((Object *)(*i))->selected = false;
706 // Check if the hover changed, and if so, reset the selection stack
707 if (oldHover.size() != v.size())
714 // Select next object in the stack under the cursor
717 if (currentSelect >= v.size())
721 dragged = (Object *)v[currentSelect];
722 dragged->selected = true;
725 VPVector DrawingView::GetSelection(void)
729 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
731 if (((Object *)(*i))->selected)
739 // When testing for hovered intersections, we need to be able to exclude some
740 // objects which have funky characteristics or handles; so we allow for that
743 VPVector DrawingView::GetHovered(bool exclude/*= false*/)
747 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
749 Object * obj = (Object *)(*i);
754 && ((obj->type == OTDimension)
755 || ((obj->type == OTCircle) && (obj->hitPoint[0] == true))
756 || ((obj->type == OTArc) && (obj->hitPoint[0] == true))
757 || (draggingObject && (obj == dragged))))
767 void DrawingView::MoveSelectedToLayer(int layer)
769 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
771 Object * obj = (Object *)(*i);
773 if (obj->selected || obj->hovered)
778 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
780 Global::screenSize = Vector(size().width(), size().height());
783 void DrawingView::ToolHandler(int mode, Point p)
785 // Drop angle snap until it's needed
788 if (Global::tool == TTLine)
789 LineHandler(mode, p);
790 else if (Global::tool == TTCircle)
791 CircleHandler(mode, p);
792 else if (Global::tool == TTArc)
794 else if (Global::tool == TTRotate)
795 RotateHandler(mode, p);
796 else if (Global::tool == TTMirror)
797 MirrorHandler(mode, p);
798 else if (Global::tool == TTDimension)
799 DimensionHandler(mode, p);
800 else if (Global::tool == TTDelete)
801 DeleteHandler(mode, p);
802 else if (Global::tool == TTTriangulate)
803 TriangulateHandler(mode, p);
804 else if (Global::tool == TTTrim)
805 TrimHandler(mode, p);
806 else if (Global::tool == TTParallel)
807 ParallelHandler(mode, p);
810 void DrawingView::ToolDraw(Painter * painter)
812 if (Global::tool == TTLine)
814 if (Global::toolState == TSNone)
816 painter->DrawHandle(toolPoint[0]);
818 else if ((Global::toolState == TSPoint2) && shiftDown)
820 painter->DrawHandle(toolPoint[1]);
824 painter->DrawLine(toolPoint[0], toolPoint[1]);
825 painter->DrawHandle(toolPoint[1]);
827 Vector v(toolPoint[0], toolPoint[1]);
828 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
829 double absLength = v.Magnitude();
830 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
831 informativeText = text.arg(absLength).arg(absAngle);
834 else if (Global::tool == TTCircle)
836 if (Global::toolState == TSNone)
838 painter->DrawHandle(toolPoint[0]);
840 else if ((Global::toolState == TSPoint2) && shiftDown)
842 painter->DrawHandle(toolPoint[1]);
846 painter->DrawCross(toolPoint[0]);
847 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
848 painter->SetBrush(QBrush(Qt::NoBrush));
849 painter->DrawEllipse(toolPoint[0], length, length);
850 QString text = tr("Radius: %1 in.");
851 informativeText = text.arg(length);
854 else if (Global::tool == TTArc)
856 if (Global::toolState == TSNone)
858 painter->DrawHandle(toolPoint[0]);
860 else if (Global::toolState == TSPoint2)
862 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
863 painter->SetBrush(QBrush(Qt::NoBrush));
864 painter->DrawEllipse(toolPoint[0], length, length);
865 painter->DrawLine(toolPoint[0], toolPoint[1]);
866 painter->DrawHandle(toolPoint[1]);
867 QString text = tr("Radius: %1 in.");
868 informativeText = text.arg(length);
870 else if (Global::toolState == TSPoint3)
872 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
873 painter->DrawLine(toolPoint[0], toolPoint[2]);
874 painter->SetBrush(QBrush(Qt::NoBrush));
875 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
876 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
877 QString text = tr("Angle start: %1") + QChar(0x00B0);
878 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
882 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
883 double span = angle - toolPoint[2].x;
888 painter->DrawLine(toolPoint[0], toolPoint[3]);
889 painter->SetBrush(QBrush(Qt::NoBrush));
890 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
891 painter->SetPen(0xFF00FF, 2.0, LSSolid);
892 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
893 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
894 QString text = tr("Arc span: %1") + QChar(0x00B0);
895 informativeText = text.arg(RADIANS_TO_DEGREES * span);
898 else if (Global::tool == TTRotate)
900 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
901 painter->DrawHandle(toolPoint[0]);
902 else if ((Global::toolState == TSPoint2) && shiftDown)
903 painter->DrawHandle(toolPoint[1]);
906 if (toolPoint[0] == toolPoint[1])
909 painter->DrawLine(toolPoint[0], toolPoint[1]);
911 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
912 QString text = QChar(0x2221) + QObject::tr(": %1");
913 informativeText = text.arg(absAngle);
916 informativeText += " (Copy)";
919 else if (Global::tool == TTMirror)
921 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
922 painter->DrawHandle(toolPoint[0]);
923 else if ((Global::toolState == TSPoint2) && shiftDown)
924 painter->DrawHandle(toolPoint[1]);
927 if (toolPoint[0] == toolPoint[1])
930 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
931 painter->DrawLine(mirrorPoint, toolPoint[1]);
933 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
935 if (absAngle > 180.0)
938 QString text = QChar(0x2221) + QObject::tr(": %1");
939 informativeText = text.arg(absAngle);
942 informativeText += " (Copy)";
945 else if (Global::tool == TTDimension)
947 if (Global::toolState == TSNone)
949 painter->DrawHandle(toolPoint[0]);
951 else if ((Global::toolState == TSPoint2) && shiftDown)
953 painter->DrawHandle(toolPoint[1]);
957 painter->DrawLine(toolPoint[0], toolPoint[1]);
958 painter->DrawHandle(toolPoint[1]);
960 Vector v(toolPoint[0], toolPoint[1]);
961 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
962 double absLength = v.Magnitude();
963 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
964 informativeText = text.arg(absLength).arg(absAngle);
967 else if (Global::tool == TTTrim)
969 if (toolObj[0] != NULL)
971 // We're assuming ATM it's just a line...
972 painter->SetPen(0xAF0000, 3.0, LSSolid);
973 painter->DrawLine(toolPoint[0], toolPoint[1]);
974 // QString text = tr("Arc span: %1") + QChar(0x00B0);
975 // informativeText = text.arg(RADIANS_TO_DEGREES * span);
978 else if (Global::tool == TTParallel)
983 void DrawingView::LineHandler(int mode, Point p)
988 /* toolObj[0] = NULL;
990 // Check to see if we can do a circle tangent snap
993 VPVector hover = GetHovered();
994 Object * obj = (Object *)hover[0];
996 // Save for later if the object clicked was a circle (need to check that it wasn't the center clicked on, because that will fuck up connecting centers of circles with lines... and now we do! :-)
997 if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
1001 if (Global::toolState == TSNone)
1009 if (Global::toolState == TSNone)
1014 /* bool isCircle = false;
1016 if (numHovered == 1)
1018 VPVector hover = GetHovered();
1019 Object * obj = (Object *)hover[0];
1021 if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
1028 // Adjust initial point if it's on a circle (tangent point)
1029 if (toolObj[0] != NULL)
1033 Geometry::FindTangents(toolObj[0], toolObj[1]);
1035 if (Global::numIntersectPoints > 0)
1037 toolPoint[0] = Global::intersectPoint[0];
1038 toolPoint[1] = Global::intersectPoint[1];
1043 Geometry::FindTangents(toolObj[0], p);
1045 if (Global::numIntersectPoints > 0)
1046 toolPoint[0] = Global::intersectPoint[0];
1053 Geometry::FindTangents(toolObj[1], toolPoint[0]);
1055 if (Global::numIntersectPoints > 0)
1056 toolPoint[1] = Global::intersectPoint[0];
1064 if (Global::toolState == TSNone)
1066 Global::toolState = TSPoint2;
1067 // Prevent spurious line from drawing...
1068 toolPoint[1] = toolPoint[0];
1070 else if ((Global::toolState == TSPoint2) && shiftDown)
1072 // Key override is telling us to make a new line, not continue the
1074 toolPoint[0] = toolPoint[1];
1078 Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
1079 l->layer = Global::activeLayer;
1080 document.objects.push_back(l);
1081 toolPoint[0] = toolPoint[1];
1086 void DrawingView::CircleHandler(int mode, Point p)
1091 if (Global::toolState == TSNone)
1099 if (Global::toolState == TSNone)
1107 if (Global::toolState == TSNone)
1109 Global::toolState = TSPoint2;
1110 // Prevent spurious line from drawing...
1111 toolPoint[1] = toolPoint[0];
1113 else if ((Global::toolState == TSPoint2) && shiftDown)
1115 // Key override is telling us to make a new line, not continue the
1117 toolPoint[0] = toolPoint[1];
1121 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1122 Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1123 c->layer = Global::activeLayer;
1124 document.objects.push_back(c);
1125 toolPoint[0] = toolPoint[1];
1126 Global::toolState = TSNone;
1131 void DrawingView::ArcHandler(int mode, Point p)
1136 if (Global::toolState == TSNone)
1138 else if (Global::toolState == TSPoint2)
1140 else if (Global::toolState == TSPoint3)
1148 if (Global::toolState == TSNone)
1150 else if (Global::toolState == TSPoint2)
1152 else if (Global::toolState == TSPoint3)
1166 if (Global::toolState == TSNone)
1168 // Prevent spurious line from drawing...
1169 toolPoint[1] = toolPoint[0];
1170 Global::toolState = TSPoint2;
1172 else if (Global::toolState == TSPoint2)
1176 // Key override is telling us to start arc at new center, not
1177 // continue the current one.
1178 toolPoint[0] = toolPoint[1];
1182 // Set the radius in toolPoint[1].x
1183 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1184 Global::toolState = TSPoint3;
1186 else if (Global::toolState == TSPoint3)
1188 // Set the angle in toolPoint[2].x
1189 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1190 Global::toolState = TSPoint4;
1194 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1195 double span = endAngle - toolPoint[2].x;
1200 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1201 arc->layer = Global::activeLayer;
1202 document.objects.push_back(arc);
1203 Global::toolState = TSNone;
1208 void DrawingView::RotateHandler(int mode, Point p)
1213 if (Global::toolState == TSNone)
1216 // SavePointsFrom(select, toolScratch);
1217 CopyObjects(select, toolScratch2);
1218 Global::toolState = TSPoint1;
1220 else if (Global::toolState == TSPoint1)
1228 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1230 else if (Global::toolState == TSPoint2)
1238 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1239 VPVectorIter j = select.begin();
1240 // std::vector<Object>::iterator i = toolScratch.begin();
1241 VPVectorIter i = toolScratch2.begin();
1243 // for(; i!=toolScratch.end(); i++, j++)
1244 for(; i!=toolScratch2.end(); i++, j++)
1246 // Object objT = *i;
1247 // Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1248 // Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1249 Object * objT = (Object *)(*i);
1250 Object * objS = (Object *)(*j);
1252 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1253 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1258 // if (objT.type == OTArc || objT.type == OTText)
1259 if (objT->type == OTArc || objT->type == OTText)
1261 // objS->angle[0] = objT.angle[0] + angle;
1262 objS->angle[0] = objT->angle[0] + angle;
1264 if (objS->angle[0] > TAU)
1265 objS->angle[0] -= TAU;
1267 // else if (objT.type == OTContainer)
1268 else if (objT->type == OTContainer)
1270 // 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...]
1271 // Container * c = (Container *)&objT;
1272 Container * c = (Container *)objT;
1273 Container * c2 = (Container *)objS;
1274 VPVectorIter l = c->objects.begin();
1275 // TODO: Rotate items in the container
1276 // TODO: Make this recursive
1277 for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1279 Object * obj3 = (Object *)(*k);
1280 Object * obj4 = (Object *)(*l);
1282 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1283 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1287 // obj3->angle[0] = objT.angle[0] + angle;
1288 obj3->angle[0] = obj4->angle[0] + angle;
1290 if (obj3->angle[0] > TAU)
1291 obj3->angle[0] -= TAU;
1294 Rect r = GetObjectExtents(objS);
1295 c2->p[0] = r.TopLeft();
1296 c2->p[1] = r.BottomRight();
1304 if (Global::toolState == TSPoint1)
1306 Global::toolState = TSPoint2;
1307 // Prevent spurious line from drawing...
1308 toolPoint[1] = toolPoint[0];
1310 else if ((Global::toolState == TSPoint2) && shiftDown)
1312 // Key override is telling us to make a new line, not continue the
1314 toolPoint[0] = toolPoint[1];
1318 // Either we're finished with our rotate, or we're stamping a copy.
1321 // Stamp a copy of the selection at the current rotation & bail
1323 CopyObjects(select, temp);
1324 ClearSelected(temp);
1325 AddObjectsTo(document.objects, temp);
1326 // RestorePointsTo(select, toolScratch);
1327 RestorePointsTo(select, toolScratch2);
1332 Global::toolState = TSPoint1;
1333 // SavePointsFrom(select, toolScratch);
1334 DeleteContents(toolScratch2);
1335 CopyObjects(select, toolScratch2);
1341 // Reset the selection if shift held down...
1343 // RestorePointsTo(select, toolScratch);
1344 RestorePointsTo(select, toolScratch2);
1349 // Reset selection when key is let up
1351 RotateHandler(ToolMouseMove, toolPoint[1]);
1356 // RestorePointsTo(select, toolScratch);
1357 RestorePointsTo(select, toolScratch2);
1358 DeleteContents(toolScratch2);
1362 void DrawingView::MirrorHandler(int mode, Point p)
1367 if (Global::toolState == TSNone)
1370 SavePointsFrom(select, toolScratch);
1371 Global::toolState = TSPoint1;
1373 else if (Global::toolState == TSPoint1)
1381 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1383 else if (Global::toolState == TSPoint2)
1391 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1392 VPVectorIter j = select.begin();
1393 std::vector<Object>::iterator i = toolScratch.begin();
1395 for(; i!=toolScratch.end(); i++, j++)
1398 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1399 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1400 Object * obj2 = (Object *)(*j);
1405 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1406 a negative start angle which makes it impossible to interact with.
1409 if (obj.type == OTArc)
1411 // This is 2*mirror angle - obj angle - obj span
1412 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1414 if (obj2->angle[0] > TAU)
1415 obj2->angle[0] -= TAU;
1423 if (Global::toolState == TSPoint1)
1425 Global::toolState = TSPoint2;
1426 // Prevent spurious line from drawing...
1427 toolPoint[1] = toolPoint[0];
1429 else if ((Global::toolState == TSPoint2) && shiftDown)
1431 // Key override is telling us to make a new line, not continue the
1433 toolPoint[0] = toolPoint[1];
1437 // Either we're finished with our rotate, or we're stamping a copy.
1440 // Stamp a copy of the selection at the current rotation & bail
1442 CopyObjects(select, temp);
1443 ClearSelected(temp);
1444 AddObjectsTo(document.objects, temp);
1445 RestorePointsTo(select, toolScratch);
1450 Global::toolState = TSPoint1;
1451 SavePointsFrom(select, toolScratch);
1457 // Reset the selection if shift held down...
1459 RestorePointsTo(select, toolScratch);
1464 // Reset selection when key is let up
1466 MirrorHandler(ToolMouseMove, toolPoint[1]);
1471 RestorePointsTo(select, toolScratch);
1475 void DrawingView::DimensionHandler(int mode, Point p)
1480 if (Global::toolState == TSNone)
1488 if (Global::toolState == TSNone)
1496 if (Global::toolState == TSNone)
1498 Global::toolState = TSPoint2;
1499 // Prevent spurious line from drawing...
1500 toolPoint[1] = toolPoint[0];
1502 else if ((Global::toolState == TSPoint2) && shiftDown)
1504 // Key override is telling us to make a new line, not continue the
1506 toolPoint[0] = toolPoint[1];
1510 Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear, 0, Global::penWidth);
1511 d->layer = Global::activeLayer;
1512 document.objects.push_back(d);
1513 Global::toolState = TSNone;
1518 void DrawingView::DeleteHandler(int mode, Point /*p*/)
1524 VPVector hovered = GetHovered();
1526 RemoveHoveredObjects(document.objects);
1527 DeleteContents(hovered);
1548 void DrawingView::TriangulateHandler(int mode, Point /*p*/)
1554 // Skip if nothing hovered...
1555 if (numHovered != 1)
1558 VPVector hover = GetHovered();
1559 Object * obj = (Object *)hover[0];
1561 // Skip if it's not a line...
1562 if (obj->type != OTLine)
1565 if (Global::toolState == TSNone)
1567 else if (Global::toolState == TSPoint2)
1576 if (Global::toolState == TSNone)
1578 else if (Global::toolState == TSPoint2)
1580 else if (Global::toolState == TSPoint3)
1594 if (Global::toolState == TSNone)
1596 Global::toolState = TSPoint2;
1598 else if (Global::toolState == TSPoint2)
1602 // Key override is telling us to start arc at new center, not
1603 // continue the current one.
1604 toolPoint[0] = toolPoint[1];
1608 Global::toolState = TSPoint3;
1612 double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1613 double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1615 Circle c1(toolObj[0]->p[0], len2);
1616 Circle c2(toolObj[0]->p[1], len3);
1618 Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1620 // Only move lines if the triangle formed by them is not degenerate
1621 if (Global::numIntersectPoints > 0)
1623 toolObj[1]->p[0] = toolObj[0]->p[0];
1624 toolObj[1]->p[1] = Global::intersectPoint[0];
1626 toolObj[2]->p[0] = Global::intersectPoint[0];
1627 toolObj[2]->p[1] = toolObj[0]->p[1];
1630 Global::toolState = TSNone;
1635 void DrawingView::TrimHandler(int mode, Point p)
1646 // Bail out if nothing hovered...
1647 if (numHovered != 1)
1653 VPVector hover = GetHovered();
1654 Object * obj = (Object *)hover[0];
1656 // Skip if it's not a line...
1657 if (obj->type != OTLine)
1664 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1665 double t = 0, u = 1.0;
1667 // Currently only deal with line against line trimming, can expand to
1668 // others as well (line/circle, circle/circle, line/arc, etc)
1670 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1672 obj = (Object *)(*i);
1674 if (obj == toolObj[0])
1676 else if (obj->type != OTLine)
1679 Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1681 if (Global::numIntersectParams > 0)
1683 // Skip endpoint-endpoint intersections
1684 if ((Global::numIntersectParams == 2)
1685 && (Global::intersectParam[0] == 0
1686 || Global::intersectParam[0] == 1.0)
1687 && (Global::intersectParam[1] == 0
1688 || Global::intersectParam[1] == 1.0))
1691 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1692 if ((Global::intersectParam[0] > t) && (Global::intersectParam[0] < hoveredParam))
1693 t = Global::intersectParam[0];
1695 if ((Global::intersectParam[0] < u) && (Global::intersectParam[0] > hoveredParam))
1696 u = Global::intersectParam[0];
1702 toolPoint[0] = Geometry::GetPointForParameter(toolObj[0], t);
1703 toolPoint[1] = Geometry::GetPointForParameter(toolObj[0], u);
1709 // Bail out if there's no object to trim
1710 if (toolObj[0] == NULL)
1713 Vector v(toolObj[0]->p[0], toolObj[0]->p[1]);
1715 // Check to see which case we have.
1716 if ((toolParam[0] == 0) && (toolParam[1] == 1.0))
1718 // There was no intersection, so delete the object
1719 toolObj[0]->selected = true;
1720 DeleteSelectedObjects(document.objects);
1722 else if (toolParam[0] == 0)
1724 // We delete the end near point #1
1725 toolObj[0]->p[0] = toolObj[0]->p[0] + (v * toolParam[1]);
1727 else if (toolParam[1] == 1.0)
1729 // We delete the end near point #2
1730 toolObj[0]->p[1] = toolObj[0]->p[0] + (v * toolParam[0]);
1734 // We delete the segment in between, and create a new line in the process
1735 Point p1 = toolObj[0]->p[0] + (v * toolParam[0]);
1736 Point p2 = toolObj[0]->p[0] + (v * toolParam[1]);
1737 Point p3 = toolObj[0]->p[1];
1738 toolObj[0]->p[1] = p1;
1739 Line * l = new Line(p2, p3, toolObj[0]->thickness, toolObj[0]->color, toolObj[0]->style);
1740 document.objects.push_back(l);
1741 // Global::toolState = TSNone;
1744 toolObj[0]->hitObject = toolObj[0]->hitPoint[0] = toolObj[0]->hitPoint[1] = false;
1760 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1784 void DrawingView::mousePressEvent(QMouseEvent * event)
1786 if (event->button() == Qt::LeftButton)
1788 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1790 // Handle tool processing, if any
1793 if (hoveringIntersection)
1794 point = intersectionPoint;
1795 else if (hoverPointValid)
1797 else if (Global::snapToGrid)
1798 point = SnapPointToGrid(point);
1800 ToolHandler(ToolMouseDown, point);
1804 // Clear the selection only if CTRL isn't being held on click
1806 ClearSelected(document.objects);
1808 // If any objects are being hovered on click, deal with 'em
1811 VPVector hover2 = GetHovered();
1812 dragged = (Object *)hover2[0];
1814 // Alert the pen widget
1815 if (Global::penDropper)
1817 Global::penColor = dragged->color;
1818 Global::penWidth = dragged->thickness;
1819 Global::penStyle = dragged->style;
1820 emit ObjectSelected(dragged);
1823 else if (Global::penStamp)
1825 dragged->color = Global::penColor;
1826 dragged->thickness = Global::penWidth;
1827 dragged->style = Global::penStyle;
1830 // See if anything is using just a straight click on a custom
1831 // object handle (like Dimension objects)
1832 else if (HandleObjectClicked())
1838 draggingObject = true;
1839 HandleSelectionClick(hover2);
1840 update(); // needed??
1842 // Needed for grab & moving objects
1843 // We do it *after*... why? (doesn't seem to confer any advantage...)
1844 if (hoveringIntersection)
1845 oldPoint = intersectionPoint;
1846 else if (hoverPointValid)
1847 oldPoint = hoverPoint;
1848 else if (Global::snapToGrid)
1849 oldPoint = SnapPointToGrid(point);
1851 // Needed for fixed length handling
1852 if (Global::fixedLength)
1854 if (dragged->type == OTLine)
1856 dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1860 if (dragged->type == OTCircle)
1862 // Save for informative text, uh, er, informing
1863 dragged->length = dragged->radius[0];
1869 // Didn't hit any object and not using a tool, so do a selection
1871 Global::selectionInProgress = true;
1872 Global::selection.setTopLeft(QPointF(point.x, point.y));
1873 Global::selection.setBottomRight(QPointF(point.x, point.y));
1874 select = GetSelection();
1876 else if (event->button() == Qt::MiddleButton)
1879 oldPoint = Vector(event->x(), event->y());
1880 // Should also change the mouse pointer as well...
1881 setCursor(Qt::SizeAllCursor);
1885 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1887 // It seems that wheelEvent() triggers this for some reason...
1888 if (scrollWheelSeen)
1890 scrollWheelSeen = false;
1894 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1895 Global::selection.setBottomRight(QPointF(point.x, point.y));
1896 // Only needs to be done here, as mouse down is always preceded by movement
1897 Global::snapPointIsValid = false;
1898 hoveringIntersection = false;
1899 oldScrollPoint = Vector(event->x(), event->y());
1902 if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1904 point = Vector(event->x(), event->y());
1905 // Since we're using Qt coords for scrolling, we have to adjust them
1906 // here to conform to Cartesian coords, since the origin is using
1908 Vector delta(oldPoint, point);
1909 delta /= Global::zoom;
1911 Global::origin -= delta;
1913 // UpdateGridBackground();
1919 // If we're doing a selection rect, see if any objects are engulfed by it
1920 // (implies left mouse button held down)
1921 if (Global::selectionInProgress)
1923 CheckObjectBounds();
1925 // Make sure previously selected objects stay selected (CTRL held)
1926 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
1928 // Make sure *not* to select items on hidden layers
1929 if (Global::layerHidden[((Object *)(*i))->layer] == false)
1930 ((Object *)(*i))->selected = true;
1937 // Do object hit testing...
1938 bool needUpdate = HitTestObjects(point);
1939 VPVector hover2 = GetHovered(true); // Exclude dimension objects and circle centers (probably need to add arc centers too) from hover (also dragged objects...)
1944 printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1946 if (hover2.size() > 0)
1947 printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1952 // Check for multi-hover...
1953 if (hover2.size() > 1)
1955 //need to check for case where hover is over 2 circles and a 3rd's center (no longer a problem, I think)...
1956 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1958 Geometry::Intersects(obj1, obj2);
1959 int numIntersecting = Global::numIntersectParams;
1960 double t = Global::intersectParam[0];
1961 double u = Global::intersectParam[1];
1963 if (numIntersecting > 0)
1965 Vector v1 = Geometry::GetPointForParameter(obj1, t);
1966 Vector v2 = Geometry::GetPointForParameter(obj2, u);
1967 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1968 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1970 hoveringIntersection = true;
1971 intersectionPoint = v1;
1974 numIntersecting = Global::numIntersectPoints;
1976 if (numIntersecting > 0)
1978 Vector v1 = Global::intersectPoint[0];
1980 if (numIntersecting == 2)
1982 Vector v2 = Global::intersectPoint[1];
1984 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1988 QString text = tr("Intersection <%1, %2>");
1989 informativeText = text.arg(v1.x).arg(v1.y);
1990 hoveringIntersection = true;
1991 intersectionPoint = v1;
1994 else if (hover2.size() == 1)
1996 Object * obj = (Object *)hover2[0];
1998 if (obj->type == OTLine)
2001 Not sure that this is the best way to handle this, but it works(TM)...
2003 Point midpoint = Geometry::Midpoint((Line *)obj);
2004 Vector v1 = Vector::Magnitude(midpoint, point);
2006 if ((v1.Magnitude() * Global::zoom) < 8.0)
2008 QString text = tr("Midpoint <%1, %2>");
2009 informativeText = text.arg(midpoint.x).arg(midpoint.y);
2010 hoverPointValid = true;
2011 hoverPoint = midpoint;
2015 else if (obj->type == OTCircle)
2017 if ((draggingObject && (dragged->type == OTLine)) && (dragged->hitPoint[0] || dragged->hitPoint[1]))
2019 Point p = (dragged->hitPoint[0] ? dragged->p[1] : dragged->p[0]);
2020 Geometry::FindTangents(obj, p);
2022 if (Global::numIntersectPoints > 0)
2024 hoveringIntersection = true;
2025 intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
2028 else if ((Global::tool == TTLine) && (Global::toolState == TSPoint2))
2030 Geometry::FindTangents(obj, toolPoint[0]);
2032 if (Global::numIntersectPoints > 0)
2034 hoveringIntersection = true;
2035 intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
2041 // Handle object movement (left button down & over an object)
2042 if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
2044 if (hoveringIntersection)
2045 point = intersectionPoint;
2046 else if (hoverPointValid)
2048 else if (Global::snapToGrid)
2049 point = SnapPointToGrid(point);
2051 HandleObjectMovement(point);
2057 // Do tool handling, if any are active...
2060 if (hoveringIntersection)
2061 point = intersectionPoint;
2062 else if (hoverPointValid)
2064 else if (Global::snapToGrid)
2067 point = SnapPointToAngle(point);
2069 point = SnapPointToGrid(point);
2072 ToolHandler(ToolMouseMove, point);
2075 // This is used to draw the tool crosshair...
2078 if (needUpdate || Global::tool)
2082 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
2084 if (event->button() == Qt::LeftButton)
2086 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
2087 //could set it up to use the document's update function (assumes that all object
2088 //updates are being reported correctly:
2089 // if (document.NeedsUpdate())
2090 // Do an update if collided with at least *one* object in the document
2096 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
2097 ToolHandler(ToolMouseUp, point);
2101 Global::selectionInProgress = false;
2102 informativeText.clear();
2104 // Should we be doing this automagically? Hmm...
2105 // Clear our vectors
2109 // 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 [where to???])
2110 select = GetSelection();
2112 draggingObject = false;
2114 else if (event->button() == Qt::MiddleButton)
2118 if (Global::penStamp)
2119 setCursor(curMarker);
2120 else if (Global::penDropper)
2121 setCursor(curDropper);
2123 setCursor(Qt::ArrowCursor);
2125 // Need to convert this, since it's in Qt coordinates (for wheelEvent)
2126 oldPoint = Painter::QtToCartesianCoords(oldPoint);
2130 void DrawingView::wheelEvent(QWheelEvent * event)
2132 double zoomFactor = 1.20;
2133 scrollWheelSeen = true;
2135 if (event->angleDelta().y() < 0)
2137 if (Global::zoom > 400.0)
2140 Global::zoom *= zoomFactor;
2144 if (Global::zoom < 0.125)
2147 Global::zoom /= zoomFactor;
2150 Point np = Painter::QtToCartesianCoords(oldScrollPoint);
2151 Global::origin += (oldPoint - np);
2153 emit(NeedZoomUpdate());
2156 void DrawingView::keyPressEvent(QKeyEvent * event)
2158 bool oldShift = shiftDown;
2159 bool oldCtrl = ctrlDown;
2160 bool oldAlt = altDown;
2162 if (event->key() == Qt::Key_Shift)
2164 else if (event->key() == Qt::Key_Control)
2166 else if (event->key() == Qt::Key_Alt)
2169 // If there's a change in any of the modifier key states, pass it on to
2170 // the current tool's handler
2171 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2174 ToolHandler(ToolKeyDown, Point(0, 0));
2179 if (oldAlt != altDown)
2182 setCursor(Qt::SizeAllCursor);
2183 oldPoint = oldScrollPoint;
2186 if (select.size() > 0)
2188 if (event->key() == Qt::Key_Up)
2190 TranslateObjects(select, Point(0, +1.0));
2193 else if (event->key() == Qt::Key_Down)
2195 TranslateObjects(select, Point(0, -1.0));
2198 else if (event->key() == Qt::Key_Right)
2200 TranslateObjects(select, Point(+1.0, 0));
2203 else if (event->key() == Qt::Key_Left)
2205 TranslateObjects(select, Point(-1.0, 0));
2211 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2213 bool oldShift = shiftDown;
2214 bool oldCtrl = ctrlDown;
2215 bool oldAlt = altDown;
2217 if (event->key() == Qt::Key_Shift)
2219 else if (event->key() == Qt::Key_Control)
2221 else if (event->key() == Qt::Key_Alt)
2224 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2227 ToolHandler(ToolKeyUp, Point(0, 0));
2232 if (oldAlt != altDown)
2236 if (Global::penStamp)
2237 setCursor(curMarker);
2238 else if (Global::penDropper)
2239 setCursor(curDropper);
2241 setCursor(Qt::ArrowCursor);
2246 // This looks strange, but it's really quite simple: We want a point that's
2247 // more than half-way to the next grid point to snap there while conversely we
2248 // want a point that's less than half-way to to the next grid point then snap
2249 // to the one before it. So we add half of the grid spacing to the point, then
2250 // divide by it so that we can remove the fractional part, then multiply it
2251 // back to get back to the correct answer.
2253 Point DrawingView::SnapPointToGrid(Point point)
2255 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
2256 point /= Global::gridSpacing;
2257 point.x = floor(point.x);//need to fix this for negative numbers...
2258 point.y = floor(point.y);
2259 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
2260 point *= Global::gridSpacing;
2264 Point DrawingView::SnapPointToAngle(Point point)
2266 // Snap to a single digit angle (using toolpoint #1 as the center)
2267 double angle = Vector::Angle(toolPoint[0], point);
2268 double length = Vector::Magnitude(toolPoint[0], point);
2270 // Convert from radians to degrees
2271 double degAngle = angle * RADIANS_TO_DEGREES;
2272 double snapAngle = (double)((int)(degAngle + 0.5));
2275 v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2276 point = toolPoint[0] + v;
2281 Rect DrawingView::GetObjectExtents(Object * obj)
2283 // Default to empty rect, if object checks below fail for some reason
2291 rect = Rect(obj->p[0], obj->p[1]);
2297 rect = Rect(obj->p[0], obj->p[0]);
2298 rect.Expand(obj->radius[0]);
2304 Arc * a = (Arc *)obj;
2306 double start = a->angle[0];
2307 double end = start + a->angle[1];
2308 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2310 // If the end of the arc is before the beginning, add 360 degrees to it
2314 // Adjust the bounds depending on which axes are crossed
2315 if ((start < QTR_TAU) && (end > QTR_TAU))
2318 if ((start < HALF_TAU) && (end > HALF_TAU))
2321 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2324 if ((start < TAU) && (end > TAU))
2327 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2330 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2333 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2336 rect *= a->radius[0];
2337 rect.Translate(a->p[0]);
2343 Text * t = (Text *)obj;
2344 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2350 Container * c = (Container *)obj;
2351 VPVectorIter i = c->objects.begin();
2352 rect = GetObjectExtents((Object *)*i);
2355 for(; i!=c->objects.end(); i++)
2356 rect |= GetObjectExtents((Object *)*i);
2366 void DrawingView::CheckObjectBounds(void)
2370 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2372 Object * obj = (Object *)(*i);
2373 obj->selected = false;
2378 case OTDimension: // N.B.: We don't check this properly...
2380 Line * l = (Line *)obj;
2382 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2390 Circle * c = (Circle *)obj;
2392 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]))
2400 Arc * a = (Arc *)obj;
2402 double start = a->angle[0];
2403 double end = start + a->angle[1];
2404 QPointF p1(cos(start), sin(start));
2405 QPointF p2(cos(end), sin(end));
2406 QRectF bounds(p1, p2);
2409 // Swap X/Y coordinates if they're backwards...
2410 if (bounds.left() > bounds.right())
2412 double temp = bounds.left();
2413 bounds.setLeft(bounds.right());
2414 bounds.setRight(temp);
2417 if (bounds.bottom() > bounds.top())
2419 double temp = bounds.bottom();
2420 bounds.setBottom(bounds.top());
2421 bounds.setTop(temp);
2424 // Doesn't work as advertised! For shame!
2425 bounds = bounds.normalized();
2428 // If the end of the arc is before the beginning, add 360 degrees
2433 // Adjust the bounds depending on which axes are crossed
2434 if ((start < QTR_TAU) && (end > QTR_TAU))
2437 if ((start < HALF_TAU) && (end > HALF_TAU))
2438 bounds.setLeft(-1.0);
2440 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2441 bounds.setBottom(-1.0);
2443 if ((start < TAU) && (end > TAU))
2444 bounds.setRight(1.0);
2446 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2449 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2450 bounds.setLeft(-1.0);
2452 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2453 bounds.setBottom(-1.0);
2455 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2456 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2457 bounds.translate(a->p[0].x, a->p[0].y);
2459 if (Global::selection.contains(bounds))
2467 Text * t = (Text *)obj;
2468 Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2470 if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2478 Container * c = (Container *)obj;
2480 if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2492 bool DrawingView::HitTestObjects(Point point)
2496 bool needUpdate = false;
2497 hoverPointValid = false;
2499 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2501 Object * obj = (Object *)(*i);
2503 // If we're seeing the object we're dragging, skip it
2504 if (draggingObject && (obj == dragged))
2507 if (HitTest(obj, point) == true)
2513 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2514 emit ObjectHovered(obj);
2521 bool DrawingView::HitTest(Object * obj, Point point)
2523 bool needUpdate = false;
2529 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2530 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2531 Vector lineSegment = obj->p[1] - obj->p[0];
2532 Vector v1 = point - obj->p[0];
2533 Vector v2 = point - obj->p[1];
2534 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2538 distance = v1.Magnitude();
2540 distance = v2.Magnitude();
2542 // distance = ?Det?(ls, v1) / |ls|
2543 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2544 / lineSegment.Magnitude());
2546 if ((v1.Magnitude() * Global::zoom) < 8.0)
2548 obj->hitPoint[0] = true;
2549 hoverPoint = obj->p[0];
2550 hoverPointValid = true;
2552 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2554 obj->hitPoint[1] = true;
2555 hoverPoint = obj->p[1];
2556 hoverPointValid = true;
2558 else if ((distance * Global::zoom) < 5.0)
2559 obj->hitObject = true;
2561 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2563 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2571 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2572 obj->hitPoint[0] = obj->hitObject = false;
2573 double length = Vector::Magnitude(obj->p[0], point);
2575 if ((length * Global::zoom) < 8.0)
2577 obj->hitPoint[0] = true;
2578 hoverPoint = obj->p[0];
2579 hoverPointValid = true;
2581 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2582 obj->hitObject = true;
2584 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2586 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2594 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2595 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2596 double length = Vector::Magnitude(obj->p[0], point);
2597 double angle = Vector::Angle(obj->p[0], point);
2599 // Make sure we get the angle in the correct spot
2600 if (angle < obj->angle[0])
2603 // Get the span that we're pointing at...
2604 double span = angle - obj->angle[0];
2606 // N.B.: Still need to hit test the arc start & arc span handles...
2607 double spanAngle = obj->angle[0] + obj->angle[1];
2608 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2609 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2610 double length2 = Vector::Magnitude(point, handle1);
2611 double length3 = Vector::Magnitude(point, handle2);
2613 if ((length * Global::zoom) < 8.0)
2615 obj->hitPoint[0] = true;
2616 hoverPoint = obj->p[0];
2617 hoverPointValid = true;
2619 else if ((length2 * Global::zoom) < 8.0)
2621 obj->hitPoint[1] = true;
2622 hoverPoint = handle1;
2623 hoverPointValid = true;
2625 else if ((length3 * Global::zoom) < 8.0)
2627 obj->hitPoint[2] = true;
2628 hoverPoint = handle2;
2629 hoverPointValid = true;
2631 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2632 obj->hitObject = true;
2634 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2636 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2644 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2645 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2647 Dimension * d = (Dimension *)obj;
2649 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2650 // Get our line parallel to our points
2651 float scaledThickness = Global::scale * obj->thickness;
2653 Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2654 Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2656 Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2657 Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2659 Point p3(p1, point);
2661 Vector v1(d->p[0], point);
2662 Vector v2(d->p[1], point);
2663 Vector lineSegment(p1, p2);
2664 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2666 Point midpoint = (p1 + p2) / 2.0;
2667 Point hFSPoint = Point(midpoint, point);
2668 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2669 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2672 distance = v1.Magnitude();
2674 distance = v2.Magnitude();
2676 // distance = ?Det?(ls, v1) / |ls|
2677 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2678 / lineSegment.Magnitude());
2680 if ((v1.Magnitude() * Global::zoom) < 8.0)
2681 obj->hitPoint[0] = true;
2682 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2683 obj->hitPoint[1] = true;
2684 else if ((distance * Global::zoom) < 5.0)
2685 obj->hitObject = true;
2687 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2688 obj->hitPoint[2] = true;
2689 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2690 obj->hitPoint[3] = true;
2691 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2692 obj->hitPoint[4] = true;
2694 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2696 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2704 Text * t = (Text *)obj;
2705 bool oldHO = obj->hitObject;
2706 obj->hitObject = false;
2708 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2709 //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);
2711 if (r.Contains(point))
2712 obj->hitObject = true;
2714 obj->hovered = (obj->hitObject ? true : false);
2716 if (oldHO != obj->hitObject)
2724 // Containers must be recursively tested... Or do they???
2726 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.
2728 // bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2729 // Object * oldClicked = c->clicked;
2731 still need to compare old state to new state, and set things up based upon that...
2732 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);
2733 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.
2735 Container * c = (Container *)obj;
2736 c->hitObject = false;
2740 VPVector flat = Flatten(c);
2742 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2743 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
2745 Object * cObj = (Object *)(*i);
2747 // Skip the flattened containers (if any)...
2748 if (cObj->type == OTContainer)
2751 // We do it this way instead of needUpdate = HitTest() because we
2752 // are checking more than one object, and that way of doing will
2753 // not return consistent results.
2754 if (HitTest(cObj, point) == true)
2756 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2758 // c->hitObject = true;
2759 // c->clicked = cObj;
2760 // c->hovered = true;
2763 // Same reasons for doing it this way here apply.
2764 if (cObj->hitObject == true)
2766 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2767 c->hitObject = true;
2771 if (cObj->hitPoint[0] == true)
2773 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2774 c->hitPoint[0] = true;
2778 if (cObj->hitPoint[1] == true)
2780 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2781 c->hitPoint[1] = true;
2785 if (cObj->hitPoint[2] == true)
2787 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2788 c->hitPoint[2] = true;
2792 if (cObj->hovered == true)
2793 c->hovered = true;//*/
2806 bool DrawingView::HandleObjectClicked(void)
2808 if (dragged->type == OTDimension)
2810 Dimension * d = (Dimension *)dragged;
2814 // Hit the "flip sides" switch, so flip 'em
2815 Point temp = d->p[0];
2820 else if (d->hitPoint[3])
2822 // There are three cases here: aligned, horizontal, & vertical.
2823 // Aligned and horizontal do the same thing, vertical goes back to
2825 if (d->subtype == DTLinearVert)
2826 d->subtype = DTLinear;
2828 d->subtype = DTLinearVert;
2832 else if (d->hitPoint[4])
2834 // There are three cases here: aligned, horizontal, & vertical.
2835 // Aligned and vertical do the same thing, horizontal goes back to
2837 if (d->subtype == DTLinearHorz)
2838 d->subtype = DTLinear;
2840 d->subtype = DTLinearHorz;
2849 void DrawingView::HandleObjectMovement(Point point)
2851 Point delta = point - oldPoint;
2852 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2853 // Object * obj = (Object *)hover[0];
2854 Object * obj = dragged;
2855 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2856 //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"));
2861 if (obj->hitPoint[0])
2863 if (Global::fixedLength)
2865 Vector line = point - obj->p[1];
2866 Vector unit = line.Unit();
2867 point = obj->p[1] + (unit * obj->length);
2872 else if (obj->hitPoint[1])
2874 if (Global::fixedLength)
2876 Vector line = point - obj->p[0];
2877 Vector unit = line.Unit();
2878 point = obj->p[0] + (unit * obj->length);
2883 else if (obj->hitObject)
2892 if (obj->hitPoint[0])
2894 else if (obj->hitObject)
2896 double oldRadius = obj->length;
2897 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2899 QString text = QObject::tr("Radius: %1\nScale: %2%");
2900 informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2906 if (obj->hitPoint[0])
2908 else if (obj->hitPoint[1])
2910 // Change the Arc's span (handle #1)
2913 double angle = Vector::Angle(obj->p[0], point);
2914 double delta = angle - obj->angle[0];
2919 obj->angle[1] -= delta;
2920 obj->angle[0] = angle;
2922 if (obj->angle[1] < 0)
2923 obj->angle[1] += TAU;
2925 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2926 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);
2930 double angle = Vector::Angle(obj->p[0], point);
2931 obj->angle[0] = angle;
2932 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2933 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2935 else if (obj->hitPoint[2])
2937 // Change the Arc's span (handle #2)
2940 double angle = Vector::Angle(obj->p[0], point);
2941 obj->angle[1] = angle - obj->angle[0];
2943 if (obj->angle[1] < 0)
2944 obj->angle[1] += TAU;
2946 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2947 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);
2951 double angle = Vector::Angle(obj->p[0], point);
2952 obj->angle[0] = angle - obj->angle[1];
2954 if (obj->angle[0] < 0)
2955 obj->angle[0] += TAU;
2957 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2958 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2960 else if (obj->hitObject)
2967 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2968 QString text = QObject::tr("Radius: %1");
2969 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2975 if (obj->hitPoint[0])
2977 else if (obj->hitPoint[1])
2979 else if (obj->hitObject)
2981 // Move measurement lines in/out
2984 Dimension * d = (Dimension *)obj;
2985 double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
2986 float scaledThickness = Global::scale * obj->thickness;
2987 // Looks like offset is 0 to +MAX, but line is at 10.0. So
2988 // anything less than 10.0 should set the offset to 0.
2991 if (dist > (10.0 * scaledThickness))
2992 d->offset = dist - (10.0 * scaledThickness);
3010 // This is shitty, but works for now until I can code up something
3013 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.
3015 // TranslateObject(obj, delta);
3016 TranslateContainer((Container *)obj, point, delta);
3024 void DrawingView::AddDimensionTo(void * o)
3026 Object * obj = (Object *)o;
3031 document.Add(new Dimension(obj->p[0], obj->p[1]));