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]);
691 // Containers require recursive rendering...
692 Container * c = (Container *)obj;
693 //printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
694 RenderObjects(painter, (*c).objects, layer, ignoreLayer);
696 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
697 // Containers also have special indicators showing they are selected
698 if (c->selected || c->hitObject)
700 // Rect r = GetObjectExtents(obj);
701 // painter->DrawRectCorners(r);
702 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
713 supressSelected = false;
717 // This toggles the selection being hovered (typically, only 1 object)
719 void DrawingView::AddHoveredToSelection(void)
721 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
723 if (((Object *)(*i))->hovered)
724 // ((Object *)(*i))->selected = true;
725 ((Object *)(*i))->selected = !((Object *)(*i))->selected;
729 VPVector DrawingView::GetSelection(void)
733 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
735 if (((Object *)(*i))->selected)
742 VPVector DrawingView::GetHovered(void)
746 for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
748 if (((Object *)(*i))->hovered)
755 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
757 Global::screenSize = Vector(size().width(), size().height());
760 void DrawingView::ToolHandler(int mode, Point p)
762 // Drop angle snap until it's needed
765 if (Global::tool == TTLine)
766 LineHandler(mode, p);
767 else if (Global::tool == TTCircle)
768 CircleHandler(mode, p);
769 else if (Global::tool == TTArc)
771 else if (Global::tool == TTRotate)
772 RotateHandler(mode, p);
773 else if (Global::tool == TTMirror)
774 MirrorHandler(mode, p);
775 else if (Global::tool == TTDimension)
776 DimensionHandler(mode, p);
777 else if (Global::tool == TTTriangulate)
778 TriangulateHandler(mode, p);
779 else if (Global::tool == TTTrim)
780 TrimHandler(mode, p);
781 else if (Global::tool == TTParallel)
782 ParallelHandler(mode, p);
785 void DrawingView::ToolDraw(Painter * painter)
787 if (Global::tool == TTLine)
789 if (Global::toolState == TSNone)
791 painter->DrawHandle(toolPoint[0]);
793 else if ((Global::toolState == TSPoint2) && shiftDown)
795 painter->DrawHandle(toolPoint[1]);
799 painter->DrawLine(toolPoint[0], toolPoint[1]);
800 painter->DrawHandle(toolPoint[1]);
802 Vector v(toolPoint[0], toolPoint[1]);
803 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
804 double absLength = v.Magnitude();
805 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
806 informativeText = text.arg(absLength).arg(absAngle);
809 else if (Global::tool == TTCircle)
811 if (Global::toolState == TSNone)
813 painter->DrawHandle(toolPoint[0]);
815 else if ((Global::toolState == TSPoint2) && shiftDown)
817 painter->DrawHandle(toolPoint[1]);
821 painter->DrawCross(toolPoint[0]);
822 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
823 painter->SetBrush(QBrush(Qt::NoBrush));
824 painter->DrawEllipse(toolPoint[0], length, length);
825 QString text = tr("Radius: %1 in.");
826 informativeText = text.arg(length);
829 else if (Global::tool == TTArc)
831 if (Global::toolState == TSNone)
833 painter->DrawHandle(toolPoint[0]);
835 else if (Global::toolState == TSPoint2)
837 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
838 painter->SetBrush(QBrush(Qt::NoBrush));
839 painter->DrawEllipse(toolPoint[0], length, length);
840 painter->DrawLine(toolPoint[0], toolPoint[1]);
841 painter->DrawHandle(toolPoint[1]);
842 QString text = tr("Radius: %1 in.");
843 informativeText = text.arg(length);
845 else if (Global::toolState == TSPoint3)
847 double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
848 painter->DrawLine(toolPoint[0], toolPoint[2]);
849 painter->SetBrush(QBrush(Qt::NoBrush));
850 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
851 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
852 QString text = tr("Angle start: %1") + QChar(0x00B0);
853 informativeText = text.arg(RADIANS_TO_DEGREES * angle);
857 double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
858 double span = angle - toolPoint[2].x;
863 painter->DrawLine(toolPoint[0], toolPoint[3]);
864 painter->SetBrush(QBrush(Qt::NoBrush));
865 painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
866 painter->SetPen(0xFF00FF, 2.0, LSSolid);
867 painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
868 painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
869 QString text = tr("Arc span: %1") + QChar(0x00B0);
870 informativeText = text.arg(RADIANS_TO_DEGREES * span);
873 else if (Global::tool == TTRotate)
875 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
876 painter->DrawHandle(toolPoint[0]);
877 else if ((Global::toolState == TSPoint2) && shiftDown)
878 painter->DrawHandle(toolPoint[1]);
881 if (toolPoint[0] == toolPoint[1])
884 painter->DrawLine(toolPoint[0], toolPoint[1]);
886 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
887 QString text = QChar(0x2221) + QObject::tr(": %1");
888 informativeText = text.arg(absAngle);
891 informativeText += " (Copy)";
894 else if (Global::tool == TTMirror)
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 Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
906 painter->DrawLine(mirrorPoint, toolPoint[1]);
908 double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
910 if (absAngle > 180.0)
913 QString text = QChar(0x2221) + QObject::tr(": %1");
914 informativeText = text.arg(absAngle);
917 informativeText += " (Copy)";
920 if (Global::tool == TTDimension)
922 if (Global::toolState == TSNone)
924 painter->DrawHandle(toolPoint[0]);
926 else if ((Global::toolState == TSPoint2) && shiftDown)
928 painter->DrawHandle(toolPoint[1]);
932 painter->DrawLine(toolPoint[0], toolPoint[1]);
933 painter->DrawHandle(toolPoint[1]);
935 Vector v(toolPoint[0], toolPoint[1]);
936 double absAngle = v.Angle() * RADIANS_TO_DEGREES;
937 double absLength = v.Magnitude();
938 QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
939 informativeText = text.arg(absLength).arg(absAngle);
944 void DrawingView::LineHandler(int mode, Point p)
949 if (Global::toolState == TSNone)
957 if (Global::toolState == TSNone)
965 if (Global::toolState == TSNone)
967 Global::toolState = TSPoint2;
968 // Prevent spurious line from drawing...
969 toolPoint[1] = toolPoint[0];
971 else if ((Global::toolState == TSPoint2) && shiftDown)
973 // Key override is telling us to make a new line, not continue the
975 toolPoint[0] = toolPoint[1];
979 Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
980 l->layer = Global::activeLayer;
981 document.objects.push_back(l);
982 toolPoint[0] = toolPoint[1];
987 void DrawingView::CircleHandler(int mode, Point p)
992 if (Global::toolState == TSNone)
1000 if (Global::toolState == TSNone)
1008 if (Global::toolState == TSNone)
1010 Global::toolState = TSPoint2;
1011 // Prevent spurious line from drawing...
1012 toolPoint[1] = toolPoint[0];
1014 else if ((Global::toolState == TSPoint2) && shiftDown)
1016 // Key override is telling us to make a new line, not continue the
1018 toolPoint[0] = toolPoint[1];
1022 double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1023 Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1024 c->layer = Global::activeLayer;
1025 document.objects.push_back(c);
1026 toolPoint[0] = toolPoint[1];
1027 Global::toolState = TSNone;
1032 void DrawingView::ArcHandler(int mode, Point p)
1037 if (Global::toolState == TSNone)
1039 else if (Global::toolState == TSPoint2)
1041 else if (Global::toolState == TSPoint3)
1049 if (Global::toolState == TSNone)
1051 else if (Global::toolState == TSPoint2)
1053 else if (Global::toolState == TSPoint3)
1067 if (Global::toolState == TSNone)
1069 // Prevent spurious line from drawing...
1070 toolPoint[1] = toolPoint[0];
1071 Global::toolState = TSPoint2;
1073 else if (Global::toolState == TSPoint2)
1077 // Key override is telling us to start arc at new center, not
1078 // continue the current one.
1079 toolPoint[0] = toolPoint[1];
1083 // Set the radius in toolPoint[1].x
1084 toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1085 Global::toolState = TSPoint3;
1087 else if (Global::toolState == TSPoint3)
1089 // Set the angle in toolPoint[2].x
1090 toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1091 Global::toolState = TSPoint4;
1095 double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1096 double span = endAngle - toolPoint[2].x;
1101 Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1102 arc->layer = Global::activeLayer;
1103 document.objects.push_back(arc);
1104 Global::toolState = TSNone;
1109 void DrawingView::RotateHandler(int mode, Point p)
1114 if (Global::toolState == TSNone)
1117 // SavePointsFrom(select, toolScratch);
1118 CopyObjects(select, toolScratch2);
1119 Global::toolState = TSPoint1;
1121 else if (Global::toolState == TSPoint1)
1129 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1131 else if (Global::toolState == TSPoint2)
1139 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1140 VPVectorIter j = select.begin();
1141 // std::vector<Object>::iterator i = toolScratch.begin();
1142 VPVectorIter i = toolScratch2.begin();
1144 // for(; i!=toolScratch.end(); i++, j++)
1145 for(; i!=toolScratch2.end(); i++, j++)
1147 // Object objT = *i;
1148 // Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1149 // Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1150 Object * objT = (Object *)(*i);
1151 Object * objS = (Object *)(*j);
1153 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1154 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1159 // if (objT.type == OTArc || objT.type == OTText)
1160 if (objT->type == OTArc || objT->type == OTText)
1162 // objS->angle[0] = objT.angle[0] + angle;
1163 objS->angle[0] = objT->angle[0] + angle;
1165 if (objS->angle[0] > TAU)
1166 objS->angle[0] -= TAU;
1168 // else if (objT.type == OTContainer)
1169 else if (objT->type == OTContainer)
1171 // 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...]
1172 // Container * c = (Container *)&objT;
1173 Container * c = (Container *)objT;
1174 Container * c2 = (Container *)objS;
1175 VPVectorIter l = c->objects.begin();
1176 // TODO: Rotate items in the container
1177 // TODO: Make this recursive
1178 for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1180 Object * obj3 = (Object *)(*k);
1181 Object * obj4 = (Object *)(*l);
1183 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1184 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1188 // obj3->angle[0] = objT.angle[0] + angle;
1189 obj3->angle[0] = obj4->angle[0] + angle;
1191 if (obj3->angle[0] > TAU)
1192 obj3->angle[0] -= TAU;
1195 Rect r = GetObjectExtents(objS);
1196 c2->p[0] = r.TopLeft();
1197 c2->p[1] = r.BottomRight();
1205 if (Global::toolState == TSPoint1)
1207 Global::toolState = TSPoint2;
1208 // Prevent spurious line from drawing...
1209 toolPoint[1] = toolPoint[0];
1211 else if ((Global::toolState == TSPoint2) && shiftDown)
1213 // Key override is telling us to make a new line, not continue the
1215 toolPoint[0] = toolPoint[1];
1219 // Either we're finished with our rotate, or we're stamping a copy.
1222 // Stamp a copy of the selection at the current rotation & bail
1224 CopyObjects(select, temp);
1225 ClearSelected(temp);
1226 AddObjectsTo(document.objects, temp);
1227 // RestorePointsTo(select, toolScratch);
1228 RestorePointsTo(select, toolScratch2);
1233 Global::toolState = TSPoint1;
1234 // SavePointsFrom(select, toolScratch);
1235 DeleteContents(toolScratch2);
1236 CopyObjects(select, toolScratch2);
1242 // Reset the selection if shift held down...
1244 // RestorePointsTo(select, toolScratch);
1245 RestorePointsTo(select, toolScratch2);
1250 // Reset selection when key is let up
1252 RotateHandler(ToolMouseMove, toolPoint[1]);
1257 // RestorePointsTo(select, toolScratch);
1258 RestorePointsTo(select, toolScratch2);
1259 DeleteContents(toolScratch2);
1263 void DrawingView::MirrorHandler(int mode, Point p)
1268 if (Global::toolState == TSNone)
1271 SavePointsFrom(select, toolScratch);
1272 Global::toolState = TSPoint1;
1274 else if (Global::toolState == TSPoint1)
1282 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1284 else if (Global::toolState == TSPoint2)
1292 double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1293 VPVectorIter j = select.begin();
1294 std::vector<Object>::iterator i = toolScratch.begin();
1296 for(; i!=toolScratch.end(); i++, j++)
1299 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1300 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1301 Object * obj2 = (Object *)(*j);
1306 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1307 a negative start angle which makes it impossible to interact with.
1310 if (obj.type == OTArc)
1312 // This is 2*mirror angle - obj angle - obj span
1313 obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1315 if (obj2->angle[0] > TAU)
1316 obj2->angle[0] -= TAU;
1324 if (Global::toolState == TSPoint1)
1326 Global::toolState = TSPoint2;
1327 // Prevent spurious line from drawing...
1328 toolPoint[1] = toolPoint[0];
1330 else if ((Global::toolState == TSPoint2) && shiftDown)
1332 // Key override is telling us to make a new line, not continue the
1334 toolPoint[0] = toolPoint[1];
1338 // Either we're finished with our rotate, or we're stamping a copy.
1341 // Stamp a copy of the selection at the current rotation & bail
1343 CopyObjects(select, temp);
1344 ClearSelected(temp);
1345 AddObjectsTo(document.objects, temp);
1346 RestorePointsTo(select, toolScratch);
1351 Global::toolState = TSPoint1;
1352 SavePointsFrom(select, toolScratch);
1358 // Reset the selection if shift held down...
1360 RestorePointsTo(select, toolScratch);
1365 // Reset selection when key is let up
1367 MirrorHandler(ToolMouseMove, toolPoint[1]);
1372 RestorePointsTo(select, toolScratch);
1376 void DrawingView::DimensionHandler(int mode, Point p)
1381 if (Global::toolState == TSNone)
1389 if (Global::toolState == TSNone)
1397 if (Global::toolState == TSNone)
1399 Global::toolState = TSPoint2;
1400 // Prevent spurious line from drawing...
1401 toolPoint[1] = toolPoint[0];
1403 else if ((Global::toolState == TSPoint2) && shiftDown)
1405 // Key override is telling us to make a new line, not continue the
1407 toolPoint[0] = toolPoint[1];
1411 Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear);
1412 d->layer = Global::activeLayer;
1413 document.objects.push_back(d);
1414 Global::toolState = TSNone;
1419 void DrawingView::TriangulateHandler(int mode, Point/*p*/)
1425 // Skip if nothing hovered...
1426 if (numHovered != 1)
1429 VPVector hover = GetHovered();
1430 Object * obj = (Object *)hover[0];
1432 // Skip if it's not a line...
1433 if (obj->type != OTLine)
1436 if (Global::toolState == TSNone)
1438 else if (Global::toolState == TSPoint2)
1447 if (Global::toolState == TSNone)
1449 else if (Global::toolState == TSPoint2)
1451 else if (Global::toolState == TSPoint3)
1465 if (Global::toolState == TSNone)
1467 Global::toolState = TSPoint2;
1469 else if (Global::toolState == TSPoint2)
1473 // Key override is telling us to start arc at new center, not
1474 // continue the current one.
1475 toolPoint[0] = toolPoint[1];
1479 Global::toolState = TSPoint3;
1483 double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1484 double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1486 Circle c1(toolObj[0]->p[0], len2);
1487 Circle c2(toolObj[0]->p[1], len3);
1489 Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1491 // Only move lines if the triangle formed by them is not degenerate
1492 if (Global::numIntersectPoints > 0)
1494 toolObj[1]->p[0] = toolObj[0]->p[0];
1495 toolObj[1]->p[1] = Global::intersectPoint[0];
1497 toolObj[2]->p[0] = Global::intersectPoint[0];
1498 toolObj[2]->p[1] = toolObj[0]->p[1];
1501 Global::toolState = TSNone;
1506 void DrawingView::TrimHandler(int mode, Point p)
1509 n.b.: this code is lifted straight out of the old oo code. needs to be updated.
1516 Object * toTrim = doc->lastObjectHovered;
1521 Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
1523 // Check to see which case we have...
1524 // We're trimming point #1...
1527 ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
1531 ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
1535 Point p1 = ((Line *)toTrim)->position + (v * t);
1536 Point p2 = ((Line *)toTrim)->position + (v * u);
1537 Point p3 = ((Line *)toTrim)->endpoint;
1538 ((Line *)toTrim)->endpoint = p1;
1539 Line * line = new Line(p2, p3);
1540 emit ObjectReady(line);
1543 doc->lastObjectHovered = NULL;
1551 Object * toTrim = doc->lastObjectHovered;
1557 if (toTrim->type != OTLine)
1560 double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
1562 std::vector<Object *>::iterator i;
1564 for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
1566 // Can't trim against yourself... :-P
1570 Object * trimAgainst = *i;
1573 if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
1576 int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
1580 // Now what? We don't know which side to trim!
1581 // ... now we do, we know which side of the Line we're on!
1582 if ((t1 > t) && (t1 < pointHoveredT))
1585 if ((t1 < u) && (t1 > pointHoveredT))
1590 // Bail out if nothing hovered...
1591 if (numHovered != 1)
1597 VPVector hover = GetHovered();
1598 Object * obj = (Object *)hover[0];
1600 // Skip if it's not a line...
1601 if (obj->type != OTLine)
1608 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1610 // Currently only deal with line against line trimming, can expand to
1611 // others as well (line/circle, circle/circle, line/arc, etc)
1613 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1615 obj = (Object *)(*i);
1617 if (obj == toolObj[0])
1619 else if (obj->type != OTLine)
1622 Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1624 if (Global::numIntersectParams > 0)
1626 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1646 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1670 void DrawingView::mousePressEvent(QMouseEvent * event)
1672 if (event->button() == Qt::LeftButton)
1674 //printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
1675 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1677 // Handle tool processing, if any
1680 if (hoveringIntersection)
1681 point = intersectionPoint;
1682 else if (hoverPointValid)
1684 else if (Global::snapToGrid)
1685 point = SnapPointToGrid(point);
1687 //Also, may want to figure out if hovering over a snap point on an
1688 //object, snap to grid if not.
1689 // Snap to object point if valid...
1690 // if (Global::snapPointIsValid)
1691 // point = Global::snapPoint;
1693 ToolHandler(ToolMouseDown, point);
1697 // Clear the selection only if CTRL isn't being held on click
1699 ClearSelected(document.objects);
1700 // ClearSelection();
1702 // If any objects are being hovered on click, add them to the selection
1706 AddHoveredToSelection();
1707 update(); // needed??
1708 // GetHovered(hover); // prolly needed
1709 VPVector hover2 = GetHovered();
1710 dragged = (Object *)hover2[0];
1711 draggingObject = true;
1712 //printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
1714 // Alert the pen widget
1715 // Maybe do this with an eyedropper tool on the pen bar? [YES]
1716 // emit ObjectSelected(dragged);
1717 if (Global::penDropper)
1719 Global::penColor = dragged->color;
1720 Global::penWidth = dragged->thickness;
1721 Global::penStyle = dragged->style;
1722 emit ObjectSelected(dragged);
1723 ClearSelected(document.objects);
1727 if (Global::penStamp)
1729 dragged->color = Global::penColor;
1730 dragged->thickness = Global::penWidth;
1731 dragged->style = Global::penStyle;
1735 // See if anything is using just a straight click on a handle
1736 if (HandleObjectClicked())
1738 draggingObject = false;
1743 // Needed for grab & moving objects
1744 // We do it *after*... why? (doesn't seem to confer any advantage...)
1745 if (hoveringIntersection)
1746 oldPoint = intersectionPoint;
1747 else if (hoverPointValid)
1748 oldPoint = hoverPoint;
1749 else if (Global::snapToGrid)
1750 oldPoint = SnapPointToGrid(point);
1752 // Needed for fixed length handling
1753 if (Global::fixedLength)
1755 if (dragged->type == OTLine)
1757 dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1761 if (dragged->type == OTCircle)
1763 // Save for informative text, uh, er, informing
1764 dragged->length = dragged->radius[0];
1770 // Didn't hit any object and not using a tool, so do a selection
1772 Global::selectionInProgress = true;
1773 Global::selection.setTopLeft(QPointF(point.x, point.y));
1774 Global::selection.setBottomRight(QPointF(point.x, point.y));
1775 select = GetSelection();
1777 else if (event->button() == Qt::MiddleButton)
1780 oldPoint = Vector(event->x(), event->y());
1781 // Should also change the mouse pointer as well...
1782 setCursor(Qt::SizeAllCursor);
1786 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1788 // It seems that wheelEvent() triggers this for some reason...
1789 if (scrollWheelSeen)
1791 scrollWheelSeen = false;
1795 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1796 Global::selection.setBottomRight(QPointF(point.x, point.y));
1797 // Only needs to be done here, as mouse down is always preceded by movement
1798 Global::snapPointIsValid = false;
1799 hoveringIntersection = false;
1800 oldScrollPoint = Vector(event->x(), event->y());
1803 if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1805 point = Vector(event->x(), event->y());
1806 // Since we're using Qt coords for scrolling, we have to adjust them
1807 // here to conform to Cartesian coords, since the origin is using
1809 Vector delta(oldPoint, point);
1810 delta /= Global::zoom;
1812 Global::origin -= delta;
1814 // UpdateGridBackground();
1820 // If we're doing a selection rect, see if any objects are engulfed by it
1821 // (implies left mouse button held down)
1822 if (Global::selectionInProgress)
1824 CheckObjectBounds();
1826 // Make sure previously selected objects stay selected (CTRL held)
1827 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
1829 // Make sure *not* to select items on hidden layers
1830 if (Global::layerHidden[((Object *)(*i))->layer] == false)
1831 ((Object *)(*i))->selected = true;
1838 // Do object hit testing...
1839 bool needUpdate = HitTestObjects(point);
1840 VPVector hover2 = GetHovered();
1845 printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1847 if (hover2.size() > 0)
1848 printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1853 // Check for multi-hover...
1856 //need to check for case where hover is over 2 circles and a 3rd's center...
1857 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1859 Geometry::Intersects(obj1, obj2);
1860 int numIntersecting = Global::numIntersectParams;
1861 double t = Global::intersectParam[0];
1862 double u = Global::intersectParam[1];
1864 if (numIntersecting > 0)
1866 Vector v1 = Geometry::GetPointForParameter(obj1, t);
1867 Vector v2 = Geometry::GetPointForParameter(obj2, u);
1868 QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1869 informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1871 hoveringIntersection = true;
1872 intersectionPoint = v1;
1875 numIntersecting = Global::numIntersectPoints;
1877 if (numIntersecting > 0)
1879 Vector v1 = Global::intersectPoint[0];
1881 if (numIntersecting == 2)
1883 Vector v2 = Global::intersectPoint[1];
1885 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1889 QString text = tr("Intersection <%1, %2>");
1890 informativeText = text.arg(v1.x).arg(v1.y);
1891 hoveringIntersection = true;
1892 intersectionPoint = v1;
1895 else if (numHovered == 1)
1897 Object * obj = (Object *)hover2[0];
1899 if (obj->type == OTLine)
1902 Not sure that this is the best way to handle this, but it works(TM)...
1903 Except when lines are overlapping, then it doesn't work... !!! FIX !!!
1905 Point midpoint = Geometry::Midpoint((Line *)obj);
1906 Vector v1 = Vector::Magnitude(midpoint, point);
1908 if ((v1.Magnitude() * Global::zoom) < 8.0)
1910 QString text = tr("Midpoint <%1, %2>");
1911 informativeText = text.arg(midpoint.x).arg(midpoint.y);
1912 hoverPointValid = true;
1913 hoverPoint = midpoint;
1919 // Handle object movement (left button down & over an object)
1920 if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
1922 if (hoveringIntersection)
1923 point = intersectionPoint;
1924 else if (hoverPointValid)
1926 else if (Global::snapToGrid)
1927 point = SnapPointToGrid(point);
1929 HandleObjectMovement(point);
1935 // Do tool handling, if any are active...
1938 if (hoveringIntersection)
1939 point = intersectionPoint;
1940 else if (hoverPointValid)
1942 else if (Global::snapToGrid)
1945 point = SnapPointToAngle(point);
1947 point = SnapPointToGrid(point);
1950 ToolHandler(ToolMouseMove, point);
1953 // This is used to draw the tool crosshair...
1956 if (needUpdate || Global::tool)
1960 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1962 if (event->button() == Qt::LeftButton)
1964 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1965 //could set it up to use the document's update function (assumes that all object
1966 //updates are being reported correctly:
1967 // if (document.NeedsUpdate())
1968 // Do an update if collided with at least *one* object in the document
1974 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1975 ToolHandler(ToolMouseUp, point);
1979 // if (Global::selectionInProgress)
1980 Global::selectionInProgress = false;
1982 informativeText.clear();
1983 // Should we be doing this automagically? Hmm...
1984 // Clear our vectors
1988 // Scoop 'em up (do we need to??? Seems we do, because keyboard movement uses it. Also, tools use it too. But we can move it out of here)
1989 select = GetSelection();
1991 draggingObject = false;
1993 else if (event->button() == Qt::MiddleButton)
1997 if (Global::penStamp)
1998 setCursor(curMarker);
1999 else if (Global::penDropper)
2000 setCursor(curDropper);
2002 setCursor(Qt::ArrowCursor);
2004 // Need to convert this, since it's in Qt coordinates (for wheelEvent)
2005 oldPoint = Painter::QtToCartesianCoords(oldPoint);
2009 void DrawingView::wheelEvent(QWheelEvent * event)
2011 double zoomFactor = 1.20;
2012 scrollWheelSeen = true;
2014 if (event->angleDelta().y() < 0)
2016 if (Global::zoom > 400.0)
2019 Global::zoom *= zoomFactor;
2023 if (Global::zoom < 0.125)
2026 Global::zoom /= zoomFactor;
2029 Point np = Painter::QtToCartesianCoords(oldScrollPoint);
2030 Global::origin += (oldPoint - np);
2032 emit(NeedZoomUpdate());
2035 void DrawingView::keyPressEvent(QKeyEvent * event)
2037 bool oldShift = shiftDown;
2038 bool oldCtrl = ctrlDown;
2039 bool oldAlt = altDown;
2041 if (event->key() == Qt::Key_Shift)
2043 else if (event->key() == Qt::Key_Control)
2045 else if (event->key() == Qt::Key_Alt)
2048 // If there's a change in any of the modifier key states, pass it on to
2049 // the current tool's handler
2050 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2053 ToolHandler(ToolKeyDown, Point(0, 0));
2058 if (oldAlt != altDown)
2061 setCursor(Qt::SizeAllCursor);
2062 oldPoint = oldScrollPoint;
2065 if (select.size() > 0)
2067 if (event->key() == Qt::Key_Up)
2069 TranslateObjects(select, Point(0, +1.0));
2072 else if (event->key() == Qt::Key_Down)
2074 TranslateObjects(select, Point(0, -1.0));
2077 else if (event->key() == Qt::Key_Right)
2079 TranslateObjects(select, Point(+1.0, 0));
2082 else if (event->key() == Qt::Key_Left)
2084 TranslateObjects(select, Point(-1.0, 0));
2090 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2092 bool oldShift = shiftDown;
2093 bool oldCtrl = ctrlDown;
2094 bool oldAlt = altDown;
2096 if (event->key() == Qt::Key_Shift)
2098 else if (event->key() == Qt::Key_Control)
2100 else if (event->key() == Qt::Key_Alt)
2103 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2106 ToolHandler(ToolKeyUp, Point(0, 0));
2111 if (oldAlt != altDown)
2115 if (Global::penStamp)
2116 setCursor(curMarker);
2117 else if (Global::penDropper)
2118 setCursor(curDropper);
2120 setCursor(Qt::ArrowCursor);
2125 // This looks strange, but it's really quite simple: We want a point that's
2126 // more than half-way to the next grid point to snap there while conversely we
2127 // want a point that's less than half-way to to the next grid point then snap
2128 // to the one before it. So we add half of the grid spacing to the point, then
2129 // divide by it so that we can remove the fractional part, then multiply it
2130 // back to get back to the correct answer.
2132 Point DrawingView::SnapPointToGrid(Point point)
2134 point += Global::gridSpacing / 2.0; // *This* adds to Z!!!
2135 point /= Global::gridSpacing;
2136 point.x = floor(point.x);//need to fix this for negative numbers...
2137 point.y = floor(point.y);
2138 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
2139 point *= Global::gridSpacing;
2143 Point DrawingView::SnapPointToAngle(Point point)
2145 // Snap to a single digit angle (using toolpoint #1 as the center)
2146 double angle = Vector::Angle(toolPoint[0], point);
2147 double length = Vector::Magnitude(toolPoint[0], point);
2149 // Convert from radians to degrees
2150 double degAngle = angle * RADIANS_TO_DEGREES;
2151 double snapAngle = (double)((int)(degAngle + 0.5));
2154 v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2155 point = toolPoint[0] + v;
2160 Rect DrawingView::GetObjectExtents(Object * obj)
2162 // Default to empty rect, if object checks below fail for some reason
2170 rect = Rect(obj->p[0], obj->p[1]);
2176 rect = Rect(obj->p[0], obj->p[0]);
2177 rect.Expand(obj->radius[0]);
2183 Arc * a = (Arc *)obj;
2185 double start = a->angle[0];
2186 double end = start + a->angle[1];
2187 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2189 // If the end of the arc is before the beginning, add 360 degrees to it
2193 // Adjust the bounds depending on which axes are crossed
2194 if ((start < QTR_TAU) && (end > QTR_TAU))
2197 if ((start < HALF_TAU) && (end > HALF_TAU))
2200 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2203 if ((start < TAU) && (end > TAU))
2206 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2209 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2212 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2215 rect *= a->radius[0];
2216 rect.Translate(a->p[0]);
2222 Text * t = (Text *)obj;
2223 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2229 Container * c = (Container *)obj;
2230 VPVectorIter i = c->objects.begin();
2231 rect = GetObjectExtents((Object *)*i);
2234 for(; i!=c->objects.end(); i++)
2235 rect |= GetObjectExtents((Object *)*i);
2245 void DrawingView::CheckObjectBounds(void)
2249 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2251 Object * obj = (Object *)(*i);
2252 obj->selected = false;
2259 Line * l = (Line *)obj;
2261 if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2269 Circle * c = (Circle *)obj;
2271 if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0]))
2279 Arc * a = (Arc *)obj;
2281 double start = a->angle[0];
2282 double end = start + a->angle[1];
2283 QPointF p1(cos(start), sin(start));
2284 QPointF p2(cos(end), sin(end));
2285 QRectF bounds(p1, p2);
2288 // Swap X/Y coordinates if they're backwards...
2289 if (bounds.left() > bounds.right())
2291 double temp = bounds.left();
2292 bounds.setLeft(bounds.right());
2293 bounds.setRight(temp);
2296 if (bounds.bottom() > bounds.top())
2298 double temp = bounds.bottom();
2299 bounds.setBottom(bounds.top());
2300 bounds.setTop(temp);
2303 // Doesn't work as advertised! For shame!
2304 bounds = bounds.normalized();
2307 // If the end of the arc is before the beginning, add 360 degrees
2312 // Adjust the bounds depending on which axes are crossed
2313 if ((start < QTR_TAU) && (end > QTR_TAU))
2316 if ((start < HALF_TAU) && (end > HALF_TAU))
2317 bounds.setLeft(-1.0);
2319 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2320 bounds.setBottom(-1.0);
2322 if ((start < TAU) && (end > TAU))
2323 bounds.setRight(1.0);
2325 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2328 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2329 bounds.setLeft(-1.0);
2331 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2332 bounds.setBottom(-1.0);
2334 bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2335 bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2336 bounds.translate(a->p[0].x, a->p[0].y);
2338 if (Global::selection.contains(bounds))
2346 Text * t = (Text *)obj;
2347 Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2349 if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2357 Container * c = (Container *)obj;
2359 if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2371 bool DrawingView::HitTestObjects(Point point)
2375 bool needUpdate = false;
2376 hoverPointValid = false;
2378 for(i=document.objects.begin(); i!=document.objects.end(); i++)
2380 Object * obj = (Object *)(*i);
2382 // If we're seeing the object we're dragging, skip it
2383 if (draggingObject && (obj == dragged))
2386 if (HitTest(obj, point) == true)
2392 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2393 emit ObjectHovered(obj);
2400 bool DrawingView::HitTest(Object * obj, Point point)
2402 bool needUpdate = false;
2408 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2409 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2410 Vector lineSegment = obj->p[1] - obj->p[0];
2411 Vector v1 = point - obj->p[0];
2412 Vector v2 = point - obj->p[1];
2413 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2417 distance = v1.Magnitude();
2419 distance = v2.Magnitude();
2421 // distance = ?Det?(ls, v1) / |ls|
2422 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2423 / lineSegment.Magnitude());
2425 if ((v1.Magnitude() * Global::zoom) < 8.0)
2427 obj->hitPoint[0] = true;
2428 hoverPoint = obj->p[0];
2429 hoverPointValid = true;
2431 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2433 obj->hitPoint[1] = true;
2434 hoverPoint = obj->p[1];
2435 hoverPointValid = true;
2437 else if ((distance * Global::zoom) < 5.0)
2438 obj->hitObject = true;
2440 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2442 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2450 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2451 obj->hitPoint[0] = obj->hitObject = false;
2452 double length = Vector::Magnitude(obj->p[0], point);
2454 if ((length * Global::zoom) < 8.0)
2456 obj->hitPoint[0] = true;
2457 hoverPoint = obj->p[0];
2458 hoverPointValid = true;
2460 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2461 obj->hitObject = true;
2463 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2465 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2473 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2474 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2475 double length = Vector::Magnitude(obj->p[0], point);
2476 double angle = Vector::Angle(obj->p[0], point);
2478 // Make sure we get the angle in the correct spot
2479 if (angle < obj->angle[0])
2482 // Get the span that we're pointing at...
2483 double span = angle - obj->angle[0];
2485 // N.B.: Still need to hit test the arc start & arc span handles...
2486 double spanAngle = obj->angle[0] + obj->angle[1];
2487 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2488 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2489 double length2 = Vector::Magnitude(point, handle1);
2490 double length3 = Vector::Magnitude(point, handle2);
2492 if ((length * Global::zoom) < 8.0)
2494 obj->hitPoint[0] = true;
2495 hoverPoint = obj->p[0];
2496 hoverPointValid = true;
2498 else if ((length2 * Global::zoom) < 8.0)
2500 obj->hitPoint[1] = true;
2501 hoverPoint = handle1;
2502 hoverPointValid = true;
2504 else if ((length3 * Global::zoom) < 8.0)
2506 obj->hitPoint[2] = true;
2507 hoverPoint = handle2;
2508 hoverPointValid = true;
2510 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2511 obj->hitObject = true;
2513 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2515 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2523 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2524 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2526 Dimension * d = (Dimension *)obj;
2528 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2529 // Get our line parallel to our points
2530 float scaledThickness = Global::scale * obj->thickness;
2532 Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2533 Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2535 Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2536 Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2538 Point p3(p1, point);
2540 Vector v1(d->p[0], point);
2541 Vector v2(d->p[1], point);
2542 Vector lineSegment(p1, p2);
2543 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2545 Point midpoint = (p1 + p2) / 2.0;
2546 Point hFSPoint = Point(midpoint, point);
2547 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2548 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2551 distance = v1.Magnitude();
2553 distance = v2.Magnitude();
2555 // distance = ?Det?(ls, v1) / |ls|
2556 distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2557 / lineSegment.Magnitude());
2559 if ((v1.Magnitude() * Global::zoom) < 8.0)
2560 obj->hitPoint[0] = true;
2561 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2562 obj->hitPoint[1] = true;
2563 else if ((distance * Global::zoom) < 5.0)
2564 obj->hitObject = true;
2566 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2567 obj->hitPoint[2] = true;
2568 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2569 obj->hitPoint[3] = true;
2570 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2571 obj->hitPoint[4] = true;
2573 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2575 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2583 Text * t = (Text *)obj;
2584 bool oldHO = obj->hitObject;
2585 obj->hitObject = false;
2587 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2588 //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);
2590 if (r.Contains(point))
2591 obj->hitObject = true;
2593 obj->hovered = (obj->hitObject ? true : false);
2595 if (oldHO != obj->hitObject)
2603 // Containers must be recursively tested... Or do they???
2605 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.
2607 // bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2608 // Object * oldClicked = c->clicked;
2610 still need to compare old state to new state, and set things up based upon that...
2611 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);
2612 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.
2614 Container * c = (Container *)obj;
2615 c->hitObject = false;
2619 VPVector flat = Flatten(c);
2621 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2622 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
2624 Object * cObj = (Object *)(*i);
2626 // Skip the flattened containers (if any)...
2627 if (cObj->type == OTContainer)
2630 // We do it this way instead of needUpdate = HitTest() because we
2631 // are checking more than one object, and that way of doing will
2632 // not return consistent results.
2633 if (HitTest(cObj, point) == true)
2635 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2637 // c->hitObject = true;
2638 // c->clicked = cObj;
2639 // c->hovered = true;
2642 // Same reasons for doing it this way here apply.
2643 if (cObj->hitObject == true)
2645 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2646 c->hitObject = true;
2650 if (cObj->hitPoint[0] == true)
2652 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2653 c->hitPoint[0] = true;
2657 if (cObj->hitPoint[1] == true)
2659 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2660 c->hitPoint[1] = true;
2664 if (cObj->hitPoint[2] == true)
2666 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2667 c->hitPoint[2] = true;
2671 if (cObj->hovered == true)
2672 c->hovered = true;//*/
2685 bool DrawingView::HandleObjectClicked(void)
2687 if (dragged->type == OTDimension)
2689 Dimension * d = (Dimension *)dragged;
2693 // Hit the "flip sides" switch, so flip 'em
2694 Point temp = d->p[0];
2699 else if (d->hitPoint[3])
2701 // There are three cases here: aligned, horizontal, & vertical.
2702 // Aligned and horizontal do the same thing, vertical goes back to
2704 if (d->subtype == DTLinearVert)
2705 d->subtype = DTLinear;
2707 d->subtype = DTLinearVert;
2711 else if (d->hitPoint[4])
2713 // There are three cases here: aligned, horizontal, & vertical.
2714 // Aligned and vertical do the same thing, horizontal goes back to
2716 if (d->subtype == DTLinearHorz)
2717 d->subtype = DTLinear;
2719 d->subtype = DTLinearHorz;
2728 void DrawingView::HandleObjectMovement(Point point)
2730 Point delta = point - oldPoint;
2731 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2732 // Object * obj = (Object *)hover[0];
2733 Object * obj = dragged;
2734 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2735 //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"));
2740 if (obj->hitPoint[0])
2742 if (Global::fixedLength)
2744 Vector line = point - obj->p[1];
2745 Vector unit = line.Unit();
2746 point = obj->p[1] + (unit * obj->length);
2751 else if (obj->hitPoint[1])
2753 if (Global::fixedLength)
2755 Vector line = point - obj->p[0];
2756 Vector unit = line.Unit();
2757 point = obj->p[0] + (unit * obj->length);
2762 else if (obj->hitObject)
2771 if (obj->hitPoint[0])
2773 else if (obj->hitObject)
2775 double oldRadius = obj->length;
2776 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2778 QString text = QObject::tr("Radius: %1\nScale: %2%");
2779 informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2785 if (obj->hitPoint[0])
2787 else if (obj->hitPoint[1])
2789 // Change the Arc's span (handle #1)
2792 double angle = Vector::Angle(obj->p[0], point);
2793 double delta = angle - obj->angle[0];
2798 obj->angle[1] -= delta;
2799 obj->angle[0] = angle;
2801 if (obj->angle[1] < 0)
2802 obj->angle[1] += TAU;
2804 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2805 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);
2809 double angle = Vector::Angle(obj->p[0], point);
2810 obj->angle[0] = angle;
2811 QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2812 informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2814 else if (obj->hitPoint[2])
2816 // Change the Arc's span (handle #2)
2819 double angle = Vector::Angle(obj->p[0], point);
2820 obj->angle[1] = angle - obj->angle[0];
2822 if (obj->angle[1] < 0)
2823 obj->angle[1] += TAU;
2825 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2826 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);
2830 double angle = Vector::Angle(obj->p[0], point);
2831 obj->angle[0] = angle - obj->angle[1];
2833 if (obj->angle[0] < 0)
2834 obj->angle[0] += TAU;
2836 QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2837 informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2839 else if (obj->hitObject)
2846 obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2847 QString text = QObject::tr("Radius: %1");
2848 informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2854 if (obj->hitPoint[0])
2856 else if (obj->hitPoint[1])
2858 else if (obj->hitObject)
2860 // Move measurement lines in/out
2863 Dimension * d = (Dimension *)obj;
2864 double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
2865 float scaledThickness = Global::scale * obj->thickness;
2866 // Looks like offset is 0 to +MAX, but line is at 10.0. So
2867 // anything less than 10.0 should set the offset to 0.
2870 if (dist > (10.0 * scaledThickness))
2871 d->offset = dist - (10.0 * scaledThickness);
2889 // This is shitty, but works for now until I can code up something
2892 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.
2894 // TranslateObject(obj, delta);
2895 TranslateContainer((Container *)obj, point, delta);
2903 void DrawingView::AddDimensionTo(void * o)
2905 Object * obj = (Object *)o;
2910 document.Add(new Dimension(obj->p[0], obj->p[1]));