xenobalanus/src/lib.rs

352 lines
13 KiB
Rust
Raw Normal View History

2024-03-17 14:46:15 +02:00
use delaunator::{triangulate, Point as DelaunatorPoint};
2024-03-26 10:25:47 +02:00
use rand::Rng;
use rayon::prelude::*;
use std::cmp::{min, max};
use std::collections::{HashMap, HashSet};
2024-03-17 19:18:53 +02:00
use std::sync::{Arc, Mutex};
2024-03-27 18:45:58 +02:00
#[derive(Debug, Clone, Copy)]
pub struct Point {
x: f32,
y: f32
}
fn distance(a: Point, b: Point) -> f32 {
( (b.x - a.x).powi(2) + (b.y - a.y).powi(2) ).sqrt()
}
2024-03-17 19:18:53 +02:00
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
2024-03-26 11:59:47 +02:00
pub struct Edge(usize, usize);
2024-03-17 19:18:53 +02:00
#[derive(Debug)]
2024-03-26 11:59:47 +02:00
pub struct TriangleData {
2024-03-26 12:01:57 +02:00
pub index: usize,
pub area: Option<f32>,
pub terminal_edge: Option<Edge>,
pub vertices: Vec<usize>
2024-03-17 19:18:53 +02:00
}
2024-03-27 18:45:58 +02:00
impl TriangleData {
pub fn get_edges(&self) -> Vec<Edge> {
let mut edges = Vec::new();
if self.vertices.len() >= 3 {
for i in 0..self.vertices.len() {
let v1 = self.vertices[i];
let v2 = if i + 1 < self.vertices.len() {
self.vertices[i + 1]
} else {
self.vertices[0]
};
edges.push(if v1 < v2 { Edge(v1, v2) } else { Edge(v2, v1) });
}
}
edges
}
}
2024-03-17 19:18:53 +02:00
#[derive(Debug)]
2024-03-26 11:56:09 +02:00
pub struct GeometryData {
2024-03-26 12:01:57 +02:00
pub triangles: Vec<TriangleData>,
pub edge_to_triangles: HashMap<Edge, Vec<usize>>, // Maps an edge to triangle indices
pub edge_lengths: HashMap<Edge, f32>, // Edge lengths
pub vertex_connections: HashMap<usize, HashSet<usize>>, // Direct connections between vertices, for DTSCAN
2024-03-17 19:18:53 +02:00
}
impl GeometryData {
fn new() -> Self {
GeometryData {
triangles: Vec::new(),
edge_to_triangles: HashMap::new(),
edge_lengths: HashMap::new(),
2024-03-18 13:10:54 +02:00
vertex_connections: HashMap::new(), // Adjusted for DTSCAN
2024-03-17 19:18:53 +02:00
}
}
2024-03-27 18:45:58 +02:00
fn add_triangle(&mut self, index: usize, points: &[Point], tri_idx: &[usize], types: usize) {
let point_a: Point = points[tri_idx[0]];
let point_b: Point = points[tri_idx[1]];
let point_c: Point = points[tri_idx[2]];
2024-03-19 11:23:57 +02:00
2024-03-26 10:25:47 +02:00
let mut vertices = vec![tri_idx[0], tri_idx[1], tri_idx[2]];
vertices.sort_unstable();
2024-03-18 13:10:54 +02:00
// Temporarily store edges_with_lengths for sorting and determining the terminal_edge.
let mut edges_with_lengths_temp = [
2024-03-27 18:45:58 +02:00
(Edge(min(tri_idx[0], tri_idx[1]), max(tri_idx[0], tri_idx[1])), distance(point_a, point_b)),
(Edge(min(tri_idx[1], tri_idx[2]), max(tri_idx[1], tri_idx[2])), distance(point_b, point_c)),
(Edge(min(tri_idx[2], tri_idx[0]), max(tri_idx[2], tri_idx[0])), distance(point_c, point_a)),
2024-03-18 13:10:54 +02:00
].to_vec();
// Sort edges by length to ensure the longest edge is identified.
edges_with_lengths_temp.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
2024-03-27 18:45:58 +02:00
2024-03-18 13:48:35 +02:00
let terminal_edge: Option<Edge> = edges_with_lengths_temp.first().map(|(edge, _)| *edge);
2024-03-27 18:45:58 +02:00
2024-03-18 13:48:35 +02:00
let area: Option<f32> = if types == 0 || types == 2 {
2024-03-27 18:45:58 +02:00
let x1 = point_a.x;
let y1 = point_a.y;
let x2 = point_b.x;
let y2 = point_b.y;
let x3 = point_c.x;
let y3 = point_c.y;
// Calculate the area using the shoelace formula
let calculated_area = (x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2)).abs() / 2.0;
Some(calculated_area)
2024-03-18 11:47:30 +02:00
} else {
None
2024-03-27 18:45:58 +02:00
};
2024-03-18 11:47:30 +02:00
2024-03-18 13:10:54 +02:00
if types == 0 || types == 1 {
for &(edge, length) in &edges_with_lengths_temp {
self.vertex_connections.entry(edge.0).or_insert_with(HashSet::new).insert(edge.1);
self.vertex_connections.entry(edge.1).or_insert_with(HashSet::new).insert(edge.0);
self.edge_lengths.insert(edge, length);
self.edge_to_triangles.entry(edge).or_default().push(index);
}
2024-03-18 11:47:30 +02:00
} else {
2024-03-18 13:10:54 +02:00
// For types == 2, only update edge_lengths and edge_to_triangles.
for &(edge, length) in &edges_with_lengths_temp {
2024-03-18 11:47:30 +02:00
self.edge_lengths.insert(edge, length);
self.edge_to_triangles.entry(edge).or_default().push(index);
}
2024-03-17 19:18:53 +02:00
}
2024-03-18 11:47:30 +02:00
2024-03-18 13:10:54 +02:00
if types == 0 || types == 2 {
self.triangles.push(TriangleData {
index,
area,
2024-03-26 10:25:47 +02:00
terminal_edge,
vertices
2024-03-18 13:10:54 +02:00
});
2024-03-17 19:18:53 +02:00
}
2024-03-27 11:08:13 +02:00
}
2024-03-17 19:18:53 +02:00
}
2024-03-17 14:46:15 +02:00
2024-03-27 11:08:13 +02:00
pub struct Xenobalanus {
geometry_data: GeometryData,
2024-03-27 18:45:58 +02:00
points: Vec<Point>,
2024-03-27 11:08:13 +02:00
triangles: Vec<usize>,
}
impl Xenobalanus {
pub fn new() -> Self {
Xenobalanus {
geometry_data: GeometryData::new(),
points: Vec::new(),
triangles: Vec::new(),
}
2024-03-17 14:46:15 +02:00
}
2024-03-27 11:08:13 +02:00
pub fn points(&self) -> Vec<Vec<f32>> {
self.points.iter()
2024-03-27 18:45:58 +02:00
.map(|point| vec![point.x, point.y])
2024-03-27 11:08:13 +02:00
.collect()
}
2024-03-17 14:46:15 +02:00
2024-03-27 11:08:13 +02:00
pub fn points_flat(&self) -> Vec<f32> {
self.points.iter()
2024-03-27 18:45:58 +02:00
.flat_map(|point| vec![point.x, point.y])
2024-03-27 11:08:13 +02:00
.collect()
}
2024-03-17 14:46:15 +02:00
2024-03-27 11:08:13 +02:00
pub fn triangles(&self) -> Vec<usize> {
self.triangles.clone()
}
2024-03-17 14:46:15 +02:00
2024-03-27 11:08:13 +02:00
pub fn triangle_vertices(&self) -> Vec<Vec<usize>> {
self.triangles.chunks(3).map(|chunk| {
chunk.iter().map(|&index| index).collect()
}).collect()
}
2024-03-27 18:45:58 +02:00
pub fn triangle_coordinates(&self) -> Vec<Vec<Vec<f32>>> {
2024-03-27 11:08:13 +02:00
self.triangles.chunks(3).map(|chunk| {
2024-03-27 18:45:58 +02:00
chunk.iter().map(|&index| {
2024-03-27 11:08:13 +02:00
let point = &self.points[index];
2024-03-27 18:45:58 +02:00
vec![point.x, point.y] // Each point is represented by a Vec<f32> of its coordinates
}).collect() // Collects points of a triangle into Vec<Vec<f32>>
}).collect() // Collects all triangles into Vec<Vec<Vec<f32>>>
2024-03-27 11:08:13 +02:00
}
2024-03-17 14:46:15 +02:00
2024-03-27 11:08:13 +02:00
// Additional methods moved into GeometryProcessor, operating on self.geometry_data
pub fn random_points(&mut self, center: (f32, f32), side_length: f32, num_points: u32) {
// generate random points in a square
let min_x = center.0 - side_length / 2.0;
let max_x = center.0 + side_length / 2.0;
let min_y = center.1 - side_length / 2.0;
let max_y = center.1 + side_length / 2.0;
let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
for _ in 0..num_points {
let x = min_x + rng.gen_range(0.0..=1.0) as f32 * ( max_x - min_x);
let y: f32 = min_y + rng.gen_range(0.0..=1.0) as f32 * ( max_y - min_y);
2024-03-27 18:45:58 +02:00
self.points.push(Point {x, y});
2024-03-27 11:08:13 +02:00
}
}
2024-03-17 16:36:07 +02:00
2024-03-27 11:08:13 +02:00
pub fn delaunay(&mut self) {
2024-03-27 18:45:58 +02:00
// Convert geo::Point to delaunator::Point for triangulation
2024-03-27 11:08:13 +02:00
let delaunator_points: Vec<DelaunatorPoint> = self.points.iter()
2024-03-27 18:45:58 +02:00
.map(|point: &Point| DelaunatorPoint { x: point.x as f64, y: point.y as f64 })
2024-03-27 11:08:13 +02:00
.collect();
2024-03-17 14:46:15 +02:00
2024-03-27 11:08:13 +02:00
// Perform Delaunay triangulation
let result: delaunator::Triangulation = triangulate(&delaunator_points);
self.triangles = result.triangles
}
2024-03-17 16:36:07 +02:00
2024-03-27 11:08:13 +02:00
pub fn preprocess(&mut self, types: usize) {
let geometry_data = Arc::new(Mutex::new(GeometryData::new()));
2024-03-17 19:18:53 +02:00
2024-03-27 11:08:13 +02:00
self.triangles.par_chunks(3).enumerate().for_each(|(index, tri_idx)| {
let gd = geometry_data.clone(); // Clone Arc for use in each thread
gd.lock().unwrap().add_triangle(index, &self.points, tri_idx, types);
});
2024-03-17 19:18:53 +02:00
2024-03-27 11:08:13 +02:00
self.geometry_data = Arc::try_unwrap(geometry_data).unwrap().into_inner().unwrap()
}
2024-03-17 19:18:53 +02:00
2024-03-27 11:08:13 +02:00
pub fn delfin(
&self,
min_area: f32,
min_distance: f32,
) -> Vec<HashSet<usize>> {
let mut void_polygons: Vec<HashSet<usize>> = Vec::new();
let mut processed_triangles: HashSet<usize> = HashSet::new();
2024-03-17 19:18:53 +02:00
2024-03-27 18:45:58 +02:00
// Create a sorted list of triangles by their terminal edge length that meet the minimum distance criteria.
let mut triangles_sorted: Vec<(usize, f32)> = self.geometry_data.triangles.iter()
.filter_map(|t| t.terminal_edge.and_then(|e| self.geometry_data.edge_lengths.get(&e).map(|&l| (t.index, l))))
.filter(|&(_, length)| length >= min_distance)
.collect();
// Sort by longest edge first
triangles_sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
// Iterate through triangles starting from the one with the longest terminal edge
for (triangle_index, _) in triangles_sorted {
// Skip if already processed
2024-03-27 11:08:13 +02:00
if processed_triangles.contains(&triangle_index) {
continue;
}
2024-03-27 18:45:58 +02:00
let mut edges_to_expand: HashSet<Edge> = HashSet::new();
let mut current_set: HashSet<usize> = HashSet::new();
// Seed the initial set and edges to expand
current_set.insert(triangle_index);
// processed_triangles.insert(triangle_index);
// Get all edges of the current triangle
if let Some(edges) = self.geometry_data.triangles.get(triangle_index).map(|t| t.get_edges()) {
for edge in edges {
// Add all edges to check for neighbors to expand
edges_to_expand.insert(edge);
}
}
// Expand the set
while let Some(edge) = edges_to_expand.iter().next().cloned() {
edges_to_expand.remove(&edge);
// Get neighbor triangles for this edge
if let Some(triangles) = self.geometry_data.edge_to_triangles.get(&edge) {
// Iterate through neighbors
for &neighbor_index in triangles {
// Skip if already processed
if processed_triangles.contains(&neighbor_index) {
continue;
}
// Get neighbor triangle
if let Some(neighbor_triangle) = self.geometry_data.triangles.get(neighbor_index) {
// Get neighbor triangle's terminal edge
if let Some(neighbor_edge) = neighbor_triangle.terminal_edge {
// If neighbor's terminal edge is edge of current triangle, add to set
if neighbor_edge == edge {
current_set.insert(neighbor_index);
processed_triangles.insert(triangle_index);
processed_triangles.insert(neighbor_index);
// Add new neighbor edges to search
neighbor_triangle.get_edges().into_iter().for_each(|e| { edges_to_expand.insert(e); });
2024-03-27 11:08:13 +02:00
}
2024-03-18 11:47:30 +02:00
}
}
2024-03-17 19:18:53 +02:00
}
}
2024-03-18 11:47:30 +02:00
}
2024-03-27 11:08:13 +02:00
2024-03-27 18:45:58 +02:00
// Add the expanded set if more than one triangle
if current_set.len() > 1 {
void_polygons.push(current_set);
}
}
println!("{:#?}", void_polygons);
// Retain only those sets that meet the minimum area criteria
void_polygons.retain(|set| {
set.iter()
.filter_map(|&i| self.geometry_data.triangles[i].area)
.sum::<f32>() >= min_area
2024-03-27 11:08:13 +02:00
});
2024-03-27 18:45:58 +02:00
void_polygons
}
2024-03-17 14:46:15 +02:00
2024-03-27 11:08:13 +02:00
pub fn dtscan(
&self,
min_pts: usize,
max_closeness: f32,
) -> Vec<Vec<usize>> {
let mut clusters: Vec<Vec<usize>> = Vec::new();
let mut visited: HashSet<usize> = HashSet::new();
for (&vertex_idx, neighbors) in &self.geometry_data.vertex_connections {
if visited.contains(&vertex_idx) {
continue;
2024-03-18 13:10:54 +02:00
}
2024-03-27 11:08:13 +02:00
// Check if vertex is a core vertex based on the number of connections and edge lengths
if neighbors.len() >= min_pts && neighbors.iter().all(|&n| {
if let Some(&length) = self.geometry_data.edge_lengths.get(&Edge(min(vertex_idx, n), max(vertex_idx, n))) {
length <= max_closeness
} else {
false
2024-03-19 11:23:57 +02:00
}
2024-03-27 11:08:13 +02:00
}) {
let mut cluster: Vec<usize> = Vec::new();
let mut to_expand: Vec<usize> = vec![vertex_idx];
while let Some(current_vertex) = to_expand.pop() {
if !visited.insert(current_vertex) {
continue;
}
cluster.push(current_vertex);
// Add neighbors that are within max_closeness to to_expand
self.geometry_data.vertex_connections.get(&current_vertex).map(|neighbors: &HashSet<usize>| {
for &neighbor in neighbors {
if let Some(&length) = self.geometry_data.edge_lengths.get(&Edge(min(current_vertex, neighbor), max(current_vertex, neighbor))) {
if length <= max_closeness && !visited.contains(&neighbor) {
to_expand.push(neighbor);
}
2024-03-19 11:23:57 +02:00
}
}
2024-03-27 11:08:13 +02:00
});
}
if !cluster.is_empty() {
clusters.push(cluster); // Add the constructed cluster to the list of clusters
}
2024-03-19 11:23:57 +02:00
}
2024-03-18 13:10:54 +02:00
}
2024-03-27 11:08:13 +02:00
clusters
2024-03-18 13:10:54 +02:00
}
2024-03-19 12:14:47 +02:00
}