2 Author: Quentin Harley (quentin.harley@gmail.com)
3 License: GPL3 or better see <http://www.gnu.org/licenses/>
7 Probes user defined amount of calculated points on the bed and creates compensation grid data of the bed surface.
9 As the head moves in X and Y it will adjust Z to keep the head level with the bed.
13 The strategy must be enabled in the config as well as zprobe.
15 leveling-strategy.ZGrid-leveling.enable true
17 The bed size limits must be defined, in order for the module to calculate the calibration points
19 leveling-strategy.ZGrid-leveling.bed_x 200
20 leveling-strategy.ZGrid-leveling.bed_y 200
22 Machine height, used to determine probe attachment point (bed_z / 2)
24 leveling-strategy.ZGrid-leveling.bed_z 20
26 Probe attachement point, if defined, overrides the calculated point
27 leveling-strategy.ZGrid-leveling.probe_x 0
28 leveling-strategy.ZGrid-leveling.probe_y 0
29 leveling-strategy.ZGrid-leveling.probe_z 30
32 Configure for Machines with bed 0:0 at center of platform
33 leveling-strategy.ZGrid-leveling.bed_zero false
35 configure for Machines with circular beds
36 leveling-strategy.ZGrid-leveling.bed_circular false
39 The number of divisions for X and Y should be defined
41 leveling-strategy.ZGrid-leveling.rows 7 # X divisions (Default 5)
42 leveling-strategy.ZGrid-leveling.cols 9 # Y divisions (Default 5)
45 The probe offset should be defined, default to zero offset
47 leveling-strategy.ZGrid-leveling.probe_offsets 0,0,16.3
49 The machine can be told to wait for probe attachment and confirmation
51 leveling-strategy.ZGrid-leveling.wait_for_probe true
53 The machine can be told to home in one of the following modes:
55 leveling-strategy.ZGrid-leveling.home_before_probe homexyz; # nohome homexy homexyz (default)
58 Slow feedrate can be defined for probe positioning speed. Note this is not Probing slow rate - it can be set to a fast speed if required.
60 leveling-strategy.ZGrid-leveling.slow_feedrate 100 # ZGrid probe positioning feedrate
66 G32 : probes the probe points and defines the bed ZGrid, this will remain in effect until reset or M370
67 G31 : reports the status - Display probe data points
69 M370 : clears the ZGrid and the bed levelling is disabled until G32 is run again
70 M370 X7 Y9 : allocates a new grid size of 7x9 and clears as above
72 M371 : moves the head to the next calibration position without saving for manual calibration
73 M372 : move the head to the next calibration position after saving the current probe point to memory - manual calbration
74 M373 : completes calibration and enables the Z compensation grid
76 M374 : Save the grid to "Zgrid" on SD card
77 M374 S### : Save custom grid to "Zgrid.###" on SD card
79 M375 : Loads grid file "Zgrid" from SD
80 M375 S### : Load custom grid file "Zgrid.###"
82 M565 X### Y### Z### : Set new probe offsets
84 M500 : saves the probe offsets
85 M503 : displays the current settings
88 #include "ZGridStrategy.h"
92 #include "StreamOutputPool.h"
94 #include "checksumm.h"
95 #include "ConfigValue.h"
96 #include "PublicDataRequest.h"
97 #include "PublicData.h"
98 #include "EndstopsPublicAccess.h"
101 #include "libs/FileStream.h"
102 #include "nuts_bolts.h"
103 #include "platform_memory.h"
104 #include "MemoryPool.h"
105 #include "libs/utils.h"
113 #define bed_x_checksum CHECKSUM("bed_x")
114 #define bed_y_checksum CHECKSUM("bed_y")
115 #define bed_z_checksum CHECKSUM("bed_z")
117 #define probe_x_checksum CHECKSUM("probe_x")
118 #define probe_y_checksum CHECKSUM("probe_y")
119 #define probe_z_checksum CHECKSUM("probe_z")
121 #define slow_feedrate_checksum CHECKSUM("slow_feedrate")
122 #define probe_offsets_checksum CHECKSUM("probe_offsets")
123 #define wait_for_probe_checksum CHECKSUM("wait_for_probe")
124 #define home_before_probe_checksum CHECKSUM("home_before_probe")
125 #define center_zero_checksum CHECKSUM("center_zero")
126 #define circular_bed_checksum CHECKSUM("circular_bed")
127 #define cal_offset_x_checksum CHECKSUM("cal_offset_x")
128 #define cal_offset_y_checksum CHECKSUM("cal_offset_y")
134 #define cols_checksum CHECKSUM("cols")
135 #define rows_checksum CHECKSUM("rows")
137 #define probe_points (this->numRows * this->numCols)
141 ZGridStrategy::ZGridStrategy(ZProbe
*zprobe
) : LevelingStrategy(zprobe
)
143 this->cal
[X_AXIS
] = 0.0f
;
144 this->cal
[Y_AXIS
] = 0.0f
;
145 this->cal
[Z_AXIS
] = 30.0f
;
147 this->in_cal
= false;
148 this->pData
= nullptr;
151 ZGridStrategy::~ZGridStrategy()
153 // Free program memory for the pData grid
154 if(this->pData
!= nullptr) AHB0
.dealloc(this->pData
);
157 bool ZGridStrategy::handleConfig()
159 this->bed_x
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, bed_x_checksum
)->by_default(200.0F
)->as_number();
160 this->bed_y
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, bed_y_checksum
)->by_default(200.0F
)->as_number();
161 this->bed_z
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, bed_z_checksum
)->by_default(20.0F
)->as_number();
163 this->probe_x
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, probe_x_checksum
)->by_default(this->center_zero
? this->bed_x
/ 2.0F
: 0.0F
)->as_number();
164 this->probe_y
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, probe_y_checksum
)->by_default(this->center_zero
? this->bed_y
/ 2.0F
: 0.0F
)->as_number();
165 this->probe_z
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, probe_z_checksum
)->by_default(this->bed_z
/ 2.0F
)->as_number(); // Do this to keep default settings the same
167 this->slow_rate
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, slow_feedrate_checksum
)->by_default(20.0F
)->as_number();
169 this->numRows
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, rows_checksum
)->by_default(5)->as_number();
170 this->numCols
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, cols_checksum
)->by_default(5)->as_number();
172 this->wait_for_probe
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, wait_for_probe_checksum
)->by_default(true)->as_bool(); // Morgan default = true
174 std::string home_mode
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, home_before_probe_checksum
)->by_default("homexyz")->as_string();
175 if (home_mode
.compare("nohome") == 0) {
176 this->home_before_probe
= NOHOME
;
178 else if (home_mode
.compare("homexy") == 0) {
179 this->home_before_probe
= HOMEXY
;
182 this->home_before_probe
= HOMEXYZ
;
186 this->center_zero
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, center_zero_checksum
)->by_default(false)->as_bool();
187 this->circular_bed
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, circular_bed_checksum
)->by_default(false)->as_bool();
189 // configures calbration positioning offset. Defaults to 0 for standard cartesian space machines, and to negative half of the current bed size in X and Y
190 this->cal_offset_x
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, cal_offset_x_checksum
)->by_default( this->center_zero
? this->bed_x
/ -2.0F
: 0.0F
)->as_number();
191 this->cal_offset_y
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, cal_offset_y_checksum
)->by_default( this->center_zero
? this->bed_y
/ -2.0F
: 0.0F
)->as_number();
194 // Probe offsets xxx,yyy,zzz
195 std::string po
= THEKERNEL
->config
->value(leveling_strategy_checksum
, ZGrid_leveling_checksum
, probe_offsets_checksum
)->by_default("0,0,0")->as_string();
196 this->probe_offsets
= parseXYZ(po
.c_str());
198 this->calcConfig(); // Run calculations for Grid size and allocate initial grid memory
200 for (int i
=0; i
<(probe_points
); i
++){
201 this->pData
[i
] = 0.0F
; // Clear the grid
207 void ZGridStrategy::calcConfig()
209 this->bed_div_x
= this->bed_x
/ float(this->numRows
-1); // Find divisors to calculate the calbration points
210 this->bed_div_y
= this->bed_y
/ float(this->numCols
-1);
212 // Ensure free program memory for the pData grid
213 if(this->pData
!= nullptr) AHB0
.dealloc(this->pData
);
215 // Allocate program memory for the pData grid
216 this->pData
= (float *)AHB0
.alloc(probe_points
* sizeof(float));
219 bool ZGridStrategy::handleGcode(Gcode
*gcode
)
221 string args
= get_arguments(gcode
->get_command());
225 if( gcode
->g
== 31 ) { // report status
227 // Bed ZGrid data as gcode:
228 gcode
->stream
->printf(";Bed Level settings:\r\n");
230 for (int x
=0; x
<this->numRows
; x
++){
231 gcode
->stream
->printf("X%i",x
);
232 for (int y
=0; y
<this->numCols
; y
++){
233 gcode
->stream
->printf(" %c%1.2f", 'A'+y
, this->pData
[(x
*this->numCols
)+y
]);
235 gcode
->stream
->printf("\r\n");
239 } else if( gcode
->g
== 32 ) { //run probe
240 // first wait for an empty queue i.e. no moves left
241 THEKERNEL
->conveyor
->wait_for_empty_queue();
243 this->setAdjustFunction(false); // Disable leveling code
244 if(!doProbing(gcode
->stream
)) {
245 gcode
->stream
->printf("Probe failed to complete, probe not triggered or other error\n");
247 this->setAdjustFunction(true); // Enable leveling code
248 gcode
->stream
->printf("Probe completed, bed grid defined\n");
253 } else if(gcode
->has_m
) {
256 // manual bed ZGrid calbration: M370 - M375
257 // M370: Clear current ZGrid for calibration, and move to first position
259 this->setAdjustFunction(false); // Disable leveling code
260 this->cal
[Z_AXIS
] = std::get
<Z_AXIS
>(this->probe_offsets
) + zprobe
->getProbeHeight();
263 if(gcode
->has_letter('X')) // Rows (X)
264 this->numRows
= gcode
->get_value('X');
265 if(gcode
->has_letter('Y')) // Cols (Y)
266 this->numCols
= gcode
->get_value('Y');
268 this->calcConfig(); // Run calculations for Grid size and allocate grid memory
271 for (int i
=0; i
<probe_points
; i
++){
272 this->pData
[i
] = 0.0F
; // Clear the ZGrid
275 this->cal
[X_AXIS
] = 0.0f
; // Clear calibration position
276 this->cal
[Y_AXIS
] = 0.0f
;
277 this->in_cal
= true; // In calbration mode
281 // M371: Move to next manual calibration position
284 this->move(this->cal
, slow_rate
);
290 // M372: save current position in ZGrid, and move to next calibration position
296 THEROBOT
->get_axis_position(cartesian
); // get actual position from robot
298 pindex
= int(cartesian
[X_AXIS
]/this->bed_div_x
+ 0.25)*this->numCols
+ int(cartesian
[Y_AXIS
]/this->bed_div_y
+ 0.25);
300 this->move(this->cal
, slow_rate
); // move to the next position
301 this->next_cal(); // to not cause damage to machine due to Z-offset
303 this->pData
[pindex
] = cartesian
[Z_AXIS
]; // save the offset
308 // M373: finalize calibration
310 // normalize the grid
311 this->normalize_grid_2home();
313 this->in_cal
= false;
314 this->setAdjustFunction(true); // Enable leveling code
323 if(gcode
->has_letter('S')) // Custom grid number
324 snprintf(gridname
, sizeof(gridname
), "S%03.0f", gcode
->get_value('S'));
328 if(this->saveGrid(gridname
)) {
329 gcode
->stream
->printf("Grid saved: Filename: /sd/Zgrid.%s\n",gridname
);
332 gcode
->stream
->printf("Error: Grid not saved: Filename: /sd/Zgrid.%s\n",gridname
);
337 case 375:{ // Load grid values
340 if(gcode
->has_letter('S')) // Custom grid number
341 snprintf(gridname
, sizeof(gridname
), "S%03.0f", gcode
->get_value('S'));
345 if(this->loadGrid(gridname
)) {
346 this->setAdjustFunction(true); // Enable leveling code
347 gcode
->stream
->printf("Grid loaded: /sd/Zgrid.%s\n",gridname
);
350 gcode
->stream
->printf("Error: Grid not loaded: /sd/Zgrid.%s\n",gridname
);
355 /* case 376: { // Check grid value calculations: For debug only.
358 for(char letter = 'X'; letter <= 'Z'; letter++) {
359 if( gcode->has_letter(letter) ) {
360 target[letter - 'X'] = gcode->get_value(letter);
363 gcode->stream->printf(" Z0 %1.3f\n",getZOffset(target[0], target[1]));
368 case 565: { // M565: Set Z probe offsets
369 float x
= 0, y
= 0, z
= 0;
370 if(gcode
->has_letter('X')) x
= gcode
->get_value('X');
371 if(gcode
->has_letter('Y')) y
= gcode
->get_value('Y');
372 if(gcode
->has_letter('Z')) z
= gcode
->get_value('Z');
373 probe_offsets
= std::make_tuple(x
, y
, z
);
377 case 500: // M500 saves probe_offsets config override file
378 gcode
->stream
->printf(";Load default grid\nM375\n");
381 case 503: { // M503 just prints the settings
384 gcode
->stream
->printf(";Probe offsets:\n");
385 std::tie(x
, y
, z
) = probe_offsets
;
386 gcode
->stream
->printf("M565 X%1.5f Y%1.5f Z%1.5f\n", x
, y
, z
);
398 bool ZGridStrategy::saveGrid(std::string args
)
400 args
= "/sd/Zgrid." + args
;
401 StreamOutput
*ZMap_file
= new FileStream(args
.c_str());
403 ZMap_file
->printf("P%i %i %i %1.3f\n", probe_points
, this->numRows
, this->numCols
, getZhomeoffset()); // Store probe points to prevent loading undefined grid files
405 for (int pos
= 0; pos
< probe_points
; pos
++){
406 ZMap_file
->printf("%1.3f\n", this->pData
[pos
]);
414 bool ZGridStrategy::loadGrid(std::string args
)
418 int fpoints
, GridX
= 5, GridY
= 5; // for 25point file
421 args
= "/sd/Zgrid." + args
;
422 FILE *fd
= fopen(args
.c_str(), "r");
424 fscanf(fd
, "%s\n", flag
);
428 sscanf(flag
, "P%i\n", &fpoints
); // read number of points, and Grid X and Y
429 fscanf(fd
, "%i %i %f\n", &GridX
, &GridY
, &GridZ
); // read number of points, and Grid X and Y and ZHoming offset
430 fscanf(fd
, "%f\n", &val
); // read first value from file
432 } else { // original 25point file -- Backwards compatibility
434 sscanf(flag
, "%f\n", &val
); // read first value from string
437 if (GridX
!= this->numRows
|| GridY
!= this->numCols
){
438 this->numRows
= GridX
; // Change Rows and Columns to match the saved data
439 this->numCols
= GridY
;
440 this->calcConfig(); // Reallocate memory for the grid according to the grid loaded
443 this->pData
[0] = val
; // Place the first read value in grid
445 for (int pos
= 1; pos
< probe_points
; pos
++){
446 fscanf(fd
, "%f\n", &val
);
447 this->pData
[pos
] = val
;
452 this->setZoffset(GridZ
);
462 float ZGridStrategy::getZhomeoffset()
466 bool ok
= PublicData::get_value( endstops_checksum
, home_offset_checksum
, &rd
);
469 return ((float*)rd
)[2];
475 void ZGridStrategy::setZoffset(float zval
)
479 // Assemble Gcode to add onto the queue
480 snprintf(cmd
, sizeof(cmd
), "M206 Z%1.3f", zval
); // Send saved Z homing offset
482 Gcode
gc(cmd
, &(StreamOutput::NullStream
));
483 THEKERNEL
->call_event(ON_GCODE_RECEIVED
, &gc
);
487 bool ZGridStrategy::doProbing(StreamOutput
*stream
) // probed calibration
489 // home first using selected mode: NOHOME, HOMEXY, HOMEXYZ
492 // deactivate correction during moves
493 this->setAdjustFunction(false);
495 for (int i
=0; i
<probe_points
; i
++){
496 this->pData
[i
] = 0.0F
; // Clear the ZGrid
499 if (this->wait_for_probe
){
501 this->cal
[X_AXIS
] = this->probe_x
; //bed_x/2.0f;
502 this->cal
[Y_AXIS
] = this->probe_y
; //bed_y/2.0f;
503 this->cal
[Z_AXIS
] = this->probe_z
; //bed_z/2.0f; // Position head for probe attachment
504 this->move(this->cal
, slow_rate
); // Move to probe attachment point
506 stream
->printf("*** Ensure probe is attached and press probe when done ***\n");
508 while(!zprobe
->getProbeStatus()){ // Wait for button press
509 THEKERNEL
->call_event(ON_IDLE
);
513 this->in_cal
= true; // In calbration mode
515 this->cal
[X_AXIS
] = 0.0f
; // Clear calibration position
516 this->cal
[Y_AXIS
] = 0.0f
;
517 this->cal
[Z_AXIS
] = std::get
<Z_AXIS
>(this->probe_offsets
) + zprobe
->getProbeHeight();
519 this->move(this->cal
, slow_rate
); // Move to probe start point
521 for (int probes
= 0; probes
< probe_points
; probes
++){
524 // z = z home offset - probed distance
525 float z
= getZhomeoffset() -zprobe
->probeDistance((this->cal
[X_AXIS
] + this->cal_offset_x
)-std::get
<X_AXIS
>(this->probe_offsets
),
526 (this->cal
[Y_AXIS
] + this->cal_offset_y
)-std::get
<Y_AXIS
>(this->probe_offsets
));
528 pindex
= int(this->cal
[X_AXIS
]/this->bed_div_x
+ 0.25)*this->numCols
+ int(this->cal
[Y_AXIS
]/this->bed_div_y
+ 0.25);
530 this->next_cal(); // Calculate next calibration position
532 this->pData
[pindex
] = z
; // save the offset
535 stream
->printf("\nCalibration done.\n");
536 if (this->wait_for_probe
) { // Only do this it the config calls for probe removal position
537 this->cal
[X_AXIS
] = this->bed_x
/2.0f
;
538 this->cal
[Y_AXIS
] = this->bed_y
/2.0f
;
539 this->cal
[Z_AXIS
] = this->bed_z
/2.0f
; // Position head for probe removal
540 this->move(this->cal
, slow_rate
);
542 stream
->printf("Please remove probe\n");
546 // activate correction
547 //this->normalize_grid();
548 this->normalize_grid_2home();
549 this->setAdjustFunction(true);
551 this->in_cal
= false;
557 void ZGridStrategy::normalize_grid_2home()
562 bool ok
= PublicData::get_value( endstops_checksum
, home_offset_checksum
, &rd
);
565 home_Z_comp
= this->getZOffset(((float*)rd
)[0],((float*)rd
)[1]); // find the Z compensation at home position
571 // subtracts the home compensation offset to create a table of deltas, normalized to home compensation zero
572 for (int i
= 0; i
< probe_points
; i
++)
573 this->pData
[i
] -= home_Z_comp
;
575 // Doing this removes the need to change homing offset in Z because the reference remains unchanged.
577 // add the offset to the current Z homing offset to preserve full probed offset.
578 // this->setZoffset(this->getZhomeoffset() + home_Z_comp);
582 void ZGridStrategy::homexyz()
585 switch(this->home_before_probe
) {
586 case NOHOME
: return;
589 Gcode
gc("G28 X0 Y0", &(StreamOutput::NullStream
));
590 THEKERNEL
->call_event(ON_GCODE_RECEIVED
, &gc
);
595 Gcode
gc("G28", &(StreamOutput::NullStream
));
596 THEKERNEL
->call_event(ON_GCODE_RECEIVED
, &gc
);
602 void ZGridStrategy::move(float *position
, float feed
)
604 // translate the position for non standard cartesian spaces (cal_offset)
605 zprobe
->coordinated_move(position
[0] + this->cal_offset_x
, position
[1] + this->cal_offset_y
, position
[2], feed
); // use specified feedrate (mm/sec)
607 //THEKERNEL->streams->printf("DEBUG: move: %s cent: %i\n", cmd, this->center_zero);
611 void ZGridStrategy::next_cal(void){
612 if ((((int) roundf(this->cal
[X_AXIS
] / this->bed_div_x
)) & 1) != 0){ // Odd row
613 this->cal
[Y_AXIS
] -= this->bed_div_y
;
614 if (this->cal
[Y_AXIS
] < (0.0F
- (bed_div_y
/ 2.0f
))){
616 //THEKERNEL->streams->printf("DEBUG: Y (%f) < cond (%f)\n",this->cal[Y_AXIS], 0.0F);
618 this->cal
[X_AXIS
] += bed_div_x
;
619 if (this->cal
[X_AXIS
] > (this->bed_x
+ (this->bed_div_x
/ 2.0f
))){
620 this->cal
[X_AXIS
] = 0.0F
;
621 this->cal
[Y_AXIS
] = 0.0F
;
624 this->cal
[Y_AXIS
] = 0.0F
;
627 else { // Even row (0 is an even row - starting point)
628 this->cal
[Y_AXIS
] += bed_div_y
;
629 if (this->cal
[Y_AXIS
] > (this->bed_y
+ (bed_div_y
/ 2.0f
))){
631 //THEKERNEL->streams->printf("DEBUG: Y (%f) > cond (%f)\n",this->cal[Y_AXIS], this->bed_y);
633 this->cal
[X_AXIS
] += bed_div_x
;
634 if (this->cal
[X_AXIS
] > (this->bed_x
+ (this->bed_div_x
/ 2.0f
))){
635 this->cal
[X_AXIS
] = 0.0F
;
636 this->cal
[Y_AXIS
] = 0.0F
;
639 this->cal
[Y_AXIS
] = this->bed_y
;
645 void ZGridStrategy::setAdjustFunction(bool on
)
648 // set the compensationTransform in robot
649 THEROBOT
->compensationTransform
= [this](float target
[3]) { target
[2] += this->getZOffset(target
[0], target
[1]); };
652 THEROBOT
->compensationTransform
= nullptr;
657 // find the Z offset for the point on the plane at x, y
658 float ZGridStrategy::getZOffset(float X
, float Y
)
660 int xIndex2
, yIndex2
;
662 // Subtract calibration offsets as applicable
663 X
-= this->cal_offset_x
;
664 Y
-= this->cal_offset_y
;
666 float xdiff
= X
/ this->bed_div_x
;
667 float ydiff
= Y
/ this->bed_div_y
;
669 float dCartX1
, dCartX2
;
671 // Get floor of xdiff. Note that (int) of a negative number is its
672 // ceiling, not its floor.
674 int xIndex
= (int)(floorf(xdiff
)); // Get the current sector (X)
675 int yIndex
= (int)(floorf(ydiff
)); // Get the current sector (Y)
677 // Index bounds limited to be inside the table
678 if (xIndex
< 0) xIndex
= 0;
679 else if (xIndex
> (this->numRows
- 2)) xIndex
= this->numRows
- 2;
681 if (yIndex
< 0) yIndex
= 0;
682 else if (yIndex
> (this->numCols
- 2)) yIndex
= this->numCols
- 2;
687 xdiff
-= xIndex
; // Find floating point
688 ydiff
-= yIndex
; // Find floating point
690 dCartX1
= (1-xdiff
) * this->pData
[(xIndex
*this->numCols
)+yIndex
] + (xdiff
) * this->pData
[(xIndex2
)*this->numCols
+yIndex
];
691 dCartX2
= (1-xdiff
) * this->pData
[(xIndex
*this->numCols
)+yIndex2
] + (xdiff
) * this->pData
[(xIndex2
)*this->numCols
+yIndex2
];
693 return ydiff
* dCartX2
+ (1-ydiff
) * dCartX1
; // Calculated Z-delta
697 // parse a "X,Y,Z" string return x,y,z tuple
698 std::tuple
<float, float, float> ZGridStrategy::parseXYZ(const char *str
)
700 float x
= 0, y
= 0, z
= 0;
703 if(p
+ 1 < str
+ strlen(str
)) {
704 y
= strtof(p
+ 1, &p
);
705 if(p
+ 1 < str
+ strlen(str
)) {
706 z
= strtof(p
+ 1, nullptr);
709 return std::make_tuple(x
, y
, z
);