chart = {
const {round,random} = Math;
const w = width;
const h = round(0.5 * w);
const r = w / 150;
const col0 = colorScheme(0), col1 = colorScheme(1);
const points = [
{x: r, y: h-r, fill: col0},
{x: w/2, y: r, fill: col0},
{x: w/2, y: h-r, fill: col1},
{x: w-r, y: h-r, fill: col1},
];
const segments = d3.range(nsteps - 1).map(i => ({
stroke: colorScheme((i + 0.5) / (nsteps - 1)), x1:0, y1:0, x2:0, y2:0 }));
function updateSegments() {
segments.forEach((segment,i) => {
if(i == 0) {
segment.x1 = points[0].x;
segment.y1 = points[0].y;
}
else {
segment.x1 = segments[i-1].x2;
segment.y1 = segments[i-1].y2;
}
const [c0,c1,c2,c3] = coef.slice(4*(i+1), 4*(i+2));
segment.x2 = c0 * points[0].x + c1 * points[1].x + c2 * points[2].x + c3 * points[3].x;
segment.y2 = c0 * points[0].y + c1 * points[1].y + c2 * points[2].y + c3 * points[3].y;
});
}
function draw() {
segs.selectAll("line")
.data(segments)
.join("line")
.attr("x1", d=>d.x1)
.attr("y1", d=>d.y1)
.attr("x2", d=>d.x2)
.attr("y2", d=>d.y2)
.attr("stroke", d=>d.stroke);
ends.selectAll("line")
.data([
{x1:points[0].x, y1:points[0].y, x2:points[1].x, y2:points[1].y},
{x1:points[3].x, y1:points[3].y, x2:points[2].x, y2:points[2].y},
])
.join("line")
.attr("x1", d=>d.x1)
.attr("y1", d=>d.y1)
.attr("x2", d=>d.x2)
.attr("y2", d=>d.y2)
.attr("stroke", d=>d.stroke);
}
function dragged(event, d) {
const x=Math.min(w-r, Math.max(r, event.x));
const y=Math.min(h-r, Math.max(r, event.y));
d3.select(this).attr("cx", d.x = x).attr("cy", d.y = y);
updateSegments();
draw();
}
const drag = d3.drag()
.on("drag", dragged);
const svg = d3.create("svg")
.attr("width", w)
.attr("height", h)
.attr("viewBox", [0, 0, w, h])
.attr("stroke", "black");
const segs = svg.append("g")
.attr("stroke", "black")
.attr("stroke-width", 5)
.attr("stroke-linecap", "round");
const ends = svg.append("g")
.attr("stroke-width", 1)
.attr("stroke-dasharray", [3, 2]);
updateSegments();
draw();
svg.append("g")
.attr("stroke", "black")
.selectAll("circle")
.data(points)
.join("circle")
.attr("cx", d=>d.x)
.attr("cy", d=>d.y)
.attr("r", r)
.attr("fill", d=>d.fill)
.attr("stroke", d=>d.stroke)
.call(drag)
return svg.node();
}