Touch UI Bed Mesh Screen refactor, enhancements (#21521)

- Split mesh view and edit screen into two screens
- The editor now live-updates the graphics
- Added Touch UI mesh progress feedback to `G26`
- Show positive / negative mesh values in different colors
This commit is contained in:
Marcio T 2021-04-04 17:58:03 -06:00 committed by GitHub
parent 8da8bf7e87
commit 75b790376d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 535 additions and 264 deletions

View File

@ -730,7 +730,7 @@ void unified_bed_leveling::shift_mesh_height() {
uint8_t count = GRID_MAX_POINTS; uint8_t count = GRID_MAX_POINTS;
mesh_index_pair best; mesh_index_pair best;
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::MESH_START)); TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::G29_START));
do { do {
if (do_ubl_mesh_map) display_map(param.T_map_type); if (do_ubl_mesh_map) display_map(param.T_map_type);
@ -755,14 +755,14 @@ void unified_bed_leveling::shift_mesh_height() {
: find_closest_mesh_point_of_type(INVALID, nearby, true); : find_closest_mesh_point_of_type(INVALID, nearby, true);
if (best.pos.x >= 0) { // mesh point found and is reachable by probe if (best.pos.x >= 0) { // mesh point found and is reachable by probe
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::PROBE_START)); TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::G29_POINT_START));
const float measured_z = probe.probe_at_point( const float measured_z = probe.probe_at_point(
best.meshpos(), best.meshpos(),
stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, param.V_verbosity stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, param.V_verbosity
); );
z_values[best.pos.x][best.pos.y] = measured_z; z_values[best.pos.x][best.pos.y] = measured_z;
#if ENABLED(EXTENSIBLE_UI) #if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(best.pos, ExtUI::PROBE_FINISH); ExtUI::onMeshUpdate(best.pos, ExtUI::G29_POINT_FINISH);
ExtUI::onMeshUpdate(best.pos, measured_z); ExtUI::onMeshUpdate(best.pos, measured_z);
#endif #endif
} }
@ -770,7 +770,7 @@ void unified_bed_leveling::shift_mesh_height() {
} while (best.pos.x >= 0 && --count); } while (best.pos.x >= 0 && --count);
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::MESH_FINISH)); TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::G29_FINISH));
// Release UI during stow to allow for PAUSE_BEFORE_DEPLOY_STOW // Release UI during stow to allow for PAUSE_BEFORE_DEPLOY_STOW
TERN_(HAS_LCD_MENU, ui.release()); TERN_(HAS_LCD_MENU, ui.release());

View File

@ -113,6 +113,10 @@
#include "../../module/temperature.h" #include "../../module/temperature.h"
#include "../../lcd/marlinui.h" #include "../../lcd/marlinui.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../lcd/extui/ui_api.h"
#endif
#if ENABLED(UBL_HILBERT_CURVE) #if ENABLED(UBL_HILBERT_CURVE)
#include "../../feature/bedlevel/hilbert_curve.h" #include "../../feature/bedlevel/hilbert_curve.h"
#endif #endif
@ -725,11 +729,13 @@ void GcodeSuite::G26() {
#endif // !ARC_SUPPORT #endif // !ARC_SUPPORT
mesh_index_pair location; mesh_index_pair location;
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location.pos, ExtUI::G26_START));
do { do {
// Find the nearest confluence // Find the nearest confluence
location = g26.find_closest_circle_to_print(g26.continue_with_closest ? xy_pos_t(current_position) : g26.xy_pos); location = g26.find_closest_circle_to_print(g26.continue_with_closest ? xy_pos_t(current_position) : g26.xy_pos);
if (location.valid()) { if (location.valid()) {
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location.pos, ExtUI::G26_POINT_START));
const xy_pos_t circle = _GET_MESH_POS(location.pos); const xy_pos_t circle = _GET_MESH_POS(location.pos);
// If this mesh location is outside the printable radius, skip it. // If this mesh location is outside the printable radius, skip it.
@ -845,6 +851,8 @@ void GcodeSuite::G26() {
g26.connect_neighbor_with_line(location.pos, 1, 0); g26.connect_neighbor_with_line(location.pos, 1, 0);
g26.connect_neighbor_with_line(location.pos, 0, -1); g26.connect_neighbor_with_line(location.pos, 0, -1);
g26.connect_neighbor_with_line(location.pos, 0, 1); g26.connect_neighbor_with_line(location.pos, 0, 1);
planner.synchronize();
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location.pos, ExtUI::G26_POINT_FINISH));
if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE; if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE;
} }
@ -854,6 +862,7 @@ void GcodeSuite::G26() {
LEAVE: LEAVE:
ui.set_status_P(GET_TEXT(MSG_G26_LEAVING), -1); ui.set_status_P(GET_TEXT(MSG_G26_LEAVING), -1);
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location, ExtUI::G26_FINISH));
g26.retract_filament(destination); g26.retract_filament(destination);
destination.z = Z_CLEARANCE_BETWEEN_PROBES; destination.z = Z_CLEARANCE_BETWEEN_PROBES;

View File

@ -141,11 +141,11 @@ namespace ExtUI {
void onMeshLevelingStart() {} void onMeshLevelingStart() {}
void onMeshUpdate(const int8_t x, const int8_t y, const_float_t val) { void onMeshUpdate(const int8_t x, const int8_t y, const_float_t val) {
BedMeshScreen::onMeshUpdate(x, y, val); BedMeshViewScreen::onMeshUpdate(x, y, val);
} }
void onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) { void onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) {
BedMeshScreen::onMeshUpdate(x, y, state); BedMeshViewScreen::onMeshUpdate(x, y, state);
} }
#endif #endif

View File

@ -1,6 +1,6 @@
/*********************** /*********************
* bed_mesh_screen.cpp * * bed_mesh_base.cpp *
***********************/ *********************/
/**************************************************************************** /****************************************************************************
* Written By Marcio Teixeira 2020 * * Written By Marcio Teixeira 2020 *
@ -21,43 +21,17 @@
#include "../config.h" #include "../config.h"
#include "screens.h" #include "screens.h"
#include "screen_data.h"
#ifdef FTDI_BED_MESH_SCREEN #ifdef FTDI_BED_MESH_BASE
using namespace FTDI; using namespace FTDI;
using namespace Theme;
using namespace ExtUI;
constexpr static BedMeshScreenData &mydata = screen_data.BedMeshScreen; void BedMeshBase::_drawMesh(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t opts, float autoscale_max, uint8_t highlightedTag, mesh_getter_ptr func, void *data) {
constexpr static float gaugeThickness = 0.25;
#if ENABLED(TOUCH_UI_PORTRAIT)
#define GRID_COLS 3
#define GRID_ROWS 10
#define MESH_POS BTN_POS(1, 2), BTN_SIZE(3,5)
#define MESSAGE_POS BTN_POS(1, 7), BTN_SIZE(3,1)
#define Z_LABEL_POS BTN_POS(1, 8), BTN_SIZE(1,1)
#define Z_VALUE_POS BTN_POS(2, 8), BTN_SIZE(2,1)
#define OKAY_POS BTN_POS(1,10), BTN_SIZE(3,1)
#else
#define GRID_COLS 5
#define GRID_ROWS 5
#define MESH_POS BTN_POS(1,1), BTN_SIZE(3,5)
#define MESSAGE_POS BTN_POS(4,1), BTN_SIZE(2,1)
#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 OKAY_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, float autoscale_max) {
constexpr uint8_t rows = GRID_MAX_POINTS_Y; constexpr uint8_t rows = GRID_MAX_POINTS_Y;
constexpr uint8_t cols = GRID_MAX_POINTS_X; constexpr uint8_t cols = GRID_MAX_POINTS_X;
#define VALUE(X,Y) (data ? data[X][Y] : 0) #define VALUE(X,Y) (func ? func(X,Y,data) : 0)
#define ISVAL(X,Y) (data ? !isnan(VALUE(X,Y)) : true) #define ISVAL(X,Y) (func ? !isnan(VALUE(X,Y)) : true)
#define HEIGHT(X,Y) (ISVAL(X,Y) ? (VALUE(X,Y) - val_min) * scale_z : 0) #define HEIGHT(X,Y) (ISVAL(X,Y) ? (VALUE(X,Y) - val_min) * scale_z : 0)
// Compute the mean, min and max for the points // Compute the mean, min and max for the points
@ -67,7 +41,7 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
float val_min = INFINITY; float val_min = INFINITY;
uint8_t val_cnt = 0; uint8_t val_cnt = 0;
if (data && (opts & USE_AUTOSCALE)) { if (opts & USE_AUTOSCALE) {
for (uint8_t y = 0; y < rows; y++) { for (uint8_t y = 0; y < rows; y++) {
for (uint8_t x = 0; x < cols; x++) { for (uint8_t x = 0; x < cols; x++) {
if (ISVAL(x,y)) { if (ISVAL(x,y)) {
@ -140,7 +114,6 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
const uint16_t basePointSize = min(w,h) / max(cols,rows); const uint16_t basePointSize = min(w,h) / max(cols,rows);
CommandProcessor cmd;
cmd.cmd(SAVE_CONTEXT()) cmd.cmd(SAVE_CONTEXT())
.cmd(TAG_MASK(false)) .cmd(TAG_MASK(false))
.cmd(SAVE_CONTEXT()); .cmd(SAVE_CONTEXT());
@ -167,10 +140,14 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
for (uint8_t x = 0; x < cols; x++) { for (uint8_t x = 0; x < cols; x++) {
if (ISVAL(x,y)) { if (ISVAL(x,y)) {
if (opts & USE_COLORS) { if (opts & USE_COLORS) {
const float val_dev = VALUE(x, y) - val_mean; const float val_dev = sq(VALUE(x, y) - val_mean);
const uint8_t neg_byte = sq(val_dev) / (val_dev < 0 ? sq_min : sq_max) * 0xFF; uint8_t r = 0, b = 0;
const uint8_t pos_byte = 255 - neg_byte; //*(VALUE(x, y) < 0 ? &r : &b) = val_dev / sq_min * 0xFF;
cmd.cmd(COLOR_RGB(pos_byte, pos_byte, 0xFF)); if (VALUE(x, y) < 0)
r = val_dev / sq_min * 0xFF;
else
b = val_dev / sq_max * 0xFF;
cmd.cmd(COLOR_RGB(0xFF - b, 0xFF - b - r, 0xFF - r));
} }
cmd.cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y)))); cmd.cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
} }
@ -198,7 +175,7 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
} }
if (opts & USE_HIGHLIGHT) { if (opts & USE_HIGHLIGHT) {
const uint8_t tag = mydata.highlightedTag; const uint8_t tag = highlightedTag;
xy_uint8_t pt; xy_uint8_t pt;
if (tagToPoint(tag, pt)) { if (tagToPoint(tag, pt)) {
cmd.cmd(COLOR_A(128)) cmd.cmd(COLOR_A(128))
@ -211,184 +188,32 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
cmd.cmd(RESTORE_CONTEXT()); cmd.cmd(RESTORE_CONTEXT());
} }
uint8_t BedMeshScreen::pointToTag(uint8_t x, uint8_t y) { uint8_t BedMeshBase::pointToTag(uint8_t x, uint8_t y) {
return y * (GRID_MAX_POINTS_X) + x + 10; return x >= 0 && x < GRID_MAX_POINTS_X && y >= 0 && y < GRID_MAX_POINTS_Y ? y * (GRID_MAX_POINTS_X) + x + 10 : 0;
} }
bool BedMeshScreen::tagToPoint(uint8_t tag, xy_uint8_t &pt) { bool BedMeshBase::tagToPoint(uint8_t tag, xy_uint8_t &pt) {
if (tag < 10) return false; if (tag < 10) return false;
pt.x = (tag - 10) % (GRID_MAX_POINTS_X); pt.x = (tag - 10) % (GRID_MAX_POINTS_X);
pt.y = (tag - 10) / (GRID_MAX_POINTS_X); pt.y = (tag - 10) / (GRID_MAX_POINTS_X);
return true; return true;
} }
void BedMeshScreen::onEntry() { void BedMeshBase::drawMeshBackground(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h) {
mydata.allowEditing = true;
mydata.highlightedTag = 0;
mydata.zAdjustment = 0;
mydata.count = GRID_MAX_POINTS;
mydata.message = mydata.MSG_NONE;
BaseScreen::onEntry();
}
float BedMeshScreen::getHighlightedValue(bool nanAsZero) {
xy_uint8_t pt;
if (tagToPoint(mydata.highlightedTag, pt)) {
const float val = ExtUI::getMeshPoint(pt);
return (isnan(val) && nanAsZero) ? 0 : val;
}
return NAN;
}
void BedMeshScreen::setHighlightedValue(float value) {
xy_uint8_t pt;
if (tagToPoint(mydata.highlightedTag, pt))
ExtUI::setMeshPoint(pt, value);
}
void BedMeshScreen::moveToHighlightedValue() {
xy_uint8_t pt;
if (tagToPoint(mydata.highlightedTag, pt))
ExtUI::moveToMeshPoint(pt, gaugeThickness + mydata.zAdjustment);
}
void BedMeshScreen::adjustHighlightedValue(float increment) {
mydata.zAdjustment += increment;
moveToHighlightedValue();
}
void BedMeshScreen::saveAdjustedHighlightedValue() {
if (mydata.zAdjustment) {
BedMeshScreen::setHighlightedValue(BedMeshScreen::getHighlightedValue(true) + mydata.zAdjustment);
mydata.zAdjustment = 0;
}
}
void BedMeshScreen::changeHighlightedValue(uint8_t tag) {
if (mydata.allowEditing) saveAdjustedHighlightedValue();
mydata.highlightedTag = tag;
if (mydata.allowEditing) moveToHighlightedValue();
}
void BedMeshScreen::drawHighlightedPointValue() {
CommandProcessor cmd;
cmd.font(Theme::font_medium)
.colors(normal_btn)
.text(Z_LABEL_POS, GET_TEXT_F(MSG_MESH_EDIT_Z))
.font(font_small);
if (mydata.allowEditing)
draw_adjuster(cmd, Z_VALUE_POS, 2, getHighlightedValue(true) + mydata.zAdjustment, GET_TEXT_F(MSG_UNITS_MM), 4, 3);
else
draw_adjuster_value(cmd, Z_VALUE_POS, getHighlightedValue(true) + mydata.zAdjustment, GET_TEXT_F(MSG_UNITS_MM), 4, 3);
cmd.colors(action_btn)
.tag(1).button(OKAY_POS, GET_TEXT_F(MSG_BUTTON_OKAY))
.tag(0);
switch (mydata.message) {
case mydata.MSG_MESH_COMPLETE: cmd.text(MESSAGE_POS, GET_TEXT_F(MSG_BED_MAPPING_DONE)); break;
case mydata.MSG_MESH_INCOMPLETE: cmd.text(MESSAGE_POS, GET_TEXT_F(MSG_BED_MAPPING_INCOMPLETE)); break;
default: break;
}
}
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))
.cmd(CLEAR(true,true,true));
// Draw the shadow and tags
cmd.cmd(COLOR_RGB(Theme::bed_mesh_shadow_rgb)); cmd.cmd(COLOR_RGB(Theme::bed_mesh_shadow_rgb));
BedMeshScreen::drawMesh(INSET_POS(MESH_POS), nullptr, USE_POINTS | USE_TAGS); _drawMesh(cmd, x, y, w, h, USE_POINTS | USE_TAGS, 0.1, 0, nullptr, nullptr);
cmd.cmd(COLOR_RGB(bg_text_enabled)); }
}
if (what & FOREGROUND) { void BedMeshBase::drawMeshForeground(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h, mesh_getter_ptr func, void *data, uint8_t highlightedTag, float progress) {
constexpr float autoscale_max_amplitude = 0.03; constexpr float autoscale_max_amplitude = 0.03;
const bool gotAllPoints = mydata.count >= GRID_MAX_POINTS;
if (gotAllPoints) {
drawHighlightedPointValue();
}
CommandProcessor cmd;
cmd.cmd(COLOR_RGB(Theme::bed_mesh_lines_rgb)); cmd.cmd(COLOR_RGB(Theme::bed_mesh_lines_rgb));
const float levelingProgress = sq(float(mydata.count) / GRID_MAX_POINTS); _drawMesh(cmd, x, y, w, h,
BedMeshScreen::drawMesh(INSET_POS(MESH_POS), ExtUI::getMeshArray(), USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (progress > 0.95 ? USE_COLORS : 0),
USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (gotAllPoints ? USE_COLORS : 0), autoscale_max_amplitude * progress,
autoscale_max_amplitude * levelingProgress highlightedTag,
func, data
); );
}
} }
bool BedMeshScreen::onTouchEnd(uint8_t tag) { #endif // FTDI_BED_MESH_BASE
constexpr float increment = 0.01;
switch (tag) {
case 1:
saveAdjustedHighlightedValue();
injectCommands_P(PSTR("G29 S1"));
GOTO_PREVIOUS();
return true;
case 2: adjustHighlightedValue(-increment); break;
case 3: adjustHighlightedValue( increment); break;
default:
if (tag >= 10)
changeHighlightedValue(tag);
else
return false;
}
return true;
}
void BedMeshScreen::onMeshUpdate(const int8_t, const int8_t, const float) {
if (AT_SCREEN(BedMeshScreen))
onRefresh();
}
void BedMeshScreen::onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) {
switch (state) {
case ExtUI::MESH_START:
mydata.allowEditing = false;
mydata.count = 0;
mydata.message = mydata.MSG_NONE;
break;
case ExtUI::MESH_FINISH:
if (mydata.count == GRID_MAX_POINTS && ExtUI::getMeshValid())
mydata.message = mydata.MSG_MESH_COMPLETE;
else
mydata.message = mydata.MSG_MESH_INCOMPLETE;
mydata.count = GRID_MAX_POINTS;
break;
case ExtUI::PROBE_START:
mydata.highlightedTag = pointToTag(x, y);
break;
case ExtUI::PROBE_FINISH:
mydata.count++;
break;
}
BedMeshScreen::onMeshUpdate(x, y, 0);
}
void BedMeshScreen::startMeshProbe() {
GOTO_SCREEN(BedMeshScreen);
mydata.allowEditing = false;
mydata.count = 0;
injectCommands_P(PSTR(BED_LEVELING_COMMANDS));
}
void BedMeshScreen::showMesh() {
GOTO_SCREEN(BedMeshScreen);
mydata.allowEditing = false;
}
void BedMeshScreen::showMeshEditor() {
SpinnerDialogBox::enqueueAndWait_P(ExtUI::isMachineHomed() ? F("M420 S1") : F("G28\nM420 S1"));
// After the spinner, go to this screen.
current_screen.forget();
PUSH_SCREEN(BedMeshScreen);
}
#endif // FTDI_BED_MESH_SCREEN

View File

@ -0,0 +1,46 @@
/*******************
* bed_mesh_base.h *
*******************/
/****************************************************************************
* Written By Marcio Teixeira 2020 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <https://www.gnu.org/licenses/>. *
****************************************************************************/
#pragma once
#define FTDI_BED_MESH_BASE
class BedMeshBase : public BaseScreen {
protected:
typedef float (*mesh_getter_ptr)(uint8_t x, uint8_t y, void *data);
private:
enum MeshOpts {
USE_POINTS = 0x01,
USE_COLORS = 0x02,
USE_TAGS = 0x04,
USE_HIGHLIGHT = 0x08,
USE_AUTOSCALE = 0x10
};
static void _drawMesh(CommandProcessor &, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t opts, float autoscale_max, uint8_t highlightedTag, mesh_getter_ptr func, void *data);
protected:
static void drawMeshForeground(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h, mesh_getter_ptr func, void *data, uint8_t highlightedTag = 0, float progress = 1.0);
static void drawMeshBackground(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h);
static uint8_t pointToTag(uint8_t x, uint8_t y);
static bool tagToPoint(uint8_t tag, xy_uint8_t &pt);
};

View File

@ -0,0 +1,186 @@
/****************************
* bed_mesh_edit_screen.cpp *
****************************/
/****************************************************************************
* Written By Marcio Teixeira 2020 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <https://www.gnu.org/licenses/>. *
****************************************************************************/
#include "../config.h"
#include "screens.h"
#include "screen_data.h"
#ifdef FTDI_BED_MESH_EDIT_SCREEN
using namespace FTDI;
using namespace Theme;
using namespace ExtUI;
constexpr static BedMeshEditScreenData &mydata = screen_data.BedMeshEditScreen;
constexpr static float gaugeThickness = 0.25;
#if ENABLED(TOUCH_UI_PORTRAIT)
#define GRID_COLS 3
#define GRID_ROWS 10
#define MESH_POS BTN_POS(1, 2), BTN_SIZE(3,5)
#define MESSAGE_POS BTN_POS(1, 7), BTN_SIZE(3,1)
#define Z_LABEL_POS BTN_POS(1, 8), BTN_SIZE(1,1)
#define Z_VALUE_POS BTN_POS(2, 8), BTN_SIZE(2,1)
#define BACK_POS BTN_POS(1,10), BTN_SIZE(2,1)
#define SAVE_POS BTN_POS(3,10), BTN_SIZE(1,1)
#else
#define GRID_COLS 5
#define GRID_ROWS 5
#define MESH_POS BTN_POS(1,1), BTN_SIZE(3,5)
#define MESSAGE_POS BTN_POS(4,1), BTN_SIZE(2,1)
#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 BACK_POS BTN_POS(4,5), BTN_SIZE(1,1)
#define SAVE_POS BTN_POS(5,5), BTN_SIZE(1,1)
#endif
static float meshGetter(uint8_t x, uint8_t y, void*) {
xy_uint8_t pos;
pos.x = x;
pos.y = y;
return ExtUI::getMeshPoint(pos) + (mydata.highlight.x != -1 && mydata.highlight == pos ? mydata.zAdjustment : 0);
}
void BedMeshEditScreen::onEntry() {
mydata.needSave = false;
mydata.highlight.x = -1;
mydata.zAdjustment = 0;
BaseScreen::onEntry();
}
float BedMeshEditScreen::getHighlightedValue() {
const float val = ExtUI::getMeshPoint(mydata.highlight);
return (isnan(val) ? 0 : val) + mydata.zAdjustment;
}
void BedMeshEditScreen::setHighlightedValue(float value) {
ExtUI::setMeshPoint(mydata.highlight, value);
}
void BedMeshEditScreen::moveToHighlightedValue() {
if (ExtUI::getMeshValid()) {
ExtUI::setLevelingActive(true);
ExtUI::moveToMeshPoint(mydata.highlight, gaugeThickness + mydata.zAdjustment);
}
}
void BedMeshEditScreen::adjustHighlightedValue(float increment) {
if(mydata.highlight.x != -1) {
mydata.zAdjustment += increment;
moveToHighlightedValue();
mydata.needSave = true;
}
}
void BedMeshEditScreen::saveAdjustedHighlightedValue() {
if (mydata.zAdjustment && mydata.highlight.x != -1) {
setHighlightedValue(getHighlightedValue());
mydata.zAdjustment = 0;
}
}
bool BedMeshEditScreen::changeHighlightedValue(uint8_t tag) {
saveAdjustedHighlightedValue();
if (tagToPoint(tag, mydata.highlight)) {
moveToHighlightedValue();
return true;
}
return false;
}
void BedMeshEditScreen::drawHighlightedPointValue() {
CommandProcessor cmd;
cmd.font(Theme::font_medium)
.colors(normal_btn)
.text(Z_LABEL_POS, GET_TEXT_F(MSG_MESH_EDIT_Z))
.font(font_small);
if(mydata.highlight.x != -1)
draw_adjuster(cmd, Z_VALUE_POS, 3, getHighlightedValue(), GET_TEXT_F(MSG_UNITS_MM), 4, 3);
cmd.colors(mydata.needSave ? normal_btn : action_btn)
.tag(1).button(BACK_POS, GET_TEXT_F(MSG_BUTTON_BACK))
.colors(mydata.needSave ? action_btn : normal_btn)
.enabled(mydata.needSave)
.tag(2).button(SAVE_POS, GET_TEXT_F(MSG_TOUCHMI_SAVE));
}
void BedMeshEditScreen::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)
CommandProcessor cmd;
if (what & BACKGROUND) {
cmd.cmd(CLEAR_COLOR_RGB(bg_color))
.cmd(CLEAR(true,true,true));
drawMeshBackground(cmd, INSET_POS(MESH_POS));
}
if (what & FOREGROUND) {
drawHighlightedPointValue();
drawMeshForeground(cmd, INSET_POS(MESH_POS), meshGetter, nullptr, pointToTag(mydata.highlight.x,mydata.highlight.y));
}
}
bool BedMeshEditScreen::onTouchHeld(uint8_t tag) {
constexpr float increment = 0.01;
switch (tag) {
case 3: adjustHighlightedValue(-increment); return true;
case 4: adjustHighlightedValue( increment); return true;
}
return false;
}
bool BedMeshEditScreen::onTouchEnd(uint8_t tag) {
switch (tag) {
case 1:
// On Cancel, reload saved mesh, discarding changes
GOTO_PREVIOUS();
injectCommands_P(PSTR("G29 L1"));
return true;
case 2:
saveAdjustedHighlightedValue();
injectCommands_P(PSTR("G29 S1"));
mydata.needSave = false;
return true;
case 3:
case 4:
return onTouchHeld(tag);
default: return changeHighlightedValue(tag);
}
return true;
}
void BedMeshEditScreen::show() {
// On entry, home if needed and save current mesh
if (!ExtUI::isMachineHomed()) {
SpinnerDialogBox::enqueueAndWait_P(F("G28\nG29 S1"));
// After the spinner, go to this screen.
current_screen.forget();
PUSH_SCREEN(BedMeshEditScreen);
} else {
injectCommands_P(PSTR("G29 S1"));
GOTO_SCREEN(BedMeshEditScreen);
}
}
#endif // FTDI_BED_MESH_EDIT_SCREEN

View File

@ -1,6 +1,6 @@
/********************* /**************************
* bed_mesh_screen.h * * bed_mesh_edit_screen.h *
*********************/ *************************/
/**************************************************************************** /****************************************************************************
* Written By Marcio Teixeira 2020 * * Written By Marcio Teixeira 2020 *
@ -21,49 +21,28 @@
#pragma once #pragma once
#define FTDI_BED_MESH_SCREEN #define FTDI_BED_MESH_EDIT_SCREEN
#define FTDI_BED_MESH_SCREEN_CLASS BedMeshScreen #define FTDI_BED_MESH_EDIT_SCREEN_CLASS BedMeshEditScreen
struct BedMeshScreenData { struct BedMeshEditScreenData {
enum : uint8_t { bool needSave;
MSG_NONE, xy_uint8_t highlight;
MSG_MESH_COMPLETE,
MSG_MESH_INCOMPLETE
} message;
uint8_t count;
uint8_t highlightedTag;
float zAdjustment; float zAdjustment;
bool allowEditing;
}; };
class BedMeshScreen : public BaseScreen, public CachedScreen<BED_MESH_SCREEN_CACHE> { class BedMeshEditScreen : public BedMeshBase, public CachedScreen<BED_MESH_EDIT_SCREEN_CACHE> {
private: private:
enum MeshOpts { static float getHighlightedValue();
USE_POINTS = 0x01,
USE_COLORS = 0x02,
USE_TAGS = 0x04,
USE_HIGHLIGHT = 0x08,
USE_AUTOSCALE = 0x10
};
static uint8_t pointToTag(uint8_t x, uint8_t y);
static bool tagToPoint(uint8_t tag, xy_uint8_t &pt);
static float getHighlightedValue(bool nanAsZero);
static void setHighlightedValue(float value); static void setHighlightedValue(float value);
static void moveToHighlightedValue(); static void moveToHighlightedValue();
static void adjustHighlightedValue(float increment); static void adjustHighlightedValue(float increment);
static void saveAdjustedHighlightedValue(); static void saveAdjustedHighlightedValue();
static void changeHighlightedValue(uint8_t tag); static bool changeHighlightedValue(uint8_t tag);
static void drawHighlightedPointValue(); static void drawHighlightedPointValue();
static void drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::bed_mesh_t data, uint8_t opts, float autoscale_max = 0.1);
public: public:
static void onMeshUpdate(const int8_t x, const int8_t y, const float val);
static void onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t);
static void onEntry(); static void onEntry();
static void onRedraw(draw_mode_t); static void onRedraw(draw_mode_t);
static bool onTouchHeld(uint8_t tag);
static bool onTouchEnd(uint8_t tag); static bool onTouchEnd(uint8_t tag);
static void show();
static void startMeshProbe();
static void showMesh();
static void showMeshEditor();
}; };

View File

@ -0,0 +1,172 @@
/****************************
* bed_mesh_view_screen.cpp *
****************************/
/****************************************************************************
* Written By Marcio Teixeira 2020 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <https://www.gnu.org/licenses/>. *
****************************************************************************/
#include "../config.h"
#include "screens.h"
#include "screen_data.h"
#ifdef FTDI_BED_MESH_VIEW_SCREEN
using namespace FTDI;
using namespace Theme;
using namespace ExtUI;
constexpr static BedMeshViewScreenData &mydata = screen_data.BedMeshViewScreen;
constexpr static float gaugeThickness = 0.25;
#if ENABLED(TOUCH_UI_PORTRAIT)
#define GRID_COLS 3
#define GRID_ROWS 10
#define MESH_POS BTN_POS(1, 2), BTN_SIZE(3,5)
#define MESSAGE_POS BTN_POS(1, 7), BTN_SIZE(3,1)
#define Z_LABEL_POS BTN_POS(1, 8), BTN_SIZE(1,1)
#define Z_VALUE_POS BTN_POS(2, 8), BTN_SIZE(2,1)
#define OKAY_POS BTN_POS(1,10), BTN_SIZE(3,1)
#else
#define GRID_COLS 5
#define GRID_ROWS 5
#define MESH_POS BTN_POS(1,1), BTN_SIZE(3,5)
#define MESSAGE_POS BTN_POS(4,1), BTN_SIZE(2,1)
#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 OKAY_POS BTN_POS(4,5), BTN_SIZE(2,1)
#endif
static float meshGetter(uint8_t x, uint8_t y, void*) {
xy_uint8_t pos;
pos.x = x;
pos.y = y;
return ExtUI::getMeshPoint(pos);
}
void BedMeshViewScreen::onEntry() {
mydata.highlight.x = -1;
mydata.count = GRID_MAX_POINTS;
mydata.message = nullptr;
BaseScreen::onEntry();
}
void BedMeshViewScreen::drawHighlightedPointValue() {
CommandProcessor cmd;
cmd.font(Theme::font_medium)
.colors(normal_btn)
.text(Z_LABEL_POS, GET_TEXT_F(MSG_MESH_EDIT_Z))
.font(font_small);
if(mydata.highlight.x != -1)
draw_adjuster_value(cmd, Z_VALUE_POS, ExtUI::getMeshPoint(mydata.highlight), GET_TEXT_F(MSG_UNITS_MM), 4, 3);
cmd.colors(action_btn)
.tag(1).button(OKAY_POS, GET_TEXT_F(MSG_BUTTON_OKAY))
.tag(0);
if(mydata.message) cmd.text(MESSAGE_POS, mydata.message);
}
void BedMeshViewScreen::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)
CommandProcessor cmd;
if (what & BACKGROUND) {
cmd.cmd(CLEAR_COLOR_RGB(bg_color))
.cmd(CLEAR(true,true,true));
drawMeshBackground(cmd, INSET_POS(MESH_POS));
}
if (what & FOREGROUND) {
const float progress = sq(float(mydata.count) / GRID_MAX_POINTS);
if (progress >= 1.0)
drawHighlightedPointValue();
drawMeshForeground(cmd, INSET_POS(MESH_POS), meshGetter, nullptr, pointToTag(mydata.highlight.x, mydata.highlight.y), progress);
}
}
bool BedMeshViewScreen::onTouchEnd(uint8_t tag) {
switch (tag) {
case 1: GOTO_PREVIOUS(); return true;
default: return tagToPoint(tag, mydata.highlight);
}
return true;
}
void BedMeshViewScreen::onMeshUpdate(const int8_t, const int8_t, const float) {
if (AT_SCREEN(BedMeshViewScreen)) {
onRefresh();
ExtUI::yield();
}
}
void BedMeshViewScreen::onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) {
switch (state) {
case ExtUI::G29_START:
mydata.message = nullptr;
mydata.count = 0;
break;
case ExtUI::G29_FINISH:
if (mydata.count == GRID_MAX_POINTS && ExtUI::getMeshValid())
mydata.message = GET_TEXT_F(MSG_BED_MAPPING_DONE);
else
mydata.message = GET_TEXT_F(MSG_BED_MAPPING_INCOMPLETE);
mydata.count = GRID_MAX_POINTS;
break;
case ExtUI::G26_START:
GOTO_SCREEN(BedMeshViewScreen);
mydata.message = nullptr;
mydata.count = 0;
break;
case ExtUI::G26_FINISH:
GOTO_SCREEN(StatusScreen);
break;
case ExtUI::G29_POINT_START:
case ExtUI::G26_POINT_START:
mydata.highlight.x = x;
mydata.highlight.y = y;
break;
case ExtUI::G29_POINT_FINISH:
case ExtUI::G26_POINT_FINISH:
mydata.count++;
break;
}
BedMeshViewScreen::onMeshUpdate(x, y, 0);
}
void BedMeshViewScreen::doProbe() {
GOTO_SCREEN(BedMeshViewScreen);
mydata.count = 0;
injectCommands_P(PSTR(BED_LEVELING_COMMANDS));
}
void BedMeshViewScreen::doMeshValidation() {
mydata.count = 0;
GOTO_SCREEN(StatusScreen);
injectCommands_P(PSTR("G28 O\nM117 Heating...\nG26 R X0 Y0"));
}
void BedMeshViewScreen::show() {
injectCommands_P(PSTR("G29 L1"));
GOTO_SCREEN(BedMeshViewScreen);
}
#endif // FTDI_BED_MESH_VIEW_SCREEN

View File

@ -0,0 +1,48 @@
/**************************
* bed_mesh_view_screen.h *
*************************/
/****************************************************************************
* Written By Marcio Teixeira 2020 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <https://www.gnu.org/licenses/>. *
****************************************************************************/
#pragma once
#define FTDI_BED_MESH_VIEW_SCREEN
#define FTDI_BED_MESH_VIEW_SCREEN_CLASS BedMeshViewScreen
struct BedMeshViewScreenData {
progmem_str message;
uint8_t count;
xy_uint8_t highlight;
};
class BedMeshViewScreen : public BedMeshBase, public CachedScreen<BED_MESH_VIEW_SCREEN_CACHE> {
private:
static float getHighlightedValue();
static bool changeHighlightedValue(uint8_t tag);
static void drawHighlightedPointValue();
public:
static void onMeshUpdate(const int8_t x, const int8_t y, const float val);
static void onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t);
static void onEntry();
static void onRedraw(draw_mode_t);
static bool onTouchEnd(uint8_t tag);
static void doProbe();
static void doMeshValidation();
static void show();
};

View File

@ -111,20 +111,17 @@ bool LevelingMenu::onTouchEnd(uint8_t tag) {
#define BED_LEVELING_COMMANDS "G29" #define BED_LEVELING_COMMANDS "G29"
#endif #endif
#if ENABLED(AUTO_BED_LEVELING_UBL) #if ENABLED(AUTO_BED_LEVELING_UBL)
BedMeshScreen::startMeshProbe(); BedMeshViewScreen::doProbe();
#else #else
SpinnerDialogBox::enqueueAndWait_P(F(BED_LEVELING_COMMANDS)); SpinnerDialogBox::enqueueAndWait_P(F(BED_LEVELING_COMMANDS));
#endif #endif
break; break;
#if ENABLED(AUTO_BED_LEVELING_UBL) #if ENABLED(AUTO_BED_LEVELING_UBL)
case 4: BedMeshScreen::showMesh(); break; case 4: BedMeshViewScreen::show(); break;
case 5: BedMeshScreen::showMeshEditor(); break; case 5: BedMeshEditScreen::show(); break;
#endif #endif
#if ENABLED(G26_MESH_VALIDATION) #if ENABLED(G26_MESH_VALIDATION)
case 6: case 6: BedMeshViewScreen::doMeshValidation(); break;
GOTO_SCREEN(StatusScreen);
injectCommands_P(PSTR("M117 Printing Test Pattern\nG28 O\nG26 R"));
break;
#endif #endif
#if ENABLED(BLTOUCH) #if ENABLED(BLTOUCH)
case 7: injectCommands_P(PSTR("M280 P0 S60")); break; case 7: injectCommands_P(PSTR("M280 P0 S60")); break;

View File

@ -55,7 +55,8 @@ union screen_data_t {
DECL_DATA_IF_INCLUDED(FTDI_CHANGE_FILAMENT_SCREEN) DECL_DATA_IF_INCLUDED(FTDI_CHANGE_FILAMENT_SCREEN)
DECL_DATA_IF_INCLUDED(FTDI_FILES_SCREEN) DECL_DATA_IF_INCLUDED(FTDI_FILES_SCREEN)
DECL_DATA_IF_INCLUDED(FTDI_MOVE_AXIS_SCREEN) DECL_DATA_IF_INCLUDED(FTDI_MOVE_AXIS_SCREEN)
DECL_DATA_IF_INCLUDED(FTDI_BED_MESH_SCREEN) DECL_DATA_IF_INCLUDED(FTDI_BED_MESH_VIEW_SCREEN)
DECL_DATA_IF_INCLUDED(FTDI_BED_MESH_EDIT_SCREEN)
DECL_DATA_IF_INCLUDED(FTDI_STRESS_TEST_SCREEN) DECL_DATA_IF_INCLUDED(FTDI_STRESS_TEST_SCREEN)
DECL_DATA_IF_INCLUDED(FTDI_COCOA_PREHEAT_SCREEN) DECL_DATA_IF_INCLUDED(FTDI_COCOA_PREHEAT_SCREEN)
DECL_DATA_IF_INCLUDED(FTDI_COCOA_LOAD_CHOCOLATE_SCREEN) DECL_DATA_IF_INCLUDED(FTDI_COCOA_LOAD_CHOCOLATE_SCREEN)

View File

@ -75,7 +75,8 @@ SCREEN_TABLE {
DECL_SCREEN_IF_INCLUDED(FTDI_STEPPER_BUMP_SENSITIVITY_SCREEN) DECL_SCREEN_IF_INCLUDED(FTDI_STEPPER_BUMP_SENSITIVITY_SCREEN)
DECL_SCREEN_IF_INCLUDED(FTDI_LEVELING_MENU) DECL_SCREEN_IF_INCLUDED(FTDI_LEVELING_MENU)
DECL_SCREEN_IF_INCLUDED(FTDI_Z_OFFSET_SCREEN) DECL_SCREEN_IF_INCLUDED(FTDI_Z_OFFSET_SCREEN)
DECL_SCREEN_IF_INCLUDED(FTDI_BED_MESH_SCREEN) DECL_SCREEN_IF_INCLUDED(FTDI_BED_MESH_VIEW_SCREEN)
DECL_SCREEN_IF_INCLUDED(FTDI_BED_MESH_EDIT_SCREEN)
DECL_SCREEN_IF_INCLUDED(FTDI_NOZZLE_OFFSETS_SCREEN) DECL_SCREEN_IF_INCLUDED(FTDI_NOZZLE_OFFSETS_SCREEN)
DECL_SCREEN_IF_INCLUDED(FTDI_BACKLASH_COMP_SCREEN) DECL_SCREEN_IF_INCLUDED(FTDI_BACKLASH_COMP_SCREEN)
DECL_SCREEN_IF_INCLUDED(FTDI_FEEDRATE_PERCENT_SCREEN) DECL_SCREEN_IF_INCLUDED(FTDI_FEEDRATE_PERCENT_SCREEN)

View File

@ -61,7 +61,8 @@ enum {
ZOFFSET_SCREEN_CACHE, ZOFFSET_SCREEN_CACHE,
#endif #endif
#if HAS_MESH #if HAS_MESH
BED_MESH_SCREEN_CACHE, BED_MESH_VIEW_SCREEN_CACHE,
BED_MESH_EDIT_SCREEN_CACHE,
#endif #endif
#endif #endif
#if ENABLED(BABYSTEPPING) #if ENABLED(BABYSTEPPING)
@ -206,7 +207,9 @@ enum {
#include "z_offset_screen.h" #include "z_offset_screen.h"
#endif #endif
#if HAS_MESH #if HAS_MESH
#include "bed_mesh_screen.h" #include "bed_mesh_base.h"
#include "bed_mesh_view_screen.h"
#include "bed_mesh_edit_screen.h"
#endif #endif
#endif #endif

View File

@ -168,10 +168,14 @@ namespace ExtUI {
inline void onMeshUpdate(const xy_int8_t &pos, const_float_t zval) { onMeshUpdate(pos.x, pos.y, zval); } inline void onMeshUpdate(const xy_int8_t &pos, const_float_t zval) { onMeshUpdate(pos.x, pos.y, zval); }
typedef enum : uint8_t { typedef enum : uint8_t {
MESH_START, // Prior to start of probe G29_START, // Prior to start of probe
MESH_FINISH, // Following probe of all points G29_FINISH, // Following probe of all points
PROBE_START, // Beginning probe of grid location G29_POINT_START, // Beginning probe of grid location
PROBE_FINISH // Finished probe of grid location G29_POINT_FINISH, // Finished probe of grid location
G26_START,
G26_FINISH,
G26_POINT_START,
G26_POINT_FINISH
} probe_state_t; } probe_state_t;
void onMeshUpdate(const int8_t xpos, const int8_t ypos, probe_state_t state); void onMeshUpdate(const int8_t xpos, const int8_t ypos, probe_state_t state);
inline void onMeshUpdate(const xy_int8_t &pos, probe_state_t state) { onMeshUpdate(pos.x, pos.y, state); } inline void onMeshUpdate(const xy_int8_t &pos, probe_state_t state) { onMeshUpdate(pos.x, pos.y, state); }