2 This code is derived from (and mostly copied from) Johann Rocholls code at https://github.com/jcrocholl/Marlin/blob/deltabot/Marlin/Marlin_main.cpp
3 license is the same as his code.
7 Probes grid_size points in X and Y (total probes grid_size * grid_size) and stores the relative offsets from the 0,0 Z height
8 When enabled every move will calculate the Z offset based on interpolating the height offset within the grids nearest 4 points.
12 The strategy must be enabled in the config as well as zprobe.
14 leveling-strategy.rectangular-grid.enable true
16 The size of the grid can be set with...
17 leveling-strategy.rectangular-grid.size 7
19 leveling-strategy.rectangular-grid.grid_x_size 7
20 leveling-strategy.rectangular-grid.grid_y_size 7
21 this is the X and Y size of the grid, it must be an odd number, the default is 7 which is 49 probe points
23 If both "size" and "grid_x_size" and "grid_x_size defined "grid_x_size" and "grid_x_size" will be used.
24 If "grid_x_size" and "grid_x_size" omitted then "size" will be used.
25 If "size" omitted default value will be used.
27 I and J params used for grid size. If both omitted values from config will be used. If only one provided (I or J) then it will be used for both x_size and y-size.
29 The width and length of the rectangle that is probed is set with...
31 leveling-strategy.rectangular-grid.x_size 100
32 leveling-strategy.rectangular-grid.y_size 90
34 Optionally probe offsets from the nozzle or tool head can be defined with...
36 leveling-strategy.rectangular-grid.probe_offsets 0,0,0 # probe offsetrs x,y,z
38 they may also be set with M565 X0 Y0 Z0
40 If the saved grid is to be loaded on boot then this must be set in the config...
42 leveling-strategy.rectangular-grid.save true
44 Then when M500 is issued it will save M375 which will cause the grid to be loaded on boot. The default is to not autoload the grid on boot
46 Optionally an initial_height can be set that tell the intial probe where to stop the fast decent before it probes, this should be around 5-10mm above the bed
47 leveling-strategy.rectangular-grid.initial_height 10
49 If two corners rectangular mode activated using "leveling-strategy.rectangular-grid.only_by_two_corners true" then G29/31/32 will not work without providing XYAB parameters
50 XY - start point, AB rectangle size from starting point
51 "Two corners"" not absolutely correct name for this mode, because it use only one corner and rectangle size.
53 Display mode of current grid can be changed to human redable mode (table with coordinates) by using
54 leveling-strategy.rectangular-grid.human_readable true
58 G29 test probes a rectangle which defaults to the width and height, can be overidden with Xnnn and Ynnn
60 G31 probes the grid and turns the compensation on, this will remain in effect until reset or M561/M370
61 optional parameters {{Xn}} {{Yn}} sets the size for this rectangular probe, which gets saved with M375
63 M370 clears the grid and turns off compensation
64 M374 Save grid to /sd/cartesian.grid
65 M374.1 delete /sd/cartesian.grid
66 M375 Load the grid from /sd/cartesian.grid and enable compensation
67 M375.1 display the current grid
68 M561 clears the grid and turns off compensation
69 M565 defines the probe offsets from the nozzle or tool head
72 M500 saves the probe points
73 M503 displays the current settings
76 #include "CartGridStrategy.h"
81 #include "StreamOutputPool.h"
83 #include "checksumm.h"
84 #include "ConfigValue.h"
85 #include "PublicDataRequest.h"
86 #include "PublicData.h"
89 #include "nuts_bolts.h"
91 #include "platform_memory.h"
99 #define grid_size_checksum CHECKSUM("size")
100 #define grid_x_size_checksum CHECKSUM("grid_x_size")
101 #define grid_y_size_checksum CHECKSUM("grid_y_size")
102 #define tolerance_checksum CHECKSUM("tolerance")
103 #define save_checksum CHECKSUM("save")
104 #define probe_offsets_checksum CHECKSUM("probe_offsets")
105 #define initial_height_checksum CHECKSUM("initial_height")
106 #define x_size_checksum CHECKSUM("x_size")
107 #define y_size_checksum CHECKSUM("y_size")
108 #define do_home_checksum CHECKSUM("do_home")
109 #define only_by_two_corners_checksum CHECKSUM("only_by_two_corners")
110 #define human_readable_checksum CHECKSUM("human_readable")
111 #define height_limit_checksum CHECKSUM("height_limit")
112 #define dampening_start_checksum CHECKSUM("dampening_start")
114 #define GRIDFILE "/sd/cartesian.grid"
115 #define GRIDFILE_NM "/sd/cartesian_nm.grid"
117 CartGridStrategy::CartGridStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe)
122 CartGridStrategy::~CartGridStrategy()
124 if(grid != nullptr) AHB0.dealloc(grid);
127 bool CartGridStrategy::handleConfig()
130 uint8_t grid_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_size_checksum)->by_default(7)->as_number();
131 this->current_grid_x_size = this->configured_grid_x_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_x_size_checksum)->by_default(grid_size)->as_number();
132 this->current_grid_y_size = this->configured_grid_y_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_y_size_checksum)->by_default(grid_size)->as_number();
133 tolerance = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, tolerance_checksum)->by_default(0.03F)->as_number();
134 save = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, save_checksum)->by_default(false)->as_bool();
135 do_home = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, do_home_checksum)->by_default(true)->as_bool();
136 only_by_two_corners = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, only_by_two_corners_checksum)->by_default(false)->as_bool();
137 human_readable = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, human_readable_checksum)->by_default(false)->as_bool();
139 this->height_limit = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, height_limit_checksum)->by_default(NAN)->as_number();
140 this->dampening_start = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, dampening_start_checksum)->by_default(NAN)->as_number();
142 if(!isnan(this->height_limit) && !isnan(this->dampening_start)) {
143 this->damping_interval = height_limit - dampening_start;
145 this->damping_interval = NAN;
148 this->x_start = 0.0F;
149 this->y_start = 0.0F;
150 this->x_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, x_size_checksum)->by_default(0.0F)->as_number();
151 this->y_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, y_size_checksum)->by_default(0.0F)->as_number();
152 if (this->x_size == 0.0F || this->y_size == 0.0F) {
153 THEKERNEL->streams->printf("Error: Invalid config, x_size and y_size must be defined\n");
157 // the initial height above the bed we stop the intial move down after home to find the bed
158 // this should be a height that is enough that the probe will not hit the bed and is an offset from max_z (can be set to 0 if max_z takes into account the probe offset)
159 this->initial_height = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, initial_height_checksum)->by_default(10)->as_number();
161 // Probe offsets xxx,yyy,zzz
163 std::string po = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string();
164 std::vector<float> v = parse_number_list(po.c_str());
166 this->probe_offsets = std::make_tuple(v[0], v[1], v[2]);
171 grid = (float *)AHB0.alloc(configured_grid_x_size * configured_grid_y_size * sizeof(float));
173 if(grid == nullptr) {
174 THEKERNEL->streams->printf("Error: Not enough memory\n");
183 void CartGridStrategy::save_grid(StreamOutput *stream)
185 if(only_by_two_corners){
186 stream->printf("error:Unable to save grid in only_by_two_corners mode\n");
191 stream->printf("error:No grid to save\n");
195 if((current_grid_x_size != configured_grid_x_size) || (current_grid_y_size != configured_grid_y_size)) {
196 stream->printf("error:Unable to save grid with size different from configured\n");
200 FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "w"):fopen(GRIDFILE_NM, "w");
202 stream->printf("error:Failed to open grid file %s\n", GRIDFILE);
205 uint8_t tmp_configured_grid_size = configured_grid_x_size;
206 if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) {
207 stream->printf("error:Failed to write grid x size\n");
212 tmp_configured_grid_size = configured_grid_y_size;
213 if(configured_grid_y_size != configured_grid_x_size){
214 if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) {
215 stream->printf("error:Failed to write grid y size\n");
221 if(fwrite(&x_size, sizeof(float), 1, fp) != 1) {
222 stream->printf("error:Failed to write x_size\n");
227 if(fwrite(&y_size, sizeof(float), 1, fp) != 1) {
228 stream->printf("error:Failed to write y_size\n");
233 for (int y = 0; y < configured_grid_y_size; y++) {
234 for (int x = 0; x < configured_grid_x_size; x++) {
235 if(fwrite(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) {
236 stream->printf("error:Failed to write grid\n");
242 stream->printf("grid saved to %s\n", GRIDFILE);
246 bool CartGridStrategy::load_grid(StreamOutput *stream)
248 if(only_by_two_corners){
249 stream->printf("error:Unable to load grid in only_by_two_corners mode\n");
253 FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "r"):fopen(GRIDFILE_NM, "r");
255 stream->printf("error:Failed to open grid %s\n", GRIDFILE);
259 uint8_t load_grid_x_size, load_grid_y_size;
262 if(fread(&load_grid_x_size, sizeof(uint8_t), 1, fp) != 1) {
263 stream->printf("error:Failed to read grid size\n");
268 if(load_grid_x_size != configured_grid_x_size) {
269 stream->printf("error:grid size x is different read %d - config %d\n", load_grid_x_size, configured_grid_x_size);
274 load_grid_y_size = load_grid_x_size;
276 if(configured_grid_x_size != configured_grid_y_size){
277 if(fread(&load_grid_y_size, sizeof(uint8_t), 1, fp) != 1) {
278 stream->printf("error:Failed to read grid size\n");
283 if(load_grid_y_size != configured_grid_y_size) {
284 stream->printf("error:grid size y is different read %d - config %d\n", load_grid_y_size, configured_grid_x_size);
290 if(fread(&x, sizeof(float), 1, fp) != 1) {
291 stream->printf("error:Failed to read grid x size\n");
296 if(fread(&y, sizeof(float), 1, fp) != 1) {
297 stream->printf("error:Failed to read grid y size\n");
302 if(x != x_size || y != y_size) {
303 stream->printf("error:bed dimensions changed read (%f, %f) - config (%f,%f)\n", x, y, x_size, y_size);
308 for (int y = 0; y < configured_grid_y_size; y++) {
309 for (int x = 0; x < configured_grid_x_size; x++) {
310 if(fread(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) {
311 stream->printf("error:Failed to read grid\n");
317 stream->printf("grid loaded, grid: (%f, %f), size: %d x %d\n", x_size, y_size, load_grid_x_size, load_grid_y_size);
322 bool CartGridStrategy::probe_grid(int n, int m, float _x_start, float _y_start, float _x_size, float _y_size, StreamOutput *stream)
324 if((n < 5)||(m < 5)) {
325 stream->printf("Need at least a 5x5 grid to probe\n");
330 if(!findBed()) return false;
332 float x_step = _x_size / n;
333 float y_step = _y_size / m;
334 for (int c = 0; c < m; ++c) {
335 float y = _y_start + y_step * c;
336 for (int r = 0; r < n; ++r) {
337 float x = _x_start + x_step * r;
340 if(!zprobe->doProbeAt(mm, x, y)) return false;
341 z = zprobe->getProbeHeight() - mm;
342 stream->printf("%1.4f ", z);
344 stream->printf("\n");
349 bool CartGridStrategy::handleGcode(Gcode *gcode)
352 if (gcode->g == 29) { // do a probe to test flatness
353 // first wait for an empty queue i.e. no moves left
354 THEKERNEL->conveyor->wait_for_idle();
356 int n = gcode->has_letter('I') ? gcode->get_value('I') : configured_grid_x_size;
357 int m = gcode->has_letter('J') ? gcode->get_value('J') : configured_grid_y_size;
359 float _x_size = this->x_size, _y_size = this->y_size;
360 float _x_start = this->x_start, _y_start = this->y_start;
362 if(only_by_two_corners){
363 if(gcode->has_letter('X') && gcode->has_letter('Y') && gcode->has_letter('A') && gcode->has_letter('B')){
364 _x_start = gcode->get_value('X'); // override default probe start point
365 _y_start = gcode->get_value('Y'); // override default probe start point
366 _x_size = gcode->get_value('A'); // override default probe width
367 _y_size = gcode->get_value('B'); // override default probe length
369 gcode->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n");
373 if(gcode->has_letter('X')) _x_size = gcode->get_value('X'); // override default probe width
374 if(gcode->has_letter('Y')) _y_size = gcode->get_value('Y'); // override default probe length
377 probe_grid(n, m, _x_start, _y_start, _x_size, _y_size, gcode->stream);
381 } else if( gcode->g == 31 || gcode->g == 32) { // do a grid probe
382 // first wait for an empty queue i.e. no moves left
383 THEKERNEL->conveyor->wait_for_idle();
385 if(!doProbe(gcode)) {
386 gcode->stream->printf("Probe failed to complete, check the initial probe height and/or initial_height settings\n");
388 gcode->stream->printf("Probe completed\n");
393 } else if(gcode->has_m) {
394 if(gcode->m == 370 || gcode->m == 561) { // M370: Clear bed, M561: Set Identity Transform
395 // delete the compensationTransform in robot
396 setAdjustFunction(false);
398 gcode->stream->printf("grid cleared and disabled\n");
401 } else if(gcode->m == 374) { // M374: Save grid, M374.1: delete saved grid
402 if(gcode->subcode == 1) {
404 gcode->stream->printf("%s deleted\n", GRIDFILE);
406 save_grid(gcode->stream);
411 } else if(gcode->m == 375) { // M375: load grid, M375.1 display grid
412 if(gcode->subcode == 1) {
413 print_bed_level(gcode->stream);
415 if(load_grid(gcode->stream)) setAdjustFunction(true);
419 } else if(gcode->m == 565) { // M565: Set Z probe offsets
420 float x = 0, y = 0, z = 0;
421 if(gcode->has_letter('X')) x = gcode->get_value('X');
422 if(gcode->has_letter('Y')) y = gcode->get_value('Y');
423 if(gcode->has_letter('Z')) z = gcode->get_value('Z');
424 probe_offsets = std::make_tuple(x, y, z);
427 } else if(gcode->m == 500 || gcode->m == 503) { // M500 save, M503 display
429 std::tie(x, y, z) = probe_offsets;
430 gcode->stream->printf(";Probe offsets:\nM565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z);
432 if(!isnan(grid[0])) gcode->stream->printf(";Load saved grid\nM375\n");
433 else if(gcode->m == 503) gcode->stream->printf(";WARNING No grid to save\n");
442 #define X_PROBE_OFFSET_FROM_EXTRUDER std::get<0>(probe_offsets)
443 #define Y_PROBE_OFFSET_FROM_EXTRUDER std::get<1>(probe_offsets)
444 #define Z_PROBE_OFFSET_FROM_EXTRUDER std::get<2>(probe_offsets)
446 void CartGridStrategy::setAdjustFunction(bool on)
449 // set the compensationTransform in robot
450 using std::placeholders::_1;
451 using std::placeholders::_2;
452 THEROBOT->compensationTransform = std::bind(&CartGridStrategy::doCompensation, this, _1, _2); // [this](float *target, bool inverse) { doCompensation(target, inverse); };
455 THEROBOT->compensationTransform = nullptr;
459 bool CartGridStrategy::findBed()
461 if (do_home) zprobe->home();
462 float z = initial_height;
463 zprobe->coordinated_move(NAN, NAN, z, zprobe->getFastFeedrate()); // move Z only to initial_height
464 zprobe->coordinated_move(x_start - X_PROBE_OFFSET_FROM_EXTRUDER, y_start - Y_PROBE_OFFSET_FROM_EXTRUDER, NAN, zprobe->getFastFeedrate()); // move at initial_height to x_start, y_start
466 // find bed at 0,0 run at slow rate so as to not hit bed hard
468 if(!zprobe->run_probe_return(mm, zprobe->getSlowFeedrate())) return false;
470 // leave head probe_height above bed
471 float dz = zprobe->getProbeHeight() - mm;
472 zprobe->coordinated_move(NAN, NAN, dz, zprobe->getFastFeedrate(), true); // relative move
477 bool CartGridStrategy::doProbe(Gcode *gc)
479 gc->stream->printf("Rectangular Grid Probe...\n");
481 if(only_by_two_corners){
482 if(gc->has_letter('X') && gc->has_letter('Y') && gc->has_letter('A') && gc->has_letter('B')){
483 this->x_start = gc->get_value('X'); // override default probe start point, will get saved
484 this->y_start = gc->get_value('Y'); // override default probe start point, will get saved
485 this->x_size = gc->get_value('A'); // override default probe width, will get saved
486 this->y_size = gc->get_value('B'); // override default probe length, will get saved
488 gc->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n");
492 if(gc->has_letter('X')) this->x_size = gc->get_value('X'); // override default probe width, will get saved
493 if(gc->has_letter('Y')) this->y_size = gc->get_value('Y'); // override default probe length, will get saved
496 setAdjustFunction(false);
499 if(gc->has_letter('I')) current_grid_x_size = gc->get_value('I'); // override default grid x size
500 if(gc->has_letter('J')) current_grid_y_size = gc->get_value('J'); // override default grid y size
502 if((this->current_grid_x_size * this->current_grid_y_size) > (this->configured_grid_x_size * this->configured_grid_y_size)){
503 gc->stream->printf("Grid size (%d x %d = %d) bigger than configured (%d x %d = %d). Change configuration.\n",
504 this->current_grid_x_size, this->current_grid_y_size, this->current_grid_x_size*this->current_grid_x_size,
505 this->configured_grid_x_size, this->configured_grid_y_size, this->configured_grid_x_size*this->configured_grid_y_size);
509 // find bed, and leave probe probe height above bed
511 gc->stream->printf("Finding bed failed, check the initial height setting\n");
515 gc->stream->printf("Probe start ht is %f mm, rectangular bed width %fmm, height %fmm, grid size is %dx%d\n", zprobe->getProbeHeight(), x_size, y_size, current_grid_x_size, current_grid_y_size);
517 // do first probe for 0,0
519 if(!zprobe->doProbeAt(mm, this->x_start - X_PROBE_OFFSET_FROM_EXTRUDER, this->y_start - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false;
520 float z_reference = zprobe->getProbeHeight() - mm; // this should be zero
521 gc->stream->printf("probe at 0,0 is %f mm\n", z_reference);
523 // probe all the points of the grid
524 for (int yCount = 0; yCount < this->current_grid_y_size; yCount++) {
525 float yProbe = this->y_start + (this->y_size / (this->current_grid_y_size - 1)) * yCount;
526 int xStart, xStop, xInc;
528 xStart = this->current_grid_x_size - 1;
533 xStop = this->current_grid_x_size;
537 for (int xCount = xStart; xCount != xStop; xCount += xInc) {
538 float xProbe = this->x_start + (this->x_size / (this->current_grid_x_size - 1)) * xCount;
540 if(!zprobe->doProbeAt(mm, xProbe - X_PROBE_OFFSET_FROM_EXTRUDER, yProbe - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false;
541 float measured_z = zprobe->getProbeHeight() - mm - z_reference; // this is the delta z from bed at 0,0
542 gc->stream->printf("DEBUG: X%1.4f, Y%1.4f, Z%1.4f\n", xProbe, yProbe, measured_z);
543 grid[xCount + (this->current_grid_x_size * yCount)] = measured_z;
547 print_bed_level(gc->stream);
549 setAdjustFunction(true);
554 void CartGridStrategy::doCompensation(float *target, bool inverse)
556 // Adjust print surface height by linear interpolation over the bed_level array.
558 // find min/maxes, and handle the case where size is negative (assuming this is possible? Legacy code supported this)
559 float min_x = std::min(this->x_start, this->x_start + this->x_size);
560 float max_x = std::max(this->x_start, this->x_start + this->x_size);
561 float min_y = std::min(this->y_start, this->y_start + this->y_size);
562 float max_y = std::max(this->y_start, this->y_start + this->y_size);
564 // clamp the input to the bounds of the compensation grid
565 // if a point is beyond the bounds of the grid, it will get the offset of the closest grid point
566 float x_target = std::min(std::max(target[X_AXIS], min_x), max_x);
567 float y_target = std::min(std::max(target[Y_AXIS], min_y), max_y);
569 float grid_x = std::max(0.001F, (x_target - this->x_start) / (this->x_size / (this->current_grid_x_size - 1)));
570 float grid_y = std::max(0.001F, (y_target - this->y_start) / (this->y_size / (this->current_grid_y_size - 1)));
571 int floor_x = floorf(grid_x);
572 int floor_y = floorf(grid_y);
573 float ratio_x = grid_x - floor_x;
574 float ratio_y = grid_y - floor_y;
575 float z1 = grid[(floor_x) + ((floor_y) * this->current_grid_x_size)];
576 float z2 = grid[(floor_x) + ((floor_y + 1) * this->current_grid_x_size)];
577 float z3 = grid[(floor_x + 1) + ((floor_y) * this->current_grid_x_size)];
578 float z4 = grid[(floor_x + 1) + ((floor_y + 1) * this->current_grid_x_size)];
579 float left = (1 - ratio_y) * z1 + ratio_y * z2;
580 float right = (1 - ratio_y) * z3 + ratio_y * z4;
581 float offset = (1 - ratio_x) * left + ratio_x * right;
582 // offset scale: 1 for default (use offset as is)
585 if (!isnan(this->damping_interval)) {
586 // first let's find out our 'world coordinate' positions for checking the limits:
587 Robot::wcs_t world_coordinates = THEROBOT->mcs2wcs(THEROBOT->get_axis_position());
588 float current_z = std::get<Z_AXIS>(world_coordinates); // no need to convert to mm, if machine is in inches; so is config!
589 // THEKERNEL->streams->printf("//DEBUG: Current Z: %f\n", current_z);
590 // if the height is below our compensation limit:
591 if(current_z <= this->height_limit) {
592 // scale the offset as necessary:
593 if( current_z >= this->dampening_start) {
594 scale = ( 1- ( (current_z - this->dampening_start ) / this->damping_interval) );
595 } // else leave scale at 1.0;
597 scale = 0.0; // if Z is higher than max, no compensation
602 target[Z_AXIS] -= offset * scale;
604 target[Z_AXIS] += offset * scale;
607 /*THEKERNEL->streams->printf("//DEBUG: TARGET: %f, %f, %f\n", target[0], target[1], target[2]);
608 THEKERNEL->streams->printf("//DEBUG: grid_x= %f\n", grid_x);
609 THEKERNEL->streams->printf("//DEBUG: grid_y= %f\n", grid_y);
610 THEKERNEL->streams->printf("//DEBUG: floor_x= %d\n", floor_x);
611 THEKERNEL->streams->printf("//DEBUG: floor_y= %d\n", floor_y);
612 THEKERNEL->streams->printf("//DEBUG: ratio_x= %f\n", ratio_x);
613 THEKERNEL->streams->printf("//DEBUG: ratio_y= %f\n", ratio_y);
614 THEKERNEL->streams->printf("//DEBUG: z1= %f\n", z1);
615 THEKERNEL->streams->printf("//DEBUG: z2= %f\n", z2);
616 THEKERNEL->streams->printf("//DEBUG: z3= %f\n", z3);
617 THEKERNEL->streams->printf("//DEBUG: z4= %f\n", z4);
618 THEKERNEL->streams->printf("//DEBUG: left= %f\n", left);
619 THEKERNEL->streams->printf("//DEBUG: right= %f\n", right);
620 THEKERNEL->streams->printf("//DEBUG: offset= %f\n", offset);
621 THEKERNEL->streams->printf("//DEBUG: scale= %f\n", scale);
626 // Print calibration results for plotting or manual frame adjustment.
627 void CartGridStrategy::print_bed_level(StreamOutput *stream)
630 for (int y = 0; y < current_grid_y_size; y++) {
631 for (int x = 0; x < current_grid_x_size; x++) {
632 stream->printf("%1.4f ", grid[x + (current_grid_x_size * y)]);
634 stream->printf("\n");
638 int xStart = (x_size>0) ? 0 : (current_grid_x_size - 1);
639 int xStop = (x_size>0) ? current_grid_x_size : -1;
640 int xInc = (x_size>0) ? 1: -1;
642 int yStart = (y_size<0) ? 0 : (current_grid_y_size - 1);
643 int yStop = (y_size<0) ? current_grid_y_size : -1;
644 int yInc = (y_size<0) ? 1: -1;
646 for (int y = yStart; y != yStop; y += yInc) {
647 stream->printf("%10.4f|", y * (y_size / (current_grid_y_size - 1)));
648 for (int x = xStart; x != xStop; x += xInc) {
649 stream->printf("%10.4f ", grid[x + (current_grid_x_size * y)]);
651 stream->printf("\n");
654 for (int x = xStart; x != xStop; x += xInc) {
655 stream->printf("-----+-----");
657 stream->printf("\n");
659 for (int x = xStart; x != xStop; x += xInc) {
660 stream->printf("%1.4f ", x * (x_size / (current_grid_x_size - 1)));
662 stream->printf("\n");
668 // Reset calibration results to zero.
669 void CartGridStrategy::reset_bed_level()
671 for (int y = 0; y < current_grid_y_size; y++) {
672 for (int x = 0; x < current_grid_x_size; x++) {
673 grid[x + (current_grid_x_size * y)] = NAN;