function DifferenceChart(data, {
x = ([x]) => x,
y1 = () => 0,
y2 = ([, y]) => y,
defined,
curve = d3.curveLinear,
marginTop = 20,
marginRight = 30,
marginBottom = 30,
marginLeft = 40,
width = 640,
height = 400,
xType = d3.scaleUtc,
xDomain,
xRange = [marginLeft, width - marginRight],
yType = d3.scaleLinear,
yDomain,
yRange = [height - marginBottom, marginTop],
yFormat,
yLabel,
colors = d3.schemePiYG[3],
stroke = "currentColor",
strokeLinecap = "round",
strokeLinejoin = "round",
strokeWidth = 1.5,
strokeOpacity = 1,
} = {}) {
const X = d3.map(data, x);
const Y1 = d3.map(data, y1);
const Y2 = d3.map(data, y2);
const I = d3.range(data.length);
if (defined === undefined) defined = (d, i) => !isNaN(X[i]) && !isNaN(Y1[i]) && !isNaN(Y2[i]);
const D = d3.map(data, defined);
if (xDomain === undefined) xDomain = d3.extent(X);
if (yDomain === undefined) yDomain = d3.extent([...Y1, ...Y2]);
const xScale = xType(xDomain, xRange);
const yScale = yType(yDomain, yRange);
const xAxis = d3.axisBottom(xScale).ticks(width / 80).tickSizeOuter(0);
const yAxis = d3.axisLeft(yScale).ticks(height / 60, yFormat);
const uid = `O-${Math.random().toString(16).slice(2)}`;
const line = (y) => d3.line().defined(i => D[i]).curve(curve).x(i => xScale(X[i])).y(y)(I);
const area = (y0, y1) => d3.area().defined(i => D[i]).curve(curve).x(i => xScale(X[i])).y0(y0).y1(y1)(I);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(yAxis)
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - marginLeft - marginRight)
.attr("stroke-opacity", 0.1))
.call(g => g.append("text")
.attr("x", -marginLeft)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text(yLabel));
svg.append("clipPath")
.attr("id", `${uid}-above`)
.append("path")
.attr("d", area(0, i => yScale(Y1[i])));
svg.append("clipPath")
.attr("id", `${uid}-below`)
.append("path")
.attr("d", area(height, i => yScale(Y1[i])));
svg.append("path")
.attr("clip-path", `url(${new URL(`#${uid}-above`, location)})`)
.attr("fill", colors[colors.length - 1])
.attr("d", area(height, i => yScale(Y2[i])));
svg.append("path")
.attr("clip-path", `url(${new URL(`#${uid}-below`, location)})`)
.attr("fill", colors[0])
.attr("d", area(0, i => yScale(Y2[i])));
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(xAxis)
.call(g => g.select(".domain").remove());
svg.append("path")
.attr("fill", "none")
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("stroke-linecap", strokeLinecap)
.attr("stroke-linejoin", strokeLinejoin)
.attr("stroke-opacity", strokeOpacity)
.attr("d", line(i => yScale(Y2[i])));
return svg.node();
}