
Сел писать статью и поймал себя на одной очень грустной мысли о том, что почти все свежие идеи и технологии придуманы не нами. Часто даже корректный перевод подобрать сложно. Думаю что Force Layout Diagramm (далее по тексту FLD) - можно обозначить как граф, в котором задана система сил притяжения и отталкивания, действующих между вершинами и ребрами графа. При этом узлы такого графа находят взаимное положение, в котором система имеет локальный минимум энергии (т.е. силы "притяжения" узлов уравновешиваются силами "отталкивания" связей).
FLD основан на методе численного интегрирования Верле (Verlet integration) разработанного для вычисления траекторий частиц в моделях молекулярной динамики. Специфика метода Верле - обсчет положения некоторой совокупности частиц, объединенных гибкими связями - нашло так же применение и в компьютерной графике для визуализации сетевых структур. В библиотеке D3.js используется фактор псевдо-гравитации, заставляющий граф располагаться в центре видимой области и используя фактор отталкивания между узлами располагать узлы на некотором фиксированном расстоянии между ними.
Коротко рассмотрим основные API DFL
#d3.layout.force()
Создает новый граф с установками по умолчанию: размер 1x1, сила связи - 1, сила трения 0.9, дистанция 20, сила заряда -30, сила гравитации 0.1, и тета-параметр 0.8. По умолчанию описание узлов и связей между ними представляет собой пустой массив. Когда диаграмма создается параметр alfa принимает значение 0.1.
Минимальный паттерн для старта диаграммы включает в себя установку всех конфигурационных параметров и вызов метода start()
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([w, h])
.start();
Обратите внимание, что D3 (как и для других диаграмм) не производит визуализацию средствами непосредственно библиотеки. Узлы отображаются в виде окружностей как SVG-элементы, а связи как линии (так же посредством SVG). Но при необходимости можно отображать узлы в виде символов или произвольные изображений.
#force.size([size])
Если количество элементов определено, то переменная size определяет двухмерный числовой массив, представляющий параметры x и y каждого узла. Если переменная size не определена, то возвращается текущий размер массива (по умолчанию 1x1). Массив size определяет два аспекта диаграммы: центр гравитации, и начальную случайную позицию каждого узла. Центр гравитации определяется очень просто: за центр диаграммы берется значение [x/2, y/2]. Потом, когда узлы добавляются на диаграмму, если они не имеют атрибута x или y, тогда эти атрибуты инициализируются случайным образом в диапазоне [0, x] и [0,y] соответственно.
force.linkDistance([distance])
Если значение strenght определено, установка силы для определенной связи находится в диапазоне [0,1]. Если strenght не определено, возвращается текущая сила связи, которая по умолчанию равна 1. Переменная strenght может быть определена в виде константы и тогда сила связи между узлами является неизменной. Так же значение strenght может вычисляется функцией для каждой связи отдельно и передается ей через индекс связи с использованием контекста this.
Функия выполняется каждый раз, когда диаграмма запускает метод .start().
force.friction([friction])
Если переменная friction определена, коэффициент трения устанавливается в определенное значение. Дипазон значений заключен в пределах [0, 1]. Если переменная friction не определена возвращается текущий коэффициент, который по умолчанию равен 0.9. Имя этого параметра не должно Вас вводить в заблуждение, потому что его физический смысл отличается от стандартного физического коэффициента трения. Скорее это коэффициент определяющий замедление скорости. При каждом тике скорость перемножается на коэффициент трения. Поэтому при установке friction=1 скорость движения узлов будет неизменна, при установке коэффициента в 0, движение “заморозится”.
#force.charge([charge])
Если переменная charge определена то устанавливается сила “заряда” в определенное значение. Если переменная charge не определена, возвращается текущее значение “заряда” которое по умолчанию равно -30. Если charge константа - то тогда все узлы имеют одинаковое значение “заряда”. Так же значение charge может вычисляется функцией для каждой связи отдельно и передаваться ей через индекс связи с использованием контекста this.
Если charge < 0 то узлы отталкиваются, если charge > 0 то узлы притягиваются.
#force.chargeDistance([distance])
Если переменная distance определена, то она определяет максимальную дистанцию на которой действуют силы “заряда”. Если переменная не определена, то максимальная дистанция взаимодействия по умолчанию равна бесконечности.
#force.theta([theta])
Если переменная theta определена то критерий апроксимации Барнеса-Хата установлен в определенное значение. Если theta не определена, возвращается текущее значение, равное по умолчанию 0.8. В отличие от силы притяжения связей, которые действуют только на взаимодействующие узлы, силы “притяжения” работают глобально: они определяют силу взаимодействия всех узлов диаграммы.
#force.gravity([gravity])
Если переменная gravity определена, то она устанавливает силу притяжения в определенное значение. Если переменная gravity не определена, то тогда возвращается текущее значение переменной, по умолчанию равное 0.1. Имя этой переменной не должно вводить в заблуждение и физический смысл не равен физической гравитации (которая может быть симулирована с использованием положительных значений параметра charge) . В нашем случае gravity реализует как слабое геометрическое ограничение, которое можно сравнить со слабой пружинкой соединяющей каждый узел с центром слоя. Сила gravity в центре слоя равна нулю, по мере удаления от центра она возрастает по законам линейной завистмости от дистанции. Таким образом gravity располагает граф по центру экрана/области и не дает не соединенным узлам покинуть слой. При установке значения gravity в 0, Вы должны принять дополнительные меры для того что бы граф не переместился за границу видимой зоны.
# force.nodes([nodes])
Если узлы (nodes) графа определены, устанавливает их связь с описывающим их массивом. Если узлы не определены, возвращает текущий массив, который по умолчанию является пустым.
Каждый узел обладает следующими атрибутами:
- index - начинающийся с нуля индекс элемента в рамках, описывающего узлы массива;
- x - координата х текущего узла;
- y - координата у текущего узла;
- px - координата - х, предыдущей позиции узла;
- py - координата у, предыдущей позиции узла;
- fixed - логическая переменная, равная 1, когда позиция узла заблокирована;
- weight - вес узла, как количество его связей.
Эти атрибуты не являются необходимыми для установки перед прорисовкой узлов на слове, если они не установлены, то при запуске процедуры start они получат значение по умолчанию. Тем не менее, устанавливая дополнительные атрибуты для узлов учитывайте возможность конфликта Ваших атрибутов с базовыми атрибутами узлов.
#force.links([links])
Если связи (links) определены, устанавливает их связь с описывающим их массивом. Если узлы не определены, возвращает текущий массив, который по умолчанию является пустым. Каждая связь обладает следующими атрибутами:
- source - исходящий узел;
- target- входящий узел.
К связям могут быть добавлены дополнительные атрибуты.
#force.start()
Начинает отображение FLD. Этот метод должен быть вызван, после того, как узлы и связи между ними определены в соответствующих массивах, а также для отображения новой структуры FLD при внесении изменений в состав узлов или связей. Внутри метода force.start() используется параметры alpha, который контролирует "температуру" графа. Чем ниже "температура" тем меньше скорость движения узлов, после перемещения одного из них.
При старте граф инициализирует свои атрибуты для всех узлов и связей. Информация об узлах и связях может быть загружена в формате JSON, TCV, CSV.
Значение параметра fixed устанавливается равным нулю.
#force.alpha([value])
Устанавливает или возвращает значение "температуры" графа. Если value больше чем ноль, этот метод производит рестарт графа, запускает событие "start" и включает счетчик "tick". Если value не положительно то на следующем кванте будет инициализировано событие "end" и симуляция FLD прекратится. Если value не определено, тогда метод вернет текущее значение alpha.
#force.resume()
Эквивалентно
force.alpha(.1);
Устанавливает "температуру" графа alpha в 0.1. Этот метод устанавливает параметр alpha в 0.1, и затем перезапускает timer. Данный метод автоматически вызывается при старте графа и при выполнении события drag.
# force.stop()
Эквивалентно выражению:
force.alpha(0);
Прерывает симуляцию графа.
#force.tick()()
Запускает один шаг симуляции графа. Этот метод может быть использован в сочетании с методами start и stop для полной отрисовки графа. Например:
force.start();
for (var i = 0; i < n; ++i) force.tick();
force.stop();
Количество итераций зависит от размера и сложности графа. Выбор начальных позиций так же имеет большое влияние на то насколько быстро граф стабилизируется. В качестве примера приведем граф с узлами, расположенными по диагонали.
var n = nodes.length;
nodes.forEach(function(d, i) {
d.x = d.y = width / n * i;
});
Если позиции узлов графа не определены, то они установятся случайным образом (в соответствии с методом Верле).
#force.on(type, listener)
Определяет дополнительные обработчики событий для определенных типов сущностей FLD. По умолчанию, поддерживаются только события "start", "tick", "end".
Событие "tick" вызывается при выполнении каждого очередного такта симуляции FLD для обновления отображаемой позиции узлов и связей графа. Например, Вы можете начать отображение узлов и связей таким образом:
var link = vis.selectAll("line")
.data(links)
.enter().append("line");
var node = vis.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", 5);
И Вы можете установить их позиции в шаге симуляции графа так:
force.on("tick", function() {
link.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; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
В этом случае мы сохраняем выбранные узлы и связи при инициализации, и теперь у нас нет необходимости переопределять их в каждом очередном шаге симуляции графа FLD. При желании, Вы так же можете изменить внешний вид узлов и связей; например используя функцию библиотеки D3 symbol Вы можете использовать для визуализации узлов не только круги, но и различные символы.
Событие "end" инициализируется когда "температурный" параметр aipha достигнет нуля.
Комментариев нет:
Отправить комментарий