Skip to content

Instantly share code, notes, and snippets.

@w8r
Forked from 0thernet/README.md
Created December 14, 2019 22:19
Show Gist options
  • Save w8r/fffedc8034c1089ff42e25e8bac3ab00 to your computer and use it in GitHub Desktop.
Save w8r/fffedc8034c1089ff42e25e8bac3ab00 to your computer and use it in GitHub Desktop.

Revisions

  1. @0thernet 0thernet revised this gist Dec 24, 2012. 1 changed file with 0 additions and 0 deletions.
    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.
  2. @0thernet 0thernet created this gist Dec 24, 2012.
    3 changes: 3 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    Drag from an existing node to add a new node or link. Click to select/deselect nodes/links. Hit the DELETE key to remove the selected node or link. Drag to pan. Scroll to zoom.

    Built with [D3.js](http://mbostock.github.com/d3/).
    257 changes: 257 additions & 0 deletions force_view.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,257 @@

    var width = 960,
    height = 500,
    fill = d3.scale.category20();

    // mouse event vars
    var selected_node = null,
    selected_link = null,
    mousedown_link = null,
    mousedown_node = null,
    mouseup_node = null;

    // init svg
    var outer = d3.select("#chart")
    .append("svg:svg")
    .attr("width", width)
    .attr("height", height)
    .attr("pointer-events", "all");

    var vis = outer
    .append('svg:g')
    .call(d3.behavior.zoom().on("zoom", rescale))
    .on("dblclick.zoom", null)
    .append('svg:g')
    .on("mousemove", mousemove)
    .on("mousedown", mousedown)
    .on("mouseup", mouseup);

    vis.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'white');

    // init force layout
    var force = d3.layout.force()
    .size([width, height])
    .nodes([{}]) // initialize with a single node
    .linkDistance(50)
    .charge(-200)
    .on("tick", tick);


    // line displayed when dragging new nodes
    var drag_line = vis.append("line")
    .attr("class", "drag_line")
    .attr("x1", 0)
    .attr("y1", 0)
    .attr("x2", 0)
    .attr("y2", 0);

    // get layout properties
    var nodes = force.nodes(),
    links = force.links(),
    node = vis.selectAll(".node"),
    link = vis.selectAll(".link");

    // add keyboard callback
    d3.select(window)
    .on("keydown", keydown);

    redraw();

    // focus on svg
    // vis.node().focus();

    function mousedown() {
    if (!mousedown_node && !mousedown_link) {
    // allow panning if nothing is selected
    vis.call(d3.behavior.zoom().on("zoom"), rescale);
    return;
    }
    }

    function mousemove() {
    if (!mousedown_node) return;

    // update drag line
    drag_line
    .attr("x1", mousedown_node.x)
    .attr("y1", mousedown_node.y)
    .attr("x2", d3.svg.mouse(this)[0])
    .attr("y2", d3.svg.mouse(this)[1]);

    }

    function mouseup() {
    if (mousedown_node) {
    // hide drag line
    drag_line
    .attr("class", "drag_line_hidden")

    if (!mouseup_node) {
    // add node
    var point = d3.mouse(this),
    node = {x: point[0], y: point[1]},
    n = nodes.push(node);

    // select new node
    selected_node = node;
    selected_link = null;

    // add link to mousedown node
    links.push({source: mousedown_node, target: node});
    }

    redraw();
    }
    // clear mouse event vars
    resetMouseVars();
    }

    function resetMouseVars() {
    mousedown_node = null;
    mouseup_node = null;
    mousedown_link = null;
    }

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

    // rescale g
    function rescale() {
    trans=d3.event.translate;
    scale=d3.event.scale;

    vis.attr("transform",
    "translate(" + trans + ")"
    + " scale(" + scale + ")");
    }

    // redraw force layout
    function redraw() {

    link = link.data(links);

    link.enter().insert("line", ".node")
    .attr("class", "link")
    .on("mousedown",
    function(d) {
    mousedown_link = d;
    if (mousedown_link == selected_link) selected_link = null;
    else selected_link = mousedown_link;
    selected_node = null;
    redraw();
    })

    link.exit().remove();

    link
    .classed("link_selected", function(d) { return d === selected_link; });

    node = node.data(nodes);

    node.enter().insert("circle")
    .attr("class", "node")
    .attr("r", 5)
    .on("mousedown",
    function(d) {
    // disable zoom
    vis.call(d3.behavior.zoom().on("zoom"), null);

    mousedown_node = d;
    if (mousedown_node == selected_node) selected_node = null;
    else selected_node = mousedown_node;
    selected_link = null;

    // reposition drag line
    drag_line
    .attr("class", "link")
    .attr("x1", mousedown_node.x)
    .attr("y1", mousedown_node.y)
    .attr("x2", mousedown_node.x)
    .attr("y2", mousedown_node.y);

    redraw();
    })
    .on("mousedrag",
    function(d) {
    // redraw();
    })
    .on("mouseup",
    function(d) {
    if (mousedown_node) {
    mouseup_node = d;
    if (mouseup_node == mousedown_node) { resetMouseVars(); return; }

    // add link
    var link = {source: mousedown_node, target: mouseup_node};
    links.push(link);

    // select new link
    selected_link = link;
    selected_node = null;

    // enable zoom
    vis.call(d3.behavior.zoom().on("zoom"), rescale);
    redraw();
    }
    })
    .transition()
    .duration(750)
    .ease("elastic")
    .attr("r", 6.5);

    node.exit().transition()
    .attr("r", 0)
    .remove();

    node
    .classed("node_selected", function(d) { return d === selected_node; });



    if (d3.event) {
    // prevent browser's default behavior
    d3.event.preventDefault();
    }

    force.start();

    }

    function spliceLinksForNode(node) {
    toSplice = links.filter(
    function(l) {
    return (l.source === node) || (l.target === node); });
    toSplice.map(
    function(l) {
    links.splice(links.indexOf(l), 1); });
    }

    function keydown() {
    if (!selected_node && !selected_link) return;
    switch (d3.event.keyCode) {
    case 8: // backspace
    case 46: { // delete
    if (selected_node) {
    nodes.splice(nodes.indexOf(selected_node), 1);
    spliceLinksForNode(selected_node);
    }
    else if (selected_link) {
    links.splice(links.indexOf(selected_link), 1);
    }
    selected_link = null;
    selected_node = null;
    redraw();
    break;
    }
    }
    }
    56 changes: 56 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    <!DOCTYPE html>
    <meta charset="utf-8">
    <head>
    <title>Force Editor</title>
    <!-- <script src="d3.v2.min.js"></script> -->
    <script src="http://d3js.org/d3.v2.min.js"></script>
    <!-- <script src="jquery-1.8.3.min.js"></script> -->
    <style>

    body {
    font: 13px sans-serif;
    position: relative;
    width: 960px;
    height: 500px;
    }

    .node {
    fill: #000;
    cursor: crosshair;
    }

    .node_selected {
    fill: #ff7f0e;
    stroke: #ff7f0e;
    }

    .drag_line {
    stroke: #999;
    stroke-width: 5;
    pointer-events: none;
    }

    .drag_line_hidden {
    stroke: #999;
    stroke-width: 0;
    pointer-events: none;
    }

    .link {
    stroke: #999;
    stroke-width: 5;
    cursor: crosshair;
    }

    .link_selected {
    stroke: #ff7f0e;
    }

    </style>
    <head>
    <body>
    <div id="chart">
    </div>
    <script src="force_view.js"></script>
    </body>
    </html>