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

Fix plot auto bounds #1865

Merged
merged 4 commits into from Jul 29, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -32,6 +32,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui-w
* Fixed dead-lock when alt-tabbing while also showing a tooltip ([#1618](https://github.com/emilk/egui/pull/1618)).
* Fixed `ScrollArea` scrolling when editing an unrelated `TextEdit` ([#1779](https://github.com/emilk/egui/pull/1779)).
* Fixed focus behavior when pressing Tab in a UI with no focused widget ([#1861](https://github.com/emilk/egui/pull/1861)).
* Fixed automatic plot bounds ([#1865](https://github.com/emilk/egui/pull/1865)).


## 0.18.1 - 2022-05-01
Expand Down
41 changes: 35 additions & 6 deletions egui/src/widgets/plot/items/values.rs
Expand Up @@ -378,15 +378,44 @@ pub struct ExplicitGenerator {

impl ExplicitGenerator {
fn estimate_bounds(&self) -> PlotBounds {
let mut bounds = PlotBounds::NOTHING;

let mut add_x = |x: f64| {
// avoid infinities, as we cannot auto-bound on them!
if x.is_finite() {
bounds.extend_with_x(x);
}
let y = (self.function)(x);
if y.is_finite() {
bounds.extend_with_y(y);
}
};

let min_x = *self.x_range.start();
let max_x = *self.x_range.end();
let min_y = (self.function)(min_x);
let max_y = (self.function)(max_x);
// TODO(emilk): sample some more points
PlotBounds {
min: [min_x, min_y],
max: [max_x, max_y],

add_x(min_x);
add_x(max_x);

if min_x.is_finite() && max_x.is_finite() {
// Sample some points in the interval:
const N: u32 = 8;
for i in 1..N {
let t = i as f64 / (N - 1) as f64;
let x = crate::lerp(min_x..=max_x, t);
add_x(x);
}
} else {
// Try adding some points anyway:
for x in [-1, 0, 1] {
let x = x as f64;
if min_x <= x && x <= max_x {
add_x(x);
}
}
}

bounds
}
}

Expand Down
26 changes: 16 additions & 10 deletions egui/src/widgets/plot/mod.rs
Expand Up @@ -682,8 +682,12 @@ impl Plot {
auto_bounds = true.into();
}

if !bounds.is_valid() {
auto_bounds = true.into();
}

// Set bounds automatically based on content.
if auto_bounds.any() || !bounds.is_valid() {
if auto_bounds.any() {
if auto_bounds.x {
bounds.set_x(&min_auto_bounds);
}
Expand All @@ -693,13 +697,13 @@ impl Plot {
}

for item in &items {
// bounds.merge(&item.get_bounds());
let item_bounds = item.get_bounds();

if auto_bounds.x {
bounds.merge_x(&item.get_bounds());
bounds.merge_x(&item_bounds);
}
if auto_bounds.y {
bounds.merge_y(&item.get_bounds());
bounds.merge_y(&item_bounds);
}
}

Expand All @@ -714,12 +718,14 @@ impl Plot {

let mut transform = ScreenTransform::new(rect, bounds, center_x_axis, center_y_axis);

// Enforce equal aspect ratio.
// Enforce aspect ratio
if let Some(data_aspect) = data_aspect {
let preserve_y = linked_axes
.as_ref()
.map_or(false, |group| group.link_y && !group.link_x);
transform.set_aspect(data_aspect as f64, preserve_y);
if let Some(linked_axes) = &linked_axes {
let change_x = linked_axes.link_y && !linked_axes.link_x;
transform.set_aspect_by_changing_axis(data_aspect as f64, change_x);
} else {
transform.set_aspect_by_expanding(data_aspect as f64);
}
}

// Dragging
Expand Down Expand Up @@ -766,7 +772,7 @@ impl Plot {
max: [box_end_pos.x, box_start_pos.y],
};
if new_bounds.is_valid() {
*transform.bounds_mut() = new_bounds;
transform.set_bounds(new_bounds);
auto_bounds = false.into();
} else {
auto_bounds = true.into();
Expand Down
35 changes: 28 additions & 7 deletions egui/src/widgets/plot/transform.rs
Expand Up @@ -206,8 +206,8 @@ impl ScreenTransform {
&self.bounds
}

pub fn bounds_mut(&mut self) -> &mut PlotBounds {
&mut self.bounds
pub fn set_bounds(&mut self, bounds: PlotBounds) {
self.bounds = bounds;
}

pub fn translate_bounds(&mut self, mut delta_pos: Vec2) {
Expand Down Expand Up @@ -299,23 +299,44 @@ impl ScreenTransform {
[1.0 / self.dpos_dvalue_x(), 1.0 / self.dpos_dvalue_y()]
}

pub fn get_aspect(&self) -> f64 {
fn aspect(&self) -> f64 {
let rw = self.frame.width() as f64;
let rh = self.frame.height() as f64;
(self.bounds.width() / rw) / (self.bounds.height() / rh)
}

/// Sets the aspect ratio by either expanding the x-axis or contracting the y-axis.
pub fn set_aspect(&mut self, aspect: f64, preserve_y: bool) {
let current_aspect = self.get_aspect();
/// Sets the aspect ratio by expanding the x- or y-axis.
///
/// This never contracts, so we don't miss out on any data.
pub fn set_aspect_by_expanding(&mut self, aspect: f64) {
let current_aspect = self.aspect();

let epsilon = 1e-5;
if (current_aspect - aspect).abs() < epsilon {
// Don't make any changes when the aspect is already almost correct.
return;
}

if current_aspect < aspect {
self.bounds
.expand_x((aspect / current_aspect - 1.0) * self.bounds.width() * 0.5);
} else {
self.bounds
.expand_y((current_aspect / aspect - 1.0) * self.bounds.height() * 0.5);
}
}

/// Sets the aspect ratio by changing either the X or Y axis (callers choice).
pub fn set_aspect_by_changing_axis(&mut self, aspect: f64, change_x: bool) {
let current_aspect = self.aspect();

let epsilon = 1e-5;
if (current_aspect - aspect).abs() < epsilon {
// Don't make any changes when the aspect is already almost correct.
return;
}

if preserve_y {
if change_x {
self.bounds
.expand_x((aspect / current_aspect - 1.0) * self.bounds.width() * 0.5);
} else {
Expand Down