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"
19 Painter::Painter(QPainter * p/*= NULL*/): painter(p)
27 Vector Painter::CartesianToQtCoords(Vector v)
29 // Convert regular Cartesian coordinates to the inverted Y-axis Qt
30 // coordinates at the current origin and zoom level.
31 return Vector((v.x - Global::origin.x) * Global::zoom, Global::screenSize.y - ((v.y - Global::origin.y) * Global::zoom));
34 Vector Painter::QtToCartesianCoords(Vector v)
36 // Convert screen location, with inverted Y-axis coordinates, to regular
37 // Cartesian coordinates at the current zoom level.
38 return Vector((v.x / Global::zoom) + Global::origin.x, ((Global::screenSize.y - v.y) / Global::zoom) + Global::origin.y);
42 e.g., we have a point on the screen at Qt coords of 10, 10, screenSize is 100, 100.
43 origin is -10, -10 and zoom level is 2 (200%)
45 1st, invert the Y: 10, 10 -> 10, 90
46 2nd, add origin: 10, 90 -> 0, 80 (no, not right--err, yes, it is)
47 3rd, apply zoom: 0, 80 -> 0, 40
51 1st, invert the Y: 10, 10 -> 10, 90
52 2nd, apply zoom: 10, 90 -> 5, 45
53 3rd, add origin: 5, 45 -> -5, 35
55 it depends on whether or not origin is in Qt coords or cartesian. If Qt, then the 1st
56 is correct, otherwise, the 2nd is correct.
58 The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
62 void Painter::SetRenderHint(int hint)
67 painter->setRenderHint((QPainter::RenderHint)hint);
70 void Painter::SetPen(QPen pen)
78 void Painter::SetPen(uint32_t color, float size/*= 0*/, int style/*= 0*/)
83 // We can cast style as Qt:PenStyle because they line up 1-to-1
84 painter->setPen(QPen(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255),
85 size, (Qt::PenStyle)style));
88 void Painter::SetBrush(QBrush brush)
93 painter->setBrush(brush);
96 void Painter::SetBrush(uint32_t color)
101 painter->setBrush(QBrush(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255)));
104 void Painter::SetFont(QFont font)
109 painter->setFont(font);
112 void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
117 // Strategy: Since Qt doesn't have any rotated text drawing functions,
118 // we instead translate the origin to the center of the text to be drawn and
119 // then rotate the frame to the desired angle.
120 center = CartesianToQtCoords(center);
122 // We may need this stuff... If dimension text is large enough.
123 // int textWidth = QFontMetrics(painter->font()).width(text);
124 // int textHeight = QFontMetrics(painter->font()).height();
125 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
127 // This is in pixels. Might not render correctly at all zoom levels.
128 // Need to figure out if dimensions are always rendered at one size
129 // regardless of zoom, or if they have a definite size, and are thus
131 // float yOffset = -12.0 * Global::zoom * size;
132 float yOffset = -8.0 * Global::zoom * size;
134 // Fix text so it isn't upside down...
135 if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
138 // yOffset = 12.0 * Global::zoom * size;
139 yOffset = 8.0 * Global::zoom * size;
142 textBox.translate(0, yOffset);
144 painter->translate(center.x, center.y);
145 // Angles are backwards in the Qt coord system, so we flip ours...
146 painter->rotate(-angle * RADIANS_TO_DEGREES);
147 //Need to fix this so the text scales as well...
148 painter->drawText(textBox, Qt::AlignCenter, text);
153 // Draw angled text. Draws text using point p as the upper left corner.
154 // Size is point size, angle is in radians (defaults to 0).
156 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
161 p = CartesianToQtCoords(p);
162 painter->setFont(QFont("Arial", Global::zoom * size));
163 int textWidth = QFontMetrics(painter->font()).horizontalAdvance(text);
164 int textHeight = QFontMetrics(painter->font()).height();
166 QRectF textBox(0, 0, textWidth, textHeight);
168 painter->translate(p.x, p.y);
169 // Angles are backwards in the Qt coord system, so we flip ours...
170 painter->rotate(-angle * RADIANS_TO_DEGREES);
171 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
176 // Return the non-rotated rectangle containing the extents of the text in
177 // Cartesian coordiates (starting from <0, 0>, the lower left hand side)
179 Rect Painter::MeasureTextObject(QString text, double size)
184 painter->setFont(QFont("Arial", Global::zoom * size));
185 int textWidth = QFontMetrics(painter->font()).horizontalAdvance(text);
186 int textHeight = QFontMetrics(painter->font()).height();
187 Point measured((double)textWidth / Global::zoom, (double)textHeight / Global::zoom);
189 return Rect(Point(0, 0), measured);
192 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
197 center = CartesianToQtCoords(center);
198 // Need to multiply scalar quantities by the zoom factor as well...
199 radius *= Global::zoom;
200 QRectF rectangle(QPointF(center.x - radius, center.y - radius),
201 QPointF(center.x + radius, center.y + radius));
202 int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
203 int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
204 painter->drawArc(rectangle, angle1, angle2);
207 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
212 // Need to multiply scalar quantities by the zoom factor as well...
213 center = CartesianToQtCoords(center);
214 painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
217 // This function is for drawing object handles without regard for zoom level;
218 // we don't want our object handle size to depend on the zoom level!
219 void Painter::DrawHandle(Vector center)
224 center = CartesianToQtCoords(center);
225 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
226 painter->setBrush(Qt::NoBrush);
227 painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
230 // This function is for drawing object handles without regard for zoom level;
231 // we don't want our object handle size to depend on the zoom level!
232 void Painter::DrawSmallHandle(Vector center)
237 center = CartesianToQtCoords(center);
238 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
239 painter->setBrush(Qt::NoBrush);
240 painter->drawEllipse(QPointF(center.x, center.y), 2.0, 2.0);
243 // This function is for drawing feedback points without regard for zoom level;
244 // we don't want our feedback point size to depend on the zoom level!
245 void Painter::DrawCross(Vector point)
250 point = CartesianToQtCoords(point);
251 painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
252 painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
253 painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.0);
256 // This function is for drawing feedback points without regard for zoom level;
257 // we don't want our feedback point size to depend on the zoom level!
258 void Painter::DrawRectCorners(Rect rect)
263 Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
264 Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
265 v1 += Vector(-8.0, -8.0);
266 v2 += Vector(+8.0, +8.0);
267 painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
268 painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
269 painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
270 painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
271 painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
272 painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
273 painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
274 painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
275 painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
279 // This function is for drawing object handles without regard for zoom level;
280 // we don't want our object handle size to depend on the zoom level!
281 void Painter::DrawArrowHandle(Vector center, double angle)
286 center = CartesianToQtCoords(center);
289 // Since we're drawing directly on the screen, the Y is inverted. So we use
290 // the mirror of the angle.
291 double orthoAngle = -angle + QTR_TAU;
292 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
293 Vector unit = Vector(cos(-angle), sin(-angle));
295 Point p0 = center + (unit * 6.0);
296 Point p1 = center + (unit * 21.0);
297 Point p1b = center + (unit * 11.0);
298 Point p2 = p1b + (orthogonal * 5.0);
299 Point p3 = p1b - (orthogonal * 5.0);
301 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
302 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
304 painter->drawPolygon(arrow);
307 // This function is for drawing object handles without regard for zoom level;
308 // we don't want our object handle size to depend on the zoom level!
309 void Painter::DrawArrowToLineHandle(Vector center, double angle)
314 DrawArrowHandle(center, angle);
315 center = CartesianToQtCoords(center);
317 // Since we're drawing directly on the screen, the Y is inverted. So we use
318 // the mirror of the angle.
319 double orthoAngle = -angle + QTR_TAU;
320 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
321 Vector unit = Vector(cos(-angle), sin(-angle));
323 Point p1 = center + (unit * 21.0);
324 Point p2 = p1 + (orthogonal * 7.0);
325 Point p3 = p1 - (orthogonal * 7.0);
327 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
330 void Painter::DrawLine(int x1, int y1, int x2, int y2)
335 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
336 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
337 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
340 void Painter::DrawLine(Vector v1, Vector v2)
345 v1 = CartesianToQtCoords(v1);
346 v2 = CartesianToQtCoords(v2);
347 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
350 void Painter::DrawHLine(double ypos)
352 double width = Global::screenSize.x / Global::zoom;
353 Vector v1 = CartesianToQtCoords(Vector(Global::origin.x, ypos));
354 Vector v2 = CartesianToQtCoords(Vector(Global::origin.x + width, ypos));
355 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
358 void Painter::DrawVLine(double xpos)
360 double height = Global::screenSize.y / Global::zoom;
361 Vector v1 = CartesianToQtCoords(Vector(xpos, Global::origin.y));
362 Vector v2 = CartesianToQtCoords(Vector(xpos, Global::origin.y + height));
363 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
366 void Painter::DrawPoint(int x, int y)
371 Vector v = CartesianToQtCoords(Vector(x, y));
372 painter->drawPoint(v.x, v.y);
375 // The rect passed in is in Qt coordinates... (?)
376 void Painter::DrawRoundedRect(Rect rect, double radiusX, double radiusY)
381 QRectF r(QPointF(rect.l, rect.t), QPointF(rect.r, rect.b));
382 painter->drawRoundedRect(r, radiusX, radiusY);
385 // The rect passed in is in Qt coordinates...
386 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
391 painter->drawRoundedRect(rect, radiusX, radiusY);
394 // The rect passed in is in Cartesian but we want to pad it by a set number of
395 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
396 void Painter::DrawPaddedRect(Rect rect)
401 Vector v1 = CartesianToQtCoords(rect.TopLeft());
402 Vector v2 = CartesianToQtCoords(rect.BottomRight());
403 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
404 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
405 painter->drawRect(screenRect);
408 void Painter::DrawRect(Rect rect)
413 Vector v1 = CartesianToQtCoords(rect.TopLeft());
414 Vector v2 = CartesianToQtCoords(rect.BottomRight());
415 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
416 painter->drawRect(screenRect);
419 void Painter::DrawText(Rect rect, int type, QString text)
424 QRectF r(QPointF(rect.l, rect.t), QPointF(rect.r, rect.b));
425 painter->drawText(r, (Qt::AlignmentFlag)type, text);
428 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
435 // We draw the arrowhead aligned along the line from tail to head
436 double angle = Vector(head - tail).Angle();
437 double orthoAngle = angle + QTR_TAU;
438 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
439 Vector unit = Vector(head - tail).Unit();
441 Point p1 = head - (unit * 9.0 * size);
442 Point p2 = p1 + (orthogonal * 3.0 * size);
443 Point p3 = p1 - (orthogonal * 3.0 * size);
445 Point p4 = CartesianToQtCoords(head);
446 Point p5 = CartesianToQtCoords(p2);
447 Point p6 = CartesianToQtCoords(p3);
449 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
451 painter->drawPolygon(arrow);
454 // Point is given in Cartesian coordinates
455 void Painter::DrawCrosshair(Vector point)
460 Vector screenPoint = CartesianToQtCoords(point);
461 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
462 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
465 void Painter::DrawInformativeText(QString text)
470 painter->setFont(*Global::font);
471 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
472 bounds.moveTo(17.0, 17.0);
473 QRectF textRect = bounds;
474 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
476 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
477 painter->setPen(pen);
478 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0xD7)));
479 painter->drawRoundedRect(textRect, 7.0, 7.0);
481 pen = QPen(QColor(0x00, 0x5F, 0xDF));
482 painter->setPen(pen);
483 painter->drawText(bounds, Qt::AlignVCenter, text);