diff --git a/docs/rust-port.md b/docs/rust-port.md index 3e96c32..6865581 100644 --- a/docs/rust-port.md +++ b/docs/rust-port.md @@ -160,11 +160,12 @@ CircuitPython `Matrix`; per-pixel I²C is too slow to animate), the **polymeter `track-format::schedule::lane_durs` (the cross-impl contract) with per-lane step clocks + ramp + gap-trainer, **4-button input** (A tap=play/stop, hold=cycle view; B tap=next track, hold=next set list; X/Y=tempo ∓ with auto-repeat), the **built-in set lists**, and three LED views: -- **Ticker** (default): track name infinite-scrolls across the left (cols 0–10, full height); BPM is - pinned right, **rotated 90° CCW** — a vertical hundreds **dot-bar** in col 11 (one dot per 100) + - the last two digits rotated into cols 12–16 (tens bottom, units top). So `130` → 1 dot + rotated - "30". This is the user-designed landscape readout. Rotation/geometry verified off-bench with an - ASCII replica of `draw_ticker`. +- **Ticker** (default): a **beat strip** on the top row (cols 0–10) — faint ticks at each beat + a + bright playhead at the master lane's current step; the track name infinite-scrolls below it (cols + 0–10, rows 2–6); BPM is pinned right, **rotated 90° CCW** — a vertical hundreds **dot-bar** in col + 11 (one dot per 100) + the last two digits rotated into cols 12–16 (tens bottom, units top). So + `130` → 1 dot + rotated "30". This is the user-designed landscape readout. Layout/rotation verified + off-bench with an ASCII replica of `draw_ticker`. Whole matrix strobes white on the downbeat. - **Grid** (lanes×steps + playhead) and **Pendulum** (bouncing arm + beat ticks) — ports of `_render_grid` / `_render_pendulum`. Boot splash scrolls "PM-G1 GRID" (liveness + pixel-map check). diff --git a/rust/pm-grid/src/main.rs b/rust/pm-grid/src/main.rs index 684e89d..f476191 100644 --- a/rust/pm-grid/src/main.rs +++ b/rust/pm-grid/src/main.rs @@ -491,11 +491,26 @@ fn render(m: &mut Matrix, app: &App, now_ns: i64) { m.show(); } -/// Ticker: track name infinite-scrolls across the left (cols 0..=10, full height), BPM is pinned to -/// the right rotated 90° CCW — a vertical "hundreds dot-bar" in col 11 (one dot per 100) plus the -/// last two digits rotated into cols 12..=16 (tens at the bottom, units on top; reads bottom→top). +/// Ticker: a beat strip runs along the top row (cols 0..=10); the track name infinite-scrolls below +/// it (cols 0..=10, rows 2..=6); BPM is pinned to the right rotated 90° CCW — a vertical "hundreds +/// dot-bar" in col 11 (one dot per 100) plus the last two digits rotated into cols 12..=16 (tens at +/// the bottom, units on top; reads bottom→top). fn draw_ticker(m: &mut Matrix, app: &App) { - // scrolling name on rows 1..=5 + // top-row beat strip (cols 0..=10): faint marks at each beat (every `sub` steps), a bright + // playhead at the master lane's current step — spread across the full name-region width. + if let Some(master) = app.track.lanes.first() { + let steps = master.levels.len().max(1) as i32; + let sub = master.sub.max(1) as i32; + for s in 0..steps { + if s % sub == 0 { + m.set_max(s * 11 / steps, 0, 24); // beat tick + } + } + if app.playing && app.step[0] >= 0 { + m.set(app.step[0] * 11 / steps, 0, 255); // live playhead + } + } + // scrolling name on rows 2..=6 (shifted down one to clear the beat strip) let total = app.scroll_total.max(1); for sx in 0..=10i32 { let i = ((app.scroll_off + sx) % total + total) % total; @@ -506,7 +521,7 @@ fn draw_ticker(m: &mut Matrix, app: &App) { }; for r in 0..5i32 { if colbits & (1 << r) != 0 { - m.set(sx, 1 + r, NAME_BRIGHT); + m.set(sx, 2 + r, NAME_BRIGHT); } } }