From f20b7b43bff322f596bf09279137d9b188eb1256 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler <49431240+abey79@users.noreply.github.com> Date: Tue, 21 Nov 2023 11:22:19 +0100 Subject: [PATCH] Plot auto-bounds API improvement (part 1/2): clean-up (#3587) Part 1 of 2 of adding a better API for egui_plot's auto-bounds feature. In this PR: - change the `Plot` builder struct field to `default_auto_bounds` (was `auto_bounds`) - change the `Plot` state field to `auto_bounds` (was `bounds_modified`) - minor improvements to `Vec2b` --- crates/egui_plot/src/lib.rs | 62 ++++++++++++++++++------------------- crates/emath/src/vec2b.rs | 14 ++++++++- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/crates/egui_plot/src/lib.rs b/crates/egui_plot/src/lib.rs index 2a05ae2d03e..912d64d9d31 100644 --- a/crates/egui_plot/src/lib.rs +++ b/crates/egui_plot/src/lib.rs @@ -81,9 +81,9 @@ const MIN_LINE_SPACING_IN_POINTS: f64 = 6.0; // TODO(emilk): large enough for a #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone)] struct PlotMemory { - /// Indicates if the user has modified the bounds, for example by moving or zooming, - /// or if the bounds should be calculated based by included point or auto bounds. - bounds_modified: Vec2b, + /// Indicates if the plot uses automatic bounds. This is disengaged whenever the user modifies + /// the bounds, for example by moving or zooming. + auto_bounds: Vec2b, hovered_entry: Option, hidden_items: ahash::HashSet, @@ -137,7 +137,7 @@ struct CursorLinkGroups(HashMap>); #[derive(Clone)] struct LinkedBounds { bounds: PlotBounds, - bounds_modified: Vec2b, + auto_bounds: Vec2b, } #[derive(Default, Clone)] @@ -183,7 +183,7 @@ pub struct Plot { allow_scroll: bool, allow_double_click_reset: bool, allow_boxed_zoom: bool, - auto_bounds: Vec2b, + default_auto_bounds: Vec2b, min_auto_bounds: PlotBounds, margin_fraction: Vec2, boxed_zoom_pointer_button: PointerButton, @@ -225,7 +225,7 @@ impl Plot { allow_scroll: true, allow_double_click_reset: true, allow_boxed_zoom: true, - auto_bounds: false.into(), + default_auto_bounds: false.into(), min_auto_bounds: PlotBounds::NOTHING, margin_fraction: Vec2::splat(0.05), boxed_zoom_pointer_button: PointerButton::Secondary, @@ -501,14 +501,14 @@ impl Plot { /// Expand bounds to fit all items across the x axis, including values given by `include_x`. #[inline] pub fn auto_bounds_x(mut self) -> Self { - self.auto_bounds.x = true; + self.default_auto_bounds.x = true; self } /// Expand bounds to fit all items across the y axis, including values given by `include_y`. #[inline] pub fn auto_bounds_y(mut self) -> Self { - self.auto_bounds.y = true; + self.default_auto_bounds.y = true; self } @@ -711,8 +711,8 @@ impl Plot { allow_scroll, allow_double_click_reset, allow_boxed_zoom, - boxed_zoom_pointer_button: boxed_zoom_pointer, - auto_bounds, + boxed_zoom_pointer_button, + default_auto_bounds, min_auto_bounds, margin_fraction, width, @@ -854,7 +854,7 @@ impl Plot { PlotMemory::load(ui.ctx(), plot_id) } .unwrap_or_else(|| PlotMemory { - bounds_modified: false.into(), + auto_bounds: true.into(), hovered_entry: None, hidden_items: Default::default(), last_plot_transform: PlotTransform::new( @@ -867,7 +867,7 @@ impl Plot { }); let PlotMemory { - mut bounds_modified, + mut auto_bounds, mut hovered_entry, mut hidden_items, last_plot_transform, @@ -963,19 +963,19 @@ impl Plot { if let Some(linked_bounds) = link_groups.0.get(id) { if axes.x { bounds.set_x(&linked_bounds.bounds); - bounds_modified.x = linked_bounds.bounds_modified.x; + auto_bounds.x = linked_bounds.auto_bounds.x; } if axes.y { bounds.set_y(&linked_bounds.bounds); - bounds_modified.y = linked_bounds.bounds_modified.y; + auto_bounds.y = linked_bounds.auto_bounds.y; } }; }); }; - // Allow double clicking to reset to the initial bounds. + // Allow double-clicking to reset to the initial bounds. if allow_double_click_reset && response.double_clicked() { - bounds_modified = false.into(); + auto_bounds = true.into(); } // Apply bounds modifications. @@ -983,25 +983,25 @@ impl Plot { match modification { BoundsModification::Set(new_bounds) => { bounds = new_bounds; - bounds_modified = true.into(); + auto_bounds = false.into(); } BoundsModification::Translate(delta) => { bounds.translate(delta); - bounds_modified = true.into(); + auto_bounds = false.into(); } } } // Reset bounds to initial bounds if they haven't been modified. - if !bounds_modified.x { + if auto_bounds.x { bounds.set_x(&min_auto_bounds); } - if !bounds_modified.y { + if auto_bounds.y { bounds.set_y(&min_auto_bounds); } - let auto_x = !bounds_modified.x && (!min_auto_bounds.is_valid_x() || auto_bounds.x); - let auto_y = !bounds_modified.y && (!min_auto_bounds.is_valid_y() || auto_bounds.y); + let auto_x = auto_bounds.x && (!min_auto_bounds.is_valid_x() || default_auto_bounds.x); + let auto_y = auto_bounds.y && (!min_auto_bounds.is_valid_y() || default_auto_bounds.y); // Set bounds automatically based on content. if auto_x || auto_y { @@ -1031,7 +1031,7 @@ impl Plot { if let Some((_, linked_axes)) = &linked_axes { let change_x = linked_axes.y && !linked_axes.x; transform.set_aspect_by_changing_axis(data_aspect as f64, change_x); - } else if auto_bounds.any() { + } else if default_auto_bounds.any() { transform.set_aspect_by_expanding(data_aspect as f64); } else { transform.set_aspect_by_changing_axis(data_aspect as f64, false); @@ -1049,14 +1049,14 @@ impl Plot { delta.y = 0.0; } transform.translate_bounds(delta); - bounds_modified = allow_drag; + auto_bounds = !allow_drag; } // Zooming let mut boxed_zoom_rect = None; if allow_boxed_zoom { // Save last click to allow boxed zooming - if response.drag_started() && response.dragged_by(boxed_zoom_pointer) { + if response.drag_started() && response.dragged_by(boxed_zoom_pointer_button) { // it would be best for egui that input has a memory of the last click pos because it's a common pattern last_click_pos_for_zoom = response.hover_pos(); } @@ -1064,7 +1064,7 @@ impl Plot { let box_end_pos = response.hover_pos(); if let (Some(box_start_pos), Some(box_end_pos)) = (box_start_pos, box_end_pos) { // while dragging prepare a Shape and draw it later on top of the plot - if response.dragged_by(boxed_zoom_pointer) { + if response.dragged_by(boxed_zoom_pointer_button) { response = response.on_hover_cursor(CursorIcon::ZoomIn); let rect = epaint::Rect::from_two_pos(box_start_pos, box_end_pos); boxed_zoom_rect = Some(( @@ -1096,7 +1096,7 @@ impl Plot { }; if new_bounds.is_valid() { transform.set_bounds(new_bounds); - bounds_modified = true.into(); + auto_bounds = false.into(); } // reset the boxed zoom state last_click_pos_for_zoom = None; @@ -1120,14 +1120,14 @@ impl Plot { } if zoom_factor != Vec2::splat(1.0) { transform.zoom(zoom_factor, hover_pos); - bounds_modified = allow_zoom; + auto_bounds = !allow_zoom; } } if allow_scroll { let scroll_delta = ui.input(|i| i.scroll_delta); if scroll_delta != Vec2::ZERO { transform.translate_bounds(-scroll_delta); - bounds_modified = true.into(); + auto_bounds = false.into(); } } } @@ -1220,14 +1220,14 @@ impl Plot { *id, LinkedBounds { bounds: *transform.bounds(), - bounds_modified, + auto_bounds, }, ); }); } let memory = PlotMemory { - bounds_modified, + auto_bounds, hovered_entry, hidden_items, last_plot_transform: transform, diff --git a/crates/emath/src/vec2b.rs b/crates/emath/src/vec2b.rs index e422c9ae524..39050db8bc6 100644 --- a/crates/emath/src/vec2b.rs +++ b/crates/emath/src/vec2b.rs @@ -1,5 +1,5 @@ /// Two bools, one for each axis (X and Y). -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Vec2b { pub x: bool, @@ -58,3 +58,15 @@ impl std::ops::IndexMut for Vec2b { } } } + +impl std::ops::Not for Vec2b { + type Output = Self; + + #[inline] + fn not(self) -> Self::Output { + Self { + x: !self.x, + y: !self.y, + } + } +}