Skip to main content

geometry_dag/
local_triangle.rs

1//! 局部三角形打包:三个 8-bit 局部索引压入一个 u32(与 TS `localTriangle.ts` 逐位一致)。
2
3use crate::error::{DagError, DagResult};
4
5/// 打包三个局部顶点索引为 `a | (b << 8) | (c << 16)`。
6///
7/// # Errors
8/// 任一索引不在 `0..=255` 时返回 [`DagError::InvalidInput`]。
9pub fn pack_local_triangle(a: u32, b: u32, c: u32) -> DagResult<u32> {
10    for (name, value) in [("a", a), ("b", b), ("c", c)] {
11        if value > 0xff {
12            return Err(DagError::invalid_input(format!(
13                "Local meshlet indices must fit in eight bits; {name} = {value}."
14            )));
15        }
16    }
17    Ok(a | (b << 8) | (c << 16))
18}
19
20/// 解包局部三角形为 `[a, b, c]`。
21///
22/// # Errors
23/// 高 8 位非零时返回 [`DagError::InvalidInput`]。
24pub fn unpack_local_triangle(packed: u32) -> DagResult<[u8; 3]> {
25    if packed > 0x00ff_ffff {
26        return Err(DagError::invalid_input(
27            "Packed local triangle must use only its low 24 bits.",
28        ));
29    }
30    Ok([
31        (packed & 0xff) as u8,
32        ((packed >> 8) & 0xff) as u8,
33        ((packed >> 16) & 0xff) as u8,
34    ])
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn pack_roundtrips() {
43        for a in [0u32, 1, 63, 127, 255] {
44            for b in [0u32, 42, 255] {
45                for c in [0u32, 7, 255] {
46                    let packed = pack_local_triangle(a, b, c).expect("valid indices");
47                    assert_eq!(unpack_local_triangle(packed).expect("valid packed"), [a as u8, b as u8, c as u8]);
48                }
49            }
50        }
51    }
52
53    #[test]
54    fn pack_rejects_out_of_range() {
55        assert!(pack_local_triangle(256, 0, 0).is_err());
56        assert!(pack_local_triangle(0, u32::MAX, 0).is_err());
57    }
58
59    #[test]
60    fn unpack_rejects_dirty_high_bits() {
61        assert!(unpack_local_triangle(0x0100_0000).is_err());
62        assert!(unpack_local_triangle(u32::MAX).is_err());
63    }
64}