Persist config changes in local storage
This commit is contained in:
parent
14208e17fb
commit
8ad5130466
3 changed files with 88 additions and 8 deletions
18
index.html
18
index.html
|
|
@ -45,6 +45,24 @@
|
||||||
width: 65px;
|
width: 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reset-btn {
|
||||||
|
width: calc(100% - 16px);
|
||||||
|
margin: 12px 8px;
|
||||||
|
padding: 6px;
|
||||||
|
background: #333;
|
||||||
|
color: #aaa;
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-btn:hover {
|
||||||
|
background: #444;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
#gui-container {
|
#gui-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
export default {
|
const STORAGE_KEY = "ants-simulation-config";
|
||||||
|
|
||||||
|
const defaults = {
|
||||||
worldSize: 1024,
|
worldSize: 1024,
|
||||||
antsCount: 12,
|
antsCount: 12,
|
||||||
simulationStepsPerSecond: 60,
|
simulationStepsPerSecond: 60,
|
||||||
|
|
@ -17,3 +19,41 @@ export default {
|
||||||
repellentMaxPerCell: 10,
|
repellentMaxPerCell: 10,
|
||||||
repellentThreshold: 0.01,
|
repellentThreshold: 0.01,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ConfigType = typeof defaults;
|
||||||
|
|
||||||
|
function loadSaved(): Partial<ConfigType> {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(STORAGE_KEY);
|
||||||
|
if (raw) return JSON.parse(raw);
|
||||||
|
} catch {
|
||||||
|
// corrupted data, ignore
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const saved = loadSaved();
|
||||||
|
const Config: ConfigType = { ...defaults, ...saved };
|
||||||
|
|
||||||
|
export function saveConfig(): void {
|
||||||
|
const toSave: Partial<ConfigType> = {};
|
||||||
|
for (const key of Object.keys(defaults) as (keyof ConfigType)[]) {
|
||||||
|
if (Config[key] !== defaults[key]) {
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: generic key/value copy
|
||||||
|
(toSave as any)[key] = Config[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(toSave).length > 0) {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(STORAGE_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetConfig(): void {
|
||||||
|
Object.assign(Config, defaults);
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
export { defaults };
|
||||||
|
export default Config;
|
||||||
|
|
|
||||||
36
src/GUI.ts
36
src/GUI.ts
|
|
@ -1,5 +1,5 @@
|
||||||
import GUI from "lil-gui";
|
import GUI from "lil-gui";
|
||||||
import Config from "./Config";
|
import Config, { resetConfig, saveConfig } from "./Config";
|
||||||
|
|
||||||
type EventHandler = () => void;
|
type EventHandler = () => void;
|
||||||
|
|
||||||
|
|
@ -16,33 +16,50 @@ class GUIController {
|
||||||
.add(Config, "worldSize", 256, 4096)
|
.add(Config, "worldSize", 256, 4096)
|
||||||
.name("World size")
|
.name("World size")
|
||||||
.step(1)
|
.step(1)
|
||||||
.onChange(() => this.emit("reset"));
|
.onChange(() => this.saveAndEmit("reset"));
|
||||||
simFolder
|
simFolder
|
||||||
.add(Config, "antsCount", 0, 22)
|
.add(Config, "antsCount", 0, 22)
|
||||||
.name("Ants count 2^")
|
.name("Ants count 2^")
|
||||||
.step(1)
|
.step(1)
|
||||||
.onChange(() => this.emit("reset"));
|
.onChange(() => this.saveAndEmit("reset"));
|
||||||
simFolder
|
simFolder
|
||||||
.add(Config, "scentFadeOutFactor", 0, 0.01)
|
.add(Config, "scentFadeOutFactor", 0, 0.01)
|
||||||
.name("Pheromone evaporation factor")
|
.name("Pheromone evaporation factor")
|
||||||
.step(0.0001)
|
.step(0.0001)
|
||||||
.onChange(() => this.emit("reset"));
|
.onChange(() => this.saveAndEmit("reset"));
|
||||||
simFolder
|
simFolder
|
||||||
.add(Config, "scentBlurRadius", 0, 0.5)
|
.add(Config, "scentBlurRadius", 0, 0.5)
|
||||||
.name("Pheromone diffusion factor")
|
.name("Pheromone diffusion factor")
|
||||||
.step(0.01)
|
.step(0.01)
|
||||||
.onChange(() => this.emit("reset"));
|
.onChange(() => this.saveAndEmit("reset"));
|
||||||
simFolder
|
simFolder
|
||||||
.add(Config, "simulationStepsPerSecond", 1, 500)
|
.add(Config, "simulationStepsPerSecond", 1, 500)
|
||||||
.name("Simulation steps per second")
|
.name("Simulation steps per second")
|
||||||
.step(1);
|
.step(1)
|
||||||
|
.onChange(() => saveConfig());
|
||||||
|
|
||||||
const controlsFolder = this.gui.addFolder("Controls");
|
const controlsFolder = this.gui.addFolder("Controls");
|
||||||
|
|
||||||
controlsFolder.add(Config, "brushRadius", 1, 100).name("Brush radius");
|
controlsFolder
|
||||||
|
.add(Config, "brushRadius", 1, 100)
|
||||||
|
.name("Brush radius")
|
||||||
|
.onChange(() => saveConfig());
|
||||||
|
|
||||||
simFolder.open();
|
simFolder.open();
|
||||||
controlsFolder.open();
|
controlsFolder.open();
|
||||||
|
|
||||||
|
const resetBtn = document.createElement("button");
|
||||||
|
resetBtn.textContent = "Reset to defaults";
|
||||||
|
resetBtn.className = "reset-btn";
|
||||||
|
resetBtn.addEventListener("click", () => {
|
||||||
|
resetConfig();
|
||||||
|
for (const c of this.gui.controllersRecursive()) {
|
||||||
|
c.updateDisplay();
|
||||||
|
}
|
||||||
|
this.emit("reset");
|
||||||
|
});
|
||||||
|
// biome-ignore lint/style/noNonNullAssertion: gui-container exists in index.html
|
||||||
|
document.getElementById("gui-container")!.appendChild(resetBtn);
|
||||||
}
|
}
|
||||||
|
|
||||||
on(event: string, handler: EventHandler): void {
|
on(event: string, handler: EventHandler): void {
|
||||||
|
|
@ -53,6 +70,11 @@ class GUIController {
|
||||||
this.listeners.get(event)!.push(handler);
|
this.listeners.get(event)!.push(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private saveAndEmit(event: string): void {
|
||||||
|
saveConfig();
|
||||||
|
this.emit(event);
|
||||||
|
}
|
||||||
|
|
||||||
private emit(event: string): void {
|
private emit(event: string): void {
|
||||||
const handlers = this.listeners.get(event);
|
const handlers = this.listeners.get(event);
|
||||||
if (handlers) {
|
if (handlers) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue