linting and formating. renamed canvas component
This commit is contained in:
parent
ceee7f13d7
commit
d201a73ede
10
.eslintrc.js
10
.eslintrc.js
@ -5,12 +5,12 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
// add more generic rulesets here, such as:
|
// add more generic rulesets here, such as:
|
||||||
'eslint:recommended',
|
"eslint:recommended",
|
||||||
'plugin:vue/vue3-recommended',
|
"plugin:vue/vue3-recommended",
|
||||||
"prettier"
|
"prettier",
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
// override/add rules settings here, such as:
|
// override/add rules settings here, such as:
|
||||||
// 'vue/no-unused-vars': 'error'
|
// 'vue/no-unused-vars': 'error'
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
@ -1,30 +1,37 @@
|
|||||||
# explorata
|
# explorata
|
||||||
|
|
||||||
Explore 1D and 2D cellular automata, with a few bells and whistles.
|
Explore 1D and 2D cellular automata, with a few bells and whistles.
|
||||||
|
|
||||||
## Project setup
|
## Project setup
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
### Compiles and hot-reloads for development
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run serve
|
npm run serve
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compiles and minifies for production
|
### Compiles and minifies for production
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Lints and fixes files
|
### Lints and fixes files
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
### Customize configuration
|
### Customize configuration
|
||||||
|
|
||||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
|
|
||||||
### References
|
### References
|
||||||
|
|
||||||
- https://natureofcode.com/book/chapter-7-cellular-automata/
|
- https://natureofcode.com/book/chapter-7-cellular-automata/
|
||||||
- https://en.wikipedia.org/wiki/Hashlife
|
- https://en.wikipedia.org/wiki/Hashlife
|
||||||
- https://plato.stanford.edu/entries/cellular-automata/supplement.html
|
- https://plato.stanford.edu/entries/cellular-automata/supplement.html
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: ["@vue/cli-plugin-babel/preset"],
|
||||||
'@vue/cli-plugin-babel/preset'
|
};
|
||||||
],
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
version: '3.4'
|
version: "3.4"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
explorata:
|
explorata:
|
||||||
|
14
index.html
14
index.html
@ -1,18 +1,18 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="">
|
<html lang="">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<title>Explorata</title>
|
<title>Explorata</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>
|
<strong>
|
||||||
Althought you're right to browse the Web with Javascript disabled, Explorata doesn't work properly without it.
|
Althought you're right to browse the Web with Javascript disabled,
|
||||||
Please enable Javascript to continue.
|
Explorata doesn't work properly without it. Please enable Javascript to
|
||||||
|
continue.
|
||||||
</strong>
|
</strong>
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"serve": "vite preview",
|
"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": {
|
"dependencies": {
|
||||||
"@vitejs/plugin-vue": "^3.2.0",
|
"@vitejs/plugin-vue": "^3.2.0",
|
||||||
|
25
src/App.vue
25
src/App.vue
@ -1,28 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<h1
|
<h1 id="main-title">Cellular Automata Explorer</h1>
|
||||||
id="main-title"
|
|
||||||
>
|
|
||||||
Cellular Automata Explorer
|
|
||||||
</h1>
|
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<MainMenu />
|
<MainMenu />
|
||||||
<Canvas />
|
<CanvasBoard />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MainMenu from './components/MainMenu.vue'
|
import MainMenu from "./components/MainMenu.vue";
|
||||||
import Canvas from './components/Canvas.vue'
|
import CanvasBoard from "./components/CanvasBoard.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: "App",
|
||||||
components: {
|
components: {
|
||||||
MainMenu,
|
MainMenu,
|
||||||
Canvas
|
CanvasBoard,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -32,7 +28,6 @@ export default {
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@ -40,7 +35,6 @@ export default {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: black;
|
background: black;
|
||||||
color: white;
|
color: white;
|
||||||
@ -53,7 +47,8 @@ canvas {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2 {
|
h1,
|
||||||
|
h2 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,19 +8,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<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 {
|
export default {
|
||||||
name: 'MainMenu',
|
name: "MainMenu",
|
||||||
components: {
|
components: {
|
||||||
MenuCellProperties,
|
MenuCellProperties,
|
||||||
MenuGeneralOptions,
|
MenuGeneralOptions,
|
||||||
MenuElementaryCA,
|
MenuElementaryCA,
|
||||||
Menu2dCA
|
Menu2dCA,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -30,7 +30,6 @@ export default {
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||||
#sidebar::-webkit-scrollbar {
|
#sidebar::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -2,65 +2,56 @@
|
|||||||
<MenuRow row-title="2D Cellular Automata">
|
<MenuRow row-title="2D Cellular Automata">
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Start from last result</label>
|
<label>Start from last result</label>
|
||||||
<input
|
<input type="button" value="start" @click="startFromLast" />
|
||||||
type="button"
|
|
||||||
value="start"
|
|
||||||
@click="startFromLast"
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<input
|
<input type="button" name="start2d" value="start" @click="draw2d" />
|
||||||
type="button"
|
|
||||||
name="start2d"
|
|
||||||
value="start"
|
|
||||||
@click="draw2d"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="button"
|
type="button"
|
||||||
name="stop"
|
name="stop"
|
||||||
class="stop"
|
class="stop"
|
||||||
value="stop"
|
value="stop"
|
||||||
@click="stop"
|
@click="stop"
|
||||||
>
|
/>
|
||||||
<input
|
<input
|
||||||
type="button"
|
type="button"
|
||||||
name="reset"
|
name="reset"
|
||||||
class="reset"
|
class="reset"
|
||||||
value="reset"
|
value="reset"
|
||||||
@click="reset"
|
@click="reset"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</MenuRow>
|
</MenuRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MenuRow from './MenuRow.vue'
|
import MenuRow from "./MenuRow.vue";
|
||||||
import {mapGetters} from 'vuex'
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Menu2dCA',
|
name: "Menu2dCA",
|
||||||
components: {
|
components: {
|
||||||
MenuRow
|
MenuRow,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
lastBoard: 'getLastBoard',
|
lastBoard: "getLastBoard",
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
draw2d() {
|
draw2d() {
|
||||||
this.$root.$store.state.drawing = 1
|
this.$root.$store.state.drawing = 1;
|
||||||
this.$root.$emit('draw2dNew')
|
this.$root.$emit("draw2dNew");
|
||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
this.$root.$emit('reset')
|
this.$root.$emit("reset");
|
||||||
},
|
},
|
||||||
stop() {
|
stop() {
|
||||||
this.$root.$emit('stop')
|
this.$root.$emit("stop");
|
||||||
},
|
},
|
||||||
startFromLast() {
|
startFromLast() {
|
||||||
this.$root.$emit('draw2dLast')
|
this.$root.$emit("draw2dLast");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
type="color"
|
type="color"
|
||||||
@value="cellProperties.liveColor"
|
@value="cellProperties.liveColor"
|
||||||
@input="updateCellProperties"
|
@input="updateCellProperties"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label for="dead">Dead cell color</label>
|
<label for="dead">Dead cell color</label>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
type="color"
|
type="color"
|
||||||
:value="cellProperties.deadColor"
|
:value="cellProperties.deadColor"
|
||||||
@input="updateCellProperties"
|
@input="updateCellProperties"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Cell size</label>
|
<label>Cell size</label>
|
||||||
@ -27,36 +27,36 @@
|
|||||||
min="1"
|
min="1"
|
||||||
:value="cellProperties.size"
|
:value="cellProperties.size"
|
||||||
@input="updateCellProperties"
|
@input="updateCellProperties"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</MenuRow>
|
</MenuRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MenuRow from './MenuRow.vue'
|
import MenuRow from "./MenuRow.vue";
|
||||||
export default {
|
export default {
|
||||||
name: 'MainMenu',
|
name: "MainMenu",
|
||||||
components: {
|
components: {
|
||||||
MenuRow
|
MenuRow,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
cellProperties: this.$store.state.cellProperties
|
cellProperties: this.$store.state.cellProperties,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getCellProperties(event) {
|
getCellProperties(event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
const prop = this.$store.state.cellProperties
|
const prop = this.$store.state.cellProperties;
|
||||||
return prop[elem.name]
|
return prop[elem.name];
|
||||||
},
|
},
|
||||||
updateCellProperties(event) {
|
updateCellProperties(event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
const prop = {'name' : elem.name, 'value' : elem.value}
|
const prop = { name: elem.name, value: elem.value };
|
||||||
//console.log(prop)
|
//console.log(prop)
|
||||||
this.$store.commit('setCellProperties', prop)
|
this.$store.commit("setCellProperties", prop);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
<MenuRow row-title="Elementary Cellular Automata">
|
<MenuRow row-title="Elementary Cellular Automata">
|
||||||
<form>
|
<form>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Initial state presets
|
<label
|
||||||
<br>
|
>Initial state presets
|
||||||
|
<br />
|
||||||
<select
|
<select
|
||||||
name="initial1dStates"
|
name="initial1dStates"
|
||||||
value="initial1dStates"
|
value="initial1dStates"
|
||||||
@ -13,7 +14,8 @@
|
|||||||
v-for="state in initial1dStates"
|
v-for="state in initial1dStates"
|
||||||
:key="'preset1d' + state.id"
|
:key="'preset1d' + state.id"
|
||||||
:value="state.id"
|
:value="state.id"
|
||||||
>{{ state.name }}
|
>
|
||||||
|
{{ state.name }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
@ -22,83 +24,129 @@
|
|||||||
<label>Rules</label>
|
<label>Rules</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Rules presets
|
<label
|
||||||
<br>
|
>Rules presets
|
||||||
<select
|
<br />
|
||||||
name="rules1d"
|
<select name="rules1d" value="rules" @input="update1dRules">
|
||||||
value="rules"
|
|
||||||
@input="update1dRules"
|
|
||||||
>
|
|
||||||
<option
|
<option
|
||||||
v-for="(rule, name) in presetRules"
|
v-for="(rule, name) in presetRules"
|
||||||
:key="'rule1d' + name"
|
:key="'rule1d' + name"
|
||||||
:value="name"
|
:value="name"
|
||||||
>{{ name }}
|
>
|
||||||
|
{{ name }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<a
|
<a style="cursor: pointer" @click="copy1dRules">copy rules</a>
|
||||||
style="cursor: pointer;"
|
|
||||||
@click="copy1dRules"
|
|
||||||
>copy rules</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="(rule, name) in rules"
|
v-for="(rule, name) in rules"
|
||||||
:key="'rule1d' + name"
|
:key="'rule1d' + name"
|
||||||
class="form-field"
|
class="form-field"
|
||||||
>
|
>
|
||||||
<label>{{ name }}
|
<label
|
||||||
|
>{{ name }}
|
||||||
<input
|
<input
|
||||||
:value="rule"
|
:value="rule"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:name="name"
|
:name="name"
|
||||||
:checked="rule"
|
:checked="rule"
|
||||||
@input="update1dSingleRule"
|
@input="update1dSingleRule"
|
||||||
>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<input
|
<input type="button" name="start" value="start" @click="draw1d" />
|
||||||
type="button"
|
|
||||||
name="start"
|
|
||||||
value="start"
|
|
||||||
@click="draw1d"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="button"
|
type="button"
|
||||||
name="reset"
|
name="reset"
|
||||||
class="reset"
|
class="reset"
|
||||||
value="reset"
|
value="reset"
|
||||||
@click="reset()"
|
@click="reset()"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</MenuRow>
|
</MenuRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapGetters} from 'vuex'
|
import { mapGetters } from "vuex";
|
||||||
import MenuRow from './MenuRow.vue'
|
import MenuRow from "./MenuRow.vue";
|
||||||
export default {
|
export default {
|
||||||
name: 'MenuElementaryCA',
|
name: "MenuElementaryCA",
|
||||||
components: {
|
components: {
|
||||||
MenuRow
|
MenuRow,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
// TODO: Why not a getter in the store?
|
// TODO: Why not a getter in the store?
|
||||||
return {
|
return {
|
||||||
presetRules: {
|
presetRules: {
|
||||||
"rule 73" : {"100":0,"101":0,"110":1,"111":0,"011":1,"010":0,"001":0,"000":1},
|
"rule 73": {
|
||||||
"rule 86" : {"100":1,"101":0,"110":0,"111":1,"011":0,"010":1,"001":0,"000":1},
|
100: 0,
|
||||||
"rule 90" : {"100":1,"101":0,"110":1,"111":0,"011":0,"010":0,"001":1,"000":0},
|
101: 0,
|
||||||
"rule 45?" : {"100":0,"101":0,"110":1,"111":0,"011":1,"010":0,"001":1,"000":1},
|
110: 1,
|
||||||
"rule 54?" : {"100":1,"101":0,"110":1,"111":1,"011":0,"010":1,"001":1,"000":0},
|
111: 0,
|
||||||
"unknown rule" : {"100":0,"101":0,"110":0,"111":1,"011":0,"010":0,"001":1,"000":1}
|
"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: [
|
initial1dStates: [
|
||||||
{ id : "onecell",
|
{
|
||||||
|
id: "onecell",
|
||||||
name: "One cell at center",
|
name: "One cell at center",
|
||||||
description: "State with a single cell in the middle",
|
description: "State with a single cell in the middle",
|
||||||
},
|
},
|
||||||
@ -106,59 +154,62 @@ export default {
|
|||||||
id: "random",
|
id: "random",
|
||||||
name: "Random cell",
|
name: "Random cell",
|
||||||
description: "State populated with random cells",
|
description: "State populated with random cells",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
initial1dState: 'getInitial1dState',
|
initial1dState: "getInitial1dState",
|
||||||
rules: 'get1dRules'
|
rules: "get1dRules",
|
||||||
}),
|
}),
|
||||||
rules1dFileName() {
|
rules1dFileName() {
|
||||||
return Object.keys(this.rules).map(
|
return (
|
||||||
(index) => {
|
Object.keys(this.rules)
|
||||||
return this.rules[index]
|
.map((index) => {
|
||||||
}).join("_") + ".json"
|
return this.rules[index];
|
||||||
|
})
|
||||||
|
.join("_") + ".json"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
copy1dRules() {
|
copy1dRules() {
|
||||||
const rules = JSON.stringify(this.rules)
|
const rules = JSON.stringify(this.rules);
|
||||||
navigator.clipboard.writeText(rules)
|
navigator.clipboard.writeText(rules);
|
||||||
},
|
},
|
||||||
isCurrentPreset(event) {
|
isCurrentPreset(event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
return this.initialState === elem.value
|
return this.initialState === elem.value;
|
||||||
},
|
},
|
||||||
update1dSingleRule(event) {
|
update1dSingleRule(event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
const value = elem.checked ? 1 : 0
|
const value = elem.checked ? 1 : 0;
|
||||||
const data = { 'rule' : elem.name, 'value' : value}
|
const data = { rule: elem.name, value: value };
|
||||||
this.$store.commit('update1dSingleRule', data)
|
this.$store.commit("update1dSingleRule", data);
|
||||||
},
|
},
|
||||||
update1dRules(event) {
|
update1dRules(event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
const name = elem.value
|
const name = elem.value;
|
||||||
const rules = this.presetRules[name]
|
const rules = this.presetRules[name];
|
||||||
Object.keys(rules).map((index) => {
|
Object.keys(rules).map((index) => {
|
||||||
const data = { 'rule' : index, 'value' : rules[index]}
|
const data = { rule: index, value: rules[index] };
|
||||||
this.$store.commit('update1dSingleRule', data)
|
this.$store.commit("update1dSingleRule", data);
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
updateInitial1dState(event) {
|
updateInitial1dState(event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
this.$store.commit('setInitial1dState', elem.value)
|
this.$store.commit("setInitial1dState", elem.value);
|
||||||
},
|
},
|
||||||
draw1d() {
|
draw1d() {
|
||||||
this.$root.$store.state.drawing = 1
|
this.$root.$store.state.drawing = 1;
|
||||||
this.$root.$emit('draw1d')
|
this.$root.$emit("draw1d");
|
||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
this.$root.$emit('reset')
|
this.$root.$emit("reset");
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.menu-row a {
|
.menu-row a {
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
step="10"
|
step="10"
|
||||||
:value="canvasWidth"
|
:value="canvasWidth"
|
||||||
@input="updateCanvasWidth"
|
@input="updateCanvasWidth"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Height</label>
|
<label>Height</label>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
step="10"
|
step="10"
|
||||||
:value="canvasHeight"
|
:value="canvasHeight"
|
||||||
@input="updateCanvasHeight"
|
@input="updateCanvasHeight"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Refresh Rate (ms)</label>
|
<label>Refresh Rate (ms)</label>
|
||||||
@ -38,16 +38,17 @@
|
|||||||
step="100"
|
step="100"
|
||||||
:value="refreshRate"
|
:value="refreshRate"
|
||||||
@input="updateRefreshRate"
|
@input="updateRefreshRate"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Invert Drawing Direction
|
<label
|
||||||
|
>Invert Drawing Direction
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="drawingDirection === 'x'"
|
:checked="drawingDirection === 'x'"
|
||||||
:value="drawingDirection"
|
:value="drawingDirection"
|
||||||
@input="updateDrawingDirection"
|
@input="updateDrawingDirection"
|
||||||
>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -55,40 +56,40 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MenuRow from './MenuRow.vue'
|
import MenuRow from "./MenuRow.vue";
|
||||||
import {mapGetters} from 'vuex'
|
import { mapGetters } from "vuex";
|
||||||
export default {
|
export default {
|
||||||
name: 'MenuGeneralOptions',
|
name: "MenuGeneralOptions",
|
||||||
components: {
|
components: {
|
||||||
MenuRow
|
MenuRow,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
canvasWidth: 'getCanvasWidth',
|
canvasWidth: "getCanvasWidth",
|
||||||
canvasHeight: 'getCanvasHeight',
|
canvasHeight: "getCanvasHeight",
|
||||||
refreshRate: 'getRefreshRate',
|
refreshRate: "getRefreshRate",
|
||||||
drawingDirection: 'getDrawingDirection'
|
drawingDirection: "getDrawingDirection",
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateCanvasHeight: function (event) {
|
updateCanvasHeight: function (event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
this.$store.commit('setCanvasHeight', elem.value)
|
this.$store.commit("setCanvasHeight", elem.value);
|
||||||
},
|
},
|
||||||
updateCanvasWidth: function (event) {
|
updateCanvasWidth: function (event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
this.$store.commit('setCanvasWidth', elem.value)
|
this.$store.commit("setCanvasWidth", elem.value);
|
||||||
},
|
},
|
||||||
updateRefreshRate: function (event) {
|
updateRefreshRate: function (event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
this.$store.commit('setRefreshRate', elem.value)
|
this.$store.commit("setRefreshRate", elem.value);
|
||||||
},
|
},
|
||||||
updateDrawingDirection: function (event) {
|
updateDrawingDirection: function (event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
const value = elem.checked ? "x" : "y"
|
const value = elem.checked ? "x" : "y";
|
||||||
this.$store.commit('setDrawingDirection', value)
|
this.$store.commit("setDrawingDirection", value);
|
||||||
console.log(this.drawingDirection)
|
console.log(this.drawingDirection);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,47 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="menu-row">
|
<div class="menu-row">
|
||||||
<h2
|
<h2 :id="rowTitle" @click="updateActiveMenu">
|
||||||
:id="rowTitle"
|
|
||||||
@click="updateActiveMenu"
|
|
||||||
>
|
|
||||||
{{ rowTitle }}
|
{{ rowTitle }}
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div v-if="activeMenu === rowTitle" class="menu-row-content">
|
||||||
v-if="activeMenu === rowTitle"
|
|
||||||
class="menu-row-content"
|
|
||||||
>
|
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapGetters} from 'vuex'
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MenuRow',
|
name: "MenuRow",
|
||||||
props: {
|
props: {
|
||||||
rowTitle: {
|
rowTitle: {
|
||||||
type: String,
|
type: String,
|
||||||
default : ''
|
default: "",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
activeMenu: 'getActiveMenu',
|
activeMenu: "getActiveMenu",
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateActiveMenu(event) {
|
updateActiveMenu(event) {
|
||||||
const elem = event.target
|
const elem = event.target;
|
||||||
const value = elem.id
|
const value = elem.id;
|
||||||
if (value == this.activeMenu)
|
if (value == this.activeMenu) this.$store.commit("setActiveMenu", "");
|
||||||
this.$store.commit('setActiveMenu', "")
|
else this.$store.commit("setActiveMenu", value);
|
||||||
else
|
|
||||||
this.$store.commit('setActiveMenu', value)
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -75,7 +67,8 @@ input[type="button"] {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
label, .form-field label {
|
label,
|
||||||
|
.form-field label {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
12
src/main.js
12
src/main.js
@ -1,10 +1,10 @@
|
|||||||
import Vue from 'vue'
|
import Vue from "vue";
|
||||||
import App from './App.vue'
|
import App from "./App.vue";
|
||||||
import store from './store'
|
import store from "./store";
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
store,
|
store,
|
||||||
render: h => h(App)
|
render: (h) => h(App),
|
||||||
}).$mount('#app')
|
}).$mount("#app");
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// handles negative index and index bigger than its array length
|
// handles negative index and index bigger than its array length
|
||||||
function guard(index, array) {
|
function guard(index, array) {
|
||||||
if (index > (array.length - 1)) return 0;
|
if (index > array.length - 1) return 0;
|
||||||
if (index < 0) return (array.length - 1);
|
if (index < 0) return array.length - 1;
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,11 +12,8 @@ function evolve1d(state, rules) {
|
|||||||
return state[safeIndex];
|
return state[safeIndex];
|
||||||
}
|
}
|
||||||
const newState = state.map((_, x) => {
|
const newState = state.map((_, x) => {
|
||||||
const cells = [
|
const cells = [getCell(x - 1), getCell(x), getCell(x + 1)];
|
||||||
getCell(x - 1),
|
return rules[cells.join("")];
|
||||||
getCell(x),
|
|
||||||
getCell(x + 1)];
|
|
||||||
return rules[cells.join('')];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return newState.map(Number);
|
return newState.map(Number);
|
||||||
@ -47,10 +44,14 @@ function getCellNeighbors(board, cellCoordinates) {
|
|||||||
|
|
||||||
// the current cell is not included in the result
|
// the current cell is not included in the result
|
||||||
return [
|
return [
|
||||||
getCell(x - 1, y - 1), getCell(x, y - 1),
|
getCell(x - 1, y - 1),
|
||||||
getCell(x + 1, y - 1), getCell(x - 1, y),
|
getCell(x, y - 1),
|
||||||
getCell(x + 1, y), 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,19 +76,20 @@ function conwayRules(cell, neighbors) {
|
|||||||
// get the next evolution of a 2D CA initial state
|
// get the next evolution of a 2D CA initial state
|
||||||
// Rules : Moore neighborhood
|
// Rules : Moore neighborhood
|
||||||
function evolve2d(board, rulesFn) {
|
function evolve2d(board, rulesFn) {
|
||||||
return board.map((row, x) => row.map((cell, y) => {
|
return board.map((row, x) =>
|
||||||
|
row.map((cell, y) => {
|
||||||
const neighbors = getCellNeighbors(board, [x, y]);
|
const neighbors = getCellNeighbors(board, [x, y]);
|
||||||
const sum = getNeighborsSum(neighbors);
|
const sum = getNeighborsSum(neighbors);
|
||||||
return rulesFn(cell, sum);
|
return rulesFn(cell, sum);
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDrawingValues(state, acc, cell) {
|
function getDrawingValues(state, acc, cell) {
|
||||||
const d = cell.dimension;
|
const d = cell.dimension;
|
||||||
return Object.keys(state).map(
|
return Object.keys(state).map((key) => {
|
||||||
(key) => {
|
|
||||||
const fillStyle = (() => {
|
const fillStyle = (() => {
|
||||||
if (state[key] === '1') return cell.liveColor;
|
if (state[key] === "1") return cell.liveColor;
|
||||||
return cell.deadColor;
|
return cell.deadColor;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -96,37 +98,38 @@ function getDrawingValues(state, acc, cell) {
|
|||||||
fill: [key * d, acc * d, d, d],
|
fill: [key * d, acc * d, d, d],
|
||||||
fillStyle,
|
fillStyle,
|
||||||
};
|
};
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populates the first state with a single living cell in the center
|
// Populates the first state with a single living cell in the center
|
||||||
function create1dStateOneCell(width) {
|
function create1dStateOneCell(width) {
|
||||||
return [...Array(width)].map(
|
return [...Array(width)].map((cell, index) => {
|
||||||
(cell, index) => {
|
if (index === width / 2 || index === width + 1 / 2) return 1;
|
||||||
if (index === width / 2 || index === width + 1 / 2) return 1
|
return 0;
|
||||||
return 0
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populates the first state of a 1D CA with cells returned
|
// Populates the first state of a 1D CA with cells returned
|
||||||
// by initFn
|
// by initFn
|
||||||
function create1dState(width, initFn, args) {
|
function create1dState(width, initFn, args) {
|
||||||
return [...Array(width)].map(
|
return [...Array(width)].map(() => initFn(...args));
|
||||||
() => initFn(...args)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populates the first state of a 2D CA with cells returned
|
// Populates the first state of a 2D CA with cells returned
|
||||||
// by initFn
|
// by initFn
|
||||||
function create2dState(width, height, initFn, args) {
|
function create2dState(width, height, initFn, args) {
|
||||||
return [...Array(height)].map(
|
return [...Array(height)].map(() =>
|
||||||
() => [...Array(width)].map(
|
[...Array(width)].map(() => initFn(...args))
|
||||||
() => initFn(...args),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
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 Vue from "vue";
|
||||||
import Vuex from 'vuex'
|
import Vuex from "vuex";
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex);
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
drawing: 0,
|
drawing: 0,
|
||||||
rules1d: {
|
rules1d: {
|
||||||
"111" : 0,
|
111: 0,
|
||||||
"110" : 1,
|
110: 1,
|
||||||
"101" : 0,
|
101: 0,
|
||||||
"100" : 0,
|
100: 0,
|
||||||
"011": 1,
|
"011": 1,
|
||||||
"010": 0,
|
"010": 0,
|
||||||
"001": 0,
|
"001": 0,
|
||||||
"000" : 1
|
"000": 1,
|
||||||
},
|
},
|
||||||
cellProperties: {
|
cellProperties: {
|
||||||
size: 3,
|
size: 3,
|
||||||
liveColor: '#000000',
|
liveColor: "#000000",
|
||||||
deadColor: '#F5F5F5',
|
deadColor: "#F5F5F5",
|
||||||
},
|
},
|
||||||
canvasWidth: 0,
|
canvasWidth: 0,
|
||||||
canvasHeight: 0,
|
canvasHeight: 0,
|
||||||
@ -27,80 +27,78 @@ export default new Vuex.Store({
|
|||||||
initial1dState: "onecell",
|
initial1dState: "onecell",
|
||||||
activeMenu: "",
|
activeMenu: "",
|
||||||
drawingDirection: "y",
|
drawingDirection: "y",
|
||||||
lastBoard: {}
|
lastBoard: {},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
update1dSingleRule(state, data) {
|
update1dSingleRule(state, data) {
|
||||||
state.rules1d[data.rule] = data.value
|
state.rules1d[data.rule] = data.value;
|
||||||
},
|
},
|
||||||
update1dRules(state, data) {
|
update1dRules(state, data) {
|
||||||
state.rules1d = data
|
state.rules1d = data;
|
||||||
},
|
},
|
||||||
setCellProperties(state, data) {
|
setCellProperties(state, data) {
|
||||||
state.cellProperties[data.name] = data.value
|
state.cellProperties[data.name] = data.value;
|
||||||
},
|
},
|
||||||
setDrawingStatus(state, data) {
|
setDrawingStatus(state, data) {
|
||||||
state.drawing = data
|
state.drawing = data;
|
||||||
},
|
},
|
||||||
setCanvasWidth(state, data) {
|
setCanvasWidth(state, data) {
|
||||||
state.canvasWidth = data
|
state.canvasWidth = data;
|
||||||
},
|
},
|
||||||
setCanvasHeight(state, data) {
|
setCanvasHeight(state, data) {
|
||||||
state.canvasHeight = data
|
state.canvasHeight = data;
|
||||||
},
|
},
|
||||||
setRefreshRate(state, data) {
|
setRefreshRate(state, data) {
|
||||||
state.refreshRate = data
|
state.refreshRate = data;
|
||||||
},
|
},
|
||||||
setInitial1dState(state, data) {
|
setInitial1dState(state, data) {
|
||||||
state.initial1dState = data
|
state.initial1dState = data;
|
||||||
},
|
},
|
||||||
setActiveMenu(state, data) {
|
setActiveMenu(state, data) {
|
||||||
state.activeMenu = data
|
state.activeMenu = data;
|
||||||
},
|
},
|
||||||
setDrawingDirection(state, data) {
|
setDrawingDirection(state, data) {
|
||||||
state.drawingDirection = data
|
state.drawingDirection = data;
|
||||||
},
|
},
|
||||||
setLastBoard(state, data) {
|
setLastBoard(state, data) {
|
||||||
state.lastBoard = data
|
state.lastBoard = data;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getCellProperties(state) {
|
getCellProperties(state) {
|
||||||
return state.cellProperties
|
return state.cellProperties;
|
||||||
},
|
},
|
||||||
get1dRules(state) {
|
get1dRules(state) {
|
||||||
return state.rules1d
|
return state.rules1d;
|
||||||
},
|
},
|
||||||
getRule1d(state) {
|
getRule1d(state) {
|
||||||
return (rule) => state.rules1d[rule]
|
return (rule) => state.rules1d[rule];
|
||||||
},
|
},
|
||||||
isDrawing(state) {
|
isDrawing(state) {
|
||||||
return state.drawing
|
return state.drawing;
|
||||||
},
|
},
|
||||||
getCanvasWidth(state) {
|
getCanvasWidth(state) {
|
||||||
return state.canvasWidth
|
return state.canvasWidth;
|
||||||
},
|
},
|
||||||
getCanvasHeight(state) {
|
getCanvasHeight(state) {
|
||||||
return state.canvasHeight
|
return state.canvasHeight;
|
||||||
},
|
},
|
||||||
getRefreshRate(state) {
|
getRefreshRate(state) {
|
||||||
return state.refreshRate
|
return state.refreshRate;
|
||||||
},
|
},
|
||||||
getInitial1dState(state) {
|
getInitial1dState(state) {
|
||||||
return state.initial1dState
|
return state.initial1dState;
|
||||||
},
|
},
|
||||||
getActiveMenu(state) {
|
getActiveMenu(state) {
|
||||||
return state.activeMenu
|
return state.activeMenu;
|
||||||
},
|
},
|
||||||
getDrawingDirection(state) {
|
getDrawingDirection(state) {
|
||||||
return state.drawingDirection
|
return state.drawingDirection;
|
||||||
},
|
},
|
||||||
getLastBoard(state) {
|
getLastBoard(state) {
|
||||||
return state.lastBoard
|
return state.lastBoard;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
actions: {
|
|
||||||
},
|
},
|
||||||
modules: {
|
actions: {},
|
||||||
}
|
modules: {},
|
||||||
})
|
});
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from "vite";
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from "@vitejs/plugin-vue";
|
||||||
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
server: {
|
server: {
|
||||||
host: '127.0.0.1'
|
host: "127.0.0.1",
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(__dirname, "./src"),
|
"@": path.resolve(__dirname, "./src"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
@ -3,11 +3,11 @@ module.exports = {
|
|||||||
devServer: {
|
devServer: {
|
||||||
overlay: {
|
overlay: {
|
||||||
warnings: true,
|
warnings: true,
|
||||||
errors: true
|
errors: true,
|
||||||
},
|
},
|
||||||
watchOptions: {
|
watchOptions: {
|
||||||
ignored: [/node_modules/, /public/, /\.#/],
|
ignored: [/node_modules/, /public/, /\.#/],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user