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