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/frame.rs | 113 +++++++++++++----------------------- 1 file changed, 40 insertions(+), 73 deletions(-) (limited to 'graphics/src/widget/canvas/frame.rs') 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, - } - } -} -- 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/frame.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'graphics/src/widget/canvas/frame.rs') 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 -- 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/frame.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'graphics/src/widget/canvas/frame.rs') 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() +} -- 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/frame.rs') 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/frame.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'graphics/src/widget/canvas/frame.rs') 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 +} -- 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/frame.rs') 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/frame.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'graphics/src/widget/canvas/frame.rs') 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 } } -- 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/frame.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'graphics/src/widget/canvas/frame.rs') 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 { -- 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/frame.rs') 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/frame.rs | 74 ++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 29 deletions(-) (limited to 'graphics/src/widget/canvas/frame.rs') 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 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/frame.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'graphics/src/widget/canvas/frame.rs') 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; -- 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/frame.rs') 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/frame.rs') 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 From 365f37a3ae10e7aff407b84050f77da10820866e Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Thu, 10 Nov 2022 14:43:38 -0800 Subject: Added conditional configurations for WASM target for gradients & storage buffers, since storage buffers are not supported on wgpu WASM target at the moment. --- graphics/src/widget/canvas/frame.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'graphics/src/widget/canvas/frame.rs') diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index cf6c6928..a7b88502 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -76,6 +76,7 @@ impl Transform { fn transform_style(&self, style: triangle::Style) -> triangle::Style { match style { triangle::Style::Solid(color) => triangle::Style::Solid(color), + #[cfg(not(target_arch = "wasm32"))] triangle::Style::Gradient(gradient) => { triangle::Style::Gradient(self.transform_gradient(gradient)) } -- cgit From 33c3c0c0aa774bb7462e3c42aa04c591a66376a7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 14 Nov 2022 00:02:42 +0100 Subject: Group all solid triangles independently of color --- graphics/src/widget/canvas/frame.rs | 196 +++++++++++++++++++++++++++--------- 1 file changed, 150 insertions(+), 46 deletions(-) (limited to 'graphics/src/widget/canvas/frame.rs') diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index a7b88502..d68548ae 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -1,7 +1,6 @@ use crate::gradient::Gradient; use crate::triangle; -use crate::triangle::Vertex2D; -use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; +use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Style, Text}; use crate::Primitive; use iced_native::{Point, Rectangle, Size, Vector}; @@ -23,8 +22,16 @@ pub struct Frame { stroke_tessellator: tessellation::StrokeTessellator, } +enum Buffer { + Solid(tessellation::VertexBuffers), + Gradient( + tessellation::VertexBuffers, + Gradient, + ), +} + struct BufferStack { - stack: Vec<(tessellation::VertexBuffers, triangle::Style)>, + stack: Vec, } impl BufferStack { @@ -32,22 +39,64 @@ impl BufferStack { Self { stack: Vec::new() } } - fn get( - &mut self, - mesh_style: triangle::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)); + fn get_mut(&mut self, style: &Style) -> &mut Buffer { + match style { + Style::Solid(_) => match self.stack.last() { + Some(Buffer::Solid(_)) => {} + _ => { + self.stack.push(Buffer::Solid( + tessellation::VertexBuffers::new(), + )); + } + }, + Style::Gradient(gradient) => match self.stack.last() { + Some(Buffer::Gradient(_, last)) if gradient == last => {} + _ => { + self.stack.push(Buffer::Gradient( + tessellation::VertexBuffers::new(), + gradient.clone(), + )); + } + }, + } + + self.stack.last_mut().unwrap() + } + + fn get_fill<'a>( + &'a mut self, + style: &Style, + ) -> Box { + match (style, self.get_mut(style)) { + (Style::Solid(color), Buffer::Solid(buffer)) => { + Box::new(tessellation::BuffersBuilder::new( + buffer, + TriangleVertex2DBuilder(color.into_linear()), + )) } - }; + (Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new( + tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder), + ), + _ => unreachable!(), + } + } - tessellation::BuffersBuilder::new( - &mut self.stack.last_mut().unwrap().0, - Vertex2DBuilder, - ) + fn get_stroke<'a>( + &'a mut self, + style: &Style, + ) -> Box { + match (style, self.get_mut(style)) { + (Style::Solid(color), Buffer::Solid(buffer)) => { + Box::new(tessellation::BuffersBuilder::new( + buffer, + TriangleVertex2DBuilder(color.into_linear()), + )) + } + (Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new( + tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder), + ), + _ => unreachable!(), + } } } @@ -73,12 +122,11 @@ impl Transform { point.y = transformed.y; } - fn transform_style(&self, style: triangle::Style) -> triangle::Style { + fn transform_style(&self, style: Style) -> Style { match style { - triangle::Style::Solid(color) => triangle::Style::Solid(color), - #[cfg(not(target_arch = "wasm32"))] - triangle::Style::Gradient(gradient) => { - triangle::Style::Gradient(self.transform_gradient(gradient)) + Style::Solid(color) => Style::Solid(color), + Style::Gradient(gradient) => { + Style::Gradient(self.transform_gradient(gradient)) } } } @@ -146,7 +194,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(style)); + .get_fill(&self.transforms.current.transform_style(style)); let options = tessellation::FillOptions::default().with_fill_rule(rule.into()); @@ -155,7 +203,7 @@ impl Frame { self.fill_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } else { let path = path.transformed(&self.transforms.current.raw); @@ -163,7 +211,7 @@ impl Frame { self.fill_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } .expect("Tessellate path."); @@ -181,7 +229,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(style)); + .get_fill(&self.transforms.current.transform_style(style)); let top_left = self.transforms.current.raw.transform_point( @@ -200,7 +248,7 @@ impl Frame { .tessellate_rectangle( &lyon::math::Box2D::new(top_left, top_left + size), &options, - &mut buffer, + buffer.as_mut(), ) .expect("Fill rectangle"); } @@ -212,7 +260,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(stroke.style)); + .get_stroke(&self.transforms.current.transform_style(stroke.style)); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; @@ -230,7 +278,7 @@ impl Frame { self.stroke_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } else { let path = path.transformed(&self.transforms.current.raw); @@ -238,7 +286,7 @@ impl Frame { self.stroke_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } .expect("Stroke path"); @@ -383,16 +431,31 @@ impl Frame { } fn into_primitives(mut self) -> Vec { - for (buffer, style) in self.buffers.stack { - if !buffer.indices.is_empty() { - self.primitives.push(Primitive::Mesh2D { - buffers: triangle::Mesh2D { - vertices: buffer.vertices, - indices: buffer.indices, - }, - size: self.size, - style, - }) + for buffer in self.buffers.stack { + match buffer { + Buffer::Solid(buffer) => { + if !buffer.indices.is_empty() { + self.primitives.push(Primitive::SolidMesh { + buffers: triangle::Mesh2D { + vertices: buffer.vertices, + indices: buffer.indices, + }, + size: self.size, + }) + } + } + Buffer::Gradient(buffer, gradient) => { + if !buffer.indices.is_empty() { + self.primitives.push(Primitive::GradientMesh { + buffers: triangle::Mesh2D { + vertices: buffer.vertices, + indices: buffer.indices, + }, + size: self.size, + gradient, + }) + } + } } } @@ -402,25 +465,66 @@ impl Frame { struct Vertex2DBuilder; -impl tessellation::FillVertexConstructor for Vertex2DBuilder { - fn new_vertex(&mut self, vertex: tessellation::FillVertex<'_>) -> Vertex2D { +impl tessellation::FillVertexConstructor + for Vertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::FillVertex<'_>, + ) -> triangle::Vertex2D { + let position = vertex.position(); + + triangle::Vertex2D { + position: [position.x, position.y], + } + } +} + +impl tessellation::StrokeVertexConstructor + for Vertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::StrokeVertex<'_, '_>, + ) -> triangle::Vertex2D { + let position = vertex.position(); + + triangle::Vertex2D { + position: [position.x, position.y], + } + } +} + +struct TriangleVertex2DBuilder([f32; 4]); + +impl tessellation::FillVertexConstructor + for TriangleVertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::FillVertex<'_>, + ) -> triangle::ColoredVertex2D { let position = vertex.position(); - Vertex2D { + triangle::ColoredVertex2D { position: [position.x, position.y], + color: self.0, } } } -impl tessellation::StrokeVertexConstructor for Vertex2DBuilder { +impl tessellation::StrokeVertexConstructor + for TriangleVertex2DBuilder +{ fn new_vertex( &mut self, vertex: tessellation::StrokeVertex<'_, '_>, - ) -> Vertex2D { + ) -> triangle::ColoredVertex2D { let position = vertex.position(); - Vertex2D { + triangle::ColoredVertex2D { position: [position.x, position.y], + color: self.0, } } } -- cgit