Skip to content

Instantly share code, notes, and snippets.

@enjalot
Created January 24, 2013 04:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enjalot/4617766 to your computer and use it in GitHub Desktop.
Save enjalot/4617766 to your computer and use it in GitHub Desktop.
saveable graph layout
{"description":"saveable graph layout","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"graph.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"style.css":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"fullscreen":false,"thumbnail":"http://i.imgur.com/hV9Eomz.png"}
{"nodes":[{"x":173,"y":306},{"x":107,"y":355},{"x":207,"y":309},{"x":200,"y":287},{"x":111,"y":300},{"x":100,"y":278},{"x":88,"y":307},{"x":93,"y":333},{"x":129,"y":361},{"x":117,"y":329},{"x":253,"y":327},{"x":299,"y":274},{"x":281,"y":190},{"x":231,"y":318},{"x":240,"y":344},{"x":242,"y":296},{"x":331,"y":163},{"x":339,"y":121},{"x":321,"y":122},{"x":304,"y":120},{"x":336,"y":104},{"x":320,"y":99},{"x":303,"y":104},{"x":318,"y":180},{"x":349,"y":236},{"x":350,"y":261},{"x":318,"y":265},{"x":331,"y":254},{"x":277,"y":219},{"x":261,"y":227},{"x":277,"y":145},{"x":304,"y":205},{"x":226,"y":281},{"x":305,"y":227},{"x":233,"y":232},{"x":223,"y":217},{"x":211,"y":230},{"x":234,"y":250},{"x":215,"y":247},{"x":319,"y":337},{"x":406,"y":200},{"x":386,"y":289},{"x":396,"y":236},{"x":281,"y":258},{"x":247,"y":204},{"x":202,"y":156},{"x":525,"y":291},{"x":460,"y":303},{"x":371,"y":319},{"x":305,"y":300},{"x":334,"y":218},{"x":288,"y":320},{"x":273,"y":387},{"x":234,"y":396},{"x":308,"y":320},{"x":348,"y":313},{"x":303,"y":360},{"x":393,"y":337},{"x":356,"y":335},{"x":372,"y":358},{"x":393,"y":445},{"x":394,"y":358},{"x":382,"y":348},{"x":379,"y":435},{"x":351,"y":352},{"x":362,"y":435},{"x":376,"y":454},{"x":447,"y":393},{"x":365,"y":271},{"x":369,"y":258},{"x":346,"y":280},{"x":360,"y":285},{"x":295,"y":244},{"x":442,"y":353},{"x":445,"y":329},{"x":395,"y":272},{"x":356,"y":452}],"links":[{"target":0,"source":1},{"target":0,"source":2},{"target":0,"source":3},{"target":2,"source":3},{"target":0,"source":4},{"target":0,"source":5},{"target":0,"source":6},{"target":0,"source":7},{"target":0,"source":8},{"target":0,"source":9},{"target":10,"source":11},{"target":3,"source":11},{"target":2,"source":11},{"target":0,"source":11},{"target":11,"source":12},{"target":11,"source":13},{"target":11,"source":14},{"target":11,"source":15},{"target":16,"source":17},{"target":16,"source":18},{"target":17,"source":18},{"target":16,"source":19},{"target":17,"source":19},{"target":18,"source":19},{"target":16,"source":20},{"target":17,"source":20},{"target":18,"source":20},{"target":19,"source":20},{"target":16,"source":21},{"target":17,"source":21},{"target":18,"source":21},{"target":19,"source":21},{"target":20,"source":21},{"target":16,"source":22},{"target":17,"source":22},{"target":18,"source":22},{"target":19,"source":22},{"target":20,"source":22},{"target":21,"source":22},{"target":16,"source":23},{"target":17,"source":23},{"target":18,"source":23},{"target":19,"source":23},{"target":20,"source":23},{"target":21,"source":23},{"target":22,"source":23},{"target":12,"source":23},{"target":11,"source":23},{"target":23,"source":24},{"target":11,"source":24},{"target":24,"source":25},{"target":23,"source":25},{"target":11,"source":25},{"target":24,"source":26},{"target":11,"source":26},{"target":16,"source":26},{"target":25,"source":26},{"target":11,"source":27},{"target":23,"source":27},{"target":25,"source":27},{"target":24,"source":27},{"target":26,"source":27},{"target":11,"source":28},{"target":27,"source":28},{"target":23,"source":29},{"target":27,"source":29},{"target":11,"source":29},{"target":23,"source":30},{"target":30,"source":31},{"target":11,"source":31},{"target":23,"source":31},{"target":27,"source":31},{"target":11,"source":32},{"target":11,"source":33},{"target":27,"source":33},{"target":11,"source":34},{"target":29,"source":34},{"target":11,"source":35},{"target":34,"source":35},{"target":29,"source":35},{"target":34,"source":36},{"target":35,"source":36},{"target":11,"source":36},{"target":29,"source":36},{"target":34,"source":37},{"target":35,"source":37},{"target":36,"source":37},{"target":11,"source":37},{"target":29,"source":37},{"target":34,"source":38},{"target":35,"source":38},{"target":36,"source":38},{"target":37,"source":38},{"target":11,"source":38},{"target":29,"source":38},{"target":25,"source":39},{"target":25,"source":40},{"target":24,"source":41},{"target":25,"source":41},{"target":41,"source":42},{"target":25,"source":42},{"target":24,"source":42},{"target":11,"source":43},{"target":26,"source":43},{"target":27,"source":43},{"target":28,"source":44},{"target":11,"source":44},{"target":28,"source":45},{"target":46,"source":47},{"target":47,"source":48},{"target":25,"source":48},{"target":27,"source":48},{"target":11,"source":48},{"target":26,"source":49},{"target":11,"source":49},{"target":49,"source":50},{"target":24,"source":50},{"target":49,"source":51},{"target":26,"source":51},{"target":11,"source":51},{"target":51,"source":52},{"target":39,"source":52},{"target":51,"source":53},{"target":51,"source":54},{"target":49,"source":54},{"target":26,"source":54},{"target":51,"source":55},{"target":49,"source":55},{"target":39,"source":55},{"target":54,"source":55},{"target":26,"source":55},{"target":11,"source":55},{"target":16,"source":55},{"target":25,"source":55},{"target":41,"source":55},{"target":48,"source":55},{"target":49,"source":56},{"target":55,"source":56},{"target":55,"source":57},{"target":41,"source":57},{"target":48,"source":57},{"target":55,"source":58},{"target":48,"source":58},{"target":27,"source":58},{"target":57,"source":58},{"target":11,"source":58},{"target":58,"source":59},{"target":55,"source":59},{"target":48,"source":59},{"target":57,"source":59},{"target":48,"source":60},{"target":58,"source":60},{"target":59,"source":60},{"target":48,"source":61},{"target":58,"source":61},{"target":60,"source":61},{"target":59,"source":61},{"target":57,"source":61},{"target":55,"source":61},{"target":55,"source":62},{"target":58,"source":62},{"target":59,"source":62},{"target":48,"source":62},{"target":57,"source":62},{"target":41,"source":62},{"target":61,"source":62},{"target":60,"source":62},{"target":59,"source":63},{"target":48,"source":63},{"target":62,"source":63},{"target":57,"source":63},{"target":58,"source":63},{"target":61,"source":63},{"target":60,"source":63},{"target":55,"source":63},{"target":55,"source":64},{"target":62,"source":64},{"target":48,"source":64},{"target":63,"source":64},{"target":58,"source":64},{"target":61,"source":64},{"target":60,"source":64},{"target":59,"source":64},{"target":57,"source":64},{"target":11,"source":64},{"target":63,"source":65},{"target":64,"source":65},{"target":48,"source":65},{"target":62,"source":65},{"target":58,"source":65},{"target":61,"source":65},{"target":60,"source":65},{"target":59,"source":65},{"target":57,"source":65},{"target":55,"source":65},{"target":64,"source":66},{"target":58,"source":66},{"target":59,"source":66},{"target":62,"source":66},{"target":65,"source":66},{"target":48,"source":66},{"target":63,"source":66},{"target":61,"source":66},{"target":60,"source":66},{"target":57,"source":67},{"target":25,"source":68},{"target":11,"source":68},{"target":24,"source":68},{"target":27,"source":68},{"target":48,"source":68},{"target":41,"source":68},{"target":25,"source":69},{"target":68,"source":69},{"target":11,"source":69},{"target":24,"source":69},{"target":27,"source":69},{"target":48,"source":69},{"target":41,"source":69},{"target":25,"source":70},{"target":69,"source":70},{"target":68,"source":70},{"target":11,"source":70},{"target":24,"source":70},{"target":27,"source":70},{"target":41,"source":70},{"target":58,"source":70},{"target":27,"source":71},{"target":69,"source":71},{"target":68,"source":71},{"target":70,"source":71},{"target":11,"source":71},{"target":48,"source":71},{"target":41,"source":71},{"target":25,"source":71},{"target":26,"source":72},{"target":27,"source":72},{"target":11,"source":72},{"target":48,"source":73},{"target":48,"source":74},{"target":73,"source":74},{"target":69,"source":75},{"target":68,"source":75},{"target":25,"source":75},{"target":48,"source":75},{"target":41,"source":75},{"target":70,"source":75},{"target":71,"source":75},{"target":64,"source":76},{"target":65,"source":76},{"target":66,"source":76},{"target":63,"source":76},{"target":62,"source":76},{"target":48,"source":76},{"target":58,"source":76}]}
//adapted from mike bostock's example:
//http://bl.ocks.org/4566102
//focused on saving the results of dragging
var cm = tributary.getCodeEditor("graph.json");
var graph = JSON.parse(cm.getValue());
var width = tributary.sw;
var height = tributary.sh;
var shiftKey;
var svg = d3.select("svg")
var link = svg.append("g")
.attr("class", "link")
.selectAll("line");
var brush = svg.append("g")
.datum(function() { return {selected: false, previouslySelected: false}; })
.attr("class", "brush");
var node = svg.append("g")
.attr("class", "node")
.selectAll("circle");
var linkIndices = _.map(graph.links, function(d) {
return {target: d.target, source: d.source};
})
graph.links.forEach(function(d) {
d.source = graph.nodes[d.source];
d.target = graph.nodes[d.target];
});
link = link.data(graph.links).enter().append("line")
.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; });
brush.call(d3.svg.brush()
.x(d3.scale.identity().domain([0, width]))
.y(d3.scale.identity().domain([0, height]))
.on("brushstart", function(d) {
node.each(function(d) { d.previouslySelected = shiftKey && d.selected; });
})
.on("brush", function() {
var extent = d3.event.target.extent();
node.classed("selected", function(d) {
return d.selected = d.previouslySelected ^
(extent[0][0] <= d.x && d.x < extent[1][0]
&& extent[0][1] <= d.y && d.y < extent[1][1]);
});
})
.on("brushend", function() {
d3.event.target.clear();
d3.select(this).call(d3.event.target);
}));
node = node.data(graph.nodes).enter().append("circle")
.attr("r", 4)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.on("mousedown", function(d) {
if (!d.selected) { // Don't deselect on shift-drag.
if (!shiftKey) node.classed("selected", function(p) { return p.selected = d === p; });
else d3.select(this).classed("selected", d.selected = true);
}
})
.on("mouseup", function(d) {
if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
})
.call(d3.behavior.drag()
.on("drag", function(d) { nudge(d3.event.dx, d3.event.dy); })
.on("dragend", function(d) {
save();
})
);
function nudge(dx, dy) {
node.filter(function(d) { return d.selected; })
.attr("cx", function(d) { return d.x += dx; })
.attr("cy", function(d) { return d.y += dy; })
link.filter(function(d) { return d.source.selected; })
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; });
link.filter(function(d) { return d.target.selected; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
//d3.event.preventDefault();
}
function save() {
//remove extraneous attributes from graph before saving;
var nodes = _.map(graph.nodes, function(d) {
return {x: d.x, y: d.y };
})
var links = _.map(graph.links, function(d) {
return {source: {x: d.source.x, y: d.source.y },
target: {x: d.target.x, y: d.target.y }
};
})
var out = {"nodes":nodes, "links":linkIndices};
//set the value of the json again, which saves it
cm.setValue(JSON.stringify(out))
}
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.node .selected {
stroke: red;
}
.link {
stroke: #999;
}
.brush .extent {
fill-opacity: .1;
stroke: #fff;
shape-rendering: crispEdges;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment