spread static method
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;
}