From 203cbb0eae95aed2ecd0dca9fc8a0575df429fd1 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 3 Oct 2021 17:08:07 +0200 Subject: [PATCH 01/13] move to a basic plot builder with callback --- egui/src/widgets/plot/mod.rs | 254 ++++++++++-------- egui/src/widgets/plot/transform.rs | 1 + egui_demo_lib/src/apps/demo/plot_demo.rs | 70 ++--- egui_demo_lib/src/apps/demo/widget_gallery.rs | 6 +- 4 files changed, 177 insertions(+), 154 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 74b873e0825..d5e05c268c7 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -29,6 +29,7 @@ struct PlotMemory { hovered_entry: Option, hidden_items: HashSet, min_auto_bounds: Bounds, + last_screen_transform: Option, } // ---------------------------------------------------------------------------- @@ -51,9 +52,6 @@ struct PlotMemory { /// ``` pub struct Plot { id_source: Id, - next_auto_color_idx: usize, - - items: Vec>, center_x_axis: bool, center_y_axis: bool, @@ -80,9 +78,6 @@ impl Plot { pub fn new(id_source: impl std::hash::Hash) -> Self { Self { id_source: Id::new(id_source), - next_auto_color_idx: 0, - - items: Default::default(), center_x_axis: false, center_y_axis: false, @@ -105,108 +100,6 @@ impl Plot { } } - fn auto_color(&mut self) -> Color32 { - let i = self.next_auto_color_idx; - self.next_auto_color_idx += 1; - let golden_ratio = (5.0_f32.sqrt() - 1.0) / 2.0; // 0.61803398875 - let h = i as f32 * golden_ratio; - Hsva::new(h, 0.85, 0.5, 1.0).into() // TODO: OkLab or some other perspective color space - } - - /// Add a data lines. - pub fn line(mut self, mut line: Line) -> Self { - if line.series.is_empty() { - return self; - }; - - // Give the stroke an automatic color if no color has been assigned. - if line.stroke.color == Color32::TRANSPARENT { - line.stroke.color = self.auto_color(); - } - self.items.push(Box::new(line)); - self - } - - /// Add a polygon. The polygon has to be convex. - pub fn polygon(mut self, mut polygon: Polygon) -> Self { - if polygon.series.is_empty() { - return self; - }; - - // Give the stroke an automatic color if no color has been assigned. - if polygon.stroke.color == Color32::TRANSPARENT { - polygon.stroke.color = self.auto_color(); - } - self.items.push(Box::new(polygon)); - self - } - - /// Add a text. - pub fn text(mut self, text: Text) -> Self { - if text.text.is_empty() { - return self; - }; - - self.items.push(Box::new(text)); - self - } - - /// Add data points. - pub fn points(mut self, mut points: Points) -> Self { - if points.series.is_empty() { - return self; - }; - - // Give the points an automatic color if no color has been assigned. - if points.color == Color32::TRANSPARENT { - points.color = self.auto_color(); - } - self.items.push(Box::new(points)); - self - } - - /// Add arrows. - pub fn arrows(mut self, mut arrows: Arrows) -> Self { - if arrows.origins.is_empty() || arrows.tips.is_empty() { - return self; - }; - - // Give the arrows an automatic color if no color has been assigned. - if arrows.color == Color32::TRANSPARENT { - arrows.color = self.auto_color(); - } - self.items.push(Box::new(arrows)); - self - } - - /// Add an image. - pub fn image(mut self, image: PlotImage) -> Self { - self.items.push(Box::new(image)); - self - } - - /// Add a horizontal line. - /// Can be useful e.g. to show min/max bounds or similar. - /// Always fills the full width of the plot. - pub fn hline(mut self, mut hline: HLine) -> Self { - if hline.stroke.color == Color32::TRANSPARENT { - hline.stroke.color = self.auto_color(); - } - self.items.push(Box::new(hline)); - self - } - - /// Add a vertical line. - /// Can be useful e.g. to show min/max bounds or similar. - /// Always fills the full height of the plot. - pub fn vline(mut self, mut vline: VLine) -> Self { - if vline.stroke.color == Color32::TRANSPARENT { - vline.stroke.color = self.auto_color(); - } - self.items.push(Box::new(vline)); - self - } - /// width / height ratio of the data. /// For instance, it can be useful to set this to `1.0` for when the two axes show the same /// unit. @@ -316,14 +209,10 @@ impl Plot { self.show_axes = show; self } -} -impl Widget for Plot { - fn ui(self, ui: &mut Ui) -> Response { + pub fn build(self, ui: &mut Ui, build_fn: impl FnOnce(&mut PlotUi)) -> Response { let Self { id_source, - next_auto_color_idx: _, - mut items, center_x_axis, center_y_axis, allow_zoom, @@ -352,6 +241,7 @@ impl Widget for Plot { hovered_entry: None, hidden_items: HashSet::new(), min_auto_bounds, + last_screen_transform: None, }) .clone(); @@ -372,6 +262,7 @@ impl Widget for Plot { mut auto_bounds, mut hovered_entry, mut hidden_items, + last_screen_transform, .. } = memory; @@ -402,6 +293,16 @@ impl Widget for Plot { let (rect, response) = ui.allocate_exact_size(size, Sense::drag()); let plot_painter = ui.painter().sub_region(rect); + // Call the plot build function. + let mut plot_ui = PlotUi { + items: Vec::new(), + next_auto_color_idx: 0, + last_screen_transform, + mouse_position: response.hover_pos(), + }; + build_fn(&mut plot_ui); + let mut items = plot_ui.items; + // Background if show_background { plot_painter.add(epaint::RectShape { @@ -497,12 +398,12 @@ impl Widget for Plot { let bounds = *transform.bounds(); - let prepared = Prepared { + let prepared = PreparedPlot { items, show_x, show_y, show_axes, - transform, + transform: transform.clone(), }; prepared.ui(ui, &response); @@ -520,6 +421,7 @@ impl Widget for Plot { hovered_entry, hidden_items, min_auto_bounds, + last_screen_transform: Some(transform), }, ); @@ -531,7 +433,125 @@ impl Widget for Plot { } } -struct Prepared { +pub struct PlotUi { + items: Vec>, + next_auto_color_idx: usize, + last_screen_transform: Option, + mouse_position: Option, +} + +impl PlotUi { + fn auto_color(&mut self) -> Color32 { + let i = self.next_auto_color_idx; + self.next_auto_color_idx += 1; + let golden_ratio = (5.0_f32.sqrt() - 1.0) / 2.0; // 0.61803398875 + let h = i as f32 * golden_ratio; + Hsva::new(h, 0.85, 0.5, 1.0).into() // TODO: OkLab or some other perspective color space + } + + pub fn get_plot_mouse_position(&self) -> Option { + self.last_screen_transform + .as_ref() + .zip(self.mouse_position) + .map(|(tf, pos)| tf.value_from_position(pos)) + .map(|value| Pos2::new(value.x as f32, value.y as f32)) + } + + pub fn screen_to_plot_coordinates(&self, position: Pos2) -> Pos2 { + self.last_screen_transform + .as_ref() + .map(|tf| tf.position_from_value(&Value::new(position.x as f64, position.y as f64))) + .unwrap_or(Pos2::ZERO) + } + + /// Add a data lines. + pub fn line(&mut self, mut line: Line) { + if line.series.is_empty() { + return; + }; + + // Give the stroke an automatic color if no color has been assigned. + if line.stroke.color == Color32::TRANSPARENT { + line.stroke.color = self.auto_color(); + } + self.items.push(Box::new(line)); + } + + /// Add a polygon. The polygon has to be convex. + pub fn polygon(&mut self, mut polygon: Polygon) { + if polygon.series.is_empty() { + return; + }; + + // Give the stroke an automatic color if no color has been assigned. + if polygon.stroke.color == Color32::TRANSPARENT { + polygon.stroke.color = self.auto_color(); + } + self.items.push(Box::new(polygon)); + } + + /// Add a text. + pub fn text(&mut self, text: Text) { + if text.text.is_empty() { + return; + }; + + self.items.push(Box::new(text)); + } + + /// Add data points. + pub fn points(&mut self, mut points: Points) { + if points.series.is_empty() { + return; + }; + + // Give the points an automatic color if no color has been assigned. + if points.color == Color32::TRANSPARENT { + points.color = self.auto_color(); + } + self.items.push(Box::new(points)); + } + + /// Add arrows. + pub fn arrows(&mut self, mut arrows: Arrows) { + if arrows.origins.is_empty() || arrows.tips.is_empty() { + return; + }; + + // Give the arrows an automatic color if no color has been assigned. + if arrows.color == Color32::TRANSPARENT { + arrows.color = self.auto_color(); + } + self.items.push(Box::new(arrows)); + } + + /// Add an image. + pub fn image(&mut self, image: PlotImage) { + self.items.push(Box::new(image)); + } + + /// Add a horizontal line. + /// Can be useful e.g. to show min/max bounds or similar. + /// Always fills the full width of the plot. + pub fn hline(&mut self, mut hline: HLine) { + if hline.stroke.color == Color32::TRANSPARENT { + hline.stroke.color = self.auto_color(); + } + self.items.push(Box::new(hline)); + } + + /// Add a vertical line. + /// Can be useful e.g. to show min/max bounds or similar. + /// Always fills the full height of the plot. + pub fn vline(&mut self, mut vline: VLine) { + if vline.stroke.color == Color32::TRANSPARENT { + vline.stroke.color = self.auto_color(); + } + self.items.push(Box::new(vline)); + } +} + +struct PreparedPlot { items: Vec>, show_x: bool, show_y: bool, @@ -539,7 +559,7 @@ struct Prepared { transform: ScreenTransform, } -impl Prepared { +impl PreparedPlot { fn ui(self, ui: &mut Ui, response: &Response) { let mut shapes = Vec::new(); diff --git a/egui/src/widgets/plot/transform.rs b/egui/src/widgets/plot/transform.rs index 9de9d32d735..7175c3e36c3 100644 --- a/egui/src/widgets/plot/transform.rs +++ b/egui/src/widgets/plot/transform.rs @@ -118,6 +118,7 @@ impl Bounds { } /// Contains the screen rectangle and the plot bounds and provides methods to transform them. +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone)] pub(crate) struct ScreenTransform { /// The screen rectangle. diff --git a/egui_demo_lib/src/apps/demo/plot_demo.rs b/egui_demo_lib/src/apps/demo/plot_demo.rs index 85f01919fe6..4bf0192e7e7 100644 --- a/egui_demo_lib/src/apps/demo/plot_demo.rs +++ b/egui_demo_lib/src/apps/demo/plot_demo.rs @@ -143,18 +143,19 @@ impl Widget for &mut LineDemo { ui.ctx().request_repaint(); self.time += ui.input().unstable_dt.at_most(1.0 / 30.0) as f64; }; - let mut plot = Plot::new("lines_demo") - .line(self.circle()) - .line(self.sin()) - .line(self.thingy()) - .legend(Legend::default()); + let mut plot = Plot::new("lines_demo").legend(Legend::default()); if self.square { plot = plot.view_aspect(1.0); } if self.proportional { plot = plot.data_aspect(1.0); } - ui.add(plot) + plot.build(ui, |plot_ui| { + dbg!(plot_ui.get_plot_mouse_position()); + plot_ui.line(self.circle()); + plot_ui.line(self.sin()); + plot_ui.line(self.thingy()); + }) } } @@ -222,13 +223,14 @@ impl Widget for &mut MarkerDemo { } }); - let mut markers_plot = Plot::new("markers_demo") + let markers_plot = Plot::new("markers_demo") .data_aspect(1.0) .legend(Legend::default()); - for marker in self.markers() { - markers_plot = markers_plot.points(marker); - } - ui.add(markers_plot) + markers_plot.build(ui, |plot_ui| { + for marker in self.markers() { + plot_ui.points(marker); + } + }) } } @@ -287,15 +289,14 @@ impl Widget for &mut LegendDemo { ui.end_row(); }); - let legend_plot = Plot::new("legend_demo") - .line(LegendDemo::line_with_slope(0.5).name("lines")) - .line(LegendDemo::line_with_slope(1.0).name("lines")) - .line(LegendDemo::line_with_slope(2.0).name("lines")) - .line(LegendDemo::sin().name("sin(x)")) - .line(LegendDemo::cos().name("cos(x)")) - .legend(*config) - .data_aspect(1.0); - ui.add(legend_plot) + let legend_plot = Plot::new("legend_demo").legend(*config).data_aspect(1.0); + legend_plot.build(ui, |plot_ui| { + plot_ui.line(LegendDemo::line_with_slope(0.5).name("lines")); + plot_ui.line(LegendDemo::line_with_slope(1.0).name("lines")); + plot_ui.line(LegendDemo::line_with_slope(2.0).name("lines")); + plot_ui.line(LegendDemo::sin().name("sin(x)")); + plot_ui.line(LegendDemo::cos().name("cos(x)")); + }) } } @@ -347,24 +348,25 @@ impl Widget for &mut ItemsDemo { ); let plot = Plot::new("items_demo") - .hline(HLine::new(9.0).name("Lines horizontal")) - .hline(HLine::new(-9.0).name("Lines horizontal")) - .vline(VLine::new(9.0).name("Lines vertical")) - .vline(VLine::new(-9.0).name("Lines vertical")) - .line(line.name("Line with fill")) - .polygon(polygon.name("Convex polygon")) - .points(points.name("Points with stems")) - .text(Text::new(Value::new(-3.0, -3.0), "wow").name("Text")) - .text(Text::new(Value::new(-2.0, 2.5), "so graph").name("Text")) - .text(Text::new(Value::new(3.0, 3.0), "much color").name("Text")) - .text(Text::new(Value::new(2.5, -2.0), "such plot").name("Text")) - .image(image.name("Image")) - .arrows(arrows.name("Arrows")) .legend(Legend::default().position(Corner::RightBottom)) .show_x(false) .show_y(false) .data_aspect(1.0); - ui.add(plot) + plot.build(ui, |plot_ui| { + plot_ui.hline(HLine::new(9.0).name("Lines horizontal")); + plot_ui.hline(HLine::new(-9.0).name("Lines horizontal")); + plot_ui.vline(VLine::new(9.0).name("Lines vertical")); + plot_ui.vline(VLine::new(-9.0).name("Lines vertical")); + plot_ui.line(line.name("Line with fill")); + plot_ui.polygon(polygon.name("Convex polygon")); + plot_ui.points(points.name("Points with stems")); + plot_ui.text(Text::new(Value::new(-3.0, -3.0), "wow").name("Text")); + plot_ui.text(Text::new(Value::new(-2.0, 2.5), "so graph").name("Text")); + plot_ui.text(Text::new(Value::new(3.0, 3.0), "much color").name("Text")); + plot_ui.text(Text::new(Value::new(2.5, -2.0), "such plot").name("Text")); + plot_ui.image(image.name("Image")); + plot_ui.arrows(arrows.name("Arrows")); + }) } } diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index 9519ac2e6a4..3944049e9aa 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -212,7 +212,7 @@ impl WidgetGallery { ui.end_row(); ui.add(doc_link_label("Plot", "plot")); - ui.add(example_plot()); + example_plot(ui); ui.end_row(); ui.hyperlink_to( @@ -227,7 +227,7 @@ impl WidgetGallery { } } -fn example_plot() -> egui::plot::Plot { +fn example_plot(ui: &mut egui::Ui) -> egui::Response { use egui::plot::{Line, Plot, Value, Values}; let n = 128; let line = Line::new(Values::from_values_iter((0..=n).map(|i| { @@ -236,9 +236,9 @@ fn example_plot() -> egui::plot::Plot { Value::new(x, x.sin()) }))); Plot::new("example_plot") - .line(line) .height(32.0) .data_aspect(1.0) + .build(ui, |plot_ui| plot_ui.line(line)) } fn doc_link_label<'a>(title: &'a str, search_term: &'a str) -> impl egui::Widget + 'a { From 8cd2de3915c24e671ee91bc695f9ed727a0d2d1c Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 3 Oct 2021 23:42:54 +0200 Subject: [PATCH 02/13] add some interaction methods --- egui/src/widgets/plot/mod.rs | 45 +++++++++++++----------- egui/src/widgets/plot/transform.rs | 15 +++++++- egui_demo_lib/src/apps/demo/plot_demo.rs | 18 +++++++++- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index d5e05c268c7..4038c020b39 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -46,9 +46,7 @@ struct PlotMemory { /// Value::new(x, x.sin()) /// }); /// let line = Line::new(Values::from_values_iter(sin)); -/// ui.add( -/// Plot::new("my_plot").line(line).view_aspect(2.0) -/// ); +/// Plot::new("my_plot").view_aspect(2.0).build(ui, |plot_ui| plot_ui.line(line)); /// ``` pub struct Plot { id_source: Id, @@ -298,7 +296,7 @@ impl Plot { items: Vec::new(), next_auto_color_idx: 0, last_screen_transform, - mouse_position: response.hover_pos(), + response: response.clone(), }; build_fn(&mut plot_ui); let mut items = plot_ui.items; @@ -316,7 +314,6 @@ impl Plot { // Legend let legend = legend_config .and_then(|config| LegendWidget::try_new(rect, config, &items, &hidden_items)); - // Don't show hover cursor when hovering over legend. if hovered_entry.is_some() { show_x = false; @@ -334,6 +331,7 @@ impl Plot { // Move highlighted items to front. items.sort_by_key(|item| item.highlighted()); + // Allow double clicking to reset to automatic bounds. auto_bounds |= response.double_clicked_by(PointerButton::Primary); // Set bounds automatically based on content. @@ -344,18 +342,6 @@ impl Plot { .for_each(|item| bounds.merge(&item.get_bounds())); bounds.add_relative_margin(margin_fraction); } - // Make sure they are not empty. - if !bounds.is_valid() { - bounds = Bounds::new_symmetrical(1.0); - } - - // Scale axes so that the origin is in the center. - if center_x_axis { - bounds.make_x_symmetrical(); - }; - if center_y_axis { - bounds.make_y_symmetrical() - }; let mut transform = ScreenTransform::new(rect, bounds, center_x_axis, center_y_axis); @@ -437,7 +423,7 @@ pub struct PlotUi { items: Vec>, next_auto_color_idx: usize, last_screen_transform: Option, - mouse_position: Option, + response: Response, } impl PlotUi { @@ -449,19 +435,36 @@ impl PlotUi { Hsva::new(h, 0.85, 0.5, 1.0).into() // TODO: OkLab or some other perspective color space } - pub fn get_plot_mouse_position(&self) -> Option { + /// The pointer position in plot coordinates, if the pointer is inside the plot area. + pub fn pointer_coordinate(&self) -> Option { self.last_screen_transform .as_ref() - .zip(self.mouse_position) - .map(|(tf, pos)| tf.value_from_position(pos)) + .zip(self.response.hover_pos()) + // We need to subtract the drag delta since the last frame. + .map(|(tf, pos)| tf.value_from_position(pos - self.response.drag_delta())) .map(|value| Pos2::new(value.x as f32, value.y as f32)) } + /// The pointer drag delta in plot coordinates. + pub fn pointer_coordinate_drag_delta(&self) -> Vec2 { + self.last_screen_transform + .as_ref() + .map(|tf| { + let delta = self.response.drag_delta(); + let dp_dv = tf.dpos_dvalue(); + Vec2::new(delta.x / dp_dv[0] as f32, delta.y / dp_dv[1] as f32) + }) + .unwrap_or(Vec2::ZERO) + } + + /// Transform the screen coordinates to plot coordinates. pub fn screen_to_plot_coordinates(&self, position: Pos2) -> Pos2 { self.last_screen_transform .as_ref() .map(|tf| tf.position_from_value(&Value::new(position.x as f64, position.y as f64))) .unwrap_or(Pos2::ZERO) + // We need to subtract the drag delta since the last frame. + - self.response.drag_delta() } /// Add a data lines. diff --git a/egui/src/widgets/plot/transform.rs b/egui/src/widgets/plot/transform.rs index 7175c3e36c3..b115c6443ce 100644 --- a/egui/src/widgets/plot/transform.rs +++ b/egui/src/widgets/plot/transform.rs @@ -132,7 +132,20 @@ pub(crate) struct ScreenTransform { } impl ScreenTransform { - pub fn new(frame: Rect, bounds: Bounds, x_centered: bool, y_centered: bool) -> Self { + pub fn new(frame: Rect, mut bounds: Bounds, x_centered: bool, y_centered: bool) -> Self { + // Make sure they are not empty. + if !bounds.is_valid() { + bounds = Bounds::new_symmetrical(1.0); + } + + // Scale axes so that the origin is in the center. + if x_centered { + bounds.make_x_symmetrical(); + }; + if y_centered { + bounds.make_y_symmetrical() + }; + Self { frame, bounds, diff --git a/egui_demo_lib/src/apps/demo/plot_demo.rs b/egui_demo_lib/src/apps/demo/plot_demo.rs index 4bf0192e7e7..b4de53df6b2 100644 --- a/egui_demo_lib/src/apps/demo/plot_demo.rs +++ b/egui_demo_lib/src/apps/demo/plot_demo.rs @@ -14,6 +14,8 @@ struct LineDemo { square: bool, proportional: bool, line_style: LineStyle, + mouse_coordinate: Option, + mouse_coordinate_drag_delta: Vec2, } impl Default for LineDemo { @@ -26,6 +28,8 @@ impl Default for LineDemo { square: false, proportional: true, line_style: LineStyle::Solid, + mouse_coordinate: None, + mouse_coordinate_drag_delta: Vec2::ZERO, } } } @@ -150,8 +154,20 @@ impl Widget for &mut LineDemo { if self.proportional { plot = plot.data_aspect(1.0); } + let coordinate_text = if let Some(coordinate) = self.mouse_coordinate { + format!("x: {:.03}, y: {:.03}", coordinate.x, coordinate.y) + } else { + "None".to_string() + }; + ui.label(format!("mouse coordinate: {}", coordinate_text)); + let coordinate_text = format!( + "x: {:.03}, y: {:.03}", + self.mouse_coordinate_drag_delta.x, self.mouse_coordinate_drag_delta.y + ); + ui.label(format!("mouse coordinate drag delta: {}", coordinate_text)); plot.build(ui, |plot_ui| { - dbg!(plot_ui.get_plot_mouse_position()); + self.mouse_coordinate = plot_ui.pointer_coordinate(); + self.mouse_coordinate_drag_delta = plot_ui.pointer_coordinate_drag_delta(); plot_ui.line(self.circle()); plot_ui.line(self.sin()); plot_ui.line(self.thingy()); From 9cada8b7871692407927b80059fe60910c43be66 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 3 Oct 2021 23:52:28 +0200 Subject: [PATCH 03/13] move interaction demo to its own panel --- egui_demo_lib/src/apps/demo/plot_demo.rs | 64 +++++++++++++++++------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/egui_demo_lib/src/apps/demo/plot_demo.rs b/egui_demo_lib/src/apps/demo/plot_demo.rs index b4de53df6b2..2a05649569a 100644 --- a/egui_demo_lib/src/apps/demo/plot_demo.rs +++ b/egui_demo_lib/src/apps/demo/plot_demo.rs @@ -14,8 +14,6 @@ struct LineDemo { square: bool, proportional: bool, line_style: LineStyle, - mouse_coordinate: Option, - mouse_coordinate_drag_delta: Vec2, } impl Default for LineDemo { @@ -28,8 +26,6 @@ impl Default for LineDemo { square: false, proportional: true, line_style: LineStyle::Solid, - mouse_coordinate: None, - mouse_coordinate_drag_delta: Vec2::ZERO, } } } @@ -154,20 +150,7 @@ impl Widget for &mut LineDemo { if self.proportional { plot = plot.data_aspect(1.0); } - let coordinate_text = if let Some(coordinate) = self.mouse_coordinate { - format!("x: {:.03}, y: {:.03}", coordinate.x, coordinate.y) - } else { - "None".to_string() - }; - ui.label(format!("mouse coordinate: {}", coordinate_text)); - let coordinate_text = format!( - "x: {:.03}, y: {:.03}", - self.mouse_coordinate_drag_delta.x, self.mouse_coordinate_drag_delta.y - ); - ui.label(format!("mouse coordinate drag delta: {}", coordinate_text)); plot.build(ui, |plot_ui| { - self.mouse_coordinate = plot_ui.pointer_coordinate(); - self.mouse_coordinate_drag_delta = plot_ui.pointer_coordinate_drag_delta(); plot_ui.line(self.circle()); plot_ui.line(self.sin()); plot_ui.line(self.thingy()); @@ -386,12 +369,52 @@ impl Widget for &mut ItemsDemo { } } +#[derive(PartialEq)] +struct InteractionDemo { + pointer_coordinate: Option, + pointer_coordinate_drag_delta: Vec2, +} + +impl Default for InteractionDemo { + fn default() -> Self { + Self { + pointer_coordinate: None, + pointer_coordinate_drag_delta: Vec2::ZERO, + } + } +} + +impl Widget for &mut InteractionDemo { + fn ui(self, ui: &mut Ui) -> Response { + let coordinate_text = if let Some(coordinate) = self.pointer_coordinate { + format!("x: {:.03}, y: {:.03}", coordinate.x, coordinate.y) + } else { + "None".to_string() + }; + ui.label(format!("pointer coordinate: {}", coordinate_text)); + let coordinate_text = format!( + "x: {:.03}, y: {:.03}", + self.pointer_coordinate_drag_delta.x, self.pointer_coordinate_drag_delta.y + ); + ui.label(format!( + "pointer coordinate drag delta: {}", + coordinate_text + )); + let plot = Plot::new("interaction_demo"); + plot.build(ui, |plot_ui| { + self.pointer_coordinate = plot_ui.pointer_coordinate(); + self.pointer_coordinate_drag_delta = plot_ui.pointer_coordinate_drag_delta(); + }) + } +} + #[derive(PartialEq, Eq)] enum Panel { Lines, Markers, Legend, Items, + Interaction, } impl Default for Panel { @@ -406,6 +429,7 @@ pub struct PlotDemo { marker_demo: MarkerDemo, legend_demo: LegendDemo, items_demo: ItemsDemo, + interaction_demo: InteractionDemo, open_panel: Panel, } @@ -431,7 +455,7 @@ impl super::View for PlotDemo { ui.collapsing("Instructions", |ui| { 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."); + ui.label("Zoom with ctrl / ⌘ + pointer wheel, or with pinch gesture."); } else if cfg!(target_os = "macos") { ui.label("Zoom with ctrl / ⌘ + scroll."); } else { @@ -447,6 +471,7 @@ impl super::View for PlotDemo { 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::Items, "Items"); + ui.selectable_value(&mut self.open_panel, Panel::Interaction, "Interaction"); }); ui.separator(); @@ -463,6 +488,9 @@ impl super::View for PlotDemo { Panel::Items => { ui.add(&mut self.items_demo); } + Panel::Interaction => { + ui.add(&mut self.interaction_demo); + } } } } From 99745246a786d3a47f2285736cff436f7726a31f Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Mon, 4 Oct 2021 00:00:29 +0200 Subject: [PATCH 04/13] add another comment --- egui/src/widgets/plot/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 4038c020b39..4fad8397a8e 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -208,6 +208,7 @@ impl Plot { self } + /// Interact with and add items to the plot and finally draw it. pub fn build(self, ui: &mut Ui, build_fn: impl FnOnce(&mut PlotUi)) -> Response { let Self { id_source, From fb8b6d993602df6d49a92c0a7843618c3f7a92c8 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Mon, 4 Oct 2021 00:02:21 +0200 Subject: [PATCH 05/13] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2251e5deee8..c825cbae25c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md), [`eg * `Fonts::layout_job*`: New text layout engine allowing mixing fonts, colors and styles, with underlining and strikethrough. * Add feature `"serialize"` separatedly from `"persistence"`. * Add `egui::widgets::global_dark_light_mode_buttons` to easily add buttons for switching the egui theme. +* Add interaction methods to the plot. ### Changed 🔧 * Label text will now be centered, right-aligned and/or justified based on the layout. @@ -23,6 +24,7 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md), [`eg * MSRV (Minimum Supported Rust Version) is now `1.54.0`. * By default, `DragValue`:s no longer show a tooltip when hovered. Change with `Style::explanation_tooltips`. * Smaller and nicer color picker. +* Plots now provide a `build` method that has to be used to add items to and show the plot. ### Fixed 🐛 * Fix wrongly sized multiline `TextEdit` in justified layouts. From 10ff7bc9fb1309512bdafd1a912a5765aed7e8d5 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 10 Oct 2021 22:03:05 +0200 Subject: [PATCH 06/13] avoid a clone --- egui/src/widgets/plot/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 4fad8397a8e..917edd6484f 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -297,10 +297,14 @@ impl Plot { items: Vec::new(), next_auto_color_idx: 0, last_screen_transform, - response: response.clone(), + response, }; build_fn(&mut plot_ui); - let mut items = plot_ui.items; + let PlotUi { + mut items, + response, + .. + } = plot_ui; // Background if show_background { @@ -468,7 +472,7 @@ impl PlotUi { - self.response.drag_delta() } - /// Add a data lines. + /// Add a data line. pub fn line(&mut self, mut line: Line) { if line.series.is_empty() { return; From 1eba777dda261eac0d37cb2f38f7d289841b71f5 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 21 Oct 2021 19:05:16 +0200 Subject: [PATCH 07/13] add a semicolon --- egui/src/widgets/plot/transform.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/transform.rs b/egui/src/widgets/plot/transform.rs index b115c6443ce..058fd7ec6d8 100644 --- a/egui/src/widgets/plot/transform.rs +++ b/egui/src/widgets/plot/transform.rs @@ -143,7 +143,7 @@ impl ScreenTransform { bounds.make_x_symmetrical(); }; if y_centered { - bounds.make_y_symmetrical() + bounds.make_y_symmetrical(); }; Self { From c89b11fc857d6c9654b8094a0b522e0af6cf6d08 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Mon, 1 Nov 2021 15:25:48 +0100 Subject: [PATCH 08/13] fix context menu --- egui_demo_lib/src/apps/demo/context_menu.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/egui_demo_lib/src/apps/demo/context_menu.rs b/egui_demo_lib/src/apps/demo/context_menu.rs index 627ef4e867a..ac6d49aa398 100644 --- a/egui_demo_lib/src/apps/demo/context_menu.rs +++ b/egui_demo_lib/src/apps/demo/context_menu.rs @@ -69,7 +69,7 @@ impl super::View for ContextMenus { ui.label("Right-click plot to edit it!"); ui.horizontal(|ui| { - ui.add(self.example_plot()).context_menu(|ui| { + self.example_plot(ui).context_menu(|ui| { ui.menu_button("Plot", |ui| { if ui.radio_value(&mut self.plot, Plot::Sin, "Sin").clicked() || ui @@ -109,7 +109,7 @@ impl super::View for ContextMenus { } impl ContextMenus { - fn example_plot(&self) -> egui::plot::Plot { + fn example_plot(&self, ui: &mut egui::Ui) -> egui::Response { use egui::plot::{Line, Value, Values}; let n = 128; let line = Line::new(Values::from_values_iter((0..=n).map(|i| { @@ -127,10 +127,10 @@ impl ContextMenus { .allow_zoom(self.allow_zoom) .center_x_axis(self.center_x_axis) .center_x_axis(self.center_y_axis) - .line(line) .width(self.width) .height(self.height) .data_aspect(1.0) + .build(ui, |plot_ui| plot_ui.line(line)) } fn nested_menus(ui: &mut egui::Ui) { From 2020fab953441c1605fe59e5ec3c1c6322fb23c6 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 11 Nov 2021 22:45:52 +0100 Subject: [PATCH 09/13] update changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 799f9bb7aa1..32596a1d691 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * You can now read and write the cursor of a `TextEdit` ([#848](https://github.com/emilk/egui/pull/848)). * Most widgets containing text (`Label`, `Button` etc) now supports rich text ([#855](https://github.com/emilk/egui/pull/855)). * When using a custom font you can now specify a font index ([#873](https://github.com/emilk/egui/pull/873)). +* You can now read the plot coordinates of the mouse when building a `Plot` ([#766](https://github.com/emilk/egui/pull/766)). ### Changed 🔧 * Unifiy the four `Memory` data buckets (`data`, `data_temp`, `id_data` and `id_data_temp`) into a single `Memory::data`, with a new interface ([#836](https://github.com/emilk/egui/pull/836)). @@ -19,6 +20,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Replace `CtxRef::begin_frame` and `end_frame` with `CtxRef::run` ([#872](https://github.com/emilk/egui/pull/872)). * Replace `Ui::__test` with `egui::__run_test_ui` ([#872](https://github.com/emilk/egui/pull/872)). * Replace `scroll_delta` and `zoom_delta` in `RawInput` with `Event::Scroll` and `Event::Zoom`. +* Plots now provide a `build` method that has to be used to add items to and show the plot ([#766](https://github.com/emilk/egui/pull/766)). ### Fixed 🐛 * Fix `ComboBox` and other popups getting clipped to parent window ([#885](https://github.com/emilk/egui/pull/885)). @@ -33,6 +35,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * [sumibi-yakitori](https://github.com/sumibi-yakitori) ([#830](https://github.com/emilk/egui/pull/830)) * [5225225](https://github.com/5225225): ([#849](https://github.com/emilk/egui/pull/849)). * [t18b219k](https://github.com/t18b219k): ([#868](https://github.com/emilk/egui/pull/868)). +* [EmbersArc](https://github.com/EmbersArc): ([#766](https://github.com/emilk/egui/pull/766)). ## 0.15.0 - 2021-10-24 - Syntax highlighting and hscroll @@ -47,7 +50,6 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Add `ui.add_enabled_ui(bool, |ui| …)` to create a possibly disabled UI section. * Add feature `"serialize"` separatedly from `"persistence"`. * Add `egui::widgets::global_dark_light_mode_buttons` to easily add buttons for switching the egui theme. -* Add interaction methods to the plot. * `TextEdit` can now be used to show text which can be selected and copied, but not edited. * Add `Memory::caches` for caching things from one frame to the next. @@ -61,7 +63,6 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * MSRV (Minimum Supported Rust Version) is now `1.54.0`. * By default, `DragValue`:s no longer show a tooltip when hovered. Change with `Style::explanation_tooltips`. * Smaller and nicer color picker. -* Plots now provide a `build` method that has to be used to add items to and show the plot. * `ScrollArea` will auto-shrink to content size unless told otherwise using `ScollArea::auto_shrink`. * By default, `Slider`'s `clamp_to_range` is set to true. * Rename `TextEdit::enabled` to `TextEdit::interactive`. From ed034b840bd89d21f956e0e1d7b9c0e124813ddc Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 11 Nov 2021 22:56:46 +0100 Subject: [PATCH 10/13] Update egui/src/widgets/plot/mod.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/plot/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 2a8d0a52d7c..a2e3e15b787 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -445,12 +445,11 @@ impl PlotUi { /// The pointer position in plot coordinates, if the pointer is inside the plot area. pub fn pointer_coordinate(&self) -> Option { - self.last_screen_transform - .as_ref() - .zip(self.response.hover_pos()) - // We need to subtract the drag delta since the last frame. - .map(|(tf, pos)| tf.value_from_position(pos - self.response.drag_delta())) - .map(|value| Pos2::new(value.x as f32, value.y as f32)) + let last_screen_transform = self.last_screen_transform.as_ref()?; + // We need to subtract the drag delta to keep in sync with the frame-delayed screen transform: + let last_pos = self.response.hover_pos()? - self.response.drag_delta(); + let value = tf.last_screen_transform(last_pos); + Some(Pos2::new(value.x as f32, value.y as f32)) } /// The pointer drag delta in plot coordinates. From 89b131a01a0a6908ce3b505587cd190e97ee7cc9 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 11 Nov 2021 23:08:08 +0100 Subject: [PATCH 11/13] apply suggestions --- CHANGELOG.md | 2 +- egui/src/widgets/plot/items.rs | 10 ++++++++++ egui/src/widgets/plot/mod.rs | 12 +++++++----- egui_demo_lib/src/apps/demo/context_menu.rs | 2 +- egui_demo_lib/src/apps/demo/plot_demo.rs | 12 ++++++------ egui_demo_lib/src/apps/demo/widget_gallery.rs | 2 +- 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32596a1d691..ab36d46582c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Replace `CtxRef::begin_frame` and `end_frame` with `CtxRef::run` ([#872](https://github.com/emilk/egui/pull/872)). * Replace `Ui::__test` with `egui::__run_test_ui` ([#872](https://github.com/emilk/egui/pull/872)). * Replace `scroll_delta` and `zoom_delta` in `RawInput` with `Event::Scroll` and `Event::Zoom`. -* Plots now provide a `build` method that has to be used to add items to and show the plot ([#766](https://github.com/emilk/egui/pull/766)). +* Plots now provide a `show` method that has to be used to add items to and show the plot ([#766](https://github.com/emilk/egui/pull/766)). ### Fixed 🐛 * Fix `ComboBox` and other popups getting clipped to parent window ([#885](https://github.com/emilk/egui/pull/885)). diff --git a/egui/src/widgets/plot/items.rs b/egui/src/widgets/plot/items.rs index 6608b12a025..fc691331c81 100644 --- a/egui/src/widgets/plot/items.rs +++ b/egui/src/widgets/plot/items.rs @@ -30,6 +30,16 @@ impl Value { y: y.into(), } } + + #[inline(always)] + pub fn to_pos2(self) -> Pos2 { + Pos2::new(self.x as f32, self.y as f32) + } + + #[inline(always)] + pub fn to_vec2(self) -> Vec2 { + Vec2::new(self.x as f32, self.y as f32) + } } // ---------------------------------------------------------------------------- diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 2f79022dc49..cf8385a0fb4 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -218,7 +218,7 @@ impl Plot { } /// Interact with and add items to the plot and finally draw it. - pub fn build(self, ui: &mut Ui, build_fn: impl FnOnce(&mut PlotUi)) -> Response { + pub fn show(self, ui: &mut Ui, build_fn: impl FnOnce(&mut PlotUi)) -> Response { let Self { id_source, center_x_axis, @@ -427,6 +427,8 @@ impl Plot { } } +/// Provides methods to interact with a plot while building it. It is the single argument of the closure +/// provided to `Plot::show`. See [`Plot`] for an example of how to use it. pub struct PlotUi { items: Vec>, next_auto_color_idx: usize, @@ -444,12 +446,12 @@ impl PlotUi { } /// The pointer position in plot coordinates, if the pointer is inside the plot area. - pub fn pointer_coordinate(&self) -> Option { + pub fn pointer_coordinate(&self) -> Option { let last_screen_transform = self.last_screen_transform.as_ref()?; // We need to subtract the drag delta to keep in sync with the frame-delayed screen transform: let last_pos = self.response.hover_pos()? - self.response.drag_delta(); - let value = tf.last_screen_transform(last_pos); - Some(Pos2::new(value.x as f32, value.y as f32)) + let value = last_screen_transform.value_from_position(last_pos); + Some(value) } /// The pointer drag delta in plot coordinates. @@ -464,7 +466,7 @@ impl PlotUi { } /// Transform the screen coordinates to plot coordinates. - pub fn screen_to_plot_coordinates(&self, position: Pos2) -> Pos2 { + pub fn plot_from_screen(&self, position: Pos2) -> Pos2 { self.last_screen_transform .as_ref() .map_or(Pos2::ZERO, |tf| { diff --git a/egui_demo_lib/src/apps/demo/context_menu.rs b/egui_demo_lib/src/apps/demo/context_menu.rs index ac6d49aa398..fb8fff7c9bd 100644 --- a/egui_demo_lib/src/apps/demo/context_menu.rs +++ b/egui_demo_lib/src/apps/demo/context_menu.rs @@ -130,7 +130,7 @@ impl ContextMenus { .width(self.width) .height(self.height) .data_aspect(1.0) - .build(ui, |plot_ui| plot_ui.line(line)) + .show(ui, |plot_ui| plot_ui.line(line)) } fn nested_menus(ui: &mut egui::Ui) { diff --git a/egui_demo_lib/src/apps/demo/plot_demo.rs b/egui_demo_lib/src/apps/demo/plot_demo.rs index 9a2c919ffe0..a85d305cf99 100644 --- a/egui_demo_lib/src/apps/demo/plot_demo.rs +++ b/egui_demo_lib/src/apps/demo/plot_demo.rs @@ -150,7 +150,7 @@ impl Widget for &mut LineDemo { if self.proportional { plot = plot.data_aspect(1.0); } - plot.build(ui, |plot_ui| { + plot.show(ui, |plot_ui| { plot_ui.line(self.circle()); plot_ui.line(self.sin()); plot_ui.line(self.thingy()); @@ -225,7 +225,7 @@ impl Widget for &mut MarkerDemo { let markers_plot = Plot::new("markers_demo") .data_aspect(1.0) .legend(Legend::default()); - markers_plot.build(ui, |plot_ui| { + markers_plot.show(ui, |plot_ui| { for marker in self.markers() { plot_ui.points(marker); } @@ -289,7 +289,7 @@ impl Widget for &mut LegendDemo { }); let legend_plot = Plot::new("legend_demo").legend(*config).data_aspect(1.0); - legend_plot.build(ui, |plot_ui| { + legend_plot.show(ui, |plot_ui| { plot_ui.line(LegendDemo::line_with_slope(0.5).name("lines")); plot_ui.line(LegendDemo::line_with_slope(1.0).name("lines")); plot_ui.line(LegendDemo::line_with_slope(2.0).name("lines")); @@ -351,7 +351,7 @@ impl Widget for &mut ItemsDemo { .show_x(false) .show_y(false) .data_aspect(1.0); - plot.build(ui, |plot_ui| { + plot.show(ui, |plot_ui| { plot_ui.hline(HLine::new(9.0).name("Lines horizontal")); plot_ui.hline(HLine::new(-9.0).name("Lines horizontal")); plot_ui.vline(VLine::new(9.0).name("Lines vertical")); @@ -371,7 +371,7 @@ impl Widget for &mut ItemsDemo { #[derive(PartialEq)] struct InteractionDemo { - pointer_coordinate: Option, + pointer_coordinate: Option, pointer_coordinate_drag_delta: Vec2, } @@ -401,7 +401,7 @@ impl Widget for &mut InteractionDemo { coordinate_text )); let plot = Plot::new("interaction_demo"); - plot.build(ui, |plot_ui| { + plot.show(ui, |plot_ui| { self.pointer_coordinate = plot_ui.pointer_coordinate(); self.pointer_coordinate_drag_delta = plot_ui.pointer_coordinate_drag_delta(); }) diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index f34586acf40..8d2cf2760f9 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -237,7 +237,7 @@ fn example_plot(ui: &mut egui::Ui) -> egui::Response { egui::plot::Plot::new("example_plot") .height(32.0) .data_aspect(1.0) - .build(ui, |plot_ui| plot_ui.line(line)) + .show(ui, |plot_ui| plot_ui.line(line)) } fn doc_link_label<'a>(title: &'a str, search_term: &'a str) -> impl egui::Widget + 'a { From 4b6be4d22865896f57c9c3c4a36b068a00dc0867 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 11 Nov 2021 23:32:11 +0100 Subject: [PATCH 12/13] fix test --- egui/src/widgets/plot/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index cf8385a0fb4..282d5023250 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -55,7 +55,7 @@ impl PlotMemory { /// Value::new(x, x.sin()) /// }); /// let line = Line::new(Values::from_values_iter(sin)); -/// Plot::new("my_plot").view_aspect(2.0).build(ui, |plot_ui| plot_ui.line(line)); +/// Plot::new("my_plot").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line)); /// ``` pub struct Plot { id_source: Id, From 7ac223ec7fcb3bb5c2148896ba3949b9688a01fe Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 11 Nov 2021 23:36:23 +0100 Subject: [PATCH 13/13] fix doctest --- egui/src/widgets/plot/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 282d5023250..3085dc0127b 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -56,6 +56,7 @@ impl PlotMemory { /// }); /// let line = Line::new(Values::from_values_iter(sin)); /// Plot::new("my_plot").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line)); +/// # }); /// ``` pub struct Plot { id_source: Id,