sendPacket rendering optimizations

This commit is contained in:
2025-11-13 11:50:35 -06:00
parent d2292dcde7
commit e00ddf2b64
2 changed files with 75 additions and 18 deletions
+8 -1
View File
@@ -33,6 +33,7 @@ class CompleteNodeOnEdgeEngine {
this.speedMultiplier = 1; this.speedMultiplier = 1;
this.timeAccumulator = 0; this.timeAccumulator = 0;
this._movementInitialized = false; this._movementInitialized = false;
this._isDragging = false;
// Keep linksByEdges up-to-date for dynamic graph changes // Keep linksByEdges up-to-date for dynamic graph changes
if (this.nodes && this.nodes.on) { if (this.nodes && this.nodes.on) {
@@ -127,6 +128,7 @@ class CompleteNodeOnEdgeEngine {
}); });
this.network.on('dragging', (params) => { this.network.on('dragging', (params) => {
this._isDragging = true;
if (params.edges.length <= 0) { if (params.edges.length <= 0) {
return; return;
} }
@@ -139,6 +141,10 @@ class CompleteNodeOnEdgeEngine {
}); });
}); });
this.network.on('dragEnd', (params) => {
this._isDragging = false;
});
this.network.on('physicsMoving', (params) => { this.network.on('physicsMoving', (params) => {
this.edgesMoved = true; this.edgesMoved = true;
// Only clear cache for edges that have moving dots // Only clear cache for edges that have moving dots
@@ -284,7 +290,8 @@ class CompleteNodeOnEdgeEngine {
}); });
// Request canvas redraw without forcing full network re-render // Request canvas redraw without forcing full network re-render
if (this.movingDots.size > 0) { // Skip during dragging as vis-network handles redraws automatically
if (this.movingDots.size > 0 && !this._isDragging) {
this.network.canvas.body.emitter.emit("_requestRedraw"); this.network.canvas.body.emitter.emit("_requestRedraw");
} }
} }
+67 -17
View File
@@ -17,6 +17,55 @@ var markNeighborRafs = new Set();
var nodeUpdateCache = []; var nodeUpdateCache = [];
var edgeUpdateCache = []; var edgeUpdateCache = [];
// Debounced batched updater for nodes/edges to reduce render thrash
var debouncedVis = (function () {
var nodeMap = new Map();
var edgeMap = new Map();
var timer = null;
var debounceMs = 50; // flush after 50ms of inactivity
function flush() {
timer = null;
if (nodeMap.size) {
nodes.update(Array.from(nodeMap.values()));
nodeMap.clear();
}
if (edgeMap.size) {
edges.update(Array.from(edgeMap.values()));
edgeMap.clear();
}
}
function schedule() {
if (timer) clearTimeout(timer);
timer = setTimeout(flush, debounceMs);
}
function enqueueNode(update) {
// keep only the latest update per id
var existing = nodeMap.get(update.id) || {};
nodeMap.set(update.id, Object.assign({}, existing, update));
schedule();
}
function enqueueEdge(update) {
var existing = edgeMap.get(update.id) || {};
edgeMap.set(update.id, Object.assign({}, existing, update));
schedule();
}
function enqueueEdges(updates) {
for (var i = 0; i < updates.length; i++) {
var u = updates[i];
var existing = edgeMap.get(u.id) || {};
edgeMap.set(u.id, Object.assign({}, existing, u));
}
schedule();
}
return { enqueueNode: enqueueNode, enqueueEdge: enqueueEdge, enqueueEdges: enqueueEdges, flush: flush };
})();
function smoothColorTransition(color1, color2, min, max, current) { function smoothColorTransition(color1, color2, min, max, current) {
// Clamp current between min and max // Clamp current between min and max
const clamped = Math.min(Math.max(current, min), max); const clamped = Math.min(Math.max(current, min), max);
@@ -123,15 +172,10 @@ onEdgeEngine.setArrivalCallback(async ({ from, to, dot }) => {
// Batch edge updates for better performance // Batch edge updates for better performance
if (document.getElementById('showTTL').checked) { if (document.getElementById('showTTL').checked) {
const edgeColor = smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1); const edgeColor = smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1);
edges.update([ debouncedVis.enqueueEdges([
{id: `${from}->${to}`, color: {color: edgeColor, highlight: edgeColor}}, {id: `${from}->${to}`, color: {color: edgeColor, highlight: edgeColor}},
{id: `${to}->${from}`, color: {color: edgeColor, highlight: edgeColor}} {id: `${to}->${from}`, color: {color: edgeColor, highlight: edgeColor}}
]); ]);
} else {
edges.update([
{id: `${from}->${to}`, color: {color: '#2b7ce9', highlight: '#2b7ce9'}},
{id: `${to}->${from}`, color: {color: '#2b7ce9', highlight: '#2b7ce9'}}
]);
} }
if (nodeTable[to].packetCache.has(dot.id.split('-')[0])) { if (nodeTable[to].packetCache.has(dot.id.split('-')[0])) {
return; return;
@@ -142,9 +186,7 @@ onEdgeEngine.setArrivalCallback(async ({ from, to, dot }) => {
var originNode = dot.id.split('-')[3]; var originNode = dot.id.split('-')[3];
ttl--; ttl--;
if (document.getElementById('showTTL').checked) { if (document.getElementById('showTTL').checked) {
nodes.update({id: to, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}}); debouncedVis.enqueueNode({id: to, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}});
} else {
nodes.update({id: to, color: {background: '#97c2fc'}});
} }
if (ttl > 0) { if (ttl > 0) {
const neighborsSnapshot = nodeTable[to].connections.slice(); const neighborsSnapshot = nodeTable[to].connections.slice();
@@ -167,7 +209,15 @@ onEdgeEngine.setArrivalCallback(async ({ from, to, dot }) => {
} }
} else { } else {
if (document.getElementById('showTTL').checked) { if (document.getElementById('showTTL').checked) {
markNeighborsAsFailed(to, from, packetId); // Use a unique marker packet ID to bypass cache and mark unreachable nodes
const markerPacketId = `${packetId}`;
const neighborsSnapshot = nodeTable[to].connections.slice();
for (let neighbor of neighborsSnapshot) {
if (neighbor === from || neighbor === originNode) {
continue;
}
quickPacketRaf(neighbor, `${markerPacketId}-${generateRealisticLabel()}-${0}-${originNode}`, from);
}
} }
} }
}); });
@@ -283,9 +333,9 @@ function quickRoute(startNode, packetInfo, fromNode) {
// Batch updates for better performance // Batch updates for better performance
if (document.getElementById('showTTL').checked) { if (document.getElementById('showTTL').checked) {
nodes.update({id: nodeId, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}}); debouncedVis.enqueueNode({id: nodeId, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}});
const edgeColor = smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1); const edgeColor = smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1);
edges.update([ debouncedVis.enqueueEdges([
{id: `${from}->${nodeId}`, color: {color: edgeColor, highlight: edgeColor}}, {id: `${from}->${nodeId}`, color: {color: edgeColor, highlight: edgeColor}},
{id: `${nodeId}->${from}`, color: {color: edgeColor, highlight: edgeColor}} {id: `${nodeId}->${from}`, color: {color: edgeColor, highlight: edgeColor}}
]); ]);
@@ -338,7 +388,7 @@ async function routePacket(currentNode, goalNode, packetInfo, first=false) {
return; return;
} }
if (document.getElementById('showTTL').checked) { if (document.getElementById('showTTL').checked) {
nodes.update({id: currentNode, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}}); debouncedVis.enqueueNode({id: currentNode, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}});
} else { } else {
if (first) { if (first) {
nodes.update({id: currentNode, color: {background: '#ff00d9'}}); nodes.update({id: currentNode, color: {background: '#ff00d9'}});
@@ -369,7 +419,7 @@ async function routePacket(currentNode, goalNode, packetInfo, first=false) {
// Batch edge updates for better performance // Batch edge updates for better performance
if (document.getElementById('showTTL').checked) { if (document.getElementById('showTTL').checked) {
const edgeColor = smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1); const edgeColor = smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1);
edges.update([ debouncedVis.enqueueEdges([
{id: `${currentNode}->${nextNode}`, color: {color: edgeColor, highlight: edgeColor}}, {id: `${currentNode}->${nextNode}`, color: {color: edgeColor, highlight: edgeColor}},
{id: `${nextNode}->${currentNode}`, color: {color: edgeColor, highlight: edgeColor}} {id: `${nextNode}->${currentNode}`, color: {color: edgeColor, highlight: edgeColor}}
]); ]);
@@ -465,9 +515,9 @@ const markNeighborsAsFailed = (startNodeId, from, packetId, visited = new Set(),
tasks.push({ nodeId: neighbor, from: task.nodeId, neighbors: nextNeighbors, idx: 0 }); tasks.push({ nodeId: neighbor, from: task.nodeId, neighbors: nextNeighbors, idx: 0 });
// batch visuals // batch visuals
nodeBatch.push({ id: neighbor, color: { background: 'white' } }); nodeBatch.push({ id: neighbor, color: { background: '#878787' } });
edgeBatch.push({ id: `${task.nodeId}->${neighbor}`, color: { color: 'white', highlight: '#97c2fc' } }); edgeBatch.push({ id: `${task.nodeId}->${neighbor}`, color: { color: '#878787', highlight: '#878787' } });
edgeBatch.push({ id: `${neighbor}->${task.nodeId}`, color: { color: 'white', highlight: '#97c2fc' } }); edgeBatch.push({ id: `${neighbor}->${task.nodeId}`, color: { color: '#878787', highlight: '#878787' } });
ops++; ops++;