]> Shamusworld >> Repos - architektonas/blob - src/painter.cpp
df1d7d5d1c548a6cfe8014300485362d6f02bce8
[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 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));
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 feedback points without regard for zoom level;
247 // we don't want our feedback point size to depend on the zoom level!
248 void Painter::DrawCross(Vector point)
249 {
250         if (!painter)
251                 return;
252
253         point = CartesianToQtCoords(point);
254         painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
255         painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
256         painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.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::DrawRectCorners(Rect rect)
263 {
264         if (!painter)
265                 return;
266
267         Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
268         Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
269         v1 += Vector(-8.0, -8.0);
270         v2 += Vector(+8.0, +8.0);
271         painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
272         painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
273         painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
274         painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
275         painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
276         painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
277         painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
278         painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
279         painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
280         
281 }
282
283
284 // This function is for drawing object handles without regard for zoom level;
285 // we don't want our object handle size to depend on the zoom level!
286 void Painter::DrawArrowHandle(Vector center, double angle)
287 {
288         if (!painter)
289                 return;
290
291         center = CartesianToQtCoords(center);
292         QPolygonF arrow;
293
294         // Since we're drawing directly on the screen, the Y is inverted. So we use
295         // the mirror of the angle.
296         double orthoAngle = -angle + QTR_TAU;
297         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
298         Vector unit = Vector(cos(-angle), sin(-angle));
299
300         Point p0 = center + (unit * 6.0);
301         Point p1 = center + (unit * 21.0);
302         Point p1b = center + (unit * 11.0);
303         Point p2 = p1b + (orthogonal * 5.0);
304         Point p3 = p1b - (orthogonal * 5.0);
305
306         painter->drawLine(p0.x, p0.y, p1.x, p1.y);
307         arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
308
309         painter->drawPolygon(arrow);
310 }
311
312
313 // This function is for drawing object handles without regard for zoom level;
314 // we don't want our object handle size to depend on the zoom level!
315 void Painter::DrawArrowToLineHandle(Vector center, double angle)
316 {
317         if (!painter)
318                 return;
319
320         DrawArrowHandle(center, angle);
321         center = CartesianToQtCoords(center);
322
323         // Since we're drawing directly on the screen, the Y is inverted. So we use
324         // the mirror of the angle.
325         double orthoAngle = -angle + QTR_TAU;
326         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
327         Vector unit = Vector(cos(-angle), sin(-angle));
328
329         Point p1 = center + (unit * 21.0);
330         Point p2 = p1 + (orthogonal * 7.0);
331         Point p3 = p1 - (orthogonal * 7.0);
332
333         painter->drawLine(p2.x, p2.y, p3.x, p3.y);
334 }
335
336
337 void Painter::DrawLine(int x1, int y1, int x2, int y2)
338 {
339         if (!painter)
340                 return;
341
342         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
343         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
344         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
345 }
346
347
348 void Painter::DrawLine(Vector v1, Vector v2)
349 {
350         if (!painter)
351                 return;
352
353         v1 = CartesianToQtCoords(v1);
354         v2 = CartesianToQtCoords(v2);
355         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
356 }
357
358
359 void Painter::DrawPoint(int x, int y)
360 {
361         if (!painter)
362                 return;
363
364         Vector v = CartesianToQtCoords(Vector(x, y));
365         painter->drawPoint(v.x, v.y);
366 }
367
368
369 // The rect passed in is in Qt coordinates...
370 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
371 {
372         if (!painter)
373                 return;
374
375         painter->drawRoundedRect(rect, radiusX, radiusY);
376 }
377
378
379 // The rect passed in is in Cartesian but we want to pad it by a set number of
380 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
381 void Painter::DrawPaddedRect(QRectF rect)
382 {
383         if (!painter)
384                 return;
385
386         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
387         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
388         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
389         screenRect.adjust(-8, 8, 8, -8);        // Left/top, right/bottom
390         painter->drawRect(screenRect);
391 }
392
393
394 void Painter::DrawRect(QRectF rect)
395 {
396         if (!painter)
397                 return;
398
399         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
400         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
401         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
402         painter->drawRect(screenRect);
403 }
404
405
406 void Painter::DrawText(QRectF rect, int type, QString text)
407 {
408         if (!painter)
409                 return;
410
411         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
412 }
413
414
415 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
416 {
417         if (!painter)
418                 return;
419
420         QPolygonF arrow;
421
422         // We draw the arrowhead aligned along the line from tail to head
423         double angle = Vector(head - tail).Angle();
424         double orthoAngle = angle + QTR_TAU;
425         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
426         Vector unit = Vector(head - tail).Unit();
427
428         Point p1 = head - (unit * 9.0 * size);
429         Point p2 = p1 + (orthogonal * 3.0 * size);
430         Point p3 = p1 - (orthogonal * 3.0 * size);
431
432         Point p4 = CartesianToQtCoords(head);
433         Point p5 = CartesianToQtCoords(p2);
434         Point p6 = CartesianToQtCoords(p3);
435
436         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
437
438         painter->drawPolygon(arrow);
439 }
440
441
442 // Point is given in Cartesian coordinates
443 void Painter::DrawCrosshair(Vector point)
444 {
445         if (!painter)
446                 return;
447
448         Vector screenPoint = CartesianToQtCoords(point);
449         painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
450         painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
451 }
452
453
454 void Painter::DrawInformativeText(QString text)
455 {
456         if (!painter)
457                 return;
458
459         painter->setFont(*Global::font);
460         QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
461         bounds.moveTo(17.0, 17.0);
462         QRectF textRect = bounds;
463         textRect.adjust(-7.0, -7.0, 7.0, 7.0);
464
465         QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
466         painter->setPen(pen);
467         painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
468         painter->drawRoundedRect(textRect, 7.0, 7.0);
469
470         pen = QPen(QColor(0x00, 0x5F, 0xDF));
471         painter->setPen(pen);
472         painter->drawText(bounds, Qt::AlignVCenter, text);
473 }
474