mod animation_frame;
mod canvas;
mod renderer;
pub mod component;
pub mod input;
use crate::{Layer, Transform, Vertex2, engine::canvas::Canvas, error::Result};
use component::Component;
use input::Input;
use std::{
cell::{RefCell, RefMut},
rc::Rc,
};
use web_sys::{CanvasRenderingContext2d, Window};
use web_time::{Duration, SystemTime};
pub struct Context<'a> {
pub input: &'a Input,
delta_time: f32,
}
impl<'a> Context<'a> {
pub fn new(input: &'a Input, delta_time: f32) -> Self {
Self { input, delta_time }
}
pub fn delta_time(&self) -> f32 {
self.delta_time
}
}
struct State {
canvas: Canvas,
components: RefCell<Vec<Box<dyn Component>>>,
input: RefCell<Input>,
last_time: RefCell<SystemTime>,
}
pub struct Engine {
window: Rc<Window>,
state: Rc<State>,
}
impl Engine {
pub fn new(canvas_id: &str, components: Vec<Box<dyn Component>>) -> Result<Self> {
let not_found_msg = |entity: &str| format!("Did not find '{}'", entity);
let window = web_sys::window().ok_or_else(|| not_found_msg("window"))?;
let canvas = {
let document = window.document().ok_or_else(|| not_found_msg("document"))?;
let canvas = document
.get_element_by_id(canvas_id)
.ok_or_else(|| not_found_msg(canvas_id))?;
Canvas::new(canvas)?
};
let input = Input::default();
input.init(&window)?;
let state = State {
canvas,
components: RefCell::new(components),
input: RefCell::new(input),
last_time: RefCell::new(SystemTime::now()),
};
Ok(Self {
state: Rc::new(state),
window: Rc::new(window),
})
}
pub fn run(&self) -> Result<()> {
let window = self.window.clone();
let state = self.state.clone();
animation_frame::request_recursive(
self.window.clone(),
Rc::new(move || Engine::main_loop(state.clone(), &window)),
)
}
fn main_loop(state: Rc<State>, window: &Window) -> Result<()> {
let window_size = get_window_inner_size(window)?;
state.canvas.resize(window_size);
state.canvas.clear();
{
let delta_time = Engine::calc_delta_and_update_last(state.last_time.borrow_mut());
let input_borrow = state.input.borrow();
let mut ctx = Context::new(&input_borrow, delta_time);
let mut components = state.components.borrow_mut();
let mut components: Vec<&mut dyn Component> =
components.iter_mut().map(|cmp| cmp.as_mut() as _).collect();
Engine::update_components(components.as_mut_slice(), &mut ctx, &Transform::default());
}
let components = state.components.borrow();
let components: Vec<&dyn Component> =
components.iter().map(|cmp| cmp.as_ref() as _).collect();
Engine::render_layers(components.as_slice(), state.canvas.context());
state.input.borrow_mut().transition_states();
Ok(())
}
fn calc_delta_and_update_last(mut last_time: RefMut<SystemTime>) -> f32 {
let elapsed = last_time.elapsed().unwrap_or_else(|_| Duration::default());
let delta_time = elapsed.as_millis() as f32 / 1000.0;
*last_time = SystemTime::now();
delta_time
}
fn update_components(
components: &mut [&mut dyn Component],
ctx: &mut Context,
parent_transform: &Transform,
) {
let mut ctx = Context::new(ctx.input, ctx.delta_time());
for component in components.iter_mut() {
let transform = parent_transform.clone() + component.transform();
let mut children = component.children_mut();
Engine::update_components(children.as_mut_slice(), &mut ctx, &transform);
}
for component in components.iter_mut() {
component.transform_mut().parent = Some(Box::new(parent_transform.clone()));
component.update(&mut ctx);
}
}
fn render_layers(components: &[&dyn Component], render_ctx: &CanvasRenderingContext2d) {
Engine::render_components(components, render_ctx, Layer::Five, &Transform::default());
Engine::render_components(components, render_ctx, Layer::Four, &Transform::default());
Engine::render_components(components, render_ctx, Layer::Three, &Transform::default());
Engine::render_components(components, render_ctx, Layer::Two, &Transform::default());
Engine::render_components(components, render_ctx, Layer::One, &Transform::default());
}
fn render_components(
components: &[&dyn Component],
render_ctx: &CanvasRenderingContext2d,
layer: Layer,
parent_transform: &Transform,
) {
for component in components.iter() {
let layer_filtered_renderables = component
.renderables()
.iter()
.filter(|renderable| renderable.layer == layer);
let renderable_parent_transform = parent_transform.clone() + component.transform();
for renderable in layer_filtered_renderables {
let transform = renderable_parent_transform.clone() + &renderable.transform;
renderer::render(render_ctx, &renderable.vertices, &transform);
(renderable.style)(render_ctx);
}
}
for component in components.iter() {
let children = component.children();
let children: Vec<&dyn Component> = children.iter().map(|child| &**child).collect();
let parent_transform = parent_transform.clone() + component.transform();
Engine::render_components(&children, render_ctx, layer, &parent_transform);
}
}
}
fn get_window_inner_size(window: &Window) -> Result<Vertex2<u32>> {
let width = window
.inner_width()
.map_err(|_| "Failed to get window's inner width")?
.as_f64()
.ok_or("Failed to convert window's inner width to f64")? as u32;
let height = window
.inner_height()
.map_err(|_| "Failed to get window's inner height")?
.as_f64()
.ok_or("Failed to convert window's inner width to f64")? as u32;
Ok(Vertex2 {
x: width,
y: height,
})
}
#[cfg(test)]
mod tests {
#[test]
fn test() {
}
}