Files
logic-gates/js/simulation.js
Jose Luis f0f3516efa fix: use requestAnimationFrame + real timestamps for clock simulation
Replace setInterval with requestAnimationFrame loop that tracks elapsed
time via performance.now(). Clock ticks now fire based on real time
rather than assuming perfect interval spacing, so user interactions
(toggling inputs, dragging gates) no longer cause the clock to stutter
or pause.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 03:16:26 +01:00

76 lines
2.1 KiB
JavaScript

// Clock simulation engine — uses real timestamps for consistent timing
import { state } from './state.js';
import { evaluateAll } from './gates.js';
import { forceRecordSample } from './waveform.js';
let lastTickTime = 0;
let animFrameId = null;
function simLoop(now) {
if (!state.simRunning) return;
// Fire ticks that are due based on real elapsed time
while (now - lastTickTime >= state.simSpeed) {
lastTickTime += state.simSpeed;
// Toggle all CLOCK gates
state.gates.forEach(g => {
if (g.type === 'CLOCK') {
g.value = g.value ? 0 : 1;
}
});
evaluateAll();
if (state.recording && state.waveformVisible) {
forceRecordSample();
}
}
animFrameId = requestAnimationFrame(simLoop);
}
export function startSim() {
if (state.simRunning) return;
const hasClocks = state.gates.some(g => g.type === 'CLOCK');
if (!hasClocks) return;
state.simRunning = true;
// Auto-open waveform panel
if (!state.waveformVisible) {
state.waveformVisible = true;
document.getElementById('waveform-panel').classList.add('visible');
document.getElementById('sim-btn').classList.add('active');
window.dispatchEvent(new Event('resize'));
}
lastTickTime = performance.now();
animFrameId = requestAnimationFrame(simLoop);
updateSimUI();
}
export function stopSim() {
state.simRunning = false;
if (animFrameId) cancelAnimationFrame(animFrameId);
animFrameId = null;
updateSimUI();
}
export function updateSimUI() {
const btn = document.getElementById('sim-run-btn');
if (state.simRunning) {
btn.textContent = '⏹ Stop';
btn.classList.add('active');
} else {
btn.textContent = '▶ Run';
btn.classList.remove('active');
}
document.getElementById('sim-speed-label').textContent = `${state.simSpeed}ms`;
}
export function adjustSpeed(delta) {
const wasRunning = state.simRunning;
state.simSpeed = Math.max(50, Math.min(2000, state.simSpeed + delta));
// No need to restart — the loop reads simSpeed dynamically
updateSimUI();
}