|
@@ -43,41 +43,33 @@ using namespace ExtUI;
|
43
|
43
|
#define GRID_COLS 5
|
44
|
44
|
#define GRID_ROWS 5
|
45
|
45
|
|
46
|
|
- #define MESH_POS BTN_POS(2,1), BTN_SIZE(4,5)
|
47
|
|
- #define Z_LABEL_POS BTN_POS(1,3), BTN_SIZE(1,1)
|
48
|
|
- #define Z_VALUE_POS BTN_POS(1,4), BTN_SIZE(2,1)
|
49
|
|
- #define WAIT_POS BTN_POS(1,3), BTN_SIZE(2,2)
|
50
|
|
- #define BACK_POS BTN_POS(1,5), BTN_SIZE(2,1)
|
|
46
|
+ #define MESH_POS BTN_POS(1,1), BTN_SIZE(3,5)
|
|
47
|
+ #define Z_LABEL_POS BTN_POS(4,2), BTN_SIZE(2,1)
|
|
48
|
+ #define Z_VALUE_POS BTN_POS(4,3), BTN_SIZE(2,1)
|
|
49
|
+ #define WAIT_POS BTN_POS(4,2), BTN_SIZE(2,2)
|
|
50
|
+ #define BACK_POS BTN_POS(4,5), BTN_SIZE(2,1)
|
51
|
51
|
#endif
|
52
|
52
|
|
53
|
|
-void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::bed_mesh_t data, uint8_t opts) {
|
54
|
|
- CommandProcessor cmd;
|
|
53
|
+void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::bed_mesh_t data, uint8_t opts, float autoscale_max) {
|
|
54
|
+ constexpr uint8_t rows = GRID_MAX_POINTS_Y;
|
|
55
|
+ constexpr uint8_t cols = GRID_MAX_POINTS_X;
|
55
|
56
|
|
56
|
|
- #define TRANSFORM_2(X,Y,Z) (X), (Y) // No transform
|
57
|
|
- #define TRANSFORM_1(X,Y,Z) TRANSFORM_2((X) + (Y) * slant, (Y) - (Z), 0) // Perspective
|
58
|
|
- #define TRANSFORM(X,Y,Z) TRANSFORM_1(float(X)/(cols-1) - 0.5, float(Y)/(rows-1) - 0.5, (Z)) // Normalize
|
|
57
|
+ #define VALUE(X,Y) (data ? data[X][Y] : 0)
|
|
58
|
+ #define ISVAL(X,Y) (data ? !isnan(VALUE(X,Y)) : true)
|
|
59
|
+ #define HEIGHT(X,Y) (ISVAL(X,Y) ? (VALUE(X,Y) - val_min) * scale_z : 0)
|
59
|
60
|
|
60
|
|
- constexpr uint8_t rows = GRID_MAX_POINTS_Y;
|
61
|
|
- constexpr uint8_t cols = GRID_MAX_POINTS_X;
|
62
|
|
- const float slant = 0.5;
|
63
|
|
- const float bounds_min[] = {TRANSFORM(0 ,0 ,0)};
|
64
|
|
- const float bounds_max[] = {TRANSFORM(cols,rows,0)};
|
65
|
|
- const float scale_x = float(w)/(bounds_max[0] - bounds_min[0]);
|
66
|
|
- const float scale_y = float(h)/(bounds_max[1] - bounds_min[1]);
|
67
|
|
- const float center_x = x + w/2;
|
68
|
|
- const float center_y = y + h/2;
|
|
61
|
+ // Compute the mean, min and max for the points
|
69
|
62
|
|
70
|
63
|
float val_mean = 0;
|
71
|
64
|
float val_max = -INFINITY;
|
72
|
65
|
float val_min = INFINITY;
|
73
|
66
|
uint8_t val_cnt = 0;
|
74
|
67
|
|
75
|
|
- if (opts & USE_AUTOSCALE) {
|
76
|
|
- // Compute the mean
|
|
68
|
+ if (data && (opts & USE_AUTOSCALE)) {
|
77
|
69
|
for (uint8_t y = 0; y < rows; y++) {
|
78
|
70
|
for (uint8_t x = 0; x < cols; x++) {
|
79
|
|
- const float val = data[x][y];
|
80
|
|
- if (!isnan(val)) {
|
|
71
|
+ if (ISVAL(x,y)) {
|
|
72
|
+ const float val = VALUE(x,y);
|
81
|
73
|
val_mean += val;
|
82
|
74
|
val_max = max(val_max, val);
|
83
|
75
|
val_min = min(val_min, val);
|
|
@@ -85,27 +77,56 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
|
85
|
77
|
}
|
86
|
78
|
}
|
87
|
79
|
}
|
88
|
|
- if (val_cnt) {
|
89
|
|
- val_mean /= val_cnt;
|
90
|
|
- val_min -= val_mean;
|
91
|
|
- val_max -= val_mean;
|
92
|
|
- } else {
|
93
|
|
- val_mean = 0;
|
94
|
|
- val_min = 0;
|
95
|
|
- val_max = 0;
|
96
|
|
- }
|
97
|
80
|
}
|
|
81
|
+ if (val_cnt) {
|
|
82
|
+ val_mean /= val_cnt;
|
|
83
|
+ } else {
|
|
84
|
+ val_mean = 0;
|
|
85
|
+ val_min = 0;
|
|
86
|
+ val_max = 0;
|
|
87
|
+ }
|
|
88
|
+
|
|
89
|
+ const float scale_z = ((val_max == val_min) ? 1 : 1/(val_max - val_min)) * autoscale_max;
|
|
90
|
+
|
|
91
|
+ // These equations determine the appearance of the grid on the screen.
|
|
92
|
+
|
|
93
|
+ #define TRANSFORM_5(X,Y,Z) (X), (Y) // No transform
|
|
94
|
+ #define TRANSFORM_4(X,Y,Z) TRANSFORM_5((X)/(Z),(Y)/-(Z), 0) // Perspective
|
|
95
|
+ #define TRANSFORM_3(X,Y,Z) TRANSFORM_4((X), (Z), (Y)) // Swap Z and Y
|
|
96
|
+ #define TRANSFORM_2(X,Y,Z) TRANSFORM_3((X), (Y) + 2.5, (Z) - 1) // Translate
|
|
97
|
+ #define TRANSFORM(X,Y,Z) TRANSFORM_2(float(X)/(cols-1) - 0.5, float(Y)/(rows-1) - 0.5, (Z)) // Normalize
|
|
98
|
+
|
|
99
|
+ // Compute the bounding box for the grid prior to scaling. Do this at compile-time by
|
|
100
|
+ // transforming the four corner points via the transformation equations and finding
|
|
101
|
+ // the min and max for each axis.
|
98
|
102
|
|
99
|
|
- const float scale_z = ((val_max == val_min) ? 1 : 1/(val_max - val_min)) * 0.1;
|
|
103
|
+ constexpr float bounds[][3] = {{TRANSFORM(0 , 0 , 0)},
|
|
104
|
+ {TRANSFORM(cols-1, 0 , 0)},
|
|
105
|
+ {TRANSFORM(0 , rows-1, 0)},
|
|
106
|
+ {TRANSFORM(cols-1, rows-1, 0)}};
|
|
107
|
+ #define APPLY(FUNC, AXIS) FUNC(FUNC(bounds[0][AXIS], bounds[1][AXIS]), FUNC(bounds[2][AXIS], bounds[3][AXIS]))
|
|
108
|
+ constexpr float grid_x = APPLY(min,0);
|
|
109
|
+ constexpr float grid_y = APPLY(min,1);
|
|
110
|
+ constexpr float grid_w = APPLY(max,0) - grid_x;
|
|
111
|
+ constexpr float grid_h = APPLY(max,1) - grid_y;
|
|
112
|
+ constexpr float grid_cx = grid_x + grid_w/2;
|
|
113
|
+ constexpr float grid_cy = grid_y + grid_h/2;
|
100
|
114
|
|
101
|
|
- #undef TRANSFORM_2
|
102
|
|
- #define TRANSFORM_2(X,Y,Z) center_x + (X) * scale_x, center_y + (Y) * scale_y // Scale and position
|
103
|
|
- #define VALUE(X,Y) ((data && ISVAL(X,Y)) ? data[X][Y] : 0)
|
104
|
|
- #define ISVAL(X,Y) (data ? !isnan(data[X][Y]) : true)
|
105
|
|
- #define HEIGHT(X,Y) (VALUE(X,Y) * scale_z)
|
|
115
|
+ // Figure out scale and offset such that the grid fits within the rectangle given by (x,y,w,h)
|
106
|
116
|
|
107
|
|
- uint16_t basePointSize = min(scale_x,scale_y) / max(cols,rows);
|
|
117
|
+ const float scale_x = float(w)/grid_w;
|
|
118
|
+ const float scale_y = float(h)/grid_h;
|
|
119
|
+ const float center_x = x + w/2;
|
|
120
|
+ const float center_y = y + h/2;
|
108
|
121
|
|
|
122
|
+ #undef TRANSFORM_5
|
|
123
|
+ #define TRANSFORM_5(X,Y,Z) center_x + (X - grid_cx) * scale_x, center_y + (Y - grid_cy) * scale_y // Fit to bounds
|
|
124
|
+
|
|
125
|
+ // Draw the grid
|
|
126
|
+
|
|
127
|
+ const uint16_t basePointSize = min(w,h) / max(cols,rows);
|
|
128
|
+
|
|
129
|
+ CommandProcessor cmd;
|
109
|
130
|
cmd.cmd(SAVE_CONTEXT())
|
110
|
131
|
.cmd(VERTEX_FORMAT(0))
|
111
|
132
|
.cmd(TAG_MASK(false))
|
|
@@ -126,13 +147,15 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
|
126
|
147
|
}
|
127
|
148
|
|
128
|
149
|
if (opts & USE_POINTS) {
|
|
150
|
+ const float sq_min = sq(val_min - val_mean);
|
|
151
|
+ const float sq_max = sq(val_max - val_mean);
|
129
|
152
|
cmd.cmd(POINT_SIZE(basePointSize * 2));
|
130
|
153
|
cmd.cmd(BEGIN(POINTS));
|
131
|
154
|
for (uint8_t x = 0; x < cols; x++) {
|
132
|
155
|
if (ISVAL(x,y)) {
|
133
|
156
|
if (opts & USE_COLORS) {
|
134
|
157
|
const float val_dev = VALUE(x, y) - val_mean;
|
135
|
|
- const uint8_t neg_byte = sq(val_dev) / sq(val_dev < 0 ? val_min : val_max) * 0xFF;
|
|
158
|
+ const uint8_t neg_byte = sq(val_dev) / (val_dev < 0 ? sq_min : sq_max) * 0xFF;
|
136
|
159
|
const uint8_t pos_byte = 255 - neg_byte;
|
137
|
160
|
cmd.cmd(COLOR_RGB(pos_byte, pos_byte, 0xFF));
|
138
|
161
|
}
|
|
@@ -164,11 +187,12 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
|
164
|
187
|
if (opts & USE_HIGHLIGHT) {
|
165
|
188
|
const uint8_t tag = screen_data.BedMeshScreen.highlightedTag;
|
166
|
189
|
uint8_t x, y;
|
167
|
|
- tagToPoint(tag, x, y);
|
168
|
|
- cmd.cmd(COLOR_A(128))
|
169
|
|
- .cmd(POINT_SIZE(basePointSize * 6))
|
170
|
|
- .cmd(BEGIN(POINTS))
|
171
|
|
- .tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
|
|
190
|
+ if (tagToPoint(tag, x, y)) {
|
|
191
|
+ cmd.cmd(COLOR_A(128))
|
|
192
|
+ .cmd(POINT_SIZE(basePointSize * 6))
|
|
193
|
+ .cmd(BEGIN(POINTS))
|
|
194
|
+ .tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
|
|
195
|
+ }
|
172
|
196
|
}
|
173
|
197
|
cmd.cmd(END());
|
174
|
198
|
cmd.cmd(RESTORE_CONTEXT());
|
|
@@ -178,9 +202,11 @@ uint8_t BedMeshScreen::pointToTag(uint8_t x, uint8_t y) {
|
178
|
202
|
return y * (GRID_MAX_POINTS_X) + x + 10;
|
179
|
203
|
}
|
180
|
204
|
|
181
|
|
-void BedMeshScreen::tagToPoint(uint8_t tag, uint8_t &x, uint8_t &y) {
|
|
205
|
+bool BedMeshScreen::tagToPoint(uint8_t tag, uint8_t &x, uint8_t &y) {
|
|
206
|
+ if (tag < 10) return false;
|
182
|
207
|
x = (tag - 10) % (GRID_MAX_POINTS_X);
|
183
|
208
|
y = (tag - 10) / (GRID_MAX_POINTS_X);
|
|
209
|
+ return true;
|
184
|
210
|
}
|
185
|
211
|
|
186
|
212
|
void BedMeshScreen::onEntry() {
|
|
@@ -217,6 +243,9 @@ void BedMeshScreen::drawHighlightedPointValue() {
|
217
|
243
|
}
|
218
|
244
|
|
219
|
245
|
void BedMeshScreen::onRedraw(draw_mode_t what) {
|
|
246
|
+ #define _INSET_POS(x,y,w,h) x + min(w,h)/10, y + min(w,h)/10, w - min(w,h)/5, h - min(w,h)/5
|
|
247
|
+ #define INSET_POS(pos) _INSET_POS(pos)
|
|
248
|
+
|
220
|
249
|
if (what & BACKGROUND) {
|
221
|
250
|
CommandProcessor cmd;
|
222
|
251
|
cmd.cmd(CLEAR_COLOR_RGB(bg_color))
|
|
@@ -224,16 +253,20 @@ void BedMeshScreen::onRedraw(draw_mode_t what) {
|
224
|
253
|
|
225
|
254
|
// Draw the shadow and tags
|
226
|
255
|
cmd.cmd(COLOR_RGB(0x444444));
|
227
|
|
- BedMeshScreen::drawMesh(MESH_POS, nullptr, USE_POINTS | USE_TAGS);
|
|
256
|
+ BedMeshScreen::drawMesh(INSET_POS(MESH_POS), nullptr, USE_POINTS | USE_TAGS);
|
228
|
257
|
cmd.cmd(COLOR_RGB(bg_text_enabled));
|
229
|
258
|
}
|
230
|
259
|
|
231
|
260
|
if (what & FOREGROUND) {
|
|
261
|
+ constexpr float autoscale_max_amplitude = 0.03;
|
232
|
262
|
const bool levelingFinished = screen_data.BedMeshScreen.count >= GRID_MAX_POINTS;
|
|
263
|
+ const float levelingProgress = sq(float(screen_data.BedMeshScreen.count) / GRID_MAX_POINTS);
|
233
|
264
|
if (levelingFinished) drawHighlightedPointValue();
|
234
|
265
|
|
235
|
|
- BedMeshScreen::drawMesh(MESH_POS, ExtUI::getMeshArray(),
|
236
|
|
- USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (levelingFinished ? USE_COLORS : 0));
|
|
266
|
+ BedMeshScreen::drawMesh(INSET_POS(MESH_POS), ExtUI::getMeshArray(),
|
|
267
|
+ USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (levelingFinished ? USE_COLORS : 0),
|
|
268
|
+ autoscale_max_amplitude * levelingProgress
|
|
269
|
+ );
|
237
|
270
|
}
|
238
|
271
|
}
|
239
|
272
|
|
|
@@ -243,7 +276,7 @@ bool BedMeshScreen::onTouchStart(uint8_t tag) {
|
243
|
276
|
}
|
244
|
277
|
|
245
|
278
|
bool BedMeshScreen::onTouchEnd(uint8_t tag) {
|
246
|
|
- switch(tag) {
|
|
279
|
+ switch (tag) {
|
247
|
280
|
case 1:
|
248
|
281
|
GOTO_PREVIOUS();
|
249
|
282
|
return true;
|