nearest method

Hct nearest(
  1. double tu,
  2. double tv
)

In-gamut color at tone whose chromaticity is nearest (tu, tv).

Implementation

Hct nearest(double tu, double tv) {
  final (wu, wv) = whiteUvPoint;
  final du = tu - wu, dv = tv - wv;
  final dist = math.sqrt(du * du + dv * dv);
  if (dist < 1e-9) return Hct.from(0.0, 0.0, tone);
  final exit = rayExit(du, dv);
  if (exit == null) return Hct.from(0.0, 0.0, tone);
  final hue = exit.$2 % 360.0;
  if (exit.$1 <= 1.0) {
    // Target at/outside boundary: nearest point on the cached polyline,
    // pure arithmetic (no solver), then a short boundary polish.
    var bestD = double.infinity;
    var bestHue = hue;
    for (var i = 0; i < 360; i++) {
      final j = (i + 1) % 360;
      final eu = us[j] - us[i], ev = vs[j] - vs[i];
      final len2 = eu * eu + ev * ev;
      var t = 0.0;
      if (len2 > 1e-18) {
        t = (((tu - us[i]) * eu + (tv - vs[i]) * ev) / len2).clamp(0.0, 1.0);
      }
      final qu = us[i] + t * eu - tu, qv = vs[i] + t * ev - tv;
      final d = qu * qu + qv * qv;
      if (d < bestD) {
        bestD = d;
        bestHue = (i + t) % 360.0;
      }
    }
    // Polish: polyline is ~1 degree coarse; ternary-search true boundary.
    var lo = bestHue - 1.0, hi = bestHue + 1.0;
    for (var i = 0; i < _polishIters; i++) {
      final m1 = lo + (hi - lo) / 3.0, m2 = hi - (hi - lo) / 3.0;
      if (_dist2(Hct.from(m1 % 360.0, 250.0, tone), tu, tv) <=
          _dist2(Hct.from(m2 % 360.0, 250.0, tone), tu, tv)) {
        hi = m2;
      } else {
        lo = m1;
      }
    }
    return Hct.from(((lo + hi) / 2.0) % 360.0, 250.0, tone);
  }
  // Inside: refine chroma, then hue, then chroma again. HCT hue lines
  // curve in u'v', so the best hue can sit a few degrees off the
  // ray-exit hue.
  var bestHue = hue;
  var best = _chromaSearch(bestHue, tu, tv);
  var lo = bestHue - _hueWidth, hi = bestHue + _hueWidth;
  for (var i = 0; i < _hueIters; i++) {
    final m1 = lo + (hi - lo) / 3.0, m2 = hi - (hi - lo) / 3.0;
    if (_dist2(Hct.from(m1 % 360.0, best.chroma, tone), tu, tv) <=
        _dist2(Hct.from(m2 % 360.0, best.chroma, tone), tu, tv)) {
      hi = m2;
    } else {
      lo = m1;
    }
  }
  bestHue = ((lo + hi) / 2.0) % 360.0;
  return _chromaSearch(bestHue, tu, tv);
}