1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00

Add bar charts and box plots (#863)

Changes:
* New `BarChart` and `BoxPlot` diagrams
* New `FloatOrd` trait for total ordering of float types
* Refactoring of existing plot items

Co-authored-by: niladic <git@nil.choron.cc>
This commit is contained in:
Jan Haller
2021-11-29 18:39:58 +01:00
committed by GitHub
parent 224d4d6d26
commit 1088d950e9
12 changed files with 1813 additions and 420 deletions

View File

@@ -1,9 +1,10 @@
use std::f64::consts::TAU;
use egui::*;
use plot::{
Arrows, Corner, HLine, Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, Points, Polygon,
Text, VLine, Value, Values,
Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, Corner, HLine, Legend, Line, LineStyle,
MarkerShape, Plot, PlotImage, Points, Polygon, Text, VLine, Value, Values,
};
use std::f64::consts::TAU;
#[derive(PartialEq)]
struct LineDemo {
@@ -430,18 +431,203 @@ impl Widget for &mut InteractionDemo {
}
}
#[derive(PartialEq, Eq)]
enum Chart {
GaussBars,
StackedBars,
BoxPlot,
}
impl Default for Chart {
fn default() -> Self {
Self::GaussBars
}
}
#[derive(PartialEq)]
struct ChartsDemo {
chart: Chart,
vertical: bool,
}
impl Default for ChartsDemo {
fn default() -> Self {
Self {
vertical: true,
chart: Chart::default(),
}
}
}
impl ChartsDemo {
fn bar_gauss(&self, ui: &mut Ui) -> Response {
let mut chart = BarChart::new(
(-395..=395)
.step_by(10)
.map(|x| x as f64 * 0.01)
.map(|x| {
(
x,
(-x * x / 2.0).exp() / (2.0 * std::f64::consts::PI).sqrt(),
)
})
// The 10 factor here is purely for a nice 1:1 aspect ratio
.map(|(x, f)| Bar::new(x, f * 10.0).width(0.095))
.collect(),
)
.color(Color32::LIGHT_BLUE)
.name("Normal Distribution");
if !self.vertical {
chart = chart.horizontal();
}
Plot::new("Normal Distribution Demo")
.legend(Legend::default())
.data_aspect(1.0)
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
.response
}
fn bar_stacked(&self, ui: &mut Ui) -> Response {
let mut chart1 = BarChart::new(vec![
Bar::new(0.5, 1.0).name("Day 1"),
Bar::new(1.5, 3.0).name("Day 2"),
Bar::new(2.5, 1.0).name("Day 3"),
Bar::new(3.5, 2.0).name("Day 4"),
Bar::new(4.5, 4.0).name("Day 5"),
])
.width(0.7)
.name("Set 1");
let mut chart2 = BarChart::new(vec![
Bar::new(0.5, 1.0),
Bar::new(1.5, 1.5),
Bar::new(2.5, 0.1),
Bar::new(3.5, 0.7),
Bar::new(4.5, 0.8),
])
.width(0.7)
.name("Set 2")
.stack_on(&[&chart1]);
let mut chart3 = BarChart::new(vec![
Bar::new(0.5, -0.5),
Bar::new(1.5, 1.0),
Bar::new(2.5, 0.5),
Bar::new(3.5, -1.0),
Bar::new(4.5, 0.3),
])
.width(0.7)
.name("Set 3")
.stack_on(&[&chart1, &chart2]);
let mut chart4 = BarChart::new(vec![
Bar::new(0.5, 0.5),
Bar::new(1.5, 1.0),
Bar::new(2.5, 0.5),
Bar::new(3.5, -0.5),
Bar::new(4.5, -0.5),
])
.width(0.7)
.name("Set 4")
.stack_on(&[&chart1, &chart2, &chart3]);
if !self.vertical {
chart1 = chart1.horizontal();
chart2 = chart2.horizontal();
chart3 = chart3.horizontal();
chart4 = chart4.horizontal();
}
Plot::new("Stacked Bar Chart Demo")
.legend(Legend::default())
.data_aspect(1.0)
.show(ui, |plot_ui| {
plot_ui.bar_chart(chart1);
plot_ui.bar_chart(chart2);
plot_ui.bar_chart(chart3);
plot_ui.bar_chart(chart4);
})
.response
}
fn box_plot(&self, ui: &mut Ui) -> Response {
let yellow = Color32::from_rgb(248, 252, 168);
let mut box1 = BoxPlot::new(vec![
BoxElem::new(0.5, BoxSpread::new(1.5, 2.2, 2.5, 2.6, 3.1)).name("Day 1"),
BoxElem::new(2.5, BoxSpread::new(0.4, 1.0, 1.1, 1.4, 2.1)).name("Day 2"),
BoxElem::new(4.5, BoxSpread::new(1.7, 2.0, 2.2, 2.5, 2.9)).name("Day 3"),
])
.name("Experiment A");
let mut box2 = BoxPlot::new(vec![
BoxElem::new(1.0, BoxSpread::new(0.2, 0.5, 1.0, 2.0, 2.7)).name("Day 1"),
BoxElem::new(3.0, BoxSpread::new(1.5, 1.7, 2.1, 2.9, 3.3))
.name("Day 2: interesting")
.stroke(Stroke::new(1.5, yellow))
.fill(yellow.linear_multiply(0.2)),
BoxElem::new(5.0, BoxSpread::new(1.3, 2.0, 2.3, 2.9, 4.0)).name("Day 3"),
])
.name("Experiment B");
let mut box3 = BoxPlot::new(vec![
BoxElem::new(1.5, BoxSpread::new(2.1, 2.2, 2.6, 2.8, 3.0)).name("Day 1"),
BoxElem::new(3.5, BoxSpread::new(1.3, 1.5, 1.9, 2.2, 2.4)).name("Day 2"),
BoxElem::new(5.5, BoxSpread::new(0.2, 0.4, 1.0, 1.3, 1.5)).name("Day 3"),
])
.name("Experiment C");
if !self.vertical {
box1 = box1.horizontal();
box2 = box2.horizontal();
box3 = box3.horizontal();
}
Plot::new("Box Plot Demo")
.legend(Legend::default())
.show(ui, |plot_ui| {
plot_ui.box_plot(box1);
plot_ui.box_plot(box2);
plot_ui.box_plot(box3);
})
.response
}
}
impl Widget for &mut ChartsDemo {
fn ui(self, ui: &mut Ui) -> Response {
ui.label("Type:");
ui.horizontal(|ui| {
ui.selectable_value(&mut self.chart, Chart::GaussBars, "Histogram");
ui.selectable_value(&mut self.chart, Chart::StackedBars, "Stacked Bar Chart");
ui.selectable_value(&mut self.chart, Chart::BoxPlot, "Box Plot");
});
ui.label("Orientation:");
ui.horizontal(|ui| {
ui.selectable_value(&mut self.vertical, true, "Vertical");
ui.selectable_value(&mut self.vertical, false, "Horizontal");
});
match self.chart {
Chart::GaussBars => self.bar_gauss(ui),
Chart::StackedBars => self.bar_stacked(ui),
Chart::BoxPlot => self.box_plot(ui),
}
}
}
#[derive(PartialEq, Eq)]
enum Panel {
Lines,
Markers,
Legend,
Charts,
Items,
Interaction,
}
impl Default for Panel {
fn default() -> Self {
Self::Lines
Self::Charts
}
}
@@ -450,6 +636,7 @@ pub struct PlotDemo {
line_demo: LineDemo,
marker_demo: MarkerDemo,
legend_demo: LegendDemo,
charts_demo: ChartsDemo,
items_demo: ItemsDemo,
interaction_demo: InteractionDemo,
open_panel: Panel,
@@ -492,6 +679,7 @@ impl super::View for PlotDemo {
ui.selectable_value(&mut self.open_panel, Panel::Lines, "Lines");
ui.selectable_value(&mut self.open_panel, Panel::Markers, "Markers");
ui.selectable_value(&mut self.open_panel, Panel::Legend, "Legend");
ui.selectable_value(&mut self.open_panel, Panel::Charts, "Charts");
ui.selectable_value(&mut self.open_panel, Panel::Items, "Items");
ui.selectable_value(&mut self.open_panel, Panel::Interaction, "Interaction");
});
@@ -507,6 +695,9 @@ impl super::View for PlotDemo {
Panel::Legend => {
ui.add(&mut self.legend_demo);
}
Panel::Charts => {
ui.add(&mut self.charts_demo);
}
Panel::Items => {
ui.add(&mut self.items_demo);
}