Browse Source

Add custom types for position (#15204)

Scott Lahteine 4 years ago
parent
commit
50e4545255
No account linked to committer's email address
100 changed files with 2095 additions and 1844 deletions
  1. 1
    3
      Marlin/Configuration_adv.h
  2. 6
    6
      Marlin/src/Marlin.cpp
  3. 0
    63
      Marlin/src/core/enum.h
  4. 1
    6
      Marlin/src/core/macros.h
  5. 1
    6
      Marlin/src/core/serial.cpp
  6. 8
    4
      Marlin/src/core/serial.h
  7. 486
    0
      Marlin/src/core/types.h
  8. 20
    30
      Marlin/src/core/utility.cpp
  9. 20
    6
      Marlin/src/core/utility.h
  10. 18
    21
      Marlin/src/feature/I2CPositionEncoder.cpp
  11. 0
    2
      Marlin/src/feature/I2CPositionEncoder.h
  12. 12
    12
      Marlin/src/feature/backlash.cpp
  13. 4
    4
      Marlin/src/feature/backlash.h
  14. 78
    84
      Marlin/src/feature/bedlevel/abl/abl.cpp
  15. 6
    6
      Marlin/src/feature/bedlevel/abl/abl.h
  16. 16
    16
      Marlin/src/feature/bedlevel/bedlevel.cpp
  17. 16
    6
      Marlin/src/feature/bedlevel/bedlevel.h
  18. 26
    29
      Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp
  19. 20
    14
      Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h
  20. 2
    3
      Marlin/src/feature/bedlevel/ubl/ubl.cpp
  21. 27
    20
      Marlin/src/feature/bedlevel/ubl/ubl.h
  22. 239
    251
      Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
  23. 156
    199
      Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp
  24. 3
    3
      Marlin/src/feature/dac/dac_mcp4728.cpp
  25. 3
    1
      Marlin/src/feature/dac/dac_mcp4728.h
  26. 4
    4
      Marlin/src/feature/dac/stepper_dac.cpp
  27. 1
    1
      Marlin/src/feature/dac/stepper_dac.h
  28. 7
    7
      Marlin/src/feature/fwretract.cpp
  29. 13
    16
      Marlin/src/feature/joystick.cpp
  30. 3
    1
      Marlin/src/feature/joystick.h
  31. 11
    11
      Marlin/src/feature/pause.cpp
  32. 1
    1
      Marlin/src/feature/pause.h
  33. 15
    17
      Marlin/src/feature/power_loss_recovery.cpp
  34. 3
    3
      Marlin/src/feature/power_loss_recovery.h
  35. 8
    8
      Marlin/src/feature/prusa_MMU2/mmu2.cpp
  36. 2
    2
      Marlin/src/feature/runout.h
  37. 2
    2
      Marlin/src/feature/tmc_util.h
  38. 142
    155
      Marlin/src/gcode/bedlevel/G26.cpp
  39. 6
    5
      Marlin/src/gcode/bedlevel/G42.cpp
  40. 5
    6
      Marlin/src/gcode/bedlevel/M420.cpp
  41. 143
    173
      Marlin/src/gcode/bedlevel/abl/G29.cpp
  42. 4
    4
      Marlin/src/gcode/bedlevel/mbl/G29.cpp
  43. 8
    11
      Marlin/src/gcode/bedlevel/ubl/M421.cpp
  44. 22
    28
      Marlin/src/gcode/calibrate/G28.cpp
  45. 72
    94
      Marlin/src/gcode/calibrate/G33.cpp
  46. 26
    15
      Marlin/src/gcode/calibrate/G34_M422.cpp
  47. 62
    92
      Marlin/src/gcode/calibrate/G425.cpp
  48. 17
    17
      Marlin/src/gcode/calibrate/M48.cpp
  49. 11
    11
      Marlin/src/gcode/calibrate/M665.cpp
  50. 5
    5
      Marlin/src/gcode/config/M200-M205.cpp
  51. 7
    7
      Marlin/src/gcode/config/M218.cpp
  52. 1
    1
      Marlin/src/gcode/config/M92.cpp
  53. 4
    8
      Marlin/src/gcode/control/M211.cpp
  54. 4
    4
      Marlin/src/gcode/control/M605.cpp
  55. 16
    16
      Marlin/src/gcode/feature/camera/M240.cpp
  56. 2
    3
      Marlin/src/gcode/feature/pause/M125.cpp
  57. 3
    3
      Marlin/src/gcode/feature/pause/M600.cpp
  58. 6
    7
      Marlin/src/gcode/feature/pause/M701_M702.cpp
  59. 5
    5
      Marlin/src/gcode/feature/trinamic/M122.cpp
  60. 8
    8
      Marlin/src/gcode/feature/trinamic/M911-M914.cpp
  61. 10
    9
      Marlin/src/gcode/gcode.cpp
  62. 1
    1
      Marlin/src/gcode/gcode.h
  63. 2
    2
      Marlin/src/gcode/geometry/G53-G59.cpp
  64. 2
    2
      Marlin/src/gcode/geometry/G92.cpp
  65. 1
    1
      Marlin/src/gcode/geometry/M206_M428.cpp
  66. 20
    20
      Marlin/src/gcode/host/M114.cpp
  67. 5
    5
      Marlin/src/gcode/motion/G0_G1.cpp
  68. 34
    39
      Marlin/src/gcode/motion/G2_G3.cpp
  69. 5
    11
      Marlin/src/gcode/motion/G5.cpp
  70. 11
    11
      Marlin/src/gcode/motion/M290.cpp
  71. 5
    6
      Marlin/src/gcode/probe/G30.cpp
  72. 3
    4
      Marlin/src/gcode/probe/G38.cpp
  73. 6
    6
      Marlin/src/gcode/probe/M851.cpp
  74. 1
    1
      Marlin/src/gcode/scara/M360-M364.cpp
  75. 6
    5
      Marlin/src/inc/Conditionals_post.h
  76. 1
    1
      Marlin/src/inc/MarlinConfig.h
  77. 0
    1
      Marlin/src/inc/MarlinConfigPre.h
  78. 2
    5
      Marlin/src/inc/SanityCheck.h
  79. 9
    11
      Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp
  80. 4
    3
      Marlin/src/lcd/dogm/status_screen_DOGM.cpp
  81. 6
    6
      Marlin/src/lcd/dogm/status_screen_lite_ST7920.cpp
  82. 2
    1
      Marlin/src/lcd/dogm/status_screen_lite_ST7920.h
  83. 4
    2
      Marlin/src/lcd/dogm/ultralcd_DOGM.cpp
  84. 4
    4
      Marlin/src/lcd/extensible_ui/lib/dgus/DGUSDisplayDefinition.cpp
  85. 16
    17
      Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/bio_status_screen.cpp
  86. 2
    2
      Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/change_filament_screen.cpp
  87. 2
    2
      Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/move_axis_screen.cpp
  88. 11
    12
      Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/nudge_nozzle_screen.cpp
  89. 1
    1
      Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/screen_data.h
  90. 34
    40
      Marlin/src/lcd/extensible_ui/ui_api.cpp
  91. 5
    4
      Marlin/src/lcd/extensible_ui/ui_api.h
  92. 9
    9
      Marlin/src/lcd/menu/menu.cpp
  93. 2
    2
      Marlin/src/lcd/menu/menu_advanced.cpp
  94. 6
    8
      Marlin/src/lcd/menu/menu_bed_corners.cpp
  95. 3
    3
      Marlin/src/lcd/menu/menu_bed_leveling.cpp
  96. 5
    5
      Marlin/src/lcd/menu/menu_configuration.cpp
  97. 21
    17
      Marlin/src/lcd/menu/menu_delta_calibrate.cpp
  98. 10
    10
      Marlin/src/lcd/menu/menu_motion.cpp
  99. 6
    9
      Marlin/src/lcd/menu/menu_ubl.cpp
  100. 0
    0
      Marlin/src/lcd/ultralcd.cpp

+ 1
- 3
Marlin/Configuration_adv.h View File

@@ -602,9 +602,7 @@
602 602
 //#define Z_STEPPER_AUTO_ALIGN
603 603
 #if ENABLED(Z_STEPPER_AUTO_ALIGN)
604 604
   // Define probe X and Y positions for Z1, Z2 [, Z3]
605
-  #define Z_STEPPER_ALIGN_X {  10, 150, 290 }
606
-  #define Z_STEPPER_ALIGN_Y { 290,  10, 290 }
607
-  // Set number of iterations to align
605
+  #define Z_STEPPER_ALIGN_XY { {  10, 290 }, { 150,  10 }, { 290, 290 } }  // Set number of iterations to align
608 606
   #define Z_STEPPER_ALIGN_ITERATIONS 3
609 607
   // Enable to restore leveling setup after operation
610 608
   #define RESTORE_LEVELING_AFTER_G34

+ 6
- 6
Marlin/src/Marlin.cpp View File

@@ -582,10 +582,10 @@ void manage_inactivity(const bool ignore_stepper_queue/*=false*/) {
582 582
         }
583 583
       #endif // !SWITCHING_EXTRUDER
584 584
 
585
-      const float olde = current_position[E_AXIS];
586
-      current_position[E_AXIS] += EXTRUDER_RUNOUT_EXTRUDE;
587
-      planner.buffer_line(current_position, MMM_TO_MMS(EXTRUDER_RUNOUT_SPEED), active_extruder);
588
-      current_position[E_AXIS] = olde;
585
+      const float olde = current_position.e;
586
+      current_position.e += EXTRUDER_RUNOUT_EXTRUDE;
587
+      line_to_current_position(MMM_TO_MMS(EXTRUDER_RUNOUT_SPEED));
588
+      current_position.e = olde;
589 589
       planner.set_e_position_mm(olde);
590 590
       planner.synchronize();
591 591
 
@@ -629,7 +629,7 @@ void manage_inactivity(const bool ignore_stepper_queue/*=false*/) {
629 629
     if (delayed_move_time && ELAPSED(ms, delayed_move_time + 1000UL) && IsRunning()) {
630 630
       // travel moves have been received so enact them
631 631
       delayed_move_time = 0xFFFFFFFFUL; // force moves to be done
632
-      set_destination_from_current();
632
+      destination = current_position;
633 633
       prepare_move_to_destination();
634 634
     }
635 635
   #endif
@@ -1002,7 +1002,7 @@ void setup() {
1002 1002
 
1003 1003
   #if HAS_M206_COMMAND
1004 1004
     // Initialize current position based on home_offset
1005
-    LOOP_XYZ(a) current_position[a] += home_offset[a];
1005
+    current_position += home_offset;
1006 1006
   #endif
1007 1007
 
1008 1008
   // Vital to init stepper/planner equivalent for current_position

+ 0
- 63
Marlin/src/core/enum.h View File

@@ -1,63 +0,0 @@
1
-/**
2
- * Marlin 3D Printer Firmware
3
- * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
- *
5
- * Based on Sprinter and grbl.
6
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
- *
8
- * This program is free software: you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License as published by
10
- * the Free Software Foundation, either version 3 of the License, or
11
- * (at your option) any later version.
12
- *
13
- * This program is distributed in the hope that it will be useful,
14
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
- * GNU General Public License for more details.
17
- *
18
- * You should have received a copy of the GNU General Public License
19
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
- *
21
- */
22
-#pragma once
23
-
24
-/**
25
- * Axis indices as enumerated constants
26
- *
27
- *  - X_AXIS, Y_AXIS, and Z_AXIS should be used for axes in Cartesian space
28
- *  - A_AXIS, B_AXIS, and C_AXIS should be used for Steppers, corresponding to XYZ on Cartesians
29
- *  - X_HEAD, Y_HEAD, and Z_HEAD should be used for Steppers on Core kinematics
30
- */
31
-enum AxisEnum : unsigned char {
32
-  X_AXIS    = 0,
33
-  A_AXIS    = 0,
34
-  Y_AXIS    = 1,
35
-  B_AXIS    = 1,
36
-  Z_AXIS    = 2,
37
-  C_AXIS    = 2,
38
-  E_AXIS    = 3,
39
-  X_HEAD    = 4,
40
-  Y_HEAD    = 5,
41
-  Z_HEAD    = 6,
42
-  E0_AXIS   = 3,
43
-  E1_AXIS   = 4,
44
-  E2_AXIS   = 5,
45
-  E3_AXIS   = 6,
46
-  E4_AXIS   = 7,
47
-  E5_AXIS   = 8,
48
-  ALL_AXES  = 0xFE,
49
-  NO_AXIS   = 0xFF
50
-};
51
-
52
-#define LOOP_S_LE_N(VAR, S, N) for (uint8_t VAR=(S); VAR<=(N); VAR++)
53
-#define LOOP_S_L_N(VAR, S, N) for (uint8_t VAR=(S); VAR<(N); VAR++)
54
-#define LOOP_LE_N(VAR, N) LOOP_S_LE_N(VAR, 0, N)
55
-#define LOOP_L_N(VAR, N) LOOP_S_L_N(VAR, 0, N)
56
-
57
-#define LOOP_NA(VAR) LOOP_L_N(VAR, NUM_AXIS)
58
-#define LOOP_XYZ(VAR) LOOP_S_LE_N(VAR, X_AXIS, Z_AXIS)
59
-#define LOOP_XYZE(VAR) LOOP_S_LE_N(VAR, X_AXIS, E_AXIS)
60
-#define LOOP_XYZE_N(VAR) LOOP_S_L_N(VAR, X_AXIS, XYZE_N)
61
-#define LOOP_ABC(VAR) LOOP_S_LE_N(VAR, A_AXIS, C_AXIS)
62
-#define LOOP_ABCE(VAR) LOOP_S_LE_N(VAR, A_AXIS, E_AXIS)
63
-#define LOOP_ABCE_N(VAR) LOOP_S_L_N(VAR, A_AXIS, XYZE_N)

+ 1
- 6
Marlin/src/core/macros.h View File

@@ -26,6 +26,7 @@
26 26
 #define XYZE 4
27 27
 #define ABC  3
28 28
 #define XYZ  3
29
+#define XY   2
29 30
 
30 31
 #define _AXIS(A) (A##_AXIS)
31 32
 
@@ -252,12 +253,6 @@
252 253
 #define DECREMENT_(n) DEC_##n
253 254
 #define DECREMENT(n) DECREMENT_(n)
254 255
 
255
-// Feedrate
256
-typedef float feedRate_t;
257
-#define MMM_TO_MMS(MM_M) ((MM_M)/60.0f)
258
-#define MMS_TO_MMM(MM_S) ((MM_S)*60.0f)
259
-#define MMS_SCALED(V)    ((V) * 0.01f * feedrate_percentage)
260
-
261 256
 #define NOOP (void(0))
262 257
 
263 258
 #define CEILING(x,y) (((x) + (y) - 1) / (y))

+ 1
- 6
Marlin/src/core/serial.cpp View File

@@ -22,7 +22,6 @@
22 22
 
23 23
 #include "serial.h"
24 24
 #include "language.h"
25
-#include "enum.h"
26 25
 
27 26
 uint8_t marlin_debug_flags = MARLIN_DEBUG_NONE;
28 27
 
@@ -68,12 +67,8 @@ void print_bin(const uint16_t val) {
68 67
   }
69 68
 }
70 69
 
71
-void print_xyz(PGM_P const prefix, PGM_P const suffix, const float &x, const float &y, const float &z) {
70
+void print_xyz(const float &x, const float &y, const float &z, PGM_P const prefix/*=nullptr*/, PGM_P const suffix/*=nullptr*/) {
72 71
   serialprintPGM(prefix);
73 72
   SERIAL_ECHOPAIR(" " MSG_X, x, " " MSG_Y, y, " " MSG_Z, z);
74 73
   if (suffix) serialprintPGM(suffix); else SERIAL_EOL();
75 74
 }
76
-
77
-void print_xyz(PGM_P const prefix, PGM_P const suffix, const float xyz[]) {
78
-  print_xyz(prefix, suffix, xyz[X_AXIS], xyz[Y_AXIS], xyz[Z_AXIS]);
79
-}

+ 8
- 4
Marlin/src/core/serial.h View File

@@ -213,7 +213,11 @@ void serial_spaces(uint8_t count);
213 213
 
214 214
 void print_bin(const uint16_t val);
215 215
 
216
-void print_xyz(PGM_P const prefix, PGM_P const suffix, const float xyz[]);
217
-void print_xyz(PGM_P const prefix, PGM_P const suffix, const float &x, const float &y, const float &z);
218
-#define SERIAL_POS(SUFFIX,VAR) do { print_xyz(PSTR("  " STRINGIFY(VAR) "="), PSTR(" : " SUFFIX "\n"), VAR); }while(0)
219
-#define SERIAL_XYZ(PREFIX,V...) do { print_xyz(PSTR(PREFIX), nullptr, V); }while(0)
216
+void print_xyz(const float &x, const float &y, const float &z, PGM_P const prefix=nullptr, PGM_P const suffix=nullptr);
217
+
218
+inline void print_xyz(const xyz_pos_t &xyz, PGM_P const prefix=nullptr, PGM_P const suffix=nullptr) {
219
+  print_xyz(xyz.x, xyz.y, xyz.z, prefix, suffix);
220
+}
221
+
222
+#define SERIAL_POS(SUFFIX,VAR) do { print_xyz(VAR, PSTR("  " STRINGIFY(VAR) "="), PSTR(" : " SUFFIX "\n")); }while(0)
223
+#define SERIAL_XYZ(PREFIX,V...) do { print_xyz(V, PSTR(PREFIX), nullptr); }while(0)

+ 486
- 0
Marlin/src/core/types.h View File

@@ -0,0 +1,486 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#pragma once
23
+
24
+#include <math.h>
25
+#include <stddef.h>
26
+
27
+#include "millis_t.h"
28
+
29
+//
30
+// Enumerated axis indices
31
+//
32
+//  - X_AXIS, Y_AXIS, and Z_AXIS should be used for axes in Cartesian space
33
+//  - A_AXIS, B_AXIS, and C_AXIS should be used for Steppers, corresponding to XYZ on Cartesians
34
+//  - X_HEAD, Y_HEAD, and Z_HEAD should be used for Steppers on Core kinematics
35
+//
36
+enum AxisEnum : uint8_t {
37
+  X_AXIS   = 0, A_AXIS = 0,
38
+  Y_AXIS   = 1, B_AXIS = 1,
39
+  Z_AXIS   = 2, C_AXIS = 2,
40
+  E_AXIS   = 3,
41
+  X_HEAD   = 4, Y_HEAD = 5, Z_HEAD = 6,
42
+  E0_AXIS  = 3,
43
+  E1_AXIS  = 4,
44
+  E2_AXIS  = 5,
45
+  E3_AXIS  = 6,
46
+  E4_AXIS  = 7,
47
+  E5_AXIS  = 8,
48
+  ALL_AXES = 0xFE, NO_AXIS = 0xFF
49
+};
50
+
51
+//
52
+// Loop over XYZE axes
53
+//
54
+
55
+#define LOOP_S_LE_N(VAR, S, N) for (uint8_t VAR=(S); VAR<=(N); VAR++)
56
+#define LOOP_S_L_N(VAR, S, N) for (uint8_t VAR=(S); VAR<(N); VAR++)
57
+#define LOOP_LE_N(VAR, N) LOOP_S_LE_N(VAR, 0, N)
58
+#define LOOP_L_N(VAR, N) LOOP_S_L_N(VAR, 0, N)
59
+
60
+#define LOOP_XYZ(VAR) LOOP_S_LE_N(VAR, X_AXIS, Z_AXIS)
61
+#define LOOP_XYZE(VAR) LOOP_S_LE_N(VAR, X_AXIS, E_AXIS)
62
+#define LOOP_XYZE_N(VAR) LOOP_S_L_N(VAR, X_AXIS, XYZE_N)
63
+#define LOOP_ABC(VAR) LOOP_S_LE_N(VAR, A_AXIS, C_AXIS)
64
+#define LOOP_ABCE(VAR) LOOP_S_LE_N(VAR, A_AXIS, E_AXIS)
65
+#define LOOP_ABCE_N(VAR) LOOP_S_L_N(VAR, A_AXIS, XYZE_N)
66
+
67
+//
68
+// Conditional type assignment magic. For example...
69
+//
70
+// typename IF<(MYOPT==12), int, float>::type myvar;
71
+//
72
+template <bool, class L, class R>
73
+struct IF { typedef R type; };
74
+template <class L, class R>
75
+struct IF<true, L, R> { typedef L type; };
76
+
77
+//
78
+// feedRate_t is just a humble float
79
+//
80
+typedef float feedRate_t;
81
+
82
+// Conversion macros
83
+#define MMM_TO_MMS(MM_M) feedRate_t(float(MM_M) / 60.0f)
84
+#define MMS_TO_MMM(MM_S) (float(MM_S) * 60.0f)
85
+#define MMS_SCALED(V)    ((V) * 0.01f * feedrate_percentage)
86
+
87
+//
88
+// Coordinates structures for XY, XYZ, XYZE...
89
+//
90
+
91
+// Helpers
92
+#define _RECIP(N) ((N) ? 1.0f / float(N) : 0.0f)
93
+#define _ABS(N) ((N) < 0 ? -(N) : (N))
94
+#define _LS(N)  (N = (T)(uint32_t(N) << v))
95
+#define _RS(N)  (N = (T)(uint32_t(N) >> v))
96
+#define FI FORCE_INLINE
97
+
98
+// Forward declarations
99
+template<typename T> struct XYval;
100
+template<typename T> struct XYZval;
101
+template<typename T> struct XYZEval;
102
+
103
+typedef struct XYval<bool>          xy_bool_t;
104
+typedef struct XYZval<bool>        xyz_bool_t;
105
+typedef struct XYZEval<bool>      xyze_bool_t;
106
+
107
+typedef struct XYval<char>          xy_char_t;
108
+typedef struct XYZval<char>        xyz_char_t;
109
+typedef struct XYZEval<char>      xyze_char_t;
110
+
111
+typedef struct XYval<unsigned char>     xy_uchar_t;
112
+typedef struct XYZval<unsigned char>   xyz_uchar_t;
113
+typedef struct XYZEval<unsigned char> xyze_uchar_t;
114
+
115
+typedef struct XYval<int8_t>        xy_int8_t;
116
+typedef struct XYZval<int8_t>      xyz_int8_t;
117
+typedef struct XYZEval<int8_t>    xyze_int8_t;
118
+
119
+typedef struct XYval<uint8_t>      xy_uint8_t;
120
+typedef struct XYZval<uint8_t>    xyz_uint8_t;
121
+typedef struct XYZEval<uint8_t>  xyze_uint8_t;
122
+
123
+typedef struct XYval<int16_t>        xy_int_t;
124
+typedef struct XYZval<int16_t>      xyz_int_t;
125
+typedef struct XYZEval<int16_t>    xyze_int_t;
126
+
127
+typedef struct XYval<uint16_t>      xy_uint_t;
128
+typedef struct XYZval<uint16_t>    xyz_uint_t;
129
+typedef struct XYZEval<uint16_t>  xyze_uint_t;
130
+
131
+typedef struct XYval<int32_t>       xy_long_t;
132
+typedef struct XYZval<int32_t>     xyz_long_t;
133
+typedef struct XYZEval<int32_t>   xyze_long_t;
134
+
135
+typedef struct XYval<uint32_t>     xy_ulong_t;
136
+typedef struct XYZval<uint32_t>   xyz_ulong_t;
137
+typedef struct XYZEval<uint32_t> xyze_ulong_t;
138
+
139
+typedef struct XYZval<volatile int32_t>   xyz_vlong_t;
140
+typedef struct XYZEval<volatile int32_t> xyze_vlong_t;
141
+
142
+typedef struct XYval<float>        xy_float_t;
143
+typedef struct XYZval<float>      xyz_float_t;
144
+typedef struct XYZEval<float>    xyze_float_t;
145
+
146
+typedef struct XYval<feedRate_t>     xy_feedrate_t;
147
+typedef struct XYZval<feedRate_t>   xyz_feedrate_t;
148
+typedef struct XYZEval<feedRate_t> xyze_feedrate_t;
149
+
150
+typedef xy_uint8_t xy_byte_t;
151
+typedef xyz_uint8_t xyz_byte_t;
152
+typedef xyze_uint8_t xyze_byte_t;
153
+
154
+typedef xyz_long_t abc_long_t;
155
+typedef xyze_long_t abce_long_t;
156
+typedef xyz_ulong_t abc_ulong_t;
157
+typedef xyze_ulong_t abce_ulong_t;
158
+
159
+typedef xy_float_t xy_pos_t;
160
+typedef xyz_float_t xyz_pos_t;
161
+typedef xyze_float_t xyze_pos_t;
162
+
163
+typedef xy_float_t ab_float_t;
164
+typedef xyz_float_t abc_float_t;
165
+typedef xyze_float_t abce_float_t;
166
+
167
+typedef ab_float_t ab_pos_t;
168
+typedef abc_float_t abc_pos_t;
169
+typedef abce_float_t abce_pos_t;
170
+
171
+// External conversion methods
172
+void toLogical(xy_pos_t &raw);
173
+void toLogical(xyz_pos_t &raw);
174
+void toLogical(xyze_pos_t &raw);
175
+void toNative(xy_pos_t &raw);
176
+void toNative(xyz_pos_t &raw);
177
+void toNative(xyze_pos_t &raw);
178
+
179
+//
180
+// XY coordinates, counters, etc.
181
+//
182
+template<typename T>
183
+struct XYval {
184
+  union {
185
+    struct { T x, y; };
186
+    struct { T a, b; };
187
+    T pos[2];
188
+  };
189
+  FI void set(const T px)                               { x = px; }
190
+  FI void set(const T px, const T py)                   { x = px; y = py; }
191
+  FI void reset()                                       { x = y = 0; }
192
+  FI T magnitude()                                const { return (T)sqrtf(x*x + y*y); }
193
+  FI operator T* ()                                     { return pos; }
194
+  FI operator bool()                                    { return x || y; }
195
+  FI XYval<T>           copy()                    const { return *this; }
196
+  FI XYval<T>            ABS()                    const { return { T(_ABS(x)), T(_ABS(y)) }; }
197
+  FI XYval<int16_t>    asInt()                          { return { int16_t(x), int16_t(y) }; }
198
+  FI XYval<int16_t>    asInt()                    const { return { int16_t(x), int16_t(y) }; }
199
+  FI XYval<int32_t>   asLong()                          { return { int32_t(x), int32_t(y) }; }
200
+  FI XYval<int32_t>   asLong()                    const { return { int32_t(x), int32_t(y) }; }
201
+  FI XYval<float>    asFloat()                          { return {   float(x),   float(y) }; }
202
+  FI XYval<float>    asFloat()                    const { return {   float(x),   float(y) }; }
203
+  FI XYval<float> reciprocal()                    const { return {  _RECIP(x),  _RECIP(y) }; }
204
+  FI XYval<float>  asLogical()                    const { XYval<float> o = asFloat(); toLogical(o); return o; }
205
+  FI XYval<float>   asNative()                    const { XYval<float> o = asFloat(); toNative(o);  return o; }
206
+  FI operator XYZval<T>()                               { return { x, y }; }
207
+  FI operator XYZval<T>()                         const { return { x, y }; }
208
+  FI operator XYZEval<T>()                              { return { x, y }; }
209
+  FI operator XYZEval<T>()                        const { return { x, y }; }
210
+  FI       T&  operator[](const int i)                  { return pos[i]; }
211
+  FI const T&  operator[](const int i)            const { return pos[i]; }
212
+  FI XYval<T>& operator= (const T v)                    { set(v,    v   ); return *this; }
213
+  FI XYval<T>& operator= (const XYZval<T>  &rs)         { set(rs.x, rs.y); return *this; }
214
+  FI XYval<T>& operator= (const XYZEval<T> &rs)         { set(rs.x, rs.y); return *this; }
215
+  FI XYval<T>  operator+ (const XYval<T>   &rs)   const { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; }
216
+  FI XYval<T>  operator+ (const XYval<T>   &rs)         { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; }
217
+  FI XYval<T>  operator- (const XYval<T>   &rs)   const { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; }
218
+  FI XYval<T>  operator- (const XYval<T>   &rs)         { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; }
219
+  FI XYval<T>  operator* (const XYval<T>   &rs)   const { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; }
220
+  FI XYval<T>  operator* (const XYval<T>   &rs)         { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; }
221
+  FI XYval<T>  operator/ (const XYval<T>   &rs)   const { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; }
222
+  FI XYval<T>  operator/ (const XYval<T>   &rs)         { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; }
223
+  FI XYval<T>  operator+ (const XYZval<T>  &rs)   const { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; }
224
+  FI XYval<T>  operator+ (const XYZval<T>  &rs)         { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; }
225
+  FI XYval<T>  operator- (const XYZval<T>  &rs)   const { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; }
226
+  FI XYval<T>  operator- (const XYZval<T>  &rs)         { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; }
227
+  FI XYval<T>  operator* (const XYZval<T>  &rs)   const { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; }
228
+  FI XYval<T>  operator* (const XYZval<T>  &rs)         { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; }
229
+  FI XYval<T>  operator/ (const XYZval<T>  &rs)   const { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; }
230
+  FI XYval<T>  operator/ (const XYZval<T>  &rs)         { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; }
231
+  FI XYval<T>  operator+ (const XYZEval<T> &rs)   const { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; }
232
+  FI XYval<T>  operator+ (const XYZEval<T> &rs)         { XYval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; }
233
+  FI XYval<T>  operator- (const XYZEval<T> &rs)   const { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; }
234
+  FI XYval<T>  operator- (const XYZEval<T> &rs)         { XYval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; }
235
+  FI XYval<T>  operator* (const XYZEval<T> &rs)   const { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; }
236
+  FI XYval<T>  operator* (const XYZEval<T> &rs)         { XYval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; }
237
+  FI XYval<T>  operator/ (const XYZEval<T> &rs)   const { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; }
238
+  FI XYval<T>  operator/ (const XYZEval<T> &rs)         { XYval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; }
239
+  FI XYval<T>  operator* (const float &v)         const { XYval<T> ls = *this; ls.x *= v;    ls.y *= v;    return ls; }
240
+  FI XYval<T>  operator* (const float &v)               { XYval<T> ls = *this; ls.x *= v;    ls.y *= v;    return ls; }
241
+  FI XYval<T>  operator* (const int &v)           const { XYval<T> ls = *this; ls.x *= v;    ls.y *= v;    return ls; }
242
+  FI XYval<T>  operator* (const int &v)                 { XYval<T> ls = *this; ls.x *= v;    ls.y *= v;    return ls; }
243
+  FI XYval<T>  operator/ (const float &v)         const { XYval<T> ls = *this; ls.x /= v;    ls.y /= v;    return ls; }
244
+  FI XYval<T>  operator/ (const float &v)               { XYval<T> ls = *this; ls.x /= v;    ls.y /= v;    return ls; }
245
+  FI XYval<T>  operator/ (const int &v)           const { XYval<T> ls = *this; ls.x /= v;    ls.y /= v;    return ls; }
246
+  FI XYval<T>  operator/ (const int &v)                 { XYval<T> ls = *this; ls.x /= v;    ls.y /= v;    return ls; }
247
+  FI XYval<T>  operator>>(const int &v)           const { XYval<T> ls = *this; _RS(ls.x);    _RS(ls.y);    return ls; }
248
+  FI XYval<T>  operator>>(const int &v)                 { XYval<T> ls = *this; _RS(ls.x);    _RS(ls.y);    return ls; }
249
+  FI XYval<T>  operator<<(const int &v)           const { XYval<T> ls = *this; _LS(ls.x);    _LS(ls.y);    return ls; }
250
+  FI XYval<T>  operator<<(const int &v)                 { XYval<T> ls = *this; _LS(ls.x);    _LS(ls.y);    return ls; }
251
+  FI XYval<T>& operator+=(const XYval<T>   &rs)         { x += rs.x; y += rs.y; return *this; }
252
+  FI XYval<T>& operator-=(const XYval<T>   &rs)         { x -= rs.x; y -= rs.y; return *this; }
253
+  FI XYval<T>& operator*=(const XYval<T>   &rs)         { x *= rs.x; y *= rs.y; return *this; }
254
+  FI XYval<T>& operator+=(const XYZval<T>  &rs)         { x += rs.x; y += rs.y; return *this; }
255
+  FI XYval<T>& operator-=(const XYZval<T>  &rs)         { x -= rs.x; y -= rs.y; return *this; }
256
+  FI XYval<T>& operator*=(const XYZval<T>  &rs)         { x *= rs.x; y *= rs.y; return *this; }
257
+  FI XYval<T>& operator+=(const XYZEval<T> &rs)         { x += rs.x; y += rs.y; return *this; }
258
+  FI XYval<T>& operator-=(const XYZEval<T> &rs)         { x -= rs.x; y -= rs.y; return *this; }
259
+  FI XYval<T>& operator*=(const XYZEval<T> &rs)         { x *= rs.x; y *= rs.y; return *this; }
260
+  FI XYval<T>& operator*=(const float &v)               { x *= v;    y *= v;    return *this; }
261
+  FI XYval<T>& operator*=(const int &v)                 { x *= v;    y *= v;    return *this; }
262
+  FI XYval<T>& operator>>=(const int &v)                { _RS(x);    _RS(y);    return *this; }
263
+  FI XYval<T>& operator<<=(const int &v)                { _LS(x);    _LS(y);    return *this; }
264
+  FI bool      operator==(const XYval<T>   &rs)         { return x == rs.x && y == rs.y; }
265
+  FI bool      operator==(const XYZval<T>  &rs)         { return x == rs.x && y == rs.y; }
266
+  FI bool      operator==(const XYZEval<T> &rs)         { return x == rs.x && y == rs.y; }
267
+  FI bool      operator==(const XYval<T>   &rs)   const { return x == rs.x && y == rs.y; }
268
+  FI bool      operator==(const XYZval<T>  &rs)   const { return x == rs.x && y == rs.y; }
269
+  FI bool      operator==(const XYZEval<T> &rs)   const { return x == rs.x && y == rs.y; }
270
+  FI bool      operator!=(const XYval<T>   &rs)         { return !operator==(rs); }
271
+  FI bool      operator!=(const XYZval<T>  &rs)         { return !operator==(rs); }
272
+  FI bool      operator!=(const XYZEval<T> &rs)         { return !operator==(rs); }
273
+  FI bool      operator!=(const XYval<T>   &rs)   const { return !operator==(rs); }
274
+  FI bool      operator!=(const XYZval<T>  &rs)   const { return !operator==(rs); }
275
+  FI bool      operator!=(const XYZEval<T> &rs)   const { return !operator==(rs); }
276
+  FI XYval<T>       operator-()                         { XYval<T> o = *this; o.x = -x; o.y = -y; return o; }
277
+  FI const XYval<T> operator-()                   const { XYval<T> o = *this; o.x = -x; o.y = -y; return o; }
278
+};
279
+
280
+//
281
+// XYZ coordinates, counters, etc.
282
+//
283
+template<typename T>
284
+struct XYZval {
285
+  union {
286
+    struct { T x, y, z; };
287
+    struct { T a, b, c; };
288
+    T pos[3];
289
+  };
290
+  FI void set(const T px)                              { x = px; }
291
+  FI void set(const T px, const T py)                  { x = px; y = py; }
292
+  FI void set(const T px, const T py, const T pz)      { x = px; y = py; z = pz; }
293
+  FI void set(const XYval<T> pxy, const T pz)          { x = pxy.x; y = pxy.y; z = pz; }
294
+  FI void reset()                                      { x = y = z = 0; }
295
+  FI T magnitude()                               const { return (T)sqrtf(x*x + y*y + z*z); }
296
+  FI operator T* ()                                    { return pos; }
297
+  FI operator bool()                                   { return z || x || y; }
298
+  FI XYZval<T>          copy()                   const { XYZval<T> o = *this; return o; }
299
+  FI XYZval<T>           ABS()                   const { return { T(_ABS(x)), T(_ABS(y)), T(_ABS(z)) }; }
300
+  FI XYZval<int16_t>   asInt()                         { return { int16_t(x), int16_t(y), int16_t(z) }; }
301
+  FI XYZval<int16_t>   asInt()                   const { return { int16_t(x), int16_t(y), int16_t(z) }; }
302
+  FI XYZval<int32_t>  asLong()                         { return { int32_t(x), int32_t(y), int32_t(z) }; }
303
+  FI XYZval<int32_t>  asLong()                   const { return { int32_t(x), int32_t(y), int32_t(z) }; }
304
+  FI XYZval<float>   asFloat()                         { return {   float(x),   float(y),   float(z) }; }
305
+  FI XYZval<float>   asFloat()                   const { return {   float(x),   float(y),   float(z) }; }
306
+  FI XYZval<float> reciprocal()                  const { return {  _RECIP(x),  _RECIP(y),  _RECIP(z) }; }
307
+  FI XYZval<float> asLogical()                   const { XYZval<float> o = asFloat(); toLogical(o); return o; }
308
+  FI XYZval<float>  asNative()                   const { XYZval<float> o = asFloat(); toNative(o);  return o; }
309
+  FI operator       XYval<T>&()                        { return *(XYval<T>*)this; }
310
+  FI operator const XYval<T>&()                  const { return *(const XYval<T>*)this; }
311
+  FI operator       XYZEval<T>()                 const { return { x, y, z }; }
312
+  FI       T&   operator[](const int i)                { return pos[i]; }
313
+  FI const T&   operator[](const int i)          const { return pos[i]; }
314
+  FI XYZval<T>& operator= (const T v)                  { set(v,    v,    v   ); return *this; }
315
+  FI XYZval<T>& operator= (const XYval<T>   &rs)       { set(rs.x, rs.y      ); return *this; }
316
+  FI XYZval<T>& operator= (const XYZEval<T> &rs)       { set(rs.x, rs.y, rs.z); return *this; }
317
+  FI XYZval<T>  operator+ (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;               return ls; }
318
+  FI XYZval<T>  operator+ (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;               return ls; }
319
+  FI XYZval<T>  operator- (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;               return ls; }
320
+  FI XYZval<T>  operator- (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;               return ls; }
321
+  FI XYZval<T>  operator* (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;               return ls; }
322
+  FI XYZval<T>  operator* (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;               return ls; }
323
+  FI XYZval<T>  operator/ (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;               return ls; }
324
+  FI XYZval<T>  operator/ (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;               return ls; }
325
+  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; }
326
+  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; }
327
+  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; }
328
+  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; }
329
+  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; }
330
+  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; }
331
+  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; }
332
+  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; }
333
+  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; }
334
+  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; }
335
+  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; }
336
+  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; }
337
+  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; }
338
+  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; }
339
+  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; }
340
+  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; }
341
+  FI XYZval<T>  operator* (const float &v)       const { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= z;    return ls; }
342
+  FI XYZval<T>  operator* (const float &v)             { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= z;    return ls; }
343
+  FI XYZval<T>  operator* (const int &v)         const { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= z;    return ls; }
344
+  FI XYZval<T>  operator* (const int &v)               { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= z;    return ls; }
345
+  FI XYZval<T>  operator/ (const float &v)       const { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= z;    return ls; }
346
+  FI XYZval<T>  operator/ (const float &v)             { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= z;    return ls; }
347
+  FI XYZval<T>  operator/ (const int &v)         const { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= z;    return ls; }
348
+  FI XYZval<T>  operator/ (const int &v)               { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= z;    return ls; }
349
+  FI XYZval<T>  operator>>(const int &v)         const { XYZval<T> ls = *this; _RS(ls.x); _RS(ls.y); _RS(ls.z); return ls; }
350
+  FI XYZval<T>  operator>>(const int &v)               { XYZval<T> ls = *this; _RS(ls.x); _RS(ls.y); _RS(ls.z); return ls; }
351
+  FI XYZval<T>  operator<<(const int &v)         const { XYZval<T> ls = *this; _LS(ls.x); _LS(ls.y); _LS(ls.z); return ls; }
352
+  FI XYZval<T>  operator<<(const int &v)               { XYZval<T> ls = *this; _LS(ls.x); _LS(ls.y); _LS(ls.z); return ls; }
353
+  FI XYZval<T>& operator+=(const XYval<T>   &rs)       { x += rs.x; y += rs.y;            return *this; }
354
+  FI XYZval<T>& operator-=(const XYval<T>   &rs)       { x -= rs.x; y -= rs.y;            return *this; }
355
+  FI XYZval<T>& operator*=(const XYval<T>   &rs)       { x *= rs.x; y *= rs.y;            return *this; }
356
+  FI XYZval<T>& operator/=(const XYval<T>   &rs)       { x /= rs.x; y /= rs.y;            return *this; }
357
+  FI XYZval<T>& operator+=(const XYZval<T>  &rs)       { x += rs.x; y += rs.y; z += rs.z; return *this; }
358
+  FI XYZval<T>& operator-=(const XYZval<T>  &rs)       { x -= rs.x; y -= rs.y; z -= rs.z; return *this; }
359
+  FI XYZval<T>& operator*=(const XYZval<T>  &rs)       { x *= rs.x; y *= rs.y; z *= rs.z; return *this; }
360
+  FI XYZval<T>& operator/=(const XYZval<T>  &rs)       { x /= rs.x; y /= rs.y; z /= rs.z; return *this; }
361
+  FI XYZval<T>& operator+=(const XYZEval<T> &rs)       { x += rs.x; y += rs.y; z += rs.z; return *this; }
362
+  FI XYZval<T>& operator-=(const XYZEval<T> &rs)       { x -= rs.x; y -= rs.y; z -= rs.z; return *this; }
363
+  FI XYZval<T>& operator*=(const XYZEval<T> &rs)       { x *= rs.x; y *= rs.y; z *= rs.z; return *this; }
364
+  FI XYZval<T>& operator/=(const XYZEval<T> &rs)       { x /= rs.x; y /= rs.y; z /= rs.z; return *this; }
365
+  FI XYZval<T>& operator*=(const float &v)             { x *= v;    y *= v;    z *= v;    return *this; }
366
+  FI XYZval<T>& operator*=(const int &v)               { x *= v;    y *= v;    z *= v;    return *this; }
367
+  FI XYZval<T>& operator>>=(const int &v)              { _RS(x);   _RS(y);   _RS(z);   return *this; }
368
+  FI XYZval<T>& operator<<=(const int &v)              { _LS(x);   _LS(y);   _LS(z);   return *this; }
369
+  FI bool       operator==(const XYZEval<T> &rs)       { return x == rs.x && y == rs.y && z == rs.z; }
370
+  FI bool       operator!=(const XYZEval<T> &rs)       { return !operator==(rs); }
371
+  FI bool       operator==(const XYZEval<T> &rs) const { return x == rs.x && y == rs.y && z == rs.z; }
372
+  FI bool       operator!=(const XYZEval<T> &rs) const { return !operator==(rs); }
373
+  FI XYZval<T>       operator-()                       { XYZval<T> o = *this; o.x = -x; o.y = -y; o.z = -z; return o; }
374
+  FI const XYZval<T> operator-()                 const { XYZval<T> o = *this; o.x = -x; o.y = -y; o.z = -z; return o; }
375
+};
376
+
377
+//
378
+// XYZE coordinates, counters, etc.
379
+//
380
+template<typename T>
381
+struct XYZEval {
382
+  union {
383
+    struct{ T x, y, z, e; };
384
+    struct{ T a, b, c; };
385
+    T pos[4];
386
+  };
387
+  FI void reset()                                             { x = y = z = e = 0; }
388
+  FI T magnitude()                                      const { return (T)sqrtf(x*x + y*y + z*z + e*e); }
389
+  FI operator T* ()                                           { return pos; }
390
+  FI operator bool()                                          { return e || z || x || y; }
391
+  FI void set(const T px)                                     { x = px;                                        }
392
+  FI void set(const T px, const T py)                         { x = px;     y = py;                            }
393
+  FI void set(const T px, const T py, const T pz)             { x = px;     y = py;     z = pz;                }
394
+  FI void set(const T px, const T py, const T pz, const T pe) { x = px;     y = py;     z = pz;     e = pe;    }
395
+  FI void set(const XYval<T> pxy)                             { x = pxy.x;  y = pxy.y;                         }
396
+  FI void set(const XYval<T> pxy, const T pz)                 { x = pxy.x;  y = pxy.y;  z = pz;                }
397
+  FI void set(const XYZval<T> pxyz)                           { x = pxyz.x; y = pxyz.y; z = pxyz.z;            }
398
+  FI void set(const XYval<T> pxy, const T pz, const T pe)     { x = pxy.x;  y = pxy.y;  z = pz;     e = pe;    }
399
+  FI void set(const XYval<T> pxy, const XYval<T> pze)         { x = pxy.x;  y = pxy.y;  z = pze.z;  e = pze.e; }
400
+  FI void set(const XYZval<T> pxyz, const T pe)               { x = pxyz.x; y = pxyz.y; z = pxyz.z; e = pe;    }
401
+  FI XYZEval<T>          copy()                         const { return *this; }
402
+  FI XYZEval<T>           ABS()                         const { return { T(_ABS(x)), T(_ABS(y)), T(_ABS(z)), T(_ABS(e)) }; }
403
+  FI XYZEval<int16_t>   asInt()                               { return { int16_t(x), int16_t(y), int16_t(z), int16_t(e) }; }
404
+  FI XYZEval<int16_t>   asInt()                         const { return { int16_t(x), int16_t(y), int16_t(z), int16_t(e) }; }
405
+  FI XYZEval<int32_t>  asLong()                         const { return { int32_t(x), int32_t(y), int32_t(z), int32_t(e) }; }
406
+  FI XYZEval<int32_t>  asLong()                               { return { int32_t(x), int32_t(y), int32_t(z), int32_t(e) }; }
407
+  FI XYZEval<float>   asFloat()                               { return {   float(x),   float(y),   float(z),   float(e) }; }
408
+  FI XYZEval<float>   asFloat()                         const { return {   float(x),   float(y),   float(z),   float(e) }; }
409
+  FI XYZEval<float> reciprocal()                        const { return {  _RECIP(x),  _RECIP(y),  _RECIP(z),  _RECIP(e) }; }
410
+  FI XYZEval<float> asLogical()                         const { XYZEval<float> o = asFloat(); toLogical(o); return o; }
411
+  FI XYZEval<float>  asNative()                         const { XYZEval<float> o = asFloat(); toNative(o);  return o; }
412
+  FI operator       XYval<T>&()                               { return *(XYval<T>*)this; }
413
+  FI operator const XYval<T>&()                         const { return *(const XYval<T>*)this; }
414
+  FI operator       XYZval<T>&()                              { return *(XYZval<T>*)this; }
415
+  FI operator const XYZval<T>&()                        const { return *(const XYZval<T>*)this; }
416
+  FI       T&    operator[](const int i)                      { return pos[i]; }
417
+  FI const T&    operator[](const int i)                const { return pos[i]; }
418
+  FI XYZEval<T>& operator= (const T v)                        { set(v, v, v, v); return *this; }
419
+  FI XYZEval<T>& operator= (const XYval<T>   &rs)             { set(rs.x, rs.y); return *this; }
420
+  FI XYZEval<T>& operator= (const XYZval<T>  &rs)             { set(rs.x, rs.y, rs.z); return *this; }
421
+  FI XYZEval<T>  operator+ (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;                             return ls; }
422
+  FI XYZEval<T>  operator+ (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;                             return ls; }
423
+  FI XYZEval<T>  operator- (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;                             return ls; }
424
+  FI XYZEval<T>  operator- (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;                             return ls; }
425
+  FI XYZEval<T>  operator* (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;                             return ls; }
426
+  FI XYZEval<T>  operator* (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;                             return ls; }
427
+  FI XYZEval<T>  operator/ (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;                             return ls; }
428
+  FI XYZEval<T>  operator/ (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;                             return ls; }
429
+  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; }
430
+  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; }
431
+  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; }
432
+  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; }
433
+  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; }
434
+  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; }
435
+  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; }
436
+  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; }
437
+  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; }
438
+  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; }
439
+  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; }
440
+  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; }
441
+  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; }
442
+  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; }
443
+  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; }
444
+  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; }
445
+  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; }
446
+  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; }
447
+  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; }
448
+  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; }
449
+  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; }
450
+  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; }
451
+  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; }
452
+  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; }
453
+  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; }
454
+  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; }
455
+  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; }
456
+  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; }
457
+  FI XYZEval<T>& operator+=(const XYval<T>   &rs)             { x += rs.x; y += rs.y;                       return *this; }
458
+  FI XYZEval<T>& operator-=(const XYval<T>   &rs)             { x -= rs.x; y -= rs.y;                       return *this; }
459
+  FI XYZEval<T>& operator*=(const XYval<T>   &rs)             { x *= rs.x; y *= rs.y;                       return *this; }
460
+  FI XYZEval<T>& operator/=(const XYval<T>   &rs)             { x /= rs.x; y /= rs.y;                       return *this; }
461
+  FI XYZEval<T>& operator+=(const XYZval<T>  &rs)             { x += rs.x; y += rs.y; z += rs.z;            return *this; }
462
+  FI XYZEval<T>& operator-=(const XYZval<T>  &rs)             { x -= rs.x; y -= rs.y; z -= rs.z;            return *this; }
463
+  FI XYZEval<T>& operator*=(const XYZval<T>  &rs)             { x *= rs.x; y *= rs.y; z *= rs.z;            return *this; }
464
+  FI XYZEval<T>& operator/=(const XYZval<T>  &rs)             { x /= rs.x; y /= rs.y; z /= rs.z;            return *this; }
465
+  FI XYZEval<T>& operator+=(const XYZEval<T> &rs)             { x += rs.x; y += rs.y; z += rs.z; e += rs.e; return *this; }
466
+  FI XYZEval<T>& operator-=(const XYZEval<T> &rs)             { x -= rs.x; y -= rs.y; z -= rs.z; e -= rs.e; return *this; }
467
+  FI XYZEval<T>& operator*=(const XYZEval<T> &rs)             { x *= rs.x; y *= rs.y; z *= rs.z; e *= rs.e; return *this; }
468
+  FI XYZEval<T>& operator/=(const XYZEval<T> &rs)             { x /= rs.x; y /= rs.y; z /= rs.z; e /= rs.e; return *this; }
469
+  FI XYZEval<T>& operator*=(const T &v)                       { x *= v;    y *= v;    z *= v;    e *= v;    return *this; }
470
+  FI XYZEval<T>& operator>>=(const int &v)                    { _RS(x);    _RS(y);    _RS(z);    _RS(e);    return *this; }
471
+  FI XYZEval<T>& operator<<=(const int &v)                    { _LS(x);    _LS(y);    _LS(z);    _LS(e);    return *this; }
472
+  FI bool        operator==(const XYZval<T>  &rs)             { return x == rs.x && y == rs.y && z == rs.z; }
473
+  FI bool        operator!=(const XYZval<T>  &rs)             { return !operator==(rs); }
474
+  FI bool        operator==(const XYZval<T>  &rs)       const { return x == rs.x && y == rs.y && z == rs.z; }
475
+  FI bool        operator!=(const XYZval<T>  &rs)       const { return !operator==(rs); }
476
+  FI       XYZEval<T> operator-()                             { return { -x, -y, -z, -e }; }
477
+  FI const XYZEval<T> operator-()                       const { return { -x, -y, -z, -e }; }
478
+};
479
+
480
+#undef _RECIP
481
+#undef _ABS
482
+#undef _LS
483
+#undef _RS
484
+#undef FI
485
+
486
+const xyze_char_t axis_codes { 'X', 'Y', 'Z', 'E' };

+ 20
- 30
Marlin/src/core/utility.cpp View File

@@ -79,36 +79,36 @@ void safe_delay(millis_t ms) {
79 79
     );
80 80
 
81 81
     #if HAS_BED_PROBE
82
-      SERIAL_ECHOPAIR("Probe Offset X:", probe_offset[X_AXIS], " Y:", probe_offset[Y_AXIS], " Z:", probe_offset[Z_AXIS]);
83
-      if (probe_offset[X_AXIS] > 0)
82
+      SERIAL_ECHOPAIR("Probe Offset X", probe_offset.x, " Y", probe_offset.y, " Z", probe_offset.z);
83
+      if (probe_offset.x > 0)
84 84
         SERIAL_ECHOPGM(" (Right");
85
-      else if (probe_offset[X_AXIS] < 0)
85
+      else if (probe_offset.x < 0)
86 86
         SERIAL_ECHOPGM(" (Left");
87
-      else if (probe_offset[Y_AXIS] != 0)
87
+      else if (probe_offset.y != 0)
88 88
         SERIAL_ECHOPGM(" (Middle");
89 89
       else
90 90
         SERIAL_ECHOPGM(" (Aligned With");
91 91
 
92
-      if (probe_offset[Y_AXIS] > 0) {
92
+      if (probe_offset.y > 0) {
93 93
         #if IS_SCARA
94 94
           SERIAL_ECHOPGM("-Distal");
95 95
         #else
96 96
           SERIAL_ECHOPGM("-Back");
97 97
         #endif
98 98
       }
99
-      else if (probe_offset[Y_AXIS] < 0) {
99
+      else if (probe_offset.y < 0) {
100 100
         #if IS_SCARA
101 101
           SERIAL_ECHOPGM("-Proximal");
102 102
         #else
103 103
           SERIAL_ECHOPGM("-Front");
104 104
         #endif
105 105
       }
106
-      else if (probe_offset[X_AXIS] != 0)
106
+      else if (probe_offset.x != 0)
107 107
         SERIAL_ECHOPGM("-Center");
108 108
 
109
-      if (probe_offset[Z_AXIS] < 0)
109
+      if (probe_offset.z < 0)
110 110
         SERIAL_ECHOPGM(" & Below");
111
-      else if (probe_offset[Z_AXIS] > 0)
111
+      else if (probe_offset.z > 0)
112 112
         SERIAL_ECHOPGM(" & Above");
113 113
       else
114 114
         SERIAL_ECHOPGM(" & Same Z as");
@@ -134,24 +134,18 @@ void safe_delay(millis_t ms) {
134 134
             SERIAL_ECHOLNPAIR("Z Fade: ", planner.z_fade_height);
135 135
         #endif
136 136
         #if ABL_PLANAR
137
-          const float diff[XYZ] = {
138
-            planner.get_axis_position_mm(X_AXIS) - current_position[X_AXIS],
139
-            planner.get_axis_position_mm(Y_AXIS) - current_position[Y_AXIS],
140
-            planner.get_axis_position_mm(Z_AXIS) - current_position[Z_AXIS]
141
-          };
142 137
           SERIAL_ECHOPGM("ABL Adjustment X");
143
-          if (diff[X_AXIS] > 0) SERIAL_CHAR('+');
144
-          SERIAL_ECHO(diff[X_AXIS]);
145
-          SERIAL_ECHOPGM(" Y");
146
-          if (diff[Y_AXIS] > 0) SERIAL_CHAR('+');
147
-          SERIAL_ECHO(diff[Y_AXIS]);
148
-          SERIAL_ECHOPGM(" Z");
149
-          if (diff[Z_AXIS] > 0) SERIAL_CHAR('+');
150
-          SERIAL_ECHO(diff[Z_AXIS]);
138
+          LOOP_XYZ(a) {
139
+            float v = planner.get_axis_position_mm(AxisEnum(a)) - current_position[a];
140
+            SERIAL_CHAR(' ');
141
+            SERIAL_CHAR('X' + char(a));
142
+            if (v > 0) SERIAL_CHAR('+');
143
+            SERIAL_ECHO(v);
144
+          }
151 145
         #else
152 146
           #if ENABLED(AUTO_BED_LEVELING_UBL)
153 147
             SERIAL_ECHOPGM("UBL Adjustment Z");
154
-            const float rz = ubl.get_z_correction(current_position[X_AXIS], current_position[Y_AXIS]);
148
+            const float rz = ubl.get_z_correction(current_position);
155 149
           #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
156 150
             SERIAL_ECHOPGM("ABL Adjustment Z");
157 151
             const float rz = bilinear_z_offset(current_position);
@@ -159,7 +153,7 @@ void safe_delay(millis_t ms) {
159 153
           SERIAL_ECHO(ftostr43sign(rz, '+'));
160 154
           #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
161 155
             if (planner.z_fade_height) {
162
-              SERIAL_ECHOPAIR(" (", ftostr43sign(rz * planner.fade_scaling_factor_for_z(current_position[Z_AXIS]), '+'));
156
+              SERIAL_ECHOPAIR(" (", ftostr43sign(rz * planner.fade_scaling_factor_for_z(current_position.z), '+'));
163 157
               SERIAL_CHAR(')');
164 158
             }
165 159
           #endif
@@ -175,15 +169,11 @@ void safe_delay(millis_t ms) {
175 169
       SERIAL_ECHOPGM("Mesh Bed Leveling");
176 170
       if (planner.leveling_active) {
177 171
         SERIAL_ECHOLNPGM(" (enabled)");
178
-        SERIAL_ECHOPAIR("MBL Adjustment Z", ftostr43sign(mbl.get_z(current_position[X_AXIS], current_position[Y_AXIS]
179
-          #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
180
-            , 1.0
181
-          #endif
182
-        ), '+'));
172
+        SERIAL_ECHOPAIR("MBL Adjustment Z", ftostr43sign(mbl.get_z(current_position), '+'));
183 173
         #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
184 174
           if (planner.z_fade_height) {
185 175
             SERIAL_ECHOPAIR(" (", ftostr43sign(
186
-              mbl.get_z(current_position[X_AXIS], current_position[Y_AXIS], planner.fade_scaling_factor_for_z(current_position[Z_AXIS])), '+'
176
+              mbl.get_z(current_position, planner.fade_scaling_factor_for_z(current_position.z)), '+'
187 177
             ));
188 178
             SERIAL_CHAR(')');
189 179
           }

+ 20
- 6
Marlin/src/core/utility.h View File

@@ -22,8 +22,7 @@
22 22
 #pragma once
23 23
 
24 24
 #include "../inc/MarlinConfigPre.h"
25
-
26
-constexpr char axis_codes[XYZE] = { 'X', 'Y', 'Z', 'E' };
25
+#include "../core/types.h"
27 26
 
28 27
 // Delay that ensures heaters and watchdog are kept alive
29 28
 void safe_delay(millis_t ms);
@@ -37,10 +36,25 @@ inline void serial_delay(const millis_t ms) {
37 36
   #endif
38 37
 }
39 38
 
40
-// 16x16 bit arrays
41
-FORCE_INLINE void bitmap_clear(uint16_t bits[16], const uint8_t x, const uint8_t y)  { CBI(bits[y], x); }
42
-FORCE_INLINE void bitmap_set(uint16_t bits[16], const uint8_t x, const uint8_t y)    { SBI(bits[y], x); }
43
-FORCE_INLINE bool is_bitmap_set(uint16_t bits[16], const uint8_t x, const uint8_t y) { return TEST(bits[y], x); }
39
+#if GRID_MAX_POINTS_X && GRID_MAX_POINTS_Y
40
+
41
+  // 16x16 bit arrays
42
+  template <int W, int H>
43
+  struct FlagBits {
44
+    typename IF<(W>8), uint16_t, uint8_t>::type bits[H];
45
+    void fill()                                   { memset(bits, 0xFF, sizeof(bits)); }
46
+    void reset()                                  { memset(bits, 0x00, sizeof(bits)); }
47
+    void unmark(const uint8_t x, const uint8_t y) { CBI(bits[y], x); }
48
+    void mark(const uint8_t x, const uint8_t y)   { SBI(bits[y], x); }
49
+    bool marked(const uint8_t x, const uint8_t y) { return TEST(bits[y], x); }
50
+    inline void unmark(const xy_int8_t &xy)       { unmark(xy.y, xy.x); }
51
+    inline void mark(const xy_int8_t &xy)         { mark(xy.y, xy.x); }
52
+    inline bool marked(const xy_int8_t &xy)       { return marked(xy.y, xy.x); }
53
+  };
54
+
55
+  typedef FlagBits<GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y> MeshFlags;
56
+
57
+#endif
44 58
 
45 59
 #if ENABLED(DEBUG_LEVELING_FEATURE)
46 60
   void log_machine_info();

+ 18
- 21
Marlin/src/feature/I2CPositionEncoder.cpp View File

@@ -326,25 +326,23 @@ bool I2CPositionEncoder::test_axis() {
326 326
   //only works on XYZ cartesian machines for the time being
327 327
   if (!(encoderAxis == X_AXIS || encoderAxis == Y_AXIS || encoderAxis == Z_AXIS)) return false;
328 328
 
329
-  float startCoord[NUM_AXIS] = { 0 }, endCoord[NUM_AXIS] = { 0 };
330
-
331
-  const float startPosition = soft_endstop[encoderAxis].min + 10,
332
-              endPosition = soft_endstop[encoderAxis].max - 10;
329
+  const float startPosition = soft_endstop.min[encoderAxis] + 10,
330
+              endPosition = soft_endstop.max[encoderAxis] - 10;
333 331
   const feedRate_t fr_mm_s = FLOOR(MMM_TO_MMS((encoderAxis == Z_AXIS) ? HOMING_FEEDRATE_Z : HOMING_FEEDRATE_XY));
334 332
 
335 333
   ec = false;
336 334
 
337
-  LOOP_XYZ(i) {
338
-    startCoord[i] = planner.get_axis_position_mm((AxisEnum)i);
339
-    endCoord[i] = planner.get_axis_position_mm((AxisEnum)i);
335
+  xyze_pos_t startCoord, endCoord;
336
+  LOOP_XYZ(a) {
337
+    startCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
338
+    endCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
340 339
   }
341 340
   startCoord[encoderAxis] = startPosition;
342 341
   endCoord[encoderAxis] = endPosition;
343 342
 
344 343
   planner.synchronize();
345
-
346
-  planner.buffer_line(startCoord[X_AXIS], startCoord[Y_AXIS], startCoord[Z_AXIS],
347
-                      planner.get_axis_position_mm(E_AXIS), fr_mm_s, 0);
344
+  startCoord.e = planner.get_axis_position_mm(E_AXIS);
345
+  planner.buffer_line(startCoord, fr_mm_s, 0);
348 346
   planner.synchronize();
349 347
 
350 348
   // 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() {
355 353
   }
356 354
 
357 355
   if (trusted) { // if trusted, commence test
358
-    planner.buffer_line(endCoord[X_AXIS], endCoord[Y_AXIS], endCoord[Z_AXIS],
359
-                        planner.get_axis_position_mm(E_AXIS), fr_mm_s, 0);
356
+    endCoord.e = planner.get_axis_position_mm(E_AXIS);
357
+    planner.buffer_line(endCoord, fr_mm_s, 0);
360 358
     planner.synchronize();
361 359
   }
362 360
 
@@ -376,8 +374,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
376 374
 
377 375
   float old_steps_mm, new_steps_mm,
378 376
         startDistance, endDistance,
379
-        travelDistance, travelledDistance, total = 0,
380
-        startCoord[NUM_AXIS] = { 0 }, endCoord[NUM_AXIS] = { 0 };
377
+        travelDistance, travelledDistance, total = 0;
381 378
 
382 379
   int32_t startCount, stopCount;
383 380
 
@@ -387,31 +384,31 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
387 384
   ec = false;
388 385
 
389 386
   startDistance = 20;
390
-  endDistance = soft_endstop[encoderAxis].max - 20;
387
+  endDistance = soft_endstop.max[encoderAxis] - 20;
391 388
   travelDistance = endDistance - startDistance;
392 389
 
390
+  xyze_pos_t startCoord, endCoord;
393 391
   LOOP_XYZ(a) {
394 392
     startCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
395 393
     endCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
396 394
   }
397
-
398 395
   startCoord[encoderAxis] = startDistance;
399 396
   endCoord[encoderAxis] = endDistance;
400 397
 
401 398
   planner.synchronize();
402 399
 
403 400
   LOOP_L_N(i, iter) {
404
-    planner.buffer_line(startCoord[X_AXIS], startCoord[Y_AXIS], startCoord[Z_AXIS],
405
-                        planner.get_axis_position_mm(E_AXIS), fr_mm_s, 0);
401
+    startCoord.e = planner.get_axis_position_mm(E_AXIS);
402
+    planner.buffer_line(startCoord, fr_mm_s, 0);
406 403
     planner.synchronize();
407 404
 
408 405
     delay(250);
409 406
     startCount = get_position();
410 407
 
411
-    //do_blocking_move_to(endCoord[X_AXIS],endCoord[Y_AXIS],endCoord[Z_AXIS]);
408
+    //do_blocking_move_to(endCoord);
412 409
 
413
-    planner.buffer_line(endCoord[X_AXIS], endCoord[Y_AXIS], endCoord[Z_AXIS],
414
-                        planner.get_axis_position_mm(E_AXIS), fr_mm_s, 0);
410
+    endCoord.e = planner.get_axis_position_mm(E_AXIS);
411
+    planner.buffer_line(endCoord, fr_mm_s, 0);
415 412
     planner.synchronize();
416 413
 
417 414
     //Read encoder distance

+ 0
- 2
Marlin/src/feature/I2CPositionEncoder.h View File

@@ -93,8 +93,6 @@
93 93
 #define LOOP_PE(VAR) LOOP_L_N(VAR, I2CPE_ENCODER_CNT)
94 94
 #define CHECK_IDX() do{ if (!WITHIN(idx, 0, I2CPE_ENCODER_CNT - 1)) return; }while(0)
95 95
 
96
-extern const char axis_codes[XYZE];
97
-
98 96
 typedef union {
99 97
   volatile int32_t val = 0;
100 98
   uint8_t          bval[4];

+ 12
- 12
Marlin/src/feature/backlash.cpp View File

@@ -31,9 +31,9 @@
31 31
 
32 32
 #ifdef BACKLASH_DISTANCE_MM
33 33
   #if ENABLED(BACKLASH_GCODE)
34
-    float Backlash::distance_mm[XYZ] = BACKLASH_DISTANCE_MM;
34
+    xyz_float_t Backlash::distance_mm = BACKLASH_DISTANCE_MM;
35 35
   #else
36
-    const float Backlash::distance_mm[XYZ] = BACKLASH_DISTANCE_MM;
36
+    const xyz_float_t Backlash::distance_mm = BACKLASH_DISTANCE_MM;
37 37
   #endif
38 38
 #endif
39 39
 
@@ -45,8 +45,8 @@
45 45
 #endif
46 46
 
47 47
 #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
48
-  float Backlash::measured_mm[XYZ] = { 0 };
49
-  uint8_t Backlash::measured_count[XYZ] = { 0 };
48
+  xyz_float_t Backlash::measured_mm{0};
49
+  xyz_uint8_t Backlash::measured_count{0};
50 50
 #endif
51 51
 
52 52
 Backlash backlash;
@@ -80,12 +80,12 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const
80 80
 
81 81
     // Residual error carried forward across multiple segments, so correction can be applied
82 82
     // to segments where there is no direction change.
83
-    static int32_t residual_error[XYZ] = { 0 };
83
+    static xyz_long_t residual_error{0};
84 84
   #else
85 85
     // No direction change, no correction.
86 86
     if (!changed_dir) return;
87 87
     // No leftover residual error from segment to segment
88
-    int32_t residual_error[XYZ] = { 0 };
88
+    xyz_long_t residual_error{0};
89 89
   #endif
90 90
 
91 91
   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
131 131
 
132 132
   // Measure Z backlash by raising nozzle in increments until probe deactivates
133 133
   void Backlash::measure_with_probe() {
134
-    if (measured_count[Z_AXIS] == 255) return;
134
+    if (measured_count.z == 255) return;
135 135
 
136
-    float start_height = current_position[Z_AXIS];
137
-    while (current_position[Z_AXIS] < (start_height + BACKLASH_MEASUREMENT_LIMIT) && TEST_PROBE_PIN)
138
-      do_blocking_move_to_z(current_position[Z_AXIS] + BACKLASH_MEASUREMENT_RESOLUTION, MMM_TO_MMS(BACKLASH_MEASUREMENT_FEEDRATE));
136
+    const float start_height = current_position.z;
137
+    while (current_position.z < (start_height + BACKLASH_MEASUREMENT_LIMIT) && TEST_PROBE_PIN)
138
+      do_blocking_move_to_z(current_position.z + BACKLASH_MEASUREMENT_RESOLUTION, MMM_TO_MMS(BACKLASH_MEASUREMENT_FEEDRATE));
139 139
 
140 140
     // The backlash from all probe points is averaged, so count the number of measurements
141
-    measured_mm[Z_AXIS] += current_position[Z_AXIS] - start_height;
142
-    measured_count[Z_AXIS]++;
141
+    measured_mm.z += current_position.z - start_height;
142
+    measured_count.z++;
143 143
   }
144 144
 #endif
145 145
 

+ 4
- 4
Marlin/src/feature/backlash.h View File

@@ -29,7 +29,7 @@ constexpr uint8_t all_on = 0xFF, all_off = 0x00;
29 29
 class Backlash {
30 30
 public:
31 31
   #if ENABLED(BACKLASH_GCODE)
32
-    static float distance_mm[XYZ];
32
+    static xyz_float_t distance_mm;
33 33
     static uint8_t correction;
34 34
     #ifdef BACKLASH_SMOOTHING_MM
35 35
       static float smoothing_mm;
@@ -39,7 +39,7 @@ public:
39 39
     static inline float get_correction() { return float(ui8_to_percent(correction)) / 100.0f; }
40 40
   #else
41 41
     static constexpr uint8_t correction = (BACKLASH_CORRECTION) * 0xFF;
42
-    static const float distance_mm[XYZ];
42
+    static const xyz_float_t distance_mm;
43 43
     #ifdef BACKLASH_SMOOTHING_MM
44 44
       static constexpr float smoothing_mm = BACKLASH_SMOOTHING_MM;
45 45
     #endif
@@ -47,8 +47,8 @@ public:
47 47
 
48 48
   #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
49 49
     private:
50
-      static float measured_mm[XYZ];
51
-      static uint8_t measured_count[XYZ];
50
+      static xyz_float_t measured_mm;
51
+      static xyz_uint8_t measured_count;
52 52
     public:
53 53
       static void measure_with_probe();
54 54
   #endif

+ 78
- 84
Marlin/src/feature/bedlevel/abl/abl.cpp View File

@@ -35,9 +35,9 @@
35 35
   #include "../../../lcd/extensible_ui/ui_api.h"
36 36
 #endif
37 37
 
38
-int bilinear_grid_spacing[2], bilinear_start[2];
39
-float bilinear_grid_factor[2],
40
-      z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
38
+xy_int_t bilinear_grid_spacing, bilinear_start;
39
+xy_float_t bilinear_grid_factor;
40
+bed_mesh_t z_values;
41 41
 
42 42
 /**
43 43
  * Extrapolate a single point from its neighbors
@@ -153,8 +153,8 @@ void print_bilinear_leveling_grid() {
153 153
   #define ABL_TEMP_POINTS_X (GRID_MAX_POINTS_X + 2)
154 154
   #define ABL_TEMP_POINTS_Y (GRID_MAX_POINTS_Y + 2)
155 155
   float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y];
156
-  int bilinear_grid_spacing_virt[2] = { 0 };
157
-  float bilinear_grid_factor_virt[2] = { 0 };
156
+  xy_int_t bilinear_grid_spacing_virt;
157
+  xy_float_t bilinear_grid_factor_virt;
158 158
 
159 159
   void print_bilinear_leveling_grid_virt() {
160 160
     SERIAL_ECHOLNPGM("Subdivided with CATMULL ROM Leveling Grid:");
@@ -207,7 +207,7 @@ void print_bilinear_leveling_grid() {
207 207
       + p[i]   * (2 - 5 * sq(t) + 3 * t * sq(t))
208 208
       + p[i+1] * t * (1 + 4 * t - 3 * sq(t))
209 209
       - p[i+2] * sq(t) * (1 - t)
210
-    ) * 0.5;
210
+    ) * 0.5f;
211 211
   }
212 212
 
213 213
   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() {
222 222
   }
223 223
 
224 224
   void bed_level_virt_interpolate() {
225
-    bilinear_grid_spacing_virt[X_AXIS] = bilinear_grid_spacing[X_AXIS] / (BILINEAR_SUBDIVISIONS);
226
-    bilinear_grid_spacing_virt[Y_AXIS] = bilinear_grid_spacing[Y_AXIS] / (BILINEAR_SUBDIVISIONS);
227
-    bilinear_grid_factor_virt[X_AXIS] = RECIPROCAL(bilinear_grid_spacing_virt[X_AXIS]);
228
-    bilinear_grid_factor_virt[Y_AXIS] = RECIPROCAL(bilinear_grid_spacing_virt[Y_AXIS]);
225
+    bilinear_grid_spacing_virt = bilinear_grid_spacing / (BILINEAR_SUBDIVISIONS);
226
+    bilinear_grid_factor_virt = bilinear_grid_spacing_virt.reciprocal();
229 227
     for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++)
230 228
       for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++)
231 229
         for (uint8_t ty = 0; ty < BILINEAR_SUBDIVISIONS; ty++)
@@ -245,40 +243,38 @@ void print_bilinear_leveling_grid() {
245 243
 
246 244
 // Refresh after other values have been updated
247 245
 void refresh_bed_level() {
248
-  bilinear_grid_factor[X_AXIS] = RECIPROCAL(bilinear_grid_spacing[X_AXIS]);
249
-  bilinear_grid_factor[Y_AXIS] = RECIPROCAL(bilinear_grid_spacing[Y_AXIS]);
246
+  bilinear_grid_factor = bilinear_grid_spacing.reciprocal();
250 247
   #if ENABLED(ABL_BILINEAR_SUBDIVISION)
251 248
     bed_level_virt_interpolate();
252 249
   #endif
253 250
 }
254 251
 
255 252
 #if ENABLED(ABL_BILINEAR_SUBDIVISION)
256
-  #define ABL_BG_SPACING(A) bilinear_grid_spacing_virt[A]
257
-  #define ABL_BG_FACTOR(A)  bilinear_grid_factor_virt[A]
253
+  #define ABL_BG_SPACING(A) bilinear_grid_spacing_virt.A
254
+  #define ABL_BG_FACTOR(A)  bilinear_grid_factor_virt.A
258 255
   #define ABL_BG_POINTS_X   ABL_GRID_POINTS_VIRT_X
259 256
   #define ABL_BG_POINTS_Y   ABL_GRID_POINTS_VIRT_Y
260 257
   #define ABL_BG_GRID(X,Y)  z_values_virt[X][Y]
261 258
 #else
262
-  #define ABL_BG_SPACING(A) bilinear_grid_spacing[A]
263
-  #define ABL_BG_FACTOR(A)  bilinear_grid_factor[A]
259
+  #define ABL_BG_SPACING(A) bilinear_grid_spacing.A
260
+  #define ABL_BG_FACTOR(A)  bilinear_grid_factor.A
264 261
   #define ABL_BG_POINTS_X   GRID_MAX_POINTS_X
265 262
   #define ABL_BG_POINTS_Y   GRID_MAX_POINTS_Y
266 263
   #define ABL_BG_GRID(X,Y)  z_values[X][Y]
267 264
 #endif
268 265
 
269 266
 // Get the Z adjustment for non-linear bed leveling
270
-float bilinear_z_offset(const float raw[XYZ]) {
267
+float bilinear_z_offset(const xy_pos_t &raw) {
271 268
 
272
-  static float z1, d2, z3, d4, L, D, ratio_x, ratio_y,
273
-               last_x = -999.999, last_y = -999.999;
269
+  static float z1, d2, z3, d4, L, D;
270
+
271
+  static xy_pos_t prev { -999.999, -999.999 }, ratio;
274 272
 
275 273
   // Whole units for the grid line indices. Constrained within bounds.
276
-  static int8_t gridx, gridy, nextx, nexty,
277
-                last_gridx = -99, last_gridy = -99;
274
+  static xy_int8_t thisg, nextg, lastg { -99, -99 };
278 275
 
279 276
   // XY relative to the probed area
280
-  const float rx = raw[X_AXIS] - bilinear_start[X_AXIS],
281
-              ry = raw[Y_AXIS] - bilinear_start[Y_AXIS];
277
+  xy_pos_t rel = raw - bilinear_start.asFloat();
282 278
 
283 279
   #if ENABLED(EXTRAPOLATE_BEYOND_GRID)
284 280
     #define FAR_EDGE_OR_BOX 2   // Keep using the last grid box
@@ -286,63 +282,62 @@ float bilinear_z_offset(const float raw[XYZ]) {
286 282
     #define FAR_EDGE_OR_BOX 1   // Just use the grid far edge
287 283
   #endif
288 284
 
289
-  if (last_x != rx) {
290
-    last_x = rx;
291
-    ratio_x = rx * ABL_BG_FACTOR(X_AXIS);
292
-    const float gx = constrain(FLOOR(ratio_x), 0, ABL_BG_POINTS_X - (FAR_EDGE_OR_BOX));
293
-    ratio_x -= gx;      // Subtract whole to get the ratio within the grid box
285
+  if (prev.x != rel.x) {
286
+    prev.x = rel.x;
287
+    ratio.x = rel.x * ABL_BG_FACTOR(x);
288
+    const float gx = constrain(FLOOR(ratio.x), 0, ABL_BG_POINTS_X - (FAR_EDGE_OR_BOX));
289
+    ratio.x -= gx;      // Subtract whole to get the ratio within the grid box
294 290
 
295 291
     #if DISABLED(EXTRAPOLATE_BEYOND_GRID)
296 292
       // Beyond the grid maintain height at grid edges
297
-      NOLESS(ratio_x, 0); // Never < 0.0. (> 1.0 is ok when nextx==gridx.)
293
+      NOLESS(ratio.x, 0); // Never <0 (>1 is ok when nextg.x==thisg.x)
298 294
     #endif
299 295
 
300
-    gridx = gx;
301
-    nextx = _MIN(gridx + 1, ABL_BG_POINTS_X - 1);
296
+    thisg.x = gx;
297
+    nextg.x = _MIN(thisg.x + 1, ABL_BG_POINTS_X - 1);
302 298
   }
303 299
 
304
-  if (last_y != ry || last_gridx != gridx) {
300
+  if (prev.y != rel.y || lastg.x != thisg.x) {
305 301
 
306
-    if (last_y != ry) {
307
-      last_y = ry;
308
-      ratio_y = ry * ABL_BG_FACTOR(Y_AXIS);
309
-      const float gy = constrain(FLOOR(ratio_y), 0, ABL_BG_POINTS_Y - (FAR_EDGE_OR_BOX));
310
-      ratio_y -= gy;
302
+    if (prev.y != rel.y) {
303
+      prev.y = rel.y;
304
+      ratio.y = rel.y * ABL_BG_FACTOR(y);
305
+      const float gy = constrain(FLOOR(ratio.y), 0, ABL_BG_POINTS_Y - (FAR_EDGE_OR_BOX));
306
+      ratio.y -= gy;
311 307
 
312 308
       #if DISABLED(EXTRAPOLATE_BEYOND_GRID)
313 309
         // Beyond the grid maintain height at grid edges
314
-        NOLESS(ratio_y, 0); // Never < 0.0. (> 1.0 is ok when nexty==gridy.)
310
+        NOLESS(ratio.y, 0); // Never < 0.0. (> 1.0 is ok when nextg.y==thisg.y.)
315 311
       #endif
316 312
 
317
-      gridy = gy;
318
-      nexty = _MIN(gridy + 1, ABL_BG_POINTS_Y - 1);
313
+      thisg.y = gy;
314
+      nextg.y = _MIN(thisg.y + 1, ABL_BG_POINTS_Y - 1);
319 315
     }
320 316
 
321
-    if (last_gridx != gridx || last_gridy != gridy) {
322
-      last_gridx = gridx;
323
-      last_gridy = gridy;
317
+    if (lastg != thisg) {
318
+      lastg = thisg;
324 319
       // Z at the box corners
325
-      z1 = ABL_BG_GRID(gridx, gridy);       // left-front
326
-      d2 = ABL_BG_GRID(gridx, nexty) - z1;  // left-back (delta)
327
-      z3 = ABL_BG_GRID(nextx, gridy);       // right-front
328
-      d4 = ABL_BG_GRID(nextx, nexty) - z3;  // right-back (delta)
320
+      z1 = ABL_BG_GRID(thisg.x, thisg.y);       // left-front
321
+      d2 = ABL_BG_GRID(thisg.x, nextg.y) - z1;  // left-back (delta)
322
+      z3 = ABL_BG_GRID(nextg.x, thisg.y);       // right-front
323
+      d4 = ABL_BG_GRID(nextg.x, nextg.y) - z3;  // right-back (delta)
329 324
     }
330 325
 
331
-    // Bilinear interpolate. Needed since ry or gridx has changed.
332
-                L = z1 + d2 * ratio_y;   // Linear interp. LF -> LB
333
-    const float R = z3 + d4 * ratio_y;   // Linear interp. RF -> RB
326
+    // Bilinear interpolate. Needed since rel.y or thisg.x has changed.
327
+                L = z1 + d2 * ratio.y;   // Linear interp. LF -> LB
328
+    const float R = z3 + d4 * ratio.y;   // Linear interp. RF -> RB
334 329
 
335 330
     D = R - L;
336 331
   }
337 332
 
338
-  const float offset = L + ratio_x * D;   // the offset almost always changes
333
+  const float offset = L + ratio.x * D;   // the offset almost always changes
339 334
 
340 335
   /*
341 336
   static float last_offset = 0;
342 337
   if (ABS(last_offset - offset) > 0.2) {
343
-    SERIAL_ECHOLNPAIR("Sudden Shift at x=", rx, " / ", bilinear_grid_spacing[X_AXIS], " -> gridx=", gridx);
344
-    SERIAL_ECHOLNPAIR(" y=", ry, " / ", bilinear_grid_spacing[Y_AXIS], " -> gridy=", gridy);
345
-    SERIAL_ECHOLNPAIR(" ratio_x=", ratio_x, " ratio_y=", ratio_y);
338
+    SERIAL_ECHOLNPAIR("Sudden Shift at x=", rel.x, " / ", bilinear_grid_spacing.x, " -> thisg.x=", thisg.x);
339
+    SERIAL_ECHOLNPAIR(" y=", rel.y, " / ", bilinear_grid_spacing.y, " -> thisg.y=", thisg.y);
340
+    SERIAL_ECHOLNPAIR(" ratio.x=", ratio.x, " ratio.y=", ratio.y);
346 341
     SERIAL_ECHOLNPAIR(" z1=", z1, " z2=", z2, " z3=", z3, " z4=", z4);
347 342
     SERIAL_ECHOLNPAIR(" L=", L, " R=", R, " offset=", offset);
348 343
   }
@@ -354,7 +349,7 @@ float bilinear_z_offset(const float raw[XYZ]) {
354 349
 
355 350
 #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
356 351
 
357
-  #define CELL_INDEX(A,V) ((V - bilinear_start[_AXIS(A)]) * ABL_BG_FACTOR(_AXIS(A)))
352
+  #define CELL_INDEX(A,V) ((V - bilinear_start.A) * ABL_BG_FACTOR(A))
358 353
 
359 354
   /**
360 355
    * Prepare a bilinear-leveled linear move on Cartesian,
@@ -362,62 +357,61 @@ float bilinear_z_offset(const float raw[XYZ]) {
362 357
    */
363 358
   void bilinear_line_to_destination(const feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) {
364 359
     // Get current and destination cells for this line
365
-    int cx1 = CELL_INDEX(X, current_position[X_AXIS]),
366
-        cy1 = CELL_INDEX(Y, current_position[Y_AXIS]),
367
-        cx2 = CELL_INDEX(X, destination[X_AXIS]),
368
-        cy2 = CELL_INDEX(Y, destination[Y_AXIS]);
369
-    LIMIT(cx1, 0, ABL_BG_POINTS_X - 2);
370
-    LIMIT(cy1, 0, ABL_BG_POINTS_Y - 2);
371
-    LIMIT(cx2, 0, ABL_BG_POINTS_X - 2);
372
-    LIMIT(cy2, 0, ABL_BG_POINTS_Y - 2);
360
+    xy_int_t c1 { CELL_INDEX(x, current_position.x), CELL_INDEX(y, current_position.y) },
361
+             c2 { CELL_INDEX(x, destination.x), CELL_INDEX(y, destination.y) };
362
+    LIMIT(c1.x, 0, ABL_BG_POINTS_X - 2);
363
+    LIMIT(c1.y, 0, ABL_BG_POINTS_Y - 2);
364
+    LIMIT(c2.x, 0, ABL_BG_POINTS_X - 2);
365
+    LIMIT(c2.y, 0, ABL_BG_POINTS_Y - 2);
373 366
 
374 367
     // Start and end in the same cell? No split needed.
375
-    if (cx1 == cx2 && cy1 == cy2) {
376
-      set_current_from_destination();
368
+    if (c1 == c2) {
369
+      current_position = destination;
377 370
       line_to_current_position(scaled_fr_mm_s);
378 371
       return;
379 372
     }
380 373
 
381
-    #define LINE_SEGMENT_END(A) (current_position[_AXIS(A)] + (destination[_AXIS(A)] - current_position[_AXIS(A)]) * normalized_dist)
374
+    #define LINE_SEGMENT_END(A) (current_position.A + (destination.A - current_position.A) * normalized_dist)
382 375
 
383
-    float normalized_dist, end[XYZE];
384
-    const int8_t gcx = _MAX(cx1, cx2), gcy = _MAX(cy1, cy2);
376
+    float normalized_dist;
377
+    xyze_pos_t end;
378
+    const xy_int8_t gc { _MAX(c1.x, c2.x), _MAX(c1.y, c2.y) };
385 379
 
386 380
     // Crosses on the X and not already split on this X?
387 381
     // The x_splits flags are insurance against rounding errors.
388
-    if (cx2 != cx1 && TEST(x_splits, gcx)) {
382
+    if (c2.x != c1.x && TEST(x_splits, gc.x)) {
389 383
       // Split on the X grid line
390
-      CBI(x_splits, gcx);
391
-      COPY(end, destination);
392
-      destination[X_AXIS] = bilinear_start[X_AXIS] + ABL_BG_SPACING(X_AXIS) * gcx;
393
-      normalized_dist = (destination[X_AXIS] - current_position[X_AXIS]) / (end[X_AXIS] - current_position[X_AXIS]);
394
-      destination[Y_AXIS] = LINE_SEGMENT_END(Y);
384
+      CBI(x_splits, gc.x);
385
+      end = destination;
386
+      destination.x = bilinear_start.x + ABL_BG_SPACING(x) * gc.x;
387
+      normalized_dist = (destination.x - current_position.x) / (end.x - current_position.x);
388
+      destination.y = LINE_SEGMENT_END(y);
395 389
     }
396 390
     // Crosses on the Y and not already split on this Y?
397
-    else if (cy2 != cy1 && TEST(y_splits, gcy)) {
391
+    else if (c2.y != c1.y && TEST(y_splits, gc.y)) {
398 392
       // Split on the Y grid line
399
-      CBI(y_splits, gcy);
400
-      COPY(end, destination);
401
-      destination[Y_AXIS] = bilinear_start[Y_AXIS] + ABL_BG_SPACING(Y_AXIS) * gcy;
402
-      normalized_dist = (destination[Y_AXIS] - current_position[Y_AXIS]) / (end[Y_AXIS] - current_position[Y_AXIS]);
403
-      destination[X_AXIS] = LINE_SEGMENT_END(X);
393
+      CBI(y_splits, gc.y);
394
+      end = destination;
395
+      destination.y = bilinear_start.y + ABL_BG_SPACING(y) * gc.y;
396
+      normalized_dist = (destination.y - current_position.y) / (end.y - current_position.y);
397
+      destination.x = LINE_SEGMENT_END(x);
404 398
     }
405 399
     else {
406 400
       // Must already have been split on these border(s)
407 401
       // This should be a rare case.
408
-      set_current_from_destination();
402
+      current_position = destination;
409 403
       line_to_current_position(scaled_fr_mm_s);
410 404
       return;
411 405
     }
412 406
 
413
-    destination[Z_AXIS] = LINE_SEGMENT_END(Z);
414
-    destination[E_AXIS] = LINE_SEGMENT_END(E);
407
+    destination.z = LINE_SEGMENT_END(z);
408
+    destination.e = LINE_SEGMENT_END(e);
415 409
 
416 410
     // Do the split and look for more borders
417 411
     bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
418 412
 
419 413
     // Restore destination from stack
420
-    COPY(destination, end);
414
+    destination = end;
421 415
     bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
422 416
   }
423 417
 

+ 6
- 6
Marlin/src/feature/bedlevel/abl/abl.h View File

@@ -23,10 +23,10 @@
23 23
 
24 24
 #include "../../../inc/MarlinConfigPre.h"
25 25
 
26
-extern int bilinear_grid_spacing[2], bilinear_start[2];
27
-extern float bilinear_grid_factor[2],
28
-             z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
29
-float bilinear_z_offset(const float raw[XYZ]);
26
+extern xy_int_t bilinear_grid_spacing, bilinear_start;
27
+extern xy_float_t bilinear_grid_factor;
28
+extern bed_mesh_t z_values;
29
+float bilinear_z_offset(const xy_pos_t &raw);
30 30
 
31 31
 void extrapolate_unprobed_bed_level();
32 32
 void print_bilinear_leveling_grid();
@@ -40,6 +40,6 @@ void refresh_bed_level();
40 40
   void bilinear_line_to_destination(const feedRate_t &scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF);
41 41
 #endif
42 42
 
43
-#define _GET_MESH_X(I) (bilinear_start[X_AXIS] + (I) * bilinear_grid_spacing[X_AXIS])
44
-#define _GET_MESH_Y(J) (bilinear_start[Y_AXIS] + (J) * bilinear_grid_spacing[Y_AXIS])
43
+#define _GET_MESH_X(I) float(bilinear_start.x + (I) * bilinear_grid_spacing.x)
44
+#define _GET_MESH_Y(J) float(bilinear_start.y + (J) * bilinear_grid_spacing.y)
45 45
 #define Z_VALUES_ARR  z_values

+ 16
- 16
Marlin/src/feature/bedlevel/bedlevel.cpp View File

@@ -51,7 +51,7 @@ bool leveling_is_valid() {
51 51
     #if ENABLED(MESH_BED_LEVELING)
52 52
       mbl.has_mesh()
53 53
     #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
54
-      !!bilinear_grid_spacing[X_AXIS]
54
+      !!bilinear_grid_spacing.x
55 55
     #elif ENABLED(AUTO_BED_LEVELING_UBL)
56 56
       ubl.mesh_is_valid()
57 57
     #else // 3POINT, LINEAR
@@ -81,13 +81,13 @@ void set_bed_leveling_enabled(const bool enable/*=true*/) {
81 81
 
82 82
     #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
83 83
       // Force bilinear_z_offset to re-calculate next time
84
-      const float reset[XYZ] = { -9999.999, -9999.999, 0 };
84
+      const xyz_pos_t reset { -9999.999, -9999.999, 0 };
85 85
       (void)bilinear_z_offset(reset);
86 86
     #endif
87 87
 
88 88
     if (planner.leveling_active) {      // leveling from on to off
89 89
       // change unleveled current_position to physical current_position without moving steppers.
90
-      planner.apply_leveling(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS]);
90
+      planner.apply_leveling(current_position);
91 91
       planner.leveling_active = false;  // disable only AFTER calling apply_leveling
92 92
     }
93 93
     else {                              // leveling from off to on
@@ -116,9 +116,9 @@ TemporaryBedLevelingState::TemporaryBedLevelingState(const bool enable) : saved(
116 116
     planner.set_z_fade_height(zfh);
117 117
 
118 118
     if (leveling_was_active) {
119
-      const float oldpos[] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] };
119
+      const xyz_pos_t oldpos = current_position;
120 120
       set_bed_leveling_enabled(true);
121
-      if (do_report && memcmp(oldpos, current_position, sizeof(oldpos)))
121
+      if (do_report && oldpos != current_position)
122 122
         report_current_position();
123 123
     }
124 124
   }
@@ -137,8 +137,8 @@ void reset_bed_level() {
137 137
     #if ENABLED(MESH_BED_LEVELING)
138 138
       mbl.reset();
139 139
     #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
140
-      bilinear_start[X_AXIS] = bilinear_start[Y_AXIS] =
141
-      bilinear_grid_spacing[X_AXIS] = bilinear_grid_spacing[Y_AXIS] = 0;
140
+      bilinear_start.reset();
141
+      bilinear_grid_spacing.reset();
142 142
       for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++)
143 143
         for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) {
144 144
           z_values[x][y] = NAN;
@@ -223,25 +223,25 @@ void reset_bed_level() {
223 223
 
224 224
 #if EITHER(MESH_BED_LEVELING, PROBE_MANUALLY)
225 225
 
226
-  void _manual_goto_xy(const float &rx, const float &ry) {
226
+  void _manual_goto_xy(const xy_pos_t &pos) {
227 227
 
228 228
     #ifdef MANUAL_PROBE_START_Z
229
+      constexpr float startz = _MAX(0, MANUAL_PROBE_START_Z);
229 230
       #if MANUAL_PROBE_HEIGHT > 0
230
-        do_blocking_move_to(rx, ry, MANUAL_PROBE_HEIGHT);
231
-        do_blocking_move_to_z(_MAX(0,MANUAL_PROBE_START_Z));
231
+        do_blocking_move_to(pos, MANUAL_PROBE_HEIGHT);
232
+        do_blocking_move_to_z(startz);
232 233
       #else
233
-        do_blocking_move_to(rx, ry, _MAX(0,MANUAL_PROBE_START_Z));
234
+        do_blocking_move_to(pos, startz);
234 235
       #endif
235 236
     #elif MANUAL_PROBE_HEIGHT > 0
236
-      const float prev_z = current_position[Z_AXIS];
237
-      do_blocking_move_to(rx, ry, MANUAL_PROBE_HEIGHT);
237
+      const float prev_z = current_position.z;
238
+      do_blocking_move_to(pos, MANUAL_PROBE_HEIGHT);
238 239
       do_blocking_move_to_z(prev_z);
239 240
     #else
240
-      do_blocking_move_to_xy(rx, ry);
241
+      do_blocking_move_to_xy(pos);
241 242
     #endif
242 243
 
243
-    current_position[X_AXIS] = rx;
244
-    current_position[Y_AXIS] = ry;
244
+    current_position = pos;
245 245
 
246 246
     #if ENABLED(LCD_BED_LEVELING)
247 247
       ui.wait_for_bl_move = false;

+ 16
- 6
Marlin/src/feature/bedlevel/bedlevel.h View File

@@ -38,7 +38,7 @@ void reset_bed_level();
38 38
 #endif
39 39
 
40 40
 #if EITHER(MESH_BED_LEVELING, PROBE_MANUALLY)
41
-  void _manual_goto_xy(const float &x, const float &y);
41
+  void _manual_goto_xy(const xy_pos_t &pos);
42 42
 #endif
43 43
 
44 44
 /**
@@ -57,11 +57,6 @@ class TemporaryBedLevelingState {
57 57
 
58 58
   typedef float bed_mesh_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
59 59
 
60
-  typedef struct {
61
-    int8_t x_index, y_index;
62
-    float distance; // When populated, the distance from the search location
63
-  } mesh_index_pair;
64
-
65 60
   #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
66 61
     #include "abl/abl.h"
67 62
   #elif ENABLED(AUTO_BED_LEVELING_UBL)
@@ -71,6 +66,7 @@ class TemporaryBedLevelingState {
71 66
   #endif
72 67
 
73 68
   #define Z_VALUES(X,Y) Z_VALUES_ARR[X][Y]
69
+  #define _GET_MESH_POS(M) { _GET_MESH_X(M.a), _GET_MESH_Y(M.b) }
74 70
 
75 71
   #if EITHER(AUTO_BED_LEVELING_BILINEAR, MESH_BED_LEVELING)
76 72
 
@@ -85,4 +81,18 @@ class TemporaryBedLevelingState {
85 81
 
86 82
   #endif
87 83
 
84
+  struct mesh_index_pair {
85
+    xy_int8_t pos;
86
+    float distance;   // When populated, the distance from the search location
87
+    void invalidate() { pos = -1; }
88
+    bool valid() const { return pos.x >= 0 && pos.y >= 0; }
89
+    #if ENABLED(AUTO_BED_LEVELING_UBL)
90
+      xy_pos_t meshpos() {
91
+        return { ubl.mesh_index_to_xpos(pos.x), ubl.mesh_index_to_ypos(pos.y) };
92
+      }
93
+    #endif
94
+    operator xy_int8_t&() { return pos; }
95
+    operator const xy_int8_t&() const { return pos; }
96
+  };
97
+
88 98
 #endif

+ 26
- 29
Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp View File

@@ -24,10 +24,9 @@
24 24
 
25 25
 #if ENABLED(MESH_BED_LEVELING)
26 26
 
27
-  #include "mesh_bed_leveling.h"
27
+  #include "../bedlevel.h"
28 28
 
29 29
   #include "../../../module/motion.h"
30
-  #include "../../../feature/bedlevel/bedlevel.h"
31 30
 
32 31
   #if ENABLED(EXTENSIBLE_UI)
33 32
     #include "../../../lcd/extensible_ui/ui_api.h"
@@ -66,62 +65,60 @@
66 65
      */
67 66
     void mesh_bed_leveling::line_to_destination(const feedRate_t &scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) {
68 67
       // Get current and destination cells for this line
69
-      int cx1 = cell_index_x(current_position[X_AXIS]),
70
-          cy1 = cell_index_y(current_position[Y_AXIS]),
71
-          cx2 = cell_index_x(destination[X_AXIS]),
72
-          cy2 = cell_index_y(destination[Y_AXIS]);
73
-      NOMORE(cx1, GRID_MAX_POINTS_X - 2);
74
-      NOMORE(cy1, GRID_MAX_POINTS_Y - 2);
75
-      NOMORE(cx2, GRID_MAX_POINTS_X - 2);
76
-      NOMORE(cy2, GRID_MAX_POINTS_Y - 2);
68
+      xy_int8_t scel = cell_indexes(current_position), ecel = cell_indexes(destination);
69
+      NOMORE(scel.x, GRID_MAX_POINTS_X - 2);
70
+      NOMORE(scel.y, GRID_MAX_POINTS_Y - 2);
71
+      NOMORE(ecel.x, GRID_MAX_POINTS_X - 2);
72
+      NOMORE(ecel.y, GRID_MAX_POINTS_Y - 2);
77 73
 
78 74
       // Start and end in the same cell? No split needed.
79
-      if (cx1 == cx2 && cy1 == cy2) {
75
+      if (scel == ecel) {
80 76
         line_to_destination(scaled_fr_mm_s);
81
-        set_current_from_destination();
77
+        current_position = destination;
82 78
         return;
83 79
       }
84 80
 
85
-      #define MBL_SEGMENT_END(A) (current_position[_AXIS(A)] + (destination[_AXIS(A)] - current_position[_AXIS(A)]) * normalized_dist)
81
+      #define MBL_SEGMENT_END(A) (current_position.A + (destination.A - current_position.A) * normalized_dist)
86 82
 
87
-      float normalized_dist, end[XYZE];
88
-      const int8_t gcx = _MAX(cx1, cx2), gcy = _MAX(cy1, cy2);
83
+      float normalized_dist;
84
+      xyze_pos_t dest;
85
+      const int8_t gcx = _MAX(scel.x, ecel.x), gcy = _MAX(scel.y, ecel.y);
89 86
 
90 87
       // Crosses on the X and not already split on this X?
91 88
       // The x_splits flags are insurance against rounding errors.
92
-      if (cx2 != cx1 && TEST(x_splits, gcx)) {
89
+      if (ecel.x != scel.x && TEST(x_splits, gcx)) {
93 90
         // Split on the X grid line
94 91
         CBI(x_splits, gcx);
95
-        COPY(end, destination);
96
-        destination[X_AXIS] = index_to_xpos[gcx];
97
-        normalized_dist = (destination[X_AXIS] - current_position[X_AXIS]) / (end[X_AXIS] - current_position[X_AXIS]);
98
-        destination[Y_AXIS] = MBL_SEGMENT_END(Y);
92
+        dest = destination;
93
+        destination.x = index_to_xpos[gcx];
94
+        normalized_dist = (destination.x - current_position.x) / (dest.x - current_position.x);
95
+        destination.y = MBL_SEGMENT_END(y);
99 96
       }
100 97
       // Crosses on the Y and not already split on this Y?
101
-      else if (cy2 != cy1 && TEST(y_splits, gcy)) {
98
+      else if (ecel.y != scel.y && TEST(y_splits, gcy)) {
102 99
         // Split on the Y grid line
103 100
         CBI(y_splits, gcy);
104
-        COPY(end, destination);
105
-        destination[Y_AXIS] = index_to_ypos[gcy];
106
-        normalized_dist = (destination[Y_AXIS] - current_position[Y_AXIS]) / (end[Y_AXIS] - current_position[Y_AXIS]);
107
-        destination[X_AXIS] = MBL_SEGMENT_END(X);
101
+        dest = destination;
102
+        destination.y = index_to_ypos[gcy];
103
+        normalized_dist = (destination.y - current_position.y) / (dest.y - current_position.y);
104
+        destination.x = MBL_SEGMENT_END(x);
108 105
       }
109 106
       else {
110 107
         // Must already have been split on these border(s)
111 108
         // This should be a rare case.
112 109
         line_to_destination(scaled_fr_mm_s);
113
-        set_current_from_destination();
110
+        current_position = destination;
114 111
         return;
115 112
       }
116 113
 
117
-      destination[Z_AXIS] = MBL_SEGMENT_END(Z);
118
-      destination[E_AXIS] = MBL_SEGMENT_END(E);
114
+      destination.z = MBL_SEGMENT_END(z);
115
+      destination.e = MBL_SEGMENT_END(e);
119 116
 
120 117
       // Do the split and look for more borders
121 118
       line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
122 119
 
123 120
       // Restore destination from stack
124
-      COPY(destination, end);
121
+      destination = dest;
125 122
       line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
126 123
     }
127 124
 

+ 20
- 14
Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h View File

@@ -76,21 +76,27 @@ public:
76 76
     int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST);
77 77
     return constrain(cx, 0, (GRID_MAX_POINTS_X) - 2);
78 78
   }
79
-
80 79
   static int8_t cell_index_y(const float &y) {
81 80
     int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST);
82 81
     return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 2);
83 82
   }
83
+  static inline xy_int8_t cell_indexes(const float &x, const float &y) {
84
+    return { cell_index_x(x), cell_index_y(y) };
85
+  }
86
+  static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
84 87
 
85 88
   static int8_t probe_index_x(const float &x) {
86 89
     int8_t px = (x - (MESH_MIN_X) + 0.5f * (MESH_X_DIST)) * RECIPROCAL(MESH_X_DIST);
87 90
     return WITHIN(px, 0, GRID_MAX_POINTS_X - 1) ? px : -1;
88 91
   }
89
-
90 92
   static int8_t probe_index_y(const float &y) {
91 93
     int8_t py = (y - (MESH_MIN_Y) + 0.5f * (MESH_Y_DIST)) * RECIPROCAL(MESH_Y_DIST);
92 94
     return WITHIN(py, 0, GRID_MAX_POINTS_Y - 1) ? py : -1;
93 95
   }
96
+  static inline xy_int8_t probe_indexes(const float &x, const float &y) {
97
+    return { probe_index_x(x), probe_index_y(y) };
98
+  }
99
+  static inline xy_int8_t probe_indexes(const xy_pos_t &xy) { return probe_indexes(xy.x, xy.y); }
94 100
 
95 101
   static float calc_z0(const float &a0, const float &a1, const float &z1, const float &a2, const float &z2) {
96 102
     const float delta_z = (z2 - z1) / (a2 - a1),
@@ -98,21 +104,21 @@ public:
98 104
     return z1 + delta_a * delta_z;
99 105
   }
100 106
 
101
-  static float get_z(const float &x0, const float &y0
107
+  static float get_z(const xy_pos_t &pos
102 108
     #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
103
-      , const float &factor
109
+      , const float &factor=1.0f
104 110
     #endif
105 111
   ) {
106
-    const int8_t cx = cell_index_x(x0), cy = cell_index_y(y0);
107
-    const float z1 = calc_z0(x0, index_to_xpos[cx], z_values[cx][cy], index_to_xpos[cx + 1], z_values[cx + 1][cy]),
108
-                z2 = calc_z0(x0, index_to_xpos[cx], z_values[cx][cy + 1], index_to_xpos[cx + 1], z_values[cx + 1][cy + 1]),
109
-                z0 = calc_z0(y0, index_to_ypos[cy], z1, index_to_ypos[cy + 1], z2);
110
-
111
-    return z_offset + z0
112
-      #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
113
-        * factor
114
-      #endif
115
-    ;
112
+    #if DISABLED(ENABLE_LEVELING_FADE_HEIGHT)
113
+      constexpr float factor = 1.0f;
114
+    #endif
115
+    const xy_int8_t ind = cell_indexes(pos);
116
+    const float x1 = index_to_xpos[ind.x], x2 = index_to_xpos[ind.x+1],
117
+                y1 = index_to_xpos[ind.y], y2 = index_to_xpos[ind.y+1],
118
+                z1 = calc_z0(pos.x, x1, z_values[ind.x][ind.y  ], x2, z_values[ind.x+1][ind.y  ]),
119
+                z2 = calc_z0(pos.x, x1, z_values[ind.x][ind.y+1], x2, z_values[ind.x+1][ind.y+1]);
120
+
121
+    return z_offset + calc_z0(pos.y, y1, z1, y2, z2) * factor;
116 122
   }
117 123
 
118 124
   #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)

+ 2
- 3
Marlin/src/feature/bedlevel/ubl/ubl.cpp View File

@@ -176,8 +176,7 @@
176 176
     // Add XY probe offset from extruder because probe_at_point() subtracts them when
177 177
     // moving to the XY position to be measured. This ensures better agreement between
178 178
     // the current Z position after G28 and the mesh values.
179
-    const float current_xi = find_closest_x_index(current_position[X_AXIS] + probe_offset[X_AXIS]),
180
-                current_yi = find_closest_y_index(current_position[Y_AXIS] + probe_offset[Y_AXIS]);
179
+    const xy_int8_t curr = closest_indexes(xy_pos_t(current_position) + xy_pos_t(probe_offset));
181 180
 
182 181
     if (!lcd) SERIAL_EOL();
183 182
     for (int8_t j = GRID_MAX_POINTS_Y - 1; j >= 0; j--) {
@@ -193,7 +192,7 @@
193 192
       for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
194 193
 
195 194
         // Opening Brace or Space
196
-        const bool is_current = i == current_xi && j == current_yi;
195
+        const bool is_current = i == curr.x && j == curr.y;
197 196
         if (human) SERIAL_CHAR(is_current ? '[' : ' ');
198 197
 
199 198
         // Z Value at current I, J

+ 27
- 20
Marlin/src/feature/bedlevel/ubl/ubl.h View File

@@ -32,15 +32,12 @@
32 32
 #define UBL_OK false
33 33
 #define UBL_ERR true
34 34
 
35
-#define USE_NOZZLE_AS_REFERENCE 0
36
-#define USE_PROBE_AS_REFERENCE 1
37
-
38
-// ubl_G29.cpp
39
-
40 35
 enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP };
41 36
 
42 37
 // External references
43 38
 
39
+struct mesh_index_pair;
40
+
44 41
 #define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / float(GRID_MAX_POINTS_X - 1))
45 42
 #define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / float(GRID_MAX_POINTS_Y - 1))
46 43
 
@@ -52,10 +49,11 @@ class unified_bed_leveling {
52 49
                   g29_repetition_cnt,
53 50
                   g29_storage_slot,
54 51
                   g29_map_type;
55
-    static bool   g29_c_flag, g29_x_flag, g29_y_flag;
56
-    static float  g29_x_pos, g29_y_pos,
57
-                  g29_card_thickness,
52
+    static bool   g29_c_flag;
53
+    static float  g29_card_thickness,
58 54
                   g29_constant;
55
+    static xy_pos_t g29_pos;
56
+    static xy_bool_t xy_seen;
59 57
 
60 58
     #if HAS_BED_PROBE
61 59
       static int  g29_grid_size;
@@ -65,16 +63,19 @@ class unified_bed_leveling {
65 63
       static void move_z_with_encoder(const float &multiplier);
66 64
       static float measure_point_with_encoder();
67 65
       static float measure_business_card_thickness(float in_height);
68
-      static void manually_probe_remaining_mesh(const float&, const float&, const float&, const float&, const bool) _O0;
69
-      static void fine_tune_mesh(const float &rx, const float &ry, const bool do_ubl_mesh_map) _O0;
66
+      static void manually_probe_remaining_mesh(const xy_pos_t&, const float&, const float&, const bool) _O0;
67
+      static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) _O0;
70 68
     #endif
71 69
 
72 70
     static bool g29_parameter_parsing() _O0;
73 71
     static void shift_mesh_height();
74
-    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;
72
+    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;
75 73
     static void tilt_mesh_based_on_3pts(const float &z1, const float &z2, const float &z3);
76 74
     static void tilt_mesh_based_on_probed_grid(const bool do_ubl_mesh_map);
77 75
     static bool smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir);
76
+    static inline bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) {
77
+      return smart_fill_one(pos.x, pos.y, dir.x, dir.y);
78
+    }
78 79
     static void smart_fill_mesh();
79 80
 
80 81
     #if ENABLED(UBL_DEVEL_DEBUGGING)
@@ -91,7 +92,7 @@ class unified_bed_leveling {
91 92
     static void save_ubl_active_state_and_disable();
92 93
     static void restore_ubl_active_state_and_leave();
93 94
     static void display_map(const int) _O0;
94
-    static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const float&, const float&, const bool, uint16_t[16]) _O0;
95
+    static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0;
95 96
     static mesh_index_pair find_furthest_invalid_mesh_point() _O0;
96 97
     static void reset();
97 98
     static void invalidate();
@@ -118,14 +119,14 @@ class unified_bed_leveling {
118 119
 
119 120
     FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const float &z) { z_values[px][py] = z; }
120 121
 
121
-    static int8_t get_cell_index_x(const float &x) {
122
+    static int8_t cell_index_x(const float &x) {
122 123
       const int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST);
123 124
       return constrain(cx, 0, (GRID_MAX_POINTS_X) - 1);   // -1 is appropriate if we want all movement to the X_MAX
124 125
     }                                                     // position. But with this defined this way, it is possible
125 126
                                                           // to extrapolate off of this point even further out. Probably
126 127
                                                           // that is OK because something else should be keeping that from
127 128
                                                           // happening and should not be worried about at this level.
128
-    static int8_t get_cell_index_y(const float &y) {
129
+    static int8_t cell_index_y(const float &y) {
129 130
       const int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST);
130 131
       return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 1);   // -1 is appropriate if we want all movement to the Y_MAX
131 132
     }                                                     // position. But with this defined this way, it is possible
@@ -133,15 +134,22 @@ class unified_bed_leveling {
133 134
                                                           // that is OK because something else should be keeping that from
134 135
                                                           // happening and should not be worried about at this level.
135 136
 
136
-    static int8_t find_closest_x_index(const float &x) {
137
+    static inline xy_int8_t cell_indexes(const float &x, const float &y) {
138
+      return { cell_index_x(x), cell_index_y(y) };
139
+    }
140
+    static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
141
+
142
+    static int8_t closest_x_index(const float &x) {
137 143
       const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST);
138 144
       return WITHIN(px, 0, GRID_MAX_POINTS_X - 1) ? px : -1;
139 145
     }
140
-
141
-    static int8_t find_closest_y_index(const float &y) {
146
+    static int8_t closest_y_index(const float &y) {
142 147
       const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST);
143 148
       return WITHIN(py, 0, GRID_MAX_POINTS_Y - 1) ? py : -1;
144 149
     }
150
+    static inline xy_int8_t closest_indexes(const xy_pos_t &xy) {
151
+      return { closest_x_index(xy.x), closest_y_index(xy.y) };
152
+    }
145 153
 
146 154
     /**
147 155
      *                           z2   --|
@@ -228,8 +236,7 @@ class unified_bed_leveling {
228 236
      * on the Y position within the cell.
229 237
      */
230 238
     static float get_z_correction(const float &rx0, const float &ry0) {
231
-      const int8_t cx = get_cell_index_x(rx0),
232
-                   cy = get_cell_index_y(ry0); // return values are clamped
239
+      const int8_t cx = cell_index_x(rx0), cy = cell_index_y(ry0); // return values are clamped
233 240
 
234 241
       /**
235 242
        * Check if the requested location is off the mesh.  If so, and
@@ -275,11 +282,11 @@ class unified_bed_leveling {
275 282
       }
276 283
       return z0;
277 284
     }
285
+    static inline float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
278 286
 
279 287
     static inline float mesh_index_to_xpos(const uint8_t i) {
280 288
       return i < GRID_MAX_POINTS_X ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST);
281 289
     }
282
-
283 290
     static inline float mesh_index_to_ypos(const uint8_t i) {
284 291
       return i < GRID_MAX_POINTS_Y ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST);
285 292
     }

+ 239
- 251
Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp View File

@@ -53,8 +53,6 @@
53 53
 
54 54
   #define UBL_G29_P31
55 55
 
56
-  extern float destination[XYZE], current_position[XYZE];
57
-
58 56
   #if HAS_LCD_MENU
59 57
     void _lcd_ubl_output_map_lcd();
60 58
   #endif
@@ -67,13 +65,11 @@
67 65
          unified_bed_leveling::g29_repetition_cnt,
68 66
          unified_bed_leveling::g29_storage_slot = 0,
69 67
          unified_bed_leveling::g29_map_type;
70
-  bool   unified_bed_leveling::g29_c_flag,
71
-         unified_bed_leveling::g29_x_flag,
72
-         unified_bed_leveling::g29_y_flag;
73
-  float  unified_bed_leveling::g29_x_pos,
74
-         unified_bed_leveling::g29_y_pos,
75
-         unified_bed_leveling::g29_card_thickness = 0,
68
+  bool   unified_bed_leveling::g29_c_flag;
69
+  float  unified_bed_leveling::g29_card_thickness = 0,
76 70
          unified_bed_leveling::g29_constant = 0;
71
+  xy_bool_t unified_bed_leveling::xy_seen;
72
+  xy_pos_t unified_bed_leveling::g29_pos;
77 73
 
78 74
   #if HAS_BED_PROBE
79 75
     int  unified_bed_leveling::g29_grid_size;
@@ -330,18 +326,19 @@
330 326
       else {
331 327
         while (g29_repetition_cnt--) {
332 328
           if (cnt > 20) { cnt = 0; idle(); }
333
-          const mesh_index_pair location = find_closest_mesh_point_of_type(REAL, g29_x_pos, g29_y_pos, USE_NOZZLE_AS_REFERENCE, nullptr);
334
-          if (location.x_index < 0) {
335
-            // No more REACHABLE mesh points to invalidate, so we ASSUME the user
329
+          const mesh_index_pair closest = find_closest_mesh_point_of_type(REAL, g29_pos);
330
+          const xy_int8_t &cpos = closest.pos;
331
+          if (cpos.x < 0) {
332
+            // No more REAL mesh points to invalidate, so we ASSUME the user
336 333
             // meant to invalidate the ENTIRE mesh, which cannot be done with
337
-            // find_closest_mesh_point loop which only returns REACHABLE points.
334
+            // find_closest_mesh_point loop which only returns REAL points.
338 335
             set_all_mesh_points_to_value(NAN);
339 336
             SERIAL_ECHOLNPGM("Entire Mesh invalidated.\n");
340 337
             break;            // No more invalid Mesh Points to populate
341 338
           }
342
-          z_values[location.x_index][location.y_index] = NAN;
339
+          z_values[cpos.x][cpos.y] = NAN;
343 340
           #if ENABLED(EXTENSIBLE_UI)
344
-            ExtUI::onMeshUpdate(location.x_index, location.y_index, 0);
341
+            ExtUI::onMeshUpdate(closest, 0);
345 342
           #endif
346 343
           cnt++;
347 344
         }
@@ -448,13 +445,13 @@
448 445
               SERIAL_ECHOLNPGM("Mesh invalidated. Probing mesh.");
449 446
             }
450 447
             if (g29_verbose_level > 1) {
451
-              SERIAL_ECHOPAIR("Probing around (", g29_x_pos);
448
+              SERIAL_ECHOPAIR("Probing around (", g29_pos.x);
452 449
               SERIAL_CHAR(',');
453
-              SERIAL_ECHO(g29_y_pos);
450
+              SERIAL_ECHO(g29_pos.y);
454 451
               SERIAL_ECHOLNPGM(").\n");
455 452
             }
456
-            probe_entire_mesh(g29_x_pos + probe_offset[X_AXIS], g29_y_pos + probe_offset[Y_AXIS],
457
-                              parser.seen('T'), parser.seen('E'), parser.seen('U'));
453
+            const xy_pos_t near = g29_pos + probe_offset;
454
+            probe_entire_mesh(near, parser.seen('T'), parser.seen('E'), parser.seen('U'));
458 455
 
459 456
             report_current_position();
460 457
             probe_deployed = true;
@@ -470,7 +467,7 @@
470 467
             SERIAL_ECHOLNPGM("Manually probing unreachable mesh locations.");
471 468
             do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
472 469
 
473
-            if (parser.seen('C') && !g29_x_flag && !g29_y_flag) {
470
+            if (parser.seen('C') && !xy_seen) {
474 471
               /**
475 472
                * Use a good default location for the path.
476 473
                * The flipped > and < operators in these comparisons is intentional.
@@ -478,13 +475,14 @@
478 475
                * It may make sense to have Delta printers default to the center of the bed.
479 476
                * Until that is decided, this can be forced with the X and Y parameters.
480 477
                */
481
-              #if IS_KINEMATIC
482
-                g29_x_pos = X_HOME_POS;
483
-                g29_y_pos = Y_HOME_POS;
484
-              #else // cartesian
485
-                g29_x_pos = probe_offset[X_AXIS] > 0 ? X_BED_SIZE : 0;
486
-                g29_y_pos = probe_offset[Y_AXIS] < 0 ? Y_BED_SIZE : 0;
487
-              #endif
478
+              g29_pos.set(
479
+                #if IS_KINEMATIC
480
+                  X_HOME_POS, Y_HOME_POS
481
+                #else
482
+                  probe_offset.x > 0 ? X_BED_SIZE : 0,
483
+                  probe_offset.y < 0 ? Y_BED_SIZE : 0
484
+                #endif
485
+              );
488 486
             }
489 487
 
490 488
             if (parser.seen('B')) {
@@ -496,13 +494,13 @@
496 494
               probe_deployed = true;
497 495
             }
498 496
 
499
-            if (!position_is_reachable(g29_x_pos, g29_y_pos)) {
497
+            if (!position_is_reachable(g29_pos)) {
500 498
               SERIAL_ECHOLNPGM("XY outside printable radius.");
501 499
               return;
502 500
             }
503 501
 
504 502
             const float height = parser.floatval('H', Z_CLEARANCE_BETWEEN_PROBES);
505
-            manually_probe_remaining_mesh(g29_x_pos, g29_y_pos, height, g29_card_thickness, parser.seen('T'));
503
+            manually_probe_remaining_mesh(g29_pos, height, g29_card_thickness, parser.seen('T'));
506 504
 
507 505
             SERIAL_ECHOLNPGM("G29 P2 finished.");
508 506
 
@@ -530,20 +528,22 @@
530 528
             }
531 529
             else {
532 530
               while (g29_repetition_cnt--) {  // this only populates reachable mesh points near
533
-                const mesh_index_pair location = find_closest_mesh_point_of_type(INVALID, g29_x_pos, g29_y_pos, USE_NOZZLE_AS_REFERENCE, nullptr);
534
-                if (location.x_index < 0) {
535
-                  // No more REACHABLE INVALID mesh points to populate, so we ASSUME
531
+                const mesh_index_pair closest = find_closest_mesh_point_of_type(INVALID, g29_pos);
532
+                const xy_int8_t &cpos = closest.pos;
533
+                if (cpos.x < 0) {
534
+                  // No more REAL INVALID mesh points to populate, so we ASSUME
536 535
                   // user meant to populate ALL INVALID mesh points to value
537 536
                   for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++)
538 537
                     for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++)
539
-                      if (isnan(z_values[x][y]))
540
-                        z_values[x][y] = g29_constant;
538
+                      if (isnan(z_values[x][y])) z_values[x][y] = g29_constant;
541 539
                   break; // No more invalid Mesh Points to populate
542 540
                 }
543
-                z_values[location.x_index][location.y_index] = g29_constant;
544
-                #if ENABLED(EXTENSIBLE_UI)
545
-                  ExtUI::onMeshUpdate(location.x_index, location.y_index, z_values[location.x_index][location.y_index]);
546
-                #endif
541
+                else {
542
+                  z_values[cpos.x][cpos.y] = g29_constant;
543
+                  #if ENABLED(EXTENSIBLE_UI)
544
+                    ExtUI::onMeshUpdate(closest, g29_constant);
545
+                  #endif
546
+                }
547 547
               }
548 548
             }
549 549
           }
@@ -576,7 +576,7 @@
576 576
 
577 577
         case 4: // Fine Tune (i.e., Edit) the Mesh
578 578
           #if HAS_LCD_MENU
579
-            fine_tune_mesh(g29_x_pos, g29_y_pos, parser.seen('T'));
579
+            fine_tune_mesh(g29_pos, parser.seen('T'));
580 580
           #else
581 581
             SERIAL_ECHOLNPGM("?P4 is only available when an LCD is present.");
582 582
             return;
@@ -740,9 +740,7 @@
740 740
      * Probe all invalidated locations of the mesh that can be reached by the probe.
741 741
      * This attempts to fill in locations closest to the nozzle's start location first.
742 742
      */
743
-    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) {
744
-      mesh_index_pair location;
745
-
743
+    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) {
746 744
       #if HAS_LCD_MENU
747 745
         ui.capture();
748 746
       #endif
@@ -752,6 +750,7 @@
752 750
 
753 751
       uint8_t count = GRID_MAX_POINTS;
754 752
 
753
+      mesh_index_pair best;
755 754
       do {
756 755
         if (do_ubl_mesh_map) display_map(g29_map_type);
757 756
 
@@ -773,23 +772,23 @@
773 772
           }
774 773
         #endif
775 774
 
776
-        if (do_furthest)
777
-          location = find_furthest_invalid_mesh_point();
778
-        else
779
-          location = find_closest_mesh_point_of_type(INVALID, rx, ry, USE_PROBE_AS_REFERENCE, nullptr);
775
+        best = do_furthest
776
+          ? find_furthest_invalid_mesh_point()
777
+          : find_closest_mesh_point_of_type(INVALID, near, true);
780 778
 
781
-        if (location.x_index >= 0) {    // mesh point found and is reachable by probe
782
-          const float rawx = mesh_index_to_xpos(location.x_index),
783
-                      rawy = mesh_index_to_ypos(location.y_index),
784
-                      measured_z = probe_at_point(rawx, rawy, stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level); // TODO: Needs error handling
785
-          z_values[location.x_index][location.y_index] = measured_z;
779
+        if (best.pos.x >= 0) {    // mesh point found and is reachable by probe
780
+          const float measured_z = probe_at_point(
781
+                        best.meshpos(),
782
+                        stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level
783
+                      );
784
+          z_values[best.pos.x][best.pos.y] = measured_z;
786 785
           #if ENABLED(EXTENSIBLE_UI)
787
-            ExtUI::onMeshUpdate(location.x_index, location.y_index, measured_z);
786
+            ExtUI::onMeshUpdate(best, measured_z);
788 787
           #endif
789 788
         }
790 789
         SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
791 790
 
792
-      } while (location.x_index >= 0 && --count);
791
+      } while (best.pos.x >= 0 && --count);
793 792
 
794 793
       STOW_PROBE();
795 794
 
@@ -800,8 +799,8 @@
800 799
       restore_ubl_active_state_and_leave();
801 800
 
802 801
       do_blocking_move_to_xy(
803
-        constrain(rx - probe_offset[X_AXIS], MESH_MIN_X, MESH_MAX_X),
804
-        constrain(ry - probe_offset[Y_AXIS], MESH_MIN_Y, MESH_MAX_Y)
802
+        constrain(near.x - probe_offset.x, MESH_MIN_X, MESH_MAX_X),
803
+        constrain(near.y - probe_offset.y, MESH_MIN_Y, MESH_MAX_Y)
805 804
       );
806 805
     }
807 806
 
@@ -835,7 +834,7 @@
835 834
         idle();
836 835
         gcode.reset_stepper_timeout(); // Keep steppers powered
837 836
         if (encoder_diff) {
838
-          do_blocking_move_to_z(current_position[Z_AXIS] + float(encoder_diff) * multiplier);
837
+          do_blocking_move_to_z(current_position.z + float(encoder_diff) * multiplier);
839 838
           encoder_diff = 0;
840 839
         }
841 840
       }
@@ -844,7 +843,7 @@
844 843
     float unified_bed_leveling::measure_point_with_encoder() {
845 844
       KEEPALIVE_STATE(PAUSED_FOR_USER);
846 845
       move_z_with_encoder(0.01f);
847
-      return current_position[Z_AXIS];
846
+      return current_position.z;
848 847
     }
849 848
 
850 849
     static void echo_and_take_a_measurement() { SERIAL_ECHOLNPGM(" and take a measurement."); }
@@ -863,7 +862,7 @@
863 862
       echo_and_take_a_measurement();
864 863
 
865 864
       const float z1 = measure_point_with_encoder();
866
-      do_blocking_move_to_z(current_position[Z_AXIS] + SIZE_OF_LITTLE_RAISE);
865
+      do_blocking_move_to_z(current_position.z + SIZE_OF_LITTLE_RAISE);
867 866
       planner.synchronize();
868 867
 
869 868
       SERIAL_ECHOPGM("Remove shim");
@@ -872,7 +871,7 @@
872 871
 
873 872
       const float z2 = measure_point_with_encoder();
874 873
 
875
-      do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES);
874
+      do_blocking_move_to_z(current_position.z + Z_CLEARANCE_BETWEEN_PROBES);
876 875
 
877 876
       const float thickness = ABS(z1 - z2);
878 877
 
@@ -888,29 +887,33 @@
888 887
       return thickness;
889 888
     }
890 889
 
891
-    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) {
890
+    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) {
892 891
 
893 892
       ui.capture();
894 893
 
895 894
       save_ubl_active_state_and_disable();  // No bed level correction so only raw data is obtained
896
-      do_blocking_move_to(current_position[X_AXIS], current_position[Y_AXIS], z_clearance);
895
+      do_blocking_move_to(current_position.x, current_position.y, z_clearance);
897 896
 
898 897
       ui.return_to_status();
899 898
 
900 899
       mesh_index_pair location;
900
+      xy_int8_t &lpos = location.pos;
901 901
       do {
902
-        location = find_closest_mesh_point_of_type(INVALID, rx, ry, USE_NOZZLE_AS_REFERENCE, nullptr);
902
+        location = find_closest_mesh_point_of_type(INVALID, pos);
903 903
         // It doesn't matter if the probe can't reach the NAN location. This is a manual probe.
904
-        if (location.x_index < 0 && location.y_index < 0) continue;
904
+        if (!location.valid()) continue;
905 905
 
906
-        const float xProbe = mesh_index_to_xpos(location.x_index),
907
-                    yProbe = mesh_index_to_ypos(location.y_index);
906
+        const xyz_pos_t ppos = {
907
+          mesh_index_to_xpos(lpos.x),
908
+          mesh_index_to_ypos(lpos.y),
909
+          Z_CLEARANCE_BETWEEN_PROBES
910
+        };
908 911
 
909
-        if (!position_is_reachable(xProbe, yProbe)) break; // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points)
912
+        if (!position_is_reachable(ppos)) break; // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points)
910 913
 
911 914
         LCD_MESSAGEPGM(MSG_UBL_MOVING_TO_NEXT);
912 915
 
913
-        do_blocking_move_to(xProbe, yProbe, Z_CLEARANCE_BETWEEN_PROBES);
916
+        do_blocking_move_to(ppos);
914 917
         do_blocking_move_to_z(z_clearance);
915 918
 
916 919
         KEEPALIVE_STATE(PAUSED_FOR_USER);
@@ -932,20 +935,20 @@
932 935
           return restore_ubl_active_state_and_leave();
933 936
         }
934 937
 
935
-        z_values[location.x_index][location.y_index] = current_position[Z_AXIS] - thick;
938
+        z_values[lpos.x][lpos.y] = current_position.z - thick;
936 939
         #if ENABLED(EXTENSIBLE_UI)
937
-          ExtUI::onMeshUpdate(location.x_index, location.y_index, z_values[location.x_index][location.y_index]);
940
+          ExtUI::onMeshUpdate(location, z_values[lpos.x][lpos.y]);
938 941
         #endif
939 942
 
940 943
         if (g29_verbose_level > 2)
941
-          SERIAL_ECHOLNPAIR_F("Mesh Point Measured at: ", z_values[location.x_index][location.y_index], 6);
944
+          SERIAL_ECHOLNPAIR_F("Mesh Point Measured at: ", z_values[lpos.x][lpos.y], 6);
942 945
         SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
943
-      } while (location.x_index >= 0 && location.y_index >= 0);
946
+      } while (location.valid());
944 947
 
945 948
       if (do_ubl_mesh_map) display_map(g29_map_type);  // show user where we're probing
946 949
 
947 950
       restore_ubl_active_state_and_leave();
948
-      do_blocking_move_to(rx, ry, Z_CLEARANCE_DEPLOY_PROBE);
951
+      do_blocking_move_to(pos, Z_CLEARANCE_DEPLOY_PROBE);
949 952
     }
950 953
 
951 954
     inline void set_message_with_feedback(PGM_P const msg_P) {
@@ -959,8 +962,8 @@
959 962
       set_message_with_feedback(PSTR(MSG_EDITING_STOPPED));
960 963
     }
961 964
 
962
-    void unified_bed_leveling::fine_tune_mesh(const float &rx, const float &ry, const bool do_ubl_mesh_map) {
963
-      if (!parser.seen('R'))    // fine_tune_mesh() is special. If no repetition count flag is specified
965
+    void unified_bed_leveling::fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) {
966
+      if (!parser.seen('R'))      // fine_tune_mesh() is special. If no repetition count flag is specified
964 967
         g29_repetition_cnt = 1;   // do exactly one mesh location. Otherwise use what the parser decided.
965 968
 
966 969
       #if ENABLED(UBL_MESH_EDIT_MOVES_Z)
@@ -973,7 +976,7 @@
973 976
 
974 977
       mesh_index_pair location;
975 978
 
976
-      if (!position_is_reachable(rx, ry)) {
979
+      if (!position_is_reachable(pos)) {
977 980
         SERIAL_ECHOLNPGM("(X,Y) outside printable radius.");
978 981
         return;
979 982
       }
@@ -981,76 +984,78 @@
981 984
       save_ubl_active_state_and_disable();
982 985
 
983 986
       LCD_MESSAGEPGM(MSG_UBL_FINE_TUNE_MESH);
984
-      ui.capture();                                                 // Take over control of the LCD encoder
987
+      ui.capture();                                         // Take over control of the LCD encoder
985 988
 
986
-      do_blocking_move_to(rx, ry, Z_CLEARANCE_BETWEEN_PROBES);      // Move to the given XY with probe clearance
989
+      do_blocking_move_to(pos, Z_CLEARANCE_BETWEEN_PROBES); // Move to the given XY with probe clearance
987 990
 
988 991
       #if ENABLED(UBL_MESH_EDIT_MOVES_Z)
989
-        do_blocking_move_to_z(h_offset);                            // Move Z to the given 'H' offset
992
+        do_blocking_move_to_z(h_offset);                    // Move Z to the given 'H' offset
990 993
       #endif
991 994
 
992
-      uint16_t not_done[16];
993
-      memset(not_done, 0xFF, sizeof(not_done));
995
+      MeshFlags done_flags{0};
996
+      xy_int8_t &lpos = location.pos;
994 997
       do {
995
-        location = find_closest_mesh_point_of_type(SET_IN_BITMAP, rx, ry, USE_NOZZLE_AS_REFERENCE, not_done);
996
-
997
-        if (location.x_index < 0) break;                            // Stop when there are no more reachable points
998
+        location = find_closest_mesh_point_of_type(SET_IN_BITMAP, pos, false, &done_flags);
998 999
 
999
-        bitmap_clear(not_done, location.x_index, location.y_index); // Mark this location as 'adjusted' so a new
1000
-                                                                    // location is used on the next loop
1000
+        if (lpos.x < 0) break;                              // Stop when there are no more reachable points
1001 1001
 
1002
-        const float rawx = mesh_index_to_xpos(location.x_index),
1003
-                    rawy = mesh_index_to_ypos(location.y_index);
1002
+        done_flags.mark(lpos);                              // Mark this location as 'adjusted' so a new
1003
+                                                            // location is used on the next loop
1004
+        const xyz_pos_t raw = {
1005
+          mesh_index_to_xpos(lpos.x),
1006
+          mesh_index_to_ypos(lpos.y),
1007
+          Z_CLEARANCE_BETWEEN_PROBES
1008
+        };
1004 1009
 
1005
-        if (!position_is_reachable(rawx, rawy)) break;              // SHOULD NOT OCCUR because find_closest_mesh_point_of_type will only return reachable
1010
+        if (!position_is_reachable(raw)) break;             // SHOULD NOT OCCUR (find_closest_mesh_point_of_type only returns reachable)
1006 1011
 
1007
-        do_blocking_move_to(rawx, rawy, Z_CLEARANCE_BETWEEN_PROBES); // Move the nozzle to the edit point with probe clearance
1012
+        do_blocking_move_to(raw);                           // Move the nozzle to the edit point with probe clearance
1008 1013
 
1009 1014
         #if ENABLED(UBL_MESH_EDIT_MOVES_Z)
1010
-          do_blocking_move_to_z(h_offset);                          // Move Z to the given 'H' offset before editing
1015
+          do_blocking_move_to_z(h_offset);                  // Move Z to the given 'H' offset before editing
1011 1016
         #endif
1012 1017
 
1013 1018
         KEEPALIVE_STATE(PAUSED_FOR_USER);
1014 1019
 
1015
-        if (do_ubl_mesh_map) display_map(g29_map_type);             // Display the current point
1020
+        if (do_ubl_mesh_map) display_map(g29_map_type);     // Display the current point
1016 1021
 
1017 1022
         ui.refresh();
1018 1023
 
1019
-        float new_z = z_values[location.x_index][location.y_index];
1020
-        if (isnan(new_z)) new_z = 0;                                // Invalid points begin at 0
1021
-        new_z = FLOOR(new_z * 1000) * 0.001f;                       // Chop off digits after the 1000ths place
1024
+        float new_z = z_values[lpos.x][lpos.y];
1025
+        if (isnan(new_z)) new_z = 0;                        // Invalid points begin at 0
1026
+        new_z = FLOOR(new_z * 1000) * 0.001f;               // Chop off digits after the 1000ths place
1022 1027
 
1023 1028
         lcd_mesh_edit_setup(new_z);
1024 1029
 
1025 1030
         do {
1026 1031
           new_z = lcd_mesh_edit();
1027 1032
           #if ENABLED(UBL_MESH_EDIT_MOVES_Z)
1028
-            do_blocking_move_to_z(h_offset + new_z);                // Move the nozzle as the point is edited
1033
+            do_blocking_move_to_z(h_offset + new_z);        // Move the nozzle as the point is edited
1029 1034
           #endif
1030 1035
           idle();
1031
-          SERIAL_FLUSH();                                           // Prevent host M105 buffer overrun.
1036
+          SERIAL_FLUSH();                                   // Prevent host M105 buffer overrun.
1032 1037
         } while (!ui.button_pressed());
1033 1038
 
1034
-        if (!lcd_map_control) ui.return_to_status();                // Just editing a single point? Return to status
1039
+        if (!lcd_map_control) ui.return_to_status();        // Just editing a single point? Return to status
1035 1040
 
1036
-        if (click_and_hold(abort_fine_tune)) break;                 // Button held down? Abort editing
1041
+        if (click_and_hold(abort_fine_tune)) break;         // Button held down? Abort editing
1037 1042
 
1038
-        z_values[location.x_index][location.y_index] = new_z;       // Save the updated Z value
1043
+        z_values[lpos.x][lpos.y] = new_z;                   // Save the updated Z value
1039 1044
         #if ENABLED(EXTENSIBLE_UI)
1040
-          ExtUI::onMeshUpdate(location.x_index, location.y_index, new_z);
1045
+          ExtUI::onMeshUpdate(location, new_z);
1041 1046
         #endif
1042 1047
 
1043
-        serial_delay(20);                                           // No switch noise
1048
+        serial_delay(20);                                   // No switch noise
1044 1049
         ui.refresh();
1045 1050
 
1046
-      } while (location.x_index >= 0 && --g29_repetition_cnt > 0);
1051
+      } while (lpos.x >= 0 && --g29_repetition_cnt > 0);
1047 1052
 
1048 1053
       ui.release();
1049 1054
 
1050 1055
       if (do_ubl_mesh_map) display_map(g29_map_type);
1051 1056
       restore_ubl_active_state_and_leave();
1052 1057
 
1053
-      do_blocking_move_to(rx, ry, Z_CLEARANCE_BETWEEN_PROBES);
1058
+      do_blocking_move_to(pos, Z_CLEARANCE_BETWEEN_PROBES);
1054 1059
 
1055 1060
       LCD_MESSAGEPGM(MSG_UBL_DONE_EDITING_MESH);
1056 1061
       SERIAL_ECHOLNPGM("Done Editing Mesh");
@@ -1073,11 +1078,6 @@
1073 1078
     g29_constant = 0;
1074 1079
     g29_repetition_cnt = 0;
1075 1080
 
1076
-    g29_x_flag = parser.seenval('X');
1077
-    g29_x_pos = g29_x_flag ? parser.value_float() : current_position[X_AXIS];
1078
-    g29_y_flag = parser.seenval('Y');
1079
-    g29_y_pos = g29_y_flag ? parser.value_float() : current_position[Y_AXIS];
1080
-
1081 1081
     if (parser.seen('R')) {
1082 1082
       g29_repetition_cnt = parser.has_value() ? parser.value_int() : GRID_MAX_POINTS;
1083 1083
       NOMORE(g29_repetition_cnt, GRID_MAX_POINTS);
@@ -1124,17 +1124,24 @@
1124 1124
       #endif
1125 1125
     }
1126 1126
 
1127
-    if (g29_x_flag != g29_y_flag) {
1127
+    xy_seen.x = parser.seenval('X');
1128
+    float sx = xy_seen.x ? parser.value_float() : current_position.x;
1129
+    xy_seen.y = parser.seenval('Y');
1130
+    float sy = xy_seen.y ? parser.value_float() : current_position.y;
1131
+
1132
+    if (xy_seen.x != xy_seen.y) {
1128 1133
       SERIAL_ECHOLNPGM("Both X & Y locations must be specified.\n");
1129 1134
       err_flag = true;
1130 1135
     }
1131 1136
 
1132 1137
     // If X or Y are not valid, use center of the bed values
1133
-    if (!WITHIN(g29_x_pos, X_MIN_BED, X_MAX_BED)) g29_x_pos = X_CENTER;
1134
-    if (!WITHIN(g29_y_pos, Y_MIN_BED, Y_MAX_BED)) g29_y_pos = Y_CENTER;
1138
+    if (!WITHIN(sx, X_MIN_BED, X_MAX_BED)) sx = X_CENTER;
1139
+    if (!WITHIN(sy, Y_MIN_BED, Y_MAX_BED)) sy = Y_CENTER;
1135 1140
 
1136 1141
     if (err_flag) return UBL_ERR;
1137 1142
 
1143
+    g29_pos.set(sx, sy);
1144
+
1138 1145
     /**
1139 1146
      * Activate or deactivate UBL
1140 1147
      * Note: UBL's G29 restores the state set here when done.
@@ -1213,26 +1220,22 @@
1213 1220
 
1214 1221
   mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() {
1215 1222
 
1216
-    bool found_a_NAN  = false, found_a_real = false;
1223
+    bool found_a_NAN = false, found_a_real = false;
1217 1224
 
1218
-    mesh_index_pair out_mesh;
1219
-    out_mesh.x_index = out_mesh.y_index = -1;
1220
-    out_mesh.distance = -99999.99f;
1225
+    mesh_index_pair farthest { -1, -1, -99999.99 };
1221 1226
 
1222 1227
     for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
1223 1228
       for (int8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {
1224 1229
 
1225
-        if (isnan(z_values[i][j])) { // Check to see if this location holds an invalid mesh point
1226
-
1227
-          const float mx = mesh_index_to_xpos(i),
1228
-                      my = mesh_index_to_ypos(j);
1230
+        if (isnan(z_values[i][j])) {                  // Invalid mesh point?
1229 1231
 
1230
-          if (!position_is_reachable_by_probe(mx, my))  // make sure the probe can get to the mesh point
1232
+          // Skip points the probe can't reach
1233
+          if (!position_is_reachable_by_probe(mesh_index_to_xpos(i), mesh_index_to_ypos(j)))
1231 1234
             continue;
1232 1235
 
1233 1236
           found_a_NAN = true;
1234 1237
 
1235
-          int8_t closest_x = -1, closest_y = -1;
1238
+          xy_int8_t near { -1, -1 };
1236 1239
           float d1, d2 = 99999.9f;
1237 1240
           for (int8_t k = 0; k < GRID_MAX_POINTS_X; k++) {
1238 1241
             for (int8_t l = 0; l < GRID_MAX_POINTS_Y; l++) {
@@ -1245,84 +1248,75 @@
1245 1248
 
1246 1249
                 d1 = HYPOT(i - k, j - l) + (1.0f / ((millis() % 47) + 13));
1247 1250
 
1248
-                if (d1 < d2) {    // found a closer distance from invalid mesh point at (i,j) to defined mesh point at (k,l)
1249
-                  d2 = d1;        // found a closer location with
1250
-                  closest_x = i;  // an assigned mesh point value
1251
-                  closest_y = j;
1251
+                if (d1 < d2) {    // Invalid mesh point (i,j) is closer to the defined point (k,l)
1252
+                  d2 = d1;
1253
+                  near.set(i, j);
1252 1254
                 }
1253 1255
               }
1254 1256
             }
1255 1257
           }
1256 1258
 
1257 1259
           //
1258
-          // At this point d2 should have the closest defined mesh point to invalid mesh point (i,j)
1260
+          // At this point d2 should have the near defined mesh point to invalid mesh point (i,j)
1259 1261
           //
1260 1262
 
1261
-          if (found_a_real && (closest_x >= 0) && (d2 > out_mesh.distance)) {
1262
-            out_mesh.distance = d2;         // found an invalid location with a greater distance
1263
-            out_mesh.x_index = closest_x;   // to a defined mesh point
1264
-            out_mesh.y_index = closest_y;
1263
+          if (found_a_real && near.x >= 0 && d2 > farthest.distance) {
1264
+            farthest.pos = near;      // Found an invalid location farther from the defined mesh point
1265
+            farthest.distance = d2;
1265 1266
           }
1266 1267
         }
1267 1268
       } // for j
1268 1269
     } // for i
1269 1270
 
1270 1271
     if (!found_a_real && found_a_NAN) {        // if the mesh is totally unpopulated, start the probing
1271
-      out_mesh.x_index = GRID_MAX_POINTS_X / 2;
1272
-      out_mesh.y_index = GRID_MAX_POINTS_Y / 2;
1273
-      out_mesh.distance = 1;
1272
+      farthest.pos.set(GRID_MAX_POINTS_X / 2, GRID_MAX_POINTS_Y / 2);
1273
+      farthest.distance = 1;
1274 1274
     }
1275
-    return out_mesh;
1275
+    return farthest;
1276 1276
   }
1277 1277
 
1278
-  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]) {
1279
-    mesh_index_pair out_mesh;
1280
-    out_mesh.x_index = out_mesh.y_index = -1;
1281
-    out_mesh.distance = -99999.9f;
1278
+  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*/) {
1279
+    mesh_index_pair closest;
1280
+    closest.invalidate();
1281
+    closest.distance = -99999.9f;
1282 1282
 
1283
-    // Get our reference position. Either the nozzle or probe location.
1284
-    const float px = rx + (probe_as_reference == USE_PROBE_AS_REFERENCE ? probe_offset[X_AXIS] : 0),
1285
-                py = ry + (probe_as_reference == USE_PROBE_AS_REFERENCE ? probe_offset[Y_AXIS] : 0);
1283
+    // Get the reference position, either nozzle or probe
1284
+    const xy_pos_t ref = probe_relative ? pos + probe_offset : pos;
1286 1285
 
1287 1286
     float best_so_far = 99999.99f;
1288 1287
 
1289 1288
     for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
1290 1289
       for (int8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {
1291
-
1292
-        if ( (type == INVALID && isnan(z_values[i][j]))  // Check to see if this location holds the right thing
1293
-          || (type == REAL && !isnan(z_values[i][j]))
1294
-          || (type == SET_IN_BITMAP && is_bitmap_set(bits, i, j))
1290
+        if ( (type == (isnan(z_values[i][j]) ? INVALID : REAL))
1291
+          || (type == SET_IN_BITMAP && !done_flags->marked(i, j))
1295 1292
         ) {
1296
-          // We only get here if we found a Mesh Point of the specified type
1297
-
1298
-          const float mx = mesh_index_to_xpos(i),
1299
-                      my = mesh_index_to_ypos(j);
1293
+          // Found a Mesh Point of the specified type!
1294
+          const xy_pos_t mpos = { mesh_index_to_xpos(i), mesh_index_to_ypos(j) };
1300 1295
 
1301 1296
           // If using the probe as the reference there are some unreachable locations.
1302 1297
           // Also for round beds, there are grid points outside the bed the nozzle can't reach.
1303 1298
           // Prune them from the list and ignore them till the next Phase (manual nozzle probing).
1304 1299
 
1305
-          if (probe_as_reference ? !position_is_reachable_by_probe(mx, my) : !position_is_reachable(mx, my))
1300
+          if (probe_relative ? !position_is_reachable_by_probe(mpos) : !position_is_reachable(mpos))
1306 1301
             continue;
1307 1302
 
1308 1303
           // Reachable. Check if it's the best_so_far location to the nozzle.
1309 1304
 
1310
-          float distance = HYPOT(px - mx, py - my);
1305
+          const xy_pos_t diff = current_position - mpos;
1306
+          const float distance = (ref - mpos).magnitude() + diff.magnitude() * 0.1f;
1311 1307
 
1312 1308
           // factor in the distance from the current location for the normal case
1313 1309
           // so the nozzle isn't running all over the bed.
1314
-          distance += HYPOT(current_position[X_AXIS] - mx, current_position[Y_AXIS] - my) * 0.1f;
1315 1310
           if (distance < best_so_far) {
1316
-            best_so_far = distance;   // We found a closer location with
1317
-            out_mesh.x_index = i;     // the specified type of mesh value.
1318
-            out_mesh.y_index = j;
1319
-            out_mesh.distance = best_so_far;
1311
+            best_so_far = distance;   // Found a closer location with the desired value type.
1312
+            closest.pos.set(i, j);
1313
+            closest.distance = best_so_far;
1320 1314
           }
1321 1315
         }
1322 1316
       } // for j
1323 1317
     } // for i
1324 1318
 
1325
-    return out_mesh;
1319
+    return closest;
1326 1320
   }
1327 1321
 
1328 1322
   /**
@@ -1332,20 +1326,20 @@
1332 1326
    */
1333 1327
 
1334 1328
   bool unified_bed_leveling::smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir) {
1335
-    const int8_t x1 = x + xdir, x2 = x1 + xdir,
1336
-                 y1 = y + ydir, y2 = y1 + ydir;
1337
-    // A NAN next to a pair of real values?
1338
-    if (isnan(z_values[x][y]) && !isnan(z_values[x1][y1]) && !isnan(z_values[x2][y2])) {
1339
-      if (z_values[x1][y1] < z_values[x2][y2])                  // Angled downward?
1340
-        z_values[x][y] = z_values[x1][y1];                      // Use nearest (maybe a little too high.)
1341
-      else
1342
-        z_values[x][y] = 2.0f * z_values[x1][y1] - z_values[x2][y2];   // Angled upward...
1343
-
1344
-      #if ENABLED(EXTENSIBLE_UI)
1345
-        ExtUI::onMeshUpdate(x, y, z_values[x][y]);
1346
-      #endif
1347
-
1348
-      return true;
1329
+    const float v = z_values[x][y];
1330
+    if (isnan(v)) {                           // A NAN...
1331
+      const int8_t dx = x + xdir, dy = y + ydir;
1332
+      const float v1 = z_values[dx][dy];
1333
+      if (!isnan(v1)) {                       // ...next to a pair of real values?
1334
+        const float v2 = z_values[dx + xdir][dy + ydir];
1335
+        if (!isnan(v2)) {
1336
+          z_values[x][y] = v1 < v2 ? v1 : v1 + v1 - v2;
1337
+          #if ENABLED(EXTENSIBLE_UI)
1338
+            ExtUI::onMeshUpdate(x, y, z_values[pos.x][pos.y]);
1339
+          #endif
1340
+          return true;
1341
+        }
1342
+      }
1349 1343
     }
1350 1344
     return false;
1351 1345
   }
@@ -1391,15 +1385,15 @@
1391 1385
                   dx = (x_max - x_min) / (g29_grid_size - 1),
1392 1386
                   dy = (y_max - y_min) / (g29_grid_size - 1);
1393 1387
 
1394
-      vector_3 points[3] = {
1388
+      const vector_3 points[3] = {
1395 1389
         #if ENABLED(HAS_FIXED_3POINT)
1396
-          vector_3(PROBE_PT_1_X, PROBE_PT_1_Y, 0),
1397
-          vector_3(PROBE_PT_2_X, PROBE_PT_2_Y, 0),
1398
-          vector_3(PROBE_PT_3_X, PROBE_PT_3_Y, 0)
1390
+          { PROBE_PT_1_X, PROBE_PT_1_Y, 0 },
1391
+          { PROBE_PT_2_X, PROBE_PT_2_Y, 0 },
1392
+          { PROBE_PT_3_X, PROBE_PT_3_Y, 0 }
1399 1393
         #else
1400
-          vector_3(x_min, y_min, 0),
1401
-          vector_3(x_max, y_min, 0),
1402
-          vector_3((x_max - x_min) / 2, y_max, 0)
1394
+          { x_min, y_min, 0 },
1395
+          { x_max, y_min, 0 },
1396
+          { (x_max - x_min) / 2, y_max, 0 }
1403 1397
         #endif
1404 1398
       };
1405 1399
 
@@ -1419,11 +1413,11 @@
1419 1413
           ui.status_printf_P(0, PSTR(MSG_LCD_TILTING_MESH " 1/3"));
1420 1414
         #endif
1421 1415
 
1422
-        measured_z = probe_at_point(points[0].x, points[0].y, PROBE_PT_RAISE, g29_verbose_level);
1416
+        measured_z = probe_at_point(points[0], PROBE_PT_RAISE, g29_verbose_level);
1423 1417
         if (isnan(measured_z))
1424 1418
           abort_flag = true;
1425 1419
         else {
1426
-          measured_z -= get_z_correction(points[0].x, points[0].y);
1420
+          measured_z -= get_z_correction(points[0]);
1427 1421
           #ifdef VALIDATE_MESH_TILT
1428 1422
             z1 = measured_z;
1429 1423
           #endif
@@ -1431,7 +1425,7 @@
1431 1425
             serial_spaces(16);
1432 1426
             SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z);
1433 1427
           }
1434
-          incremental_LSF(&lsf_results, points[0].x, points[0].y, measured_z);
1428
+          incremental_LSF(&lsf_results, points[0], measured_z);
1435 1429
         }
1436 1430
 
1437 1431
         if (!abort_flag) {
@@ -1440,19 +1434,19 @@
1440 1434
             ui.status_printf_P(0, PSTR(MSG_LCD_TILTING_MESH " 2/3"));
1441 1435
           #endif
1442 1436
 
1443
-          measured_z = probe_at_point(points[1].x, points[1].y, PROBE_PT_RAISE, g29_verbose_level);
1437
+          measured_z = probe_at_point(points[1], PROBE_PT_RAISE, g29_verbose_level);
1444 1438
           #ifdef VALIDATE_MESH_TILT
1445 1439
             z2 = measured_z;
1446 1440
           #endif
1447 1441
           if (isnan(measured_z))
1448 1442
             abort_flag = true;
1449 1443
           else {
1450
-            measured_z -= get_z_correction(points[1].x, points[1].y);
1444
+            measured_z -= get_z_correction(points[1]);
1451 1445
             if (g29_verbose_level > 3) {
1452 1446
               serial_spaces(16);
1453 1447
               SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z);
1454 1448
             }
1455
-            incremental_LSF(&lsf_results, points[1].x, points[1].y, measured_z);
1449
+            incremental_LSF(&lsf_results, points[1], measured_z);
1456 1450
           }
1457 1451
         }
1458 1452
 
@@ -1462,19 +1456,19 @@
1462 1456
             ui.status_printf_P(0, PSTR(MSG_LCD_TILTING_MESH " 3/3"));
1463 1457
           #endif
1464 1458
 
1465
-          measured_z = probe_at_point(points[2].x, points[2].y, PROBE_PT_STOW, g29_verbose_level);
1459
+          measured_z = probe_at_point(points[2], PROBE_PT_STOW, g29_verbose_level);
1466 1460
           #ifdef VALIDATE_MESH_TILT
1467 1461
             z3 = measured_z;
1468 1462
           #endif
1469 1463
           if (isnan(measured_z))
1470 1464
             abort_flag = true;
1471 1465
           else {
1472
-            measured_z -= get_z_correction(points[2].x, points[2].y);
1466
+            measured_z -= get_z_correction(points[2]);
1473 1467
             if (g29_verbose_level > 3) {
1474 1468
               serial_spaces(16);
1475 1469
               SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z);
1476 1470
             }
1477
-            incremental_LSF(&lsf_results, points[2].x, points[2].y, measured_z);
1471
+            incremental_LSF(&lsf_results, points[2], measured_z);
1478 1472
           }
1479 1473
         }
1480 1474
 
@@ -1494,10 +1488,11 @@
1494 1488
 
1495 1489
         uint16_t total_points = g29_grid_size * g29_grid_size, point_num = 1;
1496 1490
 
1491
+        xy_pos_t rpos;
1497 1492
         for (uint8_t ix = 0; ix < g29_grid_size; ix++) {
1498
-          const float rx = x_min + ix * dx;
1493
+          rpos.x = x_min + ix * dx;
1499 1494
           for (int8_t iy = 0; iy < g29_grid_size; iy++) {
1500
-            const float ry = y_min + dy * (zig_zag ? g29_grid_size - 1 - iy : iy);
1495
+            rpos.y = y_min + dy * (zig_zag ? g29_grid_size - 1 - iy : iy);
1501 1496
 
1502 1497
             if (!abort_flag) {
1503 1498
               SERIAL_ECHOLNPAIR("Tilting mesh point ", point_num, "/", total_points, "\n");
@@ -1505,24 +1500,24 @@
1505 1500
                 ui.status_printf_P(0, PSTR(MSG_LCD_TILTING_MESH " %i/%i"), point_num, total_points);
1506 1501
               #endif
1507 1502
 
1508
-              measured_z = probe_at_point(rx, ry, parser.seen('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level); // TODO: Needs error handling
1503
+              measured_z = probe_at_point(rpos, parser.seen('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level); // TODO: Needs error handling
1509 1504
 
1510 1505
               abort_flag = isnan(measured_z);
1511 1506
 
1512 1507
               if (DEBUGGING(LEVELING)) {
1508
+                const xy_pos_t lpos = rpos.asLogical();
1513 1509
                 DEBUG_CHAR('(');
1514
-                DEBUG_ECHO_F(rx, 7);
1510
+                DEBUG_ECHO_F(rpos.x, 7);
1515 1511
                 DEBUG_CHAR(',');
1516
-                DEBUG_ECHO_F(ry, 7);
1517
-                DEBUG_ECHOPGM(")   logical: (");
1518
-                DEBUG_ECHO_F(LOGICAL_X_POSITION(rx), 7);
1512
+                DEBUG_ECHO_F(rpos.y, 7);
1513
+                DEBUG_ECHOPAIR_F(")   logical: (", lpos.x, 7);
1519 1514
                 DEBUG_CHAR(',');
1520
-                DEBUG_ECHO_F(LOGICAL_Y_POSITION(ry), 7);
1515
+                DEBUG_ECHO_F(lpos.y, 7);
1521 1516
                 DEBUG_ECHOPAIR_F(")   measured: ", measured_z, 7);
1522
-                DEBUG_ECHOPAIR_F("   correction: ", get_z_correction(rx, ry), 7);
1517
+                DEBUG_ECHOPAIR_F("   correction: ", get_z_correction(rpos), 7);
1523 1518
               }
1524 1519
 
1525
-              measured_z -= get_z_correction(rx, ry) /* + probe_offset[Z_AXIS] */ ;
1520
+              measured_z -= get_z_correction(rpos) /* + probe_offset.z */ ;
1526 1521
 
1527 1522
               if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR_F("   final >>>---> ", measured_z, 7);
1528 1523
 
@@ -1530,7 +1525,7 @@
1530 1525
                 serial_spaces(16);
1531 1526
                 SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z);
1532 1527
               }
1533
-              incremental_LSF(&lsf_results, rx, ry, measured_z);
1528
+              incremental_LSF(&lsf_results, rpos, measured_z);
1534 1529
             }
1535 1530
 
1536 1531
             point_num++;
@@ -1564,33 +1559,33 @@
1564 1559
 
1565 1560
       for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
1566 1561
         for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {
1567
-          float x_tmp = mesh_index_to_xpos(i),
1568
-                y_tmp = mesh_index_to_ypos(j),
1569
-                z_tmp = z_values[i][j];
1562
+          float mx = mesh_index_to_xpos(i),
1563
+                my = mesh_index_to_ypos(j),
1564
+                mz = z_values[i][j];
1570 1565
 
1571 1566
           if (DEBUGGING(LEVELING)) {
1572
-            DEBUG_ECHOPAIR_F("before rotation = [", x_tmp, 7);
1567
+            DEBUG_ECHOPAIR_F("before rotation = [", mx, 7);
1573 1568
             DEBUG_CHAR(',');
1574
-            DEBUG_ECHO_F(y_tmp, 7);
1569
+            DEBUG_ECHO_F(my, 7);
1575 1570
             DEBUG_CHAR(',');
1576
-            DEBUG_ECHO_F(z_tmp, 7);
1571
+            DEBUG_ECHO_F(mz, 7);
1577 1572
             DEBUG_ECHOPGM("]   ---> ");
1578 1573
             DEBUG_DELAY(20);
1579 1574
           }
1580 1575
 
1581
-          apply_rotation_xyz(rotation, x_tmp, y_tmp, z_tmp);
1576
+          apply_rotation_xyz(rotation, mx, my, mz);
1582 1577
 
1583 1578
           if (DEBUGGING(LEVELING)) {
1584
-            DEBUG_ECHOPAIR_F("after rotation = [", x_tmp, 7);
1579
+            DEBUG_ECHOPAIR_F("after rotation = [", mx, 7);
1585 1580
             DEBUG_CHAR(',');
1586
-            DEBUG_ECHO_F(y_tmp, 7);
1581
+            DEBUG_ECHO_F(my, 7);
1587 1582
             DEBUG_CHAR(',');
1588
-            DEBUG_ECHO_F(z_tmp, 7);
1583
+            DEBUG_ECHO_F(mz, 7);
1589 1584
             DEBUG_ECHOLNPGM("]");
1590
-            DEBUG_DELAY(55);
1585
+            DEBUG_DELAY(20);
1591 1586
           }
1592 1587
 
1593
-          z_values[i][j] = z_tmp - lsf_results.D;
1588
+          z_values[i][j] = mz - lsf_results.D;
1594 1589
           #if ENABLED(EXTENSIBLE_UI)
1595 1590
             ExtUI::onMeshUpdate(i, j, z_values[i][j]);
1596 1591
           #endif
@@ -1613,41 +1608,32 @@
1613 1608
         DEBUG_EOL();
1614 1609
 
1615 1610
         /**
1616
-         * The following code can be used to check the validity of the mesh tilting algorithm.
1617
-         * When a 3-Point Mesh Tilt is done, the same algorithm is used as the grid based tilting.
1618
-         * The only difference is just 3 points are used in the calculations.   That fact guarantees
1619
-         * each probed point should have an exact match when a get_z_correction() for that location
1620
-         * is calculated.  The Z error between the probed point locations and the get_z_correction()
1611
+         * Use the code below to check the validity of the mesh tilting algorithm.
1612
+         * 3-Point Mesh Tilt uses the same algorithm as grid-based tilting, but only
1613
+         * three points are used in the calculation. This guarantees that each probed point
1614
+         * has an exact match when get_z_correction() for that location is calculated.
1615
+         * The Z error between the probed point locations and the get_z_correction()
1621 1616
          * numbers for those locations should be 0.
1622 1617
          */
1623 1618
         #ifdef VALIDATE_MESH_TILT
1624
-          float t, t1, d;
1625
-          t = normal.x * x_min + normal.y * y_min;
1626
-          d = t + normal.z * z1;
1627
-          DEBUG_ECHOPAIR_F("D from 1st point: ", d, 6);
1628
-          DEBUG_ECHOLNPAIR_F("   Z error: ", normal.z * z1 - get_z_correction(x_min, y_min), 6);
1629
-
1630
-          t = normal.x * x_max + normal.y * y_min;
1631
-          d = t + normal.z * z2;
1632
-          DEBUG_EOL();
1633
-          DEBUG_ECHOPAIR_F("D from 2nd point: ", d, 6);
1634
-          DEBUG_ECHOLNPAIR_F("   Z error: ", normal.z * z2 - get_z_correction(x_max, y_min), 6);
1635
-
1636
-          t = normal.x * ((x_max - x_min) / 2) + normal.y * (y_min);
1637
-          d = t + normal.z * z3;
1638
-          DEBUG_ECHOPAIR_F("D from 3rd point: ", d, 6);
1639
-          DEBUG_ECHOLNPAIR_F("   Z error: ", normal.z * z3 - get_z_correction((x_max - x_min) / 2, y_max), 6);
1640
-
1641
-          t = normal.x * (Z_SAFE_HOMING_X_POINT) + normal.y * (Z_SAFE_HOMING_Y_POINT);
1642
-          d = t + normal.z * 0;
1643
-          DEBUG_ECHOLNPAIR_F("D from home location with Z=0 : ", d, 6);
1644
-
1645
-          t = normal.x * (Z_SAFE_HOMING_X_POINT) + normal.y * (Z_SAFE_HOMING_Y_POINT);
1646
-          d = t + get_z_correction(Z_SAFE_HOMING_X_POINT, Z_SAFE_HOMING_Y_POINT); // normal.z * 0;
1647
-          DEBUG_ECHOPAIR_F("D from home location using mesh value for Z: ", d, 6);
1648
-
1619
+          auto d_from = []() { DEBUG_ECHOPGM("D from "); };
1620
+          auto normed = [&](const xy_pos_t &pos, const float &zadd) {
1621
+            return normal.x * pos.x + normal.y * pos.y + zadd;
1622
+          };
1623
+          auto debug_pt = [](PGM_P const pre, const xy_pos_t &pos, const float &zadd) {
1624
+            d_from(); serialprintPGM(pre);
1625
+            DEBUG_ECHO_F(normed(pos, zadd), 6);
1626
+            DEBUG_ECHOLNPAIR_F("   Z error: ", zadd - get_z_correction(pos), 6);
1627
+          };
1628
+          debug_pt(PSTR("1st point: "), probe_pt[0], normal.z * z1);
1629
+          debug_pt(PSTR("2nd point: "), probe_pt[1], normal.z * z2);
1630
+          debug_pt(PSTR("3rd point: "), probe_pt[2], normal.z * z3);
1631
+          d_from(); DEBUG_ECHOPGM("safe home with Z=");
1632
+          DEBUG_ECHOLNPAIR_F("0 : ", normed(safe_homing_xy, 0), 6);
1633
+          d_from(); DEBUG_ECHOPGM("safe home with Z=");
1634
+          DEBUG_ECHOLNPAIR_F("mesh value ", normed(safe_homing_xy, get_z_correction(safe_homing_xy)), 6);
1649 1635
           DEBUG_ECHOPAIR("   Z error: (", Z_SAFE_HOMING_X_POINT, ",", Z_SAFE_HOMING_Y_POINT);
1650
-          DEBUG_ECHOLNPAIR_F(") = ", get_z_correction(Z_SAFE_HOMING_X_POINT, Z_SAFE_HOMING_Y_POINT), 6);
1636
+          DEBUG_ECHOLNPAIR_F(") = ", get_z_correction(safe_homing_xy), 6);
1651 1637
         #endif
1652 1638
       } // DEBUGGING(LEVELING)
1653 1639
 
@@ -1676,21 +1662,23 @@
1676 1662
           if (!isnan(z_values[jx][jy]))
1677 1663
             SBI(bitmap[jx], jy);
1678 1664
 
1665
+      xy_pos_t ppos;
1679 1666
       for (uint8_t ix = 0; ix < GRID_MAX_POINTS_X; ix++) {
1680
-        const float px = mesh_index_to_xpos(ix);
1667
+        ppos.x = mesh_index_to_xpos(ix);
1681 1668
         for (uint8_t iy = 0; iy < GRID_MAX_POINTS_Y; iy++) {
1682
-          const float py = mesh_index_to_ypos(iy);
1669
+          ppos.y = mesh_index_to_ypos(iy);
1683 1670
           if (isnan(z_values[ix][iy])) {
1684
-            // undefined mesh point at (px,py), compute weighted LSF from original valid mesh points.
1671
+            // undefined mesh point at (ppos.x,ppos.y), compute weighted LSF from original valid mesh points.
1685 1672
             incremental_LSF_reset(&lsf_results);
1673
+            xy_pos_t rpos;
1686 1674
             for (uint8_t jx = 0; jx < GRID_MAX_POINTS_X; jx++) {
1687
-              const float rx = mesh_index_to_xpos(jx);
1675
+              rpos.x = mesh_index_to_xpos(jx);
1688 1676
               for (uint8_t jy = 0; jy < GRID_MAX_POINTS_Y; jy++) {
1689 1677
                 if (TEST(bitmap[jx], jy)) {
1690
-                  const float ry = mesh_index_to_ypos(jy),
1691
-                              rz = z_values[jx][jy],
1692
-                              w  = 1 + weight_scaled / HYPOT((rx - px), (ry - py));
1693
-                  incremental_WLSF(&lsf_results, rx, ry, rz, w);
1678
+                  rpos.y = mesh_index_to_ypos(jy);
1679
+                  const float rz = z_values[jx][jy],
1680
+                               w = 1.0f + weight_scaled / (rpos - ppos).magnitude();
1681
+                  incremental_WLSF(&lsf_results, rpos, rz, w);
1694 1682
                 }
1695 1683
               }
1696 1684
             }
@@ -1698,12 +1686,12 @@
1698 1686
               SERIAL_ECHOLNPGM("Insufficient data");
1699 1687
               return;
1700 1688
             }
1701
-            const float ez = -lsf_results.D - lsf_results.A * px - lsf_results.B * py;
1689
+            const float ez = -lsf_results.D - lsf_results.A * ppos.x - lsf_results.B * ppos.y;
1702 1690
             z_values[ix][iy] = ez;
1703 1691
             #if ENABLED(EXTENSIBLE_UI)
1704 1692
               ExtUI::onMeshUpdate(ix, iy, z_values[ix][iy]);
1705 1693
             #endif
1706
-            idle();   // housekeeping
1694
+            idle(); // housekeeping
1707 1695
           }
1708 1696
         }
1709 1697
       }
@@ -1734,7 +1722,7 @@
1734 1722
       adjust_mesh_to_mean(g29_c_flag, g29_constant);
1735 1723
 
1736 1724
       #if HAS_BED_PROBE
1737
-        SERIAL_ECHOLNPAIR_F("Probe Offset M851 Z", probe_offset[Z_AXIS], 7);
1725
+        SERIAL_ECHOLNPAIR_F("Probe Offset M851 Z", probe_offset.z, 7);
1738 1726
       #endif
1739 1727
 
1740 1728
       SERIAL_ECHOLNPAIR("MESH_MIN_X  " STRINGIFY(MESH_MIN_X) "=", MESH_MIN_X); serial_delay(50);

+ 156
- 199
Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp View File

@@ -35,12 +35,6 @@
35 35
 #include "../../../Marlin.h"
36 36
 #include <math.h>
37 37
 
38
-#if AVR_AT90USB1286_FAMILY  // Teensyduino & Printrboard IDE extensions have compile errors without this
39
-  inline void set_current_from_destination() { COPY(current_position, destination); }
40
-#else
41
-  extern void set_current_from_destination();
42
-#endif
43
-
44 38
 #if !UBL_SEGMENTED
45 39
 
46 40
   void unified_bed_leveling::line_to_destination_cartesian(const feedRate_t &scaled_fr_mm_s, const uint8_t extruder) {
@@ -50,60 +44,57 @@
50 44
      * just do the required Z-Height correction, call the Planner's buffer_line() routine, and leave
51 45
      */
52 46
     #if HAS_POSITION_MODIFIERS
53
-      float start[XYZE] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS] },
54
-            end[XYZE] = { destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS] };
47
+      xyze_pos_t start = current_position, end = destination;
55 48
       planner.apply_modifiers(start);
56 49
       planner.apply_modifiers(end);
57 50
     #else
58
-      const float (&start)[XYZE] = current_position,
59
-                    (&end)[XYZE] = destination;
51
+      const xyze_pos_t &start = current_position, &end = destination;
60 52
     #endif
61 53
 
62
-    const int cell_start_xi = get_cell_index_x(start[X_AXIS]),
63
-              cell_start_yi = get_cell_index_y(start[Y_AXIS]),
64
-              cell_dest_xi  = get_cell_index_x(end[X_AXIS]),
65
-              cell_dest_yi  = get_cell_index_y(end[Y_AXIS]);
54
+    const xy_int8_t istart = cell_indexes(start), iend = cell_indexes(end);
66 55
 
67 56
     // A move within the same cell needs no splitting
68
-    if (cell_start_xi == cell_dest_xi && cell_start_yi == cell_dest_yi) {
57
+    if (istart == iend) {
69 58
 
70 59
       // For a move off the bed, use a constant Z raise
71
-      if (!WITHIN(cell_dest_xi, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(cell_dest_yi, 0, GRID_MAX_POINTS_Y - 1)) {
60
+      if (!WITHIN(iend.x, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iend.y, 0, GRID_MAX_POINTS_Y - 1)) {
72 61
 
73 62
         // Note: There is no Z Correction in this case. We are off the grid and don't know what
74 63
         // a reasonable correction would be.  If the user has specified a UBL_Z_RAISE_WHEN_OFF_MESH
75 64
         // value, that will be used instead of a calculated (Bi-Linear interpolation) correction.
76 65
 
77
-        const float z_raise = 0.0
78
-          #ifdef UBL_Z_RAISE_WHEN_OFF_MESH
79
-            + UBL_Z_RAISE_WHEN_OFF_MESH
80
-          #endif
81
-        ;
82
-        planner.buffer_segment(end[X_AXIS], end[Y_AXIS], end[Z_AXIS] + z_raise, end[E_AXIS], scaled_fr_mm_s, extruder);
83
-        set_current_from_destination();
66
+        #ifdef UBL_Z_RAISE_WHEN_OFF_MESH
67
+          end.z += UBL_Z_RAISE_WHEN_OFF_MESH;
68
+        #endif
69
+        planner.buffer_segment(end, scaled_fr_mm_s, extruder);
70
+        current_position = destination;
84 71
         return;
85 72
       }
86 73
 
87 74
       FINAL_MOVE:
88 75
 
89 76
       // The distance is always MESH_X_DIST so multiply by the constant reciprocal.
90
-      const float xratio = (end[X_AXIS] - mesh_index_to_xpos(cell_dest_xi)) * RECIPROCAL(MESH_X_DIST);
77
+      const float xratio = (end.x - mesh_index_to_xpos(iend.x)) * RECIPROCAL(MESH_X_DIST);
91 78
 
92
-      float z1 = z_values[cell_dest_xi    ][cell_dest_yi    ] + xratio *
93
-                (z_values[cell_dest_xi + 1][cell_dest_yi    ] - z_values[cell_dest_xi][cell_dest_yi    ]),
94
-            z2 = z_values[cell_dest_xi    ][cell_dest_yi + 1] + xratio *
95
-                (z_values[cell_dest_xi + 1][cell_dest_yi + 1] - z_values[cell_dest_xi][cell_dest_yi + 1]);
96
-
97
-      if (cell_dest_xi >= GRID_MAX_POINTS_X - 1) z1 = z2 = 0.0;
79
+      float z1, z2;
80
+      if (iend.x >= GRID_MAX_POINTS_X - 1)
81
+        z1 = z2 = 0.0;
82
+      else {
83
+        z1 = z_values[iend.x    ][iend.y    ] + xratio *
84
+            (z_values[iend.x + 1][iend.y    ] - z_values[iend.x][iend.y    ]),
85
+        z2 = z_values[iend.x    ][iend.y + 1] + xratio *
86
+            (z_values[iend.x + 1][iend.y + 1] - z_values[iend.x][iend.y + 1]);
87
+      }
98 88
 
99 89
       // X cell-fraction done. Interpolate the two Z offsets with the Y fraction for the final Z offset.
100
-      const float yratio = (end[Y_AXIS] - mesh_index_to_ypos(cell_dest_yi)) * RECIPROCAL(MESH_Y_DIST),
101
-                  z0 = cell_dest_yi < GRID_MAX_POINTS_Y - 1 ? (z1 + (z2 - z1) * yratio) * planner.fade_scaling_factor_for_z(end[Z_AXIS]) : 0.0;
90
+      const float yratio = (end.y - mesh_index_to_ypos(iend.y)) * RECIPROCAL(MESH_Y_DIST),
91
+                  z0 = iend.y < GRID_MAX_POINTS_Y - 1 ? (z1 + (z2 - z1) * yratio) * planner.fade_scaling_factor_for_z(end.z) : 0.0;
102 92
 
103 93
       // Undefined parts of the Mesh in z_values[][] are NAN.
104 94
       // Replace NAN corrections with 0.0 to prevent NAN propagation.
105
-      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);
106
-      set_current_from_destination();
95
+      if (!isnan(z0)) end.z += z0;
96
+      planner.buffer_segment(end, scaled_fr_mm_s, extruder);
97
+      current_position = destination;
107 98
       return;
108 99
     }
109 100
 
@@ -112,17 +103,11 @@
112 103
      * case - crossing only one X or Y line - after details are worked out to reduce computation.
113 104
      */
114 105
 
115
-    const float dx = end[X_AXIS] - start[X_AXIS],
116
-                dy = end[Y_AXIS] - start[Y_AXIS];
117
-
118
-    const int left_flag = dx < 0.0 ? 1 : 0,
119
-              down_flag = dy < 0.0 ? 1 : 0;
120
-
121
-    const float adx = left_flag ? -dx : dx,
122
-                ady = down_flag ? -dy : dy;
123
-
124
-    const int dxi = cell_start_xi == cell_dest_xi ? 0 : left_flag ? -1 : 1,
125
-              dyi = cell_start_yi == cell_dest_yi ? 0 : down_flag ? -1 : 1;
106
+    const xy_float_t dist = end - start;
107
+    const xy_bool_t neg { dist.x < 0, dist.y < 0 };
108
+    const xy_int8_t ineg { int8_t(neg.x), int8_t(neg.y) };
109
+    const xy_float_t sign { neg.x ? -1.0f : 1.0f, neg.y ? -1.0f : 1.0f };
110
+    const xy_int8_t iadd { int8_t(iend.x == istart.x ? 0 : sign.x), int8_t(iend.y == istart.y ? 0 : sign.y) };
126 111
 
127 112
     /**
128 113
      * Compute the extruder scaling factor for each partial move, checking for
@@ -132,64 +117,64 @@
132 117
      * components. The larger of the two is used to preserve precision.
133 118
      */
134 119
 
135
-    const bool use_x_dist = adx > ady;
120
+    const xy_float_t ad = sign * dist;
121
+    const bool use_x_dist = ad.x > ad.y;
136 122
 
137
-    float on_axis_distance = use_x_dist ? dx : dy,
138
-          e_position = end[E_AXIS] - start[E_AXIS],
139
-          z_position = end[Z_AXIS] - start[Z_AXIS];
123
+    float on_axis_distance = use_x_dist ? dist.x : dist.y,
124
+          e_position = end.e - start.e,
125
+          z_position = end.z - start.z;
140 126
 
141
-    const float e_normalized_dist = e_position / on_axis_distance,
127
+    const float e_normalized_dist = e_position / on_axis_distance, // Allow divide by zero
142 128
                 z_normalized_dist = z_position / on_axis_distance;
143 129
 
144
-    int current_xi = cell_start_xi,
145
-        current_yi = cell_start_yi;
130
+    xy_int8_t icell = istart;
146 131
 
147
-    const float m = dy / dx,
148
-                c = start[Y_AXIS] - m * start[X_AXIS];
132
+    const float ratio = dist.y / dist.x,        // Allow divide by zero
133
+                c = start.y - ratio * start.x;
149 134
 
150
-    const bool inf_normalized_flag = (isinf(e_normalized_dist) != 0),
151
-               inf_m_flag = (isinf(m) != 0);
135
+    const bool inf_normalized_flag = isinf(e_normalized_dist),
136
+               inf_ratio_flag = isinf(ratio);
152 137
 
153 138
     /**
154 139
      * Handle vertical lines that stay within one column.
155 140
      * These need not be perfectly vertical.
156 141
      */
157
-    if (dxi == 0) {             // Vertical line?
158
-      current_yi += down_flag;  // Line going down? Just go to the bottom.
159
-      while (current_yi != cell_dest_yi + down_flag) {
160
-        current_yi += dyi;
161
-        const float next_mesh_line_y = mesh_index_to_ypos(current_yi);
142
+    if (iadd.x == 0) {        // Vertical line?
143
+      icell.y += ineg.y;      // Line going down? Just go to the bottom.
144
+      while (icell.y != iend.y + ineg.y) {
145
+        icell.y += iadd.y;
146
+        const float next_mesh_line_y = mesh_index_to_ypos(icell.y);
162 147
 
163 148
         /**
164 149
          * Skip the calculations for an infinite slope.
165 150
          * For others the next X is the same so this can continue.
166 151
          * Calculate X at the next Y mesh line.
167 152
          */
168
-        const float rx = inf_m_flag ? start[X_AXIS] : (next_mesh_line_y - c) / m;
153
+        const float rx = inf_ratio_flag ? start.x : (next_mesh_line_y - c) / ratio;
169 154
 
170
-        float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, current_xi, current_yi)
171
-                   * planner.fade_scaling_factor_for_z(end[Z_AXIS]);
155
+        float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, icell.x, icell.y)
156
+                   * planner.fade_scaling_factor_for_z(end.z);
172 157
 
173 158
         // Undefined parts of the Mesh in z_values[][] are NAN.
174 159
         // Replace NAN corrections with 0.0 to prevent NAN propagation.
175 160
         if (isnan(z0)) z0 = 0.0;
176 161
 
177
-        const float ry = mesh_index_to_ypos(current_yi);
162
+        const float ry = mesh_index_to_ypos(icell.y);
178 163
 
179 164
         /**
180 165
          * Without this check, it's possible to generate a zero length move, as in the case where
181 166
          * the line is heading down, starting exactly on a mesh line boundary. Since this is rare
182 167
          * it might be fine to remove this check and let planner.buffer_segment() filter it out.
183 168
          */
184
-        if (ry != start[Y_AXIS]) {
185
-          if (!inf_normalized_flag) {
186
-            on_axis_distance = use_x_dist ? rx - start[X_AXIS] : ry - start[Y_AXIS];
187
-            e_position = start[E_AXIS] + on_axis_distance * e_normalized_dist;
188
-            z_position = start[Z_AXIS] + on_axis_distance * z_normalized_dist;
169
+        if (ry != start.y) {
170
+          if (!inf_normalized_flag) { // fall-through faster than branch
171
+            on_axis_distance = use_x_dist ? rx - start.x : ry - start.y;
172
+            e_position = start.e + on_axis_distance * e_normalized_dist;
173
+            z_position = start.z + on_axis_distance * z_normalized_dist;
189 174
           }
190 175
           else {
191
-            e_position = end[E_AXIS];
192
-            z_position = end[Z_AXIS];
176
+            e_position = end.e;
177
+            z_position = end.z;
193 178
           }
194 179
 
195 180
           planner.buffer_segment(rx, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder);
@@ -197,10 +182,10 @@
197 182
       }
198 183
 
199 184
       // At the final destination? Usually not, but when on a Y Mesh Line it's completed.
200
-      if (current_position[X_AXIS] != end[X_AXIS] || current_position[Y_AXIS] != end[Y_AXIS])
185
+      if (xy_pos_t(current_position) != xy_pos_t(end))
201 186
         goto FINAL_MOVE;
202 187
 
203
-      set_current_from_destination();
188
+      current_position = destination;
204 189
       return;
205 190
     }
206 191
 
@@ -208,36 +193,34 @@
208 193
      * Handle horizontal lines that stay within one row.
209 194
      * These need not be perfectly horizontal.
210 195
      */
211
-    if (dyi == 0) {             // Horizontal line?
212
-      current_xi += left_flag;  // Heading left? Just go to the left edge of the cell for the first move.
213
-      while (current_xi != cell_dest_xi + left_flag) {
214
-        current_xi += dxi;
215
-        const float next_mesh_line_x = mesh_index_to_xpos(current_xi),
216
-                    ry = m * next_mesh_line_x + c;   // Calculate Y at the next X mesh line
196
+    if (iadd.y == 0) {      // Horizontal line?
197
+      icell.x += ineg.x;     // Heading left? Just go to the left edge of the cell for the first move.
198
+      while (icell.x != iend.x + ineg.x) {
199
+        icell.x += iadd.x;
200
+        const float rx = mesh_index_to_xpos(icell.x);
201
+        const float ry = ratio * rx + c;    // Calculate Y at the next X mesh line
217 202
 
218
-        float z0 = z_correction_for_y_on_vertical_mesh_line(ry, current_xi, current_yi)
219
-                   * planner.fade_scaling_factor_for_z(end[Z_AXIS]);
203
+        float z0 = z_correction_for_y_on_vertical_mesh_line(ry, icell.x, icell.y)
204
+                     * planner.fade_scaling_factor_for_z(end.z);
220 205
 
221 206
         // Undefined parts of the Mesh in z_values[][] are NAN.
222 207
         // Replace NAN corrections with 0.0 to prevent NAN propagation.
223 208
         if (isnan(z0)) z0 = 0.0;
224 209
 
225
-        const float rx = mesh_index_to_xpos(current_xi);
226
-
227 210
         /**
228 211
          * Without this check, it's possible to generate a zero length move, as in the case where
229 212
          * the line is heading left, starting exactly on a mesh line boundary. Since this is rare
230 213
          * it might be fine to remove this check and let planner.buffer_segment() filter it out.
231 214
          */
232
-        if (rx != start[X_AXIS]) {
215
+        if (rx != start.x) {
233 216
           if (!inf_normalized_flag) {
234
-            on_axis_distance = use_x_dist ? rx - start[X_AXIS] : ry - start[Y_AXIS];
235
-            e_position = start[E_AXIS] + on_axis_distance * e_normalized_dist;  // is based on X or Y because this is a horizontal move
236
-            z_position = start[Z_AXIS] + on_axis_distance * z_normalized_dist;
217
+            on_axis_distance = use_x_dist ? rx - start.x : ry - start.y;
218
+            e_position = start.e + on_axis_distance * e_normalized_dist;  // is based on X or Y because this is a horizontal move
219
+            z_position = start.z + on_axis_distance * z_normalized_dist;
237 220
           }
238 221
           else {
239
-            e_position = end[E_AXIS];
240
-            z_position = end[Z_AXIS];
222
+            e_position = end.e;
223
+            z_position = end.z;
241 224
           }
242 225
 
243 226
           if (!planner.buffer_segment(rx, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder))
@@ -245,93 +228,88 @@
245 228
         } //else printf("FIRST MOVE PRUNED  ");
246 229
       }
247 230
 
248
-      if (current_position[X_AXIS] != end[X_AXIS] || current_position[Y_AXIS] != end[Y_AXIS])
231
+      if (xy_pos_t(current_position) != xy_pos_t(end))
249 232
         goto FINAL_MOVE;
250 233
 
251
-      set_current_from_destination();
234
+      current_position = destination;
252 235
       return;
253 236
     }
254 237
 
255 238
     /**
256 239
      *
257
-     * Handle the generic case of a line crossing both X and Y Mesh lines.
240
+     * Generic case of a line crossing both X and Y Mesh lines.
258 241
      *
259 242
      */
260 243
 
261
-    int xi_cnt = cell_start_xi - cell_dest_xi,
262
-        yi_cnt = cell_start_yi - cell_dest_yi;
263
-
264
-    if (xi_cnt < 0) xi_cnt = -xi_cnt;
265
-    if (yi_cnt < 0) yi_cnt = -yi_cnt;
244
+    xy_int8_t cnt = (istart - iend).ABS();
266 245
 
267
-    current_xi += left_flag;
268
-    current_yi += down_flag;
246
+    icell += ineg;
269 247
 
270
-    while (xi_cnt || yi_cnt) {
248
+    while (cnt) {
271 249
 
272
-      const float next_mesh_line_x = mesh_index_to_xpos(current_xi + dxi),
273
-                  next_mesh_line_y = mesh_index_to_ypos(current_yi + dyi),
274
-                  ry = m * next_mesh_line_x + c,   // Calculate Y at the next X mesh line
275
-                  rx = (next_mesh_line_y - c) / m; // Calculate X at the next Y mesh line
276
-                                                   // (No need to worry about m being zero.
277
-                                                   //  If that was the case, it was already detected
278
-                                                   //  as a vertical line move above.)
250
+      const float next_mesh_line_x = mesh_index_to_xpos(icell.x + iadd.x),
251
+                  next_mesh_line_y = mesh_index_to_ypos(icell.y + iadd.y),
252
+                  ry = ratio * next_mesh_line_x + c,    // Calculate Y at the next X mesh line
253
+                  rx = (next_mesh_line_y - c) / ratio;  // Calculate X at the next Y mesh line
254
+                                                        // (No need to worry about ratio == 0.
255
+                                                        //  In that case, it was already detected
256
+                                                        //  as a vertical line move above.)
279 257
 
280
-      if (left_flag == (rx > next_mesh_line_x)) { // Check if we hit the Y line first
258
+      if (neg.x == (rx > next_mesh_line_x)) { // Check if we hit the Y line first
281 259
         // Yes!  Crossing a Y Mesh Line next
282
-        float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, current_xi - left_flag, current_yi + dyi)
283
-                   * planner.fade_scaling_factor_for_z(end[Z_AXIS]);
260
+        float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, icell.x - ineg.x, icell.y + iadd.y)
261
+                   * planner.fade_scaling_factor_for_z(end.z);
284 262
 
285 263
         // Undefined parts of the Mesh in z_values[][] are NAN.
286 264
         // Replace NAN corrections with 0.0 to prevent NAN propagation.
287 265
         if (isnan(z0)) z0 = 0.0;
288 266
 
289 267
         if (!inf_normalized_flag) {
290
-          on_axis_distance = use_x_dist ? rx - start[X_AXIS] : next_mesh_line_y - start[Y_AXIS];
291
-          e_position = start[E_AXIS] + on_axis_distance * e_normalized_dist;
292
-          z_position = start[Z_AXIS] + on_axis_distance * z_normalized_dist;
268
+          on_axis_distance = use_x_dist ? rx - start.x : next_mesh_line_y - start.y;
269
+          e_position = start.e + on_axis_distance * e_normalized_dist;
270
+          z_position = start.z + on_axis_distance * z_normalized_dist;
293 271
         }
294 272
         else {
295
-          e_position = end[E_AXIS];
296
-          z_position = end[Z_AXIS];
273
+          e_position = end.e;
274
+          z_position = end.z;
297 275
         }
298 276
         if (!planner.buffer_segment(rx, next_mesh_line_y, z_position + z0, e_position, scaled_fr_mm_s, extruder))
299 277
           break;
300
-        current_yi += dyi;
301
-        yi_cnt--;
278
+        icell.y += iadd.y;
279
+        cnt.y--;
302 280
       }
303 281
       else {
304 282
         // Yes!  Crossing a X Mesh Line next
305
-        float z0 = z_correction_for_y_on_vertical_mesh_line(ry, current_xi + dxi, current_yi - down_flag)
306
-                   * planner.fade_scaling_factor_for_z(end[Z_AXIS]);
283
+        float z0 = z_correction_for_y_on_vertical_mesh_line(ry, icell.x + iadd.x, icell.y - ineg.y)
284
+                   * planner.fade_scaling_factor_for_z(end.z);
307 285
 
308 286
         // Undefined parts of the Mesh in z_values[][] are NAN.
309 287
         // Replace NAN corrections with 0.0 to prevent NAN propagation.
310 288
         if (isnan(z0)) z0 = 0.0;
311 289
 
312 290
         if (!inf_normalized_flag) {
313
-          on_axis_distance = use_x_dist ? next_mesh_line_x - start[X_AXIS] : ry - start[Y_AXIS];
314
-          e_position = start[E_AXIS] + on_axis_distance * e_normalized_dist;
315
-          z_position = start[Z_AXIS] + on_axis_distance * z_normalized_dist;
291
+          on_axis_distance = use_x_dist ? next_mesh_line_x - start.x : ry - start.y;
292
+          e_position = start.e + on_axis_distance * e_normalized_dist;
293
+          z_position = start.z + on_axis_distance * z_normalized_dist;
316 294
         }
317 295
         else {
318
-          e_position = end[E_AXIS];
319
-          z_position = end[Z_AXIS];
296
+          e_position = end.e;
297
+          z_position = end.z;
320 298
         }
321 299
 
322 300
         if (!planner.buffer_segment(next_mesh_line_x, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder))
323 301
           break;
324
-        current_xi += dxi;
325
-        xi_cnt--;
302
+        icell.x += iadd.x;
303
+        cnt.x--;
326 304
       }
327 305
 
328
-      if (xi_cnt < 0 || yi_cnt < 0) break; // Too far! Exit the loop and go to FINAL_MOVE
306
+      if (cnt.x < 0 || cnt.y < 0) break; // Too far! Exit the loop and go to FINAL_MOVE
329 307
     }
330 308
 
331
-    if (current_position[X_AXIS] != end[X_AXIS] || current_position[Y_AXIS] != end[Y_AXIS])
309
+    if (xy_pos_t(current_position) != xy_pos_t(end))
332 310
       goto FINAL_MOVE;
333 311
 
334
-    set_current_from_destination();
312
+    current_position = destination;
335 313
   }
336 314
 
337 315
 #else // UBL_SEGMENTED
@@ -356,56 +334,42 @@
356 334
 
357 335
   bool _O2 unified_bed_leveling::line_to_destination_segmented(const feedRate_t &scaled_fr_mm_s) {
358 336
 
359
-    if (!position_is_reachable(destination[X_AXIS], destination[Y_AXIS]))  // fail if moving outside reachable boundary
360
-      return true; // did not move, so current_position still accurate
337
+    if (!position_is_reachable(destination))  // fail if moving outside reachable boundary
338
+      return true;                            // did not move, so current_position still accurate
361 339
 
362
-    const float total[XYZE] = {
363
-      destination[X_AXIS] - current_position[X_AXIS],
364
-      destination[Y_AXIS] - current_position[Y_AXIS],
365
-      destination[Z_AXIS] - current_position[Z_AXIS],
366
-      destination[E_AXIS] - current_position[E_AXIS]
367
-    };
340
+    const xyze_pos_t total = destination - current_position;
368 341
 
369
-    const float cartesian_xy_mm = HYPOT(total[X_AXIS], total[Y_AXIS]);  // total horizontal xy distance
342
+    const float cart_xy_mm_2 = HYPOT2(total.x, total.y),
343
+                cart_xy_mm = SQRT(cart_xy_mm_2);                                     // Total XY distance
370 344
 
371 345
     #if IS_KINEMATIC
372
-      const float seconds = cartesian_xy_mm / scaled_fr_mm_s;                             // Duration of XY move at requested rate
373
-      uint16_t segments = LROUND(delta_segments_per_second * seconds),                    // Preferred number of segments for distance @ feedrate
374
-               seglimit = LROUND(cartesian_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length
375
-      NOMORE(segments, seglimit);                                                         // Limit to minimum segment length (fewer segments)
346
+      const float seconds = cart_xy_mm / scaled_fr_mm_s;                             // Duration of XY move at requested rate
347
+      uint16_t segments = LROUND(delta_segments_per_second * seconds),               // Preferred number of segments for distance @ feedrate
348
+               seglimit = LROUND(cart_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length
349
+      NOMORE(segments, seglimit);                                                    // Limit to minimum segment length (fewer segments)
376 350
     #else
377
-      uint16_t segments = LROUND(cartesian_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // cartesian fixed segment length
351
+      uint16_t segments = LROUND(cart_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length
378 352
     #endif
379 353
 
380
-    NOLESS(segments, 1U);                        // must have at least one segment
381
-    const float inv_segments = 1.0f / segments;  // divide once, multiply thereafter
354
+    NOLESS(segments, 1U);                                                            // Must have at least one segment
355
+    const float inv_segments = 1.0f / segments,                                      // Reciprocal to save calculation
356
+                segment_xyz_mm = SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments;    // Length of each segment
382 357
 
383
-    const float segment_xyz_mm = HYPOT(cartesian_xy_mm, total[Z_AXIS]) * inv_segments;   // length of each segment
384 358
     #if ENABLED(SCARA_FEEDRATE_SCALING)
385 359
       const float inv_duration = scaled_fr_mm_s / segment_xyz_mm;
386 360
     #endif
387 361
 
388
-    const float diff[XYZE] = {
389
-      total[X_AXIS] * inv_segments,
390
-      total[Y_AXIS] * inv_segments,
391
-      total[Z_AXIS] * inv_segments,
392
-      total[E_AXIS] * inv_segments
393
-    };
362
+    xyze_float_t diff = total * inv_segments;
394 363
 
395 364
     // Note that E segment distance could vary slightly as z mesh height
396 365
     // changes for each segment, but small enough to ignore.
397 366
 
398
-    float raw[XYZE] = {
399
-      current_position[X_AXIS],
400
-      current_position[Y_AXIS],
401
-      current_position[Z_AXIS],
402
-      current_position[E_AXIS]
403
-    };
367
+    xyze_pos_t raw = current_position;
404 368
 
405 369
     // Just do plain segmentation if UBL is inactive or the target is above the fade height
406
-    if (!planner.leveling_active || !planner.leveling_active_at_z(destination[Z_AXIS])) {
370
+    if (!planner.leveling_active || !planner.leveling_active_at_z(destination.z)) {
407 371
       while (--segments) {
408
-        LOOP_XYZE(i) raw[i] += diff[i];
372
+        raw += diff;
409 373
         planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm
410 374
           #if ENABLED(SCARA_FEEDRATE_SCALING)
411 375
             , inv_duration
@@ -417,17 +381,17 @@
417 381
           , inv_duration
418 382
         #endif
419 383
       );
420
-      return false; // moved but did not set_current_from_destination();
384
+      return false; // Did not set current from destination
421 385
     }
422 386
 
423 387
     // Otherwise perform per-segment leveling
424 388
 
425 389
     #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
426
-      const float fade_scaling_factor = planner.fade_scaling_factor_for_z(destination[Z_AXIS]);
390
+      const float fade_scaling_factor = planner.fade_scaling_factor_for_z(destination.z);
427 391
     #endif
428 392
 
429
-    // increment to first segment destination
430
-    LOOP_XYZE(i) raw[i] += diff[i];
393
+    // Move to first segment destination
394
+    raw += diff;
431 395
 
432 396
     for (;;) {  // for each mesh cell encountered during the move
433 397
 
@@ -438,75 +402,68 @@
438 402
       // in top of loop and again re-find same adjacent cell and use it, just less efficient
439 403
       // for mesh inset area.
440 404
 
441
-      int8_t cell_xi = (raw[X_AXIS] - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST),
442
-             cell_yi = (raw[Y_AXIS] - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST);
405
+      xy_int8_t icell = {
406
+        int8_t((raw.x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST)),
407
+        int8_t((raw.y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST))
408
+      };
409
+      LIMIT(icell.x, 0, (GRID_MAX_POINTS_X) - 1);
410
+      LIMIT(icell.y, 0, (GRID_MAX_POINTS_Y) - 1);
443 411
 
444
-      LIMIT(cell_xi, 0, (GRID_MAX_POINTS_X) - 1);
445
-      LIMIT(cell_yi, 0, (GRID_MAX_POINTS_Y) - 1);
446
-
447
-      const float x0 = mesh_index_to_xpos(cell_xi),   // 64 byte table lookup avoids mul+add
448
-                  y0 = mesh_index_to_ypos(cell_yi);
449
-
450
-      float z_x0y0 = z_values[cell_xi  ][cell_yi  ],  // z at lower left corner
451
-            z_x1y0 = z_values[cell_xi+1][cell_yi  ],  // z at upper left corner
452
-            z_x0y1 = z_values[cell_xi  ][cell_yi+1],  // z at lower right corner
453
-            z_x1y1 = z_values[cell_xi+1][cell_yi+1];  // z at upper right corner
412
+      float z_x0y0 = z_values[icell.x  ][icell.y  ],  // z at lower left corner
413
+            z_x1y0 = z_values[icell.x+1][icell.y  ],  // z at upper left corner
414
+            z_x0y1 = z_values[icell.x  ][icell.y+1],  // z at lower right corner
415
+            z_x1y1 = z_values[icell.x+1][icell.y+1];  // z at upper right corner
454 416
 
455 417
       if (isnan(z_x0y0)) z_x0y0 = 0;              // ideally activating planner.leveling_active (G29 A)
456 418
       if (isnan(z_x1y0)) z_x1y0 = 0;              //   should refuse if any invalid mesh points
457 419
       if (isnan(z_x0y1)) z_x0y1 = 0;              //   in order to avoid isnan tests per cell,
458 420
       if (isnan(z_x1y1)) z_x1y1 = 0;              //   thus guessing zero for undefined points
459 421
 
460
-      float cx = raw[X_AXIS] - x0,   // cell-relative x and y
461
-            cy = raw[Y_AXIS] - y0;
422
+      const xy_pos_t pos = { mesh_index_to_xpos(icell.x), mesh_index_to_ypos(icell.y) };
423
+      xy_pos_t cell = raw - pos;
462 424
 
463 425
       const float z_xmy0 = (z_x1y0 - z_x0y0) * RECIPROCAL(MESH_X_DIST),   // z slope per x along y0 (lower left to lower right)
464 426
                   z_xmy1 = (z_x1y1 - z_x0y1) * RECIPROCAL(MESH_X_DIST);   // z slope per x along y1 (upper left to upper right)
465 427
 
466
-            float z_cxy0 = z_x0y0 + z_xmy0 * cx;            // z height along y0 at cx (changes for each cx in cell)
428
+            float z_cxy0 = z_x0y0 + z_xmy0 * cell.x;        // z height along y0 at cell.x (changes for each cell.x in cell)
467 429
 
468
-      const float z_cxy1 = z_x0y1 + z_xmy1 * cx,            // z height along y1 at cx
469
-                  z_cxyd = z_cxy1 - z_cxy0;                 // z height difference along cx from y0 to y1
430
+      const float z_cxy1 = z_x0y1 + z_xmy1 * cell.x,        // z height along y1 at cell.x
431
+                  z_cxyd = z_cxy1 - z_cxy0;                 // z height difference along cell.x from y0 to y1
470 432
 
471
-            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)
433
+            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)
472 434
 
473
-      //    float z_cxcy = z_cxy0 + z_cxym * cy;            // interpolated mesh z height along cx at cy (do inside the segment loop)
435
+      //    float z_cxcy = z_cxy0 + z_cxym * cell.y;        // interpolated mesh z height along cell.x at cell.y (do inside the segment loop)
474 436
 
475 437
       // As subsequent segments step through this cell, the z_cxy0 intercept will change
476
-      // and the z_cxym slope will change, both as a function of cx within the cell, and
438
+      // and the z_cxym slope will change, both as a function of cell.x within the cell, and
477 439
       // each change by a constant for fixed segment lengths.
478 440
 
479
-      const float z_sxy0 = z_xmy0 * diff[X_AXIS],                                     // per-segment adjustment to z_cxy0
480
-                  z_sxym = (z_xmy1 - z_xmy0) * RECIPROCAL(MESH_Y_DIST) * diff[X_AXIS];  // per-segment adjustment to z_cxym
441
+      const float z_sxy0 = z_xmy0 * diff.x,                                       // per-segment adjustment to z_cxy0
442
+                  z_sxym = (z_xmy1 - z_xmy0) * RECIPROCAL(MESH_Y_DIST) * diff.x;  // per-segment adjustment to z_cxym
481 443
 
482 444
       for (;;) {  // for all segments within this mesh cell
483 445
 
484
-        if (--segments == 0) COPY(raw, destination); // if this is last segment, use destination for exact
446
+        if (--segments == 0) raw = destination;     // if this is last segment, use destination for exact
485 447
 
486
-        const float z_cxcy = (z_cxy0 + z_cxym * cy) // interpolated mesh z height along cx at cy
448
+        const float z_cxcy = (z_cxy0 + z_cxym * cell.y) // interpolated mesh z height along cell.x at cell.y
487 449
           #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
488 450
             * fade_scaling_factor                   // apply fade factor to interpolated mesh height
489 451
           #endif
490 452
         ;
491 453
 
492
-        const float z = raw[Z_AXIS];
493
-        raw[Z_AXIS] += z_cxcy;
494
-        planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm
454
+        planner.buffer_line(raw.x, raw.y, raw.z + z_cxcy, raw.e, scaled_fr_mm_s, active_extruder, segment_xyz_mm
495 455
           #if ENABLED(SCARA_FEEDRATE_SCALING)
496 456
             , inv_duration
497 457
           #endif
498 458
         );
499
-        raw[Z_AXIS] = z;
500 459
 
501 460
         if (segments == 0)                        // done with last segment
502
-          return false;                           // did not set_current_from_destination()
503
-
504
-        LOOP_XYZE(i) raw[i] += diff[i];
461
+          return false;                           // didn't set current from destination
505 462
 
506
-        cx += diff[X_AXIS];
507
-        cy += diff[Y_AXIS];
463
+        raw += diff;
464
+        cell += diff;
508 465
 
509
-        if (!WITHIN(cx, 0, MESH_X_DIST) || !WITHIN(cy, 0, MESH_Y_DIST))    // done within this cell, break to next
466
+        if (!WITHIN(cell.x, 0, MESH_X_DIST) || !WITHIN(cell.y, 0, MESH_Y_DIST))    // done within this cell, break to next
510 467
           break;
511 468
 
512 469
         // Next segment still within same mesh cell, adjust the per-segment

+ 3
- 3
Marlin/src/feature/dac/dac_mcp4728.cpp View File

@@ -36,7 +36,7 @@
36 36
 
37 37
 #include "dac_mcp4728.h"
38 38
 
39
-uint16_t mcp4728_values[XYZE];
39
+xyze_uint_t mcp4728_values;
40 40
 
41 41
 /**
42 42
  * 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
121 121
  * Receives all Drive strengths as 0-100 percent values, updates
122 122
  * DAC Values array and calls fastwrite to update the DAC.
123 123
  */
124
-void mcp4728_setDrvPct(uint8_t pct[XYZE]) {
125
-  LOOP_XYZE(i) mcp4728_values[i] = 0.01 * pct[i] * (DAC_STEPPER_MAX);
124
+void mcp4728_setDrvPct(xyze_uint8_t &pct) {
125
+  mcp4728_values *= 0.01 * pct * (DAC_STEPPER_MAX);
126 126
   mcp4728_fastWrite();
127 127
 }
128 128
 

+ 3
- 1
Marlin/src/feature/dac/dac_mcp4728.h View File

@@ -25,6 +25,8 @@
25 25
  * Arduino library for MicroChip MCP4728 I2C D/A converter.
26 26
  */
27 27
 
28
+#include "../../core/types.h"
29
+
28 30
 #include <Wire.h>
29 31
 
30 32
 #define defaultVDD     DAC_STEPPER_MAX //was 5000 but differs with internal Vref
@@ -54,4 +56,4 @@ uint16_t mcp4728_getValue(const uint8_t channel);
54 56
 uint8_t mcp4728_fastWrite();
55 57
 uint8_t mcp4728_simpleCommand(const byte simpleCommand);
56 58
 uint8_t mcp4728_getDrvPct(const uint8_t channel);
57
-void mcp4728_setDrvPct(uint8_t pct[XYZE]);
59
+void mcp4728_setDrvPct(xyze_uint8_t &pct);

+ 4
- 4
Marlin/src/feature/dac/stepper_dac.cpp View File

@@ -31,8 +31,8 @@
31 31
 #include "stepper_dac.h"
32 32
 
33 33
 bool dac_present = false;
34
-const uint8_t dac_order[NUM_AXIS] = DAC_STEPPER_ORDER;
35
-uint8_t dac_channel_pct[XYZE] = DAC_MOTOR_CURRENT_DEFAULT;
34
+constexpr xyze_uint8_t dac_order = DAC_STEPPER_ORDER;
35
+xyze_uint8_t dac_channel_pct = DAC_MOTOR_CURRENT_DEFAULT;
36 36
 
37 37
 int dac_init() {
38 38
   #if PIN_EXISTS(DAC_DISABLE)
@@ -77,8 +77,8 @@ void dac_current_raw(uint8_t channel, uint16_t val) {
77 77
 static float dac_perc(int8_t n) { return 100.0 * mcp4728_getValue(dac_order[n]) * RECIPROCAL(DAC_STEPPER_MAX); }
78 78
 static float dac_amps(int8_t n) { return mcp4728_getDrvPct(dac_order[n]) * (DAC_STEPPER_MAX) * 0.125 * RECIPROCAL(DAC_STEPPER_SENSE); }
79 79
 
80
-uint8_t dac_current_get_percent(AxisEnum axis) { return mcp4728_getDrvPct(dac_order[axis]); }
81
-void dac_current_set_percents(const uint8_t pct[XYZE]) {
80
+uint8_t dac_current_get_percent(const AxisEnum axis) { return mcp4728_getDrvPct(dac_order[axis]); }
81
+void dac_current_set_percents(xyze_uint8_t &pct) {
82 82
   LOOP_XYZE(i) dac_channel_pct[i] = pct[dac_order[i]];
83 83
   mcp4728_setDrvPct(dac_channel_pct);
84 84
 }

+ 1
- 1
Marlin/src/feature/dac/stepper_dac.h View File

@@ -33,4 +33,4 @@ void dac_current_raw(uint8_t channel, uint16_t val);
33 33
 void dac_print_values();
34 34
 void dac_commit_eeprom();
35 35
 uint8_t dac_current_get_percent(AxisEnum axis);
36
-void dac_current_set_percents(const uint8_t pct[XYZE]);
36
+void dac_current_set_percents(xyze_uint8_t &pct);

+ 7
- 7
Marlin/src/feature/fwretract.cpp View File

@@ -123,8 +123,8 @@ void FWRetract::retract(const bool retracting
123 123
         SERIAL_ECHOLNPAIR("retracted_swap[", i, "] ", retracted_swap[i]);
124 124
       #endif
125 125
     }
126
-    SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]);
127
-    SERIAL_ECHOLNPAIR("current_position[e] ", current_position[E_AXIS]);
126
+    SERIAL_ECHOLNPAIR("current_position.z ", current_position.z);
127
+    SERIAL_ECHOLNPAIR("current_position.e ", current_position.e);
128 128
     SERIAL_ECHOLNPAIR("current_hop ", current_hop);
129 129
   //*/
130 130
 
@@ -136,7 +136,7 @@ void FWRetract::retract(const bool retracting
136 136
               );
137 137
 
138 138
   // The current position will be the destination for E and Z moves
139
-  set_destination_from_current();
139
+  destination = current_position;
140 140
 
141 141
   #if ENABLED(RETRACT_SYNC_MIXING)
142 142
     const uint8_t old_mixing_tool = mixer.get_current_vtool();
@@ -147,7 +147,7 @@ void FWRetract::retract(const bool retracting
147 147
   if (retracting) {
148 148
     // Retract by moving from a faux E position back to the current E position
149 149
     current_retract[active_extruder] = base_retract;
150
-    prepare_internal_move_to_destination(  // set_current_to_destination
150
+    prepare_internal_move_to_destination(                 // set current to destination
151 151
       settings.retract_feedrate_mm_s
152 152
       #if ENABLED(RETRACT_SYNC_MIXING)
153 153
         * (MIXING_STEPPERS)
@@ -171,7 +171,7 @@ void FWRetract::retract(const bool retracting
171 171
 
172 172
     const float extra_recover = swapping ? settings.swap_retract_recover_extra : settings.retract_recover_extra;
173 173
     if (extra_recover) {
174
-      current_position[E_AXIS] -= extra_recover;          // Adjust the current E position by the extra amount to recover
174
+      current_position.e -= extra_recover;          // Adjust the current E position by the extra amount to recover
175 175
       sync_plan_position_e();                             // Sync the planner position so the extra amount is recovered
176 176
     }
177 177
 
@@ -207,8 +207,8 @@ void FWRetract::retract(const bool retracting
207 207
         SERIAL_ECHOLNPAIR("retracted_swap[", i, "] ", retracted_swap[i]);
208 208
       #endif
209 209
     }
210
-    SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]);
211
-    SERIAL_ECHOLNPAIR("current_position[e] ", current_position[E_AXIS]);
210
+    SERIAL_ECHOLNPAIR("current_position.z ", current_position.z);
211
+    SERIAL_ECHOLNPAIR("current_position.e ", current_position.e);
212 212
     SERIAL_ECHOLNPAIR("current_hop ", current_hop);
213 213
   //*/
214 214
 }

+ 13
- 16
Marlin/src/feature/joystick.cpp View File

@@ -71,35 +71,35 @@ Joystick joystick;
71 71
 
72 72
 #if HAS_JOY_ADC_X || HAS_JOY_ADC_Y || HAS_JOY_ADC_Z
73 73
 
74
-  void Joystick::calculate(float (&norm_jog)[XYZ]) {
74
+  void Joystick::calculate(xyz_float_t &norm_jog) {
75 75
     // Do nothing if enable pin (active-low) is not LOW
76 76
     #if HAS_JOY_ADC_EN
77 77
       if (READ(JOY_EN_PIN)) return;
78 78
     #endif
79 79
 
80
-    auto _normalize_joy = [](float &norm_jog, const int16_t raw, const int16_t (&joy_limits)[4]) {
80
+    auto _normalize_joy = [](float &axis_jog, const int16_t raw, const int16_t (&joy_limits)[4]) {
81 81
       if (WITHIN(raw, joy_limits[0], joy_limits[3])) {
82 82
         // within limits, check deadzone
83 83
         if (raw > joy_limits[2])
84
-          norm_jog = (raw - joy_limits[2]) / float(joy_limits[3] - joy_limits[2]);
84
+          axis_jog = (raw - joy_limits[2]) / float(joy_limits[3] - joy_limits[2]);
85 85
         else if (raw < joy_limits[1])
86
-          norm_jog = (raw - joy_limits[1]) / float(joy_limits[1] - joy_limits[0]);  // negative value
86
+          axis_jog = (raw - joy_limits[1]) / float(joy_limits[1] - joy_limits[0]);  // negative value
87 87
         // Map normal to jog value via quadratic relationship
88
-        norm_jog = SIGN(norm_jog) * sq(norm_jog);
88
+        axis_jog = SIGN(axis_jog) * sq(axis_jog);
89 89
       }
90 90
     };
91 91
 
92 92
     #if HAS_JOY_ADC_X
93 93
       static constexpr int16_t joy_x_limits[4] = JOY_X_LIMITS;
94
-      _normalize_joy(norm_jog[X_AXIS], x.raw, joy_x_limits);
94
+      _normalize_joy(norm_jog.x, x.raw, joy_x_limits);
95 95
     #endif
96 96
     #if HAS_JOY_ADC_Y
97 97
       static constexpr int16_t joy_y_limits[4] = JOY_Y_LIMITS;
98
-      _normalize_joy(norm_jog[Y_AXIS], y.raw, joy_y_limits);
98
+      _normalize_joy(norm_jog.y, y.raw, joy_y_limits);
99 99
     #endif
100 100
     #if HAS_JOY_ADC_Z
101 101
       static constexpr int16_t joy_z_limits[4] = JOY_Z_LIMITS;
102
-      _normalize_joy(norm_jog[Z_AXIS], z.raw, joy_z_limits);
102
+      _normalize_joy(norm_jog.z, z.raw, joy_z_limits);
103 103
     #endif
104 104
   }
105 105
 
@@ -129,7 +129,7 @@ Joystick joystick;
129 129
     // Normalized jog values are 0 for no movement and -1 or +1 for as max feedrate (nonlinear relationship)
130 130
     // Jog are initialized to zero and handling input can update values but doesn't have to
131 131
     // You could use a two-axis joystick and a one-axis keypad and they might work together
132
-    float norm_jog[XYZ] = { 0 };
132
+    xyz_float_t norm_jog{0};
133 133
 
134 134
     // Use ADC values and defined limits. The active zone is normalized: -1..0 (dead) 0..1
135 135
     #if HAS_JOY_ADC_X || HAS_JOY_ADC_Y || HAS_JOY_ADC_Z
@@ -143,16 +143,13 @@ Joystick joystick;
143 143
       ExtUI::_joystick_update(norm_jog);
144 144
     #endif
145 145
 
146
-    #if EITHER(ULTIPANEL, EXTENSIBLE_UI)
147
-      constexpr float manual_feedrate[XYZE] = MANUAL_FEEDRATE;
148
-    #endif
149
-
150 146
     // norm_jog values of [-1 .. 1] maps linearly to [-feedrate .. feedrate]
151
-    float move_dist[XYZ] = { 0 }, hypot2 = 0;
147
+    xyz_float_t move_dist{0};
148
+    float hypot2 = 0;
152 149
     LOOP_XYZ(i) if (norm_jog[i]) {
153 150
       move_dist[i] = seg_time * norm_jog[i] *
154 151
         #if EITHER(ULTIPANEL, EXTENSIBLE_UI)
155
-          MMM_TO_MMS(manual_feedrate[i]);
152
+          MMM_TO_MMS(manual_feedrate_mm_m[i]);
156 153
         #else
157 154
           planner.settings.max_feedrate_mm_s[i];
158 155
         #endif
@@ -160,7 +157,7 @@ Joystick joystick;
160 157
     }
161 158
 
162 159
     if (!UNEAR_ZERO(hypot2)) {
163
-      LOOP_XYZ(i) current_position[i] += move_dist[i];
160
+      current_position += move_dist;
164 161
       const float length = sqrt(hypot2);
165 162
       injecting_now = true;
166 163
       planner.buffer_line(current_position, length / seg_time, active_extruder, length);

+ 3
- 1
Marlin/src/feature/joystick.h View File

@@ -25,6 +25,8 @@
25 25
  * joystick.h - joystick input / jogging
26 26
  */
27 27
 
28
+#include "../inc/MarlinConfigPre.h"
29
+#include "../core/types.h"
28 30
 #include "../core/macros.h"
29 31
 #include "../module/temperature.h"
30 32
 
@@ -46,7 +48,7 @@ class Joystick {
46 48
     #if ENABLED(JOYSTICK_DEBUG)
47 49
       static void report();
48 50
     #endif
49
-    static void calculate(float (&norm_jog)[XYZ]);
51
+    static void calculate(xyz_float_t &norm_jog);
50 52
     static void inject_jog_moves();
51 53
 };
52 54
 

+ 11
- 11
Marlin/src/feature/pause.cpp View File

@@ -64,7 +64,7 @@
64 64
 
65 65
 // private:
66 66
 
67
-static float resume_position[XYZE];
67
+static xyze_pos_t resume_position;
68 68
 
69 69
 PauseMode pause_mode = PAUSE_MODE_PAUSE_PRINT;
70 70
 
@@ -126,8 +126,8 @@ void do_pause_e_move(const float &length, const feedRate_t &fr_mm_s) {
126 126
   #if HAS_FILAMENT_SENSOR
127 127
     runout.reset();
128 128
   #endif
129
-  current_position[E_AXIS] += length / planner.e_factor[active_extruder];
130
-  planner.buffer_line(current_position, fr_mm_s, active_extruder);
129
+  current_position.e += length / planner.e_factor[active_extruder];
130
+  line_to_current_position(fr_mm_s);
131 131
   planner.synchronize();
132 132
 }
133 133
 
@@ -385,7 +385,7 @@ bool unload_filament(const float &unload_length, const bool show_lcd/*=false*/,
385 385
  */
386 386
 uint8_t did_pause_print = 0;
387 387
 
388
-bool pause_print(const float &retract, const point_t &park_point, const float &unload_length/*=0*/, const bool show_lcd/*=false*/ DXC_ARGS) {
388
+bool pause_print(const float &retract, const xyz_pos_t &park_point, const float &unload_length/*=0*/, const bool show_lcd/*=false*/ DXC_ARGS) {
389 389
 
390 390
   #if !HAS_LCD_MENU
391 391
     UNUSED(show_lcd);
@@ -432,7 +432,7 @@ bool pause_print(const float &retract, const point_t &park_point, const float &u
432 432
   print_job_timer.pause();
433 433
 
434 434
   // Save current position
435
-  COPY(resume_position, current_position);
435
+  resume_position = current_position;
436 436
 
437 437
   // Wait for buffered blocks to complete
438 438
   planner.synchronize();
@@ -611,10 +611,10 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
611 611
  * - Display "wait for print to resume"
612 612
  * - Re-prime the nozzle...
613 613
  *   -  FWRETRACT: Recover/prime from the prior G10.
614
- *   - !FWRETRACT: Retract by resume_position[E], if negative.
614
+ *   - !FWRETRACT: Retract by resume_position.e, if negative.
615 615
  *                 Not sure how this logic comes into use.
616 616
  * - Move the nozzle back to resume_position
617
- * - Sync the planner E to resume_position[E]
617
+ * - Sync the planner E to resume_position.e
618 618
  * - Send host action for resume, if configured
619 619
  * - Resume the current SD print job, if any
620 620
  */
@@ -652,13 +652,13 @@ void resume_print(const float &slow_load_length/*=0*/, const float &fast_load_le
652 652
   #endif
653 653
 
654 654
   // If resume_position is negative
655
-  if (resume_position[E_AXIS] < 0) do_pause_e_move(resume_position[E_AXIS], feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE));
655
+  if (resume_position.e < 0) do_pause_e_move(resume_position.e, feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE));
656 656
 
657 657
   // Move XY to starting position, then Z
658
-  do_blocking_move_to_xy(resume_position[X_AXIS], resume_position[Y_AXIS], feedRate_t(NOZZLE_PARK_XY_FEEDRATE));
658
+  do_blocking_move_to_xy(xy_pos_t(resume_position), feedRate_t(NOZZLE_PARK_XY_FEEDRATE));
659 659
 
660 660
   // Move Z_AXIS to saved position
661
-  do_blocking_move_to_z(resume_position[Z_AXIS], feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
661
+  do_blocking_move_to_z(resume_position.z, feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
662 662
 
663 663
   #if ADVANCED_PAUSE_RESUME_PRIME != 0
664 664
     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
666 666
 
667 667
   // Now all extrusion positions are resumed and ready to be confirmed
668 668
   // Set extruder to saved position
669
-  planner.set_e_position_mm((destination[E_AXIS] = current_position[E_AXIS] = resume_position[E_AXIS]));
669
+  planner.set_e_position_mm((destination.e = current_position.e = resume_position.e));
670 670
 
671 671
   #if HAS_LCD_MENU
672 672
     lcd_pause_show_message(PAUSE_MESSAGE_STATUS);

+ 1
- 1
Marlin/src/feature/pause.h View File

@@ -83,7 +83,7 @@ extern uint8_t did_pause_print;
83 83
 
84 84
 void do_pause_e_move(const float &length, const feedRate_t &fr_mm_s);
85 85
 
86
-bool pause_print(const float &retract, const point_t &park_point, const float &unload_length=0, const bool show_lcd=false DXC_PARAMS);
86
+bool pause_print(const float &retract, const xyz_pos_t &park_point, const float &unload_length=0, const bool show_lcd=false DXC_PARAMS);
87 87
 
88 88
 void wait_for_confirmation(const bool is_reload=false, const int8_t max_beep_count=0 DXC_PARAMS);
89 89
 

+ 15
- 17
Marlin/src/feature/power_loss_recovery.cpp View File

@@ -156,7 +156,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const bool save_queue/*=
156 156
         || ELAPSED(ms, next_save_ms)
157 157
       #endif
158 158
       // Save if Z is above the last-saved position by some minimum height
159
-      || current_position[Z_AXIS] > info.current_position[Z_AXIS] + POWER_LOSS_MIN_Z_CHANGE
159
+      || current_position.z > info.current_position.z + POWER_LOSS_MIN_Z_CHANGE
160 160
     #endif
161 161
   ) {
162 162
 
@@ -170,12 +170,12 @@ void PrintJobRecovery::save(const bool force/*=false*/, const bool save_queue/*=
170 170
     info.valid_foot = info.valid_head;
171 171
 
172 172
     // Machine state
173
-    COPY(info.current_position, current_position);
173
+    info.current_position = current_position;
174 174
     #if HAS_HOME_OFFSET
175
-      COPY(info.home_offset, home_offset);
175
+      info.home_offset = home_offset;
176 176
     #endif
177 177
     #if HAS_POSITION_SHIFT
178
-      COPY(info.position_shift, position_shift);
178
+      info.position_shift = position_shift;
179 179
     #endif
180 180
     info.feedrate = uint16_t(feedrate_mm_s * 60.0f);
181 181
 
@@ -361,13 +361,13 @@ void PrintJobRecovery::resume() {
361 361
 
362 362
   // Move back to the saved XY
363 363
   sprintf_P(cmd, PSTR("G1 X%s Y%s F3000"),
364
-    dtostrf(info.current_position[X_AXIS], 1, 3, str_1),
365
-    dtostrf(info.current_position[Y_AXIS], 1, 3, str_2)
364
+    dtostrf(info.current_position.x, 1, 3, str_1),
365
+    dtostrf(info.current_position.y, 1, 3, str_2)
366 366
   );
367 367
   gcode.process_subcommands_now(cmd);
368 368
 
369 369
   // Move back to the saved Z
370
-  dtostrf(info.current_position[Z_AXIS], 1, 3, str_1);
370
+  dtostrf(info.current_position.z, 1, 3, str_1);
371 371
   #if Z_HOME_DIR > 0
372 372
     sprintf_P(cmd, PSTR("G1 Z%s F200"), str_1);
373 373
   #else
@@ -388,22 +388,20 @@ void PrintJobRecovery::resume() {
388 388
   gcode.process_subcommands_now(cmd);
389 389
 
390 390
   // Restore E position with G92.9
391
-  sprintf_P(cmd, PSTR("G92.9 E%s"), dtostrf(info.current_position[E_AXIS], 1, 3, str_1));
391
+  sprintf_P(cmd, PSTR("G92.9 E%s"), dtostrf(info.current_position.e, 1, 3, str_1));
392 392
   gcode.process_subcommands_now(cmd);
393 393
 
394 394
   // Relative axis modes
395 395
   gcode.axis_relative = info.axis_relative;
396 396
 
397
+  #if HAS_HOME_OFFSET
398
+    home_offset = info.home_offset;
399
+  #endif
400
+  #if HAS_POSITION_SHIFT
401
+    position_shift = info.position_shift;
402
+  #endif
397 403
   #if HAS_HOME_OFFSET || HAS_POSITION_SHIFT
398
-    LOOP_XYZ(i) {
399
-      #if HAS_HOME_OFFSET
400
-        home_offset[i] = info.home_offset[i];
401
-      #endif
402
-      #if HAS_POSITION_SHIFT
403
-        position_shift[i] = info.position_shift[i];
404
-      #endif
405
-      update_workspace_offset((AxisEnum)i);
406
-    }
404
+    LOOP_XYZ(i) update_workspace_offset((AxisEnum)i);
407 405
   #endif
408 406
 
409 407
   // Resume the SD file from the last position

+ 3
- 3
Marlin/src/feature/power_loss_recovery.h View File

@@ -44,13 +44,13 @@ typedef struct {
44 44
   uint8_t valid_head;
45 45
 
46 46
   // Machine state
47
-  float current_position[NUM_AXIS];
47
+  xyze_pos_t current_position;
48 48
 
49 49
   #if HAS_HOME_OFFSET
50
-    float home_offset[XYZ];
50
+    xyz_pos_t home_offset;
51 51
   #endif
52 52
   #if HAS_POSITION_SHIFT
53
-    float position_shift[XYZ];
53
+    xyz_pos_t position_shift;
54 54
   #endif
55 55
 
56 56
   uint16_t feedrate;

+ 8
- 8
Marlin/src/feature/prusa_MMU2/mmu2.cpp View File

@@ -550,10 +550,10 @@ bool MMU2::get_response() {
550 550
  */
551 551
 void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) {
552 552
 
553
+  constexpr xyz_pos_t park_point = NOZZLE_PARK_POINT;
553 554
   bool response = false;
554 555
   mmu_print_saved = false;
555
-  point_t park_point = NOZZLE_PARK_POINT;
556
-  float resume_position[XYZE];
556
+  xyz_pos_t resume_position;
557 557
   int16_t resume_hotend_temp;
558 558
 
559 559
   KEEPALIVE_STATE(PAUSED_FOR_USER);
@@ -572,7 +572,7 @@ void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) {
572 572
         SERIAL_ECHOLNPGM("MMU not responding");
573 573
 
574 574
         resume_hotend_temp = thermalManager.degTargetHotend(active_extruder);
575
-        COPY(resume_position, current_position);
575
+        resume_position = current_position;
576 576
 
577 577
         if (move_axes && all_axes_homed())
578 578
           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) {
604 604
         BUZZ(200, 404);
605 605
 
606 606
         // Move XY to starting position, then Z
607
-        do_blocking_move_to_xy(resume_position[X_AXIS], resume_position[Y_AXIS], feedRate_t(NOZZLE_PARK_XY_FEEDRATE));
607
+        do_blocking_move_to_xy(resume_position, feedRate_t(NOZZLE_PARK_XY_FEEDRATE));
608 608
 
609 609
         // Move Z_AXIS to saved position
610
-        do_blocking_move_to_z(resume_position[Z_AXIS], feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
610
+        do_blocking_move_to_z(resume_position.z, feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
611 611
       }
612 612
       else {
613 613
         BUZZ(200, 404);
@@ -698,8 +698,8 @@ void MMU2::filament_runout() {
698 698
     LCD_MESSAGEPGM(MSG_MMU2_EJECTING_FILAMENT);
699 699
 
700 700
     enable_E0();
701
-    current_position[E_AXIS] -= MMU2_FILAMENTCHANGE_EJECT_FEED;
702
-    planner.buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 2500 / 60, active_extruder);
701
+    current_position.e -= MMU2_FILAMENTCHANGE_EJECT_FEED;
702
+    line_to_current_position(2500 / 60);
703 703
     planner.synchronize();
704 704
     command(MMU_CMD_E0 + index);
705 705
     manage_response(false, false);
@@ -787,7 +787,7 @@ void MMU2::filament_runout() {
787 787
       DEBUG_ECHO_START();
788 788
       DEBUG_ECHOLNPAIR("E step ", es, "/", fr_mm_m);
789 789
 
790
-      current_position[E_AXIS] += es;
790
+      current_position.e += es;
791 791
       line_to_current_position(MMM_TO_MMS(fr_mm_m));
792 792
       planner.synchronize();
793 793
 

+ 2
- 2
Marlin/src/feature/runout.h View File

@@ -327,14 +327,14 @@ class FilamentSensorBase {
327 327
       }
328 328
 
329 329
       static inline void block_completed(const block_t* const b) {
330
-        if (b->steps[X_AXIS] || b->steps[Y_AXIS] || b->steps[Z_AXIS]
330
+        if (b->steps.x || b->steps.y || b->steps.z
331 331
           #if ENABLED(ADVANCED_PAUSE_FEATURE)
332 332
             || did_pause_print // Allow pause purge move to re-trigger runout state
333 333
           #endif
334 334
         ) {
335 335
           // Only trigger on extrusion with XYZ movement to allow filament change and retract/recover.
336 336
           const uint8_t e = b->extruder;
337
-          const int32_t steps = b->steps[E_AXIS];
337
+          const int32_t steps = b->steps.e;
338 338
           runout_mm_countdown[e] -= (TEST(b->direction_bits, E_AXIS) ? -steps : steps) * planner.steps_to_mm[E_AXIS_N(e)];
339 339
         }
340 340
       }

+ 2
- 2
Marlin/src/feature/tmc_util.h View File

@@ -367,9 +367,9 @@ void test_tmc_connection(const bool test_x, const bool test_y, const bool test_z
367 367
     constexpr uint16_t default_sg_guard_duration = 400;
368 368
 
369 369
     struct slow_homing_t {
370
-      struct { uint32_t x, y; } acceleration;
370
+      xy_ulong_t acceleration;
371 371
       #if HAS_CLASSIC_JERK
372
-        struct { float x, y; } jerk;
372
+        xy_float_t jerk_xy;
373 373
       #endif
374 374
     };
375 375
   #endif

+ 142
- 155
Marlin/src/gcode/bedlevel/G26.cpp View File

@@ -62,7 +62,7 @@
62 62
 #define G26_ERR true
63 63
 
64 64
 #if ENABLED(ARC_SUPPORT)
65
-  void plan_arc(const float (&cart)[XYZE], const float (&offset)[2], const uint8_t clockwise);
65
+  void plan_arc(const xyze_pos_t &cart, const ab_float_t &offset, const uint8_t clockwise);
66 66
 #endif
67 67
 
68 68
 /**
@@ -142,7 +142,7 @@
142 142
 
143 143
 // Private functions
144 144
 
145
-static uint16_t circle_flags[16], horizontal_mesh_line_flags[16], vertical_mesh_line_flags[16];
145
+static MeshFlags circle_flags, horizontal_mesh_line_flags, vertical_mesh_line_flags;
146 146
 float g26_e_axis_feedrate = 0.025,
147 147
       random_deviation = 0.0;
148 148
 
@@ -154,7 +154,7 @@ float g26_extrusion_multiplier,
154 154
       g26_layer_height,
155 155
       g26_prime_length;
156 156
 
157
-float g26_x_pos = 0, g26_y_pos = 0;
157
+xy_pos_t g26_pos; // = { 0, 0 }
158 158
 
159 159
 int16_t g26_bed_temp,
160 160
         g26_hotend_temp;
@@ -178,85 +178,85 @@ int8_t g26_prime_flag;
178 178
 
179 179
 #endif
180 180
 
181
-mesh_index_pair find_closest_circle_to_print(const float &X, const float &Y) {
181
+mesh_index_pair find_closest_circle_to_print(const xy_pos_t &pos) {
182 182
   float closest = 99999.99;
183
-  mesh_index_pair return_val;
183
+  mesh_index_pair out_point;
184 184
 
185
-  return_val.x_index = return_val.y_index = -1;
185
+  out_point.pos = -1;
186 186
 
187 187
   for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
188 188
     for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {
189
-      if (!is_bitmap_set(circle_flags, i, j)) {
190
-        const float mx = _GET_MESH_X(i),  // We found a circle that needs to be printed
191
-                    my = _GET_MESH_Y(j);
189
+      if (!circle_flags.marked(i, j)) {
190
+        // We found a circle that needs to be printed
191
+        const xy_pos_t m = { _GET_MESH_X(i), _GET_MESH_Y(j) };
192 192
 
193 193
         // Get the distance to this intersection
194
-        float f = HYPOT(X - mx, Y - my);
194
+        float f = (pos - m).magnitude();
195 195
 
196 196
         // It is possible that we are being called with the values
197 197
         // to let us find the closest circle to the start position.
198 198
         // But if this is not the case, add a small weighting to the
199 199
         // distance calculation to help it choose a better place to continue.
200
-        f += HYPOT(g26_x_pos - mx, g26_y_pos - my) / 15.0;
200
+        f += (g26_pos - m).magnitude() / 15.0f;
201 201
 
202
-        // Add in the specified amount of Random Noise to our search
203
-        if (random_deviation > 1.0)
204
-          f += random(0.0, random_deviation);
202
+        // Add the specified amount of Random Noise to our search
203
+        if (random_deviation > 1.0) f += random(0.0, random_deviation);
205 204
 
206 205
         if (f < closest) {
207
-          closest = f;              // We found a closer location that is still
208
-          return_val.x_index = i;   // un-printed  --- save the data for it
209
-          return_val.y_index = j;
210
-          return_val.distance = closest;
206
+          closest = f;          // Found a closer un-printed location
207
+          out_point.pos.set(i, j);  // Save its data
208
+          out_point.distance = closest;
211 209
         }
212 210
       }
213 211
     }
214 212
   }
215
-  bitmap_set(circle_flags, return_val.x_index, return_val.y_index);   // Mark this location as done.
216
-  return return_val;
213
+  circle_flags.mark(out_point); // Mark this location as done.
214
+  return out_point;
217 215
 }
218 216
 
219 217
 void move_to(const float &rx, const float &ry, const float &z, const float &e_delta) {
220 218
   static float last_z = -999.99;
221 219
 
222
-  bool has_xy_component = (rx != current_position[X_AXIS] || ry != current_position[Y_AXIS]); // Check if X or Y is involved in the movement.
220
+  const xy_pos_t dest = { rx, ry };
223 221
 
224
-  if (z != last_z) {
225
-    last_z = z;
226
-    const feedRate_t feed_value = planner.settings.max_feedrate_mm_s[Z_AXIS] * 0.5f; // Use half of the Z_AXIS max feed rate
222
+  const bool has_xy_component = dest != current_position; // Check if X or Y is involved in the movement.
227 223
 
228
-    destination[X_AXIS] = current_position[X_AXIS];
229
-    destination[Y_AXIS] = current_position[Y_AXIS];
230
-    destination[Z_AXIS] = z;                          // We know the last_z!=z or we wouldn't be in this block of code.
231
-    destination[E_AXIS] = current_position[E_AXIS];
224
+  destination = current_position;
232 225
 
226
+  if (z != last_z) {
227
+    last_z = destination.z = z;
228
+    const feedRate_t feed_value = planner.settings.max_feedrate_mm_s[Z_AXIS] * 0.5f; // Use half of the Z_AXIS max feed rate
233 229
     prepare_internal_move_to_destination(feed_value);
234
-    set_destination_from_current();
230
+    destination = current_position;
235 231
   }
236 232
 
237 233
   // If X or Y is involved do a 'normal' move. Otherwise retract/recover/hop.
234
+  destination = dest;
235
+  destination.e += e_delta;
238 236
   const feedRate_t feed_value = has_xy_component ? feedRate_t(G26_XY_FEEDRATE) : planner.settings.max_feedrate_mm_s[E_AXIS] * 0.666f;
239
-
240
-  destination[X_AXIS] = rx;
241
-  destination[Y_AXIS] = ry;
242
-  destination[E_AXIS] += e_delta;
243
-
244 237
   prepare_internal_move_to_destination(feed_value);
245
-  set_destination_from_current();
238
+  destination = current_position;
246 239
 }
247 240
 
248
-FORCE_INLINE void move_to(const float (&where)[XYZE], const float &de) { move_to(where[X_AXIS], where[Y_AXIS], where[Z_AXIS], de); }
241
+FORCE_INLINE void move_to(const xyz_pos_t &where, const float &de) { move_to(where.x, where.y, where.z, de); }
249 242
 
250
-void retract_filament(const float (&where)[XYZE]) {
243
+void retract_filament(const xyz_pos_t &where) {
251 244
   if (!g26_retracted) { // Only retract if we are not already retracted!
252 245
     g26_retracted = true;
253
-    move_to(where, -1.0 * g26_retraction_multiplier);
246
+    move_to(where, -1.0f * g26_retraction_multiplier);
254 247
   }
255 248
 }
256 249
 
257
-void recover_filament(const float (&where)[XYZE]) {
250
+// TODO: Parameterize the Z lift with a define
251
+void retract_lift_move(const xyz_pos_t &s) {
252
+  retract_filament(destination);
253
+  move_to(current_position.x, current_position.y, current_position.z + 0.5f, 0.0);  // Z lift to minimize scraping
254
+  move_to(s.x, s.y, s.z + 0.5f, 0.0);  // Get to the starting point with no extrusion while lifted
255
+}
256
+
257
+void recover_filament(const xyz_pos_t &where) {
258 258
   if (g26_retracted) { // Only un-retract if we are retracted.
259
-    move_to(where, 1.2 * g26_retraction_multiplier);
259
+    move_to(where, 1.2f * g26_retraction_multiplier);
260 260
     g26_retracted = false;
261 261
   }
262 262
 }
@@ -276,41 +276,34 @@ void recover_filament(const float (&where)[XYZE]) {
276 276
  * segment of a 'circle'. The time this requires is very short and is easily saved by the other
277 277
  * cases where the optimization comes into play.
278 278
  */
279
-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) {
280
-  const float dx_s = current_position[X_AXIS] - sx,   // find our distance from the start of the actual line segment
281
-              dy_s = current_position[Y_AXIS] - sy,
282
-              dist_start = HYPOT2(dx_s, dy_s),        // We don't need to do a sqrt(), we can compare the distance^2
283
-                                                      // to save computation time
284
-              dx_e = current_position[X_AXIS] - ex,   // find our distance from the end of the actual line segment
285
-              dy_e = current_position[Y_AXIS] - ey,
286
-              dist_end = HYPOT2(dx_e, dy_e),
279
+void print_line_from_here_to_there(const xyz_pos_t &s, const xyz_pos_t &e) {
287 280
 
288
-              line_length = HYPOT(ex - sx, ey - sy);
281
+  // Distances to the start / end of the line
282
+  xy_float_t svec = current_position - s, evec = current_position - e;
283
+
284
+  const float dist_start = HYPOT2(svec.x, svec.y),
285
+              dist_end = HYPOT2(evec.x, evec.y),
286
+              line_length = HYPOT(e.x - s.x, e.y - s.y);
289 287
 
290 288
   // If the end point of the line is closer to the nozzle, flip the direction,
291 289
   // moving from the end to the start. On very small lines the optimization isn't worth it.
292 290
   if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < ABS(line_length))
293
-    return print_line_from_here_to_there(ex, ey, ez, sx, sy, sz);
291
+    return print_line_from_here_to_there(e, s);
294 292
 
295
-  // Decide whether to retract & bump
293
+  // Decide whether to retract & lift
294
+  if (dist_start > 2.0) retract_lift_move(s);
296 295
 
297
-  if (dist_start > 2.0) {
298
-    retract_filament(destination);
299
-    //todo:  parameterize the bump height with a define
300
-    move_to(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] + 0.500, 0.0);  // Z bump to minimize scraping
301
-    move_to(sx, sy, sz + 0.500, 0.0); // Get to the starting point with no extrusion while bumped
302
-  }
303
-
304
-  move_to(sx, sy, sz, 0.0); // Get to the starting point with no extrusion / un-Z bump
296
+  move_to(s, 0.0); // Get to the starting point with no extrusion / un-Z lift
305 297
 
306 298
   const float e_pos_delta = line_length * g26_e_axis_feedrate * g26_extrusion_multiplier;
307 299
 
308 300
   recover_filament(destination);
309
-  move_to(ex, ey, ez, e_pos_delta);  // Get to the ending point with an appropriate amount of extrusion
301
+  move_to(e, e_pos_delta);  // Get to the ending point with an appropriate amount of extrusion
310 302
 }
311 303
 
312 304
 inline bool look_for_lines_to_connect() {
313
-  float sx, sy, ex, ey;
305
+  xyz_pos_t s, e;
306
+  s.z = e.z = g26_layer_height;
314 307
 
315 308
   for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
316 309
     for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {
@@ -319,43 +312,43 @@ inline bool look_for_lines_to_connect() {
319 312
         if (user_canceled()) return true;
320 313
       #endif
321 314
 
322
-      if (i < GRID_MAX_POINTS_X) { // Can't connect to anything to the right than GRID_MAX_POINTS_X.
323
-                                   // Already a half circle at the edge of the bed.
315
+      if (i < GRID_MAX_POINTS_X) {  // Can't connect to anything farther to the right than GRID_MAX_POINTS_X.
316
+                                    // Already a half circle at the edge of the bed.
324 317
 
325
-        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
326
-          if (!is_bitmap_set(horizontal_mesh_line_flags, i, j)) {
318
+        if (circle_flags.marked(i, j) && circle_flags.marked(i + 1, j)) {   // Test whether a leftward line can be done
319
+          if (!horizontal_mesh_line_flags.marked(i, j)) {
327 320
             // Two circles need a horizontal line to connect them
328
-            sx = _GET_MESH_X(  i  ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // right edge
329
-            ex = _GET_MESH_X(i + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // left edge
321
+            s.x = _GET_MESH_X(  i  ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // right edge
322
+            e.x = _GET_MESH_X(i + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // left edge
330 323
 
331
-            LIMIT(sx, X_MIN_POS + 1, X_MAX_POS - 1);
332
-            sy = ey = constrain(_GET_MESH_Y(j), Y_MIN_POS + 1, Y_MAX_POS - 1);
333
-            LIMIT(ex, X_MIN_POS + 1, X_MAX_POS - 1);
324
+            LIMIT(s.x, X_MIN_POS + 1, X_MAX_POS - 1);
325
+            s.y = e.y = constrain(_GET_MESH_Y(j), Y_MIN_POS + 1, Y_MAX_POS - 1);
326
+            LIMIT(e.x, X_MIN_POS + 1, X_MAX_POS - 1);
334 327
 
335
-            if (position_is_reachable(sx, sy) && position_is_reachable(ex, ey))
336
-              print_line_from_here_to_there(sx, sy, g26_layer_height, ex, ey, g26_layer_height);
328
+            if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y))
329
+              print_line_from_here_to_there(s, e);
337 330
 
338
-            bitmap_set(horizontal_mesh_line_flags, i, j); // Mark done, even if skipped
331
+            horizontal_mesh_line_flags.mark(i, j); // Mark done, even if skipped
339 332
           }
340 333
         }
341 334
 
342 335
         if (j < GRID_MAX_POINTS_Y) {  // Can't connect to anything further back than GRID_MAX_POINTS_Y.
343 336
                                       // Already a half circle at the edge of the bed.
344 337
 
345
-          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
346
-            if (!is_bitmap_set( vertical_mesh_line_flags, i, j)) {
338
+          if (circle_flags.marked(i, j) && circle_flags.marked(i, j + 1)) {   // Test whether a downward line can be done
339
+            if (!vertical_mesh_line_flags.marked(i, j)) {
347 340
               // Two circles that need a vertical line to connect them
348
-              sy = _GET_MESH_Y(  j  ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // top edge
349
-              ey = _GET_MESH_Y(j + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // bottom edge
341
+              s.y = _GET_MESH_Y(  j  ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // top edge
342
+              e.y = _GET_MESH_Y(j + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // bottom edge
350 343
 
351
-              sx = ex = constrain(_GET_MESH_X(i), X_MIN_POS + 1, X_MAX_POS - 1);
352
-              LIMIT(sy, Y_MIN_POS + 1, Y_MAX_POS - 1);
353
-              LIMIT(ey, Y_MIN_POS + 1, Y_MAX_POS - 1);
344
+              s.x = e.x = constrain(_GET_MESH_X(i), X_MIN_POS + 1, X_MAX_POS - 1);
345
+              LIMIT(s.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
346
+              LIMIT(e.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
354 347
 
355
-              if (position_is_reachable(sx, sy) && position_is_reachable(ex, ey))
356
-                print_line_from_here_to_there(sx, sy, g26_layer_height, ex, ey, g26_layer_height);
348
+              if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y))
349
+                print_line_from_here_to_there(s, e);
357 350
 
358
-              bitmap_set(vertical_mesh_line_flags, i, j); // Mark done, even if skipped
351
+              vertical_mesh_line_flags.mark(i, j); // Mark done, even if skipped
359 352
             }
360 353
           }
361 354
         }
@@ -436,19 +429,19 @@ inline bool prime_nozzle() {
436 429
       ui.set_status_P(PSTR(MSG_G26_MANUAL_PRIME), 99);
437 430
       ui.chirp();
438 431
 
439
-      set_destination_from_current();
432
+      destination = current_position;
440 433
 
441 434
       recover_filament(destination); // Make sure G26 doesn't think the filament is retracted().
442 435
 
443 436
       while (!ui.button_pressed()) {
444 437
         ui.chirp();
445
-        destination[E_AXIS] += 0.25;
438
+        destination.e += 0.25;
446 439
         #if ENABLED(PREVENT_LENGTHY_EXTRUDE)
447 440
           Total_Prime += 0.25;
448 441
           if (Total_Prime >= EXTRUDE_MAXLENGTH) return G26_ERR;
449 442
         #endif
450 443
         prepare_internal_move_to_destination(fr_slow_e);
451
-        set_destination_from_current();
444
+        destination = current_position;
452 445
         planner.synchronize();    // Without this synchronize, the purge is more consistent,
453 446
                                   // but because the planner has a buffer, we won't be able
454 447
                                   // to stop as quickly. So we put up with the less smooth
@@ -468,10 +461,10 @@ inline bool prime_nozzle() {
468 461
       ui.set_status_P(PSTR(MSG_G26_FIXED_LENGTH), 99);
469 462
       ui.quick_feedback();
470 463
     #endif
471
-    set_destination_from_current();
472
-    destination[E_AXIS] += g26_prime_length;
464
+    destination = current_position;
465
+    destination.e += g26_prime_length;
473 466
     prepare_internal_move_to_destination(fr_slow_e);
474
-    set_destination_from_current();
467
+    destination.e -= g26_prime_length;
475 468
     retract_filament(destination);
476 469
   }
477 470
 
@@ -630,9 +623,9 @@ void GcodeSuite::G26() {
630 623
     return;
631 624
   }
632 625
 
633
-  g26_x_pos = parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : current_position[X_AXIS];
634
-  g26_y_pos = parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : current_position[Y_AXIS];
635
-  if (!position_is_reachable(g26_x_pos, g26_y_pos)) {
626
+  g26_pos.set(parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : current_position.x,
627
+              parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : current_position.y);
628
+  if (!position_is_reachable(g26_pos.x, g26_pos.y)) {
636 629
     SERIAL_ECHOLNPGM("?Specified X,Y coordinate out of bounds.");
637 630
     return;
638 631
   }
@@ -642,9 +635,9 @@ void GcodeSuite::G26() {
642 635
    */
643 636
   set_bed_leveling_enabled(!parser.seen('D'));
644 637
 
645
-  if (current_position[Z_AXIS] < Z_CLEARANCE_BETWEEN_PROBES) {
638
+  if (current_position.z < Z_CLEARANCE_BETWEEN_PROBES) {
646 639
     do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
647
-    set_current_from_destination();
640
+    current_position = destination;
648 641
   }
649 642
 
650 643
   #if DISABLED(NO_VOLUMETRICS)
@@ -655,7 +648,7 @@ void GcodeSuite::G26() {
655 648
 
656 649
   if (turn_on_heaters() != G26_OK) goto LEAVE;
657 650
 
658
-  current_position[E_AXIS] = 0.0;
651
+  current_position.e = 0.0;
659 652
   sync_plan_position_e();
660 653
 
661 654
   if (g26_prime_flag && prime_nozzle() != G26_OK) goto LEAVE;
@@ -670,13 +663,13 @@ void GcodeSuite::G26() {
670 663
    *  It's  "Show Time" !!!
671 664
    */
672 665
 
673
-  ZERO(circle_flags);
674
-  ZERO(horizontal_mesh_line_flags);
675
-  ZERO(vertical_mesh_line_flags);
666
+  circle_flags.reset();
667
+  horizontal_mesh_line_flags.reset();
668
+  vertical_mesh_line_flags.reset();
676 669
 
677 670
   // Move nozzle to the specified height for the first layer
678
-  set_destination_from_current();
679
-  destination[Z_AXIS] = g26_layer_height;
671
+  destination = current_position;
672
+  destination.z = g26_layer_height;
680 673
   move_to(destination, 0.0);
681 674
   move_to(destination, g26_ooze_amount);
682 675
 
@@ -706,71 +699,68 @@ void GcodeSuite::G26() {
706 699
 
707 700
   mesh_index_pair location;
708 701
   do {
709
-     location = g26_continue_with_closest
710
-      ? find_closest_circle_to_print(current_position[X_AXIS], current_position[Y_AXIS])
711
-      : find_closest_circle_to_print(g26_x_pos, g26_y_pos); // Find the closest Mesh Intersection to where we are now.
702
+    // Find the nearest confluence
703
+    location = find_closest_circle_to_print(g26_continue_with_closest ? xy_pos_t(current_position) : g26_pos);
712 704
 
713
-    if (location.x_index >= 0 && location.y_index >= 0) {
714
-      const float circle_x = _GET_MESH_X(location.x_index),
715
-                  circle_y = _GET_MESH_Y(location.y_index);
705
+    if (location.valid()) {
706
+      const xy_pos_t circle = _GET_MESH_POS(location.pos);
716 707
 
717 708
       // If this mesh location is outside the printable_radius, skip it.
718
-      if (!position_is_reachable(circle_x, circle_y)) continue;
709
+      if (!position_is_reachable(circle)) continue;
719 710
 
720 711
       // Determine where to start and end the circle,
721 712
       // which is always drawn counter-clockwise.
722
-      const uint8_t xi = location.x_index, yi = location.y_index;
723
-      const bool f = yi == 0, r = xi >= GRID_MAX_POINTS_X - 1, b = yi >= GRID_MAX_POINTS_Y - 1;
713
+      const xy_int8_t st = location;
714
+      const bool f = st.y == 0,
715
+                 r = st.x >= GRID_MAX_POINTS_X - 1,
716
+                 b = st.y >= GRID_MAX_POINTS_Y - 1;
724 717
 
725 718
       #if ENABLED(ARC_SUPPORT)
726 719
 
727 720
         #define ARC_LENGTH(quarters)  (INTERSECTION_CIRCLE_RADIUS * M_PI * (quarters) / 2)
728 721
         #define INTERSECTION_CIRCLE_DIAM  ((INTERSECTION_CIRCLE_RADIUS) * 2)
729
-        float sx = circle_x + INTERSECTION_CIRCLE_RADIUS,   // default to full circle
730
-              ex = circle_x + INTERSECTION_CIRCLE_RADIUS,
731
-              sy = circle_y, ey = circle_y,
732
-              arc_length = ARC_LENGTH(4);
722
+
723
+        xy_float_t e = { circle.x + INTERSECTION_CIRCLE_RADIUS, circle.y };
724
+        xyz_float_t s = e;
733 725
 
734 726
         // Figure out where to start and end the arc - we always print counterclockwise
735
-        if (xi == 0) {                             // left edge
736
-          if (!f) { sx = circle_x; sy -= INTERSECTION_CIRCLE_RADIUS; }
737
-          if (!b) { ex = circle_x; ey += INTERSECTION_CIRCLE_RADIUS; }
727
+        float arc_length = ARC_LENGTH(4);
728
+        if (st.x == 0) {                             // left edge
729
+          if (!f) { s.x = circle.x; s.y -= INTERSECTION_CIRCLE_RADIUS; }
730
+          if (!b) { e.x = circle.x; e.y += INTERSECTION_CIRCLE_RADIUS; }
738 731
           arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2);
739 732
         }
740 733
         else if (r) {                             // right edge
741
-          sx = b ? circle_x - (INTERSECTION_CIRCLE_RADIUS) : circle_x;
742
-          ex = f ? circle_x - (INTERSECTION_CIRCLE_RADIUS) : circle_x;
743
-          sy = b ? circle_y : circle_y + INTERSECTION_CIRCLE_RADIUS;
744
-          ey = f ? circle_y : circle_y - (INTERSECTION_CIRCLE_RADIUS);
734
+          if (b) s.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y);
735
+          else   s.set(circle.x, circle.y + INTERSECTION_CIRCLE_RADIUS);
736
+          if (f) e.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y);
737
+          else   e.set(circle.x, circle.y - (INTERSECTION_CIRCLE_RADIUS));
745 738
           arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2);
746 739
         }
747 740
         else if (f) {
748
-          ex -= INTERSECTION_CIRCLE_DIAM;
741
+          e.x -= INTERSECTION_CIRCLE_DIAM;
749 742
           arc_length = ARC_LENGTH(2);
750 743
         }
751 744
         else if (b) {
752
-          sx -= INTERSECTION_CIRCLE_DIAM;
745
+          s.x -= INTERSECTION_CIRCLE_DIAM;
753 746
           arc_length = ARC_LENGTH(2);
754 747
         }
755 748
 
756
-        const float arc_offset[2] = { circle_x - sx, circle_y - sy },
757
-                    dx_s = current_position[X_AXIS] - sx,   // find our distance from the start of the actual circle
758
-                    dy_s = current_position[Y_AXIS] - sy,
759
-                    dist_start = HYPOT2(dx_s, dy_s),
760
-                    endpoint[XYZE] = {
761
-                      ex, ey,
762
-                      g26_layer_height,
763
-                      current_position[E_AXIS] + (arc_length * g26_e_axis_feedrate * g26_extrusion_multiplier)
764
-                    };
749
+        const ab_float_t arc_offset = circle - s;
750
+        const xy_float_t dist = current_position - s;   // Distance from the start of the actual circle
751
+        const float dist_start = HYPOT2(dist.x, dist.y);
752
+        const xyze_pos_t endpoint = {
753
+          e.x, e.y, g26_layer_height,
754
+          current_position.e + (arc_length * g26_e_axis_feedrate * g26_extrusion_multiplier)
755
+        };
765 756
 
766 757
         if (dist_start > 2.0) {
767
-          retract_filament(destination);
768
-          //todo:  parameterize the bump height with a define
769
-          move_to(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] + 0.500, 0.0);  // Z bump to minimize scraping
770
-          move_to(sx, sy, g26_layer_height + 0.500, 0.0); // Get to the starting point with no extrusion while bumped
758
+          s.z = g26_layer_height + 0.5f;
759
+          retract_lift_move(s);
771 760
         }
772 761
 
773
-        move_to(sx, sy, g26_layer_height, 0.0); // Get to the starting point with no extrusion / un-Z bump
762
+        s.z = g26_layer_height;
763
+        move_to(s, 0.0);  // Get to the starting point with no extrusion / un-Z lift
774 764
 
775 765
         recover_filament(destination);
776 766
 
@@ -778,7 +768,7 @@ void GcodeSuite::G26() {
778 768
         feedrate_mm_s = PLANNER_XY_FEEDRATE() * 0.1f;
779 769
         plan_arc(endpoint, arc_offset, false);  // Draw a counter-clockwise arc
780 770
         feedrate_mm_s = old_feedrate;
781
-        set_destination_from_current();
771
+        destination = current_position;
782 772
 
783 773
         #if HAS_LCD_MENU
784 774
           if (user_canceled()) goto LEAVE; // Check if the user wants to stop the Mesh Validation
@@ -787,7 +777,7 @@ void GcodeSuite::G26() {
787 777
       #else // !ARC_SUPPORT
788 778
 
789 779
         int8_t start_ind = -2, end_ind = 9; // Assume a full circle (from 5:00 to 5:00)
790
-        if (xi == 0) {                      // Left edge? Just right half.
780
+        if (st.x == 0) {                    // Left edge? Just right half.
791 781
           start_ind = f ? 0 : -3;           //  03:00 to 12:00 for front-left
792 782
           end_ind = b ? 0 : 2;              //  06:00 to 03:00 for back-left
793 783
         }
@@ -810,23 +800,21 @@ void GcodeSuite::G26() {
810 800
             if (user_canceled()) goto LEAVE;          // Check if the user wants to stop the Mesh Validation
811 801
           #endif
812 802
 
813
-          float rx = circle_x + _COS(ind),            // For speed, these are now a lookup table entry
814
-                ry = circle_y + _SIN(ind),
815
-                xe = circle_x + _COS(ind + 1),
816
-                ye = circle_y + _SIN(ind + 1);
803
+          xy_float_t p = { circle.x + _COS(ind    ), circle.y + _SIN(ind    ), g26_layer_height },
804
+                     q = { circle.x + _COS(ind + 1), circle.y + _SIN(ind + 1), g26_layer_height };
817 805
 
818 806
           #if IS_KINEMATIC
819 807
             // Check to make sure this segment is entirely on the bed, skip if not.
820
-            if (!position_is_reachable(rx, ry) || !position_is_reachable(xe, ye)) continue;
821
-          #else                                               // not, we need to skip
822
-            LIMIT(rx, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops
823
-            LIMIT(ry, Y_MIN_POS + 1, Y_MAX_POS - 1);
824
-            LIMIT(xe, X_MIN_POS + 1, X_MAX_POS - 1);
825
-            LIMIT(ye, Y_MIN_POS + 1, Y_MAX_POS - 1);
808
+            if (!position_is_reachable(p) || !position_is_reachable(q)) continue;
809
+          #else
810
+            LIMIT(p.x, X_MIN_POS + 1, X_MAX_POS - 1); // Prevent hitting the endstops
811
+            LIMIT(p.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
812
+            LIMIT(q.x, X_MIN_POS + 1, X_MAX_POS - 1);
813
+            LIMIT(q.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
826 814
           #endif
827 815
 
828
-          print_line_from_here_to_there(rx, ry, g26_layer_height, xe, ye, g26_layer_height);
829
-          SERIAL_FLUSH();  // Prevent host M105 buffer overrun.
816
+          print_line_from_here_to_there(p, q);
817
+          SERIAL_FLUSH();   // Prevent host M105 buffer overrun.
830 818
         }
831 819
 
832 820
       #endif // !ARC_SUPPORT
@@ -836,19 +824,18 @@ void GcodeSuite::G26() {
836 824
 
837 825
     SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
838 826
 
839
-  } while (--g26_repeats && location.x_index >= 0 && location.y_index >= 0);
827
+  } while (--g26_repeats && location.valid());
840 828
 
841 829
   LEAVE:
842 830
   ui.set_status_P(PSTR(MSG_G26_LEAVING), -1);
843 831
 
844 832
   retract_filament(destination);
845
-  destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;
833
+  destination.z = Z_CLEARANCE_BETWEEN_PROBES;
846 834
 
847 835
   move_to(destination, 0); // Raise the nozzle
848 836
 
849
-  destination[X_AXIS] = g26_x_pos;                            // Move back to the starting position
850
-  destination[Y_AXIS] = g26_y_pos;
851
-  //destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;         // Keep the nozzle where it is
837
+  destination.set(g26_pos.x, g26_pos.y);                      // Move back to the starting position
838
+  //destination.z = Z_CLEARANCE_BETWEEN_PROBES;               // Keep the nozzle where it is
852 839
 
853 840
   move_to(destination, 0);                                    // Move back to the starting position
854 841
 

+ 6
- 5
Marlin/src/gcode/bedlevel/G42.cpp View File

@@ -27,6 +27,7 @@
27 27
 #include "../gcode.h"
28 28
 #include "../../Marlin.h" // for IsRunning()
29 29
 #include "../../module/motion.h"
30
+#include "../../module/probe.h" // for probe_offset
30 31
 #include "../../feature/bedlevel/bedlevel.h"
31 32
 
32 33
 /**
@@ -44,15 +45,15 @@ void GcodeSuite::G42() {
44 45
       return;
45 46
     }
46 47
 
47
-    set_destination_from_current();
48
+    destination = current_position;
48 49
 
49
-    if (hasI) destination[X_AXIS] = _GET_MESH_X(ix);
50
-    if (hasJ) destination[Y_AXIS] = _GET_MESH_Y(iy);
50
+    if (hasI) destination.x = _GET_MESH_X(ix);
51
+    if (hasJ) destination.y = _GET_MESH_Y(iy);
51 52
 
52 53
     #if HAS_BED_PROBE
53 54
       if (parser.boolval('P')) {
54
-        if (hasI) destination[X_AXIS] -= probe_offset[X_AXIS];
55
-        if (hasJ) destination[Y_AXIS] -= probe_offset[Y_AXIS];
55
+        if (hasI) destination.x -= probe_offset.x;
56
+        if (hasJ) destination.y -= probe_offset.y;
56 57
       }
57 58
     #endif
58 59
 

+ 5
- 6
Marlin/src/gcode/bedlevel/M420.cpp View File

@@ -66,10 +66,9 @@ void GcodeSuite::M420() {
66 66
       #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
67 67
         const float x_min = probe_min_x(), x_max = probe_max_x(),
68 68
                     y_min = probe_min_y(), y_max = probe_max_y();
69
-        bilinear_start[X_AXIS] = x_min;
70
-        bilinear_start[Y_AXIS] = y_min;
71
-        bilinear_grid_spacing[X_AXIS] = (x_max - x_min) / (GRID_MAX_POINTS_X - 1);
72
-        bilinear_grid_spacing[Y_AXIS] = (y_max - y_min) / (GRID_MAX_POINTS_Y - 1);
69
+        bilinear_start.set(x_min, y_min);
70
+        bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_POINTS_X - 1),
71
+                                  (y_max - y_min) / (GRID_MAX_POINTS_Y - 1));
73 72
       #endif
74 73
       for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++)
75 74
         for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) {
@@ -91,7 +90,7 @@ void GcodeSuite::M420() {
91 90
   // (Don't disable for just M420 or M420 V)
92 91
   if (seen_S && !to_enable) set_bed_leveling_enabled(false);
93 92
 
94
-  const float oldpos[] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] };
93
+  xyz_pos_t oldpos = current_position;
95 94
 
96 95
   #if ENABLED(AUTO_BED_LEVELING_UBL)
97 96
 
@@ -251,7 +250,7 @@ void GcodeSuite::M420() {
251 250
   #endif
252 251
 
253 252
   // Report change in position
254
-  if (memcmp(oldpos, current_position, sizeof(oldpos)))
253
+  if (oldpos != current_position)
255 254
     report_current_position();
256 255
 }
257 256
 

+ 143
- 173
Marlin/src/gcode/bedlevel/abl/G29.cpp View File

@@ -61,15 +61,15 @@
61 61
 
62 62
 #if ABL_GRID
63 63
   #if ENABLED(PROBE_Y_FIRST)
64
-    #define PR_OUTER_VAR xCount
65
-    #define PR_OUTER_END abl_grid_points_x
66
-    #define PR_INNER_VAR yCount
67
-    #define PR_INNER_END abl_grid_points_y
64
+    #define PR_OUTER_VAR meshCount.x
65
+    #define PR_OUTER_END abl_grid_points.x
66
+    #define PR_INNER_VAR meshCount.y
67
+    #define PR_INNER_END abl_grid_points.y
68 68
   #else
69
-    #define PR_OUTER_VAR yCount
70
-    #define PR_OUTER_END abl_grid_points_y
71
-    #define PR_INNER_VAR xCount
72
-    #define PR_INNER_END abl_grid_points_x
69
+    #define PR_OUTER_VAR meshCount.y
70
+    #define PR_OUTER_END abl_grid_points.y
71
+    #define PR_INNER_VAR meshCount.x
72
+    #define PR_INNER_END abl_grid_points.x
73 73
   #endif
74 74
 #endif
75 75
 
@@ -210,7 +210,8 @@ G29_TYPE GcodeSuite::G29() {
210 210
   #endif
211 211
 
212 212
   ABL_VAR int verbose_level;
213
-  ABL_VAR float xProbe, yProbe, measured_z;
213
+  ABL_VAR xy_pos_t probePos;
214
+  ABL_VAR float measured_z;
214 215
   ABL_VAR bool dryrun, abl_should_enable;
215 216
 
216 217
   #if EITHER(PROBE_MANUALLY, AUTO_BED_LEVELING_LINEAR)
@@ -224,20 +225,17 @@ G29_TYPE GcodeSuite::G29() {
224 225
   #if ABL_GRID
225 226
 
226 227
     #if ENABLED(PROBE_MANUALLY)
227
-      ABL_VAR uint8_t PR_OUTER_VAR;
228
-      ABL_VAR  int8_t PR_INNER_VAR;
228
+      ABL_VAR xy_int8_t meshCount;
229 229
     #endif
230 230
 
231
-    ABL_VAR int left_probe_bed_position, right_probe_bed_position, front_probe_bed_position, back_probe_bed_position;
232
-    ABL_VAR float xGridSpacing = 0, yGridSpacing = 0;
231
+    ABL_VAR xy_int_t probe_position_lf, probe_position_rb;
232
+    ABL_VAR xy_float_t gridSpacing = { 0, 0 };
233 233
 
234 234
     #if ENABLED(AUTO_BED_LEVELING_LINEAR)
235
-      ABL_VAR uint8_t abl_grid_points_x = GRID_MAX_POINTS_X,
236
-                      abl_grid_points_y = GRID_MAX_POINTS_Y;
237 235
       ABL_VAR bool do_topography_map;
236
+      ABL_VAR xy_uint8_t abl_grid_points;
238 237
     #else // Bilinear
239
-      uint8_t constexpr abl_grid_points_x = GRID_MAX_POINTS_X,
240
-                        abl_grid_points_y = GRID_MAX_POINTS_Y;
238
+      constexpr xy_uint8_t abl_grid_points = { GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y };
241 239
     #endif
242 240
 
243 241
     #if ENABLED(AUTO_BED_LEVELING_LINEAR)
@@ -269,15 +267,15 @@ G29_TYPE GcodeSuite::G29() {
269 267
     const float x_min = probe_min_x(), x_max = probe_max_x(), y_min = probe_min_y(), y_max = probe_max_y();
270 268
 
271 269
     ABL_VAR vector_3 points[3] = {
272
-    #if ENABLED(HAS_FIXED_3POINT)
273
-      vector_3(PROBE_PT_1_X, PROBE_PT_1_Y, 0),
274
-      vector_3(PROBE_PT_2_X, PROBE_PT_2_Y, 0),
275
-      vector_3(PROBE_PT_3_X, PROBE_PT_3_Y, 0)
276
-    #else
277
-      vector_3(x_min, y_min, 0),
278
-      vector_3(x_max, y_min, 0),
279
-      vector_3((x_max - x_min) / 2, y_max, 0)
280
-    #endif
270
+      #if ENABLED(HAS_FIXED_3POINT)
271
+        { PROBE_PT_1_X, PROBE_PT_1_Y, 0 },
272
+        { PROBE_PT_2_X, PROBE_PT_2_Y, 0 },
273
+        { PROBE_PT_3_X, PROBE_PT_3_Y, 0 }
274
+      #else
275
+        { x_min, y_min, 0 },
276
+        { x_max, y_min, 0 },
277
+        { (x_max - x_min) / 2, y_max, 0 }
278
+      #endif
281 279
     };
282 280
 
283 281
   #endif // AUTO_BED_LEVELING_3POINT
@@ -311,7 +309,7 @@ G29_TYPE GcodeSuite::G29() {
311 309
           G29_RETURN(false);
312 310
         }
313 311
 
314
-        const float rz = parser.seenval('Z') ? RAW_Z_POSITION(parser.value_linear_units()) : current_position[Z_AXIS];
312
+        const float rz = parser.seenval('Z') ? RAW_Z_POSITION(parser.value_linear_units()) : current_position.z;
315 313
         if (!WITHIN(rz, -10, 10)) {
316 314
           SERIAL_ERROR_MSG("Bad Z value");
317 315
           G29_RETURN(false);
@@ -323,8 +321,8 @@ G29_TYPE GcodeSuite::G29() {
323 321
 
324 322
         if (!isnan(rx) && !isnan(ry)) {
325 323
           // Get nearest i / j from rx / ry
326
-          i = (rx - bilinear_start[X_AXIS] + 0.5 * xGridSpacing) / xGridSpacing;
327
-          j = (ry - bilinear_start[Y_AXIS] + 0.5 * yGridSpacing) / yGridSpacing;
324
+          i = (rx - bilinear_start.x + 0.5 * gridSpacing.x) / gridSpacing.x;
325
+          j = (ry - bilinear_start.y + 0.5 * gridSpacing.y) / gridSpacing.y;
328 326
           LIMIT(i, 0, GRID_MAX_POINTS_X - 1);
329 327
           LIMIT(j, 0, GRID_MAX_POINTS_Y - 1);
330 328
         }
@@ -373,20 +371,22 @@ G29_TYPE GcodeSuite::G29() {
373 371
 
374 372
       // X and Y specify points in each direction, overriding the default
375 373
       // These values may be saved with the completed mesh
376
-      abl_grid_points_x = parser.intval('X', GRID_MAX_POINTS_X);
377
-      abl_grid_points_y = parser.intval('Y', GRID_MAX_POINTS_Y);
378
-      if (parser.seenval('P')) abl_grid_points_x = abl_grid_points_y = parser.value_int();
374
+      abl_grid_points.set(
375
+        parser.byteval('X', GRID_MAX_POINTS_X),
376
+        parser.byteval('Y', GRID_MAX_POINTS_Y)
377
+      );
378
+      if (parser.seenval('P')) abl_grid_points.x = abl_grid_points.y = parser.value_int();
379 379
 
380
-      if (!WITHIN(abl_grid_points_x, 2, GRID_MAX_POINTS_X)) {
380
+      if (!WITHIN(abl_grid_points.x, 2, GRID_MAX_POINTS_X)) {
381 381
         SERIAL_ECHOLNPGM("?Probe points (X) implausible (2-" STRINGIFY(GRID_MAX_POINTS_X) ").");
382 382
         G29_RETURN(false);
383 383
       }
384
-      if (!WITHIN(abl_grid_points_y, 2, GRID_MAX_POINTS_Y)) {
384
+      if (!WITHIN(abl_grid_points.y, 2, GRID_MAX_POINTS_Y)) {
385 385
         SERIAL_ECHOLNPGM("?Probe points (Y) implausible (2-" STRINGIFY(GRID_MAX_POINTS_Y) ").");
386 386
         G29_RETURN(false);
387 387
       }
388 388
 
389
-      abl_points = abl_grid_points_x * abl_grid_points_y;
389
+      abl_points = abl_grid_points.x * abl_grid_points.y;
390 390
       mean = 0;
391 391
 
392 392
     #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
@@ -404,27 +404,35 @@ G29_TYPE GcodeSuite::G29() {
404 404
 
405 405
       if (parser.seen('H')) {
406 406
         const int16_t size = (int16_t)parser.value_linear_units();
407
-        left_probe_bed_position  = _MAX(X_CENTER - size / 2, x_min);
408
-        right_probe_bed_position = _MIN(left_probe_bed_position + size, x_max);
409
-        front_probe_bed_position = _MAX(Y_CENTER - size / 2, y_min);
410
-        back_probe_bed_position  = _MIN(front_probe_bed_position + size, y_max);
407
+        probe_position_lf.set(
408
+          _MAX(X_CENTER - size / 2, x_min),
409
+          _MAX(Y_CENTER - size / 2, y_min)
410
+        );
411
+        probe_position_rb.set(
412
+          _MIN(probe_position_lf.x + size, x_max),
413
+          _MIN(probe_position_lf.y + size, y_max)
414
+        );
411 415
       }
412 416
       else {
413
-        left_probe_bed_position  = parser.seenval('L') ? (int)RAW_X_POSITION(parser.value_linear_units()) : _MAX(X_CENTER - X_BED_SIZE / 2, x_min);
414
-        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);
415
-        front_probe_bed_position = parser.seenval('F') ? (int)RAW_Y_POSITION(parser.value_linear_units()) : _MAX(Y_CENTER - Y_BED_SIZE / 2, y_min);
416
-        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);
417
+        probe_position_lf.set(
418
+          parser.seenval('L') ? (int)RAW_X_POSITION(parser.value_linear_units()) : _MAX(X_CENTER - (X_BED_SIZE) / 2,      x_min),
419
+          parser.seenval('F') ? (int)RAW_Y_POSITION(parser.value_linear_units()) : _MAX(Y_CENTER - (Y_BED_SIZE) / 2,      y_min)
420
+        );
421
+        probe_position_rb.set(
422
+          parser.seenval('R') ? (int)RAW_X_POSITION(parser.value_linear_units()) : _MIN(probe_position_lf.x + X_BED_SIZE, x_max),
423
+          parser.seenval('B') ? (int)RAW_Y_POSITION(parser.value_linear_units()) : _MIN(probe_position_lf.y + Y_BED_SIZE, y_max)
424
+        );
417 425
       }
418 426
 
419 427
       if (
420 428
         #if IS_SCARA || ENABLED(DELTA)
421
-             !position_is_reachable_by_probe(left_probe_bed_position, 0)
422
-          || !position_is_reachable_by_probe(right_probe_bed_position, 0)
423
-          || !position_is_reachable_by_probe(0, front_probe_bed_position)
424
-          || !position_is_reachable_by_probe(0, back_probe_bed_position)
429
+             !position_is_reachable_by_probe(probe_position_lf.x, 0)
430
+          || !position_is_reachable_by_probe(probe_position_rb.x, 0)
431
+          || !position_is_reachable_by_probe(0, probe_position_lf.y)
432
+          || !position_is_reachable_by_probe(0, probe_position_rb.y)
425 433
         #else
426
-             !position_is_reachable_by_probe(left_probe_bed_position, front_probe_bed_position)
427
-          || !position_is_reachable_by_probe(right_probe_bed_position, back_probe_bed_position)
434
+             !position_is_reachable_by_probe(probe_position_lf)
435
+          || !position_is_reachable_by_probe(probe_position_rb)
428 436
         #endif
429 437
       ) {
430 438
         SERIAL_ECHOLNPGM("? (L,R,F,B) out of bounds.");
@@ -432,8 +440,8 @@ G29_TYPE GcodeSuite::G29() {
432 440
       }
433 441
 
434 442
       // probe at the points of a lattice grid
435
-      xGridSpacing = (right_probe_bed_position - left_probe_bed_position) / (abl_grid_points_x - 1);
436
-      yGridSpacing = (back_probe_bed_position - front_probe_bed_position) / (abl_grid_points_y - 1);
443
+      gridSpacing.set((probe_position_rb.x - probe_position_lf.x) / (abl_grid_points.x - 1),
444
+                      (probe_position_rb.y - probe_position_lf.y) / (abl_grid_points.y - 1));
437 445
 
438 446
     #endif // ABL_GRID
439 447
 
@@ -464,19 +472,13 @@ G29_TYPE GcodeSuite::G29() {
464 472
       #if ENABLED(PROBE_MANUALLY)
465 473
         if (!no_action)
466 474
       #endif
467
-      if ( xGridSpacing != bilinear_grid_spacing[X_AXIS]
468
-        || yGridSpacing != bilinear_grid_spacing[Y_AXIS]
469
-        || left_probe_bed_position != bilinear_start[X_AXIS]
470
-        || front_probe_bed_position != bilinear_start[Y_AXIS]
471
-      ) {
475
+      if (gridSpacing != bilinear_grid_spacing || probe_position_lf != bilinear_start) {
472 476
         // Reset grid to 0.0 or "not probed". (Also disables ABL)
473 477
         reset_bed_level();
474 478
 
475 479
         // Initialize a grid with the given dimensions
476
-        bilinear_grid_spacing[X_AXIS] = xGridSpacing;
477
-        bilinear_grid_spacing[Y_AXIS] = yGridSpacing;
478
-        bilinear_start[X_AXIS] = left_probe_bed_position;
479
-        bilinear_start[Y_AXIS] = front_probe_bed_position;
480
+        bilinear_grid_spacing = gridSpacing.asInt();
481
+        bilinear_start = probe_position_lf;
480 482
 
481 483
         // Can't re-enable (on error) until the new grid is written
482 484
         abl_should_enable = false;
@@ -546,17 +548,17 @@ G29_TYPE GcodeSuite::G29() {
546 548
 
547 549
       // For G29 after adjusting Z.
548 550
       // Save the previous Z before going to the next point
549
-      measured_z = current_position[Z_AXIS];
551
+      measured_z = current_position.z;
550 552
 
551 553
       #if ENABLED(AUTO_BED_LEVELING_LINEAR)
552 554
 
553 555
         mean += measured_z;
554 556
         eqnBVector[index] = measured_z;
555
-        eqnAMatrix[index + 0 * abl_points] = xProbe;
556
-        eqnAMatrix[index + 1 * abl_points] = yProbe;
557
+        eqnAMatrix[index + 0 * abl_points] = probePos.x;
558
+        eqnAMatrix[index + 1 * abl_points] = probePos.y;
557 559
         eqnAMatrix[index + 2 * abl_points] = 1;
558 560
 
559
-        incremental_LSF(&lsf_results, xProbe, yProbe, measured_z);
561
+        incremental_LSF(&lsf_results, probePos, measured_z);
560 562
 
561 563
       #elif ENABLED(AUTO_BED_LEVELING_3POINT)
562 564
 
@@ -564,12 +566,13 @@ G29_TYPE GcodeSuite::G29() {
564 566
 
565 567
       #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
566 568
 
567
-        z_values[xCount][yCount] = measured_z + zoffset;
569
+        const float newz = measured_z + zoffset;
570
+        z_values[meshCount.x][meshCount.y] = newz;
568 571
         #if ENABLED(EXTENSIBLE_UI)
569
-          ExtUI::onMeshUpdate(xCount, yCount, z_values[xCount][yCount]);
572
+          ExtUI::onMeshUpdate(meshCount, newz);
570 573
         #endif
571 574
 
572
-        if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Save X", xCount, " Y", yCount, " Z", measured_z + zoffset);
575
+        if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Save X", meshCount.x, " Y", meshCount.y, " Z", measured_z + zoffset);
573 576
 
574 577
       #endif
575 578
     }
@@ -583,7 +586,7 @@ G29_TYPE GcodeSuite::G29() {
583 586
       // Skip any unreachable points
584 587
       while (abl_probe_index < abl_points) {
585 588
 
586
-        // Set xCount, yCount based on abl_probe_index, with zig-zag
589
+        // Set meshCount.x, meshCount.y based on abl_probe_index, with zig-zag
587 590
         PR_OUTER_VAR = abl_probe_index / PR_INNER_END;
588 591
         PR_INNER_VAR = abl_probe_index - (PR_OUTER_VAR * PR_INNER_END);
589 592
 
@@ -592,24 +595,23 @@ G29_TYPE GcodeSuite::G29() {
592 595
 
593 596
         if (zig) PR_INNER_VAR = (PR_INNER_END - 1) - PR_INNER_VAR;
594 597
 
595
-        const float xBase = xCount * xGridSpacing + left_probe_bed_position,
596
-                    yBase = yCount * yGridSpacing + front_probe_bed_position;
598
+        const xy_pos_t base = probe_position_lf.asFloat() + gridSpacing * meshCount.asFloat();
597 599
 
598
-        xProbe = FLOOR(xBase + (xBase < 0 ? 0 : 0.5));
599
-        yProbe = FLOOR(yBase + (yBase < 0 ? 0 : 0.5));
600
+        probePos.set(FLOOR(base.x + (base.x < 0 ? 0 : 0.5)),
601
+                     FLOOR(base.y + (base.y < 0 ? 0 : 0.5)));
600 602
 
601 603
         #if ENABLED(AUTO_BED_LEVELING_LINEAR)
602
-          indexIntoAB[xCount][yCount] = abl_probe_index;
604
+          indexIntoAB[meshCount.x][meshCount.y] = abl_probe_index;
603 605
         #endif
604 606
 
605 607
         // Keep looping till a reachable point is found
606
-        if (position_is_reachable(xProbe, yProbe)) break;
608
+        if (position_is_reachable(probePos)) break;
607 609
         ++abl_probe_index;
608 610
       }
609 611
 
610 612
       // Is there a next point to move to?
611 613
       if (abl_probe_index < abl_points) {
612
-        _manual_goto_xy(xProbe, yProbe); // Can be used here too!
614
+        _manual_goto_xy(probePos); // Can be used here too!
613 615
         #if HAS_SOFTWARE_ENDSTOPS
614 616
           // Disable software endstops to allow manual adjustment
615 617
           // If G29 is not completed, they will not be re-enabled
@@ -633,9 +635,8 @@ G29_TYPE GcodeSuite::G29() {
633 635
 
634 636
       // Probe at 3 arbitrary points
635 637
       if (abl_probe_index < abl_points) {
636
-        xProbe = points[abl_probe_index].x;
637
-        yProbe = points[abl_probe_index].y;
638
-        _manual_goto_xy(xProbe, yProbe);
638
+        probePos = points[abl_probe_index];
639
+        _manual_goto_xy(probePos);
639 640
         #if HAS_SOFTWARE_ENDSTOPS
640 641
           // Disable software endstops to allow manual adjustment
641 642
           // If G29 is not completed, they will not be re-enabled
@@ -654,11 +655,7 @@ G29_TYPE GcodeSuite::G29() {
654 655
 
655 656
         if (!dryrun) {
656 657
           vector_3 planeNormal = vector_3::cross(points[0] - points[1], points[2] - points[1]).get_normal();
657
-          if (planeNormal.z < 0) {
658
-            planeNormal.x *= -1;
659
-            planeNormal.y *= -1;
660
-            planeNormal.z *= -1;
661
-          }
658
+          if (planeNormal.z < 0) planeNormal *= -1;
662 659
           planner.bed_level_matrix = matrix_3x3::create_look_at(planeNormal);
663 660
 
664 661
           // Can't re-enable (on error) until the new grid is written
@@ -681,8 +678,11 @@ G29_TYPE GcodeSuite::G29() {
681 678
 
682 679
       measured_z = 0;
683 680
 
681
+      xy_int8_t meshCount;
682
+
683
+      // Outer loop is X with PROBE_Y_FIRST enabled
684 684
       // Outer loop is Y with PROBE_Y_FIRST disabled
685
-      for (uint8_t PR_OUTER_VAR = 0; PR_OUTER_VAR < PR_OUTER_END && !isnan(measured_z); PR_OUTER_VAR++) {
685
+      for (PR_OUTER_VAR = 0; PR_OUTER_VAR < PR_OUTER_END && !isnan(measured_z); PR_OUTER_VAR++) {
686 686
 
687 687
         int8_t inStart, inStop, inInc;
688 688
 
@@ -703,21 +703,21 @@ G29_TYPE GcodeSuite::G29() {
703 703
         uint8_t pt_index = (PR_OUTER_VAR) * (PR_INNER_END) + 1;
704 704
 
705 705
         // Inner loop is Y with PROBE_Y_FIRST enabled
706
-        for (int8_t PR_INNER_VAR = inStart; PR_INNER_VAR != inStop; pt_index++, PR_INNER_VAR += inInc) {
706
+        // Inner loop is X with PROBE_Y_FIRST disabled
707
+        for (PR_INNER_VAR = inStart; PR_INNER_VAR != inStop; pt_index++, PR_INNER_VAR += inInc) {
707 708
 
708
-          const float xBase = left_probe_bed_position + xGridSpacing * xCount,
709
-                      yBase = front_probe_bed_position + yGridSpacing * yCount;
709
+          const xy_pos_t base = probe_position_lf.asFloat() + gridSpacing * meshCount.asFloat();
710 710
 
711
-          xProbe = FLOOR(xBase + (xBase < 0 ? 0 : 0.5));
712
-          yProbe = FLOOR(yBase + (yBase < 0 ? 0 : 0.5));
711
+          probePos.set(FLOOR(base.x + (base.x < 0 ? 0 : 0.5)),
712
+                       FLOOR(base.y + (base.y < 0 ? 0 : 0.5)));
713 713
 
714 714
           #if ENABLED(AUTO_BED_LEVELING_LINEAR)
715
-            indexIntoAB[xCount][yCount] = ++abl_probe_index; // 0...
715
+            indexIntoAB[meshCount.x][meshCount.y] = ++abl_probe_index; // 0...
716 716
           #endif
717 717
 
718 718
           #if IS_KINEMATIC
719 719
             // Avoid probing outside the round or hexagonal area
720
-            if (!position_is_reachable_by_probe(xProbe, yProbe)) continue;
720
+            if (!position_is_reachable_by_probe(probePos)) continue;
721 721
           #endif
722 722
 
723 723
           if (verbose_level) SERIAL_ECHOLNPAIR("Probing mesh point ", int(pt_index), "/", int(GRID_MAX_POINTS), ".");
@@ -725,7 +725,7 @@ G29_TYPE GcodeSuite::G29() {
725 725
             ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), PSTR(MSG_PROBING_MESH), int(pt_index), int(GRID_MAX_POINTS));
726 726
           #endif
727 727
 
728
-          measured_z = faux ? 0.001 * random(-100, 101) : probe_at_point(xProbe, yProbe, raise_after, verbose_level);
728
+          measured_z = faux ? 0.001 * random(-100, 101) : probe_at_point(probePos, raise_after, verbose_level);
729 729
 
730 730
           if (isnan(measured_z)) {
731 731
             set_bed_leveling_enabled(abl_should_enable);
@@ -736,17 +736,17 @@ G29_TYPE GcodeSuite::G29() {
736 736
 
737 737
             mean += measured_z;
738 738
             eqnBVector[abl_probe_index] = measured_z;
739
-            eqnAMatrix[abl_probe_index + 0 * abl_points] = xProbe;
740
-            eqnAMatrix[abl_probe_index + 1 * abl_points] = yProbe;
739
+            eqnAMatrix[abl_probe_index + 0 * abl_points] = probePos.x;
740
+            eqnAMatrix[abl_probe_index + 1 * abl_points] = probePos.y;
741 741
             eqnAMatrix[abl_probe_index + 2 * abl_points] = 1;
742 742
 
743
-            incremental_LSF(&lsf_results, xProbe, yProbe, measured_z);
743
+            incremental_LSF(&lsf_results, probePos, measured_z);
744 744
 
745 745
           #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
746 746
 
747
-            z_values[xCount][yCount] = measured_z + zoffset;
747
+            z_values[meshCount.x][meshCount.y] = measured_z + zoffset;
748 748
             #if ENABLED(EXTENSIBLE_UI)
749
-              ExtUI::onMeshUpdate(xCount, yCount, z_values[xCount][yCount]);
749
+              ExtUI::onMeshUpdate(meshCount.x, meshCount.y, z_values[meshCount.x][meshCount.y]);
750 750
             #endif
751 751
 
752 752
           #endif
@@ -768,9 +768,8 @@ G29_TYPE GcodeSuite::G29() {
768 768
         #endif
769 769
 
770 770
         // Retain the last probe position
771
-        xProbe = points[i].x;
772
-        yProbe = points[i].y;
773
-        measured_z = faux ? 0.001 * random(-100, 101) : probe_at_point(xProbe, yProbe, raise_after, verbose_level);
771
+        probePos = points[i];
772
+        measured_z = faux ? 0.001 * random(-100, 101) : probe_at_point(probePos, raise_after, verbose_level);
774 773
         if (isnan(measured_z)) {
775 774
           set_bed_leveling_enabled(abl_should_enable);
776 775
           break;
@@ -845,19 +844,19 @@ G29_TYPE GcodeSuite::G29() {
845 844
        * plane equation in the standard form, which is Vx*x+Vy*y+Vz*z+d = 0
846 845
        * so Vx = -a Vy = -b Vz = 1 (we want the vector facing towards positive Z
847 846
        */
848
-      float plane_equation_coefficients[3];
847
+      struct { float a, b, d; } plane_equation_coefficients;
849 848
 
850 849
       finish_incremental_LSF(&lsf_results);
851
-      plane_equation_coefficients[0] = -lsf_results.A;  // We should be able to eliminate the '-' on these three lines and down below
852
-      plane_equation_coefficients[1] = -lsf_results.B;  // but that is not yet tested.
853
-      plane_equation_coefficients[2] = -lsf_results.D;
850
+      plane_equation_coefficients.a = -lsf_results.A;  // We should be able to eliminate the '-' on these three lines and down below
851
+      plane_equation_coefficients.b = -lsf_results.B;  // but that is not yet tested.
852
+      plane_equation_coefficients.d = -lsf_results.D;
854 853
 
855 854
       mean /= abl_points;
856 855
 
857 856
       if (verbose_level) {
858
-        SERIAL_ECHOPAIR_F("Eqn coefficients: a: ", plane_equation_coefficients[0], 8);
859
-        SERIAL_ECHOPAIR_F(" b: ", plane_equation_coefficients[1], 8);
860
-        SERIAL_ECHOPAIR_F(" d: ", plane_equation_coefficients[2], 8);
857
+        SERIAL_ECHOPAIR_F("Eqn coefficients: a: ", plane_equation_coefficients.a, 8);
858
+        SERIAL_ECHOPAIR_F(" b: ", plane_equation_coefficients.b, 8);
859
+        SERIAL_ECHOPAIR_F(" d: ", plane_equation_coefficients.d, 8);
861 860
         if (verbose_level > 2)
862 861
           SERIAL_ECHOPAIR_F("\nMean of sampled points: ", mean, 8);
863 862
         SERIAL_EOL();
@@ -866,13 +865,34 @@ G29_TYPE GcodeSuite::G29() {
866 865
       // Create the matrix but don't correct the position yet
867 866
       if (!dryrun)
868 867
         planner.bed_level_matrix = matrix_3x3::create_look_at(
869
-          vector_3(-plane_equation_coefficients[0], -plane_equation_coefficients[1], 1)    // We can eliminate the '-' here and up above
868
+          vector_3(-plane_equation_coefficients.a, -plane_equation_coefficients.b, 1)    // We can eliminate the '-' here and up above
870 869
         );
871 870
 
872 871
       // Show the Topography map if enabled
873 872
       if (do_topography_map) {
874 873
 
875
-        SERIAL_ECHOLNPGM("\nBed Height Topography:\n"
874
+        float min_diff = 999;
875
+
876
+        auto print_topo_map = [&](PGM_P const title, const bool get_min) {
877
+          serialprintPGM(title);
878
+          for (int8_t yy = abl_grid_points.y - 1; yy >= 0; yy--) {
879
+            for (uint8_t xx = 0; xx < abl_grid_points.x; xx++) {
880
+              const int ind = indexIntoAB[xx][yy];
881
+              xyz_float_t tmp = { eqnAMatrix[ind + 0 * abl_points],
882
+                                  eqnAMatrix[ind + 1 * abl_points], 0 };
883
+              apply_rotation_xyz(planner.bed_level_matrix, tmp);
884
+              if (get_min) NOMORE(min_diff, eqnBVector[ind] - tmp.z);
885
+              const float subval = get_min ? mean : tmp.z + min_diff,
886
+                            diff = eqnBVector[ind] - subval;
887
+              SERIAL_CHAR(' '); if (diff >= 0.0) SERIAL_CHAR('+');   // Include + for column alignment
888
+              SERIAL_ECHO_F(diff, 5);
889
+            } // xx
890
+            SERIAL_EOL();
891
+          } // yy
892
+          SERIAL_EOL();
893
+        };
894
+
895
+        print_topo_map(PSTR("\nBed Height Topography:\n"
876 896
                                "   +--- BACK --+\n"
877 897
                                "   |           |\n"
878 898
                                " L |    (+)    | R\n"
@@ -882,56 +902,10 @@ G29_TYPE GcodeSuite::G29() {
882 902
                                "   |    (-)    | T\n"
883 903
                                "   |           |\n"
884 904
                                "   O-- FRONT --+\n"
885
-                               " (0,0)");
905
+                               " (0,0)\n"), true);
906
+        if (verbose_level > 3)
907
+          print_topo_map(PSTR("\nCorrected Bed Height vs. Bed Topology:\n"), false);
886 908
 
887
-        float min_diff = 999;
888
-
889
-        for (int8_t yy = abl_grid_points_y - 1; yy >= 0; yy--) {
890
-          for (uint8_t xx = 0; xx < abl_grid_points_x; xx++) {
891
-            int ind = indexIntoAB[xx][yy];
892
-            float diff = eqnBVector[ind] - mean,
893
-                  x_tmp = eqnAMatrix[ind + 0 * abl_points],
894
-                  y_tmp = eqnAMatrix[ind + 1 * abl_points],
895
-                  z_tmp = 0;
896
-
897
-            apply_rotation_xyz(planner.bed_level_matrix, x_tmp, y_tmp, z_tmp);
898
-
899
-            NOMORE(min_diff, eqnBVector[ind] - z_tmp);
900
-
901
-            if (diff >= 0.0)
902
-              SERIAL_ECHOPGM(" +");   // Include + for column alignment
903
-            else
904
-              SERIAL_CHAR(' ');
905
-            SERIAL_ECHO_F(diff, 5);
906
-          } // xx
907
-          SERIAL_EOL();
908
-        } // yy
909
-        SERIAL_EOL();
910
-
911
-        if (verbose_level > 3) {
912
-          SERIAL_ECHOLNPGM("\nCorrected Bed Height vs. Bed Topology:");
913
-
914
-          for (int8_t yy = abl_grid_points_y - 1; yy >= 0; yy--) {
915
-            for (uint8_t xx = 0; xx < abl_grid_points_x; xx++) {
916
-              int ind = indexIntoAB[xx][yy];
917
-              float x_tmp = eqnAMatrix[ind + 0 * abl_points],
918
-                    y_tmp = eqnAMatrix[ind + 1 * abl_points],
919
-                    z_tmp = 0;
920
-
921
-              apply_rotation_xyz(planner.bed_level_matrix, x_tmp, y_tmp, z_tmp);
922
-
923
-              float diff = eqnBVector[ind] - z_tmp - min_diff;
924
-              if (diff >= 0.0)
925
-                SERIAL_ECHOPGM(" +");
926
-              // Include + for column alignment
927
-              else
928
-                SERIAL_CHAR(' ');
929
-              SERIAL_ECHO_F(diff, 5);
930
-            } // xx
931
-            SERIAL_EOL();
932
-          } // yy
933
-          SERIAL_EOL();
934
-        }
935 909
       } //do_topography_map
936 910
 
937 911
     #endif // AUTO_BED_LEVELING_LINEAR
@@ -950,24 +924,20 @@ G29_TYPE GcodeSuite::G29() {
950 924
 
951 925
         if (DEBUGGING(LEVELING)) DEBUG_POS("G29 uncorrected XYZ", current_position);
952 926
 
953
-        float converted[XYZ];
954
-        COPY(converted, current_position);
955
-
956
-        planner.leveling_active = true;
957
-        planner.unapply_leveling(converted); // use conversion machinery
958
-        planner.leveling_active = false;
927
+        xyze_pos_t converted = current_position;
928
+        planner.force_unapply_leveling(converted); // use conversion machinery
959 929
 
960 930
         // Use the last measured distance to the bed, if possible
961
-        if ( NEAR(current_position[X_AXIS], xProbe - probe_offset[X_AXIS])
962
-          && NEAR(current_position[Y_AXIS], yProbe - probe_offset[Y_AXIS])
931
+        if ( NEAR(current_position.x, probePos.x - probe_offset.x)
932
+          && NEAR(current_position.y, probePos.y - probe_offset.y)
963 933
         ) {
964
-          const float simple_z = current_position[Z_AXIS] - measured_z;
965
-          if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Probed Z", simple_z, "  Matrix Z", converted[Z_AXIS], "  Discrepancy ", simple_z - converted[Z_AXIS]);
966
-          converted[Z_AXIS] = simple_z;
934
+          const float simple_z = current_position.z - measured_z;
935
+          if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Probed Z", simple_z, "  Matrix Z", converted.z, "  Discrepancy ", simple_z - converted.z);
936
+          converted.z = simple_z;
967 937
         }
968 938
 
969 939
         // The rotated XY and corrected Z are now current_position
970
-        COPY(current_position, converted);
940
+        current_position = converted;
971 941
 
972 942
         if (DEBUGGING(LEVELING)) DEBUG_POS("G29 corrected XYZ", current_position);
973 943
       }
@@ -975,13 +945,13 @@ G29_TYPE GcodeSuite::G29() {
975 945
     #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
976 946
 
977 947
       if (!dryrun) {
978
-        if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("G29 uncorrected Z:", current_position[Z_AXIS]);
948
+        if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("G29 uncorrected Z:", current_position.z);
979 949
 
980 950
         // Unapply the offset because it is going to be immediately applied
981 951
         // and cause compensation movement in Z
982
-        current_position[Z_AXIS] -= bilinear_z_offset(current_position);
952
+        current_position.z -= bilinear_z_offset(current_position);
983 953
 
984
-        if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(" corrected Z:", current_position[Z_AXIS]);
954
+        if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(" corrected Z:", current_position.z);
985 955
       }
986 956
 
987 957
     #endif // ABL_PLANAR

+ 4
- 4
Marlin/src/gcode/bedlevel/mbl/G29.cpp View File

@@ -110,7 +110,7 @@ void GcodeSuite::G29() {
110 110
       }
111 111
       else {
112 112
         // Save Z for the previous mesh position
113
-        mbl.set_zigzag_z(mbl_probe_index - 1, current_position[Z_AXIS]);
113
+        mbl.set_zigzag_z(mbl_probe_index - 1, current_position.z);
114 114
         #if HAS_SOFTWARE_ENDSTOPS
115 115
           soft_endstops_enabled = saved_soft_endstops_state;
116 116
         #endif
@@ -124,11 +124,11 @@ void GcodeSuite::G29() {
124 124
         #endif
125 125
 
126 126
         mbl.zigzag(mbl_probe_index++, ix, iy);
127
-        _manual_goto_xy(mbl.index_to_xpos[ix], mbl.index_to_ypos[iy]);
127
+        _manual_goto_xy({ mbl.index_to_xpos[ix], mbl.index_to_ypos[iy] });
128 128
       }
129 129
       else {
130 130
         // One last "return to the bed" (as originally coded) at completion
131
-        current_position[Z_AXIS] = MANUAL_PROBE_HEIGHT;
131
+        current_position.z = MANUAL_PROBE_HEIGHT;
132 132
         line_to_current_position();
133 133
         planner.synchronize();
134 134
 
@@ -142,7 +142,7 @@ void GcodeSuite::G29() {
142 142
         set_bed_leveling_enabled(true);
143 143
 
144 144
         #if ENABLED(MESH_G28_REST_ORIGIN)
145
-          current_position[Z_AXIS] = 0;
145
+          current_position.z = 0;
146 146
           line_to_current_position(homing_feedrate(Z_AXIS));
147 147
           planner.synchronize();
148 148
         #endif

+ 8
- 11
Marlin/src/gcode/bedlevel/ubl/M421.cpp View File

@@ -46,28 +46,25 @@
46 46
  *   M421 C Q<offset>
47 47
  */
48 48
 void GcodeSuite::M421() {
49
-  int8_t ix = parser.intval('I', -1), iy = parser.intval('J', -1);
50
-  const bool hasI = ix >= 0,
51
-             hasJ = iy >= 0,
49
+  xy_int8_t ij = { int8_t(parser.intval('I', -1)), int8_t(parser.intval('J', -1)) };
50
+  const bool hasI = ij.x >= 0,
51
+             hasJ = ij.y >= 0,
52 52
              hasC = parser.seen('C'),
53 53
              hasN = parser.seen('N'),
54 54
              hasZ = parser.seen('Z'),
55 55
              hasQ = !hasZ && parser.seen('Q');
56 56
 
57
-  if (hasC) {
58
-    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);
59
-    ix = location.x_index;
60
-    iy = location.y_index;
61
-  }
57
+  if (hasC) ij = ubl.find_closest_mesh_point_of_type(REAL, current_position);
62 58
 
63 59
   if (int(hasC) + int(hasI && hasJ) != 1 || !(hasZ || hasQ || hasN))
64 60
     SERIAL_ERROR_MSG(MSG_ERR_M421_PARAMETERS);
65
-  else if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1))
61
+  else if (!WITHIN(ij.x, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(ij.y, 0, GRID_MAX_POINTS_Y - 1))
66 62
     SERIAL_ERROR_MSG(MSG_ERR_MESH_XY);
67 63
   else {
68
-    ubl.z_values[ix][iy] = hasN ? NAN : parser.value_linear_units() + (hasQ ? ubl.z_values[ix][iy] : 0);
64
+    float &zval = ubl.z_values[ij.x][ij.y];
65
+    zval = hasN ? NAN : parser.value_linear_units() + (hasQ ? zval : 0);
69 66
     #if ENABLED(EXTENSIBLE_UI)
70
-      ExtUI::onMeshUpdate(ix, iy, ubl.z_values[ix][iy]);
67
+      ExtUI::onMeshUpdate(ij.x, ij.y, zval);
71 68
     #endif
72 69
   }
73 70
 }

+ 22
- 28
Marlin/src/gcode/calibrate/G28.cpp View File

@@ -59,7 +59,7 @@
59 59
   static void quick_home_xy() {
60 60
 
61 61
     // Pretend the current position is 0,0
62
-    current_position[X_AXIS] = current_position[Y_AXIS] = 0.0;
62
+    current_position.set(0.0, 0.0);
63 63
     sync_plan_position();
64 64
 
65 65
     const int x_axis_home_dir =
@@ -95,7 +95,7 @@
95 95
 
96 96
     endstops.validate_homing_move();
97 97
 
98
-    current_position[X_AXIS] = current_position[Y_AXIS] = 0.0;
98
+    current_position.set(0.0, 0.0);
99 99
 
100 100
     #if ENABLED(SENSORLESS_HOMING)
101 101
       tmc_disable_stallguard(stepperX, stealth_states.x);
@@ -128,17 +128,15 @@
128 128
 
129 129
     /**
130 130
      * Move the Z probe (or just the nozzle) to the safe homing point
131
+     * (Z is already at the right height)
131 132
      */
132
-    destination[X_AXIS] = Z_SAFE_HOMING_X_POINT;
133
-    destination[Y_AXIS] = Z_SAFE_HOMING_Y_POINT;
134
-    destination[Z_AXIS] = current_position[Z_AXIS]; // Z is already at the right height
133
+    destination.set(safe_homing_xy, current_position.z);
135 134
 
136 135
     #if HOMING_Z_WITH_PROBE
137
-      destination[X_AXIS] -= probe_offset[X_AXIS];
138
-      destination[Y_AXIS] -= probe_offset[Y_AXIS];
136
+      destination -= probe_offset;
139 137
     #endif
140 138
 
141
-    if (position_is_reachable(destination[X_AXIS], destination[Y_AXIS])) {
139
+    if (position_is_reachable(destination)) {
142 140
 
143 141
       if (DEBUGGING(LEVELING)) DEBUG_POS("home_z_safely", destination);
144 142
 
@@ -151,7 +149,7 @@
151 149
         safe_delay(500); // Short delay needed to settle
152 150
       #endif
153 151
 
154
-      do_blocking_move_to_xy(destination[X_AXIS], destination[Y_AXIS]);
152
+      do_blocking_move_to_xy(destination);
155 153
       homeaxis(Z_AXIS);
156 154
     }
157 155
     else {
@@ -232,16 +230,14 @@ void GcodeSuite::G28(const bool always_home_all) {
232 230
   #endif
233 231
 
234 232
   #if ENABLED(IMPROVE_HOMING_RELIABILITY)
235
-    slow_homing_t slow_homing { 0 };
236
-    slow_homing.acceleration.x = planner.settings.max_acceleration_mm_per_s2[X_AXIS];
237
-    slow_homing.acceleration.y = planner.settings.max_acceleration_mm_per_s2[Y_AXIS];
233
+    slow_homing_t slow_homing{0};
234
+    slow_homing.acceleration.set(planner.settings.max_acceleration_mm_per_s2[X_AXIS];
235
+                                 planner.settings.max_acceleration_mm_per_s2[Y_AXIS]);
238 236
     planner.settings.max_acceleration_mm_per_s2[X_AXIS] = 100;
239 237
     planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = 100;
240 238
     #if HAS_CLASSIC_JERK
241
-      slow_homing.jerk.x = planner.max_jerk[X_AXIS];
242
-      slow_homing.jerk.y = planner.max_jerk[Y_AXIS];
243
-      planner.max_jerk[X_AXIS] = 0;
244
-      planner.max_jerk[Y_AXIS] = 0;
239
+      slow_homing.jerk_xy = planner.max_jerk;
240
+      planner.max_jerk.set(0, 0);
245 241
     #endif
246 242
 
247 243
     planner.reset_acceleration_rates();
@@ -274,7 +270,7 @@ void GcodeSuite::G28(const bool always_home_all) {
274 270
                home_all = always_home_all || (homeX == homeY && homeX == homeZ),
275 271
                doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ;
276 272
 
277
-    set_destination_from_current();
273
+    destination = current_position;
278 274
 
279 275
     #if Z_HOME_DIR > 0  // If homing away from BED do Z first
280 276
 
@@ -291,10 +287,10 @@ void GcodeSuite::G28(const bool always_home_all) {
291 287
 
292 288
     if (z_homing_height && (doX || doY)) {
293 289
       // Raise Z before homing any other axes and z is not already high enough (never lower z)
294
-      destination[Z_AXIS] = z_homing_height;
295
-      if (destination[Z_AXIS] > current_position[Z_AXIS]) {
296
-        if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) to ", destination[Z_AXIS]);
297
-        do_blocking_move_to_z(destination[Z_AXIS]);
290
+      destination.z = z_homing_height;
291
+      if (destination.z > current_position.z) {
292
+        if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) to ", destination.z);
293
+        do_blocking_move_to_z(destination.z);
298 294
       }
299 295
     }
300 296
 
@@ -329,14 +325,14 @@ void GcodeSuite::G28(const bool always_home_all) {
329 325
         homeaxis(X_AXIS);
330 326
 
331 327
         // Remember this extruder's position for later tool change
332
-        inactive_extruder_x_pos = current_position[X_AXIS];
328
+        inactive_extruder_x_pos = current_position.x;
333 329
 
334 330
         // Home the 1st (left) extruder
335 331
         active_extruder = 0;
336 332
         homeaxis(X_AXIS);
337 333
 
338 334
         // Consider the active extruder to be parked
339
-        COPY(raised_parked_position, current_position);
335
+        raised_parked_position = current_position;
340 336
         delayed_move_time = 0;
341 337
         active_extruder_parked = true;
342 338
 
@@ -390,14 +386,14 @@ void GcodeSuite::G28(const bool always_home_all) {
390 386
       homeaxis(X_AXIS);
391 387
 
392 388
       // Remember this extruder's position for later tool change
393
-      inactive_extruder_x_pos = current_position[X_AXIS];
389
+      inactive_extruder_x_pos = current_position.x;
394 390
 
395 391
       // Home the 1st (left) extruder
396 392
       active_extruder = 0;
397 393
       homeaxis(X_AXIS);
398 394
 
399 395
       // Consider the active extruder to be parked
400
-      COPY(raised_parked_position, current_position);
396
+      raised_parked_position = current_position;
401 397
       delayed_move_time = 0;
402 398
       active_extruder_parked = true;
403 399
       extruder_duplication_enabled = IDEX_saved_duplication_state;
@@ -441,10 +437,8 @@ void GcodeSuite::G28(const bool always_home_all) {
441 437
     planner.settings.max_acceleration_mm_per_s2[X_AXIS] = slow_homing.acceleration.x;
442 438
     planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = slow_homing.acceleration.y;
443 439
     #if HAS_CLASSIC_JERK
444
-      planner.max_jerk[X_AXIS] = slow_homing.jerk.x;
445
-      planner.max_jerk[Y_AXIS] = slow_homing.jerk.y;
440
+      planner.max_jerk = slow_homing.jerk_xy;
446 441
     #endif
447
-
448 442
     planner.reset_acceleration_rates();
449 443
   #endif
450 444
 

+ 72
- 94
Marlin/src/gcode/calibrate/G33.cpp View File

@@ -70,7 +70,7 @@ enum CalEnum : char {                        // the 7 main calibration points -
70 70
   #define AC_CLEANUP() ac_cleanup()
71 71
 #endif
72 72
 
73
-float lcd_probe_pt(const float &rx, const float &ry);
73
+float lcd_probe_pt(const xy_pos_t &xy);
74 74
 
75 75
 void ac_home() {
76 76
   endstops.enable(true);
@@ -122,9 +122,9 @@ void print_signed_float(PGM_P const prefix, const float &f) {
122 122
 static void print_calibration_settings(const bool end_stops, const bool tower_angles) {
123 123
   SERIAL_ECHOPAIR(".Height:", delta_height);
124 124
   if (end_stops) {
125
-    print_signed_float(PSTR("Ex"), delta_endstop_adj[A_AXIS]);
126
-    print_signed_float(PSTR("Ey"), delta_endstop_adj[B_AXIS]);
127
-    print_signed_float(PSTR("Ez"), delta_endstop_adj[C_AXIS]);
125
+    print_signed_float(PSTR("Ex"), delta_endstop_adj.a);
126
+    print_signed_float(PSTR("Ey"), delta_endstop_adj.b);
127
+    print_signed_float(PSTR("Ez"), delta_endstop_adj.c);
128 128
   }
129 129
   if (end_stops && tower_angles) {
130 130
     SERIAL_ECHOPAIR("  Radius:", delta_radius);
@@ -133,9 +133,9 @@ static void print_calibration_settings(const bool end_stops, const bool tower_an
133 133
     SERIAL_ECHO_SP(13);
134 134
   }
135 135
   if (tower_angles) {
136
-    print_signed_float(PSTR("Tx"), delta_tower_angle_trim[A_AXIS]);
137
-    print_signed_float(PSTR("Ty"), delta_tower_angle_trim[B_AXIS]);
138
-    print_signed_float(PSTR("Tz"), delta_tower_angle_trim[C_AXIS]);
136
+    print_signed_float(PSTR("Tx"), delta_tower_angle_trim.a);
137
+    print_signed_float(PSTR("Ty"), delta_tower_angle_trim.b);
138
+    print_signed_float(PSTR("Tz"), delta_tower_angle_trim.c);
139 139
   }
140 140
   if ((!end_stops && tower_angles) || (end_stops && !tower_angles)) { // XOR
141 141
     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
188 188
 /**
189 189
  *  - Probe a point
190 190
  */
191
-static float calibration_probe(const float &nx, const float &ny, const bool stow) {
191
+static float calibration_probe(const xy_pos_t &xy, const bool stow) {
192 192
   #if HAS_BED_PROBE
193
-    return probe_at_point(nx, ny, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, false);
193
+    return probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, false);
194 194
   #else
195 195
     UNUSED(stow);
196
-    return lcd_probe_pt(nx, ny);
196
+    return lcd_probe_pt(xy);
197 197
   #endif
198 198
 }
199 199
 
@@ -223,7 +223,8 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
223 223
   if (!_0p_calibration) {
224 224
 
225 225
     if (!_7p_no_intermediates && !_7p_4_intermediates && !_7p_11_intermediates) { // probe the center
226
-      z_pt[CEN] += calibration_probe(0, 0, stow_after_each);
226
+      const xy_pos_t center{0};
227
+      z_pt[CEN] += calibration_probe(center, stow_after_each);
227 228
       if (isnan(z_pt[CEN])) return false;
228 229
     }
229 230
 
@@ -233,7 +234,8 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
233 234
       I_LOOP_CAL_PT(rad, start, steps) {
234 235
         const float a = RADIANS(210 + (360 / NPP) *  (rad - 1)),
235 236
                     r = delta_calibration_radius * 0.1;
236
-        z_pt[CEN] += calibration_probe(cos(a) * r, sin(a) * r, stow_after_each);
237
+        const xy_pos_t vec = { cos(a), sin(a) };
238
+        z_pt[CEN] += calibration_probe(vec * r, stow_after_each);
237 239
         if (isnan(z_pt[CEN])) return false;
238 240
      }
239 241
       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
257 259
           const float a = RADIANS(210 + (360 / NPP) *  (rad - 1)),
258 260
                       r = delta_calibration_radius * (1 - 0.1 * (zig_zag ? offset - circle : circle)),
259 261
                       interpol = FMOD(rad, 1);
260
-          const float z_temp = calibration_probe(cos(a) * r, sin(a) * r, stow_after_each);
262
+          const xy_pos_t vec = { cos(a), sin(a) };
263
+          const float z_temp = calibration_probe(vec * r, stow_after_each);
261 264
           if (isnan(z_temp)) return false;
262 265
           // split probe point to neighbouring calibration points
263 266
           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
281 284
  *  - formulae for approximative forward kinematics in the end-stop displacement matrix
282 285
  *  - definition of the matrix scaling parameters
283 286
  */
284
-static void reverse_kinematics_probe_points(float z_pt[NPP + 1], float mm_at_pt_axis[NPP + 1][ABC]) {
285
-  float pos[XYZ] = { 0.0 };
287
+static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_at_pt_axis[NPP + 1]) {
288
+  xyz_pos_t pos{0};
286 289
 
287 290
   LOOP_CAL_ALL(rad) {
288 291
     const float a = RADIANS(210 + (360 / NPP) *  (rad - 1)),
289 292
                 r = (rad == CEN ? 0.0f : delta_calibration_radius);
290
-    pos[X_AXIS] = cos(a) * r;
291
-    pos[Y_AXIS] = sin(a) * r;
292
-    pos[Z_AXIS] = z_pt[rad];
293
+    pos.set(cos(a) * r, sin(a) * r, z_pt[rad]);
293 294
     inverse_kinematics(pos);
294
-    LOOP_XYZ(axis) mm_at_pt_axis[rad][axis] = delta[axis];
295
+    mm_at_pt_axis[rad] = delta;
295 296
   }
296 297
 }
297 298
 
298
-static void forward_kinematics_probe_points(float mm_at_pt_axis[NPP + 1][ABC], float z_pt[NPP + 1]) {
299
+static void forward_kinematics_probe_points(abc_float_t mm_at_pt_axis[NPP + 1], float z_pt[NPP + 1]) {
299 300
   const float r_quot = delta_calibration_radius / delta_radius;
300 301
 
301
-  #define ZPP(N,I,A) ((1 / 3.0f + r_quot * (N) / 3.0f ) * mm_at_pt_axis[I][A])
302
+  #define ZPP(N,I,A) (((1.0f + r_quot * (N)) / 3.0f) * mm_at_pt_axis[I].A)
302 303
   #define Z00(I, A) ZPP( 0, I, A)
303 304
   #define Zp1(I, A) ZPP(+1, I, A)
304 305
   #define Zm1(I, A) ZPP(-1, I, A)
305 306
   #define Zp2(I, A) ZPP(+2, I, A)
306 307
   #define Zm2(I, A) ZPP(-2, I, A)
307 308
 
308
-  z_pt[CEN] = Z00(CEN, A_AXIS) + Z00(CEN, B_AXIS) + Z00(CEN, C_AXIS);
309
-  z_pt[__A] = Zp2(__A, A_AXIS) + Zm1(__A, B_AXIS) + Zm1(__A, C_AXIS);
310
-  z_pt[__B] = Zm1(__B, A_AXIS) + Zp2(__B, B_AXIS) + Zm1(__B, C_AXIS);
311
-  z_pt[__C] = Zm1(__C, A_AXIS) + Zm1(__C, B_AXIS) + Zp2(__C, C_AXIS);
312
-  z_pt[_BC] = Zm2(_BC, A_AXIS) + Zp1(_BC, B_AXIS) + Zp1(_BC, C_AXIS);
313
-  z_pt[_CA] = Zp1(_CA, A_AXIS) + Zm2(_CA, B_AXIS) + Zp1(_CA, C_AXIS);
314
-  z_pt[_AB] = Zp1(_AB, A_AXIS) + Zp1(_AB, B_AXIS) + Zm2(_AB, C_AXIS);
309
+  z_pt[CEN] = Z00(CEN, a) + Z00(CEN, b) + Z00(CEN, c);
310
+  z_pt[__A] = Zp2(__A, a) + Zm1(__A, b) + Zm1(__A, c);
311
+  z_pt[__B] = Zm1(__B, a) + Zp2(__B, b) + Zm1(__B, c);
312
+  z_pt[__C] = Zm1(__C, a) + Zm1(__C, b) + Zp2(__C, c);
313
+  z_pt[_BC] = Zm2(_BC, a) + Zp1(_BC, b) + Zp1(_BC, c);
314
+  z_pt[_CA] = Zp1(_CA, a) + Zm2(_CA, b) + Zp1(_CA, c);
315
+  z_pt[_AB] = Zp1(_AB, a) + Zp1(_AB, b) + Zm2(_AB, c);
315 316
 }
316 317
 
317
-static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], float delta_e[ABC], float delta_r, float delta_t[ABC]) {
318
+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) {
318 319
   const float z_center = z_pt[CEN];
319
-  float diff_mm_at_pt_axis[NPP + 1][ABC],
320
-        new_mm_at_pt_axis[NPP + 1][ABC];
320
+  abc_float_t diff_mm_at_pt_axis[NPP + 1], new_mm_at_pt_axis[NPP + 1];
321 321
 
322 322
   reverse_kinematics_probe_points(z_pt, diff_mm_at_pt_axis);
323 323
 
324 324
   delta_radius += delta_r;
325
-  LOOP_XYZ(axis) delta_tower_angle_trim[axis] += delta_t[axis];
325
+  delta_tower_angle_trim += delta_t;
326 326
   recalc_delta_settings();
327 327
   reverse_kinematics_probe_points(z_pt, new_mm_at_pt_axis);
328 328
 
329
-  LOOP_XYZ(axis) LOOP_CAL_ALL(rad) diff_mm_at_pt_axis[rad][axis] -= new_mm_at_pt_axis[rad][axis] + delta_e[axis];
329
+  LOOP_CAL_ALL(rad) diff_mm_at_pt_axis[rad] -= new_mm_at_pt_axis[rad] + delta_e;
330 330
   forward_kinematics_probe_points(diff_mm_at_pt_axis, z_pt);
331 331
 
332 332
   LOOP_CAL_RAD(rad) z_pt[rad] -= z_pt[CEN] - z_center;
333 333
   z_pt[CEN] = z_center;
334 334
 
335 335
   delta_radius -= delta_r;
336
-  LOOP_XYZ(axis) delta_tower_angle_trim[axis] -= delta_t[axis];
336
+  delta_tower_angle_trim -= delta_t;
337 337
   recalc_delta_settings();
338 338
 }
339 339
 
340 340
 static float auto_tune_h() {
341 341
   const float r_quot = delta_calibration_radius / delta_radius;
342
-  float h_fac = 0.0f;
343
-
344
-  h_fac = r_quot / (2.0f / 3.0f);
345
-  h_fac = 1.0f / h_fac; // (2/3)/CR
346
-  return h_fac;
342
+  return RECIPROCAL(r_quot / (2.0f / 3.0f));  // (2/3)/CR
347 343
 }
348 344
 
349 345
 static float auto_tune_r() {
350
-  const float diff = 0.01f;
351
-  float r_fac = 0.0f,
352
-        z_pt[NPP + 1] = { 0.0f },
353
-        delta_e[ABC] = { 0.0f },
354
-        delta_r = { 0.0f },
355
-        delta_t[ABC] = { 0.0f };
356
-
357
-  delta_r = diff;
346
+  constexpr float diff = 0.01f, delta_r = diff;
347
+  float r_fac = 0.0f, z_pt[NPP + 1] = { 0.0f };
348
+  abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f };
349
+
358 350
   calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t);
359 351
   r_fac = -(z_pt[__A] + z_pt[__B] + z_pt[__C] + z_pt[_BC] + z_pt[_CA] + z_pt[_AB]) / 6.0f;
360 352
   r_fac = diff / r_fac / 3.0f; // 1/(3*delta_Z)
@@ -362,14 +354,11 @@ static float auto_tune_r() {
362 354
 }
363 355
 
364 356
 static float auto_tune_a() {
365
-  const float diff = 0.01f;
366
-  float a_fac = 0.0f,
367
-        z_pt[NPP + 1] = { 0.0f },
368
-        delta_e[ABC] = { 0.0f },
369
-        delta_r = { 0.0f },
370
-        delta_t[ABC] = { 0.0f };
371
-
372
-  ZERO(delta_t);
357
+  constexpr float diff = 0.01f, delta_r = 0.0f;
358
+  float a_fac = 0.0f, z_pt[NPP + 1] = { 0.0f };
359
+  abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f };
360
+
361
+  delta_t.reset();
373 362
   LOOP_XYZ(axis) {
374 363
     delta_t[axis] = diff;
375 364
     calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t);
@@ -453,21 +442,11 @@ void GcodeSuite::G33() {
453 442
         zero_std_dev = (verbose_level ? 999.0f : 0.0f), // 0.0 in dry-run mode : forced end
454 443
         zero_std_dev_min = zero_std_dev,
455 444
         zero_std_dev_old = zero_std_dev,
456
-        h_factor,
457
-        r_factor,
458
-        a_factor,
459
-        e_old[ABC] = {
460
-          delta_endstop_adj[A_AXIS],
461
-          delta_endstop_adj[B_AXIS],
462
-          delta_endstop_adj[C_AXIS]
463
-        },
445
+        h_factor, r_factor, a_factor,
464 446
         r_old = delta_radius,
465
-        h_old = delta_height,
466
-        a_old[ABC] = {
467
-          delta_tower_angle_trim[A_AXIS],
468
-          delta_tower_angle_trim[B_AXIS],
469
-          delta_tower_angle_trim[C_AXIS]
470
-        };
447
+        h_old = delta_height;
448
+
449
+  abc_pos_t e_old = delta_endstop_adj, a_old = delta_tower_angle_trim;
471 450
 
472 451
   SERIAL_ECHOLNPGM("G33 Auto Calibrate");
473 452
 
@@ -520,15 +499,14 @@ void GcodeSuite::G33() {
520 499
 
521 500
       if (zero_std_dev < zero_std_dev_min) {
522 501
         // set roll-back point
523
-        COPY(e_old, delta_endstop_adj);
502
+        e_old = delta_endstop_adj;
524 503
         r_old = delta_radius;
525 504
         h_old = delta_height;
526
-        COPY(a_old, delta_tower_angle_trim);
505
+        a_old = delta_tower_angle_trim;
527 506
       }
528 507
 
529
-      float e_delta[ABC] = { 0.0f },
530
-            r_delta = 0.0f,
531
-            t_delta[ABC] = { 0.0f };
508
+      abc_float_t e_delta = { 0.0f }, t_delta = { 0.0f };
509
+      float r_delta = 0.0f;
532 510
 
533 511
       /**
534 512
        * convergence matrices:
@@ -563,42 +541,42 @@ void GcodeSuite::G33() {
563 541
 
564 542
         case 2:
565 543
           if (towers_set) { // see 4 point calibration (towers) matrix
566
-            e_delta[A_AXIS] = (+Z4(__A) -Z2(__B) -Z2(__C)) * h_factor  +Z4(CEN);
567
-            e_delta[B_AXIS] = (-Z2(__A) +Z4(__B) -Z2(__C)) * h_factor  +Z4(CEN);
568
-            e_delta[C_AXIS] = (-Z2(__A) -Z2(__B) +Z4(__C)) * h_factor  +Z4(CEN);
569
-            r_delta         = (+Z4(__A) +Z4(__B) +Z4(__C) -Z12(CEN)) * r_factor;
544
+            e_delta.set((+Z4(__A) -Z2(__B) -Z2(__C)) * h_factor  +Z4(CEN),
545
+                        (-Z2(__A) +Z4(__B) -Z2(__C)) * h_factor  +Z4(CEN),
546
+                        (-Z2(__A) -Z2(__B) +Z4(__C)) * h_factor  +Z4(CEN));
547
+            r_delta   = (+Z4(__A) +Z4(__B) +Z4(__C) -Z12(CEN)) * r_factor;
570 548
           }
571 549
           else { // see 4 point calibration (opposites) matrix
572
-            e_delta[A_AXIS] = (-Z4(_BC) +Z2(_CA) +Z2(_AB)) * h_factor  +Z4(CEN);
573
-            e_delta[B_AXIS] = (+Z2(_BC) -Z4(_CA) +Z2(_AB)) * h_factor  +Z4(CEN);
574
-            e_delta[C_AXIS] = (+Z2(_BC) +Z2(_CA) -Z4(_AB)) * h_factor  +Z4(CEN);
575
-            r_delta         = (+Z4(_BC) +Z4(_CA) +Z4(_AB) -Z12(CEN)) * r_factor;
550
+            e_delta.set((-Z4(_BC) +Z2(_CA) +Z2(_AB)) * h_factor  +Z4(CEN),
551
+                        (+Z2(_BC) -Z4(_CA) +Z2(_AB)) * h_factor  +Z4(CEN),
552
+                        (+Z2(_BC) +Z2(_CA) -Z4(_AB)) * h_factor  +Z4(CEN));
553
+            r_delta   = (+Z4(_BC) +Z4(_CA) +Z4(_AB) -Z12(CEN)) * r_factor;
576 554
           }
577 555
           break;
578 556
 
579 557
         default: // see 7 point calibration (towers & opposites) matrix
580
-          e_delta[A_AXIS] = (+Z2(__A) -Z1(__B) -Z1(__C) -Z2(_BC) +Z1(_CA) +Z1(_AB)) * h_factor  +Z4(CEN);
581
-          e_delta[B_AXIS] = (-Z1(__A) +Z2(__B) -Z1(__C) +Z1(_BC) -Z2(_CA) +Z1(_AB)) * h_factor  +Z4(CEN);
582
-          e_delta[C_AXIS] = (-Z1(__A) -Z1(__B) +Z2(__C) +Z1(_BC) +Z1(_CA) -Z2(_AB)) * h_factor  +Z4(CEN);
583
-          r_delta         = (+Z2(__A) +Z2(__B) +Z2(__C) +Z2(_BC) +Z2(_CA) +Z2(_AB) -Z12(CEN)) * r_factor;
558
+          e_delta.set((+Z2(__A) -Z1(__B) -Z1(__C) -Z2(_BC) +Z1(_CA) +Z1(_AB)) * h_factor  +Z4(CEN),
559
+                      (-Z1(__A) +Z2(__B) -Z1(__C) +Z1(_BC) -Z2(_CA) +Z1(_AB)) * h_factor  +Z4(CEN),
560
+                      (-Z1(__A) -Z1(__B) +Z2(__C) +Z1(_BC) +Z1(_CA) -Z2(_AB)) * h_factor  +Z4(CEN));
561
+          r_delta   = (+Z2(__A) +Z2(__B) +Z2(__C) +Z2(_BC) +Z2(_CA) +Z2(_AB) -Z12(CEN)) * r_factor;
584 562
 
585 563
           if (towers_set) { // see 7 point tower angle calibration (towers & opposites) matrix
586
-            t_delta[A_AXIS] = (+Z0(__A) -Z4(__B) +Z4(__C) +Z0(_BC) -Z4(_CA) +Z4(_AB) +Z0(CEN)) * a_factor;
587
-            t_delta[B_AXIS] = (+Z4(__A) +Z0(__B) -Z4(__C) +Z4(_BC) +Z0(_CA) -Z4(_AB) +Z0(CEN)) * a_factor;
588
-            t_delta[C_AXIS] = (-Z4(__A) +Z4(__B) +Z0(__C) -Z4(_BC) +Z4(_CA) +Z0(_AB) +Z0(CEN)) * a_factor;
564
+            t_delta.set((+Z0(__A) -Z4(__B) +Z4(__C) +Z0(_BC) -Z4(_CA) +Z4(_AB) +Z0(CEN)) * a_factor,
565
+                        (+Z4(__A) +Z0(__B) -Z4(__C) +Z4(_BC) +Z0(_CA) -Z4(_AB) +Z0(CEN)) * a_factor,
566
+                        (-Z4(__A) +Z4(__B) +Z0(__C) -Z4(_BC) +Z4(_CA) +Z0(_AB) +Z0(CEN)) * a_factor);
589 567
           }
590 568
           break;
591 569
       }
592
-      LOOP_XYZ(axis) delta_endstop_adj[axis] += e_delta[axis];
570
+      delta_endstop_adj += e_delta;
593 571
       delta_radius += r_delta;
594
-      LOOP_XYZ(axis) delta_tower_angle_trim[axis] += t_delta[axis];
572
+      delta_tower_angle_trim += t_delta;
595 573
     }
596 574
     else if (zero_std_dev >= test_precision) {
597 575
       // roll back
598
-      COPY(delta_endstop_adj, e_old);
576
+      delta_endstop_adj = e_old;
599 577
       delta_radius = r_old;
600 578
       delta_height = h_old;
601
-      COPY(delta_tower_angle_trim, a_old);
579
+      delta_tower_angle_trim = a_old;
602 580
     }
603 581
 
604 582
     if (verbose_level != 0) {                                    // !dry run
@@ -611,7 +589,7 @@ void GcodeSuite::G33() {
611 589
       }
612 590
 
613 591
       // adjust delta_height and endstops by the max amount
614
-      const float z_temp = _MAX(delta_endstop_adj[A_AXIS], delta_endstop_adj[B_AXIS], delta_endstop_adj[C_AXIS]);
592
+      const float z_temp = _MAX(delta_endstop_adj.a, delta_endstop_adj.b, delta_endstop_adj.c);
615 593
       delta_height -= z_temp;
616 594
       LOOP_XYZ(axis) delta_endstop_adj[axis] -= z_temp;
617 595
     }

+ 26
- 15
Marlin/src/gcode/calibrate/G34_M422.cpp View File

@@ -45,8 +45,17 @@
45 45
 #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
46 46
 #include "../../core/debug_out.h"
47 47
 
48
-float z_auto_align_xpos[Z_STEPPER_COUNT] = Z_STEPPER_ALIGN_X,
49
-      z_auto_align_ypos[Z_STEPPER_COUNT] = Z_STEPPER_ALIGN_Y;
48
+// Sanity-check
49
+constexpr xy_pos_t sanity_arr_z_align[] = Z_STEPPER_ALIGN_XY;
50
+static_assert(COUNT(sanity_arr_z_align) == Z_STEPPER_COUNT,
51
+  #if ENABLED(Z_TRIPLE_STEPPER_DRIVERS)
52
+    "Z_STEPPER_ALIGN_XY requires three {X,Y} entries (Z, Z2, and Z3)."
53
+  #else
54
+    "Z_STEPPER_ALIGN_XY requires two {X,Y} entries (Z and Z2)."
55
+  #endif
56
+);
57
+
58
+xy_pos_t z_auto_align_pos[Z_STEPPER_COUNT] = Z_STEPPER_ALIGN_XY;
50 59
 
51 60
 inline void set_all_z_lock(const bool lock) {
52 61
   stepper.set_z_lock(lock);
@@ -123,11 +132,11 @@ void GcodeSuite::G34() {
123 132
 
124 133
     float z_probe = Z_BASIC_CLEARANCE + (G34_MAX_GRADE) * 0.01f * (
125 134
       #if ENABLED(Z_TRIPLE_STEPPER_DRIVERS)
126
-         SQRT(_MAX(HYPOT2(z_auto_align_xpos[0] - z_auto_align_ypos[0], z_auto_align_xpos[1] - z_auto_align_ypos[1]),
127
-                  HYPOT2(z_auto_align_xpos[1] - z_auto_align_ypos[1], z_auto_align_xpos[2] - z_auto_align_ypos[2]),
128
-                  HYPOT2(z_auto_align_xpos[2] - z_auto_align_ypos[2], z_auto_align_xpos[0] - z_auto_align_ypos[0])))
135
+         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),
136
+                  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),
137
+                  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)))
129 138
       #else
130
-         HYPOT(z_auto_align_xpos[0] - z_auto_align_ypos[0], z_auto_align_xpos[1] - z_auto_align_ypos[1])
139
+         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)
131 140
       #endif
132 141
     );
133 142
 
@@ -135,7 +144,7 @@ void GcodeSuite::G34() {
135 144
     if (!all_axes_known()) home_all_axes();
136 145
 
137 146
     // Move the Z coordinate realm towards the positive - dirty trick
138
-    current_position[Z_AXIS] -= z_probe * 0.5;
147
+    current_position.z -= z_probe * 0.5f;
139 148
 
140 149
     float last_z_align_move[Z_STEPPER_COUNT] = ARRAY_N(Z_STEPPER_COUNT, 10000.0f, 10000.0f, 10000.0f),
141 150
           z_measured[Z_STEPPER_COUNT] = { 0 },
@@ -162,7 +171,7 @@ void GcodeSuite::G34() {
162 171
         if (iteration == 0 || izstepper > 0) do_blocking_move_to_z(z_probe);
163 172
 
164 173
         // Probe a Z height for each stepper.
165
-        const float z_probed_height = probe_at_point(z_auto_align_xpos[zstepper], z_auto_align_ypos[zstepper], raise_after, 0, true);
174
+        const float z_probed_height = probe_at_point(z_auto_align_pos[zstepper], raise_after, 0, true);
166 175
         if (isnan(z_probed_height)) {
167 176
           SERIAL_ECHOLNPGM("Probing failed.");
168 177
           err_break = true;
@@ -240,7 +249,7 @@ void GcodeSuite::G34() {
240 249
         }
241 250
 
242 251
         // Do a move to correct part of the misalignment for the current stepper
243
-        do_blocking_move_to_z(amplification * z_align_move + current_position[Z_AXIS]);
252
+        do_blocking_move_to_z(amplification * z_align_move + current_position.z);
244 253
       } // for (zstepper)
245 254
 
246 255
       // Back to normal stepper operations
@@ -299,20 +308,22 @@ void GcodeSuite::M422() {
299 308
     return;
300 309
   }
301 310
 
302
-  const float x_pos = parser.floatval('X', z_auto_align_xpos[zstepper]);
303
-  if (!WITHIN(x_pos, X_MIN_POS, X_MAX_POS)) {
311
+  const xy_pos_t pos = {
312
+    parser.floatval('X', z_auto_align_pos[zstepper].x),
313
+    parser.floatval('Y', z_auto_align_pos[zstepper].y)
314
+  };
315
+
316
+  if (!WITHIN(pos.x, X_MIN_POS, X_MAX_POS)) {
304 317
     SERIAL_ECHOLNPGM("?(X) out of bounds.");
305 318
     return;
306 319
   }
307 320
 
308
-  const float y_pos = parser.floatval('Y', z_auto_align_ypos[zstepper]);
309
-  if (!WITHIN(y_pos, Y_MIN_POS, Y_MAX_POS)) {
321
+  if (!WITHIN(pos.y, Y_MIN_POS, Y_MAX_POS)) {
310 322
     SERIAL_ECHOLNPGM("?(Y) out of bounds.");
311 323
     return;
312 324
   }
313 325
 
314
-  z_auto_align_xpos[zstepper] = x_pos;
315
-  z_auto_align_ypos[zstepper] = y_pos;
326
+  z_auto_align_pos[zstepper] = pos;
316 327
 }
317 328
 
318 329
 #endif // Z_STEPPER_AUTO_ALIGN

+ 62
- 92
Marlin/src/gcode/calibrate/G425.cpp View File

@@ -61,17 +61,17 @@
61 61
 
62 62
 enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES };
63 63
 
64
-struct measurements_t {
65
-  static constexpr float dimensions[XYZ] = CALIBRATION_OBJECT_DIMENSIONS;
66
-  static constexpr float true_center[XYZ] = CALIBRATION_OBJECT_CENTER;
64
+static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER;
65
+static constexpr xyz_float_t dimensions CALIBRATION_OBJECT_DIMENSIONS;
66
+static constexpr xy_float_t nod = { CALIBRATION_NOZZLE_OUTER_DIAMETER, CALIBRATION_NOZZLE_OUTER_DIAMETER };
67 67
 
68
-  float obj_center[XYZ] = CALIBRATION_OBJECT_CENTER;
69
-  float obj_side[NUM_SIDES];
68
+struct measurements_t {
69
+  xyz_pos_t obj_center = true_center; // Non-static must be assigned from xyz_pos_t
70 70
 
71
-  float backlash[NUM_SIDES];
72
-  float pos_error[XYZ];
71
+  float obj_side[NUM_SIDES], backlash[NUM_SIDES];
72
+  xyz_float_t pos_error;
73 73
 
74
-  float nozzle_outer_dimension[2] = {CALIBRATION_NOZZLE_OUTER_DIAMETER, CALIBRATION_NOZZLE_OUTER_DIAMETER};
74
+  xy_float_t nozzle_outer_dimension = nod;
75 75
 };
76 76
 
77 77
 #define TEMPORARY_SOFT_ENDSTOP_STATE(enable) REMEMBER(tes, soft_endstops_enabled, enable);
@@ -88,29 +88,8 @@ struct measurements_t {
88 88
   #define TEMPORARY_BACKLASH_SMOOTHING(value)
89 89
 #endif
90 90
 
91
-/**
92
- * Move to a particular location. Up to three individual axes
93
- * and their destinations can be specified, in any order.
94
- */
95
-inline void move_to(
96
-  const AxisEnum a1 = NO_AXIS, const float p1 = 0,
97
-  const AxisEnum a2 = NO_AXIS, const float p2 = 0,
98
-  const AxisEnum a3 = NO_AXIS, const float p3 = 0
99
-) {
100
-  set_destination_from_current();
101
-
102
-  // Note: The order of p1, p2, p3 may not correspond to X, Y, Z
103
-  if (a1 != NO_AXIS) destination[a1] = p1;
104
-  if (a2 != NO_AXIS) destination[a2] = p2;
105
-  if (a3 != NO_AXIS) destination[a3] = p3;
106
-
107
-  // Make sure coordinates are within bounds
108
-  destination[X_AXIS] = _MAX(_MIN(destination[X_AXIS], X_MAX_POS), X_MIN_POS);
109
-  destination[Y_AXIS] = _MAX(_MIN(destination[Y_AXIS], Y_MAX_POS), Y_MIN_POS);
110
-  destination[Z_AXIS] = _MAX(_MIN(destination[Z_AXIS], Z_MAX_POS), Z_MIN_POS);
111
-
112
-  // Move to position
113
-  do_blocking_move_to(destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
91
+inline void calibration_move() {
92
+  do_blocking_move_to(current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
114 93
 }
115 94
 
116 95
 /**
@@ -121,10 +100,12 @@ inline void move_to(
121 100
  */
122 101
 inline void park_above_object(measurements_t &m, const float uncertainty) {
123 102
   // Move to safe distance above calibration object
124
-  move_to(Z_AXIS, m.obj_center[Z_AXIS] + m.dimensions[Z_AXIS] / 2 + uncertainty);
103
+  current_position.z = m.obj_center.z + dimensions.z / 2 + uncertainty;
104
+  calibration_move();
125 105
 
126 106
   // Move to center of calibration object in XY
127
-  move_to(X_AXIS, m.obj_center[X_AXIS], Y_AXIS, m.obj_center[Y_AXIS]);
107
+  current_position = xy_pos_t(m.obj_center);
108
+  calibration_move();
128 109
 }
129 110
 
130 111
 #if HOTENDS > 1
@@ -139,14 +120,9 @@ inline void park_above_object(measurements_t &m, const float uncertainty) {
139 120
 #if HAS_HOTEND_OFFSET
140 121
 
141 122
   inline void normalize_hotend_offsets() {
142
-    for (uint8_t e = 1; e < HOTENDS; e++) {
143
-      hotend_offset[X_AXIS][e] -= hotend_offset[X_AXIS][0];
144
-      hotend_offset[Y_AXIS][e] -= hotend_offset[Y_AXIS][0];
145
-      hotend_offset[Z_AXIS][e] -= hotend_offset[Z_AXIS][0];
146
-    }
147
-    hotend_offset[X_AXIS][0] = 0;
148
-    hotend_offset[Y_AXIS][0] = 0;
149
-    hotend_offset[Z_AXIS][0] = 0;
123
+    for (uint8_t e = 1; e < HOTENDS; e++)
124
+      hotend_offset[e] -= hotend_offset[0];
125
+    hotend_offset[0].reset();
150 126
   }
151 127
 
152 128
 #endif
@@ -175,7 +151,7 @@ float measuring_movement(const AxisEnum axis, const int dir, const bool stop_sta
175 151
   const feedRate_t mms = fast ? MMM_TO_MMS(CALIBRATION_FEEDRATE_FAST) : MMM_TO_MMS(CALIBRATION_FEEDRATE_SLOW);
176 152
   const float limit    = fast ? 50 : 5;
177 153
 
178
-  set_destination_from_current();
154
+  destination = current_position;
179 155
   for (float travel = 0; travel < limit; travel += step) {
180 156
     destination[axis] += dir * step;
181 157
     do_blocking_move_to(destination, mms);
@@ -199,7 +175,7 @@ inline float measure(const AxisEnum axis, const int dir, const bool stop_state,
199 175
   const bool fast = uncertainty == CALIBRATION_MEASUREMENT_UNKNOWN;
200 176
 
201 177
   // Save position
202
-  set_destination_from_current();
178
+  destination = current_position;
203 179
   const float start_pos = destination[axis];
204 180
   const float measured_pos = measuring_movement(axis, dir, stop_state, fast);
205 181
   // Measure backlash
@@ -223,7 +199,7 @@ inline float measure(const AxisEnum axis, const int dir, const bool stop_state,
223 199
  *                               to find out height of edge
224 200
  */
225 201
 inline void probe_side(measurements_t &m, const float uncertainty, const side_t side, const bool probe_top_at_edge=false) {
226
-  const float dimensions[]  = CALIBRATION_OBJECT_DIMENSIONS;
202
+  const xyz_float_t dimensions = CALIBRATION_OBJECT_DIMENSIONS;
227 203
   AxisEnum axis;
228 204
   float dir;
229 205
 
@@ -232,7 +208,7 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
232 208
   switch (side) {
233 209
     case TOP: {
234 210
       const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
235
-      m.obj_center[Z_AXIS] = measurement - dimensions[Z_AXIS] / 2;
211
+      m.obj_center.z = measurement - dimensions.z / 2;
236 212
       m.obj_side[TOP] = measurement;
237 213
       return;
238 214
     }
@@ -240,22 +216,24 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
240 216
     case FRONT: axis = Y_AXIS; dir =  1; break;
241 217
     case LEFT:  axis = X_AXIS; dir =  1; break;
242 218
     case BACK:  axis = Y_AXIS; dir = -1; break;
243
-    default:
244
-      return;
219
+    default: return;
245 220
   }
246 221
 
247 222
   if (probe_top_at_edge) {
248 223
     // Probe top nearest the side we are probing
249
-    move_to(axis, m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 - m.nozzle_outer_dimension[axis]));
224
+    current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 - m.nozzle_outer_dimension[axis]);
225
+    calibration_move();
250 226
     m.obj_side[TOP] = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
251
-    m.obj_center[Z_AXIS] = m.obj_side[TOP] - dimensions[Z_AXIS] / 2;
227
+    m.obj_center.z = m.obj_side[TOP] - dimensions.z / 2;
252 228
   }
253 229
 
254 230
   // Move to safe distance to the side of the calibration object
255
-  move_to(axis, m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty));
231
+  current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty);
232
+  calibration_move();
256 233
 
257 234
   // Plunge below the side of the calibration object and measure
258
-  move_to(Z_AXIS, m.obj_side[TOP] - CALIBRATION_NOZZLE_TIP_HEIGHT * 0.7);
235
+  current_position.z = m.obj_side[TOP] - CALIBRATION_NOZZLE_TIP_HEIGHT * 0.7;
236
+  calibration_move();
259 237
   const float measurement = measure(axis, dir, true, &m.backlash[side], uncertainty);
260 238
   m.obj_center[axis] = measurement + dir * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2);
261 239
   m.obj_side[side] = measurement;
@@ -294,36 +272,36 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
294 272
 
295 273
   // Compute the measured center of the calibration object.
296 274
   #if HAS_X_CENTER
297
-    m.obj_center[X_AXIS] = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2;
275
+    m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2;
298 276
   #endif
299 277
   #if HAS_Y_CENTER
300
-    m.obj_center[Y_AXIS] = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2;
278
+    m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2;
301 279
   #endif
302 280
 
303 281
   // Compute the outside diameter of the nozzle at the height
304 282
   // at which it makes contact with the calibration object
305 283
   #if HAS_X_CENTER
306
-    m.nozzle_outer_dimension[X_AXIS] = m.obj_side[RIGHT] - m.obj_side[LEFT] - m.dimensions[X_AXIS];
284
+    m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x;
307 285
   #endif
308 286
   #if HAS_Y_CENTER
309
-    m.nozzle_outer_dimension[Y_AXIS] = m.obj_side[BACK]  - m.obj_side[FRONT] - m.dimensions[Y_AXIS];
287
+    m.nozzle_outer_dimension.y = m.obj_side[BACK]  - m.obj_side[FRONT] - dimensions.y;
310 288
   #endif
311 289
 
312 290
   park_above_object(m, uncertainty);
313 291
 
314 292
   // The difference between the known and the measured location
315 293
   // of the calibration object is the positional error
316
-  m.pos_error[X_AXIS] = (0
294
+  m.pos_error.x = (0
317 295
     #if HAS_X_CENTER
318
-      + m.true_center[X_AXIS] - m.obj_center[X_AXIS]
296
+      + true_center.x - m.obj_center.x
319 297
     #endif
320 298
   );
321
-  m.pos_error[Y_AXIS] = (0
299
+  m.pos_error.y = (0
322 300
     #if HAS_Y_CENTER
323
-      + m.true_center[Y_AXIS] - m.obj_center[Y_AXIS]
301
+      + true_center.y - m.obj_center.y
324 302
     #endif
325 303
   );
326
-  m.pos_error[Z_AXIS] = m.true_center[Z_AXIS] - m.obj_center[Z_AXIS];
304
+  m.pos_error.z = true_center.z - m.obj_center.z;
327 305
 }
328 306
 
329 307
 #if ENABLED(CALIBRATION_REPORTING)
@@ -348,12 +326,12 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
348 326
   inline void report_measured_center(const measurements_t &m) {
349 327
     SERIAL_ECHOLNPGM("Center:");
350 328
     #if HAS_X_CENTER
351
-      SERIAL_ECHOLNPAIR(" X", m.obj_center[X_AXIS]);
329
+      SERIAL_ECHOLNPAIR(" X", m.obj_center.x);
352 330
     #endif
353 331
     #if HAS_Y_CENTER
354
-      SERIAL_ECHOLNPAIR(" Y", m.obj_center[Y_AXIS]);
332
+      SERIAL_ECHOLNPAIR(" Y", m.obj_center.y);
355 333
     #endif
356
-    SERIAL_ECHOLNPAIR(" Z", m.obj_center[Z_AXIS]);
334
+    SERIAL_ECHOLNPAIR(" Z", m.obj_center.z);
357 335
     SERIAL_EOL();
358 336
   }
359 337
 
@@ -380,12 +358,12 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
380 358
     SERIAL_ECHO(int(active_extruder));
381 359
     SERIAL_ECHOLNPGM(" Positional Error:");
382 360
     #if HAS_X_CENTER
383
-      SERIAL_ECHOLNPAIR(" X", m.pos_error[X_AXIS]);
361
+      SERIAL_ECHOLNPAIR(" X", m.pos_error.x);
384 362
     #endif
385 363
     #if HAS_Y_CENTER
386
-      SERIAL_ECHOLNPAIR(" Y", m.pos_error[Y_AXIS]);
364
+      SERIAL_ECHOLNPAIR(" Y", m.pos_error.y);
387 365
     #endif
388
-    SERIAL_ECHOLNPAIR(" Z", m.pos_error[Z_AXIS]);
366
+    SERIAL_ECHOLNPAIR(" Z", m.pos_error.z);
389 367
     SERIAL_EOL();
390 368
   }
391 369
 
@@ -393,10 +371,10 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
393 371
     SERIAL_ECHOLNPGM("Nozzle Tip Outer Dimensions:");
394 372
     #if HAS_X_CENTER || HAS_Y_CENTER
395 373
       #if HAS_X_CENTER
396
-        SERIAL_ECHOLNPAIR(" X", m.nozzle_outer_dimension[X_AXIS]);
374
+        SERIAL_ECHOLNPAIR(" X", m.nozzle_outer_dimension.x);
397 375
       #endif
398 376
       #if HAS_Y_CENTER
399
-        SERIAL_ECHOLNPAIR(" Y", m.nozzle_outer_dimension[Y_AXIS]);
377
+        SERIAL_ECHOLNPAIR(" Y", m.nozzle_outer_dimension.y);
400 378
       #endif
401 379
     #else
402 380
       UNUSED(m);
@@ -410,7 +388,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
410 388
     //
411 389
     inline void report_hotend_offsets() {
412 390
       for (uint8_t e = 1; e < HOTENDS; e++)
413
-        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]);
391
+        SERIAL_ECHOLNPAIR("T", int(e), " Hotend Offset X", hotend_offset[e].x, " Y", hotend_offset[e].y, " Z", hotend_offset[e].z);
414 392
     }
415 393
   #endif
416 394
 
@@ -434,49 +412,40 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
434 412
 
435 413
     #if ENABLED(BACKLASH_GCODE)
436 414
       #if HAS_X_CENTER
437
-        backlash.distance_mm[X_AXIS] = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2;
415
+        backlash.distance_mm.x = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2;
438 416
       #elif ENABLED(CALIBRATION_MEASURE_LEFT)
439
-        backlash.distance_mm[X_AXIS] = m.backlash[LEFT];
417
+        backlash.distance_mm.x = m.backlash[LEFT];
440 418
       #elif ENABLED(CALIBRATION_MEASURE_RIGHT)
441
-        backlash.distance_mm[X_AXIS] = m.backlash[RIGHT];
419
+        backlash.distance_mm.x = m.backlash[RIGHT];
442 420
       #endif
443 421
 
444 422
       #if HAS_Y_CENTER
445
-        backlash.distance_mm[Y_AXIS] = (m.backlash[FRONT] + m.backlash[BACK]) / 2;
423
+        backlash.distance_mm.y = (m.backlash[FRONT] + m.backlash[BACK]) / 2;
446 424
       #elif ENABLED(CALIBRATION_MEASURE_FRONT)
447
-        backlash.distance_mm[Y_AXIS] = m.backlash[FRONT];
425
+        backlash.distance_mm.y = m.backlash[FRONT];
448 426
       #elif ENABLED(CALIBRATION_MEASURE_BACK)
449
-        backlash.distance_mm[Y_AXIS] = m.backlash[BACK];
427
+        backlash.distance_mm.y = m.backlash[BACK];
450 428
       #endif
451 429
 
452
-      backlash.distance_mm[Z_AXIS] = m.backlash[TOP];
430
+      backlash.distance_mm.z = m.backlash[TOP];
453 431
     #endif
454 432
   }
455 433
 
456 434
   #if ENABLED(BACKLASH_GCODE)
457 435
     // Turn on backlash compensation and move in all
458 436
     // directions to take up any backlash
459
-
460 437
     {
461 438
       // New scope for TEMPORARY_BACKLASH_CORRECTION
462 439
       TEMPORARY_BACKLASH_CORRECTION(all_on);
463 440
       TEMPORARY_BACKLASH_SMOOTHING(0.0f);
464
-      move_to(
465
-        X_AXIS, current_position[X_AXIS] + 3,
466
-        Y_AXIS, current_position[Y_AXIS] + 3,
467
-        Z_AXIS, current_position[Z_AXIS] + 3
468
-      );
469
-      move_to(
470
-        X_AXIS, current_position[X_AXIS] - 3,
471
-        Y_AXIS, current_position[Y_AXIS] - 3,
472
-        Z_AXIS, current_position[Z_AXIS] - 3
473
-      );
441
+      const xyz_float_t move = { 3, 3, 3 };
442
+      current_position += move; calibration_move();
443
+      current_position -= move; calibration_move();
474 444
     }
475 445
   #endif
476 446
 }
477 447
 
478 448
 inline void update_measurements(measurements_t &m, const AxisEnum axis) {
479
-  const float true_center[XYZ] = CALIBRATION_OBJECT_CENTER;
480 449
   current_position[axis] += m.pos_error[axis];
481 450
   m.obj_center[axis] = true_center[axis];
482 451
   m.pos_error[axis] = 0;
@@ -508,12 +477,12 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
508 477
   // Adjust the hotend offset
509 478
   #if HAS_HOTEND_OFFSET
510 479
     #if HAS_X_CENTER
511
-      hotend_offset[X_AXIS][extruder] += m.pos_error[X_AXIS];
480
+      hotend_offset[extruder].x += m.pos_error.x;
512 481
     #endif
513 482
     #if HAS_Y_CENTER
514
-      hotend_offset[Y_AXIS][extruder] += m.pos_error[Y_AXIS];
483
+      hotend_offset[extruder].y += m.pos_error.y;
515 484
     #endif
516
-    hotend_offset[Z_AXIS][extruder] += m.pos_error[Z_AXIS];
485
+    hotend_offset[extruder].z += m.pos_error.z;
517 486
     normalize_hotend_offsets();
518 487
   #endif
519 488
 
@@ -589,7 +558,8 @@ inline void calibrate_all() {
589 558
   // Do a slow and precise calibration of the toolheads
590 559
   calibrate_all_toolheads(m, CALIBRATION_MEASUREMENT_UNCERTAIN);
591 560
 
592
-  move_to(X_AXIS, 150); // Park nozzle away from calibration object
561
+  current_position.x = X_CENTER;
562
+  calibration_move();         // Park nozzle away from calibration object
593 563
 }
594 564
 
595 565
 /**

+ 17
- 17
Marlin/src/gcode/calibrate/M48.cpp View File

@@ -74,13 +74,14 @@ void GcodeSuite::M48() {
74 74
 
75 75
   const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
76 76
 
77
-  float X_current = current_position[X_AXIS],
78
-        Y_current = current_position[Y_AXIS];
77
+  xy_float_t next_pos = current_position;
79 78
 
80
-  const float X_probe_location = parser.linearval('X', X_current + probe_offset[X_AXIS]),
81
-              Y_probe_location = parser.linearval('Y', Y_current + probe_offset[Y_AXIS]);
79
+  const xy_pos_t probe_pos = {
80
+    parser.linearval('X', next_pos.x + probe_offset.x),
81
+    parser.linearval('Y', next_pos.y + probe_offset.y)
82
+  };
82 83
 
83
-  if (!position_is_reachable_by_probe(X_probe_location, Y_probe_location)) {
84
+  if (!position_is_reachable_by_probe(probe_pos)) {
84 85
     SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
85 86
     return;
86 87
   }
@@ -116,7 +117,7 @@ void GcodeSuite::M48() {
116 117
   float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples];
117 118
 
118 119
   // Move to the first point, deploy, and probe
119
-  const float t = probe_at_point(X_probe_location, Y_probe_location, raise_after, verbose_level);
120
+  const float t = probe_at_point(probe_pos, raise_after, verbose_level);
120 121
   bool probing_good = !isnan(t);
121 122
 
122 123
   if (probing_good) {
@@ -165,32 +166,31 @@ void GcodeSuite::M48() {
165 166
           while (angle < 0.0) angle += 360.0;   // outside of this range.   It looks like they behave correctly with
166 167
                                                 // numbers outside of the range, but just to be safe we clamp them.
167 168
 
168
-          X_current = X_probe_location - probe_offset[X_AXIS] + cos(RADIANS(angle)) * radius;
169
-          Y_current = Y_probe_location - probe_offset[Y_AXIS] + sin(RADIANS(angle)) * radius;
169
+          next_pos.set(probe_pos.x - probe_offset.x + cos(RADIANS(angle)) * radius,
170
+                       probe_pos.y - probe_offset.y + sin(RADIANS(angle)) * radius);
170 171
 
171 172
           #if DISABLED(DELTA)
172
-            LIMIT(X_current, X_MIN_POS, X_MAX_POS);
173
-            LIMIT(Y_current, Y_MIN_POS, Y_MAX_POS);
173
+            LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
174
+            LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
174 175
           #else
175 176
             // If we have gone out too far, we can do a simple fix and scale the numbers
176 177
             // back in closer to the origin.
177
-            while (!position_is_reachable_by_probe(X_current, Y_current)) {
178
-              X_current *= 0.8;
179
-              Y_current *= 0.8;
178
+            while (!position_is_reachable_by_probe(next_pos)) {
179
+              next_pos *= 0.8;
180 180
               if (verbose_level > 3)
181
-                SERIAL_ECHOLNPAIR("Moving inward: X", X_current, " Y", Y_current);
181
+                SERIAL_ECHOLNPAIR("Moving inward: X", next_pos.x, " Y", next_pos.y);
182 182
             }
183 183
           #endif
184 184
 
185 185
           if (verbose_level > 3)
186
-            SERIAL_ECHOLNPAIR("Going to: X", X_current, " Y", Y_current, " Z", current_position[Z_AXIS]);
186
+            SERIAL_ECHOLNPAIR("Going to: X", next_pos.x, " Y", next_pos.y);
187 187
 
188
-          do_blocking_move_to_xy(X_current, Y_current);
188
+          do_blocking_move_to_xy(next_pos);
189 189
         } // n_legs loop
190 190
       } // n_legs
191 191
 
192 192
       // Probe a single point
193
-      sample_set[n] = probe_at_point(X_probe_location, Y_probe_location, raise_after, 0);
193
+      sample_set[n] = probe_at_point(probe_pos, raise_after, 0);
194 194
 
195 195
       // Break the loop if the probe fails
196 196
       probing_good = !isnan(sample_set[n]);

+ 11
- 11
Marlin/src/gcode/calibrate/M665.cpp View File

@@ -43,14 +43,14 @@
43 43
    *    Z = Gamma (Tower 3) angle trim
44 44
    */
45 45
   void GcodeSuite::M665() {
46
-    if (parser.seen('H')) delta_height                   = parser.value_linear_units();
47
-    if (parser.seen('L')) delta_diagonal_rod             = parser.value_linear_units();
48
-    if (parser.seen('R')) delta_radius                   = parser.value_linear_units();
49
-    if (parser.seen('S')) delta_segments_per_second      = parser.value_float();
50
-    if (parser.seen('B')) delta_calibration_radius       = parser.value_float();
51
-    if (parser.seen('X')) delta_tower_angle_trim[A_AXIS] = parser.value_float();
52
-    if (parser.seen('Y')) delta_tower_angle_trim[B_AXIS] = parser.value_float();
53
-    if (parser.seen('Z')) delta_tower_angle_trim[C_AXIS] = parser.value_float();
46
+    if (parser.seen('H')) delta_height              = parser.value_linear_units();
47
+    if (parser.seen('L')) delta_diagonal_rod        = parser.value_linear_units();
48
+    if (parser.seen('R')) delta_radius              = parser.value_linear_units();
49
+    if (parser.seen('S')) delta_segments_per_second = parser.value_float();
50
+    if (parser.seen('B')) delta_calibration_radius  = parser.value_float();
51
+    if (parser.seen('X')) delta_tower_angle_trim.a  = parser.value_float();
52
+    if (parser.seen('Y')) delta_tower_angle_trim.b  = parser.value_float();
53
+    if (parser.seen('Z')) delta_tower_angle_trim.c  = parser.value_float();
54 54
     recalc_delta_settings();
55 55
   }
56 56
 
@@ -76,13 +76,13 @@
76 76
 
77 77
     #if HAS_SCARA_OFFSET
78 78
 
79
-      if (parser.seenval('Z')) scara_home_offset[Z_AXIS] = parser.value_linear_units();
79
+      if (parser.seenval('Z')) scara_home_offset.z = parser.value_linear_units();
80 80
 
81 81
       const bool hasA = parser.seenval('A'), hasP = parser.seenval('P'), hasX = parser.seenval('X');
82 82
       const uint8_t sumAPX = hasA + hasP + hasX;
83 83
       if (sumAPX) {
84 84
         if (sumAPX == 1)
85
-          scara_home_offset[A_AXIS] = parser.value_float();
85
+          scara_home_offset.a = parser.value_float();
86 86
         else {
87 87
           SERIAL_ERROR_MSG("Only one of A, P, or X is allowed.");
88 88
           return;
@@ -93,7 +93,7 @@
93 93
       const uint8_t sumBTY = hasB + hasT + hasY;
94 94
       if (sumBTY) {
95 95
         if (sumBTY == 1)
96
-          scara_home_offset[B_AXIS] = parser.value_float();
96
+          scara_home_offset.b = parser.value_float();
97 97
         else {
98 98
           SERIAL_ERROR_MSG("Only one of B, T, or Y is allowed.");
99 99
           return;

+ 5
- 5
Marlin/src/gcode/config/M200-M205.cpp View File

@@ -152,17 +152,17 @@ void GcodeSuite::M205() {
152 152
     }
153 153
   #endif
154 154
   #if HAS_CLASSIC_JERK
155
-    if (parser.seen('X')) planner.max_jerk[X_AXIS] = parser.value_linear_units();
156
-    if (parser.seen('Y')) planner.max_jerk[Y_AXIS] = parser.value_linear_units();
155
+    if (parser.seen('X')) planner.max_jerk.x = parser.value_linear_units();
156
+    if (parser.seen('Y')) planner.max_jerk.y = parser.value_linear_units();
157 157
     if (parser.seen('Z')) {
158
-      planner.max_jerk[Z_AXIS] = parser.value_linear_units();
158
+      planner.max_jerk.z = parser.value_linear_units();
159 159
       #if HAS_MESH
160
-        if (planner.max_jerk[Z_AXIS] <= 0.1f)
160
+        if (planner.max_jerk.z <= 0.1f)
161 161
           SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses.");
162 162
       #endif
163 163
     }
164 164
     #if !BOTH(JUNCTION_DEVIATION, LIN_ADVANCE)
165
-      if (parser.seen('E')) planner.max_jerk[E_AXIS] = parser.value_linear_units();
165
+      if (parser.seen('E')) planner.max_jerk.e = parser.value_linear_units();
166 166
     #endif
167 167
   #endif
168 168
 }

+ 7
- 7
Marlin/src/gcode/config/M218.cpp View File

@@ -44,27 +44,27 @@ void GcodeSuite::M218() {
44 44
   const int8_t target_extruder = get_target_extruder_from_command();
45 45
   if (target_extruder < 0) return;
46 46
 
47
-  if (parser.seenval('X')) hotend_offset[X_AXIS][target_extruder] = parser.value_linear_units();
48
-  if (parser.seenval('Y')) hotend_offset[Y_AXIS][target_extruder] = parser.value_linear_units();
49
-  if (parser.seenval('Z')) hotend_offset[Z_AXIS][target_extruder] = parser.value_linear_units();
47
+  if (parser.seenval('X')) hotend_offset[target_extruder].x = parser.value_linear_units();
48
+  if (parser.seenval('Y')) hotend_offset[target_extruder].y = parser.value_linear_units();
49
+  if (parser.seenval('Z')) hotend_offset[target_extruder].z = parser.value_linear_units();
50 50
 
51 51
   if (!parser.seen("XYZ")) {
52 52
     SERIAL_ECHO_START();
53 53
     SERIAL_ECHOPGM(MSG_HOTEND_OFFSET);
54 54
     HOTEND_LOOP() {
55 55
       SERIAL_CHAR(' ');
56
-      SERIAL_ECHO(hotend_offset[X_AXIS][e]);
56
+      SERIAL_ECHO(hotend_offset[e].x);
57 57
       SERIAL_CHAR(',');
58
-      SERIAL_ECHO(hotend_offset[Y_AXIS][e]);
58
+      SERIAL_ECHO(hotend_offset[e].y);
59 59
       SERIAL_CHAR(',');
60
-      SERIAL_ECHO_F(hotend_offset[Z_AXIS][e], 3);
60
+      SERIAL_ECHO_F(hotend_offset[e].z, 3);
61 61
     }
62 62
     SERIAL_EOL();
63 63
   }
64 64
 
65 65
   #if ENABLED(DELTA)
66 66
     if (target_extruder == active_extruder)
67
-      do_blocking_move_to_xy(current_position[X_AXIS], current_position[Y_AXIS], planner.settings.max_feedrate_mm_s[X_AXIS]);
67
+      do_blocking_move_to_xy(current_position, planner.settings.max_feedrate_mm_s[X_AXIS]);
68 68
   #endif
69 69
 }
70 70
 

+ 1
- 1
Marlin/src/gcode/config/M92.cpp View File

@@ -77,7 +77,7 @@ void GcodeSuite::M92() {
77 77
         if (value < 20) {
78 78
           float factor = planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] / value; // increase e constants if M92 E14 is given for netfab.
79 79
           #if HAS_CLASSIC_JERK && !BOTH(JUNCTION_DEVIATION, LIN_ADVANCE)
80
-            planner.max_jerk[E_AXIS] *= factor;
80
+            planner.max_jerk.e *= factor;
81 81
           #endif
82 82
           planner.settings.max_feedrate_mm_s[E_AXIS_N(target_extruder)] *= factor;
83 83
           planner.max_acceleration_steps_per_s2[E_AXIS_N(target_extruder)] *= factor;

+ 4
- 8
Marlin/src/gcode/control/M211.cpp View File

@@ -33,18 +33,14 @@
33 33
  * Usage: M211 S1 to enable, M211 S0 to disable, M211 alone for report
34 34
  */
35 35
 void GcodeSuite::M211() {
36
+  const xyz_pos_t l_soft_min = soft_endstop.min.asLogical(),
37
+                  l_soft_max = soft_endstop.max.asLogical();
36 38
   SERIAL_ECHO_START();
37 39
   SERIAL_ECHOPGM(MSG_SOFT_ENDSTOPS);
38 40
   if (parser.seen('S')) soft_endstops_enabled = parser.value_bool();
39 41
   serialprint_onoff(soft_endstops_enabled);
40
-  SERIAL_ECHOPGM(MSG_SOFT_MIN);
41
-  SERIAL_ECHOPAIR(    MSG_X, LOGICAL_X_POSITION(soft_endstop[X_AXIS].min));
42
-  SERIAL_ECHOPAIR(" " MSG_Y, LOGICAL_Y_POSITION(soft_endstop[Y_AXIS].min));
43
-  SERIAL_ECHOPAIR(" " MSG_Z, LOGICAL_Z_POSITION(soft_endstop[Z_AXIS].min));
44
-  SERIAL_ECHOPGM(MSG_SOFT_MAX);
45
-  SERIAL_ECHOPAIR(    MSG_X, LOGICAL_X_POSITION(soft_endstop[X_AXIS].max));
46
-  SERIAL_ECHOPAIR(" " MSG_Y, LOGICAL_Y_POSITION(soft_endstop[Y_AXIS].max));
47
-  SERIAL_ECHOLNPAIR(" " MSG_Z, LOGICAL_Z_POSITION(soft_endstop[Z_AXIS].max));
42
+  print_xyz(l_soft_min, PSTR(MSG_SOFT_MIN), PSTR(" "));
43
+  print_xyz(l_soft_max, PSTR(MSG_SOFT_MAX));
48 44
 }
49 45
 
50 46
 #endif

+ 4
- 4
Marlin/src/gcode/control/M605.cpp View File

@@ -79,9 +79,9 @@
79 79
         }
80 80
         mirrored_duplication_mode = true;
81 81
         stepper.set_directions();
82
-        float x_jog = current_position[X_AXIS] - .1;
82
+        float x_jog = current_position.x - .1;
83 83
         for (uint8_t i = 2; --i;) {
84
-          planner.buffer_line(x_jog, current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], feedrate_mm_s, 0);
84
+          planner.buffer_line(x_jog, current_position.y, current_position.z, current_position.e, feedrate_mm_s, 0);
85 85
           x_jog += .1;
86 86
         }
87 87
         return;
@@ -122,7 +122,7 @@
122 122
         DEBUG_ECHOPAIR("\nActive Ext: ", int(active_extruder));
123 123
         if (!active_extruder_parked) DEBUG_ECHOPGM(" NOT ");
124 124
         DEBUG_ECHOPGM(" parked.");
125
-        DEBUG_ECHOPAIR("\nactive_extruder_x_pos: ", current_position[X_AXIS]);
125
+        DEBUG_ECHOPAIR("\nactive_extruder_x_pos: ", current_position.x);
126 126
         DEBUG_ECHOPAIR("\ninactive_extruder_x_pos: ", inactive_extruder_x_pos);
127 127
         DEBUG_ECHOPAIR("\nextruder_duplication_enabled: ", int(extruder_duplication_enabled));
128 128
         DEBUG_ECHOPAIR("\nduplicate_extruder_x_offset: ", duplicate_extruder_x_offset);
@@ -138,7 +138,7 @@
138 138
 
139 139
         HOTEND_LOOP() {
140 140
           DEBUG_ECHOPAIR(" T", int(e));
141
-          LOOP_XYZ(a) DEBUG_ECHOPAIR("  hotend_offset[", axis_codes[a], "_AXIS][", int(e), "]=", hotend_offset[a][e]);
141
+          LOOP_XYZ(a) DEBUG_ECHOPAIR("  hotend_offset[", int(e), "].", axis_codes[a] | 0x20, "=", hotend_offset[e][a]);
142 142
           DEBUG_EOL();
143 143
         }
144 144
         DEBUG_EOL();

+ 16
- 16
Marlin/src/gcode/feature/camera/M240.cpp View File

@@ -48,8 +48,8 @@
48 48
         #if ENABLED(ADVANCED_PAUSE_FEATURE)
49 49
           do_pause_e_move(length, fr_mm_s);
50 50
         #else
51
-          current_position[E_AXIS] += length / planner.e_factor[active_extruder];
52
-          planner.buffer_line(current_position, fr_mm_s, active_extruder);
51
+          current_position.e += length / planner.e_factor[active_extruder];
52
+          line_to_current_position(fr_mm_s);
53 53
         #endif
54 54
       }
55 55
     }
@@ -97,10 +97,10 @@ void GcodeSuite::M240() {
97 97
 
98 98
     if (axis_unhomed_error()) return;
99 99
 
100
-    const float old_pos[XYZ] = {
101
-      current_position[X_AXIS] + parser.linearval('A'),
102
-      current_position[Y_AXIS] + parser.linearval('B'),
103
-      current_position[Z_AXIS]
100
+    const xyz_pos_t old_pos = {
101
+      current_position.x + parser.linearval('A'),
102
+      current_position.y + parser.linearval('B'),
103
+      current_position.z
104 104
     };
105 105
 
106 106
     #ifdef PHOTO_RETRACT_MM
@@ -121,22 +121,22 @@ void GcodeSuite::M240() {
121 121
     feedRate_t fr_mm_s = MMM_TO_MMS(parser.linearval('F'));
122 122
     if (fr_mm_s) NOLESS(fr_mm_s, 10.0f);
123 123
 
124
-    constexpr float photo_position[XYZ] = PHOTO_POSITION;
125
-    float raw[XYZ] = {
126
-       parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : photo_position[X_AXIS],
127
-       parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_position[Y_AXIS],
128
-      (parser.seenval('Z') ? parser.value_linear_units() : photo_position[Z_AXIS]) + current_position[Z_AXIS]
124
+    constexpr xyz_pos_t photo_position = PHOTO_POSITION;
125
+    xyz_pos_t raw = {
126
+       parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : photo_position.x,
127
+       parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_position.y,
128
+      (parser.seenval('Z') ? parser.value_linear_units() : photo_position.z) + current_position.z
129 129
     };
130 130
     apply_motion_limits(raw);
131 131
     do_blocking_move_to(raw, fr_mm_s);
132 132
 
133 133
     #ifdef PHOTO_SWITCH_POSITION
134
-      constexpr float photo_switch_position[2] = PHOTO_SWITCH_POSITION;
135
-      const float sraw[] = {
136
-         parser.seenval('I') ? RAW_X_POSITION(parser.value_linear_units()) : photo_switch_position[X_AXIS],
137
-         parser.seenval('J') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_switch_position[Y_AXIS]
134
+      constexpr xy_pos_t photo_switch_position = PHOTO_SWITCH_POSITION;
135
+      const xy_pos_t sraw = {
136
+         parser.seenval('I') ? RAW_X_POSITION(parser.value_linear_units()) : photo_switch_position.x,
137
+         parser.seenval('J') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_switch_position.y
138 138
       };
139
-      do_blocking_move_to_xy(sraw[X_AXIS], sraw[Y_AXIS], get_homing_bump_feedrate(X_AXIS));
139
+      do_blocking_move_to_xy(sraw, get_homing_bump_feedrate(X_AXIS));
140 140
       #if PHOTO_SWITCH_MS > 0
141 141
         safe_delay(parser.intval('D', PHOTO_SWITCH_MS));
142 142
       #endif

+ 2
- 3
Marlin/src/gcode/feature/pause/M125.cpp View File

@@ -58,7 +58,7 @@ void GcodeSuite::M125() {
58 58
     #endif
59 59
   );
60 60
 
61
-  point_t park_point = NOZZLE_PARK_POINT;
61
+  xyz_pos_t park_point = NOZZLE_PARK_POINT;
62 62
 
63 63
   // Move XY axes to filament change position or given position
64 64
   if (parser.seenval('X')) park_point.x = RAW_X_POSITION(parser.linearval('X'));
@@ -68,8 +68,7 @@ void GcodeSuite::M125() {
68 68
   if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
69 69
 
70 70
   #if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
71
-    park_point.x += hotend_offset[X_AXIS][active_extruder];
72
-    park_point.y += hotend_offset[Y_AXIS][active_extruder];
71
+    park_point += hotend_offset[active_extruder];
73 72
   #endif
74 73
 
75 74
   #if ENABLED(SDSUPPORT)

+ 3
- 3
Marlin/src/gcode/feature/pause/M600.cpp View File

@@ -60,7 +60,6 @@
60 60
  *  Default values are used for omitted arguments.
61 61
  */
62 62
 void GcodeSuite::M600() {
63
-  point_t park_point = NOZZLE_PARK_POINT;
64 63
 
65 64
   #if ENABLED(MIXING_EXTRUDER)
66 65
     const int8_t target_e_stepper = get_target_e_stepper_from_command();
@@ -119,6 +118,8 @@ void GcodeSuite::M600() {
119 118
     #endif
120 119
   );
121 120
 
121
+  xyz_pos_t park_point NOZZLE_PARK_POINT;
122
+
122 123
   // Lift Z axis
123 124
   if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
124 125
 
@@ -127,8 +128,7 @@ void GcodeSuite::M600() {
127 128
   if (parser.seenval('Y')) park_point.y = parser.linearval('Y');
128 129
 
129 130
   #if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
130
-    park_point.x += hotend_offset[X_AXIS][active_extruder];
131
-    park_point.y += hotend_offset[Y_AXIS][active_extruder];
131
+    park_point += hotend_offset[active_extruder];
132 132
   #endif
133 133
 
134 134
   #if ENABLED(MMU2_MENUS)

+ 6
- 7
Marlin/src/gcode/feature/pause/M701_M702.cpp View File

@@ -28,7 +28,6 @@
28 28
 #include "../../../Marlin.h"
29 29
 #include "../../../module/motion.h"
30 30
 #include "../../../module/temperature.h"
31
-#include "../../../libs/point_t.h"
32 31
 
33 32
 #if EXTRUDERS > 1
34 33
   #include "../../../module/tool_change.h"
@@ -57,7 +56,7 @@
57 56
  *  Default values are used for omitted arguments.
58 57
  */
59 58
 void GcodeSuite::M701() {
60
-  point_t park_point = NOZZLE_PARK_POINT;
59
+  xyz_pos_t park_point = NOZZLE_PARK_POINT;
61 60
 
62 61
   #if ENABLED(NO_MOTION_BEFORE_HOMING)
63 62
     // Don't raise Z if the machine isn't homed
@@ -97,7 +96,7 @@ void GcodeSuite::M701() {
97 96
 
98 97
   // Lift Z axis
99 98
   if (park_point.z > 0)
100
-    do_blocking_move_to_z(_MIN(current_position[Z_AXIS] + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
99
+    do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
101 100
 
102 101
   // Load filament
103 102
   #if ENABLED(PRUSA_MMU2)
@@ -116,7 +115,7 @@ void GcodeSuite::M701() {
116 115
 
117 116
   // Restore Z axis
118 117
   if (park_point.z > 0)
119
-    do_blocking_move_to_z(_MAX(current_position[Z_AXIS] - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
118
+    do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
120 119
 
121 120
   #if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
122 121
     // Restore toolhead if it was changed
@@ -146,7 +145,7 @@ void GcodeSuite::M701() {
146 145
  *  Default values are used for omitted arguments.
147 146
  */
148 147
 void GcodeSuite::M702() {
149
-  point_t park_point = NOZZLE_PARK_POINT;
148
+  xyz_pos_t park_point = NOZZLE_PARK_POINT;
150 149
 
151 150
   #if ENABLED(NO_MOTION_BEFORE_HOMING)
152 151
     // Don't raise Z if the machine isn't homed
@@ -196,7 +195,7 @@ void GcodeSuite::M702() {
196 195
 
197 196
   // Lift Z axis
198 197
   if (park_point.z > 0)
199
-    do_blocking_move_to_z(_MIN(current_position[Z_AXIS] + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
198
+    do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
200 199
 
201 200
   // Unload filament
202 201
   #if ENABLED(PRUSA_MMU2)
@@ -226,7 +225,7 @@ void GcodeSuite::M702() {
226 225
 
227 226
   // Restore Z axis
228 227
   if (park_point.z > 0)
229
-    do_blocking_move_to_z(_MAX(current_position[Z_AXIS] - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
228
+    do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
230 229
 
231 230
   #if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
232 231
     // Restore toolhead if it was changed

+ 5
- 5
Marlin/src/gcode/feature/trinamic/M122.cpp View File

@@ -31,8 +31,8 @@
31 31
  * M122: Debug TMC drivers
32 32
  */
33 33
 void GcodeSuite::M122() {
34
-  bool print_axis[XYZE] = { false, false, false, false },
35
-       print_all = true;
34
+  xyze_bool_t print_axis = { false, false, false, false };
35
+  bool print_all = true;
36 36
   LOOP_XYZE(i) if (parser.seen(axis_codes[i])) { print_axis[i] = true; print_all = false; }
37 37
 
38 38
   if (print_all) LOOP_XYZE(i) print_axis[i] = true;
@@ -45,12 +45,12 @@ void GcodeSuite::M122() {
45 45
     #endif
46 46
 
47 47
     if (parser.seen('V'))
48
-      tmc_get_registers(print_axis[X_AXIS], print_axis[Y_AXIS], print_axis[Z_AXIS], print_axis[E_AXIS]);
48
+      tmc_get_registers(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
49 49
     else
50
-      tmc_report_all(print_axis[X_AXIS], print_axis[Y_AXIS], print_axis[Z_AXIS], print_axis[E_AXIS]);
50
+      tmc_report_all(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
51 51
   #endif
52 52
 
53
-  test_tmc_connection(print_axis[X_AXIS], print_axis[Y_AXIS], print_axis[Z_AXIS], print_axis[E_AXIS]);
53
+  test_tmc_connection(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
54 54
 }
55 55
 
56 56
 #endif // HAS_TRINAMIC

+ 8
- 8
Marlin/src/gcode/feature/trinamic/M911-M914.cpp View File

@@ -104,25 +104,25 @@
104 104
    */
105 105
   void GcodeSuite::M912() {
106 106
     #if M91x_SOME_X
107
-      const bool hasX = parser.seen(axis_codes[X_AXIS]);
107
+      const bool hasX = parser.seen(axis_codes.x);
108 108
     #else
109 109
       constexpr bool hasX = false;
110 110
     #endif
111 111
 
112 112
     #if M91x_SOME_Y
113
-      const bool hasY = parser.seen(axis_codes[Y_AXIS]);
113
+      const bool hasY = parser.seen(axis_codes.y);
114 114
     #else
115 115
       constexpr bool hasY = false;
116 116
     #endif
117 117
 
118 118
     #if M91x_SOME_Z
119
-      const bool hasZ = parser.seen(axis_codes[Z_AXIS]);
119
+      const bool hasZ = parser.seen(axis_codes.z);
120 120
     #else
121 121
       constexpr bool hasZ = false;
122 122
     #endif
123 123
 
124 124
     #if M91x_SOME_E
125
-      const bool hasE = parser.seen(axis_codes[E_AXIS]);
125
+      const bool hasE = parser.seen(axis_codes.e);
126 126
     #else
127 127
       constexpr bool hasE = false;
128 128
     #endif
@@ -130,7 +130,7 @@
130 130
     const bool hasNone = !hasX && !hasY && !hasZ && !hasE;
131 131
 
132 132
     #if M91x_SOME_X
133
-      const int8_t xval = int8_t(parser.byteval(axis_codes[X_AXIS], 0xFF));
133
+      const int8_t xval = int8_t(parser.byteval(axis_codes.x, 0xFF));
134 134
       #if M91x_USE(X)
135 135
         if (hasNone || xval == 1 || (hasX && xval < 0)) tmc_clear_otpw(stepperX);
136 136
       #endif
@@ -140,7 +140,7 @@
140 140
     #endif
141 141
 
142 142
     #if M91x_SOME_Y
143
-      const int8_t yval = int8_t(parser.byteval(axis_codes[Y_AXIS], 0xFF));
143
+      const int8_t yval = int8_t(parser.byteval(axis_codes.y, 0xFF));
144 144
       #if M91x_USE(Y)
145 145
         if (hasNone || yval == 1 || (hasY && yval < 0)) tmc_clear_otpw(stepperY);
146 146
       #endif
@@ -150,7 +150,7 @@
150 150
     #endif
151 151
 
152 152
     #if M91x_SOME_Z
153
-      const int8_t zval = int8_t(parser.byteval(axis_codes[Z_AXIS], 0xFF));
153
+      const int8_t zval = int8_t(parser.byteval(axis_codes.z, 0xFF));
154 154
       #if M91x_USE(Z)
155 155
         if (hasNone || zval == 1 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ);
156 156
       #endif
@@ -163,7 +163,7 @@
163 163
     #endif
164 164
 
165 165
     #if M91x_SOME_E
166
-      const int8_t eval = int8_t(parser.byteval(axis_codes[E_AXIS], 0xFF));
166
+      const int8_t eval = int8_t(parser.byteval(axis_codes.e, 0xFF));
167 167
       #if M91x_USE_E(0)
168 168
         if (hasNone || eval == 0 || (hasE && eval < 0)) tmc_clear_otpw(stepperE0);
169 169
       #endif

+ 10
- 9
Marlin/src/gcode/gcode.cpp View File

@@ -49,12 +49,13 @@ GcodeSuite gcode;
49 49
 
50 50
 millis_t GcodeSuite::previous_move_ms;
51 51
 
52
-static constexpr bool ar_init[XYZE] = AXIS_RELATIVE_MODES;
52
+// Relative motion mode for each logical axis
53
+static constexpr xyze_bool_t ar_init = AXIS_RELATIVE_MODES;
53 54
 uint8_t GcodeSuite::axis_relative = (
54
-    (ar_init[X_AXIS] ? _BV(REL_X) : 0)
55
-  | (ar_init[Y_AXIS] ? _BV(REL_Y) : 0)
56
-  | (ar_init[Z_AXIS] ? _BV(REL_Z) : 0)
57
-  | (ar_init[E_AXIS] ? _BV(REL_E) : 0)
55
+    (ar_init.x ? _BV(REL_X) : 0)
56
+  | (ar_init.y ? _BV(REL_Y) : 0)
57
+  | (ar_init.z ? _BV(REL_Z) : 0)
58
+  | (ar_init.e ? _BV(REL_E) : 0)
58 59
 );
59 60
 
60 61
 #if ENABLED(HOST_KEEPALIVE_FEATURE)
@@ -68,7 +69,7 @@ uint8_t GcodeSuite::axis_relative = (
68 69
 
69 70
 #if ENABLED(CNC_COORDINATE_SYSTEMS)
70 71
   int8_t GcodeSuite::active_coordinate_system = -1; // machine space
71
-  float GcodeSuite::coordinate_system[MAX_COORDINATE_SYSTEMS][XYZ];
72
+  xyz_pos_t GcodeSuite::coordinate_system[MAX_COORDINATE_SYSTEMS];
72 73
 #endif
73 74
 
74 75
 /**
@@ -112,7 +113,7 @@ int8_t GcodeSuite::get_target_e_stepper_from_command() {
112 113
  *  - Set the feedrate, if included
113 114
  */
114 115
 void GcodeSuite::get_destination_from_command() {
115
-  bool seen[XYZE] = { false, false, false, false };
116
+  xyze_bool_t seen = { false, false, false, false };
116 117
   LOOP_XYZE(i) {
117 118
     if ( (seen[i] = parser.seenval(axis_codes[i])) ) {
118 119
       const float v = parser.value_axis_units((AxisEnum)i);
@@ -124,7 +125,7 @@ void GcodeSuite::get_destination_from_command() {
124 125
 
125 126
   #if ENABLED(POWER_LOSS_RECOVERY) && !PIN_EXISTS(POWER_LOSS)
126 127
     // Only update power loss recovery on moves with E
127
-    if (recovery.enabled && IS_SD_PRINTING() && seen[E_AXIS] && (seen[X_AXIS] || seen[Y_AXIS]))
128
+    if (recovery.enabled && IS_SD_PRINTING() && seen.e && (seen.x || seen.y))
128 129
       recovery.save();
129 130
   #endif
130 131
 
@@ -133,7 +134,7 @@ void GcodeSuite::get_destination_from_command() {
133 134
 
134 135
   #if ENABLED(PRINTCOUNTER)
135 136
     if (!DEBUGGING(DRYRUN))
136
-      print_job_timer.incFilamentUsed(destination[E_AXIS] - current_position[E_AXIS]);
137
+      print_job_timer.incFilamentUsed(destination.e - current_position.e);
137 138
   #endif
138 139
 
139 140
   // Get ABCDHI mixing factors

+ 1
- 1
Marlin/src/gcode/gcode.h View File

@@ -321,7 +321,7 @@ public:
321 321
   #define MAX_COORDINATE_SYSTEMS 9
322 322
   #if ENABLED(CNC_COORDINATE_SYSTEMS)
323 323
     static int8_t active_coordinate_system;
324
-    static float coordinate_system[MAX_COORDINATE_SYSTEMS][XYZ];
324
+    static xyz_pos_t coordinate_system[MAX_COORDINATE_SYSTEMS];
325 325
     static bool select_coordinate_system(const int8_t _new);
326 326
   #endif
327 327
 

+ 2
- 2
Marlin/src/gcode/geometry/G53-G59.cpp View File

@@ -36,9 +36,9 @@
36 36
 bool GcodeSuite::select_coordinate_system(const int8_t _new) {
37 37
   if (active_coordinate_system == _new) return false;
38 38
   active_coordinate_system = _new;
39
-  float new_offset[XYZ] = { 0 };
39
+  xyz_float_t new_offset{0};
40 40
   if (WITHIN(_new, 0, MAX_COORDINATE_SYSTEMS - 1))
41
-    COPY(new_offset, coordinate_system[_new]);
41
+    new_offset = coordinate_system[_new];
42 42
   LOOP_XYZ(i) {
43 43
     if (position_shift[i] != new_offset[i]) {
44 44
       position_shift[i] = new_offset[i];

+ 2
- 2
Marlin/src/gcode/geometry/G92.cpp View File

@@ -86,7 +86,7 @@ void GcodeSuite::G92() {
86 86
             #elif HAS_POSITION_SHIFT
87 87
               if (i == E_AXIS) {
88 88
                 didE = true;
89
-                current_position[E_AXIS] = v; // When using coordinate spaces, only E is set directly
89
+                current_position.e = v; // When using coordinate spaces, only E is set directly
90 90
               }
91 91
               else {
92 92
                 position_shift[i] += d;       // Other axes simply offset the coordinate space
@@ -102,7 +102,7 @@ void GcodeSuite::G92() {
102 102
   #if ENABLED(CNC_COORDINATE_SYSTEMS)
103 103
     // Apply workspace offset to the active coordinate system
104 104
     if (WITHIN(active_coordinate_system, 0, MAX_COORDINATE_SYSTEMS - 1))
105
-      COPY(coordinate_system[active_coordinate_system], position_shift);
105
+      coordinate_system[active_coordinate_system] = position_shift;
106 106
   #endif
107 107
 
108 108
   if    (didXYZ) sync_plan_position();

+ 1
- 1
Marlin/src/gcode/geometry/M206_M428.cpp View File

@@ -63,7 +63,7 @@ void GcodeSuite::M206() {
63 63
 void GcodeSuite::M428() {
64 64
   if (axis_unhomed_error()) return;
65 65
 
66
-  float diff[XYZ];
66
+  xyz_float_t diff;
67 67
   LOOP_XYZ(i) {
68 68
     diff[i] = base_home_pos((AxisEnum)i) - current_position[i];
69 69
     if (!WITHIN(diff[i], -20, 20) && home_dir((AxisEnum)i) > 0)

+ 20
- 20
Marlin/src/gcode/host/M114.cpp View File

@@ -36,7 +36,7 @@
36 36
     #include "../../core/debug_out.h"
37 37
   #endif
38 38
 
39
-  void report_xyze(const float pos[], const uint8_t n = 4, const uint8_t precision = 3) {
39
+  void report_xyze(const xyze_pos_t &pos, const uint8_t n=4, const uint8_t precision=3) {
40 40
     char str[12];
41 41
     for (uint8_t a = 0; a < n; a++) {
42 42
       SERIAL_CHAR(' ');
@@ -47,22 +47,27 @@
47 47
     SERIAL_EOL();
48 48
   }
49 49
 
50
-  inline void report_xyz(const float pos[]) { report_xyze(pos, 3); }
50
+  void report_xyz(const xyz_pos_t &pos, const uint8_t precision=3) {
51
+    char str[12];
52
+    for (uint8_t a = X_AXIS; a <= Z_AXIS; a++) {
53
+      SERIAL_CHAR(' ');
54
+      SERIAL_CHAR(axis_codes[a]);
55
+      SERIAL_CHAR(':');
56
+      SERIAL_ECHO(dtostrf(pos[a], 1, precision, str));
57
+    }
58
+    SERIAL_EOL();
59
+  }
60
+  inline void report_xyz(const xyze_pos_t &pos) { report_xyze(pos, 3); }
51 61
 
52 62
   void report_current_position_detail() {
53 63
 
54 64
     SERIAL_ECHOPGM("\nLogical:");
55
-    const float logical[XYZ] = {
56
-      LOGICAL_X_POSITION(current_position[X_AXIS]),
57
-      LOGICAL_Y_POSITION(current_position[Y_AXIS]),
58
-      LOGICAL_Z_POSITION(current_position[Z_AXIS])
59
-    };
60
-    report_xyz(logical);
65
+    report_xyz(current_position.asLogical());
61 66
 
62 67
     SERIAL_ECHOPGM("Raw:    ");
63 68
     report_xyz(current_position);
64 69
 
65
-    float leveled[XYZ] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] };
70
+    xyze_pos_t leveled = current_position;
66 71
 
67 72
     #if HAS_LEVELING
68 73
       SERIAL_ECHOPGM("Leveled:");
@@ -70,7 +75,7 @@
70 75
       report_xyz(leveled);
71 76
 
72 77
       SERIAL_ECHOPGM("UnLevel:");
73
-      float unleveled[XYZ] = { leveled[X_AXIS], leveled[Y_AXIS], leveled[Z_AXIS] };
78
+      xyze_pos_t unleveled = leveled;
74 79
       planner.unapply_leveling(unleveled);
75 80
       report_xyz(unleveled);
76 81
     #endif
@@ -153,7 +158,7 @@
153 158
     SERIAL_EOL();
154 159
 
155 160
     #if IS_SCARA
156
-      const float deg[XYZ] = {
161
+      const xy_float_t deg = {
157 162
         planner.get_axis_position_degrees(A_AXIS),
158 163
         planner.get_axis_position_degrees(B_AXIS)
159 164
       };
@@ -162,17 +167,12 @@
162 167
     #endif
163 168
 
164 169
     SERIAL_ECHOPGM("FromStp:");
165
-    get_cartesian_from_steppers();  // writes cartes[XYZ] (with forward kinematics)
166
-    const float from_steppers[XYZE] = { cartes[X_AXIS], cartes[Y_AXIS], cartes[Z_AXIS], planner.get_axis_position_mm(E_AXIS) };
170
+    get_cartesian_from_steppers();  // writes 'cartes' (with forward kinematics)
171
+    xyze_pos_t from_steppers = { cartes.x, cartes.y, cartes.z, planner.get_axis_position_mm(E_AXIS) };
167 172
     report_xyze(from_steppers);
168 173
 
169
-    const float diff[XYZE] = {
170
-      from_steppers[X_AXIS] - leveled[X_AXIS],
171
-      from_steppers[Y_AXIS] - leveled[Y_AXIS],
172
-      from_steppers[Z_AXIS] - leveled[Z_AXIS],
173
-      from_steppers[E_AXIS] - current_position[E_AXIS]
174
-    };
175
-    SERIAL_ECHOPGM("Differ: ");
174
+    const xyze_float_t diff = from_steppers - leveled;
175
+    SERIAL_ECHOPGM("Diff: ");
176 176
     report_xyze(diff);
177 177
   }
178 178
 

+ 5
- 5
Marlin/src/gcode/motion/G0_G1.cpp View File

@@ -35,7 +35,7 @@
35 35
   #include "../../module/stepper.h"
36 36
 #endif
37 37
 
38
-extern float destination[XYZE];
38
+extern xyze_pos_t destination;
39 39
 
40 40
 #if ENABLED(VARIABLE_G0_FEEDRATE)
41 41
   feedRate_t fast_move_feedrate = MMM_TO_MMS(G0_FEEDRATE);
@@ -87,12 +87,12 @@ void GcodeSuite::G0_G1(
87 87
       if (MIN_AUTORETRACT <= MAX_AUTORETRACT) {
88 88
         // When M209 Autoretract is enabled, convert E-only moves to firmware retract/recover moves
89 89
         if (fwretract.autoretract_enabled && parser.seen('E') && !(parser.seen('X') || parser.seen('Y') || parser.seen('Z'))) {
90
-          const float echange = destination[E_AXIS] - current_position[E_AXIS];
90
+          const float echange = destination.e - current_position.e;
91 91
           // Is this a retract or recover move?
92 92
           if (WITHIN(ABS(echange), MIN_AUTORETRACT, MAX_AUTORETRACT) && fwretract.retracted[active_extruder] == (echange > 0.0)) {
93
-            current_position[E_AXIS] = destination[E_AXIS]; // Hide a G1-based retract/recover from calculations
94
-            sync_plan_position_e();                         // AND from the planner
95
-            return fwretract.retract(echange < 0.0);        // Firmware-based retract/recover (double-retract ignored)
93
+            current_position.e = destination.e;       // Hide a G1-based retract/recover from calculations
94
+            sync_plan_position_e();                   // AND from the planner
95
+            return fwretract.retract(echange < 0.0);  // Firmware-based retract/recover (double-retract ignored)
96 96
           }
97 97
         }
98 98
       }

+ 34
- 39
Marlin/src/gcode/motion/G2_G3.cpp View File

@@ -50,9 +50,9 @@
50 50
  * options for G2/G3 arc generation. In future these options may be GCode tunable.
51 51
  */
52 52
 void plan_arc(
53
-  const float (&cart)[XYZE],  // Destination position
54
-  const float (&offset)[2],   // Center of rotation relative to current_position
55
-  const uint8_t clockwise     // Clockwise?
53
+  const xyze_pos_t &cart,   // Destination position
54
+  const ab_float_t &offset, // Center of rotation relative to current_position
55
+  const uint8_t clockwise   // Clockwise?
56 56
 ) {
57 57
   #if ENABLED(CNC_WORKSPACE_PLANES)
58 58
     AxisEnum p_axis, q_axis, l_axis;
@@ -67,21 +67,21 @@ void plan_arc(
67 67
   #endif
68 68
 
69 69
   // Radius vector from center to current location
70
-  float r_P = -offset[0], r_Q = -offset[1];
70
+  ab_float_t rvec = -offset;
71 71
 
72
-  const float radius = HYPOT(r_P, r_Q),
72
+  const float radius = HYPOT(rvec.a, rvec.b),
73 73
               #if ENABLED(AUTO_BED_LEVELING_UBL)
74 74
                 start_L  = current_position[l_axis],
75 75
               #endif
76
-              center_P = current_position[p_axis] - r_P,
77
-              center_Q = current_position[q_axis] - r_Q,
76
+              center_P = current_position[p_axis] - rvec.a,
77
+              center_Q = current_position[q_axis] - rvec.b,
78 78
               rt_X = cart[p_axis] - center_P,
79 79
               rt_Y = cart[q_axis] - center_Q,
80 80
               linear_travel = cart[l_axis] - current_position[l_axis],
81
-              extruder_travel = cart[E_AXIS] - current_position[E_AXIS];
81
+              extruder_travel = cart.e - current_position.e;
82 82
 
83 83
   // CCW angle of rotation between position and target from the circle center. Only one atan2() trig computation required.
84
-  float angular_travel = ATAN2(r_P * rt_Y - r_Q * rt_X, r_P * rt_X + r_Q * rt_Y);
84
+  float angular_travel = ATAN2(rvec.a * rt_Y - rvec.b * rt_X, rvec.a * rt_X + rvec.b * rt_Y);
85 85
   if (angular_travel < 0) angular_travel += RADIANS(360);
86 86
   #ifdef MIN_ARC_SEGMENTS
87 87
     uint16_t min_segments = CEIL((MIN_ARC_SEGMENTS) * (angular_travel / RADIANS(360)));
@@ -133,7 +133,7 @@ void plan_arc(
133 133
    * This is important when there are successive arc motions.
134 134
    */
135 135
   // Vector rotation matrix values
136
-  float raw[XYZE];
136
+  xyze_pos_t raw;
137 137
   const float theta_per_segment = angular_travel / segments,
138 138
               linear_per_segment = linear_travel / segments,
139 139
               extruder_per_segment = extruder_travel / segments,
@@ -144,7 +144,7 @@ void plan_arc(
144 144
   raw[l_axis] = current_position[l_axis];
145 145
 
146 146
   // Initialize the extruder axis
147
-  raw[E_AXIS] = current_position[E_AXIS];
147
+  raw.e = current_position.e;
148 148
 
149 149
   const feedRate_t scaled_fr_mm_s = MMS_SCALED(feedrate_mm_s);
150 150
 
@@ -168,10 +168,10 @@ void plan_arc(
168 168
 
169 169
     #if N_ARC_CORRECTION > 1
170 170
       if (--arc_recalc_count) {
171
-        // Apply vector rotation matrix to previous r_P / 1
172
-        const float r_new_Y = r_P * sin_T + r_Q * cos_T;
173
-        r_P = r_P * cos_T - r_Q * sin_T;
174
-        r_Q = r_new_Y;
171
+        // Apply vector rotation matrix to previous rvec.a / 1
172
+        const float r_new_Y = rvec.a * sin_T + rvec.b * cos_T;
173
+        rvec.a = rvec.a * cos_T - rvec.b * sin_T;
174
+        rvec.b = r_new_Y;
175 175
       }
176 176
       else
177 177
     #endif
@@ -185,20 +185,20 @@ void plan_arc(
185 185
       // To reduce stuttering, the sin and cos could be computed at different times.
186 186
       // For now, compute both at the same time.
187 187
       const float cos_Ti = cos(i * theta_per_segment), sin_Ti = sin(i * theta_per_segment);
188
-      r_P = -offset[0] * cos_Ti + offset[1] * sin_Ti;
189
-      r_Q = -offset[0] * sin_Ti - offset[1] * cos_Ti;
188
+      rvec.a = -offset[0] * cos_Ti + offset[1] * sin_Ti;
189
+      rvec.b = -offset[0] * sin_Ti - offset[1] * cos_Ti;
190 190
     }
191 191
 
192 192
     // Update raw location
193
-    raw[p_axis] = center_P + r_P;
194
-    raw[q_axis] = center_Q + r_Q;
193
+    raw[p_axis] = center_P + rvec.a;
194
+    raw[q_axis] = center_Q + rvec.b;
195 195
     #if ENABLED(AUTO_BED_LEVELING_UBL)
196 196
       raw[l_axis] = start_L;
197 197
       UNUSED(linear_per_segment);
198 198
     #else
199 199
       raw[l_axis] += linear_per_segment;
200 200
     #endif
201
-    raw[E_AXIS] += extruder_per_segment;
201
+    raw.e += extruder_per_segment;
202 202
 
203 203
     apply_motion_limits(raw);
204 204
 
@@ -215,7 +215,7 @@ void plan_arc(
215 215
   }
216 216
 
217 217
   // Ensure last segment arrives at target location.
218
-  COPY(raw, cart);
218
+  raw = cart;
219 219
   #if ENABLED(AUTO_BED_LEVELING_UBL)
220 220
     raw[l_axis] = start_L;
221 221
   #endif
@@ -235,7 +235,7 @@ void plan_arc(
235 235
   #if ENABLED(AUTO_BED_LEVELING_UBL)
236 236
     raw[l_axis] = start_L;
237 237
   #endif
238
-  COPY(current_position, raw);
238
+  current_position = raw;
239 239
 } // plan_arc
240 240
 
241 241
 /**
@@ -278,32 +278,27 @@ void GcodeSuite::G2_G3(const bool clockwise) {
278 278
       relative_mode = relative_mode_backup;
279 279
     #endif
280 280
 
281
-    float arc_offset[2] = { 0, 0 };
281
+    ab_float_t arc_offset = { 0, 0 };
282 282
     if (parser.seenval('R')) {
283 283
       const float r = parser.value_linear_units();
284 284
       if (r) {
285
-        const float p1 = current_position[X_AXIS], q1 = current_position[Y_AXIS],
286
-                    p2 = destination[X_AXIS], q2 = destination[Y_AXIS];
287
-        if (p2 != p1 || q2 != q1) {
288
-          const float e = clockwise ^ (r < 0) ? -1 : 1,            // clockwise -1/1, counterclockwise 1/-1
289
-                      dx = p2 - p1, dy = q2 - q1,                  // X and Y differences
290
-                      d = HYPOT(dx, dy),                           // Linear distance between the points
291
-                      dinv = 1/d,                                  // Inverse of d
292
-                      h = SQRT(sq(r) - sq(d * 0.5f)),              // Distance to the arc pivot-point
293
-                      mx = (p1 + p2) * 0.5f, my = (q1 + q2) * 0.5f,// Point between the two points
294
-                      sx = -dy * dinv, sy = dx * dinv,             // Slope of the perpendicular bisector
295
-                      cx = mx + e * h * sx, cy = my + e * h * sy;  // Pivot-point of the arc
296
-          arc_offset[0] = cx - p1;
297
-          arc_offset[1] = cy - q1;
285
+        const xy_pos_t p1 = current_position, p2 = destination;
286
+        if (p1 != p2) {
287
+          const xy_pos_t d = p2 - p1, m = (p1 + p2) * 0.5f;   // XY distance and midpoint
288
+          const float e = clockwise ^ (r < 0) ? -1 : 1,       // clockwise -1/1, counterclockwise 1/-1
289
+                      len = d.magnitude(),                    // Total move length
290
+                      h = SQRT(sq(r) - sq(len * 0.5f));       // Distance to the arc pivot-point
291
+          const xy_pos_t s = { d.x, -d.y };                   // Inverse Slope of the perpendicular bisector
292
+          arc_offset = m + s * RECIPROCAL(len) * e * h - p1;  // The calculated offset
298 293
         }
299 294
       }
300 295
     }
301 296
     else {
302
-      if (parser.seenval('I')) arc_offset[0] = parser.value_linear_units();
303
-      if (parser.seenval('J')) arc_offset[1] = parser.value_linear_units();
297
+      if (parser.seenval('I')) arc_offset.a = parser.value_linear_units();
298
+      if (parser.seenval('J')) arc_offset.b = parser.value_linear_units();
304 299
     }
305 300
 
306
-    if (arc_offset[0] || arc_offset[1]) {
301
+    if (arc_offset) {
307 302
 
308 303
       #if ENABLED(ARC_P_CIRCLES)
309 304
         // P indicates number of circles to do

+ 5
- 11
Marlin/src/gcode/motion/G5.cpp View File

@@ -27,11 +27,6 @@
27 27
 #include "../../module/motion.h"
28 28
 #include "../../module/planner_bezier.h"
29 29
 
30
-void plan_cubic_move(const float (&cart)[XYZE], const float (&offset)[4]) {
31
-  cubic_b_spline(current_position, cart, offset, MMS_SCALED(feedrate_mm_s), active_extruder);
32
-  COPY(current_position, cart);
33
-}
34
-
35 30
 /**
36 31
  * Parameters interpreted according to:
37 32
  * http://linuxcnc.org/docs/2.6/html/gcode/parser.html#sec:G5-Cubic-Spline
@@ -57,14 +52,13 @@ void GcodeSuite::G5() {
57 52
 
58 53
     get_destination_from_command();
59 54
 
60
-    const float offset[4] = {
61
-      parser.linearval('I'),
62
-      parser.linearval('J'),
63
-      parser.linearval('P'),
64
-      parser.linearval('Q')
55
+    const xy_pos_t offsets[2] = {
56
+      { parser.linearval('I'), parser.linearval('J') },
57
+      { parser.linearval('P'), parser.linearval('Q') }
65 58
     };
66 59
 
67
-    plan_cubic_move(destination, offset);
60
+    cubic_b_spline(current_position, destination, offsets, MMS_SCALED(feedrate_mm_s), active_extruder);
61
+    current_position = destination;
68 62
   }
69 63
 }
70 64
 

+ 11
- 11
Marlin/src/gcode/motion/M290.cpp View File

@@ -40,21 +40,21 @@
40 40
 
41 41
 #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
42 42
 
43
-  FORCE_INLINE void mod_zprobe_zoffset(const float &offs) {
43
+  FORCE_INLINE void mod_probe_offset(const float &offs) {
44 44
     if (true
45 45
       #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
46 46
         && active_extruder == 0
47 47
       #endif
48 48
     ) {
49
-      probe_offset[Z_AXIS] += offs;
49
+      probe_offset.z += offs;
50 50
       SERIAL_ECHO_START();
51
-      SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET MSG_Z ": ", probe_offset[Z_AXIS]);
51
+      SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET MSG_Z ": ", probe_offset.z);
52 52
     }
53 53
     else {
54 54
       #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
55
-        hotend_offset[Z_AXIS][active_extruder] -= offs;
55
+        hotend_offset[active_extruder].z -= offs;
56 56
         SERIAL_ECHO_START();
57
-        SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET MSG_Z ": ", hotend_offset[Z_AXIS][active_extruder]);
57
+        SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET MSG_Z ": ", hotend_offset[active_extruder].z);
58 58
       #endif
59 59
     }
60 60
   }
@@ -81,7 +81,7 @@ void GcodeSuite::M290() {
81 81
         const float offs = constrain(parser.value_axis_units((AxisEnum)a), -2, 2);
82 82
         babystep.add_mm((AxisEnum)a, offs);
83 83
         #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
84
-          if (a == Z_AXIS && (!parser.seen('P') || parser.value_bool())) mod_zprobe_zoffset(offs);
84
+          if (a == Z_AXIS && (!parser.seen('P') || parser.value_bool())) mod_probe_offset(offs);
85 85
         #endif
86 86
       }
87 87
   #else
@@ -89,7 +89,7 @@ void GcodeSuite::M290() {
89 89
       const float offs = constrain(parser.value_axis_units(Z_AXIS), -2, 2);
90 90
       babystep.add_mm(Z_AXIS, offs);
91 91
       #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
92
-        if (!parser.seen('P') || parser.value_bool()) mod_zprobe_zoffset(offs);
92
+        if (!parser.seen('P') || parser.value_bool()) mod_probe_offset(offs);
93 93
       #endif
94 94
     }
95 95
   #endif
@@ -98,17 +98,17 @@ void GcodeSuite::M290() {
98 98
     SERIAL_ECHO_START();
99 99
 
100 100
     #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
101
-      SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET " " MSG_Z, probe_offset[Z_AXIS]);
101
+      SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET " " MSG_Z, probe_offset.z);
102 102
     #endif
103 103
 
104 104
     #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
105 105
     {
106 106
       SERIAL_ECHOLNPAIR("Hotend ", int(active_extruder), "Offset"
107 107
         #if ENABLED(BABYSTEP_XY)
108
-          " X", hotend_offset[X_AXIS][active_extruder],
109
-          " Y", hotend_offset[Y_AXIS][active_extruder],
108
+          " X", hotend_offset[active_extruder].x,
109
+          " Y", hotend_offset[active_extruder].y,
110 110
         #endif
111
-        " Z", hotend_offset[Z_AXIS][active_extruder]
111
+        " Z", hotend_offset[active_extruder].z
112 112
       );
113 113
     }
114 114
     #endif

+ 5
- 6
Marlin/src/gcode/probe/G30.cpp View File

@@ -39,10 +39,10 @@
39 39
  *   E   Engage the probe for each probe (default 1)
40 40
  */
41 41
 void GcodeSuite::G30() {
42
-  const float xpos = parser.linearval('X', current_position[X_AXIS] + probe_offset[X_AXIS]),
43
-              ypos = parser.linearval('Y', current_position[Y_AXIS] + probe_offset[Y_AXIS]);
42
+  const xy_pos_t pos = { parser.linearval('X', current_position.x + probe_offset.x),
43
+                         parser.linearval('Y', current_position.y + probe_offset.y) };
44 44
 
45
-  if (!position_is_reachable_by_probe(xpos, ypos)) return;
45
+  if (!position_is_reachable_by_probe(pos)) return;
46 46
 
47 47
   // Disable leveling so the planner won't mess with us
48 48
   #if HAS_LEVELING
@@ -52,10 +52,9 @@ void GcodeSuite::G30() {
52 52
   remember_feedrate_scaling_off();
53 53
 
54 54
   const ProbePtRaise raise_after = parser.boolval('E', true) ? PROBE_PT_STOW : PROBE_PT_NONE;
55
-  const float measured_z = probe_at_point(xpos, ypos, raise_after, 1);
56
-
55
+  const float measured_z = probe_at_point(pos, raise_after, 1);
57 56
   if (!isnan(measured_z))
58
-    SERIAL_ECHOLNPAIR("Bed X: ", FIXFLOAT(xpos), " Y: ", FIXFLOAT(ypos), " Z: ", FIXFLOAT(measured_z));
57
+    SERIAL_ECHOLNPAIR("Bed X: ", FIXFLOAT(pos.x), " Y: ", FIXFLOAT(pos.y), " Z: ", FIXFLOAT(measured_z));
59 58
 
60 59
   restore_feedrate_and_scaling();
61 60
 

+ 3
- 4
Marlin/src/gcode/probe/G38.cpp View File

@@ -48,7 +48,7 @@ inline bool G38_run_probe() {
48 48
 
49 49
   #if MULTIPLE_PROBING > 1
50 50
     // Get direction of move and retract
51
-    float retract_mm[XYZ];
51
+    xyz_float_t retract_mm;
52 52
     LOOP_XYZ(i) {
53 53
       const float dist = destination[i] - current_position[i];
54 54
       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() {
75 75
 
76 76
     #if MULTIPLE_PROBING > 1
77 77
       // Move away by the retract distance
78
-      set_destination_from_current();
79
-      LOOP_XYZ(i) destination[i] += retract_mm[i];
78
+      destination = current_position + retract_mm;
80 79
       endstops.enable(false);
81 80
       prepare_move_to_destination();
82 81
       planner.synchronize();
@@ -84,7 +83,7 @@ inline bool G38_run_probe() {
84 83
       REMEMBER(fr, feedrate_mm_s, feedrate_mm_s * 0.25);
85 84
 
86 85
       // Bump the target more slowly
87
-      LOOP_XYZ(i) destination[i] -= retract_mm[i] * 2;
86
+      destination -= retract_mm * 2;
88 87
 
89 88
       G38_single_probe(move_value);
90 89
     #endif

+ 6
- 6
Marlin/src/gcode/probe/M851.cpp View File

@@ -35,18 +35,18 @@ void GcodeSuite::M851() {
35 35
 
36 36
   // Show usage with no parameters
37 37
   if (!parser.seen("XYZ")) {
38
-    SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET " X", probe_offset[X_AXIS], " Y", probe_offset[Y_AXIS], " Z", probe_offset[Z_AXIS]);
38
+    SERIAL_ECHOLNPAIR(MSG_PROBE_OFFSET " X", probe_offset.x, " Y", probe_offset.y, " Z", probe_offset.z);
39 39
     return;
40 40
   }
41 41
 
42
-  float offs[XYZ] = { probe_offset[X_AXIS], probe_offset[Y_AXIS], probe_offset[Z_AXIS] };
42
+  xyz_pos_t offs = probe_offset;
43 43
 
44 44
   bool ok = true;
45 45
 
46 46
   if (parser.seenval('X')) {
47 47
     const float x = parser.value_float();
48 48
     if (WITHIN(x, -(X_BED_SIZE), X_BED_SIZE))
49
-      offs[X_AXIS] = x;
49
+      offs.x = x;
50 50
     else {
51 51
       SERIAL_ECHOLNPAIR("?X out of range (-", int(X_BED_SIZE), " to ", int(X_BED_SIZE), ")");
52 52
       ok = false;
@@ -56,7 +56,7 @@ void GcodeSuite::M851() {
56 56
   if (parser.seenval('Y')) {
57 57
     const float y = parser.value_float();
58 58
     if (WITHIN(y, -(Y_BED_SIZE), Y_BED_SIZE))
59
-      offs[Y_AXIS] = y;
59
+      offs.y = y;
60 60
     else {
61 61
       SERIAL_ECHOLNPAIR("?Y out of range (-", int(Y_BED_SIZE), " to ", int(Y_BED_SIZE), ")");
62 62
       ok = false;
@@ -66,7 +66,7 @@ void GcodeSuite::M851() {
66 66
   if (parser.seenval('Z')) {
67 67
     const float z = parser.value_float();
68 68
     if (WITHIN(z, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX))
69
-      offs[Z_AXIS] = z;
69
+      offs.z = z;
70 70
     else {
71 71
       SERIAL_ECHOLNPAIR("?Z out of range (", int(Z_PROBE_OFFSET_RANGE_MIN), " to ", int(Z_PROBE_OFFSET_RANGE_MAX), ")");
72 72
       ok = false;
@@ -74,7 +74,7 @@ void GcodeSuite::M851() {
74 74
   }
75 75
 
76 76
   // Save the new offsets
77
-  if (ok) COPY(probe_offset, offs);
77
+  if (ok) probe_offset = offs;
78 78
 }
79 79
 
80 80
 #endif // HAS_BED_PROBE

+ 1
- 1
Marlin/src/gcode/scara/M360-M364.cpp View File

@@ -32,7 +32,7 @@
32 32
 inline bool SCARA_move_to_cal(const uint8_t delta_a, const uint8_t delta_b) {
33 33
   if (IsRunning()) {
34 34
     forward_kinematics_SCARA(delta_a, delta_b);
35
-    do_blocking_move_to_xy(cartes[X_AXIS], cartes[Y_AXIS]);
35
+    do_blocking_move_to_xy(cartes);
36 36
     return true;
37 37
   }
38 38
   return false;

+ 6
- 5
Marlin/src/inc/Conditionals_post.h View File

@@ -1472,7 +1472,7 @@
1472 1472
   #define _PROBE_RADIUS (DELTA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
1473 1473
   #ifndef DELTA_CALIBRATION_RADIUS
1474 1474
     #ifdef NOZZLE_TO_PROBE_OFFSET
1475
-      #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)))
1475
+      #define DELTA_CALIBRATION_RADIUS (DELTA_PRINTABLE_RADIUS - _MAX(ABS(nozzle_to_probe_offset.x), ABS(nozzle_to_probe_offset.y), ABS(MIN_PROBE_EDGE)))
1476 1476
     #else
1477 1477
       #define DELTA_CALIBRATION_RADIUS _PROBE_RADIUS
1478 1478
     #endif
@@ -1506,6 +1506,7 @@
1506 1506
   #define PROBE_Y_MIN (Y_CENTER - (SCARA_PRINTABLE_RADIUS) + MIN_PROBE_EDGE_FRONT)
1507 1507
   #define PROBE_X_MAX (X_CENTER +  SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE_RIGHT))
1508 1508
   #define PROBE_Y_MAX (Y_CENTER +  SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE_BACK))
1509
+
1509 1510
 #endif
1510 1511
 
1511 1512
 #if ENABLED(SEGMENT_LEVELED_MOVES) && !defined(LEVELED_SEGMENT_LENGTH)
@@ -1532,10 +1533,10 @@
1532 1533
       #define _MESH_MAX_X (_MIN(X_MAX_BED - (MESH_INSET), X_MAX_POS))
1533 1534
       #define _MESH_MAX_Y (_MIN(Y_MAX_BED - (MESH_INSET), Y_MAX_POS))
1534 1535
     #else
1535
-      #define _MESH_MIN_X (_MAX(X_MIN_BED + MESH_INSET, X_MIN_POS + nozzle_to_probe_offset[X_AXIS]))
1536
-      #define _MESH_MIN_Y (_MAX(Y_MIN_BED + MESH_INSET, Y_MIN_POS + nozzle_to_probe_offset[Y_AXIS]))
1537
-      #define _MESH_MAX_X (_MIN(X_MAX_BED - (MESH_INSET), X_MAX_POS + nozzle_to_probe_offset[X_AXIS]))
1538
-      #define _MESH_MAX_Y (_MIN(Y_MAX_BED - (MESH_INSET), Y_MAX_POS + nozzle_to_probe_offset[Y_AXIS]))
1536
+      #define _MESH_MIN_X (_MAX(X_MIN_BED + MESH_INSET, X_MIN_POS + nozzle_to_probe_offset.x))
1537
+      #define _MESH_MIN_Y (_MAX(Y_MIN_BED + MESH_INSET, Y_MIN_POS + nozzle_to_probe_offset.y))
1538
+      #define _MESH_MAX_X (_MIN(X_MAX_BED - (MESH_INSET), X_MAX_POS + nozzle_to_probe_offset.x))
1539
+      #define _MESH_MAX_Y (_MIN(Y_MAX_BED - (MESH_INSET), Y_MAX_POS + nozzle_to_probe_offset.y))
1539 1540
     #endif
1540 1541
   #endif
1541 1542
 

+ 1
- 1
Marlin/src/inc/MarlinConfig.h View File

@@ -39,7 +39,7 @@
39 39
 #include HAL_PATH(../HAL, inc/SanityCheck.h)
40 40
 
41 41
 // Include all core headers
42
-#include "../core/enum.h"
42
+#include "../core/types.h"
43 43
 #include "../core/language.h"
44 44
 #include "../core/utility.h"
45 45
 #include "../core/serial.h"

+ 0
- 1
Marlin/src/inc/MarlinConfigPre.h View File

@@ -34,7 +34,6 @@
34 34
 
35 35
 #include "../core/boards.h"
36 36
 #include "../core/macros.h"
37
-#include "../core/millis_t.h"
38 37
 #include "Version.h"
39 38
 #include "../../Configuration.h"
40 39
 

+ 2
- 5
Marlin/src/inc/SanityCheck.h View File

@@ -402,6 +402,8 @@
402 402
   #error "[XYZ]_PROBE_OFFSET_FROM_EXTRUDER is now NOZZLE_TO_PROBE_OFFSET. Please update your configuration."
403 403
 #elif defined(MIN_PROBE_X) || defined(MIN_PROBE_Y) || defined(MAX_PROBE_X) || defined(MAX_PROBE_Y)
404 404
   #error "(MIN|MAX)_PROBE_[XY] are now calculated at runtime. Please remove them from Configuration.h."
405
+#elif defined(Z_STEPPER_ALIGN_X) || defined(Z_STEPPER_ALIGN_X)
406
+  #error "Z_STEPPER_ALIGN_X and Z_STEPPER_ALIGN_Y are now combined as Z_STEPPER_ALIGN_XY. Please update your Configuration_adv.h."
405 407
 #endif
406 408
 
407 409
 #define BOARD_MKS_13        -1000
@@ -2305,11 +2307,6 @@ static_assert(   _ARR_TEST(3,0) && _ARR_TEST(3,1) && _ARR_TEST(3,2)
2305 2307
   #elif !HAS_BED_PROBE
2306 2308
     #error "Z_STEPPER_AUTO_ALIGN requires a Z-bed probe."
2307 2309
   #endif
2308
-  constexpr float sanity_arr_z_align_x[] = Z_STEPPER_ALIGN_X, sanity_arr_z_align_y[] = Z_STEPPER_ALIGN_Y;
2309
-  static_assert(
2310
-    COUNT(sanity_arr_z_align_x) == Z_STEPPER_COUNT && COUNT(sanity_arr_z_align_y) == Z_STEPPER_COUNT,
2311
-    "Z_STEPPER_ALIGN_[XY] settings require one element per Z stepper."
2312
-  );
2313 2310
 #endif
2314 2311
 
2315 2312
 #if ENABLED(PRINTCOUNTER) && DISABLED(EEPROM_SETTINGS)

+ 9
- 11
Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp View File

@@ -817,11 +817,10 @@ void MarlinUI::draw_status_screen() {
817 817
 
818 818
           #else
819 819
 
820
-            _draw_axis_value(X_AXIS, ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS])), blink);
821
-
820
+            xy_pos_t lpos = current_position; toLogical(lpos);
821
+            _draw_axis_value(X_AXIS, ftostr4sign(lpos.x), blink);
822 822
             lcd_put_wchar(' ');
823
-
824
-            _draw_axis_value(Y_AXIS, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS])), blink);
823
+            _draw_axis_value(Y_AXIS, ftostr4sign(lpos.y), blink);
825 824
 
826 825
           #endif
827 826
 
@@ -830,7 +829,7 @@ void MarlinUI::draw_status_screen() {
830 829
       #endif // LCD_WIDTH >= 20
831 830
 
832 831
       lcd_moveto(LCD_WIDTH - 8, 1);
833
-      _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position[Z_AXIS])), blink);
832
+      _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position.z)), blink);
834 833
 
835 834
       #if HAS_LEVELING && !HAS_HEATED_BED
836 835
         lcd_put_wchar(planner.leveling_active || blink ? '_' : ' ');
@@ -902,7 +901,7 @@ void MarlinUI::draw_status_screen() {
902 901
     // Z Coordinate
903 902
     //
904 903
     lcd_moveto(LCD_WIDTH - 9, 0);
905
-    _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position[Z_AXIS])), blink);
904
+    _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position.z)), blink);
906 905
 
907 906
     #if HAS_LEVELING && (HOTENDS > 1 || !HAS_HEATED_BED)
908 907
       lcd_put_wchar(LCD_WIDTH - 1, 0, planner.leveling_active || blink ? '_' : ' ');
@@ -1189,10 +1188,9 @@ void MarlinUI::draw_status_screen() {
1189 1188
          * Show X and Y positions
1190 1189
          */
1191 1190
         _XLABEL(_PLOT_X, 0);
1192
-        lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]))));
1193
-
1191
+        lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(ubl.mesh_index_to_xpos(x_plot))));
1194 1192
         _YLABEL(_LCD_W_POS, 0);
1195
-        lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]))));
1193
+        lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(ubl.mesh_index_to_ypos(y_plot))));
1196 1194
 
1197 1195
         lcd_moveto(_PLOT_X, 0);
1198 1196
 
@@ -1395,9 +1393,9 @@ void MarlinUI::draw_status_screen() {
1395 1393
          * Show all values at right of screen
1396 1394
          */
1397 1395
         _XLABEL(_LCD_W_POS, 1);
1398
-        lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]))));
1396
+        lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(ubl.mesh_index_to_xpos(x_plot))));
1399 1397
         _YLABEL(_LCD_W_POS, 2);
1400
-        lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]))));
1398
+        lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(ubl.mesh_index_to_ypos(y_plot))));
1401 1399
 
1402 1400
         /**
1403 1401
          * Show the location value

+ 4
- 3
Marlin/src/lcd/dogm/status_screen_DOGM.cpp View File

@@ -345,9 +345,10 @@ void MarlinUI::draw_status_screen() {
345 345
       #endif
346 346
       heat_bits = new_bits;
347 347
     #endif
348
-    strcpy(xstring, ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS])));
349
-    strcpy(ystring, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS])));
350
-    strcpy(zstring, ftostr52sp( LOGICAL_Z_POSITION(current_position[Z_AXIS])));
348
+    const xyz_pos_t lpos = current_position.asLogical();
349
+    strcpy(xstring, ftostr4sign(lpos.x));
350
+    strcpy(ystring, ftostr4sign(lpos.y));
351
+    strcpy(zstring, ftostr52sp( lpos.z));
351 352
     #if ENABLED(FILAMENT_LCD_DISPLAY)
352 353
       strcpy(wstring, ftostr12ns(filwidth.measured_mm));
353 354
       strcpy(mstring, i16tostr3(planner.volumetric_percent(parser.volumetric_enabled)));

+ 6
- 6
Marlin/src/lcd/dogm/status_screen_lite_ST7920.cpp View File

@@ -660,7 +660,7 @@ void ST7920_Lite_Status_Screen::draw_status_message() {
660 660
   #endif
661 661
 }
662 662
 
663
-void ST7920_Lite_Status_Screen::draw_position(const float (&pos)[XYZE], const bool position_known) {
663
+void ST7920_Lite_Status_Screen::draw_position(const xyz_pos_t &pos, const bool position_known) {
664 664
   char str[7];
665 665
   set_ddram_address(DDRAM_LINE_4);
666 666
   begin_data();
@@ -669,13 +669,13 @@ void ST7920_Lite_Status_Screen::draw_position(const float (&pos)[XYZE], const bo
669 669
   const unsigned char alt_label = position_known ? 0 : (ui.get_blink() ? ' ' : 0);
670 670
 
671 671
   write_byte(alt_label ? alt_label : 'X');
672
-  write_str(dtostrf(pos[X_AXIS], -4, 0, str), 4);
672
+  write_str(dtostrf(pos.x, -4, 0, str), 4);
673 673
 
674 674
   write_byte(alt_label ? alt_label : 'Y');
675
-  write_str(dtostrf(pos[Y_AXIS], -4, 0, str), 4);
675
+  write_str(dtostrf(pos.y, -4, 0, str), 4);
676 676
 
677 677
   write_byte(alt_label ? alt_label : 'Z');
678
-  write_str(dtostrf(pos[Z_AXIS], -5, 1, str), 5);
678
+  write_str(dtostrf(pos.z, -5, 1, str), 5);
679 679
 }
680 680
 
681 681
 bool ST7920_Lite_Status_Screen::indicators_changed() {
@@ -750,8 +750,8 @@ void ST7920_Lite_Status_Screen::update_indicators(const bool forceUpdate) {
750 750
 }
751 751
 
752 752
 bool ST7920_Lite_Status_Screen::position_changed() {
753
-  const float x_pos = current_position[X_AXIS], y_pos = current_position[Y_AXIS], z_pos = current_position[Z_AXIS];
754
-  const uint8_t checksum = uint8_t(x_pos) ^ uint8_t(y_pos) ^ uint8_t(z_pos);
753
+  const xyz_pos_t pos = current_position;
754
+  const uint8_t checksum = uint8_t(pos.x) ^ uint8_t(pos.y) ^ uint8_t(pos.z);
755 755
   static uint8_t last_checksum = 0, changed = last_checksum != checksum;
756 756
   if (changed) last_checksum = checksum;
757 757
   return changed;

+ 2
- 1
Marlin/src/lcd/dogm/status_screen_lite_ST7920.h View File

@@ -17,6 +17,7 @@
17 17
 
18 18
 #include "../../HAL/shared/HAL_ST7920.h"
19 19
 
20
+#include "../../core/types.h"
20 21
 #include "../../core/macros.h"
21 22
 #include "../../libs/duration_t.h"
22 23
 
@@ -86,7 +87,7 @@ class ST7920_Lite_Status_Screen {
86 87
     static void draw_print_time(const duration_t &elapsed);
87 88
     static void draw_feedrate_percentage(const uint16_t percentage);
88 89
     static void draw_status_message();
89
-    static void draw_position(const float (&pos)[XYZE], bool position_known = true);
90
+    static void draw_position(const xyz_pos_t &pos, bool position_known = true);
90 91
 
91 92
     static bool indicators_changed();
92 93
     static bool position_changed();

+ 4
- 2
Marlin/src/lcd/dogm/ultralcd_DOGM.cpp View File

@@ -547,10 +547,12 @@ void MarlinUI::clear_lcd() { } // Automatically cleared by Picture Loop
547 547
       // Show X and Y positions at top of screen
548 548
       u8g.setColorIndex(1);
549 549
       if (PAGE_UNDER(7)) {
550
+        const xy_pos_t pos = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) },
551
+                       lpos = pos.asLogical();
550 552
         lcd_put_u8str(5, 7, "X:");
551
-        lcd_put_u8str(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]))));
553
+        lcd_put_u8str(ftostr52(lpos.x));
552 554
         lcd_put_u8str(74, 7, "Y:");
553
-        lcd_put_u8str(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]))));
555
+        lcd_put_u8str(ftostr52(lpos.y));
554 556
       }
555 557
 
556 558
       // Print plot position

+ 4
- 4
Marlin/src/lcd/extensible_ui/lib/dgus/DGUSDisplayDefinition.cpp View File

@@ -169,7 +169,7 @@ const struct DGUS_VP_Variable ListOfVP[] PROGMEM = {
169 169
     VPHELPER(VP_T_E1_Is, &thermalManager.temp_hotend[0].celsius, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<0>),
170 170
     VPHELPER(VP_T_E1_Set, &thermalManager.temp_hotend[0].target, DGUSScreenVariableHandler::HandleTemperatureChanged, &DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay),
171 171
     VPHELPER(VP_Flowrate_E1, nullptr, DGUSScreenVariableHandler::HandleFlowRateChanged, &DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay),
172
-    VPHELPER(VP_EPos, &destination[3], nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>),
172
+    VPHELPER(VP_EPos, &destination.e, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>),
173 173
     VPHELPER(VP_MOVE_E1, nullptr, &DGUSScreenVariableHandler::HandleManualExtrude, nullptr),
174 174
   #endif
175 175
   #if HOTENDS >= 2
@@ -195,9 +195,9 @@ const struct DGUS_VP_Variable ListOfVP[] PROGMEM = {
195 195
   VPHELPER(VP_Feedrate_Percentage, &feedrate_percentage, DGUSScreenVariableHandler::DGUSLCD_SetValueDirectly<int16_t>, &DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay ),
196 196
 
197 197
   // Position Data.
198
-  VPHELPER(VP_XPos, &current_position[0], nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>),
199
-  VPHELPER(VP_YPos, &current_position[1], nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>),
200
-  VPHELPER(VP_ZPos, &current_position[2], nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>),
198
+  VPHELPER(VP_XPos, &current_position.x, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>),
199
+  VPHELPER(VP_YPos, &current_position.y, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>),
200
+  VPHELPER(VP_ZPos, &current_position.z, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendFloatAsLongValueToDisplay<2>),
201 201
 
202 202
   // Print Progress.
203 203
   VPHELPER(VP_PrintProgress_Percentage, &ui.progress_bar_percent, nullptr, DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay ),

+ 16
- 17
Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/bio_status_screen.cpp View File

@@ -258,22 +258,22 @@ bool StatusScreen::onTouchStart(uint8_t) {
258 258
 
259 259
 bool StatusScreen::onTouchEnd(uint8_t tag) {
260 260
   switch (tag) {
261
-    case 1:
262
-    case 2:
263
-    case 3:
264
-    case 4:
261
+    case  1:
262
+    case  2:
263
+    case  3:
264
+    case  4:
265 265
     case 12:
266 266
       if (!jog_xy) {
267 267
         jog_xy = true;
268 268
         injectCommands_P(PSTR("M17"));
269 269
       }
270
-      jog(0,  0,  0);
270
+      jog({ 0, 0, 0 });
271 271
       break;
272
-    case 5:
273
-    case 6:
274
-      jog(0,  0,  0);
272
+    case  5:
273
+    case  6:
274
+      jog({ 0, 0, 0 });
275 275
       break;
276
-    case 9:  GOTO_SCREEN(FilesScreen); break;
276
+    case  9: GOTO_SCREEN(FilesScreen); break;
277 277
     case 10: GOTO_SCREEN(MainMenu); break;
278 278
     case 13: SpinnerDialogBox::enqueueAndWait_P(F("G112"));  break;
279 279
     case 14: SpinnerDialogBox::enqueueAndWait_P(F("G28 Z")); break;
@@ -291,14 +291,13 @@ bool StatusScreen::onTouchHeld(uint8_t tag) {
291 291
   if (tag >= 1 && tag <= 4 && !jog_xy) return false;
292 292
   const float s = min_speed + (fine_motion ? 0 : (max_speed - min_speed) * sq(increment));
293 293
   switch (tag) {
294
-    case 1: jog(-s,  0,  0); break;
295
-    case 2: jog( s,  0,  0); break;
296
-    case 4: jog( 0, -s,  0); break; // NOTE: Y directions inverted because bed rather than needle moves
297
-    case 3: jog( 0,  s,  0); break;
298
-    case 5: jog( 0,  0, -s); break;
299
-    case 6: jog( 0,  0,  s); break;
300
-    case 7:
301
-    case 8:
294
+    case 1: jog({-s,  0,  0}); break;
295
+    case 2: jog({ s,  0,  0}); break;
296
+    case 4: jog({ 0, -s,  0}); break; // NOTE: Y directions inverted because bed rather than needle moves
297
+    case 3: jog({ 0,  s,  0}); break;
298
+    case 5: jog({ 0,  0, -s}); break;
299
+    case 6: jog({ 0,  0,  s}); break;
300
+    case 7: case 8:
302 301
     {
303 302
       if (ExtUI::isMoving()) return false;
304 303
       const feedRate_t feedrate = emin_speed + (fine_motion ? 0 : (emax_speed - emin_speed) * sq(increment));

+ 2
- 2
Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/change_filament_screen.cpp View File

@@ -305,8 +305,8 @@ bool ChangeFilamentScreen::onTouchEnd(uint8_t tag) {
305 305
 bool ChangeFilamentScreen::onTouchHeld(uint8_t tag) {
306 306
   if (ExtUI::isMoving()) return false; // Don't allow moves to accumulate
307 307
   constexpr float increment = 1;
308
-  #define UI_INCREMENT_AXIS(axis) MoveAxisScreen::setManualFeedrate(axis, increment); UI_INCREMENT(AxisPosition_mm, axis);
309
-  #define UI_DECREMENT_AXIS(axis) MoveAxisScreen::setManualFeedrate(axis, increment); UI_DECREMENT(AxisPosition_mm, axis);
308
+  #define UI_INCREMENT_AXIS(axis) UI_INCREMENT(AxisPosition_mm, axis);
309
+  #define UI_DECREMENT_AXIS(axis) UI_DECREMENT(AxisPosition_mm, axis);
310 310
   switch (tag) {
311 311
     case 5: case 7: UI_DECREMENT_AXIS(getExtruder()); break;
312 312
     case 6: case 8: UI_INCREMENT_AXIS(getExtruder()); break;

+ 2
- 2
Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/move_axis_screen.cpp View File

@@ -110,8 +110,8 @@ float MoveAxisScreen::getManualFeedrate(uint8_t axis, float increment_mm) {
110 110
   // Compute feedrate so that the tool lags the adjuster when it is
111 111
   // being held down, this allows enough margin for the planner to
112 112
   // connect segments and even out the motion.
113
-  constexpr float manual_feedrate[XYZE] = MANUAL_FEEDRATE;
114
-  return min(manual_feedrate[axis] / 60.0f, abs(increment_mm * (TOUCH_REPEATS_PER_SECOND) * 0.80f));
113
+  constexpr xyze_feedrate_t max_manual_feedrate = MANUAL_FEEDRATE;
114
+  return min(max_manual_feedrate[axis] / 60.0f, abs(increment_mm * (TOUCH_REPEATS_PER_SECOND) * 0.80f));
115 115
 }
116 116
 
117 117
 void MoveAxisScreen::setManualFeedrate(ExtUI::axis_t axis, float increment_mm) {

+ 11
- 12
Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/nudge_nozzle_screen.cpp View File

@@ -36,9 +36,8 @@ void NudgeNozzleScreen::onEntry() {
36 36
   #if EXTRUDERS > 1
37 37
     screen_data.NudgeNozzleScreen.link_nozzles = true;
38 38
   #endif
39
-  LOOP_XYZ(i) {
40
-    screen_data.NudgeNozzleScreen.rel[i] = 0;
41
-  }
39
+  screen_data.NudgeNozzleScreen.rel.reset();
40
+
42 41
   BaseNumericAdjustmentScreen::onEntry();
43 42
 }
44 43
 
@@ -48,10 +47,10 @@ void NudgeNozzleScreen::onRedraw(draw_mode_t what) {
48 47
 
49 48
   w.heading(                   GET_TEXTF(NUDGE_NOZZLE));
50 49
   #if ENABLED(BABYSTEP_XY)
51
-  w.color(x_axis).adjuster(2,  GET_TEXTF(AXIS_X), screen_data.NudgeNozzleScreen.rel[0] / getAxisSteps_per_mm(X));
52
-  w.color(y_axis).adjuster(4,  GET_TEXTF(AXIS_Y), screen_data.NudgeNozzleScreen.rel[1] / getAxisSteps_per_mm(Y));
50
+  w.color(x_axis).adjuster(2,  GET_TEXTF(AXIS_X), screen_data.NudgeNozzleScreen.rel.x / getAxisSteps_per_mm(X));
51
+  w.color(y_axis).adjuster(4,  GET_TEXTF(AXIS_Y), screen_data.NudgeNozzleScreen.rel.y / getAxisSteps_per_mm(Y));
53 52
   #endif
54
-  w.color(z_axis).adjuster(6,  GET_TEXTF(AXIS_Z), screen_data.NudgeNozzleScreen.rel[2] / getAxisSteps_per_mm(Z));
53
+  w.color(z_axis).adjuster(6,  GET_TEXTF(AXIS_Z), screen_data.NudgeNozzleScreen.rel.z / getAxisSteps_per_mm(Z));
55 54
   w.increments();
56 55
   #if EXTRUDERS > 1
57 56
     w.toggle  (8,  GET_TEXTF(ADJUST_BOTH_NOZZLES), screen_data.NudgeNozzleScreen.link_nozzles);
@@ -90,12 +89,12 @@ bool NudgeNozzleScreen::onTouchHeld(uint8_t tag) {
90 89
   #endif
91 90
   int16_t steps;
92 91
   switch (tag) {
93
-    case  2: steps = mmToWholeSteps(inc, X); smartAdjustAxis_steps(-steps, X, link); screen_data.NudgeNozzleScreen.rel[0] -= steps; break;
94
-    case  3: steps = mmToWholeSteps(inc, X); smartAdjustAxis_steps( steps, X, link); screen_data.NudgeNozzleScreen.rel[0] += steps; break;
95
-    case  4: steps = mmToWholeSteps(inc, Y); smartAdjustAxis_steps(-steps, Y, link); screen_data.NudgeNozzleScreen.rel[1] -= steps; break;
96
-    case  5: steps = mmToWholeSteps(inc, Y); smartAdjustAxis_steps( steps, Y, link); screen_data.NudgeNozzleScreen.rel[1] += steps; break;
97
-    case  6: steps = mmToWholeSteps(inc, Z); smartAdjustAxis_steps(-steps, Z, link); screen_data.NudgeNozzleScreen.rel[2] -= steps; break;
98
-    case  7: steps = mmToWholeSteps(inc, Z); smartAdjustAxis_steps( steps, Z, link); screen_data.NudgeNozzleScreen.rel[2] += steps; break;
92
+    case  2: steps = mmToWholeSteps(inc, X); smartAdjustAxis_steps(-steps, X, link); screen_data.NudgeNozzleScreen.rel.x -= steps; break;
93
+    case  3: steps = mmToWholeSteps(inc, X); smartAdjustAxis_steps( steps, X, link); screen_data.NudgeNozzleScreen.rel.x += steps; break;
94
+    case  4: steps = mmToWholeSteps(inc, Y); smartAdjustAxis_steps(-steps, Y, link); screen_data.NudgeNozzleScreen.rel.y -= steps; break;
95
+    case  5: steps = mmToWholeSteps(inc, Y); smartAdjustAxis_steps( steps, Y, link); screen_data.NudgeNozzleScreen.rel.y += steps; break;
96
+    case  6: steps = mmToWholeSteps(inc, Z); smartAdjustAxis_steps(-steps, Z, link); screen_data.NudgeNozzleScreen.rel.z -= steps; break;
97
+    case  7: steps = mmToWholeSteps(inc, Z); smartAdjustAxis_steps( steps, Z, link); screen_data.NudgeNozzleScreen.rel.z += steps; break;
99 98
     #if EXTRUDERS > 1
100 99
       case  8: screen_data.NudgeNozzleScreen.link_nozzles = !link; break;
101 100
     #endif

+ 1
- 1
Marlin/src/lcd/extensible_ui/lib/lulzbot/screens/screen_data.h View File

@@ -65,7 +65,7 @@ union screen_data_t {
65 65
 #if ENABLED(BABYSTEPPING)
66 66
   struct {
67 67
     struct base_numeric_adjustment_t placeholder;
68
-    int16_t rel[XYZ];
68
+    xyz_int_t rel;
69 69
     #if EXTRUDERS > 1
70 70
       bool link_nozzles;
71 71
     #endif

+ 34
- 40
Marlin/src/lcd/extensible_ui/ui_api.cpp View File

@@ -204,33 +204,29 @@ namespace ExtUI {
204 204
      * The axis will continue to jog until this function is
205 205
      * called with all zeros.
206 206
      */
207
-    void jog(float dx, float dy, float dz) {
207
+    void jog(const xyz_float_t &dir) {
208 208
       // The "destination" variable is used as a scratchpad in
209 209
       // Marlin by GCODE routines, but should remain untouched
210 210
       // during manual jogging, allowing us to reuse the space
211 211
       // for our direction vector.
212
-      destination[X] = dx;
213
-      destination[Y] = dy;
214
-      destination[Z] = dz;
215
-      flags.jogging = !NEAR_ZERO(dx) || !NEAR_ZERO(dy) || !NEAR_ZERO(dz);
212
+      destination = dir;
213
+      flags.jogging = !NEAR_ZERO(dir.x) || !NEAR_ZERO(dir.y) || !NEAR_ZERO(dir.z);
216 214
     }
217 215
 
218 216
     // Called by the polling routine in "joystick.cpp"
219
-    void _joystick_update(float (&norm_jog)[XYZ]) {
217
+    void _joystick_update(xyz_float_t &norm_jog) {
220 218
       if (flags.jogging) {
221 219
         #define OUT_OF_RANGE(VALUE) (VALUE < -1.0f || VALUE > 1.0f)
222 220
 
223
-        if (OUT_OF_RANGE(destination[X_AXIS]) || OUT_OF_RANGE(destination[Y_AXIS]) || OUT_OF_RANGE(destination[Z_AXIS])) {
224
-          // If destination[] on any axis is out of range, it
221
+        if (OUT_OF_RANGE(destination.x) || OUT_OF_RANGE(destination.y) || OUT_OF_RANGE(destination.z)) {
222
+          // If destination on any axis is out of range, it
225 223
           // probably means the UI forgot to stop jogging and
226
-          // ran GCODE that wrote a position to destination[].
224
+          // ran GCODE that wrote a position to destination.
227 225
           // To prevent a disaster, stop jogging.
228 226
           flags.jogging = false;
229 227
           return;
230 228
         }
231
-        norm_jog[X_AXIS] = destination[X_AXIS];
232
-        norm_jog[Y_AXIS] = destination[Y_AXIS];
233
-        norm_jog[Z_AXIS] = destination[Z_AXIS];
229
+        norm_jog = destination;
234 230
       }
235 231
     }
236 232
   #endif
@@ -328,18 +324,16 @@ namespace ExtUI {
328 324
   float getAxisPosition_mm(const extruder_t extruder) {
329 325
     const extruder_t old_tool = getActiveTool();
330 326
     setActiveTool(extruder, true);
331
-    const float pos = (
327
+    const float epos = (
332 328
       #if ENABLED(JOYSTICK)
333
-        flags.jogging ? destination[E_AXIS] :
329
+        flags.jogging ? destination.e :
334 330
       #endif
335
-      current_position[E_AXIS]
331
+      current_position.e
336 332
     );
337 333
     setActiveTool(old_tool, true);
338
-    return pos;
334
+    return epos;
339 335
   }
340 336
 
341
-  constexpr feedRate_t manual_feedrate_mm_m[XYZE] = MANUAL_FEEDRATE;
342
-
343 337
   void setAxisPosition_mm(const float position, const axis_t axis) {
344 338
     // Start with no limits to movement
345 339
     float min = current_position[axis] - 1000,
@@ -350,26 +344,26 @@ namespace ExtUI {
350 344
       if (soft_endstops_enabled) switch (axis) {
351 345
         case X_AXIS:
352 346
           #if ENABLED(MIN_SOFTWARE_ENDSTOP_X)
353
-            min = soft_endstop[X_AXIS].min;
347
+            min = soft_endstop.min.x;
354 348
           #endif
355 349
           #if ENABLED(MAX_SOFTWARE_ENDSTOP_X)
356
-            max = soft_endstop[X_AXIS].max;
350
+            max = soft_endstop.max.x;
357 351
           #endif
358 352
           break;
359 353
         case Y_AXIS:
360 354
           #if ENABLED(MIN_SOFTWARE_ENDSTOP_Y)
361
-            min = soft_endstop[Y_AXIS].min;
355
+            min = soft_endstop.min.y;
362 356
           #endif
363 357
           #if ENABLED(MAX_SOFTWARE_ENDSTOP_Y)
364
-            max = soft_endstop[Y_AXIS].max;
358
+            max = soft_endstop.max.y;
365 359
           #endif
366 360
           break;
367 361
         case Z_AXIS:
368 362
           #if ENABLED(MIN_SOFTWARE_ENDSTOP_Z)
369
-            min = soft_endstop[Z_AXIS].min;
363
+            min = soft_endstop.min.z;
370 364
           #endif
371 365
           #if ENABLED(MAX_SOFTWARE_ENDSTOP_Z)
372
-            max = soft_endstop[Z_AXIS].max;
366
+            max = soft_endstop.max.z;
373 367
           #endif
374 368
         default: break;
375 369
       }
@@ -391,8 +385,8 @@ namespace ExtUI {
391 385
   void setAxisPosition_mm(const float position, const extruder_t extruder) {
392 386
     setActiveTool(extruder, true);
393 387
 
394
-    current_position[E_AXIS] = position;
395
-    line_to_current_position(MMM_TO_MMS(manual_feedrate_mm_m[E_AXIS]));
388
+    current_position.e = position;
389
+    line_to_current_position(MMM_TO_MMS(manual_feedrate_mm_m.e));
396 390
   }
397 391
 
398 392
   void setActiveTool(const extruder_t extruder, bool no_move) {
@@ -652,7 +646,7 @@ namespace ExtUI {
652 646
     }
653 647
 
654 648
     float getAxisMaxJerk_mm_s(const extruder_t) {
655
-      return planner.max_jerk[E_AXIS];
649
+      return planner.max_jerk.e;
656 650
     }
657 651
 
658 652
     void setAxisMaxJerk_mm_s(const float value, const axis_t axis) {
@@ -660,7 +654,7 @@ namespace ExtUI {
660 654
     }
661 655
 
662 656
     void setAxisMaxJerk_mm_s(const float value, const extruder_t) {
663
-      planner.max_jerk[E_AXIS] = value;
657
+      planner.max_jerk.e = value;
664 658
     }
665 659
   #endif
666 660
 
@@ -710,7 +704,7 @@ namespace ExtUI {
710 704
           #if EXTRUDERS > 1
711 705
             && (linked_nozzles || active_extruder == 0)
712 706
           #endif
713
-        ) probe_offset[Z_AXIS] += mm;
707
+        ) probe_offset.z += mm;
714 708
       #else
715 709
         UNUSED(mm);
716 710
       #endif
@@ -724,7 +718,7 @@ namespace ExtUI {
724 718
         if (!linked_nozzles) {
725 719
           HOTEND_LOOP()
726 720
             if (e != active_extruder)
727
-              hotend_offset[axis][e] += mm;
721
+              hotend_offset[e][axis] += mm;
728 722
 
729 723
           normalizeNozzleOffset(X);
730 724
           normalizeNozzleOffset(Y);
@@ -748,7 +742,7 @@ namespace ExtUI {
748 742
 
749 743
   float getZOffset_mm() {
750 744
     #if HAS_BED_PROBE
751
-      return probe_offset[Z_AXIS];
745
+      return probe_offset.z;
752 746
     #elif ENABLED(BABYSTEP_DISPLAY_TOTAL)
753 747
       return babystep.axis_total[BS_TOTAL_AXIS(Z_AXIS) + 1];
754 748
     #else
@@ -759,7 +753,7 @@ namespace ExtUI {
759 753
   void setZOffset_mm(const float value) {
760 754
     #if HAS_BED_PROBE
761 755
       if (WITHIN(value, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX))
762
-        probe_offset[Z_AXIS] = value;
756
+        probe_offset.z = value;
763 757
     #elif ENABLED(BABYSTEP_DISPLAY_TOTAL)
764 758
       babystep.add_mm(Z_AXIS, (value - babystep.axis_total[BS_TOTAL_AXIS(Z_AXIS) + 1]));
765 759
     #else
@@ -771,12 +765,12 @@ namespace ExtUI {
771 765
 
772 766
     float getNozzleOffset_mm(const axis_t axis, const extruder_t extruder) {
773 767
       if (extruder - E0 >= HOTENDS) return 0;
774
-      return hotend_offset[axis][extruder - E0];
768
+      return hotend_offset[extruder - E0][axis];
775 769
     }
776 770
 
777 771
     void setNozzleOffset_mm(const float value, const axis_t axis, const extruder_t extruder) {
778 772
       if (extruder - E0 >= HOTENDS) return;
779
-      hotend_offset[axis][extruder - E0] = value;
773
+      hotend_offset[extruder - E0][axis] = value;
780 774
     }
781 775
 
782 776
     /**
@@ -785,8 +779,8 @@ namespace ExtUI {
785 779
      * user to edit the offset the first nozzle).
786 780
      */
787 781
     void normalizeNozzleOffset(const axis_t axis) {
788
-      const float offs = hotend_offset[axis][0];
789
-      HOTEND_LOOP() hotend_offset[axis][e] -= offs;
782
+      const float offs = hotend_offset[0][axis];
783
+      HOTEND_LOOP() hotend_offset[e][axis] -= offs;
790 784
     }
791 785
 
792 786
   #endif // HAS_HOTEND_OFFSET
@@ -820,10 +814,10 @@ namespace ExtUI {
820 814
     bool getMeshValid() { return leveling_is_valid(); }
821 815
     #if HAS_MESH
822 816
       bed_mesh_t& getMeshArray() { return Z_VALUES_ARR; }
823
-      float getMeshPoint(const uint8_t xpos, const uint8_t ypos) { return Z_VALUES(xpos,ypos); }
824
-      void setMeshPoint(const uint8_t xpos, const uint8_t ypos, const float zoff) {
825
-        if (WITHIN(xpos, 0, GRID_MAX_POINTS_X) && WITHIN(ypos, 0, GRID_MAX_POINTS_Y)) {
826
-          Z_VALUES(xpos, ypos) = zoff;
817
+      float getMeshPoint(const xy_uint8_t &pos) { return Z_VALUES(pos.x, pos.y); }
818
+      void setMeshPoint(const xy_uint8_t &pos, const float zoff) {
819
+        if (WITHIN(pos.x, 0, GRID_MAX_POINTS_X) && WITHIN(pos.y, 0, GRID_MAX_POINTS_Y)) {
820
+          Z_VALUES(pos.x, pos.y) = zoff;
827 821
           #if ENABLED(ABL_BILINEAR_SUBDIVISION)
828 822
             bed_level_virt_interpolate();
829 823
           #endif

+ 5
- 4
Marlin/src/lcd/extensible_ui/ui_api.h View File

@@ -81,8 +81,8 @@ namespace ExtUI {
81 81
   void enableHeater(const extruder_t);
82 82
 
83 83
   #if ENABLED(JOYSTICK)
84
-    void jog(float dx, float dy, float dz);
85
-    void _joystick_update(float (&norm_jog)[XYZ]);
84
+    void jog(const xyz_float_t &dir);
85
+    void _joystick_update(xyz_float_t &norm_jog);
86 86
   #endif
87 87
 
88 88
   /**
@@ -135,9 +135,10 @@ namespace ExtUI {
135 135
     bool getMeshValid();
136 136
     #if HAS_MESH
137 137
       bed_mesh_t& getMeshArray();
138
-      float getMeshPoint(const uint8_t xpos, const uint8_t ypos);
139
-      void setMeshPoint(const uint8_t xpos, const uint8_t ypos, const float zval);
138
+      float getMeshPoint(const xy_uint8_t &pos);
139
+      void setMeshPoint(const xy_uint8_t &pos, const float zval);
140 140
       void onMeshUpdate(const uint8_t xpos, const uint8_t ypos, const float zval);
141
+      inline void onMeshUpdate(const xy_uint8_t &pos, const float zval) { setMeshPoint(pos, zval); }
141 142
     #endif
142 143
   #endif
143 144
 

+ 9
- 9
Marlin/src/lcd/menu/menu.cpp View File

@@ -379,8 +379,8 @@ void scroll_screen(const uint8_t limit, const bool is_menu) {
379 379
 #if HAS_LINE_TO_Z
380 380
 
381 381
   void line_to_z(const float &z) {
382
-    current_position[Z_AXIS] = z;
383
-    planner.buffer_line(current_position, MMM_TO_MMS(manual_feedrate_mm_m[Z_AXIS]), active_extruder);
382
+    current_position.z = z;
383
+    line_to_current_position(MMM_TO_MMS(manual_feedrate_mm_m.z));
384 384
   }
385 385
 
386 386
 #endif
@@ -402,10 +402,10 @@ void scroll_screen(const uint8_t limit, const bool is_menu) {
402 402
       ui.encoderPosition = 0;
403 403
 
404 404
       const float diff = planner.steps_to_mm[Z_AXIS] * babystep_increment,
405
-                  new_probe_offset = probe_offset[Z_AXIS] + diff,
405
+                  new_probe_offset = probe_offset.z + diff,
406 406
                   new_offs =
407 407
                     #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
408
-                      do_probe ? new_probe_offset : hotend_offset[Z_AXIS][active_extruder] - diff
408
+                      do_probe ? new_probe_offset : hotend_offset[active_extruder].z - diff
409 409
                     #else
410 410
                       new_probe_offset
411 411
                     #endif
@@ -414,9 +414,9 @@ void scroll_screen(const uint8_t limit, const bool is_menu) {
414 414
 
415 415
         babystep.add_steps(Z_AXIS, babystep_increment);
416 416
 
417
-        if (do_probe) probe_offset[Z_AXIS] = new_offs;
417
+        if (do_probe) probe_offset.z = new_offs;
418 418
         #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
419
-          else hotend_offset[Z_AXIS][active_extruder] = new_offs;
419
+          else hotend_offset[active_extruder].z = new_offs;
420 420
         #endif
421 421
 
422 422
         ui.refresh(LCDVIEW_CALL_REDRAW_NEXT);
@@ -425,13 +425,13 @@ void scroll_screen(const uint8_t limit, const bool is_menu) {
425 425
     if (ui.should_draw()) {
426 426
       #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
427 427
         if (!do_probe)
428
-          draw_edit_screen(PSTR(MSG_Z_OFFSET), ftostr43sign(hotend_offset[Z_AXIS][active_extruder]));
428
+          draw_edit_screen(PSTR(MSG_Z_OFFSET), ftostr43sign(hotend_offset[active_extruder].z));
429 429
         else
430 430
       #endif
431
-          draw_edit_screen(PSTR(MSG_ZPROBE_ZOFFSET), ftostr43sign(probe_offset[Z_AXIS]));
431
+          draw_edit_screen(PSTR(MSG_ZPROBE_ZOFFSET), ftostr43sign(probe_offset.z));
432 432
 
433 433
       #if ENABLED(BABYSTEP_ZPROBE_GFX_OVERLAY)
434
-        if (do_probe) _lcd_zoffset_overlay_gfx(probe_offset[Z_AXIS]);
434
+        if (do_probe) _lcd_zoffset_overlay_gfx(probe_offset.z);
435 435
       #endif
436 436
     }
437 437
   }

+ 2
- 2
Marlin/src/lcd/menu/menu_advanced.cpp View File

@@ -55,7 +55,7 @@ void menu_backlash();
55 55
 
56 56
   #include "../../feature/dac/stepper_dac.h"
57 57
 
58
-  uint8_t driverPercent[XYZE];
58
+  xyze_uint8_t driverPercent;
59 59
   inline void dac_driver_getValues() { LOOP_XYZE(i) driverPercent[i] = dac_current_get_percent((AxisEnum)i); }
60 60
   static void dac_driver_commit() { dac_current_set_percents(driverPercent); }
61 61
 
@@ -552,7 +552,7 @@ void menu_backlash();
552 552
       #if ENABLED(DELTA)
553 553
         EDIT_JERK(C);
554 554
       #else
555
-        MENU_MULTIPLIER_ITEM_EDIT(float52sign, MSG_VC_JERK, &planner.max_jerk[C_AXIS], 0.1f, 990);
555
+        MENU_MULTIPLIER_ITEM_EDIT(float52sign, MSG_VC_JERK, &planner.max_jerk.c, 0.1f, 990);
556 556
       #endif
557 557
       #if !BOTH(JUNCTION_DEVIATION, LIN_ADVANCE)
558 558
         EDIT_JERK(E);

+ 6
- 8
Marlin/src/lcd/menu/menu_bed_corners.cpp View File

@@ -58,26 +58,24 @@ static inline void _lcd_goto_next_corner() {
58 58
   line_to_z(LEVEL_CORNERS_Z_HOP);
59 59
   switch (bed_corner) {
60 60
     case 0:
61
-      current_position[X_AXIS] = X_MIN_BED + LEVEL_CORNERS_INSET;
62
-      current_position[Y_AXIS] = Y_MIN_BED + LEVEL_CORNERS_INSET;
61
+      current_position.set(X_MIN_BED + LEVEL_CORNERS_INSET, Y_MIN_BED + LEVEL_CORNERS_INSET);
63 62
       break;
64 63
     case 1:
65
-      current_position[X_AXIS] = X_MAX_BED - (LEVEL_CORNERS_INSET);
64
+      current_position.x = X_MAX_BED - (LEVEL_CORNERS_INSET);
66 65
       break;
67 66
     case 2:
68
-      current_position[Y_AXIS] = Y_MAX_BED - (LEVEL_CORNERS_INSET);
67
+      current_position.y = Y_MAX_BED - (LEVEL_CORNERS_INSET);
69 68
       break;
70 69
     case 3:
71
-      current_position[X_AXIS] = X_MIN_BED + LEVEL_CORNERS_INSET;
70
+      current_position.x = X_MIN_BED + LEVEL_CORNERS_INSET;
72 71
       break;
73 72
     #if ENABLED(LEVEL_CENTER_TOO)
74 73
       case 4:
75
-        current_position[X_AXIS] = X_CENTER;
76
-        current_position[Y_AXIS] = Y_CENTER;
74
+        current_position.set(X_CENTER, Y_CENTER);
77 75
         break;
78 76
     #endif
79 77
   }
80
-  planner.buffer_line(current_position, MMM_TO_MMS(manual_feedrate_mm_m[X_AXIS]), active_extruder);
78
+  line_to_current_position(MMM_TO_MMS(manual_feedrate_mm_m.x));
81 79
   line_to_z(LEVEL_CORNERS_HEIGHT);
82 80
   if (++bed_corner > 3
83 81
     #if ENABLED(LEVEL_CENTER_TOO)

+ 3
- 3
Marlin/src/lcd/menu/menu_bed_leveling.cpp View File

@@ -121,7 +121,7 @@
121 121
     // Encoder knob or keypad buttons adjust the Z position
122 122
     //
123 123
     if (ui.encoderPosition) {
124
-      const float z = current_position[Z_AXIS] + float(int16_t(ui.encoderPosition)) * (MESH_EDIT_Z_STEP);
124
+      const float z = current_position.z + float(int16_t(ui.encoderPosition)) * (MESH_EDIT_Z_STEP);
125 125
       line_to_z(constrain(z, -(LCD_PROBE_Z_RANGE) * 0.5f, (LCD_PROBE_Z_RANGE) * 0.5f));
126 126
       ui.refresh(LCDVIEW_CALL_REDRAW_NEXT);
127 127
       ui.encoderPosition = 0;
@@ -131,7 +131,7 @@
131 131
     // Draw on first display, then only on Z change
132 132
     //
133 133
     if (ui.should_draw()) {
134
-      const float v = current_position[Z_AXIS];
134
+      const float v = current_position.z;
135 135
       draw_edit_screen(PSTR(MSG_MOVE_Z), ftostr43sign(v + (v < 0 ? -0.0001f : 0.0001f), '+'));
136 136
     }
137 137
   }
@@ -279,7 +279,7 @@ void menu_bed_leveling() {
279 279
   #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
280 280
     MENU_ITEM(submenu, MSG_ZPROBE_ZOFFSET, lcd_babystep_zoffset);
281 281
   #elif HAS_BED_PROBE
282
-    MENU_ITEM_EDIT(float52, MSG_ZPROBE_ZOFFSET, &probe_offset[Z_AXIS], Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX);
282
+    MENU_ITEM_EDIT(float52, MSG_ZPROBE_ZOFFSET, &probe_offset.z, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX);
283 283
   #endif
284 284
 
285 285
   #if ENABLED(LEVEL_BED_CORNERS)

+ 5
- 5
Marlin/src/lcd/menu/menu_configuration.cpp View File

@@ -145,12 +145,12 @@ static void lcd_factory_settings() {
145 145
     START_MENU();
146 146
     MENU_BACK(MSG_CONFIGURATION);
147 147
     #if ENABLED(DUAL_X_CARRIAGE)
148
-      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);
148
+      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);
149 149
     #else
150
-      MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_X_OFFSET, &hotend_offset[X_AXIS][1], -99.0, 99.0, _recalc_offsets);
150
+      MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_X_OFFSET, &hotend_offset[1].x, -99.0, 99.0, _recalc_offsets);
151 151
     #endif
152
-    MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_Y_OFFSET, &hotend_offset[Y_AXIS][1], -99.0, 99.0, _recalc_offsets);
153
-    MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_Z_OFFSET, &hotend_offset[Z_AXIS][1], Z_PROBE_LOW_POINT, 10.0, _recalc_offsets);
152
+    MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_Y_OFFSET, &hotend_offset[1].y, -99.0, 99.0, _recalc_offsets);
153
+    MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float52sign, MSG_Z_OFFSET, &hotend_offset[1].z, Z_PROBE_LOW_POINT, 10.0, _recalc_offsets);
154 154
     #if ENABLED(EEPROM_SETTINGS)
155 155
       MENU_ITEM(function, MSG_STORE_EEPROM, lcd_store_settings);
156 156
     #endif
@@ -347,7 +347,7 @@ void menu_configuration() {
347 347
   #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
348 348
     MENU_ITEM(submenu, MSG_ZPROBE_ZOFFSET, lcd_babystep_zoffset);
349 349
   #elif HAS_BED_PROBE
350
-    MENU_ITEM_EDIT(float52, MSG_ZPROBE_ZOFFSET, &probe_offset[Z_AXIS], Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX);
350
+    MENU_ITEM_EDIT(float52, MSG_ZPROBE_ZOFFSET, &probe_offset.z, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX);
351 351
   #endif
352 352
 
353 353
   const bool busy = printer_busy();

+ 21
- 17
Marlin/src/lcd/menu/menu_delta_calibrate.cpp View File

@@ -40,8 +40,8 @@
40 40
   #include "../../lcd/extensible_ui/ui_api.h"
41 41
 #endif
42 42
 
43
-void _man_probe_pt(const float &rx, const float &ry) {
44
-  do_blocking_move_to(rx, ry, Z_CLEARANCE_BETWEEN_PROBES);
43
+void _man_probe_pt(const xy_pos_t &xy) {
44
+  do_blocking_move_to(xy, Z_CLEARANCE_BETWEEN_PROBES);
45 45
   ui.synchronize();
46 46
   move_menu_scale = _MAX(PROBE_MANUALLY_STEP, MIN_STEPS_PER_SEGMENT / float(DEFAULT_XYZ_STEPS_PER_UNIT));
47 47
   ui.goto_screen(lcd_move_z);
@@ -51,8 +51,8 @@ void _man_probe_pt(const float &rx, const float &ry) {
51 51
 
52 52
   #include "../../gcode/gcode.h"
53 53
 
54
-  float lcd_probe_pt(const float &rx, const float &ry) {
55
-    _man_probe_pt(rx, ry);
54
+  float lcd_probe_pt(const xy_pos_t &xy) {
55
+    _man_probe_pt(xy);
56 56
     KEEPALIVE_STATE(PAUSED_FOR_USER);
57 57
     ui.defer_status_screen();
58 58
     wait_for_user = true;
@@ -64,7 +64,7 @@ void _man_probe_pt(const float &rx, const float &ry) {
64 64
     #endif
65 65
     while (wait_for_user) idle();
66 66
     ui.goto_previous_screen_no_defer();
67
-    return current_position[Z_AXIS];
67
+    return current_position.z;
68 68
   }
69 69
 
70 70
 #endif
@@ -83,10 +83,14 @@ void _man_probe_pt(const float &rx, const float &ry) {
83 83
     ui.goto_screen(_lcd_calibrate_homing);
84 84
   }
85 85
 
86
-  void _goto_tower_x() { _man_probe_pt(cos(RADIANS(210)) * delta_calibration_radius, sin(RADIANS(210)) * delta_calibration_radius); }
87
-  void _goto_tower_y() { _man_probe_pt(cos(RADIANS(330)) * delta_calibration_radius, sin(RADIANS(330)) * delta_calibration_radius); }
88
-  void _goto_tower_z() { _man_probe_pt(cos(RADIANS( 90)) * delta_calibration_radius, sin(RADIANS( 90)) * delta_calibration_radius); }
89
-  void _goto_center()  { _man_probe_pt(0,0); }
86
+  void _goto_tower_a(const float &a) {
87
+    xy_pos_t tower_vec = { cos(RADIANS(a)), sin(RADIANS(a)) };
88
+    _man_probe_pt(tower_vec * delta_calibration_radius);
89
+  }
90
+  void _goto_tower_x() { _goto_tower_a(210); }
91
+  void _goto_tower_y() { _goto_tower_a(330); }
92
+  void _goto_tower_z() { _goto_tower_a( 90); }
93
+  void _goto_center()  { xy_pos_t ctr{0}; _man_probe_pt(ctr); }
90 94
 
91 95
 #endif
92 96
 
@@ -101,15 +105,15 @@ void lcd_delta_settings() {
101 105
   START_MENU();
102 106
   MENU_BACK(MSG_DELTA_CALIBRATE);
103 107
   MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_HEIGHT, &delta_height, delta_height - 10, delta_height + 10, _recalc_delta_settings);
104
-  #define EDIT_ENDSTOP_ADJ(LABEL,N) MENU_ITEM_EDIT_CALLBACK(float43, LABEL, &delta_endstop_adj[_AXIS(N)], -5, 5, _recalc_delta_settings)
105
-  EDIT_ENDSTOP_ADJ("Ex",A);
106
-  EDIT_ENDSTOP_ADJ("Ey",B);
107
-  EDIT_ENDSTOP_ADJ("Ez",C);
108
+  #define EDIT_ENDSTOP_ADJ(LABEL,N) MENU_ITEM_EDIT_CALLBACK(float43, LABEL, &delta_endstop_adj.N, -5, 5, _recalc_delta_settings)
109
+  EDIT_ENDSTOP_ADJ("Ex",a);
110
+  EDIT_ENDSTOP_ADJ("Ey",b);
111
+  EDIT_ENDSTOP_ADJ("Ez",c);
108 112
   MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_RADIUS, &delta_radius, delta_radius - 5, delta_radius + 5, _recalc_delta_settings);
109
-  #define EDIT_ANGLE_TRIM(LABEL,N) MENU_ITEM_EDIT_CALLBACK(float43, LABEL, &delta_tower_angle_trim[_AXIS(N)], -5, 5, _recalc_delta_settings)
110
-  EDIT_ANGLE_TRIM("Tx",A);
111
-  EDIT_ANGLE_TRIM("Ty",B);
112
-  EDIT_ANGLE_TRIM("Tz",C);
113
+  #define EDIT_ANGLE_TRIM(LABEL,N) MENU_ITEM_EDIT_CALLBACK(float43, LABEL, &delta_tower_angle_trim.N, -5, 5, _recalc_delta_settings)
114
+  EDIT_ANGLE_TRIM("Tx",a);
115
+  EDIT_ANGLE_TRIM("Ty",b);
116
+  EDIT_ANGLE_TRIM("Tz",c);
113 117
   MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_DIAG_ROD, &delta_diagonal_rod, delta_diagonal_rod - 5, delta_diagonal_rod + 5, _recalc_delta_settings);
114 118
   END_MENU();
115 119
 }

+ 10
- 10
Marlin/src/lcd/menu/menu_motion.cpp View File

@@ -92,26 +92,26 @@ static void _lcd_move_xyz(PGM_P name, AxisEnum axis) {
92 92
       if (soft_endstops_enabled) switch (axis) {
93 93
         case X_AXIS:
94 94
           #if ENABLED(MIN_SOFTWARE_ENDSTOP_X)
95
-            min = soft_endstop[X_AXIS].min;
95
+            min = soft_endstop.min.x;
96 96
           #endif
97 97
           #if ENABLED(MAX_SOFTWARE_ENDSTOP_X)
98
-            max = soft_endstop[X_AXIS].max;
98
+            max = soft_endstop.max.x;
99 99
           #endif
100 100
           break;
101 101
         case Y_AXIS:
102 102
           #if ENABLED(MIN_SOFTWARE_ENDSTOP_Y)
103
-            min = soft_endstop[Y_AXIS].min;
103
+            min = soft_endstop.min.y;
104 104
           #endif
105 105
           #if ENABLED(MAX_SOFTWARE_ENDSTOP_Y)
106
-            max = soft_endstop[Y_AXIS].max;
106
+            max = soft_endstop.max.y;
107 107
           #endif
108 108
           break;
109 109
         case Z_AXIS:
110 110
           #if ENABLED(MIN_SOFTWARE_ENDSTOP_Z)
111
-            min = soft_endstop[Z_AXIS].min;
111
+            min = soft_endstop.min.z;
112 112
           #endif
113 113
           #if ENABLED(MAX_SOFTWARE_ENDSTOP_Z)
114
-            max = soft_endstop[Z_AXIS].max;
114
+            max = soft_endstop.max.z;
115 115
           #endif
116 116
         default: break;
117 117
       }
@@ -173,7 +173,7 @@ void lcd_move_z() { _lcd_move_xyz(PSTR(MSG_MOVE_Z), Z_AXIS); }
173 173
         #if IS_KINEMATIC
174 174
           manual_move_offset += diff;
175 175
         #else
176
-          current_position[E_AXIS] += diff;
176
+          current_position.e += diff;
177 177
         #endif
178 178
         manual_move_to_current(E_AXIS
179 179
           #if E_MANUAL > 1
@@ -207,7 +207,7 @@ void lcd_move_z() { _lcd_move_xyz(PSTR(MSG_MOVE_Z), Z_AXIS); }
207 207
         }
208 208
       #endif // E_MANUAL > 1
209 209
 
210
-      draw_edit_screen(pos_label, ftostr41sign(current_position[E_AXIS]
210
+      draw_edit_screen(pos_label, ftostr41sign(current_position.e
211 211
         #if IS_KINEMATIC
212 212
           + manual_move_offset
213 213
         #endif
@@ -267,7 +267,7 @@ void _menu_move_distance(const AxisEnum axis, const screenFunc_t func, const int
267 267
       case Z_AXIS: STATIC_ITEM(MSG_MOVE_Z, SS_CENTER|SS_INVERT); break;
268 268
       default:
269 269
         #if ENABLED(MANUAL_E_MOVES_RELATIVE)
270
-          manual_move_e_origin = current_position[E_AXIS];
270
+          manual_move_e_origin = current_position.e;
271 271
         #endif
272 272
         STATIC_ITEM(MSG_MOVE_E, SS_CENTER|SS_INVERT);
273 273
         break;
@@ -345,7 +345,7 @@ void menu_move() {
345 345
   ) {
346 346
     if (
347 347
       #if ENABLED(DELTA)
348
-        current_position[Z_AXIS] <= delta_clip_start_height
348
+        current_position.z <= delta_clip_start_height
349 349
       #else
350 350
         true
351 351
       #endif

+ 6
- 9
Marlin/src/lcd/menu/menu_ubl.cpp View File

@@ -432,18 +432,16 @@ void _lcd_ubl_map_lcd_edit_cmd() {
432 432
 void ubl_map_move_to_xy() {
433 433
   const feedRate_t fr_mm_s = MMM_TO_MMS(XY_PROBE_SPEED);
434 434
 
435
-  set_destination_from_current(); // sync destination at the start
435
+  destination = current_position;          // sync destination at the start
436 436
 
437 437
   #if ENABLED(DELTA)
438
-    if (current_position[Z_AXIS] > delta_clip_start_height) {
439
-      destination[Z_AXIS] = delta_clip_start_height;
438
+    if (current_position.z > delta_clip_start_height) {
439
+      destination.z = delta_clip_start_height;
440 440
       prepare_internal_move_to_destination(fr_mm_s);
441 441
     }
442 442
   #endif
443 443
 
444
-  destination[X_AXIS] = pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]);
445
-  destination[Y_AXIS] = pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]);
446
-
444
+  destination.set(ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot));
447 445
   prepare_internal_move_to_destination(fr_mm_s);
448 446
 }
449 447
 
@@ -491,9 +489,8 @@ void _lcd_ubl_output_map_lcd() {
491 489
     if (y_plot < 0) y_plot = GRID_MAX_POINTS_Y - 1;
492 490
 
493 491
     #if IS_KINEMATIC
494
-      const float x = pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]),
495
-                  y = pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]);
496
-      if (position_is_reachable(x, y)) break; // Found a valid point
492
+      const xy_pos_t xy = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) };
493
+      if (position_is_reachable(xy)) break; // Found a valid point
497 494
       x_plot += (step_scaler < 0) ? -1 : 1;
498 495
     #endif
499 496
 

+ 0
- 0
Marlin/src/lcd/ultralcd.cpp View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save