Skip to content

Commit c7cfe9c

Browse files
committed
DYNAMIC_MARGINS | DYNAMIC_TRAMMING
This PR introduces two powerful runtime features for bed leveling in Marlin: - **`DYNAMIC_MARGINS`** — Enables live configuration of the bed probing area, dynamically adapted to probe offsets and machine constraints. - **`DYNAMIC_TRAMMING`** — Automatically generates assisted tramming points based on the current margins and probe offset, removing the need for static configuration or recompilation. These features improve flexibility, safety, and usability for leveling and tramming — especially on machines with limited probe reach or off-center probes. --- - **Runtime control of probing area via `M421`:** - Example: `M421 L10 R10 F15 B20` sets probing margins directly. Keep L R the same if you want a centered mesh. - **Auto-correcting margin enforcement:** - Each value provided to `M421` is **validated and adjusted upward** if it is smaller than the minimum safe margin required by the probe offset and bed geometry. - For example: - If the minimum front margin is 25 mm due to probe offset, then both `M421 F0` and `M421 F15` will result in `F25`. - Ensures that the probe will **never be commanded outside reachable areas**. A **minimum margin of 10 mm** is always enforced, or higher if required based on the user’s input and physical limits. - `M421 L0 R0 F0 B0` triggers full auto-recalculation of all margins based on current probe offsets and usable bed area. - **Optional margin reset:** - `M421 D`resets the margins to their defaults (PROBING_MARGIN_LEFT,PROBING_MARGIN_RIGHT,PROBING_MARGIN_FRONT, PROBING_MARGIN_BACK) - **Fully integrated into:** - **Unified Bed Leveling (UBL)**: mesh probing, interpolation, and display. - **Bilinear Leveling**: mesh creation, probing boundaries, and `M420 V` output. - **Safe and dynamic mesh access:** - All probing and interpolation logic clamps access to within valid bounds. - Prevents out-of-bounds errors or unreachable probe moves. - supports positive and negative probe offsets - respects HOME_OFFSETS if enabled - Enables automatic generation of assisted tramming points (`G35`) based on the **current margins**. - Removes the need to statically define `TRAMMING_POINT_XY` when `DYNAMIC_MARGINS` is enabled. - Reacts in real time to updated margins — no rebuild required. - Works seamlessly with `ASSISTED_TRAMMING`. - Adds feature flags: - `DYNAMIC_MARGINS` - `DYNAMIC_TRAMMING` - Saves and loads dynamic margin configuration to EEPROM. - Integrates margin and tramming state into persistent printer settings. - Modified to retrieve margin-aware tramming points if `DYNAMIC_TRAMMING` is active. - Assisted tramming logic now generates points from current margins if `DYNAMIC_TRAMMING` is enabled. - Fallbacks to static `TRAMMING_POINT_XY` only when dynamic margins are disabled. - includes M421_report if DYNAMIC_MARGINS are enabled. - `Configuration_adv.h`: Adds feature flags `DYNAMIC_MARGINS`, `DYNAMIC_TRAMMING`. - **UBL**: - `ubl.cpp`, `ubl.h`, `ubl_g29.cpp`, `ubl_motion.cpp`: Updated to support margin-aware mesh logic and probing bounds. - **Bilinear**: - `bbl.cpp`, `bbl.h`: Respect dynamic margins during mesh generation, probing, and display. - **Tramming**: - Assisted tramming points are calculated dynamically from current margins. --- ```gcode M421 L15 R15 F10 B10 ; Manually define all probing margins (reaccounting if to low) or M421 L15 R15 ; Manually set L R conditionally. or M421 L0 R0 F0 B0 ; Auto-calculate margins from probe offset and bed size ``` If the requested margin is too small (e.g., `F0`), the system will automatically **enforce a larger minimum**, such as `F25`, depending on the probe offset and physical constraints of the printer. --- ``` start PowerUp Marlin bugfix-2.1.x echo: Last Updated: 2025-06-29 | Author: (none, default config) echo: Compiled: Jul 28 2025 echo: Free Memory: 0 PlannerBufferBytes: 1536 echo:EEPROM version mismatch (EEPROM= Marlin=V90) echo:Hardcoded Default Settings Loaded [7] echo:busy: processing X:107.00 Y:107.00 Z:5.00 E:0.00 Count X:8560 Y:8560 Z:2000 ok echo:; Linear Units: echo: G21 ; (mm) echo:; Temperature Units: echo: M149 C ; Units in Celsius echo:; Filament settings (Disabled): echo: M200 S0 D1.75 echo:; Steps per unit: echo: M92 X80.00 Y80.00 Z400.00 E500.00 echo:; Max feedrates (units/s): echo: M203 X2000.00 Y2000.00 Z5.00 E1000.00 echo:; Max Acceleration (units/s2): echo: M201 X3000.00 Y3000.00 Z100.00 E10000.00 echo:; Acceleration (units/s2) (P<print-accel> R<retract-accel> T<travel-accel>): echo: M204 P3000.00 R3000.00 T3000.00 echo:; Advanced (B<min_segment_time_us> S<min_feedrate> T<min_travel_feedrate> J<junc_dev>): echo: M205 B20000.00 S0.00 T0.00 J0.01 echo:; Home offset: echo: M206 X0.00 Y0.00 Z0.00 echo:; Auto Bed Leveling: echo: M420 S0 Z10.00 ; Leveling OFF echo:; Dynamic Margins: echo: M421 L45.00 R45.00 F45.00 B45.00 ; Margins in mm echo:; Material heatup parameters: echo: M145 S0 H180.00 B70.00 F0 echo: M145 S1 H240.00 B110.00 F0 echo:; Hotend PID: echo: M301 P22.20 I1.08 D114.00 echo:; Z-Probe Offset: echo: M851 X10.00 Y10.00 Z0.00 ; (mm) ok M421 L0 R0 F0 B0 ! L margin too small (0 < 10). L set to: 10 ! F margin too small (0 < 10). F set to: 10 ! R margin too small (0 < 10). R set to: 10 ! B margin too small (0 < 10). B set to: 10 ok ``` Changing probe offsets to X=-31.5 Y-41.8, and recalculating min margins (X_MAX_POS=242, Y_MAX_POS=235, Bedsize 235x235): ``` M851 X-31.5 Y-41.8 M421 L0 R0 F0 B0 [2] ok ! L margin too small (0 < 10). L set to: 10 ! F margin too small (0 < 10). F set to: 10 ! R margin too small (0 < 25). R set to: 25 ! B margin too small (0 < 42). B set to: 42 ok ``` - ✅ Cartesian platforms (KINEMATIC not tested) - ✅ `M421` with both manual and automatic values - ✅ Correct auto-correction behavior for undersized margins - ✅ Optional margin reset via `M421 D` - ✅ UBL and Bilinear mesh generation within validated probing zones - ✅ `M420 V` output clipped to valid mesh area - ✅ `G35` assisted tramming following the current probing area - ✅ Safe operation across all margin combinations - ✅ Safe operation with positive and negative probe offsets - ✅ respects HOME_OFFSETS if enabled --- Many printers have probes that are offset from the nozzle or have restricted reach. Without this feature, users had to manually adjust and recompile constants like `PROBING_MARGIN` or `TRAMMING_POINT_XY`. These enhancements: - Eliminate the need to recompile when changing probing margins or tramming points. - Adapt automatically to the hardware layout of the printer. - Improve safety and usability during leveling and tramming. --- This implementation is isolated behind the `DYNAMIC_MARGINS` and `DYNAMIC_TRAMMING` feature flags and maintains full backward compatibility with existing configurations. It has been tested on **Cartesian platforms**. Kinematic (e.g., CoreXY, Delta) support is untested and can be addressed in follow-up work. Feedback and review are welcome! in the PR
1 parent dc6f23e commit c7cfe9c

File tree

16 files changed

+543
-135
lines changed

16 files changed

+543
-135
lines changed

Marlin/Configuration_adv.h

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,7 @@
11101110
//#define ASSISTED_TRAMMING
11111111
#if ENABLED(ASSISTED_TRAMMING)
11121112

1113-
// Define from 3 to 9 points to probe.
1113+
// Define from 3 to 9 points to probe. Overwritten by "DYNAMIC_MARGINS" if "DYNAMIC_TRAMMING" is enabled.
11141114
#define TRAMMING_POINT_XY { { 20, 20 }, { 180, 20 }, { 180, 180 }, { 20, 180 } }
11151115

11161116
// Define position names for probe points.
@@ -2448,12 +2448,34 @@
24482448
* should the probe position be modified with M851XY then the
24492449
* probe points will follow. This prevents any change from causing
24502450
* the probe to be unable to reach any points.
2451+
*
2452+
* If you enable DYNAMIC_MARGINS PROBING_MARGINs are changeable without recompilation.
2453+
* They are then bedlevel.margin_l, bedlevel.margin_r, bedlevel.margin_f, bedlevel.margin_b
2454+
* and can be overwritten with "M421 L50 R50 F50 B50" for a 50mm margin around the bed
2455+
* and saved to the EEPROM on runtime. Default values underneath.
2456+
* Setting manually via gcode respects probe_offsets and the min and max positions
2457+
* and recalculates if a too low value is set so the probed area is valid.
2458+
*
2459+
* DYNAMIC_MARGINS can be also enabled to be used for the ASSISTED_TRAMMING points with
2460+
* #define DYNAMIC_TRAMMING
2461+
*
24512462
*/
24522463
#if PROBE_SELECTED && !IS_KINEMATIC
2453-
//#define PROBING_MARGIN_LEFT PROBING_MARGIN
2454-
//#define PROBING_MARGIN_RIGHT PROBING_MARGIN
2455-
//#define PROBING_MARGIN_FRONT PROBING_MARGIN
2456-
//#define PROBING_MARGIN_BACK PROBING_MARGIN
2464+
//#define DYNAMIC_MARGINS
2465+
#if ENABLED(DYNAMIC_MARGINS)
2466+
#if ENABLED(ASSISTED_TRAMMING)
2467+
//#define DYNAMIC_TRAMMING
2468+
#endif
2469+
#define PROBING_MARGIN_LEFT 45
2470+
#define PROBING_MARGIN_RIGHT 45
2471+
#define PROBING_MARGIN_FRONT 45
2472+
#define PROBING_MARGIN_BACK 45
2473+
#else
2474+
#define PROBING_MARGIN_LEFT PROBING_MARGIN
2475+
#define PROBING_MARGIN_RIGHT PROBING_MARGIN
2476+
#define PROBING_MARGIN_FRONT PROBING_MARGIN
2477+
#define PROBING_MARGIN_BACK PROBING_MARGIN
2478+
#endif
24572479
#endif
24582480

24592481
#if ANY(MESH_BED_LEVELING, AUTO_BED_LEVELING_UBL)

Marlin/src/feature/bedlevel/abl/bbl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ xy_float_t LevelingBilinear::grid_factor;
4343
bed_mesh_t LevelingBilinear::z_values;
4444
xy_pos_t LevelingBilinear::cached_rel;
4545
xy_int8_t LevelingBilinear::cached_g;
46+
#if ENABLED(DYNAMIC_MARGINS)
47+
int16_t LevelingBilinear::margin_l, LevelingBilinear::margin_r, LevelingBilinear::margin_f, LevelingBilinear::margin_b;
48+
#endif
4649

4750
/**
4851
* Extrapolate a single point from its neighbors

Marlin/src/feature/bedlevel/abl/bbl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,17 @@
2222
#pragma once
2323

2424
#include "../../../inc/MarlinConfigPre.h"
25+
#if ENABLED(DYNAMIC_MARGINS)
26+
#include "../../../feature/bedlevel/bedlevel.h"
27+
#endif
2528

2629
class LevelingBilinear {
2730
public:
2831
static bed_mesh_t z_values;
2932
static xy_pos_t grid_spacing, grid_start;
33+
#if ENABLED(DYNAMIC_MARGINS)
34+
static int16_t margin_l, margin_r, margin_f, margin_b;
35+
#endif
3036

3137
private:
3238
static xy_float_t grid_factor;

Marlin/src/feature/bedlevel/hilbert_curve.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ bool hilbert_curve::search_from(uint8_t x, uint8_t y, hilbert_curve::callback_pt
102102
*/
103103
bool hilbert_curve::search_from_closest(const xy_pos_t &pos, hilbert_curve::callback_ptr func, void *data) {
104104
// Find closest grid intersection
105-
const uint8_t grid_x = LROUND(constrain(float(pos.x - (MESH_MIN_X)) / (MESH_X_DIST), 0, (GRID_MAX_POINTS_X) - 1));
106-
const uint8_t grid_y = LROUND(constrain(float(pos.y - (MESH_MIN_Y)) / (MESH_Y_DIST), 0, (GRID_MAX_POINTS_Y) - 1));
105+
const uint8_t grid_x = LROUND(constrain(float(pos.x - (TERN(DYNAMIC_MARGINS, bedlevel.margin_l, MESH_MIN_X))) / (TERN(DYNAMIC_MARGINS, bedlevel.get_mesh_x_dist(), MESH_X_DIST)), 0, (GRID_MAX_POINTS_X) - 1));
106+
const uint8_t grid_y = LROUND(constrain(float(pos.y - (TERN(DYNAMIC_MARGINS, bedlevel.margin_f, MESH_MIN_Y))) / (TERN(DYNAMIC_MARGINS, bedlevel.get_mesh_y_dist(), MESH_Y_DIST)), 0, (GRID_MAX_POINTS_Y) - 1));
107107
return search_from(grid_x, grid_y, func, data);
108108
}
109109

Marlin/src/feature/bedlevel/ubl/ubl.cpp

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,26 @@ void unified_bed_leveling::report_state() {
6565
int8_t unified_bed_leveling::storage_slot;
6666

6767
float unified_bed_leveling::z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
68+
#if ENABLED(DYNAMIC_MARGINS)
69+
int16_t unified_bed_leveling::margin_l, unified_bed_leveling::margin_r, unified_bed_leveling::margin_f, unified_bed_leveling::margin_b;
70+
#endif
6871

6972
#define _GRIDPOS(A,N) (MESH_MIN_##A + N * (MESH_##A##_DIST))
70-
71-
const float
72-
unified_bed_leveling::_mesh_index_to_xpos[GRID_MAX_POINTS_X] PROGMEM = ARRAY_N(GRID_MAX_POINTS_X,
73-
_GRIDPOS(X, 0), _GRIDPOS(X, 1), _GRIDPOS(X, 2), _GRIDPOS(X, 3),
74-
_GRIDPOS(X, 4), _GRIDPOS(X, 5), _GRIDPOS(X, 6), _GRIDPOS(X, 7),
75-
_GRIDPOS(X, 8), _GRIDPOS(X, 9), _GRIDPOS(X, 10), _GRIDPOS(X, 11),
76-
_GRIDPOS(X, 12), _GRIDPOS(X, 13), _GRIDPOS(X, 14), _GRIDPOS(X, 15)
77-
),
78-
unified_bed_leveling::_mesh_index_to_ypos[GRID_MAX_POINTS_Y] PROGMEM = ARRAY_N(GRID_MAX_POINTS_Y,
79-
_GRIDPOS(Y, 0), _GRIDPOS(Y, 1), _GRIDPOS(Y, 2), _GRIDPOS(Y, 3),
80-
_GRIDPOS(Y, 4), _GRIDPOS(Y, 5), _GRIDPOS(Y, 6), _GRIDPOS(Y, 7),
81-
_GRIDPOS(Y, 8), _GRIDPOS(Y, 9), _GRIDPOS(Y, 10), _GRIDPOS(Y, 11),
82-
_GRIDPOS(Y, 12), _GRIDPOS(Y, 13), _GRIDPOS(Y, 14), _GRIDPOS(Y, 15)
83-
);
73+
#if DISABLED(DYNAMIC_MARGINS)
74+
const float
75+
unified_bed_leveling::_mesh_index_to_xpos[GRID_MAX_POINTS_X] PROGMEM = ARRAY_N(GRID_MAX_POINTS_X,
76+
_GRIDPOS(X, 0), _GRIDPOS(X, 1), _GRIDPOS(X, 2), _GRIDPOS(X, 3),
77+
_GRIDPOS(X, 4), _GRIDPOS(X, 5), _GRIDPOS(X, 6), _GRIDPOS(X, 7),
78+
_GRIDPOS(X, 8), _GRIDPOS(X, 9), _GRIDPOS(X, 10), _GRIDPOS(X, 11),
79+
_GRIDPOS(X, 12), _GRIDPOS(X, 13), _GRIDPOS(X, 14), _GRIDPOS(X, 15)
80+
),
81+
unified_bed_leveling::_mesh_index_to_ypos[GRID_MAX_POINTS_Y] PROGMEM = ARRAY_N(GRID_MAX_POINTS_Y,
82+
_GRIDPOS(Y, 0), _GRIDPOS(Y, 1), _GRIDPOS(Y, 2), _GRIDPOS(Y, 3),
83+
_GRIDPOS(Y, 4), _GRIDPOS(Y, 5), _GRIDPOS(Y, 6), _GRIDPOS(Y, 7),
84+
_GRIDPOS(Y, 8), _GRIDPOS(Y, 9), _GRIDPOS(Y, 10), _GRIDPOS(Y, 11),
85+
_GRIDPOS(Y, 12), _GRIDPOS(Y, 13), _GRIDPOS(Y, 14), _GRIDPOS(Y, 15)
86+
);
87+
#endif
8488

8589
volatile int16_t unified_bed_leveling::encoder_diff;
8690

@@ -174,8 +178,8 @@ void unified_bed_leveling::display_map(const uint8_t map_type) {
174178
SERIAL_ECHOPGM("\nBed Topography Report");
175179
if (human) {
176180
SERIAL_ECHOLNPGM(":\n");
177-
serial_echo_xy(4, MESH_MIN_X, MESH_MAX_Y);
178-
serial_echo_xy(twixt, MESH_MAX_X, MESH_MAX_Y);
181+
serial_echo_xy(4, (TERN(DYNAMIC_MARGINS, unified_bed_leveling::margin_l, MESH_MIN_X)), (TERN(DYNAMIC_MARGINS, (Y_BED_SIZE - unified_bed_leveling::margin_f), MESH_MAX_Y)));
182+
serial_echo_xy(twixt, (TERN(DYNAMIC_MARGINS, (X_BED_SIZE - unified_bed_leveling::margin_r), MESH_MAX_X)), (TERN(DYNAMIC_MARGINS, (Y_BED_SIZE - unified_bed_leveling::margin_f), MESH_MAX_Y)));
179183
SERIAL_EOL();
180184
serial_echo_column_labels(eachsp - 2);
181185
}
@@ -232,8 +236,8 @@ void unified_bed_leveling::display_map(const uint8_t map_type) {
232236
if (human) {
233237
serial_echo_column_labels(eachsp - 2);
234238
SERIAL_EOL();
235-
serial_echo_xy(4, MESH_MIN_X, MESH_MIN_Y);
236-
serial_echo_xy(twixt, MESH_MAX_X, MESH_MIN_Y);
239+
serial_echo_xy(4, (TERN(DYNAMIC_MARGINS, unified_bed_leveling::margin_l, MESH_MIN_X)), (TERN(DYNAMIC_MARGINS, unified_bed_leveling::margin_l, MESH_MIN_Y)));
240+
serial_echo_xy(twixt, (TERN(DYNAMIC_MARGINS, (X_BED_SIZE - unified_bed_leveling::margin_r), MESH_MAX_X)), (TERN(DYNAMIC_MARGINS, unified_bed_leveling::margin_l, MESH_MIN_Y)));
237241
SERIAL_EOL();
238242
SERIAL_EOL();
239243
}

Marlin/src/feature/bedlevel/ubl/ubl.h

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
2929
#include "../../../core/debug_out.h"
3030

31+
#if ENABLED(DYNAMIC_MARGINS)
32+
class unified_bed_leveling;
33+
extern unified_bed_leveling bedlevel;
34+
#endif
3135
#define UBL_VERSION "1.01"
3236
#define UBL_OK false
3337
#define UBL_ERR true
@@ -37,9 +41,10 @@ enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP, CLOSEST };
3741
// External references
3842

3943
struct mesh_index_pair;
40-
41-
#define MESH_X_DIST (float((MESH_MAX_X) - (MESH_MIN_X)) / (GRID_MAX_CELLS_X))
42-
#define MESH_Y_DIST (float((MESH_MAX_Y) - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y))
44+
#if DISABLED(DYNAMIC_MARGINS)
45+
#define MESH_X_DIST (float((MESH_MAX_X) - (MESH_MIN_X)) / (GRID_MAX_CELLS_X))
46+
#define MESH_Y_DIST (float((MESH_MAX_Y) - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y))
47+
#endif
4348

4449
#if ENABLED(OPTIMIZED_MESH_STORAGE)
4550
typedef int16_t mesh_store_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
@@ -110,14 +115,20 @@ class unified_bed_leveling {
110115
static void smart_fill_wlsf(const_float_t ) __O2; // O2 gives smaller code than Os on A2560
111116

112117
static int8_t storage_slot;
118+
#if ENABLED(DYNAMIC_MARGINS)
119+
typedef float bed_mesh_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
120+
static int16_t margin_l, margin_r, margin_f, margin_b;
121+
#endif
113122

114123
static bed_mesh_t z_values;
115124
#if ENABLED(OPTIMIZED_MESH_STORAGE)
116125
static void set_store_from_mesh(const bed_mesh_t &in_values, mesh_store_t &stored_values);
117126
static void set_mesh_from_store(const mesh_store_t &stored_values, bed_mesh_t &out_values);
118127
#endif
119-
static const float _mesh_index_to_xpos[GRID_MAX_POINTS_X],
120-
_mesh_index_to_ypos[GRID_MAX_POINTS_Y];
128+
#if DISABLED(DYNAMIC_MARGINS)
129+
static const float _mesh_index_to_xpos[GRID_MAX_POINTS_X],
130+
_mesh_index_to_ypos[GRID_MAX_POINTS_Y];
131+
#endif
121132

122133
#if HAS_MARLINUI_MENU
123134
static bool lcd_map_control;
@@ -133,12 +144,12 @@ class unified_bed_leveling {
133144
FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const_float_t z) { z_values[px][py] = z; }
134145

135146
static int8_t cell_index_x_raw(const_float_t x) {
136-
return FLOOR((x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST));
147+
return FLOOR((x - (TERN(DYNAMIC_MARGINS, bedlevel.margin_l, MESH_MIN_X))) * TERN(DYNAMIC_MARGINS, RECIPROCAL(get_mesh_x_dist()), RECIPROCAL(MESH_X_DIST)));
137148
}
138149

139150
static int8_t cell_index_y_raw(const_float_t y) {
140-
return FLOOR((y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST));
141-
}
151+
return FLOOR((y - (TERN(DYNAMIC_MARGINS, bedlevel.margin_f, MESH_MIN_Y))) * TERN(DYNAMIC_MARGINS, RECIPROCAL(get_mesh_y_dist()), RECIPROCAL(MESH_Y_DIST)));
152+
}
142153

143154
static bool cell_index_x_valid(const_float_t x) {
144155
return WITHIN(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
@@ -162,11 +173,11 @@ class unified_bed_leveling {
162173
static xy_uint8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
163174

164175
static int8_t closest_x_index(const_float_t x) {
165-
const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST);
176+
const int8_t px = (x - (TERN(DYNAMIC_MARGINS, bedlevel.margin_l, MESH_MIN_X)) + (TERN(DYNAMIC_MARGINS, get_mesh_x_dist(), MESH_X_DIST)) * 0.5) * TERN(DYNAMIC_MARGINS, RECIPROCAL(get_mesh_x_dist()), RECIPROCAL(MESH_X_DIST));
166177
return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1;
167178
}
168179
static int8_t closest_y_index(const_float_t y) {
169-
const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST);
180+
const int8_t py = (y - (TERN(DYNAMIC_MARGINS, bedlevel.margin_f, MESH_MIN_Y)) + (TERN(DYNAMIC_MARGINS, get_mesh_y_dist(), MESH_Y_DIST)) * 0.5) * TERN(DYNAMIC_MARGINS, RECIPROCAL(get_mesh_y_dist()), RECIPROCAL(MESH_Y_DIST));
170181
return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1;
171182
}
172183
static xy_int8_t closest_indexes(const xy_pos_t &xy) {
@@ -214,7 +225,7 @@ class unified_bed_leveling {
214225
return _UBL_OUTER_Z_RAISE;
215226
}
216227

217-
const float xratio = (rx0 - get_mesh_x(x1_i)) * RECIPROCAL(MESH_X_DIST),
228+
const float xratio = (rx0 - get_mesh_x(x1_i)) * TERN(DYNAMIC_MARGINS, RECIPROCAL(get_mesh_x_dist()), RECIPROCAL(MESH_X_DIST)),
218229
z1 = z_values[x1_i][yi];
219230

220231
return z1 + xratio * (z_values[_MIN(x1_i, (GRID_MAX_POINTS_X) - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array
@@ -237,7 +248,7 @@ class unified_bed_leveling {
237248
return _UBL_OUTER_Z_RAISE;
238249
}
239250

240-
const float yratio = (ry0 - get_mesh_y(y1_i)) * RECIPROCAL(MESH_Y_DIST),
251+
const float yratio = (ry0 - get_mesh_y(y1_i)) * TERN(DYNAMIC_MARGINS, RECIPROCAL(get_mesh_y_dist()), RECIPROCAL(MESH_Y_DIST)),
241252
z1 = z_values[xi][y1_i];
242253

243254
return z1 + yratio * (z_values[xi][_MIN(y1_i, (GRID_MAX_POINTS_Y) - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array
@@ -259,12 +270,12 @@ class unified_bed_leveling {
259270
* UBL_Z_RAISE_WHEN_OFF_MESH is specified, that value is returned.
260271
*/
261272
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
262-
if (!WITHIN(rx0, MESH_MIN_X, MESH_MAX_X) || !WITHIN(ry0, MESH_MIN_Y, MESH_MAX_Y))
273+
if (!WITHIN(rx0, TERN(DYNAMIC_MARGINS, bedlevel.margin_l, MESH_MIN_X), TERN(DYNAMIC_MARGINS, X_BED_SIZE - bedlevel.margin_r, MESH_MAX_X)) || !WITHIN(ry0, TERN(DYNAMIC_MARGINS, bedlevel.margin_f, MESH_MIN_Y), TERN(DYNAMIC_MARGINS, Y_BED_SIZE - bedlevel.margin_b, MESH_MAX_Y)))
263274
return UBL_Z_RAISE_WHEN_OFF_MESH;
264275
#endif
265276

266-
const uint8_t mx = _MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1, my = _MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1;
267-
const float x0 = get_mesh_x(cx), x1 = get_mesh_x(cx + 1),
277+
IF_DISABLED(DYNAMIC_MARGINS, const) uint8_t mx = _MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1, my = _MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1;
278+
IF_DISABLED(DYNAMIC_MARGINS, const) float x0 = get_mesh_x(cx), x1 = get_mesh_x(cx + 1),
268279
z1 = calc_z0(rx0, x0, z_values[cx][cy], x1, z_values[mx][cy]),
269280
z2 = calc_z0(rx0, x0, z_values[cx][my], x1, z_values[mx][my]);
270281
float z0 = calc_z0(ry0, get_mesh_y(cy), z1, get_mesh_y(cy + 1), z2);
@@ -286,13 +297,28 @@ class unified_bed_leveling {
286297
static float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
287298

288299
static constexpr float get_z_offset() { return 0.0f; }
289-
290-
static float get_mesh_x(const uint8_t i) {
291-
return i < (GRID_MAX_POINTS_X) ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST);
292-
}
293-
static float get_mesh_y(const uint8_t i) {
294-
return i < (GRID_MAX_POINTS_Y) ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST);
295-
}
300+
#if DISABLED(DYNAMIC_MARGINS)
301+
static float get_mesh_x(const uint8_t i) {
302+
return i < (GRID_MAX_POINTS_X) ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST);
303+
}
304+
static float get_mesh_y(const uint8_t i) {
305+
return i < (GRID_MAX_POINTS_Y) ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST);
306+
}
307+
#endif
308+
#if ENABLED(DYNAMIC_MARGINS)
309+
static float get_mesh_x(const uint8_t i) {
310+
return bedlevel.margin_l + i * get_mesh_x_dist();
311+
}
312+
static float get_mesh_y(const uint8_t i) {
313+
return bedlevel.margin_f + i * get_mesh_y_dist();
314+
}
315+
static float get_mesh_x_dist() {
316+
return float((X_BED_SIZE - bedlevel.margin_r) - bedlevel.margin_l) / (GRID_MAX_POINTS_X - 1);
317+
}
318+
static float get_mesh_y_dist() {
319+
return float((Y_BED_SIZE - bedlevel.margin_b) - bedlevel.margin_f) / (GRID_MAX_POINTS_Y - 1);
320+
}
321+
#endif
296322

297323
#if UBL_SEGMENTED
298324
static bool line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s);
@@ -307,7 +333,9 @@ class unified_bed_leveling {
307333

308334
}; // class unified_bed_leveling
309335

310-
extern unified_bed_leveling bedlevel;
336+
#if DISABLED(DYNAMIC_MARGINS)
337+
extern unified_bed_leveling bedlevel;
338+
#endif
311339

312340
// Prevent debugging propagating to other files
313341
#include "../../../core/debug_out.h"

0 commit comments

Comments
 (0)