Compare commits
No commits in common. "495313a5c3271af627160e4f533aef99f4c5f991" and "1644c8c5ce0b7c2e72e891fcce62fda9f454f12d" have entirely different histories.
495313a5c3
...
1644c8c5ce
97
src/App.vue
97
src/App.vue
@ -1,55 +1,72 @@
|
|||||||
<script setup>
|
|
||||||
import MainMenu from "./components/MainMenu.vue";
|
|
||||||
import CanvasBoard from "./components/CanvasBoard.vue";
|
|
||||||
import MenuReset from "./components/MenuReset.vue";
|
|
||||||
import { globalStore } from "./stores/index.js";
|
|
||||||
import { nextTick, onBeforeUnmount, onMounted, ref } from "vue";
|
|
||||||
|
|
||||||
const store = globalStore();
|
|
||||||
|
|
||||||
const windowWidth = ref(window.innerWidth);
|
|
||||||
|
|
||||||
const toggleMainMenu = () => {
|
|
||||||
store.setMainMenu(!store.mainMenu);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onResize = () => {
|
|
||||||
nextTick(() => {
|
|
||||||
windowWidth.value = window.innerWidth;
|
|
||||||
store.canvasWidth = window.innerWidth;
|
|
||||||
store.canvasHeight = window.innerHeight;
|
|
||||||
store.setBoardWidth();
|
|
||||||
store.setBoardHeight();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
nextTick(() => {
|
|
||||||
window.addEventListener("resize", onResize);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener("resize", onResize);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<h1 id="main-title">
|
<h1 id="main-title">
|
||||||
<span id="burger-toggle" @click="toggleMainMenu">
|
<span id="burger-toggle" @click="toggleMainMenu">{{
|
||||||
{{ store.mainMenu == true ? "▼" : "☰" }}
|
mainMenu == true ? "▼" : "☰"
|
||||||
</span>
|
}}</span>
|
||||||
Cellular Automata Explorer
|
Cellular Automata Explorer
|
||||||
</h1>
|
</h1>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<MainMenu v-if="store.mainMenu || windowWidth >= 800" />
|
<MainMenu v-if="mainMenu || windowWidth >= 800" />
|
||||||
<CanvasBoard />
|
<CanvasBoard />
|
||||||
</div>
|
</div>
|
||||||
<MenuReset row-title="" />
|
<MenuReset row-title="" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MainMenu from "./components/MainMenu.vue";
|
||||||
|
import CanvasBoard from "./components/CanvasBoard.vue";
|
||||||
|
import MenuReset from "./components/MenuReset.vue";
|
||||||
|
import { mapState, mapWritableState, mapActions } from "pinia";
|
||||||
|
import { globalStore } from "./stores/index.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "App",
|
||||||
|
components: {
|
||||||
|
MainMenu,
|
||||||
|
MenuReset,
|
||||||
|
CanvasBoard,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
windowWidth: window.innerWidth,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(globalStore, ["mainMenu", "activeSubMenu"]),
|
||||||
|
...mapWritableState(globalStore, ["canvasWidth", "canvasHeight"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
window.addEventListener("resize", this.onResize);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
window.removeEventListener("resize", this.onResize);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(globalStore, [
|
||||||
|
"setBoardWidth",
|
||||||
|
"setBoardHeight",
|
||||||
|
"setMainMenu",
|
||||||
|
]),
|
||||||
|
toggleMainMenu() {
|
||||||
|
this.setMainMenu(!this.mainMenu);
|
||||||
|
},
|
||||||
|
onResize() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.windowWidth = window.innerWidth;
|
||||||
|
this.canvasWidth = window.innerWidth;
|
||||||
|
this.canvasHeight = window.innerHeight;
|
||||||
|
this.setBoardWidth();
|
||||||
|
this.setBoardHeight();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scope>
|
<style scope>
|
||||||
:root {
|
:root {
|
||||||
--dark1: #000000;
|
--dark1: #000000;
|
||||||
|
@ -1,5 +1,19 @@
|
|||||||
<script setup>
|
<template>
|
||||||
import { onMounted, watch } from "vue";
|
<canvas
|
||||||
|
id="board-canvas"
|
||||||
|
ref="board-canvas"
|
||||||
|
:width="canvasWidth"
|
||||||
|
:height="canvasHeight"
|
||||||
|
/>
|
||||||
|
<canvas
|
||||||
|
id="work-canvas"
|
||||||
|
ref="work-canvas"
|
||||||
|
:width="canvasWidth"
|
||||||
|
:height="canvasHeight"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||||
import { globalStore } from "../stores/index.js";
|
import { globalStore } from "../stores/index.js";
|
||||||
import {
|
import {
|
||||||
create1dState,
|
create1dState,
|
||||||
@ -17,214 +31,224 @@
|
|||||||
import { getRandomInt } from "../modules/common.js";
|
import { getRandomInt } from "../modules/common.js";
|
||||||
import { boardToPic, picToBoard } from "../modules/picture.js";
|
import { boardToPic, picToBoard } from "../modules/picture.js";
|
||||||
|
|
||||||
const store = globalStore();
|
export default {
|
||||||
|
name: "CanvasBoard",
|
||||||
// TODO: Do we really need to declare a work canvas in this scope?
|
data() {
|
||||||
// Do we really need to declare a canvas here at all?
|
return {
|
||||||
let canvas = null;
|
board: null,
|
||||||
let workCanvas = null;
|
canvas: null,
|
||||||
let workCtx = null;
|
workCanvas: null,
|
||||||
let ctx = null;
|
workCtx: null,
|
||||||
const available2dRules = {
|
ctx: null,
|
||||||
conway: conwayRules,
|
available2dRules: {
|
||||||
overpopulation: overpopulationRules,
|
conway: conwayRules,
|
||||||
loneliness: lonelinessRules,
|
overpopulation: overpopulationRules,
|
||||||
threeborn: threebornRules,
|
loneliness: lonelinessRules,
|
||||||
highlife: highLifeRules,
|
threeborn: threebornRules,
|
||||||
serviette: servietteRules,
|
highlife: highLifeRules,
|
||||||
};
|
serviette: servietteRules,
|
||||||
|
},
|
||||||
// used to determine the dimensions of the board
|
};
|
||||||
const max = () => {
|
},
|
||||||
return Math.max(store.boardWidth, store.boardHeight);
|
computed: {
|
||||||
};
|
...mapState(globalStore, {
|
||||||
|
loop: "loop",
|
||||||
const selectedRules = () => {
|
cellProperties: "cellProperties",
|
||||||
return available2dRules[store.selected2dRules.id];
|
ruleset: "ruleset1d",
|
||||||
};
|
refreshRate: "refreshRate",
|
||||||
|
initial1dState: "initial1dState",
|
||||||
watch(
|
drawingDirection: "drawingDirection",
|
||||||
() => store.draw1d,
|
canDraw: "canDraw",
|
||||||
(value) => {
|
getDraw1d: "draw1d",
|
||||||
if (value == true) draw1d();
|
getDraw2d: "draw2d",
|
||||||
}
|
getDraw2dLast: "draw2dLast",
|
||||||
);
|
getDraw2dPicture: "draw2dpicture",
|
||||||
|
boardWidth: "boardWidth",
|
||||||
watch(
|
boardHeight: "boardHeight",
|
||||||
() => store.draw2d,
|
selected2dRules: "selected2dRules",
|
||||||
(value) => {
|
picture: "picture",
|
||||||
if (value == true) draw2dNew();
|
}),
|
||||||
}
|
...mapWritableState(globalStore, {
|
||||||
);
|
lastBoard: "lastBoard",
|
||||||
|
canvasWidth: "canvasWidth",
|
||||||
watch(
|
canvasHeight: "canvasHeight",
|
||||||
() => store.draw2dLast,
|
getReset: "reset",
|
||||||
async (value) => {
|
}),
|
||||||
if (value == true) await draw2dLast();
|
// used to determine the dimensions of the board
|
||||||
}
|
max() {
|
||||||
);
|
return Math.max(this.boardWidth, this.boardHeight);
|
||||||
|
},
|
||||||
watch(
|
selectedRules() {
|
||||||
() => store.draw2dpicture,
|
return this.available2dRules[this.selected2dRules.id];
|
||||||
(value) => {
|
},
|
||||||
if (value == true) draw2dPicture();
|
},
|
||||||
}
|
watch: {
|
||||||
);
|
getDraw1d(value) {
|
||||||
|
if (value == true) this.draw1d();
|
||||||
watch(
|
},
|
||||||
() => store.reset,
|
getDraw2d(value) {
|
||||||
(value) => {
|
if (value == true) this.draw2dNew();
|
||||||
if (value == true) reset();
|
},
|
||||||
}
|
async getDraw2dLast(value) {
|
||||||
);
|
if (value == true) await this.draw2dLast();
|
||||||
|
},
|
||||||
onMounted(() => {
|
getDraw2dPicture(value) {
|
||||||
canvas = Object.freeze(document.getElementById("board-canvas"));
|
if (value == true) this.draw2dPicture();
|
||||||
workCanvas = Object.freeze(document.getElementById("work-canvas"));
|
},
|
||||||
ctx = canvas.getContext("2d", { willReadFrequently: true });
|
getReset(value) {
|
||||||
workCtx = workCanvas.getContext("2d", {
|
if (value == true) this.reset();
|
||||||
willReadFrequently: true,
|
},
|
||||||
});
|
},
|
||||||
store.canvasWidth = canvas.parentElement.clientWidth;
|
mounted() {
|
||||||
store.canvasHeight = canvas.parentElement.clientHeight;
|
this.canvas = Object.freeze(document.getElementById("board-canvas"));
|
||||||
store.setBoardWidth();
|
this.workCanvas = Object.freeze(document.getElementById("work-canvas"));
|
||||||
store.setBoardHeight();
|
this.ctx = this.canvas.getContext("2d", { willReadFrequently: true });
|
||||||
});
|
this.workCtx = this.workCanvas.getContext("2d", {
|
||||||
|
willReadFrequently: true,
|
||||||
// draws the board on the canvas
|
|
||||||
const drawCanvas = async (board, width, height) => {
|
|
||||||
const d = store.cellProperties.size;
|
|
||||||
// bool to RGBA colors
|
|
||||||
const img = await boardToPic(board, width, height, store.cellProperties);
|
|
||||||
// rescale and draw
|
|
||||||
ctx.save();
|
|
||||||
ctx.clearRect(0, 0, store.canvasWidth, store.canvasHeight);
|
|
||||||
workCtx.putImageData(img, 0, 0);
|
|
||||||
ctx.imageSmoothingEnabled = false;
|
|
||||||
ctx.scale(d, d);
|
|
||||||
ctx.drawImage(workCanvas, 0, 0, store.canvasWidth, store.canvasHeight);
|
|
||||||
ctx.restore();
|
|
||||||
};
|
|
||||||
|
|
||||||
// create a first state, either a single living cell
|
|
||||||
// at the center or random ones
|
|
||||||
const compute1dInitialState = () => {
|
|
||||||
if (store.initial1dState === "onecell")
|
|
||||||
return create1dStateOneCell(store.boardWidth);
|
|
||||||
return create1dState(store.boardWidth, getRandomInt, [0, 2]);
|
|
||||||
};
|
|
||||||
|
|
||||||
// initialize board with random cells
|
|
||||||
const randomInitialState = () => {
|
|
||||||
return create2dState(
|
|
||||||
store.boardWidth,
|
|
||||||
store.boardHeight,
|
|
||||||
getRandomInt,
|
|
||||||
[0, 2]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// draw elementary automaton on the canvas based on selected ruleset
|
|
||||||
const draw1d = () => {
|
|
||||||
const initialState = compute1dInitialState();
|
|
||||||
const board = createBoard(initialState, store.ruleset1d.rules, max());
|
|
||||||
store.lastBoard = Object.freeze(board);
|
|
||||||
// TODO: the board clearly could be an object
|
|
||||||
drawCanvas(store.lastBoard, store.boardWidth, store.boardHeight);
|
|
||||||
store.toggleStop();
|
|
||||||
};
|
|
||||||
|
|
||||||
// draw 2D automaton on the canvas in a loop
|
|
||||||
const draw2d = (board) => {
|
|
||||||
const newBoard = evolve2d(board, selectedRules());
|
|
||||||
drawCanvas(newBoard, store.boardWidth, store.boardHeight);
|
|
||||||
store.lastBoard = Object.freeze(newBoard);
|
|
||||||
};
|
|
||||||
|
|
||||||
// draw 2d automaton in a loop, starting from passed state
|
|
||||||
const draw2dNext = async (board) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
if (!store.canDraw) return;
|
|
||||||
draw2d(board);
|
|
||||||
return draw2dNext(store.lastBoard);
|
|
||||||
});
|
});
|
||||||
}, store.refreshRate);
|
this.canvasWidth = this.canvas.parentElement.clientWidth;
|
||||||
};
|
this.canvasHeight = this.canvas.parentElement.clientHeight;
|
||||||
|
this.setBoardWidth();
|
||||||
|
this.setBoardHeight();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(globalStore, [
|
||||||
|
"toggleStop",
|
||||||
|
"setBoardWidth",
|
||||||
|
"setBoardHeight",
|
||||||
|
]),
|
||||||
|
// draws the board on the canvas
|
||||||
|
async drawCanvas(board, width, height) {
|
||||||
|
const d = this.cellProperties.size;
|
||||||
|
// bool to RGBA colors
|
||||||
|
const img = await boardToPic(board, width, height, this.cellProperties);
|
||||||
|
// rescale and draw
|
||||||
|
this.ctx.save();
|
||||||
|
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
|
||||||
|
this.workCtx.putImageData(img, 0, 0);
|
||||||
|
this.ctx.imageSmoothingEnabled = false;
|
||||||
|
this.ctx.scale(d, d);
|
||||||
|
this.ctx.drawImage(
|
||||||
|
this.workCanvas,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this.canvasWidth,
|
||||||
|
this.canvasHeight
|
||||||
|
);
|
||||||
|
this.ctx.restore();
|
||||||
|
},
|
||||||
|
// create a first state, either a single living cell
|
||||||
|
// at the center or random ones
|
||||||
|
compute1dInitialState() {
|
||||||
|
if (this.initial1dState === "onecell")
|
||||||
|
return create1dStateOneCell(this.boardWidth);
|
||||||
|
return create1dState(this.boardWidth, getRandomInt, [0, 2]);
|
||||||
|
},
|
||||||
|
// initialize board with random cells
|
||||||
|
randomInitialState() {
|
||||||
|
return create2dState(
|
||||||
|
this.boardWidth,
|
||||||
|
this.boardHeight,
|
||||||
|
getRandomInt,
|
||||||
|
[0, 2]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// draw elementary automaton on the canvas based on selected ruleset
|
||||||
|
draw1d() {
|
||||||
|
const initialState = this.compute1dInitialState();
|
||||||
|
const board = createBoard(initialState, this.ruleset.rules, this.max);
|
||||||
|
this.lastBoard = Object.freeze(board);
|
||||||
|
this.drawCanvas(this.lastBoard, this.boardWidth, this.boardHeight);
|
||||||
|
this.toggleStop();
|
||||||
|
},
|
||||||
|
// draw 2D automaton on the canvas in a loop
|
||||||
|
draw2d(board) {
|
||||||
|
const newBoard = evolve2d(board, this.selectedRules);
|
||||||
|
this.drawCanvas(newBoard, this.boardWidth, this.boardHeight);
|
||||||
|
this.lastBoard = Object.freeze(newBoard);
|
||||||
|
},
|
||||||
|
// draw 2d automaton in a loop, starting from passed state
|
||||||
|
async draw2dNext(board, time) {
|
||||||
|
setTimeout(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (!this.canDraw) return;
|
||||||
|
this.draw2d(board);
|
||||||
|
return this.draw2dNext(this.lastBoard);
|
||||||
|
});
|
||||||
|
}, this.refreshRate);
|
||||||
|
},
|
||||||
|
// draw 2d automaton from a new state
|
||||||
|
async draw2dNew() {
|
||||||
|
if (!this.canDraw) return;
|
||||||
|
const initialState = this.randomInitialState();
|
||||||
|
let board = evolve2d(initialState, this.selectedRules);
|
||||||
|
if (this.loop) return this.draw2dNext(board);
|
||||||
|
else this.draw2d(board);
|
||||||
|
this.toggleStop();
|
||||||
|
},
|
||||||
|
// draw 2d automaton from the last known generated board
|
||||||
|
async draw2dLast() {
|
||||||
|
if (!this.canDraw) return;
|
||||||
|
if (this.loop) return this.draw2dNext(this.lastBoard);
|
||||||
|
else this.draw2d(this.lastBoard);
|
||||||
|
this.toggleStop();
|
||||||
|
},
|
||||||
|
// draw 2d automaton from an uploaded picture.
|
||||||
|
// use the picture representation as an initial state
|
||||||
|
draw2dPicture() {
|
||||||
|
// draw image on canvas
|
||||||
|
this.ctx.fillStyle = "black";
|
||||||
|
this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
|
||||||
|
this.ctx.drawImage(
|
||||||
|
this.picture,
|
||||||
|
Math.floor((this.canvasWidth - this.picture.width) / 2),
|
||||||
|
Math.floor((this.canvasHeight - this.picture.height) / 2),
|
||||||
|
this.picture.width,
|
||||||
|
this.picture.height
|
||||||
|
);
|
||||||
|
|
||||||
// draw 2d automaton from a new state
|
// get image data from canvas
|
||||||
const draw2dNew = async () => {
|
const imgData = this.ctx.getImageData(
|
||||||
if (!store.canDraw) return;
|
0,
|
||||||
const initialState = randomInitialState();
|
0,
|
||||||
const board = evolve2d(initialState, selectedRules());
|
this.canvasWidth,
|
||||||
if (store.loop) return draw2dNext(board);
|
this.canvasHeight
|
||||||
else draw2d(board);
|
);
|
||||||
store.toggleStop();
|
|
||||||
};
|
|
||||||
|
|
||||||
// draw 2d automaton from the last known generated board
|
// draw the image back on the work canvas with the dimensions of the board
|
||||||
const draw2dLast = async () => {
|
this.workCtx.drawImage(
|
||||||
if (!store.canDraw) return;
|
this.picture,
|
||||||
if (store.loop) return draw2dNext(store.lastBoard);
|
0,
|
||||||
else draw2d(store.lastBoard);
|
0,
|
||||||
store.toggleStop();
|
this.boardWidth,
|
||||||
};
|
this.boardHeight
|
||||||
|
);
|
||||||
|
|
||||||
// draw 2d automaton from an uploaded picture.
|
// get the resized image data from work canvas
|
||||||
// use the picture representation as an initial state
|
const resized = this.workCtx.getImageData(
|
||||||
const draw2dPicture = () => {
|
0,
|
||||||
// draw image on canvas
|
0,
|
||||||
ctx.fillStyle = "black";
|
this.boardWidth,
|
||||||
ctx.fillRect(0, 0, store.canvasWidth, store.canvasHeight);
|
this.boardHeight
|
||||||
ctx.drawImage(
|
);
|
||||||
store.picture,
|
|
||||||
Math.floor((store.canvasWidth - store.picture.width) / 2),
|
|
||||||
Math.floor((store.canvasHeight - store.picture.height) / 2),
|
|
||||||
store.picture.width,
|
|
||||||
store.picture.height
|
|
||||||
);
|
|
||||||
|
|
||||||
// draw the image back on the work canvas with the dimensions of the board
|
// convert the image into a 2D board of boolean based on pixel value
|
||||||
workCtx.drawImage(store.picture, 0, 0, store.boardWidth, store.boardHeight);
|
this.lastBoard = Object.freeze(
|
||||||
|
picToBoard(resized.data, this.boardWidth, this.boardHeight)
|
||||||
|
);
|
||||||
|
|
||||||
// get the resized image data from work canvas
|
this.toggleStop();
|
||||||
const resized = workCtx.getImageData(
|
},
|
||||||
0,
|
// stop drawing routines and clear the canvas
|
||||||
0,
|
reset() {
|
||||||
store.boardWidth,
|
this.toggleStop();
|
||||||
store.boardHeight
|
this.lastBoard = {};
|
||||||
);
|
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
|
||||||
|
this.getReset = 0;
|
||||||
// convert the image into a 2D board of boolean based on pixel value
|
},
|
||||||
store.lastBoard = Object.freeze(
|
},
|
||||||
picToBoard(resized.data, store.boardWidth, store.boardHeight)
|
|
||||||
);
|
|
||||||
|
|
||||||
store.toggleStop();
|
|
||||||
};
|
|
||||||
|
|
||||||
// stop drawing routines and clear the canvas
|
|
||||||
const reset = () => {
|
|
||||||
store.toggleStop();
|
|
||||||
store.lastBoard = null;
|
|
||||||
ctx.clearRect(0, 0, store.canvasWidth, store.canvasHeight);
|
|
||||||
store.reset = false;
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
|
||||||
<canvas
|
|
||||||
id="board-canvas"
|
|
||||||
ref="board-canvas"
|
|
||||||
:width="store.canvasWidth"
|
|
||||||
:height="store.canvasHeight"
|
|
||||||
/>
|
|
||||||
<canvas
|
|
||||||
id="work-canvas"
|
|
||||||
ref="work-canvas"
|
|
||||||
:width="store.canvasWidth"
|
|
||||||
:height="store.canvasHeight"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<style>
|
<style>
|
||||||
#canvas-board {
|
#canvas-board {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -7,11 +7,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script>
|
||||||
import MenuCellProperties from "./MenuCellProperties.vue";
|
import MenuCellProperties from "./MenuCellProperties.vue";
|
||||||
import MenuGeneralOptions from "./MenuGeneralOptions.vue";
|
import MenuGeneralOptions from "./MenuGeneralOptions.vue";
|
||||||
import MenuElementaryCA from "./MenuElementaryCA.vue";
|
import MenuElementaryCA from "./MenuElementaryCA.vue";
|
||||||
import Menu2dCA from "./Menu2dCA.vue";
|
import Menu2dCA from "./Menu2dCA.vue";
|
||||||
|
export default {
|
||||||
|
name: "MainMenu",
|
||||||
|
components: {
|
||||||
|
MenuCellProperties,
|
||||||
|
MenuGeneralOptions,
|
||||||
|
MenuElementaryCA,
|
||||||
|
Menu2dCA,
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -1,48 +1,3 @@
|
|||||||
<script setup>
|
|
||||||
import MenuRow from "./MenuRow.vue";
|
|
||||||
import { globalStore } from "../stores/index.js";
|
|
||||||
import { preset2dRules } from "../modules/preset.js";
|
|
||||||
import { shallowRef } from "vue";
|
|
||||||
|
|
||||||
const store = globalStore();
|
|
||||||
|
|
||||||
const uploadedPicture = shallowRef(null);
|
|
||||||
const img = new Image();
|
|
||||||
|
|
||||||
// TODO : I have no idea why this works
|
|
||||||
const preparePicture = () => {
|
|
||||||
const file = uploadedPicture.value.files[0];
|
|
||||||
if (!file || file.type.indexOf("image/") !== 0) return;
|
|
||||||
|
|
||||||
if (FileReader && file) {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
|
|
||||||
reader.onload = (event) => {
|
|
||||||
img.onload = () => {
|
|
||||||
store.picture.width = img.width;
|
|
||||||
store.picture.height = img.height;
|
|
||||||
};
|
|
||||||
store.picture.src = event.target.result;
|
|
||||||
store.toggle2dDrawFromPicture();
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.onerror = () => {
|
|
||||||
console.log(reader.error);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const update2dRules = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
const id = elem.value;
|
|
||||||
const newRuleset = preset2dRules.find((ruleset) => {
|
|
||||||
return ruleset.id === id;
|
|
||||||
});
|
|
||||||
store.selected2dRules = newRuleset;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MenuRow row-title="2D Cellular Automaton">
|
<MenuRow row-title="2D Cellular Automaton">
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
@ -51,16 +6,16 @@
|
|||||||
type="button"
|
type="button"
|
||||||
name="start2d"
|
name="start2d"
|
||||||
value="start"
|
value="start"
|
||||||
@click="store.toggleDraw2d()"
|
@click="toggleDraw2d()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Start from last result</label>
|
<label>Start from last result</label>
|
||||||
<input type="button" value="start" @click="store.toggleDraw2dLast()" />
|
<input type="button" value="start" @click="toggleDraw2dLast()" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Start from picture</label><br />
|
<label>Start from picture</label><br />
|
||||||
<input ref="uploadedPicture" type="file" @change="preparePicture" />
|
<input type="file" @change="preparePicture" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>
|
<label>
|
||||||
@ -68,7 +23,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<select
|
<select
|
||||||
name="preset2dRules"
|
name="preset2dRules"
|
||||||
:value="store.selected2dRules.id"
|
:value="selected2dRules.id"
|
||||||
@input="update2dRules"
|
@input="update2dRules"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
@ -83,3 +38,58 @@
|
|||||||
</div>
|
</div>
|
||||||
</MenuRow>
|
</MenuRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MenuRow from "./MenuRow.vue";
|
||||||
|
import { mapActions, mapWritableState } from "pinia";
|
||||||
|
import { globalStore } from "../stores/index.js";
|
||||||
|
import { preset2dRules } from "../modules/preset.js";
|
||||||
|
export default {
|
||||||
|
name: "Menu2dCA",
|
||||||
|
components: {
|
||||||
|
MenuRow,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
uploadedFile: "",
|
||||||
|
preset2dRules: preset2dRules,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapWritableState(globalStore, ["picture", "selected2dRules"]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(globalStore, [
|
||||||
|
"toggleDraw2dLast",
|
||||||
|
"toggleDraw2d",
|
||||||
|
"toggle2dDrawFromPicture",
|
||||||
|
]),
|
||||||
|
preparePicture(event) {
|
||||||
|
const files = event.target.files;
|
||||||
|
this.picture = new Image();
|
||||||
|
if (FileReader && files && files.length) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = () => {
|
||||||
|
this.picture.src = Object.freeze(reader.result);
|
||||||
|
this.toggle2dDrawFromPicture();
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = () => {
|
||||||
|
console.log(reader.error);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(files[0]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update2dRules(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
const id = elem.value;
|
||||||
|
const newRuleset = this.preset2dRules.find((ruleset) => {
|
||||||
|
return ruleset.id === id;
|
||||||
|
});
|
||||||
|
this.selected2dRules = newRuleset;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
@ -1,24 +1,3 @@
|
|||||||
<script setup>
|
|
||||||
import { globalStore } from "../stores/index.js";
|
|
||||||
import MenuRow from "./MenuRow.vue";
|
|
||||||
|
|
||||||
const store = globalStore();
|
|
||||||
|
|
||||||
const updateCellProperties = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
store.cellProperties[elem.name] = elem.value;
|
|
||||||
store.setBoardWidth();
|
|
||||||
store.setBoardHeight();
|
|
||||||
};
|
|
||||||
|
|
||||||
const switchColor = () => {
|
|
||||||
[store.cellProperties["liveColor"], store.cellProperties["deadColor"]] = [
|
|
||||||
store.cellProperties["deadColor"],
|
|
||||||
store.cellProperties["liveColor"],
|
|
||||||
];
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MenuRow row-title="Cell Properties">
|
<MenuRow row-title="Cell Properties">
|
||||||
<form>
|
<form>
|
||||||
@ -27,7 +6,7 @@
|
|||||||
<input
|
<input
|
||||||
name="liveColor"
|
name="liveColor"
|
||||||
type="color"
|
type="color"
|
||||||
:value="store.cellProperties.liveColor"
|
:value="cellProperties.liveColor"
|
||||||
@input="updateCellProperties"
|
@input="updateCellProperties"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -36,7 +15,7 @@
|
|||||||
<input
|
<input
|
||||||
name="deadColor"
|
name="deadColor"
|
||||||
type="color"
|
type="color"
|
||||||
:value="store.cellProperties.deadColor"
|
:value="cellProperties.deadColor"
|
||||||
@input="updateCellProperties"
|
@input="updateCellProperties"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -49,7 +28,7 @@
|
|||||||
name="size"
|
name="size"
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
:value="store.cellProperties.size"
|
:value="cellProperties.size"
|
||||||
@click="updateCellProperties"
|
@click="updateCellProperties"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -57,6 +36,40 @@
|
|||||||
</MenuRow>
|
</MenuRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapWritableState } from "pinia";
|
||||||
|
import { globalStore } from "../stores/index.js";
|
||||||
|
import MenuRow from "./MenuRow.vue";
|
||||||
|
export default {
|
||||||
|
name: "MenuCellProperties",
|
||||||
|
components: {
|
||||||
|
MenuRow,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapWritableState(globalStore, ["cellProperties"]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(globalStore, ["setBoardHeight", "setBoardWidth"]),
|
||||||
|
getCellProperties(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
const prop = this.cellProperties;
|
||||||
|
return prop[elem.name];
|
||||||
|
},
|
||||||
|
updateCellProperties(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
this.cellProperties[elem.name] = elem.value;
|
||||||
|
this.setBoardWidth();
|
||||||
|
this.setBoardHeight();
|
||||||
|
},
|
||||||
|
switchColor() {
|
||||||
|
[this.cellProperties["liveColor"], this.cellProperties["deadColor"]] = [
|
||||||
|
this.cellProperties["deadColor"],
|
||||||
|
this.cellProperties["liveColor"],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
a {
|
a {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -1,36 +1,3 @@
|
|||||||
<script setup>
|
|
||||||
import { presetRuleset, initialStates } from "../modules/preset.js";
|
|
||||||
import { globalStore } from "../stores/index.js";
|
|
||||||
import MenuRow from "./MenuRow.vue";
|
|
||||||
|
|
||||||
const store = globalStore();
|
|
||||||
|
|
||||||
const copyRuleset = () => {
|
|
||||||
const newRuleset = JSON.stringify(store.ruleset1d);
|
|
||||||
navigator.clipboard.writeText(newRuleset);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateSingleRule = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
const value = elem.checked ? 1 : 0;
|
|
||||||
store.ruleset1d.rules[elem.name] = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateRuleset = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
const name = elem.value;
|
|
||||||
const newRuleset = presetRuleset.find((ruleset) => {
|
|
||||||
return ruleset.name === name;
|
|
||||||
});
|
|
||||||
store.ruleset1d = newRuleset;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateInitialState = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
store.initial1dState = elem.value;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MenuRow row-title="Elementary Automaton">
|
<MenuRow row-title="Elementary Automaton">
|
||||||
<form>
|
<form>
|
||||||
@ -39,7 +6,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
name="start"
|
name="start"
|
||||||
value="start"
|
value="start"
|
||||||
@click="store.toggleDraw1d()"
|
@click="toggleDraw1d()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
@ -48,7 +15,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<select
|
<select
|
||||||
name="initialStates"
|
name="initialStates"
|
||||||
:value="store.initial1dState"
|
:value="initialState"
|
||||||
@input="updateInitialState"
|
@input="updateInitialState"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
@ -70,7 +37,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<select
|
<select
|
||||||
name="ruleset-elementary"
|
name="ruleset-elementary"
|
||||||
:value="store.ruleset1d.name"
|
:value="ruleset.name"
|
||||||
@input="updateRuleset"
|
@input="updateRuleset"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
@ -87,7 +54,7 @@
|
|||||||
<a style="cursor: pointer" @click="copyRuleset">copy rules</a>
|
<a style="cursor: pointer" @click="copyRuleset">copy rules</a>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="(rule, name, index) in store.ruleset1d.rules"
|
v-for="(rule, name, index) in ruleset.rules"
|
||||||
:key="'rule-' + index"
|
:key="'rule-' + index"
|
||||||
class="form-field"
|
class="form-field"
|
||||||
>
|
>
|
||||||
@ -106,6 +73,68 @@
|
|||||||
</MenuRow>
|
</MenuRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapWritableState } from "pinia";
|
||||||
|
import { presetRuleset, initialStates } from "../modules/preset.js";
|
||||||
|
import { globalStore } from "../stores/index.js";
|
||||||
|
import MenuRow from "./MenuRow.vue";
|
||||||
|
export default {
|
||||||
|
name: "MenuElementaryCA",
|
||||||
|
components: {
|
||||||
|
MenuRow,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
presetRuleset: presetRuleset,
|
||||||
|
initialStates: initialStates,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapWritableState(globalStore, {
|
||||||
|
initialState: "initial1dState",
|
||||||
|
ruleset: "ruleset1d",
|
||||||
|
}),
|
||||||
|
rules1dFileName() {
|
||||||
|
// TODO: broken
|
||||||
|
return (
|
||||||
|
Object.keys(this.ruleset)
|
||||||
|
.map((index) => {
|
||||||
|
return this.ruleset[index];
|
||||||
|
})
|
||||||
|
.join("_") + ".json"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(globalStore, ["toggleDraw1d"]),
|
||||||
|
copyRuleset() {
|
||||||
|
const newRuleset = JSON.stringify(this.ruleset);
|
||||||
|
navigator.clipboard.writeText(newRuleset);
|
||||||
|
},
|
||||||
|
isCurrentPreset(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
return this.initialState === elem.value;
|
||||||
|
},
|
||||||
|
updateSingleRule(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
const value = elem.checked ? 1 : 0;
|
||||||
|
this.ruleset.rules[elem.name] = value;
|
||||||
|
},
|
||||||
|
updateRuleset(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
const name = elem.value;
|
||||||
|
const newRuleset = this.presetRuleset.find((ruleset) => {
|
||||||
|
return ruleset.name === name;
|
||||||
|
});
|
||||||
|
this.ruleset = newRuleset;
|
||||||
|
},
|
||||||
|
updateInitialState(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
this.initialState = elem.value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.menu-row a {
|
.menu-row a {
|
||||||
color: white;
|
color: white;
|
||||||
|
@ -1,31 +1,3 @@
|
|||||||
<script setup>
|
|
||||||
import { globalStore } from "../stores/index.js";
|
|
||||||
import MenuRow from "./MenuRow.vue";
|
|
||||||
|
|
||||||
const store = globalStore();
|
|
||||||
|
|
||||||
const updateCanvasHeight = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
store.canvasHeight = elem.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateCanvasWidth = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
store.canvasWidth = elem.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateRefreshRate = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
store.refreshRate = elem.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateDrawingDirection = (event) => {
|
|
||||||
const elem = event.target;
|
|
||||||
const value = elem.checked ? "x" : "y";
|
|
||||||
store.drawingDirection = value;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MenuRow row-title="General Options">
|
<MenuRow row-title="General Options">
|
||||||
<form>
|
<form>
|
||||||
@ -39,7 +11,7 @@
|
|||||||
name="canvasWidth"
|
name="canvasWidth"
|
||||||
type="number"
|
type="number"
|
||||||
step="10"
|
step="10"
|
||||||
:value="store.canvasWidth"
|
:value="canvasWidth"
|
||||||
@input="updateCanvasWidth"
|
@input="updateCanvasWidth"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -50,7 +22,7 @@
|
|||||||
name="canvasHeight"
|
name="canvasHeight"
|
||||||
type="number"
|
type="number"
|
||||||
step="10"
|
step="10"
|
||||||
:value="store.canvasHeight"
|
:value="canvasHeight"
|
||||||
@input="updateCanvasHeight"
|
@input="updateCanvasHeight"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -64,7 +36,7 @@
|
|||||||
type="number"
|
type="number"
|
||||||
min="100"
|
min="100"
|
||||||
step="100"
|
step="100"
|
||||||
:value="store.refreshRate"
|
:value="refreshRate"
|
||||||
@input="updateRefreshRate"
|
@input="updateRefreshRate"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -73,8 +45,8 @@
|
|||||||
>Invert Drawing Direction
|
>Invert Drawing Direction
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="store.drawingDirection === 'x'"
|
:checked="drawingDirection === 'x'"
|
||||||
:value="store.drawingDirection"
|
:value="drawingDirection"
|
||||||
@input="updateDrawingDirection"
|
@input="updateDrawingDirection"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@ -82,3 +54,42 @@
|
|||||||
</form>
|
</form>
|
||||||
</MenuRow>
|
</MenuRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapWritableState } from "pinia";
|
||||||
|
import { globalStore } from "../stores/index.js";
|
||||||
|
import MenuRow from "./MenuRow.vue";
|
||||||
|
export default {
|
||||||
|
name: "MenuGeneralOptions",
|
||||||
|
components: {
|
||||||
|
MenuRow,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapWritableState(globalStore, [
|
||||||
|
"canvasWidth",
|
||||||
|
"canvasHeight",
|
||||||
|
"refreshRate",
|
||||||
|
"drawingDirection",
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateCanvasHeight: function (event) {
|
||||||
|
const elem = event.target;
|
||||||
|
this.canvasHeight = elem.value;
|
||||||
|
},
|
||||||
|
updateCanvasWidth: function (event) {
|
||||||
|
const elem = event.target;
|
||||||
|
this.canvasWidth = elem.value;
|
||||||
|
},
|
||||||
|
updateRefreshRate: function (event) {
|
||||||
|
const elem = event.target;
|
||||||
|
this.refreshRate = elem.value;
|
||||||
|
},
|
||||||
|
updateDrawingDirection: function (event) {
|
||||||
|
const elem = event.target;
|
||||||
|
const value = elem.checked ? "x" : "y";
|
||||||
|
this.drawingDirection = value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
<script setup>
|
|
||||||
import { globalStore } from "../stores/index.js";
|
|
||||||
|
|
||||||
const store = globalStore();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>
|
<label>
|
||||||
Loop
|
Loop
|
||||||
<input
|
<input
|
||||||
:value="store.loop"
|
:value="loop"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="store.loop"
|
:checked="loop"
|
||||||
@input="store.toggleLoop()"
|
@input="toggleLoop()"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -22,25 +16,43 @@
|
|||||||
name="next"
|
name="next"
|
||||||
class="next"
|
class="next"
|
||||||
value="Next"
|
value="Next"
|
||||||
@click="store.toggleNext()"
|
@click="toggleNext()"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="button"
|
type="button"
|
||||||
name="stop"
|
name="stop"
|
||||||
class="stop"
|
class="stop"
|
||||||
value="stop"
|
value="stop"
|
||||||
@click="store.toggleStop()"
|
@click="toggleStop()"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="button"
|
type="button"
|
||||||
name="reset"
|
name="reset"
|
||||||
class="reset"
|
class="reset"
|
||||||
value="reset"
|
value="reset"
|
||||||
@click="store.toggleReset()"
|
@click="toggleReset()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { globalStore } from "../stores/index.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MenuReset",
|
||||||
|
computed: {
|
||||||
|
...mapState(globalStore, ["loop"]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(globalStore, [
|
||||||
|
"toggleReset",
|
||||||
|
"toggleStop",
|
||||||
|
"toggleLoop",
|
||||||
|
"toggleNext",
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.form-field {
|
.form-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,44 +1,3 @@
|
|||||||
<script setup>
|
|
||||||
import { computed, defineProps, onBeforeUnmount, ref } from "vue";
|
|
||||||
import { globalStore } from "../stores/index.js";
|
|
||||||
|
|
||||||
const store = globalStore();
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
rowTitle: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const content = ref(null);
|
|
||||||
|
|
||||||
const isActive = computed(() => {
|
|
||||||
return props.rowTitle == store.activeSubMenu;
|
|
||||||
});
|
|
||||||
|
|
||||||
const storeActiveSubMenu = () => {
|
|
||||||
window.addEventListener("click", onWindowClick);
|
|
||||||
store.setActiveSubMenu(props.rowTitle);
|
|
||||||
};
|
|
||||||
|
|
||||||
// hides submenu when click is detected outside from it
|
|
||||||
const onWindowClick = (event) => {
|
|
||||||
const form = content.value;
|
|
||||||
if (form != null) {
|
|
||||||
if (!form.contains(event.target)) {
|
|
||||||
store.setActiveSubMenu("");
|
|
||||||
store.setMainMenu(false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener("click", onWindowClick);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="menu-row">
|
<div class="menu-row">
|
||||||
<h2 :id="rowTitle" @click.stop="storeActiveSubMenu">
|
<h2 :id="rowTitle" @click.stop="storeActiveSubMenu">
|
||||||
@ -50,6 +9,57 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import { globalStore } from "../stores/index.js";
|
||||||
|
export default {
|
||||||
|
name: "MenuRow",
|
||||||
|
props: {
|
||||||
|
rowTitle: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(globalStore, ["activeSubMenu"]),
|
||||||
|
isActive() {
|
||||||
|
return this.rowTitle == this.activeSubMenu;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
window.removeEventListener("click", this.onWindowClick);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(globalStore, [
|
||||||
|
"setActiveSubMenu",
|
||||||
|
"toggleMainMenu",
|
||||||
|
"setMainMenu",
|
||||||
|
]),
|
||||||
|
onKeyDown: function (event) {
|
||||||
|
// escape
|
||||||
|
if (event.keyCode == 27) {
|
||||||
|
this.setActiveSubMenu("");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
storeActiveSubMenu() {
|
||||||
|
window.addEventListener("click", this.onWindowClick);
|
||||||
|
this.setActiveSubMenu(this.rowTitle);
|
||||||
|
},
|
||||||
|
// hides submenu when click is detected outside from it
|
||||||
|
onWindowClick(event) {
|
||||||
|
const form = this.$refs.content;
|
||||||
|
if (form != null) {
|
||||||
|
if (!form.contains(event.target)) {
|
||||||
|
this.setActiveSubMenu("");
|
||||||
|
this.setMainMenu(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.menu-row h2 {
|
.menu-row h2 {
|
||||||
font-size: medium;
|
font-size: medium;
|
||||||
|
@ -57,7 +57,7 @@ export function boardToPic(board, width, height, cellProperties) {
|
|||||||
const img = new ImageData(width, height);
|
const img = new ImageData(width, height);
|
||||||
const colors = [hexToRGB(live), hexToRGB(dead)];
|
const colors = [hexToRGB(live), hexToRGB(dead)];
|
||||||
board.flat().reduce((acc, cell, index) => {
|
board.flat().reduce((acc, cell, index) => {
|
||||||
const color = cell === 1 ? colors[0] : colors[1];
|
const color = colors[cell];
|
||||||
const i = index * 4;
|
const i = index * 4;
|
||||||
acc[i] = color[0];
|
acc[i] = color[0];
|
||||||
acc[i + 1] = color[1];
|
acc[i + 1] = color[1];
|
||||||
|
@ -40,7 +40,7 @@ export const globalStore = defineStore("globalStore", {
|
|||||||
draw2dpicture: false,
|
draw2dpicture: false,
|
||||||
reset: false,
|
reset: false,
|
||||||
canDraw: true,
|
canDraw: true,
|
||||||
picture: new Image(),
|
picture: null,
|
||||||
mainMenu: false,
|
mainMenu: false,
|
||||||
activeSubMenu: "",
|
activeSubMenu: "",
|
||||||
loop: false,
|
loop: false,
|
||||||
|
Loading…
Reference in New Issue
Block a user