]> Shamusworld >> Repos - architektonas/blob - src/painter.cpp
2a5ed33eaef1a0f200b223d9145c1076d386d33b
[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 // NOTE: SCREEN_ZOOM is a kludge to make things look right at screen resolution...
123 //      QRectF textBox(-100.0 * zoom * SCREEN_ZOOM, -100.0 * zoom * SCREEN_ZOOM, 200.0 * zoom * SCREEN_ZOOM, 200.0 * zoom * SCREEN_ZOOM);       // x, y, w, h; x/y = upper left corner
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 regardless of zoom,
128         // or if they have a definite size, and are thus zoomable.
129         // If zoomable, this is incorrect:
130         // (Added zoom, so this is correct now :-)
131 //      float yOffset = -12.0 * zoom * SCREEN_ZOOM;
132         float yOffset = -12.0 * zoom * size;
133
134         // Fix text so it isn't upside down...
135         if ((angle > PI * 0.5) && (angle < PI * 1.5))
136         {
137                 angle += PI;
138 //              yOffset = 12.0 * zoom * SCREEN_ZOOM;
139                 yOffset = 12.0 * zoom * size;
140         }
141
142 #if 0
143         Vector offset = CartesianToQtCoords(Vector(0, yOffset));
144         textBox.translate(offset.x, offset.y);
145 #else
146         textBox.translate(0, yOffset);
147 #endif
148         painter->save();
149         painter->translate(center.x, center.y);
150         // Angles are backwards in the Qt coord system, so we flip ours...
151         painter->rotate(-angle * RADIANS_TO_DEGREES);
152 //Need to fix this so the text scales as well...
153         painter->drawText(textBox, Qt::AlignCenter, text);
154         painter->restore();
155 }
156
157
158 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
159 {
160         center = CartesianToQtCoords(center);
161         // Need to multiply scalar quantities by the zoom factor as well...
162         radius *= zoom;
163         QRectF rectangle(QPointF(center.x - radius, center.y - radius),
164                 QPointF(center.x + radius, center.y + radius));
165         int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
166         int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
167         painter->drawArc(rectangle, angle1, angle2);
168 }
169
170
171 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
172 {
173         // Need to multiply scalar quantities by the zoom factor as well...
174         center = CartesianToQtCoords(center);
175         painter->drawEllipse(QPointF(center.x, center.y), axis1 * zoom, axis2 * zoom);
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::DrawHandle(Vector center)
182 {
183         center = CartesianToQtCoords(center);
184         painter->setBrush(Qt::NoBrush);
185         painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
186 }
187
188
189 void Painter::DrawLine(int x1, int y1, int x2, int y2)
190 {
191         if (!painter)
192                 return;
193
194         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
195         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
196         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
197 }
198
199
200 void Painter::DrawLine(Vector v1, Vector v2)
201 {
202         if (!painter)
203                 return;
204
205         v1 = CartesianToQtCoords(v1);
206         v2 = CartesianToQtCoords(v2);
207         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
208 }
209
210
211 void Painter::DrawPoint(int x, int y)
212 {
213         if (!painter)
214                 return;
215
216         Vector v = CartesianToQtCoords(Vector(x, y));
217         painter->drawPoint(v.x, v.y);
218 }
219
220
221 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
222 {
223         if (!painter)
224                 return;
225
226         painter->drawRoundedRect(rect, radiusX, radiusY);
227 }
228
229
230 void Painter::DrawRect(QRectF rect)
231 {
232         if (!painter)
233                 return;
234
235         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
236         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
237         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
238         painter->drawRect(screenRect);
239 }
240
241
242 void Painter::DrawText(QRectF rect, int type, QString text)
243 {
244         if (!painter)
245                 return;
246
247         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
248 }
249
250
251 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
252 {
253         QPolygonF arrow;
254
255         // We draw the arrowhead aligned along the line from tail to head
256         double angle = Vector(head - tail).Angle();
257         double orthoAngle = angle + (PI / 2.0);
258         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
259         Vector unit = Vector(head - tail).Unit();
260
261 #if 0
262 // NOTE: SCREEN_ZOOM is a kludge to make things look right at scale...
263         Point p1 = head - (unit * 9.0 * SCREEN_ZOOM);
264         Point p2 = p1 + (orthogonal * 3.0 * SCREEN_ZOOM);
265         Point p3 = p1 - (orthogonal * 3.0 * SCREEN_ZOOM);
266 #else
267         Point p1 = head - (unit * 9.0 * size);
268         Point p2 = p1 + (orthogonal * 3.0 * size);
269         Point p3 = p1 - (orthogonal * 3.0 * size);
270 #endif
271
272         Point p4 = CartesianToQtCoords(head);
273         Point p5 = CartesianToQtCoords(p2);
274         Point p6 = CartesianToQtCoords(p3);
275
276         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
277
278         painter->drawPolygon(arrow);
279 }
280