wip optimizing render

This commit is contained in:
Ali Gator 2022-12-19 16:00:38 +01:00
parent 61846b88a5
commit cc4a7b08b3
7 changed files with 119 additions and 86 deletions

View File

@ -44,3 +44,4 @@ See [Configuration Reference](https://vitejs.dev/guide/).
- https://en.wikipedia.org/wiki/Hashlife - https://en.wikipedia.org/wiki/Hashlife
- https://plato.stanford.edu/entries/cellular-automata/supplement.html - https://plato.stanford.edu/entries/cellular-automata/supplement.html
- https://www.conwaylife.com/wiki/Cellular_automaton - https://www.conwaylife.com/wiki/Cellular_automaton
- https://conwaylife.com/

View File

@ -25,6 +25,7 @@
lonelinessRules, lonelinessRules,
threebornRules, threebornRules,
highLifeRules, highLifeRules,
servietteRules,
evolve2d, evolve2d,
} from "../modules/automata.js"; } from "../modules/automata.js";
import { getRandomInt, sleep } from "../modules/common.js"; import { getRandomInt, sleep } from "../modules/common.js";
@ -44,11 +45,13 @@
loneliness: lonelinessRules, loneliness: lonelinessRules,
threeborn: threebornRules, threeborn: threebornRules,
highlife: highLifeRules, highlife: highLifeRules,
serviette: servietteRules,
}, },
}; };
}, },
computed: { computed: {
...mapState(globalStore, { ...mapState(globalStore, {
loop: "loop",
cellProperties: "cellProperties", cellProperties: "cellProperties",
ruleset: "ruleset1d", ruleset: "ruleset1d",
refreshRate: "refreshRate", refreshRate: "refreshRate",
@ -61,7 +64,6 @@
getDraw2dPicture: "draw2dpicture", getDraw2dPicture: "draw2dpicture",
boardWidth: "boardWidth", boardWidth: "boardWidth",
boardHeight: "boardHeight", boardHeight: "boardHeight",
picture: "picture",
selected2dRules: "selected2dRules", selected2dRules: "selected2dRules",
}), }),
...mapWritableState(globalStore, { ...mapWritableState(globalStore, {
@ -69,11 +71,15 @@
canvasWidth: "canvasWidth", canvasWidth: "canvasWidth",
canvasHeight: "canvasHeight", canvasHeight: "canvasHeight",
getReset: "reset", getReset: "reset",
picture: "picture",
}), }),
// used to determine the dimensions of the board // used to determine the dimensions of the board
max() { max() {
return Math.max(this.boardWidth, this.boardHeight); return Math.max(this.boardWidth, this.boardHeight);
}, },
selectedRules() {
return this.available2dRules[this.selected2dRules.id];
},
}, },
watch: { watch: {
getDraw1d(value) { getDraw1d(value) {
@ -111,21 +117,32 @@
"setBoardHeight", "setBoardHeight",
]), ]),
// draws the board on the canvas // draws the board on the canvas
drawCanvas(board) { drawCanvas(board, width, height) {
const props = this.cellProperties; const d = this.cellProperties.size;
board.map((row, y) => { // bool to RGBA colors
const d = props.size; const img = board.flat().reduce((acc, cell, index) => {
return row.map((cell, x) => { const color = cell === 1 ? 0 : 255;
this.ctx.fillStyle = (() => { const i = index * 4;
if (cell === 1) return props.liveColor; acc.data[i] = color;
return props.deadColor; acc.data[i + 1] = color;
})(); acc.data[i + 2] = color;
if (this.drawingDirection === "x") acc.data[i + 3] = 255;
this.ctx.fillRect(y * d, x * d, d, d); return acc;
else this.ctx.fillRect(x * d, y * d, d, d); }, new ImageData(width, height));
return cell; // rescale and draw
}); this.ctx.save();
}); this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.workCtx.putImageData(img, 0, 0);
this.ctx.imageSmoothingEnabled = false;
if (this.getDraw2dPicture != true) this.ctx.scale(d, d);
this.ctx.drawImage(
this.workCanvas,
0,
0,
this.canvasWidth,
this.canvasHeight
);
this.ctx.restore();
}, },
// create a first state, either a single living cell // create a first state, either a single living cell
// at the center or random ones // at the center or random ones
@ -134,101 +151,88 @@
return create1dStateOneCell(this.boardWidth); return create1dStateOneCell(this.boardWidth);
return create1dState(this.boardWidth, getRandomInt, [0, 2]); return create1dState(this.boardWidth, getRandomInt, [0, 2]);
}, },
// draw elementary automaton on the canvas based on selected ruleset // initialize board with random cells
draw1d() { randomInitialState() {
const initialState = this.compute1dInitialState(); return create2dState(
const board = createBoard(initialState, this.ruleset.rules, this.max);
this.lastBoard = Object.freeze(board);
this.drawCanvas(this.lastBoard);
this.toggleStop();
},
// draw 2D automaton on the canvas in a loop
draw2d(board) {
if (!this.canDraw) return;
const draw2dNext = async (b) => {
if (!this.canDraw) return;
const newBoard = evolve2d(
b,
this.available2dRules[this.selected2dRules.id]
);
this.drawCanvas(b, this.cellProperties);
this.lastBoard = Object.freeze(newBoard);
await sleep(this.refreshRate);
draw2dNext(newBoard);
};
return draw2dNext(board);
},
// draw 2d automaton from a new state
draw2dNew() {
const initialState = create2dState(
this.boardWidth, this.boardWidth,
this.boardHeight, this.boardHeight,
getRandomInt, getRandomInt,
[0, 2] [0, 2]
); );
const board = evolve2d(initialState, conwayRules); },
// draw elementary automaton on the canvas based on selected ruleset
draw1d() {
const initialState = this.compute1dInitialState();
const board = createBoard(initialState, this.ruleset.rules, this.max);
this.lastBoard = Object.freeze(board); this.lastBoard = Object.freeze(board);
this.draw2d(board); this.drawCanvas(this.lastBoard, this.boardWidth, this.boardHeight);
this.toggleStop();
},
// draw 2D automaton on the canvas in a loop
draw2d(board) {
const newBoard = evolve2d(board, this.selectedRules);
this.drawCanvas(newBoard, this.boardWidth, this.boardHeight);
this.lastBoard = Object.freeze(newBoard);
},
// draw 2d automaton in a loop, starting from passed state
async draw2dNext(board) {
requestAnimationFrame(() => {
if (!this.canDraw) return;
const newBoard = evolve2d(board, this.selectedRules);
this.draw2d(board);
this.lastBoard = Object.freeze(newBoard);
/* await sleep(this.refreshRate) */
return this.draw2dNext(newBoard);
});
},
// draw 2d automaton from a new state
async draw2dNew() {
if (!this.canDraw) return;
const initialState = this.randomInitialState();
let board = evolve2d(initialState, conwayRules);
if (this.loop) return this.draw2dNext(board);
else this.draw2d(board);
this.toggleStop();
}, },
// draw 2d automaton from the last known generated board // draw 2d automaton from the last known generated board
draw2dLast() { async draw2dLast() {
if (this.lastBoard != undefined) this.draw2d(this.lastBoard); if (!this.canDraw) return;
if (this.loop) return this.draw2dNext(this.lastBoard);
else this.draw2d(this.lastBoard);
this.toggleStop();
}, },
// draw 2d automaton from an uploaded picture. // draw 2d automaton from an uploaded picture.
// use the picture representation as an initial state // use the picture representation as an initial state
draw2dPicture() { draw2dPicture() {
// get image data by drawing it on a work canvas // get image data by drawing it on a work canvas
this.workCtx.drawImage( this.ctx.fillStyle = "black";
this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
this.ctx.drawImage(
this.picture, this.picture,
0, Math.floor((this.canvasWidth - this.picture.width) / 2),
0, Math.floor((this.canvasHeight - this.picture.height) / 2),
this.canvasWidth, this.picture.width,
this.canvasHeight this.picture.height
); );
const imgData = this.workCtx.getImageData(
const imgData = this.ctx.getImageData(
0, 0,
0, 0,
this.canvasWidth, this.canvasWidth,
this.canvasHeight this.canvasHeight
); );
// convert the image to black and white // convert the image into a 2D board of boolean based on pixel value
const black = picToBlackAndWhite(
imgData.data,
this.canvasWidth,
this.canvasHeight
);
// draw it back on the canvas
this.ctx.putImageData(black, 0, 0);
// draw the image back on the work canvas with the dimensions of the board
this.workCtx.drawImage(
this.picture,
0,
0,
this.boardWidth,
this.boardHeight
);
const resized = this.workCtx.getImageData(
0,
0,
this.boardWidth,
this.boardHeight
);
// convert the resized image into a 2D board of boolean based on pixel value
this.lastBoard = Object.freeze( this.lastBoard = Object.freeze(
picToBoard(resized.data, this.boardWidth, this.boardHeight) picToBoard(imgData.data, this.canvasWidth, this.canvasHeight)
); );
this.toggleStop(); this.toggleStop();
}, },
// stop drawing routines and clear the canvas // stop drawing routines and clear the canvas
reset() { reset() {
this.toggleStop(); this.toggleStop();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.lastBoard = {};
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.getReset = 0; this.getReset = 0;
}, },
}, },

View File

@ -67,7 +67,6 @@
preparePicture(event) { preparePicture(event) {
const files = event.target.files; const files = event.target.files;
this.picture = new Image(); this.picture = new Image();
this.picture.width = this.canvasWidth;
if (FileReader && files && files.length) { if (FileReader && files && files.length) {
const reader = new FileReader(); const reader = new FileReader();

View File

@ -91,6 +91,15 @@ function conwayRules(cell, neighbors) {
return cell; return cell;
} }
// Get the next evolution of a cell according to
// Conway's game of life rules
function servietteRules(cell, neighbors) {
// loneliness rule
if (cell === 0 && [2, 3, 4].find((x) => x == neighbors)) return 1;
// the cell remains the same if none apply
return 0;
}
// variation of the game of life where a // variation of the game of life where a
// cell comes to live if 6 neigbor cells are alive // cell comes to live if 6 neigbor cells are alive
function highLifeRules(cell, neighbors) { function highLifeRules(cell, neighbors) {
@ -200,6 +209,7 @@ export {
lonelinessRules, lonelinessRules,
threebornRules, threebornRules,
highLifeRules, highLifeRules,
servietteRules,
evolve1d, evolve1d,
evolve2d, evolve2d,
}; };

View File

@ -31,7 +31,7 @@ export function picToBoard(pixels, width, height) {
if (index % 4 == 0) { if (index % 4 == 0) {
const count = pixels[index] + pixels[index + 1] + pixels[index + 2]; const count = pixels[index] + pixels[index + 1] + pixels[index + 2];
const value = count >= 255 ? 1 : 0; const value = count >= 255 ? 1 : 0;
acc.push(value); acc[index] = value;
} }
return acc; return acc;
}, []); }, []);

View File

@ -77,6 +77,19 @@ const presetRuleset = [
"000": 1, "000": 1,
}, },
}, },
{
name: "unknown rule 2",
rules: {
100: 1,
101: 0,
110: 1,
111: 0,
"011": 0,
"010": 0,
"001": 0,
"000": 1,
},
},
]; ];
const initialStates = [ const initialStates = [
@ -122,6 +135,11 @@ const preset2dRules = [
description: description:
"Variation on Conway's Game of Life where a cell live if the six neighbor cells are alive", "Variation on Conway's Game of Life where a cell live if the six neighbor cells are alive",
}, },
{
id: "serviette",
name: "Serviette variation",
description: "bla",
},
]; ];
export { presetRuleset, initialStates, preset2dRules }; export { presetRuleset, initialStates, preset2dRules };

View File

@ -33,7 +33,7 @@ export const globalStore = defineStore("globalStore", {
refreshRate: 300, refreshRate: 300,
initial1dState: "onecell", initial1dState: "onecell",
drawingDirection: "y", drawingDirection: "y",
lastBoard: {}, lastBoard: null,
draw1d: false, draw1d: false,
draw2d: false, draw2d: false,
draw2dLast: false, draw2dLast: false,
@ -43,6 +43,7 @@ export const globalStore = defineStore("globalStore", {
picture: null, picture: null,
mainMenu: false, mainMenu: false,
activeSubMenu: "", activeSubMenu: "",
loop: true,
}; };
}, },
actions: { actions: {