Выделите выбранный узел, его ссылки и его дочерние элементы в ориентированном графе D3 - PullRequest
69 голосов
/ 05 января 2012

Я работаю над форсированным графом в D3. Я хочу выделить узел mouseover'd, его ссылки и дочерние узлы, установив для всех остальных узлов и ссылок более низкую прозрачность.

В этом примере http://jsfiddle.net/xReHA/, Я могу затемнить все ссылки и узлы, затем исчезнуть в подключенных ссылках, но до сих пор я не смог элегантно исчезнуть в подключенных узлах которые являются потомками узла mouseover'd.

Это ключевая функция из кода:

function fade(opacity) {
    return function(d, i) {
        //fade all elements
        svg.selectAll("circle, line").style("opacity", opacity);

        var associated_links = svg.selectAll("line").filter(function(d) {
            return d.source.index == i || d.target.index == i;
        }).each(function(dLink, iLink) {
            //unfade links and nodes connected to the current node
            d3.select(this).style("opacity", 1);
            //THE FOLLOWING CAUSES: Uncaught TypeError: Cannot call method 'setProperty' of undefined
            d3.select(dLink.source).style("opacity", 1);
            d3.select(dLink.target).style("opacity", 1);
        });
    };
}

Я получаю ошибку Uncaught TypeError: Cannot call method 'setProperty' of undefined, когда пытаюсь установить непрозрачность для элемента, который я загрузил из source.target. Я подозреваю, что это неправильный способ загрузки этого узла в качестве объекта d3, но я не могу найти другой способ загрузить его, не повторяя итерацию по всем узлам снова, чтобы найти те, которые соответствуют цели или источнику ссылки. Чтобы обеспечить разумную производительность, я не хочу перебирать все узлы больше, чем необходимо.

Я взял пример исчезновения ссылок из http://mbostock.github.com/d3/ex/chord.html:

enter image description here

Однако это не показывает, как изменить подключенные дочерние узлы.

Любые хорошие предложения о том, как решить или улучшить это, будут яростно поддержаны:)

1 Ответ

88 голосов
/ 08 января 2012

Ошибка в том, что вы выбираете объекты данных (d.source и d.target), а не элементы DOM, связанные с этими объектами данных.

У вас есть работающая подсветка строк, но я бы, вероятно, объединил ваш код в одну итерацию, например:

 link.style("opacity", function(o) {
   return o.source === d || o.target === d ? 1 : opacity;
 });

Подсветить соседние узлы сложнее, потому что нужно знать соседей для каждого узла. Эту информацию не так просто определить с вашими текущими структурами данных, поскольку все, что у вас есть в виде массива узлов и массива ссылок. Забудьте DOM на секунду и спросите себя, как бы вы определили, являются ли два узла a и b соседями?

function neighboring(a, b) {
  // ???
}

Дорогой способ сделать это - перебрать все ссылки и посмотреть, есть ли ссылка, соединяющая a и b:

function neighboring(a, b) {
  return links.some(function(d) {
    return (d.source === a && d.target === b)
        || (d.source === b && d.target === a);
  });
}

(Предполагается, что ссылки не являются ненаправленными. Если вы хотите выделить только соседних по прямой связи, то исключите вторую половину ИЛИ.)

Более эффективный способ вычисления этого, если вам приходится делать это часто, - это иметь карту или матрицу, которая позволяет осуществлять поиск в постоянном времени, чтобы проверить, являются ли a и b соседями. Например:

var linkedByIndex = {};
links.forEach(function(d) {
  linkedByIndex[d.source.index + "," + d.target.index] = 1;
});

Теперь вы можете сказать:

function neighboring(a, b) {
  return linkedByIndex[a.index + "," + b.index];
}

И, таким образом, теперь вы можете перебирать узлы и корректно обновлять их непрозрачность:

node.style("opacity", function(o) {
  return neighboring(d, o) ? 1 : opacity;
});

(Вы также можете захотеть создать особый случай самой ссылки с указанием мыши, либо установив собственную ссылку для каждого узла в linkedByIndex, либо протестировав d непосредственно при вычислении стиля, либо с помощью! важный стиль CSS :hover.

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

...