]> Shamusworld >> Repos - ttedit/blob - src/painter.cpp
e2a457a5499a3cff2ba5732e9cc399080c57986a
[ttedit] / 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 #include "global.h"
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
39         // coordinates at the current origin and zoom level.
40         return Vector((v.x - Global::origin.x) * Global::zoom, Global::screenSize.y - ((v.y - Global::origin.y) * Global::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 / Global::zoom) + Global::origin.x, ((Global::screenSize.y - v.y) / Global::zoom) + Global::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::SetPen(QPen pen)
83 {
84         if (!painter)
85                 return;
86
87         painter->setPen(pen);
88 }
89
90
91 void Painter::SetPen(uint32_t color, float size/*= 0*/, int style/*= 0*/)
92 {
93         if (!painter)
94                 return;
95
96         // We can cast style as Qt:PenStyle because they line up 1-to-1
97         painter->setPen(QPen(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255),
98                 size, (Qt::PenStyle)style));
99 }
100
101
102 void Painter::SetBrush(QBrush brush)
103 {
104         if (!painter)
105                 return;
106
107         painter->setBrush(brush);
108 }
109
110
111 void Painter::SetBrush(uint32_t color)
112 {
113         if (!painter)
114                 return;
115
116         painter->setBrush(QBrush(QColor(color >> 16, (color >> 8) & 0xFF, color & 0xFF, 255)));
117 }
118
119
120 void Painter::SetFont(QFont font)
121 {
122         if (!painter)
123                 return;
124
125         painter->setFont(font);
126 }
127
128
129 void Painter::DrawAngledText(Vector center, double angle, QString text, double size)
130 {
131         if (!painter)
132                 return;
133
134         // Strategy: Since Qt doesn't have any rotated text drawing functions,
135         // we instead translate the origin to the center of the text to be drawn and
136         // then rotate the frame to the desired angle.
137         center = CartesianToQtCoords(center);
138
139         // We may need this stuff... If dimension text is large enough.
140 //      int textWidth = QFontMetrics(painter->font()).width(text);
141 //      int textHeight = QFontMetrics(painter->font()).height();
142         QRectF textBox(-100.0 * Global::zoom * size, -100.0 * Global::zoom * size, 200.0 * Global::zoom * size, 200.0 * Global::zoom * size);   // x, y, w, h; x/y = upper left corner
143
144         // This is in pixels. Might not render correctly at all zoom levels.
145         // Need to figure out if dimensions are always rendered at one size
146         // regardless of zoom, or if they have a definite size, and are thus
147         // zoomable.
148         float yOffset = -12.0 * Global::zoom * size;
149
150         // Fix text so it isn't upside down...
151         if ((angle > PI * 0.5) && (angle < PI * 1.5))
152         {
153                 angle += PI;
154                 yOffset = 12.0 * Global::zoom * size;
155         }
156
157         textBox.translate(0, yOffset);
158         painter->save();
159         painter->translate(center.x, center.y);
160         // Angles are backwards in the Qt coord system, so we flip ours...
161         painter->rotate(-angle * RADIANS_TO_DEGREES);
162 //Need to fix this so the text scales as well...
163         painter->drawText(textBox, Qt::AlignCenter, text);
164         painter->restore();
165 }
166
167
168 //
169 // Draw angled text. Draws text using point p as the upper left corner.
170 // Size is point size, angle is in radians (defaults to 0).
171 //
172 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
173 {
174         if (!painter)
175                 return;
176
177         p = CartesianToQtCoords(p);
178         painter->setFont(QFont("Arial", Global::zoom * size));
179         int textWidth = QFontMetrics(painter->font()).width(text);
180         int textHeight = QFontMetrics(painter->font()).height();
181
182         QRectF textBox(0, 0, textWidth, textHeight);
183         painter->save();
184         painter->translate(p.x, p.y);
185         // Angles are backwards in the Qt coord system, so we flip ours...
186         painter->rotate(-angle * RADIANS_TO_DEGREES);
187         painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
188         painter->restore();
189 }
190
191
192 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
193 {
194         center = CartesianToQtCoords(center);
195         // Need to multiply scalar quantities by the zoom factor as well...
196         radius *= Global::zoom;
197         QRectF rectangle(QPointF(center.x - radius, center.y - radius),
198                 QPointF(center.x + radius, center.y + radius));
199         int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
200         int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
201         painter->drawArc(rectangle, angle1, angle2);
202 }
203
204
205 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
206 {
207         // Need to multiply scalar quantities by the zoom factor as well...
208         center = CartesianToQtCoords(center);
209         painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
210 }
211
212
213 // This function is for drawing object handles without regard for zoom level;
214 // we don't want our object handle size to depend on the zoom level!
215 void Painter::DrawHandle(Vector center)
216 {
217         center = CartesianToQtCoords(center);
218         painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
219         painter->setBrush(Qt::NoBrush);
220         painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
221 }
222
223
224 // This function is for drawing object handles without regard for zoom level;
225 // we don't want our object handle size to depend on the zoom level!
226 void Painter::DrawArrowHandle(Vector center, double angle)
227 {
228         center = CartesianToQtCoords(center);
229         QPolygonF arrow;
230
231         // Since we're drawing directly on the screen, the Y is inverted. So we use
232         // the mirror of the angle.
233         double orthoAngle = -angle + (PI / 2.0);
234         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
235         Vector unit = Vector(cos(-angle), sin(-angle));
236
237         Point p0 = center + (unit * 6.0);
238         Point p1 = center + (unit * 21.0);
239         Point p1b = center + (unit * 11.0);
240         Point p2 = p1b + (orthogonal * 5.0);
241         Point p3 = p1b - (orthogonal * 5.0);
242
243         painter->drawLine(p0.x, p0.y, p1.x, p1.y);
244         arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
245
246         painter->drawPolygon(arrow);
247 }
248
249
250 // This function is for drawing object handles without regard for zoom level;
251 // we don't want our object handle size to depend on the zoom level!
252 void Painter::DrawArrowToLineHandle(Vector center, double angle)
253 {
254         DrawArrowHandle(center, angle);
255         center = CartesianToQtCoords(center);
256
257         // Since we're drawing directly on the screen, the Y is inverted. So we use
258         // the mirror of the angle.
259         double orthoAngle = -angle + (PI / 2.0);
260         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
261         Vector unit = Vector(cos(-angle), sin(-angle));
262
263         Point p1 = center + (unit * 21.0);
264         Point p2 = p1 + (orthogonal * 7.0);
265         Point p3 = p1 - (orthogonal * 7.0);
266
267         painter->drawLine(p2.x, p2.y, p3.x, p3.y);
268 }
269
270
271 void Painter::DrawLine(int x1, int y1, int x2, int y2)
272 {
273         if (!painter)
274                 return;
275
276         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
277         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
278         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
279 }
280
281
282 void Painter::DrawLine(IPoint p1, IPoint p2)
283 {
284         if (!painter)
285                 return;
286
287         Vector v1 = CartesianToQtCoords(Vector(p1.x, p1.y));
288         Vector v2 = CartesianToQtCoords(Vector(p2.x, p2.y));
289         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
290 }
291
292
293 void Painter::DrawLine(Vector v1, Vector v2)
294 {
295         if (!painter)
296                 return;
297
298         v1 = CartesianToQtCoords(v1);
299         v2 = CartesianToQtCoords(v2);
300         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
301 }
302
303
304 void Painter::DrawPoint(int x, int y)
305 {
306         if (!painter)
307                 return;
308
309         Vector v = CartesianToQtCoords(Vector(x, y));
310         painter->drawPoint(v.x, v.y);
311 }
312
313
314 // The rect passed in is in Qt coordinates...
315 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
316 {
317         if (!painter)
318                 return;
319
320         painter->drawRoundedRect(rect, radiusX, radiusY);
321 }
322
323
324 // The rect passed in is in Cartesian but we want to pad it by a set number of
325 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
326 void Painter::DrawPaddedRect(QRectF rect)
327 {
328         if (!painter)
329                 return;
330
331         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
332         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
333         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
334         screenRect.adjust(-8, 8, 8, -8);        // Left/top, right/bottom
335         painter->drawRect(screenRect);
336 }
337
338
339 void Painter::DrawRect(QRectF rect)
340 {
341         if (!painter)
342                 return;
343
344         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
345         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
346         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
347         painter->drawRect(screenRect);
348 }
349
350
351 void Painter::DrawText(QRectF rect, int type, QString text)
352 {
353         if (!painter)
354                 return;
355
356         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
357 }
358
359
360 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
361 {
362         if (!painter)
363                 return;
364
365         QPolygonF arrow;
366
367         // We draw the arrowhead aligned along the line from tail to head
368         double angle = Vector(head - tail).Angle();
369         double orthoAngle = angle + (PI / 2.0);
370         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
371         Vector unit = Vector(head - tail).Unit();
372
373         Point p1 = head - (unit * 9.0 * size);
374         Point p2 = p1 + (orthogonal * 3.0 * size);
375         Point p3 = p1 - (orthogonal * 3.0 * size);
376
377         Point p4 = CartesianToQtCoords(head);
378         Point p5 = CartesianToQtCoords(p2);
379         Point p6 = CartesianToQtCoords(p3);
380
381         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
382
383         painter->drawPolygon(arrow);
384 }
385
386
387 // Point is given in Cartesian coordinates
388 void Painter::DrawCrosshair(Vector point)
389 {
390         if (!painter)
391                 return;
392
393         Vector screenPoint = CartesianToQtCoords(point);
394         painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
395         painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
396 }
397
398
399 void Painter::DrawInformativeText(QString text)
400 {
401         painter->setFont(*Global::font);
402         QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
403         bounds.moveTo(17.0, 17.0);
404         QRectF textRect = bounds;
405         textRect.adjust(-7.0, -7.0, 7.0, 7.0);
406
407         QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
408         painter->setPen(pen);
409         painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
410         painter->drawRoundedRect(textRect, 7.0, 7.0);
411
412         pen = QPen(QColor(0x00, 0x5F, 0xDF));
413         painter->setPen(pen);
414         painter->drawText(bounds, Qt::AlignVCenter, text);
415 }
416
417
418 void Painter::DrawBezier(Point p1, Point p2, Point p3)
419 {
420         p1 = CartesianToQtCoords(p1);
421         p2 = CartesianToQtCoords(p2);
422         p3 = CartesianToQtCoords(p3);
423
424         QPainterPath path;
425         path.moveTo(p1.x, p1.y);
426         path.quadTo(p2.x, p2.y, p3.x, p3.y);
427         painter->drawPath(path);
428 }
429
430
431 void Painter::DrawBezier(IPoint p1, IPoint p2, IPoint p3)
432 {
433         DrawBezier(Point(p1.x, p1.y), Point(p2.x, p2.y), Point(p3.x, p3.y));
434 }
435
436
437 //
438 // Draw a sqaure dot (5x5, centered on Vector; non-scaled)
439 //
440 void Painter::DrawSquareDot(Vector v)
441 {
442         QPoint pt[4];
443         v = CartesianToQtCoords(v);
444
445         pt[0] = QPoint(v.x - 2, v.y - 2);
446         pt[1] = QPoint(v.x + 2, v.y - 2);
447         pt[2] = QPoint(v.x + 2, v.y + 2);
448         pt[3] = QPoint(v.x - 2, v.y + 2);
449
450         painter->drawPolygon(pt, 4);
451 }
452
453
454 //
455 // Draw a round dot (5x5, centered on Vector; non-scaled)
456 //
457 void Painter::DrawRoundDot(Vector v)
458 {
459         QPoint pt[8];
460         v = CartesianToQtCoords(v);
461
462         pt[0] = QPoint(v.x - 1, v.y - 2);
463         pt[1] = QPoint(v.x + 1, v.y - 2);
464         pt[2] = QPoint(v.x + 2, v.y - 1);
465         pt[3] = QPoint(v.x + 2, v.y + 1);
466         pt[4] = QPoint(v.x + 1, v.y + 2);
467         pt[5] = QPoint(v.x - 1, v.y + 2);
468         pt[6] = QPoint(v.x - 2, v.y + 1);
469         pt[7] = QPoint(v.x - 2, v.y - 1);
470
471         painter->drawPolygon(pt, 8);
472 }
473
474
475 //
476 // Draw a sqaure dot (nxn, centered on Vector; non-scaled)
477 //
478 void Painter::DrawSquareDotN(Vector v, uint32_t n)
479 {
480         QPoint pt[4];
481         uint32_t offset = (n - 1) / 2;
482         v = CartesianToQtCoords(v);
483
484         pt[0] = QPoint(v.x - offset, v.y - offset);
485         pt[1] = QPoint(v.x + offset, v.y - offset);
486         pt[2] = QPoint(v.x + offset, v.y + offset);
487         pt[3] = QPoint(v.x - offset, v.y + offset);
488
489         painter->drawPolygon(pt, 4);
490 }
491
492
493 //
494 // Draw a round dot (nxn, centered on Vector; non-scaled)
495 //
496 void Painter::DrawRoundDotN(Vector v, uint32_t n)
497 {
498         int radius = (n / 2) + 1;
499         v = CartesianToQtCoords(v);
500         painter->drawEllipse(v.x - radius, v.y - radius, n, n);
501 }