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