azalea_physics/collision/
mergers.rs

1use std::cmp::{self, Ordering};
2
3use azalea_core::math::{EPSILON, gcd, lcm};
4
5use super::CubePointRange;
6
7#[derive(Debug)]
8pub enum IndexMerger {
9    Identical {
10        coords: Vec<f64>,
11    },
12    DiscreteCube {
13        result: CubePointRange,
14        first_div: u32,
15        second_div: u32,
16    },
17    NonOverlapping {
18        lower: Vec<f64>,
19        upper: Vec<f64>,
20        swap: bool,
21    },
22    Indirect {
23        result: Vec<f64>,
24        first_indices: Vec<isize>,
25        second_indices: Vec<isize>,
26        result_length: usize,
27    },
28}
29
30impl IndexMerger {
31    pub fn get_list(&self) -> Vec<f64> {
32        match self {
33            IndexMerger::Identical { coords } => coords.clone(),
34            IndexMerger::DiscreteCube { result, .. } => result.iter(),
35            IndexMerger::NonOverlapping { lower, upper, .. } => (0..self.size())
36                .map(|i| {
37                    if i < lower.len() {
38                        lower[i]
39                    } else {
40                        upper[i - lower.len()]
41                    }
42                })
43                .collect(),
44            IndexMerger::Indirect {
45                result,
46                result_length,
47                ..
48            } => {
49                if *result_length <= 1 {
50                    vec![]
51                } else {
52                    result[..*result_length].to_vec()
53                }
54            }
55        }
56    }
57    pub fn for_merged_indexes(&self, mut consumer: impl IndexConsumer) -> bool {
58        match self {
59            IndexMerger::Identical { coords } => {
60                for coord in 0..(coords.len() - 1) {
61                    if !consumer(coord as i32, coord as i32, coord as i32) {
62                        return false;
63                    }
64                }
65                true
66            }
67            IndexMerger::DiscreteCube {
68                result,
69                first_div,
70                second_div,
71            } => {
72                for var3 in 0..(result.size() - 1) {
73                    if !consumer(
74                        (var3 / second_div).try_into().unwrap(),
75                        (var3 / first_div).try_into().unwrap(),
76                        var3.try_into().unwrap(),
77                    ) {
78                        return false;
79                    }
80                }
81                true
82            }
83            IndexMerger::NonOverlapping { lower, upper, swap } => {
84                if *swap {
85                    for_non_swapped_indexes(lower, upper, move |var1x, var2, var3| {
86                        consumer(var2, var1x, var3)
87                    })
88                } else {
89                    for_non_swapped_indexes(lower, upper, consumer)
90                }
91            }
92            IndexMerger::Indirect {
93                first_indices,
94                second_indices,
95                result_length,
96                ..
97            } => {
98                let var2 = result_length - 1;
99
100                for var3 in 0..var2 {
101                    if !consumer(
102                        first_indices[var3].try_into().unwrap(),
103                        second_indices[var3].try_into().unwrap(),
104                        var3.try_into().unwrap(),
105                    ) {
106                        return false;
107                    }
108                }
109
110                true
111            }
112        }
113    }
114    pub fn size(&self) -> usize {
115        match self {
116            IndexMerger::Identical { coords } => coords.len(),
117            IndexMerger::DiscreteCube { result, .. } => result.size().try_into().unwrap(),
118            IndexMerger::NonOverlapping { lower, upper, .. } => lower.len() + upper.len(),
119            IndexMerger::Indirect { result_length, .. } => *result_length,
120        }
121    }
122
123    pub fn new_discrete_cube(a: u32, b: u32) -> Self {
124        let result = CubePointRange {
125            parts: (u32::try_from(lcm(a, b)).expect("lcm should be able to fit in a u32"))
126                .try_into()
127                .expect("lcm should not be 0"),
128        };
129        let gcd = gcd(a, b);
130        let first_div = a / gcd;
131        let second_div = b / gcd;
132        Self::DiscreteCube {
133            result,
134            first_div,
135            second_div,
136        }
137    }
138
139    pub fn new_indirect(coords1: &[f64], coords2: &[f64], var3: bool, var4: bool) -> Self {
140        let mut var5 = f64::NAN;
141        let coords1_len = coords1.len();
142        let coords2_len = coords2.len();
143        let number_of_indices = coords1_len + coords2_len;
144        let mut result = vec![0.0; number_of_indices];
145        let mut first_indices: Vec<isize> = vec![0; number_of_indices];
146        let mut second_indices: Vec<isize> = vec![0; number_of_indices];
147        let var10 = !var3;
148        let var11 = !var4;
149        let mut var12 = 0;
150        let mut coords1_index = 0;
151        let mut coords2_index = 0;
152
153        loop {
154            let mut iterating_coords1: bool;
155            loop {
156                let at_end_of_coords1 = coords1_index >= coords1_len;
157                let at_end_of_coords2 = coords2_index >= coords2_len;
158                if at_end_of_coords1 && at_end_of_coords2 {
159                    let result_length = cmp::max(1, var12);
160                    return Self::Indirect {
161                        result,
162                        first_indices,
163                        second_indices,
164                        result_length,
165                    };
166                }
167
168                iterating_coords1 = !at_end_of_coords1
169                    && (at_end_of_coords2
170                        || coords1[coords1_index] < coords2[coords2_index] + EPSILON);
171                if iterating_coords1 {
172                    coords1_index += 1;
173                    if !var10 || coords2_index != 0 && !at_end_of_coords2 {
174                        break;
175                    }
176                } else {
177                    coords2_index += 1;
178                    if !var11 || coords1_index != 0 && !at_end_of_coords1 {
179                        break;
180                    }
181                }
182            }
183
184            let var18: isize = (coords1_index as isize) - 1;
185            let var19: isize = (coords2_index as isize) - 1;
186            let var20 = if iterating_coords1 {
187                coords1[usize::try_from(var18).unwrap()]
188            } else {
189                coords2[usize::try_from(var19).unwrap()]
190            };
191            match var5.partial_cmp(&(var20 - EPSILON)) {
192                None | Some(Ordering::Less) => {
193                    result[var12] = var20;
194                    first_indices[var12] = var18;
195                    second_indices[var12] = var19;
196                    var12 += 1;
197                    var5 = var20;
198                }
199                _ => {
200                    first_indices[var12 - 1] = var18;
201                    second_indices[var12 - 1] = var19;
202                }
203            }
204        }
205    }
206}
207
208pub trait IndexConsumer = FnMut(i32, i32, i32) -> bool;
209
210fn for_non_swapped_indexes(lower: &[f64], upper: &[f64], mut consumer: impl IndexConsumer) -> bool {
211    let var2 = lower.len();
212    for var3 in 0..var2 {
213        if !consumer(var3.try_into().unwrap(), -1, var3.try_into().unwrap()) {
214            return false;
215        }
216    }
217    let var3 = upper.len() - 1;
218    for var4 in 0..var3 {
219        if !consumer(
220            (var2 - 1).try_into().unwrap(),
221            var4.try_into().unwrap(),
222            (var2 + var4).try_into().unwrap(),
223        ) {
224            return false;
225        }
226    }
227    true
228}
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233
234    #[test]
235    fn test_indirect_index_merger() {
236        IndexMerger::new_indirect(&[0.0, 1.0], &[0.0, 0.5, 1.0], true, true);
237    }
238}