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