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

Plot boxed zoom #1188

Merged
merged 8 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
* Added `CollapsingHeader::icon` to override the default open/close icon using a custom function. ([1147](https://github.com/emilk/egui/pull/1147)).
* Added `Plot::x_axis_formatter` and `Plot::y_axis_formatter` for custom axis labels ([#1130](https://github.com/emilk/egui/pull/1130)).
* Added `ui.data()`, `ctx.data()`, `ctx.options()` and `ctx.tessellation_options()` ([#1175](https://github.com/emilk/egui/pull/1175)).
* Added `Plot::allow_boxed_zoom()`, `Plot::boxed_zoom_pointer()` for boxed zooming on plots.
nongiach marked this conversation as resolved.
Show resolved Hide resolved


### Changed 🔧
* ⚠️ `Context::input` and `Ui::input` now locks a mutex. This can lead to a dead-lock is used in an `if let` binding!
Expand Down
67 changes: 67 additions & 0 deletions egui/src/widgets/plot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ struct PlotMemory {
hidden_items: AHashSet<String>,
min_auto_bounds: PlotBounds,
last_screen_transform: ScreenTransform,
/// Allows to remember the first click position when performing a boxed zoom
last_click_pos_for_zoom: Option<Pos2>,
}

impl PlotMemory {
Expand Down Expand Up @@ -73,6 +75,8 @@ pub struct Plot {
allow_drag: bool,
min_auto_bounds: PlotBounds,
margin_fraction: Vec2,
allow_boxed_zoom: bool,
boxed_zoom_pointer: PointerButton,

min_size: Vec2,
width: Option<f32>,
Expand Down Expand Up @@ -101,6 +105,8 @@ impl Plot {
allow_drag: true,
min_auto_bounds: PlotBounds::NOTHING,
margin_fraction: Vec2::splat(0.05),
allow_boxed_zoom: true,
boxed_zoom_pointer: PointerButton::Secondary,

min_size: Vec2::splat(64.0),
width: None,
Expand Down Expand Up @@ -186,6 +192,18 @@ impl Plot {
self
}

/// Whether to allow zooming in the plot. Default: `true`.
nongiach marked this conversation as resolved.
Show resolved Hide resolved
pub fn allow_boxed_zoom(mut self, on: bool) -> Self {
self.allow_boxed_zoom = on;
self
}

/// Config the pointer to use for boxed_zoom. Default: `Secondary`
pub fn boxed_zoom_pointer(mut self, boxed_zoom_pointer: PointerButton) -> Self {
nongiach marked this conversation as resolved.
Show resolved Hide resolved
self.boxed_zoom_pointer = boxed_zoom_pointer;
self
}

/// Whether to allow dragging in the plot to move the bounds. Default: `true`.
pub fn allow_drag(mut self, on: bool) -> Self {
self.allow_drag = on;
Expand Down Expand Up @@ -289,6 +307,8 @@ impl Plot {
center_y_axis,
allow_zoom,
allow_drag,
allow_boxed_zoom,
boxed_zoom_pointer,
min_auto_bounds,
margin_fraction,
width,
Expand Down Expand Up @@ -345,6 +365,7 @@ impl Plot {
center_x_axis,
center_y_axis,
),
last_click_pos_for_zoom: None
});

// If the min bounds changed, recalculate everything.
Expand All @@ -363,6 +384,7 @@ impl Plot {
mut hovered_entry,
mut hidden_items,
last_screen_transform,
mut last_click_pos_for_zoom,
..
} = memory;

Expand Down Expand Up @@ -442,6 +464,45 @@ impl Plot {
}

// 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) {
// it would be best for egui that input has a memory of the last click pos because it's a common pattern
nongiach marked this conversation as resolved.
Show resolved Hide resolved
last_click_pos_for_zoom = response.hover_pos()
}
let box_start_pos = last_click_pos_for_zoom;
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) {
response = response.on_hover_cursor(CursorIcon::ZoomIn);
let rect = epaint::Rect::from_two_pos(box_start_pos, box_end_pos);
boxed_zoom_rect = Some((
epaint::RectShape::stroke(rect, 0.0, epaint::Stroke::new(4., Color32::DARK_BLUE)), // Outer stroke
epaint::RectShape::stroke(rect, 0.0, epaint::Stroke::new(2., Color32::WHITE)) // Inner stroke
));
}
// when the click is release perform the zoom
if response.drag_released() {
let box_start_pos = transform.value_from_position(box_start_pos);
let box_end_pos = transform.value_from_position(box_end_pos);
let new_bounds = PlotBounds{
min: [box_start_pos.x, box_end_pos.y],
max: [box_end_pos.x, box_start_pos.y]
};
if new_bounds.is_valid() {
*transform.bounds_mut() = new_bounds;
auto_bounds = false;
} else {
auto_bounds = true;
}
// reset the boxed zoom state
last_click_pos_for_zoom = None;
}
}
}

if allow_zoom {
if let Some(hover_pos) = response.hover_pos() {
let zoom_factor = if data_aspect.is_some() {
Expand Down Expand Up @@ -478,6 +539,11 @@ impl Plot {
};
prepared.ui(ui, &response);

if let Some(boxed_zoom_rect) = boxed_zoom_rect {
ui.painter().sub_region(rect).add(boxed_zoom_rect.0);
ui.painter().sub_region(rect).add(boxed_zoom_rect.1);
}

if let Some(mut legend) = legend {
ui.add(&mut legend);
hidden_items = legend.get_hidden_items();
Expand All @@ -490,6 +556,7 @@ impl Plot {
hidden_items,
min_auto_bounds,
last_screen_transform: transform,
last_click_pos_for_zoom
};
memory.store(ui.ctx(), plot_id);

Expand Down
4 changes: 4 additions & 0 deletions egui/src/widgets/plot/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ impl ScreenTransform {
&self.bounds
}

pub fn bounds_mut(&mut self) -> &mut PlotBounds {
&mut self.bounds
}

pub fn translate_bounds(&mut self, mut delta_pos: Vec2) {
if self.x_centered {
delta_pos.x = 0.;
Expand Down