From 0ce828b6fc8f835ff926ff38b29251bf2ff00e2c Mon Sep 17 00:00:00 2001 From: random-geek <35757396+random-geek@users.noreply.github.com> Date: Tue, 9 Mar 2021 15:44:23 -0800 Subject: [PATCH] spatial: testing and refinement --- src/commands/clone.rs | 2 +- src/commands/overlay.rs | 2 +- src/spatial/area.rs | 225 +++++++++++++++++++++++----------------- src/spatial/vec3.rs | 61 ++++++----- src/utils.rs | 38 ++++--- 5 files changed, 192 insertions(+), 136 deletions(-) diff --git a/src/commands/clone.rs b/src/commands/clone.rs index fb81094..c1d53e8 100644 --- a/src/commands/clone.rs +++ b/src/commands/clone.rs @@ -71,7 +71,7 @@ fn clone(inst: &mut InstBundle) { let src_part_abs = dst_part_abs - offset; let src_blocks_needed = src_part_abs.to_touching_block_area(); - for src_pos in src_blocks_needed.iterate() { + for src_pos in &src_blocks_needed { if !src_pos.is_valid_block_pos() { continue; } diff --git a/src/commands/overlay.rs b/src/commands/overlay.rs index 231ab05..aa58fa6 100644 --- a/src/commands/overlay.rs +++ b/src/commands/overlay.rs @@ -160,7 +160,7 @@ fn overlay_with_offset(inst: &mut InstBundle) { let src_part_abs = dst_part_abs - offset; let src_blocks_needed = src_part_abs.to_touching_block_area(); - for src_pos in src_blocks_needed.iterate() { + for src_pos in &src_blocks_needed { if !src_pos.is_valid_block_pos() { continue; } diff --git a/src/spatial/area.rs b/src/spatial/area.rs index bc23242..ee421dc 100644 --- a/src/spatial/area.rs +++ b/src/spatial/area.rs @@ -1,6 +1,5 @@ -use std::cmp::{min, max}; - use super::Vec3; +use std::cmp::{min, max}; pub struct AreaIterator { @@ -10,6 +9,7 @@ pub struct AreaIterator { } impl AreaIterator { + #[inline] pub fn new(min: Vec3, max: Vec3) -> Self { Self {min, max, pos: min} } @@ -40,22 +40,27 @@ impl Iterator for AreaIterator { } -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Area { pub min: Vec3, pub max: Vec3 } impl Area { + pub fn is_valid(&self) -> bool { + self.min.x <= self.max.x + && self.min.y <= self.max.y + && self.min.z <= self.max.z + } + pub fn new(min: Vec3, max: Vec3) -> Self { - assert!(min.x <= max.x - && min.y <= max.y - && min.z <= max.z); - Self {min, max} + let area = Self {min, max}; + assert!(area.is_valid()); + area } pub fn from_unsorted(a: Vec3, b: Vec3) -> Self { - Area { + Self { min: Vec3 { x: min(a.x, b.x), y: min(a.y, b.y), @@ -69,10 +74,6 @@ impl Area { } } - pub fn iterate(&self) -> AreaIterator { - AreaIterator::new(self.min, self.max) - } - pub fn volume(&self) -> u64 { (self.max.x - self.min.x + 1) as u64 * (self.max.y - self.min.y + 1) as u64 * @@ -99,88 +100,89 @@ impl Area { && self.min.z <= corner.z + 15 && corner.z <= self.max.z } - pub fn to_contained_block_area(&self) -> Self { - let min = Vec3::new( - (self.min.x + 15).div_euclid(16), - (self.min.y + 15).div_euclid(16), - (self.min.z + 15).div_euclid(16) - ); - let max = Vec3::new( - (self.max.x - 15).div_euclid(16), - (self.max.y - 15).div_euclid(16), - (self.max.z - 15).div_euclid(16) - ); - Self {min, max} + pub fn to_contained_block_area(&self) -> Option { + let contained = Self { + min: Vec3 { + x: (self.min.x + 15).div_euclid(16), + y: (self.min.y + 15).div_euclid(16), + z: (self.min.z + 15).div_euclid(16) + }, + max: Vec3 { + x: (self.max.x - 15).div_euclid(16), + y: (self.max.y - 15).div_euclid(16), + z: (self.max.z - 15).div_euclid(16) + } + }; + Some(contained).filter(Self::is_valid) } pub fn to_touching_block_area(&self) -> Self { - let min = Vec3::new( - self.min.x.div_euclid(16), - self.min.y.div_euclid(16), - self.min.z.div_euclid(16) - ); - let max = Vec3::new( - self.max.x.div_euclid(16), - self.max.y.div_euclid(16), - self.max.z.div_euclid(16) - ); - Self {min, max} - } - - pub fn abs_block_overlap(&self, block_pos: Vec3) -> Option { - let block_min = block_pos * 16; - let block_max = block_min + 15; - let node_min = Vec3 { - x: max(self.min.x, block_min.x), - y: max(self.min.y, block_min.y), - z: max(self.min.z, block_min.z) - }; - let node_max = Vec3 { - x: min(self.max.x, block_max.x), - y: min(self.max.y, block_max.y), - z: min(self.max.z, block_max.z) - }; - - if node_min.x <= node_max.x - && node_min.y <= node_max.y - && node_min.z <= node_max.z - { - Some(Self {min: node_min, max: node_max}) - } else { - None + Self { + min: Vec3 { + x: self.min.x.div_euclid(16), + y: self.min.y.div_euclid(16), + z: self.min.z.div_euclid(16) + }, + max: Vec3 { + x: self.max.x.div_euclid(16), + y: self.max.y.div_euclid(16), + z: self.max.z.div_euclid(16) + } } } - pub fn rel_block_overlap(&self, block_pos: Vec3) -> Option { + pub fn abs_block_overlap(&self, block_pos: Vec3) -> Option { + let block_min = block_pos * 16; + let block_max = block_min + 15; + let overlap = Area { + min: Vec3 { + x: max(self.min.x, block_min.x), + y: max(self.min.y, block_min.y), + z: max(self.min.z, block_min.z) + }, + max: Vec3 { + x: min(self.max.x, block_max.x), + y: min(self.max.y, block_max.y), + z: min(self.max.z, block_max.z) + } + }; + Some(overlap).filter(Self::is_valid) + } + + pub fn rel_block_overlap(&self, block_pos: Vec3) -> Option { let corner = block_pos * 16; let rel_min = self.min - corner; let rel_max = self.max - corner; - let node_min = Vec3 { - x: max(rel_min.x, 0), - y: max(rel_min.y, 0), - z: max(rel_min.z, 0) - }; - let node_max = Vec3 { - x: min(rel_max.x, 15), - y: min(rel_max.y, 15), - z: min(rel_max.z, 15) + let overlap = Area { + min: Vec3 { + x: max(rel_min.x, 0), + y: max(rel_min.y, 0), + z: max(rel_min.z, 0) + }, + max: Vec3 { + x: min(rel_max.x, 15), + y: min(rel_max.y, 15), + z: min(rel_max.z, 15) + } }; + Some(overlap).filter(Self::is_valid) + } +} - if node_min.x <= node_max.x - && node_min.y <= node_max.y - && node_min.z <= node_max.z - { - Some(Self {min: node_min, max: node_max}) - } else { - None - } +impl IntoIterator for &Area { + type Item = Vec3; + type IntoIter = AreaIterator; + + fn into_iter(self) -> Self::IntoIter { + AreaIterator::new(self.min, self.max) } } impl std::ops::Add for Area { type Output = Self; + fn add(self, rhs: Vec3) -> Self { - Area { + Self { min: self.min + rhs, max: self.max + rhs } @@ -189,8 +191,9 @@ impl std::ops::Add for Area { impl std::ops::Sub for Area { type Output = Self; + fn sub(self, rhs: Vec3) -> Self { - Area { + Self { min: self.min - rhs, max: self.max - rhs } @@ -203,7 +206,9 @@ mod tests { use super::*; #[test] - fn test_area() { + fn test_areas() { + assert_eq!(Area {min: Vec3::new(0, 3, 1), max: Vec3::new(-1, 4, -2)} + .is_valid(), false); assert_eq!( Area::from_unsorted(Vec3::new(8, 0, -10), Vec3::new(-8, 0, 10)), Area::new(Vec3::new(-8, 0, -10), Vec3::new(8, 0, 10)) @@ -212,18 +217,27 @@ mod tests { Area::from_unsorted(Vec3::new(10, 80, 42), Vec3::new(10, -50, 99)), Area::new(Vec3::new(10, -50, 42), Vec3::new(10, 80, 99)) ); - assert_eq!( Area::new(Vec3::new(0, 0, 0), Vec3::new(0, 0, 0)).volume(), 1); assert_eq!( - Area::new(Vec3::new(0, -9, 14), Vec3::new(19, 0, 17)).volume(), - 800); + Area::new( + Vec3::new(1, -3000, 800), + Vec3::new(4000, 999, 4799) + ).volume(), + 4000u64.pow(3) + ); + } + + #[test] + #[should_panic] + fn test_area_validity() { + Area::new(Vec3::new(0, 3, 1), Vec3::new(0, 2, 3)); } #[test] fn test_area_iteration() { fn iter_area(a: Area) { - let mut iter = a.iterate(); + let mut iter = a.into_iter(); for z in a.min.z..=a.max.z { for y in a.min.y..=a.max.y { for x in a.min.x..=a.max.x { @@ -242,29 +256,54 @@ mod tests { #[test] fn test_area_containment() { let area = Area::new(Vec3::new(-1, -32, 16), Vec3::new(30, -17, 54)); + + assert_eq!(area.contains(Vec3::new(0, -32, 32)), true); + assert_eq!(area.contains(Vec3::new(30, -32, 54)), true); + assert_eq!(area.contains(Vec3::new(30, -17, 55)), false); + assert_eq!(area.contains(Vec3::new(-2, -30, 16)), false); + let contained = Area::new(Vec3::new(0, -2, 1), Vec3::new(0, -2, 2)); let touching = Area::new(Vec3::new(-1, -2, 1), Vec3::new(1, -2, 3)); - for pos in Area::new(touching.min - 2, touching.max + 2).iterate() { + assert_eq!(area.to_contained_block_area(), Some(contained)); + assert_eq!(area.to_touching_block_area(), touching); + + for pos in &Area::new(touching.min - 2, touching.max + 2) { assert_eq!(area.touches_block(pos), touching.contains(pos)); assert_eq!(area.contains_block(pos), contained.contains(pos)); } + + assert_eq!( + Area::new(Vec3::new(16, 0, 1), Vec3::new(31, 15, 15)) + .to_contained_block_area(), + None + ); } #[test] fn test_area_block_overlap() { let area = Area::new(Vec3::new(-3, -3, -3), Vec3::new(15, 15, 15)); - let pairs = vec![ - (Vec3::new(-1, -1, -1), - Some(Area::new(Vec3::new(13, 13, 13), Vec3::new(15, 15, 15)))), - (Vec3::new(0, 0, 0), - Some(Area::new(Vec3::new(0, 0, 0), Vec3::new(15, 15, 15)))), + let pairs = [ + ( + Vec3::new(-1, -1, -1), + Some(Area::new(Vec3::new(-3, -3, -3), Vec3::new(-1, -1, -1))) + ), + ( + Vec3::new(0, 0, 0), + Some(Area::new(Vec3::new(0, 0, 0), Vec3::new(15, 15, 15))) + ), (Vec3::new(1, 1, 1), None), - (Vec3::new(-1, 0, 0), - Some(Area::new(Vec3::new(13, 0, 0), Vec3::new(15, 15, 15)))), + ( + Vec3::new(-1, 0, 0), + Some(Area::new(Vec3::new(-3, 0, 0), Vec3::new(-1, 15, 15))) + ), ]; - for pair in pairs { - assert_eq!(area.rel_block_overlap(pair.0), pair.1); + for pair in &pairs { + assert_eq!(area.abs_block_overlap(pair.0), pair.1); + assert_eq!( + area.rel_block_overlap(pair.0).map(|a| a + (pair.0 * 16)), + pair.1 + ); } } } diff --git a/src/spatial/vec3.rs b/src/spatial/vec3.rs index e0252cc..3b49be1 100644 --- a/src/spatial/vec3.rs +++ b/src/spatial/vec3.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Vec3 { pub x: i32, pub y: i32, @@ -54,7 +54,9 @@ impl Vec3 { && -LIMIT <= self.z && self.z <= LIMIT } - pub fn map(&self, func: fn(i32) -> i32) -> Self { + pub fn map(&self, func: F) -> Self + where F: Fn(i32) -> i32 + { Self { x: func(self.x), y: func(self.y), @@ -148,39 +150,48 @@ mod tests { #[test] fn test_vec3() { - // Test `new` function assert_eq!(Vec3::new(42, 0, -6000), Vec3 {x: 42, y: 0, z: -6000}); + assert_eq!(Vec3::new(-31000, 50, 31000).is_valid_node_pos(), true); + assert_eq!(Vec3::new(-31000, -11, 31001).is_valid_node_pos(), false); + + assert_eq!(Vec3::new(-1937, -5, 1101).is_valid_block_pos(), true); + assert_eq!(Vec3::new(-1937, 1938, -10).is_valid_block_pos(), false); + assert_eq!(Vec3::new(-1938, 4, 1900).is_valid_block_pos(), false); + + let exp = 3; + assert_eq!(Vec3::new(-3, 4, 10).map(|n| n.pow(exp)), + Vec3::new(-27, 64, 1000)); + + assert_eq!(format!("{}", Vec3::new(-1000, 0, 70)), "(-1000, 0, 70)"); + } + + #[test] + fn test_vec3_conversions() { /* Test block key/vector conversions */ const Y_FAC: i64 = 0x1_000; const Z_FAC: i64 = 0x1_000_000; let bk_pairs = [ // Basics - (Vec3 {x: 0, y: 0, z: 0}, 0), - (Vec3 {x: 1, y: 0, z: 0}, 1), - (Vec3 {x: 0, y: 1, z: 0}, 1 * Y_FAC), - (Vec3 {x: 0, y: 0, z: 1}, 1 * Z_FAC), + (Vec3::new(0, 0, 0), 0), + (Vec3::new(1, 0, 0), 1), + (Vec3::new(0, 1, 0), 1 * Y_FAC), + (Vec3::new(0, 0, 1), 1 * Z_FAC), // X/Y/Z Boundaries - (Vec3 {x: -2048, y: 0, z: 0}, -2048), - (Vec3 {x: 2047, y: 0, z: 0}, 2047), - (Vec3 {x: 0, y: -2048, z: 0}, -2048 * Y_FAC), - (Vec3 {x: 0, y: 2047, z: 0}, 2047 * Y_FAC), - (Vec3 {x: 0, y: 0, z: -2048}, -2048 * Z_FAC), - (Vec3 {x: 0, y: 0, z: 2047}, 2047 * Z_FAC), + (Vec3::new(-2048, 0, 0), -2048), + (Vec3::new(2047, 0, 0), 2047), + (Vec3::new(0, -2048, 0), -2048 * Y_FAC), + (Vec3::new(0, 2047, 0), 2047 * Y_FAC), + (Vec3::new(0, 0, -2048), -2048 * Z_FAC), + (Vec3::new(0, 0, 2047), 2047 * Z_FAC), // Extra spicy boundaries - (Vec3 {x: -42, y: 2047, z: -99}, - -42 + 2047 * Y_FAC + -99 * Z_FAC), - (Vec3 {x: 64, y: -2048, z: 22}, - 64 + -2048 * Y_FAC + 22 * Z_FAC), - (Vec3 {x: 2047, y: 555, z: 35}, - 2047 + 555 * Y_FAC + 35 * Z_FAC), - (Vec3 {x: -2048, y: 600, z: -70}, - -2048 + 600 * Y_FAC + -70 * Z_FAC), + (Vec3::new(-42, 2047, -99), -42 + 2047 * Y_FAC + -99 * Z_FAC), + (Vec3::new(64, -2048, 22), 64 + -2048 * Y_FAC + 22 * Z_FAC), + (Vec3::new(2047, 555, 35), 2047 + 555 * Y_FAC + 35 * Z_FAC), + (Vec3::new(-2048, 600, -70), -2048 + 600 * Y_FAC + -70 * Z_FAC), // Multiple boundaries - (Vec3 {x: 2047, y: -2048, z: 16}, - 2047 + -2048 * Y_FAC + 16 * Z_FAC), - (Vec3 {x: -2048, y: 2047, z: 50}, - -2048 + 2047 * Y_FAC + 50 * Z_FAC), + (Vec3::new(2047, -2048, 16), 2047 + -2048 * Y_FAC + 16 * Z_FAC), + (Vec3::new(-2048, 2047, 50), -2048 + 2047 * Y_FAC + 50 * Z_FAC), ]; for pair in &bk_pairs { diff --git a/src/utils.rs b/src/utils.rs index ac8b58b..866ec28 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -38,27 +38,33 @@ pub fn query_keys( if invert == include_partial { a.to_contained_block_area() } else { - a.to_touching_block_area() + Some(a.to_touching_block_area()) } - }); + }).flatten(); + // True if the given area contains no blocks. + let empty_area = area.is_some() && block_area.is_none(); - for (i, (key, data)) in db.iter_rows().enumerate() { - if let Some(a) = &block_area { - let block_pos = Vec3::from_block_key(key); - if a.contains(block_pos) == invert { + if !empty_area || invert { + for (i, (key, data)) in db.iter_rows().enumerate() { + if !empty_area { + if let Some(a) = &block_area { + let block_pos = Vec3::from_block_key(key); + if a.contains(block_pos) == invert { + continue; + } + } + } + if !data_searchers.is_empty() + && !data_searchers.iter().any(|s| s.search_in(&data).is_some()) + { // Data must match at least one search string. continue; } - } - if !data_searchers.is_empty() - && !data_searchers.iter().any(|s| s.search_in(&data).is_some()) - { // Data must match at least one search string. - continue; - } - keys.push(key); + keys.push(key); - // Update total every 1024 iterations. - if i & 1023 == 0 { - status.set_total(keys.len()) + // Update total every 1024 iterations. + if i & 1023 == 0 { + status.set_total(keys.len()) + } } }