From 9d04b6bba3ff16ac77cf28b10e6803f244f05a50 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 10 Jul 2022 13:49:49 +0200 Subject: [PATCH 01/12] initial work --- Cargo.lock | 3 +- eframe/src/epi.rs | 2 +- egui/Cargo.toml | 2 + egui/src/widgets/plot/items/bar.rs | 8 +- egui/src/widgets/plot/items/box_elem.rs | 14 +- egui/src/widgets/plot/items/mod.rs | 132 +++++++++---------- egui/src/widgets/plot/items/rect_elem.rs | 20 +-- egui/src/widgets/plot/items/values.rs | 158 +++++++++++++++-------- egui/src/widgets/plot/mod.rs | 56 ++++---- egui/src/widgets/plot/transform.rs | 27 ++-- egui_demo_app/Cargo.toml | 2 +- egui_demo_lib/src/demo/context_menu.rs | 24 ++-- egui_demo_lib/src/demo/plot_demo.rs | 134 +++++++++++-------- egui_demo_lib/src/demo/widget_gallery.rs | 15 ++- 14 files changed, 338 insertions(+), 259 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bc2224b324..0374b4fe39d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1199,6 +1199,7 @@ name = "egui" version = "0.18.1" dependencies = [ "ahash 0.7.6", + "bytemuck", "document-features", "epaint", "nohash-hasher", @@ -3871,7 +3872,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "static_assertions", ] diff --git a/eframe/src/epi.rs b/eframe/src/epi.rs index 2bbe82f8d15..b94e96386cb 100644 --- a/eframe/src/epi.rs +++ b/eframe/src/epi.rs @@ -374,7 +374,7 @@ impl Theme { // ---------------------------------------------------------------------------- -/// WebGl Context options +/// WebGL Context options #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum WebGlContextOption { diff --git a/egui/Cargo.toml b/egui/Cargo.toml index eea1e829252..6037a39bc6d 100644 --- a/egui/Cargo.toml +++ b/egui/Cargo.toml @@ -57,6 +57,8 @@ serde = ["dep:serde", "epaint/serde"] [dependencies] epaint = { version = "0.18.1", path = "../epaint", default-features = false } +bytemuck = { version = "1.7.0", features = ["derive"] } + ahash = "0.7" nohash-hasher = "0.2" diff --git a/egui/src/widgets/plot/items/bar.rs b/egui/src/widgets/plot/items/bar.rs index 39f3a91fbce..6d27d1285e4 100644 --- a/egui/src/widgets/plot/items/bar.rs +++ b/egui/src/widgets/plot/items/bar.rs @@ -2,7 +2,7 @@ use crate::emath::NumExt; use crate::epaint::{Color32, RectShape, Rounding, Shape, Stroke}; use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement}; -use crate::plot::{BarChart, ScreenTransform, Value}; +use crate::plot::{BarChart, PlotPoint, ScreenTransform}; /// One bar in a [`BarChart`]. Potentially floating, allowing stacked bar charts. /// Width can be changed to allow variable-width histograms. @@ -157,15 +157,15 @@ impl RectElement for Bar { self.name.as_str() } - fn bounds_min(&self) -> Value { + fn bounds_min(&self) -> PlotPoint { self.point_at(self.argument - self.bar_width / 2.0, self.lower()) } - fn bounds_max(&self) -> Value { + fn bounds_max(&self) -> PlotPoint { self.point_at(self.argument + self.bar_width / 2.0, self.upper()) } - fn values_with_ruler(&self) -> Vec { + fn values_with_ruler(&self) -> Vec { let base = self.base_offset.unwrap_or(0.0); let value_center = self.point_at(self.argument, base + self.value); diff --git a/egui/src/widgets/plot/items/box_elem.rs b/egui/src/widgets/plot/items/box_elem.rs index 7cda17c8533..985df8eeb29 100644 --- a/egui/src/widgets/plot/items/box_elem.rs +++ b/egui/src/widgets/plot/items/box_elem.rs @@ -2,7 +2,7 @@ use crate::emath::NumExt; use crate::epaint::{Color32, RectShape, Rounding, Shape, Stroke}; use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement}; -use crate::plot::{BoxPlot, ScreenTransform, Value}; +use crate::plot::{BoxPlot, PlotPoint, ScreenTransform}; /// Contains the values of a single box in a box plot. #[derive(Clone, Debug, PartialEq)] @@ -161,8 +161,8 @@ impl BoxElem { let line_between = |v1, v2| { Shape::line_segment( [ - transform.position_from_value(&v1), - transform.position_from_value(&v2), + transform.position_from_point(&v1), + transform.position_from_point(&v2), ], stroke, ) @@ -236,19 +236,19 @@ impl RectElement for BoxElem { self.name.as_str() } - fn bounds_min(&self) -> Value { + fn bounds_min(&self) -> PlotPoint { let argument = self.argument - self.box_width.max(self.whisker_width) / 2.0; let value = self.spread.lower_whisker; self.point_at(argument, value) } - fn bounds_max(&self) -> Value { + fn bounds_max(&self) -> PlotPoint { let argument = self.argument + self.box_width.max(self.whisker_width) / 2.0; let value = self.spread.upper_whisker; self.point_at(argument, value) } - fn values_with_ruler(&self) -> Vec { + fn values_with_ruler(&self) -> Vec { let median = self.point_at(self.argument, self.spread.median); let q1 = self.point_at(self.argument, self.spread.quartile1); let q3 = self.point_at(self.argument, self.spread.quartile3); @@ -262,7 +262,7 @@ impl RectElement for BoxElem { self.orientation } - fn corner_value(&self) -> Value { + fn corner_value(&self) -> PlotPoint { self.point_at(self.argument, self.spread.upper_whisker) } diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index a38bde7ce65..3b23b1e5fb3 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -13,7 +13,7 @@ use values::{ClosestElem, PlotGeometry}; pub use bar::Bar; pub use box_elem::{BoxElem, BoxSpread}; -pub use values::{LineStyle, MarkerShape, Orientation, Value, Values}; +pub use values::{LineStyle, MarkerShape, Orientation, PlotPoint, PlotPoints}; mod bar; mod box_elem; @@ -49,7 +49,7 @@ pub(super) trait PlotItem { .iter() .enumerate() .map(|(index, value)| { - let pos = transform.position_from_value(value); + let pos = transform.position_from_point(value); let dist_sq = point.distance_sq(pos); ClosestElem { index, dist_sq } }) @@ -86,7 +86,7 @@ pub(super) trait PlotItem { // this method is only called, if the value is in the result set of find_closest() let value = points[elem.index]; - let pointer = plot.transform.position_from_value(&value); + let pointer = plot.transform.position_from_point(&value); shapes.push(Shape::circle_filled(pointer, 3.0, line_color)); rulers_at_value(pointer, value, self.name(), plot, shapes, label_formatter); @@ -169,8 +169,8 @@ impl PlotItem for HLine { .. } = self; let points = vec![ - transform.position_from_value(&Value::new(transform.bounds().min[0], *y)), - transform.position_from_value(&Value::new(transform.bounds().max[0], *y)), + transform.position_from_point(&PlotPoint::new(transform.bounds().min[0], *y)), + transform.position_from_point(&PlotPoint::new(transform.bounds().max[0], *y)), ]; style.style_line(points, *stroke, *highlight, shapes); } @@ -279,8 +279,8 @@ impl PlotItem for VLine { .. } = self; let points = vec![ - transform.position_from_value(&Value::new(*x, transform.bounds().min[1])), - transform.position_from_value(&Value::new(*x, transform.bounds().max[1])), + transform.position_from_point(&PlotPoint::new(*x, transform.bounds().min[1])), + transform.position_from_point(&PlotPoint::new(*x, transform.bounds().max[1])), ]; style.style_line(points, *stroke, *highlight, shapes); } @@ -316,8 +316,8 @@ impl PlotItem for VLine { } /// A series of values forming a path. -pub struct Line { - pub(super) series: Values, +pub struct Line<'v> { + pub(super) series: PlotPoints<'v>, pub(super) stroke: Stroke, pub(super) name: String, pub(super) highlight: bool, @@ -325,10 +325,10 @@ pub struct Line { pub(super) style: LineStyle, } -impl Line { - pub fn new(series: Values) -> Self { +impl<'v> Line<'v> { + pub fn new(series: impl Into>) -> Self { Self { - series, + series: series.into(), stroke: Stroke::new(1.0, Color32::TRANSPARENT), name: Default::default(), highlight: false, @@ -393,7 +393,7 @@ fn y_intersection(p1: &Pos2, p2: &Pos2, y: f32) -> Option { .then(|| ((y * (p1.x - p2.x)) - (p1.x * p2.y - p1.y * p2.x)) / (p1.y - p2.y)) } -impl PlotItem for Line { +impl PlotItem for Line<'_> { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let Self { series, @@ -405,9 +405,9 @@ impl PlotItem for Line { } = self; let values_tf: Vec<_> = series - .values + .points() .iter() - .map(|v| transform.position_from_value(v)) + .map(|v| transform.position_from_point(v)) .collect(); let n_values = values_tf.len(); @@ -421,7 +421,7 @@ impl PlotItem for Line { fill_alpha = (2.0 * fill_alpha).at_most(1.0); } let y = transform - .position_from_value(&Value::new(0.0, y_reference)) + .position_from_point(&PlotPoint::new(0.0, y_reference)) .y; let fill_color = Rgba::from(stroke.color) .to_opaque() @@ -474,7 +474,7 @@ impl PlotItem for Line { } fn geometry(&self) -> PlotGeometry<'_> { - PlotGeometry::Points(&self.series.values) + PlotGeometry::Points(self.series.points()) } fn get_bounds(&self) -> PlotBounds { @@ -483,8 +483,8 @@ impl PlotItem for Line { } /// A convex polygon. -pub struct Polygon { - pub(super) series: Values, +pub struct Polygon<'v> { + pub(super) series: PlotPoints<'v>, pub(super) stroke: Stroke, pub(super) name: String, pub(super) highlight: bool, @@ -492,10 +492,10 @@ pub struct Polygon { pub(super) style: LineStyle, } -impl Polygon { - pub fn new(series: Values) -> Self { +impl<'v> Polygon<'v> { + pub fn new(series: impl Into>) -> Self { Self { - series, + series: series.into(), stroke: Stroke::new(1.0, Color32::TRANSPARENT), name: Default::default(), highlight: false, @@ -554,7 +554,7 @@ impl Polygon { } } -impl PlotItem for Polygon { +impl PlotItem for Polygon<'_> { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let Self { series, @@ -570,9 +570,9 @@ impl PlotItem for Polygon { } let mut values_tf: Vec<_> = series - .values + .points() .iter() - .map(|v| transform.position_from_value(v)) + .map(|v| transform.position_from_point(v)) .collect(); let fill = Rgba::from(stroke.color).to_opaque().multiply(fill_alpha); @@ -604,7 +604,7 @@ impl PlotItem for Polygon { } fn geometry(&self) -> PlotGeometry<'_> { - PlotGeometry::Points(&self.series.values) + PlotGeometry::Points(self.series.points()) } fn get_bounds(&self) -> PlotBounds { @@ -616,7 +616,7 @@ impl PlotItem for Polygon { #[derive(Clone)] pub struct Text { pub(super) text: WidgetText, - pub(super) position: Value, + pub(super) position: PlotPoint, pub(super) name: String, pub(super) highlight: bool, pub(super) color: Color32, @@ -624,7 +624,7 @@ pub struct Text { } impl Text { - pub fn new(position: Value, text: impl Into) -> Self { + pub fn new(position: PlotPoint, text: impl Into) -> Self { Self { text: text.into(), position, @@ -679,7 +679,7 @@ impl PlotItem for Text { .clone() .into_galley(ui, Some(false), f32::INFINITY, TextStyle::Small); - let pos = transform.position_from_value(&self.position); + let pos = transform.position_from_point(&self.position); let rect = self .anchor .anchor_rect(Rect::from_min_size(pos, galley.size())); @@ -729,8 +729,8 @@ impl PlotItem for Text { } /// A set of points. -pub struct Points { - pub(super) series: Values, +pub struct Points<'v> { + pub(super) series: PlotPoints<'v>, pub(super) shape: MarkerShape, /// Color of the marker. `Color32::TRANSPARENT` means that it will be picked automatically. pub(super) color: Color32, @@ -743,10 +743,10 @@ pub struct Points { pub(super) stems: Option, } -impl Points { - pub fn new(series: Values) -> Self { +impl<'v> Points<'v> { + pub fn new(series: impl Into>) -> Self { Self { - series, + series: series.into(), shape: MarkerShape::Circle, color: Color32::TRANSPARENT, filled: true, @@ -806,7 +806,7 @@ impl Points { } } -impl PlotItem for Points { +impl PlotItem for Points<'_> { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let sqrt_3 = 3_f32.sqrt(); let frac_sqrt_3_2 = 3_f32.sqrt() / 2.0; @@ -838,12 +838,12 @@ impl PlotItem for Points { } let y_reference = - stems.map(|y| transform.position_from_value(&Value::new(0.0, y)).y as f32); + stems.map(|y| transform.position_from_point(&PlotPoint::new(0.0, y)).y as f32); series - .values + .points() .iter() - .map(|value| transform.position_from_value(value)) + .map(|value| transform.position_from_point(value)) .for_each(|center| { let tf = |dx: f32, dy: f32| -> Pos2 { center + radius * vec2(dx, dy) }; @@ -956,7 +956,7 @@ impl PlotItem for Points { } fn geometry(&self) -> PlotGeometry<'_> { - PlotGeometry::Points(&self.series.values) + PlotGeometry::Points(self.series.points()) } fn get_bounds(&self) -> PlotBounds { @@ -965,19 +965,19 @@ impl PlotItem for Points { } /// A set of arrows. -pub struct Arrows { - pub(super) origins: Values, - pub(super) tips: Values, +pub struct Arrows<'v> { + pub(super) origins: PlotPoints<'v>, + pub(super) tips: PlotPoints<'v>, pub(super) color: Color32, pub(super) name: String, pub(super) highlight: bool, } -impl Arrows { - pub fn new(origins: Values, tips: Values) -> Self { +impl<'v> Arrows<'v> { + pub fn new(origins: impl Into>, tips: impl Into>) -> Self { Self { - origins, - tips, + origins: origins.into(), + tips: tips.into(), color: Color32::TRANSPARENT, name: Default::default(), highlight: false, @@ -1009,7 +1009,7 @@ impl Arrows { } } -impl PlotItem for Arrows { +impl PlotItem for Arrows<'_> { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { use crate::emath::*; let Self { @@ -1021,13 +1021,13 @@ impl PlotItem for Arrows { } = self; let stroke = Stroke::new(if *highlight { 2.0 } else { 1.0 }, *color); origins - .values + .points() .iter() - .zip(tips.values.iter()) + .zip(tips.points().iter()) .map(|(origin, tip)| { ( - transform.position_from_value(origin), - transform.position_from_value(tip), + transform.position_from_point(origin), + transform.position_from_point(tip), ) }) .for_each(|(origin, tip)| { @@ -1071,7 +1071,7 @@ impl PlotItem for Arrows { } fn geometry(&self) -> PlotGeometry<'_> { - PlotGeometry::Points(&self.origins.values) + PlotGeometry::Points(self.origins.points()) } fn get_bounds(&self) -> PlotBounds { @@ -1082,7 +1082,7 @@ impl PlotItem for Arrows { /// An image in the plot. #[derive(Clone)] pub struct PlotImage { - pub(super) position: Value, + pub(super) position: PlotPoint, pub(super) texture_id: TextureId, pub(super) uv: Rect, pub(super) size: Vec2, @@ -1096,7 +1096,7 @@ impl PlotImage { /// Create a new image with position and size in plot coordinates. pub fn new( texture_id: impl Into, - center_position: Value, + center_position: PlotPoint, size: impl Into, ) -> Self { Self { @@ -1161,16 +1161,16 @@ impl PlotItem for PlotImage { .. } = self; let rect = { - let left_top = Value::new( + let left_top = PlotPoint::new( position.x as f32 - size.x / 2.0, position.y as f32 - size.y / 2.0, ); - let right_bottom = Value::new( + let right_bottom = PlotPoint::new( position.x as f32 + size.x / 2.0, position.y as f32 + size.y / 2.0, ); - let left_top_tf = transform.position_from_value(&left_top); - let right_bottom_tf = transform.position_from_value(&right_bottom); + let left_top_tf = transform.position_from_point(&left_top); + let right_bottom_tf = transform.position_from_point(&right_bottom); Rect::from_two_pos(left_top_tf, right_bottom_tf) }; Image::new(*texture_id, *size) @@ -1211,11 +1211,11 @@ impl PlotItem for PlotImage { fn get_bounds(&self) -> PlotBounds { let mut bounds = PlotBounds::NOTHING; - let left_top = Value::new( + let left_top = PlotPoint::new( self.position.x as f32 - self.size.x / 2.0, self.position.y as f32 - self.size.y / 2.0, ); - let right_bottom = Value::new( + let right_bottom = PlotPoint::new( self.position.x as f32 + self.size.x / 2.0, self.position.y as f32 + self.size.y / 2.0, ); @@ -1587,8 +1587,8 @@ fn add_rulers_and_text( // Rulers for argument (usually vertical) if show_argument { - let push_argument_ruler = |argument: Value, shapes: &mut Vec| { - let position = plot.transform.position_from_value(&argument); + let push_argument_ruler = |argument: PlotPoint, shapes: &mut Vec| { + let position = plot.transform.position_from_point(&argument); let line = match orientation { Orientation::Horizontal => horizontal_line(position, plot.transform, line_color), Orientation::Vertical => vertical_line(position, plot.transform, line_color), @@ -1603,8 +1603,8 @@ fn add_rulers_and_text( // Rulers for values (usually horizontal) if show_values { - let push_value_ruler = |value: Value, shapes: &mut Vec| { - let position = plot.transform.position_from_value(&value); + let push_value_ruler = |value: PlotPoint, shapes: &mut Vec| { + let position = plot.transform.position_from_point(&value); let line = match orientation { Orientation::Horizontal => vertical_line(position, plot.transform, line_color), Orientation::Vertical => horizontal_line(position, plot.transform, line_color), @@ -1633,7 +1633,7 @@ fn add_rulers_and_text( let corner_value = elem.corner_value(); shapes.push(Shape::text( &*plot.ui.fonts(), - plot.transform.position_from_value(&corner_value) + vec2(3.0, -2.0), + plot.transform.position_from_point(&corner_value) + vec2(3.0, -2.0), Align2::LEFT_BOTTOM, text, font_id, @@ -1646,7 +1646,7 @@ fn add_rulers_and_text( #[allow(clippy::too_many_arguments)] pub(super) fn rulers_at_value( pointer: Pos2, - value: Value, + value: PlotPoint, name: &str, plot: &PlotConfig<'_>, shapes: &mut Vec, diff --git a/egui/src/widgets/plot/items/rect_elem.rs b/egui/src/widgets/plot/items/rect_elem.rs index 395b2ca48f2..fa9ce441a4b 100644 --- a/egui/src/widgets/plot/items/rect_elem.rs +++ b/egui/src/widgets/plot/items/rect_elem.rs @@ -1,4 +1,4 @@ -use super::{Orientation, Value}; +use super::{Orientation, PlotPoint}; use crate::plot::transform::{PlotBounds, ScreenTransform}; use epaint::emath::NumExt; use epaint::{Color32, Rgba, Stroke}; @@ -6,8 +6,8 @@ use epaint::{Color32, Rgba, Stroke}; /// Trait that abstracts from rectangular 'Value'-like elements, such as bars or boxes pub(super) trait RectElement { fn name(&self) -> &str; - fn bounds_min(&self) -> Value; - fn bounds_max(&self) -> Value; + fn bounds_min(&self) -> PlotPoint; + fn bounds_max(&self) -> PlotPoint; fn bounds(&self) -> PlotBounds { let mut bounds = PlotBounds::NOTHING; @@ -17,29 +17,29 @@ pub(super) trait RectElement { } /// At which argument (input; usually X) there is a ruler (usually vertical) - fn arguments_with_ruler(&self) -> Vec { + fn arguments_with_ruler(&self) -> Vec { // Default: one at center vec![self.bounds().center()] } /// At which value (output; usually Y) there is a ruler (usually horizontal) - fn values_with_ruler(&self) -> Vec; + fn values_with_ruler(&self) -> Vec; /// The diagram's orientation (vertical/horizontal) fn orientation(&self) -> Orientation; /// Get X/Y-value for (argument, value) pair, taking into account orientation - fn point_at(&self, argument: f64, value: f64) -> Value { + fn point_at(&self, argument: f64, value: f64) -> PlotPoint { match self.orientation() { - Orientation::Horizontal => Value::new(value, argument), - Orientation::Vertical => Value::new(argument, value), + Orientation::Horizontal => PlotPoint::new(value, argument), + Orientation::Vertical => PlotPoint::new(argument, value), } } /// Right top of the rectangle (position of text) - fn corner_value(&self) -> Value { + fn corner_value(&self) -> PlotPoint { //self.point_at(self.position + self.width / 2.0, value) - Value { + PlotPoint { x: self.bounds_max().x, y: self.bounds_max().y, } diff --git a/egui/src/widgets/plot/items/values.rs b/egui/src/widgets/plot/items/values.rs index 0a0f2d23894..0aff7e7b8bf 100644 --- a/egui/src/widgets/plot/items/values.rs +++ b/egui/src/widgets/plot/items/values.rs @@ -7,8 +7,9 @@ use crate::plot::transform::PlotBounds; /// /// Uses f64 for improved accuracy to enable plotting /// large values (e.g. unix time on x axis). -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Value { +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +pub struct PlotPoint { /// This is often something monotonically increasing, such as time, but doesn't have to be. /// Goes from left to right. pub x: f64, @@ -16,7 +17,13 @@ pub struct Value { pub y: f64, } -impl Value { +impl From<[f64; 2]> for PlotPoint { + fn from(point: [f64; 2]) -> Self { + bytemuck::cast(point) + } +} + +impl PlotPoint { #[inline(always)] pub fn new(x: impl Into, y: impl Into) -> Self { Self { @@ -140,22 +147,45 @@ impl Default for Orientation { // ---------------------------------------------------------------------------- -#[derive(Default)] -pub struct Values { - pub(super) values: Vec, - generator: Option, +pub enum PlotPoints<'v> { + Borrowed(&'v [PlotPoint]), + Owned(Vec), + Generator(ExplicitGenerator), } -impl Values { - pub fn from_values(values: Vec) -> Self { - Self { - values, - generator: None, - } +impl From<[f64; 2]> for PlotPoints<'_> { + fn from(coordinate: [f64; 2]) -> Self { + Self::new(vec![coordinate]) } +} + +impl From> for PlotPoints<'_> { + fn from(coordinates: Vec<[f64; 2]>) -> Self { + Self::new(coordinates) + } +} - pub fn from_values_iter(iter: impl Iterator) -> Self { - Self::from_values(iter.collect()) +impl<'v> From<&'v [[f64; 2]]> for PlotPoints<'v> { + fn from(coordinates: &'v [[f64; 2]]) -> Self { + Self::from_slice(coordinates) + } +} + +impl<'v> PlotPoints<'v> { + pub fn new(points: Vec<[f64; 2]>) -> Self { + Self::Owned(bytemuck::cast_vec(points)) + } + + pub fn from_slice(points: &'v [[f64; 2]]) -> Self { + Self::Borrowed(bytemuck::cast_slice(points)) + } + + pub fn points(&self) -> &[PlotPoint] { + match self { + PlotPoints::Borrowed(points) => points, + PlotPoints::Owned(points) => points.as_slice(), + PlotPoints::Generator(_) => &[], + } } /// Draw a line based on a function `y=f(x)`, a range (which can be infinite) for x and the number of points. @@ -180,10 +210,7 @@ impl Values { points, }; - Self { - values: Vec::new(), - generator: Some(generator), - } + Self::Generator(generator) } /// Draw a line based on a function `(x,y)=f(t)`, a range for t and the number of points. @@ -208,48 +235,61 @@ impl Values { } else { (end - start) / points as f64 }; - let values = (0..points).map(|i| { - let t = start + i as f64 * increment; - let (x, y) = function(t); - Value { x, y } - }); - Self::from_values_iter(values) + let values = (0..points) + .map(|i| { + let t = start + i as f64 * increment; + let (x, y) = function(t); + [x, y].into() + }) + .collect(); + Self::Owned(values) } /// From a series of y-values. /// The x-values will be the indices of these values pub fn from_ys_f32(ys: &[f32]) -> Self { - let values: Vec = ys + let points: Vec<[f64; 2]> = ys .iter() .enumerate() - .map(|(i, &y)| Value { - x: i as f64, - y: y as f64, - }) + .map(|(i, &y)| [i as f64, y as f64]) .collect(); - Self::from_values(values) + Self::new(points) + } + + /// From a series of y-values. + /// The x-values will be the indices of these values + pub fn from_ys_f64(ys: &[f64]) -> Self { + let points: Vec<[f64; 2]> = ys.iter().enumerate().map(|(i, &y)| [i as f64, y]).collect(); + Self::new(points) } /// Returns true if there are no data points available and there is no function to generate any. pub(crate) fn is_empty(&self) -> bool { - self.generator.is_none() && self.values.is_empty() + match self { + PlotPoints::Borrowed(points) => points.is_empty(), + PlotPoints::Owned(points) => points.is_empty(), + PlotPoints::Generator(_) => false, + } } /// If initialized with a generator function, this will generate `n` evenly spaced points in the /// given range. pub(super) fn generate_points(&mut self, x_range: RangeInclusive) { - if let Some(generator) = self.generator.take() { - if let Some(intersection) = Self::range_intersection(&x_range, &generator.x_range) { - let increment = - (intersection.end() - intersection.start()) / (generator.points - 1) as f64; - self.values = (0..generator.points) - .map(|i| { - let x = intersection.start() + i as f64 * increment; - let y = (generator.function)(x); - Value { x, y } - }) - .collect(); - } + if let Self::Generator(generator) = self { + let points = Self::range_intersection(&x_range, &generator.x_range) + .map(|intersection| { + let increment = + (intersection.end() - intersection.start()) / (generator.points - 1) as f64; + (0..generator.points) + .map(|i| { + let x = intersection.start() + i as f64 * increment; + let y = (generator.function)(x); + [x, y].into() + }) + .collect() + }) + .unwrap_or_default(); + *self = Self::Owned(points); } } @@ -264,18 +304,22 @@ impl Values { } pub(super) fn get_bounds(&self) -> PlotBounds { - if self.values.is_empty() { - if let Some(generator) = &self.generator { - generator.estimate_bounds() - } else { - PlotBounds::NOTHING + match self { + PlotPoints::Borrowed(points) => { + let mut bounds = PlotBounds::NOTHING; + for point in points.iter() { + bounds.extend_with(point); + } + bounds } - } else { - let mut bounds = PlotBounds::NOTHING; - for value in &self.values { - bounds.extend_with(value); + PlotPoints::Owned(points) => { + let mut bounds = PlotBounds::NOTHING; + for point in points { + bounds.extend_with(point); + } + bounds } - bounds + PlotPoints::Generator(generator) => generator.estimate_bounds(), } } } @@ -318,13 +362,13 @@ impl MarkerShape { // ---------------------------------------------------------------------------- -/// Query the values of the plot, for geometric relations like closest checks +/// Query the points of the plot, for geometric relations like closest checks pub(crate) enum PlotGeometry<'a> { /// No geometry based on single elements (examples: text, image, horizontal/vertical line) None, /// Point values (X-Y graphs) - Points(&'a [Value]), + Points(&'a [PlotPoint]), /// Rectangles (examples: boxes or bars) // Has currently no data, as it would require copying rects or iterating a list of pointers. @@ -335,7 +379,7 @@ pub(crate) enum PlotGeometry<'a> { // ---------------------------------------------------------------------------- /// Describes a function y = f(x) with an optional range for x and a number of points. -struct ExplicitGenerator { +pub struct ExplicitGenerator { function: Box f64>, x_range: RangeInclusive, points: usize, diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index dc7c5e2a3d8..f579b582303 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -13,7 +13,7 @@ use transform::ScreenTransform; pub use items::{ Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, HLine, Line, LineStyle, MarkerShape, - Orientation, PlotImage, Points, Polygon, Text, VLine, Value, Values, + Orientation, PlotImage, PlotPoint, PlotPoints, Points, Polygon, Text, VLine, }; pub use legend::{Corner, Legend}; pub use transform::PlotBounds; @@ -22,7 +22,7 @@ mod items; mod legend; mod transform; -type LabelFormatterFn = dyn Fn(&str, &Value) -> String; +type LabelFormatterFn = dyn Fn(&str, &PlotPoint) -> String; type LabelFormatter = Option>; type AxisFormatterFn = dyn Fn(f64, &RangeInclusive) -> String; type AxisFormatter = Option>; @@ -32,12 +32,12 @@ type GridSpacer = Box; /// Specifies the coordinates formatting when passed to [`Plot::coordinates_formatter`]. pub struct CoordinatesFormatter { - function: Box String>, + function: Box String>, } impl CoordinatesFormatter { /// Create a new formatter based on the pointer coordinate and the plot bounds. - pub fn new(function: impl Fn(&Value, &PlotBounds) -> String + 'static) -> Self { + pub fn new(function: impl Fn(&PlotPoint, &PlotBounds) -> String + 'static) -> Self { Self { function: Box::new(function), } @@ -52,7 +52,7 @@ impl CoordinatesFormatter { } } - fn format(&self, value: &Value, bounds: &PlotBounds) -> String { + fn format(&self, value: &PlotPoint, bounds: &PlotBounds) -> String { (self.function)(value, bounds) } } @@ -178,12 +178,12 @@ impl LinkedAxisGroup { /// /// ``` /// # egui::__run_test_ui(|ui| { -/// use egui::plot::{Line, Plot, Value, Values}; -/// let sin = (0..1000).map(|i| { +/// use egui::plot::{Line, Plot}; +/// let sin: Vec<_> = (0..1000).map(|i| { /// let x = i as f64 * 0.01; -/// Value::new(x, x.sin()) -/// }); -/// let line = Line::new(Values::from_values_iter(sin)); +/// [x, x.sin()] +/// }).collect(); +/// let line = Line::new(sin); /// Plot::new("my_plot").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line)); /// # }); /// ``` @@ -359,12 +359,12 @@ impl Plot { /// /// ``` /// # egui::__run_test_ui(|ui| { - /// use egui::plot::{Line, Plot, Value, Values}; - /// let sin = (0..1000).map(|i| { + /// use egui::plot::{Line, Plot}; + /// let sin: Vec<_> = (0..1000).map(|i| { /// let x = i as f64 * 0.01; - /// Value::new(x, x.sin()) - /// }); - /// let line = Line::new(Values::from_values_iter(sin)); + /// [x, x.sin()] + /// }).collect(); + /// let line = Line::new(sin); /// Plot::new("my_plot").view_aspect(2.0) /// .label_formatter(|name, value| { /// if !name.is_empty() { @@ -378,7 +378,7 @@ impl Plot { /// ``` pub fn label_formatter( mut self, - label_formatter: impl Fn(&str, &Value) -> String + 'static, + label_formatter: impl Fn(&str, &PlotPoint) -> String + 'static, ) -> Self { self.label_formatter = Some(Box::new(label_formatter)); self @@ -861,7 +861,7 @@ pub struct PlotUi { ctx: Context, } -impl PlotUi { +impl<'p> PlotUi { fn auto_color(&mut self) -> Color32 { let i = self.next_auto_color_idx; self.next_auto_color_idx += 1; @@ -892,7 +892,7 @@ impl PlotUi { } /// The pointer position in plot coordinates. Independent of whether the pointer is in the plot area. - pub fn pointer_coordinate(&self) -> Option { + pub fn pointer_coordinate(&self) -> Option { // We need to subtract the drag delta to keep in sync with the frame-delayed screen transform: let last_pos = self.ctx().input().pointer.latest_pos()? - self.response.drag_delta(); let value = self.plot_from_screen(last_pos); @@ -907,17 +907,17 @@ impl PlotUi { } /// Transform the plot coordinates to screen coordinates. - pub fn screen_from_plot(&self, position: Value) -> Pos2 { - self.last_screen_transform.position_from_value(&position) + pub fn screen_from_plot(&self, position: PlotPoint) -> Pos2 { + self.last_screen_transform.position_from_point(&position) } /// Transform the screen coordinates to plot coordinates. - pub fn plot_from_screen(&self, position: Pos2) -> Value { + pub fn plot_from_screen(&self, position: Pos2) -> PlotPoint { self.last_screen_transform.value_from_position(position) } /// Add a data line. - pub fn line(&mut self, mut line: Line) { + pub fn line(&mut self, mut line: Line<'static>) { if line.series.is_empty() { return; }; @@ -930,7 +930,7 @@ impl PlotUi { } /// Add a polygon. The polygon has to be convex. - pub fn polygon(&mut self, mut polygon: Polygon) { + pub fn polygon(&mut self, mut polygon: Polygon<'static>) { if polygon.series.is_empty() { return; }; @@ -952,7 +952,7 @@ impl PlotUi { } /// Add data points. - pub fn points(&mut self, mut points: Points) { + pub fn points(&mut self, mut points: Points<'static>) { if points.series.is_empty() { return; }; @@ -965,7 +965,7 @@ impl PlotUi { } /// Add arrows. - pub fn arrows(&mut self, mut arrows: Arrows) { + pub fn arrows(&mut self, mut arrows: Arrows<'static>) { if arrows.origins.is_empty() || arrows.tips.is_empty() { return; }; @@ -1188,12 +1188,12 @@ impl PreparedPlot { let value_main = step.value; let value = if axis == 0 { - Value::new(value_main, value_cross) + PlotPoint::new(value_main, value_cross) } else { - Value::new(value_cross, value_main) + PlotPoint::new(value_cross, value_main) }; - let pos_in_gui = transform.position_from_value(&value); + let pos_in_gui = transform.position_from_point(&value); let spacing_in_points = (transform.dpos_dvalue()[axis] * step.step_size).abs() as f32; let line_alpha = remap_clamp( diff --git a/egui/src/widgets/plot/transform.rs b/egui/src/widgets/plot/transform.rs index 65544b768ff..b5eb0cb2ac1 100644 --- a/egui/src/widgets/plot/transform.rs +++ b/egui/src/widgets/plot/transform.rs @@ -1,6 +1,6 @@ use std::ops::RangeInclusive; -use super::items::Value; +use super::PlotPoint; use crate::*; /// 2D bounding box of f64 precision. @@ -52,15 +52,16 @@ impl PlotBounds { self.max[1] - self.min[1] } - pub fn center(&self) -> Value { - Value { - x: (self.min[0] + self.max[0]) / 2.0, - y: (self.min[1] + self.max[1]) / 2.0, - } + pub fn center(&self) -> PlotPoint { + [ + (self.min[0] + self.max[0]) / 2.0, + (self.min[1] + self.max[1]) / 2.0, + ] + .into() } /// Expand to include the given (x,y) value - pub(crate) fn extend_with(&mut self, value: &Value) { + pub(crate) fn extend_with(&mut self, value: &PlotPoint) { self.extend_with_x(value.x); self.extend_with_y(value.y); } @@ -236,7 +237,7 @@ impl ScreenTransform { } } - pub fn position_from_value(&self, value: &Value) -> Pos2 { + pub fn position_from_point(&self, value: &PlotPoint) -> Pos2 { let x = remap( value.x, self.bounds.min[0]..=self.bounds.max[0], @@ -250,7 +251,7 @@ impl ScreenTransform { pos2(x as f32, y as f32) } - pub fn value_from_position(&self, pos: Pos2) -> Value { + pub fn value_from_position(&self, pos: Pos2) -> PlotPoint { let x = remap( pos.x as f64, (self.frame.left() as f64)..=(self.frame.right() as f64), @@ -261,16 +262,16 @@ impl ScreenTransform { (self.frame.bottom() as f64)..=(self.frame.top() as f64), // negated y axis! self.bounds.min[1]..=self.bounds.max[1], ); - Value::new(x, y) + PlotPoint::new(x, y) } /// Transform a rectangle of plot values to a screen-coordinate rectangle. /// /// This typically means that the rect is mirrored vertically (top becomes bottom and vice versa), /// since the plot's coordinate system has +Y up, while egui has +Y down. - pub fn rect_from_values(&self, value1: &Value, value2: &Value) -> Rect { - let pos1 = self.position_from_value(value1); - let pos2 = self.position_from_value(value2); + pub fn rect_from_values(&self, value1: &PlotPoint, value2: &PlotPoint) -> Rect { + let pos1 = self.position_from_point(value1); + let pos2 = self.position_from_point(value2); let mut rect = Rect::NOTHING; rect.extend_with(pos1); diff --git a/egui_demo_app/Cargo.toml b/egui_demo_app/Cargo.toml index a8955046820..48f2ba17df8 100644 --- a/egui_demo_app/Cargo.toml +++ b/egui_demo_app/Cargo.toml @@ -45,7 +45,7 @@ egui_demo_lib = { version = "0.18.0", path = "../egui_demo_lib", features = ["ch # Optional dependencies: -bytemuck = { version = "1.9.1", optional = true } +bytemuck = { version = "1.7.1", optional = true } egui_extras = { version = "0.18.0", optional = true, path = "../egui_extras" } wgpu = { version = "0.13", optional = true, features = ["webgl"] } diff --git a/egui_demo_lib/src/demo/context_menu.rs b/egui_demo_lib/src/demo/context_menu.rs index 65b532a21db..8d38178fd30 100644 --- a/egui_demo_lib/src/demo/context_menu.rs +++ b/egui_demo_lib/src/demo/context_menu.rs @@ -116,17 +116,21 @@ impl super::View for ContextMenus { impl ContextMenus { fn example_plot(&self, ui: &mut egui::Ui) -> egui::Response { - use egui::plot::{Line, Value, Values}; + use egui::plot::Line; let n = 128; - 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); - match self.plot { - Plot::Sin => Value::new(x, x.sin()), - Plot::Bell => Value::new(x, 10.0 * gaussian(x)), - Plot::Sigmoid => Value::new(x, sigmoid(x)), - } - }))); + let line = Line::new( + (0..=n) + .map(|i| { + use std::f64::consts::TAU; + let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU); + match self.plot { + Plot::Sin => [x, x.sin()], + Plot::Bell => [x, 10.0 * gaussian(x)], + Plot::Sigmoid => [x, sigmoid(x)], + } + }) + .collect::>(), + ); egui::plot::Plot::new("example_plot") .show_axes(self.show_axes) .allow_drag(self.allow_drag) diff --git a/egui_demo_lib/src/demo/plot_demo.rs b/egui_demo_lib/src/demo/plot_demo.rs index d23d6867045..ef9f170fa07 100644 --- a/egui_demo_lib/src/demo/plot_demo.rs +++ b/egui_demo_lib/src/demo/plot_demo.rs @@ -5,8 +5,8 @@ use egui::plot::{GridInput, GridMark}; use egui::*; use plot::{ Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter, Corner, HLine, - Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, Points, Polygon, Text, VLine, Value, - Values, + Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, PlotPoint, PlotPoints, Points, Polygon, + Text, VLine, }; // ---------------------------------------------------------------------------- @@ -106,25 +106,27 @@ impl LineDemo { }); } - fn circle(&self) -> Line { + fn circle(&self) -> Line<'static> { let n = 512; - let circle = (0..=n).map(|i| { - let t = remap(i as f64, 0.0..=(n as f64), 0.0..=TAU); - let r = self.circle_radius; - Value::new( - r * t.cos() + self.circle_center.x as f64, - r * t.sin() + self.circle_center.y as f64, - ) - }); - Line::new(Values::from_values_iter(circle)) + let circle_points: Vec<_> = (0..=n) + .map(|i| { + let t = remap(i as f64, 0.0..=(n as f64), 0.0..=TAU); + let r = self.circle_radius; + [ + r * t.cos() + self.circle_center.x as f64, + r * t.sin() + self.circle_center.y as f64, + ] + }) + .collect(); + Line::new(circle_points) .color(Color32::from_rgb(100, 200, 100)) .style(self.line_style) .name("circle") } - fn sin(&self) -> Line { + fn sin(&self) -> Line<'static> { let time = self.time; - Line::new(Values::from_explicit_callback( + Line::new(PlotPoints::from_explicit_callback( move |x| 0.5 * (2.0 * x).sin() * time.sin(), .., 512, @@ -134,9 +136,9 @@ impl LineDemo { .name("wave") } - fn thingy(&self) -> Line { + fn thingy(&self) -> Line<'static> { let time = self.time; - Line::new(Values::from_parametric_callback( + Line::new(PlotPoints::from_parametric_callback( move |t| ((2.0 * t + time).sin(), (3.0 * t).sin()), 0.0..=TAU, 256, @@ -195,19 +197,19 @@ impl Default for MarkerDemo { } impl MarkerDemo { - fn markers(&self) -> Vec { + fn markers(&self) -> Vec> { MarkerShape::all() .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), - ])) + let y_offset = i as f64 * 0.5 + 1.0; + let mut points = Points::new(vec![ + [1.0, 0.0 + y_offset], + [2.0, 0.5 + y_offset], + [3.0, 0.0 + y_offset], + [4.0, 0.5 + y_offset], + [5.0, 0.0 + y_offset], + [6.0, 0.5 + y_offset], + ]) .name(format!("{:?}", marker)) .filled(self.fill_markers) .radius(self.marker_radius) @@ -258,14 +260,26 @@ struct LegendDemo { } impl LegendDemo { - fn line_with_slope(slope: f64) -> Line { - Line::new(Values::from_explicit_callback(move |x| slope * x, .., 100)) + fn line_with_slope(slope: f64) -> Line<'static> { + Line::new(PlotPoints::from_explicit_callback( + move |x| slope * x, + .., + 100, + )) } - fn sin() -> Line { - Line::new(Values::from_explicit_callback(move |x| x.sin(), .., 100)) + fn sin() -> Line<'static> { + Line::new(PlotPoints::from_explicit_callback( + move |x| x.sin(), + .., + 100, + )) } - fn cos() -> Line { - Line::new(Values::from_explicit_callback(move |x| x.cos(), .., 100)) + fn cos() -> Line<'static> { + Line::new(PlotPoints::from_explicit_callback( + move |x| x.cos(), + .., + 100, + )) } fn ui(&mut self, ui: &mut Ui) -> Response { @@ -322,12 +336,12 @@ impl CustomAxisDemo { const MINS_PER_DAY: f64 = 24.0 * 60.0; const MINS_PER_H: f64 = 60.0; - fn logistic_fn() -> Line { + fn logistic_fn() -> Line<'static> { fn days(min: f64) -> f64 { CustomAxisDemo::MINS_PER_DAY * min } - let values = Values::from_explicit_callback( + let values = PlotPoints::from_explicit_callback( move |x| 1.0 / (1.0 + (-2.5 * (x / CustomAxisDemo::MINS_PER_DAY - 2.0)).exp()), days(0.0)..days(5.0), 100, @@ -410,7 +424,7 @@ impl CustomAxisDemo { } }; - let label_fmt = |_s: &str, val: &Value| { + let label_fmt = |_s: &str, val: &PlotPoint| { format!( "Day {d}, {h}:{m:02}\n{p:.2}%", d = get_day(val.x), @@ -457,14 +471,26 @@ impl Default for LinkedAxisDemo { } impl LinkedAxisDemo { - fn line_with_slope(slope: f64) -> Line { - Line::new(Values::from_explicit_callback(move |x| slope * x, .., 100)) + fn line_with_slope(slope: f64) -> Line<'static> { + Line::new(PlotPoints::from_explicit_callback( + move |x| slope * x, + .., + 100, + )) } - fn sin() -> Line { - Line::new(Values::from_explicit_callback(move |x| x.sin(), .., 100)) + fn sin() -> Line<'static> { + Line::new(PlotPoints::from_explicit_callback( + move |x| x.sin(), + .., + 100, + )) } - fn cos() -> Line { - Line::new(Values::from_explicit_callback(move |x| x.cos(), .., 100)) + fn cos() -> Line<'static> { + Line::new(PlotPoints::from_explicit_callback( + move |x| x.cos(), + .., + 100, + )) } fn configure_plot(plot_ui: &mut plot::PlotUi) { @@ -519,28 +545,26 @@ impl ItemsDemo { let n = 100; let mut sin_values: Vec<_> = (0..=n) .map(|i| remap(i as f64, 0.0..=n as f64, -TAU..=TAU)) - .map(|i| Value::new(i, i.sin())) + .map(|i| [i, i.sin()]) .collect(); - let line = Line::new(Values::from_values(sin_values.split_off(n / 2))).fill(-1.5); - let polygon = Polygon::new(Values::from_parametric_callback( + let line = Line::new(sin_values.split_off(n / 2)).fill(-1.5); + let polygon = Polygon::new(PlotPoints::from_parametric_callback( |t| (4.0 * t.sin() + 2.0 * t.cos(), 4.0 * t.cos() + 2.0 * t.sin()), 0.0..TAU, 100, )); - let points = Points::new(Values::from_values(sin_values)) - .stems(-1.5) - .radius(1.0); + let points = Points::new(sin_values).stems(-1.5).radius(1.0); let arrows = { let pos_radius = 8.0; let tip_radius = 7.0; - let arrow_origins = Values::from_parametric_callback( + let arrow_origins = PlotPoints::from_parametric_callback( |t| (pos_radius * t.sin(), pos_radius * t.cos()), 0.0..TAU, 36, ); - let arrow_tips = Values::from_parametric_callback( + let arrow_tips = PlotPoints::from_parametric_callback( |t| (tip_radius * t.sin(), tip_radius * t.cos()), 0.0..TAU, 36, @@ -557,7 +581,7 @@ impl ItemsDemo { }); let image = PlotImage::new( texture, - Value::new(0.0, 10.0), + PlotPoint::new(0.0, 10.0), 5.0 * vec2(texture.aspect_ratio(), 1.0), ); @@ -574,10 +598,10 @@ impl ItemsDemo { 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.text(Text::new(PlotPoint::new(-3.0, -3.0), "wow").name("Text")); + plot_ui.text(Text::new(PlotPoint::new(-2.0, 2.5), "so graph").name("Text")); + plot_ui.text(Text::new(PlotPoint::new(3.0, 3.0), "much color").name("Text")); + plot_ui.text(Text::new(PlotPoint::new(2.5, -2.0), "such plot").name("Text")); plot_ui.image(image.name("Image")); plot_ui.arrows(arrows.name("Arrows")); }) @@ -600,7 +624,7 @@ impl InteractionDemo { inner: (screen_pos, pointer_coordinate, pointer_coordinate_drag_delta, bounds, hovered), } = plot.show(ui, |plot_ui| { ( - plot_ui.screen_from_plot(Value::new(0.0, 0.0)), + plot_ui.screen_from_plot(PlotPoint::new(0.0, 0.0)), plot_ui.pointer_coordinate(), plot_ui.pointer_coordinate_drag_delta(), plot_ui.plot_bounds(), diff --git a/egui_demo_lib/src/demo/widget_gallery.rs b/egui_demo_lib/src/demo/widget_gallery.rs index f1e9d5fb843..267dfa0a963 100644 --- a/egui_demo_lib/src/demo/widget_gallery.rs +++ b/egui_demo_lib/src/demo/widget_gallery.rs @@ -260,13 +260,16 @@ impl WidgetGallery { } fn example_plot(ui: &mut egui::Ui) -> egui::Response { - use egui::plot::{Line, Value, Values}; + use egui::plot::Line; let n = 128; - 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); - Value::new(x, x.sin()) - }))); + let line_points: Vec<_> = (0..=n) + .map(|i| { + use std::f64::consts::TAU; + let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU); + [x, x.sin()] + }) + .collect(); + let line = Line::new(line_points); egui::plot::Plot::new("example_plot") .height(32.0) .data_aspect(1.0) From b9453f690beaccbe332bee93b97d01bfdb39fcb5 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 10 Jul 2022 14:22:46 +0200 Subject: [PATCH 02/12] changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 038bbc70153..ba5475a2b0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui-w ### Changed * `PaintCallback` shapes now require the whole callback to be put in an `Arc` with the value being a backend-specific callback type. ([#1684](https://github.com/emilk/egui/pull/1684)) * Replaced `needs_repaint` in `FullOutput` with `repaint_after`. Used to force repaint after the set duration in reactive mode.([#1694](https://github.com/emilk/egui/pull/1694)). +* Improved ergonomics of adding plot items. All plot items that take a series of 2D coordinates can now be created directly from `Vec<[f64; 2]>`. The `Value` and `Values` types were removed in favor of `PlotPoint` and `PlotPoints` respectively. ### Fixed 🐛 * Fixed `ImageButton`'s changing background padding on hover ([#1595](https://github.com/emilk/egui/pull/1595)). From 18fae35fefd809a987709b13d974dfebe0f0608d Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 10 Jul 2022 14:37:00 +0200 Subject: [PATCH 03/12] fix CI --- egui/Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/egui/Cargo.toml b/egui/Cargo.toml index 6037a39bc6d..9d2d67d9881 100644 --- a/egui/Cargo.toml +++ b/egui/Cargo.toml @@ -57,7 +57,7 @@ serde = ["dep:serde", "epaint/serde"] [dependencies] epaint = { version = "0.18.1", path = "../epaint", default-features = false } -bytemuck = { version = "1.7.0", features = ["derive"] } +bytemuck = { version = "1.7.0", features = ["derive", "extern_crate_alloc"] } ahash = "0.7" nohash-hasher = "0.2" @@ -70,4 +70,6 @@ ron = { version = "0.7", optional = true } serde = { version = "1", optional = true, features = ["derive", "rc"] } # egui doesn't log much, but when it does, it uses [`tracing`](https://docs.rs/tracing). -tracing = { version = "0.1", optional = true, default-features = false, features = ["std"] } +tracing = { version = "0.1", optional = true, default-features = false, features = [ + "std", +] } From 6a72faefc15977f32a03c1bf05ac49e34dd02341 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 21 Jul 2022 21:16:09 +0200 Subject: [PATCH 04/12] Update egui/src/widgets/plot/items/values.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/plot/items/values.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/items/values.rs b/egui/src/widgets/plot/items/values.rs index 0aff7e7b8bf..58ed0d4a24c 100644 --- a/egui/src/widgets/plot/items/values.rs +++ b/egui/src/widgets/plot/items/values.rs @@ -18,8 +18,9 @@ pub struct PlotPoint { } impl From<[f64; 2]> for PlotPoint { + #[inline] fn from(point: [f64; 2]) -> Self { - bytemuck::cast(point) + Self { x: point[0], y: point[1] } } } From a06f06b7f84c10eb6be52b2e5cd65b64548ceaaa Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 21 Jul 2022 21:16:18 +0200 Subject: [PATCH 05/12] Update egui/src/widgets/plot/items/values.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/plot/items/values.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/egui/src/widgets/plot/items/values.rs b/egui/src/widgets/plot/items/values.rs index 58ed0d4a24c..eb5087993ce 100644 --- a/egui/src/widgets/plot/items/values.rs +++ b/egui/src/widgets/plot/items/values.rs @@ -148,6 +148,9 @@ impl Default for Orientation { // ---------------------------------------------------------------------------- +/// Represents many [`PlotPoint`]s. +/// +/// These can be a borrowed slice, an owned `Vec`, or generated with a function. pub enum PlotPoints<'v> { Borrowed(&'v [PlotPoint]), Owned(Vec), From 71e0cc3e8bcb43d1945b90a489c2449be674d7fe Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 21 Jul 2022 21:43:49 +0200 Subject: [PATCH 06/12] derive 'FromIterator' --- egui/src/widgets/plot/items/values.rs | 39 +++++++++++++++--------- egui/src/widgets/plot/mod.rs | 4 +-- egui_demo_lib/src/demo/context_menu.rs | 4 +-- egui_demo_lib/src/demo/plot_demo.rs | 2 +- egui_demo_lib/src/demo/widget_gallery.rs | 4 +-- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/egui/src/widgets/plot/items/values.rs b/egui/src/widgets/plot/items/values.rs index eb5087993ce..e0a6beccfa6 100644 --- a/egui/src/widgets/plot/items/values.rs +++ b/egui/src/widgets/plot/items/values.rs @@ -20,7 +20,10 @@ pub struct PlotPoint { impl From<[f64; 2]> for PlotPoint { #[inline] fn from(point: [f64; 2]) -> Self { - Self { x: point[0], y: point[1] } + Self { + x: point[0], + y: point[1], + } } } @@ -157,6 +160,12 @@ pub enum PlotPoints<'v> { Generator(ExplicitGenerator), } +impl Default for PlotPoints<'_> { + fn default() -> Self { + Self::Owned(Vec::new()) + } +} + impl From<[f64; 2]> for PlotPoints<'_> { fn from(coordinate: [f64; 2]) -> Self { Self::new(vec![coordinate]) @@ -175,6 +184,13 @@ impl<'v> From<&'v [[f64; 2]]> for PlotPoints<'v> { } } +impl<'v> FromIterator<[f64; 2]> for PlotPoints<'v> { + fn from_iter>(iter: T) -> Self { + let values: Vec<_> = iter.into_iter().collect(); + Self::from(values) + } +} + impl<'v> PlotPoints<'v> { pub fn new(points: Vec<[f64; 2]>) -> Self { Self::Owned(bytemuck::cast_vec(points)) @@ -239,32 +255,28 @@ impl<'v> PlotPoints<'v> { } else { (end - start) / points as f64 }; - let values = (0..points) + (0..points) .map(|i| { let t = start + i as f64 * increment; let (x, y) = function(t); - [x, y].into() + [x, y] }) - .collect(); - Self::Owned(values) + .collect() } /// From a series of y-values. /// The x-values will be the indices of these values pub fn from_ys_f32(ys: &[f32]) -> Self { - let points: Vec<[f64; 2]> = ys - .iter() + ys.iter() .enumerate() .map(|(i, &y)| [i as f64, y as f64]) - .collect(); - Self::new(points) + .collect() } /// From a series of y-values. /// The x-values will be the indices of these values pub fn from_ys_f64(ys: &[f64]) -> Self { - let points: Vec<[f64; 2]> = ys.iter().enumerate().map(|(i, &y)| [i as f64, y]).collect(); - Self::new(points) + ys.iter().enumerate().map(|(i, &y)| [i as f64, y]).collect() } /// Returns true if there are no data points available and there is no function to generate any. @@ -280,7 +292,7 @@ impl<'v> PlotPoints<'v> { /// given range. pub(super) fn generate_points(&mut self, x_range: RangeInclusive) { if let Self::Generator(generator) = self { - let points = Self::range_intersection(&x_range, &generator.x_range) + *self = Self::range_intersection(&x_range, &generator.x_range) .map(|intersection| { let increment = (intersection.end() - intersection.start()) / (generator.points - 1) as f64; @@ -288,12 +300,11 @@ impl<'v> PlotPoints<'v> { .map(|i| { let x = intersection.start() + i as f64 * increment; let y = (generator.function)(x); - [x, y].into() + [x, y] }) .collect() }) .unwrap_or_default(); - *self = Self::Owned(points); } } diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index c1b80faa617..84b8603d72b 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -178,8 +178,8 @@ impl LinkedAxisGroup { /// /// ``` /// # egui::__run_test_ui(|ui| { -/// use egui::plot::{Line, Plot}; -/// let sin: Vec<_> = (0..1000).map(|i| { +/// use egui::plot::{Line, Plot, PlotPoints}; +/// let sin: PlotPoints = (0..1000).map(|i| { /// let x = i as f64 * 0.01; /// [x, x.sin()] /// }).collect(); diff --git a/egui_demo_lib/src/demo/context_menu.rs b/egui_demo_lib/src/demo/context_menu.rs index 8d38178fd30..bd951234b83 100644 --- a/egui_demo_lib/src/demo/context_menu.rs +++ b/egui_demo_lib/src/demo/context_menu.rs @@ -116,7 +116,7 @@ impl super::View for ContextMenus { impl ContextMenus { fn example_plot(&self, ui: &mut egui::Ui) -> egui::Response { - use egui::plot::Line; + use egui::plot::{Line, PlotPoints}; let n = 128; let line = Line::new( (0..=n) @@ -129,7 +129,7 @@ impl ContextMenus { Plot::Sigmoid => [x, sigmoid(x)], } }) - .collect::>(), + .collect::(), ); egui::plot::Plot::new("example_plot") .show_axes(self.show_axes) diff --git a/egui_demo_lib/src/demo/plot_demo.rs b/egui_demo_lib/src/demo/plot_demo.rs index 21d9059a6bb..2b4b9e4ab72 100644 --- a/egui_demo_lib/src/demo/plot_demo.rs +++ b/egui_demo_lib/src/demo/plot_demo.rs @@ -108,7 +108,7 @@ impl LineDemo { fn circle(&self) -> Line<'static> { let n = 512; - let circle_points: Vec<_> = (0..=n) + let circle_points: PlotPoints = (0..=n) .map(|i| { let t = remap(i as f64, 0.0..=(n as f64), 0.0..=TAU); let r = self.circle_radius; diff --git a/egui_demo_lib/src/demo/widget_gallery.rs b/egui_demo_lib/src/demo/widget_gallery.rs index 267dfa0a963..419b95c5881 100644 --- a/egui_demo_lib/src/demo/widget_gallery.rs +++ b/egui_demo_lib/src/demo/widget_gallery.rs @@ -260,9 +260,9 @@ impl WidgetGallery { } fn example_plot(ui: &mut egui::Ui) -> egui::Response { - use egui::plot::Line; + use egui::plot::{Line, PlotPoints}; let n = 128; - let line_points: Vec<_> = (0..=n) + let line_points: PlotPoints = (0..=n) .map(|i| { use std::f64::consts::TAU; let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU); From f42624b44eba8fd60a2c3e829971c553ca6f72b9 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Fri, 22 Jul 2022 11:02:22 +0200 Subject: [PATCH 07/12] remove `bytemuck` dependency again and remove borrowing plot points for now --- Cargo.lock | 1 - egui/Cargo.toml | 6 +--- egui/src/widgets/plot/items/mod.rs | 42 +++++++++++++------------- egui/src/widgets/plot/items/values.rs | 43 +++++++-------------------- egui/src/widgets/plot/mod.rs | 10 +++---- egui_demo_lib/src/demo/plot_demo.rs | 22 +++++++------- 6 files changed, 49 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0374b4fe39d..2230f30147b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1199,7 +1199,6 @@ name = "egui" version = "0.18.1" dependencies = [ "ahash 0.7.6", - "bytemuck", "document-features", "epaint", "nohash-hasher", diff --git a/egui/Cargo.toml b/egui/Cargo.toml index 9d2d67d9881..eea1e829252 100644 --- a/egui/Cargo.toml +++ b/egui/Cargo.toml @@ -57,8 +57,6 @@ serde = ["dep:serde", "epaint/serde"] [dependencies] epaint = { version = "0.18.1", path = "../epaint", default-features = false } -bytemuck = { version = "1.7.0", features = ["derive", "extern_crate_alloc"] } - ahash = "0.7" nohash-hasher = "0.2" @@ -70,6 +68,4 @@ ron = { version = "0.7", optional = true } serde = { version = "1", optional = true, features = ["derive", "rc"] } # egui doesn't log much, but when it does, it uses [`tracing`](https://docs.rs/tracing). -tracing = { version = "0.1", optional = true, default-features = false, features = [ - "std", -] } +tracing = { version = "0.1", optional = true, default-features = false, features = ["std"] } diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index 865fd0f5710..ede5226997c 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -316,8 +316,8 @@ impl PlotItem for VLine { } /// A series of values forming a path. -pub struct Line<'v> { - pub(super) series: PlotPoints<'v>, +pub struct Line { + pub(super) series: PlotPoints, pub(super) stroke: Stroke, pub(super) name: String, pub(super) highlight: bool, @@ -325,8 +325,8 @@ pub struct Line<'v> { pub(super) style: LineStyle, } -impl<'v> Line<'v> { - pub fn new(series: impl Into>) -> Self { +impl Line { + pub fn new(series: impl Into) -> Self { Self { series: series.into(), stroke: Stroke::new(1.0, Color32::TRANSPARENT), @@ -393,7 +393,7 @@ fn y_intersection(p1: &Pos2, p2: &Pos2, y: f32) -> Option { .then(|| ((y * (p1.x - p2.x)) - (p1.x * p2.y - p1.y * p2.x)) / (p1.y - p2.y)) } -impl PlotItem for Line<'_> { +impl PlotItem for Line { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let Self { series, @@ -483,8 +483,8 @@ impl PlotItem for Line<'_> { } /// A convex polygon. -pub struct Polygon<'v> { - pub(super) series: PlotPoints<'v>, +pub struct Polygon { + pub(super) series: PlotPoints, pub(super) stroke: Stroke, pub(super) name: String, pub(super) highlight: bool, @@ -492,8 +492,8 @@ pub struct Polygon<'v> { pub(super) style: LineStyle, } -impl<'v> Polygon<'v> { - pub fn new(series: impl Into>) -> Self { +impl Polygon { + pub fn new(series: impl Into) -> Self { Self { series: series.into(), stroke: Stroke::new(1.0, Color32::TRANSPARENT), @@ -554,7 +554,7 @@ impl<'v> Polygon<'v> { } } -impl PlotItem for Polygon<'_> { +impl PlotItem for Polygon { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let Self { series, @@ -729,8 +729,8 @@ impl PlotItem for Text { } /// A set of points. -pub struct Points<'v> { - pub(super) series: PlotPoints<'v>, +pub struct Points { + pub(super) series: PlotPoints, pub(super) shape: MarkerShape, /// Color of the marker. `Color32::TRANSPARENT` means that it will be picked automatically. pub(super) color: Color32, @@ -743,8 +743,8 @@ pub struct Points<'v> { pub(super) stems: Option, } -impl<'v> Points<'v> { - pub fn new(series: impl Into>) -> Self { +impl Points { + pub fn new(series: impl Into) -> Self { Self { series: series.into(), shape: MarkerShape::Circle, @@ -806,7 +806,7 @@ impl<'v> Points<'v> { } } -impl PlotItem for Points<'_> { +impl PlotItem for Points { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let sqrt_3 = 3_f32.sqrt(); let frac_sqrt_3_2 = 3_f32.sqrt() / 2.0; @@ -965,16 +965,16 @@ impl PlotItem for Points<'_> { } /// A set of arrows. -pub struct Arrows<'v> { - pub(super) origins: PlotPoints<'v>, - pub(super) tips: PlotPoints<'v>, +pub struct Arrows { + pub(super) origins: PlotPoints, + pub(super) tips: PlotPoints, pub(super) color: Color32, pub(super) name: String, pub(super) highlight: bool, } -impl<'v> Arrows<'v> { - pub fn new(origins: impl Into>, tips: impl Into>) -> Self { +impl Arrows { + pub fn new(origins: impl Into, tips: impl Into) -> Self { Self { origins: origins.into(), tips: tips.into(), @@ -1009,7 +1009,7 @@ impl<'v> Arrows<'v> { } } -impl PlotItem for Arrows<'_> { +impl PlotItem for Arrows { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { use crate::emath::*; let Self { diff --git a/egui/src/widgets/plot/items/values.rs b/egui/src/widgets/plot/items/values.rs index e0a6beccfa6..b0ea49ecf78 100644 --- a/egui/src/widgets/plot/items/values.rs +++ b/egui/src/widgets/plot/items/values.rs @@ -7,8 +7,7 @@ use crate::plot::transform::PlotBounds; /// /// Uses f64 for improved accuracy to enable plotting /// large values (e.g. unix time on x axis). -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct PlotPoint { /// This is often something monotonically increasing, such as time, but doesn't have to be. /// Goes from left to right. @@ -153,56 +152,44 @@ impl Default for Orientation { /// Represents many [`PlotPoint`]s. /// -/// These can be a borrowed slice, an owned `Vec`, or generated with a function. -pub enum PlotPoints<'v> { - Borrowed(&'v [PlotPoint]), +/// These can be an owned `Vec` or generated with a function. +pub enum PlotPoints { Owned(Vec), Generator(ExplicitGenerator), + // Borrowed(&[PlotPoint]), // TODO: Lifetimes are tricky in this case. } -impl Default for PlotPoints<'_> { +impl Default for PlotPoints { fn default() -> Self { Self::Owned(Vec::new()) } } -impl From<[f64; 2]> for PlotPoints<'_> { +impl From<[f64; 2]> for PlotPoints { fn from(coordinate: [f64; 2]) -> Self { Self::new(vec![coordinate]) } } -impl From> for PlotPoints<'_> { +impl From> for PlotPoints { fn from(coordinates: Vec<[f64; 2]>) -> Self { Self::new(coordinates) } } -impl<'v> From<&'v [[f64; 2]]> for PlotPoints<'v> { - fn from(coordinates: &'v [[f64; 2]]) -> Self { - Self::from_slice(coordinates) - } -} - -impl<'v> FromIterator<[f64; 2]> for PlotPoints<'v> { +impl FromIterator<[f64; 2]> for PlotPoints { fn from_iter>(iter: T) -> Self { - let values: Vec<_> = iter.into_iter().collect(); - Self::from(values) + Self::Owned(iter.into_iter().map(|point| point.into()).collect()) } } -impl<'v> PlotPoints<'v> { +impl PlotPoints { pub fn new(points: Vec<[f64; 2]>) -> Self { - Self::Owned(bytemuck::cast_vec(points)) - } - - pub fn from_slice(points: &'v [[f64; 2]]) -> Self { - Self::Borrowed(bytemuck::cast_slice(points)) + Self::from_iter(points) } pub fn points(&self) -> &[PlotPoint] { match self { - PlotPoints::Borrowed(points) => points, PlotPoints::Owned(points) => points.as_slice(), PlotPoints::Generator(_) => &[], } @@ -282,7 +269,6 @@ impl<'v> PlotPoints<'v> { /// Returns true if there are no data points available and there is no function to generate any. pub(crate) fn is_empty(&self) -> bool { match self { - PlotPoints::Borrowed(points) => points.is_empty(), PlotPoints::Owned(points) => points.is_empty(), PlotPoints::Generator(_) => false, } @@ -320,13 +306,6 @@ impl<'v> PlotPoints<'v> { pub(super) fn get_bounds(&self) -> PlotBounds { match self { - PlotPoints::Borrowed(points) => { - let mut bounds = PlotBounds::NOTHING; - for point in points.iter() { - bounds.extend_with(point); - } - bounds - } PlotPoints::Owned(points) => { let mut bounds = PlotBounds::NOTHING; for point in points { diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 84b8603d72b..eeaf72f9780 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -861,7 +861,7 @@ pub struct PlotUi { ctx: Context, } -impl<'p> PlotUi { +impl PlotUi { fn auto_color(&mut self) -> Color32 { let i = self.next_auto_color_idx; self.next_auto_color_idx += 1; @@ -917,7 +917,7 @@ impl<'p> PlotUi { } /// Add a data line. - pub fn line(&mut self, mut line: Line<'static>) { + pub fn line(&mut self, mut line: Line) { if line.series.is_empty() { return; }; @@ -930,7 +930,7 @@ impl<'p> PlotUi { } /// Add a polygon. The polygon has to be convex. - pub fn polygon(&mut self, mut polygon: Polygon<'static>) { + pub fn polygon(&mut self, mut polygon: Polygon) { if polygon.series.is_empty() { return; }; @@ -952,7 +952,7 @@ impl<'p> PlotUi { } /// Add data points. - pub fn points(&mut self, mut points: Points<'static>) { + pub fn points(&mut self, mut points: Points) { if points.series.is_empty() { return; }; @@ -965,7 +965,7 @@ impl<'p> PlotUi { } /// Add arrows. - pub fn arrows(&mut self, mut arrows: Arrows<'static>) { + pub fn arrows(&mut self, mut arrows: Arrows) { if arrows.origins.is_empty() || arrows.tips.is_empty() { return; }; diff --git a/egui_demo_lib/src/demo/plot_demo.rs b/egui_demo_lib/src/demo/plot_demo.rs index 2b4b9e4ab72..a751c2a864c 100644 --- a/egui_demo_lib/src/demo/plot_demo.rs +++ b/egui_demo_lib/src/demo/plot_demo.rs @@ -106,7 +106,7 @@ impl LineDemo { }); } - fn circle(&self) -> Line<'static> { + fn circle(&self) -> Line { let n = 512; let circle_points: PlotPoints = (0..=n) .map(|i| { @@ -124,7 +124,7 @@ impl LineDemo { .name("circle") } - fn sin(&self) -> Line<'static> { + fn sin(&self) -> Line { let time = self.time; Line::new(PlotPoints::from_explicit_callback( move |x| 0.5 * (2.0 * x).sin() * time.sin(), @@ -136,7 +136,7 @@ impl LineDemo { .name("wave") } - fn thingy(&self) -> Line<'static> { + fn thingy(&self) -> Line { let time = self.time; Line::new(PlotPoints::from_parametric_callback( move |t| ((2.0 * t + time).sin(), (3.0 * t).sin()), @@ -197,7 +197,7 @@ impl Default for MarkerDemo { } impl MarkerDemo { - fn markers(&self) -> Vec> { + fn markers(&self) -> Vec { MarkerShape::all() .enumerate() .map(|(i, marker)| { @@ -260,21 +260,21 @@ struct LegendDemo { } impl LegendDemo { - fn line_with_slope(slope: f64) -> Line<'static> { + fn line_with_slope(slope: f64) -> Line { Line::new(PlotPoints::from_explicit_callback( move |x| slope * x, .., 100, )) } - fn sin() -> Line<'static> { + fn sin() -> Line { Line::new(PlotPoints::from_explicit_callback( move |x| x.sin(), .., 100, )) } - fn cos() -> Line<'static> { + fn cos() -> Line { Line::new(PlotPoints::from_explicit_callback( move |x| x.cos(), .., @@ -336,7 +336,7 @@ impl CustomAxisDemo { const MINS_PER_DAY: f64 = 24.0 * 60.0; const MINS_PER_H: f64 = 60.0; - fn logistic_fn() -> Line<'static> { + fn logistic_fn() -> Line { fn days(min: f64) -> f64 { CustomAxisDemo::MINS_PER_DAY * min } @@ -471,21 +471,21 @@ impl Default for LinkedAxisDemo { } impl LinkedAxisDemo { - fn line_with_slope(slope: f64) -> Line<'static> { + fn line_with_slope(slope: f64) -> Line { Line::new(PlotPoints::from_explicit_callback( move |x| slope * x, .., 100, )) } - fn sin() -> Line<'static> { + fn sin() -> Line { Line::new(PlotPoints::from_explicit_callback( move |x| x.sin(), .., 100, )) } - fn cos() -> Line<'static> { + fn cos() -> Line { Line::new(PlotPoints::from_explicit_callback( move |x| x.cos(), .., From 7d21bd32522ae4a79cfab094fd2a4d7c69f8c9b0 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Fri, 22 Jul 2022 14:59:16 +0200 Subject: [PATCH 08/12] update doctest --- egui/src/widgets/plot/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index eeaf72f9780..911d846dacd 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -359,8 +359,8 @@ impl Plot { /// /// ``` /// # egui::__run_test_ui(|ui| { - /// use egui::plot::{Line, Plot}; - /// let sin: Vec<_> = (0..1000).map(|i| { + /// use egui::plot::{Line, Plot, PlotPoints}; + /// let sin: PlotPoints = (0..1000).map(|i| { /// let x = i as f64 * 0.01; /// [x, x.sin()] /// }).collect(); From aa416e0bb0bab8b418165c8781d549d043cf5c9d Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Fri, 22 Jul 2022 15:01:10 +0200 Subject: [PATCH 09/12] update documentation --- egui/src/widgets/plot/items/values.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/items/values.rs b/egui/src/widgets/plot/items/values.rs index b0ea49ecf78..951ca4076db 100644 --- a/egui/src/widgets/plot/items/values.rs +++ b/egui/src/widgets/plot/items/values.rs @@ -3,7 +3,7 @@ use std::ops::{Bound, RangeBounds, RangeInclusive}; use crate::plot::transform::PlotBounds; -/// A value in the value-space of the plot. +/// A point coordinate in the plot. /// /// Uses f64 for improved accuracy to enable plotting /// large values (e.g. unix time on x axis). From ca1bf0ceb9dd32070a2d08fe536d8c9cdb6af493 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sat, 23 Jul 2022 23:56:59 +0200 Subject: [PATCH 10/12] remove unnecessary numeric cast --- egui/src/widgets/plot/items/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index ede5226997c..5dcb96bde60 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -838,7 +838,7 @@ impl PlotItem for Points { } let y_reference = - stems.map(|y| transform.position_from_point(&PlotPoint::new(0.0, y)).y as f32); + stems.map(|y| transform.position_from_point(&PlotPoint::new(0.0, y)).y); series .points() From 9921d7b2d18c40726b98242cd03d8934544bb934 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 24 Jul 2022 00:05:53 +0200 Subject: [PATCH 11/12] cargo fmt --- egui/src/widgets/plot/items/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index 5dcb96bde60..ffb1bd5a7ce 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -837,8 +837,7 @@ impl PlotItem for Points { stem_stroke.width *= 2.0; } - let y_reference = - stems.map(|y| transform.position_from_point(&PlotPoint::new(0.0, y)).y); + let y_reference = stems.map(|y| transform.position_from_point(&PlotPoint::new(0.0, y)).y); series .points() From 2e1ae02a1d235be809be53062263ebf546d4d3af Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 24 Jul 2022 10:00:17 +0200 Subject: [PATCH 12/12] Update egui/src/widgets/plot/items/values.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/plot/items/values.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/egui/src/widgets/plot/items/values.rs b/egui/src/widgets/plot/items/values.rs index 951ca4076db..3cfb845d1ec 100644 --- a/egui/src/widgets/plot/items/values.rs +++ b/egui/src/widgets/plot/items/values.rs @@ -18,11 +18,8 @@ pub struct PlotPoint { impl From<[f64; 2]> for PlotPoint { #[inline] - fn from(point: [f64; 2]) -> Self { - Self { - x: point[0], - y: point[1], - } + fn from([x, y]: [f64; 2]) -> Self { + Self { x, y } } }