Rust多边形三角剖分库earcut的使用,earcut提供高效的2D多边形三角化算法实现
Rust多边形三角剖分库earcut的使用
earcut-rs是一个Rust实现的2D多边形三角剖分库,它是mapbox/earcut的Rust移植版本。
特性
- 基于最新的earcut 3.0.1版本
- 设计上避免了不必要的内存分配,内部缓冲区和输出索引向量可以在多个三角剖分中重复使用
- (实验性)提供了额外的
utils3d
模块,可以将3D共面多边形旋转到2D平面后再进行三角剖分 - 采用ISC许可证
性能基准
在Macbook Pro (M1 Pro)上的测试结果:
多边形 | earcut.hpp | earcut-rs (0.4.3) | earcutr (0.4.3) |
---|---|---|---|
bad_hole | 3.574 µs | 3.712 µs | 4.415 µs |
building | 397 ns | 180 ns | 604 ns |
degenerate | 142 ns | 44 ns | 206 ns |
dude | 5.061 µs | 6.135 µs | 8.096 µs |
empty_square | 195 ns | 74 ns | 331 ns |
water | 459.6 µs | 582.5 µs | 801.3 µs |
water2 | 334.1 µs | 391.7 µs | 450.3 µs |
water3 | 13.12 µs | 18.71 µs | 23.46 µs |
water3b | 1.340 µs | 1.334 µs | 2.165 µs |
water4 | 81.48 µs | 110.6 µs | 154.1 µs |
water_huge | 6.906 ms | 10.44 ms | 10.90 ms |
water_huge2 | 15.38 ms | 21.97 ms | 22.35 ms |
安装
在项目目录中运行以下Cargo命令:
cargo add earcut
或在Cargo.toml中添加:
earcut = "0.4.4"
使用示例
use earcut::earcut;
fn main() {
// 定义多边形顶点数据
let vertices = vec![
// 外轮廓
0.0, 0.0, // 点0
100.0, 0.0, // 点1
100.0, 100.0, // 点2
0.0, 100.0, // 点3
// 孔洞
20.0, 20.0, // 点4
80.0, 20.0, // 点5
80.0, 80.0, // 点6
20.0, 80.0, // 点7
];
// 定义轮廓和孔洞的索引范围
// 第一个元素是外轮廓,后面是孔洞
let hole_indices = vec![4];
// 每个顶点有2个坐标(x,y)
let dims = 2;
// 执行三角剖分
let triangles = earcut(&vertices, &hole_indices, dims).unwrap();
// 输出三角形索引
println!("Triangles: {:?}", triangles);
// 三角形索引可以用于绘制三角形
// 例如:
// for i in 0..triangles.len() / 3 {
// let i0 = triangles[i * 3];
// let i1 = triangles[i * 3 + 1];
// let i2 = triangles[i * 3 + 2];
// // 绘制三角形(i0, i1, i2)
// }
}
3D多边形剖分示例
use earcut::{earcut, utils3d};
fn main() {
// 3D共面多边形顶点
let vertices = vec![
// 3D坐标
0.0, 0.0, 0.0,
100.0, 0.0, 0.0,
100.0, 100.0, 0.0,
0.0, 100.0, 0.0,
];
// 将3D多边形投影到2D平面
let (vertices_2d, hole_indices, dims) = utils3d::flatten(&vertices);
// 执行三角剖分
let triangles = earcut(&vertices_2d, &hole_indices, dims).unwrap();
println!("Triangles: {:?}", triangles);
}
完整示例DEMO
下面是一个完整的示例,展示如何使用earcut库处理带孔洞的多边形:
use earcut::earcut;
fn main() {
// 定义复杂多边形顶点数据(包含多个孔洞)
let vertices = vec![
// 外轮廓 - 矩形
0.0, 0.0, // 点0
200.0, 0.0, // 点1
200.0, 200.0,// 点2
0.0, 200.0, // 点3
// 第一个孔洞 - 中心矩形
50.0, 50.0, // 点4
150.0, 50.0, // 点5
150.0, 150.0,// 点6
50.0, 150.0,// 点7
// 第二个孔洞 - 右上角三角形
180.0, 180.0,// 点8
190.0, 180.0,// 点9
190.0, 190.0,// 点10
// 第三个孔洞 - 左下角圆形(近似)
30.0, 30.0, // 点11
25.0, 40.0, // 点12
20.0, 30.0, // 点13
25.0, 20.0, // 点14
];
// 定义轮廓和孔洞的起始索引
// 第一个元素是外轮廓顶点数,后面是每个孔洞的顶点数
let hole_indices = vec![4, 8, 11];
// 每个顶点有2个坐标(x,y)
let dims = 2;
// 执行三角剖分
let triangles = earcut(&vertices, &hole_indices, dims).unwrap();
// 输出三角形索引
println!("生成的三角形索引:");
for i in 0..triangles.len() / 3 {
let i0 = triangles[i * 3];
let i1 = triangles[i * 3 + 1];
let i2 = triangles[i * 3 + 2];
println!("三角形 {}: ({}, {}, {})", i, i0, i1, i2);
}
// 实际应用中可以这样使用结果:
let vertex_count = vertices.len() / dims;
println!("顶点总数: {}", vertex_count);
println!("生成的三角形数: {}", triangles.len() / 3);
}
作者
Taku Fukada (@ciscorn)
许可证
ISC
1 回复
Rust多边形三角剖分库earcut的使用指南
介绍
earcut
是一个高效的2D多边形三角剖分库,特别适合处理带有孔洞的复杂多边形。它基于"耳切法"(ear clipping)算法实现,能够将任意简单多边形(包括带孔洞的多边形)分解为三角形集合。
这个库的主要特点包括:
- 纯Rust实现
- 无外部依赖
- 处理带孔洞的多边形
- 高效性能
- 输出三角形索引
使用方法
添加依赖
首先在Cargo.toml
中添加依赖:
[dependencies]
earcut = "0.4"
基本用法
use earcut::earcut;
fn main() {
// 定义多边形顶点坐标(扁平化的Vec<f64>)
// 格式: [x0, y0, x1, y1, x2, y2, ...]
let vertices = vec![
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
];
// 定义孔洞的起始索引(如果没有孔洞则为空vec)
let holes = Vec::<usize>::new();
// 每个顶点的维度(这里是2D)
let dims = 2;
// 执行三角剖分
let triangles = earcut(&vertices, &holes, dims).unwrap();
println!("三角形索引: {:?}", triangles);
// 输出可能是: [0, 1, 2, 0, 2, 3] 表示两个三角形
}
处理带孔洞的多边形
use earcut::earcut;
fn main() {
// 外轮廓顶点(顺时针)
let vertices = vec![
// 外矩形
0.0, 0.0,
3.0, 0.0,
3.0, 3.0,
0.0, 3.0,
// 内矩形(孔洞,逆时针)
1.0, 1.0,
1.0, 2.0,
2.0, 2.0,
2.0, 1.0,
];
// 孔洞的起始索引(这里是第4个顶点开始)
let holes = vec![4];
let dims = 2;
let triangles = earcut(&vertices, &holes, dims).unwrap();
println!("带孔洞的三角形索引: {:?}", triangles);
}
处理更复杂的多边形
use earcut::earcut;
fn main() {
// 一个更复杂的多边形,包含多个孔洞
let vertices = vec![
// 外轮廓(五边形)
0.0, 0.0,
5.0, 0.0,
5.0, 3.0,
3.0, 5.0,
0.0, 5.0,
// 第一个孔洞(三角形)
1.0, 1.0,
2.0, 1.0,
1.5, 2.0,
// 第二个孔洞(四边形)
3.0, 1.0,
4.0, 1.0,
4.0, 2.0,
3.0, 2.0,
];
// 两个孔洞的起始索引
let holes = vec![5, 8];
let dims = 2;
let triangles = earcut(&vertices, &holes, dims).unwrap();
println!("复杂多边形的三角形索引: {:?}", triangles);
}
注意事项
- 顶点顺序: 外轮廓顶点应顺时针排列,孔洞顶点应逆时针排列
- 坐标系统: 使用标准的2D笛卡尔坐标系
- 性能: 对于非常复杂的多边形,可能需要考虑性能优化
- 错误处理:
earcut
函数返回Result
,记得处理可能的错误情况
高级用法
earcut
还提供了更高级的接口Earcut
结构体,可以重用分配的内存以提高性能:
use earcut::Earcut;
fn main() {
let vertices = vec![
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
];
let mut earcut = Earcut::new();
let triangles = earcut.earcut(&vertices, &[], 2).unwrap();
println!("使用Earcut结构体的结果: {:?}", triangles);
}
完整示例
下面是一个完整的示例,展示如何使用earcut
库处理一个带孔洞的复杂多边形并可视化结果:
use earcut::earcut;
use plotters::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 定义多边形顶点数据
let vertices = vec![
// 外轮廓(六边形)
0.0, 0.0,
4.0, 0.0,
6.0, 2.0,
4.0, 4.0,
0.0, 4.0,
-2.0, 2.0,
// 第一个孔洞(三角形)
1.0, 1.0,
2.0, 1.0,
1.5, 2.0,
// 第二个孔洞(四边形)
3.0, 1.0,
3.5, 1.0,
3.5, 2.0,
3.0, 2.0,
];
// 孔洞起始索引
let holes = vec![6, 9];
let dims = 2;
// 执行三角剖分
let triangles = earcut(&vertices, &holes, dims)?;
println!("生成的三角形索引: {:?}", triangles);
// 可视化结果
let root = BitMapBackend::new("triangulation.png", (800, 600)).into_drawing_area();
root.fill(&WHITE)?;
let mut chart = ChartBuilder::on(&root)
.build_cartesian_2d(-3.0..7.0, -1.0..5.0)?;
// 绘制原始多边形
chart.draw_series(LineSeries::new(
vertices.chunks(2).take(6).map(|p| (p[0], p[1])).chain(std::iter::once((vertices[0], vertices[1]))),
&BLACK,
))?;
// 绘制孔洞
chart.draw_series(LineSeries::new(
vertices.chunks(2).skip(6).take(3).map(|p| (p[0], p[1])).chain(std::iter::once((vertices[12], vertices[13]))),
&RED,
))?;
chart.draw_series(LineSeries::new(
vertices.chunks(2).skip(9).take(4).map(|p| (p[0], p[1])).chain(std::iter::once((vertices[18], vertices[19]))),
&RED,
))?;
// 绘制三角形网格
for triangle in triangles.chunks(3) {
let points: Vec<_> = triangle.iter()
.map(|&i| (vertices[i*2], vertices[i*2+1]))
.collect();
chart.draw_series(std::iter::once(Polygon::new(
points,
BLUE.mix(0.2).filled(),
)))?;
}
Ok(())
}
这个完整示例演示了:
- 定义一个复杂的六边形外轮廓和两个孔洞
- 使用
earcut
进行三角剖分 - 使用
plotters
库可视化原始多边形和生成的三角形网格 - 展示了如何处理多个孔洞的情况
要运行此示例,需要在Cargo.toml
中添加以下依赖:
[dependencies]
earcut = "0.4"
plotters = "0.3"
这个库非常适合游戏开发、地理信息系统(GIS)和任何需要多边形三角剖分的图形应用。