From b3acb63f79387c4474ed9245b68a0644406782f6 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 10 Aug 2021 12:12:12 +0100 Subject: [PATCH 001/188] Update to released versions of e-g and simulator --- Cargo.toml | 4 ---- debug-tools/Cargo.toml | 4 ++-- framework/src/lib.rs | 5 ++++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63888fa..14ff055 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,3 @@ members = [ "framework", "debug-tools", ] - -[patch.crates-io] -embedded-graphics = { git = "https://github.com/embedded-graphics/embedded-graphics.git"} -embedded-graphics-simulator = { git = "https://github.com/embedded-graphics/simulator.git"} diff --git a/debug-tools/Cargo.toml b/debug-tools/Cargo.toml index 18aae25..b07017e 100644 --- a/debug-tools/Cargo.toml +++ b/debug-tools/Cargo.toml @@ -7,5 +7,5 @@ publish = false [dependencies] framework = { path = "../framework" } -embedded-graphics = "0.7.0-beta.2" -embedded-graphics-simulator = "0.3.0-alpha.2" +embedded-graphics = "0.7.1" +embedded-graphics-simulator = "0.3.0" diff --git a/framework/src/lib.rs b/framework/src/lib.rs index 918fb8b..fe0c02b 100644 --- a/framework/src/lib.rs +++ b/framework/src/lib.rs @@ -44,7 +44,10 @@ pub trait AppExt: App { fn run(window: Window); } -impl AppExt for T { +impl AppExt for T +where + ::Color: From, +{ fn run(mut window: Window) { let mut app = T::new(); let mut display = SimulatorDisplay::new(T::DISPLAY_SIZE); From 9e601eeda39ec3c25f8629511d319cdd93ff1eee Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 10 Aug 2021 12:46:43 +0100 Subject: [PATCH 002/188] Crude copypasta thick line --- debug-tools/examples/line.rs | 191 +++++++++++++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 9 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 54a14cb..3b01cb8 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -1,4 +1,5 @@ use embedded_graphics::{ + mock_display::MockDisplay, pixelcolor::Rgb565, prelude::*, primitives::{Line, PrimitiveStyle}, @@ -6,6 +7,163 @@ use embedded_graphics::{ use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; +fn thin_octant1( + display: &mut impl DrawTarget, + x0: i32, + y0: i32, + dx: i32, + dy: i32, +) -> Result<(), std::convert::Infallible> { + let mut error = 0; + let mut y = y0; + let mut x = x0; + let mut threshold = dx - 2 * dy; + let mut E_diag = -2 * dx; + let mut E_square = 2 * dy; + let mut length = dx; + + for p in 1..length { + Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; + + if error > threshold { + y += 1; + error += E_diag; + } + error += E_square; + x += 1; + } + + Ok(()) +} + +fn thick_octant1( + display: &mut impl DrawTarget, + x0: i32, + y0: i32, + dx: i32, + dy: i32, +) -> Result<(), std::convert::Infallible> { + // the perpendicular error or 'phase' + let mut p_error = 0; + let mut error = 0; + let mut y = y0; + let mut x = x0; + let mut threshold = dx - 2 * dy; + let mut E_diag = -2 * dx; + let mut E_square = 2 * dy; + let mut length = dx; + + for p in 1..length { + pleft_octant1(display, x, y, dx, dy, p_error)?; + pright_octant1(display, x, y, dx, dy, p_error)?; + + if error > threshold { + y = y + 1; + error = error + E_diag; + if p_error > threshold { + pleft_octant1(display, x, y, dx, dy, p_error + E_diag + E_square)?; + // FIXME: Overdraw + // pright_octant1(display, x, y, dx, dy, p_error + E_diag + E_square)?; + p_error = p_error + E_diag; + } + p_error = p_error + E_square; + } + error = error + E_square; + x = x + 1; + } + + Ok(()) +} + +fn pleft_octant1( + display: &mut impl DrawTarget, + x0: i32, + y0: i32, + dx: i32, + dy: i32, + mut error: i32, +) -> Result<(), std::convert::Infallible> { + let mut y = y0; + let mut x = x0; + let mut threshold = dx - 2 * dy; + let mut E_diag = -2 * dx; + let mut E_square = 2 * dy; + let mut thickness = 10; + + for p in 1..thickness { + Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; + + if error > threshold { + x = x - 1; + error = error + E_diag; + } + error = error + E_square; + y = y + 1; + } + + Ok(()) +} + +fn pright_octant1( + display: &mut impl DrawTarget, + x0: i32, + y0: i32, + dx: i32, + dy: i32, + mut error: i32, +) -> Result<(), std::convert::Infallible> { + let mut y = y0; + let mut x = x0; + let mut threshold = dx - 2 * dy; + let mut E_diag = -2 * dx; + let mut E_square = 2 * dy; + let mut thickness = 10; + + error = -error; + + for p in 1..thickness { + if error > threshold { + x = x + 1; + error = error + E_diag; + } + error = error + E_square; + y = y - 1; + + Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; + } + + Ok(()) +} + +// fn pright_octant1( +// display: &mut impl DrawTarget, +// x0: i32, +// y0: i32, +// dx: i32, +// dy: i32, +// mut error: i32, +// ) -> Result<(), std::convert::Infallible> { +// let mut y = y0; +// let mut x = x0; +// let mut threshold = dx - 2 * dy; +// let mut E_diag = -2 * dx; +// let mut E_square = 2 * dy; +// let mut thickness = 10; + +// for p in 1..thickness { +// if error < -threshold { +// x = x + 1; +// error = error - E_diag; +// } +// error = error - E_square; +// y = y - 1; + +// Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; +// } + +// Ok(()) +// } + struct LineDebug { start: Point, end: Point, @@ -14,12 +172,12 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - const DISPLAY_SIZE: Size = Size::new(256, 256); + const DISPLAY_SIZE: Size = Size::new(64, 64); fn new() -> Self { Self { - start: Point::new(128, 128), - end: Point::new(150, 170), + start: Point::new(32, 32), + end: Point::new(40, 50), stroke_width: 1, } } @@ -36,12 +194,27 @@ impl App for LineDebug { &self, display: &mut SimulatorDisplay, ) -> Result<(), std::convert::Infallible> { - Line::new(self.start, self.end) - .into_styled(PrimitiveStyle::with_stroke( - Rgb565::GREEN, - self.stroke_width, - )) - .draw(display) + let Point { x: x0, y: y0 } = self.start; + let Point { x: x1, y: y1 } = self.end; + + let dx = x1 - x0; + let dy = y1 - y0; + + // thin_octant1(display, x0, y0, dx, dy)?; + thick_octant1(display, x0, y0, dx, dy)?; + + let mut mock_display = MockDisplay::new(); + + thick_octant1(&mut mock_display, x0, y0, dx, dy).unwrap(); + + Ok(()) + + // Line::new(self.start, self.end) + // .into_styled(PrimitiveStyle::with_stroke( + // Rgb565::GREEN, + // self.stroke_width, + // )) + // .draw(display) } } From 71eed97d59bbcbbe8b192f3a2ec3ebeaa5ed7c04 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 10 Aug 2021 12:54:14 +0100 Subject: [PATCH 003/188] Copypasta thickness compensated --- debug-tools/examples/line.rs | 319 +++++++++++++++++++++++------------ 1 file changed, 208 insertions(+), 111 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 3b01cb8..6d33067 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -7,163 +7,263 @@ use embedded_graphics::{ use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; -fn thin_octant1( - display: &mut impl DrawTarget, - x0: i32, - y0: i32, - dx: i32, - dy: i32, -) -> Result<(), std::convert::Infallible> { - let mut error = 0; - let mut y = y0; - let mut x = x0; - let mut threshold = dx - 2 * dy; - let mut E_diag = -2 * dx; - let mut E_square = 2 * dy; - let mut length = dx; +// fn thin_octant1( +// display: &mut impl DrawTarget, +// x0: i32, +// y0: i32, +// dx: i32, +// dy: i32, +// ) -> Result<(), std::convert::Infallible> { +// let mut error = 0; +// let mut y = y0; +// let mut x = x0; +// let mut threshold = dx - 2 * dy; +// let mut E_diag = -2 * dx; +// let mut E_square = 2 * dy; +// let mut length = dx; - for p in 1..length { - Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; +// for p in 1..length { +// Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; - if error > threshold { - y += 1; - error += E_diag; - } - error += E_square; - x += 1; - } +// if error > threshold { +// y += 1; +// error += E_diag; +// } +// error += E_square; +// x += 1; +// } - Ok(()) -} +// Ok(()) +// } -fn thick_octant1( +// fn thick_octant1( +// display: &mut impl DrawTarget, +// x0: i32, +// y0: i32, +// dx: i32, +// dy: i32, +// ) -> Result<(), std::convert::Infallible> { +// // the perpendicular error or 'phase' +// let mut p_error = 0; +// let mut error = 0; +// let mut y = y0; +// let mut x = x0; +// let mut threshold = dx - 2 * dy; +// let mut E_diag = -2 * dx; +// let mut E_square = 2 * dy; +// let mut length = dx; + +// for p in 1..length { +// pleft_octant1(display, x, y, dx, dy, p_error)?; +// pright_octant1(display, x, y, dx, dy, p_error)?; + +// if error > threshold { +// y = y + 1; +// error = error + E_diag; +// if p_error > threshold { +// pleft_octant1(display, x, y, dx, dy, p_error + E_diag + E_square)?; +// // FIXME: Overdraw +// // pright_octant1(display, x, y, dx, dy, p_error + E_diag + E_square)?; +// p_error = p_error + E_diag; +// } +// p_error = p_error + E_square; +// } +// error = error + E_square; +// x = x + 1; +// } + +// Ok(()) +// } + +// fn pleft_octant1( +// display: &mut impl DrawTarget, +// x0: i32, +// y0: i32, +// dx: i32, +// dy: i32, +// mut error: i32, +// ) -> Result<(), std::convert::Infallible> { +// let mut y = y0; +// let mut x = x0; +// let mut threshold = dx - 2 * dy; +// let mut E_diag = -2 * dx; +// let mut E_square = 2 * dy; +// let mut thickness = 10; + +// for p in 1..thickness { +// Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; + +// if error > threshold { +// x = x - 1; +// error = error + E_diag; +// } +// error = error + E_square; +// y = y + 1; +// } + +// Ok(()) +// } + +// fn pright_octant1( +// display: &mut impl DrawTarget, +// x0: i32, +// y0: i32, +// dx: i32, +// dy: i32, +// mut error: i32, +// ) -> Result<(), std::convert::Infallible> { +// let mut y = y0; +// let mut x = x0; +// let mut threshold = dx - 2 * dy; +// let mut E_diag = -2 * dx; +// let mut E_square = 2 * dy; +// let mut thickness = 10; + +// error = -error; + +// for p in 1..thickness { +// if error > threshold { +// x = x + 1; +// error = error + E_diag; +// } +// error = error + E_square; +// y = y - 1; + +// Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; +// } + +// Ok(()) +// } + +// // fn pright_octant1( +// // display: &mut impl DrawTarget, +// // x0: i32, +// // y0: i32, +// // dx: i32, +// // dy: i32, +// // mut error: i32, +// // ) -> Result<(), std::convert::Infallible> { +// // let mut y = y0; +// // let mut x = x0; +// // let mut threshold = dx - 2 * dy; +// // let mut E_diag = -2 * dx; +// // let mut E_square = 2 * dy; +// // let mut thickness = 10; + +// // for p in 1..thickness { +// // if error < -threshold { +// // x = x + 1; +// // error = error - E_diag; +// // } +// // error = error - E_square; +// // y = y - 1; + +// // Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; +// // } + +// // Ok(()) +// // } + +fn perp_octant1( display: &mut impl DrawTarget, x0: i32, y0: i32, dx: i32, dy: i32, + einit: i32, + width: i32, + winit: i32, ) -> Result<(), std::convert::Infallible> { - // the perpendicular error or 'phase' - let mut p_error = 0; - let mut error = 0; - let mut y = y0; - let mut x = x0; let mut threshold = dx - 2 * dy; let mut E_diag = -2 * dx; let mut E_square = 2 * dy; - let mut length = dx; + let mut wthr = 2 * width * f32::sqrt((dx * dx + dy * dy) as f32) as i32; - for p in 1..length { - pleft_octant1(display, x, y, dx, dy, p_error)?; - pright_octant1(display, x, y, dx, dy, p_error)?; + let mut x = x0; + let mut y = y0; + let mut error = einit; + let mut tk = dx + dy - winit; + while tk <= wthr { + Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; if error > threshold { - y = y + 1; + x = x - 1; error = error + E_diag; - if p_error > threshold { - pleft_octant1(display, x, y, dx, dy, p_error + E_diag + E_square)?; - // FIXME: Overdraw - // pright_octant1(display, x, y, dx, dy, p_error + E_diag + E_square)?; - p_error = p_error + E_diag; - } - p_error = p_error + E_square; + tk = tk + 2 * dy; } error = error + E_square; - x = x + 1; + y = y + 1; + tk = tk + 2 * dx; } - Ok(()) -} - -fn pleft_octant1( - display: &mut impl DrawTarget, - x0: i32, - y0: i32, - dx: i32, - dy: i32, - mut error: i32, -) -> Result<(), std::convert::Infallible> { - let mut y = y0; let mut x = x0; - let mut threshold = dx - 2 * dy; - let mut E_diag = -2 * dx; - let mut E_square = 2 * dy; - let mut thickness = 10; + let mut y = y0; - for p in 1..thickness { - Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; + let mut error = -einit; + let mut tk = dx + dy + winit; + while tk <= wthr { + Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; if error > threshold { - x = x - 1; + x = x + 1; error = error + E_diag; + tk = tk + 2 * dy; } error = error + E_square; - y = y + 1; + y = y - 1; + tk = tk + 2 * dx; } Ok(()) } -fn pright_octant1( +fn thick_octant1( display: &mut impl DrawTarget, x0: i32, y0: i32, - dx: i32, - dy: i32, - mut error: i32, + x1: i32, + y1: i32, + width: i32, ) -> Result<(), std::convert::Infallible> { + let mut dx = x1 - x0; + let mut dy = y1 - y0; + let mut p_error = 0; + let mut error = 0; let mut y = y0; let mut x = x0; let mut threshold = dx - 2 * dy; let mut E_diag = -2 * dx; let mut E_square = 2 * dy; - let mut thickness = 10; + let mut length = dx + 1; - error = -error; + for p in 1..length { + perp_octant1(display, x, y, dx, dy, p_error, width, error)?; - for p in 1..thickness { if error > threshold { - x = x + 1; + y = y + 1; error = error + E_diag; + if p_error > threshold { + perp_octant1( + display, + x, + y, + dx, + dy, + p_error + E_diag + E_square, + width, + error, + )?; + p_error = p_error + E_diag; + } + p_error = p_error + E_square; } error = error + E_square; - y = y - 1; - - Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; + x = x + 1; } Ok(()) } -// fn pright_octant1( -// display: &mut impl DrawTarget, -// x0: i32, -// y0: i32, -// dx: i32, -// dy: i32, -// mut error: i32, -// ) -> Result<(), std::convert::Infallible> { -// let mut y = y0; -// let mut x = x0; -// let mut threshold = dx - 2 * dy; -// let mut E_diag = -2 * dx; -// let mut E_square = 2 * dy; -// let mut thickness = 10; - -// for p in 1..thickness { -// if error < -threshold { -// x = x + 1; -// error = error - E_diag; -// } -// error = error - E_square; -// y = y - 1; - -// Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; -// } - -// Ok(()) -// } - struct LineDebug { start: Point, end: Point, @@ -197,15 +297,12 @@ impl App for LineDebug { let Point { x: x0, y: y0 } = self.start; let Point { x: x1, y: y1 } = self.end; - let dx = x1 - x0; - let dy = y1 - y0; - // thin_octant1(display, x0, y0, dx, dy)?; - thick_octant1(display, x0, y0, dx, dy)?; - - let mut mock_display = MockDisplay::new(); + thick_octant1(display, x0, y0, x1, y1, 10)?; - thick_octant1(&mut mock_display, x0, y0, dx, dy).unwrap(); + // FIXME: Overdraw + // let mut mock_display = MockDisplay::new(); + // thick_octant1(&mut mock_display, x0, y0, x1, y1, 10).unwrap(); Ok(()) From fcea4fddd41c2d0d10ce6da6b4afe11bc96f0e14 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 10 Aug 2021 17:42:49 +0100 Subject: [PATCH 004/188] Crude port of C demo code --- debug-tools/examples/line.rs | 471 ++++++++++++++++++++--------------- 1 file changed, 272 insertions(+), 199 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 6d33067..4a717c6 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -1,3 +1,5 @@ +use std::convert::TryFrom; + use embedded_graphics::{ mock_display::MockDisplay, pixelcolor::Rgb565, @@ -7,169 +9,14 @@ use embedded_graphics::{ use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; -// fn thin_octant1( -// display: &mut impl DrawTarget, -// x0: i32, -// y0: i32, -// dx: i32, -// dy: i32, -// ) -> Result<(), std::convert::Infallible> { -// let mut error = 0; -// let mut y = y0; -// let mut x = x0; -// let mut threshold = dx - 2 * dy; -// let mut E_diag = -2 * dx; -// let mut E_square = 2 * dy; -// let mut length = dx; - -// for p in 1..length { -// Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; - -// if error > threshold { -// y += 1; -// error += E_diag; -// } -// error += E_square; -// x += 1; -// } - -// Ok(()) -// } - -// fn thick_octant1( -// display: &mut impl DrawTarget, -// x0: i32, -// y0: i32, -// dx: i32, -// dy: i32, -// ) -> Result<(), std::convert::Infallible> { -// // the perpendicular error or 'phase' -// let mut p_error = 0; -// let mut error = 0; -// let mut y = y0; -// let mut x = x0; -// let mut threshold = dx - 2 * dy; -// let mut E_diag = -2 * dx; -// let mut E_square = 2 * dy; -// let mut length = dx; - -// for p in 1..length { -// pleft_octant1(display, x, y, dx, dy, p_error)?; -// pright_octant1(display, x, y, dx, dy, p_error)?; - -// if error > threshold { -// y = y + 1; -// error = error + E_diag; -// if p_error > threshold { -// pleft_octant1(display, x, y, dx, dy, p_error + E_diag + E_square)?; -// // FIXME: Overdraw -// // pright_octant1(display, x, y, dx, dy, p_error + E_diag + E_square)?; -// p_error = p_error + E_diag; -// } -// p_error = p_error + E_square; -// } -// error = error + E_square; -// x = x + 1; -// } - -// Ok(()) -// } - -// fn pleft_octant1( -// display: &mut impl DrawTarget, -// x0: i32, -// y0: i32, -// dx: i32, -// dy: i32, -// mut error: i32, -// ) -> Result<(), std::convert::Infallible> { -// let mut y = y0; -// let mut x = x0; -// let mut threshold = dx - 2 * dy; -// let mut E_diag = -2 * dx; -// let mut E_square = 2 * dy; -// let mut thickness = 10; - -// for p in 1..thickness { -// Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; - -// if error > threshold { -// x = x - 1; -// error = error + E_diag; -// } -// error = error + E_square; -// y = y + 1; -// } - -// Ok(()) -// } - -// fn pright_octant1( -// display: &mut impl DrawTarget, -// x0: i32, -// y0: i32, -// dx: i32, -// dy: i32, -// mut error: i32, -// ) -> Result<(), std::convert::Infallible> { -// let mut y = y0; -// let mut x = x0; -// let mut threshold = dx - 2 * dy; -// let mut E_diag = -2 * dx; -// let mut E_square = 2 * dy; -// let mut thickness = 10; - -// error = -error; - -// for p in 1..thickness { -// if error > threshold { -// x = x + 1; -// error = error + E_diag; -// } -// error = error + E_square; -// y = y - 1; - -// Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; -// } - -// Ok(()) -// } - -// // fn pright_octant1( -// // display: &mut impl DrawTarget, -// // x0: i32, -// // y0: i32, -// // dx: i32, -// // dy: i32, -// // mut error: i32, -// // ) -> Result<(), std::convert::Infallible> { -// // let mut y = y0; -// // let mut x = x0; -// // let mut threshold = dx - 2 * dy; -// // let mut E_diag = -2 * dx; -// // let mut E_square = 2 * dy; -// // let mut thickness = 10; - -// // for p in 1..thickness { -// // if error < -threshold { -// // x = x + 1; -// // error = error - E_diag; -// // } -// // error = error - E_square; -// // y = y - 1; - -// // Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; -// // } - -// // Ok(()) -// // } - -fn perp_octant1( +fn x_perpendicular( display: &mut impl DrawTarget, x0: i32, y0: i32, dx: i32, dy: i32, + xstep: i32, + ystep: i32, einit: i32, width: i32, winit: i32, @@ -177,56 +24,71 @@ fn perp_octant1( let mut threshold = dx - 2 * dy; let mut E_diag = -2 * dx; let mut E_square = 2 * dy; - let mut wthr = 2 * width * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + let mut p = 0; + let mut q = 0; - let mut x = x0; let mut y = y0; + let mut x = x0; let mut error = einit; let mut tk = dx + dy - winit; - while tk <= wthr { - Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; - if error > threshold { - x = x - 1; - error = error + E_diag; - tk = tk + 2 * dy; + while tk <= width { + Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; + + if error >= threshold { + x += xstep; + error += E_diag; + tk += 2 * dy; } - error = error + E_square; - y = y + 1; - tk = tk + 2 * dx; + + error += E_square; + y += ystep; + tk += 2 * dx; + q += 1; } - let mut x = x0; let mut y = y0; - + let mut x = x0; let mut error = -einit; let mut tk = dx + dy + winit; - while tk <= wthr { - Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; + while tk <= width { + if p > 0 { + Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; + } + if error > threshold { - x = x + 1; - error = error + E_diag; - tk = tk + 2 * dy; + x = x - xstep; + error += E_diag; + tk += 2 * dy; } - error = error + E_square; - y = y - 1; - tk = tk + 2 * dx; + + error += E_square; + y = y - ystep; + tk += 2 * dx; + p += 1; + } + + // we need this for very thin lines + if q == 0 && p < 2 { + Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display)?; } Ok(()) } -fn thick_octant1( +fn x_varthick_line( display: &mut impl DrawTarget, x0: i32, y0: i32, - x1: i32, - y1: i32, + dx: i32, + dy: i32, + xstep: i32, + ystep: i32, + pxstep: i32, + pystep: i32, width: i32, ) -> Result<(), std::convert::Infallible> { - let mut dx = x1 - x0; - let mut dy = y1 - y0; let mut p_error = 0; let mut error = 0; let mut y = y0; @@ -235,30 +97,158 @@ fn thick_octant1( let mut E_diag = -2 * dx; let mut E_square = 2 * dy; let mut length = dx + 1; + let mut d = width * 2 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + + for p in 0..length { + x_perpendicular(display, x, y, dx, dy, pxstep, pystep, p_error, d, error)?; + if error >= threshold { + y += ystep; + error += E_diag; + if p_error >= threshold { + x_perpendicular( + display, + x, + y, + dx, + dy, + pxstep, + pystep, + p_error + E_diag + E_square, + d, + error, + )?; + p_error += E_diag; + } + p_error += E_square; + } + error += E_square; + x += xstep; + } + + Ok(()) +} + +/*********************************************************************** + * * + * Y BASED LINES * + * * + ***********************************************************************/ + +fn y_perpendicular( + display: &mut impl DrawTarget, + x0: i32, + y0: i32, + dx: i32, + dy: i32, + xstep: i32, + ystep: i32, + einit: i32, + width: i32, + winit: i32, +) -> Result<(), std::convert::Infallible> { + let mut p = 0; + let mut q = 0; + let mut threshold = dy - 2 * dx; + let mut E_diag = -2 * dy; + let mut E_square = 2 * dx; - for p in 1..length { - perp_octant1(display, x, y, dx, dy, p_error, width, error)?; + let mut y = y0; + let mut x = x0; + let mut error = -einit; + let mut tk = dx + dy + winit; + + while tk <= width { + Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; if error > threshold { - y = y + 1; - error = error + E_diag; - if p_error > threshold { - perp_octant1( + y += ystep; + error += E_diag; + tk += 2 * dx; + } + + error += E_square; + x += xstep; + tk += 2 * dy; + q += 1; + } + + let mut y = y0; + let mut x = x0; + let mut error = einit; + let mut tk = dx + dy - winit; + + while tk <= width { + if p > 0 { + Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; + } + + if error >= threshold { + y = y - ystep; + error += E_diag; + tk += 2 * dx; + } + + error += E_square; + x = x - xstep; + tk += 2 * dy; + p += 1; + } + + // we need this for very thin lines + if q == 0 && p < 2 { + Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display)?; + } + + Ok(()) +} + +fn y_varthick_line( + display: &mut impl DrawTarget, + x0: i32, + y0: i32, + dx: i32, + dy: i32, + xstep: i32, + ystep: i32, + pxstep: i32, + pystep: i32, + width: i32, +) -> Result<(), std::convert::Infallible> { + let mut p_error = 0; + let mut error = 0; + let mut y = y0; + let mut x = x0; + let mut threshold = dy - 2 * dx; + let mut E_diag = -2 * dy; + let mut E_square = 2 * dx; + let mut length = dy + 1; + let mut d = width * 2 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + + for p in 0..length { + y_perpendicular(display, x, y, dx, dy, pxstep, pystep, p_error, d, error)?; + + if error >= threshold { + x += xstep; + error += E_diag; + if p_error >= threshold { + y_perpendicular( display, x, y, dx, dy, + pxstep, + pystep, p_error + E_diag + E_square, - width, + d, error, )?; - p_error = p_error + E_diag; + p_error += E_diag; } - p_error = p_error + E_square; + p_error += E_square; } - error = error + E_square; - x = x + 1; + error += E_square; + y += ystep; } Ok(()) @@ -272,12 +262,16 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - const DISPLAY_SIZE: Size = Size::new(64, 64); + const DISPLAY_SIZE: Size = Size::new(256, 256); fn new() -> Self { + let start = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); Self { - start: Point::new(32, 32), - end: Point::new(40, 50), + start, + end: start + Point::new(40, 50), stroke_width: 1, } } @@ -297,8 +291,87 @@ impl App for LineDebug { let Point { x: x0, y: y0 } = self.start; let Point { x: x1, y: y1 } = self.end; + let pxstep; + let pystep; + let mut xch = 0; // whether left and right get switched. + + let mut dx = x1 - x0; + let mut dy = y1 - y0; + + let width = 20; + + let mut xstep = 1; + let mut ystep = 1; + + if dx < 0 { + dx = -dx; + xstep = -1; + } + if dy < 0 { + dy = -dy; + ystep = -1; + } + + if dx == 0 { + xstep = 0; + } + if dy == 0 { + ystep = 0; + } + + match (xstep, ystep) { + (-1, -1) => { + pystep = -1; + pxstep = 1; + xch = 1; + } + (-1, 0) => { + pystep = -1; + pxstep = 0; + xch = 1; + } + (-1, 1) => { + pystep = 1; + pxstep = 1; + } + (0, -1) => { + pystep = 0; + pxstep = -1; + } + (0, 0) => { + pystep = 0; + pxstep = 0; + } + (0, 1) => { + pystep = 0; + pxstep = 1; + } + (1, -1) => { + pystep = -1; + pxstep = -1; + } + (1, 0) => { + pystep = -1; + pxstep = 0; + } + (1, 1) => { + pystep = 1; + pxstep = -1; + xch = 1; + } + _ => unreachable!(), + } + + // TODO: xch or swap_sides + + if dx > dy { + x_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; + } else { + y_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; + } + // thin_octant1(display, x0, y0, dx, dy)?; - thick_octant1(display, x0, y0, x1, y1, 10)?; + // thick_octant1(display, x0, y0, x1, y1, 10)?; // FIXME: Overdraw // let mut mock_display = MockDisplay::new(); From 93bd56d3564e6c3a954084fb293b1b281421d7a5 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 10 Aug 2021 17:48:50 +0100 Subject: [PATCH 005/188] Different kind of bad width calculation --- debug-tools/examples/line.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 4a717c6..e8a8c7e 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -97,10 +97,9 @@ fn x_varthick_line( let mut E_diag = -2 * dx; let mut E_square = 2 * dy; let mut length = dx + 1; - let mut d = width * 2 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; for p in 0..length { - x_perpendicular(display, x, y, dx, dy, pxstep, pystep, p_error, d, error)?; + x_perpendicular(display, x, y, dx, dy, pxstep, pystep, p_error, width, error)?; if error >= threshold { y += ystep; error += E_diag; @@ -114,7 +113,7 @@ fn x_varthick_line( pxstep, pystep, p_error + E_diag + E_square, - d, + width, error, )?; p_error += E_diag; @@ -222,10 +221,9 @@ fn y_varthick_line( let mut E_diag = -2 * dy; let mut E_square = 2 * dx; let mut length = dy + 1; - let mut d = width * 2 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; for p in 0..length { - y_perpendicular(display, x, y, dx, dy, pxstep, pystep, p_error, d, error)?; + y_perpendicular(display, x, y, dx, dy, pxstep, pystep, p_error, width, error)?; if error >= threshold { x += xstep; @@ -240,7 +238,7 @@ fn y_varthick_line( pxstep, pystep, p_error + E_diag + E_square, - d, + width, error, )?; p_error += E_diag; @@ -298,7 +296,7 @@ impl App for LineDebug { let mut dx = x1 - x0; let mut dy = y1 - y0; - let width = 20; + let width = self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; let mut xstep = 1; let mut ystep = 1; From 93a9af4313956aee0dd1afa4d284779dd15562d2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 10 Aug 2021 17:55:11 +0100 Subject: [PATCH 006/188] Check for overdraw --- debug-tools/examples/line.rs | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index e8a8c7e..1a3012e 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -260,7 +260,7 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - const DISPLAY_SIZE: Size = Size::new(256, 256); + const DISPLAY_SIZE: Size = Size::new(64, 64); fn new() -> Self { let start = Point::new( @@ -269,7 +269,7 @@ impl App for LineDebug { ); Self { start, - end: start + Point::new(40, 50), + end: start + Point::new(25, 27), stroke_width: 1, } } @@ -362,19 +362,42 @@ impl App for LineDebug { // TODO: xch or swap_sides + let mut mock_display = MockDisplay::new(); + mock_display.set_allow_out_of_bounds_drawing(true); + if dx > dy { x_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; + x_varthick_line( + &mut mock_display, + x0, + y0, + dx, + dy, + xstep, + ystep, + pxstep, + pystep, + width, + )?; } else { y_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; + y_varthick_line( + &mut mock_display, + x0, + y0, + dx, + dy, + xstep, + ystep, + pxstep, + pystep, + width, + )?; } // thin_octant1(display, x0, y0, dx, dy)?; // thick_octant1(display, x0, y0, x1, y1, 10)?; - // FIXME: Overdraw - // let mut mock_display = MockDisplay::new(); - // thick_octant1(&mut mock_display, x0, y0, x1, y1, 10).unwrap(); - Ok(()) // Line::new(self.start, self.end) From ad0bb563751a448893aed841f1c047c29c4a70bf Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 10 Aug 2021 21:34:13 +0100 Subject: [PATCH 007/188] WIP correct centered width --- debug-tools/examples/line.rs | 122 ++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 39 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 1a3012e..fe85186 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -20,6 +20,7 @@ fn x_perpendicular( einit: i32, width: i32, winit: i32, + extra: bool, ) -> Result<(), std::convert::Infallible> { let mut threshold = dx - 2 * dy; let mut E_diag = -2 * dx; @@ -30,49 +31,88 @@ fn x_perpendicular( let mut y = y0; let mut x = x0; let mut error = einit; - let mut tk = dx + dy - winit; + // let mut tk = dx + dy - winit; - while tk <= width { - Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; + // let mut tk = (dx + dy) / 2; + // let mut tk = winit; - if error >= threshold { - x += xstep; - error += E_diag; - tk += 2 * dy; - } + let mut tk = -winit; + let mut tk2 = winit; - error += E_square; - y += ystep; - tk += 2 * dx; - q += 1; - } + let mut y2 = y0; + let mut x2 = x0; + let mut error2 = -einit; + // let mut tk2 = dx + dy + winit; - let mut y = y0; - let mut x = x0; - let mut error = -einit; - let mut tk = dx + dy + winit; + let (c1, c2) = if extra { + (Rgb565::RED, Rgb565::GREEN) + } else { + (Rgb565::CSS_CORNFLOWER_BLUE, Rgb565::YELLOW) + }; - while tk <= width { - if p > 0 { - Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; - } + let mut swap = 1; - if error > threshold { - x = x - xstep; - error += E_diag; - tk += 2 * dy; + while tk.pow(2) <= width || tk2.pow(2) <= width { + if swap == 1 && tk.pow(2) <= width { + Pixel(Point::new(x, y), c1).draw(display)?; + + if error >= threshold { + x += xstep; + error += E_diag; + tk += 2 * dy; + } + + error += E_square; + y += ystep; + tk += 2 * dx; + q += 1; + } else { + if p > 0 { + Pixel(Point::new(x2, y2), c2).draw(display)?; + } + + if error2 > threshold { + x2 -= xstep; + error2 += E_diag; + tk2 += 2 * dy; + } + + error2 += E_square; + y2 -= ystep; + tk2 += 2 * dx; + + p += 1; } - error += E_square; - y = y - ystep; - tk += 2 * dx; - p += 1; + swap *= -1; } - // we need this for very thin lines - if q == 0 && p < 2 { - Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display)?; - } + // let mut y2 = y0; + // let mut x2 = x0; + // let mut error2 = -einit; + // let mut tk = dx + dy + winit; + + // while tk <= width { + // if p > 0 { + // Pixel(Point::new(x2, y2), Rgb565::GREEN).draw(display)?; + // } + + // if error2 > threshold { + // x2 -= xstep; + // error2 += E_diag; + // tk += 2 * dy; + // } + + // error2 += E_square; + // y2 -= ystep; + // tk += 2 * dx; + // p += 1; + // } + + // // we need this for very thin lines + // if q == 0 && p < 2 { + // Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display)?; + // } Ok(()) } @@ -99,7 +139,9 @@ fn x_varthick_line( let mut length = dx + 1; for p in 0..length { - x_perpendicular(display, x, y, dx, dy, pxstep, pystep, p_error, width, error)?; + x_perpendicular( + display, x, y, dx, dy, pxstep, pystep, p_error, width, error, false, + )?; if error >= threshold { y += ystep; error += E_diag; @@ -115,6 +157,7 @@ fn x_varthick_line( p_error + E_diag + E_square, width, error, + true, )?; p_error += E_diag; } @@ -182,13 +225,13 @@ fn y_perpendicular( } if error >= threshold { - y = y - ystep; + y -= ystep; error += E_diag; tk += 2 * dx; } error += E_square; - x = x - xstep; + x -= xstep; tk += 2 * dy; p += 1; } @@ -260,7 +303,7 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - const DISPLAY_SIZE: Size = Size::new(64, 64); + const DISPLAY_SIZE: Size = Size::new(256, 256); fn new() -> Self { let start = Point::new( @@ -270,7 +313,7 @@ impl App for LineDebug { Self { start, end: start + Point::new(25, 27), - stroke_width: 1, + stroke_width: 10, } } @@ -296,7 +339,8 @@ impl App for LineDebug { let mut dx = x1 - x0; let mut dy = y1 - y0; - let width = self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); let mut xstep = 1; let mut ystep = 1; From 876e1972fea77f8a465a1bbed1156d7a62722541 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 11 Aug 2021 11:35:20 +0100 Subject: [PATCH 008/188] Crude but correct balanced width calculation --- debug-tools/examples/line.rs | 119 +++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 46 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index fe85186..668195e 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -44,6 +44,16 @@ fn x_perpendicular( let mut error2 = -einit; // let mut tk2 = dx + dy + winit; + let width = width - 1; + + // let width = width.pow(2) * (dx * dx + dy * dy); + let width_l = width; + // Put extras on right side + let width_r = width + (width % 2); + + let width_l = width_l.pow(2) * (dx * dx + dy * dy); + let width_r = width_r.pow(2) * (dx * dx + dy * dy); + let (c1, c2) = if extra { (Rgb565::RED, Rgb565::GREEN) } else { @@ -52,61 +62,76 @@ fn x_perpendicular( let mut swap = 1; - while tk.pow(2) <= width || tk2.pow(2) <= width { - if swap == 1 && tk.pow(2) <= width { - Pixel(Point::new(x, y), c1).draw(display)?; + while tk.pow(2) <= width_l { + Pixel(Point::new(x, y), c1).draw(display)?; - if error >= threshold { - x += xstep; - error += E_diag; - tk += 2 * dy; - } + if error >= threshold { + x += xstep; + error += E_diag; + tk += 2 * dy; + } - error += E_square; - y += ystep; - tk += 2 * dx; - q += 1; - } else { - if p > 0 { - Pixel(Point::new(x2, y2), c2).draw(display)?; - } + error += E_square; + y += ystep; + tk += 2 * dx; + q += 1; + } - if error2 > threshold { - x2 -= xstep; - error2 += E_diag; - tk2 += 2 * dy; - } + let mut y2 = y0; + let mut x2 = x0; + let mut error2 = -einit; + let mut tk = winit; - error2 += E_square; - y2 -= ystep; - tk2 += 2 * dx; + while tk.pow(2) <= width_r { + if p > 0 { + Pixel(Point::new(x2, y2), Rgb565::GREEN).draw(display)?; + } - p += 1; + if error2 > threshold { + x2 -= xstep; + error2 += E_diag; + tk += 2 * dy; } - swap *= -1; + error2 += E_square; + y2 -= ystep; + tk += 2 * dx; + p += 1; } - // let mut y2 = y0; - // let mut x2 = x0; - // let mut error2 = -einit; - // let mut tk = dx + dy + winit; - - // while tk <= width { - // if p > 0 { - // Pixel(Point::new(x2, y2), Rgb565::GREEN).draw(display)?; - // } - - // if error2 > threshold { - // x2 -= xstep; - // error2 += E_diag; - // tk += 2 * dy; + // while tk.pow(2) <= width_l || tk2.pow(2) <= width_r { + // if swap == 1 && tk.pow(2) <= width { + // Pixel(Point::new(x, y), c1).draw(display)?; + + // if error >= threshold { + // x += xstep; + // error += E_diag; + // tk += 2 * dy; + // } + + // error += E_square; + // y += ystep; + // tk += 2 * dx; + // q += 1; + // } else { + // if p > 0 { + // Pixel(Point::new(x2, y2), c2).draw(display)?; + // } + + // if error2 > threshold { + // x2 -= xstep; + // error2 += E_diag; + // tk2 += 2 * dy; + // } + + // error2 += E_square; + // y2 -= ystep; + // tk2 += 2 * dx; + + // p += 1; // } - // error2 += E_square; - // y2 -= ystep; - // tk += 2 * dx; - // p += 1; + // swap *= -1; // } // // we need this for very thin lines @@ -312,7 +337,8 @@ impl App for LineDebug { ); Self { start, - end: start + Point::new(25, 27), + // end: start + Point::new(25, 27), + end: start + Point::new(100, 0), stroke_width: 10, } } @@ -340,7 +366,8 @@ impl App for LineDebug { let mut dy = y1 - y0; // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; - let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; let mut xstep = 1; let mut ystep = 1; From 6775df43cee9de1532ee319737161e7ddc8ed918 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 11 Aug 2021 11:49:34 +0100 Subject: [PATCH 009/188] Line side bias impl Looks weird with 1px wide lines but it works without overdraw --- debug-tools/examples/line.rs | 148 ++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 668195e..0ba6608 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -9,6 +9,34 @@ use embedded_graphics::{ use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; +#[derive(Debug, Copy, Clone, PartialEq)] +enum LineSide { + Left, + Center, + Right, +} + +impl LineSide { + fn widths(self, width: i32) -> (i32, i32) { + match width { + // 0 => (0, 0), + width => { + match self { + Self::Left => ((width * 2).saturating_sub(1), 0), + Self::Center => { + let width = width.saturating_sub(1); + + // Right-side bias for odd width lines. Move mod2 to first item to bias to + // the left. + (width, width + (width % 2)) + } + Self::Right => (0, (width * 2).saturating_sub(1)), + } + } + } + } +} + fn x_perpendicular( display: &mut impl DrawTarget, x0: i32, @@ -22,6 +50,14 @@ fn x_perpendicular( winit: i32, extra: bool, ) -> Result<(), std::convert::Infallible> { + if width == 0 { + return Ok(()); + } + + if width == 1 { + return Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display); + } + let mut threshold = dx - 2 * dy; let mut E_diag = -2 * dx; let mut E_square = 2 * dy; @@ -44,12 +80,15 @@ fn x_perpendicular( let mut error2 = -einit; // let mut tk2 = dx + dy + winit; - let width = width - 1; + // let width = width.saturating_sub(1); + + // let width_l = width; + // // Put extras on right side. Move the %2 to width_l to place on left instead + // let width_r = width + (width % 2); - // let width = width.pow(2) * (dx * dx + dy * dy); - let width_l = width; - // Put extras on right side - let width_r = width + (width % 2); + let side = LineSide::Center; + + let (width_l, width_r) = side.widths(width); let width_l = width_l.pow(2) * (dx * dx + dy * dy); let width_r = width_r.pow(2) * (dx * dx + dy * dy); @@ -62,7 +101,9 @@ fn x_perpendicular( let mut swap = 1; - while tk.pow(2) <= width_l { + // dbg!(width_l, width_r); + + while tk.pow(2) <= width_l && width_l > 0 { Pixel(Point::new(x, y), c1).draw(display)?; if error >= threshold { @@ -82,9 +123,9 @@ fn x_perpendicular( let mut error2 = -einit; let mut tk = winit; - while tk.pow(2) <= width_r { - if p > 0 { - Pixel(Point::new(x2, y2), Rgb565::GREEN).draw(display)?; + while tk.pow(2) <= width_r && width_r > 0 { + if p > 0 && side == LineSide::Center { + Pixel(Point::new(x2, y2), c2).draw(display)?; } if error2 > threshold { @@ -99,46 +140,6 @@ fn x_perpendicular( p += 1; } - // while tk.pow(2) <= width_l || tk2.pow(2) <= width_r { - // if swap == 1 && tk.pow(2) <= width { - // Pixel(Point::new(x, y), c1).draw(display)?; - - // if error >= threshold { - // x += xstep; - // error += E_diag; - // tk += 2 * dy; - // } - - // error += E_square; - // y += ystep; - // tk += 2 * dx; - // q += 1; - // } else { - // if p > 0 { - // Pixel(Point::new(x2, y2), c2).draw(display)?; - // } - - // if error2 > threshold { - // x2 -= xstep; - // error2 += E_diag; - // tk2 += 2 * dy; - // } - - // error2 += E_square; - // y2 -= ystep; - // tk2 += 2 * dx; - - // p += 1; - // } - - // swap *= -1; - // } - - // // we need this for very thin lines - // if q == 0 && p < 2 { - // Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display)?; - // } - Ok(()) } @@ -244,27 +245,27 @@ fn y_perpendicular( let mut error = einit; let mut tk = dx + dy - winit; - while tk <= width { - if p > 0 { - Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; - } + // while tk <= width { + // if p > 0 { + // Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; + // } - if error >= threshold { - y -= ystep; - error += E_diag; - tk += 2 * dx; - } + // if error >= threshold { + // y -= ystep; + // error += E_diag; + // tk += 2 * dx; + // } - error += E_square; - x -= xstep; - tk += 2 * dy; - p += 1; - } + // error += E_square; + // x -= xstep; + // tk += 2 * dy; + // p += 1; + // } - // we need this for very thin lines - if q == 0 && p < 2 { - Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display)?; - } + // // we need this for very thin lines + // if q == 0 && p < 2 { + // Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display)?; + // } Ok(()) } @@ -328,17 +329,18 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - const DISPLAY_SIZE: Size = Size::new(256, 256); + // const DISPLAY_SIZE: Size = Size::new(256, 256); + const DISPLAY_SIZE: Size = Size::new(64, 64); fn new() -> Self { - let start = Point::new( + let end = Point::new( Self::DISPLAY_SIZE.width as i32 / 2, Self::DISPLAY_SIZE.height as i32 / 2, ); Self { - start, - // end: start + Point::new(25, 27), - end: start + Point::new(100, 0), + start: end + Point::new(10, 15), + end, + // end: start + Point::new(100, 0), stroke_width: 10, } } @@ -434,7 +436,7 @@ impl App for LineDebug { // TODO: xch or swap_sides let mut mock_display = MockDisplay::new(); - mock_display.set_allow_out_of_bounds_drawing(true); + // mock_display.set_allow_out_of_bounds_drawing(true); if dx > dy { x_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; From 2fbc56b6db22109b9676a22a3ecc1f3b15cb4107 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 11 Aug 2021 12:35:37 +0100 Subject: [PATCH 010/188] Cleanup --- debug-tools/examples/line.rs | 104 +++++++++++------------------------ 1 file changed, 33 insertions(+), 71 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 0ba6608..c6a2007 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -58,33 +58,17 @@ fn x_perpendicular( return Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display); } - let mut threshold = dx - 2 * dy; - let mut E_diag = -2 * dx; - let mut E_square = 2 * dy; + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; let mut p = 0; let mut q = 0; let mut y = y0; let mut x = x0; let mut error = einit; - // let mut tk = dx + dy - winit; - - // let mut tk = (dx + dy) / 2; - // let mut tk = winit; let mut tk = -winit; - let mut tk2 = winit; - - let mut y2 = y0; - let mut x2 = x0; - let mut error2 = -einit; - // let mut tk2 = dx + dy + winit; - - // let width = width.saturating_sub(1); - - // let width_l = width; - // // Put extras on right side. Move the %2 to width_l to place on left instead - // let width_r = width + (width % 2); let side = LineSide::Center; @@ -108,11 +92,11 @@ fn x_perpendicular( if error >= threshold { x += xstep; - error += E_diag; + error += e_minor; tk += 2 * dy; } - error += E_square; + error += e_major; y += ystep; tk += 2 * dx; q += 1; @@ -130,11 +114,11 @@ fn x_perpendicular( if error2 > threshold { x2 -= xstep; - error2 += E_diag; + error2 += e_minor; tk += 2 * dy; } - error2 += E_square; + error2 += e_major; y2 -= ystep; tk += 2 * dx; p += 1; @@ -159,10 +143,10 @@ fn x_varthick_line( let mut error = 0; let mut y = y0; let mut x = x0; - let mut threshold = dx - 2 * dy; - let mut E_diag = -2 * dx; - let mut E_square = 2 * dy; - let mut length = dx + 1; + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx + 1; for p in 0..length { x_perpendicular( @@ -170,7 +154,7 @@ fn x_varthick_line( )?; if error >= threshold { y += ystep; - error += E_diag; + error += e_minor; if p_error >= threshold { x_perpendicular( display, @@ -180,16 +164,16 @@ fn x_varthick_line( dy, pxstep, pystep, - p_error + E_diag + E_square, + p_error + e_minor + e_major, width, error, true, )?; - p_error += E_diag; + p_error += e_minor; } - p_error += E_square; + p_error += e_major; } - error += E_square; + error += e_major; x += xstep; } @@ -214,11 +198,11 @@ fn y_perpendicular( width: i32, winit: i32, ) -> Result<(), std::convert::Infallible> { - let mut p = 0; + let p = 0; let mut q = 0; - let mut threshold = dy - 2 * dx; - let mut E_diag = -2 * dy; - let mut E_square = 2 * dx; + let threshold = dy - 2 * dx; + let e_minor = -2 * dy; + let e_major = 2 * dx; let mut y = y0; let mut x = x0; @@ -230,42 +214,20 @@ fn y_perpendicular( if error > threshold { y += ystep; - error += E_diag; + error += e_minor; tk += 2 * dx; } - error += E_square; + error += e_major; x += xstep; tk += 2 * dy; q += 1; } - let mut y = y0; - let mut x = x0; - let mut error = einit; - let mut tk = dx + dy - winit; - - // while tk <= width { - // if p > 0 { - // Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; - // } - - // if error >= threshold { - // y -= ystep; - // error += E_diag; - // tk += 2 * dx; - // } - - // error += E_square; - // x -= xstep; - // tk += 2 * dy; - // p += 1; - // } - - // // we need this for very thin lines - // if q == 0 && p < 2 { - // Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display)?; - // } + let y = y0; + let x = x0; + let error = einit; + let tk = dx + dy - winit; Ok(()) } @@ -287,8 +249,8 @@ fn y_varthick_line( let mut y = y0; let mut x = x0; let mut threshold = dy - 2 * dx; - let mut E_diag = -2 * dy; - let mut E_square = 2 * dx; + let mut e_minor = -2 * dy; + let mut e_major = 2 * dx; let mut length = dy + 1; for p in 0..length { @@ -296,7 +258,7 @@ fn y_varthick_line( if error >= threshold { x += xstep; - error += E_diag; + error += e_minor; if p_error >= threshold { y_perpendicular( display, @@ -306,15 +268,15 @@ fn y_varthick_line( dy, pxstep, pystep, - p_error + E_diag + E_square, + p_error + e_minor + e_major, width, error, )?; - p_error += E_diag; + p_error += e_minor; } - p_error += E_square; + p_error += e_major; } - error += E_square; + error += e_major; y += ystep; } From 4243be42ad69508a5a8c73dcbd22096f58a5c71a Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 11 Aug 2021 14:33:43 +0100 Subject: [PATCH 011/188] All octants but with switching sides --- debug-tools/examples/line.rs | 340 +++++++++++++++++++++-------------- 1 file changed, 205 insertions(+), 135 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index c6a2007..c02757b 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -37,37 +37,70 @@ impl LineSide { } } +#[derive(Debug, Clone, Copy)] +struct MajorMinor { + major: T, + minor: T, +} + +impl MajorMinor { + fn new(major: T, minor: T) -> Self { + Self { major, minor } + } +} + fn x_perpendicular( display: &mut impl DrawTarget, x0: i32, y0: i32, - dx: i32, - dy: i32, - xstep: i32, - ystep: i32, + + delta: MajorMinor, + mut step: MajorMinor, einit: i32, width: i32, winit: i32, extra: bool, ) -> Result<(), std::convert::Infallible> { + let mut point = Point::new(x0, y0); + if width == 0 { return Ok(()); } if width == 1 { - return Pixel(Point::new(x0, y0), Rgb565::YELLOW).draw(display); + return Pixel(point, Rgb565::YELLOW).draw(display); } + let dx = delta.major; + let dy = delta.minor; + + dbg!(step.major, step.minor); + + let sign = match (step.major, step.minor) { + (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, + (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, + (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, + (Point { x: 0, y: 1 }, Point { x: 1, y: 0 }) => -1, + _ => 1, + }; + + step.major *= sign; + step.minor *= sign; + + let dx = dx.abs(); + let dy = dy.abs(); + let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; let mut p = 0; let mut q = 0; - let mut y = y0; - let mut x = x0; - let mut error = einit; + // let mut y = y0; + // let mut x = x0; + // Swap signs in some conditions + let mut error = einit; let mut tk = -winit; let side = LineSide::Center; @@ -83,43 +116,42 @@ fn x_perpendicular( (Rgb565::CSS_CORNFLOWER_BLUE, Rgb565::YELLOW) }; - let mut swap = 1; - // dbg!(width_l, width_r); while tk.pow(2) <= width_l && width_l > 0 { - Pixel(Point::new(x, y), c1).draw(display)?; + Pixel(point, c1).draw(display)?; if error >= threshold { - x += xstep; + point += step.major; error += e_minor; tk += 2 * dy; } error += e_major; - y += ystep; + point += step.minor; tk += 2 * dx; q += 1; } - let mut y2 = y0; - let mut x2 = x0; + // let mut y2 = y0; + // let mut x2 = x0; + let mut point = Point::new(x0, y0); let mut error2 = -einit; let mut tk = winit; while tk.pow(2) <= width_r && width_r > 0 { if p > 0 && side == LineSide::Center { - Pixel(Point::new(x2, y2), c2).draw(display)?; + Pixel(point, c2).draw(display)?; } if error2 > threshold { - x2 -= xstep; + point -= step.major; error2 += e_minor; tk += 2 * dy; } error2 += e_major; - y2 -= ystep; + point -= step.minor; tk += 2 * dx; p += 1; } @@ -131,18 +163,21 @@ fn x_varthick_line( display: &mut impl DrawTarget, x0: i32, y0: i32, - dx: i32, - dy: i32, - xstep: i32, - ystep: i32, - pxstep: i32, - pystep: i32, + delta: MajorMinor, + step: MajorMinor, + pstep: MajorMinor, width: i32, ) -> Result<(), std::convert::Infallible> { let mut p_error = 0; let mut error = 0; - let mut y = y0; - let mut x = x0; + // let mut y = y0; + // let mut x = x0; + + let mut point = Point::new(x0, y0); + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; @@ -150,20 +185,19 @@ fn x_varthick_line( for p in 0..length { x_perpendicular( - display, x, y, dx, dy, pxstep, pystep, p_error, width, error, false, + display, point.x, point.y, delta, pstep, p_error, width, error, false, )?; if error >= threshold { - y += ystep; + // y += ystep; + point += step.minor; error += e_minor; if p_error >= threshold { x_perpendicular( display, - x, - y, - dx, - dy, - pxstep, - pystep, + point.x, + point.y, + delta, + pstep, p_error + e_minor + e_major, width, error, @@ -174,7 +208,8 @@ fn x_varthick_line( p_error += e_major; } error += e_major; - x += xstep; + // x += xstep; + point += step.major; } Ok(()) @@ -291,8 +326,8 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - // const DISPLAY_SIZE: Size = Size::new(256, 256); - const DISPLAY_SIZE: Size = Size::new(64, 64); + const DISPLAY_SIZE: Size = Size::new(256, 256); + // const DISPLAY_SIZE: Size = Size::new(64, 64); fn new() -> Self { let end = Point::new( @@ -320,115 +355,150 @@ impl App for LineDebug { display: &mut SimulatorDisplay, ) -> Result<(), std::convert::Infallible> { let Point { x: x0, y: y0 } = self.start; - let Point { x: x1, y: y1 } = self.end; + // let Point { x: x1, y: y1 } = self.end; - let pxstep; - let pystep; - let mut xch = 0; // whether left and right get switched. + // // let pxstep; + // // let pystep; + // // let mut xch = 0; // whether left and right get switched. - let mut dx = x1 - x0; - let mut dy = y1 - y0; + // let mut dx = x1 - x0; + // let mut dy = y1 - y0; // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); let width = self.stroke_width as i32; - let mut xstep = 1; - let mut ystep = 1; - - if dx < 0 { - dx = -dx; - xstep = -1; - } - if dy < 0 { - dy = -dy; - ystep = -1; - } - - if dx == 0 { - xstep = 0; - } - if dy == 0 { - ystep = 0; - } - - match (xstep, ystep) { - (-1, -1) => { - pystep = -1; - pxstep = 1; - xch = 1; - } - (-1, 0) => { - pystep = -1; - pxstep = 0; - xch = 1; - } - (-1, 1) => { - pystep = 1; - pxstep = 1; - } - (0, -1) => { - pystep = 0; - pxstep = -1; - } - (0, 0) => { - pystep = 0; - pxstep = 0; + // let mut xstep = 1; + // let mut ystep = 1; + + // if dx < 0 { + // dx = -dx; + // xstep = -1; + // } + // if dy < 0 { + // dy = -dy; + // ystep = -1; + // } + + // if dx == 0 { + // xstep = 0; + // } + // if dy == 0 { + // ystep = 0; + // } + + // match (xstep, ystep) { + // (-1, -1) => { + // pystep = -1; + // pxstep = 1; + // xch = 1; + // } + // (-1, 0) => { + // pystep = -1; + // pxstep = 0; + // xch = 1; + // } + // (-1, 1) => { + // pystep = 1; + // pxstep = 1; + // } + // (0, -1) => { + // pystep = 0; + // pxstep = -1; + // } + // (0, 0) => { + // pystep = 0; + // pxstep = 0; + // } + // (0, 1) => { + // pystep = 0; + // pxstep = 1; + // } + // (1, -1) => { + // pystep = -1; + // pxstep = -1; + // } + // (1, 0) => { + // pystep = -1; + // pxstep = 0; + // } + // (1, 1) => { + // pystep = 1; + // pxstep = -1; + // xch = 1; + // } + // _ => unreachable!(), + // } + + // // TODO: xch or swap_sides + + let (delta, step, pstep) = { + let delta = self.end - self.start; + + let direction = Point::new( + if delta.x >= 0 { 1 } else { -1 }, + if delta.y >= 0 { 1 } else { -1 }, + ); + + let perp_direction = { + let perp_delta = Point::new(delta.y, -delta.x); + + Point::new( + if perp_delta.x >= 0 { 1 } else { -1 }, + if perp_delta.y >= 0 { 1 } else { -1 }, + ) + }; + + // let delta = delta.abs(); + + // Determine major and minor directions. + if delta.y.abs() >= delta.x.abs() { + ( + MajorMinor::new(delta.y, delta.x), + MajorMinor::new(direction.y_axis(), direction.x_axis()), + MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), + ) + } else { + ( + MajorMinor::new(delta.x, delta.y), + MajorMinor::new(direction.x_axis(), direction.y_axis()), + MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), + ) } - (0, 1) => { - pystep = 0; - pxstep = 1; - } - (1, -1) => { - pystep = -1; - pxstep = -1; - } - (1, 0) => { - pystep = -1; - pxstep = 0; - } - (1, 1) => { - pystep = 1; - pxstep = -1; - xch = 1; - } - _ => unreachable!(), - } - - // TODO: xch or swap_sides + }; - let mut mock_display = MockDisplay::new(); + let mut mock_display: MockDisplay = MockDisplay::new(); // mock_display.set_allow_out_of_bounds_drawing(true); - if dx > dy { - x_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; - x_varthick_line( - &mut mock_display, - x0, - y0, - dx, - dy, - xstep, - ystep, - pxstep, - pystep, - width, - )?; - } else { - y_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; - y_varthick_line( - &mut mock_display, - x0, - y0, - dx, - dy, - xstep, - ystep, - pxstep, - pystep, - width, - )?; - } + // if delta.x > delta.y { + x_varthick_line(display, x0, y0, delta, step, pstep, width)?; + // x_varthick_line( + // &mut mock_display, + // x0, + // y0, + // dx, + // dy, + // xstep, + // ystep, + // pxstep, + // pystep, + // width, + // )?; + // } else { + // y_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; + // // y_varthick_line( + // // &mut mock_display, + // // x0, + // // y0, + // // dx, + // // dy, + // // xstep, + // // ystep, + // // pxstep, + // // pystep, + // // width, + // // )?; + // } // thin_octant1(display, x0, y0, dx, dy)?; // thick_octant1(display, x0, y0, x1, y1, 10)?; From a9703b0b43f1042a27570c77976ab46ddc3a6b9c Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 11 Aug 2021 16:08:40 +0100 Subject: [PATCH 012/188] Fixed left/right side alignment --- debug-tools/examples/line.rs | 253 ++++------------------------------- 1 file changed, 23 insertions(+), 230 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index c02757b..18e14bc 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -19,7 +19,6 @@ enum LineSide { impl LineSide { fn widths(self, width: i32) -> (i32, i32) { match width { - // 0 => (0, 0), width => { match self { Self::Left => ((width * 2).saturating_sub(1), 0), @@ -30,7 +29,7 @@ impl LineSide { // the left. (width, width + (width % 2)) } - Self::Right => (0, (width * 2).saturating_sub(1)), + Self::Right => ((width * 2).saturating_sub(1), 0), } } } @@ -53,7 +52,6 @@ fn x_perpendicular( display: &mut impl DrawTarget, x0: i32, y0: i32, - delta: MajorMinor, mut step: MajorMinor, einit: i32, @@ -74,9 +72,7 @@ fn x_perpendicular( let dx = delta.major; let dy = delta.minor; - dbg!(step.major, step.minor); - - let sign = match (step.major, step.minor) { + let mut sign = match (step.major, step.minor) { (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, @@ -84,8 +80,10 @@ fn x_perpendicular( _ => 1, }; - step.major *= sign; - step.minor *= sign; + if sign == -1 { + step.major *= -1; + step.minor *= -1; + } let dx = dx.abs(); let dy = dy.abs(); @@ -96,16 +94,20 @@ fn x_perpendicular( let mut p = 0; let mut q = 0; - // let mut y = y0; - // let mut x = x0; - - // Swap signs in some conditions let mut error = einit; let mut tk = -winit; let side = LineSide::Center; - let (width_l, width_r) = side.widths(width); + let (mut width_l, mut width_r) = side.widths(width); + + if sign == -1 { + core::mem::swap(&mut width_l, &mut width_r); + } + + if side == LineSide::Right { + core::mem::swap(&mut width_l, &mut width_r); + } let width_l = width_l.pow(2) * (dx * dx + dy * dy); let width_r = width_r.pow(2) * (dx * dx + dy * dy); @@ -116,8 +118,6 @@ fn x_perpendicular( (Rgb565::CSS_CORNFLOWER_BLUE, Rgb565::YELLOW) }; - // dbg!(width_l, width_r); - while tk.pow(2) <= width_l && width_l > 0 { Pixel(point, c1).draw(display)?; @@ -133,24 +133,22 @@ fn x_perpendicular( q += 1; } - // let mut y2 = y0; - // let mut x2 = x0; let mut point = Point::new(x0, y0); - let mut error2 = -einit; + let mut error = -einit; let mut tk = winit; while tk.pow(2) <= width_r && width_r > 0 { - if p > 0 && side == LineSide::Center { + if p > 0 { Pixel(point, c2).draw(display)?; } - if error2 > threshold { + if error > threshold { point -= step.major; - error2 += e_minor; + error += e_minor; tk += 2 * dy; } - error2 += e_major; + error += e_major; point -= step.minor; tk += 2 * dx; p += 1; @@ -215,109 +213,6 @@ fn x_varthick_line( Ok(()) } -/*********************************************************************** - * * - * Y BASED LINES * - * * - ***********************************************************************/ - -fn y_perpendicular( - display: &mut impl DrawTarget, - x0: i32, - y0: i32, - dx: i32, - dy: i32, - xstep: i32, - ystep: i32, - einit: i32, - width: i32, - winit: i32, -) -> Result<(), std::convert::Infallible> { - let p = 0; - let mut q = 0; - let threshold = dy - 2 * dx; - let e_minor = -2 * dy; - let e_major = 2 * dx; - - let mut y = y0; - let mut x = x0; - let mut error = -einit; - let mut tk = dx + dy + winit; - - while tk <= width { - Pixel(Point::new(x, y), Rgb565::RED).draw(display)?; - - if error > threshold { - y += ystep; - error += e_minor; - tk += 2 * dx; - } - - error += e_major; - x += xstep; - tk += 2 * dy; - q += 1; - } - - let y = y0; - let x = x0; - let error = einit; - let tk = dx + dy - winit; - - Ok(()) -} - -fn y_varthick_line( - display: &mut impl DrawTarget, - x0: i32, - y0: i32, - dx: i32, - dy: i32, - xstep: i32, - ystep: i32, - pxstep: i32, - pystep: i32, - width: i32, -) -> Result<(), std::convert::Infallible> { - let mut p_error = 0; - let mut error = 0; - let mut y = y0; - let mut x = x0; - let mut threshold = dy - 2 * dx; - let mut e_minor = -2 * dy; - let mut e_major = 2 * dx; - let mut length = dy + 1; - - for p in 0..length { - y_perpendicular(display, x, y, dx, dy, pxstep, pystep, p_error, width, error)?; - - if error >= threshold { - x += xstep; - error += e_minor; - if p_error >= threshold { - y_perpendicular( - display, - x, - y, - dx, - dy, - pxstep, - pystep, - p_error + e_minor + e_major, - width, - error, - )?; - p_error += e_minor; - } - p_error += e_major; - } - error += e_major; - y += ystep; - } - - Ok(()) -} - struct LineDebug { start: Point, end: Point, @@ -326,8 +221,8 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - const DISPLAY_SIZE: Size = Size::new(256, 256); - // const DISPLAY_SIZE: Size = Size::new(64, 64); + // const DISPLAY_SIZE: Size = Size::new(256, 256); + const DISPLAY_SIZE: Size = Size::new(64, 64); fn new() -> Self { let end = Point::new( @@ -355,83 +250,11 @@ impl App for LineDebug { display: &mut SimulatorDisplay, ) -> Result<(), std::convert::Infallible> { let Point { x: x0, y: y0 } = self.start; - // let Point { x: x1, y: y1 } = self.end; - - // // let pxstep; - // // let pystep; - // // let mut xch = 0; // whether left and right get switched. - - // let mut dx = x1 - x0; - // let mut dy = y1 - y0; // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); let width = self.stroke_width as i32; - // let mut xstep = 1; - // let mut ystep = 1; - - // if dx < 0 { - // dx = -dx; - // xstep = -1; - // } - // if dy < 0 { - // dy = -dy; - // ystep = -1; - // } - - // if dx == 0 { - // xstep = 0; - // } - // if dy == 0 { - // ystep = 0; - // } - - // match (xstep, ystep) { - // (-1, -1) => { - // pystep = -1; - // pxstep = 1; - // xch = 1; - // } - // (-1, 0) => { - // pystep = -1; - // pxstep = 0; - // xch = 1; - // } - // (-1, 1) => { - // pystep = 1; - // pxstep = 1; - // } - // (0, -1) => { - // pystep = 0; - // pxstep = -1; - // } - // (0, 0) => { - // pystep = 0; - // pxstep = 0; - // } - // (0, 1) => { - // pystep = 0; - // pxstep = 1; - // } - // (1, -1) => { - // pystep = -1; - // pxstep = -1; - // } - // (1, 0) => { - // pystep = -1; - // pxstep = 0; - // } - // (1, 1) => { - // pystep = 1; - // pxstep = -1; - // xch = 1; - // } - // _ => unreachable!(), - // } - - // // TODO: xch or swap_sides - let (delta, step, pstep) = { let delta = self.end - self.start; @@ -470,38 +293,8 @@ impl App for LineDebug { let mut mock_display: MockDisplay = MockDisplay::new(); // mock_display.set_allow_out_of_bounds_drawing(true); - // if delta.x > delta.y { x_varthick_line(display, x0, y0, delta, step, pstep, width)?; - // x_varthick_line( - // &mut mock_display, - // x0, - // y0, - // dx, - // dy, - // xstep, - // ystep, - // pxstep, - // pystep, - // width, - // )?; - // } else { - // y_varthick_line(display, x0, y0, dx, dy, xstep, ystep, pxstep, pystep, width)?; - // // y_varthick_line( - // // &mut mock_display, - // // x0, - // // y0, - // // dx, - // // dy, - // // xstep, - // // ystep, - // // pxstep, - // // pystep, - // // width, - // // )?; - // } - - // thin_octant1(display, x0, y0, dx, dy)?; - // thick_octant1(display, x0, y0, x1, y1, 10)?; + x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; Ok(()) From 8ff0a77695b2d6cd07887443b6f108e0693af5eb Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 11 Aug 2021 16:24:55 +0100 Subject: [PATCH 013/188] Cleanup --- debug-tools/examples/line.rs | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 18e14bc..035b136 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -1,11 +1,4 @@ -use std::convert::TryFrom; - -use embedded_graphics::{ - mock_display::MockDisplay, - pixelcolor::Rgb565, - prelude::*, - primitives::{Line, PrimitiveStyle}, -}; +use embedded_graphics::{mock_display::MockDisplay, pixelcolor::Rgb565, prelude::*}; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; @@ -91,8 +84,6 @@ fn x_perpendicular( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; - let mut p = 0; - let mut q = 0; let mut error = einit; let mut tk = -winit; @@ -130,12 +121,12 @@ fn x_perpendicular( error += e_major; point += step.minor; tk += 2 * dx; - q += 1; } let mut point = Point::new(x0, y0); let mut error = -einit; let mut tk = winit; + let mut p = 0; while tk.pow(2) <= width_r && width_r > 0 { if p > 0 { @@ -168,9 +159,6 @@ fn x_varthick_line( ) -> Result<(), std::convert::Infallible> { let mut p_error = 0; let mut error = 0; - // let mut y = y0; - // let mut x = x0; - let mut point = Point::new(x0, y0); let dx = delta.major.abs(); @@ -181,14 +169,15 @@ fn x_varthick_line( let e_major = 2 * dy; let length = dx + 1; - for p in 0..length { + for _ in 0..length { x_perpendicular( display, point.x, point.y, delta, pstep, p_error, width, error, false, )?; - if error >= threshold { - // y += ystep; + + if error > threshold { point += step.minor; error += e_minor; + if p_error >= threshold { x_perpendicular( display, @@ -201,12 +190,14 @@ fn x_varthick_line( error, true, )?; + p_error += e_minor; } + p_error += e_major; } + error += e_major; - // x += xstep; point += step.major; } @@ -221,8 +212,8 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - // const DISPLAY_SIZE: Size = Size::new(256, 256); - const DISPLAY_SIZE: Size = Size::new(64, 64); + const DISPLAY_SIZE: Size = Size::new(256, 256); + // const DISPLAY_SIZE: Size = Size::new(64, 64); fn new() -> Self { let end = Point::new( @@ -294,7 +285,7 @@ impl App for LineDebug { // mock_display.set_allow_out_of_bounds_drawing(true); x_varthick_line(display, x0, y0, delta, step, pstep, width)?; - x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; + // x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; Ok(()) From 307789ddb2c9696585e86f2af91c1568dcf8ddef Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 11 Aug 2021 16:27:06 +0100 Subject: [PATCH 014/188] Fix single pixel wide jaggies --- debug-tools/examples/line.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 035b136..d56f497 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -179,17 +179,19 @@ fn x_varthick_line( error += e_minor; if p_error >= threshold { - x_perpendicular( - display, - point.x, - point.y, - delta, - pstep, - p_error + e_minor + e_major, - width, - error, - true, - )?; + if width > 1 { + x_perpendicular( + display, + point.x, + point.y, + delta, + pstep, + p_error + e_minor + e_major, + width, + error, + true, + )?; + } p_error += e_minor; } From 5be1aef3ef0bccc68a484e2d2c49b05f0c15f1fc Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 12 Aug 2021 10:20:50 +0100 Subject: [PATCH 015/188] Rename demo --- debug-tools/examples/line-perp.rs | 308 ++++++++++++++++++++++++++++++ debug-tools/examples/line.rs | 283 ++------------------------- 2 files changed, 322 insertions(+), 269 deletions(-) create mode 100644 debug-tools/examples/line-perp.rs diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs new file mode 100644 index 0000000..d56f497 --- /dev/null +++ b/debug-tools/examples/line-perp.rs @@ -0,0 +1,308 @@ +use embedded_graphics::{mock_display::MockDisplay, pixelcolor::Rgb565, prelude::*}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; + +#[derive(Debug, Copy, Clone, PartialEq)] +enum LineSide { + Left, + Center, + Right, +} + +impl LineSide { + fn widths(self, width: i32) -> (i32, i32) { + match width { + width => { + match self { + Self::Left => ((width * 2).saturating_sub(1), 0), + Self::Center => { + let width = width.saturating_sub(1); + + // Right-side bias for odd width lines. Move mod2 to first item to bias to + // the left. + (width, width + (width % 2)) + } + Self::Right => ((width * 2).saturating_sub(1), 0), + } + } + } + } +} + +#[derive(Debug, Clone, Copy)] +struct MajorMinor { + major: T, + minor: T, +} + +impl MajorMinor { + fn new(major: T, minor: T) -> Self { + Self { major, minor } + } +} + +fn x_perpendicular( + display: &mut impl DrawTarget, + x0: i32, + y0: i32, + delta: MajorMinor, + mut step: MajorMinor, + einit: i32, + width: i32, + winit: i32, + extra: bool, +) -> Result<(), std::convert::Infallible> { + let mut point = Point::new(x0, y0); + + if width == 0 { + return Ok(()); + } + + if width == 1 { + return Pixel(point, Rgb565::YELLOW).draw(display); + } + + let dx = delta.major; + let dy = delta.minor; + + let mut sign = match (step.major, step.minor) { + (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, + (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, + (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, + (Point { x: 0, y: 1 }, Point { x: 1, y: 0 }) => -1, + _ => 1, + }; + + if sign == -1 { + step.major *= -1; + step.minor *= -1; + } + + let dx = dx.abs(); + let dy = dy.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + + let mut error = einit; + let mut tk = -winit; + + let side = LineSide::Center; + + let (mut width_l, mut width_r) = side.widths(width); + + if sign == -1 { + core::mem::swap(&mut width_l, &mut width_r); + } + + if side == LineSide::Right { + core::mem::swap(&mut width_l, &mut width_r); + } + + let width_l = width_l.pow(2) * (dx * dx + dy * dy); + let width_r = width_r.pow(2) * (dx * dx + dy * dy); + + let (c1, c2) = if extra { + (Rgb565::RED, Rgb565::GREEN) + } else { + (Rgb565::CSS_CORNFLOWER_BLUE, Rgb565::YELLOW) + }; + + while tk.pow(2) <= width_l && width_l > 0 { + Pixel(point, c1).draw(display)?; + + if error >= threshold { + point += step.major; + error += e_minor; + tk += 2 * dy; + } + + error += e_major; + point += step.minor; + tk += 2 * dx; + } + + let mut point = Point::new(x0, y0); + let mut error = -einit; + let mut tk = winit; + let mut p = 0; + + while tk.pow(2) <= width_r && width_r > 0 { + if p > 0 { + Pixel(point, c2).draw(display)?; + } + + if error > threshold { + point -= step.major; + error += e_minor; + tk += 2 * dy; + } + + error += e_major; + point -= step.minor; + tk += 2 * dx; + p += 1; + } + + Ok(()) +} + +fn x_varthick_line( + display: &mut impl DrawTarget, + x0: i32, + y0: i32, + delta: MajorMinor, + step: MajorMinor, + pstep: MajorMinor, + width: i32, +) -> Result<(), std::convert::Infallible> { + let mut p_error = 0; + let mut error = 0; + let mut point = Point::new(x0, y0); + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx + 1; + + for _ in 0..length { + x_perpendicular( + display, point.x, point.y, delta, pstep, p_error, width, error, false, + )?; + + if error > threshold { + point += step.minor; + error += e_minor; + + if p_error >= threshold { + if width > 1 { + x_perpendicular( + display, + point.x, + point.y, + delta, + pstep, + p_error + e_minor + e_major, + width, + error, + true, + )?; + } + + p_error += e_minor; + } + + p_error += e_major; + } + + error += e_major; + point += step.major; + } + + Ok(()) +} + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, +} + +impl App for LineDebug { + type Color = Rgb565; + const DISPLAY_SIZE: Size = Size::new(256, 256); + // const DISPLAY_SIZE: Size = Size::new(64, 64); + + fn new() -> Self { + let end = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); + Self { + start: end + Point::new(10, 15), + end, + // end: start + Point::new(100, 0), + stroke_width: 10, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let Point { x: x0, y: y0 } = self.start; + + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; + + let (delta, step, pstep) = { + let delta = self.end - self.start; + + let direction = Point::new( + if delta.x >= 0 { 1 } else { -1 }, + if delta.y >= 0 { 1 } else { -1 }, + ); + + let perp_direction = { + let perp_delta = Point::new(delta.y, -delta.x); + + Point::new( + if perp_delta.x >= 0 { 1 } else { -1 }, + if perp_delta.y >= 0 { 1 } else { -1 }, + ) + }; + + // let delta = delta.abs(); + + // Determine major and minor directions. + if delta.y.abs() >= delta.x.abs() { + ( + MajorMinor::new(delta.y, delta.x), + MajorMinor::new(direction.y_axis(), direction.x_axis()), + MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), + ) + } else { + ( + MajorMinor::new(delta.x, delta.y), + MajorMinor::new(direction.x_axis(), direction.y_axis()), + MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), + ) + } + }; + + let mut mock_display: MockDisplay = MockDisplay::new(); + // mock_display.set_allow_out_of_bounds_drawing(true); + + x_varthick_line(display, x0, y0, delta, step, pstep, width)?; + // x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; + + Ok(()) + + // Line::new(self.start, self.end) + // .into_styled(PrimitiveStyle::with_stroke( + // Rgb565::GREEN, + // self.stroke_width, + // )) + // .draw(display) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(3).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index d56f497..54a14cb 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -1,211 +1,11 @@ -use embedded_graphics::{mock_display::MockDisplay, pixelcolor::Rgb565, prelude::*}; +use embedded_graphics::{ + pixelcolor::Rgb565, + prelude::*, + primitives::{Line, PrimitiveStyle}, +}; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; -#[derive(Debug, Copy, Clone, PartialEq)] -enum LineSide { - Left, - Center, - Right, -} - -impl LineSide { - fn widths(self, width: i32) -> (i32, i32) { - match width { - width => { - match self { - Self::Left => ((width * 2).saturating_sub(1), 0), - Self::Center => { - let width = width.saturating_sub(1); - - // Right-side bias for odd width lines. Move mod2 to first item to bias to - // the left. - (width, width + (width % 2)) - } - Self::Right => ((width * 2).saturating_sub(1), 0), - } - } - } - } -} - -#[derive(Debug, Clone, Copy)] -struct MajorMinor { - major: T, - minor: T, -} - -impl MajorMinor { - fn new(major: T, minor: T) -> Self { - Self { major, minor } - } -} - -fn x_perpendicular( - display: &mut impl DrawTarget, - x0: i32, - y0: i32, - delta: MajorMinor, - mut step: MajorMinor, - einit: i32, - width: i32, - winit: i32, - extra: bool, -) -> Result<(), std::convert::Infallible> { - let mut point = Point::new(x0, y0); - - if width == 0 { - return Ok(()); - } - - if width == 1 { - return Pixel(point, Rgb565::YELLOW).draw(display); - } - - let dx = delta.major; - let dy = delta.minor; - - let mut sign = match (step.major, step.minor) { - (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, - (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, - (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, - (Point { x: 0, y: 1 }, Point { x: 1, y: 0 }) => -1, - _ => 1, - }; - - if sign == -1 { - step.major *= -1; - step.minor *= -1; - } - - let dx = dx.abs(); - let dy = dy.abs(); - - let threshold = dx - 2 * dy; - let e_minor = -2 * dx; - let e_major = 2 * dy; - - let mut error = einit; - let mut tk = -winit; - - let side = LineSide::Center; - - let (mut width_l, mut width_r) = side.widths(width); - - if sign == -1 { - core::mem::swap(&mut width_l, &mut width_r); - } - - if side == LineSide::Right { - core::mem::swap(&mut width_l, &mut width_r); - } - - let width_l = width_l.pow(2) * (dx * dx + dy * dy); - let width_r = width_r.pow(2) * (dx * dx + dy * dy); - - let (c1, c2) = if extra { - (Rgb565::RED, Rgb565::GREEN) - } else { - (Rgb565::CSS_CORNFLOWER_BLUE, Rgb565::YELLOW) - }; - - while tk.pow(2) <= width_l && width_l > 0 { - Pixel(point, c1).draw(display)?; - - if error >= threshold { - point += step.major; - error += e_minor; - tk += 2 * dy; - } - - error += e_major; - point += step.minor; - tk += 2 * dx; - } - - let mut point = Point::new(x0, y0); - let mut error = -einit; - let mut tk = winit; - let mut p = 0; - - while tk.pow(2) <= width_r && width_r > 0 { - if p > 0 { - Pixel(point, c2).draw(display)?; - } - - if error > threshold { - point -= step.major; - error += e_minor; - tk += 2 * dy; - } - - error += e_major; - point -= step.minor; - tk += 2 * dx; - p += 1; - } - - Ok(()) -} - -fn x_varthick_line( - display: &mut impl DrawTarget, - x0: i32, - y0: i32, - delta: MajorMinor, - step: MajorMinor, - pstep: MajorMinor, - width: i32, -) -> Result<(), std::convert::Infallible> { - let mut p_error = 0; - let mut error = 0; - let mut point = Point::new(x0, y0); - - let dx = delta.major.abs(); - let dy = delta.minor.abs(); - - let threshold = dx - 2 * dy; - let e_minor = -2 * dx; - let e_major = 2 * dy; - let length = dx + 1; - - for _ in 0..length { - x_perpendicular( - display, point.x, point.y, delta, pstep, p_error, width, error, false, - )?; - - if error > threshold { - point += step.minor; - error += e_minor; - - if p_error >= threshold { - if width > 1 { - x_perpendicular( - display, - point.x, - point.y, - delta, - pstep, - p_error + e_minor + e_major, - width, - error, - true, - )?; - } - - p_error += e_minor; - } - - p_error += e_major; - } - - error += e_major; - point += step.major; - } - - Ok(()) -} - struct LineDebug { start: Point, end: Point, @@ -215,18 +15,12 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; const DISPLAY_SIZE: Size = Size::new(256, 256); - // const DISPLAY_SIZE: Size = Size::new(64, 64); fn new() -> Self { - let end = Point::new( - Self::DISPLAY_SIZE.width as i32 / 2, - Self::DISPLAY_SIZE.height as i32 / 2, - ); Self { - start: end + Point::new(10, 15), - end, - // end: start + Point::new(100, 0), - stroke_width: 10, + start: Point::new(128, 128), + end: Point::new(150, 170), + stroke_width: 1, } } @@ -242,61 +36,12 @@ impl App for LineDebug { &self, display: &mut SimulatorDisplay, ) -> Result<(), std::convert::Infallible> { - let Point { x: x0, y: y0 } = self.start; - - // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; - // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); - let width = self.stroke_width as i32; - - let (delta, step, pstep) = { - let delta = self.end - self.start; - - let direction = Point::new( - if delta.x >= 0 { 1 } else { -1 }, - if delta.y >= 0 { 1 } else { -1 }, - ); - - let perp_direction = { - let perp_delta = Point::new(delta.y, -delta.x); - - Point::new( - if perp_delta.x >= 0 { 1 } else { -1 }, - if perp_delta.y >= 0 { 1 } else { -1 }, - ) - }; - - // let delta = delta.abs(); - - // Determine major and minor directions. - if delta.y.abs() >= delta.x.abs() { - ( - MajorMinor::new(delta.y, delta.x), - MajorMinor::new(direction.y_axis(), direction.x_axis()), - MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), - ) - } else { - ( - MajorMinor::new(delta.x, delta.y), - MajorMinor::new(direction.x_axis(), direction.y_axis()), - MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), - ) - } - }; - - let mut mock_display: MockDisplay = MockDisplay::new(); - // mock_display.set_allow_out_of_bounds_drawing(true); - - x_varthick_line(display, x0, y0, delta, step, pstep, width)?; - // x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; - - Ok(()) - - // Line::new(self.start, self.end) - // .into_styled(PrimitiveStyle::with_stroke( - // Rgb565::GREEN, - // self.stroke_width, - // )) - // .draw(display) + Line::new(self.start, self.end) + .into_styled(PrimitiveStyle::with_stroke( + Rgb565::GREEN, + self.stroke_width, + )) + .draw(display) } } From cba9e393ccda0a6911bb532e44595eb4195d8dc6 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 12 Aug 2021 10:33:19 +0100 Subject: [PATCH 016/188] Add circle distance function demo --- debug-tools/examples/circle-distance.rs | 125 ++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 debug-tools/examples/circle-distance.rs diff --git a/debug-tools/examples/circle-distance.rs b/debug-tools/examples/circle-distance.rs new file mode 100644 index 0000000..04e7296 --- /dev/null +++ b/debug-tools/examples/circle-distance.rs @@ -0,0 +1,125 @@ +use embedded_graphics::{ + pixelcolor::{Gray4, Gray8, Rgb888}, + prelude::*, + primitives::{Circle, PrimitiveStyleBuilder}, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::{draw, prelude::*}; + +struct CircleDebug { + center: Point, + diameter: u32, + stroke_width: u32, + show_bounding_box: bool, +} + +impl App for CircleDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(128, 128); + + fn new() -> Self { + Self { + center: Point::new(64, 80), + diameter: 70, + stroke_width: 1, + show_bounding_box: false, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("center", &mut self.center), + Parameter::new("diameter", &mut self.diameter), + Parameter::new("stroke", &mut self.stroke_width), + Parameter::new("show BB", &mut self.show_bounding_box), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let circle = Circle::with_center(self.center, self.diameter); + + let bb = circle.bounding_box(); + let center = bb.center(); + + let max_distance = bb.size.width as f32 * 2.0f32.sqrt(); + + for point in bb.points() { + let distance_to_center = + f32::sqrt(((point.x - center.x).pow(2) + (point.y - center.y).pow(2)) as f32); + + let distance_to_edge = distance_to_center - circle.diameter as f32 / 2.0; + + let norm = distance_to_center / max_distance; + + let scaled = (norm * 255.0) as u8; + + // Distance field + { + let grey = Gray8::new(scaled); + + let color = Rgb888::from(grey); + + Pixel(point, color).draw(display)?; + } + + // // Non antialiased circle + // { + // let color = if distance_to_edge < 0.0 { + // Rgb888::RED + // } else { + // Rgb888::GREEN + // }; + + // Pixel(point, color).draw(display)?; + // } + + // Antialiased circle + { + let color = (0.5 - distance_to_edge).clamp(0.0, 1.0); + + let scaled = (color * 255.0) as u8; + + // Kludge for "transparent" pixels + if scaled == 0 { + continue; + } + + let color = Rgb888::new(scaled, 0, 0); + + Pixel(point, color).draw(display)?; + } + } + + // let style = PrimitiveStyleBuilder::new() + // .stroke_color(Rgb888::CSS_SPRING_GREEN) + // .stroke_width(self.stroke_width) + // .fill_color(Rgb888::CSS_DARK_SEA_GREEN) + // .build(); + // let styled_circle = circle.into_styled(style); + + // if self.show_bounding_box { + // draw::bounding_box(&styled_circle, display); + // } + + // styled_circle.draw(display)?; + + // if self.show_bounding_box { + // draw::point(self.center, Rgb888::CSS_LIGHT_SKY_BLUE, display); + // } + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new() + .scale(3) + .pixel_spacing(1) + .build(); + let window = Window::new("Circle debugger", &settings); + + CircleDebug::run(window); +} From 1fded40419bec6503c0903b358b35427872e3ba1 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 12 Aug 2021 11:43:06 +0100 Subject: [PATCH 017/188] Line distance field --- Cargo.toml | 3 + debug-tools/examples/line-distance.rs | 110 ++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 debug-tools/examples/line-distance.rs diff --git a/Cargo.toml b/Cargo.toml index 14ff055..e8d0579 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,3 +3,6 @@ members = [ "framework", "debug-tools", ] + +[patch.crates-io] +embedded-graphics = { path = "../embedded-graphics" } diff --git a/debug-tools/examples/line-distance.rs b/debug-tools/examples/line-distance.rs new file mode 100644 index 0000000..1a8d1a3 --- /dev/null +++ b/debug-tools/examples/line-distance.rs @@ -0,0 +1,110 @@ +use embedded_graphics::{ + pixelcolor::{Gray8, Rgb888}, + prelude::*, + primitives::{line::StrokeOffset, Line, PrimitiveStyle}, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, +} + +impl App for LineDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(256, 256); + + fn new() -> Self { + Self { + start: Point::new(128, 128), + end: Point::new(150, 170), + stroke_width: 15, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let skeleton = Line::new(self.start, self.end); + let (l, r) = skeleton.extents(self.stroke_width, StrokeOffset::None); + + // Structure + { + skeleton + .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + .draw(display)?; + l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) + .draw(display)?; + r.into_styled(PrimitiveStyle::with_stroke(Rgb888::YELLOW, 1)) + .draw(display)?; + } + + let length = { + let Point { x, y } = skeleton.delta(); + + f32::sqrt((x.pow(2) + y.pow(2)) as f32) + }; + + let bb = skeleton + .into_styled(PrimitiveStyle::with_stroke( + Rgb888::BLACK, + self.stroke_width, + )) + .bounding_box(); + + let max_distance = bb.size.width.max(bb.size.height) as f32 * 2.0f32.sqrt(); + + // for point in l.points() { + for point in bb.points() { + // http://paulbourke.net/geometry/pointlineplane + let distance = { + let x1 = skeleton.start.x; + let x2 = skeleton.end.x; + let x3 = point.x; + + let y1 = skeleton.start.y; + let y2 = skeleton.end.y; + let y3 = point.y; + + let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / length.powi(2); + + let tx = x1 as f32 + u * (x2 - x1) as f32; + let ty = y1 as f32 + u * (y2 - y1) as f32; + + // Tangent intersection point + let tangent = Point::new(tx as i32, ty as i32); + + let Point { x, y } = Line::new(point, tangent).delta(); + + f32::sqrt((x.pow(2) + y.pow(2)) as f32) + }; + + let norm = distance / max_distance; + + let gray = Gray8::new(unsafe { (norm * 255.0).to_int_unchecked() }); + let color = Rgb888::from(gray); + + Pixel(point, color).draw(display)?; + } + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(3).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} From 6478123d69de4c530f51543056cae3b985f6f531 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 12 Aug 2021 17:25:33 +0100 Subject: [PATCH 018/188] Antialiasing but with some jaggies left --- debug-tools/examples/line-perp.rs | 57 ++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index d56f497..e88f442 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -1,4 +1,9 @@ -use embedded_graphics::{mock_display::MockDisplay, pixelcolor::Rgb565, prelude::*}; +use embedded_graphics::{ + mock_display::MockDisplay, + pixelcolor::Rgb565, + prelude::*, + primitives::{Line, PrimitiveStyle}, +}; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; @@ -109,6 +114,8 @@ fn x_perpendicular( (Rgb565::CSS_CORNFLOWER_BLUE, Rgb565::YELLOW) }; + // let (c1, c2) = (Rgb565::GREEN, Rgb565::GREEN); + while tk.pow(2) <= width_l && width_l > 0 { Pixel(point, c1).draw(display)?; @@ -121,6 +128,25 @@ fn x_perpendicular( error += e_major; point += step.minor; tk += 2 * dx; + + // Antialiasing + { + let thing = tk.pow(2) as f32 / width_l as f32; + + if thing >= 1.0 { + let fract = 1.0 - thing.fract(); + + Pixel( + point, + Rgb565::new( + (c1.r() as f32 * fract) as u8, + (c1.g() as f32 * fract) as u8, + (c1.b() as f32 * fract) as u8, + ), + ) + .draw(display)?; + } + } } let mut point = Point::new(x0, y0); @@ -143,6 +169,25 @@ fn x_perpendicular( point -= step.minor; tk += 2 * dx; p += 1; + + // Antialiasing + { + let thing = tk.pow(2) as f32 / width_r as f32; + + if thing > 1.0 { + let fract = 1.0 - thing.fract(); + + Pixel( + point, + Rgb565::new( + (c2.r() as f32 * fract) as u8, + (c2.g() as f32 * fract) as u8, + (c2.b() as f32 * fract) as u8, + ), + ) + .draw(display)?; + } + } } Ok(()) @@ -174,6 +219,8 @@ fn x_varthick_line( display, point.x, point.y, delta, pstep, p_error, width, error, false, )?; + Pixel(point, Rgb565::BLACK).draw(display)?; + if error > threshold { point += step.minor; error += e_minor; @@ -191,6 +238,8 @@ fn x_varthick_line( error, true, )?; + + Pixel(point, Rgb565::BLACK).draw(display)?; } p_error += e_minor; @@ -289,14 +338,14 @@ impl App for LineDebug { x_varthick_line(display, x0, y0, delta, step, pstep, width)?; // x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; - Ok(()) - // Line::new(self.start, self.end) // .into_styled(PrimitiveStyle::with_stroke( // Rgb565::GREEN, // self.stroke_width, // )) - // .draw(display) + // .draw(display)?; + + Ok(()) } } From 86ae6e1f1562a9ed6c857b9cdfe10127bdd94ce1 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 12 Aug 2021 17:25:47 +0100 Subject: [PATCH 019/188] More crap --- debug-tools/examples/line-distance.rs | 161 ++++++++++++---- debug-tools/examples/line.rs | 259 +++++++++++++++++++++++++- 2 files changed, 373 insertions(+), 47 deletions(-) diff --git a/debug-tools/examples/line-distance.rs b/debug-tools/examples/line-distance.rs index 1a8d1a3..fae00e6 100644 --- a/debug-tools/examples/line-distance.rs +++ b/debug-tools/examples/line-distance.rs @@ -1,7 +1,10 @@ use embedded_graphics::{ pixelcolor::{Gray8, Rgb888}, prelude::*, - primitives::{line::StrokeOffset, Line, PrimitiveStyle}, + primitives::{ + line::{Scanline, StrokeOffset}, + Line, PrimitiveStyle, + }, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; @@ -12,6 +15,34 @@ struct LineDebug { stroke_width: u32, } +fn distance(line: Line, point: Point) -> f32 { + let length = { + let Point { x, y } = line.delta(); + + f32::sqrt((x.pow(2) + y.pow(2)) as f32) + }; + + let x1 = line.start.x; + let x2 = line.end.x; + let x3 = point.x; + + let y1 = line.start.y; + let y2 = line.end.y; + let y3 = point.y; + + let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / length.powi(2); + + let tx = x1 as f32 + u * (x2 - x1) as f32; + let ty = y1 as f32 + u * (y2 - y1) as f32; + + // Tangent intersection point + let tangent = Point::new(tx as i32, ty as i32); + + let Point { x, y } = Line::new(point, tangent).delta(); + + f32::sqrt((x.pow(2) + y.pow(2)) as f32) +} + impl App for LineDebug { type Color = Rgb888; const DISPLAY_SIZE: Size = Size::new(256, 256); @@ -39,65 +70,115 @@ impl App for LineDebug { let skeleton = Line::new(self.start, self.end); let (l, r) = skeleton.extents(self.stroke_width, StrokeOffset::None); - // Structure - { - skeleton - .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) - .draw(display)?; - l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) - .draw(display)?; - r.into_styled(PrimitiveStyle::with_stroke(Rgb888::YELLOW, 1)) - .draw(display)?; - } - - let length = { - let Point { x, y } = skeleton.delta(); - - f32::sqrt((x.pow(2) + y.pow(2)) as f32) - }; - let bb = skeleton .into_styled(PrimitiveStyle::with_stroke( Rgb888::BLACK, self.stroke_width, )) .bounding_box(); + let br = bb.bottom_right().unwrap(); let max_distance = bb.size.width.max(bb.size.height) as f32 * 2.0f32.sqrt(); - // for point in l.points() { - for point in bb.points() { - // http://paulbourke.net/geometry/pointlineplane - let distance = { - let x1 = skeleton.start.x; - let x2 = skeleton.end.x; - let x3 = point.x; + // 4 lines that construct the perimiter of the thick line + let perimiter = [l, Line::new(l.end, r.end), r, Line::new(r.start, l.start)]; + + // Draw perimiter + for line in &perimiter { + line.into_styled(PrimitiveStyle::with_stroke(Rgb888::MAGENTA, 1)) + .draw(display)?; + } + + for y in bb.top_left.y..=br.y { + // --- + + // // Scanline (integer) intersection + // { + // let mut min_x = i32::MAX; + // let mut max_x = i32::MIN; + + // for line in &perimiter { + // let mut scanline = Scanline::new_empty(y); + // scanline.bresenham_intersection(&line); - let y1 = skeleton.start.y; - let y2 = skeleton.end.y; - let y3 = point.y; + // if scanline.is_empty() { + // continue; + // } - let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / length.powi(2); + // min_x = min_x.min(scanline.x.start); + // max_x = max_x.max(scanline.x.end); + // } - let tx = x1 as f32 + u * (x2 - x1) as f32; - let ty = y1 as f32 + u * (y2 - y1) as f32; + // let scanline = Scanline::new(y, min_x..max_x); + // scanline.draw(display, Rgb888::YELLOW)?; + // } - // Tangent intersection point - let tangent = Point::new(tx as i32, ty as i32); + // --- - let Point { x, y } = Line::new(point, tangent).delta(); + // // Distance field + // { + // let scanline = Line::new(Point::new(bb.top_left.x, y), Point::new(br.x, y)); - f32::sqrt((x.pow(2) + y.pow(2)) as f32) - }; + // for point in scanline.points() { + // let distance = distance(skeleton, point); - let norm = distance / max_distance; + // let norm = distance / max_distance; - let gray = Gray8::new(unsafe { (norm * 255.0).to_int_unchecked() }); - let color = Rgb888::from(gray); + // let color = Gray8::new(unsafe { (norm * 255.0).to_int_unchecked() }); - Pixel(point, color).draw(display)?; + // let color = Rgb888::from(color); + + // Pixel(point, color).draw(display)?; + // } + // } } + // Crappy distance function + // for point in bb.points() { + // // http://paulbourke.net/geometry/pointlineplane + // // Distance between skeleton and current point + // let distance = { + // let x1 = skeleton.start.x; + // let x2 = skeleton.end.x; + // let x3 = point.x; + + // let y1 = skeleton.start.y; + // let y2 = skeleton.end.y; + // let y3 = point.y; + + // let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / length.powi(2); + + // let tx = x1 as f32 + u * (x2 - x1) as f32; + // let ty = y1 as f32 + u * (y2 - y1) as f32; + + // // Tangent intersection point + // let tangent = Point::new(tx as i32, ty as i32); + + // let Point { x, y } = Line::new(point, tangent).delta(); + + // f32::sqrt((x.pow(2) + y.pow(2)) as f32) + // }; + + // let distance_to_edge = distance - self.stroke_width as f32; + + // let color = (0.5 - distance_to_edge).clamp(0.0, 1.0); + + // let color = Rgb888::from(Gray8::new(unsafe { (color * 255.0).to_int_unchecked() })); + + // Pixel(point, color).draw(display)?; + // } + + // // Structure + // { + // skeleton + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(display)?; + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) + // .draw(display)?; + // r.into_styled(PrimitiveStyle::with_stroke(Rgb888::YELLOW, 1)) + // .draw(display)?; + // } + Ok(()) } } diff --git a/debug-tools/examples/line.rs b/debug-tools/examples/line.rs index 54a14cb..2012bbc 100644 --- a/debug-tools/examples/line.rs +++ b/debug-tools/examples/line.rs @@ -1,5 +1,5 @@ use embedded_graphics::{ - pixelcolor::Rgb565, + pixelcolor::{Gray8, Rgb565}, prelude::*, primitives::{Line, PrimitiveStyle}, }; @@ -12,6 +12,235 @@ struct LineDebug { stroke_width: u32, } +fn thin_octant1( + display: &mut SimulatorDisplay, + x0: i32, + y0: i32, + dx: i32, + dy: i32, +) -> Result<(), std::convert::Infallible> { + let mut error: i32 = 0; + let mut y = y0; + let mut x = x0; + let threshold = dx - 2 * dy; + let e_major = 2 * dx; + let e_minor = 2 * dy; + let length = dx; + + for _ in 1..length { + // Pixel(Point::new(x, y - 3), Rgb565::new(ass as u8, 0, 0)).draw(display)?; + Pixel(Point::new(x, y), Rgb565::GREEN).draw(display)?; + + if error > threshold { + y += 1; + error -= e_major; + } + + error += e_minor; + x += 1; + } + + Ok(()) +} + +fn plot( + display: &mut SimulatorDisplay, + x: f32, + y: f32, + c: f32, +) -> Result<(), std::convert::Infallible> { + let c = Rgb565::new(0, (c * 255.0) as u8, 0); + + Pixel(Point::new(x as i32, y as i32), c).draw(display) +} + +// integer part of x +fn ipart(x: f32) -> f32 { + f32::floor(x) +} + +fn round(x: f32) -> f32 { + f32::trunc(x + 0.5) +} + +// fractional part of x +fn fpart(x: f32) -> f32 { + x - f32::floor(x) +} + +fn rfpart(x: f32) -> f32 { + 1.0 - f32::fract(x) +} + +fn wu( + display: &mut SimulatorDisplay, + x0: i32, + y0: i32, + x1: i32, + y1: i32, +) -> Result<(), std::convert::Infallible> { + let mut x0 = x0 as f32; + let mut y0 = y0 as f32; + let mut x1 = x1 as f32; + let mut y1 = y1 as f32; + + let mut steep = f32::abs(y1 - y0) > f32::abs(x1 - x0); + + if steep { + core::mem::swap(&mut x0, &mut y0); + core::mem::swap(&mut x1, &mut y1); + } + if x0 > x1 { + core::mem::swap(&mut x0, &mut x1); + core::mem::swap(&mut y0, &mut y1); + } + + let mut dx = x1 - x0; + let mut dy = y1 - y0; + let mut gradient = dy / dx; + if dx == 0.0 { + let mut gradient = 1.0; + } + + // handle first endpoint + let mut xend = round(x0); + let mut yend = y0 + gradient * (xend - x0); + let mut xgap = rfpart(x0 + 0.5); + let mut xpxl1 = xend; // this will be used in the main loop + let mut ypxl1 = ipart(yend); + if steep { + plot(display, ypxl1, xpxl1, rfpart(yend) * xgap)?; + plot(display, ypxl1 + 1.0, xpxl1, fpart(yend) * xgap)?; + } else { + plot(display, xpxl1, ypxl1, rfpart(yend) * xgap)?; + plot(display, xpxl1, ypxl1 + 1.0, fpart(yend) * xgap)?; + } + let mut intery = yend + gradient; // first y-intersection for the main loop + + // handle second endpoint + let mut xend = round(x1); + let mut yend = y1 + gradient * (xend - x1); + let mut xgap = fpart(x1 + 0.5); + let mut xpxl2 = xend; //this will be used in the main loop + let mut ypxl2 = ipart(yend); + if steep { + plot(display, ypxl2, xpxl2, rfpart(yend) * xgap)?; + plot(display, ypxl2 + 1.0, xpxl2, fpart(yend) * xgap)?; + } else { + plot(display, xpxl2, ypxl2, rfpart(yend) * xgap)?; + plot(display, xpxl2, ypxl2 + 1.0, fpart(yend) * xgap)?; + } + + // main loop + if steep { + for x in (xpxl1 as i32 + 1)..(xpxl2 as i32 - 1) { + plot(display, ipart(intery), x as f32, rfpart(intery))?; + plot(display, ipart(intery) + 1.0, x as f32, fpart(intery))?; + intery = intery + gradient; + } + } else { + for x in (xpxl1 as i32 + 1)..(xpxl2 as i32 - 1) { + plot(display, x as f32, ipart(intery), rfpart(intery))?; + plot(display, x as f32, ipart(intery) + 1.0, fpart(intery))?; + intery = intery + gradient; + } + } + + Ok(()) +} + +// //returns 1 - fractional part of number +// fn rfPartOfNumber(x: f32) -> f32 { +// return 1.0 - f32::fract(x); +// } + +// fn drawPixel( +// display: &mut SimulatorDisplay, +// x: f32, +// y: f32, +// color: f32, +// ) -> Result<(), std::convert::Infallible> { +// Pixel( +// Point::new(x as i32, y as i32), +// Rgb565::new(0, (color * 255.0) as u8, 0), +// ) +// .draw(display) +// } + +// fn drawAALine( +// display: &mut SimulatorDisplay, +// mut x0: i32, +// mut y0: i32, +// mut x1: i32, +// mut y1: i32, +// ) -> Result<(), std::convert::Infallible> { +// let steep = (y1 - y0).abs() > (x1 - x0).abs(); + +// // swap the co-ordinates if slope > 1 or we +// // draw backwards +// if steep { +// core::mem::swap(&mut x0, &mut y0); +// core::mem::swap(&mut x1, &mut y1); +// } +// if x0 > x1 { +// core::mem::swap(&mut x0, &mut x1); +// core::mem::swap(&mut y0, &mut y1); +// } + +// //compute the slope +// let dx = x1 - x0; +// let dy = y1 - y0; +// let mut gradient: f32 = dy as f32 / dx as f32; +// if dx == 0 { +// gradient = 1.0; +// } + +// let xpxl1 = x0; +// let xpxl2 = x1; +// let mut intersectY = y0 as f32; + +// // main loop +// if steep { +// for x in xpxl1..=xpxl2 { +// // pixel coverage is determined by fractional +// // part of y co-ordinate +// drawPixel( +// display, +// f32::trunc(intersectY), +// x as f32, +// rfPartOfNumber(intersectY), +// )?; +// drawPixel( +// display, +// f32::trunc(intersectY) - 1.0, +// x as f32, +// f32::fract(intersectY), +// )?; +// intersectY += gradient; +// } +// } else { +// for x in xpxl1..=xpxl2 { +// // pixel coverage is determined by fractional +// // part of y co-ordinate +// drawPixel( +// display, +// x as f32, +// f32::trunc(intersectY), +// rfPartOfNumber(intersectY), +// )?; +// drawPixel( +// display, +// x as f32, +// f32::trunc(intersectY) - 1.0, +// f32::fract(intersectY), +// )?; +// intersectY += gradient; +// } +// } + +// Ok(()) +// } + impl App for LineDebug { type Color = Rgb565; const DISPLAY_SIZE: Size = Size::new(256, 256); @@ -36,12 +265,28 @@ impl App for LineDebug { &self, display: &mut SimulatorDisplay, ) -> Result<(), std::convert::Infallible> { - Line::new(self.start, self.end) - .into_styled(PrimitiveStyle::with_stroke( - Rgb565::GREEN, - self.stroke_width, - )) - .draw(display) + let line = Line::new(self.start, self.end); + + let delta = line.delta(); + + // thin_octant1(display, line.start.x, line.start.y, delta.x, delta.y)?; + wu(display, line.start.x, line.start.y, delta.x, delta.y)?; + + // { + // let Line { + // start: Point { x: x0, y: y0 }, + // end: Point { x: x1, y: y1 }, + // } = line; + + // drawAALine(display, x0, y0, x1, y1)?; + // } + + // Line::new(self.start, self.end) + // .points() + // .map(|point| Pixel(point, Rgb565::GREEN)) + // .draw(display) + + Ok(()) } } From 2b0542e63dfab312541fa581d9f3708a9a13ece9 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 18 Aug 2021 20:53:38 +0100 Subject: [PATCH 020/188] Smooth fade from center --- debug-tools/examples/line-perp.rs | 74 ++++++++++++++++++------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index e88f442..305e858 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -114,10 +114,22 @@ fn x_perpendicular( (Rgb565::CSS_CORNFLOWER_BLUE, Rgb565::YELLOW) }; - // let (c1, c2) = (Rgb565::GREEN, Rgb565::GREEN); - - while tk.pow(2) <= width_l && width_l > 0 { - Pixel(point, c1).draw(display)?; + let (c1, c2) = (Rgb565::GREEN, Rgb565::GREEN); + + while tk.pow(2) <= width_l + (tk * 2 * delta.major) && width_l > 0 { + let thing = tk.pow(2) as f32 / width_l as f32; + let fract = 1.0 - thing; + dbg!(fract); + + Pixel( + point, + Rgb565::new( + (c1.r() as f32 * fract) as u8, + (c1.g() as f32 * fract) as u8, + (c1.b() as f32 * fract) as u8, + ), + ) + .draw(display)?; if error >= threshold { point += step.major; @@ -129,24 +141,24 @@ fn x_perpendicular( point += step.minor; tk += 2 * dx; - // Antialiasing - { - let thing = tk.pow(2) as f32 / width_l as f32; - - if thing >= 1.0 { - let fract = 1.0 - thing.fract(); - - Pixel( - point, - Rgb565::new( - (c1.r() as f32 * fract) as u8, - (c1.g() as f32 * fract) as u8, - (c1.b() as f32 * fract) as u8, - ), - ) - .draw(display)?; - } - } + // // Antialiasing + // { + // let thing = tk.pow(2) as f32 / width_l as f32; + + // if thing >= 1.0 { + // let fract = 1.0 - thing.fract(); + + // Pixel( + // point, + // Rgb565::new( + // (c1.r() as f32 * fract) as u8, + // (c1.g() as f32 * fract) as u8, + // (c1.b() as f32 * fract) as u8, + // ), + // ) + // .draw(display)?; + // } + // } } let mut point = Point::new(x0, y0); @@ -219,7 +231,7 @@ fn x_varthick_line( display, point.x, point.y, delta, pstep, p_error, width, error, false, )?; - Pixel(point, Rgb565::BLACK).draw(display)?; + Pixel(point, Rgb565::WHITE).draw(display)?; if error > threshold { point += step.minor; @@ -263,7 +275,7 @@ struct LineDebug { impl App for LineDebug { type Color = Rgb565; - const DISPLAY_SIZE: Size = Size::new(256, 256); + const DISPLAY_SIZE: Size = Size::new(200, 200); // const DISPLAY_SIZE: Size = Size::new(64, 64); fn new() -> Self { @@ -338,19 +350,19 @@ impl App for LineDebug { x_varthick_line(display, x0, y0, delta, step, pstep, width)?; // x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; - // Line::new(self.start, self.end) - // .into_styled(PrimitiveStyle::with_stroke( - // Rgb565::GREEN, - // self.stroke_width, - // )) - // .draw(display)?; + Line::new(self.start, self.end) + .into_styled(PrimitiveStyle::with_stroke( + Rgb565::GREEN, + self.stroke_width, + )) + .draw(&mut display.translated(Point::new(40, 40)))?; Ok(()) } } fn main() { - let settings = OutputSettingsBuilder::new().scale(3).build(); + let settings = OutputSettingsBuilder::new().scale(5).build(); let window = Window::new("Line debugger", &settings); LineDebug::run(window); From c9b2eed9d2aa44ffcc2e38fd97e0b851ed10a5f1 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 18 Aug 2021 21:38:09 +0100 Subject: [PATCH 021/188] Better antialiasing --- debug-tools/examples/line-perp.rs | 72 +++++++++++-------------------- 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 305e858..76d01ad 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -116,10 +116,13 @@ fn x_perpendicular( let (c1, c2) = (Rgb565::GREEN, Rgb565::GREEN); - while tk.pow(2) <= width_l + (tk * 2 * delta.major) && width_l > 0 { - let thing = tk.pow(2) as f32 / width_l as f32; - let fract = 1.0 - thing; - dbg!(fract); + while tk.pow(2) <= width_l + tk.pow(2) && width_l > 0 { + let thing = (tk.pow(2) - width_l) as f32 / width_l as f32; + let fract = if tk.pow(2) > width_l { + 1.0 - thing + } else { + 1.0 + }; Pixel( point, @@ -140,25 +143,6 @@ fn x_perpendicular( error += e_major; point += step.minor; tk += 2 * dx; - - // // Antialiasing - // { - // let thing = tk.pow(2) as f32 / width_l as f32; - - // if thing >= 1.0 { - // let fract = 1.0 - thing.fract(); - - // Pixel( - // point, - // Rgb565::new( - // (c1.r() as f32 * fract) as u8, - // (c1.g() as f32 * fract) as u8, - // (c1.b() as f32 * fract) as u8, - // ), - // ) - // .draw(display)?; - // } - // } } let mut point = Point::new(x0, y0); @@ -166,9 +150,24 @@ fn x_perpendicular( let mut tk = winit; let mut p = 0; - while tk.pow(2) <= width_r && width_r > 0 { + while tk.pow(2) <= width_r + tk.pow(2) && width_r > 0 { if p > 0 { - Pixel(point, c2).draw(display)?; + let thing = (tk.pow(2) - width_l) as f32 / width_l as f32; + let fract = if tk.pow(2) > width_l { + 1.0 - thing + } else { + 1.0 + }; + + Pixel( + point, + Rgb565::new( + (c1.r() as f32 * fract) as u8, + (c1.g() as f32 * fract) as u8, + (c1.b() as f32 * fract) as u8, + ), + ) + .draw(display)?; } if error > threshold { @@ -181,25 +180,6 @@ fn x_perpendicular( point -= step.minor; tk += 2 * dx; p += 1; - - // Antialiasing - { - let thing = tk.pow(2) as f32 / width_r as f32; - - if thing > 1.0 { - let fract = 1.0 - thing.fract(); - - Pixel( - point, - Rgb565::new( - (c2.r() as f32 * fract) as u8, - (c2.g() as f32 * fract) as u8, - (c2.b() as f32 * fract) as u8, - ), - ) - .draw(display)?; - } - } } Ok(()) @@ -231,7 +211,7 @@ fn x_varthick_line( display, point.x, point.y, delta, pstep, p_error, width, error, false, )?; - Pixel(point, Rgb565::WHITE).draw(display)?; + // Pixel(point, Rgb565::WHITE).draw(display)?; if error > threshold { point += step.minor; @@ -251,7 +231,7 @@ fn x_varthick_line( true, )?; - Pixel(point, Rgb565::BLACK).draw(display)?; + // Pixel(point, Rgb565::BLACK).draw(display)?; } p_error += e_minor; From 096b5c12d46596040a85606a1370f4cea7662258 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 2 Sep 2021 19:59:04 +0100 Subject: [PATCH 022/188] Some experiments with actual width accumulator --- debug-tools/examples/line-perp.rs | 39 ++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 76d01ad..1abf5f9 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -46,7 +46,7 @@ impl MajorMinor { } } -fn x_perpendicular( +fn perpendicular( display: &mut impl DrawTarget, x0: i32, y0: i32, @@ -105,6 +105,9 @@ fn x_perpendicular( core::mem::swap(&mut width_l, &mut width_r); } + let orig_width_l = width_l as f32; + let orig_width_r = width_r as f32; + let width_l = width_l.pow(2) * (dx * dx + dy * dy); let width_r = width_r.pow(2) * (dx * dx + dy * dy); @@ -116,14 +119,28 @@ fn x_perpendicular( let (c1, c2) = (Rgb565::GREEN, Rgb565::GREEN); - while tk.pow(2) <= width_l + tk.pow(2) && width_l > 0 { + let mut distance = 0.0f32; + + let origin = Point::new(x0, y0); + + dbg!(orig_width_l); + + // while tk.pow(2) <= width_l + tk.pow(2) && width_l > 0 { + while distance.floor() <= orig_width_l * 2.0 && width_l > 0 { + println!("---"); let thing = (tk.pow(2) - width_l) as f32 / width_l as f32; + + let thing = thing / orig_width_l; + + dbg!(thing, tk.pow(2), width_l); let fract = if tk.pow(2) > width_l { 1.0 - thing } else { 1.0 }; + let fract = fract.max(0.2); + Pixel( point, Rgb565::new( @@ -143,8 +160,18 @@ fn x_perpendicular( error += e_major; point += step.minor; tk += 2 * dx; + + dbg!(distance); + + distance = { + let delta = point - origin; + + f32::sqrt((delta.x.pow(2) + delta.y.pow(2)) as f32) + }; } + println!("\n===========================\n"); + let mut point = Point::new(x0, y0); let mut error = -einit; let mut tk = winit; @@ -185,7 +212,7 @@ fn x_perpendicular( Ok(()) } -fn x_varthick_line( +fn thick_line( display: &mut impl DrawTarget, x0: i32, y0: i32, @@ -207,7 +234,7 @@ fn x_varthick_line( let length = dx + 1; for _ in 0..length { - x_perpendicular( + perpendicular( display, point.x, point.y, delta, pstep, p_error, width, error, false, )?; @@ -219,7 +246,7 @@ fn x_varthick_line( if p_error >= threshold { if width > 1 { - x_perpendicular( + perpendicular( display, point.x, point.y, @@ -327,7 +354,7 @@ impl App for LineDebug { let mut mock_display: MockDisplay = MockDisplay::new(); // mock_display.set_allow_out_of_bounds_drawing(true); - x_varthick_line(display, x0, y0, delta, step, pstep, width)?; + thick_line(display, x0, y0, delta, step, pstep, width)?; // x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; Line::new(self.start, self.end) From e4a46b95b9e78d9dcf5af51d3bdc1f140e34380a Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 2 Sep 2021 20:01:10 +0100 Subject: [PATCH 023/188] Move some computation into thick_line() --- debug-tools/examples/line-perp.rs | 83 +++++++++++++++---------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 1abf5f9..d0e2dbd 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -214,16 +214,48 @@ fn perpendicular( fn thick_line( display: &mut impl DrawTarget, - x0: i32, - y0: i32, - delta: MajorMinor, - step: MajorMinor, - pstep: MajorMinor, + start: Point, + end: Point, width: i32, ) -> Result<(), std::convert::Infallible> { + let (delta, step, pstep) = { + let delta = end - start; + + let direction = Point::new( + if delta.x >= 0 { 1 } else { -1 }, + if delta.y >= 0 { 1 } else { -1 }, + ); + + let perp_direction = { + let perp_delta = Point::new(delta.y, -delta.x); + + Point::new( + if perp_delta.x >= 0 { 1 } else { -1 }, + if perp_delta.y >= 0 { 1 } else { -1 }, + ) + }; + + // let delta = delta.abs(); + + // Determine major and minor directions. + if delta.y.abs() >= delta.x.abs() { + ( + MajorMinor::new(delta.y, delta.x), + MajorMinor::new(direction.y_axis(), direction.x_axis()), + MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), + ) + } else { + ( + MajorMinor::new(delta.x, delta.y), + MajorMinor::new(direction.x_axis(), direction.y_axis()), + MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), + ) + } + }; + let mut p_error = 0; let mut error = 0; - let mut point = Point::new(x0, y0); + let mut point = start; let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -316,46 +348,9 @@ impl App for LineDebug { // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); let width = self.stroke_width as i32; - let (delta, step, pstep) = { - let delta = self.end - self.start; - - let direction = Point::new( - if delta.x >= 0 { 1 } else { -1 }, - if delta.y >= 0 { 1 } else { -1 }, - ); - - let perp_direction = { - let perp_delta = Point::new(delta.y, -delta.x); - - Point::new( - if perp_delta.x >= 0 { 1 } else { -1 }, - if perp_delta.y >= 0 { 1 } else { -1 }, - ) - }; - - // let delta = delta.abs(); - - // Determine major and minor directions. - if delta.y.abs() >= delta.x.abs() { - ( - MajorMinor::new(delta.y, delta.x), - MajorMinor::new(direction.y_axis(), direction.x_axis()), - MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), - ) - } else { - ( - MajorMinor::new(delta.x, delta.y), - MajorMinor::new(direction.x_axis(), direction.y_axis()), - MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), - ) - } - }; - let mut mock_display: MockDisplay = MockDisplay::new(); - // mock_display.set_allow_out_of_bounds_drawing(true); - thick_line(display, x0, y0, delta, step, pstep, width)?; - // x_varthick_line(&mut mock_display, x0, y0, delta, step, pstep, width)?; + thick_line(display, self.start, self.end, width)?; Line::new(self.start, self.end) .into_styled(PrimitiveStyle::with_stroke( From 293678784f24d1fefe32087df58c7c1216f99993 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 6 Sep 2021 19:34:29 +0100 Subject: [PATCH 024/188] Compute distance from extents Looks a bit better --- debug-tools/Cargo.toml | 2 +- debug-tools/examples/line-perp.rs | 189 +++++++++++++++++++++++++----- 2 files changed, 158 insertions(+), 33 deletions(-) diff --git a/debug-tools/Cargo.toml b/debug-tools/Cargo.toml index b07017e..1c56d1d 100644 --- a/debug-tools/Cargo.toml +++ b/debug-tools/Cargo.toml @@ -7,5 +7,5 @@ publish = false [dependencies] framework = { path = "../framework" } -embedded-graphics = "0.7.1" +embedded-graphics = { version = "0.7.1", path = "../../embedded-graphics" } embedded-graphics-simulator = "0.3.0" diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index d0e2dbd..2f52714 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -1,8 +1,8 @@ use embedded_graphics::{ mock_display::MockDisplay, - pixelcolor::Rgb565, + pixelcolor::{Gray8, Rgb888}, prelude::*, - primitives::{Line, PrimitiveStyle}, + primitives::{line::StrokeOffset, Line, PrimitiveStyle}, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; @@ -46,8 +46,31 @@ impl MajorMinor { } } +fn dist(line: Line, point: Point) -> f32 { + let Line { + start: Point { x: x1, y: y1 }, + end: Point { x: x2, y: y2 }, + } = line; + let Point { x: x3, y: y3 } = point; + + let delta = line.end - line.start; + + let denom = (delta.x.pow(2) + delta.y.pow(2)) as f32; + + let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / denom; + + let x = x1 as f32 + u * (x2 - x1) as f32; + let y = y1 as f32 + u * (y2 - y1) as f32; + + let dist = f32::sqrt((x - x3 as f32).powi(2) + (y - y3 as f32).powi(2)); + + dist +} + fn perpendicular( - display: &mut impl DrawTarget, + display: &mut impl DrawTarget, + line: Line, + (mut left_extent, mut right_extent): (Line, Line), x0: i32, y0: i32, delta: MajorMinor, @@ -64,7 +87,7 @@ fn perpendicular( } if width == 1 { - return Pixel(point, Rgb565::YELLOW).draw(display); + return Pixel(point, Rgb888::YELLOW).draw(display); } let dx = delta.major; @@ -99,6 +122,7 @@ fn perpendicular( if sign == -1 { core::mem::swap(&mut width_l, &mut width_r); + core::mem::swap(&mut left_extent, &mut right_extent); } if side == LineSide::Right { @@ -112,12 +136,12 @@ fn perpendicular( let width_r = width_r.pow(2) * (dx * dx + dy * dy); let (c1, c2) = if extra { - (Rgb565::RED, Rgb565::GREEN) + (Rgb888::RED, Rgb888::GREEN) } else { - (Rgb565::CSS_CORNFLOWER_BLUE, Rgb565::YELLOW) + (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) }; - let (c1, c2) = (Rgb565::GREEN, Rgb565::GREEN); + let (c1, c2) = (Rgb888::GREEN, Rgb888::GREEN); let mut distance = 0.0f32; @@ -125,25 +149,19 @@ fn perpendicular( dbg!(orig_width_l); + let limit_l = orig_width_l * 2.0; + // while tk.pow(2) <= width_l + tk.pow(2) && width_l > 0 { - while distance.floor() <= orig_width_l * 2.0 && width_l > 0 { - println!("---"); - let thing = (tk.pow(2) - width_l) as f32 / width_l as f32; + while distance.floor() <= limit_l && width_l > 0 { + // println!("---"); - let thing = thing / orig_width_l; + let fract = 1.0 - dbg!(dist(left_extent, point)); - dbg!(thing, tk.pow(2), width_l); - let fract = if tk.pow(2) > width_l { - 1.0 - thing - } else { - 1.0 - }; - - let fract = fract.max(0.2); + // let fract = 1.0; Pixel( point, - Rgb565::new( + Rgb888::new( (c1.r() as f32 * fract) as u8, (c1.g() as f32 * fract) as u8, (c1.b() as f32 * fract) as u8, @@ -161,7 +179,7 @@ fn perpendicular( point += step.minor; tk += 2 * dx; - dbg!(distance); + // dbg!(distance); distance = { let delta = point - origin; @@ -188,7 +206,7 @@ fn perpendicular( Pixel( point, - Rgb565::new( + Rgb888::new( (c1.r() as f32 * fract) as u8, (c1.g() as f32 * fract) as u8, (c1.b() as f32 * fract) as u8, @@ -213,11 +231,14 @@ fn perpendicular( } fn thick_line( - display: &mut impl DrawTarget, - start: Point, - end: Point, + display: &mut impl DrawTarget, + line: Line, width: i32, ) -> Result<(), std::convert::Infallible> { + let Line { start, end } = line; + + let extents = line.extents(width as u32, StrokeOffset::None); + let (delta, step, pstep) = { let delta = end - start; @@ -265,21 +286,27 @@ fn thick_line( let e_major = 2 * dy; let length = dx + 1; + let greys = 255.0 / dx as f32; + for _ in 0..length { perpendicular( - display, point.x, point.y, delta, pstep, p_error, width, error, false, + display, line, extents, point.x, point.y, delta, pstep, p_error, width, error, false, )?; - // Pixel(point, Rgb565::WHITE).draw(display)?; + // Pixel(point, color).draw(display)?; if error > threshold { point += step.minor; error += e_minor; if p_error >= threshold { + // Pixel(point, color).draw(display)?; + if width > 1 { perpendicular( display, + line, + extents, point.x, point.y, delta, @@ -290,7 +317,7 @@ fn thick_line( true, )?; - // Pixel(point, Rgb565::BLACK).draw(display)?; + // Pixel(point, Rgb888::WHITE).draw(display)?; } p_error += e_minor; @@ -306,6 +333,104 @@ fn thick_line( Ok(()) } +// /// Left/right extents perpendicular to a given point. Returns `(L, R)`. +// fn perp_extents( +// start: Point, +// delta: MajorMinor, +// mut step: MajorMinor, +// width: i32, +// ) -> (Point, Point) { +// let mut point = start; + +// if width < 2 { +// return (start, start); +// } + +// let dx = delta.major; +// let dy = delta.minor; + +// let mut sign = match (step.major, step.minor) { +// (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, +// (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, +// (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, +// (Point { x: 0, y: 1 }, Point { x: 1, y: 0 }) => -1, +// _ => 1, +// }; + +// if sign == -1 { +// step.major *= -1; +// step.minor *= -1; +// } + +// let dx = dx.abs(); +// let dy = dy.abs(); + +// let threshold = dx - 2 * dy; +// let e_minor = -2 * dx; +// let e_major = 2 * dy; + +// let mut error = 0; +// let mut tk = 0i32; + +// let side = LineSide::Center; + +// let (mut width_l, mut width_r) = side.widths(width); + +// if sign == -1 { +// core::mem::swap(&mut width_l, &mut width_r); +// } + +// if side == LineSide::Right { +// core::mem::swap(&mut width_l, &mut width_r); +// } + +// let orig_width_l = width_l as f32; +// let orig_width_r = width_r as f32; + +// let width_l = width_l.pow(2) * (dx * dx + dy * dy); +// let width_r = width_r.pow(2) * (dx * dx + dy * dy); + +// while tk.pow(2) <= width_l && width_l > 0 { +// if error >= threshold { +// point += step.major; +// error += e_minor; +// tk += 2 * dy; +// } + +// error += e_major; +// point += step.minor; +// tk += 2 * dx; +// } + +// let point_l = point; + +// let mut point = start; +// let mut error = 0; +// let mut tk = 0i32; +// let mut p = 0; + +// while tk.pow(2) <= width_r && width_r > 0 { +// if error > threshold { +// point -= step.major; +// error += e_minor; +// tk += 2 * dy; +// } + +// error += e_major; +// point -= step.minor; +// tk += 2 * dx; +// p += 1; +// } + +// (point_l, point) +// } + +// fn extents(start: Point, end: Point, width: i32, step: MajorMinor) -> (Line, Line) { +// let delta = end - start; + +// let (start_l, start_r) = perp_extents(start, delta, step, width); +// } + struct LineDebug { start: Point, end: Point, @@ -313,7 +438,7 @@ struct LineDebug { } impl App for LineDebug { - type Color = Rgb565; + type Color = Rgb888; const DISPLAY_SIZE: Size = Size::new(200, 200); // const DISPLAY_SIZE: Size = Size::new(64, 64); @@ -348,13 +473,13 @@ impl App for LineDebug { // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); let width = self.stroke_width as i32; - let mut mock_display: MockDisplay = MockDisplay::new(); + let mut mock_display: MockDisplay = MockDisplay::new(); - thick_line(display, self.start, self.end, width)?; + thick_line(display, Line::new(self.start, self.end), width)?; Line::new(self.start, self.end) .into_styled(PrimitiveStyle::with_stroke( - Rgb565::GREEN, + Rgb888::GREEN, self.stroke_width, )) .draw(&mut display.translated(Point::new(40, 40)))?; From 33f37f37e1098e8e17d867249294c44a8f7c92bb Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 6 Sep 2021 19:55:34 +0100 Subject: [PATCH 025/188] Half ok AA on one side --- debug-tools/examples/line-perp.rs | 56 +++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 2f52714..e516d37 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -1,20 +1,25 @@ use embedded_graphics::{ + geometry::PointExt, mock_display::MockDisplay, pixelcolor::{Gray8, Rgb888}, prelude::*, - primitives::{line::StrokeOffset, Line, PrimitiveStyle}, + primitives::{ + common::{LineSide, LinearEquation}, + line::StrokeOffset, + Line, PrimitiveStyle, + }, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; #[derive(Debug, Copy, Clone, PartialEq)] -enum LineSide { +enum LineOffset { Left, Center, Right, } -impl LineSide { +impl LineOffset { fn widths(self, width: i32) -> (i32, i32) { match width { width => { @@ -116,16 +121,19 @@ fn perpendicular( let mut error = einit; let mut tk = -winit; - let side = LineSide::Center; + let side = LineOffset::Center; let (mut width_l, mut width_r) = side.widths(width); + let (mut side_check_left, mut side_check_right) = (LineSide::Left, LineSide::Right); + if sign == -1 { core::mem::swap(&mut width_l, &mut width_r); core::mem::swap(&mut left_extent, &mut right_extent); + core::mem::swap(&mut side_check_left, &mut side_check_right); } - if side == LineSide::Right { + if side == LineOffset::Right { core::mem::swap(&mut width_l, &mut width_r); } @@ -135,19 +143,19 @@ fn perpendicular( let width_l = width_l.pow(2) * (dx * dx + dy * dy); let width_r = width_r.pow(2) * (dx * dx + dy * dy); - let (c1, c2) = if extra { + let (c_left, c_right) = if extra { (Rgb888::RED, Rgb888::GREEN) } else { (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) }; - let (c1, c2) = (Rgb888::GREEN, Rgb888::GREEN); + // let (c1, c2) = (Rgb888::GREEN, Rgb888::GREEN); let mut distance = 0.0f32; let origin = Point::new(x0, y0); - dbg!(orig_width_l); + // dbg!(orig_width_l); let limit_l = orig_width_l * 2.0; @@ -155,16 +163,26 @@ fn perpendicular( while distance.floor() <= limit_l && width_l > 0 { // println!("---"); - let fract = 1.0 - dbg!(dist(left_extent, point)); + let is_outside = { + let le1 = LinearEquation::from_line(&left_extent); + + le1.check_side(point, side_check_left) + }; - // let fract = 1.0; + // let fract = 1.0 - dbg!(dist(left_extent, point)); + + let fract = if !is_outside { + 1.0 + } else { + 1.0 - dist(left_extent, point) + }; Pixel( point, Rgb888::new( - (c1.r() as f32 * fract) as u8, - (c1.g() as f32 * fract) as u8, - (c1.b() as f32 * fract) as u8, + (c_left.r() as f32 * fract) as u8, + (c_left.g() as f32 * fract) as u8, + (c_left.b() as f32 * fract) as u8, ), ) .draw(display)?; @@ -188,14 +206,14 @@ fn perpendicular( }; } - println!("\n===========================\n"); + // println!("\n===========================\n"); let mut point = Point::new(x0, y0); let mut error = -einit; let mut tk = winit; let mut p = 0; - while tk.pow(2) <= width_r + tk.pow(2) && width_r > 0 { + while tk.pow(2) <= width_r && width_r > 0 { if p > 0 { let thing = (tk.pow(2) - width_l) as f32 / width_l as f32; let fract = if tk.pow(2) > width_l { @@ -204,12 +222,14 @@ fn perpendicular( 1.0 }; + let fract = 1.0; + Pixel( point, Rgb888::new( - (c1.r() as f32 * fract) as u8, - (c1.g() as f32 * fract) as u8, - (c1.b() as f32 * fract) as u8, + (c_right.r() as f32 * fract) as u8, + (c_right.g() as f32 * fract) as u8, + (c_right.b() as f32 * fract) as u8, ), ) .draw(display)?; From 3d06d757e7063400e1fc7b0af321a28ad9b937fd Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 6 Sep 2021 19:59:25 +0100 Subject: [PATCH 026/188] AA on both sides --- debug-tools/examples/line-perp.rs | 203 ++++++++++++------------------ 1 file changed, 79 insertions(+), 124 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index e516d37..19e9238 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -149,17 +149,17 @@ fn perpendicular( (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) }; - // let (c1, c2) = (Rgb888::GREEN, Rgb888::GREEN); - - let mut distance = 0.0f32; + let (c_left, c_right) = (Rgb888::GREEN, Rgb888::GREEN); let origin = Point::new(x0, y0); // dbg!(orig_width_l); let limit_l = orig_width_l * 2.0; + let limit_r = orig_width_r * 2.0; + + let mut distance = 0.0f32; - // while tk.pow(2) <= width_l + tk.pow(2) && width_l > 0 { while distance.floor() <= limit_l && width_l > 0 { // println!("---"); @@ -208,32 +208,78 @@ fn perpendicular( // println!("\n===========================\n"); + // let mut point = Point::new(x0, y0); + // let mut error = -einit; + // let mut tk = winit; + // let mut p = 0; + + // while tk.pow(2) <= width_r + tk.pow(2) && width_r > 0 { + // if p > 0 { + // let thing = (tk.pow(2) - width_l) as f32 / width_l as f32; + // let fract = if tk.pow(2) > width_l { + // 1.0 - thing + // } else { + // 1.0 + // }; + + // Pixel( + // point, + // Rgb565::new( + // (c1.r() as f32 * fract) as u8, + // (c1.g() as f32 * fract) as u8, + // (c1.b() as f32 * fract) as u8, + // ), + // ) + // .draw(display)?; + // } + + // if error > threshold { + // point -= step.major; + // error += e_minor; + // tk += 2 * dy; + // } + + // error += e_major; + // point -= step.minor; + // tk += 2 * dx; + // p += 1; + // } + + // // --- + let mut point = Point::new(x0, y0); let mut error = -einit; let mut tk = winit; let mut p = 0; - while tk.pow(2) <= width_r && width_r > 0 { - if p > 0 { - let thing = (tk.pow(2) - width_l) as f32 / width_l as f32; - let fract = if tk.pow(2) > width_l { - 1.0 - thing - } else { - 1.0 - }; - - let fract = 1.0; - - Pixel( - point, - Rgb888::new( - (c_right.r() as f32 * fract) as u8, - (c_right.g() as f32 * fract) as u8, - (c_right.b() as f32 * fract) as u8, - ), - ) - .draw(display)?; - } + let mut distance = 0.0f32; + + while distance.floor() <= limit_r && width_r > 0 { + // println!("---"); + + let is_outside = { + let le1 = LinearEquation::from_line(&right_extent); + + le1.check_side(point, side_check_right) + }; + + // let fract = 1.0 - dbg!(dist(left_extent, point)); + + let fract = if !is_outside { + 1.0 + } else { + 1.0 - dist(right_extent, point) + }; + + Pixel( + point, + Rgb888::new( + (c_right.r() as f32 * fract) as u8, + (c_right.g() as f32 * fract) as u8, + (c_right.b() as f32 * fract) as u8, + ), + ) + .draw(display)?; if error > threshold { point -= step.major; @@ -244,7 +290,14 @@ fn perpendicular( error += e_major; point -= step.minor; tk += 2 * dx; - p += 1; + + // dbg!(distance); + + distance = { + let delta = point - origin; + + f32::sqrt((delta.x.pow(2) + delta.y.pow(2)) as f32) + }; } Ok(()) @@ -353,104 +406,6 @@ fn thick_line( Ok(()) } -// /// Left/right extents perpendicular to a given point. Returns `(L, R)`. -// fn perp_extents( -// start: Point, -// delta: MajorMinor, -// mut step: MajorMinor, -// width: i32, -// ) -> (Point, Point) { -// let mut point = start; - -// if width < 2 { -// return (start, start); -// } - -// let dx = delta.major; -// let dy = delta.minor; - -// let mut sign = match (step.major, step.minor) { -// (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, -// (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, -// (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, -// (Point { x: 0, y: 1 }, Point { x: 1, y: 0 }) => -1, -// _ => 1, -// }; - -// if sign == -1 { -// step.major *= -1; -// step.minor *= -1; -// } - -// let dx = dx.abs(); -// let dy = dy.abs(); - -// let threshold = dx - 2 * dy; -// let e_minor = -2 * dx; -// let e_major = 2 * dy; - -// let mut error = 0; -// let mut tk = 0i32; - -// let side = LineSide::Center; - -// let (mut width_l, mut width_r) = side.widths(width); - -// if sign == -1 { -// core::mem::swap(&mut width_l, &mut width_r); -// } - -// if side == LineSide::Right { -// core::mem::swap(&mut width_l, &mut width_r); -// } - -// let orig_width_l = width_l as f32; -// let orig_width_r = width_r as f32; - -// let width_l = width_l.pow(2) * (dx * dx + dy * dy); -// let width_r = width_r.pow(2) * (dx * dx + dy * dy); - -// while tk.pow(2) <= width_l && width_l > 0 { -// if error >= threshold { -// point += step.major; -// error += e_minor; -// tk += 2 * dy; -// } - -// error += e_major; -// point += step.minor; -// tk += 2 * dx; -// } - -// let point_l = point; - -// let mut point = start; -// let mut error = 0; -// let mut tk = 0i32; -// let mut p = 0; - -// while tk.pow(2) <= width_r && width_r > 0 { -// if error > threshold { -// point -= step.major; -// error += e_minor; -// tk += 2 * dy; -// } - -// error += e_major; -// point -= step.minor; -// tk += 2 * dx; -// p += 1; -// } - -// (point_l, point) -// } - -// fn extents(start: Point, end: Point, width: i32, step: MajorMinor) -> (Line, Line) { -// let delta = end - start; - -// let (start_l, start_r) = perp_extents(start, delta, step, width); -// } - struct LineDebug { start: Point, end: Point, From 5f3d20d7f86fe2f8478e20f04f2342b789a9261d Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 12 Nov 2021 22:10:53 +0000 Subject: [PATCH 027/188] Bit of cleanup --- debug-tools/examples/line-perp.rs | 57 ------------------------------- 1 file changed, 57 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 19e9238..b6bf031 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -153,24 +153,18 @@ fn perpendicular( let origin = Point::new(x0, y0); - // dbg!(orig_width_l); - let limit_l = orig_width_l * 2.0; let limit_r = orig_width_r * 2.0; let mut distance = 0.0f32; while distance.floor() <= limit_l && width_l > 0 { - // println!("---"); - let is_outside = { let le1 = LinearEquation::from_line(&left_extent); le1.check_side(point, side_check_left) }; - // let fract = 1.0 - dbg!(dist(left_extent, point)); - let fract = if !is_outside { 1.0 } else { @@ -197,8 +191,6 @@ fn perpendicular( point += step.minor; tk += 2 * dx; - // dbg!(distance); - distance = { let delta = point - origin; @@ -206,47 +198,6 @@ fn perpendicular( }; } - // println!("\n===========================\n"); - - // let mut point = Point::new(x0, y0); - // let mut error = -einit; - // let mut tk = winit; - // let mut p = 0; - - // while tk.pow(2) <= width_r + tk.pow(2) && width_r > 0 { - // if p > 0 { - // let thing = (tk.pow(2) - width_l) as f32 / width_l as f32; - // let fract = if tk.pow(2) > width_l { - // 1.0 - thing - // } else { - // 1.0 - // }; - - // Pixel( - // point, - // Rgb565::new( - // (c1.r() as f32 * fract) as u8, - // (c1.g() as f32 * fract) as u8, - // (c1.b() as f32 * fract) as u8, - // ), - // ) - // .draw(display)?; - // } - - // if error > threshold { - // point -= step.major; - // error += e_minor; - // tk += 2 * dy; - // } - - // error += e_major; - // point -= step.minor; - // tk += 2 * dx; - // p += 1; - // } - - // // --- - let mut point = Point::new(x0, y0); let mut error = -einit; let mut tk = winit; @@ -255,16 +206,12 @@ fn perpendicular( let mut distance = 0.0f32; while distance.floor() <= limit_r && width_r > 0 { - // println!("---"); - let is_outside = { let le1 = LinearEquation::from_line(&right_extent); le1.check_side(point, side_check_right) }; - // let fract = 1.0 - dbg!(dist(left_extent, point)); - let fract = if !is_outside { 1.0 } else { @@ -291,8 +238,6 @@ fn perpendicular( point -= step.minor; tk += 2 * dx; - // dbg!(distance); - distance = { let delta = point - origin; @@ -329,8 +274,6 @@ fn thick_line( ) }; - // let delta = delta.abs(); - // Determine major and minor directions. if delta.y.abs() >= delta.x.abs() { ( From 8194f5896fa9fe3285c1499e19a5a55365bb3e82 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 13 Nov 2021 10:07:08 +0000 Subject: [PATCH 028/188] Slightly better, but now we have holes in the middle sometimes --- debug-tools/examples/line-perp.rs | 84 ++++++++++++++++--------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index b6bf031..1247af7 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -28,8 +28,8 @@ impl LineOffset { Self::Center => { let width = width.saturating_sub(1); - // Right-side bias for odd width lines. Move mod2 to first item to bias to - // the left. + // Right-side bias for even width lines. Move mod2 to first item in the + // tuple to bias to the left instead. (width, width + (width % 2)) } Self::Right => ((width * 2).saturating_sub(1), 0), @@ -91,14 +91,14 @@ fn perpendicular( return Ok(()); } - if width == 1 { - return Pixel(point, Rgb888::YELLOW).draw(display); - } + // if width == 1 { + // return Pixel(point, Rgb888::YELLOW).draw(display); + // } let dx = delta.major; let dy = delta.minor; - let mut sign = match (step.major, step.minor) { + let sign = match (step.major, step.minor) { (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, @@ -106,10 +106,10 @@ fn perpendicular( _ => 1, }; - if sign == -1 { - step.major *= -1; - step.minor *= -1; - } + // if sign == -1 { + // step.major *= -1; + // step.minor *= -1; + // } let dx = dx.abs(); let dy = dy.abs(); @@ -118,8 +118,7 @@ fn perpendicular( let e_minor = -2 * dx; let e_major = 2 * dy; - let mut error = einit; - let mut tk = -winit; + let mut error = einit * sign; let side = LineOffset::Center; @@ -127,15 +126,15 @@ fn perpendicular( let (mut side_check_left, mut side_check_right) = (LineSide::Left, LineSide::Right); - if sign == -1 { - core::mem::swap(&mut width_l, &mut width_r); - core::mem::swap(&mut left_extent, &mut right_extent); - core::mem::swap(&mut side_check_left, &mut side_check_right); - } + // if sign == -1 { + // core::mem::swap(&mut width_l, &mut width_r); + // core::mem::swap(&mut left_extent, &mut right_extent); + // core::mem::swap(&mut side_check_left, &mut side_check_right); + // } - if side == LineOffset::Right { - core::mem::swap(&mut width_l, &mut width_r); - } + // if side == LineOffset::Right { + // core::mem::swap(&mut width_l, &mut width_r); + // } let orig_width_l = width_l as f32; let orig_width_r = width_r as f32; @@ -149,7 +148,7 @@ fn perpendicular( (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) }; - let (c_left, c_right) = (Rgb888::GREEN, Rgb888::GREEN); + // let (c_left, c_right) = (Rgb888::GREEN, Rgb888::GREEN); let origin = Point::new(x0, y0); @@ -184,12 +183,10 @@ fn perpendicular( if error >= threshold { point += step.major; error += e_minor; - tk += 2 * dy; } error += e_major; point += step.minor; - tk += 2 * dx; distance = { let delta = point - origin; @@ -199,9 +196,7 @@ fn perpendicular( } let mut point = Point::new(x0, y0); - let mut error = -einit; - let mut tk = winit; - let mut p = 0; + let mut error = einit * -sign; let mut distance = 0.0f32; @@ -231,12 +226,10 @@ fn perpendicular( if error > threshold { point -= step.major; error += e_minor; - tk += 2 * dy; } error += e_major; point -= step.minor; - tk += 2 * dx; distance = { let delta = point - origin; @@ -266,7 +259,9 @@ fn thick_line( ); let perp_direction = { - let perp_delta = Point::new(delta.y, -delta.x); + // let perp_delta = Point::new(delta.y, -delta.x); + let perp_delta = line.perpendicular(); + let perp_delta = perp_delta.end - perp_delta.start; Point::new( if perp_delta.x >= 0 { 1 } else { -1 }, @@ -304,20 +299,25 @@ fn thick_line( let greys = 255.0 / dx as f32; - for _ in 0..length { + let skele_color = Rgb888::MAGENTA; + + for i in 0..length { + // let draw_skele = i % 2 == 0; + let draw_skele = false; + perpendicular( display, line, extents, point.x, point.y, delta, pstep, p_error, width, error, false, )?; - // Pixel(point, color).draw(display)?; + if draw_skele { + Pixel(point, skele_color).draw(display)?; + } if error > threshold { point += step.minor; error += e_minor; if p_error >= threshold { - // Pixel(point, color).draw(display)?; - if width > 1 { perpendicular( display, @@ -333,7 +333,9 @@ fn thick_line( true, )?; - // Pixel(point, Rgb888::WHITE).draw(display)?; + if draw_skele { + Pixel(point, skele_color).draw(display)?; + } } p_error += e_minor; @@ -395,12 +397,14 @@ impl App for LineDebug { thick_line(display, Line::new(self.start, self.end), width)?; - Line::new(self.start, self.end) - .into_styled(PrimitiveStyle::with_stroke( - Rgb888::GREEN, - self.stroke_width, - )) - .draw(&mut display.translated(Point::new(40, 40)))?; + // let l = Line::new(self.start, self.end); + + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + // l.perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; Ok(()) } From 8c1c0e900aadef366eef3687dcbc6370a30ac910 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 13 Nov 2021 10:08:44 +0000 Subject: [PATCH 029/188] Fix holes --- debug-tools/examples/line-perp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 1247af7..2ef4f8a 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -180,7 +180,7 @@ fn perpendicular( ) .draw(display)?; - if error >= threshold { + if error > threshold { point += step.major; error += e_minor; } From 68ed6bf002ddacb15115dee4962767181b8c12bc Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 13 Nov 2021 10:37:27 +0000 Subject: [PATCH 030/188] Sligtly more optimised side --- debug-tools/examples/line-perp.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 2ef4f8a..cdbf61f 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -157,18 +157,20 @@ fn perpendicular( let mut distance = 0.0f32; - while distance.floor() <= limit_l && width_l > 0 { - let is_outside = { - let le1 = LinearEquation::from_line(&left_extent); + let mut prev_d = orig_width_l; - le1.check_side(point, side_check_left) - }; + let le1 = LinearEquation::from_line(&left_extent); - let fract = if !is_outside { - 1.0 - } else { - 1.0 - dist(left_extent, point) - }; + loop { + let is_outside = le1.check_side(point, side_check_left); + + let d = dist(left_extent, point); + + let fract = if !is_outside { 1.0 } else { 1.0 - d }; + + if fract <= 0.0 { + break; + } Pixel( point, @@ -187,12 +189,6 @@ fn perpendicular( error += e_major; point += step.minor; - - distance = { - let delta = point - origin; - - f32::sqrt((delta.x.pow(2) + delta.y.pow(2)) as f32) - }; } let mut point = Point::new(x0, y0); From d9e67a014549f7c4fd1cba78380c9a6c20aec86f Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 13 Nov 2021 11:01:25 +0000 Subject: [PATCH 031/188] Make other side nicer Still issues with 1px wide lines but everything else seems a lot better --- debug-tools/examples/line-perp.rs | 70 ++++++------------------------- 1 file changed, 12 insertions(+), 58 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index cdbf61f..8de4ed9 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -91,10 +91,6 @@ fn perpendicular( return Ok(()); } - // if width == 1 { - // return Pixel(point, Rgb888::YELLOW).draw(display); - // } - let dx = delta.major; let dy = delta.minor; @@ -106,11 +102,6 @@ fn perpendicular( _ => 1, }; - // if sign == -1 { - // step.major *= -1; - // step.minor *= -1; - // } - let dx = dx.abs(); let dy = dy.abs(); @@ -120,27 +111,7 @@ fn perpendicular( let mut error = einit * sign; - let side = LineOffset::Center; - - let (mut width_l, mut width_r) = side.widths(width); - - let (mut side_check_left, mut side_check_right) = (LineSide::Left, LineSide::Right); - - // if sign == -1 { - // core::mem::swap(&mut width_l, &mut width_r); - // core::mem::swap(&mut left_extent, &mut right_extent); - // core::mem::swap(&mut side_check_left, &mut side_check_right); - // } - - // if side == LineOffset::Right { - // core::mem::swap(&mut width_l, &mut width_r); - // } - - let orig_width_l = width_l as f32; - let orig_width_r = width_r as f32; - - let width_l = width_l.pow(2) * (dx * dx + dy * dy); - let width_r = width_r.pow(2) * (dx * dx + dy * dy); + let (side_check_left, side_check_right) = (LineSide::Left, LineSide::Right); let (c_left, c_right) = if extra { (Rgb888::RED, Rgb888::GREEN) @@ -150,19 +121,10 @@ fn perpendicular( // let (c_left, c_right) = (Rgb888::GREEN, Rgb888::GREEN); - let origin = Point::new(x0, y0); - - let limit_l = orig_width_l * 2.0; - let limit_r = orig_width_r * 2.0; - - let mut distance = 0.0f32; - - let mut prev_d = orig_width_l; - - let le1 = LinearEquation::from_line(&left_extent); + let le = LinearEquation::from_line(&left_extent); loop { - let is_outside = le1.check_side(point, side_check_left); + let is_outside = le.check_side(point, side_check_left); let d = dist(left_extent, point); @@ -194,20 +156,18 @@ fn perpendicular( let mut point = Point::new(x0, y0); let mut error = einit * -sign; - let mut distance = 0.0f32; + let le = LinearEquation::from_line(&right_extent); - while distance.floor() <= limit_r && width_r > 0 { - let is_outside = { - let le1 = LinearEquation::from_line(&right_extent); + loop { + let is_outside = le.check_side(point, side_check_right); - le1.check_side(point, side_check_right) - }; + let d = dist(right_extent, point); - let fract = if !is_outside { - 1.0 - } else { - 1.0 - dist(right_extent, point) - }; + let fract = if !is_outside { 1.0 } else { 1.0 - d }; + + if fract <= 0.0 { + break; + } Pixel( point, @@ -226,12 +186,6 @@ fn perpendicular( error += e_major; point -= step.minor; - - distance = { - let delta = point - origin; - - f32::sqrt((delta.x.pow(2) + delta.y.pow(2)) as f32) - }; } Ok(()) From 09c72a31014814dc2c96bd4dff80aab8ce54ba16 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 13 Nov 2021 11:31:38 +0000 Subject: [PATCH 032/188] Skip first pixel on left side --- debug-tools/Cargo.toml | 2 +- debug-tools/examples/line-perp.rs | 19 +++++++++++++++++++ framework/Cargo.toml | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/debug-tools/Cargo.toml b/debug-tools/Cargo.toml index 1c56d1d..835d903 100644 --- a/debug-tools/Cargo.toml +++ b/debug-tools/Cargo.toml @@ -8,4 +8,4 @@ publish = false [dependencies] framework = { path = "../framework" } embedded-graphics = { version = "0.7.1", path = "../../embedded-graphics" } -embedded-graphics-simulator = "0.3.0" +embedded-graphics-simulator = { version = "0.3.0", path = "../../simulator" } diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 8de4ed9..23029d3 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -113,6 +113,8 @@ fn perpendicular( let (side_check_left, side_check_right) = (LineSide::Left, LineSide::Right); + let (width_l, width_r) = LineOffset::Center.widths(width); + let (c_left, c_right) = if extra { (Rgb888::RED, Rgb888::GREEN) } else { @@ -123,6 +125,14 @@ fn perpendicular( let le = LinearEquation::from_line(&left_extent); + // Skip first iteration + if error > threshold { + point += step.major; + error += e_minor; + } + error += e_major; + point += step.minor; + loop { let is_outside = le.check_side(point, side_check_left); @@ -158,6 +168,15 @@ fn perpendicular( let le = LinearEquation::from_line(&right_extent); + // // No overdraw + // if error > threshold { + // point -= step.major; + // error += e_minor; + // } + + // error += e_major; + // point -= step.minor; + loop { let is_outside = le.check_side(point, side_check_right); diff --git a/framework/Cargo.toml b/framework/Cargo.toml index 7e3836f..41eb87b 100644 --- a/framework/Cargo.toml +++ b/framework/Cargo.toml @@ -7,5 +7,5 @@ publish = false [dependencies] embedded-graphics = "0.7.0-beta.1" -embedded-graphics-simulator = "0.3.0-alpha.2" +embedded-graphics-simulator = { version = "0.3.0", path = "../../simulator" } sdl2 = "0.32.2" From 5fc10e15047f5dda092086f186359f0ce15a60df Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 21 Nov 2021 19:56:14 +0000 Subject: [PATCH 033/188] Autofix some warnings in line-perp --- debug-tools/examples/line-perp.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 23029d3..2cc5cee 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -1,12 +1,11 @@ use embedded_graphics::{ - geometry::PointExt, mock_display::MockDisplay, - pixelcolor::{Gray8, Rgb888}, + pixelcolor::Rgb888, prelude::*, primitives::{ common::{LineSide, LinearEquation}, line::StrokeOffset, - Line, PrimitiveStyle, + Line, }, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; @@ -74,15 +73,15 @@ fn dist(line: Line, point: Point) -> f32 { fn perpendicular( display: &mut impl DrawTarget, - line: Line, - (mut left_extent, mut right_extent): (Line, Line), + _line: Line, + (left_extent, right_extent): (Line, Line), x0: i32, y0: i32, delta: MajorMinor, - mut step: MajorMinor, + step: MajorMinor, einit: i32, width: i32, - winit: i32, + _winit: i32, extra: bool, ) -> Result<(), std::convert::Infallible> { let mut point = Point::new(x0, y0); @@ -113,7 +112,7 @@ fn perpendicular( let (side_check_left, side_check_right) = (LineSide::Left, LineSide::Right); - let (width_l, width_r) = LineOffset::Center.widths(width); + let (_width_l, _width_r) = LineOffset::Center.widths(width); let (c_left, c_right) = if extra { (Rgb888::RED, Rgb888::GREEN) @@ -266,11 +265,11 @@ fn thick_line( let e_major = 2 * dy; let length = dx + 1; - let greys = 255.0 / dx as f32; + let _greys = 255.0 / dx as f32; let skele_color = Rgb888::MAGENTA; - for i in 0..length { + for _i in 0..length { // let draw_skele = i % 2 == 0; let draw_skele = false; @@ -356,13 +355,13 @@ impl App for LineDebug { &self, display: &mut SimulatorDisplay, ) -> Result<(), std::convert::Infallible> { - let Point { x: x0, y: y0 } = self.start; + let Point { x: _x0, y: _y0 } = self.start; // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); let width = self.stroke_width as i32; - let mut mock_display: MockDisplay = MockDisplay::new(); + let _mock_display: MockDisplay = MockDisplay::new(); thick_line(display, Line::new(self.start, self.end), width)?; From 51dac971deb40410c527fcbabf33b0d54752767a Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 21 Nov 2021 20:54:23 +0000 Subject: [PATCH 034/188] Try decoupling termination detection from colour scaling --- debug-tools/examples/line-perp.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 2cc5cee..cbcf4b2 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -1,4 +1,5 @@ use embedded_graphics::{ + geometry::PointExt, mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, @@ -135,14 +136,16 @@ fn perpendicular( loop { let is_outside = le.check_side(point, side_check_left); - let d = dist(left_extent, point); - - let fract = if !is_outside { 1.0 } else { 1.0 - d }; + let le_dist = le.distance(point); - if fract <= 0.0 { + if is_outside && le_dist.pow(2) > left_extent.delta().length_squared() { break; } + let d = dist(left_extent, point); + + let fract = if !is_outside { 1.0 } else { 1.0 - d }; + Pixel( point, Rgb888::new( From 103c6756a5988d31d680a3af8c06872a1819f62d Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 21 Nov 2021 21:37:05 +0000 Subject: [PATCH 035/188] Only do float scaling at antialiased edge --- debug-tools/examples/line-perp.rs | 62 ++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index cbcf4b2..9895c67 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -133,26 +133,46 @@ fn perpendicular( error += e_major; point += step.minor; + // println!("---"); + loop { let is_outside = le.check_side(point, side_check_left); - let le_dist = le.distance(point); + // let le_dist = le.distance(p); - if is_outside && le_dist.pow(2) > left_extent.delta().length_squared() { + // if is_outside && le_dist.pow(2) > left_extent.delta().length_squared() { + if is_outside { break; } - let d = dist(left_extent, point); + // let d = dist(left_extent, p); - let fract = if !is_outside { 1.0 } else { 1.0 - d }; + // let fract = if !is_outside { 1.0 } else { 1.0 - d }; + + // println!( + // "{} {} {} {} {} {}", + // is_outside, + // le_dist.pow(2), + // left_extent.delta().length_squared(), + // le_dist.pow(2) > left_extent.delta().length_squared(), + // dist(left_extent, p), + // fract + // ); + + // if fract <= 0.0 { + // break; + // } + + // let fract = 1.0; Pixel( point, - Rgb888::new( - (c_left.r() as f32 * fract) as u8, - (c_left.g() as f32 * fract) as u8, - (c_left.b() as f32 * fract) as u8, - ), + c_left + // Rgb888::new( + // (c_left.r() as f32 * fract) as u8, + // (c_left.g() as f32 * fract) as u8, + // (c_left.b() as f32 * fract) as u8, + // ), ) .draw(display)?; @@ -165,6 +185,30 @@ fn perpendicular( point += step.minor; } + // Last pixel, AA + { + let d = dist(left_extent, point); + + let fract = 1.0 - d; + + let fract = (fract * 255.0) as i32; + + // Don't draw any pixels that are too far away from the line + if fract > 0 { + let fract = fract as u32; + + Pixel( + point, + Rgb888::new( + ((fract * c_left.r() as u32) / 255) as u8, + ((fract * c_left.g() as u32) / 255) as u8, + ((fract * c_left.b() as u32) / 255) as u8, + ), + ) + .draw(display)?; + } + } + let mut point = Point::new(x0, y0); let mut error = einit * -sign; From 05a447bc7e61d34266ccb987e4d0dea2803bf0de Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 21 Nov 2021 21:47:52 +0000 Subject: [PATCH 036/188] Right side, cleanup --- debug-tools/examples/line-perp.rs | 85 ++++++++++--------------------- 1 file changed, 27 insertions(+), 58 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 9895c67..d76ef0e 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -133,48 +133,14 @@ fn perpendicular( error += e_major; point += step.minor; - // println!("---"); - loop { let is_outside = le.check_side(point, side_check_left); - // let le_dist = le.distance(p); - - // if is_outside && le_dist.pow(2) > left_extent.delta().length_squared() { if is_outside { break; } - // let d = dist(left_extent, p); - - // let fract = if !is_outside { 1.0 } else { 1.0 - d }; - - // println!( - // "{} {} {} {} {} {}", - // is_outside, - // le_dist.pow(2), - // left_extent.delta().length_squared(), - // le_dist.pow(2) > left_extent.delta().length_squared(), - // dist(left_extent, p), - // fract - // ); - - // if fract <= 0.0 { - // break; - // } - - // let fract = 1.0; - - Pixel( - point, - c_left - // Rgb888::new( - // (c_left.r() as f32 * fract) as u8, - // (c_left.g() as f32 * fract) as u8, - // (c_left.b() as f32 * fract) as u8, - // ), - ) - .draw(display)?; + Pixel(point, c_left).draw(display)?; if error > threshold { point += step.major; @@ -214,35 +180,14 @@ fn perpendicular( let le = LinearEquation::from_line(&right_extent); - // // No overdraw - // if error > threshold { - // point -= step.major; - // error += e_minor; - // } - - // error += e_major; - // point -= step.minor; - loop { let is_outside = le.check_side(point, side_check_right); - let d = dist(right_extent, point); - - let fract = if !is_outside { 1.0 } else { 1.0 - d }; - - if fract <= 0.0 { + if is_outside { break; } - Pixel( - point, - Rgb888::new( - (c_right.r() as f32 * fract) as u8, - (c_right.g() as f32 * fract) as u8, - (c_right.b() as f32 * fract) as u8, - ), - ) - .draw(display)?; + Pixel(point, c_right).draw(display)?; if error > threshold { point -= step.major; @@ -253,6 +198,30 @@ fn perpendicular( point -= step.minor; } + // Last pixel, AA + { + let d = dist(right_extent, point); + + let fract = 1.0 - d; + + let fract = (fract * 255.0) as i32; + + // Don't draw any pixels that are too far away from the line + if fract > 0 { + let fract = fract as u32; + + Pixel( + point, + Rgb888::new( + ((fract * c_right.r() as u32) / 255) as u8, + ((fract * c_right.g() as u32) / 255) as u8, + ((fract * c_right.b() as u32) / 255) as u8, + ), + ) + .draw(display)?; + } + } + Ok(()) } From 2db3398686dc5c9caa659f687fa463a59d384ec5 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 22 Nov 2021 20:06:40 +0000 Subject: [PATCH 037/188] Add simple 1px wide AA example skeleton --- debug-tools/examples/aa.rs | 142 +++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 debug-tools/examples/aa.rs diff --git a/debug-tools/examples/aa.rs b/debug-tools/examples/aa.rs new file mode 100644 index 0000000..8c6b9ac --- /dev/null +++ b/debug-tools/examples/aa.rs @@ -0,0 +1,142 @@ +//! Render a 1px wide antialiased line using error components and a 255 multiplier. + +use embedded_graphics::{ + mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; + +#[derive(Debug, Clone, Copy)] +struct MajorMinor { + major: T, + minor: T, +} + +impl MajorMinor { + fn new(major: T, minor: T) -> Self { + Self { major, minor } + } +} + +fn thick_line( + display: &mut impl DrawTarget, + line: Line, + _width: i32, +) -> Result<(), std::convert::Infallible> { + let Line { start, end } = line; + + let (delta, step) = { + let delta = end - start; + + let direction = Point::new( + if delta.x >= 0 { 1 } else { -1 }, + if delta.y >= 0 { 1 } else { -1 }, + ); + + // Determine major and minor directions. + if delta.y.abs() >= delta.x.abs() { + ( + MajorMinor::new(delta.y, delta.x), + MajorMinor::new(direction.y_axis(), direction.x_axis()), + ) + } else { + ( + MajorMinor::new(delta.x, delta.y), + MajorMinor::new(direction.x_axis(), direction.y_axis()), + ) + } + }; + + let mut error = 0; + let mut point = start; + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx + 1; + + let skele_color = Rgb888::MAGENTA; + + for _i in 0..length { + Pixel(point, skele_color).draw(display)?; + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + + Ok(()) +} + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, +} + +impl App for LineDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(200, 200); + // const DISPLAY_SIZE: Size = Size::new(64, 64); + + fn new() -> Self { + let end = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); + Self { + start: end + Point::new(10, 15), + end, + // end: start + Point::new(100, 0), + stroke_width: 10, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let Point { x: _x0, y: _y0 } = self.start; + + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; + + let _mock_display: MockDisplay = MockDisplay::new(); + + thick_line(display, Line::new(self.start, self.end), width)?; + + // let l = Line::new(self.start, self.end); + + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + // l.perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(5).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} From 8bdb115b6c27d0fdfa300ab23c0a11746077502f Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 22 Nov 2021 21:49:19 +0000 Subject: [PATCH 038/188] Floating point bresenham --- debug-tools/examples/aa.rs | 120 +++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 37 deletions(-) diff --git a/debug-tools/examples/aa.rs b/debug-tools/examples/aa.rs index 8c6b9ac..b546410 100644 --- a/debug-tools/examples/aa.rs +++ b/debug-tools/examples/aa.rs @@ -1,4 +1,6 @@ //! Render a 1px wide antialiased line using error components and a 255 multiplier. +//! +//! Inspiration from use embedded_graphics::{ mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, @@ -20,58 +22,102 @@ impl MajorMinor { fn thick_line( display: &mut impl DrawTarget, - line: Line, + mut line: Line, _width: i32, ) -> Result<(), std::convert::Infallible> { - let Line { start, end } = line; + let skele_color = Rgb888::MAGENTA; - let (delta, step) = { - let delta = end - start; + // let Line { start, end } = line; - let direction = Point::new( - if delta.x >= 0 { 1 } else { -1 }, - if delta.y >= 0 { 1 } else { -1 }, - ); + let orig_start_y = line.start.y; - // Determine major and minor directions. - if delta.y.abs() >= delta.x.abs() { - ( - MajorMinor::new(delta.y, delta.x), - MajorMinor::new(direction.y_axis(), direction.x_axis()), - ) - } else { - ( - MajorMinor::new(delta.x, delta.y), - MajorMinor::new(direction.x_axis(), direction.y_axis()), - ) - } - }; + // line.start.y <<= 8; + // line.end.y <<= 8; - let mut error = 0; - let mut point = start; + let delta = line.delta(); - let dx = delta.major.abs(); - let dy = delta.minor.abs(); + let dx = delta.x; + let dy = delta.y; - let threshold = dx - 2 * dy; - let e_minor = -2 * dx; - let e_major = 2 * dy; - let length = dx + 1; + let slope = dy as f32 / dx as f32; - let skele_color = Rgb888::MAGENTA; + let mut point = line.start; + + let mut error: f32 = 0.0; + + for _i in 0..=dx { + let c = skele_color; + + // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; - for _i in 0..length { - Pixel(point, skele_color).draw(display)?; + // let c = Rgb888::new( + // ((bright * skele_color.r() as u32) / 255) as u8, + // ((bright * skele_color.g() as u32) / 255) as u8, + // ((bright * skele_color.b() as u32) / 255) as u8, + // ); - if error > threshold { - point += step.minor; - error += e_minor; + Pixel(Point::new(point.x, point.y), c).draw(display)?; + + error += slope; + + if error > 0.5 { + point.y += 1; + error -= 1.0; } - error += e_major; - point += step.major; + point.x += 1; } + // // let Line { start, end } = line; + + // let orig_start_y = line.start.y; + + // // line.start.y <<= 8; + // // line.end.y <<= 8; + + // let delta = line.delta(); + + // let mut error: i32 = 0; + // let mut point = line.start; + + // // let dx = delta.major.abs(); + // // let dy = delta.minor.abs(); + // let dx = delta.x; + // let dy = delta.y; + + // let threshold = dx - 2 * dy; + // let e_minor = -2 * dx; + // let e_major = 2 * dy; + // let length = dx + 1; + + // let skele_color = Rgb888::MAGENTA; + + // let mut py = 0; + + // for _i in 0..length { + // let c = skele_color; + + // // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; + + // // let c = Rgb888::new( + // // ((bright * skele_color.r() as u32) / 255) as u8, + // // ((bright * skele_color.g() as u32) / 255) as u8, + // // ((bright * skele_color.b() as u32) / 255) as u8, + // // ); + + // Pixel(Point::new(point.x, orig_start_y + (py >> 8)), c).draw(display)?; + + // println!("{}", (error as f32) / threshold as f32); + + // if error > threshold { + // py += 1 << 8; + // error += e_minor; + // } + + // error += e_major; + // point.x += 1; + // } + Ok(()) } From 6919c5e629e939cf41d89d9db9e82982025bb17a Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 27 Nov 2021 22:03:07 +0000 Subject: [PATCH 039/188] One-sided AA using Wu algorithm (kinda) --- debug-tools/examples/aa.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/debug-tools/examples/aa.rs b/debug-tools/examples/aa.rs index b546410..ebdd221 100644 --- a/debug-tools/examples/aa.rs +++ b/debug-tools/examples/aa.rs @@ -50,19 +50,34 @@ fn thick_line( // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; - // let c = Rgb888::new( - // ((bright * skele_color.r() as u32) / 255) as u8, - // ((bright * skele_color.g() as u32) / 255) as u8, - // ((bright * skele_color.b() as u32) / 255) as u8, - // ); + let e = error.abs(); + dbg!(e); + + // AA point above line + let bright = ((1.0 - e) * 255.0) as u32; + let c = Rgb888::new( + ((bright * skele_color.r() as u32) / 255) as u8, + ((bright * skele_color.g() as u32) / 255) as u8, + ((bright * skele_color.b() as u32) / 255) as u8, + ); + Pixel(Point::new(point.x, point.y - 1), c).draw(display)?; + + // Line skeleton + // let bright = (e * 255.0) as u32; + let bright = 255; + let c = Rgb888::new( + ((bright * skele_color.r() as u32) / 255) as u8, + ((bright * skele_color.g() as u32) / 255) as u8, + ((bright * skele_color.b() as u32) / 255) as u8, + ); Pixel(Point::new(point.x, point.y), c).draw(display)?; error += slope; - if error > 0.5 { + if error > 1.0 { point.y += 1; - error -= 1.0; + error = 0.0; } point.x += 1; From 7e05502f1eb33a82535d1c7e3b56c3cfe86960c9 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 27 Nov 2021 22:11:53 +0000 Subject: [PATCH 040/188] Simple Bresenham --- debug-tools/examples/aa-bres.rs | 211 +++++++++++++++++++++++ debug-tools/examples/{aa.rs => aa-wu.rs} | 0 debug-tools/examples/line-perp.rs | 78 ++++++--- 3 files changed, 265 insertions(+), 24 deletions(-) create mode 100644 debug-tools/examples/aa-bres.rs rename debug-tools/examples/{aa.rs => aa-wu.rs} (100%) diff --git a/debug-tools/examples/aa-bres.rs b/debug-tools/examples/aa-bres.rs new file mode 100644 index 0000000..2f9ea63 --- /dev/null +++ b/debug-tools/examples/aa-bres.rs @@ -0,0 +1,211 @@ +//! Render a 1px wide antialiased line using error components and a 255 multiplier. +//! +//! Inspiration from + +use embedded_graphics::{ + mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; + +#[derive(Debug, Clone, Copy)] +struct MajorMinor { + major: T, + minor: T, +} + +impl MajorMinor { + fn new(major: T, minor: T) -> Self { + Self { major, minor } + } +} + +fn thick_line( + display: &mut impl DrawTarget, + mut line: Line, + _width: i32, +) -> Result<(), std::convert::Infallible> { + let skele_color = Rgb888::MAGENTA; + + // let Line { start, end } = line; + + let orig_start_y = line.start.y; + + // line.start.y <<= 8; + // line.end.y <<= 8; + + let delta = line.delta(); + + let dx = delta.x; + let dy = delta.y; + + let slope = dy as f32 / dx as f32; + + let mut point = line.start; + + let mut error: i32 = 0; + + let threshold = dx - 2 * dy; + // E_diag + let e_minor = -2 * dx; + // E_square + let e_major = 2 * dy; + + for _i in 0..=dx { + let c = skele_color; + + // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; + + let e = error.abs(); + + dbg!(e); + + // // AA point above line + // let bright = ((1.0 - e) * 255.0) as u32; + // let c = Rgb888::new( + // ((bright * skele_color.r() as u32) / 255) as u8, + // ((bright * skele_color.g() as u32) / 255) as u8, + // ((bright * skele_color.b() as u32) / 255) as u8, + // ); + // Pixel(Point::new(point.x, point.y - 1), c).draw(display)?; + + // Line skeleton + // let bright = (e * 255.0) as u32; + let bright = 255; + let c = Rgb888::new( + ((bright * skele_color.r() as u32) / 255) as u8, + ((bright * skele_color.g() as u32) / 255) as u8, + ((bright * skele_color.b() as u32) / 255) as u8, + ); + Pixel(Point::new(point.x, point.y), c).draw(display)?; + + // error += slope; + + if error > threshold { + point.y += 1; + // error = 0.0; + error += e_minor; + } + + error += e_major; + point.x += 1; + } + + // // let Line { start, end } = line; + + // let orig_start_y = line.start.y; + + // // line.start.y <<= 8; + // // line.end.y <<= 8; + + // let delta = line.delta(); + + // let mut error: i32 = 0; + // let mut point = line.start; + + // // let dx = delta.major.abs(); + // // let dy = delta.minor.abs(); + // let dx = delta.x; + // let dy = delta.y; + + // let threshold = dx - 2 * dy; + // let e_minor = -2 * dx; + // let e_major = 2 * dy; + // let length = dx + 1; + + // let skele_color = Rgb888::MAGENTA; + + // let mut py = 0; + + // for _i in 0..length { + // let c = skele_color; + + // // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; + + // // let c = Rgb888::new( + // // ((bright * skele_color.r() as u32) / 255) as u8, + // // ((bright * skele_color.g() as u32) / 255) as u8, + // // ((bright * skele_color.b() as u32) / 255) as u8, + // // ); + + // Pixel(Point::new(point.x, orig_start_y + (py >> 8)), c).draw(display)?; + + // println!("{}", (error as f32) / threshold as f32); + + // if error > threshold { + // py += 1 << 8; + // error += e_minor; + // } + + // error += e_major; + // point.x += 1; + // } + + Ok(()) +} + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, +} + +impl App for LineDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(200, 200); + // const DISPLAY_SIZE: Size = Size::new(64, 64); + + fn new() -> Self { + let end = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); + Self { + start: end + Point::new(10, 15), + end, + // end: start + Point::new(100, 0), + stroke_width: 10, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let Point { x: _x0, y: _y0 } = self.start; + + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; + + let _mock_display: MockDisplay = MockDisplay::new(); + + thick_line(display, Line::new(self.start, self.end), width)?; + + // let l = Line::new(self.start, self.end); + + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + // l.perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(5).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} diff --git a/debug-tools/examples/aa.rs b/debug-tools/examples/aa-wu.rs similarity index 100% rename from debug-tools/examples/aa.rs rename to debug-tools/examples/aa-wu.rs diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index d76ef0e..cf0db08 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -82,7 +82,7 @@ fn perpendicular( step: MajorMinor, einit: i32, width: i32, - _winit: i32, + winit: i32, extra: bool, ) -> Result<(), std::convert::Infallible> { let mut point = Point::new(x0, y0); @@ -133,28 +133,58 @@ fn perpendicular( error += e_major; point += step.minor; + println!("---"); + + // let mut wthr = width as f32 * f32::sqrt(dx.pow(2) as f32 + dy.pow(2) as f32); + // // TODO: init terms + // let mut tk = dx as f32 + dy as f32 - winit as f32; + + // dbg!(wthr); + + let mut distance = 0.0; + let slope = dy as f32 / dx as f32; + + dbg!(slope); + + // while tk <= wthr { loop { let is_outside = le.check_side(point, side_check_left); + dbg!(error, threshold); + if is_outside { break; } Pixel(point, c_left).draw(display)?; + distance += slope; + if error > threshold { point += step.major; error += e_minor; + // tk += 2.0 * dy as f32 } error += e_major; point += step.minor; + // tk += 2.0 * dx as f32; + + // dbg!(tk); + + // if tk > wthr { + // break; + // } } + dbg!(distance); + // Last pixel, AA { let d = dist(left_extent, point); + dbg!(d); + let fract = 1.0 - d; let fract = (fract * 255.0) as i32; @@ -198,29 +228,29 @@ fn perpendicular( point -= step.minor; } - // Last pixel, AA - { - let d = dist(right_extent, point); - - let fract = 1.0 - d; - - let fract = (fract * 255.0) as i32; - - // Don't draw any pixels that are too far away from the line - if fract > 0 { - let fract = fract as u32; - - Pixel( - point, - Rgb888::new( - ((fract * c_right.r() as u32) / 255) as u8, - ((fract * c_right.g() as u32) / 255) as u8, - ((fract * c_right.b() as u32) / 255) as u8, - ), - ) - .draw(display)?; - } - } + // // Last pixel, AA + // { + // let d = dist(right_extent, point); + + // let fract = 1.0 - d; + + // let fract = (fract * 255.0) as i32; + + // // Don't draw any pixels that are too far away from the line + // if fract > 0 { + // let fract = fract as u32; + + // Pixel( + // point, + // Rgb888::new( + // ((fract * c_right.r() as u32) / 255) as u8, + // ((fract * c_right.g() as u32) / 255) as u8, + // ((fract * c_right.b() as u32) / 255) as u8, + // ), + // ) + // .draw(display)?; + // } + // } Ok(()) } From 48ff80c0f692d1869607114a63a44d7ae66450f2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 27 Nov 2021 22:32:46 +0000 Subject: [PATCH 041/188] Bresenham AA but using floating point --- debug-tools/examples/aa-bres.rs | 77 ++++++++------------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/debug-tools/examples/aa-bres.rs b/debug-tools/examples/aa-bres.rs index 2f9ea63..bca2381 100644 --- a/debug-tools/examples/aa-bres.rs +++ b/debug-tools/examples/aa-bres.rs @@ -51,6 +51,8 @@ fn thick_line( // E_square let e_major = 2 * dy; + let mut br = 1.0; + for _i in 0..=dx { let c = skele_color; @@ -58,16 +60,23 @@ fn thick_line( let e = error.abs(); - dbg!(e); + // let bright = f32::fract(e_major as f32 / e as f32); + + // let bright = e as f32 / (dx as f32); + + dbg!(e, br); + + let bright = br; - // // AA point above line + // AA point above line // let bright = ((1.0 - e) * 255.0) as u32; - // let c = Rgb888::new( - // ((bright * skele_color.r() as u32) / 255) as u8, - // ((bright * skele_color.g() as u32) / 255) as u8, - // ((bright * skele_color.b() as u32) / 255) as u8, - // ); - // Pixel(Point::new(point.x, point.y - 1), c).draw(display)?; + let bright = (bright * 255.0) as u32; + let c = Rgb888::new( + ((bright * skele_color.r() as u32) / 255) as u8, + ((bright * skele_color.g() as u32) / 255) as u8, + ((bright * skele_color.b() as u32) / 255) as u8, + ); + Pixel(Point::new(point.x, point.y - 1), c).draw(display)?; // Line skeleton // let bright = (e * 255.0) as u32; @@ -85,62 +94,14 @@ fn thick_line( point.y += 1; // error = 0.0; error += e_minor; + br = 1.0; } error += e_major; point.x += 1; + br -= slope; } - // // let Line { start, end } = line; - - // let orig_start_y = line.start.y; - - // // line.start.y <<= 8; - // // line.end.y <<= 8; - - // let delta = line.delta(); - - // let mut error: i32 = 0; - // let mut point = line.start; - - // // let dx = delta.major.abs(); - // // let dy = delta.minor.abs(); - // let dx = delta.x; - // let dy = delta.y; - - // let threshold = dx - 2 * dy; - // let e_minor = -2 * dx; - // let e_major = 2 * dy; - // let length = dx + 1; - - // let skele_color = Rgb888::MAGENTA; - - // let mut py = 0; - - // for _i in 0..length { - // let c = skele_color; - - // // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; - - // // let c = Rgb888::new( - // // ((bright * skele_color.r() as u32) / 255) as u8, - // // ((bright * skele_color.g() as u32) / 255) as u8, - // // ((bright * skele_color.b() as u32) / 255) as u8, - // // ); - - // Pixel(Point::new(point.x, orig_start_y + (py >> 8)), c).draw(display)?; - - // println!("{}", (error as f32) / threshold as f32); - - // if error > threshold { - // py += 1 << 8; - // error += e_minor; - // } - - // error += e_major; - // point.x += 1; - // } - Ok(()) } From 6e78903a2ca0062e3269cf32a9caac71b69d6cba Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 27 Nov 2021 22:53:30 +0000 Subject: [PATCH 042/188] Brightness scaled by 255 --- debug-tools/examples/aa-bres.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/debug-tools/examples/aa-bres.rs b/debug-tools/examples/aa-bres.rs index bca2381..42e06a4 100644 --- a/debug-tools/examples/aa-bres.rs +++ b/debug-tools/examples/aa-bres.rs @@ -39,7 +39,8 @@ fn thick_line( let dx = delta.x; let dy = delta.y; - let slope = dy as f32 / dx as f32; + let slope = (dy * 255) as f32 / dx as f32; + dbg!(slope); let mut point = line.start; @@ -51,7 +52,8 @@ fn thick_line( // E_square let e_major = 2 * dy; - let mut br = 1.0; + // TODO: Calculate initial brightness + let mut br = 255.0; for _i in 0..=dx { let c = skele_color; @@ -64,13 +66,14 @@ fn thick_line( // let bright = e as f32 / (dx as f32); - dbg!(e, br); + // dbg!(e, br); - let bright = br; + // let bright = br; // AA point above line // let bright = ((1.0 - e) * 255.0) as u32; - let bright = (bright * 255.0) as u32; + // let bright = (bright * 255.0) as u32; + let bright = br as u32; let c = Rgb888::new( ((bright * skele_color.r() as u32) / 255) as u8, ((bright * skele_color.g() as u32) / 255) as u8, @@ -94,7 +97,7 @@ fn thick_line( point.y += 1; // error = 0.0; error += e_minor; - br = 1.0; + br = 255.0; } error += e_major; From 75dd30c3a5bfa030ec3a8ccaec0262c9d09c1243 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 27 Nov 2021 23:22:13 +0000 Subject: [PATCH 043/188] Integer AA using parallel Bresenham --- debug-tools/examples/aa-bres.rs | 62 ++++++++++----------------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/debug-tools/examples/aa-bres.rs b/debug-tools/examples/aa-bres.rs index 42e06a4..4da5545 100644 --- a/debug-tools/examples/aa-bres.rs +++ b/debug-tools/examples/aa-bres.rs @@ -2,45 +2,38 @@ //! //! Inspiration from +use core::convert::TryFrom; use embedded_graphics::{ mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; -#[derive(Debug, Clone, Copy)] -struct MajorMinor { - major: T, - minor: T, -} - -impl MajorMinor { - fn new(major: T, minor: T) -> Self { - Self { major, minor } - } -} - fn thick_line( display: &mut impl DrawTarget, - mut line: Line, + line: Line, _width: i32, ) -> Result<(), std::convert::Infallible> { let skele_color = Rgb888::MAGENTA; - // let Line { start, end } = line; - - let orig_start_y = line.start.y; - - // line.start.y <<= 8; - // line.end.y <<= 8; - let delta = line.delta(); let dx = delta.x; let dy = delta.y; - let slope = (dy * 255) as f32 / dx as f32; - dbg!(slope); + let num = dy * 255; + let denom = dx; + // Rounding integer division + let slope = ((num) + (denom) / 2) / (denom); + + let slope = if let Ok(slope) = u8::try_from(slope) { + slope + } else { + // Most likely cause: gradient is incorrect due to improper swapping of major/minor + // direction. The slope should always be 1.0 or less, or because we multiply by 255 in this + // case, 255 or less. + return Ok(()); + }; let mut point = line.start; @@ -53,26 +46,10 @@ fn thick_line( let e_major = 2 * dy; // TODO: Calculate initial brightness - let mut br = 255.0; + let mut br: u8 = 255; for _i in 0..=dx { - let c = skele_color; - - // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; - - let e = error.abs(); - - // let bright = f32::fract(e_major as f32 / e as f32); - - // let bright = e as f32 / (dx as f32); - - // dbg!(e, br); - - // let bright = br; - // AA point above line - // let bright = ((1.0 - e) * 255.0) as u32; - // let bright = (bright * 255.0) as u32; let bright = br as u32; let c = Rgb888::new( ((bright * skele_color.r() as u32) / 255) as u8, @@ -91,18 +68,15 @@ fn thick_line( ); Pixel(Point::new(point.x, point.y), c).draw(display)?; - // error += slope; - if error > threshold { point.y += 1; - // error = 0.0; error += e_minor; - br = 255.0; + br = 255; } error += e_major; point.x += 1; - br -= slope; + br = br.saturating_sub(slope); } Ok(()) From 1a834fd4261e7b706422fd79d8e70f233110b9e9 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 27 Nov 2021 23:25:14 +0000 Subject: [PATCH 044/188] Above and below demo --- debug-tools/examples/aa-bres.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/debug-tools/examples/aa-bres.rs b/debug-tools/examples/aa-bres.rs index 4da5545..055f5a3 100644 --- a/debug-tools/examples/aa-bres.rs +++ b/debug-tools/examples/aa-bres.rs @@ -49,7 +49,7 @@ fn thick_line( let mut br: u8 = 255; for _i in 0..=dx { - // AA point above line + // AA point above let bright = br as u32; let c = Rgb888::new( ((bright * skele_color.r() as u32) / 255) as u8, @@ -58,6 +58,15 @@ fn thick_line( ); Pixel(Point::new(point.x, point.y - 1), c).draw(display)?; + // AA point below + let bright = (255 - br) as u32; + let c = Rgb888::new( + ((bright * skele_color.r() as u32) / 255) as u8, + ((bright * skele_color.g() as u32) / 255) as u8, + ((bright * skele_color.b() as u32) / 255) as u8, + ); + Pixel(Point::new(point.x, point.y + 1), c).draw(display)?; + // Line skeleton // let bright = (e * 255.0) as u32; let bright = 255; From 1b577f52f1e99842b0c6e812395f9ad088975104 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 28 Nov 2021 13:18:38 +0000 Subject: [PATCH 045/188] Kinda sorta working parallel integer AA --- debug-tools/examples/aa-bres.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/debug-tools/examples/aa-bres.rs b/debug-tools/examples/aa-bres.rs index 055f5a3..efb2cfa 100644 --- a/debug-tools/examples/aa-bres.rs +++ b/debug-tools/examples/aa-bres.rs @@ -47,8 +47,10 @@ fn thick_line( // TODO: Calculate initial brightness let mut br: u8 = 255; + dbg!(slope); for _i in 0..=dx { + // println!("{} {}", br, error); // AA point above let bright = br as u32; let c = Rgb888::new( @@ -58,14 +60,14 @@ fn thick_line( ); Pixel(Point::new(point.x, point.y - 1), c).draw(display)?; - // AA point below - let bright = (255 - br) as u32; - let c = Rgb888::new( - ((bright * skele_color.r() as u32) / 255) as u8, - ((bright * skele_color.g() as u32) / 255) as u8, - ((bright * skele_color.b() as u32) / 255) as u8, - ); - Pixel(Point::new(point.x, point.y + 1), c).draw(display)?; + // // AA point below + // let bright = (255 - br) as u32; + // let c = Rgb888::new( + // ((bright * skele_color.r() as u32) / 255) as u8, + // ((bright * skele_color.g() as u32) / 255) as u8, + // ((bright * skele_color.b() as u32) / 255) as u8, + // ); + // Pixel(Point::new(point.x, point.y + 1), c).draw(display)?; // Line skeleton // let bright = (e * 255.0) as u32; From 7bf8afc5b554045448f4d527a0b6ca589c19277e Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 29 Nov 2021 13:40:52 +0000 Subject: [PATCH 046/188] Remove debug logs from perp line --- debug-tools/examples/line-perp.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index cf0db08..00a34b0 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -133,8 +133,6 @@ fn perpendicular( error += e_major; point += step.minor; - println!("---"); - // let mut wthr = width as f32 * f32::sqrt(dx.pow(2) as f32 + dy.pow(2) as f32); // // TODO: init terms // let mut tk = dx as f32 + dy as f32 - winit as f32; @@ -144,14 +142,10 @@ fn perpendicular( let mut distance = 0.0; let slope = dy as f32 / dx as f32; - dbg!(slope); - // while tk <= wthr { loop { let is_outside = le.check_side(point, side_check_left); - dbg!(error, threshold); - if is_outside { break; } @@ -177,14 +171,10 @@ fn perpendicular( // } } - dbg!(distance); - // Last pixel, AA { let d = dist(left_extent, point); - dbg!(d); - let fract = 1.0 - d; let fract = (fract * 255.0) as i32; From 2274c24d6888a06a08babb35864aa1a023d13c22 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 29 Nov 2021 14:19:35 +0000 Subject: [PATCH 047/188] Bresenham thickness, integer --- debug-tools/examples/line-perp.rs | 103 ++++++++++++++++-------------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 00a34b0..b6af334 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -24,15 +24,15 @@ impl LineOffset { match width { width => { match self { - Self::Left => ((width * 2).saturating_sub(1), 0), + Self::Left => (width.saturating_sub(1), 0), Self::Center => { let width = width.saturating_sub(1); // Right-side bias for even width lines. Move mod2 to first item in the // tuple to bias to the left instead. - (width, width + (width % 2)) + (width / 2, width / 2 + (width % 2)) } - Self::Right => ((width * 2).saturating_sub(1), 0), + Self::Right => (width.saturating_sub(1), 0), } } } @@ -106,7 +106,9 @@ fn perpendicular( let dy = dy.abs(); let threshold = dx - 2 * dy; + // E_diag let e_minor = -2 * dx; + // E_square let e_major = 2 * dy; let mut error = einit * sign; @@ -125,44 +127,42 @@ fn perpendicular( let le = LinearEquation::from_line(&left_extent); - // Skip first iteration - if error > threshold { - point += step.major; - error += e_minor; - } - error += e_major; - point += step.minor; + // // Skip first iteration + // if error > threshold { + // point += step.major; + // error += e_minor; + // } + // error += e_major; + // point += step.minor; - // let mut wthr = width as f32 * f32::sqrt(dx.pow(2) as f32 + dy.pow(2) as f32); - // // TODO: init terms - // let mut tk = dx as f32 + dy as f32 - winit as f32; + let wthr = width.pow(2) * (dx.pow(2) + dy.pow(2)); + let mut tk = dx + dy - (winit * sign); // dbg!(wthr); - let mut distance = 0.0; let slope = dy as f32 / dx as f32; - // while tk <= wthr { - loop { - let is_outside = le.check_side(point, side_check_left); + println!("--"); - if is_outside { - break; - } + while tk.pow(2) <= wthr { + // loop { + // let is_outside = le.check_side(point, side_check_left); - Pixel(point, c_left).draw(display)?; + // if is_outside { + // break; + // } - distance += slope; + Pixel(point, c_left).draw(display)?; if error > threshold { point += step.major; error += e_minor; - // tk += 2.0 * dy as f32 + tk += 2 * dy } error += e_major; point += step.minor; - // tk += 2.0 * dx as f32; + tk += 2 * dx; // dbg!(tk); @@ -171,51 +171,58 @@ fn perpendicular( // } } - // Last pixel, AA - { - let d = dist(left_extent, point); + let d1 = dist(_line, point); + let d2 = dist(left_extent, point); - let fract = 1.0 - d; + // // Last pixel, AA + // { + // let d = dist(left_extent, point); - let fract = (fract * 255.0) as i32; + // let fract = 1.0 - d; - // Don't draw any pixels that are too far away from the line - if fract > 0 { - let fract = fract as u32; + // let fract = (fract * 255.0) as i32; - Pixel( - point, - Rgb888::new( - ((fract * c_left.r() as u32) / 255) as u8, - ((fract * c_left.g() as u32) / 255) as u8, - ((fract * c_left.b() as u32) / 255) as u8, - ), - ) - .draw(display)?; - } - } + // // Don't draw any pixels that are too far away from the line + // if fract > 0 { + // let fract = fract as u32; + + // Pixel( + // point, + // Rgb888::new( + // ((fract * c_left.r() as u32) / 255) as u8, + // ((fract * c_left.g() as u32) / 255) as u8, + // ((fract * c_left.b() as u32) / 255) as u8, + // ), + // ) + // .draw(display)?; + // } + // } let mut point = Point::new(x0, y0); let mut error = einit * -sign; let le = LinearEquation::from_line(&right_extent); - loop { - let is_outside = le.check_side(point, side_check_right); + let mut tk = dx + dy + (winit * sign); - if is_outside { - break; - } + while tk.pow(2) <= wthr { + // let is_outside = le.check_side(point, side_check_right); + + // if is_outside { + // break; + // } Pixel(point, c_right).draw(display)?; if error > threshold { point -= step.major; error += e_minor; + tk += 2 * dy; } error += e_major; point -= step.minor; + tk += 2 * dx; } // // Last pixel, AA From 1fe7d9e3ea333836bc62b47a98f49a1c7dbf11c0 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 29 Nov 2021 15:40:42 +0000 Subject: [PATCH 048/188] Lots of floating point but nice result --- debug-tools/examples/line-perp.rs | 94 ++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 20 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index b6af334..a748a47 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -137,6 +137,7 @@ fn perpendicular( let wthr = width.pow(2) * (dx.pow(2) + dy.pow(2)); let mut tk = dx + dy - (winit * sign); + // let mut tk: i32 = 0; // dbg!(wthr); @@ -144,7 +145,14 @@ fn perpendicular( println!("--"); - while tk.pow(2) <= wthr { + let line_len = f32::sqrt(_line.delta().length_squared() as f32); + let le = LinearEquation::from_line(&_line); + let le_left = LinearEquation::from_line(&left_extent); + + // dbg!(line_len); + + // while tk.pow(2) <= wthr { + while le_left.distance(point) <= 0 { // loop { // let is_outside = le.check_side(point, side_check_left); @@ -152,12 +160,48 @@ fn perpendicular( // break; // } - Pixel(point, c_left).draw(display)?; + // Pixel(point, c_left).draw(display)?; + + let dist_from_center = le.distance(point) as f32 / line_len; + + // dbg!(le.distance(point), le_left.distance(point), ass, _width_l); + + // dbg!(_width_l); + + { + // let fract = _width_l as f32 - dist_from_center; + + // dbg!(dist_from_center); + + // let fract = (fract.abs().max(1.0)) * 255.0; + + let fract = if dist_from_center <= _width_l as f32 { + 1.0 + } else if dist_from_center - _width_l as f32 <= 1.0 { + 1.0 - dist_from_center.fract() + } else { + 0.0 + }; + + let fract = fract * 255.0; + + let fract = fract as u32; + + Pixel( + point, + Rgb888::new( + ((fract * c_left.r() as u32) / 255) as u8, + ((fract * c_left.g() as u32) / 255) as u8, + ((fract * c_left.b() as u32) / 255) as u8, + ), + ) + .draw(display)?; + } if error > threshold { point += step.major; error += e_minor; - tk += 2 * dy + tk += 2 * dy; } error += e_major; @@ -171,37 +215,47 @@ fn perpendicular( // } } - let d1 = dist(_line, point); - let d2 = dist(left_extent, point); + // let ass = le.distance(point) as f32 / line_len; + // dbg!(ass); + + // // point += step.minor; + // // point += step.minor; + + // // let accum_dist = f32::sqrt((accum_x.pow(2) + accum_y.pow(2)) as f32); // // Last pixel, AA // { // let d = dist(left_extent, point); - // let fract = 1.0 - d; + // // dbg!(d, dist(_line, point).fract(), (tk as f32).sqrt().fract()); + + // // dbg!(d, dist(_line, point)); + + // let fract = 1.0 - d.min(1.0); + // // let fract = 1.0 - dist(_line, point).fract(); + // let fract = 1.0 - ass.fract(); // let fract = (fract * 255.0) as i32; // // Don't draw any pixels that are too far away from the line - // if fract > 0 { - // let fract = fract as u32; - - // Pixel( - // point, - // Rgb888::new( - // ((fract * c_left.r() as u32) / 255) as u8, - // ((fract * c_left.g() as u32) / 255) as u8, - // ((fract * c_left.b() as u32) / 255) as u8, - // ), - // ) - // .draw(display)?; - // } + // // if fract > 0 { + // let fract = fract as u32; + + // Pixel( + // point, + // Rgb888::new( + // ((fract * c_left.r() as u32) / 255) as u8, + // ((fract * c_left.g() as u32) / 255) as u8, + // ((fract * c_left.b() as u32) / 255) as u8, + // ), + // ) + // .draw(display)?; // } let mut point = Point::new(x0, y0); let mut error = einit * -sign; - let le = LinearEquation::from_line(&right_extent); + // let le = LinearEquation::from_line(&right_extent); let mut tk = dx + dy + (winit * sign); From da252da975151d8ef6aaa3c6241b806e0379c597 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 23 Dec 2021 10:21:25 +0000 Subject: [PATCH 049/188] AA on one side using FP, again --- debug-tools/Cargo.toml | 1 + debug-tools/examples/aa-bres-float.rs | 136 +++++++++++++++ debug-tools/examples/line-perp.rs | 236 ++++++++++++++++++++------ 3 files changed, 322 insertions(+), 51 deletions(-) create mode 100644 debug-tools/examples/aa-bres-float.rs diff --git a/debug-tools/Cargo.toml b/debug-tools/Cargo.toml index 835d903..ffb594a 100644 --- a/debug-tools/Cargo.toml +++ b/debug-tools/Cargo.toml @@ -9,3 +9,4 @@ publish = false framework = { path = "../framework" } embedded-graphics = { version = "0.7.1", path = "../../embedded-graphics" } embedded-graphics-simulator = { version = "0.3.0", path = "../../simulator" } +integer-sqrt = "0.1.5" diff --git a/debug-tools/examples/aa-bres-float.rs b/debug-tools/examples/aa-bres-float.rs new file mode 100644 index 0000000..bf40506 --- /dev/null +++ b/debug-tools/examples/aa-bres-float.rs @@ -0,0 +1,136 @@ +//! Render a 1px wide antialiased line using error components and a 255 multiplier. +//! +//! Inspiration from + +use core::convert::TryFrom; +use embedded_graphics::{ + mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; + +fn thick_line( + display: &mut impl DrawTarget, + line: Line, + _width: i32, +) -> Result<(), std::convert::Infallible> { + let skele_color = Rgb888::MAGENTA; + + let delta = line.delta(); + + let dx = delta.x; + let dy = delta.y; + + let mut point = line.start; + let mut error: f32 = 0.0; + let slope = dy as f32 / dx as f32; + + println!("---"); + + for _i in 0..=dx { + // // AA point above + let bright = ((0.5 - error).abs() * 256.0) as u32; + let c = Rgb888::new( + ((bright * skele_color.r() as u32) / 255) as u8, + ((bright * skele_color.g() as u32) / 255) as u8, + ((bright * skele_color.b() as u32) / 255) as u8, + ); + Pixel(Point::new(point.x, point.y - 1), c).draw(display)?; + + // // AA point below + // let bright = (255 - br) as u32; + // let c = Rgb888::new( + // ((bright * skele_color.r() as u32) / 255) as u8, + // ((bright * skele_color.g() as u32) / 255) as u8, + // ((bright * skele_color.b() as u32) / 255) as u8, + // ); + // Pixel(Point::new(point.x, point.y + 1), c).draw(display)?; + + // Line skeleton + let bright = 255; + let c = Rgb888::new( + ((bright * skele_color.r() as u32) / 255) as u8, + ((bright * skele_color.g() as u32) / 255) as u8, + ((bright * skele_color.b() as u32) / 255) as u8, + ); + Pixel(Point::new(point.x, point.y), c).draw(display)?; + + dbg!(error); + + if error > 0.5 { + point.y += 1; + error -= 1.0; + } + + error += slope; + point.x += 1; + } + + Ok(()) +} + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, +} + +impl App for LineDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(200, 200); + // const DISPLAY_SIZE: Size = Size::new(64, 64); + + fn new() -> Self { + let end = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); + Self { + start: end + Point::new(10, 15), + end, + // end: start + Point::new(100, 0), + stroke_width: 10, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let Point { x: _x0, y: _y0 } = self.start; + + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; + + let _mock_display: MockDisplay = MockDisplay::new(); + + thick_line(display, Line::new(self.start, self.end), width)?; + + // let l = Line::new(self.start, self.end); + + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + // l.perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(5).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index a748a47..ca40806 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -11,6 +11,7 @@ use embedded_graphics::{ }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; +use integer_sqrt::IntegerSquareRoot; #[derive(Debug, Copy, Clone, PartialEq)] enum LineOffset { @@ -72,9 +73,38 @@ fn dist(line: Line, point: Point) -> f32 { dist } +/// Like `dist` but result is multiplied by 255. +fn dist_255(line: Line, point: Point) -> u32 { + let Line { + start: Point { x: x1, y: y1 }, + end: Point { x: x2, y: y2 }, + } = line; + let Point { x: x3, y: y3 } = point; + + let x1 = x1 * 255; + let y1 = y1 * 255; + let x2 = x2 * 255; + let y2 = y2 * 255; + let x3 = x3 * 255; + let y3 = y3 * 255; + + let delta = line.end - line.start; + + let denom = delta.x.pow(2) + delta.y.pow(2); + + let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) / denom; + + let x = x1 + u * (x2 - x1); + let y = y1 + u * (y2 - y1); + + let dist = u32::integer_sqrt(&((x - x3).pow(2) as u32 + (y - y3).pow(2) as u32)); + + dist +} + fn perpendicular( display: &mut impl DrawTarget, - _line: Line, + line: Line, (left_extent, right_extent): (Line, Line), x0: i32, y0: i32, @@ -135,7 +165,8 @@ fn perpendicular( // error += e_major; // point += step.minor; - let wthr = width.pow(2) * (dx.pow(2) + dy.pow(2)); + // Add one to width so we get an extra iteration for the AA edge + let wthr = (width + 1).pow(2) * (dx.pow(2) + dy.pow(2)); let mut tk = dx + dy - (winit * sign); // let mut tk: i32 = 0; @@ -143,76 +174,179 @@ fn perpendicular( let slope = dy as f32 / dx as f32; - println!("--"); + println!("==="); - let line_len = f32::sqrt(_line.delta().length_squared() as f32); - let le = LinearEquation::from_line(&_line); + let line_len_sq = line.delta().length_squared() as f32; + let line_len = f32::sqrt(line_len_sq); + let le = LinearEquation::from_line(&line); let le_left = LinearEquation::from_line(&left_extent); - // dbg!(line_len); + while tk.pow(2) <= wthr { + let distance = dist(line, point); - // while tk.pow(2) <= wthr { - while le_left.distance(point) <= 0 { - // loop { - // let is_outside = le.check_side(point, side_check_left); + dbg!(distance); - // if is_outside { - // break; - // } + let fract = if (distance.floor() as i32) < _width_l { + 255 + } else { + 255 - (distance.fract() * 255.0).round() as u32 + }; - // Pixel(point, c_left).draw(display)?; + dbg!(fract); - let dist_from_center = le.distance(point) as f32 / line_len; + let c = Rgb888::new( + ((fract * c_left.r() as u32) / 255) as u8, + ((fract * c_left.g() as u32) / 255) as u8, + ((fract * c_left.b() as u32) / 255) as u8, + ); - // dbg!(le.distance(point), le_left.distance(point), ass, _width_l); + Pixel(point, c).draw(display)?; - // dbg!(_width_l); + if error > threshold { + point += step.major; + error += e_minor; + tk += 2 * dy; + } - { - // let fract = _width_l as f32 - dist_from_center; + error += e_major; + point += step.minor; + tk += 2 * dx; + } - // dbg!(dist_from_center); + // Iterating along left extent and setting brightness based on distance to center + // { + // let Line { start, end } = left_extent; - // let fract = (fract.abs().max(1.0)) * 255.0; + // let (delta, step) = { + // let delta = end - start; - let fract = if dist_from_center <= _width_l as f32 { - 1.0 - } else if dist_from_center - _width_l as f32 <= 1.0 { - 1.0 - dist_from_center.fract() - } else { - 0.0 - }; + // let direction = Point::new( + // if delta.x >= 0 { 1 } else { -1 }, + // if delta.y >= 0 { 1 } else { -1 }, + // ); - let fract = fract * 255.0; + // // Determine major and minor directions. + // if delta.y.abs() >= delta.x.abs() { + // ( + // MajorMinor::new(delta.y, delta.x), + // MajorMinor::new(direction.y_axis(), direction.x_axis()), + // ) + // } else { + // ( + // MajorMinor::new(delta.x, delta.y), + // MajorMinor::new(direction.x_axis(), direction.y_axis()), + // ) + // } + // }; - let fract = fract as u32; + // let mut error = 0; + // let mut point = start; - Pixel( - point, - Rgb888::new( - ((fract * c_left.r() as u32) / 255) as u8, - ((fract * c_left.g() as u32) / 255) as u8, - ((fract * c_left.b() as u32) / 255) as u8, - ), - ) - .draw(display)?; - } + // let dx = delta.major.abs(); + // let dy = delta.minor.abs(); + + // let threshold = dx - 2 * dy; + // let e_minor = -2 * dx; + // let e_major = 2 * dy; + // let length = dx + 1; + + // let mut bright: u32 = 255; + // let mut bright_step = 10; + + // for _i in 0..length { + // let fract: u32 = 255; + + // // let fract = bright; + + // let distance = dist(line, point); + + // dbg!(distance); + + // let fract = if (distance.floor() as i32) < _width_l { + // 255 + // } else { + // 255 - (distance.fract() * 255.0).round() as u32 + // }; + + // // let fract: u32 = distance.abs() as u32; + + // dbg!(fract); + + // let c = Rgb888::new( + // ((fract * c_left.r() as u32) / 255) as u8, + // ((fract * c_left.g() as u32) / 255) as u8, + // ((fract * c_left.b() as u32) / 255) as u8, + // ); + + // Pixel(point, c).draw(display)?; + + // if error > threshold { + // point += step.minor; + // error += e_minor; + // bright = 255; + // } + + // error += e_major; + // point += step.major; + // bright = bright.saturating_sub(bright_step); + // } + // } + + // for point in left_extent.points() { + // let dist = le_left.distance(point) as f32 / line_len; + // // let dist_scaled = 1.0; + // let dist_scaled = dist.abs(); + + // dbg!(dist, dist_scaled); + + // let fract = (dist_scaled * 255.0) as u32; + + // let c = Rgb888::new( + // ((fract * c_left.r() as u32) / 255) as u8, + // ((fract * c_left.g() as u32) / 255) as u8, + // ((fract * c_left.b() as u32) / 255) as u8, + // ); + + // Pixel(point, c).draw(display)?; + // } + + // dbg!(line_len); + + let mut x_accum: u32 = 0; + let mut y_accum: u32 = 0; + + while tk.pow(2) <= wthr { + // println!("--"); + + // Pixel(point, c_left).draw(display)?; + + // { + // let fract = 255; + + // let fract = fract as u32; + + // Pixel( + // point, + // Rgb888::new( + // ((fract * c_left.r() as u32) / 255) as u8, + // ((fract * c_left.g() as u32) / 255) as u8, + // ((fract * c_left.b() as u32) / 255) as u8, + // ), + // ) + // .draw(display)?; + // } if error > threshold { point += step.major; error += e_minor; tk += 2 * dy; + y_accum += 1; } error += e_major; point += step.minor; tk += 2 * dx; - - // dbg!(tk); - - // if tk > wthr { - // break; - // } + x_accum += 1; } // let ass = le.distance(point) as f32 / line_len; @@ -227,12 +361,12 @@ fn perpendicular( // { // let d = dist(left_extent, point); - // // dbg!(d, dist(_line, point).fract(), (tk as f32).sqrt().fract()); + // // dbg!(d, dist(line, point).fract(), (tk as f32).sqrt().fract()); - // // dbg!(d, dist(_line, point)); + // // dbg!(d, dist(line, point)); // let fract = 1.0 - d.min(1.0); - // // let fract = 1.0 - dist(_line, point).fract(); + // // let fract = 1.0 - dist(line, point).fract(); // let fract = 1.0 - ass.fract(); // let fract = (fract * 255.0) as i32; @@ -306,7 +440,7 @@ fn perpendicular( Ok(()) } -fn thick_line( +fn thickline( display: &mut impl DrawTarget, line: Line, width: i32, @@ -460,7 +594,7 @@ impl App for LineDebug { let _mock_display: MockDisplay = MockDisplay::new(); - thick_line(display, Line::new(self.start, self.end), width)?; + thickline(display, Line::new(self.start, self.end), width)?; // let l = Line::new(self.start, self.end); From ffbc51c2c657d1cf7bc411ac20c8d62e86c1b101 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 23 Dec 2021 10:37:29 +0000 Subject: [PATCH 050/188] Distance calculation with linear equation and integer sqrt --- debug-tools/examples/line-perp.rs | 55 +++++++++++++++++-------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index ca40806..9b93f31 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -75,31 +75,40 @@ fn dist(line: Line, point: Point) -> f32 { /// Like `dist` but result is multiplied by 255. fn dist_255(line: Line, point: Point) -> u32 { - let Line { - start: Point { x: x1, y: y1 }, - end: Point { x: x2, y: y2 }, - } = line; - let Point { x: x3, y: y3 } = point; - - let x1 = x1 * 255; - let y1 = y1 * 255; - let x2 = x2 * 255; - let y2 = y2 * 255; - let x3 = x3 * 255; - let y3 = y3 * 255; + // let Line { + // start: Point { x: x1, y: y1 }, + // end: Point { x: x2, y: y2 }, + // } = line; + // let Point { x: x3, y: y3 } = point; let delta = line.end - line.start; - let denom = delta.x.pow(2) + delta.y.pow(2); + let le = LinearEquation::from_line(&line); + let le_dist = le.distance(point).abs() as u32; + let len = delta.length_squared() as u32; + let le_dist = le_dist * 255 / u32::integer_sqrt(&len); + + le_dist - let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) / denom; + // let x1 = x1 * 255; + // let y1 = y1 * 255; + // let x2 = x2 * 255; + // let y2 = y2 * 255; + // let x3 = x3 * 255; + // let y3 = y3 * 255; - let x = x1 + u * (x2 - x1); - let y = y1 + u * (y2 - y1); + // let delta = line.end - line.start; - let dist = u32::integer_sqrt(&((x - x3).pow(2) as u32 + (y - y3).pow(2) as u32)); + // let denom = delta.x.pow(2) + delta.y.pow(2); - dist + // let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) / denom; + + // let x = x1 + u * (x2 - x1); + // let y = y1 + u * (y2 - y1); + + // let dist = u32::integer_sqrt(&((x - x3).pow(2) as u32 + (y - y3).pow(2) as u32)); + + // dist } fn perpendicular( @@ -182,18 +191,14 @@ fn perpendicular( let le_left = LinearEquation::from_line(&left_extent); while tk.pow(2) <= wthr { - let distance = dist(line, point); - - dbg!(distance); + let distance = dist_255(line, point); - let fract = if (distance.floor() as i32) < _width_l { + let fract = if distance < _width_l as u32 * 255 { 255 } else { - 255 - (distance.fract() * 255.0).round() as u32 + 255 - distance % 255 }; - dbg!(fract); - let c = Rgb888::new( ((fract * c_left.r() as u32) / 255) as u8, ((fract * c_left.g() as u32) / 255) as u8, From bae80807652aeb5552bcbd16b3716ff7696d8d7c Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 23 Dec 2021 11:41:05 +0000 Subject: [PATCH 051/188] Use f32 distance calcs to fix innaccuracies --- debug-tools/examples/line-perp.rs | 46 +++++++++++-------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 9b93f31..f02c98f 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -6,7 +6,7 @@ use embedded_graphics::{ primitives::{ common::{LineSide, LinearEquation}, line::StrokeOffset, - Line, + Line, PrimitiveStyle, }, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; @@ -181,22 +181,18 @@ fn perpendicular( // dbg!(wthr); - let slope = dy as f32 / dx as f32; - println!("==="); - let line_len_sq = line.delta().length_squared() as f32; - let line_len = f32::sqrt(line_len_sq); - let le = LinearEquation::from_line(&line); - let le_left = LinearEquation::from_line(&left_extent); - + // Perpendicular iteration while tk.pow(2) <= wthr { - let distance = dist_255(line, point); + // NOTE: dist_255 has numerical innaccuraccies with very short lines + // let distance = dist_255(line, point); + let distance = dist(line, point) * 255.0; - let fract = if distance < _width_l as u32 * 255 { + let fract = if distance < _width_l as f32 * 255.0 { 255 } else { - 255 - distance % 255 + 255 - (distance % 255.0) as u32 }; let c = Rgb888::new( @@ -218,7 +214,7 @@ fn perpendicular( tk += 2 * dx; } - // Iterating along left extent and setting brightness based on distance to center + // Iterating along left extent and setting brightness based on gradient // { // let Line { start, end } = left_extent; @@ -255,27 +251,19 @@ fn perpendicular( // let e_major = 2 * dy; // let length = dx + 1; - // let mut bright: u32 = 255; - // let mut bright_step = 10; + // let mut bright = 1.0f32; + // let gradient = dy as f32 / dx as f32; // for _i in 0..length { - // let fract: u32 = 255; - - // // let fract = bright; + // // let fract: u32 = 255; - // let distance = dist(line, point); + // let fract = (255.0 * bright) as u32; - // dbg!(distance); + // bright -= gradient; - // let fract = if (distance.floor() as i32) < _width_l { - // 255 - // } else { - // 255 - (distance.fract() * 255.0).round() as u32 - // }; - - // // let fract: u32 = distance.abs() as u32; - - // dbg!(fract); + // if bright < 0.0 { + // bright = 1.0; + // } // let c = Rgb888::new( // ((fract * c_left.r() as u32) / 255) as u8, @@ -288,12 +276,10 @@ fn perpendicular( // if error > threshold { // point += step.minor; // error += e_minor; - // bright = 255; // } // error += e_major; // point += step.major; - // bright = bright.saturating_sub(bright_step); // } // } From e0317f6cb28f6c8490f6f28b4fa08e9d930501b2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 23 Dec 2021 20:48:55 +0000 Subject: [PATCH 052/188] Big cleanup --- debug-tools/examples/line-perp.rs | 209 +----------------------------- 1 file changed, 1 insertion(+), 208 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index f02c98f..62c36ba 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -162,18 +162,6 @@ fn perpendicular( (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) }; - // let (c_left, c_right) = (Rgb888::GREEN, Rgb888::GREEN); - - let le = LinearEquation::from_line(&left_extent); - - // // Skip first iteration - // if error > threshold { - // point += step.major; - // error += e_minor; - // } - // error += e_major; - // point += step.minor; - // Add one to width so we get an extra iteration for the AA edge let wthr = (width + 1).pow(2) * (dx.pow(2) + dy.pow(2)); let mut tk = dx + dy - (winit * sign); @@ -181,7 +169,7 @@ fn perpendicular( // dbg!(wthr); - println!("==="); + // println!("==="); // Perpendicular iteration while tk.pow(2) <= wthr { @@ -214,183 +202,12 @@ fn perpendicular( tk += 2 * dx; } - // Iterating along left extent and setting brightness based on gradient - // { - // let Line { start, end } = left_extent; - - // let (delta, step) = { - // let delta = end - start; - - // let direction = Point::new( - // if delta.x >= 0 { 1 } else { -1 }, - // if delta.y >= 0 { 1 } else { -1 }, - // ); - - // // Determine major and minor directions. - // if delta.y.abs() >= delta.x.abs() { - // ( - // MajorMinor::new(delta.y, delta.x), - // MajorMinor::new(direction.y_axis(), direction.x_axis()), - // ) - // } else { - // ( - // MajorMinor::new(delta.x, delta.y), - // MajorMinor::new(direction.x_axis(), direction.y_axis()), - // ) - // } - // }; - - // let mut error = 0; - // let mut point = start; - - // let dx = delta.major.abs(); - // let dy = delta.minor.abs(); - - // let threshold = dx - 2 * dy; - // let e_minor = -2 * dx; - // let e_major = 2 * dy; - // let length = dx + 1; - - // let mut bright = 1.0f32; - // let gradient = dy as f32 / dx as f32; - - // for _i in 0..length { - // // let fract: u32 = 255; - - // let fract = (255.0 * bright) as u32; - - // bright -= gradient; - - // if bright < 0.0 { - // bright = 1.0; - // } - - // let c = Rgb888::new( - // ((fract * c_left.r() as u32) / 255) as u8, - // ((fract * c_left.g() as u32) / 255) as u8, - // ((fract * c_left.b() as u32) / 255) as u8, - // ); - - // Pixel(point, c).draw(display)?; - - // if error > threshold { - // point += step.minor; - // error += e_minor; - // } - - // error += e_major; - // point += step.major; - // } - // } - - // for point in left_extent.points() { - // let dist = le_left.distance(point) as f32 / line_len; - // // let dist_scaled = 1.0; - // let dist_scaled = dist.abs(); - - // dbg!(dist, dist_scaled); - - // let fract = (dist_scaled * 255.0) as u32; - - // let c = Rgb888::new( - // ((fract * c_left.r() as u32) / 255) as u8, - // ((fract * c_left.g() as u32) / 255) as u8, - // ((fract * c_left.b() as u32) / 255) as u8, - // ); - - // Pixel(point, c).draw(display)?; - // } - - // dbg!(line_len); - - let mut x_accum: u32 = 0; - let mut y_accum: u32 = 0; - - while tk.pow(2) <= wthr { - // println!("--"); - - // Pixel(point, c_left).draw(display)?; - - // { - // let fract = 255; - - // let fract = fract as u32; - - // Pixel( - // point, - // Rgb888::new( - // ((fract * c_left.r() as u32) / 255) as u8, - // ((fract * c_left.g() as u32) / 255) as u8, - // ((fract * c_left.b() as u32) / 255) as u8, - // ), - // ) - // .draw(display)?; - // } - - if error > threshold { - point += step.major; - error += e_minor; - tk += 2 * dy; - y_accum += 1; - } - - error += e_major; - point += step.minor; - tk += 2 * dx; - x_accum += 1; - } - - // let ass = le.distance(point) as f32 / line_len; - // dbg!(ass); - - // // point += step.minor; - // // point += step.minor; - - // // let accum_dist = f32::sqrt((accum_x.pow(2) + accum_y.pow(2)) as f32); - - // // Last pixel, AA - // { - // let d = dist(left_extent, point); - - // // dbg!(d, dist(line, point).fract(), (tk as f32).sqrt().fract()); - - // // dbg!(d, dist(line, point)); - - // let fract = 1.0 - d.min(1.0); - // // let fract = 1.0 - dist(line, point).fract(); - // let fract = 1.0 - ass.fract(); - - // let fract = (fract * 255.0) as i32; - - // // Don't draw any pixels that are too far away from the line - // // if fract > 0 { - // let fract = fract as u32; - - // Pixel( - // point, - // Rgb888::new( - // ((fract * c_left.r() as u32) / 255) as u8, - // ((fract * c_left.g() as u32) / 255) as u8, - // ((fract * c_left.b() as u32) / 255) as u8, - // ), - // ) - // .draw(display)?; - // } - let mut point = Point::new(x0, y0); let mut error = einit * -sign; - // let le = LinearEquation::from_line(&right_extent); - let mut tk = dx + dy + (winit * sign); while tk.pow(2) <= wthr { - // let is_outside = le.check_side(point, side_check_right); - - // if is_outside { - // break; - // } - Pixel(point, c_right).draw(display)?; if error > threshold { @@ -404,30 +221,6 @@ fn perpendicular( tk += 2 * dx; } - // // Last pixel, AA - // { - // let d = dist(right_extent, point); - - // let fract = 1.0 - d; - - // let fract = (fract * 255.0) as i32; - - // // Don't draw any pixels that are too far away from the line - // if fract > 0 { - // let fract = fract as u32; - - // Pixel( - // point, - // Rgb888::new( - // ((fract * c_right.r() as u32) / 255) as u8, - // ((fract * c_right.g() as u32) / 255) as u8, - // ((fract * c_right.b() as u32) / 255) as u8, - // ), - // ) - // .draw(display)?; - // } - // } - Ok(()) } From 765ee057058be68a99c89d3a76fbaa39763c9c7d Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 24 Dec 2021 12:34:30 +0000 Subject: [PATCH 053/188] Optimise distance algo --- debug-tools/examples/line-perp.rs | 46 ++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 62c36ba..d32d75c 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -52,25 +52,45 @@ impl MajorMinor { } } -fn dist(line: Line, point: Point) -> f32 { - let Line { - start: Point { x: x1, y: y1 }, - end: Point { x: x2, y: y2 }, - } = line; - let Point { x: x3, y: y3 } = point; +// fn dist(line: Line, point: Point) -> f32 { +// let Line { +// start: Point { x: x1, y: y1 }, +// end: Point { x: x2, y: y2 }, +// } = line; +// let Point { x: x3, y: y3 } = point; - let delta = line.end - line.start; +// let delta = line.end - line.start; + +// let denom = (delta.x.pow(2) + delta.y.pow(2)) as f32; + +// let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / denom; + +// let x = x1 as f32 + u * (x2 - x1) as f32; +// let y = y1 as f32 + u * (y2 - y1) as f32; + +// let dist = f32::sqrt((x - x3 as f32).powi(2) + (y - y3 as f32).powi(2)); + +// dist +// } + +// From , linked from +fn dist(line: Line, point: Point) -> f32 { + let Line { start, .. } = line; - let denom = (delta.x.pow(2) + delta.y.pow(2)) as f32; + let Point { + x: point_x, + y: point_y, + } = point; - let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / denom; + let point_x = point_x as f32; + let point_y = point_y as f32; - let x = x1 as f32 + u * (x2 - x1) as f32; - let y = y1 as f32 + u * (y2 - y1) as f32; + let delta = line.delta(); - let dist = f32::sqrt((x - x3 as f32).powi(2) + (y - y3 as f32).powi(2)); + let slope = delta.y as f32 / delta.x as f32; + let intercept = start.y as f32 - (slope * start.x as f32); - dist + f32::abs(slope * point_x - point_y + intercept) / f32::sqrt(slope.powi(2) + 1.0) } /// Like `dist` but result is multiplied by 255. From 2b8c3625daf541b18698108cbf4f24b4c385d01c Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 26 Dec 2021 13:46:34 +0000 Subject: [PATCH 054/188] AA without using distance function with sqrt --- debug-tools/examples/line-perp.rs | 110 +++++++++--------------------- 1 file changed, 34 insertions(+), 76 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index d32d75c..cc43d7c 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -52,28 +52,7 @@ impl MajorMinor { } } -// fn dist(line: Line, point: Point) -> f32 { -// let Line { -// start: Point { x: x1, y: y1 }, -// end: Point { x: x2, y: y2 }, -// } = line; -// let Point { x: x3, y: y3 } = point; - -// let delta = line.end - line.start; - -// let denom = (delta.x.pow(2) + delta.y.pow(2)) as f32; - -// let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / denom; - -// let x = x1 as f32 + u * (x2 - x1) as f32; -// let y = y1 as f32 + u * (y2 - y1) as f32; - -// let dist = f32::sqrt((x - x3 as f32).powi(2) + (y - y3 as f32).powi(2)); - -// dist -// } - -// From , linked from +// From , linked from fn dist(line: Line, point: Point) -> f32 { let Line { start, .. } = line; @@ -93,44 +72,6 @@ fn dist(line: Line, point: Point) -> f32 { f32::abs(slope * point_x - point_y + intercept) / f32::sqrt(slope.powi(2) + 1.0) } -/// Like `dist` but result is multiplied by 255. -fn dist_255(line: Line, point: Point) -> u32 { - // let Line { - // start: Point { x: x1, y: y1 }, - // end: Point { x: x2, y: y2 }, - // } = line; - // let Point { x: x3, y: y3 } = point; - - let delta = line.end - line.start; - - let le = LinearEquation::from_line(&line); - let le_dist = le.distance(point).abs() as u32; - let len = delta.length_squared() as u32; - let le_dist = le_dist * 255 / u32::integer_sqrt(&len); - - le_dist - - // let x1 = x1 * 255; - // let y1 = y1 * 255; - // let x2 = x2 * 255; - // let y2 = y2 * 255; - // let x3 = x3 * 255; - // let y3 = y3 * 255; - - // let delta = line.end - line.start; - - // let denom = delta.x.pow(2) + delta.y.pow(2); - - // let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) / denom; - - // let x = x1 + u * (x2 - x1); - // let y = y1 + u * (y2 - y1); - - // let dist = u32::integer_sqrt(&((x - x3).pow(2) as u32 + (y - y3).pow(2) as u32)); - - // dist -} - fn perpendicular( display: &mut impl DrawTarget, line: Line, @@ -184,32 +125,31 @@ fn perpendicular( // Add one to width so we get an extra iteration for the AA edge let wthr = (width + 1).pow(2) * (dx.pow(2) + dy.pow(2)); - let mut tk = dx + dy - (winit * sign); + let init_offset = dx + dy - (winit * sign); + let mut tk = init_offset; // let mut tk: i32 = 0; // dbg!(wthr); - // println!("==="); + println!("==="); // Perpendicular iteration while tk.pow(2) <= wthr { - // NOTE: dist_255 has numerical innaccuraccies with very short lines - // let distance = dist_255(line, point); - let distance = dist(line, point) * 255.0; + // let distance = dist(line, point) * 255.0; - let fract = if distance < _width_l as f32 * 255.0 { - 255 - } else { - 255 - (distance % 255.0) as u32 - }; + // let fract = if distance < _width_l as f32 * 255.0 { + // 255 + // } else { + // 255 - (distance % 255.0) as u32 + // }; - let c = Rgb888::new( - ((fract * c_left.r() as u32) / 255) as u8, - ((fract * c_left.g() as u32) / 255) as u8, - ((fract * c_left.b() as u32) / 255) as u8, - ); + // let c = Rgb888::new( + // ((fract * c_left.r() as u32) / 255) as u8, + // ((fract * c_left.g() as u32) / 255) as u8, + // ((fract * c_left.b() as u32) / 255) as u8, + // ); - Pixel(point, c).draw(display)?; + Pixel(point, c_left).draw(display)?; if error > threshold { point += step.major; @@ -220,6 +160,24 @@ fn perpendicular( error += e_major; point += step.minor; tk += 2 * dx; + + if tk.pow(2) > wthr { + // dbg!(tk, wthr, tk.pow(2) as f32 / wthr as f32); + let fract = tk.pow(2) as f32 / (wthr / 2) as f32; + + // dbg!(fract); + let fract = fract.min(2.99999); + + let fract = 255 - (fract.fract() * 255.0) as u32; + + let c = Rgb888::new( + ((fract * c_left.r() as u32) / 255) as u8, + ((fract * c_left.g() as u32) / 255) as u8, + ((fract * c_left.b() as u32) / 255) as u8, + ); + + Pixel(point, c).draw(display)?; + } } let mut point = Point::new(x0, y0); From 0181f6a3a7e7126e92380e05f9afb5cadd14cf6a Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 26 Dec 2021 14:00:42 +0000 Subject: [PATCH 055/188] Integer antialiasing --- debug-tools/examples/line-perp.rs | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index cc43d7c..9b72358 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -131,24 +131,10 @@ fn perpendicular( // dbg!(wthr); - println!("==="); + // println!("==="); // Perpendicular iteration while tk.pow(2) <= wthr { - // let distance = dist(line, point) * 255.0; - - // let fract = if distance < _width_l as f32 * 255.0 { - // 255 - // } else { - // 255 - (distance % 255.0) as u32 - // }; - - // let c = Rgb888::new( - // ((fract * c_left.r() as u32) / 255) as u8, - // ((fract * c_left.g() as u32) / 255) as u8, - // ((fract * c_left.b() as u32) / 255) as u8, - // ); - Pixel(point, c_left).draw(display)?; if error > threshold { @@ -162,13 +148,9 @@ fn perpendicular( tk += 2 * dx; if tk.pow(2) > wthr { - // dbg!(tk, wthr, tk.pow(2) as f32 / wthr as f32); - let fract = tk.pow(2) as f32 / (wthr / 2) as f32; - - // dbg!(fract); - let fract = fract.min(2.99999); - - let fract = 255 - (fract.fract() * 255.0) as u32; + let fract = tk.pow(2) * 255 / (wthr / 2); + let fract = fract.min(255 * 3 - 1); + let fract = 255 - (fract % 255) as u32; let c = Rgb888::new( ((fract * c_left.r() as u32) / 255) as u8, From a7693dea7fb8466b19d31c74e5e3283e963ddea2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 27 Dec 2021 11:16:37 +0000 Subject: [PATCH 056/188] Broken but consistent edge independent of thickness --- debug-tools/examples/line-perp.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 9b72358..e05fff0 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -131,7 +131,7 @@ fn perpendicular( // dbg!(wthr); - // println!("==="); + println!("==="); // Perpendicular iteration while tk.pow(2) <= wthr { @@ -148,8 +148,12 @@ fn perpendicular( tk += 2 * dx; if tk.pow(2) > wthr { - let fract = tk.pow(2) * 255 / (wthr / 2); - let fract = fract.min(255 * 3 - 1); + let fract = tk.pow(2) as u32 * 255 / (wthr as u32 / width as u32); + + dbg!(fract); + // FIXME: Make it work with different widths. 10px wide lines only just happen to look + // right. + // let fract = fract.min(255 * 3 - 1); let fract = 255 - (fract % 255) as u32; let c = Rgb888::new( From 66f99ec6b999e8f90e1fb79b27cf6bc52c893ea7 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 27 Dec 2021 11:39:23 +0000 Subject: [PATCH 057/188] Back to fractions, kiiiinda working independent of thickness though The AA drops off too quickly, still leaving some jaggies around --- debug-tools/examples/line-perp.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index e05fff0..9fe07b3 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -148,13 +148,22 @@ fn perpendicular( tk += 2 * dx; if tk.pow(2) > wthr { - let fract = tk.pow(2) as u32 * 255 / (wthr as u32 / width as u32); + let fract = tk.pow(2) as u32 * 255 / (wthr as u32); - dbg!(fract); + let fract = { + let thickness_ratio = tk.pow(2) as f32 / (wthr as f32); + // let slope = dy as f32 / dx as f32; + + // dbg!(thickness_ratio); + + (255.0 - thickness_ratio.fract() * 255.0 * _width_l as f32) as u32 + }; // FIXME: Make it work with different widths. 10px wide lines only just happen to look // right. // let fract = fract.min(255 * 3 - 1); - let fract = 255 - (fract % 255) as u32; + // let fract = 255 - (fract % 255) as u32; + + // let fract = 255; let c = Rgb888::new( ((fract * c_left.r() as u32) / 255) as u8, From ca4aa8c022531cfedce8c98796fb9e0bcf9ab667 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 27 Dec 2021 12:20:04 +0000 Subject: [PATCH 058/188] Looks like (63, 91) from prev screenshot but... WHY 1.5??? --- debug-tools/examples/line-perp.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 9fe07b3..76805ea 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -122,6 +122,7 @@ fn perpendicular( } else { (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) }; + let c_left = Rgb888::WHITE; // Add one to width so we get an extra iteration for the AA edge let wthr = (width + 1).pow(2) * (dx.pow(2) + dy.pow(2)); @@ -152,11 +153,17 @@ fn perpendicular( let fract = { let thickness_ratio = tk.pow(2) as f32 / (wthr as f32); - // let slope = dy as f32 / dx as f32; + // let thickness_ratio = (fract % 255) as f32; - // dbg!(thickness_ratio); + // dbg!(fract, wthr * 255, wthr / (tk.pow(2) - wthr)); - (255.0 - thickness_ratio.fract() * 255.0 * _width_l as f32) as u32 + // let ass = wthr / (tk.pow(2) - wthr); + // dbg!(ass, thickness_ratio.fract() * 255.0); + + // dbg!(thickness_ratio, dx, dy); + + (255.0 - thickness_ratio.fract() * 255.0 * _width_l as f32 / 1.5) as u32 + // (255.0 - thickness_ratio * _width_l as f32 / 1.5) as u32 }; // FIXME: Make it work with different widths. 10px wide lines only just happen to look // right. From da41e92e6ee5238a38a1c717e6027489c0ef2eb4 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 27 Dec 2021 12:29:10 +0000 Subject: [PATCH 059/188] Pretty much working? --- debug-tools/examples/line-perp.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 76805ea..39951ea 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -123,6 +123,7 @@ fn perpendicular( (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) }; let c_left = Rgb888::WHITE; + let c_right = c_left; // Add one to width so we get an extra iteration for the AA edge let wthr = (width + 1).pow(2) * (dx.pow(2) + dy.pow(2)); @@ -152,8 +153,21 @@ fn perpendicular( let fract = tk.pow(2) as u32 * 255 / (wthr as u32); let fract = { - let thickness_ratio = tk.pow(2) as f32 / (wthr as f32); + // There's this weird division of 1.5 to make the AA look correct. This magic value + // is the 8 bit scaler 255 / 1.5. I haven't got to the bottom of why it must be 1.5 + // yet. Maybe something to do with Bresenham's errors being at most 0.5 away from + // pixel centers and everything being multiplied by 2? + let two_thirds_255 = 170.0; + + // let thickness_ratio = tk.pow(2) as f32 / (wthr as f32); + let thickness_ratio = (tk.pow(2) as f32 * two_thirds_255) / (wthr as f32); + let thickness_ratio = thickness_ratio % two_thirds_255; // let thickness_ratio = (fract % 255) as f32; + // dbg!( + // fract, + // thickness_ratio * 255.0, + // (tk.pow(2) as f32 * 255.0) / (wthr as f32) + // ); // dbg!(fract, wthr * 255, wthr / (tk.pow(2) - wthr)); @@ -162,7 +176,7 @@ fn perpendicular( // dbg!(thickness_ratio, dx, dy); - (255.0 - thickness_ratio.fract() * 255.0 * _width_l as f32 / 1.5) as u32 + (255.0 - thickness_ratio * _width_l as f32) as u32 // (255.0 - thickness_ratio * _width_l as f32 / 1.5) as u32 }; // FIXME: Make it work with different widths. 10px wide lines only just happen to look From c82d920e7f4236c84739cbdbf3df5802e6a36175 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 28 Dec 2021 13:37:50 +0000 Subject: [PATCH 060/188] Cleanup --- debug-tools/examples/line-perp.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index 39951ea..aaafa3c 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -159,32 +159,11 @@ fn perpendicular( // pixel centers and everything being multiplied by 2? let two_thirds_255 = 170.0; - // let thickness_ratio = tk.pow(2) as f32 / (wthr as f32); let thickness_ratio = (tk.pow(2) as f32 * two_thirds_255) / (wthr as f32); let thickness_ratio = thickness_ratio % two_thirds_255; - // let thickness_ratio = (fract % 255) as f32; - // dbg!( - // fract, - // thickness_ratio * 255.0, - // (tk.pow(2) as f32 * 255.0) / (wthr as f32) - // ); - - // dbg!(fract, wthr * 255, wthr / (tk.pow(2) - wthr)); - - // let ass = wthr / (tk.pow(2) - wthr); - // dbg!(ass, thickness_ratio.fract() * 255.0); - - // dbg!(thickness_ratio, dx, dy); (255.0 - thickness_ratio * _width_l as f32) as u32 - // (255.0 - thickness_ratio * _width_l as f32 / 1.5) as u32 }; - // FIXME: Make it work with different widths. 10px wide lines only just happen to look - // right. - // let fract = fract.min(255 * 3 - 1); - // let fract = 255 - (fract % 255) as u32; - - // let fract = 255; let c = Rgb888::new( ((fract * c_left.r() as u32) / 255) as u8, From 37d5df4d90f738fd3f51e165f2cc840c148878c2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 28 Dec 2021 14:42:58 +0000 Subject: [PATCH 061/188] Improved parallel Wu algo --- debug-tools/examples/line-parallel.rs | 353 ++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 debug-tools/examples/line-parallel.rs diff --git a/debug-tools/examples/line-parallel.rs b/debug-tools/examples/line-parallel.rs new file mode 100644 index 0000000..57a9577 --- /dev/null +++ b/debug-tools/examples/line-parallel.rs @@ -0,0 +1,353 @@ +use embedded_graphics::{ + geometry::PointExt, + mock_display::MockDisplay, + pixelcolor::Rgb888, + prelude::*, + primitives::{ + common::{LineSide, LinearEquation}, + line::StrokeOffset, + Line, PrimitiveStyle, + }, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; +use integer_sqrt::IntegerSquareRoot; + +#[derive(Debug, Copy, Clone, PartialEq)] +enum LineOffset { + Left, + Center, + Right, +} + +impl LineOffset { + fn widths(self, width: i32) -> (i32, i32) { + match width { + width => { + match self { + Self::Left => (width.saturating_sub(1), 0), + Self::Center => { + let width = width.saturating_sub(1); + + // Right-side bias for even width lines. Move mod2 to first item in the + // tuple to bias to the left instead. + (width / 2, width / 2 + (width % 2)) + } + Self::Right => (width.saturating_sub(1), 0), + } + } + } + } +} + +#[derive(Debug, Clone, Copy)] +struct MajorMinor { + major: T, + minor: T, +} + +impl MajorMinor { + fn new(major: T, minor: T) -> Self { + Self { major, minor } + } +} + +// From , linked from +fn dist(line: Line, point: Point) -> f32 { + let Line { start, .. } = line; + + let Point { + x: point_x, + y: point_y, + } = point; + + let point_x = point_x as f32; + let point_y = point_y as f32; + + let delta = line.delta(); + + let slope = delta.y as f32 / delta.x as f32; + let intercept = start.y as f32 - (slope * start.x as f32); + + f32::abs(slope * point_x - point_y + intercept) / f32::sqrt(slope.powi(2) + 1.0) +} + +fn perpendicular( + display: &mut impl DrawTarget, + line: Line, + (left_extent, right_extent): (Line, Line), + x0: i32, + y0: i32, + delta: MajorMinor, + step: MajorMinor, + einit: i32, + width: i32, + winit: i32, + extra: bool, +) -> Result<(), std::convert::Infallible> { + let mut point = Point::new(x0, y0); + + if width == 0 { + return Ok(()); + } + + let dx = delta.major; + let dy = delta.minor; + + let sign = match (step.major, step.minor) { + (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, + (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, + (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, + (Point { x: 0, y: 1 }, Point { x: 1, y: 0 }) => -1, + _ => 1, + }; + + let dx = dx.abs(); + let dy = dy.abs(); + + let threshold = dx - 2 * dy; + // E_diag + let e_minor = -2 * dx; + // E_square + let e_major = 2 * dy; + + let mut error = einit * sign; + + let (side_check_left, side_check_right) = (LineSide::Left, LineSide::Right); + + let (_width_l, _width_r) = LineOffset::Center.widths(width); + + let (c_left, c_right) = if extra { + (Rgb888::RED, Rgb888::GREEN) + } else { + (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) + }; + let c_left = Rgb888::WHITE; + let c_right = c_left; + + // Add one to width so we get an extra iteration for the AA edge + let wthr = (width + 1).pow(2) * (dx.pow(2) + dy.pow(2)); + let init_offset = dx + dy - (winit * sign); + let mut tk = init_offset; + // let mut tk: i32 = 0; + + // dbg!(wthr); + + println!("==="); + + // Perpendicular iteration + while tk.pow(2) <= wthr { + Pixel(point, c_left).draw(display)?; + + if error > threshold { + point += step.major; + error += e_minor; + tk += 2 * dy; + } + + error += e_major; + point += step.minor; + tk += 2 * dx; + + if tk.pow(2) > wthr { + let fract = tk.pow(2) as u32 * 255 / (wthr as u32); + + let fract = { + // There's this weird division of 1.5 to make the AA look correct. This magic value + // is the 8 bit scaler 255 / 1.5. I haven't got to the bottom of why it must be 1.5 + // yet. Maybe something to do with Bresenham's errors being at most 0.5 away from + // pixel centers and everything being multiplied by 2? + let two_thirds_255 = 170.0; + + let thickness_ratio = (tk.pow(2) as f32 * two_thirds_255) / (wthr as f32); + let thickness_ratio = thickness_ratio % two_thirds_255; + + (255.0 - thickness_ratio * _width_l as f32) as u32 + }; + + let c = Rgb888::new( + ((fract * c_left.r() as u32) / 255) as u8, + ((fract * c_left.g() as u32) / 255) as u8, + ((fract * c_left.b() as u32) / 255) as u8, + ); + + Pixel(point, c).draw(display)?; + } + } + + let mut point = Point::new(x0, y0); + let mut error = einit * -sign; + + let mut tk = dx + dy + (winit * sign); + + while tk.pow(2) <= wthr { + Pixel(point, c_right).draw(display)?; + + if error > threshold { + point -= step.major; + error += e_minor; + tk += 2 * dy; + } + + error += e_major; + point -= step.minor; + tk += 2 * dx; + } + + Ok(()) +} + +fn thickline( + display: &mut impl DrawTarget, + line: Line, + width: i32, +) -> Result<(), std::convert::Infallible> { + let Line { start, end } = line; + + let extents = line.extents(width as u32, StrokeOffset::None); + + let (delta, step, pstep) = { + let delta = end - start; + + let direction = Point::new( + if delta.x >= 0 { 1 } else { -1 }, + if delta.y >= 0 { 1 } else { -1 }, + ); + + let perp_direction = { + // let perp_delta = Point::new(delta.y, -delta.x); + let perp_delta = line.perpendicular(); + let perp_delta = perp_delta.end - perp_delta.start; + + Point::new( + if perp_delta.x >= 0 { 1 } else { -1 }, + if perp_delta.y >= 0 { 1 } else { -1 }, + ) + }; + + // Determine major and minor directions. + if delta.y.abs() >= delta.x.abs() { + ( + MajorMinor::new(delta.y, delta.x), + MajorMinor::new(direction.y_axis(), direction.x_axis()), + MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), + ) + } else { + ( + MajorMinor::new(delta.x, delta.y), + MajorMinor::new(direction.x_axis(), direction.y_axis()), + MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), + ) + } + }; + + let mut error = 0; + let mut point = start; + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx + 1; + + let skele_color = Rgb888::MAGENTA; + let mut e = 0.0f32; + let slope = dy as f32 / dx as f32; + + for _i in 0..length { + Pixel(point, skele_color).draw(display)?; + + { + let e = e.abs(); + + // AA point above line + let bright = ((1.0 - e) * 255.0) as u32; + let c = Rgb888::new( + ((bright * skele_color.r() as u32) / 255) as u8, + ((bright * skele_color.g() as u32) / 255) as u8, + ((bright * skele_color.b() as u32) / 255) as u8, + ); + Pixel(point - step.minor, c).draw(display)?; + } + + if error > threshold { + e = 0.0; + point += step.minor; + error += e_minor; + } + + e += slope; + error += e_major; + point += step.major; + } + + Ok(()) +} + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, +} + +impl App for LineDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(200, 200); + // const DISPLAY_SIZE: Size = Size::new(64, 64); + + fn new() -> Self { + let end = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); + Self { + start: end + Point::new(10, 15), + end, + // end: start + Point::new(100, 0), + stroke_width: 10, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let Point { x: _x0, y: _y0 } = self.start; + + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; + + let _mock_display: MockDisplay = MockDisplay::new(); + + thickline(display, Line::new(self.start, self.end), width)?; + + // let l = Line::new(self.start, self.end); + + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + // l.perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(5).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} From 3b8155516fba34d6a611afd77fac940a0aa135d3 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 29 Dec 2021 12:11:05 +0000 Subject: [PATCH 062/188] Add slower but more stable line distance function --- debug-tools/examples/line-perp.rs | 62 +++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/debug-tools/examples/line-perp.rs b/debug-tools/examples/line-perp.rs index aaafa3c..b6bf86f 100644 --- a/debug-tools/examples/line-perp.rs +++ b/debug-tools/examples/line-perp.rs @@ -52,24 +52,64 @@ impl MajorMinor { } } -// From , linked from +// // From , linked from +// fn dist(line: Line, point: Point) -> f32 { +// let Line { start, .. } = line; + +// let Point { +// x: point_x, +// y: point_y, +// } = point; + +// let point_x = point_x as f32; +// let point_y = point_y as f32; + +// let delta = line.delta(); + +// let slope = delta.y as f32 / delta.x as f32; +// let intercept = start.y as f32 - (slope * start.x as f32); + +// f32::abs(slope * point_x - point_y + intercept) / f32::sqrt(slope.powi(2) + 1.0) +// } + +// From +// Slower, but doesn't give NaNs as above solution does sometimes fn dist(line: Line, point: Point) -> f32 { - let Line { start, .. } = line; + let Line { + start: Point { x: x1, y: y1 }, + end: Point { x: x2, y: y2 }, + } = line; - let Point { - x: point_x, - y: point_y, - } = point; + let Point { x: px, y: py } = point; - let point_x = point_x as f32; - let point_y = point_y as f32; + let x1 = x1 as f32; + let y1 = y1 as f32; + let x2 = x2 as f32; + let y2 = y2 as f32; + let px = px as f32; + let py = py as f32; let delta = line.delta(); - let slope = delta.y as f32 / delta.x as f32; - let intercept = start.y as f32 - (slope * start.x as f32); + let length_sq = delta.length_squared(); + let length = f32::sqrt(length_sq as f32); + + let dx = delta.x as f32 / length; + let dy = delta.y as f32 / length; + + let p = dx * (px - x1) + dy * (py - y1); + + if p < 0.0 { + let dx = px - x1; + let dy = py - y1; + return length; + } else if p > length { + let dx = px - x2; + let dy = py - y2; + return length; + } - f32::abs(slope * point_x - point_y + intercept) / f32::sqrt(slope.powi(2) + 1.0) + return f32::abs(dy * (px - x1) - dx * (py - y1)); } fn perpendicular( From 267d7eaf87e3033448d6309c29083de2ea262da4 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 29 Dec 2021 23:19:21 +0000 Subject: [PATCH 063/188] IDEFK lmao --- debug-tools/examples/line-parallel.rs | 186 +++++++------------------- 1 file changed, 50 insertions(+), 136 deletions(-) diff --git a/debug-tools/examples/line-parallel.rs b/debug-tools/examples/line-parallel.rs index 57a9577..4c2c9e6 100644 --- a/debug-tools/examples/line-parallel.rs +++ b/debug-tools/examples/line-parallel.rs @@ -52,149 +52,45 @@ impl MajorMinor { } } -// From , linked from -fn dist(line: Line, point: Point) -> f32 { - let Line { start, .. } = line; - - let Point { - x: point_x, - y: point_y, - } = point; +// // From , linked from +// fn dist(line: Line, point: Point) -> f32 { +// let Line { start, .. } = line; - let point_x = point_x as f32; - let point_y = point_y as f32; +// let Point { +// x: point_x, +// y: point_y, +// } = point; - let delta = line.delta(); +// let point_x = point_x as f32; +// let point_y = point_y as f32; - let slope = delta.y as f32 / delta.x as f32; - let intercept = start.y as f32 - (slope * start.x as f32); +// let delta = line.delta(); - f32::abs(slope * point_x - point_y + intercept) / f32::sqrt(slope.powi(2) + 1.0) -} +// let slope = delta.y as f32 / delta.x as f32; +// let intercept = start.y as f32 - (slope * start.x as f32); -fn perpendicular( - display: &mut impl DrawTarget, - line: Line, - (left_extent, right_extent): (Line, Line), - x0: i32, - y0: i32, - delta: MajorMinor, - step: MajorMinor, - einit: i32, - width: i32, - winit: i32, - extra: bool, -) -> Result<(), std::convert::Infallible> { - let mut point = Point::new(x0, y0); +// f32::abs(slope * point_x - point_y + intercept) / f32::sqrt(slope.powi(2) + 1.0) +// } - if width == 0 { - return Ok(()); - } - - let dx = delta.major; - let dy = delta.minor; - - let sign = match (step.major, step.minor) { - (Point { x: -1, y: 0 }, Point { x: 0, y: 1 }) => -1, - (Point { x: 0, y: -1 }, Point { x: -1, y: 0 }) => -1, - (Point { x: 1, y: 0 }, Point { x: 0, y: -1 }) => -1, - (Point { x: 0, y: 1 }, Point { x: 1, y: 0 }) => -1, - _ => 1, - }; - - let dx = dx.abs(); - let dy = dy.abs(); - - let threshold = dx - 2 * dy; - // E_diag - let e_minor = -2 * dx; - // E_square - let e_major = 2 * dy; - - let mut error = einit * sign; - - let (side_check_left, side_check_right) = (LineSide::Left, LineSide::Right); - - let (_width_l, _width_r) = LineOffset::Center.widths(width); - - let (c_left, c_right) = if extra { - (Rgb888::RED, Rgb888::GREEN) - } else { - (Rgb888::CSS_CORNFLOWER_BLUE, Rgb888::YELLOW) - }; - let c_left = Rgb888::WHITE; - let c_right = c_left; - - // Add one to width so we get an extra iteration for the AA edge - let wthr = (width + 1).pow(2) * (dx.pow(2) + dy.pow(2)); - let init_offset = dx + dy - (winit * sign); - let mut tk = init_offset; - // let mut tk: i32 = 0; - - // dbg!(wthr); - - println!("==="); - - // Perpendicular iteration - while tk.pow(2) <= wthr { - Pixel(point, c_left).draw(display)?; - - if error > threshold { - point += step.major; - error += e_minor; - tk += 2 * dy; - } - - error += e_major; - point += step.minor; - tk += 2 * dx; - - if tk.pow(2) > wthr { - let fract = tk.pow(2) as u32 * 255 / (wthr as u32); - - let fract = { - // There's this weird division of 1.5 to make the AA look correct. This magic value - // is the 8 bit scaler 255 / 1.5. I haven't got to the bottom of why it must be 1.5 - // yet. Maybe something to do with Bresenham's errors being at most 0.5 away from - // pixel centers and everything being multiplied by 2? - let two_thirds_255 = 170.0; - - let thickness_ratio = (tk.pow(2) as f32 * two_thirds_255) / (wthr as f32); - let thickness_ratio = thickness_ratio % two_thirds_255; - - (255.0 - thickness_ratio * _width_l as f32) as u32 - }; - - let c = Rgb888::new( - ((fract * c_left.r() as u32) / 255) as u8, - ((fract * c_left.g() as u32) / 255) as u8, - ((fract * c_left.b() as u32) / 255) as u8, - ); - - Pixel(point, c).draw(display)?; - } - } +fn dist(line: Line, point: Point) -> f32 { + let Line { + start: Point { x: x1, y: y1 }, + end: Point { x: x2, y: y2 }, + } = line; + let Point { x: x3, y: y3 } = point; - let mut point = Point::new(x0, y0); - let mut error = einit * -sign; + let delta = line.end - line.start; - let mut tk = dx + dy + (winit * sign); + let denom = (delta.x.pow(2) + delta.y.pow(2)) as f32; - while tk.pow(2) <= wthr { - Pixel(point, c_right).draw(display)?; + let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / denom; - if error > threshold { - point -= step.major; - error += e_minor; - tk += 2 * dy; - } + let x = x1 as f32 + u * (x2 - x1) as f32; + let y = y1 as f32 + u * (y2 - y1) as f32; - error += e_major; - point -= step.minor; - tk += 2 * dx; - } + let dist = f32::sqrt((x - x3 as f32).powi(2) + (y - y3 as f32).powi(2)); - Ok(()) + dist } fn thickline( @@ -241,7 +137,7 @@ fn thickline( } }; - let mut error = 0; + let mut error = 0i32; let mut point = start; let dx = delta.major.abs(); @@ -253,29 +149,47 @@ fn thickline( let length = dx + 1; let skele_color = Rgb888::MAGENTA; - let mut e = 0.0f32; let slope = dy as f32 / dx as f32; + let mut e = 0.0f32; + println!("==="); + + let le = LinearEquation::from_line(&line); + let len = f32::sqrt(line.delta().length_squared() as f32); for _i in 0..length { + println!("---"); Pixel(point, skele_color).draw(display)?; { - let e = e.abs(); + let aa_point = point - step.minor; + let compute = e.abs() as f32; + + let d = dist(line, point); + + dbg!(e, compute, error as f32 / (2.0 * dx as f32)); + + // Same as `e`, for positive values at least. + let compute = error as f32 / (2.0 * dx as f32); + + // Max distasnce is 1.5px - starting offset is 1px as we did `-step.minor` and the max + // error from the Bresenham algo is 0.5. + // let d = dist(line, aa_point); // AA point above line - let bright = ((1.0 - e) * 255.0) as u32; + let bright = ((1.0 - compute) * 255.0) as u32; let c = Rgb888::new( ((bright * skele_color.r() as u32) / 255) as u8, ((bright * skele_color.g() as u32) / 255) as u8, ((bright * skele_color.b() as u32) / 255) as u8, ); - Pixel(point - step.minor, c).draw(display)?; + Pixel(aa_point, c).draw(display)?; } if error > threshold { e = 0.0; point += step.minor; error += e_minor; + println!("..."); } e += slope; From 4830f2b86646a9ea34e03cdb220757c22ea2a8c4 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 29 Dec 2021 23:19:12 +0000 Subject: [PATCH 064/188] Replace Wu demo with... a worse one? --- debug-tools/examples/aa-wu.rs | 227 +++++++++++++++++++++------------- 1 file changed, 142 insertions(+), 85 deletions(-) diff --git a/debug-tools/examples/aa-wu.rs b/debug-tools/examples/aa-wu.rs index ebdd221..26644e4 100644 --- a/debug-tools/examples/aa-wu.rs +++ b/debug-tools/examples/aa-wu.rs @@ -1,12 +1,44 @@ -//! Render a 1px wide antialiased line using error components and a 255 multiplier. -//! -//! Inspiration from - use embedded_graphics::{ - mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, + geometry::PointExt, + mock_display::MockDisplay, + pixelcolor::Rgb888, + prelude::*, + primitives::{ + common::{LineSide, LinearEquation}, + line::StrokeOffset, + Line, PrimitiveStyle, + }, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; +use integer_sqrt::IntegerSquareRoot; + +#[derive(Debug, Copy, Clone, PartialEq)] +enum LineOffset { + Left, + Center, + Right, +} + +impl LineOffset { + fn widths(self, width: i32) -> (i32, i32) { + match width { + width => { + match self { + Self::Left => (width.saturating_sub(1), 0), + Self::Center => { + let width = width.saturating_sub(1); + + // Right-side bias for even width lines. Move mod2 to first item in the + // tuple to bias to the left instead. + (width / 2, width / 2 + (width % 2)) + } + Self::Right => (width.saturating_sub(1), 0), + } + } + } + } +} #[derive(Debug, Clone, Copy)] struct MajorMinor { @@ -20,117 +52,142 @@ impl MajorMinor { } } -fn thick_line( - display: &mut impl DrawTarget, - mut line: Line, - _width: i32, -) -> Result<(), std::convert::Infallible> { - let skele_color = Rgb888::MAGENTA; +fn floor(x: f32) -> f32 { + x.floor() +} +fn round(x: f32) -> f32 { + x.round() +} - // let Line { start, end } = line; +fn fract(x: f32) -> f32 { + x.fract() +} - let orig_start_y = line.start.y; +fn recip_fract(x: f32) -> f32 { + 1.0 - x.fract() +} - // line.start.y <<= 8; - // line.end.y <<= 8; +fn thickline( + display: &mut impl DrawTarget, + line: Line, + width: i32, +) -> Result<(), std::convert::Infallible> { + let Line { start, end } = line; - let delta = line.delta(); + let extents = line.extents(width as u32, StrokeOffset::None); - let dx = delta.x; - let dy = delta.y; + let (delta, step, pstep) = { + let delta = end - start; + + let direction = Point::new( + if delta.x >= 0 { 1 } else { -1 }, + if delta.y >= 0 { 1 } else { -1 }, + ); - let slope = dy as f32 / dx as f32; + let perp_direction = { + // let perp_delta = Point::new(delta.y, -delta.x); + let perp_delta = line.perpendicular(); + let perp_delta = perp_delta.end - perp_delta.start; + + Point::new( + if perp_delta.x >= 0 { 1 } else { -1 }, + if perp_delta.y >= 0 { 1 } else { -1 }, + ) + }; + + // Determine major and minor directions. + if delta.y.abs() >= delta.x.abs() { + ( + MajorMinor::new(delta.y, delta.x), + MajorMinor::new(direction.y_axis(), direction.x_axis()), + MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), + ) + } else { + ( + MajorMinor::new(delta.x, delta.y), + MajorMinor::new(direction.x_axis(), direction.y_axis()), + MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), + ) + } + }; - let mut point = line.start; + let mut point = start; - let mut error: f32 = 0.0; + let dx = delta.major.abs(); + let dy = delta.minor.abs(); - for _i in 0..=dx { - let c = skele_color; + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx + 1; + let mut error = 0i32; - // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; + let skele_color = Rgb888::MAGENTA; + let mut slope = dy as f32 / dx as f32; + // dbg!(slope); + // if slope <= 0.0 { + // dbg!("shet"); + // slope = 1.0; + // } + let mut e = 0.0f32; + // println!("==="); - let e = error.abs(); + for _i in 0..length { + // println!("---"); - dbg!(e); + let bright = ((1.0 - e.fract()) * 255.0) as u32; - // AA point above line - let bright = ((1.0 - e) * 255.0) as u32; - let c = Rgb888::new( - ((bright * skele_color.r() as u32) / 255) as u8, - ((bright * skele_color.g() as u32) / 255) as u8, - ((bright * skele_color.b() as u32) / 255) as u8, + let delta = Point::new( + e.floor() as i32 * dx.signum(), + e.floor() as i32 * dy.signum(), ); - Pixel(Point::new(point.x, point.y - 1), c).draw(display)?; + let delta = delta.component_mul(step.minor); - // Line skeleton - // let bright = (e * 255.0) as u32; - let bright = 255; let c = Rgb888::new( ((bright * skele_color.r() as u32) / 255) as u8, ((bright * skele_color.g() as u32) / 255) as u8, ((bright * skele_color.b() as u32) / 255) as u8, ); - Pixel(Point::new(point.x, point.y), c).draw(display)?; + Pixel(point + delta - step.minor, c).draw(display)?; + Pixel(point + delta, skele_color).draw(display)?; - error += slope; - - if error > 1.0 { - point.y += 1; - error = 0.0; + if error > threshold { + // point += step.minor; + error += e_minor; + // println!("..."); } - point.x += 1; + e += slope; + error += e_major; + point += step.major; } - // // let Line { start, end } = line; - - // let orig_start_y = line.start.y; + // for x in 0..length { + // { + // let bright = ((1.0 - e.fract()) * 255.0) as u32; - // // line.start.y <<= 8; - // // line.end.y <<= 8; + // let c = Rgb888::new( + // ((bright * skele_color.r() as u32) / 255) as u8, + // ((bright * skele_color.g() as u32) / 255) as u8, + // ((bright * skele_color.b() as u32) / 255) as u8, + // ); - // let delta = line.delta(); - - // let mut error: i32 = 0; - // let mut point = line.start; - - // // let dx = delta.major.abs(); - // // let dy = delta.minor.abs(); - // let dx = delta.x; - // let dy = delta.y; - - // let threshold = dx - 2 * dy; - // let e_minor = -2 * dx; - // let e_major = 2 * dy; - // let length = dx + 1; - - // let skele_color = Rgb888::MAGENTA; - - // let mut py = 0; - - // for _i in 0..length { - // let c = skele_color; - - // // let bright = (1.0 - (error as f32 / e_minor as f32 * 255.0)).abs() as u32; - - // // let c = Rgb888::new( - // // ((bright * skele_color.r() as u32) / 255) as u8, - // // ((bright * skele_color.g() as u32) / 255) as u8, - // // ((bright * skele_color.b() as u32) / 255) as u8, - // // ); + // Pixel(point, c).draw(display)?; + // } - // Pixel(Point::new(point.x, orig_start_y + (py >> 8)), c).draw(display)?; + // { + // let bright = ((e.fract()) * 255.0) as u32; - // println!("{}", (error as f32) / threshold as f32); + // let c = Rgb888::new( + // ((bright * skele_color.r() as u32) / 255) as u8, + // ((bright * skele_color.g() as u32) / 255) as u8, + // ((bright * skele_color.b() as u32) / 255) as u8, + // ); - // if error > threshold { - // py += 1 << 8; - // error += e_minor; + // Pixel(point - step.minor, c).draw(display)?; // } - // error += e_major; - // point.x += 1; + // e += gradient; // } Ok(()) @@ -180,7 +237,7 @@ impl App for LineDebug { let _mock_display: MockDisplay = MockDisplay::new(); - thick_line(display, Line::new(self.start, self.end), width)?; + thickline(display, Line::new(self.start, self.end), width)?; // let l = Line::new(self.start, self.end); From 5b5d18a833e2ffcdda78c8dca2c10fcb4f7e5302 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 30 Dec 2021 13:58:17 +0000 Subject: [PATCH 065/188] Wu AA, again, slightly better this time --- debug-tools/examples/aa-wu.rs | 62 +++++++++++++---------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/debug-tools/examples/aa-wu.rs b/debug-tools/examples/aa-wu.rs index 26644e4..895bc13 100644 --- a/debug-tools/examples/aa-wu.rs +++ b/debug-tools/examples/aa-wu.rs @@ -112,6 +112,7 @@ fn thickline( }; let mut point = start; + let mut p = start; let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -124,18 +125,19 @@ fn thickline( let skele_color = Rgb888::MAGENTA; let mut slope = dy as f32 / dx as f32; - // dbg!(slope); - // if slope <= 0.0 { - // dbg!("shet"); - // slope = 1.0; - // } + let mut e = 0.0f32; - // println!("==="); + println!("==="); for _i in 0..length { - // println!("---"); + println!("---"); - let bright = ((1.0 - e.fract()) * 255.0) as u32; + let bright = if slope >= 1.0 { + // Half brightness pixels along edge when line is at 45º + 255 / 2 + } else { + ((1.0 - e.fract()) * 255.0) as u32 + }; let delta = Point::new( e.floor() as i32 * dx.signum(), @@ -143,53 +145,33 @@ fn thickline( ); let delta = delta.component_mul(step.minor); + // let delta = Point::zero(); + let c = Rgb888::new( ((bright * skele_color.r() as u32) / 255) as u8, ((bright * skele_color.g() as u32) / 255) as u8, ((bright * skele_color.b() as u32) / 255) as u8, ); - Pixel(point + delta - step.minor, c).draw(display)?; - Pixel(point + delta, skele_color).draw(display)?; + Pixel(p + delta - step.minor, c).draw(display)?; + Pixel(p + delta, skele_color).draw(display)?; + + // Pixel(point, Rgb888::WHITE).draw(display)?; + + dbg!(e, slope); if error > threshold { - // point += step.minor; + // if 1.0 - e.fract() < slope { + point += step.minor; error += e_minor; - // println!("..."); + println!("..."); } e += slope; error += e_major; point += step.major; + p += step.major; } - // for x in 0..length { - // { - // let bright = ((1.0 - e.fract()) * 255.0) as u32; - - // let c = Rgb888::new( - // ((bright * skele_color.r() as u32) / 255) as u8, - // ((bright * skele_color.g() as u32) / 255) as u8, - // ((bright * skele_color.b() as u32) / 255) as u8, - // ); - - // Pixel(point, c).draw(display)?; - // } - - // { - // let bright = ((e.fract()) * 255.0) as u32; - - // let c = Rgb888::new( - // ((bright * skele_color.r() as u32) / 255) as u8, - // ((bright * skele_color.g() as u32) / 255) as u8, - // ((bright * skele_color.b() as u32) / 255) as u8, - // ); - - // Pixel(point - step.minor, c).draw(display)?; - // } - - // e += gradient; - // } - Ok(()) } From 6048099d58d1da6ed5303101c52e8bd7011e5c2d Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 30 Dec 2021 14:46:32 +0000 Subject: [PATCH 066/188] Bit more Wu debugging --- debug-tools/examples/aa-wu.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/debug-tools/examples/aa-wu.rs b/debug-tools/examples/aa-wu.rs index 895bc13..2ccb31d 100644 --- a/debug-tools/examples/aa-wu.rs +++ b/debug-tools/examples/aa-wu.rs @@ -132,6 +132,8 @@ fn thickline( for _i in 0..length { println!("---"); + // NOTE: Numerical inaccuracies cause fireflies when using `error as f32 / (2 * dx) as f32` + // (although is yields mostly the same result) from Bresenham. let bright = if slope >= 1.0 { // Half brightness pixels along edge when line is at 45º 255 / 2 @@ -145,19 +147,19 @@ fn thickline( ); let delta = delta.component_mul(step.minor); - // let delta = Point::zero(); - let c = Rgb888::new( ((bright * skele_color.r() as u32) / 255) as u8, ((bright * skele_color.g() as u32) / 255) as u8, ((bright * skele_color.b() as u32) / 255) as u8, ); + + // Wu AA pixel Pixel(p + delta - step.minor, c).draw(display)?; + // Wu normal pixel Pixel(p + delta, skele_color).draw(display)?; - // Pixel(point, Rgb888::WHITE).draw(display)?; - - dbg!(e, slope); + // Bresenham pixel + Pixel(point - Point::new(0, 4), Rgb888::WHITE).draw(display)?; if error > threshold { // if 1.0 - e.fract() < slope { @@ -192,7 +194,8 @@ impl App for LineDebug { Self::DISPLAY_SIZE.height as i32 / 2, ); Self { - start: end + Point::new(10, 15), + // start: end + Point::new(10, 15), + start: Point::new(95, 99), end, // end: start + Point::new(100, 0), stroke_width: 10, From 2ce0bf2c4604ecce37f715f6bb3fd675ad059292 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 30 Dec 2021 17:50:34 +0000 Subject: [PATCH 067/188] Use half rate error accum --- debug-tools/examples/line-parallel.rs | 17 ++++++++++------- framework/Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/debug-tools/examples/line-parallel.rs b/debug-tools/examples/line-parallel.rs index 4c2c9e6..a4f90b5 100644 --- a/debug-tools/examples/line-parallel.rs +++ b/debug-tools/examples/line-parallel.rs @@ -137,7 +137,6 @@ fn thickline( } }; - let mut error = 0i32; let mut point = start; let dx = delta.major.abs(); @@ -147,14 +146,16 @@ fn thickline( let e_minor = -2 * dx; let e_major = 2 * dy; let length = dx + 1; + let mut error = 0; let skele_color = Rgb888::MAGENTA; let slope = dy as f32 / dx as f32; let mut e = 0.0f32; + let mut e2 = 0; println!("==="); - let le = LinearEquation::from_line(&line); - let len = f32::sqrt(line.delta().length_squared() as f32); + // let le = LinearEquation::from_line(&line); + // let len = f32::sqrt(line.delta().length_squared() as f32); for _i in 0..length { println!("---"); @@ -164,12 +165,12 @@ fn thickline( let aa_point = point - step.minor; let compute = e.abs() as f32; - let d = dist(line, point); + // let d = dist(line, point); - dbg!(e, compute, error as f32 / (2.0 * dx as f32)); + let compute = e2 as f32 / (dx as f32); - // Same as `e`, for positive values at least. - let compute = error as f32 / (2.0 * dx as f32); + dbg!(error, e2, compute, e_minor, threshold); + // assert!(compute <= 1.0); // Max distasnce is 1.5px - starting offset is 1px as we did `-step.minor` and the max // error from the Bresenham algo is 0.5. @@ -187,12 +188,14 @@ fn thickline( if error > threshold { e = 0.0; + e2 = 0; point += step.minor; error += e_minor; println!("..."); } e += slope; + e2 += dy; error += e_major; point += step.major; } diff --git a/framework/Cargo.toml b/framework/Cargo.toml index 41eb87b..79d4fe7 100644 --- a/framework/Cargo.toml +++ b/framework/Cargo.toml @@ -8,4 +8,4 @@ publish = false [dependencies] embedded-graphics = "0.7.0-beta.1" embedded-graphics-simulator = { version = "0.3.0", path = "../../simulator" } -sdl2 = "0.32.2" +sdl2 = "0.35.1" From 61cc3f80fcc82fca5180eadbc14ae98552cabde0 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 22 May 2022 21:12:52 +0100 Subject: [PATCH 068/188] Change example to draw pixel pairs --- debug-tools/examples/line-parallel.rs | 54 ++++++++------------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/debug-tools/examples/line-parallel.rs b/debug-tools/examples/line-parallel.rs index a4f90b5..6289dc1 100644 --- a/debug-tools/examples/line-parallel.rs +++ b/debug-tools/examples/line-parallel.rs @@ -137,6 +137,19 @@ fn thickline( } }; + // Direction to travel to hit pixel next to current line + let perp_direction = { + // let perp_delta = Point::new(delta.y, -delta.x); + let perp_delta = line.perpendicular(); + let perp_delta = perp_delta.end - perp_delta.start; + + if perp_delta.y.abs() > perp_delta.x.abs() { + Point::new(0, if perp_delta.y >= 0 { 1 } else { -1 }) + } else { + Point::new(if perp_delta.x >= 0 { 1 } else { -1 }, 0) + } + }; + let mut point = start; let dx = delta.major.abs(); @@ -148,54 +161,19 @@ fn thickline( let length = dx + 1; let mut error = 0; - let skele_color = Rgb888::MAGENTA; - let slope = dy as f32 / dx as f32; - let mut e = 0.0f32; - let mut e2 = 0; println!("==="); - // let le = LinearEquation::from_line(&line); - // let len = f32::sqrt(line.delta().length_squared() as f32); + dbg!(&perp_direction); for _i in 0..length { - println!("---"); - Pixel(point, skele_color).draw(display)?; - - { - let aa_point = point - step.minor; - let compute = e.abs() as f32; - - // let d = dist(line, point); - - let compute = e2 as f32 / (dx as f32); - - dbg!(error, e2, compute, e_minor, threshold); - // assert!(compute <= 1.0); - - // Max distasnce is 1.5px - starting offset is 1px as we did `-step.minor` and the max - // error from the Bresenham algo is 0.5. - // let d = dist(line, aa_point); - - // AA point above line - let bright = ((1.0 - compute) * 255.0) as u32; - let c = Rgb888::new( - ((bright * skele_color.r() as u32) / 255) as u8, - ((bright * skele_color.g() as u32) / 255) as u8, - ((bright * skele_color.b() as u32) / 255) as u8, - ); - Pixel(aa_point, c).draw(display)?; - } + Pixel(point, Rgb888::MAGENTA).draw(display)?; + Pixel(point + perp_direction, Rgb888::CYAN).draw(display)?; if error > threshold { - e = 0.0; - e2 = 0; point += step.minor; error += e_minor; - println!("..."); } - e += slope; - e2 += dy; error += e_major; point += step.major; } From 0aeeaf1c43e3407e2a1c78506504119c86fc3e67 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 22 May 2022 21:35:54 +0100 Subject: [PATCH 069/188] Very crude some-octant AA Seems to be "inverted" for some quadrants - in actuality the pixels are just misplaced --- debug-tools/examples/line-parallel.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/line-parallel.rs b/debug-tools/examples/line-parallel.rs index 6289dc1..fa933c7 100644 --- a/debug-tools/examples/line-parallel.rs +++ b/debug-tools/examples/line-parallel.rs @@ -159,19 +159,39 @@ fn thickline( let e_minor = -2 * dx; let e_major = 2 * dy; let length = dx + 1; + let aa_base_color = Rgb888::MAGENTA; let mut error = 0; + let slope = dy as f32 / dx as f32; + let mut bright = 0.5; + + let swap = false; + println!("==="); - dbg!(&perp_direction); + dbg!(line.delta()); for _i in 0..length { + let mul = (bright * 255.0) as u32; + + let aa_color = Rgb888::new( + ((mul * aa_base_color.r() as u32) / 255) as u8, + ((mul * aa_base_color.g() as u32) / 255) as u8, + ((mul * aa_base_color.b() as u32) / 255) as u8, + ); + Pixel(point, Rgb888::MAGENTA).draw(display)?; - Pixel(point + perp_direction, Rgb888::CYAN).draw(display)?; + + if !swap { + Pixel(point + perp_direction, aa_color).draw(display)?; + } + + bright = (bright - slope).max(0.0); if error > threshold { point += step.minor; error += e_minor; + bright = 1.0; } error += e_major; From c7d72377512690509b3036cdbddcbcdb5ae2ae3e Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 30 Nov 2022 12:56:45 +0000 Subject: [PATCH 070/188] Add no-AA parallel line Currently only works well in some octants. Committing here so I can work on using extents line as a basis --- debug-tools/examples/line-no-aa-parallel.rs | 269 ++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 debug-tools/examples/line-no-aa-parallel.rs diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs new file mode 100644 index 0000000..9d5a9bb --- /dev/null +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -0,0 +1,269 @@ +use embedded_graphics::{ + geometry::PointExt, + mock_display::MockDisplay, + pixelcolor::Rgb888, + prelude::*, + primitives::{ + common::{LineSide, LinearEquation}, + line::StrokeOffset, + Line, PrimitiveStyle, + }, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; +use integer_sqrt::IntegerSquareRoot; + +#[derive(Debug, Copy, Clone, PartialEq)] +enum LineOffset { + Left, + Center, + Right, +} + +impl LineOffset { + fn widths(self, width: i32) -> (i32, i32) { + match width { + width => { + match self { + Self::Left => (width.saturating_sub(1), 0), + Self::Center => { + let width = width.saturating_sub(1); + + // Right-side bias for even width lines. Move mod2 to first item in the + // tuple to bias to the left instead. + (width / 2, width / 2 + (width % 2)) + } + Self::Right => (width.saturating_sub(1), 0), + } + } + } + } +} + +#[derive(Debug, Clone, Copy)] +struct MajorMinor { + major: T, + minor: T, +} + +impl MajorMinor { + fn new(major: T, minor: T) -> Self { + Self { major, minor } + } +} + +fn thickline( + display: &mut impl DrawTarget, + line: Line, + width: i32, +) -> Result<(), std::convert::Infallible> { + let Line { start, end } = line; + + let extents = line.extents(width as u32, StrokeOffset::None); + + // // The perpendicular starting edge of the line + // let seed_line = Line::new(extents.0.start, extents.1.start); + + let (delta, step, pdelta, pstep) = { + let delta = end - start; + + let direction = Point::new( + if delta.x >= 0 { 1 } else { -1 }, + if delta.y >= 0 { 1 } else { -1 }, + ); + + let perp_delta = line.perpendicular(); + let perp_delta = perp_delta.end - perp_delta.start; + + let perp_direction = Point::new( + if perp_delta.x >= 0 { 1 } else { -1 }, + if perp_delta.y >= 0 { 1 } else { -1 }, + ); + + let (perp_delta, perp_direction) = if perp_delta.y.abs() >= perp_delta.x.abs() { + ( + MajorMinor::new(perp_delta.y, perp_delta.x), + MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), + ) + } else { + ( + MajorMinor::new(perp_delta.x, perp_delta.y), + MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), + ) + }; + + // Determine major and minor directions. + if delta.y.abs() >= delta.x.abs() { + ( + MajorMinor::new(delta.y, delta.x), + MajorMinor::new(direction.y_axis(), direction.x_axis()), + perp_delta, + perp_direction, + ) + } else { + ( + MajorMinor::new(delta.x, delta.y), + MajorMinor::new(direction.x_axis(), direction.y_axis()), + perp_delta, + perp_direction, + ) + } + }; + + let mut point = start; + + // Base line skeleton + parallel_line(point, line, step, delta, 0, Rgb888::MAGENTA, display)?; + + let dx = pdelta.major.abs(); + let dy = pdelta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx + 1; + let mut error = 0; + // Perpendicular error or "phase" + let mut perp_error = 0; + + for _i in 0..width { + Pixel(point, Rgb888::WHITE).draw(display)?; + + parallel_line( + point, + line, + step, + delta, + perp_error, + Rgb888::MAGENTA, + display, + )?; + + if error > threshold { + point += pstep.minor; + error += e_minor; + + if perp_error > threshold { + parallel_line( + point, + line, + step, + delta, + perp_error + e_minor + e_major, + Rgb888::CYAN, + display, + )?; + + perp_error += e_minor; + } + + perp_error += e_major; + } + + error += e_major; + point += pstep.major; + } + + Ok(()) +} + +fn parallel_line( + start: Point, + line: Line, + step: MajorMinor, + delta: MajorMinor, + start_error: i32, + c: Rgb888, + display: &mut impl DrawTarget, +) -> Result<(), std::convert::Infallible> { + let mut point = start; + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx + 1; + let mut error = start_error; + + for _i in 0..length { + Pixel(point, c).draw(display)?; + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + + Ok(()) +} + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, +} + +impl App for LineDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(200, 200); + // const DISPLAY_SIZE: Size = Size::new(64, 64); + + fn new() -> Self { + let end = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); + Self { + start: end + Point::new(10, 15), + end, + // end: start + Point::new(100, 0), + stroke_width: 10, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let Point { x: _x0, y: _y0 } = self.start; + + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; + + let _mock_display: MockDisplay = MockDisplay::new(); + + thickline(display, Line::new(self.start, self.end), width)?; + + // let l = Line::new(self.start, self.end); + + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + // l.perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(5).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} From 6bff3e6acab4eb8a603f59490ede18536d5ce5aa Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 6 Dec 2022 22:02:32 +0000 Subject: [PATCH 071/188] WIP: In-phase parallel lines for some octants --- debug-tools/examples/line-no-aa-parallel.rs | 200 ++++++++++++-------- 1 file changed, 125 insertions(+), 75 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 9d5a9bb..b617805 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -61,107 +61,157 @@ fn thickline( let extents = line.extents(width as u32, StrokeOffset::None); - // // The perpendicular starting edge of the line - // let seed_line = Line::new(extents.0.start, extents.1.start); + // The perpendicular starting edge of the line + let seed_line = Line::new(extents.0.start, extents.1.start); + + let parallel_delta = line.end - line.start; + let parallel_step = Point::new( + if parallel_delta.x >= 0 { 1 } else { -1 }, + if parallel_delta.y >= 0 { 1 } else { -1 }, + ); + + // let (delta, step, pdelta, pstep) = { + // let delta = end - start; + + // let direction = Point::new( + // if delta.x >= 0 { 1 } else { -1 }, + // if delta.y >= 0 { 1 } else { -1 }, + // ); + + // let perp_delta = line.perpendicular(); + // let perp_delta = perp_delta.end - perp_delta.start; + + // let perp_direction = Point::new( + // if perp_delta.x >= 0 { 1 } else { -1 }, + // if perp_delta.y >= 0 { 1 } else { -1 }, + // ); + + // let (perp_delta, perp_direction) = if perp_delta.y.abs() >= perp_delta.x.abs() { + // ( + // MajorMinor::new(perp_delta.y, perp_delta.x), + // MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), + // ) + // } else { + // ( + // MajorMinor::new(perp_delta.x, perp_delta.y), + // MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), + // ) + // }; + + // // Determine major and minor directions. + // if delta.y.abs() >= delta.x.abs() { + // ( + // MajorMinor::new(delta.y, delta.x), + // MajorMinor::new(direction.y_axis(), direction.x_axis()), + // perp_delta, + // perp_direction, + // ) + // } else { + // ( + // MajorMinor::new(delta.x, delta.y), + // MajorMinor::new(direction.x_axis(), direction.y_axis()), + // perp_delta, + // perp_direction, + // ) + // } + // }; + + let mut point = seed_line.start; - let (delta, step, pdelta, pstep) = { - let delta = end - start; - - let direction = Point::new( - if delta.x >= 0 { 1 } else { -1 }, - if delta.y >= 0 { 1 } else { -1 }, - ); - - let perp_delta = line.perpendicular(); - let perp_delta = perp_delta.end - perp_delta.start; - - let perp_direction = Point::new( - if perp_delta.x >= 0 { 1 } else { -1 }, - if perp_delta.y >= 0 { 1 } else { -1 }, - ); - - let (perp_delta, perp_direction) = if perp_delta.y.abs() >= perp_delta.x.abs() { - ( - MajorMinor::new(perp_delta.y, perp_delta.x), - MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), - ) - } else { - ( - MajorMinor::new(perp_delta.x, perp_delta.y), - MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), - ) - }; - - // Determine major and minor directions. - if delta.y.abs() >= delta.x.abs() { - ( - MajorMinor::new(delta.y, delta.x), - MajorMinor::new(direction.y_axis(), direction.x_axis()), - perp_delta, - perp_direction, - ) - } else { - ( - MajorMinor::new(delta.x, delta.y), - MajorMinor::new(direction.x_axis(), direction.y_axis()), - perp_delta, - perp_direction, - ) - } + // Base line skeleton + // parallel_line(point, line, step, delta, 0, Rgb888::MAGENTA, display)?; + + let seed_line_delta = seed_line.end - seed_line.start; + + let seed_line_direction = Point::new( + if seed_line_delta.x >= 0 { 1 } else { -1 }, + if seed_line_delta.y >= 0 { 1 } else { -1 }, + ); + + let (seed_line_delta, seed_line_step) = if seed_line_delta.y.abs() >= seed_line_delta.x.abs() { + ( + MajorMinor::new(seed_line_delta.y, seed_line_delta.x), + MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), + ) + } else { + ( + MajorMinor::new(seed_line_delta.x, seed_line_delta.y), + MajorMinor::new(seed_line_direction.x_axis(), seed_line_direction.y_axis()), + ) }; - let mut point = start; + let (parallel_delta, parallel_step) = if parallel_delta.y.abs() >= parallel_delta.x.abs() { + ( + MajorMinor::new(parallel_delta.y, parallel_delta.x), + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), + ) + } else { + ( + MajorMinor::new(parallel_delta.x, parallel_delta.y), + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), + ) + }; - // Base line skeleton - parallel_line(point, line, step, delta, 0, Rgb888::MAGENTA, display)?; + let dx = seed_line_delta.major.abs(); + let dy = seed_line_delta.minor.abs(); - let dx = pdelta.major.abs(); - let dy = pdelta.minor.abs(); + let pdx = parallel_delta.major.abs(); + let pdy = parallel_delta.minor.abs(); let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; let length = dx + 1; - let mut error = 0; + let mut seed_line_error = 0; // Perpendicular error or "phase" - let mut perp_error = 0; + let mut parallel_error = 0; + + let p_e_minor = -2 * pdx; + let p_e_major = 2 * pdy; + + println!("==="); - for _i in 0..width { + for _i in 0..length { Pixel(point, Rgb888::WHITE).draw(display)?; + dbg!(_i, parallel_error); + parallel_line( point, line, - step, - delta, - perp_error, + parallel_step, + parallel_delta, + // Negated because the parallel lines are off to the left of the seed line + -parallel_error, Rgb888::MAGENTA, display, )?; - if error > threshold { - point += pstep.minor; - error += e_minor; + if seed_line_error > threshold { + point += seed_line_step.minor; + seed_line_error += e_minor; + + if parallel_error > threshold { + // Pixel(point, Rgb888::CYAN).draw(display)?; - if perp_error > threshold { - parallel_line( - point, - line, - step, - delta, - perp_error + e_minor + e_major, - Rgb888::CYAN, - display, - )?; - - perp_error += e_minor; + // parallel_line( + // point, + // line, + // parallel_step, + // parallel_delta, + // parallel_error + e_minor + e_major, + // Rgb888::CYAN, + // display, + // )?; + + parallel_error += p_e_minor; } - perp_error += e_major; + parallel_error += p_e_major; } - error += e_major; - point += pstep.major; + point += seed_line_step.major; + seed_line_error += e_major; } Ok(()) From 7a16787779d0d402038cd0e875155914c43080f7 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 6 Dec 2022 22:51:20 +0000 Subject: [PATCH 072/188] WIP: Attempting to draw extra step lines --- debug-tools/examples/line-no-aa-parallel.rs | 38 +++++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index b617805..c51f70e 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -169,20 +169,21 @@ fn thickline( let p_e_minor = -2 * pdx; let p_e_major = 2 * pdy; - println!("==="); + let flip = if seed_line_step.minor == -parallel_step.major { + -1 + } else { + 1 + }; for _i in 0..length { Pixel(point, Rgb888::WHITE).draw(display)?; - dbg!(_i, parallel_error); - parallel_line( point, line, parallel_step, parallel_delta, - // Negated because the parallel lines are off to the left of the seed line - -parallel_error, + parallel_error * flip, Rgb888::MAGENTA, display, )?; @@ -194,15 +195,15 @@ fn thickline( if parallel_error > threshold { // Pixel(point, Rgb888::CYAN).draw(display)?; - // parallel_line( - // point, - // line, - // parallel_step, - // parallel_delta, - // parallel_error + e_minor + e_major, - // Rgb888::CYAN, - // display, - // )?; + parallel_line( + point, + line, + parallel_step, + parallel_delta, + (parallel_error + p_e_minor + p_e_major) * flip, + Rgb888::CYAN, + display, + )?; parallel_error += p_e_minor; } @@ -210,10 +211,17 @@ fn thickline( parallel_error += p_e_major; } - point += seed_line_step.major; + point += seed_line_step.major /* * 3*/; seed_line_error += e_major; } + // Pixel(line.start, Rgb888::RED).draw(display)?; + + // seed_line + // .perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::YELLOW, 1)) + // .draw(display)?; + Ok(()) } From afd3b5ced9dd7138e9c08da42f6891e02e3885d7 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 7 Dec 2022 20:55:20 +0000 Subject: [PATCH 073/188] =?UTF-8?q?WIP:=20Draw=20extra=20lines,=20but=20wi?= =?UTF-8?q?th=20some=20bugs=20around=2045=C2=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- debug-tools/examples/line-no-aa-parallel.rs | 59 ++++++++++++++------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index c51f70e..cc2f6b0 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -56,11 +56,12 @@ fn thickline( display: &mut impl DrawTarget, line: Line, width: i32, + toggle: bool, + toggle2: bool, ) -> Result<(), std::convert::Infallible> { let Line { start, end } = line; let extents = line.extents(width as u32, StrokeOffset::None); - // The perpendicular starting edge of the line let seed_line = Line::new(extents.0.start, extents.1.start); @@ -166,6 +167,7 @@ fn thickline( // Perpendicular error or "phase" let mut parallel_error = 0; + let p_threshold = pdx - 2 * pdy; let p_e_minor = -2 * pdx; let p_e_major = 2 * pdy; @@ -176,7 +178,7 @@ fn thickline( }; for _i in 0..length { - Pixel(point, Rgb888::WHITE).draw(display)?; + // Pixel(point, Rgb888::WHITE).draw(display)?; parallel_line( point, @@ -192,18 +194,28 @@ fn thickline( point += seed_line_step.minor; seed_line_error += e_minor; - if parallel_error > threshold { - // Pixel(point, Rgb888::CYAN).draw(display)?; - - parallel_line( - point, - line, - parallel_step, - parallel_delta, - (parallel_error + p_e_minor + p_e_major) * flip, - Rgb888::CYAN, - display, - )?; + if parallel_error > p_threshold { + let p = if flip == 1 { + point + } else { + // Put point on other side of the line + // FIXME: This is such a hack... + point - seed_line_step.minor + seed_line_step.major + }; + + if toggle { + Pixel(p, Rgb888::CYAN).draw(display)?; + + parallel_line( + point, + line, + parallel_step, + parallel_delta, + (parallel_error + p_e_minor + p_e_major) * flip, + Rgb888::CYAN, + display, + )?; + } parallel_error += p_e_minor; } @@ -211,15 +223,14 @@ fn thickline( parallel_error += p_e_major; } - point += seed_line_step.major /* * 3*/; + point += seed_line_step.major/* * 3*/; seed_line_error += e_major; } // Pixel(line.start, Rgb888::RED).draw(display)?; // seed_line - // .perpendicular() - // .into_styled(PrimitiveStyle::with_stroke(Rgb888::YELLOW, 1)) + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, 1)) // .draw(display)?; Ok(()) @@ -264,6 +275,8 @@ struct LineDebug { start: Point, end: Point, stroke_width: u32, + toggle: bool, + toggle2: bool, } impl App for LineDebug { @@ -281,6 +294,8 @@ impl App for LineDebug { end, // end: start + Point::new(100, 0), stroke_width: 10, + toggle: true, + toggle2: false, } } @@ -289,6 +304,8 @@ impl App for LineDebug { Parameter::new("start", &mut self.start), Parameter::new("end", &mut self.end), Parameter::new("stroke", &mut self.stroke_width), + Parameter::new("toggle", &mut self.toggle), + Parameter::new("toggle 2", &mut self.toggle2), ] } @@ -304,7 +321,13 @@ impl App for LineDebug { let _mock_display: MockDisplay = MockDisplay::new(); - thickline(display, Line::new(self.start, self.end), width)?; + thickline( + display, + Line::new(self.start, self.end), + width, + self.toggle, + self.toggle2, + )?; // let l = Line::new(self.start, self.end); From cab035fd40e2415b00876870a7a641fcd7371f07 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 7 Dec 2022 21:19:20 +0000 Subject: [PATCH 074/188] WIP: Correct extra line drawing, but broken width/offset --- debug-tools/examples/line-no-aa-parallel.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index cc2f6b0..22233f8 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -61,9 +61,11 @@ fn thickline( ) -> Result<(), std::convert::Infallible> { let Line { start, end } = line; - let extents = line.extents(width as u32, StrokeOffset::None); - // The perpendicular starting edge of the line - let seed_line = Line::new(extents.0.start, extents.1.start); + // let extents = line.extents(width as u32, StrokeOffset::None); + // // The perpendicular starting edge of the line + // let seed_line = Line::new(extents.0.start, extents.1.start); + + let seed_line = line.perpendicular(); let parallel_delta = line.end - line.start; let parallel_step = Point::new( @@ -177,7 +179,8 @@ fn thickline( 1 }; - for _i in 0..length { + // TODO: Proper thickness calculation + for i in 0..width { // Pixel(point, Rgb888::WHITE).draw(display)?; parallel_line( From 6ca78e70c595d03dff57dcd4a4a5917028436806 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 7 Dec 2022 21:55:29 +0000 Subject: [PATCH 075/188] WIP: Fix line width calculation using integers --- debug-tools/examples/line-no-aa-parallel.rs | 22 ++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 22233f8..d404ed0 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -169,9 +169,9 @@ fn thickline( // Perpendicular error or "phase" let mut parallel_error = 0; - let p_threshold = pdx - 2 * pdy; - let p_e_minor = -2 * pdx; - let p_e_major = 2 * pdy; + // let p_threshold = pdx - 2 * pdy; + // let p_e_minor = -2 * pdx; + // let p_e_major = 2 * pdy; let flip = if seed_line_step.minor == -parallel_step.major { -1 @@ -179,8 +179,10 @@ fn thickline( 1 }; - // TODO: Proper thickness calculation - for i in 0..width { + let thickness_threshold = (width * 2).pow(2) * line.delta().length_squared(); + let mut thickness_accumulator = 0i32; + + while thickness_accumulator.pow(2) <= thickness_threshold { // Pixel(point, Rgb888::WHITE).draw(display)?; parallel_line( @@ -196,8 +198,9 @@ fn thickline( if seed_line_error > threshold { point += seed_line_step.minor; seed_line_error += e_minor; + thickness_accumulator += 2 * dy; - if parallel_error > p_threshold { + if parallel_error > threshold { let p = if flip == 1 { point } else { @@ -214,20 +217,21 @@ fn thickline( line, parallel_step, parallel_delta, - (parallel_error + p_e_minor + p_e_major) * flip, + (parallel_error + e_minor + e_major) * flip, Rgb888::CYAN, display, )?; } - parallel_error += p_e_minor; + parallel_error += e_minor; } - parallel_error += p_e_major; + parallel_error += e_major; } point += seed_line_step.major/* * 3*/; seed_line_error += e_major; + thickness_accumulator += 2 * dx; } // Pixel(line.start, Rgb888::RED).draw(display)?; From e613fd8e8aee5d2f7959bdd17695605eb7f681cd Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 7 Dec 2022 22:12:32 +0000 Subject: [PATCH 076/188] WIP: Correct line offset This is unstable though. I think I need to iterate through the left/right lines like e-g does. --- debug-tools/examples/line-no-aa-parallel.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index d404ed0..247ef9f 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -61,11 +61,11 @@ fn thickline( ) -> Result<(), std::convert::Infallible> { let Line { start, end } = line; - // let extents = line.extents(width as u32, StrokeOffset::None); - // // The perpendicular starting edge of the line - // let seed_line = Line::new(extents.0.start, extents.1.start); + let extents = line.extents(width as u32, StrokeOffset::None); + // The perpendicular starting edge of the line + let extents_line = Line::new(extents.0.start, extents.1.start); - let seed_line = line.perpendicular(); + let seed_line = line.right_perpendicular(); let parallel_delta = line.end - line.start; let parallel_step = Point::new( @@ -119,7 +119,7 @@ fn thickline( // } // }; - let mut point = seed_line.start; + let mut point = extents_line.start; // Base line skeleton // parallel_line(point, line, step, delta, 0, Rgb888::MAGENTA, display)?; From f42cdeb19c15ffe7f080de8fa10effb460b9ae8c Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 7 Dec 2022 22:25:15 +0000 Subject: [PATCH 077/188] WIP: Draw legit line below new one --- debug-tools/examples/line-no-aa-parallel.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 247ef9f..f309a7c 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -236,9 +236,9 @@ fn thickline( // Pixel(line.start, Rgb888::RED).draw(display)?; - // seed_line - // .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, 1)) - // .draw(display)?; + line.translate(Point::new(0, 20)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) + .draw(display)?; Ok(()) } From 15fe073773ba0569a316714d64e28bf2fabc3484 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 12 Dec 2022 17:00:46 +0000 Subject: [PATCH 078/188] WIP: Walk line out on alternating sides --- debug-tools/examples/line-no-aa-parallel.rs | 144 +++++++++++++------- 1 file changed, 94 insertions(+), 50 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index f309a7c..242e4c6 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -59,13 +59,17 @@ fn thickline( toggle: bool, toggle2: bool, ) -> Result<(), std::convert::Infallible> { + if width == 0 { + return Ok(()); + } + let Line { start, end } = line; - let extents = line.extents(width as u32, StrokeOffset::None); - // The perpendicular starting edge of the line - let extents_line = Line::new(extents.0.start, extents.1.start); + // let extents = line.extents(width as u32, StrokeOffset::None); + // // The perpendicular starting edge of the line + // let extents_line = Line::new(extents.0.start, extents.1.start); - let seed_line = line.right_perpendicular(); + let seed_line = line.perpendicular(); let parallel_delta = line.end - line.start; let parallel_step = Point::new( @@ -119,10 +123,9 @@ fn thickline( // } // }; - let mut point = extents_line.start; - - // Base line skeleton - // parallel_line(point, line, step, delta, 0, Rgb888::MAGENTA, display)?; + // TODO: Skip drawing first part of line twice. Need to offset the thickness accumulator too. + let mut point1 = line.start; + let mut point2 = line.start; let seed_line_delta = seed_line.end - seed_line.start; @@ -155,23 +158,21 @@ fn thickline( ) }; + // Don't draw line skeleton twice + point2 -= seed_line_step.major; + let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); - let pdx = parallel_delta.major.abs(); - let pdy = parallel_delta.minor.abs(); - let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; let length = dx + 1; let mut seed_line_error = 0; + let mut seed_line_error2 = e_major; // Perpendicular error or "phase" let mut parallel_error = 0; - - // let p_threshold = pdx - 2 * pdy; - // let p_e_minor = -2 * pdx; - // let p_e_major = 2 * pdy; + let mut parallel_error2 = 0; let flip = if seed_line_step.minor == -parallel_step.major { -1 @@ -180,63 +181,94 @@ fn thickline( }; let thickness_threshold = (width * 2).pow(2) * line.delta().length_squared(); - let mut thickness_accumulator = 0i32; + let mut thickness_accumulator = 2 * dx; + + // Bias to one side of the line + // TODO: Which side actually is this lol + // TODO: The current extents() function needs to respect this too, as well as stroke offset + let mut is_left = false; while thickness_accumulator.pow(2) <= thickness_threshold { - // Pixel(point, Rgb888::WHITE).draw(display)?; + let (mut point, inc, c, seed_line_error, parallel_error, idk) = if is_left { + ( + &mut point2, + MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), + Rgb888::YELLOW, + &mut seed_line_error2, + &mut parallel_error2, + -flip, + ) + } else { + ( + &mut point1, + seed_line_step, + Rgb888::MAGENTA, + &mut seed_line_error, + &mut parallel_error, + flip, + ) + }; + + is_left = !is_left; parallel_line( - point, + *point, line, parallel_step, parallel_delta, - parallel_error * flip, - Rgb888::MAGENTA, + *parallel_error * idk, + c, + false, + 0, display, )?; - if seed_line_error > threshold { - point += seed_line_step.minor; - seed_line_error += e_minor; + if *seed_line_error > threshold { + *point += inc.minor; + *seed_line_error += e_minor; thickness_accumulator += 2 * dy; - if parallel_error > threshold { - let p = if flip == 1 { - point - } else { - // Put point on other side of the line - // FIXME: This is such a hack... - point - seed_line_step.minor + seed_line_step.major - }; - + if *parallel_error > threshold { if toggle { - Pixel(p, Rgb888::CYAN).draw(display)?; - - parallel_line( - point, - line, - parallel_step, - parallel_delta, - (parallel_error + e_minor + e_major) * flip, - Rgb888::CYAN, - display, - )?; + if thickness_accumulator.pow(2) <= thickness_threshold { + // FIXME: This entire thing is such a hack... + let (p, e) = if flip == 1 { + (*point, (*parallel_error + e_major + e_minor) * idk) + } else { + // Put point on other side of the line + (*point + inc.major - inc.minor, *parallel_error * idk) + }; + + // Pixel(p, Rgb888::CYAN).draw(display)?; + + parallel_line( + p, + line, + parallel_step, + parallel_delta, + e, + Rgb888::CYAN, + !is_left, + -1, + display, + )?; + } } - parallel_error += e_minor; + *parallel_error += e_minor; } - parallel_error += e_major; + *parallel_error += e_major; } - point += seed_line_step.major/* * 3*/; - seed_line_error += e_major; + *point += inc.major/* * 3*/; + *seed_line_error += e_major; thickness_accumulator += 2 * dx; } // Pixel(line.start, Rgb888::RED).draw(display)?; - line.translate(Point::new(0, 20)) + line.translate(Point::new(0, width + 5)) .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) .draw(display)?; @@ -250,6 +282,8 @@ fn parallel_line( delta: MajorMinor, start_error: i32, c: Rgb888, + skip_first: bool, + last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { let mut point = start; @@ -260,10 +294,20 @@ fn parallel_line( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; - let length = dx + 1; + let mut length = dx + 1; let mut error = start_error; - for _i in 0..length { + if skip_first { + error += e_major; + point += step.major; + + if error > threshold { + point += step.minor; + error += e_minor; + } + } + + for _i in 0..(length + last_offset) { Pixel(point, c).draw(display)?; if error > threshold { From 20e693c3f1d9e8d8c72b5c26ecfecf1b29553ddf Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 12 Dec 2022 19:51:56 +0000 Subject: [PATCH 079/188] Improve some variable names --- debug-tools/examples/line-no-aa-parallel.rs | 27 ++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 242e4c6..b774d57 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -124,8 +124,8 @@ fn thickline( // }; // TODO: Skip drawing first part of line twice. Need to offset the thickness accumulator too. - let mut point1 = line.start; - let mut point2 = line.start; + let mut point_left = line.start; + let mut point_right = line.start; let seed_line_delta = seed_line.end - seed_line.start; @@ -159,7 +159,7 @@ fn thickline( }; // Don't draw line skeleton twice - point2 -= seed_line_step.major; + point_right -= seed_line_step.major; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -169,10 +169,10 @@ fn thickline( let e_major = 2 * dy; let length = dx + 1; let mut seed_line_error = 0; - let mut seed_line_error2 = e_major; + let mut seed_line_error_right = e_major; // Perpendicular error or "phase" let mut parallel_error = 0; - let mut parallel_error2 = 0; + let mut parallel_error_right = 0; let flip = if seed_line_step.minor == -parallel_step.major { -1 @@ -184,23 +184,22 @@ fn thickline( let mut thickness_accumulator = 2 * dx; // Bias to one side of the line - // TODO: Which side actually is this lol // TODO: The current extents() function needs to respect this too, as well as stroke offset - let mut is_left = false; + let mut is_right = false; while thickness_accumulator.pow(2) <= thickness_threshold { - let (mut point, inc, c, seed_line_error, parallel_error, idk) = if is_left { + let (mut point, inc, c, seed_line_error, parallel_error, idk) = if is_right { ( - &mut point2, + &mut point_right, MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), Rgb888::YELLOW, - &mut seed_line_error2, - &mut parallel_error2, + &mut seed_line_error_right, + &mut parallel_error_right, -flip, ) } else { ( - &mut point1, + &mut point_left, seed_line_step, Rgb888::MAGENTA, &mut seed_line_error, @@ -209,7 +208,7 @@ fn thickline( ) }; - is_left = !is_left; + is_right = !is_right; parallel_line( *point, @@ -248,7 +247,7 @@ fn thickline( parallel_delta, e, Rgb888::CYAN, - !is_left, + !is_right, -1, display, )?; From e7f8a14bba006818954788d324ddc3781d04fd83 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 12 Dec 2022 19:55:32 +0000 Subject: [PATCH 080/188] Minor cleanup --- debug-tools/examples/line-no-aa-parallel.rs | 62 ++------------------- 1 file changed, 6 insertions(+), 56 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index b774d57..dabe6de 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -65,10 +65,6 @@ fn thickline( let Line { start, end } = line; - // let extents = line.extents(width as u32, StrokeOffset::None); - // // The perpendicular starting edge of the line - // let extents_line = Line::new(extents.0.start, extents.1.start); - let seed_line = line.perpendicular(); let parallel_delta = line.end - line.start; @@ -77,53 +73,6 @@ fn thickline( if parallel_delta.y >= 0 { 1 } else { -1 }, ); - // let (delta, step, pdelta, pstep) = { - // let delta = end - start; - - // let direction = Point::new( - // if delta.x >= 0 { 1 } else { -1 }, - // if delta.y >= 0 { 1 } else { -1 }, - // ); - - // let perp_delta = line.perpendicular(); - // let perp_delta = perp_delta.end - perp_delta.start; - - // let perp_direction = Point::new( - // if perp_delta.x >= 0 { 1 } else { -1 }, - // if perp_delta.y >= 0 { 1 } else { -1 }, - // ); - - // let (perp_delta, perp_direction) = if perp_delta.y.abs() >= perp_delta.x.abs() { - // ( - // MajorMinor::new(perp_delta.y, perp_delta.x), - // MajorMinor::new(perp_direction.y_axis(), perp_direction.x_axis()), - // ) - // } else { - // ( - // MajorMinor::new(perp_delta.x, perp_delta.y), - // MajorMinor::new(perp_direction.x_axis(), perp_direction.y_axis()), - // ) - // }; - - // // Determine major and minor directions. - // if delta.y.abs() >= delta.x.abs() { - // ( - // MajorMinor::new(delta.y, delta.x), - // MajorMinor::new(direction.y_axis(), direction.x_axis()), - // perp_delta, - // perp_direction, - // ) - // } else { - // ( - // MajorMinor::new(delta.x, delta.y), - // MajorMinor::new(direction.x_axis(), direction.y_axis()), - // perp_delta, - // perp_direction, - // ) - // } - // }; - - // TODO: Skip drawing first part of line twice. Need to offset the thickness accumulator too. let mut point_left = line.start; let mut point_right = line.start; @@ -167,7 +116,6 @@ fn thickline( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; - let length = dx + 1; let mut seed_line_error = 0; let mut seed_line_error_right = e_major; // Perpendicular error or "phase" @@ -185,7 +133,7 @@ fn thickline( // Bias to one side of the line // TODO: The current extents() function needs to respect this too, as well as stroke offset - let mut is_right = false; + let mut is_right = true; while thickness_accumulator.pow(2) <= thickness_threshold { let (mut point, inc, c, seed_line_error, parallel_error, idk) = if is_right { @@ -208,7 +156,7 @@ fn thickline( ) }; - is_right = !is_right; + // is_right = !is_right; parallel_line( *point, @@ -247,8 +195,10 @@ fn thickline( parallel_delta, e, Rgb888::CYAN, - !is_right, - -1, + false, + 0, + // !is_right, + // -1, display, )?; } From c6cbb2d399021b10a3f65ae0c3b3a1eb76f23bd3 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 12 Dec 2022 20:45:37 +0000 Subject: [PATCH 081/188] Fix extra line skip/length offset --- debug-tools/examples/line-no-aa-parallel.rs | 41 ++++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index dabe6de..16b1a49 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -57,7 +57,7 @@ fn thickline( line: Line, width: i32, toggle: bool, - toggle2: bool, + last_offset: i32, ) -> Result<(), std::convert::Infallible> { if width == 0 { return Ok(()); @@ -135,6 +135,8 @@ fn thickline( // TODO: The current extents() function needs to respect this too, as well as stroke offset let mut is_right = true; + dbg!(flip); + while thickness_accumulator.pow(2) <= thickness_threshold { let (mut point, inc, c, seed_line_error, parallel_error, idk) = if is_right { ( @@ -156,8 +158,6 @@ fn thickline( ) }; - // is_right = !is_right; - parallel_line( *point, line, @@ -183,7 +183,7 @@ fn thickline( (*point, (*parallel_error + e_major + e_minor) * idk) } else { // Put point on other side of the line - (*point + inc.major - inc.minor, *parallel_error * idk) + (*point, (*parallel_error + e_major + e_minor) * idk) }; // Pixel(p, Rgb888::CYAN).draw(display)?; @@ -195,8 +195,15 @@ fn thickline( parallel_delta, e, Rgb888::CYAN, - false, - 0, + // For the side where the Bresenham params step "away" from the line + // body, skip the first pixel to prevent jaggies on the starting edge. + !is_right && flip != 1, + // last_offset, + if is_right && flip == -1 || !is_right && flip == 1 { + -1 + } else { + 0 + }, // !is_right, // -1, display, @@ -213,11 +220,13 @@ fn thickline( *point += inc.major/* * 3*/; *seed_line_error += e_major; thickness_accumulator += 2 * dx; + + is_right = !is_right; } // Pixel(line.start, Rgb888::RED).draw(display)?; - line.translate(Point::new(0, width + 5)) + line.translate(Point::new(0, width * 2 + 5)) .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) .draw(display)?; @@ -232,7 +241,7 @@ fn parallel_line( start_error: i32, c: Rgb888, skip_first: bool, - last_offset: i32, + mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { let mut point = start; @@ -247,13 +256,17 @@ fn parallel_line( let mut error = start_error; if skip_first { - error += e_major; - point += step.major; + // Some of the length was consumed by this initial skip iteration. If this is omitted, the + // line will be drawn 1px too long. + last_offset -= 1; if error > threshold { point += step.minor; error += e_minor; } + + error += e_major; + point += step.major; } for _i in 0..(length + last_offset) { @@ -276,7 +289,7 @@ struct LineDebug { end: Point, stroke_width: u32, toggle: bool, - toggle2: bool, + last_offset: i32, } impl App for LineDebug { @@ -295,7 +308,7 @@ impl App for LineDebug { // end: start + Point::new(100, 0), stroke_width: 10, toggle: true, - toggle2: false, + last_offset: 0, } } @@ -305,7 +318,7 @@ impl App for LineDebug { Parameter::new("end", &mut self.end), Parameter::new("stroke", &mut self.stroke_width), Parameter::new("toggle", &mut self.toggle), - Parameter::new("toggle 2", &mut self.toggle2), + Parameter::new("last offset", &mut self.last_offset), ] } @@ -326,7 +339,7 @@ impl App for LineDebug { Line::new(self.start, self.end), width, self.toggle, - self.toggle2, + self.last_offset, )?; // let l = Line::new(self.start, self.end); From c029e340f18e83a7720f0afdf00e2464db068096 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 13 Dec 2022 18:11:56 +0000 Subject: [PATCH 082/188] Cleanup --- debug-tools/examples/line-no-aa-parallel.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 16b1a49..9ff880e 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -129,14 +129,14 @@ fn thickline( }; let thickness_threshold = (width * 2).pow(2) * line.delta().length_squared(); + // Add the first line drawn to the thickness. If this is left at zero, an extra line will be + // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * dx; // Bias to one side of the line // TODO: The current extents() function needs to respect this too, as well as stroke offset let mut is_right = true; - dbg!(flip); - while thickness_accumulator.pow(2) <= thickness_threshold { let (mut point, inc, c, seed_line_error, parallel_error, idk) = if is_right { ( @@ -178,27 +178,20 @@ fn thickline( if *parallel_error > threshold { if toggle { if thickness_accumulator.pow(2) <= thickness_threshold { - // FIXME: This entire thing is such a hack... - let (p, e) = if flip == 1 { - (*point, (*parallel_error + e_major + e_minor) * idk) - } else { - // Put point on other side of the line - (*point, (*parallel_error + e_major + e_minor) * idk) - }; - // Pixel(p, Rgb888::CYAN).draw(display)?; parallel_line( - p, + *point, line, parallel_step, parallel_delta, - e, + // TODO: Why do I need idk here? + (*parallel_error + e_major + e_minor) * idk, Rgb888::CYAN, // For the side where the Bresenham params step "away" from the line // body, skip the first pixel to prevent jaggies on the starting edge. !is_right && flip != 1, - // last_offset, + // TODO: Explain or at least understand the haxx here if is_right && flip == -1 || !is_right && flip == 1 { -1 } else { From 16835b190ef6bd45dc8f807da259bc8aaab22eeb Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 13 Dec 2022 18:16:19 +0000 Subject: [PATCH 083/188] Remove weird "idk" variable --- debug-tools/examples/line-no-aa-parallel.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 9ff880e..974746d 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -138,7 +138,7 @@ fn thickline( let mut is_right = true; while thickness_accumulator.pow(2) <= thickness_threshold { - let (mut point, inc, c, seed_line_error, parallel_error, idk) = if is_right { + let (mut point, inc, c, seed_line_error, parallel_error, flip) = if is_right { ( &mut point_right, MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), @@ -163,7 +163,7 @@ fn thickline( line, parallel_step, parallel_delta, - *parallel_error * idk, + *parallel_error * flip, c, false, 0, @@ -186,17 +186,13 @@ fn thickline( parallel_step, parallel_delta, // TODO: Why do I need idk here? - (*parallel_error + e_major + e_minor) * idk, + (*parallel_error + e_major + e_minor) * flip, Rgb888::CYAN, // For the side where the Bresenham params step "away" from the line // body, skip the first pixel to prevent jaggies on the starting edge. !is_right && flip != 1, // TODO: Explain or at least understand the haxx here - if is_right && flip == -1 || !is_right && flip == 1 { - -1 - } else { - 0 - }, + if flip == 1 { -1 } else { 0 }, // !is_right, // -1, display, From 4549d8234fba9bae89fb1ad7ac3d44e73db174e9 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 14 Dec 2022 10:20:22 +0000 Subject: [PATCH 084/188] Start simplifying extra lines --- debug-tools/examples/line-no-aa-parallel.rs | 52 +++++++++++---------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 974746d..28b6e4f 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -122,6 +122,8 @@ fn thickline( let mut parallel_error = 0; let mut parallel_error_right = 0; + // This fixes the phasing for parallel lines on the left side of the base line for the octants + // where the line perpendicular moves "away" from the line body. let flip = if seed_line_step.minor == -parallel_step.major { -1 } else { @@ -142,22 +144,25 @@ fn thickline( ( &mut point_right, MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), - Rgb888::YELLOW, + Rgb888::CSS_DARK_GOLDENROD, &mut seed_line_error_right, &mut parallel_error_right, + // Fix phasing for parallel lines on the right hand side of the base line -flip, ) } else { ( &mut point_left, seed_line_step, - Rgb888::MAGENTA, + Rgb888::CSS_SALMON, &mut seed_line_error, &mut parallel_error, flip, ) }; + // Pixel(*point, c).draw(display)?; + parallel_line( *point, line, @@ -178,25 +183,24 @@ fn thickline( if *parallel_error > threshold { if toggle { if thickness_accumulator.pow(2) <= thickness_threshold { - // Pixel(p, Rgb888::CYAN).draw(display)?; - - parallel_line( - *point, - line, - parallel_step, - parallel_delta, - // TODO: Why do I need idk here? - (*parallel_error + e_major + e_minor) * flip, - Rgb888::CYAN, - // For the side where the Bresenham params step "away" from the line - // body, skip the first pixel to prevent jaggies on the starting edge. - !is_right && flip != 1, - // TODO: Explain or at least understand the haxx here - if flip == 1 { -1 } else { 0 }, - // !is_right, - // -1, - display, - )?; + // Pixel(*point, Rgb888::CYAN).draw(display)?; + + // parallel_line( + // *point, + // line, + // parallel_step, + // parallel_delta, + // *parallel_error + e_minor + e_major, + // Rgb888::CYAN, + // // For the side where the Bresenham params step "away" from the line + // // body, skip the first pixel to prevent jaggies on the starting edge. + // // !is_right && flip != 1, + // // TODO: Explain or at least understand the haxx here + // // if flip == 1 { -1 } else { 0 }, + // false, + // 0, + // display, + // )?; } } @@ -215,9 +219,9 @@ fn thickline( // Pixel(line.start, Rgb888::RED).draw(display)?; - line.translate(Point::new(0, width * 2 + 5)) - .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) - .draw(display)?; + // line.translate(Point::new(0, width * 2 + 5)) + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) + // .draw(display)?; Ok(()) } From 6a6e3e92b1f3ccf17c22a1fb58d98be7cc9af5b2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 14 Dec 2022 10:21:16 +0000 Subject: [PATCH 085/188] Reintroduce correctly phased extra lines --- debug-tools/examples/line-no-aa-parallel.rs | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 28b6e4f..37e36f9 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -185,22 +185,22 @@ fn thickline( if thickness_accumulator.pow(2) <= thickness_threshold { // Pixel(*point, Rgb888::CYAN).draw(display)?; - // parallel_line( - // *point, - // line, - // parallel_step, - // parallel_delta, - // *parallel_error + e_minor + e_major, - // Rgb888::CYAN, - // // For the side where the Bresenham params step "away" from the line - // // body, skip the first pixel to prevent jaggies on the starting edge. - // // !is_right && flip != 1, - // // TODO: Explain or at least understand the haxx here - // // if flip == 1 { -1 } else { 0 }, - // false, - // 0, - // display, - // )?; + parallel_line( + *point, + line, + parallel_step, + parallel_delta, + (*parallel_error + e_minor + e_major) * flip, + Rgb888::CYAN, + // For the side where the Bresenham params step "away" from the line + // body, skip the first pixel to prevent jaggies on the starting edge. + // !is_right && flip != 1, + // TODO: Explain or at least understand the haxx here + // if flip == 1 { -1 } else { 0 }, + false, + 0, + display, + )?; } } From 4aa04c26ab8200002e4dca2fd18cd4839eb004f0 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 14 Dec 2022 10:42:03 +0000 Subject: [PATCH 086/188] Correct line algorithm! --- debug-tools/examples/line-no-aa-parallel.rs | 30 ++++++++++++--------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 37e36f9..42b1945 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -5,13 +5,11 @@ use embedded_graphics::{ prelude::*, primitives::{ common::{LineSide, LinearEquation}, - line::StrokeOffset, - Line, PrimitiveStyle, + Line, }, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; -use integer_sqrt::IntegerSquareRoot; #[derive(Debug, Copy, Clone, PartialEq)] enum LineOffset { @@ -124,7 +122,7 @@ fn thickline( // This fixes the phasing for parallel lines on the left side of the base line for the octants // where the line perpendicular moves "away" from the line body. - let flip = if seed_line_step.minor == -parallel_step.major { + let original_flip = if seed_line_step.minor == -parallel_step.major { -1 } else { 1 @@ -148,7 +146,7 @@ fn thickline( &mut seed_line_error_right, &mut parallel_error_right, // Fix phasing for parallel lines on the right hand side of the base line - -flip, + -original_flip, ) } else { ( @@ -157,7 +155,7 @@ fn thickline( Rgb888::CSS_SALMON, &mut seed_line_error, &mut parallel_error, - flip, + original_flip, ) }; @@ -192,13 +190,19 @@ fn thickline( parallel_delta, (*parallel_error + e_minor + e_major) * flip, Rgb888::CYAN, - // For the side where the Bresenham params step "away" from the line - // body, skip the first pixel to prevent jaggies on the starting edge. - // !is_right && flip != 1, - // TODO: Explain or at least understand the haxx here - // if flip == 1 { -1 } else { 0 }, - false, - 0, + // If we're on the side of the base line where the perpendicular + // Bresenham steps "into" the thick line body, skip the first extra + // line point as it's on the wrong side of the perpendicular and leads + // to a jagged edge. + original_flip == -1 && !is_right || original_flip == 1 && is_right, + if original_flip == -1 && !is_right || original_flip == 1 && is_right { + 0 + } else { + // Because the opposite side's extra lines start one step into the thick + // line body, we must reduce its total length by 1 to prevent jagged + // edges on the end edge of the line. + -1 + }, display, )?; } From 7372cabfe421bdf0bbada606e18e8f4c6dbf5170 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 14 Dec 2022 10:43:43 +0000 Subject: [PATCH 087/188] Configurable last offset --- debug-tools/examples/line-no-aa-parallel.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 42b1945..92e2887 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -169,7 +169,7 @@ fn thickline( *parallel_error * flip, c, false, - 0, + last_offset, display, )?; @@ -202,7 +202,7 @@ fn thickline( // line body, we must reduce its total length by 1 to prevent jagged // edges on the end edge of the line. -1 - }, + } + last_offset, display, )?; } From f4758ad34a0638dd416b073a37e198434bee43d4 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 15 Dec 2022 10:19:51 +0000 Subject: [PATCH 088/188] Basic single-side AA --- debug-tools/examples/line-no-aa-parallel.rs | 102 +++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 92e2887..12f34ab 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -189,7 +189,8 @@ fn thickline( parallel_step, parallel_delta, (*parallel_error + e_minor + e_major) * flip, - Rgb888::CYAN, + // Rgb888::CYAN, + c, // If we're on the side of the base line where the perpendicular // Bresenham steps "into" the thick line body, skip the first extra // line point as it's on the wrong side of the perpendicular and leads @@ -227,6 +228,105 @@ fn thickline( // .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) // .draw(display)?; + let (mut point, inc, c, seed_line_error, parallel_error, flip) = if is_right { + ( + &mut point_right, + MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), + Rgb888::CSS_DARK_GOLDENROD, + &mut seed_line_error_right, + &mut parallel_error_right, + // Fix phasing for parallel lines on the right hand side of the base line + -original_flip, + ) + } else { + ( + &mut point_left, + seed_line_step, + Rgb888::CSS_SALMON, + &mut seed_line_error, + &mut parallel_error, + original_flip, + ) + }; + + parallel_line_aa( + *point, + line, + parallel_step, + parallel_delta, + *parallel_error * flip, + c, + false, + last_offset, + display, + )?; + + Ok(()) +} + +fn parallel_line_aa( + start: Point, + line: Line, + step: MajorMinor, + delta: MajorMinor, + start_error: i32, + c: Rgb888, + skip_first: bool, + mut last_offset: i32, + display: &mut impl DrawTarget, +) -> Result<(), std::convert::Infallible> { + let mut point = start; + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let mut length = dx + 1; + let mut error = start_error; + + if skip_first { + // Some of the length was consumed by this initial skip iteration. If this is omitted, the + // line will be drawn 1px too long. + last_offset -= 1; + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + + for _i in 0..(length + last_offset) { + let bright = 1.0 - (-((error + threshold) as f32 / e_minor as f32)).max(0.0); + + // println!( + // "{error} : {threshold}, {e_major}, {e_minor}, {} {} | {}", + // e_major / 2, + // e_minor / 2, + // bright + // ); + + let c = Rgb888::new( + (bright * c.r() as f32) as u8, + (bright * c.g() as f32) as u8, + (bright * c.b() as f32) as u8, + ); + + Pixel(point, c).draw(display)?; + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + Ok(()) } From f874e17d5e52212700ea6ccc7d5b1fe34dd45c24 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 15 Dec 2022 11:18:04 +0000 Subject: [PATCH 089/188] WIP AA --- debug-tools/examples/line-no-aa-parallel.rs | 164 ++++++++++++-------- 1 file changed, 97 insertions(+), 67 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 12f34ab..80cb2f1 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -179,34 +179,32 @@ fn thickline( thickness_accumulator += 2 * dy; if *parallel_error > threshold { - if toggle { - if thickness_accumulator.pow(2) <= thickness_threshold { - // Pixel(*point, Rgb888::CYAN).draw(display)?; - - parallel_line( - *point, - line, - parallel_step, - parallel_delta, - (*parallel_error + e_minor + e_major) * flip, - // Rgb888::CYAN, - c, - // If we're on the side of the base line where the perpendicular - // Bresenham steps "into" the thick line body, skip the first extra - // line point as it's on the wrong side of the perpendicular and leads - // to a jagged edge. - original_flip == -1 && !is_right || original_flip == 1 && is_right, - if original_flip == -1 && !is_right || original_flip == 1 && is_right { - 0 - } else { - // Because the opposite side's extra lines start one step into the thick - // line body, we must reduce its total length by 1 to prevent jagged - // edges on the end edge of the line. - -1 - } + last_offset, - display, - )?; - } + if thickness_accumulator.pow(2) <= thickness_threshold { + // Pixel(*point, Rgb888::CYAN).draw(display)?; + + parallel_line( + *point, + line, + parallel_step, + parallel_delta, + (*parallel_error + e_minor + e_major) * flip, + // Rgb888::CYAN, + c, + // If we're on the side of the base line where the perpendicular + // Bresenham steps "into" the thick line body, skip the first extra + // line point as it's on the wrong side of the perpendicular and leads + // to a jagged edge. + original_flip == -1 && !is_right || original_flip == 1 && is_right, + if original_flip == -1 && !is_right || original_flip == 1 && is_right { + 0 + } else { + // Because the opposite side's extra lines start one step into the thick + // line body, we must reduce its total length by 1 to prevent jagged + // edges on the end edge of the line. + -1 + } + last_offset, + display, + )?; } *parallel_error += e_minor; @@ -228,38 +226,64 @@ fn thickline( // .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) // .draw(display)?; - let (mut point, inc, c, seed_line_error, parallel_error, flip) = if is_right { - ( - &mut point_right, - MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), - Rgb888::CSS_DARK_GOLDENROD, - &mut seed_line_error_right, - &mut parallel_error_right, - // Fix phasing for parallel lines on the right hand side of the base line - -original_flip, - ) - } else { - ( - &mut point_left, - seed_line_step, - Rgb888::CSS_SALMON, - &mut seed_line_error, - &mut parallel_error, - original_flip, - ) - }; + // let (mut point, inc, c, seed_line_error, parallel_error, flip) = if is_right { + // ( + // &mut point_right, + // MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), + // Rgb888::CSS_DARK_GOLDENROD, + // &mut seed_line_error_right, + // &mut parallel_error_right, + // // Fix phasing for parallel lines on the right hand side of the base line + // -original_flip, + // ) + // } else { + // ( + // &mut point_left, + // seed_line_step, + // Rgb888::CSS_SALMON, + // &mut seed_line_error, + // &mut parallel_error, + // original_flip, + // ) + // }; + + // let flip = if is_right { + // -original_flip + // } else { + // original_flip + // }; + let flip = original_flip; + + if toggle { + parallel_line_aa( + point_right, + line, + parallel_step, + parallel_delta, + // parallel_error_right * -flip, + parallel_error_right * -flip, + // Rgb888::CSS_DARK_GOLDENROD, + Rgb888::CYAN, + false, + flip == 1, + last_offset, + display, + )?; - parallel_line_aa( - *point, - line, - parallel_step, - parallel_delta, - *parallel_error * flip, - c, - false, - last_offset, - display, - )?; + parallel_line_aa( + point_left, + line, + parallel_step, + parallel_delta, + parallel_error * flip, + // Rgb888::CSS_SALMON, + Rgb888::CYAN, + false, + flip == -1, + last_offset, + display, + )?; + } Ok(()) } @@ -272,6 +296,7 @@ fn parallel_line_aa( start_error: i32, c: Rgb888, skip_first: bool, + invert: bool, mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { @@ -299,16 +324,21 @@ fn parallel_line_aa( error += e_major; point += step.major; } + println!("---"); for _i in 0..(length + last_offset) { - let bright = 1.0 - (-((error + threshold) as f32 / e_minor as f32)).max(0.0); - - // println!( - // "{error} : {threshold}, {e_major}, {e_minor}, {} {} | {}", - // e_major / 2, - // e_minor / 2, - // bright - // ); + let bright = if !invert { + 1.0 - (-((error + threshold) as f32 / e_minor as f32)).max(0.0) + } else { + (-((error + threshold) as f32 / e_minor as f32)).max(0.0) + }; + + println!( + "{error} : {threshold}, {e_major}, {e_minor}, {} {} | {}", + e_major / 2, + e_minor / 2, + bright + ); let c = Rgb888::new( (bright * c.r() as f32) as u8, From 4e060268c1bde3e942f70a9157365ba680a69803 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 15 Dec 2022 13:15:43 +0000 Subject: [PATCH 090/188] Fix(?) weirdnesses around gradients = 1 --- debug-tools/examples/line-no-aa-parallel.rs | 59 ++++++++++----------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 80cb2f1..6a97821 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -255,20 +255,20 @@ fn thickline( let flip = original_flip; if toggle { - parallel_line_aa( - point_right, - line, - parallel_step, - parallel_delta, - // parallel_error_right * -flip, - parallel_error_right * -flip, - // Rgb888::CSS_DARK_GOLDENROD, - Rgb888::CYAN, - false, - flip == 1, - last_offset, - display, - )?; + // parallel_line_aa( + // point_right, + // line, + // parallel_step, + // parallel_delta, + // // parallel_error_right * -flip, + // parallel_error_right * -flip, + // // Rgb888::CSS_DARK_GOLDENROD, + // Rgb888::CYAN, + // false, + // flip == 1, + // last_offset, + // display, + // )?; parallel_line_aa( point_left, @@ -324,26 +324,23 @@ fn parallel_line_aa( error += e_major; point += step.major; } - println!("---"); + println!("--- {invert}"); - for _i in 0..(length + last_offset) { - let bright = if !invert { - 1.0 - (-((error + threshold) as f32 / e_minor as f32)).max(0.0) - } else { - (-((error + threshold) as f32 / e_minor as f32)).max(0.0) - }; + let grad = (dy as f32 / dx as f32); - println!( - "{error} : {threshold}, {e_major}, {e_minor}, {} {} | {}", - e_major / 2, - e_minor / 2, - bright - ); + let grad_step = (dy as f32 / dx as f32) * (1.0 - grad / 2.0); + let reset = 0.0; + let mut bright = 0.0f32; + + dbg!(grad_step); + + for _i in 0..(length + last_offset) { + let b = bright; let c = Rgb888::new( - (bright * c.r() as f32) as u8, - (bright * c.g() as f32) as u8, - (bright * c.b() as f32) as u8, + (b * c.r() as f32) as u8, + (b * c.g() as f32) as u8, + (b * c.b() as f32) as u8, ); Pixel(point, c).draw(display)?; @@ -351,10 +348,12 @@ fn parallel_line_aa( if error > threshold { point += step.minor; error += e_minor; + bright = reset; } error += e_major; point += step.major; + bright += grad_step; } Ok(()) From 5377cf8ef7a5427207ab12d838b6f57ca4f74309 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 15 Dec 2022 13:33:44 +0000 Subject: [PATCH 091/188] AA tweaks --- debug-tools/examples/line-no-aa-parallel.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 6a97821..5f484f8 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -276,8 +276,8 @@ fn thickline( parallel_step, parallel_delta, parallel_error * flip, - // Rgb888::CSS_SALMON, - Rgb888::CYAN, + Rgb888::CSS_SALMON, + // Rgb888::CYAN, false, flip == -1, last_offset, @@ -324,15 +324,14 @@ fn parallel_line_aa( error += e_major; point += step.major; } - println!("--- {invert}"); - let grad = (dy as f32 / dx as f32); - - let grad_step = (dy as f32 / dx as f32) * (1.0 - grad / 2.0); + let grad_step = (dy as f32 / dx as f32); + // The closer we get to diagonal, the more the gradient must be scaled so the max value becomes + // 0.5 + let grad_step = grad_step * (1.0 - grad_step / 2.0); let reset = 0.0; - let mut bright = 0.0f32; - - dbg!(grad_step); + // We're in the center of a pixel at the start of the line + let mut bright = grad_step * 0.5; for _i in 0..(length + last_offset) { let b = bright; From 806b84079570ae9f926fd09620eb5c21895e488f Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 15 Dec 2022 14:09:15 +0000 Subject: [PATCH 092/188] Integer-only AA! --- debug-tools/examples/line-no-aa-parallel.rs | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 5f484f8..a3ee483 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -325,21 +325,21 @@ fn parallel_line_aa( point += step.major; } - let grad_step = (dy as f32 / dx as f32); - // The closer we get to diagonal, the more the gradient must be scaled so the max value becomes - // 0.5 - let grad_step = grad_step * (1.0 - grad_step / 2.0); - let reset = 0.0; + let grad_step_int = (dy * 255) / dx; + // The gradient step is scaled based on how close to the diagonal we are. Horizontal/vertical + // uses a scale of 1.0, trending to a scale of 0.5 for the diagonal. This allows diagonal lines + // to have proper AA with each AA pixel having 0.5 brightness. + let grad_step_int = (grad_step_int * 255) / (255 + grad_step_int); // We're in the center of a pixel at the start of the line - let mut bright = grad_step * 0.5; + let mut bright_int: i32 = grad_step_int; for _i in 0..(length + last_offset) { - let b = bright; + let b = bright_int; let c = Rgb888::new( - (b * c.r() as f32) as u8, - (b * c.g() as f32) as u8, - (b * c.b() as f32) as u8, + ((bright_int * c.r() as i32) / 255) as u8, + ((bright_int * c.g() as i32) / 255) as u8, + ((bright_int * c.b() as i32) / 255) as u8, ); Pixel(point, c).draw(display)?; @@ -347,12 +347,12 @@ fn parallel_line_aa( if error > threshold { point += step.minor; error += e_minor; - bright = reset; + bright_int = 0; } error += e_major; point += step.major; - bright += grad_step; + bright_int += grad_step_int; } Ok(()) From 2b41e7716512f355133736df87e80aae0089b817 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 15 Dec 2022 15:35:05 +0000 Subject: [PATCH 093/188] Fix AA for broken octants --- debug-tools/examples/line-no-aa-parallel.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index a3ee483..985e4a7 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -334,12 +334,12 @@ fn parallel_line_aa( let mut bright_int: i32 = grad_step_int; for _i in 0..(length + last_offset) { - let b = bright_int; + let b = if invert { bright_int } else { 255 - bright_int }; let c = Rgb888::new( - ((bright_int * c.r() as i32) / 255) as u8, - ((bright_int * c.g() as i32) / 255) as u8, - ((bright_int * c.b() as i32) / 255) as u8, + ((b * c.r() as i32) / 255) as u8, + ((b * c.g() as i32) / 255) as u8, + ((b * c.b() as i32) / 255) as u8, ); Pixel(point, c).draw(display)?; From 870156d71141f9f41d1edf9f3711591fa21284b5 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 15 Dec 2022 17:56:46 +0000 Subject: [PATCH 094/188] Fix some old examples --- debug-tools/examples/line-intersection.rs | 2 +- debug-tools/examples/line-parallel.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/line-intersection.rs b/debug-tools/examples/line-intersection.rs index 4a5a8bd..466e6f0 100644 --- a/debug-tools/examples/line-intersection.rs +++ b/debug-tools/examples/line-intersection.rs @@ -1,6 +1,6 @@ use embedded_graphics::{ geometry::AnchorPoint, - mono_font::{latin1::FONT_6X10, MonoTextStyle}, + mono_font::{ascii::FONT_6X10, MonoTextStyle}, pixelcolor::{Rgb565, WebColors}, prelude::*, primitives::Line, diff --git a/debug-tools/examples/line-parallel.rs b/debug-tools/examples/line-parallel.rs index fa933c7..c6af450 100644 --- a/debug-tools/examples/line-parallel.rs +++ b/debug-tools/examples/line-parallel.rs @@ -182,9 +182,9 @@ fn thickline( Pixel(point, Rgb888::MAGENTA).draw(display)?; - if !swap { - Pixel(point + perp_direction, aa_color).draw(display)?; - } + // if !swap { + // Pixel(point + perp_direction, aa_color).draw(display)?; + // } bright = (bright - slope).max(0.0); From 798e6919210dffb4e9da9b4c3eaf8730b9e3748c Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 15 Dec 2022 17:57:05 +0000 Subject: [PATCH 095/188] Bump sim version for local use --- debug-tools/Cargo.toml | 2 +- framework/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debug-tools/Cargo.toml b/debug-tools/Cargo.toml index ffb594a..d0ed3b4 100644 --- a/debug-tools/Cargo.toml +++ b/debug-tools/Cargo.toml @@ -8,5 +8,5 @@ publish = false [dependencies] framework = { path = "../framework" } embedded-graphics = { version = "0.7.1", path = "../../embedded-graphics" } -embedded-graphics-simulator = { version = "0.3.0", path = "../../simulator" } +embedded-graphics-simulator = { version = "0.4.0", path = "../../simulator" } integer-sqrt = "0.1.5" diff --git a/framework/Cargo.toml b/framework/Cargo.toml index 79d4fe7..ba0ce34 100644 --- a/framework/Cargo.toml +++ b/framework/Cargo.toml @@ -7,5 +7,5 @@ publish = false [dependencies] embedded-graphics = "0.7.0-beta.1" -embedded-graphics-simulator = { version = "0.3.0", path = "../../simulator" } +embedded-graphics-simulator = { version = "0.4.0", path = "../../simulator" } sdl2 = "0.35.1" From 484308ef12a701528de0506c5e58498f3a61998f Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Dec 2022 13:12:16 +0000 Subject: [PATCH 096/188] Fix start brightness for AA line --- debug-tools/examples/line-no-aa-parallel.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 985e4a7..c100215 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -308,7 +308,7 @@ fn parallel_line_aa( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; - let mut length = dx + 1; + let length = dx + 1; let mut error = start_error; if skip_first { @@ -325,13 +325,13 @@ fn parallel_line_aa( point += step.major; } - let grad_step_int = (dy * 255) / dx; + let grad_step = (dy * 255) / dx; // The gradient step is scaled based on how close to the diagonal we are. Horizontal/vertical // uses a scale of 1.0, trending to a scale of 0.5 for the diagonal. This allows diagonal lines // to have proper AA with each AA pixel having 0.5 brightness. - let grad_step_int = (grad_step_int * 255) / (255 + grad_step_int); - // We're in the center of a pixel at the start of the line - let mut bright_int: i32 = grad_step_int; + let grad_step = (grad_step * 255) / (255 + grad_step); + // We're in the center of a pixel at the start of the line, so start at half brightness + let mut bright_int: i32 = 127; for _i in 0..(length + last_offset) { let b = if invert { bright_int } else { 255 - bright_int }; @@ -352,7 +352,7 @@ fn parallel_line_aa( error += e_major; point += step.major; - bright_int += grad_step_int; + bright_int += grad_step; } Ok(()) From 003ac5e7eb1ec169d647ffa07aff0ed1ee3c2efc Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Dec 2022 13:21:33 +0000 Subject: [PATCH 097/188] Compute delta inside AA function A precursor to non-square end AA --- debug-tools/examples/line-no-aa-parallel.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index c100215..34b059d 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -274,7 +274,6 @@ fn thickline( point_left, line, parallel_step, - parallel_delta, parallel_error * flip, Rgb888::CSS_SALMON, // Rgb888::CYAN, @@ -292,7 +291,6 @@ fn parallel_line_aa( start: Point, line: Line, step: MajorMinor, - delta: MajorMinor, start_error: i32, c: Rgb888, skip_first: bool, @@ -302,6 +300,13 @@ fn parallel_line_aa( ) -> Result<(), std::convert::Infallible> { let mut point = start; + let delta = line.delta(); + let delta = if delta.y.abs() >= delta.x.abs() { + MajorMinor::new(delta.y, delta.x) + } else { + MajorMinor::new(delta.x, delta.y) + }; + let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -327,8 +332,9 @@ fn parallel_line_aa( let grad_step = (dy * 255) / dx; // The gradient step is scaled based on how close to the diagonal we are. Horizontal/vertical - // uses a scale of 1.0, trending to a scale of 0.5 for the diagonal. This allows diagonal lines - // to have proper AA with each AA pixel having 0.5 brightness. + // values are scaled by 1.0, trending to a scale of 0.5 for the diagonal. This allows diagonal + // lines to have proper AA with each AA pixel having 0.5 brightness. Everything is multiplied + // by 255 so we can do this with no floating point. let grad_step = (grad_step * 255) / (255 + grad_step); // We're in the center of a pixel at the start of the line, so start at half brightness let mut bright_int: i32 = 127; From 095c9ef24824904d0cf8eca6630f0dae3e92fc36 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Dec 2022 13:26:45 +0000 Subject: [PATCH 098/188] Fix some fireflies --- debug-tools/examples/line-no-aa-parallel.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 34b059d..6c8e6ef 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -336,8 +336,10 @@ fn parallel_line_aa( // lines to have proper AA with each AA pixel having 0.5 brightness. Everything is multiplied // by 255 so we can do this with no floating point. let grad_step = (grad_step * 255) / (255 + grad_step); - // We're in the center of a pixel at the start of the line, so start at half brightness - let mut bright_int: i32 = 127; + // We're in the center of a pixel at the start of the line, so start at half brightness. We'll + // subtract one gradient step to prevent fireflies due to numerical errors in the second + // iteration of the gradient loop. + let mut bright_int: i32 = 127 - grad_step; for _i in 0..(length + last_offset) { let b = if invert { bright_int } else { 255 - bright_int }; From b090659ac727cf417857dd41c350fb2655a03383 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Dec 2022 13:56:53 +0000 Subject: [PATCH 099/188] Add another toggle var for debugging --- debug-tools/examples/line-no-aa-parallel.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 6c8e6ef..98e1f67 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -55,6 +55,7 @@ fn thickline( line: Line, width: i32, toggle: bool, + toggle2: bool, last_offset: i32, ) -> Result<(), std::convert::Infallible> { if width == 0 { @@ -422,6 +423,7 @@ struct LineDebug { end: Point, stroke_width: u32, toggle: bool, + toggle2: bool, last_offset: i32, } @@ -441,6 +443,7 @@ impl App for LineDebug { // end: start + Point::new(100, 0), stroke_width: 10, toggle: true, + toggle2: true, last_offset: 0, } } @@ -451,6 +454,7 @@ impl App for LineDebug { Parameter::new("end", &mut self.end), Parameter::new("stroke", &mut self.stroke_width), Parameter::new("toggle", &mut self.toggle), + Parameter::new("toggle2", &mut self.toggle2), Parameter::new("last offset", &mut self.last_offset), ] } @@ -472,6 +476,7 @@ impl App for LineDebug { Line::new(self.start, self.end), width, self.toggle, + self.toggle2, self.last_offset, )?; From 8fedc36b99e911fa0667132e82bd86a71969f535 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Dec 2022 14:30:41 +0000 Subject: [PATCH 100/188] Cleanup --- debug-tools/examples/line-no-aa-parallel.rs | 60 +-------------------- 1 file changed, 2 insertions(+), 58 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 98e1f67..0a319fe 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -1,12 +1,5 @@ use embedded_graphics::{ - geometry::PointExt, - mock_display::MockDisplay, - pixelcolor::Rgb888, - prelude::*, - primitives::{ - common::{LineSide, LinearEquation}, - Line, - }, + geometry::PointExt, mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; @@ -62,8 +55,6 @@ fn thickline( return Ok(()); } - let Line { start, end } = line; - let seed_line = line.perpendicular(); let parallel_delta = line.end - line.start; @@ -139,7 +130,7 @@ fn thickline( let mut is_right = true; while thickness_accumulator.pow(2) <= thickness_threshold { - let (mut point, inc, c, seed_line_error, parallel_error, flip) = if is_right { + let (point, inc, c, seed_line_error, parallel_error, flip) = if is_right { ( &mut point_right, MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), @@ -221,56 +212,9 @@ fn thickline( is_right = !is_right; } - // Pixel(line.start, Rgb888::RED).draw(display)?; - - // line.translate(Point::new(0, width * 2 + 5)) - // .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) - // .draw(display)?; - - // let (mut point, inc, c, seed_line_error, parallel_error, flip) = if is_right { - // ( - // &mut point_right, - // MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), - // Rgb888::CSS_DARK_GOLDENROD, - // &mut seed_line_error_right, - // &mut parallel_error_right, - // // Fix phasing for parallel lines on the right hand side of the base line - // -original_flip, - // ) - // } else { - // ( - // &mut point_left, - // seed_line_step, - // Rgb888::CSS_SALMON, - // &mut seed_line_error, - // &mut parallel_error, - // original_flip, - // ) - // }; - - // let flip = if is_right { - // -original_flip - // } else { - // original_flip - // }; let flip = original_flip; if toggle { - // parallel_line_aa( - // point_right, - // line, - // parallel_step, - // parallel_delta, - // // parallel_error_right * -flip, - // parallel_error_right * -flip, - // // Rgb888::CSS_DARK_GOLDENROD, - // Rgb888::CYAN, - // false, - // flip == 1, - // last_offset, - // display, - // )?; - parallel_line_aa( point_left, line, From 48609ac7cb666c656ca21141d24aa5c73af04355 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Dec 2022 16:29:59 +0000 Subject: [PATCH 101/188] Nearly working both sides AA --- debug-tools/examples/line-no-aa-parallel.rs | 74 +++++++++++++++++---- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 0a319fe..2a69866 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -129,6 +129,9 @@ fn thickline( // TODO: The current extents() function needs to respect this too, as well as stroke offset let mut is_right = true; + let mut right_side_aa_done = false; + let mut left_side_aa_done = false; + while thickness_accumulator.pow(2) <= thickness_threshold { let (point, inc, c, seed_line_error, parallel_error, flip) = if is_right { ( @@ -199,6 +202,36 @@ fn thickline( )?; } + // Next step would be line edge, so draw an extra AA line + if thickness_accumulator.pow(2) + 2 * dx > thickness_threshold { + if is_right { + right_side_aa_done = true; + } else { + left_side_aa_done = true; + } + + if toggle { + parallel_line_aa( + *point, + line, + parallel_step, + (*parallel_error + e_minor + e_major) * flip, + c, + original_flip == -1 && !is_right || original_flip == 1 && is_right, + true, + if original_flip == -1 && !is_right || original_flip == 1 && is_right { + 0 + } else { + // Because the opposite side's extra lines start one step into the thick + // line body, we must reduce its total length by 1 to prevent jagged + // edges on the end edge of the line. + -1 + } + last_offset, + display, + )?; + } + } + *parallel_error += e_minor; } @@ -215,18 +248,35 @@ fn thickline( let flip = original_flip; if toggle { - parallel_line_aa( - point_left, - line, - parallel_step, - parallel_error * flip, - Rgb888::CSS_SALMON, - // Rgb888::CYAN, - false, - flip == -1, - last_offset, - display, - )?; + if !left_side_aa_done { + parallel_line_aa( + point_left, + line, + parallel_step, + parallel_error * flip, + Rgb888::CSS_SALMON, + false, + flip == -1, + last_offset, + display, + )?; + } + + dbg!(flip, original_flip); + + if !right_side_aa_done { + parallel_line_aa( + point_right, + line, + parallel_step, + parallel_error * -flip, + Rgb888::CSS_DARK_GOLDENROD, + false, + flip == 1, + last_offset, + display, + )?; + } } Ok(()) From a923997e4093f4a89b163faa42ae50ac02974f0e Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Dec 2022 16:52:43 +0000 Subject: [PATCH 102/188] Simplify AA thickness check --- debug-tools/examples/line-no-aa-parallel.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 2a69866..5764af3 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -203,7 +203,7 @@ fn thickline( } // Next step would be line edge, so draw an extra AA line - if thickness_accumulator.pow(2) + 2 * dx > thickness_threshold { + if thickness_accumulator.pow(2) > thickness_threshold { if is_right { right_side_aa_done = true; } else { @@ -262,8 +262,6 @@ fn thickline( )?; } - dbg!(flip, original_flip); - if !right_side_aa_done { parallel_line_aa( point_right, From 73c0bda91d67f75d6a4c8e3c1889f2bdd2e8198c Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 21 Dec 2022 18:24:20 +0000 Subject: [PATCH 103/188] Final threshold fiddling --- debug-tools/examples/line-no-aa-parallel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 5764af3..e191afd 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -203,7 +203,7 @@ fn thickline( } // Next step would be line edge, so draw an extra AA line - if thickness_accumulator.pow(2) > thickness_threshold { + if thickness_accumulator.pow(2) + 2 * dy > thickness_threshold { if is_right { right_side_aa_done = true; } else { From 8d09b718257fa5c2b88615e9f02004abf39ebe1d Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 7 Jan 2023 11:38:44 +0000 Subject: [PATCH 104/188] Fix right-side final holes Stupid typo argh --- debug-tools/examples/line-no-aa-parallel.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index e191afd..661ca08 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -206,8 +206,10 @@ fn thickline( if thickness_accumulator.pow(2) + 2 * dy > thickness_threshold { if is_right { right_side_aa_done = true; + println!("Right side AA inside loop"); } else { left_side_aa_done = true; + println!("Left side AA inside loop"); } if toggle { @@ -267,7 +269,7 @@ fn thickline( point_right, line, parallel_step, - parallel_error * -flip, + parallel_error_right * -flip, Rgb888::CSS_DARK_GOLDENROD, false, flip == 1, @@ -342,6 +344,7 @@ fn parallel_line_aa( ((b * c.g() as i32) / 255) as u8, ((b * c.b() as i32) / 255) as u8, ); + let c = Rgb888::CYAN; Pixel(point, c).draw(display)?; @@ -372,6 +375,9 @@ fn parallel_line( ) -> Result<(), std::convert::Infallible> { let mut point = start; + // Pixel(point, c).draw(display)?; + // return Ok(()); + let dx = delta.major.abs(); let dy = delta.minor.abs(); From 5b7dbb499d302e4d2e9f0978bf5e8969a5165ad0 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 7 Jan 2023 12:10:18 +0000 Subject: [PATCH 105/188] Reinstate AA --- debug-tools/examples/line-no-aa-parallel.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 661ca08..f0231f8 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -344,7 +344,6 @@ fn parallel_line_aa( ((b * c.g() as i32) / 255) as u8, ((b * c.b() as i32) / 255) as u8, ); - let c = Rgb888::CYAN; Pixel(point, c).draw(display)?; From ca6397fc8dd1203acd2bc8f2277def697346fee3 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 7 Jan 2023 12:13:14 +0000 Subject: [PATCH 106/188] Sort of make 1px wide lines work But not really --- debug-tools/examples/line-no-aa-parallel.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index f0231f8..345fc07 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -120,7 +120,8 @@ fn thickline( 1 }; - let thickness_threshold = (width * 2).pow(2) * line.delta().length_squared(); + // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill + let thickness_threshold = ((width - 1) * 2).pow(2) * line.delta().length_squared(); // Add the first line drawn to the thickness. If this is left at zero, an extra line will be // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * dx; @@ -137,7 +138,8 @@ fn thickline( ( &mut point_right, MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), - Rgb888::CSS_DARK_GOLDENROD, + // Rgb888::CSS_DARK_GOLDENROD, + Rgb888::CSS_SALMON, &mut seed_line_error_right, &mut parallel_error_right, // Fix phasing for parallel lines on the right hand side of the base line @@ -270,7 +272,7 @@ fn thickline( line, parallel_step, parallel_error_right * -flip, - Rgb888::CSS_DARK_GOLDENROD, + Rgb888::CSS_SALMON, false, flip == 1, last_offset, From b8f7f5c9614cf73d583d95f64e26278808fddd13 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 7 Jan 2023 13:31:30 +0000 Subject: [PATCH 107/188] Add a note --- debug-tools/examples/line-no-aa-parallel.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 345fc07..db2e978 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -204,7 +204,9 @@ fn thickline( )?; } - // Next step would be line edge, so draw an extra AA line + // We're currently drawing an "extra" line. Special case: if the next step would be + // line edge, draw an extra AA line and mark it as not needing to be drawn after + // the main loop. if thickness_accumulator.pow(2) + 2 * dy > thickness_threshold { if is_right { right_side_aa_done = true; From 966987922f458b3aee1d300b457aa32b9f479263 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 15 Jan 2023 11:35:04 +0000 Subject: [PATCH 108/188] Simplify last edge drawing a bit --- debug-tools/examples/line-no-aa-parallel.rs | 60 +++++++++++---------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index db2e978..332aba1 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -208,34 +208,38 @@ fn thickline( // line edge, draw an extra AA line and mark it as not needing to be drawn after // the main loop. if thickness_accumulator.pow(2) + 2 * dy > thickness_threshold { - if is_right { - right_side_aa_done = true; - println!("Right side AA inside loop"); - } else { - left_side_aa_done = true; - println!("Left side AA inside loop"); - } - - if toggle { - parallel_line_aa( - *point, - line, - parallel_step, - (*parallel_error + e_minor + e_major) * flip, - c, - original_flip == -1 && !is_right || original_flip == 1 && is_right, - true, - if original_flip == -1 && !is_right || original_flip == 1 && is_right { - 0 - } else { - // Because the opposite side's extra lines start one step into the thick - // line body, we must reduce its total length by 1 to prevent jagged - // edges on the end edge of the line. - -1 - } + last_offset, - display, - )?; - } + // if is_right { + // right_side_aa_done = true; + // println!("Right side AA inside loop"); + // } else { + // left_side_aa_done = true; + // println!("Left side AA inside loop"); + // } + + // if toggle { + // parallel_line_aa( + // *point, + // line, + // parallel_step, + // (*parallel_error + e_minor + e_major) * flip, + // c, + // original_flip == -1 && !is_right || original_flip == 1 && is_right, + // true, + // if original_flip == -1 && !is_right || original_flip == 1 && is_right { + // 0 + // } else { + // // Because the opposite side's extra lines start one step into the thick + // // line body, we must reduce its total length by 1 to prevent jagged + // // edges on the end edge of the line. + // -1 + // } + last_offset, + // display, + // )?; + // } + + // Makes right-side AA line hug the rest of the line body by undoing the + // position increment at the end of the main loop. + *point -= inc.major; } *parallel_error += e_minor; From b4356ad33c1a8681f4bf48b83f711cb89cb981ed Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 19 Jan 2023 19:47:50 +0000 Subject: [PATCH 109/188] Debugging AA using floating point --- debug-tools/examples/line-no-aa-parallel.rs | 44 ++++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 332aba1..0aeb715 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -333,32 +333,46 @@ fn parallel_line_aa( point += step.major; } - let grad_step = (dy * 255) / dx; - // The gradient step is scaled based on how close to the diagonal we are. Horizontal/vertical - // values are scaled by 1.0, trending to a scale of 0.5 for the diagonal. This allows diagonal - // lines to have proper AA with each AA pixel having 0.5 brightness. Everything is multiplied - // by 255 so we can do this with no floating point. - let grad_step = (grad_step * 255) / (255 + grad_step); - // We're in the center of a pixel at the start of the line, so start at half brightness. We'll - // subtract one gradient step to prevent fireflies due to numerical errors in the second - // iteration of the gradient loop. - let mut bright_int: i32 = 127 - grad_step; + // let grad_step = (dy * 255) / dx; + // // The gradient step is scaled based on how close to the diagonal we are. Horizontal/vertical + // // values are scaled by 1.0, trending to a scale of 0.5 for the diagonal. This allows diagonal + // // lines to have proper AA with each AA pixel having 0.5 brightness. Everything is multiplied + // // by 255 so we can do this with no floating point. + // let grad_step = (grad_step * 255) / (255 + grad_step); + // // We're in the center of a pixel at the start of the line, so start at half brightness. We'll + // // subtract one gradient step to prevent fireflies due to numerical errors in the second + // // iteration of the gradient loop. + // let mut bright_int: i32 = 127 - grad_step; + + let grad_step = dy as f32 / dx as f32; + // let mut bright_int = 0.5f32 - grad_step; + let grad_step = grad_step - (grad_step * grad_step) / 2.0; + let mut bright_int = 0.5; + + // Flat or vertical = gradient near 0 + // Diagonal = 1.0 for _i in 0..(length + last_offset) { - let b = if invert { bright_int } else { 255 - bright_int }; + let b = if invert { bright_int } else { 1.0 - bright_int }; let c = Rgb888::new( - ((b * c.r() as i32) / 255) as u8, - ((b * c.g() as i32) / 255) as u8, - ((b * c.b() as i32) / 255) as u8, + (b * c.r() as f32) as u8, + (b * c.g() as f32) as u8, + (b * c.b() as f32) as u8, ); + // let c = Rgb888::new( + // ((b * c.r() as i32) / 255) as u8, + // ((b * c.g() as i32) / 255) as u8, + // ((b * c.b() as i32) / 255) as u8, + // ); + Pixel(point, c).draw(display)?; if error > threshold { point += step.minor; error += e_minor; - bright_int = 0; + bright_int = 0.0; } error += e_major; From fdc60d9128312b99a89000d0574caa72ccdf64c2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 19 Jan 2023 20:26:54 +0000 Subject: [PATCH 110/188] More FP debugging --- debug-tools/examples/line-no-aa-parallel.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 0aeb715..06a86fb 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -344,9 +344,13 @@ fn parallel_line_aa( // // iteration of the gradient loop. // let mut bright_int: i32 = 127 - grad_step; - let grad_step = dy as f32 / dx as f32; + let gradient = dy as f32 / dx as f32; // let mut bright_int = 0.5f32 - grad_step; - let grad_step = grad_step - (grad_step * grad_step) / 2.0; + + // The closer we get to diagonal, the less we need to reduce the AA pixel + let grad_step = gradient - gradient.powi(2) / 2.0; + + // Start at half brightness because we're "half way along" a Bresenham step let mut bright_int = 0.5; // Flat or vertical = gradient near 0 From 1ae37c222f42314503a4ccd65ae22aa08d5d72ed Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 22 Jan 2023 09:45:14 +0000 Subject: [PATCH 111/188] Make AA sort of work with just Bresenham vars --- debug-tools/examples/line-no-aa-parallel.rs | 45 ++++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 06a86fb..db0f322 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -272,19 +272,19 @@ fn thickline( )?; } - if !right_side_aa_done { - parallel_line_aa( - point_right, - line, - parallel_step, - parallel_error_right * -flip, - Rgb888::CSS_SALMON, - false, - flip == 1, - last_offset, - display, - )?; - } + // if !right_side_aa_done { + // parallel_line_aa( + // point_right, + // line, + // parallel_step, + // parallel_error_right * -flip, + // Rgb888::CSS_SALMON, + // false, + // flip == 1, + // last_offset, + // display, + // )?; + // } } Ok(()) @@ -353,12 +353,26 @@ fn parallel_line_aa( // Start at half brightness because we're "half way along" a Bresenham step let mut bright_int = 0.5; - // Flat or vertical = gradient near 0 - // Diagonal = 1.0 + let range = threshold - e_minor; + + println!("--- {range} {threshold} {e_minor} {e_major}"); for _i in 0..(length + last_offset) { let b = if invert { bright_int } else { 1.0 - bright_int }; + let error2 = error.min(threshold) as f32 / threshold as f32; + let error2 = (1.0 - error2) / 2.0; + + println!( + "E {:+03} E2 {:0.3} B {:0.3} D {:0.3}", + error, + error2, + b, + f32::abs(error2 - b) + ); + + let b = error2.abs().min(1.0); + let c = Rgb888::new( (b * c.r() as f32) as u8, (b * c.g() as f32) as u8, @@ -374,6 +388,7 @@ fn parallel_line_aa( Pixel(point, c).draw(display)?; if error > threshold { + println!("MINOR"); point += step.minor; error += e_minor; bright_int = 0.0; From ba5f2191e69b2be3b6fd12ac82f6748397604ccb Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 08:56:41 +0100 Subject: [PATCH 112/188] Some stuff from a very long time ago --- debug-tools/examples/line-no-aa-parallel.rs | 34 +++++++++++++-------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index db0f322..a3f662a 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -1,5 +1,9 @@ use embedded_graphics::{ - geometry::PointExt, mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, + geometry::PointExt, + mock_display::MockDisplay, + pixelcolor::Rgb888, + prelude::*, + primitives::{Line, PrimitiveStyle}, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; @@ -353,9 +357,12 @@ fn parallel_line_aa( // Start at half brightness because we're "half way along" a Bresenham step let mut bright_int = 0.5; - let range = threshold - e_minor; + println!("--- dy/dx {gradient} T {threshold} Emin {e_minor} Emaj {e_major} G {gradient}"); - println!("--- {range} {threshold} {e_minor} {e_major}"); + let mut accum = 0.0f32; + + let mut x = line.start.x as f32; + let mut y = line.start.y as f32; for _i in 0..(length + last_offset) { let b = if invert { bright_int } else { 1.0 - bright_int }; @@ -363,15 +370,10 @@ fn parallel_line_aa( let error2 = error.min(threshold) as f32 / threshold as f32; let error2 = (1.0 - error2) / 2.0; - println!( - "E {:+03} E2 {:0.3} B {:0.3} D {:0.3}", - error, - error2, - b, - f32::abs(error2 - b) - ); + let e = + ((error as f32 / (2.0 * dx as f32)) / (threshold as f32 / (2.0 * dx as f32))).min(1.0); - let b = error2.abs().min(1.0); + let b = if e < 0.0 { 1.0 + e.abs() } else { 1.0 - e } / 2.0; let c = Rgb888::new( (b * c.r() as f32) as u8, @@ -379,6 +381,8 @@ fn parallel_line_aa( (b * c.b() as f32) as u8, ); + println!("b {:+0.2} e {:+0.2}", b, e); + // let c = Rgb888::new( // ((b * c.r() as i32) / 255) as u8, // ((b * c.g() as i32) / 255) as u8, @@ -392,11 +396,15 @@ fn parallel_line_aa( point += step.minor; error += e_minor; bright_int = 0.0; + accum = 0.0; } error += e_major; point += step.major; bright_int += grad_step; + accum += gradient; + x += 1.0; + y += gradient; } Ok(()) @@ -520,8 +528,8 @@ impl App for LineDebug { // let l = Line::new(self.start, self.end); - // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 1)) - // .draw(&mut display.translated(Point::new(40, 40)))?; + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, width as u32)) + // .draw(display)?; // l.perpendicular() // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) From 720ec11824eccf7d987e87485c6b92ff498c7d98 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 10:01:37 +0100 Subject: [PATCH 113/188] Glitchy but decent looking 256 mul AA --- debug-tools/Cargo.toml | 4 +- debug-tools/examples/line-no-aa-parallel.rs | 196 ++++++++++---------- framework/Cargo.toml | 4 +- 3 files changed, 104 insertions(+), 100 deletions(-) diff --git a/debug-tools/Cargo.toml b/debug-tools/Cargo.toml index d0ed3b4..d6528ad 100644 --- a/debug-tools/Cargo.toml +++ b/debug-tools/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] framework = { path = "../framework" } -embedded-graphics = { version = "0.7.1", path = "../../embedded-graphics" } -embedded-graphics-simulator = { version = "0.4.0", path = "../../simulator" } +embedded-graphics = { version = "0.8.1", path = "../../embedded-graphics" } +embedded-graphics-simulator = { version = "0.6.0", path = "../../simulator" } integer-sqrt = "0.1.5" diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index a3f662a..dc9c617 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -49,12 +49,15 @@ impl MajorMinor { fn thickline( display: &mut impl DrawTarget, - line: Line, + mut line: Line, width: i32, toggle: bool, toggle2: bool, last_offset: i32, ) -> Result<(), std::convert::Infallible> { + line.start.y *= 256; + line.end.y *= 256; + if width == 0 { return Ok(()); } @@ -162,17 +165,17 @@ fn thickline( // Pixel(*point, c).draw(display)?; - parallel_line( - *point, - line, - parallel_step, - parallel_delta, - *parallel_error * flip, - c, - false, - last_offset, - display, - )?; + // parallel_line( + // *point, + // line, + // parallel_step, + // parallel_delta, + // *parallel_error * flip, + // c, + // false, + // last_offset, + // display, + // )?; if *seed_line_error > threshold { *point += inc.minor; @@ -183,29 +186,29 @@ fn thickline( if thickness_accumulator.pow(2) <= thickness_threshold { // Pixel(*point, Rgb888::CYAN).draw(display)?; - parallel_line( - *point, - line, - parallel_step, - parallel_delta, - (*parallel_error + e_minor + e_major) * flip, - // Rgb888::CYAN, - c, - // If we're on the side of the base line where the perpendicular - // Bresenham steps "into" the thick line body, skip the first extra - // line point as it's on the wrong side of the perpendicular and leads - // to a jagged edge. - original_flip == -1 && !is_right || original_flip == 1 && is_right, - if original_flip == -1 && !is_right || original_flip == 1 && is_right { - 0 - } else { - // Because the opposite side's extra lines start one step into the thick - // line body, we must reduce its total length by 1 to prevent jagged - // edges on the end edge of the line. - -1 - } + last_offset, - display, - )?; + // parallel_line( + // *point, + // line, + // parallel_step, + // parallel_delta, + // (*parallel_error + e_minor + e_major) * flip, + // // Rgb888::CYAN, + // c, + // // If we're on the side of the base line where the perpendicular + // // Bresenham steps "into" the thick line body, skip the first extra + // // line point as it's on the wrong side of the perpendicular and leads + // // to a jagged edge. + // original_flip == -1 && !is_right || original_flip == 1 && is_right, + // if original_flip == -1 && !is_right || original_flip == 1 && is_right { + // 0 + // } else { + // // Because the opposite side's extra lines start one step into the thick + // // line body, we must reduce its total length by 1 to prevent jagged + // // edges on the end edge of the line. + // -1 + // } + last_offset, + // display, + // )?; } // We're currently drawing an "extra" line. Special case: if the next step would be @@ -267,8 +270,9 @@ fn thickline( point_left, line, parallel_step, + parallel_delta, parallel_error * flip, - Rgb888::CSS_SALMON, + Rgb888::CSS_ALICE_BLUE, false, flip == -1, last_offset, @@ -298,6 +302,7 @@ fn parallel_line_aa( start: Point, line: Line, step: MajorMinor, + delta: MajorMinor, start_error: i32, c: Rgb888, skip_first: bool, @@ -307,12 +312,12 @@ fn parallel_line_aa( ) -> Result<(), std::convert::Infallible> { let mut point = start; - let delta = line.delta(); - let delta = if delta.y.abs() >= delta.x.abs() { - MajorMinor::new(delta.y, delta.x) - } else { - MajorMinor::new(delta.x, delta.y) - }; + // Pixel(point, c).draw(display)?; + // return Ok(()); + + // https://computergraphics.stackexchange.com/a/10675 + let step = MajorMinor::new(step.major, step.minor); + let delta = MajorMinor::new(delta.major, delta.minor); let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -320,7 +325,7 @@ fn parallel_line_aa( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; - let length = dx + 1; + let mut length = dx + 1; let mut error = start_error; if skip_first { @@ -337,74 +342,42 @@ fn parallel_line_aa( point += step.major; } - // let grad_step = (dy * 255) / dx; - // // The gradient step is scaled based on how close to the diagonal we are. Horizontal/vertical - // // values are scaled by 1.0, trending to a scale of 0.5 for the diagonal. This allows diagonal - // // lines to have proper AA with each AA pixel having 0.5 brightness. Everything is multiplied - // // by 255 so we can do this with no floating point. - // let grad_step = (grad_step * 255) / (255 + grad_step); - // // We're in the center of a pixel at the start of the line, so start at half brightness. We'll - // // subtract one gradient step to prevent fireflies due to numerical errors in the second - // // iteration of the gradient loop. - // let mut bright_int: i32 = 127 - grad_step; - - let gradient = dy as f32 / dx as f32; - // let mut bright_int = 0.5f32 - grad_step; - - // The closer we get to diagonal, the less we need to reduce the AA pixel - let grad_step = gradient - gradient.powi(2) / 2.0; - - // Start at half brightness because we're "half way along" a Bresenham step - let mut bright_int = 0.5; - - println!("--- dy/dx {gradient} T {threshold} Emin {e_minor} Emaj {e_major} G {gradient}"); - - let mut accum = 0.0f32; - - let mut x = line.start.x as f32; - let mut y = line.start.y as f32; - for _i in 0..(length + last_offset) { - let b = if invert { bright_int } else { 1.0 - bright_int }; + // https://computergraphics.stackexchange.com/a/10675 + let draw_p = Point::new(point.x, point.y >> 8); - let error2 = error.min(threshold) as f32 / threshold as f32; - let error2 = (1.0 - error2) / 2.0; + Pixel(draw_p, Rgb888::CYAN).draw(display)?; - let e = - ((error as f32 / (2.0 * dx as f32)) / (threshold as f32 / (2.0 * dx as f32))).min(1.0); + let aa_colour = { + let c = Rgb888::RED; - let b = if e < 0.0 { 1.0 + e.abs() } else { 1.0 - e } / 2.0; + let mul = (point.y & 255) as u8; - let c = Rgb888::new( - (b * c.r() as f32) as u8, - (b * c.g() as f32) as u8, - (b * c.b() as f32) as u8, - ); + Rgb888::new( + // TODO: Proper colour blend + // (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + 255 - mul, + 255 - mul, + 255 - mul, + ) + }; - println!("b {:+0.2} e {:+0.2}", b, e); + let aa_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum()); - // let c = Rgb888::new( - // ((b * c.r() as i32) / 255) as u8, - // ((b * c.g() as i32) / 255) as u8, - // ((b * c.b() as i32) / 255) as u8, - // ); + Pixel(aa_p, aa_colour).draw(display)?; - Pixel(point, c).draw(display)?; + // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not + // quite sure why but we don't get a smooth increase over the length of the line. if error > threshold { - println!("MINOR"); point += step.minor; error += e_minor; - bright_int = 0.0; - accum = 0.0; } error += e_major; point += step.major; - bright_int += grad_step; - accum += gradient; - x += 1.0; - y += gradient; } Ok(()) @@ -450,7 +423,7 @@ fn parallel_line( } for _i in 0..(length + last_offset) { - Pixel(point, c).draw(display)?; + Pixel(Point::new(point.x, point.y >> 8), c).draw(display)?; if error > threshold { point += step.minor; @@ -464,6 +437,37 @@ fn parallel_line( Ok(()) } +/// Minimum distane between a line and a point. +/// +/// From +fn line_point_distance(line: Line, point: Point) -> f32 { + let length = { + let Point { x, y } = line.delta(); + + f32::sqrt((x.pow(2) + y.pow(2)) as f32) + }; + + let x1 = line.start.x; + let x2 = line.end.x; + let x3 = point.x; + + let y1 = line.start.y; + let y2 = line.end.y; + let y3 = point.y; + + let u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) as f32 / length.powi(2); + + let tx = x1 as f32 + u * (x2 - x1) as f32; + let ty = y1 as f32 + u * (y2 - y1) as f32; + + // Tangent intersection point + let tangent = Point::new(tx as i32, ty as i32); + + let Point { x, y } = Line::new(point, tangent).delta(); + + f32::sqrt((x.pow(2) + y.pow(2)) as f32) +} + struct LineDebug { start: Point, end: Point, diff --git a/framework/Cargo.toml b/framework/Cargo.toml index ba0ce34..41e786e 100644 --- a/framework/Cargo.toml +++ b/framework/Cargo.toml @@ -6,6 +6,6 @@ edition = "2018" publish = false [dependencies] -embedded-graphics = "0.7.0-beta.1" -embedded-graphics-simulator = { version = "0.4.0", path = "../../simulator" } +embedded-graphics = "0.8.1" +embedded-graphics-simulator = { version = "0.6.0", path = "../../simulator" } sdl2 = "0.35.1" From 4e346e0be617b220a0a7edd85b7a8b1015ca5137 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 10:35:03 +0100 Subject: [PATCH 114/188] Kinda correctly scaled positions --- debug-tools/examples/line-no-aa-parallel.rs | 43 +++++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index dc9c617..9e4f656 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -55,25 +55,27 @@ fn thickline( toggle2: bool, last_offset: i32, ) -> Result<(), std::convert::Infallible> { - line.start.y *= 256; - line.end.y *= 256; - if width == 0 { return Ok(()); } + let non_mul_line = line; + let seed_line = line.perpendicular(); - let parallel_delta = line.end - line.start; + let mut point_left = seed_line.start; + let mut point_right = seed_line.start; + + line.start.y *= 256; + line.end.y *= 256; + + let parallel_delta = line.delta(); let parallel_step = Point::new( if parallel_delta.x >= 0 { 1 } else { -1 }, if parallel_delta.y >= 0 { 1 } else { -1 }, ); - let mut point_left = line.start; - let mut point_right = line.start; - - let seed_line_delta = seed_line.end - seed_line.start; + let seed_line_delta = seed_line.delta(); let seed_line_direction = Point::new( if seed_line_delta.x >= 0 { 1 } else { -1 }, @@ -128,7 +130,7 @@ fn thickline( }; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill - let thickness_threshold = ((width - 1) * 2).pow(2) * line.delta().length_squared(); + let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_line.delta().length_squared(); // Add the first line drawn to the thickness. If this is left at zero, an extra line will be // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * dx; @@ -156,14 +158,14 @@ fn thickline( ( &mut point_left, seed_line_step, - Rgb888::CSS_SALMON, + Rgb888::CSS_ALICE_BLUE, &mut seed_line_error, &mut parallel_error, original_flip, ) }; - // Pixel(*point, c).draw(display)?; + Pixel(Point::new(point.x, point.y), c).draw(display)?; // parallel_line( // *point, @@ -255,7 +257,7 @@ fn thickline( *parallel_error += e_major; } - *point += inc.major/* * 3*/; + *point += inc.major; *seed_line_error += e_major; thickness_accumulator += 2 * dx; @@ -310,14 +312,14 @@ fn parallel_line_aa( mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { - let mut point = start; + let mut point = Point::new(start.x, start.y * 256); // Pixel(point, c).draw(display)?; // return Ok(()); - // https://computergraphics.stackexchange.com/a/10675 - let step = MajorMinor::new(step.major, step.minor); - let delta = MajorMinor::new(delta.major, delta.minor); + // // https://computergraphics.stackexchange.com/a/10675 + // let step = MajorMinor::new(step.major, step.minor); + // let delta = MajorMinor::new(delta.major, delta.minor); let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -399,6 +401,10 @@ fn parallel_line( // Pixel(point, c).draw(display)?; // return Ok(()); + // https://computergraphics.stackexchange.com/a/10675 + let step = MajorMinor::new(step.major, step.minor); + let delta = MajorMinor::new(delta.major, delta.minor); + let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -423,7 +429,10 @@ fn parallel_line( } for _i in 0..(length + last_offset) { - Pixel(Point::new(point.x, point.y >> 8), c).draw(display)?; + // https://computergraphics.stackexchange.com/a/10675 + let draw_p = Point::new(point.x, point.y >> 8); + + Pixel(draw_p, c).draw(display)?; if error > threshold { point += step.minor; From cd4106bcf5415be2c2fa21d7fe72c93ccc077a73 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 11:40:32 +0100 Subject: [PATCH 115/188] Debugging incorrect phasing for AA edge --- debug-tools/examples/line-no-aa-parallel.rs | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/debug-tools/examples/line-no-aa-parallel.rs b/debug-tools/examples/line-no-aa-parallel.rs index 9e4f656..0a480f7 100644 --- a/debug-tools/examples/line-no-aa-parallel.rs +++ b/debug-tools/examples/line-no-aa-parallel.rs @@ -158,7 +158,7 @@ fn thickline( ( &mut point_left, seed_line_step, - Rgb888::CSS_ALICE_BLUE, + Rgb888::CSS_FOREST_GREEN, &mut seed_line_error, &mut parallel_error, original_flip, @@ -167,17 +167,17 @@ fn thickline( Pixel(Point::new(point.x, point.y), c).draw(display)?; - // parallel_line( - // *point, - // line, - // parallel_step, - // parallel_delta, - // *parallel_error * flip, - // c, - // false, - // last_offset, - // display, - // )?; + parallel_line( + *point, + line, + parallel_step, + parallel_delta, + *parallel_error * flip, + c, + false, + last_offset, + display, + )?; if *seed_line_error > threshold { *point += inc.minor; @@ -346,7 +346,7 @@ fn parallel_line_aa( for _i in 0..(length + last_offset) { // https://computergraphics.stackexchange.com/a/10675 - let draw_p = Point::new(point.x, point.y >> 8); + let draw_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum()); Pixel(draw_p, Rgb888::CYAN).draw(display)?; @@ -366,7 +366,7 @@ fn parallel_line_aa( ) }; - let aa_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum()); + let aa_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum() * 2); Pixel(aa_p, aa_colour).draw(display)?; @@ -396,7 +396,7 @@ fn parallel_line( mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { - let mut point = start; + let mut point = Point::new(start.x, start.y * 256); // Pixel(point, c).draw(display)?; // return Ok(()); From 85ae96341be19fba311e50e273af83386df76021 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 12:58:04 +0100 Subject: [PATCH 116/188] Smaller debug tool to test thick line seed * 256 --- debug-tools/examples/thick-line-mul-256.rs | 192 +++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 debug-tools/examples/thick-line-mul-256.rs diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs new file mode 100644 index 0000000..a1e42ad --- /dev/null +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -0,0 +1,192 @@ +//! Testing perpendicular seed line for thick lines using the 256 mul trick as per +//! . +//! +//! Copied from `line-no-aa-parallel.rs`. + +use embedded_graphics::{ + geometry::PointExt, mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; + +#[derive(Debug, Clone, Copy)] +struct MajorMinor { + major: T, + minor: T, +} + +impl MajorMinor { + fn new(major: T, minor: T) -> Self { + Self { major, minor } + } +} + +fn thickline( + display: &mut impl DrawTarget, + line: Line, + width: i32, +) -> Result<(), std::convert::Infallible> { + if width == 0 { + return Ok(()); + } + + let non_mul_line = line; + + let mut seed_line = line.perpendicular(); + + seed_line.start.x *= 256; + seed_line.end.x *= 256; + + // line.start.y *= 256; + // line.end.y *= 256; + + let mut point_left = seed_line.start; + let mut point_right = seed_line.start; + + let seed_line_delta = seed_line.delta(); + let seed_line_direction = Point::new( + if seed_line_delta.x >= 0 { 1 } else { -1 }, + if seed_line_delta.y >= 0 { 1 } else { -1 }, + ); + + let (seed_line_delta, seed_line_step) = if seed_line_delta.y.abs() >= seed_line_delta.x.abs() { + ( + MajorMinor::new(seed_line_delta.y, seed_line_delta.x), + MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), + ) + } else { + ( + MajorMinor::new(seed_line_delta.x, seed_line_delta.y), + MajorMinor::new(seed_line_direction.x_axis(), seed_line_direction.y_axis()), + ) + }; + + // Don't draw line skeleton twice + point_right -= seed_line_step.major; + + let dx = seed_line_delta.major.abs(); + let dy = seed_line_delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let mut seed_line_error = 0; + let mut seed_line_error_right = e_major; + + // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill + let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_line.delta().length_squared(); + // Add the first line drawn to the thickness. If this is left at zero, an extra line will be + // drawn as the lines are drawn before checking for thickness. + let mut thickness_accumulator = 2 * dx; + + // Bias to one side of the line + // TODO: The current extents() function needs to respect this too, as well as stroke offset + let mut is_right = true; + + println!("---"); + + // TODO + // while thickness_accumulator.pow(2) <= thickness_threshold { + for _ in 0..width { + let (point, inc, c, seed_line_error) = if is_right { + ( + &mut point_right, + MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), + // Rgb888::CSS_DARK_GOLDENROD, + Rgb888::CSS_SALMON, + &mut seed_line_error_right, + ) + } else { + ( + &mut point_left, + seed_line_step, + Rgb888::CSS_FOREST_GREEN, + &mut seed_line_error, + ) + }; + + dbg!(&point); + + Pixel(dbg!(Point::new(point.x >> 8, point.y)), c).draw(display)?; + + if *seed_line_error > threshold { + *point += inc.minor; + *seed_line_error += e_minor; + thickness_accumulator += 2 * dy; + } + + *point += inc.major; + *seed_line_error += e_major; + thickness_accumulator += 2 * dx; + + is_right = !is_right; + } + + Ok(()) +} + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, +} + +impl App for LineDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(200, 200); + // const DISPLAY_SIZE: Size = Size::new(64, 64); + + fn new() -> Self { + let end = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); + Self { + start: end + Point::new(10, 15), + end, + // end: start + Point::new(100, 0), + stroke_width: 10, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let Point { x: _x0, y: _y0 } = self.start; + + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; + + let _mock_display: MockDisplay = MockDisplay::new(); + + thickline(display, Line::new(self.start, self.end), width)?; + + // let l = Line::new(self.start, self.end); + + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, width as u32)) + // .draw(display)?; + + // l.perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(5).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} From 99e1e7b375adad91007923d2eb0b223eaa74aa32 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 16:56:07 +0100 Subject: [PATCH 117/188] Start again again again --- debug-tools/examples/thick-line-mul-256.rs | 29 ++++++---------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index a1e42ad..cc641ad 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -1,8 +1,3 @@ -//! Testing perpendicular seed line for thick lines using the 256 mul trick as per -//! . -//! -//! Copied from `line-no-aa-parallel.rs`. - use embedded_graphics::{ geometry::PointExt, mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, }; @@ -23,7 +18,7 @@ impl MajorMinor { fn thickline( display: &mut impl DrawTarget, - line: Line, + mut line: Line, width: i32, ) -> Result<(), std::convert::Infallible> { if width == 0 { @@ -32,18 +27,16 @@ fn thickline( let non_mul_line = line; - let mut seed_line = line.perpendicular(); - - seed_line.start.x *= 256; - seed_line.end.x *= 256; - - // line.start.y *= 256; - // line.end.y *= 256; + let seed_line = line.perpendicular(); let mut point_left = seed_line.start; let mut point_right = seed_line.start; + line.start.y *= 256; + line.end.y *= 256; + let seed_line_delta = seed_line.delta(); + let seed_line_direction = Point::new( if seed_line_delta.x >= 0 { 1 } else { -1 }, if seed_line_delta.y >= 0 { 1 } else { -1 }, @@ -83,11 +76,7 @@ fn thickline( // TODO: The current extents() function needs to respect this too, as well as stroke offset let mut is_right = true; - println!("---"); - - // TODO - // while thickness_accumulator.pow(2) <= thickness_threshold { - for _ in 0..width { + while thickness_accumulator.pow(2) <= thickness_threshold { let (point, inc, c, seed_line_error) = if is_right { ( &mut point_right, @@ -105,9 +94,7 @@ fn thickline( ) }; - dbg!(&point); - - Pixel(dbg!(Point::new(point.x >> 8, point.y)), c).draw(display)?; + Pixel(Point::new(point.x, point.y), c).draw(display)?; if *seed_line_error > threshold { *point += inc.minor; From 67be67aca8ddddee80ebe8c194d3452e45ff41b7 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 18:49:30 +0100 Subject: [PATCH 118/188] Single sided seed line --- debug-tools/examples/thick-line-mul-256.rs | 45 ++++------------------ 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index cc641ad..c3041c9 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -18,7 +18,7 @@ impl MajorMinor { fn thickline( display: &mut impl DrawTarget, - mut line: Line, + line: Line, width: i32, ) -> Result<(), std::convert::Infallible> { if width == 0 { @@ -29,12 +29,6 @@ fn thickline( let seed_line = line.perpendicular(); - let mut point_left = seed_line.start; - let mut point_right = seed_line.start; - - line.start.y *= 256; - line.end.y *= 256; - let seed_line_delta = seed_line.delta(); let seed_line_direction = Point::new( @@ -54,8 +48,7 @@ fn thickline( ) }; - // Don't draw line skeleton twice - point_right -= seed_line_step.major; + let mut point = seed_line.start; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -64,7 +57,6 @@ fn thickline( let e_minor = -2 * dx; let e_major = 2 * dy; let mut seed_line_error = 0; - let mut seed_line_error_right = e_major; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_line.delta().length_squared(); @@ -72,41 +64,20 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * dx; - // Bias to one side of the line - // TODO: The current extents() function needs to respect this too, as well as stroke offset - let mut is_right = true; - while thickness_accumulator.pow(2) <= thickness_threshold { - let (point, inc, c, seed_line_error) = if is_right { - ( - &mut point_right, - MajorMinor::new(-seed_line_step.major, -seed_line_step.minor), - // Rgb888::CSS_DARK_GOLDENROD, - Rgb888::CSS_SALMON, - &mut seed_line_error_right, - ) - } else { - ( - &mut point_left, - seed_line_step, - Rgb888::CSS_FOREST_GREEN, - &mut seed_line_error, - ) - }; + let c = Rgb888::CSS_FOREST_GREEN; Pixel(Point::new(point.x, point.y), c).draw(display)?; - if *seed_line_error > threshold { - *point += inc.minor; - *seed_line_error += e_minor; + if seed_line_error > threshold { + point += seed_line_step.minor; + seed_line_error += e_minor; thickness_accumulator += 2 * dy; } - *point += inc.major; - *seed_line_error += e_major; + point += seed_line_step.major; + seed_line_error += e_major; thickness_accumulator += 2 * dx; - - is_right = !is_right; } Ok(()) From a2987849d8395e35185e9dd1f0b276a055d1a7a0 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 18:52:29 +0100 Subject: [PATCH 119/188] Correctly calculate X/Y major when multiplying Y values --- debug-tools/examples/thick-line-mul-256.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index c3041c9..db5ef58 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -26,8 +26,12 @@ fn thickline( } let non_mul_line = line; + let non_mul_delta = line.delta(); - let seed_line = line.perpendicular(); + // let mut seed_line = line.perpendicular(); + let mut seed_line = line; + seed_line.start.y *= 256; + seed_line.end.y *= 256; let seed_line_delta = seed_line.delta(); @@ -36,7 +40,9 @@ fn thickline( if seed_line_delta.y >= 0 { 1 } else { -1 }, ); - let (seed_line_delta, seed_line_step) = if seed_line_delta.y.abs() >= seed_line_delta.x.abs() { + let y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); + + let (seed_line_delta, seed_line_step) = if y_major { ( MajorMinor::new(seed_line_delta.y, seed_line_delta.x), MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), @@ -67,7 +73,7 @@ fn thickline( while thickness_accumulator.pow(2) <= thickness_threshold { let c = Rgb888::CSS_FOREST_GREEN; - Pixel(Point::new(point.x, point.y), c).draw(display)?; + Pixel(Point::new(point.x, point.y >> 8), c).draw(display)?; if seed_line_error > threshold { point += seed_line_step.minor; From b31d014ec70ad022f9a79214a1699acc2a17ff72 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 19:07:06 +0100 Subject: [PATCH 120/188] Threshold seems to be miscalculated --- debug-tools/examples/thick-line-mul-256.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index db5ef58..262de4c 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -54,27 +54,32 @@ fn thickline( ) }; - let mut point = seed_line.start; + let mut point = non_mul_line.start; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); + // dbg!(y_major, seed_line_step.minor , dx, dy); + let threshold = dx - 2 * dy; + // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; let e_major = 2 * dy; let mut seed_line_error = 0; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill - let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_line.delta().length_squared(); + let thickness_threshold = ((width - 1) * 2).pow(2) * seed_line.delta().length_squared(); // Add the first line drawn to the thickness. If this is left at zero, an extra line will be // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * dx; - while thickness_accumulator.pow(2) <= thickness_threshold { + // while thickness_accumulator.pow(2) <= thickness_threshold { + for _ in 0..20 { let c = Rgb888::CSS_FOREST_GREEN; - Pixel(Point::new(point.x, point.y >> 8), c).draw(display)?; + Pixel(Point::new(point.x, point.y), c).draw(display)?; + // We seem to hit the threshold too early and end up with a 45 degree line everywhere. if seed_line_error > threshold { point += seed_line_step.minor; seed_line_error += e_minor; From 57ec6d983672f55688a212ea8cf30d8d0d719be3 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 19:17:36 +0100 Subject: [PATCH 121/188] Make threshold work by using non-mul line delta --- debug-tools/examples/thick-line-mul-256.rs | 31 +++++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 262de4c..1ead949 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -30,8 +30,8 @@ fn thickline( // let mut seed_line = line.perpendicular(); let mut seed_line = line; - seed_line.start.y *= 256; - seed_line.end.y *= 256; + seed_line.start *= 256; + seed_line.end *= 256; let seed_line_delta = seed_line.delta(); @@ -42,13 +42,15 @@ fn thickline( let y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); - let (seed_line_delta, seed_line_step) = if y_major { + let (non_mul_majorminor, seed_line_delta, seed_line_step) = if y_major { ( + MajorMinor::new(non_mul_delta.y, non_mul_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), ) } else { ( + MajorMinor::new(non_mul_delta.x, non_mul_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), MajorMinor::new(seed_line_direction.x_axis(), seed_line_direction.y_axis()), ) @@ -59,7 +61,10 @@ fn thickline( let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); - // dbg!(y_major, seed_line_step.minor , dx, dy); + // Using non-multiplied line delta otherwise thickness threshold runs into overflow issues (I + // think? It ended up negative in testing) + let non_mul_dx = non_mul_majorminor.major.abs(); + let non_mul_dy = non_mul_majorminor.minor.abs(); let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square @@ -68,13 +73,19 @@ fn thickline( let mut seed_line_error = 0; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill - let thickness_threshold = ((width - 1) * 2).pow(2) * seed_line.delta().length_squared(); + let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_delta.length_squared(); // Add the first line drawn to the thickness. If this is left at zero, an extra line will be // drawn as the lines are drawn before checking for thickness. - let mut thickness_accumulator = 2 * dx; + let mut thickness_accumulator = 2 * non_mul_dx; + + // println!( + // "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {}", + // threshold, thickness_threshold, e_minor, e_major, dx, dy + // ); + + while thickness_accumulator.pow(2) <= thickness_threshold { + // println!("error {}", seed_line_error); - // while thickness_accumulator.pow(2) <= thickness_threshold { - for _ in 0..20 { let c = Rgb888::CSS_FOREST_GREEN; Pixel(Point::new(point.x, point.y), c).draw(display)?; @@ -83,12 +94,12 @@ fn thickline( if seed_line_error > threshold { point += seed_line_step.minor; seed_line_error += e_minor; - thickness_accumulator += 2 * dy; + thickness_accumulator += 2 * non_mul_dy; } point += seed_line_step.major; seed_line_error += e_major; - thickness_accumulator += 2 * dx; + thickness_accumulator += 2 * non_mul_dx; } Ok(()) From f54c2f723d4b8356d71e7678ef5877ac13e3497a Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 19:18:48 +0100 Subject: [PATCH 122/188] Tweak some defaults --- debug-tools/examples/thick-line-mul-256.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 1ead949..a2c4ea2 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -122,10 +122,10 @@ impl App for LineDebug { Self::DISPLAY_SIZE.height as i32 / 2, ); Self { - start: end + Point::new(10, 15), + start: end - Point::new(80, 35), end, // end: start + Point::new(100, 0), - stroke_width: 10, + stroke_width: 20, } } From d853ba3a6a5211a047b96a0201ffd17bfe0b0a04 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 19:21:18 +0100 Subject: [PATCH 123/188] Get everything working with *256 --- debug-tools/examples/thick-line-mul-256.rs | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index a2c4ea2..d170ddf 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -29,11 +29,11 @@ fn thickline( let non_mul_delta = line.delta(); // let mut seed_line = line.perpendicular(); - let mut seed_line = line; - seed_line.start *= 256; - seed_line.end *= 256; + let mut line = line; + line.start *= 256; + line.end *= 256; - let seed_line_delta = seed_line.delta(); + let seed_line_delta = line.delta(); let seed_line_direction = Point::new( if seed_line_delta.x >= 0 { 1 } else { -1 }, @@ -56,7 +56,8 @@ fn thickline( ) }; - let mut point = non_mul_line.start; + // let mut point = non_mul_line.start; + let mut point = line.start; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -78,26 +79,26 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * non_mul_dx; - // println!( - // "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {}", - // threshold, thickness_threshold, e_minor, e_major, dx, dy - // ); + println!( + "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {}", + threshold, thickness_threshold, e_minor, e_major, dx, dy + ); while thickness_accumulator.pow(2) <= thickness_threshold { - // println!("error {}", seed_line_error); + println!("error {} point {}", seed_line_error, point); let c = Rgb888::CSS_FOREST_GREEN; - Pixel(Point::new(point.x, point.y), c).draw(display)?; + Pixel(Point::new(point.x >> 8, point.y >> 8), c).draw(display)?; // We seem to hit the threshold too early and end up with a 45 degree line everywhere. if seed_line_error > threshold { - point += seed_line_step.minor; + point += seed_line_step.minor * 256; seed_line_error += e_minor; thickness_accumulator += 2 * non_mul_dy; } - point += seed_line_step.major; + point += seed_line_step.major * 256; seed_line_error += e_major; thickness_accumulator += 2 * non_mul_dx; } From d44f5450d2ab12dabec2fec8c4cd39567ca6b0fd Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 19:25:04 +0100 Subject: [PATCH 124/188] Add AA pixel drawing code Doesn't draw anything other than solid colour though --- debug-tools/examples/thick-line-mul-256.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index d170ddf..d6762e9 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -91,6 +91,26 @@ fn thickline( Pixel(Point::new(point.x >> 8, point.y >> 8), c).draw(display)?; + { + let aa_colour = { + let mul = (point.y & 255) as u8; + + Rgb888::new( + // TODO: Proper colour blend + // (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + 255 - mul, + 255 - mul, + 255 - mul, + ) + }; + + let aa_p = Point::new(point.x >> 8, (point.y >> 8) - (line.delta().y).signum() * 2); + + Pixel(aa_p, aa_colour).draw(display)?; + } + // We seem to hit the threshold too early and end up with a 45 degree line everywhere. if seed_line_error > threshold { point += seed_line_step.minor * 256; From a36b64d78bc48c3f6ee851911d3063de33578fdc Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 19:30:34 +0100 Subject: [PATCH 125/188] Use multiplied slope as Y coordinate increment --- debug-tools/examples/thick-line-mul-256.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index d6762e9..ef72a0c 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -30,8 +30,8 @@ fn thickline( // let mut seed_line = line.perpendicular(); let mut line = line; - line.start *= 256; - line.end *= 256; + line.start.y *= 256; + line.end.y *= 256; let seed_line_delta = line.delta(); @@ -79,9 +79,12 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * non_mul_dx; + // dy is multiplied by 256 so we don't get huge integer rounding errors + let slope = dy / dx; + println!( - "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {}", - threshold, thickness_threshold, e_minor, e_major, dx, dy + "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {} slope {}", + threshold, thickness_threshold, e_minor, e_major, dx, dy, slope ); while thickness_accumulator.pow(2) <= thickness_threshold { @@ -89,7 +92,7 @@ fn thickline( let c = Rgb888::CSS_FOREST_GREEN; - Pixel(Point::new(point.x >> 8, point.y >> 8), c).draw(display)?; + Pixel(Point::new(point.x, point.y >> 8), c).draw(display)?; { let aa_colour = { @@ -106,19 +109,19 @@ fn thickline( ) }; - let aa_p = Point::new(point.x >> 8, (point.y >> 8) - (line.delta().y).signum() * 2); + let aa_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum() * 2); Pixel(aa_p, aa_colour).draw(display)?; } // We seem to hit the threshold too early and end up with a 45 degree line everywhere. if seed_line_error > threshold { - point += seed_line_step.minor * 256; + point.y += slope; seed_line_error += e_minor; thickness_accumulator += 2 * non_mul_dy; } - point += seed_line_step.major * 256; + point += seed_line_step.major; seed_line_error += e_major; thickness_accumulator += 2 * non_mul_dx; } From bcb3548b8a6af3394ad8046e01c6851f7fe8c57d Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 20:24:46 +0100 Subject: [PATCH 126/188] Make things work in X-major octants --- debug-tools/examples/thick-line-mul-256.rs | 46 ++++++++++++++-------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index ef72a0c..3b27bd9 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -30,8 +30,16 @@ fn thickline( // let mut seed_line = line.perpendicular(); let mut line = line; - line.start.y *= 256; - line.end.y *= 256; + + let y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); + + if y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } let seed_line_delta = line.delta(); @@ -40,19 +48,23 @@ fn thickline( if seed_line_delta.y >= 0 { 1 } else { -1 }, ); - let y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); - - let (non_mul_majorminor, seed_line_delta, seed_line_step) = if y_major { + let (thickness_majorminor, seed_line_delta, seed_line_step) = if y_major { ( MajorMinor::new(non_mul_delta.y, non_mul_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), ) - } else { + } + // X-major line (i.e. X delta is longer than Y) + else { ( MajorMinor::new(non_mul_delta.x, non_mul_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), - MajorMinor::new(seed_line_direction.x_axis(), seed_line_direction.y_axis()), + MajorMinor::new( + seed_line_direction.x_axis(), + Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) + .component_mul(seed_line_direction), + ), ) }; @@ -64,8 +76,8 @@ fn thickline( // Using non-multiplied line delta otherwise thickness threshold runs into overflow issues (I // think? It ended up negative in testing) - let non_mul_dx = non_mul_majorminor.major.abs(); - let non_mul_dy = non_mul_majorminor.minor.abs(); + let thickness_dx = thickness_majorminor.major.abs(); + let thickness_dy = thickness_majorminor.minor.abs(); let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square @@ -77,14 +89,14 @@ fn thickline( let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_delta.length_squared(); // Add the first line drawn to the thickness. If this is left at zero, an extra line will be // drawn as the lines are drawn before checking for thickness. - let mut thickness_accumulator = 2 * non_mul_dx; + let mut thickness_accumulator = 2 * thickness_dx; - // dy is multiplied by 256 so we don't get huge integer rounding errors - let slope = dy / dx; + // // dy is multiplied by 256 so we don't get huge integer rounding errors + // let slope = dy / dx; println!( - "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {} slope {}", - threshold, thickness_threshold, e_minor, e_major, dx, dy, slope + "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {} y major {}", + threshold, thickness_threshold, e_minor, e_major, dx, dy, y_major ); while thickness_accumulator.pow(2) <= thickness_threshold { @@ -116,14 +128,14 @@ fn thickline( // We seem to hit the threshold too early and end up with a 45 degree line everywhere. if seed_line_error > threshold { - point.y += slope; + point += seed_line_step.minor; seed_line_error += e_minor; - thickness_accumulator += 2 * non_mul_dy; + thickness_accumulator += 2 * thickness_dy; } point += seed_line_step.major; seed_line_error += e_major; - thickness_accumulator += 2 * non_mul_dx; + thickness_accumulator += 2 * thickness_dx; } Ok(()) From 6f8ca4aa0285a524c863d8b6c4043bf31f1db8cc Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 20:28:39 +0100 Subject: [PATCH 127/188] Big hacks to get things working in X-major octants SURELY I can optimise this a bit lol... --- debug-tools/examples/thick-line-mul-256.rs | 48 ++++++++++++++++------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 3b27bd9..4153554 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -52,7 +52,12 @@ fn thickline( ( MajorMinor::new(non_mul_delta.y, non_mul_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), - MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), + // MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), + MajorMinor::new( + seed_line_direction.y_axis(), + Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) + .component_mul(seed_line_direction), + ), ) } // X-major line (i.e. X delta is longer than Y) @@ -91,24 +96,32 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * thickness_dx; - // // dy is multiplied by 256 so we don't get huge integer rounding errors - // let slope = dy / dx; - - println!( - "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {} y major {}", - threshold, thickness_threshold, e_minor, e_major, dx, dy, y_major - ); + // println!( + // "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {} y major {}", + // threshold, thickness_threshold, e_minor, e_major, dx, dy, y_major + // ); while thickness_accumulator.pow(2) <= thickness_threshold { - println!("error {} point {}", seed_line_error, point); + // println!("error {} point {}", seed_line_error, point); let c = Rgb888::CSS_FOREST_GREEN; - Pixel(Point::new(point.x, point.y >> 8), c).draw(display)?; + Pixel( + Point::new( + if y_major { point.x >> 8 } else { point.x }, + if y_major { point.y } else { point.y >> 8 }, + ), + c, + ) + .draw(display)?; { let aa_colour = { - let mul = (point.y & 255) as u8; + let mul = (if y_major { + point.x & 255 + } else { + point.y & 255 + }) as u8; Rgb888::new( // TODO: Proper colour blend @@ -121,7 +134,18 @@ fn thickline( ) }; - let aa_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum() * 2); + let aa_p = Point::new( + if y_major { + (point.x >> 8) - (line.delta().x).signum() * 2 + } else { + point.x + }, + if y_major { + point.y + } else { + (point.y >> 8) - (line.delta().y).signum() * 2 + }, + ); Pixel(aa_p, aa_colour).draw(display)?; } From 72d7d8f47535a2d630e6d63dc49cc77a2c31cf31 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 20:56:10 +0100 Subject: [PATCH 128/188] Switch over to seed line --- debug-tools/examples/thick-line-mul-256.rs | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 4153554..b65eb9c 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -25,23 +25,23 @@ fn thickline( return Ok(()); } - let non_mul_line = line; - let non_mul_delta = line.delta(); + let mut seed_line = line.perpendicular(); + // let mut seed_line = line; - // let mut seed_line = line.perpendicular(); - let mut line = line; + let non_mul_line = seed_line; + let non_mul_delta = seed_line.delta(); let y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); if y_major { - line.start.x *= 256; - line.end.x *= 256; + seed_line.start.x *= 256; + seed_line.end.x *= 256; } else { - line.start.y *= 256; - line.end.y *= 256; + seed_line.start.y *= 256; + seed_line.end.y *= 256; } - let seed_line_delta = line.delta(); + let seed_line_delta = seed_line.delta(); let seed_line_direction = Point::new( if seed_line_delta.x >= 0 { 1 } else { -1 }, @@ -74,7 +74,7 @@ fn thickline( }; // let mut point = non_mul_line.start; - let mut point = line.start; + let mut point = seed_line.start; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -136,14 +136,14 @@ fn thickline( let aa_p = Point::new( if y_major { - (point.x >> 8) - (line.delta().x).signum() * 2 + (point.x >> 8) - (seed_line.delta().x).signum() * 2 } else { point.x }, if y_major { point.y } else { - (point.y >> 8) - (line.delta().y).signum() * 2 + (point.y >> 8) - (seed_line.delta().y).signum() * 2 }, ); From 1339d317745701861019857f07034f2eaa24ed4d Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 30 Jun 2024 21:52:11 +0100 Subject: [PATCH 129/188] Add parallel lines, but with broken starting error value --- debug-tools/examples/thick-line-mul-256.rs | 271 ++++++++++++++++++--- 1 file changed, 241 insertions(+), 30 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index b65eb9c..fe6ec48 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -18,7 +18,7 @@ impl MajorMinor { fn thickline( display: &mut impl DrawTarget, - line: Line, + mut line: Line, width: i32, ) -> Result<(), std::convert::Infallible> { if width == 0 { @@ -26,14 +26,14 @@ fn thickline( } let mut seed_line = line.perpendicular(); - // let mut seed_line = line; - let non_mul_line = seed_line; let non_mul_delta = seed_line.delta(); - let y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); + let seed_is_y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); - if y_major { + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed + // line directly, but is used to scale initial error for each parallel line. + if seed_is_y_major { seed_line.start.x *= 256; seed_line.end.x *= 256; } else { @@ -43,20 +43,42 @@ fn thickline( let seed_line_delta = seed_line.delta(); - let seed_line_direction = Point::new( + let seed_line_step = Point::new( if seed_line_delta.x >= 0 { 1 } else { -1 }, if seed_line_delta.y >= 0 { 1 } else { -1 }, ); - let (thickness_majorminor, seed_line_delta, seed_line_step) = if y_major { + // --- + + let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if parallel_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } + + let parallel_delta = line.delta(); + + let parallel_step = Point::new( + if parallel_delta.x >= 0 { 1 } else { -1 }, + if parallel_delta.y >= 0 { 1 } else { -1 }, + ); + + // --- + + let (thickness_majorminor, seed_line_delta, seed_line_step) = if seed_is_y_major { ( MajorMinor::new(non_mul_delta.y, non_mul_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), // MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), MajorMinor::new( - seed_line_direction.y_axis(), + seed_line_step.y_axis(), Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) - .component_mul(seed_line_direction), + .component_mul(seed_line_step), ), ) } @@ -66,14 +88,37 @@ fn thickline( MajorMinor::new(non_mul_delta.x, non_mul_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), MajorMinor::new( - seed_line_direction.x_axis(), + seed_line_step.x_axis(), Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) - .component_mul(seed_line_direction), + .component_mul(seed_line_step), ), ) }; - // let mut point = non_mul_line.start; + let (parallel_delta, parallel_step) = if parallel_is_y_major { + ( + MajorMinor::new(parallel_delta.y, parallel_delta.x), + // MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), + MajorMinor::new( + parallel_step.y_axis(), + Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) + .component_mul(parallel_step), + ), + ) + } else { + ( + MajorMinor::new(parallel_delta.x, parallel_delta.y), + // MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), + MajorMinor::new( + parallel_step.x_axis(), + Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) + .component_mul(parallel_step), + ), + ) + }; + + // --- + let mut point = seed_line.start; let dx = seed_line_delta.major.abs(); @@ -89,6 +134,7 @@ fn thickline( let e_minor = -2 * dx; let e_major = 2 * dy; let mut seed_line_error = 0; + let mut parallel_error_left = 0; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_delta.length_squared(); @@ -106,14 +152,111 @@ fn thickline( let c = Rgb888::CSS_FOREST_GREEN; - Pixel( - Point::new( - if y_major { point.x >> 8 } else { point.x }, - if y_major { point.y } else { point.y >> 8 }, - ), - c, - ) - .draw(display)?; + let p = Point::new( + if seed_is_y_major { + point.x >> 8 + } else { + point.x + }, + if seed_is_y_major { + point.y + } else { + point.y >> 8 + }, + ); + + Pixel(p, c).draw(display)?; + + parallel_line( + point, + line, + parallel_step, + parallel_delta, + parallel_error_left, + Rgb888::CSS_AQUAMARINE, + false, + 0, + display, + )?; + + // We seem to hit the threshold too early and end up with a 45 degree line everywhere. + if seed_line_error > threshold { + point += seed_line_step.minor; + seed_line_error += e_minor; + thickness_accumulator += 2 * thickness_dy; + + // FIXME: This has no effect on parallel lines + if parallel_error_left > threshold { + parallel_error_left += e_minor; + } + + parallel_error_left += e_major; + } + + point += seed_line_step.major * 2; + seed_line_error += e_major; + thickness_accumulator += 2 * thickness_dx; + } + + parallel_line( + point, + line, + parallel_step, + parallel_delta, + parallel_error_left, + Rgb888::CSS_SALMON, + false, + 0, + display, + )?; + + Ok(()) +} + +fn parallel_line_aa( + start: Point, + line: Line, + step: MajorMinor, + delta: MajorMinor, + start_error: i32, + c: Rgb888, + skip_first: bool, + invert: bool, + mut last_offset: i32, + display: &mut impl DrawTarget, +) -> Result<(), std::convert::Infallible> { + let mut point = start; + + let y_major = line.delta().y >= line.delta().x; + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let mut length = dx + 1; + let mut error = start_error; + + if skip_first { + // Some of the length was consumed by this initial skip iteration. If this is omitted, the + // line will be drawn 1px too long. + last_offset -= 1; + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + + for _i in 0..(length + last_offset) { + // https://computergraphics.stackexchange.com/a/10675 + let draw_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum()); + + Pixel(draw_p, Rgb888::CYAN).draw(display)?; { let aa_colour = { @@ -136,30 +279,98 @@ fn thickline( let aa_p = Point::new( if y_major { - (point.x >> 8) - (seed_line.delta().x).signum() * 2 + (point.x >> 8) - (line.delta().x).signum() * 2 } else { point.x }, if y_major { point.y } else { - (point.y >> 8) - (seed_line.delta().y).signum() * 2 + (point.y >> 8) - (line.delta().y).signum() * 2 }, ); Pixel(aa_p, aa_colour).draw(display)?; } - // We seem to hit the threshold too early and end up with a 45 degree line everywhere. - if seed_line_error > threshold { - point += seed_line_step.minor; - seed_line_error += e_minor; - thickness_accumulator += 2 * thickness_dy; + // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not + // quite sure why but we don't get a smooth increase over the length of the line. + + if error > threshold { + point += step.minor; + error += e_minor; } - point += seed_line_step.major; - seed_line_error += e_major; - thickness_accumulator += 2 * thickness_dx; + error += e_major; + point += step.major; + } + + Ok(()) +} + +fn parallel_line( + start: Point, + line: Line, + step: MajorMinor, + delta: MajorMinor, + start_error: i32, + c: Rgb888, + skip_first: bool, + mut last_offset: i32, + display: &mut impl DrawTarget, +) -> Result<(), std::convert::Infallible> { + let y_major = line.delta().y.abs() >= line.delta().x.abs(); + + let mut point = if y_major { + Point::new(start.x >> 8, start.y * 256) + } else { + Point::new(start.x * 256, start.y >> 8) + }; + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let mut length = dx + 1; + let mut error = start_error; + + if skip_first { + // Some of the length was consumed by this initial skip iteration. If this is omitted, the + // line will be drawn 1px too long. + last_offset -= 1; + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + + for _i in 0..(length + last_offset) { + // https://computergraphics.stackexchange.com/a/10675 + let draw_p = Point::new( + if !y_major { point.x >> 8 } else { point.x }, + if !y_major { point.y } else { point.y >> 8 }, + // Are the shifts on both axes because the seed line sets the perp axis to *256, and we + // then set the line axis to *256 along side it? + // point.x >> 8, + // point.x, + // (point.y >> 8) - (line.delta().y).signum() * 2, + ); + + Pixel(draw_p, c).draw(display)?; + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; } Ok(()) From 68ac77f699daf982615300e1c970c2b99040661d Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 12 Jul 2024 11:47:35 +0100 Subject: [PATCH 130/188] Do a whole bunch of mul by 256 Still doesn't fix line start error problem --- debug-tools/examples/thick-line-mul-256.rs | 103 +++++++++------------ 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index fe6ec48..c04b5e9 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -26,27 +26,28 @@ fn thickline( } let mut seed_line = line.perpendicular(); + let deleteme = line.perpendicular().delta(); + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed + // line directly, but is used to scale initial error for each parallel line. + // if seed_is_y_major { + seed_line.start.x *= 256; + seed_line.end.x *= 256; + // } else { + seed_line.start.y *= 256; + seed_line.end.y *= 256; + // } let non_mul_delta = seed_line.delta(); let seed_is_y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed - // line directly, but is used to scale initial error for each parallel line. - if seed_is_y_major { - seed_line.start.x *= 256; - seed_line.end.x *= 256; - } else { - seed_line.start.y *= 256; - seed_line.end.y *= 256; - } - let seed_line_delta = seed_line.delta(); let seed_line_step = Point::new( if seed_line_delta.x >= 0 { 1 } else { -1 }, if seed_line_delta.y >= 0 { 1 } else { -1 }, - ); + ) * 256; // --- @@ -72,26 +73,27 @@ fn thickline( let (thickness_majorminor, seed_line_delta, seed_line_step) = if seed_is_y_major { ( - MajorMinor::new(non_mul_delta.y, non_mul_delta.x), + MajorMinor::new(deleteme.y, deleteme.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), - // MajorMinor::new(seed_line_direction.y_axis(), seed_line_direction.x_axis()), - MajorMinor::new( - seed_line_step.y_axis(), - Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) - .component_mul(seed_line_step), - ), + MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), + // MajorMinor::new( + // seed_line_step.y_axis(), + // Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) + // .component_mul(seed_line_step), + // ), ) } // X-major line (i.e. X delta is longer than Y) else { ( - MajorMinor::new(non_mul_delta.x, non_mul_delta.y), + MajorMinor::new(deleteme.x, deleteme.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), - MajorMinor::new( - seed_line_step.x_axis(), - Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) - .component_mul(seed_line_step), - ), + MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), + // MajorMinor::new( + // seed_line_step.x_axis(), + // Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) + // .component_mul(seed_line_step), + // ), ) }; @@ -100,7 +102,7 @@ fn thickline( MajorMinor::new(parallel_delta.y, parallel_delta.x), // MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), MajorMinor::new( - parallel_step.y_axis(), + parallel_step.y_axis() * 256, Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) .component_mul(parallel_step), ), @@ -110,7 +112,7 @@ fn thickline( MajorMinor::new(parallel_delta.x, parallel_delta.y), // MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), MajorMinor::new( - parallel_step.x_axis(), + parallel_step.x_axis() * 256, Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) .component_mul(parallel_step), ), @@ -137,7 +139,7 @@ fn thickline( let mut parallel_error_left = 0; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill - let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_delta.length_squared(); + let thickness_threshold = ((width - 1) * 2).pow(2) * deleteme.length_squared(); // Add the first line drawn to the thickness. If this is left at zero, an extra line will be // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * thickness_dx; @@ -156,10 +158,10 @@ fn thickline( if seed_is_y_major { point.x >> 8 } else { - point.x + point.x >> 8 }, if seed_is_y_major { - point.y + point.y >> 8 } else { point.y >> 8 }, @@ -185,7 +187,8 @@ fn thickline( seed_line_error += e_minor; thickness_accumulator += 2 * thickness_dy; - // FIXME: This has no effect on parallel lines + // Attempted fixes: + // - Threshold can be negative. .abs() doesn't fix the start error value if parallel_error_left > threshold { parallel_error_left += e_minor; } @@ -198,17 +201,17 @@ fn thickline( thickness_accumulator += 2 * thickness_dx; } - parallel_line( - point, - line, - parallel_step, - parallel_delta, - parallel_error_left, - Rgb888::CSS_SALMON, - false, - 0, - display, - )?; + // parallel_line( + // point, + // line, + // parallel_step, + // parallel_delta, + // parallel_error_left, + // Rgb888::CSS_SALMON, + // false, + // 0, + // display, + // )?; Ok(()) } @@ -319,13 +322,7 @@ fn parallel_line( mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { - let y_major = line.delta().y.abs() >= line.delta().x.abs(); - - let mut point = if y_major { - Point::new(start.x >> 8, start.y * 256) - } else { - Point::new(start.x * 256, start.y >> 8) - }; + let mut point = start; let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -352,15 +349,7 @@ fn parallel_line( for _i in 0..(length + last_offset) { // https://computergraphics.stackexchange.com/a/10675 - let draw_p = Point::new( - if !y_major { point.x >> 8 } else { point.x }, - if !y_major { point.y } else { point.y >> 8 }, - // Are the shifts on both axes because the seed line sets the perp axis to *256, and we - // then set the line axis to *256 along side it? - // point.x >> 8, - // point.x, - // (point.y >> 8) - (line.delta().y).signum() * 2, - ); + let draw_p = Point::new(point.x >> 8, point.y >> 8); Pixel(draw_p, c).draw(display)?; From 8cbafdcf039bef4ee6568b07b90d7260f2b7c84b Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 12 Jul 2024 12:34:39 +0100 Subject: [PATCH 131/188] Make stuff work again --- debug-tools/examples/thick-line-mul-256.rs | 143 +++++++++++++-------- 1 file changed, 86 insertions(+), 57 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index c04b5e9..ea31749 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -18,91 +18,97 @@ impl MajorMinor { fn thickline( display: &mut impl DrawTarget, - mut line: Line, + line: Line, width: i32, ) -> Result<(), std::convert::Infallible> { if width == 0 { return Ok(()); } + let non_mul_line = line; + let non_mul_perpendicular_delta = line.perpendicular().delta(); let mut seed_line = line.perpendicular(); - let deleteme = line.perpendicular().delta(); - - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed - // line directly, but is used to scale initial error for each parallel line. - // if seed_is_y_major { - seed_line.start.x *= 256; - seed_line.end.x *= 256; - // } else { - seed_line.start.y *= 256; - seed_line.end.y *= 256; - // } let non_mul_delta = seed_line.delta(); let seed_is_y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed + // line directly, but is used to scale initial error for each parallel line. + if seed_is_y_major { + seed_line.start.x *= 256; + seed_line.end.x *= 256; + } else { + seed_line.start.y *= 256; + seed_line.end.y *= 256; + } + let seed_line_delta = seed_line.delta(); let seed_line_step = Point::new( if seed_line_delta.x >= 0 { 1 } else { -1 }, if seed_line_delta.y >= 0 { 1 } else { -1 }, - ) * 256; - - // --- - - let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); - - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if parallel_is_y_major { - line.start.x *= 256; - line.end.x *= 256; - } else { - line.start.y *= 256; - line.end.y *= 256; - } - - let parallel_delta = line.delta(); - - let parallel_step = Point::new( - if parallel_delta.x >= 0 { 1 } else { -1 }, - if parallel_delta.y >= 0 { 1 } else { -1 }, ); - // --- - let (thickness_majorminor, seed_line_delta, seed_line_step) = if seed_is_y_major { ( - MajorMinor::new(deleteme.y, deleteme.x), + MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), - MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), - // MajorMinor::new( - // seed_line_step.y_axis(), - // Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) - // .component_mul(seed_line_step), - // ), + // MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), + MajorMinor::new( + seed_line_step.y_axis(), + Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) + .component_mul(seed_line_step), + ), ) } // X-major line (i.e. X delta is longer than Y) else { ( - MajorMinor::new(deleteme.x, deleteme.y), + MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), - MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), - // MajorMinor::new( - // seed_line_step.x_axis(), - // Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) - // .component_mul(seed_line_step), - // ), + // MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), + MajorMinor::new( + seed_line_step.x_axis(), + Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) + .component_mul(seed_line_step), + ), ) }; + // --- + + let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); + + // Using a block to isolate mutability + let line = { + let mut line = line; + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if parallel_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } + + line + }; + + let parallel_delta = line.delta(); + + let parallel_step = Point::new( + if parallel_delta.x >= 0 { 1 } else { -1 }, + if parallel_delta.y >= 0 { 1 } else { -1 }, + ); + let (parallel_delta, parallel_step) = if parallel_is_y_major { ( MajorMinor::new(parallel_delta.y, parallel_delta.x), // MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), MajorMinor::new( - parallel_step.y_axis() * 256, + parallel_step.y_axis(), Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) .component_mul(parallel_step), ), @@ -112,7 +118,7 @@ fn thickline( MajorMinor::new(parallel_delta.x, parallel_delta.y), // MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), MajorMinor::new( - parallel_step.x_axis() * 256, + parallel_step.x_axis(), Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) .component_mul(parallel_step), ), @@ -139,7 +145,8 @@ fn thickline( let mut parallel_error_left = 0; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill - let thickness_threshold = ((width - 1) * 2).pow(2) * deleteme.length_squared(); + let thickness_threshold = + ((width - 1) * 2).pow(2) * non_mul_perpendicular_delta.length_squared(); // Add the first line drawn to the thickness. If this is left at zero, an extra line will be // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * thickness_dx; @@ -158,10 +165,10 @@ fn thickline( if seed_is_y_major { point.x >> 8 } else { - point.x >> 8 + point.x }, if seed_is_y_major { - point.y >> 8 + point.y } else { point.y >> 8 }, @@ -171,7 +178,7 @@ fn thickline( parallel_line( point, - line, + non_mul_line, parallel_step, parallel_delta, parallel_error_left, @@ -203,7 +210,7 @@ fn thickline( // parallel_line( // point, - // line, + // non_mul_line, // parallel_step, // parallel_delta, // parallel_error_left, @@ -322,7 +329,16 @@ fn parallel_line( mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { - let mut point = start; + dbg!(start, step); + + let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; + + // Perpendicular axis is scaled by 256. We need to swap the scaled axes to account for this. + let mut point = if line_is_y_major { + Point::new(start.x * 256, start.y >> 8) + } else { + Point::new(start.x >> 8, start.y * 256) + }; let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -349,7 +365,20 @@ fn parallel_line( for _i in 0..(length + last_offset) { // https://computergraphics.stackexchange.com/a/10675 - let draw_p = Point::new(point.x >> 8, point.y >> 8); + let draw_p = Point::new( + if line_is_y_major { + point.x >> 8 + } else { + point.x + }, + if line_is_y_major { + point.y + } else { + point.y >> 8 + }, + ); + + // dbg!(draw_p, step); Pixel(draw_p, c).draw(display)?; From 23dee7412d1a377bcc10f0e9abd1e68afab0844b Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 12 Jul 2024 19:35:10 +0100 Subject: [PATCH 132/188] Correct start error offset calculation Don't need to multiply nearly everything in the minor axis by 256 - I can just do it along the parallel line. --- debug-tools/examples/thick-line-mul-256.rs | 179 +++++++++++++-------- 1 file changed, 114 insertions(+), 65 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index ea31749..cfbed91 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -27,21 +27,27 @@ fn thickline( let non_mul_line = line; let non_mul_perpendicular_delta = line.perpendicular().delta(); - let mut seed_line = line.perpendicular(); + let seed_line = line.perpendicular(); - let non_mul_delta = seed_line.delta(); - - let seed_is_y_major = non_mul_delta.y.abs() >= non_mul_delta.x.abs(); + let seed_is_y_major = + non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed - // line directly, but is used to scale initial error for each parallel line. - if seed_is_y_major { - seed_line.start.x *= 256; - seed_line.end.x *= 256; - } else { - seed_line.start.y *= 256; - seed_line.end.y *= 256; - } + // line directly, but is used to scale initial error for each parallel line. Using a block to + // contain mutability. + // let seed_line = { + // let mut seed_line = seed_line; + + // if seed_is_y_major { + // seed_line.start.x *= 256; + // seed_line.end.x *= 256; + // } else { + // seed_line.start.y *= 256; + // seed_line.end.y *= 256; + // } + + // seed_line + // }; let seed_line_delta = seed_line.delta(); @@ -54,12 +60,12 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), - // MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), - MajorMinor::new( - seed_line_step.y_axis(), - Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) - .component_mul(seed_line_step), - ), + MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), + // MajorMinor::new( + // seed_line_step.y_axis(), + // Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) + // .component_mul(seed_line_step), + // ), ) } // X-major line (i.e. X delta is longer than Y) @@ -67,12 +73,12 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), - // MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), - MajorMinor::new( - seed_line_step.x_axis(), - Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) - .component_mul(seed_line_step), - ), + MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), + // MajorMinor::new( + // seed_line_step.x_axis(), + // Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) + // .component_mul(seed_line_step), + // ), ) }; @@ -80,21 +86,21 @@ fn thickline( let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); - // Using a block to isolate mutability - let line = { - let mut line = line; - - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if parallel_is_y_major { - line.start.x *= 256; - line.end.x *= 256; - } else { - line.start.y *= 256; - line.end.y *= 256; - } + // // Using a block to isolate mutability + // let line = { + // let mut line = line; - line - }; + // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + // if parallel_is_y_major { + // line.start.x *= 256; + // line.end.x *= 256; + // } else { + // line.start.y *= 256; + // line.end.y *= 256; + // } + + // line + // }; let parallel_delta = line.delta(); @@ -106,22 +112,22 @@ fn thickline( let (parallel_delta, parallel_step) = if parallel_is_y_major { ( MajorMinor::new(parallel_delta.y, parallel_delta.x), - // MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), - MajorMinor::new( - parallel_step.y_axis(), - Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) - .component_mul(parallel_step), - ), + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), + // MajorMinor::new( + // parallel_step.y_axis(), + // Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) + // .component_mul(parallel_step), + // ), ) } else { ( MajorMinor::new(parallel_delta.x, parallel_delta.y), - // MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), - MajorMinor::new( - parallel_step.x_axis(), - Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) - .component_mul(parallel_step), - ), + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), + // MajorMinor::new( + // parallel_step.x_axis(), + // Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) + // .component_mul(parallel_step), + // ), ) }; @@ -137,6 +143,26 @@ fn thickline( let thickness_dx = thickness_majorminor.major.abs(); let thickness_dy = thickness_majorminor.minor.abs(); + // Start error must be scaled the same as the major/minor errors used in `parallel_line()` to + // set the starting error correctly. + let parallel_dx = parallel_delta.major.abs(); + let parallel_dy = parallel_delta.minor.abs(); + let parallel_e_minor = -2 * parallel_dx; + let parallel_e_major = 2 * parallel_dy; + let parallel_threshold = parallel_dx - 2 * parallel_dy; + + dbg!( + dx, + dy, + parallel_dx, + parallel_dy, + seed_is_y_major, + parallel_is_y_major, + parallel_e_minor, + parallel_e_major, + parallel_threshold + ); + let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; @@ -144,6 +170,8 @@ fn thickline( let mut seed_line_error = 0; let mut parallel_error_left = 0; + let mut parallel_error_scaled = 0; + // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_perpendicular_delta.length_squared(); @@ -156,21 +184,29 @@ fn thickline( // threshold, thickness_threshold, e_minor, e_major, dx, dy, y_major // ); + println!("\n=== start loop ===\n"); + + dbg!(seed_line_step, seed_line_delta, e_major, e_minor); + while thickness_accumulator.pow(2) <= thickness_threshold { + println!("--- Seed iter"); + // println!("error {} point {}", seed_line_error, point); let c = Rgb888::CSS_FOREST_GREEN; let p = Point::new( if seed_is_y_major { - point.x >> 8 + // point.x >> 8 + point.x } else { point.x }, if seed_is_y_major { point.y } else { - point.y >> 8 + // point.y >> 8 + point.y }, ); @@ -181,28 +217,35 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - parallel_error_left, + parallel_error_scaled, Rgb888::CSS_AQUAMARINE, false, 0, display, )?; - // We seem to hit the threshold too early and end up with a 45 degree line everywhere. + // Move seed line in minor direction if seed_line_error > threshold { point += seed_line_step.minor; seed_line_error += e_minor; thickness_accumulator += 2 * thickness_dy; + println!("---- Seed minor step"); + // Attempted fixes: // - Threshold can be negative. .abs() doesn't fix the start error value - if parallel_error_left > threshold { - parallel_error_left += e_minor; + if parallel_error_left > parallel_threshold { + println!("---- Parallel minor step"); + + parallel_error_left += parallel_e_minor; + parallel_error_scaled += parallel_e_minor; } - parallel_error_left += e_major; + parallel_error_left += parallel_e_major; + parallel_error_scaled += parallel_e_major; } + // Multiply by 2 to separate individual lines for dbugging reasons point += seed_line_step.major * 2; seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; @@ -321,7 +364,7 @@ fn parallel_line_aa( fn parallel_line( start: Point, line: Line, - step: MajorMinor, + mut step: MajorMinor, delta: MajorMinor, start_error: i32, c: Rgb888, @@ -329,17 +372,19 @@ fn parallel_line( mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { - dbg!(start, step); - let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - // Perpendicular axis is scaled by 256. We need to swap the scaled axes to account for this. + // let mut point = start; + + // Scale minor axis by 256 let mut point = if line_is_y_major { - Point::new(start.x * 256, start.y >> 8) + Point::new(start.x * 256, start.y) } else { - Point::new(start.x >> 8, start.y * 256) + Point::new(start.x, start.y * 256) }; + step.minor *= 256; + let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -349,6 +394,8 @@ fn parallel_line( let mut length = dx + 1; let mut error = start_error; + dbg!(start_error, e_minor, e_major, threshold); + if skip_first { // Some of the length was consumed by this initial skip iteration. If this is omitted, the // line will be drawn 1px too long. @@ -368,6 +415,7 @@ fn parallel_line( let draw_p = Point::new( if line_is_y_major { point.x >> 8 + // point.x } else { point.x }, @@ -375,6 +423,7 @@ fn parallel_line( point.y } else { point.y >> 8 + // point.y }, ); @@ -383,8 +432,8 @@ fn parallel_line( Pixel(draw_p, c).draw(display)?; if error > threshold { - point += step.minor; error += e_minor; + point += step.minor; } error += e_major; @@ -414,7 +463,7 @@ impl App for LineDebug { start: end - Point::new(80, 35), end, // end: start + Point::new(100, 0), - stroke_width: 20, + stroke_width: 10, } } From 81fa44b9379937a958dc690a0b5f46193d58ab0f Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 12 Jul 2024 19:36:53 +0100 Subject: [PATCH 133/188] Minor cleanup --- debug-tools/examples/thick-line-mul-256.rs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index cfbed91..54f6b2e 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -170,8 +170,6 @@ fn thickline( let mut seed_line_error = 0; let mut parallel_error_left = 0; - let mut parallel_error_scaled = 0; - // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill let thickness_threshold = ((width - 1) * 2).pow(2) * non_mul_perpendicular_delta.length_squared(); @@ -196,18 +194,8 @@ fn thickline( let c = Rgb888::CSS_FOREST_GREEN; let p = Point::new( - if seed_is_y_major { - // point.x >> 8 - point.x - } else { - point.x - }, - if seed_is_y_major { - point.y - } else { - // point.y >> 8 - point.y - }, + if seed_is_y_major { point.x } else { point.x }, + if seed_is_y_major { point.y } else { point.y }, ); Pixel(p, c).draw(display)?; @@ -217,7 +205,7 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - parallel_error_scaled, + parallel_error_left, Rgb888::CSS_AQUAMARINE, false, 0, @@ -238,11 +226,9 @@ fn thickline( println!("---- Parallel minor step"); parallel_error_left += parallel_e_minor; - parallel_error_scaled += parallel_e_minor; } parallel_error_left += parallel_e_major; - parallel_error_scaled += parallel_e_major; } // Multiply by 2 to separate individual lines for dbugging reasons From 19bac1c89764c500c9d9fc0493b60d8d748dd6ba Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 13 Jul 2024 11:07:35 +0100 Subject: [PATCH 134/188] WIP I forgot what I was doing --- debug-tools/examples/thick-line-mul-256.rs | 106 ++++++++++++++++----- 1 file changed, 84 insertions(+), 22 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 54f6b2e..d4023fe 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -218,12 +218,27 @@ fn thickline( seed_line_error += e_minor; thickness_accumulator += 2 * thickness_dy; - println!("---- Seed minor step"); + // println!("---- Seed minor step"); // Attempted fixes: // - Threshold can be negative. .abs() doesn't fix the start error value if parallel_error_left > parallel_threshold { - println!("---- Parallel minor step"); + // println!("---- Parallel minor step"); + + // Add some spacing for debugging + point += seed_line_step.major * 2; + + parallel_line( + point, + non_mul_line, + parallel_step, + parallel_delta, + parallel_error_left + e_minor + e_major, + Rgb888::CSS_SALMON, + false, + 0, + display, + )?; parallel_error_left += parallel_e_minor; } @@ -237,17 +252,19 @@ fn thickline( thickness_accumulator += 2 * thickness_dx; } - // parallel_line( - // point, - // non_mul_line, - // parallel_step, - // parallel_delta, - // parallel_error_left, - // Rgb888::CSS_SALMON, - // false, - // 0, - // display, - // )?; + // Final AA line + parallel_line_aa( + point, + non_mul_line, + parallel_step, + parallel_delta, + 0, + Rgb888::CSS_GOLDENROD, + false, + false, + 0, + display, + )?; Ok(()) } @@ -255,7 +272,7 @@ fn thickline( fn parallel_line_aa( start: Point, line: Line, - step: MajorMinor, + mut step: MajorMinor, delta: MajorMinor, start_error: i32, c: Rgb888, @@ -266,7 +283,52 @@ fn parallel_line_aa( ) -> Result<(), std::convert::Infallible> { let mut point = start; - let y_major = line.delta().y >= line.delta().x; + let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; + + // Using a block to isolate mutability + let line = { + let mut line = line; + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if line_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + point.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + point.y *= 256; + } + + line + }; + + let parallel_delta = line.delta(); + + let parallel_step = Point::new( + if parallel_delta.x >= 0 { 1 } else { -1 }, + if parallel_delta.y >= 0 { 1 } else { -1 }, + ); + + let (delta, step) = if line_is_y_major { + ( + MajorMinor::new(parallel_delta.y, parallel_delta.x), + MajorMinor::new( + parallel_step.y_axis(), + Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) + .component_mul(parallel_step), + ), + ) + } else { + ( + MajorMinor::new(parallel_delta.x, parallel_delta.y), + MajorMinor::new( + parallel_step.x_axis(), + Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) + .component_mul(parallel_step), + ), + ) + }; let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -292,14 +354,14 @@ fn parallel_line_aa( } for _i in 0..(length + last_offset) { - // https://computergraphics.stackexchange.com/a/10675 - let draw_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum()); + // // https://computergraphics.stackexchange.com/a/10675 + // let draw_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum()); - Pixel(draw_p, Rgb888::CYAN).draw(display)?; + // Pixel(draw_p, c).draw(display)?; { let aa_colour = { - let mul = (if y_major { + let mul = (if line_is_y_major { point.x & 255 } else { point.y & 255 @@ -317,12 +379,12 @@ fn parallel_line_aa( }; let aa_p = Point::new( - if y_major { + if line_is_y_major { (point.x >> 8) - (line.delta().x).signum() * 2 } else { point.x }, - if y_major { + if line_is_y_major { point.y } else { (point.y >> 8) - (line.delta().y).signum() * 2 @@ -380,7 +442,7 @@ fn parallel_line( let mut length = dx + 1; let mut error = start_error; - dbg!(start_error, e_minor, e_major, threshold); + // dbg!(start_error, e_minor, e_major, threshold); if skip_first { // Some of the length was consumed by this initial skip iteration. If this is omitted, the From cc46f59240fc34ec9e72c5b5ed5b22d48dc3e1dd Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 17 Aug 2024 13:29:57 +0100 Subject: [PATCH 135/188] Fix flipped octant line phasing --- debug-tools/examples/thick-line-mul-256.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index d4023fe..6bf5990 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -186,6 +186,14 @@ fn thickline( dbg!(seed_line_step, seed_line_delta, e_major, e_minor); + // This fixes the phasing for parallel lines on the left side of the base line for the octants + // where the line perpendicular moves "away" from the line body. + let flip = if seed_line_step.minor == -parallel_step.major { + -1 + } else { + 1 + }; + while thickness_accumulator.pow(2) <= thickness_threshold { println!("--- Seed iter"); @@ -205,7 +213,7 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - parallel_error_left, + parallel_error_left * flip, Rgb888::CSS_AQUAMARINE, false, 0, @@ -233,7 +241,7 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - parallel_error_left + e_minor + e_major, + (parallel_error_left + e_minor + e_major) * flip, Rgb888::CSS_SALMON, false, 0, From 413ee55947810c9c4b6b109596c34307ca783c08 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 17 Aug 2024 13:32:09 +0100 Subject: [PATCH 136/188] Disable a bunch of debug prints --- debug-tools/examples/thick-line-mul-256.rs | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 6bf5990..c77580f 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -151,17 +151,17 @@ fn thickline( let parallel_e_major = 2 * parallel_dy; let parallel_threshold = parallel_dx - 2 * parallel_dy; - dbg!( - dx, - dy, - parallel_dx, - parallel_dy, - seed_is_y_major, - parallel_is_y_major, - parallel_e_minor, - parallel_e_major, - parallel_threshold - ); + // dbg!( + // dx, + // dy, + // parallel_dx, + // parallel_dy, + // seed_is_y_major, + // parallel_is_y_major, + // parallel_e_minor, + // parallel_e_major, + // parallel_threshold + // ); let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square @@ -182,7 +182,7 @@ fn thickline( // threshold, thickness_threshold, e_minor, e_major, dx, dy, y_major // ); - println!("\n=== start loop ===\n"); + // println!("\n=== start loop ===\n"); dbg!(seed_line_step, seed_line_delta, e_major, e_minor); @@ -195,7 +195,7 @@ fn thickline( }; while thickness_accumulator.pow(2) <= thickness_threshold { - println!("--- Seed iter"); + // println!("--- Seed iter"); // println!("error {} point {}", seed_line_error, point); From ff66ec94480881f43f0a25a889a92ae7227c1ad7 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 17 Aug 2024 13:36:39 +0100 Subject: [PATCH 137/188] Draw non-AA final line with correct phasing Just need to add AA back in now --- debug-tools/examples/thick-line-mul-256.rs | 134 +++++++++++---------- 1 file changed, 70 insertions(+), 64 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index c77580f..2d42e8e 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -266,7 +266,7 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - 0, + parallel_error_left * flip, Rgb888::CSS_GOLDENROD, false, false, @@ -293,50 +293,50 @@ fn parallel_line_aa( let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - // Using a block to isolate mutability - let line = { - let mut line = line; - - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if line_is_y_major { - line.start.x *= 256; - line.end.x *= 256; - point.x *= 256; - } else { - line.start.y *= 256; - line.end.y *= 256; - point.y *= 256; - } + // // Using a block to isolate mutability + // let line = { + // let mut line = line; - line - }; + // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + // if line_is_y_major { + // line.start.x *= 256; + // line.end.x *= 256; + // point.x *= 256; + // } else { + // line.start.y *= 256; + // line.end.y *= 256; + // point.y *= 256; + // } - let parallel_delta = line.delta(); + // line + // }; - let parallel_step = Point::new( - if parallel_delta.x >= 0 { 1 } else { -1 }, - if parallel_delta.y >= 0 { 1 } else { -1 }, - ); + // let parallel_delta = line.delta(); - let (delta, step) = if line_is_y_major { - ( - MajorMinor::new(parallel_delta.y, parallel_delta.x), - MajorMinor::new( - parallel_step.y_axis(), - Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) - .component_mul(parallel_step), - ), - ) - } else { - ( - MajorMinor::new(parallel_delta.x, parallel_delta.y), - MajorMinor::new( - parallel_step.x_axis(), - Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) - .component_mul(parallel_step), - ), - ) - }; + // let parallel_step = Point::new( + // if parallel_delta.x >= 0 { 1 } else { -1 }, + // if parallel_delta.y >= 0 { 1 } else { -1 }, + // ); + + // let (delta, step) = if line_is_y_major { + // ( + // MajorMinor::new(parallel_delta.y, parallel_delta.x), + // MajorMinor::new( + // parallel_step.y_axis(), + // Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) + // .component_mul(parallel_step), + // ), + // ) + // } else { + // ( + // MajorMinor::new(parallel_delta.x, parallel_delta.y), + // MajorMinor::new( + // parallel_step.x_axis(), + // Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) + // .component_mul(parallel_step), + // ), + // ) + // }; let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -370,34 +370,40 @@ fn parallel_line_aa( { let aa_colour = { let mul = (if line_is_y_major { - point.x & 255 + // point.x & 255 + point.x } else { - point.y & 255 + // point.y & 255 + point.y }) as u8; - Rgb888::new( - // TODO: Proper colour blend - // (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - // (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - // (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - 255 - mul, - 255 - mul, - 255 - mul, - ) + // Rgb888::new( + // // TODO: Proper colour blend + // // (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // // (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // // (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // 255 - mul, + // 255 - mul, + // 255 - mul, + // ) + + c }; - let aa_p = Point::new( - if line_is_y_major { - (point.x >> 8) - (line.delta().x).signum() * 2 - } else { - point.x - }, - if line_is_y_major { - point.y - } else { - (point.y >> 8) - (line.delta().y).signum() * 2 - }, - ); + // let aa_p = Point::new( + // if line_is_y_major { + // (point.x >> 8) - (line.delta().x).signum() * 2 + // } else { + // point.x + // }, + // if line_is_y_major { + // point.y + // } else { + // (point.y >> 8) - (line.delta().y).signum() * 2 + // }, + // ); + + let aa_p = point; Pixel(aa_p, aa_colour).draw(display)?; } From 3a8de9c277580c424841715e7d64a8082b8b72e9 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 17 Aug 2024 15:33:03 +0100 Subject: [PATCH 138/188] Reuse AA function to draw thick line in phase with AA Still need to figure out initial error - there are still holes in the thick line interior --- debug-tools/examples/thick-line-mul-256.rs | 283 +++++++++++++++------ 1 file changed, 201 insertions(+), 82 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 2d42e8e..77d192f 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -184,7 +184,7 @@ fn thickline( // println!("\n=== start loop ===\n"); - dbg!(seed_line_step, seed_line_delta, e_major, e_minor); + // dbg!(seed_line_step, seed_line_delta, e_major, e_minor); // This fixes the phasing for parallel lines on the left side of the base line for the octants // where the line perpendicular moves "away" from the line body. @@ -208,7 +208,7 @@ fn thickline( Pixel(p, c).draw(display)?; - parallel_line( + parallel_line_2( point, non_mul_line, parallel_step, @@ -216,6 +216,7 @@ fn thickline( parallel_error_left * flip, Rgb888::CSS_AQUAMARINE, false, + false, 0, display, )?; @@ -236,17 +237,18 @@ fn thickline( // Add some spacing for debugging point += seed_line_step.major * 2; - parallel_line( - point, - non_mul_line, - parallel_step, - parallel_delta, - (parallel_error_left + e_minor + e_major) * flip, - Rgb888::CSS_SALMON, - false, - 0, - display, - )?; + // parallel_line_2( + // point, + // non_mul_line, + // parallel_step, + // parallel_delta, + // (parallel_error_left + e_minor + e_major) * flip, + // Rgb888::CSS_SALMON, + // false, + // false, + // 0, + // display, + // )?; parallel_error_left += parallel_e_minor; } @@ -293,50 +295,50 @@ fn parallel_line_aa( let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - // // Using a block to isolate mutability - // let line = { - // let mut line = line; - - // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - // if line_is_y_major { - // line.start.x *= 256; - // line.end.x *= 256; - // point.x *= 256; - // } else { - // line.start.y *= 256; - // line.end.y *= 256; - // point.y *= 256; - // } + // Using a block to isolate mutability + let line = { + let mut line = line; + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if line_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + point.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + point.y *= 256; + } - // line - // }; + line + }; - // let parallel_delta = line.delta(); + let parallel_delta = line.delta(); - // let parallel_step = Point::new( - // if parallel_delta.x >= 0 { 1 } else { -1 }, - // if parallel_delta.y >= 0 { 1 } else { -1 }, - // ); + let parallel_step = Point::new( + if parallel_delta.x >= 0 { 1 } else { -1 }, + if parallel_delta.y >= 0 { 1 } else { -1 }, + ); - // let (delta, step) = if line_is_y_major { - // ( - // MajorMinor::new(parallel_delta.y, parallel_delta.x), - // MajorMinor::new( - // parallel_step.y_axis(), - // Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) - // .component_mul(parallel_step), - // ), - // ) - // } else { - // ( - // MajorMinor::new(parallel_delta.x, parallel_delta.y), - // MajorMinor::new( - // parallel_step.x_axis(), - // Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) - // .component_mul(parallel_step), - // ), - // ) - // }; + let (delta, step) = if line_is_y_major { + ( + MajorMinor::new(parallel_delta.y, parallel_delta.x), + MajorMinor::new( + parallel_step.y_axis(), + Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) + .component_mul(parallel_step), + ), + ) + } else { + ( + MajorMinor::new(parallel_delta.x, parallel_delta.y), + MajorMinor::new( + parallel_step.x_axis(), + Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) + .component_mul(parallel_step), + ), + ) + }; let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -370,40 +372,40 @@ fn parallel_line_aa( { let aa_colour = { let mul = (if line_is_y_major { - // point.x & 255 - point.x + point.x & 255 + // point.x } else { - // point.y & 255 - point.y + point.y & 255 + // point.y }) as u8; - // Rgb888::new( - // // TODO: Proper colour blend - // // (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - // // (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - // // (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - // 255 - mul, - // 255 - mul, - // 255 - mul, - // ) - - c + Rgb888::new( + // TODO: Proper colour blend + // (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + 255 - mul, + 255 - mul, + 255 - mul, + ) + + // c }; - // let aa_p = Point::new( - // if line_is_y_major { - // (point.x >> 8) - (line.delta().x).signum() * 2 - // } else { - // point.x - // }, - // if line_is_y_major { - // point.y - // } else { - // (point.y >> 8) - (line.delta().y).signum() * 2 - // }, - // ); - - let aa_p = point; + let aa_p = Point::new( + if line_is_y_major { + (point.x >> 8) - (line.delta().x).signum() * 2 + } else { + point.x + }, + if line_is_y_major { + point.y + } else { + (point.y >> 8) - (line.delta().y).signum() * 2 + }, + ); + + // let aa_p = point; Pixel(aa_p, aa_colour).draw(display)?; } @@ -423,6 +425,123 @@ fn parallel_line_aa( Ok(()) } +fn parallel_line_2( + start: Point, + line: Line, + mut step: MajorMinor, + delta: MajorMinor, + start_error: i32, + c: Rgb888, + skip_first: bool, + invert: bool, + mut last_offset: i32, + display: &mut impl DrawTarget, +) -> Result<(), std::convert::Infallible> { + let mut point = start; + + let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; + + // Using a block to isolate mutability + let line = { + let mut line = line; + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if line_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + point.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + point.y *= 256; + } + + line + }; + + let parallel_delta = line.delta(); + + let parallel_step = Point::new( + if parallel_delta.x >= 0 { 1 } else { -1 }, + if parallel_delta.y >= 0 { 1 } else { -1 }, + ); + + let (delta, step) = if line_is_y_major { + ( + MajorMinor::new(parallel_delta.y, parallel_delta.x), + MajorMinor::new( + parallel_step.y_axis(), + Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) + .component_mul(parallel_step), + ), + ) + } else { + ( + MajorMinor::new(parallel_delta.x, parallel_delta.y), + MajorMinor::new( + parallel_step.x_axis(), + Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) + .component_mul(parallel_step), + ), + ) + }; + + let dx = delta.major.abs(); + let dy = delta.minor.abs(); + + let threshold = dx - 2 * dy; + let e_minor = -2 * dx; + let e_major = 2 * dy; + let mut length = dx + 1; + let mut error = start_error; + + if skip_first { + // Some of the length was consumed by this initial skip iteration. If this is omitted, the + // line will be drawn 1px too long. + last_offset -= 1; + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + + for _i in 0..(length + last_offset) { + let aa_colour = c; + + let aa_p = Point::new( + if line_is_y_major { + point.x >> 8 + } else { + point.x + }, + if line_is_y_major { + point.y + } else { + point.y >> 8 + }, + ); + + Pixel(aa_p, aa_colour).draw(display)?; + + // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not + // quite sure why but we don't get a smooth increase over the length of the line. + + if error > threshold { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + + Ok(()) +} + fn parallel_line( start: Point, line: Line, From 5f364c922a6e30c755a4c7b8712966eaea173362 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 10:50:30 +0100 Subject: [PATCH 139/188] Don't recalculate parallel step/delta in line function --- debug-tools/examples/thick-line-mul-256.rs | 159 +++++++++++---------- 1 file changed, 85 insertions(+), 74 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 77d192f..fe4c111 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -28,6 +28,7 @@ fn thickline( let non_mul_line = line; let non_mul_perpendicular_delta = line.perpendicular().delta(); let seed_line = line.perpendicular(); + let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; let seed_is_y_major = non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); @@ -86,23 +87,25 @@ fn thickline( let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); - // // Using a block to isolate mutability - // let line = { - // let mut line = line; + // Using a block to isolate mutability + let mul_line = { + let mut line = line; - // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - // if parallel_is_y_major { - // line.start.x *= 256; - // line.end.x *= 256; - // } else { - // line.start.y *= 256; - // line.end.y *= 256; - // } + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if parallel_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } - // line - // }; + line + }; - let parallel_delta = line.delta(); + let mul_delta = mul_line.delta(); + + let parallel_delta = mul_line.delta(); let parallel_step = Point::new( if parallel_delta.x >= 0 { 1 } else { -1 }, @@ -112,22 +115,20 @@ fn thickline( let (parallel_delta, parallel_step) = if parallel_is_y_major { ( MajorMinor::new(parallel_delta.y, parallel_delta.x), - MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), - // MajorMinor::new( - // parallel_step.y_axis(), - // Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) - // .component_mul(parallel_step), - // ), + // MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), + MajorMinor::new( + parallel_step.y_axis(), + Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), + ), ) } else { ( MajorMinor::new(parallel_delta.x, parallel_delta.y), - MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), - // MajorMinor::new( - // parallel_step.x_axis(), - // Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) - // .component_mul(parallel_step), - // ), + // MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), + MajorMinor::new( + parallel_step.x_axis(), + Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), + ), ) }; @@ -147,9 +148,10 @@ fn thickline( // set the starting error correctly. let parallel_dx = parallel_delta.major.abs(); let parallel_dy = parallel_delta.minor.abs(); + + let parallel_threshold = parallel_dx - 2 * parallel_dy; let parallel_e_minor = -2 * parallel_dx; let parallel_e_major = 2 * parallel_dy; - let parallel_threshold = parallel_dx - 2 * parallel_dy; // dbg!( // dx, @@ -237,18 +239,18 @@ fn thickline( // Add some spacing for debugging point += seed_line_step.major * 2; - // parallel_line_2( - // point, - // non_mul_line, - // parallel_step, - // parallel_delta, - // (parallel_error_left + e_minor + e_major) * flip, - // Rgb888::CSS_SALMON, - // false, - // false, - // 0, - // display, - // )?; + parallel_line_2( + point, + non_mul_line, + parallel_step, + parallel_delta, + (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, + Rgb888::CSS_SALMON, + false, + false, + 0, + display, + )?; parallel_error_left += parallel_e_minor; } @@ -428,7 +430,7 @@ fn parallel_line_aa( fn parallel_line_2( start: Point, line: Line, - mut step: MajorMinor, + step: MajorMinor, delta: MajorMinor, start_error: i32, c: Rgb888, @@ -437,54 +439,63 @@ fn parallel_line_2( mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { - let mut point = start; - let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - // Using a block to isolate mutability - let line = { - let mut line = line; + let mut point = { + let mut point = start; // Multiply minor direction by 256 so we get AA resolution in lower 8 bits if line_is_y_major { - line.start.x *= 256; - line.end.x *= 256; point.x *= 256; } else { - line.start.y *= 256; - line.end.y *= 256; point.y *= 256; } - line + point }; - let parallel_delta = line.delta(); + // // Using a block to isolate mutability + // let line = { + // let mut line = line; - let parallel_step = Point::new( - if parallel_delta.x >= 0 { 1 } else { -1 }, - if parallel_delta.y >= 0 { 1 } else { -1 }, - ); + // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + // if line_is_y_major { + // line.start.x *= 256; + // line.end.x *= 256; + // point.x *= 256; + // } else { + // line.start.y *= 256; + // line.end.y *= 256; + // point.y *= 256; + // } - let (delta, step) = if line_is_y_major { - ( - MajorMinor::new(parallel_delta.y, parallel_delta.x), - MajorMinor::new( - parallel_step.y_axis(), - Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) - .component_mul(parallel_step), - ), - ) - } else { - ( - MajorMinor::new(parallel_delta.x, parallel_delta.y), - MajorMinor::new( - parallel_step.x_axis(), - Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) - .component_mul(parallel_step), - ), - ) - }; + // line + // }; + + // let delta = line.delta(); + + // let step = Point::new( + // if delta.x >= 0 { 1 } else { -1 }, + // if delta.y >= 0 { 1 } else { -1 }, + // ); + + // let (delta, step) = if line_is_y_major { + // ( + // MajorMinor::new(delta.y, delta.x), + // MajorMinor::new( + // step.y_axis(), + // Point::new((delta.x / delta.y).abs(), 0).component_mul(step), + // ), + // ) + // } else { + // ( + // MajorMinor::new(delta.x, delta.y), + // MajorMinor::new( + // step.x_axis(), + // Point::new(0, (delta.y / delta.x).abs()).component_mul(step), + // ), + // ) + // }; let dx = delta.major.abs(); let dy = delta.minor.abs(); From ff5cd422cd2b3ca11e3dbb0de68afd095d658582 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 11:44:46 +0100 Subject: [PATCH 140/188] Mul 256 seed line (doesn't work very well) --- debug-tools/examples/thick-line-mul-256.rs | 288 +++++++++++---------- 1 file changed, 155 insertions(+), 133 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index fe4c111..952e990 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -25,14 +25,49 @@ fn thickline( return Ok(()); } - let non_mul_line = line; - let non_mul_perpendicular_delta = line.perpendicular().delta(); - let seed_line = line.perpendicular(); let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; + let non_mul_line = line; + let non_mul_perpendicular_delta = line.perpendicular().delta(); let seed_is_y_major = non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); + let seed_line = line.perpendicular(); + + dbg!(seed_is_y_major); + + // Using a block to isolate mutability + let seed_line = { + let mut seed_line = seed_line; + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if seed_is_y_major { + seed_line.start.x *= 256; + seed_line.end.x *= 256; + } else { + seed_line.start.y *= 256; + seed_line.end.y *= 256; + } + + seed_line + }; + + // Using a block to isolate mutability + let line = { + let mut line = line; + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if line_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } + + line + }; + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed // line directly, but is used to scale initial error for each parallel line. Using a block to // contain mutability. @@ -61,12 +96,11 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), - MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), - // MajorMinor::new( - // seed_line_step.y_axis(), - // Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) - // .component_mul(seed_line_step), - // ), + MajorMinor::new( + seed_line_step.y_axis(), + Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) + .component_mul(seed_line_step), + ), ) } // X-major line (i.e. X delta is longer than Y) @@ -74,34 +108,36 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), - MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), - // MajorMinor::new( - // seed_line_step.x_axis(), - // Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) - // .component_mul(seed_line_step), - // ), + MajorMinor::new( + seed_line_step.x_axis(), + Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) + .component_mul(seed_line_step), + ), ) }; // --- - let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); + // let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); + let parallel_is_y_major = line_is_y_major; - // Using a block to isolate mutability - let mul_line = { - let mut line = line; + let mul_line = line; - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if parallel_is_y_major { - line.start.x *= 256; - line.end.x *= 256; - } else { - line.start.y *= 256; - line.end.y *= 256; - } + // // Using a block to isolate mutability + // let mul_line = { + // let mut line = line; - line - }; + // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + // if parallel_is_y_major { + // line.start.x *= 256; + // line.end.x *= 256; + // } else { + // line.start.y *= 256; + // line.end.y *= 256; + // } + + // line + // }; let mul_delta = mul_line.delta(); @@ -118,7 +154,7 @@ fn thickline( // MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), MajorMinor::new( parallel_step.y_axis(), - Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), + Point::new(mul_delta.x / mul_delta.y, 0), ), ) } else { @@ -127,7 +163,7 @@ fn thickline( // MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), MajorMinor::new( parallel_step.x_axis(), - Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), + Point::new(0, mul_delta.y / mul_delta.x), ), ) }; @@ -153,18 +189,6 @@ fn thickline( let parallel_e_minor = -2 * parallel_dx; let parallel_e_major = 2 * parallel_dy; - // dbg!( - // dx, - // dy, - // parallel_dx, - // parallel_dy, - // seed_is_y_major, - // parallel_is_y_major, - // parallel_e_minor, - // parallel_e_major, - // parallel_threshold - // ); - let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; @@ -179,22 +203,16 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * thickness_dx; - // println!( - // "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {} y major {}", - // threshold, thickness_threshold, e_minor, e_major, dx, dy, y_major - // ); - - // println!("\n=== start loop ===\n"); - - // dbg!(seed_line_step, seed_line_delta, e_major, e_minor); + // // This fixes the phasing for parallel lines on the left side of the base line for the octants + // // where the line perpendicular moves "away" from the line body. + // let flip = if seed_line_step.minor == -parallel_step.major { + // -1 + // } else { + // 1 + // }; + let flip = 1; - // This fixes the phasing for parallel lines on the left side of the base line for the octants - // where the line perpendicular moves "away" from the line body. - let flip = if seed_line_step.minor == -parallel_step.major { - -1 - } else { - 1 - }; + println!("---"); while thickness_accumulator.pow(2) <= thickness_threshold { // println!("--- Seed iter"); @@ -204,8 +222,16 @@ fn thickline( let c = Rgb888::CSS_FOREST_GREEN; let p = Point::new( - if seed_is_y_major { point.x } else { point.x }, - if seed_is_y_major { point.y } else { point.y }, + if seed_is_y_major { + point.x >> 8 + } else { + point.x + }, + if seed_is_y_major { + point.y + } else { + point.y >> 8 + }, ); Pixel(p, c).draw(display)?; @@ -215,7 +241,8 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - parallel_error_left * flip, + // parallel_error_left * flip, + 0, Rgb888::CSS_AQUAMARINE, false, false, @@ -223,37 +250,41 @@ fn thickline( display, )?; - // Move seed line in minor direction + // Move seed line in minor direction (or step along parallel line in major direction) if seed_line_error > threshold { + println!("Pop"); point += seed_line_step.minor; seed_line_error += e_minor; thickness_accumulator += 2 * thickness_dy; // println!("---- Seed minor step"); - // Attempted fixes: - // - Threshold can be negative. .abs() doesn't fix the start error value - if parallel_error_left > parallel_threshold { - // println!("---- Parallel minor step"); - - // Add some spacing for debugging - point += seed_line_step.major * 2; - - parallel_line_2( - point, - non_mul_line, - parallel_step, - parallel_delta, - (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, - Rgb888::CSS_SALMON, - false, - false, - 0, - display, - )?; - - parallel_error_left += parallel_e_minor; - } + // if parallel_error_left > parallel_threshold { + // // println!("---- Parallel minor step"); + + // // Add some spacing for debugging + // point += seed_line_step.major * 2; + + // // parallel_line_2( + // // point, + // // non_mul_line, + // // parallel_step, + // // parallel_delta, + // // (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, + // // Rgb888::CSS_SALMON, + // // false, + // // false, + // // 0, + // // display, + // // )?; + + // // Add some spacing for debugging + // point += seed_line_step.major; + + // println!("Step minor"); + + // parallel_error_left += parallel_e_minor; + // } parallel_error_left += parallel_e_major; } @@ -264,19 +295,19 @@ fn thickline( thickness_accumulator += 2 * thickness_dx; } - // Final AA line - parallel_line_aa( - point, - non_mul_line, - parallel_step, - parallel_delta, - parallel_error_left * flip, - Rgb888::CSS_GOLDENROD, - false, - false, - 0, - display, - )?; + // // Final AA line + // parallel_line_aa( + // point, + // non_mul_line, + // parallel_step, + // parallel_delta, + // parallel_error_left * flip, + // Rgb888::CSS_GOLDENROD, + // false, + // false, + // 0, + // display, + // )?; Ok(()) } @@ -441,18 +472,20 @@ fn parallel_line_2( ) -> Result<(), std::convert::Infallible> { let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - let mut point = { - let mut point = start; + let mut point = start; - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if line_is_y_major { - point.x *= 256; - } else { - point.y *= 256; - } + // let mut point = { + // let mut point = start; - point - }; + // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + // if line_is_y_major { + // point.x *= 256; + // } else { + // point.y *= 256; + // } + + // point + // }; // // Using a block to isolate mutability // let line = { @@ -503,44 +536,33 @@ fn parallel_line_2( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; - let mut length = dx + 1; + let length = dx + 1; let mut error = start_error; - if skip_first { - // Some of the length was consumed by this initial skip iteration. If this is omitted, the - // line will be drawn 1px too long. - last_offset -= 1; + // if skip_first { + // // Some of the length was consumed by this initial skip iteration. If this is omitted, the + // // line will be drawn 1px too long. + // last_offset -= 1; - if error > threshold { - point += step.minor; - error += e_minor; - } + // if error > threshold { + // point += step.minor; + // error += e_minor; + // } - error += e_major; - point += step.major; - } + // error += e_major; + // point += step.major; + // } for _i in 0..(length + last_offset) { let aa_colour = c; let aa_p = Point::new( - if line_is_y_major { - point.x >> 8 - } else { - point.x - }, - if line_is_y_major { - point.y - } else { - point.y >> 8 - }, + if line_is_y_major { point.x } else { point.x }, + if line_is_y_major { point.y } else { point.y }, ); Pixel(aa_p, aa_colour).draw(display)?; - // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not - // quite sure why but we don't get a smooth increase over the length of the line. - if error > threshold { point += step.minor; error += e_minor; From 6ef4eb88b67c04048c24c1a6250bb683c7dc865a Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 11:44:56 +0100 Subject: [PATCH 141/188] Revert "Mul 256 seed line (doesn't work very well)" This reverts commit ff5cd422cd2b3ca11e3dbb0de68afd095d658582. --- debug-tools/examples/thick-line-mul-256.rs | 288 ++++++++++----------- 1 file changed, 133 insertions(+), 155 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 952e990..fe4c111 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -25,48 +25,13 @@ fn thickline( return Ok(()); } - let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - let non_mul_line = line; let non_mul_perpendicular_delta = line.perpendicular().delta(); - let seed_is_y_major = - non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); - let seed_line = line.perpendicular(); + let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - dbg!(seed_is_y_major); - - // Using a block to isolate mutability - let seed_line = { - let mut seed_line = seed_line; - - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if seed_is_y_major { - seed_line.start.x *= 256; - seed_line.end.x *= 256; - } else { - seed_line.start.y *= 256; - seed_line.end.y *= 256; - } - - seed_line - }; - - // Using a block to isolate mutability - let line = { - let mut line = line; - - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if line_is_y_major { - line.start.x *= 256; - line.end.x *= 256; - } else { - line.start.y *= 256; - line.end.y *= 256; - } - - line - }; + let seed_is_y_major = + non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed // line directly, but is used to scale initial error for each parallel line. Using a block to @@ -96,11 +61,12 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), - MajorMinor::new( - seed_line_step.y_axis(), - Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) - .component_mul(seed_line_step), - ), + MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), + // MajorMinor::new( + // seed_line_step.y_axis(), + // Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) + // .component_mul(seed_line_step), + // ), ) } // X-major line (i.e. X delta is longer than Y) @@ -108,36 +74,34 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), - MajorMinor::new( - seed_line_step.x_axis(), - Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) - .component_mul(seed_line_step), - ), + MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), + // MajorMinor::new( + // seed_line_step.x_axis(), + // Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) + // .component_mul(seed_line_step), + // ), ) }; // --- - // let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); - let parallel_is_y_major = line_is_y_major; - - let mul_line = line; + let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); - // // Using a block to isolate mutability - // let mul_line = { - // let mut line = line; + // Using a block to isolate mutability + let mul_line = { + let mut line = line; - // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - // if parallel_is_y_major { - // line.start.x *= 256; - // line.end.x *= 256; - // } else { - // line.start.y *= 256; - // line.end.y *= 256; - // } + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if parallel_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } - // line - // }; + line + }; let mul_delta = mul_line.delta(); @@ -154,7 +118,7 @@ fn thickline( // MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), MajorMinor::new( parallel_step.y_axis(), - Point::new(mul_delta.x / mul_delta.y, 0), + Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), ), ) } else { @@ -163,7 +127,7 @@ fn thickline( // MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), MajorMinor::new( parallel_step.x_axis(), - Point::new(0, mul_delta.y / mul_delta.x), + Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), ), ) }; @@ -189,6 +153,18 @@ fn thickline( let parallel_e_minor = -2 * parallel_dx; let parallel_e_major = 2 * parallel_dy; + // dbg!( + // dx, + // dy, + // parallel_dx, + // parallel_dy, + // seed_is_y_major, + // parallel_is_y_major, + // parallel_e_minor, + // parallel_e_major, + // parallel_threshold + // ); + let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; @@ -203,16 +179,22 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * thickness_dx; - // // This fixes the phasing for parallel lines on the left side of the base line for the octants - // // where the line perpendicular moves "away" from the line body. - // let flip = if seed_line_step.minor == -parallel_step.major { - // -1 - // } else { - // 1 - // }; - let flip = 1; + // println!( + // "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {} y major {}", + // threshold, thickness_threshold, e_minor, e_major, dx, dy, y_major + // ); + + // println!("\n=== start loop ===\n"); + + // dbg!(seed_line_step, seed_line_delta, e_major, e_minor); - println!("---"); + // This fixes the phasing for parallel lines on the left side of the base line for the octants + // where the line perpendicular moves "away" from the line body. + let flip = if seed_line_step.minor == -parallel_step.major { + -1 + } else { + 1 + }; while thickness_accumulator.pow(2) <= thickness_threshold { // println!("--- Seed iter"); @@ -222,16 +204,8 @@ fn thickline( let c = Rgb888::CSS_FOREST_GREEN; let p = Point::new( - if seed_is_y_major { - point.x >> 8 - } else { - point.x - }, - if seed_is_y_major { - point.y - } else { - point.y >> 8 - }, + if seed_is_y_major { point.x } else { point.x }, + if seed_is_y_major { point.y } else { point.y }, ); Pixel(p, c).draw(display)?; @@ -241,8 +215,7 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - // parallel_error_left * flip, - 0, + parallel_error_left * flip, Rgb888::CSS_AQUAMARINE, false, false, @@ -250,41 +223,37 @@ fn thickline( display, )?; - // Move seed line in minor direction (or step along parallel line in major direction) + // Move seed line in minor direction if seed_line_error > threshold { - println!("Pop"); point += seed_line_step.minor; seed_line_error += e_minor; thickness_accumulator += 2 * thickness_dy; // println!("---- Seed minor step"); - // if parallel_error_left > parallel_threshold { - // // println!("---- Parallel minor step"); - - // // Add some spacing for debugging - // point += seed_line_step.major * 2; - - // // parallel_line_2( - // // point, - // // non_mul_line, - // // parallel_step, - // // parallel_delta, - // // (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, - // // Rgb888::CSS_SALMON, - // // false, - // // false, - // // 0, - // // display, - // // )?; - - // // Add some spacing for debugging - // point += seed_line_step.major; - - // println!("Step minor"); - - // parallel_error_left += parallel_e_minor; - // } + // Attempted fixes: + // - Threshold can be negative. .abs() doesn't fix the start error value + if parallel_error_left > parallel_threshold { + // println!("---- Parallel minor step"); + + // Add some spacing for debugging + point += seed_line_step.major * 2; + + parallel_line_2( + point, + non_mul_line, + parallel_step, + parallel_delta, + (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, + Rgb888::CSS_SALMON, + false, + false, + 0, + display, + )?; + + parallel_error_left += parallel_e_minor; + } parallel_error_left += parallel_e_major; } @@ -295,19 +264,19 @@ fn thickline( thickness_accumulator += 2 * thickness_dx; } - // // Final AA line - // parallel_line_aa( - // point, - // non_mul_line, - // parallel_step, - // parallel_delta, - // parallel_error_left * flip, - // Rgb888::CSS_GOLDENROD, - // false, - // false, - // 0, - // display, - // )?; + // Final AA line + parallel_line_aa( + point, + non_mul_line, + parallel_step, + parallel_delta, + parallel_error_left * flip, + Rgb888::CSS_GOLDENROD, + false, + false, + 0, + display, + )?; Ok(()) } @@ -472,20 +441,18 @@ fn parallel_line_2( ) -> Result<(), std::convert::Infallible> { let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - let mut point = start; - - // let mut point = { - // let mut point = start; + let mut point = { + let mut point = start; - // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - // if line_is_y_major { - // point.x *= 256; - // } else { - // point.y *= 256; - // } + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if line_is_y_major { + point.x *= 256; + } else { + point.y *= 256; + } - // point - // }; + point + }; // // Using a block to isolate mutability // let line = { @@ -536,33 +503,44 @@ fn parallel_line_2( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; - let length = dx + 1; + let mut length = dx + 1; let mut error = start_error; - // if skip_first { - // // Some of the length was consumed by this initial skip iteration. If this is omitted, the - // // line will be drawn 1px too long. - // last_offset -= 1; + if skip_first { + // Some of the length was consumed by this initial skip iteration. If this is omitted, the + // line will be drawn 1px too long. + last_offset -= 1; - // if error > threshold { - // point += step.minor; - // error += e_minor; - // } + if error > threshold { + point += step.minor; + error += e_minor; + } - // error += e_major; - // point += step.major; - // } + error += e_major; + point += step.major; + } for _i in 0..(length + last_offset) { let aa_colour = c; let aa_p = Point::new( - if line_is_y_major { point.x } else { point.x }, - if line_is_y_major { point.y } else { point.y }, + if line_is_y_major { + point.x >> 8 + } else { + point.x + }, + if line_is_y_major { + point.y + } else { + point.y >> 8 + }, ); Pixel(aa_p, aa_colour).draw(display)?; + // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not + // quite sure why but we don't get a smooth increase over the length of the line. + if error > threshold { point += step.minor; error += e_minor; From 61ce78a922f3eaf81464ef0e06ba393b3e08930f Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 15:19:54 +0100 Subject: [PATCH 142/188] Something kind of working with separate parallel start point! --- debug-tools/examples/thick-line-mul-256.rs | 63 +++++++++++++--------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index fe4c111..e1564f2 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -132,9 +132,12 @@ fn thickline( ) }; + dbg!(parallel_step); + // --- let mut point = seed_line.start; + let mut parallel_point = non_mul_line.start; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -146,8 +149,8 @@ fn thickline( // Start error must be scaled the same as the major/minor errors used in `parallel_line()` to // set the starting error correctly. - let parallel_dx = parallel_delta.major.abs(); - let parallel_dy = parallel_delta.minor.abs(); + let parallel_dx = parallel_delta.major; + let parallel_dy = parallel_delta.minor; let parallel_threshold = parallel_dx - 2 * parallel_dy; let parallel_e_minor = -2 * parallel_dx; @@ -190,11 +193,12 @@ fn thickline( // This fixes the phasing for parallel lines on the left side of the base line for the octants // where the line perpendicular moves "away" from the line body. - let flip = if seed_line_step.minor == -parallel_step.major { - -1 - } else { - 1 - }; + // let flip = if seed_line_step.minor == -parallel_step.major { + // -1 + // } else { + // 1 + // }; + let flip = 1; while thickness_accumulator.pow(2) <= thickness_threshold { // println!("--- Seed iter"); @@ -211,7 +215,7 @@ fn thickline( Pixel(p, c).draw(display)?; parallel_line_2( - point, + parallel_point, non_mul_line, parallel_step, parallel_delta, @@ -236,28 +240,33 @@ fn thickline( if parallel_error_left > parallel_threshold { // println!("---- Parallel minor step"); - // Add some spacing for debugging - point += seed_line_step.major * 2; - - parallel_line_2( - point, - non_mul_line, - parallel_step, - parallel_delta, - (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, - Rgb888::CSS_SALMON, - false, - false, - 0, - display, - )?; + // // Add some spacing for debugging + // point += seed_line_step.major * 2; + + // parallel_line_2( + // point, + // non_mul_line, + // parallel_step, + // parallel_delta, + // (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, + // Rgb888::CSS_SALMON, + // false, + // false, + // 0, + // display, + // )?; parallel_error_left += parallel_e_minor; + parallel_point += seed_line_step.major; } + parallel_point += seed_line_step.minor; + parallel_error_left += parallel_e_major; } + parallel_point += seed_line_step.major; + // Multiply by 2 to separate individual lines for dbugging reasons point += seed_line_step.major * 2; seed_line_error += e_major; @@ -497,8 +506,8 @@ fn parallel_line_2( // ) // }; - let dx = delta.major.abs(); - let dy = delta.minor.abs(); + let dx = delta.major; + let dy = delta.minor; let threshold = dx - 2 * dy; let e_minor = -2 * dx; @@ -536,6 +545,10 @@ fn parallel_line_2( }, ); + if _i == 0 { + dbg!(line_is_y_major, aa_p, point); + } + Pixel(aa_p, aa_colour).draw(display)?; // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not From 97768c3f12711a32560c737e08f2febb1795c169 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 20:51:54 +0100 Subject: [PATCH 143/188] Revert "Something kind of working with separate parallel start point!" This reverts commit 61ce78a922f3eaf81464ef0e06ba393b3e08930f. --- debug-tools/examples/thick-line-mul-256.rs | 63 +++++++++------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index e1564f2..fe4c111 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -132,12 +132,9 @@ fn thickline( ) }; - dbg!(parallel_step); - // --- let mut point = seed_line.start; - let mut parallel_point = non_mul_line.start; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -149,8 +146,8 @@ fn thickline( // Start error must be scaled the same as the major/minor errors used in `parallel_line()` to // set the starting error correctly. - let parallel_dx = parallel_delta.major; - let parallel_dy = parallel_delta.minor; + let parallel_dx = parallel_delta.major.abs(); + let parallel_dy = parallel_delta.minor.abs(); let parallel_threshold = parallel_dx - 2 * parallel_dy; let parallel_e_minor = -2 * parallel_dx; @@ -193,12 +190,11 @@ fn thickline( // This fixes the phasing for parallel lines on the left side of the base line for the octants // where the line perpendicular moves "away" from the line body. - // let flip = if seed_line_step.minor == -parallel_step.major { - // -1 - // } else { - // 1 - // }; - let flip = 1; + let flip = if seed_line_step.minor == -parallel_step.major { + -1 + } else { + 1 + }; while thickness_accumulator.pow(2) <= thickness_threshold { // println!("--- Seed iter"); @@ -215,7 +211,7 @@ fn thickline( Pixel(p, c).draw(display)?; parallel_line_2( - parallel_point, + point, non_mul_line, parallel_step, parallel_delta, @@ -240,33 +236,28 @@ fn thickline( if parallel_error_left > parallel_threshold { // println!("---- Parallel minor step"); - // // Add some spacing for debugging - // point += seed_line_step.major * 2; - - // parallel_line_2( - // point, - // non_mul_line, - // parallel_step, - // parallel_delta, - // (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, - // Rgb888::CSS_SALMON, - // false, - // false, - // 0, - // display, - // )?; + // Add some spacing for debugging + point += seed_line_step.major * 2; + + parallel_line_2( + point, + non_mul_line, + parallel_step, + parallel_delta, + (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, + Rgb888::CSS_SALMON, + false, + false, + 0, + display, + )?; parallel_error_left += parallel_e_minor; - parallel_point += seed_line_step.major; } - parallel_point += seed_line_step.minor; - parallel_error_left += parallel_e_major; } - parallel_point += seed_line_step.major; - // Multiply by 2 to separate individual lines for dbugging reasons point += seed_line_step.major * 2; seed_line_error += e_major; @@ -506,8 +497,8 @@ fn parallel_line_2( // ) // }; - let dx = delta.major; - let dy = delta.minor; + let dx = delta.major.abs(); + let dy = delta.minor.abs(); let threshold = dx - 2 * dy; let e_minor = -2 * dx; @@ -545,10 +536,6 @@ fn parallel_line_2( }, ); - if _i == 0 { - dbg!(line_is_y_major, aa_p, point); - } - Pixel(aa_p, aa_colour).draw(display)?; // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not From f0cfd2e15cdd9830196015437461655c3ede6968 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 21:01:32 +0100 Subject: [PATCH 144/188] Add `extra` param --- debug-tools/examples/thick-line-mul-256.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index fe4c111..485a8be 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -20,6 +20,7 @@ fn thickline( display: &mut impl DrawTarget, line: Line, width: i32, + extra: bool, ) -> Result<(), std::convert::Infallible> { if width == 0 { return Ok(()); @@ -639,6 +640,7 @@ struct LineDebug { start: Point, end: Point, stroke_width: u32, + extra: bool, } impl App for LineDebug { @@ -656,6 +658,7 @@ impl App for LineDebug { end, // end: start + Point::new(100, 0), stroke_width: 10, + extra: true, } } @@ -664,6 +667,7 @@ impl App for LineDebug { Parameter::new("start", &mut self.start), Parameter::new("end", &mut self.end), Parameter::new("stroke", &mut self.stroke_width), + Parameter::new("extra", &mut self.extra), ] } @@ -679,7 +683,7 @@ impl App for LineDebug { let _mock_display: MockDisplay = MockDisplay::new(); - thickline(display, Line::new(self.start, self.end), width)?; + thickline(display, Line::new(self.start, self.end), width, self.extra)?; // let l = Line::new(self.start, self.end); From e6dd4f10363cb943d30ff3de6e0b6069097572b5 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 21:20:30 +0100 Subject: [PATCH 145/188] Somewhat slightly working start error offset --- debug-tools/examples/thick-line-mul-256.rs | 59 ++++++++++++++-------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 485a8be..b3707ee 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -135,6 +135,12 @@ fn thickline( // --- + let slope = if parallel_is_y_major { + mul_delta.x / mul_delta.y + } else { + mul_delta.y / mul_delta.x + }; + let mut point = seed_line.start; let dx = seed_line_delta.major.abs(); @@ -150,7 +156,7 @@ fn thickline( let parallel_dx = parallel_delta.major.abs(); let parallel_dy = parallel_delta.minor.abs(); - let parallel_threshold = parallel_dx - 2 * parallel_dy; + let parallel_threshold = (parallel_dx - 2 * parallel_dy).abs(); let parallel_e_minor = -2 * parallel_dx; let parallel_e_major = 2 * parallel_dy; @@ -191,11 +197,16 @@ fn thickline( // This fixes the phasing for parallel lines on the left side of the base line for the octants // where the line perpendicular moves "away" from the line body. - let flip = if seed_line_step.minor == -parallel_step.major { - -1 - } else { - 1 - }; + // let flip = if seed_line_step.minor == -parallel_step.major { + // -1 + // } else { + // 1 + // }; + let flip = 1; + + println!("---"); + + dbg!(slope, parallel_e_major, parallel_e_minor); while thickness_accumulator.pow(2) <= thickness_threshold { // println!("--- Seed iter"); @@ -216,7 +227,7 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - parallel_error_left * flip, + if extra { parallel_error_left * flip } else { 0 }, Rgb888::CSS_AQUAMARINE, false, false, @@ -253,9 +264,13 @@ fn thickline( display, )?; + dbg!("Add minor"); + parallel_error_left += parallel_e_minor; } + dbg!("Add major"); + parallel_error_left += parallel_e_major; } @@ -265,19 +280,19 @@ fn thickline( thickness_accumulator += 2 * thickness_dx; } - // Final AA line - parallel_line_aa( - point, - non_mul_line, - parallel_step, - parallel_delta, - parallel_error_left * flip, - Rgb888::CSS_GOLDENROD, - false, - false, - 0, - display, - )?; + // // Final AA line + // parallel_line_aa( + // point, + // non_mul_line, + // parallel_step, + // parallel_delta, + // parallel_error_left * flip, + // Rgb888::CSS_GOLDENROD, + // false, + // false, + // 0, + // display, + // )?; Ok(()) } @@ -501,12 +516,14 @@ fn parallel_line_2( let dx = delta.major.abs(); let dy = delta.minor.abs(); - let threshold = dx - 2 * dy; + let threshold = (dx - 2 * dy).abs(); let e_minor = -2 * dx; let e_major = 2 * dy; let mut length = dx + 1; let mut error = start_error; + dbg!(threshold, start_error); + if skip_first { // Some of the length was consumed by this initial skip iteration. If this is omitted, the // line will be drawn 1px too long. From 295f1bd593a4dd8cd4e38d198c7e66e2d0bfdc2d Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 21:36:25 +0100 Subject: [PATCH 146/188] Correct phasing without needing extra lines, but only in some octants --- debug-tools/examples/thick-line-mul-256.rs | 61 +++++++++++++--------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index b3707ee..f6f5d57 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -104,6 +104,8 @@ fn thickline( line }; + let mut mul_point = mul_line.start; + let mul_delta = mul_line.delta(); let parallel_delta = mul_line.delta(); @@ -156,7 +158,7 @@ fn thickline( let parallel_dx = parallel_delta.major.abs(); let parallel_dy = parallel_delta.minor.abs(); - let parallel_threshold = (parallel_dx - 2 * parallel_dy).abs(); + let parallel_threshold = parallel_dx - 2 * parallel_dy; let parallel_e_minor = -2 * parallel_dx; let parallel_e_major = 2 * parallel_dy; @@ -223,11 +225,12 @@ fn thickline( Pixel(p, c).draw(display)?; parallel_line_2( - point, + mul_point, non_mul_line, parallel_step, parallel_delta, - if extra { parallel_error_left * flip } else { 0 }, + // if extra { parallel_error_left * flip } else { 0 }, + 0, Rgb888::CSS_AQUAMARINE, false, false, @@ -251,29 +254,34 @@ fn thickline( // Add some spacing for debugging point += seed_line_step.major * 2; - parallel_line_2( - point, - non_mul_line, - parallel_step, - parallel_delta, - (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, - Rgb888::CSS_SALMON, - false, - false, - 0, - display, - )?; + // parallel_line_2( + // point, + // non_mul_line, + // parallel_step, + // parallel_delta, + // (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, + // Rgb888::CSS_SALMON, + // false, + // false, + // 0, + // display, + // )?; dbg!("Add minor"); parallel_error_left += parallel_e_minor; + mul_point += parallel_step.minor; } dbg!("Add major"); parallel_error_left += parallel_e_major; + mul_point += parallel_step.major; } + mul_point += seed_line_step.major * 256; + mul_point += seed_line_step.major * 256; + // Multiply by 2 to separate individual lines for dbugging reasons point += seed_line_step.major * 2; seed_line_error += e_major; @@ -457,18 +465,19 @@ fn parallel_line_2( ) -> Result<(), std::convert::Infallible> { let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - let mut point = { - let mut point = start; + let mut point = start; + // let mut point = { + // let mut point = start; - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if line_is_y_major { - point.x *= 256; - } else { - point.y *= 256; - } + // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + // if line_is_y_major { + // point.x *= 256; + // } else { + // point.y *= 256; + // } - point - }; + // point + // }; // // Using a block to isolate mutability // let line = { @@ -516,7 +525,7 @@ fn parallel_line_2( let dx = delta.major.abs(); let dy = delta.minor.abs(); - let threshold = (dx - 2 * dy).abs(); + let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; let mut length = dx + 1; From f3b01aefa14d5e8cfc6d22ba47c0ecf03a98bcb2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 21:43:24 +0100 Subject: [PATCH 147/188] Draw lien with existing algo to double check --- debug-tools/examples/thick-line-mul-256.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index f6f5d57..22cdfdb 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -1,5 +1,9 @@ use embedded_graphics::{ - geometry::PointExt, mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, + geometry::PointExt, + mock_display::MockDisplay, + pixelcolor::Rgb888, + prelude::*, + primitives::{Line, PrimitiveStyle}, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; @@ -26,6 +30,17 @@ fn thickline( return Ok(()); } + // Draw line using existing algorithm to check against + { + let mut line = line; + + line.start.y += width * 2; + line.end.y += width * 2; + + line.into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) + .draw(display)?; + } + let non_mul_line = line; let non_mul_perpendicular_delta = line.perpendicular().delta(); let seed_line = line.perpendicular(); From ca9782073cba099ae1dbcf367b4ed5b763fc816a Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 18 Aug 2024 21:50:21 +0100 Subject: [PATCH 148/188] Wahey it friggin works! (with f32 colour blend) --- debug-tools/examples/thick-line-mul-256.rs | 110 +++++---------------- 1 file changed, 26 insertions(+), 84 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 22cdfdb..bc8c0c0 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -237,7 +237,7 @@ fn thickline( if seed_is_y_major { point.y } else { point.y }, ); - Pixel(p, c).draw(display)?; + // Pixel(p, c).draw(display)?; parallel_line_2( mul_point, @@ -294,8 +294,9 @@ fn thickline( mul_point += parallel_step.major; } + // Twice to add some debug space mul_point += seed_line_step.major * 256; - mul_point += seed_line_step.major * 256; + // mul_point += seed_line_step.major * 256; // Multiply by 2 to separate individual lines for dbugging reasons point += seed_line_step.major * 2; @@ -303,19 +304,21 @@ fn thickline( thickness_accumulator += 2 * thickness_dx; } - // // Final AA line - // parallel_line_aa( - // point, - // non_mul_line, - // parallel_step, - // parallel_delta, - // parallel_error_left * flip, - // Rgb888::CSS_GOLDENROD, - // false, - // false, - // 0, - // display, - // )?; + // Final AA line + parallel_line_aa( + mul_point, + non_mul_line, + parallel_step, + parallel_delta, + // parallel_error_left * flip, + 0, + // Rgb888::CSS_GOLDENROD, + Rgb888::CSS_AQUAMARINE, + false, + false, + 0, + display, + )?; Ok(()) } @@ -336,51 +339,6 @@ fn parallel_line_aa( let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - // Using a block to isolate mutability - let line = { - let mut line = line; - - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if line_is_y_major { - line.start.x *= 256; - line.end.x *= 256; - point.x *= 256; - } else { - line.start.y *= 256; - line.end.y *= 256; - point.y *= 256; - } - - line - }; - - let parallel_delta = line.delta(); - - let parallel_step = Point::new( - if parallel_delta.x >= 0 { 1 } else { -1 }, - if parallel_delta.y >= 0 { 1 } else { -1 }, - ); - - let (delta, step) = if line_is_y_major { - ( - MajorMinor::new(parallel_delta.y, parallel_delta.x), - MajorMinor::new( - parallel_step.y_axis(), - Point::new((parallel_delta.x / parallel_delta.y).abs(), 0) - .component_mul(parallel_step), - ), - ) - } else { - ( - MajorMinor::new(parallel_delta.x, parallel_delta.y), - MajorMinor::new( - parallel_step.x_axis(), - Point::new(0, (parallel_delta.y / parallel_delta.x).abs()) - .component_mul(parallel_step), - ), - ) - }; - let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -390,20 +348,6 @@ fn parallel_line_aa( let mut length = dx + 1; let mut error = start_error; - if skip_first { - // Some of the length was consumed by this initial skip iteration. If this is omitted, the - // line will be drawn 1px too long. - last_offset -= 1; - - if error > threshold { - point += step.minor; - error += e_minor; - } - - error += e_major; - point += step.major; - } - for _i in 0..(length + last_offset) { // // https://computergraphics.stackexchange.com/a/10675 // let draw_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum()); @@ -422,12 +366,12 @@ fn parallel_line_aa( Rgb888::new( // TODO: Proper colour blend - // (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - // (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - // (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - 255 - mul, - 255 - mul, - 255 - mul, + (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + // 255 - mul, + // 255 - mul, + // 255 - mul, ) // c @@ -435,14 +379,14 @@ fn parallel_line_aa( let aa_p = Point::new( if line_is_y_major { - (point.x >> 8) - (line.delta().x).signum() * 2 + point.x >> 8 } else { point.x }, if line_is_y_major { point.y } else { - (point.y >> 8) - (line.delta().y).signum() * 2 + point.y >> 8 }, ); @@ -546,8 +490,6 @@ fn parallel_line_2( let mut length = dx + 1; let mut error = start_error; - dbg!(threshold, start_error); - if skip_first { // Some of the length was consumed by this initial skip iteration. If this is omitted, the // line will be drawn 1px too long. From d06bf1638b97e399f4d54180c53c39384858a866 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 10:33:08 +0100 Subject: [PATCH 149/188] Enormous cleanup --- debug-tools/examples/thick-line-mul-256.rs | 335 +++------------------ 1 file changed, 38 insertions(+), 297 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index bc8c0c0..cbdc246 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -44,28 +44,10 @@ fn thickline( let non_mul_line = line; let non_mul_perpendicular_delta = line.perpendicular().delta(); let seed_line = line.perpendicular(); - let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; let seed_is_y_major = non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits. Not used for seed - // line directly, but is used to scale initial error for each parallel line. Using a block to - // contain mutability. - // let seed_line = { - // let mut seed_line = seed_line; - - // if seed_is_y_major { - // seed_line.start.x *= 256; - // seed_line.end.x *= 256; - // } else { - // seed_line.start.y *= 256; - // seed_line.end.y *= 256; - // } - - // seed_line - // }; - let seed_line_delta = seed_line.delta(); let seed_line_step = Point::new( @@ -78,11 +60,6 @@ fn thickline( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), - // MajorMinor::new( - // seed_line_step.y_axis(), - // Point::new((seed_line_delta.x / seed_line_delta.y).abs(), 0) - // .component_mul(seed_line_step), - // ), ) } // X-major line (i.e. X delta is longer than Y) @@ -91,11 +68,6 @@ fn thickline( MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), - // MajorMinor::new( - // seed_line_step.x_axis(), - // Point::new(0, (seed_line_delta.y / seed_line_delta.x).abs()) - // .component_mul(seed_line_step), - // ), ) }; @@ -133,7 +105,6 @@ fn thickline( let (parallel_delta, parallel_step) = if parallel_is_y_major { ( MajorMinor::new(parallel_delta.y, parallel_delta.x), - // MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), MajorMinor::new( parallel_step.y_axis(), Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), @@ -142,7 +113,6 @@ fn thickline( } else { ( MajorMinor::new(parallel_delta.x, parallel_delta.y), - // MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), MajorMinor::new( parallel_step.x_axis(), Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), @@ -152,12 +122,6 @@ fn thickline( // --- - let slope = if parallel_is_y_major { - mul_delta.x / mul_delta.y - } else { - mul_delta.y / mul_delta.x - }; - let mut point = seed_line.start; let dx = seed_line_delta.major.abs(); @@ -177,18 +141,6 @@ fn thickline( let parallel_e_minor = -2 * parallel_dx; let parallel_e_major = 2 * parallel_dy; - // dbg!( - // dx, - // dy, - // parallel_dx, - // parallel_dy, - // seed_is_y_major, - // parallel_is_y_major, - // parallel_e_minor, - // parallel_e_major, - // parallel_threshold - // ); - let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; @@ -203,39 +155,13 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * thickness_dx; - // println!( - // "thresh {} thick thresh {} e_minor {} e_major {} dx {} dy {} y major {}", - // threshold, thickness_threshold, e_minor, e_major, dx, dy, y_major - // ); - - // println!("\n=== start loop ===\n"); - - // dbg!(seed_line_step, seed_line_delta, e_major, e_minor); - - // This fixes the phasing for parallel lines on the left side of the base line for the octants - // where the line perpendicular moves "away" from the line body. - // let flip = if seed_line_step.minor == -parallel_step.major { - // -1 - // } else { - // 1 - // }; - let flip = 1; - - println!("---"); - - dbg!(slope, parallel_e_major, parallel_e_minor); - while thickness_accumulator.pow(2) <= thickness_threshold { - // println!("--- Seed iter"); - - // println!("error {} point {}", seed_line_error, point); + // let c = Rgb888::CSS_FOREST_GREEN; - let c = Rgb888::CSS_FOREST_GREEN; - - let p = Point::new( - if seed_is_y_major { point.x } else { point.x }, - if seed_is_y_major { point.y } else { point.y }, - ); + // let p = Point::new( + // if seed_is_y_major { point.x } else { point.x }, + // if seed_is_y_major { point.y } else { point.y }, + // ); // Pixel(p, c).draw(display)?; @@ -244,11 +170,8 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - // if extra { parallel_error_left * flip } else { 0 }, - 0, Rgb888::CSS_AQUAMARINE, false, - false, 0, display, )?; @@ -259,43 +182,21 @@ fn thickline( seed_line_error += e_minor; thickness_accumulator += 2 * thickness_dy; - // println!("---- Seed minor step"); - - // Attempted fixes: - // - Threshold can be negative. .abs() doesn't fix the start error value if parallel_error_left > parallel_threshold { - // println!("---- Parallel minor step"); - + point += seed_line_step.major; // Add some spacing for debugging - point += seed_line_step.major * 2; - - // parallel_line_2( - // point, - // non_mul_line, - // parallel_step, - // parallel_delta, - // (parallel_error_left + parallel_e_minor + parallel_e_major) * flip, - // Rgb888::CSS_SALMON, - // false, - // false, - // 0, - // display, - // )?; - - dbg!("Add minor"); + point += seed_line_step.major; parallel_error_left += parallel_e_minor; mul_point += parallel_step.minor; } - dbg!("Add major"); - parallel_error_left += parallel_e_major; mul_point += parallel_step.major; } - // Twice to add some debug space mul_point += seed_line_step.major * 256; + // Twice to add some debug space // mul_point += seed_line_step.major * 256; // Multiply by 2 to separate individual lines for dbugging reasons @@ -310,12 +211,9 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - // parallel_error_left * flip, - 0, // Rgb888::CSS_GOLDENROD, Rgb888::CSS_AQUAMARINE, false, - false, 0, display, )?; @@ -326,12 +224,10 @@ fn thickline( fn parallel_line_aa( start: Point, line: Line, - mut step: MajorMinor, + step: MajorMinor, delta: MajorMinor, - start_error: i32, c: Rgb888, skip_first: bool, - invert: bool, mut last_offset: i32, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { @@ -345,167 +241,27 @@ fn parallel_line_aa( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; + // TODO: Skip first/last offset let mut length = dx + 1; - let mut error = start_error; + let mut error = 0; for _i in 0..(length + last_offset) { - // // https://computergraphics.stackexchange.com/a/10675 - // let draw_p = Point::new(point.x, (point.y >> 8) - (line.delta().y).signum()); - - // Pixel(draw_p, c).draw(display)?; - - { - let aa_colour = { - let mul = (if line_is_y_major { - point.x & 255 - // point.x - } else { - point.y & 255 - // point.y - }) as u8; - - Rgb888::new( - // TODO: Proper colour blend - (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - // 255 - mul, - // 255 - mul, - // 255 - mul, - ) - - // c - }; - - let aa_p = Point::new( - if line_is_y_major { - point.x >> 8 - } else { - point.x - }, - if line_is_y_major { - point.y - } else { - point.y >> 8 - }, - ); - - // let aa_p = point; - - Pixel(aa_p, aa_colour).draw(display)?; - } - - // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not - // quite sure why but we don't get a smooth increase over the length of the line. - - if error > threshold { - point += step.minor; - error += e_minor; - } - - error += e_major; - point += step.major; - } - - Ok(()) -} - -fn parallel_line_2( - start: Point, - line: Line, - step: MajorMinor, - delta: MajorMinor, - start_error: i32, - c: Rgb888, - skip_first: bool, - invert: bool, - mut last_offset: i32, - display: &mut impl DrawTarget, -) -> Result<(), std::convert::Infallible> { - let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - - let mut point = start; - // let mut point = { - // let mut point = start; - - // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - // if line_is_y_major { - // point.x *= 256; - // } else { - // point.y *= 256; - // } - - // point - // }; - - // // Using a block to isolate mutability - // let line = { - // let mut line = line; - - // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - // if line_is_y_major { - // line.start.x *= 256; - // line.end.x *= 256; - // point.x *= 256; - // } else { - // line.start.y *= 256; - // line.end.y *= 256; - // point.y *= 256; - // } - - // line - // }; - - // let delta = line.delta(); - - // let step = Point::new( - // if delta.x >= 0 { 1 } else { -1 }, - // if delta.y >= 0 { 1 } else { -1 }, - // ); - - // let (delta, step) = if line_is_y_major { - // ( - // MajorMinor::new(delta.y, delta.x), - // MajorMinor::new( - // step.y_axis(), - // Point::new((delta.x / delta.y).abs(), 0).component_mul(step), - // ), - // ) - // } else { - // ( - // MajorMinor::new(delta.x, delta.y), - // MajorMinor::new( - // step.x_axis(), - // Point::new(0, (delta.y / delta.x).abs()).component_mul(step), - // ), - // ) - // }; - - let dx = delta.major.abs(); - let dy = delta.minor.abs(); - - let threshold = dx - 2 * dy; - let e_minor = -2 * dx; - let e_major = 2 * dy; - let mut length = dx + 1; - let mut error = start_error; - - if skip_first { - // Some of the length was consumed by this initial skip iteration. If this is omitted, the - // line will be drawn 1px too long. - last_offset -= 1; - - if error > threshold { - point += step.minor; - error += e_minor; - } - - error += e_major; - point += step.major; - } + let aa_colour = { + let mul = (if line_is_y_major { + point.x & 255 + // point.x + } else { + point.y & 255 + // point.y + }) as u8; - for _i in 0..(length + last_offset) { - let aa_colour = c; + Rgb888::new( + // TODO: Proper colour blend + (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + ) + }; let aa_p = Point::new( if line_is_y_major { @@ -520,10 +276,9 @@ fn parallel_line_2( }, ); - Pixel(aa_p, aa_colour).draw(display)?; + // let aa_p = point; - // Doesn't work: mathematical distance from ideal line using line_point_distance(). Not - // quite sure why but we don't get a smooth increase over the length of the line. + Pixel(aa_p, aa_colour).draw(display)?; if error > threshold { point += step.minor; @@ -537,12 +292,11 @@ fn parallel_line_2( Ok(()) } -fn parallel_line( +fn parallel_line_2( start: Point, line: Line, - mut step: MajorMinor, + step: MajorMinor, delta: MajorMinor, - start_error: i32, c: Rgb888, skip_first: bool, mut last_offset: i32, @@ -550,16 +304,7 @@ fn parallel_line( ) -> Result<(), std::convert::Infallible> { let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - // let mut point = start; - - // Scale minor axis by 256 - let mut point = if line_is_y_major { - Point::new(start.x * 256, start.y) - } else { - Point::new(start.x, start.y * 256) - }; - - step.minor *= 256; + let mut point = start; let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -567,10 +312,9 @@ fn parallel_line( let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; + // TODO: Might need skip_first/last offset let mut length = dx + 1; - let mut error = start_error; - - // dbg!(start_error, e_minor, e_major, threshold); + let mut error = 0; if skip_first { // Some of the length was consumed by this initial skip iteration. If this is omitted, the @@ -587,11 +331,11 @@ fn parallel_line( } for _i in 0..(length + last_offset) { - // https://computergraphics.stackexchange.com/a/10675 - let draw_p = Point::new( + let aa_colour = c; + + let aa_p = Point::new( if line_is_y_major { point.x >> 8 - // point.x } else { point.x }, @@ -599,17 +343,14 @@ fn parallel_line( point.y } else { point.y >> 8 - // point.y }, ); - // dbg!(draw_p, step); - - Pixel(draw_p, c).draw(display)?; + Pixel(aa_p, aa_colour).draw(display)?; if error > threshold { - error += e_minor; point += step.minor; + error += e_minor; } error += e_major; From 3d8cf6c6a4f64f782949081f7d4a9db898af564c Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 10:36:04 +0100 Subject: [PATCH 150/188] Get rid of `point` --- debug-tools/examples/thick-line-mul-256.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index cbdc246..471ee7b 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -122,8 +122,6 @@ fn thickline( // --- - let mut point = seed_line.start; - let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -156,15 +154,6 @@ fn thickline( let mut thickness_accumulator = 2 * thickness_dx; while thickness_accumulator.pow(2) <= thickness_threshold { - // let c = Rgb888::CSS_FOREST_GREEN; - - // let p = Point::new( - // if seed_is_y_major { point.x } else { point.x }, - // if seed_is_y_major { point.y } else { point.y }, - // ); - - // Pixel(p, c).draw(display)?; - parallel_line_2( mul_point, non_mul_line, @@ -178,15 +167,10 @@ fn thickline( // Move seed line in minor direction if seed_line_error > threshold { - point += seed_line_step.minor; seed_line_error += e_minor; thickness_accumulator += 2 * thickness_dy; if parallel_error_left > parallel_threshold { - point += seed_line_step.major; - // Add some spacing for debugging - point += seed_line_step.major; - parallel_error_left += parallel_e_minor; mul_point += parallel_step.minor; } @@ -198,9 +182,6 @@ fn thickline( mul_point += seed_line_step.major * 256; // Twice to add some debug space // mul_point += seed_line_step.major * 256; - - // Multiply by 2 to separate individual lines for dbugging reasons - point += seed_line_step.major * 2; seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; } From 118ad09f842194db29193751309b565855522487 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 10:54:59 +0100 Subject: [PATCH 151/188] Add proper LERP for AA pixel colour --- debug-tools/examples/thick-line-mul-256.rs | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 471ee7b..020e889 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -202,6 +202,19 @@ fn thickline( Ok(()) } +/// Integer-only LERP with 8 bits of precision. +/// +/// Thanks to for the inspiration. +fn integer_lerp(a: u8, b: u8, f: u8) -> u8 { + let a = u16::from(a); + let b = u16::from(b); + let f = u16::from(f); + + let res = (a * (u16::from(u8::MAX) - f) + b * f) >> 8; + + res as u8 +} + fn parallel_line_aa( start: Point, line: Line, @@ -226,21 +239,20 @@ fn parallel_line_aa( let mut length = dx + 1; let mut error = 0; + let background = Rgb888::BLACK; + for _i in 0..(length + last_offset) { let aa_colour = { let mul = (if line_is_y_major { point.x & 255 - // point.x } else { point.y & 255 - // point.y }) as u8; Rgb888::new( - // TODO: Proper colour blend - (c.r() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - (c.g() as f32 * (1.0 - mul as f32 / 255.0)) as u8, - (c.b() as f32 * (1.0 - mul as f32 / 255.0)) as u8, + integer_lerp(c.r(), background.r(), mul), + integer_lerp(c.g(), background.g(), mul), + integer_lerp(c.b(), background.b(), mul), ) }; From 8e31f15191122ffa112b2ac8215ce5773910e5ca Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 11:03:03 +0100 Subject: [PATCH 152/188] Break phasing, but make all octants work --- debug-tools/examples/thick-line-mul-256.rs | 62 ++++++++++++---------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 020e889..7ca1cf2 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -1,9 +1,5 @@ use embedded_graphics::{ - geometry::PointExt, - mock_display::MockDisplay, - pixelcolor::Rgb888, - prelude::*, - primitives::{Line, PrimitiveStyle}, + geometry::PointExt, mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, }; use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; use framework::prelude::*; @@ -31,15 +27,15 @@ fn thickline( } // Draw line using existing algorithm to check against - { - let mut line = line; + // { + // let mut line = line; - line.start.y += width * 2; - line.end.y += width * 2; + // line.start.y += width * 2; + // line.end.y += width * 2; - line.into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) - .draw(display)?; - } + // line.into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) + // .draw(display)?; + // } let non_mul_line = line; let non_mul_perpendicular_delta = line.perpendicular().delta(); @@ -55,7 +51,7 @@ fn thickline( if seed_line_delta.y >= 0 { 1 } else { -1 }, ); - let (thickness_majorminor, seed_line_delta, seed_line_step) = if seed_is_y_major { + let (thickness_majorminor, seed_line_delta, mut seed_line_step) = if seed_is_y_major { ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), @@ -153,6 +149,16 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * thickness_dx; + // This fixes the phasing for parallel lines on the left side of the base line for the octants + // where the line perpendicular moves "away" from the line body. + let flip = if seed_line_step.minor == -parallel_step.major { + -1 + } else { + 1 + }; + + dbg!(seed_line_step, parallel_step, flip); + while thickness_accumulator.pow(2) <= thickness_threshold { parallel_line_2( mul_point, @@ -172,32 +178,32 @@ fn thickline( if parallel_error_left > parallel_threshold { parallel_error_left += parallel_e_minor; - mul_point += parallel_step.minor; + // mul_point += parallel_step.minor; } parallel_error_left += parallel_e_major; mul_point += parallel_step.major; } - mul_point += seed_line_step.major * 256; + mul_point += seed_line_step.major * 256 * flip; // Twice to add some debug space - // mul_point += seed_line_step.major * 256; + // mul_point += seed_line_step.major * 256 * flip; seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; } - // Final AA line - parallel_line_aa( - mul_point, - non_mul_line, - parallel_step, - parallel_delta, - // Rgb888::CSS_GOLDENROD, - Rgb888::CSS_AQUAMARINE, - false, - 0, - display, - )?; + // // Final AA line + // parallel_line_aa( + // mul_point, + // non_mul_line, + // parallel_step, + // parallel_delta, + // // Rgb888::CSS_GOLDENROD, + // Rgb888::CSS_AQUAMARINE, + // false, + // 0, + // display, + // )?; Ok(()) } From 612fc4c6d18b51404d36942fedbf89c4ea71c3f1 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 11:15:12 +0100 Subject: [PATCH 153/188] Correct in all octants but with missing extra lines --- debug-tools/examples/thick-line-mul-256.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 7ca1cf2..539b3d4 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -118,6 +118,12 @@ fn thickline( // --- + let slope = if parallel_is_y_major { + mul_delta.x / mul_delta.y + } else { + mul_delta.y / mul_delta.x + }; + let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -178,7 +184,8 @@ fn thickline( if parallel_error_left > parallel_threshold { parallel_error_left += parallel_e_minor; - // mul_point += parallel_step.minor; + // This gives correct phase offset + mul_point += seed_line_step.major * 256 * flip; } parallel_error_left += parallel_e_major; From f539c33dc09bbbeaf58b6b141d36cc6acf015a93 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 11:40:53 +0100 Subject: [PATCH 154/188] Revert "Correct in all octants but with missing extra lines" This reverts commit 612fc4c6d18b51404d36942fedbf89c4ea71c3f1. --- debug-tools/examples/thick-line-mul-256.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 539b3d4..7ca1cf2 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -118,12 +118,6 @@ fn thickline( // --- - let slope = if parallel_is_y_major { - mul_delta.x / mul_delta.y - } else { - mul_delta.y / mul_delta.x - }; - let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -184,8 +178,7 @@ fn thickline( if parallel_error_left > parallel_threshold { parallel_error_left += parallel_e_minor; - // This gives correct phase offset - mul_point += seed_line_step.major * 256 * flip; + // mul_point += parallel_step.minor; } parallel_error_left += parallel_e_major; From 3a50ce18efa315dedf4613c43fd1cec9cb418f40 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 11:56:04 +0100 Subject: [PATCH 155/188] Use slope to make all quadrants correct (but phase is still broken) --- debug-tools/examples/thick-line-mul-256.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 7ca1cf2..5c6974f 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -118,6 +118,12 @@ fn thickline( // --- + let slope = if parallel_is_y_major { + mul_delta.x / mul_delta.y + } else { + mul_delta.y / mul_delta.x + }; + let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -178,7 +184,7 @@ fn thickline( if parallel_error_left > parallel_threshold { parallel_error_left += parallel_e_minor; - // mul_point += parallel_step.minor; + mul_point += parallel_step.minor / slope; } parallel_error_left += parallel_e_major; @@ -347,7 +353,7 @@ fn parallel_line_2( Pixel(aa_p, aa_colour).draw(display)?; - if error > threshold { + if error >= threshold { point += step.minor; error += e_minor; } From 960afa03902eb64a4df993312321e21d2aa2f43a Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 15:11:23 +0100 Subject: [PATCH 156/188] Kinda start again --- debug-tools/examples/thick-line-mul-256.rs | 72 ++++++++++++++++------ 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 5c6974f..03ddba1 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -87,8 +87,6 @@ fn thickline( line }; - let mut mul_point = mul_line.start; - let mul_delta = mul_line.delta(); let parallel_delta = mul_line.delta(); @@ -98,13 +96,14 @@ fn thickline( if parallel_delta.y >= 0 { 1 } else { -1 }, ); - let (parallel_delta, parallel_step) = if parallel_is_y_major { + let (parallel_delta, parallel_step, parallel_step_full) = if parallel_is_y_major { ( MajorMinor::new(parallel_delta.y, parallel_delta.x), MajorMinor::new( parallel_step.y_axis(), Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), ), + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis() * 256), ) } else { ( @@ -113,6 +112,7 @@ fn thickline( parallel_step.x_axis(), Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), ), + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis() * 256), ) }; @@ -137,16 +137,17 @@ fn thickline( let parallel_dx = parallel_delta.major.abs(); let parallel_dy = parallel_delta.minor.abs(); - let parallel_threshold = parallel_dx - 2 * parallel_dy; + let parallel_threshold = 2 * parallel_dy - parallel_dx; let parallel_e_minor = -2 * parallel_dx; let parallel_e_major = 2 * parallel_dy; - let threshold = dx - 2 * dy; + // let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; let e_major = 2 * dy; let mut seed_line_error = 0; - let mut parallel_error_left = 0; + let mut parallel_error_left = 2 * parallel_dy - parallel_dx; + // let mut parallel_error_left = 0i32; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill let thickness_threshold = @@ -163,7 +164,27 @@ fn thickline( 1 }; - dbg!(seed_line_step, parallel_step, flip); + let mut mul_point = mul_line.start; + + // dbg!( + // seed_line_step, + // parallel_step, + // parallel_e_major, + // parallel_e_minor, + // flip + // ); + + dbg!( + seed_line_step, + parallel_step, + parallel_step_full, + parallel_e_major, + parallel_e_minor, + flip, + parallel_threshold + ); + + let mut offset = 0; while thickness_accumulator.pow(2) <= thickness_threshold { parallel_line_2( @@ -171,31 +192,41 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, + // if extra { parallel_error_left } else { 0 }, + // 2 * parallel_dy - parallel_dx, + // if extra { + // parallel_error_left + // } else { + // 2 * parallel_dy - parallel_dx + // }, + 2 * parallel_dy - parallel_dx, Rgb888::CSS_AQUAMARINE, false, 0, display, + extra, )?; + // Pixel(point, Rgb888::RED).draw(display)?; + // Move seed line in minor direction - if seed_line_error > threshold { + if seed_line_error > 0 { seed_line_error += e_minor; thickness_accumulator += 2 * thickness_dy; - if parallel_error_left > parallel_threshold { + if parallel_error_left > 0 { parallel_error_left += parallel_e_minor; - mul_point += parallel_step.minor / slope; } parallel_error_left += parallel_e_major; - mul_point += parallel_step.major; + + mul_point += parallel_step_full.major * flip; } - mul_point += seed_line_step.major * 256 * flip; - // Twice to add some debug space - // mul_point += seed_line_step.major * 256 * flip; seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; + + mul_point += parallel_step_full.minor * 2 * -flip; } // // Final AA line @@ -302,10 +333,12 @@ fn parallel_line_2( line: Line, step: MajorMinor, delta: MajorMinor, + start_error: i32, c: Rgb888, skip_first: bool, mut last_offset: i32, display: &mut impl DrawTarget, + extra: bool, ) -> Result<(), std::convert::Infallible> { let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; @@ -314,19 +347,22 @@ fn parallel_line_2( let dx = delta.major.abs(); let dy = delta.minor.abs(); - let threshold = dx - 2 * dy; + // let threshold = 2 * dy - dx; let e_minor = -2 * dx; let e_major = 2 * dy; // TODO: Might need skip_first/last offset let mut length = dx + 1; - let mut error = 0; + // let mut error = if extra { 2 * dy - dx } else { start_error }; + let mut error = start_error; + + dbg!(start_error, e_minor, e_major); if skip_first { // Some of the length was consumed by this initial skip iteration. If this is omitted, the // line will be drawn 1px too long. last_offset -= 1; - if error > threshold { + if error > 0 { point += step.minor; error += e_minor; } @@ -353,7 +389,7 @@ fn parallel_line_2( Pixel(aa_p, aa_colour).draw(display)?; - if error >= threshold { + if error > 0 { point += step.minor; error += e_minor; } From 7a1025425f4a637fac5dba8bb856e7bd417dc3c0 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 15:19:39 +0100 Subject: [PATCH 157/188] Back to fully missing lines --- debug-tools/examples/thick-line-mul-256.rs | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 03ddba1..e5b40b8 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -174,15 +174,15 @@ fn thickline( // flip // ); - dbg!( - seed_line_step, - parallel_step, - parallel_step_full, - parallel_e_major, - parallel_e_minor, - flip, - parallel_threshold - ); + // dbg!( + // seed_line_step, + // parallel_step, + // parallel_step_full, + // parallel_e_major, + // parallel_e_minor, + // flip, + // parallel_threshold + // ); let mut offset = 0; @@ -199,7 +199,7 @@ fn thickline( // } else { // 2 * parallel_dy - parallel_dx // }, - 2 * parallel_dy - parallel_dx, + parallel_error_left, Rgb888::CSS_AQUAMARINE, false, 0, @@ -221,12 +221,13 @@ fn thickline( parallel_error_left += parallel_e_major; mul_point += parallel_step_full.major * flip; + mul_point += parallel_step_full.minor * -flip; } seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - mul_point += parallel_step_full.minor * 2 * -flip; + mul_point += parallel_step_full.minor * -flip; } // // Final AA line @@ -355,7 +356,7 @@ fn parallel_line_2( // let mut error = if extra { 2 * dy - dx } else { start_error }; let mut error = start_error; - dbg!(start_error, e_minor, e_major); + // dbg!(start_error, e_minor, e_major); if skip_first { // Some of the length was consumed by this initial skip iteration. If this is omitted, the From bcb2adb3212460d3c84ff5fedcaf2d65d5385086 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 15:31:12 +0100 Subject: [PATCH 158/188] Sort of make extra line fill work Line start/end still skews weirdly when close to 45deg though --- debug-tools/examples/thick-line-mul-256.rs | 97 +++++++++++++++------- 1 file changed, 68 insertions(+), 29 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index e5b40b8..e2f6141 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -145,7 +145,7 @@ fn thickline( // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; let e_major = 2 * dy; - let mut seed_line_error = 0; + let mut seed_line_error = 2 * dy - dx; let mut parallel_error_left = 2 * parallel_dy - parallel_dx; // let mut parallel_error_left = 0i32; @@ -166,6 +166,8 @@ fn thickline( let mut mul_point = mul_line.start; + let flip = 1; + // dbg!( // seed_line_step, // parallel_step, @@ -199,12 +201,13 @@ fn thickline( // } else { // 2 * parallel_dy - parallel_dx // }, - parallel_error_left, + // parallel_error_left, + 2 * parallel_dy - parallel_dx, Rgb888::CSS_AQUAMARINE, false, 0, display, - extra, + false, )?; // Pixel(point, Rgb888::RED).draw(display)?; @@ -216,12 +219,29 @@ fn thickline( if parallel_error_left > 0 { parallel_error_left += parallel_e_minor; + + mul_point += parallel_step_full.major * flip; + mul_point += parallel_step_full.minor * -flip; + + // TODO: Check line limit here to see if we actually need to draw another line or + // not. + if extra { + parallel_line_2( + mul_point, + non_mul_line, + parallel_step, + parallel_delta, + 2 * parallel_dy - parallel_dx, + Rgb888::CSS_GOLDENROD, + false, + 0, + display, + true, + )?; + } } parallel_error_left += parallel_e_major; - - mul_point += parallel_step_full.major * flip; - mul_point += parallel_step_full.minor * -flip; } seed_line_error += e_major; @@ -230,18 +250,18 @@ fn thickline( mul_point += parallel_step_full.minor * -flip; } - // // Final AA line - // parallel_line_aa( - // mul_point, - // non_mul_line, - // parallel_step, - // parallel_delta, - // // Rgb888::CSS_GOLDENROD, - // Rgb888::CSS_AQUAMARINE, - // false, - // 0, - // display, - // )?; + // Final AA line + parallel_line_aa( + mul_point, + non_mul_line, + parallel_step, + parallel_delta, + // Rgb888::CSS_GOLDENROD, + Rgb888::CSS_AQUAMARINE, + false, + 0, + display, + )?; Ok(()) } @@ -358,19 +378,19 @@ fn parallel_line_2( // dbg!(start_error, e_minor, e_major); - if skip_first { - // Some of the length was consumed by this initial skip iteration. If this is omitted, the - // line will be drawn 1px too long. - last_offset -= 1; + // if skip_first { + // // Some of the length was consumed by this initial skip iteration. If this is omitted, the + // // line will be drawn 1px too long. + // last_offset -= 1; - if error > 0 { - point += step.minor; - error += e_minor; - } + // if error > 0 { + // point += step.minor; + // error += e_minor; + // } - error += e_major; - point += step.major; - } + // error += e_major; + // point += step.major; + // } for _i in 0..(length + last_offset) { let aa_colour = c; @@ -390,6 +410,25 @@ fn parallel_line_2( Pixel(aa_p, aa_colour).draw(display)?; + if extra { + let point = point + step.minor; + + let aa_p = Point::new( + if line_is_y_major { + point.x >> 8 + } else { + point.x + }, + if line_is_y_major { + point.y + } else { + point.y >> 8 + }, + ); + + Pixel(aa_p, aa_colour).draw(display)?; + } + if error > 0 { point += step.minor; error += e_minor; From 9ae97e741adfe1374374374e47ba4290fff14274 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 18:22:26 +0100 Subject: [PATCH 159/188] Check limit for extra lines too --- debug-tools/examples/thick-line-mul-256.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index e2f6141..f5a0a2f 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -215,7 +215,6 @@ fn thickline( // Move seed line in minor direction if seed_line_error > 0 { seed_line_error += e_minor; - thickness_accumulator += 2 * thickness_dy; if parallel_error_left > 0 { parallel_error_left += parallel_e_minor; @@ -223,9 +222,7 @@ fn thickline( mul_point += parallel_step_full.major * flip; mul_point += parallel_step_full.minor * -flip; - // TODO: Check line limit here to see if we actually need to draw another line or - // not. - if extra { + if thickness_accumulator.pow(2) <= thickness_threshold && extra { parallel_line_2( mul_point, non_mul_line, @@ -241,6 +238,7 @@ fn thickline( } } + thickness_accumulator += 2 * thickness_dy; parallel_error_left += parallel_e_major; } From 8b46627561eed4891f242901f91ae56b935f92f2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 18:56:22 +0100 Subject: [PATCH 160/188] Note --- debug-tools/examples/thick-line-mul-256.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index f5a0a2f..d08d1a5 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -220,6 +220,7 @@ fn thickline( parallel_error_left += parallel_e_minor; mul_point += parallel_step_full.major * flip; + // This makes things align properly, but it skews the seed line mul_point += parallel_step_full.minor * -flip; if thickness_accumulator.pow(2) <= thickness_threshold && extra { From c64b98df108dd742895ee5e5f977a07ff93397bc Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 19 Aug 2024 19:16:38 +0100 Subject: [PATCH 161/188] Working again, with extra line The AA looks weird at near 45deg, but when scaled to 1x, it looks fantastic --- debug-tools/examples/thick-line-mul-256.rs | 49 ++++++++++------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index d08d1a5..b8cea30 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -189,27 +189,6 @@ fn thickline( let mut offset = 0; while thickness_accumulator.pow(2) <= thickness_threshold { - parallel_line_2( - mul_point, - non_mul_line, - parallel_step, - parallel_delta, - // if extra { parallel_error_left } else { 0 }, - // 2 * parallel_dy - parallel_dx, - // if extra { - // parallel_error_left - // } else { - // 2 * parallel_dy - parallel_dx - // }, - // parallel_error_left, - 2 * parallel_dy - parallel_dx, - Rgb888::CSS_AQUAMARINE, - false, - 0, - display, - false, - )?; - // Pixel(point, Rgb888::RED).draw(display)?; // Move seed line in minor direction @@ -220,17 +199,18 @@ fn thickline( parallel_error_left += parallel_e_minor; mul_point += parallel_step_full.major * flip; - // This makes things align properly, but it skews the seed line - mul_point += parallel_step_full.minor * -flip; + // This makes things align properly, but it skews the seed line. This also makes + // full gaps which require an extra line to be drawn (below). + // mul_point += parallel_step_full.minor * -flip; - if thickness_accumulator.pow(2) <= thickness_threshold && extra { + if extra { parallel_line_2( mul_point, non_mul_line, parallel_step, parallel_delta, 2 * parallel_dy - parallel_dx, - Rgb888::CSS_GOLDENROD, + Rgb888::CSS_AQUAMARINE, false, 0, display, @@ -241,6 +221,21 @@ fn thickline( thickness_accumulator += 2 * thickness_dy; parallel_error_left += parallel_e_major; + } else { + parallel_line_2( + mul_point, + non_mul_line, + parallel_step, + parallel_delta, + // Required instead of zero otherwise the starting pixels of the line before the first + // minor step are too long + 2 * parallel_dy - parallel_dx, + Rgb888::CSS_AQUAMARINE, + false, + 0, + display, + false, + )?; } seed_line_error += e_major; @@ -255,6 +250,7 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, + 2 * parallel_dy - parallel_dx, // Rgb888::CSS_GOLDENROD, Rgb888::CSS_AQUAMARINE, false, @@ -283,6 +279,7 @@ fn parallel_line_aa( line: Line, step: MajorMinor, delta: MajorMinor, + start_error: i32, c: Rgb888, skip_first: bool, mut last_offset: i32, @@ -300,7 +297,7 @@ fn parallel_line_aa( let e_major = 2 * dy; // TODO: Skip first/last offset let mut length = dx + 1; - let mut error = 0; + let mut error = start_error; let background = Rgb888::BLACK; From 8187cc31b46c28367351611f8335fa1707aa1b57 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 20 Aug 2024 10:01:22 +0100 Subject: [PATCH 162/188] Clean a few things up, add some comments --- debug-tools/examples/thick-line-mul-256.rs | 79 ++++++---------------- 1 file changed, 21 insertions(+), 58 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index b8cea30..456249f 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -51,7 +51,7 @@ fn thickline( if seed_line_delta.y >= 0 { 1 } else { -1 }, ); - let (thickness_majorminor, seed_line_delta, mut seed_line_step) = if seed_is_y_major { + let (thickness_majorminor, seed_line_delta, seed_line_step) = if seed_is_y_major { ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), @@ -118,11 +118,11 @@ fn thickline( // --- - let slope = if parallel_is_y_major { - mul_delta.x / mul_delta.y - } else { - mul_delta.y / mul_delta.x - }; + // let slope = if parallel_is_y_major { + // mul_delta.x / mul_delta.y + // } else { + // mul_delta.y / mul_delta.x + // }; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -137,7 +137,7 @@ fn thickline( let parallel_dx = parallel_delta.major.abs(); let parallel_dy = parallel_delta.minor.abs(); - let parallel_threshold = 2 * parallel_dy - parallel_dx; + // let parallel_threshold = 2 * parallel_dy - parallel_dx; let parallel_e_minor = -2 * parallel_dx; let parallel_e_major = 2 * parallel_dy; @@ -166,28 +166,6 @@ fn thickline( let mut mul_point = mul_line.start; - let flip = 1; - - // dbg!( - // seed_line_step, - // parallel_step, - // parallel_e_major, - // parallel_e_minor, - // flip - // ); - - // dbg!( - // seed_line_step, - // parallel_step, - // parallel_step_full, - // parallel_e_major, - // parallel_e_minor, - // flip, - // parallel_threshold - // ); - - let mut offset = 0; - while thickness_accumulator.pow(2) <= thickness_threshold { // Pixel(point, Rgb888::RED).draw(display)?; @@ -198,10 +176,7 @@ fn thickline( if parallel_error_left > 0 { parallel_error_left += parallel_e_minor; - mul_point += parallel_step_full.major * flip; - // This makes things align properly, but it skews the seed line. This also makes - // full gaps which require an extra line to be drawn (below). - // mul_point += parallel_step_full.minor * -flip; + mul_point += parallel_step_full.major; if extra { parallel_line_2( @@ -241,7 +216,7 @@ fn thickline( seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - mul_point += parallel_step_full.minor * -flip; + mul_point += parallel_step_full.minor * -1; } // Final AA line @@ -329,8 +304,6 @@ fn parallel_line_aa( }, ); - // let aa_p = point; - Pixel(aa_p, aa_colour).draw(display)?; if error > threshold { @@ -369,11 +342,8 @@ fn parallel_line_2( let e_major = 2 * dy; // TODO: Might need skip_first/last offset let mut length = dx + 1; - // let mut error = if extra { 2 * dy - dx } else { start_error }; let mut error = start_error; - // dbg!(start_error, e_minor, e_major); - // if skip_first { // // Some of the length was consumed by this initial skip iteration. If this is omitted, the // // line will be drawn 1px too long. @@ -389,9 +359,7 @@ fn parallel_line_2( // } for _i in 0..(length + last_offset) { - let aa_colour = c; - - let aa_p = Point::new( + let p = Point::new( if line_is_y_major { point.x >> 8 } else { @@ -404,25 +372,20 @@ fn parallel_line_2( }, ); - Pixel(aa_p, aa_colour).draw(display)?; + Pixel(p, c).draw(display)?; + // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is + // required for the additional diagonal move lines that are drawn when stepping in both the + // major and minor directions in the seed line. if extra { - let point = point + step.minor; - - let aa_p = Point::new( - if line_is_y_major { - point.x >> 8 - } else { - point.x - }, - if line_is_y_major { - point.y - } else { - point.y >> 8 - }, + let p = point + step.minor; + + let p = Point::new( + if line_is_y_major { p.x >> 8 } else { p.x }, + if line_is_y_major { p.y } else { p.y >> 8 }, ); - Pixel(aa_p, aa_colour).draw(display)?; + Pixel(p, c).draw(display)?; } if error > 0 { @@ -430,8 +393,8 @@ fn parallel_line_2( error += e_minor; } - error += e_major; point += step.major; + error += e_major; } Ok(()) From 5145e5e0e71b355ce16c6a68ec1dc3d2e37db3fd Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 20 Aug 2024 10:39:32 +0100 Subject: [PATCH 163/188] Correct AA direction for all octants --- debug-tools/examples/thick-line-mul-256.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 456249f..b8652e7 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -156,13 +156,15 @@ fn thickline( // drawn as the lines are drawn before checking for thickness. let mut thickness_accumulator = 2 * thickness_dx; - // This fixes the phasing for parallel lines on the left side of the base line for the octants - // where the line perpendicular moves "away" from the line body. - let flip = if seed_line_step.minor == -parallel_step.major { - -1 - } else { - 1 - }; + // // This fixes the phasing for parallel lines on the left side of the base line for the octants + // // where the line perpendicular moves "away" from the line body. + // let flip = if seed_line_step.minor == -parallel_step.major { + // -1 + // } else { + // 1 + // }; + + let swap_aa_direction = parallel_step.minor.x < 0 || parallel_step.minor.y < 0; let mut mul_point = mul_line.start; @@ -230,6 +232,7 @@ fn thickline( Rgb888::CSS_AQUAMARINE, false, 0, + swap_aa_direction, display, )?; @@ -258,6 +261,7 @@ fn parallel_line_aa( c: Rgb888, skip_first: bool, mut last_offset: i32, + swap_aa_direction: bool, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { let mut point = start; @@ -284,6 +288,9 @@ fn parallel_line_aa( point.y & 255 }) as u8; + // Some octants need the AA direction to go the other way + let mul = if swap_aa_direction { 255 - mul } else { mul }; + Rgb888::new( integer_lerp(c.r(), background.r(), mul), integer_lerp(c.g(), background.g(), mul), From f10b669011189b005d0c6ba4b73412a0f8db0f09 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 20 Aug 2024 10:48:48 +0100 Subject: [PATCH 164/188] Huge cleanup --- debug-tools/examples/thick-line-mul-256.rs | 115 ++++++--------------- 1 file changed, 31 insertions(+), 84 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index b8652e7..a460c71 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -27,14 +27,17 @@ fn thickline( } // Draw line using existing algorithm to check against - // { + // if extra { // let mut line = line; - // line.start.y += width * 2; - // line.end.y += width * 2; + // // line.start.y += width * 2; + // // line.end.y += width * 2; - // line.into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, width as u32)) - // .draw(display)?; + // line.into_styled(embedded_graphics::primitives::PrimitiveStyle::with_stroke( + // Rgb888::WHITE, + // width as u32, + // )) + // .draw(display)?; // } let non_mul_line = line; @@ -46,16 +49,10 @@ fn thickline( let seed_line_delta = seed_line.delta(); - let seed_line_step = Point::new( - if seed_line_delta.x >= 0 { 1 } else { -1 }, - if seed_line_delta.y >= 0 { 1 } else { -1 }, - ); - - let (thickness_majorminor, seed_line_delta, seed_line_step) = if seed_is_y_major { + let (thickness_majorminor, seed_line_delta) = if seed_is_y_major { ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(seed_line_delta.y, seed_line_delta.x), - MajorMinor::new(seed_line_step.y_axis(), seed_line_step.x_axis()), ) } // X-major line (i.e. X delta is longer than Y) @@ -63,7 +60,6 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), MajorMinor::new(seed_line_delta.x, seed_line_delta.y), - MajorMinor::new(seed_line_step.x_axis(), seed_line_step.y_axis()), ) }; @@ -132,22 +128,11 @@ fn thickline( let thickness_dx = thickness_majorminor.major.abs(); let thickness_dy = thickness_majorminor.minor.abs(); - // Start error must be scaled the same as the major/minor errors used in `parallel_line()` to - // set the starting error correctly. - let parallel_dx = parallel_delta.major.abs(); - let parallel_dy = parallel_delta.minor.abs(); - - // let parallel_threshold = 2 * parallel_dy - parallel_dx; - let parallel_e_minor = -2 * parallel_dx; - let parallel_e_major = 2 * parallel_dy; - // let threshold = dx - 2 * dy; // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; let e_major = 2 * dy; let mut seed_line_error = 2 * dy - dx; - let mut parallel_error_left = 2 * parallel_dy - parallel_dx; - // let mut parallel_error_left = 0i32; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill let thickness_threshold = @@ -175,41 +160,26 @@ fn thickline( if seed_line_error > 0 { seed_line_error += e_minor; - if parallel_error_left > 0 { - parallel_error_left += parallel_e_minor; - - mul_point += parallel_step_full.major; - - if extra { - parallel_line_2( - mul_point, - non_mul_line, - parallel_step, - parallel_delta, - 2 * parallel_dy - parallel_dx, - Rgb888::CSS_AQUAMARINE, - false, - 0, - display, - true, - )?; - } - } + mul_point += parallel_step_full.major; + + parallel_line_2( + mul_point, + non_mul_line, + parallel_step, + parallel_delta, + Rgb888::CSS_AQUAMARINE, + display, + true, + )?; thickness_accumulator += 2 * thickness_dy; - parallel_error_left += parallel_e_major; } else { parallel_line_2( mul_point, non_mul_line, parallel_step, parallel_delta, - // Required instead of zero otherwise the starting pixels of the line before the first - // minor step are too long - 2 * parallel_dy - parallel_dx, Rgb888::CSS_AQUAMARINE, - false, - 0, display, false, )?; @@ -227,11 +197,8 @@ fn thickline( non_mul_line, parallel_step, parallel_delta, - 2 * parallel_dy - parallel_dx, // Rgb888::CSS_GOLDENROD, Rgb888::CSS_AQUAMARINE, - false, - 0, swap_aa_direction, display, )?; @@ -257,10 +224,7 @@ fn parallel_line_aa( line: Line, step: MajorMinor, delta: MajorMinor, - start_error: i32, c: Rgb888, - skip_first: bool, - mut last_offset: i32, swap_aa_direction: bool, display: &mut impl DrawTarget, ) -> Result<(), std::convert::Infallible> { @@ -271,16 +235,17 @@ fn parallel_line_aa( let dx = delta.major.abs(); let dy = delta.minor.abs(); - let threshold = dx - 2 * dy; let e_minor = -2 * dx; let e_major = 2 * dy; - // TODO: Skip first/last offset - let mut length = dx + 1; - let mut error = start_error; + let length = dx; + let mut error = 2 * dy - dx; + // Blend colour for AA edge let background = Rgb888::BLACK; - for _i in 0..(length + last_offset) { + // FIXME: If line is exactly diagonal, no AA is performed. It should have a 50% edge. + + for _i in 0..length { let aa_colour = { let mul = (if line_is_y_major { point.x & 255 @@ -313,7 +278,7 @@ fn parallel_line_aa( Pixel(aa_p, aa_colour).draw(display)?; - if error > threshold { + if error > 0 { point += step.minor; error += e_minor; } @@ -330,10 +295,7 @@ fn parallel_line_2( line: Line, step: MajorMinor, delta: MajorMinor, - start_error: i32, c: Rgb888, - skip_first: bool, - mut last_offset: i32, display: &mut impl DrawTarget, extra: bool, ) -> Result<(), std::convert::Infallible> { @@ -344,28 +306,13 @@ fn parallel_line_2( let dx = delta.major.abs(); let dy = delta.minor.abs(); - // let threshold = 2 * dy - dx; let e_minor = -2 * dx; let e_major = 2 * dy; - // TODO: Might need skip_first/last offset - let mut length = dx + 1; - let mut error = start_error; - - // if skip_first { - // // Some of the length was consumed by this initial skip iteration. If this is omitted, the - // // line will be drawn 1px too long. - // last_offset -= 1; - - // if error > 0 { - // point += step.minor; - // error += e_minor; - // } - - // error += e_major; - // point += step.major; - // } + let length = dx; + // Setting this to zero causes the first segment before the minor step to be too long + let mut error = 2 * dy - dx; - for _i in 0..(length + last_offset) { + for _i in 0..length { let p = Point::new( if line_is_y_major { point.x >> 8 From 3020e7eac665a9363fb7f13c9e7c5e334a27dbff Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 20 Aug 2024 10:54:41 +0100 Subject: [PATCH 165/188] More cleanup --- debug-tools/examples/thick-line-mul-256.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index a460c71..be76431 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -149,7 +149,7 @@ fn thickline( // 1 // }; - let swap_aa_direction = parallel_step.minor.x < 0 || parallel_step.minor.y < 0; + let swap_aa_direction = parallel_step_full.minor.x < 0 || parallel_step_full.minor.y < 0; let mut mul_point = mul_line.start; @@ -164,7 +164,7 @@ fn thickline( parallel_line_2( mul_point, - non_mul_line, + parallel_is_y_major, parallel_step, parallel_delta, Rgb888::CSS_AQUAMARINE, @@ -176,7 +176,7 @@ fn thickline( } else { parallel_line_2( mul_point, - non_mul_line, + parallel_is_y_major, parallel_step, parallel_delta, Rgb888::CSS_AQUAMARINE, @@ -194,7 +194,7 @@ fn thickline( // Final AA line parallel_line_aa( mul_point, - non_mul_line, + parallel_is_y_major, parallel_step, parallel_delta, // Rgb888::CSS_GOLDENROD, @@ -221,7 +221,7 @@ fn integer_lerp(a: u8, b: u8, f: u8) -> u8 { fn parallel_line_aa( start: Point, - line: Line, + line_is_y_major: bool, step: MajorMinor, delta: MajorMinor, c: Rgb888, @@ -230,8 +230,6 @@ fn parallel_line_aa( ) -> Result<(), std::convert::Infallible> { let mut point = start; - let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - let dx = delta.major.abs(); let dy = delta.minor.abs(); @@ -292,15 +290,13 @@ fn parallel_line_aa( fn parallel_line_2( start: Point, - line: Line, + line_is_y_major: bool, step: MajorMinor, delta: MajorMinor, c: Rgb888, display: &mut impl DrawTarget, extra: bool, ) -> Result<(), std::convert::Infallible> { - let line_is_y_major = line.delta().abs().y >= line.delta().abs().x; - let mut point = start; let dx = delta.major.abs(); From 3ccbe6284daf8a99e2187d7b906c2e270df4f4db Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 20 Aug 2024 10:56:03 +0100 Subject: [PATCH 166/188] Do fewer `abs()` calls --- debug-tools/examples/thick-line-mul-256.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index be76431..de1bc0c 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -94,7 +94,7 @@ fn thickline( let (parallel_delta, parallel_step, parallel_step_full) = if parallel_is_y_major { ( - MajorMinor::new(parallel_delta.y, parallel_delta.x), + MajorMinor::new(parallel_delta.y.abs(), parallel_delta.x.abs()), MajorMinor::new( parallel_step.y_axis(), Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), @@ -103,7 +103,7 @@ fn thickline( ) } else { ( - MajorMinor::new(parallel_delta.x, parallel_delta.y), + MajorMinor::new(parallel_delta.x.abs(), parallel_delta.y.abs()), MajorMinor::new( parallel_step.x_axis(), Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), @@ -230,8 +230,8 @@ fn parallel_line_aa( ) -> Result<(), std::convert::Infallible> { let mut point = start; - let dx = delta.major.abs(); - let dy = delta.minor.abs(); + let dx = delta.major; + let dy = delta.minor; let e_minor = -2 * dx; let e_major = 2 * dy; @@ -299,8 +299,8 @@ fn parallel_line_2( ) -> Result<(), std::convert::Infallible> { let mut point = start; - let dx = delta.major.abs(); - let dy = delta.minor.abs(); + let dx = delta.major; + let dy = delta.minor; let e_minor = -2 * dx; let e_major = 2 * dy; From 8b98763ac4eee46cd2f96422c43065458b99fdd2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 20 Aug 2024 12:31:03 +0100 Subject: [PATCH 167/188] AA on other edge of line too --- debug-tools/examples/thick-line-mul-256.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index de1bc0c..4c348ef 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -203,6 +203,20 @@ fn thickline( display, )?; + if extra { + // First AA line + parallel_line_aa( + mul_line.start + parallel_step_full.minor, + parallel_is_y_major, + parallel_step, + parallel_delta, + // Rgb888::CSS_GOLDENROD, + Rgb888::CSS_AQUAMARINE, + !swap_aa_direction, + display, + )?; + } + Ok(()) } From e3cd41ce81f0c8c6fd0ef4405447694efa259197 Mon Sep 17 00:00:00 2001 From: James Waples Date: Tue, 20 Aug 2024 12:33:22 +0100 Subject: [PATCH 168/188] Non-conditional --- debug-tools/examples/thick-line-mul-256.rs | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 4c348ef..687cf1b 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -203,19 +203,17 @@ fn thickline( display, )?; - if extra { - // First AA line - parallel_line_aa( - mul_line.start + parallel_step_full.minor, - parallel_is_y_major, - parallel_step, - parallel_delta, - // Rgb888::CSS_GOLDENROD, - Rgb888::CSS_AQUAMARINE, - !swap_aa_direction, - display, - )?; - } + // First AA line + parallel_line_aa( + mul_line.start + parallel_step_full.minor, + parallel_is_y_major, + parallel_step, + parallel_delta, + // Rgb888::CSS_GOLDENROD, + Rgb888::CSS_AQUAMARINE, + !swap_aa_direction, + display, + )?; Ok(()) } From 834f2ee4898db17fd95da60fd0074136633d42e7 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 21 Aug 2024 15:04:12 +0100 Subject: [PATCH 169/188] Draw parallel seed line ready for AA --- debug-tools/examples/thick-line-mul-256.rs | 135 +++++++++++++++++---- 1 file changed, 112 insertions(+), 23 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 687cf1b..265625b 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -152,6 +152,9 @@ fn thickline( let swap_aa_direction = parallel_step_full.minor.x < 0 || parallel_step_full.minor.y < 0; let mut mul_point = mul_line.start; + let mut mul_point2 = mul_line.start; + + dbg!(parallel_step, parallel_step_full); while thickness_accumulator.pow(2) <= thickness_threshold { // Pixel(point, Rgb888::RED).draw(display)?; @@ -161,6 +164,7 @@ fn thickline( seed_line_error += e_minor; mul_point += parallel_step_full.major; + mul_point2 += parallel_step.major; parallel_line_2( mul_point, @@ -188,32 +192,117 @@ fn thickline( seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; + dbg!(mul_point, mul_point2, mul_point2.y & 255); + + if extra { + let point = mul_point2; + let background = Rgb888::BLACK; + let c = Rgb888::RED; + + let aa_colour = { + let mul = (if parallel_is_y_major { + point.y & 255 + } else { + point.x & 255 + }) as u8; + + // Some octants need the AA direction to go the other way + let mul = if swap_aa_direction { 255 - mul } else { mul }; + + Rgb888::new( + integer_lerp(c.r(), background.r(), mul), + integer_lerp(c.g(), background.g(), mul), + integer_lerp(c.b(), background.b(), mul), + ) + }; + + let point = mul_point - parallel_step_full.major; + let aa_p = Point::new( + if parallel_is_y_major { + point.x >> 8 + } else { + point.x + }, + if parallel_is_y_major { + point.y + } else { + point.y >> 8 + }, + ); + + Pixel(aa_p, aa_colour).draw(display)?; + } + mul_point += parallel_step_full.minor * -1; + mul_point2 += parallel_step.minor * -1; } - // Final AA line - parallel_line_aa( - mul_point, - parallel_is_y_major, - parallel_step, - parallel_delta, - // Rgb888::CSS_GOLDENROD, - Rgb888::CSS_AQUAMARINE, - swap_aa_direction, - display, - )?; - - // First AA line - parallel_line_aa( - mul_line.start + parallel_step_full.minor, - parallel_is_y_major, - parallel_step, - parallel_delta, - // Rgb888::CSS_GOLDENROD, - Rgb888::CSS_AQUAMARINE, - !swap_aa_direction, - display, - )?; + if extra { + // Final AA line + parallel_line_aa( + mul_point, + parallel_is_y_major, + parallel_step, + parallel_delta, + // Rgb888::CSS_GOLDENROD, + Rgb888::CSS_AQUAMARINE, + swap_aa_direction, + display, + )?; + + // First AA line + parallel_line_aa( + mul_line.start + parallel_step_full.minor, + parallel_is_y_major, + parallel_step, + parallel_delta, + // Rgb888::CSS_GOLDENROD, + Rgb888::CSS_AQUAMARINE, + !swap_aa_direction, + display, + )?; + } + + // { + // let line = Line::new(mul_line.start, mul_point); + + // let delta = line.delta(); + + // let step = Point::new( + // if delta.x >= 0 { 1 } else { -1 }, + // if delta.y >= 0 { 1 } else { -1 }, + // ); + + // let (delta, step) = if !seed_is_y_major { + // ( + // MajorMinor::new(delta.y.abs(), delta.x.abs()), + // MajorMinor::new( + // step.y_axis(), + // Point::new((delta.x / delta.y).abs(), 0).component_mul(step), + // ), + // ) + // } else { + // ( + // MajorMinor::new(delta.x.abs(), delta.y.abs()), + // MajorMinor::new( + // step.x_axis(), + // Point::new(0, (delta.y / delta.x).abs()).component_mul(step), + // ), + // ) + // }; + + // dbg!(delta, step, line, seed_is_y_major); + + // parallel_line_2( + // line.start, + // !seed_is_y_major, + // step, + // delta, + // Rgb888::RED, + // display, + // false, + // )?; + // } Ok(()) } From 4cf6617c4684174a0121df70c1e0dcc2c06bca06 Mon Sep 17 00:00:00 2001 From: James Waples Date: Wed, 21 Aug 2024 17:41:46 +0100 Subject: [PATCH 170/188] Add configurable phase and broken naive AA algo --- debug-tools/examples/thick-line-mul-256.rs | 92 ++++++++++++++-------- 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 265625b..087ad65 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -21,6 +21,7 @@ fn thickline( line: Line, width: i32, extra: bool, + phase: i32, ) -> Result<(), std::convert::Infallible> { if width == 0 { return Ok(()); @@ -85,6 +86,10 @@ fn thickline( let mul_delta = mul_line.delta(); + let mul_seed = mul_line.perpendicular(); + + let mul_seed_delta = mul_seed.delta(); + let parallel_delta = mul_line.delta(); let parallel_step = Point::new( @@ -114,11 +119,17 @@ fn thickline( // --- - // let slope = if parallel_is_y_major { - // mul_delta.x / mul_delta.y - // } else { - // mul_delta.y / mul_delta.x - // }; + let slope = if parallel_is_y_major { + mul_delta.x / mul_delta.y + } else { + mul_delta.y / mul_delta.x + }; + + let seed_slope = if seed_is_y_major { + mul_seed_delta.x / mul_seed_delta.y + } else { + mul_seed_delta.y / mul_seed_delta.x + }; let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -154,7 +165,10 @@ fn thickline( let mut mul_point = mul_line.start; let mut mul_point2 = mul_line.start; - dbg!(parallel_step, parallel_step_full); + // let mut aa = -128i32; + let mut aa = phase; + + dbg!(mul_point, mul_point2, slope); while thickness_accumulator.pow(2) <= thickness_threshold { // Pixel(point, Rgb888::RED).draw(display)?; @@ -192,8 +206,6 @@ fn thickline( seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - dbg!(mul_point, mul_point2, mul_point2.y & 255); - if extra { let point = mul_point2; let background = Rgb888::BLACK; @@ -209,6 +221,8 @@ fn thickline( // Some octants need the AA direction to go the other way let mul = if swap_aa_direction { 255 - mul } else { mul }; + let mul = (aa & 255) as u8; + Rgb888::new( integer_lerp(c.r(), background.r(), mul), integer_lerp(c.g(), background.g(), mul), @@ -235,33 +249,35 @@ fn thickline( mul_point += parallel_step_full.minor * -1; mul_point2 += parallel_step.minor * -1; + + aa += seed_slope; } - if extra { - // Final AA line - parallel_line_aa( - mul_point, - parallel_is_y_major, - parallel_step, - parallel_delta, - // Rgb888::CSS_GOLDENROD, - Rgb888::CSS_AQUAMARINE, - swap_aa_direction, - display, - )?; + // if extra { + // // Final AA line + // parallel_line_aa( + // mul_point, + // parallel_is_y_major, + // parallel_step, + // parallel_delta, + // // Rgb888::CSS_GOLDENROD, + // Rgb888::CSS_AQUAMARINE, + // swap_aa_direction, + // display, + // )?; - // First AA line - parallel_line_aa( - mul_line.start + parallel_step_full.minor, - parallel_is_y_major, - parallel_step, - parallel_delta, - // Rgb888::CSS_GOLDENROD, - Rgb888::CSS_AQUAMARINE, - !swap_aa_direction, - display, - )?; - } + // // First AA line + // parallel_line_aa( + // mul_line.start + parallel_step_full.minor, + // parallel_is_y_major, + // parallel_step, + // parallel_delta, + // // Rgb888::CSS_GOLDENROD, + // Rgb888::CSS_AQUAMARINE, + // !swap_aa_direction, + // display, + // )?; + // } // { // let line = Line::new(mul_line.start, mul_point); @@ -455,6 +471,7 @@ struct LineDebug { start: Point, end: Point, stroke_width: u32, + phase: i32, extra: bool, } @@ -471,8 +488,8 @@ impl App for LineDebug { Self { start: end - Point::new(80, 35), end, - // end: start + Point::new(100, 0), stroke_width: 10, + phase: 0, extra: true, } } @@ -482,6 +499,7 @@ impl App for LineDebug { Parameter::new("start", &mut self.start), Parameter::new("end", &mut self.end), Parameter::new("stroke", &mut self.stroke_width), + Parameter::new("phase", &mut self.phase), Parameter::new("extra", &mut self.extra), ] } @@ -498,7 +516,13 @@ impl App for LineDebug { let _mock_display: MockDisplay = MockDisplay::new(); - thickline(display, Line::new(self.start, self.end), width, self.extra)?; + thickline( + display, + Line::new(self.start, self.end), + width, + self.extra, + self.phase, + )?; // let l = Line::new(self.start, self.end); From a329b70e2fff3a9f0759cc38752b09329d583b02 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 26 Aug 2024 10:41:15 +0100 Subject: [PATCH 171/188] Get rid of `mul_point2` --- debug-tools/examples/thick-line-mul-256.rs | 46 +--------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 087ad65..6c49b4e 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -163,12 +163,11 @@ fn thickline( let swap_aa_direction = parallel_step_full.minor.x < 0 || parallel_step_full.minor.y < 0; let mut mul_point = mul_line.start; - let mut mul_point2 = mul_line.start; // let mut aa = -128i32; let mut aa = phase; - dbg!(mul_point, mul_point2, slope); + dbg!(mul_point, slope); while thickness_accumulator.pow(2) <= thickness_threshold { // Pixel(point, Rgb888::RED).draw(display)?; @@ -178,7 +177,6 @@ fn thickline( seed_line_error += e_minor; mul_point += parallel_step_full.major; - mul_point2 += parallel_step.major; parallel_line_2( mul_point, @@ -206,49 +204,7 @@ fn thickline( seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - if extra { - let point = mul_point2; - let background = Rgb888::BLACK; - let c = Rgb888::RED; - - let aa_colour = { - let mul = (if parallel_is_y_major { - point.y & 255 - } else { - point.x & 255 - }) as u8; - - // Some octants need the AA direction to go the other way - let mul = if swap_aa_direction { 255 - mul } else { mul }; - - let mul = (aa & 255) as u8; - - Rgb888::new( - integer_lerp(c.r(), background.r(), mul), - integer_lerp(c.g(), background.g(), mul), - integer_lerp(c.b(), background.b(), mul), - ) - }; - - let point = mul_point - parallel_step_full.major; - let aa_p = Point::new( - if parallel_is_y_major { - point.x >> 8 - } else { - point.x - }, - if parallel_is_y_major { - point.y - } else { - point.y >> 8 - }, - ); - - Pixel(aa_p, aa_colour).draw(display)?; - } - mul_point += parallel_step_full.minor * -1; - mul_point2 += parallel_step.minor * -1; aa += seed_slope; } From 8ba4b520875bf5fa7a15080d4648c22be5de29c2 Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 26 Aug 2024 10:55:25 +0100 Subject: [PATCH 172/188] Draw seed line --- debug-tools/examples/thick-line-mul-256.rs | 143 ++++++++++++--------- 1 file changed, 82 insertions(+), 61 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 6c49b4e..902d069 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -45,29 +45,11 @@ fn thickline( let non_mul_perpendicular_delta = line.perpendicular().delta(); let seed_line = line.perpendicular(); + let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); + let seed_is_y_major = non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); - let seed_line_delta = seed_line.delta(); - - let (thickness_majorminor, seed_line_delta) = if seed_is_y_major { - ( - MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), - MajorMinor::new(seed_line_delta.y, seed_line_delta.x), - ) - } - // X-major line (i.e. X delta is longer than Y) - else { - ( - MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), - MajorMinor::new(seed_line_delta.x, seed_line_delta.y), - ) - }; - - // --- - - let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); - // Using a block to isolate mutability let mul_line = { let mut line = line; @@ -84,13 +66,54 @@ fn thickline( line }; + let parallel_delta = mul_line.delta(); + let mul_delta = mul_line.delta(); - let mul_seed = mul_line.perpendicular(); + let mul_seed = { + let mut line = seed_line; + + if seed_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } + + line + }; let mul_seed_delta = mul_seed.delta(); - let parallel_delta = mul_line.delta(); + let seed_step = Point::new( + if mul_seed_delta.x >= 0 { 1 } else { -1 }, + if mul_seed_delta.y >= 0 { 1 } else { -1 }, + ); + + let (thickness_majorminor, seed_line_delta, seed_step) = if seed_is_y_major { + ( + MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), + MajorMinor::new(mul_seed_delta.y, mul_seed_delta.x), + MajorMinor::new( + seed_step.y_axis(), + Point::new((mul_seed_delta.x / mul_seed_delta.y).abs(), 0).component_mul(seed_step), + ), + ) + } + // X-major line (i.e. X delta is longer than Y) + else { + ( + MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), + MajorMinor::new(mul_seed_delta.x, mul_seed_delta.y), + MajorMinor::new( + seed_step.x_axis(), + Point::new(0, (mul_seed_delta.y / mul_seed_delta.x).abs()).component_mul(seed_step), + ), + ) + }; + + // --- let parallel_step = Point::new( if parallel_delta.x >= 0 { 1 } else { -1 }, @@ -119,18 +142,6 @@ fn thickline( // --- - let slope = if parallel_is_y_major { - mul_delta.x / mul_delta.y - } else { - mul_delta.y / mul_delta.x - }; - - let seed_slope = if seed_is_y_major { - mul_seed_delta.x / mul_seed_delta.y - } else { - mul_seed_delta.y / mul_seed_delta.x - }; - let dx = seed_line_delta.major.abs(); let dy = seed_line_delta.minor.abs(); @@ -163,50 +174,60 @@ fn thickline( let swap_aa_direction = parallel_step_full.minor.x < 0 || parallel_step_full.minor.y < 0; let mut mul_point = mul_line.start; + let mut seed_point = mul_seed.start; - // let mut aa = -128i32; - let mut aa = phase; - - dbg!(mul_point, slope); + dbg!(seed_point, seed_step, seed_is_y_major); while thickness_accumulator.pow(2) <= thickness_threshold { - // Pixel(point, Rgb888::RED).draw(display)?; + let p = Point::new( + if seed_is_y_major { + seed_point.x >> 8 + } else { + seed_point.x + }, + if seed_is_y_major { + seed_point.y + } else { + seed_point.y >> 8 + }, + ); + + Pixel(p, Rgb888::RED).draw(display)?; // Move seed line in minor direction if seed_line_error > 0 { seed_line_error += e_minor; mul_point += parallel_step_full.major; - - parallel_line_2( - mul_point, - parallel_is_y_major, - parallel_step, - parallel_delta, - Rgb888::CSS_AQUAMARINE, - display, - true, - )?; + seed_point += seed_step.minor; + + // parallel_line_2( + // mul_point, + // parallel_is_y_major, + // parallel_step, + // parallel_delta, + // Rgb888::CSS_AQUAMARINE, + // display, + // true, + // )?; thickness_accumulator += 2 * thickness_dy; } else { - parallel_line_2( - mul_point, - parallel_is_y_major, - parallel_step, - parallel_delta, - Rgb888::CSS_AQUAMARINE, - display, - false, - )?; + // parallel_line_2( + // mul_point, + // parallel_is_y_major, + // parallel_step, + // parallel_delta, + // Rgb888::CSS_AQUAMARINE, + // display, + // false, + // )?; } seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - + seed_point += seed_step.major; mul_point += parallel_step_full.minor * -1; - - aa += seed_slope; } // if extra { From f5e6b9395658a4c5182f473b6177d28c5378f66a Mon Sep 17 00:00:00 2001 From: James Waples Date: Mon, 26 Aug 2024 11:11:33 +0100 Subject: [PATCH 173/188] Draw AA pixels on seed line --- debug-tools/examples/thick-line-mul-256.rs | 39 ++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 902d069..e786831 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -91,7 +91,7 @@ fn thickline( if mul_seed_delta.y >= 0 { 1 } else { -1 }, ); - let (thickness_majorminor, seed_line_delta, seed_step) = if seed_is_y_major { + let (thickness_majorminor, seed_line_delta, seed_step, seed_step_full) = if seed_is_y_major { ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(mul_seed_delta.y, mul_seed_delta.x), @@ -99,6 +99,7 @@ fn thickline( seed_step.y_axis(), Point::new((mul_seed_delta.x / mul_seed_delta.y).abs(), 0).component_mul(seed_step), ), + MajorMinor::new(seed_step.y_axis(), seed_step.x_axis() * 256), ) } // X-major line (i.e. X delta is longer than Y) @@ -110,6 +111,7 @@ fn thickline( seed_step.x_axis(), Point::new(0, (mul_seed_delta.y / mul_seed_delta.x).abs()).component_mul(seed_step), ), + MajorMinor::new(seed_step.x_axis(), seed_step.y_axis() * 256), ) }; @@ -192,7 +194,40 @@ fn thickline( }, ); - Pixel(p, Rgb888::RED).draw(display)?; + let background = Rgb888::BLACK; + let c = Rgb888::RED; + + Pixel(p, c).draw(display)?; + + let aa_c = { + let c = Rgb888::GREEN; + + let mul = (if seed_is_y_major { + seed_point.x & 255 + } else { + seed_point.y & 255 + }) as u8; + + // Some octants need the AA direction to go the other way + let mul = if swap_aa_direction { 255 - mul } else { mul }; + + Rgb888::new( + integer_lerp(c.r(), background.r(), mul), + integer_lerp(c.g(), background.g(), mul), + integer_lerp(c.b(), background.b(), mul), + ) + }; + + let aa_p = { + let p = seed_point - seed_step_full.minor; + + Point::new( + if seed_is_y_major { p.x >> 8 } else { p.x }, + if seed_is_y_major { p.y } else { p.y >> 8 }, + ) + }; + + Pixel(aa_p, aa_c).draw(display)?; // Move seed line in minor direction if seed_line_error > 0 { From 3869a08aee89dec83e09918ae7583933f905131c Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 29 Nov 2024 19:28:25 +0000 Subject: [PATCH 174/188] Go back to just integers and no multiplication --- debug-tools/examples/thick-line-mul-256.rs | 270 ++++++++++----------- 1 file changed, 134 insertions(+), 136 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index e786831..54aa6d1 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -50,39 +50,41 @@ fn thickline( let seed_is_y_major = non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); - // Using a block to isolate mutability - let mul_line = { - let mut line = line; - - // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - if parallel_is_y_major { - line.start.x *= 256; - line.end.x *= 256; - } else { - line.start.y *= 256; - line.end.y *= 256; - } + // // Using a block to isolate mutability + // let mul_line = { + // let mut line = line; - line - }; + // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + // if parallel_is_y_major { + // line.start.x *= 256; + // line.end.x *= 256; + // } else { + // line.start.y *= 256; + // line.end.y *= 256; + // } - let parallel_delta = mul_line.delta(); + // line + // }; + let mul_line = line; + + let parallel_delta = line.delta(); let mul_delta = mul_line.delta(); - let mul_seed = { - let mut line = seed_line; + // let mul_seed = { + // let mut line = seed_line; - if seed_is_y_major { - line.start.x *= 256; - line.end.x *= 256; - } else { - line.start.y *= 256; - line.end.y *= 256; - } + // if seed_is_y_major { + // line.start.x *= 256; + // line.end.x *= 256; + // } else { + // line.start.y *= 256; + // line.end.y *= 256; + // } - line - }; + // line + // }; + let mul_seed = seed_line; let mul_seed_delta = mul_seed.delta(); @@ -95,11 +97,13 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(mul_seed_delta.y, mul_seed_delta.x), - MajorMinor::new( - seed_step.y_axis(), - Point::new((mul_seed_delta.x / mul_seed_delta.y).abs(), 0).component_mul(seed_step), - ), - MajorMinor::new(seed_step.y_axis(), seed_step.x_axis() * 256), + // MajorMinor::new( + // seed_step.y_axis(), + // Point::new((mul_seed_delta.x / mul_seed_delta.y).abs(), 0).component_mul(seed_step), + // ), + MajorMinor::new(seed_step.y_axis(), seed_step.x_axis()), + // MajorMinor::new(seed_step.y_axis(), seed_step.x_axis() * 256), + MajorMinor::new(seed_step.y_axis(), seed_step.x_axis()), ) } // X-major line (i.e. X delta is longer than Y) @@ -107,11 +111,13 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), MajorMinor::new(mul_seed_delta.x, mul_seed_delta.y), - MajorMinor::new( - seed_step.x_axis(), - Point::new(0, (mul_seed_delta.y / mul_seed_delta.x).abs()).component_mul(seed_step), - ), - MajorMinor::new(seed_step.x_axis(), seed_step.y_axis() * 256), + // MajorMinor::new( + // seed_step.x_axis(), + // Point::new(0, (mul_seed_delta.y / mul_seed_delta.x).abs()).component_mul(seed_step), + // ), + MajorMinor::new(seed_step.x_axis(), seed_step.y_axis()), + // MajorMinor::new(seed_step.x_axis(), seed_step.y_axis() * 256), + MajorMinor::new(seed_step.x_axis(), seed_step.y_axis()), ) }; @@ -125,20 +131,22 @@ fn thickline( let (parallel_delta, parallel_step, parallel_step_full) = if parallel_is_y_major { ( MajorMinor::new(parallel_delta.y.abs(), parallel_delta.x.abs()), - MajorMinor::new( - parallel_step.y_axis(), - Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), - ), - MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis() * 256), + // MajorMinor::new( + // parallel_step.y_axis(), + // Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), + // ), + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), ) } else { ( MajorMinor::new(parallel_delta.x.abs(), parallel_delta.y.abs()), - MajorMinor::new( - parallel_step.x_axis(), - Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), - ), - MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis() * 256), + // MajorMinor::new( + // parallel_step.x_axis(), + // Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), + // ), + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), ) }; @@ -157,6 +165,7 @@ fn thickline( let e_minor = -2 * dx; let e_major = 2 * dy; let mut seed_line_error = 2 * dy - dx; + let mut parallel_error = 0; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill let thickness_threshold = @@ -175,94 +184,92 @@ fn thickline( let swap_aa_direction = parallel_step_full.minor.x < 0 || parallel_step_full.minor.y < 0; - let mut mul_point = mul_line.start; + // let mut mul_point = mul_line.start; + let mut mul_point = non_mul_line.start; let mut seed_point = mul_seed.start; - dbg!(seed_point, seed_step, seed_is_y_major); - while thickness_accumulator.pow(2) <= thickness_threshold { - let p = Point::new( - if seed_is_y_major { - seed_point.x >> 8 - } else { - seed_point.x - }, - if seed_is_y_major { - seed_point.y - } else { - seed_point.y >> 8 - }, - ); + // let p = Point::new( + // if seed_is_y_major { + // seed_point.x >> 8 + // } else { + // seed_point.x + // }, + // if seed_is_y_major { + // seed_point.y + // } else { + // seed_point.y >> 8 + // }, + // ); + + let p = seed_point; let background = Rgb888::BLACK; let c = Rgb888::RED; Pixel(p, c).draw(display)?; - let aa_c = { - let c = Rgb888::GREEN; - - let mul = (if seed_is_y_major { - seed_point.x & 255 - } else { - seed_point.y & 255 - }) as u8; - - // Some octants need the AA direction to go the other way - let mul = if swap_aa_direction { 255 - mul } else { mul }; - - Rgb888::new( - integer_lerp(c.r(), background.r(), mul), - integer_lerp(c.g(), background.g(), mul), - integer_lerp(c.b(), background.b(), mul), - ) - }; - - let aa_p = { - let p = seed_point - seed_step_full.minor; - - Point::new( - if seed_is_y_major { p.x >> 8 } else { p.x }, - if seed_is_y_major { p.y } else { p.y >> 8 }, - ) - }; - - Pixel(aa_p, aa_c).draw(display)?; + // let aa_c = { + // let c = Rgb888::GREEN; + + // let mul = (if seed_is_y_major { + // seed_point.x & 255 + // } else { + // seed_point.y & 255 + // }) as u8; + + // // Some octants need the AA direction to go the other way + // let mul = if swap_aa_direction { 255 - mul } else { mul }; + + // Rgb888::new( + // integer_lerp(c.r(), background.r(), mul), + // integer_lerp(c.g(), background.g(), mul), + // integer_lerp(c.b(), background.b(), mul), + // ) + // }; + + // let aa_p = { + // let p = seed_point - seed_step_full.minor; + + // Point::new( + // if seed_is_y_major { p.x >> 8 } else { p.x }, + // if seed_is_y_major { p.y } else { p.y >> 8 }, + // ) + // }; + + // Pixel(aa_p, aa_c).draw(display)?; + + parallel_line_2( + p, + parallel_is_y_major, + parallel_step, + parallel_delta, + Rgb888::CSS_AQUAMARINE, + display, + true, + parallel_error, + )?; // Move seed line in minor direction if seed_line_error > 0 { seed_line_error += e_minor; - mul_point += parallel_step_full.major; seed_point += seed_step.minor; + mul_point += parallel_step_full.major; - // parallel_line_2( - // mul_point, - // parallel_is_y_major, - // parallel_step, - // parallel_delta, - // Rgb888::CSS_AQUAMARINE, - // display, - // true, - // )?; + if parallel_error > 0 { + parallel_error += e_minor; + } thickness_accumulator += 2 * thickness_dy; - } else { - // parallel_line_2( - // mul_point, - // parallel_is_y_major, - // parallel_step, - // parallel_delta, - // Rgb888::CSS_AQUAMARINE, - // display, - // false, - // )?; + parallel_error += e_major; } seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - seed_point += seed_step.major; - mul_point += parallel_step_full.minor * -1; + seed_point += seed_step.major * 2; + // FIXME: Why is this negative? + mul_point += parallel_step_full.minor * -2; } // if extra { @@ -425,6 +432,7 @@ fn parallel_line_2( c: Rgb888, display: &mut impl DrawTarget, extra: bool, + initial_error: i32, ) -> Result<(), std::convert::Infallible> { let mut point = start; @@ -435,37 +443,27 @@ fn parallel_line_2( let e_major = 2 * dy; let length = dx; // Setting this to zero causes the first segment before the minor step to be too long - let mut error = 2 * dy - dx; + // let mut error = 2 * dy - dx; + let mut error = initial_error; for _i in 0..length { - let p = Point::new( - if line_is_y_major { - point.x >> 8 - } else { - point.x - }, - if line_is_y_major { - point.y - } else { - point.y >> 8 - }, - ); + let p = point; Pixel(p, c).draw(display)?; - // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is - // required for the additional diagonal move lines that are drawn when stepping in both the - // major and minor directions in the seed line. - if extra { - let p = point + step.minor; + // // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is + // // required for the additional diagonal move lines that are drawn when stepping in both the + // // major and minor directions in the seed line. + // if extra { + // let p = point + step.minor; - let p = Point::new( - if line_is_y_major { p.x >> 8 } else { p.x }, - if line_is_y_major { p.y } else { p.y >> 8 }, - ); + // let p = Point::new( + // if line_is_y_major { p.x >> 8 } else { p.x }, + // if line_is_y_major { p.y } else { p.y >> 8 }, + // ); - Pixel(p, c).draw(display)?; - } + // Pixel(p, c).draw(display)?; + // } if error > 0 { point += step.minor; From 804c3c3727413d7f3c2322262d8b660b3a8611db Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 29 Nov 2024 20:23:59 +0000 Subject: [PATCH 175/188] Revert "Go back to just integers and no multiplication" This reverts commit 3869a08aee89dec83e09918ae7583933f905131c. --- debug-tools/examples/thick-line-mul-256.rs | 270 +++++++++++---------- 1 file changed, 136 insertions(+), 134 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 54aa6d1..e786831 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -50,41 +50,39 @@ fn thickline( let seed_is_y_major = non_mul_perpendicular_delta.y.abs() >= non_mul_perpendicular_delta.x.abs(); - // // Using a block to isolate mutability - // let mul_line = { - // let mut line = line; - - // // Multiply minor direction by 256 so we get AA resolution in lower 8 bits - // if parallel_is_y_major { - // line.start.x *= 256; - // line.end.x *= 256; - // } else { - // line.start.y *= 256; - // line.end.y *= 256; - // } + // Using a block to isolate mutability + let mul_line = { + let mut line = line; + + // Multiply minor direction by 256 so we get AA resolution in lower 8 bits + if parallel_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } - // line - // }; - let mul_line = line; + line + }; - let parallel_delta = line.delta(); + let parallel_delta = mul_line.delta(); let mul_delta = mul_line.delta(); - // let mul_seed = { - // let mut line = seed_line; + let mul_seed = { + let mut line = seed_line; - // if seed_is_y_major { - // line.start.x *= 256; - // line.end.x *= 256; - // } else { - // line.start.y *= 256; - // line.end.y *= 256; - // } + if seed_is_y_major { + line.start.x *= 256; + line.end.x *= 256; + } else { + line.start.y *= 256; + line.end.y *= 256; + } - // line - // }; - let mul_seed = seed_line; + line + }; let mul_seed_delta = mul_seed.delta(); @@ -97,13 +95,11 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.y, non_mul_perpendicular_delta.x), MajorMinor::new(mul_seed_delta.y, mul_seed_delta.x), - // MajorMinor::new( - // seed_step.y_axis(), - // Point::new((mul_seed_delta.x / mul_seed_delta.y).abs(), 0).component_mul(seed_step), - // ), - MajorMinor::new(seed_step.y_axis(), seed_step.x_axis()), - // MajorMinor::new(seed_step.y_axis(), seed_step.x_axis() * 256), - MajorMinor::new(seed_step.y_axis(), seed_step.x_axis()), + MajorMinor::new( + seed_step.y_axis(), + Point::new((mul_seed_delta.x / mul_seed_delta.y).abs(), 0).component_mul(seed_step), + ), + MajorMinor::new(seed_step.y_axis(), seed_step.x_axis() * 256), ) } // X-major line (i.e. X delta is longer than Y) @@ -111,13 +107,11 @@ fn thickline( ( MajorMinor::new(non_mul_perpendicular_delta.x, non_mul_perpendicular_delta.y), MajorMinor::new(mul_seed_delta.x, mul_seed_delta.y), - // MajorMinor::new( - // seed_step.x_axis(), - // Point::new(0, (mul_seed_delta.y / mul_seed_delta.x).abs()).component_mul(seed_step), - // ), - MajorMinor::new(seed_step.x_axis(), seed_step.y_axis()), - // MajorMinor::new(seed_step.x_axis(), seed_step.y_axis() * 256), - MajorMinor::new(seed_step.x_axis(), seed_step.y_axis()), + MajorMinor::new( + seed_step.x_axis(), + Point::new(0, (mul_seed_delta.y / mul_seed_delta.x).abs()).component_mul(seed_step), + ), + MajorMinor::new(seed_step.x_axis(), seed_step.y_axis() * 256), ) }; @@ -131,22 +125,20 @@ fn thickline( let (parallel_delta, parallel_step, parallel_step_full) = if parallel_is_y_major { ( MajorMinor::new(parallel_delta.y.abs(), parallel_delta.x.abs()), - // MajorMinor::new( - // parallel_step.y_axis(), - // Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), - // ), - MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), - MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), + MajorMinor::new( + parallel_step.y_axis(), + Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), + ), + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis() * 256), ) } else { ( MajorMinor::new(parallel_delta.x.abs(), parallel_delta.y.abs()), - // MajorMinor::new( - // parallel_step.x_axis(), - // Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), - // ), - MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), - MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), + MajorMinor::new( + parallel_step.x_axis(), + Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), + ), + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis() * 256), ) }; @@ -165,7 +157,6 @@ fn thickline( let e_minor = -2 * dx; let e_major = 2 * dy; let mut seed_line_error = 2 * dy - dx; - let mut parallel_error = 0; // Subtract 1 if using AA so 1px wide lines are _only_ drawn with AA - no solid fill let thickness_threshold = @@ -184,92 +175,94 @@ fn thickline( let swap_aa_direction = parallel_step_full.minor.x < 0 || parallel_step_full.minor.y < 0; - // let mut mul_point = mul_line.start; - let mut mul_point = non_mul_line.start; + let mut mul_point = mul_line.start; let mut seed_point = mul_seed.start; + dbg!(seed_point, seed_step, seed_is_y_major); + while thickness_accumulator.pow(2) <= thickness_threshold { - // let p = Point::new( - // if seed_is_y_major { - // seed_point.x >> 8 - // } else { - // seed_point.x - // }, - // if seed_is_y_major { - // seed_point.y - // } else { - // seed_point.y >> 8 - // }, - // ); - - let p = seed_point; + let p = Point::new( + if seed_is_y_major { + seed_point.x >> 8 + } else { + seed_point.x + }, + if seed_is_y_major { + seed_point.y + } else { + seed_point.y >> 8 + }, + ); let background = Rgb888::BLACK; let c = Rgb888::RED; Pixel(p, c).draw(display)?; - // let aa_c = { - // let c = Rgb888::GREEN; - - // let mul = (if seed_is_y_major { - // seed_point.x & 255 - // } else { - // seed_point.y & 255 - // }) as u8; - - // // Some octants need the AA direction to go the other way - // let mul = if swap_aa_direction { 255 - mul } else { mul }; - - // Rgb888::new( - // integer_lerp(c.r(), background.r(), mul), - // integer_lerp(c.g(), background.g(), mul), - // integer_lerp(c.b(), background.b(), mul), - // ) - // }; - - // let aa_p = { - // let p = seed_point - seed_step_full.minor; - - // Point::new( - // if seed_is_y_major { p.x >> 8 } else { p.x }, - // if seed_is_y_major { p.y } else { p.y >> 8 }, - // ) - // }; - - // Pixel(aa_p, aa_c).draw(display)?; - - parallel_line_2( - p, - parallel_is_y_major, - parallel_step, - parallel_delta, - Rgb888::CSS_AQUAMARINE, - display, - true, - parallel_error, - )?; + let aa_c = { + let c = Rgb888::GREEN; + + let mul = (if seed_is_y_major { + seed_point.x & 255 + } else { + seed_point.y & 255 + }) as u8; + + // Some octants need the AA direction to go the other way + let mul = if swap_aa_direction { 255 - mul } else { mul }; + + Rgb888::new( + integer_lerp(c.r(), background.r(), mul), + integer_lerp(c.g(), background.g(), mul), + integer_lerp(c.b(), background.b(), mul), + ) + }; + + let aa_p = { + let p = seed_point - seed_step_full.minor; + + Point::new( + if seed_is_y_major { p.x >> 8 } else { p.x }, + if seed_is_y_major { p.y } else { p.y >> 8 }, + ) + }; + + Pixel(aa_p, aa_c).draw(display)?; // Move seed line in minor direction if seed_line_error > 0 { seed_line_error += e_minor; - seed_point += seed_step.minor; mul_point += parallel_step_full.major; + seed_point += seed_step.minor; - if parallel_error > 0 { - parallel_error += e_minor; - } + // parallel_line_2( + // mul_point, + // parallel_is_y_major, + // parallel_step, + // parallel_delta, + // Rgb888::CSS_AQUAMARINE, + // display, + // true, + // )?; thickness_accumulator += 2 * thickness_dy; - parallel_error += e_major; + } else { + // parallel_line_2( + // mul_point, + // parallel_is_y_major, + // parallel_step, + // parallel_delta, + // Rgb888::CSS_AQUAMARINE, + // display, + // false, + // )?; } seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - seed_point += seed_step.major * 2; - // FIXME: Why is this negative? - mul_point += parallel_step_full.minor * -2; + seed_point += seed_step.major; + mul_point += parallel_step_full.minor * -1; } // if extra { @@ -432,7 +425,6 @@ fn parallel_line_2( c: Rgb888, display: &mut impl DrawTarget, extra: bool, - initial_error: i32, ) -> Result<(), std::convert::Infallible> { let mut point = start; @@ -443,27 +435,37 @@ fn parallel_line_2( let e_major = 2 * dy; let length = dx; // Setting this to zero causes the first segment before the minor step to be too long - // let mut error = 2 * dy - dx; - let mut error = initial_error; + let mut error = 2 * dy - dx; for _i in 0..length { - let p = point; + let p = Point::new( + if line_is_y_major { + point.x >> 8 + } else { + point.x + }, + if line_is_y_major { + point.y + } else { + point.y >> 8 + }, + ); Pixel(p, c).draw(display)?; - // // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is - // // required for the additional diagonal move lines that are drawn when stepping in both the - // // major and minor directions in the seed line. - // if extra { - // let p = point + step.minor; + // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is + // required for the additional diagonal move lines that are drawn when stepping in both the + // major and minor directions in the seed line. + if extra { + let p = point + step.minor; - // let p = Point::new( - // if line_is_y_major { p.x >> 8 } else { p.x }, - // if line_is_y_major { p.y } else { p.y >> 8 }, - // ); + let p = Point::new( + if line_is_y_major { p.x >> 8 } else { p.x }, + if line_is_y_major { p.y } else { p.y >> 8 }, + ); - // Pixel(p, c).draw(display)?; - // } + Pixel(p, c).draw(display)?; + } if error > 0 { point += step.minor; From 5816164690a3320c03ed8480bed05812d97628a4 Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 29 Nov 2024 20:28:18 +0000 Subject: [PATCH 176/188] Extremely hacky correct step --- debug-tools/examples/thick-line-mul-256.rs | 38 +++++++++++++++------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index e786831..b4a6e63 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -178,7 +178,18 @@ fn thickline( let mut mul_point = mul_line.start; let mut seed_point = mul_seed.start; - dbg!(seed_point, seed_step, seed_is_y_major); + let mut prev = Point::new( + if seed_is_y_major { + seed_point.x >> 8 + } else { + seed_point.x + }, + if seed_is_y_major { + seed_point.y + } else { + seed_point.y >> 8 + }, + ); while thickness_accumulator.pow(2) <= thickness_threshold { let p = Point::new( @@ -233,18 +244,21 @@ fn thickline( if seed_line_error > 0 { seed_line_error += e_minor; - mul_point += parallel_step_full.major; seed_point += seed_step.minor; - // parallel_line_2( - // mul_point, - // parallel_is_y_major, - // parallel_step, - // parallel_delta, - // Rgb888::CSS_AQUAMARINE, - // display, - // true, - // )?; + if prev.x != p.x { + mul_point += parallel_step_full.major; + } + + parallel_line_2( + mul_point, + parallel_is_y_major, + parallel_step, + parallel_delta, + Rgb888::CSS_AQUAMARINE, + display, + true, + )?; thickness_accumulator += 2 * thickness_dy; } else { @@ -263,6 +277,8 @@ fn thickline( thickness_accumulator += 2 * thickness_dx; seed_point += seed_step.major; mul_point += parallel_step_full.minor * -1; + + prev = p; } // if extra { From fceacc0652ae8f66876b3dac4fce7ed57b7828aa Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 29 Nov 2024 20:38:18 +0000 Subject: [PATCH 177/188] Tweaks to start debugging parallel initial error --- debug-tools/examples/thick-line-mul-256.rs | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index b4a6e63..5cc11bb 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -275,8 +275,8 @@ fn thickline( seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - seed_point += seed_step.major; - mul_point += parallel_step_full.minor * -1; + seed_point += seed_step.major * 2; + mul_point += parallel_step_full.minor * -1 * 2; prev = p; } @@ -444,6 +444,8 @@ fn parallel_line_2( ) -> Result<(), std::convert::Infallible> { let mut point = start; + point += step.major * 2; + let dx = delta.major; let dy = delta.minor; @@ -469,19 +471,19 @@ fn parallel_line_2( Pixel(p, c).draw(display)?; - // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is - // required for the additional diagonal move lines that are drawn when stepping in both the - // major and minor directions in the seed line. - if extra { - let p = point + step.minor; + // // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is + // // required for the additional diagonal move lines that are drawn when stepping in both the + // // major and minor directions in the seed line. + // if extra { + // let p = point + step.minor; - let p = Point::new( - if line_is_y_major { p.x >> 8 } else { p.x }, - if line_is_y_major { p.y } else { p.y >> 8 }, - ); + // let p = Point::new( + // if line_is_y_major { p.x >> 8 } else { p.x }, + // if line_is_y_major { p.y } else { p.y >> 8 }, + // ); - Pixel(p, c).draw(display)?; - } + // Pixel(p, c).draw(display)?; + // } if error > 0 { point += step.minor; From 4b82812321c6bceb910841c5f323a74a797def9d Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 29 Nov 2024 21:07:08 +0000 Subject: [PATCH 178/188] Slightly closer to correct parallel line phasing --- debug-tools/examples/thick-line-mul-256.rs | 90 +++++++++++++--------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 5cc11bb..6d5d95c 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -66,6 +66,8 @@ fn thickline( line }; + let mul_line = line; + let parallel_delta = mul_line.delta(); let mul_delta = mul_line.delta(); @@ -127,18 +129,20 @@ fn thickline( MajorMinor::new(parallel_delta.y.abs(), parallel_delta.x.abs()), MajorMinor::new( parallel_step.y_axis(), - Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), + parallel_step.x_axis(), + // Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), ), - MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis() * 256), + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), ) } else { ( MajorMinor::new(parallel_delta.x.abs(), parallel_delta.y.abs()), MajorMinor::new( parallel_step.x_axis(), - Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), + parallel_step.y_axis(), + // Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), ), - MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis() * 256), + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), ) }; @@ -175,7 +179,7 @@ fn thickline( let swap_aa_direction = parallel_step_full.minor.x < 0 || parallel_step_full.minor.y < 0; - let mut mul_point = mul_line.start; + let mut mul_point = non_mul_line.start; let mut seed_point = mul_seed.start; let mut prev = Point::new( @@ -191,6 +195,9 @@ fn thickline( }, ); + let mut parallel_error = 2 * parallel_delta.minor + parallel_delta.major; + // let mut parallel_error = 0; + while thickness_accumulator.pow(2) <= thickness_threshold { let p = Point::new( if seed_is_y_major { @@ -246,10 +253,6 @@ fn thickline( seed_point += seed_step.minor; - if prev.x != p.x { - mul_point += parallel_step_full.major; - } - parallel_line_2( mul_point, parallel_is_y_major, @@ -257,9 +260,20 @@ fn thickline( parallel_delta, Rgb888::CSS_AQUAMARINE, display, - true, + false, + parallel_error, )?; + if prev.x != p.x { + mul_point += parallel_step_full.major; + + if parallel_error > 0 { + parallel_error += -2 * parallel_delta.major; + } + + parallel_error += 2 * parallel_delta.minor; + } + thickness_accumulator += 2 * thickness_dy; } else { // parallel_line_2( @@ -275,8 +289,8 @@ fn thickline( seed_line_error += e_major; thickness_accumulator += 2 * thickness_dx; - seed_point += seed_step.major * 2; - mul_point += parallel_step_full.minor * -1 * 2; + seed_point += seed_step.major * 3; + mul_point += parallel_step_full.minor * -1 * 3; prev = p; } @@ -441,6 +455,7 @@ fn parallel_line_2( c: Rgb888, display: &mut impl DrawTarget, extra: bool, + initial_error: i32, ) -> Result<(), std::convert::Infallible> { let mut point = start; @@ -453,37 +468,40 @@ fn parallel_line_2( let e_major = 2 * dy; let length = dx; // Setting this to zero causes the first segment before the minor step to be too long - let mut error = 2 * dy - dx; + // let mut error = 2 * dy - dx; + let mut error = initial_error; for _i in 0..length { - let p = Point::new( - if line_is_y_major { - point.x >> 8 - } else { - point.x - }, - if line_is_y_major { - point.y - } else { - point.y >> 8 - }, - ); + // let p = Point::new( + // if line_is_y_major { + // point.x >> 8 + // } else { + // point.x + // }, + // if line_is_y_major { + // point.y + // } else { + // point.y >> 8 + // }, + // ); + + let p = point; Pixel(p, c).draw(display)?; - // // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is - // // required for the additional diagonal move lines that are drawn when stepping in both the - // // major and minor directions in the seed line. - // if extra { - // let p = point + step.minor; + // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is + // required for the additional diagonal move lines that are drawn when stepping in both the + // major and minor directions in the seed line. + if extra { + let p = point + step.minor; - // let p = Point::new( - // if line_is_y_major { p.x >> 8 } else { p.x }, - // if line_is_y_major { p.y } else { p.y >> 8 }, - // ); + let p = Point::new( + if line_is_y_major { p.x >> 8 } else { p.x }, + if line_is_y_major { p.y } else { p.y >> 8 }, + ); - // Pixel(p, c).draw(display)?; - // } + Pixel(p, c).draw(display)?; + } if error > 0 { point += step.minor; From eba5ebf977017597ec506ed22ecd5d4d7c8bee76 Mon Sep 17 00:00:00 2001 From: James Waples Date: Fri, 29 Nov 2024 21:25:23 +0000 Subject: [PATCH 179/188] Correct parallel line phasing But with no extra lines yet --- debug-tools/examples/thick-line-mul-256.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 6d5d95c..94bcd37 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -253,17 +253,6 @@ fn thickline( seed_point += seed_step.minor; - parallel_line_2( - mul_point, - parallel_is_y_major, - parallel_step, - parallel_delta, - Rgb888::CSS_AQUAMARINE, - display, - false, - parallel_error, - )?; - if prev.x != p.x { mul_point += parallel_step_full.major; @@ -274,6 +263,17 @@ fn thickline( parallel_error += 2 * parallel_delta.minor; } + parallel_line_2( + p, + parallel_is_y_major, + parallel_step, + parallel_delta, + Rgb888::CSS_AQUAMARINE, + display, + false, + parallel_error, + )?; + thickness_accumulator += 2 * thickness_dy; } else { // parallel_line_2( From 442da301aa35011086847d7fc760547f7ef5a875 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 18:03:47 +0000 Subject: [PATCH 180/188] Add example that muls both axes by 256 --- debug-tools/examples/mul-both-axes.rs | 263 +++++++++++++++++++++ debug-tools/examples/thick-line-mul-256.rs | 211 +++++++++++++---- 2 files changed, 423 insertions(+), 51 deletions(-) create mode 100644 debug-tools/examples/mul-both-axes.rs diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs new file mode 100644 index 0000000..dba9fe1 --- /dev/null +++ b/debug-tools/examples/mul-both-axes.rs @@ -0,0 +1,263 @@ +use embedded_graphics::{ + geometry::PointExt, mock_display::MockDisplay, pixelcolor::Rgb888, prelude::*, primitives::Line, +}; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; +use framework::prelude::*; + +#[derive(Debug, Clone, Copy)] +struct MajorMinor { + major: T, + minor: T, +} + +impl MajorMinor { + fn new(major: T, minor: T) -> Self { + Self { major, minor } + } +} + +fn thickline( + display: &mut impl DrawTarget, + line: Line, + width: i32, + extra: bool, + phase: i32, +) -> Result<(), std::convert::Infallible> { + if width == 0 { + return Ok(()); + } + + // Draw line using existing algorithm to check against + // if extra { + // let mut line = line; + + // // line.start.y += width * 2; + // // line.end.y += width * 2; + + // line.into_styled(embedded_graphics::primitives::PrimitiveStyle::with_stroke( + // Rgb888::WHITE, + // width as u32, + // )) + // .draw(display)?; + // } + + let line = Line::new(line.start * 256, line.end * 256); + + let seed_line = line.perpendicular(); + let seed_delta = seed_line.delta(); + + let parallel_delta = line.delta(); + + let parallel_is_y_major = line.delta().y.abs() >= line.delta().x.abs(); + let seed_is_y_major = seed_delta.y.abs() >= seed_delta.x.abs(); + + let seed_step = Point::new( + if seed_delta.x >= 0 { 1 } else { -1 }, + if seed_delta.y >= 0 { 1 } else { -1 }, + ) * 256; + + let parallel_step = Point::new( + if parallel_delta.x >= 0 { 1 } else { -1 }, + if parallel_delta.y >= 0 { 1 } else { -1 }, + ) * 256; + + // --- + + let seed_delta_majorminor = if seed_is_y_major { + MajorMinor::new(seed_delta.y, seed_delta.x) + } else { + MajorMinor::new(seed_delta.x, seed_delta.y) + }; + + let seed_step_majorminor = if seed_is_y_major { + MajorMinor::new(seed_step.y_axis(), seed_step.x_axis()) + } else { + MajorMinor::new(seed_step.x_axis(), seed_step.y_axis()) + }; + + // --- + + let dx = seed_delta_majorminor.major.abs(); + let dy = seed_delta_majorminor.minor.abs(); + + // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square + let e_minor = -2 * dx; + let e_major = 2 * dy; + + let mut seed_line_error = 2 * dy - dx; + let mut point = seed_line.start; + + for i in 0..width { + let p = point / 256; + + Pixel(p, Rgb888::CSS_AQUAMARINE).draw(display)?; + + if seed_line_error > 0 { + point += seed_step_majorminor.minor; + seed_line_error += e_minor; + } + + point += seed_step_majorminor.major; + seed_line_error += e_major; + } + + Ok(()) +} + +/// Integer-only LERP with 8 bits of precision. +/// +/// Thanks to for the inspiration. +fn integer_lerp(a: u8, b: u8, f: u8) -> u8 { + let a = u16::from(a); + let b = u16::from(b); + let f = u16::from(f); + + let res = (a * (u16::from(u8::MAX) - f) + b * f) >> 8; + + res as u8 +} + +fn parallel_line_2( + start: Point, + line_is_y_major: bool, + step: MajorMinor, + delta: MajorMinor, + c: Rgb888, + display: &mut impl DrawTarget, + extra: bool, + initial_error: i32, +) -> Result<(), std::convert::Infallible> { + let mut point = start; + + point += step.major * 2; + + let dx = delta.major; + let dy = delta.minor; + + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx; + // Setting this to zero causes the first segment before the minor step to be too long + // let mut error = 2 * dy - dx; + let mut error = initial_error; + + for _i in 0..length { + // let p = Point::new( + // if line_is_y_major { + // point.x >> 8 + // } else { + // point.x + // }, + // if line_is_y_major { + // point.y + // } else { + // point.y >> 8 + // }, + // ); + + let p = point; + + Pixel(p, c).draw(display)?; + + // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is + // required for the additional diagonal move lines that are drawn when stepping in both the + // major and minor directions in the seed line. + if extra { + let p = point + step.minor; + + let p = Point::new( + if line_is_y_major { p.x >> 8 } else { p.x }, + if line_is_y_major { p.y } else { p.y >> 8 }, + ); + + Pixel(p, c).draw(display)?; + } + + if error > 0 { + point += step.minor; + error += e_minor; + } + + point += step.major; + error += e_major; + } + + Ok(()) +} + +struct LineDebug { + start: Point, + end: Point, + stroke_width: u32, + phase: i32, + extra: bool, +} + +impl App for LineDebug { + type Color = Rgb888; + const DISPLAY_SIZE: Size = Size::new(200, 200); + // const DISPLAY_SIZE: Size = Size::new(64, 64); + + fn new() -> Self { + let end = Point::new( + Self::DISPLAY_SIZE.width as i32 / 2, + Self::DISPLAY_SIZE.height as i32 / 2, + ); + Self { + start: end - Point::new(80, 35), + end, + stroke_width: 10, + phase: 0, + extra: true, + } + } + + fn parameters(&mut self) -> Vec { + vec![ + Parameter::new("start", &mut self.start), + Parameter::new("end", &mut self.end), + Parameter::new("stroke", &mut self.stroke_width), + Parameter::new("phase", &mut self.phase), + Parameter::new("extra", &mut self.extra), + ] + } + + fn draw( + &self, + display: &mut SimulatorDisplay, + ) -> Result<(), std::convert::Infallible> { + let Point { x: _x0, y: _y0 } = self.start; + + // let width = 2 * self.stroke_width as i32 * f32::sqrt((dx * dx + dy * dy) as f32) as i32; + // let width = (self.stroke_width as i32).pow(2) * (dx * dx + dy * dy); + let width = self.stroke_width as i32; + + let _mock_display: MockDisplay = MockDisplay::new(); + + thickline( + display, + Line::new(self.start, self.end), + width, + self.extra, + self.phase, + )?; + + // let l = Line::new(self.start, self.end); + + // l.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, width as u32)) + // .draw(display)?; + + // l.perpendicular() + // .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 1)) + // .draw(&mut display.translated(Point::new(40, 40)))?; + + Ok(()) + } +} + +fn main() { + let settings = OutputSettingsBuilder::new().scale(5).build(); + let window = Window::new("Line debugger", &settings); + + LineDebug::run(window); +} diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 94bcd37..598ead1 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -66,11 +66,9 @@ fn thickline( line }; - let mul_line = line; + let parallel_delta = line.delta(); - let parallel_delta = mul_line.delta(); - - let mul_delta = mul_line.delta(); + let parallel_delta_mul = mul_line.delta(); let mul_seed = { let mut line = seed_line; @@ -124,27 +122,38 @@ fn thickline( if parallel_delta.y >= 0 { 1 } else { -1 }, ); - let (parallel_delta, parallel_step, parallel_step_full) = if parallel_is_y_major { - ( - MajorMinor::new(parallel_delta.y.abs(), parallel_delta.x.abs()), - MajorMinor::new( - parallel_step.y_axis(), - parallel_step.x_axis(), - // Point::new((mul_delta.x / mul_delta.y).abs(), 0).component_mul(parallel_step), - ), - MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), - ) - } else { - ( - MajorMinor::new(parallel_delta.x.abs(), parallel_delta.y.abs()), - MajorMinor::new( - parallel_step.x_axis(), - parallel_step.y_axis(), - // Point::new(0, (mul_delta.y / mul_delta.x).abs()).component_mul(parallel_step), - ), - MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), - ) - }; + let (parallel_delta, parallel_step, parallel_step_mul, parallel_step_full) = + if parallel_is_y_major { + ( + MajorMinor::new(parallel_delta.y.abs(), parallel_delta.x.abs()), + MajorMinor::new( + parallel_step.y_axis(), + parallel_step.x_axis(), + // Point::new((parallel_delta_mul.x / parallel_delta_mul.y).abs(), 0).component_mul(parallel_step), + ), + MajorMinor::new( + parallel_step.y_axis(), + Point::new((parallel_delta_mul.x / parallel_delta_mul.y).abs(), 0) + .component_mul(parallel_step), + ), + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()), + ) + } else { + ( + MajorMinor::new(parallel_delta.x.abs(), parallel_delta.y.abs()), + MajorMinor::new( + parallel_step.x_axis(), + parallel_step.y_axis(), + // Point::new(0, (parallel_delta_mul.y / parallel_delta_mul.x).abs()).component_mul(parallel_step), + ), + MajorMinor::new( + parallel_step.x_axis(), + Point::new(0, (parallel_delta_mul.y / parallel_delta_mul.x).abs()) + .component_mul(parallel_step), + ), + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()), + ) + }; // --- @@ -295,6 +304,17 @@ fn thickline( prev = p; } + parallel_line_2_aa( + mul_point, + parallel_is_y_major, + parallel_step_mul, + parallel_delta, + Rgb888::CSS_GOLDENROD, + display, + false, + phase, + )?; + // if extra { // // Final AA line // parallel_line_aa( @@ -378,32 +398,51 @@ fn integer_lerp(a: u8, b: u8, f: u8) -> u8 { res as u8 } -fn parallel_line_aa( +fn parallel_line_2_aa( start: Point, line_is_y_major: bool, - step: MajorMinor, + mut step: MajorMinor, delta: MajorMinor, c: Rgb888, - swap_aa_direction: bool, display: &mut impl DrawTarget, + extra: bool, + initial_error: i32, ) -> Result<(), std::convert::Infallible> { let mut point = start; - let dx = delta.major; - let dy = delta.minor; + point += step.major * 2; + + let mut dx = delta.major; + let mut dy = delta.minor; + + if line_is_y_major { + dx *= 256; + point.x *= 256; + // step.minor.x = (step.minor.x * 256) / (step.minor.y * 256); + } else { + dy *= 256; + point.y *= 256; + // step.minor.y = (step.minor.y * 256) / (step.minor.x * 256); + } let e_minor = -2 * dx; let e_major = 2 * dy; - let length = dx; - let mut error = 2 * dy - dx; + let length = delta.major; + // Setting this to zero causes the first segment before the minor step to be too long + // let mut error = 2 * dy - dx; + let mut error = initial_error; - // Blend colour for AA edge let background = Rgb888::BLACK; - // FIXME: If line is exactly diagonal, no AA is performed. It should have a 50% edge. - for _i in 0..length { - let aa_colour = { + let p = point; + + let p = Point::new( + if line_is_y_major { p.x >> 8 } else { p.x }, + if line_is_y_major { p.y } else { p.y >> 8 }, + ); + + let c = { let mul = (if line_is_y_major { point.x & 255 } else { @@ -411,7 +450,7 @@ fn parallel_line_aa( }) as u8; // Some octants need the AA direction to go the other way - let mul = if swap_aa_direction { 255 - mul } else { mul }; + // let mul = if swap_aa_direction { 255 - mul } else { mul }; Rgb888::new( integer_lerp(c.r(), background.r(), mul), @@ -420,28 +459,29 @@ fn parallel_line_aa( ) }; - let aa_p = Point::new( - if line_is_y_major { - point.x >> 8 - } else { - point.x - }, - if line_is_y_major { - point.y - } else { - point.y >> 8 - }, - ); + Pixel(p, c).draw(display)?; - Pixel(aa_p, aa_colour).draw(display)?; + // Draws a pixel connecting a diagonal move into a solid stairstep-looking piece. This is + // required for the additional diagonal move lines that are drawn when stepping in both the + // major and minor directions in the seed line. + if extra { + let p = point + step.minor; + + let p = Point::new( + if line_is_y_major { p.x >> 8 } else { p.x }, + if line_is_y_major { p.y } else { p.y >> 8 }, + ); + + Pixel(p, c).draw(display)?; + } if error > 0 { point += step.minor; error += e_minor; } - error += e_major; point += step.major; + error += e_major; } Ok(()) @@ -515,6 +555,75 @@ fn parallel_line_2( Ok(()) } +fn parallel_line_aa( + start: Point, + line_is_y_major: bool, + step: MajorMinor, + delta: MajorMinor, + c: Rgb888, + swap_aa_direction: bool, + display: &mut impl DrawTarget, +) -> Result<(), std::convert::Infallible> { + let mut point = start; + + let dx = delta.major; + let dy = delta.minor; + + let e_minor = -2 * dx; + let e_major = 2 * dy; + let length = dx; + let mut error = 2 * dy - dx; + + // Blend colour for AA edge + let background = Rgb888::BLACK; + + // FIXME: If line is exactly diagonal, no AA is performed. It should have a 50% edge. + + for _i in 0..length { + let aa_colour = { + let mul = (if line_is_y_major { + point.x & 255 + } else { + point.y & 255 + }) as u8; + + // Some octants need the AA direction to go the other way + let mul = if swap_aa_direction { 255 - mul } else { mul }; + + Rgb888::new( + integer_lerp(c.r(), background.r(), mul), + integer_lerp(c.g(), background.g(), mul), + integer_lerp(c.b(), background.b(), mul), + ) + }; + + let aa_p = Point::new( + if line_is_y_major { + point.x >> 8 + } else { + point.x + }, + if line_is_y_major { + point.y + } else { + point.y >> 8 + }, + ); + + Pixel(aa_p, aa_colour).draw(display)?; + + if error > 0 { + point += step.minor; + error += e_minor; + } + + error += e_major; + point += step.major; + } + + Ok(()) +} + struct LineDebug { start: Point, end: Point, From f88a0cffa7ca38611ad7c4f5ff78c3f7b7a91bef Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 18:17:04 +0000 Subject: [PATCH 181/188] Use line slope for seed step --- debug-tools/examples/mul-both-axes.rs | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs index dba9fe1..cfb2d9f 100644 --- a/debug-tools/examples/mul-both-axes.rs +++ b/debug-tools/examples/mul-both-axes.rs @@ -41,6 +41,11 @@ fn thickline( // .draw(display)?; // } + let original_line = line; + let original_delta = line.delta(); + let original_seed = line.perpendicular(); + let original_seed_delta = original_seed.delta(); + let line = Line::new(line.start * 256, line.end * 256); let seed_line = line.perpendicular(); @@ -69,12 +74,38 @@ fn thickline( MajorMinor::new(seed_delta.x, seed_delta.y) }; + // Plain old boring multiplied by 256 let seed_step_majorminor = if seed_is_y_major { MajorMinor::new(seed_step.y_axis(), seed_step.x_axis()) } else { MajorMinor::new(seed_step.x_axis(), seed_step.y_axis()) }; + // Using line slope + let seed_step_majorminor = if seed_is_y_major { + MajorMinor::new( + seed_step.y_axis(), + Point::new( + seed_delta + .x + .checked_div(original_seed_delta.x * seed_step.x.signum()) + .unwrap_or(0), + 0, + ), + ) + } else { + MajorMinor::new( + seed_step.x_axis(), + Point::new( + 0, + seed_delta + .y + .checked_div(original_seed_delta.y * seed_step.y.signum()) + .unwrap_or(0), + ), + ) + }; + // --- let dx = seed_delta_majorminor.major.abs(); From 931b10f0c19774174d446f983cf9ea510c41a15a Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 18:28:24 +0000 Subject: [PATCH 182/188] Draw parallel lines using multiplied coords --- debug-tools/examples/mul-both-axes.rs | 73 +++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs index cfb2d9f..53d2a5b 100644 --- a/debug-tools/examples/mul-both-axes.rs +++ b/debug-tools/examples/mul-both-axes.rs @@ -41,10 +41,16 @@ fn thickline( // .draw(display)?; // } - let original_line = line; - let original_delta = line.delta(); + let original_parallel = line; + let original_parallel_delta = line.delta(); let original_seed = line.perpendicular(); let original_seed_delta = original_seed.delta(); + let original_delta_majorminor = + if original_parallel_delta.y.abs() >= original_parallel_delta.x.abs() { + MajorMinor::new(original_parallel_delta.y, original_parallel_delta.x) + } else { + MajorMinor::new(original_parallel_delta.x, original_parallel_delta.y) + }; let line = Line::new(line.start * 256, line.end * 256); @@ -74,12 +80,23 @@ fn thickline( MajorMinor::new(seed_delta.x, seed_delta.y) }; + let parallel_delta_majorminor = if parallel_is_y_major { + MajorMinor::new(parallel_delta.y, parallel_delta.x) + } else { + MajorMinor::new(parallel_delta.x, parallel_delta.y) + }; + // Plain old boring multiplied by 256 let seed_step_majorminor = if seed_is_y_major { MajorMinor::new(seed_step.y_axis(), seed_step.x_axis()) } else { MajorMinor::new(seed_step.x_axis(), seed_step.y_axis()) }; + let parallel_step_majorminor = if parallel_is_y_major { + MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()) + } else { + MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()) + }; // Using line slope let seed_step_majorminor = if seed_is_y_major { @@ -105,6 +122,29 @@ fn thickline( ), ) }; + let parallel_step_majorminor = if parallel_is_y_major { + MajorMinor::new( + parallel_step.y_axis(), + Point::new( + parallel_delta + .x + .checked_div(original_parallel_delta.x * parallel_step.x.signum()) + .unwrap_or(0), + 0, + ), + ) + } else { + MajorMinor::new( + parallel_step.x_axis(), + Point::new( + 0, + parallel_delta + .y + .checked_div(original_parallel_delta.y * parallel_step.y.signum()) + .unwrap_or(0), + ), + ) + }; // --- @@ -121,7 +161,34 @@ fn thickline( for i in 0..width { let p = point / 256; - Pixel(p, Rgb888::CSS_AQUAMARINE).draw(display)?; + Pixel(p, Rgb888::RED).draw(display)?; + + // Draw parallel line + { + let dx = parallel_delta_majorminor.major.abs(); + let dy = parallel_delta_majorminor.minor.abs(); + + // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square + let e_minor = -2 * dx; + let e_major = 2 * dy; + + let mut parallel_line_error = 2 * dy - dx; + let mut parallel_point = point + parallel_step_majorminor.major; + + for i in 0..original_delta_majorminor.major.abs() { + let p = parallel_point / 256; + + Pixel(p, Rgb888::CSS_AQUAMARINE).draw(display)?; + + if parallel_line_error > 0 { + parallel_point += parallel_step_majorminor.minor; + parallel_line_error += e_minor; + } + + parallel_point += parallel_step_majorminor.major; + parallel_line_error += e_major; + } + } if seed_line_error > 0 { point += seed_step_majorminor.minor; From 8442de31916dce911f7ac178376064dc724415c0 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 18:40:49 +0000 Subject: [PATCH 183/188] Start drawing parallel AA line Colours aren't right yet though - the AA steps in chunks of a few pixels at a time. The actual position is in phase though. Excellent! --- debug-tools/examples/mul-both-axes.rs | 71 ++++++++++++++++++++------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs index 53d2a5b..215e010 100644 --- a/debug-tools/examples/mul-both-axes.rs +++ b/debug-tools/examples/mul-both-axes.rs @@ -103,10 +103,7 @@ fn thickline( MajorMinor::new( seed_step.y_axis(), Point::new( - seed_delta - .x - .checked_div(original_seed_delta.x * seed_step.x.signum()) - .unwrap_or(0), + seed_delta.x / original_seed_delta.y * seed_step.y.signum(), 0, ), ) @@ -115,10 +112,7 @@ fn thickline( seed_step.x_axis(), Point::new( 0, - seed_delta - .y - .checked_div(original_seed_delta.y * seed_step.y.signum()) - .unwrap_or(0), + seed_delta.y / original_seed_delta.x * seed_step.x.signum(), ), ) }; @@ -126,10 +120,7 @@ fn thickline( MajorMinor::new( parallel_step.y_axis(), Point::new( - parallel_delta - .x - .checked_div(original_parallel_delta.x * parallel_step.x.signum()) - .unwrap_or(0), + parallel_delta.x / original_parallel_delta.y * parallel_step.y.signum(), 0, ), ) @@ -138,10 +129,7 @@ fn thickline( parallel_step.x_axis(), Point::new( 0, - parallel_delta - .y - .checked_div(original_parallel_delta.y * parallel_step.y.signum()) - .unwrap_or(0), + parallel_delta.y / original_parallel_delta.x * parallel_step.x.signum(), ), ) }; @@ -173,7 +161,7 @@ fn thickline( let e_major = 2 * dy; let mut parallel_line_error = 2 * dy - dx; - let mut parallel_point = point + parallel_step_majorminor.major; + let mut parallel_point = point + parallel_step_majorminor.major * 2; for i in 0..original_delta_majorminor.major.abs() { let p = parallel_point / 256; @@ -195,10 +183,57 @@ fn thickline( seed_line_error += e_minor; } - point += seed_step_majorminor.major; + point += seed_step_majorminor.major * 2; seed_line_error += e_major; } + // Draw AA line + { + let dx = parallel_delta_majorminor.major.abs(); + let dy = parallel_delta_majorminor.minor.abs(); + + // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square + let e_minor = -2 * dx; + let e_major = 2 * dy; + + let mut parallel_line_error = 2 * dy - dx; + let mut parallel_point = point + parallel_step_majorminor.major * 2; + + for i in 0..original_delta_majorminor.major.abs() { + let p = parallel_point / 256; + + let aa_c = { + let c = Rgb888::CSS_GOLDENROD; + let background = Rgb888::BLACK; + + let mul = (if parallel_is_y_major { + parallel_point.x & 255 + } else { + parallel_point.y & 255 + }) as u8; + + // // Some octants need the AA direction to go the other way + // let mul = if swap_aa_direction { 255 - mul } else { mul }; + + Rgb888::new( + integer_lerp(c.r(), background.r(), mul), + integer_lerp(c.g(), background.g(), mul), + integer_lerp(c.b(), background.b(), mul), + ) + }; + + Pixel(p, aa_c).draw(display)?; + + if parallel_line_error > 0 { + parallel_point += parallel_step_majorminor.minor; + parallel_line_error += e_minor; + } + + parallel_point += parallel_step_majorminor.major; + parallel_line_error += e_major; + } + } + Ok(()) } From eea83fde68317800a7006050f2e41fb0fb4e9234 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 19:27:56 +0000 Subject: [PATCH 184/188] Fix slope calculation --- debug-tools/examples/mul-both-axes.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs index 215e010..5bf801d 100644 --- a/debug-tools/examples/mul-both-axes.rs +++ b/debug-tools/examples/mul-both-axes.rs @@ -103,7 +103,10 @@ fn thickline( MajorMinor::new( seed_step.y_axis(), Point::new( - seed_delta.x / original_seed_delta.y * seed_step.y.signum(), + seed_delta + .x + .checked_div(original_seed_delta.x * seed_step.x.signum()) + .unwrap_or(0), 0, ), ) @@ -112,7 +115,10 @@ fn thickline( seed_step.x_axis(), Point::new( 0, - seed_delta.y / original_seed_delta.x * seed_step.x.signum(), + seed_delta + .y + .checked_div(original_seed_delta.y * seed_step.y.signum()) + .unwrap_or(0), ), ) }; @@ -120,7 +126,10 @@ fn thickline( MajorMinor::new( parallel_step.y_axis(), Point::new( - parallel_delta.x / original_parallel_delta.y * parallel_step.y.signum(), + parallel_delta + .x + .checked_div(original_parallel_delta.x * parallel_step.x.signum()) + .unwrap_or(0), 0, ), ) @@ -129,7 +138,10 @@ fn thickline( parallel_step.x_axis(), Point::new( 0, - parallel_delta.y / original_parallel_delta.x * parallel_step.x.signum(), + parallel_delta + .y + .checked_div(original_parallel_delta.y * parallel_step.y.signum()) + .unwrap_or(0), ), ) }; From 873c99859663b40915632e0b4e915b2480317daa Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 20:13:23 +0000 Subject: [PATCH 185/188] HELL YEAH in phase parallel edge AA! --- debug-tools/examples/mul-both-axes.rs | 93 +++++++++++++++------- debug-tools/examples/thick-line-mul-256.rs | 9 +++ 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs index 5bf801d..4eeb4cc 100644 --- a/debug-tools/examples/mul-both-axes.rs +++ b/debug-tools/examples/mul-both-axes.rs @@ -27,7 +27,7 @@ fn thickline( return Ok(()); } - // Draw line using existing algorithm to check against + // // Draw line using existing algorithm to check against // if extra { // let mut line = line; @@ -36,14 +36,14 @@ fn thickline( // line.into_styled(embedded_graphics::primitives::PrimitiveStyle::with_stroke( // Rgb888::WHITE, - // width as u32, + // 1, // )) // .draw(display)?; // } let original_parallel = line; - let original_parallel_delta = line.delta(); - let original_seed = line.perpendicular(); + let original_parallel_delta = original_parallel.delta(); + let original_seed = original_parallel.perpendicular(); let original_seed_delta = original_seed.delta(); let original_delta_majorminor = if original_parallel_delta.y.abs() >= original_parallel_delta.x.abs() { @@ -51,6 +51,16 @@ fn thickline( } else { MajorMinor::new(original_parallel_delta.x, original_parallel_delta.y) }; + let original_seed_step = Point::new( + if original_seed_delta.x >= 0 { 1 } else { -1 }, + if original_seed_delta.y >= 0 { 1 } else { -1 }, + ); + let original_seed_step_majorminor = + if original_seed_delta.y.abs() >= original_seed_delta.x.abs() { + MajorMinor::new(original_seed_step.y_axis(), original_seed_step.x_axis()) + } else { + MajorMinor::new(original_seed_step.x_axis(), original_seed_step.y_axis()) + }; let line = Line::new(line.start * 256, line.end * 256); @@ -79,11 +89,10 @@ fn thickline( } else { MajorMinor::new(seed_delta.x, seed_delta.y) }; - let parallel_delta_majorminor = if parallel_is_y_major { - MajorMinor::new(parallel_delta.y, parallel_delta.x) + MajorMinor::new(parallel_delta.y / 256, parallel_delta.x) } else { - MajorMinor::new(parallel_delta.x, parallel_delta.y) + MajorMinor::new(parallel_delta.x / 256, parallel_delta.y) }; // Plain old boring multiplied by 256 @@ -92,7 +101,7 @@ fn thickline( } else { MajorMinor::new(seed_step.x_axis(), seed_step.y_axis()) }; - let parallel_step_majorminor = if parallel_is_y_major { + let parallel_step_256_majorminor = if parallel_is_y_major { MajorMinor::new(parallel_step.y_axis(), parallel_step.x_axis()) } else { MajorMinor::new(parallel_step.x_axis(), parallel_step.y_axis()) @@ -126,10 +135,8 @@ fn thickline( MajorMinor::new( parallel_step.y_axis(), Point::new( - parallel_delta - .x - .checked_div(original_parallel_delta.x * parallel_step.x.signum()) - .unwrap_or(0), + (original_parallel_delta.x * 256) + / (original_parallel_delta.y * parallel_step.y.signum()), 0, ), ) @@ -138,10 +145,8 @@ fn thickline( parallel_step.x_axis(), Point::new( 0, - parallel_delta - .y - .checked_div(original_parallel_delta.y * parallel_step.y.signum()) - .unwrap_or(0), + (original_parallel_delta.y * 256) + / (original_parallel_delta.x * parallel_step.x.signum()), ), ) }; @@ -157,12 +162,39 @@ fn thickline( let mut seed_line_error = 2 * dy - dx; let mut point = seed_line.start; + let mut parallel_point = line.start; for i in 0..width { let p = point / 256; + // assert_eq!(if seed_is_y_major { point.y } else { point.x } % 256, 0); + Pixel(p, Rgb888::RED).draw(display)?; + let aa_p = point / 256 - original_seed_step_majorminor.minor * 2; + + let aa_c = { + let c = Rgb888::CSS_GOLDENROD; + let background = Rgb888::BLACK; + + let mul = (if seed_is_y_major { + point.x & 255 + } else { + point.y & 255 + }) as u8; + + // // Some octants need the AA direction to go the other way + // let mul = if swap_aa_direction { 255 - mul } else { mul }; + + Rgb888::new( + integer_lerp(c.r(), background.r(), mul), + integer_lerp(c.g(), background.g(), mul), + integer_lerp(c.b(), background.b(), mul), + ) + }; + + Pixel(aa_p, aa_c).draw(display)?; + // Draw parallel line { let dx = parallel_delta_majorminor.major.abs(); @@ -173,32 +205,37 @@ fn thickline( let e_major = 2 * dy; let mut parallel_line_error = 2 * dy - dx; - let mut parallel_point = point + parallel_step_majorminor.major * 2; + let mut point = parallel_point + parallel_step_majorminor.major * 2; for i in 0..original_delta_majorminor.major.abs() { - let p = parallel_point / 256; + let p = point / 256; Pixel(p, Rgb888::CSS_AQUAMARINE).draw(display)?; if parallel_line_error > 0 { - parallel_point += parallel_step_majorminor.minor; + point += parallel_step_majorminor.minor; parallel_line_error += e_minor; } - parallel_point += parallel_step_majorminor.major; + point += parallel_step_majorminor.major; parallel_line_error += e_major; } } if seed_line_error > 0 { point += seed_step_majorminor.minor; + parallel_point += parallel_step_256_majorminor.major; seed_line_error += e_minor; } - point += seed_step_majorminor.major * 2; + point += seed_step_majorminor.major; + parallel_point -= parallel_step_256_majorminor.minor; seed_line_error += e_major; } + // A gap for debugging + parallel_point += seed_step_majorminor.major; + // Draw AA line { let dx = parallel_delta_majorminor.major.abs(); @@ -209,19 +246,19 @@ fn thickline( let e_major = 2 * dy; let mut parallel_line_error = 2 * dy - dx; - let mut parallel_point = point + parallel_step_majorminor.major * 2; + let mut point = parallel_point + parallel_step_majorminor.major * 2; for i in 0..original_delta_majorminor.major.abs() { - let p = parallel_point / 256; + let aa_p = point / 256; let aa_c = { let c = Rgb888::CSS_GOLDENROD; let background = Rgb888::BLACK; let mul = (if parallel_is_y_major { - parallel_point.x & 255 + point.x & 255 } else { - parallel_point.y & 255 + point.y & 255 }) as u8; // // Some octants need the AA direction to go the other way @@ -234,14 +271,14 @@ fn thickline( ) }; - Pixel(p, aa_c).draw(display)?; + Pixel(aa_p, aa_c).draw(display)?; if parallel_line_error > 0 { - parallel_point += parallel_step_majorminor.minor; + point += parallel_step_majorminor.minor; parallel_line_error += e_minor; } - parallel_point += parallel_step_majorminor.major; + point += parallel_step_majorminor.major; parallel_line_error += e_major; } } diff --git a/debug-tools/examples/thick-line-mul-256.rs b/debug-tools/examples/thick-line-mul-256.rs index 598ead1..59291d0 100644 --- a/debug-tools/examples/thick-line-mul-256.rs +++ b/debug-tools/examples/thick-line-mul-256.rs @@ -155,6 +155,15 @@ fn thickline( ) }; + dbg!( + parallel_delta, + parallel_step, + parallel_step_mul, + parallel_step_full + ); + + todo!(); + // --- let dx = seed_line_delta.major.abs(); From ab4aca29957a4b2de14ecb09e0eda7c66652fbb6 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 20:25:55 +0000 Subject: [PATCH 186/188] Starting edge AA too --- debug-tools/examples/mul-both-axes.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs index 4eeb4cc..f9ee591 100644 --- a/debug-tools/examples/mul-both-axes.rs +++ b/debug-tools/examples/mul-both-axes.rs @@ -85,9 +85,9 @@ fn thickline( // --- let seed_delta_majorminor = if seed_is_y_major { - MajorMinor::new(seed_delta.y, seed_delta.x) + MajorMinor::new(seed_delta.y / 256, seed_delta.x) } else { - MajorMinor::new(seed_delta.x, seed_delta.y) + MajorMinor::new(seed_delta.x / 256, seed_delta.y) }; let parallel_delta_majorminor = if parallel_is_y_major { MajorMinor::new(parallel_delta.y / 256, parallel_delta.x) @@ -112,10 +112,7 @@ fn thickline( MajorMinor::new( seed_step.y_axis(), Point::new( - seed_delta - .x - .checked_div(original_seed_delta.x * seed_step.x.signum()) - .unwrap_or(0), + (original_seed_delta.x * 256) / (original_seed_delta.y * seed_step.y.signum()), 0, ), ) @@ -124,10 +121,7 @@ fn thickline( seed_step.x_axis(), Point::new( 0, - seed_delta - .y - .checked_div(original_seed_delta.y * seed_step.y.signum()) - .unwrap_or(0), + (original_seed_delta.y * 256) / (original_seed_delta.x * seed_step.x.signum()), ), ) }; @@ -162,13 +156,12 @@ fn thickline( let mut seed_line_error = 2 * dy - dx; let mut point = seed_line.start; + let mut prev = point; let mut parallel_point = line.start; for i in 0..width { let p = point / 256; - // assert_eq!(if seed_is_y_major { point.y } else { point.x } % 256, 0); - Pixel(p, Rgb888::RED).draw(display)?; let aa_p = point / 256 - original_seed_step_majorminor.minor * 2; @@ -224,12 +217,12 @@ fn thickline( if seed_line_error > 0 { point += seed_step_majorminor.minor; - parallel_point += parallel_step_256_majorminor.major; + parallel_point += seed_step_majorminor.minor; seed_line_error += e_minor; } point += seed_step_majorminor.major; - parallel_point -= parallel_step_256_majorminor.minor; + parallel_point += seed_step_majorminor.major; seed_line_error += e_major; } From 6e7bb5b0d2b024720263158f7645f53acc1f838e Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 20:28:37 +0000 Subject: [PATCH 187/188] Make AA lines hug rest --- debug-tools/examples/mul-both-axes.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs index f9ee591..dd0b68f 100644 --- a/debug-tools/examples/mul-both-axes.rs +++ b/debug-tools/examples/mul-both-axes.rs @@ -160,11 +160,11 @@ fn thickline( let mut parallel_point = line.start; for i in 0..width { - let p = point / 256; + // let p = point / 256; - Pixel(p, Rgb888::RED).draw(display)?; + // Pixel(p, Rgb888::RED).draw(display)?; - let aa_p = point / 256 - original_seed_step_majorminor.minor * 2; + let aa_p = point / 256 - original_seed_step_majorminor.minor; let aa_c = { let c = Rgb888::CSS_GOLDENROD; @@ -198,7 +198,7 @@ fn thickline( let e_major = 2 * dy; let mut parallel_line_error = 2 * dy - dx; - let mut point = parallel_point + parallel_step_majorminor.major * 2; + let mut point = parallel_point; for i in 0..original_delta_majorminor.major.abs() { let p = point / 256; @@ -226,9 +226,6 @@ fn thickline( seed_line_error += e_major; } - // A gap for debugging - parallel_point += seed_step_majorminor.major; - // Draw AA line { let dx = parallel_delta_majorminor.major.abs(); @@ -239,7 +236,7 @@ fn thickline( let e_major = 2 * dy; let mut parallel_line_error = 2 * dy - dx; - let mut point = parallel_point + parallel_step_majorminor.major * 2; + let mut point = parallel_point; for i in 0..original_delta_majorminor.major.abs() { let aa_p = point / 256; From 096dc31175c40ee32d8a778749f404252f68de90 Mon Sep 17 00:00:00 2001 From: James Waples Date: Thu, 5 Dec 2024 20:59:58 +0000 Subject: [PATCH 188/188] Declare parallel Bresenham stuff outside loop --- debug-tools/examples/mul-both-axes.rs | 35 +++++++++++---------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/debug-tools/examples/mul-both-axes.rs b/debug-tools/examples/mul-both-axes.rs index dd0b68f..b006ba2 100644 --- a/debug-tools/examples/mul-both-axes.rs +++ b/debug-tools/examples/mul-both-axes.rs @@ -149,18 +149,23 @@ fn thickline( let dx = seed_delta_majorminor.major.abs(); let dy = seed_delta_majorminor.minor.abs(); + let parallel_dx = parallel_delta_majorminor.major.abs(); + let parallel_dy = parallel_delta_majorminor.minor.abs(); // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square let e_minor = -2 * dx; let e_major = 2 * dy; + let parallel_e_minor = -2 * parallel_dx; + let parallel_e_major = 2 * parallel_dy; let mut seed_line_error = 2 * dy - dx; + let mut parallel_start_error = 2 * parallel_dy - parallel_dx; let mut point = seed_line.start; let mut prev = point; let mut parallel_point = line.start; for i in 0..width { - // let p = point / 256; + let p = point / 256; // Pixel(p, Rgb888::RED).draw(display)?; @@ -190,14 +195,7 @@ fn thickline( // Draw parallel line { - let dx = parallel_delta_majorminor.major.abs(); - let dy = parallel_delta_majorminor.minor.abs(); - - // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square - let e_minor = -2 * dx; - let e_major = 2 * dy; - - let mut parallel_line_error = 2 * dy - dx; + let mut parallel_line_error = parallel_start_error; let mut point = parallel_point; for i in 0..original_delta_majorminor.major.abs() { @@ -207,11 +205,11 @@ fn thickline( if parallel_line_error > 0 { point += parallel_step_majorminor.minor; - parallel_line_error += e_minor; + parallel_line_error += parallel_e_minor; } point += parallel_step_majorminor.major; - parallel_line_error += e_major; + parallel_line_error += parallel_e_major; } } @@ -224,18 +222,13 @@ fn thickline( point += seed_step_majorminor.major; parallel_point += seed_step_majorminor.major; seed_line_error += e_major; + + prev = p; } // Draw AA line { - let dx = parallel_delta_majorminor.major.abs(); - let dy = parallel_delta_majorminor.minor.abs(); - - // http://kt8216.unixcab.org/murphy/index.html calls e_minor E_diag, and e_major E_square - let e_minor = -2 * dx; - let e_major = 2 * dy; - - let mut parallel_line_error = 2 * dy - dx; + let mut parallel_line_error = parallel_start_error; let mut point = parallel_point; for i in 0..original_delta_majorminor.major.abs() { @@ -265,11 +258,11 @@ fn thickline( if parallel_line_error > 0 { point += parallel_step_majorminor.minor; - parallel_line_error += e_minor; + parallel_line_error += parallel_e_minor; } point += parallel_step_majorminor.major; - parallel_line_error += e_major; + parallel_line_error += parallel_e_major; } }