Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProUI feature utilization / Misc. Fixes #26434

Merged
Merged
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
73693bb
Fix typo in stm32f4.ini
classicrocker883 Nov 17, 2023
f60abea
Add ProUI feature options
classicrocker883 Nov 17, 2023
3d8088b
fix `SDSORT_GCODE false` warning
classicrocker883 Nov 17, 2023
3a46132
include #ifdef in MarlinConfigPre.h
classicrocker883 Nov 19, 2023
9fe4190
enabled
thinkyhead Nov 20, 2023
d715ff0
Merge branch 'bugfix-2.1.x' into pr/26434
thinkyhead Nov 20, 2023
cc17151
tweak errors
thinkyhead Nov 20, 2023
28b8571
clarify
thinkyhead Nov 20, 2023
7f2c141
Merge branch 'bugfix-2.1.x' into pr/26434
thinkyhead Nov 20, 2023
b707878
Fix warning: narrowing conversion
classicrocker883 Nov 20, 2023
2920b3d
fix file wrappers
thinkyhead Nov 21, 2023
3f2df7e
Merge branch 'bugfix-2.1.x-Nov3' of https://github.com/classicrocker8…
classicrocker883 Nov 21, 2023
8a5faf1
Fix warning: narrowing conversion in JyersUI
classicrocker883 Nov 21, 2023
031eecc
fixes for dwin.cpp
classicrocker883 Nov 21, 2023
6358ade
indent
classicrocker883 Nov 21, 2023
10bea42
add brief and param to Menu Line Spacing
classicrocker883 Nov 21, 2023
ea9c430
update dwin_defines.h
classicrocker883 Nov 21, 2023
e109c3c
undo extraneous changes
thinkyhead Nov 21, 2023
758ebab
rename UBL_Z_OFFSET_MIN/MAX to Z_OFFSET_MIN/MAX
classicrocker883 Nov 21, 2023
6dd992b
Merge branch 'bugfix-2.1.x-Nov3' of https://github.com/classicrocker8…
classicrocker883 Nov 21, 2023
1b41c4b
forgot indent bedlevel_tools.cpp
classicrocker883 Nov 21, 2023
41081cc
unindent
thinkyhead Nov 21, 2023
86819bb
move to dwin_defines
thinkyhead Nov 21, 2023
43ff659
rgb type, some case fix
thinkyhead Nov 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
unindent
thinkyhead committed Nov 21, 2023
commit 41081cc75708e71394bcae53b923bb0e99684016
398 changes: 199 additions & 199 deletions Marlin/src/lcd/e3v2/proui/bedlevel_tools.cpp
Original file line number Diff line number Diff line change
@@ -48,234 +48,234 @@

#if ALL(DWIN_LCD_PROUI, HAS_LEVELING)

#include "../../marlinui.h"
#include "../../../core/types.h"
#include "../../../feature/bedlevel/bedlevel.h"
#include "../../../module/probe.h"
#include "../../../gcode/gcode.h"
#include "../../../module/planner.h"
#include "../../../gcode/queue.h"
#include "../../../libs/least_squares_fit.h"
#include "../../../libs/vector_3.h"

#include "dwin.h"
#include "dwinui.h"
#include "dwin_popup.h"
#include "bedlevel_tools.h"

BedLevelTools bedLevelTools;

#if ENABLED(USE_GRID_MESHVIEWER)
bool BedLevelTools::viewer_print_value = false;
#endif
bool BedLevelTools::goto_mesh_value = false;
uint8_t BedLevelTools::mesh_x = 0;
uint8_t BedLevelTools::mesh_y = 0;
uint8_t BedLevelTools::tilt_grid = 1;

bool drawing_mesh = false;

#if ENABLED(AUTO_BED_LEVELING_UBL)

void BedLevelTools::manualValueUpdate(const uint8_t mesh_x, const uint8_t mesh_y, bool undefined/*=false*/) {
MString<MAX_CMD_SIZE> cmd;
cmd.set(F("M421 I"), mesh_x, 'J', mesh_y, 'Z', p_float_t(current_position.z, 3));
if (undefined) cmd += F(" N");
gcode.process_subcommands_now(cmd);
planner.synchronize();
}

bool BedLevelTools::createPlaneFromMesh() {
struct linear_fit_data lsf_results;
incremental_LSF_reset(&lsf_results);
GRID_LOOP(x, y) {
if (!isnan(bedlevel.z_values[x][y])) {
xy_pos_t rpos = { bedlevel.get_mesh_x(x), bedlevel.get_mesh_y(y) };
incremental_LSF(&lsf_results, rpos, bedlevel.z_values[x][y]);
}
}
#include "../../marlinui.h"
#include "../../../core/types.h"
#include "../../../feature/bedlevel/bedlevel.h"
#include "../../../module/probe.h"
#include "../../../gcode/gcode.h"
#include "../../../module/planner.h"
#include "../../../gcode/queue.h"
#include "../../../libs/least_squares_fit.h"
#include "../../../libs/vector_3.h"

#include "dwin.h"
#include "dwinui.h"
#include "dwin_popup.h"
#include "bedlevel_tools.h"

BedLevelTools bedLevelTools;

#if ENABLED(USE_GRID_MESHVIEWER)
bool BedLevelTools::viewer_print_value = false;
#endif
bool BedLevelTools::goto_mesh_value = false;
uint8_t BedLevelTools::mesh_x = 0;
uint8_t BedLevelTools::mesh_y = 0;
uint8_t BedLevelTools::tilt_grid = 1;

bool drawing_mesh = false;

#if ENABLED(AUTO_BED_LEVELING_UBL)

void BedLevelTools::manualValueUpdate(const uint8_t mesh_x, const uint8_t mesh_y, bool undefined/*=false*/) {
MString<MAX_CMD_SIZE> cmd;
cmd.set(F("M421 I"), mesh_x, 'J', mesh_y, 'Z', p_float_t(current_position.z, 3));
if (undefined) cmd += F(" N");
gcode.process_subcommands_now(cmd);
planner.synchronize();
}

if (finish_incremental_LSF(&lsf_results)) {
SERIAL_ECHOPGM("Could not complete LSF!");
return true;
bool BedLevelTools::createPlaneFromMesh() {
struct linear_fit_data lsf_results;
incremental_LSF_reset(&lsf_results);
GRID_LOOP(x, y) {
if (!isnan(bedlevel.z_values[x][y])) {
xy_pos_t rpos = { bedlevel.get_mesh_x(x), bedlevel.get_mesh_y(y) };
incremental_LSF(&lsf_results, rpos, bedlevel.z_values[x][y]);
}
}

bedlevel.set_all_mesh_points_to_value(0);

matrix_3x3 rotation = matrix_3x3::create_look_at(vector_3(lsf_results.A, lsf_results.B, 1));
GRID_LOOP(i, j) {
float mx = bedlevel.get_mesh_x(i), my = bedlevel.get_mesh_y(j), mz = bedlevel.z_values[i][j];

if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLN(F("before rotation = ["), p_float_t(mx, 7), AS_CHAR(','), p_float_t(my, 7), AS_CHAR(','), p_float_t(mz, 7), F("] ---> "));
DEBUG_DELAY(20);
}
if (finish_incremental_LSF(&lsf_results)) {
SERIAL_ECHOPGM("Could not complete LSF!");
return true;
}

rotation.apply_rotation_xyz(mx, my, mz);
bedlevel.set_all_mesh_points_to_value(0);

if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLN(F("after rotation = ["), p_float_t(mx, 7), AS_CHAR(','), p_float_t(my, 7), AS_CHAR(','), p_float_t(mz, 7), F("] ---> "));
DEBUG_DELAY(20);
}
matrix_3x3 rotation = matrix_3x3::create_look_at(vector_3(lsf_results.A, lsf_results.B, 1));
GRID_LOOP(i, j) {
float mx = bedlevel.get_mesh_x(i), my = bedlevel.get_mesh_y(j), mz = bedlevel.z_values[i][j];

bedlevel.z_values[i][j] = mz - lsf_results.D;
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLN(F("before rotation = ["), p_float_t(mx, 7), AS_CHAR(','), p_float_t(my, 7), AS_CHAR(','), p_float_t(mz, 7), F("] ---> "));
DEBUG_DELAY(20);
}
return false;
}

#else
rotation.apply_rotation_xyz(mx, my, mz);

void BedLevelTools::manualValueUpdate(const uint8_t mesh_x, const uint8_t mesh_y) {
gcode.process_subcommands_now(
TS(F("G29 I"), mesh_x, 'J', mesh_y, 'Z', p_float_t(current_position.z, 3))
);
planner.synchronize();
}

#endif
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLN(F("after rotation = ["), p_float_t(mx, 7), AS_CHAR(','), p_float_t(my, 7), AS_CHAR(','), p_float_t(mz, 7), F("] ---> "));
DEBUG_DELAY(20);
}

void BedLevelTools::manualMove(const uint8_t mesh_x, const uint8_t mesh_y, bool zmove/*=false*/) {
gcode.process_subcommands_now(F("G28O"));
if (zmove) {
planner.synchronize();
current_position.z = goto_mesh_value ? bedlevel.z_values[mesh_x][mesh_y] : Z_CLEARANCE_BETWEEN_PROBES;
planner.buffer_line(current_position, homing_feedrate(Z_AXIS), active_extruder);
planner.synchronize();
}
else {
dwinShowPopup(ICON_BLTouch, F("Moving to Point"), F("Please wait until done."));
hmiSaveProcessID(ID_NothingToDo);
gcode.process_subcommands_now(TS(F("G0 F300 Z"), p_float_t(Z_CLEARANCE_BETWEEN_PROBES, 3)));
gcode.process_subcommands_now(TS(F("G42 F4000 I"), mesh_x, F(" J"), mesh_y));
planner.synchronize();
current_position.z = goto_mesh_value ? bedlevel.z_values[mesh_x][mesh_y] : Z_CLEARANCE_BETWEEN_PROBES;
planner.buffer_line(current_position, homing_feedrate(Z_AXIS), active_extruder);
planner.synchronize();
hmiReturnScreen();
bedlevel.z_values[i][j] = mz - lsf_results.D;
}
return false;
}

// Move / Probe methods. As examples, not yet used.
void BedLevelTools::moveToXYZ() {
goto_mesh_value = true;
manualMove(mesh_x, mesh_y, false);
}
void BedLevelTools::moveToXY() {
goto_mesh_value = false;
manualMove(mesh_x, mesh_y, false);
}
void BedLevelTools::moveToZ() {
goto_mesh_value = true;
manualMove(mesh_x, mesh_y, true);
}
void BedLevelTools::probeXY() {
#else

void BedLevelTools::manualValueUpdate(const uint8_t mesh_x, const uint8_t mesh_y) {
gcode.process_subcommands_now(
MString<MAX_CMD_SIZE>(
F("G28O\nG0Z"), uint16_t(Z_CLEARANCE_DEPLOY_PROBE),
F("\nG30X"), p_float_t(bedlevel.get_mesh_x(mesh_x), 2),
F("Y"), p_float_t(bedlevel.get_mesh_y(mesh_y), 2)
)
TS(F("G29 I"), mesh_x, 'J', mesh_y, 'Z', p_float_t(current_position.z, 3))
);
planner.synchronize();
}

void BedLevelTools::meshReset() {
ZERO(bedlevel.z_values);
TERN_(AUTO_BED_LEVELING_BILINEAR, bedlevel.refresh_bed_level());
}
#endif

// Accessors
float BedLevelTools::getMaxValue() {
float max = -(__FLT_MAX__);
GRID_LOOP(x, y) { const float z = bedlevel.z_values[x][y]; if (!isnan(z)) NOLESS(max, z); }
return max;
void BedLevelTools::manualMove(const uint8_t mesh_x, const uint8_t mesh_y, bool zmove/*=false*/) {
gcode.process_subcommands_now(F("G28O"));
if (zmove) {
planner.synchronize();
current_position.z = goto_mesh_value ? bedlevel.z_values[mesh_x][mesh_y] : Z_CLEARANCE_BETWEEN_PROBES;
planner.buffer_line(current_position, homing_feedrate(Z_AXIS), active_extruder);
planner.synchronize();
}

float BedLevelTools::getMinValue() {
float min = __FLT_MAX__;
GRID_LOOP(x, y) { const float z = bedlevel.z_values[x][y]; if (!isnan(z)) NOMORE(min, z); }
return min;
else {
dwinShowPopup(ICON_BLTouch, F("Moving to Point"), F("Please wait until done."));
hmiSaveProcessID(ID_NothingToDo);
gcode.process_subcommands_now(TS(F("G0 F300 Z"), p_float_t(Z_CLEARANCE_BETWEEN_PROBES, 3)));
gcode.process_subcommands_now(TS(F("G42 F4000 I"), mesh_x, F(" J"), mesh_y));
planner.synchronize();
current_position.z = goto_mesh_value ? bedlevel.z_values[mesh_x][mesh_y] : Z_CLEARANCE_BETWEEN_PROBES;
planner.buffer_line(current_position, homing_feedrate(Z_AXIS), active_extruder);
planner.synchronize();
hmiReturnScreen();
}

// Return 'true' if mesh is good and within LCD limits
bool BedLevelTools::meshValidate() {
GRID_LOOP(x, y) {
const float v = bedlevel.z_values[x][y];
if (isnan(v) || !WITHIN(v, Z_OFFSET_MIN, Z_OFFSET_MAX)) return false;
}
return true;
}

// Move / Probe methods. As examples, not yet used.
void BedLevelTools::moveToXYZ() {
goto_mesh_value = true;
manualMove(mesh_x, mesh_y, false);
}
void BedLevelTools::moveToXY() {
goto_mesh_value = false;
manualMove(mesh_x, mesh_y, false);
}
void BedLevelTools::moveToZ() {
goto_mesh_value = true;
manualMove(mesh_x, mesh_y, true);
}
void BedLevelTools::probeXY() {
gcode.process_subcommands_now(
MString<MAX_CMD_SIZE>(
F("G28O\nG0Z"), uint16_t(Z_CLEARANCE_DEPLOY_PROBE),
F("\nG30X"), p_float_t(bedlevel.get_mesh_x(mesh_x), 2),
F("Y"), p_float_t(bedlevel.get_mesh_y(mesh_y), 2)
)
);
}

void BedLevelTools::meshReset() {
ZERO(bedlevel.z_values);
TERN_(AUTO_BED_LEVELING_BILINEAR, bedlevel.refresh_bed_level());
}

// Accessors
float BedLevelTools::getMaxValue() {
float max = -(__FLT_MAX__);
GRID_LOOP(x, y) { const float z = bedlevel.z_values[x][y]; if (!isnan(z)) NOLESS(max, z); }
return max;
}

float BedLevelTools::getMinValue() {
float min = __FLT_MAX__;
GRID_LOOP(x, y) { const float z = bedlevel.z_values[x][y]; if (!isnan(z)) NOMORE(min, z); }
return min;
}

// Return 'true' if mesh is good and within LCD limits
bool BedLevelTools::meshValidate() {
GRID_LOOP(x, y) {
const float v = bedlevel.z_values[x][y];
if (isnan(v) || !WITHIN(v, Z_OFFSET_MIN, Z_OFFSET_MAX)) return false;
}
return true;
}

#if ENABLED(USE_GRID_MESHVIEWER)

constexpr uint8_t meshfont = TERN(TJC_DISPLAY, font8x16, font6x12);

void BedLevelTools::drawBedMesh(int16_t selected/*=-1*/, uint8_t gridline_width/*=1*/, uint16_t padding_x/*=8*/, uint16_t padding_y_top/*=(40 + 53 - 7)*/) {
drawing_mesh = true;
const uint16_t total_width_px = DWIN_WIDTH - padding_x - padding_x;
const uint16_t cell_width_px = total_width_px / (GRID_MAX_POINTS_X);
const uint16_t cell_height_px = total_width_px / (GRID_MAX_POINTS_Y);
const float v_max = abs(getMaxValue()), v_min = abs(getMinValue()), rmax = _MAX(v_min, v_max);

// Clear background from previous selection and select new square
dwinDrawRectangle(1, COLOR_BG_BLACK, _MAX(0, padding_x - gridline_width), _MAX(0, padding_y_top - gridline_width), padding_x + total_width_px, padding_y_top + total_width_px);
if (selected >= 0) {
const auto selected_y = selected / (GRID_MAX_POINTS_X);
const auto selected_x = selected - (GRID_MAX_POINTS_X) * selected_y;
const auto start_y_px = padding_y_top + selected_y * cell_height_px;
const auto start_x_px = padding_x + selected_x * cell_width_px;
dwinDrawRectangle(1, COLOR_WHITE, _MAX(0, start_x_px - gridline_width), _MAX(0, start_y_px - gridline_width), start_x_px + cell_width_px, start_y_px + cell_height_px);
}

#if ENABLED(USE_GRID_MESHVIEWER)

constexpr uint8_t meshfont = TERN(TJC_DISPLAY, font8x16, font6x12);

void BedLevelTools::drawBedMesh(int16_t selected/*=-1*/, uint8_t gridline_width/*=1*/, uint16_t padding_x/*=8*/, uint16_t padding_y_top/*=(40 + 53 - 7)*/) {
drawing_mesh = true;
const uint16_t total_width_px = DWIN_WIDTH - padding_x - padding_x;
const uint16_t cell_width_px = total_width_px / (GRID_MAX_POINTS_X);
const uint16_t cell_height_px = total_width_px / (GRID_MAX_POINTS_Y);
const float v_max = abs(getMaxValue()), v_min = abs(getMinValue()), rmax = _MAX(v_min, v_max);

// Clear background from previous selection and select new square
dwinDrawRectangle(1, COLOR_BG_BLACK, _MAX(0, padding_x - gridline_width), _MAX(0, padding_y_top - gridline_width), padding_x + total_width_px, padding_y_top + total_width_px);
if (selected >= 0) {
const auto selected_y = selected / (GRID_MAX_POINTS_X);
const auto selected_x = selected - (GRID_MAX_POINTS_X) * selected_y;
const auto start_y_px = padding_y_top + selected_y * cell_height_px;
const auto start_x_px = padding_x + selected_x * cell_width_px;
dwinDrawRectangle(1, COLOR_WHITE, _MAX(0, start_x_px - gridline_width), _MAX(0, start_y_px - gridline_width), start_x_px + cell_width_px, start_y_px + cell_height_px);
}
// Draw value square grid
GRID_LOOP(x, y) {
const auto start_x_px = padding_x + x * cell_width_px;
const auto end_x_px = start_x_px + cell_width_px - 1 - gridline_width;
const auto start_y_px = padding_y_top + ((GRID_MAX_POINTS_Y) - y - 1) * cell_height_px;
const auto end_y_px = start_y_px + cell_height_px - 1 - gridline_width;
dwinDrawRectangle(1, // RGB565 colors: http://www.barth-dev.de/online/rgb565-color-picker/
isnan(bedlevel.z_values[x][y]) ? COLOR_GREY : ( // gray if undefined
(bedlevel.z_values[x][y] < 0 ?
(uint16_t)round(0x1F * -bedlevel.z_values[x][y] / rmax) << 11 : // red if mesh point value is negative
(uint16_t)round(0x3F * bedlevel.z_values[x][y] / rmax) << 5) | // green if mesh point value is positive
_MIN(0x1F, (((uint8_t)abs(bedlevel.z_values[x][y]) / 10) * 4))), // + blue stepping for every mm
start_x_px, start_y_px, end_x_px, end_y_px
);

// Draw value square grid
GRID_LOOP(x, y) {
const auto start_x_px = padding_x + x * cell_width_px;
const auto end_x_px = start_x_px + cell_width_px - 1 - gridline_width;
const auto start_y_px = padding_y_top + ((GRID_MAX_POINTS_Y) - y - 1) * cell_height_px;
const auto end_y_px = start_y_px + cell_height_px - 1 - gridline_width;
dwinDrawRectangle(1, // RGB565 colors: http://www.barth-dev.de/online/rgb565-color-picker/
isnan(bedlevel.z_values[x][y]) ? COLOR_GREY : ( // gray if undefined
(bedlevel.z_values[x][y] < 0 ?
(uint16_t)round(0x1F * -bedlevel.z_values[x][y] / rmax) << 11 : // red if mesh point value is negative
(uint16_t)round(0x3F * bedlevel.z_values[x][y] / rmax) << 5) | // green if mesh point value is positive
_MIN(0x1F, (((uint8_t)abs(bedlevel.z_values[x][y]) / 10) * 4))), // + blue stepping for every mm
start_x_px, start_y_px, end_x_px, end_y_px
);
safe_delay(10);
LCD_SERIAL.flushTX();

// Draw value text on
const uint8_t fs = DWINUI::fontWidth(meshfont);
if (viewer_print_value) {
xy_long_t offset { 0, cell_height_px / 2 - fs };
if (isnan(bedlevel.z_values[x][y])) { // undefined
dwinDrawString(false, meshfont, COLOR_WHITE, COLOR_BG_BLUE, start_x_px + cell_width_px / 2 - 5, start_y_px + offset.y, F("X"));
}
else { // has value
MString<12> msg;
if ((GRID_MAX_POINTS_X) < TERN(TJC_DISPLAY, 8, 10))
msg.set(p_float_t(abs(bedlevel.z_values[x][y]), 2));
else
msg.setf(F("%02i"), uint16_t(abs(bedlevel.z_values[x][y] - int16_t(bedlevel.z_values[x][y])) * 100));
offset.x = cell_width_px / 2 - (fs / 2) * msg.length() - 2;
if ((GRID_MAX_POINTS_X) >= TERN(TJC_DISPLAY, 8, 10))
dwinDrawString(false, meshfont, COLOR_WHITE, COLOR_BG_BLUE, start_x_px - 2 + offset.x, start_y_px + offset.y, F("."));
dwinDrawString(false, meshfont, COLOR_WHITE, COLOR_BG_BLUE, start_x_px + 1 + offset.x, start_y_px + offset.y, msg);
}
safe_delay(10);
LCD_SERIAL.flushTX();

// Draw value text on
const uint8_t fs = DWINUI::fontWidth(meshfont);
if (viewer_print_value) {
xy_long_t offset { 0, cell_height_px / 2 - fs };
if (isnan(bedlevel.z_values[x][y])) { // undefined
dwinDrawString(false, meshfont, COLOR_WHITE, COLOR_BG_BLUE, start_x_px + cell_width_px / 2 - 5, start_y_px + offset.y, F("X"));
}
else { // has value
MString<12> msg;
if ((GRID_MAX_POINTS_X) < TERN(TJC_DISPLAY, 8, 10))
msg.set(p_float_t(abs(bedlevel.z_values[x][y]), 2));
else
msg.setf(F("%02i"), uint16_t(abs(bedlevel.z_values[x][y] - int16_t(bedlevel.z_values[x][y])) * 100));
offset.x = cell_width_px / 2 - (fs / 2) * msg.length() - 2;
if ((GRID_MAX_POINTS_X) >= TERN(TJC_DISPLAY, 8, 10))
dwinDrawString(false, meshfont, COLOR_WHITE, COLOR_BG_BLUE, start_x_px - 2 + offset.x, start_y_px + offset.y, F("."));
dwinDrawString(false, meshfont, COLOR_WHITE, COLOR_BG_BLUE, start_x_px + 1 + offset.x, start_y_px + offset.y, msg);
}
safe_delay(10);
LCD_SERIAL.flushTX();
}
}
}
}

void BedLevelTools::setMeshViewerStatus() { // TODO: draw gradient with values as a legend instead
float v_max = abs(getMaxValue()), v_min = abs(getMinValue()), rmax = _MAX(v_min, v_max), rmin = _MIN(v_min, v_max);
if (rmax > 3e+10f) rmax = 0.0000001;
if (rmin > 3e+10f) rmin = 0.0000001;
ui.set_status(&MString<47>(F("Red "), p_float_t(-rmax, 3), F("..0.."), p_float_t(rmin, 3), F(" Green")));
drawing_mesh = false;
}
void BedLevelTools::setMeshViewerStatus() { // TODO: draw gradient with values as a legend instead
float v_max = abs(getMaxValue()), v_min = abs(getMinValue()), rmax = _MAX(v_min, v_max), rmin = _MIN(v_min, v_max);
if (rmax > 3e+10f) rmax = 0.0000001;
if (rmin > 3e+10f) rmin = 0.0000001;
ui.set_status(&MString<47>(F("Red "), p_float_t(-rmax, 3), F("..0.."), p_float_t(rmin, 3), F(" Green")));
drawing_mesh = false;
}

#endif // USE_GRID_MESHVIEWER
#endif // USE_GRID_MESHVIEWER

#endif // DWIN_LCD_PROUI && HAS_LEVELING