diff --git a/index.html b/index.html index 277deeb..4a4b08c 100644 --- a/index.html +++ b/index.html @@ -12,7 +12,26 @@
- +
Simulation Properties
+
+
+ Simple Settings +
+ + + + + + +
+
+
+ Engine Settings +
+ + +
+
@@ -20,10 +39,15 @@
+
+ + + +
diff --git a/script.js b/script.js index fe79e1b..97627b8 100644 --- a/script.js +++ b/script.js @@ -1,6 +1,52 @@ var UPDATE_INTERVAL = 10; 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([ ]); @@ -51,11 +97,65 @@ var onEdgeEngine = new CompleteNodeOnEdgeEngine(network, nodes, dotNodes, edges, onEdgeEngine.createEdgesTable(); onEdgeEngine.initMovement(); onEdgeEngine.setArrivalCallback(({ from, to, dot }) => { - // Example: log arrival; dot removal is handled by engine - // For flood behavior, you can spawn new dots here if needed - console.log(`Dot ${dot.id} arrived from ${from} to ${to}`); + if (nodeTable[to].packetCache.includes(dot.id.split('-')[0])) { + return; + } + // 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) { // get containers const incomingTable = document.getElementById('incomingConnections'); @@ -259,7 +359,7 @@ function main() { if (nodeTable[existingNode].incoming.length == 0) { existingScore += 10000; } - if (existingScore * DROP_PENTALTY < score.score) { + if (existingScore * DROP_PENALTY < score.score) { //console.log(`Dropping ${existingNode} from ${node1} for better ${score.node}`); const outIdx = nodeTable[node1].outgoing.indexOf(existingNode); if (outIdx !== -1) { @@ -297,17 +397,45 @@ main(); document.getElementById('addNode').addEventListener('click', function() { const nodeId = generateRealisticLabel(); 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() { + if (selectedNode == null) { + return; + } onEdgeEngine.createEdgesTable(); 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) { var movingNode = { - id: `${packetId}-${packetShift}`, - label: packetId, + id: `${packetId}-${packetShift}-${ttl}`, + label: packetId + `-${ttl}`, shape: 'dot', size: 6, color: { @@ -316,6 +444,29 @@ document.getElementById('sendPacket').addEventListener('click', function() { }, }; 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); }); \ No newline at end of file diff --git a/style.css b/style.css index 7906bd7..007264d 100644 --- a/style.css +++ b/style.css @@ -66,4 +66,28 @@ html, body { flex-direction: row; flex-shrink: 1; 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; } \ No newline at end of file