1100 lines
28 KiB
Rust
1100 lines
28 KiB
Rust
pub mod util;
|
|
pub mod shadows;
|
|
|
|
use macroquad::prelude::draw_texture;
|
|
use macroquad::prelude::DMat3;
|
|
use smallvec::SmallVec;
|
|
use smallvec::smallvec;
|
|
use std::collections::HashSet;
|
|
use macroquad::prelude::set_camera;
|
|
use macroquad::prelude::DVec2;
|
|
use macroquad::prelude::Camera2D;
|
|
use core::mem::swap;
|
|
use std::collections::HashMap;
|
|
use core::fmt;
|
|
use core::cell::Cell;
|
|
use std::collections::hash_map::Entry;
|
|
use core::f64::consts::PI;
|
|
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 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::clear_background;
|
|
use macroquad::prelude::Color;
|
|
use macroquad::input::MouseButton;
|
|
use std::sync::Arc;
|
|
|
|
const FG: Color = Color::new(0.7686, 0.7216, 0.4078, 1.0);
|
|
|
|
const SPEED: f64 = 0.1;
|
|
const BOING_RADIUS: f64 = 30.0;
|
|
const LIGHT_RADIUS: f32 = 128.;
|
|
const WALL_THICKNESS: f32 = 5.;
|
|
|
|
#[macroquad::main("BasicShapes")]
|
|
async fn main() {
|
|
let mut dd = DelaunayDemo::default();
|
|
let mut camera = dd.get_camera();
|
|
set_camera(&camera);
|
|
let mut iter = 0;
|
|
|
|
for _ in 0..100 {
|
|
dd.add_random_point().unwrap();
|
|
}
|
|
|
|
let gradient = shadows::get_gradient(LIGHT_RADIUS);
|
|
|
|
loop {
|
|
if iter == 100 {
|
|
iter = 0;
|
|
dd.resize((screen_width()/screen_height()) as f64);
|
|
camera = dd.get_camera();
|
|
set_camera(&mut camera);
|
|
}
|
|
|
|
let mouse_position: Vec2 = camera.screen_to_world(mouse_position().into());
|
|
if is_mouse_button_pressed(MouseButton::Left) {
|
|
dd.click(mouse_position);
|
|
}
|
|
if is_mouse_button_pressed(MouseButton::Right) {
|
|
|
|
println!("{}", mouse_position);
|
|
|
|
for triangle in &dd.triangles {
|
|
if triangle.invalidated_by_point(mouse_position.as_f64()) {
|
|
println!(
|
|
"{:?} has center {} with r={}",
|
|
triangle,
|
|
triangle.circumcenter().0,
|
|
triangle.circumcenter().1,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
clear_background(shadows::BLACK);
|
|
|
|
draw_texture(
|
|
gradient,
|
|
mouse_position.x - LIGHT_RADIUS,
|
|
mouse_position.y - LIGHT_RADIUS,
|
|
FG
|
|
);
|
|
|
|
for (w1, w2) in dd.get_walls() {
|
|
let w1 = w1.as_f32();
|
|
let w2 = w2.as_f32();
|
|
if shadows::wall_needs_to_be_drawn(w1, w2, mouse_position, LIGHT_RADIUS) {
|
|
let perpendicular = (w1 - w2).perp();
|
|
let shift = perpendicular / perpendicular.length() * WALL_THICKNESS;
|
|
let points = [
|
|
w1 + shift,
|
|
w1 - shift,
|
|
w2 + shift,
|
|
w2 - shift,
|
|
];
|
|
shadows::draw_wall(points[0], points[1], mouse_position, LIGHT_RADIUS);
|
|
shadows::draw_wall(points[1], points[2], mouse_position, LIGHT_RADIUS);
|
|
shadows::draw_wall(points[2], points[3], mouse_position, LIGHT_RADIUS);
|
|
shadows::draw_wall(points[3], points[0], mouse_position, LIGHT_RADIUS);
|
|
}
|
|
}
|
|
|
|
dd.update();
|
|
|
|
iter += 1;
|
|
next_frame().await;
|
|
}
|
|
}
|
|
|
|
pub struct DelaunayDemo {
|
|
pub nodes: Vec<NodeRef>,
|
|
pub triangles: Vec<Triangle>,
|
|
pub spanning_tree: HashSet<Edge>,
|
|
spanning_tree_conn: HashMap<usize, SmallVec<[usize; 8]>>, // usize is casted from a noderef
|
|
adjacency: HashMap<Edge, (NeighborOne, NeighborTwo)>,
|
|
poisoned: bool,
|
|
random_state: u128,
|
|
width: f64,
|
|
height: f64,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum NeighborOne {
|
|
Occupant(Triangle),
|
|
Border,
|
|
}
|
|
#[derive(Debug)]
|
|
enum NeighborTwo {
|
|
Friend(Triangle),
|
|
Hole,
|
|
}
|
|
use NeighborOne::*;
|
|
use NeighborTwo::*;
|
|
|
|
impl From<Triangle> for NeighborOne {
|
|
fn from(triangle: Triangle) -> NeighborOne {
|
|
Occupant(triangle)
|
|
}
|
|
}
|
|
|
|
impl From<&Triangle> for NeighborOne {
|
|
fn from(triangle: &Triangle) -> NeighborOne {
|
|
triangle.clone().into()
|
|
}
|
|
}
|
|
|
|
impl From<Triangle> for NeighborTwo {
|
|
fn from(triangle: Triangle) -> NeighborTwo {
|
|
Friend(triangle)
|
|
}
|
|
}
|
|
|
|
impl From<&Triangle> for NeighborTwo {
|
|
fn from(triangle: &Triangle) -> NeighborTwo {
|
|
triangle.clone().into()
|
|
}
|
|
}
|
|
|
|
impl NeighborTwo {
|
|
fn unwrap_mut(&mut self) -> &mut Triangle {
|
|
if let Friend(triangle) = self {
|
|
triangle
|
|
} else {
|
|
panic!("Tried to unwrap a neighbor that wasn't a friend");
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for DelaunayDemo {
|
|
fn default() -> Self {
|
|
const HEIGHT: f64 = 900.0;
|
|
const WIDTH: f64 = 1200.0;
|
|
let nodes: Vec<NodeRef> = vec![
|
|
Arc::new(Cell::new((-5.0 - BOING_RADIUS, 0.0 - BOING_RADIUS).into())),
|
|
Arc::new(Cell::new((0.0 - BOING_RADIUS, HEIGHT + BOING_RADIUS).into())),
|
|
Arc::new(Cell::new((WIDTH + BOING_RADIUS, 0.0 - BOING_RADIUS).into())),
|
|
Arc::new(Cell::new((WIDTH + BOING_RADIUS, HEIGHT + BOING_RADIUS).into())),
|
|
];
|
|
let edges = (
|
|
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()],
|
|
),
|
|
];
|
|
let adjacency = [
|
|
(edges.0, (Border, (&triangles[0]).into())),
|
|
(edges.1, (Border, (&triangles[0]).into())),
|
|
(edges.2, ((&triangles[0]).into(), (&triangles[1]).into())),
|
|
(edges.3, (Border, (&triangles[1]).into())),
|
|
(edges.4, (Border, (&triangles[1]).into())),
|
|
].into();
|
|
let mut random = 0xACAB_1312;
|
|
util::pcg64_iterstate(&mut random);
|
|
let dd = DelaunayDemo {
|
|
nodes,
|
|
adjacency,
|
|
triangles,
|
|
height: HEIGHT,
|
|
width: WIDTH,
|
|
random_state: random,
|
|
poisoned: false,
|
|
spanning_tree: HashSet::with_capacity(100),
|
|
spanning_tree_conn: HashMap::with_capacity(100),
|
|
};
|
|
|
|
/*let n_v_nodes = ((height / 50.0) - 1.0).round();
|
|
let v_inc = height / n_v_nodes;
|
|
for i in 0..(n_v_nodes as usize) {
|
|
println!("A");
|
|
let random = util::pcg64(&mut dd.random_state);
|
|
let offset = (random as f64) / (u64::MAX as f64) * 10.0;
|
|
dd.add_point((offset, (i + 1) as f64 * v_inc).into()).unwrap();
|
|
println!("B");
|
|
dd.add_point((width - offset, (i + 1) as f64 * v_inc).into()).unwrap();
|
|
}
|
|
let n_h_nodes = ((width / 50.0) - 1.0).round();
|
|
let h_inc = width / n_h_nodes;
|
|
for i in 0..(n_h_nodes as usize) {
|
|
println!("C");
|
|
let random = util::pcg64(&mut dd.random_state);
|
|
let offset = (random as f64) / (u64::MAX as f64) * 10.0;
|
|
dd.add_point(((i + 1) as f64 * h_inc, offset).into()).unwrap();
|
|
println!("D");
|
|
dd.add_point(((i + 1) as f64 * h_inc, height - offset).into()).unwrap();
|
|
}*/
|
|
|
|
dd
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct Node(DVec2, DVec2);
|
|
|
|
impl Deref for Node {
|
|
type Target = DVec2;
|
|
|
|
fn deref(&self) -> &DVec2 {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl DerefMut for Node {
|
|
fn deref_mut(&mut self) -> &mut DVec2 {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl From<(f64, f64)> for Node {
|
|
fn from((x, y): (f64, f64)) -> Node {
|
|
Node(DVec2::new(x, y), DVec2::ZERO)
|
|
}
|
|
}
|
|
|
|
type NodeRef = Arc<Cell<Node>>;
|
|
|
|
#[derive(Clone)]
|
|
pub struct Edge(NodeRef, NodeRef);
|
|
|
|
impl Edge {
|
|
fn node_thats_not(&self, node: &NodeRef) -> Result<&NodeRef, String> {
|
|
[&self.0, &self.1].into_iter()
|
|
.filter(|n| !Arc::ptr_eq(n, node))
|
|
.next()
|
|
.ok_or_else(|| format!("Edge {:?} connects two identical nodes", self))
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Edge {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f,
|
|
"Edge({},{} -> {},{})",
|
|
self.0.get().x,
|
|
self.0.get().y,
|
|
self.1.get().x,
|
|
self.1.get().y,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Triangle {
|
|
nodes: [NodeRef; 3],
|
|
edges: [Edge; 3],
|
|
}
|
|
|
|
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()
|
|
}
|
|
];
|
|
Triangle {
|
|
nodes,
|
|
edges,
|
|
}
|
|
}
|
|
|
|
pub fn new_from_points(nodes: [NodeRef; 3]) -> Triangle {
|
|
let edges = [
|
|
Edge(nodes[0].clone(), nodes[1].clone()),
|
|
Edge(nodes[1].clone(), nodes[2].clone()),
|
|
Edge(nodes[2].clone(), nodes[0].clone()),
|
|
];
|
|
Triangle {
|
|
nodes,
|
|
edges,
|
|
}
|
|
}
|
|
|
|
pub fn circumcenter(&self) -> (DVec2, f64) {
|
|
Self::calc_circumcenter(*self.nodes[0].get(), *self.nodes[1].get(), *self.nodes[2].get())
|
|
}
|
|
|
|
fn calc_circumcenter(a: DVec2, b: DVec2, c: DVec2) -> (DVec2, f64) {
|
|
|
|
let mag_a2 = a.powf(2.).dot(DVec2::ONE);
|
|
let mag_b2 = b.powf(2.).dot(DVec2::ONE);
|
|
let mag_c2 = c.powf(2.).dot(DVec2::ONE);
|
|
|
|
let s = DVec2::new(
|
|
DMat3::from_cols_array(&[
|
|
mag_a2, a.y, 1.,
|
|
mag_b2, b.y, 1.,
|
|
mag_c2, c.y, 1.,
|
|
]).determinant() * 0.5,
|
|
DMat3::from_cols_array(&[
|
|
a.x, mag_a2, 1.,
|
|
b.x, mag_b2, 1.,
|
|
c.x, mag_c2, 1.,
|
|
]).determinant() * 0.5,
|
|
);
|
|
|
|
let little_a = DMat3::from_cols_array(&[
|
|
a.x, a.y, 1.,
|
|
b.x, b.y, 1.,
|
|
c.x, c.y, 1.,
|
|
]).determinant();
|
|
|
|
let circumcenter = s / little_a;
|
|
let radius = circumcenter.distance(a);
|
|
|
|
(circumcenter, radius)
|
|
}
|
|
|
|
pub fn invalidated_by_point(&self, point: DVec2) -> bool {
|
|
let (c, r) = self.circumcenter();
|
|
c.distance(point) < r
|
|
}
|
|
|
|
pub fn common_edge(&self, other: &Triangle) -> Result<&Edge, String> {
|
|
let common_edge = self.edges.iter()
|
|
.filter(|e| other.edges.contains(e))
|
|
.collect::<Vec<_>>();
|
|
|
|
if common_edge.len() > 1 {
|
|
Err(format!(
|
|
"Tried to call common_edge on two triangles which share more \
|
|
than one common edge?: {:?} and {:?}.",
|
|
self, other
|
|
))
|
|
} else if let Some(common_edge) = common_edge.first() {
|
|
Ok(common_edge)
|
|
} else {
|
|
Err(format!(
|
|
"Tried to call common_edge on two triangles which don't \
|
|
share a common edges: {:?} and {:?}.",
|
|
self, other
|
|
))
|
|
}
|
|
}
|
|
|
|
pub fn point_not_on(&self, edge: &Edge) -> Result<&NodeRef, String> {
|
|
self.nodes.iter()
|
|
.filter(|n|
|
|
!Arc::ptr_eq(n, &edge.0) &&
|
|
!Arc::ptr_eq(n, &edge.1)
|
|
)
|
|
.next()
|
|
.ok_or_else(||format!(
|
|
".point_not_on called on {:?} with edge {:?}, but no points were found \
|
|
not on this edge",
|
|
self, edge
|
|
))
|
|
}
|
|
|
|
pub fn invalidated_by_neighbor(&self, other: &Triangle) -> Result<bool, String> {
|
|
|
|
let common_edge = self.common_edge(other)?;
|
|
|
|
let self_point = self.point_not_on(common_edge)?;
|
|
let othr_point = other.point_not_on(common_edge)?;
|
|
|
|
let common_distance = common_edge.0.get().distance(*common_edge.1.get());
|
|
let self_sidea = common_edge.0.get().distance(*self_point.get());
|
|
let self_sideb = common_edge.1.get().distance(*self_point.get());
|
|
let othr_sidea = common_edge.0.get().distance(*othr_point.get());
|
|
let othr_sideb = common_edge.1.get().distance(*othr_point.get());
|
|
|
|
let self_angle = f64::acos(
|
|
(
|
|
self_sidea.powf(2.0)
|
|
+ self_sideb.powf(2.0)
|
|
- common_distance.powf(2.0)
|
|
) / (2.0 * self_sidea * self_sideb)
|
|
);
|
|
let othr_angle = f64::acos(
|
|
(
|
|
othr_sidea.powf(2.0)
|
|
+ othr_sideb.powf(2.0)
|
|
- common_distance.powf(2.0)
|
|
) / (2.0 * othr_sidea * othr_sideb)
|
|
);
|
|
|
|
return Ok(self_angle + othr_angle > PI)
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Triangle {
|
|
fn eq(&self, other: &Triangle) -> bool {
|
|
let mut our_ptrs = [
|
|
Arc::as_ptr(&self.nodes[0]),
|
|
Arc::as_ptr(&self.nodes[1]),
|
|
Arc::as_ptr(&self.nodes[2]),
|
|
];
|
|
let mut other_ptrs = [
|
|
Arc::as_ptr(&other.nodes[0]),
|
|
Arc::as_ptr(&other.nodes[1]),
|
|
Arc::as_ptr(&other.nodes[2]),
|
|
];
|
|
our_ptrs.sort_unstable();
|
|
other_ptrs.sort_unstable();
|
|
|
|
our_ptrs[0] == other_ptrs[0] &&
|
|
our_ptrs[1] == other_ptrs[1] &&
|
|
our_ptrs[2] == other_ptrs[2]
|
|
}
|
|
}
|
|
|
|
impl Eq for Triangle {}
|
|
|
|
impl Hash for Triangle {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
let mut ptrs = [
|
|
Arc::as_ptr(&self.nodes[0]),
|
|
Arc::as_ptr(&self.nodes[1]),
|
|
Arc::as_ptr(&self.nodes[2]),
|
|
];
|
|
ptrs.sort_unstable();
|
|
ptrs.hash(state);
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Triangle {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let coords: Vec<_> = self.nodes.iter()
|
|
.flat_map(|n| [n.get().x, n.get().y])
|
|
.collect();
|
|
write!(
|
|
f,
|
|
"Triangle[({}, {}), ({}, {}), ({}, {}))",
|
|
coords[0],
|
|
coords[1],
|
|
coords[2],
|
|
coords[3],
|
|
coords[4],
|
|
coords[5],
|
|
)
|
|
}
|
|
}
|
|
|
|
impl DelaunayDemo {
|
|
pub fn get_camera(&self) -> Camera2D {
|
|
|
|
let width = self.width as f32;
|
|
let height = self.height as f32;
|
|
|
|
Camera2D {
|
|
target: Vec2::new(width / 2.0, height / 2.0),
|
|
zoom: Vec2::new(2./width, 2./height),
|
|
.. Default::default()
|
|
}
|
|
}
|
|
|
|
pub fn resize(&mut self, aspect_ratio: f64) {
|
|
let minimum_dimension = f64::min(self.width, self.height);
|
|
let (new_width, new_height) = if aspect_ratio < 1.0 {
|
|
(minimum_dimension, minimum_dimension / aspect_ratio)
|
|
} else {
|
|
(minimum_dimension * aspect_ratio, minimum_dimension)
|
|
};
|
|
|
|
let resize_factor = DVec2::new(
|
|
new_width / self.width,
|
|
new_height / self.height,
|
|
);
|
|
|
|
if resize_factor != DVec2::ONE {
|
|
|
|
for node in &self.nodes {
|
|
let mut node_inner = node.get();
|
|
node_inner.0 *= resize_factor;
|
|
node.set(node_inner);
|
|
}
|
|
|
|
self.width = new_width;
|
|
self.height = new_height;
|
|
|
|
if let Err(msg) = self.re_delaunize() {
|
|
eprintln!("POISONED: {}", msg);
|
|
self.poisoned = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_walls(&self) -> impl Iterator<Item=(DVec2, DVec2)> + '_ {
|
|
self.adjacency.values()
|
|
.filter_map(|n|
|
|
if let (Occupant(tri1), Friend(tri2)) = n {
|
|
Some((tri1, tri2))
|
|
} else {
|
|
None
|
|
}
|
|
)
|
|
.filter(|(tri1, tri2)| !self.spanning_tree.contains(tri1.common_edge(tri2).unwrap()))
|
|
.map(|(tri1, tri2)| (tri1.circumcenter().0, tri2.circumcenter().0))
|
|
}
|
|
|
|
fn update(&mut self) {
|
|
if self.poisoned {
|
|
return
|
|
}
|
|
for node in self.nodes.clone() {
|
|
if node.get().1 != DVec2::ZERO {
|
|
self.move_point(node);
|
|
}
|
|
}
|
|
if let Err(msg) = self.re_delaunize() {
|
|
eprintln!("POISONED: {}", msg);
|
|
self.poisoned = true;
|
|
}
|
|
}
|
|
|
|
fn click(&mut self, pos: Vec2) {
|
|
if let Err(msg) = self.add_point_random_dir(pos.as_f64()) {
|
|
self.poisoned = true;
|
|
eprintln!("POISONED: {}", msg);
|
|
}
|
|
}
|
|
|
|
fn add_random_point(&mut self) -> Result<(), String> {
|
|
let random = util::pcg64(&mut self.random_state);
|
|
let x = (random & 0xffffffff) as f64 / (0xffffffffu32 as f64) * self.width;
|
|
let y = (random >> 32) as f64 / (0xffffffffu32 as f64) * self.height;
|
|
self.add_point_random_dir((x, y).into())
|
|
}
|
|
|
|
fn add_point_random_dir(&mut self, pos: DVec2) -> Result<(), String> {
|
|
let random = util::pcg64(&mut self.random_state);
|
|
let angle = (random as f64) / (u64::MAX as f64) * (2.0 * std::f64::consts::PI);
|
|
let direction = DVec2::new(
|
|
f64::cos(angle),
|
|
f64::sin(angle),
|
|
);
|
|
self.add_point(Node(pos, direction * SPEED))
|
|
}
|
|
|
|
fn add_point(&mut self, mut node: Node) -> Result<(), String> {
|
|
|
|
// If this node is on top of an existing node, move it out of the way
|
|
let mut cycle_completed = false;
|
|
while !cycle_completed {
|
|
cycle_completed = true;
|
|
for other in &self.nodes {
|
|
let other_pos = other.get();
|
|
if
|
|
node.distance(other_pos.0) < BOING_RADIUS
|
|
{
|
|
let direction = node.0 - other_pos.0;
|
|
let offset = direction / direction.length() * BOING_RADIUS;
|
|
|
|
node.0 += offset;
|
|
node.0.x = node.0.x.rem_euclid(self.width);
|
|
node.0.y = node.0.y.rem_euclid(self.width);
|
|
|
|
cycle_completed = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove invalid triangles and coalesce their edges
|
|
let mut orphaned_edges: 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) {
|
|
orphaned_edges.extend(
|
|
self.remove_triangle(i)?.edges
|
|
);
|
|
} else {
|
|
i = i + 1;
|
|
}
|
|
}
|
|
orphaned_edges.retain(|e| self.adjacency.contains_key(e));
|
|
|
|
// For every edge form a new triangle with that edge and this point
|
|
let node = Arc::new(Cell::new(node));
|
|
let mut node_connected_edges = HashSet::with_capacity(orphaned_edges.len());
|
|
for edge in orphaned_edges {
|
|
let edges = [
|
|
Edge(edge.0.clone(), node.clone()),
|
|
Edge(edge.1.clone(), node.clone()),
|
|
edge,
|
|
];
|
|
for edge in &edges[..2] {
|
|
if self.is_point_inbounds(*edge.0.get()) {
|
|
node_connected_edges.insert(edge.clone());
|
|
}
|
|
}
|
|
self.create_triangle(edges)?;
|
|
}
|
|
|
|
// Update the spanning tree
|
|
let our_id = node.as_ptr() as usize;
|
|
if self.spanning_tree.is_empty() && self.nodes.len() == 5 {
|
|
|
|
// Connect the two non-border points
|
|
let other_node: &NodeRef = self.nodes.iter()
|
|
.filter(|n| self.is_point_inbounds(*n.get()))
|
|
.next()
|
|
.unwrap();
|
|
|
|
let joining_edge = Edge(other_node.clone(), node.clone());
|
|
let node_a_id = other_node.as_ptr() as usize;
|
|
let node_b_id = node.as_ptr() as usize;
|
|
|
|
self.spanning_tree.insert(joining_edge);
|
|
self.spanning_tree_conn.insert(node_a_id, smallvec![node_b_id]);
|
|
self.spanning_tree_conn.insert(node_b_id, smallvec![node_a_id]);
|
|
|
|
} else if !self.spanning_tree.is_empty() {
|
|
// Pick a random edge off this node and add it to the spanning tree
|
|
let random = util::pcg64(&mut self.random_state);
|
|
let random = random as usize % node_connected_edges.len();
|
|
let lucky_edge = node_connected_edges.into_iter()
|
|
.skip(random)
|
|
.next()
|
|
.unwrap();
|
|
let connected_node = lucky_edge.node_thats_not(&node)
|
|
.unwrap()
|
|
.as_ptr() as usize;
|
|
|
|
self.spanning_tree.insert(lucky_edge.clone());
|
|
self.spanning_tree_conn.insert(our_id, smallvec![connected_node]);
|
|
if let Some(other_edges) = self.spanning_tree_conn.get_mut(&connected_node) {
|
|
other_edges.push(our_id);
|
|
} else {
|
|
return Err(format!(
|
|
"Found node {} in triangle list, but it does not have an entry in \
|
|
the spanning tree connections list",
|
|
*lucky_edge.node_thats_not(&node)?.get()
|
|
));
|
|
}
|
|
}
|
|
|
|
self.nodes.push(node);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn remove_triangle(&mut self, index: usize) -> Result<Triangle, String> {
|
|
let triangle = self.triangles.remove(index);
|
|
for edge in &triangle.edges {
|
|
match self.adjacency.get_mut(edge) {
|
|
Some((Occupant(side1), side2 @ Friend(_))) => {
|
|
let side2_inner = side2.unwrap_mut();
|
|
if side1 == &triangle {
|
|
swap(side1, side2_inner);
|
|
} else if side2_inner != &triangle {
|
|
return Err(format!(
|
|
"{:?} does not contain triangle \
|
|
{:?}, but this triangle's list of edges includes this \
|
|
edge. Triangle was in the triangle index until now.",
|
|
edge, triangle
|
|
));
|
|
}
|
|
*side2 = Hole;
|
|
},
|
|
Some((Occupant(our_side), Hole)) => {
|
|
if our_side != &triangle {
|
|
return Err(format!(
|
|
"{:?} does not contain triangle \
|
|
{:?}, but this triangle's list of edges includes this \
|
|
edge. Triangle was in the triangle index until now.",
|
|
edge, triangle
|
|
));
|
|
}
|
|
self.remove_edge(&edge)?;
|
|
},
|
|
Some((Border, our_side @ Friend(_))) => {
|
|
*our_side = Hole;
|
|
},
|
|
Some((Border, Hole)) => {
|
|
return Err(format!(
|
|
"{:?} removed from graph, but it's edge {:?} is \
|
|
already marked as a border-hole edge", triangle, edge
|
|
));
|
|
}
|
|
None => {
|
|
return Err(format!(
|
|
"{:?} removed from graph, but it's edge {:?} was \
|
|
missing from adjacency graph", triangle, edge
|
|
));
|
|
},
|
|
}
|
|
}
|
|
Ok(triangle)
|
|
}
|
|
|
|
fn remove_edge(&mut self, edge: &Edge) -> Result<Edge, String> {
|
|
// Remove edge from the edge graph
|
|
let edge = if let Some((edge, _)) = self.adjacency.remove_entry(edge) {
|
|
edge
|
|
} else {
|
|
return Err(format!(
|
|
"Tried to remove edge {:?} from the list of edges, but it wasn't present",
|
|
edge
|
|
));
|
|
};
|
|
|
|
// If the edge was part of the spanning tree and the tree is initialized
|
|
if !self.spanning_tree.is_empty() && self.spanning_tree.remove(&edge) {
|
|
let left_id = edge.0.as_ptr() as usize;
|
|
let right_id = edge.1.as_ptr() as usize;
|
|
// Remove it completely from the spanning tree
|
|
for (node, to_node) in [(left_id, right_id), (right_id, left_id)] {
|
|
if let Some(connected_nodes) = self.spanning_tree_conn.get_mut(&node) {
|
|
connected_nodes.retain(|n| *n != to_node);
|
|
} else {
|
|
return Err(format!(
|
|
"Edge {:?} was previously in edge graph, but it's node {} was not in \
|
|
the connections list", edge, node
|
|
));
|
|
}
|
|
}
|
|
|
|
// Traverse the tree on either side of the edge
|
|
let tree_a = self.traverse_spanning_tree(edge.0.as_ptr() as usize)?;
|
|
let tree_b = self.traverse_spanning_tree(edge.1.as_ptr() as usize)?;
|
|
|
|
// Find a list of edges that:
|
|
// - Have one node from each tree
|
|
// - Do not connect to an out-of-bounds node
|
|
let candidate_edges: Vec<_> = self.adjacency.keys()
|
|
.filter(|e|
|
|
(
|
|
tree_a.contains(&(e.0.as_ptr() as usize)) &&
|
|
tree_b.contains(&(e.1.as_ptr() as usize))
|
|
) || (
|
|
tree_a.contains(&(e.1.as_ptr() as usize)) &&
|
|
tree_b.contains(&(e.0.as_ptr() as usize))
|
|
)
|
|
)
|
|
.filter(|e|
|
|
self.is_point_inbounds(*e.0.get()) &&
|
|
self.is_point_inbounds(*e.1.get())
|
|
)
|
|
.collect();
|
|
|
|
// Pick one at random and add it to the spanning tree
|
|
let random = util::pcg64(&mut self.random_state);
|
|
let random = random as usize % candidate_edges.len();
|
|
let new_edge = candidate_edges[random];
|
|
|
|
let node_a = new_edge.0.as_ptr() as usize;
|
|
let node_b = new_edge.1.as_ptr() as usize;
|
|
self.spanning_tree.insert(new_edge.clone());
|
|
for (from, to) in [(node_a, node_b), (node_b, node_a)] {
|
|
let connected = self.spanning_tree_conn.get_mut(&from).unwrap();
|
|
connected.push(to);
|
|
}
|
|
}
|
|
|
|
Ok(edge)
|
|
}
|
|
|
|
fn traverse_spanning_tree(&self, root: usize) -> Result<HashSet<usize>, String> {
|
|
let mut visited = HashSet::with_capacity(self.nodes.len());
|
|
let mut to_traverse: SmallVec<[usize; 40]> = smallvec![root];
|
|
|
|
while let Some(node) = to_traverse.pop() {
|
|
visited.insert(node);
|
|
|
|
if let Some(c) = self.spanning_tree_conn.get(&node) {
|
|
to_traverse.extend(
|
|
c.iter()
|
|
.filter(|c| !visited.contains(c))
|
|
.map(|c| *c)
|
|
);
|
|
} else {
|
|
return Err(format!(
|
|
"Spanning tree connection database lists a connection to node id {}, \
|
|
but that node lacks an entry in the database", node
|
|
));
|
|
};
|
|
|
|
}
|
|
|
|
Ok(visited)
|
|
}
|
|
|
|
fn is_point_inbounds(&self, p: DVec2) -> bool {
|
|
p.x > 0. &&
|
|
p.y > 0. &&
|
|
p.x < self.width &&
|
|
p.y < self.height
|
|
}
|
|
|
|
fn create_triangle(&mut self, edges: [Edge; 3]) -> Result<&Triangle, String> {
|
|
let triangle = Triangle::new(edges.clone());
|
|
for edge in edges {
|
|
match self.adjacency.entry(edge) {
|
|
Entry::Occupied(mut entry) => {
|
|
match entry.get_mut() {
|
|
(Occupant(tri1), Friend(tri2)) => {
|
|
let (tri1, tri2) = (tri1.clone(), tri2.clone());
|
|
return Err(format!(
|
|
"Tried to create a triangle with edge {:?}, \
|
|
but this edge is already in use by triangles {:?} and \
|
|
{:?}.",
|
|
entry.key(), tri1, tri2
|
|
));
|
|
},
|
|
(Border, Friend(tri)) => {
|
|
let tri = tri.clone();
|
|
return Err(format!(
|
|
"Tried to create a triangle with edge {:?}, \
|
|
but this edge is already in use by triangle {:?} and \
|
|
is a border.",
|
|
entry.key(), tri
|
|
));
|
|
},
|
|
(Occupant(_) | Border, new_home @ Hole) => {
|
|
*new_home = Friend(triangle.clone());
|
|
},
|
|
}
|
|
},
|
|
Entry::Vacant(entry) => {
|
|
entry.insert(((&triangle).into(), Hole));
|
|
}
|
|
}
|
|
}
|
|
self.triangles.push(triangle);
|
|
Ok(self.triangles.last().unwrap())
|
|
}
|
|
|
|
fn move_point(&mut self, node: NodeRef) {
|
|
|
|
// Update position
|
|
let current_pos = node.get();
|
|
let mut new_pos = Node(current_pos.0 + current_pos.1, current_pos.1);
|
|
|
|
// Check if it is going to hit another point
|
|
for other in &self.nodes {
|
|
// If we are currently inside the boing radius of the node (e.g. because we spawned
|
|
// there), then move out.
|
|
if
|
|
!Arc::ptr_eq(other, &node)
|
|
&& current_pos.distance(other.get().0) < BOING_RADIUS
|
|
{
|
|
let other_pos = other.get();
|
|
let direction = current_pos.0 - other_pos.0;
|
|
let offset = direction / direction.length() * BOING_RADIUS / 2.;
|
|
|
|
node.set(Node(current_pos.0 + offset, current_pos.1));
|
|
other.set(Node(other_pos.0 - offset, other_pos.1));
|
|
return self.move_point(node);
|
|
}
|
|
|
|
// If we are going to walk into the boing radius of another node, boing!
|
|
if
|
|
!Arc::ptr_eq(other, &node)
|
|
&& new_pos.distance(other.get().0) < BOING_RADIUS
|
|
{
|
|
let mut node_inner = node.get();
|
|
let mut other_inner = other.get();
|
|
|
|
swap(&mut other_inner.1, &mut node_inner.1);
|
|
|
|
other.set(other_inner);
|
|
node.set(node_inner);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check for a bounce
|
|
if new_pos.0.y > self.height {
|
|
new_pos.0.y = 2.0 * self.height - new_pos.0.y;
|
|
new_pos.1.y = -new_pos.1.y;
|
|
}
|
|
if new_pos.0.y < 0.0 {
|
|
new_pos.0.y = -new_pos.0.y;
|
|
new_pos.1.y = -new_pos.1.y;
|
|
}
|
|
if new_pos.0.x > self.width {
|
|
new_pos.0.x = 2.0 * self.width - new_pos.0.x;
|
|
new_pos.1.x = -new_pos.1.x;
|
|
}
|
|
if new_pos.0.x < 0.0 {
|
|
new_pos.0.x = -new_pos.0.x;
|
|
new_pos.1.x = -new_pos.1.x;
|
|
}
|
|
|
|
node.set(new_pos);
|
|
}
|
|
|
|
fn get_neighbors(&self, triangle: &Triangle) -> Result<Vec<&Triangle>, String> {
|
|
triangle.edges.iter()
|
|
.filter_map(|e|
|
|
match self.adjacency.get(e) {
|
|
Some((Occupant(a), Friend(b))) => {
|
|
if a != triangle && b == triangle {
|
|
Some(Ok(a))
|
|
} else if a == triangle && b != triangle {
|
|
Some(Ok(b))
|
|
} else if a == triangle && b == triangle {
|
|
Some(Err(format!(
|
|
"{:?} has {:?} in adjacency graph which lists itself as \
|
|
its own neighbor",
|
|
triangle, e
|
|
)))
|
|
} else {
|
|
Some(Err(format!(
|
|
"{:?} has {:?}, but the adjacency graph reports that the \
|
|
triangles next to this edge are {:?} and {:?}",
|
|
triangle, e, a, b
|
|
)))
|
|
}
|
|
},
|
|
Some((Border, Friend(tri))) => {
|
|
if tri == triangle {
|
|
None
|
|
} else {
|
|
Some(Err(format!(
|
|
"{:?} has {:?}, but the adjacency graph reports that \
|
|
this edge is an edge between the graph hull and {:?}",
|
|
triangle, e, tri
|
|
)))
|
|
}
|
|
},
|
|
Some((Occupant(tri), Hole)) => {
|
|
if tri == triangle {
|
|
None
|
|
} else {
|
|
Some(Err(format!(
|
|
"{:?} has {:?}, but the adjacency graph reports that \
|
|
this edge is an edge between a polygonal hole and {:?}",
|
|
triangle, e, tri
|
|
)))
|
|
}
|
|
},
|
|
Some((Border, Hole)) => {
|
|
Some(Err(format!(
|
|
"{:?} has {:?}, but the adjacency graph lists this edge as \
|
|
being between the graph hull and a polygonal hole (aka, it \
|
|
has no neighbors",
|
|
triangle, e
|
|
)))
|
|
},
|
|
None => {
|
|
Some(Err(format!(
|
|
"get_neighbors called on {:?}, but its edge {:?} is not \
|
|
present in the adjacency graph.",
|
|
triangle, e
|
|
)))
|
|
},
|
|
}
|
|
)
|
|
.collect()
|
|
}
|
|
|
|
fn flip_triangles(
|
|
&mut self,
|
|
tri1: &Triangle,
|
|
tri2: &Triangle
|
|
) -> Result<(Triangle, &Triangle), String> {
|
|
let mut indices = self.triangles.iter()
|
|
.enumerate()
|
|
.filter(|(_, t)| t == &tri1 || t == &tri2)
|
|
.map(|(i, _)| i);
|
|
let err = || format!(
|
|
"flip_triangles called on {:?} and {:?}, but not both of these were found in \
|
|
the list of triangles",
|
|
tri1, tri2
|
|
);
|
|
|
|
let mut indices = [
|
|
indices.next().ok_or_else(err)?,
|
|
indices.next().ok_or_else(err)?,
|
|
];
|
|
indices.sort_unstable();
|
|
|
|
let tri1 = self.remove_triangle(indices[1])?;
|
|
let tri2 = self.remove_triangle(indices[0])?;
|
|
|
|
let common_edge = tri1.common_edge(&tri2)?;
|
|
|
|
let tri1_point = tri1.point_not_on(common_edge)?;
|
|
let tri2_point = tri2.point_not_on(common_edge)?;
|
|
|
|
Ok((
|
|
self.create_triangle([
|
|
Edge(tri1_point.clone(), common_edge.0.clone()),
|
|
Edge(tri2_point.clone(), common_edge.0.clone()),
|
|
Edge(tri1_point.clone(), tri2_point.clone()),
|
|
])?.clone(),
|
|
self.create_triangle([
|
|
Edge(tri1_point.clone(), common_edge.1.clone()),
|
|
Edge(tri2_point.clone(), common_edge.1.clone()),
|
|
Edge(tri1_point.clone(), tri2_point.clone()),
|
|
])?,
|
|
))
|
|
}
|
|
|
|
fn re_delaunize(&mut self) -> Result<(), String> {
|
|
let mut i = 0;
|
|
while i < self.triangles.len() {
|
|
let triangle = &self.triangles[i];
|
|
let neighbors: Vec<_> = self.get_neighbors(triangle)?
|
|
.into_iter()
|
|
.cloned()
|
|
.collect();
|
|
let mut flipped = false;
|
|
for neighbor in neighbors {
|
|
if triangle.invalidated_by_neighbor(&neighbor)? {
|
|
let triangle = triangle.clone();
|
|
self.flip_triangles(&triangle, &neighbor)?;
|
|
i = 0;
|
|
flipped = true;
|
|
break;
|
|
}
|
|
}
|
|
if !flipped {
|
|
i = i + 1;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|