parent
4a4824e268
commit
b6f3a2b858
@ -0,0 +1,85 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use serde_wasm_bindgen::{to_value, from_value};
|
||||
|
||||
pub mod instruction;
|
||||
use instruction::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[wasm_bindgen]
|
||||
pub struct Node {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[wasm_bindgen]
|
||||
pub struct Program {
|
||||
nodes: Vec<Node>,
|
||||
instructions: Vec<Instruction>
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new(nodes: Vec<Node>, instructions: Vec<Instruction>) -> Option<Program> {
|
||||
if instructions.len() == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
Some(Program {
|
||||
nodes,
|
||||
instructions,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn nodes(&self) -> &Vec<Node> {
|
||||
&self.nodes
|
||||
}
|
||||
|
||||
pub fn instructions(&self) -> &Vec<Instruction> {
|
||||
&self.instructions
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(typescript_type = "Node[]")]
|
||||
pub type NodeArray;
|
||||
|
||||
#[wasm_bindgen(typescript_type = "Instruction[]")]
|
||||
pub type InstructionArray;
|
||||
|
||||
#[wasm_bindgen(typescript_type = "Error")]
|
||||
pub type IError;
|
||||
}
|
||||
|
||||
impl IError {
|
||||
pub fn from_err<E>(err: E) -> IError where JsValue: From<E> {
|
||||
IError::from(JsValue::from(err))
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Program {
|
||||
pub fn from(nodes: NodeArray, instructions: InstructionArray) -> Result<Program, IError> {
|
||||
let nodes: Vec<Node> = from_value(nodes.into()).map_err(IError::from_err)?;
|
||||
let instructions: Vec<Instruction> = from_value(instructions.into()).map_err(IError::from_err)?;
|
||||
|
||||
Program::new(nodes, instructions).ok_or_else(|| {
|
||||
unimplemented!();
|
||||
})
|
||||
}
|
||||
|
||||
pub fn empty() -> Program {
|
||||
Self::new(vec![], vec![Instruction::noop()]).unwrap()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter = nodes)]
|
||||
pub fn nodes_js(&self) -> JsValue {
|
||||
to_value(&self.nodes).unwrap()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toJSON)]
|
||||
pub fn to_json(&self) -> JsValue {
|
||||
to_value(&self).unwrap()
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum Action {
|
||||
Noop,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Instruction {
|
||||
pub next: usize,
|
||||
pub action: Action
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub fn noop() -> Instruction {
|
||||
Instruction {
|
||||
next: 0,
|
||||
action: Action::Noop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const TS_INSTRUCTION: &'static str = r#"
|
||||
export interface Instruction {
|
||||
next: number;
|
||||
action: "Noop";
|
||||
}
|
||||
"#;
|
@ -0,0 +1,22 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
use crate::program::Program;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Runner {
|
||||
program: Program,
|
||||
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Runner {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(program: Program, width: usize, height: usize) -> Runner {
|
||||
Runner {
|
||||
program,
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,71 @@
|
||||
import { hello_world } from "chaos-toy-rs";
|
||||
import type { Component } from "solid-js";
|
||||
|
||||
const Simulator: Component = () => {
|
||||
return (<>
|
||||
<div>{hello_world()}</div>
|
||||
<canvas>Sorry, your browser needs to support canvases</canvas>
|
||||
</>);
|
||||
import { Program, Node, Instruction, Runner } from "chaos-toy-rs";
|
||||
import { batch, Component, createEffect, createSignal, onCleanup } from "solid-js";
|
||||
|
||||
const Simulator: Component<{nodes: Node[], instructions: Instruction[]}> = (props) => {
|
||||
const [getRunner, setRunner] = createSignal<Runner>();
|
||||
const [getWidth, setWidth] = createSignal<number>(100);
|
||||
const [getHeight, setHeight] = createSignal<number>(100);
|
||||
let canvas: HTMLCanvasElement;
|
||||
let parent: HTMLDivElement;
|
||||
let ctx: CanvasRenderingContext2D;
|
||||
|
||||
createEffect<Runner>((prev) => {
|
||||
if (prev) {
|
||||
prev.free();
|
||||
}
|
||||
// TODO: avoid re-creating a program when nodes and instructions are the same
|
||||
const program = Program.from(props.nodes, props.instructions);
|
||||
const runner = new Runner(program, getWidth(), getHeight());
|
||||
setRunner(runner);
|
||||
|
||||
return runner;
|
||||
});
|
||||
|
||||
function resizeListener() {
|
||||
batch(() => {
|
||||
const dpr = window.devicePixelRatio ?? 1;
|
||||
if (parent.clientWidth * dpr !== getWidth()) {
|
||||
setWidth(Math.ceil(parent.clientWidth * dpr));
|
||||
}
|
||||
if (parent.clientHeight * dpr !== getHeight()) {
|
||||
setHeight(Math.ceil(parent.clientHeight * dpr));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function draw() {
|
||||
const width = getWidth();
|
||||
const height = getHeight();
|
||||
ctx.fillStyle = "gray";
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
window.addEventListener("resize", resizeListener);
|
||||
resizeListener();
|
||||
ctx = canvas.getContext("2d") ?? (() => {
|
||||
throw new Error("Couldn't create canvas context");
|
||||
})();
|
||||
draw();
|
||||
|
||||
onCleanup(() => window.removeEventListener("resize", resizeListener));
|
||||
});
|
||||
|
||||
return (<div ref={parent!} style={{
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
overflow: "hidden",
|
||||
}}>
|
||||
<canvas
|
||||
ref={canvas!}
|
||||
width={getWidth()}
|
||||
height={getHeight()}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
>Sorry, your browser needs to support canvases</canvas>
|
||||
</div>);
|
||||
};
|
||||
|
||||
export default Simulator;
|
||||
|
@ -0,0 +1,8 @@
|
||||
import {Instruction} from "chaos-toy-rs";
|
||||
|
||||
export function noop(): Instruction {
|
||||
return {
|
||||
next: 0,
|
||||
action: "Noop"
|
||||
};
|
||||
}
|
Loading…
Reference in new issue