Как мне исправить положение каждого узла на графике, чтобы он выглядел как дерево?(иерархический слева направо) - PullRequest
0 голосов
/ 01 июля 2019

Необходимо исправить положения узлов, чтобы график отображался в порядке слева направо.Я использую d3.js версии 4 для этого.Я пытался изменить позиции, изменяя .x и .ys, но этот код позиционирует стрелки отдельно, и это влияет на его способность выглядеть иерархически.

   var width = 960,
          height = 500,
          nodeSize = 20,
          arrowWidth = 8,
          svg = d3.select("body")
            .append("svg")
            .attr("width", width)
            .attr("height", height)
          linkG = svg.append("g")
          nodeG = svg.append("g")
          // Arrows are separate from link lines so that their size
          // can be controlled independently from the link lines.
          arrowG = svg.append("g");

      // Arrowhead setup.
      // Draws from Mobile Patent Suits example:
      // http://bl.ocks.org/mbostock/1153292
      svg.append("defs")
        .append("marker")
          .attr("id", "arrow")
          .attr("orient", "auto")
          .attr("preserveAspectRatio", "none")
          // See also http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
          //.attr("viewBox", "0 -" + arrowWidth + " 10 " + (2 * arrowWidth))
          .attr("viewBox", "0 -5 10 10")
          // See also http://www.w3.org/TR/SVG/painting.html#MarkerElementRefXAttribute
          .attr("refX", 10)
          .attr("refY", 0)
          .attr("markerWidth", 10)
          .attr("markerHeight", arrowWidth)
        .append("path")
          .attr("d", "M0,-5L10,0L0,5");

      var simulation = d3.forceSimulation()
        .force("link", d3.forceLink())
        .force("charge", d3.forceManyBody().strength(-200))
        .force("center", d3.forceCenter(width / 2, height / 2));

      simulation.force("link")
        .distance(250);



      function render(graph){

        var link = linkG.selectAll("line").data(graph.links);
        var linkEnter = link.enter().append("line")
          .attr("class", "link-line");
        link.exit().remove();
        link = link.merge(linkEnter);

        var arrow = arrowG.selectAll("line").data(graph.links);
        var arrowEnter = arrow.enter().append("line")
          .attr("class", "arrow")
          .attr("marker-end", "url(#arrow)" );
        arrow.exit().remove();
        arrow = arrow.merge(arrowEnter);

        var node = nodeG.selectAll("g").data(graph.nodes);
        var nodeEnter = node.enter().append("g");
        node.exit().remove();

        nodeEnter.append("rect")
            .attr("class", "node-rect")
          .attr("y", -nodeSize)
          .attr("height", nodeSize * 2)
          .attr("rx", nodeSize)
          .attr("ry", nodeSize)
          .on("click", function (d){
            simulation.unfix(d);
          });

        nodeEnter.append("text")
          .attr("class", "node-text");

        node = node.merge(nodeEnter);

        node.select(".node-text")
          .text(function (d){ return d.name; })
          .each(function (d) {

            var circleWidth = nodeSize * 2,
                textLength = this.getComputedTextLength(),
                textWidth = textLength + nodeSize;

            if(circleWidth > textWidth) {
              d.isCircle = true;
              d.rectX = -nodeSize;
              d.rectWidth = circleWidth;
            } else {
              d.isCircle = false;
              d.rectX = -(textLength + nodeSize) / 2;
              d.rectWidth = textWidth;
              d.textLength = textLength;
            }
          });

        node.select(".node-rect")
          .attr("x", function(d) { return d.rectX; })
          .attr("width", function(d) { return d.rectWidth; });

        simulation.force("link").links(graph.links);

        simulation.nodes(graph.nodes).on("tick", function (){

          graph.nodes.forEach(function (d) {
            if(d.isCircle){
              d.leftX = d.rightX = d.x;
            } else {
              d.leftX =  d.x - d.textLength / 2 + nodeSize / 2;
              d.rightX = d.x + d.textLength / 2 - nodeSize / 2;
            }
          });

          link.call(edge);
          arrow.call(edge);

          node.attr("transform", function(d) {      
            return "translate(" + d.x + "," + d.y + ")";
          });
        });
      }

      // Sets the (x1, y1, x2, y2) line properties for graph edges.
      function edge(selection){
        selection
          .each(function (d) {
            var sourceX, targetX, midX, dy, dy, angle;

            // This mess makes the arrows exactly perfect.
            if( d.source.rightX < d.target.leftX ){
              sourceX = d.source.rightX;
              targetX = d.target.leftX;
            } else if( d.target.rightX < d.source.leftX ){
              targetX = d.target.rightX;
              sourceX = d.source.leftX;
            } else if (d.target.isCircle) {
              targetX = sourceX = d.target.x;
            } else if (d.source.isCircle) {
              targetX = sourceX = d.source.x;
            } else {
              midX = (d.source.x + d.target.x) / 2;
              if(midX > d.target.rightX){
                midX = d.target.rightX;
              } else if(midX > d.source.rightX){
                midX = d.source.rightX;
              } else if(midX < d.target.leftX){
                midX = d.target.leftX;
              } else if(midX < d.source.leftX){
                midX = d.source.leftX;
              }
              targetX = sourceX = midX;
            }

            dx = targetX - sourceX;
            dy = d.target.y - d.source.y;
            angle = Math.atan2(dx, dy);

            // Compute the line endpoint such that the arrow
            // is touching the edge of the node rectangle perfectly.
            d.sourceX = sourceX + Math.sin(angle) * nodeSize;
            d.targetX = targetX - Math.sin(angle) * nodeSize;
            d.sourceY = d.source.y + Math.cos(angle) * nodeSize;
            d.targetY = d.target.y - Math.cos(angle) * nodeSize;
          })
          .attr("x1", function(d) { return d.sourceX; })
          .attr("y1", function(d) { return d.sourceY; })
          .attr("x2", function(d) { return d.targetX; })
          .attr("y2", function(d) { return d.targetY; });
      }

      var graph = {"nodes":["socks","shoes","shirt","belt","tie","jacket","pants","underpants"],"links":[{"source":0,"target":1},{"source":2,"target":3},{"source":2,"target":4},{"source":3,"target":5},{"source":4,"target":5},{"source":6,"target":1},{"source":6,"target":3},{"source":7,"target":6}]};

      graph.nodes = graph.nodes.map(function (d){
        return { name: d };
      });
      graph.links = graph.links.map(function (d){
        d.source = graph.nodes[d.source];
        d.target = graph.nodes[d.target];
        return d;
      });`enter code here`
      render(graph);
...