use image::{GenericImageView, ImageReader, Rgba}; const TOAST_TIME_SECONDS: u64 = 10; const CENTER_X: f64 = 100.0; const CENTER_Y: f64 = 100.0; const BREAD_HEIGHT: f64 = 0.3; const IDLE_HEIGHT: f64 = BREAD_HEIGHT + 8.0; const PIXEL_SIZE_MM: f64 = 1.5; fn main() -> anyhow::Result<()> { let img = ImageReader::open("/home/stephen/Downloads/scd31-qr.png")?.decode()?; preamble(); // 2x oversampling for x in 0..(img.width() * 2 - 1) { for y in 0..(img.height() * 2 - 1) { let black = is_black(img.get_pixel(x / 2, y / 2)) && is_black(img.get_pixel(x / 2 + x % 2, y / 2)) && is_black(img.get_pixel(x / 2, y / 2 + y % 2)) && is_black(img.get_pixel(x / 2 + x % 2, y / 2 + y % 2)); if black { toast( x as f64 - (img.width() as f64 / 2.0), y as f64 + (img.height() as f64 / 2.0), // correct for reflection when printing ); } } } Ok(()) } fn is_black(p: Rgba<u8>) -> bool { match p.0 { [0, 0, 0, 255] => true, [255, 255, 255, 255] => false, _ => panic!("Pixel was not entirely black nor entirely white: {:?}", p), } } fn preamble() { println!( r#" G28 ; home G90 ; absolute positioning G0 Z{IDLE_HEIGHT} G0 X{CENTER_X} Y{CENTER_Y} M109 S260 ; heat hotend and wait "# ); } fn toast(x: f64, y: f64) { println!( r#" G0 X{:.2} Y{:.2}; G0 Z{BREAD_HEIGHT}; G4 S{TOAST_TIME_SECONDS} ; wait G0 Z{IDLE_HEIGHT}; "#, x * PIXEL_SIZE_MM + CENTER_X, y * PIXEL_SIZE_MM + CENTER_Y ); }