Skip to content

Commit

Permalink
Merge pull request #7 from nxsaken/release-0.1.3
Browse files Browse the repository at this point in the history
Release 0.1.3
  • Loading branch information
nxsaken committed Dec 7, 2023
2 parents 4f43ed4 + 42cd927 commit d21d939
Show file tree
Hide file tree
Showing 7 changed files with 839 additions and 330 deletions.
210 changes: 100 additions & 110 deletions BENCHMARKS.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "clipline"
version = "0.1.2"
version = "0.1.3"
authors = ["Nurzhan Sakén <nurzhan.sakenov@gmail.com>"]
edition = "2021"
description = "Efficient scan conversion (rasterization) of line segments with clipping to a rectangular window."
readme = "README.md"
repository = "https://github.com/nxsaken/clipline/"
license = "MIT OR Apache-2.0"
keywords = ["graphics", "clipping", "line", "bresenham", "rasterization"]
categories = ["graphics", "rendering", "algorithms"]
categories = ["graphics", "rendering", "algorithms", "game-development", "rendering"]
include = [
"**/*.rs",
"Cargo.toml",
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ clipping algorithms like [Cohen-Sutherland](https://en.wikipedia.org/wiki/Cohen%
[Benchmarks are available here](BENCHMARKS.md). I used [criterion.rs](https://github.com/bheisler/criterion.rs) to
compare
`clipline` to two
popular line drawing crates – `bresenham` and `line_drawing`, on a variety of
clipping window sizes, line orientations and counts.
popular line drawing crates – `bresenham` and `line_drawing`, by "drawing" 256 lines of varying
clipping window sizes and line orientations.

In practice, `bresenham` and `line_drawing` will require bounds checks when indexing into a frame buffer, hence the difference between the `draw_pixel_checked` and `draw_pixel_unchecked` functions.

## Installation

To use `clipline`, add it to your `Cargo.toml` file:

```toml
[dependencies]
clipline = "0.1.2"
clipline = "0.1.3"
```

## Usage
Expand Down
118 changes: 59 additions & 59 deletions benches/bresenham_comparison.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fn clipline_c(line: Line, clip: Rect) {
}

fn bench_lines(c: &mut Criterion) {
let cases = [1, 8, 32].iter().flat_map(|mult| {
let cases = [1, 4, 8, 16].iter().flat_map(|mult| {
let (w, h) = (160 * mult, 160 * mult);
[
(
Expand Down Expand Up @@ -87,64 +87,64 @@ fn bench_lines(c: &mut Criterion) {
});
for (case_name, clip_size, line) in cases {
let mut group = c.benchmark_group(case_name);
for num_lines in [1, 1024, 8192, 32768] {
group.throughput(Throughput::Elements(num_lines));
group.bench_with_input(
BenchmarkId::new("bresenham", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
line_a(line, ((0, 0), clip_size));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("line_drawing", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
line_b(line, ((0, 0), clip_size));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("clipline(fn)", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
clipline_a(line, ((0, 0), clip_size));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("Clipline(iter)", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
clipline_b(line, ((0, 0), clip_size));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("Clipline(match-iter)", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
clipline_c(line, ((0, 0), clip_size));
}
});
},
);
}
let num_lines = 256;
group.throughput(Throughput::Elements(num_lines));
group.bench_with_input(
BenchmarkId::new("bresenham", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
line_a(line, ((0, 0), clip_size));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("line_drawing", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
line_b(line, ((0, 0), clip_size));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("clipline(fn)", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
clipline_a(line, ((0, 0), clip_size));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("Clipline(iter)", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
clipline_b(line, ((0, 0), clip_size));
}
});
},
);
group.bench_with_input(
BenchmarkId::new("Clipline(match-iter)", num_lines),
&num_lines,
|b, &num_lines| {
b.iter(|| {
for _ in 0..num_lines {
clipline_c(line, ((0, 0), clip_size));
}
});
},
);

group.finish()
}
}
Expand Down
63 changes: 55 additions & 8 deletions src/func.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::util::{
bresenham_step, clip_rect_entry, clip_rect_exit, destandardize, horizontal_line, standardize,
vertical_line, Point,
vertical_line, Constant, Point,
};
use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Range, Rem, Sub, SubAssign};

/// Performs scan conversion of a line segment using Bresenham's algorithm,
/// while clipping it to a specified rectangle.
Expand Down Expand Up @@ -53,13 +54,27 @@ use crate::util::{
///
/// This is slightly more optimized than the iterator version, but uses internal iteration. Unlike
/// the iterator version, vertical and horizontal lines will always be traversed in an ascending order.
pub fn clipline<F>(
line: (Point, Point),
clip_rect: (Point, Point),
#[allow(private_bounds)]
pub fn clipline<T, F>(
line: (Point<T>, Point<T>),
clip_rect: (Point<T>, Point<T>),
mut pixel_op: F,
) -> Option<(Point, Point)>
) -> Option<(Point<T>, Point<T>)>
where
F: FnMut(isize, isize),
T: Copy
+ Ord
+ Neg<Output = T>
+ Add<Output = T>
+ AddAssign
+ Sub<Output = T>
+ SubAssign
+ Mul<Output = T>
+ Div<Output = T>
+ Rem<Output = T>
+ MulAssign
+ Constant<Output = T>,
Range<T>: Iterator<Item = T>,
F: FnMut(T, T),
{
let ((x1, y1), (x2, y2)) = line;
let ((wx1, wy1), (wx2, wy2)) = clip_rect;
Expand All @@ -83,8 +98,7 @@ where
let dx = x2 - x1;
let dy = y2 - y1;

let (dx2, dy2) = (2 * dx, 2 * dy);

let (dx2, dy2) = (T::TWO * dx, T::TWO * dy);
if dx >= dy {
let (yd, xd, mut err) = clip_rect_entry(y1, x1, wy1, wy2, wx1, wx2, dx, dy2, dx2)?;
let term = clip_rect_exit(y1, y2, x1, x2, wy2, dx, dy2, dx2);
Expand Down Expand Up @@ -344,4 +358,37 @@ mod tests {
let clipped_line = clipline(line, clip_rect, |_, _| {});
assert_eq!(clipped_line, Some(((4, 3), (4, 8))),);
}

#[test]
fn test_all_signed_integers() {
let points: [(isize, isize); 2] = [(0, 0), (1, 1)];
fn assert(
points: [(isize, isize); 2],
x: impl TryInto<isize> + Sized,
y: impl TryInto<isize> + Sized,
) {
assert!(points.contains(&(
x.try_into().unwrap_or_else(|_| unreachable!()),
y.try_into().unwrap_or_else(|_| unreachable!())
)))
}
clipline::<i8, _>(((0, 0), (1, 1)), ((0, 0), (1, 1)), |x, y| {
assert(points, x, y)
});
clipline::<i16, _>(((0, 0), (1, 1)), ((0, 0), (1, 1)), |x, y| {
assert(points, x, y)
});
clipline::<i32, _>(((0, 0), (1, 1)), ((0, 0), (1, 1)), |x, y| {
assert(points, x, y)
});
clipline::<i64, _>(((0, 0), (1, 1)), ((0, 0), (1, 1)), |x, y| {
assert(points, x, y)
});
clipline::<i128, _>(((0, 0), (1, 1)), ((0, 0), (1, 1)), |x, y| {
assert(points, x, y)
});
clipline::<isize, _>(((0, 0), (1, 1)), ((0, 0), (1, 1)), |x, y| {
assert(points, x, y)
});
}
}
Loading

0 comments on commit d21d939

Please sign in to comment.