So I have a D3 SVG bezier curve
I want to get X, Y coordinates of points with given step from the D3 path like so:
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).