diff options
author | Malte Voos <git@mal.tc> | 2023-04-02 12:24:23 +0200 |
---|---|---|
committer | Malte Voos <git@mal.tc> | 2023-04-02 12:24:23 +0200 |
commit | 58b1fced2563f40990123ab362b0df53b5a91c0e (patch) | |
tree | 84faaa57a23633041baffe5d50467e60abc9d5e9 /src | |
parent | 7d9836ebef1950c550081b2ea9cd3f7718280a02 (diff) | |
download | life-58b1fced2563f40990123ab362b0df53b5a91c0e.tar.gz life-58b1fced2563f40990123ab362b0df53b5a91c0e.zip |
make it run in the browser
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs (renamed from src/main.rs) | 143 |
1 files changed, 109 insertions, 34 deletions
diff --git a/src/main.rs b/src/lib.rs index a9af30d..cbac12e 100644 --- a/src/main.rs +++ b/src/lib.rs @@ -1,54 +1,114 @@ +use std::rc::Rc; + use pixels::wgpu::Color; use pixels::PixelsBuilder; use pixels::SurfaceTexture; +use rand::distributions::Distribution; use rand::distributions::Standard; -use rand::prelude::Distribution; -use winit::dpi::PhysicalSize; +use wasm_bindgen::prelude::*; +use winit::dpi::LogicalSize; use winit::event::Event; use winit::event_loop::EventLoop; use winit::window::WindowBuilder; use winit_input_helper::WinitInputHelper; -const RES_X: usize = 265; -const RES_Y: usize = 265; +// const WIDTH: usize = 256; +// const HEIGHT: usize = 256; -const WIN_WIDTH: u32 = 4 * RES_X as u32; -const WIN_HEIGHT: u32 = 4 * RES_Y as u32; +#[wasm_bindgen(start)] +async fn start() -> Result<(), JsValue> { + return run().await.map_err(|err| JsValue::from(err.to_string())); +} -fn main() -> anyhow::Result<()> { +async fn run() -> anyhow::Result<()> { let event_loop = EventLoop::new(); + + // let size = LogicalSize { + // width: WIDTH as f64, + // height: HEIGHT as f64, + // }; + let window = WindowBuilder::new() - .with_inner_size(PhysicalSize { - width: WIN_WIDTH, - height: WIN_HEIGHT, - }) - .with_resizable(false) + // .with_inner_size(size) + // .with_min_inner_size(size) .build(&event_loop)?; + + let window = Rc::new(window); + + #[cfg(target_arch = "wasm32")] + { + use winit::platform::web::WindowExtWebSys; + + // Retrieve current width and height dimensions of browser client window + let get_window_size = || { + let client_window = web_sys::window().unwrap(); + LogicalSize::new( + client_window.inner_width().unwrap().as_f64().unwrap(), + client_window.inner_height().unwrap().as_f64().unwrap(), + ) + }; + + let window = Rc::clone(&window); + + // Initialize winit window with current dimensions of browser client + window.set_inner_size(get_window_size()); + + let client_window = web_sys::window().unwrap(); + + // Attach winit canvas to body element + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.body()) + .and_then(|body| { + body.append_child(&web_sys::Element::from(window.canvas())) + .ok() + }) + .expect("couldn't append canvas to document body"); + + // Listen for resize event on browser client. Adjust winit window dimensions + // on event trigger + let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_e: web_sys::Event| { + let size = get_window_size(); + window.set_inner_size(size) + }) as Box<dyn FnMut(_)>); + client_window + .add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref()) + .unwrap(); + closure.forget(); + } + let mut input = WinitInputHelper::new(); let window_size = window.inner_size(); - let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window); + let surface_texture = + SurfaceTexture::new(window_size.width, window_size.height, window.as_ref()); + + let width = window_size.width / 8; + let height = window_size.height / 8; - let mut pixels = PixelsBuilder::new(RES_X as u32, RES_Y as u32, surface_texture) + web_sys::console::log_1(&format!("{} x {}", width, height).into()); + + let mut pixels = PixelsBuilder::new(width, height, surface_texture) .clear_color(Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0, }) - .build()?; - let mut life = Life::new(); + .build_async() + .await?; + let mut life = Life::new(width as usize, height as usize); event_loop.run(move |event, _, control_flow| { control_flow.set_poll(); if let Event::RedrawRequested(_) = event { - life.draw(pixels.get_frame()); + life.draw(pixels.frame_mut()); pixels.render().expect("failed to render"); } if input.update(&event) { - if input.quit() { + if input.close_requested() || input.destroyed() { control_flow.set_exit(); return; }; @@ -67,7 +127,9 @@ enum Cell { #[derive(Debug)] struct Grid { - pub cells: [[Cell; RES_X]; RES_Y], + pub width: usize, + pub height: usize, + pub cells: Vec<Vec<Cell>>, } enum FrontGrid { @@ -76,6 +138,8 @@ enum FrontGrid { } struct Life { + pub width: usize, + pub height: usize, grid_a: Grid, grid_b: Grid, front_grid: FrontGrid, @@ -91,26 +155,30 @@ impl Cell { } impl Grid { - pub fn blank() -> Self { + pub fn blank(width: usize, height: usize) -> Self { Self { - cells: [[Cell::Dead { since: 0xff }; RES_X]; RES_Y], + width, + height, + cells: vec![vec![Cell::Dead { since: 0xff }; width]; height], } } - pub fn with_random_borders() -> Self { - let mut ret = Self::blank(); + pub fn with_random_borders(width: usize, height: usize) -> Self { + let mut ret = Self::blank(width, height); ret.randomize_border(); ret } pub fn randomize_border(&mut self) { - self.cells[RES_X - 1] = rand::random(); + for col in 0..self.width { + self.cells[self.height - 1][col] = rand::random(); + } } pub fn draw(&self, frame: &mut [u8]) { - for row in 0..RES_X { - for col in 0..RES_Y { - let pixel_idx = 4 * (RES_X * row + col); + for row in 0..self.height { + for col in 0..self.width { + let pixel_idx = 4 * (self.width * row + col); match self.cells[row][col] { Cell::Alive => { @@ -138,8 +206,8 @@ impl Grid { pub fn num_alive_neighbors(&self, row: usize, col: usize) -> u8 { let mut ret = 0; - for i in row.saturating_sub(1)..=(row + 1).min(RES_X - 1) { - for j in col.saturating_sub(1)..=(col + 1).min(RES_Y - 1) { + for i in row.saturating_sub(1)..=(row + 1).min(self.height - 1) { + for j in col.saturating_sub(1)..=(col + 1).min(self.width - 1) { if self.cells[i][j].is_alive() { ret += 1; } @@ -175,10 +243,12 @@ impl Grid { } impl Life { - pub fn new() -> Self { + pub fn new(width: usize, height: usize) -> Self { Life { - grid_a: Grid::with_random_borders(), - grid_b: Grid::blank(), + width, + height, + grid_a: Grid::with_random_borders(width, height), + grid_b: Grid::blank(width, height), front_grid: FrontGrid::A, } } @@ -188,10 +258,11 @@ impl Life { } pub fn update(&mut self) { + let (width, height) = self.dimensions(); let (front, back) = self.grids(); - for row in 0..RES_X { - for col in 0..RES_Y { + for row in 0..height { + for col in 0..width { back.cells[row][col] = front.new_state(row, col); } } @@ -200,6 +271,10 @@ impl Life { self.swap_grids(); } + fn dimensions(&self) -> (usize, usize) { + (self.width, self.height) + } + fn grids(&mut self) -> (&Grid, &mut Grid) { match self.front_grid { FrontGrid::A => (&self.grid_a, &mut self.grid_b), |