Basic Delaunay triangulation

This commit is contained in:
Emi Simpson 2021-11-13 15:18:08 -05:00
commit 3be8db1fdc
Signed by: Emi
GPG Key ID: A12F2C2FFDC3D847
6 changed files with 733 additions and 0 deletions

8
.editorconfig Normal file
View File

@ -0,0 +1,8 @@
[*.rs]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false
max_line_length = 90

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

431
Cargo.lock generated Normal file
View File

@ -0,0 +1,431 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "ahash"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
[[package]]
name = "audir-sles"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea47348666a8edb7ad80cbee3940eb2bccf70df0e6ce09009abe1a836cb779f5"
[[package]]
name = "audrey"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58b92a84e89497e3cd25d3672cd5d1c288abaac02c18ff21283f17d118b889b8"
dependencies = [
"dasp_frame",
"dasp_sample",
"hound",
"lewton",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
[[package]]
name = "bytemuck"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "crc32fast"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
dependencies = [
"cfg-if",
]
[[package]]
name = "dasp_frame"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a3937f5fe2135702897535c8d4a5553f8b116f76c1529088797f2eee7c5cd6"
dependencies = [
"dasp_sample",
]
[[package]]
name = "dasp_sample"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
[[package]]
name = "deflate"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [
"adler32",
"byteorder",
]
[[package]]
name = "fontdue"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75712fff1702bac51b7eaa5a5ca9f9853b8055ef5906088a32f4fe196595a1d"
dependencies = [
"hashbrown",
"ttf-parser",
]
[[package]]
name = "glam"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da"
[[package]]
name = "glow"
version = "0.1.0"
dependencies = [
"macroquad",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
dependencies = [
"ahash",
]
[[package]]
name = "hound"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]]
name = "image"
version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-iter",
"num-rational",
"num-traits",
"png",
]
[[package]]
name = "lewton"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d542c1a317036c45c2aa1cf10cc9d403ca91eb2d333ef1a4917e5cb10628bd0"
dependencies = [
"byteorder",
"ogg",
"smallvec",
]
[[package]]
name = "libc"
version = "0.2.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
[[package]]
name = "macroquad"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54a31d3996831b27b429f8e9d7b948aa589d3571305eba39f57b4600bd2cd01"
dependencies = [
"bumpalo",
"fontdue",
"glam",
"image",
"macroquad_macro",
"miniquad",
"quad-rand",
"quad-snd",
]
[[package]]
name = "macroquad_macro"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5cecfede1e530599c8686f7f2d609489101d3d63741a6dc423afc997ce3fcc8"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "miniquad"
version = "0.3.0-alpha.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6793a3ef846953fc7c01302093abf8749be22742c63db05c66ef0a2c889a7fbb"
dependencies = [
"sapp-android",
"sapp-darwin",
"sapp-dummy",
"sapp-ios",
"sapp-linux",
"sapp-wasm",
"sapp-windows",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "ndk-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d"
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "ogg"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e571c3517af9e1729d4c63571a27edd660ade0667973bfc74a67c660c2b651"
dependencies = [
"byteorder",
]
[[package]]
name = "png"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [
"bitflags",
"crc32fast",
"deflate",
"miniz_oxide",
]
[[package]]
name = "quad-alsa-sys"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66c2f04a6946293477973d85adc251d502da51c57b08cd9c997f0cfd8dcd4b5"
dependencies = [
"libc",
]
[[package]]
name = "quad-rand"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658fa1faf7a4cc5f057c9ee5ef560f717ad9d8dc66d975267f709624d6e1ab88"
[[package]]
name = "quad-snd"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86e0b4259cfd6a317a46df7b7cb4c09a08ba150642e6f6fb7df5a6b3450a0a29"
dependencies = [
"audir-sles",
"audrey",
"libc",
"quad-alsa-sys",
"winapi",
]
[[package]]
name = "sapp-android"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a4a81f462ba2783213978528560aa138adf2f94da1ac940c1b5c854c03e1724"
dependencies = [
"libc",
"ndk-sys",
]
[[package]]
name = "sapp-darwin"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0310e2445f307468aa13f1cde94d6fba6b8fd329afbb642dedbe3faf1a145f31"
dependencies = [
"cc",
]
[[package]]
name = "sapp-dummy"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66f1ad26a5b6c682b9ca27c66db9aa91002b8d98a82ac7101ded57285215a478"
dependencies = [
"libc",
]
[[package]]
name = "sapp-ios"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "081e6e5261c9ac2e938979b6a854a53b439f065fc3c897205ce7e69d3028b4a9"
dependencies = [
"cc",
]
[[package]]
name = "sapp-linux"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbdb2f8011955c62544d9e626a58333e788810d00bd7411d52b81611b92af142"
dependencies = [
"libc",
]
[[package]]
name = "sapp-wasm"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00e859e8645a3bcb85aecd40bab883438e4105f21b21bccbeac2348760f508bb"
[[package]]
name = "sapp-windows"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8faec983cb54ce5e9529815fc0aae6c36bab9fba9cd0ae5590dfa17bc0719fa"
dependencies = [
"winapi",
]
[[package]]
name = "smallvec"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
dependencies = [
"maybe-uninit",
]
[[package]]
name = "ttf-parser"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "glow"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
macroquad = "0.3"
#itertools = "0.10.1"

283
src/main.rs Normal file
View File

@ -0,0 +1,283 @@
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
}*/
}

0
src/util.rs Normal file
View File