Skip to main content

sim_lib_pitch_serial/
render.rs

1//! Structured and ASCII projections of a row matrix.
2
3use std::fmt::Write;
4
5use crate::{
6    MatrixCoordinate, ROW_MATRIX_SIZE, RowLabelConvention, RowMatrix, RowMatrixCell,
7    RowMatrixEdgeLabels, RowOperation, ToneRow,
8};
9
10/// A complete structured projection of a [`RowMatrix`].
11///
12/// Cells are row-major and retain explicit coordinates. Source identity,
13/// operations, edge labels, and the selected convention travel with the data,
14/// so a consumer never has to infer matrix semantics from display text.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct RowMatrixData {
17    source: ToneRow,
18    convention: RowLabelConvention,
19    cells: Vec<RowMatrixCell>,
20    row_operations: [RowOperation; ROW_MATRIX_SIZE],
21    column_operations: [RowOperation; ROW_MATRIX_SIZE],
22    edge_labels: RowMatrixEdgeLabels,
23}
24
25impl RowMatrixData {
26    /// Returns the matrix's explicit source row.
27    pub const fn source(&self) -> &ToneRow {
28        &self.source
29    }
30
31    /// Returns the convention used to derive the edge labels.
32    pub const fn convention(&self) -> RowLabelConvention {
33        self.convention
34    }
35
36    /// Returns the 144 coordinate-bearing cells in row-major order.
37    pub fn cells(&self) -> &[RowMatrixCell] {
38        &self.cells
39    }
40
41    /// Returns a cell by its validated coordinate.
42    pub fn cell(&self, coordinate: MatrixCoordinate) -> &RowMatrixCell {
43        &self.cells[coordinate.row() * ROW_MATRIX_SIZE + coordinate.column()]
44    }
45
46    /// Returns the P operations for rows ordered top to bottom.
47    pub const fn row_operations(&self) -> &[RowOperation; ROW_MATRIX_SIZE] {
48        &self.row_operations
49    }
50
51    /// Returns the I operations for columns ordered left to right.
52    pub const fn column_operations(&self) -> &[RowOperation; ROW_MATRIX_SIZE] {
53        &self.column_operations
54    }
55
56    /// Returns all labels on the top, right, bottom, and left edges.
57    pub const fn edge_labels(&self) -> &RowMatrixEdgeLabels {
58        &self.edge_labels
59    }
60}
61
62impl RowMatrix {
63    /// Projects this matrix to structured, coordinate-bearing data.
64    pub fn render_data(&self) -> RowMatrixData {
65        let cells = (0..ROW_MATRIX_SIZE)
66            .flat_map(|row| {
67                (0..ROW_MATRIX_SIZE).map(move |column| {
68                    let coordinate = MatrixCoordinate::new(row, column)
69                        .expect("fixed matrix iteration always yields a valid coordinate");
70                    self.cell(coordinate)
71                })
72            })
73            .collect();
74        let row_operations = std::array::from_fn(|row| {
75            self.row_operation(row)
76                .expect("fixed matrix iteration always yields a row operation")
77        });
78        let column_operations = std::array::from_fn(|column| {
79            self.column_operation(column)
80                .expect("fixed matrix iteration always yields a column operation")
81        });
82
83        RowMatrixData {
84            source: self.source().clone(),
85            convention: self.convention(),
86            cells,
87            row_operations,
88            column_operations,
89            edge_labels: *self.edge_labels(),
90        }
91    }
92
93    /// Renders a self-describing ASCII matrix from the structured projection.
94    pub fn render_ascii(&self) -> String {
95        let data = self.render_data();
96        let mut output = String::new();
97        writeln!(output, "label-convention: {}", data.convention().as_str())
98            .expect("writing to a String cannot fail");
99        write!(output, "source:").expect("writing to a String cannot fail");
100        for class in data.source().classes() {
101            write!(output, " {:>2}", class.value()).expect("writing to a String cannot fail");
102        }
103        output.push('\n');
104
105        output.push_str("     ");
106        for label in data.edge_labels().top() {
107            write!(output, " {:>4}", label).expect("writing to a String cannot fail");
108        }
109        output.push('\n');
110
111        for row in 0..ROW_MATRIX_SIZE {
112            write!(output, "{:>4} |", data.edge_labels().left()[row])
113                .expect("writing to a String cannot fail");
114            for column in 0..ROW_MATRIX_SIZE {
115                let coordinate = MatrixCoordinate::new(row, column)
116                    .expect("fixed matrix iteration always yields a valid coordinate");
117                write!(output, " {:>4}", data.cell(coordinate).class().value())
118                    .expect("writing to a String cannot fail");
119            }
120            writeln!(output, " | {:<4}", data.edge_labels().right()[row])
121                .expect("writing to a String cannot fail");
122        }
123
124        output.push_str("     ");
125        for label in data.edge_labels().bottom() {
126            write!(output, " {:>4}", label).expect("writing to a String cannot fail");
127        }
128        output.push('\n');
129        output
130    }
131}