repairPair function
Repairs a label/face pair to policy (research §4.3 repair order):
- move the label lightness (keeping hue and chroma);
- if the label had to go to pure black/white or lost most of its chroma, move the face lightness 0.04 away from the label and retry (up to 4 rounds);
- fall back to a neutral label, then pure
#000/#FFF; if even those fail (targets above 4.583:1 on mid-tone faces), keep moving the face away until they pass.
The returned pair always meets policy.minRatio (and the dark-face
target where it applies). Throws only for targets above 21:1.
Implementation
RepairedPair repairPair(
Lch face,
Lch label, {
ContrastPolicy policy = ContrastPolicy.standard,
}) {
if (policy.minRatio > 21) {
throw ArgumentError.value(policy.minRatio, 'minRatio', 'above 21:1');
}
RepairedPair pair(int f, int l, double fl, int rounds) => (
face: f,
label: l,
faceLch: (l: fl, c: face.c, h: face.h),
rounds: rounds,
);
// Steps 1–2: chromatic label, moving the face away when needed.
final labelLighter = label.l >= face.l;
RepairedPair? fallback;
var faceL = face.l;
for (var round = 0; round < 4; round++) {
final faceArgb = oklchToArgb(faceL, face.c, face.h);
final solved = solveLabel(faceArgb, label, policy: policy);
if (solved != null) {
final got = argbToOklch(solved);
final extreme = got.l <= 0.0001 || got.l >= 0.9999;
final stripped = label.c > 0.01 && got.c < label.c * 0.5;
if (!extreme && !stripped) return pair(faceArgb, solved, faceL, round);
fallback ??= pair(faceArgb, solved, faceL, round);
}
faceL = clamp01(faceL + (labelLighter ? -0.04 : 0.04));
}
if (fallback != null) return fallback;
// Step 3: neutral label (then black/white), moving the original face
// toward whichever end gives the label room, until the floor is met.
faceL = face.l;
for (var round = 4; round < 40; round++) {
final faceArgb = oklchToArgb(faceL, face.c, face.h);
final faceY = luminanceArgb(faceArgb);
for (final t in {policy.targetFor(faceY), policy.minRatio}) {
final neutral = solveLightness(
l0: label.l,
c: 0,
hDeg: 0,
againstArgb: faceArgb,
target: t,
);
if (neutral != null) return pair(faceArgb, neutral, faceL, round);
}
faceL = clamp01(faceL + (faceY < yEqualContrast ? -0.04 : 0.04));
}
throw StateError('repairPair did not converge');
}