Public
Edited
May 16, 2024
Insert cell
Insert cell
renderSplines(straightenedControlPoints, width / 5, width / 5)
Insert cell
Insert cell
Insert cell
Insert cell
function renderSplines(controlPointsArray, width, height, straightenedPointsArray = null) {
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);

const xScale = d3.scaleLinear()
.domain([-1, 1])
.range([50, width - 50]);

const yScale = d3.scaleLinear()
.domain([-1, 1])
.range([height - 50, 50]);

const line = d3.line()
.x(d => xScale(d[0]))
.y(d => yScale(d[1]))
.curve(/* d3.curveCatmullRom.alpha(0.5) */d3.curveBasis);

controlPointsArray.forEach((controlPoints, index) => {
svg.append('path')
.datum(controlPoints)
.attr('d', line)
.attr('stroke', 'blue')
.attr('stroke-width', 2)
.attr('fill', 'none');

svg.selectAll(`circle-start-end-${index}`)
.data(controlPoints.filter((_, i) => i !== 1))
.join('circle')
.attr('cx', d => xScale(d[0]))
.attr('cy', d => yScale(d[1]))
.attr('r', 5)
.attr('fill', 'red');

svg.selectAll(`circle-middle-${index}`)
.data(controlPoints.filter((_, i) => i === 1))
.join('circle')
.attr('cx', d => xScale(d[0]))
.attr('cy', d => yScale(d[1]))
.attr('r', 5)
.attr('fill', 'green');
});

if (straightenedPointsArray) {
const straightLine = d3.line()
.x(d => xScale(d[0]))
.y(d => yScale(d[1]));

straightenedPointsArray.forEach(straightenedPoints => {
svg.append('path')
.datum(straightenedPoints)
.attr('d', straightLine)
.attr('stroke', 'green')
.attr('stroke-width', 2)
.attr('fill', 'none');
});
}

return svg.node();
}
Insert cell
function generateNGonSplineControlPoints(n, radius, centerX, centerY) {
const controlPointsArray = [];
const angle = (2 * Math.PI) / n;

for (let i = 0; i < n; i++) {
const startX = centerX + radius * Math.cos(i * angle);
const startY = centerY + radius * Math.sin(i * angle);
const endX = centerX + radius * Math.cos((i + 1) * angle);
const endY = centerY + radius * Math.sin((i + 1) * angle);
controlPointsArray.push([[startX, startY], [centerX, centerY], [endX, endY]]);
}

return controlPointsArray;
}
Insert cell
straightenedControlPoints = straightenSplineCurves(controlPoints, beta);
Insert cell
function straightenSplineCurves(controlPointsArray, beta) {
return controlPointsArray.map(controlPoints => {
const N = controlPoints.length;
const straightenedPoints = [];

for (let i = 0; i < N; i++) {
const Pi = controlPoints[i];
const P0 = i === 0 ? controlPoints[0] : controlPoints[i - 1];
const PN_1 = i === N - 1 ? controlPoints[N - 1] : controlPoints[i + 1];

const straightenedPoint = [
beta * Pi[0] + (1 - beta) * (P0[0] + (i / (N - 1)) * (PN_1[0] - P0[0])),
beta * Pi[1] + (1 - beta) * (P0[1] + (i / (N - 1)) * (PN_1[1] - P0[1])),
];

straightenedPoints.push(straightenedPoint);
}

return straightenedPoints;
});
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more