Add interactivity to solar_system example

This commit is contained in:
Héctor Ramón Jiménez 2020-04-19 18:48:30 +02:00
parent a97acd8fa8
commit bb424e54c5
2 changed files with 81 additions and 56 deletions

View File

@ -10,6 +10,7 @@ use iced::{
canvas, executor, window, Application, Canvas, Color, Command, Container,
Element, Length, Point, Settings, Size, Subscription, Vector,
};
use iced_native::input::{self, mouse};
use std::time::Instant;
@ -22,7 +23,7 @@ pub fn main() {
struct SolarSystem {
state: State,
solar_system: canvas::layer::Cache<State>,
now: Instant,
}
#[derive(Debug, Clone, Copy)]
@ -39,7 +40,7 @@ impl Application for SolarSystem {
(
SolarSystem {
state: State::new(),
solar_system: Default::default(),
now: Instant::now(),
},
Command::none(),
)
@ -52,8 +53,8 @@ impl Application for SolarSystem {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Tick(instant) => {
self.state.update(instant);
self.solar_system.clear();
self.now = instant;
self.state.clear();
}
}
@ -66,7 +67,7 @@ impl Application for SolarSystem {
}
fn view(&mut self) -> Element<Message> {
let canvas = Canvas::new(&mut self.solar_system, &self.state)
let canvas = Canvas::new(&mut self.state, &self.now)
.width(Length::Fill)
.height(Length::Fill);
@ -81,25 +82,21 @@ impl Application for SolarSystem {
#[derive(Debug)]
struct State {
cache: canvas::layer::Cache,
cursor_position: Point,
start: Instant,
current: Instant,
stars: Vec<(Point, f32)>,
}
impl State {
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;
pub fn new() -> State {
let now = Instant::now();
let (width, height) = window::Settings::default().size;
State {
cache: Default::default(),
cursor_position: Point::ORIGIN,
start: now,
current: now,
stars: {
use rand::Rng;
@ -120,12 +117,66 @@ impl State {
}
}
pub fn update(&mut self, now: Instant) {
self.current = now;
pub fn clear(&mut self) {
self.cache.clear();
}
}
impl canvas::Drawable for State {
impl canvas::Program for State {
type Input = Instant;
fn update(
&mut self,
event: canvas::Event,
_bounds: Size,
_input: &Instant,
) {
match event {
canvas::Event::Mouse(mouse_event) => match mouse_event {
mouse::Event::CursorMoved { x, y } => {
self.cursor_position = Point::new(x, y);
}
mouse::Event::Input {
button: mouse::Button::Left,
state: input::ButtonState::Released,
} => {
self.stars.push((self.cursor_position, 2.0));
}
_ => {}
},
}
}
fn layers<'a>(
&'a self,
now: &'a Instant,
) -> Vec<Box<dyn canvas::Layer + 'a>> {
let system = System {
stars: &self.stars,
start: &self.start,
now,
};
vec![Box::new(self.cache.with(system))]
}
}
#[derive(Debug)]
struct System<'a> {
stars: &'a [(Point, f32)],
start: &'a Instant,
now: &'a 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<'a> canvas::Drawable for System<'a> {
fn draw(&self, frame: &mut canvas::Frame) {
use canvas::{Path, Stroke};
use std::f32::consts::PI;
@ -135,7 +186,7 @@ impl canvas::Drawable for State {
let space = Path::rectangle(Point::new(0.0, 0.0), frame.size());
let stars = Path::new(|path| {
for (p, size) in &self.stars {
for (p, size) in self.stars {
path.rectangle(*p, Size::new(*size, *size));
}
});
@ -155,7 +206,7 @@ impl canvas::Drawable for State {
},
);
let elapsed = self.current - self.start;
let elapsed = *self.now - *self.start;
let elapsed_seconds = elapsed.as_secs() as f32;
let elapsed_millis = elapsed.subsec_millis() as f32;

View File

@ -1,5 +1,5 @@
use crate::{
canvas::{Drawable, Frame, Layer, Program},
canvas::{Drawable, Frame, Layer},
Primitive,
};
@ -26,34 +26,17 @@ impl Default for State {
///
/// [`Layer`]: ../trait.Layer.html
/// [`Cache`]: struct.Cache.html
#[derive(Debug)]
pub struct Cache<T: Drawable> {
input: PhantomData<T>,
#[derive(Debug, Default)]
pub struct Cache {
state: RefCell<State>,
}
impl<T> Default for Cache<T>
where
T: Drawable,
{
fn default() -> Self {
Self {
input: PhantomData,
state: Default::default(),
}
}
}
impl<T> Cache<T>
where
T: Drawable + std::fmt::Debug,
{
impl Cache {
/// Creates a new empty [`Cache`].
///
/// [`Cache`]: struct.Cache.html
pub fn new() -> Self {
Cache {
input: PhantomData,
state: Default::default(),
}
}
@ -71,35 +54,26 @@ where
/// [`Cache`]: struct.Cache.html
/// [`Layer`]: ../trait.Layer.html
/// [`Canvas`]: ../../struct.Canvas.html
pub fn with<'a>(
pub fn with<'a, T>(
&'a self,
input: impl Borrow<T> + std::fmt::Debug + 'a,
) -> impl Layer + 'a {
) -> impl Layer + 'a
where
T: Drawable + std::fmt::Debug + 'a,
{
Bind {
cache: self,
input: input,
drawable: PhantomData,
}
}
}
impl<T> Program for Cache<T>
where
T: Drawable + std::fmt::Debug,
{
type Input = T;
fn layers<'a>(
&'a self,
input: &'a Self::Input,
) -> Vec<Box<dyn Layer + 'a>> {
vec![Box::new(self.with(input))]
}
}
#[derive(Debug)]
struct Bind<'a, T: Drawable, I: Borrow<T> + 'a> {
cache: &'a Cache<T>,
cache: &'a Cache,
input: I,
drawable: PhantomData<T>,
}
impl<'a, T, I> Layer for Bind<'a, T, I>