Browse Source

forcegraph: get rid of shadow svg

Nils Schneider 9 years ago
parent
commit
4c97886039
1 changed files with 102 additions and 67 deletions
  1. 102 67
      lib/forcegraph.js

+ 102 - 67
lib/forcegraph.js

@@ -1,10 +1,11 @@
 define(["d3"], function (d3) {
   var margin = 200
+  var NODE_RADIUS = 15
+  var LINE_RADIUS = 12
 
   return function (config, linkScale, sidebar, router) {
     var self = this
-    var svg, canvas, ctx, screenRect
-    var svgNodes, svgLinks
+    var canvas, ctx, screenRect
     var nodesDict, linksDict
     var zoomBehavior
     var force
@@ -18,6 +19,8 @@ define(["d3"], function (d3) {
     var nodes = []
     var unknownNodes = []
 
+    var draggedNode
+
     var LINK_DISTANCE = 70
 
     function graphDiameter(nodes) {
@@ -42,20 +45,39 @@ define(["d3"], function (d3) {
         return d.o.id
     }
 
-    function dragstart(d) {
+    function dragstart() {
+      var e = translateXY(d3.mouse(el))
+
+      var nodes = intNodes.filter(function (d) {
+        return distancePoint(e, d) < NODE_RADIUS
+      })
+
+      if (nodes.length === 0)
+        return
+
+      draggedNode = nodes[0]
       d3.event.sourceEvent.stopPropagation()
-      d.fixed |= 2
+      d3.event.sourceEvent.preventDefault()
+      draggedNode.fixed |= 2
     }
 
-    function dragmove(d) {
-      d.px = d3.event.x
-      d.py = d3.event.y
-      force.resume()
+    function dragmove() {
+      if (draggedNode) {
+        var e = translateXY(d3.mouse(el))
+
+        draggedNode.px = e.x
+        draggedNode.py = e.y
+        force.resume()
+      }
     }
 
-    function dragend(d) {
-      d3.event.sourceEvent.stopPropagation()
-      d.fixed &= 1
+    function dragend() {
+      if (draggedNode) {
+        d3.event.sourceEvent.stopPropagation()
+        d3.event.sourceEvent.preventDefault()
+        draggedNode.fixed &= 1
+        draggedNode = undefined
+      }
     }
 
     var draggableNode = d3.behavior.drag()
@@ -112,9 +134,6 @@ define(["d3"], function (d3) {
                     right: (canvas.width - translate[0]) / scale,
                     bottom: (canvas.height - translate[1]) / scale}
 
-      svg.attr("transform", "translate(" + translate + ") " +
-                            "scale(" + scale + ")")
-
       redraw()
     }
 
@@ -183,38 +202,6 @@ define(["d3"], function (d3) {
         panzoomTo([0, 0], force.size())
     }
 
-    function updateLinks(vis, data) {
-      var link = vis.selectAll("line")
-                    .data(data, function (d) { return d.o.id })
-
-      link.exit().remove()
-
-      link.enter().append("line")
-                  .on("click", function (d) {
-                    if (!d3.event.defaultPrevented)
-                      router.link(d.o)()
-                  })
-
-      return link
-    }
-
-    function updateNodes(vis, data) {
-      var node = vis.selectAll("circle")
-                    .data(data, function(d) { return d.o.id })
-
-      node.exit().remove()
-
-      node.enter().append("circle")
-          .attr("r", 12)
-          .on("click", function (d) {
-            if (!d3.event.defaultPrevented)
-              router.node(d.o.node)()
-          })
-          .call(draggableNode)
-
-      return node
-    }
-
     function drawLabel(d) {
       var sum = d.neighbours.reduce(function (a, b) {
         return [a[0] + b.x, a[1] + b.y]
@@ -354,14 +341,6 @@ define(["d3"], function (d3) {
 
     function tickEvent() {
       redraw()
-
-      svgLinks.attr("x1", function(d) { return d.source.x })
-              .attr("y1", function(d) { return d.source.y })
-              .attr("x2", function(d) { return d.target.x })
-              .attr("y2", function(d) { return d.target.y })
-
-      svgNodes.attr("cx", function (d) { return d.x })
-              .attr("cy", function (d) { return d.y })
     }
 
     function resizeCanvas() {
@@ -375,6 +354,68 @@ define(["d3"], function (d3) {
       redraw()
     }
 
+    function distance(a, b) {
+      return Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)
+    }
+
+    function distancePoint(a, b) {
+      return Math.sqrt(distance(a, b))
+    }
+
+    function distanceLink(p, a, b) {
+      /* http://stackoverflow.com/questions/849211 */
+
+      var l2 = distance(a, b)
+
+      if (l2 === 0)
+        return distance(p, a)
+
+      var t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / l2
+
+      if (t < 0)
+        return distance(p, a)
+
+      if (t > 1)
+        return distance(p, b)
+
+      return Math.sqrt(distance(p, { x: a.x + t * (b.x - a.x),
+                                     y: a.y + t * (b.y - a.y) }))
+    }
+
+    function translateXY(d) {
+      var translate = zoomBehavior.translate()
+      var scale = zoomBehavior.scale()
+
+      return {x: (d[0] - translate[0]) / scale,
+              y: (d[1] - translate[1]) / scale
+             }
+    }
+
+    function onClick() {
+      if (d3.event.defaultPrevented)
+        return
+
+      var e = translateXY(d3.mouse(el))
+
+      var nodes = intNodes.filter(function (d) {
+        return distancePoint(e, d) < NODE_RADIUS
+      })
+
+      if (nodes.length > 0) {
+        router.node(nodes[0].o.node)()
+        return
+      }
+
+      var links = intLinks.filter(function (d) {
+        return distanceLink(e, d.source, d.target) < LINE_RADIUS
+      })
+
+      if (links.length > 0) {
+        router.link(links[0].o)()
+        return
+      }
+    }
+
     el = document.createElement("div")
     el.classList.add("graph")
     self.div = el
@@ -384,15 +425,13 @@ define(["d3"], function (d3) {
                      .on("zoom", panzoom)
                      .translate([sidebar.getWidth(), 0])
 
-    canvas = d3.select(el).append("canvas").node()
-
-    svg = d3.select(el).append("svg")
-            .attr("pointer-events", "all")
-            .call(zoomBehavior)
-            .append("g")
-
-    var visLinks = svg.append("g")
-    var visNodes = svg.append("g")
+    canvas = d3.select(el)
+               .call(zoomBehavior)
+               .append("canvas")
+               .attr("pointer-events", "all")
+               .on("click", onClick)
+               .call(draggableNode)
+               .node()
 
     ctx = canvas.getContext("2d")
 
@@ -512,9 +551,6 @@ 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 })
 
@@ -570,7 +606,6 @@ define(["d3"], function (d3) {
       force.stop()
       canvas.remove()
       force = null
-      svg = null
     }
 
     return self