Несколько экземпляров принудительного макета d3 на одной странице

Моя цель — использовать макет d3 force для отображения двух разных сетей, которые используют одни и те же узлы. Например, среди четырех человек вы можете определить социальную сеть и генеалогическую сеть; узлы будут одинаковыми (люди), но связи (отношения) могут быть другими. Несмотря на создание двух отдельных силовых макетов, двух отдельных холстов svg и попытки определить отдельные переменные, узлы совместно используют информацию о позициях x и y. Вот минимальный пример, в котором перетаскивание узлов в одной сети изменяет их позиции в другой сети: http://amath.colorado.edu/student/larremore/nodesSharingPositiond3< /а>

Ниже я разместил функцию, которая вызывается для создания одной из сетей, а код для создания другой очень похож, но во всех случаях использует разные имена переменных. Код с комментариями для создания обеих сетей можно найти на странице http://amath.colorado.edu/student/larremore/nodesSharingPositiond3/lib/minimal.js, а скрипт, используемый для определения переменных, можно найти в /driver/minimalScript.js ‹ — у меня недостаточно репутации, чтобы связать это напрямую . Мои извенения!

Где-то в том, как работает d3.force, позиционная информация является глобальной или выбирается глобально, или что-то в этом роде. Может ли кто-нибудь пролить свет на это? Меня интересует как решение для разделения позиционной информации, так и понимание того, как d3.force обрабатывает и обновляет вычисления позиции.

function makeYNet() {

// This populates the YactiveLinks variable with the proper YLinks. The goal is to be able to only display links whose value is above some threshold.
for (var i=0; i<YLinks.length; i++) {
    if (YLinks[i].link2 > thr) {
        YactiveLinks.push(YLinks[i]);
    }
}

// Add nodes and links to forceY
forceY
.nodes(YNodes)
.links(YactiveLinks);

// Draw lines
var Ylink = svgY.selectAll("line.link")
.data(YactiveLinks)
.enter()
.append("line")
.attr("class", "link")
.style("stroke-width", 2.0);

// Draw nodes
var Ynode = svgY.selectAll("circle.node")
.data(YNodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", radius)
.attr("high",0)
.attr("id", function(d,i) {
      return ("idy" + i);
      })
.style("fill", function(d) { return color(d.group1); })
.call(forceY.drag)
;

// Define tick 
forceY.on("tick", function() {
          Ylink
          .attr("x1", function(d) { return d.source.x; })
          .attr("y1", function(d) { return d.source.y; })
          .attr("x2", function(d) { return d.target.x; })
          .attr("y2", function(d) { return d.target.y; });

          Ynode.attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
          });

// Start 'er up
forceY.start();
}

person DBLarremore    schedule 30.11.2012    source источник
comment
Я пытаюсь сделать то же самое, что и вы: синхронизировать позиции узлов двух силовых макетов на одной странице. У меня есть два силовых макета, которые работают благодаря ответу Алекса Рейнольдса. Однако я не понимаю, где установить крючки для синхронизации положения. Поскольку ссылки на подход DBLaremore больше не работают, есть ли у кого-нибудь подсказка, с чего начать, или, может быть, ссылки на другой пример?   -  person tty56    schedule 17.06.2013


Ответы (2)


Я написал инструмент, который позволяет просматривать биологические регуляторные сети, показывая две панели SVG рядом друг с другом. Каждая панель содержит сеть принудительного макета, нарисованную API d3.js.

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

В моем случае я использовал _left и _right для каждого элемента панели, где элемент находится на левой или правой панели соответственно. Отслеживание требует много работы, но сетевой рендерер может нацеливать свои вызовы и события на правильный элемент и сеть.

В твоем случае:

.attr("id", function(d,i) {
      return ("idx" + i);
      })

Вы хотите заменить значение return каким-то уникальным адресом сети, с которой связан узел. Независимо от того, используете ли вы схему индексной нумерации или подход, основанный на суффиксах, как я, хитрость заключается в том, чтобы убедиться, что все id имена уникальны.

person Alex Reynolds    schedule 30.11.2012
comment
Пример вашего сайта хорош для подражания — выглядит великолепно! Однако я не могу решить свою проблему, изменив идентификаторы. Я пробовал использовать разные идентификаторы, а также не использовать идентификаторы, и в обоих случаях дисплеи все еще обновляют друг друга. Я проверил, что имена для левого (X) и правого (Y) различаются именами макета, узлов, ссылок и холстов svg. Любые другие предложения? Я не знаю, актуально ли это, но если вы перетащите и удержите одну сеть достаточно долго, другая остынет / зависнет, а затем больше не будет связана (очевидно). - person DBLarremore; 30.11.2012
comment
Я не знаю, что еще сработает. Я знаю, что ключом к успеху является обеспечение уникальности каждого отдельного элемента в каждой сети. Если есть повторяющиеся имена id, все ломается. Хотел бы я быть более полезным! - person Alex Reynolds; 01.12.2012
comment
Я заметил еще одну вещь, которая (возможно) дает подсказку: после некоторого количества времени/рассеивания макет заморозит узлы в их текущем местоположении. Если вы возьмете левый макет и переместите его, пока правый не застынет, вы больше не сможете косвенно перемещать правый макет. Затем, если вы возьмете правый макет, вы сможете использовать его, чтобы воздействовать только на левый, пока левый не зависнет. Для меня это означает, что, хотя положение макета может быть затронуто, это движение не влияет на таймер охлаждения. Узлы двигаются на обоих графиках, но только один регистрирует динамику. - person DBLarremore; 01.12.2012
comment
Я не опубликовал код, который делает это (пока), но я могу разделить два макета, используя асинхронный вызов json d3, d3.json(...) для загрузки данных. Кажется, это работает, хотя это не лучшее решение. Мне все еще любопытно, почему мой простой пример из предыдущего (и исправление @Alex-Reynolds) не сработал... - person DBLarremore; 04.12.2012

Javascript печально известен тем, что скрывает, является ли состояние общим или нет. Ваш минимальный пример больше недоступен, но когда я столкнулся с той же проблемой, я пришел к выводу, что я делал только поверхностные копии там, где мне нужны были полные клоны. (Я говорю о ссылках и узлах, которые D3 принимает на вход.)

Без настоящей копии результирующее поведение будет немного случайным, как вы обнаружили, в зависимости от того, решит ли код D3 изменить какие-либо общие данные «на месте» или нет. Как правило, D3 пытается не создавать копии из соображений производительности.

Вот почему обходной путь json-вызова работает: полностью упорядочив все данные, вы де-факто форсируете операцию клонирования.

person Peter S Magnusson    schedule 24.09.2017