【问题标题】:Line and Rect intersection not calculated properly on one axisLine 和 Rect 交点在一个轴上没有正确计算
【发布时间】:2021-03-16 22:38:58
【问题描述】:

我正在尝试通过两个节点之间的连接来实现基本的 d3 网络,并且我希望连接在目标节点的边缘结束(因此结束标记正确定位)而不是中心它。我使用来自Svg draw connection line between two rectanglesgetIntersection()

在 Y 轴上,链接的终点似乎具有正确的值,但在 X 轴上,当 I 远离原点时,会出现偏移。

下面是失败的代码:

  graph = {
    "nodes": [{id: 0},{id: 1}],
    "links": [{source: 0,target: 1}]
  }

  const width = 800, height = 600
  const nodeWidth = 80, nodeHeight = 20
  const arrowDepth = 10

  var svg = d3.select("#svg-body").append("svg").attr("viewBox", [0, 0, width, height])

  svg.append("svg:defs").selectAll("marker")
    .data(["end"])
    .enter().append("svg:marker")
    .attr("id", String)
    .attr("viewBox", `0 -5 ${arrowDepth} ${arrowDepth}`)
    .attr("refX", 0)
    .attr("refY", -0.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
    .append("svg:polyline")
    .attr("points", `0,-5 ${arrowDepth},0 0,5`)
  ;

  let link = svg
    .selectAll(".link")
    .data(graph.links)
    .join("line")
    .classed("link", true)
    .attr("marker-end", "url(#end)")

  const node = svg.selectAll(".node")
    .data(graph.nodes)
    .enter().append("g")
    .classed("node", true)
    .on("click", click)
    .call(
      d3.drag()
      .on("start", dragstart)
      .on("drag", dragged)
    )

  node.append("rect")
    .attr("height", nodeHeight)
    .attr("width", nodeWidth);

  const simulation = d3
    .forceSimulation()
    .nodes(graph.nodes)
    .force("charge_force",
      d3.forceManyBody().strength(-100))
    .force("center_force",
      d3.forceCenter(width / 2, height / 2))
    .force("links",
        d3.forceLink(graph.links)
        .distance(80)
    )
    .on("tick", tick)
    .tick(100)

  function getIntersection(dx, dy, cx, cy, w, h) {
    // Hit vertical edge of box1
    if (Math.abs(dy / dx) < h / w) {
      return [cx + (dx > 0 ? w : -w), cy + dy * w / Math.abs(dx)];
    } else {
      // Hit horizontal edge of box1
      return [cx + dx * h / Math.abs(dy), cy + (dy > 0 ? h : -h)];
    }
  };

  function tick() {

    link
      .attr("x1", d => d.source.x)
      .attr("y1", d => d.source.y)
      .attr("x2", d => getIntersection(
        d.source.x-d.target.x,
        d.source.y-d.target.y,
        d.target.x,
        d.target.y,
        nodeWidth,
        nodeHeight
      )[0])
      .attr("y2", d => getIntersection(
        d.source.x-d.target.x,
        d.source.y-d.target.y,
        d.target.x,
        d.target.y,
        nodeWidth,
        nodeHeight
      )[1]);

    node.attr("transform", function(d) { return `translate(${d.x - nodeWidth/2}, ${d.y - nodeHeight/2})`; });

  }

  function click(event, d) {
    delete d.fx;
    delete d.fy;
    d3.select(this).classed("fixed", false);
    simulation.alpha(1).restart()
  }

  function clamp(x, lo, hi) {
    return x < lo ? lo : x > hi ? hi : x;
  }


  function dragstart() {
    d3.select(this).classed("fixed", true);
  }

  function dragged(event, d) {
    d.fx = clamp(event.x, 0, width);
    d.fy = clamp(event.y, 0, height);
    simulation.alpha(1).restart()
  }
    .link {
        stroke: #000;
        stroke-width: 1.5px;
    }

    .node {
        cursor: move;
        fill: #ccc;
        stroke: #000;
        stroke-width: 1.5px;
    }

    .node.fixed {
        fill: #f00;
    }
<script src="http://d3js.org/d3.v6.min.js"></script>
<div id="svg-body"></div>

【问题讨论】:

    标签: javascript svg d3.js graph


    【解决方案1】:

    如果您查看链接示例,您会发现回答者明确表示您必须将宽度和高度除以 2:

    宽度和高度也有除以 2。 (他们的重点)

    另外,我正在更改refX,所以箭头正好在行尾结束。

    以下是您的代码进行了这些更改:

    graph = {
      "nodes": [{
        id: 0
      }, {
        id: 1
      }],
      "links": [{
        source: 0,
        target: 1
      }]
    }
    
    const width = 800,
      height = 600
    const nodeWidth = 80,
      nodeHeight = 20
    const arrowDepth = 10
    
    var svg = d3.select("#svg-body").append("svg").attr("viewBox", [0, 0, width, height])
    
    svg.append("svg:defs").selectAll("marker")
      .data(["end"])
      .enter().append("svg:marker")
      .attr("id", String)
      .attr("viewBox", `0 -5 ${arrowDepth} ${arrowDepth}`)
      .attr("refX", 10)
      .attr("refY", 0)
      .attr("markerWidth", 6)
      .attr("markerHeight", 6)
      .attr("orient", "auto")
      .append("svg:polyline")
      .attr("points", `0,-5 ${arrowDepth},0 0,5`);
    
    let link = svg
      .selectAll(".link")
      .data(graph.links)
      .join("line")
      .classed("link", true)
      .attr("marker-end", "url(#end)")
    
    const node = svg.selectAll(".node")
      .data(graph.nodes)
      .enter().append("g")
      .classed("node", true)
      .on("click", click)
      .call(
        d3.drag()
        .on("start", dragstart)
        .on("drag", dragged)
      )
    
    node.append("rect")
      .attr("height", nodeHeight)
      .attr("width", nodeWidth);
    
    const simulation = d3
      .forceSimulation()
      .nodes(graph.nodes)
      .force("charge_force",
        d3.forceManyBody().strength(-100))
      .force("center_force",
        d3.forceCenter(width / 2, height / 2))
      .force("links",
        d3.forceLink(graph.links)
        .distance(80)
      )
      .on("tick", tick)
      .tick(100)
    
    function getIntersection(dx, dy, cx, cy, w, h) {
      // Hit vertical edge of box1
      if (Math.abs(dy / dx) < h / w) {
        return [cx + (dx > 0 ? w : -w), cy + dy * w / Math.abs(dx)];
      } else {
        // Hit horizontal edge of box1
        return [cx + dx * h / Math.abs(dy), cy + (dy > 0 ? h : -h)];
      }
    };
    
    function tick() {
    
      link
        .attr("x1", d => d.source.x)
        .attr("y1", d => d.source.y)
        .attr("x2", d => getIntersection(
          d.source.x - d.target.x,
          d.source.y - d.target.y,
          d.target.x,
          d.target.y,
          nodeWidth / 2,
          nodeHeight / 2
        )[0])
        .attr("y2", d => getIntersection(
          d.source.x - d.target.x,
          d.source.y - d.target.y,
          d.target.x,
          d.target.y,
          nodeWidth / 2,
          nodeHeight / 2
        )[1]);
    
      node.attr("transform", function(d) {
        return `translate(${d.x - nodeWidth/2}, ${d.y - nodeHeight/2})`;
      });
    
    }
    
    function click(event, d) {
      delete d.fx;
      delete d.fy;
      d3.select(this).classed("fixed", false);
      simulation.alpha(1).restart()
    }
    
    function clamp(x, lo, hi) {
      return x < lo ? lo : x > hi ? hi : x;
    }
    
    
    function dragstart() {
      d3.select(this).classed("fixed", true);
    }
    
    function dragged(event, d) {
      d.fx = clamp(event.x, 0, width);
      d.fy = clamp(event.y, 0, height);
      simulation.alpha(1).restart()
    }
    .link {
      stroke: #000;
      stroke-width: 1.5px;
    }
    
    .node {
      cursor: move;
      fill: #ccc;
      stroke: #000;
      stroke-width: 1.5px;
    }
    
    .node.fixed {
      fill: #f00;
    }
    <script src="http://d3js.org/d3.v6.min.js"></script>
    <div id="svg-body"></div>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 2015-04-14
      • 1970-01-01
      • 2013-09-20
      • 2021-11-13
      相关资源
      最近更新 更多