From 7a667c286243b9cb72453353569e8260fed2c5af Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sat, 26 Jun 2021 00:12:21 +0200 Subject: [PATCH 1/7] added new line styles --- egui/src/widgets/plot/items.rs | 233 +++++++++++++++++++---- egui/src/widgets/plot/mod.rs | 6 +- egui_demo_lib/src/apps/demo/plot_demo.rs | 28 ++- 3 files changed, 228 insertions(+), 39 deletions(-) diff --git a/egui/src/widgets/plot/items.rs b/egui/src/widgets/plot/items.rs index b5372f57b3a..99aa1871164 100644 --- a/egui/src/widgets/plot/items.rs +++ b/egui/src/widgets/plot/items.rs @@ -34,6 +34,143 @@ impl Value { // ---------------------------------------------------------------------------- +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum LineStyle { + Solid, + Dotted { px: f32 }, + Dashed { px: f32 }, +} + +impl LineStyle { + pub fn dashed_loose() -> Self { + Self::Dashed { px: 10.0 } + } + + pub fn dashed_dense() -> Self { + Self::Dashed { px: 5.0 } + } + + pub fn dotted_loose() -> Self { + Self::Dotted { px: 10.0 } + } + + pub fn dotted_dense() -> Self { + Self::Dotted { px: 5.0 } + } + + fn style_line( + &self, + line: Vec, + mut stroke: Stroke, + highlight: bool, + shapes: &mut Vec, + ) { + match line.len() { + 0 => {} + 1 => { + let mut radius = stroke.width / 2.0; + if highlight { + radius *= 2f32.sqrt(); + } + shapes.push(Shape::circle_filled(line[0], radius, stroke.color)); + } + _ => { + match self { + LineStyle::Solid => { + if highlight { + stroke.width *= 2.0; + } + shapes.push(Shape::line(line, stroke)); + } + LineStyle::Dotted { px } => { + // Take the stroke width for the radius even though it's not "correct", otherwise + // the dots would become too small. + let mut radius = stroke.width; + if highlight { + radius *= 2f32.sqrt(); + } + points_from_line(&line, *px, radius, stroke.color, shapes) + } + LineStyle::Dashed { px } => { + if highlight { + stroke.width *= 2.0; + } + dashes_from_line(&line, *px, stroke, shapes); + } + } + } + } + } +} + +impl ToString for LineStyle { + fn to_string(&self) -> String { + match self { + LineStyle::Solid => "Solid".into(), + LineStyle::Dotted { px } => format!("Dotted{}Px", px), + LineStyle::Dashed { px } => format!("Dashed{}Px", px), + } + } +} + +fn points_from_line( + line: &[Pos2], + spacing: f32, + radius: f32, + color: Color32, + shapes: &mut Vec, +) { + let mut position_on_segment = 0.0; + line.windows(2).for_each(|window| { + let start = window[0]; + let end = window[1]; + let vector = end - start; + let segment_length = vector.length(); + while position_on_segment < segment_length { + let new_point = start + vector * (position_on_segment / segment_length); + shapes.push(Shape::circle_filled(new_point, radius, color)); + position_on_segment += spacing; + } + position_on_segment -= segment_length; + }); +} + +fn dashes_from_line(line: &[Pos2], dash_length: f32, stroke: Stroke, shapes: &mut Vec) { + let gap_length = 0.5 * dash_length; + let mut position_on_segment = 0.0; + let mut drawing_dash = false; + line.windows(2).for_each(|window| { + let start = window[0]; + let end = window[1]; + let vector = end - start; + let segment_length = vector.length(); + while position_on_segment < segment_length { + let new_point = start + vector * (position_on_segment / segment_length); + if drawing_dash { + // This is the end point. + if let Shape::Path { points, .. } = shapes.last_mut().unwrap() { + points.push(new_point); + } + position_on_segment += gap_length; + } else { + // Start a new dash. + shapes.push(Shape::line(vec![new_point], stroke)); + position_on_segment += dash_length; + } + drawing_dash = !drawing_dash; + } + // If the segment ends and the dash is not finished, add the segment's end point. + if drawing_dash { + if let Shape::Path { points, .. } = shapes.last_mut().unwrap() { + points.push(end); + } + } + position_on_segment -= segment_length; + }); +} + +// ---------------------------------------------------------------------------- + /// A horizontal line in a plot, filling the full width #[derive(Clone, Debug, PartialEq)] pub struct HLine { @@ -41,6 +178,7 @@ pub struct HLine { pub(super) stroke: Stroke, pub(super) name: String, pub(super) highlight: bool, + pub(super) style: LineStyle, } impl HLine { @@ -50,9 +188,16 @@ impl HLine { stroke: Stroke::new(1.0, Color32::TRANSPARENT), name: String::default(), highlight: false, + style: LineStyle::Solid, } } + /// Set the line's style. Default is `LineStyle::Solid`. + pub fn style(mut self, style: LineStyle) -> Self { + self.style = style; + self + } + /// Name of this horizontal line. /// /// This name will show up in the plot legend, if legends are turned on. @@ -70,18 +215,16 @@ impl PlotItem for HLine { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let HLine { y, - mut stroke, + stroke, highlight, + style, .. } = self; - if *highlight { - stroke.width *= 2.0; - } - let points = [ + 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)), ]; - shapes.push(Shape::line_segment(points, stroke)); + style.style_line(points, *stroke, *highlight, shapes); } fn initialize(&mut self, _x_range: RangeInclusive) {} @@ -121,6 +264,7 @@ pub struct VLine { pub(super) stroke: Stroke, pub(super) name: String, pub(super) highlight: bool, + pub(super) style: LineStyle, } impl VLine { @@ -130,9 +274,16 @@ impl VLine { stroke: Stroke::new(1.0, Color32::TRANSPARENT), name: String::default(), highlight: false, + style: LineStyle::Solid, } } + /// Set the line's style. Default is `LineStyle::Solid`. + pub fn style(mut self, style: LineStyle) -> Self { + self.style = style; + self + } + /// Name of this vertical line. /// /// This name will show up in the plot legend, if legends are turned on. @@ -150,18 +301,16 @@ impl PlotItem for VLine { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let VLine { x, - mut stroke, + stroke, highlight, + style, .. } = self; - if *highlight { - stroke.width *= 2.0; - } - let points = [ + 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])), ]; - shapes.push(Shape::line_segment(points, stroke)); + style.style_line(points, *stroke, *highlight, shapes) } fn initialize(&mut self, _x_range: RangeInclusive) {} @@ -373,8 +522,8 @@ pub enum MarkerShape { impl MarkerShape { /// Get a vector containing all marker shapes. - pub fn all() -> Vec { - vec![ + pub fn all() -> impl Iterator { + [ Self::Circle, Self::Diamond, Self::Square, @@ -386,6 +535,8 @@ impl MarkerShape { Self::Right, Self::Asterisk, ] + .iter() + .copied() } } @@ -396,6 +547,7 @@ pub struct Line { pub(super) name: String, pub(super) highlight: bool, pub(super) fill: Option, + pub(super) style: LineStyle, } impl Line { @@ -406,6 +558,7 @@ impl Line { name: Default::default(), highlight: false, fill: None, + style: LineStyle::Solid, } } @@ -439,6 +592,12 @@ impl Line { self } + /// Set the line's style. Default is `LineStyle::Solid`. + pub fn style(mut self, style: LineStyle) -> Self { + self.style = style; + self + } + /// Name of this line. /// /// This name will show up in the plot legend, if legends are turned on. @@ -463,19 +622,13 @@ impl PlotItem for Line { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let Self { series, - mut stroke, + stroke, highlight, mut fill, + style, .. } = self; - let mut fill_alpha = DEFAULT_FILL_ALPHA; - - if *highlight { - stroke.width *= 2.0; - fill_alpha = (2.0 * fill_alpha).at_most(1.0); - } - let values_tf: Vec<_> = series .values .iter() @@ -488,6 +641,10 @@ impl PlotItem for Line { fill = None; } if let Some(y_reference) = fill { + let mut fill_alpha = DEFAULT_FILL_ALPHA; + if *highlight { + fill_alpha = (2.0 * fill_alpha).at_most(1.0); + } let y = transform .position_from_value(&Value::new(0.0, y_reference)) .y; @@ -518,13 +675,7 @@ impl PlotItem for Line { mesh.colored_vertex(pos2(last.x, y), fill_color); shapes.push(Shape::Mesh(mesh)); } - - let line_shape = if n_values > 1 { - Shape::line(values_tf, stroke) - } else { - Shape::circle_filled(values_tf[0], stroke.width / 2.0, stroke.color) - }; - shapes.push(line_shape); + style.style_line(values_tf, *stroke, *highlight, shapes); } fn initialize(&mut self, x_range: RangeInclusive) { @@ -563,6 +714,7 @@ pub struct Polygon { pub(super) name: String, pub(super) highlight: bool, pub(super) fill_alpha: f32, + pub(super) style: LineStyle, } impl Polygon { @@ -573,6 +725,7 @@ impl Polygon { name: Default::default(), highlight: false, fill_alpha: DEFAULT_FILL_ALPHA, + style: LineStyle::Solid, } } @@ -607,6 +760,12 @@ impl Polygon { self } + /// Set the outline's style. Default is `LineStyle::Solid`. + pub fn style(mut self, style: LineStyle) -> Self { + self.style = style; + self + } + /// Name of this polygon. /// /// This name will show up in the plot legend, if legends are turned on. @@ -624,18 +783,18 @@ impl PlotItem for Polygon { fn get_shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec) { let Self { series, - mut stroke, + stroke, highlight, mut fill_alpha, + style, .. } = self; if *highlight { - stroke.width *= 2.0; fill_alpha = (2.0 * fill_alpha).at_most(1.0); } - let values_tf: Vec<_> = series + let mut values_tf: Vec<_> = series .values .iter() .map(|v| transform.position_from_value(v)) @@ -643,9 +802,15 @@ impl PlotItem for Polygon { let fill = Rgba::from(stroke.color).to_opaque().multiply(fill_alpha); - let shape = Shape::convex_polygon(values_tf, fill, stroke); - + let shape = Shape::Path { + points: values_tf.clone(), + closed: true, + fill: fill.into(), + stroke: Stroke::none(), + }; shapes.push(shape); + values_tf.push(*values_tf.first().unwrap()); + style.style_line(values_tf, *stroke, *highlight, shapes); } fn initialize(&mut self, x_range: RangeInclusive) { diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index b6278400748..cda710795e5 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -7,8 +7,10 @@ mod transform; use std::collections::HashSet; use items::PlotItem; -pub use items::{Arrows, Line, MarkerShape, PlotImage, Points, Polygon, Text, Value, Values}; -pub use items::{HLine, VLine}; +pub use items::{ + Arrows, HLine, Line, LineStyle, MarkerShape, PlotImage, Points, Polygon, Text, VLine, Value, + Values, +}; use legend::LegendWidget; pub use legend::{Corner, Legend}; use transform::{Bounds, ScreenTransform}; diff --git a/egui_demo_lib/src/apps/demo/plot_demo.rs b/egui_demo_lib/src/apps/demo/plot_demo.rs index 415dbd3a759..adc5e0ac700 100644 --- a/egui_demo_lib/src/apps/demo/plot_demo.rs +++ b/egui_demo_lib/src/apps/demo/plot_demo.rs @@ -1,7 +1,7 @@ use egui::*; use plot::{ - Arrows, Corner, HLine, Legend, Line, MarkerShape, Plot, PlotImage, Points, Polygon, Text, - VLine, Value, Values, + Arrows, Corner, HLine, Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, Points, Polygon, + Text, VLine, Value, Values, }; use std::f64::consts::TAU; @@ -13,6 +13,7 @@ struct LineDemo { circle_center: Pos2, square: bool, proportional: bool, + line_style: LineStyle, } impl Default for LineDemo { @@ -24,6 +25,7 @@ impl Default for LineDemo { circle_center: Pos2::new(0.0, 0.0), square: false, proportional: true, + line_style: LineStyle::Solid, } } } @@ -37,6 +39,7 @@ impl LineDemo { circle_center, square, proportional, + line_style, .. } = self; @@ -73,6 +76,23 @@ impl LineDemo { ui.checkbox(proportional, "Proportional data axes") .on_hover_text("Tick are the same size on both axes."); }); + ui.vertical(|ui| { + ComboBox::from_label("Line style") + .selected_text(line_style.to_string()) + .show_ui(ui, |ui| { + [ + LineStyle::Solid, + LineStyle::dashed_dense(), + LineStyle::dashed_loose(), + LineStyle::dotted_dense(), + LineStyle::dotted_loose(), + ] + .iter() + .for_each(|style| { + ui.selectable_value(line_style, *style, style.to_string()); + }); + }); + }); }); } @@ -88,6 +108,7 @@ impl LineDemo { }); Line::new(Values::from_values_iter(circle)) .color(Color32::from_rgb(100, 200, 100)) + .style(self.line_style) .name("circle") } @@ -99,6 +120,7 @@ impl LineDemo { 512, )) .color(Color32::from_rgb(200, 100, 100)) + .style(self.line_style) .name("wave") } @@ -110,6 +132,7 @@ impl LineDemo { 256, )) .color(Color32::from_rgb(100, 150, 250)) + .style(self.line_style) .name("x = sin(2t), y = sin(3t)") } } @@ -158,7 +181,6 @@ impl Default for MarkerDemo { impl MarkerDemo { fn markers(&self) -> Vec { MarkerShape::all() - .into_iter() .enumerate() .map(|(i, marker)| { let y_offset = i as f32 * 0.5 + 1.0; From 5ec3ae803d7d118dfacd1aff5cb9b932a45b0368 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sat, 26 Jun 2021 00:13:16 +0200 Subject: [PATCH 2/7] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42deab2a655..72571a75224 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [ ## Unreleased - +* Plot: + * [Line styles](https://github.com/emilk/egui/pull/482) ## 0.13.0 - 2021-06-24 - Better panels, plots and new visual style From 173095e5b6b57437a6a3cd7287482367204f9af7 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 27 Jun 2021 11:58:58 +0200 Subject: [PATCH 3/7] fix #524 Add missing functions to `HLine` and `VLine` --- egui/src/widgets/plot/items.rs | 50 +++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/items.rs b/egui/src/widgets/plot/items.rs index 99aa1871164..870cd20628b 100644 --- a/egui/src/widgets/plot/items.rs +++ b/egui/src/widgets/plot/items.rs @@ -192,6 +192,30 @@ impl HLine { } } + /// Highlight this line in the plot by scaling up the line. + pub fn highlight(mut self) -> Self { + self.highlight = true; + self + } + + /// Add a stroke. + pub fn stroke(mut self, stroke: impl Into) -> Self { + self.stroke = stroke.into(); + self + } + + /// Stroke width. A high value means the plot thickens. + pub fn width(mut self, width: impl Into) -> Self { + self.stroke.width = width.into(); + self + } + + /// Stroke color. Default is `Color32::TRANSPARENT` which means a color will be auto-assigned. + pub fn color(mut self, color: impl Into) -> Self { + self.stroke.color = color.into(); + self + } + /// Set the line's style. Default is `LineStyle::Solid`. pub fn style(mut self, style: LineStyle) -> Self { self.style = style; @@ -278,6 +302,30 @@ impl VLine { } } + /// Highlight this line in the plot by scaling up the line. + pub fn highlight(mut self) -> Self { + self.highlight = true; + self + } + + /// Add a stroke. + pub fn stroke(mut self, stroke: impl Into) -> Self { + self.stroke = stroke.into(); + self + } + + /// Stroke width. A high value means the plot thickens. + pub fn width(mut self, width: impl Into) -> Self { + self.stroke.width = width.into(); + self + } + + /// Stroke color. Default is `Color32::TRANSPARENT` which means a color will be auto-assigned. + pub fn color(mut self, color: impl Into) -> Self { + self.stroke.color = color.into(); + self + } + /// Set the line's style. Default is `LineStyle::Solid`. pub fn style(mut self, style: LineStyle) -> Self { self.style = style; @@ -562,7 +610,7 @@ impl Line { } } - /// Highlight this line in the plot by scaling up the line and marker size. + /// Highlight this line in the plot by scaling up the line. pub fn highlight(mut self) -> Self { self.highlight = true; self From 97124c1747d9d1f72d9d320227d6c4639ddc8b42 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sat, 3 Jul 2021 16:05:37 +0200 Subject: [PATCH 4/7] add functions for creating points and dashes from a line --- epaint/src/shape.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/epaint/src/shape.rs b/epaint/src/shape.rs index aa46c4d4df4..4fb9f4ff60a 100644 --- a/epaint/src/shape.rs +++ b/epaint/src/shape.rs @@ -66,7 +66,7 @@ impl Shape { /// A line through many points. /// - /// Use [`Self::line_segment`] instead if your line only connect two points. + /// Use [`Self::line_segment`] instead if your line only connects two points. pub fn line(points: Vec, stroke: impl Into) -> Self { Self::Path { points, @@ -86,6 +86,36 @@ impl Shape { } } + /// Turn a line into equally spaced dots. + pub fn dotted_line( + points: &[Pos2], + color: impl Into, + spacing: f32, + radius: f32, + ) -> Vec { + let mut shapes = Vec::new(); + points_from_line(points, spacing, radius, color.into(), &mut shapes); + shapes + } + + /// Turn a line into dashes. + pub fn dashed_line( + points: &[Pos2], + stroke: impl Into, + dash_length: f32, + dash_gap_ratio: f32, + ) -> Vec { + let mut shapes = Vec::new(); + dashes_from_line( + &points, + stroke.into(), + dash_length, + dash_gap_ratio, + &mut shapes, + ); + shapes + } + /// A convex polygon with a fill and optional stroke. pub fn convex_polygon( points: Vec, @@ -161,6 +191,68 @@ impl Shape { } } +fn points_from_line( + line: &[Pos2], + spacing: f32, + radius: f32, + color: Color32, + shapes: &mut Vec, +) { + let mut position_on_segment = 0.0; + line.windows(2).for_each(|window| { + let start = window[0]; + let end = window[1]; + let vector = end - start; + let segment_length = vector.length(); + while position_on_segment < segment_length { + let new_point = start + vector * (position_on_segment / segment_length); + shapes.push(Shape::circle_filled(new_point, radius, color)); + position_on_segment += spacing; + } + position_on_segment -= segment_length; + }); +} + +fn dashes_from_line( + line: &[Pos2], + stroke: Stroke, + dash_length: f32, + dash_gap_ratio: f32, + shapes: &mut Vec, +) { + let gap_length = dash_length / dash_gap_ratio; + let mut position_on_segment = 0.0; + let mut drawing_dash = false; + line.windows(2).for_each(|window| { + let start = window[0]; + let end = window[1]; + let vector = end - start; + let segment_length = vector.length(); + while position_on_segment < segment_length { + let new_point = start + vector * (position_on_segment / segment_length); + if drawing_dash { + // This is the end point. + if let Shape::Path { points, .. } = shapes.last_mut().unwrap() { + points.push(new_point); + } + position_on_segment += gap_length; + } else { + // Start a new dash. + shapes.push(Shape::line(vec![new_point], stroke)); + position_on_segment += dash_length; + } + drawing_dash = !drawing_dash; + } + // If the segment ends and the dash is not finished, add the segment's end point. + if drawing_dash { + if let Shape::Path { points, .. } = shapes.last_mut().unwrap() { + points.push(end); + } + } + position_on_segment -= segment_length; + }); +} + /// ## Operations impl Shape { pub fn mesh(mesh: Mesh) -> Self { From 80936ba876da11bd2dd0881eade0d85fa25ae10e Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sat, 3 Jul 2021 16:06:01 +0200 Subject: [PATCH 5/7] apply suggestions --- egui/src/widgets/plot/items.rs | 86 +++++++--------------------------- epaint/src/shape.rs | 2 + 2 files changed, 20 insertions(+), 68 deletions(-) diff --git a/egui/src/widgets/plot/items.rs b/egui/src/widgets/plot/items.rs index 870cd20628b..20fd84332a1 100644 --- a/egui/src/widgets/plot/items.rs +++ b/egui/src/widgets/plot/items.rs @@ -37,25 +37,25 @@ impl Value { #[derive(Debug, PartialEq, Clone, Copy)] pub enum LineStyle { Solid, - Dotted { px: f32 }, - Dashed { px: f32 }, + Dotted { spacing: f32 }, + Dashed { length: f32 }, } impl LineStyle { pub fn dashed_loose() -> Self { - Self::Dashed { px: 10.0 } + Self::Dashed { length: 10.0 } } pub fn dashed_dense() -> Self { - Self::Dashed { px: 5.0 } + Self::Dashed { length: 5.0 } } pub fn dotted_loose() -> Self { - Self::Dotted { px: 10.0 } + Self::Dotted { spacing: 10.0 } } pub fn dotted_dense() -> Self { - Self::Dotted { px: 5.0 } + Self::Dotted { spacing: 5.0 } } fn style_line( @@ -82,20 +82,26 @@ impl LineStyle { } shapes.push(Shape::line(line, stroke)); } - LineStyle::Dotted { px } => { + LineStyle::Dotted { spacing } => { // Take the stroke width for the radius even though it's not "correct", otherwise // the dots would become too small. let mut radius = stroke.width; if highlight { radius *= 2f32.sqrt(); } - points_from_line(&line, *px, radius, stroke.color, shapes) + shapes.extend(Shape::dotted_line(&line, stroke.color, *spacing, radius)) } - LineStyle::Dashed { px } => { + LineStyle::Dashed { length } => { if highlight { stroke.width *= 2.0; } - dashes_from_line(&line, *px, stroke, shapes); + let golden_ratio = (5.0_f32.sqrt() - 1.0) / 2.0; // 0.61803398875 + shapes.extend(Shape::dashed_line( + &line, + stroke, + *length, + 1.0 / golden_ratio, + )) } } } @@ -107,68 +113,12 @@ impl ToString for LineStyle { fn to_string(&self) -> String { match self { LineStyle::Solid => "Solid".into(), - LineStyle::Dotted { px } => format!("Dotted{}Px", px), - LineStyle::Dashed { px } => format!("Dashed{}Px", px), + LineStyle::Dotted { spacing } => format!("Dotted{}Px", spacing), + LineStyle::Dashed { length } => format!("Dashed{}Px", length), } } } -fn points_from_line( - line: &[Pos2], - spacing: f32, - radius: f32, - color: Color32, - shapes: &mut Vec, -) { - let mut position_on_segment = 0.0; - line.windows(2).for_each(|window| { - let start = window[0]; - let end = window[1]; - let vector = end - start; - let segment_length = vector.length(); - while position_on_segment < segment_length { - let new_point = start + vector * (position_on_segment / segment_length); - shapes.push(Shape::circle_filled(new_point, radius, color)); - position_on_segment += spacing; - } - position_on_segment -= segment_length; - }); -} - -fn dashes_from_line(line: &[Pos2], dash_length: f32, stroke: Stroke, shapes: &mut Vec) { - let gap_length = 0.5 * dash_length; - let mut position_on_segment = 0.0; - let mut drawing_dash = false; - line.windows(2).for_each(|window| { - let start = window[0]; - let end = window[1]; - let vector = end - start; - let segment_length = vector.length(); - while position_on_segment < segment_length { - let new_point = start + vector * (position_on_segment / segment_length); - if drawing_dash { - // This is the end point. - if let Shape::Path { points, .. } = shapes.last_mut().unwrap() { - points.push(new_point); - } - position_on_segment += gap_length; - } else { - // Start a new dash. - shapes.push(Shape::line(vec![new_point], stroke)); - position_on_segment += dash_length; - } - drawing_dash = !drawing_dash; - } - // If the segment ends and the dash is not finished, add the segment's end point. - if drawing_dash { - if let Shape::Path { points, .. } = shapes.last_mut().unwrap() { - points.push(end); - } - } - position_on_segment -= segment_length; - }); -} - // ---------------------------------------------------------------------------- /// A horizontal line in a plot, filling the full width diff --git a/epaint/src/shape.rs b/epaint/src/shape.rs index 4fb9f4ff60a..9ea03dac5b9 100644 --- a/epaint/src/shape.rs +++ b/epaint/src/shape.rs @@ -191,6 +191,7 @@ impl Shape { } } +/// Creates equally spaced filled circles from a line. fn points_from_line( line: &[Pos2], spacing: f32, @@ -213,6 +214,7 @@ fn points_from_line( }); } +/// Creates dashes from a line. fn dashes_from_line( line: &[Pos2], stroke: Stroke, From e75e8027883cf6501b894a176e6ef0323bc3b5f1 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sat, 3 Jul 2021 16:09:45 +0200 Subject: [PATCH 6/7] clippy fix --- epaint/src/shape.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epaint/src/shape.rs b/epaint/src/shape.rs index 9ea03dac5b9..a6b9d312fa7 100644 --- a/epaint/src/shape.rs +++ b/epaint/src/shape.rs @@ -107,7 +107,7 @@ impl Shape { ) -> Vec { let mut shapes = Vec::new(); dashes_from_line( - &points, + points, stroke.into(), dash_length, dash_gap_ratio, From 5418cd7a195743f1b2d2344b9c297ef21952df37 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Tue, 6 Jul 2021 20:08:39 +0200 Subject: [PATCH 7/7] address comments --- egui/src/widgets/plot/items.rs | 2 +- epaint/src/shape.rs | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/egui/src/widgets/plot/items.rs b/egui/src/widgets/plot/items.rs index 20fd84332a1..fffd15e835f 100644 --- a/egui/src/widgets/plot/items.rs +++ b/egui/src/widgets/plot/items.rs @@ -100,7 +100,7 @@ impl LineStyle { &line, stroke, *length, - 1.0 / golden_ratio, + length * golden_ratio, )) } } diff --git a/epaint/src/shape.rs b/epaint/src/shape.rs index a6b9d312fa7..ff38093d412 100644 --- a/epaint/src/shape.rs +++ b/epaint/src/shape.rs @@ -103,16 +103,10 @@ impl Shape { points: &[Pos2], stroke: impl Into, dash_length: f32, - dash_gap_ratio: f32, + gap_length: f32, ) -> Vec { let mut shapes = Vec::new(); - dashes_from_line( - points, - stroke.into(), - dash_length, - dash_gap_ratio, - &mut shapes, - ); + dashes_from_line(points, stroke.into(), dash_length, gap_length, &mut shapes); shapes } @@ -219,10 +213,9 @@ fn dashes_from_line( line: &[Pos2], stroke: Stroke, dash_length: f32, - dash_gap_ratio: f32, + gap_length: f32, shapes: &mut Vec, ) { - let gap_length = dash_length / dash_gap_ratio; let mut position_on_segment = 0.0; let mut drawing_dash = false; line.windows(2).for_each(|window| {