2012-06-15 23 views
18

D3 có một loạt các bố trí cho đồ thị có hướng có cây nghiêm ngặt, chẳng hạn như sau:Làm thế nào để bố trí một hệ thống phi cây với D3

A 
|\ 
B C 
/\ 
D E 

tôi cần phải rút ra một hệ thống các nút đó không phải là một cây, nhưng là một đồ thị theo chu kỳ. Đây là vấn đề đối với bố cục cây, vì một số nhánh hội tụ:

A 
|\ 
B C 
\| 
    D 

Có ai biết bố cục D3 cho phân cấp chung không? Hoặc cách khác, một số thông minh hack vào treelayout hiện tại? Tôi đã nhận thấy GraphVis xử lý tình huống này tốt, nhưng D3 tạo ra một biểu đồ phù hợp hơn với các yêu cầu ở đây.

+0

Bạn có thể muốn xem xét bố cục đồ thị theo hướng lực lượng. –

Trả lời

1

Nói chung cây và hệ thống phân cấp dữ liệu, bạn chỉ cần có "D" trong danh sách trẻ em cho cả B và C.

Tạo danh sách nút của bạn, hãy chắc chắn bạn có một id duy nhất quay trở lại để "D" không xuất hiện hai lần.

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

Sau đó, khi bạn gọi

var links = tree.links(nodes) 

bạn sẽ nhận được D xuất hiện như là "mục tiêu" hai lần (với B và C là "nguồn" tương ứng) mà kết quả trong hai dòng đến nút đơn "D".

+0

Tôi đã có thể có được cây lắp ráp theo cách bạn mô tả, nhưng TreeLayout dường như không thể xử lý nó. Có một cơ hội tôi hơi say lên bằng cách nào đó; bạn đã có may mắn nhận được một bố trí để chạy theo cách này? Trong thời gian chờ đợi, tôi đã quản lý cách giải quyết khá phù hợp bằng cách sử dụng GraphViz ở phía máy chủ. GraphViz sẽ thực hiện bố cục phân cấp và đầu ra theo định dạng DOT với vị trí (x, y) cho mỗi nút. Từ đó, nó tương đối dễ dàng để gói thông tin đó vào trang và vẽ nó bằng cách sử dụng D3. Cảm ơn sự giúp đỡ của bạn, tôi đánh giá cao nỗ lực! –

+0

Tôi chơi xung quanh với nó và nó đã được khá điên rồ. Tùy thuộc vào cha mẹ nó đôi khi sẽ thất bại bên trong d3: "nôn là không xác định: 6444" và lần khác nó sẽ làm cho, nhưng đặt đứa trẻ ở một chỗ xấu xí. Câu trả lời ngắn gọn là, bạn nói đúng. Bố trí cây trong trường hợp là vấn đề. – Glenn

10

Bạn có thể tạo mã của riêng mình mà không phải dựa vào bố cục D3 để hoàn thành.

Tôi đã cung cấp example in a jsFiddle. Ví dụ là khá đơn giản và sẽ cần phải được làm việc một chút để chứa các ví dụ phức tạp hơn.

Ví dụ này có thể được làm việc lại để xử lý dữ liệu phân cấp cũng như ít nỗ lực tương đối.

Đây là mã tôi đã sử dụng trong 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; 
}); 
+3

Cảm ơn ví dụ, Phil! Tôi đã làm một cái gì đó rất giống nhau. Nó chỉ ra các thuật toán bố trí tôi thực sự muốn được thực hiện trong GraphViz. Nó có các ràng buộc python, nhưng chúng không hoạt động.Thay vào đó, tôi đã làm như sau: 1) Biểu đồ biểu mẫu thành ngôn ngữ DOT (http://www.graphviz.org/content/dot-language) 2) Chuyển biểu đồ tới lệnh dấu chấm của graphviz qua dòng lệnh, bố cục và đặt (x, y) tọa độ vào DOT 3) Định dạng lại DOT thành đối tượng javascript, nhúng vào trang 4) Sử dụng D3 để đặt các nút theo tọa độ (x, y) trong DOT. Điều này hoạt động thực sự tốt với đồ thị rất lớn. –

7

Như ví dụ này: "Force Directed Trees" minh họa có một trick mà thường hoạt động. Trong ví dụ, hành vi của lực tác động được điều chỉnh trên mỗi lần đánh dấu sao cho các nút hơi lệch lên hoặc xuống tùy thuộc vào hướng của các liên kết. Như thể hiện điều này sẽ làm một công việc tốt cho cây, nhưng tôi đã tìm thấy nó cũng hoạt động tốt cho các đồ thị tuần hoàn. Không hứa hẹn, nhưng nó có thể giúp đỡ.

Các vấn đề liên quan