pm-grid: start main.rs modularization (extract fonts module)
Move the 3x5 LED font (DIGITS, glyph, build_name_cols) into src/fonts.rs. Pure code move, compiler-verified identical behavior; main.rs 1835 -> ~1770 lines. First step of the recommended main.rs split; further extraction (FAT/MSC storage, views) to continue incrementally as those areas are touched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
72dbb2ecd0
commit
bdf69cfd30
2 changed files with 80 additions and 75 deletions
78
rust/pm-grid/src/fonts.rs
Normal file
78
rust/pm-grid/src/fonts.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
//! 3x5 LED font (bit2 = leftmost column) — shared by the views, the scrolling name, and the splash.
|
||||||
|
//! Same bit convention as pico-scroll/app.py: glyph row value's bit `(1 << (2 - col))` lights a column.
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
pub const DIGITS: [[u8; 5]; 10] = [
|
||||||
|
[7, 5, 5, 5, 7], // 0
|
||||||
|
[2, 6, 2, 2, 7], // 1
|
||||||
|
[7, 1, 7, 4, 7], // 2
|
||||||
|
[7, 1, 7, 1, 7], // 3
|
||||||
|
[5, 5, 7, 1, 1], // 4
|
||||||
|
[7, 4, 7, 1, 7], // 5
|
||||||
|
[7, 4, 7, 5, 7], // 6
|
||||||
|
[7, 1, 2, 2, 2], // 7
|
||||||
|
[7, 5, 7, 5, 7], // 8
|
||||||
|
[7, 5, 7, 1, 7], // 9
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Full 3x5 uppercase glyph for a character (used by the scrolling name + boot splash).
|
||||||
|
/// Unknown characters render blank. Digits reuse `DIGITS`.
|
||||||
|
fn glyph(c: char) -> [u8; 5] {
|
||||||
|
match c {
|
||||||
|
'0'..='9' => DIGITS[c as usize - '0' as usize],
|
||||||
|
'A' => [2, 5, 7, 5, 5],
|
||||||
|
'B' => [6, 5, 6, 5, 6],
|
||||||
|
'C' => [3, 4, 4, 4, 3],
|
||||||
|
'D' => [6, 5, 5, 5, 6],
|
||||||
|
'E' => [7, 4, 6, 4, 7],
|
||||||
|
'F' => [7, 4, 6, 4, 4],
|
||||||
|
'G' => [7, 4, 5, 5, 7],
|
||||||
|
'H' => [5, 5, 7, 5, 5],
|
||||||
|
'I' => [7, 2, 2, 2, 7],
|
||||||
|
'J' => [1, 1, 1, 5, 2],
|
||||||
|
'K' => [5, 6, 4, 6, 5],
|
||||||
|
'L' => [4, 4, 4, 4, 7],
|
||||||
|
'M' => [5, 7, 7, 5, 5],
|
||||||
|
'N' => [5, 7, 7, 7, 5],
|
||||||
|
'O' => [2, 5, 5, 5, 2],
|
||||||
|
'P' => [7, 5, 7, 4, 4],
|
||||||
|
'Q' => [2, 5, 5, 6, 3],
|
||||||
|
'R' => [7, 5, 7, 6, 5],
|
||||||
|
'S' => [3, 4, 2, 1, 6],
|
||||||
|
'T' => [7, 2, 2, 2, 2],
|
||||||
|
'U' => [5, 5, 5, 5, 7],
|
||||||
|
'V' => [5, 5, 5, 5, 2],
|
||||||
|
'W' => [5, 5, 7, 7, 5],
|
||||||
|
'X' => [5, 5, 2, 5, 5],
|
||||||
|
'Y' => [5, 5, 2, 2, 2],
|
||||||
|
'Z' => [7, 1, 2, 4, 7],
|
||||||
|
'-' => [0, 0, 7, 0, 0],
|
||||||
|
'/' => [1, 1, 2, 4, 4],
|
||||||
|
'(' => [1, 2, 2, 2, 1],
|
||||||
|
')' => [4, 2, 2, 2, 4],
|
||||||
|
'.' => [0, 0, 0, 0, 2],
|
||||||
|
'+' => [0, 2, 7, 2, 0],
|
||||||
|
'&' => [2, 5, 2, 5, 3],
|
||||||
|
_ => [0, 0, 0, 0, 0], // space + anything unmapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a string into a list of 5-bit column slices (3 columns per glyph + a 1px gap), for the
|
||||||
|
/// horizontal scrollers (Ticker name + boot splash). Lowercase is folded to uppercase.
|
||||||
|
pub fn build_name_cols(name: &str) -> Vec<u8> {
|
||||||
|
let mut cols = Vec::new();
|
||||||
|
for ch in name.chars() {
|
||||||
|
let g = glyph(ch.to_ascii_uppercase());
|
||||||
|
for c in 0..3 {
|
||||||
|
let mut col = 0u8;
|
||||||
|
for r in 0..5 {
|
||||||
|
if g[r] & (1 << (2 - c)) != 0 {
|
||||||
|
col |= 1 << r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cols.push(col);
|
||||||
|
}
|
||||||
|
cols.push(0); // 1px gap between glyphs
|
||||||
|
}
|
||||||
|
cols
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
mod fonts;
|
||||||
|
use fonts::{build_name_cols, DIGITS};
|
||||||
use alloc::collections::VecDeque;
|
use alloc::collections::VecDeque;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
@ -311,63 +313,6 @@ const APP_VERSION: &str = "0.1.0";
|
||||||
const BRIGHTNESS: u8 = 160; // accent
|
const BRIGHTNESS: u8 = 160; // accent
|
||||||
const NAME_BRIGHT: u8 = 120; // ticker name pixels
|
const NAME_BRIGHT: u8 = 120; // ticker name pixels
|
||||||
|
|
||||||
// ============================== FONTS (3x5; bit2 = leftmost column) ==============================
|
|
||||||
// Same bit convention as pico-scroll/app.py: glyph row value's bit (1<<(2-col)) lights that column.
|
|
||||||
const DIGITS: [[u8; 5]; 10] = [
|
|
||||||
[7, 5, 5, 5, 7], // 0
|
|
||||||
[2, 6, 2, 2, 7], // 1
|
|
||||||
[7, 1, 7, 4, 7], // 2
|
|
||||||
[7, 1, 7, 1, 7], // 3
|
|
||||||
[5, 5, 7, 1, 1], // 4
|
|
||||||
[7, 4, 7, 1, 7], // 5
|
|
||||||
[7, 4, 7, 5, 7], // 6
|
|
||||||
[7, 1, 2, 2, 2], // 7
|
|
||||||
[7, 5, 7, 5, 7], // 8
|
|
||||||
[7, 5, 7, 1, 7], // 9
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Full 3x5 uppercase glyph for a character (used by the scrolling name + boot splash).
|
|
||||||
/// Unknown characters render blank. Digits reuse `DIGITS`.
|
|
||||||
fn glyph(c: char) -> [u8; 5] {
|
|
||||||
match c {
|
|
||||||
'0'..='9' => DIGITS[c as usize - '0' as usize],
|
|
||||||
'A' => [2, 5, 7, 5, 5],
|
|
||||||
'B' => [6, 5, 6, 5, 6],
|
|
||||||
'C' => [3, 4, 4, 4, 3],
|
|
||||||
'D' => [6, 5, 5, 5, 6],
|
|
||||||
'E' => [7, 4, 6, 4, 7],
|
|
||||||
'F' => [7, 4, 6, 4, 4],
|
|
||||||
'G' => [7, 4, 5, 5, 7],
|
|
||||||
'H' => [5, 5, 7, 5, 5],
|
|
||||||
'I' => [7, 2, 2, 2, 7],
|
|
||||||
'J' => [1, 1, 1, 5, 2],
|
|
||||||
'K' => [5, 6, 4, 6, 5],
|
|
||||||
'L' => [4, 4, 4, 4, 7],
|
|
||||||
'M' => [5, 7, 7, 5, 5],
|
|
||||||
'N' => [5, 7, 7, 7, 5],
|
|
||||||
'O' => [2, 5, 5, 5, 2],
|
|
||||||
'P' => [7, 5, 7, 4, 4],
|
|
||||||
'Q' => [2, 5, 5, 6, 3],
|
|
||||||
'R' => [7, 5, 7, 6, 5],
|
|
||||||
'S' => [3, 4, 2, 1, 6],
|
|
||||||
'T' => [7, 2, 2, 2, 2],
|
|
||||||
'U' => [5, 5, 5, 5, 7],
|
|
||||||
'V' => [5, 5, 5, 5, 2],
|
|
||||||
'W' => [5, 5, 7, 7, 5],
|
|
||||||
'X' => [5, 5, 2, 5, 5],
|
|
||||||
'Y' => [5, 5, 2, 2, 2],
|
|
||||||
'Z' => [7, 1, 2, 4, 7],
|
|
||||||
'-' => [0, 0, 7, 0, 0],
|
|
||||||
'/' => [1, 1, 2, 4, 4],
|
|
||||||
'(' => [1, 2, 2, 2, 1],
|
|
||||||
')' => [4, 2, 2, 2, 4],
|
|
||||||
'.' => [0, 0, 0, 0, 2],
|
|
||||||
'+' => [0, 2, 7, 2, 0],
|
|
||||||
'&' => [2, 5, 2, 5, 3],
|
|
||||||
_ => [0, 0, 0, 0, 0], // space + anything unmapped
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================== IS31FL3731 DRIVER (bulk framebuffer) ==============================
|
// ============================== IS31FL3731 DRIVER (bulk framebuffer) ==============================
|
||||||
// Faithful port of pico-scroll/app.py's `Matrix`: keep a 144-byte PWM framebuffer and push the
|
// Faithful port of pico-scroll/app.py's `Matrix`: keep a 144-byte PWM framebuffer and push the
|
||||||
// WHOLE thing in one I2C block write per frame (per-pixel I2C is far too slow to animate). The
|
// WHOLE thing in one I2C block write per frame (per-pixel I2C is far too slow to animate). The
|
||||||
|
|
@ -559,24 +504,6 @@ fn master_bar_ns(track: &track_format::Track, tempo: i64) -> i64 {
|
||||||
beat * beats.max(1)
|
beat * beats.max(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_name_cols(name: &str) -> Vec<u8> {
|
|
||||||
let mut cols = Vec::new();
|
|
||||||
for ch in name.chars() {
|
|
||||||
let g = glyph(ch.to_ascii_uppercase());
|
|
||||||
for c in 0..3 {
|
|
||||||
let mut col = 0u8;
|
|
||||||
for r in 0..5 {
|
|
||||||
if g[r] & (1 << (2 - c)) != 0 {
|
|
||||||
col |= 1 << r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cols.push(col);
|
|
||||||
}
|
|
||||||
cols.push(0); // 1px gap between glyphs
|
|
||||||
}
|
|
||||||
cols
|
|
||||||
}
|
|
||||||
|
|
||||||
const SCROLL_GAP: i32 = 13; // blank columns between repeats (>= name region width) for a clean loop
|
const SCROLL_GAP: i32 = 13; // blank columns between repeats (>= name region width) for a clean loop
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue