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);
186 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
191 center = CartesianToQtCoords(center);
192 // Need to multiply scalar quantities by the zoom factor as well...
193 radius *= Global::zoom;
194 QRectF rectangle(QPointF(center.x - radius, center.y - radius),
195 QPointF(center.x + radius, center.y + radius));
196 int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
197 int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
198 painter->drawArc(rectangle, angle1, angle2);
202 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)
220 center = CartesianToQtCoords(center);
221 painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
222 painter->setBrush(Qt::NoBrush);
223 painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
227 // This function is for drawing feedback points without regard for zoom level;
228 // we don't want our feedback point size to depend on the zoom level!
229 void Painter::DrawCross(Vector point)
234 point = CartesianToQtCoords(point);
235 painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
236 painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
237 painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.0);
241 // This function is for drawing feedback points without regard for zoom level;
242 // we don't want our feedback point size to depend on the zoom level!
243 void Painter::DrawRectCorners(Rect rect)
248 // QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
250 Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
251 Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
252 // QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
253 // screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
254 // painter->drawRect(screenRect);
255 v1 += Vector(-8.0, -8.0);
256 v2 += Vector(+8.0, +8.0);
257 painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
258 painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
259 painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
260 painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
261 painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
262 painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
263 painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
264 painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
265 painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
270 // This function is for drawing object handles without regard for zoom level;
271 // we don't want our object handle size to depend on the zoom level!
272 void Painter::DrawArrowHandle(Vector center, double angle)
277 center = CartesianToQtCoords(center);
280 // Since we're drawing directly on the screen, the Y is inverted. So we use
281 // the mirror of the angle.
282 double orthoAngle = -angle + QTR_TAU;
283 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
284 Vector unit = Vector(cos(-angle), sin(-angle));
286 Point p0 = center + (unit * 6.0);
287 Point p1 = center + (unit * 21.0);
288 Point p1b = center + (unit * 11.0);
289 Point p2 = p1b + (orthogonal * 5.0);
290 Point p3 = p1b - (orthogonal * 5.0);
292 painter->drawLine(p0.x, p0.y, p1.x, p1.y);
293 arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
295 painter->drawPolygon(arrow);
299 // This function is for drawing object handles without regard for zoom level;
300 // we don't want our object handle size to depend on the zoom level!
301 void Painter::DrawArrowToLineHandle(Vector center, double angle)
306 DrawArrowHandle(center, angle);
307 center = CartesianToQtCoords(center);
309 // Since we're drawing directly on the screen, the Y is inverted. So we use
310 // the mirror of the angle.
311 double orthoAngle = -angle + QTR_TAU;
312 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
313 Vector unit = Vector(cos(-angle), sin(-angle));
315 Point p1 = center + (unit * 21.0);
316 Point p2 = p1 + (orthogonal * 7.0);
317 Point p3 = p1 - (orthogonal * 7.0);
319 painter->drawLine(p2.x, p2.y, p3.x, p3.y);
323 void Painter::DrawLine(int x1, int y1, int x2, int y2)
328 Vector v1 = CartesianToQtCoords(Vector(x1, y1));
329 Vector v2 = CartesianToQtCoords(Vector(x2, y2));
330 painter->drawLine(v1.x, v1.y, v2.x, v2.y);
334 void Painter::DrawLine(Vector v1, Vector v2)
339 v1 = CartesianToQtCoords(v1);
340 v2 = CartesianToQtCoords(v2);
341 painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
345 void Painter::DrawPoint(int x, int y)
350 Vector v = CartesianToQtCoords(Vector(x, y));
351 painter->drawPoint(v.x, v.y);
355 // The rect passed in is in Qt coordinates...
356 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
361 painter->drawRoundedRect(rect, radiusX, radiusY);
365 // The rect passed in is in Cartesian but we want to pad it by a set number of
366 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
367 void Painter::DrawPaddedRect(QRectF rect)
372 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
373 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
374 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
375 screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
376 painter->drawRect(screenRect);
380 void Painter::DrawRect(QRectF rect)
385 Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
386 Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
387 QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
388 painter->drawRect(screenRect);
392 void Painter::DrawText(QRectF rect, int type, QString text)
397 painter->drawText(rect, (Qt::AlignmentFlag)type, text);
401 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
408 // We draw the arrowhead aligned along the line from tail to head
409 double angle = Vector(head - tail).Angle();
410 double orthoAngle = angle + QTR_TAU;
411 Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
412 Vector unit = Vector(head - tail).Unit();
414 Point p1 = head - (unit * 9.0 * size);
415 Point p2 = p1 + (orthogonal * 3.0 * size);
416 Point p3 = p1 - (orthogonal * 3.0 * size);
418 Point p4 = CartesianToQtCoords(head);
419 Point p5 = CartesianToQtCoords(p2);
420 Point p6 = CartesianToQtCoords(p3);
422 arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
424 painter->drawPolygon(arrow);
428 // Point is given in Cartesian coordinates
429 void Painter::DrawCrosshair(Vector point)
434 Vector screenPoint = CartesianToQtCoords(point);
435 painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
436 painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
440 void Painter::DrawInformativeText(QString text)
445 painter->setFont(*Global::font);
446 QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
447 bounds.moveTo(17.0, 17.0);
448 QRectF textRect = bounds;
449 textRect.adjust(-7.0, -7.0, 7.0, 7.0);
451 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
452 painter->setPen(pen);
453 painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
454 painter->drawRoundedRect(textRect, 7.0, 7.0);
456 pen = QPen(QColor(0x00, 0x5F, 0xDF));
457 painter->setPen(pen);
458 painter->drawText(bounds, Qt::AlignVCenter, text);