Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add requirement for 'shift' before triggering a click for click delta #172

Merged
merged 9 commits into from
Jan 28, 2025
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [1.6.3]

### Changed

- Click delta now needs the `shift` modifier to place a point for measuring distance between points
- Loaded log IDs are now guaranteed to be unique
- When a logs settings/metadata window is open, the plots from that log is highlighted
- Allow highlighting of plots from 2 logs by having the settings/metadata from one log open and hovering on another ones name

### Fixed

- Line width would not apply when displaying plots where down sampling was manually disabled

## [1.6.2]

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "plotinator3000"
description = "Log viewer app for viewing plots of data from projects such as motor and generator control"
authors = ["SkyTEM Surveys", "Marc Beck KΓΆnig"]
version = "1.6.2"
version = "1.6.3"
edition = "2021"
repository = "https://github.com/luftkode/plotinator3000"
homepage = "https://github.com/luftkode/plotinator3000"
Expand Down
18 changes: 13 additions & 5 deletions crates/plot_util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ pub fn plot_lines<'pv>(
let (x_lower, x_higher) = extended_x_plot_bound(plot_ui.plot_bounds(), 0.1);
for plot_vals in plots {
match mipmap_cfg {
MipMapConfiguration::Disabled => plot_raw(plot_ui, plot_vals, (x_lower, x_higher)),
MipMapConfiguration::Disabled => {
plot_raw(plot_ui, plot_vals, line_width, (x_lower, x_higher));
}
MipMapConfiguration::Auto => {
let (level, idx_range) =
plot_vals.get_scaled_mipmap_levels(plots_width_pixels, (x_lower, x_higher));
Expand Down Expand Up @@ -68,12 +70,12 @@ fn plot_with_mipmapping(
let (x_lower, x_higher) = x_range;
// If the mipmap level is 0 or 1 plotting all data points is just as efficient.
if mipmap_lvl < 2 {
plot_raw(plot_ui, plot_vals, (x_lower, x_higher));
plot_raw(plot_ui, plot_vals, line_width, (x_lower, x_higher));
} else {
let (plot_points_min, plot_points_max) = plot_vals.get_level_or_max(mipmap_lvl);
if plot_points_min.is_empty() {
// In this case there was so few samples that downsampling just once was below the minimum threshold, so we just plot all samples
plot_raw(plot_ui, plot_vals, (x_lower, x_higher));
plot_raw(plot_ui, plot_vals, line_width, (x_lower, x_higher));
} else {
let (plot_points_min, plot_points_max) = match known_idx_range {
Some((start, end)) => {
Expand Down Expand Up @@ -155,7 +157,7 @@ fn plot_min_max_lines(
plot_ui.line(line_max.width(line_width));
}

pub fn plot_labels(plot_ui: &mut egui_plot::PlotUi, plot_data: &PlotData, id_filter: &[usize]) {
pub fn plot_labels(plot_ui: &mut egui_plot::PlotUi, plot_data: &PlotData, id_filter: &[u16]) {
for plot_labels in plot_data
.plot_labels()
.iter()
Expand All @@ -173,10 +175,16 @@ pub fn plot_labels(plot_ui: &mut egui_plot::PlotUi, plot_data: &PlotData, id_fil
}
}

fn plot_raw(plot_ui: &mut egui_plot::PlotUi, plot_vals: &PlotValues, x_min_max_ext: (f64, f64)) {
fn plot_raw(
plot_ui: &mut egui_plot::PlotUi,
plot_vals: &PlotValues,
line_width: f32,
x_min_max_ext: (f64, f64),
) {
let plot_points = plot_vals.get_raw();
let filtered_points = filter_plot_points(plot_points, x_min_max_ext);
let line = Line::new(filtered_points)
.width(line_width)
.name(plot_vals.label())
.color(plot_vals.get_color())
.highlight(plot_vals.get_highlight());
Expand Down
14 changes: 7 additions & 7 deletions crates/plot_util/src/plots/plot_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl PlotData {
}

/// Adds a plot to the [`PlotData`] collection if another plot with the same label doesn't already exist
pub fn add_plot_if_not_exists(&mut self, raw_plot: &RawPlot, log_id: usize) {
pub fn add_plot_if_not_exists(&mut self, raw_plot: &RawPlot, log_id: u16) {
let mut plot_label = String::with_capacity(30); // Approx. enough to not reallocate
plot_label.push('#');
plot_label.push_str(&log_id.to_string());
Expand Down Expand Up @@ -77,7 +77,7 @@ pub struct PlotValues {
mipmap_max: MipMap2D<f64>,
mipmap_min: MipMap2D<f64>,
name: String,
log_id: usize,
log_id: u16,
// Label = "<name> #<log_id>"
label: String,
color: Color32,
Expand All @@ -90,7 +90,7 @@ impl PlotValues {
// Don't mipmap/downsample to more than this amount of elements
const MIPMAP_MIN_ELEMENTS: usize = 512;

pub fn new(raw_plot: Vec<[f64; 2]>, name: String, log_id: usize) -> Self {
pub fn new(raw_plot: Vec<[f64; 2]>, name: String, log_id: u16) -> Self {
let label = format!("{name} #{log_id}");
Self {
mipmap_max: MipMap2D::without_base(
Expand Down Expand Up @@ -192,7 +192,7 @@ impl PlotValues {
}

/// The ID of the log that the plot belongs to
pub fn log_id(&self) -> usize {
pub fn log_id(&self) -> u16 {
self.log_id
}

Expand All @@ -215,13 +215,13 @@ impl PlotValues {
/// Represents all the plotlabels from a given log
#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub struct StoredPlotLabels {
pub log_id: usize,
pub log_id: u16,
pub label_points: Vec<PlotLabel>,
pub highlight: bool,
}

impl StoredPlotLabels {
pub fn new(label_points: Vec<([f64; 2], String)>, log_id: usize) -> Self {
pub fn new(label_points: Vec<([f64; 2], String)>, log_id: u16) -> Self {
Self {
label_points: label_points.into_iter().map(PlotLabel::from).collect(),
log_id,
Expand All @@ -243,7 +243,7 @@ impl StoredPlotLabels {
self.label_points.iter_mut().map(|label| &mut label.point)
}

pub fn log_id(&self) -> usize {
pub fn log_id(&self) -> u16 {
self.log_id
}

Expand Down
65 changes: 35 additions & 30 deletions src/plot/plot_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ impl PlotSettingsUi {

#[derive(Default, PartialEq, Deserialize, Serialize)]
pub struct PlotSettings {
// The ID to assign to the next loaded log
next_log_id: u16,
/// Used for invalidating any cached values that determines plot layout etc.
invalidate_plot: bool,
visibility: PlotVisibilityConfig,
Expand All @@ -53,14 +55,14 @@ pub struct PlotSettings {
// Plot names and whether or not they should be shown (painted)
plot_name_filter: PlotNameFilter,
ps_ui: PlotSettingsUi,
log_start_date_settings: Vec<LoadedLogSettings>,
loaded_log_settings: Vec<LoadedLogSettings>,
mipmap_settings: MipMapSettings,
apply_deletions: bool,
}

impl PlotSettings {
pub fn show(&mut self, ui: &mut egui::Ui) {
if self.log_start_date_settings.is_empty() {
if self.loaded_log_settings.is_empty() {
ui.label(RichText::new("No Files Loaded").color(Color32::RED));
} else {
self.show_loaded_files(ui);
Expand Down Expand Up @@ -112,7 +114,7 @@ impl PlotSettings {
}

fn show_loaded_files(&mut self, ui: &mut egui::Ui) {
let loaded_files_count = self.log_start_date_settings.len();
let loaded_files_count = self.loaded_log_settings.len();
let visibility_icon = if self.ps_ui.show_loaded_logs {
regular::EYE
} else {
Expand All @@ -128,7 +130,7 @@ impl PlotSettings {
if self.ps_ui.show_loaded_logs {
// Only react on Escape input if no settings are currently open
if ui.ctx().input(|i| i.key_pressed(Key::Escape))
&& !self.log_start_date_settings.iter().any(|s| s.clicked)
&& !self.loaded_log_settings.iter().any(|s| s.clicked())
{
self.ps_ui.show_loaded_logs = false;
}
Expand All @@ -137,17 +139,14 @@ impl PlotSettings {
.show(ui.ctx(), |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
ui.horizontal_wrapped(|ui| {
Self::ui_show_or_hide_all_buttons(
ui,
&mut self.log_start_date_settings,
);
Self::ui_show_or_hide_all_buttons(ui, &mut self.loaded_log_settings);
});
egui::Grid::new("log_settings_grid").show(ui, |ui| {
ui.label("");
ui.label("");
ui.label("");
let any_marked_for_deletion = self
.log_start_date_settings
.loaded_log_settings
.iter()
.any(|s| s.marked_for_deletion());
let apply_text = if any_marked_for_deletion {
Expand All @@ -163,7 +162,7 @@ impl PlotSettings {
}

ui.end_row();
for settings in &mut self.log_start_date_settings {
for settings in &mut self.loaded_log_settings {
loaded_logs::log_date_settings_ui(ui, settings);
ui.end_row();
}
Expand Down Expand Up @@ -255,22 +254,19 @@ impl PlotSettings {
}

/// Get the next ID for a loaded data format, used for when a new file is loaded and added to the collection of plot data and plot settings
pub fn next_log_id(&self) -> usize {
(self.total_loaded() + 1).into()
}

pub fn total_loaded(&self) -> u16 {
self.log_start_date_settings.len() as u16
pub fn next_log_id(&mut self) -> u16 {
self.next_log_id += 1;
self.next_log_id
}

pub fn add_log_setting(&mut self, log_settings: LoadedLogSettings) {
self.log_start_date_settings.push(log_settings);
self.loaded_log_settings.push(log_settings);
}

// The id filter specifies which plots belonging to which logs should not be painted on the plot ui.
pub fn log_id_filter(&self) -> Vec<usize> {
let mut log_id_filter: Vec<usize> = vec![];
for settings in &self.log_start_date_settings {
pub fn log_id_filter(&self) -> Vec<u16> {
let mut log_id_filter: Vec<u16> = vec![];
for settings in &self.loaded_log_settings {
if !settings.show_log() {
log_id_filter.push(settings.log_id());
}
Expand All @@ -279,25 +275,34 @@ impl PlotSettings {
}

fn update_plot_dates(&mut self, plots: &mut Plots) {
for settings in &mut self.log_start_date_settings {
for settings in &mut self.loaded_log_settings {
date_settings::update_plot_dates(&mut self.invalidate_plot, plots, settings);
}
}

fn set_highlighted(&self, plots: &mut Plots) {
let mut id_to_highlight = None;
for log_setting in &self.log_start_date_settings {
if log_setting.cursor_hovering_on() {
id_to_highlight = Some(log_setting.log_id());
// At most 2, since one can be open and another be hovered on but no more than that
// uses u16::MAX as a special value to signify `None` basically
let mut ids_to_highlight = [u16::MAX, u16::MAX];
for log_setting in &self.loaded_log_settings {
if ids_to_highlight[0] != u16::MAX && ids_to_highlight[1] != u16::MAX {
break;
}
if log_setting.cursor_hovering_on() || log_setting.clicked() {
// Could actually be made branchless but might compromise readability too much
if ids_to_highlight[0] == u16::MAX {
ids_to_highlight[0] = log_setting.log_id();
} else {
ids_to_highlight[1] = log_setting.log_id();
}
}
}
let set_plot_highlight = |plot_data: &mut plot_util::PlotData| {
for pd in plot_data.plots_as_mut() {
*pd.get_highlight_mut() = pd.log_id() == id_to_highlight.unwrap_or(usize::MAX);
*pd.get_highlight_mut() = ids_to_highlight.contains(&pd.log_id());
}
for pl in plot_data.plot_labels_as_mut() {
*pl.get_highlight_mut() = pl.log_id() == id_to_highlight.unwrap_or(usize::MAX);
*pl.get_highlight_mut() = ids_to_highlight.contains(&pl.log_id());
}
};
set_plot_highlight(plots.percentage_mut());
Expand All @@ -308,8 +313,8 @@ impl PlotSettings {
// Remove log settings and plots that match their ID if they are marked for deletion
fn remove_if_marked_for_deletion(&mut self, plots: &mut Plots) {
// Get the log IDs for settings marked for deletion
let log_ids_to_remove: Vec<usize> = self
.log_start_date_settings
let log_ids_to_remove: Vec<u16> = self
.loaded_log_settings
.iter()
.filter(|settings| settings.marked_for_deletion())
.map(|settings| settings.log_id())
Expand Down Expand Up @@ -339,7 +344,7 @@ impl PlotSettings {
remove_matching_plots(plots.thousands_mut());

// Remove the settings marked for deletion
self.log_start_date_settings
self.loaded_log_settings
.retain(|settings| !settings.marked_for_deletion());

// Invalidate plot cache since we modified the data
Expand Down
21 changes: 16 additions & 5 deletions src/plot/plot_settings/date_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,11 @@ impl LoadedLogMetadata {

#[derive(PartialEq, Eq, Deserialize, Serialize)]
pub struct LoadedLogSettings {
//
log_id: usize,
log_id: u16,
log_descriptive_name: String,
pub original_start_date: DateTime<Utc>,
start_date: DateTime<Utc>,
pub clicked: bool,
clicked: bool,
pub tmp_date_buf: String,
pub err_msg: String,
pub new_date_candidate: Option<NaiveDateTime>,
Expand All @@ -63,7 +62,7 @@ pub struct LoadedLogSettings {

impl LoadedLogSettings {
pub fn new(
log_id: usize,
log_id: u16,
descriptive_name: String,
start_date: DateTime<Utc>,
log_metadata: Option<Vec<(String, String)>>,
Expand Down Expand Up @@ -110,7 +109,7 @@ impl LoadedLogSettings {
}

/// This is the ID that connects settings to plots
pub fn log_id(&self) -> usize {
pub fn log_id(&self) -> u16 {
self.log_id
}

Expand Down Expand Up @@ -138,6 +137,18 @@ impl LoadedLogSettings {
&mut self.marked_for_deletion
}

pub fn clicked(&self) -> bool {
self.clicked
}

pub fn clicked_mut(&mut self) -> &mut bool {
&mut self.clicked
}

pub fn toggle_clicked(&mut self) {
self.clicked = !self.clicked;
}

pub fn cursor_hovering_on(&self) -> bool {
self.is_hovered
}
Expand Down
Loading