vor 0.2.1

Cross-platform performance instrumentation with an in-app egui panel and live system and GPU metrics.
Documentation
use std::ops::Range;

use egui::Rect;

/// Per-row x-axis layout for the bars + metric plots.
///
/// Without a selection, the row reserves `history_capacity` slots
/// across `rect` and right-aligns the present samples so older
/// frames scroll left as new ones arrive. With a selection
/// `(lo, hi)`, the row zooms to that index range only — the
/// selection fills `rect` and `range` covers exactly the visible
/// indices.
pub(super) struct PlotLayout {
    pub(super) range: Range<usize>,
    pub(super) slot_w: f32,
    pub(super) base_x: f32,
}

impl PlotLayout {
    pub(super) fn new(
        rect: Rect,
        present_len: usize,
        selection: Option<(usize, usize)>,
        history_capacity: usize,
    ) -> Self {
        match selection {
            Some((lo, hi)) if lo < present_len => {
                let hi = hi.min(present_len - 1);
                let vis_n = (hi - lo + 1).max(1);
                let slot_w = rect.width() / vis_n as f32;
                Self {
                    range: lo..hi + 1,
                    slot_w,
                    base_x: rect.left(),
                }
            }
            _ => {
                let slot_w = rect.width() / history_capacity as f32;
                Self {
                    range: 0..present_len,
                    slot_w,
                    base_x: rect.right() - present_len as f32 * slot_w,
                }
            }
        }
    }

    /// Center of the bar/slot for index `idx`, or `None` if `idx`
    /// is outside the visible range.
    pub(super) fn cursor_x(&self, idx: usize) -> Option<f32> {
        if !self.range.contains(&idx) {
            return None;
        }
        Some(self.base_x + (idx - self.range.start) as f32 * self.slot_w + self.slot_w * 0.5)
    }

    /// The slot index under x-coordinate `x`, or `None` if left of the
    /// first slot or past the visible range. Maps a pointer to the
    /// frame it hovers, for the bars' pin / select interaction.
    pub(super) fn hover_to_idx(&self, x: f32) -> Option<usize> {
        let rel = (x - self.base_x) / self.slot_w;
        if rel < 0.0 {
            return None;
        }
        let idx = self.range.start + rel as usize;
        (idx < self.range.end).then_some(idx)
    }
}