#egui #plotter #plotters #back-end

egui-plotter

Simple to use utilties for integrating plotter into egui

5 unstable releases

0.3.0 Jun 25, 2023
0.2.1 Jun 18, 2023
0.2.0 Jun 15, 2023
0.1.1 Jun 12, 2023
0.1.0 Jun 11, 2023

#211 in Visualization

26 downloads per month

MIT license

62KB
1K SLoC

egui-plotter

MIT License Crates.io Documentation APE

simple to use utilties for integrating plotter into egui

3d Graph Live Demo Spiral Live Demo

Usage

This crate can be used by adding egui-plotter to the dependencies in your project's Cargo.toml.

[dependencies]
egui-plotter = "0.3.0"

It is also heavily recommended you disable feathering in your egui context, as not only does it slow things down but it causes artifacts with certain plots.

See line 24 example below to see how to disable feathering.

Examples

Here's a simple plotter example being run on native eframe. Derived from eframe and plotters.

use eframe::egui::{self, CentralPanel, Visuals};
use egui_plotter::EguiBackend;
use plotters::prelude::*;

fn main() {
    let native_options = eframe::NativeOptions::default();
    eframe::run_native(
        "Simple Example",
        native_options,
        Box::new(|cc| Box::new(Simple::new(cc))),
    )
    .unwrap();
}

#[derive(Default)]
struct Simple {}

impl Simple {
    fn new(cc: &eframe::CreationContext<'_>) -> Self {
        // Disable feathering as it causes artifacts
        let context = &cc.egui_ctx;

        context.tessellation_options_mut(|tess_options| {
            tess_options.feathering = false;
        });

        // Also enable light mode
        context.set_visuals(Visuals::light());

        Self::default()
    }
}

impl eframe::App for Simple {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        CentralPanel::default().show(ctx, |ui| {
            let root = EguiBackend::new(ui).into_drawing_area();
            root.fill(&WHITE).unwrap();
            let mut chart = ChartBuilder::on(&root)
                .caption("y=x^2", ("sans-serif", 50).into_font())
                .margin(5)
                .x_label_area_size(30)
                .y_label_area_size(30)
                .build_cartesian_2d(-1f32..1f32, -0.1f32..1f32)
                .unwrap();

            chart.configure_mesh().draw().unwrap();

            chart
                .draw_series(LineSeries::new(
                    (-50..=50).map(|x| x as f32 / 50.0).map(|x| (x, x * x)),
                    &RED,
                ))
                .unwrap()
                .label("y = x^2")
                .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED));

            chart
                .configure_series_labels()
                .background_style(&WHITE.mix(0.8))
                .border_style(&BLACK)
                .draw()
                .unwrap();

            root.present().unwrap();
        });
    }
}

Charts

Alternatively, the above example can be made with a Chart type to allow easy user interactivity with your plotter charts. You can either make your own chart or use a prebuilt chart type included in the charts module.

use eframe::egui::{self, CentralPanel, Visuals};
use egui::Key;
use egui_plotter::{Chart, MouseConfig};
use plotters::prelude::*;
use std::ops::Range;

fn main() {
    let native_options = eframe::NativeOptions::default();
    eframe::run_native(
        "ParaChart Example",
        native_options,
        Box::new(|cc| Box::new(ParaChart::new(cc))),
    )
    .unwrap();
}

struct ParaChart {
    chart: Chart,
}

impl ParaChart {
    fn new(cc: &eframe::CreationContext<'_>) -> Self {
        // Disable feathering as it causes artifacts
        let context = &cc.egui_ctx;

        context.tessellation_options_mut(|tess_options| {
            tess_options.feathering = false;
        });

        // Also enable light mode
        context.set_visuals(Visuals::light());

        // We use data to adjust the range of the chart. This can be useful for
        // line plots where the X represents time and we want to play through
        // the X, but that is not what we are using it for here
        let chart = Chart::new()
            .mouse(MouseConfig::enabled())
            .data(Box::new((-3f32..3f32, -0.5f32..3f32)))
            .builder_cb(Box::new(|area, _t, ranges| {
                // Build a chart like you would in any other plotter chart.
                // The drawing area and ranges are provided by the callback,
                // but otherwise everything else is the same.
                let ranges: &(Range<f32>, Range<f32>) =
                    ranges.as_ref().unwrap().downcast_ref().unwrap();

                let (x_range, y_range) = ranges;

                let mut chart = ChartBuilder::on(area)
                    .caption("y=x^2", ("sans-serif", 50).into_font())
                    .margin(5)
                    .x_label_area_size(30)
                    .y_label_area_size(30)
                    .build_cartesian_2d(x_range.to_owned(), y_range.to_owned())
                    .unwrap();

                chart.configure_mesh().draw().unwrap();

                chart
                    .draw_series(LineSeries::new(
                        (-50 * (x_range.end as i32)..=(50 * x_range.end as i32))
                            .map(|x| x as f32 / 50.0)
                            .map(|x| (x, x * x)),
                        &RED,
                    ))
                    .unwrap()
                    .label("y = x^2")
                    .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED));

                chart
                    .configure_series_labels()
                    .background_style(&WHITE.mix(0.8))
                    .border_style(&BLACK)
                    .draw()
                    .unwrap();
            }));

        Self { chart }
    }
}

impl eframe::App for ParaChart {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        CentralPanel::default().show(ctx, |ui| {
            // Press 1 for the range -1..1, 2 for -2..2, 3 for -3..3
            ui.input(|input| {
                if input.key_down(Key::Num1) {
                    self.chart.set_data(Box::new((-1f32..1f32, -0.5f32..1f32)));
                }
                if input.key_down(Key::Num2) {
                    self.chart.set_data(Box::new((-2f32..2f32, -0.5f32..2f32)));
                }

                if input.key_down(Key::Num3) {
                    self.chart.set_data(Box::new((-3f32..3f32, -0.5f32..3f32)));
                }
            });

            self.chart.draw(ui);
        });
    }
}

Dependencies

~8–15MB
~180K SLoC