Compare commits

..

No commits in common. "6dad5a9dd9192ae8e76ace722c17ca594236d5f5" and "1264c2fbbecfcf489595cd46a03a114f439676f6" have entirely different histories.

11 changed files with 117 additions and 343 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

@ -1,81 +1,27 @@
<template>
<div id="main">
<h1 id="main-title">
<span id="burger-toggle" @click="toggleMainMenu">{{
mainMenu == true ? "▼" : "☰"
}}</span>
Cellular Automata Explorer
</h1>
<h1 id="main-title">Cellular Automata Explorer</h1>
<div id="container">
<MainMenu v-if="mainMenu || windowWidth >= 800" />
<MainMenu />
<CanvasBoard />
</div>
<MenuReset row-title="" />
</div>
</template>
<script>
import MainMenu from "./components/MainMenu.vue";
import CanvasBoard from "./components/CanvasBoard.vue";
import MenuReset from "./components/MenuReset.vue";
import { mapWritableState, mapActions } from "pinia";
import { globalStore } from "./stores/index.js";
export default {
name: "App",
components: {
MainMenu,
MenuReset,
CanvasBoard,
},
data() {
return {
mainMenu: false,
windowWidth: window.innerWidth,
};
},
computed: {
...mapWritableState(globalStore, {
canvasWidth: "canvasWidth",
canvasHeight: "canvasHeight",
}),
},
mounted() {
this.$nextTick(() => {
window.addEventListener("resize", this.onResize);
});
},
beforeUnmount() {
window.removeEventListener("resize", this.onResize);
},
methods: {
...mapActions(globalStore, ["setBoardWidth", "setBoardHeight"]),
toggleMainMenu() {
this.mainMenu = !this.mainMenu;
},
onResize() {
this.$nextTick(() => {
this.windowWidth = window.innerWidth;
this.canvasWidth = window.innerWidth;
this.canvasHeight = window.innerHeight;
this.setBoardWidth();
this.setBoardHeight();
});
},
},
};
</script>
<style scope>
:root {
--dark1: #000000;
--dark2: #333333;
--dark3: #666666;
--light1: #999999;
--light2: #cccccc;
--light3: #eeeeee;
}
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
@ -90,20 +36,15 @@
}
body {
background: var(--dark1);
color: var(--light3);
background: black;
color: white;
font-family: Courier New;
}
canvas {
flex: auto;
background: rgb(0, 0, 0);
background: linear-gradient(
90deg,
rgba(0, 0, 0, 1) 0%,
rgba(131, 131, 131, 1) 52%,
rgba(0, 0, 0, 1) 100%
);
background: #110812;
margin-right: 10px;
}
h1,
@ -114,40 +55,12 @@
h1 {
margin: 10px auto;
font-size: larger;
text-transform: uppercase;
text-align: center;
}
#container {
display: flex;
height: calc(100vh - 100px);
overflow: hidden;
flex-direction: column;
}
#burger-toggle {
display: none;
cursor: pointer;
font-size: 1.5em;
vertical-align: middle;
color: var(--light2);
}
@media screen and (max-width: 800px) {
h1 {
font-size: medium;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
}
#burger-toggle {
display: inline;
}
#main-menu {
background: var(--dark2);
margin: 0 auto;
}
}
</style>

View File

@ -1,10 +1,12 @@
<template>
<main id="mainContent">
<canvas
id="canvas-board"
ref="canvas-board"
:width="canvasWidth"
:height="canvasHeight"
/>
</main>
</template>
<script>
import { mapActions, mapState, mapWritableState } from "pinia";
@ -30,7 +32,7 @@
computed: {
...mapState(globalStore, {
cellProperties: "cellProperties",
ruleset: "ruleset1d",
rules: "rules1d",
refreshRate: "refreshRate",
initial1dState: "initial1dState",
drawingDirection: "drawingDirection",
@ -38,8 +40,6 @@
getDraw1d: "draw1d",
getDraw2d: "draw2d",
getDraw2dLast: "draw2dLast",
boardWidth: "boardWidth",
boardHeight: "boardHeight",
}),
...mapWritableState(globalStore, {
lastBoard: "lastBoard",
@ -47,6 +47,12 @@
canvasHeight: "canvasHeight",
getReset: "reset",
}),
boardWidth: function () {
return Math.floor(this.canvasWidth / this.cellProperties.size);
},
boardHeight: function () {
return Math.floor(this.canvasHeight / this.cellProperties.size);
},
},
watch: {
getDraw1d(value) {
@ -67,15 +73,9 @@
this.ctx = this.canvas.getContext("2d");
this.canvasWidth = this.canvas.parentElement.clientWidth;
this.canvasHeight = this.canvas.parentElement.clientHeight;
this.setBoardWidth();
this.setBoardHeight();
},
methods: {
...mapActions(globalStore, [
"toggleStop",
"setBoardWidth",
"setBoardHeight",
]),
...mapActions(globalStore, ["toggleStop"]),
drawCanvas(board) {
const props = this.cellProperties;
board.map((row, y) => {
@ -101,7 +101,7 @@
const initialState = this.compute1dInitialState();
const board = createBoard(
initialState,
this.ruleset.rules,
this.rules.rules,
this.boardWidth
);
this.lastBoard = Object.freeze(board);
@ -142,8 +142,7 @@
};
</script>
<style>
#canvas-board {
flex: 1;
margin: 0 auto;
#mainContent {
min-width: 70%;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div id="main-menu">
<div id="sidebar">
<MenuGeneralOptions />
<MenuCellProperties />
<MenuElementaryCA />
@ -24,29 +24,39 @@
</script>
<style>
#main-menu {
display: flex;
flex-direction: row;
width: 100%;
flex: 1;
#sidebar {
width: 25%;
padding: 0 10px;
overflow-y: scroll;
}
/* Hide scrollbar for Chrome, Safari and Opera */
#main-menu::-webkit-scrollbar {
#sidebar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
#main-menu {
#sidebar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
@media screen and (max-width: 800px) {
#main-menu {
#container {
display: flex;
flex-direction: column;
position: absolute;
justify-content: center;
}
#mainContent {
flex: 1;
width: 100%;
}
#sidebar {
flex: 1;
padding: 0;
width: 100%;
}
}
</style>

View File

@ -1,17 +1,30 @@
<template>
<MenuRow row-title="2D Cellular Automaton">
<MenuRow row-title="2D Cellular Automata">
<div class="form-field">
<label>Start from last result</label>
<input type="button" value="start" @click="toggleDraw2dLast()" />
</div>
<div class="form-field">
<label>Start from empty board</label>
<input
type="button"
name="start2d"
value="start"
@click="toggleDraw2d()"
/>
</div>
<div class="form-field">
<label>Start from last result</label>
<input type="button" value="start" @click="toggleDraw2dLast()" />
<input
type="button"
name="stop"
class="stop"
value="stop"
@click="toggleStop()"
/>
<input
type="button"
name="reset"
class="reset"
value="reset"
@click="toggleReset()"
/>
</div>
</MenuRow>
</template>
@ -27,7 +40,12 @@
MenuRow,
},
methods: {
...mapActions(globalStore, ["toggleDraw2dLast", "toggleDraw2d"]),
...mapActions(globalStore, [
"toggleDraw2d",
"toggleDraw2dLast",
"toggleReset",
"toggleStop",
]),
},
};
</script>

View File

@ -1,5 +1,5 @@
<template>
<MenuRow row-title="Elementary Automaton">
<MenuRow row-title="Elementary Cellular Automata">
<form>
<div class="form-field">
<label>
@ -29,11 +29,11 @@
<br />
<select
name="ruleset-elementary"
:value="ruleset.name"
@input="updateRuleset"
:value="rules.name"
@input="updateRules"
>
<option
v-for="(ruleset, index) in presetRuleset"
v-for="(ruleset, index) in presetRules"
:key="'ruleset-preset-elementary-' + index"
:value="ruleset.name"
>
@ -43,10 +43,10 @@
</label>
</div>
<div class="form-field">
<a style="cursor: pointer" @click="copyRuleset">copy rules</a>
<a style="cursor: pointer" @click="copyRules">copy rules</a>
</div>
<div
v-for="(rule, name, index) in ruleset.rules"
v-for="(rule, name, index) in rules.rules"
:key="'rule-' + index"
class="form-field"
>
@ -64,13 +64,20 @@
</form>
<div class="form-field">
<input type="button" name="start" value="start" @click="toggleDraw1d()" />
<input
type="button"
name="reset"
class="reset"
value="reset"
@click="toggleReset"
/>
</div>
</MenuRow>
</template>
<script>
import { mapActions, mapWritableState } from "pinia";
import { presetRuleset, initialStates } from "../modules/preset.js";
import { presetRules, initialStates } from "./preset.js";
import { globalStore } from "../stores/index.js";
import MenuRow from "./MenuRow.vue";
export default {
@ -80,31 +87,31 @@
},
data() {
return {
presetRuleset: presetRuleset,
presetRules: presetRules,
initialStates: initialStates,
};
},
computed: {
...mapWritableState(globalStore, {
initialState: "initial1dState",
ruleset: "ruleset1d",
rules: "rules1d",
}),
rules1dFileName() {
// TODO: broken
return (
Object.keys(this.ruleset)
Object.keys(this.rules)
.map((index) => {
return this.ruleset[index];
return this.rules[index];
})
.join("_") + ".json"
);
},
},
methods: {
...mapActions(globalStore, ["toggleDraw1d"]),
copyRuleset() {
const newRuleset = JSON.stringify(this.ruleset);
navigator.clipboard.writeText(newRuleset);
...mapActions(globalStore, ["toggleDraw1d", "toggleReset"]),
copyRules() {
const rules = JSON.stringify(this.rules);
navigator.clipboard.writeText(rules);
},
isCurrentPreset(event) {
const elem = event.target;
@ -113,15 +120,15 @@
updateSingleRule(event) {
const elem = event.target;
const value = elem.checked ? 1 : 0;
this.ruleset.rules[elem.name] = value;
this.rules.rules[elem.name] = value;
},
updateRuleset(event) {
updateRules(event) {
const elem = event.target;
const name = elem.value;
const newRuleset = this.presetRuleset.find((ruleset) => {
const newRuleset = this.presetRules.find((ruleset) => {
return ruleset.name === name;
});
this.ruleset = newRuleset;
this.rules.rules = newRuleset.rules;
},
updateInitialState(event) {
const elem = event.target;

View File

@ -1,36 +0,0 @@
<template>
<div class="form-field">
<input
type="button"
name="stop"
class="stop"
value="stop"
@click="toggleStop()"
/>
<input
type="button"
name="reset"
class="reset"
value="reset"
@click="toggleReset()"
/>
</div>
</template>
<script>
import { mapActions } from "pinia";
import { globalStore } from "../stores/index.js";
export default {
name: "MenuReset",
methods: {
...mapActions(globalStore, ["toggleReset", "toggleStop"]),
},
};
</script>
<style scoped>
.form-field {
display: flex;
margin: 5px;
justify-content: flex-end;
}
</style>

View File

@ -3,7 +3,7 @@
<h2 :id="rowTitle" @click="updateActiveMenu">
{{ rowTitle }}
</h2>
<div class="menu-row-content">
<div v-if="activeMenu === rowTitle" class="menu-row-content">
<slot />
</div>
</div>
@ -18,6 +18,19 @@
default: "",
},
},
data() {
return {
activeMenu: "",
};
},
methods: {
updateActiveMenu(event) {
const elem = event.target;
const value = elem.id;
if (value == this.activeMenu) this.activeMenu = "";
else this.activeMenu = value;
},
},
};
</script>
@ -26,8 +39,7 @@
font-size: medium;
padding: 10px;
cursor: pointer;
border-bottom: 1px solid var(--dark3);
border-top: 1px solid var(--dark3);
border: 2px solid darkgrey;
margin: 0 0 10px 0;
}
@ -51,18 +63,6 @@
.menu-row {
flex: 1;
position: relative;
}
.menu-row:hover .menu-row-content {
display: block;
width: 100%;
}
.menu-row-content {
position: absolute;
background: var(--dark1);
display: none;
}
label,
@ -70,36 +70,4 @@
margin-right: 10px;
font-weight: bold;
}
@media screen and (max-width: 800px) {
.menu-row {
margin: 0 auto;
width: 100%;
}
.menu-row h2,
.form-field {
margin: 0;
}
.menu-row h2 {
border-bottom: 1px solid var(--dark3);
border-top: none;
}
.form-field {
padding: 10px;
}
.menu-row:active .menu-row-content {
display: flex;
flex-direction: column;
height: 100%;
}
.menu-row-content {
position: relative;
width: 100%;
}
}
</style>

View File

@ -1,4 +1,4 @@
const presetRuleset = [
const presetRules = [
{
name: "rule 73",
rules: {
@ -92,4 +92,4 @@ const initialStates = [
},
];
export { presetRuleset, initialStates };
export { presetRules, initialStates };

View File

@ -1,95 +0,0 @@
const presetRuleset = [
{
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,
},
},
];
const 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",
},
];
export { presetRuleset, initialStates };

View File

@ -3,7 +3,7 @@ import { defineStore } from "pinia";
export const globalStore = defineStore("globalStore", {
state: () => {
return {
ruleset1d: {
rules1d: {
name: "rule 73",
rules: {
111: 0,
@ -37,24 +37,14 @@ export const globalStore = defineStore("globalStore", {
};
},
actions: {
setBoardWidth() {
this.boardWidth = Math.floor(this.canvasWidth / this.cellProperties.size);
},
setBoardHeight() {
this.boardHeight = Math.floor(
this.canvasHeight / this.cellProperties.size
);
},
toggleDraw1d() {
this.draw1d = true;
},
toggleDraw2d() {
this.toggleStop();
this.canDraw = true;
this.draw2d = true;
},
toggleDraw2dLast() {
this.toggleStop();
this.canDraw = true;
this.draw2dLast = true;
},