|
@@ -0,0 +1,268 @@
|
|
1
|
+/***********************
|
|
2
|
+ * bed_mesh_screen.cpp *
|
|
3
|
+ ***********************/
|
|
4
|
+
|
|
5
|
+/****************************************************************************
|
|
6
|
+ * Written By Marcio Teixeira 2020 *
|
|
7
|
+ * *
|
|
8
|
+ * This program is free software: you can redistribute it and/or modify *
|
|
9
|
+ * it under the terms of the GNU General Public License as published by *
|
|
10
|
+ * the Free Software Foundation, either version 3 of the License, or *
|
|
11
|
+ * (at your option) any later version. *
|
|
12
|
+ * *
|
|
13
|
+ * This program is distributed in the hope that it will be useful, *
|
|
14
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
15
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
16
|
+ * GNU General Public License for more details. *
|
|
17
|
+ * *
|
|
18
|
+ * To view a copy of the GNU General Public License, go to the following *
|
|
19
|
+ * location: <http://www.gnu.org/licenses/>. *
|
|
20
|
+ ****************************************************************************/
|
|
21
|
+
|
|
22
|
+#include "../config.h"
|
|
23
|
+
|
|
24
|
+#if ENABLED(TOUCH_UI_FTDI_EVE) && HAS_MESH
|
|
25
|
+
|
|
26
|
+#include "screens.h"
|
|
27
|
+#include "screen_data.h"
|
|
28
|
+
|
|
29
|
+using namespace FTDI;
|
|
30
|
+using namespace Theme;
|
|
31
|
+using namespace ExtUI;
|
|
32
|
+
|
|
33
|
+#ifdef TOUCH_UI_PORTRAIT
|
|
34
|
+ #define GRID_COLS 2
|
|
35
|
+ #define GRID_ROWS 10
|
|
36
|
+
|
|
37
|
+ #define MESH_POS BTN_POS(1, 2), BTN_SIZE(2,5)
|
|
38
|
+ #define Z_LABEL_POS BTN_POS(1, 8), BTN_SIZE(1,1)
|
|
39
|
+ #define Z_VALUE_POS BTN_POS(2, 8), BTN_SIZE(1,1)
|
|
40
|
+ #define WAIT_POS BTN_POS(1, 8), BTN_SIZE(2,1)
|
|
41
|
+ #define BACK_POS BTN_POS(1,10), BTN_SIZE(2,1)
|
|
42
|
+#else
|
|
43
|
+ #define GRID_COLS 5
|
|
44
|
+ #define GRID_ROWS 5
|
|
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)
|
|
51
|
+#endif
|
|
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;
|
|
55
|
+
|
|
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
|
|
59
|
+
|
|
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;
|
|
69
|
+
|
|
70
|
+ float val_mean = 0;
|
|
71
|
+ float val_max = -INFINITY;
|
|
72
|
+ float val_min = INFINITY;
|
|
73
|
+ uint8_t val_cnt = 0;
|
|
74
|
+
|
|
75
|
+ if (opts & USE_AUTOSCALE) {
|
|
76
|
+ // Compute the mean
|
|
77
|
+ for (uint8_t y = 0; y < rows; y++) {
|
|
78
|
+ for (uint8_t x = 0; x < cols; x++) {
|
|
79
|
+ const float val = data[x][y];
|
|
80
|
+ if (!isnan(val)) {
|
|
81
|
+ val_mean += val;
|
|
82
|
+ val_max = max(val_max, val);
|
|
83
|
+ val_min = min(val_min, val);
|
|
84
|
+ val_cnt++;
|
|
85
|
+ }
|
|
86
|
+ }
|
|
87
|
+ }
|
|
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
|
+ }
|
|
98
|
+
|
|
99
|
+ const float scale_z = ((val_max == val_min) ? 1 : 1/(val_max - val_min)) * 0.1;
|
|
100
|
+
|
|
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)
|
|
106
|
+
|
|
107
|
+ uint16_t basePointSize = min(scale_x,scale_y) / max(cols,rows);
|
|
108
|
+
|
|
109
|
+ cmd.cmd(SAVE_CONTEXT())
|
|
110
|
+ .cmd(VERTEX_FORMAT(0))
|
|
111
|
+ .cmd(TAG_MASK(false))
|
|
112
|
+ .cmd(SAVE_CONTEXT());
|
|
113
|
+
|
|
114
|
+ for (uint8_t y = 0; y < rows; y++) {
|
|
115
|
+ for (uint8_t x = 0; x < cols; x++) {
|
|
116
|
+ if (ISVAL(x,y)) {
|
|
117
|
+ const bool hasLeftSegment = x < cols - 1 && ISVAL(x+1,y);
|
|
118
|
+ const bool hasRightSegment = y < rows - 1 && ISVAL(x,y+1);
|
|
119
|
+ if (hasLeftSegment || hasRightSegment) {
|
|
120
|
+ cmd.cmd(BEGIN(LINE_STRIP));
|
|
121
|
+ if (hasLeftSegment) cmd.cmd(VERTEX2F(TRANSFORM(x + 1, y , HEIGHT(x + 1, y ))));
|
|
122
|
+ cmd.cmd( VERTEX2F(TRANSFORM(x , y , HEIGHT(x , y ))));
|
|
123
|
+ if (hasRightSegment) cmd.cmd(VERTEX2F(TRANSFORM(x , y + 1, HEIGHT(x , y + 1))));
|
|
124
|
+ }
|
|
125
|
+ }
|
|
126
|
+ }
|
|
127
|
+
|
|
128
|
+ if (opts & USE_POINTS) {
|
|
129
|
+ cmd.cmd(POINT_SIZE(basePointSize * 2));
|
|
130
|
+ cmd.cmd(BEGIN(POINTS));
|
|
131
|
+ for (uint8_t x = 0; x < cols; x++) {
|
|
132
|
+ if (ISVAL(x,y)) {
|
|
133
|
+ if (opts & USE_COLORS) {
|
|
134
|
+ 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;
|
|
136
|
+ const uint8_t pos_byte = 255 - neg_byte;
|
|
137
|
+ cmd.cmd(COLOR_RGB(pos_byte, pos_byte, 0xFF));
|
|
138
|
+ }
|
|
139
|
+ cmd.cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
|
|
140
|
+ }
|
|
141
|
+ }
|
|
142
|
+ if (opts & USE_COLORS) {
|
|
143
|
+ cmd.cmd(RESTORE_CONTEXT())
|
|
144
|
+ .cmd(SAVE_CONTEXT());
|
|
145
|
+ }
|
|
146
|
+ }
|
|
147
|
+ }
|
|
148
|
+ cmd.cmd(RESTORE_CONTEXT())
|
|
149
|
+ .cmd(TAG_MASK(true));
|
|
150
|
+
|
|
151
|
+ if (opts & USE_TAGS) {
|
|
152
|
+ cmd.cmd(COLOR_MASK(false, false, false, false))
|
|
153
|
+ .cmd(POINT_SIZE(basePointSize * 10))
|
|
154
|
+ .cmd(BEGIN(POINTS));
|
|
155
|
+ for (uint8_t y = 0; y < rows; y++) {
|
|
156
|
+ for (uint8_t x = 0; x < cols; x++) {
|
|
157
|
+ const uint8_t tag = pointToTag(x, y);
|
|
158
|
+ cmd.tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
|
|
159
|
+ }
|
|
160
|
+ }
|
|
161
|
+ cmd.cmd(COLOR_MASK(true, true, true, true));
|
|
162
|
+ }
|
|
163
|
+
|
|
164
|
+ if (opts & USE_HIGHLIGHT) {
|
|
165
|
+ const uint8_t tag = screen_data.BedMeshScreen.highlightedTag;
|
|
166
|
+ 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))));
|
|
172
|
+ }
|
|
173
|
+ cmd.cmd(END());
|
|
174
|
+ cmd.cmd(RESTORE_CONTEXT());
|
|
175
|
+}
|
|
176
|
+
|
|
177
|
+uint8_t BedMeshScreen::pointToTag(uint8_t x, uint8_t y) {
|
|
178
|
+ return y * (GRID_MAX_POINTS_X) + x + 10;
|
|
179
|
+}
|
|
180
|
+
|
|
181
|
+void BedMeshScreen::tagToPoint(uint8_t tag, uint8_t &x, uint8_t &y) {
|
|
182
|
+ x = (tag - 10) % (GRID_MAX_POINTS_X);
|
|
183
|
+ y = (tag - 10) / (GRID_MAX_POINTS_X);
|
|
184
|
+}
|
|
185
|
+
|
|
186
|
+void BedMeshScreen::onEntry() {
|
|
187
|
+ screen_data.BedMeshScreen.highlightedTag = 0;
|
|
188
|
+ screen_data.BedMeshScreen.count = 0;
|
|
189
|
+ BaseScreen::onEntry();
|
|
190
|
+}
|
|
191
|
+
|
|
192
|
+float BedMeshScreen::getHightlightedValue() {
|
|
193
|
+ if (screen_data.BedMeshScreen.highlightedTag) {
|
|
194
|
+ xy_uint8_t pt;
|
|
195
|
+ tagToPoint(screen_data.BedMeshScreen.highlightedTag, pt.x, pt.y);
|
|
196
|
+ return ExtUI::getMeshPoint(pt);
|
|
197
|
+ }
|
|
198
|
+ return NAN;
|
|
199
|
+}
|
|
200
|
+
|
|
201
|
+void BedMeshScreen::drawHighlightedPointValue() {
|
|
202
|
+ char str[16];
|
|
203
|
+ const float val = getHightlightedValue();
|
|
204
|
+ const bool isGood = !isnan(val);
|
|
205
|
+ if (isGood)
|
|
206
|
+ dtostrf(val, 5, 3, str);
|
|
207
|
+ else
|
|
208
|
+ strcpy_P(str, PSTR("-"));
|
|
209
|
+
|
|
210
|
+ CommandProcessor cmd;
|
|
211
|
+ cmd.font(Theme::font_medium)
|
|
212
|
+ .text(Z_LABEL_POS, GET_TEXT_F(MSG_MESH_EDIT_Z))
|
|
213
|
+ .text(Z_VALUE_POS, str)
|
|
214
|
+ .colors(action_btn)
|
|
215
|
+ .tag(1).button( BACK_POS, GET_TEXT_F(MSG_BACK))
|
|
216
|
+ .tag(0);
|
|
217
|
+}
|
|
218
|
+
|
|
219
|
+void BedMeshScreen::onRedraw(draw_mode_t what) {
|
|
220
|
+ if (what & BACKGROUND) {
|
|
221
|
+ CommandProcessor cmd;
|
|
222
|
+ cmd.cmd(CLEAR_COLOR_RGB(bg_color))
|
|
223
|
+ .cmd(CLEAR(true,true,true));
|
|
224
|
+
|
|
225
|
+ // Draw the shadow and tags
|
|
226
|
+ cmd.cmd(COLOR_RGB(0x444444));
|
|
227
|
+ BedMeshScreen::drawMesh(MESH_POS, nullptr, USE_POINTS | USE_TAGS);
|
|
228
|
+ cmd.cmd(COLOR_RGB(bg_text_enabled));
|
|
229
|
+ }
|
|
230
|
+
|
|
231
|
+ if (what & FOREGROUND) {
|
|
232
|
+ const bool levelingFinished = screen_data.BedMeshScreen.count >= GRID_MAX_POINTS;
|
|
233
|
+ if (levelingFinished) drawHighlightedPointValue();
|
|
234
|
+
|
|
235
|
+ BedMeshScreen::drawMesh(MESH_POS, ExtUI::getMeshArray(),
|
|
236
|
+ USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (levelingFinished ? USE_COLORS : 0));
|
|
237
|
+ }
|
|
238
|
+}
|
|
239
|
+
|
|
240
|
+bool BedMeshScreen::onTouchStart(uint8_t tag) {
|
|
241
|
+ screen_data.BedMeshScreen.highlightedTag = tag;
|
|
242
|
+ return true;
|
|
243
|
+}
|
|
244
|
+
|
|
245
|
+bool BedMeshScreen::onTouchEnd(uint8_t tag) {
|
|
246
|
+ switch(tag) {
|
|
247
|
+ case 1:
|
|
248
|
+ GOTO_PREVIOUS();
|
|
249
|
+ return true;
|
|
250
|
+ default:
|
|
251
|
+ return false;
|
|
252
|
+ }
|
|
253
|
+}
|
|
254
|
+
|
|
255
|
+void BedMeshScreen::onMeshUpdate(const int8_t, const int8_t, const float) {
|
|
256
|
+ if (AT_SCREEN(BedMeshScreen))
|
|
257
|
+ onRefresh();
|
|
258
|
+}
|
|
259
|
+
|
|
260
|
+void BedMeshScreen::onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) {
|
|
261
|
+ if (state == ExtUI::PROBE_FINISH) {
|
|
262
|
+ screen_data.BedMeshScreen.highlightedTag = pointToTag(x, y);
|
|
263
|
+ screen_data.BedMeshScreen.count++;
|
|
264
|
+ }
|
|
265
|
+ BedMeshScreen::onMeshUpdate(x, y, 0);
|
|
266
|
+}
|
|
267
|
+
|
|
268
|
+#endif // TOUCH_UI_FTDI_EVE
|