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

Improve behavior of plot auto-bounds with reduced data #4632

Merged
merged 2 commits into from
Jun 7, 2024

Conversation

abey79
Copy link
Collaborator

@abey79 abey79 commented Jun 6, 2024

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.

better_plot_bounds.mp4
Test code
#![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:?}.");
            }
        }
    }
}

@abey79 abey79 added the egui_plot Related to egui_plot label Jun 6, 2024
Comment on lines 255 to 256
pub fn new(frame: Rect, bounds: PlotBounds, x_centered: bool, y_centered: bool) -> Self {
let mut new_bounds = bounds;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative 🤷

Suggested change
pub fn new(frame: Rect, bounds: PlotBounds, x_centered: bool, y_centered: bool) -> Self {
let mut new_bounds = bounds;
pub fn new(frame: Rect, mut bounds: PlotBounds, x_centered: bool, y_centered: bool) -> Self {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope! 😱 I need the original, unmodified bounds throughout, otherwise the modifications for the X axis interact badly with the modifications for the Y axis. Took me the longest time to figure that out.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ill add a comment to that effect

@abey79 abey79 merged commit 9f12432 into master Jun 7, 2024
35 checks passed
@abey79 abey79 deleted the antoine/improve-plot-auto-bounds branch June 7, 2024 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
egui_plot Related to egui_plot
Projects
None yet
2 participants