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 Painter::Painter(QPainter * p/*= NULL*/): painter(p)
30 Vector Painter::CartesianToQtCoords(Vector v)
32 // Convert regular Cartesian coordinates to the inverted Y-axis Qt
33 // coordinates at the current origin and zoom level.
34 return Vector((v.x - Global::origin.x) * Global::zoom, Global::screenSize.y - ((v.y - Global::origin.y) * Global::zoom));
38 Vector Painter::QtToCartesianCoords(Vector v)
40 // Convert screen location, with inverted Y-axis coordinates, to regular
41 // Cartesian coordinates at the current zoom level.
42 return Vector((v.x / Global::zoom) + Global::origin.x, ((Global::screenSize.y - v.y) / Global::zoom) + Global::origin.y);
46 e.g., we have a point on the screen at Qt coords of 10, 10, screenSize is 100, 100.
47 origin is -10, -10 and zoom level is 2 (200%)
49 1st, invert the Y: 10, 10 -> 10, 90
50 2nd, add origin: 10, 90 -> 0, 80 (no, not right--err, yes, it is)
51 3rd, aply zoom: 0, 80 -> 0, 40
55 1st, invert the Y: 10, 10 -> 10, 90
56 2nd, aply zoom: 10, 90 -> 5, 45
57 3rd, add origin: 5, 45 -> -5, 35
59 it depends on whether or not origin is in Qt coords or cartesian. If Qt, then the 1st
60 is correct, otherwise, the 2nd is correct.
62 The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
67 void Painter::SetRenderHint(int hint)
72 painter->setRenderHint((QPainter::RenderHint)hint);
76 void Painter::SetPen(QPen pen)
85 void Painter::SetPen(uint32_t color, float size/*= 0*/, int style/*= 0*/)
90 // We can cast style as Qt:PenStyle because they line up 1-to-1
91 painter->setPen(QPen(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255),
92 size, (Qt::PenStyle)style));
96 void Painter::SetBrush(QBrush brush)
101 painter->setBrush(brush);
105 void Painter::SetBrush(uint32_t color)
110 painter->setBrush(QBrush(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255)));
114 void Painter::SetFont(QFont font)
119 painter->setFont(font);
123 void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
128 // Strategy: Since Qt doesn't have any rotated text drawing functions,
129 // we instead translate the origin to the center of the text to be drawn and
130 // then rotate the frame to the desired angle.
131 center = CartesianToQtCoords(center);
133 // We may need this stuff... If dimension text is large enough.
134 // int textWidth = QFontMetrics(painter->font()).width(text);
135 // int textHeight = QFontMetrics(painter->font()).height();
136 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
138 // This is in pixels. Might not render correctly at all zoom levels.
139 // Need to figure out if dimensions are always rendered at one size
140 // regardless of zoom, or if they have a definite size, and are thus
142 float yOffset = -12.0 * Global::zoom * size;
144 // Fix text so it isn't upside down...
145 if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
148 yOffset = 12.0 * Global::zoom * size;
151 textBox.translate(0, yOffset);
153 painter->translate(center.x, center.y);
154 // Angles are backwards in the Qt coord system, so we flip ours...
155 painter->rotate(-angle * RADIANS_TO_DEGREES);
156 //Need to fix this so the text scales as well...
157 painter->drawText(textBox, Qt::AlignCenter, text);
163 // Draw angled text. Draws text using point p as the upper left corner.
164 // Size is point size, angle is in radians (defaults to 0).
166 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
171 p = CartesianToQtCoords(p);
172 painter->setFont(QFont("Arial", Global::zoom * size));
173 int textWidth = QFontMetrics(painter->font()).width(text);
174 int textHeight = QFontMetrics(painter->font()).height();
176 QRectF textBox(0, 0, textWidth, textHeight);
178 painter->translate(p.x, p.y);
179 // Angles are backwards in the Qt coord system, so we flip ours...
180 painter->rotate(-angle * RADIANS_TO_DEGREES);
181 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
187 // Return the non-rotated rectangle containing the extents of the text in
188 // Cartesian coordiates (starting from <0, 0>, the lower left hand side)
190 Rect Painter::MeasureTextObject(QString text, double size)
195 painter->setFont(QFont("Arial", Global::zoom * size));
196 int textWidth = QFontMetrics(painter->font()).width(text);
197 int textHeight = QFontMetrics(painter->font()).height();
198 Point measured((double)textWidth / Global::zoom, (double)textHeight / Global::zoom);// = QtToCartesianCoords(Point(textWidth, textHeight));
199 //printf("QFontMetrics w/h=%i/%i, measured=%lf/%lf\n", textWidth, textHeight, measured.x, measured.y);
201 return Rect(Point(0, 0), measured);
205 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
210 center = CartesianToQtCoords(center);
211 // Need to multiply scalar quantities by the zoom factor as well...
212 radius *= Global::zoom;
213 QRectF rectangle(QPointF(center.x - radius, center.y - radius),
214 QPointF(center.x + radius, center.y + radius));
215 int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
216 int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
217 painter->drawArc(rectangle, angle1, angle2);
221 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
226 // Need to multiply scalar quantities by the zoom factor as well...
227 center = CartesianToQtCoords(center);
228 painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
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::DrawHandle(Vector center)
239 center = CartesianToQtCoords(center);
240 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
241 painter->setBrush(Qt::NoBrush);
242 painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
246 // This function is for drawing object handles without regard for zoom level;
247 // we don't want our object handle size to depend on the zoom level!
248 void Painter::DrawSmallHandle(Vector center)
253 center = CartesianToQtCoords(center);
254 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
255 painter->setBrush(Qt::NoBrush);
256 painter->drawEllipse(QPointF(center.x, center.y), 2.0, 2.0);
260 // This function is for drawing feedback points without regard for zoom level;
261 // we don't want our feedback point size to depend on the zoom level!
262 void Painter::DrawCross(Vector point)
267 point = CartesianToQtCoords(point);
268 painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
269 painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
270 painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.0);
274 // This function is for drawing feedback points without regard for zoom level;
275 // we don't want our feedback point size to depend on the zoom level!
276 void Painter::DrawRectCorners(Rect rect)
281 Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
282 Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
283 v1 += Vector(-8.0, -8.0);
284 v2 += Vector(+8.0, +8.0);
285 painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
286 painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
287 painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
288 painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
289 painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
290 painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
291 painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
292 painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
293 painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
298 // This function is for drawing object handles without regard for zoom level;
299 // we don't want our object handle size to depend on the zoom level!
300 void Painter::DrawArrowHandle(Vector center, double angle)
305 center = CartesianToQtCoords(center);
308 // Since we're drawing directly on the screen, the Y is inverted. So we use
309 // the mirror of the angle.
310 double orthoAngle = -angle + QTR_TAU;
311 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
312 Vector unit = Vector(cos(-angle), sin(-angle));
314 Point p0 = center + (unit * 6.0);
315 Point p1 = center + (unit * 21.0);
316 Point p1b = center + (unit * 11.0);
317 Point p2 = p1b + (orthogonal * 5.0);
318 Point p3 = p1b - (orthogonal * 5.0);
320 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
321 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
323 painter->drawPolygon(arrow);
327 // This function is for drawing object handles without regard for zoom level;
328 // we don't want our object handle size to depend on the zoom level!
329 void Painter::DrawArrowToLineHandle(Vector center, double angle)
334 DrawArrowHandle(center, angle);
335 center = CartesianToQtCoords(center);
337 // Since we're drawing directly on the screen, the Y is inverted. So we use
338 // the mirror of the angle.
339 double orthoAngle = -angle + QTR_TAU;
340 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
341 Vector unit = Vector(cos(-angle), sin(-angle));
343 Point p1 = center + (unit * 21.0);
344 Point p2 = p1 + (orthogonal * 7.0);
345 Point p3 = p1 - (orthogonal * 7.0);
347 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
351 void Painter::DrawLine(int x1, int y1, int x2, int y2)
356 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
357 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
358 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
362 void Painter::DrawLine(Vector v1, Vector v2)
367 v1 = CartesianToQtCoords(v1);
368 v2 = CartesianToQtCoords(v2);
369 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
373 void Painter::DrawPoint(int x, int y)
378 Vector v = CartesianToQtCoords(Vector(x, y));
379 painter->drawPoint(v.x, v.y);
383 // The rect passed in is in Qt coordinates...
384 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
389 painter->drawRoundedRect(rect, radiusX, radiusY);
393 // The rect passed in is in Cartesian but we want to pad it by a set number of
394 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
395 void Painter::DrawPaddedRect(QRectF rect)
400 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
401 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
402 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
403 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
404 painter->drawRect(screenRect);
408 void Painter::DrawRect(QRectF rect)
413 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
414 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
415 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
416 painter->drawRect(screenRect);
420 void Painter::DrawText(QRectF rect, int type, QString text)
425 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
429 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
436 // We draw the arrowhead aligned along the line from tail to head
437 double angle = Vector(head - tail).Angle();
438 double orthoAngle = angle + QTR_TAU;
439 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
440 Vector unit = Vector(head - tail).Unit();
442 Point p1 = head - (unit * 9.0 * size);
443 Point p2 = p1 + (orthogonal * 3.0 * size);
444 Point p3 = p1 - (orthogonal * 3.0 * size);
446 Point p4 = CartesianToQtCoords(head);
447 Point p5 = CartesianToQtCoords(p2);
448 Point p6 = CartesianToQtCoords(p3);
450 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
452 painter->drawPolygon(arrow);
456 // Point is given in Cartesian coordinates
457 void Painter::DrawCrosshair(Vector point)
462 Vector screenPoint = CartesianToQtCoords(point);
463 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
464 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
468 void Painter::DrawInformativeText(QString text)
473 painter->setFont(*Global::font);
474 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
475 bounds.moveTo(17.0, 17.0);
476 QRectF textRect = bounds;
477 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
479 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
480 painter->setPen(pen);
481 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0xD7)));
482 painter->drawRoundedRect(textRect, 7.0, 7.0);
484 pen = QPen(QColor(0x00, 0x5F, 0xDF));
485 painter->setPen(pen);
486 painter->drawText(bounds, Qt::AlignVCenter, text);