Skip to content

Instantly share code, notes, and snippets.

@puleos
Created December 17, 2012 16:11
Show Gist options
  • Save puleos/4319480 to your computer and use it in GitHub Desktop.
Save puleos/4319480 to your computer and use it in GitHub Desktop.
Node Manipulation using D3
// Ben Bederson
// June 2012
// www.cs.umd.edu/~bederson
$(function() {
var svg_w = 550,
svg_h = 400,
node_w = 70,
node_h = 20,
node_active_h = 50,
node_stroke = "8, 2",
cursor_r = 30,
rx = 2,
active_rx = 5,
src_r = 7,
nodesrc_stroke = "3, 3",
fill = d3.scale.category20(),
max_id = 1,
nodes = [],
links = [];
var svg = d3.select("#svg").append("svg:svg")
.attr("id", "svg")
.attr("width", svg_w)
.attr("height", svg_h);
var bgnd = svg.append("svg:rect")
.attr("id", "bgnd")
.attr("width", svg_w)
.attr("height", svg_h);
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([svg_w, svg_h])
.charge(-200);
force.on("tick", function() {
svg.selectAll("g.node")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("transform", function(d) { return "translate(" + d.x + ", " + d.y + ")"; });
svg.selectAll("line.link")
.attr("x1", function(d) { return parseInt(d.source.attr("x")) + node_w/2; })
.attr("y1", function(d) { return parseInt(d.source.attr("y")) + node_h/2; })
.attr("x2", function(d) { return parseInt(d.target.attr("x")) + node_w/2; })
.attr("y2", function(d) { return parseInt(d.target.attr("y")) + node_h/2; });
});
bgnd.on("mousedown", function() {
var point = d3.mouse(this),
node = {id: max_id++, x: point[0], y: point[1]};
nodes.push(node);
restart();
});
// Mouse moves over the background (thus exiting nodes)
bgnd.on("mouseover", function(d, i) {
unselectNodes();
});
restart();
function restart() {
// Create a group for each node
var groups = svg.selectAll("g.node")
.data(nodes)
.enter()
.append("svg:g")
.attr("id", function(d) {
return "node" + d.id;
})
.attr("class", "node nodeInactive")
.attr("width", node_w)
.attr("height", node_h)
.on("mouseover", function(d, i) {
selectNode(d3.select(this));
});
// .call(force.drag);
createNodeContent(groups);
// Create links
svg.selectAll("line.link")
.data(links)
.enter().insert("svg:line", "g.node")
.attr("class", "link")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", 0);
force.start();
}
function createNodeContent(groups) {
// Create node rectangles
groups
.append("svg:rect")
.attr("class", "nodeBgnd nodeBgndInactive")
.attr("width", node_w)
.attr("height", node_h)
.attr("stroke-dasharray", node_stroke)
.attr("rx", rx);
// Create node labels
groups
.append("svg:text")
.attr("class", "nodeLabel nodeLabelInactive")
.attr("x", function(d) { return 5; })
.attr("y", function(d) { return 15; })
.style("-moz-user-select", "none")
.style("-webkit-user-select", "none")
.text(function(d) { return "(" + Math.floor(d.x) + ", " + Math.floor(d.y) + ")"});
}
function selectNode(node) {
var activeLine = [];
var drawingLine = false;
var sourceMouseDown;
if (node.attr("class").indexOf("nodeInactive") != -1) {
unselectNodes();
}
node
.attr("class", "node nodeActive")
.attr("height", node_active_h);
node.on("mouseover", null);
node.select("rect")
.attr("class", "nodeBgnd nodeBgndActive")
.attr("stroke-dasharray", "0")
.transition()
.attr("rx", active_rx)
.attr("height", node_active_h);
node.select(".nodeLabel")
.attr("class", "nodeLabel nodeLabelActive")
.attr("orig", node.select(".nodeLabel").text())
.text("More detail");
var nodeSrc = node.selectAll("circle.nodeSrc");
if (nodeSrc.empty()) {
var line = d3.svg.line()
.x(function(d, i) { return d.x; })
.y(function(d, i) { return d.y; })
var nodeSrc = node.append("svg:circle")
.attr("class", "nodeSrc nodeSrcInactive")
.attr("stroke-dasharray", nodesrc_stroke)
.attr("cx", node_w)
.attr("cy", 0)
.attr("r", 0)
.on("mouseover", function(d, i) {
if (!drawingLine) {
d3.select(this)
.attr("class", "nodeSrc nodeSrcActive")
.attr("stroke-dasharray", "0");
}
})
.on("mouseout", function(d, i) {
if (!drawingLine) {
d3.select(this)
.attr("class", "nodeSrc nodeSrcInactive")
.attr("stroke-dasharray", nodesrc_stroke);
}
})
.on("mousedown", function(d, i) {
drawingLine = true;
node.attr("class", "node nodeSelected");
node.select("rect")
.attr("class", "nodeBgnd nodeBgndSelected");
d3.select(".activeLine").remove(); // Only want one targetting line
var cx = parseInt(node.attr("x")) + parseInt(d3.select(this).attr("cx"));
var cy = parseInt(node.attr("y")) + parseInt(d3.select(this).attr("cy"));
var dx = - d3.mouse(svg.node())[0];
var dy = - d3.mouse(svg.node())[1];
sourceMouseDown = [d3.event.clientX + dx, d3.event.clientY + dy];
activeLine = [{x:cx, y:cy}, {x:cx, y:cy}];
svg
.append("svg:path")
.data([activeLine])
.attr("class", "activeLine")
.attr("d", line);
// Use JQuery events here so they are active even when mouse over SVG elements
$("#svg").mousemove(function(evt) {
if (evt.which > 0) {
var pt = [evt.clientX, evt.clientY];
pt[0] -= sourceMouseDown[0];
pt[1] -= sourceMouseDown[1];
activeLine[1] = {x:pt[0], y:pt[1]};
d3.select(".activeLine")
.data([activeLine])
.attr("d", line);
}
});
$("#svg").mouseup(function(evt) {
drawingLine = false;
// First get rid of line drawing event handlers
$("#svg").unbind('mousemove');
$("#svg").unbind('mouseup');
// Then update graph connectivity
d3.select(".activeLine").remove();
var target = d3.select(".nodeActive");
if (!target.empty()) {
links.push({source: node, target: target});
restart();
}
// Update classes
d3.select(".nodeSelected")
.attr("class", "node nodeActive");
d3.select(".nodeBgndSelected")
.attr("class", "nodeBgnd nodeBgndActive");
unselectNodes();
// Update node event handlers
});
})
.transition()
.attr("cy", node_active_h/2)
.attr("r", src_r)
.each("end", function() {
restart();
});
}
}
function unselectNodes() {
var nodes = d3.selectAll(".nodeActive")
if (!nodes.empty()) {
nodes
.attr("class", "node nodeInactive")
.attr("height", 20)
.on("mouseover", function(d, i) {
selectNode(d3.select(this));
});
nodes.select("rect")
.attr("class", "nodeBgnd nodeBgndInactive")
.attr("stroke-dasharray", node_stroke)
.transition()
.attr("rx", rx)
.attr("height", 20);
nodes.select(".nodeLabel")
.attr("class", "nodeLabel nodeLabelInactive")
.text(nodes.select(".nodeLabel").attr("orig"));
d3.selectAll(".nodeSrc")
.transition()
.attr("cy", 0)
.attr("r", 0)
.remove();
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment