y2kLabelZoneSamples function
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;
}