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