diff --git a/index.html b/index.html index 811f2e2..3cc059c 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,13 @@
- +
+ +
+
+ + +
diff --git a/script.js b/script.js index 4ebebce..cc3f861 100644 --- a/script.js +++ b/script.js @@ -7,6 +7,8 @@ var nodes = new vis.DataSet([ var edges = new vis.DataSet([ ]); +var nodeTable = {}; + var container = document.getElementById('simContainer'); var data = { @@ -37,32 +39,138 @@ var options = { }; var network = new vis.Network(container, data, options); +let selectedNode = null; + +network.on('selectNode', function(properties) { + if (document.getElementById('showRanges').checked) { + selectedNode = properties.nodes[0]; + const centerPoint = network.getPosition(properties.nodes[0]); + const centerPointDOM = network.canvasToDOM({x: centerPoint.x, y: centerPoint.y}); + const edgePointDOM = network.canvasToDOM({x: centerPoint.x + CONNECTION_DISTANCE, y: centerPoint.y}); + const radius = Math.abs(edgePointDOM.x - centerPointDOM.x); + const rangeVis = document.createElement('div'); + rangeVis.style.position = 'absolute'; + rangeVis.style.top = (centerPointDOM.y - radius) + 'px'; + rangeVis.style.left = (centerPointDOM.x - radius) + 'px'; + rangeVis.style.width = radius * 2 + 'px'; + rangeVis.style.height = radius * 2 + 'px'; + rangeVis.classList.add('rangeVis'); + document.getElementById('simContainer').appendChild(rangeVis); + } +}); + +network.on('dragging', function(properties) { + if (document.getElementById('showRanges').checked) { + if (selectedNode != null) { + if (properties.nodes.length > 0) { + selectedNode = properties.nodes[0]; + } + const centerPoint = network.getPosition(selectedNode); + const centerPointDOM = network.canvasToDOM({x: centerPoint.x, y: centerPoint.y}); + const edgePointDOM = network.canvasToDOM({x: centerPoint.x + CONNECTION_DISTANCE, y: centerPoint.y}); + const radius = Math.abs(edgePointDOM.x - centerPointDOM.x); + const rangeVis = document.querySelector('.rangeVis'); + if (rangeVis) { + rangeVis.style.top = (centerPointDOM.y - radius) + 'px'; + rangeVis.style.left = (centerPointDOM.x - radius) + 'px'; + rangeVis.style.width = radius * 2 + 'px'; + rangeVis.style.height = radius * 2 + 'px'; + } else { + const rangeVis = document.createElement('div'); + rangeVis.style.position = 'absolute'; + rangeVis.style.top = (centerPointDOM.y - radius) + 'px'; + rangeVis.style.left = (centerPointDOM.x - radius) + 'px'; + rangeVis.style.width = radius * 2 + 'px'; + rangeVis.style.height = radius * 2 + 'px'; + rangeVis.classList.add('rangeVis'); + document.getElementById('simContainer').appendChild(rangeVis); + } + } else if (properties.nodes.length > 0) { + selectedNode = properties.nodes[0]; + const centerPoint = network.getPosition(selectedNode); + const centerPointDOM = network.canvasToDOM({x: centerPoint.x, y: centerPoint.y}); + const edgePointDOM = network.canvasToDOM({x: centerPoint.x + CONNECTION_DISTANCE, y: centerPoint.y}); + const radius = Math.abs(edgePointDOM.x - centerPointDOM.x); + const rangeVis = document.querySelector('.rangeVis'); + if (rangeVis) { + rangeVis.style.top = (centerPointDOM.y - radius) + 'px'; + rangeVis.style.left = (centerPointDOM.x - radius) + 'px'; + rangeVis.style.width = radius * 2 + 'px'; + rangeVis.style.height = radius * 2 + 'px'; + } + } + } +}); + +network.on('zoom', function(properties) { + if (document.getElementById('showRanges').checked) { + if (selectedNode) { + const centerPoint = network.getPosition(selectedNode); + const centerPointDOM = network.canvasToDOM({x: centerPoint.x, y: centerPoint.y}); + const edgePointDOM = network.canvasToDOM({x: centerPoint.x + CONNECTION_DISTANCE, y: centerPoint.y}); + const radius = Math.abs(edgePointDOM.x - centerPointDOM.x); + const rangeVis = document.querySelector('.rangeVis'); + if (rangeVis) { + rangeVis.style.top = (centerPointDOM.y - radius) + 'px'; + rangeVis.style.left = (centerPointDOM.x - radius) + 'px'; + rangeVis.style.width = radius * 2 + 'px'; + rangeVis.style.height = radius * 2 + 'px'; + } + } + } +}); + +network.on('deselectNode', function(properties) { + selectedNode = null; + if (document.getElementById('showRanges').checked) { + const rangeVis = document.querySelector('.rangeVis'); + if (rangeVis) { + rangeVis.remove(); + } + } +}); // main event loop function main() { currentNodes = network.getPositions(); currentNodesKeys = Object.keys(currentNodes); for (let node1 of currentNodesKeys) { + neighbors = []; for (let node2 of currentNodesKeys) { if (node1 !== node2) { const node1data = currentNodes[node1]; const node2data = currentNodes[node2]; const distance = calculateDistance(node1data, node2data); - const edgeId = `${node1}->${node2}`; if (distance < CONNECTION_DISTANCE) { - if (!edges.get(edgeId)) { - edges.add({ - id: edgeId, - from: node1, - to: node2, - smooth: { enabled: true, type: 'curvedCW', roundness: 0.1 } - }); - } - } else if (distance >= CONNECTION_DISTANCE) { - edges.remove(edgeId); + neighbors.push({'node': node2, 'distance': distanceToRSSI(distance), }); + } else { + edges.remove(`${node1}->${node2}`); + nodeTable[node1].connections.splice(nodeTable[node1].connections.indexOf(node2), 1); } } } + const scores = []; + for (let neighbor of neighbors) { + let connectionScore = 0; + connectionScore += (100 + neighbor.distance); + connectionScore += 100/nodeTable[neighbor.node].connections.length; + if (nodeTable[neighbor.node].connections.length == 0) { + connectionScore += 100; + } + if (nodeTable[neighbor.node].connections.length > 3) { + continue; + } + scores.push({'node': neighbor.node, 'score': connectionScore}); + } + scores.sort((a, b) => b.score - a.score); + top3 = scores.slice(0, 3); + for (let score of top3) { + if (nodeTable[node1].connections.includes(score.node)) { + continue; + } + nodeTable[node1].connections.push(score.node); + connectNodes(node1, score.node); + } } setTimeout(main, UPDATE_INTERVAL); } @@ -71,4 +179,5 @@ main(); document.getElementById('addNode').addEventListener('click', function() { const nodeId = generateRealisticLabel(); nodes.add({id: nodeId, label: nodeId}); + nodeTable[nodeId] = {'connections': []}; }); \ No newline at end of file diff --git a/simhelper.js b/simhelper.js index ebece14..a394606 100644 --- a/simhelper.js +++ b/simhelper.js @@ -4,4 +4,21 @@ function generateRealisticLabel() { function calculateDistance(node1, node2) { return Math.sqrt((node1.x - node2.x) ** 2 + (node1.y - node2.y) ** 2); +} + +function connectNodes(node1, node2) { + if (edges.get(`${node1}->${node2}`)) { + return; + } + const edgeId = `${node1}->${node2}`; + edges.add({ + id: edgeId, + from: node1, + to: node2, + smooth: { enabled: true, type: 'curvedCW', roundness: 0.1 } + }); +} + +function distanceToRSSI(distance) { + return -10 - 26 * Math.log10(distance); } \ No newline at end of file diff --git a/style.css b/style.css index 5ce6e98..bf984eb 100644 --- a/style.css +++ b/style.css @@ -3,11 +3,10 @@ height: 90%; margin: 0 auto; border: 1px solid #000; - padding: 10px; box-sizing: border-box; - display: flex; - justify-content: center; - align-items: center; + position: relative; + padding: 0; + overflow: hidden; } #controlContainer { @@ -15,9 +14,11 @@ height: 10%; margin: 0 auto; border: 1px solid #000; - padding: 10px; + padding: 5px; box-sizing: border-box; display: flex; + gap: 5px; + flex-direction: column; justify-content: center; align-items: center; } @@ -33,4 +34,28 @@ html, body { margin: 0; padding: 0; box-sizing: border-box; +} + +.rangeVis { + position: absolute; + border: 1px solid black; + border-radius: 50%; + z-index: 1000; + opacity: 0.2; + background-color: red; + pointer-events: none; +} + +#controlButtons { + display: flex; + flex-direction: row; + flex-grow: 1; + gap: 10px; +} + +#simToggles { + display: flex; + flex-direction: row; + flex-shrink: 1; + gap: 10px; } \ No newline at end of file