Fix plot auto bounds (#1865)
* Better estimate the plot bounds for generator functions Avoid infinities, and sample more densely * Optimize and improve plot auto-bounds logic * Fix cropping out of the top/bottom of plots during auto-bounds
This commit is contained in:
parent
97880e18d7
commit
278db1c94b
4 changed files with 80 additions and 23 deletions
|
@ -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 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 `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 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
|
## 0.18.1 - 2022-05-01
|
||||||
|
|
|
@ -378,15 +378,44 @@ pub struct ExplicitGenerator {
|
||||||
|
|
||||||
impl ExplicitGenerator {
|
impl ExplicitGenerator {
|
||||||
fn estimate_bounds(&self) -> PlotBounds {
|
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 min_x = *self.x_range.start();
|
||||||
let max_x = *self.x_range.end();
|
let max_x = *self.x_range.end();
|
||||||
let min_y = (self.function)(min_x);
|
|
||||||
let max_y = (self.function)(max_x);
|
add_x(min_x);
|
||||||
// TODO(emilk): sample some more points
|
add_x(max_x);
|
||||||
PlotBounds {
|
|
||||||
min: [min_x, min_y],
|
if min_x.is_finite() && max_x.is_finite() {
|
||||||
max: [max_x, max_y],
|
// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -682,8 +682,12 @@ impl Plot {
|
||||||
auto_bounds = true.into();
|
auto_bounds = true.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !bounds.is_valid() {
|
||||||
|
auto_bounds = true.into();
|
||||||
|
}
|
||||||
|
|
||||||
// Set bounds automatically based on content.
|
// Set bounds automatically based on content.
|
||||||
if auto_bounds.any() || !bounds.is_valid() {
|
if auto_bounds.any() {
|
||||||
if auto_bounds.x {
|
if auto_bounds.x {
|
||||||
bounds.set_x(&min_auto_bounds);
|
bounds.set_x(&min_auto_bounds);
|
||||||
}
|
}
|
||||||
|
@ -693,13 +697,13 @@ impl Plot {
|
||||||
}
|
}
|
||||||
|
|
||||||
for item in &items {
|
for item in &items {
|
||||||
// bounds.merge(&item.get_bounds());
|
let item_bounds = item.get_bounds();
|
||||||
|
|
||||||
if auto_bounds.x {
|
if auto_bounds.x {
|
||||||
bounds.merge_x(&item.get_bounds());
|
bounds.merge_x(&item_bounds);
|
||||||
}
|
}
|
||||||
if auto_bounds.y {
|
if auto_bounds.y {
|
||||||
bounds.merge_y(&item.get_bounds());
|
bounds.merge_y(&item_bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,12 +718,14 @@ impl Plot {
|
||||||
|
|
||||||
let mut transform = ScreenTransform::new(rect, bounds, center_x_axis, center_y_axis);
|
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 {
|
if let Some(data_aspect) = data_aspect {
|
||||||
let preserve_y = linked_axes
|
if let Some(linked_axes) = &linked_axes {
|
||||||
.as_ref()
|
let change_x = linked_axes.link_y && !linked_axes.link_x;
|
||||||
.map_or(false, |group| group.link_y && !group.link_x);
|
transform.set_aspect_by_changing_axis(data_aspect as f64, change_x);
|
||||||
transform.set_aspect(data_aspect as f64, preserve_y);
|
} else {
|
||||||
|
transform.set_aspect_by_expanding(data_aspect as f64);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dragging
|
// Dragging
|
||||||
|
@ -766,7 +772,7 @@ impl Plot {
|
||||||
max: [box_end_pos.x, box_start_pos.y],
|
max: [box_end_pos.x, box_start_pos.y],
|
||||||
};
|
};
|
||||||
if new_bounds.is_valid() {
|
if new_bounds.is_valid() {
|
||||||
*transform.bounds_mut() = new_bounds;
|
transform.set_bounds(new_bounds);
|
||||||
auto_bounds = false.into();
|
auto_bounds = false.into();
|
||||||
} else {
|
} else {
|
||||||
auto_bounds = true.into();
|
auto_bounds = true.into();
|
||||||
|
|
|
@ -206,8 +206,8 @@ impl ScreenTransform {
|
||||||
&self.bounds
|
&self.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bounds_mut(&mut self) -> &mut PlotBounds {
|
pub fn set_bounds(&mut self, bounds: PlotBounds) {
|
||||||
&mut self.bounds
|
self.bounds = bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn translate_bounds(&mut self, mut delta_pos: Vec2) {
|
pub fn translate_bounds(&mut self, mut delta_pos: Vec2) {
|
||||||
|
@ -299,15 +299,17 @@ impl ScreenTransform {
|
||||||
[1.0 / self.dpos_dvalue_x(), 1.0 / self.dpos_dvalue_y()]
|
[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 rw = self.frame.width() as f64;
|
||||||
let rh = self.frame.height() as f64;
|
let rh = self.frame.height() as f64;
|
||||||
(self.bounds.width() / rw) / (self.bounds.height() / rh)
|
(self.bounds.width() / rw) / (self.bounds.height() / rh)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the aspect ratio by either expanding the x-axis or contracting the y-axis.
|
/// Sets the aspect ratio by expanding the x- or y-axis.
|
||||||
pub fn set_aspect(&mut self, aspect: f64, preserve_y: bool) {
|
///
|
||||||
let current_aspect = self.get_aspect();
|
/// 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;
|
let epsilon = 1e-5;
|
||||||
if (current_aspect - aspect).abs() < epsilon {
|
if (current_aspect - aspect).abs() < epsilon {
|
||||||
|
@ -315,7 +317,26 @@ impl ScreenTransform {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if preserve_y {
|
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 change_x {
|
||||||
self.bounds
|
self.bounds
|
||||||
.expand_x((aspect / current_aspect - 1.0) * self.bounds.width() * 0.5);
|
.expand_x((aspect / current_aspect - 1.0) * self.bounds.width() * 0.5);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue