Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
kimonp committed Dec 22, 2023
1 parent b27d42e commit 66ac799
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 65 deletions.
22 changes: 18 additions & 4 deletions src/bindgen_glue.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Glue code to access wasm-bindgen code.
use wasm_bindgen::prelude::*;
use web_sys::CanvasRenderingContext2d;

#[macro_export]
macro_rules! console_log {
Expand All @@ -20,17 +21,30 @@ pub fn request_animation_frame(f: &Closure<dyn FnMut()>) -> i32 {
.expect("should register `requestAnimationFrame` OK")
}

/// Cancel a running aninmation frame.
pub fn cancel_animation_frame(animation_id: i32) {
window().cancel_animation_frame(animation_id).expect("Unable to cancel animation_frame")
}

/// Get the DOM document.
pub fn document() -> web_sys::Document {
window()
.document()
.expect("should have a document on window")
}

#[allow(dead_code)]
fn body() -> web_sys::HtmlElement {
document().body().expect("document should have a body")
}
/// Return the 2d canvas context of the given element id.
pub fn get_2d_context(element_id: &str) -> CanvasRenderingContext2d {
let canvas_ele = document().get_element_by_id(element_id).expect("requested element not found");
let canvas_ele: web_sys::HtmlCanvasElement = canvas_ele
.dyn_into::<web_sys::HtmlCanvasElement>()
.map_err(|_| ())
.unwrap();

canvas_ele
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()
.unwrap()
}
105 changes: 44 additions & 61 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
#![allow(non_snake_case)]
use std::cell::RefCell;
use std::rc::Rc;

use dioxus::html::GlobalAttributes;
// use dioxus_elements::canvas;
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
use dioxus::prelude::*;

use game_of_life::bindgen_glue::{cancel_animation_frame, document, request_animation_frame};
use game_of_life::bindgen_glue::*;
use game_of_life::console_log;
use game_of_life::frames_per_second::FramesPerSecond;
use game_of_life::universe::{Cell, Universe, GRID_COLUMNS, GRID_ROWS};
use wasm_bindgen::prelude::{wasm_bindgen, Closure};

use wasm_bindgen::prelude::Closure;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::CanvasRenderingContext2d;

use std::cell::RefCell;
use std::rc::Rc;

const CANVAS_ID: &str = "game-of-life-canvas";
const CELL_SIZE: u32 = 6; // px
const GRID_WIDTH: u32 = (CELL_SIZE + 1) * GRID_COLUMNS + 1;
const GRID_HEIGHT: u32 = (CELL_SIZE + 1) * GRID_ROWS + 1;

const GRID_COLOR: &str = "#CCCCCC";
const ALIVE_COLOR: &str = "#FFFFFF";
const DEAD_COLOR: &str = "#000000";
const ALIVE_COLOR: &str = "#000000";
const DEAD_COLOR: &str = "#FFFFFF";

extern crate console_error_panic_hook;
use std::panic;
// extern crate console_error_panic_hook;
// use std::panic;

#[wasm_bindgen]
pub fn init_panic_hook() {
// Better logging of panics in the browser
console_error_panic_hook::set_once();
}
// #[wasm_bindgen]
// pub fn init_panic_hook() {
// // Better logging of panics in the browser
// console_error_panic_hook::set_once();
// }

// Entry point
fn main() {
console_log!("Starting...");

Expand All @@ -42,7 +42,7 @@ fn main() {
dioxus_web::launch(App);
}

// define a component that renders a div with the text "Hello, world!"
#[component]
fn App(cx: Scope) -> Element {
let test_string = String::from("test");

Expand All @@ -52,21 +52,6 @@ fn App(cx: Scope) -> Element {
}
}

fn get_2d_context() -> CanvasRenderingContext2d {
let canvas_ele = document().get_element_by_id(CANVAS_ID).unwrap();
let canvas_ele: web_sys::HtmlCanvasElement = canvas_ele
.dyn_into::<web_sys::HtmlCanvasElement>()
.map_err(|_| ())
.unwrap();

canvas_ele
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()
.unwrap()
}

/// Wait for the first animation frame before re-configuring the canvas element.
///
/// If we try to configure it before the first render, it may not have been creted yet.
Expand All @@ -79,6 +64,7 @@ fn config_canvas_after_render(universe: Rc<RefCell<Universe>>) {
config_canvas_closure.forget();
}

// Configure the canvas size and add an event listener for clicks within the canvas grid.
fn config_canvas(universe: Rc<RefCell<Universe>>) {
if let Some(canvas_ele) = document().get_element_by_id(CANVAS_ID) {
console_log!("Configuring canvas...");
Expand Down Expand Up @@ -133,8 +119,9 @@ fn config_canvas(universe: Rc<RefCell<Universe>>) {
}
}

// Draw the grid lines which contain the game of life cells.
fn draw_grid() {
let context = get_2d_context();
let context = get_2d_context(CANVAS_ID);
let grid_color = JsValue::from_str(GRID_COLOR);
let height = GRID_HEIGHT;
let width = GRID_WIDTH;
Expand Down Expand Up @@ -166,58 +153,60 @@ fn get_grid_index(row: u32, col: u32) -> u32 {
row * GRID_ROWS + col
}

// Draw all cells in the grid based on the state of the universe.
fn draw_cells(universe: &Universe) {
let context = get_2d_context();
let context = get_2d_context(CANVAS_ID);
let cells = universe.cells();
let alive_color = JsValue::from_str(ALIVE_COLOR);
let dead_color = JsValue::from_str(DEAD_COLOR);

context.begin_path();

context.set_fill_style(&alive_color);
fill_cells(&context, cells, Cell::Alive);

context.set_fill_style(&dead_color);
fill_cells(&context, cells, Cell::Dead);
}

// Fill all the cells of the grid of the given cell_type (dead or alive).
//
// Use javascript canvas API for the rendering.
fn fill_cells(context: &CanvasRenderingContext2d, cells: &[Cell], cell_type: Cell) {
let fill_color = JsValue::from_str(match cell_type {
Cell::Alive => ALIVE_COLOR,
Cell::Dead => DEAD_COLOR,
});
context.set_fill_style(&fill_color);

for row in 0..GRID_ROWS {
for col in 0..GRID_COLUMNS {
let index = get_grid_index(row, col);
let cell = cells.get(index as usize);

if let Some(cell) = cell {
if let Some(cell) = cells.get(index as usize) {
if cell == &cell_type {
continue;
context.fill_rect(
(col * (CELL_SIZE + 1) + 1) as f64,
(row * (CELL_SIZE + 1) + 1) as f64,
CELL_SIZE as f64,
CELL_SIZE as f64,
)
}
context.fill_rect(
(col * (CELL_SIZE + 1) + 1) as f64,
(row * (CELL_SIZE + 1) + 1) as f64,
CELL_SIZE as f64,
CELL_SIZE as f64,
)
}
}
}
}

// Every time we get a new animation frame, draw the grid, and advance the game.
//
// This includes the game itself (grid and cells) as well ad the
// frames per second widget.
pub fn update_frame_loop(universe: Rc<RefCell<Universe>>) {
let f = Rc::new(RefCell::new(None));
let g = f.clone();

let mut frames_per_second = FramesPerSecond::new("frames-per-second");
let mut check = false;

// Does not need to be FnMut if universe is behing a mutex
*g.borrow_mut() = Some(Closure::<dyn FnMut()>::new(move || {
frames_per_second.update_frame();
let id = request_animation_frame(f.borrow().as_ref().unwrap());

if !check {
// config_canvas(universe.clone());
check = true;
}
draw_grid();
draw_cells(&universe.borrow_mut());
universe.borrow_mut().tick();
Expand All @@ -230,6 +219,7 @@ pub fn update_frame_loop(universe: Rc<RefCell<Universe>>) {
request_animation_frame(g.borrow().as_ref().unwrap());
}

// Component that holds the grid and cells of the game of life.
#[component]
fn Universe(cx: Scope, name: String) -> Element {
let universe = Rc::new(RefCell::new(Universe::new()));
Expand Down Expand Up @@ -287,15 +277,6 @@ fn Universe(cx: Scope, name: String) -> Element {
}
}
div { display: "flex", justify_content: "center", canvas { id: CANVAS_ID } }
div {
// button { onclick: move |_| { universe.lock().unwrap().set_height(height + 1) }, "Universe Height Up" }
// button {
// onclick: move |_| {
// universe.lock().unwrap().set_height(height - 1);
// },
// "Universe Height Down"
// }
}
div { hidden: false, color: "green", font_family: "arial", padding: "0.5rem", position: "relative",
"Universe Size: {height}, {width}"
}
Expand All @@ -307,6 +288,8 @@ fn Universe(cx: Scope, name: String) -> Element {
}
}

// Frames per second component that shows how quickly the app is rendering animation frames.
#[component]
fn FramesPerSecond(cx: Scope) -> Element {
render! {
div { id: "frames-per-second", white_space: "pre", font_family: "monospace" }
Expand Down

0 comments on commit 66ac799

Please sign in to comment.