bunch of QoL stuff and full packet forwarding/visualization (this is so cool!)

This commit is contained in:
2025-09-08 14:10:45 -05:00
parent f5e5233515
commit 6a7651a96f
3 changed files with 210 additions and 11 deletions
+25 -1
View File
@@ -12,7 +12,26 @@
<body> <body>
<div id="rowFlex" style="display: flex; flex-direction: row; align-items: start; justify-content: center; width: 100%; height: 100%;"> <div id="rowFlex" style="display: flex; flex-direction: row; align-items: start; justify-content: center; width: 100%; height: 100%;">
<div id="propertyViewer"> <div id="propertyViewer">
<div id="propertyViewerHeader">Simulation Properties</div>
<hr/>
<details id="simpleSettings">
<summary>Simple Settings</summary>
<div id="simpleSettingsContent">
<label for="ttl">TTL</label>
<input type="number" id="ttl" value="5" autocomplete="off">
<label for="dropPenalty">Drop Penalty</label>
<input type="number" id="dropPenalty" value="1.2" autocomplete="off">
<label for="connectionDistance">Connection Distance</label>
<input type="number" id="connectionDistance" value="250" autocomplete="off">
</div>
</details>
<details id="engineSettings">
<summary>Engine Settings</summary>
<div id="engineSettingsContent">
<label for="updateInterval">Update Interval</label>
<input type="number" id="updateInterval" value="10" autocomplete="off">
</div>
</details>
</div> </div>
<div id="centerColumn" style="display: flex; flex-direction: column; align-items: center; justify-content: center; width: 70%; height: 100%; margin: 0 auto;"> <div id="centerColumn" style="display: flex; flex-direction: column; align-items: center; justify-content: center; width: 70%; height: 100%; margin: 0 auto;">
<div id="simContainer"></div> <div id="simContainer"></div>
@@ -20,10 +39,15 @@
<div id="controlButtons"> <div id="controlButtons">
<button id="addNode" class="controlButton">Add Node</button> <button id="addNode" class="controlButton">Add Node</button>
<button id="sendPacket" class="controlButton">Send Packet</button> <button id="sendPacket" class="controlButton">Send Packet</button>
<button id="resetTTLColors" class="controlButton">Reset TTL Colors</button>
</div> </div>
<div id="simToggles"> <div id="simToggles">
<input type="checkbox" id="showRanges" class="simToggle"> <input type="checkbox" id="showRanges" class="simToggle">
<label for="showRanges">Show Ranges</label> <label for="showRanges">Show Ranges</label>
<input type="checkbox" id="showTTL" class="simToggle" checked>
<label for="showTTL">Show TTL Colors</label>
<input type="checkbox" id="quickPlace" class="simToggle">
<label for="quickPlace">Quick Place</label>
</div> </div>
</div> </div>
</div> </div>
+161 -10
View File
@@ -1,6 +1,52 @@
var UPDATE_INTERVAL = 10; var UPDATE_INTERVAL = 10;
var CONNECTION_DISTANCE = 250; var CONNECTION_DISTANCE = 250;
var DROP_PENTALTY = 1.2; var DROP_PENALTY = 1.2;
var TTL = 5;
function smoothColorTransition(color1, color2, min, max, current) {
// Clamp current between min and max
const clamped = Math.min(Math.max(current, min), max);
// Convert hex to RGB
const hexToRgb = (hex) => {
hex = hex.replace(/^#/, "");
if (hex.length === 3) {
hex = hex
.split("")
.map((c) => c + c)
.join("");
}
const num = parseInt(hex, 16);
return {
r: (num >> 16) & 255,
g: (num >> 8) & 255,
b: num & 255,
};
};
// Convert RGB to hex
const rgbToHex = (r, g, b) =>
"#" +
[r, g, b]
.map((x) => {
const hex = x.toString(16);
return hex.length === 1 ? "0" + hex : hex;
})
.join("");
const c1 = hexToRgb(color1);
const c2 = hexToRgb(color2);
// Normalize factor (0 → min, 1 → max)
const t = (clamped - min) / (max - min);
// Interpolate each channel
const r = Math.round(c1.r + (c2.r - c1.r) * t);
const g = Math.round(c1.g + (c2.g - c1.g) * t);
const b = Math.round(c1.b + (c2.b - c1.b) * t);
return rgbToHex(r, g, b);
}
var nodes = new vis.DataSet([ var nodes = new vis.DataSet([
]); ]);
@@ -51,11 +97,65 @@ var onEdgeEngine = new CompleteNodeOnEdgeEngine(network, nodes, dotNodes, edges,
onEdgeEngine.createEdgesTable(); onEdgeEngine.createEdgesTable();
onEdgeEngine.initMovement(); onEdgeEngine.initMovement();
onEdgeEngine.setArrivalCallback(({ from, to, dot }) => { onEdgeEngine.setArrivalCallback(({ from, to, dot }) => {
// Example: log arrival; dot removal is handled by engine if (nodeTable[to].packetCache.includes(dot.id.split('-')[0])) {
// For flood behavior, you can spawn new dots here if needed return;
console.log(`Dot ${dot.id} arrived from ${from} to ${to}`); }
// update color of receiving node for visualization
nodeTable[to].packetCache.push(dot.id.split('-')[0]);
var packetId = dot.id.split('-')[0];
var ttl = parseInt(dot.id.split('-')[2]);
ttl--;
if (document.getElementById('showTTL').checked) {
nodes.update({id: to, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}});
} else {
nodes.update({id: to, color: {background: '#97c2fc'}});
}
if (ttl > 0) {
const neighborsSnapshot = nodeTable[to].outgoing.slice();
for (let neighbor of neighborsSnapshot) {
if (neighbor === from) {
continue;
}
console.log(`Sending packet ${packetId} from ${to} to ${neighbor}`);
var packetShift = generateRealisticLabel();
var movingNode = {
id: `${packetId}-${packetShift}-${ttl}`,
label: packetId + `-${ttl}`,
shape: dot.shape,
size: dot.size,
color: dot.color,
};
onEdgeEngine.createDotNode(movingNode, to, neighbor);
}
} else {
if (document.getElementById('showTTL').checked) {
markNeighborsAsFailed(to, from, packetId);
}
}
}); });
const markNeighborsAsFailed = (nodeId, from, packetId, visited = new Set()) => {
if (visited.has(nodeId)) {
return;
}
visited.add(nodeId);
const neighborsSnapshot = (nodeTable[nodeId]?.outgoing?.slice()) || [];
for (let neighbor of neighborsSnapshot) {
if (visited.has(neighbor)) {
continue;
}
if (nodeTable[neighbor]?.packetCache?.includes(packetId)) {
continue;
}
if (neighbor === from) {
continue;
}
nodes.update({id: neighbor, color: {background: 'white'}});
markNeighborsAsFailed(neighbor, nodeId, packetId, visited);
}
}
function nodeSelectUpdateHandler(properties) { function nodeSelectUpdateHandler(properties) {
// get containers // get containers
const incomingTable = document.getElementById('incomingConnections'); const incomingTable = document.getElementById('incomingConnections');
@@ -259,7 +359,7 @@ function main() {
if (nodeTable[existingNode].incoming.length == 0) { if (nodeTable[existingNode].incoming.length == 0) {
existingScore += 10000; existingScore += 10000;
} }
if (existingScore * DROP_PENTALTY < score.score) { if (existingScore * DROP_PENALTY < score.score) {
//console.log(`Dropping ${existingNode} from ${node1} for better ${score.node}`); //console.log(`Dropping ${existingNode} from ${node1} for better ${score.node}`);
const outIdx = nodeTable[node1].outgoing.indexOf(existingNode); const outIdx = nodeTable[node1].outgoing.indexOf(existingNode);
if (outIdx !== -1) { if (outIdx !== -1) {
@@ -297,17 +397,45 @@ main();
document.getElementById('addNode').addEventListener('click', function() { document.getElementById('addNode').addEventListener('click', function() {
const nodeId = generateRealisticLabel(); const nodeId = generateRealisticLabel();
nodes.add({id: nodeId, label: nodeId}); nodes.add({id: nodeId, label: nodeId});
nodeTable[nodeId] = {'incoming': [], 'outgoing': []}; nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []};
});
network.on('click', function(properties) {
if (properties.nodes.length > 0) {
return;
}
if (document.getElementById('quickPlace').checked) {
const nodeId = generateRealisticLabel();
nodes.add({id: nodeId, label: nodeId, x: properties.pointer.canvas.x, y: properties.pointer.canvas.y});
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []};
}
});
network.on('doubleClick', function(properties) {
if (document.getElementById('quickPlace').checked) {
return;
}
if (properties.nodes.length > 0) {
return;
}
const nodeId = generateRealisticLabel();
nodes.add({id: nodeId, label: nodeId, x: properties.pointer.canvas.x, y: properties.pointer.canvas.y});
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []};
}); });
document.getElementById('sendPacket').addEventListener('click', function() { document.getElementById('sendPacket').addEventListener('click', function() {
if (selectedNode == null) {
return;
}
onEdgeEngine.createEdgesTable(); onEdgeEngine.createEdgesTable();
const packetId = generateRealisticLabel(); const packetId = generateRealisticLabel();
let packetShift = 0; var packetShift = generateRealisticLabel();
let ttl = TTL;
nodes.update({id: selectedNode, color: {background: '#97c2fc'}});
for (let connection of nodeTable[selectedNode].outgoing) { for (let connection of nodeTable[selectedNode].outgoing) {
var movingNode = { var movingNode = {
id: `${packetId}-${packetShift}`, id: `${packetId}-${packetShift}-${ttl}`,
label: packetId, label: packetId + `-${ttl}`,
shape: 'dot', shape: 'dot',
size: 6, size: 6,
color: { color: {
@@ -316,6 +444,29 @@ document.getElementById('sendPacket').addEventListener('click', function() {
}, },
}; };
onEdgeEngine.createDotNode(movingNode, selectedNode, connection); onEdgeEngine.createDotNode(movingNode, selectedNode, connection);
packetShift++; nodeTable[selectedNode].packetCache.push(packetId);
packetShift = generateRealisticLabel();
} }
}); });
document.getElementById('resetTTLColors').addEventListener('click', function() {
for (let node of nodes.get()) {
nodes.update({id: node.id, color: {background: '#97c2fc'}});
}
});
document.getElementById('updateInterval').addEventListener('change', function() {
UPDATE_INTERVAL = parseInt(this.value);
});
document.getElementById('connectionDistance').addEventListener('change', function() {
CONNECTION_DISTANCE = parseInt(this.value);
});
document.getElementById('dropPenalty').addEventListener('change', function() {
DROP_PENALTY = parseFloat(this.value);
});
document.getElementById('ttl').addEventListener('change', function() {
TTL = parseInt(this.value);
});
+24
View File
@@ -67,3 +67,27 @@ html, body {
flex-shrink: 1; flex-shrink: 1;
gap: 10px; gap: 10px;
} }
#simpleSettingsContent {
display: flex;
flex-direction: column;
gap: 10px;
padding-left: 20px;
padding-top: 10px;
}
#engineSettingsContent {
display: flex;
flex-direction: column;
gap: 10px;
padding-left: 20px;
padding-top: 10px;
}
#simpleSettings {
margin-bottom: 10px;
}
#engineSettings {
margin-bottom: 10px;
}