new render method #8
@ -44,3 +44,4 @@ See [Configuration Reference](https://vitejs.dev/guide/).
|
||||
- https://en.wikipedia.org/wiki/Hashlife
|
||||
- https://plato.stanford.edu/entries/cellular-automata/supplement.html
|
||||
- https://www.conwaylife.com/wiki/Cellular_automaton
|
||||
- https://conwaylife.com/
|
||||
|
@ -25,6 +25,7 @@
|
||||
lonelinessRules,
|
||||
threebornRules,
|
||||
highLifeRules,
|
||||
servietteRules,
|
||||
evolve2d,
|
||||
} from "../modules/automata.js";
|
||||
import { getRandomInt, sleep } from "../modules/common.js";
|
||||
@ -44,11 +45,13 @@
|
||||
loneliness: lonelinessRules,
|
||||
threeborn: threebornRules,
|
||||
highlife: highLifeRules,
|
||||
serviette: servietteRules,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(globalStore, {
|
||||
loop: "loop",
|
||||
cellProperties: "cellProperties",
|
||||
ruleset: "ruleset1d",
|
||||
refreshRate: "refreshRate",
|
||||
@ -61,7 +64,6 @@
|
||||
getDraw2dPicture: "draw2dpicture",
|
||||
boardWidth: "boardWidth",
|
||||
boardHeight: "boardHeight",
|
||||
picture: "picture",
|
||||
selected2dRules: "selected2dRules",
|
||||
}),
|
||||
...mapWritableState(globalStore, {
|
||||
@ -69,11 +71,15 @@
|
||||
canvasWidth: "canvasWidth",
|
||||
canvasHeight: "canvasHeight",
|
||||
getReset: "reset",
|
||||
picture: "picture",
|
||||
}),
|
||||
// used to determine the dimensions of the board
|
||||
max() {
|
||||
return Math.max(this.boardWidth, this.boardHeight);
|
||||
},
|
||||
selectedRules() {
|
||||
return this.available2dRules[this.selected2dRules.id];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
getDraw1d(value) {
|
||||
@ -111,21 +117,32 @@
|
||||
"setBoardHeight",
|
||||
]),
|
||||
// draws the board on the canvas
|
||||
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;
|
||||
});
|
||||
});
|
||||
drawCanvas(board, width, height) {
|
||||
const d = this.cellProperties.size;
|
||||
// bool to RGBA colors
|
||||
const img = board.flat().reduce((acc, cell, index) => {
|
||||
const color = cell === 1 ? 0 : 255;
|
||||
const i = index * 4;
|
||||
acc.data[i] = color;
|
||||
acc.data[i + 1] = color;
|
||||
acc.data[i + 2] = color;
|
||||
acc.data[i + 3] = 255;
|
||||
return acc;
|
||||
}, new ImageData(width, height));
|
||||
// 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
|
||||
// at the center or random ones
|
||||
@ -134,101 +151,88 @@
|
||||
return create1dStateOneCell(this.boardWidth);
|
||||
return create1dState(this.boardWidth, getRandomInt, [0, 2]);
|
||||
},
|
||||
// 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.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(
|
||||
// initialize board with random cells
|
||||
randomInitialState() {
|
||||
return create2dState(
|
||||
this.boardWidth,
|
||||
this.boardHeight,
|
||||
getRandomInt,
|
||||
[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.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
|
||||
draw2dLast() {
|
||||
if (this.lastBoard != undefined) this.draw2d(this.lastBoard);
|
||||
async draw2dLast() {
|
||||
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.
|
||||
// use the picture representation as an initial state
|
||||
draw2dPicture() {
|
||||
// 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,
|
||||
0,
|
||||
0,
|
||||
this.canvasWidth,
|
||||
this.canvasHeight
|
||||
Math.floor((this.canvasWidth - this.picture.width) / 2),
|
||||
Math.floor((this.canvasHeight - this.picture.height) / 2),
|
||||
this.picture.width,
|
||||
this.picture.height
|
||||
);
|
||||
const imgData = this.workCtx.getImageData(
|
||||
|
||||
const imgData = this.ctx.getImageData(
|
||||
0,
|
||||
0,
|
||||
this.canvasWidth,
|
||||
this.canvasHeight
|
||||
);
|
||||
|
||||
// convert the image to black and white
|
||||
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
|
||||
// convert the image into a 2D board of boolean based on pixel value
|
||||
this.lastBoard = Object.freeze(
|
||||
picToBoard(resized.data, this.boardWidth, this.boardHeight)
|
||||
picToBoard(imgData.data, this.canvasWidth, this.canvasHeight)
|
||||
);
|
||||
|
||||
this.toggleStop();
|
||||
},
|
||||
// stop drawing routines and clear the canvas
|
||||
reset() {
|
||||
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;
|
||||
},
|
||||
},
|
||||
|
@ -67,7 +67,6 @@
|
||||
preparePicture(event) {
|
||||
const files = event.target.files;
|
||||
this.picture = new Image();
|
||||
this.picture.width = this.canvasWidth;
|
||||
if (FileReader && files && files.length) {
|
||||
const reader = new FileReader();
|
||||
|
||||
|
@ -91,6 +91,15 @@ function conwayRules(cell, neighbors) {
|
||||
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
|
||||
// cell comes to live if 6 neigbor cells are alive
|
||||
function highLifeRules(cell, neighbors) {
|
||||
@ -200,6 +209,7 @@ export {
|
||||
lonelinessRules,
|
||||
threebornRules,
|
||||
highLifeRules,
|
||||
servietteRules,
|
||||
evolve1d,
|
||||
evolve2d,
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ export function picToBoard(pixels, width, height) {
|
||||
if (index % 4 == 0) {
|
||||
const count = pixels[index] + pixels[index + 1] + pixels[index + 2];
|
||||
const value = count >= 255 ? 1 : 0;
|
||||
acc.push(value);
|
||||
acc[index] = value;
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
@ -77,6 +77,19 @@ const presetRuleset = [
|
||||
"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 = [
|
||||
@ -122,6 +135,11 @@ const preset2dRules = [
|
||||
description:
|
||||
"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 };
|
||||
|
@ -33,7 +33,7 @@ export const globalStore = defineStore("globalStore", {
|
||||
refreshRate: 300,
|
||||
initial1dState: "onecell",
|
||||
drawingDirection: "y",
|
||||
lastBoard: {},
|
||||
lastBoard: null,
|
||||
draw1d: false,
|
||||
draw2d: false,
|
||||
draw2dLast: false,
|
||||
@ -43,6 +43,7 @@ export const globalStore = defineStore("globalStore", {
|
||||
picture: null,
|
||||
mainMenu: false,
|
||||
activeSubMenu: "",
|
||||
loop: true,
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
|
Loading…
Reference in New Issue
Block a user