]> Shamusworld >> Repos - architektonas/blob - src/painter.cpp
a649306c74ecd96be81cbba047c8cba253be5d99
[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
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 - origin.x) * zoom, screenSize.y - ((v.y - origin.y) * 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 / zoom) + origin.x, ((screenSize.y - v.y) / zoom) + 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 * zoom * size, -100.0 * zoom * size, 200.0 * zoom * size, 200.0 * 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 * 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 * 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 *= 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 * zoom, axis2 * 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 void Painter::DrawLine(int x1, int y1, int x2, int y2)
180 {
181         if (!painter)
182                 return;
183
184         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
185         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
186         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
187 }
188
189
190 void Painter::DrawLine(Vector v1, Vector v2)
191 {
192         if (!painter)
193                 return;
194
195         v1 = CartesianToQtCoords(v1);
196         v2 = CartesianToQtCoords(v2);
197         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
198 }
199
200
201 void Painter::DrawPoint(int x, int y)
202 {
203         if (!painter)
204                 return;
205
206         Vector v = CartesianToQtCoords(Vector(x, y));
207         painter->drawPoint(v.x, v.y);
208 }
209
210
211 // The rect passed in is in Qt coordinates...
212 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
213 {
214         if (!painter)
215                 return;
216
217         painter->drawRoundedRect(rect, radiusX, radiusY);
218 }
219
220
221 // The rect passed in is in Cartesian but we want to pad it by a set number of
222 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
223 void Painter::DrawPaddedRect(QRectF rect)
224 {
225         if (!painter)
226                 return;
227
228         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
229         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
230         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
231         screenRect.adjust(-8, 8, 8, -8);        // Left/top, right/bottom
232         painter->drawRect(screenRect);
233 }
234
235
236 void Painter::DrawRect(QRectF rect)
237 {
238         if (!painter)
239                 return;
240
241         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
242         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
243         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
244         painter->drawRect(screenRect);
245 }
246
247
248 void Painter::DrawText(QRectF rect, int type, QString text)
249 {
250         if (!painter)
251                 return;
252
253         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
254 }
255
256
257 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
258 {
259         if (!painter)
260                 return;
261
262         QPolygonF arrow;
263
264         // We draw the arrowhead aligned along the line from tail to head
265         double angle = Vector(head - tail).Angle();
266         double orthoAngle = angle + (PI / 2.0);
267         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
268         Vector unit = Vector(head - tail).Unit();
269
270         Point p1 = head - (unit * 9.0 * size);
271         Point p2 = p1 + (orthogonal * 3.0 * size);
272         Point p3 = p1 - (orthogonal * 3.0 * size);
273
274         Point p4 = CartesianToQtCoords(head);
275         Point p5 = CartesianToQtCoords(p2);
276         Point p6 = CartesianToQtCoords(p3);
277
278         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
279
280         painter->drawPolygon(arrow);
281 }
282
283
284 // Point is given in Cartesian coordinates
285 void Painter::DrawCrosshair(Vector point)
286 {
287         if (!painter)
288                 return;
289
290         Vector screenPoint = CartesianToQtCoords(point);
291         painter->drawLine(0, screenPoint.y, screenSize.x, screenPoint.y);
292         painter->drawLine(screenPoint.x, 0, screenPoint.x, screenSize.y);
293 }
294