diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index a85a964c..827379fa 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -95,68 +95,57 @@ impl From> for LocalTime { impl canvas::Drawable for LocalTime { fn draw(&self, frame: &mut canvas::Frame) { + use canvas::Path; + let center = frame.center(); let radius = frame.width().min(frame.height()) / 2.0; - let offset = Vector::new(center.x, center.y); - let clock = canvas::Path::new(|path| path.circle(center, radius)); + let clock = Path::circle(center, radius); + frame.fill(&clock, Color::from_rgb8(0x12, 0x93, 0xD8)); - frame.fill( - &clock, - canvas::Fill::Color(Color::from_rgb8(0x12, 0x93, 0xD8)), - ); + let short_hand = + Path::line(Point::ORIGIN, Point::new(0.0, -0.5 * radius)); - fn draw_hand( - n: u32, - total: u32, - length: f32, - offset: Vector, - path: &mut canvas::path::Builder, - ) { - let turns = n as f32 / total as f32; - let t = 2.0 * std::f32::consts::PI * (turns - 0.25); + let long_hand = + Path::line(Point::ORIGIN, Point::new(0.0, -0.8 * radius)); - let x = length * t.cos(); - let y = length * t.sin(); + let thin_stroke = canvas::Stroke { + width: radius / 100.0, + color: Color::WHITE, + line_cap: canvas::LineCap::Round, + ..canvas::Stroke::default() + }; - path.line_to(Point::new(x, y) + offset); - } + let wide_stroke = canvas::Stroke { + width: thin_stroke.width * 3.0, + ..thin_stroke + }; - let hour_and_minute_hands = canvas::Path::new(|path| { - path.move_to(center); - draw_hand(self.hour, 12, 0.5 * radius, offset, path); + frame.translate(Vector::new(center.x, center.y)); - path.move_to(center); - draw_hand(self.minute, 60, 0.8 * radius, offset, path) + frame.with_save(|frame| { + frame.rotate(hand_rotation(self.hour, 12)); + frame.stroke(&short_hand, wide_stroke); }); - frame.stroke( - &hour_and_minute_hands, - canvas::Stroke { - width: radius / 100.0 * 3.0, - color: Color::WHITE, - line_cap: canvas::LineCap::Round, - ..canvas::Stroke::default() - }, - ); - - let second_hand = canvas::Path::new(|path| { - path.move_to(center); - draw_hand(self.second, 60, 0.8 * radius, offset, path) + frame.with_save(|frame| { + frame.rotate(hand_rotation(self.minute, 60)); + frame.stroke(&long_hand, wide_stroke); }); - frame.stroke( - &second_hand, - canvas::Stroke { - width: radius / 100.0, - color: Color::WHITE, - line_cap: canvas::LineCap::Round, - ..canvas::Stroke::default() - }, - ); + frame.with_save(|frame| { + frame.rotate(hand_rotation(self.second, 60)); + frame.stroke(&long_hand, thin_stroke); + }); } } +fn hand_rotation(n: u32, total: u32) -> f32 { + let turns = n as f32 / total as f32; + + 2.0 * std::f32::consts::PI * turns +} + mod time { use iced::futures; diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 963f047b..bcd1dc71 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -128,14 +128,12 @@ impl State { impl canvas::Drawable for State { fn draw(&self, frame: &mut canvas::Frame) { - use canvas::{Fill, Path, Stroke}; + use canvas::{Path, Stroke}; use std::f32::consts::PI; let center = frame.center(); - let space = Path::new(|path| { - path.rectangle(Point::new(0.0, 0.0), frame.size()) - }); + let space = Path::rectangle(Point::new(0.0, 0.0), frame.size()); let stars = Path::new(|path| { for (p, size) in &self.stars { @@ -143,12 +141,12 @@ impl canvas::Drawable for State { } }); - let sun = Path::new(|path| path.circle(center, Self::SUN_RADIUS)); - let orbit = Path::new(|path| path.circle(center, Self::ORBIT_RADIUS)); + let sun = Path::circle(center, Self::SUN_RADIUS); + let orbit = Path::circle(center, Self::ORBIT_RADIUS); - frame.fill(&space, Fill::Color(Color::BLACK)); - frame.fill(&stars, Fill::Color(Color::WHITE)); - frame.fill(&sun, Fill::Color(Color::from_rgb8(0xF9, 0xD7, 0x1C))); + frame.fill(&space, Color::BLACK); + frame.fill(&stars, Color::WHITE); + frame.fill(&sun, Color::from_rgb8(0xF9, 0xD7, 0x1C)); frame.stroke( &orbit, Stroke { @@ -170,21 +168,13 @@ impl canvas::Drawable for State { ); frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0)); - let earth = Path::new(|path| { - path.circle(Point::ORIGIN, Self::EARTH_RADIUS) - }); + let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS); + let shadow = Path::rectangle( + Point::new(0.0, -Self::EARTH_RADIUS), + Size::new(Self::EARTH_RADIUS * 4.0, Self::EARTH_RADIUS * 2.0), + ); - let shadow = Path::new(|path| { - path.rectangle( - Point::new(0.0, -Self::EARTH_RADIUS), - Size::new( - Self::EARTH_RADIUS * 4.0, - Self::EARTH_RADIUS * 2.0, - ), - ) - }); - - frame.fill(&earth, Fill::Color(Color::from_rgb8(0x6B, 0x93, 0xD6))); + frame.fill(&earth, Color::from_rgb8(0x6B, 0x93, 0xD6)); frame.with_save(|frame| { frame.rotate( @@ -193,19 +183,16 @@ impl canvas::Drawable for State { ); frame.translate(Vector::new(0.0, Self::MOON_DISTANCE)); - let moon = Path::new(|path| { - path.circle(Point::ORIGIN, Self::MOON_RADIUS) - }); - - frame.fill(&moon, Fill::Color(Color::WHITE)); + let moon = Path::circle(Point::ORIGIN, Self::MOON_RADIUS); + frame.fill(&moon, Color::WHITE); }); frame.fill( &shadow, - Fill::Color(Color { + Color { a: 0.7, ..Color::BLACK - }), + }, ); }); } diff --git a/wgpu/src/widget/canvas/fill.rs b/wgpu/src/widget/canvas/fill.rs index 5ce24cf3..a2010e45 100644 --- a/wgpu/src/widget/canvas/fill.rs +++ b/wgpu/src/widget/canvas/fill.rs @@ -12,3 +12,9 @@ impl Default for Fill { Fill::Color(Color::BLACK) } } + +impl From for Fill { + fn from(color: Color) -> Fill { + Fill::Color(color) + } +} diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index 940be402..de4717f1 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -89,14 +89,14 @@ impl Frame { /// /// [`Path`]: path/struct.Path.html /// [`Frame`]: struct.Frame.html - pub fn fill(&mut self, path: &Path, fill: Fill) { + pub fn fill(&mut self, path: &Path, fill: impl Into) { use lyon::tessellation::{ BuffersBuilder, FillOptions, FillTessellator, }; let mut buffers = BuffersBuilder::new( &mut self.buffers, - FillVertex(match fill { + FillVertex(match fill.into() { Fill::Color(color) => color.into_linear(), }), ); @@ -127,11 +127,13 @@ impl Frame { /// /// [`Path`]: path/struct.Path.html /// [`Frame`]: struct.Frame.html - pub fn stroke(&mut self, path: &Path, stroke: Stroke) { + pub fn stroke(&mut self, path: &Path, stroke: impl Into) { use lyon::tessellation::{ BuffersBuilder, StrokeOptions, StrokeTessellator, }; + let stroke = stroke.into(); + let mut buffers = BuffersBuilder::new( &mut self.buffers, StrokeVertex(stroke.color.into_linear()), @@ -173,9 +175,11 @@ impl Frame { /// [`Text`]: struct.Text.html /// [`Frame`]: struct.Frame.html /// [`Canvas`]: struct.Canvas.html - pub fn fill_text(&mut self, text: Text) { + pub fn fill_text(&mut self, text: impl Into) { use std::f32; + let text = text.into(); + let position = if self.transforms.current.is_identity { text.position } else { diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index e7ff47f3..c26bf187 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -7,6 +7,8 @@ mod builder; pub use arc::Arc; pub use builder::Builder; +use iced_native::{Point, Size}; + /// An immutable set of points that may or may not be connected. /// /// A single [`Path`] can represent different kinds of 2D shapes! @@ -33,6 +35,33 @@ impl Path { builder.build() } + /// Creates a new [`Path`] representing a line segment given its starting + /// and end points. + /// + /// [`Path`]: struct.Path.html + pub fn line(from: Point, to: Point) -> Self { + Self::new(|p| { + p.move_to(from); + p.line_to(to); + }) + } + + /// Creates a new [`Path`] representing a rectangle given its top-left + /// corner coordinate and its `Size`. + /// + /// [`Path`]: struct.Path.html + pub fn rectangle(top_left: Point, size: Size) -> Self { + Self::new(|p| p.rectangle(top_left, size)) + } + + /// Creates a new [`Path`] representing a circle given its center + /// coordinate and its radius. + /// + /// [`Path`]: struct.Path.html + pub fn circle(center: Point, radius: f32) -> Self { + Self::new(|p| p.circle(center, radius)) + } + #[inline] pub(crate) fn raw(&self) -> &lyon::path::Path { &self.raw diff --git a/wgpu/src/widget/canvas/path/builder.rs b/wgpu/src/widget/canvas/path/builder.rs index a013149e..6511fa52 100644 --- a/wgpu/src/widget/canvas/path/builder.rs +++ b/wgpu/src/widget/canvas/path/builder.rs @@ -133,11 +133,14 @@ impl Builder { /// /// [`Path`]: struct.Path.html #[inline] - pub fn rectangle(&mut self, p: Point, size: Size) { - self.move_to(p); - self.line_to(Point::new(p.x + size.width, p.y)); - self.line_to(Point::new(p.x + size.width, p.y + size.height)); - self.line_to(Point::new(p.x, p.y + size.height)); + pub fn rectangle(&mut self, top_left: Point, size: Size) { + self.move_to(top_left); + self.line_to(Point::new(top_left.x + size.width, top_left.y)); + self.line_to(Point::new( + top_left.x + size.width, + top_left.y + size.height, + )); + self.line_to(Point::new(top_left.x, top_left.y + size.height)); self.close(); } diff --git a/wgpu/src/widget/canvas/text.rs b/wgpu/src/widget/canvas/text.rs index d1cf1a0f..c4cae30e 100644 --- a/wgpu/src/widget/canvas/text.rs +++ b/wgpu/src/widget/canvas/text.rs @@ -32,3 +32,18 @@ impl Default for Text { } } } + +impl From for Text { + fn from(content: String) -> Text { + Text { + content, + ..Default::default() + } + } +} + +impl From<&str> for Text { + fn from(content: &str) -> Text { + String::from(content).into() + } +}