Skip to main content

loam_egui/
bivector_matrix.rs

1use egui::{Grid, Label, Response, RichText, Ui};
2use loam_math::{Bivector4, Plane4};
3
4/// Cells are degrees per unit time, `+5.1` format.
5pub fn bivector_matrix(ui: &mut Ui, b: &Bivector4) -> Response {
6    Grid::new("loam_egui_bivector_matrix")
7        .num_columns(5)
8        .spacing([8.0, 2.0])
9        .show(ui, |ui| {
10            ui.label("");
11            for axis in AXIS {
12                ui.add(Label::new(RichText::new(axis).monospace().weak()));
13            }
14            ui.end_row();
15            for (row, row_axis) in AXIS.iter().enumerate() {
16                ui.add(Label::new(RichText::new(*row_axis).monospace().weak()));
17                for col in 0..4 {
18                    let text = cell_text(b, row, col);
19                    ui.add(Label::new(RichText::new(text).monospace()));
20                }
21                ui.end_row();
22            }
23        })
24        .response
25}
26
27const AXIS: [&str; 4] = ["x", "y", "z", "w"];
28
29pub fn cell_text(b: &Bivector4, row: usize, col: usize) -> String {
30    if row == col {
31        "0".to_string()
32    } else if row < col {
33        format!("{:>+5.1}", upper_pair(b, row, col).to_degrees())
34    } else {
35        format!("{:>+5.1}", -upper_pair(b, col, row).to_degrees())
36    }
37}
38
39fn upper_pair(b: &Bivector4, row: usize, col: usize) -> f32 {
40    let plane = match (row, col) {
41        (0, 1) => Plane4::Xy,
42        (0, 2) => Plane4::Xz,
43        (0, 3) => Plane4::Xw,
44        (1, 2) => Plane4::Yz,
45        (1, 3) => Plane4::Yw,
46        (2, 3) => Plane4::Zw,
47        _ => unreachable!("upper_pair expects row < col, got ({row}, {col})"),
48    };
49    b.component(plane)
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    fn pure(plane: Plane4) -> Bivector4 {
57        let mut b = Bivector4::ZERO;
58        b.set_component(plane, 1.0);
59        b
60    }
61
62    #[test]
63    fn cell_text_each_basis_plane_lights_correct_cell() {
64        let cases = [
65            (Plane4::Xy, (0, 1)),
66            (Plane4::Xz, (0, 2)),
67            (Plane4::Xw, (0, 3)),
68            (Plane4::Yz, (1, 2)),
69            (Plane4::Yw, (1, 3)),
70            (Plane4::Zw, (2, 3)),
71        ];
72        for (plane, (row, col)) in cases {
73            let b = pure(plane);
74            assert_eq!(
75                cell_text(&b, row, col),
76                "+57.3",
77                "plane {plane:?} should populate ({row}, {col})",
78            );
79            assert_eq!(
80                cell_text(&b, col, row),
81                "-57.3",
82                "plane {plane:?} mirror ({col}, {row}) should be negated",
83            );
84        }
85    }
86}