Разделение макета дерева 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.