Skip to content

Commit

Permalink
Touch UI Bed Mesh Screen refactor, enhancements (MarlinFirmware#21521)
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
marciot authored Apr 4, 2021
1 parent 8da8bf7 commit 75b7903
Show file tree
Hide file tree
Showing 14 changed files with 534 additions and 263 deletions.
8 changes: 4 additions & 4 deletions Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ void unified_bed_leveling::shift_mesh_height() {
uint8_t count = GRID_MAX_POINTS;

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 {
if (do_ubl_mesh_map) display_map(param.T_map_type);

Expand All @@ -755,22 +755,22 @@ void unified_bed_leveling::shift_mesh_height() {
: find_closest_mesh_point_of_type(INVALID, nearby, true);

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(
best.meshpos(),
stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, param.V_verbosity
);
z_values[best.pos.x][best.pos.y] = measured_z;
#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);
#endif
}
SERIAL_FLUSH(); // Prevent host M105 buffer overrun.

} 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
TERN_(HAS_LCD_MENU, ui.release());
Expand Down
9 changes: 9 additions & 0 deletions Marlin/src/gcode/bedlevel/G26.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@
#include "../../module/temperature.h"
#include "../../lcd/marlinui.h"

#if ENABLED(EXTENSIBLE_UI)
#include "../../lcd/extui/ui_api.h"
#endif

#if ENABLED(UBL_HILBERT_CURVE)
#include "../../feature/bedlevel/hilbert_curve.h"
#endif
Expand Down Expand Up @@ -725,11 +729,13 @@ void GcodeSuite::G26() {
#endif // !ARC_SUPPORT

mesh_index_pair location;
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location.pos, ExtUI::G26_START));
do {
// Find the nearest confluence
location = g26.find_closest_circle_to_print(g26.continue_with_closest ? xy_pos_t(current_position) : g26.xy_pos);

if (location.valid()) {
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location.pos, ExtUI::G26_POINT_START));
const xy_pos_t circle = _GET_MESH_POS(location.pos);

// If this mesh location is outside the printable radius, skip it.
Expand Down Expand Up @@ -845,6 +851,8 @@ void GcodeSuite::G26() {
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);
planner.synchronize();
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location.pos, ExtUI::G26_POINT_FINISH));
if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE;
}

Expand All @@ -854,6 +862,7 @@ void GcodeSuite::G26() {

LEAVE:
ui.set_status_P(GET_TEXT(MSG_G26_LEAVING), -1);
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location, ExtUI::G26_FINISH));

g26.retract_filament(destination);
destination.z = Z_CLEARANCE_BETWEEN_PROBES;
Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/marlin_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,11 @@ namespace ExtUI {
void onMeshLevelingStart() {}

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) {
BedMeshScreen::onMeshUpdate(x, y, state);
BedMeshViewScreen::onMeshUpdate(x, y, state);
}
#endif

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***********************
* bed_mesh_screen.cpp *
***********************/
/*********************
* bed_mesh_base.cpp *
*********************/

/****************************************************************************
* Written By Marcio Teixeira 2020 *
Expand All @@ -21,43 +21,17 @@

#include "../config.h"
#include "screens.h"
#include "screen_data.h"

#ifdef FTDI_BED_MESH_SCREEN
#ifdef FTDI_BED_MESH_BASE

using namespace FTDI;
using namespace Theme;
using namespace ExtUI;

constexpr static BedMeshScreenData &mydata = screen_data.BedMeshScreen;
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) {
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 uint8_t rows = GRID_MAX_POINTS_Y;
constexpr uint8_t cols = GRID_MAX_POINTS_X;

#define VALUE(X,Y) (data ? data[X][Y] : 0)
#define ISVAL(X,Y) (data ? !isnan(VALUE(X,Y)) : true)
#define VALUE(X,Y) (func ? func(X,Y,data) : 0)
#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)

// Compute the mean, min and max for the points
Expand All @@ -67,7 +41,7 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
float val_min = INFINITY;
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 x = 0; x < cols; x++) {
if (ISVAL(x,y)) {
Expand Down Expand Up @@ -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);

CommandProcessor cmd;
cmd.cmd(SAVE_CONTEXT())
.cmd(TAG_MASK(false))
.cmd(SAVE_CONTEXT());
Expand All @@ -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++) {
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) / (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));
const float val_dev = sq(VALUE(x, y) - val_mean);
uint8_t r = 0, b = 0;
//*(VALUE(x, y) < 0 ? &r : &b) = val_dev / sq_min * 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))));
}
Expand Down Expand Up @@ -198,7 +175,7 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
}

if (opts & USE_HIGHLIGHT) {
const uint8_t tag = mydata.highlightedTag;
const uint8_t tag = highlightedTag;
xy_uint8_t pt;
if (tagToPoint(tag, pt)) {
cmd.cmd(COLOR_A(128))
Expand All @@ -211,184 +188,32 @@ void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::
cmd.cmd(RESTORE_CONTEXT());
}

uint8_t BedMeshScreen::pointToTag(uint8_t x, uint8_t y) {
return y * (GRID_MAX_POINTS_X) + x + 10;
uint8_t BedMeshBase::pointToTag(uint8_t x, uint8_t y) {
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;
pt.x = (tag - 10) % (GRID_MAX_POINTS_X);
pt.y = (tag - 10) / (GRID_MAX_POINTS_X);
return true;
}

void BedMeshScreen::onEntry() {
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));
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 gotAllPoints = mydata.count >= GRID_MAX_POINTS;
if (gotAllPoints) {
drawHighlightedPointValue();
}
CommandProcessor cmd;
cmd.cmd(COLOR_RGB(Theme::bed_mesh_lines_rgb));
const float levelingProgress = sq(float(mydata.count) / GRID_MAX_POINTS);
BedMeshScreen::drawMesh(INSET_POS(MESH_POS), ExtUI::getMeshArray(),
USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (gotAllPoints ? USE_COLORS : 0),
autoscale_max_amplitude * levelingProgress
);
}
}

bool BedMeshScreen::onTouchEnd(uint8_t tag) {
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 BedMeshBase::drawMeshBackground(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h) {
cmd.cmd(COLOR_RGB(Theme::bed_mesh_shadow_rgb));
_drawMesh(cmd, x, y, w, h, USE_POINTS | USE_TAGS, 0.1, 0, nullptr, nullptr);
}

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 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;

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);
cmd.cmd(COLOR_RGB(Theme::bed_mesh_lines_rgb));
_drawMesh(cmd, x, y, w, h,
USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (progress > 0.95 ? USE_COLORS : 0),
autoscale_max_amplitude * progress,
highlightedTag,
func, data
);
}

#endif // FTDI_BED_MESH_SCREEN
#endif // FTDI_BED_MESH_BASE
Loading

0 comments on commit 75b7903

Please sign in to comment.