Skip to main content

loam_shape/
projection.rs

1use glam::Vec4;
2
3use crate::polytope::Polytope4;
4
5/// Cell frame and eye in the polytope's unit-circumradius coordinates.
6#[derive(Clone, Copy, Debug, PartialEq)]
7pub struct SchlegelParams {
8    pub cell_index: u32,
9    pub cell_normal: Vec4,
10    pub cell_basis: [Vec4; 3],
11    pub cell_offset: f32,
12    pub viewpoint_distance: f32,
13}
14
15impl SchlegelParams {
16    pub fn new(polytope: Polytope4, cell_index: u32, eye_margin: f32) -> Option<Self> {
17        if !eye_margin.is_finite() || eye_margin <= 0.0 {
18            return None;
19        }
20        let topology = polytope.topology();
21        let cell = topology.cells.get(cell_index as usize)?;
22        let anchor = topology.vertices[*cell.first()? as usize];
23        let centroid = cell
24            .iter()
25            .map(|&index| topology.vertices[index as usize])
26            .sum::<Vec4>()
27            / cell.len() as f32;
28        let cell_offset = centroid.length();
29        let cell_normal = centroid.try_normalize()?;
30        let mut cell_basis = [Vec4::ZERO; 3];
31        let mut count = 0;
32
33        for &index in cell.iter().skip(1) {
34            let delta = topology.vertices[index as usize] - anchor;
35            let mut axis = delta - delta.dot(cell_normal) * cell_normal;
36            for &previous in cell_basis.iter().take(count) {
37                axis -= axis.dot(previous) * previous;
38            }
39            let length = axis.length();
40            if length > 1e-6 {
41                cell_basis[count] = axis / length;
42                count += 1;
43                if count == cell_basis.len() {
44                    break;
45                }
46            }
47        }
48        if count != cell_basis.len() {
49            return None;
50        }
51
52        // Coxeter, Regular Polytopes, 3rd ed., ch. 13.
53        let support = topology
54            .vertices
55            .iter()
56            .map(|vertex| cell_normal.dot(*vertex))
57            .fold(f32::NEG_INFINITY, f32::max);
58        let viewpoint_distance = support + eye_margin;
59        if !viewpoint_distance.is_finite() || viewpoint_distance <= support {
60            return None;
61        }
62        Some(Self {
63            cell_index,
64            cell_normal,
65            cell_basis,
66            cell_offset,
67            viewpoint_distance,
68        })
69    }
70}