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...
151 if ((angle > PI * 0.5) && (angle < PI * 1.5))
154 yOffset = 12.0 * Global::zoom * size;
157 textBox.translate(0, yOffset);
159 painter->translate(center.x, center.y);
160 // Angles are backwards in the Qt coord system, so we flip ours...
161 painter->rotate(-angle * RADIANS_TO_DEGREES);
162 //Need to fix this so the text scales as well...
163 painter->drawText(textBox, Qt::AlignCenter, text);
169 // Draw angled text. Draws text using point p as the upper left corner.
170 // Size is point size, angle is in radians (defaults to 0).
172 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
177 p = CartesianToQtCoords(p);
178 painter->setFont(QFont("Arial", Global::zoom * size));
179 int textWidth = QFontMetrics(painter->font()).width(text);
180 int textHeight = QFontMetrics(painter->font()).height();
182 QRectF textBox(0, 0, textWidth, textHeight);
184 painter->translate(p.x, p.y);
185 // Angles are backwards in the Qt coord system, so we flip ours...
186 painter->rotate(-angle * RADIANS_TO_DEGREES);
187 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
192 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
194 center = CartesianToQtCoords(center);
195 // Need to multiply scalar quantities by the zoom factor as well...
196 radius *= Global::zoom;
197 QRectF rectangle(QPointF(center.x - radius, center.y - radius),
198 QPointF(center.x + radius, center.y + radius));
199 int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
200 int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
201 painter->drawArc(rectangle, angle1, angle2);
205 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
207 // Need to multiply scalar quantities by the zoom factor as well...
208 center = CartesianToQtCoords(center);
209 painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
213 // This function is for drawing object handles without regard for zoom level;
214 // we don't want our object handle size to depend on the zoom level!
215 void Painter::DrawHandle(Vector center)
217 center = CartesianToQtCoords(center);
218 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
219 painter->setBrush(Qt::NoBrush);
220 painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
224 // This function is for drawing object handles without regard for zoom level;
225 // we don't want our object handle size to depend on the zoom level!
226 void Painter::DrawArrowHandle(Vector center, double angle)
228 center = CartesianToQtCoords(center);
231 // Since we're drawing directly on the screen, the Y is inverted. So we use
232 // the mirror of the angle.
233 double orthoAngle = -angle + (PI / 2.0);
234 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
235 Vector unit = Vector(cos(-angle), sin(-angle));
237 Point p0 = center + (unit * 6.0);
238 Point p1 = center + (unit * 21.0);
239 Point p1b = center + (unit * 11.0);
240 Point p2 = p1b + (orthogonal * 5.0);
241 Point p3 = p1b - (orthogonal * 5.0);
243 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
244 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
246 painter->drawPolygon(arrow);
250 // This function is for drawing object handles without regard for zoom level;
251 // we don't want our object handle size to depend on the zoom level!
252 void Painter::DrawArrowToLineHandle(Vector center, double angle)
254 DrawArrowHandle(center, angle);
255 center = CartesianToQtCoords(center);
257 // Since we're drawing directly on the screen, the Y is inverted. So we use
258 // the mirror of the angle.
259 double orthoAngle = -angle + (PI / 2.0);
260 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
261 Vector unit = Vector(cos(-angle), sin(-angle));
263 Point p1 = center + (unit * 21.0);
264 Point p2 = p1 + (orthogonal * 7.0);
265 Point p3 = p1 - (orthogonal * 7.0);
267 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
271 void Painter::DrawLine(int x1, int y1, int x2, int y2)
276 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
277 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
278 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
282 void Painter::DrawLine(Vector v1, Vector v2)
287 v1 = CartesianToQtCoords(v1);
288 v2 = CartesianToQtCoords(v2);
289 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
293 void Painter::DrawPoint(int x, int y)
298 Vector v = CartesianToQtCoords(Vector(x, y));
299 painter->drawPoint(v.x, v.y);
303 // The rect passed in is in Qt coordinates...
304 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
309 painter->drawRoundedRect(rect, radiusX, radiusY);
313 // The rect passed in is in Cartesian but we want to pad it by a set number of
314 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
315 void Painter::DrawPaddedRect(QRectF rect)
320 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
321 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
322 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
323 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
324 painter->drawRect(screenRect);
328 void Painter::DrawRect(QRectF rect)
333 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
334 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
335 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
336 painter->drawRect(screenRect);
340 void Painter::DrawText(QRectF rect, int type, QString text)
345 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
349 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
356 // We draw the arrowhead aligned along the line from tail to head
357 double angle = Vector(head - tail).Angle();
358 double orthoAngle = angle + (PI / 2.0);
359 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
360 Vector unit = Vector(head - tail).Unit();
362 Point p1 = head - (unit * 9.0 * size);
363 Point p2 = p1 + (orthogonal * 3.0 * size);
364 Point p3 = p1 - (orthogonal * 3.0 * size);
366 Point p4 = CartesianToQtCoords(head);
367 Point p5 = CartesianToQtCoords(p2);
368 Point p6 = CartesianToQtCoords(p3);
370 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
372 painter->drawPolygon(arrow);
376 // Point is given in Cartesian coordinates
377 void Painter::DrawCrosshair(Vector point)
382 Vector screenPoint = CartesianToQtCoords(point);
383 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
384 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
388 void Painter::DrawInformativeText(QString text)
390 painter->setFont(*Global::font);
391 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
392 bounds.moveTo(17.0, 17.0);
393 QRectF textRect = bounds;
394 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
396 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
397 painter->setPen(pen);
398 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
399 painter->drawRoundedRect(textRect, 7.0, 7.0);
401 pen = QPen(QColor(0x00, 0x5F, 0xDF));
402 painter->setPen(pen);
403 painter->drawText(bounds, Qt::AlignVCenter, text);