Refactor Hilbert curve. Enhance Touch UI Bed Level Screen. (#21453)

This commit is contained in:
Marcio T 2021-03-27 21:57:12 -06:00 committed by GitHub
parent 5d0e6c21aa
commit c45b91aa94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 341 additions and 144 deletions

View file

@ -0,0 +1,112 @@
/*********************
* hilbert_curve.cpp *
*********************/
/****************************************************************************
* Written By Marcio Teixeira 2021 - SynDaver Labs, Inc. *
* *
* 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 "../../inc/MarlinConfig.h"
#if ENABLED(UBL_HILBERT_CURVE)
#include "bedlevel.h"
#include "hilbert_curve.h"
constexpr int8_t to_fix(int8_t v) { return v * 2; }
constexpr int8_t to_int(int8_t v) { return v / 2; }
constexpr uint8_t log2(uint8_t n) { return (n > 1) ? 1 + log2(n >> 1) : 0; }
constexpr uint8_t order(uint8_t n) { return uint8_t(log2(n - 1)) + 1; }
constexpr uint8_t ord = order(_MAX(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y));
constexpr uint8_t dim = _BV(ord);
static inline bool eval_candidate(int8_t x, int8_t y, hilbert_curve::callback_ptr func, void *data) {
// The print bed likely has fewer points than the full Hilbert
// curve, so cull unecessary points
return x < GRID_MAX_POINTS_X && y < GRID_MAX_POINTS_Y ? func(x, y, data) : false;
}
bool hilbert_curve::hilbert(int8_t x, int8_t y, int8_t xi, int8_t xj, int8_t yi, int8_t yj, uint8_t n, hilbert_curve::callback_ptr func, void *data) {
/**
* Hilbert space-filling curve implementation
*
* x and y : coordinates of the bottom left corner
* xi and xj : i and j components of the unit x vector of the frame
* yi and yj : i and j components of the unit y vector of the frame
*
* From: http://www.fundza.com/algorithmic/space_filling/hilbert/basics/index.html
*/
if (n)
return hilbert(x, y, yi/2, yj/2, xi/2, xj/2, n-1, func, data) ||
hilbert(x+xi/2, y+xj/2, xi/2, xj/2, yi/2, yj/2, n-1, func, data) ||
hilbert(x+xi/2+yi/2, y+xj/2+yj/2, xi/2, xj/2, yi/2, yj/2, n-1, func, data) ||
hilbert(x+xi/2+yi, y+xj/2+yj, -yi/2, -yj/2, -xi/2, -xj/2, n-1, func, data);
else
return eval_candidate(to_int(x+(xi+yi)/2), to_int(y+(xj+yj)/2), func, data);
}
/**
* Calls func(x, y, data) for all points in the Hilbert curve.
* If that function returns true, the search is terminated.
*/
bool hilbert_curve::search(hilbert_curve::callback_ptr func, void *data) {
return hilbert(to_fix(0), to_fix(0),to_fix(dim), to_fix(0), to_fix(0), to_fix(dim), ord, func, data);
}
/* Helper function for starting the search at a particular point */
typedef struct {
uint8_t x, y;
bool found_1st;
hilbert_curve::callback_ptr func;
void *data;
} search_from_t;
static bool search_from_helper(uint8_t x, uint8_t y, void *data) {
search_from_t *d = (search_from_t *) data;
if (d->x == x && d->y == y)
d->found_1st = true;
return d->found_1st ? d->func(x, y, d->data) : false;
}
/**
* Same as search, except start at a specific grid intersection point.
*/
bool hilbert_curve::search_from(uint8_t x, uint8_t y, hilbert_curve::callback_ptr func, void *data) {
search_from_t d;
d.x = x;
d.y = y;
d.found_1st = false;
d.func = func;
d.data = data;
// Call twice to allow search to wrap back to the beginning and picked up points prior to the start.
return search(search_from_helper, &d) || search(search_from_helper, &d);
}
/**
* Like search_from, but takes a bed position and starts from the nearest
* point on the Hilbert curve.
*/
bool hilbert_curve::search_from_closest(const xy_pos_t &pos, hilbert_curve::callback_ptr func, void *data) {
// Find closest grid intersection
uint8_t grid_x = LROUND(float(pos.x - MESH_MIN_X) / MESH_X_DIST);
uint8_t grid_y = LROUND(float(pos.y - MESH_MIN_Y) / MESH_Y_DIST);
LIMIT(grid_x, 0, GRID_MAX_POINTS_X);
LIMIT(grid_y, 0, GRID_MAX_POINTS_Y);
return search_from(grid_x, grid_y, func, data);
}
#endif // UBL_HILBERT_CURVE

View file

@ -0,0 +1,32 @@
/*******************
* hilbert_curve.h *
*******************/
/****************************************************************************
* Written By Marcio Teixeira 2021 - SynDaver Labs, Inc. *
* *
* 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
class hilbert_curve {
public:
typedef bool (*callback_ptr)(uint8_t x, uint8_t y, void *data);
static bool search(callback_ptr func, void *data);
static bool search_from(uint8_t x, uint8_t y, callback_ptr func, void *data);
static bool search_from_closest(const xy_pos_t &pos, callback_ptr func, void *data);
private:
static bool hilbert(int8_t x, int8_t y, int8_t xi, int8_t xj, int8_t yi, int8_t yj, uint8_t n, callback_ptr func, void *data);
};

View file

@ -101,11 +101,6 @@ public:
static void display_map(const int) _O0; static void display_map(const int) _O0;
static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0; static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0;
static mesh_index_pair find_furthest_invalid_mesh_point() _O0; static mesh_index_pair find_furthest_invalid_mesh_point() _O0;
#if ENABLED(UBL_HILBERT_CURVE)
static void check_if_missing(mesh_index_pair &pt, int x, int y);
static void hilbert(mesh_index_pair &pt, int8_t x, int8_t y, int8_t xi, int8_t xj, int8_t yi, int8_t yj, uint8_t n);
static mesh_index_pair find_next_mesh_point();
#endif
static void reset(); static void reset();
static void invalidate(); static void invalidate();
static void set_all_mesh_points_to_value(const float value); static void set_all_mesh_points_to_value(const float value);

View file

@ -49,6 +49,10 @@
#include "../../../lcd/extui/ui_api.h" #include "../../../lcd/extui/ui_api.h"
#endif #endif
#if ENABLED(UBL_HILBERT_CURVE)
#include "../hilbert_curve.h"
#endif
#include <math.h> #include <math.h>
#define UBL_G29_P31 #define UBL_G29_P31
@ -747,11 +751,9 @@ void unified_bed_leveling::shift_mesh_height() {
} }
#endif #endif
best = do_furthest ? find_furthest_invalid_mesh_point() best = do_furthest
: TERN(UBL_HILBERT_CURVE, ? find_furthest_invalid_mesh_point()
find_next_mesh_point(), : 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::PROBE_START));
@ -1269,97 +1271,93 @@ mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() {
return farthest; return farthest;
} }
mesh_index_pair unified_bed_leveling::find_closest_mesh_point_of_type(const MeshPointType type, const xy_pos_t &pos, const bool probe_relative/*=false*/, MeshFlags *done_flags/*=nullptr*/) { #if ENABLED(UBL_HILBERT_CURVE)
mesh_index_pair closest;
closest.invalidate();
closest.distance = -99999.9f;
// Get the reference position, either nozzle or probe typedef struct {
const xy_pos_t ref = probe_relative ? pos + probe.offset_xy : pos; MeshPointType type;
MeshFlags *done_flags;
bool probe_relative;
mesh_index_pair closest;
} find_closest_t;
float best_so_far = 99999.99f; static bool test_func(uint8_t i, uint8_t j, void *data) {
find_closest_t *d = (find_closest_t*)data;
GRID_LOOP(i, j) { if ( (d->type == (isnan(ubl.z_values[i][j]) ? INVALID : REAL))
if ( (type == (isnan(z_values[i][j]) ? INVALID : REAL)) || (d->type == SET_IN_BITMAP && !d->done_flags->marked(i, j))
|| (type == SET_IN_BITMAP && !done_flags->marked(i, j))
) { ) {
// Found a Mesh Point of the specified type! // Found a Mesh Point of the specified type!
const xy_pos_t mpos = { mesh_index_to_xpos(i), mesh_index_to_ypos(j) }; const xy_pos_t mpos = { ubl.mesh_index_to_xpos(i), ubl.mesh_index_to_ypos(j) };
// If using the probe as the reference there are some unreachable locations. // If using the probe as the reference there are some unreachable locations.
// Also for round beds, there are grid points outside the bed the nozzle can't reach. // Also for round beds, there are grid points outside the bed the nozzle can't reach.
// Prune them from the list and ignore them till the next Phase (manual nozzle probing). // Prune them from the list and ignore them till the next Phase (manual nozzle probing).
if (!(probe_relative ? probe.can_reach(mpos) : position_is_reachable(mpos))) if (!(d->probe_relative ? probe.can_reach(mpos) : position_is_reachable(mpos)))
continue; return false;
d->closest.pos.set(i, j);
// Reachable. Check if it's the best_so_far location to the nozzle. return true;
const xy_pos_t diff = current_position - mpos;
const float distance = (ref - mpos).magnitude() + diff.magnitude() * 0.1f;
// factor in the distance from the current location for the normal case
// so the nozzle isn't running all over the bed.
if (distance < best_so_far) {
best_so_far = distance; // Found a closer location with the desired value type.
closest.pos.set(i, j);
closest.distance = best_so_far;
}
}
} // GRID_LOOP
return closest;
}
#if ENABLED(UBL_HILBERT_CURVE)
constexpr int8_t to_fix(int8_t v) { return v << 1; }
constexpr int8_t to_int(int8_t v) { return v >> 1; }
constexpr uint8_t log2(uint8_t n) { return (n > 1) ? 1 + log2(n >> 1) : 0; }
constexpr uint8_t order(uint8_t n) { return uint8_t(log2(n - 1)) + 1; }
void unified_bed_leveling::hilbert(mesh_index_pair &pt, int8_t x, int8_t y, int8_t xi, int8_t xj, int8_t yi, int8_t yj, uint8_t n) {
/* Hilbert space filling curve implementation
*
* x and y are the coordinates of the bottom left corner
* xi & xj are the i & j components of the unit x vector of the frame
* similarly yi and yj
*
* From: http://www.fundza.com/algorithmic/space_filling/hilbert/basics/index.html
*/
if (n <= 0)
check_if_missing(pt, to_int(x+(xi+yi)/2),to_int(y+(xj+yj)/2));
else {
hilbert(pt, x, y, yi/2, yj/2, xi/2, xj/2, n-1);
hilbert(pt, x+xi/2, y+xj/2, xi/2, xj/2, yi/2, yj/2, n-1);
hilbert(pt, x+xi/2+yi/2, y+xj/2+yj/2, xi/2, xj/2, yi/2, yj/2, n-1);
hilbert(pt, x+xi/2+yi, y+xj/2+yj, -yi/2, -yj/2, -xi/2, -xj/2, n-1);
} }
return false;
} }
void unified_bed_leveling::check_if_missing(mesh_index_pair &pt, int x, int y) { #endif
if ( pt.distance < 0
&& x < GRID_MAX_POINTS_X mesh_index_pair unified_bed_leveling::find_closest_mesh_point_of_type(const MeshPointType type, const xy_pos_t &pos, const bool probe_relative/*=false*/, MeshFlags *done_flags/*=nullptr*/) {
&& y < GRID_MAX_POINTS_Y
&& isnan(z_values[x][y]) #if ENABLED(UBL_HILBERT_CURVE)
&& probe.can_reach(mesh_index_to_xpos(x), mesh_index_to_ypos(y))
find_closest_t d;
d.type = type;
d.done_flags = done_flags;
d.probe_relative = probe_relative;
d.closest.invalidate();
hilbert_curve::search_from_closest(pos, test_func, &d);
return d.closest;
#else
mesh_index_pair closest;
closest.invalidate();
closest.distance = -99999.9f;
// Get the reference position, either nozzle or probe
const xy_pos_t ref = probe_relative ? pos + probe.offset_xy : pos;
float best_so_far = 99999.99f;
GRID_LOOP(i, j) {
if ( (type == (isnan(z_values[i][j]) ? INVALID : REAL))
|| (type == SET_IN_BITMAP && !done_flags->marked(i, j))
) { ) {
pt.pos.set(x, y); // Found a Mesh Point of the specified type!
pt.distance = 1; const xy_pos_t mpos = { mesh_index_to_xpos(i), mesh_index_to_ypos(j) };
// If using the probe as the reference there are some unreachable locations.
// Also for round beds, there are grid points outside the bed the nozzle can't reach.
// Prune them from the list and ignore them till the next Phase (manual nozzle probing).
if (!(probe_relative ? probe.can_reach(mpos) : position_is_reachable(mpos)))
continue;
// Reachable. Check if it's the best_so_far location to the nozzle.
const xy_pos_t diff = current_position - mpos;
const float distance = (ref - mpos).magnitude() + diff.magnitude() * 0.1f;
// factor in the distance from the current location for the normal case
// so the nozzle isn't running all over the bed.
if (distance < best_so_far) {
best_so_far = distance; // Found a closer location with the desired value type.
closest.pos.set(i, j);
closest.distance = best_so_far;
}
} }
} } // GRID_LOOP
mesh_index_pair unified_bed_leveling::find_next_mesh_point() { return closest;
mesh_index_pair pt;
pt.invalidate();
pt.distance = -99999.9f;
constexpr uint8_t ord = order(_MAX(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y));
constexpr uint8_t dim = _BV(ord);
hilbert(pt, to_fix(0), to_fix(0), to_fix(dim), to_fix(0), to_fix(0), to_fix(dim), ord);
return pt;
}
#endif // UBL_HILBERT_CURVE #endif
}
/** /**
* 'Smart Fill': Scan from the outward edges of the mesh towards the center. * 'Smart Fill': Scan from the outward edges of the mesh towards the center.

View file

@ -30,25 +30,28 @@
#define INC_POS SUB_POS(6,1), SUB_SIZE(2,1) #define INC_POS SUB_POS(6,1), SUB_SIZE(2,1)
#define DEC_POS SUB_POS(8,1), SUB_SIZE(2,1) #define DEC_POS SUB_POS(8,1), SUB_SIZE(2,1)
void draw_adjuster(CommandProcessor& cmd, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t tag, float value, progmem_str units, int8_t width, uint8_t precision, draw_mode_t what) { void draw_adjuster_value(CommandProcessor& cmd, int16_t x, int16_t y, int16_t w, int16_t h, float value, progmem_str units, int8_t width, uint8_t precision) {
if (what & BACKGROUND) char str[width + precision + 10 + (units ? strlen_P((const char*) units) : 0)];
cmd.tag(0).button(VAL_POS, F(""), FTDI::OPT_FLAT); if (isnan(value))
strcpy_P(str, PSTR("-"));
else
dtostrf(value, width, precision, str);
if (what & FOREGROUND) { if (units) {
char str[width + precision + 10 + (units ? strlen_P((const char*) units) : 0)]; strcat_P(str, PSTR(" "));
if (isnan(value)) strcat_P(str, (const char*) units);
strcpy_P(str, PSTR("-")); }
else
dtostrf(value, width, precision, str);
if (units) { cmd.text(VAL_POS, str);
strcat_P(str, PSTR(" ")); }
strcat_P(str, (const char*) units);
} void draw_adjuster(CommandProcessor& cmd, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t tag, float value, progmem_str units, int8_t width, uint8_t precision, draw_mode_t what) {
if (what & BACKGROUND)
cmd.tag(0) cmd.tag(0).button(VAL_POS, F(""), FTDI::OPT_FLAT);
.text(VAL_POS, str)
.tag(tag ).button(INC_POS, F("-")) if (what & FOREGROUND) {
.tag(tag+1).button(DEC_POS, F("+")); draw_adjuster_value(cmd, x, y, w, h, value, units, width, precision);
} cmd.tag(tag ).button(INC_POS, F("-"))
.tag(tag+1).button(DEC_POS, F("+"));
}
} }

View file

@ -22,6 +22,13 @@
#pragma once #pragma once
#include "../extended/screen_types.h" #include "../extended/screen_types.h"
void draw_adjuster_value(
CommandProcessor& cmd,
int16_t x, int16_t y, int16_t w, int16_t h,
float value, progmem_str units = nullptr,
int8_t width = 5, uint8_t precision = 1
);
void draw_adjuster( void draw_adjuster(
CommandProcessor& cmd, CommandProcessor& cmd,
int16_t x, int16_t y, int16_t w, int16_t h, int16_t x, int16_t y, int16_t w, int16_t h,

View file

@ -140,10 +140,13 @@ namespace Language_en {
PROGMEM Language_Str MSG_TOUCH_CALIBRATION_START = u8"Release to begin screen calibration"; PROGMEM Language_Str MSG_TOUCH_CALIBRATION_START = u8"Release to begin screen calibration";
PROGMEM Language_Str MSG_TOUCH_CALIBRATION_PROMPT = u8"Touch the dots to calibrate"; PROGMEM Language_Str MSG_TOUCH_CALIBRATION_PROMPT = u8"Touch the dots to calibrate";
PROGMEM Language_Str MSG_AUTOLEVEL_X_AXIS = u8"Level X Axis";
PROGMEM Language_Str MSG_BED_MAPPING_DONE = u8"Bed mapping finished"; PROGMEM Language_Str MSG_BED_MAPPING_DONE = u8"Bed mapping finished";
PROGMEM Language_Str MSG_BED_MAPPING_INCOMPLETE = u8"Not all points probed"; PROGMEM Language_Str MSG_BED_MAPPING_INCOMPLETE = u8"Not all points probed";
PROGMEM Language_Str MSG_LEVELING = u8"Leveling"; PROGMEM Language_Str MSG_LEVELING = u8"Leveling";
PROGMEM Language_Str MSG_AXIS_LEVELING = u8"Axis Leveling";
PROGMEM Language_Str MSG_PROBE_BED = u8"Probe Mesh";
PROGMEM Language_Str MSG_SHOW_MESH = u8"View Mesh";
PROGMEM Language_Str MSG_PRINT_TEST = u8"Print Test";
#if ENABLED(TOUCH_UI_LULZBOT_BIO) #if ENABLED(TOUCH_UI_LULZBOT_BIO)
PROGMEM Language_Str MSG_MOVE_TO_HOME = u8"Move to Home"; PROGMEM Language_Str MSG_MOVE_TO_HOME = u8"Move to Home";

View file

@ -225,6 +225,7 @@ bool BedMeshScreen::tagToPoint(uint8_t tag, xy_uint8_t &pt) {
} }
void BedMeshScreen::onEntry() { void BedMeshScreen::onEntry() {
mydata.allowEditing = true;
mydata.highlightedTag = 0; mydata.highlightedTag = 0;
mydata.zAdjustment = 0; mydata.zAdjustment = 0;
mydata.count = GRID_MAX_POINTS; mydata.count = GRID_MAX_POINTS;
@ -259,16 +260,16 @@ void BedMeshScreen::adjustHighlightedValue(float increment) {
} }
void BedMeshScreen::saveAdjustedHighlightedValue() { void BedMeshScreen::saveAdjustedHighlightedValue() {
if(mydata.zAdjustment) { if (mydata.zAdjustment) {
BedMeshScreen::setHighlightedValue(BedMeshScreen::getHighlightedValue(true) + mydata.zAdjustment); BedMeshScreen::setHighlightedValue(BedMeshScreen::getHighlightedValue(true) + mydata.zAdjustment);
mydata.zAdjustment = 0; mydata.zAdjustment = 0;
} }
} }
void BedMeshScreen::changeHighlightedValue(uint8_t tag) { void BedMeshScreen::changeHighlightedValue(uint8_t tag) {
saveAdjustedHighlightedValue(); if (mydata.allowEditing) saveAdjustedHighlightedValue();
mydata.highlightedTag = tag; mydata.highlightedTag = tag;
moveToHighlightedValue(); if (mydata.allowEditing) moveToHighlightedValue();
} }
void BedMeshScreen::drawHighlightedPointValue() { void BedMeshScreen::drawHighlightedPointValue() {
@ -277,7 +278,12 @@ void BedMeshScreen::drawHighlightedPointValue() {
.colors(normal_btn) .colors(normal_btn)
.text(Z_LABEL_POS, GET_TEXT_F(MSG_MESH_EDIT_Z)) .text(Z_LABEL_POS, GET_TEXT_F(MSG_MESH_EDIT_Z))
.font(font_small); .font(font_small);
draw_adjuster(cmd, Z_VALUE_POS, 2, getHighlightedValue(true) + mydata.zAdjustment, GET_TEXT_F(MSG_UNITS_MM), 4, 3);
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) cmd.colors(action_btn)
.tag(1).button(OKAY_POS, GET_TEXT_F(MSG_BUTTON_OKAY)) .tag(1).button(OKAY_POS, GET_TEXT_F(MSG_BUTTON_OKAY))
.tag(0); .tag(0);
@ -347,6 +353,7 @@ void BedMeshScreen::onMeshUpdate(const int8_t, const int8_t, const float) {
void BedMeshScreen::onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) { void BedMeshScreen::onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) {
switch (state) { switch (state) {
case ExtUI::MESH_START: case ExtUI::MESH_START:
mydata.allowEditing = false;
mydata.count = 0; mydata.count = 0;
mydata.message = mydata.MSG_NONE; mydata.message = mydata.MSG_NONE;
break; break;
@ -369,10 +376,16 @@ void BedMeshScreen::onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::pr
void BedMeshScreen::startMeshProbe() { void BedMeshScreen::startMeshProbe() {
GOTO_SCREEN(BedMeshScreen); GOTO_SCREEN(BedMeshScreen);
mydata.allowEditing = false;
mydata.count = 0; mydata.count = 0;
injectCommands_P(PSTR(BED_LEVELING_COMMANDS)); injectCommands_P(PSTR(BED_LEVELING_COMMANDS));
} }
void BedMeshScreen::showMesh() {
GOTO_SCREEN(BedMeshScreen);
mydata.allowEditing = false;
}
void BedMeshScreen::showMeshEditor() { void BedMeshScreen::showMeshEditor() {
SpinnerDialogBox::enqueueAndWait_P(ExtUI::isMachineHomed() ? F("M420 S1") : F("G28\nM420 S1")); SpinnerDialogBox::enqueueAndWait_P(ExtUI::isMachineHomed() ? F("M420 S1") : F("G28\nM420 S1"));
// After the spinner, go to this screen. // After the spinner, go to this screen.

View file

@ -33,6 +33,7 @@ struct BedMeshScreenData {
uint8_t count; uint8_t count;
uint8_t highlightedTag; uint8_t highlightedTag;
float zAdjustment; float zAdjustment;
bool allowEditing;
}; };
class BedMeshScreen : public BaseScreen, public CachedScreen<BED_MESH_SCREEN_CACHE> { class BedMeshScreen : public BaseScreen, public CachedScreen<BED_MESH_SCREEN_CACHE> {
@ -63,5 +64,6 @@ class BedMeshScreen : public BaseScreen, public CachedScreen<BED_MESH_SCREEN_CAC
static bool onTouchEnd(uint8_t tag); static bool onTouchEnd(uint8_t tag);
static void startMeshProbe(); static void startMeshProbe();
static void showMesh();
static void showMeshEditor(); static void showMeshEditor();
}; };

View file

@ -34,27 +34,33 @@ using namespace ExtUI;
using namespace Theme; using namespace Theme;
#if ENABLED(TOUCH_UI_PORTRAIT) #if ENABLED(TOUCH_UI_PORTRAIT)
#define GRID_ROWS 9 #define GRID_ROWS 8
#define GRID_COLS 2 #define GRID_COLS 2
#define TITLE_POS BTN_POS(1,1), BTN_SIZE(2,1) #define LEVELING_TITLE_POS BTN_POS(1,1), BTN_SIZE(2,1)
#define LEVEL_AXIS_POS BTN_POS(1,2), BTN_SIZE(2,1) #define LEVEL_AXIS_POS BTN_POS(1,2), BTN_SIZE(2,1)
#define LEVEL_BED_POS BTN_POS(1,3), BTN_SIZE(2,1) #define BED_MESH_TITLE_POS BTN_POS(1,3), BTN_SIZE(2,1)
#define EDIT_MESH_POS BTN_POS(1,4), BTN_SIZE(2,1) #define PROBE_BED_POS BTN_POS(1,4), BTN_SIZE(1,1)
#define TEST_MESH_POS BTN_POS(2,4), BTN_SIZE(1,1)
#define SHOW_MESH_POS BTN_POS(1,5), BTN_SIZE(1,1)
#define EDIT_MESH_POS BTN_POS(2,5), BTN_SIZE(1,1)
#define BLTOUCH_TITLE_POS BTN_POS(1,6), BTN_SIZE(2,1) #define BLTOUCH_TITLE_POS BTN_POS(1,6), BTN_SIZE(2,1)
#define BLTOUCH_RESET_POS BTN_POS(1,7), BTN_SIZE(1,1) #define BLTOUCH_RESET_POS BTN_POS(1,7), BTN_SIZE(1,1)
#define BLTOUCH_TEST_POS BTN_POS(2,7), BTN_SIZE(1,1) #define BLTOUCH_TEST_POS BTN_POS(2,7), BTN_SIZE(1,1)
#define BACK_POS BTN_POS(1,9), BTN_SIZE(2,1) #define BACK_POS BTN_POS(1,8), BTN_SIZE(2,1)
#else #else
#define GRID_ROWS 7 #define GRID_ROWS 6
#define GRID_COLS 2 #define GRID_COLS 3
#define TITLE_POS BTN_POS(1,1), BTN_SIZE(2,1) #define LEVELING_TITLE_POS BTN_POS(1,1), BTN_SIZE(3,1)
#define LEVEL_AXIS_POS BTN_POS(1,2), BTN_SIZE(2,1) #define LEVEL_AXIS_POS BTN_POS(1,2), BTN_SIZE(3,1)
#define LEVEL_BED_POS BTN_POS(1,3), BTN_SIZE(2,1) #define BED_MESH_TITLE_POS BTN_POS(1,3), BTN_SIZE(2,1)
#define EDIT_MESH_POS BTN_POS(1,4), BTN_SIZE(2,1) #define PROBE_BED_POS BTN_POS(1,4), BTN_SIZE(1,1)
#define BLTOUCH_TITLE_POS BTN_POS(1,5), BTN_SIZE(2,1) #define TEST_MESH_POS BTN_POS(2,4), BTN_SIZE(1,1)
#define BLTOUCH_RESET_POS BTN_POS(1,6), BTN_SIZE(1,1) #define SHOW_MESH_POS BTN_POS(1,5), BTN_SIZE(1,1)
#define BLTOUCH_TEST_POS BTN_POS(2,6), BTN_SIZE(1,1) #define EDIT_MESH_POS BTN_POS(2,5), BTN_SIZE(1,1)
#define BACK_POS BTN_POS(1,7), BTN_SIZE(2,1) #define BLTOUCH_TITLE_POS BTN_POS(3,3), BTN_SIZE(1,1)
#define BLTOUCH_RESET_POS BTN_POS(3,4), BTN_SIZE(1,1)
#define BLTOUCH_TEST_POS BTN_POS(3,5), BTN_SIZE(1,1)
#define BACK_POS BTN_POS(1,6), BTN_SIZE(3,1)
#endif #endif
void LevelingMenu::onRedraw(draw_mode_t what) { void LevelingMenu::onRedraw(draw_mode_t what) {
@ -69,20 +75,25 @@ void LevelingMenu::onRedraw(draw_mode_t what) {
CommandProcessor cmd; CommandProcessor cmd;
cmd.font(font_large) cmd.font(font_large)
.cmd(COLOR_RGB(bg_text_enabled)) .cmd(COLOR_RGB(bg_text_enabled))
.text(TITLE_POS, GET_TEXT_F(MSG_LEVELING)) .text(LEVELING_TITLE_POS, GET_TEXT_F(MSG_AXIS_LEVELING))
.text(BED_MESH_TITLE_POS, GET_TEXT_F(MSG_BED_LEVELING))
#if ENABLED(BLTOUCH) #if ENABLED(BLTOUCH)
.text(BLTOUCH_TITLE_POS, GET_TEXT_F(MSG_BLTOUCH)) .text(BLTOUCH_TITLE_POS, GET_TEXT_F(MSG_BLTOUCH))
#endif #endif
.font(font_medium).colors(normal_btn) .font(font_medium).colors(normal_btn)
#if EITHER(Z_STEPPER_AUTO_ALIGN,MECHANICAL_GANTRY_CALIBRATION) #if EITHER(Z_STEPPER_AUTO_ALIGN,MECHANICAL_GANTRY_CALIBRATION)
.tag(2).button(LEVEL_AXIS_POS, GET_TEXT_F(MSG_AUTOLEVEL_X_AXIS)) .tag(2).button(LEVEL_AXIS_POS, GET_TEXT_F(MSG_LEVEL_X_AXIS))
#endif #endif
.tag(3).button(LEVEL_BED_POS, GET_TEXT_F(MSG_LEVEL_BED)) .tag(3).button(PROBE_BED_POS, GET_TEXT_F(MSG_PROBE_BED))
.enabled(ENABLED(HAS_MESH)) .enabled(ENABLED(HAS_MESH))
.tag(4).button(EDIT_MESH_POS, GET_TEXT_F(MSG_EDIT_MESH)) .tag(4).button(SHOW_MESH_POS, GET_TEXT_F(MSG_SHOW_MESH))
.enabled(ENABLED(HAS_MESH))
.tag(5).button(EDIT_MESH_POS, GET_TEXT_F(MSG_EDIT_MESH))
.enabled(ENABLED(G26_MESH_VALIDATION))
.tag(6).button(TEST_MESH_POS, GET_TEXT_F(MSG_PRINT_TEST))
#if ENABLED(BLTOUCH) #if ENABLED(BLTOUCH)
.tag(5).button(BLTOUCH_RESET_POS, GET_TEXT_F(MSG_BLTOUCH_RESET)) .tag(7).button(BLTOUCH_RESET_POS, GET_TEXT_F(MSG_BLTOUCH_RESET))
.tag(6).button(BLTOUCH_TEST_POS, GET_TEXT_F(MSG_BLTOUCH_SELFTEST)) .tag(8).button(BLTOUCH_TEST_POS, GET_TEXT_F(MSG_BLTOUCH_SELFTEST))
#endif #endif
.colors(action_btn) .colors(action_btn)
.tag(1).button(BACK_POS, GET_TEXT_F(MSG_BACK)); .tag(1).button(BACK_POS, GET_TEXT_F(MSG_BACK));
@ -106,11 +117,18 @@ bool LevelingMenu::onTouchEnd(uint8_t tag) {
#endif #endif
break; break;
#if ENABLED(AUTO_BED_LEVELING_UBL) #if ENABLED(AUTO_BED_LEVELING_UBL)
case 4: BedMeshScreen::showMeshEditor(); break; case 4: BedMeshScreen::showMesh(); break;
case 5: BedMeshScreen::showMeshEditor(); break;
#endif
#if ENABLED(G26_MESH_VALIDATION)
case 6:
GOTO_SCREEN(StatusScreen);
injectCommands_P(PSTR("M117 Printing Test Pattern\nG28 O\nG26 R"));
break;
#endif #endif
#if ENABLED(BLTOUCH) #if ENABLED(BLTOUCH)
case 5: injectCommands_P(PSTR("M280 P0 S60")); break; case 7: injectCommands_P(PSTR("M280 P0 S60")); break;
case 6: SpinnerDialogBox::enqueueAndWait_P(F("M280 P0 S90\nG4 P100\nM280 P0 S120")); break; case 8: SpinnerDialogBox::enqueueAndWait_P(F("M280 P0 S90\nG4 P100\nM280 P0 S120")); break;
#endif #endif
default: return false; default: return false;
} }
@ -118,3 +136,4 @@ bool LevelingMenu::onTouchEnd(uint8_t tag) {
} }
#endif // FTDI_LEVELING_MENU #endif // FTDI_LEVELING_MENU

View file

@ -62,7 +62,7 @@ void TuneMenu::onRedraw(draw_mode_t what) {
#define FILAMENT_POS BTN_POS(1,4), BTN_SIZE(1,1) #define FILAMENT_POS BTN_POS(1,4), BTN_SIZE(1,1)
#define CASE_LIGHT_POS BTN_POS(2,4), BTN_SIZE(1,1) #define CASE_LIGHT_POS BTN_POS(2,4), BTN_SIZE(1,1)
#define ADVANCED_SETTINGS_POS BTN_POS(1,5), BTN_SIZE(1,1) #define ADVANCED_SETTINGS_POS BTN_POS(1,5), BTN_SIZE(1,1)
#define BACK_POS BTN_POS(2,5), BTN_SIZE(2,1) #define BACK_POS BTN_POS(2,5), BTN_SIZE(1,1)
#endif #endif
if (what & FOREGROUND) { if (what & FOREGROUND) {

View file

@ -849,10 +849,13 @@ namespace ExtUI {
} }
#if HAS_LEVELING #if HAS_LEVELING
bool getLevelingActive() { return planner.leveling_active; } bool getLevelingActive() { return planner.leveling_active; }
void setLevelingActive(const bool state) { set_bed_leveling_enabled(state); } void setLevelingActive(const bool state) { set_bed_leveling_enabled(state); }
bool getMeshValid() { return leveling_is_valid(); } bool getMeshValid() { return leveling_is_valid(); }
#if HAS_MESH #if HAS_MESH
bed_mesh_t& getMeshArray() { return Z_VALUES_ARR; } bed_mesh_t& getMeshArray() { return Z_VALUES_ARR; }
float getMeshPoint(const xy_uint8_t &pos) { return Z_VALUES(pos.x, pos.y); } float getMeshPoint(const xy_uint8_t &pos) { return Z_VALUES(pos.x, pos.y); }
void setMeshPoint(const xy_uint8_t &pos, const float &zoff) { void setMeshPoint(const xy_uint8_t &pos, const float &zoff) {
@ -861,17 +864,23 @@ namespace ExtUI {
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate()); TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());
} }
} }
void moveToMeshPoint(const xy_uint8_t &pos, const float &z) { void moveToMeshPoint(const xy_uint8_t &pos, const float &z) {
#if EITHER(MESH_BED_LEVELING, AUTO_BED_LEVELING_UBL) #if EITHER(MESH_BED_LEVELING, AUTO_BED_LEVELING_UBL)
const feedRate_t old_feedrate = feedrate_mm_s; const feedRate_t old_feedrate = feedrate_mm_s;
feedrate_mm_s = Z_PROBE_FEEDRATE_FAST; const float x_target = MESH_MIN_X + pos.x * (MESH_X_DIST),
destination = current_position; y_target = MESH_MIN_Y + pos.y * (MESH_Y_DIST);
destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES; if (x_target != current_position[X_AXIS] || y_target != current_position[Y_AXIS]) {
prepare_line_to_destination(); // If moving across bed, raise nozzle to safe height over bed
feedrate_mm_s = XY_PROBE_FEEDRATE; feedrate_mm_s = Z_PROBE_FEEDRATE_FAST;
destination[X_AXIS] = MESH_MIN_X + pos.x * MESH_X_DIST; destination = current_position;
destination[Y_AXIS] = MESH_MIN_Y + pos.y * MESH_Y_DIST; destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;
prepare_line_to_destination(); prepare_line_to_destination();
feedrate_mm_s = XY_PROBE_FEEDRATE;
destination[X_AXIS] = x_target;
destination[Y_AXIS] = y_target;
prepare_line_to_destination();
}
feedrate_mm_s = Z_PROBE_FEEDRATE_FAST; feedrate_mm_s = Z_PROBE_FEEDRATE_FAST;
destination[Z_AXIS] = z; destination[Z_AXIS] = z;
prepare_line_to_destination(); prepare_line_to_destination();
@ -881,8 +890,10 @@ namespace ExtUI {
UNUSED(z); UNUSED(z);
#endif #endif
} }
#endif
#endif #endif // HAS_MESH
#endif // HAS_LEVELING
#if ENABLED(HOST_PROMPT_SUPPORT) #if ENABLED(HOST_PROMPT_SUPPORT)
void setHostResponse(const uint8_t response) { host_response_handler(response); } void setHostResponse(const uint8_t response) { host_response_handler(response); }

View file

@ -75,6 +75,7 @@ default_src_filter = +<src/*> -<src/config> -<src/HAL> +<src/HAL/shared>
-<src/feature/bedlevel/abl> -<src/gcode/bedlevel/abl> -<src/feature/bedlevel/abl> -<src/gcode/bedlevel/abl>
-<src/feature/bedlevel/mbl> -<src/gcode/bedlevel/mbl> -<src/feature/bedlevel/mbl> -<src/gcode/bedlevel/mbl>
-<src/feature/bedlevel/ubl> -<src/gcode/bedlevel/ubl> -<src/feature/bedlevel/ubl> -<src/gcode/bedlevel/ubl>
-<src/feature/bedlevel/hilbert_curve.cpp>
-<src/feature/binary_stream.cpp> -<src/libs/heatshrink> -<src/feature/binary_stream.cpp> -<src/libs/heatshrink>
-<src/feature/bltouch.cpp> -<src/feature/bltouch.cpp>
-<src/feature/cancel_object.cpp> -<src/gcode/feature/cancel> -<src/feature/cancel_object.cpp> -<src/gcode/feature/cancel>
@ -306,6 +307,7 @@ AUTO_BED_LEVELING_BILINEAR = src_filter=+<src/feature/bedlevel/abl>
AUTO_BED_LEVELING_(3POINT|(BI)?LINEAR) = src_filter=+<src/gcode/bedlevel/abl> AUTO_BED_LEVELING_(3POINT|(BI)?LINEAR) = src_filter=+<src/gcode/bedlevel/abl>
MESH_BED_LEVELING = src_filter=+<src/feature/bedlevel/mbl> +<src/gcode/bedlevel/mbl> MESH_BED_LEVELING = src_filter=+<src/feature/bedlevel/mbl> +<src/gcode/bedlevel/mbl>
AUTO_BED_LEVELING_UBL = src_filter=+<src/feature/bedlevel/ubl> +<src/gcode/bedlevel/ubl> AUTO_BED_LEVELING_UBL = src_filter=+<src/feature/bedlevel/ubl> +<src/gcode/bedlevel/ubl>
UBL_HILBERT_CURVE = src_filter=+<src/feature/bedlevel/hilbert_curve.cpp>
BACKLASH_COMPENSATION = src_filter=+<src/feature/backlash.cpp> BACKLASH_COMPENSATION = src_filter=+<src/feature/backlash.cpp>
BARICUDA = src_filter=+<src/feature/baricuda.cpp> +<src/gcode/feature/baricuda> BARICUDA = src_filter=+<src/feature/baricuda.cpp> +<src/gcode/feature/baricuda>
BINARY_FILE_TRANSFER = src_filter=+<src/feature/binary_stream.cpp> +<src/libs/heatshrink> BINARY_FILE_TRANSFER = src_filter=+<src/feature/binary_stream.cpp> +<src/libs/heatshrink>