my $DELTA_PRINTABLE_RADIUS = 42.0;
my $DELTA_PROBABLE_RADIUS = $DELTA_PRINTABLE_RADIUS;
my $LEFT_PROBE_BED_POSITION = -$DELTA_PROBABLE_RADIUS;
my $RIGHT_PROBE_BED_POSITION = $DELTA_PROBABLE_RADIUS;
my $BACK_PROBE_BED_POSITION = $DELTA_PROBABLE_RADIUS;
my $FRONT_PROBE_BED_POSITION = -$DELTA_PROBABLE_RADIUS;
my $Z_RAISE_BEFORE_PROBING = 100.0;
my $Z_RAISE_BETWEEN_PROBINGS = 5.0;
my $ACCURATE_BED_LEVELING_POINTS = 7;
my $ACCURATE_BED_LEVELING_GRID_X = (($RIGHT_PROBE_BED_POSITION - $LEFT_PROBE_BED_POSITION) / ($ACCURATE_BED_LEVELING_POINTS - 1.0));
my $ACCURATE_BED_LEVELING_GRID_Y = (($BACK_PROBE_BED_POSITION - $FRONT_PROBE_BED_POSITION) / ($ACCURATE_BED_LEVELING_POINTS - 1.0));

$|=1;	# don't buffer STDOUT

print "G28\n";
print "G28\n";
reset_bed_level();
print "G0 Z100 F8000\n";
print "G0 F2000\n";

my $curr_z = $Z_RAISE_BEFORE_PROBING;
my ($xcount,$ycount);
my (@bed_level);
OUTER: for ($ycount=0; $ycount < $ACCURATE_BED_LEVELING_POINTS; $ycount++) {
	my $curr_y = $FRONT_PROBE_BED_POSITION +
			($ACCURATE_BED_LEVELING_GRID_Y * $ycount);
	my ($xstart,$xstop,$xinc);
	if ($ycount % 2) {
		$xstart = 0;
		$xstop = $ACCURATE_BED_LEVELING_POINTS;
		$xinc = 1;
	} else {
		$xstart = $ACCURATE_BED_LEVELING_POINTS - 1;
		$xstop = -1;
		$xinc = -1;
	}
	for ($xcount = $xstart; $xcount != $xstop; $xcount += $xinc) {
		my $curr_x = $LEFT_PROBE_BED_POSITION +
				($ACCURATE_BED_LEVELING_GRID_X * $xcount);
		my $done = 0;
		my $distance = sqrt($curr_x*$curr_x + $curr_y*$curr_y);
		if (1) { # ($distance <= $DELTA_PRINTABLE_RADIUS)
			while (!$done) {
				printf("G0 X%.2f Y%.2f Z%.2f\n", $curr_x, $curr_y, $curr_z);
				print STDERR "z=$curr_z (x=down, -x=up, 'n'ext point, 'q'uit, '=' 0.1mm, '\\' 0.05mm):\n";
				my $A=<>;
				chomp $A;
				if ($A =~ /^(-?[0-9.]+)/) {
					$curr_z -= $1;
				} elsif ($A =~ /^n/) {
					$done = 1;
				} elsif ($A =~ /^=/) {
					# descend by 0.1mm
					$curr_z -= 0.1;
				} elsif ($A =~ /^\\/) {
					# descend by 0.05mm
					$curr_z -= 0.05;
				} elsif ($A =~ /^q/) {
					last OUTER;
				}
			}
			$bed_level[$xcount][$ycount] = $curr_z;
			$curr_z += $Z_RAISE_BETWEEN_PROBINGS;
			printf("G0 Z%.2f\n", $curr_z);
		}
	}
}

print "G0 Z100 F8000\n";
print "G0 X0 Y0\n";
print "G0 Z200\n";

my (@t) = (localtime);
my $now = sprintf("%04d%02d%02d-%02d%02d%02d",
		$t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);

open OUT,">level.$now";
for ($ycount = 0; $ycount < $ACCURATE_BED_LEVELING_POINTS; $ycount++) {
	for ($xcount = 0; $xcount < $ACCURATE_BED_LEVELING_POINTS; $xcount++) {
#		my $curr_x = $LEFT_PROBE_BED_POSITION +
#				($ACCURATE_BED_LEVELING_GRID_X * $xcount);
#		my $curr_y = $FRONT_PROBE_BED_POSITION +
#			($ACCURATE_BED_LEVELING_GRID_Y * $ycount);
#		print OUT sprintf("M403 X$xcount Y$ycount Z%.2f (%.1f %.1f)\n", $bed_level[$xcount][$ycount], $curr_x, $curr_y);
		print OUT sprintf("M403 X$xcount Y$ycount Z%.2f\n", $bed_level[$xcount][$ycount]);
	}
}

sub reset_bed_level {
	for ($ycount = 0; $ycount < $ACCURATE_BED_LEVELING_POINTS; $ycount++) {
		for ($xcount = 0; $xcount < $ACCURATE_BED_LEVELING_POINTS; $xcount++) {
			print "M403 X$xcount Y$ycount Z0\n";
		}
	}
}
