mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-22 10:28:21 -06:00
use clipline crate
This commit is contained in:
parent
c02376b96b
commit
1e98d959db
|
@ -18,6 +18,7 @@ vecto = "0.1.0"
|
||||||
umath = "0.0.7"
|
umath = "0.0.7"
|
||||||
fr = { version = "0.1.1", package = "fer", optional = true }
|
fr = { version = "0.1.1", package = "fer", optional = true }
|
||||||
stackblur-iter = { version = "0.2.0", features = ["simd"], optional = true }
|
stackblur-iter = { version = "0.2.0", features = ["simd"], optional = true }
|
||||||
|
clipline = "0.1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
iai = { git = "https://github.com/bend-n/iai.git" }
|
iai = { git = "https://github.com/bend-n/iai.git" }
|
||||||
|
|
|
@ -2,6 +2,12 @@ use fimg::*;
|
||||||
fn tri() {
|
fn tri() {
|
||||||
let mut i: Image<_, 4> = fimg::make!(4 channels 1000 x 1000).boxed();
|
let mut i: Image<_, 4> = fimg::make!(4 channels 1000 x 1000).boxed();
|
||||||
i.tri((0., 0.), (1000., 500.), (0., 999.), [255, 255, 255, 255]);
|
i.tri((0., 0.), (1000., 500.), (0., 999.), [255, 255, 255, 255]);
|
||||||
std::hint::black_box(i);
|
iai::black_box(i);
|
||||||
}
|
}
|
||||||
iai::main!(tri);
|
fn line() {
|
||||||
|
let mut i: Image<_, 4> = fimg::make!(4 channels 500 x 750).boxed();
|
||||||
|
i.line((-50, 20), (550, 800), [255, 165, 0, 255]);
|
||||||
|
i.save("z.png");
|
||||||
|
iai::black_box(i);
|
||||||
|
}
|
||||||
|
iai::main!(tri, line);
|
||||||
|
|
|
@ -64,12 +64,12 @@ boxconv!(4 => 2);
|
||||||
boxconv!(4 => 3);
|
boxconv!(4 => 3);
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pack([r, g, b, a]: [u8; 4]) -> u32 {
|
const fn pack([r, g, b, a]: [u8; 4]) -> u32 {
|
||||||
((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
|
((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn unpack(n: u32) -> [u8; 4] {
|
const fn unpack(n: u32) -> [u8; 4] {
|
||||||
[
|
[
|
||||||
((n >> 16) & 0xFF) as u8,
|
((n >> 16) & 0xFF) as u8,
|
||||||
((n >> 8) & 0xFF) as u8,
|
((n >> 8) & 0xFF) as u8,
|
||||||
|
|
|
@ -1,146 +1,21 @@
|
||||||
//! adds a `line` function to Image
|
//! adds a `line` function to Image
|
||||||
#![allow(clippy::missing_docs_in_private_items)]
|
|
||||||
use crate::Image;
|
use crate::Image;
|
||||||
use std::iter::Iterator;
|
|
||||||
use vecto::Vec2;
|
use vecto::Vec2;
|
||||||
|
|
||||||
/// taken from [bresenham-rs](https://github.com/mbr/bresenham-rs)
|
|
||||||
pub struct Bresenham {
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
dx: i32,
|
|
||||||
dy: i32,
|
|
||||||
x1: i32,
|
|
||||||
diff: i32,
|
|
||||||
octant: Octant,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Octant(u8);
|
|
||||||
|
|
||||||
impl Octant {
|
|
||||||
#[inline]
|
|
||||||
const fn from_points(start: (i32, i32), end: (i32, i32)) -> Self {
|
|
||||||
let mut dx = end.0 - start.0;
|
|
||||||
let mut dy = end.1 - start.1;
|
|
||||||
|
|
||||||
let mut octant = 0;
|
|
||||||
|
|
||||||
if dy < 0 {
|
|
||||||
dx = -dx;
|
|
||||||
dy = -dy;
|
|
||||||
octant += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if dx < 0 {
|
|
||||||
let tmp = dx;
|
|
||||||
dx = dy;
|
|
||||||
dy = -tmp;
|
|
||||||
octant += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if dx < dy {
|
|
||||||
octant += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self(octant)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
const fn to_octant0(self, p: (i32, i32)) -> (i32, i32) {
|
|
||||||
match self.0 {
|
|
||||||
0 => (p.0, p.1),
|
|
||||||
1 => (p.1, p.0),
|
|
||||||
2 => (p.1, -p.0),
|
|
||||||
3 => (-p.0, p.1),
|
|
||||||
4 => (-p.0, -p.1),
|
|
||||||
5 => (-p.1, -p.0),
|
|
||||||
6 => (-p.1, p.0),
|
|
||||||
7 => (p.0, -p.1),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[allow(clippy::wrong_self_convention)]
|
|
||||||
fn from_octant0(self, p: (i32, i32)) -> (i32, i32) {
|
|
||||||
match self.0 {
|
|
||||||
0 => (p.0, p.1),
|
|
||||||
1 => (p.1, p.0),
|
|
||||||
2 => (-p.1, p.0),
|
|
||||||
3 => (-p.0, p.1),
|
|
||||||
4 => (-p.0, -p.1),
|
|
||||||
5 => (-p.1, -p.0),
|
|
||||||
6 => (p.1, -p.0),
|
|
||||||
7 => (p.0, -p.1),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) -> Self {
|
|
||||||
let octant = Octant::from_points(start, end);
|
|
||||||
|
|
||||||
let start = octant.to_octant0(start);
|
|
||||||
let end = octant.to_octant0(end);
|
|
||||||
|
|
||||||
let dx = end.0 - start.0;
|
|
||||||
let dy = end.1 - start.1;
|
|
||||||
|
|
||||||
Self {
|
|
||||||
x: start.0,
|
|
||||||
y: start.1,
|
|
||||||
dy,
|
|
||||||
dx,
|
|
||||||
x1: end.0,
|
|
||||||
diff: dy - dx,
|
|
||||||
octant,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Bresenham {
|
|
||||||
type Item = (i32, i32);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.x > self.x1 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let p = (self.x, self.y);
|
|
||||||
|
|
||||||
if self.diff >= 0 {
|
|
||||||
self.y += 1;
|
|
||||||
self.diff -= self.dx;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.diff += self.dy;
|
|
||||||
|
|
||||||
// loop inc
|
|
||||||
self.x += 1;
|
|
||||||
|
|
||||||
Some(self.octant.from_octant0(p))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
/// Draw a line from point a to point b.
|
/// Draw a line from point a to point b.
|
||||||
///
|
///
|
||||||
/// Points not in bounds will not be included.
|
/// Points not in bounds will not be included.
|
||||||
///
|
|
||||||
/// Uses [bresenshams](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm) line algorithm.
|
|
||||||
pub fn line(&mut self, a: (i32, i32), b: (i32, i32), color: [u8; CHANNELS]) {
|
pub fn line(&mut self, a: (i32, i32), b: (i32, i32), color: [u8; CHANNELS]) {
|
||||||
for (x, y) in Bresenham::new(a, b).map(|(x, y)| (x as u32, y as u32)) {
|
clipline::clipline(
|
||||||
if x < self.width() && y < self.height() {
|
((a.0 as isize, a.1 as isize), (b.0 as isize, b.1 as isize)),
|
||||||
// SAFETY: bound are checked ^
|
(
|
||||||
unsafe { self.set_pixel(x, y, color) };
|
(0, 0),
|
||||||
}
|
(self.width() as isize - 1, self.height() as isize - 1),
|
||||||
}
|
),
|
||||||
|
// SAFETY: clipline clips
|
||||||
|
|x, y| unsafe { self.set_pixel(x as u32, y as u32, color) },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a thick line from point a to point b.
|
/// Draw a thick line from point a to point b.
|
||||||
|
@ -185,32 +60,12 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bresenham() {
|
|
||||||
macro_rules! test_bresenham {
|
|
||||||
($a:expr, $b:expr => [$(($x:expr, $y:expr)),+]) => {{
|
|
||||||
let mut bi = Bresenham::new($a, $b);
|
|
||||||
$(assert_eq!(bi.next(), Some(($x, $y)));)+
|
|
||||||
assert_eq!(bi.next(), None);
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
test_bresenham!((6, 4), (0, 1) => [(6, 4), (5, 4), (4, 3), (3, 3), (2, 2), (1, 2), (0, 1)]);
|
|
||||||
test_bresenham!((2, 3), (2, 6) => [(2, 3), (2, 4), (2, 5), (2, 6)]);
|
|
||||||
test_bresenham!((2, 3), (5, 3) => [(2, 3), (3, 3), (4, 3), (5, 3)]);
|
|
||||||
test_bresenham!((0, 1), (6, 4) => [(0, 1), (1, 1), (2, 2), (3, 2), (4, 3), (5, 3), (6, 4)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn line() {
|
fn line() {
|
||||||
let mut a = Image::build(5, 5).alloc();
|
let mut a = Image::build(5, 5).alloc();
|
||||||
a.as_mut().line((0, 1), (6, 4), [255]);
|
a.as_mut().line((0, 1), (6, 4), [255]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
a.buffer,
|
a.buffer,
|
||||||
b"\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00"
|
b"\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
/// # use fimg::Image;
|
/// # use fimg::Image;
|
||||||
/// let mut i = Image::alloc(10, 10);
|
/// let mut i = Image::alloc(10, 10);
|
||||||
/// i.points(&[(1, 8), (3, 1), (8, 1), (6, 6), (8, 8), (1, 8)], [255]);
|
/// 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");
|
/// # 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\x00\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\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\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]) {
|
pub fn points(&mut self, poly: &[(i32, i32)], c: [u8; CHANNELS]) {
|
||||||
if poly.len() <= 1 {
|
if poly.len() <= 1 {
|
||||||
|
@ -110,7 +110,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
match sides {
|
match sides {
|
||||||
3 => {
|
3 => {
|
||||||
let space = TAU / 3.0;
|
let space = TAU / 3.0;
|
||||||
self.tri(
|
self.tri::<f32>(
|
||||||
trans(space + rotation) + pos,
|
trans(space + rotation) + pos,
|
||||||
trans(rotation) + pos,
|
trans(rotation) + pos,
|
||||||
trans(madd(space, 2.0, rotation)) + pos,
|
trans(madd(space, 2.0, rotation)) + pos,
|
||||||
|
@ -132,7 +132,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
if sides % 2 != 0 && sides > 4 {
|
if sides % 2 != 0 && sides > 4 {
|
||||||
let i = (sides - 1) as f32;
|
let i = (sides - 1) as f32;
|
||||||
// the missing piece
|
// the missing piece
|
||||||
self.tri(
|
self.tri::<f32>(
|
||||||
pos,
|
pos,
|
||||||
trans(madd(space, i, rotation)) + pos,
|
trans(madd(space, i, rotation)) + pos,
|
||||||
trans(madd(space, i + 1., rotation)) + pos,
|
trans(madd(space, i + 1., rotation)) + pos,
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue