Compare commits
8 Commits
3e15269afb
...
main
Author | SHA1 | Date |
---|---|---|
Emi Simpson | 261392ee3d | |
Emi Simpson | 07ddec501a | |
Emi Simpson | 66093071ac | |
Emi Simpson | 38810f749c | |
Emi Simpson | a745c63de9 | |
Emi Simpson | 115df64386 | |
Emi Simpson | 184d74a5aa | |
Emi Simpson | df4fa8789c |
|
@ -135,6 +135,7 @@ name = "glow"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"macroquad",
|
"macroquad",
|
||||||
|
"smallvec 1.7.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -175,7 +176,7 @@ checksum = "8d542c1a317036c45c2aa1cf10cc9d403ca91eb2d333ef1a4917e5cb10628bd0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"ogg",
|
"ogg",
|
||||||
"smallvec",
|
"smallvec 0.6.14",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -402,6 +403,12 @@ dependencies = [
|
||||||
"maybe-uninit",
|
"maybe-uninit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ttf-parser"
|
name = "ttf-parser"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
|
|
@ -7,4 +7,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
macroquad = "0.3"
|
macroquad = "0.3"
|
||||||
#itertools = "0.10.1"
|
|
||||||
|
[dependencies.smallvec]
|
||||||
|
version = "1.7.0"
|
||||||
|
features = ["union", "const_generics", "const_new"]
|
||||||
|
|
380
src/main.rs
380
src/main.rs
|
@ -1,5 +1,11 @@
|
||||||
pub mod util;
|
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::set_camera;
|
||||||
use macroquad::prelude::DVec2;
|
use macroquad::prelude::DVec2;
|
||||||
use macroquad::prelude::Camera2D;
|
use macroquad::prelude::Camera2D;
|
||||||
|
@ -8,33 +14,28 @@ use std::collections::HashMap;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use macroquad::prelude::draw_circle_lines;
|
|
||||||
use core::f64::consts::PI;
|
use core::f64::consts::PI;
|
||||||
use macroquad::prelude::mouse_position;
|
use macroquad::prelude::mouse_position;
|
||||||
use macroquad::prelude::is_mouse_button_pressed;
|
use macroquad::prelude::is_mouse_button_pressed;
|
||||||
use macroquad::prelude::Vec2;
|
use macroquad::prelude::Vec2;
|
||||||
use core::ops::DerefMut;
|
use core::ops::DerefMut;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use macroquad::prelude::draw_line;
|
|
||||||
use core::hash::Hasher;
|
use core::hash::Hasher;
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
use macroquad::prelude::screen_height;
|
use macroquad::prelude::screen_height;
|
||||||
use macroquad::prelude::next_frame;
|
use macroquad::prelude::next_frame;
|
||||||
use macroquad::prelude::screen_width;
|
use macroquad::prelude::screen_width;
|
||||||
use macroquad::prelude::draw_circle;
|
|
||||||
use macroquad::prelude::clear_background;
|
use macroquad::prelude::clear_background;
|
||||||
use macroquad::prelude::Color;
|
use macroquad::prelude::Color;
|
||||||
use macroquad::input::MouseButton;
|
use macroquad::input::MouseButton;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
const BG: Color = Color::new(0.16863, 0.23922, 0.21176, 1.0);
|
const FG: Color = Color::new(0.7686, 0.7216, 0.4078, 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);
|
|
||||||
|
|
||||||
const SPEED: f64 = 0.1;
|
const SPEED: f64 = 0.1;
|
||||||
const BOING_RADIUS: f64 = 30.0;
|
const BOING_RADIUS: f64 = 30.0;
|
||||||
const HILITE: bool = false;
|
const LIGHT_RADIUS: f32 = 128.;
|
||||||
|
const WALL_THICKNESS: f32 = 5.;
|
||||||
|
|
||||||
#[macroquad::main("BasicShapes")]
|
#[macroquad::main("BasicShapes")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -42,6 +43,13 @@ async fn main() {
|
||||||
let mut camera = dd.get_camera();
|
let mut camera = dd.get_camera();
|
||||||
set_camera(&camera);
|
set_camera(&camera);
|
||||||
let mut iter = 0;
|
let mut iter = 0;
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
dd.add_random_point().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let gradient = shadows::get_gradient(LIGHT_RADIUS);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if iter == 100 {
|
if iter == 100 {
|
||||||
iter = 0;
|
iter = 0;
|
||||||
|
@ -50,11 +58,11 @@ async fn main() {
|
||||||
set_camera(&mut camera);
|
set_camera(&mut camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mouse_position: Vec2 = camera.screen_to_world(mouse_position().into());
|
||||||
if is_mouse_button_pressed(MouseButton::Left) {
|
if is_mouse_button_pressed(MouseButton::Left) {
|
||||||
dd.click(camera.screen_to_world(mouse_position().into()));
|
dd.click(mouse_position);
|
||||||
}
|
}
|
||||||
if is_mouse_button_pressed(MouseButton::Right) {
|
if is_mouse_button_pressed(MouseButton::Right) {
|
||||||
let mouse_position: Vec2 = camera.screen_to_world(mouse_position().into());
|
|
||||||
|
|
||||||
println!("{}", mouse_position);
|
println!("{}", mouse_position);
|
||||||
|
|
||||||
|
@ -70,10 +78,35 @@ async fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_background(BG);
|
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();
|
dd.update();
|
||||||
//dd.draw_delaunay();
|
|
||||||
dd.draw_voronoi();
|
|
||||||
|
|
||||||
iter += 1;
|
iter += 1;
|
||||||
next_frame().await;
|
next_frame().await;
|
||||||
|
@ -83,6 +116,8 @@ async fn main() {
|
||||||
pub struct DelaunayDemo {
|
pub struct DelaunayDemo {
|
||||||
pub nodes: Vec<NodeRef>,
|
pub nodes: Vec<NodeRef>,
|
||||||
pub triangles: Vec<Triangle>,
|
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)>,
|
adjacency: HashMap<Edge, (NeighborOne, NeighborTwo)>,
|
||||||
poisoned: bool,
|
poisoned: bool,
|
||||||
random_state: u128,
|
random_state: u128,
|
||||||
|
@ -169,14 +204,18 @@ impl Default for DelaunayDemo {
|
||||||
(edges.3, (Border, (&triangles[1]).into())),
|
(edges.3, (Border, (&triangles[1]).into())),
|
||||||
(edges.4, (Border, (&triangles[1]).into())),
|
(edges.4, (Border, (&triangles[1]).into())),
|
||||||
].into();
|
].into();
|
||||||
|
let mut random = 0xACAB_1312;
|
||||||
|
util::pcg64_iterstate(&mut random);
|
||||||
let dd = DelaunayDemo {
|
let dd = DelaunayDemo {
|
||||||
nodes,
|
nodes,
|
||||||
adjacency,
|
adjacency,
|
||||||
triangles,
|
triangles,
|
||||||
height: HEIGHT,
|
height: HEIGHT,
|
||||||
width: WIDTH,
|
width: WIDTH,
|
||||||
random_state: 1312_1312_1312,
|
random_state: random,
|
||||||
poisoned: false,
|
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 n_v_nodes = ((height / 50.0) - 1.0).round();
|
||||||
|
@ -232,6 +271,15 @@ type NodeRef = Arc<Cell<Node>>;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Edge(NodeRef, NodeRef);
|
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 {
|
impl PartialEq for Edge {
|
||||||
fn eq(&self, other: &Edge) -> bool {
|
fn eq(&self, other: &Edge) -> bool {
|
||||||
let mut our_ptrs = [
|
let mut our_ptrs = [
|
||||||
|
@ -314,25 +362,31 @@ impl Triangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_circumcenter(a: DVec2, b: DVec2, c: DVec2) -> (DVec2, f64) {
|
fn calc_circumcenter(a: DVec2, b: DVec2, c: DVec2) -> (DVec2, f64) {
|
||||||
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 mag_a2 = a.powf(2.).dot(DVec2::ONE);
|
||||||
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 mag_b2 = b.powf(2.).dot(DVec2::ONE);
|
||||||
|
let mag_c2 = c.powf(2.).dot(DVec2::ONE);
|
||||||
|
|
||||||
let ang_a = f64::acos(cos_a);
|
let s = DVec2::new(
|
||||||
let ang_b = f64::acos(cos_b);
|
DMat3::from_cols_array(&[
|
||||||
let ang_c = PI - ang_a - ang_b;
|
mag_a2, a.y, 1.,
|
||||||
|
mag_b2, b.y, 1.,
|
||||||
let sin_2a = f64::sin(2.0 * ang_a);
|
mag_c2, c.y, 1.,
|
||||||
let sin_2b = f64::sin(2.0 * ang_b);
|
]).determinant() * 0.5,
|
||||||
let sin_2c = f64::sin(2.0 * ang_c);
|
DMat3::from_cols_array(&[
|
||||||
|
a.x, mag_a2, 1.,
|
||||||
let circumcenter = DVec2::new(
|
b.x, mag_b2, 1.,
|
||||||
(a.x * sin_2a + b.x * sin_2b + c.x * sin_2c) / (sin_2a + sin_2b + sin_2c),
|
c.x, mag_c2, 1.,
|
||||||
(a.y * sin_2a + b.y * sin_2b + c.y * sin_2c) / (sin_2a + sin_2b + sin_2c),
|
]).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);
|
let radius = circumcenter.distance(a);
|
||||||
|
|
||||||
(circumcenter, radius)
|
(circumcenter, radius)
|
||||||
|
@ -508,64 +562,17 @@ impl DelaunayDemo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_delaunay(&self) {
|
fn get_walls(&self) -> impl Iterator<Item=(DVec2, DVec2)> + '_ {
|
||||||
let mut highlight_segments = Vec::new();
|
self.adjacency.values()
|
||||||
for triangle in &self.triangles {
|
.filter_map(|n|
|
||||||
let (c, r) = triangle.circumcenter();
|
if let (Occupant(tri1), Friend(tri2)) = n {
|
||||||
let mpos: Vec2 = mouse_position().into();
|
Some((tri1, tri2))
|
||||||
let color = if c.distance(mpos.as_f64()) < r && HILITE {
|
} else {
|
||||||
highlight_segments.extend(&triangle.edges);
|
None
|
||||||
HIL_FG
|
}
|
||||||
} else {
|
)
|
||||||
DIM_FG
|
.filter(|(tri1, tri2)| !self.spanning_tree.contains(tri1.common_edge(tri2).unwrap()))
|
||||||
};
|
.map(|(tri1, tri2)| (tri1.circumcenter().0, tri2.circumcenter().0))
|
||||||
draw_circle_lines(c.x as f32, c.y as f32, r as f32, 1.0, color);
|
|
||||||
}
|
|
||||||
for edge in self.adjacency.keys() {
|
|
||||||
let color = if highlight_segments.contains(&edge) {
|
|
||||||
HIL_FG
|
|
||||||
} else {
|
|
||||||
FG
|
|
||||||
};
|
|
||||||
let edge_0 = edge.0.get().as_f32();
|
|
||||||
let edge_1 = edge.1.get().as_f32();
|
|
||||||
|
|
||||||
if
|
|
||||||
edge_0.x < 0.0 || edge_0.x > self.width as f32
|
|
||||||
|| edge_0.y < 0.0 || edge_0.y > self.height as f32
|
|
||||||
|| edge_1.x < 0.0 || edge_1.x > self.width as f32
|
|
||||||
|| edge_1.y < 0.0 || edge_1.y > self.height as f32
|
|
||||||
{
|
|
||||||
continue; //don't draw container nodes
|
|
||||||
}
|
|
||||||
draw_line(
|
|
||||||
edge_0.x,
|
|
||||||
edge_0.y,
|
|
||||||
edge_1.x,
|
|
||||||
edge_1.y,
|
|
||||||
3.0, color
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for node in &self.nodes {
|
|
||||||
let pos = node.get().as_f32();
|
|
||||||
draw_circle(pos.x, pos.y, 10.0, FG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_voronoi(&self) {
|
|
||||||
for neighbors in self.adjacency.values() {
|
|
||||||
if let (Occupant(tri1), Friend(tri2)) = neighbors {
|
|
||||||
let (center1, _) = tri1.circumcenter();
|
|
||||||
let (center2, _) = tri2.circumcenter();
|
|
||||||
draw_line(
|
|
||||||
center1.x as f32,
|
|
||||||
center1.y as f32,
|
|
||||||
center2.x as f32,
|
|
||||||
center2.y as f32,
|
|
||||||
3.0, FG
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
|
@ -584,20 +591,52 @@ impl DelaunayDemo {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn click(&mut self, pos: Vec2) {
|
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 random = util::pcg64(&mut self.random_state);
|
||||||
let angle = (random as f64) / (u64::MAX as f64) * (2.0 * std::f64::consts::PI);
|
let angle = (random as f64) / (u64::MAX as f64) * (2.0 * std::f64::consts::PI);
|
||||||
let direction = DVec2::new(
|
let direction = DVec2::new(
|
||||||
f64::cos(angle),
|
f64::cos(angle),
|
||||||
f64::sin(angle),
|
f64::sin(angle),
|
||||||
);
|
);
|
||||||
if let Err(msg) = self.add_point(Node(pos.as_f64(), direction * SPEED)) {
|
self.add_point(Node(pos, direction * SPEED))
|
||||||
self.poisoned = true;
|
|
||||||
eprintln!("POISONED: {}", msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_point(&mut self, node: Node) -> Result<(), String> {
|
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
|
// Remove invalid triangles and coalesce their edges
|
||||||
let mut orphaned_edges: Vec<_> = Vec::with_capacity(self.triangles.len() * 3);
|
let mut orphaned_edges: Vec<_> = Vec::with_capacity(self.triangles.len() * 3);
|
||||||
|
@ -616,12 +655,62 @@ impl DelaunayDemo {
|
||||||
|
|
||||||
// For every edge form a new triangle with that edge and this point
|
// For every edge form a new triangle with that edge and this point
|
||||||
let node = Arc::new(Cell::new(node));
|
let node = Arc::new(Cell::new(node));
|
||||||
|
let mut node_connected_edges = HashSet::with_capacity(orphaned_edges.len());
|
||||||
for edge in orphaned_edges {
|
for edge in orphaned_edges {
|
||||||
self.create_triangle([
|
let edges = [
|
||||||
Edge(edge.0.clone(), node.clone()),
|
Edge(edge.0.clone(), node.clone()),
|
||||||
Edge(edge.1.clone(), node.clone()),
|
Edge(edge.1.clone(), node.clone()),
|
||||||
edge,
|
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);
|
self.nodes.push(node);
|
||||||
|
@ -656,7 +745,7 @@ impl DelaunayDemo {
|
||||||
edge, triangle
|
edge, triangle
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
self.adjacency.remove(&edge);
|
self.remove_edge(&edge)?;
|
||||||
},
|
},
|
||||||
Some((Border, our_side @ Friend(_))) => {
|
Some((Border, our_side @ Friend(_))) => {
|
||||||
*our_side = Hole;
|
*our_side = Hole;
|
||||||
|
@ -678,6 +767,105 @@ impl DelaunayDemo {
|
||||||
Ok(triangle)
|
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> {
|
fn create_triangle(&mut self, edges: [Edge; 3]) -> Result<&Triangle, String> {
|
||||||
let triangle = Triangle::new(edges.clone());
|
let triangle = Triangle::new(edges.clone());
|
||||||
for edge in edges {
|
for edge in edges {
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
use macroquad::prelude::Mat2;
|
||||||
|
use macroquad::prelude::draw_triangle;
|
||||||
|
use macroquad::prelude::DVec2;
|
||||||
|
use macroquad::prelude::draw_mesh;
|
||||||
|
use macroquad::prelude::Vec3;
|
||||||
|
use macroquad::models::Vertex;
|
||||||
|
use macroquad::prelude::Mesh;
|
||||||
|
use macroquad::prelude::Texture2D;
|
||||||
|
use macroquad::prelude::Vec2;
|
||||||
|
use macroquad::prelude::Image;
|
||||||
|
use macroquad::prelude::Color;
|
||||||
|
|
||||||
|
//pub const LIGHT_COLOR: Color = Color::new(0.2980, 0.2824, 0.1686, 1.);
|
||||||
|
pub const WHITE: Color = Color::new(1., 1., 1., 1.);
|
||||||
|
pub const BLACK: Color = Color::new(0., 0., 0., 1.);
|
||||||
|
|
||||||
|
pub fn get_gradient(radius: f32) -> Texture2D {
|
||||||
|
let dimension = (radius as u16) * 2 + 1;
|
||||||
|
let center = Vec2::new(radius, radius);
|
||||||
|
let mut img = Image::gen_image_color(dimension, dimension, BLACK);
|
||||||
|
for x in 0..(dimension as u32) {
|
||||||
|
for y in 0..(dimension as u32) {
|
||||||
|
let pix = Vec2::new(x as f32, y as f32);
|
||||||
|
let intensity = f32::max(0., (radius - pix.distance(center)) / radius);
|
||||||
|
let mut color = WHITE;
|
||||||
|
color.a = intensity;
|
||||||
|
img.set_pixel(x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D::from_image(&img)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_wall(a: Vec2, b: Vec2, light_source: Vec2, radius: f32) {
|
||||||
|
|
||||||
|
let a_shft = a - light_source;
|
||||||
|
let b_shft = b - light_source;
|
||||||
|
|
||||||
|
let mag_a = a_shft.length();
|
||||||
|
let mag_b = b_shft.length();
|
||||||
|
|
||||||
|
let a_prime = a_shft.clamp_length_min(mag_a + radius);
|
||||||
|
let b_prime = b_shft.clamp_length_min(mag_b + radius);
|
||||||
|
let opposite_src = a_prime.lerp(b_prime, 0.5)
|
||||||
|
.clamp_length_min(radius * 1.5);
|
||||||
|
|
||||||
|
let a_prime = light_source + a_prime;
|
||||||
|
let b_prime = light_source + b_prime;
|
||||||
|
let opposite_src = light_source + opposite_src;
|
||||||
|
|
||||||
|
draw_triangle(a, b_prime, b, BLACK);
|
||||||
|
draw_triangle(a, b_prime, a_prime, BLACK);
|
||||||
|
draw_triangle(b_prime, a_prime, opposite_src, BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wall_needs_to_be_drawn(a: Vec2, b: Vec2, light_source: Vec2, radius: f32) -> bool {
|
||||||
|
let a = a - light_source;
|
||||||
|
let b = b - light_source;
|
||||||
|
let delta = a - b;
|
||||||
|
let unit = delta / delta.length();
|
||||||
|
|
||||||
|
let a_dot = unit.dot(a);
|
||||||
|
let b_dot = unit.dot(b);
|
||||||
|
|
||||||
|
let d = Mat2::from_cols(unit, a).determinant();
|
||||||
|
|
||||||
|
(d < radius && d > -radius) && (
|
||||||
|
a.length() < radius ||
|
||||||
|
b.length() < radius ||
|
||||||
|
(a_dot < 0. && b_dot > 0.) || (a_dot > 0. && b_dot < 0.)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// Code adapted from https://github.com/not-fl3/macroquad/issues/174
|
||||||
|
pub fn draw_polygon(points: &[Vec2], color: Color) {
|
||||||
|
let points_length = points.len();
|
||||||
|
let mut vertices = Vec::<Vertex>::with_capacity(points_length as usize + 2);
|
||||||
|
let mut indices = Vec::<u16>::with_capacity(points_length as usize * 3);
|
||||||
|
|
||||||
|
for (i, point) in points.iter().enumerate() {
|
||||||
|
let vertex = Vertex {
|
||||||
|
position: Vec3::new(point.x, point.y, 0.0),
|
||||||
|
uv: Vec2::default(),
|
||||||
|
color
|
||||||
|
};
|
||||||
|
|
||||||
|
vertices.push(vertex);
|
||||||
|
indices.extend_from_slice(&[0, i as u16 + 1, i as u16 + 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mesh = Mesh {
|
||||||
|
vertices,
|
||||||
|
indices,
|
||||||
|
texture: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_mesh(&mesh);
|
||||||
|
}*/
|
|
@ -6,7 +6,7 @@ pub fn pcg64(state: &mut u128) -> u64 {
|
||||||
((x >> 58) as u64).rotate_right(count)
|
((x >> 58) as u64).rotate_right(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pcg64_iterstate(state: &mut u128) {
|
pub fn pcg64_iterstate(state: &mut u128) {
|
||||||
const MULTIPLIER: u128 = 0xde92a69f6e2f9f25fd0d90f576075fbd;
|
const MULTIPLIER: u128 = 0xde92a69f6e2f9f25fd0d90f576075fbd;
|
||||||
const INCREMENT: u128 = 621;
|
const INCREMENT: u128 = 621;
|
||||||
*state = state.wrapping_mul(MULTIPLIER).wrapping_add(INCREMENT);
|
*state = state.wrapping_mul(MULTIPLIER).wrapping_add(INCREMENT);
|
||||||
|
|
Loading…
Reference in New Issue