Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
"Bash(wc:*)",
"Bash(grep:*)",
"Bash(ls:*)",
"Bash(mkdir:*)"
"Bash(mkdir:*)",
"Bash(git config:*)",
"Bash(/Users/colegentry/Development/UltraLog/.githooks/pre-commit)",
"Bash(./scripts/bump-version.sh:*)"
]
}
}
36 changes: 24 additions & 12 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1351,20 +1351,28 @@ impl eframe::App for UltraLogApp {
.show(ctx, |ui| {
self.render_channel_selection(ui);
});
}

// Bottom panel for timeline scrubber (only in Log Viewer mode)
if self.get_time_range().is_some() && !self.get_selected_channels().is_empty() {
egui::TopBottomPanel::bottom("timeline_panel")
.resizable(false)
.min_height(60.0)
.show(ctx, |ui| {
ui.add_space(5.0);
self.render_record_indicator(ui);
ui.separator();
self.render_timeline_scrubber(ui);
ui.add_space(5.0);
});
// Bottom panel for timeline scrubber (Log Viewer and Histogram modes)
let show_timeline = match self.active_tool {
ActiveTool::LogViewer => {
self.get_time_range().is_some() && !self.get_selected_channels().is_empty()
}
ActiveTool::Histogram => self.get_time_range().is_some(),
ActiveTool::ScatterPlot => false,
};

if show_timeline {
egui::TopBottomPanel::bottom("timeline_panel")
.resizable(false)
.min_height(60.0)
.show(ctx, |ui| {
ui.add_space(5.0);
self.render_record_indicator(ui);
ui.separator();
self.render_timeline_scrubber(ui);
ui.add_space(5.0);
});
}

// Main content area - render based on active tool
Expand All @@ -1388,6 +1396,10 @@ impl eframe::App for UltraLogApp {
ui.add_space(10.0);
self.render_scatter_plot_view(ui);
}
ActiveTool::Histogram => {
ui.add_space(10.0);
self.render_histogram_view(ui);
}
}
});
}
Expand Down
103 changes: 103 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ pub enum ActiveTool {
LogViewer,
/// Scatter plot view for comparing two variables with color coding
ScatterPlot,
/// Histogram view for 2D distribution analysis
Histogram,
}

impl ActiveTool {
Expand All @@ -188,6 +190,7 @@ impl ActiveTool {
match self {
ActiveTool::LogViewer => "Log Viewer",
ActiveTool::ScatterPlot => "Scatter Plots",
ActiveTool::Histogram => "Histogram",
}
}
}
Expand Down Expand Up @@ -227,6 +230,103 @@ pub struct ScatterPlotState {
pub right: ScatterPlotConfig,
}

// ============================================================================
// Histogram Types
// ============================================================================

/// Display mode for histogram cell values
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum HistogramMode {
/// Show average Z-channel value in cells
#[default]
AverageZ,
/// Show hit count (number of data points) in cells
HitCount,
}

/// Grid size options for histogram
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum HistogramGridSize {
/// 16x16 grid
Size16,
/// 32x32 grid
#[default]
Size32,
/// 64x64 grid
Size64,
}

impl HistogramGridSize {
/// Get the numeric size value
pub fn size(&self) -> usize {
match self {
HistogramGridSize::Size16 => 16,
HistogramGridSize::Size32 => 32,
HistogramGridSize::Size64 => 64,
}
}

/// Get display name
pub fn name(&self) -> &'static str {
match self {
HistogramGridSize::Size16 => "16x16",
HistogramGridSize::Size32 => "32x32",
HistogramGridSize::Size64 => "64x64",
}
}
}

/// Statistics for a selected histogram cell
#[derive(Clone, Default)]
pub struct SelectedHistogramCell {
/// X bin index
pub x_bin: usize,
/// Y bin index
pub y_bin: usize,
/// X axis value range (min, max) for this cell
pub x_range: (f64, f64),
/// Y axis value range (min, max) for this cell
pub y_range: (f64, f64),
/// Number of data points in cell
pub hit_count: u32,
/// Sum of weights (for weighted averaging)
pub cell_weight: f64,
/// Variance of Z values
pub variance: f64,
/// Standard deviation of Z values
pub std_dev: f64,
/// Minimum Z value in cell
pub minimum: f64,
/// Mean Z value in cell
pub mean: f64,
/// Maximum Z value in cell
pub maximum: f64,
}

/// Configuration for the histogram view
#[derive(Clone, Default)]
pub struct HistogramConfig {
/// Channel index for X axis
pub x_channel: Option<usize>,
/// Channel index for Y axis
pub y_channel: Option<usize>,
/// Channel index for Z axis (value to average)
pub z_channel: Option<usize>,
/// Display mode (average Z vs hit count)
pub mode: HistogramMode,
/// Grid size
pub grid_size: HistogramGridSize,
/// Currently selected cell (for statistics display)
pub selected_cell: Option<SelectedHistogramCell>,
}

/// State for the histogram view
#[derive(Clone, Default)]
pub struct HistogramState {
/// Histogram configuration
pub config: HistogramConfig,
}

// ============================================================================
// Tab Types
// ============================================================================
Expand All @@ -252,6 +352,8 @@ pub struct Tab {
pub time_range: Option<(f64, f64)>,
/// Scatter plot state for this tab (dual heatmaps)
pub scatter_plot_state: ScatterPlotState,
/// Histogram state for this tab
pub histogram_state: HistogramState,
/// Request to jump the view to a specific time (used for min/max jump buttons)
pub jump_to_time: Option<f64>,
}
Expand All @@ -274,6 +376,7 @@ impl Tab {
chart_interacted: false,
time_range: None,
scatter_plot_state,
histogram_state: HistogramState::default(),
jump_to_time: None,
}
}
Expand Down
Loading
Loading