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