2 // painter.cpp: Paint abstraction layer between Archtektonas and Qt
4 // Part of the Architektonas Project
5 // (C) 2011 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
8 // JLH = James Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 09/20/2011 Created this file
17 #include "mathconstants.h"
20 // Set class variable defaults
21 //Vector Painter::origin(-10.0, -10.0);
22 //double Painter::zoom = 1.0;
23 //Vector Painter::screenSize(200.0, 200.0);
26 Painter::Painter(QPainter * p/*= NULL*/): painter(p)
36 Vector Painter::CartesianToQtCoords(Vector v)
38 // Convert regular Cartesian coordinates to the inverted Y-axis Qt
39 // coordinates at the current origin and zoom level.
40 return Vector((v.x - Global::origin.x) * Global::zoom, Global::screenSize.y - ((v.y - Global::origin.y) * Global::zoom));
44 Vector Painter::QtToCartesianCoords(Vector v)
46 // Convert screen location, with inverted Y-axis coordinates, to regular
47 // Cartesian coordinates at the current zoom level.
48 return Vector((v.x / Global::zoom) + Global::origin.x, ((Global::screenSize.y - v.y) / Global::zoom) + Global::origin.y);
52 e.g., we have a point on the screen at Qt coords of 10, 10, screenSize is 100, 100.
53 origin is -10, -10 and zoom level is 2 (200%)
55 1st, invert the Y: 10, 10 -> 10, 90
56 2nd, add origin: 10, 90 -> 0, 80 (no, not right--err, yes, it is)
57 3rd, aply zoom: 0, 80 -> 0, 40
61 1st, invert the Y: 10, 10 -> 10, 90
62 2nd, aply zoom: 10, 90 -> 5, 45
63 3rd, add origin: 5, 45 -> -5, 35
65 it depends on whether or not origin is in Qt coords or cartesian. If Qt, then the 1st
66 is correct, otherwise, the 2nd is correct.
68 The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
73 void Painter::SetRenderHint(int hint)
78 painter->setRenderHint((QPainter::RenderHint)hint);
82 void Painter::SetPen(QPen pen)
91 void Painter::SetPen(uint32_t color, float size/*= 0*/, int style/*= 0*/)
96 // We can cast style as Qt:PenStyle because they line up 1-to-1
97 painter->setPen(QPen(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255),
98 size, (Qt::PenStyle)style));
102 void Painter::SetBrush(QBrush brush)
107 painter->setBrush(brush);
111 void Painter::SetBrush(uint32_t color)
116 painter->setBrush(QBrush(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255)));
120 void Painter::SetFont(QFont font)
125 painter->setFont(font);
129 void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
134 // Strategy: Since Qt doesn't have any rotated text drawing functions,
135 // we instead translate the origin to the center of the text to be drawn and
136 // then rotate the frame to the desired angle.
137 center = CartesianToQtCoords(center);
139 // We may need this stuff... If dimension text is large enough.
140 // int textWidth = QFontMetrics(painter->font()).width(text);
141 // int textHeight = QFontMetrics(painter->font()).height();
142 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
144 // This is in pixels. Might not render correctly at all zoom levels.
145 // Need to figure out if dimensions are always rendered at one size
146 // regardless of zoom, or if they have a definite size, and are thus
148 float yOffset = -12.0 * Global::zoom * size;
150 // Fix text so it isn't upside down...
152 if ((angle > PI * 0.5) && (angle < PI * 1.5))
155 yOffset = 12.0 * Global::zoom * size;
158 if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
161 yOffset = 12.0 * Global::zoom * size;
165 textBox.translate(0, yOffset);
167 painter->translate(center.x, center.y);
168 // Angles are backwards in the Qt coord system, so we flip ours...
169 painter->rotate(-angle * RADIANS_TO_DEGREES);
170 //Need to fix this so the text scales as well...
171 painter->drawText(textBox, Qt::AlignCenter, text);
177 // Draw angled text. Draws text using point p as the upper left corner.
178 // Size is point size, angle is in radians (defaults to 0).
180 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
185 p = CartesianToQtCoords(p);
186 painter->setFont(QFont("Arial", Global::zoom * size));
187 int textWidth = QFontMetrics(painter->font()).width(text);
188 int textHeight = QFontMetrics(painter->font()).height();
190 QRectF textBox(0, 0, textWidth, textHeight);
192 painter->translate(p.x, p.y);
193 // Angles are backwards in the Qt coord system, so we flip ours...
194 painter->rotate(-angle * RADIANS_TO_DEGREES);
195 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
200 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
202 center = CartesianToQtCoords(center);
203 // Need to multiply scalar quantities by the zoom factor as well...
204 radius *= Global::zoom;
205 QRectF rectangle(QPointF(center.x - radius, center.y - radius),
206 QPointF(center.x + radius, center.y + radius));
207 int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
208 int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
209 painter->drawArc(rectangle, angle1, angle2);
213 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
215 // Need to multiply scalar quantities by the zoom factor as well...
216 center = CartesianToQtCoords(center);
217 painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
221 // This function is for drawing object handles without regard for zoom level;
222 // we don't want our object handle size to depend on the zoom level!
223 void Painter::DrawHandle(Vector center)
225 center = CartesianToQtCoords(center);
226 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
227 painter->setBrush(Qt::NoBrush);
228 painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
232 // This function is for drawing object handles without regard for zoom level;
233 // we don't want our object handle size to depend on the zoom level!
234 void Painter::DrawArrowHandle(Vector center, double angle)
236 center = CartesianToQtCoords(center);
239 // Since we're drawing directly on the screen, the Y is inverted. So we use
240 // the mirror of the angle.
242 double orthoAngle = -angle + (PI / 2.0);
244 double orthoAngle = -angle + QTR_TAU;
246 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
247 Vector unit = Vector(cos(-angle), sin(-angle));
249 Point p0 = center + (unit * 6.0);
250 Point p1 = center + (unit * 21.0);
251 Point p1b = center + (unit * 11.0);
252 Point p2 = p1b + (orthogonal * 5.0);
253 Point p3 = p1b - (orthogonal * 5.0);
255 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
256 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
258 painter->drawPolygon(arrow);
262 // This function is for drawing object handles without regard for zoom level;
263 // we don't want our object handle size to depend on the zoom level!
264 void Painter::DrawArrowToLineHandle(Vector center, double angle)
266 DrawArrowHandle(center, angle);
267 center = CartesianToQtCoords(center);
269 // Since we're drawing directly on the screen, the Y is inverted. So we use
270 // the mirror of the angle.
272 double orthoAngle = -angle + (PI / 2.0);
274 double orthoAngle = -angle + QTR_TAU;
276 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
277 Vector unit = Vector(cos(-angle), sin(-angle));
279 Point p1 = center + (unit * 21.0);
280 Point p2 = p1 + (orthogonal * 7.0);
281 Point p3 = p1 - (orthogonal * 7.0);
283 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
287 void Painter::DrawLine(int x1, int y1, int x2, int y2)
292 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
293 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
294 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
298 void Painter::DrawLine(IPoint p1, IPoint p2)
303 Vector v1 = CartesianToQtCoords(Vector(p1.x, p1.y));
304 Vector v2 = CartesianToQtCoords(Vector(p2.x, p2.y));
305 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
309 void Painter::DrawLine(Vector v1, Vector v2)
314 v1 = CartesianToQtCoords(v1);
315 v2 = CartesianToQtCoords(v2);
316 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
320 void Painter::DrawPoint(int x, int y)
325 Vector v = CartesianToQtCoords(Vector(x, y));
326 painter->drawPoint(v.x, v.y);
330 // The rect passed in is in Qt coordinates...
331 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
336 painter->drawRoundedRect(rect, radiusX, radiusY);
340 // The rect passed in is in Cartesian but we want to pad it by a set number of
341 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
342 void Painter::DrawPaddedRect(QRectF rect)
347 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
348 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
349 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
350 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
351 painter->drawRect(screenRect);
355 void Painter::DrawRect(QRectF rect)
360 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
361 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
362 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
363 painter->drawRect(screenRect);
367 void Painter::DrawText(QRectF rect, int type, QString text)
372 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
376 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
383 // We draw the arrowhead aligned along the line from tail to head
384 double angle = Vector(head - tail).Angle();
386 double orthoAngle = angle + (PI / 2.0);
388 double orthoAngle = angle + QTR_TAU;
390 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
391 Vector unit = Vector(head - tail).Unit();
393 Point p1 = head - (unit * 9.0 * size);
394 Point p2 = p1 + (orthogonal * 3.0 * size);
395 Point p3 = p1 - (orthogonal * 3.0 * size);
397 Point p4 = CartesianToQtCoords(head);
398 Point p5 = CartesianToQtCoords(p2);
399 Point p6 = CartesianToQtCoords(p3);
401 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
403 painter->drawPolygon(arrow);
407 // Point is given in Cartesian coordinates
408 void Painter::DrawCrosshair(Vector point)
413 Vector screenPoint = CartesianToQtCoords(point);
414 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
415 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
419 void Painter::DrawInformativeText(QString text)
421 painter->setFont(*Global::font);
422 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
423 bounds.moveTo(17.0, 17.0);
424 QRectF textRect = bounds;
425 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
427 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
428 painter->setPen(pen);
429 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
430 painter->drawRoundedRect(textRect, 7.0, 7.0);
432 pen = QPen(QColor(0x00, 0x5F, 0xDF));
433 painter->setPen(pen);
434 painter->drawText(bounds, Qt::AlignVCenter, text);
438 void Painter::DrawBezier(Point p1, Point p2, Point p3)
440 p1 = CartesianToQtCoords(p1);
441 p2 = CartesianToQtCoords(p2);
442 p3 = CartesianToQtCoords(p3);
445 path.moveTo(p1.x, p1.y);
446 path.quadTo(p2.x, p2.y, p3.x, p3.y);
447 painter->drawPath(path);
451 void Painter::DrawBezier(IPoint p1, IPoint p2, IPoint p3)
453 DrawBezier(Point(p1.x, p1.y), Point(p2.x, p2.y), Point(p3.x, p3.y));
458 // Draw a sqaure dot (5x5, centered on Vector; non-scaled)
460 void Painter::DrawSquareDot(Vector v)
463 v = CartesianToQtCoords(v);
465 pt[0] = QPoint(v.x - 2, v.y - 2);
466 pt[1] = QPoint(v.x + 2, v.y - 2);
467 pt[2] = QPoint(v.x + 2, v.y + 2);
468 pt[3] = QPoint(v.x - 2, v.y + 2);
470 painter->drawPolygon(pt, 4);
475 // Draw a round dot (5x5, centered on Vector; non-scaled)
477 void Painter::DrawRoundDot(Vector v)
480 v = CartesianToQtCoords(v);
482 pt[0] = QPoint(v.x - 1, v.y - 2);
483 pt[1] = QPoint(v.x + 1, v.y - 2);
484 pt[2] = QPoint(v.x + 2, v.y - 1);
485 pt[3] = QPoint(v.x + 2, v.y + 1);
486 pt[4] = QPoint(v.x + 1, v.y + 2);
487 pt[5] = QPoint(v.x - 1, v.y + 2);
488 pt[6] = QPoint(v.x - 2, v.y + 1);
489 pt[7] = QPoint(v.x - 2, v.y - 1);
491 painter->drawPolygon(pt, 8);
496 // Draw a sqaure dot (nxn, centered on Vector; non-scaled)
498 void Painter::DrawSquareDotN(Vector v, uint32_t n)
501 uint32_t offset = (n - 1) / 2;
502 v = CartesianToQtCoords(v);
504 pt[0] = QPoint(v.x - offset, v.y - offset);
505 pt[1] = QPoint(v.x + offset, v.y - offset);
506 pt[2] = QPoint(v.x + offset, v.y + offset);
507 pt[3] = QPoint(v.x - offset, v.y + offset);
509 painter->drawPolygon(pt, 4);
514 // Draw a round dot (nxn, centered on Vector; non-scaled)
516 void Painter::DrawRoundDotN(Vector v, uint32_t n)
518 int radius = (n / 2) + 1;
519 v = CartesianToQtCoords(v);
520 painter->drawEllipse(v.x - radius, v.y - radius, n, n);