Merge pull request 'vue 2 to 3' (#1) from dev into master
Reviewed-on: #1
This commit is contained in:
commit
f9354d0a17
23
.eslintrc.js
23
.eslintrc.js
@ -1,12 +1,25 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
node: true, // remove
|
||||||
|
es2021: true,
|
||||||
|
},
|
||||||
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-essential",
|
||||||
'plugin:vue/recommended', // Use this if you are using Vue.js 2.x.
|
"plugin:vue/vue3-strongly-recommended",
|
||||||
|
"plugin:vue/vue3-recommended",
|
||||||
|
"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'
|
||||||
}
|
"vue/match-component-import-name": "warn",
|
||||||
}
|
"vue/no-ref-object-destructure": "warn",
|
||||||
|
"vue/no-required-prop-with-default": "warn",
|
||||||
|
"vue/no-restricted-class": "warn",
|
||||||
|
"vue/no-template-target-blank": "warn",
|
||||||
|
"vue/no-this-in-before-route-enter": "warn",
|
||||||
|
"vue/prefer-prop-type-boolean-first": "warn",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
23
Dockerfile
Normal file
23
Dockerfile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
FROM node:lts-alpine
|
||||||
|
|
||||||
|
# install simple http server for serving static content
|
||||||
|
RUN npm install -g http-server
|
||||||
|
|
||||||
|
# make the 'app' folder the current working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# copy both 'package.json' and 'package-lock.json' (if available)
|
||||||
|
COPY package*.json ./
|
||||||
|
# build stage
|
||||||
|
FROM node:lts-alpine as build-stage
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# production stage
|
||||||
|
FROM nginx:stable-alpine as production-stage
|
||||||
|
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
@ -1,29 +1,38 @@
|
|||||||
# 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://www.conwaylife.com/wiki/Cellular_automaton
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: ["@vue/cli-plugin-babel/preset"],
|
||||||
'@vue/cli-plugin-babel/preset'
|
};
|
||||||
],
|
|
||||||
}
|
|
||||||
|
15
docker-compose.yml
Normal file
15
docker-compose.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
version: "3.4"
|
||||||
|
|
||||||
|
services:
|
||||||
|
explorata:
|
||||||
|
container_name: explorata
|
||||||
|
build: .
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
networks:
|
||||||
|
- ariona
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ariona:
|
||||||
|
external: true
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
21
index.html
Normal file
21
index.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!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" />
|
||||||
|
<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.
|
||||||
|
</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
32379
package-lock.json
generated
32379
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@ -3,26 +3,24 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"dev": "vite",
|
||||||
"build": "vue-cli-service build",
|
"build": "vite build",
|
||||||
"lint": "vue-cli-service lint"
|
"serve": "vite preview",
|
||||||
|
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
|
||||||
|
"format": "prettier . --write"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/cli": "^4.5.15",
|
"@vitejs/plugin-vue": "^3.2.0",
|
||||||
"@vue/cli-service-global": "^4.5.15",
|
"install": "^0.13.0",
|
||||||
"core-js": "^3.6.5",
|
"vite": "^3.2.4",
|
||||||
"vue": "^2.6.11",
|
"vue": "3.2",
|
||||||
"vuex": "^3.6.2"
|
"vuex": "4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"eslint": "^8.28.0",
|
||||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
"eslint-plugin-vue": "^9.8.0",
|
||||||
"@vue/cli-service": "~4.5.0",
|
"prettier": "2.8.0"
|
||||||
"babel-eslint": "^10.1.0",
|
|
||||||
"eslint": "^6.7.2",
|
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
|
||||||
"vue-template-compiler": "^2.6.11"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
<!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="<%= BASE_URL %>favicon.ico">
|
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
|
||||||
</noscript>
|
|
||||||
<div id="app"></div>
|
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
34
src/App.vue
34
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>
|
||||||
@ -31,8 +27,7 @@ export default {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #2c3e50;
|
/* color: #2c3e50; */
|
||||||
margin-top: 60px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@ -52,21 +47,20 @@ canvas {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2 {
|
h1,
|
||||||
|
h2 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin : 10px auto;
|
margin: 10px auto;
|
||||||
font-size: larger;
|
font-size: larger;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main {
|
|
||||||
flex: 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
height: calc(100vh - 100px);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
0
src/assets/main.css
Normal file
0
src/assets/main.css
Normal file
@ -1,100 +0,0 @@
|
|||||||
<template>
|
|
||||||
<main id="main">
|
|
||||||
<canvas
|
|
||||||
id="canvas"
|
|
||||||
ref="canvas"
|
|
||||||
:width="canvasWidth"
|
|
||||||
:height="canvasHeight"
|
|
||||||
/>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import { evolve2d, initialState1d, initialState2d, conwayRules, createBoard } 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: 'getRuleSet1d',
|
|
||||||
drawing: 'isDrawing',
|
|
||||||
canvasWidth: 'getCanvasWidth',
|
|
||||||
canvasHeight: 'getCanvasHeight'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.canvas = this.$refs['canvas']
|
|
||||||
this.ctx = this.canvas.getContext('2d')
|
|
||||||
this.$root.$on('draw1d', () => { this.draw1d() })
|
|
||||||
this.$root.$on('draw2d', () => { this.draw2d() })
|
|
||||||
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
|
|
||||||
})()
|
|
||||||
this.ctx.fillRect(x * d, y * d, d, d)
|
|
||||||
return cell
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async draw1d() {
|
|
||||||
const initialState = initialState1d(
|
|
||||||
Math.floor(this.canvas.width / this.cellProperties.size),
|
|
||||||
getRandomInt,
|
|
||||||
[0, 2],
|
|
||||||
)
|
|
||||||
const board = createBoard(
|
|
||||||
initialState,
|
|
||||||
this.rules,
|
|
||||||
Math.floor(this.canvas.height / this.cellProperties.size)
|
|
||||||
)
|
|
||||||
this.drawCanvas(board)
|
|
||||||
},
|
|
||||||
async draw2d() {
|
|
||||||
if (this.drawing === 0) return
|
|
||||||
const initialState = initialState2d(
|
|
||||||
Math.floor(this.canvas.width / this.cellProperties.size),
|
|
||||||
Math.floor(this.canvas.height / this.cellProperties.size),
|
|
||||||
getRandomInt,
|
|
||||||
[0, 2],
|
|
||||||
);
|
|
||||||
const board = evolve2d(initialState, conwayRules);
|
|
||||||
|
|
||||||
const draw2dNext = async (b) => {
|
|
||||||
if (this.drawing === 0) return
|
|
||||||
const newBoard = evolve2d(b, conwayRules)
|
|
||||||
this.drawCanvas(b, this.cellProperties)
|
|
||||||
await sleep(300)
|
|
||||||
draw2dNext(newBoard)
|
|
||||||
}
|
|
||||||
return draw2dNext(board)
|
|
||||||
},
|
|
||||||
stop() {
|
|
||||||
this.$store.commit('setDrawingStatus', 0)
|
|
||||||
},
|
|
||||||
reset() {
|
|
||||||
this.stop()
|
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
146
src/components/CanvasBoard.vue
Normal file
146
src/components/CanvasBoard.vue
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<main id="mainContent">
|
||||||
|
<canvas
|
||||||
|
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",
|
||||||
|
canvasWidth: "getCanvasWidth",
|
||||||
|
canvasHeight: "getCanvasHeight",
|
||||||
|
refreshRate: "getRefreshRate",
|
||||||
|
initial1dState: "getInitial1dState",
|
||||||
|
drawingDirection: "getDrawingDirection",
|
||||||
|
canDraw: "getCanDraw",
|
||||||
|
lastBoard: "getLastBoard",
|
||||||
|
getDraw1d: "getDraw1d",
|
||||||
|
getDraw2d: "getDraw2d",
|
||||||
|
getDraw2dLast: "getDraw2dLast",
|
||||||
|
getReset: "getReset",
|
||||||
|
}),
|
||||||
|
boardWidth: function () {
|
||||||
|
return Math.floor(this.canvasWidth / this.cellProperties.size);
|
||||||
|
},
|
||||||
|
boardHeight: function () {
|
||||||
|
return Math.floor(this.canvasHeight / this.cellProperties.size);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
getDraw1d(value) {
|
||||||
|
if (value == true) this.draw1d();
|
||||||
|
},
|
||||||
|
getDraw2d(value) {
|
||||||
|
if (value == true) this.draw2dNew();
|
||||||
|
},
|
||||||
|
getDraw2dLast(value) {
|
||||||
|
if (value == true) this.draw2dLast();
|
||||||
|
},
|
||||||
|
getReset(value) {
|
||||||
|
if (value == true) this.reset();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.canvas = Object.freeze(document.getElementById("canvas-board"));
|
||||||
|
this.ctx = this.canvas.getContext("2d");
|
||||||
|
this.$store.commit("setCanvasWidth", this.canvas.parentElement.clientWidth);
|
||||||
|
this.$store.commit(
|
||||||
|
"setCanvasHeight",
|
||||||
|
this.canvas.parentElement.clientHeight
|
||||||
|
);
|
||||||
|
},
|
||||||
|
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]);
|
||||||
|
},
|
||||||
|
draw1d() {
|
||||||
|
const initialState = this.compute1dInitialState();
|
||||||
|
const board = createBoard(
|
||||||
|
initialState,
|
||||||
|
this.rules.rules,
|
||||||
|
this.boardWidth
|
||||||
|
);
|
||||||
|
this.$store.commit("setLastBoard", Object.freeze(board));
|
||||||
|
this.drawCanvas(board);
|
||||||
|
this.$store.dispatch("stop");
|
||||||
|
},
|
||||||
|
draw2d(board) {
|
||||||
|
if (!this.canDraw) return;
|
||||||
|
const draw2dNext = async (b) => {
|
||||||
|
if (!this.canDraw) return;
|
||||||
|
const newBoard = evolve2d(b, conwayRules);
|
||||||
|
this.drawCanvas(b, this.cellProperties);
|
||||||
|
await sleep(this.refreshRate);
|
||||||
|
draw2dNext(newBoard);
|
||||||
|
};
|
||||||
|
return draw2dNext(board);
|
||||||
|
},
|
||||||
|
draw2dNew() {
|
||||||
|
const initialState = create2dState(
|
||||||
|
this.boardWidth,
|
||||||
|
this.boardHeight,
|
||||||
|
getRandomInt,
|
||||||
|
[0, 2]
|
||||||
|
);
|
||||||
|
const board = evolve2d(initialState, conwayRules);
|
||||||
|
this.$store.commit("setLastBoard", Object.freeze(board));
|
||||||
|
this.draw2d(board);
|
||||||
|
},
|
||||||
|
async draw2dLast() {
|
||||||
|
this.draw2d(this.lastBoard);
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.$store.dispatch("stop");
|
||||||
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
this.$store.commit("toggleReset", 0);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
#mainContent {
|
||||||
|
min-width: 70%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="form-field" v-if="type === 'checkbox'">
|
|
||||||
<label>{{ name }}
|
|
||||||
<input type="checkbox" name="{{ name }}">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-field" v-else-if="type === ''">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@ -8,25 +8,55 @@
|
|||||||
</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>
|
||||||
#sidebar {
|
#sidebar {
|
||||||
flex-basis: fit-content;
|
width: 25%;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
width: fit-content;
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||||
|
#sidebar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide scrollbar for IE, Edge and Firefox */
|
||||||
|
#sidebar {
|
||||||
|
-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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainContent {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,48 +1,56 @@
|
|||||||
<template>
|
<template>
|
||||||
<MenuRow row-title="2D Cellular Automata">
|
<MenuRow row-title="2D Cellular Automata">
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<input
|
<label>Start from last result</label>
|
||||||
type="button"
|
<input type="button" value="start" @click="draw2dLast" />
|
||||||
name="start2d"
|
</div>
|
||||||
value="start"
|
<div class="form-field">
|
||||||
@click="draw2d"
|
<input 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";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Menu2dCA',
|
name: "Menu2dCA",
|
||||||
components: {
|
components: {
|
||||||
MenuRow
|
MenuRow,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
lastBoard: "getLastBoard",
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
draw2d() {
|
draw2d() {
|
||||||
this.$root.$store.state.drawing = 1
|
this.$store.dispatch("draw2d");
|
||||||
this.$root.$emit('draw2d')
|
},
|
||||||
|
draw2dLast() {
|
||||||
|
this.$store.dispatch("draw2dLast");
|
||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
this.$root.$emit('reset')
|
this.$store.dispatch("reset");
|
||||||
},
|
},
|
||||||
stop() {
|
stop() {
|
||||||
this.$root.$emit('stop')
|
this.$store.dispatch("stop");
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</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,45 +17,46 @@
|
|||||||
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>
|
||||||
<input
|
<input
|
||||||
name="size"
|
name="size"
|
||||||
type="number"
|
type="number"
|
||||||
|
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>
|
||||||
|
@ -1,79 +1,245 @@
|
|||||||
<template>
|
<template>
|
||||||
<MenuRow row-title="Elementary Cellular Automata">
|
<MenuRow row-title="Elementary Cellular Automata">
|
||||||
|
<form>
|
||||||
|
<div class="form-field">
|
||||||
|
<label>
|
||||||
|
Initial state presets
|
||||||
|
<br />
|
||||||
|
<select
|
||||||
|
name="initialStates"
|
||||||
|
:value="initialState"
|
||||||
|
@input="updateInitialState"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(state, index) in initialStates"
|
||||||
|
:key="'initial-state-elementary' + index"
|
||||||
|
:value="state.id"
|
||||||
|
>
|
||||||
|
{{ state.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label>Rules</label>
|
<label>Rules</label>
|
||||||
</div>
|
</div>
|
||||||
<form>
|
<div class="form-field">
|
||||||
|
<label
|
||||||
|
>Rules presets
|
||||||
|
<br />
|
||||||
|
<select
|
||||||
|
name="ruleset-elementary"
|
||||||
|
:value="rules.name"
|
||||||
|
@input="updateRules"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(ruleset, index) in presetRules"
|
||||||
|
:key="'ruleset-preset-elementary-' + index"
|
||||||
|
:value="ruleset.name"
|
||||||
|
>
|
||||||
|
{{ ruleset.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<a style="cursor: pointer" @click="copyRules">copy rules</a>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="rule in rules"
|
v-for="(rule, name, index) in rules.rules"
|
||||||
:key="rule"
|
:key="'rule-' + index"
|
||||||
class="form-field"
|
class="form-field"
|
||||||
>
|
>
|
||||||
<label>{{ rule }}
|
<label
|
||||||
|
>{{ name }}
|
||||||
<input
|
<input
|
||||||
:value="getRule(rule)"
|
:value="rule"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:name="rule"
|
:name="name"
|
||||||
:checked="getRule(rule) == 1"
|
:checked="rule"
|
||||||
@input="update1dRules"
|
@input="updateSingleRule"
|
||||||
>
|
/>
|
||||||
</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 -->
|
|
||||||
<!-- type="button" -->
|
|
||||||
<!-- name="stop" -->
|
|
||||||
<!-- class="stop" -->
|
|
||||||
<!-- value="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 { mapGetters } from "vuex";
|
||||||
|
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?
|
||||||
return {
|
return {
|
||||||
rules: ["111", "110", "101", "100", "011", "010", "001", "000"]
|
presetRules: [
|
||||||
}
|
{
|
||||||
|
name: "rule 73",
|
||||||
|
rules: {
|
||||||
|
100: 0,
|
||||||
|
101: 0,
|
||||||
|
110: 1,
|
||||||
|
111: 0,
|
||||||
|
"011": 1,
|
||||||
|
"010": 0,
|
||||||
|
"001": 0,
|
||||||
|
"000": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule 86",
|
||||||
|
rules: {
|
||||||
|
100: 1,
|
||||||
|
101: 0,
|
||||||
|
110: 0,
|
||||||
|
111: 1,
|
||||||
|
"011": 0,
|
||||||
|
"010": 1,
|
||||||
|
"001": 0,
|
||||||
|
"000": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule 90",
|
||||||
|
rules: {
|
||||||
|
100: 1,
|
||||||
|
101: 0,
|
||||||
|
110: 1,
|
||||||
|
111: 0,
|
||||||
|
"011": 0,
|
||||||
|
"010": 0,
|
||||||
|
"001": 1,
|
||||||
|
"000": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule 45?",
|
||||||
|
rules: {
|
||||||
|
100: 0,
|
||||||
|
101: 0,
|
||||||
|
110: 1,
|
||||||
|
111: 0,
|
||||||
|
"011": 1,
|
||||||
|
"010": 0,
|
||||||
|
"001": 1,
|
||||||
|
"000": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule 54?",
|
||||||
|
rules: {
|
||||||
|
100: 1,
|
||||||
|
101: 0,
|
||||||
|
110: 1,
|
||||||
|
111: 1,
|
||||||
|
"011": 0,
|
||||||
|
"010": 1,
|
||||||
|
"001": 1,
|
||||||
|
"000": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown rule",
|
||||||
|
rules: {
|
||||||
|
100: 0,
|
||||||
|
101: 0,
|
||||||
|
110: 0,
|
||||||
|
111: 1,
|
||||||
|
"011": 0,
|
||||||
|
"010": 0,
|
||||||
|
"001": 1,
|
||||||
|
"000": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
initialStates: [
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
initialState: "getInitial1dState",
|
||||||
|
rules: "get1dRules",
|
||||||
|
}),
|
||||||
|
rules1dFileName() {
|
||||||
|
return (
|
||||||
|
Object.keys(this.rules)
|
||||||
|
.map((index) => {
|
||||||
|
return this.rules[index];
|
||||||
|
})
|
||||||
|
.join("_") + ".json"
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
update1dRules(event) {
|
copyRules() {
|
||||||
const elem = event.target
|
const rules = JSON.stringify(this.rules);
|
||||||
const value = elem.checked ? 1 : 0
|
navigator.clipboard.writeText(rules);
|
||||||
const data = { 'rule' : elem.name, 'value' : value}
|
},
|
||||||
this.$store.commit('update1dRules', data)
|
isCurrentPreset(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
return this.initialState === elem.value;
|
||||||
|
},
|
||||||
|
updateSingleRule(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
const value = elem.checked ? 1 : 0;
|
||||||
|
const data = { rule: elem.name, value: value };
|
||||||
|
this.$store.commit("update1dSingleRule", data);
|
||||||
|
},
|
||||||
|
updateRules(event) {
|
||||||
|
// TODO : change this, awfully confusing
|
||||||
|
const elem = event.target;
|
||||||
|
const name = elem.value;
|
||||||
|
const rules = this.presetRules.find((ruleset) => {
|
||||||
|
return ruleset.name === name;
|
||||||
|
});
|
||||||
|
Object.keys(rules.rules).map((value) => {
|
||||||
|
const data = { name: name, rule: value, value: rules.rules[value] };
|
||||||
|
this.$store.commit("update1dSingleRule", data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateInitialState(event) {
|
||||||
|
const elem = event.target;
|
||||||
|
this.$store.commit("setInitial1dState", elem.value);
|
||||||
},
|
},
|
||||||
draw1d() {
|
draw1d() {
|
||||||
this.$root.$store.state.drawing = 1
|
this.$store.dispatch("draw1d");
|
||||||
this.$root.$emit('draw1d')
|
|
||||||
},
|
|
||||||
getRule(id) {
|
|
||||||
const rules = this.$store.state.rules1d
|
|
||||||
return rules[id]
|
|
||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
this.$root.$emit('reset')
|
this.$store.dispatch("reset");
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.menu-row a {
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -2,53 +2,94 @@
|
|||||||
<MenuRow row-title="General Options">
|
<MenuRow row-title="General Options">
|
||||||
<form>
|
<form>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label for="live">Canvas Resolution</label>
|
<label>Canvas Resolution</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
|
<label>Width</label>
|
||||||
<input
|
<input
|
||||||
id="canvasWidth"
|
id="canvasWidth"
|
||||||
name="canvasWidth"
|
name="canvasWidth"
|
||||||
type="number"
|
type="number"
|
||||||
|
step="10"
|
||||||
:value="canvasWidth"
|
:value="canvasWidth"
|
||||||
@input="updateCanvasWidth"
|
@input="updateCanvasWidth"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
|
<label>Height</label>
|
||||||
<input
|
<input
|
||||||
id="canvasHeight"
|
id="canvasHeight"
|
||||||
name="canvasHeight"
|
name="canvasHeight"
|
||||||
type="number"
|
type="number"
|
||||||
|
step="10"
|
||||||
:value="canvasHeight"
|
:value="canvasHeight"
|
||||||
@input="updateCanvasHeight"
|
@input="updateCanvasHeight"
|
||||||
>
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<label>Refresh Rate (ms)</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<input
|
||||||
|
id="refreshRate"
|
||||||
|
name="refreshRate"
|
||||||
|
type="number"
|
||||||
|
min="100"
|
||||||
|
step="100"
|
||||||
|
:value="refreshRate"
|
||||||
|
@input="updateRefreshRate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<label
|
||||||
|
>Invert Drawing Direction
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="drawingDirection === 'x'"
|
||||||
|
:value="drawingDirection"
|
||||||
|
@input="updateDrawingDirection"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</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: 'MenuGeneralOptions',
|
name: "MenuGeneralOptions",
|
||||||
components: {
|
components: {
|
||||||
MenuRow
|
MenuRow,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
canvasWidth: 'getCanvasWidth',
|
canvasWidth: "getCanvasWidth",
|
||||||
canvasHeight: 'getCanvasHeight'
|
canvasHeight: "getCanvasHeight",
|
||||||
})
|
refreshRate: "getRefreshRate",
|
||||||
|
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) {
|
||||||
}
|
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);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,34 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="menu-row">
|
<div class="menu-row">
|
||||||
<h2
|
<h2 :id="rowTitle" @click="updateActiveMenu">
|
||||||
@click="isHidden = !isHidden"
|
|
||||||
>
|
|
||||||
{{ rowTitle }}
|
{{ rowTitle }}
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div v-if="activeMenu === rowTitle" class="menu-row-content">
|
||||||
v-if="!isHidden"
|
|
||||||
class="menu-row-content"
|
|
||||||
>
|
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MenuRow',
|
name: "MenuRow",
|
||||||
props: {
|
props: {
|
||||||
rowTitle: {
|
rowTitle: {
|
||||||
type: String,
|
type: String,
|
||||||
default : ''
|
default: "",
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
},
|
||||||
return {
|
computed: {
|
||||||
isHidden: true
|
...mapGetters({
|
||||||
}
|
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);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -38,7 +43,12 @@ export default {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 2px solid darkgrey;
|
border: 2px solid darkgrey;
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
input[type="button"] {
|
input[type="button"] {
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
@ -50,13 +60,15 @@ input[type="button"] {
|
|||||||
.form-field {
|
.form-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-row {
|
.menu-row {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
label, .form-field label {
|
label,
|
||||||
|
.form-field label {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
15
src/main.js
15
src/main.js
@ -1,10 +1,9 @@
|
|||||||
import Vue from 'vue'
|
import { createApp } from "vue";
|
||||||
import App from './App.vue'
|
import App from "./App.vue";
|
||||||
import store from './store'
|
import { store } from "./store";
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
const app = createApp(App);
|
||||||
|
|
||||||
new Vue({
|
app.use(store);
|
||||||
store,
|
|
||||||
render: h => h(App)
|
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,25 +12,39 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a 2D board from a 1D CA initial state
|
// create a 2D board from a 1D CA initial state
|
||||||
|
// function createBoard(state, rules, height) {
|
||||||
|
// function createBoardAcc(s, h, acc) {
|
||||||
|
// if (h === 0) return acc;
|
||||||
|
// const newState = evolve1d(s, rules);
|
||||||
|
// const newAcc = acc.concat([s]);
|
||||||
|
// return createBoardAcc(newState, h - 1, newAcc);
|
||||||
|
// }
|
||||||
|
// return createBoardAcc(state, height, []);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// performance "choke point" in full imperative
|
||||||
function createBoard(state, rules, height) {
|
function createBoard(state, rules, height) {
|
||||||
function createBoardAcc(s, h, acc) {
|
var board = [];
|
||||||
if (h === 0) return acc;
|
let prevState = [];
|
||||||
const newState = evolve1d(s, rules);
|
for (let i = 0; i < height; i++) {
|
||||||
const newAcc = acc.concat([s]);
|
let nextState = [];
|
||||||
return createBoardAcc(newState, h - 1, newAcc);
|
if (i == 0) {
|
||||||
|
nextState = evolve1d(state, rules);
|
||||||
|
} else {
|
||||||
|
nextState = evolve1d(prevState, rules);
|
||||||
}
|
}
|
||||||
return createBoardAcc(state, height, []);
|
board = board.concat([nextState]);
|
||||||
|
prevState = nextState;
|
||||||
|
}
|
||||||
|
return board;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the neighbor of a given cell in a 2D CA board
|
// Find the neighbor of a given cell in a 2D CA board
|
||||||
@ -47,10 +61,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),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,20 +91,22 @@ 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
|
||||||
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;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -95,28 +115,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
|
||||||
|
function create1dStateOneCell(width) {
|
||||||
|
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
|
// Populates the first state of a 1D CA with cells returned
|
||||||
// by initFn
|
// by initFn
|
||||||
function initialState1d(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 initialState2d(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, initialState1d, initialState2d, evolve1d, evolve2d, conwayRules, createBoard,
|
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,68 +1,174 @@
|
|||||||
import Vue from 'vue'
|
/* TODO: terminology is to be changed for :
|
||||||
import Vuex from 'vuex'
|
canvas/board :
|
||||||
|
currently, the canvas object is named board,
|
||||||
|
while the structure used to store automata current state is named "board" as well. This is confusing
|
||||||
|
drawing board could be enough to lift any ambiguity
|
||||||
|
|
||||||
Vue.use(Vuex)
|
rules:
|
||||||
|
confusion bewteen ruleset and rules.
|
||||||
|
it's never clear if we refers to a rule or the whole (named) set
|
||||||
|
*/
|
||||||
|
import { createStore } from "vuex";
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export const store = createStore({
|
||||||
|
strict: process.env.NODE_ENV !== "production",
|
||||||
state: {
|
state: {
|
||||||
drawing: 0,
|
rules1d: {
|
||||||
rules1d : {
|
name: "rule 73",
|
||||||
"111" : 0,
|
rules: {
|
||||||
"110" : 1,
|
111: 0,
|
||||||
"101" : 0,
|
110: 1,
|
||||||
"100" : 0,
|
101: 0,
|
||||||
"011" : 1,
|
100: 0,
|
||||||
"010" : 0,
|
"011": 1,
|
||||||
"001" : 0,
|
"010": 0,
|
||||||
"000" : 1
|
"001": 0,
|
||||||
|
"000": 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cellProperties: {
|
cellProperties: {
|
||||||
size: 3,
|
size: 3,
|
||||||
liveColor: '#000000',
|
liveColor: "#000000",
|
||||||
deadColor: '#AA78E8',
|
deadColor: "#F5F5F5",
|
||||||
},
|
},
|
||||||
canvasWidth: 1280,
|
canvasWidth: 0,
|
||||||
canvasHeight: 720,
|
canvasHeight: 0,
|
||||||
|
boardWidth: 0,
|
||||||
|
boardHeight: 0,
|
||||||
|
refreshRate: 300,
|
||||||
|
initial1dState: "onecell",
|
||||||
|
activeMenu: "",
|
||||||
|
drawingDirection: "y",
|
||||||
|
lastBoard: {},
|
||||||
|
draw1d: false,
|
||||||
|
draw2d: false,
|
||||||
|
draw2dLast: false,
|
||||||
|
reset: false,
|
||||||
|
canDraw: true,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
update1dSingleRule(state, data) {
|
||||||
|
state.rules1d.name = data.name;
|
||||||
|
state.rules1d.rules[data.rule] = data.value;
|
||||||
|
},
|
||||||
update1dRules(state, data) {
|
update1dRules(state, data) {
|
||||||
state.rules1d[data.rule] = data.value
|
state.rules1d = data;
|
||||||
},
|
},
|
||||||
setCellProperties(state, data) {
|
setCellProperties(state, data) {
|
||||||
state.cellProperties[data.name] = data.value
|
state.cellProperties[data.name] = data.value;
|
||||||
},
|
|
||||||
setDrawingStatus(state, 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) {
|
||||||
|
state.refreshRate = data;
|
||||||
|
},
|
||||||
|
setInitial1dState(state, data) {
|
||||||
|
state.initial1dState = data;
|
||||||
|
},
|
||||||
|
setActiveMenu(state, data) {
|
||||||
|
state.activeMenu = data;
|
||||||
|
},
|
||||||
|
setDrawingDirection(state, data) {
|
||||||
|
state.drawingDirection = data;
|
||||||
|
},
|
||||||
|
setLastBoard(state, data) {
|
||||||
|
state.lastBoard = data;
|
||||||
|
},
|
||||||
|
setCanvas(state, data) {
|
||||||
|
state.canvas = data;
|
||||||
|
},
|
||||||
|
setContext(state, data) {
|
||||||
|
state.ctx = data;
|
||||||
|
},
|
||||||
|
toggleDraw1d(state, data) {
|
||||||
|
state.draw1d = data;
|
||||||
|
},
|
||||||
|
toggleDraw2d(state, data) {
|
||||||
|
state.draw2d = data;
|
||||||
|
},
|
||||||
|
toggleDraw2dLast(state, data) {
|
||||||
|
state.draw2dLast = data;
|
||||||
|
},
|
||||||
|
toggleReset(state, data) {
|
||||||
|
state.reset = data;
|
||||||
|
},
|
||||||
|
canDraw(state, data) {
|
||||||
|
state.canDraw = data;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getCellProperties(state) {
|
getCellProperties(state) {
|
||||||
return state.cellProperties
|
return state.cellProperties;
|
||||||
},
|
},
|
||||||
getRuleSet1d(state) {
|
get1dRules(state) {
|
||||||
return state.rules1d
|
return state.rules1d;
|
||||||
},
|
},
|
||||||
getRule1d(state) {
|
getRule1d(state) {
|
||||||
return (rule) => state.rules1d[rule]
|
return state.rules1d;
|
||||||
},
|
|
||||||
isDrawing(state) {
|
|
||||||
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) {
|
||||||
|
return state.refreshRate;
|
||||||
|
},
|
||||||
|
getInitial1dState(state) {
|
||||||
|
return state.initial1dState;
|
||||||
|
},
|
||||||
|
getActiveMenu(state) {
|
||||||
|
return state.activeMenu;
|
||||||
|
},
|
||||||
|
getDrawingDirection(state) {
|
||||||
|
return state.drawingDirection;
|
||||||
|
},
|
||||||
|
getLastBoard(state) {
|
||||||
|
return state.lastBoard;
|
||||||
|
},
|
||||||
|
getDraw1d(state) {
|
||||||
|
return state.draw1d;
|
||||||
|
},
|
||||||
|
getDraw2d(state) {
|
||||||
|
return state.draw2d;
|
||||||
|
},
|
||||||
|
getDraw2dLast(state) {
|
||||||
|
return state.draw2dLast;
|
||||||
|
},
|
||||||
|
getReset(state) {
|
||||||
|
return state.reset;
|
||||||
|
},
|
||||||
|
getCanDraw(state) {
|
||||||
|
return state.canDraw;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
draw1d({ commit }) {
|
||||||
|
commit("toggleDraw1d", true);
|
||||||
},
|
},
|
||||||
modules: {
|
draw2d({ commit }) {
|
||||||
}
|
commit("canDraw", true);
|
||||||
})
|
commit("toggleDraw2d", true);
|
||||||
|
},
|
||||||
|
draw2dLast({ commit }) {
|
||||||
|
commit("canDraw", true);
|
||||||
|
commit("toggleDraw2dLast", true);
|
||||||
|
},
|
||||||
|
reset({ dispatch, commit }) {
|
||||||
|
dispatch("stop");
|
||||||
|
commit("toggleReset", true);
|
||||||
|
},
|
||||||
|
stop({ commit }) {
|
||||||
|
commit("toggleDraw1d", false);
|
||||||
|
commit("toggleDraw2d", false);
|
||||||
|
commit("toggleDraw2dLast", false);
|
||||||
|
commit("canDraw", false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
modules: {},
|
||||||
|
});
|
||||||
|
16
vite.config.js
Normal file
16
vite.config.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
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",
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": 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