D3.js направленный граф, уменьшает пересечения кромок, заставляя края отталкивать друг друга

Итак, у меня есть страница, которая рисует диаграмму направленности силы, например, показанную здесь здесь.

И это прекрасно работает. Я использую JS из здесь, с несколькими настройками, чтобы немного расширить узлы.

Это более или менее единственные отличия:

d3.json("force.json", function(json) {
  var force = d3.layout.force()
      .gravity(0.1)
      .charge(-2000)
      .linkDistance(1)
      .linkStrength(0.1)
      .nodes(json.nodes)
      .links(json.links)
      .size([w, h])
      .start();

Там, где сокращение прочности связи, похоже, делает ссылки более похожими на пружины, поэтому она становится похожей на часто используемую технику Fruchterman и Reingold. Это работает достаточно хорошо, но только для довольно небольших графиков. С большими графиками количество переходов просто растет - как и следовало ожидать, но решение, на которое он приземляется, обычно далек от оптимального. Я не ищу способ получить оптимальное решение, я знаю, что это очень сложно. Я просто хотел бы, чтобы у него было грубое дополнение, которое пытается закрепить линии друг от друга, а также узлы.

Есть ли способ добавить отталкивание между ссылками, а также между узлами? Я не знаком с тем, как работает сила D3, и я не могу найти ничего что это возможно...

Ответ 1

К сожалению, ответа на ваш вопрос не существует.

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

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

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

Частично это объясняется тем, что выкладка графиков жесткая. В частности, минимизация пересечений кромок NP-hard, поэтому я подозреваю, что большинство дизайнеров макетов поразило эту проблему, несколько раз ударило головой о клавиатуру и сдался.

Если у кого-то есть хорошая библиотека для этого, пожалуйста, опубликуйте ее для остальных из нас:)

Ответ 2

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

http://en.wikipedia.org/wiki/Simulated_annealing

Начните с узлов с наименьшим количеством соединений и пошагите.

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

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

Ответ 3

Я следовал примеру Force Editor, и я увидел, что параметры charge и linkDistance решают проблему.

  ...
  .charge(-200)
  .linkDistance(50)
  ...

Скриншот:

enter image description here

Ответ 4

Я "решил" проблему с этим:

nodes[0].x = width / 2;
nodes[0].y = 100;
nodes[0].fixed = true;
force.on("tick", function(e) {

    var kx = .4 * e.alpha, ky = 1.4 * e.alpha;
    links.forEach(function(d, i) {
      d.target.x += (d.source.x - d.target.x) * kx;
      d.target.y += (d.source.y + 80 - d.target.y) * ky;
    });
    [...]
 }

http://mbostock.github.io/d3/talk/20110921/parent-foci.html

Это не совсем то, что мы хотели, но лучше, чем раньше. Importend - это то, что вы определяете "root" - Node и исправляете его.

nodes[0].fixed = true;

Он выглядит больше как дерево, но так яснее.