From e835cea03c5d6eeba2d76b52206516dcc2a6b628 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Thu, 27 Jan 2022 09:40:52 -0800 Subject: [PATCH 1/6] Add line dash API --- examples/solar_system/src/main.rs | 4 +++ graphics/Cargo.toml | 6 +++- graphics/src/widget/canvas.rs | 2 +- graphics/src/widget/canvas/frame.rs | 9 ++++++ graphics/src/widget/canvas/path.rs | 44 ++++++++++++++++++++++++++++ graphics/src/widget/canvas/stroke.rs | 14 ++++++++- 6 files changed, 76 insertions(+), 3 deletions(-) diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 5f9724f386..0e7da97f7b 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -163,6 +163,10 @@ impl canvas::Program for State { Stroke { width: 1.0, color: Color::from_rgba8(0, 153, 255, 0.1), + line_dash: canvas::LineDash { + offset: 0, + segments: vec![6.0, 3.0], + }, ..Stroke::default() }, ); diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 12f38cceea..07a19807d0 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] [features] -canvas = ["lyon"] +canvas = ["lyon", "lyon_algorithms"] qr_code = ["qrcode", "canvas"] font-source = ["font-kit"] font-fallback = [] @@ -39,6 +39,10 @@ path = "../style" version = "0.17" optional = true +[dependencies.lyon_algorithms] +version = "0.17" +optional = true + [dependencies.qrcode] version = "0.12" optional = true diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index f9722f33ca..1016bbe3d0 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -35,7 +35,7 @@ pub use frame::Frame; pub use geometry::Geometry; pub use path::Path; pub use program::Program; -pub use stroke::{LineCap, LineJoin, Stroke}; +pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use text::Text; /// A widget capable of drawing 2D graphics. diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 4873e7fb8a..b98c30020d 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -1,6 +1,9 @@ +use std::borrow::Cow; + use iced_native::{Point, Rectangle, Size, Vector}; use crate::{ + canvas::path, canvas::{Fill, Geometry, Path, Stroke, Text}, triangle, Primitive, }; @@ -164,6 +167,12 @@ impl Frame { options.end_cap = stroke.line_cap.into(); options.line_join = stroke.line_join.into(); + let path = if stroke.line_dash.segments.is_empty() { + Cow::Borrowed(path) + } else { + Cow::Owned(path::dashed(path, stroke.line_dash)) + }; + let result = if self.transforms.current.is_identity { self.stroke_tessellator.tessellate_path( path.raw(), diff --git a/graphics/src/widget/canvas/path.rs b/graphics/src/widget/canvas/path.rs index 4e4fd7344b..b52c1f90d0 100644 --- a/graphics/src/widget/canvas/path.rs +++ b/graphics/src/widget/canvas/path.rs @@ -7,7 +7,11 @@ mod builder; pub use arc::Arc; pub use builder::Builder; +use crate::canvas::LineDash; + use iced_native::{Point, Size}; +use lyon::path::iterator::PathIterator; +use lyon_algorithms::walk::{walk_along_path, RepeatedPattern}; /// An immutable set of points that may or may not be connected. /// @@ -66,3 +70,43 @@ impl Path { } } } + +pub(super) fn dashed(path: &Path, line_dash: LineDash) -> Path { + let segments_odd = line_dash.segments.len() % 2 == 1; + + let segments = segments_odd + .then(|| [&line_dash.segments[..], &line_dash.segments[..]].concat()) + .unwrap_or(line_dash.segments); + + let mut points = vec![]; + + walk_along_path( + path.raw().iter().flattened(0.01), + 0.0, + &mut RepeatedPattern { + callback: |position: lyon_algorithms::math::Point, + _tangent, + _distance| { + points.push(Point { + x: position.x, + y: position.y, + }); + true + }, + index: line_dash.offset, + intervals: &segments, + }, + ); + + Path::new(|builder| { + for (idx, point) in points.into_iter().enumerate() { + let is_even = idx % 2 == 0; + + if is_even { + builder.move_to(point); + } else { + builder.line_to(point); + } + } + }) +} diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 9f0449d0a5..5c20405e88 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,7 +1,7 @@ use iced_native::Color; /// The style of a stroke. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct Stroke { /// The color of the stroke. pub color: Color, @@ -12,6 +12,8 @@ pub struct Stroke { /// The shape to be used at the corners of paths or basic shapes when they /// are stroked. pub line_join: LineJoin, + /// The dash pattern used when stroking the line. + pub line_dash: LineDash, } impl Stroke { @@ -43,6 +45,7 @@ impl Default for Stroke { width: 1.0, line_cap: LineCap::default(), line_join: LineJoin::default(), + line_dash: LineDash::default(), } } } @@ -103,3 +106,12 @@ impl From for lyon::tessellation::LineJoin { } } } + +/// The dash pattern used when stroking the line. +#[derive(Debug, Clone, Default)] +pub struct LineDash { + /// The alternating lengths of lines and gaps which describe the pattern. + pub segments: Vec, + /// The offset of [`LineDash::segments`] to start the pattern. + pub offset: usize, +} From 730c57ba67e20e2179418961e50fdc24acfdb677 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Thu, 27 Jan 2022 10:01:03 -0800 Subject: [PATCH 2/6] Remove vec allocation --- graphics/src/widget/canvas/path.rs | 71 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/graphics/src/widget/canvas/path.rs b/graphics/src/widget/canvas/path.rs index b52c1f90d0..bc258b67ae 100644 --- a/graphics/src/widget/canvas/path.rs +++ b/graphics/src/widget/canvas/path.rs @@ -72,41 +72,42 @@ impl Path { } pub(super) fn dashed(path: &Path, line_dash: LineDash) -> Path { - let segments_odd = line_dash.segments.len() % 2 == 1; - - let segments = segments_odd - .then(|| [&line_dash.segments[..], &line_dash.segments[..]].concat()) - .unwrap_or(line_dash.segments); - - let mut points = vec![]; - - walk_along_path( - path.raw().iter().flattened(0.01), - 0.0, - &mut RepeatedPattern { - callback: |position: lyon_algorithms::math::Point, - _tangent, - _distance| { - points.push(Point { - x: position.x, - y: position.y, - }); - true - }, - index: line_dash.offset, - intervals: &segments, - }, - ); - Path::new(|builder| { - for (idx, point) in points.into_iter().enumerate() { - let is_even = idx % 2 == 0; - - if is_even { - builder.move_to(point); - } else { - builder.line_to(point); - } - } + let segments_odd = line_dash.segments.len() % 2 == 1; + + let segments = segments_odd + .then(|| { + [&line_dash.segments[..], &line_dash.segments[..]].concat() + }) + .unwrap_or(line_dash.segments); + + let mut draw_line = false; + + walk_along_path( + path.raw().iter().flattened(0.01), + 0.0, + &mut RepeatedPattern { + callback: |position: lyon_algorithms::math::Point, + _tangent, + _distance| { + let point = Point { + x: position.x, + y: position.y, + }; + + if draw_line { + builder.line_to(point); + } else { + builder.move_to(point); + } + + draw_line = !draw_line; + + true + }, + index: line_dash.offset, + intervals: &segments, + }, + ); }) } From 39800b445f135211504b18bbc03ab9840198a64f Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Thu, 27 Jan 2022 10:01:16 -0800 Subject: [PATCH 3/6] Increase gap in example --- examples/solar_system/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 0e7da97f7b..fb782d9ca2 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -165,7 +165,7 @@ impl canvas::Program for State { color: Color::from_rgba8(0, 153, 255, 0.1), line_dash: canvas::LineDash { offset: 0, - segments: vec![6.0, 3.0], + segments: vec![3.0, 6.0], }, ..Stroke::default() }, From 76c03de58729783513504f8115d7381f9a52fd23 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Thu, 27 Jan 2022 10:04:23 -0800 Subject: [PATCH 4/6] Fix examples, no longer Copy --- examples/clock/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 325ccc1ad8..fce57cc09e 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -100,14 +100,14 @@ impl canvas::Program for Clock { let wide_stroke = Stroke { width: thin_stroke.width * 3.0, - ..thin_stroke + ..thin_stroke.clone() }; frame.translate(Vector::new(center.x, center.y)); frame.with_save(|frame| { frame.rotate(hand_rotation(self.now.hour(), 12)); - frame.stroke(&short_hand, wide_stroke); + frame.stroke(&short_hand, wide_stroke.clone()); }); frame.with_save(|frame| { From f56c8a7361ceb215bce68e88bd6ce402e2694693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 3 Feb 2022 17:18:05 +0700 Subject: [PATCH 5/6] Ask for a slice of segments instead of ownership in `LineDash` --- examples/clock/src/main.rs | 4 ++-- examples/solar_system/src/main.rs | 2 +- graphics/src/widget/canvas/frame.rs | 2 +- graphics/src/widget/canvas/path.rs | 17 ++++++++--------- graphics/src/widget/canvas/stroke.rs | 27 ++++++++++++++------------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index fce57cc09e..325ccc1ad8 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -100,14 +100,14 @@ impl canvas::Program for Clock { let wide_stroke = Stroke { width: thin_stroke.width * 3.0, - ..thin_stroke.clone() + ..thin_stroke }; frame.translate(Vector::new(center.x, center.y)); frame.with_save(|frame| { frame.rotate(hand_rotation(self.now.hour(), 12)); - frame.stroke(&short_hand, wide_stroke.clone()); + frame.stroke(&short_hand, wide_stroke); }); frame.with_save(|frame| { diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index fb782d9ca2..12184dd19d 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -165,7 +165,7 @@ impl canvas::Program for State { color: Color::from_rgba8(0, 153, 255, 0.1), line_dash: canvas::LineDash { offset: 0, - segments: vec![3.0, 6.0], + segments: &[3.0, 6.0], }, ..Stroke::default() }, diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index b98c30020d..357dfa6286 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -153,7 +153,7 @@ impl Frame { /// Draws the stroke of the given [`Path`] on the [`Frame`] with the /// provided style. - pub fn stroke(&mut self, path: &Path, stroke: impl Into) { + pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>) { let stroke = stroke.into(); let mut buffers = tessellation::BuffersBuilder::new( diff --git a/graphics/src/widget/canvas/path.rs b/graphics/src/widget/canvas/path.rs index bc258b67ae..cb7e50359e 100644 --- a/graphics/src/widget/canvas/path.rs +++ b/graphics/src/widget/canvas/path.rs @@ -71,15 +71,11 @@ impl Path { } } -pub(super) fn dashed(path: &Path, line_dash: LineDash) -> Path { +pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { Path::new(|builder| { - let segments_odd = line_dash.segments.len() % 2 == 1; - - let segments = segments_odd - .then(|| { - [&line_dash.segments[..], &line_dash.segments[..]].concat() - }) - .unwrap_or(line_dash.segments); + let segments_odd = (line_dash.segments.len() % 2 == 1).then(|| { + [&line_dash.segments[..], &line_dash.segments[..]].concat() + }); let mut draw_line = false; @@ -106,7 +102,10 @@ pub(super) fn dashed(path: &Path, line_dash: LineDash) -> Path { true }, index: line_dash.offset, - intervals: &segments, + intervals: segments_odd + .as_ref() + .map(Vec::as_slice) + .unwrap_or(line_dash.segments), }, ); }) diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 5c20405e88..6accc2fbde 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,8 +1,8 @@ use iced_native::Color; /// The style of a stroke. -#[derive(Debug, Clone)] -pub struct Stroke { +#[derive(Debug, Clone, Copy)] +pub struct Stroke<'a> { /// The color of the stroke. pub color: Color, /// The distance between the two edges of the stroke. @@ -13,33 +13,33 @@ pub struct Stroke { /// are stroked. pub line_join: LineJoin, /// The dash pattern used when stroking the line. - pub line_dash: LineDash, + pub line_dash: LineDash<'a>, } -impl Stroke { +impl<'a> Stroke<'a> { /// Sets the color of the [`Stroke`]. - pub fn with_color(self, color: Color) -> Stroke { + pub fn with_color(self, color: Color) -> Self { Stroke { color, ..self } } /// Sets the width of the [`Stroke`]. - pub fn with_width(self, width: f32) -> Stroke { + pub fn with_width(self, width: f32) -> Self { Stroke { width, ..self } } /// Sets the [`LineCap`] of the [`Stroke`]. - pub fn with_line_cap(self, line_cap: LineCap) -> Stroke { + pub fn with_line_cap(self, line_cap: LineCap) -> Self { Stroke { line_cap, ..self } } /// Sets the [`LineJoin`] of the [`Stroke`]. - pub fn with_line_join(self, line_join: LineJoin) -> Stroke { + pub fn with_line_join(self, line_join: LineJoin) -> Self { Stroke { line_join, ..self } } } -impl Default for Stroke { - fn default() -> Stroke { +impl<'a> Default for Stroke<'a> { + fn default() -> Self { Stroke { color: Color::BLACK, width: 1.0, @@ -108,10 +108,11 @@ impl From for lyon::tessellation::LineJoin { } /// The dash pattern used when stroking the line. -#[derive(Debug, Clone, Default)] -pub struct LineDash { +#[derive(Debug, Clone, Copy, Default)] +pub struct LineDash<'a> { /// The alternating lengths of lines and gaps which describe the pattern. - pub segments: Vec, + pub segments: &'a [f32], + /// The offset of [`LineDash::segments`] to start the pattern. pub offset: usize, } From bace264bfe5e3cb5046867bd411e54969a637c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 3 Feb 2022 17:19:48 +0700 Subject: [PATCH 6/6] Access `lyon_algorithms` indirectly through `lyon` --- graphics/Cargo.toml | 6 +----- graphics/src/widget/canvas/path.rs | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 07a19807d0..12f38cceea 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] [features] -canvas = ["lyon", "lyon_algorithms"] +canvas = ["lyon"] qr_code = ["qrcode", "canvas"] font-source = ["font-kit"] font-fallback = [] @@ -39,10 +39,6 @@ path = "../style" version = "0.17" optional = true -[dependencies.lyon_algorithms] -version = "0.17" -optional = true - [dependencies.qrcode] version = "0.12" optional = true diff --git a/graphics/src/widget/canvas/path.rs b/graphics/src/widget/canvas/path.rs index cb7e50359e..1728f0607c 100644 --- a/graphics/src/widget/canvas/path.rs +++ b/graphics/src/widget/canvas/path.rs @@ -10,8 +10,8 @@ pub use builder::Builder; use crate::canvas::LineDash; use iced_native::{Point, Size}; +use lyon::algorithms::walk::{walk_along_path, RepeatedPattern}; use lyon::path::iterator::PathIterator; -use lyon_algorithms::walk::{walk_along_path, RepeatedPattern}; /// An immutable set of points that may or may not be connected. /// @@ -83,7 +83,7 @@ pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { path.raw().iter().flattened(0.01), 0.0, &mut RepeatedPattern { - callback: |position: lyon_algorithms::math::Point, + callback: |position: lyon::algorithms::math::Point, _tangent, _distance| { let point = Point {