G26 Hilbert Curve followup (#21480)

This commit is contained in:
Marcio T 2021-04-01 18:12:00 -06:00 committed by Scott Lahteine
parent b1be96e40e
commit 82e6a2ed62
24 changed files with 442 additions and 444 deletions

View file

@ -34,7 +34,7 @@ void safe_delay(millis_t ms);
inline void serial_delay(const millis_t) {} inline void serial_delay(const millis_t) {}
#endif #endif
#if GRID_MAX_POINTS_X && GRID_MAX_POINTS_Y #if (GRID_MAX_POINTS_X) && (GRID_MAX_POINTS_Y)
// 16x16 bit arrays // 16x16 bit arrays
template <int W, int H> template <int W, int H>

View file

@ -85,9 +85,9 @@ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t
//#define EXTRAPOLATE_FROM_EDGE //#define EXTRAPOLATE_FROM_EDGE
#if ENABLED(EXTRAPOLATE_FROM_EDGE) #if ENABLED(EXTRAPOLATE_FROM_EDGE)
#if GRID_MAX_POINTS_X < GRID_MAX_POINTS_Y #if (GRID_MAX_POINTS_X) < (GRID_MAX_POINTS_Y)
#define HALF_IN_X #define HALF_IN_X
#elif GRID_MAX_POINTS_Y < GRID_MAX_POINTS_X #elif (GRID_MAX_POINTS_Y) < (GRID_MAX_POINTS_X)
#define HALF_IN_Y #define HALF_IN_Y
#endif #endif
#endif #endif
@ -98,23 +98,23 @@ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t
*/ */
void extrapolate_unprobed_bed_level() { void extrapolate_unprobed_bed_level() {
#ifdef HALF_IN_X #ifdef HALF_IN_X
constexpr uint8_t ctrx2 = 0, xlen = GRID_MAX_POINTS_X - 1; constexpr uint8_t ctrx2 = 0, xend = GRID_MAX_POINTS_X - 1;
#else #else
constexpr uint8_t ctrx1 = (GRID_MAX_POINTS_X - 1) / 2, // left-of-center constexpr uint8_t ctrx1 = (GRID_MAX_CELLS_X) / 2, // left-of-center
ctrx2 = (GRID_MAX_POINTS_X) / 2, // right-of-center ctrx2 = (GRID_MAX_POINTS_X) / 2, // right-of-center
xlen = ctrx1; xend = ctrx1;
#endif #endif
#ifdef HALF_IN_Y #ifdef HALF_IN_Y
constexpr uint8_t ctry2 = 0, ylen = GRID_MAX_POINTS_Y - 1; constexpr uint8_t ctry2 = 0, yend = GRID_MAX_POINTS_Y - 1;
#else #else
constexpr uint8_t ctry1 = (GRID_MAX_POINTS_Y - 1) / 2, // top-of-center constexpr uint8_t ctry1 = (GRID_MAX_CELLS_Y) / 2, // top-of-center
ctry2 = (GRID_MAX_POINTS_Y) / 2, // bottom-of-center ctry2 = (GRID_MAX_POINTS_Y) / 2, // bottom-of-center
ylen = ctry1; yend = ctry1;
#endif #endif
LOOP_LE_N(xo, xlen) LOOP_LE_N(xo, xend)
LOOP_LE_N(yo, ylen) { LOOP_LE_N(yo, yend) {
uint8_t x2 = ctrx2 + xo, y2 = ctry2 + yo; uint8_t x2 = ctrx2 + xo, y2 = ctry2 + yo;
#ifndef HALF_IN_X #ifndef HALF_IN_X
const uint8_t x1 = ctrx1 - xo; const uint8_t x1 = ctrx1 - xo;
@ -143,8 +143,8 @@ void print_bilinear_leveling_grid() {
#if ENABLED(ABL_BILINEAR_SUBDIVISION) #if ENABLED(ABL_BILINEAR_SUBDIVISION)
#define ABL_GRID_POINTS_VIRT_X (GRID_MAX_POINTS_X - 1) * (BILINEAR_SUBDIVISIONS) + 1 #define ABL_GRID_POINTS_VIRT_X GRID_MAX_CELLS_X * (BILINEAR_SUBDIVISIONS) + 1
#define ABL_GRID_POINTS_VIRT_Y (GRID_MAX_POINTS_Y - 1) * (BILINEAR_SUBDIVISIONS) + 1 #define ABL_GRID_POINTS_VIRT_Y GRID_MAX_CELLS_Y * (BILINEAR_SUBDIVISIONS) + 1
#define ABL_TEMP_POINTS_X (GRID_MAX_POINTS_X + 2) #define ABL_TEMP_POINTS_X (GRID_MAX_POINTS_X + 2)
#define ABL_TEMP_POINTS_Y (GRID_MAX_POINTS_Y + 2) #define ABL_TEMP_POINTS_Y (GRID_MAX_POINTS_Y + 2)
float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y]; float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y];
@ -161,7 +161,7 @@ void print_bilinear_leveling_grid() {
#define LINEAR_EXTRAPOLATION(E, I) ((E) * 2 - (I)) #define LINEAR_EXTRAPOLATION(E, I) ((E) * 2 - (I))
float bed_level_virt_coord(const uint8_t x, const uint8_t y) { float bed_level_virt_coord(const uint8_t x, const uint8_t y) {
uint8_t ep = 0, ip = 1; uint8_t ep = 0, ip = 1;
if (x > GRID_MAX_POINTS_X + 1 || y > GRID_MAX_POINTS_Y + 1) { if (x > (GRID_MAX_POINTS_X) + 1 || y > (GRID_MAX_POINTS_Y) + 1) {
// The requested point requires extrapolating two points beyond the mesh. // The requested point requires extrapolating two points beyond the mesh.
// These values are only requested for the edges of the mesh, which are always an actual mesh point, // These values are only requested for the edges of the mesh, which are always an actual mesh point,
// and do not require interpolation. When interpolation is not needed, this "Mesh + 2" point is // and do not require interpolation. When interpolation is not needed, this "Mesh + 2" point is
@ -171,8 +171,8 @@ void print_bilinear_leveling_grid() {
} }
if (!x || x == ABL_TEMP_POINTS_X - 1) { if (!x || x == ABL_TEMP_POINTS_X - 1) {
if (x) { if (x) {
ep = GRID_MAX_POINTS_X - 1; ep = (GRID_MAX_POINTS_X) - 1;
ip = GRID_MAX_POINTS_X - 2; ip = GRID_MAX_CELLS_X - 1;
} }
if (WITHIN(y, 1, ABL_TEMP_POINTS_Y - 2)) if (WITHIN(y, 1, ABL_TEMP_POINTS_Y - 2))
return LINEAR_EXTRAPOLATION( return LINEAR_EXTRAPOLATION(
@ -187,8 +187,8 @@ void print_bilinear_leveling_grid() {
} }
if (!y || y == ABL_TEMP_POINTS_Y - 1) { if (!y || y == ABL_TEMP_POINTS_Y - 1) {
if (y) { if (y) {
ep = GRID_MAX_POINTS_Y - 1; ep = (GRID_MAX_POINTS_Y) - 1;
ip = GRID_MAX_POINTS_Y - 2; ip = GRID_MAX_CELLS_Y - 1;
} }
if (WITHIN(x, 1, ABL_TEMP_POINTS_X - 2)) if (WITHIN(x, 1, ABL_TEMP_POINTS_X - 2))
return LINEAR_EXTRAPOLATION( return LINEAR_EXTRAPOLATION(

View file

@ -36,7 +36,7 @@ constexpr uint8_t dim = _BV(ord);
static inline bool eval_candidate(int8_t x, int8_t y, hilbert_curve::callback_ptr func, void *data) { 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 // The print bed likely has fewer points than the full Hilbert
// curve, so cull unecessary points // curve, so cull unecessary points
return x < GRID_MAX_POINTS_X && y < GRID_MAX_POINTS_Y ? func(x, y, data) : false; 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) { 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) {
@ -102,10 +102,8 @@ bool hilbert_curve::search_from(uint8_t x, uint8_t y, hilbert_curve::callback_pt
*/ */
bool hilbert_curve::search_from_closest(const xy_pos_t &pos, hilbert_curve::callback_ptr func, void *data) { bool hilbert_curve::search_from_closest(const xy_pos_t &pos, hilbert_curve::callback_ptr func, void *data) {
// Find closest grid intersection // Find closest grid intersection
uint8_t grid_x = LROUND(float(pos.x - MESH_MIN_X) / MESH_X_DIST); const uint8_t grid_x = LROUND(constrain(float(pos.x - (MESH_MIN_X)) / (MESH_X_DIST), 0, (GRID_MAX_POINTS_X) - 1));
uint8_t grid_y = LROUND(float(pos.y - MESH_MIN_Y) / MESH_Y_DIST); const uint8_t grid_y = LROUND(constrain(float(pos.y - (MESH_MIN_Y)) / (MESH_Y_DIST), 0, (GRID_MAX_POINTS_Y) - 1));
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); return search_from(grid_x, grid_y, func, data);
} }

View file

@ -64,10 +64,10 @@
void mesh_bed_leveling::line_to_destination(const_feedRate_t scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) { void mesh_bed_leveling::line_to_destination(const_feedRate_t scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) {
// Get current and destination cells for this line // Get current and destination cells for this line
xy_int8_t scel = cell_indexes(current_position), ecel = cell_indexes(destination); xy_int8_t scel = cell_indexes(current_position), ecel = cell_indexes(destination);
NOMORE(scel.x, GRID_MAX_POINTS_X - 2); NOMORE(scel.x, GRID_MAX_CELLS_X - 1);
NOMORE(scel.y, GRID_MAX_POINTS_Y - 2); NOMORE(scel.y, GRID_MAX_CELLS_Y - 1);
NOMORE(ecel.x, GRID_MAX_POINTS_X - 2); NOMORE(ecel.x, GRID_MAX_CELLS_X - 1);
NOMORE(ecel.y, GRID_MAX_POINTS_Y - 2); NOMORE(ecel.y, GRID_MAX_CELLS_Y - 1);
// Start and end in the same cell? No split needed. // Start and end in the same cell? No split needed.
if (scel == ecel) { if (scel == ecel) {

View file

@ -32,8 +32,8 @@ enum MeshLevelingState : char {
MeshReset // G29 S5 MeshReset // G29 S5
}; };
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / float(GRID_MAX_POINTS_X - 1)) #define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / (GRID_MAX_CELLS_X))
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / float(GRID_MAX_POINTS_Y - 1)) #define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y))
#define _GET_MESH_X(I) mbl.index_to_xpos[I] #define _GET_MESH_X(I) mbl.index_to_xpos[I]
#define _GET_MESH_Y(J) mbl.index_to_ypos[J] #define _GET_MESH_Y(J) mbl.index_to_ypos[J]
#define Z_VALUES_ARR mbl.z_values #define Z_VALUES_ARR mbl.z_values
@ -61,7 +61,7 @@ public:
static inline void zigzag(const int8_t index, int8_t &px, int8_t &py) { static inline void zigzag(const int8_t index, int8_t &px, int8_t &py) {
px = index % (GRID_MAX_POINTS_X); px = index % (GRID_MAX_POINTS_X);
py = index / (GRID_MAX_POINTS_X); py = index / (GRID_MAX_POINTS_X);
if (py & 1) px = (GRID_MAX_POINTS_X - 1) - px; // Zig zag if (py & 1) px = (GRID_MAX_POINTS_X) - 1 - px; // Zig zag
} }
static void set_zigzag_z(const int8_t index, const_float_t z) { static void set_zigzag_z(const int8_t index, const_float_t z) {
@ -72,11 +72,11 @@ public:
static int8_t cell_index_x(const_float_t x) { static int8_t cell_index_x(const_float_t x) {
int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST); int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST);
return constrain(cx, 0, (GRID_MAX_POINTS_X) - 2); return constrain(cx, 0, GRID_MAX_CELLS_X - 1);
} }
static int8_t cell_index_y(const_float_t y) { static int8_t cell_index_y(const_float_t y) {
int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST); int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST);
return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 2); return constrain(cy, 0, GRID_MAX_CELLS_Y - 1);
} }
static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) { static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
return { cell_index_x(x), cell_index_y(y) }; return { cell_index_x(x), cell_index_y(y) };
@ -85,11 +85,11 @@ public:
static int8_t probe_index_x(const_float_t x) { static int8_t probe_index_x(const_float_t x) {
int8_t px = (x - (MESH_MIN_X) + 0.5f * (MESH_X_DIST)) * RECIPROCAL(MESH_X_DIST); int8_t px = (x - (MESH_MIN_X) + 0.5f * (MESH_X_DIST)) * RECIPROCAL(MESH_X_DIST);
return WITHIN(px, 0, GRID_MAX_POINTS_X - 1) ? px : -1; return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1;
} }
static int8_t probe_index_y(const_float_t y) { static int8_t probe_index_y(const_float_t y) {
int8_t py = (y - (MESH_MIN_Y) + 0.5f * (MESH_Y_DIST)) * RECIPROCAL(MESH_Y_DIST); int8_t py = (y - (MESH_MIN_Y) + 0.5f * (MESH_Y_DIST)) * RECIPROCAL(MESH_Y_DIST);
return WITHIN(py, 0, GRID_MAX_POINTS_Y - 1) ? py : -1; return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1;
} }
static inline xy_int8_t probe_indexes(const_float_t x, const_float_t y) { static inline xy_int8_t probe_indexes(const_float_t x, const_float_t y) {
return { probe_index_x(x), probe_index_y(y) }; return { probe_index_x(x), probe_index_y(y) };

View file

@ -190,7 +190,7 @@ void unified_bed_leveling::display_map(const int map_type) {
const xy_int8_t curr = closest_indexes(xy_pos_t(current_position) + probe.offset_xy); const xy_int8_t curr = closest_indexes(xy_pos_t(current_position) + probe.offset_xy);
if (!lcd) SERIAL_EOL(); if (!lcd) SERIAL_EOL();
for (int8_t j = GRID_MAX_POINTS_Y - 1; j >= 0; j--) { for (int8_t j = (GRID_MAX_POINTS_Y) - 1; j >= 0; j--) {
// Row Label (J index) // Row Label (J index)
if (human) { if (human) {
@ -217,7 +217,7 @@ void unified_bed_leveling::display_map(const int map_type) {
if (human && f >= 0.0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Display sign also for positive numbers (' ' for 0) if (human && f >= 0.0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Display sign also for positive numbers (' ' for 0)
SERIAL_ECHO_F(f, 3); // Positive: 5 digits, Negative: 6 digits SERIAL_ECHO_F(f, 3); // Positive: 5 digits, Negative: 6 digits
} }
if (csv && i < GRID_MAX_POINTS_X - 1) SERIAL_CHAR('\t'); if (csv && i < (GRID_MAX_POINTS_X) - 1) SERIAL_CHAR('\t');
// Closing Brace or Space // Closing Brace or Space
if (human) SERIAL_CHAR(is_current ? ']' : ' '); if (human) SERIAL_CHAR(is_current ? ']' : ' ');

View file

@ -38,8 +38,8 @@ enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP };
struct mesh_index_pair; struct mesh_index_pair;
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / float(GRID_MAX_POINTS_X - 1)) #define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / (GRID_MAX_CELLS_X))
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / float(GRID_MAX_POINTS_Y - 1)) #define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y))
#if ENABLED(OPTIMIZED_MESH_STORAGE) #if ENABLED(OPTIMIZED_MESH_STORAGE)
typedef int16_t mesh_store_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y]; typedef int16_t mesh_store_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
@ -142,19 +142,19 @@ public:
} }
static int8_t cell_index_x_valid(const_float_t x) { static int8_t cell_index_x_valid(const_float_t x) {
return WITHIN(cell_index_x_raw(x), 0, (GRID_MAX_POINTS_X - 2)); return WITHIN(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
} }
static int8_t cell_index_y_valid(const_float_t y) { static int8_t cell_index_y_valid(const_float_t y) {
return WITHIN(cell_index_y_raw(y), 0, (GRID_MAX_POINTS_Y - 2)); return WITHIN(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
} }
static int8_t cell_index_x(const_float_t x) { static int8_t cell_index_x(const_float_t x) {
return constrain(cell_index_x_raw(x), 0, (GRID_MAX_POINTS_X) - 2); return constrain(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
} }
static int8_t cell_index_y(const_float_t y) { static int8_t cell_index_y(const_float_t y) {
return constrain(cell_index_y_raw(y), 0, (GRID_MAX_POINTS_Y) - 2); return constrain(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
} }
static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) { static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
@ -164,11 +164,11 @@ public:
static int8_t closest_x_index(const_float_t x) { static int8_t closest_x_index(const_float_t x) {
const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST); const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST);
return WITHIN(px, 0, GRID_MAX_POINTS_X - 1) ? px : -1; return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1;
} }
static int8_t closest_y_index(const_float_t y) { static int8_t closest_y_index(const_float_t y) {
const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST); const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST);
return WITHIN(py, 0, GRID_MAX_POINTS_Y - 1) ? py : -1; return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1;
} }
static inline xy_int8_t closest_indexes(const xy_pos_t &xy) { static inline xy_int8_t closest_indexes(const xy_pos_t &xy) {
return { closest_x_index(xy.x), closest_y_index(xy.y) }; return { closest_x_index(xy.x), closest_y_index(xy.y) };
@ -204,10 +204,10 @@ public:
* the case where the printer is making a vertical line that only crosses horizontal mesh lines. * the case where the printer is making a vertical line that only crosses horizontal mesh lines.
*/ */
static inline float z_correction_for_x_on_horizontal_mesh_line(const_float_t rx0, const int x1_i, const int yi) { static inline float z_correction_for_x_on_horizontal_mesh_line(const_float_t rx0, const int x1_i, const int yi) {
if (!WITHIN(x1_i, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(yi, 0, GRID_MAX_POINTS_Y - 1)) { if (!WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(yi, 0, (GRID_MAX_POINTS_Y) - 1)) {
if (DEBUGGING(LEVELING)) { if (DEBUGGING(LEVELING)) {
if (WITHIN(x1_i, 0, GRID_MAX_POINTS_X - 1)) DEBUG_ECHOPGM("yi"); else DEBUG_ECHOPGM("x1_i"); if (WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1)) DEBUG_ECHOPGM("yi"); else DEBUG_ECHOPGM("x1_i");
DEBUG_ECHOLNPAIR(" out of bounds in z_correction_for_x_on_horizontal_mesh_line(rx0=", rx0, ",x1_i=", x1_i, ",yi=", yi, ")"); DEBUG_ECHOLNPAIR(" out of bounds in z_correction_for_x_on_horizontal_mesh_line(rx0=", rx0, ",x1_i=", x1_i, ",yi=", yi, ")");
} }
@ -218,19 +218,19 @@ public:
const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * RECIPROCAL(MESH_X_DIST), const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * RECIPROCAL(MESH_X_DIST),
z1 = z_values[x1_i][yi]; z1 = z_values[x1_i][yi];
return z1 + xratio * (z_values[_MIN(x1_i, GRID_MAX_POINTS_X - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array return z1 + xratio * (z_values[_MIN(x1_i, (GRID_MAX_POINTS_X) - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array
// If it is, it is clamped to the last element of the // If it is, it is clamped to the last element of the
// z_values[][] array and no correction is applied. // z_values[][] array and no correction is applied.
} }
// //
// See comments above for z_correction_for_x_on_horizontal_mesh_line // See comments above for z_correction_for_x_on_horizontal_mesh_line
// //
static inline float z_correction_for_y_on_vertical_mesh_line(const_float_t ry0, const int xi, const int y1_i) { static inline float z_correction_for_y_on_vertical_mesh_line(const_float_t ry0, const int xi, const int y1_i) {
if (!WITHIN(xi, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(y1_i, 0, GRID_MAX_POINTS_Y - 1)) { if (!WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(y1_i, 0, (GRID_MAX_POINTS_Y) - 1)) {
if (DEBUGGING(LEVELING)) { if (DEBUGGING(LEVELING)) {
if (WITHIN(xi, 0, GRID_MAX_POINTS_X - 1)) DEBUG_ECHOPGM("y1_i"); else DEBUG_ECHOPGM("xi"); if (WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1)) DEBUG_ECHOPGM("y1_i"); else DEBUG_ECHOPGM("xi");
DEBUG_ECHOLNPAIR(" out of bounds in z_correction_for_y_on_vertical_mesh_line(ry0=", ry0, ", xi=", xi, ", y1_i=", y1_i, ")"); DEBUG_ECHOLNPAIR(" out of bounds in z_correction_for_y_on_vertical_mesh_line(ry0=", ry0, ", xi=", xi, ", y1_i=", y1_i, ")");
} }
@ -241,9 +241,9 @@ public:
const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * RECIPROCAL(MESH_Y_DIST), const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * RECIPROCAL(MESH_Y_DIST),
z1 = z_values[xi][y1_i]; z1 = z_values[xi][y1_i];
return z1 + yratio * (z_values[xi][_MIN(y1_i, GRID_MAX_POINTS_Y - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array return z1 + yratio * (z_values[xi][_MIN(y1_i, (GRID_MAX_POINTS_Y) - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array
// If it is, it is clamped to the last element of the // If it is, it is clamped to the last element of the
// z_values[][] array and no correction is applied. // z_values[][] array and no correction is applied.
} }
/** /**
@ -266,11 +266,11 @@ public:
const float z1 = calc_z0(rx0, const float z1 = calc_z0(rx0,
mesh_index_to_xpos(cx), z_values[cx][cy], mesh_index_to_xpos(cx), z_values[cx][cy],
mesh_index_to_xpos(cx + 1), z_values[_MIN(cx, GRID_MAX_POINTS_X - 2) + 1][cy]); mesh_index_to_xpos(cx + 1), z_values[_MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1][cy]);
const float z2 = calc_z0(rx0, const float z2 = calc_z0(rx0,
mesh_index_to_xpos(cx), z_values[cx][_MIN(cy, GRID_MAX_POINTS_Y - 2) + 1], mesh_index_to_xpos(cx), z_values[cx][_MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1],
mesh_index_to_xpos(cx + 1), z_values[_MIN(cx, GRID_MAX_POINTS_X - 2) + 1][_MIN(cy, GRID_MAX_POINTS_Y - 2) + 1]); mesh_index_to_xpos(cx + 1), z_values[_MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1][_MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1]);
float z0 = calc_z0(ry0, float z0 = calc_z0(ry0,
mesh_index_to_ypos(cy), z1, mesh_index_to_ypos(cy), z1,
@ -302,10 +302,10 @@ public:
static inline float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); } static inline float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
static inline float mesh_index_to_xpos(const uint8_t i) { static inline float mesh_index_to_xpos(const uint8_t i) {
return i < GRID_MAX_POINTS_X ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST); return i < (GRID_MAX_POINTS_X) ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST);
} }
static inline float mesh_index_to_ypos(const uint8_t i) { static inline float mesh_index_to_ypos(const uint8_t i) {
return i < GRID_MAX_POINTS_Y ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST); return i < (GRID_MAX_POINTS_Y) ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST);
} }
#if UBL_SEGMENTED #if UBL_SEGMENTED

View file

@ -433,8 +433,7 @@ void unified_bed_leveling::G29() {
SERIAL_DECIMAL(param.XY_pos.y); SERIAL_DECIMAL(param.XY_pos.y);
SERIAL_ECHOLNPGM(").\n"); SERIAL_ECHOLNPGM(").\n");
} }
const xy_pos_t near_probe_xy = param.XY_pos + probe.offset_xy; probe_entire_mesh(param.XY_pos, parser.seen('T'), parser.seen('E'), parser.seen('U'));
probe_entire_mesh(near_probe_xy, parser.seen('T'), parser.seen('E'), parser.seen('U'));
report_current_position(); report_current_position();
probe_deployed = true; probe_deployed = true;
@ -1140,8 +1139,9 @@ bool unified_bed_leveling::G29_parse_parameters() {
} }
// If X or Y are not valid, use center of the bed values // If X or Y are not valid, use center of the bed values
if (!COORDINATE_OKAY(sx, X_MIN_BED, X_MAX_BED)) sx = X_CENTER; // (for UBL_HILBERT_CURVE default to lower-left corner instead)
if (!COORDINATE_OKAY(sy, Y_MIN_BED, Y_MAX_BED)) sy = Y_CENTER; if (!COORDINATE_OKAY(sx, X_MIN_BED, X_MAX_BED)) sx = TERN(UBL_HILBERT_CURVE, 0, X_CENTER);
if (!COORDINATE_OKAY(sy, Y_MIN_BED, Y_MAX_BED)) sy = TERN(UBL_HILBERT_CURVE, 0, Y_CENTER);
if (err_flag) return UBL_ERR; if (err_flag) return UBL_ERR;

View file

@ -397,8 +397,8 @@
int8_t((raw.x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST)), int8_t((raw.x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST)),
int8_t((raw.y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST)) int8_t((raw.y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST))
}; };
LIMIT(icell.x, 0, (GRID_MAX_POINTS_X) - 1); LIMIT(icell.x, 0, GRID_MAX_CELLS_X);
LIMIT(icell.y, 0, (GRID_MAX_POINTS_Y) - 1); LIMIT(icell.y, 0, GRID_MAX_CELLS_Y);
float z_x0y0 = z_values[icell.x ][icell.y ], // z at lower left corner float z_x0y0 = z_values[icell.x ][icell.y ], // z at lower left corner
z_x1y0 = z_values[icell.x+1][icell.y ], // z at upper left corner z_x1y0 = z_values[icell.x+1][icell.y ], // z at upper left corner

View file

@ -113,6 +113,10 @@
#include "../../module/temperature.h" #include "../../module/temperature.h"
#include "../../lcd/marlinui.h" #include "../../lcd/marlinui.h"
#if ENABLED(UBL_HILBERT_CURVE)
#include "../../feature/bedlevel/hilbert_curve.h"
#endif
#define EXTRUSION_MULTIPLIER 1.0 #define EXTRUSION_MULTIPLIER 1.0
#define PRIME_LENGTH 10.0 #define PRIME_LENGTH 10.0
#define OOZE_AMOUNT 0.3 #define OOZE_AMOUNT 0.3
@ -145,24 +149,9 @@
constexpr float g26_e_axis_feedrate = 0.025; constexpr float g26_e_axis_feedrate = 0.025;
static MeshFlags circle_flags, horizontal_mesh_line_flags, vertical_mesh_line_flags; static MeshFlags circle_flags;
float g26_random_deviation = 0.0; float g26_random_deviation = 0.0;
static bool g26_retracted = false; // Track the retracted state of the nozzle so mismatched
// retracts/recovers won't result in a bad state.
float g26_extrusion_multiplier,
g26_retraction_multiplier,
g26_layer_height,
g26_prime_length;
xy_pos_t g26_xy_pos; // = { 0, 0 }
int16_t g26_bed_temp,
g26_hotend_temp;
int8_t g26_prime_flag;
#if HAS_LCD_MENU #if HAS_LCD_MENU
/** /**
@ -178,52 +167,17 @@ int8_t g26_prime_flag;
#endif #endif
mesh_index_pair find_closest_circle_to_print(const xy_pos_t &pos) {
float closest = 99999.99;
mesh_index_pair out_point;
out_point.pos = -1;
GRID_LOOP(i, j) {
if (!circle_flags.marked(i, j)) {
// We found a circle that needs to be printed
const xy_pos_t m = { _GET_MESH_X(i), _GET_MESH_Y(j) };
// Get the distance to this intersection
float f = (pos - m).magnitude();
// It is possible that we are being called with the values
// to let us find the closest circle to the start position.
// But if this is not the case, add a small weighting to the
// distance calculation to help it choose a better place to continue.
f += (g26_xy_pos - m).magnitude() / 15.0f;
// Add the specified amount of Random Noise to our search
if (g26_random_deviation > 1.0) f += random(0.0, g26_random_deviation);
if (f < closest) {
closest = f; // Found a closer un-printed location
out_point.pos.set(i, j); // Save its data
out_point.distance = closest;
}
}
}
circle_flags.mark(out_point); // Mark this location as done.
return out_point;
}
void move_to(const_float_t rx, const_float_t ry, const_float_t z, const_float_t e_delta) { void move_to(const_float_t rx, const_float_t ry, const_float_t z, const_float_t e_delta) {
static float last_z = -999.99; static float last_z = -999.99;
const xy_pos_t dest = { rx, ry }; const xy_pos_t dest = { rx, ry };
const bool has_xy_component = dest != current_position; // Check if X or Y is involved in the movement. const bool has_xy_component = dest != current_position, // Check if X or Y is involved in the movement.
const bool has_e_component = e_delta != 0.0; has_e_component = e_delta != 0.0;
destination = current_position;
if (z != last_z) { if (z != last_z) {
last_z = destination.z = z; last_z = z;
destination.set(current_position.x, current_position.y, z, current_position.e);
const feedRate_t fr_mm_s = planner.settings.max_feedrate_mm_s[Z_AXIS] * 0.5f; // Use half of the Z_AXIS max feed rate const feedRate_t fr_mm_s = planner.settings.max_feedrate_mm_s[Z_AXIS] * 0.5f; // Use half of the Z_AXIS max feed rate
prepare_internal_move_to_destination(fr_mm_s); prepare_internal_move_to_destination(fr_mm_s);
} }
@ -239,241 +193,293 @@ void move_to(const_float_t rx, const_float_t ry, const_float_t z, const_float_t
prepare_internal_move_to_destination(fr_mm_s); prepare_internal_move_to_destination(fr_mm_s);
} }
FORCE_INLINE void move_to(const xyz_pos_t &where, const_float_t de) { move_to(where.x, where.y, where.z, de); } void move_to(const xyz_pos_t &where, const_float_t de) { move_to(where.x, where.y, where.z, de); }
void retract_filament(const xyz_pos_t &where) { typedef struct {
if (!g26_retracted) { // Only retract if we are not already retracted! float extrusion_multiplier = EXTRUSION_MULTIPLIER,
g26_retracted = true; retraction_multiplier = G26_RETRACT_MULTIPLIER,
move_to(where, -1.0f * g26_retraction_multiplier); layer_height = MESH_TEST_LAYER_HEIGHT,
} prime_length = PRIME_LENGTH;
}
// TODO: Parameterize the Z lift with a define int16_t bed_temp = MESH_TEST_BED_TEMP,
void retract_lift_move(const xyz_pos_t &s) { hotend_temp = MESH_TEST_HOTEND_TEMP;
retract_filament(destination);
move_to(current_position.x, current_position.y, current_position.z + 0.5f, 0.0); // Z lift to minimize scraping
move_to(s.x, s.y, s.z + 0.5f, 0.0); // Get to the starting point with no extrusion while lifted
}
void recover_filament(const xyz_pos_t &where) { float nozzle = MESH_TEST_NOZZLE_SIZE,
if (g26_retracted) { // Only un-retract if we are retracted. filament_diameter = DEFAULT_NOMINAL_FILAMENT_DIA,
move_to(where, 1.2f * g26_retraction_multiplier); ooze_amount; // 'O' ... OOZE_AMOUNT
g26_retracted = false;
}
}
/** bool continue_with_closest, // 'C'
* print_line_from_here_to_there() takes two cartesian coordinates and draws a line from one keep_heaters_on; // 'K'
* to the other. But there are really three sets of coordinates involved. The first coordinate
* is the present location of the nozzle. We don't necessarily want to print from this location.
* We first need to move the nozzle to the start of line segment where we want to print. Once
* there, we can use the two coordinates supplied to draw the line.
*
* Note: Although we assume the first set of coordinates is the start of the line and the second
* set of coordinates is the end of the line, it does not always work out that way. This function
* optimizes the movement to minimize the travel distance before it can start printing. This saves
* a lot of time and eliminates a lot of nonsensical movement of the nozzle. However, it does
* cause a lot of very little short retracement of th nozzle when it draws the very first line
* segment of a 'circle'. The time this requires is very short and is easily saved by the other
* cases where the optimization comes into play.
*/
void print_line_from_here_to_there(const xyz_pos_t &s, const xyz_pos_t &e) {
// Distances to the start / end of the line xy_pos_t xy_pos; // = { 0, 0 }
xy_float_t svec = current_position - s, evec = current_position - e;
const float dist_start = HYPOT2(svec.x, svec.y), int8_t prime_flag = 0;
dist_end = HYPOT2(evec.x, evec.y),
line_length = HYPOT(e.x - s.x, e.y - s.y);
// If the end point of the line is closer to the nozzle, flip the direction, bool g26_retracted = false; // Track the retracted state during G26 so mismatched
// moving from the end to the start. On very small lines the optimization isn't worth it. // retracts/recovers don't result in a bad state.
if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < ABS(line_length))
return print_line_from_here_to_there(e, s);
// Decide whether to retract & lift void retract_filament(const xyz_pos_t &where) {
if (dist_start > 2.0) retract_lift_move(s); if (!g26_retracted) { // Only retract if we are not already retracted!
g26_retracted = true;
move_to(s, 0.0); // Get to the starting point with no extrusion / un-Z lift move_to(where, -1.0f * retraction_multiplier);
const float e_pos_delta = line_length * g26_e_axis_feedrate * g26_extrusion_multiplier;
recover_filament(destination);
move_to(e, e_pos_delta); // Get to the ending point with an appropriate amount of extrusion
}
inline bool look_for_lines_to_connect() {
xyz_pos_t s, e;
s.z = e.z = g26_layer_height;
GRID_LOOP(i, j) {
if (TERN0(HAS_LCD_MENU, user_canceled())) return true;
if (i < (GRID_MAX_POINTS_X)) { // Can't connect to anything farther to the right than GRID_MAX_POINTS_X.
// Already a half circle at the edge of the bed.
if (circle_flags.marked(i, j) && circle_flags.marked(i + 1, j)) { // Test whether a leftward line can be done
if (!horizontal_mesh_line_flags.marked(i, j)) {
// Two circles need a horizontal line to connect them
s.x = _GET_MESH_X( i ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // right edge
e.x = _GET_MESH_X(i + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // left edge
#if HAS_ENDSTOPS
LIMIT(s.x, X_MIN_POS + 1, X_MAX_POS - 1);
s.y = e.y = constrain(_GET_MESH_Y(j), Y_MIN_POS + 1, Y_MAX_POS - 1);
LIMIT(e.x, X_MIN_POS + 1, X_MAX_POS - 1);
#else
s.y = e.y = _GET_MESH_Y(j);
#endif
if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y))
print_line_from_here_to_there(s, e);
horizontal_mesh_line_flags.mark(i, j); // Mark done, even if skipped
}
}
if (j < (GRID_MAX_POINTS_Y)) { // Can't connect to anything further back than GRID_MAX_POINTS_Y.
// Already a half circle at the edge of the bed.
if (circle_flags.marked(i, j) && circle_flags.marked(i, j + 1)) { // Test whether a downward line can be done
if (!vertical_mesh_line_flags.marked(i, j)) {
// Two circles that need a vertical line to connect them
s.y = _GET_MESH_Y( j ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // top edge
e.y = _GET_MESH_Y(j + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // bottom edge
#if HAS_ENDSTOPS
s.x = e.x = constrain(_GET_MESH_X(i), X_MIN_POS + 1, X_MAX_POS - 1);
LIMIT(s.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
LIMIT(e.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
#else
s.x = e.x = _GET_MESH_X(i);
#endif
if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y))
print_line_from_here_to_there(s, e);
vertical_mesh_line_flags.mark(i, j); // Mark done, even if skipped
}
}
}
} }
} }
return false;
}
/** // TODO: Parameterize the Z lift with a define
* Turn on the bed and nozzle heat and void retract_lift_move(const xyz_pos_t &s) {
* wait for them to get up to temperature.
*/
inline bool turn_on_heaters() {
SERIAL_ECHOLNPGM("Waiting for heatup.");
#if HAS_HEATED_BED
if (g26_bed_temp > 25) {
#if HAS_WIRED_LCD
ui.set_status_P(GET_TEXT(MSG_G26_HEATING_BED), 99);
ui.quick_feedback();
TERN_(HAS_LCD_MENU, ui.capture());
#endif
thermalManager.setTargetBed(g26_bed_temp);
// Wait for the temperature to stabilize
if (!thermalManager.wait_for_bed(true
#if G26_CLICK_CAN_CANCEL
, true
#endif
)
) return G26_ERR;
}
#endif // HAS_HEATED_BED
// Start heating the active nozzle
#if HAS_WIRED_LCD
ui.set_status_P(GET_TEXT(MSG_G26_HEATING_NOZZLE), 99);
ui.quick_feedback();
#endif
thermalManager.setTargetHotend(g26_hotend_temp, active_extruder);
// Wait for the temperature to stabilize
if (!thermalManager.wait_for_hotend(active_extruder, true
#if G26_CLICK_CAN_CANCEL
, true
#endif
)) return G26_ERR;
#if HAS_WIRED_LCD
ui.reset_status();
ui.quick_feedback();
#endif
return G26_OK;
}
/**
* Prime the nozzle if needed. Return true on error.
*/
inline bool prime_nozzle() {
const feedRate_t fr_slow_e = planner.settings.max_feedrate_mm_s[E_AXIS] / 15.0f;
#if HAS_LCD_MENU && !HAS_TOUCH_BUTTONS // ui.button_pressed issue with touchscreen
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
float Total_Prime = 0.0;
#endif
if (g26_prime_flag == -1) { // The user wants to control how much filament gets purged
ui.capture();
ui.set_status_P(GET_TEXT(MSG_G26_MANUAL_PRIME), 99);
ui.chirp();
destination = current_position;
recover_filament(destination); // Make sure G26 doesn't think the filament is retracted().
while (!ui.button_pressed()) {
ui.chirp();
destination.e += 0.25;
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
Total_Prime += 0.25;
if (Total_Prime >= EXTRUDE_MAXLENGTH) {
ui.release();
return G26_ERR;
}
#endif
prepare_internal_move_to_destination(fr_slow_e);
destination = current_position;
planner.synchronize(); // Without this synchronize, the purge is more consistent,
// but because the planner has a buffer, we won't be able
// to stop as quickly. So we put up with the less smooth
// action to give the user a more responsive 'Stop'.
}
ui.wait_for_release();
ui.set_status_P(GET_TEXT(MSG_G26_PRIME_DONE), 99);
ui.quick_feedback();
ui.release();
}
else
#endif
{
#if HAS_WIRED_LCD
ui.set_status_P(GET_TEXT(MSG_G26_FIXED_LENGTH), 99);
ui.quick_feedback();
#endif
destination = current_position;
destination.e += g26_prime_length;
prepare_internal_move_to_destination(fr_slow_e);
destination.e -= g26_prime_length;
retract_filament(destination); retract_filament(destination);
move_to(current_position.x, current_position.y, current_position.z + 0.5f, 0.0f); // Z lift to minimize scraping
move_to(s.x, s.y, s.z + 0.5f, 0.0f); // Get to the starting point with no extrusion while lifted
} }
return G26_OK; void recover_filament(const xyz_pos_t &where) {
} if (g26_retracted) { // Only un-retract if we are retracted.
move_to(where, 1.2f * retraction_multiplier);
g26_retracted = false;
}
}
/**
* print_line_from_here_to_there() takes two cartesian coordinates and draws a line from one
* to the other. But there are really three sets of coordinates involved. The first coordinate
* is the present location of the nozzle. We don't necessarily want to print from this location.
* We first need to move the nozzle to the start of line segment where we want to print. Once
* there, we can use the two coordinates supplied to draw the line.
*
* Note: Although we assume the first set of coordinates is the start of the line and the second
* set of coordinates is the end of the line, it does not always work out that way. This function
* optimizes the movement to minimize the travel distance before it can start printing. This saves
* a lot of time and eliminates a lot of nonsensical movement of the nozzle. However, it does
* cause a lot of very little short retracement of th nozzle when it draws the very first line
* segment of a 'circle'. The time this requires is very short and is easily saved by the other
* cases where the optimization comes into play.
*/
void print_line_from_here_to_there(const xyz_pos_t &s, const xyz_pos_t &e) {
// Distances to the start / end of the line
xy_float_t svec = current_position - s, evec = current_position - e;
const float dist_start = HYPOT2(svec.x, svec.y),
dist_end = HYPOT2(evec.x, evec.y),
line_length = HYPOT(e.x - s.x, e.y - s.y);
// If the end point of the line is closer to the nozzle, flip the direction,
// moving from the end to the start. On very small lines the optimization isn't worth it.
if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < ABS(line_length))
return print_line_from_here_to_there(e, s);
// Decide whether to retract & lift
if (dist_start > 2.0) retract_lift_move(s);
move_to(s, 0.0); // Get to the starting point with no extrusion / un-Z lift
const float e_pos_delta = line_length * g26_e_axis_feedrate * extrusion_multiplier;
recover_filament(destination);
move_to(e, e_pos_delta); // Get to the ending point with an appropriate amount of extrusion
}
void connect_neighbor_with_line(const xy_int8_t &p1, int8_t dx, int8_t dy) {
xy_int8_t p2;
p2.x = p1.x + dx;
p2.y = p1.y + dy;
if (p2.x < 0 || p2.x >= (GRID_MAX_POINTS_X)) return;
if (p2.y < 0 || p2.y >= (GRID_MAX_POINTS_Y)) return;
if(circle_flags.marked(p1.x, p1.y) && circle_flags.marked(p2.x, p2.y)) {
xyz_pos_t s, e;
s.x = _GET_MESH_X(p1.x) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dx;
e.x = _GET_MESH_X(p2.x) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dx;
s.y = _GET_MESH_Y(p1.y) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dy;
e.y = _GET_MESH_Y(p2.y) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dy;
s.z = e.z = layer_height;
#if HAS_ENDSTOPS
LIMIT(s.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
LIMIT(e.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
LIMIT(s.x, X_MIN_POS + 1, X_MAX_POS - 1);
LIMIT(e.x, X_MIN_POS + 1, X_MAX_POS - 1);
#endif
if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y))
print_line_from_here_to_there(s, e);
}
}
/**
* Turn on the bed and nozzle heat and
* wait for them to get up to temperature.
*/
bool turn_on_heaters() {
SERIAL_ECHOLNPGM("Waiting for heatup.");
#if HAS_HEATED_BED
if (bed_temp > 25) {
#if HAS_WIRED_LCD
ui.set_status_P(GET_TEXT(MSG_G26_HEATING_BED), 99);
ui.quick_feedback();
TERN_(HAS_LCD_MENU, ui.capture());
#endif
thermalManager.setTargetBed(bed_temp);
// Wait for the temperature to stabilize
if (!thermalManager.wait_for_bed(true
#if G26_CLICK_CAN_CANCEL
, true
#endif
)
) return G26_ERR;
}
#else
UNUSED(bed_temp);
#endif // HAS_HEATED_BED
// Start heating the active nozzle
#if HAS_WIRED_LCD
ui.set_status_P(GET_TEXT(MSG_G26_HEATING_NOZZLE), 99);
ui.quick_feedback();
#endif
thermalManager.setTargetHotend(hotend_temp, active_extruder);
// Wait for the temperature to stabilize
if (!thermalManager.wait_for_hotend(active_extruder, true
#if G26_CLICK_CAN_CANCEL
, true
#endif
)) return G26_ERR;
#if HAS_WIRED_LCD
ui.reset_status();
ui.quick_feedback();
#endif
return G26_OK;
}
/**
* Prime the nozzle if needed. Return true on error.
*/
bool prime_nozzle() {
const feedRate_t fr_slow_e = planner.settings.max_feedrate_mm_s[E_AXIS] / 15.0f;
#if HAS_LCD_MENU && !HAS_TOUCH_BUTTONS // ui.button_pressed issue with touchscreen
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
float Total_Prime = 0.0;
#endif
if (prime_flag == -1) { // The user wants to control how much filament gets purged
ui.capture();
ui.set_status_P(GET_TEXT(MSG_G26_MANUAL_PRIME), 99);
ui.chirp();
destination = current_position;
recover_filament(destination); // Make sure G26 doesn't think the filament is retracted().
while (!ui.button_pressed()) {
ui.chirp();
destination.e += 0.25;
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
Total_Prime += 0.25;
if (Total_Prime >= EXTRUDE_MAXLENGTH) {
ui.release();
return G26_ERR;
}
#endif
prepare_internal_move_to_destination(fr_slow_e);
destination = current_position;
planner.synchronize(); // Without this synchronize, the purge is more consistent,
// but because the planner has a buffer, we won't be able
// to stop as quickly. So we put up with the less smooth
// action to give the user a more responsive 'Stop'.
}
ui.wait_for_release();
ui.set_status_P(GET_TEXT(MSG_G26_PRIME_DONE), 99);
ui.quick_feedback();
ui.release();
}
else
#endif
{
#if HAS_WIRED_LCD
ui.set_status_P(GET_TEXT(MSG_G26_FIXED_LENGTH), 99);
ui.quick_feedback();
#endif
destination = current_position;
destination.e += prime_length;
prepare_internal_move_to_destination(fr_slow_e);
destination.e -= prime_length;
retract_filament(destination);
}
return G26_OK;
}
/**
* Find the nearest point at which to print a circle
*/
mesh_index_pair find_closest_circle_to_print(const xy_pos_t &pos) {
mesh_index_pair out_point;
out_point.pos = -1;
#if ENABLED(UBL_HILBERT_CURVE)
auto test_func = [](uint8_t i, uint8_t j, void *data) -> bool {
if (!circle_flags.marked(i, j)) {
mesh_index_pair *out_point = (mesh_index_pair*)data;
out_point->pos.set(i, j); // Save its data
return true;
}
return false;
};
hilbert_curve::search_from_closest(pos, test_func, &out_point);
#else
float closest = 99999.99;
GRID_LOOP(i, j) {
if (!circle_flags.marked(i, j)) {
// We found a circle that needs to be printed
const xy_pos_t m = { _GET_MESH_X(i), _GET_MESH_Y(j) };
// Get the distance to this intersection
float f = (pos - m).magnitude();
// It is possible that we are being called with the values
// to let us find the closest circle to the start position.
// But if this is not the case, add a small weighting to the
// distance calculation to help it choose a better place to continue.
f += (xy_pos - m).magnitude() / 15.0f;
// Add the specified amount of Random Noise to our search
if (g26_random_deviation > 1.0) f += random(0.0, g26_random_deviation);
if (f < closest) {
closest = f; // Found a closer un-printed location
out_point.pos.set(i, j); // Save its data
out_point.distance = closest;
}
}
}
#endif
circle_flags.mark(out_point); // Mark this location as done.
return out_point;
}
} g26_helper_t;
/** /**
* G26: Mesh Validation Pattern generation. * G26: Mesh Validation Pattern generation.
@ -510,20 +516,11 @@ void GcodeSuite::G26() {
// Change the tool first, if specified // Change the tool first, if specified
if (parser.seenval('T')) tool_change(parser.value_int()); if (parser.seenval('T')) tool_change(parser.value_int());
g26_extrusion_multiplier = EXTRUSION_MULTIPLIER; g26_helper_t g26;
g26_retraction_multiplier = G26_RETRACT_MULTIPLIER;
g26_layer_height = MESH_TEST_LAYER_HEIGHT;
g26_prime_length = PRIME_LENGTH;
g26_bed_temp = MESH_TEST_BED_TEMP;
g26_hotend_temp = MESH_TEST_HOTEND_TEMP;
g26_prime_flag = 0;
float g26_nozzle = MESH_TEST_NOZZLE_SIZE, g26.ooze_amount = parser.linearval('O', OOZE_AMOUNT);
g26_filament_diameter = DEFAULT_NOMINAL_FILAMENT_DIA, g26.continue_with_closest = parser.boolval('C');
g26_ooze_amount = parser.linearval('O', OOZE_AMOUNT); g26.keep_heaters_on = parser.boolval('K');
bool g26_continue_with_closest = parser.boolval('C'),
g26_keep_heaters_on = parser.boolval('K');
// Accept 'I' if temperature presets are defined // Accept 'I' if temperature presets are defined
#if PREHEAT_COUNT #if PREHEAT_COUNT
@ -548,14 +545,14 @@ void GcodeSuite::G26() {
SERIAL_ECHOLNPAIR("?Specified bed temperature not plausible (40-", BED_MAX_TARGET, "C)."); SERIAL_ECHOLNPAIR("?Specified bed temperature not plausible (40-", BED_MAX_TARGET, "C).");
return; return;
} }
g26_bed_temp = bedtemp; g26.bed_temp = bedtemp;
} }
#endif // HAS_HEATED_BED #endif // HAS_HEATED_BED
if (parser.seenval('L')) { if (parser.seenval('L')) {
g26_layer_height = parser.value_linear_units(); g26.layer_height = parser.value_linear_units();
if (!WITHIN(g26_layer_height, 0.0, 2.0)) { if (!WITHIN(g26.layer_height, 0.0, 2.0)) {
SERIAL_ECHOLNPGM("?Specified layer height not plausible."); SERIAL_ECHOLNPGM("?Specified layer height not plausible.");
return; return;
} }
@ -563,8 +560,8 @@ void GcodeSuite::G26() {
if (parser.seen('Q')) { if (parser.seen('Q')) {
if (parser.has_value()) { if (parser.has_value()) {
g26_retraction_multiplier = parser.value_float(); g26.retraction_multiplier = parser.value_float();
if (!WITHIN(g26_retraction_multiplier, 0.05, 15.0)) { if (!WITHIN(g26.retraction_multiplier, 0.05, 15.0)) {
SERIAL_ECHOLNPGM("?Specified Retraction Multiplier not plausible."); SERIAL_ECHOLNPGM("?Specified Retraction Multiplier not plausible.");
return; return;
} }
@ -576,8 +573,8 @@ void GcodeSuite::G26() {
} }
if (parser.seenval('S')) { if (parser.seenval('S')) {
g26_nozzle = parser.value_float(); g26.nozzle = parser.value_float();
if (!WITHIN(g26_nozzle, 0.1, 2.0)) { if (!WITHIN(g26.nozzle, 0.1, 2.0)) {
SERIAL_ECHOLNPGM("?Specified nozzle size not plausible."); SERIAL_ECHOLNPGM("?Specified nozzle size not plausible.");
return; return;
} }
@ -586,16 +583,16 @@ void GcodeSuite::G26() {
if (parser.seen('P')) { if (parser.seen('P')) {
if (!parser.has_value()) { if (!parser.has_value()) {
#if HAS_LCD_MENU #if HAS_LCD_MENU
g26_prime_flag = -1; g26.prime_flag = -1;
#else #else
SERIAL_ECHOLNPGM("?Prime length must be specified when not using an LCD."); SERIAL_ECHOLNPGM("?Prime length must be specified when not using an LCD.");
return; return;
#endif #endif
} }
else { else {
g26_prime_flag++; g26.prime_flag++;
g26_prime_length = parser.value_linear_units(); g26.prime_length = parser.value_linear_units();
if (!WITHIN(g26_prime_length, 0.0, 25.0)) { if (!WITHIN(g26.prime_length, 0.0, 25.0)) {
SERIAL_ECHOLNPGM("?Specified prime length not plausible."); SERIAL_ECHOLNPGM("?Specified prime length not plausible.");
return; return;
} }
@ -603,17 +600,17 @@ void GcodeSuite::G26() {
} }
if (parser.seenval('F')) { if (parser.seenval('F')) {
g26_filament_diameter = parser.value_linear_units(); g26.filament_diameter = parser.value_linear_units();
if (!WITHIN(g26_filament_diameter, 1.0, 4.0)) { if (!WITHIN(g26.filament_diameter, 1.0, 4.0)) {
SERIAL_ECHOLNPGM("?Specified filament size not plausible."); SERIAL_ECHOLNPGM("?Specified filament size not plausible.");
return; return;
} }
} }
g26_extrusion_multiplier *= sq(1.75) / sq(g26_filament_diameter); // If we aren't using 1.75mm filament, we need to g26.extrusion_multiplier *= sq(1.75) / sq(g26.filament_diameter); // If we aren't using 1.75mm filament, we need to
// scale up or down the length needed to get the // scale up or down the length needed to get the
// same volume of filament // same volume of filament
g26_extrusion_multiplier *= g26_filament_diameter * sq(g26_nozzle) / sq(0.3); // Scale up by nozzle size g26.extrusion_multiplier *= g26.filament_diameter * sq(g26.nozzle) / sq(0.3); // Scale up by nozzle size
// Get a temperature from 'I' or 'H' // Get a temperature from 'I' or 'H'
celsius_t noztemp = 0; celsius_t noztemp = 0;
@ -632,7 +629,7 @@ void GcodeSuite::G26() {
SERIAL_ECHOLNPGM("?Specified nozzle temperature not plausible."); SERIAL_ECHOLNPGM("?Specified nozzle temperature not plausible.");
return; return;
} }
g26_hotend_temp = noztemp; g26.hotend_temp = noztemp;
} }
// 'U' to Randomize and optionally set circle deviation // 'U' to Randomize and optionally set circle deviation
@ -660,9 +657,9 @@ void GcodeSuite::G26() {
} }
// Set a position with 'X' and/or 'Y'. Default: current_position // Set a position with 'X' and/or 'Y'. Default: current_position
g26_xy_pos.set(parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : current_position.x, g26.xy_pos.set(parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : current_position.x,
parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : current_position.y); parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : current_position.y);
if (!position_is_reachable(g26_xy_pos)) { if (!position_is_reachable(g26.xy_pos)) {
SERIAL_ECHOLNPGM("?Specified X,Y coordinate out of bounds."); SERIAL_ECHOLNPGM("?Specified X,Y coordinate out of bounds.");
return; return;
} }
@ -680,12 +677,12 @@ void GcodeSuite::G26() {
planner.calculate_volumetric_multipliers(); planner.calculate_volumetric_multipliers();
#endif #endif
if (turn_on_heaters() != G26_OK) goto LEAVE; if (g26.turn_on_heaters() != G26_OK) goto LEAVE;
current_position.e = 0.0; current_position.e = 0.0;
sync_plan_position_e(); sync_plan_position_e();
if (g26_prime_flag && prime_nozzle() != G26_OK) goto LEAVE; if (g26.prime_flag && g26.prime_nozzle() != G26_OK) goto LEAVE;
/** /**
* Bed is preheated * Bed is preheated
@ -698,14 +695,12 @@ void GcodeSuite::G26() {
*/ */
circle_flags.reset(); circle_flags.reset();
horizontal_mesh_line_flags.reset();
vertical_mesh_line_flags.reset();
// Move nozzle to the specified height for the first layer // Move nozzle to the specified height for the first layer
destination = current_position; destination = current_position;
destination.z = g26_layer_height; destination.z = g26.layer_height;
move_to(destination, 0.0); move_to(destination, 0.0);
move_to(destination, g26_ooze_amount); move_to(destination, g26.ooze_amount);
TERN_(HAS_LCD_MENU, ui.capture()); TERN_(HAS_LCD_MENU, ui.capture());
@ -732,7 +727,7 @@ void GcodeSuite::G26() {
mesh_index_pair location; mesh_index_pair location;
do { do {
// Find the nearest confluence // Find the nearest confluence
location = 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()) {
const xy_pos_t circle = _GET_MESH_POS(location.pos); const xy_pos_t circle = _GET_MESH_POS(location.pos);
@ -762,7 +757,7 @@ void GcodeSuite::G26() {
if (!b) { e.x = circle.x; e.y += INTERSECTION_CIRCLE_RADIUS; } if (!b) { e.x = circle.x; e.y += INTERSECTION_CIRCLE_RADIUS; }
arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2); arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2);
} }
else if (r) { // right edge else if (r) { // right edge
if (b) s.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y); if (b) s.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y);
else s.set(circle.x, circle.y + INTERSECTION_CIRCLE_RADIUS); else s.set(circle.x, circle.y + INTERSECTION_CIRCLE_RADIUS);
if (f) e.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y); if (f) e.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y);
@ -782,25 +777,24 @@ void GcodeSuite::G26() {
const xy_float_t dist = current_position - s; // Distance from the start of the actual circle const xy_float_t dist = current_position - s; // Distance from the start of the actual circle
const float dist_start = HYPOT2(dist.x, dist.y); const float dist_start = HYPOT2(dist.x, dist.y);
const xyze_pos_t endpoint = { const xyze_pos_t endpoint = {
e.x, e.y, g26_layer_height, e.x, e.y, g26.layer_height,
current_position.e + (arc_length * g26_e_axis_feedrate * g26_extrusion_multiplier) current_position.e + (arc_length * g26_e_axis_feedrate * g26.extrusion_multiplier)
}; };
if (dist_start > 2.0) { if (dist_start > 2.0) {
s.z = g26_layer_height + 0.5f; s.z = g26.layer_height + 0.5f;
retract_lift_move(s); g26.retract_lift_move(s);
} }
s.z = g26_layer_height; s.z = g26.layer_height;
move_to(s, 0.0); // Get to the starting point with no extrusion / un-Z lift move_to(s, 0.0); // Get to the starting point with no extrusion / un-Z lift
recover_filament(destination); g26.recover_filament(destination);
const feedRate_t old_feedrate = feedrate_mm_s; { REMEMBER(fr, feedrate_mm_s, PLANNER_XY_FEEDRATE() * 0.1f);
feedrate_mm_s = PLANNER_XY_FEEDRATE() * 0.1f; plan_arc(endpoint, arc_offset, false, 0); // Draw a counter-clockwise arc
plan_arc(endpoint, arc_offset, false, 0); // Draw a counter-clockwise arc destination = current_position;
feedrate_mm_s = old_feedrate; }
destination = current_position;
if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE; // Check if the user wants to stop the Mesh Validation if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE; // Check if the user wants to stop the Mesh Validation
@ -828,8 +822,8 @@ void GcodeSuite::G26() {
if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE; // Check if the user wants to stop the Mesh Validation if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE; // Check if the user wants to stop the Mesh Validation
xyz_float_t p = { circle.x + _COS(ind ), circle.y + _SIN(ind ), g26_layer_height }, xyz_float_t p = { circle.x + _COS(ind ), circle.y + _SIN(ind ), g26.layer_height },
q = { circle.x + _COS(ind + 1), circle.y + _SIN(ind + 1), g26_layer_height }; q = { circle.x + _COS(ind + 1), circle.y + _SIN(ind + 1), g26.layer_height };
#if IS_KINEMATIC #if IS_KINEMATIC
// Check to make sure this segment is entirely on the bed, skip if not. // Check to make sure this segment is entirely on the bed, skip if not.
@ -841,13 +835,17 @@ void GcodeSuite::G26() {
LIMIT(q.y, Y_MIN_POS + 1, Y_MAX_POS - 1); LIMIT(q.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
#endif #endif
print_line_from_here_to_there(p, q); g26.print_line_from_here_to_there(p, q);
SERIAL_FLUSH(); // Prevent host M105 buffer overrun. SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
} }
#endif // !ARC_SUPPORT #endif // !ARC_SUPPORT
if (look_for_lines_to_connect()) goto LEAVE; 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);
if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE;
} }
SERIAL_FLUSH(); // Prevent host M105 buffer overrun. SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
@ -857,12 +855,9 @@ 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);
retract_filament(destination); g26.retract_filament(destination);
destination.z = Z_CLEARANCE_BETWEEN_PROBES; destination.z = Z_CLEARANCE_BETWEEN_PROBES;
move_to(destination, 0); // Raise the nozzle move_to(destination, 0); // Raise the nozzle
destination = g26_xy_pos; // Move back to the starting XY position
move_to(destination, 0); // Move back to the starting position
#if DISABLED(NO_VOLUMETRICS) #if DISABLED(NO_VOLUMETRICS)
parser.volumetric_enabled = volumetric_was_enabled; parser.volumetric_enabled = volumetric_was_enabled;
@ -871,7 +866,7 @@ void GcodeSuite::G26() {
TERN_(HAS_LCD_MENU, ui.release()); // Give back control of the LCD TERN_(HAS_LCD_MENU, ui.release()); // Give back control of the LCD
if (!g26_keep_heaters_on) { if (!g26.keep_heaters_on) {
TERN_(HAS_HEATED_BED, thermalManager.setTargetBed(0)); TERN_(HAS_HEATED_BED, thermalManager.setTargetBed(0));
thermalManager.setTargetHotend(active_extruder, 0); thermalManager.setTargetHotend(active_extruder, 0);
} }

View file

@ -68,8 +68,8 @@ void GcodeSuite::M420() {
y_min = probe.min_y(), y_max = probe.max_y(); y_min = probe.min_y(), y_max = probe.max_y();
#if ENABLED(AUTO_BED_LEVELING_BILINEAR) #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
bilinear_start.set(x_min, y_min); bilinear_start.set(x_min, y_min);
bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_POINTS_X - 1), bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_CELLS_X),
(y_max - y_min) / (GRID_MAX_POINTS_Y - 1)); (y_max - y_min) / (GRID_MAX_CELLS_Y));
#endif #endif
GRID_LOOP(x, y) { GRID_LOOP(x, y) {
Z_VALUES(x, y) = 0.001 * random(-200, 200); Z_VALUES(x, y) = 0.001 * random(-200, 200);

View file

@ -295,10 +295,10 @@ G29_TYPE GcodeSuite::G29() {
// Get nearest i / j from rx / ry // Get nearest i / j from rx / ry
i = (rx - bilinear_start.x + 0.5 * abl.gridSpacing.x) / abl.gridSpacing.x; i = (rx - bilinear_start.x + 0.5 * abl.gridSpacing.x) / abl.gridSpacing.x;
j = (ry - bilinear_start.y + 0.5 * abl.gridSpacing.y) / abl.gridSpacing.y; j = (ry - bilinear_start.y + 0.5 * abl.gridSpacing.y) / abl.gridSpacing.y;
LIMIT(i, 0, GRID_MAX_POINTS_X - 1); LIMIT(i, 0, (GRID_MAX_POINTS_X) - 1);
LIMIT(j, 0, GRID_MAX_POINTS_Y - 1); LIMIT(j, 0, (GRID_MAX_POINTS_Y) - 1);
} }
if (WITHIN(i, 0, GRID_MAX_POINTS_X - 1) && WITHIN(j, 0, GRID_MAX_POINTS_Y)) { if (WITHIN(i, 0, (GRID_MAX_POINTS_X) - 1) && WITHIN(j, 0, (GRID_MAX_POINTS_Y) - 1)) {
set_bed_leveling_enabled(false); set_bed_leveling_enabled(false);
z_values[i][j] = rz; z_values[i][j] = rz;
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate()); TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());

View file

@ -106,7 +106,7 @@ void GcodeSuite::G29() {
SET_SOFT_ENDSTOP_LOOSE(false); SET_SOFT_ENDSTOP_LOOSE(false);
} }
// If there's another point to sample, move there with optional lift. // If there's another point to sample, move there with optional lift.
if (mbl_probe_index < GRID_MAX_POINTS) { if (mbl_probe_index < (GRID_MAX_POINTS)) {
// Disable software endstops to allow manual adjustment // Disable software endstops to allow manual adjustment
// If G29 is left hanging without completion they won't be re-enabled! // If G29 is left hanging without completion they won't be re-enabled!
SET_SOFT_ENDSTOP_LOOSE(true); SET_SOFT_ENDSTOP_LOOSE(true);
@ -142,8 +142,8 @@ void GcodeSuite::G29() {
case MeshSet: case MeshSet:
if (parser.seenval('I')) { if (parser.seenval('I')) {
ix = parser.value_int(); ix = parser.value_int();
if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1)) { if (!WITHIN(ix, 0, (GRID_MAX_POINTS_X) - 1)) {
SERIAL_ECHOLNPAIR("I out of range (0-", GRID_MAX_POINTS_X - 1, ")"); SERIAL_ECHOLNPAIR("I out of range (0-", (GRID_MAX_POINTS_X) - 1, ")");
return; return;
} }
} }
@ -152,8 +152,8 @@ void GcodeSuite::G29() {
if (parser.seenval('J')) { if (parser.seenval('J')) {
iy = parser.value_int(); iy = parser.value_int();
if (!WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1)) { if (!WITHIN(iy, 0, (GRID_MAX_POINTS_Y) - 1)) {
SERIAL_ECHOLNPAIR("J out of range (0-", GRID_MAX_POINTS_Y - 1, ")"); SERIAL_ECHOLNPAIR("J out of range (0-", (GRID_MAX_POINTS_Y) - 1, ")");
return; return;
} }
} }

View file

@ -224,6 +224,11 @@
#endif #endif
#endif #endif
#ifdef GRID_MAX_POINTS_X
#define GRID_MAX_CELLS_X (GRID_MAX_POINTS_X - 1)
#define GRID_MAX_CELLS_Y (GRID_MAX_POINTS_Y - 1)
#endif
/** /**
* Host keep alive * Host keep alive
*/ */

View file

@ -1263,9 +1263,9 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
#elif ENABLED(DELTA_CALIBRATION_MENU) && !HAS_LCD_MENU #elif ENABLED(DELTA_CALIBRATION_MENU) && !HAS_LCD_MENU
#error "DELTA_CALIBRATION_MENU requires an LCD Controller." #error "DELTA_CALIBRATION_MENU requires an LCD Controller."
#elif ABL_USES_GRID #elif ABL_USES_GRID
#if (GRID_MAX_POINTS_X & 1) == 0 || (GRID_MAX_POINTS_Y & 1) == 0 #if ((GRID_MAX_POINTS_X) & 1) == 0 || ((GRID_MAX_POINTS_Y) & 1) == 0
#error "DELTA requires GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y to be odd numbers." #error "DELTA requires GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y to be odd numbers."
#elif GRID_MAX_POINTS_X < 3 #elif (GRID_MAX_POINTS_X) < 3
#error "DELTA requires GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y to be 3 or higher." #error "DELTA requires GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y to be 3 or higher."
#endif #endif
#endif #endif
@ -1518,7 +1518,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
// Mesh Bed Leveling // Mesh Bed Leveling
#if ENABLED(DELTA) #if ENABLED(DELTA)
#error "MESH_BED_LEVELING is not compatible with DELTA printers." #error "MESH_BED_LEVELING is not compatible with DELTA printers."
#elif GRID_MAX_POINTS_X > 9 || GRID_MAX_POINTS_Y > 9 #elif (GRID_MAX_POINTS_X) > 9 || (GRID_MAX_POINTS_Y) > 9
#error "GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y must be less than 10 for MBL." #error "GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y must be less than 10 for MBL."
#endif #endif

View file

@ -1279,7 +1279,7 @@ void MarlinUI::draw_status_screen() {
pixels_per_x_mesh_pnt, pixels_per_y_mesh_pnt, pixels_per_x_mesh_pnt, pixels_per_y_mesh_pnt,
suppress_x_offset = 0, suppress_y_offset = 0; suppress_x_offset = 0, suppress_y_offset = 0;
const uint8_t y_plot_inv = (GRID_MAX_POINTS_Y - 1) - y_plot; const uint8_t y_plot_inv = (GRID_MAX_POINTS_Y) - 1 - y_plot;
upper_left.column = 0; upper_left.column = 0;
upper_left.row = 0; upper_left.row = 0;

View file

@ -529,7 +529,7 @@ void MarlinUI::clear_lcd() { } // Automatically cleared by Picture Loop
// Fill in the Specified Mesh Point // Fill in the Specified Mesh Point
const uint8_t y_plot_inv = (GRID_MAX_POINTS_Y - 1) - y_plot; // The origin is typically in the lower right corner. We need to const uint8_t y_plot_inv = (GRID_MAX_POINTS_Y) - 1 - y_plot; // The origin is typically in the lower right corner. We need to
// invert the Y to get it to plot in the right location. // invert the Y to get it to plot in the right location.
const u8g_uint_t by = y_offset + y_plot_inv * pixels_per_y_mesh_pnt; const u8g_uint_t by = y_offset + y_plot_inv * pixels_per_y_mesh_pnt;

View file

@ -53,12 +53,12 @@ constexpr static float gaugeThickness = 0.25;
#endif #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 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) (data ? data[X][Y] : 0)
#define ISVAL(X,Y) (data ? !isnan(VALUE(X,Y)) : true) #define ISVAL(X,Y) (data ? !isnan(VALUE(X,Y)) : true)
#define HEIGHT(X,Y) (ISVAL(X,Y) ? (VALUE(X,Y) - val_min) * scale_z : 0) #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

View file

@ -859,7 +859,7 @@ namespace ExtUI {
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_t zoff) { void setMeshPoint(const xy_uint8_t &pos, const_float_t zoff) {
if (WITHIN(pos.x, 0, GRID_MAX_POINTS_X) && WITHIN(pos.y, 0, GRID_MAX_POINTS_Y)) { if (WITHIN(pos.x, 0, (GRID_MAX_POINTS_X) - 1) && WITHIN(pos.y, 0, (GRID_MAX_POINTS_Y) - 1)) {
Z_VALUES(pos.x, pos.y) = zoff; Z_VALUES(pos.x, pos.y) = zoff;
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate()); TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());
} }

View file

@ -214,8 +214,8 @@
static uint8_t xind, yind; // =0 static uint8_t xind, yind; // =0
START_MENU(); START_MENU();
BACK_ITEM(MSG_BED_LEVELING); BACK_ITEM(MSG_BED_LEVELING);
EDIT_ITEM(uint8, MSG_MESH_X, &xind, 0, GRID_MAX_POINTS_X - 1); EDIT_ITEM(uint8, MSG_MESH_X, &xind, 0, (GRID_MAX_POINTS_X) - 1);
EDIT_ITEM(uint8, MSG_MESH_Y, &yind, 0, GRID_MAX_POINTS_Y - 1); EDIT_ITEM(uint8, MSG_MESH_Y, &yind, 0, (GRID_MAX_POINTS_Y) - 1);
EDIT_ITEM_FAST(float43, MSG_MESH_EDIT_Z, &Z_VALUES(xind, yind), -(LCD_PROBE_Z_RANGE) * 0.5, (LCD_PROBE_Z_RANGE) * 0.5, refresh_planner); EDIT_ITEM_FAST(float43, MSG_MESH_EDIT_Z, &Z_VALUES(xind, yind), -(LCD_PROBE_Z_RANGE) * 0.5, (LCD_PROBE_Z_RANGE) * 0.5, refresh_planner);
END_MENU(); END_MENU();
} }

View file

@ -484,8 +484,8 @@ void ubl_map_screen() {
#if IS_KINEMATIC #if IS_KINEMATIC
n_edit_pts = 9; // TODO: Delta accessible edit points n_edit_pts = 9; // TODO: Delta accessible edit points
#else #else
const bool xc = WITHIN(x, 1, GRID_MAX_POINTS_X - 2), const bool xc = WITHIN(x, 1, (GRID_MAX_POINTS_X) - 2),
yc = WITHIN(y, 1, GRID_MAX_POINTS_Y - 2); yc = WITHIN(y, 1, (GRID_MAX_POINTS_Y) - 2);
n_edit_pts = yc ? (xc ? 9 : 6) : (xc ? 6 : 4); // Corners n_edit_pts = yc ? (xc ? 9 : 6) : (xc ? 6 : 4); // Corners
#endif #endif

View file

@ -462,12 +462,12 @@ void MenuItem_confirm::draw_select_screen(PGM_P const yes, PGM_P const no, const
tft.set_background(COLOR_BACKGROUND); tft.set_background(COLOR_BACKGROUND);
tft.add_rectangle(0, 0, GRID_WIDTH, GRID_HEIGHT, COLOR_WHITE); tft.add_rectangle(0, 0, GRID_WIDTH, GRID_HEIGHT, COLOR_WHITE);
for (uint16_t x = 0; x < GRID_MAX_POINTS_X ; x++) for (uint16_t x = 0; x < (GRID_MAX_POINTS_X); x++)
for (uint16_t y = 0; y < GRID_MAX_POINTS_Y ; y++) for (uint16_t y = 0; y < (GRID_MAX_POINTS_Y); y++)
if (position_is_reachable({ ubl.mesh_index_to_xpos(x), ubl.mesh_index_to_ypos(y) })) if (position_is_reachable({ ubl.mesh_index_to_xpos(x), ubl.mesh_index_to_ypos(y) }))
tft.add_bar(1 + (x * 2 + 1) * (GRID_WIDTH - 4) / GRID_MAX_POINTS_X / 2, GRID_HEIGHT - 3 - ((y * 2 + 1) * (GRID_HEIGHT - 4) / GRID_MAX_POINTS_Y / 2), 2, 2, COLOR_UBL); tft.add_bar(1 + (x * 2 + 1) * (GRID_WIDTH - 4) / (GRID_MAX_POINTS_X) / 2, GRID_HEIGHT - 3 - ((y * 2 + 1) * (GRID_HEIGHT - 4) / (GRID_MAX_POINTS_Y) / 2), 2, 2, COLOR_UBL);
tft.add_rectangle((x_plot * 2 + 1) * (GRID_WIDTH - 4) / GRID_MAX_POINTS_X / 2 - 1, GRID_HEIGHT - 5 - ((y_plot * 2 + 1) * (GRID_HEIGHT - 4) / GRID_MAX_POINTS_Y / 2), 6, 6, COLOR_UBL); tft.add_rectangle((x_plot * 2 + 1) * (GRID_WIDTH - 4) / (GRID_MAX_POINTS_X) / 2 - 1, GRID_HEIGHT - 5 - ((y_plot * 2 + 1) * (GRID_HEIGHT - 4) / (GRID_MAX_POINTS_Y) / 2), 6, 6, COLOR_UBL);
const xy_pos_t pos = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) }, const xy_pos_t pos = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) },
lpos = pos.asLogical(); lpos = pos.asLogical();
@ -512,9 +512,9 @@ void MenuItem_confirm::draw_select_screen(PGM_P const yes, PGM_P const no, const
#if ENABLED(TOUCH_SCREEN) #if ENABLED(TOUCH_SCREEN)
touch.clear(); touch.clear();
draw_menu_navigation = false; draw_menu_navigation = false;
add_control(GRID_OFFSET_X + GRID_WIDTH + CONTROL_OFFSET, GRID_OFFSET_Y + CONTROL_OFFSET, UBL, ENCODER_STEPS_PER_MENU_ITEM * GRID_MAX_POINTS_X, imgUp); add_control(GRID_OFFSET_X + GRID_WIDTH + CONTROL_OFFSET, GRID_OFFSET_Y + CONTROL_OFFSET, UBL, (ENCODER_STEPS_PER_MENU_ITEM) * (GRID_MAX_POINTS_X), imgUp);
add_control(GRID_OFFSET_X + GRID_WIDTH + CONTROL_OFFSET, GRID_OFFSET_Y + GRID_HEIGHT - CONTROL_OFFSET - 32, UBL, - ENCODER_STEPS_PER_MENU_ITEM * GRID_MAX_POINTS_X, imgDown); add_control(GRID_OFFSET_X + GRID_WIDTH + CONTROL_OFFSET, GRID_OFFSET_Y + GRID_HEIGHT - CONTROL_OFFSET - 32, UBL, -(ENCODER_STEPS_PER_MENU_ITEM) * (GRID_MAX_POINTS_X), imgDown);
add_control(GRID_OFFSET_X + CONTROL_OFFSET, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, UBL, - ENCODER_STEPS_PER_MENU_ITEM, imgLeft); add_control(GRID_OFFSET_X + CONTROL_OFFSET, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, UBL, -(ENCODER_STEPS_PER_MENU_ITEM), imgLeft);
add_control(GRID_OFFSET_X + GRID_WIDTH - CONTROL_OFFSET - 32, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, UBL, ENCODER_STEPS_PER_MENU_ITEM, imgRight); add_control(GRID_OFFSET_X + GRID_WIDTH - CONTROL_OFFSET - 32, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, UBL, ENCODER_STEPS_PER_MENU_ITEM, imgRight);
add_control(224, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, CLICK, imgLeveling); add_control(224, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, CLICK, imgLeveling);
add_control(144, 206, BACK, imgBack); add_control(144, 206, BACK, imgBack);

View file

@ -462,12 +462,12 @@ void MenuItem_confirm::draw_select_screen(PGM_P const yes, PGM_P const no, const
tft.set_background(COLOR_BACKGROUND); tft.set_background(COLOR_BACKGROUND);
tft.add_rectangle(0, 0, GRID_WIDTH, GRID_HEIGHT, COLOR_WHITE); tft.add_rectangle(0, 0, GRID_WIDTH, GRID_HEIGHT, COLOR_WHITE);
for (uint16_t x = 0; x < GRID_MAX_POINTS_X ; x++) for (uint16_t x = 0; x < (GRID_MAX_POINTS_X); x++)
for (uint16_t y = 0; y < GRID_MAX_POINTS_Y ; y++) for (uint16_t y = 0; y < (GRID_MAX_POINTS_Y); y++)
if (position_is_reachable({ ubl.mesh_index_to_xpos(x), ubl.mesh_index_to_ypos(y) })) if (position_is_reachable({ ubl.mesh_index_to_xpos(x), ubl.mesh_index_to_ypos(y) }))
tft.add_bar(1 + (x * 2 + 1) * (GRID_WIDTH - 4) / GRID_MAX_POINTS_X / 2, GRID_HEIGHT - 3 - ((y * 2 + 1) * (GRID_HEIGHT - 4) / GRID_MAX_POINTS_Y / 2), 2, 2, COLOR_UBL); tft.add_bar(1 + (x * 2 + 1) * (GRID_WIDTH - 4) / (GRID_MAX_POINTS_X) / 2, GRID_HEIGHT - 3 - ((y * 2 + 1) * (GRID_HEIGHT - 4) / (GRID_MAX_POINTS_Y) / 2), 2, 2, COLOR_UBL);
tft.add_rectangle((x_plot * 2 + 1) * (GRID_WIDTH - 4) / GRID_MAX_POINTS_X / 2 - 1, GRID_HEIGHT - 5 - ((y_plot * 2 + 1) * (GRID_HEIGHT - 4) / GRID_MAX_POINTS_Y / 2), 6, 6, COLOR_UBL); tft.add_rectangle((x_plot * 2 + 1) * (GRID_WIDTH - 4) / (GRID_MAX_POINTS_X) / 2 - 1, GRID_HEIGHT - 5 - ((y_plot * 2 + 1) * (GRID_HEIGHT - 4) / (GRID_MAX_POINTS_Y) / 2), 6, 6, COLOR_UBL);
const xy_pos_t pos = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) }, const xy_pos_t pos = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) },
lpos = pos.asLogical(); lpos = pos.asLogical();
@ -512,9 +512,9 @@ void MenuItem_confirm::draw_select_screen(PGM_P const yes, PGM_P const no, const
#if ENABLED(TOUCH_SCREEN) #if ENABLED(TOUCH_SCREEN)
touch.clear(); touch.clear();
draw_menu_navigation = false; draw_menu_navigation = false;
add_control(GRID_OFFSET_X + GRID_WIDTH + CONTROL_OFFSET, GRID_OFFSET_Y + CONTROL_OFFSET, UBL, ENCODER_STEPS_PER_MENU_ITEM * GRID_MAX_POINTS_X, imgUp); add_control(GRID_OFFSET_X + GRID_WIDTH + CONTROL_OFFSET, GRID_OFFSET_Y + CONTROL_OFFSET, UBL, (ENCODER_STEPS_PER_MENU_ITEM) * (GRID_MAX_POINTS_X), imgUp);
add_control(GRID_OFFSET_X + GRID_WIDTH + CONTROL_OFFSET, GRID_OFFSET_Y + GRID_HEIGHT - CONTROL_OFFSET - 32, UBL, - ENCODER_STEPS_PER_MENU_ITEM * GRID_MAX_POINTS_X, imgDown); add_control(GRID_OFFSET_X + GRID_WIDTH + CONTROL_OFFSET, GRID_OFFSET_Y + GRID_HEIGHT - CONTROL_OFFSET - 32, UBL, -(ENCODER_STEPS_PER_MENU_ITEM) * (GRID_MAX_POINTS_X), imgDown);
add_control(GRID_OFFSET_X + CONTROL_OFFSET, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, UBL, - ENCODER_STEPS_PER_MENU_ITEM, imgLeft); add_control(GRID_OFFSET_X + CONTROL_OFFSET, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, UBL, -(ENCODER_STEPS_PER_MENU_ITEM), imgLeft);
add_control(GRID_OFFSET_X + GRID_WIDTH - CONTROL_OFFSET - 32, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, UBL, ENCODER_STEPS_PER_MENU_ITEM, imgRight); add_control(GRID_OFFSET_X + GRID_WIDTH - CONTROL_OFFSET - 32, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, UBL, ENCODER_STEPS_PER_MENU_ITEM, imgRight);
add_control(320, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, CLICK, imgLeveling); add_control(320, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, CLICK, imgLeveling);
add_control(224, TFT_HEIGHT - 34, BACK, imgBack); add_control(224, TFT_HEIGHT - 34, BACK, imgBack);

View file

@ -1605,7 +1605,7 @@ void MarlinSettings::postprocess() {
#if ENABLED(MESH_BED_LEVELING) #if ENABLED(MESH_BED_LEVELING)
if (!validating) mbl.z_offset = dummyf; if (!validating) mbl.z_offset = dummyf;
if (mesh_num_x == GRID_MAX_POINTS_X && mesh_num_y == GRID_MAX_POINTS_Y) { if (mesh_num_x == (GRID_MAX_POINTS_X) && mesh_num_y == (GRID_MAX_POINTS_Y)) {
// EEPROM data fits the current mesh // EEPROM data fits the current mesh
EEPROM_READ(mbl.z_values); EEPROM_READ(mbl.z_values);
} }
@ -1652,7 +1652,7 @@ void MarlinSettings::postprocess() {
EEPROM_READ_ALWAYS(grid_max_x); // 1 byte EEPROM_READ_ALWAYS(grid_max_x); // 1 byte
EEPROM_READ_ALWAYS(grid_max_y); // 1 byte EEPROM_READ_ALWAYS(grid_max_y); // 1 byte
#if ENABLED(AUTO_BED_LEVELING_BILINEAR) #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
if (grid_max_x == GRID_MAX_POINTS_X && grid_max_y == GRID_MAX_POINTS_Y) { if (grid_max_x == (GRID_MAX_POINTS_X) && grid_max_y == (GRID_MAX_POINTS_Y)) {
if (!validating) set_bed_leveling_enabled(false); if (!validating) set_bed_leveling_enabled(false);
EEPROM_READ(bilinear_grid_spacing); // 2 ints EEPROM_READ(bilinear_grid_spacing); // 2 ints
EEPROM_READ(bilinear_start); // 2 ints EEPROM_READ(bilinear_start); // 2 ints