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 // Set class variable defaults
21 //Vector Painter::origin(-10.0, -10.0);
22 //double Painter::zoom = 1.0;
23 //Vector Painter::screenSize(200.0, 200.0);
26 Painter::Painter(QPainter * p/*= NULL*/): painter(p)
36 Vector Painter::CartesianToQtCoords(Vector v)
38 // Convert regular Cartesian coordinates to the inverted Y-axis Qt coordinates
39 // at the current origin and zoom level.
40 return Vector((v.x - Global::origin.x) * Global::zoom, Global::screenSize.y - ((v.y - Global::origin.y) * Global::zoom));
44 Vector Painter::QtToCartesianCoords(Vector v)
46 // Convert screen location, with inverted Y-axis coordinates, to regular
47 // Cartesian coordinates at the current zoom level.
48 return Vector((v.x / Global::zoom) + Global::origin.x, ((Global::screenSize.y - v.y) / Global::zoom) + Global::origin.y);
52 e.g., we have a point on the screen at Qt coords of 10, 10, screenSize is 100, 100.
53 origin is -10, -10 and zoom level is 2 (200%)
55 1st, invert the Y: 10, 10 -> 10, 90
56 2nd, add origin: 10, 90 -> 0, 80 (no, not right--err, yes, it is)
57 3rd, aply zoom: 0, 80 -> 0, 40
61 1st, invert the Y: 10, 10 -> 10, 90
62 2nd, aply zoom: 10, 90 -> 5, 45
63 3rd, add origin: 5, 45 -> -5, 35
65 it depends on whether or not origin is in Qt coords or cartesian. If Qt, then the 1st
66 is correct, otherwise, the 2nd is correct.
68 The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
73 void Painter::SetRenderHint(int hint)
78 painter->setRenderHint((QPainter::RenderHint)hint);
82 void Painter::SetPen(QPen pen)
91 void Painter::SetPen(uint32_t color, float size/*= 0*/, int style/*= 0*/)
96 // We can cast style as Qt:PenStyle because they line up 1-to-1
97 painter->setPen(QPen(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255),
98 size, (Qt::PenStyle)style));
102 void Painter::SetBrush(QBrush brush)
107 painter->setBrush(brush);
111 void Painter::SetBrush(uint32_t color)
116 painter->setBrush(QBrush(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255)));
120 void Painter::SetFont(QFont font)
125 painter->setFont(font);
129 void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
134 // Strategy: Since Qt doesn't have any rotated text drawing functions,
135 // we instead translate the origin to the center of the text to be drawn and
136 // then rotate the frame to the desired angle.
137 center = CartesianToQtCoords(center);
139 // We may need this stuff... If dimension text is large enough.
140 // int textWidth = QFontMetrics(painter->font()).width(text);
141 // int textHeight = QFontMetrics(painter->font()).height();
142 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
144 // This is in pixels. Might not render correctly at all zoom levels.
145 // Need to figure out if dimensions are always rendered at one size
146 // regardless of zoom, or if they have a definite size, and are thus
148 float yOffset = -12.0 * Global::zoom * size;
150 // Fix text so it isn't upside down...
152 if ((angle > PI * 0.5) && (angle < PI * 1.5))
155 yOffset = 12.0 * Global::zoom * size;
158 if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
161 yOffset = 12.0 * Global::zoom * size;
165 textBox.translate(0, yOffset);
167 painter->translate(center.x, center.y);
168 // Angles are backwards in the Qt coord system, so we flip ours...
169 painter->rotate(-angle * RADIANS_TO_DEGREES);
170 //Need to fix this so the text scales as well...
171 painter->drawText(textBox, Qt::AlignCenter, text);
177 // Draw angled text. Draws text using point p as the upper left corner.
178 // Size is point size, angle is in radians (defaults to 0).
180 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
185 p = CartesianToQtCoords(p);
186 painter->setFont(QFont("Arial", Global::zoom * size));
187 int textWidth = QFontMetrics(painter->font()).width(text);
188 int textHeight = QFontMetrics(painter->font()).height();
190 QRectF textBox(0, 0, textWidth, textHeight);
192 painter->translate(p.x, p.y);
193 // Angles are backwards in the Qt coord system, so we flip ours...
194 painter->rotate(-angle * RADIANS_TO_DEGREES);
195 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
200 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
202 center = CartesianToQtCoords(center);
203 // Need to multiply scalar quantities by the zoom factor as well...
204 radius *= Global::zoom;
205 QRectF rectangle(QPointF(center.x - radius, center.y - radius),
206 QPointF(center.x + radius, center.y + radius));
207 int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
208 int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
209 painter->drawArc(rectangle, angle1, angle2);
213 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
215 // Need to multiply scalar quantities by the zoom factor as well...
216 center = CartesianToQtCoords(center);
217 painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
221 // This function is for drawing object handles without regard for zoom level;
222 // we don't want our object handle size to depend on the zoom level!
223 void Painter::DrawHandle(Vector center)
225 center = CartesianToQtCoords(center);
226 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
227 painter->setBrush(Qt::NoBrush);
228 painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
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::DrawArrowHandle(Vector center, double angle)
236 center = CartesianToQtCoords(center);
239 // Since we're drawing directly on the screen, the Y is inverted. So we use
240 // the mirror of the angle.
241 double orthoAngle = -angle + QTR_TAU;//(PI / 2.0);
242 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
243 Vector unit = Vector(cos(-angle), sin(-angle));
245 Point p0 = center + (unit * 6.0);
246 Point p1 = center + (unit * 21.0);
247 Point p1b = center + (unit * 11.0);
248 Point p2 = p1b + (orthogonal * 5.0);
249 Point p3 = p1b - (orthogonal * 5.0);
251 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
252 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
254 painter->drawPolygon(arrow);
258 // This function is for drawing object handles without regard for zoom level;
259 // we don't want our object handle size to depend on the zoom level!
260 void Painter::DrawArrowToLineHandle(Vector center, double angle)
262 DrawArrowHandle(center, angle);
263 center = CartesianToQtCoords(center);
265 // Since we're drawing directly on the screen, the Y is inverted. So we use
266 // the mirror of the angle.
267 double orthoAngle = -angle + QTR_TAU;//(PI / 2.0);
268 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
269 Vector unit = Vector(cos(-angle), sin(-angle));
271 Point p1 = center + (unit * 21.0);
272 Point p2 = p1 + (orthogonal * 7.0);
273 Point p3 = p1 - (orthogonal * 7.0);
275 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
279 void Painter::DrawLine(int x1, int y1, int x2, int y2)
284 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
285 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
286 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
290 void Painter::DrawLine(Vector v1, Vector v2)
295 v1 = CartesianToQtCoords(v1);
296 v2 = CartesianToQtCoords(v2);
297 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
301 void Painter::DrawPoint(int x, int y)
306 Vector v = CartesianToQtCoords(Vector(x, y));
307 painter->drawPoint(v.x, v.y);
311 // The rect passed in is in Qt coordinates...
312 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
317 painter->drawRoundedRect(rect, radiusX, radiusY);
321 // The rect passed in is in Cartesian but we want to pad it by a set number of
322 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
323 void Painter::DrawPaddedRect(QRectF rect)
328 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
329 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
330 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
331 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
332 painter->drawRect(screenRect);
336 void Painter::DrawRect(QRectF rect)
341 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
342 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
343 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
344 painter->drawRect(screenRect);
348 void Painter::DrawText(QRectF rect, int type, QString text)
353 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
357 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
364 // We draw the arrowhead aligned along the line from tail to head
365 double angle = Vector(head - tail).Angle();
366 double orthoAngle = angle + QTR_TAU;//(PI / 2.0);
367 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
368 Vector unit = Vector(head - tail).Unit();
370 Point p1 = head - (unit * 9.0 * size);
371 Point p2 = p1 + (orthogonal * 3.0 * size);
372 Point p3 = p1 - (orthogonal * 3.0 * size);
374 Point p4 = CartesianToQtCoords(head);
375 Point p5 = CartesianToQtCoords(p2);
376 Point p6 = CartesianToQtCoords(p3);
378 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
380 painter->drawPolygon(arrow);
384 // Point is given in Cartesian coordinates
385 void Painter::DrawCrosshair(Vector point)
390 Vector screenPoint = CartesianToQtCoords(point);
391 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
392 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
396 void Painter::DrawInformativeText(QString text)
398 painter->setFont(*Global::font);
399 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
400 bounds.moveTo(17.0, 17.0);
401 QRectF textRect = bounds;
402 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
404 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
405 painter->setPen(pen);
406 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
407 painter->drawRoundedRect(textRect, 7.0, 7.0);
409 pen = QPen(QColor(0x00, 0x5F, 0xDF));
410 painter->setPen(pen);
411 painter->drawText(bounds, Qt::AlignVCenter, text);