在 d3 中,如何从 SVG 线获取插值线数据?

IT技术 javascript graphics svg d3.js
2021-02-20 18:34:30

我显示与D3的线图与大致下面的代码(给定规模的功能xy并且浮子阵列data):

 var line = d3.svg.line()
         .interpolate("basis")
         .x(function (d, i) { return x(i); })
         .y(function (d) { return y(d); });
 d3.select('.line').attr('d', line(data));

现在我想知道给定水平像素位置的线垂直高度data阵列的数据点少于像素,并且显示的线是内插的,因此仅从data阵列推导出给定像素处的线的高度并不是直接的

任何提示?

3个回答

该解决方案比公认的答案更有效。它的执行时间是对数的(而接受的答案具有线性复杂性)。

var findYatXbyBisection = function(x, path, error){
  var length_end = path.getTotalLength()
    , length_start = 0
    , point = path.getPointAtLength((length_end + length_start) / 2) // get the middle point
    , bisection_iterations_max = 50
    , bisection_iterations = 0

  error = error || 0.01

  while (x < point.x - error || x > point.x + error) {
    // get the middle point
    point = path.getPointAtLength((length_end + length_start) / 2)

    if (x < point.x) {
      length_end = (length_start + length_end)/2
    } else {
      length_start = (length_start + length_end)/2
    }

    // Increase iteration
    if(bisection_iterations_max < ++ bisection_iterations)
      break;
  }
  return point.y
}
很好!炒锅很棒,它比公认的解决方案快得多!所以它适用于鼠标悬停或滚动事件。
2021-04-30 18:34:30
当路径之间存在空值时,这不起作用..就像我们使用 d3 的 line.undefined 函数一样
2021-05-05 18:34:30
谢谢 :) 你帮助了我
2021-05-11 18:34:30

2012 年 9 月 19 日根据评论编辑 ,非常感谢 nrabinowitz!

您将需要对 返回的数据进行某种搜索getPointAtLength(参见https://developer.mozilla.org/en-US/docs/DOM/SVGPathElement。)

// Line
var line = d3.svg.line()
     .interpolate("basis")
     .x(function (d) { return i; })
     .y(function(d, i) { return 100*Math.sin(i) + 100; });

// Append the path to the DOM
d3.select("svg#chart") //or whatever your SVG container is
     .append("svg:path")
     .attr("d", line([0,10,20,30,40,50,60,70,80,90,100]))
     .attr("id", "myline");

// Get the coordinates
function findYatX(x, linePath) {
     function getXY(len) {
          var point = linePath.getPointAtLength(len);
          return [point.x, point.y];
     }
     var curlen = 0;
     while (getXY(curlen)[0] < x) { curlen += 0.01; }
     return getXY(curlen);
}

console.log(findYatX(5, document.getElementById("myline")));

对我来说,这返回 [5.000403881072998, 140.6229248046875]。

这个搜索函数,findYatX,远非有效(在O(n)时间内运行),但说明了这一点。

@nrabinowitz oop,你说得对。不知道我是怎么误读的。当我找到真正的解决方案时将编辑我的答案。谢谢。
2021-04-17 18:34:30
嗯,我也只能想到这些了。见编辑后的帖子。非常感谢@nrabinowitz。
2021-04-19 18:34:30
看看我的回答。这是非常快的 O(log(n)) 并且您可以设置精度/误差。
2021-04-22 18:34:30
除非我误解了文档,否则这是不正确的 - 你不会明白x=3,你会得到pathLength=3,这可能会因路径的形状而有很大差异。
2021-05-13 18:34:30
我很确定内插线的唯一解决方案是搜索.getPointAtLength(i)直到Math.abs(i - x)小于某个阈值。二进制搜索在这里可能工作得相当好。我会自己发布答案,但我没有时间:)。
2021-05-13 18:34:30

我已经尝试实现 findYatXbisection(正如 bumbu 很好地建议的那样),但我无法让它按原样工作。

我没有将长度修改为 length_end 和 length_start 的函数,而是将长度减少 50%(如果 x < point.x)或增加 50%(如果 x> point.x)但始终相对于零的起始长度. 我还结合了 revXscale/revYscale 将像素转换为由我的 d3.scale 函数设置的 x/y 值。

function findYatX(x,path,error){
    var length = apath.getTotalLength()
        , point = path.getPointAtLength(length)
        , bisection_iterations_max=50
        , bisection_iterations = 0
    error = error || 0.1
    while (x < revXscale(point.x) -error || x> revXscale(point.x + error) {
        point = path.getPointAtlength(length)
        if (x < revXscale(point.x)) {
             length = length/2
        } else {
             length = 3/2*length
        }
        if (bisection_iterations_max < ++ bisection_iterations) {
              break;
        }
    }
return revYscale(point.y)
}
我认为你的算法在某些情况下会很糟糕。此外,它似乎比二分法有更多的迭代。例如,如果你的长度是 100 而你的点是 90 那么你的算法会做:1) 90 < 100 所以长度 = 100/2 = 50 2) 90 > 50 所以长度 = 50*1.5 = 75 3) 90 > 75所以长度 = 75*1.5 = 112.5 4) 90 < 112.5 所以长度 = 112.5/2 = 56.5 5) 90 > 56.5 所以长度 = 56.5*1.5 = 84.75 6) 90 > 84.75 所以长度 = 5*17) 17) 17) < 127.125 所以长度 = 127.125/2 = 63.5625 正如你看到的,你的算法有时甚至超过了初始长度。
2021-04-25 18:34:30