vue 2 to 3 #1
10
.eslintrc.js
10
.eslintrc.js
@ -5,12 +5,12 @@ module.exports = {
|
||||
},
|
||||
extends: [
|
||||
// add more generic rulesets here, such as:
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-recommended',
|
||||
"prettier"
|
||||
"eslint:recommended",
|
||||
"plugin:vue/vue3-recommended",
|
||||
"prettier",
|
||||
],
|
||||
rules: {
|
||||
// override/add rules settings here, such as:
|
||||
// 'vue/no-unused-vars': 'error'
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
@ -1,30 +1,37 @@
|
||||
# explorata
|
||||
|
||||
Explore 1D and 2D cellular automata, with a few bells and whistles.
|
||||
|
||||
## Project setup
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
||||
### References
|
||||
|
||||
- https://natureofcode.com/book/chapter-7-cellular-automata/
|
||||
- https://en.wikipedia.org/wiki/Hashlife
|
||||
- https://plato.stanford.edu/entries/cellular-automata/supplement.html
|
||||
|
@ -1,5 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
}
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: '3.4'
|
||||
version: "3.4"
|
||||
|
||||
services:
|
||||
explorata:
|
||||
|
14
index.html
14
index.html
@ -1,18 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<title>Explorata</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>
|
||||
Althought you're right to browse the Web with Javascript disabled, Explorata doesn't work properly without it.
|
||||
Please enable Javascript to continue.
|
||||
|
||||
Althought you're right to browse the Web with Javascript disabled,
|
||||
Explorata doesn't work properly without it. Please enable Javascript to
|
||||
continue.
|
||||
</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
|
@ -6,7 +6,8 @@
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src"
|
||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
|
||||
"format": "prettier . --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-vue": "^3.2.0",
|
||||
|
65
src/App.vue
65
src/App.vue
@ -1,71 +1,66 @@
|
||||
<template>
|
||||
<div id="main">
|
||||
<h1
|
||||
id="main-title"
|
||||
>
|
||||
Cellular Automata Explorer
|
||||
</h1>
|
||||
<h1 id="main-title">Cellular Automata Explorer</h1>
|
||||
<div id="container">
|
||||
<MainMenu />
|
||||
<Canvas />
|
||||
<CanvasBoard />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MainMenu from './components/MainMenu.vue'
|
||||
import Canvas from './components/Canvas.vue'
|
||||
import MainMenu from "./components/MainMenu.vue";
|
||||
import CanvasBoard from "./components/CanvasBoard.vue";
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
name: "App",
|
||||
components: {
|
||||
MainMenu,
|
||||
Canvas
|
||||
}
|
||||
}
|
||||
CanvasBoard,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
background: black;
|
||||
color: white;
|
||||
font-family: Courier New;
|
||||
background: black;
|
||||
color: white;
|
||||
font-family: Courier New;
|
||||
}
|
||||
|
||||
canvas {
|
||||
flex: auto;
|
||||
background: #110812;
|
||||
margin-right: 10px;
|
||||
flex: auto;
|
||||
background: #110812;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
font-weight: bold;
|
||||
h1,
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin : 10px auto;
|
||||
font-size: larger;
|
||||
text-align: center;
|
||||
margin: 10px auto;
|
||||
font-size: larger;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#container {
|
||||
display: flex;
|
||||
height: calc(100vh - 100px);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
height: calc(100vh - 100px);
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<main id="mainContent">
|
||||
<canvas
|
||||
id="canvas"
|
||||
ref="canvas"
|
||||
:width="canvasWidth"
|
||||
:height="canvasHeight"
|
||||
/>
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
import { create1dState, create1dStateOneCell, create2dState, createBoard, conwayRules, evolve2d } from '../modules/automata.js'
|
||||
import { getRandomInt, sleep } from '../modules/common.js'
|
||||
import {mapGetters} from 'vuex'
|
||||
export default {
|
||||
name: 'Canvas',
|
||||
data() {
|
||||
return {
|
||||
canvas: null,
|
||||
ctx: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
cellProperties: 'getCellProperties',
|
||||
rules: 'get1dRules',
|
||||
drawing: 'isDrawing',
|
||||
canvasWidth: 'getCanvasWidth',
|
||||
canvasHeight: 'getCanvasHeight',
|
||||
refreshRate: 'getRefreshRate',
|
||||
initial1dState: 'getInitial1dState',
|
||||
drawingDirection: 'getDrawingDirection',
|
||||
lastBoard: 'getLastBoard'
|
||||
}),
|
||||
boardWidth: function() {
|
||||
return Math.floor(
|
||||
this.canvasWidth /
|
||||
this.cellProperties.size)
|
||||
},
|
||||
boardHeight: function() {
|
||||
return Math.floor(
|
||||
this.canvasHeight /
|
||||
this.cellProperties.size)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.canvas = this.$refs['canvas']
|
||||
this.ctx = this.canvas.getContext('2d')
|
||||
this.$store.commit('setCanvasWidth', this.canvas.parentElement.clientWidth)
|
||||
this.$store.commit('setCanvasHeight', this.canvas.parentElement.clientHeight)
|
||||
this.$root.$on('draw1d', () => { this.draw1d() })
|
||||
this.$root.$on('draw2dNew', () => { this.draw2dNew() })
|
||||
this.$root.$on('draw2dLast', () => { this.draw2dLast() })
|
||||
this.$root.$on('reset', () => { this.reset() })
|
||||
this.$root.$on('stop', () => { this.stop() })
|
||||
},
|
||||
methods: {
|
||||
drawCanvas(board) {
|
||||
const props = this.cellProperties
|
||||
board.map((row, y) => {
|
||||
const d = props.size
|
||||
return row.map(
|
||||
(cell, x) => {
|
||||
this.ctx.fillStyle = (
|
||||
() => {
|
||||
if (cell === 1) return props.liveColor
|
||||
return props.deadColor
|
||||
})()
|
||||
if (this.drawingDirection === "x")
|
||||
this.ctx.fillRect(y * d, x * d, d, d)
|
||||
else
|
||||
this.ctx.fillRect(x * d, y * d, d, d)
|
||||
return cell
|
||||
},
|
||||
)
|
||||
})
|
||||
},
|
||||
compute1dInitialState() {
|
||||
if(this.initial1dState === "onecell")
|
||||
return create1dStateOneCell(this.boardWidth)
|
||||
return create1dState(this.boardWidth, getRandomInt, [0, 2])
|
||||
},
|
||||
async draw1d() {
|
||||
const initialState = this.compute1dInitialState()
|
||||
const board = createBoard(
|
||||
initialState,
|
||||
this.rules,
|
||||
this.boardWidth
|
||||
)
|
||||
this.$store.commit('setLastBoard', board)
|
||||
this.drawCanvas(board)
|
||||
},
|
||||
async draw2d(board) {
|
||||
if (this.drawing === 0) return
|
||||
|
||||
const draw2dNext = async (b) => {
|
||||
if (this.drawing === 0) return
|
||||
const newBoard = evolve2d(b, conwayRules)
|
||||
this.drawCanvas(b, this.cellProperties)
|
||||
await sleep(this.refreshRate)
|
||||
draw2dNext(newBoard)
|
||||
}
|
||||
return draw2dNext(board)
|
||||
},
|
||||
async draw2dNew() {
|
||||
const initialState = create2dState(
|
||||
this.boardWidth,
|
||||
this.boardHeight,
|
||||
getRandomInt,
|
||||
[0, 2],
|
||||
);
|
||||
const board = evolve2d(initialState, conwayRules);
|
||||
this.$store.commit('setLastBoard', board)
|
||||
this.draw2d(board)
|
||||
},
|
||||
async draw2dLast() {
|
||||
console.log(this.lastBoard)
|
||||
this.draw2d(this.lastBoard)
|
||||
},
|
||||
stop() {
|
||||
this.$store.commit('setDrawingStatus', 0)
|
||||
},
|
||||
reset() {
|
||||
this.stop()
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
#mainContent {
|
||||
min-width: 70%;
|
||||
}
|
||||
</style>
|
142
src/components/CanvasBoard.vue
Normal file
142
src/components/CanvasBoard.vue
Normal file
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<main id="mainContent">
|
||||
<CanvasBoard
|
||||
id="canvas-board"
|
||||
ref="canvas-board"
|
||||
:width="canvasWidth"
|
||||
:height="canvasHeight"
|
||||
/>
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
create1dState,
|
||||
create1dStateOneCell,
|
||||
create2dState,
|
||||
createBoard,
|
||||
conwayRules,
|
||||
evolve2d,
|
||||
} from "../modules/automata.js";
|
||||
import { getRandomInt, sleep } from "../modules/common.js";
|
||||
import { mapGetters } from "vuex";
|
||||
export default {
|
||||
name: "CanvasBoard",
|
||||
data() {
|
||||
return {
|
||||
canvas: null,
|
||||
ctx: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
cellProperties: "getCellProperties",
|
||||
rules: "get1dRules",
|
||||
drawing: "isDrawing",
|
||||
canvasWidth: "getCanvasWidth",
|
||||
canvasHeight: "getCanvasHeight",
|
||||
refreshRate: "getRefreshRate",
|
||||
initial1dState: "getInitial1dState",
|
||||
drawingDirection: "getDrawingDirection",
|
||||
lastBoard: "getLastBoard",
|
||||
}),
|
||||
boardWidth: function () {
|
||||
return Math.floor(this.canvasWidth / this.cellProperties.size);
|
||||
},
|
||||
boardHeight: function () {
|
||||
return Math.floor(this.canvasHeight / this.cellProperties.size);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.canvas = this.$refs["canvas"];
|
||||
this.ctx = this.canvas.getContext("2d");
|
||||
this.$store.commit("setCanvasWidth", this.canvas.parentElement.clientWidth);
|
||||
this.$store.commit(
|
||||
"setCanvasHeight",
|
||||
this.canvas.parentElement.clientHeight
|
||||
);
|
||||
this.$root.$on("draw1d", () => {
|
||||
this.draw1d();
|
||||
});
|
||||
this.$root.$on("draw2dNew", () => {
|
||||
this.draw2dNew();
|
||||
});
|
||||
this.$root.$on("draw2dLast", () => {
|
||||
this.draw2dLast();
|
||||
});
|
||||
this.$root.$on("reset", () => {
|
||||
this.reset();
|
||||
});
|
||||
this.$root.$on("stop", () => {
|
||||
this.stop();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
drawCanvas(board) {
|
||||
const props = this.cellProperties;
|
||||
board.map((row, y) => {
|
||||
const d = props.size;
|
||||
return row.map((cell, x) => {
|
||||
this.ctx.fillStyle = (() => {
|
||||
if (cell === 1) return props.liveColor;
|
||||
return props.deadColor;
|
||||
})();
|
||||
if (this.drawingDirection === "x")
|
||||
this.ctx.fillRect(y * d, x * d, d, d);
|
||||
else this.ctx.fillRect(x * d, y * d, d, d);
|
||||
return cell;
|
||||
});
|
||||
});
|
||||
},
|
||||
compute1dInitialState() {
|
||||
if (this.initial1dState === "onecell")
|
||||
return create1dStateOneCell(this.boardWidth);
|
||||
return create1dState(this.boardWidth, getRandomInt, [0, 2]);
|
||||
},
|
||||
async draw1d() {
|
||||
const initialState = this.compute1dInitialState();
|
||||
const board = createBoard(initialState, this.rules, this.boardWidth);
|
||||
this.$store.commit("setLastBoard", board);
|
||||
this.drawCanvas(board);
|
||||
},
|
||||
async draw2d(board) {
|
||||
if (this.drawing === 0) return;
|
||||
|
||||
const draw2dNext = async (b) => {
|
||||
if (this.drawing === 0) return;
|
||||
const newBoard = evolve2d(b, conwayRules);
|
||||
this.drawCanvas(b, this.cellProperties);
|
||||
await sleep(this.refreshRate);
|
||||
draw2dNext(newBoard);
|
||||
};
|
||||
return draw2dNext(board);
|
||||
},
|
||||
async draw2dNew() {
|
||||
const initialState = create2dState(
|
||||
this.boardWidth,
|
||||
this.boardHeight,
|
||||
getRandomInt,
|
||||
[0, 2]
|
||||
);
|
||||
const board = evolve2d(initialState, conwayRules);
|
||||
this.$store.commit("setLastBoard", board);
|
||||
this.draw2d(board);
|
||||
},
|
||||
async draw2dLast() {
|
||||
console.log(this.lastBoard);
|
||||
this.draw2d(this.lastBoard);
|
||||
},
|
||||
stop() {
|
||||
this.$store.commit("setDrawingStatus", 0);
|
||||
},
|
||||
reset() {
|
||||
this.stop();
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
#mainContent {
|
||||
min-width: 70%;
|
||||
}
|
||||
</style>
|
@ -8,29 +8,28 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MenuCellProperties from './MenuCellProperties.vue'
|
||||
import MenuGeneralOptions from './MenuGeneralOptions.vue'
|
||||
import MenuElementaryCA from './MenuElementaryCA.vue'
|
||||
import Menu2dCA from './Menu2dCA.vue'
|
||||
import MenuCellProperties from "./MenuCellProperties.vue";
|
||||
import MenuGeneralOptions from "./MenuGeneralOptions.vue";
|
||||
import MenuElementaryCA from "./MenuElementaryCA.vue";
|
||||
import Menu2dCA from "./Menu2dCA.vue";
|
||||
export default {
|
||||
name: 'MainMenu',
|
||||
name: "MainMenu",
|
||||
components: {
|
||||
MenuCellProperties,
|
||||
MenuGeneralOptions,
|
||||
MenuElementaryCA,
|
||||
Menu2dCA
|
||||
Menu2dCA,
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#sidebar {
|
||||
width: 25%;
|
||||
padding: 0 10px;
|
||||
overflow-y: scroll;
|
||||
width: 25%;
|
||||
padding: 0 10px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
#sidebar::-webkit-scrollbar {
|
||||
display: none;
|
||||
@ -38,26 +37,26 @@ export default {
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
#sidebar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
#container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
#container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#mainContent {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
#mainContent {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#sidebar {
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -2,65 +2,56 @@
|
||||
<MenuRow row-title="2D Cellular Automata">
|
||||
<div class="form-field">
|
||||
<label>Start from last result</label>
|
||||
<input
|
||||
type="button"
|
||||
value="start"
|
||||
@click="startFromLast"
|
||||
>
|
||||
<input type="button" value="start" @click="startFromLast" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<input
|
||||
type="button"
|
||||
name="start2d"
|
||||
value="start"
|
||||
@click="draw2d"
|
||||
>
|
||||
<input type="button" name="start2d" value="start" @click="draw2d" />
|
||||
<input
|
||||
type="button"
|
||||
name="stop"
|
||||
class="stop"
|
||||
value="stop"
|
||||
@click="stop"
|
||||
>
|
||||
/>
|
||||
<input
|
||||
type="button"
|
||||
name="reset"
|
||||
class="reset"
|
||||
value="reset"
|
||||
@click="reset"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</MenuRow>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MenuRow from './MenuRow.vue'
|
||||
import {mapGetters} from 'vuex'
|
||||
import MenuRow from "./MenuRow.vue";
|
||||
import { mapGetters } from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'Menu2dCA',
|
||||
name: "Menu2dCA",
|
||||
components: {
|
||||
MenuRow
|
||||
MenuRow,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
lastBoard: 'getLastBoard',
|
||||
})
|
||||
lastBoard: "getLastBoard",
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
draw2d() {
|
||||
this.$root.$store.state.drawing = 1
|
||||
this.$root.$emit('draw2dNew')
|
||||
this.$root.$store.state.drawing = 1;
|
||||
this.$root.$emit("draw2dNew");
|
||||
},
|
||||
reset() {
|
||||
this.$root.$emit('reset')
|
||||
this.$root.$emit("reset");
|
||||
},
|
||||
stop() {
|
||||
this.$root.$emit('stop')
|
||||
this.$root.$emit("stop");
|
||||
},
|
||||
startFromLast() {
|
||||
this.$root.$emit('draw2dLast')
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$root.$emit("draw2dLast");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -8,7 +8,7 @@
|
||||
type="color"
|
||||
@value="cellProperties.liveColor"
|
||||
@input="updateCellProperties"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label for="dead">Dead cell color</label>
|
||||
@ -17,7 +17,7 @@
|
||||
type="color"
|
||||
:value="cellProperties.deadColor"
|
||||
@input="updateCellProperties"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Cell size</label>
|
||||
@ -27,36 +27,36 @@
|
||||
min="1"
|
||||
:value="cellProperties.size"
|
||||
@input="updateCellProperties"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</MenuRow>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MenuRow from './MenuRow.vue'
|
||||
import MenuRow from "./MenuRow.vue";
|
||||
export default {
|
||||
name: 'MainMenu',
|
||||
name: "MainMenu",
|
||||
components: {
|
||||
MenuRow
|
||||
MenuRow,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cellProperties: this.$store.state.cellProperties
|
||||
}
|
||||
cellProperties: this.$store.state.cellProperties,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getCellProperties(event) {
|
||||
const elem = event.target
|
||||
const prop = this.$store.state.cellProperties
|
||||
return prop[elem.name]
|
||||
const elem = event.target;
|
||||
const prop = this.$store.state.cellProperties;
|
||||
return prop[elem.name];
|
||||
},
|
||||
updateCellProperties(event) {
|
||||
const elem = event.target
|
||||
const prop = {'name' : elem.name, 'value' : elem.value}
|
||||
const elem = event.target;
|
||||
const prop = { name: elem.name, value: elem.value };
|
||||
//console.log(prop)
|
||||
this.$store.commit('setCellProperties', prop)
|
||||
}
|
||||
this.$store.commit("setCellProperties", prop);
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -2,8 +2,9 @@
|
||||
<MenuRow row-title="Elementary Cellular Automata">
|
||||
<form>
|
||||
<div class="form-field">
|
||||
<label>Initial state presets
|
||||
<br>
|
||||
<label
|
||||
>Initial state presets
|
||||
<br />
|
||||
<select
|
||||
name="initial1dStates"
|
||||
value="initial1dStates"
|
||||
@ -13,7 +14,8 @@
|
||||
v-for="state in initial1dStates"
|
||||
:key="'preset1d' + state.id"
|
||||
:value="state.id"
|
||||
>{{ state.name }}
|
||||
>
|
||||
{{ state.name }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
@ -22,149 +24,198 @@
|
||||
<label>Rules</label>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Rules presets
|
||||
<br>
|
||||
<select
|
||||
name="rules1d"
|
||||
value="rules"
|
||||
@input="update1dRules"
|
||||
>
|
||||
<label
|
||||
>Rules presets
|
||||
<br />
|
||||
<select name="rules1d" value="rules" @input="update1dRules">
|
||||
<option
|
||||
v-for="(rule, name) in presetRules"
|
||||
:key="'rule1d' + name"
|
||||
:value="name"
|
||||
>{{ name }}
|
||||
>
|
||||
{{ name }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<a
|
||||
style="cursor: pointer;"
|
||||
@click="copy1dRules"
|
||||
>copy rules</a>
|
||||
<a style="cursor: pointer" @click="copy1dRules">copy rules</a>
|
||||
</div>
|
||||
<div
|
||||
v-for="(rule, name) in rules"
|
||||
:key="'rule1d' + name"
|
||||
class="form-field"
|
||||
>
|
||||
<label>{{ name }}
|
||||
<label
|
||||
>{{ name }}
|
||||
<input
|
||||
:value="rule"
|
||||
type="checkbox"
|
||||
:name="name"
|
||||
:checked="rule"
|
||||
@input="update1dSingleRule"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-field">
|
||||
<input
|
||||
type="button"
|
||||
name="start"
|
||||
value="start"
|
||||
@click="draw1d"
|
||||
>
|
||||
<input type="button" name="start" value="start" @click="draw1d" />
|
||||
<input
|
||||
type="button"
|
||||
name="reset"
|
||||
class="reset"
|
||||
value="reset"
|
||||
@click="reset()"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</MenuRow>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex'
|
||||
import MenuRow from './MenuRow.vue'
|
||||
import { mapGetters } from "vuex";
|
||||
import MenuRow from "./MenuRow.vue";
|
||||
export default {
|
||||
name: 'MenuElementaryCA',
|
||||
name: "MenuElementaryCA",
|
||||
components: {
|
||||
MenuRow
|
||||
MenuRow,
|
||||
},
|
||||
data() {
|
||||
// TODO: Why not a getter in the store?
|
||||
return {
|
||||
presetRules: {
|
||||
"rule 73" : {"100":0,"101":0,"110":1,"111":0,"011":1,"010":0,"001":0,"000":1},
|
||||
"rule 86" : {"100":1,"101":0,"110":0,"111":1,"011":0,"010":1,"001":0,"000":1},
|
||||
"rule 90" : {"100":1,"101":0,"110":1,"111":0,"011":0,"010":0,"001":1,"000":0},
|
||||
"rule 45?" : {"100":0,"101":0,"110":1,"111":0,"011":1,"010":0,"001":1,"000":1},
|
||||
"rule 54?" : {"100":1,"101":0,"110":1,"111":1,"011":0,"010":1,"001":1,"000":0},
|
||||
"unknown rule" : {"100":0,"101":0,"110":0,"111":1,"011":0,"010":0,"001":1,"000":1}
|
||||
"rule 73": {
|
||||
100: 0,
|
||||
101: 0,
|
||||
110: 1,
|
||||
111: 0,
|
||||
"011": 1,
|
||||
"010": 0,
|
||||
"001": 0,
|
||||
"000": 1,
|
||||
},
|
||||
"rule 86": {
|
||||
100: 1,
|
||||
101: 0,
|
||||
110: 0,
|
||||
111: 1,
|
||||
"011": 0,
|
||||
"010": 1,
|
||||
"001": 0,
|
||||
"000": 1,
|
||||
},
|
||||
"rule 90": {
|
||||
100: 1,
|
||||
101: 0,
|
||||
110: 1,
|
||||
111: 0,
|
||||
"011": 0,
|
||||
"010": 0,
|
||||
"001": 1,
|
||||
"000": 0,
|
||||
},
|
||||
"rule 45?": {
|
||||
100: 0,
|
||||
101: 0,
|
||||
110: 1,
|
||||
111: 0,
|
||||
"011": 1,
|
||||
"010": 0,
|
||||
"001": 1,
|
||||
"000": 1,
|
||||
},
|
||||
"rule 54?": {
|
||||
100: 1,
|
||||
101: 0,
|
||||
110: 1,
|
||||
111: 1,
|
||||
"011": 0,
|
||||
"010": 1,
|
||||
"001": 1,
|
||||
"000": 0,
|
||||
},
|
||||
"unknown rule": {
|
||||
100: 0,
|
||||
101: 0,
|
||||
110: 0,
|
||||
111: 1,
|
||||
"011": 0,
|
||||
"010": 0,
|
||||
"001": 1,
|
||||
"000": 1,
|
||||
},
|
||||
},
|
||||
initial1dStates: [
|
||||
{ id : "onecell",
|
||||
name : "One cell at center",
|
||||
description : "State with a single cell in the middle",
|
||||
{
|
||||
id: "onecell",
|
||||
name: "One cell at center",
|
||||
description: "State with a single cell in the middle",
|
||||
},
|
||||
{
|
||||
id : "random",
|
||||
name : "Random cell",
|
||||
description : "State populated with random cells",
|
||||
}
|
||||
]
|
||||
}
|
||||
id: "random",
|
||||
name: "Random cell",
|
||||
description: "State populated with random cells",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
initial1dState: 'getInitial1dState',
|
||||
rules: 'get1dRules'
|
||||
initial1dState: "getInitial1dState",
|
||||
rules: "get1dRules",
|
||||
}),
|
||||
rules1dFileName() {
|
||||
return Object.keys(this.rules).map(
|
||||
(index) => {
|
||||
return this.rules[index]
|
||||
}).join("_") + ".json"
|
||||
return (
|
||||
Object.keys(this.rules)
|
||||
.map((index) => {
|
||||
return this.rules[index];
|
||||
})
|
||||
.join("_") + ".json"
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
copy1dRules(){
|
||||
const rules = JSON.stringify(this.rules)
|
||||
navigator.clipboard.writeText(rules)
|
||||
copy1dRules() {
|
||||
const rules = JSON.stringify(this.rules);
|
||||
navigator.clipboard.writeText(rules);
|
||||
},
|
||||
isCurrentPreset(event) {
|
||||
const elem = event.target
|
||||
return this.initialState === elem.value
|
||||
const elem = event.target;
|
||||
return this.initialState === elem.value;
|
||||
},
|
||||
update1dSingleRule(event) {
|
||||
const elem = event.target
|
||||
const value = elem.checked ? 1 : 0
|
||||
const data = { 'rule' : elem.name, 'value' : value}
|
||||
this.$store.commit('update1dSingleRule', data)
|
||||
const elem = event.target;
|
||||
const value = elem.checked ? 1 : 0;
|
||||
const data = { rule: elem.name, value: value };
|
||||
this.$store.commit("update1dSingleRule", data);
|
||||
},
|
||||
update1dRules(event) {
|
||||
const elem = event.target
|
||||
const name = elem.value
|
||||
const rules = this.presetRules[name]
|
||||
const elem = event.target;
|
||||
const name = elem.value;
|
||||
const rules = this.presetRules[name];
|
||||
Object.keys(rules).map((index) => {
|
||||
const data = { 'rule' : index, 'value' : rules[index]}
|
||||
this.$store.commit('update1dSingleRule', data)
|
||||
})
|
||||
const data = { rule: index, value: rules[index] };
|
||||
this.$store.commit("update1dSingleRule", data);
|
||||
});
|
||||
},
|
||||
updateInitial1dState(event) {
|
||||
const elem = event.target
|
||||
this.$store.commit('setInitial1dState', elem.value)
|
||||
const elem = event.target;
|
||||
this.$store.commit("setInitial1dState", elem.value);
|
||||
},
|
||||
draw1d() {
|
||||
this.$root.$store.state.drawing = 1
|
||||
this.$root.$emit('draw1d')
|
||||
this.$root.$store.state.drawing = 1;
|
||||
this.$root.$emit("draw1d");
|
||||
},
|
||||
reset() {
|
||||
this.$root.$emit('reset')
|
||||
this.$root.$emit("reset");
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.menu-row a {
|
||||
.menu-row a {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
font-size: small;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -13,7 +13,7 @@
|
||||
step="10"
|
||||
:value="canvasWidth"
|
||||
@input="updateCanvasWidth"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Height</label>
|
||||
@ -24,7 +24,7 @@
|
||||
step="10"
|
||||
:value="canvasHeight"
|
||||
@input="updateCanvasHeight"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Refresh Rate (ms)</label>
|
||||
@ -38,57 +38,58 @@
|
||||
step="100"
|
||||
:value="refreshRate"
|
||||
@input="updateRefreshRate"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Invert Drawing Direction
|
||||
<label
|
||||
>Invert Drawing Direction
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="drawingDirection === 'x'"
|
||||
:value="drawingDirection"
|
||||
@input="updateDrawingDirection"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</MenuRow>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MenuRow from './MenuRow.vue'
|
||||
import {mapGetters} from 'vuex'
|
||||
import MenuRow from "./MenuRow.vue";
|
||||
import { mapGetters } from "vuex";
|
||||
export default {
|
||||
name: 'MenuGeneralOptions',
|
||||
name: "MenuGeneralOptions",
|
||||
components: {
|
||||
MenuRow
|
||||
MenuRow,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
canvasWidth: 'getCanvasWidth',
|
||||
canvasHeight: 'getCanvasHeight',
|
||||
refreshRate: 'getRefreshRate',
|
||||
drawingDirection: 'getDrawingDirection'
|
||||
})
|
||||
canvasWidth: "getCanvasWidth",
|
||||
canvasHeight: "getCanvasHeight",
|
||||
refreshRate: "getRefreshRate",
|
||||
drawingDirection: "getDrawingDirection",
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
updateCanvasHeight: function(event) {
|
||||
const elem = event.target
|
||||
this.$store.commit('setCanvasHeight', elem.value)
|
||||
updateCanvasHeight: function (event) {
|
||||
const elem = event.target;
|
||||
this.$store.commit("setCanvasHeight", elem.value);
|
||||
},
|
||||
updateCanvasWidth: function(event) {
|
||||
const elem = event.target
|
||||
this.$store.commit('setCanvasWidth', elem.value)
|
||||
updateCanvasWidth: function (event) {
|
||||
const elem = event.target;
|
||||
this.$store.commit("setCanvasWidth", elem.value);
|
||||
},
|
||||
updateRefreshRate: function(event) {
|
||||
const elem = event.target
|
||||
this.$store.commit('setRefreshRate', elem.value)
|
||||
updateRefreshRate: function (event) {
|
||||
const elem = event.target;
|
||||
this.$store.commit("setRefreshRate", elem.value);
|
||||
},
|
||||
updateDrawingDirection: function(event) {
|
||||
const elem = event.target
|
||||
const value = elem.checked ? "x" : "y"
|
||||
this.$store.commit('setDrawingDirection', value)
|
||||
console.log(this.drawingDirection)
|
||||
}
|
||||
}
|
||||
}
|
||||
updateDrawingDirection: function (event) {
|
||||
const elem = event.target;
|
||||
const value = elem.checked ? "x" : "y";
|
||||
this.$store.commit("setDrawingDirection", value);
|
||||
console.log(this.drawingDirection);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,82 +1,75 @@
|
||||
<template>
|
||||
<div class="menu-row">
|
||||
<h2
|
||||
:id="rowTitle"
|
||||
@click="updateActiveMenu"
|
||||
>
|
||||
<h2 :id="rowTitle" @click="updateActiveMenu">
|
||||
{{ rowTitle }}
|
||||
</h2>
|
||||
<div
|
||||
v-if="activeMenu === rowTitle"
|
||||
class="menu-row-content"
|
||||
>
|
||||
<div v-if="activeMenu === rowTitle" class="menu-row-content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex'
|
||||
import { mapGetters } from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'MenuRow',
|
||||
name: "MenuRow",
|
||||
props: {
|
||||
rowTitle: {
|
||||
type: String,
|
||||
default : ''
|
||||
}
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
activeMenu: 'getActiveMenu',
|
||||
})
|
||||
activeMenu: "getActiveMenu",
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
updateActiveMenu(event) {
|
||||
const elem = event.target
|
||||
const value = elem.id
|
||||
if (value == this.activeMenu)
|
||||
this.$store.commit('setActiveMenu', "")
|
||||
else
|
||||
this.$store.commit('setActiveMenu', value)
|
||||
const elem = event.target;
|
||||
const value = elem.id;
|
||||
if (value == this.activeMenu) this.$store.commit("setActiveMenu", "");
|
||||
else this.$store.commit("setActiveMenu", value);
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.menu-row h2 {
|
||||
font-size: medium;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
border: 2px solid darkgrey;
|
||||
margin: 0 0 10px 0;
|
||||
font-size: medium;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
border: 2px solid darkgrey;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
select {
|
||||
margin-top: 10px;
|
||||
padding: 5px;
|
||||
margin-top: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
input[type="button"] {
|
||||
min-width: 60px;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
min-width: 60px;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.menu-row {
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
label, .form-field label {
|
||||
margin-right: 10px;
|
||||
font-weight: bold;
|
||||
label,
|
||||
.form-field label {
|
||||
margin-right: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
12
src/main.js
12
src/main.js
@ -1,10 +1,10 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import store from './store'
|
||||
import Vue from "vue";
|
||||
import App from "./App.vue";
|
||||
import store from "./store";
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
render: (h) => h(App),
|
||||
}).$mount("#app");
|
||||
|
@ -1,7 +1,7 @@
|
||||
// handles negative index and index bigger than its array length
|
||||
function guard(index, array) {
|
||||
if (index > (array.length - 1)) return 0;
|
||||
if (index < 0) return (array.length - 1);
|
||||
if (index > array.length - 1) return 0;
|
||||
if (index < 0) return array.length - 1;
|
||||
return index;
|
||||
}
|
||||
|
||||
@ -12,11 +12,8 @@ function evolve1d(state, rules) {
|
||||
return state[safeIndex];
|
||||
}
|
||||
const newState = state.map((_, x) => {
|
||||
const cells = [
|
||||
getCell(x - 1),
|
||||
getCell(x),
|
||||
getCell(x + 1)];
|
||||
return rules[cells.join('')];
|
||||
const cells = [getCell(x - 1), getCell(x), getCell(x + 1)];
|
||||
return rules[cells.join("")];
|
||||
});
|
||||
|
||||
return newState.map(Number);
|
||||
@ -47,10 +44,14 @@ function getCellNeighbors(board, cellCoordinates) {
|
||||
|
||||
// the current cell is not included in the result
|
||||
return [
|
||||
getCell(x - 1, y - 1), getCell(x, y - 1),
|
||||
getCell(x + 1, y - 1), getCell(x - 1, y),
|
||||
getCell(x + 1, y), getCell(x - 1, y + 1),
|
||||
getCell(x, y + 1), getCell(x + 1, y - 1),
|
||||
getCell(x - 1, y - 1),
|
||||
getCell(x, y - 1),
|
||||
getCell(x + 1, y - 1),
|
||||
getCell(x - 1, y),
|
||||
getCell(x + 1, y),
|
||||
getCell(x - 1, y + 1),
|
||||
getCell(x, y + 1),
|
||||
getCell(x + 1, y - 1),
|
||||
];
|
||||
}
|
||||
|
||||
@ -75,58 +76,60 @@ function conwayRules(cell, neighbors) {
|
||||
// get the next evolution of a 2D CA initial state
|
||||
// Rules : Moore neighborhood
|
||||
function evolve2d(board, rulesFn) {
|
||||
return board.map((row, x) => row.map((cell, y) => {
|
||||
const neighbors = getCellNeighbors(board, [x, y]);
|
||||
const sum = getNeighborsSum(neighbors);
|
||||
return rulesFn(cell, sum);
|
||||
}));
|
||||
return board.map((row, x) =>
|
||||
row.map((cell, y) => {
|
||||
const neighbors = getCellNeighbors(board, [x, y]);
|
||||
const sum = getNeighborsSum(neighbors);
|
||||
return rulesFn(cell, sum);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function getDrawingValues(state, acc, cell) {
|
||||
const d = cell.dimension;
|
||||
return Object.keys(state).map(
|
||||
(key) => {
|
||||
const fillStyle = (() => {
|
||||
if (state[key] === '1') return cell.liveColor;
|
||||
return cell.deadColor;
|
||||
})();
|
||||
return Object.keys(state).map((key) => {
|
||||
const fillStyle = (() => {
|
||||
if (state[key] === "1") return cell.liveColor;
|
||||
return cell.deadColor;
|
||||
})();
|
||||
|
||||
return {
|
||||
move: [key * d, acc * d],
|
||||
fill: [key * d, acc * d, d, d],
|
||||
fillStyle,
|
||||
};
|
||||
},
|
||||
);
|
||||
return {
|
||||
move: [key * d, acc * d],
|
||||
fill: [key * d, acc * d, d, d],
|
||||
fillStyle,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Populates the first state with a single living cell in the center
|
||||
function create1dStateOneCell(width) {
|
||||
return [...Array(width)].map(
|
||||
(cell, index) => {
|
||||
if (index === width / 2 || index === width + 1 / 2) return 1
|
||||
return 0
|
||||
})
|
||||
return [...Array(width)].map((cell, index) => {
|
||||
if (index === width / 2 || index === width + 1 / 2) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Populates the first state of a 1D CA with cells returned
|
||||
// by initFn
|
||||
function create1dState(width, initFn, args) {
|
||||
return [...Array(width)].map(
|
||||
() => initFn(...args)
|
||||
);
|
||||
return [...Array(width)].map(() => initFn(...args));
|
||||
}
|
||||
|
||||
// Populates the first state of a 2D CA with cells returned
|
||||
// by initFn
|
||||
function create2dState(width, height, initFn, args) {
|
||||
return [...Array(height)].map(
|
||||
() => [...Array(width)].map(
|
||||
() => initFn(...args),
|
||||
),
|
||||
return [...Array(height)].map(() =>
|
||||
[...Array(width)].map(() => initFn(...args))
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
getDrawingValues, create1dState, create2dState, createBoard, create1dStateOneCell, conwayRules, evolve1d, evolve2d
|
||||
getDrawingValues,
|
||||
create1dState,
|
||||
create2dState,
|
||||
createBoard,
|
||||
create1dStateOneCell,
|
||||
conwayRules,
|
||||
evolve1d,
|
||||
evolve2d,
|
||||
};
|
||||
|
@ -1,146 +0,0 @@
|
||||
import { getRandomInt, sleep } from './common.js';
|
||||
import {
|
||||
evolve2d, initialState1d, initialState2d, conwayRules, createBoard,
|
||||
} from './automata.js';
|
||||
|
||||
let drawing = 1;
|
||||
|
||||
const form = Array.from(document.forms.rules.elements);
|
||||
const canvas = document.querySelector('#canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const main = document.querySelector('#main');
|
||||
const dead = document.querySelector('#dead');
|
||||
const live = document.querySelector('#live');
|
||||
const cellSize = document.querySelector('#cellSize');
|
||||
const startBtn = document.querySelector('#start');
|
||||
const startBtn2d = document.querySelector('#start2d');
|
||||
const canvasRefreshBtn = document.querySelector('#canvasRefresh');
|
||||
const resetBtn = document.querySelectorAll('.reset');
|
||||
const stopBtn = document.querySelectorAll('.stop');
|
||||
// const loop = document.querySelector('#loop');
|
||||
const menuRow = document.querySelectorAll('.menu-row');
|
||||
const menuRowContent = document.querySelectorAll('.menu-row-content');
|
||||
|
||||
function drawCanvas(board, props) {
|
||||
board.map((row, y) => {
|
||||
const d = props.size;
|
||||
return row.map(
|
||||
(cell, x) => {
|
||||
ctx.fillStyle = (
|
||||
() => {
|
||||
if (cell === 1) return props.liveColor;
|
||||
return props.deadColor;
|
||||
})();
|
||||
ctx.fillRect(x * d, y * d, d, d);
|
||||
return cell;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function getRules() {
|
||||
const rules = {};
|
||||
|
||||
form.reduce((_, i) => {
|
||||
if (i !== undefined
|
||||
&& i.type === 'checkbox') {
|
||||
if (i.checked) rules[i.name] = '1';
|
||||
else rules[i.name] = '0';
|
||||
}
|
||||
return rules;
|
||||
}, rules);
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
function getCellProperties() {
|
||||
return {
|
||||
size: cellSize.value,
|
||||
liveColor: live.value,
|
||||
deadColor: dead.value,
|
||||
};
|
||||
}
|
||||
|
||||
function reset() {
|
||||
drawing = 0;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
function resizeCanvas() {
|
||||
canvas.width = main.offsetWidth * 0.9;
|
||||
canvas.height = main.offsetHeight * 0.9;
|
||||
}
|
||||
|
||||
async function draw1d() {
|
||||
const rules = getRules();
|
||||
const props = getCellProperties();
|
||||
const initialState = initialState1d(
|
||||
Math.floor(canvas.width / props.size),
|
||||
getRandomInt,
|
||||
[0, 2],
|
||||
);
|
||||
const board = createBoard(initialState, rules, Math.floor(canvas.height / props.size));
|
||||
|
||||
drawCanvas(board, props);
|
||||
}
|
||||
|
||||
async function draw2d() {
|
||||
const props = getCellProperties();
|
||||
const initialState = initialState2d(
|
||||
Math.floor(canvas.width / props.size),
|
||||
Math.floor(canvas.height / props.size),
|
||||
getRandomInt,
|
||||
[0, 2],
|
||||
);
|
||||
const board = evolve2d(initialState, conwayRules);
|
||||
|
||||
async function draw2dNext(b) {
|
||||
if (drawing === 0) return;
|
||||
const newBoard = evolve2d(b, conwayRules);
|
||||
drawCanvas(b, props);
|
||||
await sleep(300);
|
||||
draw2dNext(newBoard);
|
||||
}
|
||||
return draw2dNext(board);
|
||||
}
|
||||
|
||||
// Listeners
|
||||
|
||||
startBtn.addEventListener('click', async () => {
|
||||
reset();
|
||||
|
||||
await sleep(60);
|
||||
|
||||
drawing = 1;
|
||||
|
||||
draw1d();
|
||||
});
|
||||
|
||||
startBtn2d.addEventListener('click', async () => {
|
||||
reset();
|
||||
|
||||
await sleep(60);
|
||||
|
||||
drawing = 1;
|
||||
|
||||
draw2d();
|
||||
});
|
||||
|
||||
resetBtn.forEach((elem) => {
|
||||
elem.addEventListener('click', async () => {
|
||||
reset();
|
||||
});
|
||||
});
|
||||
|
||||
stopBtn.forEach((elem) => {
|
||||
elem.addEventListener('click', async () => {
|
||||
drawing = 0;
|
||||
});
|
||||
});
|
||||
|
||||
canvasRefreshBtn.addEventListener('click', () => {
|
||||
const width = document.querySelector('#canvasWidth').value;
|
||||
const height = document.querySelector('#canvasWidth').value;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
});
|
@ -1,25 +1,25 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import Vue from "vue";
|
||||
import Vuex from "vuex";
|
||||
|
||||
Vue.use(Vuex)
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
drawing: 0,
|
||||
rules1d : {
|
||||
"111" : 0,
|
||||
"110" : 1,
|
||||
"101" : 0,
|
||||
"100" : 0,
|
||||
"011" : 1,
|
||||
"010" : 0,
|
||||
"001" : 0,
|
||||
"000" : 1
|
||||
rules1d: {
|
||||
111: 0,
|
||||
110: 1,
|
||||
101: 0,
|
||||
100: 0,
|
||||
"011": 1,
|
||||
"010": 0,
|
||||
"001": 0,
|
||||
"000": 1,
|
||||
},
|
||||
cellProperties: {
|
||||
size: 3,
|
||||
liveColor: '#000000',
|
||||
deadColor: '#F5F5F5',
|
||||
liveColor: "#000000",
|
||||
deadColor: "#F5F5F5",
|
||||
},
|
||||
canvasWidth: 0,
|
||||
canvasHeight: 0,
|
||||
@ -27,80 +27,78 @@ export default new Vuex.Store({
|
||||
initial1dState: "onecell",
|
||||
activeMenu: "",
|
||||
drawingDirection: "y",
|
||||
lastBoard: {}
|
||||
lastBoard: {},
|
||||
},
|
||||
mutations: {
|
||||
update1dSingleRule(state, data) {
|
||||
state.rules1d[data.rule] = data.value
|
||||
state.rules1d[data.rule] = data.value;
|
||||
},
|
||||
update1dRules(state, data) {
|
||||
state.rules1d = data
|
||||
state.rules1d = data;
|
||||
},
|
||||
setCellProperties(state, data) {
|
||||
state.cellProperties[data.name] = data.value
|
||||
state.cellProperties[data.name] = data.value;
|
||||
},
|
||||
setDrawingStatus(state, data) {
|
||||
state.drawing = data
|
||||
state.drawing = data;
|
||||
},
|
||||
setCanvasWidth(state, data) {
|
||||
state.canvasWidth = data
|
||||
state.canvasWidth = data;
|
||||
},
|
||||
setCanvasHeight(state, data) {
|
||||
state.canvasHeight = data
|
||||
state.canvasHeight = data;
|
||||
},
|
||||
setRefreshRate(state, data) {
|
||||
state.refreshRate = data
|
||||
state.refreshRate = data;
|
||||
},
|
||||
setInitial1dState(state, data) {
|
||||
state.initial1dState = data
|
||||
state.initial1dState = data;
|
||||
},
|
||||
setActiveMenu(state, data) {
|
||||
state.activeMenu = data
|
||||
state.activeMenu = data;
|
||||
},
|
||||
setDrawingDirection(state, data) {
|
||||
state.drawingDirection = data
|
||||
state.drawingDirection = data;
|
||||
},
|
||||
setLastBoard(state, data) {
|
||||
state.lastBoard = data
|
||||
state.lastBoard = data;
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
getCellProperties(state) {
|
||||
return state.cellProperties
|
||||
return state.cellProperties;
|
||||
},
|
||||
get1dRules(state) {
|
||||
return state.rules1d
|
||||
return state.rules1d;
|
||||
},
|
||||
getRule1d(state) {
|
||||
return (rule) => state.rules1d[rule]
|
||||
return (rule) => state.rules1d[rule];
|
||||
},
|
||||
isDrawing(state) {
|
||||
return state.drawing
|
||||
return state.drawing;
|
||||
},
|
||||
getCanvasWidth(state) {
|
||||
return state.canvasWidth
|
||||
return state.canvasWidth;
|
||||
},
|
||||
getCanvasHeight(state) {
|
||||
return state.canvasHeight
|
||||
return state.canvasHeight;
|
||||
},
|
||||
getRefreshRate(state) {
|
||||
return state.refreshRate
|
||||
return state.refreshRate;
|
||||
},
|
||||
getInitial1dState(state) {
|
||||
return state.initial1dState
|
||||
return state.initial1dState;
|
||||
},
|
||||
getActiveMenu(state) {
|
||||
return state.activeMenu
|
||||
return state.activeMenu;
|
||||
},
|
||||
getDrawingDirection(state) {
|
||||
return state.drawingDirection
|
||||
return state.drawingDirection;
|
||||
},
|
||||
getLastBoard(state) {
|
||||
return state.lastBoard
|
||||
}
|
||||
return state.lastBoard;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
},
|
||||
modules: {
|
||||
}
|
||||
})
|
||||
actions: {},
|
||||
modules: {},
|
||||
});
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
|
||||
const path = require("path");
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
host: '127.0.0.1'
|
||||
host: "127.0.0.1",
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
|
@ -3,11 +3,11 @@ module.exports = {
|
||||
devServer: {
|
||||
overlay: {
|
||||
warnings: true,
|
||||
errors: true
|
||||
errors: true,
|
||||
},
|
||||
watchOptions: {
|
||||
ignored: [/node_modules/, /public/, /\.#/],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user