Merge pull request #293 from hecrj/improvement/canvas-ergonomics
Improve `Canvas` ergonomics
This commit is contained in:
commit
99352f02fd
@ -95,68 +95,57 @@ impl From<chrono::DateTime<chrono::Local>> 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();
|
||||
|
||||
path.line_to(Point::new(x, y) + offset);
|
||||
}
|
||||
|
||||
let hour_and_minute_hands = canvas::Path::new(|path| {
|
||||
path.move_to(center);
|
||||
draw_hand(self.hour, 12, 0.5 * radius, offset, path);
|
||||
|
||||
path.move_to(center);
|
||||
draw_hand(self.minute, 60, 0.8 * radius, offset, path)
|
||||
});
|
||||
|
||||
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.stroke(
|
||||
&second_hand,
|
||||
canvas::Stroke {
|
||||
let thin_stroke = canvas::Stroke {
|
||||
width: radius / 100.0,
|
||||
color: Color::WHITE,
|
||||
line_cap: canvas::LineCap::Round,
|
||||
..canvas::Stroke::default()
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
let wide_stroke = canvas::Stroke {
|
||||
width: thin_stroke.width * 3.0,
|
||||
..thin_stroke
|
||||
};
|
||||
|
||||
frame.translate(Vector::new(center.x, center.y));
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.rotate(hand_rotation(self.hour, 12));
|
||||
frame.stroke(&short_hand, wide_stroke);
|
||||
});
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.rotate(hand_rotation(self.minute, 60));
|
||||
frame.stroke(&long_hand, wide_stroke);
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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 shadow = Path::new(|path| {
|
||||
path.rectangle(
|
||||
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,
|
||||
),
|
||||
)
|
||||
});
|
||||
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
|
||||
}),
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -12,3 +12,9 @@ impl Default for Fill {
|
||||
Fill::Color(Color::BLACK)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Fill {
|
||||
fn from(color: Color) -> Fill {
|
||||
Fill::Color(color)
|
||||
}
|
||||
}
|
||||
|
@ -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<Fill>) {
|
||||
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<Stroke>) {
|
||||
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<Text>) {
|
||||
use std::f32;
|
||||
|
||||
let text = text.into();
|
||||
|
||||
let position = if self.transforms.current.is_identity {
|
||||
text.position
|
||||
} else {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -32,3 +32,18 @@ impl Default for Text {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> 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()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user