From 6f1e813316ffb893d418192e23e2653e18dc9f47 Mon Sep 17 00:00:00 2001 From: Gator Date: Mon, 10 Jan 2022 20:49:09 +0100 Subject: [PATCH 01/25] configurable refresh rate for 2d CA --- src/components/Canvas.vue | 5 +++-- src/components/MenuGeneralOptions.vue | 21 ++++++++++++++++++++- src/store/index.js | 7 +++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/components/Canvas.vue b/src/components/Canvas.vue index 361c436..1b68572 100644 --- a/src/components/Canvas.vue +++ b/src/components/Canvas.vue @@ -26,7 +26,8 @@ export default { rules: 'getRuleSet1d', drawing: 'isDrawing', canvasWidth: 'getCanvasWidth', - canvasHeight: 'getCanvasHeight' + canvasHeight: 'getCanvasHeight', + refreshRate: 'getRefreshRate' }) }, mounted() { @@ -82,7 +83,7 @@ export default { if (this.drawing === 0) return const newBoard = evolve2d(b, conwayRules) this.drawCanvas(b, this.cellProperties) - await sleep(300) + await sleep(this.refreshRate) draw2dNext(newBoard) } return draw2dNext(board) diff --git a/src/components/MenuGeneralOptions.vue b/src/components/MenuGeneralOptions.vue index f7f76d7..df419ff 100644 --- a/src/components/MenuGeneralOptions.vue +++ b/src/components/MenuGeneralOptions.vue @@ -22,6 +22,20 @@ @input="updateCanvasHeight" > +
+ +
+
+ +
@@ -37,7 +51,8 @@ export default { computed: { ...mapGetters({ canvasWidth: 'getCanvasWidth', - canvasHeight: 'getCanvasHeight' + canvasHeight: 'getCanvasHeight', + refreshRate: 'getRefreshRate' }) }, methods: { @@ -48,6 +63,10 @@ export default { updateCanvasWidth: function(event) { const elem = event.target this.$store.commit('setCanvasWidth', elem.value) + }, + updateRefreshRate: function(event) { + const elem = event.target + this.$store.commit('setRefreshRate', elem.value) } } } diff --git a/src/store/index.js b/src/store/index.js index d3dd52d..ab2b8b6 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -23,6 +23,7 @@ export default new Vuex.Store({ }, canvasWidth: 1280, canvasHeight: 720, + refreshRate: 300 }, mutations: { update1dRules(state, data) { @@ -39,6 +40,9 @@ export default new Vuex.Store({ }, setCanvasHeight(state, data) { state.canvasHeight = data; + }, + setRefreshRate(state, data) { + state.refreshRate = data; } }, getters: { @@ -59,6 +63,9 @@ export default new Vuex.Store({ }, getCanvasHeight(state) { return state.canvasHeight + }, + getRefreshRate(state) { + return state.refreshRate } }, actions: { -- 2.45.2 From 73e690aa07041cb2967c310a4934ec24fe2ff944 Mon Sep 17 00:00:00 2001 From: Gator Date: Mon, 10 Jan 2022 21:01:39 +0100 Subject: [PATCH 02/25] fluff pour number inputs --- src/components/MenuCellProperties.vue | 1 + src/components/MenuGeneralOptions.vue | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/MenuCellProperties.vue b/src/components/MenuCellProperties.vue index abc7daa..b1b6efc 100644 --- a/src/components/MenuCellProperties.vue +++ b/src/components/MenuCellProperties.vue @@ -24,6 +24,7 @@ diff --git a/src/components/MenuGeneralOptions.vue b/src/components/MenuGeneralOptions.vue index df419ff..fb858e6 100644 --- a/src/components/MenuGeneralOptions.vue +++ b/src/components/MenuGeneralOptions.vue @@ -2,28 +2,32 @@
- +
+
+
- +
Date: Mon, 10 Jan 2022 22:31:40 +0100 Subject: [PATCH 03/25] references in readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index fde767a..973f04f 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,5 @@ See [Configuration Reference](https://cli.vuejs.org/config/). ### References - https://natureofcode.com/book/chapter-7-cellular-automata/ - https://en.wikipedia.org/wiki/Hashlife +- https://plato.stanford.edu/entries/cellular-automata/supplement.html +- https://www.conwaylife.com/wiki/Cellular_automaton -- 2.45.2 From 93f9426f56e8849e310e5766e85e50a7cac35660 Mon Sep 17 00:00:00 2001 From: Gator Date: Tue, 11 Jan 2022 13:16:42 +0100 Subject: [PATCH 04/25] preset for 1D CA initial state --- src/components/Canvas.vue | 36 +++++++++++++------- src/components/MenuElementaryCA.vue | 52 ++++++++++++++++++++++++++--- src/modules/automata.js | 17 +++++++--- src/store/index.js | 9 ++++- 4 files changed, 93 insertions(+), 21 deletions(-) diff --git a/src/components/Canvas.vue b/src/components/Canvas.vue index 1b68572..88c10d4 100644 --- a/src/components/Canvas.vue +++ b/src/components/Canvas.vue @@ -9,7 +9,7 @@ + diff --git a/src/store/index.js b/src/store/index.js index 37145f1..5a7069e 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -27,9 +27,12 @@ export default new Vuex.Store({ initial1dState: "onecell" }, mutations: { - update1dRules(state, data) { + update1dSingleRule(state, data) { state.rules1d[data.rule] = data.value }, + update1dRules(state, data) { + state.rules1d = data + }, setCellProperties(state, data) { state.cellProperties[data.name] = data.value }, @@ -53,7 +56,7 @@ export default new Vuex.Store({ getCellProperties(state) { return state.cellProperties }, - getRuleSet1d(state) { + get1dRules(state) { return state.rules1d }, getRule1d(state) { -- 2.45.2 From 59b102e5a7bbcb6a59f010c395e3df35c6ac2f39 Mon Sep 17 00:00:00 2001 From: Gator Date: Thu, 13 Jan 2022 14:05:08 +0100 Subject: [PATCH 07/25] keeps a single menu open --- src/components/MenuRow.vue | 25 ++++++++++++++++++++----- src/store/index.js | 11 +++++++++-- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/components/MenuRow.vue b/src/components/MenuRow.vue index 65579fd..8cef067 100644 --- a/src/components/MenuRow.vue +++ b/src/components/MenuRow.vue @@ -1,12 +1,13 @@ -- 2.45.2 From f41e415b325a72da7c194cb6db88c5260783fa3e Mon Sep 17 00:00:00 2001 From: Gator Date: Thu, 13 Jan 2022 14:24:43 +0100 Subject: [PATCH 09/25] canvas dimensions stored but never used --- src/components/Canvas.vue | 4 ++-- src/components/MenuElementaryCA.vue | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/Canvas.vue b/src/components/Canvas.vue index 5e4f74a..dc68614 100644 --- a/src/components/Canvas.vue +++ b/src/components/Canvas.vue @@ -32,12 +32,12 @@ export default { }), boardWidth: function() { return Math.floor( - this.canvas.width / + this.canvasWidth / this.cellProperties.size) }, boardHeight: function() { return Math.floor( - this.canvas.height / + this.canvasHeight / this.cellProperties.size) } }, diff --git a/src/components/MenuElementaryCA.vue b/src/components/MenuElementaryCA.vue index 56423c7..1e186d7 100644 --- a/src/components/MenuElementaryCA.vue +++ b/src/components/MenuElementaryCA.vue @@ -140,10 +140,8 @@ export default { const elem = event.target const name = elem.value const rules = this.presetRules[name] - console.log(rules) Object.keys(rules).map((index) => { const data = { 'rule' : index, 'value' : rules[index]} - console.log(rules[index]) this.$store.commit('update1dSingleRule', data) }) }, -- 2.45.2 From 980cdc35a22ac366d5540ef120c743b1c352d69e Mon Sep 17 00:00:00 2001 From: Gator Date: Thu, 13 Jan 2022 18:07:41 +0100 Subject: [PATCH 10/25] option to change drawing direction --- src/components/Canvas.vue | 10 +++++++--- src/components/MenuElementaryCA.vue | 3 ++- src/components/MenuGeneralOptions.vue | 19 ++++++++++++++++++- src/store/index.js | 15 +++++++++++---- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/components/Canvas.vue b/src/components/Canvas.vue index dc68614..a0153de 100644 --- a/src/components/Canvas.vue +++ b/src/components/Canvas.vue @@ -28,7 +28,8 @@ export default { canvasWidth: 'getCanvasWidth', canvasHeight: 'getCanvasHeight', refreshRate: 'getRefreshRate', - initial1dState: 'getInitial1dState' + initial1dState: 'getInitial1dState', + drawingDirection: 'getDrawingDirection' }), boardWidth: function() { return Math.floor( @@ -61,7 +62,10 @@ export default { if (cell === 1) return props.liveColor return props.deadColor })() - this.ctx.fillRect(x * d, y * d, d, d) + 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 }, ) @@ -77,7 +81,7 @@ export default { const board = createBoard( initialState, this.rules, - this.boardHeight + this.boardWidth ) this.drawCanvas(board) }, diff --git a/src/components/MenuElementaryCA.vue b/src/components/MenuElementaryCA.vue index 1e186d7..37656d1 100644 --- a/src/components/MenuElementaryCA.vue +++ b/src/components/MenuElementaryCA.vue @@ -94,7 +94,8 @@ export default { "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} + "rule 54?" : {"100":1,"101":0,"110":1,"111":1,"011":0,"010":1,"001":1,"000":0}, + "unknown rule" : {"100":0,"101":0,"110":0,"111":1,"011":0,"010":0,"001":1,"000":1} }, initial1dStates: [ { id : "onecell", diff --git a/src/components/MenuGeneralOptions.vue b/src/components/MenuGeneralOptions.vue index fb858e6..c51dd89 100644 --- a/src/components/MenuGeneralOptions.vue +++ b/src/components/MenuGeneralOptions.vue @@ -40,6 +40,16 @@ @input="updateRefreshRate" >
+
+ +
@@ -56,7 +66,8 @@ export default { ...mapGetters({ canvasWidth: 'getCanvasWidth', canvasHeight: 'getCanvasHeight', - refreshRate: 'getRefreshRate' + refreshRate: 'getRefreshRate', + drawingDirection: 'getDrawingDirection' }) }, methods: { @@ -71,6 +82,12 @@ export default { 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) } } } diff --git a/src/store/index.js b/src/store/index.js index c3bfbcb..4b5fa81 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -25,7 +25,8 @@ export default new Vuex.Store({ canvasHeight: 720, refreshRate: 300, initial1dState: "onecell", - activeMenu: "Elementary Cellular Automata" + activeMenu: "Elementary Cellular Automata", + drawingDirection: "y" }, mutations: { update1dSingleRule(state, data) { @@ -41,13 +42,13 @@ export default new Vuex.Store({ state.drawing = data }, setCanvasWidth(state, data) { - state.canvasWidth = data; + state.canvasWidth = data }, setCanvasHeight(state, data) { - state.canvasHeight = data; + state.canvasHeight = data }, setRefreshRate(state, data) { - state.refreshRate = data; + state.refreshRate = data }, setInitial1dState(state, data) { state.initial1dState = data @@ -55,6 +56,9 @@ export default new Vuex.Store({ setActiveMenu(state, data) { state.activeMenu = data }, + setDrawingDirection(state, data) { + state.drawingDirection = data + }, }, getters: { getCellProperties(state) { @@ -83,6 +87,9 @@ export default new Vuex.Store({ }, getActiveMenu(state) { return state.activeMenu + }, + getDrawingDirection(state) { + return state.drawingDirection } }, actions: { -- 2.45.2 From 523dda45e9a92b200df80491beb305c0d99c9c3d Mon Sep 17 00:00:00 2001 From: Gator Date: Thu, 13 Jan 2022 23:13:22 +0100 Subject: [PATCH 11/25] option : last generated 2d board as initial state --- src/components/Canvas.vue | 31 +++++++++++++++++++++---------- src/components/Menu2dCA.vue | 20 +++++++++++++++++++- src/store/index.js | 9 ++++++++- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/components/Canvas.vue b/src/components/Canvas.vue index a0153de..7865f36 100644 --- a/src/components/Canvas.vue +++ b/src/components/Canvas.vue @@ -29,7 +29,8 @@ export default { canvasHeight: 'getCanvasHeight', refreshRate: 'getRefreshRate', initial1dState: 'getInitial1dState', - drawingDirection: 'getDrawingDirection' + drawingDirection: 'getDrawingDirection', + lastBoard: 'getLastBoard' }), boardWidth: function() { return Math.floor( @@ -46,7 +47,8 @@ export default { 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('draw2dNew', () => { this.draw2dNew() }) + this.$root.$on('draw2dLast', () => { this.draw2dLast() }) this.$root.$on('reset', () => { this.reset() }) this.$root.$on('stop', () => { this.stop() }) }, @@ -83,17 +85,11 @@ export default { this.rules, this.boardWidth ) + this.$store.commit('setLastBoard', board) this.drawCanvas(board) }, - async draw2d() { + async draw2d(board) { if (this.drawing === 0) return - const initialState = create2dState( - this.boardWidth, - this.boardHeight, - getRandomInt, - [0, 2], - ); - const board = evolve2d(initialState, conwayRules); const draw2dNext = async (b) => { if (this.drawing === 0) return @@ -104,6 +100,21 @@ export default { } 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) }, diff --git a/src/components/Menu2dCA.vue b/src/components/Menu2dCA.vue index 81fcdbf..20e8287 100644 --- a/src/components/Menu2dCA.vue +++ b/src/components/Menu2dCA.vue @@ -1,5 +1,13 @@ diff --git a/src/components/MenuCellProperties.vue b/src/components/MenuCellProperties.vue index b1b6efc..a88bf50 100644 --- a/src/components/MenuCellProperties.vue +++ b/src/components/MenuCellProperties.vue @@ -8,7 +8,7 @@ type="color" @value="cellProperties.liveColor" @input="updateCellProperties" - > + />
@@ -17,7 +17,7 @@ type="color" :value="cellProperties.deadColor" @input="updateCellProperties" - > + />
@@ -27,36 +27,36 @@ min="1" :value="cellProperties.size" @input="updateCellProperties" - > + />
diff --git a/src/components/MenuElementaryCA.vue b/src/components/MenuElementaryCA.vue index 37656d1..914708d 100644 --- a/src/components/MenuElementaryCA.vue +++ b/src/components/MenuElementaryCA.vue @@ -2,8 +2,9 @@
-
-
-
- + + />
diff --git a/src/components/MenuGeneralOptions.vue b/src/components/MenuGeneralOptions.vue index c51dd89..66cb8aa 100644 --- a/src/components/MenuGeneralOptions.vue +++ b/src/components/MenuGeneralOptions.vue @@ -13,7 +13,7 @@ step="10" :value="canvasWidth" @input="updateCanvasWidth" - > + />
@@ -24,7 +24,7 @@ step="10" :value="canvasHeight" @input="updateCanvasHeight" - > + />
@@ -38,57 +38,58 @@ step="100" :value="refreshRate" @input="updateRefreshRate" - > + />
-
- + diff --git a/src/components/MenuRow.vue b/src/components/MenuRow.vue index 42e053a..1db739a 100644 --- a/src/components/MenuRow.vue +++ b/src/components/MenuRow.vue @@ -1,82 +1,75 @@ diff --git a/src/main.js b/src/main.js index ba1641e..1f764d8 100644 --- a/src/main.js +++ b/src/main.js @@ -1,10 +1,10 @@ -import Vue from 'vue' -import App from './App.vue' -import store from './store' +import Vue from "vue"; +import App from "./App.vue"; +import store from "./store"; -Vue.config.productionTip = false +Vue.config.productionTip = false; new Vue({ store, - render: h => h(App) -}).$mount('#app') + render: (h) => h(App), +}).$mount("#app"); diff --git a/src/modules/automata.js b/src/modules/automata.js index 6f9454d..4fb914c 100644 --- a/src/modules/automata.js +++ b/src/modules/automata.js @@ -1,7 +1,7 @@ // handles negative index and index bigger than its array length function guard(index, array) { - if (index > (array.length - 1)) return 0; - if (index < 0) return (array.length - 1); + if (index > array.length - 1) return 0; + if (index < 0) return array.length - 1; return index; } @@ -12,11 +12,8 @@ function evolve1d(state, rules) { return state[safeIndex]; } const newState = state.map((_, x) => { - const cells = [ - getCell(x - 1), - getCell(x), - getCell(x + 1)]; - return rules[cells.join('')]; + const cells = [getCell(x - 1), getCell(x), getCell(x + 1)]; + return rules[cells.join("")]; }); return newState.map(Number); @@ -47,10 +44,14 @@ function getCellNeighbors(board, cellCoordinates) { // the current cell is not included in the result return [ - getCell(x - 1, y - 1), getCell(x, y - 1), - getCell(x + 1, y - 1), getCell(x - 1, y), - getCell(x + 1, y), getCell(x - 1, y + 1), - getCell(x, y + 1), getCell(x + 1, y - 1), + getCell(x - 1, y - 1), + getCell(x, y - 1), + getCell(x + 1, y - 1), + getCell(x - 1, y), + getCell(x + 1, y), + getCell(x - 1, y + 1), + getCell(x, y + 1), + getCell(x + 1, y - 1), ]; } @@ -75,58 +76,60 @@ function conwayRules(cell, neighbors) { // get the next evolution of a 2D CA initial state // Rules : Moore neighborhood function evolve2d(board, rulesFn) { - return board.map((row, x) => row.map((cell, y) => { - const neighbors = getCellNeighbors(board, [x, y]); - const sum = getNeighborsSum(neighbors); - return rulesFn(cell, sum); - })); + return board.map((row, x) => + row.map((cell, y) => { + const neighbors = getCellNeighbors(board, [x, y]); + const sum = getNeighborsSum(neighbors); + return rulesFn(cell, sum); + }) + ); } function getDrawingValues(state, acc, cell) { const d = cell.dimension; - return Object.keys(state).map( - (key) => { - const fillStyle = (() => { - if (state[key] === '1') return cell.liveColor; - return cell.deadColor; - })(); + return Object.keys(state).map((key) => { + const fillStyle = (() => { + if (state[key] === "1") return cell.liveColor; + return cell.deadColor; + })(); - return { - move: [key * d, acc * d], - fill: [key * d, acc * d, d, d], - fillStyle, - }; - }, - ); + return { + move: [key * d, acc * d], + fill: [key * d, acc * d, d, d], + fillStyle, + }; + }); } // Populates the first state with a single living cell in the center function create1dStateOneCell(width) { - return [...Array(width)].map( - (cell, index) => { - if (index === width / 2 || index === width + 1 / 2) return 1 - return 0 - }) + return [...Array(width)].map((cell, index) => { + if (index === width / 2 || index === width + 1 / 2) return 1; + return 0; + }); } // Populates the first state of a 1D CA with cells returned // by initFn function create1dState(width, initFn, args) { - return [...Array(width)].map( - () => initFn(...args) - ); + return [...Array(width)].map(() => initFn(...args)); } // Populates the first state of a 2D CA with cells returned // by initFn function create2dState(width, height, initFn, args) { - return [...Array(height)].map( - () => [...Array(width)].map( - () => initFn(...args), - ), + return [...Array(height)].map(() => + [...Array(width)].map(() => initFn(...args)) ); } export { - getDrawingValues, create1dState, create2dState, createBoard, create1dStateOneCell, conwayRules, evolve1d, evolve2d + getDrawingValues, + create1dState, + create2dState, + createBoard, + create1dStateOneCell, + conwayRules, + evolve1d, + evolve2d, }; diff --git a/src/modules/main.js b/src/modules/main.js deleted file mode 100644 index d203fe3..0000000 --- a/src/modules/main.js +++ /dev/null @@ -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; -}); diff --git a/src/store/index.js b/src/store/index.js index a111bc5..4847d02 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,25 +1,25 @@ -import Vue from 'vue' -import Vuex from 'vuex' +import Vue from "vue"; +import Vuex from "vuex"; -Vue.use(Vuex) +Vue.use(Vuex); export default new Vuex.Store({ state: { drawing: 0, - rules1d : { - "111" : 0, - "110" : 1, - "101" : 0, - "100" : 0, - "011" : 1, - "010" : 0, - "001" : 0, - "000" : 1 + rules1d: { + 111: 0, + 110: 1, + 101: 0, + 100: 0, + "011": 1, + "010": 0, + "001": 0, + "000": 1, }, cellProperties: { size: 3, - liveColor: '#000000', - deadColor: '#F5F5F5', + liveColor: "#000000", + deadColor: "#F5F5F5", }, canvasWidth: 0, canvasHeight: 0, @@ -27,80 +27,78 @@ export default new Vuex.Store({ initial1dState: "onecell", activeMenu: "", drawingDirection: "y", - lastBoard: {} + lastBoard: {}, }, mutations: { update1dSingleRule(state, data) { - state.rules1d[data.rule] = data.value + state.rules1d[data.rule] = data.value; }, update1dRules(state, data) { - state.rules1d = data + state.rules1d = data; }, setCellProperties(state, data) { - state.cellProperties[data.name] = data.value + state.cellProperties[data.name] = data.value; }, setDrawingStatus(state, data) { - state.drawing = data + state.drawing = data; }, setCanvasWidth(state, data) { - state.canvasWidth = data + state.canvasWidth = data; }, setCanvasHeight(state, data) { - state.canvasHeight = data + state.canvasHeight = data; }, setRefreshRate(state, data) { - state.refreshRate = data + state.refreshRate = data; }, setInitial1dState(state, data) { - state.initial1dState = data + state.initial1dState = data; }, setActiveMenu(state, data) { - state.activeMenu = data + state.activeMenu = data; }, setDrawingDirection(state, data) { - state.drawingDirection = data + state.drawingDirection = data; }, setLastBoard(state, data) { - state.lastBoard = data + state.lastBoard = data; }, }, getters: { getCellProperties(state) { - return state.cellProperties + return state.cellProperties; }, get1dRules(state) { - return state.rules1d + return state.rules1d; }, getRule1d(state) { - return (rule) => state.rules1d[rule] + return (rule) => state.rules1d[rule]; }, isDrawing(state) { - return state.drawing + return state.drawing; }, getCanvasWidth(state) { - return state.canvasWidth + return state.canvasWidth; }, getCanvasHeight(state) { - return state.canvasHeight + return state.canvasHeight; }, getRefreshRate(state) { - return state.refreshRate + return state.refreshRate; }, getInitial1dState(state) { - return state.initial1dState + return state.initial1dState; }, getActiveMenu(state) { - return state.activeMenu + return state.activeMenu; }, getDrawingDirection(state) { - return state.drawingDirection + return state.drawingDirection; }, getLastBoard(state) { - return state.lastBoard - } + return state.lastBoard; + }, }, - actions: { - }, - modules: { - } -}) + actions: {}, + modules: {}, +}); diff --git a/vite.config.js b/vite.config.js index fc8661f..45f01b9 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,16 +1,16 @@ -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; const path = require("path"); export default defineConfig({ plugins: [vue()], server: { - host: '127.0.0.1' + host: "127.0.0.1", }, resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, -}) +}); diff --git a/vue.config.js b/vue.config.js index 4463e85..680f0de 100644 --- a/vue.config.js +++ b/vue.config.js @@ -3,11 +3,11 @@ module.exports = { devServer: { overlay: { warnings: true, - errors: true + errors: true, }, watchOptions: { ignored: [/node_modules/, /public/, /\.#/], - } - } - } -} + }, + }, + }, +}; -- 2.45.2 From 20434ab52a62d56c913bc4011b640845f1fca289 Mon Sep 17 00:00:00 2001 From: Gator Date: Wed, 30 Nov 2022 19:37:35 +0100 Subject: [PATCH 19/25] new global api for vue and vuex --- src/App.vue | 3 +-- src/main.js | 13 ++++++------- src/store/index.js | 7 ++----- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/App.vue b/src/App.vue index 379637c..cf18b8c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,8 +12,7 @@ import MainMenu from "./components/MainMenu.vue"; import CanvasBoard from "./components/CanvasBoard.vue"; -export default { - name: "App", +export const App = { components: { MainMenu, CanvasBoard, diff --git a/src/main.js b/src/main.js index 1f764d8..654699f 100644 --- a/src/main.js +++ b/src/main.js @@ -1,10 +1,9 @@ -import Vue from "vue"; +import { createApp } from 'vue' import App from "./App.vue"; -import store from "./store"; +import { store } from "./store"; -Vue.config.productionTip = false; +const app = createApp(App) -new Vue({ - store, - render: (h) => h(App), -}).$mount("#app"); +app.use(store) + +app.mount('#app') diff --git a/src/store/index.js b/src/store/index.js index 4847d02..82c3907 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,9 +1,6 @@ -import Vue from "vue"; -import Vuex from "vuex"; +import { createStore } from 'vuex' -Vue.use(Vuex); - -export default new Vuex.Store({ +export const store = createStore({ state: { drawing: 0, rules1d: { -- 2.45.2 From 454befaa24444e83c1fce2c2d3b0965b1bc10aad Mon Sep 17 00:00:00 2001 From: Gator Date: Wed, 30 Nov 2022 20:57:26 +0100 Subject: [PATCH 20/25] renamed canvas component (again) and fixed some issues --- src/App.vue | 11 ++++---- src/components/{CanvasBoard.vue => Board.vue} | 26 +++++-------------- 2 files changed, 12 insertions(+), 25 deletions(-) rename src/components/{CanvasBoard.vue => Board.vue} (88%) diff --git a/src/App.vue b/src/App.vue index cf18b8c..5489aeb 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,19 +3,20 @@

Cellular Automata Explorer

- +
@@ -26,7 +27,7 @@ export const App = { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; - color: #2c3e50; + /* color: #2c3e50; */ } * { diff --git a/src/components/CanvasBoard.vue b/src/components/Board.vue similarity index 88% rename from src/components/CanvasBoard.vue rename to src/components/Board.vue index eda14c6..ba75795 100644 --- a/src/components/CanvasBoard.vue +++ b/src/components/Board.vue @@ -1,11 +1,12 @@ diff --git a/src/assets/main.css b/src/assets/main.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Board.vue b/src/components/Board.vue deleted file mode 100644 index ba75795..0000000 --- a/src/components/Board.vue +++ /dev/null @@ -1,128 +0,0 @@ - - - diff --git a/src/components/CanvasBoard.vue b/src/components/CanvasBoard.vue new file mode 100644 index 0000000..d307a31 --- /dev/null +++ b/src/components/CanvasBoard.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/components/MenuElementaryCA.vue b/src/components/MenuElementaryCA.vue index 914708d..4e3ce31 100644 --- a/src/components/MenuElementaryCA.vue +++ b/src/components/MenuElementaryCA.vue @@ -2,17 +2,16 @@
-
@@ -72,150 +73,171 @@ diff --git a/src/main.js b/src/main.js index 654699f..94d53fb 100644 --- a/src/main.js +++ b/src/main.js @@ -2,8 +2,12 @@ import { createApp } from 'vue' import App from "./App.vue"; import { store } from "./store"; +const gobalsProperties = {'canvas': null}; + const app = createApp(App) +app.provide('gobalsProperties', gobalsProperties) + app.use(store) app.mount('#app') diff --git a/src/store/index.js b/src/store/index.js index 82c3907..af39d2d 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,25 +1,53 @@ +/* TODO: terminology is to be changed for : +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 + +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' +import { + create1dState, + create1dStateOneCell, + create2dState, + createBoard, + conwayRules, + evolve2d, +} from "../modules/automata.js"; +import { getRandomInt, sleep } from "../modules/common.js"; export const store = createStore({ + strict: process.env.NODE_ENV !== 'production', state: { drawing: 0, rules1d: { - 111: 0, - 110: 1, - 101: 0, - 100: 0, - "011": 1, - "010": 0, - "001": 0, - "000": 1, + name: "rule 73", + rules: + { + 111: 0, + 110: 1, + 101: 0, + 100: 0, + "011": 1, + "010": 0, + "001": 0, + "000": 1, + } }, cellProperties: { size: 3, liveColor: "#000000", deadColor: "#F5F5F5", }, + canvas: null, + ctx: null, canvasWidth: 0, canvasHeight: 0, + boardWidth: 0, + boardHeight: 0, refreshRate: 300, initial1dState: "onecell", activeMenu: "", @@ -28,7 +56,8 @@ export const store = createStore({ }, mutations: { update1dSingleRule(state, data) { - state.rules1d[data.rule] = data.value; + state.rules1d.name = data.name + state.rules1d.rules[data.rule] = data.value; }, update1dRules(state, data) { state.rules1d = data; @@ -60,6 +89,20 @@ export const store = createStore({ setLastBoard(state, data) { state.lastBoard = data; }, + setCanvas(state, data) { + state.canvas = data + }, + setContext(state, data) { + state.ctx = data + }, + setBoardWidth(state) { + const width = Math.floor(state.canvasWidth / state.cellProperties.size); + state.boardWidth = width + }, + setBoardHeight(state) { + const height = Math.floor(state.canvasHeight / state.cellProperties.size); + state.boardHeight = height + }, }, getters: { getCellProperties(state) { @@ -69,7 +112,8 @@ export const store = createStore({ return state.rules1d; }, getRule1d(state) { - return (rule) => state.rules1d[rule]; + // getter with side-effect. no work + return state.rules1d; }, isDrawing(state) { return state.drawing; @@ -96,6 +140,84 @@ export const store = createStore({ return state.lastBoard; }, }, - actions: {}, + actions: { + async drawCanvas({state}, board) { + /** Draw the board on the canvas according to the + cells' properties and drawing direction */ + const props = state.cellProperties + board.map((row, y) => { + const d = props.size; + return row.map((cell, x) => { + state.ctx.fillStyle = (() => { + if (cell === 1) return props.liveColor; + return props.deadColor; + })(); + if (state.drawingDirection === "x") + state.ctx.fillRect(y * d, x * d, d, d); + else state.ctx.fillRect(x * d, y * d, d, d); + return cell; + }); + }); + }, + async compute1dInitialState({state}) { + /** + create the initial state for an elementary automaton based : + - on the selected board dimensions + - if we want to start from a single cell or several random ones + */ + if (state.initial1dState === "onecell") + return create1dStateOneCell(state.canvasWidth); + return create1dState(state.canvasWidth, getRandomInt, [0, 2]); + }, + async draw1d({state, commit, dispatch}) { + /** draw an elementary cellular automaton from an initial state and + a set of rules */ + const initialState = await dispatch("compute1dInitialState") + const board = createBoard(initialState, state.rules1d.rules, state.boardWidth); + commit("setLastBoard", board); + await dispatch("drawCanvas", board); + }, + async draw2d({state, dispatch}, board) { + /** draw a 2D cellular automaton from an initial state and + a set of rules */ + if (this.drawing === 0) return; + + const draw2dNext = async (b) => { + if (state.drawing === 0) return; + const newBoard = evolve2d(b, conwayRules); + dispatch("drawCanvas", b, this.cellProperties); + await sleep(this.refreshRate); + draw2dNext(newBoard); + }; + return draw2dNext(board); + }, + async draw2dNew({state, commit, dispatch}) { + /** draw a 2d cellular automaton from a random initial state + and a set of rules */ + const initialState = create2dState( + state.boardWidth, + state.boardHeight, + getRandomInt, + [0, 2] + ); + const board = evolve2d(initialState, conwayRules); + commit("setLastBoard", board); + await dispatch("draw2d", board); + }, + async draw2dLast({state, dispatch}) { + /** draw a 2d cellular automaton from the latest known state + of the board and a set of rules */ + await dispatch("draw2d", state.lastBoard); + }, + stop({ commit }) { + /** stop currently running drawing routine */ + commit("setDrawingStatus", 0); + }, + reset({state, dispatch}) { + /** stop currently running drawing routine and clear the board */ + dispatch("stop"); + state.ctx.clearRect(0, 0, state.canvasWidth, state.canvasHeight); + } + }, modules: {}, }); -- 2.45.2 From d0802d850bd60747e0874308e7bc4386deded5e9 Mon Sep 17 00:00:00 2001 From: Gator Date: Thu, 1 Dec 2022 20:32:19 +0100 Subject: [PATCH 23/25] createBoard imperative version --- src/modules/automata.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/modules/automata.js b/src/modules/automata.js index 4fb914c..e8ee506 100644 --- a/src/modules/automata.js +++ b/src/modules/automata.js @@ -20,14 +20,31 @@ function evolve1d(state, rules) { } // 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 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); + var board = []; + let prevState = []; + for (let i = 0; i < height; i++) { + let nextState = []; + if (i == 0) { + nextState = evolve1d(state, rules); + } else { + nextState = evolve1d(prevState, rules); + } + board = board.concat([nextState]); + prevState = nextState; } - return createBoardAcc(state, height, []); + return board; } // Find the neighbor of a given cell in a 2D CA board -- 2.45.2 From 9b39bba6e7e54a7d404e851ab8db94ae184ab14c Mon Sep 17 00:00:00 2001 From: Gator Date: Thu, 1 Dec 2022 20:37:03 +0100 Subject: [PATCH 24/25] drawing logic back into component --- .eslintrc.js | 15 +- src/App.vue | 2 +- src/components/CanvasBoard.vue | 165 ++++++++++++----- src/components/MenuElementaryCA.vue | 263 ++++++++++++++-------------- src/main.js | 12 +- src/store/index.js | 124 ++----------- 6 files changed, 286 insertions(+), 295 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b7db048..edac359 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -14,13 +14,12 @@ module.exports = { rules: { // override/add rules settings here, such as: // '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', - + "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", }, }; diff --git a/src/App.vue b/src/App.vue index 10b1827..1519d90 100644 --- a/src/App.vue +++ b/src/App.vue @@ -27,7 +27,7 @@ export default { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; - /* color: #2c3e50; */ + /* color: #2c3e50; */ } * { diff --git a/src/components/CanvasBoard.vue b/src/components/CanvasBoard.vue index d307a31..1f1338d 100644 --- a/src/components/CanvasBoard.vue +++ b/src/components/CanvasBoard.vue @@ -5,52 +5,135 @@ ref="canvas-board" :width="canvasWidth" :height="canvasHeight" - > - + /> diff --git a/src/components/MenuElementaryCA.vue b/src/components/MenuElementaryCA.vue index 4e3ce31..d968453 100644 --- a/src/components/MenuElementaryCA.vue +++ b/src/components/MenuElementaryCA.vue @@ -11,7 +11,8 @@ @input="updateInitialState" >