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...
151 if ((angle > PI * 0.5) && (angle < PI * 1.5))
154 yOffset = 12.0 * Global::zoom * size;
157 textBox.translate(0, yOffset);
159 painter->translate(center.x, center.y);
160 // Angles are backwards in the Qt coord system, so we flip ours...
161 painter->rotate(-angle * RADIANS_TO_DEGREES);
162 //Need to fix this so the text scales as well...
163 painter->drawText(textBox, Qt::AlignCenter, text);
169 // Draw angled text. Draws text using point p as the upper left corner.
170 // Size is point size, angle is in radians (defaults to 0).
172 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
177 p = CartesianToQtCoords(p);
178 painter->setFont(QFont("Arial", Global::zoom * size));
179 int textWidth = QFontMetrics(painter->font()).width(text);
180 int textHeight = QFontMetrics(painter->font()).height();
182 QRectF textBox(0, 0, textWidth, textHeight);
184 painter->translate(p.x, p.y);
185 // Angles are backwards in the Qt coord system, so we flip ours...
186 painter->rotate(-angle * RADIANS_TO_DEGREES);
187 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
192 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
194 center = CartesianToQtCoords(center);
195 // Need to multiply scalar quantities by the zoom factor as well...
196 radius *= Global::zoom;
197 QRectF rectangle(QPointF(center.x - radius, center.y - radius),
198 QPointF(center.x + radius, center.y + radius));
199 int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
200 int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
201 painter->drawArc(rectangle, angle1, angle2);
205 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
207 // Need to multiply scalar quantities by the zoom factor as well...
208 center = CartesianToQtCoords(center);
209 painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
213 // This function is for drawing object handles without regard for zoom level;
214 // we don't want our object handle size to depend on the zoom level!
215 void Painter::DrawHandle(Vector center)
217 center = CartesianToQtCoords(center);
218 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
219 painter->setBrush(Qt::NoBrush);
220 painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
224 // This function is for drawing object handles without regard for zoom level;
225 // we don't want our object handle size to depend on the zoom level!
226 void Painter::DrawArrowHandle(Vector center, double angle)
228 center = CartesianToQtCoords(center);
231 // Since we're drawing directly on the screen, the Y is inverted. So we use
232 // the mirror of the angle.
233 double orthoAngle = -angle + (PI / 2.0);
234 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
235 Vector unit = Vector(cos(-angle), sin(-angle));
237 Point p0 = center + (unit * 6.0);
238 Point p1 = center + (unit * 21.0);
239 Point p1b = center + (unit * 11.0);
240 Point p2 = p1b + (orthogonal * 5.0);
241 Point p3 = p1b - (orthogonal * 5.0);
243 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
244 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
246 painter->drawPolygon(arrow);
250 // This function is for drawing object handles without regard for zoom level;
251 // we don't want our object handle size to depend on the zoom level!
252 void Painter::DrawArrowToLineHandle(Vector center, double angle)
254 DrawArrowHandle(center, angle);
255 center = CartesianToQtCoords(center);
257 // Since we're drawing directly on the screen, the Y is inverted. So we use
258 // the mirror of the angle.
259 double orthoAngle = -angle + (PI / 2.0);
260 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
261 Vector unit = Vector(cos(-angle), sin(-angle));
263 Point p1 = center + (unit * 21.0);
264 Point p2 = p1 + (orthogonal * 7.0);
265 Point p3 = p1 - (orthogonal * 7.0);
267 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
271 void Painter::DrawLine(int x1, int y1, int x2, int y2)
276 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
277 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
278 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
282 void Painter::DrawLine(IPoint p1, IPoint p2)
287 Vector v1 = CartesianToQtCoords(Vector(p1.x, p1.y));
288 Vector v2 = CartesianToQtCoords(Vector(p2.x, p2.y));
289 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
293 void Painter::DrawLine(Vector v1, Vector v2)
298 v1 = CartesianToQtCoords(v1);
299 v2 = CartesianToQtCoords(v2);
300 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
304 void Painter::DrawPoint(int x, int y)
309 Vector v = CartesianToQtCoords(Vector(x, y));
310 painter->drawPoint(v.x, v.y);
314 // The rect passed in is in Qt coordinates...
315 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
320 painter->drawRoundedRect(rect, radiusX, radiusY);
324 // The rect passed in is in Cartesian but we want to pad it by a set number of
325 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
326 void Painter::DrawPaddedRect(QRectF rect)
331 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
332 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
333 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
334 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
335 painter->drawRect(screenRect);
339 void Painter::DrawRect(QRectF rect)
344 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
345 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
346 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
347 painter->drawRect(screenRect);
351 void Painter::DrawText(QRectF rect, int type, QString text)
356 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
360 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
367 // We draw the arrowhead aligned along the line from tail to head
368 double angle = Vector(head - tail).Angle();
369 double orthoAngle = angle + (PI / 2.0);
370 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
371 Vector unit = Vector(head - tail).Unit();
373 Point p1 = head - (unit * 9.0 * size);
374 Point p2 = p1 + (orthogonal * 3.0 * size);
375 Point p3 = p1 - (orthogonal * 3.0 * size);
377 Point p4 = CartesianToQtCoords(head);
378 Point p5 = CartesianToQtCoords(p2);
379 Point p6 = CartesianToQtCoords(p3);
381 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
383 painter->drawPolygon(arrow);
387 // Point is given in Cartesian coordinates
388 void Painter::DrawCrosshair(Vector point)
393 Vector screenPoint = CartesianToQtCoords(point);
394 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
395 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
399 void Painter::DrawInformativeText(QString text)
401 painter->setFont(*Global::font);
402 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
403 bounds.moveTo(17.0, 17.0);
404 QRectF textRect = bounds;
405 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
407 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
408 painter->setPen(pen);
409 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
410 painter->drawRoundedRect(textRect, 7.0, 7.0);
412 pen = QPen(QColor(0x00, 0x5F, 0xDF));
413 painter->setPen(pen);
414 painter->drawText(bounds, Qt::AlignVCenter, text);
418 void Painter::DrawBezier(Point p1, Point p2, Point p3)
420 p1 = CartesianToQtCoords(p1);
421 p2 = CartesianToQtCoords(p2);
422 p3 = CartesianToQtCoords(p3);
425 path.moveTo(p1.x, p1.y);
426 path.quadTo(p2.x, p2.y, p3.x, p3.y);
427 painter->drawPath(path);
431 void Painter::DrawBezier(IPoint p1, IPoint p2, IPoint p3)
433 DrawBezier(Point(p1.x, p1.y), Point(p2.x, p2.y), Point(p3.x, p3.y));
438 // Draw a sqaure dot (5x5, centered on Vector; non-scaled)
440 void Painter::DrawSquareDot(Vector v)
443 v = CartesianToQtCoords(v);
445 pt[0] = QPoint(v.x - 2, v.y - 2);
446 pt[1] = QPoint(v.x + 2, v.y - 2);
447 pt[2] = QPoint(v.x + 2, v.y + 2);
448 pt[3] = QPoint(v.x - 2, v.y + 2);
450 painter->drawPolygon(pt, 4);
455 // Draw a round dot (5x5, centered on Vector; non-scaled)
457 void Painter::DrawRoundDot(Vector v)
460 v = CartesianToQtCoords(v);
462 pt[0] = QPoint(v.x - 1, v.y - 2);
463 pt[1] = QPoint(v.x + 1, v.y - 2);
464 pt[2] = QPoint(v.x + 2, v.y - 1);
465 pt[3] = QPoint(v.x + 2, v.y + 1);
466 pt[4] = QPoint(v.x + 1, v.y + 2);
467 pt[5] = QPoint(v.x - 1, v.y + 2);
468 pt[6] = QPoint(v.x - 2, v.y + 1);
469 pt[7] = QPoint(v.x - 2, v.y - 1);
471 painter->drawPolygon(pt, 8);
476 // Draw a sqaure dot (nxn, centered on Vector; non-scaled)
478 void Painter::DrawSquareDotN(Vector v, uint32_t n)
481 uint32_t offset = (n - 1) / 2;
482 v = CartesianToQtCoords(v);
484 pt[0] = QPoint(v.x - offset, v.y - offset);
485 pt[1] = QPoint(v.x + offset, v.y - offset);
486 pt[2] = QPoint(v.x + offset, v.y + offset);
487 pt[3] = QPoint(v.x - offset, v.y + offset);
489 painter->drawPolygon(pt, 4);
494 // Draw a round dot (nxn, centered on Vector; non-scaled)
496 void Painter::DrawRoundDotN(Vector v, uint32_t n)
498 int radius = (n / 2) + 1;
499 v = CartesianToQtCoords(v);
500 painter->drawEllipse(v.x - radius, v.y - radius, n, n);