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