diff --git a/Cargo.lock b/Cargo.lock index cd7e461..893b4c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,31 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + [[package]] name = "delaunator" version = "1.0.2" @@ -253,6 +278,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "robust" version = "0.2.3" @@ -338,6 +383,7 @@ dependencies = [ "delaunator", "geo", "rand", + "rayon", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 01a3feb..69e3a2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" delaunator = "1.0.2" geo = "0.28.0" rand = "0.8.5" +rayon = "1.9.0" diff --git a/src/main.rs b/src/main.rs index 8cfb0a5..28ef627 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use geo::{Point, Polygon, LineString, EuclideanDistance, Area}; use std::collections::{HashMap, HashSet}; use std::cmp::{min, max}; use rand::Rng; +use rayon::prelude::*; use delaunator::{triangulate, Point as DelaunatorPoint}; pub fn random_points(center: (f32, f32), radius: f32, num_points: usize) -> Vec> { @@ -23,7 +24,7 @@ pub fn random_points(center: (f32, f32), radius: f32, num_points: usize) -> Vec< points } -pub fn delaunay(points: Vec>) -> Vec { +pub fn delaunay(points: &Vec>) -> Vec { // Convert geo::Point to delaunator::Point for triangulation let delaunator_points: Vec = points.iter() .map(|point: &Point| DelaunatorPoint { x: point.x() as f64, y: point.y() as f64 }) @@ -36,55 +37,80 @@ pub fn delaunay(points: Vec>) -> Vec { result.triangles } -fn preprocess(dots: Vec<(f32, f32)>, triangles: Vec) -> (Vec>, HashMap>, HashMap, HashMap<(usize, usize), HashSet>, HashMap<(usize, usize), f32>, HashMap>) { - let points: Vec> = dots.into_iter().map(|(x, y)| Point::new(x, y)).collect(); +fn preprocess(points: &[Point], triangles: &[usize]) -> ( + HashMap>, + HashMap, + HashMap<(usize, usize), HashSet>, + HashMap<(usize, usize), f32>, + HashMap>, +) { + // Process each set of triangle indices in parallel + let triangle_calculation: Vec<_> = triangles.par_chunks(3).map(|tri_idx| { + let point_a: Point = points[tri_idx[0]]; + let point_b: Point = points[tri_idx[1]]; + let point_c: Point = points[tri_idx[2]]; - let mut wing_map: HashMap<(usize, usize), HashSet> = HashMap::new(); - let mut node_map: HashMap> = HashMap::new(); - let mut edge_map: HashMap<(usize, usize), f32> = HashMap::new(); - let mut terminal_map: HashMap> = HashMap::new(); - let mut area_map: HashMap = HashMap::new(); - - for i in (0..triangles.len()).step_by(3) { - let tri_idx = &triangles[i..i + 3]; - let point_a = points[tri_idx[0]]; - let point_b = points[tri_idx[1]]; - let point_c = points[tri_idx[2]]; - - let edges_with_lengths = [ + // Calculate the lengths of each edge and pair them with their vertex indices + let edges_with_lengths: [((usize, usize), f32); 3] = [ ((min(tri_idx[0], tri_idx[1]), max(tri_idx[0], tri_idx[1])), point_a.euclidean_distance(&point_b)), ((min(tri_idx[1], tri_idx[2]), max(tri_idx[1], tri_idx[2])), point_b.euclidean_distance(&point_c)), ((min(tri_idx[2], tri_idx[0]), max(tri_idx[2], tri_idx[0])), point_c.euclidean_distance(&point_a)), ]; - let mut edges_sorted = edges_with_lengths.to_vec(); - edges_sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + // Sort edges by length to ensure the longest edge is first + let mut edges_sorted: Vec<((usize, usize), f32)> = edges_with_lengths.to_vec(); + edges_sorted.sort_by(|a: &((usize, usize), f32), b: &((usize, usize), f32)| b.1.partial_cmp(&a.1).unwrap()); + let terminal_edges: HashSet = [edges_sorted[0].0 .0, edges_sorted[0].0 .1].iter().cloned().collect::>(); - terminal_map.insert(i / 3, [edges_sorted[0].0 .0, edges_sorted[0].0 .1].iter().cloned().collect()); - - for &(edge, length) in &edges_sorted { - wing_map.entry(edge).or_insert_with(HashSet::new).insert(i / 3); - edge_map.insert(edge, length); - } - - for &idx in tri_idx.iter() { - node_map.entry(idx).or_insert_with(HashSet::new).extend(tri_idx.iter().filter(|&&x| x != idx).cloned()); - } - - let poly = Polygon::new(LineString::from(vec![ + // Collect 'area_map' with area for each triangle + let poly: Polygon = Polygon::new(LineString::from(vec![ (point_a.x(), point_a.y()), (point_b.x(), point_b.y()), (point_c.x(), point_c.y()), - (point_a.x(), point_a.y()), // Close the loop + (point_a.x(), point_a.y()), ]), vec![]); + let area: f32 = poly.unsigned_area(); - area_map.insert(i / 3, poly.unsigned_area()); // Correctly calculate the area + // Generate the node connections + let mut node_connections: HashMap> = HashMap::new(); + for &idx in tri_idx.iter() { + let connected_nodes: HashSet = tri_idx.iter().filter(|&&x| x != idx).cloned().collect::>(); + node_connections.insert(idx, connected_nodes); + } + + // Return all calculated data for this triangle + (tri_idx[0] / 3, terminal_edges, area, edges_sorted, node_connections) + }).collect(); + + // Initialize shared data structures + let mut terminal_map: HashMap> = HashMap::new(); // terminal edge for each triangle + let mut area_map: HashMap = HashMap::new(); // area for each triangle + let mut wing_map: HashMap<(usize, usize), HashSet> = HashMap::new(); // triangle index for each edge + let mut edge_map: HashMap<(usize, usize), f32> = HashMap::new(); // length for each edge + let mut node_map: HashMap> = HashMap::new(); // vertices connected to each vertex + + // Merge all triangle results + for (triangle_index, terminal_edges, area, edges_sorted, node_connections) in triangle_calculation { + terminal_map.insert(triangle_index, terminal_edges); + area_map.insert(triangle_index, area); + + for &(edge, length) in &edges_sorted { + wing_map.entry(edge).or_insert_with(HashSet::new).insert(triangle_index); + edge_map.insert(edge, length); + } + + for (idx, connections) in node_connections { + node_map.entry(idx).or_insert_with(HashSet::new).extend(connections); + } } - (points, terminal_map, area_map, wing_map, edge_map, node_map) + (terminal_map, area_map, wing_map, edge_map, node_map) } fn main() { - println!("Hello, world!"); + let points: Vec> = random_points((0.0, 0.0), 300.0, 2000); + let triangles: Vec = delaunay(&points); + let result: (HashMap>, HashMap, HashMap<(usize, usize), HashSet>, HashMap<(usize, usize), f32>, HashMap>) = preprocess(&points, &triangles); + println!("{:?}", result); }