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