+#include "editwindow.h"
+//#include <QtGui>
+#include "graphicprimitives.h"
+#include "debug.h"
+#include "vector.h"
+
+EditWindow::EditWindow(QWidget * parent/*=NULL*/): QWidget(parent),
+ scale(1.0), offsetX(-10), offsetY(-10), tool(TOOLSelect),
+ ptHighlight(-1), oldPtHighlight(-1), ptNextHighlight(-1), oldPtNextHighlight(-1),
+ polyFirstPoint(true)
+{
+ setBackgroundRole(QPalette::Base);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+ toolPalette = new ToolWindow();
+ CreateCursors();
+ setCursor(cur[TOOLSelect]);
+ setMouseTracking(true);
+}
+
+QSize EditWindow::minimumSizeHint() const
+{
+ return QSize(50, 50);
+}
+
+QSize EditWindow::sizeHint() const
+{
+ return QSize(400, 400);
+}
+
+void EditWindow::CreateCursors(void)
+{
+ int hotx[8] = { 1, 1, 11, 15, 1, 1, 1, 1 };
+ int hoty[8] = { 1, 1, 11, 13, 1, 1, 1, 1 };
+
+ for(int i=0; i<8; i++)
+ {
+ QString s;
+ s.sprintf(":/res/cursor%u.png", i+1);
+ QPixmap pmTmp(s);
+ cur[i] = QCursor(pmTmp, hotx[i], hoty[i]);
+ }
+}
+
+QPoint EditWindow::GetAdjustedMousePosition(QMouseEvent * event)
+{
+ QSize winSize = size();
+ // This is undoing the transform, e.g. going from client coords to local coords.
+ // In essence, the height - y is height + (y * -1), the (y * -1) term doing the conversion
+ // of the y-axis from increasing bottom to top.
+ return QPoint(offsetX + event->x(), offsetY + (winSize.height() - event->y()));
+}
+
+QPoint EditWindow::GetAdjustedClientPosition(int x, int y)
+{
+ QSize winSize = size();
+
+ // VOODOO ALERT (ON Y COMPONENT!!!!)
+ return QPoint(-offsetX + x, (winSize.height() - (-offsetY + y)) * +1.0);
+}
+
+void EditWindow::paintEvent(QPaintEvent * /*event*/)
+{
+ QPainter p(this);
+//hm, causes lockup
+// p.setRenderHint(QPainter::Antialiasing);
+//Doesn't do crap!
+//dc.SetBackground(*wxWHITE_BRUSH);
+
+// Due to the screwiness of wxWidgets coord system, the origin is ALWAYS
+// the upper left corner--regardless of axis orientation, etc...
+// int width, height;
+// dc.GetSize(&width, &height);
+ QSize winSize = size();
+
+// dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
+// dc.SetAxisOrientation(true, true);
+ p.translate(QPoint(-offsetX, winSize.height() - (-offsetY)));
+ p.scale(1.0, -1.0);
+
+// Scrolling can be done by using OffsetViewportOrgEx
+// Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
+// you'd use: % = ViewportExt / WindowExt
+// But it makes the window look like crap: fuggetuboutit.
+// Instead, we have to scale EVERYTHING by hand. Crap!
+// It's not *that* bad, but not as convenient either...
+
+// dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0xFF), 1, wxDOT)));
+//// dc.DrawLine(0, 0, 10, 10);
+ p.setPen(QPen(Qt::blue, 1.0, Qt::DotLine));
+
+ // Draw coordinate axes
+
+// dc.CrossHair(0, 0);
+ p.drawLine(0, -16384, 0, 16384);
+ p.drawLine(-16384, 0, 16384, 0);
+
+ // Draw points
+
+ for(int i=0; i<pts.GetNumPoints(); i++)
+ {
+ if (i == ptHighlight)
+ {
+// dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
+//// SelectObject(hdc, hRedPen1);
+ p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
+
+ if (pts.GetOnCurve(i))
+ {
+ DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
+ DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
+ }
+ else
+ {
+ DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
+ DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
+ }
+ }
+ else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
+ {
+// dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0xAF, 0x00), 1, wxSOLID)));
+//// SelectObject(hdc, hGreenPen1);
+ p.setPen(QPen(Qt::green, 1.0, Qt::SolidLine));
+
+ if (pts.GetOnCurve(i))
+ {
+ DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
+ DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
+ }
+ else
+ {
+ DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
+ DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
+ }
+ }
+ else
+ {
+// dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
+//// SelectObject(hdc, hBlackPen1);
+ p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+
+ if (pts.GetOnCurve(i))
+ DrawSquareDot(p, pts.GetX(i), pts.GetY(i));
+ else
+ DrawRoundDot(p, pts.GetX(i), pts.GetY(i));
+ }
+
+ if (tool == TOOLDelPt && i == ptHighlight)
+ {
+#if 0
+ dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
+// SelectObject(hdc, hRedPen1);
+// MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
+// LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
+// LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
+// MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
+// LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
+// LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
+#endif
+ p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
+ p.drawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
+ p.drawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
+ }
+ }
+
+//// SelectObject(hdc, hBlackPen1);
+// dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
+ p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+
+ // Draw curve formed by points
+
+ for(int poly=0; poly<pts.GetNumPolys(); poly++)
+ {
+ if (pts.GetNumPoints(poly) > 2)
+ {
+ // Initial move...
+ // If it's not on curve, then move to it, otherwise move to last point...
+
+ int x, y;
+
+ if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
+ x = (int)pts.GetX(poly, pts.GetNumPoints(poly) - 1), y = (int)pts.GetY(poly, pts.GetNumPoints(poly) - 1);
+ else
+ x = (int)pts.GetX(poly, 0), y = (int)pts.GetY(poly, 0);
+
+ for(int i=0; i<pts.GetNumPoints(poly); i++)
+ {
+ if (pts.GetOnCurve(poly, i))
+// LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
+ {
+ p.drawLine(x, y, pts.GetX(poly, i), pts.GetY(poly, i));
+ x = (int)pts.GetX(poly, i), y = (int)pts.GetY(poly, i);
+ }
+ else
+ {
+ uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
+ float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
+ nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
+
+ if (!pts.GetOnCurve(poly, prev))
+ px = (px + pts.GetX(poly, i)) / 2.0f,
+ py = (py + pts.GetY(poly, i)) / 2.0f;
+
+ if (!pts.GetOnCurve(poly, next))
+ nx = (nx + pts.GetX(poly, i)) / 2.0f,
+ ny = (ny + pts.GetY(poly, i)) / 2.0f;
+
+ Bezier(p, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
+ x = (int)nx, y = (int)ny;
+
+ if (pts.GetOnCurve(poly, next))
+ i++; // Following point is on curve, so move past it
+ }
+ }
+ }
+ }
+}
+
+void EditWindow::mousePressEvent(QMouseEvent * event)
+{
+ if (event->button() == Qt::RightButton)
+ {
+ toolPalette->move(event->globalPos());
+ toolPalette->setVisible(true);
+ setCursor(cur[TOOLSelect]);
+ toolPalette->prevTool = TOOLSelect;
+ }
+ else if (event->button() == Qt::MidButton)
+ {
+ setCursor(cur[2]); // Scrolling cursor
+ }
+ else if (event->button() == Qt::LeftButton)
+ {
+ if (tool == TOOLScroll || tool == TOOLZoom)
+;//meh CaptureMouse(); // Make sure we capture the mouse when in scroll/zoom mode
+ else if (tool == TOOLAddPt) // "Add Point" tool
+ {
+ if (pts.GetNumPoints() > 0)
+ {
+ QPoint pt = GetAdjustedMousePosition(event);
+ pts.InsertPoint(pts.GetNext(ptHighlight), pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
+ ptHighlight = ptNextHighlight;
+ update();
+ }
+ }
+ else if (tool == TOOLAddPoly) // "Add Poly" tool
+ {
+#ifdef DEBUGFOO
+WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
+#endif
+ if (polyFirstPoint)
+ {
+ polyFirstPoint = false;
+ pts.AddNewPolyAtEnd();
+ }
+
+ QPoint pt = GetAdjustedMousePosition(event);
+//printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
+ // Append a point to the end of the structure
+ pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
+ ptHighlight = pts.GetNumPoints() - 1;
+ update();
+#ifdef DEBUGFOO
+WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
+#endif
+ }
+ else if (tool == TOOLSelect || tool == TOOLPolySelect)
+ {
+ if (pts.GetNumPoints() > 0)
+ {
+ pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
+//printf("GetAdjustedClientPosition = %i, %i\n", pt.x(), pt.y());
+// WarpPointer(pt.x, pt.y);
+ QCursor::setPos(mapToGlobal(pt));
+
+ if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
+ {
+ pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
+ update();
+ }
+ }
+ }
+ else if (tool == TOOLDelPt)
+ {
+ if (pts.GetNumPoints() > 0)
+//Or could use:
+// if (ptHighlight != -1)
+ {
+//This assumes that WM_MOUSEMOVE happens before this!
+//The above commented out line should take care of this contingency... !!! FIX !!!
+ pts.DeletePoint(ptHighlight);
+ update();
+ }
+ }
+ }
+
+ event->accept();
+}
+
+void EditWindow::mouseMoveEvent(QMouseEvent * event)
+{
+ if (event->buttons() == Qt::RightButton)
+ {
+ ToolType newTool = toolPalette->FindSelectedTool();
+
+ if (newTool != toolPalette->prevTool)
+ {
+ toolPalette->prevTool = newTool;
+ toolPalette->repaint();
+ }
+ }
+ else if (event->buttons() == Qt::MidButton)
+ {
+ // Calc offset from previous point
+ pt = event->pos();
+ ptOffset = QPoint(pt.x() - ptPrevious.x(), pt.y() - ptPrevious.y());
+
+// Then multiply it by the scaling factor. Whee!
+ // This looks wacky because we're using screen coords for the offset...
+ // Otherwise, we would subtract both offsets!
+ offsetX -= ptOffset.x(), offsetY += ptOffset.y();
+ update();
+ ptPrevious = pt;
+ }
+ else if (event->buttons() == Qt::LeftButton)
+ {
+#if 0
+ if (tool == TOOLScroll)
+ {
+ // Extract current point from lParam/calc offset from previous point
+
+ pt = e.GetPosition();
+ ptOffset.x = pt.x - ptPrevious.x,
+ ptOffset.y = pt.y - ptPrevious.y;
+
+ // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
+
+//Seems there's no equivalent for this in wxWidgets...!
+//!!! FIX !!!
+// hdc = GetDC(hWnd);
+// OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
+// ReleaseDC(hWnd, hdc);
+
+// this shows that it works, so the logic above must be faulty...
+// And it is. It should convert the coords first, then do the subtraction to figure the offset...
+// Above: DONE
+// Then multiply it by the scaling factor. Whee!
+ // This looks wacky because we're using screen coords for the offset...
+ // Otherwise, we would subtract both offsets!
+ offsetX -= ptOffset.x, offsetY += ptOffset.y;
+ Refresh();
+ }
+ else
+#endif
+ if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
+ {
+ if (tool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
+ {
+//temporary, for testing. BTW, Select drag bug is here...!
+#if 1
+ QPoint pt2 = GetAdjustedMousePosition(event);
+ pts.SetXY(ptHighlight, pt2.x(), pt2.y());
+ update();
+#endif
+ }
+ }
+ else if (tool == TOOLPolySelect)
+ {
+ if (pts.GetNumPoints() > 0)
+ {
+ QPoint pt2 = GetAdjustedMousePosition(event);
+ // Should also set onCurve here as well, depending on keystate
+//Or should we?
+ pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x() - pts.GetX(ptHighlight), pt2.y() - pts.GetY(ptHighlight));
+ update();
+ }
+ }
+ }
+ else if (event->buttons() == Qt::NoButton)
+ {
+ // Moving, not dragging...
+ if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
+ || tool == TOOLPolySelect)// || tool == TOOLAddPoly)
+ {
+ QPoint pt2 = GetAdjustedMousePosition(event);
+ double closest = 1.0e+99;
+
+ for(int i=0; i<pts.GetNumPoints(); i++)
+ {
+ double dist = ((pt2.x() - pts.GetX(i)) * (pt2.x() - pts.GetX(i)))
+ + ((pt2.y() - pts.GetY(i)) * (pt2.y() - pts.GetY(i)));
+
+ if (dist < closest)
+ closest = dist, ptHighlight = i;
+ }
+
+ if (ptHighlight != oldPtHighlight)
+ {
+ oldPtHighlight = ptHighlight;
+ update();
+ }
+
+ // What follows here looks like voodoo, but is really simple. What we do is
+ // check to see if the mouse point has a perpendicular intersection with any of
+ // the line segments. If it does, calculate the length of the perpendicular
+ // and choose the smallest length. If there is no perpendicular, then choose the
+ // length of line connecting the closer of either the first endpoint or the
+ // second and choose the smallest of those.
+
+ // There is one bit of math that looks like voodoo to me ATM--will explain once
+ // I understand it better (the calculation of the length of the perpendicular).
+
+ if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
+ {
+ double smallest = 1.0e+99;
+
+ for(int i=0; i<pts.GetNumPoints(); i++)
+ {
+ int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
+ p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
+
+ vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x(), pt2.y(), 0, p1x, p1y, 0),
+ v2(pt2.x(), pt2.y(), 0, p2x, p2y, 0);
+ double pp = ls.dot(v1) / ls.length(), dist;
+// Geometric interpretation:
+// pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
+// If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
+// then the perpendicular lies beyond the 2nd endpoint.
+
+ if (pp < 0.0)
+ dist = v1.length();
+ else if (pp > ls.length())
+ dist = v2.length();
+ else // distance = ?Det?(ls, v1) / |ls|
+ dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.length());
+
+//The answer to the above looks like it might be found here:
+//
+//If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
+//intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
+//{e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is
+//computed by first computing the area of the triangle the three points form, then dividing by the
+//length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the
+//triangle formed by three points is the determinant of the following matrix:
+//
+//sx sy 1
+//ex ey 1
+//px py 1
+//
+//(???) By translating the start point to the origin, this can be rewritten as:
+//By subtracting row 1 from all rows, you get the following:
+//
+//0 0 0
+//(ex - sx) (ey - sy) 0
+//(px - sx) (py - sy) 0
+//
+//which greatly simplifies the calculation of the determinant.
+
+ if (dist < smallest)
+ smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
+ }
+
+ if (ptNextHighlight != oldPtNextHighlight)
+ {
+ oldPtNextHighlight = ptNextHighlight;
+ update();
+ }
+ }
+ }
+
+ ptPrevious = event->pos();
+ }
+
+ event->accept();
+}
+
+void EditWindow::mouseReleaseEvent(QMouseEvent * event)
+{
+ if (event->button() == Qt::RightButton)
+ {
+ ToolType newTool = toolPalette->FindSelectedTool();
+
+ // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
+ if (newTool != TOOLNone)
+ {
+ tool = newTool;
+
+ if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
+ || tool == TOOLDelPoly)
+ ptHighlight = -1;
+
+ if (tool == TOOLAddPoly)
+ polyFirstPoint = true;
+ }
+
+ toolPalette->setVisible(false);
+ setCursor(cur[tool]);
+ // Just in case we changed highlighting style with the new tool...
+ update();
+ }
+ else if (event->button() == Qt::MidButton)
+ {
+ setCursor(cur[tool]); // Restore previous cursor
+ }
+ else if (event->button() == Qt::LeftButton)
+ {
+// if (tool == TOOLScroll || tool == TOOLZoom)
+// ReleaseMouse();
+ }
+
+ event->accept();
+}
+
+
+
+#if 0