function OverlapDodgeY(options) {
const { maxIterations = 10, maxError = 0.1, padding = 1 } = options;
return function(labels) {
const n = labels.length;
if (n <= 1) return labels;
const y0 = new Map();
const y = new Map();
const h = new Map();
const xx = new Map();
for (const label of labels) {
const { min, max } = getBoundsWithoutConnector(label);
const [x0, y0Val] = min;
const [x1, y1] = max;
y0.set(label, y0Val);
y.set(label, y0Val);
h.set(label, y1 - y0Val);
xx.set(label, [x0, x1]);
}
for (let iter = 0; iter < maxIterations; iter++) {
labels.sort((a, b) => ascending(y.get(a), y.get(b)));
let error = 0;
for (let i = 0; i < n - 1; i++) {
const l0 = labels[i];
let j = i + 1;
let l1;
while ((l1 = labels[j]) && !isSegmentIntersect(xx.get(l0), xx.get(l1))) j += 1;
if (l1) {
const y0Val = y.get(l0);
const h0 = h.get(l0);
const y1 = y.get(l1);
const delta = y1 - (y0Val + h0);
if (delta < padding) {
const newDelta = (padding - delta) / 2;
error = Math.max(error, newDelta);
y.set(l0, y0Val - newDelta);
y.set(l1, y1 + newDelta);
}
}
}
if (error < maxError) break;
}
for (const label of labels) {
label.style.y += y.get(label) - y0.get(label);
}
return labels;
};
}