5个不稳定版本
0.3.0 | 2024年8月11日 |
---|---|
0.2.0 | 2023年12月18日 |
0.1.3 |
|
0.1.2 | 2023年11月2日 |
0.1.1 | 2023年10月30日 |
#14 in 渲染
282 每月下载量
用于 7 个crate(2直接)
135KB
2.5K SLoC
clipline
使用像素完美的裁剪高效渲染线段。
概述
- 提供裁剪和未裁剪的渲染线段迭代器。
- 消除边界检查:裁剪的线段保证在区域内。
- 保证裁剪的线段与其未裁剪版本匹配。
- 支持大多数大小的有符号和无符号整数坐标。
- 仅使用整数算术。
- 防止溢出和除以零,禁止
clippy::arithmetic_side_effects
。 - 在底层数值类型的整个域上定义迭代器。
- 可在
const
上下文和#![no_std]
环境中使用。
用法
将 clipline
添加到 Cargo.toml
[dependencies]
clipline = "0.3.0"
功能标志
octant_64
- 为所有目标启用
Octant
和AnyOctant
在i64
/u64
上,以及为64位目标在isize
/usize
上。 - 仅在使用完整的64位范围时使用此标志,因为
Octant
将使用u128
和i128
进行某些计算。
- 为所有目标启用
try_fold
,is_empty
(nightly-only)- 启用优化的
Iterator::try_fold
和ExactSizeIterator::is_empty
实现。
- 启用优化的
示例
use clipline::{AnyOctant, Clip, Diagonal0, Point};
/// Width of the pixel buffer.
const WIDTH: usize = 64;
/// Height of the pixel buffer.
const HEIGHT: usize = 48;
/// Pixel color value.
const RGBA: u32 = 0xFFFFFFFF;
/// A function that operates on a single pixel in a pixel buffer.
///
/// ## Safety
/// `(x, y)` must be inside the `buffer`.
unsafe fn draw(buffer: &mut [u32], (x, y): Point<i8>, rgba: u32) {
let index = y as usize * WIDTH + x as usize;
debug_assert!(index < buffer.len());
*buffer.get_unchecked_mut(index) = rgba;
}
fn main() {
let mut buffer = [0_u32; WIDTH * HEIGHT];
// The clipping region is closed/inclusive, thus 1 needs to be subtracted from the size.
let clip = Clip::<i8>::new((0, 0), (WIDTH as i8 - 1, HEIGHT as i8 - 1)).unwrap();
// `Clip` has convenience methods for the general iterators.
clip.any_octant((-128, -100), (100, 80))
// None if the line segment is completely invisible.
// You might want to handle that case differently.
.unwrap()
// clipped to [(0, 1), ..., (58, 47)]
.for_each(|xy| {
// SAFETY: (x, y) has been clipped to the buffer.
unsafe { draw(&mut buffer, xy, RGBA) }
});
// Alternatively, use the iterator constructors.
AnyOctant::<i8>::clip((12, 0), (87, 23), &clip)
.into_iter()
.flatten()
// clipped to [(12, 0), ..., (63, 16)]
.for_each(|xy| {
// SAFETY: (x, y) has been clipped to the buffer.
unsafe { draw(&mut buffer, xy, RGBA) }
});
// Horizontal and vertical line segments.
clip.axis_0(32, 76, -23)
.unwrap()
// clipped to [(63, 32), ..., (0, 32)]
.for_each(|xy| {
// SAFETY: (x, y) has been clipped to the buffer.
unsafe { draw(&mut buffer, xy, RGBA) }
});
clip.axis_1(32, -23, 76)
.unwrap()
// clipped to [(32, 0), ..., (32, 47)]
.for_each(|xy| {
// SAFETY: (x, y) has been clipped to the buffer.
unsafe { draw(&mut buffer, xy, RGBA) }
});
// Unclipped iterators are also available.
// (-2, -2) -> (12, 12) is covered by Diagonal0, we can construct it directly.
Diagonal0::<i8>::new((-2, -2), (12, 12))
.unwrap()
// Need to check every pixel to avoid going out of bounds.
.filter(|&xy| clip.point(xy))
.for_each(|xy| {
// SAFETY: (x, y) is inside the buffer.
unsafe { draw(&mut buffer, xy, RGBA) }
});
}
限制
- 为了支持在
const
上下文中的使用,类型必须为每个支持的数值类型都有一个固有的实现,而不是依赖于特质。这一点加上 Rust 对函数重载的支持不足,意味着数值类型参数必须始终指定。 - 目前,只能迭代半开线段。这允许为所有类型实现
ExactSizeIterator
。包含迭代器在 #1 中跟踪。
基准测试
divan
用于 基准测试clipline
的不同版本,以及line_drawing
。使用cargo bench
运行基准测试。- 实际上,对于未裁剪的线段,在索引到网格时需要进行边界检查,因此
draw_pixel_checked
和draw_pixel_unchecked
函数之间存在差异。
参考文献
clipline
启发于以下论文
- 通过线编码实现快速二维线裁剪算法,Mark S. Sobkow,Paul Pospisil,Yee-Hong Yang,1987。
- 参数线裁剪的新方法,Michael Dörr,1990。
- 带有内置裁剪的 Bresenham 线生成算法,Yevgeny P. Kuzmin,1995。