Подтвердить что ты не робот

Как компоновать недревесную иерархию с D3

D3 имеет множество макетов для ориентированных графов, которые являются строгими деревьями, например:

A
|\
B C
 / \
D   E

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

A
|\
B C
 \|
  D

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

4b9b3361

Ответ 1

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

Я представил пример в jsFiddle. Пример довольно упрощен и должен быть немного обработан для размещения более сложных примеров.

Пример может быть переработан для обработки иерархических данных с относительно небольшим усилием.

Вот код, который я использовал в jsFiddle:

 // Sample data set
var json = {
    nodes: [{
        name: 'A'},
    {
        name: 'B'},
    {
        name: 'C'},
    {
        name: 'D'}],
    links: [{
        source: 'A',
        target: 'B'},
    {
        source: 'A',
        target: 'C'},
    {
        source: 'B',
        target: 'D'},
    {
        source: 'C',
        target: 'D'}
                                                                                   ]

};

var vis = d3.select('#vis').attr('transform', 'translate(20, 20)');

// Build initial link elements - Build first so they are under the nodes
var links = vis.selectAll('line.link').data(json.links);
links.enter().append('line').attr('class', 'link').attr('stroke', '#000');

// Build initial node elements
var nodes = vis.selectAll('g.node').data(json.nodes);
nodes.enter().append('g').attr('class', 'node').append('circle').attr('r', 10).append('title').text(function(d) {
    return d.name;
});

// Store nodes in a hash by name
var nodesByName = {};
nodes.each(function(d) {
    nodesByName[d.name] = d;
});

// Convert link references to objects
links.each(function(link) {
    link.source = nodesByName[link.source];
    link.target = nodesByName[link.target];
    if (!link.source.links) {
        link.source.links = [];
    }
    link.source.links.push(link.target);
    if (!link.target.links) {
        link.target.links = [];
    }
    link.target.links.push(link.source);
});

// Compute positions based on distance from root
var setPosition = function(node, i, depth) {
    if (!depth) {
        depth = 0;
    }
    if (!node.x) {
        node.x = (i + 1) * 40;
        node.y = (depth + 1) * 40;
        if (depth <= 1) {
            node.links.each(function(d, i2) {
                setPosition(d, i2, depth + 1);
            });
        }

    }

};
nodes.each(setPosition);

// Update inserted elements with computed positions
nodes.attr('transform', function(d) {
    return 'translate(' + d.x + ', ' + d.y + ')';
});

links.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;
});

Ответ 2

В качестве примера: " Force Directed Trees" показывает, что часто работает трюк. В этом примере поведение направления движения настраивается на каждом тике, так что узлы слегка дрейфуют вверх или вниз в зависимости от направления ссылок. Как показано, это будет прекрасно работать для деревьев, но я нашел, что он также хорошо переносится для ациклических графов. Нет promises, но это может помочь.

Ответ 3

Говоря в целом о деревьях и иерархии данных, вам просто нужно иметь "D" в списке детей для B и C.

Создав список node, убедитесь, что у вас есть уникальный идентификатор, так что "D" не появляется дважды.

vis.selectAll("g.node").data(nodes, function(d) { return d.id; });

Затем, когда вы вызываете

var links = tree.links(nodes)

вы должны получить D дважды в качестве "цели" (с B и C в качестве "источника" соответственно), что приводит к двум строкам к единственному node "D" .