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 coordinates
33 // 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 feedback points without regard for zoom level;
247 // we don't want our feedback point size to depend on the zoom level!
248 void Painter::DrawCross(Vector point)
253 point = CartesianToQtCoords(point);
254 painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
255 painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
256 painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.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::DrawRectCorners(Rect rect)
267 Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
268 Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
269 v1 += Vector(-8.0, -8.0);
270 v2 += Vector(+8.0, +8.0);
271 painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
272 painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
273 painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
274 painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
275 painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
276 painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
277 painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
278 painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
279 painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
284 // This function is for drawing object handles without regard for zoom level;
285 // we don't want our object handle size to depend on the zoom level!
286 void Painter::DrawArrowHandle(Vector center, double angle)
291 center = CartesianToQtCoords(center);
294 // Since we're drawing directly on the screen, the Y is inverted. So we use
295 // the mirror of the angle.
296 double orthoAngle = -angle + QTR_TAU;
297 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
298 Vector unit = Vector(cos(-angle), sin(-angle));
300 Point p0 = center + (unit * 6.0);
301 Point p1 = center + (unit * 21.0);
302 Point p1b = center + (unit * 11.0);
303 Point p2 = p1b + (orthogonal * 5.0);
304 Point p3 = p1b - (orthogonal * 5.0);
306 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
307 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
309 painter->drawPolygon(arrow);
313 // This function is for drawing object handles without regard for zoom level;
314 // we don't want our object handle size to depend on the zoom level!
315 void Painter::DrawArrowToLineHandle(Vector center, double angle)
320 DrawArrowHandle(center, angle);
321 center = CartesianToQtCoords(center);
323 // Since we're drawing directly on the screen, the Y is inverted. So we use
324 // the mirror of the angle.
325 double orthoAngle = -angle + QTR_TAU;
326 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
327 Vector unit = Vector(cos(-angle), sin(-angle));
329 Point p1 = center + (unit * 21.0);
330 Point p2 = p1 + (orthogonal * 7.0);
331 Point p3 = p1 - (orthogonal * 7.0);
333 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
337 void Painter::DrawLine(int x1, int y1, int x2, int y2)
342 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
343 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
344 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
348 void Painter::DrawLine(Vector v1, Vector v2)
353 v1 = CartesianToQtCoords(v1);
354 v2 = CartesianToQtCoords(v2);
355 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
359 void Painter::DrawPoint(int x, int y)
364 Vector v = CartesianToQtCoords(Vector(x, y));
365 painter->drawPoint(v.x, v.y);
369 // The rect passed in is in Qt coordinates...
370 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
375 painter->drawRoundedRect(rect, radiusX, radiusY);
379 // The rect passed in is in Cartesian but we want to pad it by a set number of
380 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
381 void Painter::DrawPaddedRect(QRectF rect)
386 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
387 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
388 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
389 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
390 painter->drawRect(screenRect);
394 void Painter::DrawRect(QRectF rect)
399 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
400 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
401 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
402 painter->drawRect(screenRect);
406 void Painter::DrawText(QRectF rect, int type, QString text)
411 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
415 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
422 // We draw the arrowhead aligned along the line from tail to head
423 double angle = Vector(head - tail).Angle();
424 double orthoAngle = angle + QTR_TAU;
425 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
426 Vector unit = Vector(head - tail).Unit();
428 Point p1 = head - (unit * 9.0 * size);
429 Point p2 = p1 + (orthogonal * 3.0 * size);
430 Point p3 = p1 - (orthogonal * 3.0 * size);
432 Point p4 = CartesianToQtCoords(head);
433 Point p5 = CartesianToQtCoords(p2);
434 Point p6 = CartesianToQtCoords(p3);
436 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
438 painter->drawPolygon(arrow);
442 // Point is given in Cartesian coordinates
443 void Painter::DrawCrosshair(Vector point)
448 Vector screenPoint = CartesianToQtCoords(point);
449 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
450 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
454 void Painter::DrawInformativeText(QString text)
459 painter->setFont(*Global::font);
460 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
461 bounds.moveTo(17.0, 17.0);
462 QRectF textRect = bounds;
463 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
465 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
466 painter->setPen(pen);
467 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
468 painter->drawRoundedRect(textRect, 7.0, 7.0);
470 pen = QPen(QColor(0x00, 0x5F, 0xDF));
471 painter->setPen(pen);
472 painter->drawText(bounds, Qt::AlignVCenter, text);