Настройте статический граф с фиксированным макетом в d3.js

У меня есть пример рабочего кода (только часть <script type="text/javascript">) статического графика с использованием d3.js, как показано ниже:

        /* Create graph data */
        var nodes = [];
        for (var i = 0; i < 13; i++) 
        {
            var datum = {
                "value": i
            };
            nodes.push(datum);
        }

        var links = [{"source": 0, "target": 1},
                     {"source": 1, "target": 2},
                     {"source": 2, "target": 0},
                     {"source": 1, "target": 3},
                     {"source": 3, "target": 2},
                     {"source": 3, "target": 4},
                     {"source": 4, "target": 5},
                     {"source": 5, "target": 6},
                     {"source": 5, "target": 7},
                     {"source": 6, "target": 7},
                     {"source": 6, "target": 8},
                     {"source": 7, "target": 8},
                     {"source": 9, "target": 4},
                     {"source": 9, "target": 11},
                     {"source": 9, "target": 10},
                     {"source": 10, "target": 11},
                     {"source": 11, "target": 12},
                     {"source": 12, "target": 10}];

        /* Create force graph */
        var w = 800;
        var h = 500;

        var size = nodes.length;
        nodes.forEach(function(d, i) { d.x = d.y = w / size * i});

        var svg = d3.select("body").append("svg")
                    .attr("width", w)
                    .attr("weight", h);

        var force = d3.layout.force()
                      .nodes(nodes)
                      .links(links)
                      .linkDistance(200)
                      .size([w, h]);

        setTimeout(function() {

            var n = 400
            force.start();
            for (var i = n * n; i > 0; --i) force.tick();
            force.stop();

            svg.selectAll("line")
               .data(links)
               .enter().append("line")
               .attr("class", "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; });

            svg.append("svg:g")
               .selectAll("circle")
               .data(nodes)
               .enter().append("svg:circle")
               .attr("class", "node")
               .attr("cx", function(d) { return d.x; })
               .attr("cy", function(d) { return d.y; })
               .attr("r", 15);

            svg.append("svg:g")
               .selectAll("text")
               .data(nodes)
               .enter().append("svg:text")
               .attr("class", "label")
               .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
               .attr("text-anchor", "middle")
               .attr("y", ".3em")
               .text(function(d) { return d.value; });

        }, 10);

и он производит этот довольно зашифрованный макет:

введите здесь описание изображения

Хотя технически это правильный график, идеальная компоновка должна быть примерно такой (не обращая внимания на различную визуальную графику):

введите здесь описание изображения

Обратите внимание, что макет должен быть фиксированным, чтобы перезагрузка страницы не меняла положение каждого узла; макет также должен быть статичным, в нем нет эффекта анимации и узлы нельзя перетаскивать. Оба требования уже выполнены в приведенном выше сценарии.

Итак, как мне дальше настроить этот скрипт d3 для создания макета, показанного на втором изображении?


person MLister    schedule 10.08.2012    source источник


Ответы (1)


Во-первых, увеличьте силу заряда и уменьшите расстояние связи. При этом больший упор делается на глобальную структуру, а не на локальные связи. Кроме того, если вы достаточно увеличите силу заряда, отталкивающий заряд будет отталкивать даже напрямую соединенные узлы дальше друг от друга, тем самым эффективно увеличивая расстояние связи и улучшая общую структуру. (Обратной стороной более сильной силы заряда является то, что инициализация графа более хаотична, но это не должно быть проблемой для статических макетов.)

Во-вторых, вам может потребоваться увеличить количество итераций или добавить настраиваемые силы, чтобы получить лучшие результаты. Макеты Force часто хорошо работают на произвольных графиках, но нет гарантии, что они дадут оптимальный (или даже хороший) результат. Для любого графа, где можно сделать упрощающие предположения (например, деревья), могут быть дополнительные силы или ограничения, которые вы можете применить, чтобы стимулировать сходимость моделирования к лучшему решению.

person mbostock    schedule 10.08.2012
comment
большая мощность заряда и меньшее расстояние связи делают свое дело. Что касается добавления пользовательских сил, есть ли пример/руководство, которому я могу следовать? Не могли бы вы привести несколько примеров дополнительных ограничений, которые можно применить для лучшей конвергенции? Большое спасибо! - person MLister; 10.08.2012
comment
Чтобы узнать о настраиваемых принудительных действиях, см. мой разговор о принудительных макетах и прилагаемые слайды. - person mbostock; 13.08.2012