3

I am attempting to make a tree viewing web application. I am using the d3 tree layout to generate my trees. What I would like to be able to do is scale the size of the tree in either axis. In otherwords being able to scale the x and y axis of the viewer. The would effectively just increase either the vertical distance between nodes or increase the length of the branches.

I have been unable to accomplish this. I have attempted to change the nodeSize() of the tree layout, and then update which had no effect. I have also attempted to modify the x or y coordinate of each node which again had no effect. I have been completely unable to modify the physical layout of the tree once it has been rendered.

I have read and attempted the fixes in the following threads:

But they did not solve my problem.

I am implementing a TreeGenerator object, which handles all of the rendering of the SVG, using prototyped functions. I am going for a full screen feeling, so the TreeGenerator appends an SVG to a div with width: 100%; height: 100%. I should node that I have implemented both zooming and panning functionality.

Here is the constructor:

TreeGenerator = function(target, treeString) {

    // Set up everything here
    // colors
    this.nodeColor = "#3B6073";
    this.highlightColor = "#F22248";
    this.searchColor = "#0DFFC2";

    this.nodeHeight = 5;
    this.nodeLength = 20;

    this.zoomX = 1;
    this.zoomY = 5;

    this._winHeight = window.innerHeight;
    this._winWidth = window.innerWidth;

    //this.xScale = d3.scale.linear().domain([0,100]).range([0, this._winWidth])
    //this.yScale = d3.scale.linear().domain([0,100]).range([0, this._winHeight])

    // keep the matching results from last search
    this._searchResults = [];

    // path lengths
    this.maxPathLength = 0;
    this._loadSVG(target);
    this._tree(treeString);
}

And here are the two functions _loadSVG and _tree which are called from the constructor:

TreeGenerator.prototype._loadSVG = function(target) {

    var zoom = d3.behavior.zoom()
        //.size([this._winWidth, this._winHeight])
       .scaleExtent([this.zoomX,this.zoomY])
        //.x(this.xScale)
        //.y(this.yScale)
        //.center([height/2, width/2])
        .on("zoom", zoomed);


    var drag = d3.behavior.drag()
        .origin(function(d) { return d; })
        .on("dragstart", dragstarted)
        .on("drag", dragged)
        .on("dragend", dragended);

    this.svg = d3.select(target, ":first-child").append("svg")
      .append("g")
        //.attr("transform", "translate("+width / 2+","+height / 2+")")
        .call(zoom)
        .on("dblclick.zoom", null)

    var rect = this.svg.append("rect")
        .attr("width", "100%")
        .attr("height", "100%")
        .style("fill", "none")
        .style("pointer-events", "all");

    this.container = this.svg.append("g")

    // scope it for d3 funcs
    var container = this.container
    var self = this;

    function dottype(d) {
      d.x = +d.x;
      d.y = +d.y;
      return d;
    }

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

    function dragstarted(d) {
        d3.event.sourceEvent.stopPropagation();
        d3.select(this).classed("dragging", true);
    }

    function dragged(d) {
        d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
    }

    function dragended(d) {
        d3.select(this).classed("dragging", false);
    }
}

And _tree:

TreeGenerator.prototype._tree = function (treeString) {

    // needs an array for d3
    this.treeData = this.parseTree(treeString);

    this.root = this.treeData[0];

    // set up the layout
    this.tree = d3.layout.tree()
        .nodeSize([this.nodeHeight,this.nodeLength]);

    // set path dists
    this._setPathDist();
    this.yScale = d3.scale.linear().domain([0, this.maxPathLength]).range([0, 20]);
    this.xScale = d3.scale.linear().domain([1,100]).range([10, 30]);

    var self = this;
    update(this.root);

    function update(source) {
        var i = 0;

        // generator for paths from
        // node ---> node
        var horizontal = d3.svg.line().interpolate('step-before')
            .x(function (d) { return d.x; })
            .y(function (d) { return d.y; });

        // Compute the new tree layout.
        var nodes = self.tree.nodes(source).reverse()

        nodes.forEach(function (d) {
            if(!d.pathLength == 0) {
                d.y = d.y * self.yScale(d.pathLength);
            }
            else if(d.children != undefined) {
                d.y += 5;
            }
        });

        links = self.tree.links(nodes);

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

        // Enter the nodes.
        doubleclickTimer = false // dont ask

        var nodeEnter = node.enter().append("g")
            .attr("class", "node")
            .attr("id", function(d) { return "node-"+d.id.toString(); })
            .attr("transform", function(d) { 
                return "translate(" + d.y + "," + d.x + ")"; })
            .on("click", clicked);

        nodeEnter.append("circle")
            .attr("r", 1)
            .style("fill", self.nodeColor)

        nodeEnter.append("text")
            .attr("x", function(d) { 
                return d.children || d._children ? -1 : 1; })
            .attr("y", function(d) {
                return d.children == undefined ? 1 : -1;
            })
            .attr("text-anchor", function(d) { 
                return d.children || d._children ? "end" : "start"; })
            .text(function(d) { return d.name; })
            .style("fill-opacity", 1);

        // try and update
        //var nodeUpdate = node.update()
        //  .attr("x", function(d) { return d.x });

        // Declare the links
        var link = self.container.selectAll("path.link")
            .data(links, function(d) { return d.target.id; });

        // Enter the links.
        link.enter().insert("path", "g")
            .attr("class", "link")
            .attr("d", function(d) {
                return horizontal([
                    {y: d.source.x, x: d.source.y},
                    {y: d.target.x, x: d.target.y}
                ]);
            });

        function clicked(d) {

            // need the g group for highlight
            node = this;

            // if double click timer is active, this click is the double click
            if ( doubleclickTimer )
            {
                clearTimeout(doubleclickTimer);
                doubleclickTimer = false;
                collapse(d);
            }

            // otherwise, what to do after single click (double click has timed out)
            else {
                doubleclickTimer = setTimeout( function() {
                    doubleclickTimer = false;
                    highlight(node, d);
                }, 175);
            }
        }

        function highlight(node,d) {

            // we want to bold the text
            // and color the node
            self._unhighlightNode();

            // toggle if clicking again
            if(self._highlightedNode == node) {
                self._highlightedNode = undefined;
                self.selectedNode = undefined;
                return;
            }

            // set the new one
            var circle = d3.select(node).select("circle");
            var text = d3.select(node).select("text");

            circle.style("fill", self.highlightColor);
            text.style("font-size","4px");

            // update the pointer
            self._highlightedNode = node;
            self.selectedNode = d;
        }

        function collapse(d) {

        }
    }
};

I know the code is a bit messy, but it is just full of commented out things I did in an attempt to fix this issue. I appreciate any help that can be offered. Hopefully I included enough info.

Community
  • 1
  • 1
mbiokyle
  • 152
  • 9
  • You need to change the size of the tree layout (using its `.size()` function), then rerun it and then render the tree again. – Lars Kotthoff Jul 23 '15 at 16:29
  • Can you make a fiddle showing what you have? – Alexey Ayzin Jul 23 '15 at 16:56
  • Apologies to both of you on the delay here! But I have a JS fiddle set up here: https://jsfiddle.net/mbiokyle29/owLd2g3x/ I have most of my attempts to change the scale of the tree commented out in the code. Also notice that the tree does not load in the center of the scren. I have tried adding a transform to render it in the center, which works until the screen is zoomed or panned which causes the tree to jump back to the upper left position it currently loads in. – mbiokyle Aug 06 '15 at 19:07
  • 1
    I'd like too. Did you finally find a solution, mbiokyle ? – Stéphane Laurent Feb 25 '17 at 15:47

0 Answers0