chunkgen 0.2.0

Minecraft world generation library
Documentation
use std::sync::Arc;

use crate::util::shapes::double_list::DoubleList;

pub enum IndexMerger {
    Identical {
        coords: DoubleList,
    },
    DiscreteCube {
        result: DoubleList,
        first_div: usize,
        second_div: usize,
    },
    NonOverlapping {
        lower: Arc<DoubleList>,
        upper: Arc<DoubleList>,
        swap: bool,
    },
    Indirect {
        result: Vec<f64>,
        first_indices: Vec<i32>,
        second_indices: Vec<i32>,
        result_length: usize,
    },
}

const fn gcd(a: usize, b: usize) -> usize {
    if b == 0 { a } else { gcd(b, a % b) }
}

pub const fn lcm(first: usize, second: usize) -> u64 {
    first as u64 * (second / gcd(first, second)) as u64
}

impl IndexMerger {
    pub const fn discrete_cube(first_size: usize, second_size: usize) -> Self {
        let gcd = gcd(first_size, second_size);
        Self::DiscreteCube {
            result: DoubleList::cube_point_range(lcm(first_size, second_size) as usize),
            first_div: first_size / gcd,
            second_div: second_size / gcd,
        }
    }

    pub fn non_overlapping(lower: DoubleList, upper: DoubleList, swap: bool) -> Self {
        Self::NonOverlapping {
            lower: Arc::new(lower),
            upper: Arc::new(upper),
            swap,
        }
    }

    pub fn indirect(
        first: &DoubleList,
        second: &DoubleList,
        first_only_matters: bool,
        second_only_matters: bool,
    ) -> Self {
        let mut last_value = f64::NAN;
        let first_size = first.size();
        let second_size = second.size();
        let capacity = first_size + second_size;
        let mut result = vec![0.0; capacity];
        let mut first_indices = vec![0; capacity];
        let mut second_indices = vec![0; capacity];
        let can_skip_first = !first_only_matters;
        let can_skip_second = !second_only_matters;
        let mut result_index = 0usize;
        let mut first_index = 0usize;
        let mut second_index = 0usize;

        loop {
            let ran_out_of_first = first_index >= first_size;
            let ran_out_of_second = second_index >= second_size;
            if ran_out_of_first && ran_out_of_second {
                return Self::Indirect {
                    result,
                    first_indices,
                    second_indices,
                    result_length: result_index.max(1),
                };
            }

            let chose_first = !ran_out_of_first
                && (ran_out_of_second
                    || first.get(first_index) < second.get(second_index) + 1.0E-7);
            if chose_first {
                first_index += 1;
                if can_skip_first && (second_index == 0 || ran_out_of_second) {
                    continue;
                }
            } else {
                second_index += 1;
                if can_skip_second && (first_index == 0 || ran_out_of_first) {
                    continue;
                }
            }

            let current_first_index = first_index as i32 - 1;
            let current_second_index = second_index as i32 - 1;
            let next_value = if chose_first {
                first.get(current_first_index as usize)
            } else {
                second.get(current_second_index as usize)
            };

            if last_value.is_nan() || last_value < next_value - 1.0E-7 {
                first_indices[result_index] = current_first_index;
                second_indices[result_index] = current_second_index;
                result[result_index] = next_value;
                result_index += 1;
                last_value = next_value;
            } else {
                first_indices[result_index - 1] = current_first_index;
                second_indices[result_index - 1] = current_second_index;
            }
        }
    }

    pub fn size(&self) -> usize {
        match self {
            Self::Identical { coords } => coords.size(),
            Self::DiscreteCube { result, .. } => result.size(),
            Self::NonOverlapping { lower, upper, .. } => lower.size() + upper.size(),
            Self::Indirect { result_length, .. } => *result_length,
        }
    }

    pub fn get_list(&self) -> DoubleList {
        match self {
            Self::Identical { coords } => coords.clone(),
            Self::DiscreteCube { result, .. } => result.clone(),
            Self::NonOverlapping { lower, upper, .. } => DoubleList::NonOverlapping {
                lower: lower.clone(),
                upper: upper.clone(),
            },
            Self::Indirect {
                result,
                result_length,
                ..
            } => {
                if *result_length <= 1 {
                    DoubleList::from_slice(&[0.0])
                } else {
                    DoubleList::from_slice(&result[..*result_length])
                }
            }
        }
    }

    pub fn for_merged_indexes(&self, consumer: &mut dyn FnMut(i32, i32, i32) -> bool) -> bool {
        match self {
            Self::Identical { coords } => {
                let size = coords.size() - 1;
                for index in 0..size as i32 {
                    if !consumer(index, index, index) {
                        return false;
                    }
                }
                true
            }
            Self::DiscreteCube {
                result,
                first_div,
                second_div,
            } => {
                let size = result.size() - 1;
                for index in 0..size as i32 {
                    if !consumer(index / *second_div as i32, index / *first_div as i32, index) {
                        return false;
                    }
                }
                true
            }
            Self::NonOverlapping { lower, upper, swap } => {
                let lower_size = lower.size() as i32;
                let upper_size = upper.size() as i32 - 1;
                let mut merge = |first_index: i32, second_index: i32, result_index: i32| {
                    if *swap {
                        consumer(second_index, first_index, result_index)
                    } else {
                        consumer(first_index, second_index, result_index)
                    }
                };

                for index in 0..lower_size {
                    if !merge(index, -1, index) {
                        return false;
                    }
                }
                for index in 0..upper_size {
                    if !merge(lower_size - 1, index, lower_size + index) {
                        return false;
                    }
                }
                true
            }
            Self::Indirect {
                first_indices,
                second_indices,
                result_length,
                ..
            } => {
                let length = *result_length - 1;
                for index in 0..length {
                    if !consumer(first_indices[index], second_indices[index], index as i32) {
                        return false;
                    }
                }
                true
            }
        }
    }

    pub const fn is_discrete_cube(&self) -> bool {
        matches!(self, Self::DiscreteCube { .. })
    }
}