]> Shamusworld >> Repos - architektonas/blob - src/painter.cpp
08068119afde63c7ccd3b5b0eb263b3ab774663a
[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 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
187 {
188         if (!painter)
189                 return;
190
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);
199 }
200
201
202 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
203 {
204         if (!painter)
205                 return;
206
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);
210 }
211
212
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)
216 {
217         if (!painter)
218                 return;
219
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);
224 }
225
226
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)
230 {
231         if (!painter)
232                 return;
233
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);
238 }
239
240
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)
244 {
245         if (!painter)
246                 return;
247
248 //      QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
249         
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);
266         
267 }
268
269
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)
273 {
274         if (!painter)
275                 return;
276
277         center = CartesianToQtCoords(center);
278         QPolygonF arrow;
279
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));
285
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);
291
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);
294
295         painter->drawPolygon(arrow);
296 }
297
298
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)
302 {
303         if (!painter)
304                 return;
305
306         DrawArrowHandle(center, angle);
307         center = CartesianToQtCoords(center);
308
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));
314
315         Point p1 = center + (unit * 21.0);
316         Point p2 = p1 + (orthogonal * 7.0);
317         Point p3 = p1 - (orthogonal * 7.0);
318
319         painter->drawLine(p2.x, p2.y, p3.x, p3.y);
320 }
321
322
323 void Painter::DrawLine(int x1, int y1, int x2, int y2)
324 {
325         if (!painter)
326                 return;
327
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);
331 }
332
333
334 void Painter::DrawLine(Vector v1, Vector v2)
335 {
336         if (!painter)
337                 return;
338
339         v1 = CartesianToQtCoords(v1);
340         v2 = CartesianToQtCoords(v2);
341         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
342 }
343
344
345 void Painter::DrawPoint(int x, int y)
346 {
347         if (!painter)
348                 return;
349
350         Vector v = CartesianToQtCoords(Vector(x, y));
351         painter->drawPoint(v.x, v.y);
352 }
353
354
355 // The rect passed in is in Qt coordinates...
356 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
357 {
358         if (!painter)
359                 return;
360
361         painter->drawRoundedRect(rect, radiusX, radiusY);
362 }
363
364
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)
368 {
369         if (!painter)
370                 return;
371
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);
377 }
378
379
380 void Painter::DrawRect(QRectF rect)
381 {
382         if (!painter)
383                 return;
384
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);
389 }
390
391
392 void Painter::DrawText(QRectF rect, int type, QString text)
393 {
394         if (!painter)
395                 return;
396
397         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
398 }
399
400
401 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
402 {
403         if (!painter)
404                 return;
405
406         QPolygonF arrow;
407
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();
413
414         Point p1 = head - (unit * 9.0 * size);
415         Point p2 = p1 + (orthogonal * 3.0 * size);
416         Point p3 = p1 - (orthogonal * 3.0 * size);
417
418         Point p4 = CartesianToQtCoords(head);
419         Point p5 = CartesianToQtCoords(p2);
420         Point p6 = CartesianToQtCoords(p3);
421
422         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
423
424         painter->drawPolygon(arrow);
425 }
426
427
428 // Point is given in Cartesian coordinates
429 void Painter::DrawCrosshair(Vector point)
430 {
431         if (!painter)
432                 return;
433
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);
437 }
438
439
440 void Painter::DrawInformativeText(QString text)
441 {
442         if (!painter)
443                 return;
444
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);
450
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);
455
456         pen = QPen(QColor(0x00, 0x5F, 0xDF));
457         painter->setPen(pen);
458         painter->drawText(bounds, Qt::AlignVCenter, text);
459 }
460