284 lines
6.8 KiB
Rust
284 lines
6.8 KiB
Rust
use std::collections::HashSet;
|
|
use core::f32::consts::PI;
|
|
use macroquad::prelude::draw_circle_lines;
|
|
use macroquad::prelude::mouse_position;
|
|
use macroquad::prelude::is_mouse_button_pressed;
|
|
use macroquad::prelude::Vec2;
|
|
use core::ops::DerefMut;
|
|
use core::ops::Deref;
|
|
use macroquad::prelude::draw_line;
|
|
use core::hash::Hasher;
|
|
use core::hash::Hash;
|
|
use macroquad::prelude::screen_height;
|
|
use macroquad::prelude::next_frame;
|
|
use macroquad::prelude::screen_width;
|
|
use macroquad::prelude::draw_circle;
|
|
use macroquad::prelude::clear_background;
|
|
use macroquad::prelude::Color;
|
|
use macroquad::input::MouseButton;
|
|
use std::sync::Arc;
|
|
|
|
const BG: Color = Color::new(0.16863, 0.23922, 0.21176, 1.0);
|
|
const FG: Color = Color::new(0.91569, 0.71765, 0.00000, 1.0);
|
|
const DIM_FG: Color = Color::new(0.29412, 0.27451, 0.13333, 1.0);
|
|
const HIL_FG: Color = Color::new(0.25806, 0.61290, 1.09032, 1.0);
|
|
|
|
#[macroquad::main("BasicShapes")]
|
|
async fn main() {
|
|
let mut dd = DelaunayDemo::default();
|
|
loop {
|
|
if is_mouse_button_pressed(MouseButton::Left) {
|
|
dd.click(mouse_position().into())
|
|
}
|
|
dd.draw();
|
|
next_frame().await;
|
|
}
|
|
}
|
|
|
|
pub struct DelaunayDemo {
|
|
pub nodes: Vec<Arc<Node>>,
|
|
pub triangles: Vec<Triangle>,
|
|
pub edges: Vec<Edge>,
|
|
}
|
|
|
|
impl Default for DelaunayDemo {
|
|
fn default() -> Self {
|
|
let nodes: Vec<Arc<Node>> = vec![
|
|
Arc::new((0.0, 0.0).into()),
|
|
Arc::new((0.0, screen_height()).into()),
|
|
Arc::new((screen_width(), 0.0).into()),
|
|
Arc::new((screen_width(), screen_height()).into()),
|
|
];
|
|
let edges = vec![
|
|
Edge(nodes[2].clone(), nodes[0].clone()),
|
|
Edge(nodes[0].clone(), nodes[1].clone()),
|
|
Edge(nodes[1].clone(), nodes[2].clone()),
|
|
Edge(nodes[2].clone(), nodes[3].clone()),
|
|
Edge(nodes[3].clone(), nodes[1].clone()),
|
|
];
|
|
let triangles = vec![
|
|
Triangle::new(
|
|
[edges[0].clone(), edges[1].clone(), edges[2].clone()],
|
|
),
|
|
Triangle::new(
|
|
[edges[2].clone(), edges[3].clone(), edges[4].clone()],
|
|
),
|
|
];
|
|
DelaunayDemo {
|
|
nodes, edges, triangles,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct Node(Vec2);
|
|
|
|
impl Deref for Node {
|
|
type Target = Vec2;
|
|
|
|
fn deref(&self) -> &Vec2 {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl DerefMut for Node {
|
|
fn deref_mut(&mut self) -> &mut Vec2 {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl From<(f32, f32)> for Node {
|
|
fn from((x, y): (f32, f32)) -> Node {
|
|
Node(Vec2::new(x, y))
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Edge(Arc<Node>, Arc<Node>);
|
|
|
|
impl PartialEq for Edge {
|
|
fn eq(&self, other: &Edge) -> bool {
|
|
let mut our_ptrs = [
|
|
Arc::as_ptr(&self.0),
|
|
Arc::as_ptr(&self.1),
|
|
];
|
|
let mut other_ptrs = [
|
|
Arc::as_ptr(&other.0),
|
|
Arc::as_ptr(&other.1),
|
|
];
|
|
our_ptrs.sort_unstable();
|
|
other_ptrs.sort_unstable();
|
|
|
|
our_ptrs[0] == other_ptrs[0] && our_ptrs[1] == other_ptrs[1]
|
|
}
|
|
}
|
|
|
|
impl Eq for Edge {}
|
|
|
|
impl Hash for Edge {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
let mut ptrs = [
|
|
Arc::as_ptr(&self.0),
|
|
Arc::as_ptr(&self.1),
|
|
];
|
|
ptrs.sort_unstable();
|
|
ptrs.hash(state);
|
|
}
|
|
}
|
|
|
|
pub struct Triangle {
|
|
nodes: [Arc<Node>; 3],
|
|
edges: [Edge; 3],
|
|
circumcenter: Vec2,
|
|
radius: f32,
|
|
}
|
|
|
|
impl Triangle {
|
|
pub fn new(edges: [Edge; 3]) -> Triangle {
|
|
let nodes = [
|
|
edges[0].0.clone(),
|
|
edges[0].1.clone(),
|
|
if Arc::ptr_eq(&edges[1].0, &edges[0].0) || Arc::ptr_eq(&edges[1].0, &edges[0].1) {
|
|
edges[1].1.clone()
|
|
} else {
|
|
edges[1].0.clone()
|
|
}
|
|
];
|
|
let circumcenter = Self::circumcenter(**nodes[0], **nodes[1], **nodes[2]);
|
|
let radius = circumcenter.distance(**nodes[0]);
|
|
Triangle {
|
|
nodes,
|
|
edges,
|
|
circumcenter,
|
|
radius,
|
|
}
|
|
}
|
|
|
|
fn circumcenter(a: Vec2, b: Vec2, c: Vec2) -> Vec2 {
|
|
let len_a = b.distance(c);
|
|
let len_b = c.distance(a);
|
|
let len_c = a.distance(b);
|
|
|
|
let cos_a = (len_b.powf(2.0) + len_c.powf(2.0) - len_a.powf(2.0)) / (2.0 * len_b * len_c );
|
|
let cos_b = (len_c.powf(2.0) + len_a.powf(2.0) - len_b.powf(2.0)) / (2.0 * len_c * len_a );
|
|
|
|
let ang_a = f32::acos(cos_a);
|
|
let ang_b = f32::acos(cos_b);
|
|
let ang_c = PI - ang_a - ang_b;
|
|
|
|
let sin_2a = f32::sin(2.0 * ang_a);
|
|
let sin_2b = f32::sin(2.0 * ang_b);
|
|
let sin_2c = f32::sin(2.0 * ang_c);
|
|
|
|
Vec2::new(
|
|
(a.x * sin_2a + b.x * sin_2b + c.x * sin_2c) / (sin_2a + sin_2b + sin_2c),
|
|
(a.y * sin_2a + b.y * sin_2b + c.y * sin_2c) / (sin_2a + sin_2b + sin_2c),
|
|
)
|
|
}
|
|
|
|
pub fn invalidated_by_point(&self, point: Vec2) -> bool {
|
|
self.circumcenter.distance(point) < self.radius
|
|
}
|
|
}
|
|
|
|
impl DelaunayDemo {
|
|
fn draw(&self) {
|
|
clear_background(BG);
|
|
let mut danger_lines = Vec::with_capacity(3);
|
|
for triangle in &self.triangles {
|
|
let c = triangle.circumcenter;
|
|
let color = if c.distance(mouse_position().into()) < triangle.radius {
|
|
danger_lines.extend(&triangle.edges);
|
|
DIM_FG //HIL_FG
|
|
} else {
|
|
DIM_FG
|
|
};
|
|
draw_circle_lines(c.x, c.y, triangle.radius, 1.0, color);
|
|
}
|
|
for edge in &self.edges {
|
|
let color = if danger_lines.contains(&edge) {
|
|
FG //HIL_FG
|
|
} else {
|
|
FG
|
|
};
|
|
draw_line(edge.0.x, edge.0.y, edge.1.x, edge.1.y, 3.0, color);
|
|
}
|
|
for node in &self.nodes {
|
|
draw_circle(node.x, node.y, 10.0, FG);
|
|
}
|
|
}
|
|
|
|
fn click(&mut self, pos: Vec2) {
|
|
self.add_point(Node(pos));
|
|
}
|
|
|
|
fn add_point(&mut self, node: Node) {
|
|
|
|
// Remove invalid triangles and coalesce their edges
|
|
let mut bad_triangle_sides: Vec<_> = Vec::with_capacity(self.triangles.len() * 3);
|
|
let mut i = 0;
|
|
while i < self.triangles.len() {
|
|
let triangle = &self.triangles[i];
|
|
if triangle.invalidated_by_point(*node) {
|
|
bad_triangle_sides.extend(
|
|
self.triangles.remove(i).edges
|
|
);
|
|
} else {
|
|
i = i + 1;
|
|
}
|
|
}
|
|
|
|
// For every edge
|
|
|
|
// If it has a duplicate later in the list
|
|
|
|
// Remove the edge from existance
|
|
|
|
// Otherwise
|
|
|
|
// Form a new triangle with that edge and this point
|
|
|
|
let node = Arc::new(node);
|
|
let mut new_edges = HashSet::with_capacity(bad_triangle_sides.len());
|
|
i = 0;
|
|
while i < bad_triangle_sides.len() {
|
|
let edge = &bad_triangle_sides[i];
|
|
let other_edge_i = bad_triangle_sides[(i + 1)..].iter()
|
|
.position(|other_edge| edge == other_edge);
|
|
|
|
if let Some(other_edge_i) = other_edge_i {
|
|
bad_triangle_sides.remove(other_edge_i + i + 1);
|
|
i = i + 1;
|
|
} else {
|
|
let edge = bad_triangle_sides.remove(i);
|
|
let new_edge_a = Edge(edge.0.clone(), node.clone());
|
|
let new_edge_b = Edge(edge.1.clone(), node.clone());
|
|
self.triangles.push(Triangle::new([
|
|
edge,
|
|
new_edge_a.clone(),
|
|
new_edge_b.clone(),
|
|
]));
|
|
new_edges.extend([new_edge_a, new_edge_b]);
|
|
}
|
|
}
|
|
self.edges.retain(|e| !bad_triangle_sides.iter().any(|bad_e| e == bad_e));
|
|
self.edges.extend(new_edges);
|
|
self.nodes.push(node);
|
|
}
|
|
|
|
/*fn move_point(&mut self, node: Arc<Node>) {
|
|
|
|
// Update position
|
|
|
|
// Check for broken triangles
|
|
|
|
// Remove triange
|
|
|
|
// Remove triangle consisting of common points + self
|
|
|
|
// Form triangle consisting of common point A + self + opposite
|
|
|
|
// Form triangle consisting of common point B + self + opposite
|
|
}*/
|
|
}
|