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 //printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
197 VPVectorIter i = document.objects.begin();
199 while (i != document.objects.end())
201 Object * obj = (Object *)(*i);
203 if (obj->layer < layer)
205 else if (obj->layer == layer)
207 document.objects.erase(i);
217 // We've just done a destructive action, so update the screen!
221 void DrawingView::HandleLayerToggle(void)
223 // A layer's visibility was toggled, so update the screen...
228 // A layer was moved up or down in the layer list, so we have to swap the
229 // document's object's layer numbers in the layers that were swapped.
231 void DrawingView::HandleLayerSwap(int layer1, int layer2)
233 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
234 HandleLayerSwap(layer1, layer2, document.objects);
238 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
240 void DrawingView::HandleLayerSwap(int layer1, int layer2, VPVector & v)
242 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
244 Object * obj = (Object *)(*i);
246 if (obj->layer == layer1)
248 else if (obj->layer == layer2)
251 if (obj->type == OTContainer)
252 HandleLayerSwap(layer1, layer2, ((Container *)obj)->objects);
256 void DrawingView::HandlePenWidth(float width)
258 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
260 Object * obj = (Object *)(*i);
261 obj->thickness = width;
264 supressSelected = true;
268 void DrawingView::HandlePenStyle(int style)
270 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
272 Object * obj = (Object *)(*i);
276 supressSelected = true;
280 void DrawingView::HandlePenColor(uint32_t color)
282 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
284 Object * obj = (Object *)(*i);
288 supressSelected = true;
292 void DrawingView::HandlePenStamp(QAction * action)
294 PenWidget * pw = (PenWidget *)action->parentWidget();
295 pw->dropperAction->setChecked(false);
296 Global::penDropper = false;
297 Global::penStamp = action->isChecked();
299 if (Global::penStamp)
300 setCursor(curMarker);
302 setCursor(Qt::ArrowCursor);
304 if (Global::penStamp == false)
305 ClearSelected(document.objects);
310 void DrawingView::HandlePenDropper(QAction * action)
312 PenWidget * pw = (PenWidget *)action->parentWidget();
313 pw->stampAction->setChecked(false);
314 Global::penStamp = false;
315 Global::penDropper = action->isChecked();
317 if (Global::penDropper)
318 setCursor(curDropper);
320 setCursor(Qt::ArrowCursor);
325 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
327 // This is undoing the transform, e.g. going from client coords to local
328 // coords. In essence, the height - y is height + (y * -1), the (y * -1)
329 // term doing the conversion of the y-axis from increasing bottom to top.
330 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
333 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
335 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
336 // No voodoo here, it's just grouped wrong to see it. It should be:
337 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
338 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
341 void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
343 // printf("DrawingView::focusOutEvent()...\n");
344 // Make sure all modkeys being held are marked as released when the app
345 // loses focus (N.B.: This only works because the app sets the focus policy
346 // of this object to something other than Qt::NoFocus)
347 shiftDown = ctrlDown = altDown = false;
349 setCursor(Qt::ArrowCursor);
352 void DrawingView::focusInEvent(QFocusEvent * /*event*/)
354 if (Global::penStamp)
355 setCursor(curMarker);
356 else if (Global::penDropper)
357 setCursor(curDropper);
358 //FocusOut already set this...
360 // setCursor(Qt::ArrowCursor);
363 void DrawingView::paintEvent(QPaintEvent * /*event*/)
365 QPainter qtPainter(this);
366 Painter painter(&qtPainter);
369 qtPainter.setRenderHint(QPainter::Antialiasing);
371 Global::viewportHeight = size().height();
373 DrawBackground(&painter);
375 // Draw coordinate axes
376 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
377 painter.DrawLine(0, -16384, 0, 16384);
378 painter.DrawLine(-16384, 0, 16384, 0);
380 // Do object rendering...
381 for(int i=0; i<Global::numLayers; i++)
383 if (Global::layerHidden[i] == false)
384 RenderObjects(&painter, document.objects, i);
387 // Do tool rendering, if any...
390 if (Global::toolSuppressCrosshair == false)
392 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
393 painter.DrawCrosshair(oldPoint);
399 // Do selection rectangle rendering, if any
400 if (Global::selectionInProgress)
402 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
403 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
404 painter.DrawRect(Global::selection);
407 if (hoveringIntersection)
408 painter.DrawHandle(intersectionPoint);
411 painter.DrawHandle(hoverPoint);
413 if (!informativeText.isEmpty())
414 painter.DrawInformativeText(informativeText);
418 // Renders objects in the passed in vector
421 N.B.: Since we have "hoverPointValid" drawing regular object handles above,
422 we can probably do away with a lot of them that are being done down below.
424 [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...]
426 void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool ignoreLayer/*= false*/)
428 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
430 Object * obj = (Object *)(*i);
431 float scaledThickness = Global::scale * obj->thickness;
433 // If the object isn't on the current layer being drawn, skip it
434 if (!ignoreLayer && (obj->layer != layer))
437 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
439 painter->SetPen(0x00FF00, 2.0, LSSolid);
443 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
444 painter->SetBrush(obj->color);
446 // penStamp supresses object highlighting, so that changes can be seen.
447 if (supressSelected || Global::penStamp)
451 painter->SetPen(Global::penColor, Global::zoom * Global::scale * Global::penWidth, Global::penStyle);
452 painter->SetBrush(Global::penColor);
455 else if (obj->selected || obj->hitObject)
456 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
462 painter->DrawLine(obj->p[0], obj->p[1]);
464 if (obj->hitPoint[0])
465 painter->DrawHandle(obj->p[0]);
467 if (obj->hitPoint[1])
468 painter->DrawHandle(obj->p[1]);
471 painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
476 painter->SetBrush(QBrush(Qt::NoBrush));
477 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
479 if (obj->hitPoint[0])
480 painter->DrawHandle(obj->p[0]);
485 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
487 if (obj->hitPoint[0])
488 painter->DrawHandle(obj->p[0]);
490 if (obj->hitPoint[1])
491 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
493 if (obj->hitPoint[2])
494 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
500 Dimension * d = (Dimension *)obj;
502 Vector v(d->p[0], d->p[1]);
503 double angle = v.Angle();
504 Vector unit = v.Unit();
505 d->lp[0] = d->p[0], d->lp[1] = d->p[1];
507 double x1, y1, length;
509 if (d->subtype == DTLinearVert)
511 if ((angle < 0) || (angle > HALF_TAU))
513 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
514 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
515 ortho = Vector(1.0, 0);
516 angle = THREE_QTR_TAU;
520 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
521 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
522 ortho = Vector(-1.0, 0);
526 d->lp[0].x = d->lp[1].x = x1;
527 length = fabs(d->p[0].y - d->p[1].y);
529 else if (d->subtype == DTLinearHorz)
531 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
533 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
534 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
535 ortho = Vector(0, 1.0);
540 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
541 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
542 ortho = Vector(0, -1.0);
546 d->lp[0].y = d->lp[1].y = y1;
547 length = fabs(d->p[0].x - d->p[1].x);
549 else if (d->subtype == DTLinear)
551 angle = Vector(d->lp[0], d->lp[1]).Angle();
552 ortho = Vector::Normal(d->lp[0], d->lp[1]);
553 length = v.Magnitude();
556 unit = Vector(d->lp[0], d->lp[1]).Unit();
558 Point p1 = d->lp[0] + (ortho * (d->offset + (10.0 * scaledThickness)));
559 Point p2 = d->lp[1] + (ortho * (d->offset + (10.0 * scaledThickness)));
560 Point p3 = d->lp[0] + (ortho * (d->offset + (16.0 * scaledThickness)));
561 Point p4 = d->lp[1] + (ortho * (d->offset + (16.0 * scaledThickness)));
562 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
563 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
566 The numbers hardcoded into here, what are they?
567 I believe they are pixels.
569 // Draw extension lines (if certain type)
570 painter->DrawLine(p3, p5);
571 painter->DrawLine(p4, p6);
573 // Calculate whether or not the arrowheads are too crowded to put
574 // inside the extension lines. 9.0 is the length of the arrowhead.
575 double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
577 // On the screen, it's acting like this is actually 58%...
578 // This is correct, we want it to happen at > 50%
581 // Draw main dimension line + arrowheads
582 painter->DrawLine(p1, p2);
583 painter->DrawArrowhead(p1, p2, scaledThickness);
584 painter->DrawArrowhead(p2, p1, scaledThickness);
588 // Draw outside arrowheads
589 Point p7 = p1 - (unit * 9.0 * scaledThickness);
590 Point p8 = p2 + (unit * 9.0 * scaledThickness);
591 painter->DrawArrowhead(p1, p7, scaledThickness);
592 painter->DrawArrowhead(p2, p8, scaledThickness);
593 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
594 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
597 // Draw length of dimension line...
598 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
599 Point ctr = p2 + (Vector(p2, p1) / 2.0);
604 dimText = QString("%1\"").arg(length);
607 double feet = (double)((int)length / 12);
608 double inches = length - (feet * 12.0);
611 dimText = QString("%1'").arg(feet);
613 dimText = QString("%1' %2\"").arg(feet).arg(inches);
617 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().
619 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
623 Point hp1 = (p1 + p2) / 2.0;
624 Point hp2 = (p1 + hp1) / 2.0;
625 Point hp3 = (hp1 + p2) / 2.0;
629 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
630 painter->SetBrush(QBrush(QColor(Qt::magenta)));
631 painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
632 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
635 painter->DrawHandle(hp1);
636 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
640 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
641 painter->SetBrush(QBrush(QColor(Qt::magenta)));
642 painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
643 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
646 painter->DrawHandle(hp2);
647 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
651 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
652 painter->SetBrush(QBrush(QColor(Qt::magenta)));
653 painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
654 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
657 painter->DrawHandle(hp3);
660 if (obj->hitPoint[0])
661 painter->DrawHandle(obj->p[0]);
663 if (obj->hitPoint[1])
664 painter->DrawHandle(obj->p[1]);
671 Text * t = (Text *)obj;
673 if (t->measured == false)
675 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
679 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
700 // Containers require recursive rendering...
701 Container * c = (Container *)obj;
702 //printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
703 RenderObjects(painter, (*c).objects, layer, ignoreLayer);
705 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
706 // Containers also have special indicators showing they are selected
707 if (c->selected || c->hitObject)
709 // Rect r = GetObjectExtents(obj);
710 // painter->DrawRectCorners(r);
711 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
722 supressSelected = false;
726 // This toggles the selection being hovered (typically, only 1 object)
728 void DrawingView::AddHoveredToSelection(void)
730 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
732 if (((Object *)(*i))->hovered)
733 // ((Object *)(*i))->selected = true;
734 ((Object *)(*i))->selected = !((Object *)(*i))->selected;
738 VPVector DrawingView::GetSelection(void)
742 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
744 if (((Object *)(*i))->selected)
752 // When testing for hovered intersections, we need to be able to exclude some
753 // objects which have funky characteristics or handles; so we allow for that
756 VPVector DrawingView::GetHovered(bool exclude/*= false*/)
760 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
762 Object * obj = (Object *)(*i);
767 && ((obj->type == OTDimension)
768 || ((obj->type == OTCircle) && (obj->hitPoint[0] == true))
769 || ((obj->type == OTArc) && (obj->hitPoint[0] == true))
770 || (draggingObject && (obj == dragged))))
780 void DrawingView::MoveSelectedToLayer(int layer)
782 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
784 Object * obj = (Object *)(*i);
786 if (obj->selected || obj->hovered)
791 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
793 Global::screenSize = Vector(size().width(), size().height());
796 void DrawingView::ToolHandler(int mode, Point p)
798 // Drop angle snap until it's needed
801 if (Global::tool == TTLine)
802 LineHandler(mode, p);
803 else if (Global::tool == TTCircle)
804 CircleHandler(mode, p);
805 else if (Global::tool == TTArc)
807 else if (Global::tool == TTRotate)
808 RotateHandler(mode, p);
809 else if (Global::tool == TTMirror)
810 MirrorHandler(mode, p);
811 else if (Global::tool == TTDimension)
812 DimensionHandler(mode, p);
813 else if (Global::tool == TTDelete)
814 DeleteHandler(mode, p);
815 else if (Global::tool == TTTriangulate)
816 TriangulateHandler(mode, p);
817 else if (Global::tool == TTTrim)
818 TrimHandler(mode, p);
819 else if (Global::tool == TTParallel)
820 ParallelHandler(mode, p);
823 void DrawingView::ToolDraw(Painter * painter)
825 if (Global::tool == TTLine)
827 if (Global::toolState == TSNone)
829 painter->DrawHandle(toolPoint[0]);
831 else if ((Global::toolState == TSPoint2) && shiftDown)
833 painter->DrawHandle(toolPoint[1]);
837 painter->DrawLine(toolPoint[0], toolPoint[1]);
838 painter->DrawHandle(toolPoint[1]);
840 Vector v(toolPoint[0], toolPoint[1]);
841 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
842 double absLength = v.Magnitude();
843 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
844 informativeText = text.arg(absLength).arg(absAngle);
847 else if (Global::tool == TTCircle)
849 if (Global::toolState == TSNone)
851 painter->DrawHandle(toolPoint[0]);
853 else if ((Global::toolState == TSPoint2) && shiftDown)
855 painter->DrawHandle(toolPoint[1]);
859 painter->DrawCross(toolPoint[0]);
860 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
861 painter->SetBrush(QBrush(Qt::NoBrush));
862 painter->DrawEllipse(toolPoint[0], length, length);
863 QString text = tr("Radius: %1 in.");
864 informativeText = text.arg(length);
867 else if (Global::tool == TTArc)
869 if (Global::toolState == TSNone)
871 painter->DrawHandle(toolPoint[0]);
873 else if (Global::toolState == TSPoint2)
875 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
876 painter->SetBrush(QBrush(Qt::NoBrush));
877 painter->DrawEllipse(toolPoint[0], length, length);
878 painter->DrawLine(toolPoint[0], toolPoint[1]);
879 painter->DrawHandle(toolPoint[1]);
880 QString text = tr("Radius: %1 in.");
881 informativeText = text.arg(length);
883 else if (Global::toolState == TSPoint3)
885 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
886 painter->DrawLine(toolPoint[0], toolPoint[2]);
887 painter->SetBrush(QBrush(Qt::NoBrush));
888 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
889 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
890 QString text = tr("Angle start: %1") + QChar(0x00B0);
891 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
895 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
896 double span = angle - toolPoint[2].x;
901 painter->DrawLine(toolPoint[0], toolPoint[3]);
902 painter->SetBrush(QBrush(Qt::NoBrush));
903 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
904 painter->SetPen(0xFF00FF, 2.0, LSSolid);
905 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
906 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
907 QString text = tr("Arc span: %1") + QChar(0x00B0);
908 informativeText = text.arg(RADIANS_TO_DEGREES * span);
911 else if (Global::tool == TTRotate)
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 painter->DrawLine(toolPoint[0], toolPoint[1]);
924 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
925 QString text = QChar(0x2221) + QObject::tr(": %1");
926 informativeText = text.arg(absAngle);
929 informativeText += " (Copy)";
932 else if (Global::tool == TTMirror)
934 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
935 painter->DrawHandle(toolPoint[0]);
936 else if ((Global::toolState == TSPoint2) && shiftDown)
937 painter->DrawHandle(toolPoint[1]);
940 if (toolPoint[0] == toolPoint[1])
943 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
944 painter->DrawLine(mirrorPoint, toolPoint[1]);
946 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
948 if (absAngle > 180.0)
951 QString text = QChar(0x2221) + QObject::tr(": %1");
952 informativeText = text.arg(absAngle);
955 informativeText += " (Copy)";
958 else if (Global::tool == TTDimension)
960 if (Global::toolState == TSNone)
962 painter->DrawHandle(toolPoint[0]);
964 else if ((Global::toolState == TSPoint2) && shiftDown)
966 painter->DrawHandle(toolPoint[1]);
970 painter->DrawLine(toolPoint[0], toolPoint[1]);
971 painter->DrawHandle(toolPoint[1]);
973 Vector v(toolPoint[0], toolPoint[1]);
974 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
975 double absLength = v.Magnitude();
976 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
977 informativeText = text.arg(absLength).arg(absAngle);
980 else if (Global::tool == TTTrim)
982 if (toolObj[0] != NULL)
984 // We're assuming ATM it's just a line...
985 painter->SetPen(0xAF0000, 3.0, LSSolid);
986 painter->DrawLine(toolPoint[0], toolPoint[1]);
987 // QString text = tr("Arc span: %1") + QChar(0x00B0);
988 // informativeText = text.arg(RADIANS_TO_DEGREES * span);
991 else if (Global::tool == TTParallel)
996 void DrawingView::LineHandler(int mode, Point p)
1001 /* toolObj[0] = NULL;
1003 // Check to see if we can do a circle tangent snap
1004 if (numHovered == 1)
1006 VPVector hover = GetHovered();
1007 Object * obj = (Object *)hover[0];
1009 // 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! :-)
1010 if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
1014 if (Global::toolState == TSNone)
1022 if (Global::toolState == TSNone)
1027 /* bool isCircle = false;
1029 if (numHovered == 1)
1031 VPVector hover = GetHovered();
1032 Object * obj = (Object *)hover[0];
1034 if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
1041 // Adjust initial point if it's on a circle (tangent point)
1042 if (toolObj[0] != NULL)
1046 Geometry::FindTangents(toolObj[0], toolObj[1]);
1048 if (Global::numIntersectPoints > 0)
1050 toolPoint[0] = Global::intersectPoint[0];
1051 toolPoint[1] = Global::intersectPoint[1];
1056 Geometry::FindTangents(toolObj[0], p);
1058 if (Global::numIntersectPoints > 0)
1059 toolPoint[0] = Global::intersectPoint[0];
1066 Geometry::FindTangents(toolObj[1], toolPoint[0]);
1068 if (Global::numIntersectPoints > 0)
1069 toolPoint[1] = Global::intersectPoint[0];
1077 if (Global::toolState == TSNone)
1079 Global::toolState = TSPoint2;
1080 // Prevent spurious line from drawing...
1081 toolPoint[1] = toolPoint[0];
1083 else if ((Global::toolState == TSPoint2) && shiftDown)
1085 // Key override is telling us to make a new line, not continue the
1087 toolPoint[0] = toolPoint[1];
1091 Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
1092 l->layer = Global::activeLayer;
1093 document.objects.push_back(l);
1094 toolPoint[0] = toolPoint[1];
1099 void DrawingView::CircleHandler(int mode, Point p)
1104 if (Global::toolState == TSNone)
1112 if (Global::toolState == TSNone)
1120 if (Global::toolState == TSNone)
1122 Global::toolState = TSPoint2;
1123 // Prevent spurious line from drawing...
1124 toolPoint[1] = toolPoint[0];
1126 else if ((Global::toolState == TSPoint2) && shiftDown)
1128 // Key override is telling us to make a new line, not continue the
1130 toolPoint[0] = toolPoint[1];
1134 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1135 Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1136 c->layer = Global::activeLayer;
1137 document.objects.push_back(c);
1138 toolPoint[0] = toolPoint[1];
1139 Global::toolState = TSNone;
1144 void DrawingView::ArcHandler(int mode, Point p)
1149 if (Global::toolState == TSNone)
1151 else if (Global::toolState == TSPoint2)
1153 else if (Global::toolState == TSPoint3)
1161 if (Global::toolState == TSNone)
1163 else if (Global::toolState == TSPoint2)
1165 else if (Global::toolState == TSPoint3)
1179 if (Global::toolState == TSNone)
1181 // Prevent spurious line from drawing...
1182 toolPoint[1] = toolPoint[0];
1183 Global::toolState = TSPoint2;
1185 else if (Global::toolState == TSPoint2)
1189 // Key override is telling us to start arc at new center, not
1190 // continue the current one.
1191 toolPoint[0] = toolPoint[1];
1195 // Set the radius in toolPoint[1].x
1196 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1197 Global::toolState = TSPoint3;
1199 else if (Global::toolState == TSPoint3)
1201 // Set the angle in toolPoint[2].x
1202 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1203 Global::toolState = TSPoint4;
1207 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1208 double span = endAngle - toolPoint[2].x;
1213 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1214 arc->layer = Global::activeLayer;
1215 document.objects.push_back(arc);
1216 Global::toolState = TSNone;
1221 void DrawingView::RotateHandler(int mode, Point p)
1226 if (Global::toolState == TSNone)
1229 // SavePointsFrom(select, toolScratch);
1230 CopyObjects(select, toolScratch2);
1231 Global::toolState = TSPoint1;
1233 else if (Global::toolState == TSPoint1)
1241 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1243 else if (Global::toolState == TSPoint2)
1251 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1252 VPVectorIter j = select.begin();
1253 // std::vector<Object>::iterator i = toolScratch.begin();
1254 VPVectorIter i = toolScratch2.begin();
1256 // for(; i!=toolScratch.end(); i++, j++)
1257 for(; i!=toolScratch2.end(); i++, j++)
1259 // Object objT = *i;
1260 // Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1261 // Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1262 Object * objT = (Object *)(*i);
1263 Object * objS = (Object *)(*j);
1265 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1266 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1271 // if (objT.type == OTArc || objT.type == OTText)
1272 if (objT->type == OTArc || objT->type == OTText)
1274 // objS->angle[0] = objT.angle[0] + angle;
1275 objS->angle[0] = objT->angle[0] + angle;
1277 if (objS->angle[0] > TAU)
1278 objS->angle[0] -= TAU;
1280 // else if (objT.type == OTContainer)
1281 else if (objT->type == OTContainer)
1283 // 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...]
1284 // Container * c = (Container *)&objT;
1285 Container * c = (Container *)objT;
1286 Container * c2 = (Container *)objS;
1287 VPVectorIter l = c->objects.begin();
1288 // TODO: Rotate items in the container
1289 // TODO: Make this recursive
1290 for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1292 Object * obj3 = (Object *)(*k);
1293 Object * obj4 = (Object *)(*l);
1295 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1296 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1300 // obj3->angle[0] = objT.angle[0] + angle;
1301 obj3->angle[0] = obj4->angle[0] + angle;
1303 if (obj3->angle[0] > TAU)
1304 obj3->angle[0] -= TAU;
1307 Rect r = GetObjectExtents(objS);
1308 c2->p[0] = r.TopLeft();
1309 c2->p[1] = r.BottomRight();
1317 if (Global::toolState == TSPoint1)
1319 Global::toolState = TSPoint2;
1320 // Prevent spurious line from drawing...
1321 toolPoint[1] = toolPoint[0];
1323 else if ((Global::toolState == TSPoint2) && shiftDown)
1325 // Key override is telling us to make a new line, not continue the
1327 toolPoint[0] = toolPoint[1];
1331 // Either we're finished with our rotate, or we're stamping a copy.
1334 // Stamp a copy of the selection at the current rotation & bail
1336 CopyObjects(select, temp);
1337 ClearSelected(temp);
1338 AddObjectsTo(document.objects, temp);
1339 // RestorePointsTo(select, toolScratch);
1340 RestorePointsTo(select, toolScratch2);
1345 Global::toolState = TSPoint1;
1346 // SavePointsFrom(select, toolScratch);
1347 DeleteContents(toolScratch2);
1348 CopyObjects(select, toolScratch2);
1354 // Reset the selection if shift held down...
1356 // RestorePointsTo(select, toolScratch);
1357 RestorePointsTo(select, toolScratch2);
1362 // Reset selection when key is let up
1364 RotateHandler(ToolMouseMove, toolPoint[1]);
1369 // RestorePointsTo(select, toolScratch);
1370 RestorePointsTo(select, toolScratch2);
1371 DeleteContents(toolScratch2);
1375 void DrawingView::MirrorHandler(int mode, Point p)
1380 if (Global::toolState == TSNone)
1383 SavePointsFrom(select, toolScratch);
1384 Global::toolState = TSPoint1;
1386 else if (Global::toolState == TSPoint1)
1394 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1396 else if (Global::toolState == TSPoint2)
1404 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1405 VPVectorIter j = select.begin();
1406 std::vector<Object>::iterator i = toolScratch.begin();
1408 for(; i!=toolScratch.end(); i++, j++)
1411 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1412 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1413 Object * obj2 = (Object *)(*j);
1418 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1419 a negative start angle which makes it impossible to interact with.
1422 if (obj.type == OTArc)
1424 // This is 2*mirror angle - obj angle - obj span
1425 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1427 if (obj2->angle[0] > TAU)
1428 obj2->angle[0] -= TAU;
1436 if (Global::toolState == TSPoint1)
1438 Global::toolState = TSPoint2;
1439 // Prevent spurious line from drawing...
1440 toolPoint[1] = toolPoint[0];
1442 else if ((Global::toolState == TSPoint2) && shiftDown)
1444 // Key override is telling us to make a new line, not continue the
1446 toolPoint[0] = toolPoint[1];
1450 // Either we're finished with our rotate, or we're stamping a copy.
1453 // Stamp a copy of the selection at the current rotation & bail
1455 CopyObjects(select, temp);
1456 ClearSelected(temp);
1457 AddObjectsTo(document.objects, temp);
1458 RestorePointsTo(select, toolScratch);
1463 Global::toolState = TSPoint1;
1464 SavePointsFrom(select, toolScratch);
1470 // Reset the selection if shift held down...
1472 RestorePointsTo(select, toolScratch);
1477 // Reset selection when key is let up
1479 MirrorHandler(ToolMouseMove, toolPoint[1]);
1484 RestorePointsTo(select, toolScratch);
1488 void DrawingView::DimensionHandler(int mode, Point p)
1493 if (Global::toolState == TSNone)
1501 if (Global::toolState == TSNone)
1509 if (Global::toolState == TSNone)
1511 Global::toolState = TSPoint2;
1512 // Prevent spurious line from drawing...
1513 toolPoint[1] = toolPoint[0];
1515 else if ((Global::toolState == TSPoint2) && shiftDown)
1517 // Key override is telling us to make a new line, not continue the
1519 toolPoint[0] = toolPoint[1];
1523 Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear, 0, Global::penWidth);
1524 d->layer = Global::activeLayer;
1525 document.objects.push_back(d);
1526 Global::toolState = TSNone;
1531 void DrawingView::DeleteHandler(int mode, Point /*p*/)
1537 VPVector hovered = GetHovered();
1539 RemoveHoveredObjects(document.objects);
1540 DeleteContents(hovered);
1561 void DrawingView::TriangulateHandler(int mode, Point /*p*/)
1567 // Skip if nothing hovered...
1568 if (numHovered != 1)
1571 VPVector hover = GetHovered();
1572 Object * obj = (Object *)hover[0];
1574 // Skip if it's not a line...
1575 if (obj->type != OTLine)
1578 if (Global::toolState == TSNone)
1580 else if (Global::toolState == TSPoint2)
1589 if (Global::toolState == TSNone)
1591 else if (Global::toolState == TSPoint2)
1593 else if (Global::toolState == TSPoint3)
1607 if (Global::toolState == TSNone)
1609 Global::toolState = TSPoint2;
1611 else if (Global::toolState == TSPoint2)
1615 // Key override is telling us to start arc at new center, not
1616 // continue the current one.
1617 toolPoint[0] = toolPoint[1];
1621 Global::toolState = TSPoint3;
1625 double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1626 double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1628 Circle c1(toolObj[0]->p[0], len2);
1629 Circle c2(toolObj[0]->p[1], len3);
1631 Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1633 // Only move lines if the triangle formed by them is not degenerate
1634 if (Global::numIntersectPoints > 0)
1636 toolObj[1]->p[0] = toolObj[0]->p[0];
1637 toolObj[1]->p[1] = Global::intersectPoint[0];
1639 toolObj[2]->p[0] = Global::intersectPoint[0];
1640 toolObj[2]->p[1] = toolObj[0]->p[1];
1643 Global::toolState = TSNone;
1648 void DrawingView::TrimHandler(int mode, Point p)
1659 // Bail out if nothing hovered...
1660 if (numHovered != 1)
1666 VPVector hover = GetHovered();
1667 Object * obj = (Object *)hover[0];
1669 // Skip if it's not a line...
1670 if (obj->type != OTLine)
1677 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1678 double t = 0, u = 1.0;
1680 // Currently only deal with line against line trimming, can expand to
1681 // others as well (line/circle, circle/circle, line/arc, etc)
1683 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1685 obj = (Object *)(*i);
1687 if (obj == toolObj[0])
1689 else if (obj->type != OTLine)
1692 Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1694 if (Global::numIntersectParams > 0)
1696 // Skip endpoint-endpoint intersections
1697 if ((Global::numIntersectParams == 2)
1698 && (Global::intersectParam[0] == 0
1699 || Global::intersectParam[0] == 1.0)
1700 && (Global::intersectParam[1] == 0
1701 || Global::intersectParam[1] == 1.0))
1704 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1705 if ((Global::intersectParam[0] > t) && (Global::intersectParam[0] < hoveredParam))
1706 t = Global::intersectParam[0];
1708 if ((Global::intersectParam[0] < u) && (Global::intersectParam[0] > hoveredParam))
1709 u = Global::intersectParam[0];
1715 toolPoint[0] = Geometry::GetPointForParameter(toolObj[0], t);
1716 toolPoint[1] = Geometry::GetPointForParameter(toolObj[0], u);
1722 // Bail out if there's no object to trim
1723 if (toolObj[0] == NULL)
1726 Vector v(toolObj[0]->p[0], toolObj[0]->p[1]);
1728 // Check to see which case we have.
1729 if ((toolParam[0] == 0) && (toolParam[1] == 1.0))
1731 // There was no intersection, so delete the object
1732 toolObj[0]->selected = true;
1733 DeleteSelectedObjects(document.objects);
1735 else if (toolParam[0] == 0)
1737 // We delete the end near point #1
1738 toolObj[0]->p[0] = toolObj[0]->p[0] + (v * toolParam[1]);
1740 else if (toolParam[1] == 1.0)
1742 // We delete the end near point #2
1743 toolObj[0]->p[1] = toolObj[0]->p[0] + (v * toolParam[0]);
1747 // We delete the segment in between, and create a new line in the process
1748 Point p1 = toolObj[0]->p[0] + (v * toolParam[0]);
1749 Point p2 = toolObj[0]->p[0] + (v * toolParam[1]);
1750 Point p3 = toolObj[0]->p[1];
1751 toolObj[0]->p[1] = p1;
1752 Line * l = new Line(p2, p3, toolObj[0]->thickness, toolObj[0]->color, toolObj[0]->style);
1753 document.objects.push_back(l);
1754 // Global::toolState = TSNone;
1757 toolObj[0]->hitObject = toolObj[0]->hitPoint[0] = toolObj[0]->hitPoint[1] = false;
1773 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1797 void DrawingView::mousePressEvent(QMouseEvent * event)
1799 if (event->button() == Qt::LeftButton)
1801 //printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
1802 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1804 // Handle tool processing, if any
1807 if (hoveringIntersection)
1808 point = intersectionPoint;
1809 else if (hoverPointValid)
1811 else if (Global::snapToGrid)
1812 point = SnapPointToGrid(point);
1814 ToolHandler(ToolMouseDown, point);
1818 // Clear the selection only if CTRL isn't being held on click
1820 ClearSelected(document.objects);
1822 // If any objects are being hovered on click, add them to the selection
1826 AddHoveredToSelection();
1827 update(); // needed??
1828 // GetHovered(hover); // prolly needed
1829 VPVector hover2 = GetHovered();
1830 dragged = (Object *)hover2[0];
1831 draggingObject = true;
1832 //printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
1834 // Alert the pen widget
1835 if (Global::penDropper)
1837 Global::penColor = dragged->color;
1838 Global::penWidth = dragged->thickness;
1839 Global::penStyle = dragged->style;
1840 emit ObjectSelected(dragged);
1841 ClearSelected(document.objects);
1845 if (Global::penStamp)
1847 dragged->color = Global::penColor;
1848 dragged->thickness = Global::penWidth;
1849 dragged->style = Global::penStyle;
1853 // See if anything is using just a straight click on a handle
1854 if (HandleObjectClicked())
1856 draggingObject = false;
1861 // Needed for grab & moving objects
1862 // We do it *after*... why? (doesn't seem to confer any advantage...)
1863 if (hoveringIntersection)
1864 oldPoint = intersectionPoint;
1865 else if (hoverPointValid)
1866 oldPoint = hoverPoint;
1867 else if (Global::snapToGrid)
1868 oldPoint = SnapPointToGrid(point);
1870 // Needed for fixed length handling
1871 if (Global::fixedLength)
1873 if (dragged->type == OTLine)
1875 dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1879 if (dragged->type == OTCircle)
1881 // Save for informative text, uh, er, informing
1882 dragged->length = dragged->radius[0];
1888 // Didn't hit any object and not using a tool, so do a selection
1890 Global::selectionInProgress = true;
1891 Global::selection.setTopLeft(QPointF(point.x, point.y));
1892 Global::selection.setBottomRight(QPointF(point.x, point.y));
1893 select = GetSelection();
1895 else if (event->button() == Qt::MiddleButton)
1898 oldPoint = Vector(event->x(), event->y());
1899 // Should also change the mouse pointer as well...
1900 setCursor(Qt::SizeAllCursor);
1904 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1906 // It seems that wheelEvent() triggers this for some reason...
1907 if (scrollWheelSeen)
1909 scrollWheelSeen = false;
1913 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1914 Global::selection.setBottomRight(QPointF(point.x, point.y));
1915 // Only needs to be done here, as mouse down is always preceded by movement
1916 Global::snapPointIsValid = false;
1917 hoveringIntersection = false;
1918 oldScrollPoint = Vector(event->x(), event->y());
1921 if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1923 point = Vector(event->x(), event->y());
1924 // Since we're using Qt coords for scrolling, we have to adjust them
1925 // here to conform to Cartesian coords, since the origin is using
1927 Vector delta(oldPoint, point);
1928 delta /= Global::zoom;
1930 Global::origin -= delta;
1932 // UpdateGridBackground();
1938 // If we're doing a selection rect, see if any objects are engulfed by it
1939 // (implies left mouse button held down)
1940 if (Global::selectionInProgress)
1942 CheckObjectBounds();
1944 // Make sure previously selected objects stay selected (CTRL held)
1945 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
1947 // Make sure *not* to select items on hidden layers
1948 if (Global::layerHidden[((Object *)(*i))->layer] == false)
1949 ((Object *)(*i))->selected = true;
1956 // Do object hit testing...
1957 bool needUpdate = HitTestObjects(point);
1958 VPVector hover2 = GetHovered(true); // Exclude dimension objects and circle centers (probably need to add arc centers too) from hover (also dragged objects...)
1963 printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1965 if (hover2.size() > 0)
1966 printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1971 // Check for multi-hover...
1972 if (hover2.size() > 1)
1974 //need to check for case where hover is over 2 circles and a 3rd's center (no longer a problem, I think)...
1975 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1977 Geometry::Intersects(obj1, obj2);
1978 int numIntersecting = Global::numIntersectParams;
1979 double t = Global::intersectParam[0];
1980 double u = Global::intersectParam[1];
1982 if (numIntersecting > 0)
1984 Vector v1 = Geometry::GetPointForParameter(obj1, t);
1985 Vector v2 = Geometry::GetPointForParameter(obj2, u);
1986 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1987 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1989 hoveringIntersection = true;
1990 intersectionPoint = v1;
1993 numIntersecting = Global::numIntersectPoints;
1995 if (numIntersecting > 0)
1997 Vector v1 = Global::intersectPoint[0];
1999 if (numIntersecting == 2)
2001 Vector v2 = Global::intersectPoint[1];
2003 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
2007 QString text = tr("Intersection <%1, %2>");
2008 informativeText = text.arg(v1.x).arg(v1.y);
2009 hoveringIntersection = true;
2010 intersectionPoint = v1;
2013 else if (hover2.size() == 1)
2015 Object * obj = (Object *)hover2[0];
2017 if (obj->type == OTLine)
2020 Not sure that this is the best way to handle this, but it works(TM)...
2022 Point midpoint = Geometry::Midpoint((Line *)obj);
2023 Vector v1 = Vector::Magnitude(midpoint, point);
2025 if ((v1.Magnitude() * Global::zoom) < 8.0)
2027 QString text = tr("Midpoint <%1, %2>");
2028 informativeText = text.arg(midpoint.x).arg(midpoint.y);
2029 hoverPointValid = true;
2030 hoverPoint = midpoint;
2034 else if (obj->type == OTCircle)
2036 if ((draggingObject && (dragged->type == OTLine)) && (dragged->hitPoint[0] || dragged->hitPoint[1]))
2038 Point p = (dragged->hitPoint[0] ? dragged->p[1] : dragged->p[0]);
2039 Geometry::FindTangents(obj, p);
2041 if (Global::numIntersectPoints > 0)
2043 hoveringIntersection = true;
2044 intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
2047 else if ((Global::tool == TTLine) && (Global::toolState == TSPoint2))
2049 Geometry::FindTangents(obj, toolPoint[0]);
2051 if (Global::numIntersectPoints > 0)
2053 hoveringIntersection = true;
2054 intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
2060 // Handle object movement (left button down & over an object)
2061 if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
2063 if (hoveringIntersection)
2064 point = intersectionPoint;
2065 else if (hoverPointValid)
2067 else if (Global::snapToGrid)
2068 point = SnapPointToGrid(point);
2070 HandleObjectMovement(point);
2076 // Do tool handling, if any are active...
2079 if (hoveringIntersection)
2080 point = intersectionPoint;
2081 else if (hoverPointValid)
2083 else if (Global::snapToGrid)
2086 point = SnapPointToAngle(point);
2088 point = SnapPointToGrid(point);
2091 ToolHandler(ToolMouseMove, point);
2094 // This is used to draw the tool crosshair...
2097 if (needUpdate || Global::tool)
2101 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
2103 if (event->button() == Qt::LeftButton)
2105 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
2106 //could set it up to use the document's update function (assumes that all object
2107 //updates are being reported correctly:
2108 // if (document.NeedsUpdate())
2109 // Do an update if collided with at least *one* object in the document
2115 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
2116 ToolHandler(ToolMouseUp, point);
2120 Global::selectionInProgress = false;
2121 informativeText.clear();
2123 // Should we be doing this automagically? Hmm...
2124 // Clear our vectors
2128 // 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???])
2129 select = GetSelection();
2131 draggingObject = false;
2133 else if (event->button() == Qt::MiddleButton)
2137 if (Global::penStamp)
2138 setCursor(curMarker);
2139 else if (Global::penDropper)
2140 setCursor(curDropper);
2142 setCursor(Qt::ArrowCursor);
2144 // Need to convert this, since it's in Qt coordinates (for wheelEvent)
2145 oldPoint = Painter::QtToCartesianCoords(oldPoint);
2149 void DrawingView::wheelEvent(QWheelEvent * event)
2151 double zoomFactor = 1.20;
2152 scrollWheelSeen = true;
2154 if (event->angleDelta().y() < 0)
2156 if (Global::zoom > 400.0)
2159 Global::zoom *= zoomFactor;
2163 if (Global::zoom < 0.125)
2166 Global::zoom /= zoomFactor;
2169 Point np = Painter::QtToCartesianCoords(oldScrollPoint);
2170 Global::origin += (oldPoint - np);
2172 emit(NeedZoomUpdate());
2175 void DrawingView::keyPressEvent(QKeyEvent * event)
2177 bool oldShift = shiftDown;
2178 bool oldCtrl = ctrlDown;
2179 bool oldAlt = altDown;
2181 if (event->key() == Qt::Key_Shift)
2183 else if (event->key() == Qt::Key_Control)
2185 else if (event->key() == Qt::Key_Alt)
2188 // If there's a change in any of the modifier key states, pass it on to
2189 // the current tool's handler
2190 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2193 ToolHandler(ToolKeyDown, Point(0, 0));
2198 if (oldAlt != altDown)
2201 setCursor(Qt::SizeAllCursor);
2202 oldPoint = oldScrollPoint;
2205 if (select.size() > 0)
2207 if (event->key() == Qt::Key_Up)
2209 TranslateObjects(select, Point(0, +1.0));
2212 else if (event->key() == Qt::Key_Down)
2214 TranslateObjects(select, Point(0, -1.0));
2217 else if (event->key() == Qt::Key_Right)
2219 TranslateObjects(select, Point(+1.0, 0));
2222 else if (event->key() == Qt::Key_Left)
2224 TranslateObjects(select, Point(-1.0, 0));
2230 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2232 bool oldShift = shiftDown;
2233 bool oldCtrl = ctrlDown;
2234 bool oldAlt = altDown;
2236 if (event->key() == Qt::Key_Shift)
2238 else if (event->key() == Qt::Key_Control)
2240 else if (event->key() == Qt::Key_Alt)
2243 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2246 ToolHandler(ToolKeyUp, Point(0, 0));
2251 if (oldAlt != altDown)
2255 if (Global::penStamp)
2256 setCursor(curMarker);
2257 else if (Global::penDropper)
2258 setCursor(curDropper);
2260 setCursor(Qt::ArrowCursor);
2265 // This looks strange, but it's really quite simple: We want a point that's
2266 // more than half-way to the next grid point to snap there while conversely we
2267 // want a point that's less than half-way to to the next grid point then snap
2268 // to the one before it. So we add half of the grid spacing to the point, then
2269 // divide by it so that we can remove the fractional part, then multiply it
2270 // back to get back to the correct answer.
2272 Point DrawingView::SnapPointToGrid(Point point)
2274 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
2275 point /= Global::gridSpacing;
2276 point.x = floor(point.x);//need to fix this for negative numbers...
2277 point.y = floor(point.y);
2278 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
2279 point *= Global::gridSpacing;
2283 Point DrawingView::SnapPointToAngle(Point point)
2285 // Snap to a single digit angle (using toolpoint #1 as the center)
2286 double angle = Vector::Angle(toolPoint[0], point);
2287 double length = Vector::Magnitude(toolPoint[0], point);
2289 // Convert from radians to degrees
2290 double degAngle = angle * RADIANS_TO_DEGREES;
2291 double snapAngle = (double)((int)(degAngle + 0.5));
2294 v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2295 point = toolPoint[0] + v;
2300 Rect DrawingView::GetObjectExtents(Object * obj)
2302 // Default to empty rect, if object checks below fail for some reason
2310 rect = Rect(obj->p[0], obj->p[1]);
2316 rect = Rect(obj->p[0], obj->p[0]);
2317 rect.Expand(obj->radius[0]);
2323 Arc * a = (Arc *)obj;
2325 double start = a->angle[0];
2326 double end = start + a->angle[1];
2327 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2329 // If the end of the arc is before the beginning, add 360 degrees to it
2333 // Adjust the bounds depending on which axes are crossed
2334 if ((start < QTR_TAU) && (end > QTR_TAU))
2337 if ((start < HALF_TAU) && (end > HALF_TAU))
2340 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2343 if ((start < TAU) && (end > TAU))
2346 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2349 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2352 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2355 rect *= a->radius[0];
2356 rect.Translate(a->p[0]);
2362 Text * t = (Text *)obj;
2363 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2369 Container * c = (Container *)obj;
2370 VPVectorIter i = c->objects.begin();
2371 rect = GetObjectExtents((Object *)*i);
2374 for(; i!=c->objects.end(); i++)
2375 rect |= GetObjectExtents((Object *)*i);
2385 void DrawingView::CheckObjectBounds(void)
2389 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2391 Object * obj = (Object *)(*i);
2392 obj->selected = false;
2397 case OTDimension: // N.B.: We don't check this properly...
2399 Line * l = (Line *)obj;
2401 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2409 Circle * c = (Circle *)obj;
2411 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]))
2419 Arc * a = (Arc *)obj;
2421 double start = a->angle[0];
2422 double end = start + a->angle[1];
2423 QPointF p1(cos(start), sin(start));
2424 QPointF p2(cos(end), sin(end));
2425 QRectF bounds(p1, p2);
2428 // Swap X/Y coordinates if they're backwards...
2429 if (bounds.left() > bounds.right())
2431 double temp = bounds.left();
2432 bounds.setLeft(bounds.right());
2433 bounds.setRight(temp);
2436 if (bounds.bottom() > bounds.top())
2438 double temp = bounds.bottom();
2439 bounds.setBottom(bounds.top());
2440 bounds.setTop(temp);
2443 // Doesn't work as advertised! For shame!
2444 bounds = bounds.normalized();
2447 // If the end of the arc is before the beginning, add 360 degrees
2452 // Adjust the bounds depending on which axes are crossed
2453 if ((start < QTR_TAU) && (end > QTR_TAU))
2456 if ((start < HALF_TAU) && (end > HALF_TAU))
2457 bounds.setLeft(-1.0);
2459 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2460 bounds.setBottom(-1.0);
2462 if ((start < TAU) && (end > TAU))
2463 bounds.setRight(1.0);
2465 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2468 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2469 bounds.setLeft(-1.0);
2471 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2472 bounds.setBottom(-1.0);
2474 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2475 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2476 bounds.translate(a->p[0].x, a->p[0].y);
2478 if (Global::selection.contains(bounds))
2486 Text * t = (Text *)obj;
2487 Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2489 if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2497 Container * c = (Container *)obj;
2499 if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2511 bool DrawingView::HitTestObjects(Point point)
2515 bool needUpdate = false;
2516 hoverPointValid = false;
2518 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2520 Object * obj = (Object *)(*i);
2522 // If we're seeing the object we're dragging, skip it
2523 if (draggingObject && (obj == dragged))
2526 if (HitTest(obj, point) == true)
2532 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2533 emit ObjectHovered(obj);
2540 bool DrawingView::HitTest(Object * obj, Point point)
2542 bool needUpdate = false;
2548 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2549 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2550 Vector lineSegment = obj->p[1] - obj->p[0];
2551 Vector v1 = point - obj->p[0];
2552 Vector v2 = point - obj->p[1];
2553 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2557 distance = v1.Magnitude();
2559 distance = v2.Magnitude();
2561 // distance = ?Det?(ls, v1) / |ls|
2562 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2563 / lineSegment.Magnitude());
2565 if ((v1.Magnitude() * Global::zoom) < 8.0)
2567 obj->hitPoint[0] = true;
2568 hoverPoint = obj->p[0];
2569 hoverPointValid = true;
2571 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2573 obj->hitPoint[1] = true;
2574 hoverPoint = obj->p[1];
2575 hoverPointValid = true;
2577 else if ((distance * Global::zoom) < 5.0)
2578 obj->hitObject = true;
2580 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2582 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2590 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2591 obj->hitPoint[0] = obj->hitObject = false;
2592 double length = Vector::Magnitude(obj->p[0], point);
2594 if ((length * Global::zoom) < 8.0)
2596 obj->hitPoint[0] = true;
2597 hoverPoint = obj->p[0];
2598 hoverPointValid = true;
2600 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2601 obj->hitObject = true;
2603 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2605 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2613 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2614 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2615 double length = Vector::Magnitude(obj->p[0], point);
2616 double angle = Vector::Angle(obj->p[0], point);
2618 // Make sure we get the angle in the correct spot
2619 if (angle < obj->angle[0])
2622 // Get the span that we're pointing at...
2623 double span = angle - obj->angle[0];
2625 // N.B.: Still need to hit test the arc start & arc span handles...
2626 double spanAngle = obj->angle[0] + obj->angle[1];
2627 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2628 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2629 double length2 = Vector::Magnitude(point, handle1);
2630 double length3 = Vector::Magnitude(point, handle2);
2632 if ((length * Global::zoom) < 8.0)
2634 obj->hitPoint[0] = true;
2635 hoverPoint = obj->p[0];
2636 hoverPointValid = true;
2638 else if ((length2 * Global::zoom) < 8.0)
2640 obj->hitPoint[1] = true;
2641 hoverPoint = handle1;
2642 hoverPointValid = true;
2644 else if ((length3 * Global::zoom) < 8.0)
2646 obj->hitPoint[2] = true;
2647 hoverPoint = handle2;
2648 hoverPointValid = true;
2650 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2651 obj->hitObject = true;
2653 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2655 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2663 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2664 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2666 Dimension * d = (Dimension *)obj;
2668 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2669 // Get our line parallel to our points
2670 float scaledThickness = Global::scale * obj->thickness;
2672 Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2673 Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2675 Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2676 Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2678 Point p3(p1, point);
2680 Vector v1(d->p[0], point);
2681 Vector v2(d->p[1], point);
2682 Vector lineSegment(p1, p2);
2683 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2685 Point midpoint = (p1 + p2) / 2.0;
2686 Point hFSPoint = Point(midpoint, point);
2687 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2688 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2691 distance = v1.Magnitude();
2693 distance = v2.Magnitude();
2695 // distance = ?Det?(ls, v1) / |ls|
2696 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2697 / lineSegment.Magnitude());
2699 if ((v1.Magnitude() * Global::zoom) < 8.0)
2700 obj->hitPoint[0] = true;
2701 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2702 obj->hitPoint[1] = true;
2703 else if ((distance * Global::zoom) < 5.0)
2704 obj->hitObject = true;
2706 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2707 obj->hitPoint[2] = true;
2708 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2709 obj->hitPoint[3] = true;
2710 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2711 obj->hitPoint[4] = true;
2713 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2715 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2723 Text * t = (Text *)obj;
2724 bool oldHO = obj->hitObject;
2725 obj->hitObject = false;
2727 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2728 //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);
2730 if (r.Contains(point))
2731 obj->hitObject = true;
2733 obj->hovered = (obj->hitObject ? true : false);
2735 if (oldHO != obj->hitObject)
2743 // Containers must be recursively tested... Or do they???
2745 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.
2747 // bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2748 // Object * oldClicked = c->clicked;
2750 still need to compare old state to new state, and set things up based upon that...
2751 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);
2752 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.
2754 Container * c = (Container *)obj;
2755 c->hitObject = false;
2759 VPVector flat = Flatten(c);
2761 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2762 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
2764 Object * cObj = (Object *)(*i);
2766 // Skip the flattened containers (if any)...
2767 if (cObj->type == OTContainer)
2770 // We do it this way instead of needUpdate = HitTest() because we
2771 // are checking more than one object, and that way of doing will
2772 // not return consistent results.
2773 if (HitTest(cObj, point) == true)
2775 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2777 // c->hitObject = true;
2778 // c->clicked = cObj;
2779 // c->hovered = true;
2782 // Same reasons for doing it this way here apply.
2783 if (cObj->hitObject == true)
2785 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2786 c->hitObject = true;
2790 if (cObj->hitPoint[0] == true)
2792 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2793 c->hitPoint[0] = true;
2797 if (cObj->hitPoint[1] == true)
2799 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2800 c->hitPoint[1] = true;
2804 if (cObj->hitPoint[2] == true)
2806 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2807 c->hitPoint[2] = true;
2811 if (cObj->hovered == true)
2812 c->hovered = true;//*/
2825 bool DrawingView::HandleObjectClicked(void)
2827 if (dragged->type == OTDimension)
2829 Dimension * d = (Dimension *)dragged;
2833 // Hit the "flip sides" switch, so flip 'em
2834 Point temp = d->p[0];
2839 else if (d->hitPoint[3])
2841 // There are three cases here: aligned, horizontal, & vertical.
2842 // Aligned and horizontal do the same thing, vertical goes back to
2844 if (d->subtype == DTLinearVert)
2845 d->subtype = DTLinear;
2847 d->subtype = DTLinearVert;
2851 else if (d->hitPoint[4])
2853 // There are three cases here: aligned, horizontal, & vertical.
2854 // Aligned and vertical do the same thing, horizontal goes back to
2856 if (d->subtype == DTLinearHorz)
2857 d->subtype = DTLinear;
2859 d->subtype = DTLinearHorz;
2868 void DrawingView::HandleObjectMovement(Point point)
2870 Point delta = point - oldPoint;
2871 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2872 // Object * obj = (Object *)hover[0];
2873 Object * obj = dragged;
2874 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2875 //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"));
2880 if (obj->hitPoint[0])
2882 if (Global::fixedLength)
2884 Vector line = point - obj->p[1];
2885 Vector unit = line.Unit();
2886 point = obj->p[1] + (unit * obj->length);
2891 else if (obj->hitPoint[1])
2893 if (Global::fixedLength)
2895 Vector line = point - obj->p[0];
2896 Vector unit = line.Unit();
2897 point = obj->p[0] + (unit * obj->length);
2902 else if (obj->hitObject)
2911 if (obj->hitPoint[0])
2913 else if (obj->hitObject)
2915 double oldRadius = obj->length;
2916 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2918 QString text = QObject::tr("Radius: %1\nScale: %2%");
2919 informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2925 if (obj->hitPoint[0])
2927 else if (obj->hitPoint[1])
2929 // Change the Arc's span (handle #1)
2932 double angle = Vector::Angle(obj->p[0], point);
2933 double delta = angle - obj->angle[0];
2938 obj->angle[1] -= delta;
2939 obj->angle[0] = angle;
2941 if (obj->angle[1] < 0)
2942 obj->angle[1] += TAU;
2944 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2945 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);
2949 double angle = Vector::Angle(obj->p[0], point);
2950 obj->angle[0] = angle;
2951 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2952 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2954 else if (obj->hitPoint[2])
2956 // Change the Arc's span (handle #2)
2959 double angle = Vector::Angle(obj->p[0], point);
2960 obj->angle[1] = angle - obj->angle[0];
2962 if (obj->angle[1] < 0)
2963 obj->angle[1] += TAU;
2965 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2966 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);
2970 double angle = Vector::Angle(obj->p[0], point);
2971 obj->angle[0] = angle - obj->angle[1];
2973 if (obj->angle[0] < 0)
2974 obj->angle[0] += TAU;
2976 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2977 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2979 else if (obj->hitObject)
2986 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2987 QString text = QObject::tr("Radius: %1");
2988 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2994 if (obj->hitPoint[0])
2996 else if (obj->hitPoint[1])
2998 else if (obj->hitObject)
3000 // Move measurement lines in/out
3003 Dimension * d = (Dimension *)obj;
3004 double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
3005 float scaledThickness = Global::scale * obj->thickness;
3006 // Looks like offset is 0 to +MAX, but line is at 10.0. So
3007 // anything less than 10.0 should set the offset to 0.
3010 if (dist > (10.0 * scaledThickness))
3011 d->offset = dist - (10.0 * scaledThickness);
3029 // This is shitty, but works for now until I can code up something
3032 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.
3034 // TranslateObject(obj, delta);
3035 TranslateContainer((Container *)obj, point, delta);
3043 void DrawingView::AddDimensionTo(void * o)
3045 Object * obj = (Object *)o;
3050 document.Add(new Dimension(obj->p[0], obj->p[1]));