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