let { clientHeight, clientWidth } = document.documentElement; const w = clientWidth, h = clientHeight; const canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; document.body.appendChild(canvas); const ctx = canvas.getContext('2d'); ctx.translate(w/2, h/2); const canvasS = document.createElement('canvas'); canvasS.width = w; canvasS.height = h; document.body.appendChild(canvasS); canvasS.style.position = 'absolute'; canvasS.style.top = canvasS.style.left = 0; const ctxS = canvasS.getContext('2d'); ctxS.translate(w/2, h/2); function generate (N, E) { let i, nodes = new Array(N), edges = new Array(E); for (i = 0; i < N; i++) nodes[i] = { id: 'n' + i, text: 'Node ' + i, x: -w/2 + Math.random() * w, y: -h/2 + Math.random() * h }; for (i = 0; i < E; i++) edges[i] = { id: 'e' + i, text: 'Edge ' + i, source: (Math.random() * N | 0), target: (Math.random() * N | 0) }; return { nodes, edges }; } var N = 10000; var E = 10000; var G = generate (N, E); var sel = {}; function render () { let i; ctx.clearRect(-w/2, -h/2, w, h); ctx.strokeStyle = 'black'; ctx.fillStyle = 'black'; ctx.globalAlpha = 0.5; for (i = 0; i < E; i++) { let e = G.edges[i]; let source = G.nodes[e.source]; let target = G.nodes[e.target]; ctx.strokeStyle = sel[i] ? 'red' : 'black'; ctx.beginPath(); ctx.moveTo(source.x, source.y); ctx.lineTo(target.x, target.y); ctx.stroke(); } ctx.fillStyle = '#cccccc'; ctx.globalAlpha = 1; for (i = 0; i < N; i++) { let n = G.nodes[i]; ctx.moveTo(n.x, n.y); ctx.beginPath(); ctx.arc(n.x, n.y, 2, 0, 2 * Math.PI); ctx.stroke(); ctx.fill(); } } function renderSel () { ctxS.clearRect(-w/2, -h/2, w, h); ctxS.strokeStyle = 'red'; ctxS.fillStyle = 'black'; ctxS.globalAlpha = 0.5; for (let i = 0; i < E; i++) { let e = G.edges[i]; let source = G.nodes[e.source]; let target = G.nodes[e.target]; if (sel[i]) { ctxS.beginPath(); ctxS.moveTo(source.x, source.y); ctxS.lineTo(target.x, target.y); ctxS.stroke(); } } } console.time('render'); render(); console.timeEnd('render'); class er { constructor(s, t, id) { this.s = s; this.t = t; this.id = id; } get minX () { return G.nodes[this.s].x; } get minY () { return G.nodes[this.s].y; } get maxX () { return G.nodes[this.t].x; } get maxY () { return G.nodes[this.t].y; } } console.time('tree'); var tree = rbush(9); var bulk = []; for (var i = 0; i < E; i++) { var e = G.edges[i]; var s = G.nodes[e.source]; var t = G.nodes[e.target]; bulk.push(new er(e.source, e.target, i)); } tree.load(bulk); console.timeEnd('tree'); function onLine (x, y, x1, y1, x2, y2, width) { } function detectBrute (x, y) { let i; for (i = 0; i < E; i++) { } } function detectWithTree (x, y) { sel = tree .search({ minX: x, minY: y, maxX: x, maxY: y}) .reduce ((acc, e) => { acc[e.id] = true; return acc; }, {}); } var mx = 0; var my = 0; var timer = 0; const modes = { BRUTE: 0, TREE: 1 }; var mode = modes.TREE; function detect (x, y) { if (mode = modes.BRUTE) detectBrute(x, y); else detectWithTree(x, y); clearTimeout(timer); timer = setTimeout(renderSel, 16); } canvasS.addEventListener('mousemove', (evt) => { mx = evt.clientX - w / 2; my = evt.clientY - h / 2; setTimeout(() => detect(mx, my)); });