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, 0xDBDBFF, 0.25, start, size);
164 if (Global::gridSpacing <= 0.5)
165 DrawSubGrid(painter, 0xDBDBFF, 0.5, start, size);
167 painter->SetPen(QPen(QColor(0xD2, 0xD2, 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 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
391 painter.DrawCrosshair(oldPoint);
395 // Do selection rectangle rendering, if any
396 if (Global::selectionInProgress)
398 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
399 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
400 painter.DrawRect(Global::selection);
403 if (hoveringIntersection)
404 painter.DrawHandle(intersectionPoint);
407 painter.DrawHandle(hoverPoint);
409 if (!informativeText.isEmpty())
410 painter.DrawInformativeText(informativeText);
414 // Renders objects in the passed in vector
417 N.B.: Since we have "hoverPointValid" drawing regular object handles above,
418 we can probably do away with a lot of them that are being done down below.
420 [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...]
422 void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool ignoreLayer/*= false*/)
424 for(VPVectorIter i=v.begin(); i!=v.end(); i++)
426 Object * obj = (Object *)(*i);
427 float scaledThickness = Global::scale * obj->thickness;
429 // If the object isn't on the current layer being drawn, skip it
430 if (!ignoreLayer && (obj->layer != layer))
433 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
435 painter->SetPen(0x00FF00, 2.0, LSSolid);
439 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
440 painter->SetBrush(obj->color);
442 // penStamp supresses object highlighting, so that changes can be seen.
443 if (supressSelected || Global::penStamp)
447 painter->SetPen(Global::penColor, Global::zoom * Global::scale * Global::penWidth, Global::penStyle);
448 painter->SetBrush(Global::penColor);
451 else if (obj->selected || obj->hitObject)
452 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
458 painter->DrawLine(obj->p[0], obj->p[1]);
460 if (obj->hitPoint[0])
461 painter->DrawHandle(obj->p[0]);
463 if (obj->hitPoint[1])
464 painter->DrawHandle(obj->p[1]);
467 painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
472 painter->SetBrush(QBrush(Qt::NoBrush));
473 painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
475 if (obj->hitPoint[0])
476 painter->DrawHandle(obj->p[0]);
481 painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
483 if (obj->hitPoint[0])
484 painter->DrawHandle(obj->p[0]);
486 if (obj->hitPoint[1])
487 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
489 if (obj->hitPoint[2])
490 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
496 Dimension * d = (Dimension *)obj;
498 Vector v(d->p[0], d->p[1]);
499 double angle = v.Angle();
500 Vector unit = v.Unit();
501 d->lp[0] = d->p[0], d->lp[1] = d->p[1];
503 double x1, y1, length;
505 if (d->subtype == DTLinearVert)
507 if ((angle < 0) || (angle > HALF_TAU))
509 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
510 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
511 ortho = Vector(1.0, 0);
512 angle = THREE_QTR_TAU;
516 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
517 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
518 ortho = Vector(-1.0, 0);
522 d->lp[0].x = d->lp[1].x = x1;
523 length = fabs(d->p[0].y - d->p[1].y);
525 else if (d->subtype == DTLinearHorz)
527 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
529 x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
530 y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
531 ortho = Vector(0, 1.0);
536 x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
537 y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
538 ortho = Vector(0, -1.0);
542 d->lp[0].y = d->lp[1].y = y1;
543 length = fabs(d->p[0].x - d->p[1].x);
545 else if (d->subtype == DTLinear)
547 angle = Vector(d->lp[0], d->lp[1]).Angle();
548 ortho = Vector::Normal(d->lp[0], d->lp[1]);
549 length = v.Magnitude();
552 unit = Vector(d->lp[0], d->lp[1]).Unit();
554 Point p1 = d->lp[0] + (ortho * (d->offset + (10.0 * scaledThickness)));
555 Point p2 = d->lp[1] + (ortho * (d->offset + (10.0 * scaledThickness)));
556 Point p3 = d->lp[0] + (ortho * (d->offset + (16.0 * scaledThickness)));
557 Point p4 = d->lp[1] + (ortho * (d->offset + (16.0 * scaledThickness)));
558 Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
559 Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
562 The numbers hardcoded into here, what are they?
563 I believe they are pixels.
565 // Draw extension lines (if certain type)
566 painter->DrawLine(p3, p5);
567 painter->DrawLine(p4, p6);
569 // Calculate whether or not the arrowheads are too crowded to put
570 // inside the extension lines. 9.0 is the length of the arrowhead.
571 double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
573 // On the screen, it's acting like this is actually 58%...
574 // This is correct, we want it to happen at > 50%
577 // Draw main dimension line + arrowheads
578 painter->DrawLine(p1, p2);
579 painter->DrawArrowhead(p1, p2, scaledThickness);
580 painter->DrawArrowhead(p2, p1, scaledThickness);
584 // Draw outside arrowheads
585 Point p7 = p1 - (unit * 9.0 * scaledThickness);
586 Point p8 = p2 + (unit * 9.0 * scaledThickness);
587 painter->DrawArrowhead(p1, p7, scaledThickness);
588 painter->DrawArrowhead(p2, p8, scaledThickness);
589 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
590 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
593 // Draw length of dimension line...
594 painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
595 Point ctr = p2 + (Vector(p2, p1) / 2.0);
600 dimText = QString("%1\"").arg(length);
603 double feet = (double)((int)length / 12);
604 double inches = length - (feet * 12.0);
607 dimText = QString("%1'").arg(feet);
609 dimText = QString("%1' %2\"").arg(feet).arg(inches);
613 Where is the text offset? It looks like it's drawing in the center, but obviously it isn't. It isn't here, it's in Painter::DrawAngledText().
615 painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
619 Point hp1 = (p1 + p2) / 2.0;
620 Point hp2 = (p1 + hp1) / 2.0;
621 Point hp3 = (hp1 + p2) / 2.0;
625 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
626 painter->SetBrush(QBrush(QColor(Qt::magenta)));
627 painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
628 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
631 painter->DrawHandle(hp1);
632 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
636 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
637 painter->SetBrush(QBrush(QColor(Qt::magenta)));
638 painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
639 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
642 painter->DrawHandle(hp2);
643 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
647 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
648 painter->SetBrush(QBrush(QColor(Qt::magenta)));
649 painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
650 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
653 painter->DrawHandle(hp3);
656 if (obj->hitPoint[0])
657 painter->DrawHandle(obj->p[0]);
659 if (obj->hitPoint[1])
660 painter->DrawHandle(obj->p[1]);
667 Text * t = (Text *)obj;
669 if (t->measured == false)
671 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
675 painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
696 // Containers require recursive rendering...
697 Container * c = (Container *)obj;
698 //printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
699 RenderObjects(painter, (*c).objects, layer, ignoreLayer);
701 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
702 // Containers also have special indicators showing they are selected
703 if (c->selected || c->hitObject)
705 // Rect r = GetObjectExtents(obj);
706 // painter->DrawRectCorners(r);
707 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
718 supressSelected = false;
722 // This toggles the selection being hovered (typically, only 1 object)
724 void DrawingView::AddHoveredToSelection(void)
726 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
728 if (((Object *)(*i))->hovered)
729 // ((Object *)(*i))->selected = true;
730 ((Object *)(*i))->selected = !((Object *)(*i))->selected;
734 VPVector DrawingView::GetSelection(void)
738 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
740 if (((Object *)(*i))->selected)
748 // When testing for hovered intersections, we need to be able to exclude some
749 // objects which have funky characteristics or handles; so we allow for that
752 VPVector DrawingView::GetHovered(bool exclude/*= false*/)
756 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
758 Object * obj = (Object *)(*i);
763 && ((obj->type == OTDimension)
764 || ((obj->type == OTCircle) && (obj->hitPoint[0] == true))
765 || ((obj->type == OTArc) && (obj->hitPoint[0] == true))
766 || (draggingObject && (obj == dragged))))
776 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
778 Global::screenSize = Vector(size().width(), size().height());
781 void DrawingView::ToolHandler(int mode, Point p)
783 // Drop angle snap until it's needed
786 if (Global::tool == TTLine)
787 LineHandler(mode, p);
788 else if (Global::tool == TTCircle)
789 CircleHandler(mode, p);
790 else if (Global::tool == TTArc)
792 else if (Global::tool == TTRotate)
793 RotateHandler(mode, p);
794 else if (Global::tool == TTMirror)
795 MirrorHandler(mode, p);
796 else if (Global::tool == TTDimension)
797 DimensionHandler(mode, p);
798 else if (Global::tool == TTTriangulate)
799 TriangulateHandler(mode, p);
800 else if (Global::tool == TTTrim)
801 TrimHandler(mode, p);
802 else if (Global::tool == TTParallel)
803 ParallelHandler(mode, p);
806 void DrawingView::ToolDraw(Painter * painter)
808 if (Global::tool == TTLine)
810 if (Global::toolState == TSNone)
812 painter->DrawHandle(toolPoint[0]);
814 else if ((Global::toolState == TSPoint2) && shiftDown)
816 painter->DrawHandle(toolPoint[1]);
820 painter->DrawLine(toolPoint[0], toolPoint[1]);
821 painter->DrawHandle(toolPoint[1]);
823 Vector v(toolPoint[0], toolPoint[1]);
824 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
825 double absLength = v.Magnitude();
826 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
827 informativeText = text.arg(absLength).arg(absAngle);
830 else if (Global::tool == TTCircle)
832 if (Global::toolState == TSNone)
834 painter->DrawHandle(toolPoint[0]);
836 else if ((Global::toolState == TSPoint2) && shiftDown)
838 painter->DrawHandle(toolPoint[1]);
842 painter->DrawCross(toolPoint[0]);
843 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
844 painter->SetBrush(QBrush(Qt::NoBrush));
845 painter->DrawEllipse(toolPoint[0], length, length);
846 QString text = tr("Radius: %1 in.");
847 informativeText = text.arg(length);
850 else if (Global::tool == TTArc)
852 if (Global::toolState == TSNone)
854 painter->DrawHandle(toolPoint[0]);
856 else if (Global::toolState == TSPoint2)
858 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
859 painter->SetBrush(QBrush(Qt::NoBrush));
860 painter->DrawEllipse(toolPoint[0], length, length);
861 painter->DrawLine(toolPoint[0], toolPoint[1]);
862 painter->DrawHandle(toolPoint[1]);
863 QString text = tr("Radius: %1 in.");
864 informativeText = text.arg(length);
866 else if (Global::toolState == TSPoint3)
868 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
869 painter->DrawLine(toolPoint[0], toolPoint[2]);
870 painter->SetBrush(QBrush(Qt::NoBrush));
871 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
872 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
873 QString text = tr("Angle start: %1") + QChar(0x00B0);
874 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
878 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
879 double span = angle - toolPoint[2].x;
884 painter->DrawLine(toolPoint[0], toolPoint[3]);
885 painter->SetBrush(QBrush(Qt::NoBrush));
886 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
887 painter->SetPen(0xFF00FF, 2.0, LSSolid);
888 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
889 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
890 QString text = tr("Arc span: %1") + QChar(0x00B0);
891 informativeText = text.arg(RADIANS_TO_DEGREES * span);
894 else if (Global::tool == TTRotate)
896 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
897 painter->DrawHandle(toolPoint[0]);
898 else if ((Global::toolState == TSPoint2) && shiftDown)
899 painter->DrawHandle(toolPoint[1]);
902 if (toolPoint[0] == toolPoint[1])
905 painter->DrawLine(toolPoint[0], toolPoint[1]);
907 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
908 QString text = QChar(0x2221) + QObject::tr(": %1");
909 informativeText = text.arg(absAngle);
912 informativeText += " (Copy)";
915 else if (Global::tool == TTMirror)
917 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
918 painter->DrawHandle(toolPoint[0]);
919 else if ((Global::toolState == TSPoint2) && shiftDown)
920 painter->DrawHandle(toolPoint[1]);
923 if (toolPoint[0] == toolPoint[1])
926 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
927 painter->DrawLine(mirrorPoint, toolPoint[1]);
929 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
931 if (absAngle > 180.0)
934 QString text = QChar(0x2221) + QObject::tr(": %1");
935 informativeText = text.arg(absAngle);
938 informativeText += " (Copy)";
941 if (Global::tool == TTDimension)
943 if (Global::toolState == TSNone)
945 painter->DrawHandle(toolPoint[0]);
947 else if ((Global::toolState == TSPoint2) && shiftDown)
949 painter->DrawHandle(toolPoint[1]);
953 painter->DrawLine(toolPoint[0], toolPoint[1]);
954 painter->DrawHandle(toolPoint[1]);
956 Vector v(toolPoint[0], toolPoint[1]);
957 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
958 double absLength = v.Magnitude();
959 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
960 informativeText = text.arg(absLength).arg(absAngle);
965 void DrawingView::LineHandler(int mode, Point p)
970 /* toolObj[0] = NULL;
972 // Check to see if we can do a circle tangent snap
975 VPVector hover = GetHovered();
976 Object * obj = (Object *)hover[0];
978 // 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! :-)
979 if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
983 if (Global::toolState == TSNone)
991 if (Global::toolState == TSNone)
996 /* bool isCircle = false;
1000 VPVector hover = GetHovered();
1001 Object * obj = (Object *)hover[0];
1003 if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
1010 // Adjust initial point if it's on a circle (tangent point)
1011 if (toolObj[0] != NULL)
1015 Geometry::FindTangents(toolObj[0], toolObj[1]);
1017 if (Global::numIntersectPoints > 0)
1019 toolPoint[0] = Global::intersectPoint[0];
1020 toolPoint[1] = Global::intersectPoint[1];
1025 Geometry::FindTangents(toolObj[0], p);
1027 if (Global::numIntersectPoints > 0)
1028 toolPoint[0] = Global::intersectPoint[0];
1035 Geometry::FindTangents(toolObj[1], toolPoint[0]);
1037 if (Global::numIntersectPoints > 0)
1038 toolPoint[1] = Global::intersectPoint[0];
1046 if (Global::toolState == TSNone)
1048 Global::toolState = TSPoint2;
1049 // Prevent spurious line from drawing...
1050 toolPoint[1] = toolPoint[0];
1052 else if ((Global::toolState == TSPoint2) && shiftDown)
1054 // Key override is telling us to make a new line, not continue the
1056 toolPoint[0] = toolPoint[1];
1060 Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
1061 l->layer = Global::activeLayer;
1062 document.objects.push_back(l);
1063 toolPoint[0] = toolPoint[1];
1068 void DrawingView::CircleHandler(int mode, Point p)
1073 if (Global::toolState == TSNone)
1081 if (Global::toolState == TSNone)
1089 if (Global::toolState == TSNone)
1091 Global::toolState = TSPoint2;
1092 // Prevent spurious line from drawing...
1093 toolPoint[1] = toolPoint[0];
1095 else if ((Global::toolState == TSPoint2) && shiftDown)
1097 // Key override is telling us to make a new line, not continue the
1099 toolPoint[0] = toolPoint[1];
1103 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1104 Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1105 c->layer = Global::activeLayer;
1106 document.objects.push_back(c);
1107 toolPoint[0] = toolPoint[1];
1108 Global::toolState = TSNone;
1113 void DrawingView::ArcHandler(int mode, Point p)
1118 if (Global::toolState == TSNone)
1120 else if (Global::toolState == TSPoint2)
1122 else if (Global::toolState == TSPoint3)
1130 if (Global::toolState == TSNone)
1132 else if (Global::toolState == TSPoint2)
1134 else if (Global::toolState == TSPoint3)
1148 if (Global::toolState == TSNone)
1150 // Prevent spurious line from drawing...
1151 toolPoint[1] = toolPoint[0];
1152 Global::toolState = TSPoint2;
1154 else if (Global::toolState == TSPoint2)
1158 // Key override is telling us to start arc at new center, not
1159 // continue the current one.
1160 toolPoint[0] = toolPoint[1];
1164 // Set the radius in toolPoint[1].x
1165 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1166 Global::toolState = TSPoint3;
1168 else if (Global::toolState == TSPoint3)
1170 // Set the angle in toolPoint[2].x
1171 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1172 Global::toolState = TSPoint4;
1176 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1177 double span = endAngle - toolPoint[2].x;
1182 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1183 arc->layer = Global::activeLayer;
1184 document.objects.push_back(arc);
1185 Global::toolState = TSNone;
1190 void DrawingView::RotateHandler(int mode, Point p)
1195 if (Global::toolState == TSNone)
1198 // SavePointsFrom(select, toolScratch);
1199 CopyObjects(select, toolScratch2);
1200 Global::toolState = TSPoint1;
1202 else if (Global::toolState == TSPoint1)
1210 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1212 else if (Global::toolState == TSPoint2)
1220 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1221 VPVectorIter j = select.begin();
1222 // std::vector<Object>::iterator i = toolScratch.begin();
1223 VPVectorIter i = toolScratch2.begin();
1225 // for(; i!=toolScratch.end(); i++, j++)
1226 for(; i!=toolScratch2.end(); i++, j++)
1228 // Object objT = *i;
1229 // Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1230 // Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1231 Object * objT = (Object *)(*i);
1232 Object * objS = (Object *)(*j);
1234 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1235 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1240 // if (objT.type == OTArc || objT.type == OTText)
1241 if (objT->type == OTArc || objT->type == OTText)
1243 // objS->angle[0] = objT.angle[0] + angle;
1244 objS->angle[0] = objT->angle[0] + angle;
1246 if (objS->angle[0] > TAU)
1247 objS->angle[0] -= TAU;
1249 // else if (objT.type == OTContainer)
1250 else if (objT->type == OTContainer)
1252 // 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...]
1253 // Container * c = (Container *)&objT;
1254 Container * c = (Container *)objT;
1255 Container * c2 = (Container *)objS;
1256 VPVectorIter l = c->objects.begin();
1257 // TODO: Rotate items in the container
1258 // TODO: Make this recursive
1259 for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1261 Object * obj3 = (Object *)(*k);
1262 Object * obj4 = (Object *)(*l);
1264 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1265 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1269 // obj3->angle[0] = objT.angle[0] + angle;
1270 obj3->angle[0] = obj4->angle[0] + angle;
1272 if (obj3->angle[0] > TAU)
1273 obj3->angle[0] -= TAU;
1276 Rect r = GetObjectExtents(objS);
1277 c2->p[0] = r.TopLeft();
1278 c2->p[1] = r.BottomRight();
1286 if (Global::toolState == TSPoint1)
1288 Global::toolState = TSPoint2;
1289 // Prevent spurious line from drawing...
1290 toolPoint[1] = toolPoint[0];
1292 else if ((Global::toolState == TSPoint2) && shiftDown)
1294 // Key override is telling us to make a new line, not continue the
1296 toolPoint[0] = toolPoint[1];
1300 // Either we're finished with our rotate, or we're stamping a copy.
1303 // Stamp a copy of the selection at the current rotation & bail
1305 CopyObjects(select, temp);
1306 ClearSelected(temp);
1307 AddObjectsTo(document.objects, temp);
1308 // RestorePointsTo(select, toolScratch);
1309 RestorePointsTo(select, toolScratch2);
1314 Global::toolState = TSPoint1;
1315 // SavePointsFrom(select, toolScratch);
1316 DeleteContents(toolScratch2);
1317 CopyObjects(select, toolScratch2);
1323 // Reset the selection if shift held down...
1325 // RestorePointsTo(select, toolScratch);
1326 RestorePointsTo(select, toolScratch2);
1331 // Reset selection when key is let up
1333 RotateHandler(ToolMouseMove, toolPoint[1]);
1338 // RestorePointsTo(select, toolScratch);
1339 RestorePointsTo(select, toolScratch2);
1340 DeleteContents(toolScratch2);
1344 void DrawingView::MirrorHandler(int mode, Point p)
1349 if (Global::toolState == TSNone)
1352 SavePointsFrom(select, toolScratch);
1353 Global::toolState = TSPoint1;
1355 else if (Global::toolState == TSPoint1)
1363 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1365 else if (Global::toolState == TSPoint2)
1373 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1374 VPVectorIter j = select.begin();
1375 std::vector<Object>::iterator i = toolScratch.begin();
1377 for(; i!=toolScratch.end(); i++, j++)
1380 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1381 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1382 Object * obj2 = (Object *)(*j);
1387 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1388 a negative start angle which makes it impossible to interact with.
1391 if (obj.type == OTArc)
1393 // This is 2*mirror angle - obj angle - obj span
1394 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1396 if (obj2->angle[0] > TAU)
1397 obj2->angle[0] -= TAU;
1405 if (Global::toolState == TSPoint1)
1407 Global::toolState = TSPoint2;
1408 // Prevent spurious line from drawing...
1409 toolPoint[1] = toolPoint[0];
1411 else if ((Global::toolState == TSPoint2) && shiftDown)
1413 // Key override is telling us to make a new line, not continue the
1415 toolPoint[0] = toolPoint[1];
1419 // Either we're finished with our rotate, or we're stamping a copy.
1422 // Stamp a copy of the selection at the current rotation & bail
1424 CopyObjects(select, temp);
1425 ClearSelected(temp);
1426 AddObjectsTo(document.objects, temp);
1427 RestorePointsTo(select, toolScratch);
1432 Global::toolState = TSPoint1;
1433 SavePointsFrom(select, toolScratch);
1439 // Reset the selection if shift held down...
1441 RestorePointsTo(select, toolScratch);
1446 // Reset selection when key is let up
1448 MirrorHandler(ToolMouseMove, toolPoint[1]);
1453 RestorePointsTo(select, toolScratch);
1457 void DrawingView::DimensionHandler(int mode, Point p)
1462 if (Global::toolState == TSNone)
1470 if (Global::toolState == TSNone)
1478 if (Global::toolState == TSNone)
1480 Global::toolState = TSPoint2;
1481 // Prevent spurious line from drawing...
1482 toolPoint[1] = toolPoint[0];
1484 else if ((Global::toolState == TSPoint2) && shiftDown)
1486 // Key override is telling us to make a new line, not continue the
1488 toolPoint[0] = toolPoint[1];
1492 Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear);
1493 d->layer = Global::activeLayer;
1494 document.objects.push_back(d);
1495 Global::toolState = TSNone;
1500 void DrawingView::TriangulateHandler(int mode, Point/*p*/)
1506 // Skip if nothing hovered...
1507 if (numHovered != 1)
1510 VPVector hover = GetHovered();
1511 Object * obj = (Object *)hover[0];
1513 // Skip if it's not a line...
1514 if (obj->type != OTLine)
1517 if (Global::toolState == TSNone)
1519 else if (Global::toolState == TSPoint2)
1528 if (Global::toolState == TSNone)
1530 else if (Global::toolState == TSPoint2)
1532 else if (Global::toolState == TSPoint3)
1546 if (Global::toolState == TSNone)
1548 Global::toolState = TSPoint2;
1550 else if (Global::toolState == TSPoint2)
1554 // Key override is telling us to start arc at new center, not
1555 // continue the current one.
1556 toolPoint[0] = toolPoint[1];
1560 Global::toolState = TSPoint3;
1564 double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1565 double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1567 Circle c1(toolObj[0]->p[0], len2);
1568 Circle c2(toolObj[0]->p[1], len3);
1570 Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1572 // Only move lines if the triangle formed by them is not degenerate
1573 if (Global::numIntersectPoints > 0)
1575 toolObj[1]->p[0] = toolObj[0]->p[0];
1576 toolObj[1]->p[1] = Global::intersectPoint[0];
1578 toolObj[2]->p[0] = Global::intersectPoint[0];
1579 toolObj[2]->p[1] = toolObj[0]->p[1];
1582 Global::toolState = TSNone;
1587 void DrawingView::TrimHandler(int mode, Point p)
1590 N.B.: this code is lifted straight out of the old oo code. needs to be updated.
1591 Also: trim tool should ignore snap.
1598 Object * toTrim = doc->lastObjectHovered;
1603 Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
1605 // Check to see which case we have...
1606 // We're trimming point #1...
1609 ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
1613 ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
1617 Point p1 = ((Line *)toTrim)->position + (v * t);
1618 Point p2 = ((Line *)toTrim)->position + (v * u);
1619 Point p3 = ((Line *)toTrim)->endpoint;
1620 ((Line *)toTrim)->endpoint = p1;
1621 Line * line = new Line(p2, p3);
1622 emit ObjectReady(line);
1625 doc->lastObjectHovered = NULL;
1633 Object * toTrim = doc->lastObjectHovered;
1639 if (toTrim->type != OTLine)
1642 double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
1644 std::vector<Object *>::iterator i;
1646 for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
1648 // Can't trim against yourself... :-P
1652 Object * trimAgainst = *i;
1655 if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
1658 int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
1662 // Now what? We don't know which side to trim!
1663 // ... now we do, we know which side of the Line we're on!
1664 if ((t1 > t) && (t1 < pointHoveredT))
1667 if ((t1 < u) && (t1 > pointHoveredT))
1672 // Bail out if nothing hovered...
1673 if (numHovered != 1)
1679 VPVector hover = GetHovered();
1680 Object * obj = (Object *)hover[0];
1682 // Skip if it's not a line...
1683 if (obj->type != OTLine)
1690 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1692 // Currently only deal with line against line trimming, can expand to
1693 // others as well (line/circle, circle/circle, line/arc, etc)
1695 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1697 obj = (Object *)(*i);
1699 if (obj == toolObj[0])
1701 else if (obj->type != OTLine)
1704 Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1706 if (Global::numIntersectParams > 0)
1708 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1728 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1752 void DrawingView::mousePressEvent(QMouseEvent * event)
1754 if (event->button() == Qt::LeftButton)
1756 //printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
1757 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1759 // Handle tool processing, if any
1762 if (hoveringIntersection)
1763 point = intersectionPoint;
1764 else if (hoverPointValid)
1766 else if (Global::snapToGrid)
1767 point = SnapPointToGrid(point);
1769 //Also, may want to figure out if hovering over a snap point on an
1770 //object, snap to grid if not.
1771 // Snap to object point if valid...
1772 // if (Global::snapPointIsValid)
1773 // point = Global::snapPoint;
1775 ToolHandler(ToolMouseDown, point);
1779 // Clear the selection only if CTRL isn't being held on click
1781 ClearSelected(document.objects);
1782 // ClearSelection();
1784 // If any objects are being hovered on click, add them to the selection
1788 AddHoveredToSelection();
1789 update(); // needed??
1790 // GetHovered(hover); // prolly needed
1791 VPVector hover2 = GetHovered();
1792 dragged = (Object *)hover2[0];
1793 draggingObject = true;
1794 //printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
1796 // Alert the pen widget
1797 // Maybe do this with an eyedropper tool on the pen bar? [YES]
1798 // emit ObjectSelected(dragged);
1799 if (Global::penDropper)
1801 Global::penColor = dragged->color;
1802 Global::penWidth = dragged->thickness;
1803 Global::penStyle = dragged->style;
1804 emit ObjectSelected(dragged);
1805 ClearSelected(document.objects);
1809 if (Global::penStamp)
1811 dragged->color = Global::penColor;
1812 dragged->thickness = Global::penWidth;
1813 dragged->style = Global::penStyle;
1817 // See if anything is using just a straight click on a handle
1818 if (HandleObjectClicked())
1820 draggingObject = false;
1825 // Needed for grab & moving objects
1826 // We do it *after*... why? (doesn't seem to confer any advantage...)
1827 if (hoveringIntersection)
1828 oldPoint = intersectionPoint;
1829 else if (hoverPointValid)
1830 oldPoint = hoverPoint;
1831 else if (Global::snapToGrid)
1832 oldPoint = SnapPointToGrid(point);
1834 // Needed for fixed length handling
1835 if (Global::fixedLength)
1837 if (dragged->type == OTLine)
1839 dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1843 if (dragged->type == OTCircle)
1845 // Save for informative text, uh, er, informing
1846 dragged->length = dragged->radius[0];
1852 // Didn't hit any object and not using a tool, so do a selection
1854 Global::selectionInProgress = true;
1855 Global::selection.setTopLeft(QPointF(point.x, point.y));
1856 Global::selection.setBottomRight(QPointF(point.x, point.y));
1857 select = GetSelection();
1859 else if (event->button() == Qt::MiddleButton)
1862 oldPoint = Vector(event->x(), event->y());
1863 // Should also change the mouse pointer as well...
1864 setCursor(Qt::SizeAllCursor);
1868 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1870 // It seems that wheelEvent() triggers this for some reason...
1871 if (scrollWheelSeen)
1873 scrollWheelSeen = false;
1877 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1878 Global::selection.setBottomRight(QPointF(point.x, point.y));
1879 // Only needs to be done here, as mouse down is always preceded by movement
1880 Global::snapPointIsValid = false;
1881 hoveringIntersection = false;
1882 oldScrollPoint = Vector(event->x(), event->y());
1885 if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1887 point = Vector(event->x(), event->y());
1888 // Since we're using Qt coords for scrolling, we have to adjust them
1889 // here to conform to Cartesian coords, since the origin is using
1891 Vector delta(oldPoint, point);
1892 delta /= Global::zoom;
1894 Global::origin -= delta;
1896 // UpdateGridBackground();
1902 // If we're doing a selection rect, see if any objects are engulfed by it
1903 // (implies left mouse button held down)
1904 if (Global::selectionInProgress)
1906 CheckObjectBounds();
1908 // Make sure previously selected objects stay selected (CTRL held)
1909 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
1911 // Make sure *not* to select items on hidden layers
1912 if (Global::layerHidden[((Object *)(*i))->layer] == false)
1913 ((Object *)(*i))->selected = true;
1920 // Do object hit testing...
1921 bool needUpdate = HitTestObjects(point);
1922 VPVector hover2 = GetHovered(true); // Exclude dimension objects and circle centers (probably need to add arc centers too) from hover (also dragged objects...)
1927 printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1929 if (hover2.size() > 0)
1930 printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1935 // Check for multi-hover...
1936 if (hover2.size() > 1)
1938 //need to check for case where hover is over 2 circles and a 3rd's center (no longer a problem, I think)...
1939 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1941 Geometry::Intersects(obj1, obj2);
1942 int numIntersecting = Global::numIntersectParams;
1943 double t = Global::intersectParam[0];
1944 double u = Global::intersectParam[1];
1946 if (numIntersecting > 0)
1948 Vector v1 = Geometry::GetPointForParameter(obj1, t);
1949 Vector v2 = Geometry::GetPointForParameter(obj2, u);
1950 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1951 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1953 hoveringIntersection = true;
1954 intersectionPoint = v1;
1957 numIntersecting = Global::numIntersectPoints;
1959 if (numIntersecting > 0)
1961 Vector v1 = Global::intersectPoint[0];
1963 if (numIntersecting == 2)
1965 Vector v2 = Global::intersectPoint[1];
1967 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1971 QString text = tr("Intersection <%1, %2>");
1972 informativeText = text.arg(v1.x).arg(v1.y);
1973 hoveringIntersection = true;
1974 intersectionPoint = v1;
1977 else if (hover2.size() == 1)
1979 Object * obj = (Object *)hover2[0];
1981 if (obj->type == OTLine)
1984 Not sure that this is the best way to handle this, but it works(TM)...
1986 Point midpoint = Geometry::Midpoint((Line *)obj);
1987 Vector v1 = Vector::Magnitude(midpoint, point);
1989 if ((v1.Magnitude() * Global::zoom) < 8.0)
1991 QString text = tr("Midpoint <%1, %2>");
1992 informativeText = text.arg(midpoint.x).arg(midpoint.y);
1993 hoverPointValid = true;
1994 hoverPoint = midpoint;
1998 else if (obj->type == OTCircle)
2000 if ((draggingObject && (dragged->type == OTLine)) && (dragged->hitPoint[0] || dragged->hitPoint[1]))
2002 Point p = (dragged->hitPoint[0] ? dragged->p[1] : dragged->p[0]);
2003 Geometry::FindTangents(obj, p);
2005 if (Global::numIntersectPoints > 0)
2007 hoveringIntersection = true;
2008 intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
2011 else if ((Global::tool == TTLine) && (Global::toolState == TSPoint2))
2013 Geometry::FindTangents(obj, toolPoint[0]);
2015 if (Global::numIntersectPoints > 0)
2017 hoveringIntersection = true;
2018 intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
2024 // Handle object movement (left button down & over an object)
2025 if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
2027 if (hoveringIntersection)
2028 point = intersectionPoint;
2029 else if (hoverPointValid)
2031 else if (Global::snapToGrid)
2032 point = SnapPointToGrid(point);
2034 HandleObjectMovement(point);
2040 // Do tool handling, if any are active...
2043 if (hoveringIntersection)
2044 point = intersectionPoint;
2045 else if (hoverPointValid)
2047 else if (Global::snapToGrid)
2050 point = SnapPointToAngle(point);
2052 point = SnapPointToGrid(point);
2055 ToolHandler(ToolMouseMove, point);
2058 // This is used to draw the tool crosshair...
2061 if (needUpdate || Global::tool)
2065 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
2067 if (event->button() == Qt::LeftButton)
2069 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
2070 //could set it up to use the document's update function (assumes that all object
2071 //updates are being reported correctly:
2072 // if (document.NeedsUpdate())
2073 // Do an update if collided with at least *one* object in the document
2079 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
2080 ToolHandler(ToolMouseUp, point);
2084 // if (Global::selectionInProgress)
2085 Global::selectionInProgress = false;
2087 informativeText.clear();
2088 // Should we be doing this automagically? Hmm...
2089 // Clear our vectors
2093 // 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)
2094 select = GetSelection();
2096 draggingObject = false;
2098 else if (event->button() == Qt::MiddleButton)
2102 if (Global::penStamp)
2103 setCursor(curMarker);
2104 else if (Global::penDropper)
2105 setCursor(curDropper);
2107 setCursor(Qt::ArrowCursor);
2109 // Need to convert this, since it's in Qt coordinates (for wheelEvent)
2110 oldPoint = Painter::QtToCartesianCoords(oldPoint);
2114 void DrawingView::wheelEvent(QWheelEvent * event)
2116 double zoomFactor = 1.20;
2117 scrollWheelSeen = true;
2119 if (event->angleDelta().y() < 0)
2121 if (Global::zoom > 400.0)
2124 Global::zoom *= zoomFactor;
2128 if (Global::zoom < 0.125)
2131 Global::zoom /= zoomFactor;
2134 Point np = Painter::QtToCartesianCoords(oldScrollPoint);
2135 Global::origin += (oldPoint - np);
2137 emit(NeedZoomUpdate());
2140 void DrawingView::keyPressEvent(QKeyEvent * event)
2142 bool oldShift = shiftDown;
2143 bool oldCtrl = ctrlDown;
2144 bool oldAlt = altDown;
2146 if (event->key() == Qt::Key_Shift)
2148 else if (event->key() == Qt::Key_Control)
2150 else if (event->key() == Qt::Key_Alt)
2153 // If there's a change in any of the modifier key states, pass it on to
2154 // the current tool's handler
2155 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2158 ToolHandler(ToolKeyDown, Point(0, 0));
2163 if (oldAlt != altDown)
2166 setCursor(Qt::SizeAllCursor);
2167 oldPoint = oldScrollPoint;
2170 if (select.size() > 0)
2172 if (event->key() == Qt::Key_Up)
2174 TranslateObjects(select, Point(0, +1.0));
2177 else if (event->key() == Qt::Key_Down)
2179 TranslateObjects(select, Point(0, -1.0));
2182 else if (event->key() == Qt::Key_Right)
2184 TranslateObjects(select, Point(+1.0, 0));
2187 else if (event->key() == Qt::Key_Left)
2189 TranslateObjects(select, Point(-1.0, 0));
2195 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2197 bool oldShift = shiftDown;
2198 bool oldCtrl = ctrlDown;
2199 bool oldAlt = altDown;
2201 if (event->key() == Qt::Key_Shift)
2203 else if (event->key() == Qt::Key_Control)
2205 else if (event->key() == Qt::Key_Alt)
2208 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2211 ToolHandler(ToolKeyUp, Point(0, 0));
2216 if (oldAlt != altDown)
2220 if (Global::penStamp)
2221 setCursor(curMarker);
2222 else if (Global::penDropper)
2223 setCursor(curDropper);
2225 setCursor(Qt::ArrowCursor);
2230 // This looks strange, but it's really quite simple: We want a point that's
2231 // more than half-way to the next grid point to snap there while conversely we
2232 // want a point that's less than half-way to to the next grid point then snap
2233 // to the one before it. So we add half of the grid spacing to the point, then
2234 // divide by it so that we can remove the fractional part, then multiply it
2235 // back to get back to the correct answer.
2237 Point DrawingView::SnapPointToGrid(Point point)
2239 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
2240 point /= Global::gridSpacing;
2241 point.x = floor(point.x);//need to fix this for negative numbers...
2242 point.y = floor(point.y);
2243 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
2244 point *= Global::gridSpacing;
2248 Point DrawingView::SnapPointToAngle(Point point)
2250 // Snap to a single digit angle (using toolpoint #1 as the center)
2251 double angle = Vector::Angle(toolPoint[0], point);
2252 double length = Vector::Magnitude(toolPoint[0], point);
2254 // Convert from radians to degrees
2255 double degAngle = angle * RADIANS_TO_DEGREES;
2256 double snapAngle = (double)((int)(degAngle + 0.5));
2259 v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2260 point = toolPoint[0] + v;
2265 Rect DrawingView::GetObjectExtents(Object * obj)
2267 // Default to empty rect, if object checks below fail for some reason
2275 rect = Rect(obj->p[0], obj->p[1]);
2281 rect = Rect(obj->p[0], obj->p[0]);
2282 rect.Expand(obj->radius[0]);
2288 Arc * a = (Arc *)obj;
2290 double start = a->angle[0];
2291 double end = start + a->angle[1];
2292 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2294 // If the end of the arc is before the beginning, add 360 degrees to it
2298 // Adjust the bounds depending on which axes are crossed
2299 if ((start < QTR_TAU) && (end > QTR_TAU))
2302 if ((start < HALF_TAU) && (end > HALF_TAU))
2305 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2308 if ((start < TAU) && (end > TAU))
2311 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2314 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2317 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2320 rect *= a->radius[0];
2321 rect.Translate(a->p[0]);
2327 Text * t = (Text *)obj;
2328 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2334 Container * c = (Container *)obj;
2335 VPVectorIter i = c->objects.begin();
2336 rect = GetObjectExtents((Object *)*i);
2339 for(; i!=c->objects.end(); i++)
2340 rect |= GetObjectExtents((Object *)*i);
2350 void DrawingView::CheckObjectBounds(void)
2354 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2356 Object * obj = (Object *)(*i);
2357 obj->selected = false;
2362 case OTDimension: // N.B.: We don't check this properly...
2364 Line * l = (Line *)obj;
2366 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2374 Circle * c = (Circle *)obj;
2376 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]))
2384 Arc * a = (Arc *)obj;
2386 double start = a->angle[0];
2387 double end = start + a->angle[1];
2388 QPointF p1(cos(start), sin(start));
2389 QPointF p2(cos(end), sin(end));
2390 QRectF bounds(p1, p2);
2393 // Swap X/Y coordinates if they're backwards...
2394 if (bounds.left() > bounds.right())
2396 double temp = bounds.left();
2397 bounds.setLeft(bounds.right());
2398 bounds.setRight(temp);
2401 if (bounds.bottom() > bounds.top())
2403 double temp = bounds.bottom();
2404 bounds.setBottom(bounds.top());
2405 bounds.setTop(temp);
2408 // Doesn't work as advertised! For shame!
2409 bounds = bounds.normalized();
2412 // If the end of the arc is before the beginning, add 360 degrees
2417 // Adjust the bounds depending on which axes are crossed
2418 if ((start < QTR_TAU) && (end > QTR_TAU))
2421 if ((start < HALF_TAU) && (end > HALF_TAU))
2422 bounds.setLeft(-1.0);
2424 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2425 bounds.setBottom(-1.0);
2427 if ((start < TAU) && (end > TAU))
2428 bounds.setRight(1.0);
2430 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2433 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2434 bounds.setLeft(-1.0);
2436 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2437 bounds.setBottom(-1.0);
2439 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2440 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2441 bounds.translate(a->p[0].x, a->p[0].y);
2443 if (Global::selection.contains(bounds))
2451 Text * t = (Text *)obj;
2452 Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2454 if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2462 Container * c = (Container *)obj;
2464 if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2476 bool DrawingView::HitTestObjects(Point point)
2480 bool needUpdate = false;
2481 hoverPointValid = false;
2483 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2485 Object * obj = (Object *)(*i);
2487 // If we're seeing the object we're dragging, skip it
2488 if (draggingObject && (obj == dragged))
2491 if (HitTest(obj, point) == true)
2497 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2498 emit ObjectHovered(obj);
2505 bool DrawingView::HitTest(Object * obj, Point point)
2507 bool needUpdate = false;
2513 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2514 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2515 Vector lineSegment = obj->p[1] - obj->p[0];
2516 Vector v1 = point - obj->p[0];
2517 Vector v2 = point - obj->p[1];
2518 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2522 distance = v1.Magnitude();
2524 distance = v2.Magnitude();
2526 // distance = ?Det?(ls, v1) / |ls|
2527 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2528 / lineSegment.Magnitude());
2530 if ((v1.Magnitude() * Global::zoom) < 8.0)
2532 obj->hitPoint[0] = true;
2533 hoverPoint = obj->p[0];
2534 hoverPointValid = true;
2536 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2538 obj->hitPoint[1] = true;
2539 hoverPoint = obj->p[1];
2540 hoverPointValid = true;
2542 else if ((distance * Global::zoom) < 5.0)
2543 obj->hitObject = true;
2545 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2547 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2555 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2556 obj->hitPoint[0] = obj->hitObject = false;
2557 double length = Vector::Magnitude(obj->p[0], point);
2559 if ((length * Global::zoom) < 8.0)
2561 obj->hitPoint[0] = true;
2562 hoverPoint = obj->p[0];
2563 hoverPointValid = true;
2565 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2566 obj->hitObject = true;
2568 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2570 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2578 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2579 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2580 double length = Vector::Magnitude(obj->p[0], point);
2581 double angle = Vector::Angle(obj->p[0], point);
2583 // Make sure we get the angle in the correct spot
2584 if (angle < obj->angle[0])
2587 // Get the span that we're pointing at...
2588 double span = angle - obj->angle[0];
2590 // N.B.: Still need to hit test the arc start & arc span handles...
2591 double spanAngle = obj->angle[0] + obj->angle[1];
2592 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2593 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2594 double length2 = Vector::Magnitude(point, handle1);
2595 double length3 = Vector::Magnitude(point, handle2);
2597 if ((length * Global::zoom) < 8.0)
2599 obj->hitPoint[0] = true;
2600 hoverPoint = obj->p[0];
2601 hoverPointValid = true;
2603 else if ((length2 * Global::zoom) < 8.0)
2605 obj->hitPoint[1] = true;
2606 hoverPoint = handle1;
2607 hoverPointValid = true;
2609 else if ((length3 * Global::zoom) < 8.0)
2611 obj->hitPoint[2] = true;
2612 hoverPoint = handle2;
2613 hoverPointValid = true;
2615 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2616 obj->hitObject = true;
2618 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2620 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2628 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2629 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2631 Dimension * d = (Dimension *)obj;
2633 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2634 // Get our line parallel to our points
2635 float scaledThickness = Global::scale * obj->thickness;
2637 Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2638 Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2640 Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2641 Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2643 Point p3(p1, point);
2645 Vector v1(d->p[0], point);
2646 Vector v2(d->p[1], point);
2647 Vector lineSegment(p1, p2);
2648 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2650 Point midpoint = (p1 + p2) / 2.0;
2651 Point hFSPoint = Point(midpoint, point);
2652 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2653 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2656 distance = v1.Magnitude();
2658 distance = v2.Magnitude();
2660 // distance = ?Det?(ls, v1) / |ls|
2661 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2662 / lineSegment.Magnitude());
2664 if ((v1.Magnitude() * Global::zoom) < 8.0)
2665 obj->hitPoint[0] = true;
2666 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2667 obj->hitPoint[1] = true;
2668 else if ((distance * Global::zoom) < 5.0)
2669 obj->hitObject = true;
2671 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2672 obj->hitPoint[2] = true;
2673 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2674 obj->hitPoint[3] = true;
2675 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2676 obj->hitPoint[4] = true;
2678 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2680 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2688 Text * t = (Text *)obj;
2689 bool oldHO = obj->hitObject;
2690 obj->hitObject = false;
2692 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2693 //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);
2695 if (r.Contains(point))
2696 obj->hitObject = true;
2698 obj->hovered = (obj->hitObject ? true : false);
2700 if (oldHO != obj->hitObject)
2708 // Containers must be recursively tested... Or do they???
2710 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.
2712 // bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2713 // Object * oldClicked = c->clicked;
2715 still need to compare old state to new state, and set things up based upon that...
2716 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);
2717 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.
2719 Container * c = (Container *)obj;
2720 c->hitObject = false;
2724 VPVector flat = Flatten(c);
2726 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2727 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
2729 Object * cObj = (Object *)(*i);
2731 // Skip the flattened containers (if any)...
2732 if (cObj->type == OTContainer)
2735 // We do it this way instead of needUpdate = HitTest() because we
2736 // are checking more than one object, and that way of doing will
2737 // not return consistent results.
2738 if (HitTest(cObj, point) == true)
2740 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2742 // c->hitObject = true;
2743 // c->clicked = cObj;
2744 // c->hovered = true;
2747 // Same reasons for doing it this way here apply.
2748 if (cObj->hitObject == true)
2750 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2751 c->hitObject = true;
2755 if (cObj->hitPoint[0] == true)
2757 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2758 c->hitPoint[0] = true;
2762 if (cObj->hitPoint[1] == true)
2764 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2765 c->hitPoint[1] = true;
2769 if (cObj->hitPoint[2] == true)
2771 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2772 c->hitPoint[2] = true;
2776 if (cObj->hovered == true)
2777 c->hovered = true;//*/
2790 bool DrawingView::HandleObjectClicked(void)
2792 if (dragged->type == OTDimension)
2794 Dimension * d = (Dimension *)dragged;
2798 // Hit the "flip sides" switch, so flip 'em
2799 Point temp = d->p[0];
2804 else if (d->hitPoint[3])
2806 // There are three cases here: aligned, horizontal, & vertical.
2807 // Aligned and horizontal do the same thing, vertical goes back to
2809 if (d->subtype == DTLinearVert)
2810 d->subtype = DTLinear;
2812 d->subtype = DTLinearVert;
2816 else if (d->hitPoint[4])
2818 // There are three cases here: aligned, horizontal, & vertical.
2819 // Aligned and vertical do the same thing, horizontal goes back to
2821 if (d->subtype == DTLinearHorz)
2822 d->subtype = DTLinear;
2824 d->subtype = DTLinearHorz;
2833 void DrawingView::HandleObjectMovement(Point point)
2835 Point delta = point - oldPoint;
2836 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2837 // Object * obj = (Object *)hover[0];
2838 Object * obj = dragged;
2839 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2840 //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"));
2845 if (obj->hitPoint[0])
2847 if (Global::fixedLength)
2849 Vector line = point - obj->p[1];
2850 Vector unit = line.Unit();
2851 point = obj->p[1] + (unit * obj->length);
2856 else if (obj->hitPoint[1])
2858 if (Global::fixedLength)
2860 Vector line = point - obj->p[0];
2861 Vector unit = line.Unit();
2862 point = obj->p[0] + (unit * obj->length);
2867 else if (obj->hitObject)
2876 if (obj->hitPoint[0])
2878 else if (obj->hitObject)
2880 double oldRadius = obj->length;
2881 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2883 QString text = QObject::tr("Radius: %1\nScale: %2%");
2884 informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2890 if (obj->hitPoint[0])
2892 else if (obj->hitPoint[1])
2894 // Change the Arc's span (handle #1)
2897 double angle = Vector::Angle(obj->p[0], point);
2898 double delta = angle - obj->angle[0];
2903 obj->angle[1] -= delta;
2904 obj->angle[0] = angle;
2906 if (obj->angle[1] < 0)
2907 obj->angle[1] += TAU;
2909 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2910 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);
2914 double angle = Vector::Angle(obj->p[0], point);
2915 obj->angle[0] = angle;
2916 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2917 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2919 else if (obj->hitPoint[2])
2921 // Change the Arc's span (handle #2)
2924 double angle = Vector::Angle(obj->p[0], point);
2925 obj->angle[1] = angle - obj->angle[0];
2927 if (obj->angle[1] < 0)
2928 obj->angle[1] += TAU;
2930 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2931 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);
2935 double angle = Vector::Angle(obj->p[0], point);
2936 obj->angle[0] = angle - obj->angle[1];
2938 if (obj->angle[0] < 0)
2939 obj->angle[0] += TAU;
2941 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2942 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2944 else if (obj->hitObject)
2951 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2952 QString text = QObject::tr("Radius: %1");
2953 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2959 if (obj->hitPoint[0])
2961 else if (obj->hitPoint[1])
2963 else if (obj->hitObject)
2965 // Move measurement lines in/out
2968 Dimension * d = (Dimension *)obj;
2969 double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
2970 float scaledThickness = Global::scale * obj->thickness;
2971 // Looks like offset is 0 to +MAX, but line is at 10.0. So
2972 // anything less than 10.0 should set the offset to 0.
2975 if (dist > (10.0 * scaledThickness))
2976 d->offset = dist - (10.0 * scaledThickness);
2994 // This is shitty, but works for now until I can code up something
2997 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.
2999 // TranslateObject(obj, delta);
3000 TranslateContainer((Container *)obj, point, delta);
3008 void DrawingView::AddDimensionTo(void * o)
3010 Object * obj = (Object *)o;
3015 document.Add(new Dimension(obj->p[0], obj->p[1]));