Skip to content

Instantly share code, notes, and snippets.

@pkerpedjiev
Last active May 25, 2023 04:59
Show Gist options
  • Select an option

  • Save pkerpedjiev/0389e39fad95e1cf29ce to your computer and use it in GitHub Desktop.

Select an option

Save pkerpedjiev/0389e39fad95e1cf29ce to your computer and use it in GitHub Desktop.

Revisions

  1. pkerpedjiev renamed this gist Feb 8, 2015. 1 changed file with 0 additions and 0 deletions.
  2. pkerpedjiev revised this gist Feb 8, 2015. 2 changed files with 1 addition and 1 deletion.
    1 change: 0 additions & 1 deletion SelectableForceDirectedGraph
    Original file line number Diff line number Diff line change
    @@ -1 +0,0 @@
    A variation of the force directed graph which allows selection of multiple nodes.
    1 change: 1 addition & 0 deletions SelectableForceDirectedGraph
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    .
  3. pkerpedjiev revised this gist Feb 8, 2015. 2 changed files with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions SelectableForceDirectedGraph
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    A variation of the force directed graph which allows selection of multiple nodes.
    Binary file modified thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  4. Peter Kerpedjiev revised this gist Feb 8, 2015. 2 changed files with 255 additions and 189 deletions.
    2 changes: 2 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -23,5 +23,7 @@

    </style>
    <body>
    <div align='center' id="d3_selectable_force_directed_graph"></div>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="plot.js"></script>
    <script>selectableForceDirectedGraph();</script>
    442 changes: 253 additions & 189 deletions plot.js
    Original file line number Diff line number Diff line change
    @@ -1,232 +1,296 @@
    var width = 960,
    height = 500,
    shiftKey, ctrlKey;

    var xScale = d3.scale.linear()
    .domain([0,width]).range([0,width]);
    var yScale = d3.scale.linear()
    .domain([0,height]).range([0, height]);

    var svg = d3.select("body")
    .attr("tabindex", 1)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
    .each(function() { this.focus(); })
    .append("svg")
    .attr("width", width)
    .attr("height", height);

    var zoomer = d3.behavior.zoom().
    scaleExtent([0.1,10]).
    x(xScale).
    y(yScale).
    on("zoomstart", zoomstart).
    on("zoom", redraw);

    function zoomstart() {
    node.each(function(d) {
    d.selected = false;
    d.previouslySelected = false;
    });
    node.classed("selected", false);
    }
    function selectableForceDirectedGraph() {
    var width = 960,

    height = 500,
    shiftKey, ctrlKey;

    var nodeGraph = null;
    var xScale = d3.scale.linear()
    .domain([0,width]).range([0,width]);
    var yScale = d3.scale.linear()
    .domain([0,height]).range([0, height]);

    var svg = d3.select("#d3_selectable_force_directed_graph")
    .attr("tabindex", 1)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
    .each(function() { this.focus(); })
    .append("svg")
    .attr("width", width)
    .attr("height", height);

    var zoomer = d3.behavior.zoom().
    scaleExtent([0.1,10]).
    x(xScale).
    y(yScale).
    on("zoomstart", zoomstart).
    on("zoom", redraw);

    function zoomstart() {
    node.each(function(d) {
    d.selected = false;
    d.previouslySelected = false;
    });
    node.classed("selected", false);
    }

    function redraw() {
    vis.attr("transform",
    "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }
    function redraw() {
    vis.attr("transform",
    "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }

    var brusher = d3.svg.brush()
    //.x(d3.scale.identity().domain([0, width]))
    //.y(d3.scale.identity().domain([0, height]))
    .x(xScale)
    .y(yScale)
    .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]);
    var brusher = d3.svg.brush()
    //.x(d3.scale.identity().domain([0, width]))
    //.y(d3.scale.identity().domain([0, height]))
    .x(xScale)
    .y(yScale)
    .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);
    });
    })
    .on("brushend", function() {
    d3.event.target.clear();
    d3.select(this).call(d3.event.target);
    })

    var svg_graph = svg.append('svg:g')
    .call(zoomer)
    //.call(brusher)
    var svg_graph = svg.append('svg:g')
    .call(zoomer)
    //.call(brusher)

    var rect = svg_graph.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'transparent')
    //.attr('opacity', 0.5)
    .attr('stroke', 'transparent')
    .attr('stroke-width', 1)
    //.attr("pointer-events", "all")
    .attr("id", "zrect")
    var rect = svg_graph.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'transparent')
    //.attr('opacity', 0.5)
    .attr('stroke', 'transparent')
    .attr('stroke-width', 1)
    //.attr("pointer-events", "all")
    .attr("id", "zrect")

    var brush = svg_graph.append("g")
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush");
    var brush = svg_graph.append("g")
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush");

    var vis = svg_graph.append("svg:g");
    var vis = svg_graph.append("svg:g");

    vis.attr('fill', 'red')
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('opacity', 0.5)
    .attr('id', 'vis')
    vis.attr('fill', 'red')
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('opacity', 0.5)
    .attr('id', 'vis')


    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);
    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    brush.select('.background').style('cursor', 'auto');
    brush.select('.background').style('cursor', 'auto');

    var link = vis.append("g")
    .attr("class", "link")
    .selectAll("line");
    var link = vis.append("g")
    .attr("class", "link")
    .selectAll("line");

    var node = vis.append("g")
    .attr("class", "node")
    .selectAll("circle");
    var node = vis.append("g")
    .attr("class", "node")
    .selectAll("circle");

    center_view = function() {
    // Center the view on the molecule(s) and scale it so that everything
    // fits in the window

    function dragended(d) {
    //d3.select(self).classed("dragging", false);
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed &= ~6; })
    if (nodeGraph === null)
    return;

    }
    d3.json("graph.json", function(error, graph) {
    var nodes = nodeGraph.nodes;

    graph.links.forEach(function(d) {
    d.source = graph.nodes[d.source];
    d.target = graph.nodes[d.target];
    });
    //no molecules, nothing to do
    if (nodes.length === 0)
    return;

    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; });


    var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .nodes(graph.nodes)
    .links(graph.links)
    .size([width, height])
    .start();

    function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    if (!d.selected && !shiftKey) {
    // if this node isn't selected, then we have to unselect every other node
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; });
    }
    // Get the bounding box
    min_x = d3.min(nodes.map(function(d) {return d.x;}));
    min_y = d3.min(nodes.map(function(d) {return d.y;}));

    d3.select(this).classed("selected", function(p) { d.previouslySelected = d.selected; return d.selected = true; });
    max_x = d3.max(nodes.map(function(d) {return d.x;}));
    max_y = d3.max(nodes.map(function(d) {return d.y;}));


    // The width and the height of the graph
    mol_width = max_x - min_x;
    mol_height = max_y - min_y;

    // how much larger the drawing area is than the width and the height
    width_ratio = width / mol_width;
    height_ratio = height / mol_height;

    // we need to fit it in both directions, so we scale according to
    // the direction in which we need to shrink the most
    min_ratio = Math.min(width_ratio, height_ratio) * 0.8;

    // the new dimensions of the molecule
    new_mol_width = mol_width * min_ratio;
    new_mol_height = mol_height * min_ratio;

    // translate so that it's in the center of the window
    x_trans = -(min_x) * min_ratio + (width - new_mol_width) / 2;
    y_trans = -(min_y) * min_ratio + (height - new_mol_height) / 2;


    // do the actual moving
    vis.attr("transform",
    "translate(" + [x_trans, y_trans] + ")" + " scale(" + min_ratio + ")");

    // tell the zoomer what we did so that next we zoom, it uses the
    // transformation we entered here
    zoomer.translate([x_trans, y_trans ]);
    zoomer.scale(min_ratio);

    };

    function dragended(d) {
    //d3.select(self).classed("dragging", false);
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    .each(function(d) { d.fixed &= ~6; })

    }

    function dragged(d) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;
    d3.json("graph.json", function(error, graph) {
    nodeGraph = graph;

    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })
    graph.links.forEach(function(d) {
    d.source = graph.nodes[d.source];
    d.target = graph.nodes[d.target];
    });

    force.resume();
    }
    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("dblclick", function(d) { d3.event.stopPropagation(); })
    .on("click", function(d) {
    if (d3.event.defaultPrevented) return;

    if (!shiftKey) {
    //if the shift key isn't down, unselect everything
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; })
    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; });


    var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .nodes(graph.nodes)
    .links(graph.links)
    .size([width, height])
    .start();

    function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    if (!d.selected && !shiftKey) {
    // if this node isn't selected, then we have to unselect every other node
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; });
    }

    d3.select(this).classed("selected", function(p) { d.previouslySelected = d.selected; return d.selected = true; });

    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    }

    // always select this node
    d3.select(this).classed("selected", d.selected = !d.previouslySelected);
    })
    function dragged(d) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;

    .on("mouseup", function(d) {
    //if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
    })
    .call(d3.behavior.drag()
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended));
    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })

    function tick() {
    link.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; });
    force.resume();
    }
    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("dblclick", function(d) { d3.event.stopPropagation(); })
    .on("click", function(d) {
    if (d3.event.defaultPrevented) return;

    if (!shiftKey) {
    //if the shift key isn't down, unselect everything
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; })
    }

    // always select this node
    d3.select(this).classed("selected", d.selected = !d.previouslySelected);
    })

    node.attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; });
    .on("mouseup", function(d) {
    //if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
    })
    .call(d3.behavior.drag()
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended));

    };
    function tick() {
    link.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; });

    force.on("tick", tick);
    node.attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; });

    });
    };

    force.on("tick", tick);

    function keydown() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;
    });


    function keydown() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    console.log('d3.event', d3.event)

    if (d3.event.keyCode == 67) { //the 'c' key
    center_view();
    }

    if (shiftKey) {
    svg_graph.call(zoomer)
    .on("mousedown.zoom", null)
    .on("touchstart.zoom", null)
    .on("touchmove.zoom", null)
    .on("touchend.zoom", null);
    if (shiftKey) {
    svg_graph.call(zoomer)
    .on("mousedown.zoom", null)
    .on("touchstart.zoom", null)
    .on("touchmove.zoom", null)
    .on("touchend.zoom", null);

    //svg_graph.on('zoom', null);
    vis.selectAll('g.gnode')
    .on('mousedown.drag', null);
    //svg_graph.on('zoom', null);
    vis.selectAll('g.gnode')
    .on('mousedown.drag', null);

    brush.select('.background').style('cursor', 'crosshair')
    brush.call(brusher);
    brush.select('.background').style('cursor', 'crosshair')
    brush.call(brusher);
    }
    }
    }

    function keyup() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;
    function keyup() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);
    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    brush.select('.background').style('cursor', 'auto')
    svg_graph.call(zoomer);
    brush.select('.background').style('cursor', 'auto')
    svg_graph.call(zoomer);
    }
    }
  5. Peter Kerpedjiev revised this gist Feb 7, 2015. 1 changed file with 0 additions and 0 deletions.
    Binary file modified thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  6. Peter Kerpedjiev revised this gist Feb 7, 2015. 2 changed files with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@
    }

    .node .selected {
    stroke: red;
    stroke: black;
    }

    .link {
    @@ -23,5 +23,5 @@

    </style>
    <body>
    <script src="d3.js"></script>
    <script src="plot.js"></script>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="plot.js"></script>
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  7. pkerpedjiev revised this gist Feb 7, 2015. 1 changed file with 7 additions and 2 deletions.
    9 changes: 7 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,8 @@
    This is an extension of Mike Bostock's [Draggable Network II](http://bl.ocks.org/mbostock/4566102) example, allowing one to drag multiple nodes in a force-directed graph. Nodes can be selected by dragging on the canvas and moved by dragging on the selected nodes.
    This is an extension of Mike Bostock's [Draggable Network II](http://bl.ocks.org/mbostock/4566102) example, allowing one to drag multiple nodes in a force-directed graph. Nodes can be selected by holding the shift key and either dragging on the canvas or clicking on specific nodes. The selection and dragging semantics aim to mirror those of most window managers:

    1. Shift clicking on a node toggles whether it is selected
    2. Clicking (without shift) on a node, selects it and deselects everything else.
    3. Shift dragging on the canvas toggles the selection status of the nodes enclosed within it.
    4. Dragging a set of selected nodes drags all of them.
    5. Clicking on the canvas de-selects everything.

    The key changes are the additional handlers for `dragstart` and `dragend` which mark the selected nodes as `fixed` when starting to drag and unmark them when the dragging is complete so that the force can be computed.
  8. pkerpedjiev revised this gist Feb 7, 2015. 1 changed file with 174 additions and 180 deletions.
    354 changes: 174 additions & 180 deletions plot.js
    Original file line number Diff line number Diff line change
    @@ -1,237 +1,231 @@
    var width = 960,
    height = 500,
    shiftKey, ctrlKey;
    height = 500,
    shiftKey, ctrlKey;

    var xScale = d3.scale.linear()
    .domain([0,width]).range([0,width]);
    var yScale = d3.scale.linear()
    .domain([0,height]).range([0, height]);
    var xScale = d3.scale.linear()
    .domain([0,width]).range([0,width]);
    var yScale = d3.scale.linear()
    .domain([0,height]).range([0, height]);

    var svg = d3.select("body")
    .attr("tabindex", 1)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
    .each(function() { this.focus(); })
    .append("svg")
    .attr("width", width)
    .attr("height", height);

    var zoomer = d3.behavior.zoom().
    .attr("tabindex", 1)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
    .each(function() { this.focus(); })
    .append("svg")
    .attr("width", width)
    .attr("height", height);

    var zoomer = d3.behavior.zoom().
    scaleExtent([0.1,10]).
    x(xScale).
    y(yScale).
    on("zoomstart", zoomstart).
    on("zoom", redraw);

    function zoomstart() {
    node.each(function(d) {
    d.selected = false;
    d.previouslySelected = false;
    })
    node.classed("selected", false);
    }
    function zoomstart() {
    node.each(function(d) {
    d.selected = false;
    d.previouslySelected = false;
    });
    node.classed("selected", false);
    }

    function redraw() {
    vis.attr("transform",
    "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }
    function redraw() {
    vis.attr("transform",
    "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }

    var brusher = d3.svg.brush()
    //.x(d3.scale.identity().domain([0, width]))
    //.y(d3.scale.identity().domain([0, height]))
    .x(xScale)
    .y(yScale)
    .on("brushstart", function(d) {
    console.log("brushstart");
    node.each(function(d) {
    console.log('d.previouslySelect:', d.previouslySelected);
    d.previouslySelected = shiftKey && d.selected; });
    })
    .on("brush", function() {
    var extent = d3.event.target.extent();
    console.log('extent:', extent.toString());
    console.log('brusher.x().range()', brusher.y().domain(), brusher.x().range());
    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);
    })

    var svg_graph = svg.append('svg:g')
    .call(zoomer)
    //.call(brusher)

    var rect = svg_graph.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'transparent')
    //.attr('opacity', 0.5)
    .attr('stroke', 'transparent')
    .attr('stroke-width', 1)
    //.attr("pointer-events", "all")
    .attr("id", "zrect")
    //.x(d3.scale.identity().domain([0, width]))
    //.y(d3.scale.identity().domain([0, height]))
    .x(xScale)
    .y(yScale)
    .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);
    })

    var svg_graph = svg.append('svg:g')
    .call(zoomer)
    //.call(brusher)

    var rect = svg_graph.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'transparent')
    //.attr('opacity', 0.5)
    .attr('stroke', 'transparent')
    .attr('stroke-width', 1)
    //.attr("pointer-events", "all")
    .attr("id", "zrect")

    var brush = svg_graph.append("g")
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush");
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush");

    var vis = svg_graph.append("svg:g");
    var vis = svg_graph.append("svg:g");

    vis.attr('fill', 'red')
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('opacity', 0.5)
    .attr('id', 'vis')
    vis.attr('fill', 'red')
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('opacity', 0.5)
    .attr('id', 'vis')


    console.log('ole');
    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    brush.select('.background').style('cursor', 'auto');

    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    var link = vis.append("g")
    .attr("class", "link")
    .selectAll("line");
    var link = vis.append("g")
    .attr("class", "link")
    .selectAll("line");

    var node = vis.append("g")
    .attr("class", "node")
    .selectAll("circle");
    .attr("class", "node")
    .selectAll("circle");



    function dragended(d) {
    //d3.select(self).classed("dragging", false);
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed &= ~6; })
    .each(function(d) { d.fixed &= ~6; })

    }
    d3.json("graph.json", function(error, graph) {

    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; });


    var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .nodes(graph.nodes)
    .links(graph.links)
    .size([width, height])
    .start();

    function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    console.log('dragstarted', d.previouslySelected, d.selected)

    d3.select(this).classed("selected", function(p) { d.previouslySelected = d.selected; return d.selected = true; });

    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    }
    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; });


    var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .nodes(graph.nodes)
    .links(graph.links)
    .size([width, height])
    .start();

    function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    if (!d.selected && !shiftKey) {
    // if this node isn't selected, then we have to unselect every other node
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; });
    }

    d3.select(this).classed("selected", function(p) { d.previouslySelected = d.selected; return d.selected = true; });

    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    }

    function dragged(d) {
    console.log('dragged')
    node.filter(function(d) { return d.selected; })
    function dragged(d) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;
    d.x += d3.event.dx;
    d.y += d3.event.dy;

    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })
    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })

    force.resume();
    }
    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("dblclick", function(d) { d3.event.stopPropagation(); })
    .on("click", function(d) {
    console.log('click')
    console.log('click', d.previouslySelected, d.selected)

    if (d3.event.defaultPrevented) return;
    console.log('click1')

    if (!shiftKey) {
    //if the shift key isn't down, unselect everything
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; })
    }

    // always select this node
    d3.select(this).classed("selected", d.selected = !d.previouslySelected);
    })

    .on("mouseup", function(d) {
    force.resume();
    }
    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("dblclick", function(d) { d3.event.stopPropagation(); })
    .on("click", function(d) {
    if (d3.event.defaultPrevented) return;

    if (!shiftKey) {
    //if the shift key isn't down, unselect everything
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; })
    }

    // always select this node
    d3.select(this).classed("selected", d.selected = !d.previouslySelected);
    })

    .on("mouseup", function(d) {
    //if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
    })
    .call(d3.behavior.drag()
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended));

    function tick() {
    link.attr("x1", function(d) { return d.source.x; })
    })
    .call(d3.behavior.drag()
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended));

    function tick() {
    link.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; });

    node.attr('cx', function(d) { return d.x; })
    node.attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; });

    };
    };

    force.on("tick", tick);
    force.on("tick", tick);

    });


    function keydown() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    if (shiftKey) {
    svg_graph.call(zoomer)
    .on("mousedown.zoom", null)
    .on("touchstart.zoom", null)
    .on("touchmove.zoom", null)
    .on("touchend.zoom", null);

    //svg_graph.on('zoom', null);
    vis.selectAll('g.gnode')
    .on('mousedown.drag', null);

    brush.select('.background').style('cursor', 'crosshair')
    brush.call(brusher);
    }
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    if (shiftKey) {
    svg_graph.call(zoomer)
    .on("mousedown.zoom", null)
    .on("touchstart.zoom", null)
    .on("touchmove.zoom", null)
    .on("touchend.zoom", null);

    //svg_graph.on('zoom', null);
    vis.selectAll('g.gnode')
    .on('mousedown.drag', null);

    brush.select('.background').style('cursor', 'crosshair')
    brush.call(brusher);
    }
    }

    function keyup() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);
    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    brush.select('.background').style('cursor', 'auto')
    svg_graph.call(zoomer);
  9. pkerpedjiev revised this gist Feb 7, 2015. 1 changed file with 1 addition and 243 deletions.
    244 changes: 1 addition & 243 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -24,246 +24,4 @@
    </style>
    <body>
    <script src="d3.js"></script>
    <script>

    var width = 960,
    height = 500,
    shiftKey, ctrlKey;

    var xScale = d3.scale.linear()
    .domain([0,width]).range([0,width]);
    var yScale = d3.scale.linear()
    .domain([0,height]).range([0, height]);

    var svg = d3.select("body")
    .attr("tabindex", 1)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
    .each(function() { this.focus(); })
    .append("svg")
    .attr("width", width)
    .attr("height", height);

    var zoomer = d3.behavior.zoom().
    scaleExtent([0.1,10]).
    x(xScale).
    y(yScale).
    on("zoomstart", zoomstart).
    on("zoom", redraw);

    function zoomstart() {
    node.each(function(d) {
    d.selected = false;
    d.previouslySelected = false;
    })
    node.classed("selected", false);
    }

    function redraw() {
    vis.attr("transform",
    "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }

    var brusher = d3.svg.brush()
    //.x(d3.scale.identity().domain([0, width]))
    //.y(d3.scale.identity().domain([0, height]))
    .x(xScale)
    .y(yScale)
    .on("brushstart", function(d) {
    console.log("brushstart");
    node.each(function(d) {
    console.log('d.previouslySelect:', d.previouslySelected);
    d.previouslySelected = shiftKey && d.selected; });
    })
    .on("brush", function() {
    var extent = d3.event.target.extent();
    console.log('extent:', extent.toString());
    console.log('brusher.x().range()', brusher.y().domain(), brusher.x().range());
    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);
    })

    var svg_graph = svg.append('svg:g')
    .call(zoomer)
    //.call(brusher)

    var rect = svg_graph.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'transparent')
    //.attr('opacity', 0.5)
    .attr('stroke', 'transparent')
    .attr('stroke-width', 1)
    //.attr("pointer-events", "all")
    .attr("id", "zrect")

    var brush = svg_graph.append("g")
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush");

    var vis = svg_graph.append("svg:g");

    vis.attr('fill', 'red')
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('opacity', 0.5)
    .attr('id', 'vis')


    console.log('ole');


    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    var link = vis.append("g")
    .attr("class", "link")
    .selectAll("line");

    var node = vis.append("g")
    .attr("class", "node")
    .selectAll("circle");


    function dragended(d) {
    //d3.select(self).classed("dragging", false);
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed &= ~6; })

    }
    d3.json("graph.json", function(error, graph) {

    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; });


    var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .nodes(graph.nodes)
    .links(graph.links)
    .size([width, height])
    .start();

    function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    console.log('dragstarted', d.previouslySelected, d.selected)

    d3.select(this).classed("selected", function(p) { d.previouslySelected = d.selected; return d.selected = true; });

    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    }

    function dragged(d) {
    console.log('dragged')
    node.filter(function(d) { return d.selected; })
    .each(function(d) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;

    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })

    force.resume();
    }
    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("dblclick", function(d) { d3.event.stopPropagation(); })
    .on("click", function(d) {
    console.log('click')
    console.log('click', d.previouslySelected, d.selected)

    if (d3.event.defaultPrevented) return;
    console.log('click1')

    if (!shiftKey) {
    //if the shift key isn't down, unselect everything
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; })
    }

    // always select this node
    d3.select(this).classed("selected", d.selected = !d.previouslySelected);
    })

    .on("mouseup", function(d) {
    //if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
    })
    .call(d3.behavior.drag()
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended));

    function tick() {
    link.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; });

    node.attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; });

    };

    force.on("tick", tick);

    });


    function keydown() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    if (shiftKey) {
    svg_graph.call(zoomer)
    .on("mousedown.zoom", null)
    .on("touchstart.zoom", null)
    .on("touchmove.zoom", null)
    .on("touchend.zoom", null);

    //svg_graph.on('zoom', null);
    vis.selectAll('g.gnode')
    .on('mousedown.drag', null);

    brush.select('.background').style('cursor', 'crosshair')
    brush.call(brusher);
    }
    }

    function keyup() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    brush.select('.background').style('cursor', 'auto')
    svg_graph.call(zoomer);
    }


    </script>
    <script src="plot.js"></script>
  10. pkerpedjiev revised this gist Feb 7, 2015. 1 changed file with 238 additions and 0 deletions.
    238 changes: 238 additions & 0 deletions plot.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,238 @@
    var width = 960,
    height = 500,
    shiftKey, ctrlKey;

    var xScale = d3.scale.linear()
    .domain([0,width]).range([0,width]);
    var yScale = d3.scale.linear()
    .domain([0,height]).range([0, height]);

    var svg = d3.select("body")
    .attr("tabindex", 1)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
    .each(function() { this.focus(); })
    .append("svg")
    .attr("width", width)
    .attr("height", height);

    var zoomer = d3.behavior.zoom().
    scaleExtent([0.1,10]).
    x(xScale).
    y(yScale).
    on("zoomstart", zoomstart).
    on("zoom", redraw);

    function zoomstart() {
    node.each(function(d) {
    d.selected = false;
    d.previouslySelected = false;
    })
    node.classed("selected", false);
    }

    function redraw() {
    vis.attr("transform",
    "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }

    var brusher = d3.svg.brush()
    //.x(d3.scale.identity().domain([0, width]))
    //.y(d3.scale.identity().domain([0, height]))
    .x(xScale)
    .y(yScale)
    .on("brushstart", function(d) {
    console.log("brushstart");
    node.each(function(d) {
    console.log('d.previouslySelect:', d.previouslySelected);
    d.previouslySelected = shiftKey && d.selected; });
    })
    .on("brush", function() {
    var extent = d3.event.target.extent();
    console.log('extent:', extent.toString());
    console.log('brusher.x().range()', brusher.y().domain(), brusher.x().range());
    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);
    })

    var svg_graph = svg.append('svg:g')
    .call(zoomer)
    //.call(brusher)

    var rect = svg_graph.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'transparent')
    //.attr('opacity', 0.5)
    .attr('stroke', 'transparent')
    .attr('stroke-width', 1)
    //.attr("pointer-events", "all")
    .attr("id", "zrect")

    var brush = svg_graph.append("g")
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush");

    var vis = svg_graph.append("svg:g");

    vis.attr('fill', 'red')
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('opacity', 0.5)
    .attr('id', 'vis')


    console.log('ole');


    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    var link = vis.append("g")
    .attr("class", "link")
    .selectAll("line");

    var node = vis.append("g")
    .attr("class", "node")
    .selectAll("circle");


    function dragended(d) {
    //d3.select(self).classed("dragging", false);
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed &= ~6; })

    }
    d3.json("graph.json", function(error, graph) {

    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; });


    var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .nodes(graph.nodes)
    .links(graph.links)
    .size([width, height])
    .start();

    function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    console.log('dragstarted', d.previouslySelected, d.selected)

    d3.select(this).classed("selected", function(p) { d.previouslySelected = d.selected; return d.selected = true; });

    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    }

    function dragged(d) {
    console.log('dragged')
    node.filter(function(d) { return d.selected; })
    .each(function(d) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;

    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })

    force.resume();
    }
    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("dblclick", function(d) { d3.event.stopPropagation(); })
    .on("click", function(d) {
    console.log('click')
    console.log('click', d.previouslySelected, d.selected)

    if (d3.event.defaultPrevented) return;
    console.log('click1')

    if (!shiftKey) {
    //if the shift key isn't down, unselect everything
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; })
    }

    // always select this node
    d3.select(this).classed("selected", d.selected = !d.previouslySelected);
    })

    .on("mouseup", function(d) {
    //if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
    })
    .call(d3.behavior.drag()
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended));

    function tick() {
    link.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; });

    node.attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; });

    };

    force.on("tick", tick);

    });


    function keydown() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    if (shiftKey) {
    svg_graph.call(zoomer)
    .on("mousedown.zoom", null)
    .on("touchstart.zoom", null)
    .on("touchmove.zoom", null)
    .on("touchend.zoom", null);

    //svg_graph.on('zoom', null);
    vis.selectAll('g.gnode')
    .on('mousedown.drag', null);

    brush.select('.background').style('cursor', 'crosshair')
    brush.call(brusher);
    }
    }

    function keyup() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    brush.select('.background').style('cursor', 'auto')
    svg_graph.call(zoomer);
    }
  11. pkerpedjiev renamed this gist Feb 7, 2015. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  12. pkerpedjiev revised this gist Feb 7, 2015. 2 changed files with 1330 additions and 1 deletion.
    1,330 changes: 1,330 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1330 @@
    {
    "nodes":[
    {
    "x":444,
    "y":275
    },
    {
    "x":378,
    "y":324
    },
    {
    "x":478,
    "y":278
    },
    {
    "x":471,
    "y":256
    },
    {
    "x":382,
    "y":269
    },
    {
    "x":371,
    "y":247
    },
    {
    "x":359,
    "y":276
    },
    {
    "x":364,
    "y":302
    },
    {
    "x":400,
    "y":330
    },
    {
    "x":388,
    "y":298
    },
    {
    "x":524,
    "y":296
    },
    {
    "x":570,
    "y":243
    },
    {
    "x":552,
    "y":159
    },
    {
    "x":502,
    "y":287
    },
    {
    "x":511,
    "y":313
    },
    {
    "x":513,
    "y":265
    },
    {
    "x":602,
    "y":132
    },
    {
    "x":610,
    "y":90
    },
    {
    "x":592,
    "y":91
    },
    {
    "x":575,
    "y":89
    },
    {
    "x":607,
    "y":73
    },
    {
    "x":591,
    "y":68
    },
    {
    "x":574,
    "y":73
    },
    {
    "x":589,
    "y":149
    },
    {
    "x":620,
    "y":205
    },
    {
    "x":621,
    "y":230
    },
    {
    "x":589,
    "y":234
    },
    {
    "x":602,
    "y":223
    },
    {
    "x":548,
    "y":188
    },
    {
    "x":532,
    "y":196
    },
    {
    "x":548,
    "y":114
    },
    {
    "x":575,
    "y":174
    },
    {
    "x":497,
    "y":250
    },
    {
    "x":576,
    "y":196
    },
    {
    "x":504,
    "y":201
    },
    {
    "x":494,
    "y":186
    },
    {
    "x":482,
    "y":199
    },
    {
    "x":505,
    "y":219
    },
    {
    "x":486,
    "y":216
    },
    {
    "x":590,
    "y":306
    },
    {
    "x":677,
    "y":169
    },
    {
    "x":657,
    "y":258
    },
    {
    "x":667,
    "y":205
    },
    {
    "x":552,
    "y":227
    },
    {
    "x":518,
    "y":173
    },
    {
    "x":473,
    "y":125
    },
    {
    "x":796,
    "y":260
    },
    {
    "x":731,
    "y":272
    },
    {
    "x":642,
    "y":288
    },
    {
    "x":576,
    "y":269
    },
    {
    "x":605,
    "y":187
    },
    {
    "x":559,
    "y":289
    },
    {
    "x":544,
    "y":356
    },
    {
    "x":505,
    "y":365
    },
    {
    "x":579,
    "y":289
    },
    {
    "x":619,
    "y":282
    },
    {
    "x":574,
    "y":329
    },
    {
    "x":664,
    "y":306
    },
    {
    "x":627,
    "y":304
    },
    {
    "x":643,
    "y":327
    },
    {
    "x":664,
    "y":348
    },
    {
    "x":665,
    "y":327
    },
    {
    "x":653,
    "y":317
    },
    {
    "x":650,
    "y":338
    },
    {
    "x":622,
    "y":321
    },
    {
    "x":633,
    "y":338
    },
    {
    "x":647,
    "y":357
    },
    {
    "x":718,
    "y":362
    },
    {
    "x":636,
    "y":240
    },
    {
    "x":640,
    "y":227
    },
    {
    "x":617,
    "y":249
    },
    {
    "x":631,
    "y":254
    },
    {
    "x":566,
    "y":213
    },
    {
    "x":713,
    "y":322
    },
    {
    "x":716,
    "y":298
    },
    {
    "x":666,
    "y":241
    },
    {
    "x":627,
    "y":355
    }
    ],
    "links":[
    {
    "source":1,
    "target":0
    },
    {
    "source":2,
    "target":0
    },
    {
    "source":3,
    "target":0
    },
    {
    "source":3,
    "target":2
    },
    {
    "source":4,
    "target":0
    },
    {
    "source":5,
    "target":0
    },
    {
    "source":6,
    "target":0
    },
    {
    "source":7,
    "target":0
    },
    {
    "source":8,
    "target":0
    },
    {
    "source":9,
    "target":0
    },
    {
    "source":11,
    "target":10
    },
    {
    "source":11,
    "target":3
    },
    {
    "source":11,
    "target":2
    },
    {
    "source":11,
    "target":0
    },
    {
    "source":12,
    "target":11
    },
    {
    "source":13,
    "target":11
    },
    {
    "source":14,
    "target":11
    },
    {
    "source":15,
    "target":11
    },
    {
    "source":17,
    "target":16
    },
    {
    "source":18,
    "target":16
    },
    {
    "source":18,
    "target":17
    },
    {
    "source":19,
    "target":16
    },
    {
    "source":19,
    "target":17
    },
    {
    "source":19,
    "target":18
    },
    {
    "source":20,
    "target":16
    },
    {
    "source":20,
    "target":17
    },
    {
    "source":20,
    "target":18
    },
    {
    "source":20,
    "target":19
    },
    {
    "source":21,
    "target":16
    },
    {
    "source":21,
    "target":17
    },
    {
    "source":21,
    "target":18
    },
    {
    "source":21,
    "target":19
    },
    {
    "source":21,
    "target":20
    },
    {
    "source":22,
    "target":16
    },
    {
    "source":22,
    "target":17
    },
    {
    "source":22,
    "target":18
    },
    {
    "source":22,
    "target":19
    },
    {
    "source":22,
    "target":20
    },
    {
    "source":22,
    "target":21
    },
    {
    "source":23,
    "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":24,
    "target":23
    },
    {
    "source":24,
    "target":11
    },
    {
    "source":25,
    "target":24
    },
    {
    "source":25,
    "target":23
    },
    {
    "source":25,
    "target":11
    },
    {
    "source":26,
    "target":24
    },
    {
    "source":26,
    "target":11
    },
    {
    "source":26,
    "target":16
    },
    {
    "source":26,
    "target":25
    },
    {
    "source":27,
    "target":11
    },
    {
    "source":27,
    "target":23
    },
    {
    "source":27,
    "target":25
    },
    {
    "source":27,
    "target":24
    },
    {
    "source":27,
    "target":26
    },
    {
    "source":28,
    "target":11
    },
    {
    "source":28,
    "target":27
    },
    {
    "source":29,
    "target":23
    },
    {
    "source":29,
    "target":27
    },
    {
    "source":29,
    "target":11
    },
    {
    "source":30,
    "target":23
    },
    {
    "source":31,
    "target":30
    },
    {
    "source":31,
    "target":11
    },
    {
    "source":31,
    "target":23
    },
    {
    "source":31,
    "target":27
    },
    {
    "source":32,
    "target":11
    },
    {
    "source":33,
    "target":11
    },
    {
    "source":33,
    "target":27
    },
    {
    "source":34,
    "target":11
    },
    {
    "source":34,
    "target":29
    },
    {
    "source":35,
    "target":11
    },
    {
    "source":35,
    "target":34
    },
    {
    "source":35,
    "target":29
    },
    {
    "source":36,
    "target":34
    },
    {
    "source":36,
    "target":35
    },
    {
    "source":36,
    "target":11
    },
    {
    "source":36,
    "target":29
    },
    {
    "source":37,
    "target":34
    },
    {
    "source":37,
    "target":35
    },
    {
    "source":37,
    "target":36
    },
    {
    "source":37,
    "target":11
    },
    {
    "source":37,
    "target":29
    },
    {
    "source":38,
    "target":34
    },
    {
    "source":38,
    "target":35
    },
    {
    "source":38,
    "target":36
    },
    {
    "source":38,
    "target":37
    },
    {
    "source":38,
    "target":11
    },
    {
    "source":38,
    "target":29
    },
    {
    "source":39,
    "target":25
    },
    {
    "source":40,
    "target":25
    },
    {
    "source":41,
    "target":24
    },
    {
    "source":41,
    "target":25
    },
    {
    "source":42,
    "target":41
    },
    {
    "source":42,
    "target":25
    },
    {
    "source":42,
    "target":24
    },
    {
    "source":43,
    "target":11
    },
    {
    "source":43,
    "target":26
    },
    {
    "source":43,
    "target":27
    },
    {
    "source":44,
    "target":28
    },
    {
    "source":44,
    "target":11
    },
    {
    "source":45,
    "target":28
    },
    {
    "source":47,
    "target":46
    },
    {
    "source":48,
    "target":47
    },
    {
    "source":48,
    "target":25
    },
    {
    "source":48,
    "target":27
    },
    {
    "source":48,
    "target":11
    },
    {
    "source":49,
    "target":26
    },
    {
    "source":49,
    "target":11
    },
    {
    "source":50,
    "target":49
    },
    {
    "source":50,
    "target":24
    },
    {
    "source":51,
    "target":49
    },
    {
    "source":51,
    "target":26
    },
    {
    "source":51,
    "target":11
    },
    {
    "source":52,
    "target":51
    },
    {
    "source":52,
    "target":39
    },
    {
    "source":53,
    "target":51
    },
    {
    "source":54,
    "target":51
    },
    {
    "source":54,
    "target":49
    },
    {
    "source":54,
    "target":26
    },
    {
    "source":55,
    "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":56,
    "target":49
    },
    {
    "source":56,
    "target":55
    },
    {
    "source":57,
    "target":55
    },
    {
    "source":57,
    "target":41
    },
    {
    "source":57,
    "target":48
    },
    {
    "source":58,
    "target":55
    },
    {
    "source":58,
    "target":48
    },
    {
    "source":58,
    "target":27
    },
    {
    "source":58,
    "target":57
    },
    {
    "source":58,
    "target":11
    },
    {
    "source":59,
    "target":58
    },
    {
    "source":59,
    "target":55
    },
    {
    "source":59,
    "target":48
    },
    {
    "source":59,
    "target":57
    },
    {
    "source":60,
    "target":48
    },
    {
    "source":60,
    "target":58
    },
    {
    "source":60,
    "target":59
    },
    {
    "source":61,
    "target":48
    },
    {
    "source":61,
    "target":58
    },
    {
    "source":61,
    "target":60
    },
    {
    "source":61,
    "target":59
    },
    {
    "source":61,
    "target":57
    },
    {
    "source":61,
    "target":55
    },
    {
    "source":62,
    "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":63,
    "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":64,
    "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":65,
    "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":66,
    "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":67,
    "target":57
    },
    {
    "source":68,
    "target":25
    },
    {
    "source":68,
    "target":11
    },
    {
    "source":68,
    "target":24
    },
    {
    "source":68,
    "target":27
    },
    {
    "source":68,
    "target":48
    },
    {
    "source":68,
    "target":41
    },
    {
    "source":69,
    "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":70,
    "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":71,
    "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":72,
    "target":26
    },
    {
    "source":72,
    "target":27
    },
    {
    "source":72,
    "target":11
    },
    {
    "source":73,
    "target":48
    },
    {
    "source":74,
    "target":48
    },
    {
    "source":74,
    "target":73
    },
    {
    "source":75,
    "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":76,
    "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
    }
    ]
    }
    1 change: 0 additions & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
  13. pkerpedjiev revised this gist Feb 7, 2015. 1 changed file with 207 additions and 57 deletions.
    264 changes: 207 additions & 57 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,40 @@
    var width = 400,

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>

    .node {
    stroke: #fff;
    stroke-width: 1.5px;
    }

    .node .selected {
    stroke: red;
    }

    .link {
    stroke: #999;
    }

    .brush .extent {
    fill-opacity: .1;
    stroke: #fff;
    shape-rendering: crispEdges;
    }

    </style>
    <body>
    <script src="d3.js"></script>
    <script>

    var width = 960,
    height = 500,
    shiftKey;
    shiftKey, ctrlKey;

    var xScale = d3.scale.linear()
    .domain([0,width]).range([0,width]);
    var yScale = d3.scale.linear()
    .domain([0,height]).range([0, height]);

    var svg = d3.select("body")
    .attr("tabindex", 1)
    @@ -11,19 +45,104 @@
    .attr("width", width)
    .attr("height", height);

    var link = svg.append("g")
    .attr("class", "link")
    .selectAll("line");
    var zoomer = d3.behavior.zoom().
    scaleExtent([0.1,10]).
    x(xScale).
    y(yScale).
    on("zoomstart", zoomstart).
    on("zoom", redraw);

    function zoomstart() {
    node.each(function(d) {
    d.selected = false;
    d.previouslySelected = false;
    })
    node.classed("selected", false);
    }

    var brush = svg.append("g")
    function redraw() {
    vis.attr("transform",
    "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }

    var brusher = d3.svg.brush()
    //.x(d3.scale.identity().domain([0, width]))
    //.y(d3.scale.identity().domain([0, height]))
    .x(xScale)
    .y(yScale)
    .on("brushstart", function(d) {
    console.log("brushstart");
    node.each(function(d) {
    console.log('d.previouslySelect:', d.previouslySelected);
    d.previouslySelected = shiftKey && d.selected; });
    })
    .on("brush", function() {
    var extent = d3.event.target.extent();
    console.log('extent:', extent.toString());
    console.log('brusher.x().range()', brusher.y().domain(), brusher.x().range());
    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);
    })

    var svg_graph = svg.append('svg:g')
    .call(zoomer)
    //.call(brusher)

    var rect = svg_graph.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'transparent')
    //.attr('opacity', 0.5)
    .attr('stroke', 'transparent')
    .attr('stroke-width', 1)
    //.attr("pointer-events", "all")
    .attr("id", "zrect")

    var brush = svg_graph.append("g")
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush");

    var node = svg.append("g")
    var vis = svg_graph.append("svg:g");

    vis.attr('fill', 'red')
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('opacity', 0.5)
    .attr('id', 'vis')


    console.log('ole');


    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    var link = vis.append("g")
    .attr("class", "link")
    .selectAll("line");

    var node = vis.append("g")
    .attr("class", "node")
    .selectAll("circle");

    var graph = {"nodes":[{"x":444,"y":275},{"x":378,"y":324},{"x":478,"y":278},{"x":471,"y":256},{"x":382,"y":269},{"x":371,"y":247},{"x":359,"y":276},{"x":364,"y":302},{"x":400,"y":330},{"x":388,"y":298},{"x":524,"y":296},{"x":570,"y":243},{"x":552,"y":159},{"x":502,"y":287},{"x":511,"y":313},{"x":513,"y":265},{"x":602,"y":132},{"x":610,"y":90},{"x":592,"y":91},{"x":575,"y":89},{"x":607,"y":73},{"x":591,"y":68},{"x":574,"y":73},{"x":589,"y":149},{"x":620,"y":205},{"x":621,"y":230},{"x":589,"y":234},{"x":602,"y":223},{"x":548,"y":188},{"x":532,"y":196},{"x":548,"y":114},{"x":575,"y":174},{"x":497,"y":250},{"x":576,"y":196},{"x":504,"y":201},{"x":494,"y":186},{"x":482,"y":199},{"x":505,"y":219},{"x":486,"y":216},{"x":590,"y":306},{"x":677,"y":169},{"x":657,"y":258},{"x":667,"y":205},{"x":552,"y":227},{"x":518,"y":173},{"x":473,"y":125},{"x":796,"y":260},{"x":731,"y":272},{"x":642,"y":288},{"x":576,"y":269},{"x":605,"y":187},{"x":559,"y":289},{"x":544,"y":356},{"x":505,"y":365},{"x":579,"y":289},{"x":619,"y":282},{"x":574,"y":329},{"x":664,"y":306},{"x":627,"y":304},{"x":643,"y":327},{"x":664,"y":348},{"x":665,"y":327},{"x":653,"y":317},{"x":650,"y":338},{"x":622,"y":321},{"x":633,"y":338},{"x":647,"y":357},{"x":718,"y":362},{"x":636,"y":240},{"x":640,"y":227},{"x":617,"y":249},{"x":631,"y":254},{"x":566,"y":213},{"x":713,"y":322},{"x":716,"y":298},{"x":666,"y":241},{"x":627,"y":355}],"links":[{"source":1,"target":0},{"source":2,"target":0},{"source":3,"target":0},{"source":3,"target":2},{"source":4,"target":0},{"source":5,"target":0},{"source":6,"target":0},{"source":7,"target":0},{"source":8,"target":0},{"source":9,"target":0},{"source":11,"target":10},{"source":11,"target":3},{"source":11,"target":2},{"source":11,"target":0},{"source":12,"target":11},{"source":13,"target":11},{"source":14,"target":11},{"source":15,"target":11},{"source":17,"target":16},{"source":18,"target":16},{"source":18,"target":17},{"source":19,"target":16},{"source":19,"target":17},{"source":19,"target":18},{"source":20,"target":16},{"source":20,"target":17},{"source":20,"target":18},{"source":20,"target":19},{"source":21,"target":16},{"source":21,"target":17},{"source":21,"target":18},{"source":21,"target":19},{"source":21,"target":20},{"source":22,"target":16},{"source":22,"target":17},{"source":22,"target":18},{"source":22,"target":19},{"source":22,"target":20},{"source":22,"target":21},{"source":23,"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":24,"target":23},{"source":24,"target":11},{"source":25,"target":24},{"source":25,"target":23},{"source":25,"target":11},{"source":26,"target":24},{"source":26,"target":11},{"source":26,"target":16},{"source":26,"target":25},{"source":27,"target":11},{"source":27,"target":23},{"source":27,"target":25},{"source":27,"target":24},{"source":27,"target":26},{"source":28,"target":11},{"source":28,"target":27},{"source":29,"target":23},{"source":29,"target":27},{"source":29,"target":11},{"source":30,"target":23},{"source":31,"target":30},{"source":31,"target":11},{"source":31,"target":23},{"source":31,"target":27},{"source":32,"target":11},{"source":33,"target":11},{"source":33,"target":27},{"source":34,"target":11},{"source":34,"target":29},{"source":35,"target":11},{"source":35,"target":34},{"source":35,"target":29},{"source":36,"target":34},{"source":36,"target":35},{"source":36,"target":11},{"source":36,"target":29},{"source":37,"target":34},{"source":37,"target":35},{"source":37,"target":36},{"source":37,"target":11},{"source":37,"target":29},{"source":38,"target":34},{"source":38,"target":35},{"source":38,"target":36},{"source":38,"target":37},{"source":38,"target":11},{"source":38,"target":29},{"source":39,"target":25},{"source":40,"target":25},{"source":41,"target":24},{"source":41,"target":25},{"source":42,"target":41},{"source":42,"target":25},{"source":42,"target":24},{"source":43,"target":11},{"source":43,"target":26},{"source":43,"target":27},{"source":44,"target":28},{"source":44,"target":11},{"source":45,"target":28},{"source":47,"target":46},{"source":48,"target":47},{"source":48,"target":25},{"source":48,"target":27},{"source":48,"target":11},{"source":49,"target":26},{"source":49,"target":11},{"source":50,"target":49},{"source":50,"target":24},{"source":51,"target":49},{"source":51,"target":26},{"source":51,"target":11},{"source":52,"target":51},{"source":52,"target":39},{"source":53,"target":51},{"source":54,"target":51},{"source":54,"target":49},{"source":54,"target":26},{"source":55,"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":56,"target":49},{"source":56,"target":55},{"source":57,"target":55},{"source":57,"target":41},{"source":57,"target":48},{"source":58,"target":55},{"source":58,"target":48},{"source":58,"target":27},{"source":58,"target":57},{"source":58,"target":11},{"source":59,"target":58},{"source":59,"target":55},{"source":59,"target":48},{"source":59,"target":57},{"source":60,"target":48},{"source":60,"target":58},{"source":60,"target":59},{"source":61,"target":48},{"source":61,"target":58},{"source":61,"target":60},{"source":61,"target":59},{"source":61,"target":57},{"source":61,"target":55},{"source":62,"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":63,"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":64,"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":65,"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":66,"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":67,"target":57},{"source":68,"target":25},{"source":68,"target":11},{"source":68,"target":24},{"source":68,"target":27},{"source":68,"target":48},{"source":68,"target":41},{"source":69,"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":70,"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":71,"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":72,"target":26},{"source":72,"target":27},{"source":72,"target":11},{"source":73,"target":48},{"source":74,"target":48},{"source":74,"target":73},{"source":75,"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":76,"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}]}

    function dragended(d) {
    //d3.select(self).classed("dragging", false);
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed &= ~6; })

    }
    d3.json("graph.json", function(error, graph) {

    graph.links.forEach(function(d) {
    d.source = graph.nodes[d.source];
    @@ -36,24 +155,6 @@
    .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);
    }));

    var force = d3.layout.force()
    .charge(-120)
    @@ -63,58 +164,107 @@
    .size([width, height])
    .start();

    node = node.data(graph.nodes).enter().append("circle")
    function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    console.log('dragstarted', d.previouslySelected, d.selected)

    d3.select(this).classed("selected", function(p) { d.previouslySelected = d.selected; return d.selected = true; });

    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    }

    function dragged(d) {
    console.log('dragged')
    node.filter(function(d) { return d.selected; })
    .each(function(d) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;

    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })

    force.resume();
    }
    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("dblclick", function(d) { d3.event.stopPropagation(); })
    .on("click", function(d) {
    console.log('click')
    console.log('click', d.previouslySelected, d.selected)

    if (d3.event.defaultPrevented) return;
    console.log('click1')

    if (!shiftKey) {
    //if the shift key isn't down, unselect everything
    node.classed("selected", function(p) { return p.selected = p.previouslySelected = false; })
    }

    // always select this node
    d3.select(this).classed("selected", d.selected = !d.previouslySelected);
    })

    .on("mouseup", function(d) {
    if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
    //if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
    })
    .call(d3.behavior.drag()
    .on("dragstart", function(d1) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    })
    .on("drag", function(d1) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;

    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })

    force.resume();
    })
    .on("dragend", function(d) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed &= ~6; })
    }));
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended));

    function tick() {
    link.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; });

    node.attr('cx', function(d) { return d.x; }).attr('cy', function(d) { return d.y; });

    node.attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; });

    };

    force.on("tick", tick);

    });


    function keydown() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    if (shiftKey) {
    svg_graph.call(zoomer)
    .on("mousedown.zoom", null)
    .on("touchstart.zoom", null)
    .on("touchmove.zoom", null)
    .on("touchend.zoom", null);

    //svg_graph.on('zoom', null);
    vis.selectAll('g.gnode')
    .on('mousedown.drag', null);

    brush.select('.background').style('cursor', 'crosshair')
    brush.call(brusher);
    }
    }

    function keyup() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    ctrlKey = d3.event.ctrlKey;

    brush.call(brusher)
    .on("mousedown.brush", null)
    .on("touchstart.brush", null)
    .on("touchmove.brush", null)
    .on("touchend.brush", null);

    brush.select('.background').style('cursor', 'auto')
    svg_graph.call(zoomer);
    }


    </script>
  14. pkerpedjiev revised this gist Jan 26, 2015. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,3 @@
    This is an extension of Mike Bostock's [Draggable Network II](http://bl.ocks.org/mbostock/4566102) example, allowing one to drag multiple nodes in a force-directed graph. The key changes are the additional handlers for `dragstart` and `dragend` which mark the selected nodes as `fixed` when starting to drag and unmark them when the dragging is complete so that the force can be computed.
    This is an extension of Mike Bostock's [Draggable Network II](http://bl.ocks.org/mbostock/4566102) example, allowing one to drag multiple nodes in a force-directed graph. Nodes can be selected by dragging on the canvas and moved by dragging on the selected nodes.

    The key changes are the additional handlers for `dragstart` and `dragend` which mark the selected nodes as `fixed` when starting to drag and unmark them when the dragging is complete so that the force can be computed.
  15. pkerpedjiev revised this gist Jan 26, 2015. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    This is an extension of Mike Bostock's [Draggable Network II](http://bl.ocks.org/mbostock/4566102) example, allowing one to drag multiple nodes in a force-directed graph. The key changes are the additional handlers for `dragstart` and `dragend` which mark the selected nodes as `fixed` when starting to drag and unmark them when the dragging is complete so that the force can be computed.
  16. pkerpedjiev renamed this gist Jan 26, 2015. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  17. pkerpedjiev revised this gist Jan 26, 2015. No changes.
  18. pkerpedjiev created this gist Jan 26, 2015.
    120 changes: 120 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,120 @@
    var width = 400,
    height = 500,
    shiftKey;

    var svg = d3.select("body")
    .attr("tabindex", 1)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
    .each(function() { this.focus(); })
    .append("svg")
    .attr("width", width)
    .attr("height", height);

    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 graph = {"nodes":[{"x":444,"y":275},{"x":378,"y":324},{"x":478,"y":278},{"x":471,"y":256},{"x":382,"y":269},{"x":371,"y":247},{"x":359,"y":276},{"x":364,"y":302},{"x":400,"y":330},{"x":388,"y":298},{"x":524,"y":296},{"x":570,"y":243},{"x":552,"y":159},{"x":502,"y":287},{"x":511,"y":313},{"x":513,"y":265},{"x":602,"y":132},{"x":610,"y":90},{"x":592,"y":91},{"x":575,"y":89},{"x":607,"y":73},{"x":591,"y":68},{"x":574,"y":73},{"x":589,"y":149},{"x":620,"y":205},{"x":621,"y":230},{"x":589,"y":234},{"x":602,"y":223},{"x":548,"y":188},{"x":532,"y":196},{"x":548,"y":114},{"x":575,"y":174},{"x":497,"y":250},{"x":576,"y":196},{"x":504,"y":201},{"x":494,"y":186},{"x":482,"y":199},{"x":505,"y":219},{"x":486,"y":216},{"x":590,"y":306},{"x":677,"y":169},{"x":657,"y":258},{"x":667,"y":205},{"x":552,"y":227},{"x":518,"y":173},{"x":473,"y":125},{"x":796,"y":260},{"x":731,"y":272},{"x":642,"y":288},{"x":576,"y":269},{"x":605,"y":187},{"x":559,"y":289},{"x":544,"y":356},{"x":505,"y":365},{"x":579,"y":289},{"x":619,"y":282},{"x":574,"y":329},{"x":664,"y":306},{"x":627,"y":304},{"x":643,"y":327},{"x":664,"y":348},{"x":665,"y":327},{"x":653,"y":317},{"x":650,"y":338},{"x":622,"y":321},{"x":633,"y":338},{"x":647,"y":357},{"x":718,"y":362},{"x":636,"y":240},{"x":640,"y":227},{"x":617,"y":249},{"x":631,"y":254},{"x":566,"y":213},{"x":713,"y":322},{"x":716,"y":298},{"x":666,"y":241},{"x":627,"y":355}],"links":[{"source":1,"target":0},{"source":2,"target":0},{"source":3,"target":0},{"source":3,"target":2},{"source":4,"target":0},{"source":5,"target":0},{"source":6,"target":0},{"source":7,"target":0},{"source":8,"target":0},{"source":9,"target":0},{"source":11,"target":10},{"source":11,"target":3},{"source":11,"target":2},{"source":11,"target":0},{"source":12,"target":11},{"source":13,"target":11},{"source":14,"target":11},{"source":15,"target":11},{"source":17,"target":16},{"source":18,"target":16},{"source":18,"target":17},{"source":19,"target":16},{"source":19,"target":17},{"source":19,"target":18},{"source":20,"target":16},{"source":20,"target":17},{"source":20,"target":18},{"source":20,"target":19},{"source":21,"target":16},{"source":21,"target":17},{"source":21,"target":18},{"source":21,"target":19},{"source":21,"target":20},{"source":22,"target":16},{"source":22,"target":17},{"source":22,"target":18},{"source":22,"target":19},{"source":22,"target":20},{"source":22,"target":21},{"source":23,"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":24,"target":23},{"source":24,"target":11},{"source":25,"target":24},{"source":25,"target":23},{"source":25,"target":11},{"source":26,"target":24},{"source":26,"target":11},{"source":26,"target":16},{"source":26,"target":25},{"source":27,"target":11},{"source":27,"target":23},{"source":27,"target":25},{"source":27,"target":24},{"source":27,"target":26},{"source":28,"target":11},{"source":28,"target":27},{"source":29,"target":23},{"source":29,"target":27},{"source":29,"target":11},{"source":30,"target":23},{"source":31,"target":30},{"source":31,"target":11},{"source":31,"target":23},{"source":31,"target":27},{"source":32,"target":11},{"source":33,"target":11},{"source":33,"target":27},{"source":34,"target":11},{"source":34,"target":29},{"source":35,"target":11},{"source":35,"target":34},{"source":35,"target":29},{"source":36,"target":34},{"source":36,"target":35},{"source":36,"target":11},{"source":36,"target":29},{"source":37,"target":34},{"source":37,"target":35},{"source":37,"target":36},{"source":37,"target":11},{"source":37,"target":29},{"source":38,"target":34},{"source":38,"target":35},{"source":38,"target":36},{"source":38,"target":37},{"source":38,"target":11},{"source":38,"target":29},{"source":39,"target":25},{"source":40,"target":25},{"source":41,"target":24},{"source":41,"target":25},{"source":42,"target":41},{"source":42,"target":25},{"source":42,"target":24},{"source":43,"target":11},{"source":43,"target":26},{"source":43,"target":27},{"source":44,"target":28},{"source":44,"target":11},{"source":45,"target":28},{"source":47,"target":46},{"source":48,"target":47},{"source":48,"target":25},{"source":48,"target":27},{"source":48,"target":11},{"source":49,"target":26},{"source":49,"target":11},{"source":50,"target":49},{"source":50,"target":24},{"source":51,"target":49},{"source":51,"target":26},{"source":51,"target":11},{"source":52,"target":51},{"source":52,"target":39},{"source":53,"target":51},{"source":54,"target":51},{"source":54,"target":49},{"source":54,"target":26},{"source":55,"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":56,"target":49},{"source":56,"target":55},{"source":57,"target":55},{"source":57,"target":41},{"source":57,"target":48},{"source":58,"target":55},{"source":58,"target":48},{"source":58,"target":27},{"source":58,"target":57},{"source":58,"target":11},{"source":59,"target":58},{"source":59,"target":55},{"source":59,"target":48},{"source":59,"target":57},{"source":60,"target":48},{"source":60,"target":58},{"source":60,"target":59},{"source":61,"target":48},{"source":61,"target":58},{"source":61,"target":60},{"source":61,"target":59},{"source":61,"target":57},{"source":61,"target":55},{"source":62,"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":63,"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":64,"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":65,"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":66,"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":67,"target":57},{"source":68,"target":25},{"source":68,"target":11},{"source":68,"target":24},{"source":68,"target":27},{"source":68,"target":48},{"source":68,"target":41},{"source":69,"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":70,"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":71,"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":72,"target":26},{"source":72,"target":27},{"source":72,"target":11},{"source":73,"target":48},{"source":74,"target":48},{"source":74,"target":73},{"source":75,"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":76,"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}]}

    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);
    }));

    var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .nodes(graph.nodes)
    .links(graph.links)
    .size([width, height])
    .start();

    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("dragstart", function(d1) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed |= 2; })
    })
    .on("drag", function(d1) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;

    d.px += d3.event.dx;
    d.py += d3.event.dy;
    })

    force.resume();
    })
    .on("dragend", function(d) {
    node.filter(function(d) { return d.selected; })
    .each(function(d) { d.fixed &= ~6; })
    }));

    function tick() {
    link.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; });

    node.attr('cx', function(d) { return d.x; }).attr('cy', function(d) { return d.y; });

    };

    force.on("tick", tick);


    function keydown() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    }

    function keyup() {
    shiftKey = d3.event.shiftKey || d3.event.metaKey;
    }