1 #include "IsometricBackgroundPainter.h"
2
3 #include <cmath>
4
5 #include "Util.h"
6
IsometricBackgroundPainter(bool drawLines)7 IsometricBackgroundPainter::IsometricBackgroundPainter(bool drawLines): drawLines(drawLines) {}
8
9 IsometricBackgroundPainter::~IsometricBackgroundPainter() = default;
10
resetConfig()11 void IsometricBackgroundPainter::resetConfig() {
12 this->defaultForegroundColor1 = 0xBDBDBDU;
13 this->defaultAlternativeForegroundColor1 = 0x434343U;
14 this->lineWidth = drawLines ? 1.0 : 1.5;
15 this->drawRaster1 = 14.17;
16 }
17
18 namespace {
19 template <class DrawFunc>
paintBackgroundDotted(int cols,int rows,double xstep,double ystep,DrawFunc drawDot)20 void paintBackgroundDotted(int cols, int rows, double xstep, double ystep, DrawFunc drawDot) {
21 for (int col = 0; col <= cols; ++col) {
22 const auto x = col * xstep;
23
24 const auto evenCol = col % 2 == 0;
25 const auto yoffset = evenCol ? ystep : 0.0;
26 const auto rowsInCol = evenCol ? rows - 2 : rows;
27
28 for (int row = 0; row <= rowsInCol; row += 2) {
29 const auto y = yoffset + row * ystep;
30 drawDot(x, y);
31 }
32 }
33 }
34
35 template <class DrawFunc>
paintBackgroundGraph(int cols,int rows,double xstep,double ystep,DrawFunc drawLine)36 void paintBackgroundGraph(int cols, int rows, double xstep, double ystep, DrawFunc drawLine) {
37 const auto contentWidth = cols * xstep;
38 const auto contentHeight = rows * ystep;
39
40 // Draw Orthogonal Grid
41 drawLine(0.0, 0.0, contentWidth, 0.0); // top
42 drawLine(0.0, contentHeight, contentWidth, contentHeight); // bottom
43
44 for (int col = 0; col <= cols; ++col) {
45 const auto x = col * xstep;
46 drawLine(x, 0.0, x, contentHeight);
47 }
48
49 // Determine the number of diagonals to draw
50 auto hdiags = static_cast<int>(std::floor(cols / 2));
51 auto vdiags = static_cast<int>(std::floor(rows / 2));
52 auto diags = hdiags + vdiags;
53 auto hcorr = cols - 2 * hdiags;
54 auto vcorr = rows - 2 * vdiags;
55
56 // Draw diagonals starting in the top left corner (left-down)
57 for (int d = 0; d < diags + hcorr * vcorr; ++d) {
58 // Point 1 travels horizontally from top left to top right,
59 // then from top right to bottom right.
60 double x1 = contentWidth, y1 = 0.0;
61 if (d < hdiags) {
62 x1 = xstep + d * 2 * xstep;
63 } else {
64 y1 = ystep + (d - hdiags) * 2 * ystep - hcorr * ystep;
65 }
66
67 // Point 2 travels verticlally from top left to bottom left,
68 // then from bottom left to bottom right.
69 double x2 = 0.0, y2 = contentHeight;
70 if (d < vdiags) {
71 y2 = ystep + d * 2 * ystep;
72 } else {
73 x2 = xstep + (d - vdiags) * 2 * xstep - vcorr * xstep;
74 }
75
76 drawLine(x1, y1, x2, y2);
77 }
78
79 // Draw diagonals starting in the top right corner (right-down)
80 for (int d = 0; d < diags; ++d) {
81 // Point 1 travels horizontally from top right to top left,
82 // then from top left to bottom left.
83 double x1 = 0.0, y1 = 0.0;
84 if (d < hdiags) {
85 x1 = contentWidth - (xstep + d * 2 * xstep) - hcorr * xstep;
86 } else {
87 y1 = ystep + (d - hdiags) * 2 * ystep;
88 }
89
90 // Point 2 travels vertically from top right to bottom right,
91 // then from top bottom right to bottom left.
92 double x2 = contentWidth, y2 = contentHeight;
93 if (d < vdiags) {
94 y2 = ystep + d * 2 * ystep + hcorr * ystep;
95 } else {
96 x2 = contentWidth - (xstep + (d - vdiags) * 2 * xstep) + (vcorr - hcorr) * xstep;
97 }
98
99 drawLine(x1, y1, x2, y2);
100 }
101 }
102 } // namespace
103
paint()104 void IsometricBackgroundPainter::paint() {
105 paintBackgroundColor();
106
107 Util::cairo_set_source_rgbi(cr, this->foregroundColor1);
108
109 cairo_set_line_width(cr, lineWidth * lineWidthFactor);
110 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
111
112 const auto xstep = std::sqrt(3.0) / 2.0 * drawRaster1;
113 const auto ystep = drawRaster1 / 2.0;
114
115 // Deduce the maximum grid size
116 const auto margin = drawRaster1;
117 const int cols = static_cast<int>(std::floor((width - 2 * margin) / xstep));
118 const int rows = static_cast<int>(std::floor((height - 2 * margin) / ystep));
119
120 // Center the grid on the page
121 const auto contentWidth = cols * xstep;
122 const auto contentHeight = rows * ystep;
123 const auto contentXOffset = (width - contentWidth) / 2;
124 const auto contentYOffset = (height - contentHeight) / 2;
125
126 if (drawLines) {
127 auto drawLine = [&](double x1, double y1, double x2, double y2) {
128 cairo_move_to(cr, contentXOffset + x1, contentYOffset + y1);
129 cairo_line_to(cr, contentXOffset + x2, contentYOffset + y2);
130 };
131 paintBackgroundGraph(cols, rows, xstep, ystep, drawLine);
132 } else {
133 auto drawDot = [&](double x, double y) {
134 cairo_move_to(cr, contentXOffset + x, contentYOffset + y);
135 cairo_line_to(cr, contentXOffset + x, contentYOffset + y);
136 };
137 paintBackgroundDotted(cols, rows, xstep, ystep, drawDot);
138 }
139
140 cairo_stroke(cr);
141 }
142