xenobalanus/src/main.rs

117 lines
5.3 KiB
Rust
Raw Normal View History

2024-03-17 14:46:15 +02:00
use geo::{Point, Polygon, LineString, EuclideanDistance, Area};
use std::collections::{HashMap, HashSet};
use std::cmp::{min, max};
use rand::Rng;
2024-03-17 16:36:07 +02:00
use rayon::prelude::*;
2024-03-17 14:46:15 +02:00
use delaunator::{triangulate, Point as DelaunatorPoint};
pub fn random_points(center: (f32, f32), radius: f32, num_points: usize) -> Vec<Point<f32>> {
let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
let mut points: Vec<Point<f32>> = Vec::with_capacity(num_points);
for _ in 0..num_points {
// Generate a random angle between 0 and 2*PI.
let angle: f32 = rng.gen_range(0.0..(2.0 * std::f32::consts::PI));
// Generate a random radius to ensure uniform distribution within the circle.
let r: f32 = (rng.gen_range(0.0..=1.0) as f32).sqrt() * radius;
// Calculate x and y coordinates based on the random angle and radius.
let x: f32 = center.0 + r * angle.cos();
let y: f32 = center.1 + r * angle.sin();
// Add the generated point to the points vector.
points.push(Point::new(x, y));
}
points
}
2024-03-17 16:36:07 +02:00
pub fn delaunay(points: &Vec<Point<f32>>) -> Vec<usize> {
2024-03-17 14:46:15 +02:00
// Convert geo::Point<f32> to delaunator::Point for triangulation
let delaunator_points: Vec<DelaunatorPoint> = points.iter()
.map(|point: &Point<f32>| DelaunatorPoint { x: point.x() as f64, y: point.y() as f64 })
.collect();
// Perform Delaunay triangulation
let result: delaunator::Triangulation = triangulate(&delaunator_points);
// Return the indices of points in the triangles
result.triangles
}
2024-03-17 16:36:07 +02:00
fn preprocess(points: &[Point<f32>], triangles: &[usize]) -> (
HashMap<usize, HashSet<usize>>,
HashMap<usize, f32>,
HashMap<(usize, usize), HashSet<usize>>,
HashMap<(usize, usize), f32>,
HashMap<usize, HashSet<usize>>,
) {
// Process each set of triangle indices in parallel
let triangle_calculation: Vec<_> = triangles.par_chunks(3).map(|tri_idx| {
let point_a: Point<f32> = points[tri_idx[0]];
let point_b: Point<f32> = points[tri_idx[1]];
let point_c: Point<f32> = points[tri_idx[2]];
// Calculate the lengths of each edge and pair them with their vertex indices
let edges_with_lengths: [((usize, usize), f32); 3] = [
2024-03-17 14:46:15 +02:00
((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)),
];
2024-03-17 16:36:07 +02:00
// 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<usize> = [edges_sorted[0].0 .0, edges_sorted[0].0 .1].iter().cloned().collect::<HashSet<_>>();
2024-03-17 14:46:15 +02:00
2024-03-17 16:36:07 +02:00
// Collect 'area_map' with area for each triangle
let poly: Polygon<f32> = 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()),
]), vec![]);
let area: f32 = poly.unsigned_area();
// Generate the node connections
let mut node_connections: HashMap<usize, HashSet<usize>> = HashMap::new();
for &idx in tri_idx.iter() {
let connected_nodes: HashSet<usize> = tri_idx.iter().filter(|&&x| x != idx).cloned().collect::<HashSet<_>>();
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<usize, HashSet<usize>> = HashMap::new(); // terminal edge for each triangle
let mut area_map: HashMap<usize, f32> = HashMap::new(); // area for each triangle
let mut wing_map: HashMap<(usize, usize), HashSet<usize>> = 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<usize, HashSet<usize>> = 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);
2024-03-17 14:46:15 +02:00
for &(edge, length) in &edges_sorted {
2024-03-17 16:36:07 +02:00
wing_map.entry(edge).or_insert_with(HashSet::new).insert(triangle_index);
2024-03-17 14:46:15 +02:00
edge_map.insert(edge, length);
}
2024-03-17 16:36:07 +02:00
for (idx, connections) in node_connections {
node_map.entry(idx).or_insert_with(HashSet::new).extend(connections);
2024-03-17 14:46:15 +02:00
}
}
2024-03-17 16:36:07 +02:00
(terminal_map, area_map, wing_map, edge_map, node_map)
2024-03-17 14:46:15 +02:00
}
fn main() {
2024-03-17 16:36:07 +02:00
let points: Vec<Point<f32>> = random_points((0.0, 0.0), 300.0, 2000);
let triangles: Vec<usize> = delaunay(&points);
let result: (HashMap<usize, HashSet<usize>>, HashMap<usize, f32>, HashMap<(usize, usize), HashSet<usize>>, HashMap<(usize, usize), f32>, HashMap<usize, HashSet<usize>>) = preprocess(&points, &triangles);
println!("{:?}", result);
2024-03-17 14:46:15 +02:00
}