Simulation speed settings for large network simulations

This commit is contained in:
2025-09-08 16:15:30 -05:00
parent 193a83b1d5
commit 63ef2b3a9b
3 changed files with 60 additions and 18 deletions
+33 -6
View File
@@ -29,6 +29,8 @@ class CompleteNodeOnEdgeEngine {
this.timer = null; this.timer = null;
this.lastTime = 0; this.lastTime = 0;
this.targetInterval = this.time_delay; this.targetInterval = this.time_delay;
this.speedMultiplier = 1;
this.timeAccumulator = 0;
this._movementInitialized = false; this._movementInitialized = false;
// Keep linksByEdges up-to-date for dynamic graph changes // Keep linksByEdges up-to-date for dynamic graph changes
@@ -87,6 +89,20 @@ class CompleteNodeOnEdgeEngine {
this.onArrival = callback; this.onArrival = callback;
} }
/**
* Adjust the simulation speed multiplier.
* A value > 1 speeds up the simulation, 0 < value < 1 slows it down.
* @param {number} multiplier
*/
setSimulationSpeed(multiplier) {
if (!isFinite(multiplier) || multiplier <= 0) {
return;
}
this.speedMultiplier = multiplier;
const minInterval = 1; // ms
this.targetInterval = Math.max(minInterval, this.time_delay / this.speedMultiplier);
}
/** /**
* Initializes the movement of the nodes on edges. * Initializes the movement of the nodes on edges.
*/ */
@@ -178,11 +194,22 @@ class CompleteNodeOnEdgeEngine {
} }
loop = (timestamp) => { loop = (timestamp) => {
const elapsed = timestamp - this.lastTime; if (this.lastTime === 0) {
if (elapsed >= this.targetInterval) {
this.lastTime = timestamp; this.lastTime = timestamp;
this.eventProcess();
} }
const delta = timestamp - this.lastTime;
this.lastTime = timestamp;
this.timeAccumulator += delta * this.speedMultiplier;
let steps = 0;
const maxStepsPerFrame = 50;
while (this.timeAccumulator >= this.time_delay && steps < maxStepsPerFrame) {
this.eventProcess();
this.timeAccumulator -= this.time_delay;
steps++;
}
this.timer = requestAnimationFrame(this.loop); this.timer = requestAnimationFrame(this.loop);
}; };
@@ -190,18 +217,18 @@ class CompleteNodeOnEdgeEngine {
* Processes the movement events and updates the nodes on edges accordingly. * Processes the movement events and updates the nodes on edges accordingly.
*/ */
eventProcess() { eventProcess() {
var ratio = this.ratio_inc; var ratio = this.ratio_inc * this.speedMultiplier;
if (ratio < 0) { if (ratio < 0) {
this.moveDot(ratio, this.currentTime); this.moveDot(ratio, this.currentTime);
this.currentTime = this.currentTime + this.time_delay * Math.sign(ratio); this.currentTime = this.currentTime + this.time_delay * Math.sign(this.ratio_inc) * this.speedMultiplier;
this.replayManager(this.currentTime, true); this.replayManager(this.currentTime, true);
this.maxTime = Math.max(this.maxTime, this.currentTime); this.maxTime = Math.max(this.maxTime, this.currentTime);
} else { } else {
this.moveDot(ratio, this.currentTime); this.moveDot(ratio, this.currentTime);
this.currentTime = this.currentTime + this.time_delay * Math.sign(ratio); this.currentTime = this.currentTime + this.time_delay * Math.sign(this.ratio_inc) * this.speedMultiplier;
this.replayManager(this.currentTime, false); this.replayManager(this.currentTime, false);
this.maxTime = Math.max(this.maxTime, this.currentTime); this.maxTime = Math.max(this.maxTime, this.currentTime);
+2
View File
@@ -39,6 +39,8 @@
<div id="engineSettingsContent"> <div id="engineSettingsContent">
<label for="updateInterval">Update Interval</label> <label for="updateInterval">Update Interval</label>
<input type="number" id="updateInterval" value="10" autocomplete="off"> <input type="number" id="updateInterval" value="10" autocomplete="off">
<label for="simSpeed">Simulation Speed (x)</label>
<input type="number" id="simSpeed" value="1" min="0.1" step="0.1" autocomplete="off">
</div> </div>
</details> </details>
</div> </div>
+16 -3
View File
@@ -461,6 +461,14 @@ document.getElementById('updateInterval').addEventListener('change', function()
UPDATE_INTERVAL = parseInt(this.value); UPDATE_INTERVAL = parseInt(this.value);
}); });
document.getElementById('simSpeed').addEventListener('change', function() {
const speed = parseFloat(this.value);
if (!isFinite(speed) || speed <= 0) {
return;
}
onEdgeEngine.setSimulationSpeed(speed);
});
document.getElementById('connectionDistance').addEventListener('change', function() { document.getElementById('connectionDistance').addEventListener('change', function() {
CONNECTION_DISTANCE = parseInt(this.value); CONNECTION_DISTANCE = parseInt(this.value);
}); });
@@ -493,12 +501,15 @@ document.getElementById('createRandomGraphConfirm').addEventListener('click', fu
physics: true, physics: true,
}, },
}); });
for (let i = 0; i < nodeCount; i++) { let nodeCounter = 0;
const interval = setInterval(() => {
const nodeId = generateRealisticLabel(); const nodeId = generateRealisticLabel();
nodes.add({id: nodeId, label: nodeId, x: Math.random() * 100, y: Math.random() * 100}); nodes.add({id: nodeId, label: nodeId, x: Math.random() * 100, y: Math.random() * 100});
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []}; nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []};
} nodeCounter++;
if (nodeCounter >= nodeCount) {
console.log('Node count reached');
clearInterval(interval);
setTimeout(() => { setTimeout(() => {
network.setOptions({ network.setOptions({
nodes: { nodes: {
@@ -507,5 +518,7 @@ document.getElementById('createRandomGraphConfirm').addEventListener('click', fu
} }
}); });
}, 2000); }, 2000);
}
}, 10);
document.getElementById('randomGraphModal').style.display = 'none'; document.getElementById('randomGraphModal').style.display = 'none';
}); });