Note that the scrolling & single point selection are still a bit wonky.
// CHARNAMES.CPP
//
// A header file that links Unicode character names to character numbers.
-// by James L. Hammons
+// by James Hammons
// (C) 2004 Underground Software
//
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH ??/??/200? Created this file
//
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH 08/28/2008 Created this file
// JLH 03/19/2009 Converted from wxWidgets to Qt
// JLH 03/21/2009 Fixed main screen points rendering
// JLH = James Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- ------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH 07/31/2002 Created this file
// JLH 07/31/2002 Added debug log functions & system error logging functions
// JLH 08/16/2002 Added debug log function for SQL error reporting, made
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH 08/28/2008 Created this file
// JLH 09/02/2008 Separated scrolling from dedicated tool to MMB drag
// JLH 03/13/2009 Converted from wxWidgets to Qt
#include "editwindow.h"
#include "charwindow.h"
#include "debug.h"
-#include "graphicprimitives.h"
+#include "global.h"
#include "mainwindow.h"
#include "ttedit.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), showRotationCenter(false), haveZeroPoint(false)
+ polyFirstPoint(true), showRotationCenter(false), haveZeroPoint(false),
+ selectionInProgress(false)
{
setBackgroundRole(QPalette::Base);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
CreateCursors();
setCursor(cur[TOOLSelect]);
setMouseTracking(true);
+ ClearSelection();
}
void EditWindow::CreateCursors(void)
{
- int hotx[11] = { 1, 1, 11, 15, 1, 1, 1, 1, 1, 1, 1 };
- int hoty[11] = { 1, 1, 11, 13, 1, 1, 1, 1, 1, 1, 1 };
- char cursorName[11][48] = { "select", "select-poly", "scroll", "zoom", "add-point",
- "add-poly", "del-point", "del-poly", "rotate", "rotate", "select" };
+ int hotx[12] = { 1, 1, 1, 15, 1, 1, 1, 1, 1, 1, 1, 11 };
+ int hoty[12] = { 1, 1, 1, 13, 1, 1, 1, 1, 1, 1, 1, 11 };
+ char cursorName[12][48] = { "select", "select-poly", "select-multi", "zoom",
+ "add-point", "add-poly", "del-point", "del-poly", "rotate", "rotate",
+ "select", "scroll" };
- for(int i=0; i<11; i++)
+ for(int i=0; i<12; i++)
{
QString s;
s.sprintf(":/res/cursor-%s.png", cursorName[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()));
+ // 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 + (size().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);
+ return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
}
*/
void EditWindow::paintEvent(QPaintEvent * /*event*/)
{
- QPainter p(this);
+ QPainter qtp(this);
+ Painter painter(&qtp);
//hm, causes lockup (or does it???)
- p.setRenderHint(QPainter::Antialiasing);
-
- QSize winSize = size();
-
- p.translate(QPoint(-offsetX, winSize.height() - (-offsetY)));
- p.scale(1.0, -1.0);
+//& it doesn't help with our Bezier rendering code :-P
+ painter.SetRenderHint(QPainter::Antialiasing);
+
+ Global::zoom = scale;
+ Global::origin = Vector(offsetX, offsetY);
+ Global::viewportHeight = size().height();
+ Global::screenSize = Vector(size().width(), size().height());
+// p.translate(QPoint(-offsetX, size().height() - (-offsetY)));
+// p.scale(1.0, -1.0);
+//Nope, a big load of shit. So we'll have to bite the bullet and do it the
+//hard way™.
+// p.scale(scale, -scale);
// Scrolling can be done by using OffsetViewportOrgEx
// Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
// Instead, we have to scale EVERYTHING by hand. Crap!
// It's not *that* bad, but not as convenient either...
- p.setPen(QPen(Qt::blue, 1.0, Qt::DotLine));
+ painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
// Draw coordinate axes
- p.drawLine(0, -16384, 0, 16384);
- p.drawLine(-16384, 0, 16384, 0);
+ painter.DrawLine(0, -16384, 0, 16384);
+ painter.DrawLine(-16384, 0, 16384, 0);
// Draw rotation center (if active)
if (showRotationCenter)
{
- p.setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
- p.drawLine(rotationCenter.x() + 7, rotationCenter.y(), rotationCenter.x() - 7, rotationCenter.y());
- p.drawLine(rotationCenter.x(), rotationCenter.y() + 7, rotationCenter.x(), rotationCenter.y() - 7);
+ painter.SetPen(QPen(Qt::red, 2.0, Qt::SolidLine));
+ painter.DrawLine(rotationCenter.x + 7, rotationCenter.y, rotationCenter.x - 7, rotationCenter.y);
+ painter.DrawLine(rotationCenter.x, rotationCenter.y + 7, rotationCenter.x, rotationCenter.y - 7);
}
// Draw points
for(int i=0; i<pts.GetNumPoints(); i++)
{
- if (i == ptHighlight)
+ /*if (tool == TOOLMultiSelect)
{
- p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
+ if (selectedPoints[i])
+ {
+ qtp.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
+ }
+ else
+ {
+ qtp.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+ }
+ }
+ else*/
+ if (((i == ptHighlight) && (tool != TOOLMultiSelect)) || ((tool == TOOLMultiSelect) && selectedPoints[i]))
+ {
+ painter.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);
+ painter.DrawSquareDotN(pts.GetXY(i), 7);
+ painter.DrawSquareDotN(pts.GetXY(i), 9);
}
else
{
- DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
- DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
+ painter.DrawRoundDotN(pts.GetXY(i), 7);
+ painter.DrawRoundDotN(pts.GetXY(i), 9);
}
}
else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
{
- p.setPen(QPen(Qt::green, 1.0, Qt::SolidLine));
+ painter.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);
+ painter.DrawSquareDotN(pts.GetXY(i), 7);
+ painter.DrawSquareDotN(pts.GetXY(i), 9);
}
else
{
- DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
- DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
+ painter.DrawRoundDotN(pts.GetXY(i), 7);
+ painter.DrawRoundDotN(pts.GetXY(i), 9);
}
}
else
{
- p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+ painter.SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+#if 1
if (pts.GetOnCurve(i))
- DrawSquareDot(p, pts.GetX(i), pts.GetY(i));
+ painter.DrawSquareDot(pts.GetXY(i));
else
- DrawRoundDot(p, pts.GetX(i), pts.GetY(i));
+ painter.DrawRoundDot(pts.GetXY(i));
+#else
+ (pts.GetOnCurve(i) ? DrawSquareDot(p, pts.GetX(i), pts.GetY(i))
+ : DrawRoundDot(p, pts.GetX(i), pts.GetY(i)));
+#endif
}
if (tool == TOOLDelPt && i == ptHighlight)
{
- 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);
+ painter.SetPen(QPen(Qt::red, 1.0, Qt::SolidLine));
+ painter.DrawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
+ painter.DrawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
}
}
// Draw curve formed by points
- p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
- DrawGlyph(p, pts);
+ painter.SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+ DrawGlyph(painter, pts);
if (haveZeroPoint)
{
GlyphPoints rotated = pts;
if (tool == TOOLRotate)
- rotated.RotatePoints(rotationAngle, IPoint(rotationCenter.x(), rotationCenter.y()));
+ rotated.RotatePoints(rotationAngle, IPoint(rotationCenter.x, rotationCenter.y));
else if (tool == TOOLRotatePoly)
{
uint16_t poly = rotated.GetPolyForPointNumber(ptHighlight);
rotated.RotatePolyAroundCentroid(poly, rotationAngle);
}
- p.setPen(QPen(QColor(255, 0, 255), 1.0, Qt::SolidLine));
- DrawGlyph(p, rotated);
+ painter.SetPen(QPen(QColor(255, 0, 255), 1.0, Qt::SolidLine));
+ DrawGlyph(painter, rotated);
+ }
+
+ if (selectionInProgress)
+ {
+ painter.SetPen(QPen(QColor(255, 127, 0, 255)));
+ painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
+ painter.DrawRect(selection);
}
}
-void EditWindow::DrawGlyph(QPainter & p, GlyphPoints & glyph)
+void EditWindow::DrawGlyph(Painter & p, GlyphPoints & glyph)
{
for(int poly=0; poly<glyph.GetNumPolys(); poly++)
{
}
-void EditWindow::DrawGlyphPoly(QPainter & p, GlyphPoints & glyph, uint16_t poly)
+void EditWindow::DrawGlyphPoly(Painter & p, GlyphPoints & glyph, uint16_t poly)
{
// Sanity check
if (glyph.GetNumPoints(poly) < 3)
if (glyph.GetOnCurve(poly, i) && glyph.GetNextOnCurve(poly, i))
{
IPoint pt2 = glyph.GetNextPoint(poly, i);
- p.drawLine(pt.x, pt.y, pt2.x, pt2.y);
+ p.DrawLine(pt.x, pt.y, pt2.x, pt2.y);
pt = pt2;
}
else
if (glyph.GetOnCurve(poly, i))
continue;
- // We are now guaranteed that we are sitting on a curve control point
- // (off curve). Figure the extent of the curve: If the following is a
- // curve control point, then use the midpoint to it otherwise go to
- // the next point since it's on curve.
+ // We are now guaranteed that we are sitting on a curve control
+ // point (off curve). Figure the extent of the curve: If the
+ // following is a curve control point, then use the midpoint to it
+ // otherwise go to the next point since it's on curve.
IPoint pt2 = (glyph.GetNextOnCurve(poly, i)
? glyph.GetNextPoint(poly, i) : glyph.GetMidpointToNext(poly, i));
- Bezier(p, pt, glyph.GetPoint(poly, i), pt2);
+ p.DrawBezier(pt, glyph.GetPoint(poly, i), pt2);
pt = pt2;
}
}
}
+void EditWindow::ClearSelection(void)
+{
+ for(int i=0; i<65536; i++)
+ selectedPoints[i] = false;
+}
+
+
void EditWindow::mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::RightButton)
}
else if (event->button() == Qt::MidButton)
{
- setCursor(cur[2]); // Scrolling cursor
+ // Scrolling cursor
+ setCursor(cur[11]);
+ ptPrevious = Vector(event->x(), event->y());
+ ptPrevious /= Global::zoom;
}
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 == TOOLMultiSelect)
+ {
+// QPoint pt = GetAdjustedMousePosition(event);
+ Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+ selectionInProgress = true;
+ selection.setTopLeft(QPoint(pt.x, pt.y));
+ selection.setBottomRight(QPoint(pt.x, pt.y));
+ }
else if (tool == TOOLAddPt) // "Add Point" tool
{
- QPoint pt = GetAdjustedMousePosition(event);
- IPoint pointToAdd(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
+// QPoint pt = GetAdjustedMousePosition(event);
+ Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+ IPoint pointToAdd(pt.x, pt.y, ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
if (pts.GetNumPoints() < 2)
{
pts.AddNewPolyAtEnd();
}
- QPoint pt = GetAdjustedMousePosition(event);
+// QPoint pt = GetAdjustedMousePosition(event);
+ Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
//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));
+ pts += IPoint(pt.x, pt.y, ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
ptHighlight = pts.GetNumPoints() - 1;
update();
#ifdef DEBUGFOO
{
if (pts.GetNumPoints() > 0)
{
- pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
+// pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
+ Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
//printf("GetAdjustedClientPosition = %i, %i\n", pt.x(), pt.y());
// WarpPointer(pt.x, pt.y);
- QCursor::setPos(mapToGlobal(pt));
+ QPoint warp(pt.x, pt.y);
+ QCursor::setPos(mapToGlobal(warp));
if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
{
// paint the rotation center, then use the 1st mouse move event to establish
// the rotation "zero line", which becomes the line of reference to all
// subsequent mouse moves.
- rotationCenter = GetAdjustedMousePosition(event);
+// rotationCenter = GetAdjustedMousePosition(event);
+ rotationCenter = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
showRotationCenter = true;
haveZeroPoint = false;
rotationAngle = 0;
else if (tool == TOOLRotatePoly)
{
IPoint centroid = pts.GetPolyCentroid(pts.GetPolyForPointNumber(ptHighlight));
- rotationCenter = QPoint(centroid.x, centroid.y);
+ rotationCenter = Vector(centroid.x, centroid.y);
showRotationCenter = true;
- pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
- QCursor::setPos(mapToGlobal(pt));
- rotationZeroPoint = QPoint(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
+// pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
+ Vector pt = Painter::QtToCartesianCoords(Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight)));
+ QCursor::setPos(mapToGlobal(QPoint(pt.x, pt.y)));
+ rotationZeroPoint = Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
haveZeroPoint = true;
rotationAngle = 0;
update();
else if (tool == TOOLFlipWinding)
{
pts.InvertPolyDrawSequence(pts.GetPolyForPointNumber(ptHighlight));
- pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
- QCursor::setPos(mapToGlobal(pt));
+// pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
+ Vector pt = Painter::QtToCartesianCoords(Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight)));
+ QCursor::setPos(mapToGlobal(QPoint(pt.x, pt.y)));
update();
}
}
else if (event->buttons() == Qt::MidButton)
{
// Calc offset from previous point
- pt = event->pos();
- ptOffset = QPoint(pt.x() - ptPrevious.x(), pt.y() - ptPrevious.y());
+ Vector pt(event->x(), event->y());
+ pt /= Global::zoom;
+ ptOffset = Vector(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();
+ offsetX -= ptOffset.x, offsetY += ptOffset.y;
update();
ptPrevious = pt;
}
if (tool == TOOLSelect && pts.GetNumPoints() == 0)
return;
- QPoint pt2 = GetAdjustedMousePosition(event);
- pts.SetXY(ptHighlight, pt2.x(), pt2.y());
+// QPoint pt2 = GetAdjustedMousePosition(event);
+ Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+ pts.SetXY(ptHighlight, pt2.x, pt2.y);
update();
}
else if (tool == TOOLPolySelect)
{
if (pts.GetNumPoints() > 0)
{
- QPoint pt2 = GetAdjustedMousePosition(event);
+// QPoint pt2 = GetAdjustedMousePosition(event);
+ Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
// Should also set onCurve here as well, depending on keystate
//Or should we?
//Would be nice, but we'd need to trap the keyPressEvent() as well, otherwise pressing/releasing
//the hotkey would show no change until the user moved their mouse.
- pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x() - pts.GetX(ptHighlight), pt2.y() - pts.GetY(ptHighlight));
+ pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
update();
}
}
{
if (!haveZeroPoint)
{
- rotationZeroPoint = GetAdjustedMousePosition(event);
+// rotationZeroPoint = GetAdjustedMousePosition(event);
+ rotationZeroPoint = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
haveZeroPoint = true;
}
else
{
// Figure out the angle between the "zero" vector and the current one,
// then rotate all points relative to the "zero" vector (done by paint())
- QPoint currentPoint = GetAdjustedMousePosition(event);
- Vector v1(rotationZeroPoint.x(), rotationZeroPoint.y(), 0,
- rotationCenter.x(), rotationCenter.y(), 0);
- Vector v2(currentPoint.x(), currentPoint.y(), 0,
- rotationCenter.x(), rotationCenter.y(), 0);
+// QPoint currentPoint = GetAdjustedMousePosition(event);
+ Vector currentPoint = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+ Vector v1(rotationZeroPoint.x, rotationZeroPoint.y, 0,
+ rotationCenter.x, rotationCenter.y, 0);
+ Vector v2(currentPoint.x, currentPoint.y, 0,
+ rotationCenter.x, rotationCenter.y, 0);
// rotationAngle = v1.Angle(v2);
rotationAngle = v2.Angle(v1);
update();
}
}
+ else if (tool == TOOLMultiSelect)
+ {
+// QPoint pt = GetAdjustedMousePosition(event);
+ Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+ selection.setBottomRight(QPoint(pt.x, pt.y));
+
+ for(int i=0; i<pts.GetNumPoints(); i++)
+ {
+ if (selection.contains(QPoint(pts.GetX(i), pts.GetY(i))))
+ selectedPoints[i] = true;
+ else
+ selectedPoints[i] = false;
+ }
+
+ update();
+ }
}
else if (event->buttons() == Qt::NoButton)
{
if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
|| tool == TOOLPolySelect || tool == TOOLRotatePoly || tool == TOOLFlipWinding)
{
- QPoint pt2 = GetAdjustedMousePosition(event);
+// QPoint pt2 = GetAdjustedMousePosition(event);
+ Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
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)));
+ 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;
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.
+ // 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).
+ // 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)
{
int32_t 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);
+ 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.Magnitude(), 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.
+// 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.Magnitude();
//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:
+//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
//
//By translating the start point to the origin, this can be rewritten as:
//By subtracting row 1 from all rows, you get the following:
-//[because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
-// row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
+//[because sx = sy = 0. you could leave out the -sx/y terms below. because we
+//subtracted row 1 from all rows (including row 1) row 1 turns out to be zero.
+//duh!]
//
//0 0 0
//(ex - sx) (ey - sy) 0
}
}
- ptPrevious = event->pos();
+ ptPrevious = Vector(event->x(), event->y());
}
event->accept();
haveZeroPoint = false;
if (tool == TOOLRotate)
- pts.RotatePoints(rotationAngle, IPoint(rotationCenter.x(), rotationCenter.y()));
+ pts.RotatePoints(rotationAngle, IPoint(rotationCenter.x, rotationCenter.y));
else
{
uint16_t poly = pts.GetPolyForPointNumber(ptHighlight);
((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
((TTEdit *)qApp)->charWnd->update();
+ if (tool == TOOLMultiSelect)
+ {
+ selectionInProgress = false;
+ update();
+ }
}
event->accept();
#include <stdint.h>
#include "toolwindow.h" // For ToolType enum
#include "glyphpoints.h"
+#include "painter.h"
+
class EditWindow: public QWidget
{
void CreateCursors(void);
QPoint GetAdjustedMousePosition(QMouseEvent *);
QPoint GetAdjustedClientPosition(int x, int y);
- void DrawGlyph(QPainter & p, GlyphPoints & glyph);
- void DrawGlyphPoly(QPainter & p, GlyphPoints & glyph, uint16_t poly);
+ void DrawGlyph(Painter & p, GlyphPoints & glyph);
+ void DrawGlyphPoly(Painter & p, GlyphPoints & glyph, uint16_t poly);
+ void ClearSelection(void);
public:
QImage image;
- QPoint pt, ptOffset, ptPrevious;
- double scale; // Window scaling factor
- int32_t offsetX, offsetY; // Window offsets
- ToolType tool; // Current tool
- GlyphPoints pts; // Glyph point structure
+ Vector pt, ptOffset, ptPrevious;
+ double scale; // Window scaling factor
+ int32_t offsetX, offsetY; // Window offsets
+ ToolType tool; // Current tool
+ GlyphPoints pts; // Glyph point structure
int32_t ptHighlight, oldPtHighlight, ptNextHighlight, oldPtNextHighlight;
int16_t polyHighlight, oldPolyHighlight;
bool polyFirstPoint;
bool showRotationCenter, haveZeroPoint;
- QPoint rotationCenter, rotationZeroPoint, rotationCurrentPoint;
+ Vector rotationCenter, rotationZeroPoint, rotationCurrentPoint;
double rotationAngle;
ToolWindow * toolPalette;
- QCursor cur[11];
+ QCursor cur[12];
+ bool selectedPoints[65536]; // Potential memory leak :-O
+ QRect selection;
+ bool selectionInProgress;
};
#endif // __EDITWINDOW_H__
--- /dev/null
+//
+// This page intentionally left (mostly) blank
+//
+
+#include "global.h"
+#include <QFont>
+//#include "structs.h"
+#include "toolwindow.h"
+
+// Initialize static variables
+
+bool Global::fixedAngle = false;
+bool Global::fixedLength = false;
+QFont * Global::font = 0;
+int Global::viewportHeight = 0;
+
+bool Global::deleteActive = false;
+bool Global::dimensionActive = false;
+bool Global::snapToGrid = true;
+
+//snapToPoints all well here?
+bool Global::ignoreClicks = false;
+bool Global::dontMove = false;
+bool Global::selectionInProgress = false;
+QRectF Global::selection;
+
+int Global::tool = TOOLSelect;
+//int Global::toolState = TSNone;
+
+double Global::gridSpacing;
+int Global::currentLayer = 0;
+Point Global::snapPoint;
+bool Global::snapPointIsValid = false;
+uint32_t Global::objectID = 1;
+
+Vector Global::origin(-10.0, -10.0);
+double Global::zoom = 1.0;
+Vector Global::screenSize(200.0, 200.0);
+
+float Global::scale = 0.5;
+
+Point Global::intersectPoint[16]; // Overkill, yes
+double Global::intersectParam[16]; // Ditto
+int Global::numIntersectPoints = 0;
+int Global::numIntersectParams = 0;
+
+int Global::activeLayer = 0;
+int Global::numLayers = 1;
+std::vector<bool> Global::layerHidden;
+std::vector<bool> Global::layerLocked;
+
--- /dev/null
+#ifndef __GLOBALS_H__
+#define __GLOBALS_H__
+
+// Global variable class. Note that all vars are class vars, so we don't have
+// to do any instantiation shite--it's treated as a namespace.
+
+#include <stdint.h>
+#include <vector>
+#include <QRectF>
+#include "vector.h"
+
+class QFont;
+
+enum LineStyle { LSNone, LSSolid, LSDash, LSDot, LSDashDot, LSDashDotDot };
+
+
+class Global
+{
+ public:
+ static double gridSpacing;
+ static bool selectionInProgress;
+ static QRectF selection;
+ static int currentLayer;
+ static QFont * font;
+ static Point snapPoint;
+ static bool snapPointIsValid;
+
+ static bool fixedAngle;
+ static bool fixedLength;
+ static int viewportHeight;
+ static bool deleteActive;
+ static bool dimensionActive;
+
+ static bool snapToGrid;
+ static bool ignoreClicks;
+ static bool dontMove;
+ static uint32_t objectID;
+ static int tool;
+ static int toolState;
+
+ static Point origin;
+ static double zoom;
+ static Vector screenSize;
+
+ static float scale;
+
+ static Point intersectPoint[16]; // Overkill, yes
+ static double intersectParam[16]; // Ditto
+ static int numIntersectPoints;
+ static int numIntersectParams;
+
+ static int activeLayer;
+ static int numLayers;
+ static std::vector<bool> layerHidden;
+ static std::vector<bool> layerLocked;
+};
+
+#endif // __GLOBALS_H__
+
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- ------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH ??/??/200? Created this file
// JLH 05/18/2004 Added pure point adding, inserting, better polygon handling
//
void GlyphPoints::InsertPoint(uint16_t pt, int xx, int yy, bool oc)
{
+//wouldn't it be better to treat this case as inserting at the end?
if (pt > numPoints) // > because we can insert at end...!
throw GP_OUT_OF_RANGE;
//we're adding to the end of the structure: [DONE, below]
int polyInsert = (pt == numPoints ? numPolys - 1 : GetPoly(pt));
for(int i=polyInsert; i<numPolys; i++)
-// for(int i=GetPoly(pt); i<numPolys; i++)
polyEnd[i]++; // Bump polygons' end point
numPoints++;
}
+Vector GlyphPoints::GetXY(uint16_t pt)
+{
+ if (pt >= numPoints)
+#ifdef DEBUG
+{
+WriteLogMsg("Exception: GetXY(uint16_t). pt=%u, numPoints=%u\xD\xA", pt, numPoints);
+#endif
+ throw GP_OUT_OF_RANGE;
+#ifdef DEBUG
+}
+#endif
+
+ return Vector(x[pt], y[pt]);
+}
+
+
bool GlyphPoints::GetOnCurve(uint16_t pt)
{
if (pt >= numPoints)
#include <stdint.h>
#include <stdio.h>
+#include "vector.h"
// "IPoint" is an Integer based Point
uint16_t GetNumPolys(void);
int GetX(uint16_t);
int GetY(uint16_t);
+ Vector GetXY(uint16_t);
bool GetOnCurve(uint16_t);
int GetX(uint16_t, uint16_t);
int GetNextX(uint16_t, uint16_t);
+++ /dev/null
-//
-// Graphics primitives
-//
-// Various graphic functions that are slightly more complex than those that
-// come with various widget libraries.
-//
-// by James L. Hammons
-// (C) 2005 Underground Software
-//
-// JLH = James L. Hammons <jlhamm@acm.org>
-//
-// Who When What
-// --- ---------- -------------------------------------------------------------
-// JLH 03/14/1998 Created this file
-// JLH 01/20/2005 Converted to use wxWidgets
-// JLH 08/30/2008 Repurposed file to handle more than just bezier curves
-//
-
-#include "graphicprimitives.h"
-
-double abs(double n) // Helper function
-{
- return (n < 0 ? -n : n);
-}
-
-
-//
-// This function takes three points and draws a curve using a second order
-// Bezier function.
-//
-void Bezier(QPainter &p, point p1, point p2, point p3)
-{
- double step = abs(p1.x - p3.x), tmp = abs(p1.y - p3.y);
- step = (tmp > step ? tmp : step); // Get the larger of the two...
- step = (step > 0 ? 1/step : 1); // & convert to valid step value
- step *= 2.0; // (double it to draw less...)
-
- int prevX = (int)p1.x, prevY = (int)p1.y;
-
- for(double u=0; u<=1; u+=step)
- {
- double _2u = 2*u, _uu = u*u, _2uu = 2*_uu;
- double x = (p1.x * (1 - _2u + _uu)) + (p2.x * (_2u - _2uu)) + (p3.x * _uu);
- double y = (p1.y * (1 - _2u + _uu)) + (p2.y * (_2u - _2uu)) + (p3.y * _uu);
-
- p.drawLine(prevX, prevY, (int)x, (int)y);
- prevX = (int)x, prevY = (int)y;
- }
-
- p.drawLine(prevX, prevY, (int)p3.x, (int)p3.y);
-}
-
-
-//
-// This is a convenience funtion, using IPoints :-)
-//
-void Bezier(QPainter &p, IPoint p1, IPoint p2, IPoint p3)
-{
- Bezier(p, point(p1.x, p1.y), point(p2.x, p2.y), point(p3.x, p3.y));
-}
-
-
-//
-// Draw a round dot (5x5, centered on [x, y])
-//
-void DrawRoundDot(QPainter &p, int32_t x, int32_t y)
-{
- QPoint pt[8];
-
- pt[0] = QPoint(x - 1, y - 2);
- pt[1] = QPoint(x + 1, y - 2);
- pt[2] = QPoint(x + 2, y - 1);
- pt[3] = QPoint(x + 2, y + 1);
- pt[4] = QPoint(x + 1, y + 2);
- pt[5] = QPoint(x - 1, y + 2);
- pt[6] = QPoint(x - 2, y + 1);
- pt[7] = QPoint(x - 2, y - 1);
-
- p.drawPolygon(pt, 8);
-}
-
-
-//
-// Draw a sqaure dot (5x5, centered on [x, y])
-//
-void DrawSquareDot(QPainter &p, int32_t x, int32_t y)
-{
- QPoint pt[4];
-
- pt[0] = QPoint(x - 2, y - 2);
- pt[1] = QPoint(x + 2, y - 2);
- pt[2] = QPoint(x + 2, y + 2);
- pt[3] = QPoint(x - 2, y + 2);
-
- p.drawPolygon(pt, 4);
-}
-
-
-//
-// Draw a sqaure dot (nxn, centered on [x, y])
-//
-void DrawSquareDotN(QPainter &p, int32_t x, int32_t y, uint32_t n)
-{
- QPoint pt[4];
- uint32_t offset = (n - 1) / 2;
-
- pt[0] = QPoint(x - offset, y - offset);
- pt[1] = QPoint(x + offset, y - offset);
- pt[2] = QPoint(x + offset, y + offset);
- pt[3] = QPoint(x - offset, y + offset);
-
- p.drawPolygon(pt, 4);
-}
-
-
-//
-// Draw a round dot (nxn, centered on [x, y])
-//
-void DrawRoundDotN(QPainter &p, int32_t x, int32_t y, uint32_t n)
-{
- int radius = (n / 2) + 1;
- p.drawEllipse(x - radius, y - radius, n, n);
-}
+++ /dev/null
-//
-// Graphics primitives
-//
-// Various graphic functions that are slightly more complex than those that
-// come with various widget libraries.
-//
-
-#ifndef __GRAPHICPRIMITIVES_H__
-#define __GRAPHICPRIMITIVES_H__
-
-#include <stdint.h>
-#include <QtGui> // For QPainter
-#include "glyphpoints.h" // For IPoint
-
-struct point
-{
- float x, y;
-
- point(float xx = 0, float yy = 0): x(xx), y(yy) {}
-};
-
-void Bezier(QPainter &, point, point, point);
-void Bezier(QPainter &, IPoint, IPoint, IPoint);
-void DrawRoundDot(QPainter &, int32_t, int32_t);
-void DrawSquareDot(QPainter &, int32_t, int32_t);
-void DrawRoundDotN(QPainter &, int32_t, int32_t, uint32_t);
-void DrawSquareDotN(QPainter &, int32_t, int32_t, uint32_t);
-
-#endif // __GRAPHICPRIMITIVES_H__
//
// A generic list class using a circular singly linked list
-// by James L. Hammons
-// (C) 2004 Underground Software
+// by James Hammons
+// (C) 2016 Underground Software
//
-// Based upon work I did for CS240 Project 5 at Cal Poly Pomona for Dr. Bruce Hillam.
-// Hello Dr. Hillam! :-)
+// Based upon work I did for CS240 Project 5 at Cal Poly Pomona for Dr. Bruce
+// Hillam. Hello Dr. Hillam! :-)
//
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH 08/30/1999 Created this file
// JLH 05/11/2004 Cleaned up a few things in the implementation
//
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH 04/10/2002 Created this file
// JLH 05/10/2004 Translated file from ASM to CPP
// JLH 05/14/2004 Added rudimentary editing capability to tool palette tools
// FIXED:
//
-// - Fix problem with tool palette not getting focus 1st time it's called up [DONE]
+// - Fix problem with tool palette not getting focus 1st time it's called up
+// [DONE]
// - Split out windows/classes defined here into their own files [DONE]
//
// STILL TO BE DONE:
setCentralWidget(editWnd);
editWnd->setFocus();
setWindowIcon(QIcon(":/res/ttedit.png"));
- setWindowTitle("TTEdit!");
+ setWindowTitle("TTEdit! - Untitled");
#if 0
// createActions();
#endif
// Create status bar
+ scaleIndicator = new QLabel("Scale: 100%");
+ statusBar()->addPermanentWidget(scaleIndicator);
statusBar()->showMessage(tr("Ready"));
ReadSettings();
//
// Consolidates action creation from a multi-step process to a single-step one.
//
-QAction * MainWindow::CreateAction(QString name, QString tooltip, QString statustip,
- QIcon icon, QKeySequence key, bool checkable/*= false*/)
+QAction * MainWindow::CreateAction(QString name, QString tooltip, QString
+ statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
{
QAction * action = new QAction(icon, name, this);
action->setToolTip(tooltip);
// This is essentially the same as the previous function, but this allows more
// than one key sequence to be added as key shortcuts.
//
-QAction * MainWindow::CreateAction(QString name, QString tooltip, QString statustip,
- QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
+QAction * MainWindow::CreateAction(QString name, QString tooltip, QString
+ statustip, QIcon icon, QKeySequence key1, QKeySequence key2, bool
+ checkable/*= false*/)
{
QAction * action = new QAction(icon, name, this);
action->setToolTip(tooltip);
void MainWindow::CreateActions(void)
{
newGlyphAct = CreateAction("&New Glyph", "New Glyph", "Create a new glyph", QIcon(), QKeySequence());
- openFileAct = CreateAction("&Open File", "Open File", "Open a glyph file", QIcon(), QKeySequence());
- saveFileAct = CreateAction("&Save File", "Save File", "Save a glyph file", QIcon(), QKeySequence());
+ openFileAct = CreateAction("&Open File", "Open File", "Open a glyph file", QIcon(), QKeySequence("ctrl+o"));
+ saveFileAct = CreateAction("&Save File", "Save File", "Save a glyph file", QIcon(), QKeySequence("ctrl+s"));
+ quitAct = CreateAction("&Quit", "Quit", "Exits from the TTEdit application", QIcon(), QKeySequence("ctrl+q"));
+ zoomInAct = CreateAction("Zoom In", "Zoom In", "Zoom into the canvas", QIcon(), QKeySequence("+"), QKeySequence("="));
+ zoomOutAct = CreateAction("Zoom Out", "Zoom Out", "Zoom out of canvas", QIcon(), QKeySequence("-"));
connect(newGlyphAct, SIGNAL(triggered()), this, SLOT(NewGlyph()));
connect(openFileAct, SIGNAL(triggered()), this, SLOT(OpenFile()));
connect(saveFileAct, SIGNAL(triggered()), this, SLOT(SaveFile()));
+ connect(quitAct, SIGNAL(triggered()), this, SLOT(close()));
+ connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomIn()));
+ connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOut()));
}
menu->addAction(newGlyphAct);
menu->addAction(openFileAct);
menu->addAction(saveFileAct);
+ menu->addSeparator();
+ menu->addAction(quitAct);
// menu->addAction(fileSaveAsAct);
// menu->addAction(fileCloseAct);
+ menu = menuBar()->addMenu(tr("&View"));
+ menu->addAction(zoomInAct);
+ menu->addAction(zoomOutAct);
}
((TTEdit *)qApp)->charWnd->update();
// editWnd->polyFirstPoint = true;
editWnd->update();
+ setWindowTitle("TTEdit! - Untitled");
+ editWnd->setFocus();
}
((TTEdit *)qApp)->charWnd->MakePathFromPoints(&(editWnd->pts));
((TTEdit *)qApp)->charWnd->update();
editWnd->update();
+ setWindowTitle(QString("TTEdit! - %1").arg(filename));
+ editWnd->setFocus();
}
editWnd->pts.SaveGlyphToFile(file);
fclose(file);
+ setWindowTitle(QString("TTEdit! - %1").arg(filename));
+ editWnd->setFocus();
+}
+
+
+void MainWindow::ZoomIn(void)
+{
+ if (editWnd->scale == 4.0)
+ return;
+
+ editWnd->scale *= 2.0;
+ scaleIndicator->setText(QString("Scale: %1%").arg(editWnd->scale * 100.0));
+ editWnd->update();
+}
+
+
+void MainWindow::ZoomOut(void)
+{
+ if (editWnd->scale == 0.25)
+ return;
+
+ editWnd->scale *= 0.5;
+ scaleIndicator->setText(QString("Scale: %1%").arg(editWnd->scale * 100.0));
+ editWnd->update();
}
void NewGlyph(void);
void OpenFile(void);
void SaveFile(void);
+ void ZoomIn(void);
+ void ZoomOut(void);
private:
QAction * CreateAction(QString name, QString tooltip, QString statustip,
EditWindow * editWnd;
// CharWindow * charWnd;
+ QLabel * scaleIndicator;
#if 0
private:
protected:
QAction * newGlyphAct;
QAction * openFileAct;
QAction * saveFileAct;
+ QAction * quitAct;
+ QAction * zoomInAct;
+ QAction * zoomOutAct;
+ QAction * scaleAct;
+// QString filename;
};
#endif // __MAINWINDOW_H__
--- /dev/null
+// Mathematical Constants used by Architektonas
+//
+// Part of the Architektonas Project
+// (C) 2011 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// NOTE: Since this has no code associated with it, there is no corresponding
+// .cpp file.
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO WHEN WHAT
+// --- ---------- ------------------------------------------------------------
+// JLH 04/01/2011 Created this file
+//
+
+#define PI 3.14159265358979323846264338327
+#define PI_OVER_2 (PI / 2.0)
+#define PI3_OVER_2 ((3.0 * PI) / 2.0)
+#define PI_TIMES_2 (PI * 2.0)
+#define RADIANS_TO_DEGREES (180.0 / PI)
+#define DEGREES_TO_RADIANS (PI / 180.0)
+
--- /dev/null
+//
+// painter.cpp: Paint abstraction layer between Archtektonas and Qt
+//
+// Part of the Architektonas Project
+// (C) 2011 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO WHEN WHAT
+// --- ---------- ------------------------------------------------------------
+// JLH 09/20/2011 Created this file
+//
+
+#include "painter.h"
+#include "global.h"
+#include "mathconstants.h"
+
+
+// Set class variable defaults
+//Vector Painter::origin(-10.0, -10.0);
+//double Painter::zoom = 1.0;
+//Vector Painter::screenSize(200.0, 200.0);
+
+
+Painter::Painter(QPainter * p/*= NULL*/): painter(p)
+{
+}
+
+
+Painter::~Painter()
+{
+}
+
+
+Vector Painter::CartesianToQtCoords(Vector v)
+{
+ // Convert regular Cartesian coordinates to the inverted Y-axis Qt
+ // coordinates at the current origin and zoom level.
+ return Vector((v.x - Global::origin.x) * Global::zoom, Global::screenSize.y - ((v.y - Global::origin.y) * Global::zoom));
+}
+
+
+Vector Painter::QtToCartesianCoords(Vector v)
+{
+ // Convert screen location, with inverted Y-axis coordinates, to regular
+ // Cartesian coordinates at the current zoom level.
+ return Vector((v.x / Global::zoom) + Global::origin.x, ((Global::screenSize.y - v.y) / Global::zoom) + Global::origin.y);
+/*
+How to do it:
+
+e.g., we have a point on the screen at Qt coords of 10, 10, screenSize is 100, 100.
+origin is -10, -10 and zoom level is 2 (200%)
+
+1st, invert the Y: 10, 10 -> 10, 90
+2nd, add origin: 10, 90 -> 0, 80 (no, not right--err, yes, it is)
+3rd, aply zoom: 0, 80 -> 0, 40
+
+or, is it:
+
+1st, invert the Y: 10, 10 -> 10, 90
+2nd, aply zoom: 10, 90 -> 5, 45
+3rd, add origin: 5, 45 -> -5, 35
+
+it depends on whether or not origin is in Qt coords or cartesian. If Qt, then the 1st
+is correct, otherwise, the 2nd is correct.
+
+The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
+*/
+}
+
+
+void Painter::SetRenderHint(int hint)
+{
+ if (!painter)
+ return;
+
+ painter->setRenderHint((QPainter::RenderHint)hint);
+}
+
+
+void Painter::SetPen(QPen pen)
+{
+ if (!painter)
+ return;
+
+ painter->setPen(pen);
+}
+
+
+void Painter::SetPen(uint32_t color, float size/*= 0*/, int style/*= 0*/)
+{
+ if (!painter)
+ return;
+
+ // We can cast style as Qt:PenStyle because they line up 1-to-1
+ painter->setPen(QPen(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255),
+ size, (Qt::PenStyle)style));
+}
+
+
+void Painter::SetBrush(QBrush brush)
+{
+ if (!painter)
+ return;
+
+ painter->setBrush(brush);
+}
+
+
+void Painter::SetBrush(uint32_t color)
+{
+ if (!painter)
+ return;
+
+ painter->setBrush(QBrush(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255)));
+}
+
+
+void Painter::SetFont(QFont font)
+{
+ if (!painter)
+ return;
+
+ painter->setFont(font);
+}
+
+
+void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
+{
+ if (!painter)
+ return;
+
+ // Strategy: Since Qt doesn't have any rotated text drawing functions,
+ // we instead translate the origin to the center of the text to be drawn and
+ // then rotate the frame to the desired angle.
+ center = CartesianToQtCoords(center);
+
+ // We may need this stuff... If dimension text is large enough.
+// int textWidth = QFontMetrics(painter->font()).width(text);
+// int textHeight = QFontMetrics(painter->font()).height();
+ QRectF textBox(-100.0 * Global::zoom * size, -100.0 * Global::zoom * size, 200.0 * Global::zoom * size, 200.0 * Global::zoom * size); // x, y, w, h; x/y = upper left corner
+
+ // This is in pixels. Might not render correctly at all zoom levels.
+ // Need to figure out if dimensions are always rendered at one size
+ // regardless of zoom, or if they have a definite size, and are thus
+ // zoomable.
+ float yOffset = -12.0 * Global::zoom * size;
+
+ // Fix text so it isn't upside down...
+ if ((angle > PI * 0.5) && (angle < PI * 1.5))
+ {
+ angle += PI;
+ yOffset = 12.0 * Global::zoom * size;
+ }
+
+ textBox.translate(0, yOffset);
+ painter->save();
+ painter->translate(center.x, center.y);
+ // Angles are backwards in the Qt coord system, so we flip ours...
+ painter->rotate(-angle * RADIANS_TO_DEGREES);
+//Need to fix this so the text scales as well...
+ painter->drawText(textBox, Qt::AlignCenter, text);
+ painter->restore();
+}
+
+
+//
+// Draw angled text. Draws text using point p as the upper left corner.
+// Size is point size, angle is in radians (defaults to 0).
+//
+void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
+{
+ if (!painter)
+ return;
+
+ p = CartesianToQtCoords(p);
+ painter->setFont(QFont("Arial", Global::zoom * size));
+ int textWidth = QFontMetrics(painter->font()).width(text);
+ int textHeight = QFontMetrics(painter->font()).height();
+
+ QRectF textBox(0, 0, textWidth, textHeight);
+ painter->save();
+ painter->translate(p.x, p.y);
+ // Angles are backwards in the Qt coord system, so we flip ours...
+ painter->rotate(-angle * RADIANS_TO_DEGREES);
+ painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
+ painter->restore();
+}
+
+
+void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
+{
+ center = CartesianToQtCoords(center);
+ // Need to multiply scalar quantities by the zoom factor as well...
+ radius *= Global::zoom;
+ QRectF rectangle(QPointF(center.x - radius, center.y - radius),
+ QPointF(center.x + radius, center.y + radius));
+ int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
+ int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
+ painter->drawArc(rectangle, angle1, angle2);
+}
+
+
+void Painter::DrawEllipse(Vector center, double axis1, double axis2)
+{
+ // Need to multiply scalar quantities by the zoom factor as well...
+ center = CartesianToQtCoords(center);
+ painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
+}
+
+
+// This function is for drawing object handles without regard for zoom level;
+// we don't want our object handle size to depend on the zoom level!
+void Painter::DrawHandle(Vector center)
+{
+ center = CartesianToQtCoords(center);
+ painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
+}
+
+
+// This function is for drawing object handles without regard for zoom level;
+// we don't want our object handle size to depend on the zoom level!
+void Painter::DrawArrowHandle(Vector center, double angle)
+{
+ center = CartesianToQtCoords(center);
+ QPolygonF arrow;
+
+ // Since we're drawing directly on the screen, the Y is inverted. So we use
+ // the mirror of the angle.
+ double orthoAngle = -angle + (PI / 2.0);
+ Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
+ Vector unit = Vector(cos(-angle), sin(-angle));
+
+ Point p0 = center + (unit * 6.0);
+ Point p1 = center + (unit * 21.0);
+ Point p1b = center + (unit * 11.0);
+ Point p2 = p1b + (orthogonal * 5.0);
+ Point p3 = p1b - (orthogonal * 5.0);
+
+ painter->drawLine(p0.x, p0.y, p1.x, p1.y);
+ arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
+
+ painter->drawPolygon(arrow);
+}
+
+
+// This function is for drawing object handles without regard for zoom level;
+// we don't want our object handle size to depend on the zoom level!
+void Painter::DrawArrowToLineHandle(Vector center, double angle)
+{
+ DrawArrowHandle(center, angle);
+ center = CartesianToQtCoords(center);
+
+ // Since we're drawing directly on the screen, the Y is inverted. So we use
+ // the mirror of the angle.
+ double orthoAngle = -angle + (PI / 2.0);
+ Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
+ Vector unit = Vector(cos(-angle), sin(-angle));
+
+ Point p1 = center + (unit * 21.0);
+ Point p2 = p1 + (orthogonal * 7.0);
+ Point p3 = p1 - (orthogonal * 7.0);
+
+ painter->drawLine(p2.x, p2.y, p3.x, p3.y);
+}
+
+
+void Painter::DrawLine(int x1, int y1, int x2, int y2)
+{
+ if (!painter)
+ return;
+
+ Vector v1 = CartesianToQtCoords(Vector(x1, y1));
+ Vector v2 = CartesianToQtCoords(Vector(x2, y2));
+ painter->drawLine(v1.x, v1.y, v2.x, v2.y);
+}
+
+
+void Painter::DrawLine(Vector v1, Vector v2)
+{
+ if (!painter)
+ return;
+
+ v1 = CartesianToQtCoords(v1);
+ v2 = CartesianToQtCoords(v2);
+ painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
+}
+
+
+void Painter::DrawPoint(int x, int y)
+{
+ if (!painter)
+ return;
+
+ Vector v = CartesianToQtCoords(Vector(x, y));
+ painter->drawPoint(v.x, v.y);
+}
+
+
+// The rect passed in is in Qt coordinates...
+void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
+{
+ if (!painter)
+ return;
+
+ painter->drawRoundedRect(rect, radiusX, radiusY);
+}
+
+
+// The rect passed in is in Cartesian but we want to pad it by a set number of
+// pixels (currently set at 8), so the pad looks the same regardless of zoom.
+void Painter::DrawPaddedRect(QRectF rect)
+{
+ if (!painter)
+ return;
+
+ Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
+ Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
+ QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
+ screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
+ painter->drawRect(screenRect);
+}
+
+
+void Painter::DrawRect(QRectF rect)
+{
+ if (!painter)
+ return;
+
+ Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
+ Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
+ QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
+ painter->drawRect(screenRect);
+}
+
+
+void Painter::DrawText(QRectF rect, int type, QString text)
+{
+ if (!painter)
+ return;
+
+ painter->drawText(rect, (Qt::AlignmentFlag)type, text);
+}
+
+
+void Painter::DrawArrowhead(Vector head, Vector tail, double size)
+{
+ if (!painter)
+ return;
+
+ QPolygonF arrow;
+
+ // We draw the arrowhead aligned along the line from tail to head
+ double angle = Vector(head - tail).Angle();
+ double orthoAngle = angle + (PI / 2.0);
+ Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
+ Vector unit = Vector(head - tail).Unit();
+
+ Point p1 = head - (unit * 9.0 * size);
+ Point p2 = p1 + (orthogonal * 3.0 * size);
+ Point p3 = p1 - (orthogonal * 3.0 * size);
+
+ Point p4 = CartesianToQtCoords(head);
+ Point p5 = CartesianToQtCoords(p2);
+ Point p6 = CartesianToQtCoords(p3);
+
+ arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
+
+ painter->drawPolygon(arrow);
+}
+
+
+// Point is given in Cartesian coordinates
+void Painter::DrawCrosshair(Vector point)
+{
+ if (!painter)
+ return;
+
+ Vector screenPoint = CartesianToQtCoords(point);
+ painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
+ painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
+}
+
+
+void Painter::DrawInformativeText(QString text)
+{
+ painter->setFont(*Global::font);
+ QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
+ bounds.moveTo(17.0, 17.0);
+ QRectF textRect = bounds;
+ textRect.adjust(-7.0, -7.0, 7.0, 7.0);
+
+ QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
+ painter->setPen(pen);
+ painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
+ painter->drawRoundedRect(textRect, 7.0, 7.0);
+
+ pen = QPen(QColor(0x00, 0x5F, 0xDF));
+ painter->setPen(pen);
+ painter->drawText(bounds, Qt::AlignVCenter, text);
+}
+
+
+void Painter::DrawBezier(Point p1, Point p2, Point p3)
+{
+ p1 = CartesianToQtCoords(p1);
+ p2 = CartesianToQtCoords(p2);
+ p3 = CartesianToQtCoords(p3);
+
+ QPainterPath path;
+ path.moveTo(p1.x, p1.y);
+ path.quadTo(p2.x, p2.y, p3.x, p3.y);
+ painter->drawPath(path);
+}
+
+
+void Painter::DrawBezier(IPoint p1, IPoint p2, IPoint p3)
+{
+ DrawBezier(Point(p1.x, p1.y), Point(p2.x, p2.y), Point(p3.x, p3.y));
+}
+
+
+//
+// Draw a sqaure dot (5x5, centered on Vector; non-scaled)
+//
+void Painter::DrawSquareDot(Vector v)
+{
+ QPoint pt[4];
+ v = CartesianToQtCoords(v);
+
+ pt[0] = QPoint(v.x - 2, v.y - 2);
+ pt[1] = QPoint(v.x + 2, v.y - 2);
+ pt[2] = QPoint(v.x + 2, v.y + 2);
+ pt[3] = QPoint(v.x - 2, v.y + 2);
+
+ painter->drawPolygon(pt, 4);
+}
+
+
+//
+// Draw a round dot (5x5, centered on Vector; non-scaled)
+//
+void Painter::DrawRoundDot(Vector v)
+{
+ QPoint pt[8];
+ v = CartesianToQtCoords(v);
+
+ pt[0] = QPoint(v.x - 1, v.y - 2);
+ pt[1] = QPoint(v.x + 1, v.y - 2);
+ pt[2] = QPoint(v.x + 2, v.y - 1);
+ pt[3] = QPoint(v.x + 2, v.y + 1);
+ pt[4] = QPoint(v.x + 1, v.y + 2);
+ pt[5] = QPoint(v.x - 1, v.y + 2);
+ pt[6] = QPoint(v.x - 2, v.y + 1);
+ pt[7] = QPoint(v.x - 2, v.y - 1);
+
+ painter->drawPolygon(pt, 8);
+}
+
+
+//
+// Draw a sqaure dot (nxn, centered on Vector; non-scaled)
+//
+void Painter::DrawSquareDotN(Vector v, uint32_t n)
+{
+ QPoint pt[4];
+ uint32_t offset = (n - 1) / 2;
+ v = CartesianToQtCoords(v);
+
+ pt[0] = QPoint(v.x - offset, v.y - offset);
+ pt[1] = QPoint(v.x + offset, v.y - offset);
+ pt[2] = QPoint(v.x + offset, v.y + offset);
+ pt[3] = QPoint(v.x - offset, v.y + offset);
+
+ painter->drawPolygon(pt, 4);
+}
+
+
+//
+// Draw a round dot (nxn, centered on Vector; non-scaled)
+//
+void Painter::DrawRoundDotN(Vector v, uint32_t n)
+{
+ int radius = (n / 2) + 1;
+ v = CartesianToQtCoords(v);
+ painter->drawEllipse(v.x - radius, v.y - radius, n, n);
+}
--- /dev/null
+#ifndef __PAINTER_H__
+#define __PAINTER_H__
+
+#include <stdint.h>
+#include <QtWidgets>
+#include "vector.h"
+#include "glyphpoints.h"
+
+//#define SCREEN_ZOOM (1.0 / 4.0)
+
+// Forward declarations
+
+class Painter
+{
+ public:
+ Painter(QPainter * p = 0);
+ ~Painter();
+
+ void SetRenderHint(int);
+ void SetPen(QPen);
+ void SetPen(uint32_t, float size = 0, int style = 0);
+ void SetBrush(QBrush);
+ void SetBrush(uint32_t);
+ void SetFont(QFont);
+ void DrawAngledText(Vector, double, QString, double);
+ void DrawTextObject(Vector, QString, double, double angle = 0);
+ void DrawArc(Vector, double, double, double);
+ void DrawEllipse(Vector, double, double);
+ void DrawHandle(Vector);
+ void DrawArrowHandle(Vector, double);
+ void DrawArrowToLineHandle(Vector, double);
+ void DrawLine(int, int, int, int);
+ void DrawLine(Vector, Vector);
+ void DrawPoint(int, int);
+ void DrawRoundedRect(QRectF, double, double);
+ void DrawPaddedRect(QRectF);
+ void DrawRect(QRectF);
+ void DrawText(QRectF, int, QString);
+ void DrawArrowhead(Vector, Vector, double);
+ void DrawCrosshair(Vector);
+ void DrawInformativeText(QString);
+ void DrawBezier(Point, Point, Point);
+ void DrawBezier(IPoint, IPoint, IPoint);
+ void DrawSquareDot(Vector);
+ void DrawRoundDot(Vector);
+ void DrawSquareDotN(Vector, uint32_t);
+ void DrawRoundDotN(Vector, uint32_t);
+
+ public:
+ static Vector CartesianToQtCoords(Vector);
+ static Vector QtToCartesianCoords(Vector);
+
+ public:
+ // Class variables
+// static Vector origin; // The window origin, not location of the origin
+// static double zoom; // Window zoom factor
+// static Vector screenSize; // Width & height of the window we're drawing on
+
+ private:
+ QPainter * painter;
+};
+
+#endif // __PAINTER_H__
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH 08/28/2008 Created this file
// JLH 03/11/2009 Converted from wxWidgets to Qt
//
newTool = (ToolType)((y * 4) + x);
// We don't have 11 yet, so fix this if the user selected the blank space
- if (newTool > 10)
- newTool = TOOLNone;
+//now we do!
+// if (newTool > 10)
+// newTool = TOOLNone;
return newTool;
}
+
TOOLNone = -1, // No tool
TOOLSelect = 0, // The "selection" tool
TOOLPolySelect, // Polygon selection tool
- TOOLScroll, // Scroll window tool
+ TOOLMultiSelect, // Rectangle selection tool
TOOLZoom, // Zoom window tool
TOOLAddPt, // Add point tool
TOOLAddPoly, // Polygon creation tool
TOOLDelPoly, // Delete polygon tool
TOOLRotate, // Rotate tool
TOOLRotatePoly, // Rotate polygon around centroid tool
- TOOLFlipWinding // Change polygon's winding direction tool
+ TOOLFlipWinding, // Change polygon's winding direction tool
+ TOOLScroll // Scroll window tool
};
class ToolWindow: public QWidget
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH 04/10/2002 Created this file
// JLH 05/10/2004 Translated file from ASM to CPP
// JLH 05/14/2004 Added rudimentary editing capability to tool palette tools
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH ??/??/199? Created this file
//
//
// JLH = James L. Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH ??/??/2003 Created original implementation
// JLH 05/14/2004 Separated header from implementation, added operator-
// function
// JLH = James L. Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
-// --- ---------- ------------------------------------------------------------
+// --- ---------- ----------------------------------------------------------
// JLH 09/19/2006 Created this file
// JLH 03/22/2011 Moved implementation of constructor from header to here
// JLH 04/02/2011 Fixed divide-by-zero bug in Unit(), added Angle() function
#CONFIG += qt debug
QT += widgets
-HEADERS += src/ttedit.h
-HEADERS += src/mainwindow.h
+HEADERS += src/charwindow.h
+HEADERS += src/debug.h
HEADERS += src/editwindow.h
+HEADERS += src/global.h
HEADERS += src/glyphpoints.h
-HEADERS += src/debug.h
+HEADERS += src/list.h
+HEADERS += src/mainwindow.h
+HEADERS += src/mathconstants.h
+HEADERS += src/painter.h
HEADERS += src/toolwindow.h
-HEADERS += src/charwindow.h
+HEADERS += src/ttedit.h
HEADERS += src/vector.h
-HEADERS += src/graphicprimitives.h
-HEADERS += src/list.h
-SOURCES += src/ttedit.cpp
-SOURCES += src/mainwindow.cpp
+SOURCES += src/charwindow.cpp
+SOURCES += src/debug.cpp
SOURCES += src/editwindow.cpp
+SOURCES += src/global.cpp
SOURCES += src/glyphpoints.cpp
-SOURCES += src/debug.cpp
+SOURCES += src/mainwindow.cpp
+SOURCES += src/painter.cpp
SOURCES += src/toolwindow.cpp
-SOURCES += src/charwindow.cpp
+SOURCES += src/ttedit.cpp
SOURCES += src/vector.cpp
-SOURCES += src/graphicprimitives.cpp
RESOURCES += ttedit.qrc
<qresource>
<file>res/cursor-select.png</file>
<file>res/cursor-select-poly.png</file>
+ <file>res/cursor-select-multi.png</file>
<file>res/cursor-scroll.png</file>
<file>res/cursor-zoom.png</file>
<file>res/cursor-add-point.png</file>