Remove Layer trait and simplify Canvas

This commit is contained in:
Héctor Ramón Jiménez 2020-04-19 21:55:23 +02:00
parent bb424e54c5
commit 592cc68506
9 changed files with 181 additions and 174 deletions

View file

@ -12,7 +12,7 @@ pub fn main() {
struct Clock { struct Clock {
now: LocalTime, now: LocalTime,
clock: canvas::layer::Cache<LocalTime>, clock: canvas::Cache,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -59,7 +59,7 @@ impl Application for Clock {
} }
fn view(&mut self) -> Element<Message> { fn view(&mut self) -> Element<Message> {
let canvas = Canvas::new(&mut self.clock, &self.now) let canvas = Canvas::new(self.clock.with(&self.now))
.width(Length::Units(400)) .width(Length::Units(400))
.height(Length::Units(400)); .height(Length::Units(400));

View file

@ -23,7 +23,6 @@ pub fn main() {
struct SolarSystem { struct SolarSystem {
state: State, state: State,
now: Instant,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -40,7 +39,6 @@ impl Application for SolarSystem {
( (
SolarSystem { SolarSystem {
state: State::new(), state: State::new(),
now: Instant::now(),
}, },
Command::none(), Command::none(),
) )
@ -53,8 +51,7 @@ impl Application for SolarSystem {
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Command<Message> {
match message { match message {
Message::Tick(instant) => { Message::Tick(instant) => {
self.now = instant; self.state.update(instant);
self.state.clear();
} }
} }
@ -67,7 +64,7 @@ impl Application for SolarSystem {
} }
fn view(&mut self) -> Element<Message> { fn view(&mut self) -> Element<Message> {
let canvas = Canvas::new(&mut self.state, &self.now) let canvas = Canvas::new(&mut self.state)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill); .height(Length::Fill);
@ -82,9 +79,11 @@ impl Application for SolarSystem {
#[derive(Debug)] #[derive(Debug)]
struct State { struct State {
cache: canvas::layer::Cache, space_cache: canvas::Cache,
system_cache: canvas::Cache,
cursor_position: Point, cursor_position: Point,
start: Instant, start: Instant,
now: Instant,
stars: Vec<(Point, f32)>, stars: Vec<(Point, f32)>,
} }
@ -94,43 +93,52 @@ impl State {
let (width, height) = window::Settings::default().size; let (width, height) = window::Settings::default().size;
State { State {
cache: Default::default(), space_cache: Default::default(),
system_cache: Default::default(),
cursor_position: Point::ORIGIN, cursor_position: Point::ORIGIN,
start: now, start: now,
stars: { now,
use rand::Rng; stars: Self::generate_stars(width, height),
let mut rng = rand::thread_rng();
(0..100)
.map(|_| {
(
Point::new(
rng.gen_range(0.0, width as f32),
rng.gen_range(0.0, height as f32),
),
rng.gen_range(0.5, 1.0),
)
})
.collect()
},
} }
} }
pub fn clear(&mut self) { pub fn space(&self) -> Space<'_> {
self.cache.clear(); Space { stars: &self.stars }
}
pub fn system(&self) -> System {
System {
start: self.start,
now: self.now,
}
}
pub fn update(&mut self, now: Instant) {
self.now = now;
self.system_cache.clear();
}
fn generate_stars(width: u32, height: u32) -> Vec<(Point, f32)> {
use rand::Rng;
let mut rng = rand::thread_rng();
(0..100)
.map(|_| {
(
Point::new(
rng.gen_range(0.0, width as f32),
rng.gen_range(0.0, height as f32),
),
rng.gen_range(0.5, 1.0),
)
})
.collect()
} }
} }
impl canvas::Program for State { impl canvas::State for State {
type Input = Instant; fn update(&mut self, event: canvas::Event, _bounds: Size) {
fn update(
&mut self,
event: canvas::Event,
_bounds: Size,
_input: &Instant,
) {
match event { match event {
canvas::Event::Mouse(mouse_event) => match mouse_event { canvas::Event::Mouse(mouse_event) => match mouse_event {
mouse::Event::CursorMoved { x, y } => { mouse::Event::CursorMoved { x, y } => {
@ -141,47 +149,29 @@ impl canvas::Program for State {
state: input::ButtonState::Released, state: input::ButtonState::Released,
} => { } => {
self.stars.push((self.cursor_position, 2.0)); self.stars.push((self.cursor_position, 2.0));
self.space_cache.clear();
} }
_ => {} _ => {}
}, },
} }
} }
fn layers<'a>( fn draw(&self, bounds: Size) -> Vec<canvas::Geometry> {
&'a self, vec![
now: &'a Instant, self.space_cache.draw(bounds, self.space()),
) -> Vec<Box<dyn canvas::Layer + 'a>> { self.system_cache.draw(bounds, self.system()),
let system = System { ]
stars: &self.stars,
start: &self.start,
now,
};
vec![Box::new(self.cache.with(system))]
} }
} }
#[derive(Debug)] #[derive(Debug)]
struct System<'a> { struct Space<'a> {
stars: &'a [(Point, f32)], stars: &'a [(Point, f32)],
start: &'a Instant,
now: &'a Instant,
} }
impl System<'_> { impl canvas::Drawable for Space<'_> {
const SUN_RADIUS: f32 = 70.0;
const ORBIT_RADIUS: f32 = 150.0;
const EARTH_RADIUS: f32 = 12.0;
const MOON_RADIUS: f32 = 4.0;
const MOON_DISTANCE: f32 = 28.0;
}
impl<'a> canvas::Drawable for System<'a> {
fn draw(&self, frame: &mut canvas::Frame) { fn draw(&self, frame: &mut canvas::Frame) {
use canvas::{Path, Stroke}; use canvas::Path;
use std::f32::consts::PI;
let center = frame.center();
let space = Path::rectangle(Point::new(0.0, 0.0), frame.size()); let space = Path::rectangle(Point::new(0.0, 0.0), frame.size());
@ -191,11 +181,35 @@ impl<'a> canvas::Drawable for System<'a> {
} }
}); });
frame.fill(&space, Color::BLACK);
frame.fill(&stars, Color::WHITE);
}
}
#[derive(Debug)]
struct System {
start: Instant,
now: Instant,
}
impl System {
const SUN_RADIUS: f32 = 70.0;
const ORBIT_RADIUS: f32 = 150.0;
const EARTH_RADIUS: f32 = 12.0;
const MOON_RADIUS: f32 = 4.0;
const MOON_DISTANCE: f32 = 28.0;
}
impl canvas::Drawable for System {
fn draw(&self, frame: &mut canvas::Frame) {
use canvas::{Path, Stroke};
use std::f32::consts::PI;
let center = frame.center();
let sun = Path::circle(center, Self::SUN_RADIUS); let sun = Path::circle(center, Self::SUN_RADIUS);
let orbit = Path::circle(center, Self::ORBIT_RADIUS); let orbit = Path::circle(center, Self::ORBIT_RADIUS);
frame.fill(&space, Color::BLACK);
frame.fill(&stars, Color::WHITE);
frame.fill(&sun, Color::from_rgb8(0xF9, 0xD7, 0x1C)); frame.fill(&sun, Color::from_rgb8(0xF9, 0xD7, 0x1C));
frame.stroke( frame.stroke(
&orbit, &orbit,
@ -206,7 +220,7 @@ impl<'a> canvas::Drawable for System<'a> {
}, },
); );
let elapsed = *self.now - *self.start; let elapsed = self.now - self.start;
let elapsed_seconds = elapsed.as_secs() as f32; let elapsed_seconds = elapsed.as_secs() as f32;
let elapsed_millis = elapsed.subsec_millis() as f32; let elapsed_millis = elapsed.subsec_millis() as f32;

View file

@ -14,24 +14,26 @@ use iced_native::{
}; };
use std::hash::Hash; use std::hash::Hash;
pub mod layer;
pub mod path; pub mod path;
mod cache;
mod drawable; mod drawable;
mod event; mod event;
mod fill; mod fill;
mod frame; mod frame;
mod program; mod geometry;
mod state;
mod stroke; mod stroke;
mod text; mod text;
pub use cache::Cache;
pub use drawable::Drawable; pub use drawable::Drawable;
pub use event::Event; pub use event::Event;
pub use fill::Fill; pub use fill::Fill;
pub use frame::Frame; pub use frame::Frame;
pub use layer::Layer; pub use geometry::Geometry;
pub use path::Path; pub use path::Path;
pub use program::Program; pub use state::State;
pub use stroke::{LineCap, LineJoin, Stroke}; pub use stroke::{LineCap, LineJoin, Stroke};
pub use text::Text; pub use text::Text;
@ -65,7 +67,7 @@ pub use text::Text;
/// # pub use iced_wgpu::canvas; /// # pub use iced_wgpu::canvas;
/// # pub use iced_native::Color; /// # pub use iced_native::Color;
/// # } /// # }
/// use iced::canvas::{self, layer, Canvas, Drawable, Fill, Frame, Path}; /// use iced::canvas::{self, Cache, Canvas, Drawable, Fill, Frame, Path};
/// use iced::Color; /// use iced::Color;
/// ///
/// // First, we define the data we need for drawing /// // First, we define the data we need for drawing
@ -86,31 +88,29 @@ pub use text::Text;
/// } /// }
/// ///
/// // We can use a `Cache` to avoid unnecessary re-tessellation /// // We can use a `Cache` to avoid unnecessary re-tessellation
/// let mut cache: layer::Cache<Circle> = layer::Cache::new(); /// let cache = Cache::new();
/// ///
/// // Finally, we simply use our `Cache` to create the `Canvas`! /// // Finally, we simply use our `Cache` to create the `Canvas`!
/// let canvas = Canvas::new(&mut cache, &Circle { radius: 50.0 }); /// let canvas = Canvas::new(cache.with(Circle { radius: 50.0 }));
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct Canvas<'a, P: Program> { pub struct Canvas<S: State> {
width: Length, width: Length,
height: Length, height: Length,
program: &'a mut P, state: S,
input: &'a P::Input,
} }
impl<'a, P: Program> Canvas<'a, P> { impl<S: State> Canvas<S> {
const DEFAULT_SIZE: u16 = 100; const DEFAULT_SIZE: u16 = 100;
/// Creates a new [`Canvas`] with no layers. /// Creates a new [`Canvas`] with no layers.
/// ///
/// [`Canvas`]: struct.Canvas.html /// [`Canvas`]: struct.Canvas.html
pub fn new(program: &'a mut P, input: &'a P::Input) -> Self { pub fn new(state: S) -> Self {
Canvas { Canvas {
width: Length::Units(Self::DEFAULT_SIZE), width: Length::Units(Self::DEFAULT_SIZE),
height: Length::Units(Self::DEFAULT_SIZE), height: Length::Units(Self::DEFAULT_SIZE),
program, state,
input,
} }
} }
@ -131,7 +131,7 @@ impl<'a, P: Program> Canvas<'a, P> {
} }
} }
impl<'a, Message, P: Program> Widget<Message, Renderer> for Canvas<'a, P> { impl<Message, S: State> Widget<Message, Renderer> for Canvas<S> {
fn width(&self) -> Length { fn width(&self) -> Length {
self.width self.width
} }
@ -178,7 +178,7 @@ impl<'a, Message, P: Program> Widget<Message, Renderer> for Canvas<'a, P> {
}; };
if let Some(canvas_event) = canvas_event { if let Some(canvas_event) = canvas_event {
self.program.update(canvas_event, bounds.size(), self.input) self.state.update(canvas_event, bounds.size())
} }
} }
@ -196,12 +196,12 @@ impl<'a, Message, P: Program> Widget<Message, Renderer> for Canvas<'a, P> {
( (
Primitive::Group { Primitive::Group {
primitives: self primitives: self
.program .state
.layers(self.input) .draw(size)
.iter() .into_iter()
.map(|layer| Primitive::Cached { .map(|geometry| Primitive::Cached {
origin, origin,
cache: layer.draw(size), cache: geometry.into_primitive(),
}) })
.collect(), .collect(),
}, },
@ -218,12 +218,12 @@ impl<'a, Message, P: Program> Widget<Message, Renderer> for Canvas<'a, P> {
} }
} }
impl<'a, Message, P: Program> From<Canvas<'a, P>> impl<'a, Message, S: State + 'a> From<Canvas<S>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Message: 'static, Message: 'static,
{ {
fn from(canvas: Canvas<'a, P>) -> Element<'a, Message, Renderer> { fn from(canvas: Canvas<S>) -> Element<'a, Message, Renderer> {
Element::new(canvas) Element::new(canvas)
} }
} }

View file

@ -1,10 +1,10 @@
use crate::{ use crate::{
canvas::{Drawable, Frame, Layer}, canvas::{Drawable, Frame, Geometry},
Primitive, Primitive,
}; };
use iced_native::Size; use iced_native::Size;
use std::{borrow::Borrow, cell::RefCell, marker::PhantomData, sync::Arc}; use std::{cell::RefCell, sync::Arc};
enum State { enum State {
Empty, Empty,
@ -48,61 +48,51 @@ impl Cache {
*self.state.borrow_mut() = State::Empty; *self.state.borrow_mut() = State::Empty;
} }
/// Binds the [`Cache`] with some data, producing a [`Layer`] that can be pub fn draw<T>(&self, new_bounds: Size, input: T) -> Geometry
/// added to a [`Canvas`].
///
/// [`Cache`]: struct.Cache.html
/// [`Layer`]: ../trait.Layer.html
/// [`Canvas`]: ../../struct.Canvas.html
pub fn with<'a, T>(
&'a self,
input: impl Borrow<T> + std::fmt::Debug + 'a,
) -> impl Layer + 'a
where where
T: Drawable + std::fmt::Debug + 'a, T: Drawable + std::fmt::Debug,
{ {
Bind {
cache: self,
input: input,
drawable: PhantomData,
}
}
}
#[derive(Debug)]
struct Bind<'a, T: Drawable, I: Borrow<T> + 'a> {
cache: &'a Cache,
input: I,
drawable: PhantomData<T>,
}
impl<'a, T, I> Layer for Bind<'a, T, I>
where
T: Drawable + std::fmt::Debug,
I: Borrow<T> + std::fmt::Debug + 'a,
{
fn draw(&self, current_bounds: Size) -> Arc<Primitive> {
use std::ops::Deref; use std::ops::Deref;
if let State::Filled { bounds, primitive } = if let State::Filled { bounds, primitive } = self.state.borrow().deref()
self.cache.state.borrow().deref()
{ {
if *bounds == current_bounds { if *bounds == new_bounds {
return primitive.clone(); return Geometry::from_primitive(primitive.clone());
} }
} }
let mut frame = Frame::new(current_bounds.width, current_bounds.height); let mut frame = Frame::new(new_bounds.width, new_bounds.height);
self.input.borrow().draw(&mut frame); input.draw(&mut frame);
let primitive = Arc::new(frame.into_primitive()); let primitive = Arc::new(frame.into_primitive());
*self.cache.state.borrow_mut() = State::Filled { *self.state.borrow_mut() = State::Filled {
bounds: current_bounds, bounds: new_bounds,
primitive: primitive.clone(), primitive: primitive.clone(),
}; };
primitive Geometry::from_primitive(primitive)
}
pub fn with<'a, T>(&'a self, input: T) -> impl crate::canvas::State + 'a
where
T: Drawable + std::fmt::Debug + 'a,
{
Bind { cache: self, input }
}
}
struct Bind<'a, T> {
cache: &'a Cache,
input: T,
}
impl<'a, T> crate::canvas::State for Bind<'a, T>
where
T: Drawable + std::fmt::Debug + 'a,
{
fn draw(&self, bounds: Size) -> Vec<Geometry> {
vec![self.cache.draw(bounds, &self.input)]
} }
} }

View file

@ -16,3 +16,12 @@ impl<'a> Drawable for dyn Fn(&mut Frame) + 'a {
self(frame) self(frame)
} }
} }
impl<T> Drawable for &T
where
T: Drawable,
{
fn draw(&self, frame: &mut Frame) {
T::draw(self, frame)
}
}

View file

@ -0,0 +1,15 @@
use crate::Primitive;
use std::sync::Arc;
#[derive(Debug)]
pub struct Geometry(Arc<Primitive>);
impl Geometry {
pub(crate) fn from_primitive(primitive: Arc<Primitive>) -> Self {
Self(primitive)
}
pub(crate) fn into_primitive(self) -> Arc<Primitive> {
self.0
}
}

View file

@ -1,25 +0,0 @@
//! Produce, store, and reuse geometry.
mod cache;
pub use cache::Cache;
use crate::Primitive;
use iced_native::Size;
use std::sync::Arc;
/// A layer that can be presented at a [`Canvas`].
///
/// [`Canvas`]: ../struct.Canvas.html
pub trait Layer: std::fmt::Debug {
/// Draws the [`Layer`] in the given bounds and produces a [`Primitive`] as
/// a result.
///
/// The [`Layer`] may choose to store the produced [`Primitive`] locally and
/// only recompute it when the bounds change, its contents change, or is
/// otherwise explicitly cleared by other means.
///
/// [`Layer`]: trait.Layer.html
/// [`Primitive`]: ../../../enum.Primitive.html
fn draw(&self, bounds: Size) -> Arc<Primitive>;
}

View file

@ -1,16 +0,0 @@
use crate::canvas::{Event, Layer, Size};
pub trait Program {
type Input;
fn layers<'a>(&'a self, input: &'a Self::Input)
-> Vec<Box<dyn Layer + 'a>>;
fn update<'a>(
&'a mut self,
_event: Event,
_bounds: Size,
_input: &'a Self::Input,
) {
}
}

View file

@ -0,0 +1,20 @@
use crate::canvas::{Event, Geometry, Size};
pub trait State {
fn update(&mut self, _event: Event, _bounds: Size) {}
fn draw(&self, bounds: Size) -> Vec<Geometry>;
}
impl<T> State for &mut T
where
T: State,
{
fn update(&mut self, event: Event, bounds: Size) {
T::update(self, event, bounds);
}
fn draw(&self, bounds: Size) -> Vec<Geometry> {
T::draw(self, bounds)
}
}