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 `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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
@ -299,15 +299,17 @@ 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 {
|
||||
|
@ -315,7 +317,26 @@ impl ScreenTransform {
|
|||
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
|
||||
.expand_x((aspect / current_aspect - 1.0) * self.bounds.width() * 0.5);
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue