// Copyright 2014 The Cayley Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. function Network() { var height = 800; var width = 800; // Things in the first force graph. var nodesG = null; var linksG = null; var linkNodesG = null; var node = null; var linknode = null; var link = null; var curLinksData = null; var curNodesData = null; // Things in the second force graph var tag_nodesG = null; var tag_linksG = null; var tag_node = null; var tag_link = null; var curTagLinksData = null; var curTagNodesData = null; var allData = null; var dragstart = function(d) { d3.select(this).classed("fixed", d.fixed = true); } //our force directed layout var force = d3.layout.force() var force_drag = force.drag().on("dragstart", dragstart) var tag_force = d3.layout.force() //color function used to color nodes var baseNodeColors = d3.scale.category20() var linkNodeColors = function(d) { return d3.rgb("#555").toString() } var strokeFor = function(d) { return d3.rgb("#000").brighter().toString() } var rotationTransformForLinkNode = function (d) { var center_x = (d.link.source.x + d.link.target.x) / 2 var center_y = (d.link.source.y + d.link.target.y) / 2 var dx = d.link.target.x - d.link.source.x var dy = d.link.target.y - d.link.source.y var rotation_radians = Math.atan2(dy,dx) var rotation_degrees = rotation_radians * (180 / Math.PI) return "rotate(" + rotation_degrees + ", " + center_x + ", " + center_y + ")" } var updateLink = function() { this.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}) } var forceTick = function(e) { tag_force.start() node .attr("cx", function(d) {return d.x}) .attr("cy", function(d) {return d.y}) linknode.each(function(d) { d.x = (d.link.source.x + d.link.target.x) / 2 d.y = (d.link.source.y + d.link.target.y) / 2 }) linknode .attr("cx", function(d) {return d.x }) .attr("cy", function(d) {return d.y }) .attr("transform", rotationTransformForLinkNode) link.call(updateLink); tagForceTick(e) } var tagForceTick = function(e) { tag_node.each(function(d) { if(d.is_tag === false) { d.x = d.node.x; d.y = d.node.y; } else { var b = this.childNodes[1].getBBox(); var diffX = d.x - d.node.x; var diffY = d.y - d.node.y; var dist = Math.sqrt(diffX * diffX + diffY * diffY); var shiftX = b.width * (diffX - dist) / (dist * 2); shiftX = Math.max(-b.width, Math.min(0, shiftX)); var shiftY = 5; this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")"); } }); tag_node .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); tag_link.call(updateLink); } var setupData = function (data) { data.nodes.forEach(function (n) { n.x = randomnumber=Math.floor(Math.random()*width) n.y = randomnumber=Math.floor(Math.random()*height) n.radius = 10 }); var nodesMap = mapNodes(data.nodes) data.links.forEach(function (l) { l.source = nodesMap.get(l.source) l.target = nodesMap.get(l.target) nodesMap.get(l.link_node).link = l }) data.tag_links = [] data.tag_nodes = [] var tag_id_counter = 0 data.nodes.forEach(function (n) { if (n.tags !== undefined) { n.tags.forEach( function (tag) { var tag_node = {} tag_node.id = "tag" + tag_id_counter tag_node.tag = tag tag_node.x = n.x tag_node.y = n.y tag_node.is_tag = true tag_node.is_value = false tag_node.node = n tag_node.radius = 0 tag_id_counter += 1; var fake_node = {} fake_node.id = "tag" + tag_id_counter fake_node.x = n.x fake_node.y = n.y fake_node.node = n fake_node.radius = 0 fake_node.is_tag = false tag_id_counter += 1; var tag_link = {} tag_link.source = fake_node tag_link.target = tag_node data.tag_nodes.push(tag_node) data.tag_nodes.push(fake_node) data.tag_links.push(tag_link) }) } if (n.values !== undefined) { n.values.forEach( function (value) { var tag_node = {} tag_node.id = "tag" + tag_id_counter tag_node.tag = value tag_node.x = n.x tag_node.y = n.y tag_node.is_tag = true tag_node.is_value = true tag_node.node = n tag_node.radius = 0 tag_id_counter += 1; var fake_node = {} fake_node.id = "tag" + tag_id_counter fake_node.x = n.x fake_node.y = n.y fake_node.node = n fake_node.radius = 0 fake_node.is_tag = false tag_id_counter += 1; var tag_link = {} tag_link.source = fake_node tag_link.target = tag_node data.tag_nodes.push(tag_node) data.tag_nodes.push(fake_node) data.tag_links.push(tag_link) }) } }) return data; } var mapNodes = function (nodes) { var nodesMap = d3.map() nodes.forEach(function (n) { nodesMap.set(n.id, n) }) return nodesMap } var network = function (selection, data) { allData = setupData(data) var vis = d3.select(selection).append("svg") .attr("width", width) .attr("height", height) vis.append("defs").append("marker") .attr("id", "arrowhead") .attr("refX", 6 + 3) // shift? .attr("refY", 2) .attr("markerWidth", 6) .attr("markerHeight", 4) .attr("orient", "auto") .append("path") .attr("d", "M 0,0 V 4 L6,2 Z"); linksG = vis.append("g").attr("id", "links") nodesG = vis.append("g").attr("id", "nodes") linkNodesG = vis.append("g").attr("id", "link-nodes") tagNodesG = vis.append("g").attr("id", "tag_nodes") tagLinksG = vis.append("g").attr("id", "tag_links") force.size([width, height]) force.on("tick", forceTick) .charge(-200) .linkDistance(100); tag_force .gravity(0) .linkDistance(25) .linkStrength(20) .charge(-100) .size([width, height]) tag_force.start() //perform rendering and start force layout update() } var update = function () { allNodes = allData.nodes; curLinksData = allData.links; curNodesData = $.grep(allData.nodes, function(n) { return n.is_link_node === false; }) curLinkNodesData = $.grep(allData.nodes, function(n) { return n.is_link_node; }) curTagLinksData = allData.tag_links curTagNodesData = allData.tag_nodes force.nodes(allNodes); node = nodesG.selectAll("circle.node") .data(curNodesData, function(d) {return d.id}) node.enter().append("circle") .attr("class", "node") .attr("cx", function(d) {return d.x}) .attr("cy", function(d) {return d.y}) .attr("r", function(d) {return d.radius;}) .style("fill", function(d) {return baseNodeColors(d.id);}) .style("stroke", function(d) {return strokeFor(d);}) .style("stroke-width", 1.0) .call(force_drag) node.exit().remove(); linknode = linkNodesG.selectAll("ellipse.node") .data(curLinkNodesData, function(d) { return d.id }) linknode.enter().append("ellipse") .attr("class", "node") .attr("cx", function(d) {return d.x}) .attr("cy", function(d) {return d.y}) .attr("rx", function(d) {return d.radius;}) .attr("ry", function(d) {return d.radius / 2;}) .style("fill", function(d) {return linkNodeColors(d.id);}) .style("stroke", function(d) {return strokeFor(d);}) .style("stroke-width", 1.0); linknode.exit().remove(); force.links(curLinksData); link = linksG.selectAll("line.link") .data(curLinksData, function(d) { return d.source.id + "_" + d.target.id}); link.enter().append("line") .attr("class", "link") .attr("stroke", "#222") .attr("stroke-opacity", 1.0) .attr("marker-end", "url(#arrowhead)") .style("stroke-width", 2.0) .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}); link.exit().remove(); force.start(); tag_force.nodes(curTagNodesData); tag_node = nodesG.selectAll("g.tag_node") .data(curTagNodesData, function(d) { return d.id }) var tag_g = tag_node.enter().append("g").attr("class", "tag_node") tag_g.append("svg:circle").attr("r", 0).style("fill", "#FFF"); tag_g.append("svg:text") .text(function(d) { return d.is_tag ? d.tag : "" }) .style("fill", "#555") .style("font-family", function(d) { return d.is_value ? "Courier" : "Arial"}) .style("font-size", 12); tag_force.links(curTagLinksData); tag_link = linksG.selectAll("line.tag_link") .data(curTagLinksData, function(d) { return d.source.id + "_" + d.target.id}); tag_link.enter().append("line") .attr("class", "tag_link") .attr("stroke", "#ddd") .attr("stroke-opacity", 0.5) .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}); tag_link.exit().remove(); tag_force.start() } return network };