diff --git a/src/commands/clone.rs b/src/commands/clone.rs index 3903b84..fb81094 100644 --- a/src/commands/clone.rs +++ b/src/commands/clone.rs @@ -1,8 +1,7 @@ use super::{Command, BLOCK_CACHE_SIZE}; use crate::{unwrap_or, opt_unwrap_or}; -use crate::spatial::{Vec3, area_rel_block_overlap, - area_abs_block_overlap}; +use crate::spatial::Vec3; use crate::map_database::MapDatabase; use crate::map_block::{MapBlock, MapBlockError, is_valid_generated, NodeMetadataList, NodeMetadataListExt}; @@ -68,8 +67,7 @@ fn clone(inst: &mut InstBundle) { ); let dst_pos = Vec3::from_block_key(dst_key); - let dst_part_abs = area_abs_block_overlap(&dst_area, dst_pos) - .unwrap(); + let dst_part_abs = dst_area.abs_block_overlap(dst_pos).unwrap(); let src_part_abs = dst_part_abs - offset; let src_blocks_needed = src_part_abs.to_touching_block_area(); @@ -89,11 +87,11 @@ fn clone(inst: &mut InstBundle) { continue ); - let src_frag_abs = area_abs_block_overlap(&src_part_abs, src_pos) + let src_frag_abs = src_part_abs.abs_block_overlap(src_pos) .unwrap(); let src_frag_rel = src_frag_abs - src_pos * 16; - let dst_frag_rel = area_rel_block_overlap( - &(src_frag_abs + offset), dst_pos).unwrap(); + let dst_frag_rel = (src_frag_abs + offset) + .rel_block_overlap(dst_pos).unwrap(); { let _t = tk.get_timer("merge"); diff --git a/src/commands/fill.rs b/src/commands/fill.rs index 57a0ba9..54e9ef0 100644 --- a/src/commands/fill.rs +++ b/src/commands/fill.rs @@ -1,7 +1,7 @@ use super::Command; use crate::unwrap_or; -use crate::spatial::{Vec3, Area, area_rel_block_overlap, area_contains_block}; +use crate::spatial::{Vec3, Area}; use crate::instance::{ArgType, InstBundle}; use crate::map_block::MapBlock; use crate::block_utils::clean_name_id_map; @@ -42,7 +42,7 @@ fn fill(inst: &mut InstBundle) { continue; }); - if area_contains_block(&area, pos) { + if area.contains_block(pos) { let nd = block.node_data.get_mut(); for x in &mut nd.nodes { *x = 0; @@ -51,7 +51,7 @@ fn fill(inst: &mut InstBundle) { block.nimap.0.insert(0, node.to_vec()); count += nd.nodes.len() as u64; } else { - let slice = area_rel_block_overlap(&area, pos).unwrap(); + let slice = area.rel_block_overlap(pos).unwrap(); let fill_id = block.nimap.get_id(&node).unwrap_or_else(|| { let next = block.nimap.get_max_id().unwrap() + 1; block.nimap.0.insert(next, node.to_vec()); diff --git a/src/commands/overlay.rs b/src/commands/overlay.rs index c8713b0..231ab05 100644 --- a/src/commands/overlay.rs +++ b/src/commands/overlay.rs @@ -1,8 +1,7 @@ use super::{Command, BLOCK_CACHE_SIZE}; use crate::{unwrap_or, opt_unwrap_or}; -use crate::spatial::{Vec3, Area, area_rel_block_overlap, - area_abs_block_overlap, area_contains_block, area_touches_block}; +use crate::spatial::{Vec3, Area}; use crate::instance::{ArgType, InstArgs, InstBundle}; use crate::map_database::MapDatabase; use crate::map_block::{MapBlock, MapBlockError, is_valid_generated, @@ -44,8 +43,8 @@ fn overlay_no_offset(inst: &mut InstBundle) { if let Some(area) = inst.args.area { let pos = Vec3::from_block_key(key); - if (!invert && area_contains_block(&area, pos)) - || (invert && !area_touches_block(&area, pos)) + if (!invert && area.contains_block(pos)) + || (invert && !area.touches_block(pos)) { // If possible, copy whole map block. let data = idb.get_block(key).unwrap(); if is_valid_generated(&data) { @@ -66,8 +65,7 @@ fn overlay_no_offset(inst: &mut InstBundle) { let mut dst_meta = NodeMetadataList::deserialize( &dst_block.metadata.get_ref())?; - let block_part = area_rel_block_overlap(&area, pos) - .unwrap(); + let block_part = area.rel_block_overlap(pos).unwrap(); if invert { // For inverted selections, reverse the order of the // overlay operations. @@ -157,7 +155,7 @@ fn overlay_with_offset(inst: &mut InstBundle) { let dst_part_abs = dst_area.map_or( Area::new(dst_pos * 16, dst_pos * 16 + 15), - |ref a| area_abs_block_overlap(a, dst_pos).unwrap() + |ref a| a.abs_block_overlap(dst_pos).unwrap() ); let src_part_abs = dst_part_abs - offset; let src_blocks_needed = src_part_abs.to_touching_block_area(); @@ -177,11 +175,11 @@ fn overlay_with_offset(inst: &mut InstBundle) { continue ); - let src_frag_abs = area_abs_block_overlap(&src_part_abs, src_pos) + let src_frag_abs = src_part_abs.abs_block_overlap(src_pos) .unwrap(); let src_frag_rel = src_frag_abs - src_pos * 16; - let dst_frag_rel = area_rel_block_overlap( - &(src_frag_abs + offset), dst_pos).unwrap(); + let dst_frag_rel = (src_frag_abs + offset) + .rel_block_overlap(dst_pos).unwrap(); merge_blocks(&src_block, &mut dst_block, src_frag_rel, dst_frag_rel); diff --git a/src/commands/replace_nodes.rs b/src/commands/replace_nodes.rs index 5d88f89..dcd660a 100644 --- a/src/commands/replace_nodes.rs +++ b/src/commands/replace_nodes.rs @@ -1,7 +1,6 @@ use super::Command; -use crate::spatial::{Vec3, Area, area_contains_block, area_touches_block, - area_rel_block_overlap}; +use crate::spatial::{Vec3, Area}; use crate::instance::{ArgType, InstArgs, InstBundle}; use crate::map_block::MapBlock; use crate::time_keeper::TimeKeeper; @@ -22,12 +21,11 @@ fn do_replace( let mut count = 0; // Replace nodes in a portion of a map block. - if area.is_some() && area_contains_block(&area.unwrap(), block_pos) != - area_touches_block(&area.unwrap(), block_pos) + if area.is_some() && area.unwrap().contains_block(block_pos) != + area.unwrap().touches_block(block_pos) { let _t = tk.get_timer("replace (partial block)"); - let node_area = area_rel_block_overlap(&area.unwrap(), block_pos) - .unwrap(); + let node_area = area.unwrap().rel_block_overlap(block_pos).unwrap(); let mut new_replace_id = false; let replace_id = block.nimap.get_id(new_node) diff --git a/src/commands/set_param2.rs b/src/commands/set_param2.rs index 0ab1c3f..fcd1305 100644 --- a/src/commands/set_param2.rs +++ b/src/commands/set_param2.rs @@ -1,6 +1,6 @@ use super::Command; -use crate::spatial::{Vec3, Area, area_rel_block_overlap, area_contains_block}; +use crate::spatial::{Vec3, Area}; use crate::instance::{ArgType, InstArgs, InstBundle}; use crate::map_block::MapBlock; use crate::utils::{query_keys, to_bytes, to_slice, fmt_big_num}; @@ -66,9 +66,9 @@ fn set_param2(inst: &mut InstBundle) { let nd = block.node_data.get_mut(); if let Some(area) = inst.args.area - .filter(|a| !area_contains_block(&a, pos)) + .filter(|a| !a.contains_block(pos)) { // Modify part of block - let overlap = area_rel_block_overlap(&area, pos).unwrap(); + let overlap = area.rel_block_overlap(pos).unwrap(); if let Some(nid) = node_id { count += set_in_area_node(&mut block, overlap, nid, param2_val); diff --git a/src/spatial/area.rs b/src/spatial/area.rs index 8b13b08..bc23242 100644 --- a/src/spatial/area.rs +++ b/src/spatial/area.rs @@ -69,6 +69,10 @@ 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 * @@ -81,8 +85,18 @@ impl Area { && self.min.z <= pos.z && pos.z <= self.max.z } - pub fn iterate(&self) -> AreaIterator { - AreaIterator::new(self.min, self.max) + pub fn contains_block(&self, block_pos: Vec3) -> bool { + let corner = block_pos * 16; + self.min.x <= corner.x && corner.x + 15 <= self.max.x + && self.min.y <= corner.y && corner.y + 15 <= self.max.y + && self.min.z <= corner.z && corner.z + 15 <= self.max.z + } + + pub fn touches_block(&self, block_pos: Vec3) -> bool { + let corner = block_pos * 16; + self.min.x <= corner.x + 15 && corner.x <= self.max.x + && self.min.y <= corner.y + 15 && corner.y <= self.max.y + && self.min.z <= corner.z + 15 && corner.z <= self.max.z } pub fn to_contained_block_area(&self) -> Self { @@ -112,6 +126,55 @@ impl Area { ); 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 + } + } + + 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) + }; + + 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 std::ops::Add for Area { @@ -175,4 +238,33 @@ mod tests { iter_area(Area::new(Vec3::new(10, -99, 11), Vec3::new(10, -99, 12))); iter_area(Area::new(Vec3::new(0, -1, -2), Vec3::new(5, 7, 11))); } + + #[test] + fn test_area_containment() { + let area = Area::new(Vec3::new(-1, -32, 16), Vec3::new(30, -17, 54)); + 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.touches_block(pos), touching.contains(pos)); + assert_eq!(area.contains_block(pos), contained.contains(pos)); + } + } + + #[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)))), + (Vec3::new(1, 1, 1), None), + (Vec3::new(-1, 0, 0), + Some(Area::new(Vec3::new(13, 0, 0), Vec3::new(15, 15, 15)))), + ]; + for pair in pairs { + assert_eq!(area.rel_block_overlap(pair.0), pair.1); + } + } } diff --git a/src/spatial/mod.rs b/src/spatial/mod.rs index 3b7fe2b..1bb1ee4 100644 --- a/src/spatial/mod.rs +++ b/src/spatial/mod.rs @@ -1,130 +1,5 @@ -use std::cmp::{min, max}; - mod vec3; mod area; pub use vec3::Vec3; pub use area::Area; - - -// TODO: Should these go in the area impl? -pub fn area_contains_block(area: &Area, block_pos: Vec3) -> bool { - let corner = block_pos * 16; - area.min.x <= corner.x && corner.x + 15 <= area.max.x - && area.min.y <= corner.y && corner.y + 15 <= area.max.y - && area.min.z <= corner.z && corner.z + 15 <= area.max.z -} - - -pub fn area_touches_block(area: &Area, block_pos: Vec3) -> bool { - let corner = block_pos * 16; - area.min.x <= corner.x + 15 && corner.x <= area.max.x - && area.min.y <= corner.y + 15 && corner.y <= area.max.y - && area.min.z <= corner.z + 15 && corner.z <= area.max.z -} - - -pub fn area_abs_block_overlap(area: &Area, block_pos: Vec3) -> Option { - let block_min = block_pos * 16; - let block_max = block_min + 15; - let node_min = Vec3 { - x: max(area.min.x, block_min.x), - y: max(area.min.y, block_min.y), - z: max(area.min.z, block_min.z) - }; - let node_max = Vec3 { - x: min(area.max.x, block_max.x), - y: min(area.max.y, block_max.y), - z: min(area.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(Area {min: node_min, max: node_max}) - } else { - None - } -} - - -pub fn area_rel_block_overlap(area: &Area, block_pos: Vec3) -> Option { - let corner = block_pos * 16; - let rel_min = area.min - corner; - let rel_max = area.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) - }; - - if node_min.x <= node_max.x - && node_min.y <= node_max.y - && node_min.z <= node_max.z - { - Some(Area {min: node_min, max: node_max}) - } else { - None - } -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_area_containment() { - let area = Area::new(Vec3::new(-1, -32, 16), Vec3::new(30, -17, 54)); - let test_blocks = vec![ - // Fully contained - (Vec3::new(0, -2, 1), true, true), - (Vec3::new(0, -2, 2), true, true), - // Partially contained - (Vec3::new(-1, -2, 1), true, false), - (Vec3::new(-1, -2, 2), true, false), - (Vec3::new(-1, -2, 3), true, false), - (Vec3::new(0, -2, 3), true, false), - (Vec3::new(1, -2, 3), true, false), - (Vec3::new(1, -2, 3), true, false), - (Vec3::new(1, -2, 2), true, false), - (Vec3::new(1, -2, 1), true, false), - // Not contained - (Vec3::new(-1, -2, 0), false, false), - (Vec3::new(0, -2, 0), false, false), - (Vec3::new(1, -2, 0), false, false), - (Vec3::new(2, -2, 0), false, false), - (Vec3::new(2, -2, 1), false, false), - (Vec3::new(2, -2, 2), false, false), - (Vec3::new(2, -2, 3), false, false), - ]; - - for (pos, touches, contains) in test_blocks { - assert_eq!(area_touches_block(&area, pos), touches); - assert_eq!(area_contains_block(&area, pos), contains); - } - } - - #[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)))), - (Vec3::new(1, 1, 1), None), - (Vec3::new(-1, 0, 0), - Some(Area::new(Vec3::new(13, 0, 0), Vec3::new(15, 15, 15)))), - ]; - for pair in pairs { - assert_eq!(area_rel_block_overlap(&area, pair.0), pair.1); - } - } -} diff --git a/src/spatial/vec3.rs b/src/spatial/vec3.rs index 59f98b5..e0252cc 100644 --- a/src/spatial/vec3.rs +++ b/src/spatial/vec3.rs @@ -99,6 +99,18 @@ impl std::ops::Sub for Vec3 { } } +impl std::ops::Sub for Vec3 { + type Output = Self; + + fn sub(self, rhs: i32) -> Self { + Self { + x: self.x - rhs, + y: self.y - rhs, + z: self.z - rhs + } + } +} + impl std::ops::Mul for Vec3 { type Output = Self;