Add custom types for position (#15204)
This commit is contained in:
		| @@ -602,9 +602,7 @@ | ||||
| //#define Z_STEPPER_AUTO_ALIGN | ||||
| #if ENABLED(Z_STEPPER_AUTO_ALIGN) | ||||
|   // Define probe X and Y positions for Z1, Z2 [, Z3] | ||||
|   #define Z_STEPPER_ALIGN_X {  10, 150, 290 } | ||||
|   #define Z_STEPPER_ALIGN_Y { 290,  10, 290 } | ||||
|   // Set number of iterations to align | ||||
|   #define Z_STEPPER_ALIGN_XY { {  10, 290 }, { 150,  10 }, { 290, 290 } }  // Set number of iterations to align | ||||
|   #define Z_STEPPER_ALIGN_ITERATIONS 3 | ||||
|   // Enable to restore leveling setup after operation | ||||
|   #define RESTORE_LEVELING_AFTER_G34 | ||||
|   | ||||
| @@ -582,10 +582,10 @@ void manage_inactivity(const bool ignore_stepper_queue/*=false*/) { | ||||
|         } | ||||
|       #endif // !SWITCHING_EXTRUDER | ||||
|  | ||||
|       const float olde = current_position[E_AXIS]; | ||||
|       current_position[E_AXIS] += EXTRUDER_RUNOUT_EXTRUDE; | ||||
|       planner.buffer_line(current_position, MMM_TO_MMS(EXTRUDER_RUNOUT_SPEED), active_extruder); | ||||
|       current_position[E_AXIS] = olde; | ||||
|       const float olde = current_position.e; | ||||
|       current_position.e += EXTRUDER_RUNOUT_EXTRUDE; | ||||
|       line_to_current_position(MMM_TO_MMS(EXTRUDER_RUNOUT_SPEED)); | ||||
|       current_position.e = olde; | ||||
|       planner.set_e_position_mm(olde); | ||||
|       planner.synchronize(); | ||||
|  | ||||
| @@ -629,7 +629,7 @@ void manage_inactivity(const bool ignore_stepper_queue/*=false*/) { | ||||
|     if (delayed_move_time && ELAPSED(ms, delayed_move_time + 1000UL) && IsRunning()) { | ||||
|       // travel moves have been received so enact them | ||||
|       delayed_move_time = 0xFFFFFFFFUL; // force moves to be done | ||||
|       set_destination_from_current(); | ||||
|       destination = current_position; | ||||
|       prepare_move_to_destination(); | ||||
|     } | ||||
|   #endif | ||||
| @@ -1002,7 +1002,7 @@ void setup() { | ||||
|  | ||||
|   #if HAS_M206_COMMAND | ||||
|     // Initialize current position based on home_offset | ||||
|     LOOP_XYZ(a) current_position[a] += home_offset[a]; | ||||
|     current_position += home_offset; | ||||
|   #endif | ||||
|  | ||||
|   // Vital to init stepper/planner equivalent for current_position | ||||
|   | ||||
| @@ -1,63 +0,0 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| /** | ||||
|  * Axis indices as enumerated constants | ||||
|  * | ||||
|  *  - X_AXIS, Y_AXIS, and Z_AXIS should be used for axes in Cartesian space | ||||
|  *  - A_AXIS, B_AXIS, and C_AXIS should be used for Steppers, corresponding to XYZ on Cartesians | ||||
|  *  - X_HEAD, Y_HEAD, and Z_HEAD should be used for Steppers on Core kinematics | ||||
|  */ | ||||
| enum AxisEnum : unsigned char { | ||||
|   X_AXIS    = 0, | ||||
|   A_AXIS    = 0, | ||||
|   Y_AXIS    = 1, | ||||
|   B_AXIS    = 1, | ||||
|   Z_AXIS    = 2, | ||||
|   C_AXIS    = 2, | ||||
|   E_AXIS    = 3, | ||||
|   X_HEAD    = 4, | ||||
|   Y_HEAD    = 5, | ||||
|   Z_HEAD    = 6, | ||||
|   E0_AXIS   = 3, | ||||
|   E1_AXIS   = 4, | ||||
|   E2_AXIS   = 5, | ||||
|   E3_AXIS   = 6, | ||||
|   E4_AXIS   = 7, | ||||
|   E5_AXIS   = 8, | ||||
|   ALL_AXES  = 0xFE, | ||||
|   NO_AXIS   = 0xFF | ||||
| }; | ||||
|  | ||||
| #define LOOP_S_LE_N(VAR, S, N) for (uint8_t VAR=(S); VAR<=(N); VAR++) | ||||
| #define LOOP_S_L_N(VAR, S, N) for (uint8_t VAR=(S); VAR<(N); VAR++) | ||||
| #define LOOP_LE_N(VAR, N) LOOP_S_LE_N(VAR, 0, N) | ||||
| #define LOOP_L_N(VAR, N) LOOP_S_L_N(VAR, 0, N) | ||||
|  | ||||
| #define LOOP_NA(VAR) LOOP_L_N(VAR, NUM_AXIS) | ||||
| #define LOOP_XYZ(VAR) LOOP_S_LE_N(VAR, X_AXIS, Z_AXIS) | ||||
| #define LOOP_XYZE(VAR) LOOP_S_LE_N(VAR, X_AXIS, E_AXIS) | ||||
| #define LOOP_XYZE_N(VAR) LOOP_S_L_N(VAR, X_AXIS, XYZE_N) | ||||
| #define LOOP_ABC(VAR) LOOP_S_LE_N(VAR, A_AXIS, C_AXIS) | ||||
| #define LOOP_ABCE(VAR) LOOP_S_LE_N(VAR, A_AXIS, E_AXIS) | ||||
| #define LOOP_ABCE_N(VAR) LOOP_S_L_N(VAR, A_AXIS, XYZE_N) | ||||
| @@ -26,6 +26,7 @@ | ||||
| #define XYZE 4 | ||||
| #define ABC  3 | ||||
| #define XYZ  3 | ||||
| #define XY   2 | ||||
|  | ||||
| #define _AXIS(A) (A##_AXIS) | ||||
|  | ||||
| @@ -252,12 +253,6 @@ | ||||
| #define DECREMENT_(n) DEC_##n | ||||
| #define DECREMENT(n) DECREMENT_(n) | ||||
|  | ||||
| // Feedrate | ||||
| typedef float feedRate_t; | ||||
| #define MMM_TO_MMS(MM_M) ((MM_M)/60.0f) | ||||
| #define MMS_TO_MMM(MM_S) ((MM_S)*60.0f) | ||||
| #define MMS_SCALED(V)    ((V) * 0.01f * feedrate_percentage) | ||||
|  | ||||
| #define NOOP (void(0)) | ||||
|  | ||||
| #define CEILING(x,y) (((x) + (y) - 1) / (y)) | ||||
|   | ||||
| @@ -22,7 +22,6 @@ | ||||
|  | ||||
| #include "serial.h" | ||||
| #include "language.h" | ||||
| #include "enum.h" | ||||
|  | ||||
| uint8_t marlin_debug_flags = MARLIN_DEBUG_NONE; | ||||
|  | ||||
| @@ -68,12 +67,8 @@ void print_bin(const uint16_t val) { | ||||
|   } | ||||
| } | ||||
|  | ||||
| void print_xyz(PGM_P const prefix, PGM_P const suffix, const float &x, const float &y, const float &z) { | ||||
| void print_xyz(const float &x, const float &y, const float &z, PGM_P const prefix/*=nullptr*/, PGM_P const suffix/*=nullptr*/) { | ||||
|   serialprintPGM(prefix); | ||||
|   SERIAL_ECHOPAIR(" " MSG_X, x, " " MSG_Y, y, " " MSG_Z, z); | ||||
|   if (suffix) serialprintPGM(suffix); else SERIAL_EOL(); | ||||
| } | ||||
|  | ||||
| void print_xyz(PGM_P const prefix, PGM_P const suffix, const float xyz[]) { | ||||
|   print_xyz(prefix, suffix, xyz[X_AXIS], xyz[Y_AXIS], xyz[Z_AXIS]); | ||||
| } | ||||
|   | ||||
| @@ -213,7 +213,11 @@ void serial_spaces(uint8_t count); | ||||
|  | ||||
| void print_bin(const uint16_t val); | ||||
|  | ||||
| void print_xyz(PGM_P const prefix, PGM_P const suffix, const float xyz[]); | ||||
| void print_xyz(PGM_P const prefix, PGM_P const suffix, const float &x, const float &y, const float &z); | ||||
| #define SERIAL_POS(SUFFIX,VAR) do { print_xyz(PSTR("  " STRINGIFY(VAR) "="), PSTR(" : " SUFFIX "\n"), VAR); }while(0) | ||||
| #define SERIAL_XYZ(PREFIX,V...) do { print_xyz(PSTR(PREFIX), nullptr, V); }while(0) | ||||
| void print_xyz(const float &x, const float &y, const float &z, PGM_P const prefix=nullptr, PGM_P const suffix=nullptr); | ||||
|  | ||||
| inline void print_xyz(const xyz_pos_t &xyz, PGM_P const prefix=nullptr, PGM_P const suffix=nullptr) { | ||||
|   print_xyz(xyz.x, xyz.y, xyz.z, prefix, suffix); | ||||
| } | ||||
|  | ||||
| #define SERIAL_POS(SUFFIX,VAR) do { print_xyz(VAR, PSTR("  " STRINGIFY(VAR) "="), PSTR(" : " SUFFIX "\n")); }while(0) | ||||
| #define SERIAL_XYZ(PREFIX,V...) do { print_xyz(V, PSTR(PREFIX), nullptr); }while(0) | ||||
|   | ||||
							
								
								
									
										486
									
								
								Marlin/src/core/types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										486
									
								
								Marlin/src/core/types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,486 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <math.h> | ||||
| #include <stddef.h> | ||||
|  | ||||
| #include "millis_t.h" | ||||
|  | ||||
| // | ||||
| // Enumerated axis indices | ||||
| // | ||||
| //  - X_AXIS, Y_AXIS, and Z_AXIS should be used for axes in Cartesian space | ||||
| //  - A_AXIS, B_AXIS, and C_AXIS should be used for Steppers, corresponding to XYZ on Cartesians | ||||
| //  - X_HEAD, Y_HEAD, and Z_HEAD should be used for Steppers on Core kinematics | ||||
| // | ||||
| enum AxisEnum : uint8_t { | ||||
|   X_AXIS   = 0, A_AXIS = 0, | ||||
|   Y_AXIS   = 1, B_AXIS = 1, | ||||
|   Z_AXIS   = 2, C_AXIS = 2, | ||||
|   E_AXIS   = 3, | ||||
|   X_HEAD   = 4, Y_HEAD = 5, Z_HEAD = 6, | ||||
|   E0_AXIS  = 3, | ||||
|   E1_AXIS  = 4, | ||||
|   E2_AXIS  = 5, | ||||
|   E3_AXIS  = 6, | ||||
|   E4_AXIS  = 7, | ||||
|   E5_AXIS  = 8, | ||||
|   ALL_AXES = 0xFE, NO_AXIS = 0xFF | ||||
| }; | ||||
|  | ||||
| // | ||||
| // Loop over XYZE axes | ||||
| // | ||||
|  | ||||
| #define LOOP_S_LE_N(VAR, S, N) for (uint8_t VAR=(S); VAR<=(N); VAR++) | ||||
| #define LOOP_S_L_N(VAR, S, N) for (uint8_t VAR=(S); VAR<(N); VAR++) | ||||
| #define LOOP_LE_N(VAR, N) LOOP_S_LE_N(VAR, 0, N) | ||||
| #define LOOP_L_N(VAR, N) LOOP_S_L_N(VAR, 0, N) | ||||
|  | ||||
| #define LOOP_XYZ(VAR) LOOP_S_LE_N(VAR, X_AXIS, Z_AXIS) | ||||
| #define LOOP_XYZE(VAR) LOOP_S_LE_N(VAR, X_AXIS, E_AXIS) | ||||
| #define LOOP_XYZE_N(VAR) LOOP_S_L_N(VAR, X_AXIS, XYZE_N) | ||||
| #define LOOP_ABC(VAR) LOOP_S_LE_N(VAR, A_AXIS, C_AXIS) | ||||
| #define LOOP_ABCE(VAR) LOOP_S_LE_N(VAR, A_AXIS, E_AXIS) | ||||
| #define LOOP_ABCE_N(VAR) LOOP_S_L_N(VAR, A_AXIS, XYZE_N) | ||||
|  | ||||
| // | ||||
| // Conditional type assignment magic. For example... | ||||
| // | ||||
| // typename IF<(MYOPT==12), int, float>::type myvar; | ||||
| // | ||||
| template <bool, class L, class R> | ||||
| struct IF { typedef R type; }; | ||||
| template <class L, class R> | ||||
| struct IF<true, L, R> { typedef L type; }; | ||||
|  | ||||
| // | ||||
| // feedRate_t is just a humble float | ||||
| // | ||||
| typedef float feedRate_t; | ||||
|  | ||||
| // Conversion macros | ||||
| #define MMM_TO_MMS(MM_M) feedRate_t(float(MM_M) / 60.0f) | ||||
| #define MMS_TO_MMM(MM_S) (float(MM_S) * 60.0f) | ||||
| #define MMS_SCALED(V)    ((V) * 0.01f * feedrate_percentage) | ||||
|  | ||||
| // | ||||
| // Coordinates structures for XY, XYZ, XYZE... | ||||
| // | ||||
|  | ||||
| // Helpers | ||||
| #define _RECIP(N) ((N) ? 1.0f / float(N) : 0.0f) | ||||
| #define _ABS(N) ((N) < 0 ? -(N) : (N)) | ||||
| #define _LS(N)  (N = (T)(uint32_t(N) << v)) | ||||
| #define _RS(N)  (N = (T)(uint32_t(N) >> v)) | ||||
| #define FI FORCE_INLINE | ||||
|  | ||||
| // Forward declarations | ||||
| template<typename T> struct XYval; | ||||
| template<typename T> struct XYZval; | ||||
| template<typename T> struct XYZEval; | ||||
|  | ||||
| typedef struct XYval<bool>          xy_bool_t; | ||||
| typedef struct XYZval<bool>        xyz_bool_t; | ||||
| typedef struct XYZEval<bool>      xyze_bool_t; | ||||
|  | ||||
| typedef struct XYval<char>          xy_char_t; | ||||
| typedef struct XYZval<char>        xyz_char_t; | ||||
| typedef struct XYZEval<char>      xyze_char_t; | ||||
|  | ||||
| typedef struct XYval<unsigned char>     xy_uchar_t; | ||||
| typedef struct XYZval<unsigned char>   xyz_uchar_t; | ||||
| typedef struct XYZEval<unsigned char> xyze_uchar_t; | ||||
|  | ||||
| typedef struct XYval<int8_t>        xy_int8_t; | ||||
| typedef struct XYZval<int8_t>      xyz_int8_t; | ||||
| typedef struct XYZEval<int8_t>    xyze_int8_t; | ||||
|  | ||||
| typedef struct XYval<uint8_t>      xy_uint8_t; | ||||
| typedef struct XYZval<uint8_t>    xyz_uint8_t; | ||||
| typedef struct XYZEval<uint8_t>  xyze_uint8_t; | ||||
|  | ||||
| typedef struct XYval<int16_t>        xy_int_t; | ||||
| typedef struct XYZval<int16_t>      xyz_int_t; | ||||
| typedef struct XYZEval<int16_t>    xyze_int_t; | ||||
|  | ||||
| typedef struct XYval<uint16_t>      xy_uint_t; | ||||
| typedef struct XYZval<uint16_t>    xyz_uint_t; | ||||
| typedef struct XYZEval<uint16_t>  xyze_uint_t; | ||||
|  | ||||
| typedef struct XYval<int32_t>       xy_long_t; | ||||
| typedef struct XYZval<int32_t>     xyz_long_t; | ||||
| typedef struct XYZEval<int32_t>   xyze_long_t; | ||||
|  | ||||
| typedef struct XYval<uint32_t>     xy_ulong_t; | ||||
| typedef struct XYZval<uint32_t>   xyz_ulong_t; | ||||
| typedef struct XYZEval<uint32_t> xyze_ulong_t; | ||||
|  | ||||
| typedef struct XYZval<volatile int32_t>   xyz_vlong_t; | ||||
| typedef struct XYZEval<volatile int32_t> xyze_vlong_t; | ||||
|  | ||||
| typedef struct XYval<float>        xy_float_t; | ||||
| typedef struct XYZval<float>      xyz_float_t; | ||||
| typedef struct XYZEval<float>    xyze_float_t; | ||||
|  | ||||
| typedef struct XYval<feedRate_t>     xy_feedrate_t; | ||||
| typedef struct XYZval<feedRate_t>   xyz_feedrate_t; | ||||
| typedef struct XYZEval<feedRate_t> xyze_feedrate_t; | ||||
|  | ||||
| typedef xy_uint8_t xy_byte_t; | ||||
| typedef xyz_uint8_t xyz_byte_t; | ||||
| typedef xyze_uint8_t xyze_byte_t; | ||||
|  | ||||
| typedef xyz_long_t abc_long_t; | ||||
| typedef xyze_long_t abce_long_t; | ||||
| typedef xyz_ulong_t abc_ulong_t; | ||||
| typedef xyze_ulong_t abce_ulong_t; | ||||
|  | ||||
| typedef xy_float_t xy_pos_t; | ||||
| typedef xyz_float_t xyz_pos_t; | ||||
| typedef xyze_float_t xyze_pos_t; | ||||
|  | ||||
| typedef xy_float_t ab_float_t; | ||||
| typedef xyz_float_t abc_float_t; | ||||
| typedef xyze_float_t abce_float_t; | ||||
|  | ||||
| typedef ab_float_t ab_pos_t; | ||||
| typedef abc_float_t abc_pos_t; | ||||
| typedef abce_float_t abce_pos_t; | ||||
|  | ||||
| // External conversion methods | ||||
| void toLogical(xy_pos_t &raw); | ||||
| void toLogical(xyz_pos_t &raw); | ||||
| void toLogical(xyze_pos_t &raw); | ||||
| void toNative(xy_pos_t &raw); | ||||
| void toNative(xyz_pos_t &raw); | ||||
| void toNative(xyze_pos_t &raw); | ||||
|  | ||||
| // | ||||
| // XY coordinates, counters, etc. | ||||
| // | ||||
| template<typename T> | ||||
| struct XYval { | ||||
|   union { | ||||
|     struct { T x, y; }; | ||||
|     struct { T a, b; }; | ||||
|     T pos[2]; | ||||
|   }; | ||||
|   FI void set(const T px)                               { x = px; } | ||||
|   FI void set(const T px, const T py)                   { x = px; y = py; } | ||||
|   FI void reset()                                       { x = y = 0; } | ||||
|   FI T magnitude()                                const { return (T)sqrtf(x*x + y*y); } | ||||
|   FI operator T* ()                                     { return pos; } | ||||
|   FI operator bool()                                    { return x || y; } | ||||
|   FI XYval<T>           copy()                    const { return *this; } | ||||
|   FI XYval<T>            ABS()                    const { return { T(_ABS(x)), T(_ABS(y)) }; } | ||||
|   FI XYval<int16_t>    asInt()                          { return { int16_t(x), int16_t(y) }; } | ||||
|   FI XYval<int16_t>    asInt()                    const { return { int16_t(x), int16_t(y) }; } | ||||
|   FI XYval<int32_t>   asLong()                          { return { int32_t(x), int32_t(y) }; } | ||||
|   FI XYval<int32_t>   asLong()                    const { return { int32_t(x), int32_t(y) }; } | ||||
|   FI XYval<float>    asFloat()                          { return {   float(x),   float(y) }; } | ||||
|   FI XYval<float>    asFloat()                    const { return {   float(x),   float(y) }; } | ||||
|   FI XYval<float> reciprocal()                    const { return {  _RECIP(x),  _RECIP(y) }; } | ||||
|   FI XYval<float>  asLogical()                    const { XYval<float> o = asFloat(); toLogical(o); return o; } | ||||
|   FI XYval<float>   asNative()                    const { XYval<float> o = asFloat(); toNative(o);  return o; } | ||||
|   FI operator XYZval<T>()                               { return { x, y }; } | ||||
|   FI operator XYZval<T>()                         const { return { x, y }; } | ||||
|   FI operator XYZEval<T>()                              { return { x, y }; } | ||||
|   FI operator XYZEval<T>()                        const { return { x, y }; } | ||||
|   FI       T&  operator[](const int i)                  { return pos[i]; } | ||||
|   FI const T&  operator[](const int i)            const { return pos[i]; } | ||||
|   FI XYval<T>& operator= (const T v)                    { set(v,    v   ); return *this; } | ||||
|   FI XYval<T>& operator= (const XYZval<T>  &rs)         { set(rs.x, rs.y); return *this; } | ||||
|   FI XYval<T>& operator= (const XYZEval<T> &rs)         { set(rs.x, rs.y); return *this; } | ||||
|   FI XYval<T>  operator+ (const XYval<T>   &rs)   const { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; } | ||||
|   FI XYval<T>  operator+ (const XYval<T>   &rs)         { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; } | ||||
|   FI XYval<T>  operator- (const XYval<T>   &rs)   const { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; } | ||||
|   FI XYval<T>  operator- (const XYval<T>   &rs)         { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; } | ||||
|   FI XYval<T>  operator* (const XYval<T>   &rs)   const { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; } | ||||
|   FI XYval<T>  operator* (const XYval<T>   &rs)         { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; } | ||||
|   FI XYval<T>  operator/ (const XYval<T>   &rs)   const { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; } | ||||
|   FI XYval<T>  operator/ (const XYval<T>   &rs)         { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; } | ||||
|   FI XYval<T>  operator+ (const XYZval<T>  &rs)   const { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; } | ||||
|   FI XYval<T>  operator+ (const XYZval<T>  &rs)         { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; } | ||||
|   FI XYval<T>  operator- (const XYZval<T>  &rs)   const { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; } | ||||
|   FI XYval<T>  operator- (const XYZval<T>  &rs)         { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; } | ||||
|   FI XYval<T>  operator* (const XYZval<T>  &rs)   const { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; } | ||||
|   FI XYval<T>  operator* (const XYZval<T>  &rs)         { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; } | ||||
|   FI XYval<T>  operator/ (const XYZval<T>  &rs)   const { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; } | ||||
|   FI XYval<T>  operator/ (const XYZval<T>  &rs)         { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; } | ||||
|   FI XYval<T>  operator+ (const XYZEval<T> &rs)   const { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; } | ||||
|   FI XYval<T>  operator+ (const XYZEval<T> &rs)         { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; } | ||||
|   FI XYval<T>  operator- (const XYZEval<T> &rs)   const { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; } | ||||
|   FI XYval<T>  operator- (const XYZEval<T> &rs)         { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; } | ||||
|   FI XYval<T>  operator* (const XYZEval<T> &rs)   const { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; } | ||||
|   FI XYval<T>  operator* (const XYZEval<T> &rs)         { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; } | ||||
|   FI XYval<T>  operator/ (const XYZEval<T> &rs)   const { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; } | ||||
|   FI XYval<T>  operator/ (const XYZEval<T> &rs)         { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; } | ||||
|   FI XYval<T>  operator* (const float &v)         const { XYval<T> ls = *this; ls.x *= v;    ls.y *= v;    return ls; } | ||||
|   FI XYval<T>  operator* (const float &v)               { XYval<T> ls = *this; ls.x *= v;    ls.y *= v;    return ls; } | ||||
|   FI XYval<T>  operator* (const int &v)           const { XYval<T> ls = *this; ls.x *= v;    ls.y *= v;    return ls; } | ||||
|   FI XYval<T>  operator* (const int &v)                 { XYval<T> ls = *this; ls.x *= v;    ls.y *= v;    return ls; } | ||||
|   FI XYval<T>  operator/ (const float &v)         const { XYval<T> ls = *this; ls.x /= v;    ls.y /= v;    return ls; } | ||||
|   FI XYval<T>  operator/ (const float &v)               { XYval<T> ls = *this; ls.x /= v;    ls.y /= v;    return ls; } | ||||
|   FI XYval<T>  operator/ (const int &v)           const { XYval<T> ls = *this; ls.x /= v;    ls.y /= v;    return ls; } | ||||
|   FI XYval<T>  operator/ (const int &v)                 { XYval<T> ls = *this; ls.x /= v;    ls.y /= v;    return ls; } | ||||
|   FI XYval<T>  operator>>(const int &v)           const { XYval<T> ls = *this; _RS(ls.x);    _RS(ls.y);    return ls; } | ||||
|   FI XYval<T>  operator>>(const int &v)                 { XYval<T> ls = *this; _RS(ls.x);    _RS(ls.y);    return ls; } | ||||
|   FI XYval<T>  operator<<(const int &v)           const { XYval<T> ls = *this; _LS(ls.x);    _LS(ls.y);    return ls; } | ||||
|   FI XYval<T>  operator<<(const int &v)                 { XYval<T> ls = *this; _LS(ls.x);    _LS(ls.y);    return ls; } | ||||
|   FI XYval<T>& operator+=(const XYval<T>   &rs)         { x += rs.x; y += rs.y; return *this; } | ||||
|   FI XYval<T>& operator-=(const XYval<T>   &rs)         { x -= rs.x; y -= rs.y; return *this; } | ||||
|   FI XYval<T>& operator*=(const XYval<T>   &rs)         { x *= rs.x; y *= rs.y; return *this; } | ||||
|   FI XYval<T>& operator+=(const XYZval<T>  &rs)         { x += rs.x; y += rs.y; return *this; } | ||||
|   FI XYval<T>& operator-=(const XYZval<T>  &rs)         { x -= rs.x; y -= rs.y; return *this; } | ||||
|   FI XYval<T>& operator*=(const XYZval<T>  &rs)         { x *= rs.x; y *= rs.y; return *this; } | ||||
|   FI XYval<T>& operator+=(const XYZEval<T> &rs)         { x += rs.x; y += rs.y; return *this; } | ||||
|   FI XYval<T>& operator-=(const XYZEval<T> &rs)         { x -= rs.x; y -= rs.y; return *this; } | ||||
|   FI XYval<T>& operator*=(const XYZEval<T> &rs)         { x *= rs.x; y *= rs.y; return *this; } | ||||
|   FI XYval<T>& operator*=(const float &v)               { x *= v;    y *= v;    return *this; } | ||||
|   FI XYval<T>& operator*=(const int &v)                 { x *= v;    y *= v;    return *this; } | ||||
|   FI XYval<T>& operator>>=(const int &v)                { _RS(x);    _RS(y);    return *this; } | ||||
|   FI XYval<T>& operator<<=(const int &v)                { _LS(x);    _LS(y);    return *this; } | ||||
|   FI bool      operator==(const XYval<T>   &rs)         { return x == rs.x && y == rs.y; } | ||||
|   FI bool      operator==(const XYZval<T>  &rs)         { return x == rs.x && y == rs.y; } | ||||
|   FI bool      operator==(const XYZEval<T> &rs)         { return x == rs.x && y == rs.y; } | ||||
|   FI bool      operator==(const XYval<T>   &rs)   const { return x == rs.x && y == rs.y; } | ||||
|   FI bool      operator==(const XYZval<T>  &rs)   const { return x == rs.x && y == rs.y; } | ||||
|   FI bool      operator==(const XYZEval<T> &rs)   const { return x == rs.x && y == rs.y; } | ||||
|   FI bool      operator!=(const XYval<T>   &rs)         { return !operator==(rs); } | ||||
|   FI bool      operator!=(const XYZval<T>  &rs)         { return !operator==(rs); } | ||||
|   FI bool      operator!=(const XYZEval<T> &rs)         { return !operator==(rs); } | ||||
|   FI bool      operator!=(const XYval<T>   &rs)   const { return !operator==(rs); } | ||||
|   FI bool      operator!=(const XYZval<T>  &rs)   const { return !operator==(rs); } | ||||
|   FI bool      operator!=(const XYZEval<T> &rs)   const { return !operator==(rs); } | ||||
|   FI XYval<T>       operator-()                         { XYval<T> o = *this; o.x = -x; o.y = -y; return o; } | ||||
|   FI const XYval<T> operator-()                   const { XYval<T> o = *this; o.x = -x; o.y = -y; return o; } | ||||
| }; | ||||
|  | ||||
| // | ||||
| // XYZ coordinates, counters, etc. | ||||
| // | ||||
| template<typename T> | ||||
| struct XYZval { | ||||
|   union { | ||||
|     struct { T x, y, z; }; | ||||
|     struct { T a, b, c; }; | ||||
|     T pos[3]; | ||||
|   }; | ||||
|   FI void set(const T px)                              { x = px; } | ||||
|   FI void set(const T px, const T py)                  { x = px; y = py; } | ||||
|   FI void set(const T px, const T py, const T pz)      { x = px; y = py; z = pz; } | ||||
|   FI void set(const XYval<T> pxy, const T pz)          { x = pxy.x; y = pxy.y; z = pz; } | ||||
|   FI void reset()                                      { x = y = z = 0; } | ||||
|   FI T magnitude()                               const { return (T)sqrtf(x*x + y*y + z*z); } | ||||
|   FI operator T* ()                                    { return pos; } | ||||
|   FI operator bool()                                   { return z || x || y; } | ||||
|   FI XYZval<T>          copy()                   const { XYZval<T> o = *this; return o; } | ||||
|   FI XYZval<T>           ABS()                   const { return { T(_ABS(x)), T(_ABS(y)), T(_ABS(z)) }; } | ||||
|   FI XYZval<int16_t>   asInt()                         { return { int16_t(x), int16_t(y), int16_t(z) }; } | ||||
|   FI XYZval<int16_t>   asInt()                   const { return { int16_t(x), int16_t(y), int16_t(z) }; } | ||||
|   FI XYZval<int32_t>  asLong()                         { return { int32_t(x), int32_t(y), int32_t(z) }; } | ||||
|   FI XYZval<int32_t>  asLong()                   const { return { int32_t(x), int32_t(y), int32_t(z) }; } | ||||
|   FI XYZval<float>   asFloat()                         { return {   float(x),   float(y),   float(z) }; } | ||||
|   FI XYZval<float>   asFloat()                   const { return {   float(x),   float(y),   float(z) }; } | ||||
|   FI XYZval<float> reciprocal()                  const { return {  _RECIP(x),  _RECIP(y),  _RECIP(z) }; } | ||||
|   FI XYZval<float> asLogical()                   const { XYZval<float> o = asFloat(); toLogical(o); return o; } | ||||
|   FI XYZval<float>  asNative()                   const { XYZval<float> o = asFloat(); toNative(o);  return o; } | ||||
|   FI operator       XYval<T>&()                        { return *(XYval<T>*)this; } | ||||
|   FI operator const XYval<T>&()                  const { return *(const XYval<T>*)this; } | ||||
|   FI operator       XYZEval<T>()                 const { return { x, y, z }; } | ||||
|   FI       T&   operator[](const int i)                { return pos[i]; } | ||||
|   FI const T&   operator[](const int i)          const { return pos[i]; } | ||||
|   FI XYZval<T>& operator= (const T v)                  { set(v,    v,    v   ); return *this; } | ||||
|   FI XYZval<T>& operator= (const XYval<T>   &rs)       { set(rs.x, rs.y      ); return *this; } | ||||
|   FI XYZval<T>& operator= (const XYZEval<T> &rs)       { set(rs.x, rs.y, rs.z); return *this; } | ||||
|   FI XYZval<T>  operator+ (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;               return ls; } | ||||
|   FI XYZval<T>  operator+ (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;               return ls; } | ||||
|   FI XYZval<T>  operator- (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;               return ls; } | ||||
|   FI XYZval<T>  operator- (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;               return ls; } | ||||
|   FI XYZval<T>  operator* (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;               return ls; } | ||||
|   FI XYZval<T>  operator* (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;               return ls; } | ||||
|   FI XYZval<T>  operator/ (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;               return ls; } | ||||
|   FI XYZval<T>  operator/ (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;               return ls; } | ||||
|   FI XYZval<T>  operator+ (const XYZval<T>  &rs) const { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; return ls; } | ||||
|   FI XYZval<T>  operator+ (const XYZval<T>  &rs)       { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; return ls; } | ||||
|   FI XYZval<T>  operator- (const XYZval<T>  &rs) const { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator- (const XYZval<T>  &rs)       { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator* (const XYZval<T>  &rs) const { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator* (const XYZval<T>  &rs)       { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator/ (const XYZval<T>  &rs) const { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator/ (const XYZval<T>  &rs)       { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator+ (const XYZEval<T> &rs) const { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; return ls; } | ||||
|   FI XYZval<T>  operator+ (const XYZEval<T> &rs)       { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; return ls; } | ||||
|   FI XYZval<T>  operator- (const XYZEval<T> &rs) const { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator- (const XYZEval<T> &rs)       { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator* (const XYZEval<T> &rs) const { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator* (const XYZEval<T> &rs)       { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator/ (const XYZEval<T> &rs) const { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator/ (const XYZEval<T> &rs)       { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; return ls; } | ||||
|   FI XYZval<T>  operator* (const float &v)       const { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= z;    return ls; } | ||||
|   FI XYZval<T>  operator* (const float &v)             { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= z;    return ls; } | ||||
|   FI XYZval<T>  operator* (const int &v)         const { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= z;    return ls; } | ||||
|   FI XYZval<T>  operator* (const int &v)               { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= z;    return ls; } | ||||
|   FI XYZval<T>  operator/ (const float &v)       const { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= z;    return ls; } | ||||
|   FI XYZval<T>  operator/ (const float &v)             { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= z;    return ls; } | ||||
|   FI XYZval<T>  operator/ (const int &v)         const { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= z;    return ls; } | ||||
|   FI XYZval<T>  operator/ (const int &v)               { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= z;    return ls; } | ||||
|   FI XYZval<T>  operator>>(const int &v)         const { XYZval<T> ls = *this; _RS(ls.x); _RS(ls.y); _RS(ls.z); return ls; } | ||||
|   FI XYZval<T>  operator>>(const int &v)               { XYZval<T> ls = *this; _RS(ls.x); _RS(ls.y); _RS(ls.z); return ls; } | ||||
|   FI XYZval<T>  operator<<(const int &v)         const { XYZval<T> ls = *this; _LS(ls.x); _LS(ls.y); _LS(ls.z); return ls; } | ||||
|   FI XYZval<T>  operator<<(const int &v)               { XYZval<T> ls = *this; _LS(ls.x); _LS(ls.y); _LS(ls.z); return ls; } | ||||
|   FI XYZval<T>& operator+=(const XYval<T>   &rs)       { x += rs.x; y += rs.y;            return *this; } | ||||
|   FI XYZval<T>& operator-=(const XYval<T>   &rs)       { x -= rs.x; y -= rs.y;            return *this; } | ||||
|   FI XYZval<T>& operator*=(const XYval<T>   &rs)       { x *= rs.x; y *= rs.y;            return *this; } | ||||
|   FI XYZval<T>& operator/=(const XYval<T>   &rs)       { x /= rs.x; y /= rs.y;            return *this; } | ||||
|   FI XYZval<T>& operator+=(const XYZval<T>  &rs)       { x += rs.x; y += rs.y; z += rs.z; return *this; } | ||||
|   FI XYZval<T>& operator-=(const XYZval<T>  &rs)       { x -= rs.x; y -= rs.y; z -= rs.z; return *this; } | ||||
|   FI XYZval<T>& operator*=(const XYZval<T>  &rs)       { x *= rs.x; y *= rs.y; z *= rs.z; return *this; } | ||||
|   FI XYZval<T>& operator/=(const XYZval<T>  &rs)       { x /= rs.x; y /= rs.y; z /= rs.z; return *this; } | ||||
|   FI XYZval<T>& operator+=(const XYZEval<T> &rs)       { x += rs.x; y += rs.y; z += rs.z; return *this; } | ||||
|   FI XYZval<T>& operator-=(const XYZEval<T> &rs)       { x -= rs.x; y -= rs.y; z -= rs.z; return *this; } | ||||
|   FI XYZval<T>& operator*=(const XYZEval<T> &rs)       { x *= rs.x; y *= rs.y; z *= rs.z; return *this; } | ||||
|   FI XYZval<T>& operator/=(const XYZEval<T> &rs)       { x /= rs.x; y /= rs.y; z /= rs.z; return *this; } | ||||
|   FI XYZval<T>& operator*=(const float &v)             { x *= v;    y *= v;    z *= v;    return *this; } | ||||
|   FI XYZval<T>& operator*=(const int &v)               { x *= v;    y *= v;    z *= v;    return *this; } | ||||
|   FI XYZval<T>& operator>>=(const int &v)              { _RS(x);   _RS(y);   _RS(z);   return *this; } | ||||
|   FI XYZval<T>& operator<<=(const int &v)              { _LS(x);   _LS(y);   _LS(z);   return *this; } | ||||
|   FI bool       operator==(const XYZEval<T> &rs)       { return x == rs.x && y == rs.y && z == rs.z; } | ||||
|   FI bool       operator!=(const XYZEval<T> &rs)       { return !operator==(rs); } | ||||
|   FI bool       operator==(const XYZEval<T> &rs) const { return x == rs.x && y == rs.y && z == rs.z; } | ||||
|   FI bool       operator!=(const XYZEval<T> &rs) const { return !operator==(rs); } | ||||
|   FI XYZval<T>       operator-()                       { XYZval<T> o = *this; o.x = -x; o.y = -y; o.z = -z; return o; } | ||||
|   FI const XYZval<T> operator-()                 const { XYZval<T> o = *this; o.x = -x; o.y = -y; o.z = -z; return o; } | ||||
| }; | ||||
|  | ||||
| // | ||||
| // XYZE coordinates, counters, etc. | ||||
| // | ||||
| template<typename T> | ||||
| struct XYZEval { | ||||
|   union { | ||||
|     struct{ T x, y, z, e; }; | ||||
|     struct{ T a, b, c; }; | ||||
|     T pos[4]; | ||||
|   }; | ||||
|   FI void reset()                                             { x = y = z = e = 0; } | ||||
|   FI T magnitude()                                      const { return (T)sqrtf(x*x + y*y + z*z + e*e); } | ||||
|   FI operator T* ()                                           { return pos; } | ||||
|   FI operator bool()                                          { return e || z || x || y; } | ||||
|   FI void set(const T px)                                     { x = px;                                        } | ||||
|   FI void set(const T px, const T py)                         { x = px;     y = py;                            } | ||||
|   FI void set(const T px, const T py, const T pz)             { x = px;     y = py;     z = pz;                } | ||||
|   FI void set(const T px, const T py, const T pz, const T pe) { x = px;     y = py;     z = pz;     e = pe;    } | ||||
|   FI void set(const XYval<T> pxy)                             { x = pxy.x;  y = pxy.y;                         } | ||||
|   FI void set(const XYval<T> pxy, const T pz)                 { x = pxy.x;  y = pxy.y;  z = pz;                } | ||||
|   FI void set(const XYZval<T> pxyz)                           { x = pxyz.x; y = pxyz.y; z = pxyz.z;            } | ||||
|   FI void set(const XYval<T> pxy, const T pz, const T pe)     { x = pxy.x;  y = pxy.y;  z = pz;     e = pe;    } | ||||
|   FI void set(const XYval<T> pxy, const XYval<T> pze)         { x = pxy.x;  y = pxy.y;  z = pze.z;  e = pze.e; } | ||||
|   FI void set(const XYZval<T> pxyz, const T pe)               { x = pxyz.x; y = pxyz.y; z = pxyz.z; e = pe;    } | ||||
|   FI XYZEval<T>          copy()                         const { return *this; } | ||||
|   FI XYZEval<T>           ABS()                         const { return { T(_ABS(x)), T(_ABS(y)), T(_ABS(z)), T(_ABS(e)) }; } | ||||
|   FI XYZEval<int16_t>   asInt()                               { return { int16_t(x), int16_t(y), int16_t(z), int16_t(e) }; } | ||||
|   FI XYZEval<int16_t>   asInt()                         const { return { int16_t(x), int16_t(y), int16_t(z), int16_t(e) }; } | ||||
|   FI XYZEval<int32_t>  asLong()                         const { return { int32_t(x), int32_t(y), int32_t(z), int32_t(e) }; } | ||||
|   FI XYZEval<int32_t>  asLong()                               { return { int32_t(x), int32_t(y), int32_t(z), int32_t(e) }; } | ||||
|   FI XYZEval<float>   asFloat()                               { return {   float(x),   float(y),   float(z),   float(e) }; } | ||||
|   FI XYZEval<float>   asFloat()                         const { return {   float(x),   float(y),   float(z),   float(e) }; } | ||||
|   FI XYZEval<float> reciprocal()                        const { return {  _RECIP(x),  _RECIP(y),  _RECIP(z),  _RECIP(e) }; } | ||||
|   FI XYZEval<float> asLogical()                         const { XYZEval<float> o = asFloat(); toLogical(o); return o; } | ||||
|   FI XYZEval<float>  asNative()                         const { XYZEval<float> o = asFloat(); toNative(o);  return o; } | ||||
|   FI operator       XYval<T>&()                               { return *(XYval<T>*)this; } | ||||
|   FI operator const XYval<T>&()                         const { return *(const XYval<T>*)this; } | ||||
|   FI operator       XYZval<T>&()                              { return *(XYZval<T>*)this; } | ||||
|   FI operator const XYZval<T>&()                        const { return *(const XYZval<T>*)this; } | ||||
|   FI       T&    operator[](const int i)                      { return pos[i]; } | ||||
|   FI const T&    operator[](const int i)                const { return pos[i]; } | ||||
|   FI XYZEval<T>& operator= (const T v)                        { set(v, v, v, v); return *this; } | ||||
|   FI XYZEval<T>& operator= (const XYval<T>   &rs)             { set(rs.x, rs.y); return *this; } | ||||
|   FI XYZEval<T>& operator= (const XYZval<T>  &rs)             { set(rs.x, rs.y, rs.z); return *this; } | ||||
|   FI XYZEval<T>  operator+ (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;                             return ls; } | ||||
|   FI XYZEval<T>  operator+ (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;                             return ls; } | ||||
|   FI XYZEval<T>  operator- (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;                             return ls; } | ||||
|   FI XYZEval<T>  operator- (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;                             return ls; } | ||||
|   FI XYZEval<T>  operator* (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;                             return ls; } | ||||
|   FI XYZEval<T>  operator* (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;                             return ls; } | ||||
|   FI XYZEval<T>  operator/ (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;                             return ls; } | ||||
|   FI XYZEval<T>  operator/ (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;                             return ls; } | ||||
|   FI XYZEval<T>  operator+ (const XYZval<T>  &rs)       const { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z;               return ls; } | ||||
|   FI XYZEval<T>  operator+ (const XYZval<T>  &rs)             { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z;               return ls; } | ||||
|   FI XYZEval<T>  operator- (const XYZval<T>  &rs)       const { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z;               return ls; } | ||||
|   FI XYZEval<T>  operator- (const XYZval<T>  &rs)             { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z;               return ls; } | ||||
|   FI XYZEval<T>  operator* (const XYZval<T>  &rs)       const { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z;               return ls; } | ||||
|   FI XYZEval<T>  operator* (const XYZval<T>  &rs)             { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z;               return ls; } | ||||
|   FI XYZEval<T>  operator/ (const XYZval<T>  &rs)       const { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z;               return ls; } | ||||
|   FI XYZEval<T>  operator/ (const XYZval<T>  &rs)             { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z;               return ls; } | ||||
|   FI XYZEval<T>  operator+ (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; ls.e += rs.e; return ls; } | ||||
|   FI XYZEval<T>  operator+ (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; ls.e += rs.e; return ls; } | ||||
|   FI XYZEval<T>  operator- (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; ls.e -= rs.e; return ls; } | ||||
|   FI XYZEval<T>  operator- (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; ls.e -= rs.e; return ls; } | ||||
|   FI XYZEval<T>  operator* (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; ls.e *= rs.e; return ls; } | ||||
|   FI XYZEval<T>  operator* (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; ls.e *= rs.e; return ls; } | ||||
|   FI XYZEval<T>  operator/ (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; ls.e /= rs.e; return ls; } | ||||
|   FI XYZEval<T>  operator/ (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; ls.e /= rs.e; return ls; } | ||||
|   FI XYZEval<T>  operator* (const float &v)             const { XYZEval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    ls.e *= v;    return ls; } | ||||
|   FI XYZEval<T>  operator* (const float &v)                   { XYZEval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    ls.e *= v;    return ls; } | ||||
|   FI XYZEval<T>  operator* (const int &v)               const { XYZEval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    ls.e *= v;    return ls; } | ||||
|   FI XYZEval<T>  operator* (const int &v)                     { XYZEval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    ls.e *= v;    return ls; } | ||||
|   FI XYZEval<T>  operator/ (const float &v)             const { XYZEval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    ls.e /= v;    return ls; } | ||||
|   FI XYZEval<T>  operator/ (const float &v)                   { XYZEval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    ls.e /= v;    return ls; } | ||||
|   FI XYZEval<T>  operator/ (const int &v)               const { XYZEval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    ls.e /= v;    return ls; } | ||||
|   FI XYZEval<T>  operator/ (const int &v)                     { XYZEval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    ls.e /= v;    return ls; } | ||||
|   FI XYZEval<T>  operator>>(const int &v)               const { XYZEval<T> ls = *this; _RS(ls.x);    _RS(ls.y);    _RS(ls.z);    _RS(ls.e);    return ls; } | ||||
|   FI XYZEval<T>  operator>>(const int &v)                     { XYZEval<T> ls = *this; _RS(ls.x);    _RS(ls.y);    _RS(ls.z);    _RS(ls.e);    return ls; } | ||||
|   FI XYZEval<T>  operator<<(const int &v)               const { XYZEval<T> ls = *this; _LS(ls.x);    _LS(ls.y);    _LS(ls.z);    _LS(ls.e);    return ls; } | ||||
|   FI XYZEval<T>  operator<<(const int &v)                     { XYZEval<T> ls = *this; _LS(ls.x);    _LS(ls.y);    _LS(ls.z);    _LS(ls.e);    return ls; } | ||||
|   FI XYZEval<T>& operator+=(const XYval<T>   &rs)             { x += rs.x; y += rs.y;                       return *this; } | ||||
|   FI XYZEval<T>& operator-=(const XYval<T>   &rs)             { x -= rs.x; y -= rs.y;                       return *this; } | ||||
|   FI XYZEval<T>& operator*=(const XYval<T>   &rs)             { x *= rs.x; y *= rs.y;                       return *this; } | ||||
|   FI XYZEval<T>& operator/=(const XYval<T>   &rs)             { x /= rs.x; y /= rs.y;                       return *this; } | ||||
|   FI XYZEval<T>& operator+=(const XYZval<T>  &rs)             { x += rs.x; y += rs.y; z += rs.z;            return *this; } | ||||
|   FI XYZEval<T>& operator-=(const XYZval<T>  &rs)             { x -= rs.x; y -= rs.y; z -= rs.z;            return *this; } | ||||
|   FI XYZEval<T>& operator*=(const XYZval<T>  &rs)             { x *= rs.x; y *= rs.y; z *= rs.z;            return *this; } | ||||
|   FI XYZEval<T>& operator/=(const XYZval<T>  &rs)             { x /= rs.x; y /= rs.y; z /= rs.z;            return *this; } | ||||
|   FI XYZEval<T>& operator+=(const XYZEval<T> &rs)             { x += rs.x; y += rs.y; z += rs.z; e += rs.e; return *this; } | ||||
|   FI XYZEval<T>& operator-=(const XYZEval<T> &rs)             { x -= rs.x; y -= rs.y; z -= rs.z; e -= rs.e; return *this; } | ||||
|   FI XYZEval<T>& operator*=(const XYZEval<T> &rs)             { x *= rs.x; y *= rs.y; z *= rs.z; e *= rs.e; return *this; } | ||||
|   FI XYZEval<T>& operator/=(const XYZEval<T> &rs)             { x /= rs.x; y /= rs.y; z /= rs.z; e /= rs.e; return *this; } | ||||
|   FI XYZEval<T>& operator*=(const T &v)                       { x *= v;    y *= v;    z *= v;    e *= v;    return *this; } | ||||
|   FI XYZEval<T>& operator>>=(const int &v)                    { _RS(x);    _RS(y);    _RS(z);    _RS(e);    return *this; } | ||||
|   FI XYZEval<T>& operator<<=(const int &v)                    { _LS(x);    _LS(y);    _LS(z);    _LS(e);    return *this; } | ||||
|   FI bool        operator==(const XYZval<T>  &rs)             { return x == rs.x && y == rs.y && z == rs.z; } | ||||
|   FI bool        operator!=(const XYZval<T>  &rs)             { return !operator==(rs); } | ||||
|   FI bool        operator==(const XYZval<T>  &rs)       const { return x == rs.x && y == rs.y && z == rs.z; } | ||||
|   FI bool        operator!=(const XYZval<T>  &rs)       const { return !operator==(rs); } | ||||
|   FI       XYZEval<T> operator-()                             { return { -x, -y, -z, -e }; } | ||||
|   FI const XYZEval<T> operator-()                       const { return { -x, -y, -z, -e }; } | ||||
| }; | ||||
|  | ||||
| #undef _RECIP | ||||
| #undef _ABS | ||||
| #undef _LS | ||||
| #undef _RS | ||||
| #undef FI | ||||
|  | ||||
| const xyze_char_t axis_codes { 'X', 'Y', 'Z', 'E' }; | ||||
| @@ -79,36 +79,36 @@ void safe_delay(millis_t ms) { | ||||
|     ); | ||||
|  | ||||
|     #if HAS_BED_PROBE | ||||
|       SERIAL_ECHOPAIR("Probe Offset X:", probe_offset[X_AXIS], " Y:", probe_offset[Y_AXIS], " Z:", probe_offset[Z_AXIS]); | ||||
|       if (probe_offset[X_AXIS] > 0) | ||||
|       SERIAL_ECHOPAIR("Probe Offset X", probe_offset.x, " Y", probe_offset.y, " Z", probe_offset.z); | ||||
|       if (probe_offset.x > 0) | ||||
|         SERIAL_ECHOPGM(" (Right"); | ||||
|       else if (probe_offset[X_AXIS] < 0) | ||||
|       else if (probe_offset.x < 0) | ||||
|         SERIAL_ECHOPGM(" (Left"); | ||||
|       else if (probe_offset[Y_AXIS] != 0) | ||||
|       else if (probe_offset.y != 0) | ||||
|         SERIAL_ECHOPGM(" (Middle"); | ||||
|       else | ||||
|         SERIAL_ECHOPGM(" (Aligned With"); | ||||
|  | ||||
|       if (probe_offset[Y_AXIS] > 0) { | ||||
|       if (probe_offset.y > 0) { | ||||
|         #if IS_SCARA | ||||
|           SERIAL_ECHOPGM("-Distal"); | ||||
|         #else | ||||
|           SERIAL_ECHOPGM("-Back"); | ||||
|         #endif | ||||
|       } | ||||
|       else if (probe_offset[Y_AXIS] < 0) { | ||||
|       else if (probe_offset.y < 0) { | ||||
|         #if IS_SCARA | ||||
|           SERIAL_ECHOPGM("-Proximal"); | ||||
|         #else | ||||
|           SERIAL_ECHOPGM("-Front"); | ||||
|         #endif | ||||
|       } | ||||
|       else if (probe_offset[X_AXIS] != 0) | ||||
|       else if (probe_offset.x != 0) | ||||
|         SERIAL_ECHOPGM("-Center"); | ||||
|  | ||||
|       if (probe_offset[Z_AXIS] < 0) | ||||
|       if (probe_offset.z < 0) | ||||
|         SERIAL_ECHOPGM(" & Below"); | ||||
|       else if (probe_offset[Z_AXIS] > 0) | ||||
|       else if (probe_offset.z > 0) | ||||
|         SERIAL_ECHOPGM(" & Above"); | ||||
|       else | ||||
|         SERIAL_ECHOPGM(" & Same Z as"); | ||||
| @@ -134,24 +134,18 @@ void safe_delay(millis_t ms) { | ||||
|             SERIAL_ECHOLNPAIR("Z Fade: ", planner.z_fade_height); | ||||
|         #endif | ||||
|         #if ABL_PLANAR | ||||
|           const float diff[XYZ] = { | ||||
|             planner.get_axis_position_mm(X_AXIS) - current_position[X_AXIS], | ||||
|             planner.get_axis_position_mm(Y_AXIS) - current_position[Y_AXIS], | ||||
|             planner.get_axis_position_mm(Z_AXIS) - current_position[Z_AXIS] | ||||
|           }; | ||||
|           SERIAL_ECHOPGM("ABL Adjustment X"); | ||||
|           if (diff[X_AXIS] > 0) SERIAL_CHAR('+'); | ||||
|           SERIAL_ECHO(diff[X_AXIS]); | ||||
|           SERIAL_ECHOPGM(" Y"); | ||||
|           if (diff[Y_AXIS] > 0) SERIAL_CHAR('+'); | ||||
|           SERIAL_ECHO(diff[Y_AXIS]); | ||||
|           SERIAL_ECHOPGM(" Z"); | ||||
|           if (diff[Z_AXIS] > 0) SERIAL_CHAR('+'); | ||||
|           SERIAL_ECHO(diff[Z_AXIS]); | ||||
|           LOOP_XYZ(a) { | ||||
|             float v = planner.get_axis_position_mm(AxisEnum(a)) - current_position[a]; | ||||
|             SERIAL_CHAR(' '); | ||||
|             SERIAL_CHAR('X' + char(a)); | ||||
|             if (v > 0) SERIAL_CHAR('+'); | ||||
|             SERIAL_ECHO(v); | ||||
|           } | ||||
|         #else | ||||
|           #if ENABLED(AUTO_BED_LEVELING_UBL) | ||||
|             SERIAL_ECHOPGM("UBL Adjustment Z"); | ||||
|             const float rz = ubl.get_z_correction(current_position[X_AXIS], current_position[Y_AXIS]); | ||||
|             const float rz = ubl.get_z_correction(current_position); | ||||
|           #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|             SERIAL_ECHOPGM("ABL Adjustment Z"); | ||||
|             const float rz = bilinear_z_offset(current_position); | ||||
| @@ -159,7 +153,7 @@ void safe_delay(millis_t ms) { | ||||
|           SERIAL_ECHO(ftostr43sign(rz, '+')); | ||||
|           #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|             if (planner.z_fade_height) { | ||||
|               SERIAL_ECHOPAIR(" (", ftostr43sign(rz * planner.fade_scaling_factor_for_z(current_position[Z_AXIS]), '+')); | ||||
|               SERIAL_ECHOPAIR(" (", ftostr43sign(rz * planner.fade_scaling_factor_for_z(current_position.z), '+')); | ||||
|               SERIAL_CHAR(')'); | ||||
|             } | ||||
|           #endif | ||||
| @@ -175,15 +169,11 @@ void safe_delay(millis_t ms) { | ||||
|       SERIAL_ECHOPGM("Mesh Bed Leveling"); | ||||
|       if (planner.leveling_active) { | ||||
|         SERIAL_ECHOLNPGM(" (enabled)"); | ||||
|         SERIAL_ECHOPAIR("MBL Adjustment Z", ftostr43sign(mbl.get_z(current_position[X_AXIS], current_position[Y_AXIS] | ||||
|           #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|             , 1.0 | ||||
|           #endif | ||||
|         ), '+')); | ||||
|         SERIAL_ECHOPAIR("MBL Adjustment Z", ftostr43sign(mbl.get_z(current_position), '+')); | ||||
|         #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|           if (planner.z_fade_height) { | ||||
|             SERIAL_ECHOPAIR(" (", ftostr43sign( | ||||
|               mbl.get_z(current_position[X_AXIS], current_position[Y_AXIS], planner.fade_scaling_factor_for_z(current_position[Z_AXIS])), '+' | ||||
|               mbl.get_z(current_position, planner.fade_scaling_factor_for_z(current_position.z)), '+' | ||||
|             )); | ||||
|             SERIAL_CHAR(')'); | ||||
|           } | ||||
|   | ||||
| @@ -22,8 +22,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../inc/MarlinConfigPre.h" | ||||
|  | ||||
| constexpr char axis_codes[XYZE] = { 'X', 'Y', 'Z', 'E' }; | ||||
| #include "../core/types.h" | ||||
|  | ||||
| // Delay that ensures heaters and watchdog are kept alive | ||||
| void safe_delay(millis_t ms); | ||||
| @@ -37,10 +36,25 @@ inline void serial_delay(const millis_t ms) { | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| // 16x16 bit arrays | ||||
| FORCE_INLINE void bitmap_clear(uint16_t bits[16], const uint8_t x, const uint8_t y)  { CBI(bits[y], x); } | ||||
| FORCE_INLINE void bitmap_set(uint16_t bits[16], const uint8_t x, const uint8_t y)    { SBI(bits[y], x); } | ||||
| FORCE_INLINE bool is_bitmap_set(uint16_t bits[16], const uint8_t x, const uint8_t y) { return TEST(bits[y], x); } | ||||
| #if GRID_MAX_POINTS_X && GRID_MAX_POINTS_Y | ||||
|  | ||||
|   // 16x16 bit arrays | ||||
|   template <int W, int H> | ||||
|   struct FlagBits { | ||||
|     typename IF<(W>8), uint16_t, uint8_t>::type bits[H]; | ||||
|     void fill()                                   { memset(bits, 0xFF, sizeof(bits)); } | ||||
|     void reset()                                  { memset(bits, 0x00, sizeof(bits)); } | ||||
|     void unmark(const uint8_t x, const uint8_t y) { CBI(bits[y], x); } | ||||
|     void mark(const uint8_t x, const uint8_t y)   { SBI(bits[y], x); } | ||||
|     bool marked(const uint8_t x, const uint8_t y) { return TEST(bits[y], x); } | ||||
|     inline void unmark(const xy_int8_t &xy)       { unmark(xy.y, xy.x); } | ||||
|     inline void mark(const xy_int8_t &xy)         { mark(xy.y, xy.x); } | ||||
|     inline bool marked(const xy_int8_t &xy)       { return marked(xy.y, xy.x); } | ||||
|   }; | ||||
|  | ||||
|   typedef FlagBits<GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y> MeshFlags; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(DEBUG_LEVELING_FEATURE) | ||||
|   void log_machine_info(); | ||||
|   | ||||
| @@ -326,25 +326,23 @@ bool I2CPositionEncoder::test_axis() { | ||||
|   //only works on XYZ cartesian machines for the time being | ||||
|   if (!(encoderAxis == X_AXIS || encoderAxis == Y_AXIS || encoderAxis == Z_AXIS)) return false; | ||||
|  | ||||
|   float startCoord[NUM_AXIS] = { 0 }, endCoord[NUM_AXIS] = { 0 }; | ||||
|  | ||||
|   const float startPosition = soft_endstop[encoderAxis].min + 10, | ||||
|               endPosition = soft_endstop[encoderAxis].max - 10; | ||||
|   const float startPosition = soft_endstop.min[encoderAxis] + 10, | ||||
|               endPosition = soft_endstop.max[encoderAxis] - 10; | ||||
|   const feedRate_t fr_mm_s = FLOOR(MMM_TO_MMS((encoderAxis == Z_AXIS) ? HOMING_FEEDRATE_Z : HOMING_FEEDRATE_XY)); | ||||
|  | ||||
|   ec = false; | ||||
|  | ||||
|   LOOP_XYZ(i) { | ||||
|     startCoord[i] = planner.get_axis_position_mm((AxisEnum)i); | ||||
|     endCoord[i] = planner.get_axis_position_mm((AxisEnum)i); | ||||
|   xyze_pos_t startCoord, endCoord; | ||||
|   LOOP_XYZ(a) { | ||||
|     startCoord[a] = planner.get_axis_position_mm((AxisEnum)a); | ||||
|     endCoord[a] = planner.get_axis_position_mm((AxisEnum)a); | ||||
|   } | ||||
|   startCoord[encoderAxis] = startPosition; | ||||
|   endCoord[encoderAxis] = endPosition; | ||||
|  | ||||
|   planner.synchronize(); | ||||
|  | ||||
|   planner.buffer_line(startCoord[X_AXIS], startCoord[Y_AXIS], startCoord[Z_AXIS], | ||||
|                       planner.get_axis_position_mm(E_AXIS), fr_mm_s, 0); | ||||
|   startCoord.e = planner.get_axis_position_mm(E_AXIS); | ||||
|   planner.buffer_line(startCoord, fr_mm_s, 0); | ||||
|   planner.synchronize(); | ||||
|  | ||||
|   // if the module isn't currently trusted, wait until it is (or until it should be if things are working) | ||||
| @@ -355,8 +353,8 @@ bool I2CPositionEncoder::test_axis() { | ||||
|   } | ||||
|  | ||||
|   if (trusted) { // if trusted, commence test | ||||
|     planner.buffer_line(endCoord[X_AXIS], endCoord[Y_AXIS], endCoord[Z_AXIS], | ||||
|                         planner.get_axis_position_mm(E_AXIS), fr_mm_s, 0); | ||||
|     endCoord.e = planner.get_axis_position_mm(E_AXIS); | ||||
|     planner.buffer_line(endCoord, fr_mm_s, 0); | ||||
|     planner.synchronize(); | ||||
|   } | ||||
|  | ||||
| @@ -376,8 +374,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) { | ||||
|  | ||||
|   float old_steps_mm, new_steps_mm, | ||||
|         startDistance, endDistance, | ||||
|         travelDistance, travelledDistance, total = 0, | ||||
|         startCoord[NUM_AXIS] = { 0 }, endCoord[NUM_AXIS] = { 0 }; | ||||
|         travelDistance, travelledDistance, total = 0; | ||||
|  | ||||
|   int32_t startCount, stopCount; | ||||
|  | ||||
| @@ -387,31 +384,31 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) { | ||||
|   ec = false; | ||||
|  | ||||
|   startDistance = 20; | ||||
|   endDistance = soft_endstop[encoderAxis].max - 20; | ||||
|   endDistance = soft_endstop.max[encoderAxis] - 20; | ||||
|   travelDistance = endDistance - startDistance; | ||||
|  | ||||
|   xyze_pos_t startCoord, endCoord; | ||||
|   LOOP_XYZ(a) { | ||||
|     startCoord[a] = planner.get_axis_position_mm((AxisEnum)a); | ||||
|     endCoord[a] = planner.get_axis_position_mm((AxisEnum)a); | ||||
|   } | ||||
|  | ||||
|   startCoord[encoderAxis] = startDistance; | ||||
|   endCoord[encoderAxis] = endDistance; | ||||
|  | ||||
|   planner.synchronize(); | ||||
|  | ||||
|   LOOP_L_N(i, iter) { | ||||
|     planner.buffer_line(startCoord[X_AXIS], startCoord[Y_AXIS], startCoord[Z_AXIS], | ||||
|                         planner.get_axis_position_mm(E_AXIS), fr_mm_s, 0); | ||||
|     startCoord.e = planner.get_axis_position_mm(E_AXIS); | ||||
|     planner.buffer_line(startCoord, fr_mm_s, 0); | ||||
|     planner.synchronize(); | ||||
|  | ||||
|     delay(250); | ||||
|     startCount = get_position(); | ||||
|  | ||||
|     //do_blocking_move_to(endCoord[X_AXIS],endCoord[Y_AXIS],endCoord[Z_AXIS]); | ||||
|     //do_blocking_move_to(endCoord); | ||||
|  | ||||
|     planner.buffer_line(endCoord[X_AXIS], endCoord[Y_AXIS], endCoord[Z_AXIS], | ||||
|                         planner.get_axis_position_mm(E_AXIS), fr_mm_s, 0); | ||||
|     endCoord.e = planner.get_axis_position_mm(E_AXIS); | ||||
|     planner.buffer_line(endCoord, fr_mm_s, 0); | ||||
|     planner.synchronize(); | ||||
|  | ||||
|     //Read encoder distance | ||||
|   | ||||
| @@ -93,8 +93,6 @@ | ||||
| #define LOOP_PE(VAR) LOOP_L_N(VAR, I2CPE_ENCODER_CNT) | ||||
| #define CHECK_IDX() do{ if (!WITHIN(idx, 0, I2CPE_ENCODER_CNT - 1)) return; }while(0) | ||||
|  | ||||
| extern const char axis_codes[XYZE]; | ||||
|  | ||||
| typedef union { | ||||
|   volatile int32_t val = 0; | ||||
|   uint8_t          bval[4]; | ||||
|   | ||||
| @@ -31,9 +31,9 @@ | ||||
|  | ||||
| #ifdef BACKLASH_DISTANCE_MM | ||||
|   #if ENABLED(BACKLASH_GCODE) | ||||
|     float Backlash::distance_mm[XYZ] = BACKLASH_DISTANCE_MM; | ||||
|     xyz_float_t Backlash::distance_mm = BACKLASH_DISTANCE_MM; | ||||
|   #else | ||||
|     const float Backlash::distance_mm[XYZ] = BACKLASH_DISTANCE_MM; | ||||
|     const xyz_float_t Backlash::distance_mm = BACKLASH_DISTANCE_MM; | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| @@ -45,8 +45,8 @@ | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) | ||||
|   float Backlash::measured_mm[XYZ] = { 0 }; | ||||
|   uint8_t Backlash::measured_count[XYZ] = { 0 }; | ||||
|   xyz_float_t Backlash::measured_mm{0}; | ||||
|   xyz_uint8_t Backlash::measured_count{0}; | ||||
| #endif | ||||
|  | ||||
| Backlash backlash; | ||||
| @@ -80,12 +80,12 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const | ||||
|  | ||||
|     // Residual error carried forward across multiple segments, so correction can be applied | ||||
|     // to segments where there is no direction change. | ||||
|     static int32_t residual_error[XYZ] = { 0 }; | ||||
|     static xyz_long_t residual_error{0}; | ||||
|   #else | ||||
|     // No direction change, no correction. | ||||
|     if (!changed_dir) return; | ||||
|     // No leftover residual error from segment to segment | ||||
|     int32_t residual_error[XYZ] = { 0 }; | ||||
|     xyz_long_t residual_error{0}; | ||||
|   #endif | ||||
|  | ||||
|   const float f_corr = float(correction) / 255.0f; | ||||
| @@ -131,15 +131,15 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const | ||||
|  | ||||
|   // Measure Z backlash by raising nozzle in increments until probe deactivates | ||||
|   void Backlash::measure_with_probe() { | ||||
|     if (measured_count[Z_AXIS] == 255) return; | ||||
|     if (measured_count.z == 255) return; | ||||
|  | ||||
|     float start_height = current_position[Z_AXIS]; | ||||
|     while (current_position[Z_AXIS] < (start_height + BACKLASH_MEASUREMENT_LIMIT) && TEST_PROBE_PIN) | ||||
|       do_blocking_move_to_z(current_position[Z_AXIS] + BACKLASH_MEASUREMENT_RESOLUTION, MMM_TO_MMS(BACKLASH_MEASUREMENT_FEEDRATE)); | ||||
|     const float start_height = current_position.z; | ||||
|     while (current_position.z < (start_height + BACKLASH_MEASUREMENT_LIMIT) && TEST_PROBE_PIN) | ||||
|       do_blocking_move_to_z(current_position.z + BACKLASH_MEASUREMENT_RESOLUTION, MMM_TO_MMS(BACKLASH_MEASUREMENT_FEEDRATE)); | ||||
|  | ||||
|     // The backlash from all probe points is averaged, so count the number of measurements | ||||
|     measured_mm[Z_AXIS] += current_position[Z_AXIS] - start_height; | ||||
|     measured_count[Z_AXIS]++; | ||||
|     measured_mm.z += current_position.z - start_height; | ||||
|     measured_count.z++; | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -29,7 +29,7 @@ constexpr uint8_t all_on = 0xFF, all_off = 0x00; | ||||
| class Backlash { | ||||
| public: | ||||
|   #if ENABLED(BACKLASH_GCODE) | ||||
|     static float distance_mm[XYZ]; | ||||
|     static xyz_float_t distance_mm; | ||||
|     static uint8_t correction; | ||||
|     #ifdef BACKLASH_SMOOTHING_MM | ||||
|       static float smoothing_mm; | ||||
| @@ -39,7 +39,7 @@ public: | ||||
|     static inline float get_correction() { return float(ui8_to_percent(correction)) / 100.0f; } | ||||
|   #else | ||||
|     static constexpr uint8_t correction = (BACKLASH_CORRECTION) * 0xFF; | ||||
|     static const float distance_mm[XYZ]; | ||||
|     static const xyz_float_t distance_mm; | ||||
|     #ifdef BACKLASH_SMOOTHING_MM | ||||
|       static constexpr float smoothing_mm = BACKLASH_SMOOTHING_MM; | ||||
|     #endif | ||||
| @@ -47,8 +47,8 @@ public: | ||||
|  | ||||
|   #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) | ||||
|     private: | ||||
|       static float measured_mm[XYZ]; | ||||
|       static uint8_t measured_count[XYZ]; | ||||
|       static xyz_float_t measured_mm; | ||||
|       static xyz_uint8_t measured_count; | ||||
|     public: | ||||
|       static void measure_with_probe(); | ||||
|   #endif | ||||
|   | ||||
| @@ -35,9 +35,9 @@ | ||||
|   #include "../../../lcd/extensible_ui/ui_api.h" | ||||
| #endif | ||||
|  | ||||
| int bilinear_grid_spacing[2], bilinear_start[2]; | ||||
| float bilinear_grid_factor[2], | ||||
|       z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y]; | ||||
| xy_int_t bilinear_grid_spacing, bilinear_start; | ||||
| xy_float_t bilinear_grid_factor; | ||||
| bed_mesh_t z_values; | ||||
|  | ||||
| /** | ||||
|  * Extrapolate a single point from its neighbors | ||||
| @@ -153,8 +153,8 @@ void print_bilinear_leveling_grid() { | ||||
|   #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]; | ||||
|   int bilinear_grid_spacing_virt[2] = { 0 }; | ||||
|   float bilinear_grid_factor_virt[2] = { 0 }; | ||||
|   xy_int_t bilinear_grid_spacing_virt; | ||||
|   xy_float_t bilinear_grid_factor_virt; | ||||
|  | ||||
|   void print_bilinear_leveling_grid_virt() { | ||||
|     SERIAL_ECHOLNPGM("Subdivided with CATMULL ROM Leveling Grid:"); | ||||
| @@ -207,7 +207,7 @@ void print_bilinear_leveling_grid() { | ||||
|       + p[i]   * (2 - 5 * sq(t) + 3 * t * sq(t)) | ||||
|       + p[i+1] * t * (1 + 4 * t - 3 * sq(t)) | ||||
|       - p[i+2] * sq(t) * (1 - t) | ||||
|     ) * 0.5; | ||||
|     ) * 0.5f; | ||||
|   } | ||||
|  | ||||
|   static float bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const float &tx, const float &ty) { | ||||
| @@ -222,10 +222,8 @@ void print_bilinear_leveling_grid() { | ||||
|   } | ||||
|  | ||||
|   void bed_level_virt_interpolate() { | ||||
|     bilinear_grid_spacing_virt[X_AXIS] = bilinear_grid_spacing[X_AXIS] / (BILINEAR_SUBDIVISIONS); | ||||
|     bilinear_grid_spacing_virt[Y_AXIS] = bilinear_grid_spacing[Y_AXIS] / (BILINEAR_SUBDIVISIONS); | ||||
|     bilinear_grid_factor_virt[X_AXIS] = RECIPROCAL(bilinear_grid_spacing_virt[X_AXIS]); | ||||
|     bilinear_grid_factor_virt[Y_AXIS] = RECIPROCAL(bilinear_grid_spacing_virt[Y_AXIS]); | ||||
|     bilinear_grid_spacing_virt = bilinear_grid_spacing / (BILINEAR_SUBDIVISIONS); | ||||
|     bilinear_grid_factor_virt = bilinear_grid_spacing_virt.reciprocal(); | ||||
|     for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) | ||||
|       for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) | ||||
|         for (uint8_t ty = 0; ty < BILINEAR_SUBDIVISIONS; ty++) | ||||
| @@ -245,40 +243,38 @@ void print_bilinear_leveling_grid() { | ||||
|  | ||||
| // Refresh after other values have been updated | ||||
| void refresh_bed_level() { | ||||
|   bilinear_grid_factor[X_AXIS] = RECIPROCAL(bilinear_grid_spacing[X_AXIS]); | ||||
|   bilinear_grid_factor[Y_AXIS] = RECIPROCAL(bilinear_grid_spacing[Y_AXIS]); | ||||
|   bilinear_grid_factor = bilinear_grid_spacing.reciprocal(); | ||||
|   #if ENABLED(ABL_BILINEAR_SUBDIVISION) | ||||
|     bed_level_virt_interpolate(); | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| #if ENABLED(ABL_BILINEAR_SUBDIVISION) | ||||
|   #define ABL_BG_SPACING(A) bilinear_grid_spacing_virt[A] | ||||
|   #define ABL_BG_FACTOR(A)  bilinear_grid_factor_virt[A] | ||||
|   #define ABL_BG_SPACING(A) bilinear_grid_spacing_virt.A | ||||
|   #define ABL_BG_FACTOR(A)  bilinear_grid_factor_virt.A | ||||
|   #define ABL_BG_POINTS_X   ABL_GRID_POINTS_VIRT_X | ||||
|   #define ABL_BG_POINTS_Y   ABL_GRID_POINTS_VIRT_Y | ||||
|   #define ABL_BG_GRID(X,Y)  z_values_virt[X][Y] | ||||
| #else | ||||
|   #define ABL_BG_SPACING(A) bilinear_grid_spacing[A] | ||||
|   #define ABL_BG_FACTOR(A)  bilinear_grid_factor[A] | ||||
|   #define ABL_BG_SPACING(A) bilinear_grid_spacing.A | ||||
|   #define ABL_BG_FACTOR(A)  bilinear_grid_factor.A | ||||
|   #define ABL_BG_POINTS_X   GRID_MAX_POINTS_X | ||||
|   #define ABL_BG_POINTS_Y   GRID_MAX_POINTS_Y | ||||
|   #define ABL_BG_GRID(X,Y)  z_values[X][Y] | ||||
| #endif | ||||
|  | ||||
| // Get the Z adjustment for non-linear bed leveling | ||||
| float bilinear_z_offset(const float raw[XYZ]) { | ||||
| float bilinear_z_offset(const xy_pos_t &raw) { | ||||
|  | ||||
|   static float z1, d2, z3, d4, L, D, ratio_x, ratio_y, | ||||
|                last_x = -999.999, last_y = -999.999; | ||||
|   static float z1, d2, z3, d4, L, D; | ||||
|  | ||||
|   static xy_pos_t prev { -999.999, -999.999 }, ratio; | ||||
|  | ||||
|   // Whole units for the grid line indices. Constrained within bounds. | ||||
|   static int8_t gridx, gridy, nextx, nexty, | ||||
|                 last_gridx = -99, last_gridy = -99; | ||||
|   static xy_int8_t thisg, nextg, lastg { -99, -99 }; | ||||
|  | ||||
|   // XY relative to the probed area | ||||
|   const float rx = raw[X_AXIS] - bilinear_start[X_AXIS], | ||||
|               ry = raw[Y_AXIS] - bilinear_start[Y_AXIS]; | ||||
|   xy_pos_t rel = raw - bilinear_start.asFloat(); | ||||
|  | ||||
|   #if ENABLED(EXTRAPOLATE_BEYOND_GRID) | ||||
|     #define FAR_EDGE_OR_BOX 2   // Keep using the last grid box | ||||
| @@ -286,63 +282,62 @@ float bilinear_z_offset(const float raw[XYZ]) { | ||||
|     #define FAR_EDGE_OR_BOX 1   // Just use the grid far edge | ||||
|   #endif | ||||
|  | ||||
|   if (last_x != rx) { | ||||
|     last_x = rx; | ||||
|     ratio_x = rx * ABL_BG_FACTOR(X_AXIS); | ||||
|     const float gx = constrain(FLOOR(ratio_x), 0, ABL_BG_POINTS_X - (FAR_EDGE_OR_BOX)); | ||||
|     ratio_x -= gx;      // Subtract whole to get the ratio within the grid box | ||||
|   if (prev.x != rel.x) { | ||||
|     prev.x = rel.x; | ||||
|     ratio.x = rel.x * ABL_BG_FACTOR(x); | ||||
|     const float gx = constrain(FLOOR(ratio.x), 0, ABL_BG_POINTS_X - (FAR_EDGE_OR_BOX)); | ||||
|     ratio.x -= gx;      // Subtract whole to get the ratio within the grid box | ||||
|  | ||||
|     #if DISABLED(EXTRAPOLATE_BEYOND_GRID) | ||||
|       // Beyond the grid maintain height at grid edges | ||||
|       NOLESS(ratio_x, 0); // Never < 0.0. (> 1.0 is ok when nextx==gridx.) | ||||
|       NOLESS(ratio.x, 0); // Never <0 (>1 is ok when nextg.x==thisg.x) | ||||
|     #endif | ||||
|  | ||||
|     gridx = gx; | ||||
|     nextx = _MIN(gridx + 1, ABL_BG_POINTS_X - 1); | ||||
|     thisg.x = gx; | ||||
|     nextg.x = _MIN(thisg.x + 1, ABL_BG_POINTS_X - 1); | ||||
|   } | ||||
|  | ||||
|   if (last_y != ry || last_gridx != gridx) { | ||||
|   if (prev.y != rel.y || lastg.x != thisg.x) { | ||||
|  | ||||
|     if (last_y != ry) { | ||||
|       last_y = ry; | ||||
|       ratio_y = ry * ABL_BG_FACTOR(Y_AXIS); | ||||
|       const float gy = constrain(FLOOR(ratio_y), 0, ABL_BG_POINTS_Y - (FAR_EDGE_OR_BOX)); | ||||
|       ratio_y -= gy; | ||||
|     if (prev.y != rel.y) { | ||||
|       prev.y = rel.y; | ||||
|       ratio.y = rel.y * ABL_BG_FACTOR(y); | ||||
|       const float gy = constrain(FLOOR(ratio.y), 0, ABL_BG_POINTS_Y - (FAR_EDGE_OR_BOX)); | ||||
|       ratio.y -= gy; | ||||
|  | ||||
|       #if DISABLED(EXTRAPOLATE_BEYOND_GRID) | ||||
|         // Beyond the grid maintain height at grid edges | ||||
|         NOLESS(ratio_y, 0); // Never < 0.0. (> 1.0 is ok when nexty==gridy.) | ||||
|         NOLESS(ratio.y, 0); // Never < 0.0. (> 1.0 is ok when nextg.y==thisg.y.) | ||||
|       #endif | ||||
|  | ||||
|       gridy = gy; | ||||
|       nexty = _MIN(gridy + 1, ABL_BG_POINTS_Y - 1); | ||||
|       thisg.y = gy; | ||||
|       nextg.y = _MIN(thisg.y + 1, ABL_BG_POINTS_Y - 1); | ||||
|     } | ||||
|  | ||||
|     if (last_gridx != gridx || last_gridy != gridy) { | ||||
|       last_gridx = gridx; | ||||
|       last_gridy = gridy; | ||||
|     if (lastg != thisg) { | ||||
|       lastg = thisg; | ||||
|       // Z at the box corners | ||||
|       z1 = ABL_BG_GRID(gridx, gridy);       // left-front | ||||
|       d2 = ABL_BG_GRID(gridx, nexty) - z1;  // left-back (delta) | ||||
|       z3 = ABL_BG_GRID(nextx, gridy);       // right-front | ||||
|       d4 = ABL_BG_GRID(nextx, nexty) - z3;  // right-back (delta) | ||||
|       z1 = ABL_BG_GRID(thisg.x, thisg.y);       // left-front | ||||
|       d2 = ABL_BG_GRID(thisg.x, nextg.y) - z1;  // left-back (delta) | ||||
|       z3 = ABL_BG_GRID(nextg.x, thisg.y);       // right-front | ||||
|       d4 = ABL_BG_GRID(nextg.x, nextg.y) - z3;  // right-back (delta) | ||||
|     } | ||||
|  | ||||
|     // Bilinear interpolate. Needed since ry or gridx has changed. | ||||
|                 L = z1 + d2 * ratio_y;   // Linear interp. LF -> LB | ||||
|     const float R = z3 + d4 * ratio_y;   // Linear interp. RF -> RB | ||||
|     // Bilinear interpolate. Needed since rel.y or thisg.x has changed. | ||||
|                 L = z1 + d2 * ratio.y;   // Linear interp. LF -> LB | ||||
|     const float R = z3 + d4 * ratio.y;   // Linear interp. RF -> RB | ||||
|  | ||||
|     D = R - L; | ||||
|   } | ||||
|  | ||||
|   const float offset = L + ratio_x * D;   // the offset almost always changes | ||||
|   const float offset = L + ratio.x * D;   // the offset almost always changes | ||||
|  | ||||
|   /* | ||||
|   static float last_offset = 0; | ||||
|   if (ABS(last_offset - offset) > 0.2) { | ||||
|     SERIAL_ECHOLNPAIR("Sudden Shift at x=", rx, " / ", bilinear_grid_spacing[X_AXIS], " -> gridx=", gridx); | ||||
|     SERIAL_ECHOLNPAIR(" y=", ry, " / ", bilinear_grid_spacing[Y_AXIS], " -> gridy=", gridy); | ||||
|     SERIAL_ECHOLNPAIR(" ratio_x=", ratio_x, " ratio_y=", ratio_y); | ||||
|     SERIAL_ECHOLNPAIR("Sudden Shift at x=", rel.x, " / ", bilinear_grid_spacing.x, " -> thisg.x=", thisg.x); | ||||
|     SERIAL_ECHOLNPAIR(" y=", rel.y, " / ", bilinear_grid_spacing.y, " -> thisg.y=", thisg.y); | ||||
|     SERIAL_ECHOLNPAIR(" ratio.x=", ratio.x, " ratio.y=", ratio.y); | ||||
|     SERIAL_ECHOLNPAIR(" z1=", z1, " z2=", z2, " z3=", z3, " z4=", z4); | ||||
|     SERIAL_ECHOLNPAIR(" L=", L, " R=", R, " offset=", offset); | ||||
|   } | ||||
| @@ -354,7 +349,7 @@ float bilinear_z_offset(const float raw[XYZ]) { | ||||
|  | ||||
| #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES) | ||||
|  | ||||
|   #define CELL_INDEX(A,V) ((V - bilinear_start[_AXIS(A)]) * ABL_BG_FACTOR(_AXIS(A))) | ||||
|   #define CELL_INDEX(A,V) ((V - bilinear_start.A) * ABL_BG_FACTOR(A)) | ||||
|  | ||||
|   /** | ||||
|    * Prepare a bilinear-leveled linear move on Cartesian, | ||||
| @@ -362,62 +357,61 @@ float bilinear_z_offset(const float raw[XYZ]) { | ||||
|    */ | ||||
|   void bilinear_line_to_destination(const feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) { | ||||
|     // Get current and destination cells for this line | ||||
|     int cx1 = CELL_INDEX(X, current_position[X_AXIS]), | ||||
|         cy1 = CELL_INDEX(Y, current_position[Y_AXIS]), | ||||
|         cx2 = CELL_INDEX(X, destination[X_AXIS]), | ||||
|         cy2 = CELL_INDEX(Y, destination[Y_AXIS]); | ||||
|     LIMIT(cx1, 0, ABL_BG_POINTS_X - 2); | ||||
|     LIMIT(cy1, 0, ABL_BG_POINTS_Y - 2); | ||||
|     LIMIT(cx2, 0, ABL_BG_POINTS_X - 2); | ||||
|     LIMIT(cy2, 0, ABL_BG_POINTS_Y - 2); | ||||
|     xy_int_t c1 { CELL_INDEX(x, current_position.x), CELL_INDEX(y, current_position.y) }, | ||||
|              c2 { CELL_INDEX(x, destination.x), CELL_INDEX(y, destination.y) }; | ||||
|     LIMIT(c1.x, 0, ABL_BG_POINTS_X - 2); | ||||
|     LIMIT(c1.y, 0, ABL_BG_POINTS_Y - 2); | ||||
|     LIMIT(c2.x, 0, ABL_BG_POINTS_X - 2); | ||||
|     LIMIT(c2.y, 0, ABL_BG_POINTS_Y - 2); | ||||
|  | ||||
|     // Start and end in the same cell? No split needed. | ||||
|     if (cx1 == cx2 && cy1 == cy2) { | ||||
|       set_current_from_destination(); | ||||
|     if (c1 == c2) { | ||||
|       current_position = destination; | ||||
|       line_to_current_position(scaled_fr_mm_s); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     #define LINE_SEGMENT_END(A) (current_position[_AXIS(A)] + (destination[_AXIS(A)] - current_position[_AXIS(A)]) * normalized_dist) | ||||
|     #define LINE_SEGMENT_END(A) (current_position.A + (destination.A - current_position.A) * normalized_dist) | ||||
|  | ||||
|     float normalized_dist, end[XYZE]; | ||||
|     const int8_t gcx = _MAX(cx1, cx2), gcy = _MAX(cy1, cy2); | ||||
|     float normalized_dist; | ||||
|     xyze_pos_t end; | ||||
|     const xy_int8_t gc { _MAX(c1.x, c2.x), _MAX(c1.y, c2.y) }; | ||||
|  | ||||
|     // Crosses on the X and not already split on this X? | ||||
|     // The x_splits flags are insurance against rounding errors. | ||||
|     if (cx2 != cx1 && TEST(x_splits, gcx)) { | ||||
|     if (c2.x != c1.x && TEST(x_splits, gc.x)) { | ||||
|       // Split on the X grid line | ||||
|       CBI(x_splits, gcx); | ||||
|       COPY(end, destination); | ||||
|       destination[X_AXIS] = bilinear_start[X_AXIS] + ABL_BG_SPACING(X_AXIS) * gcx; | ||||
|       normalized_dist = (destination[X_AXIS] - current_position[X_AXIS]) / (end[X_AXIS] - current_position[X_AXIS]); | ||||
|       destination[Y_AXIS] = LINE_SEGMENT_END(Y); | ||||
|       CBI(x_splits, gc.x); | ||||
|       end = destination; | ||||
|       destination.x = bilinear_start.x + ABL_BG_SPACING(x) * gc.x; | ||||
|       normalized_dist = (destination.x - current_position.x) / (end.x - current_position.x); | ||||
|       destination.y = LINE_SEGMENT_END(y); | ||||
|     } | ||||
|     // Crosses on the Y and not already split on this Y? | ||||
|     else if (cy2 != cy1 && TEST(y_splits, gcy)) { | ||||
|     else if (c2.y != c1.y && TEST(y_splits, gc.y)) { | ||||
|       // Split on the Y grid line | ||||
|       CBI(y_splits, gcy); | ||||
|       COPY(end, destination); | ||||
|       destination[Y_AXIS] = bilinear_start[Y_AXIS] + ABL_BG_SPACING(Y_AXIS) * gcy; | ||||
|       normalized_dist = (destination[Y_AXIS] - current_position[Y_AXIS]) / (end[Y_AXIS] - current_position[Y_AXIS]); | ||||
|       destination[X_AXIS] = LINE_SEGMENT_END(X); | ||||
|       CBI(y_splits, gc.y); | ||||
|       end = destination; | ||||
|       destination.y = bilinear_start.y + ABL_BG_SPACING(y) * gc.y; | ||||
|       normalized_dist = (destination.y - current_position.y) / (end.y - current_position.y); | ||||
|       destination.x = LINE_SEGMENT_END(x); | ||||
|     } | ||||
|     else { | ||||
|       // Must already have been split on these border(s) | ||||
|       // This should be a rare case. | ||||
|       set_current_from_destination(); | ||||
|       current_position = destination; | ||||
|       line_to_current_position(scaled_fr_mm_s); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     destination[Z_AXIS] = LINE_SEGMENT_END(Z); | ||||
|     destination[E_AXIS] = LINE_SEGMENT_END(E); | ||||
|     destination.z = LINE_SEGMENT_END(z); | ||||
|     destination.e = LINE_SEGMENT_END(e); | ||||
|  | ||||
|     // Do the split and look for more borders | ||||
|     bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits); | ||||
|  | ||||
|     // Restore destination from stack | ||||
|     COPY(destination, end); | ||||
|     destination = end; | ||||
|     bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -23,10 +23,10 @@ | ||||
|  | ||||
| #include "../../../inc/MarlinConfigPre.h" | ||||
|  | ||||
| extern int bilinear_grid_spacing[2], bilinear_start[2]; | ||||
| extern float bilinear_grid_factor[2], | ||||
|              z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y]; | ||||
| float bilinear_z_offset(const float raw[XYZ]); | ||||
| extern xy_int_t bilinear_grid_spacing, bilinear_start; | ||||
| extern xy_float_t bilinear_grid_factor; | ||||
| extern bed_mesh_t z_values; | ||||
| float bilinear_z_offset(const xy_pos_t &raw); | ||||
|  | ||||
| void extrapolate_unprobed_bed_level(); | ||||
| void print_bilinear_leveling_grid(); | ||||
| @@ -40,6 +40,6 @@ void refresh_bed_level(); | ||||
|   void bilinear_line_to_destination(const feedRate_t &scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF); | ||||
| #endif | ||||
|  | ||||
| #define _GET_MESH_X(I) (bilinear_start[X_AXIS] + (I) * bilinear_grid_spacing[X_AXIS]) | ||||
| #define _GET_MESH_Y(J) (bilinear_start[Y_AXIS] + (J) * bilinear_grid_spacing[Y_AXIS]) | ||||
| #define _GET_MESH_X(I) float(bilinear_start.x + (I) * bilinear_grid_spacing.x) | ||||
| #define _GET_MESH_Y(J) float(bilinear_start.y + (J) * bilinear_grid_spacing.y) | ||||
| #define Z_VALUES_ARR  z_values | ||||
|   | ||||
| @@ -51,7 +51,7 @@ bool leveling_is_valid() { | ||||
|     #if ENABLED(MESH_BED_LEVELING) | ||||
|       mbl.has_mesh() | ||||
|     #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|       !!bilinear_grid_spacing[X_AXIS] | ||||
|       !!bilinear_grid_spacing.x | ||||
|     #elif ENABLED(AUTO_BED_LEVELING_UBL) | ||||
|       ubl.mesh_is_valid() | ||||
|     #else // 3POINT, LINEAR | ||||
| @@ -81,13 +81,13 @@ void set_bed_leveling_enabled(const bool enable/*=true*/) { | ||||
|  | ||||
|     #if ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|       // Force bilinear_z_offset to re-calculate next time | ||||
|       const float reset[XYZ] = { -9999.999, -9999.999, 0 }; | ||||
|       const xyz_pos_t reset { -9999.999, -9999.999, 0 }; | ||||
|       (void)bilinear_z_offset(reset); | ||||
|     #endif | ||||
|  | ||||
|     if (planner.leveling_active) {      // leveling from on to off | ||||
|       // change unleveled current_position to physical current_position without moving steppers. | ||||
|       planner.apply_leveling(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS]); | ||||
|       planner.apply_leveling(current_position); | ||||
|       planner.leveling_active = false;  // disable only AFTER calling apply_leveling | ||||
|     } | ||||
|     else {                              // leveling from off to on | ||||
| @@ -116,9 +116,9 @@ TemporaryBedLevelingState::TemporaryBedLevelingState(const bool enable) : saved( | ||||
|     planner.set_z_fade_height(zfh); | ||||
|  | ||||
|     if (leveling_was_active) { | ||||
|       const float oldpos[] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] }; | ||||
|       const xyz_pos_t oldpos = current_position; | ||||
|       set_bed_leveling_enabled(true); | ||||
|       if (do_report && memcmp(oldpos, current_position, sizeof(oldpos))) | ||||
|       if (do_report && oldpos != current_position) | ||||
|         report_current_position(); | ||||
|     } | ||||
|   } | ||||
| @@ -137,8 +137,8 @@ void reset_bed_level() { | ||||
|     #if ENABLED(MESH_BED_LEVELING) | ||||
|       mbl.reset(); | ||||
|     #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|       bilinear_start[X_AXIS] = bilinear_start[Y_AXIS] = | ||||
|       bilinear_grid_spacing[X_AXIS] = bilinear_grid_spacing[Y_AXIS] = 0; | ||||
|       bilinear_start.reset(); | ||||
|       bilinear_grid_spacing.reset(); | ||||
|       for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) | ||||
|         for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) { | ||||
|           z_values[x][y] = NAN; | ||||
| @@ -223,25 +223,25 @@ void reset_bed_level() { | ||||
|  | ||||
| #if EITHER(MESH_BED_LEVELING, PROBE_MANUALLY) | ||||
|  | ||||
|   void _manual_goto_xy(const float &rx, const float &ry) { | ||||
|   void _manual_goto_xy(const xy_pos_t &pos) { | ||||
|  | ||||
|     #ifdef MANUAL_PROBE_START_Z | ||||
|       constexpr float startz = _MAX(0, MANUAL_PROBE_START_Z); | ||||
|       #if MANUAL_PROBE_HEIGHT > 0 | ||||
|         do_blocking_move_to(rx, ry, MANUAL_PROBE_HEIGHT); | ||||
|         do_blocking_move_to_z(_MAX(0,MANUAL_PROBE_START_Z)); | ||||
|         do_blocking_move_to(pos, MANUAL_PROBE_HEIGHT); | ||||
|         do_blocking_move_to_z(startz); | ||||
|       #else | ||||
|         do_blocking_move_to(rx, ry, _MAX(0,MANUAL_PROBE_START_Z)); | ||||
|         do_blocking_move_to(pos, startz); | ||||
|       #endif | ||||
|     #elif MANUAL_PROBE_HEIGHT > 0 | ||||
|       const float prev_z = current_position[Z_AXIS]; | ||||
|       do_blocking_move_to(rx, ry, MANUAL_PROBE_HEIGHT); | ||||
|       const float prev_z = current_position.z; | ||||
|       do_blocking_move_to(pos, MANUAL_PROBE_HEIGHT); | ||||
|       do_blocking_move_to_z(prev_z); | ||||
|     #else | ||||
|       do_blocking_move_to_xy(rx, ry); | ||||
|       do_blocking_move_to_xy(pos); | ||||
|     #endif | ||||
|  | ||||
|     current_position[X_AXIS] = rx; | ||||
|     current_position[Y_AXIS] = ry; | ||||
|     current_position = pos; | ||||
|  | ||||
|     #if ENABLED(LCD_BED_LEVELING) | ||||
|       ui.wait_for_bl_move = false; | ||||
|   | ||||
| @@ -38,7 +38,7 @@ void reset_bed_level(); | ||||
| #endif | ||||
|  | ||||
| #if EITHER(MESH_BED_LEVELING, PROBE_MANUALLY) | ||||
|   void _manual_goto_xy(const float &x, const float &y); | ||||
|   void _manual_goto_xy(const xy_pos_t &pos); | ||||
| #endif | ||||
|  | ||||
| /** | ||||
| @@ -57,11 +57,6 @@ class TemporaryBedLevelingState { | ||||
|  | ||||
|   typedef float bed_mesh_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y]; | ||||
|  | ||||
|   typedef struct { | ||||
|     int8_t x_index, y_index; | ||||
|     float distance; // When populated, the distance from the search location | ||||
|   } mesh_index_pair; | ||||
|  | ||||
|   #if ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|     #include "abl/abl.h" | ||||
|   #elif ENABLED(AUTO_BED_LEVELING_UBL) | ||||
| @@ -71,6 +66,7 @@ class TemporaryBedLevelingState { | ||||
|   #endif | ||||
|  | ||||
|   #define Z_VALUES(X,Y) Z_VALUES_ARR[X][Y] | ||||
|   #define _GET_MESH_POS(M) { _GET_MESH_X(M.a), _GET_MESH_Y(M.b) } | ||||
|  | ||||
|   #if EITHER(AUTO_BED_LEVELING_BILINEAR, MESH_BED_LEVELING) | ||||
|  | ||||
| @@ -85,4 +81,18 @@ class TemporaryBedLevelingState { | ||||
|  | ||||
|   #endif | ||||
|  | ||||
|   struct mesh_index_pair { | ||||
|     xy_int8_t pos; | ||||
|     float distance;   // When populated, the distance from the search location | ||||
|     void invalidate() { pos = -1; } | ||||
|     bool valid() const { return pos.x >= 0 && pos.y >= 0; } | ||||
|     #if ENABLED(AUTO_BED_LEVELING_UBL) | ||||
|       xy_pos_t meshpos() { | ||||
|         return { ubl.mesh_index_to_xpos(pos.x), ubl.mesh_index_to_ypos(pos.y) }; | ||||
|       } | ||||
|     #endif | ||||
|     operator xy_int8_t&() { return pos; } | ||||
|     operator const xy_int8_t&() const { return pos; } | ||||
|   }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -24,10 +24,9 @@ | ||||
|  | ||||
| #if ENABLED(MESH_BED_LEVELING) | ||||
|  | ||||
|   #include "mesh_bed_leveling.h" | ||||
|   #include "../bedlevel.h" | ||||
|  | ||||
|   #include "../../../module/motion.h" | ||||
|   #include "../../../feature/bedlevel/bedlevel.h" | ||||
|  | ||||
|   #if ENABLED(EXTENSIBLE_UI) | ||||
|     #include "../../../lcd/extensible_ui/ui_api.h" | ||||
| @@ -66,62 +65,60 @@ | ||||
|      */ | ||||
|     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 | ||||
|       int cx1 = cell_index_x(current_position[X_AXIS]), | ||||
|           cy1 = cell_index_y(current_position[Y_AXIS]), | ||||
|           cx2 = cell_index_x(destination[X_AXIS]), | ||||
|           cy2 = cell_index_y(destination[Y_AXIS]); | ||||
|       NOMORE(cx1, GRID_MAX_POINTS_X - 2); | ||||
|       NOMORE(cy1, GRID_MAX_POINTS_Y - 2); | ||||
|       NOMORE(cx2, GRID_MAX_POINTS_X - 2); | ||||
|       NOMORE(cy2, GRID_MAX_POINTS_Y - 2); | ||||
|       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); | ||||
|  | ||||
|       // Start and end in the same cell? No split needed. | ||||
|       if (cx1 == cx2 && cy1 == cy2) { | ||||
|       if (scel == ecel) { | ||||
|         line_to_destination(scaled_fr_mm_s); | ||||
|         set_current_from_destination(); | ||||
|         current_position = destination; | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       #define MBL_SEGMENT_END(A) (current_position[_AXIS(A)] + (destination[_AXIS(A)] - current_position[_AXIS(A)]) * normalized_dist) | ||||
|       #define MBL_SEGMENT_END(A) (current_position.A + (destination.A - current_position.A) * normalized_dist) | ||||
|  | ||||
|       float normalized_dist, end[XYZE]; | ||||
|       const int8_t gcx = _MAX(cx1, cx2), gcy = _MAX(cy1, cy2); | ||||
|       float normalized_dist; | ||||
|       xyze_pos_t dest; | ||||
|       const int8_t gcx = _MAX(scel.x, ecel.x), gcy = _MAX(scel.y, ecel.y); | ||||
|  | ||||
|       // Crosses on the X and not already split on this X? | ||||
|       // The x_splits flags are insurance against rounding errors. | ||||
|       if (cx2 != cx1 && TEST(x_splits, gcx)) { | ||||
|       if (ecel.x != scel.x && TEST(x_splits, gcx)) { | ||||
|         // Split on the X grid line | ||||
|         CBI(x_splits, gcx); | ||||
|         COPY(end, destination); | ||||
|         destination[X_AXIS] = index_to_xpos[gcx]; | ||||
|         normalized_dist = (destination[X_AXIS] - current_position[X_AXIS]) / (end[X_AXIS] - current_position[X_AXIS]); | ||||
|         destination[Y_AXIS] = MBL_SEGMENT_END(Y); | ||||
|         dest = destination; | ||||
|         destination.x = index_to_xpos[gcx]; | ||||
|         normalized_dist = (destination.x - current_position.x) / (dest.x - current_position.x); | ||||
|         destination.y = MBL_SEGMENT_END(y); | ||||
|       } | ||||
|       // Crosses on the Y and not already split on this Y? | ||||
|       else if (cy2 != cy1 && TEST(y_splits, gcy)) { | ||||
|       else if (ecel.y != scel.y && TEST(y_splits, gcy)) { | ||||
|         // Split on the Y grid line | ||||
|         CBI(y_splits, gcy); | ||||
|         COPY(end, destination); | ||||
|         destination[Y_AXIS] = index_to_ypos[gcy]; | ||||
|         normalized_dist = (destination[Y_AXIS] - current_position[Y_AXIS]) / (end[Y_AXIS] - current_position[Y_AXIS]); | ||||
|         destination[X_AXIS] = MBL_SEGMENT_END(X); | ||||
|         dest = destination; | ||||
|         destination.y = index_to_ypos[gcy]; | ||||
|         normalized_dist = (destination.y - current_position.y) / (dest.y - current_position.y); | ||||
|         destination.x = MBL_SEGMENT_END(x); | ||||
|       } | ||||
|       else { | ||||
|         // Must already have been split on these border(s) | ||||
|         // This should be a rare case. | ||||
|         line_to_destination(scaled_fr_mm_s); | ||||
|         set_current_from_destination(); | ||||
|         current_position = destination; | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       destination[Z_AXIS] = MBL_SEGMENT_END(Z); | ||||
|       destination[E_AXIS] = MBL_SEGMENT_END(E); | ||||
|       destination.z = MBL_SEGMENT_END(z); | ||||
|       destination.e = MBL_SEGMENT_END(e); | ||||
|  | ||||
|       // Do the split and look for more borders | ||||
|       line_to_destination(scaled_fr_mm_s, x_splits, y_splits); | ||||
|  | ||||
|       // Restore destination from stack | ||||
|       COPY(destination, end); | ||||
|       destination = dest; | ||||
|       line_to_destination(scaled_fr_mm_s, x_splits, y_splits); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -76,21 +76,27 @@ public: | ||||
|     int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST); | ||||
|     return constrain(cx, 0, (GRID_MAX_POINTS_X) - 2); | ||||
|   } | ||||
|  | ||||
|   static int8_t cell_index_y(const float &y) { | ||||
|     int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST); | ||||
|     return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 2); | ||||
|   } | ||||
|   static inline xy_int8_t cell_indexes(const float &x, const float &y) { | ||||
|     return { cell_index_x(x), cell_index_y(y) }; | ||||
|   } | ||||
|   static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); } | ||||
|  | ||||
|   static int8_t probe_index_x(const float &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; | ||||
|   } | ||||
|  | ||||
|   static int8_t probe_index_y(const float &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; | ||||
|   } | ||||
|   static inline xy_int8_t probe_indexes(const float &x, const float &y) { | ||||
|     return { probe_index_x(x), probe_index_y(y) }; | ||||
|   } | ||||
|   static inline xy_int8_t probe_indexes(const xy_pos_t &xy) { return probe_indexes(xy.x, xy.y); } | ||||
|  | ||||
|   static float calc_z0(const float &a0, const float &a1, const float &z1, const float &a2, const float &z2) { | ||||
|     const float delta_z = (z2 - z1) / (a2 - a1), | ||||
| @@ -98,21 +104,21 @@ public: | ||||
|     return z1 + delta_a * delta_z; | ||||
|   } | ||||
|  | ||||
|   static float get_z(const float &x0, const float &y0 | ||||
|   static float get_z(const xy_pos_t &pos | ||||
|     #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|       , const float &factor | ||||
|       , const float &factor=1.0f | ||||
|     #endif | ||||
|   ) { | ||||
|     const int8_t cx = cell_index_x(x0), cy = cell_index_y(y0); | ||||
|     const float z1 = calc_z0(x0, index_to_xpos[cx], z_values[cx][cy], index_to_xpos[cx + 1], z_values[cx + 1][cy]), | ||||
|                 z2 = calc_z0(x0, index_to_xpos[cx], z_values[cx][cy + 1], index_to_xpos[cx + 1], z_values[cx + 1][cy + 1]), | ||||
|                 z0 = calc_z0(y0, index_to_ypos[cy], z1, index_to_ypos[cy + 1], z2); | ||||
|     #if DISABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|       constexpr float factor = 1.0f; | ||||
|     #endif | ||||
|     const xy_int8_t ind = cell_indexes(pos); | ||||
|     const float x1 = index_to_xpos[ind.x], x2 = index_to_xpos[ind.x+1], | ||||
|                 y1 = index_to_xpos[ind.y], y2 = index_to_xpos[ind.y+1], | ||||
|                 z1 = calc_z0(pos.x, x1, z_values[ind.x][ind.y  ], x2, z_values[ind.x+1][ind.y  ]), | ||||
|                 z2 = calc_z0(pos.x, x1, z_values[ind.x][ind.y+1], x2, z_values[ind.x+1][ind.y+1]); | ||||
|  | ||||
|     return z_offset + z0 | ||||
|       #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|         * factor | ||||
|       #endif | ||||
|     ; | ||||
|     return z_offset + calc_z0(pos.y, y1, z1, y2, z2) * factor; | ||||
|   } | ||||
|  | ||||
|   #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES) | ||||
|   | ||||
| @@ -176,8 +176,7 @@ | ||||
|     // Add XY probe offset from extruder because probe_at_point() subtracts them when | ||||
|     // moving to the XY position to be measured. This ensures better agreement between | ||||
|     // the current Z position after G28 and the mesh values. | ||||
|     const float current_xi = find_closest_x_index(current_position[X_AXIS] + probe_offset[X_AXIS]), | ||||
|                 current_yi = find_closest_y_index(current_position[Y_AXIS] + probe_offset[Y_AXIS]); | ||||
|     const xy_int8_t curr = closest_indexes(xy_pos_t(current_position) + xy_pos_t(probe_offset)); | ||||
|  | ||||
|     if (!lcd) SERIAL_EOL(); | ||||
|     for (int8_t j = GRID_MAX_POINTS_Y - 1; j >= 0; j--) { | ||||
| @@ -193,7 +192,7 @@ | ||||
|       for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) { | ||||
|  | ||||
|         // Opening Brace or Space | ||||
|         const bool is_current = i == current_xi && j == current_yi; | ||||
|         const bool is_current = i == curr.x && j == curr.y; | ||||
|         if (human) SERIAL_CHAR(is_current ? '[' : ' '); | ||||
|  | ||||
|         // Z Value at current I, J | ||||
|   | ||||
| @@ -32,15 +32,12 @@ | ||||
| #define UBL_OK false | ||||
| #define UBL_ERR true | ||||
|  | ||||
| #define USE_NOZZLE_AS_REFERENCE 0 | ||||
| #define USE_PROBE_AS_REFERENCE 1 | ||||
|  | ||||
| // ubl_G29.cpp | ||||
|  | ||||
| enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP }; | ||||
|  | ||||
| // External references | ||||
|  | ||||
| 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)) | ||||
|  | ||||
| @@ -52,10 +49,11 @@ class unified_bed_leveling { | ||||
|                   g29_repetition_cnt, | ||||
|                   g29_storage_slot, | ||||
|                   g29_map_type; | ||||
|     static bool   g29_c_flag, g29_x_flag, g29_y_flag; | ||||
|     static float  g29_x_pos, g29_y_pos, | ||||
|                   g29_card_thickness, | ||||
|     static bool   g29_c_flag; | ||||
|     static float  g29_card_thickness, | ||||
|                   g29_constant; | ||||
|     static xy_pos_t g29_pos; | ||||
|     static xy_bool_t xy_seen; | ||||
|  | ||||
|     #if HAS_BED_PROBE | ||||
|       static int  g29_grid_size; | ||||
| @@ -65,16 +63,19 @@ class unified_bed_leveling { | ||||
|       static void move_z_with_encoder(const float &multiplier); | ||||
|       static float measure_point_with_encoder(); | ||||
|       static float measure_business_card_thickness(float in_height); | ||||
|       static void manually_probe_remaining_mesh(const float&, const float&, const float&, const float&, const bool) _O0; | ||||
|       static void fine_tune_mesh(const float &rx, const float &ry, const bool do_ubl_mesh_map) _O0; | ||||
|       static void manually_probe_remaining_mesh(const xy_pos_t&, const float&, const float&, const bool) _O0; | ||||
|       static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) _O0; | ||||
|     #endif | ||||
|  | ||||
|     static bool g29_parameter_parsing() _O0; | ||||
|     static void shift_mesh_height(); | ||||
|     static void probe_entire_mesh(const float &rx, const float &ry, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) _O0; | ||||
|     static void probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) _O0; | ||||
|     static void tilt_mesh_based_on_3pts(const float &z1, const float &z2, const float &z3); | ||||
|     static void tilt_mesh_based_on_probed_grid(const bool do_ubl_mesh_map); | ||||
|     static bool smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir); | ||||
|     static inline bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) { | ||||
|       return smart_fill_one(pos.x, pos.y, dir.x, dir.y); | ||||
|     } | ||||
|     static void smart_fill_mesh(); | ||||
|  | ||||
|     #if ENABLED(UBL_DEVEL_DEBUGGING) | ||||
| @@ -91,7 +92,7 @@ class unified_bed_leveling { | ||||
|     static void save_ubl_active_state_and_disable(); | ||||
|     static void restore_ubl_active_state_and_leave(); | ||||
|     static void display_map(const int) _O0; | ||||
|     static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const float&, const float&, const bool, uint16_t[16]) _O0; | ||||
|     static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0; | ||||
|     static mesh_index_pair find_furthest_invalid_mesh_point() _O0; | ||||
|     static void reset(); | ||||
|     static void invalidate(); | ||||
| @@ -118,14 +119,14 @@ class unified_bed_leveling { | ||||
|  | ||||
|     FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const float &z) { z_values[px][py] = z; } | ||||
|  | ||||
|     static int8_t get_cell_index_x(const float &x) { | ||||
|     static int8_t cell_index_x(const float &x) { | ||||
|       const int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST); | ||||
|       return constrain(cx, 0, (GRID_MAX_POINTS_X) - 1);   // -1 is appropriate if we want all movement to the X_MAX | ||||
|     }                                                     // position. But with this defined this way, it is possible | ||||
|                                                           // to extrapolate off of this point even further out. Probably | ||||
|                                                           // that is OK because something else should be keeping that from | ||||
|                                                           // happening and should not be worried about at this level. | ||||
|     static int8_t get_cell_index_y(const float &y) { | ||||
|     static int8_t cell_index_y(const float &y) { | ||||
|       const int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST); | ||||
|       return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 1);   // -1 is appropriate if we want all movement to the Y_MAX | ||||
|     }                                                     // position. But with this defined this way, it is possible | ||||
| @@ -133,15 +134,22 @@ class unified_bed_leveling { | ||||
|                                                           // that is OK because something else should be keeping that from | ||||
|                                                           // happening and should not be worried about at this level. | ||||
|  | ||||
|     static int8_t find_closest_x_index(const float &x) { | ||||
|     static inline xy_int8_t cell_indexes(const float &x, const float &y) { | ||||
|       return { cell_index_x(x), cell_index_y(y) }; | ||||
|     } | ||||
|     static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); } | ||||
|  | ||||
|     static int8_t closest_x_index(const float &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; | ||||
|     } | ||||
|  | ||||
|     static int8_t find_closest_y_index(const float &y) { | ||||
|     static int8_t closest_y_index(const float &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; | ||||
|     } | ||||
|     static inline xy_int8_t closest_indexes(const xy_pos_t &xy) { | ||||
|       return { closest_x_index(xy.x), closest_y_index(xy.y) }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      *                           z2   --| | ||||
| @@ -228,8 +236,7 @@ class unified_bed_leveling { | ||||
|      * on the Y position within the cell. | ||||
|      */ | ||||
|     static float get_z_correction(const float &rx0, const float &ry0) { | ||||
|       const int8_t cx = get_cell_index_x(rx0), | ||||
|                    cy = get_cell_index_y(ry0); // return values are clamped | ||||
|       const int8_t cx = cell_index_x(rx0), cy = cell_index_y(ry0); // return values are clamped | ||||
|  | ||||
|       /** | ||||
|        * Check if the requested location is off the mesh.  If so, and | ||||
| @@ -275,11 +282,11 @@ class unified_bed_leveling { | ||||
|       } | ||||
|       return z0; | ||||
|     } | ||||
|     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); | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|     } | ||||
|   | ||||
| @@ -53,8 +53,6 @@ | ||||
|  | ||||
|   #define UBL_G29_P31 | ||||
|  | ||||
|   extern float destination[XYZE], current_position[XYZE]; | ||||
|  | ||||
|   #if HAS_LCD_MENU | ||||
|     void _lcd_ubl_output_map_lcd(); | ||||
|   #endif | ||||
| @@ -67,13 +65,11 @@ | ||||
|          unified_bed_leveling::g29_repetition_cnt, | ||||
|          unified_bed_leveling::g29_storage_slot = 0, | ||||
|          unified_bed_leveling::g29_map_type; | ||||
|   bool   unified_bed_leveling::g29_c_flag, | ||||
|          unified_bed_leveling::g29_x_flag, | ||||
|          unified_bed_leveling::g29_y_flag; | ||||
|   float  unified_bed_leveling::g29_x_pos, | ||||
|          unified_bed_leveling::g29_y_pos, | ||||
|          unified_bed_leveling::g29_card_thickness = 0, | ||||
|   bool   unified_bed_leveling::g29_c_flag; | ||||
|   float  unified_bed_leveling::g29_card_thickness = 0, | ||||
|          unified_bed_leveling::g29_constant = 0; | ||||
|   xy_bool_t unified_bed_leveling::xy_seen; | ||||
|   xy_pos_t unified_bed_leveling::g29_pos; | ||||
|  | ||||
|   #if HAS_BED_PROBE | ||||
|     int  unified_bed_leveling::g29_grid_size; | ||||
| @@ -330,18 +326,19 @@ | ||||
|       else { | ||||
|         while (g29_repetition_cnt--) { | ||||
|           if (cnt > 20) { cnt = 0; idle(); } | ||||
|           const mesh_index_pair location = find_closest_mesh_point_of_type(REAL, g29_x_pos, g29_y_pos, USE_NOZZLE_AS_REFERENCE, nullptr); | ||||
|           if (location.x_index < 0) { | ||||
|             // No more REACHABLE mesh points to invalidate, so we ASSUME the user | ||||
|           const mesh_index_pair closest = find_closest_mesh_point_of_type(REAL, g29_pos); | ||||
|           const xy_int8_t &cpos = closest.pos; | ||||
|           if (cpos.x < 0) { | ||||
|             // No more REAL mesh points to invalidate, so we ASSUME the user | ||||
|             // meant to invalidate the ENTIRE mesh, which cannot be done with | ||||
|             // find_closest_mesh_point loop which only returns REACHABLE points. | ||||
|             // find_closest_mesh_point loop which only returns REAL points. | ||||
|             set_all_mesh_points_to_value(NAN); | ||||
|             SERIAL_ECHOLNPGM("Entire Mesh invalidated.\n"); | ||||
|             break;            // No more invalid Mesh Points to populate | ||||
|           } | ||||
|           z_values[location.x_index][location.y_index] = NAN; | ||||
|           z_values[cpos.x][cpos.y] = NAN; | ||||
|           #if ENABLED(EXTENSIBLE_UI) | ||||
|             ExtUI::onMeshUpdate(location.x_index, location.y_index, 0); | ||||
|             ExtUI::onMeshUpdate(closest, 0); | ||||
|           #endif | ||||
|           cnt++; | ||||
|         } | ||||
| @@ -448,13 +445,13 @@ | ||||
|               SERIAL_ECHOLNPGM("Mesh invalidated. Probing mesh."); | ||||
|             } | ||||
|             if (g29_verbose_level > 1) { | ||||
|               SERIAL_ECHOPAIR("Probing around (", g29_x_pos); | ||||
|               SERIAL_ECHOPAIR("Probing around (", g29_pos.x); | ||||
|               SERIAL_CHAR(','); | ||||
|               SERIAL_ECHO(g29_y_pos); | ||||
|               SERIAL_ECHO(g29_pos.y); | ||||
|               SERIAL_ECHOLNPGM(").\n"); | ||||
|             } | ||||
|             probe_entire_mesh(g29_x_pos + probe_offset[X_AXIS], g29_y_pos + probe_offset[Y_AXIS], | ||||
|                               parser.seen('T'), parser.seen('E'), parser.seen('U')); | ||||
|             const xy_pos_t near = g29_pos + probe_offset; | ||||
|             probe_entire_mesh(near, parser.seen('T'), parser.seen('E'), parser.seen('U')); | ||||
|  | ||||
|             report_current_position(); | ||||
|             probe_deployed = true; | ||||
| @@ -470,7 +467,7 @@ | ||||
|             SERIAL_ECHOLNPGM("Manually probing unreachable mesh locations."); | ||||
|             do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES); | ||||
|  | ||||
|             if (parser.seen('C') && !g29_x_flag && !g29_y_flag) { | ||||
|             if (parser.seen('C') && !xy_seen) { | ||||
|               /** | ||||
|                * Use a good default location for the path. | ||||
|                * The flipped > and < operators in these comparisons is intentional. | ||||
| @@ -478,13 +475,14 @@ | ||||
|                * It may make sense to have Delta printers default to the center of the bed. | ||||
|                * Until that is decided, this can be forced with the X and Y parameters. | ||||
|                */ | ||||
|               #if IS_KINEMATIC | ||||
|                 g29_x_pos = X_HOME_POS; | ||||
|                 g29_y_pos = Y_HOME_POS; | ||||
|               #else // cartesian | ||||
|                 g29_x_pos = probe_offset[X_AXIS] > 0 ? X_BED_SIZE : 0; | ||||
|                 g29_y_pos = probe_offset[Y_AXIS] < 0 ? Y_BED_SIZE : 0; | ||||
|               #endif | ||||
|               g29_pos.set( | ||||
|                 #if IS_KINEMATIC | ||||
|                   X_HOME_POS, Y_HOME_POS | ||||
|                 #else | ||||
|                   probe_offset.x > 0 ? X_BED_SIZE : 0, | ||||
|                   probe_offset.y < 0 ? Y_BED_SIZE : 0 | ||||
|                 #endif | ||||
|               ); | ||||
|             } | ||||
|  | ||||
|             if (parser.seen('B')) { | ||||
| @@ -496,13 +494,13 @@ | ||||
|               probe_deployed = true; | ||||
|             } | ||||
|  | ||||
|             if (!position_is_reachable(g29_x_pos, g29_y_pos)) { | ||||
|             if (!position_is_reachable(g29_pos)) { | ||||
|               SERIAL_ECHOLNPGM("XY outside printable radius."); | ||||
|               return; | ||||
|             } | ||||
|  | ||||
|             const float height = parser.floatval('H', Z_CLEARANCE_BETWEEN_PROBES); | ||||
|             manually_probe_remaining_mesh(g29_x_pos, g29_y_pos, height, g29_card_thickness, parser.seen('T')); | ||||
|             manually_probe_remaining_mesh(g29_pos, height, g29_card_thickness, parser.seen('T')); | ||||
|  | ||||
|             SERIAL_ECHOLNPGM("G29 P2 finished."); | ||||
|  | ||||
| @@ -530,20 +528,22 @@ | ||||
|             } | ||||
|             else { | ||||
|               while (g29_repetition_cnt--) {  // this only populates reachable mesh points near | ||||
|                 const mesh_index_pair location = find_closest_mesh_point_of_type(INVALID, g29_x_pos, g29_y_pos, USE_NOZZLE_AS_REFERENCE, nullptr); | ||||
|                 if (location.x_index < 0) { | ||||
|                   // No more REACHABLE INVALID mesh points to populate, so we ASSUME | ||||
|                 const mesh_index_pair closest = find_closest_mesh_point_of_type(INVALID, g29_pos); | ||||
|                 const xy_int8_t &cpos = closest.pos; | ||||
|                 if (cpos.x < 0) { | ||||
|                   // No more REAL INVALID mesh points to populate, so we ASSUME | ||||
|                   // user meant to populate ALL INVALID mesh points to value | ||||
|                   for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) | ||||
|                     for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) | ||||
|                       if (isnan(z_values[x][y])) | ||||
|                         z_values[x][y] = g29_constant; | ||||
|                       if (isnan(z_values[x][y])) z_values[x][y] = g29_constant; | ||||
|                   break; // No more invalid Mesh Points to populate | ||||
|                 } | ||||
|                 z_values[location.x_index][location.y_index] = g29_constant; | ||||
|                 #if ENABLED(EXTENSIBLE_UI) | ||||
|                   ExtUI::onMeshUpdate(location.x_index, location.y_index, z_values[location.x_index][location.y_index]); | ||||
|                 #endif | ||||
|                 else { | ||||
|                   z_values[cpos.x][cpos.y] = g29_constant; | ||||
|                   #if ENABLED(EXTENSIBLE_UI) | ||||
|                     ExtUI::onMeshUpdate(closest, g29_constant); | ||||
|                   #endif | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
| @@ -576,7 +576,7 @@ | ||||
|  | ||||
|         case 4: // Fine Tune (i.e., Edit) the Mesh | ||||
|           #if HAS_LCD_MENU | ||||
|             fine_tune_mesh(g29_x_pos, g29_y_pos, parser.seen('T')); | ||||
|             fine_tune_mesh(g29_pos, parser.seen('T')); | ||||
|           #else | ||||
|             SERIAL_ECHOLNPGM("?P4 is only available when an LCD is present."); | ||||
|             return; | ||||
| @@ -740,9 +740,7 @@ | ||||
|      * Probe all invalidated locations of the mesh that can be reached by the probe. | ||||
|      * This attempts to fill in locations closest to the nozzle's start location first. | ||||
|      */ | ||||
|     void unified_bed_leveling::probe_entire_mesh(const float &rx, const float &ry, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) { | ||||
|       mesh_index_pair location; | ||||
|  | ||||
|     void unified_bed_leveling::probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) { | ||||
|       #if HAS_LCD_MENU | ||||
|         ui.capture(); | ||||
|       #endif | ||||
| @@ -752,6 +750,7 @@ | ||||
|  | ||||
|       uint8_t count = GRID_MAX_POINTS; | ||||
|  | ||||
|       mesh_index_pair best; | ||||
|       do { | ||||
|         if (do_ubl_mesh_map) display_map(g29_map_type); | ||||
|  | ||||
| @@ -773,23 +772,23 @@ | ||||
|           } | ||||
|         #endif | ||||
|  | ||||
|         if (do_furthest) | ||||
|           location = find_furthest_invalid_mesh_point(); | ||||
|         else | ||||
|           location = find_closest_mesh_point_of_type(INVALID, rx, ry, USE_PROBE_AS_REFERENCE, nullptr); | ||||
|         best = do_furthest | ||||
|           ? find_furthest_invalid_mesh_point() | ||||
|           : find_closest_mesh_point_of_type(INVALID, near, true); | ||||
|  | ||||
|         if (location.x_index >= 0) {    // mesh point found and is reachable by probe | ||||
|           const float rawx = mesh_index_to_xpos(location.x_index), | ||||
|                       rawy = mesh_index_to_ypos(location.y_index), | ||||
|                       measured_z = probe_at_point(rawx, rawy, stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level); // TODO: Needs error handling | ||||
|           z_values[location.x_index][location.y_index] = measured_z; | ||||
|         if (best.pos.x >= 0) {    // mesh point found and is reachable by probe | ||||
|           const float measured_z = probe_at_point( | ||||
|                         best.meshpos(), | ||||
|                         stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level | ||||
|                       ); | ||||
|           z_values[best.pos.x][best.pos.y] = measured_z; | ||||
|           #if ENABLED(EXTENSIBLE_UI) | ||||
|             ExtUI::onMeshUpdate(location.x_index, location.y_index, measured_z); | ||||
|             ExtUI::onMeshUpdate(best, measured_z); | ||||
|           #endif | ||||
|         } | ||||
|         SERIAL_FLUSH(); // Prevent host M105 buffer overrun. | ||||
|  | ||||
|       } while (location.x_index >= 0 && --count); | ||||
|       } while (best.pos.x >= 0 && --count); | ||||
|  | ||||
|       STOW_PROBE(); | ||||
|  | ||||
| @@ -800,8 +799,8 @@ | ||||
|       restore_ubl_active_state_and_leave(); | ||||
|  | ||||
|       do_blocking_move_to_xy( | ||||
|         constrain(rx - probe_offset[X_AXIS], MESH_MIN_X, MESH_MAX_X), | ||||
|         constrain(ry - probe_offset[Y_AXIS], MESH_MIN_Y, MESH_MAX_Y) | ||||
|         constrain(near.x - probe_offset.x, MESH_MIN_X, MESH_MAX_X), | ||||
|         constrain(near.y - probe_offset.y, MESH_MIN_Y, MESH_MAX_Y) | ||||
|       ); | ||||
|     } | ||||
|  | ||||
| @@ -835,7 +834,7 @@ | ||||
|         idle(); | ||||
|         gcode.reset_stepper_timeout(); // Keep steppers powered | ||||
|         if (encoder_diff) { | ||||
|           do_blocking_move_to_z(current_position[Z_AXIS] + float(encoder_diff) * multiplier); | ||||
|           do_blocking_move_to_z(current_position.z + float(encoder_diff) * multiplier); | ||||
|           encoder_diff = 0; | ||||
|         } | ||||
|       } | ||||
| @@ -844,7 +843,7 @@ | ||||
|     float unified_bed_leveling::measure_point_with_encoder() { | ||||
|       KEEPALIVE_STATE(PAUSED_FOR_USER); | ||||
|       move_z_with_encoder(0.01f); | ||||
|       return current_position[Z_AXIS]; | ||||
|       return current_position.z; | ||||
|     } | ||||
|  | ||||
|     static void echo_and_take_a_measurement() { SERIAL_ECHOLNPGM(" and take a measurement."); } | ||||
| @@ -863,7 +862,7 @@ | ||||
|       echo_and_take_a_measurement(); | ||||
|  | ||||
|       const float z1 = measure_point_with_encoder(); | ||||
|       do_blocking_move_to_z(current_position[Z_AXIS] + SIZE_OF_LITTLE_RAISE); | ||||
|       do_blocking_move_to_z(current_position.z + SIZE_OF_LITTLE_RAISE); | ||||
|       planner.synchronize(); | ||||
|  | ||||
|       SERIAL_ECHOPGM("Remove shim"); | ||||
| @@ -872,7 +871,7 @@ | ||||
|  | ||||
|       const float z2 = measure_point_with_encoder(); | ||||
|  | ||||
|       do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES); | ||||
|       do_blocking_move_to_z(current_position.z + Z_CLEARANCE_BETWEEN_PROBES); | ||||
|  | ||||
|       const float thickness = ABS(z1 - z2); | ||||
|  | ||||
| @@ -888,29 +887,33 @@ | ||||
|       return thickness; | ||||
|     } | ||||
|  | ||||
|     void unified_bed_leveling::manually_probe_remaining_mesh(const float &rx, const float &ry, const float &z_clearance, const float &thick, const bool do_ubl_mesh_map) { | ||||
|     void unified_bed_leveling::manually_probe_remaining_mesh(const xy_pos_t &pos, const float &z_clearance, const float &thick, const bool do_ubl_mesh_map) { | ||||
|  | ||||
|       ui.capture(); | ||||
|  | ||||
|       save_ubl_active_state_and_disable();  // No bed level correction so only raw data is obtained | ||||
|       do_blocking_move_to(current_position[X_AXIS], current_position[Y_AXIS], z_clearance); | ||||
|       do_blocking_move_to(current_position.x, current_position.y, z_clearance); | ||||
|  | ||||
|       ui.return_to_status(); | ||||
|  | ||||
|       mesh_index_pair location; | ||||
|       xy_int8_t &lpos = location.pos; | ||||
|       do { | ||||
|         location = find_closest_mesh_point_of_type(INVALID, rx, ry, USE_NOZZLE_AS_REFERENCE, nullptr); | ||||
|         location = find_closest_mesh_point_of_type(INVALID, pos); | ||||
|         // It doesn't matter if the probe can't reach the NAN location. This is a manual probe. | ||||
|         if (location.x_index < 0 && location.y_index < 0) continue; | ||||
|         if (!location.valid()) continue; | ||||
|  | ||||
|         const float xProbe = mesh_index_to_xpos(location.x_index), | ||||
|                     yProbe = mesh_index_to_ypos(location.y_index); | ||||
|         const xyz_pos_t ppos = { | ||||
|           mesh_index_to_xpos(lpos.x), | ||||
|           mesh_index_to_ypos(lpos.y), | ||||
|           Z_CLEARANCE_BETWEEN_PROBES | ||||
|         }; | ||||
|  | ||||
|         if (!position_is_reachable(xProbe, yProbe)) break; // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points) | ||||
|         if (!position_is_reachable(ppos)) break; // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points) | ||||
|  | ||||
|         LCD_MESSAGEPGM(MSG_UBL_MOVING_TO_NEXT); | ||||
|  | ||||
|         do_blocking_move_to(xProbe, yProbe, Z_CLEARANCE_BETWEEN_PROBES); | ||||
|         do_blocking_move_to(ppos); | ||||
|         do_blocking_move_to_z(z_clearance); | ||||
|  | ||||
|         KEEPALIVE_STATE(PAUSED_FOR_USER); | ||||
| @@ -932,20 +935,20 @@ | ||||
|           return restore_ubl_active_state_and_leave(); | ||||
|         } | ||||
|  | ||||
|         z_values[location.x_index][location.y_index] = current_position[Z_AXIS] - thick; | ||||
|         z_values[lpos.x][lpos.y] = current_position.z - thick; | ||||
|         #if ENABLED(EXTENSIBLE_UI) | ||||
|           ExtUI::onMeshUpdate(location.x_index, location.y_index, z_values[location.x_index][location.y_index]); | ||||
|           ExtUI::onMeshUpdate(location, z_values[lpos.x][lpos.y]); | ||||
|         #endif | ||||
|  | ||||
|         if (g29_verbose_level > 2) | ||||
|           SERIAL_ECHOLNPAIR_F("Mesh Point Measured at: ", z_values[location.x_index][location.y_index], 6); | ||||
|           SERIAL_ECHOLNPAIR_F("Mesh Point Measured at: ", z_values[lpos.x][lpos.y], 6); | ||||
|         SERIAL_FLUSH(); // Prevent host M105 buffer overrun. | ||||
|       } while (location.x_index >= 0 && location.y_index >= 0); | ||||
|       } while (location.valid()); | ||||
|  | ||||
|       if (do_ubl_mesh_map) display_map(g29_map_type);  // show user where we're probing | ||||
|  | ||||
|       restore_ubl_active_state_and_leave(); | ||||
|       do_blocking_move_to(rx, ry, Z_CLEARANCE_DEPLOY_PROBE); | ||||
|       do_blocking_move_to(pos, Z_CLEARANCE_DEPLOY_PROBE); | ||||
|     } | ||||
|  | ||||
|     inline void set_message_with_feedback(PGM_P const msg_P) { | ||||
| @@ -959,8 +962,8 @@ | ||||
|       set_message_with_feedback(PSTR(MSG_EDITING_STOPPED)); | ||||
|     } | ||||
|  | ||||
|     void unified_bed_leveling::fine_tune_mesh(const float &rx, const float &ry, const bool do_ubl_mesh_map) { | ||||
|       if (!parser.seen('R'))    // fine_tune_mesh() is special. If no repetition count flag is specified | ||||
|     void unified_bed_leveling::fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) { | ||||
|       if (!parser.seen('R'))      // fine_tune_mesh() is special. If no repetition count flag is specified | ||||
|         g29_repetition_cnt = 1;   // do exactly one mesh location. Otherwise use what the parser decided. | ||||
|  | ||||
|       #if ENABLED(UBL_MESH_EDIT_MOVES_Z) | ||||
| @@ -973,7 +976,7 @@ | ||||
|  | ||||
|       mesh_index_pair location; | ||||
|  | ||||
|       if (!position_is_reachable(rx, ry)) { | ||||
|       if (!position_is_reachable(pos)) { | ||||
|         SERIAL_ECHOLNPGM("(X,Y) outside printable radius."); | ||||
|         return; | ||||
|       } | ||||
| @@ -981,76 +984,78 @@ | ||||
|       save_ubl_active_state_and_disable(); | ||||
|  | ||||
|       LCD_MESSAGEPGM(MSG_UBL_FINE_TUNE_MESH); | ||||
|       ui.capture();                                                 // Take over control of the LCD encoder | ||||
|       ui.capture();                                         // Take over control of the LCD encoder | ||||
|  | ||||
|       do_blocking_move_to(rx, ry, Z_CLEARANCE_BETWEEN_PROBES);      // Move to the given XY with probe clearance | ||||
|       do_blocking_move_to(pos, Z_CLEARANCE_BETWEEN_PROBES); // Move to the given XY with probe clearance | ||||
|  | ||||
|       #if ENABLED(UBL_MESH_EDIT_MOVES_Z) | ||||
|         do_blocking_move_to_z(h_offset);                            // Move Z to the given 'H' offset | ||||
|         do_blocking_move_to_z(h_offset);                    // Move Z to the given 'H' offset | ||||
|       #endif | ||||
|  | ||||
|       uint16_t not_done[16]; | ||||
|       memset(not_done, 0xFF, sizeof(not_done)); | ||||
|       MeshFlags done_flags{0}; | ||||
|       xy_int8_t &lpos = location.pos; | ||||
|       do { | ||||
|         location = find_closest_mesh_point_of_type(SET_IN_BITMAP, rx, ry, USE_NOZZLE_AS_REFERENCE, not_done); | ||||
|         location = find_closest_mesh_point_of_type(SET_IN_BITMAP, pos, false, &done_flags); | ||||
|  | ||||
|         if (location.x_index < 0) break;                            // Stop when there are no more reachable points | ||||
|         if (lpos.x < 0) break;                              // Stop when there are no more reachable points | ||||
|  | ||||
|         bitmap_clear(not_done, location.x_index, location.y_index); // Mark this location as 'adjusted' so a new | ||||
|                                                                     // location is used on the next loop | ||||
|         done_flags.mark(lpos);                              // Mark this location as 'adjusted' so a new | ||||
|                                                             // location is used on the next loop | ||||
|         const xyz_pos_t raw = { | ||||
|           mesh_index_to_xpos(lpos.x), | ||||
|           mesh_index_to_ypos(lpos.y), | ||||
|           Z_CLEARANCE_BETWEEN_PROBES | ||||
|         }; | ||||
|  | ||||
|         const float rawx = mesh_index_to_xpos(location.x_index), | ||||
|                     rawy = mesh_index_to_ypos(location.y_index); | ||||
|         if (!position_is_reachable(raw)) break;             // SHOULD NOT OCCUR (find_closest_mesh_point_of_type only returns reachable) | ||||
|  | ||||
|         if (!position_is_reachable(rawx, rawy)) break;              // SHOULD NOT OCCUR because find_closest_mesh_point_of_type will only return reachable | ||||
|  | ||||
|         do_blocking_move_to(rawx, rawy, Z_CLEARANCE_BETWEEN_PROBES); // Move the nozzle to the edit point with probe clearance | ||||
|         do_blocking_move_to(raw);                           // Move the nozzle to the edit point with probe clearance | ||||
|  | ||||
|         #if ENABLED(UBL_MESH_EDIT_MOVES_Z) | ||||
|           do_blocking_move_to_z(h_offset);                          // Move Z to the given 'H' offset before editing | ||||
|           do_blocking_move_to_z(h_offset);                  // Move Z to the given 'H' offset before editing | ||||
|         #endif | ||||
|  | ||||
|         KEEPALIVE_STATE(PAUSED_FOR_USER); | ||||
|  | ||||
|         if (do_ubl_mesh_map) display_map(g29_map_type);             // Display the current point | ||||
|         if (do_ubl_mesh_map) display_map(g29_map_type);     // Display the current point | ||||
|  | ||||
|         ui.refresh(); | ||||
|  | ||||
|         float new_z = z_values[location.x_index][location.y_index]; | ||||
|         if (isnan(new_z)) new_z = 0;                                // Invalid points begin at 0 | ||||
|         new_z = FLOOR(new_z * 1000) * 0.001f;                       // Chop off digits after the 1000ths place | ||||
|         float new_z = z_values[lpos.x][lpos.y]; | ||||
|         if (isnan(new_z)) new_z = 0;                        // Invalid points begin at 0 | ||||
|         new_z = FLOOR(new_z * 1000) * 0.001f;               // Chop off digits after the 1000ths place | ||||
|  | ||||
|         lcd_mesh_edit_setup(new_z); | ||||
|  | ||||
|         do { | ||||
|           new_z = lcd_mesh_edit(); | ||||
|           #if ENABLED(UBL_MESH_EDIT_MOVES_Z) | ||||
|             do_blocking_move_to_z(h_offset + new_z);                // Move the nozzle as the point is edited | ||||
|             do_blocking_move_to_z(h_offset + new_z);        // Move the nozzle as the point is edited | ||||
|           #endif | ||||
|           idle(); | ||||
|           SERIAL_FLUSH();                                           // Prevent host M105 buffer overrun. | ||||
|           SERIAL_FLUSH();                                   // Prevent host M105 buffer overrun. | ||||
|         } while (!ui.button_pressed()); | ||||
|  | ||||
|         if (!lcd_map_control) ui.return_to_status();                // Just editing a single point? Return to status | ||||
|         if (!lcd_map_control) ui.return_to_status();        // Just editing a single point? Return to status | ||||
|  | ||||
|         if (click_and_hold(abort_fine_tune)) break;                 // Button held down? Abort editing | ||||
|         if (click_and_hold(abort_fine_tune)) break;         // Button held down? Abort editing | ||||
|  | ||||
|         z_values[location.x_index][location.y_index] = new_z;       // Save the updated Z value | ||||
|         z_values[lpos.x][lpos.y] = new_z;                   // Save the updated Z value | ||||
|         #if ENABLED(EXTENSIBLE_UI) | ||||
|           ExtUI::onMeshUpdate(location.x_index, location.y_index, new_z); | ||||
|           ExtUI::onMeshUpdate(location, new_z); | ||||
|         #endif | ||||
|  | ||||
|         serial_delay(20);                                           // No switch noise | ||||
|         serial_delay(20);                                   // No switch noise | ||||
|         ui.refresh(); | ||||
|  | ||||
|       } while (location.x_index >= 0 && --g29_repetition_cnt > 0); | ||||
|       } while (lpos.x >= 0 && --g29_repetition_cnt > 0); | ||||
|  | ||||
|       ui.release(); | ||||
|  | ||||
|       if (do_ubl_mesh_map) display_map(g29_map_type); | ||||
|       restore_ubl_active_state_and_leave(); | ||||
|  | ||||
|       do_blocking_move_to(rx, ry, Z_CLEARANCE_BETWEEN_PROBES); | ||||
|       do_blocking_move_to(pos, Z_CLEARANCE_BETWEEN_PROBES); | ||||
|  | ||||
|       LCD_MESSAGEPGM(MSG_UBL_DONE_EDITING_MESH); | ||||
|       SERIAL_ECHOLNPGM("Done Editing Mesh"); | ||||
| @@ -1073,11 +1078,6 @@ | ||||
|     g29_constant = 0; | ||||
|     g29_repetition_cnt = 0; | ||||
|  | ||||
|     g29_x_flag = parser.seenval('X'); | ||||
|     g29_x_pos = g29_x_flag ? parser.value_float() : current_position[X_AXIS]; | ||||
|     g29_y_flag = parser.seenval('Y'); | ||||
|     g29_y_pos = g29_y_flag ? parser.value_float() : current_position[Y_AXIS]; | ||||
|  | ||||
|     if (parser.seen('R')) { | ||||
|       g29_repetition_cnt = parser.has_value() ? parser.value_int() : GRID_MAX_POINTS; | ||||
|       NOMORE(g29_repetition_cnt, GRID_MAX_POINTS); | ||||
| @@ -1124,17 +1124,24 @@ | ||||
|       #endif | ||||
|     } | ||||
|  | ||||
|     if (g29_x_flag != g29_y_flag) { | ||||
|     xy_seen.x = parser.seenval('X'); | ||||
|     float sx = xy_seen.x ? parser.value_float() : current_position.x; | ||||
|     xy_seen.y = parser.seenval('Y'); | ||||
|     float sy = xy_seen.y ? parser.value_float() : current_position.y; | ||||
|  | ||||
|     if (xy_seen.x != xy_seen.y) { | ||||
|       SERIAL_ECHOLNPGM("Both X & Y locations must be specified.\n"); | ||||
|       err_flag = true; | ||||
|     } | ||||
|  | ||||
|     // If X or Y are not valid, use center of the bed values | ||||
|     if (!WITHIN(g29_x_pos, X_MIN_BED, X_MAX_BED)) g29_x_pos = X_CENTER; | ||||
|     if (!WITHIN(g29_y_pos, Y_MIN_BED, Y_MAX_BED)) g29_y_pos = Y_CENTER; | ||||
|     if (!WITHIN(sx, X_MIN_BED, X_MAX_BED)) sx = X_CENTER; | ||||
|     if (!WITHIN(sy, Y_MIN_BED, Y_MAX_BED)) sy = Y_CENTER; | ||||
|  | ||||
|     if (err_flag) return UBL_ERR; | ||||
|  | ||||
|     g29_pos.set(sx, sy); | ||||
|  | ||||
|     /** | ||||
|      * Activate or deactivate UBL | ||||
|      * Note: UBL's G29 restores the state set here when done. | ||||
| @@ -1213,26 +1220,22 @@ | ||||
|  | ||||
|   mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() { | ||||
|  | ||||
|     bool found_a_NAN  = false, found_a_real = false; | ||||
|     bool found_a_NAN = false, found_a_real = false; | ||||
|  | ||||
|     mesh_index_pair out_mesh; | ||||
|     out_mesh.x_index = out_mesh.y_index = -1; | ||||
|     out_mesh.distance = -99999.99f; | ||||
|     mesh_index_pair farthest { -1, -1, -99999.99 }; | ||||
|  | ||||
|     for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) { | ||||
|       for (int8_t j = 0; j < GRID_MAX_POINTS_Y; j++) { | ||||
|  | ||||
|         if (isnan(z_values[i][j])) { // Check to see if this location holds an invalid mesh point | ||||
|         if (isnan(z_values[i][j])) {                  // Invalid mesh point? | ||||
|  | ||||
|           const float mx = mesh_index_to_xpos(i), | ||||
|                       my = mesh_index_to_ypos(j); | ||||
|  | ||||
|           if (!position_is_reachable_by_probe(mx, my))  // make sure the probe can get to the mesh point | ||||
|           // Skip points the probe can't reach | ||||
|           if (!position_is_reachable_by_probe(mesh_index_to_xpos(i), mesh_index_to_ypos(j))) | ||||
|             continue; | ||||
|  | ||||
|           found_a_NAN = true; | ||||
|  | ||||
|           int8_t closest_x = -1, closest_y = -1; | ||||
|           xy_int8_t near { -1, -1 }; | ||||
|           float d1, d2 = 99999.9f; | ||||
|           for (int8_t k = 0; k < GRID_MAX_POINTS_X; k++) { | ||||
|             for (int8_t l = 0; l < GRID_MAX_POINTS_Y; l++) { | ||||
| @@ -1245,84 +1248,75 @@ | ||||
|  | ||||
|                 d1 = HYPOT(i - k, j - l) + (1.0f / ((millis() % 47) + 13)); | ||||
|  | ||||
|                 if (d1 < d2) {    // found a closer distance from invalid mesh point at (i,j) to defined mesh point at (k,l) | ||||
|                   d2 = d1;        // found a closer location with | ||||
|                   closest_x = i;  // an assigned mesh point value | ||||
|                   closest_y = j; | ||||
|                 if (d1 < d2) {    // Invalid mesh point (i,j) is closer to the defined point (k,l) | ||||
|                   d2 = d1; | ||||
|                   near.set(i, j); | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           // | ||||
|           // At this point d2 should have the closest defined mesh point to invalid mesh point (i,j) | ||||
|           // At this point d2 should have the near defined mesh point to invalid mesh point (i,j) | ||||
|           // | ||||
|  | ||||
|           if (found_a_real && (closest_x >= 0) && (d2 > out_mesh.distance)) { | ||||
|             out_mesh.distance = d2;         // found an invalid location with a greater distance | ||||
|             out_mesh.x_index = closest_x;   // to a defined mesh point | ||||
|             out_mesh.y_index = closest_y; | ||||
|           if (found_a_real && near.x >= 0 && d2 > farthest.distance) { | ||||
|             farthest.pos = near;      // Found an invalid location farther from the defined mesh point | ||||
|             farthest.distance = d2; | ||||
|           } | ||||
|         } | ||||
|       } // for j | ||||
|     } // for i | ||||
|  | ||||
|     if (!found_a_real && found_a_NAN) {        // if the mesh is totally unpopulated, start the probing | ||||
|       out_mesh.x_index = GRID_MAX_POINTS_X / 2; | ||||
|       out_mesh.y_index = GRID_MAX_POINTS_Y / 2; | ||||
|       out_mesh.distance = 1; | ||||
|       farthest.pos.set(GRID_MAX_POINTS_X / 2, GRID_MAX_POINTS_Y / 2); | ||||
|       farthest.distance = 1; | ||||
|     } | ||||
|     return out_mesh; | ||||
|     return farthest; | ||||
|   } | ||||
|  | ||||
|   mesh_index_pair unified_bed_leveling::find_closest_mesh_point_of_type(const MeshPointType type, const float &rx, const float &ry, const bool probe_as_reference, uint16_t bits[16]) { | ||||
|     mesh_index_pair out_mesh; | ||||
|     out_mesh.x_index = out_mesh.y_index = -1; | ||||
|     out_mesh.distance = -99999.9f; | ||||
|   mesh_index_pair unified_bed_leveling::find_closest_mesh_point_of_type(const MeshPointType type, const xy_pos_t &pos, const bool probe_relative/*=false*/, MeshFlags *done_flags/*=nullptr*/) { | ||||
|     mesh_index_pair closest; | ||||
|     closest.invalidate(); | ||||
|     closest.distance = -99999.9f; | ||||
|  | ||||
|     // Get our reference position. Either the nozzle or probe location. | ||||
|     const float px = rx + (probe_as_reference == USE_PROBE_AS_REFERENCE ? probe_offset[X_AXIS] : 0), | ||||
|                 py = ry + (probe_as_reference == USE_PROBE_AS_REFERENCE ? probe_offset[Y_AXIS] : 0); | ||||
|     // Get the reference position, either nozzle or probe | ||||
|     const xy_pos_t ref = probe_relative ? pos + probe_offset : pos; | ||||
|  | ||||
|     float best_so_far = 99999.99f; | ||||
|  | ||||
|     for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) { | ||||
|       for (int8_t j = 0; j < GRID_MAX_POINTS_Y; j++) { | ||||
|  | ||||
|         if ( (type == INVALID && isnan(z_values[i][j]))  // Check to see if this location holds the right thing | ||||
|           || (type == REAL && !isnan(z_values[i][j])) | ||||
|           || (type == SET_IN_BITMAP && is_bitmap_set(bits, i, j)) | ||||
|         if ( (type == (isnan(z_values[i][j]) ? INVALID : REAL)) | ||||
|           || (type == SET_IN_BITMAP && !done_flags->marked(i, j)) | ||||
|         ) { | ||||
|           // We only get here if we found a Mesh Point of the specified type | ||||
|  | ||||
|           const float mx = mesh_index_to_xpos(i), | ||||
|                       my = mesh_index_to_ypos(j); | ||||
|           // Found a Mesh Point of the specified type! | ||||
|           const xy_pos_t mpos = { mesh_index_to_xpos(i), mesh_index_to_ypos(j) }; | ||||
|  | ||||
|           // If using the probe as the reference there are some unreachable locations. | ||||
|           // Also for round beds, there are grid points outside the bed the nozzle can't reach. | ||||
|           // Prune them from the list and ignore them till the next Phase (manual nozzle probing). | ||||
|  | ||||
|           if (probe_as_reference ? !position_is_reachable_by_probe(mx, my) : !position_is_reachable(mx, my)) | ||||
|           if (probe_relative ? !position_is_reachable_by_probe(mpos) : !position_is_reachable(mpos)) | ||||
|             continue; | ||||
|  | ||||
|           // Reachable. Check if it's the best_so_far location to the nozzle. | ||||
|  | ||||
|           float distance = HYPOT(px - mx, py - my); | ||||
|           const xy_pos_t diff = current_position - mpos; | ||||
|           const float distance = (ref - mpos).magnitude() + diff.magnitude() * 0.1f; | ||||
|  | ||||
|           // factor in the distance from the current location for the normal case | ||||
|           // so the nozzle isn't running all over the bed. | ||||
|           distance += HYPOT(current_position[X_AXIS] - mx, current_position[Y_AXIS] - my) * 0.1f; | ||||
|           if (distance < best_so_far) { | ||||
|             best_so_far = distance;   // We found a closer location with | ||||
|             out_mesh.x_index = i;     // the specified type of mesh value. | ||||
|             out_mesh.y_index = j; | ||||
|             out_mesh.distance = best_so_far; | ||||
|             best_so_far = distance;   // Found a closer location with the desired value type. | ||||
|             closest.pos.set(i, j); | ||||
|             closest.distance = best_so_far; | ||||
|           } | ||||
|         } | ||||
|       } // for j | ||||
|     } // for i | ||||
|  | ||||
|     return out_mesh; | ||||
|     return closest; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -1332,20 +1326,20 @@ | ||||
|    */ | ||||
|  | ||||
|   bool unified_bed_leveling::smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir) { | ||||
|     const int8_t x1 = x + xdir, x2 = x1 + xdir, | ||||
|                  y1 = y + ydir, y2 = y1 + ydir; | ||||
|     // A NAN next to a pair of real values? | ||||
|     if (isnan(z_values[x][y]) && !isnan(z_values[x1][y1]) && !isnan(z_values[x2][y2])) { | ||||
|       if (z_values[x1][y1] < z_values[x2][y2])                  // Angled downward? | ||||
|         z_values[x][y] = z_values[x1][y1];                      // Use nearest (maybe a little too high.) | ||||
|       else | ||||
|         z_values[x][y] = 2.0f * z_values[x1][y1] - z_values[x2][y2];   // Angled upward... | ||||
|  | ||||
|       #if ENABLED(EXTENSIBLE_UI) | ||||
|         ExtUI::onMeshUpdate(x, y, z_values[x][y]); | ||||
|       #endif | ||||
|  | ||||
|       return true; | ||||
|     const float v = z_values[x][y]; | ||||
|     if (isnan(v)) {                           // A NAN... | ||||
|       const int8_t dx = x + xdir, dy = y + ydir; | ||||
|       const float v1 = z_values[dx][dy]; | ||||
|       if (!isnan(v1)) {                       // ...next to a pair of real values? | ||||
|         const float v2 = z_values[dx + xdir][dy + ydir]; | ||||
|         if (!isnan(v2)) { | ||||
|           z_values[x][y] = v1 < v2 ? v1 : v1 + v1 - v2; | ||||
|           #if ENABLED(EXTENSIBLE_UI) | ||||
|             ExtUI::onMeshUpdate(x, y, z_values[pos.x][pos.y]); | ||||
|           #endif | ||||
|           return true; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| @@ -1391,15 +1385,15 @@ | ||||
|                   dx = (x_max - x_min) / (g29_grid_size - 1), | ||||
|                   dy = (y_max - y_min) / (g29_grid_size - 1); | ||||
|  | ||||
|       vector_3 points[3] = { | ||||
|       const vector_3 points[3] = { | ||||
|         #if ENABLED(HAS_FIXED_3POINT) | ||||
|           vector_3(PROBE_PT_1_X, PROBE_PT_1_Y, 0), | ||||
|           vector_3(PROBE_PT_2_X, PROBE_PT_2_Y, 0), | ||||
|           vector_3(PROBE_PT_3_X, PROBE_PT_3_Y, 0) | ||||
|           { PROBE_PT_1_X, PROBE_PT_1_Y, 0 }, | ||||
|           { PROBE_PT_2_X, PROBE_PT_2_Y, 0 }, | ||||
|           { PROBE_PT_3_X, PROBE_PT_3_Y, 0 } | ||||
|         #else | ||||
|           vector_3(x_min, y_min, 0), | ||||
|           vector_3(x_max, y_min, 0), | ||||
|           vector_3((x_max - x_min) / 2, y_max, 0) | ||||
|           { x_min, y_min, 0 }, | ||||
|           { x_max, y_min, 0 }, | ||||
|           { (x_max - x_min) / 2, y_max, 0 } | ||||
|         #endif | ||||
|       }; | ||||
|  | ||||
| @@ -1419,11 +1413,11 @@ | ||||
|           ui.status_printf_P(0, PSTR(MSG_LCD_TILTING_MESH " 1/3")); | ||||
|         #endif | ||||
|  | ||||
|         measured_z = probe_at_point(points[0].x, points[0].y, PROBE_PT_RAISE, g29_verbose_level); | ||||
|         measured_z = probe_at_point(points[0], PROBE_PT_RAISE, g29_verbose_level); | ||||
|         if (isnan(measured_z)) | ||||
|           abort_flag = true; | ||||
|         else { | ||||
|           measured_z -= get_z_correction(points[0].x, points[0].y); | ||||
|           measured_z -= get_z_correction(points[0]); | ||||
|           #ifdef VALIDATE_MESH_TILT | ||||
|             z1 = measured_z; | ||||
|           #endif | ||||
| @@ -1431,7 +1425,7 @@ | ||||
|             serial_spaces(16); | ||||
|             SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z); | ||||
|           } | ||||
|           incremental_LSF(&lsf_results, points[0].x, points[0].y, measured_z); | ||||
|           incremental_LSF(&lsf_results, points[0], measured_z); | ||||
|         } | ||||
|  | ||||
|         if (!abort_flag) { | ||||
| @@ -1440,19 +1434,19 @@ | ||||
|             ui.status_printf_P(0, PSTR(MSG_LCD_TILTING_MESH " 2/3")); | ||||
|           #endif | ||||
|  | ||||
|           measured_z = probe_at_point(points[1].x, points[1].y, PROBE_PT_RAISE, g29_verbose_level); | ||||
|           measured_z = probe_at_point(points[1], PROBE_PT_RAISE, g29_verbose_level); | ||||
|           #ifdef VALIDATE_MESH_TILT | ||||
|             z2 = measured_z; | ||||
|           #endif | ||||
|           if (isnan(measured_z)) | ||||
|             abort_flag = true; | ||||
|           else { | ||||
|             measured_z -= get_z_correction(points[1].x, points[1].y); | ||||
|             measured_z -= get_z_correction(points[1]); | ||||
|             if (g29_verbose_level > 3) { | ||||
|               serial_spaces(16); | ||||
|               SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z); | ||||
|             } | ||||
|             incremental_LSF(&lsf_results, points[1].x, points[1].y, measured_z); | ||||
|             incremental_LSF(&lsf_results, points[1], measured_z); | ||||
|           } | ||||
|         } | ||||
|  | ||||
| @@ -1462,19 +1456,19 @@ | ||||
|             ui.status_printf_P(0, PSTR(MSG_LCD_TILTING_MESH " 3/3")); | ||||
|           #endif | ||||
|  | ||||
|           measured_z = probe_at_point(points[2].x, points[2].y, PROBE_PT_STOW, g29_verbose_level); | ||||
|           measured_z = probe_at_point(points[2], PROBE_PT_STOW, g29_verbose_level); | ||||
|           #ifdef VALIDATE_MESH_TILT | ||||
|             z3 = measured_z; | ||||
|           #endif | ||||
|           if (isnan(measured_z)) | ||||
|             abort_flag = true; | ||||
|           else { | ||||
|             measured_z -= get_z_correction(points[2].x, points[2].y); | ||||
|             measured_z -= get_z_correction(points[2]); | ||||
|             if (g29_verbose_level > 3) { | ||||
|               serial_spaces(16); | ||||
|               SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z); | ||||
|             } | ||||
|             incremental_LSF(&lsf_results, points[2].x, points[2].y, measured_z); | ||||
|             incremental_LSF(&lsf_results, points[2], measured_z); | ||||
|           } | ||||
|         } | ||||
|  | ||||
| @@ -1494,10 +1488,11 @@ | ||||
|  | ||||
|         uint16_t total_points = g29_grid_size * g29_grid_size, point_num = 1; | ||||
|  | ||||
|         xy_pos_t rpos; | ||||
|         for (uint8_t ix = 0; ix < g29_grid_size; ix++) { | ||||
|           const float rx = x_min + ix * dx; | ||||
|           rpos.x = x_min + ix * dx; | ||||
|           for (int8_t iy = 0; iy < g29_grid_size; iy++) { | ||||
|             const float ry = y_min + dy * (zig_zag ? g29_grid_size - 1 - iy : iy); | ||||
|             rpos.y = y_min + dy * (zig_zag ? g29_grid_size - 1 - iy : iy); | ||||
|  | ||||
|             if (!abort_flag) { | ||||
|               SERIAL_ECHOLNPAIR("Tilting mesh point ", point_num, "/", total_points, "\n"); | ||||
| @@ -1505,24 +1500,24 @@ | ||||
|                 ui.status_printf_P(0, PSTR(MSG_LCD_TILTING_MESH " %i/%i"), point_num, total_points); | ||||
|               #endif | ||||
|  | ||||
|               measured_z = probe_at_point(rx, ry, parser.seen('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level); // TODO: Needs error handling | ||||
|               measured_z = probe_at_point(rpos, parser.seen('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level); // TODO: Needs error handling | ||||
|  | ||||
|               abort_flag = isnan(measured_z); | ||||
|  | ||||
|               if (DEBUGGING(LEVELING)) { | ||||
|                 const xy_pos_t lpos = rpos.asLogical(); | ||||
|                 DEBUG_CHAR('('); | ||||
|                 DEBUG_ECHO_F(rx, 7); | ||||
|                 DEBUG_ECHO_F(rpos.x, 7); | ||||
|                 DEBUG_CHAR(','); | ||||
|                 DEBUG_ECHO_F(ry, 7); | ||||
|                 DEBUG_ECHOPGM(")   logical: ("); | ||||
|                 DEBUG_ECHO_F(LOGICAL_X_POSITION(rx), 7); | ||||
|                 DEBUG_ECHO_F(rpos.y, 7); | ||||
|                 DEBUG_ECHOPAIR_F(")   logical: (", lpos.x, 7); | ||||
|                 DEBUG_CHAR(','); | ||||
|                 DEBUG_ECHO_F(LOGICAL_Y_POSITION(ry), 7); | ||||
|                 DEBUG_ECHO_F(lpos.y, 7); | ||||
|                 DEBUG_ECHOPAIR_F(")   measured: ", measured_z, 7); | ||||
|                 DEBUG_ECHOPAIR_F("   correction: ", get_z_correction(rx, ry), 7); | ||||
|                 DEBUG_ECHOPAIR_F("   correction: ", get_z_correction(rpos), 7); | ||||
|               } | ||||
|  | ||||
|               measured_z -= get_z_correction(rx, ry) /* + probe_offset[Z_AXIS] */ ; | ||||
|               measured_z -= get_z_correction(rpos) /* + probe_offset.z */ ; | ||||
|  | ||||
|               if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR_F("   final >>>---> ", measured_z, 7); | ||||
|  | ||||
| @@ -1530,7 +1525,7 @@ | ||||
|                 serial_spaces(16); | ||||
|                 SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z); | ||||
|               } | ||||
|               incremental_LSF(&lsf_results, rx, ry, measured_z); | ||||
|               incremental_LSF(&lsf_results, rpos, measured_z); | ||||
|             } | ||||
|  | ||||
|             point_num++; | ||||
| @@ -1564,33 +1559,33 @@ | ||||
|  | ||||
|       for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) { | ||||
|         for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) { | ||||
|           float x_tmp = mesh_index_to_xpos(i), | ||||
|                 y_tmp = mesh_index_to_ypos(j), | ||||
|                 z_tmp = z_values[i][j]; | ||||
|           float mx = mesh_index_to_xpos(i), | ||||
|                 my = mesh_index_to_ypos(j), | ||||
|                 mz = z_values[i][j]; | ||||
|  | ||||
|           if (DEBUGGING(LEVELING)) { | ||||
|             DEBUG_ECHOPAIR_F("before rotation = [", x_tmp, 7); | ||||
|             DEBUG_ECHOPAIR_F("before rotation = [", mx, 7); | ||||
|             DEBUG_CHAR(','); | ||||
|             DEBUG_ECHO_F(y_tmp, 7); | ||||
|             DEBUG_ECHO_F(my, 7); | ||||
|             DEBUG_CHAR(','); | ||||
|             DEBUG_ECHO_F(z_tmp, 7); | ||||
|             DEBUG_ECHO_F(mz, 7); | ||||
|             DEBUG_ECHOPGM("]   ---> "); | ||||
|             DEBUG_DELAY(20); | ||||
|           } | ||||
|  | ||||
|           apply_rotation_xyz(rotation, x_tmp, y_tmp, z_tmp); | ||||
|           apply_rotation_xyz(rotation, mx, my, mz); | ||||
|  | ||||
|           if (DEBUGGING(LEVELING)) { | ||||
|             DEBUG_ECHOPAIR_F("after rotation = [", x_tmp, 7); | ||||
|             DEBUG_ECHOPAIR_F("after rotation = [", mx, 7); | ||||
|             DEBUG_CHAR(','); | ||||
|             DEBUG_ECHO_F(y_tmp, 7); | ||||
|             DEBUG_ECHO_F(my, 7); | ||||
|             DEBUG_CHAR(','); | ||||
|             DEBUG_ECHO_F(z_tmp, 7); | ||||
|             DEBUG_ECHO_F(mz, 7); | ||||
|             DEBUG_ECHOLNPGM("]"); | ||||
|             DEBUG_DELAY(55); | ||||
|             DEBUG_DELAY(20); | ||||
|           } | ||||
|  | ||||
|           z_values[i][j] = z_tmp - lsf_results.D; | ||||
|           z_values[i][j] = mz - lsf_results.D; | ||||
|           #if ENABLED(EXTENSIBLE_UI) | ||||
|             ExtUI::onMeshUpdate(i, j, z_values[i][j]); | ||||
|           #endif | ||||
| @@ -1613,41 +1608,32 @@ | ||||
|         DEBUG_EOL(); | ||||
|  | ||||
|         /** | ||||
|          * The following code can be used to check the validity of the mesh tilting algorithm. | ||||
|          * When a 3-Point Mesh Tilt is done, the same algorithm is used as the grid based tilting. | ||||
|          * The only difference is just 3 points are used in the calculations.   That fact guarantees | ||||
|          * each probed point should have an exact match when a get_z_correction() for that location | ||||
|          * is calculated.  The Z error between the probed point locations and the get_z_correction() | ||||
|          * Use the code below to check the validity of the mesh tilting algorithm. | ||||
|          * 3-Point Mesh Tilt uses the same algorithm as grid-based tilting, but only | ||||
|          * three points are used in the calculation. This guarantees that each probed point | ||||
|          * has an exact match when get_z_correction() for that location is calculated. | ||||
|          * The Z error between the probed point locations and the get_z_correction() | ||||
|          * numbers for those locations should be 0. | ||||
|          */ | ||||
|         #ifdef VALIDATE_MESH_TILT | ||||
|           float t, t1, d; | ||||
|           t = normal.x * x_min + normal.y * y_min; | ||||
|           d = t + normal.z * z1; | ||||
|           DEBUG_ECHOPAIR_F("D from 1st point: ", d, 6); | ||||
|           DEBUG_ECHOLNPAIR_F("   Z error: ", normal.z * z1 - get_z_correction(x_min, y_min), 6); | ||||
|  | ||||
|           t = normal.x * x_max + normal.y * y_min; | ||||
|           d = t + normal.z * z2; | ||||
|           DEBUG_EOL(); | ||||
|           DEBUG_ECHOPAIR_F("D from 2nd point: ", d, 6); | ||||
|           DEBUG_ECHOLNPAIR_F("   Z error: ", normal.z * z2 - get_z_correction(x_max, y_min), 6); | ||||
|  | ||||
|           t = normal.x * ((x_max - x_min) / 2) + normal.y * (y_min); | ||||
|           d = t + normal.z * z3; | ||||
|           DEBUG_ECHOPAIR_F("D from 3rd point: ", d, 6); | ||||
|           DEBUG_ECHOLNPAIR_F("   Z error: ", normal.z * z3 - get_z_correction((x_max - x_min) / 2, y_max), 6); | ||||
|  | ||||
|           t = normal.x * (Z_SAFE_HOMING_X_POINT) + normal.y * (Z_SAFE_HOMING_Y_POINT); | ||||
|           d = t + normal.z * 0; | ||||
|           DEBUG_ECHOLNPAIR_F("D from home location with Z=0 : ", d, 6); | ||||
|  | ||||
|           t = normal.x * (Z_SAFE_HOMING_X_POINT) + normal.y * (Z_SAFE_HOMING_Y_POINT); | ||||
|           d = t + get_z_correction(Z_SAFE_HOMING_X_POINT, Z_SAFE_HOMING_Y_POINT); // normal.z * 0; | ||||
|           DEBUG_ECHOPAIR_F("D from home location using mesh value for Z: ", d, 6); | ||||
|  | ||||
|           auto d_from = []() { DEBUG_ECHOPGM("D from "); }; | ||||
|           auto normed = [&](const xy_pos_t &pos, const float &zadd) { | ||||
|             return normal.x * pos.x + normal.y * pos.y + zadd; | ||||
|           }; | ||||
|           auto debug_pt = [](PGM_P const pre, const xy_pos_t &pos, const float &zadd) { | ||||
|             d_from(); serialprintPGM(pre); | ||||
|             DEBUG_ECHO_F(normed(pos, zadd), 6); | ||||
|             DEBUG_ECHOLNPAIR_F("   Z error: ", zadd - get_z_correction(pos), 6); | ||||
|           }; | ||||
|           debug_pt(PSTR("1st point: "), probe_pt[0], normal.z * z1); | ||||
|           debug_pt(PSTR("2nd point: "), probe_pt[1], normal.z * z2); | ||||
|           debug_pt(PSTR("3rd point: "), probe_pt[2], normal.z * z3); | ||||
|           d_from(); DEBUG_ECHOPGM("safe home with Z="); | ||||
|           DEBUG_ECHOLNPAIR_F("0 : ", normed(safe_homing_xy, 0), 6); | ||||
|           d_from(); DEBUG_ECHOPGM("safe home with Z="); | ||||
|           DEBUG_ECHOLNPAIR_F("mesh value ", normed(safe_homing_xy, get_z_correction(safe_homing_xy)), 6); | ||||
|           DEBUG_ECHOPAIR("   Z error: (", Z_SAFE_HOMING_X_POINT, ",", Z_SAFE_HOMING_Y_POINT); | ||||
|           DEBUG_ECHOLNPAIR_F(") = ", get_z_correction(Z_SAFE_HOMING_X_POINT, Z_SAFE_HOMING_Y_POINT), 6); | ||||
|           DEBUG_ECHOLNPAIR_F(") = ", get_z_correction(safe_homing_xy), 6); | ||||
|         #endif | ||||
|       } // DEBUGGING(LEVELING) | ||||
|  | ||||
| @@ -1676,21 +1662,23 @@ | ||||
|           if (!isnan(z_values[jx][jy])) | ||||
|             SBI(bitmap[jx], jy); | ||||
|  | ||||
|       xy_pos_t ppos; | ||||
|       for (uint8_t ix = 0; ix < GRID_MAX_POINTS_X; ix++) { | ||||
|         const float px = mesh_index_to_xpos(ix); | ||||
|         ppos.x = mesh_index_to_xpos(ix); | ||||
|         for (uint8_t iy = 0; iy < GRID_MAX_POINTS_Y; iy++) { | ||||
|           const float py = mesh_index_to_ypos(iy); | ||||
|           ppos.y = mesh_index_to_ypos(iy); | ||||
|           if (isnan(z_values[ix][iy])) { | ||||
|             // undefined mesh point at (px,py), compute weighted LSF from original valid mesh points. | ||||
|             // undefined mesh point at (ppos.x,ppos.y), compute weighted LSF from original valid mesh points. | ||||
|             incremental_LSF_reset(&lsf_results); | ||||
|             xy_pos_t rpos; | ||||
|             for (uint8_t jx = 0; jx < GRID_MAX_POINTS_X; jx++) { | ||||
|               const float rx = mesh_index_to_xpos(jx); | ||||
|               rpos.x = mesh_index_to_xpos(jx); | ||||
|               for (uint8_t jy = 0; jy < GRID_MAX_POINTS_Y; jy++) { | ||||
|                 if (TEST(bitmap[jx], jy)) { | ||||
|                   const float ry = mesh_index_to_ypos(jy), | ||||
|                               rz = z_values[jx][jy], | ||||
|                               w  = 1 + weight_scaled / HYPOT((rx - px), (ry - py)); | ||||
|                   incremental_WLSF(&lsf_results, rx, ry, rz, w); | ||||
|                   rpos.y = mesh_index_to_ypos(jy); | ||||
|                   const float rz = z_values[jx][jy], | ||||
|                                w = 1.0f + weight_scaled / (rpos - ppos).magnitude(); | ||||
|                   incremental_WLSF(&lsf_results, rpos, rz, w); | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
| @@ -1698,12 +1686,12 @@ | ||||
|               SERIAL_ECHOLNPGM("Insufficient data"); | ||||
|               return; | ||||
|             } | ||||
|             const float ez = -lsf_results.D - lsf_results.A * px - lsf_results.B * py; | ||||
|             const float ez = -lsf_results.D - lsf_results.A * ppos.x - lsf_results.B * ppos.y; | ||||
|             z_values[ix][iy] = ez; | ||||
|             #if ENABLED(EXTENSIBLE_UI) | ||||
|               ExtUI::onMeshUpdate(ix, iy, z_values[ix][iy]); | ||||
|             #endif | ||||
|             idle();   // housekeeping | ||||
|             idle(); // housekeeping | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| @@ -1734,7 +1722,7 @@ | ||||
|       adjust_mesh_to_mean(g29_c_flag, g29_constant); | ||||
|  | ||||
|       #if HAS_BED_PROBE | ||||
|         SERIAL_ECHOLNPAIR_F("Probe Offset M851 Z", probe_offset[Z_AXIS], 7); | ||||
|         SERIAL_ECHOLNPAIR_F("Probe Offset M851 Z", probe_offset.z, 7); | ||||
|       #endif | ||||
|  | ||||
|       SERIAL_ECHOLNPAIR("MESH_MIN_X  " STRINGIFY(MESH_MIN_X) "=", MESH_MIN_X); serial_delay(50); | ||||
|   | ||||
| @@ -35,12 +35,6 @@ | ||||
| #include "../../../Marlin.h" | ||||
| #include <math.h> | ||||
|  | ||||
| #if AVR_AT90USB1286_FAMILY  // Teensyduino & Printrboard IDE extensions have compile errors without this | ||||
|   inline void set_current_from_destination() { COPY(current_position, destination); } | ||||
| #else | ||||
|   extern void set_current_from_destination(); | ||||
| #endif | ||||
|  | ||||
| #if !UBL_SEGMENTED | ||||
|  | ||||
|   void unified_bed_leveling::line_to_destination_cartesian(const feedRate_t &scaled_fr_mm_s, const uint8_t extruder) { | ||||
| @@ -50,60 +44,57 @@ | ||||
|      * just do the required Z-Height correction, call the Planner's buffer_line() routine, and leave | ||||
|      */ | ||||
|     #if HAS_POSITION_MODIFIERS | ||||
|       float start[XYZE] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS] }, | ||||
|             end[XYZE] = { destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS] }; | ||||
|       xyze_pos_t start = current_position, end = destination; | ||||
|       planner.apply_modifiers(start); | ||||
|       planner.apply_modifiers(end); | ||||
|     #else | ||||
|       const float (&start)[XYZE] = current_position, | ||||
|                     (&end)[XYZE] = destination; | ||||
|       const xyze_pos_t &start = current_position, &end = destination; | ||||
|     #endif | ||||
|  | ||||
|     const int cell_start_xi = get_cell_index_x(start[X_AXIS]), | ||||
|               cell_start_yi = get_cell_index_y(start[Y_AXIS]), | ||||
|               cell_dest_xi  = get_cell_index_x(end[X_AXIS]), | ||||
|               cell_dest_yi  = get_cell_index_y(end[Y_AXIS]); | ||||
|     const xy_int8_t istart = cell_indexes(start), iend = cell_indexes(end); | ||||
|  | ||||
|     // A move within the same cell needs no splitting | ||||
|     if (cell_start_xi == cell_dest_xi && cell_start_yi == cell_dest_yi) { | ||||
|     if (istart == iend) { | ||||
|  | ||||
|       // For a move off the bed, use a constant Z raise | ||||
|       if (!WITHIN(cell_dest_xi, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(cell_dest_yi, 0, GRID_MAX_POINTS_Y - 1)) { | ||||
|       if (!WITHIN(iend.x, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iend.y, 0, GRID_MAX_POINTS_Y - 1)) { | ||||
|  | ||||
|         // Note: There is no Z Correction in this case. We are off the grid and don't know what | ||||
|         // a reasonable correction would be.  If the user has specified a UBL_Z_RAISE_WHEN_OFF_MESH | ||||
|         // value, that will be used instead of a calculated (Bi-Linear interpolation) correction. | ||||
|  | ||||
|         const float z_raise = 0.0 | ||||
|           #ifdef UBL_Z_RAISE_WHEN_OFF_MESH | ||||
|             + UBL_Z_RAISE_WHEN_OFF_MESH | ||||
|           #endif | ||||
|         ; | ||||
|         planner.buffer_segment(end[X_AXIS], end[Y_AXIS], end[Z_AXIS] + z_raise, end[E_AXIS], scaled_fr_mm_s, extruder); | ||||
|         set_current_from_destination(); | ||||
|         #ifdef UBL_Z_RAISE_WHEN_OFF_MESH | ||||
|           end.z += UBL_Z_RAISE_WHEN_OFF_MESH; | ||||
|         #endif | ||||
|         planner.buffer_segment(end, scaled_fr_mm_s, extruder); | ||||
|         current_position = destination; | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       FINAL_MOVE: | ||||
|  | ||||
|       // The distance is always MESH_X_DIST so multiply by the constant reciprocal. | ||||
|       const float xratio = (end[X_AXIS] - mesh_index_to_xpos(cell_dest_xi)) * RECIPROCAL(MESH_X_DIST); | ||||
|       const float xratio = (end.x - mesh_index_to_xpos(iend.x)) * RECIPROCAL(MESH_X_DIST); | ||||
|  | ||||
|       float z1 = z_values[cell_dest_xi    ][cell_dest_yi    ] + xratio * | ||||
|                 (z_values[cell_dest_xi + 1][cell_dest_yi    ] - z_values[cell_dest_xi][cell_dest_yi    ]), | ||||
|             z2 = z_values[cell_dest_xi    ][cell_dest_yi + 1] + xratio * | ||||
|                 (z_values[cell_dest_xi + 1][cell_dest_yi + 1] - z_values[cell_dest_xi][cell_dest_yi + 1]); | ||||
|  | ||||
|       if (cell_dest_xi >= GRID_MAX_POINTS_X - 1) z1 = z2 = 0.0; | ||||
|       float z1, z2; | ||||
|       if (iend.x >= GRID_MAX_POINTS_X - 1) | ||||
|         z1 = z2 = 0.0; | ||||
|       else { | ||||
|         z1 = z_values[iend.x    ][iend.y    ] + xratio * | ||||
|             (z_values[iend.x + 1][iend.y    ] - z_values[iend.x][iend.y    ]), | ||||
|         z2 = z_values[iend.x    ][iend.y + 1] + xratio * | ||||
|             (z_values[iend.x + 1][iend.y + 1] - z_values[iend.x][iend.y + 1]); | ||||
|       } | ||||
|  | ||||
|       // X cell-fraction done. Interpolate the two Z offsets with the Y fraction for the final Z offset. | ||||
|       const float yratio = (end[Y_AXIS] - mesh_index_to_ypos(cell_dest_yi)) * RECIPROCAL(MESH_Y_DIST), | ||||
|                   z0 = cell_dest_yi < GRID_MAX_POINTS_Y - 1 ? (z1 + (z2 - z1) * yratio) * planner.fade_scaling_factor_for_z(end[Z_AXIS]) : 0.0; | ||||
|       const float yratio = (end.y - mesh_index_to_ypos(iend.y)) * RECIPROCAL(MESH_Y_DIST), | ||||
|                   z0 = iend.y < GRID_MAX_POINTS_Y - 1 ? (z1 + (z2 - z1) * yratio) * planner.fade_scaling_factor_for_z(end.z) : 0.0; | ||||
|  | ||||
|       // Undefined parts of the Mesh in z_values[][] are NAN. | ||||
|       // Replace NAN corrections with 0.0 to prevent NAN propagation. | ||||
|       planner.buffer_segment(end[X_AXIS], end[Y_AXIS], end[Z_AXIS] + (isnan(z0) ? 0.0 : z0), end[E_AXIS], scaled_fr_mm_s, extruder); | ||||
|       set_current_from_destination(); | ||||
|       if (!isnan(z0)) end.z += z0; | ||||
|       planner.buffer_segment(end, scaled_fr_mm_s, extruder); | ||||
|       current_position = destination; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @@ -112,17 +103,11 @@ | ||||
|      * case - crossing only one X or Y line - after details are worked out to reduce computation. | ||||
|      */ | ||||
|  | ||||
|     const float dx = end[X_AXIS] - start[X_AXIS], | ||||
|                 dy = end[Y_AXIS] - start[Y_AXIS]; | ||||
|  | ||||
|     const int left_flag = dx < 0.0 ? 1 : 0, | ||||
|               down_flag = dy < 0.0 ? 1 : 0; | ||||
|  | ||||
|     const float adx = left_flag ? -dx : dx, | ||||
|                 ady = down_flag ? -dy : dy; | ||||
|  | ||||
|     const int dxi = cell_start_xi == cell_dest_xi ? 0 : left_flag ? -1 : 1, | ||||
|               dyi = cell_start_yi == cell_dest_yi ? 0 : down_flag ? -1 : 1; | ||||
|     const xy_float_t dist = end - start; | ||||
|     const xy_bool_t neg { dist.x < 0, dist.y < 0 }; | ||||
|     const xy_int8_t ineg { int8_t(neg.x), int8_t(neg.y) }; | ||||
|     const xy_float_t sign { neg.x ? -1.0f : 1.0f, neg.y ? -1.0f : 1.0f }; | ||||
|     const xy_int8_t iadd { int8_t(iend.x == istart.x ? 0 : sign.x), int8_t(iend.y == istart.y ? 0 : sign.y) }; | ||||
|  | ||||
|     /** | ||||
|      * Compute the extruder scaling factor for each partial move, checking for | ||||
| @@ -132,64 +117,64 @@ | ||||
|      * components. The larger of the two is used to preserve precision. | ||||
|      */ | ||||
|  | ||||
|     const bool use_x_dist = adx > ady; | ||||
|     const xy_float_t ad = sign * dist; | ||||
|     const bool use_x_dist = ad.x > ad.y; | ||||
|  | ||||
|     float on_axis_distance = use_x_dist ? dx : dy, | ||||
|           e_position = end[E_AXIS] - start[E_AXIS], | ||||
|           z_position = end[Z_AXIS] - start[Z_AXIS]; | ||||
|     float on_axis_distance = use_x_dist ? dist.x : dist.y, | ||||
|           e_position = end.e - start.e, | ||||
|           z_position = end.z - start.z; | ||||
|  | ||||
|     const float e_normalized_dist = e_position / on_axis_distance, | ||||
|     const float e_normalized_dist = e_position / on_axis_distance, // Allow divide by zero | ||||
|                 z_normalized_dist = z_position / on_axis_distance; | ||||
|  | ||||
|     int current_xi = cell_start_xi, | ||||
|         current_yi = cell_start_yi; | ||||
|     xy_int8_t icell = istart; | ||||
|  | ||||
|     const float m = dy / dx, | ||||
|                 c = start[Y_AXIS] - m * start[X_AXIS]; | ||||
|     const float ratio = dist.y / dist.x,        // Allow divide by zero | ||||
|                 c = start.y - ratio * start.x; | ||||
|  | ||||
|     const bool inf_normalized_flag = (isinf(e_normalized_dist) != 0), | ||||
|                inf_m_flag = (isinf(m) != 0); | ||||
|     const bool inf_normalized_flag = isinf(e_normalized_dist), | ||||
|                inf_ratio_flag = isinf(ratio); | ||||
|  | ||||
|     /** | ||||
|      * Handle vertical lines that stay within one column. | ||||
|      * These need not be perfectly vertical. | ||||
|      */ | ||||
|     if (dxi == 0) {             // Vertical line? | ||||
|       current_yi += down_flag;  // Line going down? Just go to the bottom. | ||||
|       while (current_yi != cell_dest_yi + down_flag) { | ||||
|         current_yi += dyi; | ||||
|         const float next_mesh_line_y = mesh_index_to_ypos(current_yi); | ||||
|     if (iadd.x == 0) {        // Vertical line? | ||||
|       icell.y += ineg.y;      // Line going down? Just go to the bottom. | ||||
|       while (icell.y != iend.y + ineg.y) { | ||||
|         icell.y += iadd.y; | ||||
|         const float next_mesh_line_y = mesh_index_to_ypos(icell.y); | ||||
|  | ||||
|         /** | ||||
|          * Skip the calculations for an infinite slope. | ||||
|          * For others the next X is the same so this can continue. | ||||
|          * Calculate X at the next Y mesh line. | ||||
|          */ | ||||
|         const float rx = inf_m_flag ? start[X_AXIS] : (next_mesh_line_y - c) / m; | ||||
|         const float rx = inf_ratio_flag ? start.x : (next_mesh_line_y - c) / ratio; | ||||
|  | ||||
|         float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, current_xi, current_yi) | ||||
|                    * planner.fade_scaling_factor_for_z(end[Z_AXIS]); | ||||
|         float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, icell.x, icell.y) | ||||
|                    * planner.fade_scaling_factor_for_z(end.z); | ||||
|  | ||||
|         // Undefined parts of the Mesh in z_values[][] are NAN. | ||||
|         // Replace NAN corrections with 0.0 to prevent NAN propagation. | ||||
|         if (isnan(z0)) z0 = 0.0; | ||||
|  | ||||
|         const float ry = mesh_index_to_ypos(current_yi); | ||||
|         const float ry = mesh_index_to_ypos(icell.y); | ||||
|  | ||||
|         /** | ||||
|          * Without this check, it's possible to generate a zero length move, as in the case where | ||||
|          * the line is heading down, starting exactly on a mesh line boundary. Since this is rare | ||||
|          * it might be fine to remove this check and let planner.buffer_segment() filter it out. | ||||
|          */ | ||||
|         if (ry != start[Y_AXIS]) { | ||||
|           if (!inf_normalized_flag) { | ||||
|             on_axis_distance = use_x_dist ? rx - start[X_AXIS] : ry - start[Y_AXIS]; | ||||
|             e_position = start[E_AXIS] + on_axis_distance * e_normalized_dist; | ||||
|             z_position = start[Z_AXIS] + on_axis_distance * z_normalized_dist; | ||||
|         if (ry != start.y) { | ||||
|           if (!inf_normalized_flag) { // fall-through faster than branch | ||||
|             on_axis_distance = use_x_dist ? rx - start.x : ry - start.y; | ||||
|             e_position = start.e + on_axis_distance * e_normalized_dist; | ||||
|             z_position = start.z + on_axis_distance * z_normalized_dist; | ||||
|           } | ||||
|           else { | ||||
|             e_position = end[E_AXIS]; | ||||
|             z_position = end[Z_AXIS]; | ||||
|             e_position = end.e; | ||||
|             z_position = end.z; | ||||
|           } | ||||
|  | ||||
|           planner.buffer_segment(rx, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder); | ||||
| @@ -197,10 +182,10 @@ | ||||
|       } | ||||
|  | ||||
|       // At the final destination? Usually not, but when on a Y Mesh Line it's completed. | ||||
|       if (current_position[X_AXIS] != end[X_AXIS] || current_position[Y_AXIS] != end[Y_AXIS]) | ||||
|       if (xy_pos_t(current_position) != xy_pos_t(end)) | ||||
|         goto FINAL_MOVE; | ||||
|  | ||||
|       set_current_from_destination(); | ||||
|       current_position = destination; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @@ -208,36 +193,34 @@ | ||||
|      * Handle horizontal lines that stay within one row. | ||||
|      * These need not be perfectly horizontal. | ||||
|      */ | ||||
|     if (dyi == 0) {             // Horizontal line? | ||||
|       current_xi += left_flag;  // Heading left? Just go to the left edge of the cell for the first move. | ||||
|       while (current_xi != cell_dest_xi + left_flag) { | ||||
|         current_xi += dxi; | ||||
|         const float next_mesh_line_x = mesh_index_to_xpos(current_xi), | ||||
|                     ry = m * next_mesh_line_x + c;   // Calculate Y at the next X mesh line | ||||
|     if (iadd.y == 0) {      // Horizontal line? | ||||
|       icell.x += ineg.x;     // Heading left? Just go to the left edge of the cell for the first move. | ||||
|       while (icell.x != iend.x + ineg.x) { | ||||
|         icell.x += iadd.x; | ||||
|         const float rx = mesh_index_to_xpos(icell.x); | ||||
|         const float ry = ratio * rx + c;    // Calculate Y at the next X mesh line | ||||
|  | ||||
|         float z0 = z_correction_for_y_on_vertical_mesh_line(ry, current_xi, current_yi) | ||||
|                    * planner.fade_scaling_factor_for_z(end[Z_AXIS]); | ||||
|         float z0 = z_correction_for_y_on_vertical_mesh_line(ry, icell.x, icell.y) | ||||
|                      * planner.fade_scaling_factor_for_z(end.z); | ||||
|  | ||||
|         // Undefined parts of the Mesh in z_values[][] are NAN. | ||||
|         // Replace NAN corrections with 0.0 to prevent NAN propagation. | ||||
|         if (isnan(z0)) z0 = 0.0; | ||||
|  | ||||
|         const float rx = mesh_index_to_xpos(current_xi); | ||||
|  | ||||
|         /** | ||||
|          * Without this check, it's possible to generate a zero length move, as in the case where | ||||
|          * the line is heading left, starting exactly on a mesh line boundary. Since this is rare | ||||
|          * it might be fine to remove this check and let planner.buffer_segment() filter it out. | ||||
|          */ | ||||
|         if (rx != start[X_AXIS]) { | ||||
|         if (rx != start.x) { | ||||
|           if (!inf_normalized_flag) { | ||||
|             on_axis_distance = use_x_dist ? rx - start[X_AXIS] : ry - start[Y_AXIS]; | ||||
|             e_position = start[E_AXIS] + on_axis_distance * e_normalized_dist;  // is based on X or Y because this is a horizontal move | ||||
|             z_position = start[Z_AXIS] + on_axis_distance * z_normalized_dist; | ||||
|             on_axis_distance = use_x_dist ? rx - start.x : ry - start.y; | ||||
|             e_position = start.e + on_axis_distance * e_normalized_dist;  // is based on X or Y because this is a horizontal move | ||||
|             z_position = start.z + on_axis_distance * z_normalized_dist; | ||||
|           } | ||||
|           else { | ||||
|             e_position = end[E_AXIS]; | ||||
|             z_position = end[Z_AXIS]; | ||||
|             e_position = end.e; | ||||
|             z_position = end.z; | ||||
|           } | ||||
|  | ||||
|           if (!planner.buffer_segment(rx, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder)) | ||||
| @@ -245,93 +228,88 @@ | ||||
|         } //else printf("FIRST MOVE PRUNED  "); | ||||
|       } | ||||
|  | ||||
|       if (current_position[X_AXIS] != end[X_AXIS] || current_position[Y_AXIS] != end[Y_AXIS]) | ||||
|       if (xy_pos_t(current_position) != xy_pos_t(end)) | ||||
|         goto FINAL_MOVE; | ||||
|  | ||||
|       set_current_from_destination(); | ||||
|       current_position = destination; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * Handle the generic case of a line crossing both X and Y Mesh lines. | ||||
|      * Generic case of a line crossing both X and Y Mesh lines. | ||||
|      * | ||||
|      */ | ||||
|  | ||||
|     int xi_cnt = cell_start_xi - cell_dest_xi, | ||||
|         yi_cnt = cell_start_yi - cell_dest_yi; | ||||
|     xy_int8_t cnt = (istart - iend).ABS(); | ||||
|  | ||||
|     if (xi_cnt < 0) xi_cnt = -xi_cnt; | ||||
|     if (yi_cnt < 0) yi_cnt = -yi_cnt; | ||||
|     icell += ineg; | ||||
|  | ||||
|     current_xi += left_flag; | ||||
|     current_yi += down_flag; | ||||
|     while (cnt) { | ||||
|  | ||||
|     while (xi_cnt || yi_cnt) { | ||||
|       const float next_mesh_line_x = mesh_index_to_xpos(icell.x + iadd.x), | ||||
|                   next_mesh_line_y = mesh_index_to_ypos(icell.y + iadd.y), | ||||
|                   ry = ratio * next_mesh_line_x + c,    // Calculate Y at the next X mesh line | ||||
|                   rx = (next_mesh_line_y - c) / ratio;  // Calculate X at the next Y mesh line | ||||
|                                                         // (No need to worry about ratio == 0. | ||||
|                                                         //  In that case, it was already detected | ||||
|                                                         //  as a vertical line move above.) | ||||
|  | ||||
|       const float next_mesh_line_x = mesh_index_to_xpos(current_xi + dxi), | ||||
|                   next_mesh_line_y = mesh_index_to_ypos(current_yi + dyi), | ||||
|                   ry = m * next_mesh_line_x + c,   // Calculate Y at the next X mesh line | ||||
|                   rx = (next_mesh_line_y - c) / m; // Calculate X at the next Y mesh line | ||||
|                                                    // (No need to worry about m being zero. | ||||
|                                                    //  If that was the case, it was already detected | ||||
|                                                    //  as a vertical line move above.) | ||||
|  | ||||
|       if (left_flag == (rx > next_mesh_line_x)) { // Check if we hit the Y line first | ||||
|       if (neg.x == (rx > next_mesh_line_x)) { // Check if we hit the Y line first | ||||
|         // Yes!  Crossing a Y Mesh Line next | ||||
|         float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, current_xi - left_flag, current_yi + dyi) | ||||
|                    * planner.fade_scaling_factor_for_z(end[Z_AXIS]); | ||||
|         float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, icell.x - ineg.x, icell.y + iadd.y) | ||||
|                    * planner.fade_scaling_factor_for_z(end.z); | ||||
|  | ||||
|         // Undefined parts of the Mesh in z_values[][] are NAN. | ||||
|         // Replace NAN corrections with 0.0 to prevent NAN propagation. | ||||
|         if (isnan(z0)) z0 = 0.0; | ||||
|  | ||||
|         if (!inf_normalized_flag) { | ||||
|           on_axis_distance = use_x_dist ? rx - start[X_AXIS] : next_mesh_line_y - start[Y_AXIS]; | ||||
|           e_position = start[E_AXIS] + on_axis_distance * e_normalized_dist; | ||||
|           z_position = start[Z_AXIS] + on_axis_distance * z_normalized_dist; | ||||
|           on_axis_distance = use_x_dist ? rx - start.x : next_mesh_line_y - start.y; | ||||
|           e_position = start.e + on_axis_distance * e_normalized_dist; | ||||
|           z_position = start.z + on_axis_distance * z_normalized_dist; | ||||
|         } | ||||
|         else { | ||||
|           e_position = end[E_AXIS]; | ||||
|           z_position = end[Z_AXIS]; | ||||
|           e_position = end.e; | ||||
|           z_position = end.z; | ||||
|         } | ||||
|         if (!planner.buffer_segment(rx, next_mesh_line_y, z_position + z0, e_position, scaled_fr_mm_s, extruder)) | ||||
|           break; | ||||
|         current_yi += dyi; | ||||
|         yi_cnt--; | ||||
|         icell.y += iadd.y; | ||||
|         cnt.y--; | ||||
|       } | ||||
|       else { | ||||
|         // Yes!  Crossing a X Mesh Line next | ||||
|         float z0 = z_correction_for_y_on_vertical_mesh_line(ry, current_xi + dxi, current_yi - down_flag) | ||||
|                    * planner.fade_scaling_factor_for_z(end[Z_AXIS]); | ||||
|         float z0 = z_correction_for_y_on_vertical_mesh_line(ry, icell.x + iadd.x, icell.y - ineg.y) | ||||
|                    * planner.fade_scaling_factor_for_z(end.z); | ||||
|  | ||||
|         // Undefined parts of the Mesh in z_values[][] are NAN. | ||||
|         // Replace NAN corrections with 0.0 to prevent NAN propagation. | ||||
|         if (isnan(z0)) z0 = 0.0; | ||||
|  | ||||
|         if (!inf_normalized_flag) { | ||||
|           on_axis_distance = use_x_dist ? next_mesh_line_x - start[X_AXIS] : ry - start[Y_AXIS]; | ||||
|           e_position = start[E_AXIS] + on_axis_distance * e_normalized_dist; | ||||
|           z_position = start[Z_AXIS] + on_axis_distance * z_normalized_dist; | ||||
|           on_axis_distance = use_x_dist ? next_mesh_line_x - start.x : ry - start.y; | ||||
|           e_position = start.e + on_axis_distance * e_normalized_dist; | ||||
|           z_position = start.z + on_axis_distance * z_normalized_dist; | ||||
|         } | ||||
|         else { | ||||
|           e_position = end[E_AXIS]; | ||||
|           z_position = end[Z_AXIS]; | ||||
|           e_position = end.e; | ||||
|           z_position = end.z; | ||||
|         } | ||||
|  | ||||
|         if (!planner.buffer_segment(next_mesh_line_x, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder)) | ||||
|           break; | ||||
|         current_xi += dxi; | ||||
|         xi_cnt--; | ||||
|         icell.x += iadd.x; | ||||
|         cnt.x--; | ||||
|       } | ||||
|  | ||||
|       if (xi_cnt < 0 || yi_cnt < 0) break; // Too far! Exit the loop and go to FINAL_MOVE | ||||
|       if (cnt.x < 0 || cnt.y < 0) break; // Too far! Exit the loop and go to FINAL_MOVE | ||||
|     } | ||||
|  | ||||
|     if (current_position[X_AXIS] != end[X_AXIS] || current_position[Y_AXIS] != end[Y_AXIS]) | ||||
|     if (xy_pos_t(current_position) != xy_pos_t(end)) | ||||
|       goto FINAL_MOVE; | ||||
|  | ||||
|     set_current_from_destination(); | ||||
|     current_position = destination; | ||||
|   } | ||||
|  | ||||
| #else // UBL_SEGMENTED | ||||
| @@ -356,56 +334,42 @@ | ||||
|  | ||||
|   bool _O2 unified_bed_leveling::line_to_destination_segmented(const feedRate_t &scaled_fr_mm_s) { | ||||
|  | ||||
|     if (!position_is_reachable(destination[X_AXIS], destination[Y_AXIS]))  // fail if moving outside reachable boundary | ||||
|       return true; // did not move, so current_position still accurate | ||||
|     if (!position_is_reachable(destination))  // fail if moving outside reachable boundary | ||||
|       return true;                            // did not move, so current_position still accurate | ||||
|  | ||||
|     const float total[XYZE] = { | ||||
|       destination[X_AXIS] - current_position[X_AXIS], | ||||
|       destination[Y_AXIS] - current_position[Y_AXIS], | ||||
|       destination[Z_AXIS] - current_position[Z_AXIS], | ||||
|       destination[E_AXIS] - current_position[E_AXIS] | ||||
|     }; | ||||
|     const xyze_pos_t total = destination - current_position; | ||||
|  | ||||
|     const float cartesian_xy_mm = HYPOT(total[X_AXIS], total[Y_AXIS]);  // total horizontal xy distance | ||||
|     const float cart_xy_mm_2 = HYPOT2(total.x, total.y), | ||||
|                 cart_xy_mm = SQRT(cart_xy_mm_2);                                     // Total XY distance | ||||
|  | ||||
|     #if IS_KINEMATIC | ||||
|       const float seconds = cartesian_xy_mm / scaled_fr_mm_s;                             // Duration of XY move at requested rate | ||||
|       uint16_t segments = LROUND(delta_segments_per_second * seconds),                    // Preferred number of segments for distance @ feedrate | ||||
|                seglimit = LROUND(cartesian_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length | ||||
|       NOMORE(segments, seglimit);                                                         // Limit to minimum segment length (fewer segments) | ||||
|       const float seconds = cart_xy_mm / scaled_fr_mm_s;                             // Duration of XY move at requested rate | ||||
|       uint16_t segments = LROUND(delta_segments_per_second * seconds),               // Preferred number of segments for distance @ feedrate | ||||
|                seglimit = LROUND(cart_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length | ||||
|       NOMORE(segments, seglimit);                                                    // Limit to minimum segment length (fewer segments) | ||||
|     #else | ||||
|       uint16_t segments = LROUND(cartesian_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // cartesian fixed segment length | ||||
|       uint16_t segments = LROUND(cart_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length | ||||
|     #endif | ||||
|  | ||||
|     NOLESS(segments, 1U);                        // must have at least one segment | ||||
|     const float inv_segments = 1.0f / segments;  // divide once, multiply thereafter | ||||
|     NOLESS(segments, 1U);                                                            // Must have at least one segment | ||||
|     const float inv_segments = 1.0f / segments,                                      // Reciprocal to save calculation | ||||
|                 segment_xyz_mm = SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments;    // Length of each segment | ||||
|  | ||||
|     const float segment_xyz_mm = HYPOT(cartesian_xy_mm, total[Z_AXIS]) * inv_segments;   // length of each segment | ||||
|     #if ENABLED(SCARA_FEEDRATE_SCALING) | ||||
|       const float inv_duration = scaled_fr_mm_s / segment_xyz_mm; | ||||
|     #endif | ||||
|  | ||||
|     const float diff[XYZE] = { | ||||
|       total[X_AXIS] * inv_segments, | ||||
|       total[Y_AXIS] * inv_segments, | ||||
|       total[Z_AXIS] * inv_segments, | ||||
|       total[E_AXIS] * inv_segments | ||||
|     }; | ||||
|     xyze_float_t diff = total * inv_segments; | ||||
|  | ||||
|     // Note that E segment distance could vary slightly as z mesh height | ||||
|     // changes for each segment, but small enough to ignore. | ||||
|  | ||||
|     float raw[XYZE] = { | ||||
|       current_position[X_AXIS], | ||||
|       current_position[Y_AXIS], | ||||
|       current_position[Z_AXIS], | ||||
|       current_position[E_AXIS] | ||||
|     }; | ||||
|     xyze_pos_t raw = current_position; | ||||
|  | ||||
|     // Just do plain segmentation if UBL is inactive or the target is above the fade height | ||||
|     if (!planner.leveling_active || !planner.leveling_active_at_z(destination[Z_AXIS])) { | ||||
|     if (!planner.leveling_active || !planner.leveling_active_at_z(destination.z)) { | ||||
|       while (--segments) { | ||||
|         LOOP_XYZE(i) raw[i] += diff[i]; | ||||
|         raw += diff; | ||||
|         planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm | ||||
|           #if ENABLED(SCARA_FEEDRATE_SCALING) | ||||
|             , inv_duration | ||||
| @@ -417,17 +381,17 @@ | ||||
|           , inv_duration | ||||
|         #endif | ||||
|       ); | ||||
|       return false; // moved but did not set_current_from_destination(); | ||||
|       return false; // Did not set current from destination | ||||
|     } | ||||
|  | ||||
|     // Otherwise perform per-segment leveling | ||||
|  | ||||
|     #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|       const float fade_scaling_factor = planner.fade_scaling_factor_for_z(destination[Z_AXIS]); | ||||
|       const float fade_scaling_factor = planner.fade_scaling_factor_for_z(destination.z); | ||||
|     #endif | ||||
|  | ||||
|     // increment to first segment destination | ||||
|     LOOP_XYZE(i) raw[i] += diff[i]; | ||||
|     // Move to first segment destination | ||||
|     raw += diff; | ||||
|  | ||||
|     for (;;) {  // for each mesh cell encountered during the move | ||||
|  | ||||
| @@ -438,75 +402,68 @@ | ||||
|       // in top of loop and again re-find same adjacent cell and use it, just less efficient | ||||
|       // for mesh inset area. | ||||
|  | ||||
|       int8_t cell_xi = (raw[X_AXIS] - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST), | ||||
|              cell_yi = (raw[Y_AXIS] - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST); | ||||
|       xy_int8_t icell = { | ||||
|         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(cell_xi, 0, (GRID_MAX_POINTS_X) - 1); | ||||
|       LIMIT(cell_yi, 0, (GRID_MAX_POINTS_Y) - 1); | ||||
|  | ||||
|       const float x0 = mesh_index_to_xpos(cell_xi),   // 64 byte table lookup avoids mul+add | ||||
|                   y0 = mesh_index_to_ypos(cell_yi); | ||||
|  | ||||
|       float z_x0y0 = z_values[cell_xi  ][cell_yi  ],  // z at lower left corner | ||||
|             z_x1y0 = z_values[cell_xi+1][cell_yi  ],  // z at upper left corner | ||||
|             z_x0y1 = z_values[cell_xi  ][cell_yi+1],  // z at lower right corner | ||||
|             z_x1y1 = z_values[cell_xi+1][cell_yi+1];  // z at upper right 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_x0y1 = z_values[icell.x  ][icell.y+1],  // z at lower right corner | ||||
|             z_x1y1 = z_values[icell.x+1][icell.y+1];  // z at upper right corner | ||||
|  | ||||
|       if (isnan(z_x0y0)) z_x0y0 = 0;              // ideally activating planner.leveling_active (G29 A) | ||||
|       if (isnan(z_x1y0)) z_x1y0 = 0;              //   should refuse if any invalid mesh points | ||||
|       if (isnan(z_x0y1)) z_x0y1 = 0;              //   in order to avoid isnan tests per cell, | ||||
|       if (isnan(z_x1y1)) z_x1y1 = 0;              //   thus guessing zero for undefined points | ||||
|  | ||||
|       float cx = raw[X_AXIS] - x0,   // cell-relative x and y | ||||
|             cy = raw[Y_AXIS] - y0; | ||||
|       const xy_pos_t pos = { mesh_index_to_xpos(icell.x), mesh_index_to_ypos(icell.y) }; | ||||
|       xy_pos_t cell = raw - pos; | ||||
|  | ||||
|       const float z_xmy0 = (z_x1y0 - z_x0y0) * RECIPROCAL(MESH_X_DIST),   // z slope per x along y0 (lower left to lower right) | ||||
|                   z_xmy1 = (z_x1y1 - z_x0y1) * RECIPROCAL(MESH_X_DIST);   // z slope per x along y1 (upper left to upper right) | ||||
|  | ||||
|             float z_cxy0 = z_x0y0 + z_xmy0 * cx;            // z height along y0 at cx (changes for each cx in cell) | ||||
|             float z_cxy0 = z_x0y0 + z_xmy0 * cell.x;        // z height along y0 at cell.x (changes for each cell.x in cell) | ||||
|  | ||||
|       const float z_cxy1 = z_x0y1 + z_xmy1 * cx,            // z height along y1 at cx | ||||
|                   z_cxyd = z_cxy1 - z_cxy0;                 // z height difference along cx from y0 to y1 | ||||
|       const float z_cxy1 = z_x0y1 + z_xmy1 * cell.x,        // z height along y1 at cell.x | ||||
|                   z_cxyd = z_cxy1 - z_cxy0;                 // z height difference along cell.x from y0 to y1 | ||||
|  | ||||
|             float z_cxym = z_cxyd * RECIPROCAL(MESH_Y_DIST);  // z slope per y along cx from y0 to y1 (changes for each cx in cell) | ||||
|             float z_cxym = z_cxyd * RECIPROCAL(MESH_Y_DIST); // z slope per y along cell.x from pos.y to y1 (changes for each cell.x in cell) | ||||
|  | ||||
|       //    float z_cxcy = z_cxy0 + z_cxym * cy;            // interpolated mesh z height along cx at cy (do inside the segment loop) | ||||
|       //    float z_cxcy = z_cxy0 + z_cxym * cell.y;        // interpolated mesh z height along cell.x at cell.y (do inside the segment loop) | ||||
|  | ||||
|       // As subsequent segments step through this cell, the z_cxy0 intercept will change | ||||
|       // and the z_cxym slope will change, both as a function of cx within the cell, and | ||||
|       // and the z_cxym slope will change, both as a function of cell.x within the cell, and | ||||
|       // each change by a constant for fixed segment lengths. | ||||
|  | ||||
|       const float z_sxy0 = z_xmy0 * diff[X_AXIS],                                     // per-segment adjustment to z_cxy0 | ||||
|                   z_sxym = (z_xmy1 - z_xmy0) * RECIPROCAL(MESH_Y_DIST) * diff[X_AXIS];  // per-segment adjustment to z_cxym | ||||
|       const float z_sxy0 = z_xmy0 * diff.x,                                       // per-segment adjustment to z_cxy0 | ||||
|                   z_sxym = (z_xmy1 - z_xmy0) * RECIPROCAL(MESH_Y_DIST) * diff.x;  // per-segment adjustment to z_cxym | ||||
|  | ||||
|       for (;;) {  // for all segments within this mesh cell | ||||
|  | ||||
|         if (--segments == 0) COPY(raw, destination); // if this is last segment, use destination for exact | ||||
|         if (--segments == 0) raw = destination;     // if this is last segment, use destination for exact | ||||
|  | ||||
|         const float z_cxcy = (z_cxy0 + z_cxym * cy) // interpolated mesh z height along cx at cy | ||||
|         const float z_cxcy = (z_cxy0 + z_cxym * cell.y) // interpolated mesh z height along cell.x at cell.y | ||||
|           #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|             * fade_scaling_factor                   // apply fade factor to interpolated mesh height | ||||
|           #endif | ||||
|         ; | ||||
|  | ||||
|         const float z = raw[Z_AXIS]; | ||||
|         raw[Z_AXIS] += z_cxcy; | ||||
|         planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm | ||||
|         planner.buffer_line(raw.x, raw.y, raw.z + z_cxcy, raw.e, scaled_fr_mm_s, active_extruder, segment_xyz_mm | ||||
|           #if ENABLED(SCARA_FEEDRATE_SCALING) | ||||
|             , inv_duration | ||||
|           #endif | ||||
|         ); | ||||
|         raw[Z_AXIS] = z; | ||||
|  | ||||
|         if (segments == 0)                        // done with last segment | ||||
|           return false;                           // did not set_current_from_destination() | ||||
|           return false;                           // didn't set current from destination | ||||
|  | ||||
|         LOOP_XYZE(i) raw[i] += diff[i]; | ||||
|         raw += diff; | ||||
|         cell += diff; | ||||
|  | ||||
|         cx += diff[X_AXIS]; | ||||
|         cy += diff[Y_AXIS]; | ||||
|  | ||||
|         if (!WITHIN(cx, 0, MESH_X_DIST) || !WITHIN(cy, 0, MESH_Y_DIST))    // done within this cell, break to next | ||||
|         if (!WITHIN(cell.x, 0, MESH_X_DIST) || !WITHIN(cell.y, 0, MESH_Y_DIST))    // done within this cell, break to next | ||||
|           break; | ||||
|  | ||||
|         // Next segment still within same mesh cell, adjust the per-segment | ||||
|   | ||||
| @@ -36,7 +36,7 @@ | ||||
|  | ||||
| #include "dac_mcp4728.h" | ||||
|  | ||||
| uint16_t mcp4728_values[XYZE]; | ||||
| xyze_uint_t mcp4728_values; | ||||
|  | ||||
| /** | ||||
|  * Begin I2C, get current values (input register and eeprom) of mcp4728 | ||||
| @@ -121,8 +121,8 @@ uint8_t mcp4728_getDrvPct(const uint8_t channel) { return uint8_t(100.0 * mcp472 | ||||
|  * Receives all Drive strengths as 0-100 percent values, updates | ||||
|  * DAC Values array and calls fastwrite to update the DAC. | ||||
|  */ | ||||
| void mcp4728_setDrvPct(uint8_t pct[XYZE]) { | ||||
|   LOOP_XYZE(i) mcp4728_values[i] = 0.01 * pct[i] * (DAC_STEPPER_MAX); | ||||
| void mcp4728_setDrvPct(xyze_uint8_t &pct) { | ||||
|   mcp4728_values *= 0.01 * pct * (DAC_STEPPER_MAX); | ||||
|   mcp4728_fastWrite(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -25,6 +25,8 @@ | ||||
|  * Arduino library for MicroChip MCP4728 I2C D/A converter. | ||||
|  */ | ||||
|  | ||||
| #include "../../core/types.h" | ||||
|  | ||||
| #include <Wire.h> | ||||
|  | ||||
| #define defaultVDD     DAC_STEPPER_MAX //was 5000 but differs with internal Vref | ||||
| @@ -54,4 +56,4 @@ uint16_t mcp4728_getValue(const uint8_t channel); | ||||
| uint8_t mcp4728_fastWrite(); | ||||
| uint8_t mcp4728_simpleCommand(const byte simpleCommand); | ||||
| uint8_t mcp4728_getDrvPct(const uint8_t channel); | ||||
| void mcp4728_setDrvPct(uint8_t pct[XYZE]); | ||||
| void mcp4728_setDrvPct(xyze_uint8_t &pct); | ||||
|   | ||||
| @@ -31,8 +31,8 @@ | ||||
| #include "stepper_dac.h" | ||||
|  | ||||
| bool dac_present = false; | ||||
| const uint8_t dac_order[NUM_AXIS] = DAC_STEPPER_ORDER; | ||||
| uint8_t dac_channel_pct[XYZE] = DAC_MOTOR_CURRENT_DEFAULT; | ||||
| constexpr xyze_uint8_t dac_order = DAC_STEPPER_ORDER; | ||||
| xyze_uint8_t dac_channel_pct = DAC_MOTOR_CURRENT_DEFAULT; | ||||
|  | ||||
| int dac_init() { | ||||
|   #if PIN_EXISTS(DAC_DISABLE) | ||||
| @@ -77,8 +77,8 @@ void dac_current_raw(uint8_t channel, uint16_t val) { | ||||
| static float dac_perc(int8_t n) { return 100.0 * mcp4728_getValue(dac_order[n]) * RECIPROCAL(DAC_STEPPER_MAX); } | ||||
| static float dac_amps(int8_t n) { return mcp4728_getDrvPct(dac_order[n]) * (DAC_STEPPER_MAX) * 0.125 * RECIPROCAL(DAC_STEPPER_SENSE); } | ||||
|  | ||||
| uint8_t dac_current_get_percent(AxisEnum axis) { return mcp4728_getDrvPct(dac_order[axis]); } | ||||
| void dac_current_set_percents(const uint8_t pct[XYZE]) { | ||||
| uint8_t dac_current_get_percent(const AxisEnum axis) { return mcp4728_getDrvPct(dac_order[axis]); } | ||||
| void dac_current_set_percents(xyze_uint8_t &pct) { | ||||
|   LOOP_XYZE(i) dac_channel_pct[i] = pct[dac_order[i]]; | ||||
|   mcp4728_setDrvPct(dac_channel_pct); | ||||
| } | ||||
|   | ||||
| @@ -33,4 +33,4 @@ void dac_current_raw(uint8_t channel, uint16_t val); | ||||
| void dac_print_values(); | ||||
| void dac_commit_eeprom(); | ||||
| uint8_t dac_current_get_percent(AxisEnum axis); | ||||
| void dac_current_set_percents(const uint8_t pct[XYZE]); | ||||
| void dac_current_set_percents(xyze_uint8_t &pct); | ||||
|   | ||||
| @@ -123,8 +123,8 @@ void FWRetract::retract(const bool retracting | ||||
|         SERIAL_ECHOLNPAIR("retracted_swap[", i, "] ", retracted_swap[i]); | ||||
|       #endif | ||||
|     } | ||||
|     SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]); | ||||
|     SERIAL_ECHOLNPAIR("current_position[e] ", current_position[E_AXIS]); | ||||
|     SERIAL_ECHOLNPAIR("current_position.z ", current_position.z); | ||||
|     SERIAL_ECHOLNPAIR("current_position.e ", current_position.e); | ||||
|     SERIAL_ECHOLNPAIR("current_hop ", current_hop); | ||||
|   //*/ | ||||
|  | ||||
| @@ -136,7 +136,7 @@ void FWRetract::retract(const bool retracting | ||||
|               ); | ||||
|  | ||||
|   // The current position will be the destination for E and Z moves | ||||
|   set_destination_from_current(); | ||||
|   destination = current_position; | ||||
|  | ||||
|   #if ENABLED(RETRACT_SYNC_MIXING) | ||||
|     const uint8_t old_mixing_tool = mixer.get_current_vtool(); | ||||
| @@ -147,7 +147,7 @@ void FWRetract::retract(const bool retracting | ||||
|   if (retracting) { | ||||
|     // Retract by moving from a faux E position back to the current E position | ||||
|     current_retract[active_extruder] = base_retract; | ||||
|     prepare_internal_move_to_destination(  // set_current_to_destination | ||||
|     prepare_internal_move_to_destination(                 // set current to destination | ||||
|       settings.retract_feedrate_mm_s | ||||
|       #if ENABLED(RETRACT_SYNC_MIXING) | ||||
|         * (MIXING_STEPPERS) | ||||
| @@ -171,7 +171,7 @@ void FWRetract::retract(const bool retracting | ||||
|  | ||||
|     const float extra_recover = swapping ? settings.swap_retract_recover_extra : settings.retract_recover_extra; | ||||
|     if (extra_recover) { | ||||
|       current_position[E_AXIS] -= extra_recover;          // Adjust the current E position by the extra amount to recover | ||||
|       current_position.e -= extra_recover;          // Adjust the current E position by the extra amount to recover | ||||
|       sync_plan_position_e();                             // Sync the planner position so the extra amount is recovered | ||||
|     } | ||||
|  | ||||
| @@ -207,8 +207,8 @@ void FWRetract::retract(const bool retracting | ||||
|         SERIAL_ECHOLNPAIR("retracted_swap[", i, "] ", retracted_swap[i]); | ||||
|       #endif | ||||
|     } | ||||
|     SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]); | ||||
|     SERIAL_ECHOLNPAIR("current_position[e] ", current_position[E_AXIS]); | ||||
|     SERIAL_ECHOLNPAIR("current_position.z ", current_position.z); | ||||
|     SERIAL_ECHOLNPAIR("current_position.e ", current_position.e); | ||||
|     SERIAL_ECHOLNPAIR("current_hop ", current_hop); | ||||
|   //*/ | ||||
| } | ||||
|   | ||||
| @@ -71,35 +71,35 @@ Joystick joystick; | ||||
|  | ||||
| #if HAS_JOY_ADC_X || HAS_JOY_ADC_Y || HAS_JOY_ADC_Z | ||||
|  | ||||
|   void Joystick::calculate(float (&norm_jog)[XYZ]) { | ||||
|   void Joystick::calculate(xyz_float_t &norm_jog) { | ||||
|     // Do nothing if enable pin (active-low) is not LOW | ||||
|     #if HAS_JOY_ADC_EN | ||||
|       if (READ(JOY_EN_PIN)) return; | ||||
|     #endif | ||||
|  | ||||
|     auto _normalize_joy = [](float &norm_jog, const int16_t raw, const int16_t (&joy_limits)[4]) { | ||||
|     auto _normalize_joy = [](float &axis_jog, const int16_t raw, const int16_t (&joy_limits)[4]) { | ||||
|       if (WITHIN(raw, joy_limits[0], joy_limits[3])) { | ||||
|         // within limits, check deadzone | ||||
|         if (raw > joy_limits[2]) | ||||
|           norm_jog = (raw - joy_limits[2]) / float(joy_limits[3] - joy_limits[2]); | ||||
|           axis_jog = (raw - joy_limits[2]) / float(joy_limits[3] - joy_limits[2]); | ||||
|         else if (raw < joy_limits[1]) | ||||
|           norm_jog = (raw - joy_limits[1]) / float(joy_limits[1] - joy_limits[0]);  // negative value | ||||
|           axis_jog = (raw - joy_limits[1]) / float(joy_limits[1] - joy_limits[0]);  // negative value | ||||
|         // Map normal to jog value via quadratic relationship | ||||
|         norm_jog = SIGN(norm_jog) * sq(norm_jog); | ||||
|         axis_jog = SIGN(axis_jog) * sq(axis_jog); | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     #if HAS_JOY_ADC_X | ||||
|       static constexpr int16_t joy_x_limits[4] = JOY_X_LIMITS; | ||||
|       _normalize_joy(norm_jog[X_AXIS], x.raw, joy_x_limits); | ||||
|       _normalize_joy(norm_jog.x, x.raw, joy_x_limits); | ||||
|     #endif | ||||
|     #if HAS_JOY_ADC_Y | ||||
|       static constexpr int16_t joy_y_limits[4] = JOY_Y_LIMITS; | ||||
|       _normalize_joy(norm_jog[Y_AXIS], y.raw, joy_y_limits); | ||||
|       _normalize_joy(norm_jog.y, y.raw, joy_y_limits); | ||||
|     #endif | ||||
|     #if HAS_JOY_ADC_Z | ||||
|       static constexpr int16_t joy_z_limits[4] = JOY_Z_LIMITS; | ||||
|       _normalize_joy(norm_jog[Z_AXIS], z.raw, joy_z_limits); | ||||
|       _normalize_joy(norm_jog.z, z.raw, joy_z_limits); | ||||
|     #endif | ||||
|   } | ||||
|  | ||||
| @@ -129,7 +129,7 @@ Joystick joystick; | ||||
|     // Normalized jog values are 0 for no movement and -1 or +1 for as max feedrate (nonlinear relationship) | ||||
|     // Jog are initialized to zero and handling input can update values but doesn't have to | ||||
|     // You could use a two-axis joystick and a one-axis keypad and they might work together | ||||
|     float norm_jog[XYZ] = { 0 }; | ||||
|     xyz_float_t norm_jog{0}; | ||||
|  | ||||
|     // Use ADC values and defined limits. The active zone is normalized: -1..0 (dead) 0..1 | ||||
|     #if HAS_JOY_ADC_X || HAS_JOY_ADC_Y || HAS_JOY_ADC_Z | ||||
| @@ -143,16 +143,13 @@ Joystick joystick; | ||||
|       ExtUI::_joystick_update(norm_jog); | ||||
|     #endif | ||||
|  | ||||
|     #if EITHER(ULTIPANEL, EXTENSIBLE_UI) | ||||
|       constexpr float manual_feedrate[XYZE] = MANUAL_FEEDRATE; | ||||
|     #endif | ||||
|  | ||||
|     // norm_jog values of [-1 .. 1] maps linearly to [-feedrate .. feedrate] | ||||
|     float move_dist[XYZ] = { 0 }, hypot2 = 0; | ||||
|     xyz_float_t move_dist{0}; | ||||
|     float hypot2 = 0; | ||||
|     LOOP_XYZ(i) if (norm_jog[i]) { | ||||
|       move_dist[i] = seg_time * norm_jog[i] * | ||||
|         #if EITHER(ULTIPANEL, EXTENSIBLE_UI) | ||||
|           MMM_TO_MMS(manual_feedrate[i]); | ||||
|           MMM_TO_MMS(manual_feedrate_mm_m[i]); | ||||
|         #else | ||||
|           planner.settings.max_feedrate_mm_s[i]; | ||||
|         #endif | ||||
| @@ -160,7 +157,7 @@ Joystick joystick; | ||||
|     } | ||||
|  | ||||
|     if (!UNEAR_ZERO(hypot2)) { | ||||
|       LOOP_XYZ(i) current_position[i] += move_dist[i]; | ||||
|       current_position += move_dist; | ||||
|       const float length = sqrt(hypot2); | ||||
|       injecting_now = true; | ||||
|       planner.buffer_line(current_position, length / seg_time, active_extruder, length); | ||||
|   | ||||
| @@ -25,6 +25,8 @@ | ||||
|  * joystick.h - joystick input / jogging | ||||
|  */ | ||||
|  | ||||
| #include "../inc/MarlinConfigPre.h" | ||||
| #include "../core/types.h" | ||||
| #include "../core/macros.h" | ||||
| #include "../module/temperature.h" | ||||
|  | ||||
| @@ -46,7 +48,7 @@ class Joystick { | ||||
|     #if ENABLED(JOYSTICK_DEBUG) | ||||
|       static void report(); | ||||
|     #endif | ||||
|     static void calculate(float (&norm_jog)[XYZ]); | ||||
|     static void calculate(xyz_float_t &norm_jog); | ||||
|     static void inject_jog_moves(); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -64,7 +64,7 @@ | ||||
|  | ||||
| // private: | ||||
|  | ||||
| static float resume_position[XYZE]; | ||||
| static xyze_pos_t resume_position; | ||||
|  | ||||
| PauseMode pause_mode = PAUSE_MODE_PAUSE_PRINT; | ||||
|  | ||||
| @@ -126,8 +126,8 @@ void do_pause_e_move(const float &length, const feedRate_t &fr_mm_s) { | ||||
|   #if HAS_FILAMENT_SENSOR | ||||
|     runout.reset(); | ||||
|   #endif | ||||
|   current_position[E_AXIS] += length / planner.e_factor[active_extruder]; | ||||
|   planner.buffer_line(current_position, fr_mm_s, active_extruder); | ||||
|   current_position.e += length / planner.e_factor[active_extruder]; | ||||
|   line_to_current_position(fr_mm_s); | ||||
|   planner.synchronize(); | ||||
| } | ||||
|  | ||||
| @@ -385,7 +385,7 @@ bool unload_filament(const float &unload_length, const bool show_lcd/*=false*/, | ||||
|  */ | ||||
| uint8_t did_pause_print = 0; | ||||
|  | ||||
| bool pause_print(const float &retract, const point_t &park_point, const float &unload_length/*=0*/, const bool show_lcd/*=false*/ DXC_ARGS) { | ||||
| bool pause_print(const float &retract, const xyz_pos_t &park_point, const float &unload_length/*=0*/, const bool show_lcd/*=false*/ DXC_ARGS) { | ||||
|  | ||||
|   #if !HAS_LCD_MENU | ||||
|     UNUSED(show_lcd); | ||||
| @@ -432,7 +432,7 @@ bool pause_print(const float &retract, const point_t &park_point, const float &u | ||||
|   print_job_timer.pause(); | ||||
|  | ||||
|   // Save current position | ||||
|   COPY(resume_position, current_position); | ||||
|   resume_position = current_position; | ||||
|  | ||||
|   // Wait for buffered blocks to complete | ||||
|   planner.synchronize(); | ||||
| @@ -611,10 +611,10 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep | ||||
|  * - Display "wait for print to resume" | ||||
|  * - Re-prime the nozzle... | ||||
|  *   -  FWRETRACT: Recover/prime from the prior G10. | ||||
|  *   - !FWRETRACT: Retract by resume_position[E], if negative. | ||||
|  *   - !FWRETRACT: Retract by resume_position.e, if negative. | ||||
|  *                 Not sure how this logic comes into use. | ||||
|  * - Move the nozzle back to resume_position | ||||
|  * - Sync the planner E to resume_position[E] | ||||
|  * - Sync the planner E to resume_position.e | ||||
|  * - Send host action for resume, if configured | ||||
|  * - Resume the current SD print job, if any | ||||
|  */ | ||||
| @@ -652,13 +652,13 @@ void resume_print(const float &slow_load_length/*=0*/, const float &fast_load_le | ||||
|   #endif | ||||
|  | ||||
|   // If resume_position is negative | ||||
|   if (resume_position[E_AXIS] < 0) do_pause_e_move(resume_position[E_AXIS], feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE)); | ||||
|   if (resume_position.e < 0) do_pause_e_move(resume_position.e, feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE)); | ||||
|  | ||||
|   // Move XY to starting position, then Z | ||||
|   do_blocking_move_to_xy(resume_position[X_AXIS], resume_position[Y_AXIS], feedRate_t(NOZZLE_PARK_XY_FEEDRATE)); | ||||
|   do_blocking_move_to_xy(xy_pos_t(resume_position), feedRate_t(NOZZLE_PARK_XY_FEEDRATE)); | ||||
|  | ||||
|   // Move Z_AXIS to saved position | ||||
|   do_blocking_move_to_z(resume_position[Z_AXIS], feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|   do_blocking_move_to_z(resume_position.z, feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|  | ||||
|   #if ADVANCED_PAUSE_RESUME_PRIME != 0 | ||||
|     do_pause_e_move(ADVANCED_PAUSE_RESUME_PRIME, feedRate_t(ADVANCED_PAUSE_PURGE_FEEDRATE)); | ||||
| @@ -666,7 +666,7 @@ void resume_print(const float &slow_load_length/*=0*/, const float &fast_load_le | ||||
|  | ||||
|   // Now all extrusion positions are resumed and ready to be confirmed | ||||
|   // Set extruder to saved position | ||||
|   planner.set_e_position_mm((destination[E_AXIS] = current_position[E_AXIS] = resume_position[E_AXIS])); | ||||
|   planner.set_e_position_mm((destination.e = current_position.e = resume_position.e)); | ||||
|  | ||||
|   #if HAS_LCD_MENU | ||||
|     lcd_pause_show_message(PAUSE_MESSAGE_STATUS); | ||||
|   | ||||
| @@ -83,7 +83,7 @@ extern uint8_t did_pause_print; | ||||
|  | ||||
| void do_pause_e_move(const float &length, const feedRate_t &fr_mm_s); | ||||
|  | ||||
| bool pause_print(const float &retract, const point_t &park_point, const float &unload_length=0, const bool show_lcd=false DXC_PARAMS); | ||||
| bool pause_print(const float &retract, const xyz_pos_t &park_point, const float &unload_length=0, const bool show_lcd=false DXC_PARAMS); | ||||
|  | ||||
| void wait_for_confirmation(const bool is_reload=false, const int8_t max_beep_count=0 DXC_PARAMS); | ||||
|  | ||||
|   | ||||
| @@ -156,7 +156,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const bool save_queue/*= | ||||
|         || ELAPSED(ms, next_save_ms) | ||||
|       #endif | ||||
|       // Save if Z is above the last-saved position by some minimum height | ||||
|       || current_position[Z_AXIS] > info.current_position[Z_AXIS] + POWER_LOSS_MIN_Z_CHANGE | ||||
|       || current_position.z > info.current_position.z + POWER_LOSS_MIN_Z_CHANGE | ||||
|     #endif | ||||
|   ) { | ||||
|  | ||||
| @@ -170,12 +170,12 @@ void PrintJobRecovery::save(const bool force/*=false*/, const bool save_queue/*= | ||||
|     info.valid_foot = info.valid_head; | ||||
|  | ||||
|     // Machine state | ||||
|     COPY(info.current_position, current_position); | ||||
|     info.current_position = current_position; | ||||
|     #if HAS_HOME_OFFSET | ||||
|       COPY(info.home_offset, home_offset); | ||||
|       info.home_offset = home_offset; | ||||
|     #endif | ||||
|     #if HAS_POSITION_SHIFT | ||||
|       COPY(info.position_shift, position_shift); | ||||
|       info.position_shift = position_shift; | ||||
|     #endif | ||||
|     info.feedrate = uint16_t(feedrate_mm_s * 60.0f); | ||||
|  | ||||
| @@ -361,13 +361,13 @@ void PrintJobRecovery::resume() { | ||||
|  | ||||
|   // Move back to the saved XY | ||||
|   sprintf_P(cmd, PSTR("G1 X%s Y%s F3000"), | ||||
|     dtostrf(info.current_position[X_AXIS], 1, 3, str_1), | ||||
|     dtostrf(info.current_position[Y_AXIS], 1, 3, str_2) | ||||
|     dtostrf(info.current_position.x, 1, 3, str_1), | ||||
|     dtostrf(info.current_position.y, 1, 3, str_2) | ||||
|   ); | ||||
|   gcode.process_subcommands_now(cmd); | ||||
|  | ||||
|   // Move back to the saved Z | ||||
|   dtostrf(info.current_position[Z_AXIS], 1, 3, str_1); | ||||
|   dtostrf(info.current_position.z, 1, 3, str_1); | ||||
|   #if Z_HOME_DIR > 0 | ||||
|     sprintf_P(cmd, PSTR("G1 Z%s F200"), str_1); | ||||
|   #else | ||||
| @@ -388,22 +388,20 @@ void PrintJobRecovery::resume() { | ||||
|   gcode.process_subcommands_now(cmd); | ||||
|  | ||||
|   // Restore E position with G92.9 | ||||
|   sprintf_P(cmd, PSTR("G92.9 E%s"), dtostrf(info.current_position[E_AXIS], 1, 3, str_1)); | ||||
|   sprintf_P(cmd, PSTR("G92.9 E%s"), dtostrf(info.current_position.e, 1, 3, str_1)); | ||||
|   gcode.process_subcommands_now(cmd); | ||||
|  | ||||
|   // Relative axis modes | ||||
|   gcode.axis_relative = info.axis_relative; | ||||
|  | ||||
|   #if HAS_HOME_OFFSET | ||||
|     home_offset = info.home_offset; | ||||
|   #endif | ||||
|   #if HAS_POSITION_SHIFT | ||||
|     position_shift = info.position_shift; | ||||
|   #endif | ||||
|   #if HAS_HOME_OFFSET || HAS_POSITION_SHIFT | ||||
|     LOOP_XYZ(i) { | ||||
|       #if HAS_HOME_OFFSET | ||||
|         home_offset[i] = info.home_offset[i]; | ||||
|       #endif | ||||
|       #if HAS_POSITION_SHIFT | ||||
|         position_shift[i] = info.position_shift[i]; | ||||
|       #endif | ||||
|       update_workspace_offset((AxisEnum)i); | ||||
|     } | ||||
|     LOOP_XYZ(i) update_workspace_offset((AxisEnum)i); | ||||
|   #endif | ||||
|  | ||||
|   // Resume the SD file from the last position | ||||
|   | ||||
| @@ -44,13 +44,13 @@ typedef struct { | ||||
|   uint8_t valid_head; | ||||
|  | ||||
|   // Machine state | ||||
|   float current_position[NUM_AXIS]; | ||||
|   xyze_pos_t current_position; | ||||
|  | ||||
|   #if HAS_HOME_OFFSET | ||||
|     float home_offset[XYZ]; | ||||
|     xyz_pos_t home_offset; | ||||
|   #endif | ||||
|   #if HAS_POSITION_SHIFT | ||||
|     float position_shift[XYZ]; | ||||
|     xyz_pos_t position_shift; | ||||
|   #endif | ||||
|  | ||||
|   uint16_t feedrate; | ||||
|   | ||||
| @@ -550,10 +550,10 @@ bool MMU2::get_response() { | ||||
|  */ | ||||
| void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { | ||||
|  | ||||
|   constexpr xyz_pos_t park_point = NOZZLE_PARK_POINT; | ||||
|   bool response = false; | ||||
|   mmu_print_saved = false; | ||||
|   point_t park_point = NOZZLE_PARK_POINT; | ||||
|   float resume_position[XYZE]; | ||||
|   xyz_pos_t resume_position; | ||||
|   int16_t resume_hotend_temp; | ||||
|  | ||||
|   KEEPALIVE_STATE(PAUSED_FOR_USER); | ||||
| @@ -572,7 +572,7 @@ void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { | ||||
|         SERIAL_ECHOLNPGM("MMU not responding"); | ||||
|  | ||||
|         resume_hotend_temp = thermalManager.degTargetHotend(active_extruder); | ||||
|         COPY(resume_position, current_position); | ||||
|         resume_position = current_position; | ||||
|  | ||||
|         if (move_axes && all_axes_homed()) | ||||
|           nozzle.park(2, park_point /*= NOZZLE_PARK_POINT*/); | ||||
| @@ -604,10 +604,10 @@ void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { | ||||
|         BUZZ(200, 404); | ||||
|  | ||||
|         // Move XY to starting position, then Z | ||||
|         do_blocking_move_to_xy(resume_position[X_AXIS], resume_position[Y_AXIS], feedRate_t(NOZZLE_PARK_XY_FEEDRATE)); | ||||
|         do_blocking_move_to_xy(resume_position, feedRate_t(NOZZLE_PARK_XY_FEEDRATE)); | ||||
|  | ||||
|         // Move Z_AXIS to saved position | ||||
|         do_blocking_move_to_z(resume_position[Z_AXIS], feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|         do_blocking_move_to_z(resume_position.z, feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|       } | ||||
|       else { | ||||
|         BUZZ(200, 404); | ||||
| @@ -698,8 +698,8 @@ void MMU2::filament_runout() { | ||||
|     LCD_MESSAGEPGM(MSG_MMU2_EJECTING_FILAMENT); | ||||
|  | ||||
|     enable_E0(); | ||||
|     current_position[E_AXIS] -= MMU2_FILAMENTCHANGE_EJECT_FEED; | ||||
|     planner.buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 2500 / 60, active_extruder); | ||||
|     current_position.e -= MMU2_FILAMENTCHANGE_EJECT_FEED; | ||||
|     line_to_current_position(2500 / 60); | ||||
|     planner.synchronize(); | ||||
|     command(MMU_CMD_E0 + index); | ||||
|     manage_response(false, false); | ||||
| @@ -787,7 +787,7 @@ void MMU2::filament_runout() { | ||||
|       DEBUG_ECHO_START(); | ||||
|       DEBUG_ECHOLNPAIR("E step ", es, "/", fr_mm_m); | ||||
|  | ||||
|       current_position[E_AXIS] += es; | ||||
|       current_position.e += es; | ||||
|       line_to_current_position(MMM_TO_MMS(fr_mm_m)); | ||||
|       planner.synchronize(); | ||||
|  | ||||
|   | ||||
| @@ -327,14 +327,14 @@ class FilamentSensorBase { | ||||
|       } | ||||
|  | ||||
|       static inline void block_completed(const block_t* const b) { | ||||
|         if (b->steps[X_AXIS] || b->steps[Y_AXIS] || b->steps[Z_AXIS] | ||||
|         if (b->steps.x || b->steps.y || b->steps.z | ||||
|           #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|             || did_pause_print // Allow pause purge move to re-trigger runout state | ||||
|           #endif | ||||
|         ) { | ||||
|           // Only trigger on extrusion with XYZ movement to allow filament change and retract/recover. | ||||
|           const uint8_t e = b->extruder; | ||||
|           const int32_t steps = b->steps[E_AXIS]; | ||||
|           const int32_t steps = b->steps.e; | ||||
|           runout_mm_countdown[e] -= (TEST(b->direction_bits, E_AXIS) ? -steps : steps) * planner.steps_to_mm[E_AXIS_N(e)]; | ||||
|         } | ||||
|       } | ||||
|   | ||||
| @@ -367,9 +367,9 @@ void test_tmc_connection(const bool test_x, const bool test_y, const bool test_z | ||||
|     constexpr uint16_t default_sg_guard_duration = 400; | ||||
|  | ||||
|     struct slow_homing_t { | ||||
|       struct { uint32_t x, y; } acceleration; | ||||
|       xy_ulong_t acceleration; | ||||
|       #if HAS_CLASSIC_JERK | ||||
|         struct { float x, y; } jerk; | ||||
|         xy_float_t jerk_xy; | ||||
|       #endif | ||||
|     }; | ||||
|   #endif | ||||
|   | ||||
| @@ -62,7 +62,7 @@ | ||||
| #define G26_ERR true | ||||
|  | ||||
| #if ENABLED(ARC_SUPPORT) | ||||
|   void plan_arc(const float (&cart)[XYZE], const float (&offset)[2], const uint8_t clockwise); | ||||
|   void plan_arc(const xyze_pos_t &cart, const ab_float_t &offset, const uint8_t clockwise); | ||||
| #endif | ||||
|  | ||||
| /** | ||||
| @@ -142,7 +142,7 @@ | ||||
|  | ||||
| // Private functions | ||||
|  | ||||
| static uint16_t circle_flags[16], horizontal_mesh_line_flags[16], vertical_mesh_line_flags[16]; | ||||
| static MeshFlags circle_flags, horizontal_mesh_line_flags, vertical_mesh_line_flags; | ||||
| float g26_e_axis_feedrate = 0.025, | ||||
|       random_deviation = 0.0; | ||||
|  | ||||
| @@ -154,7 +154,7 @@ float g26_extrusion_multiplier, | ||||
|       g26_layer_height, | ||||
|       g26_prime_length; | ||||
|  | ||||
| float g26_x_pos = 0, g26_y_pos = 0; | ||||
| xy_pos_t g26_pos; // = { 0, 0 } | ||||
|  | ||||
| int16_t g26_bed_temp, | ||||
|         g26_hotend_temp; | ||||
| @@ -178,85 +178,85 @@ int8_t g26_prime_flag; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| mesh_index_pair find_closest_circle_to_print(const float &X, const float &Y) { | ||||
| mesh_index_pair find_closest_circle_to_print(const xy_pos_t &pos) { | ||||
|   float closest = 99999.99; | ||||
|   mesh_index_pair return_val; | ||||
|   mesh_index_pair out_point; | ||||
|  | ||||
|   return_val.x_index = return_val.y_index = -1; | ||||
|   out_point.pos = -1; | ||||
|  | ||||
|   for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) { | ||||
|     for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) { | ||||
|       if (!is_bitmap_set(circle_flags, i, j)) { | ||||
|         const float mx = _GET_MESH_X(i),  // We found a circle that needs to be printed | ||||
|                     my = _GET_MESH_Y(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 = HYPOT(X - mx, Y - my); | ||||
|         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 += HYPOT(g26_x_pos - mx, g26_y_pos - my) / 15.0; | ||||
|         f += (g26_pos - m).magnitude() / 15.0f; | ||||
|  | ||||
|         // Add in the specified amount of Random Noise to our search | ||||
|         if (random_deviation > 1.0) | ||||
|           f += random(0.0, random_deviation); | ||||
|         // Add the specified amount of Random Noise to our search | ||||
|         if (random_deviation > 1.0) f += random(0.0, random_deviation); | ||||
|  | ||||
|         if (f < closest) { | ||||
|           closest = f;              // We found a closer location that is still | ||||
|           return_val.x_index = i;   // un-printed  --- save the data for it | ||||
|           return_val.y_index = j; | ||||
|           return_val.distance = closest; | ||||
|           closest = f;          // Found a closer un-printed location | ||||
|           out_point.pos.set(i, j);  // Save its data | ||||
|           out_point.distance = closest; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   bitmap_set(circle_flags, return_val.x_index, return_val.y_index);   // Mark this location as done. | ||||
|   return return_val; | ||||
|   circle_flags.mark(out_point); // Mark this location as done. | ||||
|   return out_point; | ||||
| } | ||||
|  | ||||
| void move_to(const float &rx, const float &ry, const float &z, const float &e_delta) { | ||||
|   static float last_z = -999.99; | ||||
|  | ||||
|   bool has_xy_component = (rx != current_position[X_AXIS] || ry != current_position[Y_AXIS]); // Check if X or Y is involved in the movement. | ||||
|   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. | ||||
|  | ||||
|   destination = current_position; | ||||
|  | ||||
|   if (z != last_z) { | ||||
|     last_z = z; | ||||
|     last_z = destination.z = z; | ||||
|     const feedRate_t feed_value = planner.settings.max_feedrate_mm_s[Z_AXIS] * 0.5f; // Use half of the Z_AXIS max feed rate | ||||
|  | ||||
|     destination[X_AXIS] = current_position[X_AXIS]; | ||||
|     destination[Y_AXIS] = current_position[Y_AXIS]; | ||||
|     destination[Z_AXIS] = z;                          // We know the last_z!=z or we wouldn't be in this block of code. | ||||
|     destination[E_AXIS] = current_position[E_AXIS]; | ||||
|  | ||||
|     prepare_internal_move_to_destination(feed_value); | ||||
|     set_destination_from_current(); | ||||
|     destination = current_position; | ||||
|   } | ||||
|  | ||||
|   // If X or Y is involved do a 'normal' move. Otherwise retract/recover/hop. | ||||
|   destination = dest; | ||||
|   destination.e += e_delta; | ||||
|   const feedRate_t feed_value = has_xy_component ? feedRate_t(G26_XY_FEEDRATE) : planner.settings.max_feedrate_mm_s[E_AXIS] * 0.666f; | ||||
|  | ||||
|   destination[X_AXIS] = rx; | ||||
|   destination[Y_AXIS] = ry; | ||||
|   destination[E_AXIS] += e_delta; | ||||
|  | ||||
|   prepare_internal_move_to_destination(feed_value); | ||||
|   set_destination_from_current(); | ||||
|   destination = current_position; | ||||
| } | ||||
|  | ||||
| FORCE_INLINE void move_to(const float (&where)[XYZE], const float &de) { move_to(where[X_AXIS], where[Y_AXIS], where[Z_AXIS], de); } | ||||
| FORCE_INLINE void move_to(const xyz_pos_t &where, const float &de) { move_to(where.x, where.y, where.z, de); } | ||||
|  | ||||
| void retract_filament(const float (&where)[XYZE]) { | ||||
| 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.0 * g26_retraction_multiplier); | ||||
|     move_to(where, -1.0f * g26_retraction_multiplier); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void recover_filament(const float (&where)[XYZE]) { | ||||
| // 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 | ||||
| } | ||||
|  | ||||
| void recover_filament(const xyz_pos_t &where) { | ||||
|   if (g26_retracted) { // Only un-retract if we are retracted. | ||||
|     move_to(where, 1.2 * g26_retraction_multiplier); | ||||
|     move_to(where, 1.2f * g26_retraction_multiplier); | ||||
|     g26_retracted = false; | ||||
|   } | ||||
| } | ||||
| @@ -276,41 +276,34 @@ void recover_filament(const float (&where)[XYZE]) { | ||||
|  * 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 float &sx, const float &sy, const float &sz, const float &ex, const float &ey, const float &ez) { | ||||
|   const float dx_s = current_position[X_AXIS] - sx,   // find our distance from the start of the actual line segment | ||||
|               dy_s = current_position[Y_AXIS] - sy, | ||||
|               dist_start = HYPOT2(dx_s, dy_s),        // We don't need to do a sqrt(), we can compare the distance^2 | ||||
|                                                       // to save computation time | ||||
|               dx_e = current_position[X_AXIS] - ex,   // find our distance from the end of the actual line segment | ||||
|               dy_e = current_position[Y_AXIS] - ey, | ||||
|               dist_end = HYPOT2(dx_e, dy_e), | ||||
| void print_line_from_here_to_there(const xyz_pos_t &s, const xyz_pos_t &e) { | ||||
|  | ||||
|               line_length = HYPOT(ex - sx, ey - sy); | ||||
|   // 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(ex, ey, ez, sx, sy, sz); | ||||
|     return print_line_from_here_to_there(e, s); | ||||
|  | ||||
|   // Decide whether to retract & bump | ||||
|   // Decide whether to retract & lift | ||||
|   if (dist_start > 2.0) retract_lift_move(s); | ||||
|  | ||||
|   if (dist_start > 2.0) { | ||||
|     retract_filament(destination); | ||||
|     //todo:  parameterize the bump height with a define | ||||
|     move_to(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] + 0.500, 0.0);  // Z bump to minimize scraping | ||||
|     move_to(sx, sy, sz + 0.500, 0.0); // Get to the starting point with no extrusion while bumped | ||||
|   } | ||||
|  | ||||
|   move_to(sx, sy, sz, 0.0); // Get to the starting point with no extrusion / un-Z bump | ||||
|   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; | ||||
|  | ||||
|   recover_filament(destination); | ||||
|   move_to(ex, ey, ez, e_pos_delta);  // Get to the ending point with an appropriate amount of extrusion | ||||
|   move_to(e, e_pos_delta);  // Get to the ending point with an appropriate amount of extrusion | ||||
| } | ||||
|  | ||||
| inline bool look_for_lines_to_connect() { | ||||
|   float sx, sy, ex, ey; | ||||
|   xyz_pos_t s, e; | ||||
|   s.z = e.z = g26_layer_height; | ||||
|  | ||||
|   for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) { | ||||
|     for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) { | ||||
| @@ -319,43 +312,43 @@ inline bool look_for_lines_to_connect() { | ||||
|         if (user_canceled()) return true; | ||||
|       #endif | ||||
|  | ||||
|       if (i < GRID_MAX_POINTS_X) { // Can't connect to anything to the right than GRID_MAX_POINTS_X. | ||||
|                                    // Already a half circle at the edge of the bed. | ||||
|       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 (is_bitmap_set(circle_flags, i, j) && is_bitmap_set(circle_flags, i + 1, j)) { // check if we can do a line to the left | ||||
|           if (!is_bitmap_set(horizontal_mesh_line_flags, i, j)) { | ||||
|         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 | ||||
|             sx = _GET_MESH_X(  i  ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // right edge | ||||
|             ex = _GET_MESH_X(i + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // left edge | ||||
|             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 | ||||
|  | ||||
|             LIMIT(sx, X_MIN_POS + 1, X_MAX_POS - 1); | ||||
|             sy = ey = constrain(_GET_MESH_Y(j), Y_MIN_POS + 1, Y_MAX_POS - 1); | ||||
|             LIMIT(ex, X_MIN_POS + 1, X_MAX_POS - 1); | ||||
|             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); | ||||
|  | ||||
|             if (position_is_reachable(sx, sy) && position_is_reachable(ex, ey)) | ||||
|               print_line_from_here_to_there(sx, sy, g26_layer_height, ex, ey, g26_layer_height); | ||||
|             if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y)) | ||||
|               print_line_from_here_to_there(s, e); | ||||
|  | ||||
|             bitmap_set(horizontal_mesh_line_flags, i, j); // Mark done, even if skipped | ||||
|             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 (is_bitmap_set(circle_flags, i, j) && is_bitmap_set(circle_flags, i, j + 1)) { // check if we can do a line straight down | ||||
|             if (!is_bitmap_set( vertical_mesh_line_flags, i, j)) { | ||||
|           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 | ||||
|               sy = _GET_MESH_Y(  j  ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // top edge | ||||
|               ey = _GET_MESH_Y(j + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // bottom edge | ||||
|               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 | ||||
|  | ||||
|               sx = ex = constrain(_GET_MESH_X(i), X_MIN_POS + 1, X_MAX_POS - 1); | ||||
|               LIMIT(sy, Y_MIN_POS + 1, Y_MAX_POS - 1); | ||||
|               LIMIT(ey, Y_MIN_POS + 1, Y_MAX_POS - 1); | ||||
|               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); | ||||
|  | ||||
|               if (position_is_reachable(sx, sy) && position_is_reachable(ex, ey)) | ||||
|                 print_line_from_here_to_there(sx, sy, g26_layer_height, ex, ey, g26_layer_height); | ||||
|               if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y)) | ||||
|                 print_line_from_here_to_there(s, e); | ||||
|  | ||||
|               bitmap_set(vertical_mesh_line_flags, i, j); // Mark done, even if skipped | ||||
|               vertical_mesh_line_flags.mark(i, j); // Mark done, even if skipped | ||||
|             } | ||||
|           } | ||||
|         } | ||||
| @@ -436,19 +429,19 @@ inline bool prime_nozzle() { | ||||
|       ui.set_status_P(PSTR(MSG_G26_MANUAL_PRIME), 99); | ||||
|       ui.chirp(); | ||||
|  | ||||
|       set_destination_from_current(); | ||||
|       destination = current_position; | ||||
|  | ||||
|       recover_filament(destination); // Make sure G26 doesn't think the filament is retracted(). | ||||
|  | ||||
|       while (!ui.button_pressed()) { | ||||
|         ui.chirp(); | ||||
|         destination[E_AXIS] += 0.25; | ||||
|         destination.e += 0.25; | ||||
|         #if ENABLED(PREVENT_LENGTHY_EXTRUDE) | ||||
|           Total_Prime += 0.25; | ||||
|           if (Total_Prime >= EXTRUDE_MAXLENGTH) return G26_ERR; | ||||
|         #endif | ||||
|         prepare_internal_move_to_destination(fr_slow_e); | ||||
|         set_destination_from_current(); | ||||
|         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 | ||||
| @@ -468,10 +461,10 @@ inline bool prime_nozzle() { | ||||
|       ui.set_status_P(PSTR(MSG_G26_FIXED_LENGTH), 99); | ||||
|       ui.quick_feedback(); | ||||
|     #endif | ||||
|     set_destination_from_current(); | ||||
|     destination[E_AXIS] += g26_prime_length; | ||||
|     destination = current_position; | ||||
|     destination.e += g26_prime_length; | ||||
|     prepare_internal_move_to_destination(fr_slow_e); | ||||
|     set_destination_from_current(); | ||||
|     destination.e -= g26_prime_length; | ||||
|     retract_filament(destination); | ||||
|   } | ||||
|  | ||||
| @@ -630,9 +623,9 @@ void GcodeSuite::G26() { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   g26_x_pos = parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : current_position[X_AXIS]; | ||||
|   g26_y_pos = parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : current_position[Y_AXIS]; | ||||
|   if (!position_is_reachable(g26_x_pos, g26_y_pos)) { | ||||
|   g26_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_pos.x, g26_pos.y)) { | ||||
|     SERIAL_ECHOLNPGM("?Specified X,Y coordinate out of bounds."); | ||||
|     return; | ||||
|   } | ||||
| @@ -642,9 +635,9 @@ void GcodeSuite::G26() { | ||||
|    */ | ||||
|   set_bed_leveling_enabled(!parser.seen('D')); | ||||
|  | ||||
|   if (current_position[Z_AXIS] < Z_CLEARANCE_BETWEEN_PROBES) { | ||||
|   if (current_position.z < Z_CLEARANCE_BETWEEN_PROBES) { | ||||
|     do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES); | ||||
|     set_current_from_destination(); | ||||
|     current_position = destination; | ||||
|   } | ||||
|  | ||||
|   #if DISABLED(NO_VOLUMETRICS) | ||||
| @@ -655,7 +648,7 @@ void GcodeSuite::G26() { | ||||
|  | ||||
|   if (turn_on_heaters() != G26_OK) goto LEAVE; | ||||
|  | ||||
|   current_position[E_AXIS] = 0.0; | ||||
|   current_position.e = 0.0; | ||||
|   sync_plan_position_e(); | ||||
|  | ||||
|   if (g26_prime_flag && prime_nozzle() != G26_OK) goto LEAVE; | ||||
| @@ -670,13 +663,13 @@ void GcodeSuite::G26() { | ||||
|    *  It's  "Show Time" !!! | ||||
|    */ | ||||
|  | ||||
|   ZERO(circle_flags); | ||||
|   ZERO(horizontal_mesh_line_flags); | ||||
|   ZERO(vertical_mesh_line_flags); | ||||
|   circle_flags.reset(); | ||||
|   horizontal_mesh_line_flags.reset(); | ||||
|   vertical_mesh_line_flags.reset(); | ||||
|  | ||||
|   // Move nozzle to the specified height for the first layer | ||||
|   set_destination_from_current(); | ||||
|   destination[Z_AXIS] = g26_layer_height; | ||||
|   destination = current_position; | ||||
|   destination.z = g26_layer_height; | ||||
|   move_to(destination, 0.0); | ||||
|   move_to(destination, g26_ooze_amount); | ||||
|  | ||||
| @@ -706,71 +699,68 @@ void GcodeSuite::G26() { | ||||
|  | ||||
|   mesh_index_pair location; | ||||
|   do { | ||||
|      location = g26_continue_with_closest | ||||
|       ? find_closest_circle_to_print(current_position[X_AXIS], current_position[Y_AXIS]) | ||||
|       : find_closest_circle_to_print(g26_x_pos, g26_y_pos); // Find the closest Mesh Intersection to where we are now. | ||||
|     // Find the nearest confluence | ||||
|     location = find_closest_circle_to_print(g26_continue_with_closest ? xy_pos_t(current_position) : g26_pos); | ||||
|  | ||||
|     if (location.x_index >= 0 && location.y_index >= 0) { | ||||
|       const float circle_x = _GET_MESH_X(location.x_index), | ||||
|                   circle_y = _GET_MESH_Y(location.y_index); | ||||
|     if (location.valid()) { | ||||
|       const xy_pos_t circle = _GET_MESH_POS(location.pos); | ||||
|  | ||||
|       // If this mesh location is outside the printable_radius, skip it. | ||||
|       if (!position_is_reachable(circle_x, circle_y)) continue; | ||||
|       if (!position_is_reachable(circle)) continue; | ||||
|  | ||||
|       // Determine where to start and end the circle, | ||||
|       // which is always drawn counter-clockwise. | ||||
|       const uint8_t xi = location.x_index, yi = location.y_index; | ||||
|       const bool f = yi == 0, r = xi >= GRID_MAX_POINTS_X - 1, b = yi >= GRID_MAX_POINTS_Y - 1; | ||||
|       const xy_int8_t st = location; | ||||
|       const bool f = st.y == 0, | ||||
|                  r = st.x >= GRID_MAX_POINTS_X - 1, | ||||
|                  b = st.y >= GRID_MAX_POINTS_Y - 1; | ||||
|  | ||||
|       #if ENABLED(ARC_SUPPORT) | ||||
|  | ||||
|         #define ARC_LENGTH(quarters)  (INTERSECTION_CIRCLE_RADIUS * M_PI * (quarters) / 2) | ||||
|         #define INTERSECTION_CIRCLE_DIAM  ((INTERSECTION_CIRCLE_RADIUS) * 2) | ||||
|         float sx = circle_x + INTERSECTION_CIRCLE_RADIUS,   // default to full circle | ||||
|               ex = circle_x + INTERSECTION_CIRCLE_RADIUS, | ||||
|               sy = circle_y, ey = circle_y, | ||||
|               arc_length = ARC_LENGTH(4); | ||||
|  | ||||
|         xy_float_t e = { circle.x + INTERSECTION_CIRCLE_RADIUS, circle.y }; | ||||
|         xyz_float_t s = e; | ||||
|  | ||||
|         // Figure out where to start and end the arc - we always print counterclockwise | ||||
|         if (xi == 0) {                             // left edge | ||||
|           if (!f) { sx = circle_x; sy -= INTERSECTION_CIRCLE_RADIUS; } | ||||
|           if (!b) { ex = circle_x; ey += INTERSECTION_CIRCLE_RADIUS; } | ||||
|         float arc_length = ARC_LENGTH(4); | ||||
|         if (st.x == 0) {                             // left edge | ||||
|           if (!f) { s.x = circle.x; s.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); | ||||
|         } | ||||
|         else if (r) {                             // right edge | ||||
|           sx = b ? circle_x - (INTERSECTION_CIRCLE_RADIUS) : circle_x; | ||||
|           ex = f ? circle_x - (INTERSECTION_CIRCLE_RADIUS) : circle_x; | ||||
|           sy = b ? circle_y : circle_y + INTERSECTION_CIRCLE_RADIUS; | ||||
|           ey = f ? circle_y : circle_y - (INTERSECTION_CIRCLE_RADIUS); | ||||
|           if (b) s.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y); | ||||
|           else   s.set(circle.x, circle.y + INTERSECTION_CIRCLE_RADIUS); | ||||
|           if (f) e.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y); | ||||
|           else   e.set(circle.x, circle.y - (INTERSECTION_CIRCLE_RADIUS)); | ||||
|           arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2); | ||||
|         } | ||||
|         else if (f) { | ||||
|           ex -= INTERSECTION_CIRCLE_DIAM; | ||||
|           e.x -= INTERSECTION_CIRCLE_DIAM; | ||||
|           arc_length = ARC_LENGTH(2); | ||||
|         } | ||||
|         else if (b) { | ||||
|           sx -= INTERSECTION_CIRCLE_DIAM; | ||||
|           s.x -= INTERSECTION_CIRCLE_DIAM; | ||||
|           arc_length = ARC_LENGTH(2); | ||||
|         } | ||||
|  | ||||
|         const float arc_offset[2] = { circle_x - sx, circle_y - sy }, | ||||
|                     dx_s = current_position[X_AXIS] - sx,   // find our distance from the start of the actual circle | ||||
|                     dy_s = current_position[Y_AXIS] - sy, | ||||
|                     dist_start = HYPOT2(dx_s, dy_s), | ||||
|                     endpoint[XYZE] = { | ||||
|                       ex, ey, | ||||
|                       g26_layer_height, | ||||
|                       current_position[E_AXIS] + (arc_length * g26_e_axis_feedrate * g26_extrusion_multiplier) | ||||
|                     }; | ||||
|         const ab_float_t arc_offset = circle - s; | ||||
|         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) | ||||
|         }; | ||||
|  | ||||
|         if (dist_start > 2.0) { | ||||
|           retract_filament(destination); | ||||
|           //todo:  parameterize the bump height with a define | ||||
|           move_to(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] + 0.500, 0.0);  // Z bump to minimize scraping | ||||
|           move_to(sx, sy, g26_layer_height + 0.500, 0.0); // Get to the starting point with no extrusion while bumped | ||||
|           s.z = g26_layer_height + 0.5f; | ||||
|           retract_lift_move(s); | ||||
|         } | ||||
|  | ||||
|         move_to(sx, sy, g26_layer_height, 0.0); // Get to the starting point with no extrusion / un-Z bump | ||||
|         s.z = g26_layer_height; | ||||
|         move_to(s, 0.0);  // Get to the starting point with no extrusion / un-Z lift | ||||
|  | ||||
|         recover_filament(destination); | ||||
|  | ||||
| @@ -778,7 +768,7 @@ void GcodeSuite::G26() { | ||||
|         feedrate_mm_s = PLANNER_XY_FEEDRATE() * 0.1f; | ||||
|         plan_arc(endpoint, arc_offset, false);  // Draw a counter-clockwise arc | ||||
|         feedrate_mm_s = old_feedrate; | ||||
|         set_destination_from_current(); | ||||
|         destination = current_position; | ||||
|  | ||||
|         #if HAS_LCD_MENU | ||||
|           if (user_canceled()) goto LEAVE; // Check if the user wants to stop the Mesh Validation | ||||
| @@ -787,7 +777,7 @@ void GcodeSuite::G26() { | ||||
|       #else // !ARC_SUPPORT | ||||
|  | ||||
|         int8_t start_ind = -2, end_ind = 9; // Assume a full circle (from 5:00 to 5:00) | ||||
|         if (xi == 0) {                      // Left edge? Just right half. | ||||
|         if (st.x == 0) {                    // Left edge? Just right half. | ||||
|           start_ind = f ? 0 : -3;           //  03:00 to 12:00 for front-left | ||||
|           end_ind = b ? 0 : 2;              //  06:00 to 03:00 for back-left | ||||
|         } | ||||
| @@ -810,23 +800,21 @@ void GcodeSuite::G26() { | ||||
|             if (user_canceled()) goto LEAVE;          // Check if the user wants to stop the Mesh Validation | ||||
|           #endif | ||||
|  | ||||
|           float rx = circle_x + _COS(ind),            // For speed, these are now a lookup table entry | ||||
|                 ry = circle_y + _SIN(ind), | ||||
|                 xe = circle_x + _COS(ind + 1), | ||||
|                 ye = circle_y + _SIN(ind + 1); | ||||
|           xy_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. | ||||
|             if (!position_is_reachable(rx, ry) || !position_is_reachable(xe, ye)) continue; | ||||
|           #else                                               // not, we need to skip | ||||
|             LIMIT(rx, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops | ||||
|             LIMIT(ry, Y_MIN_POS + 1, Y_MAX_POS - 1); | ||||
|             LIMIT(xe, X_MIN_POS + 1, X_MAX_POS - 1); | ||||
|             LIMIT(ye, Y_MIN_POS + 1, Y_MAX_POS - 1); | ||||
|             if (!position_is_reachable(p) || !position_is_reachable(q)) continue; | ||||
|           #else | ||||
|             LIMIT(p.x, X_MIN_POS + 1, X_MAX_POS - 1); // Prevent hitting the endstops | ||||
|             LIMIT(p.y, Y_MIN_POS + 1, Y_MAX_POS - 1); | ||||
|             LIMIT(q.x, X_MIN_POS + 1, X_MAX_POS - 1); | ||||
|             LIMIT(q.y, Y_MIN_POS + 1, Y_MAX_POS - 1); | ||||
|           #endif | ||||
|  | ||||
|           print_line_from_here_to_there(rx, ry, g26_layer_height, xe, ye, g26_layer_height); | ||||
|           SERIAL_FLUSH();  // Prevent host M105 buffer overrun. | ||||
|           print_line_from_here_to_there(p, q); | ||||
|           SERIAL_FLUSH();   // Prevent host M105 buffer overrun. | ||||
|         } | ||||
|  | ||||
|       #endif // !ARC_SUPPORT | ||||
| @@ -836,19 +824,18 @@ void GcodeSuite::G26() { | ||||
|  | ||||
|     SERIAL_FLUSH(); // Prevent host M105 buffer overrun. | ||||
|  | ||||
|   } while (--g26_repeats && location.x_index >= 0 && location.y_index >= 0); | ||||
|   } while (--g26_repeats && location.valid()); | ||||
|  | ||||
|   LEAVE: | ||||
|   ui.set_status_P(PSTR(MSG_G26_LEAVING), -1); | ||||
|  | ||||
|   retract_filament(destination); | ||||
|   destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES; | ||||
|   destination.z = Z_CLEARANCE_BETWEEN_PROBES; | ||||
|  | ||||
|   move_to(destination, 0); // Raise the nozzle | ||||
|  | ||||
|   destination[X_AXIS] = g26_x_pos;                            // Move back to the starting position | ||||
|   destination[Y_AXIS] = g26_y_pos; | ||||
|   //destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;         // Keep the nozzle where it is | ||||
|   destination.set(g26_pos.x, g26_pos.y);                      // Move back to the starting position | ||||
|   //destination.z = Z_CLEARANCE_BETWEEN_PROBES;               // Keep the nozzle where it is | ||||
|  | ||||
|   move_to(destination, 0);                                    // Move back to the starting position | ||||
|  | ||||
|   | ||||
| @@ -27,6 +27,7 @@ | ||||
| #include "../gcode.h" | ||||
| #include "../../Marlin.h" // for IsRunning() | ||||
| #include "../../module/motion.h" | ||||
| #include "../../module/probe.h" // for probe_offset | ||||
| #include "../../feature/bedlevel/bedlevel.h" | ||||
|  | ||||
| /** | ||||
| @@ -44,15 +45,15 @@ void GcodeSuite::G42() { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     set_destination_from_current(); | ||||
|     destination = current_position; | ||||
|  | ||||
|     if (hasI) destination[X_AXIS] = _GET_MESH_X(ix); | ||||
|     if (hasJ) destination[Y_AXIS] = _GET_MESH_Y(iy); | ||||
|     if (hasI) destination.x = _GET_MESH_X(ix); | ||||
|     if (hasJ) destination.y = _GET_MESH_Y(iy); | ||||
|  | ||||
|     #if HAS_BED_PROBE | ||||
|       if (parser.boolval('P')) { | ||||
|         if (hasI) destination[X_AXIS] -= probe_offset[X_AXIS]; | ||||
|         if (hasJ) destination[Y_AXIS] -= probe_offset[Y_AXIS]; | ||||
|         if (hasI) destination.x -= probe_offset.x; | ||||
|         if (hasJ) destination.y -= probe_offset.y; | ||||
|       } | ||||
|     #endif | ||||
|  | ||||
|   | ||||
| @@ -66,10 +66,9 @@ void GcodeSuite::M420() { | ||||
|       #if ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|         const float x_min = probe_min_x(), x_max = probe_max_x(), | ||||
|                     y_min = probe_min_y(), y_max = probe_max_y(); | ||||
|         bilinear_start[X_AXIS] = x_min; | ||||
|         bilinear_start[Y_AXIS] = y_min; | ||||
|         bilinear_grid_spacing[X_AXIS] = (x_max - x_min) / (GRID_MAX_POINTS_X - 1); | ||||
|         bilinear_grid_spacing[Y_AXIS] = (y_max - y_min) / (GRID_MAX_POINTS_Y - 1); | ||||
|         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)); | ||||
|       #endif | ||||
|       for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) | ||||
|         for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) { | ||||
| @@ -91,7 +90,7 @@ void GcodeSuite::M420() { | ||||
|   // (Don't disable for just M420 or M420 V) | ||||
|   if (seen_S && !to_enable) set_bed_leveling_enabled(false); | ||||
|  | ||||
|   const float oldpos[] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] }; | ||||
|   xyz_pos_t oldpos = current_position; | ||||
|  | ||||
|   #if ENABLED(AUTO_BED_LEVELING_UBL) | ||||
|  | ||||
| @@ -251,7 +250,7 @@ void GcodeSuite::M420() { | ||||
|   #endif | ||||
|  | ||||
|   // Report change in position | ||||
|   if (memcmp(oldpos, current_position, sizeof(oldpos))) | ||||
|   if (oldpos != current_position) | ||||
|     report_current_position(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -61,15 +61,15 @@ | ||||
|  | ||||
| #if ABL_GRID | ||||
|   #if ENABLED(PROBE_Y_FIRST) | ||||
|     #define PR_OUTER_VAR xCount | ||||
|     #define PR_OUTER_END abl_grid_points_x | ||||
|     #define PR_INNER_VAR yCount | ||||
|     #define PR_INNER_END abl_grid_points_y | ||||
|     #define PR_OUTER_VAR meshCount.x | ||||
|     #define PR_OUTER_END abl_grid_points.x | ||||
|     #define PR_INNER_VAR meshCount.y | ||||
|     #define PR_INNER_END abl_grid_points.y | ||||
|   #else | ||||
|     #define PR_OUTER_VAR yCount | ||||
|     #define PR_OUTER_END abl_grid_points_y | ||||
|     #define PR_INNER_VAR xCount | ||||
|     #define PR_INNER_END abl_grid_points_x | ||||
|     #define PR_OUTER_VAR meshCount.y | ||||
|     #define PR_OUTER_END abl_grid_points.y | ||||
|     #define PR_INNER_VAR meshCount.x | ||||
|     #define PR_INNER_END abl_grid_points.x | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| @@ -210,7 +210,8 @@ G29_TYPE GcodeSuite::G29() { | ||||
|   #endif | ||||
|  | ||||
|   ABL_VAR int verbose_level; | ||||
|   ABL_VAR float xProbe, yProbe, measured_z; | ||||
|   ABL_VAR xy_pos_t probePos; | ||||
|   ABL_VAR float measured_z; | ||||
|   ABL_VAR bool dryrun, abl_should_enable; | ||||
|  | ||||
|   #if EITHER(PROBE_MANUALLY, AUTO_BED_LEVELING_LINEAR) | ||||
| @@ -224,20 +225,17 @@ G29_TYPE GcodeSuite::G29() { | ||||
|   #if ABL_GRID | ||||
|  | ||||
|     #if ENABLED(PROBE_MANUALLY) | ||||
|       ABL_VAR uint8_t PR_OUTER_VAR; | ||||
|       ABL_VAR  int8_t PR_INNER_VAR; | ||||
|       ABL_VAR xy_int8_t meshCount; | ||||
|     #endif | ||||
|  | ||||
|     ABL_VAR int left_probe_bed_position, right_probe_bed_position, front_probe_bed_position, back_probe_bed_position; | ||||
|     ABL_VAR float xGridSpacing = 0, yGridSpacing = 0; | ||||
|     ABL_VAR xy_int_t probe_position_lf, probe_position_rb; | ||||
|     ABL_VAR xy_float_t gridSpacing = { 0, 0 }; | ||||
|  | ||||
|     #if ENABLED(AUTO_BED_LEVELING_LINEAR) | ||||
|       ABL_VAR uint8_t abl_grid_points_x = GRID_MAX_POINTS_X, | ||||
|                       abl_grid_points_y = GRID_MAX_POINTS_Y; | ||||
|       ABL_VAR bool do_topography_map; | ||||
|       ABL_VAR xy_uint8_t abl_grid_points; | ||||
|     #else // Bilinear | ||||
|       uint8_t constexpr abl_grid_points_x = GRID_MAX_POINTS_X, | ||||
|                         abl_grid_points_y = GRID_MAX_POINTS_Y; | ||||
|       constexpr xy_uint8_t abl_grid_points = { GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y }; | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(AUTO_BED_LEVELING_LINEAR) | ||||
| @@ -269,15 +267,15 @@ G29_TYPE GcodeSuite::G29() { | ||||
|     const float x_min = probe_min_x(), x_max = probe_max_x(), y_min = probe_min_y(), y_max = probe_max_y(); | ||||
|  | ||||
|     ABL_VAR vector_3 points[3] = { | ||||
|     #if ENABLED(HAS_FIXED_3POINT) | ||||
|       vector_3(PROBE_PT_1_X, PROBE_PT_1_Y, 0), | ||||
|       vector_3(PROBE_PT_2_X, PROBE_PT_2_Y, 0), | ||||
|       vector_3(PROBE_PT_3_X, PROBE_PT_3_Y, 0) | ||||
|     #else | ||||
|       vector_3(x_min, y_min, 0), | ||||
|       vector_3(x_max, y_min, 0), | ||||
|       vector_3((x_max - x_min) / 2, y_max, 0) | ||||
|     #endif | ||||
|       #if ENABLED(HAS_FIXED_3POINT) | ||||
|         { PROBE_PT_1_X, PROBE_PT_1_Y, 0 }, | ||||
|         { PROBE_PT_2_X, PROBE_PT_2_Y, 0 }, | ||||
|         { PROBE_PT_3_X, PROBE_PT_3_Y, 0 } | ||||
|       #else | ||||
|         { x_min, y_min, 0 }, | ||||
|         { x_max, y_min, 0 }, | ||||
|         { (x_max - x_min) / 2, y_max, 0 } | ||||
|       #endif | ||||
|     }; | ||||
|  | ||||
|   #endif // AUTO_BED_LEVELING_3POINT | ||||
| @@ -311,7 +309,7 @@ G29_TYPE GcodeSuite::G29() { | ||||
|           G29_RETURN(false); | ||||
|         } | ||||
|  | ||||
|         const float rz = parser.seenval('Z') ? RAW_Z_POSITION(parser.value_linear_units()) : current_position[Z_AXIS]; | ||||
|         const float rz = parser.seenval('Z') ? RAW_Z_POSITION(parser.value_linear_units()) : current_position.z; | ||||
|         if (!WITHIN(rz, -10, 10)) { | ||||
|           SERIAL_ERROR_MSG("Bad Z value"); | ||||
|           G29_RETURN(false); | ||||
| @@ -323,8 +321,8 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|         if (!isnan(rx) && !isnan(ry)) { | ||||
|           // Get nearest i / j from rx / ry | ||||
|           i = (rx - bilinear_start[X_AXIS] + 0.5 * xGridSpacing) / xGridSpacing; | ||||
|           j = (ry - bilinear_start[Y_AXIS] + 0.5 * yGridSpacing) / yGridSpacing; | ||||
|           i = (rx - bilinear_start.x + 0.5 * gridSpacing.x) / gridSpacing.x; | ||||
|           j = (ry - bilinear_start.y + 0.5 * gridSpacing.y) / gridSpacing.y; | ||||
|           LIMIT(i, 0, GRID_MAX_POINTS_X - 1); | ||||
|           LIMIT(j, 0, GRID_MAX_POINTS_Y - 1); | ||||
|         } | ||||
| @@ -373,20 +371,22 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|       // X and Y specify points in each direction, overriding the default | ||||
|       // These values may be saved with the completed mesh | ||||
|       abl_grid_points_x = parser.intval('X', GRID_MAX_POINTS_X); | ||||
|       abl_grid_points_y = parser.intval('Y', GRID_MAX_POINTS_Y); | ||||
|       if (parser.seenval('P')) abl_grid_points_x = abl_grid_points_y = parser.value_int(); | ||||
|       abl_grid_points.set( | ||||
|         parser.byteval('X', GRID_MAX_POINTS_X), | ||||
|         parser.byteval('Y', GRID_MAX_POINTS_Y) | ||||
|       ); | ||||
|       if (parser.seenval('P')) abl_grid_points.x = abl_grid_points.y = parser.value_int(); | ||||
|  | ||||
|       if (!WITHIN(abl_grid_points_x, 2, GRID_MAX_POINTS_X)) { | ||||
|       if (!WITHIN(abl_grid_points.x, 2, GRID_MAX_POINTS_X)) { | ||||
|         SERIAL_ECHOLNPGM("?Probe points (X) implausible (2-" STRINGIFY(GRID_MAX_POINTS_X) ")."); | ||||
|         G29_RETURN(false); | ||||
|       } | ||||
|       if (!WITHIN(abl_grid_points_y, 2, GRID_MAX_POINTS_Y)) { | ||||
|       if (!WITHIN(abl_grid_points.y, 2, GRID_MAX_POINTS_Y)) { | ||||
|         SERIAL_ECHOLNPGM("?Probe points (Y) implausible (2-" STRINGIFY(GRID_MAX_POINTS_Y) ")."); | ||||
|         G29_RETURN(false); | ||||
|       } | ||||
|  | ||||
|       abl_points = abl_grid_points_x * abl_grid_points_y; | ||||
|       abl_points = abl_grid_points.x * abl_grid_points.y; | ||||
|       mean = 0; | ||||
|  | ||||
|     #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
| @@ -404,27 +404,35 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|       if (parser.seen('H')) { | ||||
|         const int16_t size = (int16_t)parser.value_linear_units(); | ||||
|         left_probe_bed_position  = _MAX(X_CENTER - size / 2, x_min); | ||||
|         right_probe_bed_position = _MIN(left_probe_bed_position + size, x_max); | ||||
|         front_probe_bed_position = _MAX(Y_CENTER - size / 2, y_min); | ||||
|         back_probe_bed_position  = _MIN(front_probe_bed_position + size, y_max); | ||||
|         probe_position_lf.set( | ||||
|           _MAX(X_CENTER - size / 2, x_min), | ||||
|           _MAX(Y_CENTER - size / 2, y_min) | ||||
|         ); | ||||
|         probe_position_rb.set( | ||||
|           _MIN(probe_position_lf.x + size, x_max), | ||||
|           _MIN(probe_position_lf.y + size, y_max) | ||||
|         ); | ||||
|       } | ||||
|       else { | ||||
|         left_probe_bed_position  = parser.seenval('L') ? (int)RAW_X_POSITION(parser.value_linear_units()) : _MAX(X_CENTER - X_BED_SIZE / 2, x_min); | ||||
|         right_probe_bed_position = parser.seenval('R') ? (int)RAW_X_POSITION(parser.value_linear_units()) : _MIN(left_probe_bed_position + X_BED_SIZE, x_max); | ||||
|         front_probe_bed_position = parser.seenval('F') ? (int)RAW_Y_POSITION(parser.value_linear_units()) : _MAX(Y_CENTER - Y_BED_SIZE / 2, y_min); | ||||
|         back_probe_bed_position  = parser.seenval('B') ? (int)RAW_Y_POSITION(parser.value_linear_units()) : _MIN(front_probe_bed_position + Y_BED_SIZE, y_max); | ||||
|         probe_position_lf.set( | ||||
|           parser.seenval('L') ? (int)RAW_X_POSITION(parser.value_linear_units()) : _MAX(X_CENTER - (X_BED_SIZE) / 2,      x_min), | ||||
|           parser.seenval('F') ? (int)RAW_Y_POSITION(parser.value_linear_units()) : _MAX(Y_CENTER - (Y_BED_SIZE) / 2,      y_min) | ||||
|         ); | ||||
|         probe_position_rb.set( | ||||
|           parser.seenval('R') ? (int)RAW_X_POSITION(parser.value_linear_units()) : _MIN(probe_position_lf.x + X_BED_SIZE, x_max), | ||||
|           parser.seenval('B') ? (int)RAW_Y_POSITION(parser.value_linear_units()) : _MIN(probe_position_lf.y + Y_BED_SIZE, y_max) | ||||
|         ); | ||||
|       } | ||||
|  | ||||
|       if ( | ||||
|         #if IS_SCARA || ENABLED(DELTA) | ||||
|              !position_is_reachable_by_probe(left_probe_bed_position, 0) | ||||
|           || !position_is_reachable_by_probe(right_probe_bed_position, 0) | ||||
|           || !position_is_reachable_by_probe(0, front_probe_bed_position) | ||||
|           || !position_is_reachable_by_probe(0, back_probe_bed_position) | ||||
|              !position_is_reachable_by_probe(probe_position_lf.x, 0) | ||||
|           || !position_is_reachable_by_probe(probe_position_rb.x, 0) | ||||
|           || !position_is_reachable_by_probe(0, probe_position_lf.y) | ||||
|           || !position_is_reachable_by_probe(0, probe_position_rb.y) | ||||
|         #else | ||||
|              !position_is_reachable_by_probe(left_probe_bed_position, front_probe_bed_position) | ||||
|           || !position_is_reachable_by_probe(right_probe_bed_position, back_probe_bed_position) | ||||
|              !position_is_reachable_by_probe(probe_position_lf) | ||||
|           || !position_is_reachable_by_probe(probe_position_rb) | ||||
|         #endif | ||||
|       ) { | ||||
|         SERIAL_ECHOLNPGM("? (L,R,F,B) out of bounds."); | ||||
| @@ -432,8 +440,8 @@ G29_TYPE GcodeSuite::G29() { | ||||
|       } | ||||
|  | ||||
|       // probe at the points of a lattice grid | ||||
|       xGridSpacing = (right_probe_bed_position - left_probe_bed_position) / (abl_grid_points_x - 1); | ||||
|       yGridSpacing = (back_probe_bed_position - front_probe_bed_position) / (abl_grid_points_y - 1); | ||||
|       gridSpacing.set((probe_position_rb.x - probe_position_lf.x) / (abl_grid_points.x - 1), | ||||
|                       (probe_position_rb.y - probe_position_lf.y) / (abl_grid_points.y - 1)); | ||||
|  | ||||
|     #endif // ABL_GRID | ||||
|  | ||||
| @@ -464,19 +472,13 @@ G29_TYPE GcodeSuite::G29() { | ||||
|       #if ENABLED(PROBE_MANUALLY) | ||||
|         if (!no_action) | ||||
|       #endif | ||||
|       if ( xGridSpacing != bilinear_grid_spacing[X_AXIS] | ||||
|         || yGridSpacing != bilinear_grid_spacing[Y_AXIS] | ||||
|         || left_probe_bed_position != bilinear_start[X_AXIS] | ||||
|         || front_probe_bed_position != bilinear_start[Y_AXIS] | ||||
|       ) { | ||||
|       if (gridSpacing != bilinear_grid_spacing || probe_position_lf != bilinear_start) { | ||||
|         // Reset grid to 0.0 or "not probed". (Also disables ABL) | ||||
|         reset_bed_level(); | ||||
|  | ||||
|         // Initialize a grid with the given dimensions | ||||
|         bilinear_grid_spacing[X_AXIS] = xGridSpacing; | ||||
|         bilinear_grid_spacing[Y_AXIS] = yGridSpacing; | ||||
|         bilinear_start[X_AXIS] = left_probe_bed_position; | ||||
|         bilinear_start[Y_AXIS] = front_probe_bed_position; | ||||
|         bilinear_grid_spacing = gridSpacing.asInt(); | ||||
|         bilinear_start = probe_position_lf; | ||||
|  | ||||
|         // Can't re-enable (on error) until the new grid is written | ||||
|         abl_should_enable = false; | ||||
| @@ -546,17 +548,17 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|       // For G29 after adjusting Z. | ||||
|       // Save the previous Z before going to the next point | ||||
|       measured_z = current_position[Z_AXIS]; | ||||
|       measured_z = current_position.z; | ||||
|  | ||||
|       #if ENABLED(AUTO_BED_LEVELING_LINEAR) | ||||
|  | ||||
|         mean += measured_z; | ||||
|         eqnBVector[index] = measured_z; | ||||
|         eqnAMatrix[index + 0 * abl_points] = xProbe; | ||||
|         eqnAMatrix[index + 1 * abl_points] = yProbe; | ||||
|         eqnAMatrix[index + 0 * abl_points] = probePos.x; | ||||
|         eqnAMatrix[index + 1 * abl_points] = probePos.y; | ||||
|         eqnAMatrix[index + 2 * abl_points] = 1; | ||||
|  | ||||
|         incremental_LSF(&lsf_results, xProbe, yProbe, measured_z); | ||||
|         incremental_LSF(&lsf_results, probePos, measured_z); | ||||
|  | ||||
|       #elif ENABLED(AUTO_BED_LEVELING_3POINT) | ||||
|  | ||||
| @@ -564,12 +566,13 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|       #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|  | ||||
|         z_values[xCount][yCount] = measured_z + zoffset; | ||||
|         const float newz = measured_z + zoffset; | ||||
|         z_values[meshCount.x][meshCount.y] = newz; | ||||
|         #if ENABLED(EXTENSIBLE_UI) | ||||
|           ExtUI::onMeshUpdate(xCount, yCount, z_values[xCount][yCount]); | ||||
|           ExtUI::onMeshUpdate(meshCount, newz); | ||||
|         #endif | ||||
|  | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Save X", xCount, " Y", yCount, " Z", measured_z + zoffset); | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Save X", meshCount.x, " Y", meshCount.y, " Z", measured_z + zoffset); | ||||
|  | ||||
|       #endif | ||||
|     } | ||||
| @@ -583,7 +586,7 @@ G29_TYPE GcodeSuite::G29() { | ||||
|       // Skip any unreachable points | ||||
|       while (abl_probe_index < abl_points) { | ||||
|  | ||||
|         // Set xCount, yCount based on abl_probe_index, with zig-zag | ||||
|         // Set meshCount.x, meshCount.y based on abl_probe_index, with zig-zag | ||||
|         PR_OUTER_VAR = abl_probe_index / PR_INNER_END; | ||||
|         PR_INNER_VAR = abl_probe_index - (PR_OUTER_VAR * PR_INNER_END); | ||||
|  | ||||
| @@ -592,24 +595,23 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|         if (zig) PR_INNER_VAR = (PR_INNER_END - 1) - PR_INNER_VAR; | ||||
|  | ||||
|         const float xBase = xCount * xGridSpacing + left_probe_bed_position, | ||||
|                     yBase = yCount * yGridSpacing + front_probe_bed_position; | ||||
|         const xy_pos_t base = probe_position_lf.asFloat() + gridSpacing * meshCount.asFloat(); | ||||
|  | ||||
|         xProbe = FLOOR(xBase + (xBase < 0 ? 0 : 0.5)); | ||||
|         yProbe = FLOOR(yBase + (yBase < 0 ? 0 : 0.5)); | ||||
|         probePos.set(FLOOR(base.x + (base.x < 0 ? 0 : 0.5)), | ||||
|                      FLOOR(base.y + (base.y < 0 ? 0 : 0.5))); | ||||
|  | ||||
|         #if ENABLED(AUTO_BED_LEVELING_LINEAR) | ||||
|           indexIntoAB[xCount][yCount] = abl_probe_index; | ||||
|           indexIntoAB[meshCount.x][meshCount.y] = abl_probe_index; | ||||
|         #endif | ||||
|  | ||||
|         // Keep looping till a reachable point is found | ||||
|         if (position_is_reachable(xProbe, yProbe)) break; | ||||
|         if (position_is_reachable(probePos)) break; | ||||
|         ++abl_probe_index; | ||||
|       } | ||||
|  | ||||
|       // Is there a next point to move to? | ||||
|       if (abl_probe_index < abl_points) { | ||||
|         _manual_goto_xy(xProbe, yProbe); // Can be used here too! | ||||
|         _manual_goto_xy(probePos); // Can be used here too! | ||||
|         #if HAS_SOFTWARE_ENDSTOPS | ||||
|           // Disable software endstops to allow manual adjustment | ||||
|           // If G29 is not completed, they will not be re-enabled | ||||
| @@ -633,9 +635,8 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|       // Probe at 3 arbitrary points | ||||
|       if (abl_probe_index < abl_points) { | ||||
|         xProbe = points[abl_probe_index].x; | ||||
|         yProbe = points[abl_probe_index].y; | ||||
|         _manual_goto_xy(xProbe, yProbe); | ||||
|         probePos = points[abl_probe_index]; | ||||
|         _manual_goto_xy(probePos); | ||||
|         #if HAS_SOFTWARE_ENDSTOPS | ||||
|           // Disable software endstops to allow manual adjustment | ||||
|           // If G29 is not completed, they will not be re-enabled | ||||
| @@ -654,11 +655,7 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|         if (!dryrun) { | ||||
|           vector_3 planeNormal = vector_3::cross(points[0] - points[1], points[2] - points[1]).get_normal(); | ||||
|           if (planeNormal.z < 0) { | ||||
|             planeNormal.x *= -1; | ||||
|             planeNormal.y *= -1; | ||||
|             planeNormal.z *= -1; | ||||
|           } | ||||
|           if (planeNormal.z < 0) planeNormal *= -1; | ||||
|           planner.bed_level_matrix = matrix_3x3::create_look_at(planeNormal); | ||||
|  | ||||
|           // Can't re-enable (on error) until the new grid is written | ||||
| @@ -681,8 +678,11 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|       measured_z = 0; | ||||
|  | ||||
|       xy_int8_t meshCount; | ||||
|  | ||||
|       // Outer loop is X with PROBE_Y_FIRST enabled | ||||
|       // Outer loop is Y with PROBE_Y_FIRST disabled | ||||
|       for (uint8_t PR_OUTER_VAR = 0; PR_OUTER_VAR < PR_OUTER_END && !isnan(measured_z); PR_OUTER_VAR++) { | ||||
|       for (PR_OUTER_VAR = 0; PR_OUTER_VAR < PR_OUTER_END && !isnan(measured_z); PR_OUTER_VAR++) { | ||||
|  | ||||
|         int8_t inStart, inStop, inInc; | ||||
|  | ||||
| @@ -703,21 +703,21 @@ G29_TYPE GcodeSuite::G29() { | ||||
|         uint8_t pt_index = (PR_OUTER_VAR) * (PR_INNER_END) + 1; | ||||
|  | ||||
|         // Inner loop is Y with PROBE_Y_FIRST enabled | ||||
|         for (int8_t PR_INNER_VAR = inStart; PR_INNER_VAR != inStop; pt_index++, PR_INNER_VAR += inInc) { | ||||
|         // Inner loop is X with PROBE_Y_FIRST disabled | ||||
|         for (PR_INNER_VAR = inStart; PR_INNER_VAR != inStop; pt_index++, PR_INNER_VAR += inInc) { | ||||
|  | ||||
|           const float xBase = left_probe_bed_position + xGridSpacing * xCount, | ||||
|                       yBase = front_probe_bed_position + yGridSpacing * yCount; | ||||
|           const xy_pos_t base = probe_position_lf.asFloat() + gridSpacing * meshCount.asFloat(); | ||||
|  | ||||
|           xProbe = FLOOR(xBase + (xBase < 0 ? 0 : 0.5)); | ||||
|           yProbe = FLOOR(yBase + (yBase < 0 ? 0 : 0.5)); | ||||
|           probePos.set(FLOOR(base.x + (base.x < 0 ? 0 : 0.5)), | ||||
|                        FLOOR(base.y + (base.y < 0 ? 0 : 0.5))); | ||||
|  | ||||
|           #if ENABLED(AUTO_BED_LEVELING_LINEAR) | ||||
|             indexIntoAB[xCount][yCount] = ++abl_probe_index; // 0... | ||||
|             indexIntoAB[meshCount.x][meshCount.y] = ++abl_probe_index; // 0... | ||||
|           #endif | ||||
|  | ||||
|           #if IS_KINEMATIC | ||||
|             // Avoid probing outside the round or hexagonal area | ||||
|             if (!position_is_reachable_by_probe(xProbe, yProbe)) continue; | ||||
|             if (!position_is_reachable_by_probe(probePos)) continue; | ||||
|           #endif | ||||
|  | ||||
|           if (verbose_level) SERIAL_ECHOLNPAIR("Probing mesh point ", int(pt_index), "/", int(GRID_MAX_POINTS), "."); | ||||
| @@ -725,7 +725,7 @@ G29_TYPE GcodeSuite::G29() { | ||||
|             ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), PSTR(MSG_PROBING_MESH), int(pt_index), int(GRID_MAX_POINTS)); | ||||
|           #endif | ||||
|  | ||||
|           measured_z = faux ? 0.001 * random(-100, 101) : probe_at_point(xProbe, yProbe, raise_after, verbose_level); | ||||
|           measured_z = faux ? 0.001 * random(-100, 101) : probe_at_point(probePos, raise_after, verbose_level); | ||||
|  | ||||
|           if (isnan(measured_z)) { | ||||
|             set_bed_leveling_enabled(abl_should_enable); | ||||
| @@ -736,17 +736,17 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|             mean += measured_z; | ||||
|             eqnBVector[abl_probe_index] = measured_z; | ||||
|             eqnAMatrix[abl_probe_index + 0 * abl_points] = xProbe; | ||||
|             eqnAMatrix[abl_probe_index + 1 * abl_points] = yProbe; | ||||
|             eqnAMatrix[abl_probe_index + 0 * abl_points] = probePos.x; | ||||
|             eqnAMatrix[abl_probe_index + 1 * abl_points] = probePos.y; | ||||
|             eqnAMatrix[abl_probe_index + 2 * abl_points] = 1; | ||||
|  | ||||
|             incremental_LSF(&lsf_results, xProbe, yProbe, measured_z); | ||||
|             incremental_LSF(&lsf_results, probePos, measured_z); | ||||
|  | ||||
|           #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|  | ||||
|             z_values[xCount][yCount] = measured_z + zoffset; | ||||
|             z_values[meshCount.x][meshCount.y] = measured_z + zoffset; | ||||
|             #if ENABLED(EXTENSIBLE_UI) | ||||
|               ExtUI::onMeshUpdate(xCount, yCount, z_values[xCount][yCount]); | ||||
|               ExtUI::onMeshUpdate(meshCount.x, meshCount.y, z_values[meshCount.x][meshCount.y]); | ||||
|             #endif | ||||
|  | ||||
|           #endif | ||||
| @@ -768,9 +768,8 @@ G29_TYPE GcodeSuite::G29() { | ||||
|         #endif | ||||
|  | ||||
|         // Retain the last probe position | ||||
|         xProbe = points[i].x; | ||||
|         yProbe = points[i].y; | ||||
|         measured_z = faux ? 0.001 * random(-100, 101) : probe_at_point(xProbe, yProbe, raise_after, verbose_level); | ||||
|         probePos = points[i]; | ||||
|         measured_z = faux ? 0.001 * random(-100, 101) : probe_at_point(probePos, raise_after, verbose_level); | ||||
|         if (isnan(measured_z)) { | ||||
|           set_bed_leveling_enabled(abl_should_enable); | ||||
|           break; | ||||
| @@ -845,19 +844,19 @@ G29_TYPE GcodeSuite::G29() { | ||||
|        * plane equation in the standard form, which is Vx*x+Vy*y+Vz*z+d = 0 | ||||
|        * so Vx = -a Vy = -b Vz = 1 (we want the vector facing towards positive Z | ||||
|        */ | ||||
|       float plane_equation_coefficients[3]; | ||||
|       struct { float a, b, d; } plane_equation_coefficients; | ||||
|  | ||||
|       finish_incremental_LSF(&lsf_results); | ||||
|       plane_equation_coefficients[0] = -lsf_results.A;  // We should be able to eliminate the '-' on these three lines and down below | ||||
|       plane_equation_coefficients[1] = -lsf_results.B;  // but that is not yet tested. | ||||
|       plane_equation_coefficients[2] = -lsf_results.D; | ||||
|       plane_equation_coefficients.a = -lsf_results.A;  // We should be able to eliminate the '-' on these three lines and down below | ||||
|       plane_equation_coefficients.b = -lsf_results.B;  // but that is not yet tested. | ||||
|       plane_equation_coefficients.d = -lsf_results.D; | ||||
|  | ||||
|       mean /= abl_points; | ||||
|  | ||||
|       if (verbose_level) { | ||||
|         SERIAL_ECHOPAIR_F("Eqn coefficients: a: ", plane_equation_coefficients[0], 8); | ||||
|         SERIAL_ECHOPAIR_F(" b: ", plane_equation_coefficients[1], 8); | ||||
|         SERIAL_ECHOPAIR_F(" d: ", plane_equation_coefficients[2], 8); | ||||
|         SERIAL_ECHOPAIR_F("Eqn coefficients: a: ", plane_equation_coefficients.a, 8); | ||||
|         SERIAL_ECHOPAIR_F(" b: ", plane_equation_coefficients.b, 8); | ||||
|         SERIAL_ECHOPAIR_F(" d: ", plane_equation_coefficients.d, 8); | ||||
|         if (verbose_level > 2) | ||||
|           SERIAL_ECHOPAIR_F("\nMean of sampled points: ", mean, 8); | ||||
|         SERIAL_EOL(); | ||||
| @@ -866,13 +865,34 @@ G29_TYPE GcodeSuite::G29() { | ||||
|       // Create the matrix but don't correct the position yet | ||||
|       if (!dryrun) | ||||
|         planner.bed_level_matrix = matrix_3x3::create_look_at( | ||||
|           vector_3(-plane_equation_coefficients[0], -plane_equation_coefficients[1], 1)    // We can eliminate the '-' here and up above | ||||
|           vector_3(-plane_equation_coefficients.a, -plane_equation_coefficients.b, 1)    // We can eliminate the '-' here and up above | ||||
|         ); | ||||
|  | ||||
|       // Show the Topography map if enabled | ||||
|       if (do_topography_map) { | ||||
|  | ||||
|         SERIAL_ECHOLNPGM("\nBed Height Topography:\n" | ||||
|         float min_diff = 999; | ||||
|  | ||||
|         auto print_topo_map = [&](PGM_P const title, const bool get_min) { | ||||
|           serialprintPGM(title); | ||||
|           for (int8_t yy = abl_grid_points.y - 1; yy >= 0; yy--) { | ||||
|             for (uint8_t xx = 0; xx < abl_grid_points.x; xx++) { | ||||
|               const int ind = indexIntoAB[xx][yy]; | ||||
|               xyz_float_t tmp = { eqnAMatrix[ind + 0 * abl_points], | ||||
|                                   eqnAMatrix[ind + 1 * abl_points], 0 }; | ||||
|               apply_rotation_xyz(planner.bed_level_matrix, tmp); | ||||
|               if (get_min) NOMORE(min_diff, eqnBVector[ind] - tmp.z); | ||||
|               const float subval = get_min ? mean : tmp.z + min_diff, | ||||
|                             diff = eqnBVector[ind] - subval; | ||||
|               SERIAL_CHAR(' '); if (diff >= 0.0) SERIAL_CHAR('+');   // Include + for column alignment | ||||
|               SERIAL_ECHO_F(diff, 5); | ||||
|             } // xx | ||||
|             SERIAL_EOL(); | ||||
|           } // yy | ||||
|           SERIAL_EOL(); | ||||
|         }; | ||||
|  | ||||
|         print_topo_map(PSTR("\nBed Height Topography:\n" | ||||
|                                "   +--- BACK --+\n" | ||||
|                                "   |           |\n" | ||||
|                                " L |    (+)    | R\n" | ||||
| @@ -882,56 +902,10 @@ G29_TYPE GcodeSuite::G29() { | ||||
|                                "   |    (-)    | T\n" | ||||
|                                "   |           |\n" | ||||
|                                "   O-- FRONT --+\n" | ||||
|                                " (0,0)"); | ||||
|                                " (0,0)\n"), true); | ||||
|         if (verbose_level > 3) | ||||
|           print_topo_map(PSTR("\nCorrected Bed Height vs. Bed Topology:\n"), false); | ||||
|  | ||||
|         float min_diff = 999; | ||||
|  | ||||
|         for (int8_t yy = abl_grid_points_y - 1; yy >= 0; yy--) { | ||||
|           for (uint8_t xx = 0; xx < abl_grid_points_x; xx++) { | ||||
|             int ind = indexIntoAB[xx][yy]; | ||||
|             float diff = eqnBVector[ind] - mean, | ||||
|                   x_tmp = eqnAMatrix[ind + 0 * abl_points], | ||||
|                   y_tmp = eqnAMatrix[ind + 1 * abl_points], | ||||
|                   z_tmp = 0; | ||||
|  | ||||
|             apply_rotation_xyz(planner.bed_level_matrix, x_tmp, y_tmp, z_tmp); | ||||
|  | ||||
|             NOMORE(min_diff, eqnBVector[ind] - z_tmp); | ||||
|  | ||||
|             if (diff >= 0.0) | ||||
|               SERIAL_ECHOPGM(" +");   // Include + for column alignment | ||||
|             else | ||||
|               SERIAL_CHAR(' '); | ||||
|             SERIAL_ECHO_F(diff, 5); | ||||
|           } // xx | ||||
|           SERIAL_EOL(); | ||||
|         } // yy | ||||
|         SERIAL_EOL(); | ||||
|  | ||||
|         if (verbose_level > 3) { | ||||
|           SERIAL_ECHOLNPGM("\nCorrected Bed Height vs. Bed Topology:"); | ||||
|  | ||||
|           for (int8_t yy = abl_grid_points_y - 1; yy >= 0; yy--) { | ||||
|             for (uint8_t xx = 0; xx < abl_grid_points_x; xx++) { | ||||
|               int ind = indexIntoAB[xx][yy]; | ||||
|               float x_tmp = eqnAMatrix[ind + 0 * abl_points], | ||||
|                     y_tmp = eqnAMatrix[ind + 1 * abl_points], | ||||
|                     z_tmp = 0; | ||||
|  | ||||
|               apply_rotation_xyz(planner.bed_level_matrix, x_tmp, y_tmp, z_tmp); | ||||
|  | ||||
|               float diff = eqnBVector[ind] - z_tmp - min_diff; | ||||
|               if (diff >= 0.0) | ||||
|                 SERIAL_ECHOPGM(" +"); | ||||
|               // Include + for column alignment | ||||
|               else | ||||
|                 SERIAL_CHAR(' '); | ||||
|               SERIAL_ECHO_F(diff, 5); | ||||
|             } // xx | ||||
|             SERIAL_EOL(); | ||||
|           } // yy | ||||
|           SERIAL_EOL(); | ||||
|         } | ||||
|       } //do_topography_map | ||||
|  | ||||
|     #endif // AUTO_BED_LEVELING_LINEAR | ||||
| @@ -950,24 +924,20 @@ G29_TYPE GcodeSuite::G29() { | ||||
|  | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_POS("G29 uncorrected XYZ", current_position); | ||||
|  | ||||
|         float converted[XYZ]; | ||||
|         COPY(converted, current_position); | ||||
|  | ||||
|         planner.leveling_active = true; | ||||
|         planner.unapply_leveling(converted); // use conversion machinery | ||||
|         planner.leveling_active = false; | ||||
|         xyze_pos_t converted = current_position; | ||||
|         planner.force_unapply_leveling(converted); // use conversion machinery | ||||
|  | ||||
|         // Use the last measured distance to the bed, if possible | ||||
|         if ( NEAR(current_position[X_AXIS], xProbe - probe_offset[X_AXIS]) | ||||
|           && NEAR(current_position[Y_AXIS], yProbe - probe_offset[Y_AXIS]) | ||||
|         if ( NEAR(current_position.x, probePos.x - probe_offset.x) | ||||
|           && NEAR(current_position.y, probePos.y - probe_offset.y) | ||||
|         ) { | ||||
|           const float simple_z = current_position[Z_AXIS] - measured_z; | ||||
|           if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Probed Z", simple_z, "  Matrix Z", converted[Z_AXIS], "  Discrepancy ", simple_z - converted[Z_AXIS]); | ||||
|           converted[Z_AXIS] = simple_z; | ||||
|           const float simple_z = current_position.z - measured_z; | ||||
|           if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Probed Z", simple_z, "  Matrix Z", converted.z, "  Discrepancy ", simple_z - converted.z); | ||||
|           converted.z = simple_z; | ||||
|         } | ||||
|  | ||||
|         // The rotated XY and corrected Z are now current_position | ||||
|         COPY(current_position, converted); | ||||
|         current_position = converted; | ||||
|  | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_POS("G29 corrected XYZ", current_position); | ||||
|       } | ||||
| @@ -975,13 +945,13 @@ G29_TYPE GcodeSuite::G29() { | ||||
|     #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) | ||||
|  | ||||
|       if (!dryrun) { | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("G29 uncorrected Z:", current_position[Z_AXIS]); | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("G29 uncorrected Z:", current_position.z); | ||||
|  | ||||
|         // Unapply the offset because it is going to be immediately applied | ||||
|         // and cause compensation movement in Z | ||||
|         current_position[Z_AXIS] -= bilinear_z_offset(current_position); | ||||
|         current_position.z -= bilinear_z_offset(current_position); | ||||
|  | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(" corrected Z:", current_position[Z_AXIS]); | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(" corrected Z:", current_position.z); | ||||
|       } | ||||
|  | ||||
|     #endif // ABL_PLANAR | ||||
|   | ||||
| @@ -110,7 +110,7 @@ void GcodeSuite::G29() { | ||||
|       } | ||||
|       else { | ||||
|         // Save Z for the previous mesh position | ||||
|         mbl.set_zigzag_z(mbl_probe_index - 1, current_position[Z_AXIS]); | ||||
|         mbl.set_zigzag_z(mbl_probe_index - 1, current_position.z); | ||||
|         #if HAS_SOFTWARE_ENDSTOPS | ||||
|           soft_endstops_enabled = saved_soft_endstops_state; | ||||
|         #endif | ||||
| @@ -124,11 +124,11 @@ void GcodeSuite::G29() { | ||||
|         #endif | ||||
|  | ||||
|         mbl.zigzag(mbl_probe_index++, ix, iy); | ||||
|         _manual_goto_xy(mbl.index_to_xpos[ix], mbl.index_to_ypos[iy]); | ||||
|         _manual_goto_xy({ mbl.index_to_xpos[ix], mbl.index_to_ypos[iy] }); | ||||
|       } | ||||
|       else { | ||||
|         // One last "return to the bed" (as originally coded) at completion | ||||
|         current_position[Z_AXIS] = MANUAL_PROBE_HEIGHT; | ||||
|         current_position.z = MANUAL_PROBE_HEIGHT; | ||||
|         line_to_current_position(); | ||||
|         planner.synchronize(); | ||||
|  | ||||
| @@ -142,7 +142,7 @@ void GcodeSuite::G29() { | ||||
|         set_bed_leveling_enabled(true); | ||||
|  | ||||
|         #if ENABLED(MESH_G28_REST_ORIGIN) | ||||
|           current_position[Z_AXIS] = 0; | ||||
|           current_position.z = 0; | ||||
|           line_to_current_position(homing_feedrate(Z_AXIS)); | ||||
|           planner.synchronize(); | ||||
|         #endif | ||||
|   | ||||
| @@ -46,28 +46,25 @@ | ||||
|  *   M421 C Q<offset> | ||||
|  */ | ||||
| void GcodeSuite::M421() { | ||||
|   int8_t ix = parser.intval('I', -1), iy = parser.intval('J', -1); | ||||
|   const bool hasI = ix >= 0, | ||||
|              hasJ = iy >= 0, | ||||
|   xy_int8_t ij = { int8_t(parser.intval('I', -1)), int8_t(parser.intval('J', -1)) }; | ||||
|   const bool hasI = ij.x >= 0, | ||||
|              hasJ = ij.y >= 0, | ||||
|              hasC = parser.seen('C'), | ||||
|              hasN = parser.seen('N'), | ||||
|              hasZ = parser.seen('Z'), | ||||
|              hasQ = !hasZ && parser.seen('Q'); | ||||
|  | ||||
|   if (hasC) { | ||||
|     const mesh_index_pair location = ubl.find_closest_mesh_point_of_type(REAL, current_position[X_AXIS], current_position[Y_AXIS], USE_NOZZLE_AS_REFERENCE, nullptr); | ||||
|     ix = location.x_index; | ||||
|     iy = location.y_index; | ||||
|   } | ||||
|   if (hasC) ij = ubl.find_closest_mesh_point_of_type(REAL, current_position); | ||||
|  | ||||
|   if (int(hasC) + int(hasI && hasJ) != 1 || !(hasZ || hasQ || hasN)) | ||||
|     SERIAL_ERROR_MSG(MSG_ERR_M421_PARAMETERS); | ||||
|   else if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1)) | ||||
|   else if (!WITHIN(ij.x, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(ij.y, 0, GRID_MAX_POINTS_Y - 1)) | ||||
|     SERIAL_ERROR_MSG(MSG_ERR_MESH_XY); | ||||
|   else { | ||||
|     ubl.z_values[ix][iy] = hasN ? NAN : parser.value_linear_units() + (hasQ ? ubl.z_values[ix][iy] : 0); | ||||
|     float &zval = ubl.z_values[ij.x][ij.y]; | ||||
|     zval = hasN ? NAN : parser.value_linear_units() + (hasQ ? zval : 0); | ||||
|     #if ENABLED(EXTENSIBLE_UI) | ||||
|       ExtUI::onMeshUpdate(ix, iy, ubl.z_values[ix][iy]); | ||||
|       ExtUI::onMeshUpdate(ij.x, ij.y, zval); | ||||
|     #endif | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -59,7 +59,7 @@ | ||||
|   static void quick_home_xy() { | ||||
|  | ||||
|     // Pretend the current position is 0,0 | ||||
|     current_position[X_AXIS] = current_position[Y_AXIS] = 0.0; | ||||
|     current_position.set(0.0, 0.0); | ||||
|     sync_plan_position(); | ||||
|  | ||||
|     const int x_axis_home_dir = | ||||
| @@ -95,7 +95,7 @@ | ||||
|  | ||||
|     endstops.validate_homing_move(); | ||||
|  | ||||
|     current_position[X_AXIS] = current_position[Y_AXIS] = 0.0; | ||||
|     current_position.set(0.0, 0.0); | ||||
|  | ||||
|     #if ENABLED(SENSORLESS_HOMING) | ||||
|       tmc_disable_stallguard(stepperX, stealth_states.x); | ||||
| @@ -128,17 +128,15 @@ | ||||
|  | ||||
|     /** | ||||
|      * Move the Z probe (or just the nozzle) to the safe homing point | ||||
|      * (Z is already at the right height) | ||||
|      */ | ||||
|     destination[X_AXIS] = Z_SAFE_HOMING_X_POINT; | ||||
|     destination[Y_AXIS] = Z_SAFE_HOMING_Y_POINT; | ||||
|     destination[Z_AXIS] = current_position[Z_AXIS]; // Z is already at the right height | ||||
|     destination.set(safe_homing_xy, current_position.z); | ||||
|  | ||||
|     #if HOMING_Z_WITH_PROBE | ||||
|       destination[X_AXIS] -= probe_offset[X_AXIS]; | ||||
|       destination[Y_AXIS] -= probe_offset[Y_AXIS]; | ||||
|       destination -= probe_offset; | ||||
|     #endif | ||||
|  | ||||
|     if (position_is_reachable(destination[X_AXIS], destination[Y_AXIS])) { | ||||
|     if (position_is_reachable(destination)) { | ||||
|  | ||||
|       if (DEBUGGING(LEVELING)) DEBUG_POS("home_z_safely", destination); | ||||
|  | ||||
| @@ -151,7 +149,7 @@ | ||||
|         safe_delay(500); // Short delay needed to settle | ||||
|       #endif | ||||
|  | ||||
|       do_blocking_move_to_xy(destination[X_AXIS], destination[Y_AXIS]); | ||||
|       do_blocking_move_to_xy(destination); | ||||
|       homeaxis(Z_AXIS); | ||||
|     } | ||||
|     else { | ||||
| @@ -232,16 +230,14 @@ void GcodeSuite::G28(const bool always_home_all) { | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(IMPROVE_HOMING_RELIABILITY) | ||||
|     slow_homing_t slow_homing { 0 }; | ||||
|     slow_homing.acceleration.x = planner.settings.max_acceleration_mm_per_s2[X_AXIS]; | ||||
|     slow_homing.acceleration.y = planner.settings.max_acceleration_mm_per_s2[Y_AXIS]; | ||||
|     slow_homing_t slow_homing{0}; | ||||
|     slow_homing.acceleration.set(planner.settings.max_acceleration_mm_per_s2[X_AXIS]; | ||||
|                                  planner.settings.max_acceleration_mm_per_s2[Y_AXIS]); | ||||
|     planner.settings.max_acceleration_mm_per_s2[X_AXIS] = 100; | ||||
|     planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = 100; | ||||
|     #if HAS_CLASSIC_JERK | ||||
|       slow_homing.jerk.x = planner.max_jerk[X_AXIS]; | ||||
|       slow_homing.jerk.y = planner.max_jerk[Y_AXIS]; | ||||
|       planner.max_jerk[X_AXIS] = 0; | ||||
|       planner.max_jerk[Y_AXIS] = 0; | ||||
|       slow_homing.jerk_xy = planner.max_jerk; | ||||
|       planner.max_jerk.set(0, 0); | ||||
|     #endif | ||||
|  | ||||
|     planner.reset_acceleration_rates(); | ||||
| @@ -274,7 +270,7 @@ void GcodeSuite::G28(const bool always_home_all) { | ||||
|                home_all = always_home_all || (homeX == homeY && homeX == homeZ), | ||||
|                doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ; | ||||
|  | ||||
|     set_destination_from_current(); | ||||
|     destination = current_position; | ||||
|  | ||||
|     #if Z_HOME_DIR > 0  // If homing away from BED do Z first | ||||
|  | ||||
| @@ -291,10 +287,10 @@ void GcodeSuite::G28(const bool always_home_all) { | ||||
|  | ||||
|     if (z_homing_height && (doX || doY)) { | ||||
|       // Raise Z before homing any other axes and z is not already high enough (never lower z) | ||||
|       destination[Z_AXIS] = z_homing_height; | ||||
|       if (destination[Z_AXIS] > current_position[Z_AXIS]) { | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) to ", destination[Z_AXIS]); | ||||
|         do_blocking_move_to_z(destination[Z_AXIS]); | ||||
|       destination.z = z_homing_height; | ||||
|       if (destination.z > current_position.z) { | ||||
|         if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) to ", destination.z); | ||||
|         do_blocking_move_to_z(destination.z); | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @@ -329,14 +325,14 @@ void GcodeSuite::G28(const bool always_home_all) { | ||||
|         homeaxis(X_AXIS); | ||||
|  | ||||
|         // Remember this extruder's position for later tool change | ||||
|         inactive_extruder_x_pos = current_position[X_AXIS]; | ||||
|         inactive_extruder_x_pos = current_position.x; | ||||
|  | ||||
|         // Home the 1st (left) extruder | ||||
|         active_extruder = 0; | ||||
|         homeaxis(X_AXIS); | ||||
|  | ||||
|         // Consider the active extruder to be parked | ||||
|         COPY(raised_parked_position, current_position); | ||||
|         raised_parked_position = current_position; | ||||
|         delayed_move_time = 0; | ||||
|         active_extruder_parked = true; | ||||
|  | ||||
| @@ -390,14 +386,14 @@ void GcodeSuite::G28(const bool always_home_all) { | ||||
|       homeaxis(X_AXIS); | ||||
|  | ||||
|       // Remember this extruder's position for later tool change | ||||
|       inactive_extruder_x_pos = current_position[X_AXIS]; | ||||
|       inactive_extruder_x_pos = current_position.x; | ||||
|  | ||||
|       // Home the 1st (left) extruder | ||||
|       active_extruder = 0; | ||||
|       homeaxis(X_AXIS); | ||||
|  | ||||
|       // Consider the active extruder to be parked | ||||
|       COPY(raised_parked_position, current_position); | ||||
|       raised_parked_position = current_position; | ||||
|       delayed_move_time = 0; | ||||
|       active_extruder_parked = true; | ||||
|       extruder_duplication_enabled = IDEX_saved_duplication_state; | ||||
| @@ -441,10 +437,8 @@ void GcodeSuite::G28(const bool always_home_all) { | ||||
|     planner.settings.max_acceleration_mm_per_s2[X_AXIS] = slow_homing.acceleration.x; | ||||
|     planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = slow_homing.acceleration.y; | ||||
|     #if HAS_CLASSIC_JERK | ||||
|       planner.max_jerk[X_AXIS] = slow_homing.jerk.x; | ||||
|       planner.max_jerk[Y_AXIS] = slow_homing.jerk.y; | ||||
|       planner.max_jerk = slow_homing.jerk_xy; | ||||
|     #endif | ||||
|  | ||||
|     planner.reset_acceleration_rates(); | ||||
|   #endif | ||||
|  | ||||
|   | ||||
| @@ -70,7 +70,7 @@ enum CalEnum : char {                        // the 7 main calibration points - | ||||
|   #define AC_CLEANUP() ac_cleanup() | ||||
| #endif | ||||
|  | ||||
| float lcd_probe_pt(const float &rx, const float &ry); | ||||
| float lcd_probe_pt(const xy_pos_t &xy); | ||||
|  | ||||
| void ac_home() { | ||||
|   endstops.enable(true); | ||||
| @@ -122,9 +122,9 @@ void print_signed_float(PGM_P const prefix, const float &f) { | ||||
| static void print_calibration_settings(const bool end_stops, const bool tower_angles) { | ||||
|   SERIAL_ECHOPAIR(".Height:", delta_height); | ||||
|   if (end_stops) { | ||||
|     print_signed_float(PSTR("Ex"), delta_endstop_adj[A_AXIS]); | ||||
|     print_signed_float(PSTR("Ey"), delta_endstop_adj[B_AXIS]); | ||||
|     print_signed_float(PSTR("Ez"), delta_endstop_adj[C_AXIS]); | ||||
|     print_signed_float(PSTR("Ex"), delta_endstop_adj.a); | ||||
|     print_signed_float(PSTR("Ey"), delta_endstop_adj.b); | ||||
|     print_signed_float(PSTR("Ez"), delta_endstop_adj.c); | ||||
|   } | ||||
|   if (end_stops && tower_angles) { | ||||
|     SERIAL_ECHOPAIR("  Radius:", delta_radius); | ||||
| @@ -133,9 +133,9 @@ static void print_calibration_settings(const bool end_stops, const bool tower_an | ||||
|     SERIAL_ECHO_SP(13); | ||||
|   } | ||||
|   if (tower_angles) { | ||||
|     print_signed_float(PSTR("Tx"), delta_tower_angle_trim[A_AXIS]); | ||||
|     print_signed_float(PSTR("Ty"), delta_tower_angle_trim[B_AXIS]); | ||||
|     print_signed_float(PSTR("Tz"), delta_tower_angle_trim[C_AXIS]); | ||||
|     print_signed_float(PSTR("Tx"), delta_tower_angle_trim.a); | ||||
|     print_signed_float(PSTR("Ty"), delta_tower_angle_trim.b); | ||||
|     print_signed_float(PSTR("Tz"), delta_tower_angle_trim.c); | ||||
|   } | ||||
|   if ((!end_stops && tower_angles) || (end_stops && !tower_angles)) { // XOR | ||||
|     SERIAL_ECHOPAIR("  Radius:", delta_radius); | ||||
| @@ -188,12 +188,12 @@ static float std_dev_points(float z_pt[NPP + 1], const bool _0p_cal, const bool | ||||
| /** | ||||
|  *  - Probe a point | ||||
|  */ | ||||
| static float calibration_probe(const float &nx, const float &ny, const bool stow) { | ||||
| static float calibration_probe(const xy_pos_t &xy, const bool stow) { | ||||
|   #if HAS_BED_PROBE | ||||
|     return probe_at_point(nx, ny, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, false); | ||||
|     return probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, false); | ||||
|   #else | ||||
|     UNUSED(stow); | ||||
|     return lcd_probe_pt(nx, ny); | ||||
|     return lcd_probe_pt(xy); | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| @@ -223,7 +223,8 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi | ||||
|   if (!_0p_calibration) { | ||||
|  | ||||
|     if (!_7p_no_intermediates && !_7p_4_intermediates && !_7p_11_intermediates) { // probe the center | ||||
|       z_pt[CEN] += calibration_probe(0, 0, stow_after_each); | ||||
|       const xy_pos_t center{0}; | ||||
|       z_pt[CEN] += calibration_probe(center, stow_after_each); | ||||
|       if (isnan(z_pt[CEN])) return false; | ||||
|     } | ||||
|  | ||||
| @@ -233,7 +234,8 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi | ||||
|       I_LOOP_CAL_PT(rad, start, steps) { | ||||
|         const float a = RADIANS(210 + (360 / NPP) *  (rad - 1)), | ||||
|                     r = delta_calibration_radius * 0.1; | ||||
|         z_pt[CEN] += calibration_probe(cos(a) * r, sin(a) * r, stow_after_each); | ||||
|         const xy_pos_t vec = { cos(a), sin(a) }; | ||||
|         z_pt[CEN] += calibration_probe(vec * r, stow_after_each); | ||||
|         if (isnan(z_pt[CEN])) return false; | ||||
|      } | ||||
|       z_pt[CEN] /= float(_7p_2_intermediates ? 7 : probe_points); | ||||
| @@ -257,7 +259,8 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi | ||||
|           const float a = RADIANS(210 + (360 / NPP) *  (rad - 1)), | ||||
|                       r = delta_calibration_radius * (1 - 0.1 * (zig_zag ? offset - circle : circle)), | ||||
|                       interpol = FMOD(rad, 1); | ||||
|           const float z_temp = calibration_probe(cos(a) * r, sin(a) * r, stow_after_each); | ||||
|           const xy_pos_t vec = { cos(a), sin(a) }; | ||||
|           const float z_temp = calibration_probe(vec * r, stow_after_each); | ||||
|           if (isnan(z_temp)) return false; | ||||
|           // split probe point to neighbouring calibration points | ||||
|           z_pt[uint8_t(LROUND(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cos(RADIANS(interpol * 90))); | ||||
| @@ -281,80 +284,69 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi | ||||
|  *  - formulae for approximative forward kinematics in the end-stop displacement matrix | ||||
|  *  - definition of the matrix scaling parameters | ||||
|  */ | ||||
| static void reverse_kinematics_probe_points(float z_pt[NPP + 1], float mm_at_pt_axis[NPP + 1][ABC]) { | ||||
|   float pos[XYZ] = { 0.0 }; | ||||
| static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_at_pt_axis[NPP + 1]) { | ||||
|   xyz_pos_t pos{0}; | ||||
|  | ||||
|   LOOP_CAL_ALL(rad) { | ||||
|     const float a = RADIANS(210 + (360 / NPP) *  (rad - 1)), | ||||
|                 r = (rad == CEN ? 0.0f : delta_calibration_radius); | ||||
|     pos[X_AXIS] = cos(a) * r; | ||||
|     pos[Y_AXIS] = sin(a) * r; | ||||
|     pos[Z_AXIS] = z_pt[rad]; | ||||
|     pos.set(cos(a) * r, sin(a) * r, z_pt[rad]); | ||||
|     inverse_kinematics(pos); | ||||
|     LOOP_XYZ(axis) mm_at_pt_axis[rad][axis] = delta[axis]; | ||||
|     mm_at_pt_axis[rad] = delta; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void forward_kinematics_probe_points(float mm_at_pt_axis[NPP + 1][ABC], float z_pt[NPP + 1]) { | ||||
| static void forward_kinematics_probe_points(abc_float_t mm_at_pt_axis[NPP + 1], float z_pt[NPP + 1]) { | ||||
|   const float r_quot = delta_calibration_radius / delta_radius; | ||||
|  | ||||
|   #define ZPP(N,I,A) ((1 / 3.0f + r_quot * (N) / 3.0f ) * mm_at_pt_axis[I][A]) | ||||
|   #define ZPP(N,I,A) (((1.0f + r_quot * (N)) / 3.0f) * mm_at_pt_axis[I].A) | ||||
|   #define Z00(I, A) ZPP( 0, I, A) | ||||
|   #define Zp1(I, A) ZPP(+1, I, A) | ||||
|   #define Zm1(I, A) ZPP(-1, I, A) | ||||
|   #define Zp2(I, A) ZPP(+2, I, A) | ||||
|   #define Zm2(I, A) ZPP(-2, I, A) | ||||
|  | ||||
|   z_pt[CEN] = Z00(CEN, A_AXIS) + Z00(CEN, B_AXIS) + Z00(CEN, C_AXIS); | ||||
|   z_pt[__A] = Zp2(__A, A_AXIS) + Zm1(__A, B_AXIS) + Zm1(__A, C_AXIS); | ||||
|   z_pt[__B] = Zm1(__B, A_AXIS) + Zp2(__B, B_AXIS) + Zm1(__B, C_AXIS); | ||||
|   z_pt[__C] = Zm1(__C, A_AXIS) + Zm1(__C, B_AXIS) + Zp2(__C, C_AXIS); | ||||
|   z_pt[_BC] = Zm2(_BC, A_AXIS) + Zp1(_BC, B_AXIS) + Zp1(_BC, C_AXIS); | ||||
|   z_pt[_CA] = Zp1(_CA, A_AXIS) + Zm2(_CA, B_AXIS) + Zp1(_CA, C_AXIS); | ||||
|   z_pt[_AB] = Zp1(_AB, A_AXIS) + Zp1(_AB, B_AXIS) + Zm2(_AB, C_AXIS); | ||||
|   z_pt[CEN] = Z00(CEN, a) + Z00(CEN, b) + Z00(CEN, c); | ||||
|   z_pt[__A] = Zp2(__A, a) + Zm1(__A, b) + Zm1(__A, c); | ||||
|   z_pt[__B] = Zm1(__B, a) + Zp2(__B, b) + Zm1(__B, c); | ||||
|   z_pt[__C] = Zm1(__C, a) + Zm1(__C, b) + Zp2(__C, c); | ||||
|   z_pt[_BC] = Zm2(_BC, a) + Zp1(_BC, b) + Zp1(_BC, c); | ||||
|   z_pt[_CA] = Zp1(_CA, a) + Zm2(_CA, b) + Zp1(_CA, c); | ||||
|   z_pt[_AB] = Zp1(_AB, a) + Zp1(_AB, b) + Zm2(_AB, c); | ||||
| } | ||||
|  | ||||
| static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], float delta_e[ABC], float delta_r, float delta_t[ABC]) { | ||||
| static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], abc_float_t delta_e, const float delta_r, abc_float_t delta_t) { | ||||
|   const float z_center = z_pt[CEN]; | ||||
|   float diff_mm_at_pt_axis[NPP + 1][ABC], | ||||
|         new_mm_at_pt_axis[NPP + 1][ABC]; | ||||
|   abc_float_t diff_mm_at_pt_axis[NPP + 1], new_mm_at_pt_axis[NPP + 1]; | ||||
|  | ||||
|   reverse_kinematics_probe_points(z_pt, diff_mm_at_pt_axis); | ||||
|  | ||||
|   delta_radius += delta_r; | ||||
|   LOOP_XYZ(axis) delta_tower_angle_trim[axis] += delta_t[axis]; | ||||
|   delta_tower_angle_trim += delta_t; | ||||
|   recalc_delta_settings(); | ||||
|   reverse_kinematics_probe_points(z_pt, new_mm_at_pt_axis); | ||||
|  | ||||
|   LOOP_XYZ(axis) LOOP_CAL_ALL(rad) diff_mm_at_pt_axis[rad][axis] -= new_mm_at_pt_axis[rad][axis] + delta_e[axis]; | ||||
|   LOOP_CAL_ALL(rad) diff_mm_at_pt_axis[rad] -= new_mm_at_pt_axis[rad] + delta_e; | ||||
|   forward_kinematics_probe_points(diff_mm_at_pt_axis, z_pt); | ||||
|  | ||||
|   LOOP_CAL_RAD(rad) z_pt[rad] -= z_pt[CEN] - z_center; | ||||
|   z_pt[CEN] = z_center; | ||||
|  | ||||
|   delta_radius -= delta_r; | ||||
|   LOOP_XYZ(axis) delta_tower_angle_trim[axis] -= delta_t[axis]; | ||||
|   delta_tower_angle_trim -= delta_t; | ||||
|   recalc_delta_settings(); | ||||
| } | ||||
|  | ||||
| static float auto_tune_h() { | ||||
|   const float r_quot = delta_calibration_radius / delta_radius; | ||||
|   float h_fac = 0.0f; | ||||
|  | ||||
|   h_fac = r_quot / (2.0f / 3.0f); | ||||
|   h_fac = 1.0f / h_fac; // (2/3)/CR | ||||
|   return h_fac; | ||||
|   return RECIPROCAL(r_quot / (2.0f / 3.0f));  // (2/3)/CR | ||||
| } | ||||
|  | ||||
| static float auto_tune_r() { | ||||
|   const float diff = 0.01f; | ||||
|   float r_fac = 0.0f, | ||||
|         z_pt[NPP + 1] = { 0.0f }, | ||||
|         delta_e[ABC] = { 0.0f }, | ||||
|         delta_r = { 0.0f }, | ||||
|         delta_t[ABC] = { 0.0f }; | ||||
|   constexpr float diff = 0.01f, delta_r = diff; | ||||
|   float r_fac = 0.0f, z_pt[NPP + 1] = { 0.0f }; | ||||
|   abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f }; | ||||
|  | ||||
|   delta_r = diff; | ||||
|   calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t); | ||||
|   r_fac = -(z_pt[__A] + z_pt[__B] + z_pt[__C] + z_pt[_BC] + z_pt[_CA] + z_pt[_AB]) / 6.0f; | ||||
|   r_fac = diff / r_fac / 3.0f; // 1/(3*delta_Z) | ||||
| @@ -362,14 +354,11 @@ static float auto_tune_r() { | ||||
| } | ||||
|  | ||||
| static float auto_tune_a() { | ||||
|   const float diff = 0.01f; | ||||
|   float a_fac = 0.0f, | ||||
|         z_pt[NPP + 1] = { 0.0f }, | ||||
|         delta_e[ABC] = { 0.0f }, | ||||
|         delta_r = { 0.0f }, | ||||
|         delta_t[ABC] = { 0.0f }; | ||||
|   constexpr float diff = 0.01f, delta_r = 0.0f; | ||||
|   float a_fac = 0.0f, z_pt[NPP + 1] = { 0.0f }; | ||||
|   abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f }; | ||||
|  | ||||
|   ZERO(delta_t); | ||||
|   delta_t.reset(); | ||||
|   LOOP_XYZ(axis) { | ||||
|     delta_t[axis] = diff; | ||||
|     calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t); | ||||
| @@ -453,21 +442,11 @@ void GcodeSuite::G33() { | ||||
|         zero_std_dev = (verbose_level ? 999.0f : 0.0f), // 0.0 in dry-run mode : forced end | ||||
|         zero_std_dev_min = zero_std_dev, | ||||
|         zero_std_dev_old = zero_std_dev, | ||||
|         h_factor, | ||||
|         r_factor, | ||||
|         a_factor, | ||||
|         e_old[ABC] = { | ||||
|           delta_endstop_adj[A_AXIS], | ||||
|           delta_endstop_adj[B_AXIS], | ||||
|           delta_endstop_adj[C_AXIS] | ||||
|         }, | ||||
|         h_factor, r_factor, a_factor, | ||||
|         r_old = delta_radius, | ||||
|         h_old = delta_height, | ||||
|         a_old[ABC] = { | ||||
|           delta_tower_angle_trim[A_AXIS], | ||||
|           delta_tower_angle_trim[B_AXIS], | ||||
|           delta_tower_angle_trim[C_AXIS] | ||||
|         }; | ||||
|         h_old = delta_height; | ||||
|  | ||||
|   abc_pos_t e_old = delta_endstop_adj, a_old = delta_tower_angle_trim; | ||||
|  | ||||
|   SERIAL_ECHOLNPGM("G33 Auto Calibrate"); | ||||
|  | ||||
| @@ -520,15 +499,14 @@ void GcodeSuite::G33() { | ||||
|  | ||||
|       if (zero_std_dev < zero_std_dev_min) { | ||||
|         // set roll-back point | ||||
|         COPY(e_old, delta_endstop_adj); | ||||
|         e_old = delta_endstop_adj; | ||||
|         r_old = delta_radius; | ||||
|         h_old = delta_height; | ||||
|         COPY(a_old, delta_tower_angle_trim); | ||||
|         a_old = delta_tower_angle_trim; | ||||
|       } | ||||
|  | ||||
|       float e_delta[ABC] = { 0.0f }, | ||||
|             r_delta = 0.0f, | ||||
|             t_delta[ABC] = { 0.0f }; | ||||
|       abc_float_t e_delta = { 0.0f }, t_delta = { 0.0f }; | ||||
|       float r_delta = 0.0f; | ||||
|  | ||||
|       /** | ||||
|        * convergence matrices: | ||||
| @@ -563,42 +541,42 @@ void GcodeSuite::G33() { | ||||
|  | ||||
|         case 2: | ||||
|           if (towers_set) { // see 4 point calibration (towers) matrix | ||||
|             e_delta[A_AXIS] = (+Z4(__A) -Z2(__B) -Z2(__C)) * h_factor  +Z4(CEN); | ||||
|             e_delta[B_AXIS] = (-Z2(__A) +Z4(__B) -Z2(__C)) * h_factor  +Z4(CEN); | ||||
|             e_delta[C_AXIS] = (-Z2(__A) -Z2(__B) +Z4(__C)) * h_factor  +Z4(CEN); | ||||
|             r_delta         = (+Z4(__A) +Z4(__B) +Z4(__C) -Z12(CEN)) * r_factor; | ||||
|             e_delta.set((+Z4(__A) -Z2(__B) -Z2(__C)) * h_factor  +Z4(CEN), | ||||
|                         (-Z2(__A) +Z4(__B) -Z2(__C)) * h_factor  +Z4(CEN), | ||||
|                         (-Z2(__A) -Z2(__B) +Z4(__C)) * h_factor  +Z4(CEN)); | ||||
|             r_delta   = (+Z4(__A) +Z4(__B) +Z4(__C) -Z12(CEN)) * r_factor; | ||||
|           } | ||||
|           else { // see 4 point calibration (opposites) matrix | ||||
|             e_delta[A_AXIS] = (-Z4(_BC) +Z2(_CA) +Z2(_AB)) * h_factor  +Z4(CEN); | ||||
|             e_delta[B_AXIS] = (+Z2(_BC) -Z4(_CA) +Z2(_AB)) * h_factor  +Z4(CEN); | ||||
|             e_delta[C_AXIS] = (+Z2(_BC) +Z2(_CA) -Z4(_AB)) * h_factor  +Z4(CEN); | ||||
|             r_delta         = (+Z4(_BC) +Z4(_CA) +Z4(_AB) -Z12(CEN)) * r_factor; | ||||
|             e_delta.set((-Z4(_BC) +Z2(_CA) +Z2(_AB)) * h_factor  +Z4(CEN), | ||||
|                         (+Z2(_BC) -Z4(_CA) +Z2(_AB)) * h_factor  +Z4(CEN), | ||||
|                         (+Z2(_BC) +Z2(_CA) -Z4(_AB)) * h_factor  +Z4(CEN)); | ||||
|             r_delta   = (+Z4(_BC) +Z4(_CA) +Z4(_AB) -Z12(CEN)) * r_factor; | ||||
|           } | ||||
|           break; | ||||
|  | ||||
|         default: // see 7 point calibration (towers & opposites) matrix | ||||
|           e_delta[A_AXIS] = (+Z2(__A) -Z1(__B) -Z1(__C) -Z2(_BC) +Z1(_CA) +Z1(_AB)) * h_factor  +Z4(CEN); | ||||
|           e_delta[B_AXIS] = (-Z1(__A) +Z2(__B) -Z1(__C) +Z1(_BC) -Z2(_CA) +Z1(_AB)) * h_factor  +Z4(CEN); | ||||
|           e_delta[C_AXIS] = (-Z1(__A) -Z1(__B) +Z2(__C) +Z1(_BC) +Z1(_CA) -Z2(_AB)) * h_factor  +Z4(CEN); | ||||
|           r_delta         = (+Z2(__A) +Z2(__B) +Z2(__C) +Z2(_BC) +Z2(_CA) +Z2(_AB) -Z12(CEN)) * r_factor; | ||||
|           e_delta.set((+Z2(__A) -Z1(__B) -Z1(__C) -Z2(_BC) +Z1(_CA) +Z1(_AB)) * h_factor  +Z4(CEN), | ||||
|                       (-Z1(__A) +Z2(__B) -Z1(__C) +Z1(_BC) -Z2(_CA) +Z1(_AB)) * h_factor  +Z4(CEN), | ||||
|                       (-Z1(__A) -Z1(__B) +Z2(__C) +Z1(_BC) +Z1(_CA) -Z2(_AB)) * h_factor  +Z4(CEN)); | ||||
|           r_delta   = (+Z2(__A) +Z2(__B) +Z2(__C) +Z2(_BC) +Z2(_CA) +Z2(_AB) -Z12(CEN)) * r_factor; | ||||
|  | ||||
|           if (towers_set) { // see 7 point tower angle calibration (towers & opposites) matrix | ||||
|             t_delta[A_AXIS] = (+Z0(__A) -Z4(__B) +Z4(__C) +Z0(_BC) -Z4(_CA) +Z4(_AB) +Z0(CEN)) * a_factor; | ||||
|             t_delta[B_AXIS] = (+Z4(__A) +Z0(__B) -Z4(__C) +Z4(_BC) +Z0(_CA) -Z4(_AB) +Z0(CEN)) * a_factor; | ||||
|             t_delta[C_AXIS] = (-Z4(__A) +Z4(__B) +Z0(__C) -Z4(_BC) +Z4(_CA) +Z0(_AB) +Z0(CEN)) * a_factor; | ||||
|             t_delta.set((+Z0(__A) -Z4(__B) +Z4(__C) +Z0(_BC) -Z4(_CA) +Z4(_AB) +Z0(CEN)) * a_factor, | ||||
|                         (+Z4(__A) +Z0(__B) -Z4(__C) +Z4(_BC) +Z0(_CA) -Z4(_AB) +Z0(CEN)) * a_factor, | ||||
|                         (-Z4(__A) +Z4(__B) +Z0(__C) -Z4(_BC) +Z4(_CA) +Z0(_AB) +Z0(CEN)) * a_factor); | ||||
|           } | ||||
|           break; | ||||
|       } | ||||
|       LOOP_XYZ(axis) delta_endstop_adj[axis] += e_delta[axis]; | ||||
|       delta_endstop_adj += e_delta; | ||||
|       delta_radius += r_delta; | ||||
|       LOOP_XYZ(axis) delta_tower_angle_trim[axis] += t_delta[axis]; | ||||
|       delta_tower_angle_trim += t_delta; | ||||
|     } | ||||
|     else if (zero_std_dev >= test_precision) { | ||||
|       // roll back | ||||
|       COPY(delta_endstop_adj, e_old); | ||||
|       delta_endstop_adj = e_old; | ||||
|       delta_radius = r_old; | ||||
|       delta_height = h_old; | ||||
|       COPY(delta_tower_angle_trim, a_old); | ||||
|       delta_tower_angle_trim = a_old; | ||||
|     } | ||||
|  | ||||
|     if (verbose_level != 0) {                                    // !dry run | ||||
| @@ -611,7 +589,7 @@ void GcodeSuite::G33() { | ||||
|       } | ||||
|  | ||||
|       // adjust delta_height and endstops by the max amount | ||||
|       const float z_temp = _MAX(delta_endstop_adj[A_AXIS], delta_endstop_adj[B_AXIS], delta_endstop_adj[C_AXIS]); | ||||
|       const float z_temp = _MAX(delta_endstop_adj.a, delta_endstop_adj.b, delta_endstop_adj.c); | ||||
|       delta_height -= z_temp; | ||||
|       LOOP_XYZ(axis) delta_endstop_adj[axis] -= z_temp; | ||||
|     } | ||||
|   | ||||
| @@ -45,8 +45,17 @@ | ||||
| #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) | ||||
| #include "../../core/debug_out.h" | ||||
|  | ||||
| float z_auto_align_xpos[Z_STEPPER_COUNT] = Z_STEPPER_ALIGN_X, | ||||
|       z_auto_align_ypos[Z_STEPPER_COUNT] = Z_STEPPER_ALIGN_Y; | ||||
| // Sanity-check | ||||
| constexpr xy_pos_t sanity_arr_z_align[] = Z_STEPPER_ALIGN_XY; | ||||
| static_assert(COUNT(sanity_arr_z_align) == Z_STEPPER_COUNT, | ||||
|   #if ENABLED(Z_TRIPLE_STEPPER_DRIVERS) | ||||
|     "Z_STEPPER_ALIGN_XY requires three {X,Y} entries (Z, Z2, and Z3)." | ||||
|   #else | ||||
|     "Z_STEPPER_ALIGN_XY requires two {X,Y} entries (Z and Z2)." | ||||
|   #endif | ||||
| ); | ||||
|  | ||||
| xy_pos_t z_auto_align_pos[Z_STEPPER_COUNT] = Z_STEPPER_ALIGN_XY; | ||||
|  | ||||
| inline void set_all_z_lock(const bool lock) { | ||||
|   stepper.set_z_lock(lock); | ||||
| @@ -123,11 +132,11 @@ void GcodeSuite::G34() { | ||||
|  | ||||
|     float z_probe = Z_BASIC_CLEARANCE + (G34_MAX_GRADE) * 0.01f * ( | ||||
|       #if ENABLED(Z_TRIPLE_STEPPER_DRIVERS) | ||||
|          SQRT(_MAX(HYPOT2(z_auto_align_xpos[0] - z_auto_align_ypos[0], z_auto_align_xpos[1] - z_auto_align_ypos[1]), | ||||
|                   HYPOT2(z_auto_align_xpos[1] - z_auto_align_ypos[1], z_auto_align_xpos[2] - z_auto_align_ypos[2]), | ||||
|                   HYPOT2(z_auto_align_xpos[2] - z_auto_align_ypos[2], z_auto_align_xpos[0] - z_auto_align_ypos[0]))) | ||||
|          SQRT(_MAX(HYPOT2(z_auto_align_pos[0].x - z_auto_align_pos[0].y, z_auto_align_pos[1].x - z_auto_align_pos[1].y), | ||||
|                   HYPOT2(z_auto_align_pos[1].x - z_auto_align_pos[1].y, z_auto_align_pos[2].x - z_auto_align_pos[2].y), | ||||
|                   HYPOT2(z_auto_align_pos[2].x - z_auto_align_pos[2].y, z_auto_align_pos[0].x - z_auto_align_pos[0].y))) | ||||
|       #else | ||||
|          HYPOT(z_auto_align_xpos[0] - z_auto_align_ypos[0], z_auto_align_xpos[1] - z_auto_align_ypos[1]) | ||||
|          HYPOT(z_auto_align_pos[0].x - z_auto_align_pos[0].y, z_auto_align_pos[1].x - z_auto_align_pos[1].y) | ||||
|       #endif | ||||
|     ); | ||||
|  | ||||
| @@ -135,7 +144,7 @@ void GcodeSuite::G34() { | ||||
|     if (!all_axes_known()) home_all_axes(); | ||||
|  | ||||
|     // Move the Z coordinate realm towards the positive - dirty trick | ||||
|     current_position[Z_AXIS] -= z_probe * 0.5; | ||||
|     current_position.z -= z_probe * 0.5f; | ||||
|  | ||||
|     float last_z_align_move[Z_STEPPER_COUNT] = ARRAY_N(Z_STEPPER_COUNT, 10000.0f, 10000.0f, 10000.0f), | ||||
|           z_measured[Z_STEPPER_COUNT] = { 0 }, | ||||
| @@ -162,7 +171,7 @@ void GcodeSuite::G34() { | ||||
|         if (iteration == 0 || izstepper > 0) do_blocking_move_to_z(z_probe); | ||||
|  | ||||
|         // Probe a Z height for each stepper. | ||||
|         const float z_probed_height = probe_at_point(z_auto_align_xpos[zstepper], z_auto_align_ypos[zstepper], raise_after, 0, true); | ||||
|         const float z_probed_height = probe_at_point(z_auto_align_pos[zstepper], raise_after, 0, true); | ||||
|         if (isnan(z_probed_height)) { | ||||
|           SERIAL_ECHOLNPGM("Probing failed."); | ||||
|           err_break = true; | ||||
| @@ -240,7 +249,7 @@ void GcodeSuite::G34() { | ||||
|         } | ||||
|  | ||||
|         // Do a move to correct part of the misalignment for the current stepper | ||||
|         do_blocking_move_to_z(amplification * z_align_move + current_position[Z_AXIS]); | ||||
|         do_blocking_move_to_z(amplification * z_align_move + current_position.z); | ||||
|       } // for (zstepper) | ||||
|  | ||||
|       // Back to normal stepper operations | ||||
| @@ -299,20 +308,22 @@ void GcodeSuite::M422() { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const float x_pos = parser.floatval('X', z_auto_align_xpos[zstepper]); | ||||
|   if (!WITHIN(x_pos, X_MIN_POS, X_MAX_POS)) { | ||||
|   const xy_pos_t pos = { | ||||
|     parser.floatval('X', z_auto_align_pos[zstepper].x), | ||||
|     parser.floatval('Y', z_auto_align_pos[zstepper].y) | ||||
|   }; | ||||
|  | ||||
|   if (!WITHIN(pos.x, X_MIN_POS, X_MAX_POS)) { | ||||
|     SERIAL_ECHOLNPGM("?(X) out of bounds."); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const float y_pos = parser.floatval('Y', z_auto_align_ypos[zstepper]); | ||||
|   if (!WITHIN(y_pos, Y_MIN_POS, Y_MAX_POS)) { | ||||
|   if (!WITHIN(pos.y, Y_MIN_POS, Y_MAX_POS)) { | ||||
|     SERIAL_ECHOLNPGM("?(Y) out of bounds."); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   z_auto_align_xpos[zstepper] = x_pos; | ||||
|   z_auto_align_ypos[zstepper] = y_pos; | ||||
|   z_auto_align_pos[zstepper] = pos; | ||||
| } | ||||
|  | ||||
| #endif // Z_STEPPER_AUTO_ALIGN | ||||
|   | ||||
| @@ -61,17 +61,17 @@ | ||||
|  | ||||
| enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES }; | ||||
|  | ||||
| static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER; | ||||
| static constexpr xyz_float_t dimensions CALIBRATION_OBJECT_DIMENSIONS; | ||||
| static constexpr xy_float_t nod = { CALIBRATION_NOZZLE_OUTER_DIAMETER, CALIBRATION_NOZZLE_OUTER_DIAMETER }; | ||||
|  | ||||
| struct measurements_t { | ||||
|   static constexpr float dimensions[XYZ] = CALIBRATION_OBJECT_DIMENSIONS; | ||||
|   static constexpr float true_center[XYZ] = CALIBRATION_OBJECT_CENTER; | ||||
|   xyz_pos_t obj_center = true_center; // Non-static must be assigned from xyz_pos_t | ||||
|  | ||||
|   float obj_center[XYZ] = CALIBRATION_OBJECT_CENTER; | ||||
|   float obj_side[NUM_SIDES]; | ||||
|   float obj_side[NUM_SIDES], backlash[NUM_SIDES]; | ||||
|   xyz_float_t pos_error; | ||||
|  | ||||
|   float backlash[NUM_SIDES]; | ||||
|   float pos_error[XYZ]; | ||||
|  | ||||
|   float nozzle_outer_dimension[2] = {CALIBRATION_NOZZLE_OUTER_DIAMETER, CALIBRATION_NOZZLE_OUTER_DIAMETER}; | ||||
|   xy_float_t nozzle_outer_dimension = nod; | ||||
| }; | ||||
|  | ||||
| #define TEMPORARY_SOFT_ENDSTOP_STATE(enable) REMEMBER(tes, soft_endstops_enabled, enable); | ||||
| @@ -88,29 +88,8 @@ struct measurements_t { | ||||
|   #define TEMPORARY_BACKLASH_SMOOTHING(value) | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Move to a particular location. Up to three individual axes | ||||
|  * and their destinations can be specified, in any order. | ||||
|  */ | ||||
| inline void move_to( | ||||
|   const AxisEnum a1 = NO_AXIS, const float p1 = 0, | ||||
|   const AxisEnum a2 = NO_AXIS, const float p2 = 0, | ||||
|   const AxisEnum a3 = NO_AXIS, const float p3 = 0 | ||||
| ) { | ||||
|   set_destination_from_current(); | ||||
|  | ||||
|   // Note: The order of p1, p2, p3 may not correspond to X, Y, Z | ||||
|   if (a1 != NO_AXIS) destination[a1] = p1; | ||||
|   if (a2 != NO_AXIS) destination[a2] = p2; | ||||
|   if (a3 != NO_AXIS) destination[a3] = p3; | ||||
|  | ||||
|   // Make sure coordinates are within bounds | ||||
|   destination[X_AXIS] = _MAX(_MIN(destination[X_AXIS], X_MAX_POS), X_MIN_POS); | ||||
|   destination[Y_AXIS] = _MAX(_MIN(destination[Y_AXIS], Y_MAX_POS), Y_MIN_POS); | ||||
|   destination[Z_AXIS] = _MAX(_MIN(destination[Z_AXIS], Z_MAX_POS), Z_MIN_POS); | ||||
|  | ||||
|   // Move to position | ||||
|   do_blocking_move_to(destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL)); | ||||
| inline void calibration_move() { | ||||
|   do_blocking_move_to(current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL)); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -121,10 +100,12 @@ inline void move_to( | ||||
|  */ | ||||
| inline void park_above_object(measurements_t &m, const float uncertainty) { | ||||
|   // Move to safe distance above calibration object | ||||
|   move_to(Z_AXIS, m.obj_center[Z_AXIS] + m.dimensions[Z_AXIS] / 2 + uncertainty); | ||||
|   current_position.z = m.obj_center.z + dimensions.z / 2 + uncertainty; | ||||
|   calibration_move(); | ||||
|  | ||||
|   // Move to center of calibration object in XY | ||||
|   move_to(X_AXIS, m.obj_center[X_AXIS], Y_AXIS, m.obj_center[Y_AXIS]); | ||||
|   current_position = xy_pos_t(m.obj_center); | ||||
|   calibration_move(); | ||||
| } | ||||
|  | ||||
| #if HOTENDS > 1 | ||||
| @@ -139,14 +120,9 @@ inline void park_above_object(measurements_t &m, const float uncertainty) { | ||||
| #if HAS_HOTEND_OFFSET | ||||
|  | ||||
|   inline void normalize_hotend_offsets() { | ||||
|     for (uint8_t e = 1; e < HOTENDS; e++) { | ||||
|       hotend_offset[X_AXIS][e] -= hotend_offset[X_AXIS][0]; | ||||
|       hotend_offset[Y_AXIS][e] -= hotend_offset[Y_AXIS][0]; | ||||
|       hotend_offset[Z_AXIS][e] -= hotend_offset[Z_AXIS][0]; | ||||
|     } | ||||
|     hotend_offset[X_AXIS][0] = 0; | ||||
|     hotend_offset[Y_AXIS][0] = 0; | ||||
|     hotend_offset[Z_AXIS][0] = 0; | ||||
|     for (uint8_t e = 1; e < HOTENDS; e++) | ||||
|       hotend_offset[e] -= hotend_offset[0]; | ||||
|     hotend_offset[0].reset(); | ||||
|   } | ||||
|  | ||||
| #endif | ||||
| @@ -175,7 +151,7 @@ float measuring_movement(const AxisEnum axis, const int dir, const bool stop_sta | ||||
|   const feedRate_t mms = fast ? MMM_TO_MMS(CALIBRATION_FEEDRATE_FAST) : MMM_TO_MMS(CALIBRATION_FEEDRATE_SLOW); | ||||
|   const float limit    = fast ? 50 : 5; | ||||
|  | ||||
|   set_destination_from_current(); | ||||
|   destination = current_position; | ||||
|   for (float travel = 0; travel < limit; travel += step) { | ||||
|     destination[axis] += dir * step; | ||||
|     do_blocking_move_to(destination, mms); | ||||
| @@ -199,7 +175,7 @@ inline float measure(const AxisEnum axis, const int dir, const bool stop_state, | ||||
|   const bool fast = uncertainty == CALIBRATION_MEASUREMENT_UNKNOWN; | ||||
|  | ||||
|   // Save position | ||||
|   set_destination_from_current(); | ||||
|   destination = current_position; | ||||
|   const float start_pos = destination[axis]; | ||||
|   const float measured_pos = measuring_movement(axis, dir, stop_state, fast); | ||||
|   // Measure backlash | ||||
| @@ -223,7 +199,7 @@ inline float measure(const AxisEnum axis, const int dir, const bool stop_state, | ||||
|  *                               to find out height of edge | ||||
|  */ | ||||
| inline void probe_side(measurements_t &m, const float uncertainty, const side_t side, const bool probe_top_at_edge=false) { | ||||
|   const float dimensions[]  = CALIBRATION_OBJECT_DIMENSIONS; | ||||
|   const xyz_float_t dimensions = CALIBRATION_OBJECT_DIMENSIONS; | ||||
|   AxisEnum axis; | ||||
|   float dir; | ||||
|  | ||||
| @@ -232,7 +208,7 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t | ||||
|   switch (side) { | ||||
|     case TOP: { | ||||
|       const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty); | ||||
|       m.obj_center[Z_AXIS] = measurement - dimensions[Z_AXIS] / 2; | ||||
|       m.obj_center.z = measurement - dimensions.z / 2; | ||||
|       m.obj_side[TOP] = measurement; | ||||
|       return; | ||||
|     } | ||||
| @@ -240,22 +216,24 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t | ||||
|     case FRONT: axis = Y_AXIS; dir =  1; break; | ||||
|     case LEFT:  axis = X_AXIS; dir =  1; break; | ||||
|     case BACK:  axis = Y_AXIS; dir = -1; break; | ||||
|     default: | ||||
|       return; | ||||
|     default: return; | ||||
|   } | ||||
|  | ||||
|   if (probe_top_at_edge) { | ||||
|     // Probe top nearest the side we are probing | ||||
|     move_to(axis, m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 - m.nozzle_outer_dimension[axis])); | ||||
|     current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 - m.nozzle_outer_dimension[axis]); | ||||
|     calibration_move(); | ||||
|     m.obj_side[TOP] = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty); | ||||
|     m.obj_center[Z_AXIS] = m.obj_side[TOP] - dimensions[Z_AXIS] / 2; | ||||
|     m.obj_center.z = m.obj_side[TOP] - dimensions.z / 2; | ||||
|   } | ||||
|  | ||||
|   // Move to safe distance to the side of the calibration object | ||||
|   move_to(axis, m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty)); | ||||
|   current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty); | ||||
|   calibration_move(); | ||||
|  | ||||
|   // Plunge below the side of the calibration object and measure | ||||
|   move_to(Z_AXIS, m.obj_side[TOP] - CALIBRATION_NOZZLE_TIP_HEIGHT * 0.7); | ||||
|   current_position.z = m.obj_side[TOP] - CALIBRATION_NOZZLE_TIP_HEIGHT * 0.7; | ||||
|   calibration_move(); | ||||
|   const float measurement = measure(axis, dir, true, &m.backlash[side], uncertainty); | ||||
|   m.obj_center[axis] = measurement + dir * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2); | ||||
|   m.obj_side[side] = measurement; | ||||
| @@ -294,36 +272,36 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { | ||||
|  | ||||
|   // Compute the measured center of the calibration object. | ||||
|   #if HAS_X_CENTER | ||||
|     m.obj_center[X_AXIS] = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2; | ||||
|     m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2; | ||||
|   #endif | ||||
|   #if HAS_Y_CENTER | ||||
|     m.obj_center[Y_AXIS] = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2; | ||||
|     m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2; | ||||
|   #endif | ||||
|  | ||||
|   // Compute the outside diameter of the nozzle at the height | ||||
|   // at which it makes contact with the calibration object | ||||
|   #if HAS_X_CENTER | ||||
|     m.nozzle_outer_dimension[X_AXIS] = m.obj_side[RIGHT] - m.obj_side[LEFT] - m.dimensions[X_AXIS]; | ||||
|     m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x; | ||||
|   #endif | ||||
|   #if HAS_Y_CENTER | ||||
|     m.nozzle_outer_dimension[Y_AXIS] = m.obj_side[BACK]  - m.obj_side[FRONT] - m.dimensions[Y_AXIS]; | ||||
|     m.nozzle_outer_dimension.y = m.obj_side[BACK]  - m.obj_side[FRONT] - dimensions.y; | ||||
|   #endif | ||||
|  | ||||
|   park_above_object(m, uncertainty); | ||||
|  | ||||
|   // The difference between the known and the measured location | ||||
|   // of the calibration object is the positional error | ||||
|   m.pos_error[X_AXIS] = (0 | ||||
|   m.pos_error.x = (0 | ||||
|     #if HAS_X_CENTER | ||||
|       + m.true_center[X_AXIS] - m.obj_center[X_AXIS] | ||||
|       + true_center.x - m.obj_center.x | ||||
|     #endif | ||||
|   ); | ||||
|   m.pos_error[Y_AXIS] = (0 | ||||
|   m.pos_error.y = (0 | ||||
|     #if HAS_Y_CENTER | ||||
|       + m.true_center[Y_AXIS] - m.obj_center[Y_AXIS] | ||||
|       + true_center.y - m.obj_center.y | ||||
|     #endif | ||||
|   ); | ||||
|   m.pos_error[Z_AXIS] = m.true_center[Z_AXIS] - m.obj_center[Z_AXIS]; | ||||
|   m.pos_error.z = true_center.z - m.obj_center.z; | ||||
| } | ||||
|  | ||||
| #if ENABLED(CALIBRATION_REPORTING) | ||||
| @@ -348,12 +326,12 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { | ||||
|   inline void report_measured_center(const measurements_t &m) { | ||||
|     SERIAL_ECHOLNPGM("Center:"); | ||||
|     #if HAS_X_CENTER | ||||
|       SERIAL_ECHOLNPAIR(" X", m.obj_center[X_AXIS]); | ||||
|       SERIAL_ECHOLNPAIR(" X", m.obj_center.x); | ||||
|     #endif | ||||
|     #if HAS_Y_CENTER | ||||
|       SERIAL_ECHOLNPAIR(" Y", m.obj_center[Y_AXIS]); | ||||
|       SERIAL_ECHOLNPAIR(" Y", m.obj_center.y); | ||||
|     #endif | ||||
|     SERIAL_ECHOLNPAIR(" Z", m.obj_center[Z_AXIS]); | ||||
|     SERIAL_ECHOLNPAIR(" Z", m.obj_center.z); | ||||
|     SERIAL_EOL(); | ||||
|   } | ||||
|  | ||||
| @@ -380,12 +358,12 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { | ||||
|     SERIAL_ECHO(int(active_extruder)); | ||||
|     SERIAL_ECHOLNPGM(" Positional Error:"); | ||||
|     #if HAS_X_CENTER | ||||
|       SERIAL_ECHOLNPAIR(" X", m.pos_error[X_AXIS]); | ||||
|       SERIAL_ECHOLNPAIR(" X", m.pos_error.x); | ||||
|     #endif | ||||
|     #if HAS_Y_CENTER | ||||
|       SERIAL_ECHOLNPAIR(" Y", m.pos_error[Y_AXIS]); | ||||
|       SERIAL_ECHOLNPAIR(" Y", m.pos_error.y); | ||||
|     #endif | ||||
|     SERIAL_ECHOLNPAIR(" Z", m.pos_error[Z_AXIS]); | ||||
|     SERIAL_ECHOLNPAIR(" Z", m.pos_error.z); | ||||
|     SERIAL_EOL(); | ||||
|   } | ||||
|  | ||||
| @@ -393,10 +371,10 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { | ||||
|     SERIAL_ECHOLNPGM("Nozzle Tip Outer Dimensions:"); | ||||
|     #if HAS_X_CENTER || HAS_Y_CENTER | ||||
|       #if HAS_X_CENTER | ||||
|         SERIAL_ECHOLNPAIR(" X", m.nozzle_outer_dimension[X_AXIS]); | ||||
|         SERIAL_ECHOLNPAIR(" X", m.nozzle_outer_dimension.x); | ||||
|       #endif | ||||
|       #if HAS_Y_CENTER | ||||
|         SERIAL_ECHOLNPAIR(" Y", m.nozzle_outer_dimension[Y_AXIS]); | ||||
|         SERIAL_ECHOLNPAIR(" Y", m.nozzle_outer_dimension.y); | ||||
|       #endif | ||||
|     #else | ||||
|       UNUSED(m); | ||||
| @@ -410,7 +388,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { | ||||
|     // | ||||
|     inline void report_hotend_offsets() { | ||||
|       for (uint8_t e = 1; e < HOTENDS; e++) | ||||
|         SERIAL_ECHOLNPAIR("T", int(e), " Hotend Offset X", hotend_offset[X_AXIS][e], " Y", hotend_offset[Y_AXIS][e], " Z", hotend_offset[Z_AXIS][e]); | ||||
|         SERIAL_ECHOLNPAIR("T", int(e), " Hotend Offset X", hotend_offset[e].x, " Y", hotend_offset[e].y, " Z", hotend_offset[e].z); | ||||
|     } | ||||
|   #endif | ||||
|  | ||||
| @@ -434,49 +412,40 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) { | ||||
|  | ||||
|     #if ENABLED(BACKLASH_GCODE) | ||||
|       #if HAS_X_CENTER | ||||
|         backlash.distance_mm[X_AXIS] = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2; | ||||
|         backlash.distance_mm.x = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2; | ||||
|       #elif ENABLED(CALIBRATION_MEASURE_LEFT) | ||||
|         backlash.distance_mm[X_AXIS] = m.backlash[LEFT]; | ||||
|         backlash.distance_mm.x = m.backlash[LEFT]; | ||||
|       #elif ENABLED(CALIBRATION_MEASURE_RIGHT) | ||||
|         backlash.distance_mm[X_AXIS] = m.backlash[RIGHT]; | ||||
|         backlash.distance_mm.x = m.backlash[RIGHT]; | ||||
|       #endif | ||||
|  | ||||
|       #if HAS_Y_CENTER | ||||
|         backlash.distance_mm[Y_AXIS] = (m.backlash[FRONT] + m.backlash[BACK]) / 2; | ||||
|         backlash.distance_mm.y = (m.backlash[FRONT] + m.backlash[BACK]) / 2; | ||||
|       #elif ENABLED(CALIBRATION_MEASURE_FRONT) | ||||
|         backlash.distance_mm[Y_AXIS] = m.backlash[FRONT]; | ||||
|         backlash.distance_mm.y = m.backlash[FRONT]; | ||||
|       #elif ENABLED(CALIBRATION_MEASURE_BACK) | ||||
|         backlash.distance_mm[Y_AXIS] = m.backlash[BACK]; | ||||
|         backlash.distance_mm.y = m.backlash[BACK]; | ||||
|       #endif | ||||
|  | ||||
|       backlash.distance_mm[Z_AXIS] = m.backlash[TOP]; | ||||
|       backlash.distance_mm.z = m.backlash[TOP]; | ||||
|     #endif | ||||
|   } | ||||
|  | ||||
|   #if ENABLED(BACKLASH_GCODE) | ||||
|     // Turn on backlash compensation and move in all | ||||
|     // directions to take up any backlash | ||||
|  | ||||
|     { | ||||
|       // New scope for TEMPORARY_BACKLASH_CORRECTION | ||||
|       TEMPORARY_BACKLASH_CORRECTION(all_on); | ||||
|       TEMPORARY_BACKLASH_SMOOTHING(0.0f); | ||||
|       move_to( | ||||
|         X_AXIS, current_position[X_AXIS] + 3, | ||||
|         Y_AXIS, current_position[Y_AXIS] + 3, | ||||
|         Z_AXIS, current_position[Z_AXIS] + 3 | ||||
|       ); | ||||
|       move_to( | ||||
|         X_AXIS, current_position[X_AXIS] - 3, | ||||
|         Y_AXIS, current_position[Y_AXIS] - 3, | ||||
|         Z_AXIS, current_position[Z_AXIS] - 3 | ||||
|       ); | ||||
|       const xyz_float_t move = { 3, 3, 3 }; | ||||
|       current_position += move; calibration_move(); | ||||
|       current_position -= move; calibration_move(); | ||||
|     } | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| inline void update_measurements(measurements_t &m, const AxisEnum axis) { | ||||
|   const float true_center[XYZ] = CALIBRATION_OBJECT_CENTER; | ||||
|   current_position[axis] += m.pos_error[axis]; | ||||
|   m.obj_center[axis] = true_center[axis]; | ||||
|   m.pos_error[axis] = 0; | ||||
| @@ -508,12 +477,12 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const | ||||
|   // Adjust the hotend offset | ||||
|   #if HAS_HOTEND_OFFSET | ||||
|     #if HAS_X_CENTER | ||||
|       hotend_offset[X_AXIS][extruder] += m.pos_error[X_AXIS]; | ||||
|       hotend_offset[extruder].x += m.pos_error.x; | ||||
|     #endif | ||||
|     #if HAS_Y_CENTER | ||||
|       hotend_offset[Y_AXIS][extruder] += m.pos_error[Y_AXIS]; | ||||
|       hotend_offset[extruder].y += m.pos_error.y; | ||||
|     #endif | ||||
|     hotend_offset[Z_AXIS][extruder] += m.pos_error[Z_AXIS]; | ||||
|     hotend_offset[extruder].z += m.pos_error.z; | ||||
|     normalize_hotend_offsets(); | ||||
|   #endif | ||||
|  | ||||
| @@ -589,7 +558,8 @@ inline void calibrate_all() { | ||||
|   // Do a slow and precise calibration of the toolheads | ||||
|   calibrate_all_toolheads(m, CALIBRATION_MEASUREMENT_UNCERTAIN); | ||||
|  | ||||
|   move_to(X_AXIS, 150); // Park nozzle away from calibration object | ||||
|   current_position.x = X_CENTER; | ||||
|   calibration_move();         // Park nozzle away from calibration object | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -74,13 +74,14 @@ void GcodeSuite::M48() { | ||||
|  | ||||
|   const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE; | ||||
|  | ||||
|   float X_current = current_position[X_AXIS], | ||||
|         Y_current = current_position[Y_AXIS]; | ||||
|   xy_float_t next_pos = current_position; | ||||
|  | ||||
|   const float X_probe_location = parser.linearval('X', X_current + probe_offset[X_AXIS]), | ||||
|               Y_probe_location = parser.linearval('Y', Y_current + probe_offset[Y_AXIS]); | ||||
|   const xy_pos_t probe_pos = { | ||||
|     parser.linearval('X', next_pos.x + probe_offset.x), | ||||
|     parser.linearval('Y', next_pos.y + probe_offset.y) | ||||
|   }; | ||||
|  | ||||
|   if (!position_is_reachable_by_probe(X_probe_location, Y_probe_location)) { | ||||
|   if (!position_is_reachable_by_probe(probe_pos)) { | ||||
|     SERIAL_ECHOLNPGM("? (X,Y) out of bounds."); | ||||
|     return; | ||||
|   } | ||||
| @@ -116,7 +117,7 @@ void GcodeSuite::M48() { | ||||
|   float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples]; | ||||
|  | ||||
|   // Move to the first point, deploy, and probe | ||||
|   const float t = probe_at_point(X_probe_location, Y_probe_location, raise_after, verbose_level); | ||||
|   const float t = probe_at_point(probe_pos, raise_after, verbose_level); | ||||
|   bool probing_good = !isnan(t); | ||||
|  | ||||
|   if (probing_good) { | ||||
| @@ -165,32 +166,31 @@ void GcodeSuite::M48() { | ||||
|           while (angle < 0.0) angle += 360.0;   // outside of this range.   It looks like they behave correctly with | ||||
|                                                 // numbers outside of the range, but just to be safe we clamp them. | ||||
|  | ||||
|           X_current = X_probe_location - probe_offset[X_AXIS] + cos(RADIANS(angle)) * radius; | ||||
|           Y_current = Y_probe_location - probe_offset[Y_AXIS] + sin(RADIANS(angle)) * radius; | ||||
|           next_pos.set(probe_pos.x - probe_offset.x + cos(RADIANS(angle)) * radius, | ||||
|                        probe_pos.y - probe_offset.y + sin(RADIANS(angle)) * radius); | ||||
|  | ||||
|           #if DISABLED(DELTA) | ||||
|             LIMIT(X_current, X_MIN_POS, X_MAX_POS); | ||||
|             LIMIT(Y_current, Y_MIN_POS, Y_MAX_POS); | ||||
|             LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS); | ||||
|             LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS); | ||||
|           #else | ||||
|             // If we have gone out too far, we can do a simple fix and scale the numbers | ||||
|             // back in closer to the origin. | ||||
|             while (!position_is_reachable_by_probe(X_current, Y_current)) { | ||||
|               X_current *= 0.8; | ||||
|               Y_current *= 0.8; | ||||
|             while (!position_is_reachable_by_probe(next_pos)) { | ||||
|               next_pos *= 0.8; | ||||
|               if (verbose_level > 3) | ||||
|                 SERIAL_ECHOLNPAIR("Moving inward: X", X_current, " Y", Y_current); | ||||
|                 SERIAL_ECHOLNPAIR("Moving inward: X", next_pos.x, " Y", next_pos.y); | ||||
|             } | ||||
|           #endif | ||||
|  | ||||
|           if (verbose_level > 3) | ||||
|             SERIAL_ECHOLNPAIR("Going to: X", X_current, " Y", Y_current, " Z", current_position[Z_AXIS]); | ||||
|             SERIAL_ECHOLNPAIR("Going to: X", next_pos.x, " Y", next_pos.y); | ||||
|  | ||||
|           do_blocking_move_to_xy(X_current, Y_current); | ||||
|           do_blocking_move_to_xy(next_pos); | ||||
|         } // n_legs loop | ||||
|       } // n_legs | ||||
|  | ||||
|       // Probe a single point | ||||
|       sample_set[n] = probe_at_point(X_probe_location, Y_probe_location, raise_after, 0); | ||||
|       sample_set[n] = probe_at_point(probe_pos, raise_after, 0); | ||||
|  | ||||
|       // Break the loop if the probe fails | ||||
|       probing_good = !isnan(sample_set[n]); | ||||
|   | ||||
| @@ -43,14 +43,14 @@ | ||||
|    *    Z = Gamma (Tower 3) angle trim | ||||
|    */ | ||||
|   void GcodeSuite::M665() { | ||||
|     if (parser.seen('H')) delta_height                   = parser.value_linear_units(); | ||||
|     if (parser.seen('L')) delta_diagonal_rod             = parser.value_linear_units(); | ||||
|     if (parser.seen('R')) delta_radius                   = parser.value_linear_units(); | ||||
|     if (parser.seen('S')) delta_segments_per_second      = parser.value_float(); | ||||
|     if (parser.seen('B')) delta_calibration_radius       = parser.value_float(); | ||||
|     if (parser.seen('X')) delta_tower_angle_trim[A_AXIS] = parser.value_float(); | ||||
|     if (parser.seen('Y')) delta_tower_angle_trim[B_AXIS] = parser.value_float(); | ||||
|     if (parser.seen('Z')) delta_tower_angle_trim[C_AXIS] = parser.value_float(); | ||||
|     if (parser.seen('H')) delta_height              = parser.value_linear_units(); | ||||
|     if (parser.seen('L')) delta_diagonal_rod        = parser.value_linear_units(); | ||||
|     if (parser.seen('R')) delta_radius              = parser.value_linear_units(); | ||||
|     if (parser.seen('S')) delta_segments_per_second = parser.value_float(); | ||||
|     if (parser.seen('B')) delta_calibration_radius  = parser.value_float(); | ||||
|     if (parser.seen('X')) delta_tower_angle_trim.a  = parser.value_float(); | ||||
|     if (parser.seen('Y')) delta_tower_angle_trim.b  = parser.value_float(); | ||||
|     if (parser.seen('Z')) delta_tower_angle_trim.c  = parser.value_float(); | ||||
|     recalc_delta_settings(); | ||||
|   } | ||||
|  | ||||
| @@ -76,13 +76,13 @@ | ||||
|  | ||||
|     #if HAS_SCARA_OFFSET | ||||
|  | ||||
|       if (parser.seenval('Z')) scara_home_offset[Z_AXIS] = parser.value_linear_units(); | ||||
|       if (parser.seenval('Z')) scara_home_offset.z = parser.value_linear_units(); | ||||
|  | ||||
|       const bool hasA = parser.seenval('A'), hasP = parser.seenval('P'), hasX = parser.seenval('X'); | ||||
|       const uint8_t sumAPX = hasA + hasP + hasX; | ||||
|       if (sumAPX) { | ||||
|         if (sumAPX == 1) | ||||
|           scara_home_offset[A_AXIS] = parser.value_float(); | ||||
|           scara_home_offset.a = parser.value_float(); | ||||
|         else { | ||||
|           SERIAL_ERROR_MSG("Only one of A, P, or X is allowed."); | ||||
|           return; | ||||
| @@ -93,7 +93,7 @@ | ||||
|       const uint8_t sumBTY = hasB + hasT + hasY; | ||||
|       if (sumBTY) { | ||||
|         if (sumBTY == 1) | ||||
|           scara_home_offset[B_AXIS] = parser.value_float(); | ||||
|           scara_home_offset.b = parser.value_float(); | ||||
|         else { | ||||
|           SERIAL_ERROR_MSG("Only one of B, T, or Y is allowed."); | ||||
|           return; | ||||
|   | ||||
| @@ -152,17 +152,17 @@ void GcodeSuite::M205() { | ||||
|     } | ||||
|   #endif | ||||
|   #if HAS_CLASSIC_JERK | ||||
|     if (parser.seen('X')) planner.max_jerk[X_AXIS] = parser.value_linear_units(); | ||||
|     if (parser.seen('Y')) planner.max_jerk[Y_AXIS] = parser.value_linear_units(); | ||||
|     if (parser.seen('X')) planner.max_jerk.x = parser.value_linear_units(); | ||||
|     if (parser.seen('Y')) planner.max_jerk.y = parser.value_linear_units(); | ||||
|     if (parser.seen('Z')) { | ||||
|       planner.max_jerk[Z_AXIS] = parser.value_linear_units(); | ||||
|       planner.max_jerk.z = parser.value_linear_units(); | ||||
|       #if HAS_MESH | ||||
|         if (planner.max_jerk[Z_AXIS] <= 0.1f) | ||||
|         if (planner.max_jerk.z <= 0.1f) | ||||
|           SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses."); | ||||
|       #endif | ||||
|     } | ||||
|     #if !BOTH(JUNCTION_DEVIATION, LIN_ADVANCE) | ||||
|       if (parser.seen('E')) planner.max_jerk[E_AXIS] = parser.value_linear_units(); | ||||
|       if (parser.seen('E')) planner.max_jerk.e = parser.value_linear_units(); | ||||
|     #endif | ||||
|   #endif | ||||
| } | ||||
|   | ||||
| @@ -44,27 +44,27 @@ void GcodeSuite::M218() { | ||||
|   const int8_t target_extruder = get_target_extruder_from_command(); | ||||
|   if (target_extruder < 0) return; | ||||
|  | ||||
|   if (parser.seenval('X')) hotend_offset[X_AXIS][target_extruder] = parser.value_linear_units(); | ||||
|   if (parser.seenval('Y')) hotend_offset[Y_AXIS][target_extruder] = parser.value_linear_units(); | ||||
|   if (parser.seenval('Z')) hotend_offset[Z_AXIS][target_extruder] = parser.value_linear_units(); | ||||
|   if (parser.seenval('X')) hotend_offset[target_extruder].x = parser.value_linear_units(); | ||||
|   if (parser.seenval('Y')) hotend_offset[target_extruder].y = parser.value_linear_units(); | ||||
|   if (parser.seenval('Z')) hotend_offset[target_extruder].z = parser.value_linear_units(); | ||||
|  | ||||
|   if (!parser.seen("XYZ")) { | ||||
|     SERIAL_ECHO_START(); | ||||
|     SERIAL_ECHOPGM(MSG_HOTEND_OFFSET); | ||||
|     HOTEND_LOOP() { | ||||
|       SERIAL_CHAR(' '); | ||||
|       SERIAL_ECHO(hotend_offset[X_AXIS][e]); | ||||
|       SERIAL_ECHO(hotend_offset[e].x); | ||||
|       SERIAL_CHAR(','); | ||||
|       SERIAL_ECHO(hotend_offset[Y_AXIS][e]); | ||||
|       SERIAL_ECHO(hotend_offset[e].y); | ||||
|       SERIAL_CHAR(','); | ||||
|       SERIAL_ECHO_F(hotend_offset[Z_AXIS][e], 3); | ||||
|       SERIAL_ECHO_F(hotend_offset[e].z, 3); | ||||
|     } | ||||
|     SERIAL_EOL(); | ||||
|   } | ||||
|  | ||||
|   #if ENABLED(DELTA) | ||||
|     if (target_extruder == active_extruder) | ||||
|       do_blocking_move_to_xy(current_position[X_AXIS], current_position[Y_AXIS], planner.settings.max_feedrate_mm_s[X_AXIS]); | ||||
|       do_blocking_move_to_xy(current_position, planner.settings.max_feedrate_mm_s[X_AXIS]); | ||||
|   #endif | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -77,7 +77,7 @@ void GcodeSuite::M92() { | ||||
|         if (value < 20) { | ||||
|           float factor = planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] / value; // increase e constants if M92 E14 is given for netfab. | ||||
|           #if HAS_CLASSIC_JERK && !BOTH(JUNCTION_DEVIATION, LIN_ADVANCE) | ||||
|             planner.max_jerk[E_AXIS] *= factor; | ||||
|             planner.max_jerk.e *= factor; | ||||
|           #endif | ||||
|           planner.settings.max_feedrate_mm_s[E_AXIS_N(target_extruder)] *= factor; | ||||
|           planner.max_acceleration_steps_per_s2[E_AXIS_N(target_extruder)] *= factor; | ||||
|   | ||||
| @@ -33,18 +33,14 @@ | ||||
|  * Usage: M211 S1 to enable, M211 S0 to disable, M211 alone for report | ||||
|  */ | ||||
| void GcodeSuite::M211() { | ||||
|   const xyz_pos_t l_soft_min = soft_endstop.min.asLogical(), | ||||
|                   l_soft_max = soft_endstop.max.asLogical(); | ||||
|   SERIAL_ECHO_START(); | ||||
|   SERIAL_ECHOPGM(MSG_SOFT_ENDSTOPS); | ||||
|   if (parser.seen('S')) soft_endstops_enabled = parser.value_bool(); | ||||
|   serialprint_onoff(soft_endstops_enabled); | ||||
|   SERIAL_ECHOPGM(MSG_SOFT_MIN); | ||||
|   SERIAL_ECHOPAIR(    MSG_X, LOGICAL_X_POSITION(soft_endstop[X_AXIS].min)); | ||||
|   SERIAL_ECHOPAIR(" " MSG_Y, LOGICAL_Y_POSITION(soft_endstop[Y_AXIS].min)); | ||||
|   SERIAL_ECHOPAIR(" " MSG_Z, LOGICAL_Z_POSITION(soft_endstop[Z_AXIS].min)); | ||||
|   SERIAL_ECHOPGM(MSG_SOFT_MAX); | ||||
|   SERIAL_ECHOPAIR(    MSG_X, LOGICAL_X_POSITION(soft_endstop[X_AXIS].max)); | ||||
|   SERIAL_ECHOPAIR(" " MSG_Y, LOGICAL_Y_POSITION(soft_endstop[Y_AXIS].max)); | ||||
|   SERIAL_ECHOLNPAIR(" " MSG_Z, LOGICAL_Z_POSITION(soft_endstop[Z_AXIS].max)); | ||||
|   print_xyz(l_soft_min, PSTR(MSG_SOFT_MIN), PSTR(" ")); | ||||
|   print_xyz(l_soft_max, PSTR(MSG_SOFT_MAX)); | ||||
| } | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -79,9 +79,9 @@ | ||||
|         } | ||||
|         mirrored_duplication_mode = true; | ||||
|         stepper.set_directions(); | ||||
|         float x_jog = current_position[X_AXIS] - .1; | ||||
|         float x_jog = current_position.x - .1; | ||||
|         for (uint8_t i = 2; --i;) { | ||||
|           planner.buffer_line(x_jog, current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], feedrate_mm_s, 0); | ||||
|           planner.buffer_line(x_jog, current_position.y, current_position.z, current_position.e, feedrate_mm_s, 0); | ||||
|           x_jog += .1; | ||||
|         } | ||||
|         return; | ||||
| @@ -122,7 +122,7 @@ | ||||
|         DEBUG_ECHOPAIR("\nActive Ext: ", int(active_extruder)); | ||||
|         if (!active_extruder_parked) DEBUG_ECHOPGM(" NOT "); | ||||
|         DEBUG_ECHOPGM(" parked."); | ||||
|         DEBUG_ECHOPAIR("\nactive_extruder_x_pos: ", current_position[X_AXIS]); | ||||
|         DEBUG_ECHOPAIR("\nactive_extruder_x_pos: ", current_position.x); | ||||
|         DEBUG_ECHOPAIR("\ninactive_extruder_x_pos: ", inactive_extruder_x_pos); | ||||
|         DEBUG_ECHOPAIR("\nextruder_duplication_enabled: ", int(extruder_duplication_enabled)); | ||||
|         DEBUG_ECHOPAIR("\nduplicate_extruder_x_offset: ", duplicate_extruder_x_offset); | ||||
| @@ -138,7 +138,7 @@ | ||||
|  | ||||
|         HOTEND_LOOP() { | ||||
|           DEBUG_ECHOPAIR(" T", int(e)); | ||||
|           LOOP_XYZ(a) DEBUG_ECHOPAIR("  hotend_offset[", axis_codes[a], "_AXIS][", int(e), "]=", hotend_offset[a][e]); | ||||
|           LOOP_XYZ(a) DEBUG_ECHOPAIR("  hotend_offset[", int(e), "].", axis_codes[a] | 0x20, "=", hotend_offset[e][a]); | ||||
|           DEBUG_EOL(); | ||||
|         } | ||||
|         DEBUG_EOL(); | ||||
|   | ||||
| @@ -48,8 +48,8 @@ | ||||
|         #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|           do_pause_e_move(length, fr_mm_s); | ||||
|         #else | ||||
|           current_position[E_AXIS] += length / planner.e_factor[active_extruder]; | ||||
|           planner.buffer_line(current_position, fr_mm_s, active_extruder); | ||||
|           current_position.e += length / planner.e_factor[active_extruder]; | ||||
|           line_to_current_position(fr_mm_s); | ||||
|         #endif | ||||
|       } | ||||
|     } | ||||
| @@ -97,10 +97,10 @@ void GcodeSuite::M240() { | ||||
|  | ||||
|     if (axis_unhomed_error()) return; | ||||
|  | ||||
|     const float old_pos[XYZ] = { | ||||
|       current_position[X_AXIS] + parser.linearval('A'), | ||||
|       current_position[Y_AXIS] + parser.linearval('B'), | ||||
|       current_position[Z_AXIS] | ||||
|     const xyz_pos_t old_pos = { | ||||
|       current_position.x + parser.linearval('A'), | ||||
|       current_position.y + parser.linearval('B'), | ||||
|       current_position.z | ||||
|     }; | ||||
|  | ||||
|     #ifdef PHOTO_RETRACT_MM | ||||
| @@ -121,22 +121,22 @@ void GcodeSuite::M240() { | ||||
|     feedRate_t fr_mm_s = MMM_TO_MMS(parser.linearval('F')); | ||||
|     if (fr_mm_s) NOLESS(fr_mm_s, 10.0f); | ||||
|  | ||||
|     constexpr float photo_position[XYZ] = PHOTO_POSITION; | ||||
|     float raw[XYZ] = { | ||||
|        parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : photo_position[X_AXIS], | ||||
|        parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_position[Y_AXIS], | ||||
|       (parser.seenval('Z') ? parser.value_linear_units() : photo_position[Z_AXIS]) + current_position[Z_AXIS] | ||||
|     constexpr xyz_pos_t photo_position = PHOTO_POSITION; | ||||
|     xyz_pos_t raw = { | ||||
|        parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : photo_position.x, | ||||
|        parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_position.y, | ||||
|       (parser.seenval('Z') ? parser.value_linear_units() : photo_position.z) + current_position.z | ||||
|     }; | ||||
|     apply_motion_limits(raw); | ||||
|     do_blocking_move_to(raw, fr_mm_s); | ||||
|  | ||||
|     #ifdef PHOTO_SWITCH_POSITION | ||||
|       constexpr float photo_switch_position[2] = PHOTO_SWITCH_POSITION; | ||||
|       const float sraw[] = { | ||||
|          parser.seenval('I') ? RAW_X_POSITION(parser.value_linear_units()) : photo_switch_position[X_AXIS], | ||||
|          parser.seenval('J') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_switch_position[Y_AXIS] | ||||
|       constexpr xy_pos_t photo_switch_position = PHOTO_SWITCH_POSITION; | ||||
|       const xy_pos_t sraw = { | ||||
|          parser.seenval('I') ? RAW_X_POSITION(parser.value_linear_units()) : photo_switch_position.x, | ||||
|          parser.seenval('J') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_switch_position.y | ||||
|       }; | ||||
|       do_blocking_move_to_xy(sraw[X_AXIS], sraw[Y_AXIS], get_homing_bump_feedrate(X_AXIS)); | ||||
|       do_blocking_move_to_xy(sraw, get_homing_bump_feedrate(X_AXIS)); | ||||
|       #if PHOTO_SWITCH_MS > 0 | ||||
|         safe_delay(parser.intval('D', PHOTO_SWITCH_MS)); | ||||
|       #endif | ||||
|   | ||||
| @@ -58,7 +58,7 @@ void GcodeSuite::M125() { | ||||
|     #endif | ||||
|   ); | ||||
|  | ||||
|   point_t park_point = NOZZLE_PARK_POINT; | ||||
|   xyz_pos_t park_point = NOZZLE_PARK_POINT; | ||||
|  | ||||
|   // Move XY axes to filament change position or given position | ||||
|   if (parser.seenval('X')) park_point.x = RAW_X_POSITION(parser.linearval('X')); | ||||
| @@ -68,8 +68,7 @@ void GcodeSuite::M125() { | ||||
|   if (parser.seenval('Z')) park_point.z = parser.linearval('Z'); | ||||
|  | ||||
|   #if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA) | ||||
|     park_point.x += hotend_offset[X_AXIS][active_extruder]; | ||||
|     park_point.y += hotend_offset[Y_AXIS][active_extruder]; | ||||
|     park_point += hotend_offset[active_extruder]; | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(SDSUPPORT) | ||||
|   | ||||
| @@ -60,7 +60,6 @@ | ||||
|  *  Default values are used for omitted arguments. | ||||
|  */ | ||||
| void GcodeSuite::M600() { | ||||
|   point_t park_point = NOZZLE_PARK_POINT; | ||||
|  | ||||
|   #if ENABLED(MIXING_EXTRUDER) | ||||
|     const int8_t target_e_stepper = get_target_e_stepper_from_command(); | ||||
| @@ -119,6 +118,8 @@ void GcodeSuite::M600() { | ||||
|     #endif | ||||
|   ); | ||||
|  | ||||
|   xyz_pos_t park_point NOZZLE_PARK_POINT; | ||||
|  | ||||
|   // Lift Z axis | ||||
|   if (parser.seenval('Z')) park_point.z = parser.linearval('Z'); | ||||
|  | ||||
| @@ -127,8 +128,7 @@ void GcodeSuite::M600() { | ||||
|   if (parser.seenval('Y')) park_point.y = parser.linearval('Y'); | ||||
|  | ||||
|   #if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA) | ||||
|     park_point.x += hotend_offset[X_AXIS][active_extruder]; | ||||
|     park_point.y += hotend_offset[Y_AXIS][active_extruder]; | ||||
|     park_point += hotend_offset[active_extruder]; | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(MMU2_MENUS) | ||||
|   | ||||
| @@ -28,7 +28,6 @@ | ||||
| #include "../../../Marlin.h" | ||||
| #include "../../../module/motion.h" | ||||
| #include "../../../module/temperature.h" | ||||
| #include "../../../libs/point_t.h" | ||||
|  | ||||
| #if EXTRUDERS > 1 | ||||
|   #include "../../../module/tool_change.h" | ||||
| @@ -57,7 +56,7 @@ | ||||
|  *  Default values are used for omitted arguments. | ||||
|  */ | ||||
| void GcodeSuite::M701() { | ||||
|   point_t park_point = NOZZLE_PARK_POINT; | ||||
|   xyz_pos_t park_point = NOZZLE_PARK_POINT; | ||||
|  | ||||
|   #if ENABLED(NO_MOTION_BEFORE_HOMING) | ||||
|     // Don't raise Z if the machine isn't homed | ||||
| @@ -97,7 +96,7 @@ void GcodeSuite::M701() { | ||||
|  | ||||
|   // Lift Z axis | ||||
|   if (park_point.z > 0) | ||||
|     do_blocking_move_to_z(_MIN(current_position[Z_AXIS] + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|     do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|  | ||||
|   // Load filament | ||||
|   #if ENABLED(PRUSA_MMU2) | ||||
| @@ -116,7 +115,7 @@ void GcodeSuite::M701() { | ||||
|  | ||||
|   // Restore Z axis | ||||
|   if (park_point.z > 0) | ||||
|     do_blocking_move_to_z(_MAX(current_position[Z_AXIS] - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|     do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|  | ||||
|   #if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2) | ||||
|     // Restore toolhead if it was changed | ||||
| @@ -146,7 +145,7 @@ void GcodeSuite::M701() { | ||||
|  *  Default values are used for omitted arguments. | ||||
|  */ | ||||
| void GcodeSuite::M702() { | ||||
|   point_t park_point = NOZZLE_PARK_POINT; | ||||
|   xyz_pos_t park_point = NOZZLE_PARK_POINT; | ||||
|  | ||||
|   #if ENABLED(NO_MOTION_BEFORE_HOMING) | ||||
|     // Don't raise Z if the machine isn't homed | ||||
| @@ -196,7 +195,7 @@ void GcodeSuite::M702() { | ||||
|  | ||||
|   // Lift Z axis | ||||
|   if (park_point.z > 0) | ||||
|     do_blocking_move_to_z(_MIN(current_position[Z_AXIS] + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|     do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|  | ||||
|   // Unload filament | ||||
|   #if ENABLED(PRUSA_MMU2) | ||||
| @@ -226,7 +225,7 @@ void GcodeSuite::M702() { | ||||
|  | ||||
|   // Restore Z axis | ||||
|   if (park_point.z > 0) | ||||
|     do_blocking_move_to_z(_MAX(current_position[Z_AXIS] - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|     do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); | ||||
|  | ||||
|   #if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2) | ||||
|     // Restore toolhead if it was changed | ||||
|   | ||||
| @@ -31,8 +31,8 @@ | ||||
|  * M122: Debug TMC drivers | ||||
|  */ | ||||
| void GcodeSuite::M122() { | ||||
|   bool print_axis[XYZE] = { false, false, false, false }, | ||||
|        print_all = true; | ||||
|   xyze_bool_t print_axis = { false, false, false, false }; | ||||
|   bool print_all = true; | ||||
|   LOOP_XYZE(i) if (parser.seen(axis_codes[i])) { print_axis[i] = true; print_all = false; } | ||||
|  | ||||
|   if (print_all) LOOP_XYZE(i) print_axis[i] = true; | ||||
| @@ -45,12 +45,12 @@ void GcodeSuite::M122() { | ||||
|     #endif | ||||
|  | ||||
|     if (parser.seen('V')) | ||||
|       tmc_get_registers(print_axis[X_AXIS], print_axis[Y_AXIS], print_axis[Z_AXIS], print_axis[E_AXIS]); | ||||
|       tmc_get_registers(print_axis.x, print_axis.y, print_axis.z, print_axis.e); | ||||
|     else | ||||
|       tmc_report_all(print_axis[X_AXIS], print_axis[Y_AXIS], print_axis[Z_AXIS], print_axis[E_AXIS]); | ||||
|       tmc_report_all(print_axis.x, print_axis.y, print_axis.z, print_axis.e); | ||||
|   #endif | ||||
|  | ||||
|   test_tmc_connection(print_axis[X_AXIS], print_axis[Y_AXIS], print_axis[Z_AXIS], print_axis[E_AXIS]); | ||||
|   test_tmc_connection(print_axis.x, print_axis.y, print_axis.z, print_axis.e); | ||||
| } | ||||
|  | ||||
| #endif // HAS_TRINAMIC | ||||
|   | ||||
| @@ -104,25 +104,25 @@ | ||||
|    */ | ||||
|   void GcodeSuite::M912() { | ||||
|     #if M91x_SOME_X | ||||
|       const bool hasX = parser.seen(axis_codes[X_AXIS]); | ||||
|       const bool hasX = parser.seen(axis_codes.x); | ||||
|     #else | ||||
|       constexpr bool hasX = false; | ||||
|     #endif | ||||
|  | ||||
|     #if M91x_SOME_Y | ||||
|       const bool hasY = parser.seen(axis_codes[Y_AXIS]); | ||||
|       const bool hasY = parser.seen(axis_codes.y); | ||||
|     #else | ||||
|       constexpr bool hasY = false; | ||||
|     #endif | ||||
|  | ||||
|     #if M91x_SOME_Z | ||||
|       const bool hasZ = parser.seen(axis_codes[Z_AXIS]); | ||||
|       const bool hasZ = parser.seen(axis_codes.z); | ||||
|     #else | ||||
|       constexpr bool hasZ = false; | ||||
|     #endif | ||||
|  | ||||
|     #if M91x_SOME_E | ||||
|       const bool hasE = parser.seen(axis_codes[E_AXIS]); | ||||
|       const bool hasE = parser.seen(axis_codes.e); | ||||
|     #else | ||||
|       constexpr bool hasE = false; | ||||
|     #endif | ||||
| @@ -130,7 +130,7 @@ | ||||
|     const bool hasNone = !hasX && !hasY && !hasZ && !hasE; | ||||
|  | ||||
|     #if M91x_SOME_X | ||||
|       const int8_t xval = int8_t(parser.byteval(axis_codes[X_AXIS], 0xFF)); | ||||
|       const int8_t xval = int8_t(parser.byteval(axis_codes.x, 0xFF)); | ||||
|       #if M91x_USE(X) | ||||
|         if (hasNone || xval == 1 || (hasX && xval < 0)) tmc_clear_otpw(stepperX); | ||||
|       #endif | ||||
| @@ -140,7 +140,7 @@ | ||||
|     #endif | ||||
|  | ||||
|     #if M91x_SOME_Y | ||||
|       const int8_t yval = int8_t(parser.byteval(axis_codes[Y_AXIS], 0xFF)); | ||||
|       const int8_t yval = int8_t(parser.byteval(axis_codes.y, 0xFF)); | ||||
|       #if M91x_USE(Y) | ||||
|         if (hasNone || yval == 1 || (hasY && yval < 0)) tmc_clear_otpw(stepperY); | ||||
|       #endif | ||||
| @@ -150,7 +150,7 @@ | ||||
|     #endif | ||||
|  | ||||
|     #if M91x_SOME_Z | ||||
|       const int8_t zval = int8_t(parser.byteval(axis_codes[Z_AXIS], 0xFF)); | ||||
|       const int8_t zval = int8_t(parser.byteval(axis_codes.z, 0xFF)); | ||||
|       #if M91x_USE(Z) | ||||
|         if (hasNone || zval == 1 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ); | ||||
|       #endif | ||||
| @@ -163,7 +163,7 @@ | ||||
|     #endif | ||||
|  | ||||
|     #if M91x_SOME_E | ||||
|       const int8_t eval = int8_t(parser.byteval(axis_codes[E_AXIS], 0xFF)); | ||||
|       const int8_t eval = int8_t(parser.byteval(axis_codes.e, 0xFF)); | ||||
|       #if M91x_USE_E(0) | ||||
|         if (hasNone || eval == 0 || (hasE && eval < 0)) tmc_clear_otpw(stepperE0); | ||||
|       #endif | ||||
|   | ||||
| @@ -49,12 +49,13 @@ GcodeSuite gcode; | ||||
|  | ||||
| millis_t GcodeSuite::previous_move_ms; | ||||
|  | ||||
| static constexpr bool ar_init[XYZE] = AXIS_RELATIVE_MODES; | ||||
| // Relative motion mode for each logical axis | ||||
| static constexpr xyze_bool_t ar_init = AXIS_RELATIVE_MODES; | ||||
| uint8_t GcodeSuite::axis_relative = ( | ||||
|     (ar_init[X_AXIS] ? _BV(REL_X) : 0) | ||||
|   | (ar_init[Y_AXIS] ? _BV(REL_Y) : 0) | ||||
|   | (ar_init[Z_AXIS] ? _BV(REL_Z) : 0) | ||||
|   | (ar_init[E_AXIS] ? _BV(REL_E) : 0) | ||||
|     (ar_init.x ? _BV(REL_X) : 0) | ||||
|   | (ar_init.y ? _BV(REL_Y) : 0) | ||||
|   | (ar_init.z ? _BV(REL_Z) : 0) | ||||
|   | (ar_init.e ? _BV(REL_E) : 0) | ||||
| ); | ||||
|  | ||||
| #if ENABLED(HOST_KEEPALIVE_FEATURE) | ||||
| @@ -68,7 +69,7 @@ uint8_t GcodeSuite::axis_relative = ( | ||||
|  | ||||
| #if ENABLED(CNC_COORDINATE_SYSTEMS) | ||||
|   int8_t GcodeSuite::active_coordinate_system = -1; // machine space | ||||
|   float GcodeSuite::coordinate_system[MAX_COORDINATE_SYSTEMS][XYZ]; | ||||
|   xyz_pos_t GcodeSuite::coordinate_system[MAX_COORDINATE_SYSTEMS]; | ||||
| #endif | ||||
|  | ||||
| /** | ||||
| @@ -112,7 +113,7 @@ int8_t GcodeSuite::get_target_e_stepper_from_command() { | ||||
|  *  - Set the feedrate, if included | ||||
|  */ | ||||
| void GcodeSuite::get_destination_from_command() { | ||||
|   bool seen[XYZE] = { false, false, false, false }; | ||||
|   xyze_bool_t seen = { false, false, false, false }; | ||||
|   LOOP_XYZE(i) { | ||||
|     if ( (seen[i] = parser.seenval(axis_codes[i])) ) { | ||||
|       const float v = parser.value_axis_units((AxisEnum)i); | ||||
| @@ -124,7 +125,7 @@ void GcodeSuite::get_destination_from_command() { | ||||
|  | ||||
|   #if ENABLED(POWER_LOSS_RECOVERY) && !PIN_EXISTS(POWER_LOSS) | ||||
|     // Only update power loss recovery on moves with E | ||||
|     if (recovery.enabled && IS_SD_PRINTING() && seen[E_AXIS] && (seen[X_AXIS] || seen[Y_AXIS])) | ||||
|     if (recovery.enabled && IS_SD_PRINTING() && seen.e && (seen.x || seen.y)) | ||||
|       recovery.save(); | ||||
|   #endif | ||||
|  | ||||
| @@ -133,7 +134,7 @@ void GcodeSuite::get_destination_from_command() { | ||||
|  | ||||
|   #if ENABLED(PRINTCOUNTER) | ||||
|     if (!DEBUGGING(DRYRUN)) | ||||
|       print_job_timer.incFilamentUsed(destination[E_AXIS] - current_position[E_AXIS]); | ||||
|       print_job_timer.incFilamentUsed(destination.e - current_position.e); | ||||
|   #endif | ||||
|  | ||||
|   // Get ABCDHI mixing factors | ||||
|   | ||||
| @@ -321,7 +321,7 @@ public: | ||||
|   #define MAX_COORDINATE_SYSTEMS 9 | ||||
|   #if ENABLED(CNC_COORDINATE_SYSTEMS) | ||||
|     static int8_t active_coordinate_system; | ||||
|     static float coordinate_system[MAX_COORDINATE_SYSTEMS][XYZ]; | ||||
|     static xyz_pos_t coordinate_system[MAX_COORDINATE_SYSTEMS]; | ||||
|     static bool select_coordinate_system(const int8_t _new); | ||||
|   #endif | ||||
|  | ||||
|   | ||||
| @@ -36,9 +36,9 @@ | ||||
| bool GcodeSuite::select_coordinate_system(const int8_t _new) { | ||||
|   if (active_coordinate_system == _new) return false; | ||||
|   active_coordinate_system = _new; | ||||
|   float new_offset[XYZ] = { 0 }; | ||||
|   xyz_float_t new_offset{0}; | ||||
|   if (WITHIN(_new, 0, MAX_COORDINATE_SYSTEMS - 1)) | ||||
|     COPY(new_offset, coordinate_system[_new]); | ||||
|     new_offset = coordinate_system[_new]; | ||||
|   LOOP_XYZ(i) { | ||||
|     if (position_shift[i] != new_offset[i]) { | ||||
|       position_shift[i] = new_offset[i]; | ||||
|   | ||||
| @@ -86,7 +86,7 @@ void GcodeSuite::G92() { | ||||
|             #elif HAS_POSITION_SHIFT | ||||
|               if (i == E_AXIS) { | ||||
|                 didE = true; | ||||
|                 current_position[E_AXIS] = v; // When using coordinate spaces, only E is set directly | ||||
|                 current_position.e = v; // When using coordinate spaces, only E is set directly | ||||
|               } | ||||
|               else { | ||||
|                 position_shift[i] += d;       // Other axes simply offset the coordinate space | ||||
| @@ -102,7 +102,7 @@ void GcodeSuite::G92() { | ||||
|   #if ENABLED(CNC_COORDINATE_SYSTEMS) | ||||
|     // Apply workspace offset to the active coordinate system | ||||
|     if (WITHIN(active_coordinate_system, 0, MAX_COORDINATE_SYSTEMS - 1)) | ||||
|       COPY(coordinate_system[active_coordinate_system], position_shift); | ||||
|       coordinate_system[active_coordinate_system] = position_shift; | ||||
|   #endif | ||||
|  | ||||
|   if    (didXYZ) sync_plan_position(); | ||||
|   | ||||
| @@ -63,7 +63,7 @@ void GcodeSuite::M206() { | ||||
| void GcodeSuite::M428() { | ||||
|   if (axis_unhomed_error()) return; | ||||
|  | ||||
|   float diff[XYZ]; | ||||
|   xyz_float_t diff; | ||||
|   LOOP_XYZ(i) { | ||||
|     diff[i] = base_home_pos((AxisEnum)i) - current_position[i]; | ||||
|     if (!WITHIN(diff[i], -20, 20) && home_dir((AxisEnum)i) > 0) | ||||
|   | ||||
| @@ -36,7 +36,7 @@ | ||||
|     #include "../../core/debug_out.h" | ||||
|   #endif | ||||
|  | ||||
|   void report_xyze(const float pos[], const uint8_t n = 4, const uint8_t precision = 3) { | ||||
|   void report_xyze(const xyze_pos_t &pos, const uint8_t n=4, const uint8_t precision=3) { | ||||
|     char str[12]; | ||||
|     for (uint8_t a = 0; a < n; a++) { | ||||
|       SERIAL_CHAR(' '); | ||||
| @@ -47,22 +47,27 @@ | ||||
|     SERIAL_EOL(); | ||||
|   } | ||||
|  | ||||
|   inline void report_xyz(const float pos[]) { report_xyze(pos, 3); } | ||||
|   void report_xyz(const xyz_pos_t &pos, const uint8_t precision=3) { | ||||
|     char str[12]; | ||||
|     for (uint8_t a = X_AXIS; a <= Z_AXIS; a++) { | ||||
|       SERIAL_CHAR(' '); | ||||
|       SERIAL_CHAR(axis_codes[a]); | ||||
|       SERIAL_CHAR(':'); | ||||
|       SERIAL_ECHO(dtostrf(pos[a], 1, precision, str)); | ||||
|     } | ||||
|     SERIAL_EOL(); | ||||
|   } | ||||
|   inline void report_xyz(const xyze_pos_t &pos) { report_xyze(pos, 3); } | ||||
|  | ||||
|   void report_current_position_detail() { | ||||
|  | ||||
|     SERIAL_ECHOPGM("\nLogical:"); | ||||
|     const float logical[XYZ] = { | ||||
|       LOGICAL_X_POSITION(current_position[X_AXIS]), | ||||
|       LOGICAL_Y_POSITION(current_position[Y_AXIS]), | ||||
|       LOGICAL_Z_POSITION(current_position[Z_AXIS]) | ||||
|     }; | ||||
|     report_xyz(logical); | ||||
|     report_xyz(current_position.asLogical()); | ||||
|  | ||||
|     SERIAL_ECHOPGM("Raw:    "); | ||||
|     report_xyz(current_position); | ||||
|  | ||||
|     float leveled[XYZ] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] }; | ||||
|     xyze_pos_t leveled = current_position; | ||||
|  | ||||
|     #if HAS_LEVELING | ||||
|       SERIAL_ECHOPGM("Leveled:"); | ||||
| @@ -70,7 +75,7 @@ | ||||
|       report_xyz(leveled); | ||||
|  | ||||
|       SERIAL_ECHOPGM("UnLevel:"); | ||||
|       float unleveled[XYZ] = { leveled[X_AXIS], leveled[Y_AXIS], leveled[Z_AXIS] }; | ||||
|       xyze_pos_t unleveled = leveled; | ||||
|       planner.unapply_leveling(unleveled); | ||||
|       report_xyz(unleveled); | ||||
|     #endif | ||||
| @@ -153,7 +158,7 @@ | ||||
|     SERIAL_EOL(); | ||||
|  | ||||
|     #if IS_SCARA | ||||
|       const float deg[XYZ] = { | ||||
|       const xy_float_t deg = { | ||||
|         planner.get_axis_position_degrees(A_AXIS), | ||||
|         planner.get_axis_position_degrees(B_AXIS) | ||||
|       }; | ||||
| @@ -162,17 +167,12 @@ | ||||
|     #endif | ||||
|  | ||||
|     SERIAL_ECHOPGM("FromStp:"); | ||||
|     get_cartesian_from_steppers();  // writes cartes[XYZ] (with forward kinematics) | ||||
|     const float from_steppers[XYZE] = { cartes[X_AXIS], cartes[Y_AXIS], cartes[Z_AXIS], planner.get_axis_position_mm(E_AXIS) }; | ||||
|     get_cartesian_from_steppers();  // writes 'cartes' (with forward kinematics) | ||||
|     xyze_pos_t from_steppers = { cartes.x, cartes.y, cartes.z, planner.get_axis_position_mm(E_AXIS) }; | ||||
|     report_xyze(from_steppers); | ||||
|  | ||||
|     const float diff[XYZE] = { | ||||
|       from_steppers[X_AXIS] - leveled[X_AXIS], | ||||
|       from_steppers[Y_AXIS] - leveled[Y_AXIS], | ||||
|       from_steppers[Z_AXIS] - leveled[Z_AXIS], | ||||
|       from_steppers[E_AXIS] - current_position[E_AXIS] | ||||
|     }; | ||||
|     SERIAL_ECHOPGM("Differ: "); | ||||
|     const xyze_float_t diff = from_steppers - leveled; | ||||
|     SERIAL_ECHOPGM("Diff: "); | ||||
|     report_xyze(diff); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -35,7 +35,7 @@ | ||||
|   #include "../../module/stepper.h" | ||||
| #endif | ||||
|  | ||||
| extern float destination[XYZE]; | ||||
| extern xyze_pos_t destination; | ||||
|  | ||||
| #if ENABLED(VARIABLE_G0_FEEDRATE) | ||||
|   feedRate_t fast_move_feedrate = MMM_TO_MMS(G0_FEEDRATE); | ||||
| @@ -87,12 +87,12 @@ void GcodeSuite::G0_G1( | ||||
|       if (MIN_AUTORETRACT <= MAX_AUTORETRACT) { | ||||
|         // When M209 Autoretract is enabled, convert E-only moves to firmware retract/recover moves | ||||
|         if (fwretract.autoretract_enabled && parser.seen('E') && !(parser.seen('X') || parser.seen('Y') || parser.seen('Z'))) { | ||||
|           const float echange = destination[E_AXIS] - current_position[E_AXIS]; | ||||
|           const float echange = destination.e - current_position.e; | ||||
|           // Is this a retract or recover move? | ||||
|           if (WITHIN(ABS(echange), MIN_AUTORETRACT, MAX_AUTORETRACT) && fwretract.retracted[active_extruder] == (echange > 0.0)) { | ||||
|             current_position[E_AXIS] = destination[E_AXIS]; // Hide a G1-based retract/recover from calculations | ||||
|             sync_plan_position_e();                         // AND from the planner | ||||
|             return fwretract.retract(echange < 0.0);        // Firmware-based retract/recover (double-retract ignored) | ||||
|             current_position.e = destination.e;       // Hide a G1-based retract/recover from calculations | ||||
|             sync_plan_position_e();                   // AND from the planner | ||||
|             return fwretract.retract(echange < 0.0);  // Firmware-based retract/recover (double-retract ignored) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|   | ||||
| @@ -50,9 +50,9 @@ | ||||
|  * options for G2/G3 arc generation. In future these options may be GCode tunable. | ||||
|  */ | ||||
| void plan_arc( | ||||
|   const float (&cart)[XYZE],  // Destination position | ||||
|   const float (&offset)[2],   // Center of rotation relative to current_position | ||||
|   const uint8_t clockwise     // Clockwise? | ||||
|   const xyze_pos_t &cart,   // Destination position | ||||
|   const ab_float_t &offset, // Center of rotation relative to current_position | ||||
|   const uint8_t clockwise   // Clockwise? | ||||
| ) { | ||||
|   #if ENABLED(CNC_WORKSPACE_PLANES) | ||||
|     AxisEnum p_axis, q_axis, l_axis; | ||||
| @@ -67,21 +67,21 @@ void plan_arc( | ||||
|   #endif | ||||
|  | ||||
|   // Radius vector from center to current location | ||||
|   float r_P = -offset[0], r_Q = -offset[1]; | ||||
|   ab_float_t rvec = -offset; | ||||
|  | ||||
|   const float radius = HYPOT(r_P, r_Q), | ||||
|   const float radius = HYPOT(rvec.a, rvec.b), | ||||
|               #if ENABLED(AUTO_BED_LEVELING_UBL) | ||||
|                 start_L  = current_position[l_axis], | ||||
|               #endif | ||||
|               center_P = current_position[p_axis] - r_P, | ||||
|               center_Q = current_position[q_axis] - r_Q, | ||||
|               center_P = current_position[p_axis] - rvec.a, | ||||
|               center_Q = current_position[q_axis] - rvec.b, | ||||
|               rt_X = cart[p_axis] - center_P, | ||||
|               rt_Y = cart[q_axis] - center_Q, | ||||
|               linear_travel = cart[l_axis] - current_position[l_axis], | ||||
|               extruder_travel = cart[E_AXIS] - current_position[E_AXIS]; | ||||
|               extruder_travel = cart.e - current_position.e; | ||||
|  | ||||
|   // CCW angle of rotation between position and target from the circle center. Only one atan2() trig computation required. | ||||
|   float angular_travel = ATAN2(r_P * rt_Y - r_Q * rt_X, r_P * rt_X + r_Q * rt_Y); | ||||
|   float angular_travel = ATAN2(rvec.a * rt_Y - rvec.b * rt_X, rvec.a * rt_X + rvec.b * rt_Y); | ||||
|   if (angular_travel < 0) angular_travel += RADIANS(360); | ||||
|   #ifdef MIN_ARC_SEGMENTS | ||||
|     uint16_t min_segments = CEIL((MIN_ARC_SEGMENTS) * (angular_travel / RADIANS(360))); | ||||
| @@ -133,7 +133,7 @@ void plan_arc( | ||||
|    * This is important when there are successive arc motions. | ||||
|    */ | ||||
|   // Vector rotation matrix values | ||||
|   float raw[XYZE]; | ||||
|   xyze_pos_t raw; | ||||
|   const float theta_per_segment = angular_travel / segments, | ||||
|               linear_per_segment = linear_travel / segments, | ||||
|               extruder_per_segment = extruder_travel / segments, | ||||
| @@ -144,7 +144,7 @@ void plan_arc( | ||||
|   raw[l_axis] = current_position[l_axis]; | ||||
|  | ||||
|   // Initialize the extruder axis | ||||
|   raw[E_AXIS] = current_position[E_AXIS]; | ||||
|   raw.e = current_position.e; | ||||
|  | ||||
|   const feedRate_t scaled_fr_mm_s = MMS_SCALED(feedrate_mm_s); | ||||
|  | ||||
| @@ -168,10 +168,10 @@ void plan_arc( | ||||
|  | ||||
|     #if N_ARC_CORRECTION > 1 | ||||
|       if (--arc_recalc_count) { | ||||
|         // Apply vector rotation matrix to previous r_P / 1 | ||||
|         const float r_new_Y = r_P * sin_T + r_Q * cos_T; | ||||
|         r_P = r_P * cos_T - r_Q * sin_T; | ||||
|         r_Q = r_new_Y; | ||||
|         // Apply vector rotation matrix to previous rvec.a / 1 | ||||
|         const float r_new_Y = rvec.a * sin_T + rvec.b * cos_T; | ||||
|         rvec.a = rvec.a * cos_T - rvec.b * sin_T; | ||||
|         rvec.b = r_new_Y; | ||||
|       } | ||||
|       else | ||||
|     #endif | ||||
| @@ -185,20 +185,20 @@ void plan_arc( | ||||
|       // To reduce stuttering, the sin and cos could be computed at different times. | ||||
|       // For now, compute both at the same time. | ||||
|       const float cos_Ti = cos(i * theta_per_segment), sin_Ti = sin(i * theta_per_segment); | ||||
|       r_P = -offset[0] * cos_Ti + offset[1] * sin_Ti; | ||||
|       r_Q = -offset[0] * sin_Ti - offset[1] * cos_Ti; | ||||
|       rvec.a = -offset[0] * cos_Ti + offset[1] * sin_Ti; | ||||
|       rvec.b = -offset[0] * sin_Ti - offset[1] * cos_Ti; | ||||
|     } | ||||
|  | ||||
|     // Update raw location | ||||
|     raw[p_axis] = center_P + r_P; | ||||
|     raw[q_axis] = center_Q + r_Q; | ||||
|     raw[p_axis] = center_P + rvec.a; | ||||
|     raw[q_axis] = center_Q + rvec.b; | ||||
|     #if ENABLED(AUTO_BED_LEVELING_UBL) | ||||
|       raw[l_axis] = start_L; | ||||
|       UNUSED(linear_per_segment); | ||||
|     #else | ||||
|       raw[l_axis] += linear_per_segment; | ||||
|     #endif | ||||
|     raw[E_AXIS] += extruder_per_segment; | ||||
|     raw.e += extruder_per_segment; | ||||
|  | ||||
|     apply_motion_limits(raw); | ||||
|  | ||||
| @@ -215,7 +215,7 @@ void plan_arc( | ||||
|   } | ||||
|  | ||||
|   // Ensure last segment arrives at target location. | ||||
|   COPY(raw, cart); | ||||
|   raw = cart; | ||||
|   #if ENABLED(AUTO_BED_LEVELING_UBL) | ||||
|     raw[l_axis] = start_L; | ||||
|   #endif | ||||
| @@ -235,7 +235,7 @@ void plan_arc( | ||||
|   #if ENABLED(AUTO_BED_LEVELING_UBL) | ||||
|     raw[l_axis] = start_L; | ||||
|   #endif | ||||
|   COPY(current_position, raw); | ||||
|   current_position = raw; | ||||
| } // plan_arc | ||||
|  | ||||
| /** | ||||
| @@ -278,32 +278,27 @@ void GcodeSuite::G2_G3(const bool clockwise) { | ||||
|       relative_mode = relative_mode_backup; | ||||
|     #endif | ||||
|  | ||||
|     float arc_offset[2] = { 0, 0 }; | ||||
|     ab_float_t arc_offset = { 0, 0 }; | ||||
|     if (parser.seenval('R')) { | ||||
|       const float r = parser.value_linear_units(); | ||||
|       if (r) { | ||||
|         const float p1 = current_position[X_AXIS], q1 = current_position[Y_AXIS], | ||||
|                     p2 = destination[X_AXIS], q2 = destination[Y_AXIS]; | ||||
|         if (p2 != p1 || q2 != q1) { | ||||
|           const float e = clockwise ^ (r < 0) ? -1 : 1,            // clockwise -1/1, counterclockwise 1/-1 | ||||
|                       dx = p2 - p1, dy = q2 - q1,                  // X and Y differences | ||||
|                       d = HYPOT(dx, dy),                           // Linear distance between the points | ||||
|                       dinv = 1/d,                                  // Inverse of d | ||||
|                       h = SQRT(sq(r) - sq(d * 0.5f)),              // Distance to the arc pivot-point | ||||
|                       mx = (p1 + p2) * 0.5f, my = (q1 + q2) * 0.5f,// Point between the two points | ||||
|                       sx = -dy * dinv, sy = dx * dinv,             // Slope of the perpendicular bisector | ||||
|                       cx = mx + e * h * sx, cy = my + e * h * sy;  // Pivot-point of the arc | ||||
|           arc_offset[0] = cx - p1; | ||||
|           arc_offset[1] = cy - q1; | ||||
|         const xy_pos_t p1 = current_position, p2 = destination; | ||||
|         if (p1 != p2) { | ||||
|           const xy_pos_t d = p2 - p1, m = (p1 + p2) * 0.5f;   // XY distance and midpoint | ||||
|           const float e = clockwise ^ (r < 0) ? -1 : 1,       // clockwise -1/1, counterclockwise 1/-1 | ||||
|                       len = d.magnitude(),                    // Total move length | ||||
|                       h = SQRT(sq(r) - sq(len * 0.5f));       // Distance to the arc pivot-point | ||||
|           const xy_pos_t s = { d.x, -d.y };                   // Inverse Slope of the perpendicular bisector | ||||
|           arc_offset = m + s * RECIPROCAL(len) * e * h - p1;  // The calculated offset | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       if (parser.seenval('I')) arc_offset[0] = parser.value_linear_units(); | ||||
|       if (parser.seenval('J')) arc_offset[1] = parser.value_linear_units(); | ||||
|       if (parser.seenval('I')) arc_offset.a = parser.value_linear_units(); | ||||
|       if (parser.seenval('J')) arc_offset.b = parser.value_linear_units(); | ||||
|     } | ||||
|  | ||||
|     if (arc_offset[0] || arc_offset[1]) { | ||||
|     if (arc_offset) { | ||||
|  | ||||
|       #if ENABLED(ARC_P_CIRCLES) | ||||
|         // P indicates number of circles to do | ||||
|   | ||||
| @@ -27,11 +27,6 @@ | ||||
| #include "../../module/motion.h" | ||||
| #include "../../module/planner_bezier.h" | ||||
|  | ||||
| void plan_cubic_move(const float (&cart)[XYZE], const float (&offset)[4]) { | ||||
|   cubic_b_spline(current_position, cart, offset, MMS_SCALED(feedrate_mm_s), active_extruder); | ||||
|   COPY(current_position, cart); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Parameters interpreted according to: | ||||
|  * http://linuxcnc.org/docs/2.6/html/gcode/parser.html#sec:G5-Cubic-Spline | ||||
| @@ -57,14 +52,13 @@ void GcodeSuite::G5() { | ||||
|  | ||||
|     get_destination_from_command(); | ||||
|  | ||||
|     const float offset[4] = { | ||||
|       parser.linearval('I'), | ||||
|       parser.linearval('J'), | ||||
|       parser.linearval('P'), | ||||
|       parser.linearval('Q') | ||||
|     const xy_pos_t offsets[2] = { | ||||
|       { parser.linearval('I'), parser.linearval('J') }, | ||||
|       { parser.linearval('P'), parser.linearval('Q') } | ||||
|     }; | ||||
|  | ||||
|     plan_cubic_move(destination, offset); | ||||
|     cubic_b_spline(current_position, destination, offsets, MMS_SCALED(feedrate_mm_s), active_extruder); | ||||
|     current_position = destination; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -40,21 +40,21 @@ | ||||
|  | ||||
| #if ENABLED(BABYSTEP_ZPROBE_OFFSET) | ||||
|  | ||||
|   FORCE_INLINE void mod_zprobe_zoffset(const float &offs) { | ||||
|   FORCE_INLINE void mod_probe_offset(const float &offs) { | ||||
|     if (true | ||||
|       #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET) | ||||
|         && active_extruder == 0 | ||||
|       #endif | ||||
|     ) { | ||||
|       probe_offset[Z_AXIS] += offs; | ||||
|       probe_offset.z += offs; | ||||
|       SERIAL_ECHO_START(); | ||||
|       SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET MSG_Z ": ", probe_offset[Z_AXIS]); | ||||
|       SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET MSG_Z ": ", probe_offset.z); | ||||
|     } | ||||
|     else { | ||||
|       #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET) | ||||
|         hotend_offset[Z_AXIS][active_extruder] -= offs; | ||||
|         hotend_offset[active_extruder].z -= offs; | ||||
|         SERIAL_ECHO_START(); | ||||
|         SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET MSG_Z ": ", hotend_offset[Z_AXIS][active_extruder]); | ||||
|         SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET MSG_Z ": ", hotend_offset[active_extruder].z); | ||||
|       #endif | ||||
|     } | ||||
|   } | ||||
| @@ -81,7 +81,7 @@ void GcodeSuite::M290() { | ||||
|         const float offs = constrain(parser.value_axis_units((AxisEnum)a), -2, 2); | ||||
|         babystep.add_mm((AxisEnum)a, offs); | ||||
|         #if ENABLED(BABYSTEP_ZPROBE_OFFSET) | ||||
|           if (a == Z_AXIS && (!parser.seen('P') || parser.value_bool())) mod_zprobe_zoffset(offs); | ||||
|           if (a == Z_AXIS && (!parser.seen('P') || parser.value_bool())) mod_probe_offset(offs); | ||||
|         #endif | ||||
|       } | ||||
|   #else | ||||
| @@ -89,7 +89,7 @@ void GcodeSuite::M290() { | ||||
|       const float offs = constrain(parser.value_axis_units(Z_AXIS), -2, 2); | ||||
|       babystep.add_mm(Z_AXIS, offs); | ||||
|       #if ENABLED(BABYSTEP_ZPROBE_OFFSET) | ||||
|         if (!parser.seen('P') || parser.value_bool()) mod_zprobe_zoffset(offs); | ||||
|         if (!parser.seen('P') || parser.value_bool()) mod_probe_offset(offs); | ||||
|       #endif | ||||
|     } | ||||
|   #endif | ||||
| @@ -98,17 +98,17 @@ void GcodeSuite::M290() { | ||||
|     SERIAL_ECHO_START(); | ||||
|  | ||||
|     #if ENABLED(BABYSTEP_ZPROBE_OFFSET) | ||||
|       SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET " " MSG_Z, probe_offset[Z_AXIS]); | ||||
|       SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET " " MSG_Z, probe_offset.z); | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET) | ||||
|     { | ||||
|       SERIAL_ECHOLNPAIR("Hotend ", int(active_extruder), "Offset" | ||||
|         #if ENABLED(BABYSTEP_XY) | ||||
|           " X", hotend_offset[X_AXIS][active_extruder], | ||||
|           " Y", hotend_offset[Y_AXIS][active_extruder], | ||||
|           " X", hotend_offset[active_extruder].x, | ||||
|           " Y", hotend_offset[active_extruder].y, | ||||
|         #endif | ||||
|         " Z", hotend_offset[Z_AXIS][active_extruder] | ||||
|         " Z", hotend_offset[active_extruder].z | ||||
|       ); | ||||
|     } | ||||
|     #endif | ||||
|   | ||||
| @@ -39,10 +39,10 @@ | ||||
|  *   E   Engage the probe for each probe (default 1) | ||||
|  */ | ||||
| void GcodeSuite::G30() { | ||||
|   const float xpos = parser.linearval('X', current_position[X_AXIS] + probe_offset[X_AXIS]), | ||||
|               ypos = parser.linearval('Y', current_position[Y_AXIS] + probe_offset[Y_AXIS]); | ||||
|   const xy_pos_t pos = { parser.linearval('X', current_position.x + probe_offset.x), | ||||
|                          parser.linearval('Y', current_position.y + probe_offset.y) }; | ||||
|  | ||||
|   if (!position_is_reachable_by_probe(xpos, ypos)) return; | ||||
|   if (!position_is_reachable_by_probe(pos)) return; | ||||
|  | ||||
|   // Disable leveling so the planner won't mess with us | ||||
|   #if HAS_LEVELING | ||||
| @@ -52,10 +52,9 @@ void GcodeSuite::G30() { | ||||
|   remember_feedrate_scaling_off(); | ||||
|  | ||||
|   const ProbePtRaise raise_after = parser.boolval('E', true) ? PROBE_PT_STOW : PROBE_PT_NONE; | ||||
|   const float measured_z = probe_at_point(xpos, ypos, raise_after, 1); | ||||
|  | ||||
|   const float measured_z = probe_at_point(pos, raise_after, 1); | ||||
|   if (!isnan(measured_z)) | ||||
|     SERIAL_ECHOLNPAIR("Bed X: ", FIXFLOAT(xpos), " Y: ", FIXFLOAT(ypos), " Z: ", FIXFLOAT(measured_z)); | ||||
|     SERIAL_ECHOLNPAIR("Bed X: ", FIXFLOAT(pos.x), " Y: ", FIXFLOAT(pos.y), " Z: ", FIXFLOAT(measured_z)); | ||||
|  | ||||
|   restore_feedrate_and_scaling(); | ||||
|  | ||||
|   | ||||
| @@ -48,7 +48,7 @@ inline bool G38_run_probe() { | ||||
|  | ||||
|   #if MULTIPLE_PROBING > 1 | ||||
|     // Get direction of move and retract | ||||
|     float retract_mm[XYZ]; | ||||
|     xyz_float_t retract_mm; | ||||
|     LOOP_XYZ(i) { | ||||
|       const float dist = destination[i] - current_position[i]; | ||||
|       retract_mm[i] = ABS(dist) < G38_MINIMUM_MOVE ? 0 : home_bump_mm((AxisEnum)i) * (dist > 0 ? -1 : 1); | ||||
| @@ -75,8 +75,7 @@ inline bool G38_run_probe() { | ||||
|  | ||||
|     #if MULTIPLE_PROBING > 1 | ||||
|       // Move away by the retract distance | ||||
|       set_destination_from_current(); | ||||
|       LOOP_XYZ(i) destination[i] += retract_mm[i]; | ||||
|       destination = current_position + retract_mm; | ||||
|       endstops.enable(false); | ||||
|       prepare_move_to_destination(); | ||||
|       planner.synchronize(); | ||||
| @@ -84,7 +83,7 @@ inline bool G38_run_probe() { | ||||
|       REMEMBER(fr, feedrate_mm_s, feedrate_mm_s * 0.25); | ||||
|  | ||||
|       // Bump the target more slowly | ||||
|       LOOP_XYZ(i) destination[i] -= retract_mm[i] * 2; | ||||
|       destination -= retract_mm * 2; | ||||
|  | ||||
|       G38_single_probe(move_value); | ||||
|     #endif | ||||
|   | ||||
| @@ -35,18 +35,18 @@ void GcodeSuite::M851() { | ||||
|  | ||||
|   // Show usage with no parameters | ||||
|   if (!parser.seen("XYZ")) { | ||||
|     SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET " X", probe_offset[X_AXIS], " Y", probe_offset[Y_AXIS], " Z", probe_offset[Z_AXIS]); | ||||
|     SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET " X", probe_offset.x, " Y", probe_offset.y, " Z", probe_offset.z); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   float offs[XYZ] = { probe_offset[X_AXIS], probe_offset[Y_AXIS], probe_offset[Z_AXIS] }; | ||||
|   xyz_pos_t offs = probe_offset; | ||||
|  | ||||
|   bool ok = true; | ||||
|  | ||||
|   if (parser.seenval('X')) { | ||||
|     const float x = parser.value_float(); | ||||
|     if (WITHIN(x, -(X_BED_SIZE), X_BED_SIZE)) | ||||
|       offs[X_AXIS] = x; | ||||
|       offs.x = x; | ||||
|     else { | ||||
|       SERIAL_ECHOLNPAIR("?X out of range (-", int(X_BED_SIZE), " to ", int(X_BED_SIZE), ")"); | ||||
|       ok = false; | ||||
| @@ -56,7 +56,7 @@ void GcodeSuite::M851() { | ||||
|   if (parser.seenval('Y')) { | ||||
|     const float y = parser.value_float(); | ||||
|     if (WITHIN(y, -(Y_BED_SIZE), Y_BED_SIZE)) | ||||
|       offs[Y_AXIS] = y; | ||||
|       offs.y = y; | ||||
|     else { | ||||
|       SERIAL_ECHOLNPAIR("?Y out of range (-", int(Y_BED_SIZE), " to ", int(Y_BED_SIZE), ")"); | ||||
|       ok = false; | ||||
| @@ -66,7 +66,7 @@ void GcodeSuite::M851() { | ||||
|   if (parser.seenval('Z')) { | ||||
|     const float z = parser.value_float(); | ||||
|     if (WITHIN(z, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX)) | ||||
|       offs[Z_AXIS] = z; | ||||
|       offs.z = z; | ||||
|     else { | ||||
|       SERIAL_ECHOLNPAIR("?Z out of range (", int(Z_PROBE_OFFSET_RANGE_MIN), " to ", int(Z_PROBE_OFFSET_RANGE_MAX), ")"); | ||||
|       ok = false; | ||||
| @@ -74,7 +74,7 @@ void GcodeSuite::M851() { | ||||
|   } | ||||
|  | ||||
|   // Save the new offsets | ||||
|   if (ok) COPY(probe_offset, offs); | ||||
|   if (ok) probe_offset = offs; | ||||
| } | ||||
|  | ||||
| #endif // HAS_BED_PROBE | ||||
|   | ||||
| @@ -32,7 +32,7 @@ | ||||
| inline bool SCARA_move_to_cal(const uint8_t delta_a, const uint8_t delta_b) { | ||||
|   if (IsRunning()) { | ||||
|     forward_kinematics_SCARA(delta_a, delta_b); | ||||
|     do_blocking_move_to_xy(cartes[X_AXIS], cartes[Y_AXIS]); | ||||
|     do_blocking_move_to_xy(cartes); | ||||
|     return true; | ||||
|   } | ||||
|   return false; | ||||
|   | ||||
| @@ -1472,7 +1472,7 @@ | ||||
|   #define _PROBE_RADIUS (DELTA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE)) | ||||
|   #ifndef DELTA_CALIBRATION_RADIUS | ||||
|     #ifdef NOZZLE_TO_PROBE_OFFSET | ||||
|       #define DELTA_CALIBRATION_RADIUS (DELTA_PRINTABLE_RADIUS - _MAX(ABS(nozzle_to_probe_offset[X_AXIS]), ABS(nozzle_to_probe_offset[Y_AXIS]), ABS(MIN_PROBE_EDGE))) | ||||
|       #define DELTA_CALIBRATION_RADIUS (DELTA_PRINTABLE_RADIUS - _MAX(ABS(nozzle_to_probe_offset.x), ABS(nozzle_to_probe_offset.y), ABS(MIN_PROBE_EDGE))) | ||||
|     #else | ||||
|       #define DELTA_CALIBRATION_RADIUS _PROBE_RADIUS | ||||
|     #endif | ||||
| @@ -1506,6 +1506,7 @@ | ||||
|   #define PROBE_Y_MIN (Y_CENTER - (SCARA_PRINTABLE_RADIUS) + MIN_PROBE_EDGE_FRONT) | ||||
|   #define PROBE_X_MAX (X_CENTER +  SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE_RIGHT)) | ||||
|   #define PROBE_Y_MAX (Y_CENTER +  SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE_BACK)) | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(SEGMENT_LEVELED_MOVES) && !defined(LEVELED_SEGMENT_LENGTH) | ||||
| @@ -1532,10 +1533,10 @@ | ||||
|       #define _MESH_MAX_X (_MIN(X_MAX_BED - (MESH_INSET), X_MAX_POS)) | ||||
|       #define _MESH_MAX_Y (_MIN(Y_MAX_BED - (MESH_INSET), Y_MAX_POS)) | ||||
|     #else | ||||
|       #define _MESH_MIN_X (_MAX(X_MIN_BED + MESH_INSET, X_MIN_POS + nozzle_to_probe_offset[X_AXIS])) | ||||
|       #define _MESH_MIN_Y (_MAX(Y_MIN_BED + MESH_INSET, Y_MIN_POS + nozzle_to_probe_offset[Y_AXIS])) | ||||
|       #define _MESH_MAX_X (_MIN(X_MAX_BED - (MESH_INSET), X_MAX_POS + nozzle_to_probe_offset[X_AXIS])) | ||||
|       #define _MESH_MAX_Y (_MIN(Y_MAX_BED - (MESH_INSET), Y_MAX_POS + nozzle_to_probe_offset[Y_AXIS])) | ||||
|       #define _MESH_MIN_X (_MAX(X_MIN_BED + MESH_INSET, X_MIN_POS + nozzle_to_probe_offset.x)) | ||||
|       #define _MESH_MIN_Y (_MAX(Y_MIN_BED + MESH_INSET, Y_MIN_POS + nozzle_to_probe_offset.y)) | ||||
|       #define _MESH_MAX_X (_MIN(X_MAX_BED - (MESH_INSET), X_MAX_POS + nozzle_to_probe_offset.x)) | ||||
|       #define _MESH_MAX_Y (_MIN(Y_MAX_BED - (MESH_INSET), Y_MAX_POS + nozzle_to_probe_offset.y)) | ||||
|     #endif | ||||
|   #endif | ||||
|  | ||||
|   | ||||
| @@ -39,7 +39,7 @@ | ||||
| #include HAL_PATH(../HAL, inc/SanityCheck.h) | ||||
|  | ||||
| // Include all core headers | ||||
| #include "../core/enum.h" | ||||
| #include "../core/types.h" | ||||
| #include "../core/language.h" | ||||
| #include "../core/utility.h" | ||||
| #include "../core/serial.h" | ||||
|   | ||||
| @@ -34,7 +34,6 @@ | ||||
|  | ||||
| #include "../core/boards.h" | ||||
| #include "../core/macros.h" | ||||
| #include "../core/millis_t.h" | ||||
| #include "Version.h" | ||||
| #include "../../Configuration.h" | ||||
|  | ||||
|   | ||||
| @@ -402,6 +402,8 @@ | ||||
|   #error "[XYZ]_PROBE_OFFSET_FROM_EXTRUDER is now NOZZLE_TO_PROBE_OFFSET. Please update your configuration." | ||||
| #elif defined(MIN_PROBE_X) || defined(MIN_PROBE_Y) || defined(MAX_PROBE_X) || defined(MAX_PROBE_Y) | ||||
|   #error "(MIN|MAX)_PROBE_[XY] are now calculated at runtime. Please remove them from Configuration.h." | ||||
| #elif defined(Z_STEPPER_ALIGN_X) || defined(Z_STEPPER_ALIGN_X) | ||||
|   #error "Z_STEPPER_ALIGN_X and Z_STEPPER_ALIGN_Y are now combined as Z_STEPPER_ALIGN_XY. Please update your Configuration_adv.h." | ||||
| #endif | ||||
|  | ||||
| #define BOARD_MKS_13        -1000 | ||||
| @@ -2305,11 +2307,6 @@ static_assert(   _ARR_TEST(3,0) && _ARR_TEST(3,1) && _ARR_TEST(3,2) | ||||
|   #elif !HAS_BED_PROBE | ||||
|     #error "Z_STEPPER_AUTO_ALIGN requires a Z-bed probe." | ||||
|   #endif | ||||
|   constexpr float sanity_arr_z_align_x[] = Z_STEPPER_ALIGN_X, sanity_arr_z_align_y[] = Z_STEPPER_ALIGN_Y; | ||||
|   static_assert( | ||||
|     COUNT(sanity_arr_z_align_x) == Z_STEPPER_COUNT && COUNT(sanity_arr_z_align_y) == Z_STEPPER_COUNT, | ||||
|     "Z_STEPPER_ALIGN_[XY] settings require one element per Z stepper." | ||||
|   ); | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(PRINTCOUNTER) && DISABLED(EEPROM_SETTINGS) | ||||
|   | ||||
| @@ -817,11 +817,10 @@ void MarlinUI::draw_status_screen() { | ||||
|  | ||||
|           #else | ||||
|  | ||||
|             _draw_axis_value(X_AXIS, ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS])), blink); | ||||
|  | ||||
|             xy_pos_t lpos = current_position; toLogical(lpos); | ||||
|             _draw_axis_value(X_AXIS, ftostr4sign(lpos.x), blink); | ||||
|             lcd_put_wchar(' '); | ||||
|  | ||||
|             _draw_axis_value(Y_AXIS, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS])), blink); | ||||
|             _draw_axis_value(Y_AXIS, ftostr4sign(lpos.y), blink); | ||||
|  | ||||
|           #endif | ||||
|  | ||||
| @@ -830,7 +829,7 @@ void MarlinUI::draw_status_screen() { | ||||
|       #endif // LCD_WIDTH >= 20 | ||||
|  | ||||
|       lcd_moveto(LCD_WIDTH - 8, 1); | ||||
|       _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position[Z_AXIS])), blink); | ||||
|       _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position.z)), blink); | ||||
|  | ||||
|       #if HAS_LEVELING && !HAS_HEATED_BED | ||||
|         lcd_put_wchar(planner.leveling_active || blink ? '_' : ' '); | ||||
| @@ -902,7 +901,7 @@ void MarlinUI::draw_status_screen() { | ||||
|     // Z Coordinate | ||||
|     // | ||||
|     lcd_moveto(LCD_WIDTH - 9, 0); | ||||
|     _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position[Z_AXIS])), blink); | ||||
|     _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position.z)), blink); | ||||
|  | ||||
|     #if HAS_LEVELING && (HOTENDS > 1 || !HAS_HEATED_BED) | ||||
|       lcd_put_wchar(LCD_WIDTH - 1, 0, planner.leveling_active || blink ? '_' : ' '); | ||||
| @@ -1189,10 +1188,9 @@ void MarlinUI::draw_status_screen() { | ||||
|          * Show X and Y positions | ||||
|          */ | ||||
|         _XLABEL(_PLOT_X, 0); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot])))); | ||||
|  | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(ubl.mesh_index_to_xpos(x_plot)))); | ||||
|         _YLABEL(_LCD_W_POS, 0); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot])))); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(ubl.mesh_index_to_ypos(y_plot)))); | ||||
|  | ||||
|         lcd_moveto(_PLOT_X, 0); | ||||
|  | ||||
| @@ -1395,9 +1393,9 @@ void MarlinUI::draw_status_screen() { | ||||
|          * Show all values at right of screen | ||||
|          */ | ||||
|         _XLABEL(_LCD_W_POS, 1); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot])))); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(ubl.mesh_index_to_xpos(x_plot)))); | ||||
|         _YLABEL(_LCD_W_POS, 2); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot])))); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(ubl.mesh_index_to_ypos(y_plot)))); | ||||
|  | ||||
|         /** | ||||
|          * Show the location value | ||||
|   | ||||
| @@ -345,9 +345,10 @@ void MarlinUI::draw_status_screen() { | ||||
|       #endif | ||||
|       heat_bits = new_bits; | ||||
|     #endif | ||||
|     strcpy(xstring, ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS]))); | ||||
|     strcpy(ystring, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS]))); | ||||
|     strcpy(zstring, ftostr52sp( LOGICAL_Z_POSITION(current_position[Z_AXIS]))); | ||||
|     const xyz_pos_t lpos = current_position.asLogical(); | ||||
|     strcpy(xstring, ftostr4sign(lpos.x)); | ||||
|     strcpy(ystring, ftostr4sign(lpos.y)); | ||||
|     strcpy(zstring, ftostr52sp( lpos.z)); | ||||
|     #if ENABLED(FILAMENT_LCD_DISPLAY) | ||||
|       strcpy(wstring, ftostr12ns(filwidth.measured_mm)); | ||||
|       strcpy(mstring, i16tostr3(planner.volumetric_percent(parser.volumetric_enabled))); | ||||
|   | ||||
| @@ -660,7 +660,7 @@ void ST7920_Lite_Status_Screen::draw_status_message() { | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| void ST7920_Lite_Status_Screen::draw_position(const float (&pos)[XYZE], const bool position_known) { | ||||
| void ST7920_Lite_Status_Screen::draw_position(const xyz_pos_t &pos, const bool position_known) { | ||||
|   char str[7]; | ||||
|   set_ddram_address(DDRAM_LINE_4); | ||||
|   begin_data(); | ||||
| @@ -669,13 +669,13 @@ void ST7920_Lite_Status_Screen::draw_position(const float (&pos)[XYZE], const bo | ||||
|   const unsigned char alt_label = position_known ? 0 : (ui.get_blink() ? ' ' : 0); | ||||
|  | ||||
|   write_byte(alt_label ? alt_label : 'X'); | ||||
|   write_str(dtostrf(pos[X_AXIS], -4, 0, str), 4); | ||||
|   write_str(dtostrf(pos.x, -4, 0, str), 4); | ||||
|  | ||||
|   write_byte(alt_label ? alt_label : 'Y'); | ||||
|   write_str(dtostrf(pos[Y_AXIS], -4, 0, str), 4); | ||||
|   write_str(dtostrf(pos.y, -4, 0, str), 4); | ||||
|  | ||||
|   write_byte(alt_label ? alt_label : 'Z'); | ||||
|   write_str(dtostrf(pos[Z_AXIS], -5, 1, str), 5); | ||||
|   write_str(dtostrf(pos.z, -5, 1, str), 5); | ||||
| } | ||||
|  | ||||
| bool ST7920_Lite_Status_Screen::indicators_changed() { | ||||
| @@ -750,8 +750,8 @@ void ST7920_Lite_Status_Screen::update_indicators(const bool forceUpdate) { | ||||
| } | ||||
|  | ||||
| bool ST7920_Lite_Status_Screen::position_changed() { | ||||
|   const float x_pos = current_position[X_AXIS], y_pos = current_position[Y_AXIS], z_pos = current_position[Z_AXIS]; | ||||
|   const uint8_t checksum = uint8_t(x_pos) ^ uint8_t(y_pos) ^ uint8_t(z_pos); | ||||
|   const xyz_pos_t pos = current_position; | ||||
|   const uint8_t checksum = uint8_t(pos.x) ^ uint8_t(pos.y) ^ uint8_t(pos.z); | ||||
|   static uint8_t last_checksum = 0, changed = last_checksum != checksum; | ||||
|   if (changed) last_checksum = checksum; | ||||
|   return changed; | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
|  | ||||
| #include "../../HAL/shared/HAL_ST7920.h" | ||||
|  | ||||
| #include "../../core/types.h" | ||||
| #include "../../core/macros.h" | ||||
| #include "../../libs/duration_t.h" | ||||
|  | ||||
| @@ -86,7 +87,7 @@ class ST7920_Lite_Status_Screen { | ||||
|     static void draw_print_time(const duration_t &elapsed); | ||||
|     static void draw_feedrate_percentage(const uint16_t percentage); | ||||
|     static void draw_status_message(); | ||||
|     static void draw_position(const float (&pos)[XYZE], bool position_known = true); | ||||
|     static void draw_position(const xyz_pos_t &pos, bool position_known = true); | ||||
|  | ||||
|     static bool indicators_changed(); | ||||
|     static bool position_changed(); | ||||
|   | ||||
| @@ -547,10 +547,12 @@ void MarlinUI::clear_lcd() { } // Automatically cleared by Picture Loop | ||||
|       // Show X and Y positions at top of screen | ||||
|       u8g.setColorIndex(1); | ||||
|       if (PAGE_UNDER(7)) { | ||||
|         const xy_pos_t pos = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) }, | ||||
|                        lpos = pos.asLogical(); | ||||
|         lcd_put_u8str(5, 7, "X:"); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot])))); | ||||
|         lcd_put_u8str(ftostr52(lpos.x)); | ||||
|         lcd_put_u8str(74, 7, "Y:"); | ||||
|         lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot])))); | ||||
|         lcd_put_u8str(ftostr52(lpos.y)); | ||||
|       } | ||||
|  | ||||
|       // Print plot position | ||||
|   | ||||
| @@ -169,7 +169,7 @@ const struct DGUS_VP_Variable ListOfVP[] PROGMEM = { | ||||
|     VPHELPER(VP_T_E1_Is, &thermalManager.temp_hotend[0].celsius, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<0>), | ||||
|     VPHELPER(VP_T_E1_Set, &thermalManager.temp_hotend[0].target, DGUSScreenVariableHandler::HandleTemperatureChanged, &DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay), | ||||
|     VPHELPER(VP_Flowrate_E1, nullptr, DGUSScreenVariableHandler::HandleFlowRateChanged, &DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay), | ||||
|     VPHELPER(VP_EPos, &destination[3], nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>), | ||||
|     VPHELPER(VP_EPos, &destination.e, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>), | ||||
|     VPHELPER(VP_MOVE_E1, nullptr, &DGUSScreenVariableHandler::HandleManualExtrude, nullptr), | ||||
|   #endif | ||||
|   #if HOTENDS >= 2 | ||||
| @@ -195,9 +195,9 @@ const struct DGUS_VP_Variable ListOfVP[] PROGMEM = { | ||||
|   VPHELPER(VP_Feedrate_Percentage, &feedrate_percentage, DGUSScreenVariableHandler::DGUSLCD_SetValueDirectly<int16_t>, &DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay ), | ||||
|  | ||||
|   // Position Data. | ||||
|   VPHELPER(VP_XPos, ¤t_position[0], nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>), | ||||
|   VPHELPER(VP_YPos, ¤t_position[1], nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>), | ||||
|   VPHELPER(VP_ZPos, ¤t_position[2], nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>), | ||||
|   VPHELPER(VP_XPos, ¤t_position.x, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>), | ||||
|   VPHELPER(VP_YPos, ¤t_position.y, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>), | ||||
|   VPHELPER(VP_ZPos, ¤t_position.z, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>), | ||||
|  | ||||
|   // Print Progress. | ||||
|   VPHELPER(VP_PrintProgress_Percentage, &ui.progress_bar_percent, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay ), | ||||
|   | ||||
| @@ -258,22 +258,22 @@ bool StatusScreen::onTouchStart(uint8_t) { | ||||
|  | ||||
| bool StatusScreen::onTouchEnd(uint8_t tag) { | ||||
|   switch (tag) { | ||||
|     case 1: | ||||
|     case 2: | ||||
|     case 3: | ||||
|     case 4: | ||||
|     case  1: | ||||
|     case  2: | ||||
|     case  3: | ||||
|     case  4: | ||||
|     case 12: | ||||
|       if (!jog_xy) { | ||||
|         jog_xy = true; | ||||
|         injectCommands_P(PSTR("M17")); | ||||
|       } | ||||
|       jog(0,  0,  0); | ||||
|       jog({ 0, 0, 0 }); | ||||
|       break; | ||||
|     case 5: | ||||
|     case 6: | ||||
|       jog(0,  0,  0); | ||||
|     case  5: | ||||
|     case  6: | ||||
|       jog({ 0, 0, 0 }); | ||||
|       break; | ||||
|     case 9:  GOTO_SCREEN(FilesScreen); break; | ||||
|     case  9: GOTO_SCREEN(FilesScreen); break; | ||||
|     case 10: GOTO_SCREEN(MainMenu); break; | ||||
|     case 13: SpinnerDialogBox::enqueueAndWait_P(F("G112"));  break; | ||||
|     case 14: SpinnerDialogBox::enqueueAndWait_P(F("G28 Z")); break; | ||||
| @@ -291,14 +291,13 @@ bool StatusScreen::onTouchHeld(uint8_t tag) { | ||||
|   if (tag >= 1 && tag <= 4 && !jog_xy) return false; | ||||
|   const float s = min_speed + (fine_motion ? 0 : (max_speed - min_speed) * sq(increment)); | ||||
|   switch (tag) { | ||||
|     case 1: jog(-s,  0,  0); break; | ||||
|     case 2: jog( s,  0,  0); break; | ||||
|     case 4: jog( 0, -s,  0); break; // NOTE: Y directions inverted because bed rather than needle moves | ||||
|     case 3: jog( 0,  s,  0); break; | ||||
|     case 5: jog( 0,  0, -s); break; | ||||
|     case 6: jog( 0,  0,  s); break; | ||||
|     case 7: | ||||
|     case 8: | ||||
|     case 1: jog({-s,  0,  0}); break; | ||||
|     case 2: jog({ s,  0,  0}); break; | ||||
|     case 4: jog({ 0, -s,  0}); break; // NOTE: Y directions inverted because bed rather than needle moves | ||||
|     case 3: jog({ 0,  s,  0}); break; | ||||
|     case 5: jog({ 0,  0, -s}); break; | ||||
|     case 6: jog({ 0,  0,  s}); break; | ||||
|     case 7: case 8: | ||||
|     { | ||||
|       if (ExtUI::isMoving()) return false; | ||||
|       const feedRate_t feedrate = emin_speed + (fine_motion ? 0 : (emax_speed - emin_speed) * sq(increment)); | ||||
|   | ||||
| @@ -305,8 +305,8 @@ bool ChangeFilamentScreen::onTouchEnd(uint8_t tag) { | ||||
| bool ChangeFilamentScreen::onTouchHeld(uint8_t tag) { | ||||
|   if (ExtUI::isMoving()) return false; // Don't allow moves to accumulate | ||||
|   constexpr float increment = 1; | ||||
|   #define UI_INCREMENT_AXIS(axis) MoveAxisScreen::setManualFeedrate(axis, increment); UI_INCREMENT(AxisPosition_mm, axis); | ||||
|   #define UI_DECREMENT_AXIS(axis) MoveAxisScreen::setManualFeedrate(axis, increment); UI_DECREMENT(AxisPosition_mm, axis); | ||||
|   #define UI_INCREMENT_AXIS(axis) UI_INCREMENT(AxisPosition_mm, axis); | ||||
|   #define UI_DECREMENT_AXIS(axis) UI_DECREMENT(AxisPosition_mm, axis); | ||||
|   switch (tag) { | ||||
|     case 5: case 7: UI_DECREMENT_AXIS(getExtruder()); break; | ||||
|     case 6: case 8: UI_INCREMENT_AXIS(getExtruder()); break; | ||||
|   | ||||
| @@ -110,8 +110,8 @@ float MoveAxisScreen::getManualFeedrate(uint8_t axis, float increment_mm) { | ||||
|   // Compute feedrate so that the tool lags the adjuster when it is | ||||
|   // being held down, this allows enough margin for the planner to | ||||
|   // connect segments and even out the motion. | ||||
|   constexpr float manual_feedrate[XYZE] = MANUAL_FEEDRATE; | ||||
|   return min(manual_feedrate[axis] / 60.0f, abs(increment_mm * (TOUCH_REPEATS_PER_SECOND) * 0.80f)); | ||||
|   constexpr xyze_feedrate_t max_manual_feedrate = MANUAL_FEEDRATE; | ||||
|   return min(max_manual_feedrate[axis] / 60.0f, abs(increment_mm * (TOUCH_REPEATS_PER_SECOND) * 0.80f)); | ||||
| } | ||||
|  | ||||
| void MoveAxisScreen::setManualFeedrate(ExtUI::axis_t axis, float increment_mm) { | ||||
|   | ||||
| @@ -36,9 +36,8 @@ void NudgeNozzleScreen::onEntry() { | ||||
|   #if EXTRUDERS > 1 | ||||
|     screen_data.NudgeNozzleScreen.link_nozzles = true; | ||||
|   #endif | ||||
|   LOOP_XYZ(i) { | ||||
|     screen_data.NudgeNozzleScreen.rel[i] = 0; | ||||
|   } | ||||
|   screen_data.NudgeNozzleScreen.rel.reset(); | ||||
|  | ||||
|   BaseNumericAdjustmentScreen::onEntry(); | ||||
| } | ||||
|  | ||||
| @@ -48,10 +47,10 @@ void NudgeNozzleScreen::onRedraw(draw_mode_t what) { | ||||
|  | ||||
|   w.heading(                   GET_TEXTF(NUDGE_NOZZLE)); | ||||
|   #if ENABLED(BABYSTEP_XY) | ||||
|   w.color(x_axis).adjuster(2,  GET_TEXTF(AXIS_X), screen_data.NudgeNozzleScreen.rel[0] / getAxisSteps_per_mm(X)); | ||||
|   w.color(y_axis).adjuster(4,  GET_TEXTF(AXIS_Y), screen_data.NudgeNozzleScreen.rel[1] / getAxisSteps_per_mm(Y)); | ||||
|   w.color(x_axis).adjuster(2,  GET_TEXTF(AXIS_X), screen_data.NudgeNozzleScreen.rel.x / getAxisSteps_per_mm(X)); | ||||
|   w.color(y_axis).adjuster(4,  GET_TEXTF(AXIS_Y), screen_data.NudgeNozzleScreen.rel.y / getAxisSteps_per_mm(Y)); | ||||
|   #endif | ||||
|   w.color(z_axis).adjuster(6,  GET_TEXTF(AXIS_Z), screen_data.NudgeNozzleScreen.rel[2] / getAxisSteps_per_mm(Z)); | ||||
|   w.color(z_axis).adjuster(6,  GET_TEXTF(AXIS_Z), screen_data.NudgeNozzleScreen.rel.z / getAxisSteps_per_mm(Z)); | ||||
|   w.increments(); | ||||
|   #if EXTRUDERS > 1 | ||||
|     w.toggle  (8,  GET_TEXTF(ADJUST_BOTH_NOZZLES), screen_data.NudgeNozzleScreen.link_nozzles); | ||||
| @@ -90,12 +89,12 @@ bool NudgeNozzleScreen::onTouchHeld(uint8_t tag) { | ||||
|   #endif | ||||
|   int16_t steps; | ||||
|   switch (tag) { | ||||
|     case  2: steps = mmToWholeSteps(inc, X); smartAdjustAxis_steps(-steps, X, link); screen_data.NudgeNozzleScreen.rel[0] -= steps; break; | ||||
|     case  3: steps = mmToWholeSteps(inc, X); smartAdjustAxis_steps( steps, X, link); screen_data.NudgeNozzleScreen.rel[0] += steps; break; | ||||
|     case  4: steps = mmToWholeSteps(inc, Y); smartAdjustAxis_steps(-steps, Y, link); screen_data.NudgeNozzleScreen.rel[1] -= steps; break; | ||||
|     case  5: steps = mmToWholeSteps(inc, Y); smartAdjustAxis_steps( steps, Y, link); screen_data.NudgeNozzleScreen.rel[1] += steps; break; | ||||
|     case  6: steps = mmToWholeSteps(inc, Z); smartAdjustAxis_steps(-steps, Z, link); screen_data.NudgeNozzleScreen.rel[2] -= steps; break; | ||||
|     case  7: steps = mmToWholeSteps(inc, Z); smartAdjustAxis_steps( steps, Z, link); screen_data.NudgeNozzleScreen.rel[2] += steps; break; | ||||
|     case  2: steps = mmToWholeSteps(inc, X); smartAdjustAxis_steps(-steps, X, link); screen_data.NudgeNozzleScreen.rel.x -= steps; break; | ||||
|     case  3: steps = mmToWholeSteps(inc, X); smartAdjustAxis_steps( steps, X, link); screen_data.NudgeNozzleScreen.rel.x += steps; break; | ||||
|     case  4: steps = mmToWholeSteps(inc, Y); smartAdjustAxis_steps(-steps, Y, link); screen_data.NudgeNozzleScreen.rel.y -= steps; break; | ||||
|     case  5: steps = mmToWholeSteps(inc, Y); smartAdjustAxis_steps( steps, Y, link); screen_data.NudgeNozzleScreen.rel.y += steps; break; | ||||
|     case  6: steps = mmToWholeSteps(inc, Z); smartAdjustAxis_steps(-steps, Z, link); screen_data.NudgeNozzleScreen.rel.z -= steps; break; | ||||
|     case  7: steps = mmToWholeSteps(inc, Z); smartAdjustAxis_steps( steps, Z, link); screen_data.NudgeNozzleScreen.rel.z += steps; break; | ||||
|     #if EXTRUDERS > 1 | ||||
|       case  8: screen_data.NudgeNozzleScreen.link_nozzles = !link; break; | ||||
|     #endif | ||||
|   | ||||
| @@ -65,7 +65,7 @@ union screen_data_t { | ||||
| #if ENABLED(BABYSTEPPING) | ||||
|   struct { | ||||
|     struct base_numeric_adjustment_t placeholder; | ||||
|     int16_t rel[XYZ]; | ||||
|     xyz_int_t rel; | ||||
|     #if EXTRUDERS > 1 | ||||
|       bool link_nozzles; | ||||
|     #endif | ||||
|   | ||||
| @@ -204,33 +204,29 @@ namespace ExtUI { | ||||
|      * The axis will continue to jog until this function is | ||||
|      * called with all zeros. | ||||
|      */ | ||||
|     void jog(float dx, float dy, float dz) { | ||||
|     void jog(const xyz_float_t &dir) { | ||||
|       // The "destination" variable is used as a scratchpad in | ||||
|       // Marlin by GCODE routines, but should remain untouched | ||||
|       // during manual jogging, allowing us to reuse the space | ||||
|       // for our direction vector. | ||||
|       destination[X] = dx; | ||||
|       destination[Y] = dy; | ||||
|       destination[Z] = dz; | ||||
|       flags.jogging = !NEAR_ZERO(dx) || !NEAR_ZERO(dy) || !NEAR_ZERO(dz); | ||||
|       destination = dir; | ||||
|       flags.jogging = !NEAR_ZERO(dir.x) || !NEAR_ZERO(dir.y) || !NEAR_ZERO(dir.z); | ||||
|     } | ||||
|  | ||||
|     // Called by the polling routine in "joystick.cpp" | ||||
|     void _joystick_update(float (&norm_jog)[XYZ]) { | ||||
|     void _joystick_update(xyz_float_t &norm_jog) { | ||||
|       if (flags.jogging) { | ||||
|         #define OUT_OF_RANGE(VALUE) (VALUE < -1.0f || VALUE > 1.0f) | ||||
|  | ||||
|         if (OUT_OF_RANGE(destination[X_AXIS]) || OUT_OF_RANGE(destination[Y_AXIS]) || OUT_OF_RANGE(destination[Z_AXIS])) { | ||||
|           // If destination[] on any axis is out of range, it | ||||
|         if (OUT_OF_RANGE(destination.x) || OUT_OF_RANGE(destination.y) || OUT_OF_RANGE(destination.z)) { | ||||
|           // If destination on any axis is out of range, it | ||||
|           // probably means the UI forgot to stop jogging and | ||||
|           // ran GCODE that wrote a position to destination[]. | ||||
|           // ran GCODE that wrote a position to destination. | ||||
|           // To prevent a disaster, stop jogging. | ||||
|           flags.jogging = false; | ||||
|           return; | ||||
|         } | ||||
|         norm_jog[X_AXIS] = destination[X_AXIS]; | ||||
|         norm_jog[Y_AXIS] = destination[Y_AXIS]; | ||||
|         norm_jog[Z_AXIS] = destination[Z_AXIS]; | ||||
|         norm_jog = destination; | ||||
|       } | ||||
|     } | ||||
|   #endif | ||||
| @@ -328,18 +324,16 @@ namespace ExtUI { | ||||
|   float getAxisPosition_mm(const extruder_t extruder) { | ||||
|     const extruder_t old_tool = getActiveTool(); | ||||
|     setActiveTool(extruder, true); | ||||
|     const float pos = ( | ||||
|     const float epos = ( | ||||
|       #if ENABLED(JOYSTICK) | ||||
|         flags.jogging ? destination[E_AXIS] : | ||||
|         flags.jogging ? destination.e : | ||||
|       #endif | ||||
|       current_position[E_AXIS] | ||||
|       current_position.e | ||||
|     ); | ||||
|     setActiveTool(old_tool, true); | ||||
|     return pos; | ||||
|     return epos; | ||||
|   } | ||||
|  | ||||
|   constexpr feedRate_t manual_feedrate_mm_m[XYZE] = MANUAL_FEEDRATE; | ||||
|  | ||||
|   void setAxisPosition_mm(const float position, const axis_t axis) { | ||||
|     // Start with no limits to movement | ||||
|     float min = current_position[axis] - 1000, | ||||
| @@ -350,26 +344,26 @@ namespace ExtUI { | ||||
|       if (soft_endstops_enabled) switch (axis) { | ||||
|         case X_AXIS: | ||||
|           #if ENABLED(MIN_SOFTWARE_ENDSTOP_X) | ||||
|             min = soft_endstop[X_AXIS].min; | ||||
|             min = soft_endstop.min.x; | ||||
|           #endif | ||||
|           #if ENABLED(MAX_SOFTWARE_ENDSTOP_X) | ||||
|             max = soft_endstop[X_AXIS].max; | ||||
|             max = soft_endstop.max.x; | ||||
|           #endif | ||||
|           break; | ||||
|         case Y_AXIS: | ||||
|           #if ENABLED(MIN_SOFTWARE_ENDSTOP_Y) | ||||
|             min = soft_endstop[Y_AXIS].min; | ||||
|             min = soft_endstop.min.y; | ||||
|           #endif | ||||
|           #if ENABLED(MAX_SOFTWARE_ENDSTOP_Y) | ||||
|             max = soft_endstop[Y_AXIS].max; | ||||
|             max = soft_endstop.max.y; | ||||
|           #endif | ||||
|           break; | ||||
|         case Z_AXIS: | ||||
|           #if ENABLED(MIN_SOFTWARE_ENDSTOP_Z) | ||||
|             min = soft_endstop[Z_AXIS].min; | ||||
|             min = soft_endstop.min.z; | ||||
|           #endif | ||||
|           #if ENABLED(MAX_SOFTWARE_ENDSTOP_Z) | ||||
|             max = soft_endstop[Z_AXIS].max; | ||||
|             max = soft_endstop.max.z; | ||||
|           #endif | ||||
|         default: break; | ||||
|       } | ||||
| @@ -391,8 +385,8 @@ namespace ExtUI { | ||||
|   void setAxisPosition_mm(const float position, const extruder_t extruder) { | ||||
|     setActiveTool(extruder, true); | ||||
|  | ||||
|     current_position[E_AXIS] = position; | ||||
|     line_to_current_position(MMM_TO_MMS(manual_feedrate_mm_m[E_AXIS])); | ||||
|     current_position.e = position; | ||||
|     line_to_current_position(MMM_TO_MMS(manual_feedrate_mm_m.e)); | ||||
|   } | ||||
|  | ||||
|   void setActiveTool(const extruder_t extruder, bool no_move) { | ||||
| @@ -652,7 +646,7 @@ namespace ExtUI { | ||||
|     } | ||||
|  | ||||
|     float getAxisMaxJerk_mm_s(const extruder_t) { | ||||
|       return planner.max_jerk[E_AXIS]; | ||||
|       return planner.max_jerk.e; | ||||
|     } | ||||
|  | ||||
|     void setAxisMaxJerk_mm_s(const float value, const axis_t axis) { | ||||
| @@ -660,7 +654,7 @@ namespace ExtUI { | ||||
|     } | ||||
|  | ||||
|     void setAxisMaxJerk_mm_s(const float value, const extruder_t) { | ||||
|       planner.max_jerk[E_AXIS] = value; | ||||
|       planner.max_jerk.e = value; | ||||
|     } | ||||
|   #endif | ||||
|  | ||||
| @@ -710,7 +704,7 @@ namespace ExtUI { | ||||
|           #if EXTRUDERS > 1 | ||||
|             && (linked_nozzles || active_extruder == 0) | ||||
|           #endif | ||||
|         ) probe_offset[Z_AXIS] += mm; | ||||
|         ) probe_offset.z += mm; | ||||
|       #else | ||||
|         UNUSED(mm); | ||||
|       #endif | ||||
| @@ -724,7 +718,7 @@ namespace ExtUI { | ||||
|         if (!linked_nozzles) { | ||||
|           HOTEND_LOOP() | ||||
|             if (e != active_extruder) | ||||
|               hotend_offset[axis][e] += mm; | ||||
|               hotend_offset[e][axis] += mm; | ||||
|  | ||||
|           normalizeNozzleOffset(X); | ||||
|           normalizeNozzleOffset(Y); | ||||
| @@ -748,7 +742,7 @@ namespace ExtUI { | ||||
|  | ||||
|   float getZOffset_mm() { | ||||
|     #if HAS_BED_PROBE | ||||
|       return probe_offset[Z_AXIS]; | ||||
|       return probe_offset.z; | ||||
|     #elif ENABLED(BABYSTEP_DISPLAY_TOTAL) | ||||
|       return babystep.axis_total[BS_TOTAL_AXIS(Z_AXIS) + 1]; | ||||
|     #else | ||||
| @@ -759,7 +753,7 @@ namespace ExtUI { | ||||
|   void setZOffset_mm(const float value) { | ||||
|     #if HAS_BED_PROBE | ||||
|       if (WITHIN(value, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX)) | ||||
|         probe_offset[Z_AXIS] = value; | ||||
|         probe_offset.z = value; | ||||
|     #elif ENABLED(BABYSTEP_DISPLAY_TOTAL) | ||||
|       babystep.add_mm(Z_AXIS, (value - babystep.axis_total[BS_TOTAL_AXIS(Z_AXIS) + 1])); | ||||
|     #else | ||||
| @@ -771,12 +765,12 @@ namespace ExtUI { | ||||
|  | ||||
|     float getNozzleOffset_mm(const axis_t axis, const extruder_t extruder) { | ||||
|       if (extruder - E0 >= HOTENDS) return 0; | ||||
|       return hotend_offset[axis][extruder - E0]; | ||||
|       return hotend_offset[extruder - E0][axis]; | ||||
|     } | ||||
|  | ||||
|     void setNozzleOffset_mm(const float value, const axis_t axis, const extruder_t extruder) { | ||||
|       if (extruder - E0 >= HOTENDS) return; | ||||
|       hotend_offset[axis][extruder - E0] = value; | ||||
|       hotend_offset[extruder - E0][axis] = value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -785,8 +779,8 @@ namespace ExtUI { | ||||
|      * user to edit the offset the first nozzle). | ||||
|      */ | ||||
|     void normalizeNozzleOffset(const axis_t axis) { | ||||
|       const float offs = hotend_offset[axis][0]; | ||||
|       HOTEND_LOOP() hotend_offset[axis][e] -= offs; | ||||
|       const float offs = hotend_offset[0][axis]; | ||||
|       HOTEND_LOOP() hotend_offset[e][axis] -= offs; | ||||
|     } | ||||
|  | ||||
|   #endif // HAS_HOTEND_OFFSET | ||||
| @@ -820,10 +814,10 @@ namespace ExtUI { | ||||
|     bool getMeshValid() { return leveling_is_valid(); } | ||||
|     #if HAS_MESH | ||||
|       bed_mesh_t& getMeshArray() { return Z_VALUES_ARR; } | ||||
|       float getMeshPoint(const uint8_t xpos, const uint8_t ypos) { return Z_VALUES(xpos,ypos); } | ||||
|       void setMeshPoint(const uint8_t xpos, const uint8_t ypos, const float zoff) { | ||||
|         if (WITHIN(xpos, 0, GRID_MAX_POINTS_X) && WITHIN(ypos, 0, GRID_MAX_POINTS_Y)) { | ||||
|           Z_VALUES(xpos, ypos) = zoff; | ||||
|       float getMeshPoint(const xy_uint8_t &pos) { return Z_VALUES(pos.x, pos.y); } | ||||
|       void setMeshPoint(const xy_uint8_t &pos, const float zoff) { | ||||
|         if (WITHIN(pos.x, 0, GRID_MAX_POINTS_X) && WITHIN(pos.y, 0, GRID_MAX_POINTS_Y)) { | ||||
|           Z_VALUES(pos.x, pos.y) = zoff; | ||||
|           #if ENABLED(ABL_BILINEAR_SUBDIVISION) | ||||
|             bed_level_virt_interpolate(); | ||||
|           #endif | ||||
|   | ||||
| @@ -81,8 +81,8 @@ namespace ExtUI { | ||||
|   void enableHeater(const extruder_t); | ||||
|  | ||||
|   #if ENABLED(JOYSTICK) | ||||
|     void jog(float dx, float dy, float dz); | ||||
|     void _joystick_update(float (&norm_jog)[XYZ]); | ||||
|     void jog(const xyz_float_t &dir); | ||||
|     void _joystick_update(xyz_float_t &norm_jog); | ||||
|   #endif | ||||
|  | ||||
|   /** | ||||
| @@ -135,9 +135,10 @@ namespace ExtUI { | ||||
|     bool getMeshValid(); | ||||
|     #if HAS_MESH | ||||
|       bed_mesh_t& getMeshArray(); | ||||
|       float getMeshPoint(const uint8_t xpos, const uint8_t ypos); | ||||
|       void setMeshPoint(const uint8_t xpos, const uint8_t ypos, const float zval); | ||||
|       float getMeshPoint(const xy_uint8_t &pos); | ||||
|       void setMeshPoint(const xy_uint8_t &pos, const float zval); | ||||
|       void onMeshUpdate(const uint8_t xpos, const uint8_t ypos, const float zval); | ||||
|       inline void onMeshUpdate(const xy_uint8_t &pos, const float zval) { setMeshPoint(pos, zval); } | ||||
|     #endif | ||||
|   #endif | ||||
|  | ||||
|   | ||||
| @@ -379,8 +379,8 @@ void scroll_screen(const uint8_t limit, const bool is_menu) { | ||||
| #if HAS_LINE_TO_Z | ||||
|  | ||||
|   void line_to_z(const float &z) { | ||||
|     current_position[Z_AXIS] = z; | ||||
|     planner.buffer_line(current_position, MMM_TO_MMS(manual_feedrate_mm_m[Z_AXIS]), active_extruder); | ||||
|     current_position.z = z; | ||||
|     line_to_current_position(MMM_TO_MMS(manual_feedrate_mm_m.z)); | ||||
|   } | ||||
|  | ||||
| #endif | ||||
| @@ -402,10 +402,10 @@ void scroll_screen(const uint8_t limit, const bool is_menu) { | ||||
|       ui.encoderPosition = 0; | ||||
|  | ||||
|       const float diff = planner.steps_to_mm[Z_AXIS] * babystep_increment, | ||||
|                   new_probe_offset = probe_offset[Z_AXIS] + diff, | ||||
|                   new_probe_offset = probe_offset.z + diff, | ||||
|                   new_offs = | ||||
|                     #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET) | ||||
|                       do_probe ? new_probe_offset : hotend_offset[Z_AXIS][active_extruder] - diff | ||||
|                       do_probe ? new_probe_offset : hotend_offset[active_extruder].z - diff | ||||
|                     #else | ||||
|                       new_probe_offset | ||||
|                     #endif | ||||
| @@ -414,9 +414,9 @@ void scroll_screen(const uint8_t limit, const bool is_menu) { | ||||
|  | ||||
|         babystep.add_steps(Z_AXIS, babystep_increment); | ||||
|  | ||||
|         if (do_probe) probe_offset[Z_AXIS] = new_offs; | ||||
|         if (do_probe) probe_offset.z = new_offs; | ||||
|         #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET) | ||||
|           else hotend_offset[Z_AXIS][active_extruder] = new_offs; | ||||
|           else hotend_offset[active_extruder].z = new_offs; | ||||
|         #endif | ||||
|  | ||||
|         ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); | ||||
| @@ -425,13 +425,13 @@ void scroll_screen(const uint8_t limit, const bool is_menu) { | ||||
|     if (ui.should_draw()) { | ||||
|       #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET) | ||||
|         if (!do_probe) | ||||
|           draw_edit_screen(PSTR(MSG_Z_OFFSET), ftostr43sign(hotend_offset[Z_AXIS][active_extruder])); | ||||
|           draw_edit_screen(PSTR(MSG_Z_OFFSET), ftostr43sign(hotend_offset[active_extruder].z)); | ||||
|         else | ||||
|       #endif | ||||
|           draw_edit_screen(PSTR(MSG_ZPROBE_ZOFFSET), ftostr43sign(probe_offset[Z_AXIS])); | ||||
|           draw_edit_screen(PSTR(MSG_ZPROBE_ZOFFSET), ftostr43sign(probe_offset.z)); | ||||
|  | ||||
|       #if ENABLED(BABYSTEP_ZPROBE_GFX_OVERLAY) | ||||
|         if (do_probe) _lcd_zoffset_overlay_gfx(probe_offset[Z_AXIS]); | ||||
|         if (do_probe) _lcd_zoffset_overlay_gfx(probe_offset.z); | ||||
|       #endif | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -55,7 +55,7 @@ void menu_backlash(); | ||||
|  | ||||
|   #include "../../feature/dac/stepper_dac.h" | ||||
|  | ||||
|   uint8_t driverPercent[XYZE]; | ||||
|   xyze_uint8_t driverPercent; | ||||
|   inline void dac_driver_getValues() { LOOP_XYZE(i) driverPercent[i] = dac_current_get_percent((AxisEnum)i); } | ||||
|   static void dac_driver_commit() { dac_current_set_percents(driverPercent); } | ||||
|  | ||||
| @@ -552,7 +552,7 @@ void menu_backlash(); | ||||
|       #if ENABLED(DELTA) | ||||
|         EDIT_JERK(C); | ||||
|       #else | ||||
|         MENU_MULTIPLIER_ITEM_EDIT(float52sign, MSG_VC_JERK, &planner.max_jerk[C_AXIS], 0.1f, 990); | ||||
|         MENU_MULTIPLIER_ITEM_EDIT(float52sign, MSG_VC_JERK, &planner.max_jerk.c, 0.1f, 990); | ||||
|       #endif | ||||
|       #if !BOTH(JUNCTION_DEVIATION, LIN_ADVANCE) | ||||
|         EDIT_JERK(E); | ||||
|   | ||||
| @@ -58,26 +58,24 @@ static inline void _lcd_goto_next_corner() { | ||||
|   line_to_z(LEVEL_CORNERS_Z_HOP); | ||||
|   switch (bed_corner) { | ||||
|     case 0: | ||||
|       current_position[X_AXIS] = X_MIN_BED + LEVEL_CORNERS_INSET; | ||||
|       current_position[Y_AXIS] = Y_MIN_BED + LEVEL_CORNERS_INSET; | ||||
|       current_position.set(X_MIN_BED + LEVEL_CORNERS_INSET, Y_MIN_BED + LEVEL_CORNERS_INSET); | ||||
|       break; | ||||
|     case 1: | ||||
|       current_position[X_AXIS] = X_MAX_BED - (LEVEL_CORNERS_INSET); | ||||
|       current_position.x = X_MAX_BED - (LEVEL_CORNERS_INSET); | ||||
|       break; | ||||
|     case 2: | ||||
|       current_position[Y_AXIS] = Y_MAX_BED - (LEVEL_CORNERS_INSET); | ||||
|       current_position.y = Y_MAX_BED - (LEVEL_CORNERS_INSET); | ||||
|       break; | ||||
|     case 3: | ||||
|       current_position[X_AXIS] = X_MIN_BED + LEVEL_CORNERS_INSET; | ||||
|       current_position.x = X_MIN_BED + LEVEL_CORNERS_INSET; | ||||
|       break; | ||||
|     #if ENABLED(LEVEL_CENTER_TOO) | ||||
|       case 4: | ||||
|         current_position[X_AXIS] = X_CENTER; | ||||
|         current_position[Y_AXIS] = Y_CENTER; | ||||
|         current_position.set(X_CENTER, Y_CENTER); | ||||
|         break; | ||||
|     #endif | ||||
|   } | ||||
|   planner.buffer_line(current_position, MMM_TO_MMS(manual_feedrate_mm_m[X_AXIS]), active_extruder); | ||||
|   line_to_current_position(MMM_TO_MMS(manual_feedrate_mm_m.x)); | ||||
|   line_to_z(LEVEL_CORNERS_HEIGHT); | ||||
|   if (++bed_corner > 3 | ||||
|     #if ENABLED(LEVEL_CENTER_TOO) | ||||
|   | ||||
| @@ -121,7 +121,7 @@ | ||||
|     // Encoder knob or keypad buttons adjust the Z position | ||||
|     // | ||||
|     if (ui.encoderPosition) { | ||||
|       const float z = current_position[Z_AXIS] + float(int16_t(ui.encoderPosition)) * (MESH_EDIT_Z_STEP); | ||||
|       const float z = current_position.z + float(int16_t(ui.encoderPosition)) * (MESH_EDIT_Z_STEP); | ||||
|       line_to_z(constrain(z, -(LCD_PROBE_Z_RANGE) * 0.5f, (LCD_PROBE_Z_RANGE) * 0.5f)); | ||||
|       ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); | ||||
|       ui.encoderPosition = 0; | ||||
| @@ -131,7 +131,7 @@ | ||||
|     // Draw on first display, then only on Z change | ||||
|     // | ||||
|     if (ui.should_draw()) { | ||||
|       const float v = current_position[Z_AXIS]; | ||||
|       const float v = current_position.z; | ||||
|       draw_edit_screen(PSTR(MSG_MOVE_Z), ftostr43sign(v + (v < 0 ? -0.0001f : 0.0001f), '+')); | ||||
|     } | ||||
|   } | ||||
| @@ -279,7 +279,7 @@ void menu_bed_leveling() { | ||||
|   #if ENABLED(BABYSTEP_ZPROBE_OFFSET) | ||||
|     MENU_ITEM(submenu, MSG_ZPROBE_ZOFFSET, lcd_babystep_zoffset); | ||||
|   #elif HAS_BED_PROBE | ||||
|     MENU_ITEM_EDIT(float52, MSG_ZPROBE_ZOFFSET, &probe_offset[Z_AXIS], Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX); | ||||
|     MENU_ITEM_EDIT(float52, MSG_ZPROBE_ZOFFSET, &probe_offset.z, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX); | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(LEVEL_BED_CORNERS) | ||||
|   | ||||
| @@ -145,12 +145,12 @@ static void lcd_factory_settings() { | ||||
|     START_MENU(); | ||||
|     MENU_BACK(MSG_CONFIGURATION); | ||||
|     #if ENABLED(DUAL_X_CARRIAGE) | ||||
|       MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float51, MSG_X_OFFSET, &hotend_offset[X_AXIS][1], float(X2_HOME_POS - 25), float(X2_HOME_POS + 25), _recalc_offsets); | ||||
|       MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float51, MSG_X_OFFSET, &hotend_offset[1].x, float(X2_HOME_POS - 25), float(X2_HOME_POS + 25), _recalc_offsets); | ||||
|     #else | ||||
|       MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_X_OFFSET, &hotend_offset[X_AXIS][1], -99.0, 99.0, _recalc_offsets); | ||||
|       MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_X_OFFSET, &hotend_offset[1].x, -99.0, 99.0, _recalc_offsets); | ||||
|     #endif | ||||
|     MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_Y_OFFSET, &hotend_offset[Y_AXIS][1], -99.0, 99.0, _recalc_offsets); | ||||
|     MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_Z_OFFSET, &hotend_offset[Z_AXIS][1], Z_PROBE_LOW_POINT, 10.0, _recalc_offsets); | ||||
|     MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_Y_OFFSET, &hotend_offset[1].y, -99.0, 99.0, _recalc_offsets); | ||||
|     MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_Z_OFFSET, &hotend_offset[1].z, Z_PROBE_LOW_POINT, 10.0, _recalc_offsets); | ||||
|     #if ENABLED(EEPROM_SETTINGS) | ||||
|       MENU_ITEM(function, MSG_STORE_EEPROM, lcd_store_settings); | ||||
|     #endif | ||||
| @@ -347,7 +347,7 @@ void menu_configuration() { | ||||
|   #if ENABLED(BABYSTEP_ZPROBE_OFFSET) | ||||
|     MENU_ITEM(submenu, MSG_ZPROBE_ZOFFSET, lcd_babystep_zoffset); | ||||
|   #elif HAS_BED_PROBE | ||||
|     MENU_ITEM_EDIT(float52, MSG_ZPROBE_ZOFFSET, &probe_offset[Z_AXIS], Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX); | ||||
|     MENU_ITEM_EDIT(float52, MSG_ZPROBE_ZOFFSET, &probe_offset.z, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX); | ||||
|   #endif | ||||
|  | ||||
|   const bool busy = printer_busy(); | ||||
|   | ||||
| @@ -40,8 +40,8 @@ | ||||
|   #include "../../lcd/extensible_ui/ui_api.h" | ||||
| #endif | ||||
|  | ||||
| void _man_probe_pt(const float &rx, const float &ry) { | ||||
|   do_blocking_move_to(rx, ry, Z_CLEARANCE_BETWEEN_PROBES); | ||||
| void _man_probe_pt(const xy_pos_t &xy) { | ||||
|   do_blocking_move_to(xy, Z_CLEARANCE_BETWEEN_PROBES); | ||||
|   ui.synchronize(); | ||||
|   move_menu_scale = _MAX(PROBE_MANUALLY_STEP, MIN_STEPS_PER_SEGMENT / float(DEFAULT_XYZ_STEPS_PER_UNIT)); | ||||
|   ui.goto_screen(lcd_move_z); | ||||
| @@ -51,8 +51,8 @@ void _man_probe_pt(const float &rx, const float &ry) { | ||||
|  | ||||
|   #include "../../gcode/gcode.h" | ||||
|  | ||||
|   float lcd_probe_pt(const float &rx, const float &ry) { | ||||
|     _man_probe_pt(rx, ry); | ||||
|   float lcd_probe_pt(const xy_pos_t &xy) { | ||||
|     _man_probe_pt(xy); | ||||
|     KEEPALIVE_STATE(PAUSED_FOR_USER); | ||||
|     ui.defer_status_screen(); | ||||
|     wait_for_user = true; | ||||
| @@ -64,7 +64,7 @@ void _man_probe_pt(const float &rx, const float &ry) { | ||||
|     #endif | ||||
|     while (wait_for_user) idle(); | ||||
|     ui.goto_previous_screen_no_defer(); | ||||
|     return current_position[Z_AXIS]; | ||||
|     return current_position.z; | ||||
|   } | ||||
|  | ||||
| #endif | ||||
| @@ -83,10 +83,14 @@ void _man_probe_pt(const float &rx, const float &ry) { | ||||
|     ui.goto_screen(_lcd_calibrate_homing); | ||||
|   } | ||||
|  | ||||
|   void _goto_tower_x() { _man_probe_pt(cos(RADIANS(210)) * delta_calibration_radius, sin(RADIANS(210)) * delta_calibration_radius); } | ||||
|   void _goto_tower_y() { _man_probe_pt(cos(RADIANS(330)) * delta_calibration_radius, sin(RADIANS(330)) * delta_calibration_radius); } | ||||
|   void _goto_tower_z() { _man_probe_pt(cos(RADIANS( 90)) * delta_calibration_radius, sin(RADIANS( 90)) * delta_calibration_radius); } | ||||
|   void _goto_center()  { _man_probe_pt(0,0); } | ||||
|   void _goto_tower_a(const float &a) { | ||||
|     xy_pos_t tower_vec = { cos(RADIANS(a)), sin(RADIANS(a)) }; | ||||
|     _man_probe_pt(tower_vec * delta_calibration_radius); | ||||
|   } | ||||
|   void _goto_tower_x() { _goto_tower_a(210); } | ||||
|   void _goto_tower_y() { _goto_tower_a(330); } | ||||
|   void _goto_tower_z() { _goto_tower_a( 90); } | ||||
|   void _goto_center()  { xy_pos_t ctr{0}; _man_probe_pt(ctr); } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| @@ -101,15 +105,15 @@ void lcd_delta_settings() { | ||||
|   START_MENU(); | ||||
|   MENU_BACK(MSG_DELTA_CALIBRATE); | ||||
|   MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_HEIGHT, &delta_height, delta_height - 10, delta_height + 10, _recalc_delta_settings); | ||||
|   #define EDIT_ENDSTOP_ADJ(LABEL,N) MENU_ITEM_EDIT_CALLBACK(float43, LABEL, &delta_endstop_adj[_AXIS(N)], -5, 5, _recalc_delta_settings) | ||||
|   EDIT_ENDSTOP_ADJ("Ex",A); | ||||
|   EDIT_ENDSTOP_ADJ("Ey",B); | ||||
|   EDIT_ENDSTOP_ADJ("Ez",C); | ||||
|   #define EDIT_ENDSTOP_ADJ(LABEL,N) MENU_ITEM_EDIT_CALLBACK(float43, LABEL, &delta_endstop_adj.N, -5, 5, _recalc_delta_settings) | ||||
|   EDIT_ENDSTOP_ADJ("Ex",a); | ||||
|   EDIT_ENDSTOP_ADJ("Ey",b); | ||||
|   EDIT_ENDSTOP_ADJ("Ez",c); | ||||
|   MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_RADIUS, &delta_radius, delta_radius - 5, delta_radius + 5, _recalc_delta_settings); | ||||
|   #define EDIT_ANGLE_TRIM(LABEL,N) MENU_ITEM_EDIT_CALLBACK(float43, LABEL, &delta_tower_angle_trim[_AXIS(N)], -5, 5, _recalc_delta_settings) | ||||
|   EDIT_ANGLE_TRIM("Tx",A); | ||||
|   EDIT_ANGLE_TRIM("Ty",B); | ||||
|   EDIT_ANGLE_TRIM("Tz",C); | ||||
|   #define EDIT_ANGLE_TRIM(LABEL,N) MENU_ITEM_EDIT_CALLBACK(float43, LABEL, &delta_tower_angle_trim.N, -5, 5, _recalc_delta_settings) | ||||
|   EDIT_ANGLE_TRIM("Tx",a); | ||||
|   EDIT_ANGLE_TRIM("Ty",b); | ||||
|   EDIT_ANGLE_TRIM("Tz",c); | ||||
|   MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_DIAG_ROD, &delta_diagonal_rod, delta_diagonal_rod - 5, delta_diagonal_rod + 5, _recalc_delta_settings); | ||||
|   END_MENU(); | ||||
| } | ||||
|   | ||||
| @@ -92,26 +92,26 @@ static void _lcd_move_xyz(PGM_P name, AxisEnum axis) { | ||||
|       if (soft_endstops_enabled) switch (axis) { | ||||
|         case X_AXIS: | ||||
|           #if ENABLED(MIN_SOFTWARE_ENDSTOP_X) | ||||
|             min = soft_endstop[X_AXIS].min; | ||||
|             min = soft_endstop.min.x; | ||||
|           #endif | ||||
|           #if ENABLED(MAX_SOFTWARE_ENDSTOP_X) | ||||
|             max = soft_endstop[X_AXIS].max; | ||||
|             max = soft_endstop.max.x; | ||||
|           #endif | ||||
|           break; | ||||
|         case Y_AXIS: | ||||
|           #if ENABLED(MIN_SOFTWARE_ENDSTOP_Y) | ||||
|             min = soft_endstop[Y_AXIS].min; | ||||
|             min = soft_endstop.min.y; | ||||
|           #endif | ||||
|           #if ENABLED(MAX_SOFTWARE_ENDSTOP_Y) | ||||
|             max = soft_endstop[Y_AXIS].max; | ||||
|             max = soft_endstop.max.y; | ||||
|           #endif | ||||
|           break; | ||||
|         case Z_AXIS: | ||||
|           #if ENABLED(MIN_SOFTWARE_ENDSTOP_Z) | ||||
|             min = soft_endstop[Z_AXIS].min; | ||||
|             min = soft_endstop.min.z; | ||||
|           #endif | ||||
|           #if ENABLED(MAX_SOFTWARE_ENDSTOP_Z) | ||||
|             max = soft_endstop[Z_AXIS].max; | ||||
|             max = soft_endstop.max.z; | ||||
|           #endif | ||||
|         default: break; | ||||
|       } | ||||
| @@ -173,7 +173,7 @@ void lcd_move_z() { _lcd_move_xyz(PSTR(MSG_MOVE_Z), Z_AXIS); } | ||||
|         #if IS_KINEMATIC | ||||
|           manual_move_offset += diff; | ||||
|         #else | ||||
|           current_position[E_AXIS] += diff; | ||||
|           current_position.e += diff; | ||||
|         #endif | ||||
|         manual_move_to_current(E_AXIS | ||||
|           #if E_MANUAL > 1 | ||||
| @@ -207,7 +207,7 @@ void lcd_move_z() { _lcd_move_xyz(PSTR(MSG_MOVE_Z), Z_AXIS); } | ||||
|         } | ||||
|       #endif // E_MANUAL > 1 | ||||
|  | ||||
|       draw_edit_screen(pos_label, ftostr41sign(current_position[E_AXIS] | ||||
|       draw_edit_screen(pos_label, ftostr41sign(current_position.e | ||||
|         #if IS_KINEMATIC | ||||
|           + manual_move_offset | ||||
|         #endif | ||||
| @@ -267,7 +267,7 @@ void _menu_move_distance(const AxisEnum axis, const screenFunc_t func, const int | ||||
|       case Z_AXIS: STATIC_ITEM(MSG_MOVE_Z, SS_CENTER|SS_INVERT); break; | ||||
|       default: | ||||
|         #if ENABLED(MANUAL_E_MOVES_RELATIVE) | ||||
|           manual_move_e_origin = current_position[E_AXIS]; | ||||
|           manual_move_e_origin = current_position.e; | ||||
|         #endif | ||||
|         STATIC_ITEM(MSG_MOVE_E, SS_CENTER|SS_INVERT); | ||||
|         break; | ||||
| @@ -345,7 +345,7 @@ void menu_move() { | ||||
|   ) { | ||||
|     if ( | ||||
|       #if ENABLED(DELTA) | ||||
|         current_position[Z_AXIS] <= delta_clip_start_height | ||||
|         current_position.z <= delta_clip_start_height | ||||
|       #else | ||||
|         true | ||||
|       #endif | ||||
|   | ||||
| @@ -432,18 +432,16 @@ void _lcd_ubl_map_lcd_edit_cmd() { | ||||
| void ubl_map_move_to_xy() { | ||||
|   const feedRate_t fr_mm_s = MMM_TO_MMS(XY_PROBE_SPEED); | ||||
|  | ||||
|   set_destination_from_current(); // sync destination at the start | ||||
|   destination = current_position;          // sync destination at the start | ||||
|  | ||||
|   #if ENABLED(DELTA) | ||||
|     if (current_position[Z_AXIS] > delta_clip_start_height) { | ||||
|       destination[Z_AXIS] = delta_clip_start_height; | ||||
|     if (current_position.z > delta_clip_start_height) { | ||||
|       destination.z = delta_clip_start_height; | ||||
|       prepare_internal_move_to_destination(fr_mm_s); | ||||
|     } | ||||
|   #endif | ||||
|  | ||||
|   destination[X_AXIS] = pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]); | ||||
|   destination[Y_AXIS] = pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]); | ||||
|  | ||||
|   destination.set(ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot)); | ||||
|   prepare_internal_move_to_destination(fr_mm_s); | ||||
| } | ||||
|  | ||||
| @@ -491,9 +489,8 @@ void _lcd_ubl_output_map_lcd() { | ||||
|     if (y_plot < 0) y_plot = GRID_MAX_POINTS_Y - 1; | ||||
|  | ||||
|     #if IS_KINEMATIC | ||||
|       const float x = pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]), | ||||
|                   y = pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]); | ||||
|       if (position_is_reachable(x, y)) break; // Found a valid point | ||||
|       const xy_pos_t xy = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) }; | ||||
|       if (position_is_reachable(xy)) break; // Found a valid point | ||||
|       x_plot += (step_scaler < 0) ? -1 : 1; | ||||
|     #endif | ||||
|  | ||||
|   | ||||
| @@ -671,7 +671,7 @@ void MarlinUI::quick_feedback(const bool clear_buttons/*=true*/) { | ||||
|         #endif | ||||
|  | ||||
|         // Set movement on a single axis | ||||
|         set_destination_from_current(); | ||||
|         destination = current_position; | ||||
|         destination[manual_move_axis] += manual_move_offset; | ||||
|  | ||||
|         // Reset for the next move | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user