In d3, how to get the interpolated line data from a SVG line?

前端 未结 3 878
时光说笑
时光说笑 2020-11-29 06:40

I display a line chart with D3 with roughly the following code (given the scale functions x, y and the float array data):



        
相关标签:
3条回答
  • 2020-11-29 07:09

    I have tried implementing findYatXbisection (as nicely suggested by bumbu), and I could not get it to work AS IS.

    Instead of modifying the length as a function of length_end and length_start, I just decreased the length by 50% (if x < point.x) or increased by 50% (if x> point.x) but always relative to start length of zero. I have also incorporated revXscale/revYscale to convert pixels to x/y values as set by my d3.scale functions.

    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)
    }
    
    0 讨论(0)
  • 2020-11-29 07:11

    Edited 19-Sep-2012 per comments with many thanks to nrabinowitz!

    You will need to do some sort of search of the data returned by getPointAtLength. (See 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")));
    

    For me this returns [5.000403881072998, 140.6229248046875].

    This search function, findYatX, is far from efficient (runs in O(n) time), but illustrates the point.

    0 讨论(0)
  • 2020-11-29 07:12

    This solution is much more efficient than the accepted answer. It's execution time is logarithmic (while accepted answer has linear complexity).

    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
    }
    
    0 讨论(0)
提交回复
热议问题