Разделение макета дерева D3 между узлами с помощью NodeSize

Сейчас я пытаюсь отделить мои узлы прямоугольника, потому что они перекрываются, как показано на рисунке ниже:

enter image description here

Я взглянул и узнал, что D3 предлагает nodeSize и separation, но по какой-то причине это не сработало.

Я нашел этот пост в блоге, говорящий о проблеме, но он говорит

Свойство size не существует в узлах, поэтому это будет любое свойство, которое вы хотите контролировать их размер.

но, очевидно, существует метод nodeSize, поэтому я чувствую, что просто использую метод неправильно и/или сообщение в блоге устарело. Я хочу, чтобы мои узлы были размером прямоугольника и равномерно распределяли их, чтобы они не перекрывали друг друга. Кто-нибудь знает, как правильно использовать методы? Документация об этих методах не очень хорошо объясняется и не дает никакой разницы. Я также не мог найти много примеров, когда люди меняли nodeSize деревьев или нуждались в разделении для прямоугольных объектов (были некоторые примеры относительно круговых, но я чувствую, что слишком разные...)

Вот соответствующий код. Я попытаюсь подготовить JSFiddle.

var margin = {top: 20, right: 120, bottom: 20, left: 120},
    height = 960 - margin.right - margin.left,
    width = 800 - margin.top - margin.bottom,
    rectW = 70;
    rectH = 30;
    //bbox = NaN,
    maxTextLength = 0;

var i = 0,
    duration = 750,
    root;


//paths from each node drawn initially here
//changed to d.x, d.y
var diagonal = d3.svg.diagonal()
    .projection(function(d) { return [d.x+rectW/2, d.y+rectH/2];
    //.projection(function(d) { return [d.x+bbox.getBBox().width/2, d.y+bbox.getBBox().height/2]; 
});

var tree = d3.layout.tree()
    .nodeSize([30,70])
    .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2); })
    .size([width, height]);

var svg = d3.select("body")
            .append("svg")
              .attr("height","100%").attr("width","100%")
              .call(d3.behavior.zoom().on("zoom", redraw))
              .append("g")
                .attr("transform", "translate(" + margin.top + "," + margin.left + ")");

Ответ 1

ОБНОВЛЕНИЕ 05/04/2018: Насколько я понимаю, d3 сильно изменился (в лучшую сторону) и стал более модульным. Для тех, кто ищет этот ответ, он использовал гораздо более старую версию d3 (в частности, v3).

Многие результаты по-прежнему актуальны для пакета d3-hierarchy в разделах cluster.size() и cluster.nodeSize(), и я планирую потенциально обновить свой пример, чтобы использовать его. Для исторической справки я оставляю дно нетронутым.


Вот jsFiddle: http://jsfiddle.net/augburto/YMa2y/

ОБНОВЛЕНИЕ: Обновлен и переместить пример в Codepen. Пример все еще существует на jsFiddle, но Codepen, кажется, имеет более приятный редактор и позволяет легко переходить с ветвления. Я также постараюсь добавить пример непосредственно к этому ответу, как только уменьшу количество содержания в нем.

http://codepen.io/augbog/pen/LEXZKK

Обновление этого ответа. Я разговаривал с моим другом, и мы посмотрели на источник для size и nodeSize

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

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

Когда вы устанавливаете size для дерева, вы устанавливаете фиксированный размер, чтобы дерево соответствовало этой ширине и высоте. Когда вы устанавливаете nodeSize, дерево должно быть динамическим, чтобы оно сбрасывало размер дерева.

Когда я указал size после nodeSize, я в значительной степени переопределил то, что хотел, ха-ха...

Итог: если вы хотите, чтобы nodeSize работал, у вас не может быть фиксированного размера дерева. Будет установлен размер null. Не объявляйте size, если вы объявляете nodeSize.

ОБНОВЛЕНИЕ: D3.js фактически обновил документацию. Спасибо тому, кто сделал это, потому что теперь это стало намного понятнее!

Свойство nodeSize является эксклюзивным для tree.size; установка tree.nodeSize устанавливает для tree.size значение null.

Вот так выглядит мое дерево сейчас. Я также добавил функцию масштабирования и то, как центрировать текст внутри прямоangularьника.

Picture where nodeSize is set at width of 100 and height of 30

Ответ 2

Я не совсем понял принятый ответ до тех пор, пока не поработал сам, поэтому решил, что делюсь тем, что нашел...

Если вы используете .size(), а ваши узлы перекрываются, используйте .nodeSize() вместо

Как объяснено в принятом ответе, .size() устанавливает размер дерева, и поэтому в зависимости от расстояния между узлами кузена, вторых кузенов и т.д. они могут сжиматься вместе и перекрываться. Использование .nodeSize() просто говорит, что каждый node должен получить это много места, поэтому они никогда не будут перекрываться!

Код, который работал у меня, был

var nodeWidth = 300;
var nodeHeight = 75;
var horizontalSeparationBetweenNodes = 16;
var verticalSeparationBetweenNodes = 128;

var tree = d3.layout.tree()
    .nodeSize([nodeWidth + horizontalSeparationBetweenNodes, nodeHeight + verticalSeparationBetweenNodes])
    .separation(function(a, b) {
        return a.parent == b.parent ? 1 : 1.25;
    });

Без horizontalSeparationBetweenNodes и verticalSeparationBetweenNodes края узлов касались друг друга. Я также добавил это .separation(), чтобы уменьшить объем пространства между узлами кузена, так как мои узлы довольно широкие, и много места терялось.

Примечание: это для d3 v3, а не v4

Ответ 3

Во-первых, спасибо всем тем, кто опубликовал ранее, чистое золото. Я хотел бы добавить к этому сообщению те, которые могут быть связаны с проблемой смещения, связанной с горизонтальным рисунком.

Ключ: если вы переключитесь с .size() на .nodeSize() на горизонтальное дерево, вы заметите, что ваш корень node, кажется, прыгает/переориентируется, чтобы быть расположенным в (0,0). И в документации d3.js это на самом деле (см. https://github.com/d3/d3-hierarchy/blob/master/README.md#tree_nodeSize)

Однако для настройки вам просто нужно переориентировать свой viewBox. То есть, когда вы .append ваш svg, вам нужно явно указать ваш viewBox. Вот моя хакерская маленькая линия, где она работала для меня...

svg = d3.select("#tree").append("svg")
            .attr("width", width + margin.right + margin.left)
            .attr("height", height + 0 + 0)
            .attr("viewBox", "0 "+(-1*(height-margin.top-margin.bottom)/2)+" "+width+" "+height)
            .append("g")
            .attr("transform", "translate("
                  + margin.left + "," + 0 + ")");

Ответ 4

D3 теперь работает через Observable.

Чтобы установить nodeSize, найдите строку:

main.variable(observer("tree")).define("tree", ["d3","dx","dy"],
function(d3,dx,dy){return(
d3.tree()

И установите размер узла с добавленными константами:

main.variable(observer("tree")).define("tree", ["d3","dx","dy"],
function(d3,dx,dy){return(
d3.tree().nodeSize([dx + 10, dy + 10])

Или используйте функцию для установки значений относительно размера диаграммы, как обсуждалось в другом ответе, используя более старый подход D3.