use presentar_core::{Canvas, Point, TextStyle};
use presentar_terminal::direct::DirectTerminalCanvas;
use presentar_terminal::Theme;
use crate::app::hardware::LoadMetrics;
use crate::app::CbtopApp;
use crate::config::ComputeBackend;
impl CbtopApp {
pub(super) fn make_bar(value: f64, max: f64, width: usize) -> String {
let filled = ((value / max) * width as f64).round() as usize;
let empty = width.saturating_sub(filled);
format!("[{}{}]", "█".repeat(filled), "░".repeat(empty))
}
pub(super) fn make_mini_bar(value: f64, max: f64, width: usize) -> String {
let filled = ((value / max) * width as f64).round() as usize;
let empty = width.saturating_sub(filled);
format!("{}{}", "▓".repeat(filled), "░".repeat(empty))
}
pub(super) fn make_sparkline(data: &[f64], width: usize) -> String {
const CHARS: [char; 8] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
if data.is_empty() {
return " ".repeat(width);
}
let max = data
.iter()
.cloned()
.fold(f64::NEG_INFINITY, f64::max)
.max(1.0);
let min = data.iter().cloned().fold(f64::INFINITY, f64::min).min(0.0);
let range = (max - min).max(1.0);
let step = data.len().max(1) as f64 / width as f64;
let mut result = String::with_capacity(width);
for i in 0..width {
let idx = (i as f64 * step) as usize;
if idx < data.len() {
let normalized = (data[idx] - min) / range;
let char_idx = (normalized * 7.0).round() as usize;
result.push(CHARS[char_idx.min(7)]);
} else {
result.push(' ');
}
}
result
}
pub(super) fn make_braille_sparkline(data: &[f64], width: usize) -> String {
if data.is_empty() {
return " ".repeat(width);
}
let max = data
.iter()
.cloned()
.fold(f64::NEG_INFINITY, f64::max)
.max(1.0);
let min = data.iter().cloned().fold(f64::INFINITY, f64::min).min(0.0);
let range = (max - min).max(1.0);
const COL1_DOTS: [u8; 4] = [0x40, 0x04, 0x02, 0x01]; const COL2_DOTS: [u8; 4] = [0x80, 0x20, 0x10, 0x08];
let mut result = String::with_capacity(width);
let points_per_char = 2;
let step = data.len().max(1) as f64 / (width * points_per_char) as f64;
for i in 0..width {
let mut pattern: u8 = 0;
let idx1 = ((i * 2) as f64 * step) as usize;
if idx1 < data.len() {
let normalized = (data[idx1] - min) / range;
let dots = (normalized * 4.0).round() as usize;
for d in 0..dots.min(4) {
pattern |= COL1_DOTS[d];
}
}
let idx2 = ((i * 2 + 1) as f64 * step) as usize;
if idx2 < data.len() {
let normalized = (data[idx2] - min) / range;
let dots = (normalized * 4.0).round() as usize;
for d in 0..dots.min(4) {
pattern |= COL2_DOTS[d];
}
}
result.push(char::from_u32(0x2800 + pattern as u32).unwrap_or(' '));
}
result
}
pub(super) fn format_number(n: u64) -> String {
if n >= 1_000_000_000 {
format!("{:.2}B", n as f64 / 1_000_000_000.0)
} else if n >= 1_000_000 {
format!("{:.2}M", n as f64 / 1_000_000.0)
} else if n >= 1_000 {
format!("{:.2}K", n as f64 / 1_000.0)
} else {
n.to_string()
}
}
#[allow(clippy::too_many_arguments)]
pub(super) fn render_status_bar(
canvas: &mut DirectTerminalCanvas,
width: u16,
height: u16,
is_running: bool,
intensity: f64,
backend: ComputeBackend,
show_fps: bool,
frame_avg: f64,
metrics: &LoadMetrics,
theme: &Theme,
) {
let y = height as f32 - 1.0;
let dim_style = TextStyle {
color: theme.dim,
..Default::default()
};
let status = if is_running { "●RUN" } else { "○OFF" };
let status_color = if is_running {
theme.cpu.sample(0.0)
} else {
theme.dim
};
canvas.draw_text(
&format!(" {} ", status),
Point::new(0.0, y),
&TextStyle {
color: status_color,
..Default::default()
},
);
canvas.draw_text(&format!("│ {:?} ", backend), Point::new(6.0, y), &dim_style);
canvas.draw_text(
&format!("│ Int:{:.0}% ", intensity * 100.0),
Point::new(16.0, y),
&dim_style,
);
canvas.draw_text(
&format!(
"│ {:.0} brick/s │ {:.1}μs ",
metrics.bricks_per_second, metrics.avg_latency_us
),
Point::new(28.0, y),
&TextStyle {
color: theme.foreground,
..Default::default()
},
);
let gflops = metrics.ops_per_second / 1_000_000_000.0;
canvas.draw_text(
&format!("│ {:.2} GFLOP/s ", gflops),
Point::new(55.0, y),
&TextStyle {
color: theme.cpu.sample(0.3),
..Default::default()
},
);
if show_fps || frame_avg > 0.0 {
let fps = if frame_avg > 0.0 {
1000.0 / frame_avg
} else {
0.0
};
canvas.draw_text(
&format!("│ {:.0} FPS", fps),
Point::new(width as f32 - 10.0, y),
&dim_style,
);
}
}
}