]> Shamusworld >> Repos - architektonas/blob - src/painter.cpp
Added Architektonas drawing file loading/saving infrastructure.
[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 Painter::~Painter()
31 {
32 }
33
34 Vector Painter::CartesianToQtCoords(Vector v)
35 {
36         // Convert regular Cartesian coordinates to the inverted Y-axis Qt coordinates
37         // at the current origin and zoom level.
38         return Vector((v.x - origin.x) * zoom, screenSize.y - ((v.y - origin.y) * zoom));
39 }
40
41 Vector Painter::QtToCartesianCoords(Vector v)
42 {
43         // Convert screen location, with inverted Y-axis coordinates, to regular
44         // Cartesian coordinates at the current zoom level.
45         return Vector((v.x / zoom) + origin.x, ((screenSize.y - v.y) / zoom) + origin.y);
46 /*
47 How to do it:
48
49 e.g., we have a point on the screen at Qt coords of 10, 10, screenSize is 100, 100.
50 origin is -10, -10 and zoom level is 2 (200%)
51
52 1st, invert the Y: 10, 10 -> 10, 90
53 2nd, add origin:   10, 90 ->  0, 80 (no, not right--err, yes, it is)
54 3rd, aply zoom:     0, 80 ->  0, 40
55
56 or, is it:
57
58 1st, invert the Y: 10, 10 -> 10, 90
59 2nd, aply zoom:    10, 90 ->  5, 45
60 3rd, add origin:    5, 45 -> -5, 35
61
62 it depends on whether or not origin is in Qt coords or cartesian. If Qt, then the 1st
63 is correct, otherwise, the 2nd is correct.
64
65 The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to be correct.
66 */
67 }
68
69 void Painter::SetRenderHint(int hint)
70 {
71         if (!painter)
72                 return;
73
74         painter->setRenderHint((QPainter::RenderHint)hint);
75 }
76
77 void Painter::SetBrush(QBrush brush)
78 {
79         if (!painter)
80                 return;
81
82         painter->setBrush(brush);
83 }
84
85 void Painter::SetFont(QFont font)
86 {
87         if (!painter)
88                 return;
89
90         painter->setFont(font);
91 }
92
93 void Painter::SetPen(QPen pen)
94 {
95         if (!painter)
96                 return;
97
98         painter->setPen(pen);
99 }
100
101 void Painter::DrawAngledText(Vector center, double angle, QString text)
102 {
103         if (!painter)
104                 return;
105
106         // Strategy: Since Qt doesn't have any rotated text drawing functions,
107         // we instead translate the origin to the center of the text to be drawn and
108         // then rotate the frame to the desired angle.
109         center = CartesianToQtCoords(center);
110
111         // We may need this stuff... If dimension text is large enough.
112 //      int textWidth = QFontMetrics(painter->font()).width(text);
113 //      int textHeight = QFontMetrics(painter->font()).height();
114 // NOTE: SCREEN_ZOOM is a kludge to make things look right at screen resolution...
115         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
116
117         // This is in pixels. Might not render correctly at all zoom levels.
118         // Need to figure out if dimensions are always rendered at one size regardless of zoom,
119         // or if they have a definite size, and are thus zoomable.
120         // If zoomable, this is incorrect:
121         // (Added zoom, so this is correct now :-)
122         float yOffset = -12.0 * zoom * SCREEN_ZOOM;
123
124         // Fix text so it isn't upside down...
125         if ((angle > PI * 0.5) && (angle < PI * 1.5))
126         {
127                 angle += PI;
128                 yOffset = 12.0 * zoom * SCREEN_ZOOM;
129         }
130
131 #if 0
132         Vector offset = CartesianToQtCoords(Vector(0, yOffset));
133         textBox.translate(offset.x, offset.y);
134 #else
135         textBox.translate(0, yOffset);
136 #endif
137         painter->save();
138         painter->translate(center.x, center.y);
139         // Angles are backwards in the Qt coord system, so we flip ours...
140         painter->rotate(-angle * RADIANS_TO_DEGREES);
141 //Need to fix this so the text scales as well...
142         painter->drawText(textBox, Qt::AlignCenter, text);
143         painter->restore();
144 }
145
146 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
147 {
148         center = CartesianToQtCoords(center);
149         // Need to multiply scalar quantities by the zoom factor as well...
150         radius *= zoom;
151         QRectF rectangle(QPointF(center.x - radius, center.y - radius),
152                 QPointF(center.x + radius, center.y + radius));
153         int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
154         int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
155         painter->drawArc(rectangle, angle1, angle2);
156 }
157
158 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
159 {
160         // Need to multiply scalar quantities by the zoom factor as well...
161         center = CartesianToQtCoords(center);
162         painter->drawEllipse(QPointF(center.x, center.y), axis1 * zoom, axis2 * zoom);
163 }
164
165 // This function is for drawing object handles without regard for zoom level;
166 // we don't want our object handle size to depend on the zoom level!
167 void Painter::DrawHandle(Vector center)
168 {
169         center = CartesianToQtCoords(center);
170         painter->setBrush(Qt::NoBrush);
171         painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
172 }
173
174 void Painter::DrawLine(int x1, int y1, int x2, int y2)
175 {
176         if (!painter)
177                 return;
178
179         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
180         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
181         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
182 }
183
184 void Painter::DrawLine(Vector v1, Vector v2)
185 {
186         if (!painter)
187                 return;
188
189         v1 = CartesianToQtCoords(v1);
190         v2 = CartesianToQtCoords(v2);
191         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
192 }
193
194 void Painter::DrawPoint(int x, int y)
195 {
196         if (!painter)
197                 return;
198
199         Vector v = CartesianToQtCoords(Vector(x, y));
200         painter->drawPoint(v.x, v.y);
201 }
202
203 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
204 {
205         if (!painter)
206                 return;
207
208         painter->drawRoundedRect(rect, radiusX, radiusY);
209 }
210
211 void Painter::DrawText(QRectF rect, int type, QString text)
212 {
213         if (!painter)
214                 return;
215
216         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
217 }
218
219 void Painter::DrawArrowhead(Vector head, Vector tail)
220 {
221         QPolygonF arrow;
222
223         // We draw the arrowhead aligned along the line from tail to head
224         double angle = Vector(head - tail).Angle();
225         double orthoAngle = angle + (PI / 2.0);
226         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
227         Vector unit = Vector(head - tail).Unit();
228
229 // NOTE: SCREEN_ZOOM is a kludge to make things look right at scale...
230         Point p1 = head - (unit * 9.0 * SCREEN_ZOOM);
231         Point p2 = p1 + (orthogonal * 3.0 * SCREEN_ZOOM);
232         Point p3 = p1 - (orthogonal * 3.0 * SCREEN_ZOOM);
233
234         Point p4 = CartesianToQtCoords(head);
235         Point p5 = CartesianToQtCoords(p2);
236         Point p6 = CartesianToQtCoords(p3);
237
238         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
239
240         painter->drawPolygon(arrow);
241 }
242