-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
1,067 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
use criterion::{black_box, criterion_group, criterion_main, Criterion}; | ||
use glam::vec2; | ||
use polyanya::{Mesh, Triangulation}; | ||
|
||
fn triangulation(c: &mut Criterion) { | ||
c.bench_function(&"triangulation".to_string(), |b| { | ||
b.iter(|| { | ||
// Equivalent to the arena mesh | ||
let mut triangulation = Triangulation::from_outer_edges(vec![ | ||
vec2(1., 3.), | ||
vec2(2., 3.), | ||
vec2(2., 2.), | ||
vec2(3., 2.), | ||
vec2(3., 1.), | ||
vec2(15., 1.), | ||
vec2(15., 3.), | ||
vec2(18., 3.), | ||
vec2(18., 2.), | ||
vec2(19., 2.), | ||
vec2(19., 1.), | ||
vec2(20., 1.), | ||
vec2(20., 2.), | ||
vec2(23., 2.), | ||
vec2(23., 1.), | ||
vec2(26., 1.), | ||
vec2(26., 3.), | ||
vec2(29., 3.), | ||
vec2(29., 2.), | ||
vec2(30., 2.), | ||
vec2(30., 1.), | ||
vec2(31., 1.), | ||
vec2(31., 3.), | ||
vec2(34., 3.), | ||
vec2(34., 2.), | ||
vec2(35., 2.), | ||
vec2(35., 1.), | ||
vec2(47., 1.), | ||
vec2(47., 3.), | ||
vec2(48., 3.), | ||
vec2(48., 15.), | ||
vec2(47., 15.), | ||
vec2(47., 19.), | ||
vec2(48., 19.), | ||
vec2(48., 31.), | ||
vec2(47., 31.), | ||
vec2(47., 35.), | ||
vec2(48., 35.), | ||
vec2(48., 47.), | ||
vec2(47., 47.), | ||
vec2(47., 48.), | ||
vec2(35., 48.), | ||
vec2(35., 47.), | ||
vec2(31., 47.), | ||
vec2(31., 48.), | ||
vec2(30., 48.), | ||
vec2(30., 47.), | ||
vec2(29., 47.), | ||
vec2(29., 46.), | ||
vec2(26., 46.), | ||
vec2(26., 48.), | ||
vec2(24., 48.), | ||
vec2(24., 47.), | ||
vec2(23., 47.), | ||
vec2(23., 46.), | ||
vec2(20., 46.), | ||
vec2(20., 48.), | ||
vec2(19., 48.), | ||
vec2(19., 47.), | ||
vec2(15., 47.), | ||
vec2(15., 48.), | ||
vec2(3., 48.), | ||
vec2(3., 47.), | ||
vec2(1., 47.), | ||
vec2(1., 35.), | ||
vec2(2., 35.), | ||
vec2(2., 34.), | ||
vec2(3., 34.), | ||
vec2(3., 31.), | ||
vec2(1., 31.), | ||
vec2(1., 30.), | ||
vec2(3., 30.), | ||
vec2(3., 27.), | ||
vec2(2., 27.), | ||
vec2(2., 26.), | ||
vec2(1., 26.), | ||
vec2(1., 23.), | ||
vec2(2., 23.), | ||
vec2(2., 18.), | ||
vec2(3., 18.), | ||
vec2(3., 15.), | ||
vec2(1., 15.), | ||
]); | ||
|
||
triangulation.add_obstacle(vec![ | ||
vec2(15., 15.), | ||
vec2(19., 15.), | ||
vec2(19., 18.), | ||
vec2(18., 18.), | ||
vec2(18., 19.), | ||
vec2(15., 19.), | ||
]); | ||
triangulation.add_obstacle(vec![ | ||
vec2(31., 15.), | ||
vec2(35., 15.), | ||
vec2(35., 18.), | ||
vec2(34., 18.), | ||
vec2(34., 19.), | ||
vec2(31., 19.), | ||
]); | ||
triangulation.add_obstacle(vec![ | ||
vec2(15., 31.), | ||
vec2(19., 31.), | ||
vec2(19., 34.), | ||
vec2(18., 34.), | ||
vec2(18., 35.), | ||
vec2(15., 35.), | ||
]); | ||
triangulation.add_obstacle(vec![ | ||
vec2(31., 31.), | ||
vec2(35., 31.), | ||
vec2(35., 34.), | ||
vec2(34., 34.), | ||
vec2(34., 35.), | ||
vec2(31., 35.), | ||
]); | ||
triangulation.add_obstacle(vec![ | ||
vec2(23., 10.), | ||
vec2(23., 8.), | ||
vec2(24., 8.), | ||
vec2(24., 7.), | ||
vec2(26., 7.), | ||
vec2(26., 10.), | ||
]); | ||
let mesh: Mesh = triangulation.into(); | ||
black_box(mesh); | ||
}) | ||
}); | ||
} | ||
|
||
criterion_group!(benches, triangulation); | ||
criterion_main!(benches); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
pub mod polyanya_file; | ||
pub mod triangulation; | ||
pub mod trimesh; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
use std::collections::VecDeque; | ||
|
||
use glam::{vec2, Vec2}; | ||
use hashbrown::HashMap; | ||
use spade::{ConstrainedDelaunayTriangulation, Point2, Triangulation as SpadeTriangulation}; | ||
|
||
use crate::{ | ||
helpers::{line_intersect_segment, Vec2Helper}, | ||
Mesh, Polygon, Vertex, | ||
}; | ||
|
||
/// An helper to create a [`Mesh`] from a list of edges and obstacle, using a constrained Delaunay triangulation. | ||
#[derive(Debug, Clone)] | ||
pub struct Triangulation { | ||
edges: Vec<Vec2>, | ||
obstacles: Vec<Vec<Vec2>>, | ||
} | ||
|
||
impl Triangulation { | ||
/// Create a new triangulation from a the list of points on its outer edges. | ||
pub fn from_outer_edges(edges: Vec<Vec2>) -> Triangulation { | ||
Self { | ||
edges, | ||
obstacles: Default::default(), | ||
} | ||
} | ||
|
||
/// Add an obstacle delimited by the list of points on its edges. | ||
pub fn add_obstacle(&mut self, edges: Vec<Vec2>) { | ||
self.obstacles.push(edges); | ||
} | ||
|
||
#[inline] | ||
fn add_constraint_edges( | ||
cdt: &mut ConstrainedDelaunayTriangulation<Point2<f32>>, | ||
edges: Vec<Vec2>, | ||
) -> (Vec<(Vec2, Vec2)>, (Vec2, Vec2)) { | ||
let mut edge_iter = edges.iter().peekable(); | ||
let mut vertex_pairs = Vec::new(); | ||
let mut aabb_min = edges[0]; | ||
let mut aabb_max = edges[0]; | ||
loop { | ||
let from = edge_iter.next().unwrap(); | ||
let next = edge_iter.peek(); | ||
|
||
aabb_min = aabb_min.min(*from); | ||
aabb_max = aabb_max.max(*from); | ||
|
||
if let Some(next) = next { | ||
cdt.add_constraint_edge( | ||
Point2 { | ||
x: from.x, | ||
y: from.y, | ||
}, | ||
Point2 { | ||
x: next.x, | ||
y: next.y, | ||
}, | ||
) | ||
.unwrap(); | ||
vertex_pairs.push((*from, **next)); | ||
} else { | ||
cdt.add_constraint_edge( | ||
Point2 { | ||
x: from.x, | ||
y: from.y, | ||
}, | ||
Point2 { | ||
x: edges[0].x, | ||
y: edges[0].y, | ||
}, | ||
) | ||
.unwrap(); | ||
vertex_pairs.push((*from, edges[0])); | ||
break; | ||
} | ||
} | ||
|
||
(vertex_pairs, (aabb_min, aabb_max)) | ||
} | ||
} | ||
|
||
// Check if a point is in a polygon, first by checking its AABB, then by checking the number of times | ||
// a ray from the point to the top of the AABB intersects the polygon. | ||
#[inline] | ||
fn in_polygon(point: Vec2, edges: &Vec<(Vec2, Vec2)>, aabb: (Vec2, Vec2)) -> bool { | ||
if point.x < aabb.0.x || point.x > aabb.1.x || point.y < aabb.0.y || point.y > aabb.1.y { | ||
return false; | ||
} | ||
let mut parallel = 0; | ||
let intersect = edges | ||
.iter() | ||
.filter(|edge| { | ||
let start = point; | ||
let far = point + vec2(0.0, aabb.1.y + 1.0); | ||
if edge.0.on_segment((start, far)) && edge.1.on_segment((start, far)) { | ||
parallel += 1; | ||
} | ||
line_intersect_segment((start, far), **edge) | ||
.map(|i| i.y > start.y) | ||
.unwrap_or(false) | ||
}) | ||
.count(); | ||
(intersect - parallel) % 2 == 1 | ||
} | ||
|
||
impl From<Triangulation> for Mesh { | ||
fn from(value: Triangulation) -> Self { | ||
let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f32>>::new(); | ||
let (outer_edges, aabb_outer) = Triangulation::add_constraint_edges(&mut cdt, value.edges); | ||
|
||
let mut obstacles = vec![]; | ||
for obstacle in value.obstacles { | ||
obstacles.push(Triangulation::add_constraint_edges(&mut cdt, obstacle)); | ||
} | ||
|
||
let mut face_to_polygon = HashMap::new(); | ||
let polygons = cdt | ||
.inner_faces() | ||
.filter_map(|face| { | ||
let center = face.center(); | ||
let center = vec2(center.x, center.y); | ||
(in_polygon(center, &outer_edges, aabb_outer) | ||
&& obstacles | ||
.iter() | ||
.map(|(edges, aabb)| !in_polygon(center, edges, *aabb)) | ||
.all(|b| b)) | ||
.then(|| { | ||
face_to_polygon.insert(face.index(), face_to_polygon.len() as isize); | ||
Polygon::new( | ||
face.vertices() | ||
.iter() | ||
.map(|vertex| vertex.index() as u32) | ||
.collect(), | ||
// TODO: can this be set to the correct value | ||
false, | ||
) | ||
}) | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
let vertices = cdt | ||
.vertices() | ||
.map(|point| { | ||
let mut neighbour_polygons = point | ||
.out_edges() | ||
.map(|out_edge| { | ||
face_to_polygon | ||
.get(&out_edge.face().index()) | ||
.cloned() | ||
.unwrap_or(-1) | ||
}) | ||
.collect::<VecDeque<_>>(); | ||
while neighbour_polygons[0] == -1 { | ||
neighbour_polygons.rotate_left(1); | ||
} | ||
let mut neighbour_polygons: Vec<_> = neighbour_polygons.into(); | ||
neighbour_polygons.dedup(); | ||
let point = point.position(); | ||
Vertex::new(vec2(point.x, point.y), neighbour_polygons) | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
Mesh::new(vertices, polygons) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.