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;
143 float yOffset = -8.0 * Global::zoom * size;
145 // Fix text so it isn't upside down...
146 if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
149 // yOffset = 12.0 * Global::zoom * size;
150 yOffset = 8.0 * Global::zoom * size;
153 textBox.translate(0, yOffset);
155 painter->translate(center.x, center.y);
156 // Angles are backwards in the Qt coord system, so we flip ours...
157 painter->rotate(-angle * RADIANS_TO_DEGREES);
158 //Need to fix this so the text scales as well...
159 painter->drawText(textBox, Qt::AlignCenter, text);
165 // Draw angled text. Draws text using point p as the upper left corner.
166 // Size is point size, angle is in radians (defaults to 0).
168 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
173 p = CartesianToQtCoords(p);
174 painter->setFont(QFont("Arial", Global::zoom * size));
175 int textWidth = QFontMetrics(painter->font()).width(text);
176 int textHeight = QFontMetrics(painter->font()).height();
178 QRectF textBox(0, 0, textWidth, textHeight);
180 painter->translate(p.x, p.y);
181 // Angles are backwards in the Qt coord system, so we flip ours...
182 painter->rotate(-angle * RADIANS_TO_DEGREES);
183 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
189 // Return the non-rotated rectangle containing the extents of the text in
190 // Cartesian coordiates (starting from <0, 0>, the lower left hand side)
192 Rect Painter::MeasureTextObject(QString text, double size)
197 painter->setFont(QFont("Arial", Global::zoom * size));
198 int textWidth = QFontMetrics(painter->font()).width(text);
199 int textHeight = QFontMetrics(painter->font()).height();
200 Point measured((double)textWidth / Global::zoom, (double)textHeight / Global::zoom);// = QtToCartesianCoords(Point(textWidth, textHeight));
201 //printf("QFontMetrics w/h=%i/%i, measured=%lf/%lf\n", textWidth, textHeight, measured.x, measured.y);
203 return Rect(Point(0, 0), measured);
207 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
212 center = CartesianToQtCoords(center);
213 // Need to multiply scalar quantities by the zoom factor as well...
214 radius *= Global::zoom;
215 QRectF rectangle(QPointF(center.x - radius, center.y - radius),
216 QPointF(center.x + radius, center.y + radius));
217 int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
218 int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
219 painter->drawArc(rectangle, angle1, angle2);
223 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
228 // Need to multiply scalar quantities by the zoom factor as well...
229 center = CartesianToQtCoords(center);
230 painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
234 // This function is for drawing object handles without regard for zoom level;
235 // we don't want our object handle size to depend on the zoom level!
236 void Painter::DrawHandle(Vector center)
241 center = CartesianToQtCoords(center);
242 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
243 painter->setBrush(Qt::NoBrush);
244 painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
248 // This function is for drawing object handles without regard for zoom level;
249 // we don't want our object handle size to depend on the zoom level!
250 void Painter::DrawSmallHandle(Vector center)
255 center = CartesianToQtCoords(center);
256 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
257 painter->setBrush(Qt::NoBrush);
258 painter->drawEllipse(QPointF(center.x, center.y), 2.0, 2.0);
262 // This function is for drawing feedback points without regard for zoom level;
263 // we don't want our feedback point size to depend on the zoom level!
264 void Painter::DrawCross(Vector point)
269 point = CartesianToQtCoords(point);
270 painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
271 painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
272 painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.0);
276 // This function is for drawing feedback points without regard for zoom level;
277 // we don't want our feedback point size to depend on the zoom level!
278 void Painter::DrawRectCorners(Rect rect)
283 Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
284 Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
285 v1 += Vector(-8.0, -8.0);
286 v2 += Vector(+8.0, +8.0);
287 painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
288 painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
289 painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
290 painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
291 painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
292 painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
293 painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
294 painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
295 painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
300 // This function is for drawing object handles without regard for zoom level;
301 // we don't want our object handle size to depend on the zoom level!
302 void Painter::DrawArrowHandle(Vector center, double angle)
307 center = CartesianToQtCoords(center);
310 // Since we're drawing directly on the screen, the Y is inverted. So we use
311 // the mirror of the angle.
312 double orthoAngle = -angle + QTR_TAU;
313 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
314 Vector unit = Vector(cos(-angle), sin(-angle));
316 Point p0 = center + (unit * 6.0);
317 Point p1 = center + (unit * 21.0);
318 Point p1b = center + (unit * 11.0);
319 Point p2 = p1b + (orthogonal * 5.0);
320 Point p3 = p1b - (orthogonal * 5.0);
322 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
323 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
325 painter->drawPolygon(arrow);
329 // This function is for drawing object handles without regard for zoom level;
330 // we don't want our object handle size to depend on the zoom level!
331 void Painter::DrawArrowToLineHandle(Vector center, double angle)
336 DrawArrowHandle(center, angle);
337 center = CartesianToQtCoords(center);
339 // Since we're drawing directly on the screen, the Y is inverted. So we use
340 // the mirror of the angle.
341 double orthoAngle = -angle + QTR_TAU;
342 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
343 Vector unit = Vector(cos(-angle), sin(-angle));
345 Point p1 = center + (unit * 21.0);
346 Point p2 = p1 + (orthogonal * 7.0);
347 Point p3 = p1 - (orthogonal * 7.0);
349 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
353 void Painter::DrawLine(int x1, int y1, int x2, int y2)
358 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
359 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
360 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
364 void Painter::DrawLine(Vector v1, Vector v2)
369 v1 = CartesianToQtCoords(v1);
370 v2 = CartesianToQtCoords(v2);
371 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
375 void Painter::DrawPoint(int x, int y)
380 Vector v = CartesianToQtCoords(Vector(x, y));
381 painter->drawPoint(v.x, v.y);
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);
395 // The rect passed in is in Cartesian but we want to pad it by a set number of
396 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
397 void Painter::DrawPaddedRect(QRectF rect)
402 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
403 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
404 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
405 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
406 painter->drawRect(screenRect);
410 void Painter::DrawRect(QRectF rect)
415 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
416 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
417 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
418 painter->drawRect(screenRect);
422 void Painter::DrawText(QRectF rect, int type, QString text)
427 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
431 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
438 // We draw the arrowhead aligned along the line from tail to head
439 double angle = Vector(head - tail).Angle();
440 double orthoAngle = angle + QTR_TAU;
441 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
442 Vector unit = Vector(head - tail).Unit();
444 Point p1 = head - (unit * 9.0 * size);
445 Point p2 = p1 + (orthogonal * 3.0 * size);
446 Point p3 = p1 - (orthogonal * 3.0 * size);
448 Point p4 = CartesianToQtCoords(head);
449 Point p5 = CartesianToQtCoords(p2);
450 Point p6 = CartesianToQtCoords(p3);
452 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
454 painter->drawPolygon(arrow);
458 // Point is given in Cartesian coordinates
459 void Painter::DrawCrosshair(Vector point)
464 Vector screenPoint = CartesianToQtCoords(point);
465 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
466 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
470 void Painter::DrawInformativeText(QString text)
475 painter->setFont(*Global::font);
476 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
477 bounds.moveTo(17.0, 17.0);
478 QRectF textRect = bounds;
479 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
481 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
482 painter->setPen(pen);
483 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0xD7)));
484 painter->drawRoundedRect(textRect, 7.0, 7.0);
486 pen = QPen(QColor(0x00, 0x5F, 0xDF));
487 painter->setPen(pen);
488 painter->drawText(bounds, Qt::AlignVCenter, text);