Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canvas widget for 2D graphics #193

Merged
merged 28 commits into from
Feb 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8daf798
Add `canvas` feature to `iced_wgpu`
hecrj Feb 11, 2020
f436f20
Draft `Canvas` types and `clock` example
hecrj Feb 12, 2020
4777f5d
Remove unnecessary `Arc` from `clock` example
hecrj Feb 12, 2020
6409798
Expose `Point` in `iced_web`
hecrj Feb 12, 2020
74dd79e
Rename current `Path` to `path::Builder`
hecrj Feb 12, 2020
f34407b
Implement `Frame::fill` and `Frame::stroke`
hecrj Feb 12, 2020
578ea4a
Finish `clock` example
hecrj Feb 12, 2020
96b36d0
Increase line width in `clock` example
hecrj Feb 12, 2020
1beeaf9
Fix examples using `Mesh2D`
hecrj Feb 12, 2020
de8f06b
Split `Fill` and `Stroke` into their own modules
hecrj Feb 12, 2020
4a24392
Simplify `Clock::new` in `clock` example
hecrj Feb 12, 2020
6291535
Remove `canvas::Data` leftover
hecrj Feb 12, 2020
265c086
Fix circle `end_angle` in `clock` example
hecrj Feb 12, 2020
9dc9305
Remove redundant conversion in `clock` example
hecrj Feb 12, 2020
e7c400a
Improve naming in `clock` example
hecrj Feb 12, 2020
979edeb
Fix `clock` example eventually skipping a second
hecrj Feb 12, 2020
df90c47
Move `layer::Cached` to its own module
hecrj Feb 13, 2020
76df374
Implement additional methods in `path::Builder`
hecrj Feb 14, 2020
558abf6
Add transform stack to `canvas::Frame`
hecrj Feb 14, 2020
ad3a0a1
Add `solar_system` example
hecrj Feb 14, 2020
945dfab
Move `Size` to `iced_core`
hecrj Feb 14, 2020
f5c80a6
Upgrade `Mesh2D` indices from `u16` to `u32`
hecrj Feb 14, 2020
4969bfd
Merge branch 'master' into feature/canvas
hecrj Feb 14, 2020
dadae12
Implement MSAA for `triangle` pipeline in `iced_wgpu`
hecrj Feb 15, 2020
fe61d2f
Request high performance adapter if MSAA is enabled
hecrj Feb 15, 2020
570f769
Rename `Settings::antialiasing` to `use_antialiasing`
hecrj Feb 15, 2020
9c06756
Write documentation for new `canvas` module
hecrj Feb 18, 2020
6f7247c
Rename `Settings::use_antialiasing` to `antialiasing`
hecrj Feb 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ categories = ["gui"]
image = ["iced_wgpu/image"]
# Enables the `Svg` widget
svg = ["iced_wgpu/svg"]
# Enables the `Canvas` widget
canvas = ["iced_wgpu/canvas"]
# Enables a debug view in native platforms (press F12)
debug = ["iced_winit/debug"]
# Enables `tokio` as the `executor::Default` on native platforms
Expand All @@ -36,13 +38,15 @@ members = [
"wgpu",
"winit",
"examples/bezier_tool",
"examples/clock",
"examples/counter",
"examples/custom_widget",
"examples/events",
"examples/geometry",
"examples/integration",
"examples/pokedex",
"examples/progress_bar",
"examples/solar_system",
"examples/stopwatch",
"examples/styling",
"examples/svg",
Expand Down
9 changes: 8 additions & 1 deletion core/src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,18 @@ impl Color {
///
/// [`Color`]: struct.Color.html
pub fn from_rgb8(r: u8, g: u8, b: u8) -> Color {
Color::from_rgba8(r, g, b, 1.0)
}

/// Creates a [`Color`] from its RGB8 components and an alpha value.
///
/// [`Color`]: struct.Color.html
pub fn from_rgba8(r: u8, g: u8, b: u8, a: f32) -> Color {
Color {
r: f32::from(r) / 255.0,
g: f32::from(g) / 255.0,
b: f32::from(b) / 255.0,
a: 1.0,
a,
}
}

Expand Down
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod font;
mod length;
mod point;
mod rectangle;
mod size;
mod vector;

pub use align::{Align, HorizontalAlignment, VerticalAlignment};
Expand All @@ -31,4 +32,5 @@ pub use font::Font;
pub use length::Length;
pub use point::Point;
pub use rectangle::Rectangle;
pub use size::Size;
pub use vector::Vector;
7 changes: 6 additions & 1 deletion core/src/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ pub struct Point {
}

impl Point {
/// The origin (i.e. a [`Point`] with both X=0 and Y=0).
///
/// [`Point`]: struct.Point.html
pub const ORIGIN: Point = Point::new(0.0, 0.0);

/// Creates a new [`Point`] with the given coordinates.
///
/// [`Point`]: struct.Point.html
pub fn new(x: f32, y: f32) -> Self {
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
Expand Down
File renamed without changes.
59 changes: 26 additions & 33 deletions examples/bezier_tool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,15 @@ mod bezier {
layout: Layout<'_>,
cursor_position: Point,
) -> (Primitive, MouseCursor) {
let mut buffer: VertexBuffers<Vertex2D, u16> = VertexBuffers::new();
let mut buffer: VertexBuffers<Vertex2D, u32> = VertexBuffers::new();
let mut path_builder = lyon::path::Path::builder();

let bounds = layout.bounds();

// Draw rectangle border with lyon.
basic_shapes::stroke_rectangle(
&lyon::math::Rect::new(
lyon::math::Point::new(bounds.x + 0.5, bounds.y + 0.5),
lyon::math::Point::new(0.5, 0.5),
lyon::math::Size::new(
bounds.width - 1.0,
bounds.height - 1.0,
Expand All @@ -121,48 +121,35 @@ mod bezier {

for curve in self.curves {
path_builder.move_to(lyon::math::Point::new(
curve.from.x + bounds.x,
curve.from.y + bounds.y,
curve.from.x,
curve.from.y,
));

path_builder.quadratic_bezier_to(
lyon::math::Point::new(
curve.control.x + bounds.x,
curve.control.y + bounds.y,
),
lyon::math::Point::new(
curve.to.x + bounds.x,
curve.to.y + bounds.y,
),
lyon::math::Point::new(curve.control.x, curve.control.y),
lyon::math::Point::new(curve.to.x, curve.to.y),
);
}

match self.state.pending {
None => {}
Some(Pending::One { from }) => {
path_builder.move_to(lyon::math::Point::new(
from.x + bounds.x,
from.y + bounds.y,
));
path_builder
.move_to(lyon::math::Point::new(from.x, from.y));
path_builder.line_to(lyon::math::Point::new(
cursor_position.x,
cursor_position.y,
cursor_position.x - bounds.x,
cursor_position.y - bounds.y,
));
}
Some(Pending::Two { from, to }) => {
path_builder.move_to(lyon::math::Point::new(
from.x + bounds.x,
from.y + bounds.y,
));
path_builder
.move_to(lyon::math::Point::new(from.x, from.y));
path_builder.quadratic_bezier_to(
lyon::math::Point::new(
cursor_position.x,
cursor_position.y,
),
lyon::math::Point::new(
to.x + bounds.x,
to.y + bounds.y,
cursor_position.x - bounds.x,
cursor_position.y - bounds.y,
),
lyon::math::Point::new(to.x, to.y),
);
}
}
Expand All @@ -186,10 +173,13 @@ mod bezier {
)
.unwrap();

let mesh = Primitive::Mesh2D(Arc::new(Mesh2D {
vertices: buffer.vertices,
indices: buffer.indices,
}));
let mesh = Primitive::Mesh2D {
origin: Point::new(bounds.x, bounds.y),
buffers: Arc::new(Mesh2D {
vertices: buffer.vertices,
indices: buffer.indices,
}),
};

(
Primitive::Clip {
Expand Down Expand Up @@ -296,7 +286,10 @@ use iced::{
};

pub fn main() {
Example::run(Settings::default())
Example::run(Settings {
antialiasing: true,
..Settings::default()
});
}

#[derive(Default)]
Expand Down
15 changes: 15 additions & 0 deletions examples/clock/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "clock"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
publish = false

[features]
canvas = []

[dependencies]
iced = { path = "../..", features = ["canvas", "async-std", "debug"] }
iced_native = { path = "../../native" }
chrono = "0.4"
async-std = { version = "1.0", features = ["unstable"] }
193 changes: 193 additions & 0 deletions examples/clock/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
use iced::{
canvas, executor, Application, Canvas, Color, Command, Container, Element,
Length, Point, Settings, Subscription, Vector,
};

pub fn main() {
Clock::run(Settings {
antialiasing: true,
..Settings::default()
})
}

struct Clock {
now: LocalTime,
clock: canvas::layer::Cache<LocalTime>,
}

#[derive(Debug, Clone, Copy)]
enum Message {
Tick(chrono::DateTime<chrono::Local>),
}

impl Application for Clock {
type Executor = executor::Default;
type Message = Message;

fn new() -> (Self, Command<Message>) {
(
Clock {
now: chrono::Local::now().into(),
clock: canvas::layer::Cache::new(),
},
Command::none(),
)
}

fn title(&self) -> String {
String::from("Clock - Iced")
}

fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Tick(local_time) => {
let now = local_time.into();

if now != self.now {
self.now = now;
self.clock.clear();
}
}
}

Command::none()
}

fn subscription(&self) -> Subscription<Message> {
time::every(std::time::Duration::from_millis(500)).map(Message::Tick)
}

fn view(&mut self) -> Element<Message> {
let canvas = Canvas::new()
.width(Length::Units(400))
.height(Length::Units(400))
.push(self.clock.with(&self.now));

Container::new(canvas)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into()
}
}

#[derive(Debug, PartialEq, Eq)]
struct LocalTime {
hour: u32,
minute: u32,
second: u32,
}

impl From<chrono::DateTime<chrono::Local>> for LocalTime {
fn from(date_time: chrono::DateTime<chrono::Local>) -> LocalTime {
use chrono::Timelike;

LocalTime {
hour: date_time.hour(),
minute: date_time.minute(),
second: date_time.second(),
}
}
}

impl canvas::Drawable for LocalTime {
fn draw(&self, frame: &mut canvas::Frame) {
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));

frame.fill(
&clock,
canvas::Fill::Color(Color::from_rgb8(0x12, 0x93, 0xD8)),
);

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 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: 6.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 {
width: 3.0,
color: Color::WHITE,
line_cap: canvas::LineCap::Round,
..canvas::Stroke::default()
},
);
}
}

mod time {
use iced::futures;

pub fn every(
duration: std::time::Duration,
) -> iced::Subscription<chrono::DateTime<chrono::Local>> {
iced::Subscription::from_recipe(Every(duration))
}

struct Every(std::time::Duration);

impl<H, I> iced_native::subscription::Recipe<H, I> for Every
where
H: std::hash::Hasher,
{
type Output = chrono::DateTime<chrono::Local>;

fn hash(&self, state: &mut H) {
use std::hash::Hash;

std::any::TypeId::of::<Self>().hash(state);
self.0.hash(state);
}

fn stream(
self: Box<Self>,
_input: futures::stream::BoxStream<'static, I>,
) -> futures::stream::BoxStream<'static, Self::Output> {
use futures::stream::StreamExt;

async_std::stream::interval(self.0)
.map(|_| chrono::Local::now())
.boxed()
}
}
}
Loading