|
|
@ -43,41 +43,33 @@ using namespace ExtUI; |
|
|
|
#define GRID_COLS 5 |
|
|
|
#define GRID_ROWS 5 |
|
|
|
|
|
|
|
#define MESH_POS BTN_POS(2,1), BTN_SIZE(4,5) |
|
|
|
#define Z_LABEL_POS BTN_POS(1,3), BTN_SIZE(1,1) |
|
|
|
#define Z_VALUE_POS BTN_POS(1,4), BTN_SIZE(2,1) |
|
|
|
#define WAIT_POS BTN_POS(1,3), BTN_SIZE(2,2) |
|
|
|
#define BACK_POS BTN_POS(1,5), BTN_SIZE(2,1) |
|
|
|
#define MESH_POS BTN_POS(1,1), BTN_SIZE(3,5) |
|
|
|
#define Z_LABEL_POS BTN_POS(4,2), BTN_SIZE(2,1) |
|
|
|
#define Z_VALUE_POS BTN_POS(4,3), BTN_SIZE(2,1) |
|
|
|
#define WAIT_POS BTN_POS(4,2), BTN_SIZE(2,2) |
|
|
|
#define BACK_POS BTN_POS(4,5), BTN_SIZE(2,1) |
|
|
|
#endif |
|
|
|
|
|
|
|
void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::bed_mesh_t data, uint8_t opts) { |
|
|
|
CommandProcessor cmd; |
|
|
|
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) { |
|
|
|
constexpr uint8_t rows = GRID_MAX_POINTS_Y; |
|
|
|
constexpr uint8_t cols = GRID_MAX_POINTS_X; |
|
|
|
|
|
|
|
#define TRANSFORM_2(X,Y,Z) (X), (Y) // No transform
|
|
|
|
#define TRANSFORM_1(X,Y,Z) TRANSFORM_2((X) + (Y) * slant, (Y) - (Z), 0) // Perspective
|
|
|
|
#define TRANSFORM(X,Y,Z) TRANSFORM_1(float(X)/(cols-1) - 0.5, float(Y)/(rows-1) - 0.5, (Z)) // Normalize
|
|
|
|
#define VALUE(X,Y) (data ? data[X][Y] : 0) |
|
|
|
#define ISVAL(X,Y) (data ? !isnan(VALUE(X,Y)) : true) |
|
|
|
#define HEIGHT(X,Y) (ISVAL(X,Y) ? (VALUE(X,Y) - val_min) * scale_z : 0) |
|
|
|
|
|
|
|
constexpr uint8_t rows = GRID_MAX_POINTS_Y; |
|
|
|
constexpr uint8_t cols = GRID_MAX_POINTS_X; |
|
|
|
const float slant = 0.5; |
|
|
|
const float bounds_min[] = {TRANSFORM(0 ,0 ,0)}; |
|
|
|
const float bounds_max[] = {TRANSFORM(cols,rows,0)}; |
|
|
|
const float scale_x = float(w)/(bounds_max[0] - bounds_min[0]); |
|
|
|
const float scale_y = float(h)/(bounds_max[1] - bounds_min[1]); |
|
|
|
const float center_x = x + w/2; |
|
|
|
const float center_y = y + h/2; |
|
|
|
// Compute the mean, min and max for the points
|
|
|
|
|
|
|
|
float val_mean = 0; |
|
|
|
float val_max = -INFINITY; |
|
|
|
float val_min = INFINITY; |
|
|
|
uint8_t val_cnt = 0; |
|
|
|
|
|
|
|
if (opts & USE_AUTOSCALE) { |
|
|
|
// Compute the mean
|
|
|
|
if (data && (opts & USE_AUTOSCALE)) { |
|
|
|
for (uint8_t y = 0; y < rows; y++) { |
|
|
|
for (uint8_t x = 0; x < cols; x++) { |
|
|
|
const float val = data[x][y]; |
|
|
|
if (!isnan(val)) { |
|
|
|
if (ISVAL(x,y)) { |
|
|
|
const float val = VALUE(x,y); |
|
|
|
val_mean += val; |
|
|
|
val_max = max(val_max, val); |
|
|
|
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:: |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (val_cnt) { |
|
|
|
val_mean /= val_cnt; |
|
|
|
val_min -= val_mean; |
|
|
|
val_max -= val_mean; |
|
|
|
} else { |
|
|
|
val_mean = 0; |
|
|
|
val_min = 0; |
|
|
|
val_max = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
if (val_cnt) { |
|
|
|
val_mean /= val_cnt; |
|
|
|
} else { |
|
|
|
val_mean = 0; |
|
|
|
val_min = 0; |
|
|
|
val_max = 0; |
|
|
|
} |
|
|
|
|
|
|
|
const float scale_z = ((val_max == val_min) ? 1 : 1/(val_max - val_min)) * autoscale_max; |
|
|
|
|
|
|
|
// These equations determine the appearance of the grid on the screen.
|
|
|
|
|
|
|
|
#define TRANSFORM_5(X,Y,Z) (X), (Y) // No transform
|
|
|
|
#define TRANSFORM_4(X,Y,Z) TRANSFORM_5((X)/(Z),(Y)/-(Z), 0) // Perspective
|
|
|
|
#define TRANSFORM_3(X,Y,Z) TRANSFORM_4((X), (Z), (Y)) // Swap Z and Y
|
|
|
|
#define TRANSFORM_2(X,Y,Z) TRANSFORM_3((X), (Y) + 2.5, (Z) - 1) // Translate
|
|
|
|
#define TRANSFORM(X,Y,Z) TRANSFORM_2(float(X)/(cols-1) - 0.5, float(Y)/(rows-1) - 0.5, (Z)) // Normalize
|
|
|
|
|
|
|
|
// Compute the bounding box for the grid prior to scaling. Do this at compile-time by
|
|
|
|
// transforming the four corner points via the transformation equations and finding
|
|
|
|
// the min and max for each axis.
|
|
|
|
|
|
|
|
const float scale_z = ((val_max == val_min) ? 1 : 1/(val_max - val_min)) * 0.1; |
|
|
|
constexpr float bounds[][3] = {{TRANSFORM(0 , 0 , 0)}, |
|
|
|
{TRANSFORM(cols-1, 0 , 0)}, |
|
|
|
{TRANSFORM(0 , rows-1, 0)}, |
|
|
|
{TRANSFORM(cols-1, rows-1, 0)}}; |
|
|
|
#define APPLY(FUNC, AXIS) FUNC(FUNC(bounds[0][AXIS], bounds[1][AXIS]), FUNC(bounds[2][AXIS], bounds[3][AXIS])) |
|
|
|
constexpr float grid_x = APPLY(min,0); |
|
|
|
constexpr float grid_y = APPLY(min,1); |
|
|
|
constexpr float grid_w = APPLY(max,0) - grid_x; |
|
|
|
constexpr float grid_h = APPLY(max,1) - grid_y; |
|
|
|
constexpr float grid_cx = grid_x + grid_w/2; |
|
|
|
constexpr float grid_cy = grid_y + grid_h/2; |
|
|
|
|
|
|
|
#undef TRANSFORM_2 |
|
|
|
#define TRANSFORM_2(X,Y,Z) center_x + (X) * scale_x, center_y + (Y) * scale_y // Scale and position
|
|
|
|
#define VALUE(X,Y) ((data && ISVAL(X,Y)) ? data[X][Y] : 0) |
|
|
|
#define ISVAL(X,Y) (data ? !isnan(data[X][Y]) : true) |
|
|
|
#define HEIGHT(X,Y) (VALUE(X,Y) * scale_z) |
|
|
|
// Figure out scale and offset such that the grid fits within the rectangle given by (x,y,w,h)
|
|
|
|
|
|
|
|
uint16_t basePointSize = min(scale_x,scale_y) / max(cols,rows); |
|
|
|
const float scale_x = float(w)/grid_w; |
|
|
|
const float scale_y = float(h)/grid_h; |
|
|
|
const float center_x = x + w/2; |
|
|
|
const float center_y = y + h/2; |
|
|
|
|
|
|
|
#undef TRANSFORM_5 |
|
|
|
#define TRANSFORM_5(X,Y,Z) center_x + (X - grid_cx) * scale_x, center_y + (Y - grid_cy) * scale_y // Fit to bounds
|
|
|
|
|
|
|
|
// Draw the grid
|
|
|
|
|
|
|
|
const uint16_t basePointSize = min(w,h) / max(cols,rows); |
|
|
|
|
|
|
|
CommandProcessor cmd; |
|
|
|
cmd.cmd(SAVE_CONTEXT()) |
|
|
|
.cmd(VERTEX_FORMAT(0)) |
|
|
|
.cmd(TAG_MASK(false)) |
|
|
@ -126,13 +147,15 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI:: |
|
|
|
} |
|
|
|
|
|
|
|
if (opts & USE_POINTS) { |
|
|
|
const float sq_min = sq(val_min - val_mean); |
|
|
|
const float sq_max = sq(val_max - val_mean); |
|
|
|
cmd.cmd(POINT_SIZE(basePointSize * 2)); |
|
|
|
cmd.cmd(BEGIN(POINTS)); |
|
|
|
for (uint8_t x = 0; x < cols; x++) { |
|
|
|
if (ISVAL(x,y)) { |
|
|
|
if (opts & USE_COLORS) { |
|
|
|
const float val_dev = VALUE(x, y) - val_mean; |
|
|
|
const uint8_t neg_byte = sq(val_dev) / sq(val_dev < 0 ? val_min : val_max) * 0xFF; |
|
|
|
const uint8_t neg_byte = sq(val_dev) / (val_dev < 0 ? sq_min : sq_max) * 0xFF; |
|
|
|
const uint8_t pos_byte = 255 - neg_byte; |
|
|
|
cmd.cmd(COLOR_RGB(pos_byte, pos_byte, 0xFF)); |
|
|
|
} |
|
|
@ -164,11 +187,12 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI:: |
|
|
|
if (opts & USE_HIGHLIGHT) { |
|
|
|
const uint8_t tag = screen_data.BedMeshScreen.highlightedTag; |
|
|
|
uint8_t x, y; |
|
|
|
tagToPoint(tag, x, y); |
|
|
|
cmd.cmd(COLOR_A(128)) |
|
|
|
.cmd(POINT_SIZE(basePointSize * 6)) |
|
|
|
.cmd(BEGIN(POINTS)) |
|
|
|
.tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y)))); |
|
|
|
if (tagToPoint(tag, x, y)) { |
|
|
|
cmd.cmd(COLOR_A(128)) |
|
|
|
.cmd(POINT_SIZE(basePointSize * 6)) |
|
|
|
.cmd(BEGIN(POINTS)) |
|
|
|
.tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y)))); |
|
|
|
} |
|
|
|
} |
|
|
|
cmd.cmd(END()); |
|
|
|
cmd.cmd(RESTORE_CONTEXT()); |
|
|
@ -178,9 +202,11 @@ uint8_t BedMeshScreen::pointToTag(uint8_t x, uint8_t y) { |
|
|
|
return y * (GRID_MAX_POINTS_X) + x + 10; |
|
|
|
} |
|
|
|
|
|
|
|
void BedMeshScreen::tagToPoint(uint8_t tag, uint8_t &x, uint8_t &y) { |
|
|
|
bool BedMeshScreen::tagToPoint(uint8_t tag, uint8_t &x, uint8_t &y) { |
|
|
|
if (tag < 10) return false; |
|
|
|
x = (tag - 10) % (GRID_MAX_POINTS_X); |
|
|
|
y = (tag - 10) / (GRID_MAX_POINTS_X); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
void BedMeshScreen::onEntry() { |
|
|
@ -217,6 +243,9 @@ void BedMeshScreen::drawHighlightedPointValue() { |
|
|
|
} |
|
|
|
|
|
|
|
void BedMeshScreen::onRedraw(draw_mode_t what) { |
|
|
|
#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 |
|
|
|
#define INSET_POS(pos) _INSET_POS(pos) |
|
|
|
|
|
|
|
if (what & BACKGROUND) { |
|
|
|
CommandProcessor cmd; |
|
|
|
cmd.cmd(CLEAR_COLOR_RGB(bg_color)) |
|
|
@ -224,16 +253,20 @@ void BedMeshScreen::onRedraw(draw_mode_t what) { |
|
|
|
|
|
|
|
// Draw the shadow and tags
|
|
|
|
cmd.cmd(COLOR_RGB(0x444444)); |
|
|
|
BedMeshScreen::drawMesh(MESH_POS, nullptr, USE_POINTS | USE_TAGS); |
|
|
|
BedMeshScreen::drawMesh(INSET_POS(MESH_POS), nullptr, USE_POINTS | USE_TAGS); |
|
|
|
cmd.cmd(COLOR_RGB(bg_text_enabled)); |
|
|
|
} |
|
|
|
|
|
|
|
if (what & FOREGROUND) { |
|
|
|
constexpr float autoscale_max_amplitude = 0.03; |
|
|
|
const bool levelingFinished = screen_data.BedMeshScreen.count >= GRID_MAX_POINTS; |
|
|
|
const float levelingProgress = sq(float(screen_data.BedMeshScreen.count) / GRID_MAX_POINTS); |
|
|
|
if (levelingFinished) drawHighlightedPointValue(); |
|
|
|
|
|
|
|
BedMeshScreen::drawMesh(MESH_POS, ExtUI::getMeshArray(), |
|
|
|
USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (levelingFinished ? USE_COLORS : 0)); |
|
|
|
BedMeshScreen::drawMesh(INSET_POS(MESH_POS), ExtUI::getMeshArray(), |
|
|
|
USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (levelingFinished ? USE_COLORS : 0), |
|
|
|
autoscale_max_amplitude * levelingProgress |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -243,7 +276,7 @@ bool BedMeshScreen::onTouchStart(uint8_t tag) { |
|
|
|
} |
|
|
|
|
|
|
|
bool BedMeshScreen::onTouchEnd(uint8_t tag) { |
|
|
|
switch(tag) { |
|
|
|
switch (tag) { |
|
|
|
case 1: |
|
|
|
GOTO_PREVIOUS(); |
|
|
|
return true; |
|
|
|