Home > Blockchain >  How to get points from a D3 SVG bezier curve with given step size?
How to get points from a D3 SVG bezier curve with given step size?

Time:10-17

So I have a D3 SVG bezier curve enter image description here

I want to get X, Y coordinates of points with given step from the D3 path like so:

enter image description here

So to get an array of coordinate pairs with equal steps. If possible not recalculating bezier curve. How to do such a thing in D3js?

CodePudding user response:

With the help of a bisection function from here you probably want something like this:

const
  svg = d3.select("svg"),
  width = 512,
  height = 200;
const data = [];
const curve = "curveBasis";

var walk = function() {
  for (let i = 0, v = 2; i < 50;   i) {
    v  = Math.random() - 0.5;
    v = Math.max(Math.min(v, 4), 0);
    data.push({step: i, value: v});
  }
}

walk();

walkX = d3.scaleLinear()
  .domain([0, 49])
  .range([10, width - 10]);

walkY = d3.scaleLinear()
  .domain([0, 4])
  .range([200 - 10, 10]);

const line = d3.line()
  .curve(d3[curve])
  .x(d => walkX(d.step))
  .y(d => walkY(d.value));

svg
  .append("path")
  .attr("id", "svgpath")
  .attr("d", line(data))
  .attr("fill", "none")
  .attr("stroke", "black");

var svgpath = document.getElementById("svgpath");

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
}

for (let i = 0; i < data.length;   i) {
  console.log(findYatXbyBisection(walkX(i), svgpath, 0.01).toFixed(4));
}
<html>
<head>
<meta charset="utf-8" />
<title>SVG Line</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.1.1/d3.min.js" integrity="sha512-COTaPOlz12cG4fSfcBsxZsjauBAyldqp 8FQUM/dZHm ts/jR4AFoJhCqxy8K10Jrf3pojfsbq7fAPTb1XaVkg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<svg id="chart" width="512" height="200">
</svg>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Do note that Y values returned are SVG coordinates so they start at 0 from the top of page. Check the range function used in the walkY function to refresh how you have to reverse the values for typical line and bar charts in D3.js.

And of course instead of logging to the console you can push the values to your custom array and use different interval value e.g. 1/n of the total line (path) width instead of 1/50 I've used (for 50 data points).

  • Related