G26 Hilbert Curve followup (#21480)

This commit is contained in:
Marcio T 2021-04-01 18:12:00 -06:00 committed by GitHub
parent 05b39623b0
commit 45c1432946
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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) {}
#endif
#if GRID_MAX_POINTS_X && GRID_MAX_POINTS_Y
#if (GRID_MAX_POINTS_X) && (GRID_MAX_POINTS_Y)
// 16x16 bit arrays
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
#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
#elif GRID_MAX_POINTS_Y < GRID_MAX_POINTS_X
#elif (GRID_MAX_POINTS_Y) < (GRID_MAX_POINTS_X)
#define HALF_IN_Y
#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() {
#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
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
xlen = ctrx1;
xend = ctrx1;
#endif
#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
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
ylen = ctry1;
yend = ctry1;
#endif
LOOP_LE_N(xo, xlen)
LOOP_LE_N(yo, ylen) {
LOOP_LE_N(xo, xend)
LOOP_LE_N(yo, yend) {
uint8_t x2 = ctrx2 + xo, y2 = ctry2 + yo;
#ifndef HALF_IN_X
const uint8_t x1 = ctrx1 - xo;
@ -143,8 +143,8 @@ void print_bilinear_leveling_grid() {
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
#define ABL_GRID_POINTS_VIRT_X (GRID_MAX_POINTS_X - 1) * (BILINEAR_SUBDIVISIONS) + 1
#define ABL_GRID_POINTS_VIRT_Y (GRID_MAX_POINTS_Y - 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_CELLS_Y * (BILINEAR_SUBDIVISIONS) + 1
#define ABL_TEMP_POINTS_X (GRID_MAX_POINTS_X + 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];
@ -161,7 +161,7 @@ void print_bilinear_leveling_grid() {
#define LINEAR_EXTRAPOLATION(E, I) ((E) * 2 - (I))
float bed_level_virt_coord(const uint8_t x, const uint8_t y) {
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.
// 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
@ -171,8 +171,8 @@ void print_bilinear_leveling_grid() {
}
if (!x || x == ABL_TEMP_POINTS_X - 1) {
if (x) {
ep = GRID_MAX_POINTS_X - 1;
ip = GRID_MAX_POINTS_X - 2;
ep = (GRID_MAX_POINTS_X) - 1;
ip = GRID_MAX_CELLS_X - 1;
}
if (WITHIN(y, 1, ABL_TEMP_POINTS_Y - 2))
return LINEAR_EXTRAPOLATION(
@ -187,8 +187,8 @@ void print_bilinear_leveling_grid() {
}
if (!y || y == ABL_TEMP_POINTS_Y - 1) {
if (y) {
ep = GRID_MAX_POINTS_Y - 1;
ip = GRID_MAX_POINTS_Y - 2;
ep = (GRID_MAX_POINTS_Y) - 1;
ip = GRID_MAX_CELLS_Y - 1;
}
if (WITHIN(x, 1, ABL_TEMP_POINTS_X - 2))
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) {
// 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;
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) {
@ -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) {
// 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);
const uint8_t grid_x = LROUND(constrain(float(pos.x - (MESH_MIN_X)) / (MESH_X_DIST), 0, (GRID_MAX_POINTS_X) - 1));
const uint8_t grid_y = LROUND(constrain(float(pos.y - (MESH_MIN_Y)) / (MESH_Y_DIST), 0, (GRID_MAX_POINTS_Y) - 1));
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) {
// Get current and destination cells for this line
xy_int8_t scel = cell_indexes(current_position), ecel = cell_indexes(destination);
NOMORE(scel.x, GRID_MAX_POINTS_X - 2);
NOMORE(scel.y, GRID_MAX_POINTS_Y - 2);
NOMORE(ecel.x, GRID_MAX_POINTS_X - 2);
NOMORE(ecel.y, GRID_MAX_POINTS_Y - 2);
NOMORE(scel.x, GRID_MAX_CELLS_X - 1);
NOMORE(scel.y, GRID_MAX_CELLS_Y - 1);
NOMORE(ecel.x, GRID_MAX_CELLS_X - 1);
NOMORE(ecel.y, GRID_MAX_CELLS_Y - 1);
// Start and end in the same cell? No split needed.
if (scel == ecel) {

View file

@ -32,8 +32,8 @@ enum MeshLevelingState : char {
MeshReset // G29 S5
};
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / float(GRID_MAX_POINTS_X - 1))
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / float(GRID_MAX_POINTS_Y - 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)) / (GRID_MAX_CELLS_Y))
#define _GET_MESH_X(I) mbl.index_to_xpos[I]
#define _GET_MESH_Y(J) mbl.index_to_ypos[J]
#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) {
px = 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) {
@ -72,11 +72,11 @@ public:
static int8_t cell_index_x(const_float_t x) {
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) {
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) {
return { cell_index_x(x), cell_index_y(y) };
@ -85,11 +85,11 @@ public:
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);
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) {
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) {
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);
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)
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)
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
if (human) SERIAL_CHAR(is_current ? ']' : ' ');

View file

@ -38,8 +38,8 @@ enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP };
struct mesh_index_pair;
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / float(GRID_MAX_POINTS_X - 1))
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / float(GRID_MAX_POINTS_Y - 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)) / (GRID_MAX_CELLS_Y))
#if ENABLED(OPTIMIZED_MESH_STORAGE)
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) {
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) {
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) {
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) {
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) {
@ -164,11 +164,11 @@ public:
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);
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) {
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) {
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.
*/
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 (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, ")");
}
@ -218,7 +218,7 @@ public:
const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * RECIPROCAL(MESH_X_DIST),
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
// z_values[][] array and no correction is applied.
}
@ -227,10 +227,10 @@ public:
// 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) {
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 (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, ")");
}
@ -241,7 +241,7 @@ public:
const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * RECIPROCAL(MESH_Y_DIST),
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
// z_values[][] array and no correction is applied.
}
@ -266,11 +266,11 @@ public:
const float z1 = calc_z0(rx0,
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,
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), 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]);
float z0 = calc_z0(ry0,
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 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) {
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

View file

@ -433,8 +433,7 @@ void unified_bed_leveling::G29() {
SERIAL_DECIMAL(param.XY_pos.y);
SERIAL_ECHOLNPGM(").\n");
}
const xy_pos_t near_probe_xy = param.XY_pos + probe.offset_xy;
probe_entire_mesh(near_probe_xy, parser.seen('T'), parser.seen('E'), parser.seen('U'));
probe_entire_mesh(param.XY_pos, parser.seen('T'), parser.seen('E'), parser.seen('U'));
report_current_position();
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 (!COORDINATE_OKAY(sx, X_MIN_BED, X_MAX_BED)) sx = X_CENTER;
if (!COORDINATE_OKAY(sy, Y_MIN_BED, Y_MAX_BED)) sy = Y_CENTER;
// (for UBL_HILBERT_CURVE default to lower-left corner instead)
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;

View file

@ -397,8 +397,8 @@
int8_t((raw.x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST)),
int8_t((raw.y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST))
};
LIMIT(icell.x, 0, (GRID_MAX_POINTS_X) - 1);
LIMIT(icell.y, 0, (GRID_MAX_POINTS_Y) - 1);
LIMIT(icell.x, 0, GRID_MAX_CELLS_X);
LIMIT(icell.y, 0, GRID_MAX_CELLS_Y);
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

View file

@ -113,6 +113,10 @@
#include "../../module/temperature.h"
#include "../../lcd/marlinui.h"
#if ENABLED(UBL_HILBERT_CURVE)
#include "../../feature/bedlevel/hilbert_curve.h"
#endif
#define EXTRUSION_MULTIPLIER 1.0
#define PRIME_LENGTH 10.0
#define OOZE_AMOUNT 0.3
@ -145,24 +149,9 @@
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;
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
/**
@ -178,52 +167,17 @@ int8_t g26_prime_flag;
#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) {
static float last_z = -999.99;
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_e_component = e_delta != 0.0;
destination = current_position;
const bool has_xy_component = dest != current_position, // Check if X or Y is involved in the movement.
has_e_component = e_delta != 0.0;
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
prepare_internal_move_to_destination(fr_mm_s);
}
@ -239,30 +193,53 @@ 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);
}
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 {
float extrusion_multiplier = EXTRUSION_MULTIPLIER,
retraction_multiplier = G26_RETRACT_MULTIPLIER,
layer_height = MESH_TEST_LAYER_HEIGHT,
prime_length = PRIME_LENGTH;
int16_t bed_temp = MESH_TEST_BED_TEMP,
hotend_temp = MESH_TEST_HOTEND_TEMP;
float nozzle = MESH_TEST_NOZZLE_SIZE,
filament_diameter = DEFAULT_NOMINAL_FILAMENT_DIA,
ooze_amount; // 'O' ... OOZE_AMOUNT
bool continue_with_closest, // 'C'
keep_heaters_on; // 'K'
xy_pos_t xy_pos; // = { 0, 0 }
int8_t prime_flag = 0;
bool g26_retracted = false; // Track the retracted state during G26 so mismatched
// retracts/recovers don't result in a bad state.
void retract_filament(const xyz_pos_t &where) {
if (!g26_retracted) { // Only retract if we are not already retracted!
g26_retracted = true;
move_to(where, -1.0f * g26_retraction_multiplier);
move_to(where, -1.0f * retraction_multiplier);
}
}
}
// TODO: Parameterize the Z lift with a define
void retract_lift_move(const xyz_pos_t &s) {
// TODO: Parameterize the Z lift with a define
void retract_lift_move(const xyz_pos_t &s) {
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
}
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
}
void recover_filament(const xyz_pos_t &where) {
void recover_filament(const xyz_pos_t &where) {
if (g26_retracted) { // Only un-retract if we are retracted.
move_to(where, 1.2f * g26_retraction_multiplier);
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.
@ -277,7 +254,7 @@ void recover_filament(const xyz_pos_t &where) {
* 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) {
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;
@ -296,90 +273,57 @@ void print_line_from_here_to_there(const xyz_pos_t &s, const xyz_pos_t &e) {
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 * g26_extrusion_multiplier;
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
}
}
inline bool look_for_lines_to_connect() {
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.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
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.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);
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);
}
}
vertical_mesh_line_flags.mark(i, j); // Mark done, even if skipped
}
}
}
}
}
return false;
}
/**
/**
* Turn on the bed and nozzle heat and
* wait for them to get up to temperature.
*/
inline bool turn_on_heaters() {
bool turn_on_heaters() {
SERIAL_ECHOLNPGM("Waiting for heatup.");
#if HAS_HEATED_BED
if (g26_bed_temp > 25) {
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(g26_bed_temp);
thermalManager.setTargetBed(bed_temp);
// Wait for the temperature to stabilize
if (!thermalManager.wait_for_bed(true
@ -390,6 +334,10 @@ inline bool turn_on_heaters() {
) return G26_ERR;
}
#else
UNUSED(bed_temp);
#endif // HAS_HEATED_BED
// Start heating the active nozzle
@ -397,7 +345,7 @@ inline bool turn_on_heaters() {
ui.set_status_P(GET_TEXT(MSG_G26_HEATING_NOZZLE), 99);
ui.quick_feedback();
#endif
thermalManager.setTargetHotend(g26_hotend_temp, active_extruder);
thermalManager.setTargetHotend(hotend_temp, active_extruder);
// Wait for the temperature to stabilize
if (!thermalManager.wait_for_hotend(active_extruder, true
@ -412,12 +360,12 @@ inline bool turn_on_heaters() {
#endif
return G26_OK;
}
}
/**
/**
* Prime the nozzle if needed. Return true on error.
*/
inline bool prime_nozzle() {
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
@ -425,7 +373,7 @@ inline bool prime_nozzle() {
float Total_Prime = 0.0;
#endif
if (g26_prime_flag == -1) { // The user wants to control how much filament gets purged
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();
@ -466,14 +414,72 @@ inline bool prime_nozzle() {
ui.quick_feedback();
#endif
destination = current_position;
destination.e += g26_prime_length;
destination.e += prime_length;
prepare_internal_move_to_destination(fr_slow_e);
destination.e -= g26_prime_length;
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.
@ -510,20 +516,11 @@ void GcodeSuite::G26() {
// Change the tool first, if specified
if (parser.seenval('T')) tool_change(parser.value_int());
g26_extrusion_multiplier = EXTRUSION_MULTIPLIER;
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;
g26_helper_t g26;
float g26_nozzle = MESH_TEST_NOZZLE_SIZE,
g26_filament_diameter = DEFAULT_NOMINAL_FILAMENT_DIA,
g26_ooze_amount = parser.linearval('O', OOZE_AMOUNT);
bool g26_continue_with_closest = parser.boolval('C'),
g26_keep_heaters_on = parser.boolval('K');
g26.ooze_amount = parser.linearval('O', OOZE_AMOUNT);
g26.continue_with_closest = parser.boolval('C');
g26.keep_heaters_on = parser.boolval('K');
// Accept 'I' if temperature presets are defined
#if PREHEAT_COUNT
@ -548,14 +545,14 @@ void GcodeSuite::G26() {
SERIAL_ECHOLNPAIR("?Specified bed temperature not plausible (40-", BED_MAX_TARGET, "C).");
return;
}
g26_bed_temp = bedtemp;
g26.bed_temp = bedtemp;
}
#endif // HAS_HEATED_BED
if (parser.seenval('L')) {
g26_layer_height = parser.value_linear_units();
if (!WITHIN(g26_layer_height, 0.0, 2.0)) {
g26.layer_height = parser.value_linear_units();
if (!WITHIN(g26.layer_height, 0.0, 2.0)) {
SERIAL_ECHOLNPGM("?Specified layer height not plausible.");
return;
}
@ -563,8 +560,8 @@ void GcodeSuite::G26() {
if (parser.seen('Q')) {
if (parser.has_value()) {
g26_retraction_multiplier = parser.value_float();
if (!WITHIN(g26_retraction_multiplier, 0.05, 15.0)) {
g26.retraction_multiplier = parser.value_float();
if (!WITHIN(g26.retraction_multiplier, 0.05, 15.0)) {
SERIAL_ECHOLNPGM("?Specified Retraction Multiplier not plausible.");
return;
}
@ -576,8 +573,8 @@ void GcodeSuite::G26() {
}
if (parser.seenval('S')) {
g26_nozzle = parser.value_float();
if (!WITHIN(g26_nozzle, 0.1, 2.0)) {
g26.nozzle = parser.value_float();
if (!WITHIN(g26.nozzle, 0.1, 2.0)) {
SERIAL_ECHOLNPGM("?Specified nozzle size not plausible.");
return;
}
@ -586,16 +583,16 @@ void GcodeSuite::G26() {
if (parser.seen('P')) {
if (!parser.has_value()) {
#if HAS_LCD_MENU
g26_prime_flag = -1;
g26.prime_flag = -1;
#else
SERIAL_ECHOLNPGM("?Prime length must be specified when not using an LCD.");
return;
#endif
}
else {
g26_prime_flag++;
g26_prime_length = parser.value_linear_units();
if (!WITHIN(g26_prime_length, 0.0, 25.0)) {
g26.prime_flag++;
g26.prime_length = parser.value_linear_units();
if (!WITHIN(g26.prime_length, 0.0, 25.0)) {
SERIAL_ECHOLNPGM("?Specified prime length not plausible.");
return;
}
@ -603,17 +600,17 @@ void GcodeSuite::G26() {
}
if (parser.seenval('F')) {
g26_filament_diameter = parser.value_linear_units();
if (!WITHIN(g26_filament_diameter, 1.0, 4.0)) {
g26.filament_diameter = parser.value_linear_units();
if (!WITHIN(g26.filament_diameter, 1.0, 4.0)) {
SERIAL_ECHOLNPGM("?Specified filament size not plausible.");
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
// 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'
celsius_t noztemp = 0;
@ -632,7 +629,7 @@ void GcodeSuite::G26() {
SERIAL_ECHOLNPGM("?Specified nozzle temperature not plausible.");
return;
}
g26_hotend_temp = noztemp;
g26.hotend_temp = noztemp;
}
// '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
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);
if (!position_is_reachable(g26_xy_pos)) {
if (!position_is_reachable(g26.xy_pos)) {
SERIAL_ECHOLNPGM("?Specified X,Y coordinate out of bounds.");
return;
}
@ -680,12 +677,12 @@ void GcodeSuite::G26() {
planner.calculate_volumetric_multipliers();
#endif
if (turn_on_heaters() != G26_OK) goto LEAVE;
if (g26.turn_on_heaters() != G26_OK) goto LEAVE;
current_position.e = 0.0;
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
@ -698,14 +695,12 @@ void GcodeSuite::G26() {
*/
circle_flags.reset();
horizontal_mesh_line_flags.reset();
vertical_mesh_line_flags.reset();
// Move nozzle to the specified height for the first layer
destination = current_position;
destination.z = g26_layer_height;
destination.z = g26.layer_height;
move_to(destination, 0.0);
move_to(destination, g26_ooze_amount);
move_to(destination, g26.ooze_amount);
TERN_(HAS_LCD_MENU, ui.capture());
@ -732,7 +727,7 @@ void GcodeSuite::G26() {
mesh_index_pair location;
do {
// 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()) {
const xy_pos_t circle = _GET_MESH_POS(location.pos);
@ -782,25 +777,24 @@ void GcodeSuite::G26() {
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 xyze_pos_t endpoint = {
e.x, e.y, g26_layer_height,
current_position.e + (arc_length * g26_e_axis_feedrate * g26_extrusion_multiplier)
e.x, e.y, g26.layer_height,
current_position.e + (arc_length * g26_e_axis_feedrate * g26.extrusion_multiplier)
};
if (dist_start > 2.0) {
s.z = g26_layer_height + 0.5f;
retract_lift_move(s);
s.z = g26.layer_height + 0.5f;
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
recover_filament(destination);
g26.recover_filament(destination);
const feedRate_t old_feedrate = feedrate_mm_s;
feedrate_mm_s = PLANNER_XY_FEEDRATE() * 0.1f;
{ REMEMBER(fr, feedrate_mm_s, PLANNER_XY_FEEDRATE() * 0.1f);
plan_arc(endpoint, arc_offset, false, 0); // Draw a counter-clockwise arc
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
@ -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
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 };
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 };
#if IS_KINEMATIC
// 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);
#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.
}
#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.
@ -857,13 +855,10 @@ void GcodeSuite::G26() {
LEAVE:
ui.set_status_P(GET_TEXT(MSG_G26_LEAVING), -1);
retract_filament(destination);
g26.retract_filament(destination);
destination.z = Z_CLEARANCE_BETWEEN_PROBES;
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)
parser.volumetric_enabled = volumetric_was_enabled;
planner.calculate_volumetric_multipliers();
@ -871,7 +866,7 @@ void GcodeSuite::G26() {
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));
thermalManager.setTargetHotend(active_extruder, 0);
}

View file

@ -68,8 +68,8 @@ void GcodeSuite::M420() {
y_min = probe.min_y(), y_max = probe.max_y();
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
bilinear_start.set(x_min, y_min);
bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_POINTS_X - 1),
(y_max - y_min) / (GRID_MAX_POINTS_Y - 1));
bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_CELLS_X),
(y_max - y_min) / (GRID_MAX_CELLS_Y));
#endif
GRID_LOOP(x, y) {
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
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;
LIMIT(i, 0, GRID_MAX_POINTS_X - 1);
LIMIT(j, 0, GRID_MAX_POINTS_Y - 1);
LIMIT(i, 0, (GRID_MAX_POINTS_X) - 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);
z_values[i][j] = rz;
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());

View file

@ -106,7 +106,7 @@ void GcodeSuite::G29() {
SET_SOFT_ENDSTOP_LOOSE(false);
}
// 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
// If G29 is left hanging without completion they won't be re-enabled!
SET_SOFT_ENDSTOP_LOOSE(true);
@ -142,8 +142,8 @@ void GcodeSuite::G29() {
case MeshSet:
if (parser.seenval('I')) {
ix = parser.value_int();
if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1)) {
SERIAL_ECHOLNPAIR("I out of range (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, ")");
return;
}
}
@ -152,8 +152,8 @@ void GcodeSuite::G29() {
if (parser.seenval('J')) {
iy = parser.value_int();
if (!WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1)) {
SERIAL_ECHOLNPAIR("J out of range (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, ")");
return;
}
}

View file

@ -224,6 +224,11 @@
#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
*/

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
#error "DELTA_CALIBRATION_MENU requires an LCD Controller."
#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."
#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."
#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
#if ENABLED(DELTA)
#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."
#endif

View file

@ -1279,7 +1279,7 @@ void MarlinUI::draw_status_screen() {
pixels_per_x_mesh_pnt, pixels_per_y_mesh_pnt,
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.row = 0;

View file

@ -529,7 +529,7 @@ void MarlinUI::clear_lcd() { } // Automatically cleared by Picture Loop
// 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.
const u8g_uint_t by = y_offset + y_plot_inv * pixels_per_y_mesh_pnt;

View file

@ -859,7 +859,7 @@ namespace ExtUI {
bed_mesh_t& getMeshArray() { return Z_VALUES_ARR; }
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) {
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;
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());
}

View file

@ -214,8 +214,8 @@
static uint8_t xind, yind; // =0
START_MENU();
BACK_ITEM(MSG_BED_LEVELING);
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_X, &xind, 0, (GRID_MAX_POINTS_X) - 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);
END_MENU();
}

View file

@ -484,8 +484,8 @@ void ubl_map_screen() {
#if IS_KINEMATIC
n_edit_pts = 9; // TODO: Delta accessible edit points
#else
const bool xc = WITHIN(x, 1, GRID_MAX_POINTS_X - 2),
yc = WITHIN(y, 1, GRID_MAX_POINTS_Y - 2);
const bool xc = WITHIN(x, 1, (GRID_MAX_POINTS_X) - 2),
yc = WITHIN(y, 1, (GRID_MAX_POINTS_Y) - 2);
n_edit_pts = yc ? (xc ? 9 : 6) : (xc ? 6 : 4); // Corners
#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.add_rectangle(0, 0, GRID_WIDTH, GRID_HEIGHT, COLOR_WHITE);
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 x = 0; x < (GRID_MAX_POINTS_X); x++)
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) }))
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) },
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)
touch.clear();
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 + 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 + 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 + 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(224, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, CLICK, imgLeveling);
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.add_rectangle(0, 0, GRID_WIDTH, GRID_HEIGHT, COLOR_WHITE);
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 x = 0; x < (GRID_MAX_POINTS_X); x++)
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) }))
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) },
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)
touch.clear();
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 + 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 + 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 + 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(320, GRID_OFFSET_Y + GRID_HEIGHT + CONTROL_OFFSET, CLICK, imgLeveling);
add_control(224, TFT_HEIGHT - 34, BACK, imgBack);

View file

@ -1605,7 +1605,7 @@ void MarlinSettings::postprocess() {
#if ENABLED(MESH_BED_LEVELING)
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_READ(mbl.z_values);
}
@ -1652,7 +1652,7 @@ void MarlinSettings::postprocess() {
EEPROM_READ_ALWAYS(grid_max_x); // 1 byte
EEPROM_READ_ALWAYS(grid_max_y); // 1 byte
#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);
EEPROM_READ(bilinear_grid_spacing); // 2 ints
EEPROM_READ(bilinear_start); // 2 ints