Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions app/(core)/constants/Time.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ let timeScale = 1;
let paused = false;
let manualStepDelta = 0;
let simulationInstances = new Map(); // Mappa per tenere traccia di ogni istanza
let accumulators = new Map(); // Fixed timestep accumulators per instance
const FIXED_DT = 1 / 120; // 120Hz fixed physics timestep

/**
* Returns dt in seconds, limited to a max step for stability.
Expand All @@ -26,16 +28,25 @@ export function computeDelta(p) {
}

let lastInstanceMillis = simulationInstances.get(instanceId);
let dt = (now - lastInstanceMillis) / 1000; // seconds
let rawDt = (now - lastInstanceMillis) / 1000; // seconds

// Aggiorna lastMillis per questa istanza
simulationInstances.set(instanceId, now);

// limit burst (e.g. when tab regains focus)
const maxStep = 1 / 30; // ~33ms
if (dt > maxStep) dt = maxStep;
if (rawDt > maxStep) rawDt = maxStep;

return dt * timeScale;
rawDt *= timeScale;

// Fixed timestep accumulator — prevents energy drift from variable frame rate
const acc = (accumulators.get(instanceId) || 0) + rawDt;
if (acc < FIXED_DT) {
accumulators.set(instanceId, acc);
return 0;
}
accumulators.set(instanceId, acc - FIXED_DT);
return FIXED_DT;
}

export function setTimeScale(scale) {
Expand All @@ -47,13 +58,15 @@ export function togglePause() {
// Quando mettiamo in pausa, resettiamo i lastMillis per evitare salti al resume
if (paused) {
simulationInstances.clear();
accumulators.clear();
}
}

export function setPause(value) {
paused = value;
if (paused) {
simulationInstances.clear();
accumulators.clear();
}
}

Expand All @@ -66,6 +79,7 @@ export function stepSimulation(delta) {
export function resetTime() {
paused = false;
simulationInstances.clear(); // Pulisci tutte le istanze
accumulators.clear();
}

export function isPaused() {
Expand Down
14 changes: 12 additions & 2 deletions simulations/BouncingBall.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default function BouncingBall() {
const dragControllerRef = useRef(null);
const maxHeightRef = useRef(0);
const fallStartTimeRef = useRef(0);
const accumulatorRef = useRef(0);

// Sim info
const { simData, updateSimInfo } = useSimInfo({
Expand Down Expand Up @@ -145,8 +146,17 @@ export default function BouncingBall() {
p.draw = () => {
if (!bodyRef.current || !trailLayer) return;

const dt = computeDelta(p);
if (dt === 0) return;
const rawDt = computeDelta(p);
if (rawDt === 0) return;

// Fixed timestep accumulator — prevents energy drift from variable frame rate
const FIXED_DT = 1 / 120; // 120Hz physics steps
accumulatorRef.current += rawDt;
// Cap accumulator to prevent spiral of death
if (accumulatorRef.current > 0.1) accumulatorRef.current = 0.1;
const dt = FIXED_DT;
if (accumulatorRef.current < FIXED_DT) return;
accumulatorRef.current -= FIXED_DT;

const { size, gravity, trailEnabled, ballColor, mass, restitution } =
inputsRef.current;
Expand Down
Loading