编辑代码

function OverlapDodgeY(options) {
  const { maxIterations = 10, maxError = 0.1, padding = 1 } = options;

  return function(labels) {
    const n = labels.length;
    if (n <= 1) return labels;

    // 定义存储 y, x0, x, height 的映射,以标签为 key
    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]);
    }

    // 根据最大迭代次数和最大误差,偏移标签的 Y 坐标位置
    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;
        // 找到与当前标签在 x 方向上重叠的下一个标签
        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;
    }

    // 将每个标签的 Y 坐标位置更新到样式中
    for (const label of labels) {
      label.style.y += y.get(label) - y0.get(label);
    }

    return labels;
  };
}