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

Plotting: Add line markers (#363)

* initial work on markers

* clippy fix

* simplify marker

* use option for color

* prepare for more demo plots

* more improvements for markers

* some small adjustments

* better highlighting

* don't draw transparent lines

* use transparent color instead of option

* don't brighten curves when highlighting

* update changelog

* avoid allocations and use line_segment

* compare against transparent color

* create new Points primitive

* fix doctest

* some cleanup and fix hover

* common interface for lines and points

* clippy fixes

* reduce visibilities

* Update egui/src/widgets/plot/mod.rs

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* Update egui/src/widgets/plot/mod.rs

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* Update egui_demo_lib/src/apps/demo/plot_demo.rs

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* Update egui_demo_lib/src/apps/demo/plot_demo.rs

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* changes based on review

* fix test

* dynamic plot size

* remove height

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
Sven Niederberger
2021-05-27 18:40:20 +02:00
committed by GitHub
parent e320ef6c64
commit 8623909d82
5 changed files with 663 additions and 206 deletions

View File

@@ -1,9 +1,9 @@
use egui::plot::{Curve, Plot, Value};
use egui::plot::{Line, MarkerShape, Plot, Points, Value, Values};
use egui::*;
use std::f64::consts::TAU;
#[derive(PartialEq)]
pub struct PlotDemo {
struct LineDemo {
animate: bool,
time: f64,
circle_radius: f64,
@@ -13,7 +13,7 @@ pub struct PlotDemo {
proportional: bool,
}
impl Default for PlotDemo {
impl Default for LineDemo {
fn default() -> Self {
Self {
animate: true,
@@ -27,29 +27,8 @@ impl Default for PlotDemo {
}
}
impl super::Demo for PlotDemo {
fn name(&self) -> &'static str {
"🗠 Plot"
}
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
use super::View;
Window::new(self.name())
.open(open)
.default_size(vec2(400.0, 400.0))
.scroll(false)
.show(ctx, |ui| self.ui(ui));
}
}
impl PlotDemo {
impl LineDemo {
fn options_ui(&mut self, ui: &mut Ui) {
ui.vertical_centered(|ui| {
egui::reset_button(ui, self);
ui.add(crate::__egui_github_link_file!());
});
ui.separator();
let Self {
animate,
time: _,
@@ -58,6 +37,7 @@ impl PlotDemo {
square,
legend,
proportional,
..
} = self;
ui.horizontal(|ui| {
@@ -88,25 +68,14 @@ impl PlotDemo {
ui.vertical(|ui| {
ui.style_mut().wrap = Some(false);
ui.checkbox(animate, "animate");
ui.add_space(8.0);
ui.checkbox(square, "square view");
ui.checkbox(legend, "legend");
ui.checkbox(proportional, "proportional data axes");
});
});
ui.label("Pan by dragging, or scroll (+ shift = horizontal).");
if cfg!(target_arch = "wasm32") {
ui.label("Zoom with ctrl / ⌘ + mouse wheel, or with pinch gesture.");
} else if cfg!(target_os = "macos") {
ui.label("Zoom with ctrl / ⌘ + scroll.");
} else {
ui.label("Zoom with ctrl + scroll.");
}
ui.label("Reset view with double-click.");
}
fn circle(&self) -> Curve {
fn circle(&self) -> Line {
let n = 512;
let circle = (0..=n).map(|i| {
let t = remap(i as f64, 0.0..=(n as f64), 0.0..=TAU);
@@ -116,55 +85,192 @@ impl PlotDemo {
r * t.sin() + self.circle_center.y as f64,
)
});
Curve::from_values_iter(circle)
Line::new(Values::from_values_iter(circle))
.color(Color32::from_rgb(100, 200, 100))
.name("circle")
}
fn sin(&self) -> Curve {
fn sin(&self) -> Line {
let time = self.time;
Curve::from_explicit_callback(
Line::new(Values::from_explicit_callback(
move |x| 0.5 * (2.0 * x).sin() * time.sin(),
f64::NEG_INFINITY..=f64::INFINITY,
512,
)
))
.color(Color32::from_rgb(200, 100, 100))
.name("wave")
}
fn thingy(&self) -> Curve {
fn thingy(&self) -> Line {
let time = self.time;
Curve::from_parametric_callback(
Line::new(Values::from_parametric_callback(
move |t| ((2.0 * t + time).sin(), (3.0 * t).sin()),
0.0..=TAU,
512,
)
256,
))
.color(Color32::from_rgb(100, 150, 250))
.name("x = sin(2t), y = sin(3t)")
}
}
impl super::View for PlotDemo {
fn ui(&mut self, ui: &mut Ui) {
impl Widget for &mut LineDemo {
fn ui(self, ui: &mut Ui) -> Response {
self.options_ui(ui);
if self.animate {
ui.ctx().request_repaint();
self.time += ui.input().unstable_dt.at_most(1.0 / 30.0) as f64;
};
let mut plot = Plot::new("Demo Plot")
.curve(self.circle())
.curve(self.sin())
.curve(self.thingy())
.show_legend(self.legend)
.min_size(Vec2::new(200.0, 200.0));
let mut plot = Plot::new("Lines Demo")
.line(self.circle())
.line(self.sin())
.line(self.thingy())
.show_legend(self.legend);
if self.square {
plot = plot.view_aspect(1.0);
}
if self.proportional {
plot = plot.data_aspect(1.0);
}
ui.add(plot);
ui.add(plot)
}
}
#[derive(PartialEq)]
struct MarkerDemo {
fill_markers: bool,
marker_radius: f32,
custom_marker_color: bool,
marker_color: Color32,
}
impl Default for MarkerDemo {
fn default() -> Self {
Self {
fill_markers: true,
marker_radius: 5.0,
custom_marker_color: false,
marker_color: Color32::GRAY,
}
}
}
impl MarkerDemo {
fn markers(&self) -> Vec<Points> {
MarkerShape::all()
.into_iter()
.enumerate()
.map(|(i, marker)| {
let y_offset = i as f32 * 0.5 + 1.0;
let mut points = Points::new(Values::from_values(vec![
Value::new(1.0, 0.0 + y_offset),
Value::new(2.0, 0.5 + y_offset),
Value::new(3.0, 0.0 + y_offset),
Value::new(4.0, 0.5 + y_offset),
Value::new(5.0, 0.0 + y_offset),
Value::new(6.0, 0.5 + y_offset),
]))
.name(format!("{:?}", marker))
.filled(self.fill_markers)
.radius(self.marker_radius)
.shape(marker);
if self.custom_marker_color {
points = points.color(self.marker_color);
}
points
})
.collect()
}
}
impl Widget for &mut MarkerDemo {
fn ui(self, ui: &mut Ui) -> Response {
ui.horizontal(|ui| {
ui.checkbox(&mut self.fill_markers, "fill markers");
ui.add(
egui::DragValue::new(&mut self.marker_radius)
.speed(0.1)
.clamp_range(0.0..=f32::INFINITY)
.prefix("marker radius: "),
);
ui.checkbox(&mut self.custom_marker_color, "custom marker color");
if self.custom_marker_color {
ui.color_edit_button_srgba(&mut self.marker_color);
}
});
let mut markers_plot = Plot::new("Markers Demo").data_aspect(1.0);
for marker in self.markers() {
markers_plot = markers_plot.points(marker);
}
ui.add(markers_plot)
}
}
#[derive(PartialEq, Eq)]
enum Panel {
Lines,
Markers,
}
impl Default for Panel {
fn default() -> Self {
Self::Lines
}
}
#[derive(PartialEq, Default)]
pub struct PlotDemo {
line_demo: LineDemo,
marker_demo: MarkerDemo,
open_panel: Panel,
}
impl super::Demo for PlotDemo {
fn name(&self) -> &'static str {
"🗠 Plot"
}
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
use super::View;
Window::new(self.name())
.open(open)
.default_size(vec2(400.0, 400.0))
.scroll(false)
.show(ctx, |ui| self.ui(ui));
}
}
impl super::View for PlotDemo {
fn ui(&mut self, ui: &mut Ui) {
ui.vertical_centered(|ui| {
egui::reset_button(ui, self);
ui.add(crate::__egui_github_link_file!());
ui.label("Pan by dragging, or scroll (+ shift = horizontal).");
if cfg!(target_arch = "wasm32") {
ui.label("Zoom with ctrl / ⌘ + mouse wheel, or with pinch gesture.");
} else if cfg!(target_os = "macos") {
ui.label("Zoom with ctrl / ⌘ + scroll.");
} else {
ui.label("Zoom with ctrl + scroll.");
}
ui.label("Reset view with double-click.");
});
ui.separator();
ui.horizontal(|ui| {
ui.selectable_value(&mut self.open_panel, Panel::Lines, "Lines");
ui.selectable_value(&mut self.open_panel, Panel::Markers, "Markers");
});
ui.separator();
match self.open_panel {
Panel::Lines => {
ui.add(&mut self.line_demo);
}
Panel::Markers => {
ui.add(&mut self.marker_demo);
}
}
}
}

View File

@@ -205,14 +205,15 @@ impl WidgetGallery {
}
fn example_plot() -> egui::plot::Plot {
use egui::plot::{Line, Plot, Value, Values};
let n = 128;
let curve = egui::plot::Curve::from_values_iter((0..=n).map(|i| {
let line = Line::new(Values::from_values_iter((0..=n).map(|i| {
use std::f64::consts::TAU;
let x = egui::remap(i as f64, 0.0..=(n as f64), -TAU..=TAU);
egui::plot::Value::new(x, x.sin())
}));
egui::plot::Plot::new("Example Plot")
.curve(curve)
Value::new(x, x.sin())
})));
Plot::new("Example Plot")
.line(line)
.height(32.0)
.data_aspect(1.0)
}