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