]> Shamusworld >> Repos - ttedit/blob - src/painter.cpp
Minor fixes to polygon adding, added Tau constants to math package.
[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 0
152         if ((angle > PI * 0.5) && (angle < PI * 1.5))
153         {
154                 angle += PI;
155                 yOffset = 12.0 * Global::zoom * size;
156         }
157 #else
158         if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
159         {
160                 angle += HALF_TAU;
161                 yOffset = 12.0 * Global::zoom * size;
162         }
163 #endif
164
165         textBox.translate(0, yOffset);
166         painter->save();
167         painter->translate(center.x, center.y);
168         // Angles are backwards in the Qt coord system, so we flip ours...
169         painter->rotate(-angle * RADIANS_TO_DEGREES);
170 //Need to fix this so the text scales as well...
171         painter->drawText(textBox, Qt::AlignCenter, text);
172         painter->restore();
173 }
174
175
176 //
177 // Draw angled text. Draws text using point p as the upper left corner.
178 // Size is point size, angle is in radians (defaults to 0).
179 //
180 void Painter::DrawTextObject(Point p, QString text, double size, double angle/*= 0*/)
181 {
182         if (!painter)
183                 return;
184
185         p = CartesianToQtCoords(p);
186         painter->setFont(QFont("Arial", Global::zoom * size));
187         int textWidth = QFontMetrics(painter->font()).width(text);
188         int textHeight = QFontMetrics(painter->font()).height();
189
190         QRectF textBox(0, 0, textWidth, textHeight);
191         painter->save();
192         painter->translate(p.x, p.y);
193         // Angles are backwards in the Qt coord system, so we flip ours...
194         painter->rotate(-angle * RADIANS_TO_DEGREES);
195         painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop , text);
196         painter->restore();
197 }
198
199
200 void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
201 {
202         center = CartesianToQtCoords(center);
203         // Need to multiply scalar quantities by the zoom factor as well...
204         radius *= Global::zoom;
205         QRectF rectangle(QPointF(center.x - radius, center.y - radius),
206                 QPointF(center.x + radius, center.y + radius));
207         int angle1 = (int)(startAngle * RADIANS_TO_DEGREES * 16.0);
208         int angle2 = (int)(span * RADIANS_TO_DEGREES * 16.0);
209         painter->drawArc(rectangle, angle1, angle2);
210 }
211
212
213 void Painter::DrawEllipse(Vector center, double axis1, double axis2)
214 {
215         // Need to multiply scalar quantities by the zoom factor as well...
216         center = CartesianToQtCoords(center);
217         painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
218 }
219
220
221 // This function is for drawing object handles without regard for zoom level;
222 // we don't want our object handle size to depend on the zoom level!
223 void Painter::DrawHandle(Vector center)
224 {
225         center = CartesianToQtCoords(center);
226         painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
227         painter->setBrush(Qt::NoBrush);
228         painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
229 }
230
231
232 // This function is for drawing object handles without regard for zoom level;
233 // we don't want our object handle size to depend on the zoom level!
234 void Painter::DrawArrowHandle(Vector center, double angle)
235 {
236         center = CartesianToQtCoords(center);
237         QPolygonF arrow;
238
239         // Since we're drawing directly on the screen, the Y is inverted. So we use
240         // the mirror of the angle.
241 #if 0
242         double orthoAngle = -angle + (PI / 2.0);
243 #else
244         double orthoAngle = -angle + QTR_TAU;
245 #endif
246         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
247         Vector unit = Vector(cos(-angle), sin(-angle));
248
249         Point p0 = center + (unit * 6.0);
250         Point p1 = center + (unit * 21.0);
251         Point p1b = center + (unit * 11.0);
252         Point p2 = p1b + (orthogonal * 5.0);
253         Point p3 = p1b - (orthogonal * 5.0);
254
255         painter->drawLine(p0.x, p0.y, p1.x, p1.y);
256         arrow << QPointF(p1.x, p1.y) << QPointF(p2.x, p2.y) << QPointF(p3.x, p3.y);
257
258         painter->drawPolygon(arrow);
259 }
260
261
262 // This function is for drawing object handles without regard for zoom level;
263 // we don't want our object handle size to depend on the zoom level!
264 void Painter::DrawArrowToLineHandle(Vector center, double angle)
265 {
266         DrawArrowHandle(center, angle);
267         center = CartesianToQtCoords(center);
268
269         // Since we're drawing directly on the screen, the Y is inverted. So we use
270         // the mirror of the angle.
271 #if 0
272         double orthoAngle = -angle + (PI / 2.0);
273 #else
274         double orthoAngle = -angle + QTR_TAU;
275 #endif
276         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
277         Vector unit = Vector(cos(-angle), sin(-angle));
278
279         Point p1 = center + (unit * 21.0);
280         Point p2 = p1 + (orthogonal * 7.0);
281         Point p3 = p1 - (orthogonal * 7.0);
282
283         painter->drawLine(p2.x, p2.y, p3.x, p3.y);
284 }
285
286
287 void Painter::DrawLine(int x1, int y1, int x2, int y2)
288 {
289         if (!painter)
290                 return;
291
292         Vector v1 = CartesianToQtCoords(Vector(x1, y1));
293         Vector v2 = CartesianToQtCoords(Vector(x2, y2));
294         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
295 }
296
297
298 void Painter::DrawLine(IPoint p1, IPoint p2)
299 {
300         if (!painter)
301                 return;
302
303         Vector v1 = CartesianToQtCoords(Vector(p1.x, p1.y));
304         Vector v2 = CartesianToQtCoords(Vector(p2.x, p2.y));
305         painter->drawLine(v1.x, v1.y, v2.x, v2.y);
306 }
307
308
309 void Painter::DrawLine(Vector v1, Vector v2)
310 {
311         if (!painter)
312                 return;
313
314         v1 = CartesianToQtCoords(v1);
315         v2 = CartesianToQtCoords(v2);
316         painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
317 }
318
319
320 void Painter::DrawPoint(int x, int y)
321 {
322         if (!painter)
323                 return;
324
325         Vector v = CartesianToQtCoords(Vector(x, y));
326         painter->drawPoint(v.x, v.y);
327 }
328
329
330 // The rect passed in is in Qt coordinates...
331 void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
332 {
333         if (!painter)
334                 return;
335
336         painter->drawRoundedRect(rect, radiusX, radiusY);
337 }
338
339
340 // The rect passed in is in Cartesian but we want to pad it by a set number of
341 // pixels (currently set at 8), so the pad looks the same regardless of zoom.
342 void Painter::DrawPaddedRect(QRectF rect)
343 {
344         if (!painter)
345                 return;
346
347         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
348         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
349         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
350         screenRect.adjust(-8, 8, 8, -8);        // Left/top, right/bottom
351         painter->drawRect(screenRect);
352 }
353
354
355 void Painter::DrawRect(QRectF rect)
356 {
357         if (!painter)
358                 return;
359
360         Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
361         Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
362         QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
363         painter->drawRect(screenRect);
364 }
365
366
367 void Painter::DrawText(QRectF rect, int type, QString text)
368 {
369         if (!painter)
370                 return;
371
372         painter->drawText(rect, (Qt::AlignmentFlag)type, text);
373 }
374
375
376 void Painter::DrawArrowhead(Vector head, Vector tail, double size)
377 {
378         if (!painter)
379                 return;
380
381         QPolygonF arrow;
382
383         // We draw the arrowhead aligned along the line from tail to head
384         double angle = Vector(head - tail).Angle();
385 #if 0
386         double orthoAngle = angle + (PI / 2.0);
387 #else
388         double orthoAngle = angle + QTR_TAU;
389 #endif
390         Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
391         Vector unit = Vector(head - tail).Unit();
392
393         Point p1 = head - (unit * 9.0 * size);
394         Point p2 = p1 + (orthogonal * 3.0 * size);
395         Point p3 = p1 - (orthogonal * 3.0 * size);
396
397         Point p4 = CartesianToQtCoords(head);
398         Point p5 = CartesianToQtCoords(p2);
399         Point p6 = CartesianToQtCoords(p3);
400
401         arrow << QPointF(p4.x, p4.y) << QPointF(p5.x, p5.y) << QPointF(p6.x, p6.y);
402
403         painter->drawPolygon(arrow);
404 }
405
406
407 // Point is given in Cartesian coordinates
408 void Painter::DrawCrosshair(Vector point)
409 {
410         if (!painter)
411                 return;
412
413         Vector screenPoint = CartesianToQtCoords(point);
414         painter->drawLine(0, screenPoint.y, Global::screenSize.x, screenPoint.y);
415         painter->drawLine(screenPoint.x, 0, screenPoint.x, Global::screenSize.y);
416 }
417
418
419 void Painter::DrawInformativeText(QString text)
420 {
421         painter->setFont(*Global::font);
422         QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
423         bounds.moveTo(17.0, 17.0);
424         QRectF textRect = bounds;
425         textRect.adjust(-7.0, -7.0, 7.0, 7.0);
426
427         QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
428         painter->setPen(pen);
429         painter->setBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
430         painter->drawRoundedRect(textRect, 7.0, 7.0);
431
432         pen = QPen(QColor(0x00, 0x5F, 0xDF));
433         painter->setPen(pen);
434         painter->drawText(bounds, Qt::AlignVCenter, text);
435 }
436
437
438 void Painter::DrawBezier(Point p1, Point p2, Point p3)
439 {
440         p1 = CartesianToQtCoords(p1);
441         p2 = CartesianToQtCoords(p2);
442         p3 = CartesianToQtCoords(p3);
443
444         QPainterPath path;
445         path.moveTo(p1.x, p1.y);
446         path.quadTo(p2.x, p2.y, p3.x, p3.y);
447         painter->drawPath(path);
448 }
449
450
451 void Painter::DrawBezier(IPoint p1, IPoint p2, IPoint p3)
452 {
453         DrawBezier(Point(p1.x, p1.y), Point(p2.x, p2.y), Point(p3.x, p3.y));
454 }
455
456
457 //
458 // Draw a sqaure dot (5x5, centered on Vector; non-scaled)
459 //
460 void Painter::DrawSquareDot(Vector v)
461 {
462         QPoint pt[4];
463         v = CartesianToQtCoords(v);
464
465         pt[0] = QPoint(v.x - 2, v.y - 2);
466         pt[1] = QPoint(v.x + 2, v.y - 2);
467         pt[2] = QPoint(v.x + 2, v.y + 2);
468         pt[3] = QPoint(v.x - 2, v.y + 2);
469
470         painter->drawPolygon(pt, 4);
471 }
472
473
474 //
475 // Draw a round dot (5x5, centered on Vector; non-scaled)
476 //
477 void Painter::DrawRoundDot(Vector v)
478 {
479         QPoint pt[8];
480         v = CartesianToQtCoords(v);
481
482         pt[0] = QPoint(v.x - 1, v.y - 2);
483         pt[1] = QPoint(v.x + 1, v.y - 2);
484         pt[2] = QPoint(v.x + 2, v.y - 1);
485         pt[3] = QPoint(v.x + 2, v.y + 1);
486         pt[4] = QPoint(v.x + 1, v.y + 2);
487         pt[5] = QPoint(v.x - 1, v.y + 2);
488         pt[6] = QPoint(v.x - 2, v.y + 1);
489         pt[7] = QPoint(v.x - 2, v.y - 1);
490
491         painter->drawPolygon(pt, 8);
492 }
493
494
495 //
496 // Draw a sqaure dot (nxn, centered on Vector; non-scaled)
497 //
498 void Painter::DrawSquareDotN(Vector v, uint32_t n)
499 {
500         QPoint pt[4];
501         uint32_t offset = (n - 1) / 2;
502         v = CartesianToQtCoords(v);
503
504         pt[0] = QPoint(v.x - offset, v.y - offset);
505         pt[1] = QPoint(v.x + offset, v.y - offset);
506         pt[2] = QPoint(v.x + offset, v.y + offset);
507         pt[3] = QPoint(v.x - offset, v.y + offset);
508
509         painter->drawPolygon(pt, 4);
510 }
511
512
513 //
514 // Draw a round dot (nxn, centered on Vector; non-scaled)
515 //
516 void Painter::DrawRoundDotN(Vector v, uint32_t n)
517 {
518         int radius = (n / 2) + 1;
519         v = CartesianToQtCoords(v);
520         painter->drawEllipse(v.x - radius, v.y - radius, n, n);
521 }