]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
Preliminary start on zooming/circle hover feedback.
[architektonas] / src / drawingview.cpp
1 // drawingview.cpp
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  03/22/2011  Created this file
12 //
13
14 // FIXED:
15 //
16 //
17 // STILL TO BE DONE:
18 //
19 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
20 //   to a left-handed system and we need a right-handed one.
21 //
22
23 // Uncomment this for debugging...
24 //#define DEBUG
25 //#define DEBUGFOO                              // Various tool debugging...
26 //#define DEBUGTP                               // Toolpalette debugging...
27
28 #include "drawingview.h"
29
30 #include <stdint.h>
31 #include "mathconstants.h"
32
33 #include "arc.h"
34 #include "circle.h"
35 #include "dimension.h"
36 #include "line.h"
37 #include "painter.h"
38
39
40 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
41         // The value in the settings file will override this.
42         useAntialiasing(true),
43         scale(1.0), offsetX(-10), offsetY(-10),
44         document(Vector(0, 0)),
45         gridSpacing(32.0), collided(false), rotateTool(false), rx(150.0), ry(150.0)
46 {
47         setBackgroundRole(QPalette::Base);
48         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
49
50 //      toolPalette = new ToolWindow();
51 //      CreateCursors();
52 //      setCursor(cur[TOOLSelect]);
53 //      setMouseTracking(true);
54
55         Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
56         document.Add(line);
57         document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
58         document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
59         document.Add(new Circle(Vector(100, 100), 36, &document));
60         document.Add(new Circle(Vector(50, 150), 49, &document));
61         document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
62         document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
63 #if 1
64         Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), &document);
65         line->SetDimensionOnLine(dimension);
66         document.Add(dimension);
67 #else
68         // Alternate way to do the above...
69         line->SetDimensionOnLine();
70 #endif
71 }
72
73 void DrawingView::SetRotateToolActive(bool state/*= true*/)
74 {
75         rotateTool = state;
76         update();
77 }
78
79 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
80 {
81         // This is undoing the transform, e.g. going from client coords to local coords.
82         // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
83         // conversion of the y-axis from increasing bottom to top.
84         return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
85 }
86
87 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
88 {
89         // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
90         // No voodoo here, it's just grouped wrong to see it. It should be:
91         // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
92         return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
93 }
94
95 void DrawingView::paintEvent(QPaintEvent * /*event*/)
96 {
97         QPainter qtPainter(this);
98         Painter painter(&qtPainter);
99
100         if (useAntialiasing)
101                 qtPainter.setRenderHint(QPainter::Antialiasing);
102
103         Painter::screenSize = Vector(size().width(), size().height());
104         Painter::zoom = 2.0;    // 200% zoom
105 #if 0
106 #if 0
107         painter.translate(QPoint(-offsetX, size.height() - (-offsetY)));
108         painter.scale(1.0, -1.0);
109 #else
110         QTransform transform;
111 //order of operations is important! N.B.: Can't use scaling other than 1.0, it
112 //causes lines to look strange (i.e., it scales the pen strokes too)
113 //      transform.translate(-offsetX, size().height() - (-offsetY));
114         transform.scale(1.0, -1.0);
115         transform.translate(-offsetX, -size().height() - offsetY);
116 //      transform.scale(0.25, 0.25);
117         painter.setTransform(transform);
118 #endif
119 #endif
120         Object::SetViewportHeight(size().height());
121
122         // Draw coordinate axes
123
124         painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
125         painter.DrawLine(0, -16384, 0, 16384);
126         painter.DrawLine(-16384, 0, 16384, 0);
127
128         // Draw supplemental (tool related) points
129
130         if (rotateTool)
131         {
132                 painter.SetPen(QPen(QColor(0, 200, 0), 2.0, Qt::SolidLine));
133                 painter.DrawLine(rx - 10, ry, rx + 10, ry);
134                 painter.DrawLine(rx, ry - 10, rx, ry + 10);
135         }
136
137 // Maybe we can make the grid into a background brush instead, and let Qt deal
138 // with it???
139         // Draw grid
140
141 #if 0
142         painter.setPen(QPen(QColor(90, 90, 90), 1.0, Qt::DotLine));
143
144         //these two loops kill performance!
145         // Also, these overwrite our coordinate axes
146         for(double x=0; x<size().width(); x+=gridSpacing*10.0)
147                 painter.drawLine((int)x, -16384, (int)x, 16384);
148
149         for(double y=0; y<size().height(); y+=gridSpacing*10.0)
150                 painter.drawLine(-16384, (int)y, 16384, (int)y);
151 #endif
152
153         painter.SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
154
155         for(double x=0; x<size().width(); x+=gridSpacing)
156                 for(double y=0; y<size().height(); y+=gridSpacing)
157                         painter.DrawPoint((int)x, (int)y);
158
159         // The top level document takes care of rendering for us...
160         document.Draw(&painter);
161 }
162
163 void DrawingView::mousePressEvent(QMouseEvent * event)
164 {
165         if (event->button() == Qt::LeftButton)
166         {
167 //              QPoint pt = GetAdjustedMousePosition(event);
168 //              Vector point(pt.x(), pt.y());
169                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
170
171                 collided = document.Collided(point);
172
173                 if (collided)
174                         update();       // Do an update if collided with at least *one* object in the document
175         }
176 }
177
178 void DrawingView::mouseMoveEvent(QMouseEvent * event)
179 {
180 //      QPoint pt = GetAdjustedMousePosition(event);
181 //      Vector point(pt.x(), pt.y());
182         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
183
184         // Grid processing...
185 #if 1
186         // This looks strange, but it's really quite simple: We want a point that's
187         // more than half-way to the next grid point to snap there while conversely
188         // we want a point that's less than half-way to to the next grid point then
189         // snap to the one before it. So we add half of the grid spacing to the
190         // point, then divide by it so that we can remove the fractional part, then
191         // multiply it back to get back to the correct answer.
192         if (event->buttons() & Qt::LeftButton)
193         {
194                 point += gridSpacing / 2.0;                                     // *This* adds to Z!!!
195                 point /= gridSpacing;
196                 point.x = floor(point.x);//need to fix this for negative numbers...
197                 point.y = floor(point.y);
198                 point.z = 0;                                                            // Make *sure* Z doesn't go anywhere!!!
199                 point *= gridSpacing;
200         }
201 #endif
202 //we should keep track of the last point here and only pass this down *if* the point
203 //changed...
204         document.PointerMoved(point);
205
206         if (document.NeedsUpdate())
207                 update();
208 }
209
210 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
211 {
212         if (event->button() == Qt::LeftButton)
213         {
214                 document.PointerReleased();
215
216 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
217 //could set it up to use the document's update function (assumes that all object updates
218 //are being reported correctly:
219 //              if (document.NeedsUpdate())
220 //              if (collided)
221                         update();       // Do an update if collided with at least *one* object in the document
222         }
223 }
224
225
226 #if 0
227 QSize DrawingView::minimumSizeHint() const
228 {
229         return QSize(50, 50);
230 }
231
232 QSize DrawingView::sizeHint() const
233 {
234         return QSize(400, 400);
235 }
236
237 void DrawingView::CreateCursors(void)
238 {
239         int hotx[8] = {  1,  1, 11, 15,  1,  1,  1,  1 };
240         int hoty[8] = {  1,  1, 11, 13,  1,  1,  1,  1 };
241
242         for(int i=0; i<8; i++)
243         {
244                 QString s;
245                 s.sprintf(":/res/cursor%u.png", i+1);
246                 QPixmap pmTmp(s);
247                 cur[i] = QCursor(pmTmp, hotx[i], hoty[i]);
248         }
249 }
250
251 /*
252 TODO:
253  o  Different colors for polys on selected points
254  o  Different colors for handles on non-selected polys
255  o  Line of sight (dashed, dotted) for off-curve points
256  o  Repaints for press/release of CTRL/SHIFT during point creation
257 */
258 void DrawingView::paintEvent(QPaintEvent * /*event*/)
259 {
260         QPainter p(this);
261 //hm, causes lockup
262 //      p.setRenderHint(QPainter::Antialiasing);
263 //Doesn't do crap!
264 //dc.SetBackground(*wxWHITE_BRUSH);
265
266 // Due to the screwiness of wxWidgets coord system, the origin is ALWAYS
267 // the upper left corner--regardless of axis orientation, etc...
268 //      int width, height;
269 //      dc.GetSize(&width, &height);
270         QSize winSize = size();
271
272 //      dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
273 //      dc.SetAxisOrientation(true, true);
274         p.translate(QPoint(-offsetX, winSize.height() - (-offsetY)));
275         p.scale(1.0, -1.0);
276
277 // Scrolling can be done by using OffsetViewportOrgEx
278 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
279 // you'd use: % = ViewportExt / WindowExt
280 // But it makes the window look like crap: fuggetuboutit.
281 // Instead, we have to scale EVERYTHING by hand. Crap!
282 // It's not *that* bad, but not as convenient either...
283
284 //      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0xFF), 1, wxDOT)));
285 ////    dc.DrawLine(0, 0, 10, 10);
286         p.setPen(QPen(Qt::blue, 1.0, Qt::DotLine));
287
288     // Draw coordinate axes
289
290 //      dc.CrossHair(0, 0);
291         p.drawLine(0, -16384, 0, 16384);
292         p.drawLine(-16384, 0, 16384, 0);
293
294     // Draw points
295
296         for(int i=0; i<pts.GetNumPoints(); i++)
297         {
298                 if (i == ptHighlight)
299                 {
300 //                      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
301 ////                    SelectObject(hdc, hRedPen1);
302                         p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
303
304                         if (pts.GetOnCurve(i))
305                         {
306                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
307                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
308                         }
309                         else
310                         {
311                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
312                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
313                         }
314                 }
315                 else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
316                 {
317 //                      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0xAF, 0x00), 1, wxSOLID)));
318 ////                    SelectObject(hdc, hGreenPen1);
319                         p.setPen(QPen(Qt::green, 1.0, Qt::SolidLine));
320
321                         if (pts.GetOnCurve(i))
322                         {
323                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
324                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
325                         }
326                         else
327                         {
328                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
329                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
330                         }
331                 }
332                 else
333                 {
334 //                      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
335 ////                    SelectObject(hdc, hBlackPen1);
336                         p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
337
338                         if (pts.GetOnCurve(i))
339                                 DrawSquareDot(p, pts.GetX(i), pts.GetY(i));
340                         else
341                                 DrawRoundDot(p, pts.GetX(i), pts.GetY(i));
342                 }
343
344                 if (tool == TOOLDelPt && i == ptHighlight)
345                 {
346 #if 0
347                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
348 //                      SelectObject(hdc, hRedPen1);
349 //                      MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
350 //                      LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
351 //                      LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
352 //                      MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
353 //                      LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
354 //                      LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
355 #endif
356                         p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
357                         p.drawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
358                         p.drawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
359                 }
360         }
361
362 ////            SelectObject(hdc, hBlackPen1);
363 //      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
364         p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
365
366         // Draw curve formed by points
367
368         for(int poly=0; poly<pts.GetNumPolys(); poly++)
369         {
370                 if (pts.GetNumPoints(poly) > 2)
371                 {
372                         // Initial move...
373                         // If it's not on curve, then move to it, otherwise move to last point...
374
375                         int x, y;
376
377                         if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
378                                 x = (int)pts.GetX(poly, pts.GetNumPoints(poly) - 1), y = (int)pts.GetY(poly, pts.GetNumPoints(poly) - 1);
379                         else
380                                 x = (int)pts.GetX(poly, 0), y = (int)pts.GetY(poly, 0);
381
382                         for(int i=0; i<pts.GetNumPoints(poly); i++)
383                         {
384                                 if (pts.GetOnCurve(poly, i))
385 //                                      LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
386                                 {
387                                         p.drawLine(x, y, pts.GetX(poly, i), pts.GetY(poly, i));
388                                         x = (int)pts.GetX(poly, i), y = (int)pts.GetY(poly, i);
389                                 }
390                                 else
391                                 {
392                                         uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
393                                         float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
394                                                 nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
395
396                                         if (!pts.GetOnCurve(poly, prev))
397                                                 px = (px + pts.GetX(poly, i)) / 2.0f,
398                                                 py = (py + pts.GetY(poly, i)) / 2.0f;
399
400                                         if (!pts.GetOnCurve(poly, next))
401                                                 nx = (nx + pts.GetX(poly, i)) / 2.0f,
402                                                 ny = (ny + pts.GetY(poly, i)) / 2.0f;
403
404                                         Bezier(p, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
405                                         x = (int)nx, y = (int)ny;
406
407                                         if (pts.GetOnCurve(poly, next))
408                                                 i++;                                    // Following point is on curve, so move past it
409                                 }
410                         }
411                 }
412         }
413 }
414
415 void DrawingView::mousePressEvent(QMouseEvent * event)
416 {
417         if (event->button() == Qt::RightButton)
418         {
419                 toolPalette->move(event->globalPos());
420                 toolPalette->setVisible(true);
421                 setCursor(cur[TOOLSelect]);
422                 toolPalette->prevTool = TOOLSelect;
423         }
424         else if (event->button() == Qt::MidButton)
425         {
426                 setCursor(cur[2]);                                                      // Scrolling cursor
427         }
428         else if (event->button() == Qt::LeftButton)
429         {
430                 if (tool == TOOLScroll || tool == TOOLZoom)
431 ;//meh                  CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
432                 else if (tool == TOOLAddPt)             // "Add Point" tool
433                 {
434                         if (pts.GetNumPoints() > 0)
435                         {
436                                 QPoint pt = GetAdjustedMousePosition(event);
437                                 pts.InsertPoint(pts.GetNext(ptHighlight), pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
438                                 ptHighlight = ptNextHighlight;
439                                 update();
440                         }
441                 }
442                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
443                 {
444 #ifdef DEBUGFOO
445 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
446 #endif
447                         if (polyFirstPoint)
448                         {
449                                 polyFirstPoint = false;
450                                 pts.AddNewPolyAtEnd();
451                         }
452
453                         QPoint pt = GetAdjustedMousePosition(event);
454 //printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
455                         // Append a point to the end of the structure
456                         pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
457                         ptHighlight = pts.GetNumPoints() - 1;
458                         update();
459 #ifdef DEBUGFOO
460 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
461 #endif
462                 }
463                 else if (tool == TOOLSelect || tool == TOOLPolySelect)
464                 {
465                         if (pts.GetNumPoints() > 0)
466                         {
467                                 pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
468 //printf("GetAdjustedClientPosition = %i, %i\n", pt.x(), pt.y());
469 //                              WarpPointer(pt.x, pt.y);
470                                 QCursor::setPos(mapToGlobal(pt));
471
472                                 if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
473                                 {
474                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
475                                         update();
476                                 }
477                         }
478                 }
479                 else if (tool == TOOLDelPt)
480                 {
481                         if (pts.GetNumPoints() > 0)
482 //Or could use:
483 //                      if (ptHighlight != -1)
484                         {
485 //This assumes that WM_MOUSEMOVE happens before this!
486 //The above commented out line should take care of this contingency... !!! FIX !!!
487                                 pts.DeletePoint(ptHighlight);
488                                 update();
489                         }
490                 }
491         }
492
493         event->accept();
494 }
495
496 void DrawingView::mouseMoveEvent(QMouseEvent * event)
497 {
498         if (event->buttons() == Qt::RightButton)
499         {
500                 ToolType newTool = toolPalette->FindSelectedTool();
501
502                 if (newTool != toolPalette->prevTool)
503                 {
504                         toolPalette->prevTool = newTool;
505                         toolPalette->repaint();
506                 }
507         }
508         else if (event->buttons() == Qt::MidButton)
509         {
510                 // Calc offset from previous point
511                 pt = event->pos();
512                 ptOffset = QPoint(pt.x() - ptPrevious.x(), pt.y() - ptPrevious.y());
513
514 // Then multiply it by the scaling factor. Whee!
515                 // This looks wacky because we're using screen coords for the offset...
516                 // Otherwise, we would subtract both offsets!
517                 offsetX -= ptOffset.x(), offsetY += ptOffset.y();
518                 update();
519                 ptPrevious = pt;
520         }
521         else if (event->buttons() == Qt::LeftButton)
522         {
523 #if 0
524                         if (tool == TOOLScroll)
525                         {
526                             // Extract current point from lParam/calc offset from previous point
527
528                                 pt = e.GetPosition();
529                                 ptOffset.x = pt.x - ptPrevious.x,
530                                 ptOffset.y = pt.y - ptPrevious.y;
531
532                                 // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
533
534 //Seems there's no equivalent for this in wxWidgets...!
535 //!!! FIX !!!
536 //                              hdc = GetDC(hWnd);
537 //                              OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
538 //                              ReleaseDC(hWnd, hdc);
539
540 // this shows that it works, so the logic above must be faulty...
541 // And it is. It should convert the coords first, then do the subtraction to figure the offset...
542 // Above: DONE
543 // Then multiply it by the scaling factor. Whee!
544                                 // This looks wacky because we're using screen coords for the offset...
545                                 // Otherwise, we would subtract both offsets!
546                                 offsetX -= ptOffset.x, offsetY += ptOffset.y;
547                                 Refresh();
548                         }
549                         else
550 #endif
551                         if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
552                         {
553                                 if (tool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
554                                 {
555 //temporary, for testing. BTW, Select drag bug is here...!
556 #if 1
557                                         QPoint pt2 = GetAdjustedMousePosition(event);
558                                         pts.SetXY(ptHighlight, pt2.x(), pt2.y());
559                                         update();
560 #endif
561                                 }
562                         }
563                         else if (tool == TOOLPolySelect)
564                         {
565                                 if (pts.GetNumPoints() > 0)
566                                 {
567                                         QPoint pt2 = GetAdjustedMousePosition(event);
568                                         // Should also set onCurve here as well, depending on keystate
569 //Or should we?
570                                         pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x() - pts.GetX(ptHighlight), pt2.y() - pts.GetY(ptHighlight));
571                                         update();
572                                 }
573                         }
574         }
575         else if (event->buttons() == Qt::NoButton)
576         {
577                 // Moving, not dragging...
578                 if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
579                         || tool == TOOLPolySelect)// || tool == TOOLAddPoly)
580                 {
581                         QPoint pt2 = GetAdjustedMousePosition(event);
582                         double closest = 1.0e+99;
583
584                         for(int i=0; i<pts.GetNumPoints(); i++)
585                         {
586                                 double dist = ((pt2.x() - pts.GetX(i)) * (pt2.x() - pts.GetX(i)))
587                                         + ((pt2.y() - pts.GetY(i)) * (pt2.y() - pts.GetY(i)));
588
589                                 if (dist < closest)
590                                         closest = dist, ptHighlight = i;
591                         }
592
593                         if (ptHighlight != oldPtHighlight)
594                         {
595                                 oldPtHighlight = ptHighlight;
596                                 update();
597                         }
598
599                         // What follows here looks like voodoo, but is really simple. What we do is
600                         // check to see if the mouse point has a perpendicular intersection with any of
601                         // the line segments. If it does, calculate the length of the perpendicular
602                         // and choose the smallest length. If there is no perpendicular, then choose the
603                         // length of line connecting the closer of either the first endpoint or the
604                         // second and choose the smallest of those.
605
606                         // There is one bit of math that looks like voodoo to me ATM--will explain once
607                         // I understand it better (the calculation of the length of the perpendicular).
608
609                         if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
610                         {
611                                 double smallest = 1.0e+99;
612
613                                 for(int i=0; i<pts.GetNumPoints(); i++)
614                                 {
615                                         int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
616                                                 p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
617
618                                         vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x(), pt2.y(), 0, p1x, p1y, 0),
619                                                 v2(pt2.x(), pt2.y(), 0, p2x, p2y, 0);
620                                         double pp = ls.dot(v1) / ls.length(), dist;
621 // Geometric interpretation:
622 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
623 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
624 // then the perpendicular lies beyond the 2nd endpoint.
625
626                                         if (pp < 0.0)
627                                                 dist = v1.length();
628                                         else if (pp > ls.length())
629                                                 dist = v2.length();
630                                         else                                    // distance = ?Det?(ls, v1) / |ls|
631                                                 dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.length());
632
633 //The answer to the above looks like it might be found here:
634 //
635 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
636 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
637 //{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
638 //computed by first computing the area of the triangle the three points form, then dividing by the
639 //length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
640 //triangle formed by three points is the determinant of the following matrix:
641 //
642 //sx sy 1
643 //ex ey 1
644 //px py 1
645 //
646 //By translating the start point to the origin, this can be rewritten as:
647 //By subtracting row 1 from all rows, you get the following:
648 //[because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
649 // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
650 //
651 //0         0         0
652 //(ex - sx) (ey - sy) 0
653 //(px - sx) (py - sy) 0
654 //
655 //which greatly simplifies the calculation of the determinant.
656
657                                         if (dist < smallest)
658                                                 smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
659                                 }
660
661                                 if (ptNextHighlight != oldPtNextHighlight)
662                                 {
663                                         oldPtNextHighlight = ptNextHighlight;
664                                         update();
665                                 }
666                         }
667                 }
668
669                 ptPrevious = event->pos();
670         }
671
672         event->accept();
673 }
674
675 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
676 {
677         if (event->button() == Qt::RightButton)
678         {
679                 ToolType newTool = toolPalette->FindSelectedTool();
680
681                 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
682                 if (newTool != TOOLNone)
683                 {
684                         tool = newTool;
685
686                         if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
687                                 || tool == TOOLDelPoly)
688                                 ptHighlight = -1;
689
690                         if (tool == TOOLAddPoly)
691                                 polyFirstPoint = true;
692                 }
693
694                 toolPalette->setVisible(false);
695                 setCursor(cur[tool]);
696                 // Just in case we changed highlighting style with the new tool...
697                 update();
698         }
699         else if (event->button() == Qt::MidButton)
700         {
701                 setCursor(cur[tool]);                                           // Restore previous cursor
702         }
703         else if (event->button() == Qt::LeftButton)
704         {
705 //              if (tool == TOOLScroll || tool == TOOLZoom)
706 //                      ReleaseMouse();
707 //this is prolly too much
708                 ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
709                 ((TTEdit *)qApp)->charWnd->update();
710         }
711
712         event->accept();
713 }
714 #endif