diff --git a/lib/forcegraph.js b/lib/forcegraph.js index 4a35ae5..95b6332 100644 --- a/lib/forcegraph.js +++ b/lib/forcegraph.js @@ -1,7 +1,9 @@ define(["d3"], function (d3) { - return function (config, linkScale, sidebar, router) { + var margin = 200 + + return function (config, linkScale, sidebar, router) { var self = this - var svg, canvas, ctx + var svg, canvas, ctx, screenRect var svgNodes, svgLinks var nodesDict, linksDict var zoomBehavior @@ -13,6 +15,8 @@ define(["d3"], function (d3) { var highlight var highlightedNodes = [] var highlightedLinks = [] + var nodes = [] + var unknownNodes = [] var LINK_DISTANCE = 70 @@ -90,14 +94,24 @@ define(["d3"], function (d3) { } } + var translateP, scaleP + function panzoom() { var translate = zoomBehavior.translate() var scale = zoomBehavior.scale() + panzoomReal(translate, scale) + + translateP = translate + scaleP = scale } function panzoomReal(translate, scale) { + screenRect = {left: -translate[0] / scale, top: -translate[1] / scale, + right: (canvas.width - translate[0]) / scale, + bottom: (canvas.height - translate[1]) / scale} + svg.attr("transform", "translate(" + translate + ") " + "scale(" + scale + ")") @@ -222,18 +236,47 @@ define(["d3"], function (d3) { ctx.fillText(d.label, x, y) } + function visibleLinks(d) { + return (d.source.x > screenRect.left && d.source.x < screenRect.right && + d.source.y > screenRect.top && d.source.y < screenRect.bottom) || + (d.target.x > screenRect.left && d.target.x < screenRect.right && + d.target.y > screenRect.top && d.target.y < screenRect.bottom) + } + + function visibleNodes(d) { + return d.x + margin > screenRect.left && d.x - margin < screenRect.right && + d.y + margin > screenRect.top && d.y - margin < screenRect.bottom + } + function redraw() { var translate = zoomBehavior.translate() var scale = zoomBehavior.scale() - var nodes = intNodes.filter(function (d) { return d.o.node }) - var unknownNodes = intNodes.filter(function (d) { return !d.o.node }) - var links = intLinks - ctx.save() + var links = intLinks.filter(visibleLinks) + + var xExtent = d3.extent(intNodes, function (d) { return d.px }) + var yExtent = d3.extent(intNodes, function (d) { return d.py }) + ctx.font = "11px Roboto" - ctx.clearRect(0, 0, canvas.width, canvas.height) + + if (translateP) { + ctx.save() + ctx.translate(translateP[0], translateP[1]) + ctx.scale(scaleP, scaleP) + ctx.clearRect(xExtent[0] - margin, yExtent[0] - margin, + xExtent[1] - xExtent[0] + 2 * margin, + yExtent[1] - yExtent[0] + 2 * margin) + ctx.restore() + } + + ctx.save() ctx.translate(translate[0], translate[1]) ctx.scale(scale, scale) + if (!translateP) + ctx.clearRect(xExtent[0] - margin, yExtent[0] - margin, + xExtent[1] - xExtent[0] + 2 * margin, + yExtent[1] - yExtent[0] + 2 * margin) + if (highlightedLinks.length) { ctx.save() ctx.lineWidth = 16 @@ -263,10 +306,11 @@ define(["d3"], function (d3) { ctx.textBaseline = "middle" ctx.fillStyle = "rgba(0, 0, 0, 0.6)" - intNodes.forEach(drawLabel) + intNodes.filter(visibleNodes).forEach(drawLabel) ctx.beginPath() - unknownNodes.forEach(function (d) { + + unknownNodes.filter(visibleNodes).forEach(function (d) { ctx.moveTo(d.x + 8, d.y) ctx.arc(d.x, d.y, 8, 0, 2 * Math.PI) }) @@ -278,7 +322,7 @@ define(["d3"], function (d3) { ctx.stroke() ctx.beginPath() - nodes.forEach(function (d) { + nodes.filter(visibleNodes).forEach(function (d) { ctx.moveTo(d.x + 8, d.y) ctx.arc(d.x, d.y, 8, 0, 2 * Math.PI) }) @@ -447,6 +491,9 @@ define(["d3"], function (d3) { svgLinks = updateLinks(visLinks, intLinks) svgNodes = updateNodes(visNodes, intNodes) + nodes = intNodes.filter(function (d) { return d.o.node }) + unknownNodes = intNodes.filter(function (d) { return !d.o.node }) + if (localStorageTest()) { var save = JSON.parse(localStorage.getItem("graph/nodeposition"))