]> Shamusworld >> Repos - architektonas/blob - src/painter.cpp
16edb26e580c7aae7f42086abe2479182f134471
[architektonas] / src / painter.cpp
1 //
2 // painter.cpp: Paint abstraction layer between Archtektonas and Qt
3 //
4 // Part of the Architektonas Project
5 // (C) 2011 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 //
10 // WHO  WHEN        WHAT
11 // ---  ----------  ------------------------------------------------------------
12 // JLH  09/20/2011  Created this file
13 //
14
15 #include "painter.h"
16 #include "global.h"
17 #include "mathconstants.h"
18
19
20 Painter::Painter(QPainter * p/*= NULL*/): painter(p)
21 {
22 }
23
24
25 Painter::~Painter()
26 {
27 }
28
29
30 Vector Painter::CartesianToQtCoords(Vector v)
31 {
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));
35 }
36
37
38 Vector Painter::QtToCartesianCoords(Vector v)
39 {
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);
43 /*
44 How to do it:
45
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%)
48
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
52
53 or, is it:
54
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
58
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.
61
62 The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
63 */
64 }
65
66
67 void Painter::SetRenderHint(int hint)
68 {
69         if (!painter)
70                 return;
71
72         painter->setRenderHint((QPainter::RenderHint)hint);
73 }
74
75
76 void Painter::SetPen(QPen pen)
77 {
78         if (!painter)
79                 return;
80
81         painter->setPen(pen);
82 }
83
84
85 void Painter::SetPen(uint32_t color, float size/*= 0*/, int style/*= 0*/)
86 {
87         if (!painter)
88                 return;
89
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));
93 }
94
95
96 void Painter::SetBrush(QBrush brush)
97 {
98         if (!painter)
99                 return;
100
101         painter->setBrush(brush);
102 }
103
104
105 void Painter::SetBrush(uint32_t color)
106 {
107         if (!painter)
108                 return;
109
110         painter->setBrush(QBrush(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255)));
111 }
112
113
114 void Painter::SetFont(QFont font)
115 {
116         if (!painter)
117                 return;
118
119         painter->setFont(font);
120 }
121
122
123 void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
124 {
125         if (!painter)
126                 return;
127
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);
132
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
137
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
141         // zoomable.
142         float yOffset = -12.0 * Global::zoom * size;
143
144         // Fix text so it isn't upside down...
145         if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
146         {
147                 angle += HALF_TAU;
148                 yOffset = 12.0 * Global::zoom * size;
149         }
150
151         textBox.translate(0, yOffset);
152         painter->save();
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);
158         painter->restore();
159 }
160
161
162 //
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).
165 //
166 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
167 {
168         if (!painter)
169                 return;
170
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();
175
176         QRectF textBox(0, 0, textWidth, textHeight);
177         painter->save();
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);
182         painter->restore();
183 }
184
185
186 //
187 // Return the non-rotated rectangle containing the extents of the text in
188 // Cartesian coordiates (starting from <0, 0>, the lower left hand side)
189 //
190 Rect Painter::MeasureTextObject(QString text, double size)
191 {
192         if (!painter)
193                 return Rect();
194
195         painter->setFont(QFont("Arial", Global::zoom * size));
196         int textWidth = QFontMetrics(painter->font()).width(text);
197         int textHeight = QFontMetrics(painter->font()).height();
198         Point measured((double)textWidth / Global::zoom, (double)textHeight / Global::zoom);// = QtToCartesianCoords(Point(textWidth, textHeight));
199 //printf("QFontMetrics w/h=%i/%i, measured=%lf/%lf\n", textWidth, textHeight, measured.x, measured.y);
200
201         return Rect(Point(0, 0), measured);
202 }
203
204
205 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
206 {
207         if (!painter)
208                 return;
209
210         center = CartesianToQtCoords(center);
211         // Need to multiply scalar quantities by the zoom factor as well...
212         radius *= Global::zoom;
213         QRectF rectangle(QPointF(center.x - radius, center.y - radius),
214                 QPointF(center.x + radius, center.y + radius));
215         int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
216         int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
217         painter->drawArc(rectangle, angle1, angle2);
218 }
219
220
221 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
222 {
223         if (!painter)
224                 return;
225
226         // Need to multiply scalar quantities by the zoom factor as well...
227         center = CartesianToQtCoords(center);
228         painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
229 }
230
231
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::DrawHandle(Vector center)
235 {
236         if (!painter)
237                 return;
238
239         center = CartesianToQtCoords(center);
240         painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
241         painter->setBrush(Qt::NoBrush);
242         painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
243 }
244
245
246 // This function is for drawing object handles without regard for zoom level;
247 // we don't want our object handle size to depend on the zoom level!
248 void Painter::DrawSmallHandle(Vector center)
249 {
250         if (!painter)
251                 return;
252
253         center = CartesianToQtCoords(center);
254         painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
255         painter->setBrush(Qt::NoBrush);
256         painter->drawEllipse(QPointF(center.x, center.y), 2.0, 2.0);
257 }
258
259
260 // This function is for drawing feedback points without regard for zoom level;
261 // we don't want our feedback point size to depend on the zoom level!
262 void Painter::DrawCross(Vector point)
263 {
264         if (!painter)
265                 return;
266
267         point = CartesianToQtCoords(point);
268         painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
269         painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
270         painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.0);
271 }
272
273
274 // This function is for drawing feedback points without regard for zoom level;
275 // we don't want our feedback point size to depend on the zoom level!
276 void Painter::DrawRectCorners(Rect rect)
277 {
278         if (!painter)
279                 return;
280
281         Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
282         Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
283         v1 += Vector(-8.0, -8.0);
284         v2 += Vector(+8.0, +8.0);
285         painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
286         painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
287         painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
288         painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
289         painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
290         painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
291         painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
292         painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
293         painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
294
295 }
296
297
298 // This function is for drawing object handles without regard for zoom level;
299 // we don't want our object handle size to depend on the zoom level!
300 void Painter::DrawArrowHandle(Vector center, double angle)
301 {
302         if (!painter)
303                 return;
304
305         center = CartesianToQtCoords(center);
306         QPolygonF arrow;
307
308         // Since we're drawing directly on the screen, the Y is inverted. So we use
309         // the mirror of the angle.
310         double orthoAngle = -angle + QTR_TAU;
311         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
312         Vector unit = Vector(cos(-angle), sin(-angle));
313
314         Point p0 = center + (unit * 6.0);
315         Point p1 = center + (unit * 21.0);
316         Point p1b = center + (unit * 11.0);
317         Point p2 = p1b + (orthogonal * 5.0);
318         Point p3 = p1b - (orthogonal * 5.0);
319
320         painter->drawLine(p0.x, p0.y, p1.x, p1.y);
321         arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
322
323         painter->drawPolygon(arrow);
324 }
325
326
327 // This function is for drawing object handles without regard for zoom level;
328 // we don't want our object handle size to depend on the zoom level!
329 void Painter::DrawArrowToLineHandle(Vector center, double angle)
330 {
331         if (!painter)
332                 return;
333
334         DrawArrowHandle(center, angle);
335         center = CartesianToQtCoords(center);
336
337         // Since we're drawing directly on the screen, the Y is inverted. So we use
338         // the mirror of the angle.
339         double orthoAngle = -angle + QTR_TAU;
340         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
341         Vector unit = Vector(cos(-angle), sin(-angle));
342
343         Point p1 = center + (unit * 21.0);
344         Point p2 = p1 + (orthogonal * 7.0);
345         Point p3 = p1 - (orthogonal * 7.0);
346
347         painter->drawLine(p2.x, p2.y, p3.x, p3.y);
348 }
349
350
351 void Painter::DrawLine(int x1, int y1, int x2, int y2)
352 {
353         if (!painter)
354                 return;
355
356         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
357         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
358         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
359 }
360
361
362 void Painter::DrawLine(Vector v1, Vector v2)
363 {
364         if (!painter)
365                 return;
366
367         v1 = CartesianToQtCoords(v1);
368         v2 = CartesianToQtCoords(v2);
369         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
370 }
371
372
373 void Painter::DrawPoint(int x, int y)
374 {
375         if (!painter)
376                 return;
377
378         Vector v = CartesianToQtCoords(Vector(x, y));
379         painter->drawPoint(v.x, v.y);
380 }
381
382
383 // The rect passed in is in Qt coordinates...
384 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
385 {
386         if (!painter)
387                 return;
388
389         painter->drawRoundedRect(rect, radiusX, radiusY);
390 }
391
392
393 // The rect passed in is in Cartesian but we want to pad it by a set number of
394 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
395 void Painter::DrawPaddedRect(QRectF rect)
396 {
397         if (!painter)
398                 return;
399
400         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
401         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
402         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
403         screenRect.adjust(-8, 8, 8, -8);        // Left/top, right/bottom
404         painter->drawRect(screenRect);
405 }
406
407
408 void Painter::DrawRect(QRectF rect)
409 {
410         if (!painter)
411                 return;
412
413         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
414         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
415         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
416         painter->drawRect(screenRect);
417 }
418
419
420 void Painter::DrawText(QRectF rect, int type, QString text)
421 {
422         if (!painter)
423                 return;
424
425         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
426 }
427
428
429 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
430 {
431         if (!painter)
432                 return;
433
434         QPolygonF arrow;
435
436         // We draw the arrowhead aligned along the line from tail to head
437         double angle = Vector(head - tail).Angle();
438         double orthoAngle = angle + QTR_TAU;
439         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
440         Vector unit = Vector(head - tail).Unit();
441
442         Point p1 = head - (unit * 9.0 * size);
443         Point p2 = p1 + (orthogonal * 3.0 * size);
444         Point p3 = p1 - (orthogonal * 3.0 * size);
445
446         Point p4 = CartesianToQtCoords(head);
447         Point p5 = CartesianToQtCoords(p2);
448         Point p6 = CartesianToQtCoords(p3);
449
450         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
451
452         painter->drawPolygon(arrow);
453 }
454
455
456 // Point is given in Cartesian coordinates
457 void Painter::DrawCrosshair(Vector point)
458 {
459         if (!painter)
460                 return;
461
462         Vector screenPoint = CartesianToQtCoords(point);
463         painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
464         painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
465 }
466
467
468 void Painter::DrawInformativeText(QString text)
469 {
470         if (!painter)
471                 return;
472
473         painter->setFont(*Global::font);
474         QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
475         bounds.moveTo(17.0, 17.0);
476         QRectF textRect = bounds;
477         textRect.adjust(-7.0, -7.0, 7.0, 7.0);
478
479         QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
480         painter->setPen(pen);
481         painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0xD7)));
482         painter->drawRoundedRect(textRect, 7.0, 7.0);
483
484         pen = QPen(QColor(0x00, 0x5F, 0xDF));
485         painter->setPen(pen);
486         painter->drawText(bounds, Qt::AlignVCenter, text);
487 }
488