javascript - D3 图形布局 - 具有多个父级的树结构

标签 javascript json d3.js layout

首先我要声明,我对 D3 和 Javascript 完全陌生。经过一些实验,我试图开发一个类似树的结构,但问题是,节点可以有多个父节点。

输入:

带有节点链接信息的Json,例如,

 var graph = {
    "nodes": [  { "id" : 0},
                { "id" : 1},
                { "id" : 2},
                { "id" : 3},
                { "id" : 4},
                { "id" : 5}
            ],
    "links": [  { "target": 5, "source":  1 },
                { "target":  2, "source":  0 },
                { "target": 3, "source":  4 },
                { "target": 1, "source":  2},
                { "target":  1, "source":  0 },
                
            ]
    };

注意:这仅供引用,因为我如何获取数据,可能有数千个节点。

输出:

我想要像树结构一样的输出,有点像 this ,也就是说,节点必须以类似深度/级别的结构绘制,根位于顶部,相同级别的节点以最小化链接重叠的方式排列。

编辑:上面提供的输入的输出是 this假设零是根。

我面临的问题:

  1. 由于我只给出节点及其源和目标,而不是它们的坐标,因此我必须解析整个列表计算级别,然后根据该级别划分高度,然后绘制节点。处理此类问题最有效的方法是什么?我应该深入研究算法解决方案吗?

或者

  • D3 中是否有一些图形绘制函数可以将此 JSON 作为输入并完成其余工作?
  • 请注意,节点可以有多个父节点,我的主要重点是以正确/分层顺序绘制这些节点,因此我面临的主要问题是计算仅给定的所有节点的 X/Y 坐标如上所述输入。

    请帮助我更好地理解这个主题,谢谢。

    最佳答案

    一整天都在断断续续地思考这个问题。通过内置的 d3 构造,我认为 this大约是您所能达到的最接近的程度。我已获取您的数据并将其转换为版本 4 示例:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
      .links line {
        stroke: #aaa;
        stroke-width: 5px;
      }
      
      .nodes circle {
        pointer-events: all;
        stroke: none;
      }
    </style>
    <svg width="600" height="300"></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script>
      var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height");
    
      var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(function(d) {
          return d.id;
        }))
        .force("charge", d3.forceManyBody())
        .force("center", d3.forceCenter(width / 2, height / 2))
        .force("y", d3.forceY())
    
      var graph = {
        "nodes": [{
          "id": 0
        }, {
          "id": 1
        }, {
          "id": 2
        }, {
          "id": 3
        }, {
          "id": 4
        }, {
          "id": 5
        }],
        "links": [{
            "target": 5,
            "source": 1
          }, {
            "target": 2,
            "source": 0
          }, {
            "target": 3,
            "source": 4
          }, {
            "target": 1,
            "source": 2
          }, {
            "target": 1,
            "source": 0
          },
    
        ]
      };
    
      var link = svg.append("g")
        .attr("class", "links")
        .selectAll("line")
        .data(graph.links)
        .enter().append("line");
    
      var node = svg.append("g")
        .attr("class", "nodes")
        .selectAll("circle")
        .data(graph.nodes)
        .enter().append("circle")
        .attr("r", 10);
    
      node.append("title")
        .text(function(d) {
          return d.id;
        });
    
      simulation
        .nodes(graph.nodes)
        .on("tick", ticked);
    
      simulation.force("link")
        .links(graph.links);
    
      function ticked() {
    
        var k = 6 * simulation.alpha();
    
        // Push sources up and targets down to form a weak tree.
        link
          .each(function(d) {
            d.source.y -= k, d.target.y += k;
          })
          .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;
          });
    
      }
    </script>

    如果你不想要移动和沉降的效果,可以预先计算布局使其static .


    我还尝试将数据转换为分层格式以与 d3.tree 一起使用但没有太多运气复制你的图像:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
      .node circle {
        fill: #999;
      }
      
      .node text {
        font: 10px sans-serif;
      }
      
      .node--internal circle {
        fill: #555;
      }
      
      .node--internal text {
        text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
      }
      
      .link {
        fill: none;
        stroke: #555;
        stroke-opacity: 0.4;
        stroke-width: 1.5px;
      }
    </style>
    <svg width="300" height="300"></svg>
    <script src="//d3js.org/d3.v4.min.js"></script>
    <script>
      var graph = {
        "nodes": [{
          "id": 0
        }, {
          "id": 1
        }, {
          "id": 2
        }, {
          "id": 3
        }, {
          "id": 4
        }, {
          "id": 5
        }],
        "links": [{
          "target": 5,
          "source": 1
        },{
          "target": 2,
          "source": 0
        }, {
          "target": 3,
          "source": 4
        }, {
          "target": 1,
          "source": 2
        }, {
          "target": 1,
          "source": 0
        }]
      };
    
      var root = {
        id: 0,
        data: graph.nodes[0],
        children: []
      }
    
      function recurChild(obj) {
        graph.links.forEach(function(d) {
          if (d.source === obj.id) {
            var c = {
              id: d.target,
              data: graph.nodes[d.target],
              children: []
            };
            obj.children.push(c);
            recurChild(c);
          }
        });
      }
      recurChild(root);
    
      root = d3.hierarchy(root);
    
      var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height"),
        g = svg.append("g").attr("transform", "translate(40,0)");
    
      var tree = d3.tree()
        .size([height, width - 160]);
    
      tree(root);
      
      console.log(root.descendants())
    
      var link = g.selectAll(".link")
        .data(root.descendants().slice(1))
        .enter().append("path")
        .attr("class", "link")
        .attr("d", function(d) {
          return "M" + d.y + "," + d.x + "C" + (d.y + d.parent.y) / 2 + "," + d.x + " " + (d.y + d.parent.y) / 2 + "," + d.parent.x + " " + d.parent.y + "," + d.parent.x;
        });
    
      var node = g.selectAll(".node")
        .data(root.descendants())
        .enter().append("g")
        .attr("class", function(d) {
          return "node" + (d.children ? " node--internal" : " node--leaf");
        })
        .attr("transform", function(d) {
          return "translate(" + d.y + "," + d.x + ")";
        })
    
      node.append("circle")
        .attr("r", 2.5);
    
      node.append("text")
        .attr("dy", 3)
        .attr("x", function(d) {
          return d.children ? -8 : 8;
        })
        .style("text-anchor", function(d) {
          return d.children ? "end" : "start";
        })
        .text(function(d) {
          return d.data.id;
        });
    </script>

    d3.hierarchy 实际上意味着,没有多个父结构的直接层次结构。

    关于javascript - D3 图形布局 - 具有多个父级的树结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39818279/

    相关文章:

    javascript - 检测 iframe 上的鼠标位置

    javascript - 强制 RTL 的 NativeBase 选项卡问题 - 修复

    javascript - 如何在生产环境中动态加载多个优化的 requirejs 模块?

    java - 使用 com.google.code.gson 在 Java8 中创建 JSON 对象

    javascript - R Shiny 中的绘图,包括 Javascript (d3.js)

    javascript - JavaScript中如何解析JSON取值

    python - 如何解决 ImportError : cannot import name simplejson in Django

    java - W/System.err : org. json.JSONException : Value array of type java. lang.String 无法转换为 JSONObject

    javascript - 为什么 Tooltip 不随鼠标移动?

    javascript - 获取 d3.js 中的事件函数