Scotty Bollinger
Scotty Bollinger

Reputation: 2363

D3 Tree Layout - Distance between child nodes that have children

Trying to get the tree layout in D3 to render the child nodes with children closer together. Here is the code:

var margin = {
            top: 20,
            right: 120,
            bottom: 20,
            left: 120
    },
    width = 960 - margin.right - margin.left,
    height = 800 - margin.top - margin.bottom;

    var i = 0,
            duration = 750,
            rectW = 185,
            rectH = 45;

    var tree = d3.layout.tree()
        .nodeSize([200, 40]);

    var diagonal = d3.svg.diagonal()
        .projection(function (d) {
        return [d.x + rectW / 2, d.y + rectH / 2];
    });

    var svg = d3.select("#body").append("svg").attr("width", 1000).attr("height", 1000)
        .call(zm = d3.behavior.zoom().scaleExtent([0,1]).on("zoom", redraw))
        .append("g")
        .attr("transform", "translate(" + 30 + "," + 20 + ")");

    //necessary so that zoom knows where to zoom and unzoom from
    zm.translate([350, 20]);

    root.x0 = width / 2;
    root.y0 = 0;

    update(root);

    d3.select("#body").style("height", "800px");

    function update(source) {

        // Compute the new tree layout.
        var nodes = tree.nodes(root).reverse(),
            links = tree.links(nodes);


        // Normalize for fixed-depth.
        nodes.forEach(function (d) {
            d.y = (d.depth * 120);
        });

        // Update the nodes…
        var node = svg.selectAll("g.node")
            .data(nodes, function (d) {
            return d.id || (d.id = ++i);
        });

        // Enter any new nodes at the parent's previous position.
        var nodeEnter = node.enter().append("g")
            .attr("class", "node")
            .attr("transform", function (d) {
                return "translate(" + source.x0 + "," + source.y0 + ")";
            })
            .on("click", click);

        // Add rectangles to nodes
        nodeEnter.append("rect")
            .attr("width", function (d) {                   
                return rectW;
                //return d._children ? "lightsteelblue" : "#fff";
            })
            .attr("height", rectH)
            .attr("class", function (d) {
                return "rect-" + d.state;
            });

        // Add text to nodes
        nodeEnter.append("text")
            .attr("x", rectW / 2)
            .attr("y", rectH / 2)
            .attr("dy", ".35em")
            .attr("text-anchor", "middle")
            .text(function (d) {
                return d.name;
            });

        // Transition nodes to their new position.
        var nodeUpdate = node.transition()
            .duration(duration)
            .attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });

        nodeUpdate.select("rect")
            .attr("width", rectW)
            .attr("height", rectH)
            .attr("class", function (d) {
                return "rect-" + d.state;
            });

        nodeUpdate.select("text")
            .style("fill-opacity", 1);

        // Update the links…
        var link = svg.selectAll("path.link")
            .data(links, function (d) {
                return d.target.id;
            });

        // Enter any new links at the parent's previous position.
        link.enter().insert("path", "g")
            .attr("class", function (d) {
                return "link " + d.target.dest;
            })
            .attr("x", rectW / 2)
            .attr("d", function (d) {
                var o = {
                    x: source.x0,
                    y: source.y0
                };
                return diagonal({
                    source: o,
                    target: o
                });
            });

        // Transition links to their new position.
        link.transition()
                .duration(duration)
                .attr("d", diagonal);

        // Transition exiting nodes to the parent's new position.
        link.exit().transition()
            .duration(duration)
            .attr("d", function (d) {
                var o = {
                    x: source.x,
                    y: source.y
                };
                return diagonal({
                    source: o,
                    target: o
                });
            }).remove();


        // Update the link labels…
        var linkLabel = svg.selectAll("text.link-label")
            .data(links, function (d) {
                return d.target.id;
            });

        // Enter any new links at the parent's previous position.
        linkLabel.enter().insert("text", "path")
            .text(function (d) {
                return (d.target.state !== "open") ? null : "If " + d.target.dest;
            })
            .attr("class", function (d) {
                return "link-label " + d.target.dest;
            })
            .attr("x",  function (d) {
                return d.target.x + rectW / 2;
            })
            .attr("y", function (d) {
                return d.target.y + rectH * 2 - 30;
            })              
            .attr('text-anchor', 'middle')
            .style("fill-opacity", 0);;

        // Transition link labels
        linkLabel.transition()
            .delay(duration)
            .style("fill-opacity", 1);


        // Stash the old positions for transition.
        nodes.forEach(function (d) {
            d.x0 = d.x;
            d.y0 = d.y;
        });
    }

    // Toggle children on click.
    function click(d) {
        return false;
        if (d.children) {
            d._children = d.children;
            d.children = null;
        } else {
            d.children = d._children;
            d._children = null;
        }
        update(d);
    }

    //Redraw for zoom
    function redraw() {
        //console.log("here", d3.event.translate, d3.event.scale);
        svg.attr("transform",
            "translate(" + d3.event.translate + ")"
            + " scale(" + d3.event.scale + ")");
    }

Here is a jsbin of the above code. What I would like to see is the navy-colored "Node 1" and "Node 2" be closer together while preserving the distance between the nodes without children (grey nodes).

Is this possible and how would I do so?

Upvotes: 3

Views: 2954

Answers (1)

Scotty Bollinger
Scotty Bollinger

Reputation: 2363

I found the answer. It's the separation method. This got me what I was looking for:

tree.separation(function separation(a, b) {
            return a.parent == b.parent ? 1 : 1.5;
        });

Upvotes: 7

Related Questions