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:
committed by
GitHub
parent
e320ef6c64
commit
8623909d82
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user