Трансформация force в Vega используется для физического
моделирования графов и наборов связанных объектов. Она реализует
симуляцию сил, аналогичную движку D3 Force Simulation, позволяя
автоматически располагать узлы на плоскости с учётом различных
физических взаимодействий.
Наиболее распространённые сценарии применения:
Трансформация доступна только в Vega. В Vega-Lite прямой поддержки
force нет, поскольку Vega-Lite ориентирован на
декларативные статистические визуализации, а не на физические
симуляции.
Симуляция выполняется итеративно. На каждом шаге:
Каждый элемент набора данных обычно представляет отдельный узел графа.
После завершения симуляции данные получают вычисленные координаты:
xyvxvyЭти поля затем используются при отрисовке.
Минимальная конфигурация выглядит следующим образом:
{
"transform": [
{
"type": "force",
"iterations": 300,
"forces": [
{
"force": "center",
"x": 300,
"y": 200
}
]
}
]
}
Определяет режим симуляции.
{
"type": "force",
"static": true
}
Если значение:
true — симуляция выполняется один раз;false — симуляция остаётся активной.Для большинства диаграмм используется static: true.
Количество итераций физического расчёта.
{
"iterations": 500
}
Чем больше значение:
Типичные значения:
| Размер графа | Итерации |
|---|---|
| Маленький | 50–100 |
| Средний | 200–400 |
| Большой | 500–1000 |
Перезапуск симуляции при изменении данных.
{
"restart": true
}
Позволяет управлять симуляцией через сигналы Vega.
{
"signal": "forceSim"
}
Через сигнал можно:
Ключевой элемент трансформации — массив forces.
{
"forces": [
{ ... },
{ ... }
]
}
Каждый объект массива задаёт отдельную физическую силу.
Сила center притягивает все узлы к определённой
точке.
{
"force": "center",
"x": 400,
"y": 300
}
Без этой силы граф может смещаться за пределы области отображения.
{
"transform": [
{
"type": "force",
"static": true,
"iterations": 300,
"forces": [
{
"force": "center",
"x": 300,
"y": 200
}
]
}
]
}
Предотвращает наложение узлов.
{
"force": "collide",
"radius": 20
}
Каждый объект получает виртуальный радиус столкновения.
Радиус столкновения.
{
"radius": 30
}
Можно использовать выражение:
{
"radius": {"expr": "datum.size"}
}
Интенсивность отталкивания.
{
"strength": 1
}
Диапазон:
0 → 1
Количество внутренних итераций столкновения.
{
"iterations": 4
}
Большие значения повышают точность.
{
"forces": [
{
"force": "collide",
"radius": 25,
"strength": 1
}
]
}
Моделирует взаимное притяжение или отталкивание между всеми узлами.
Это одна из важнейших сил в графовых визуализациях.
{
"force": "nbody",
"strength": -50
}
Отрицательное значение создаёт отталкивание.
{
"force": "nbody",
"strength": 20
}
Положительное значение создаёт притяжение.
Определяет мощность взаимодействия.
{
"strength": -200
}
Типичные значения:
| Эффект | Значение |
|---|---|
| Слабое | -10 |
| Среднее | -50 |
| Сильное | -300 |
Параметр оптимизации Barnes–Hut.
{
"theta": 0.9
}
Меньшие значения:
Минимальная дистанция.
{
"distanceMin": 10
}
Максимальная дистанция действия силы.
{
"distanceMax": 300
}
{
"forces": [
{
"force": "nbody",
"strength": -80,
"distanceMax": 500
}
]
}
Создаёт связи между узлами графа.
Используется совместно с отдельным набором данных рёбер.
{
"name": "nodes",
"values": [
{"id": "A"},
{"id": "B"},
{"id": "C"}
]
}
{
"name": "links",
"values": [
{"source": "A", "target": "B"},
{"source": "B", "target": "C"}
]
}
{
"force": "link",
"links": "links"
}
Желаемая длина связи.
{
"distance": 100
}
Жёсткость связи.
{
"strength": 0.7
}
Поле идентификатора узла.
{
"id": "id"
}
{
"forces": [
{
"force": "link",
"links": "links",
"id": "id",
"distance": 120,
"strength": 0.5
}
]
}
Притягивает узлы к определённой координате по оси X.
{
"force": "x",
"x": 300
}
{
"force": "x",
"x": {
"expr": "datum.group === 'A' ? 200 : 500"
}
}
{
"strength": 0.2
}
{
"forces": [
{
"force": "x",
"x": {
"expr": "datum.type === 'server' ? 200 : 600"
},
"strength": 0.3
}
]
}
Работает аналогично x, но по вертикальной оси.
{
"force": "y",
"y": 250,
"strength": 0.1
}
Располагает узлы по окружности.
{
"force": "radial",
"radius": 150,
"x": 300,
"y": 300
}
{
"strength": 0.5
}
{
"force": "radial",
"radius": {
"expr": "datum.level * 80"
}
}
{
"data": [
{
"name": "nodes",
"values": [
{"id": "A"},
{"id": "B"},
{"id": "C"},
{"id": "D"}
],
"transform": [
{
"type": "force",
"static": true,
"iterations": 300,
"forces": [
{
"force": "center",
"x": 400,
"y": 300
},
{
"force": "nbody",
"strength": -100
},
{
"force": "collide",
"radius": 20
},
{
"force": "link",
"links": "links",
"id": "id",
"distance": 120
}
]
}
]
},
{
"name": "links",
"values": [
{"source": "A", "target": "B"},
{"source": "A", "target": "C"},
{"source": "B", "target": "D"}
]
}
]
}
{
"type": "path",
"from": {"data": "links"},
"encode": {
"update": {
"stroke": {"value": "#999"}
}
},
"transform": [
{
"type": "linkpath",
"shape": "line"
}
]
}
{
"type": "symbol",
"from": {"data": "nodes"},
"encode": {
"enter": {
"size": {"value": 400},
"fill": {"value": "steelblue"}
},
"update": {
"x": {"field": "x"},
"y": {"field": "y"}
}
}
}
На практике почти всегда используется комбинация нескольких сил.
Типичная конфигурация:
{
"forces": [
{
"force": "center",
"x": 400,
"y": 300
},
{
"force": "nbody",
"strength": -80
},
{
"force": "collide",
"radius": 15
},
{
"force": "link",
"links": "links",
"id": "id",
"distance": 100
}
]
}
Каждая сила отвечает за отдельный аспект поведения:
| Сила | Назначение |
|---|---|
| center | Центрирование |
| nbody | Притяжение/отталкивание |
| collide | Исключение наложений |
| link | Связи между узлами |
| x/y | Позиционные ограничения |
| radial | Круговая компоновка |
{
"static": false
}
В этом режиме:
Пример сигнала:
{
"signals": [
{
"name": "drag",
"value": {},
"on": [
{
"events": "symbol:mousedown",
"update": "datum"
}
]
}
]
}
Наибольшие вычисления создают:
nbody;{
"iterations": 100
}
{
"distanceMax": 200
}
{
"iterations": 1
}
{
"static": true
}
Это значительно снижает нагрузку.
Причина:
Решение:
{
"strength": -30
}
Причина:
collide;nbody.Решение:
{
"radius": 20
}
Причина:
Решение:
Решение:
{
"force": "center"
}
или:
{
"force": "x"
}
и
{
"force": "y"
}
Трансформация force выполняется:
Симуляция изменяет сами объекты данных, добавляя:
x
y
vx
vy
index
Эти поля становятся частью потока данных Vega.
Статически вычисляют положение:
Вычисляет позиции через физическое моделирование:
Трансформация особенно эффективна для:
Симуляция может быть избыточной для:
Внутри Vega используется механизм, концептуально близкий к
d3-force.
Многие параметры совпадают:
| Vega force | D3 equivalent |
|---|---|
| center | forceCenter |
| collide | forceCollide |
| nbody | forceManyBody |
| link | forceLink |
| radial | forceRadial |
| x | forceX |
| y | forceY |
Это позволяет легко переносить знания между Vega и D3.
{
"transform": [
{
"type": "force",
"static": true,
"iterations": 300,
"forces": [
{
"force": "center",
"x": 400,
"y": 300
},
{
"force": "nbody",
"strength": -80
},
{
"force": "collide",
"radius": 20
},
{
"force": "link",
"links": "links",
"id": "id",
"distance": 100
}
]
}
]
}