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::DrawHLine(double ypos)
377 double width = Global::screenSize.x / Global::zoom;
378 Vector v1 = CartesianToQtCoords(Vector(Global::origin.x, ypos));
379 Vector v2 = CartesianToQtCoords(Vector(Global::origin.x + width, ypos));
380 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
384 void Painter::DrawVLine(double xpos)
386 double height = Global::screenSize.y / Global::zoom;
387 Vector v1 = CartesianToQtCoords(Vector(xpos, Global::origin.y));
388 Vector v2 = CartesianToQtCoords(Vector(xpos, Global::origin.y + height));
389 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
393 void Painter::DrawPoint(int x, int y)
398 Vector v = CartesianToQtCoords(Vector(x, y));
399 painter->drawPoint(v.x, v.y);
403 // The rect passed in is in Qt coordinates...
404 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
409 painter->drawRoundedRect(rect, radiusX, radiusY);
413 // The rect passed in is in Cartesian but we want to pad it by a set number of
414 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
415 void Painter::DrawPaddedRect(QRectF rect)
420 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
421 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
422 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
423 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
424 painter->drawRect(screenRect);
428 void Painter::DrawRect(QRectF rect)
433 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
434 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
435 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
436 painter->drawRect(screenRect);
440 void Painter::DrawText(QRectF rect, int type, QString text)
445 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
449 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
456 // We draw the arrowhead aligned along the line from tail to head
457 double angle = Vector(head - tail).Angle();
458 double orthoAngle = angle + QTR_TAU;
459 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
460 Vector unit = Vector(head - tail).Unit();
462 Point p1 = head - (unit * 9.0 * size);
463 Point p2 = p1 + (orthogonal * 3.0 * size);
464 Point p3 = p1 - (orthogonal * 3.0 * size);
466 Point p4 = CartesianToQtCoords(head);
467 Point p5 = CartesianToQtCoords(p2);
468 Point p6 = CartesianToQtCoords(p3);
470 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
472 painter->drawPolygon(arrow);
476 // Point is given in Cartesian coordinates
477 void Painter::DrawCrosshair(Vector point)
482 Vector screenPoint = CartesianToQtCoords(point);
483 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
484 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
488 void Painter::DrawInformativeText(QString text)
493 painter->setFont(*Global::font);
494 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
495 bounds.moveTo(17.0, 17.0);
496 QRectF textRect = bounds;
497 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
499 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
500 painter->setPen(pen);
501 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0xD7)));
502 painter->drawRoundedRect(textRect, 7.0, 7.0);
504 pen = QPen(QColor(0x00, 0x5F, 0xDF));
505 painter->setPen(pen);
506 painter->drawText(bounds, Qt::AlignVCenter, text);