From 7edbb4449ec1be13fa47694d535a0cc2ba7360a8 Mon Sep 17 00:00:00 2001 From: Youser Nayme <7685106-NeutralModder@users.noreply.gitlab.com> Date: Mon, 3 Jun 2024 23:12:53 +0000 Subject: [PATCH] Update kiddo from 0.2 to 4.2.0 --- Cargo.lock | 98 +++++++++- common/Cargo.toml | 2 +- common/src/path.rs | 135 +++++++------- world/Cargo.toml | 3 +- .../examples/chunk_compression_benchmarks.rs | 26 +-- world/examples/world_block_statistics.rs | 169 ++++++++++++++---- world/src/site2/plot/gnarling.rs | 40 ++--- 7 files changed, 342 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1219d7aad..868fa0047a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -450,6 +450,12 @@ dependencies = [ "tower-service", ] +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "backtrace" version = "0.3.71" @@ -1842,6 +1848,12 @@ dependencies = [ "syn 2.0.65", ] +[[package]] +name = "divrem" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69dde51e8fef5e12c1d65e0929b03d66e4c0c18282bc30ed2ca050ad6f44dd82" + [[package]] name = "dlib" version = "0.5.2" @@ -1851,6 +1863,12 @@ dependencies = [ "libloading 0.8.3", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dot_vox" version = "5.1.1" @@ -1930,6 +1948,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +[[package]] +name = "elapsed" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f4e5af126dafd0741c2ad62d47f68b28602550102e5f0dd45c8a97fc8b49c29" + [[package]] name = "emath" version = "0.23.0" @@ -2166,6 +2190,19 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f6d018fb95a0b59f854aed68ecd96ce2b80af7911b92b1fed3c4b1fa516b91b" +[[package]] +name = "fixed" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc715d38bea7b5bf487fcd79bcf8c209f0b58014f3018a7a19c2b855f472048" +dependencies = [ + "az", + "bytemuck", + "half", + "num-traits", + "typenum", +] + [[package]] name = "fixedbitset" version = "0.1.9" @@ -2445,6 +2482,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows 0.48.0", +] + [[package]] name = "generator" version = "0.8.1" @@ -3157,6 +3207,12 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +[[package]] +name = "init_with" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0175f63815ce00183bf755155ad0cb48c65226c5d17a724e369c25418d2b7699" + [[package]] name = "inline_tweak" version = "1.1.1" @@ -3433,11 +3489,25 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kiddo" -version = "0.2.5" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ced2e69cfc5f22f86ccc9ce4ecff9f19917f3083a4bac0f402bdab034d73f1" +checksum = "9d2f8d9e1bc7c6919ad2cdc83472a9a4b5ed2ea2c5392c9514fdf958a7920f9a" dependencies = [ + "az", + "divrem", + "doc-comment", + "elapsed", + "fixed", + "generator 0.7.5", + "init_with", + "itertools 0.12.1", + "log", "num-traits", + "ordered-float 4.2.0", + "sorted-vec", + "tracing", + "tracing-subscriber", + "ubyte", ] [[package]] @@ -3620,7 +3690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ "cfg-if 1.0.0", - "generator", + "generator 0.8.1", "scoped-tls", "tracing", "tracing-subscriber", @@ -6132,6 +6202,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "sorted-vec" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6734caf0b6f51addd5eeacca12fb39b2c6c14e8d4f3ac42f3a78955c0467458" + [[package]] name = "specs" version = "0.20.0" @@ -6880,6 +6956,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ubyte" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" + [[package]] name = "unic-langid" version = "0.9.5" @@ -7559,6 +7641,7 @@ dependencies = [ "enum-map", "enumset", "fallible-iterator", + "fixed", "flate2", "fxhash", "hashbrown 0.13.2", @@ -8536,6 +8619,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows" version = "0.51.1" diff --git a/common/Cargo.toml b/common/Cargo.toml index 88afe19155..a75f4aa4a3 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -65,7 +65,7 @@ csv = { version = "1.1.3", optional = true } # graphviz exporters petgraph = { version = "0.6", optional = true } # K-d trees used for RRT pathfinding -kiddo = { version = "0.2", optional = true } +kiddo = { version = "4.2.0", optional = true } clap = { workspace = true, optional = true } # Data structures diff --git a/common/src/path.rs b/common/src/path.rs index 47fa4e35dd..8c5775646d 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -8,9 +8,12 @@ use hashbrown::hash_map::DefaultHashBuilder; #[cfg(feature = "rrt_pathfinding")] use hashbrown::HashMap; #[cfg(feature = "rrt_pathfinding")] -use kiddo::{distance::squared_euclidean, KdTree}; // For RRT paths (disabled for now) +use kiddo::{float::kdtree::KdTree, nearest_neighbour::NearestNeighbour, SquaredEuclidean}; /* For RRT paths (disabled for now) */ #[cfg(feature = "rrt_pathfinding")] -use rand::distributions::Uniform; +use rand::{ + distributions::{Distribution, Uniform}, + prelude::IteratorRandom, +}; use rand::{thread_rng, Rng}; #[cfg(feature = "rrt_pathfinding")] use std::f32::consts::PI; @@ -442,7 +445,7 @@ impl Chaser { self.last_search_tgt = Some(tgt); // NOTE: Enable air paths when air braking has been figured out - let (path, complete) = /*if cfg!(rrt_pathfinding) && traversal_cfg.can_fly { + let (path, complete) = /*if cfg!(feature = "rrt_pathfinding") && traversal_cfg.can_fly { find_air_path(vol, pos, tgt, &traversal_cfg) } else */{ find_path(&mut self.astar, vol, pos, tgt, &traversal_cfg) @@ -718,7 +721,6 @@ where V: BaseVol + ReadVol, { let radius = traversal_cfg.node_tolerance; - let mut connect = false; let total_dist_sqrd = startf.distance_squared(endf); // First check if a straight line path works if vol @@ -731,7 +733,7 @@ where { let mut path = Vec::new(); path.push(endf.map(|e| e.floor() as i32)); - connect = true; + let connect = true; (Some(path.into_iter().collect()), connect) // Else use RRTs } else { @@ -745,7 +747,7 @@ where //vol.get(*pos).ok().copied().unwrap_or_else(Block::empty). // is_fluid(); }; - informed_rrt_connect(start, end, is_traversable) + informed_rrt_connect(vol, startf, endf, is_traversable, radius) } } @@ -760,11 +762,17 @@ where /// with narrow gaps, such as navigating a maze. /// Returns a path and whether that path is complete or not. #[cfg(feature = "rrt_pathfinding")] -fn informed_rrt_connect( - start: Vec3, - end: Vec3, +fn informed_rrt_connect( + vol: &V, + startf: Vec3, + endf: Vec3, is_valid_edge: impl Fn(&Vec3, &Vec3) -> bool, -) -> (Option>>, bool) { + radius: f32, +) -> (Option>>, bool) +where + V: BaseVol + ReadVol, +{ + const MAX_POINTS: usize = 7000; let mut path = Vec::new(); // Each tree has a vector of nodes @@ -784,20 +792,16 @@ fn informed_rrt_connect( let mut path2 = Vec::new(); // K-d trees are used to find the closest nodes rapidly - let mut kdtree1 = KdTree::new(); - let mut kdtree2 = KdTree::new(); + let mut kdtree1: KdTree = KdTree::with_capacity(MAX_POINTS); + let mut kdtree2: KdTree = KdTree::with_capacity(MAX_POINTS); // Add the start as the first node of the first k-d tree - kdtree1 - .add(&[startf.x, startf.y, startf.z], node_index1) - .unwrap_or_default(); + kdtree1.add(&[startf.x, startf.y, startf.z], node_index1); nodes1.push(startf); node_index1 += 1; // Add the end as the first node of the second k-d tree - kdtree2 - .add(&[endf.x, endf.y, endf.z], node_index2) - .unwrap_or_default(); + kdtree2.add(&[endf.x, endf.y, endf.z], node_index2); nodes2.push(endf); node_index2 += 1; @@ -810,8 +814,8 @@ fn informed_rrt_connect( // sample spheroid volume. This increases in value until a path is found. let mut search_parameter = 0.01; - // Maximum of 7000 iterations - for _i in 0..7000 { + // Maximum of MAX_POINTS iterations + for _i in 0..MAX_POINTS { if connect { break; } @@ -824,17 +828,19 @@ fn informed_rrt_connect( // Find the nearest nodes to the the sampled point let nearest_index1 = kdtree1 - .nearest_one( - &[sampled_point1.x, sampled_point1.y, sampled_point1.z], - &squared_euclidean, - ) - .map_or(0, |n| *n.1); + .nearest_one::(&[ + sampled_point1.x, + sampled_point1.y, + sampled_point1.z, + ]) + .item; let nearest_index2 = kdtree2 - .nearest_one( - &[sampled_point2.x, sampled_point2.y, sampled_point2.z], - &squared_euclidean, - ) - .map_or(0, |n| *n.1); + .nearest_one::(&[ + sampled_point2.x, + sampled_point2.y, + sampled_point2.z, + ]) + .item; let nearest1 = nodes1[nearest_index1]; let nearest2 = nodes2[nearest_index2]; @@ -844,49 +850,51 @@ fn informed_rrt_connect( // Ensure the new nodes are valid/traversable if is_valid_edge(&nearest1, &new_point1) { - kdtree1 - .add(&[new_point1.x, new_point1.y, new_point1.z], node_index1) - .unwrap_or_default(); + kdtree1.add(&[new_point1.x, new_point1.y, new_point1.z], node_index1); nodes1.push(new_point1); parents1.insert(node_index1, nearest_index1); node_index1 += 1; // Check if the trees connect - if let Ok((check, index)) = kdtree2.nearest_one( - &[new_point1.x, new_point1.y, new_point1.z], - &squared_euclidean, - ) { - if check < radius { - let connection = nodes2[*index]; - connection2_idx = *index; - nodes1.push(connection); - connection1_idx = nodes1.len() - 1; - parents1.insert(node_index1, node_index1 - 1); - connect = true; - } + let NearestNeighbour { + distance: check, + item: index, + } = kdtree2.nearest_one::(&[ + new_point1.x, + new_point1.y, + new_point1.z, + ]); + if check < radius { + let connection = nodes2[index]; + connection2_idx = index; + nodes1.push(connection); + connection1_idx = nodes1.len() - 1; + parents1.insert(node_index1, node_index1 - 1); + connect = true; } } // Repeat the validity check for the second tree if is_valid_edge(&nearest2, &new_point2) { - kdtree2 - .add(&[new_point2.x, new_point2.y, new_point1.z], node_index2) - .unwrap_or_default(); + kdtree2.add(&[new_point2.x, new_point2.y, new_point1.z], node_index2); nodes2.push(new_point2); parents2.insert(node_index2, nearest_index2); node_index2 += 1; // Again check for a connection - if let Ok((check, index)) = kdtree1.nearest_one( - &[new_point2.x, new_point2.y, new_point1.z], - &squared_euclidean, - ) { - if check < radius { - let connection = nodes1[*index]; - connection1_idx = *index; - nodes2.push(connection); - connection2_idx = nodes2.len() - 1; - parents2.insert(node_index2, node_index2 - 1); - connect = true; - } + let NearestNeighbour { + distance: check, + item: index, + } = kdtree1.nearest_one::(&[ + new_point2.x, + new_point2.y, + new_point1.z, + ]); + if check < radius { + let connection = nodes1[index]; + connection1_idx = index; + nodes2.push(connection); + connection2_idx = nodes2.len() - 1; + parents2.insert(node_index2, node_index2 - 1); + connect = true; } } // Increase the search parameter to widen the sample volume @@ -915,14 +923,14 @@ fn informed_rrt_connect( // If the trees did not connect, construct a path from the start to // the closest node to the end let mut current_node_index1 = kdtree1 - .nearest_one(&[endf.x, endf.y, endf.z], &squared_euclidean) - .map_or(0, |c| *c.1); + .nearest_one::(&[endf.x, endf.y, endf.z]) + .item; // Attempt to pick a node other than the start node for _i in 0..3 { if current_node_index1 == 0 || nodes1[current_node_index1].distance_squared(startf) < 4.0 { - if let Some(index) = parents1.values().choose(&mut thread_rng()) { + if let Some(index) = parents1.values().into_iter().choose(&mut thread_rng()) { current_node_index1 = *index; } else { break; @@ -973,6 +981,7 @@ fn informed_rrt_connect( node = path[node_idx]; } path = new_path; + (Some(path.into_iter().collect()), connect) } /// Returns a random point within a radially symmetrical ellipsoid with given diff --git a/world/Cargo.toml b/world/Cargo.toml index 1b8fb42406..13db1ce949 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -49,7 +49,8 @@ rayon = { workspace = true } serde = { workspace = true } ron = { workspace = true } # inline_tweak = { workspace = true, features = ["derive"] } -kiddo = "0.2" +kiddo = "4.2.0" +fixed = "1" strum = { workspace = true } # compression benchmarks diff --git a/world/examples/chunk_compression_benchmarks.rs b/world/examples/chunk_compression_benchmarks.rs index ae60471a54..b8191d69eb 100644 --- a/world/examples/chunk_compression_benchmarks.rs +++ b/world/examples/chunk_compression_benchmarks.rs @@ -493,7 +493,8 @@ impl VoxelImageEncoding for MixedEncodingDenseSprites { } } -use kiddo::KdTree; +use fixed::types::U32F0; +use kiddo::fixed::{distance::SquaredEuclidean, kdtree::KdTree}; use rstar::{PointDistance, RTree, RTreeObject, RTreeParams}; #[derive(Debug)] @@ -548,17 +549,16 @@ lazy_static::lazy_static! { }) .collect() }; - pub static ref PALETTE_KDTREE: HashMap> = { + pub static ref PALETTE_KDTREE: HashMap> = { let ron_bytes = include_bytes!("palettes.ron"); let palettes: HashMap>> = ron::de::from_bytes(ron_bytes).expect("palette should parse"); palettes .into_iter() .map(|(k, v)| { - let mut tree = KdTree::new(); + let mut tree: KdTree = KdTree::new(); for (i, rgb) in v.into_iter().enumerate() { - tree.add(&[rgb.r as f32, rgb.g as f32, rgb.b as f32], i as u8) - .expect("kdtree insert should succeed"); + tree.add(&[U32F0::from(rgb.r), U32F0::from(rgb.g), U32F0::from(rgb.b)], i as u16); } (k, tree) }) @@ -570,14 +570,18 @@ pub trait NearestNeighbor { fn nearest_neighbor(&self, x: &Rgb) -> Option; } -impl NearestNeighbor for KdTree { +impl NearestNeighbor for KdTree { fn nearest_neighbor(&self, x: &Rgb) -> Option { - self.nearest_one( - &[x.r as f32, x.g as f32, x.b as f32], - &kiddo::distance::squared_euclidean, + Some( + self.nearest_one::(&[ + U32F0::from(x.r), + U32F0::from(x.g), + U32F0::from(x.b), + ]) + .item + .try_into() + .unwrap(), ) - .map(|(_, i)| *i) - .ok() } } diff --git a/world/examples/world_block_statistics.rs b/world/examples/world_block_statistics.rs index 6d597e5905..1e6e189696 100644 --- a/world/examples/world_block_statistics.rs +++ b/world/examples/world_block_statistics.rs @@ -4,17 +4,28 @@ use common::{ vol::{IntoVolIterator, RectVolSize}, }; use fallible_iterator::FallibleIterator; -use kiddo::{distance::squared_euclidean, KdTree}; +use fixed::{ + types::{extra::U0, U32F0, U8F0}, + FixedU8, +}; +use kiddo::{ + fixed::{distance::SquaredEuclidean, kdtree::KdTree}, + nearest_neighbour::NearestNeighbour, +}; +use num_traits::identities::{One, Zero}; use rayon::{ iter::{IntoParallelIterator, ParallelIterator}, ThreadPoolBuilder, }; use rusqlite::{Connection, ToSql, Transaction, TransactionBehavior}; +//use serde::{Serialize, Deserialize}; use std::{ + cmp::Ordering, collections::{HashMap, HashSet}, error::Error, fs::File, io::Write, + ops::{Add, Mul, SubAssign}, str::FromStr, sync::mpsc, time::{SystemTime, UNIX_EPOCH}, @@ -25,6 +36,75 @@ use veloren_world::{ World, }; +#[derive(Debug, Default, Clone, Copy, Hash, Eq, PartialEq /* , Serialize, Deserialize */)] +struct KiddoRgb(Rgb); + +impl PartialOrd for KiddoRgb { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +impl Ord for KiddoRgb { + fn cmp(&self, other: &Self) -> Ordering { + (self.0.r, self.0.g, self.0.b).cmp(&(other.0.r, other.0.g, other.0.b)) + } +} + +impl Zero for KiddoRgb { + fn zero() -> Self { KiddoRgb(Rgb::zero()) } + + fn is_zero(&self) -> bool { self == &Self::zero() } +} + +impl One for KiddoRgb { + fn one() -> Self { KiddoRgb(Rgb::one()) } + + fn is_one(&self) -> bool { self == &Self::one() } +} + +impl SubAssign for KiddoRgb { + fn sub_assign(&mut self, other: Self) { + *self = Self(Rgb { + r: self.0.r - other.0.r, + g: self.0.g - other.0.g, + b: self.0.b - other.0.b, + }); + } +} + +impl Add for KiddoRgb { + type Output = Self; + + fn add(self, other: Self) -> Self { + Self(Rgb { + r: self.0.r + other.0.r, + g: self.0.g + other.0.g, + b: self.0.b + other.0.b, + }) + } +} + +impl Mul for KiddoRgb { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + Self(Rgb { + r: self.0.r * rhs.0.r, + g: self.0.g * rhs.0.g, + b: self.0.b * rhs.0.b, + }) + } +} + +impl From> for KiddoRgb { + fn from(value: Rgb) -> Self { + Self(Rgb { + r: FixedU8::::from_num(value.r), + g: FixedU8::::from_num(value.g), + b: FixedU8::::from_num(value.b), + }) + } +} + fn block_statistics_db(db_path: &str) -> Result> { let conn = Connection::open(db_path)?; #[rustfmt::skip] @@ -95,31 +175,40 @@ fn generate(db_path: &str, ymin: Option, ymax: Option) -> Result<(), B if existing_chunks.contains(&(x, y)) { return; } - println!("Generating chunk at ({}, {})", x, y); let start_time = SystemTime::now(); if let Ok((chunk, _supplement)) = world.generate_chunk(index.as_index_ref(), Vec2::new(x, y), None, || false, None) { let end_time = SystemTime::now(); - // TODO: can kiddo be made to work without the `Float` bound, so we can use - // `KdTree` (currently it uses 15 bytes per point instead of 3)? - let mut block_colors = KdTree::, 3>::new(); + // TODO: The KiddoRgb wrapper type is necessary to satisfy trait bounds. + // We store the colors twice currently, once as coordinates and another time + // as Content. Kiddo version 5.x is supposed to add the ability to have + // Content be (), which would be useful here. Once that's added, do that. + // TODO: dist_sq is the same type as the coordinates, and since squared + // euclidean distances between colors go way higher than 255, + // we're using a U32F0 here instead of the optimal U8F0 (A U16F0 + // works too, but it could theoretically still overflow so U32F0 + // is used to be safe). If this ever changes, replace U32F0 with + // U8F0. + let mut block_colors: KdTree = KdTree::new(); let mut block_counts = HashMap::new(); let mut sprite_counts = HashMap::new(); let lo = Vec3::new(0, 0, chunk.get_min_z()); let hi = TerrainChunkSize::RECT_SIZE.as_().with_z(chunk.get_max_z()); let height = chunk.get_max_z() - chunk.get_min_z(); for (_, block) in chunk.vol_iter(lo, hi) { - let mut rgb = block.get_color().unwrap_or_else(|| Rgb::new(0, 0, 0)); - let color: [f32; 3] = [rgb.r as _, rgb.g as _, rgb.b as _]; - if let Ok((dist, nearest)) = - block_colors.nearest_one(&color, &squared_euclidean) - { - if dist < (5.0f32).powf(2.0) { - rgb = *nearest; - } + let mut rgb = + KiddoRgb::from(block.get_color().unwrap_or_else(|| Rgb::new(0, 0, 0))); + let color: [U32F0; 3] = [rgb.0.r.into(), rgb.0.g.into(), rgb.0.b.into()]; + let NearestNeighbour { + distance: dist_sq, + item: nearest, + } = block_colors.nearest_one::(&color); + if dist_sq < 5_u32.pow(2) { + rgb = nearest; + } else { + block_colors.add(&color, rgb); } - let _ = block_colors.add(&color, rgb); *block_counts.entry((block.kind(), rgb)).or_insert(0) += 1; if let Some(sprite) = block.get_sprite() { *sprite_counts.entry(sprite).or_insert(0) += 1; @@ -139,6 +228,7 @@ fn generate(db_path: &str, ymin: Option, ymax: Option) -> Result<(), B }); let mut tx = Transaction::new_unchecked(&conn, TransactionBehavior::Deferred)?; let mut i = 0; + let mut j = 0; while let Ok((x, y, height, start_time, end_time, block_counts, sprite_counts)) = rx.recv() { #[rustfmt::skip] let mut insert_block = tx.prepare_cached(" @@ -155,15 +245,14 @@ fn generate(db_path: &str, ymin: Option, ymax: Option) -> Result<(), B REPLACE INTO chunk (xcoord, ycoord, height, start_time, end_time) VALUES (?1, ?2, ?3, ?4, ?5) ")?; - println!("Inserting results for chunk at ({}, {}): {}", x, y, i); for ((kind, color), count) in block_counts.iter() { insert_block.execute([ &x as &dyn ToSql, &y, &format!("{:?}", kind), - &color.r, - &color.g, - &color.b, + &color.0.r.to_num::(), + &color.0.g.to_num::(), + &color.0.b.to_num::(), &count, ])?; } @@ -174,12 +263,13 @@ fn generate(db_path: &str, ymin: Option, ymax: Option) -> Result<(), B let end_time = end_time.duration_since(UNIX_EPOCH)?.as_secs_f64(); insert_chunk.execute([&x as &dyn ToSql, &y, &height, &start_time, &end_time])?; if i % 32 == 0 { - println!("Committing last 32 chunks"); + println!("Committing hunk of 32 chunks: {}", j); drop(insert_block); drop(insert_sprite); drop(insert_chunk); tx.commit()?; tx = Transaction::new_unchecked(&conn, TransactionBehavior::Deferred)?; + j += 1; } i += 1; } @@ -189,12 +279,12 @@ fn generate(db_path: &str, ymin: Option, ymax: Option) -> Result<(), B fn palette(conn: Connection) -> Result<(), Box> { let mut stmt = conn.prepare("SELECT kind, r, g, b, SUM(quantity) FROM block GROUP BY kind, r, g, b")?; - let mut block_colors: HashMap, i64)>> = HashMap::new(); + let mut block_colors: HashMap> = HashMap::new(); let mut rows = stmt.query([])?; while let Some(row) = rows.next()? { let kind = BlockKind::from_str(&row.get::<_, String>(0)?)?; - let rgb: Rgb = Rgb::new(row.get(1)?, row.get(2)?, row.get(3)?); + let rgb: KiddoRgb = KiddoRgb::from(Rgb::new(row.get(1)?, row.get(2)?, row.get(3)?)); let count: i64 = row.get(4)?; block_colors.entry(kind).or_default().push((rgb, count)); } @@ -202,7 +292,7 @@ fn palette(conn: Connection) -> Result<(), Box> { v.sort_by(|a, b| b.1.cmp(&a.1)); } - let mut palettes: HashMap>> = HashMap::new(); + let mut palettes: HashMap> = HashMap::new(); for (kind, colors) in block_colors.iter() { let palette = palettes.entry(*kind).or_default(); if colors.len() <= 256 { @@ -213,24 +303,43 @@ fn palette(conn: Connection) -> Result<(), Box> { continue; } let mut radius = 1024.0; - let mut tree = KdTree::, 3>::new(); + let mut tree: KdTree = KdTree::new(); while palette.len() < 256 { if let Some((color, _)) = colors.iter().find(|(color, _)| { - tree.nearest_one( - &[color.r as f32, color.g as f32, color.b as f32], - &squared_euclidean, - ) - .map(|(dist, _)| dist > radius) - .unwrap_or(true) + tree.nearest_one::(&[ + color.0.r.into(), + color.0.g.into(), + color.0.b.into(), + ]) + .distance + > radius }) { palette.push(*color); - tree.add(&[color.r as f32, color.g as f32, color.b as f32], *color)?; + tree.add( + &[color.0.r.into(), color.0.g.into(), color.0.b.into()], + *color, + ); println!("{:?}, {:?}: {:?}", kind, radius, *color); } else { radius -= 1.0; } } } + let palettes: HashMap>> = palettes + .iter() + .map(|(k, v)| { + ( + *k, + v.iter() + .map(|c| Rgb { + r: c.0.r.to_num::(), + g: c.0.g.to_num::(), + b: c.0.b.to_num::(), + }) + .collect(), + ) + }) + .collect(); let mut f = File::create("palettes.ron")?; let pretty = ron::ser::PrettyConfig::default().depth_limit(2); write!(f, "{}", ron::ser::to_string_pretty(&palettes, pretty)?)?; diff --git a/world/src/site2/plot/gnarling.rs b/world/src/site2/plot/gnarling.rs index 2f75357d21..f321c219c1 100644 --- a/world/src/site2/plot/gnarling.rs +++ b/world/src/site2/plot/gnarling.rs @@ -9,7 +9,7 @@ use common::{ generation::{ChunkSupplement, EntityInfo}, terrain::{Structure as PrefabStructure, StructuresGroup}, }; -use kiddo::{distance::squared_euclidean, KdTree}; +use kiddo::{float::kdtree::KdTree, SquaredEuclidean}; use lazy_static::lazy_static; use rand::prelude::*; use std::collections::HashMap; @@ -1974,13 +1974,14 @@ impl Tunnels { where F: Fn(Vec3, Vec3) -> bool, { + const MAX_POINTS: usize = 7000; let mut nodes = Vec::new(); let mut node_index: usize = 0; // HashMap let mut parents = HashMap::new(); - let mut kdtree = KdTree::new(); + let mut kdtree: KdTree = KdTree::with_capacity(MAX_POINTS); let startf = start.map(|a| (a + 1) as f32); let endf = end.map(|a| (a + 1) as f32); @@ -1995,14 +1996,12 @@ impl Tunnels { startf.z.max(endf.z), ); - kdtree - .add(&[startf.x, startf.y, startf.z], node_index) - .ok()?; + kdtree.add(&[startf.x, startf.y, startf.z], node_index); nodes.push(startf); node_index += 1; let mut connect = false; - for _i in 0..7000 { + for _i in 0..MAX_POINTS { let radius: f32 = rng.gen_range(radius_range.0..radius_range.1); let radius_sqrd = radius.powi(2); if connect { @@ -2013,13 +2012,13 @@ impl Tunnels { rng.gen_range(min.y - 20.0..max.y + 20.0), rng.gen_range(min.z - 20.0..max.z - 7.0), ); - let nearest_index = *kdtree - .nearest_one( - &[sampled_point.x, sampled_point.y, sampled_point.z], - &squared_euclidean, - ) - .ok()? - .1; + let nearest_index = kdtree + .nearest_one::(&[ + sampled_point.x, + sampled_point.y, + sampled_point.z, + ]) + .item; let nearest = nodes[nearest_index]; let dist_sqrd = sampled_point.distance_squared(nearest); let new_point = if dist_sqrd > radius_sqrd { @@ -2031,24 +2030,21 @@ impl Tunnels { nearest.map(|e| e.floor() as i32), new_point.map(|e| e.floor() as i32), ) { - kdtree - .add(&[new_point.x, new_point.y, new_point.z], node_index) - .ok()?; + kdtree.add(&[new_point.x, new_point.y, new_point.z], node_index); nodes.push(new_point); parents.insert(node_index, nearest_index); node_index += 1; } - if new_point.distance_squared(endf) < radius.powi(2) { + if new_point.distance_squared(endf) < radius_sqrd { connect = true; } } let mut path = Vec::new(); - let nearest_index = *kdtree - .nearest_one(&[endf.x, endf.y, endf.z], &squared_euclidean) - .ok()? - .1; - kdtree.add(&[endf.x, endf.y, endf.z], node_index).ok()?; + let nearest_index = kdtree + .nearest_one::(&[endf.x, endf.y, endf.z]) + .item; + kdtree.add(&[endf.x, endf.y, endf.z], node_index); nodes.push(endf); parents.insert(node_index, nearest_index); path.push(endf);