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