From 40f45d7b7e35dd4937abe6b5ce16b6256b4f1eeb Mon Sep 17 00:00:00 2001 From: shan Date: Thu, 29 Sep 2022 10:52:58 -0700 Subject: Adds linear gradient support to 2D meshes in the canvas widget. --- graphics/src/widget/canvas/fill.rs | 33 +++++--- graphics/src/widget/canvas/frame.rs | 113 +++++++++----------------- graphics/src/widget/canvas/gradient.rs | 21 +++++ graphics/src/widget/canvas/gradient/linear.rs | 73 +++++++++++++++++ graphics/src/widget/canvas/stroke.rs | 26 ++++-- 5 files changed, 177 insertions(+), 89 deletions(-) create mode 100644 graphics/src/widget/canvas/gradient.rs create mode 100644 graphics/src/widget/canvas/gradient/linear.rs (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 56495435..02d2311f 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -1,12 +1,14 @@ use iced_native::Color; +use crate::widget::canvas::Gradient; + /// The style used to fill geometry. -#[derive(Debug, Clone, Copy)] -pub struct Fill { - /// The color used to fill geometry. +#[derive(Debug, Clone)] +pub struct Fill<'a> { + /// The color or gradient of the fill. /// - /// By default, it is set to `BLACK`. - pub color: Color, + /// By default, it is set to [`FillStyle::Solid`] `BLACK`. + pub style: FillStyle<'a>, /// The fill rule defines how to determine what is inside and what is /// outside of a shape. @@ -19,24 +21,33 @@ pub struct Fill { pub rule: FillRule, } -impl Default for Fill { - fn default() -> Fill { +impl <'a> Default for Fill<'a> { + fn default() -> Fill<'a> { Fill { - color: Color::BLACK, + style: FillStyle::Solid(Color::BLACK), rule: FillRule::NonZero, } } } -impl From for Fill { - fn from(color: Color) -> Fill { +impl<'a> From for Fill<'a> { + fn from(color: Color) -> Fill<'a> { Fill { - color, + style: FillStyle::Solid(color), ..Fill::default() } } } +/// The color or gradient of a [`Fill`]. +#[derive(Debug, Clone)] +pub enum FillStyle<'a> { + /// A solid color + Solid(Color), + /// A color gradient + Gradient(&'a Gradient), +} + /// The fill rule defines how to determine what is inside and what is outside of /// a shape. /// diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 516539ca..8845bc6a 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -3,11 +3,13 @@ use std::borrow::Cow; use iced_native::{Point, Rectangle, Size, Vector}; use crate::triangle; -use crate::widget::canvas::path; -use crate::widget::canvas::{Fill, Geometry, Path, Stroke, Text}; +use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; use crate::Primitive; +use crate::shader::Shader; +use crate::triangle::Vertex2D; use lyon::tessellation; +use lyon::tessellation::geometry_builder::Positions; /// The frame of a [`Canvas`]. /// @@ -15,7 +17,7 @@ use lyon::tessellation; #[allow(missing_debug_implementations)] pub struct Frame { size: Size, - buffers: lyon::tessellation::VertexBuffers, + buffers: Vec<(tessellation::VertexBuffers, Shader)>, primitives: Vec, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, @@ -42,7 +44,7 @@ impl Frame { pub fn new(size: Size) -> Frame { Frame { size, - buffers: lyon::tessellation::VertexBuffers::new(), + buffers: Vec::new(), primitives: Vec::new(), transforms: Transforms { previous: Vec::new(), @@ -82,18 +84,18 @@ impl Frame { /// Draws the given [`Path`] on the [`Frame`] by filling it with the /// provided style. - pub fn fill(&mut self, path: &Path, fill: impl Into) { - let Fill { color, rule } = fill.into(); + pub fn fill<'a>(&mut self, path: &Path, fill: impl Into>) { + let Fill { style, rule } = fill.into(); - let mut buffers = tessellation::BuffersBuilder::new( - &mut self.buffers, - FillVertex(color.into_linear()), - ); + let mut buf = tessellation::VertexBuffers::new(); + + let mut buffers = + tessellation::BuffersBuilder::new(&mut buf, Positions); let options = tessellation::FillOptions::default().with_fill_rule(rule.into()); - let result = if self.transforms.current.is_identity { + if self.transforms.current.is_identity { self.fill_tessellator.tessellate_path( path.raw(), &options, @@ -107,25 +109,24 @@ impl Frame { &options, &mut buffers, ) - }; + }.expect("Tessellate path."); - result.expect("Tessellate path"); + self.buffers.push((buf, style.into())) } /// Draws an axis-aligned rectangle given its top-left corner coordinate and /// its `Size` on the [`Frame`] by filling it with the provided style. - pub fn fill_rectangle( + pub fn fill_rectangle<'a>( &mut self, top_left: Point, size: Size, - fill: impl Into, + fill: impl Into>, ) { - let Fill { color, rule } = fill.into(); + let Fill { style, rule } = fill.into(); + + let mut buf = tessellation::VertexBuffers::new(); - let mut buffers = tessellation::BuffersBuilder::new( - &mut self.buffers, - FillVertex(color.into_linear()), - ); + let mut buffers = tessellation::BuffersBuilder::new(&mut buf, Positions); let top_left = self.transforms.current.raw.transform_point( @@ -147,6 +148,8 @@ impl Frame { &mut buffers, ) .expect("Fill rectangle"); + + self.buffers.push((buf, style.into())) } /// Draws the stroke of the given [`Path`] on the [`Frame`] with the @@ -154,10 +157,9 @@ impl Frame { pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>) { let stroke = stroke.into(); - let mut buffers = tessellation::BuffersBuilder::new( - &mut self.buffers, - StrokeVertex(stroke.color.into_linear()), - ); + let mut buf = tessellation::VertexBuffers::new(); + + let mut buffers = tessellation::BuffersBuilder::new(&mut buf, Positions); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; @@ -171,7 +173,7 @@ impl Frame { Cow::Owned(path::dashed(path, stroke.line_dash)) }; - let result = if self.transforms.current.is_identity { + if self.transforms.current.is_identity { self.stroke_tessellator.tessellate_path( path.raw(), &options, @@ -185,9 +187,9 @@ impl Frame { &options, &mut buffers, ) - }; + }.expect("Stroke path"); - result.expect("Stroke path"); + self.buffers.push((buf, stroke.style.into())) } /// Draws the characters of the given [`Text`] on the [`Frame`], filling @@ -206,8 +208,6 @@ impl Frame { /// /// [`Canvas`]: crate::widget::Canvas pub fn fill_text(&mut self, text: impl Into) { - use std::f32; - let text = text.into(); let position = if self.transforms.current.is_identity { @@ -331,52 +331,19 @@ impl Frame { } fn into_primitives(mut self) -> Vec { - if !self.buffers.indices.is_empty() { - self.primitives.push(Primitive::Mesh2D { - buffers: triangle::Mesh2D { - vertices: self.buffers.vertices, - indices: self.buffers.indices, - }, - size: self.size, - }); + for (buffer, shader) in self.buffers { + if !buffer.indices.is_empty() { + self.primitives.push(Primitive::Mesh2D { + buffers: triangle::Mesh2D { + vertices: Vertex2D::from(buffer.vertices), + indices: buffer.indices, + }, + size: self.size, + shader, + }) + } } self.primitives } } - -struct FillVertex([f32; 4]); - -impl lyon::tessellation::FillVertexConstructor - for FillVertex -{ - fn new_vertex( - &mut self, - vertex: lyon::tessellation::FillVertex<'_>, - ) -> triangle::Vertex2D { - let position = vertex.position(); - - triangle::Vertex2D { - position: [position.x, position.y], - color: self.0, - } - } -} - -struct StrokeVertex([f32; 4]); - -impl lyon::tessellation::StrokeVertexConstructor - for StrokeVertex -{ - fn new_vertex( - &mut self, - vertex: lyon::tessellation::StrokeVertex<'_, '_>, - ) -> triangle::Vertex2D { - let position = vertex.position(); - - triangle::Vertex2D { - position: [position.x, position.y], - color: self.0, - } - } -} diff --git a/graphics/src/widget/canvas/gradient.rs b/graphics/src/widget/canvas/gradient.rs new file mode 100644 index 00000000..7d2daabc --- /dev/null +++ b/graphics/src/widget/canvas/gradient.rs @@ -0,0 +1,21 @@ +//! Define a color gradient. +use iced_native::Point; + +pub mod linear; + +pub use linear::Linear; + +/// A gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. +#[derive(Debug, Clone)] +pub enum Gradient { + /// A linear gradient + Linear(Linear), + //TODO: radial, conical +} + +impl Gradient { + /// Creates a new linear [`linear::Builder`]. + pub fn linear(start: Point, end: Point) -> linear::Builder { + linear::Builder::new(start, end) + } +} \ No newline at end of file diff --git a/graphics/src/widget/canvas/gradient/linear.rs b/graphics/src/widget/canvas/gradient/linear.rs new file mode 100644 index 00000000..37533e19 --- /dev/null +++ b/graphics/src/widget/canvas/gradient/linear.rs @@ -0,0 +1,73 @@ +//! A linear color gradient. +use iced_native::{Color, Point}; + +use crate::gradient::ColorStop; + +use super::Gradient; + +/// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. +#[derive(Debug, Clone, PartialEq)] +pub struct Linear { + /// The point where the linear gradient begins. + pub start: Point, + /// The point where the linear gradient ends. + pub end: Point, + /// [`ColorStop`]s along the linear gradient path. + pub color_stops: Vec, +} + +/// A [`Linear`] builder. +#[derive(Debug)] +pub struct Builder { + start: Point, + end: Point, + stops: Vec<(f32, Color)>, + valid: bool, +} + +impl Builder { + /// Creates a new [`Builder`]. + pub fn new(start: Point, end: Point) -> Self { + Self { + start, + end, + stops: vec![], + valid: true, + } + } + + /// Adds a new stop, defined by an offset and a color, to the gradient. + /// + /// `offset` must be between `0.0` and `1.0`. + pub fn add_stop(mut self, offset: f32, color: Color) -> Self { + if !(0.0..=1.0).contains(&offset) { + self.valid = false; + } + + self.stops.push((offset, color)); + self + } + + /// Builds the linear [`Gradient`] of this [`Builder`]. + /// + /// Returns `None` if no stops were added to the builder or + /// if stops not between 0.0 and 1.0 were added. + pub fn build(self) -> Option { + if self.stops.is_empty() || !self.valid { + return None; + } + + Some(Gradient::Linear(Linear { + start: self.start, + end: self.end, + color_stops: self + .stops + .into_iter() + .map(|f| ColorStop { + offset: f.0, + color: f.1, + }) + .collect(), + })) + } +} diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 6accc2fb..c319b398 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,10 +1,14 @@ use iced_native::Color; +use crate::widget::canvas::Gradient; + /// The style of a stroke. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct Stroke<'a> { - /// The color of the stroke. - pub color: Color, + /// The color or gradient of the stroke. + /// + /// By default, it is set to [`StrokeStyle::Solid`] `BLACK`. + pub style: StrokeStyle<'a>, /// The distance between the two edges of the stroke. pub width: f32, /// The shape to be used at the end of open subpaths when they are stroked. @@ -19,7 +23,10 @@ pub struct Stroke<'a> { impl<'a> Stroke<'a> { /// Sets the color of the [`Stroke`]. pub fn with_color(self, color: Color) -> Self { - Stroke { color, ..self } + Stroke { + style: StrokeStyle::Solid(color), + ..self + } } /// Sets the width of the [`Stroke`]. @@ -41,7 +48,7 @@ impl<'a> Stroke<'a> { impl<'a> Default for Stroke<'a> { fn default() -> Self { Stroke { - color: Color::BLACK, + style: StrokeStyle::Solid(Color::BLACK), width: 1.0, line_cap: LineCap::default(), line_join: LineJoin::default(), @@ -50,6 +57,15 @@ impl<'a> Default for Stroke<'a> { } } +/// The color or gradient of a [`Stroke`]. +#[derive(Debug, Clone, Copy)] +pub enum StrokeStyle<'a> { + /// A solid color + Solid(Color), + /// A color gradient + Gradient(&'a Gradient), +} + /// The shape used at the end of open subpaths when they are stroked. #[derive(Debug, Clone, Copy)] pub enum LineCap { -- cgit From 734557bda5924e563a9f0b39aca37d5953fcda5c Mon Sep 17 00:00:00 2001 From: shan Date: Thu, 29 Sep 2022 14:01:57 -0700 Subject: Fixed issue where stops could be declared out of order in the builder but must be sorted before being passed to shader. --- graphics/src/widget/canvas/gradient/linear.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/gradient/linear.rs b/graphics/src/widget/canvas/gradient/linear.rs index 37533e19..1dc7e3a8 100644 --- a/graphics/src/widget/canvas/gradient/linear.rs +++ b/graphics/src/widget/canvas/gradient/linear.rs @@ -57,17 +57,17 @@ impl Builder { return None; } + let mut stops: Vec = self.stops.clone().into_iter().map(|f| ColorStop { + offset: f.0, + color: f.1 + }).collect(); + + stops.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap()); + Some(Gradient::Linear(Linear { start: self.start, end: self.end, - color_stops: self - .stops - .into_iter() - .map(|f| ColorStop { - offset: f.0, - color: f.1, - }) - .collect(), + color_stops: stops })) } } -- cgit From 5d0fffc626928177239336757507b986b081b878 Mon Sep 17 00:00:00 2001 From: shan Date: Fri, 30 Sep 2022 10:27:00 -0700 Subject: Fixed some importing issues since you can use a Shader::Gradient outside a Canvas widget, where it was previously only accessible. --- graphics/src/widget/canvas/fill.rs | 13 ++++- graphics/src/widget/canvas/frame.rs | 7 ++- graphics/src/widget/canvas/gradient.rs | 21 -------- graphics/src/widget/canvas/gradient/linear.rs | 73 --------------------------- graphics/src/widget/canvas/stroke.rs | 13 ++++- 5 files changed, 28 insertions(+), 99 deletions(-) delete mode 100644 graphics/src/widget/canvas/gradient.rs delete mode 100644 graphics/src/widget/canvas/gradient/linear.rs (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 02d2311f..60029e03 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -1,6 +1,6 @@ use iced_native::Color; - -use crate::widget::canvas::Gradient; +use crate::gradient::Gradient; +use crate::shader::Shader; /// The style used to fill geometry. #[derive(Debug, Clone)] @@ -48,6 +48,15 @@ pub enum FillStyle<'a> { Gradient(&'a Gradient), } +impl <'a> Into for FillStyle<'a> { + fn into(self) -> Shader { + match self { + FillStyle::Solid(color) => Shader::Solid(color), + FillStyle::Gradient(gradient) => gradient.clone().into() + } + } +} + /// The fill rule defines how to determine what is inside and what is outside of /// a shape. /// diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 8845bc6a..30239b2a 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -335,7 +335,7 @@ impl Frame { if !buffer.indices.is_empty() { self.primitives.push(Primitive::Mesh2D { buffers: triangle::Mesh2D { - vertices: Vertex2D::from(buffer.vertices), + vertices: vertices_from(buffer.vertices), indices: buffer.indices, }, size: self.size, @@ -347,3 +347,8 @@ impl Frame { self.primitives } } + +/// Converts from [`lyon::math::Point`] to [`Vertex2D`]. Used for generating primitives. +fn vertices_from(points: Vec) -> Vec { + points.iter().map(|p| Vertex2D { position: [p.x, p.y]}).collect() +} \ No newline at end of file diff --git a/graphics/src/widget/canvas/gradient.rs b/graphics/src/widget/canvas/gradient.rs deleted file mode 100644 index 7d2daabc..00000000 --- a/graphics/src/widget/canvas/gradient.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Define a color gradient. -use iced_native::Point; - -pub mod linear; - -pub use linear::Linear; - -/// A gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. -#[derive(Debug, Clone)] -pub enum Gradient { - /// A linear gradient - Linear(Linear), - //TODO: radial, conical -} - -impl Gradient { - /// Creates a new linear [`linear::Builder`]. - pub fn linear(start: Point, end: Point) -> linear::Builder { - linear::Builder::new(start, end) - } -} \ No newline at end of file diff --git a/graphics/src/widget/canvas/gradient/linear.rs b/graphics/src/widget/canvas/gradient/linear.rs deleted file mode 100644 index 1dc7e3a8..00000000 --- a/graphics/src/widget/canvas/gradient/linear.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! A linear color gradient. -use iced_native::{Color, Point}; - -use crate::gradient::ColorStop; - -use super::Gradient; - -/// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. -#[derive(Debug, Clone, PartialEq)] -pub struct Linear { - /// The point where the linear gradient begins. - pub start: Point, - /// The point where the linear gradient ends. - pub end: Point, - /// [`ColorStop`]s along the linear gradient path. - pub color_stops: Vec, -} - -/// A [`Linear`] builder. -#[derive(Debug)] -pub struct Builder { - start: Point, - end: Point, - stops: Vec<(f32, Color)>, - valid: bool, -} - -impl Builder { - /// Creates a new [`Builder`]. - pub fn new(start: Point, end: Point) -> Self { - Self { - start, - end, - stops: vec![], - valid: true, - } - } - - /// Adds a new stop, defined by an offset and a color, to the gradient. - /// - /// `offset` must be between `0.0` and `1.0`. - pub fn add_stop(mut self, offset: f32, color: Color) -> Self { - if !(0.0..=1.0).contains(&offset) { - self.valid = false; - } - - self.stops.push((offset, color)); - self - } - - /// Builds the linear [`Gradient`] of this [`Builder`]. - /// - /// Returns `None` if no stops were added to the builder or - /// if stops not between 0.0 and 1.0 were added. - pub fn build(self) -> Option { - if self.stops.is_empty() || !self.valid { - return None; - } - - let mut stops: Vec = self.stops.clone().into_iter().map(|f| ColorStop { - offset: f.0, - color: f.1 - }).collect(); - - stops.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap()); - - Some(Gradient::Linear(Linear { - start: self.start, - end: self.end, - color_stops: stops - })) - } -} diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index c319b398..ed82f189 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,6 +1,6 @@ use iced_native::Color; - -use crate::widget::canvas::Gradient; +use crate::gradient::Gradient; +use crate::shader::Shader; /// The style of a stroke. #[derive(Debug, Clone)] @@ -66,6 +66,15 @@ pub enum StrokeStyle<'a> { Gradient(&'a Gradient), } +impl <'a> Into for StrokeStyle<'a> { + fn into(self) -> Shader { + match self { + StrokeStyle::Solid(color) => Shader::Solid(color), + StrokeStyle::Gradient(gradient) => gradient.clone().into() + } + } +} + /// The shape used at the end of open subpaths when they are stroked. #[derive(Debug, Clone, Copy)] pub enum LineCap { -- cgit From 6e7b3ced0b1daf368e44e181ecdb4ae529877eb6 Mon Sep 17 00:00:00 2001 From: shan Date: Tue, 4 Oct 2022 18:24:46 -0700 Subject: Reworked wgpu buffers, updated glow side to have proper transform location storage, attempting to fix visibility modifiers, implemented some of the feedback received in initial PR. --- graphics/src/widget/canvas/fill.rs | 14 +++++++------- graphics/src/widget/canvas/stroke.rs | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 60029e03..6f10505c 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -8,7 +8,7 @@ pub struct Fill<'a> { /// The color or gradient of the fill. /// /// By default, it is set to [`FillStyle::Solid`] `BLACK`. - pub style: FillStyle<'a>, + pub style: Style<'a>, /// The fill rule defines how to determine what is inside and what is /// outside of a shape. @@ -24,7 +24,7 @@ pub struct Fill<'a> { impl <'a> Default for Fill<'a> { fn default() -> Fill<'a> { Fill { - style: FillStyle::Solid(Color::BLACK), + style: Style::Solid(Color::BLACK), rule: FillRule::NonZero, } } @@ -33,7 +33,7 @@ impl <'a> Default for Fill<'a> { impl<'a> From for Fill<'a> { fn from(color: Color) -> Fill<'a> { Fill { - style: FillStyle::Solid(color), + style: Style::Solid(color), ..Fill::default() } } @@ -41,18 +41,18 @@ impl<'a> From for Fill<'a> { /// The color or gradient of a [`Fill`]. #[derive(Debug, Clone)] -pub enum FillStyle<'a> { +pub enum Style<'a> { /// A solid color Solid(Color), /// A color gradient Gradient(&'a Gradient), } -impl <'a> Into for FillStyle<'a> { +impl <'a> Into for Style<'a> { fn into(self) -> Shader { match self { - FillStyle::Solid(color) => Shader::Solid(color), - FillStyle::Gradient(gradient) => gradient.clone().into() + Style::Solid(color) => Shader::Solid(color), + Style::Gradient(gradient) => gradient.clone().into() } } } diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index ed82f189..7ce5ff1d 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -8,7 +8,7 @@ pub struct Stroke<'a> { /// The color or gradient of the stroke. /// /// By default, it is set to [`StrokeStyle::Solid`] `BLACK`. - pub style: StrokeStyle<'a>, + pub style: Style<'a>, /// The distance between the two edges of the stroke. pub width: f32, /// The shape to be used at the end of open subpaths when they are stroked. @@ -24,7 +24,7 @@ impl<'a> Stroke<'a> { /// Sets the color of the [`Stroke`]. pub fn with_color(self, color: Color) -> Self { Stroke { - style: StrokeStyle::Solid(color), + style: Style::Solid(color), ..self } } @@ -48,7 +48,7 @@ impl<'a> Stroke<'a> { impl<'a> Default for Stroke<'a> { fn default() -> Self { Stroke { - style: StrokeStyle::Solid(Color::BLACK), + style: Style::Solid(Color::BLACK), width: 1.0, line_cap: LineCap::default(), line_join: LineJoin::default(), @@ -59,18 +59,18 @@ impl<'a> Default for Stroke<'a> { /// The color or gradient of a [`Stroke`]. #[derive(Debug, Clone, Copy)] -pub enum StrokeStyle<'a> { +pub enum Style<'a> { /// A solid color Solid(Color), /// A color gradient Gradient(&'a Gradient), } -impl <'a> Into for StrokeStyle<'a> { +impl <'a> Into for Style<'a> { fn into(self) -> Shader { match self { - StrokeStyle::Solid(color) => Shader::Solid(color), - StrokeStyle::Gradient(gradient) => gradient.clone().into() + Style::Solid(color) => Shader::Solid(color), + Style::Gradient(gradient) => gradient.clone().into() } } } -- cgit From 30432cbade3d9b25c4df62656a7494db3f4ea82a Mon Sep 17 00:00:00 2001 From: shan Date: Wed, 5 Oct 2022 10:49:58 -0700 Subject: Readjusted namespaces, removed Geometry example as it's no longer relevant. --- graphics/src/widget/canvas/fill.rs | 16 +++++++++------- graphics/src/widget/canvas/frame.rs | 32 ++++++++++++++++++++++---------- graphics/src/widget/canvas/stroke.rs | 10 ++++++---- 3 files changed, 37 insertions(+), 21 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 6f10505c..55cb3966 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -1,6 +1,8 @@ -use iced_native::Color; +//! Fill [crate::widget::canvas::Geometry] with a certain style. + use crate::gradient::Gradient; -use crate::shader::Shader; +use crate::layer::mesh; +use iced_native::Color; /// The style used to fill geometry. #[derive(Debug, Clone)] @@ -21,7 +23,7 @@ pub struct Fill<'a> { pub rule: FillRule, } -impl <'a> Default for Fill<'a> { +impl<'a> Default for Fill<'a> { fn default() -> Fill<'a> { Fill { style: Style::Solid(Color::BLACK), @@ -48,11 +50,11 @@ pub enum Style<'a> { Gradient(&'a Gradient), } -impl <'a> Into for Style<'a> { - fn into(self) -> Shader { +impl<'a> Into for Style<'a> { + fn into(self) -> mesh::Style { match self { - Style::Solid(color) => Shader::Solid(color), - Style::Gradient(gradient) => gradient.clone().into() + Style::Solid(color) => mesh::Style::Solid(color), + Style::Gradient(gradient) => gradient.clone().into(), } } } diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 30239b2a..6517d62a 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -6,7 +6,7 @@ use crate::triangle; use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; use crate::Primitive; -use crate::shader::Shader; +use crate::layer::mesh; use crate::triangle::Vertex2D; use lyon::tessellation; use lyon::tessellation::geometry_builder::Positions; @@ -17,7 +17,10 @@ use lyon::tessellation::geometry_builder::Positions; #[allow(missing_debug_implementations)] pub struct Frame { size: Size, - buffers: Vec<(tessellation::VertexBuffers, Shader)>, + buffers: Vec<( + tessellation::VertexBuffers, + mesh::Style, + )>, primitives: Vec, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, @@ -109,7 +112,8 @@ impl Frame { &options, &mut buffers, ) - }.expect("Tessellate path."); + } + .expect("Tessellate path."); self.buffers.push((buf, style.into())) } @@ -126,7 +130,8 @@ impl Frame { let mut buf = tessellation::VertexBuffers::new(); - let mut buffers = tessellation::BuffersBuilder::new(&mut buf, Positions); + let mut buffers = + tessellation::BuffersBuilder::new(&mut buf, Positions); let top_left = self.transforms.current.raw.transform_point( @@ -159,7 +164,8 @@ impl Frame { let mut buf = tessellation::VertexBuffers::new(); - let mut buffers = tessellation::BuffersBuilder::new(&mut buf, Positions); + let mut buffers = + tessellation::BuffersBuilder::new(&mut buf, Positions); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; @@ -187,7 +193,8 @@ impl Frame { &options, &mut buffers, ) - }.expect("Stroke path"); + } + .expect("Stroke path"); self.buffers.push((buf, stroke.style.into())) } @@ -331,7 +338,7 @@ impl Frame { } fn into_primitives(mut self) -> Vec { - for (buffer, shader) in self.buffers { + for (buffer, style) in self.buffers { if !buffer.indices.is_empty() { self.primitives.push(Primitive::Mesh2D { buffers: triangle::Mesh2D { @@ -339,7 +346,7 @@ impl Frame { indices: buffer.indices, }, size: self.size, - shader, + style, }) } } @@ -350,5 +357,10 @@ impl Frame { /// Converts from [`lyon::math::Point`] to [`Vertex2D`]. Used for generating primitives. fn vertices_from(points: Vec) -> Vec { - points.iter().map(|p| Vertex2D { position: [p.x, p.y]}).collect() -} \ No newline at end of file + points + .iter() + .map(|p| Vertex2D { + position: [p.x, p.y], + }) + .collect() +} diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 7ce5ff1d..a19937ea 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,6 +1,8 @@ +//! Create lines from a [crate::widget::canvas::Path] and render with various attributes/styles. + use iced_native::Color; use crate::gradient::Gradient; -use crate::shader::Shader; +use crate::layer::mesh; /// The style of a stroke. #[derive(Debug, Clone)] @@ -66,10 +68,10 @@ pub enum Style<'a> { Gradient(&'a Gradient), } -impl <'a> Into for Style<'a> { - fn into(self) -> Shader { +impl <'a> Into for Style<'a> { + fn into(self) -> mesh::Style { match self { - Style::Solid(color) => Shader::Solid(color), + Style::Solid(color) => mesh::Style::Solid(color), Style::Gradient(gradient) => gradient.clone().into() } } -- cgit From f4878a1a66a2e95e9430a8ccee8da823d2cb17ff Mon Sep 17 00:00:00 2001 From: shan Date: Thu, 6 Oct 2022 08:04:43 -0700 Subject: Changed tesselation functions to take Vertex2D builder instead of using lyon's builtin Point type to avoid extra copies. --- graphics/src/widget/canvas/frame.rs | 48 ++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 6517d62a..427a2e2a 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -9,7 +9,6 @@ use crate::Primitive; use crate::layer::mesh; use crate::triangle::Vertex2D; use lyon::tessellation; -use lyon::tessellation::geometry_builder::Positions; /// The frame of a [`Canvas`]. /// @@ -17,10 +16,7 @@ use lyon::tessellation::geometry_builder::Positions; #[allow(missing_debug_implementations)] pub struct Frame { size: Size, - buffers: Vec<( - tessellation::VertexBuffers, - mesh::Style, - )>, + buffers: Vec<(tessellation::VertexBuffers, mesh::Style)>, primitives: Vec, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, @@ -93,7 +89,7 @@ impl Frame { let mut buf = tessellation::VertexBuffers::new(); let mut buffers = - tessellation::BuffersBuilder::new(&mut buf, Positions); + tessellation::BuffersBuilder::new(&mut buf, Vertex2DBuilder); let options = tessellation::FillOptions::default().with_fill_rule(rule.into()); @@ -131,7 +127,7 @@ impl Frame { let mut buf = tessellation::VertexBuffers::new(); let mut buffers = - tessellation::BuffersBuilder::new(&mut buf, Positions); + tessellation::BuffersBuilder::new(&mut buf, Vertex2DBuilder); let top_left = self.transforms.current.raw.transform_point( @@ -165,7 +161,7 @@ impl Frame { let mut buf = tessellation::VertexBuffers::new(); let mut buffers = - tessellation::BuffersBuilder::new(&mut buf, Positions); + tessellation::BuffersBuilder::new(&mut buf, Vertex2DBuilder); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; @@ -342,7 +338,7 @@ impl Frame { if !buffer.indices.is_empty() { self.primitives.push(Primitive::Mesh2D { buffers: triangle::Mesh2D { - vertices: vertices_from(buffer.vertices), + vertices: buffer.vertices, indices: buffer.indices, }, size: self.size, @@ -355,12 +351,30 @@ impl Frame { } } -/// Converts from [`lyon::math::Point`] to [`Vertex2D`]. Used for generating primitives. -fn vertices_from(points: Vec) -> Vec { - points - .iter() - .map(|p| Vertex2D { - position: [p.x, p.y], - }) - .collect() +struct Vertex2DBuilder; + +impl tessellation::FillVertexConstructor for Vertex2DBuilder { + fn new_vertex( + &mut self, + vertex: tessellation::FillVertex<'_>, + ) -> Vertex2D { + let position = vertex.position(); + + Vertex2D { + position: [position.x, position.y], + } + } } + +impl tessellation::StrokeVertexConstructor for Vertex2DBuilder { + fn new_vertex( + &mut self, + vertex: tessellation::StrokeVertex<'_, '_>, + ) -> Vertex2D { + let position = vertex.position(); + + Vertex2D { + position: [position.x, position.y], + } + } +} \ No newline at end of file -- cgit From 9c7bf417ac9c1ac72bcc55aa3cd5e8eb962243a2 Mon Sep 17 00:00:00 2001 From: shan Date: Thu, 6 Oct 2022 16:57:38 -0700 Subject: Added support for gradients to respect current frame transform. --- graphics/src/widget/canvas/fill.rs | 14 ++++++++++---- graphics/src/widget/canvas/frame.rs | 32 ++++++++++++++++++++++---------- graphics/src/widget/canvas/stroke.rs | 14 ++++++++++---- 3 files changed, 42 insertions(+), 18 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 55cb3966..768c2b9c 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -3,6 +3,7 @@ use crate::gradient::Gradient; use crate::layer::mesh; use iced_native::Color; +use crate::widget::canvas::frame::Transform; /// The style used to fill geometry. #[derive(Debug, Clone)] @@ -50,11 +51,16 @@ pub enum Style<'a> { Gradient(&'a Gradient), } -impl<'a> Into for Style<'a> { - fn into(self) -> mesh::Style { +impl<'a> Style<'a> { + /// Converts a fill's [Style] to a [mesh::Style] for use in the renderer's shader. + pub(crate) fn as_mesh_style(&self, transform: &Transform) -> mesh::Style { match self { - Style::Solid(color) => mesh::Style::Solid(color), - Style::Gradient(gradient) => gradient.clone().into(), + Style::Solid(color) => { + mesh::Style::Solid(*color) + }, + Style::Gradient(gradient) => { + mesh::Style::Gradient((*gradient).clone().transform(transform)) + } } } } diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 427a2e2a..ccba840a 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -1,3 +1,4 @@ +use lyon::geom::euclid::Vector2D; use std::borrow::Cow; use iced_native::{Point, Rectangle, Size, Vector}; @@ -30,11 +31,22 @@ struct Transforms { } #[derive(Debug, Clone, Copy)] -struct Transform { +pub(crate) struct Transform { raw: lyon::math::Transform, is_identity: bool, } +impl Transform { + /// Transforms the given [Point] by the transformation matrix. + pub(crate) fn apply_to(&self, mut point: Point) -> Point { + let transformed = + self.raw.transform_vector(Vector2D::new(point.x, point.y)); + point.x = transformed.x + self.raw.m31; + point.y = transformed.y + self.raw.m32; + point + } +} + impl Frame { /// Creates a new empty [`Frame`] with the given dimensions. /// @@ -111,7 +123,8 @@ impl Frame { } .expect("Tessellate path."); - self.buffers.push((buf, style.into())) + self.buffers + .push((buf, style.as_mesh_style(&self.transforms.current))); } /// Draws an axis-aligned rectangle given its top-left corner coordinate and @@ -150,7 +163,8 @@ impl Frame { ) .expect("Fill rectangle"); - self.buffers.push((buf, style.into())) + self.buffers + .push((buf, style.as_mesh_style(&self.transforms.current))); } /// Draws the stroke of the given [`Path`] on the [`Frame`] with the @@ -192,7 +206,8 @@ impl Frame { } .expect("Stroke path"); - self.buffers.push((buf, stroke.style.into())) + self.buffers + .push((buf, stroke.style.as_mesh_style(&self.transforms.current))) } /// Draws the characters of the given [`Text`] on the [`Frame`], filling @@ -307,7 +322,7 @@ impl Frame { self.transforms.current.is_identity = false; } - /// Applies a rotation to the current transform of the [`Frame`]. + /// Applies a rotation in radians to the current transform of the [`Frame`]. #[inline] pub fn rotate(&mut self, angle: f32) { self.transforms.current.raw = self @@ -354,10 +369,7 @@ impl Frame { struct Vertex2DBuilder; impl tessellation::FillVertexConstructor for Vertex2DBuilder { - fn new_vertex( - &mut self, - vertex: tessellation::FillVertex<'_>, - ) -> Vertex2D { + fn new_vertex(&mut self, vertex: tessellation::FillVertex<'_>) -> Vertex2D { let position = vertex.position(); Vertex2D { @@ -377,4 +389,4 @@ impl tessellation::StrokeVertexConstructor for Vertex2DBuilder { position: [position.x, position.y], } } -} \ No newline at end of file +} diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index a19937ea..aaac15bb 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -3,6 +3,7 @@ use iced_native::Color; use crate::gradient::Gradient; use crate::layer::mesh; +use crate::widget::canvas::frame::Transform; /// The style of a stroke. #[derive(Debug, Clone)] @@ -68,11 +69,16 @@ pub enum Style<'a> { Gradient(&'a Gradient), } -impl <'a> Into for Style<'a> { - fn into(self) -> mesh::Style { +impl<'a> Style<'a> { + /// Converts a fill's [Style] to a [mesh::Style] for use in the renderer's shader. + pub(crate) fn as_mesh_style(&self, transform: &Transform) -> mesh::Style { match self { - Style::Solid(color) => mesh::Style::Solid(color), - Style::Gradient(gradient) => gradient.clone().into() + Style::Solid(color) => { + mesh::Style::Solid(*color) + }, + Style::Gradient(gradient) => { + mesh::Style::Gradient((*gradient).clone().transform(transform)) + } } } } -- cgit From f9a6efcaa03728f43aaa105af8936c1ed4778388 Mon Sep 17 00:00:00 2001 From: shan Date: Thu, 6 Oct 2022 19:41:00 -0700 Subject: Fixed some more imports/documentation. --- graphics/src/widget/canvas/fill.rs | 2 +- graphics/src/widget/canvas/stroke.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 768c2b9c..cd56aa9e 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -42,7 +42,7 @@ impl<'a> From for Fill<'a> { } } -/// The color or gradient of a [`Fill`]. +/// The style of a [`Fill`]. #[derive(Debug, Clone)] pub enum Style<'a> { /// A solid color diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index aaac15bb..5cb63a91 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,4 +1,4 @@ -//! Create lines from a [crate::widget::canvas::Path] and render with various attributes/styles. +//! Create lines from a [crate::widget::canvas::Path] and assigns them various attributes/styles. use iced_native::Color; use crate::gradient::Gradient; @@ -60,7 +60,7 @@ impl<'a> Default for Stroke<'a> { } } -/// The color or gradient of a [`Stroke`]. +/// The style of a [`Stroke`]. #[derive(Debug, Clone, Copy)] pub enum Style<'a> { /// A solid color -- cgit From 12a87c54eb68b992676060c80e518ffb29445cfc Mon Sep 17 00:00:00 2001 From: shan Date: Fri, 7 Oct 2022 11:41:50 -0700 Subject: Added support for relative positioning of gradient fills. Addressed some PR feedback. --- graphics/src/widget/canvas/frame.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index ccba840a..6dd0d06a 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -1,4 +1,4 @@ -use lyon::geom::euclid::Vector2D; +use lyon::geom::euclid::Point2D; use std::borrow::Cow; use iced_native::{Point, Rectangle, Size, Vector}; @@ -38,11 +38,11 @@ pub(crate) struct Transform { impl Transform { /// Transforms the given [Point] by the transformation matrix. - pub(crate) fn apply_to(&self, mut point: Point) -> Point { + pub(crate) fn transform_point(&self, mut point: Point) -> Point { let transformed = - self.raw.transform_vector(Vector2D::new(point.x, point.y)); - point.x = transformed.x + self.raw.m31; - point.y = transformed.y + self.raw.m32; + self.raw.transform_point(Point2D::new(point.x, point.y)); + point.x = transformed.x; + point.y = transformed.y; point } } -- cgit From a4a1262fa2d625be5ad7a37e409e0e9c399b09b6 Mon Sep 17 00:00:00 2001 From: shan Date: Fri, 7 Oct 2022 16:28:13 -0700 Subject: Fixed import issue with canvas in the gradient mod for situations where canvas feature is not enabled. --- graphics/src/widget/canvas/fill.rs | 15 ++++++++++++++- graphics/src/widget/canvas/frame.rs | 3 +-- graphics/src/widget/canvas/stroke.rs | 6 +++++- 3 files changed, 20 insertions(+), 4 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index cd56aa9e..477a7020 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -42,6 +42,15 @@ impl<'a> From for Fill<'a> { } } +impl<'a> Into> for &'a Gradient { + fn into(self) -> Fill<'a> { + Fill { + style: Style::Gradient(self), + ..Default::default() + } + } +} + /// The style of a [`Fill`]. #[derive(Debug, Clone)] pub enum Style<'a> { @@ -59,7 +68,11 @@ impl<'a> Style<'a> { mesh::Style::Solid(*color) }, Style::Gradient(gradient) => { - mesh::Style::Gradient((*gradient).clone().transform(transform)) + let mut gradient = (*gradient).clone(); + let coordinates = gradient.coords(); + transform.transform_point(coordinates.0); + transform.transform_point(coordinates.1); + mesh::Style::Gradient(gradient) } } } diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 6dd0d06a..d505cef7 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -38,12 +38,11 @@ pub(crate) struct Transform { impl Transform { /// Transforms the given [Point] by the transformation matrix. - pub(crate) fn transform_point(&self, mut point: Point) -> Point { + pub(crate) fn transform_point(&self, point: &mut Point) { let transformed = self.raw.transform_point(Point2D::new(point.x, point.y)); point.x = transformed.x; point.y = transformed.y; - point } } diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 5cb63a91..6e49f994 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -77,7 +77,11 @@ impl<'a> Style<'a> { mesh::Style::Solid(*color) }, Style::Gradient(gradient) => { - mesh::Style::Gradient((*gradient).clone().transform(transform)) + let mut gradient = (*gradient).clone(); + let coordinates = gradient.coords(); + transform.transform_point(coordinates.0); + transform.transform_point(coordinates.1); + mesh::Style::Gradient(gradient) } } } -- cgit From fd5e1e5ab0b712cd6719733b5a68602176ae5ec4 Mon Sep 17 00:00:00 2001 From: shan Date: Fri, 7 Oct 2022 16:55:55 -0700 Subject: Adjusted gradient transform function to be more readable. --- graphics/src/widget/canvas/fill.rs | 10 +++------- graphics/src/widget/canvas/frame.rs | 19 ++++++++++++++++--- graphics/src/widget/canvas/stroke.rs | 16 +++++----------- 3 files changed, 24 insertions(+), 21 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 477a7020..34cc5e50 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -67,13 +67,9 @@ impl<'a> Style<'a> { Style::Solid(color) => { mesh::Style::Solid(*color) }, - Style::Gradient(gradient) => { - let mut gradient = (*gradient).clone(); - let coordinates = gradient.coords(); - transform.transform_point(coordinates.0); - transform.transform_point(coordinates.1); - mesh::Style::Gradient(gradient) - } + Style::Gradient(gradient) => mesh::Style::Gradient( + transform.transform_gradient((*gradient).clone()), + ), } } } diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index d505cef7..1660fd6e 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -3,12 +3,13 @@ use std::borrow::Cow; use iced_native::{Point, Rectangle, Size, Vector}; +use crate::gradient::Gradient; +use crate::layer::mesh; use crate::triangle; +use crate::triangle::Vertex2D; use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; use crate::Primitive; -use crate::layer::mesh; -use crate::triangle::Vertex2D; use lyon::tessellation; /// The frame of a [`Canvas`]. @@ -38,12 +39,24 @@ pub(crate) struct Transform { impl Transform { /// Transforms the given [Point] by the transformation matrix. - pub(crate) fn transform_point(&self, point: &mut Point) { + fn transform_point(&self, point: &mut Point) { let transformed = self.raw.transform_point(Point2D::new(point.x, point.y)); point.x = transformed.x; point.y = transformed.y; } + + pub(crate) fn transform_gradient( + &self, + mut gradient: Gradient, + ) -> Gradient { + let coords = match &mut gradient { + Gradient::Linear(linear) => (&mut linear.start, &mut linear.end), + }; + self.transform_point(coords.0); + self.transform_point(coords.1); + gradient + } } impl Frame { diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 6e49f994..2f02a2f3 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,9 +1,9 @@ //! Create lines from a [crate::widget::canvas::Path] and assigns them various attributes/styles. -use iced_native::Color; use crate::gradient::Gradient; use crate::layer::mesh; use crate::widget::canvas::frame::Transform; +use iced_native::Color; /// The style of a stroke. #[derive(Debug, Clone)] @@ -73,16 +73,10 @@ impl<'a> Style<'a> { /// Converts a fill's [Style] to a [mesh::Style] for use in the renderer's shader. pub(crate) fn as_mesh_style(&self, transform: &Transform) -> mesh::Style { match self { - Style::Solid(color) => { - mesh::Style::Solid(*color) - }, - Style::Gradient(gradient) => { - let mut gradient = (*gradient).clone(); - let coordinates = gradient.coords(); - transform.transform_point(coordinates.0); - transform.transform_point(coordinates.1); - mesh::Style::Gradient(gradient) - } + Style::Solid(color) => mesh::Style::Solid(*color), + Style::Gradient(gradient) => mesh::Style::Gradient( + transform.transform_gradient((*gradient).clone()), + ), } } } -- cgit From 7a124476b1f609019bb0fdfa1254236af2d2a49e Mon Sep 17 00:00:00 2001 From: shan Date: Fri, 7 Oct 2022 16:57:29 -0700 Subject: Jumped the gun on pushing; one more readability update :P --- graphics/src/widget/canvas/frame.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 1660fd6e..3ba72179 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -50,11 +50,11 @@ impl Transform { &self, mut gradient: Gradient, ) -> Gradient { - let coords = match &mut gradient { + let (start, end) = match &mut gradient { Gradient::Linear(linear) => (&mut linear.start, &mut linear.end), }; - self.transform_point(coords.0); - self.transform_point(coords.1); + self.transform_point(start); + self.transform_point(end); gradient } } -- cgit From 20a0577034b40a6bbabee9bbbfc085f3fd5016c0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 3 Nov 2022 04:33:54 +0100 Subject: Reuse last buffer in `Frame` if `mesh_style` matches --- graphics/src/widget/canvas/fill.rs | 6 +-- graphics/src/widget/canvas/frame.rs | 74 ++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 33 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 34cc5e50..9582bf27 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -2,8 +2,8 @@ use crate::gradient::Gradient; use crate::layer::mesh; -use iced_native::Color; use crate::widget::canvas::frame::Transform; +use iced_native::Color; /// The style used to fill geometry. #[derive(Debug, Clone)] @@ -64,9 +64,7 @@ impl<'a> Style<'a> { /// Converts a fill's [Style] to a [mesh::Style] for use in the renderer's shader. pub(crate) fn as_mesh_style(&self, transform: &Transform) -> mesh::Style { match self { - Style::Solid(color) => { - mesh::Style::Solid(*color) - }, + Style::Solid(color) => mesh::Style::Solid(*color), Style::Gradient(gradient) => mesh::Style::Gradient( transform.transform_gradient((*gradient).clone()), ), diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 3ba72179..677d7bc5 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -18,13 +18,41 @@ use lyon::tessellation; #[allow(missing_debug_implementations)] pub struct Frame { size: Size, - buffers: Vec<(tessellation::VertexBuffers, mesh::Style)>, + buffers: BufferStack, primitives: Vec, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, stroke_tessellator: tessellation::StrokeTessellator, } +struct BufferStack { + stack: Vec<(tessellation::VertexBuffers, mesh::Style)>, +} + +impl BufferStack { + fn new() -> Self { + Self { stack: Vec::new() } + } + + fn get( + &mut self, + mesh_style: mesh::Style, + ) -> tessellation::BuffersBuilder<'_, Vertex2D, u32, Vertex2DBuilder> { + match self.stack.last_mut() { + Some((_, current_style)) if current_style == &mesh_style => {} + _ => { + self.stack + .push((tessellation::VertexBuffers::new(), mesh_style)); + } + }; + + tessellation::BuffersBuilder::new( + &mut self.stack.last_mut().unwrap().0, + Vertex2DBuilder, + ) + } +} + #[derive(Debug)] struct Transforms { previous: Vec, @@ -67,7 +95,7 @@ impl Frame { pub fn new(size: Size) -> Frame { Frame { size, - buffers: Vec::new(), + buffers: BufferStack::new(), primitives: Vec::new(), transforms: Transforms { previous: Vec::new(), @@ -110,10 +138,9 @@ impl Frame { pub fn fill<'a>(&mut self, path: &Path, fill: impl Into>) { let Fill { style, rule } = fill.into(); - let mut buf = tessellation::VertexBuffers::new(); - - let mut buffers = - tessellation::BuffersBuilder::new(&mut buf, Vertex2DBuilder); + let mut buffer = self + .buffers + .get(style.as_mesh_style(&self.transforms.current)); let options = tessellation::FillOptions::default().with_fill_rule(rule.into()); @@ -122,7 +149,7 @@ impl Frame { self.fill_tessellator.tessellate_path( path.raw(), &options, - &mut buffers, + &mut buffer, ) } else { let path = path.transformed(&self.transforms.current.raw); @@ -130,13 +157,10 @@ impl Frame { self.fill_tessellator.tessellate_path( path.raw(), &options, - &mut buffers, + &mut buffer, ) } .expect("Tessellate path."); - - self.buffers - .push((buf, style.as_mesh_style(&self.transforms.current))); } /// Draws an axis-aligned rectangle given its top-left corner coordinate and @@ -149,10 +173,9 @@ impl Frame { ) { let Fill { style, rule } = fill.into(); - let mut buf = tessellation::VertexBuffers::new(); - - let mut buffers = - tessellation::BuffersBuilder::new(&mut buf, Vertex2DBuilder); + let mut buffer = self + .buffers + .get(style.as_mesh_style(&self.transforms.current)); let top_left = self.transforms.current.raw.transform_point( @@ -171,12 +194,9 @@ impl Frame { .tessellate_rectangle( &lyon::math::Box2D::new(top_left, top_left + size), &options, - &mut buffers, + &mut buffer, ) .expect("Fill rectangle"); - - self.buffers - .push((buf, style.as_mesh_style(&self.transforms.current))); } /// Draws the stroke of the given [`Path`] on the [`Frame`] with the @@ -184,10 +204,9 @@ impl Frame { pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>) { let stroke = stroke.into(); - let mut buf = tessellation::VertexBuffers::new(); - - let mut buffers = - tessellation::BuffersBuilder::new(&mut buf, Vertex2DBuilder); + let mut buffer = self + .buffers + .get(stroke.style.as_mesh_style(&self.transforms.current)); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; @@ -205,7 +224,7 @@ impl Frame { self.stroke_tessellator.tessellate_path( path.raw(), &options, - &mut buffers, + &mut buffer, ) } else { let path = path.transformed(&self.transforms.current.raw); @@ -213,13 +232,10 @@ impl Frame { self.stroke_tessellator.tessellate_path( path.raw(), &options, - &mut buffers, + &mut buffer, ) } .expect("Stroke path"); - - self.buffers - .push((buf, stroke.style.as_mesh_style(&self.transforms.current))) } /// Draws the characters of the given [`Text`] on the [`Frame`], filling @@ -361,7 +377,7 @@ impl Frame { } fn into_primitives(mut self) -> Vec { - for (buffer, style) in self.buffers { + for (buffer, style) in self.buffers.stack { if !buffer.indices.is_empty() { self.primitives.push(Primitive::Mesh2D { buffers: triangle::Mesh2D { -- cgit From 7e22e2d45293c5916812be03dc7367134b69b3ad Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 3 Nov 2022 04:53:27 +0100 Subject: Fix lints by `clippy` --- graphics/src/widget/canvas/fill.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 9582bf27..2be8ed41 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -42,10 +42,10 @@ impl<'a> From for Fill<'a> { } } -impl<'a> Into> for &'a Gradient { - fn into(self) -> Fill<'a> { +impl<'a> From<&'a Gradient> for Fill<'a> { + fn from(gradient: &'a Gradient) -> Self { Fill { - style: Style::Gradient(self), + style: Style::Gradient(gradient), ..Default::default() } } -- cgit From 84d1b79fefc88534835fdfbe79bc0eb3b43627cf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 3 Nov 2022 05:50:53 +0100 Subject: Move `mesh::Style` to `triangle` and reuse it in `fill` and `stroke` --- graphics/src/widget/canvas/fill.rs | 45 +++++++++--------------------------- graphics/src/widget/canvas/frame.rs | 26 +++++++++++++-------- graphics/src/widget/canvas/stroke.rs | 27 ++-------------------- 3 files changed, 30 insertions(+), 68 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 2be8ed41..c69fc0d7 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -1,17 +1,15 @@ //! Fill [crate::widget::canvas::Geometry] with a certain style. +use crate::{Color, Gradient}; -use crate::gradient::Gradient; -use crate::layer::mesh; -use crate::widget::canvas::frame::Transform; -use iced_native::Color; +pub use crate::triangle::Style; /// The style used to fill geometry. #[derive(Debug, Clone)] -pub struct Fill<'a> { +pub struct Fill { /// The color or gradient of the fill. /// /// By default, it is set to [`FillStyle::Solid`] `BLACK`. - pub style: Style<'a>, + pub style: Style, /// The fill rule defines how to determine what is inside and what is /// outside of a shape. @@ -24,17 +22,17 @@ pub struct Fill<'a> { pub rule: FillRule, } -impl<'a> Default for Fill<'a> { - fn default() -> Fill<'a> { - Fill { +impl Default for Fill { + fn default() -> Self { + Self { style: Style::Solid(Color::BLACK), rule: FillRule::NonZero, } } } -impl<'a> From for Fill<'a> { - fn from(color: Color) -> Fill<'a> { +impl From for Fill { + fn from(color: Color) -> Fill { Fill { style: Style::Solid(color), ..Fill::default() @@ -42,8 +40,8 @@ impl<'a> From for Fill<'a> { } } -impl<'a> From<&'a Gradient> for Fill<'a> { - fn from(gradient: &'a Gradient) -> Self { +impl From for Fill { + fn from(gradient: Gradient) -> Self { Fill { style: Style::Gradient(gradient), ..Default::default() @@ -51,27 +49,6 @@ impl<'a> From<&'a Gradient> for Fill<'a> { } } -/// The style of a [`Fill`]. -#[derive(Debug, Clone)] -pub enum Style<'a> { - /// A solid color - Solid(Color), - /// A color gradient - Gradient(&'a Gradient), -} - -impl<'a> Style<'a> { - /// Converts a fill's [Style] to a [mesh::Style] for use in the renderer's shader. - pub(crate) fn as_mesh_style(&self, transform: &Transform) -> mesh::Style { - match self { - Style::Solid(color) => mesh::Style::Solid(*color), - Style::Gradient(gradient) => mesh::Style::Gradient( - transform.transform_gradient((*gradient).clone()), - ), - } - } -} - /// The fill rule defines how to determine what is inside and what is outside of /// a shape. /// diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 677d7bc5..ed0578b9 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -4,7 +4,6 @@ use std::borrow::Cow; use iced_native::{Point, Rectangle, Size, Vector}; use crate::gradient::Gradient; -use crate::layer::mesh; use crate::triangle; use crate::triangle::Vertex2D; use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; @@ -26,7 +25,7 @@ pub struct Frame { } struct BufferStack { - stack: Vec<(tessellation::VertexBuffers, mesh::Style)>, + stack: Vec<(tessellation::VertexBuffers, triangle::Style)>, } impl BufferStack { @@ -36,7 +35,7 @@ impl BufferStack { fn get( &mut self, - mesh_style: mesh::Style, + mesh_style: triangle::Style, ) -> tessellation::BuffersBuilder<'_, Vertex2D, u32, Vertex2DBuilder> { match self.stack.last_mut() { Some((_, current_style)) if current_style == &mesh_style => {} @@ -74,6 +73,15 @@ impl Transform { point.y = transformed.y; } + fn transform_style(&self, style: triangle::Style) -> triangle::Style { + match style { + triangle::Style::Solid(color) => triangle::Style::Solid(color), + triangle::Style::Gradient(gradient) => { + triangle::Style::Gradient(self.transform_gradient(gradient)) + } + } + } + pub(crate) fn transform_gradient( &self, mut gradient: Gradient, @@ -135,12 +143,12 @@ impl Frame { /// Draws the given [`Path`] on the [`Frame`] by filling it with the /// provided style. - pub fn fill<'a>(&mut self, path: &Path, fill: impl Into>) { + pub fn fill(&mut self, path: &Path, fill: impl Into) { let Fill { style, rule } = fill.into(); let mut buffer = self .buffers - .get(style.as_mesh_style(&self.transforms.current)); + .get(self.transforms.current.transform_style(style)); let options = tessellation::FillOptions::default().with_fill_rule(rule.into()); @@ -165,17 +173,17 @@ impl Frame { /// Draws an axis-aligned rectangle given its top-left corner coordinate and /// its `Size` on the [`Frame`] by filling it with the provided style. - pub fn fill_rectangle<'a>( + pub fn fill_rectangle( &mut self, top_left: Point, size: Size, - fill: impl Into>, + fill: impl Into, ) { let Fill { style, rule } = fill.into(); let mut buffer = self .buffers - .get(style.as_mesh_style(&self.transforms.current)); + .get(self.transforms.current.transform_style(style)); let top_left = self.transforms.current.raw.transform_point( @@ -206,7 +214,7 @@ impl Frame { let mut buffer = self .buffers - .get(stroke.style.as_mesh_style(&self.transforms.current)); + .get(self.transforms.current.transform_style(stroke.style)); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 2f02a2f3..f9b8e447 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,8 +1,6 @@ //! Create lines from a [crate::widget::canvas::Path] and assigns them various attributes/styles. +pub use crate::triangle::Style; -use crate::gradient::Gradient; -use crate::layer::mesh; -use crate::widget::canvas::frame::Transform; use iced_native::Color; /// The style of a stroke. @@ -11,7 +9,7 @@ pub struct Stroke<'a> { /// The color or gradient of the stroke. /// /// By default, it is set to [`StrokeStyle::Solid`] `BLACK`. - pub style: Style<'a>, + pub style: Style, /// The distance between the two edges of the stroke. pub width: f32, /// The shape to be used at the end of open subpaths when they are stroked. @@ -60,27 +58,6 @@ impl<'a> Default for Stroke<'a> { } } -/// The style of a [`Stroke`]. -#[derive(Debug, Clone, Copy)] -pub enum Style<'a> { - /// A solid color - Solid(Color), - /// A color gradient - Gradient(&'a Gradient), -} - -impl<'a> Style<'a> { - /// Converts a fill's [Style] to a [mesh::Style] for use in the renderer's shader. - pub(crate) fn as_mesh_style(&self, transform: &Transform) -> mesh::Style { - match self { - Style::Solid(color) => mesh::Style::Solid(*color), - Style::Gradient(gradient) => mesh::Style::Gradient( - transform.transform_gradient((*gradient).clone()), - ), - } - } -} - /// The shape used at the end of open subpaths when they are stroked. #[derive(Debug, Clone, Copy)] pub enum LineCap { -- cgit From 4ef778aff3aaba7e7732fb395c034382a4a475a2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 3 Nov 2022 06:01:34 +0100 Subject: Refactor imports of `widget::canvas::frame` in `iced_graphics` --- graphics/src/widget/canvas/frame.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index ed0578b9..ce36bc53 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -1,15 +1,14 @@ -use lyon::geom::euclid::Point2D; -use std::borrow::Cow; - -use iced_native::{Point, Rectangle, Size, Vector}; - use crate::gradient::Gradient; use crate::triangle; use crate::triangle::Vertex2D; use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; use crate::Primitive; +use iced_native::{Point, Rectangle, Size, Vector}; + +use lyon::geom::euclid; use lyon::tessellation; +use std::borrow::Cow; /// The frame of a [`Canvas`]. /// @@ -67,8 +66,9 @@ pub(crate) struct Transform { impl Transform { /// Transforms the given [Point] by the transformation matrix. fn transform_point(&self, point: &mut Point) { - let transformed = - self.raw.transform_point(Point2D::new(point.x, point.y)); + let transformed = self + .raw + .transform_point(euclid::Point2D::new(point.x, point.y)); point.x = transformed.x; point.y = transformed.y; } -- cgit From 68cddd64687b8df63306631c062048d6d2fffbd1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 3 Nov 2022 06:02:11 +0100 Subject: Remove unnecessary `pub(crate)` in `widget::canvas::frame` --- graphics/src/widget/canvas/frame.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'graphics/src/widget/canvas') diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index ce36bc53..cf6c6928 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -58,7 +58,7 @@ struct Transforms { } #[derive(Debug, Clone, Copy)] -pub(crate) struct Transform { +struct Transform { raw: lyon::math::Transform, is_identity: bool, } @@ -82,10 +82,7 @@ impl Transform { } } - pub(crate) fn transform_gradient( - &self, - mut gradient: Gradient, - ) -> Gradient { + fn transform_gradient(&self, mut gradient: Gradient) -> Gradient { let (start, end) = match &mut gradient { Gradient::Linear(linear) => (&mut linear.start, &mut linear.end), }; -- cgit