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 { Program, Node, Instruction, Runner } from "chaos-toy-rs";
|
||||||
import type { Component } from "solid-js";
|
import { batch, Component, createEffect, createSignal, onCleanup } from "solid-js";
|
||||||
|
|
||||||
const Simulator: Component = () => {
|
const Simulator: Component<{nodes: Node[], instructions: Instruction[]}> = (props) => {
|
||||||
return (<>
|
const [getRunner, setRunner] = createSignal<Runner>();
|
||||||
<div>{hello_world()}</div>
|
const [getWidth, setWidth] = createSignal<number>(100);
|
||||||
<canvas>Sorry, your browser needs to support canvases</canvas>
|
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;
|
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