y2kLabelZoneSamples function

List<int> y2kLabelZoneSamples(
  1. int face, {
  2. required int accentHueTenths,
  3. required int depthPermille,
  4. required int refractionPermille,
  5. bool metal = false,
  6. bool accessible = false,
})

Every colour a label of a key with face can sit on inside the label zone, at rest and pressed: the body profile (y2kGelProfile or, for metal faces, y2kMetalProfile) of the face and of its pressed face (y2kPressedFace at accentHueTenths), each adjacent pair of stops that reaches into the zone blended at 0, ½ and 1, every rest / pressed pair blended at 0, ¼, ½, ¾ and 1 (the renderer fades the pressed body over the rest body with paint alpha), plus the caustic glow at its in-zone peak (refractionPermille at rest, × 1.8 pressed). White overlays (cap, sheen, hover, streaks) only raise luminance, so they are not samples: labels are always darker than their faces.

Implementation

List<int> y2kLabelZoneSamples(
  int face, {
  required int accentHueTenths,
  required int depthPermille,
  required int refractionPermille,
  bool metal = false,
  bool accessible = false,
}) {
  final f = face | 0xFF000000;
  final key = (
    f,
    accentHueTenths,
    depthPermille,
    refractionPermille,
    metal,
    accessible,
  );
  final hit = _zoneMemo.remove(key);
  if (hit != null) return _zoneMemo[key] = hit;
  final floor = y2kDeepFloorFor(accessible: accessible);
  final pressed = y2kPressedFace(f, accentHueTenths);
  final rest = metal
      ? y2kMetalProfile(f, depthPermille, floor)
      : y2kGelProfile(f, depthPermille, floor);
  final down = metal
      ? y2kMetalProfile(pressed, depthPermille, floor - 25)
      : y2kGelProfile(pressed, depthPermille, floor - 25);
  final stops = metal ? y2kMetalStops : y2kGelStops;
  final out = <int>[];
  for (var i = 0; i + 1 < stops.length; i++) {
    if (stops[i + 1] <= y2kLabelZoneTop || stops[i] >= y2kLabelZoneBottom) {
      continue;
    }
    for (final s in const [0.0, 0.5, 1.0]) {
      final a = y2kMix(rest[i], rest[i + 1], s);
      final b = y2kMix(down[i], down[i + 1], s);
      for (final k in const [0.0, 0.25, 0.5, 0.75, 1.0]) {
        out.add(y2kMix(a, b, k));
      }
    }
  }
  final g0 = refractionPermille / 1000;
  final g1 = g0 * 1.8 < 0.9 ? g0 * 1.8 : 0.9;
  final m = y2kTone(f, 25, 960);
  final mDown = y2kTone(pressed, 25, 960);
  int glow(int on, int base, double alpha) => compositeOver(
    withAlpha(
      y2kCausticGlow(base),
      (255 * alpha * y2kCausticZoneShare).round(),
    ),
    on,
  );
  out
    ..add(glow(f, f, g0))
    ..add(glow(m, f, g0))
    ..add(glow(pressed, pressed, g1))
    ..add(glow(mDown, pressed, g1));
  final list = List<int>.unmodifiable(out);
  _zoneMemo[key] = list;
  while (_zoneMemo.length > 64) {
    _zoneMemo.remove(_zoneMemo.keys.first);
  }
  return list;
}