]> Shamusworld >> Repos - architektonas/blob - src/painter.cpp
Lines respond to mouse movement, added Text rendering.
[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 // 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);
24
25
26 Painter::Painter(QPainter * p/*= NULL*/): painter(p)
27 {
28 }
29
30
31 Painter::~Painter()
32 {
33 }
34
35
36 Vector Painter::CartesianToQtCoords(Vector v)
37 {
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));
41 }
42
43
44 Vector Painter::QtToCartesianCoords(Vector v)
45 {
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);
49 /*
50 How to do it:
51
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%)
54
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
58
59 or, is it:
60
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
64
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.
67
68 The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
69 */
70 }
71
72
73 void Painter::SetRenderHint(int hint)
74 {
75         if (!painter)
76                 return;
77
78         painter->setRenderHint((QPainter::RenderHint)hint);
79 }
80
81
82 void Painter::SetPen(QPen pen)
83 {
84         if (!painter)
85                 return;
86
87         painter->setPen(pen);
88 }
89
90
91 void Painter::SetPen(uint32_t color, float size/*= 0*/, int style/*= 0*/)
92 {
93         if (!painter)
94                 return;
95
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));
99 }
100
101
102 void Painter::SetBrush(QBrush brush)
103 {
104         if (!painter)
105                 return;
106
107         painter->setBrush(brush);
108 }
109
110
111 void Painter::SetBrush(uint32_t color)
112 {
113         if (!painter)
114                 return;
115
116         painter->setBrush(QBrush(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255)));
117 }
118
119
120 void Painter::SetFont(QFont font)
121 {
122         if (!painter)
123                 return;
124
125         painter->setFont(font);
126 }
127
128
129 void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
130 {
131         if (!painter)
132                 return;
133
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);
138
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
143
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
147         // zoomable.
148         float yOffset = -12.0 * Global::zoom * size;
149
150         // Fix text so it isn't upside down...
151         if ((angle > PI * 0.5) && (angle < PI * 1.5))
152         {
153                 angle += PI;
154                 yOffset = 12.0 * Global::zoom * size;
155         }
156
157         textBox.translate(0, yOffset);
158         painter->save();
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);
164         painter->restore();
165 }
166
167
168 //
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).
171 //
172 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
173 {
174         if (!painter)
175                 return;
176
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();
181
182         QRectF textBox(0, 0, textWidth, textHeight);
183         painter->save();
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);
188         painter->restore();
189 }
190
191
192 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
193 {
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);
202 }
203
204
205 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
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         center = CartesianToQtCoords(center);
218         painter->setBrush(Qt::NoBrush);
219         painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
220 }
221
222
223 // This function is for drawing object handles without regard for zoom level;
224 // we don't want our object handle size to depend on the zoom level!
225 void Painter::DrawArrowHandle(Vector center, double angle)
226 {
227         center = CartesianToQtCoords(center);
228         QPolygonF arrow;
229
230         // Since we're drawing directly on the screen, the Y is inverted. So we use
231         // the mirror of the angle.
232         double orthoAngle = -angle + (PI / 2.0);
233         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
234         Vector unit = Vector(cos(-angle), sin(-angle));
235
236         Point p0 = center + (unit * 6.0);
237         Point p1 = center + (unit * 21.0);
238         Point p1b = center + (unit * 11.0);
239         Point p2 = p1b + (orthogonal * 5.0);
240         Point p3 = p1b - (orthogonal * 5.0);
241
242         painter->drawLine(p0.x, p0.y, p1.x, p1.y);
243         arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
244
245         painter->drawPolygon(arrow);
246 }
247
248
249 // This function is for drawing object handles without regard for zoom level;
250 // we don't want our object handle size to depend on the zoom level!
251 void Painter::DrawArrowToLineHandle(Vector center, double angle)
252 {
253         DrawArrowHandle(center, angle);
254         center = CartesianToQtCoords(center);
255
256         // Since we're drawing directly on the screen, the Y is inverted. So we use
257         // the mirror of the angle.
258         double orthoAngle = -angle + (PI / 2.0);
259         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
260         Vector unit = Vector(cos(-angle), sin(-angle));
261
262         Point p1 = center + (unit * 21.0);
263         Point p2 = p1 + (orthogonal * 7.0);
264         Point p3 = p1 - (orthogonal * 7.0);
265
266         painter->drawLine(p2.x, p2.y, p3.x, p3.y);
267 }
268
269
270 void Painter::DrawLine(int x1, int y1, int x2, int y2)
271 {
272         if (!painter)
273                 return;
274
275         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
276         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
277         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
278 }
279
280
281 void Painter::DrawLine(Vector v1, Vector v2)
282 {
283         if (!painter)
284                 return;
285
286         v1 = CartesianToQtCoords(v1);
287         v2 = CartesianToQtCoords(v2);
288         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
289 }
290
291
292 void Painter::DrawPoint(int x, int y)
293 {
294         if (!painter)
295                 return;
296
297         Vector v = CartesianToQtCoords(Vector(x, y));
298         painter->drawPoint(v.x, v.y);
299 }
300
301
302 // The rect passed in is in Qt coordinates...
303 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
304 {
305         if (!painter)
306                 return;
307
308         painter->drawRoundedRect(rect, radiusX, radiusY);
309 }
310
311
312 // The rect passed in is in Cartesian but we want to pad it by a set number of
313 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
314 void Painter::DrawPaddedRect(QRectF rect)
315 {
316         if (!painter)
317                 return;
318
319         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
320         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
321         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
322         screenRect.adjust(-8, 8, 8, -8);        // Left/top, right/bottom
323         painter->drawRect(screenRect);
324 }
325
326
327 void Painter::DrawRect(QRectF rect)
328 {
329         if (!painter)
330                 return;
331
332         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
333         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
334         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
335         painter->drawRect(screenRect);
336 }
337
338
339 void Painter::DrawText(QRectF rect, int type, QString text)
340 {
341         if (!painter)
342                 return;
343
344         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
345 }
346
347
348 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
349 {
350         if (!painter)
351                 return;
352
353         QPolygonF arrow;
354
355         // We draw the arrowhead aligned along the line from tail to head
356         double angle = Vector(head - tail).Angle();
357         double orthoAngle = angle + (PI / 2.0);
358         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
359         Vector unit = Vector(head - tail).Unit();
360
361         Point p1 = head - (unit * 9.0 * size);
362         Point p2 = p1 + (orthogonal * 3.0 * size);
363         Point p3 = p1 - (orthogonal * 3.0 * size);
364
365         Point p4 = CartesianToQtCoords(head);
366         Point p5 = CartesianToQtCoords(p2);
367         Point p6 = CartesianToQtCoords(p3);
368
369         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
370
371         painter->drawPolygon(arrow);
372 }
373
374
375 // Point is given in Cartesian coordinates
376 void Painter::DrawCrosshair(Vector point)
377 {
378         if (!painter)
379                 return;
380
381         Vector screenPoint = CartesianToQtCoords(point);
382         painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
383         painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
384 }
385
386
387 void Painter::DrawInformativeText(QString text)
388 {
389         painter->setFont(*Global::font);
390         QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
391         bounds.moveTo(17.0, 17.0);
392         QRectF textRect = bounds;
393         textRect.adjust(-7.0, -7.0, 7.0, 7.0);
394
395         QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
396         painter->setPen(pen);
397         painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
398         painter->drawRoundedRect(textRect, 7.0, 7.0);
399
400         pen = QPen(QColor(0x00, 0x5F, 0xDF));
401         painter->setPen(pen);
402         painter->drawText(bounds, Qt::AlignVCenter, text);
403 }
404