Skip to content

Commit

Permalink
rely on geo for polygon management
Browse files Browse the repository at this point in the history
  • Loading branch information
mockersf committed Aug 15, 2023
1 parent 916ff98 commit c7ba6dc
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 47 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ smallvec = { version = "1.9", features = ["union", "const_generics"] }
bvh2d = { version = "0.3", git = "https://github.com/mockersf/bvh2d" }
serde = { version = "1.0", features = ["derive"], optional = true }
spade = "2.2"
geo = "0.26.0"

[dev-dependencies]
criterion = "0.5"
Expand Down
65 changes: 18 additions & 47 deletions src/input/triangulation.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
use std::collections::VecDeque;

use geo::{Contains, Coord, LineString};
use glam::{vec2, Vec2};
use hashbrown::HashMap;
use spade::{ConstrainedDelaunayTriangulation, Point2, Triangulation as SpadeTriangulation};

use crate::{
helpers::{line_intersect_segment, Vec2Helper},
Mesh, Polygon, Vertex,
};
use crate::{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>>,
edges: LineString<f32>,
obstacles: Vec<LineString<f32>>,
}

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,
edges: LineString::from(edges.iter().map(|v| (v.x, v.y)).collect::<Vec<_>>()),
obstacles: Default::default(),
}
}
Expand All @@ -29,25 +27,22 @@ impl Triangulation {
///
/// Obstacles *MUST NOT* overlap.
pub fn add_obstacle(&mut self, edges: Vec<Vec2>) {
self.obstacles.push(edges);
self.obstacles.push(LineString::from(
edges.iter().map(|v| (v.x, v.y)).collect::<Vec<_>>(),
));
}

#[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();
edges: &LineString<f32>,
) {
let mut edge_iter = edges.coords().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 {
Expand Down Expand Up @@ -77,55 +72,31 @@ impl Triangulation {
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: &[(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);
Triangulation::add_constraint_edges(&mut cdt, &value.edges);
let outer = geo::Polygon::new(value.edges, vec![]);

let mut obstacles = vec![];
for obstacle in value.obstacles {
obstacles.push(Triangulation::add_constraint_edges(&mut cdt, obstacle));
Triangulation::add_constraint_edges(&mut cdt, &obstacle);
obstacles.push(geo::Polygon::new(obstacle, vec![]));
}

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)
let center = Coord::from((center.x, center.y));
(outer.contains(&center)
&& obstacles
.iter()
.map(|(edges, aabb)| !in_polygon(center, edges, *aabb))
.map(|poly| !poly.contains(&center))
.all(|b| b))
.then(|| {
face_to_polygon.insert(face.index(), face_to_polygon.len() as isize);
Expand Down

0 comments on commit c7ba6dc

Please sign in to comment.