spread static method

List<double> spread(
  1. List<double> centers,
  2. double minGap,
  3. double lo,
  4. double hi,
)

Nudges centers apart so adjacent labels keep at least minGap between their centers, while staying within [lo, hi]. Preserves input order in the returned list; the result is the adjusted center for each input index.

Use for stacked trackball value labels at one x: several series can share a y, and this spreads them into a readable column without reordering.

Implementation

static List<double> spread(
  List<double> centers,
  double minGap,
  double lo,
  double hi,
) {
  final n = centers.length;
  if (n == 0) return const [];
  if (n == 1) return [centers[0].clamp(lo, hi)];

  // Work in sorted-by-position order so neighbors are actually adjacent.
  final order = List<int>.generate(n, (i) => i)
    ..sort((a, b) => centers[a].compareTo(centers[b]));
  final placed = List<double>.filled(n, 0);

  // Forward pass: push each label down past the previous one.
  var cursor = lo;
  for (final idx in order) {
    var p = centers[idx];
    if (p < cursor) p = cursor;
    placed[idx] = p;
    cursor = p + minGap;
  }

  // If we overran the bottom, pull back up from [hi] to fit within bounds.
  if (cursor - minGap > hi) {
    cursor = hi;
    for (var k = n - 1; k >= 0; k--) {
      final idx = order[k];
      var p = placed[idx];
      if (p > cursor) p = cursor;
      placed[idx] = p;
      cursor = p - minGap;
    }
  }
  return placed;
}