spatial: testing and refinement

master
random-geek 2021-03-09 15:44:23 -08:00
parent 6db7c73ff0
commit 0ce828b6fc
5 changed files with 192 additions and 136 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<Self> {
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<Area> {
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<Area> {
pub fn abs_block_overlap(&self, block_pos: Vec3) -> Option<Self> {
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<Self> {
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<Vec3> 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<Vec3> for Area {
impl std::ops::Sub<Vec3> 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
);
}
}
}

View File

@ -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<F>(&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 {

View File

@ -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())
}
}
}