* Fixes#3808
* Fixes#2307
This PR improves the behaviour of auto-bounds with data that:
- is a single point
- where all X values are the same (e.g. vertical line)
- where all Y values are the same (e.g. horizontal line)
In all case, the auto-bound now aim to center on the data. For span,
when available, it use the same as the other axis. If the data range of
the other axis is also degenerate, then it defaults to +/- 1.0.
https://github.com/emilk/egui/assets/49431240/a62d2b5b-7856-4415-8534-83dc58cfac98
<details>
<summary>Test code</summary>
```rust
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
#![allow(rustdoc::missing_crate_level_docs)] // it's an example
use eframe::egui;
use egui_plot::{Legend, Line, Plot, PlotPoints, Points};
fn main() -> Result<(), eframe::Error> {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([350.0, 200.0]),
..Default::default()
};
eframe::run_native(
"My egui App with a plot",
options,
Box::new(|_cc| Ok(Box::<MyApp>::default())),
)
}
#[derive(Default)]
struct MyApp {}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let mut plot_rect = None;
egui::CentralPanel::default().show(ctx, |ui| {
if ui.button("Save Plot").clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot);
}
let my_plot = Plot::new("My Plot").legend(Legend::default());
// let's create a dummy line in the plot
let inner = my_plot.show(ui, |plot_ui| {
plot_ui.line(
Line::new(PlotPoints::from(vec![
[0.0, 10.0],
[2.0, 10.0],
[3.0, 10.0],
]))
.name("y = 10.0"),
);
plot_ui.line(
Line::new(PlotPoints::from(vec![
[10.0, 10.0],
[10.0, 11.0],
[10.0, 12.0],
]))
.name("x = 10.0"),
);
plot_ui.points(
Points::new(PlotPoints::from(vec![[5.0, 5.0]]))
.name("(5,5)")
.radius(3.0),
);
plot_ui.points(
Points::new(PlotPoints::from(vec![[5.0, 7.0]]))
.name("(5,7)")
.radius(3.0),
);
});
// Remember the position of the plot
plot_rect = Some(inner.response.rect);
});
// Check for returned screenshot:
let screenshot = ctx.input(|i| {
for event in &i.raw.events {
if let egui::Event::Screenshot { image, .. } = event {
return Some(image.clone());
}
}
None
});
if let (Some(screenshot), Some(plot_location)) = (screenshot, plot_rect) {
if let Some(mut path) = rfd::FileDialog::new().save_file() {
path.set_extension("png");
// for a full size application, we should put this in a different thread,
// so that the GUI doesn't lag during saving
let pixels_per_point = ctx.pixels_per_point();
let plot = screenshot.region(&plot_location, Some(pixels_per_point));
// save the plot to png
image::save_buffer(
&path,
plot.as_raw(),
plot.width() as u32,
plot.height() as u32,
image::ColorType::Rgba8,
)
.unwrap();
eprintln!("Image saved to {path:?}.");
}
}
}
}
```
</details>