getTone method

  1. @override
double getTone(
  1. DynamicScheme scheme,
  2. DynamicColor color
)
override

Implementation

@override
double getTone(DynamicScheme scheme, DynamicColor color) {
  final toneDeltaPair = color.toneDeltaPair?.call(scheme);

  // Case 0: tone delta pair.
  if (toneDeltaPair != null) {
    final roleA = toneDeltaPair.roleA;
    final roleB = toneDeltaPair.roleB;
    final polarity = toneDeltaPair.polarity;
    final constraint = toneDeltaPair.constraint;
    final absoluteDelta =
        polarity == .darker ||
            (polarity == .relativeLighter && scheme.isDark) ||
            (polarity == .relativeDarker && !scheme.isDark)
        ? -toneDeltaPair.delta
        : toneDeltaPair.delta;

    final amRoleA = color.name == roleA.name;
    final selfRole = amRoleA ? roleA : roleB;
    final referenceRole = amRoleA ? roleB : roleA;
    var selfTone = selfRole.tone(scheme);
    final referenceTone = referenceRole.getTone(scheme);
    final relativeDelta = absoluteDelta * (amRoleA ? 1.0 : -1.0);

    switch (constraint) {
      case .exact:
        selfTone = MathUtils.clampDouble(
          0.0,
          100.0,
          referenceTone + relativeDelta,
        );
        break;
      case .nearer:
        if (relativeDelta > 0.0) {
          selfTone = MathUtils.clampDouble(
            0.0,
            100.0,
            MathUtils.clampDouble(
              referenceTone,
              referenceTone + relativeDelta,
              selfTone,
            ),
          );
        } else {
          selfTone = MathUtils.clampDouble(
            0.0,
            100.0,
            MathUtils.clampDouble(
              referenceTone + relativeDelta,
              referenceTone,
              selfTone,
            ),
          );
        }
        break;
      case .farther:
        if (relativeDelta > 0.0) {
          selfTone = MathUtils.clampDouble(
            referenceTone + relativeDelta,
            100.0,
            selfTone,
          );
        } else {
          selfTone = MathUtils.clampDouble(
            0.0,
            referenceTone + relativeDelta,
            selfTone,
          );
        }
        break;
    }

    if (color.background != null && color.contrastCurve != null) {
      final background = color.background!(scheme);
      final contrastCurve = color.contrastCurve!(scheme);
      if (background != null && contrastCurve != null) {
        final bgTone = background.getTone(scheme);
        final selfContrast = contrastCurve.get(scheme.contrastLevel);
        selfTone =
            Contrast.ratioOfTones(bgTone, selfTone) >= selfContrast &&
                scheme.contrastLevel >= 0.0
            ? selfTone
            : DynamicColor.foregroundTone(bgTone, selfContrast);
      }
    }

    // This can avoid the awkward tones for background colors including the access fixed colors.
    // Accent fixed dim colors should not be adjusted.
    if (color.isBackground && !color.name.endsWith("_fixed_dim")) {
      if (selfTone >= 57) {
        selfTone = MathUtils.clampDouble(65.0, 100.0, selfTone);
      } else {
        selfTone = MathUtils.clampDouble(0.0, 49.0, selfTone);
      }
    }

    return selfTone;
  } else {
    // Case 1: No tone delta pair; just solve for itself.
    double answer = color.tone(scheme);

    if (color.background?.call(scheme) == null ||
        color.contrastCurve?.call(scheme) == null) {
      return answer; // No adjustment for colors with no background.
    }

    final bgTone = color.background!(scheme)!.getTone(scheme);
    final desiredRatio = color.contrastCurve!(scheme)!.get(
      scheme.contrastLevel,
    );

    // Recalculate the tone from desired contrast ratio if the current
    // contrast ratio is not enough or desired contrast level is decreasing
    // (<0).
    answer =
        Contrast.ratioOfTones(bgTone, answer) >= desiredRatio &&
            scheme.contrastLevel >= 0.0
        ? answer
        : DynamicColor.foregroundTone(bgTone, desiredRatio);

    // This can avoid the awkward tones for background colors including the access fixed colors.
    // Accent fixed dim colors should not be adjusted.
    if (color.isBackground && !color.name.endsWith("_fixed_dim")) {
      if (answer >= 57.0) {
        answer = MathUtils.clampDouble(65.0, 100.0, answer);
      } else {
        answer = MathUtils.clampDouble(0.0, 49.0, answer);
      }
    }

    if (color.secondBackground?.call(scheme) == null) {
      return answer;
    }

    // Case 2: Adjust for dual backgrounds.
    final bgTone1 = color.background!(scheme)!.getTone(scheme);
    final bgTone2 = color.secondBackground!(scheme)!.getTone(scheme);
    final upper = math.max(bgTone1, bgTone2);
    final lower = math.min(bgTone1, bgTone2);

    if (Contrast.ratioOfTones(upper, answer) >= desiredRatio &&
        Contrast.ratioOfTones(lower, answer) >= desiredRatio) {
      return answer;
    }

    // The darkest light tone that satisfies the desired ratio,
    // or -1 if such ratio cannot be reached.
    final lightOption = Contrast.lighter(upper, desiredRatio);

    // The lightest dark tone that satisfies the desired ratio,
    // or -1 if such ratio cannot be reached.
    final darkOption = Contrast.darker(lower, desiredRatio);

    // Tones suitable for the foreground.
    final availables = <double>[?lightOption, ?darkOption];
    final prefersLight =
        DynamicColor.tonePrefersLightForeground(bgTone1) ||
        DynamicColor.tonePrefersLightForeground(bgTone2);
    if (prefersLight) {
      return lightOption ?? 100.0;
    }
    return availables.length == 1 ? availables[0] : (darkOption ?? 0.0);
  }
}