From c42a898c2ccdfe7ebaee26bf06e7a15ce47c4e49 Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 29 Jun 2022 18:27:21 -0500 Subject: [PATCH] binary_search --- azalea-core/src/direction.rs | 29 ++++--- azalea-core/src/lib.rs | 17 ++++ azalea-physics/src/discrete_voxel_shape.rs | 16 +--- azalea-physics/src/shape.rs | 90 ++++++++++------------ 4 files changed, 79 insertions(+), 73 deletions(-) diff --git a/azalea-core/src/direction.rs b/azalea-core/src/direction.rs index ffb517f4..c7b89784 100644 --- a/azalea-core/src/direction.rs +++ b/azalea-core/src/direction.rs @@ -14,16 +14,16 @@ pub enum Direction { #[derive(Clone, Copy, Debug)] pub enum Axis { - X, - Y, - Z, + X = 0, + Y = 1, + Z = 2, } #[derive(Clone, Copy, Debug)] pub enum AxisCycle { - None, - Forward, - Backward, + None = 0, + Forward = 1, + Backward = 2, } impl Axis { @@ -36,6 +36,15 @@ impl Axis { Axis::Z => z, } } + + pub fn from_ordinal(ordinal: u32) -> Self { + match ordinal { + 0 => Axis::X, + 1 => Axis::Y, + 2 => Axis::Z, + _ => panic!("Invalid ordinal {}", ordinal), + } + } } impl AxisCycle { @@ -57,11 +66,11 @@ impl AxisCycle { Self::Backward => Self::Forward, } } - pub fn cycle(self, axis: Axis) -> Self { + pub fn cycle(self, axis: Axis) -> Axis { match self { - Self::None => Self::None, - Self::Forward => Self::from_ordinal(floor_mod(axis as i32 + 1, 3)), - Self::Backward => Self::from_ordinal(floor_mod(axis as i32 - 1, 3)), + Self::None => axis, + Self::Forward => Axis::from_ordinal(floor_mod(axis as i32 + 1, 3)), + Self::Backward => Axis::from_ordinal(floor_mod(axis as i32 - 1, 3)), } } } diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs index cea97458..7d0df41c 100755 --- a/azalea-core/src/lib.rs +++ b/azalea-core/src/lib.rs @@ -41,3 +41,20 @@ pub fn floor_mod(x: i32, y: u32) -> u32 { x as u32 % y } } + +// TODO: make this generic +pub fn binary_search(mut min: u32, max: u32, predicate: &dyn Fn(u32) -> bool) -> u32 { + let mut diff = max - min; + while diff > 0 { + let diff_mid = diff / 2; + let mid = min + diff_mid; + if predicate(mid) { + diff = diff_mid; + } else { + min = mid + 1; + diff -= diff_mid + 1; + } + } + + min +} diff --git a/azalea-physics/src/discrete_voxel_shape.rs b/azalea-physics/src/discrete_voxel_shape.rs index 13c22de2..0cf8ddf0 100644 --- a/azalea-physics/src/discrete_voxel_shape.rs +++ b/azalea-physics/src/discrete_voxel_shape.rs @@ -1,4 +1,4 @@ -use azalea_core::BitSet; +use azalea_core::{Axis, BitSet}; // TODO: every impl of DiscreteVoxelShape could be turned into a single enum as an optimization @@ -19,9 +19,7 @@ pub trait DiscreteVoxelShape { // } // return false; // } - fn x_size(&self) -> u32; - fn y_size(&self) -> u32; - fn z_size(&self) -> u32; + fn size(&self, axis: Axis) -> u32; fn first_full_x(&self) -> u32; fn first_full_y(&self) -> u32; @@ -126,14 +124,8 @@ impl BitSetDiscreteVoxelShape { } impl DiscreteVoxelShape for BitSetDiscreteVoxelShape { - fn x_size(&self) -> u32 { - self.x_size - } - fn y_size(&self) -> u32 { - self.y_size - } - fn z_size(&self) -> u32 { - self.z_size + fn size(&self, axis: Axis) -> u32 { + axis.choose(self.x_size, self.y_size, self.z_size) } fn first_full_x(&self) -> u32 { diff --git a/azalea-physics/src/shape.rs b/azalea-physics/src/shape.rs index a9f0eb7e..ff74111c 100644 --- a/azalea-physics/src/shape.rs +++ b/azalea-physics/src/shape.rs @@ -1,7 +1,7 @@ -use azalea_core::{Axis, AxisCycle}; +use azalea_core::{binary_search, Axis, AxisCycle}; use crate::{BitSetDiscreteVoxelShape, DiscreteVoxelShape, AABB, EPSILON}; -use std::ops::Add; +use std::{cmp, ops::Add}; pub struct Shapes {} @@ -40,9 +40,7 @@ impl Shapes { pub trait VoxelShape { fn shape(&self) -> Box; - fn get_x_coords(&self) -> Vec; - fn get_y_coords(&self) -> Vec; - fn get_z_coords(&self) -> Vec; + fn get_coords(&self, axis: Axis) -> Vec; // TODO: optimization: should this be changed to return ArrayVoxelShape? // i might change the implementation of empty_shape in the future so not 100% sure @@ -52,12 +50,22 @@ pub trait VoxelShape { } Box::new(ArrayVoxelShape::new( self.shape(), - self.get_x_coords().iter().map(|c| c + x).collect(), - self.get_y_coords().iter().map(|c| c + y).collect(), - self.get_z_coords().iter().map(|c| c + z).collect(), + self.get_coords(Axis::X).iter().map(|c| c + x).collect(), + self.get_coords(Axis::Y).iter().map(|c| c + y).collect(), + self.get_coords(Axis::Z).iter().map(|c| c + z).collect(), )) } + fn get(&self, axis: Axis, index: usize) -> f64 { + self.get_coords(axis)[index] + } + + fn find_index(&self, axis: Axis, coord: f64) -> u32 { + binary_search(0, self.shape().size(axis) + 1, &|t| { + coord < self.get(axis, t as usize) + }) - 1 + } + fn collide(&self, axis: &Axis, entity_box: &AABB, movement: f64) -> f64 { self.collide_x(AxisCycle::between(*axis, Axis::X), entity_box, movement) } @@ -77,25 +85,31 @@ pub trait VoxelShape { let z_axis = inverse_axis_cycle.cycle(Axis::Z); // i gave up on names at this point (these are the obfuscated names from fernflower) - let var9 = entity_box.max(x_axis); - let var11 = entity_box.min(x_axis); + let var9 = entity_box.max(&x_axis); + let var11 = entity_box.min(&x_axis); let var13 = self.find_index(x_axis, var11 + EPSILON); let var14 = self.find_index(x_axis, var9 - EPSILON); - let var15 = cmp::max(0, self.find_index(y_axis, entity_box.min(y_axis) + EPSILON)); + let var15 = cmp::max( + 0, + self.find_index(y_axis, entity_box.min(&y_axis) + EPSILON), + ); let var16 = cmp::min( - self.shape().get_size(y_axis), - self.find_index(y_axis, entity_box.max(y_axis) - EPSILON) + 1, + self.shape().size(y_axis), + self.find_index(y_axis, entity_box.max(&y_axis) - EPSILON) + 1, ); - let var17 = cmp::max(0, self.find_index(z_axis, entity_box.min(z_axis) + EPSILON)); + let var17 = cmp::max( + 0, + self.find_index(z_axis, entity_box.min(&z_axis) + EPSILON), + ); let var18 = cmp::min( - self.shape().get_size(z_axis), - self.find_index(z_axis, entity_box.max(z_axis) - EPSILON) + 1, + self.shape().size(z_axis), + self.find_index(z_axis, entity_box.max(&z_axis) - EPSILON) + 1, ); - let var19 = self.shape().get_size(x_axis); + let var19 = self.shape().size(x_axis); if movement > 0. { for var20 in var14 + 1..var19 { @@ -105,7 +119,7 @@ pub trait VoxelShape { .shape() .is_full_wide(inverse_axis_cycle, var20, var21, var22) { - let var23 = self.get(x_axis, var20) - var9; + let var23 = self.get(x_axis, var20 as usize) - var9; if var23 >= -EPSILON { movement = cmp::min(movement, var23); } @@ -158,9 +172,9 @@ impl ArrayVoxelShape { ys: Vec, zs: Vec, ) -> Self { - let x_size = shape.x_size() + 1; - let y_size = shape.y_size() + 1; - let z_size = shape.z_size() + 1; + let x_size = shape.size(Axis::X) + 1; + let y_size = shape.size(Axis::Y) + 1; + let z_size = shape.size(Axis::Z) + 1; // Lengths of point arrays must be consistent with the size of the VoxelShape. assert_eq!(x_size, xs.len() as u32); @@ -188,16 +202,8 @@ impl VoxelShape for ArrayVoxelShape { self.shape.clone() } - fn get_x_coords(&self) -> Vec { - self.xs.clone() - } - - fn get_y_coords(&self) -> Vec { - self.ys.clone() - } - - fn get_z_coords(&self) -> Vec { - self.zs.clone() + fn get_coords(&self, axis: Axis) -> Vec { + axis.choose(self.xs.clone(), self.ys.clone(), self.zs.clone()) } } @@ -206,26 +212,8 @@ impl VoxelShape for CubeVoxelShape { self.shape.clone() } - fn get_x_coords(&self) -> Vec { - let size = self.shape.x_size(); - let mut parts = Vec::with_capacity(size as usize); - for i in 0..size { - parts.push(i as f64 / size as f64); - } - parts - } - - fn get_y_coords(&self) -> Vec { - let size = self.shape.y_size(); - let mut parts = Vec::with_capacity(size as usize); - for i in 0..size { - parts.push(i as f64 / size as f64); - } - parts - } - - fn get_z_coords(&self) -> Vec { - let size = self.shape.z_size(); + fn get_coords(&self, axis: Axis) -> Vec { + let size = self.shape.size(axis); let mut parts = Vec::with_capacity(size as usize); for i in 0..size { parts.push(i as f64 / size as f64);