Merge branch 'dev'
CI/CD / deploy (push) Successful in 2m5s
Details
CI/CD / deploy (push) Successful in 2m5s
Details
This commit is contained in:
commit
f1f9e3b34d
|
|
@ -382,7 +382,10 @@ class FolkFlowRiver extends HTMLElement {
|
||||||
private shadow: ShadowRoot;
|
private shadow: ShadowRoot;
|
||||||
private nodes: FlowNode[] = [];
|
private nodes: FlowNode[] = [];
|
||||||
private simulating = false;
|
private simulating = false;
|
||||||
|
private looping = false;
|
||||||
private simTimer: ReturnType<typeof setInterval> | null = null;
|
private simTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
|
private initialNodes: FlowNode[] | null = null;
|
||||||
|
private steadyCount = 0;
|
||||||
private dragging = false;
|
private dragging = false;
|
||||||
private dragStartX = 0;
|
private dragStartX = 0;
|
||||||
private dragStartY = 0;
|
private dragStartY = 0;
|
||||||
|
|
@ -430,14 +433,51 @@ class FolkFlowRiver extends HTMLElement {
|
||||||
|
|
||||||
private startSimulation() {
|
private startSimulation() {
|
||||||
if (this.simTimer) return;
|
if (this.simTimer) return;
|
||||||
|
// Snapshot initial state for loop reset
|
||||||
|
if (!this.initialNodes) {
|
||||||
|
this.initialNodes = this.nodes.map(n => ({ ...n, data: { ...n.data } }));
|
||||||
|
}
|
||||||
|
this.steadyCount = 0;
|
||||||
this.simTimer = setInterval(() => {
|
this.simTimer = setInterval(() => {
|
||||||
|
const prev = this.nodes;
|
||||||
this.nodes = simulateTick(this.nodes, DEFAULT_CONFIG);
|
this.nodes = simulateTick(this.nodes, DEFAULT_CONFIG);
|
||||||
|
|
||||||
|
// Detect steady state: check if values stopped changing
|
||||||
|
let changed = false;
|
||||||
|
for (let i = 0; i < this.nodes.length; i++) {
|
||||||
|
const a = prev[i].data as Record<string, unknown>;
|
||||||
|
const b = this.nodes[i].data as Record<string, unknown>;
|
||||||
|
if (a.currentValue !== b.currentValue || a.fundingReceived !== b.fundingReceived) {
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!changed) this.steadyCount++;
|
||||||
|
else this.steadyCount = 0;
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
|
// If looping and steady for 3 ticks, reset after a brief pause
|
||||||
|
if (this.looping && this.steadyCount >= 3 && this.initialNodes) {
|
||||||
|
this.stopSimulation();
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.looping) return;
|
||||||
|
this.nodes = this.initialNodes!.map(n => ({ ...n, data: { ...n.data } }));
|
||||||
|
this.render();
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.looping) return;
|
||||||
|
this.simulating = true;
|
||||||
|
this.startSimulation();
|
||||||
|
this.render();
|
||||||
|
}, 500);
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
private stopSimulation() {
|
private stopSimulation() {
|
||||||
if (this.simTimer) { clearInterval(this.simTimer); this.simTimer = null; }
|
if (this.simTimer) { clearInterval(this.simTimer); this.simTimer = null; }
|
||||||
|
this.simulating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private showAmountPopover(sourceId: string, anchorX: number, anchorY: number) {
|
private showAmountPopover(sourceId: string, anchorX: number, anchorY: number) {
|
||||||
|
|
@ -535,6 +575,7 @@ class FolkFlowRiver extends HTMLElement {
|
||||||
</svg>
|
</svg>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button class="${this.simulating ? "active" : ""}" data-action="toggle-sim">${this.simulating ? "Pause" : "Simulate"}</button>
|
<button class="${this.simulating ? "active" : ""}" data-action="toggle-sim">${this.simulating ? "Pause" : "Simulate"}</button>
|
||||||
|
<button class="${this.looping ? "active" : ""}" data-action="toggle-loop" title="Loop simulation">🔁</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="legend">
|
<div class="legend">
|
||||||
<div class="legend-item"><div class="legend-dot" style="background:${COLORS.inflow}"></div> Inflow</div>
|
<div class="legend-item"><div class="legend-dot" style="background:${COLORS.inflow}"></div> Inflow</div>
|
||||||
|
|
@ -551,6 +592,19 @@ class FolkFlowRiver extends HTMLElement {
|
||||||
this.render();
|
this.render();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Event: toggle loop
|
||||||
|
this.shadow.querySelector("[data-action=toggle-loop]")?.addEventListener("click", () => {
|
||||||
|
this.looping = !this.looping;
|
||||||
|
if (this.looping && !this.simulating) {
|
||||||
|
// Auto-start simulation when enabling loop
|
||||||
|
this.initialNodes = null; // fresh snapshot
|
||||||
|
this.simulating = true;
|
||||||
|
this.startSimulation();
|
||||||
|
}
|
||||||
|
if (!this.looping) this.initialNodes = null;
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
|
||||||
// Event delegation for interactive elements + drag-to-pan
|
// Event delegation for interactive elements + drag-to-pan
|
||||||
const container = this.shadow.querySelector(".container") as HTMLElement;
|
const container = this.shadow.querySelector(".container") as HTMLElement;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue