在两个圆圈之间画一个箭头?

And*_*yuk 4 svg d3.js

如何在两个圆圈之间绘制一条箭头线,给出:

  1. cirlces中心的位置
  2. 圆的半径

我正在使用linemarker svg对象.

如果我将箭头绘制到圆圈的"中心" - 那么箭头是不可见的.如果我将箭头向后移动太远 - 那么该线显示并隐藏箭头的尖端(这里为了更好的可见性而夸大): 箭头移回了waaaay

根据请求,这是我的代码的相关位(在生活脚本中):

# Draw an arrow to use for lines
svg.append("svg:defs")
 .append("svg:marker")
  .attr("id", "arrow")
  .attr("viewBox", "0 0 10 10")
  .attr("refX", 27)
  .attr("refY", 5)
  .attr("markerUnits", "strokeWidth")
  .attr("markerWidth", 8)
  .attr("markerHeight", 6)
  .attr("orient", "auto")
  .append("svg:path")
  .attr("d", "M 0 0 L 10 5 L 0 10 z")

svg.append("line")
 .attr "x1" 5 
 .attr "x2" 50 
 .attr "y1" 5 
 .attr "y2" 50
 .style "stroke" "black"
 .attr "stroke-width" 2
 .attr "marker-end" "url(\#arrow)"
Run Code Online (Sandbox Code Playgroud)

或者,这里是工作示例的JSFiddle(注意箭头是"坐立不安"看起来恰到好处):http://jsfiddle.net/yeQS2/

Joa*_*ant 15

如果我理解正确,您需要找到需要添加到源的2D矢量才能到达目标圆的边界.

伪代码:

d = distance between A and B; // (sqrt((xB-xA)² + (yB-yA)²)).
d2 = d - radius;

ratio = d2 / d;

dx = (xB - xA) * ratio;
dy = (yB - yA) * ratio;

x = xA + dx;
y = yA + dy;
Run Code Online (Sandbox Code Playgroud)


cur*_*ran 7

我有同样的问题,这就是我如何解决它.对原始小提琴的改动:

更改.attr("refX", 27).attr("refX", 0).这使得箭头绘制超出了线的末端.

使用三角函数计算线的正确结束位置,考虑箭头,通过将以下代码添加到"tick":

var arrowheadLength = 8, // from markerWidth
    nodeRadius = 10;
link.each(function(d) {
  var x1 = d.source.x,
      y1 = d.source.y,
      x2 = d.target.x,
      y2 = d.target.y,
      angle = Math.atan2(y2 - y1, x2 - x1);
  d.targetX = x2 - Math.cos(angle) * (nodeRadius + arrowheadLength);
  d.targetY = y2 - Math.sin(angle) * (nodeRadius + arrowheadLength);
});
Run Code Online (Sandbox Code Playgroud)

使用计算的targetX和targetY链接属性:

.attr("x2", function(d){
  return d.targetX;
}).attr("y2", function(d){
  return d.targetY;
})
Run Code Online (Sandbox Code Playgroud)

这是更新的小提琴.


and*_*ens 5

好的,所以我想尝试一下并用一些向量数学来实现它,它更漂亮并且结果是可重用的。

一些澄清:

  • “向量”只是两个数字(x 和 y)
  • “坐标”在结构上与向量相同,只是对我们来说意味着不同的东西。不过我们可以进行同样的数学计算。
  • “定位向量”是两个向量(如源向量和目标向量)
  • 您可以通过从第二个向量中减去第一个向量来“释放”定位向量(您将获得一个不再锚定在坐标系中的新向量)
  • 向量的“长度”是使用毕达哥拉斯定理(也称为范数)计算的
  • 向量加法”只是将两个或多个向量的 xs 和 ys 相加,得到一个新向量。
  • 标量乘法”和除法是通过将 x 和 y 除以标量来完成
  • 单位向量”是向量除以其长度

假设我们希望它动态工作(“每个刻度”),初始链接调整如下所示(我正在使用coffeescript):

links.attr('x1', ({source,target}) -> source.x)
     .attr('y1', ({source,target}) -> source.y)
     .attr('x2', ({source,target}) -> target.x)
     .attr('y2', ({source,target}) -> target.y)
Run Code Online (Sandbox Code Playgroud)

我们想要做的是将源和目标移nodeRadius离圆。为此,我们使用向量数学来

  1. 释放定位向量(链接由两个坐标组成,我们需要一个未锚定的向量)
  2. 计算自由向量的单位向量
  3. 一旦我们有了它,我们就可以将它乘以nodeRadius。这个新向量表示节点中心与其边界之间的距离,其方向与链接相同。
  4. 将向量添加到源坐标,这些新坐标将位于圆的边缘。

好的,我们将使用以下函数来做到这一点:

length = ({x,y}) -> Math.sqrt(x*x + y*y)
sum = ({x:x1,y:y1}, {x:x2,y:y2}) -> {x:x1+x2, y:y1+y2}
diff = ({x:x1,y:y1}, {x:x2,y:y2}) -> {x:x1-x2, y:y1-y2}
prod = ({x,y}, scalar) -> {x:x*scalar, y:y*scalar}
div = ({x,y}, scalar) -> {x:x/scalar, y:y/scalar}
unit = (vector) -> div(vector, length(vector))
scale = (vector, scalar) -> prod(unit(vector), scalar)

free = ([coord1, coord2]) -> diff(coord2, coord1)
Run Code Online (Sandbox Code Playgroud)

这可能看起来有点不知所措,它非常紧凑,因为coffeescript允许我们直接在方法签名中解构事物,非常方便!正如您所看到的,还有另一个函数称为scale. 这只是一个组合步骤 2. 和 3 的便利函数。

现在让我们尝试为链接源设置新的 x 坐标。请记住:坐标应移动nodeRadius,以便它从圆的边界开始,而不是在圆内部。

(d) ->
    # Step 1
    freed = free(d)
    # Step 2
    unit = unit(freed)
    # Step 3
    scaled = prod(unit, nodeRadius)
    # Step 2+3 would be scale(freed, nodeRadius)
    # Step 4, coords are pretty much just vectors,
    # so we just use the sum() function to move the source coords
    coords = sum(d.source, scaled)
    return coords.x
Run Code Online (Sandbox Code Playgroud)

没什么可说的!将所有这些放入tick()函数中,我们得到:

links.attr('x1', ({source,target}) -> sum(source, scale(free([source,target]), nodeRadius)).x)
     .attr('y1', ({source,target}) -> sum(source, scale(free([source,target]), nodeRadius)).y)
     .attr('x2', ({source,target}) -> diff(target, scale(free([source,target]), nodeRadius)).x)
     .attr('y2', ({source,target}) -> diff(target, scale(free([source,target]), nodeRadius)).y)
Run Code Online (Sandbox Code Playgroud)

哦,不要忘记从目标坐标中减去,否则你只会再次使线更长(即移动它nodeRadius)。