function distribution(
n = 10,
d = d3.range(n).map(x => 1 / n),
height = 100,
width = 600
) {
const value = d;
const x = d3
.scaleLinear()
.domain([0, n])
.range([0, width]);
const y = d3
.scaleLog()
.domain([0.00001, 1])
.range([height, 0])
.clamp(true);
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width);
svg
.append("line")
.attr("x1", x(0))
.attr("x2", x(n))
.attr("y1", y(0))
.attr("y2", y(0))
.attr("stroke", "#000");
const rect = svg
.selectAll("rect")
.data({ length: n })
.join("rect")
.attr("x", (_, i) => x(i))
.attr("width", width / n - 2)
.attr("fill", "brown");
function update(frozen_index) {
normalize(frozen_index, value);
rect.attr("y", (_, i) => y(value[i]));
rect.attr("height", (_, i) => Math.abs(-y(value[i]) + y(0)));
svg.node().dispatchEvent(new CustomEvent('input'));
}
update(0);
const drag = d3
.drag()
.subject(event => Math.floor(x.invert(event.x)))
.on("drag", event => {
value[event.subject] = y.invert(event.y);
update(event.subject);
});
svg.call(drag);
return Object.assign(svg.node(), { value });
function normalize(frozen_index, D) {
const compsum = 1 - D.slice(0, frozen_index + 1).reduce((a, b) => a + b);
if (compsum >= 0) {
const parsum = D.slice(frozen_index + 1).reduce((a, b) => a + b);
const sum1 = parsum ? (1 * compsum) / parsum : 1;
for (let i = frozen_index; i < D.length; i++) D[i] *= sum1;
} else {
const max_partial = partialSums(D);
for (let i = max_partial; i < D.length; i++) D[i] = 0;
const parsum = D.slice(max_partial, frozen_index + 1).reduce(
(a, b) => a + b
);
const sum1 = parsum ? 1 / parsum : 1;
for (let i = max_partial + 1; i < D.length; i++)
D[i] = (D[i] + epsilon) * sum1;
}
return D;
}
}