All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.d3custom.d3.layout.js Maven / Gradle / Ivy

There is a newer version: 1.2.2.1-jre17
Show newest version
(function () {
    d3.layout = {};
// Implements hierarchical edge bundling using Holten's algorithm. For each
// input link, a path is computed that travels through the tree, up the parent
// hierarchy to the least common ancestor, and then back down to the destination
// node. Each path is simply an array of nodes.
    d3.layout.bundle = function () {
        return function (links) {
            var paths = [],
                i = -1,
                n = links.length;
            while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
            return paths;
        };
    };

    function d3_layout_bundlePath(link) {
        var start = link.source,
            end = link.target,
            lca = d3_layout_bundleLeastCommonAncestor(start, end),
            points = [start];
        while (start !== lca) {
            start = start.parent;
            points.push(start);
        }
        var k = points.length;
        while (end !== lca) {
            points.splice(k, 0, end);
            end = end.parent;
        }
        return points;
    }

    function d3_layout_bundleAncestors(node) {
        var ancestors = [],
            parent = node.parent;
        while (parent != null) {
            ancestors.push(node);
            node = parent;
            parent = parent.parent;
        }
        ancestors.push(node);
        return ancestors;
    }

    function d3_layout_bundleLeastCommonAncestor(a, b) {
        if (a === b) return a;
        var aNodes = d3_layout_bundleAncestors(a),
            bNodes = d3_layout_bundleAncestors(b),
            aNode = aNodes.pop(),
            bNode = bNodes.pop(),
            sharedNode = null;
        while (aNode === bNode) {
            sharedNode = aNode;
            aNode = aNodes.pop();
            bNode = bNodes.pop();
        }
        return sharedNode;
    }

    d3.layout.chord = function () {
        var chord = {},
            chords,
            groups,
            matrix,
            n,
            padding = 0,
            sortGroups,
            sortSubgroups,
            sortChords;

        function relayout() {
            var subgroups = {},
                groupSums = [],
                groupIndex = d3.range(n),
                subgroupIndex = [],
                k,
                x,
                x0,
                i,
                j;

            chords = [];
            groups = [];

            // Compute the sum.
            k = 0, i = -1;
            while (++i < n) {
                x = 0, j = -1;
                while (++j < n) {
                    x += matrix[i][j];
                }
                groupSums.push(x);
                subgroupIndex.push(d3.range(n));
                k += x;
            }

            // Sort groups…
            if (sortGroups) {
                groupIndex.sort(function (a, b) {
                    return sortGroups(groupSums[a], groupSums[b]);
                });
            }

            // Sort subgroups…
            if (sortSubgroups) {
                subgroupIndex.forEach(function (d, i) {
                    d.sort(function (a, b) {
                        return sortSubgroups(matrix[i][a], matrix[i][b]);
                    });
                });
            }

            // Convert the sum to scaling factor for [0, 2pi].
            // TODO Allow start and end angle to be specified.
            // TODO Allow padding to be specified as percentage?
            k = (2 * Math.PI - padding * n) / k;

            // Compute the start and end angle for each group and subgroup.
            x = 0, i = -1;
            while (++i < n) {
                x0 = x, j = -1;
                while (++j < n) {
                    var di = groupIndex[i],
                        dj = subgroupIndex[i][j],
                        v = matrix[di][dj];
                    subgroups[di + "-" + dj] = {
                        index: di,
                        subindex: dj,
                        startAngle: x,
                        endAngle: x += v * k,
                        value: v
                    };
                }
                groups.push({
                    index: di,
                    startAngle: x0,
                    endAngle: x,
                    value: (x - x0) / k
                });
                x += padding;
            }

            // Generate chords for each (non-empty) subgroup-subgroup link.
            i = -1;
            while (++i < n) {
                j = i - 1;
                while (++j < n) {
                    var source = subgroups[i + "-" + j],
                        target = subgroups[j + "-" + i];
                    if (source.value || target.value) {
                        chords.push(source.value < target.value
                            ? {source: target, target: source}
                            : {source: source, target: target});
                    }
                }
            }

            if (sortChords) resort();
        }

        function resort() {
            chords.sort(function (a, b) {
                return sortChords(a.target.value, b.target.value);
            });
        }

        chord.matrix = function (x) {
            if (!arguments.length) return matrix;
            n = (matrix = x) && matrix.length;
            chords = groups = null;
            return chord;
        };

        chord.padding = function (x) {
            if (!arguments.length) return padding;
            padding = x;
            chords = groups = null;
            return chord;
        };

        chord.sortGroups = function (x) {
            if (!arguments.length) return sortGroups;
            sortGroups = x;
            chords = groups = null;
            return chord;
        };

        chord.sortSubgroups = function (x) {
            if (!arguments.length) return sortSubgroups;
            sortSubgroups = x;
            chords = null;
            return chord;
        };

        chord.sortChords = function (x) {
            if (!arguments.length) return sortChords;
            sortChords = x;
            if (chords) resort();
            return chord;
        };

        chord.chords = function () {
            if (!chords) relayout();
            return chords;
        };

        chord.groups = function () {
            if (!groups) relayout();
            return groups;
        };

        return chord;
    };
// A rudimentary force layout using Gauss-Seidel.
    d3.layout.force = function () {
        var force = {},
            event = d3.dispatch("tick"),
            size = [1, 1],
            drag,
            alpha,
            friction = .9,
            linkDistance = d3_layout_forceLinkDistance,
            linkStrength = d3_layout_forceLinkStrength,
            charge = -30,
            gravity = .1,
            theta = .8,
            interval,
            nodes = [],
            links = [],
            distances,
            strengths,
            charges;

        function repulse(node) {
            return function (quad, x1, y1, x2, y2) {
                if (quad.point !== node) {
                    var dx = quad.cx - node.x,
                        dy = quad.cy - node.y,
                        dn = 1 / Math.sqrt(dx * dx + dy * dy);

                    /* Barnes-Hut criterion. */
                    if ((x2 - x1) * dn < theta) {
                        var k = quad.charge * dn * dn;
                        node.px -= dx * k;
                        node.py -= dy * k;
                        return true;
                    }

                    if (quad.point && isFinite(dn)) {
                        var k = quad.pointCharge * dn * dn;
                        node.px -= dx * k;
                        node.py -= dy * k;
                    }
                }
                return !quad.charge;
            };
        }

        function tick() {
            var n = nodes.length,
                m = links.length,
                q,
                i, // current index
                o, // current object
                s, // current source
                t, // current target
                l, // current distance
                k, // current force
                x, // x-distance
                y; // y-distance

            // gauss-seidel relaxation for links
            for (i = 0; i < m; ++i) {
                o = links[i];
                s = o.source;
                t = o.target;
                x = t.x - s.x;
                y = t.y - s.y;
                if (l = (x * x + y * y)) {
                    l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
                    x *= l;
                    y *= l;
                    t.x -= x * (k = s.weight / (t.weight + s.weight));
                    t.y -= y * k;
                    s.x += x * (k = 1 - k);
                    s.y += y * k;
                }
            }

            // apply gravity forces
            if (k = alpha * gravity) {
                x = size[0] / 2;
                y = size[1] / 2;
                i = -1;
                if (k) while (++i < n) {
                    o = nodes[i];
                    o.x += (x - o.x) * k;
                    o.y += (y - o.y) * k;
                }
            }

            // compute quadtree center of mass and apply charge forces
            if (charge) {
                d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
                i = -1;
                while (++i < n) {
                    if (!(o = nodes[i]).fixed) {
                        q.visit(repulse(o));
                    }
                }
            }

            // position verlet integration
            i = -1;
            while (++i < n) {
                o = nodes[i];
                if (o.fixed) {
                    o.x = o.px;
                    o.y = o.py;
                } else {
                    o.x -= (o.px - (o.px = o.x)) * friction;
                    o.y -= (o.py - (o.py = o.y)) * friction;
                }
            }

            event.tick.dispatch({type: "tick", alpha: alpha});

            // simulated annealing, basically
            return (alpha *= .99) < .005;
        }

        force.on = function (type, listener) {
            event[type].add(listener);
            return force;
        };

        force.nodes = function (x) {
            if (!arguments.length) return nodes;
            nodes = x;
            return force;
        };

        force.links = function (x) {
            if (!arguments.length) return links;
            links = x;
            return force;
        };

        force.size = function (x) {
            if (!arguments.length) return size;
            size = x;
            return force;
        };

        force.linkDistance = function (x) {
            if (!arguments.length) return linkDistance;
            linkDistance = d3.functor(x);
            return force;
        };

        // For backwards-compatibility.
        force.distance = force.linkDistance;

        force.linkStrength = function (x) {
            if (!arguments.length) return linkStrength;
            linkStrength = d3.functor(x);
            return force;
        };

        force.friction = function (x) {
            if (!arguments.length) return friction;
            friction = x;
            return force;
        };

        force.charge = function (x) {
            if (!arguments.length) return charge;
            charge = typeof x === "function" ? x : +x;
            return force;
        };

        force.gravity = function (x) {
            if (!arguments.length) return gravity;
            gravity = x;
            return force;
        };

        force.theta = function (x) {
            if (!arguments.length) return theta;
            theta = x;
            return force;
        };

        force.start = function () {
            var i,
                j,
                n = nodes.length,
                m = links.length,
                w = size[0],
                h = size[1],
                neighbors,
                o;

            for (i = 0; i < n; ++i) {
                (o = nodes[i]).index = i;
                o.weight = 0;
            }

            distances = [];
            strengths = [];
            for (i = 0; i < m; ++i) {
                o = links[i];
                if (typeof o.source == "number") o.source = nodes[o.source];
                if (typeof o.target == "number") o.target = nodes[o.target];
                distances[i] = linkDistance.call(this, o, i);
                strengths[i] = linkStrength.call(this, o, i);
                ++o.source.weight;
                ++o.target.weight;
            }

            for (i = 0; i < n; ++i) {
                o = nodes[i];
                if (isNaN(o.x)) o.x = position("x", w);
                if (isNaN(o.y)) o.y = position("y", h);
                if (isNaN(o.px)) o.px = o.x;
                if (isNaN(o.py)) o.py = o.y;
            }

            charges = [];
            if (typeof charge === "function") {
                for (i = 0; i < n; ++i) {
                    charges[i] = +charge.call(this, nodes[i], i);
                }
            } else {
                for (i = 0; i < n; ++i) {
                    charges[i] = charge;
                }
            }

            // initialize node position based on first neighbor
            function position(dimension, size) {
                var neighbors = neighbor(i),
                    j = -1,
                    m = neighbors.length,
                    x;
                while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x;
                return Math.random() * size;
            }

            // initialize neighbors lazily
            function neighbor() {
                if (!neighbors) {
                    neighbors = [];
                    for (j = 0; j < n; ++j) {
                        neighbors[j] = [];
                    }
                    for (j = 0; j < m; ++j) {
                        var o = links[j];
                        neighbors[o.source.index].push(o.target);
                        neighbors[o.target.index].push(o.source);
                    }
                }
                return neighbors[i];
            }

            return force.resume();
        };

        force.resume = function () {
            alpha = .1;
            d3.timer(tick);
            return force;
        };

        force.stop = function () {
            alpha = 0;
            return force;
        };

        // use `node.call(force.drag)` to make nodes draggable
        force.drag = function () {
            if (!drag) drag = d3.behavior.drag()
                .on("dragstart", dragstart)
                .on("drag", d3_layout_forceDrag)
                .on("dragend", d3_layout_forceDragEnd);

            this.on("mouseover.force", d3_layout_forceDragOver)
                .on("mouseout.force", d3_layout_forceDragOut)
                .call(drag);
        };

        function dragstart(d) {
            d3_layout_forceDragOver(d3_layout_forceDragNode = d);
            d3_layout_forceDragForce = force;
        }

        return force;
    };

    var d3_layout_forceDragForce,
        d3_layout_forceDragNode;

    function d3_layout_forceDragOver(d) {
        d.fixed |= 2;
    }

    function d3_layout_forceDragOut(d) {
        if (d !== d3_layout_forceDragNode) d.fixed &= 1;
    }

    function d3_layout_forceDragEnd() {
        d3_layout_forceDrag();
        d3_layout_forceDragNode.fixed &= 1;
        d3_layout_forceDragForce = d3_layout_forceDragNode = null;
    }

    function d3_layout_forceDrag() {
        d3_layout_forceDragNode.px += d3.event.dx;
        d3_layout_forceDragNode.py += d3.event.dy;
        d3_layout_forceDragForce.resume(); // restart annealing
    }

    function d3_layout_forceAccumulate(quad, alpha, charges) {
        var cx = 0,
            cy = 0;
        quad.charge = 0;
        if (!quad.leaf) {
            var nodes = quad.nodes,
                n = nodes.length,
                i = -1,
                c;
            while (++i < n) {
                c = nodes[i];
                if (c == null) continue;
                d3_layout_forceAccumulate(c, alpha, charges);
                quad.charge += c.charge;
                cx += c.charge * c.cx;
                cy += c.charge * c.cy;
            }
        }
        if (quad.point) {
            // jitter internal nodes that are coincident
            if (!quad.leaf) {
                quad.point.x += Math.random() - .5;
                quad.point.y += Math.random() - .5;
            }
            var k = alpha * charges[quad.point.index];
            quad.charge += quad.pointCharge = k;
            cx += k * quad.point.x;
            cy += k * quad.point.y;
        }
        quad.cx = cx / quad.charge;
        quad.cy = cy / quad.charge;
    }

    function d3_layout_forceLinkDistance(link) {
        return 20;
    }

    function d3_layout_forceLinkStrength(link) {
        return 1;
    }

    d3.layout.partition = function () {
        var hierarchy = d3.layout.hierarchy(),
            size = [1, 1]; // width, height

        function position(node, x, dx, dy) {
            var children = node.children;
            node.x = x;
            node.y = node.depth * dy;
            node.dx = dx;
            node.dy = dy;
            if (children && (n = children.length)) {
                var i = -1,
                    n,
                    c,
                    d;
                dx = node.value ? dx / node.value : 0;
                while (++i < n) {
                    position(c = children[i], x, d = c.value * dx, dy);
                    x += d;
                }
            }
        }

        function depth(node) {
            var children = node.children,
                d = 0;
            if (children && (n = children.length)) {
                var i = -1,
                    n;
                while (++i < n) d = Math.max(d, depth(children[i]));
            }
            return 1 + d;
        }

        function partition(d, i) {
            var nodes = hierarchy.call(this, d, i);
            position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
            return nodes;
        }

        partition.size = function (x) {
            if (!arguments.length) return size;
            size = x;
            return partition;
        };

        return d3_layout_hierarchyRebind(partition, hierarchy);
    };
    d3.layout.pie = function () {
        var value = Number,
            sort = null,
            startAngle = 0,
            endAngle = 2 * Math.PI;

        function pie(data, i) {

            // Compute the start angle.
            var a = +(typeof startAngle === "function"
                ? startAngle.apply(this, arguments)
                : startAngle);

            // Compute the angular range (end - start).
            var k = (typeof endAngle === "function"
                ? endAngle.apply(this, arguments)
                : endAngle) - startAngle;

            // Optionally sort the data.
            var index = d3.range(data.length);
            if (sort != null) index.sort(function (i, j) {
                return sort(data[i], data[j]);
            });

            // Compute the numeric values for each data element.
            var values = data.map(value);

            // Convert k into a scale factor from value to angle, using the sum.
            k /= values.reduce(function (p, d) {
                return p + d;
            }, 0);

            // Compute the arcs!
            var arcs = index.map(function (i) {
                return {
                    data: data[i],
                    value: d = values[i],
                    startAngle: a,
                    endAngle: a += d * k
                };
            });

            // Return the arcs in the original data's order.
            return data.map(function (d, i) {
                return arcs[index[i]];
            });
        }

        /**
         * Specifies the value function *x*, which returns a nonnegative numeric value
         * for each datum. The default value function is `Number`. The value function
         * is passed two arguments: the current datum and the current index.
         */
        pie.value = function (x) {
            if (!arguments.length) return value;
            value = x;
            return pie;
        };

        /**
         * Specifies a sort comparison operator *x*. The comparator is passed two data
         * elements from the data array, a and b; it returns a negative value if a is
         * less than b, a positive value if a is greater than b, and zero if a equals
         * b.
         */
        pie.sort = function (x) {
            if (!arguments.length) return sort;
            sort = x;
            return pie;
        };

        /**
         * Specifies the overall start angle of the pie chart. Defaults to 0. The
         * start angle can be specified either as a constant or as a function; in the
         * case of a function, it is evaluated once per array (as opposed to per
         * element).
         */
        pie.startAngle = function (x) {
            if (!arguments.length) return startAngle;
            startAngle = x;
            return pie;
        };

        /**
         * Specifies the overall end angle of the pie chart. Defaults to 2π. The
         * end angle can be specified either as a constant or as a function; in the
         * case of a function, it is evaluated once per array (as opposed to per
         * element).
         */
        pie.endAngle = function (x) {
            if (!arguments.length) return endAngle;
            endAngle = x;
            return pie;
        };

        return pie;
    };
// data is two-dimensional array of x,y; we populate y0
    d3.layout.stack = function () {
        var values = Object,
            order = d3_layout_stackOrders["default"],
            offset = d3_layout_stackOffsets["zero"],
            out = d3_layout_stackOut,
            x = d3_layout_stackX,
            y = d3_layout_stackY;

        function stack(data, index) {

            // Convert series to canonical two-dimensional representation.
            var series = data.map(function (d, i) {
                return values.call(stack, d, i);
            });

            // Convert each series to canonical [[x,y]] representation.
            var points = series.map(function (d, i) {
                return d.map(function (v, i) {
                    return [x.call(stack, v, i), y.call(stack, v, i)];
                });
            });

            // Compute the order of series, and permute them.
            var orders = order.call(stack, points, index);
            series = d3.permute(series, orders);
            points = d3.permute(points, orders);

            // Compute the baseline…
            var offsets = offset.call(stack, points, index);

            // And propagate it to other series.
            var n = series.length,
                m = series[0].length,
                i,
                j,
                o;
            for (j = 0; j < m; ++j) {
                out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
                for (i = 1; i < n; ++i) {
                    out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
                }
            }

            return data;
        }

        stack.values = function (x) {
            if (!arguments.length) return values;
            values = x;
            return stack;
        };

        stack.order = function (x) {
            if (!arguments.length) return order;
            order = typeof x === "function" ? x : d3_layout_stackOrders[x];
            return stack;
        };

        stack.offset = function (x) {
            if (!arguments.length) return offset;
            offset = typeof x === "function" ? x : d3_layout_stackOffsets[x];
            return stack;
        };

        stack.x = function (z) {
            if (!arguments.length) return x;
            x = z;
            return stack;
        };

        stack.y = function (z) {
            if (!arguments.length) return y;
            y = z;
            return stack;
        };

        stack.out = function (z) {
            if (!arguments.length) return out;
            out = z;
            return stack;
        };

        return stack;
    };

    function d3_layout_stackX(d) {
        return d.x;
    }

    function d3_layout_stackY(d) {
        return d.y;
    }

    function d3_layout_stackOut(d, y0, y) {
        d.y0 = y0;
        d.y = y;
    }

    var d3_layout_stackOrders = {

        "inside-out": function (data) {
            var n = data.length,
                i,
                j,
                max = data.map(d3_layout_stackMaxIndex),
                sums = data.map(d3_layout_stackReduceSum),
                index = d3.range(n).sort(function (a, b) {
                    return max[a] - max[b];
                }),
                top = 0,
                bottom = 0,
                tops = [],
                bottoms = [];
            for (i = 0; i < n; ++i) {
                j = index[i];
                if (top < bottom) {
                    top += sums[j];
                    tops.push(j);
                } else {
                    bottom += sums[j];
                    bottoms.push(j);
                }
            }
            return bottoms.reverse().concat(tops);
        },

        "reverse": function (data) {
            return d3.range(data.length).reverse();
        },

        "default": function (data) {
            return d3.range(data.length);
        }

    };

    var d3_layout_stackOffsets = {

        "silhouette": function (data) {
            var n = data.length,
                m = data[0].length,
                sums = [],
                max = 0,
                i,
                j,
                o,
                y0 = [];
            for (j = 0; j < m; ++j) {
                for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
                if (o > max) max = o;
                sums.push(o);
            }
            for (j = 0; j < m; ++j) {
                y0[j] = (max - sums[j]) / 2;
            }
            return y0;
        },

        "wiggle": function (data) {
            var n = data.length,
                x = data[0],
                m = x.length,
                max = 0,
                i,
                j,
                k,
                s1,
                s2,
                s3,
                dx,
                o,
                o0,
                y0 = [];
            y0[0] = o = o0 = 0;
            for (j = 1; j < m; ++j) {
                for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
                for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
                    for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
                        s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
                    }
                    s2 += s3 * data[i][j][1];
                }
                y0[j] = o -= s1 ? s2 / s1 * dx : 0;
                if (o < o0) o0 = o;
            }
            for (j = 0; j < m; ++j) y0[j] -= o0;
            return y0;
        },

        "expand": function (data) {
            var n = data.length,
                m = data[0].length,
                k = 1 / n,
                i,
                j,
                o,
                y0 = [];
            for (j = 0; j < m; ++j) {
                for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
                if (o) for (i = 0; i < n; i++) data[i][j][1] /= o;
                else for (i = 0; i < n; i++) data[i][j][1] = k;
            }
            for (j = 0; j < m; ++j) y0[j] = 0;
            return y0;
        },

        "zero": function (data) {
            var j = -1,
                m = data[0].length,
                y0 = [];
            while (++j < m) y0[j] = 0;
            return y0;
        }

    };

    function d3_layout_stackMaxIndex(array) {
        var i = 1,
            j = 0,
            v = array[0][1],
            k,
            n = array.length;
        for (; i < n; ++i) {
            if ((k = array[i][1]) > v) {
                j = i;
                v = k;
            }
        }
        return j;
    }

    function d3_layout_stackReduceSum(d) {
        return d.reduce(d3_layout_stackSum, 0);
    }

    function d3_layout_stackSum(p, d) {
        return p + d[1];
    }

    d3.layout.histogram = function () {
        var frequency = true,
            valuer = Number,
            ranger = d3_layout_histogramRange,
            binner = d3_layout_histogramBinSturges;

        function histogram(data, i) {
            var bins = [],
                values = data.map(valuer, this),
                range = ranger.call(this, values, i),
                thresholds = binner.call(this, range, values, i),
                bin,
                i = -1,
                n = values.length,
                m = thresholds.length - 1,
                k = frequency ? 1 : 1 / n,
                x;

            // Initialize the bins.
            while (++i < m) {
                bin = bins[i] = [];
                bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
                bin.y = 0;
            }

            // Fill the bins, ignoring values outside the range.
            i = -1;
            while (++i < n) {
                x = values[i];
                if ((x >= range[0]) && (x <= range[1])) {
                    bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
                    bin.y += k;
                    bin.push(data[i]);
                }
            }

            return bins;
        }

        // Specifies how to extract a value from the associated data. The default
        // value function is `Number`, which is equivalent to the identity function.
        histogram.value = function (x) {
            if (!arguments.length) return valuer;
            valuer = x;
            return histogram;
        };

        // Specifies the range of the histogram. Values outside the specified range
        // will be ignored. The argument `x` may be specified either as a two-element
        // array representing the minimum and maximum value of the range, or as a
        // function that returns the range given the array of values and the current
        // index `i`. The default range is the extent (minimum and maximum) of the
        // values.
        histogram.range = function (x) {
            if (!arguments.length) return ranger;
            ranger = d3.functor(x);
            return histogram;
        };

        // Specifies how to bin values in the histogram. The argument `x` may be
        // specified as a number, in which case the range of values will be split
        // uniformly into the given number of bins. Or, `x` may be an array of
        // threshold values, defining the bins; the specified array must contain the
        // rightmost (upper) value, thus specifying n + 1 values for n bins. Or, `x`
        // may be a function which is evaluated, being passed the range, the array of
        // values, and the current index `i`, returning an array of thresholds. The
        // default bin function will divide the values into uniform bins using
        // Sturges' formula.
        histogram.bins = function (x) {
            if (!arguments.length) return binner;
            binner = typeof x === "number"
                ? function (range) {
                    return d3_layout_histogramBinFixed(range, x);
                }
                : d3.functor(x);
            return histogram;
        };

        // Specifies whether the histogram's `y` value is a count (frequency) or a
        // probability (density). The default value is true.
        histogram.frequency = function (x) {
            if (!arguments.length) return frequency;
            frequency = !!x;
            return histogram;
        };

        return histogram;
    };

    function d3_layout_histogramBinSturges(range, values) {
        return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
    }

    function d3_layout_histogramBinFixed(range, n) {
        var x = -1,
            b = +range[0],
            m = (range[1] - b) / n,
            f = [];
        while (++x <= n) f[x] = m * x + b;
        return f;
    }

    function d3_layout_histogramRange(values) {
        return [d3.min(values), d3.max(values)];
    }

    d3.layout.hierarchy = function () {
        var sort = d3_layout_hierarchySort,
            children = d3_layout_hierarchyChildren,
            value = d3_layout_hierarchyValue;

        // Recursively compute the node depth and value.
        // Also converts the data representation into a standard hierarchy structure.
        function recurse(data, depth, nodes) {
            var childs = children.call(hierarchy, data, depth),
                node = d3_layout_hierarchyInline ? data : {data: data};
            node.depth = depth;
            nodes.push(node);
            if (childs && (n = childs.length)) {
                var i = -1,
                    n,
                    c = node.children = [],
                    v = 0,
                    j = depth + 1;
                while (++i < n) {
                    d = recurse(childs[i], j, nodes);
                    d.parent = node;
                    c.push(d);
                    v += d.value;
                }
                if (sort) c.sort(sort);
                if (value) node.value = v;
            } else if (value) {
                node.value = +value.call(hierarchy, data, depth) || 0;
            }
            return node;
        }

        // Recursively re-evaluates the node value.
        function revalue(node, depth) {
            var children = node.children,
                v = 0;
            if (children && (n = children.length)) {
                var i = -1,
                    n,
                    j = depth + 1;
                while (++i < n) v += revalue(children[i], j);
            } else if (value) {
                v = +value.call(hierarchy, d3_layout_hierarchyInline ? node : node.data, depth) || 0;
            }
            if (value) node.value = v;
            return v;
        }

        function hierarchy(d) {
            var nodes = [];
            recurse(d, 0, nodes);
            return nodes;
        }

        hierarchy.sort = function (x) {
            if (!arguments.length) return sort;
            sort = x;
            return hierarchy;
        };

        hierarchy.children = function (x) {
            if (!arguments.length) return children;
            children = x;
            return hierarchy;
        };

        hierarchy.value = function (x) {
            if (!arguments.length) return value;
            value = x;
            return hierarchy;
        };

        // Re-evaluates the `value` property for the specified hierarchy.
        hierarchy.revalue = function (root) {
            revalue(root, 0);
            return root;
        };

        return hierarchy;
    };

// A method assignment helper for hierarchy subclasses.
    function d3_layout_hierarchyRebind(object, hierarchy) {
        object.sort = d3.rebind(object, hierarchy.sort);
        object.children = d3.rebind(object, hierarchy.children);
        object.links = d3_layout_hierarchyLinks;
        object.value = d3.rebind(object, hierarchy.value);

        // If the new API is used, enabling inlining.
        object.nodes = function (d) {
            d3_layout_hierarchyInline = true;
            return (object.nodes = object)(d);
        };

        return object;
    }

    function d3_layout_hierarchyChildren(d) {
        return d.children;
    }

    function d3_layout_hierarchyValue(d) {
        return d.value;
    }

    function d3_layout_hierarchySort(a, b) {
        return b.value - a.value;
    }

// Returns an array source+target objects for the specified nodes.
    function d3_layout_hierarchyLinks(nodes) {
        return d3.merge(nodes.map(function (parent) {
            return (parent.children || []).map(function (child) {
                return {source: parent, target: child};
            });
        }));
    }

// For backwards-compatibility, don't enable inlining by default.
    var d3_layout_hierarchyInline = false;
    d3.layout.pack = function () {
        var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort),
            size = [1, 1];

        function pack(d, i) {
            var nodes = hierarchy.call(this, d, i),
                root = nodes[0];

            // Recursively compute the layout.
            root.x = 0;
            root.y = 0;
            d3_layout_packTree(root);

            // Scale the layout to fit the requested size.
            var w = size[0],
                h = size[1],
                k = 1 / Math.max(2 * root.r / w, 2 * root.r / h);
            d3_layout_packTransform(root, w / 2, h / 2, k);

            return nodes;
        }

        pack.size = function (x) {
            if (!arguments.length) return size;
            size = x;
            return pack;
        };

        return d3_layout_hierarchyRebind(pack, hierarchy);
    };

    function d3_layout_packSort(a, b) {
        return a.value - b.value;
    }

    function d3_layout_packInsert(a, b) {
        var c = a._pack_next;
        a._pack_next = b;
        b._pack_prev = a;
        b._pack_next = c;
        c._pack_prev = b;
    }

    function d3_layout_packSplice(a, b) {
        a._pack_next = b;
        b._pack_prev = a;
    }

    function d3_layout_packIntersects(a, b) {
        var dx = b.x - a.x,
            dy = b.y - a.y,
            dr = a.r + b.r;
        return (dr * dr - dx * dx - dy * dy) > .001; // within epsilon
    }

    function d3_layout_packCircle(nodes) {
        var xMin = Infinity,
            xMax = -Infinity,
            yMin = Infinity,
            yMax = -Infinity,
            n = nodes.length,
            a, b, c, j, k;

        function bound(node) {
            xMin = Math.min(node.x - node.r, xMin);
            xMax = Math.max(node.x + node.r, xMax);
            yMin = Math.min(node.y - node.r, yMin);
            yMax = Math.max(node.y + node.r, yMax);
        }

        // Create node links.
        nodes.forEach(d3_layout_packLink);

        // Create first node.
        a = nodes[0];
        a.x = -a.r;
        a.y = 0;
        bound(a);

        // Create second node.
        if (n > 1) {
            b = nodes[1];
            b.x = b.r;
            b.y = 0;
            bound(b);

            // Create third node and build chain.
            if (n > 2) {
                c = nodes[2];
                d3_layout_packPlace(a, b, c);
                bound(c);
                d3_layout_packInsert(a, c);
                a._pack_prev = c;
                d3_layout_packInsert(c, b);
                b = a._pack_next;

                // Now iterate through the rest.
                for (var i = 3; i < n; i++) {
                    d3_layout_packPlace(a, b, c = nodes[i]);

                    // Search for the closest intersection.
                    var isect = 0, s1 = 1, s2 = 1;
                    for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
                        if (d3_layout_packIntersects(j, c)) {
                            isect = 1;
                            break;
                        }
                    }
                    if (isect == 1) {
                        for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
                            if (d3_layout_packIntersects(k, c)) {
                                if (s2 < s1) {
                                    isect = -1;
                                    j = k;
                                }
                                break;
                            }
                        }
                    }

                    // Update node chain.
                    if (isect == 0) {
                        d3_layout_packInsert(a, c);
                        b = c;
                        bound(c);
                    } else if (isect > 0) {
                        d3_layout_packSplice(a, j);
                        b = j;
                        i--;
                    } else { // isect < 0
                        d3_layout_packSplice(j, b);
                        a = j;
                        i--;
                    }
                }
            }
        }

        // Re-center the circles and return the encompassing radius.
        var cx = (xMin + xMax) / 2,
            cy = (yMin + yMax) / 2,
            cr = 0;
        for (var i = 0; i < n; i++) {
            var node = nodes[i];
            node.x -= cx;
            node.y -= cy;
            cr = Math.max(cr, node.r + Math.sqrt(node.x * node.x + node.y * node.y));
        }

        // Remove node links.
        nodes.forEach(d3_layout_packUnlink);

        return cr;
    }

    function d3_layout_packLink(node) {
        node._pack_next = node._pack_prev = node;
    }

    function d3_layout_packUnlink(node) {
        delete node._pack_next;
        delete node._pack_prev;
    }

    function d3_layout_packTree(node) {
        var children = node.children;
        if (children && children.length) {
            children.forEach(d3_layout_packTree);
            node.r = d3_layout_packCircle(children);
        } else {
            node.r = Math.sqrt(node.value);
        }
    }

    function d3_layout_packTransform(node, x, y, k) {
        var children = node.children;
        node.x = (x += k * node.x);
        node.y = (y += k * node.y);
        node.r *= k;
        if (children) {
            var i = -1, n = children.length;
            while (++i < n) d3_layout_packTransform(children[i], x, y, k);
        }
    }

    function d3_layout_packPlace(a, b, c) {
        var db = a.r + c.r,
            dx = b.x - a.x,
            dy = b.y - a.y;
        if (db && (dx || dy)) {
            var da = b.r + c.r,
                dc = Math.sqrt(dx * dx + dy * dy),
                cos = Math.max(-1, Math.min(1, (db * db + dc * dc - da * da) / (2 * db * dc))),
                theta = Math.acos(cos),
                x = cos * (db /= dc),
                y = Math.sin(theta) * db;
            c.x = a.x + x * dx + y * dy;
            c.y = a.y + x * dy - y * dx;
        } else {
            c.x = a.x + db;
            c.y = a.y;
        }
    }

// Implements a hierarchical layout using the cluster (or dendogram) algorithm.
    d3.layout.cluster = function () {
        var hierarchy = d3.layout.hierarchy().sort(null).value(null),
            separation = d3_layout_treeSeparation,
            size = [1, 1]; // width, height

        function cluster(d, i) {
            var nodes = hierarchy.call(this, d, i),
                root = nodes[0],
                previousNode,
                x = 0,
                kx,
                ky;

            // First walk, computing the initial x & y values.
            d3_layout_treeVisitAfter(root, function (node) {
                var children = node.children;
                if (children && children.length) {
                    node.x = d3_layout_clusterX(children);
                    node.y = d3_layout_clusterY(children);
                } else {
                    node.x = previousNode ? x += separation(node, previousNode) : 0;
                    node.y = 0;
                    previousNode = node;
                }
            });

            // Compute the left-most, right-most, and depth-most nodes for extents.
            var left = d3_layout_clusterLeft(root),
                right = d3_layout_clusterRight(root),
                x0 = left.x - separation(left, right) / 2,
                x1 = right.x + separation(right, left) / 2;

            // Second walk, normalizing x & y to the desired size.
            d3_layout_treeVisitAfter(root, function (node) {
                node.x = (node.x - x0) / (x1 - x0) * size[0];
                node.y = (1 - node.y / root.y) * size[1];
            });

            return nodes;
        }

        cluster.separation = function (x) {
            if (!arguments.length) return separation;
            separation = x;
            return cluster;
        };

        cluster.size = function (x) {
            if (!arguments.length) return size;
            size = x;
            return cluster;
        };

        return d3_layout_hierarchyRebind(cluster, hierarchy);
    };

    function d3_layout_clusterY(children) {
        return 1 + d3.max(children, function (child) {
            return child.y;
        });
    }

    function d3_layout_clusterX(children) {
        return children.reduce(function (x, child) {
            return x + child.x;
        }, 0) / children.length;
    }

    function d3_layout_clusterLeft(node) {
        var children = node.children;
        return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
    }

    function d3_layout_clusterRight(node) {
        var children = node.children, n;
        return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
    }

// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
    d3.layout.tree = function () {
        var hierarchy = d3.layout.hierarchy().sort(null).value(null),
            separation = d3_layout_treeSeparation,
            size = [1, 1]; // width, height

        function tree(d, i) {
            var nodes = hierarchy.call(this, d, i),
                root = nodes[0];

            function firstWalk(node, previousSibling) {
                var children = node.children,
                    layout = node._tree;
                if (children && (n = children.length)) {
                    var n,
                        firstChild = children[0],
                        previousChild,
                        ancestor = firstChild,
                        child,
                        i = -1;
                    while (++i < n) {
                        child = children[i];
                        firstWalk(child, previousChild);
                        ancestor = apportion(child, previousChild, ancestor);
                        previousChild = child;
                    }
                    d3_layout_treeShift(node);
                    var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
                    if (previousSibling) {
                        layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
                        layout.mod = layout.prelim - midpoint;
                    } else {
                        layout.prelim = midpoint;
                    }
                } else {
                    if (previousSibling) {
                        layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
                    }
                }
            }

            function secondWalk(node, x) {
                node.x = node._tree.prelim + x;
                var children = node.children;
                if (children && (n = children.length)) {
                    var i = -1,
                        n;
                    x += node._tree.mod;
                    while (++i < n) {
                        secondWalk(children[i], x);
                    }
                }
            }

            function apportion(node, previousSibling, ancestor) {
                if (previousSibling) {
                    var vip = node,
                        vop = node,
                        vim = previousSibling,
                        vom = node.parent.children[0],
                        sip = vip._tree.mod,
                        sop = vop._tree.mod,
                        sim = vim._tree.mod,
                        som = vom._tree.mod,
                        shift;
                    while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
                        vom = d3_layout_treeLeft(vom);
                        vop = d3_layout_treeRight(vop);
                        vop._tree.ancestor = node;
                        shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
                        if (shift > 0) {
                            d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
                            sip += shift;
                            sop += shift;
                        }
                        sim += vim._tree.mod;
                        sip += vip._tree.mod;
                        som += vom._tree.mod;
                        sop += vop._tree.mod;
                    }
                    if (vim && !d3_layout_treeRight(vop)) {
                        vop._tree.thread = vim;
                        vop._tree.mod += sim - sop;
                    }
                    if (vip && !d3_layout_treeLeft(vom)) {
                        vom._tree.thread = vip;
                        vom._tree.mod += sip - som;
                        ancestor = node;
                    }
                }
                return ancestor;
            }

            // Initialize temporary layout variables.
            d3_layout_treeVisitAfter(root, function (node, previousSibling) {
                node._tree = {
                    ancestor: node,
                    prelim: 0,
                    mod: 0,
                    change: 0,
                    shift: 0,
                    number: previousSibling ? previousSibling._tree.number + 1 : 0
                };
            });

            // Compute the layout using Buchheim et al.'s algorithm.
            firstWalk(root);
            secondWalk(root, -root._tree.prelim);

            // Compute the left-most, right-most, and depth-most nodes for extents.
            var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost),
                right = d3_layout_treeSearch(root, d3_layout_treeRightmost),
                deep = d3_layout_treeSearch(root, d3_layout_treeDeepest),
                x0 = left.x - separation(left, right) / 2,
                x1 = right.x + separation(right, left) / 2,
                y1 = deep.depth || 1;

            // Clear temporary layout variables; transform x and y.
            d3_layout_treeVisitAfter(root, function (node) {
                node.x = (node.x - x0) / (x1 - x0) * size[0];
                node.y = node.depth / y1 * size[1];
                delete node._tree;
            });

            return nodes;
        }

        tree.separation = function (x) {
            if (!arguments.length) return separation;
            separation = x;
            return tree;
        };

        tree.size = function (x) {
            if (!arguments.length) return size;
            size = x;
            return tree;
        };

        return d3_layout_hierarchyRebind(tree, hierarchy);
    };

    function d3_layout_treeSeparation(a, b) {
        return a.parent == b.parent ? 1 : 2;
    }

// function d3_layout_treeSeparationRadial(a, b) {
//   return (a.parent == b.parent ? 1 : 2) / a.depth;
// }

    function d3_layout_treeLeft(node) {
        var children = node.children;
        return children && children.length ? children[0] : node._tree.thread;
    }

    function d3_layout_treeRight(node) {
        var children = node.children,
            n;
        return children && (n = children.length) ? children[n - 1] : node._tree.thread;
    }

    function d3_layout_treeSearch(node, compare) {
        var children = node.children;
        if (children && (n = children.length)) {
            var child,
                n,
                i = -1;
            while (++i < n) {
                if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) {
                    node = child;
                }
            }
        }
        return node;
    }

    function d3_layout_treeRightmost(a, b) {
        return a.x - b.x;
    }

    function d3_layout_treeLeftmost(a, b) {
        return b.x - a.x;
    }

    function d3_layout_treeDeepest(a, b) {
        return a.depth - b.depth;
    }

    function d3_layout_treeVisitAfter(node, callback) {
        function visit(node, previousSibling) {
            var children = node.children;
            if (children && (n = children.length)) {
                var child,
                    previousChild = null,
                    i = -1,
                    n;
                while (++i < n) {
                    child = children[i];
                    visit(child, previousChild);
                    previousChild = child;
                }
            }
            callback(node, previousSibling);
        }

        visit(node, null);
    }

    function d3_layout_treeShift(node) {
        var shift = 0,
            change = 0,
            children = node.children,
            i = children.length,
            child;
        while (--i >= 0) {
            child = children[i]._tree;
            child.prelim += shift;
            child.mod += shift;
            shift += child.shift + (change += child.change);
        }
    }

    function d3_layout_treeMove(ancestor, node, shift) {
        ancestor = ancestor._tree;
        node = node._tree;
        var change = shift / (node.number - ancestor.number);
        ancestor.change += change;
        node.change -= change;
        node.shift += shift;
        node.prelim += shift;
        node.mod += shift;
    }

    function d3_layout_treeAncestor(vim, node, ancestor) {
        return vim._tree.ancestor.parent == node.parent
            ? vim._tree.ancestor
            : ancestor;
    }

// Squarified Treemaps by Mark Bruls, Kees Huizing, and Jarke J. van Wijk
// Modified to support a target aspect ratio by Jeff Heer
    d3.layout.treemap = function () {
        var hierarchy = d3.layout.hierarchy(),
            round = Math.round,
            size = [1, 1], // width, height
            padding = null,
            pad = d3_layout_treemapPadNull,
            sticky = false,
            stickies,
            ratio = 0.5 * (1 + Math.sqrt(5)); // golden ratio

        // Compute the area for each child based on value & scale.
        function scale(children, k) {
            var i = -1,
                n = children.length,
                child,
                area;
            while (++i < n) {
                area = (child = children[i]).value * (k < 0 ? 0 : k);
                child.area = isNaN(area) || area <= 0 ? 0 : area;
            }
        }

        // Recursively arranges the specified node's children into squarified rows.
        function squarify(node) {
            var children = node.children;
            if (children && children.length) {
                var rect = pad(node),
                    row = [],
                    remaining = children.slice(), // copy-on-write
                    child,
                    best = Infinity, // the best row score so far
                    score, // the current row score
                    u = Math.min(rect.dx, rect.dy), // initial orientation
                    n;
                scale(remaining, rect.dx * rect.dy / node.value);
                row.area = 0;
                while ((n = remaining.length) > 0) {
                    row.push(child = remaining[n - 1]);
                    row.area += child.area;
                    if ((score = worst(row, u)) <= best) { // continue with this orientation
                        remaining.pop();
                        best = score;
                    } else { // abort, and try a different orientation
                        row.area -= row.pop().area;
                        position(row, u, rect, false);
                        u = Math.min(rect.dx, rect.dy);
                        row.length = row.area = 0;
                        best = Infinity;
                    }
                }
                if (row.length) {
                    position(row, u, rect, true);
                    row.length = row.area = 0;
                }
                children.forEach(squarify);
            }
        }

        // Recursively resizes the specified node's children into existing rows.
        // Preserves the existing layout!
        function stickify(node) {
            var children = node.children;
            if (children && children.length) {
                var rect = pad(node),
                    remaining = children.slice(), // copy-on-write
                    child,
                    row = [];
                scale(remaining, rect.dx * rect.dy / node.value);
                row.area = 0;
                while (child = remaining.pop()) {
                    row.push(child);
                    row.area += child.area;
                    if (child.z != null) {
                        position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
                        row.length = row.area = 0;
                    }
                }
                children.forEach(stickify);
            }
        }

        // Computes the score for the specified row, as the worst aspect ratio.
        function worst(row, u) {
            var s = row.area,
                r,
                rmax = 0,
                rmin = Infinity,
                i = -1,
                n = row.length;
            while (++i < n) {
                if (!(r = row[i].area)) continue;
                if (r < rmin) rmin = r;
                if (r > rmax) rmax = r;
            }
            s *= s;
            u *= u;
            return s
                ? Math.max((u * rmax * ratio) / s, s / (u * rmin * ratio))
                : Infinity;
        }

        // Positions the specified row of nodes. Modifies `rect`.
        function position(row, u, rect, flush) {
            var i = -1,
                n = row.length,
                x = rect.x,
                y = rect.y,
                v = u ? round(row.area / u) : 0,
                o;
            if (u == rect.dx) { // horizontal subdivision
                if (flush || v > rect.dy) v = v ? rect.dy : 0; // over+underflow
                while (++i < n) {
                    o = row[i];
                    o.x = x;
                    o.y = y;
                    o.dy = v;
                    x += o.dx = v ? round(o.area / v) : 0;
                }
                o.z = true;
                o.dx += rect.x + rect.dx - x; // rounding error
                rect.y += v;
                rect.dy -= v;
            } else { // vertical subdivision
                if (flush || v > rect.dx) v = v ? rect.dx : 0; // over+underflow
                while (++i < n) {
                    o = row[i];
                    o.x = x;
                    o.y = y;
                    o.dx = v;
                    y += o.dy = v ? round(o.area / v) : 0;
                }
                o.z = false;
                o.dy += rect.y + rect.dy - y; // rounding error
                rect.x += v;
                rect.dx -= v;
            }
        }

        function treemap(d) {
            var nodes = stickies || hierarchy(d),
                root = nodes[0];
            root.x = 0;
            root.y = 0;
            root.dx = size[0];
            root.dy = size[1];
            if (stickies) hierarchy.revalue(root);
            scale([root], root.dx * root.dy / root.value);
            (stickies ? stickify : squarify)(root);
            if (sticky) stickies = nodes;
            return nodes;
        }

        treemap.size = function (x) {
            if (!arguments.length) return size;
            size = x;
            return treemap;
        };

        treemap.padding = function (x) {
            if (!arguments.length) return padding;

            function padFunction(node) {
                var p = x.call(treemap, node, node.depth);
                return p == null
                    ? d3_layout_treemapPadNull(node)
                    : d3_layout_treemapPad(node, typeof p === "number" ? [p, p, p, p] : p);
            }

            function padConstant(node) {
                return d3_layout_treemapPad(node, x);
            }

            var type;
            pad = (padding = x) == null ? d3_layout_treemapPadNull
                : (type = typeof x) === "function" ? padFunction
                    : type === "number" ? (x = [x, x, x, x], padConstant)
                        : padConstant;
            return treemap;
        };

        treemap.round = function (x) {
            if (!arguments.length) return round != Number;
            round = x ? Math.round : Number;
            return treemap;
        };

        treemap.sticky = function (x) {
            if (!arguments.length) return sticky;
            sticky = x;
            stickies = null;
            return treemap;
        };

        treemap.ratio = function (x) {
            if (!arguments.length) return ratio;
            ratio = x;
            return treemap;
        };

        return d3_layout_hierarchyRebind(treemap, hierarchy);
    };

    function d3_layout_treemapPadNull(node) {
        return {x: node.x, y: node.y, dx: node.dx, dy: node.dy};
    }

    function d3_layout_treemapPad(node, padding) {
        var x = node.x + padding[3],
            y = node.y + padding[0],
            dx = node.dx - padding[1] - padding[3],
            dy = node.dy - padding[0] - padding[2];
        if (dx < 0) {
            x += dx / 2;
            dx = 0;
        }
        if (dy < 0) {
            y += dy / 2;
            dy = 0;
        }
        return {x: x, y: y, dx: dx, dy: dy};
    }
})();




© 2015 - 2025 Weber Informatics LLC | Privacy Policy