Wire live serial data into matrix renderer

This commit is contained in:
lennlouisgeek
2026-05-20 01:23:02 +08:00
parent a7b617419d
commit d2c9fad556
18 changed files with 874 additions and 63 deletions

View File

@@ -1,25 +1,29 @@
use std::time::Instant;
use eframe::{egui, egui_wgpu};
use std::sync::Arc;
use crate::connection::ConnectionManager;
use crate::theme::{ENGINEERING_DARK, apply_fonts, apply_theme};
use crate::{
matrix::{MATRIX_COLS, MATRIX_ROWS},
render::{BackgroundRenderResources, WgpuBackgroundCallback},
render::{BackgroundRenderResources, PRESSURE_CELL_COUNT, WgpuBackgroundCallback},
ui::{
ConfigPanelState, ConnectPanelState, FloatingPanelState, draw_config_panel,
draw_connect_panel, draw_scene_panel, draw_stats_panel,
},
};
const DATA_LOG_EVERY_FRAMES: u64 = 30;
pub struct EskinDesktopApp {
connect_panel: FloatingPanelState,
connect_state: ConnectPanelState,
connection: Arc<ConnectionManager>,
pressure_matrix: [f32; PRESSURE_CELL_COUNT],
data_log_frame: u64,
scene_panel: FloatingPanelState,
config_panel: FloatingPanelState,
config_state: ConfigPanelState,
stats_panel: FloatingPanelState,
started_at: Instant,
}
impl EskinDesktopApp {
@@ -46,15 +50,19 @@ impl EskinDesktopApp {
Self {
connect_panel: FloatingPanelState::new([0.0, 0.0], [0.0, 0.0]),
connect_state: ConnectPanelState::default(),
connection: Arc::new(ConnectionManager::new()),
pressure_matrix: [0.0; PRESSURE_CELL_COUNT],
data_log_frame: 0,
scene_panel: FloatingPanelState::new([16.0, 48.0], [16.0, 48.0]),
config_panel: FloatingPanelState::new([840.0, 48.0], [128.0, 48.0]),
config_state: ConfigPanelState::default(),
stats_panel: FloatingPanelState::new([16.0, 520.0], [240.0, 48.0]),
started_at: Instant::now(),
}
}
fn draw_wgpu_background(&mut self, ui: &mut egui::Ui) {
self.update_pressure_matrix();
let rect = ui.max_rect();
let width = rect.width().max(1.0);
let height = rect.height().max(1.0);
@@ -64,11 +72,26 @@ impl EskinDesktopApp {
WgpuBackgroundCallback {
width,
height,
time: self.started_at.elapsed().as_secs_f32(),
pressure: self.pressure_matrix,
},
));
}
fn update_pressure_matrix(&mut self) {
if let Some(sample) = self.connection.take_latest_sample() {
normalize_pressure_sample(
&sample.matrix,
sample.rows,
sample.cols,
&mut self.pressure_matrix,
);
self.data_log_frame += 1;
if self.data_log_frame % DATA_LOG_EVERY_FRAMES == 0 {
log_pressure_sample(&sample.matrix, sample.rows, sample.cols);
}
}
}
fn draw_toolbar(&mut self, ui: &mut egui::Ui) {
egui::Panel::top("main_menu").show_inside(ui, |ui| {
ui.horizontal(|ui| {
@@ -81,13 +104,63 @@ impl EskinDesktopApp {
}
fn draw_floating_panels(&mut self, ctx: &egui::Context) {
draw_connect_panel(ctx, &mut self.connect_panel, &mut self.connect_state);
draw_connect_panel(
ctx,
&mut self.connect_panel,
&mut self.connect_state,
&self.connection,
);
draw_scene_panel(ctx, &mut self.scene_panel);
draw_config_panel(ctx, &mut self.config_panel, &mut self.config_state);
draw_stats_panel(ctx, &mut self.stats_panel);
}
}
fn log_pressure_sample(raw: &[u32], rows: u32, cols: u32) {
let max = raw.iter().copied().max().unwrap_or(0);
let sum: u64 = raw.iter().map(|value| *value as u64).sum();
let non_zero = raw.iter().filter(|value| **value != 0).count();
let preview = raw
.iter()
.take(12)
.map(u32::to_string)
.collect::<Vec<_>>()
.join(", ");
println!(
"[pressure] {rows}x{cols} cells={} non_zero={non_zero} max={max} sum={sum} first=[{preview}]",
raw.len()
);
}
fn normalize_pressure_sample(
raw: &[u32],
rows: u32,
cols: u32,
normalized: &mut [f32; PRESSURE_CELL_COUNT],
) {
normalized.fill(0.0);
let max_value = raw.iter().copied().max().unwrap_or(0);
if max_value == 0 {
return;
}
let src_cols = cols.max(1);
let copy_rows = MATRIX_ROWS.min(rows);
let copy_cols = MATRIX_COLS.min(cols);
for row in 0..copy_rows {
for col in 0..copy_cols {
let src_index = (row * src_cols + col) as usize;
let dst_index = (row * MATRIX_COLS + col) as usize;
if let Some(value) = raw.get(src_index) {
normalized[dst_index] = (*value as f32 / max_value as f32).clamp(0.0, 1.0);
}
}
}
}
impl eframe::App for EskinDesktopApp {
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
let ctx = ui.ctx().clone();