]> Shamusworld >> Repos - architektonas/blob - src/painter.cpp
bdac361f59b64d7f68b2aca874ac169c0d92837d
[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
17 #include "mathconstants.h"
18 //#include "object.h"
19 #include "global.h"
20
21
22 // Set class variable defaults
23 Vector Painter::origin(-10.0, -10.0);
24 double Painter::zoom = 1.0;
25 Vector Painter::screenSize(200.0, 200.0);
26
27
28 Painter::Painter(QPainter * p/*= NULL*/): painter(p)
29 {
30 }
31
32
33 Painter::~Painter()
34 {
35 }
36
37
38 Vector Painter::CartesianToQtCoords(Vector v)
39 {
40         // Convert regular Cartesian coordinates to the inverted Y-axis Qt coordinates
41         // at the current origin and zoom level.
42         return Vector((v.x - origin.x) * zoom, screenSize.y - ((v.y - origin.y) * zoom));
43 }
44
45
46 Vector Painter::QtToCartesianCoords(Vector v)
47 {
48         // Convert screen location, with inverted Y-axis coordinates, to regular
49         // Cartesian coordinates at the current zoom level.
50         return Vector((v.x / zoom) + origin.x, ((screenSize.y - v.y) / zoom) + origin.y);
51 /*
52 How to do it:
53
54 e.g., we have a point on the screen at Qt coords of 10, 10, screenSize is 100, 100.
55 origin is -10, -10 and zoom level is 2 (200%)
56
57 1st, invert the Y: 10, 10 -> 10, 90
58 2nd, add origin:   10, 90 ->  0, 80 (no, not right--err, yes, it is)
59 3rd, aply zoom:     0, 80 ->  0, 40
60
61 or, is it:
62
63 1st, invert the Y: 10, 10 -> 10, 90
64 2nd, aply zoom:    10, 90 ->  5, 45
65 3rd, add origin:    5, 45 -> -5, 35
66
67 it depends on whether or not origin is in Qt coords or cartesian. If Qt, then the 1st
68 is correct, otherwise, the 2nd is correct.
69
70 The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
71 */
72 }
73
74
75 void Painter::SetRenderHint(int hint)
76 {
77         if (!painter)
78                 return;
79
80         painter->setRenderHint((QPainter::RenderHint)hint);
81 }
82
83
84 void Painter::SetBrush(QBrush brush)
85 {
86         if (!painter)
87                 return;
88
89         painter->setBrush(brush);
90 }
91
92
93 void Painter::SetFont(QFont font)
94 {
95         if (!painter)
96                 return;
97
98         painter->setFont(font);
99 }
100
101
102 void Painter::SetPen(QPen pen)
103 {
104         if (!painter)
105                 return;
106
107         painter->setPen(pen);
108 }
109
110
111 void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
112 {
113         if (!painter)
114                 return;
115
116         // Strategy: Since Qt doesn't have any rotated text drawing functions,
117         // we instead translate the origin to the center of the text to be drawn and
118         // then rotate the frame to the desired angle.
119         center = CartesianToQtCoords(center);
120
121         // We may need this stuff... If dimension text is large enough.
122 //      int textWidth = QFontMetrics(painter->font()).width(text);
123 //      int textHeight = QFontMetrics(painter->font()).height();
124         QRectF textBox(-100.0 * zoom * size, -100.0 * zoom * size, 200.0 * zoom * size, 200.0 * zoom * size);   // x, y, w, h; x/y = upper left corner
125
126         // This is in pixels. Might not render correctly at all zoom levels.
127         // Need to figure out if dimensions are always rendered at one size
128         // regardless of zoom, or if they have a definite size, and are thus
129         // zoomable.
130         float yOffset = -12.0 * zoom * size;
131
132         // Fix text so it isn't upside down...
133         if ((angle > PI * 0.5) && (angle < PI * 1.5))
134         {
135                 angle += PI;
136                 yOffset = 12.0 * zoom * size;
137         }
138
139         textBox.translate(0, yOffset);
140         painter->save();
141         painter->translate(center.x, center.y);
142         // Angles are backwards in the Qt coord system, so we flip ours...
143         painter->rotate(-angle * RADIANS_TO_DEGREES);
144 //Need to fix this so the text scales as well...
145         painter->drawText(textBox, Qt::AlignCenter, text);
146         painter->restore();
147 }
148
149
150 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
151 {
152         center = CartesianToQtCoords(center);
153         // Need to multiply scalar quantities by the zoom factor as well...
154         radius *= zoom;
155         QRectF rectangle(QPointF(center.x - radius, center.y - radius),
156                 QPointF(center.x + radius, center.y + radius));
157         int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
158         int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
159         painter->drawArc(rectangle, angle1, angle2);
160 }
161
162
163 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
164 {
165         // Need to multiply scalar quantities by the zoom factor as well...
166         center = CartesianToQtCoords(center);
167         painter->drawEllipse(QPointF(center.x, center.y), axis1 * zoom, axis2 * zoom);
168 }
169
170
171 // This function is for drawing object handles without regard for zoom level;
172 // we don't want our object handle size to depend on the zoom level!
173 void Painter::DrawHandle(Vector center)
174 {
175         center = CartesianToQtCoords(center);
176         painter->setBrush(Qt::NoBrush);
177         painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
178 }
179
180
181 // This function is for drawing object handles without regard for zoom level;
182 // we don't want our object handle size to depend on the zoom level!
183 void Painter::DrawArrowHandle(Vector center, double angle)
184 {
185         center = CartesianToQtCoords(center);
186         QPolygonF arrow;
187
188         // Since we're drawing directly on the screen, the Y is inverted. So we use
189         // the mirror of the angle.
190         double orthoAngle = -angle + (PI / 2.0);
191         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
192         Vector unit = Vector(cos(-angle), sin(-angle));
193
194         Point p0 = center + (unit * 6.0);
195         Point p1 = center + (unit * 21.0);
196         Point p1b = center + (unit * 11.0);
197         Point p2 = p1b + (orthogonal * 5.0);
198         Point p3 = p1b - (orthogonal * 5.0);
199
200         painter->drawLine(p0.x, p0.y, p1.x, p1.y);
201         arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
202
203         painter->drawPolygon(arrow);
204 }
205
206
207 // This function is for drawing object handles without regard for zoom level;
208 // we don't want our object handle size to depend on the zoom level!
209 void Painter::DrawArrowToLineHandle(Vector center, double angle)
210 {
211         DrawArrowHandle(center, angle);
212         center = CartesianToQtCoords(center);
213
214         // Since we're drawing directly on the screen, the Y is inverted. So we use
215         // the mirror of the angle.
216         double orthoAngle = -angle + (PI / 2.0);
217         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
218         Vector unit = Vector(cos(-angle), sin(-angle));
219
220         Point p1 = center + (unit * 21.0);
221         Point p2 = p1 + (orthogonal * 7.0);
222         Point p3 = p1 - (orthogonal * 7.0);
223
224         painter->drawLine(p2.x, p2.y, p3.x, p3.y);
225 }
226
227
228 void Painter::DrawLine(int x1, int y1, int x2, int y2)
229 {
230         if (!painter)
231                 return;
232
233         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
234         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
235         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
236 }
237
238
239 void Painter::DrawLine(Vector v1, Vector v2)
240 {
241         if (!painter)
242                 return;
243
244         v1 = CartesianToQtCoords(v1);
245         v2 = CartesianToQtCoords(v2);
246         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
247 }
248
249
250 void Painter::DrawPoint(int x, int y)
251 {
252         if (!painter)
253                 return;
254
255         Vector v = CartesianToQtCoords(Vector(x, y));
256         painter->drawPoint(v.x, v.y);
257 }
258
259
260 // The rect passed in is in Qt coordinates...
261 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
262 {
263         if (!painter)
264                 return;
265
266         painter->drawRoundedRect(rect, radiusX, radiusY);
267 }
268
269
270 // The rect passed in is in Cartesian but we want to pad it by a set number of
271 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
272 void Painter::DrawPaddedRect(QRectF rect)
273 {
274         if (!painter)
275                 return;
276
277         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
278         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
279         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
280         screenRect.adjust(-8, 8, 8, -8);        // Left/top, right/bottom
281         painter->drawRect(screenRect);
282 }
283
284
285 void Painter::DrawRect(QRectF rect)
286 {
287         if (!painter)
288                 return;
289
290         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
291         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
292         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
293         painter->drawRect(screenRect);
294 }
295
296
297 void Painter::DrawText(QRectF rect, int type, QString text)
298 {
299         if (!painter)
300                 return;
301
302         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
303 }
304
305
306 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
307 {
308         if (!painter)
309                 return;
310
311         QPolygonF arrow;
312
313         // We draw the arrowhead aligned along the line from tail to head
314         double angle = Vector(head - tail).Angle();
315         double orthoAngle = angle + (PI / 2.0);
316         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
317         Vector unit = Vector(head - tail).Unit();
318
319         Point p1 = head - (unit * 9.0 * size);
320         Point p2 = p1 + (orthogonal * 3.0 * size);
321         Point p3 = p1 - (orthogonal * 3.0 * size);
322
323         Point p4 = CartesianToQtCoords(head);
324         Point p5 = CartesianToQtCoords(p2);
325         Point p6 = CartesianToQtCoords(p3);
326
327         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
328
329         painter->drawPolygon(arrow);
330 }
331
332
333 // Point is given in Cartesian coordinates
334 void Painter::DrawCrosshair(Vector point)
335 {
336         if (!painter)
337                 return;
338
339         Vector screenPoint = CartesianToQtCoords(point);
340         painter->drawLine(0, screenPoint.y, screenSize.x, screenPoint.y);
341         painter->drawLine(screenPoint.x, 0, screenPoint.x, screenSize.y);
342 }
343
344
345 void Painter::DrawInformativeText(QString text)
346 {
347         painter->setFont(*Global::font);
348         QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
349         bounds.moveTo(17.0, 17.0);
350         QRectF textRect = bounds;
351         textRect.adjust(-7.0, -7.0, 7.0, 7.0);
352
353         QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
354         painter->setPen(pen);
355         painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
356         painter->drawRoundedRect(textRect, 7.0, 7.0);
357
358         pen = QPen(QColor(0x00, 0x5F, 0xDF));
359         painter->setPen(pen);
360         painter->drawText(bounds, Qt::AlignVCenter, text);
361 }
362