explorata/src/components/CanvasBoard.vue

200 lines
5.2 KiB
Vue

<script setup>
import { onMounted, watch } from "vue";
import { globalStore } from "../stores/index.js";
import {
createBoard,
conwayRules,
overpopulationRules,
lonelinessRules,
threebornRules,
highLifeRules,
servietteRules,
evolve2d,
} from "../modules/core.js";
import {
create1dInitialState,
create2dRandomGrid,
} from "../modules/board.js";
import { picToBoard } from "../modules/picture.js";
const store = globalStore();
const available2dRules = {
conway: conwayRules,
overpopulation: overpopulationRules,
loneliness: lonelinessRules,
threeborn: threebornRules,
highlife: highLifeRules,
serviette: servietteRules,
};
// used to determine the dimensions of the board
// TODO: should be a Board method
const max = () => {
return Math.max(store.board.width, store.board.height);
};
const selectedRules = () => {
return available2dRules[store.selected2dRules.id];
};
watch(
() => store.draw1d,
(value) => {
if (value == true) draw1d();
}
);
watch(
() => store.draw2d,
(value) => {
if (value == true) draw2dNew();
}
);
watch(
() => store.draw2dLast,
async (value) => {
if (value == true) await draw2dLast();
}
);
watch(
() => store.draw2dpicture,
(value) => {
if (value == true) draw2dPicture();
}
);
watch(
() => store.reset,
(value) => {
if (value == true) reset();
}
);
onMounted(() => {
// TODO : butt ugly.
const canvas = document.getElementById("board-canvas");
store.renderer.width = canvas.parentElement.clientWidth;
store.renderer.height = canvas.parentElement.clientHeight;
store.renderer.canvas = canvas;
store.renderer.ctx = store.renderer.canvas.getContext("2d", {
willReadFrequently: true,
});
if (typeof OffscreenCanvas != "undefined") {
store.renderer.workCanvas = new OffscreenCanvas(
canvas.parentElement.clientWidth,
canvas.parentElement.clientHeight
);
}
// https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas/OffscreenCanvas#Browser_compatibility
// Fallback for when offscreenCanvas is unsupported or disabled (Firefox < 44, default for Firefox?)
else {
store.renderer.workCanvas = document.createElement("canvas");
store.renderer.workCanvas.width = canvas.parentElement.clientWidth;
store.renderer.workCanvas.height = canvas.parentElement.clientHeight;
}
store.renderer.workCtx = store.renderer.workCanvas.getContext("2d", {
willReadFrequently: true,
});
store.setBoardWidth();
store.setBoardHeight();
});
// draws the board on the canvas
const drawCanvas = async (board) => {
store.renderer.render(board);
};
// draw elementary automaton on the canvas based on selected ruleset
const draw1d = () => {
const initialState = create1dInitialState(
store.board,
store.initial1dState
);
const board = createBoard(initialState, store.ruleset1d.rules, max());
store.board.grid = Object.freeze(board);
drawCanvas(store.board);
store.toggleStop();
};
// draw 2D automaton on the canvas in a loop
const draw2d = (board) => {
drawCanvas(store.board);
const newBoard = Object.freeze(evolve2d(board.grid, selectedRules()));
if (store.board.grid == newBoard) store.toggleStop();
store.board.grid = newBoard;
};
// draw 2d automaton in a loop, starting from passed state
const draw2dNext = async (board) => {
setTimeout(() => {
if (!store.canDraw) return;
draw2d(board);
return draw2dNext(store.board);
}, store.renderer.refreshRate);
};
// draw 2d automaton from a new state
const draw2dNew = async () => {
if (!store.canDraw) return;
const initialGrid = create2dRandomGrid(
store.board.width,
store.board.height
);
store.board.grid = Object.freeze(evolve2d(initialGrid, selectedRules()));
if (store.loop) return draw2dNext(store.board);
else draw2d(store.board);
store.toggleStop();
};
// draw 2d automaton from the last known generated board
const draw2dLast = async () => {
if (!store.canDraw) return;
if (store.loop) return draw2dNext(store.board);
else draw2d(store.board);
store.toggleStop();
};
// draw 2d automaton from an uploaded picture.
// use the picture representation as an initial state
const draw2dPicture = async () => {
store.renderer.renderImage(
store.picture,
store.renderer.ctx,
store.renderer.width,
store.renderer.height
);
const resized = store.renderer.renderImage(
store.picture,
store.renderer.workCtx,
store.board.width,
store.board.height
);
const newBoard = picToBoard(resized.data, store.board);
store.board.grid = Object.freeze(newBoard);
store.toggleStop();
};
const reset = () => {
store.toggleStop();
store.board.grid = null;
store.renderer.reset();
store.toggleReset();
};
</script>
<template>
<canvas
id="board-canvas"
ref="board-canvas"
:width="store.renderer.width"
:height="store.renderer.height"
/>
</template>
<style>
#canvas-board {
flex: 1;
margin: 0 auto;
}
</style>