From 611739ee6dcdcb3d32c09a1675737c3c30d083c0 Mon Sep 17 00:00:00 2001 From: bendn Date: Tue, 26 Sep 2023 07:47:51 +0700 Subject: [PATCH] add `points` --- Cargo.toml | 2 +- src/drawing/line.rs | 6 ++-- src/drawing/mod.rs | 3 +- src/drawing/poly.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ 5 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 src/drawing/poly.rs diff --git a/Cargo.toml b/Cargo.toml index ac759ae..cead450 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fimg" -version = "0.4.7" +version = "0.4.8" authors = ["bend-n "] license = "MIT" edition = "2021" diff --git a/src/drawing/line.rs b/src/drawing/line.rs index 81fe162..d6453ca 100644 --- a/src/drawing/line.rs +++ b/src/drawing/line.rs @@ -45,7 +45,7 @@ impl Octant { octant += 1; } - Octant(octant) + Self(octant) } #[inline] @@ -84,7 +84,7 @@ impl Bresenham { /// Creates a new iterator. Yields intermediate points between `start` /// and `end`. Includes `start` and `end`. #[inline] - pub const fn new(start: (i32, i32), end: (i32, i32)) -> Bresenham { + pub const fn new(start: (i32, i32), end: (i32, i32)) -> Self { let octant = Octant::from_points(start, end); let start = octant.to_octant0(start); @@ -93,7 +93,7 @@ impl Bresenham { let dx = end.0 - start.0; let dy = end.1 - start.1; - Bresenham { + Self { x: start.0, y: start.1, dy, diff --git a/src/drawing/mod.rs b/src/drawing/mod.rs index 6ffdf23..87b4b88 100644 --- a/src/drawing/mod.rs +++ b/src/drawing/mod.rs @@ -1,4 +1,5 @@ -//! contains drawing operations, like {line, box, triangle} drawing +//! contains drawing operations, like {line, box, triangle, polygon} drawing mod r#box; mod line; +mod poly; mod tri; diff --git a/src/drawing/poly.rs b/src/drawing/poly.rs new file mode 100644 index 0000000..408aa47 --- /dev/null +++ b/src/drawing/poly.rs @@ -0,0 +1,74 @@ +//! draw polygons +use std::{ + cmp::{max, min}, + ops::{Deref, DerefMut}, +}; + +use crate::Image; + +impl + DerefMut, const CHANNELS: usize> Image { + /// Draws a filled polygon from a slice of points. Please close your poly. (first == last) + /// Borrowed from [imageproc](https://docs.rs/imageproc/latest/src/imageproc/drawing/polygon.rs.html#31), modified for less allocations. + /// ``` + /// # use fimg::Image; + /// let mut i = Image::alloc(10, 10); + /// i.points(&[(1, 8), (3, 1), (8, 1), (6, 6), (8, 8), (1, 8)], [255]); + /// # assert_eq!(i.buffer(), b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); + /// ``` + pub fn points(&mut self, poly: &[(i32, i32)], c: [u8; CHANNELS]) { + if poly.len() <= 1 { + return; + } + let (mut y_max, mut y_min) = poly[..poly.len() - 1] + .iter() + .fold((i32::MIN, i32::MAX), |(max, min), &(_, y)| { + (y.max(max), y.min(min)) + }); + y_min = max(0, min(y_min, self.height() as i32 - 1)); + y_max = max(0, min(y_max, self.height() as i32 - 1)); + let mut intersections = vec![]; + for y in y_min..=y_max { + for [p0, p1] in poly.array_windows::<2>() { + if p0.1 <= y && p1.1 >= y || p1.1 <= y && p0.1 >= y { + if p0.1 == p1.1 { + intersections.push(p0.0); + intersections.push(p1.0); + } else if p0.1 == y || p1.1 == y { + if p1.1 > y { + intersections.push(p0.0); + } + if p0.1 > y { + intersections.push(p1.0); + } + } else { + let fraction = (y - p0.1) as f32 / (p1.1 - p0.1) as f32; + let inter = fraction.mul_add((p1.0 - p0.0) as f32, p0.0 as f32); + intersections.push(inter.round() as i32); + } + } + } + intersections.sort_unstable(); + // SAFETY: must. + unsafe { crate::assert_unchecked!(intersections.len() % 2 == 0) }; + for &[x, y_] in intersections.array_chunks::<2>() { + let mut from = min(x, self.width() as i32); + let mut to = min(y_, self.width() as i32 - 1); + if from < self.width() as i32 && to >= 0 { + // check bounds + from = max(0, from); + to = max(0, to); + + for x in from..=to { + // SAFETY: bounds are checked + unsafe { self.set_pixel(x as u32, y as u32, c) }; + } + } + } + intersections.clear(); + } + + for &[(x1, y1), (x2, y2)] in poly.array_windows::<2>() { + self.line((x1, y1), (x2, y2), c); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 3b62765..4d31ef3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ slice_as_chunks, unchecked_math, portable_simd, + array_windows, const_option, array_chunks, test @@ -19,8 +20,10 @@ clippy::undocumented_unsafe_blocks, clippy::missing_const_for_fn, clippy::missing_safety_doc, + clippy::suboptimal_flops, unsafe_op_in_unsafe_fn, clippy::dbg_macro, + clippy::use_self, missing_docs )] #![allow(clippy::zero_prefixed_literal, incomplete_features)]