From 2249956b2f0bfaad96d70aab94a3e15e9aa18d64 Mon Sep 17 00:00:00 2001 From: yuksblog Date: Mon, 13 Jul 2026 16:35:56 +0900 Subject: [PATCH 1/2] fix: guard TemperatureCache.complement against a zero temperature range A seed whose HCT sits at a tone extreme (#FFFFFF, #000000, ...) collapses the gamut to a single point, so every hue in the 361-entry sweep resolves to the same color and the warmest and coldest entries are identical. complement divided by that zero range and raised ZeroDivisionError, which is reachable from any scheme that needs the complement -- SchemeFidelity, for instance, via get_tertiary_palette. Return the input when the range is zero: every candidate has the same temperature, so none is a better complement than another. This mirrors the zero-range case that relative_temperature already handles, and leaves every non-degenerate seed bit-for-bit unchanged. --- materialyoucolor/temperature/temperature_cache.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/materialyoucolor/temperature/temperature_cache.py b/materialyoucolor/temperature/temperature_cache.py index 52c5de6..5c442a4 100644 --- a/materialyoucolor/temperature/temperature_cache.py +++ b/materialyoucolor/temperature/temperature_cache.py @@ -117,6 +117,13 @@ def complement(self) -> Hct: warmest_hue = self.warmest.hue warmest_temp = self.temps_by_hct.get(self.warmest, 0.0) range_temp = warmest_temp - coldest_temp + + # At a tone extreme every hue resolves to the same color, leaving no range to + # divide by; return the input, as relative_temperature does in that case. + if range_temp == 0.0: + self.complement_cache = self.hcts_by_hue[round(self.input.hue)] + return self.complement_cache + start_hue_is_coldest_to_warmest = TemperatureCache.is_between( self.input.hue, coldest_hue, warmest_hue ) From ca9a5919ddfbb9886422da09c8eafa279f38c4b8 Mon Sep 17 00:00:00 2001 From: yuksblog Date: Mon, 13 Jul 2026 16:35:56 +0900 Subject: [PATCH 2/2] test: cover seeds at a tone extreme CI already runs test_all.py and fails the build on a non-zero exit, so the degenerate seeds fit as an extra construction check alongside the existing ones: without the preceding fix, this raises ZeroDivisionError and the wheel build goes red. Placed ahead of the early exit on Windows so the check runs on every platform. --- tests/test_all.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_all.py b/tests/test_all.py index 1d1d715..59637e5 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -19,6 +19,7 @@ from materialyoucolor.scheme.scheme import Scheme from materialyoucolor.scheme.scheme_android import SchemeAndroid from materialyoucolor.score.score import Score +from materialyoucolor.temperature.temperature_cache import TemperatureCache from materialyoucolor.utils.color_utils import hex_from_rgba, rgba_from_argb @@ -119,6 +120,21 @@ def get_current_rss_mb(): selected = Score.score(colors) + +# Regression: tone extremes leave TemperatureCache with a zero temperature range, +# which used to make complement raise ZeroDivisionError (issue #28). +def check_tone_extremes(): + for argb in [0xFFFFFFFF, 0xFF000000, 0xFF010101]: + source = Hct.from_int(argb) + assert TemperatureCache(source).complement.to_int() == source.to_int() + for spec_version in ["2021", "2025"]: + for is_dark in [False, True]: + # SchemeFidelity reaches complement via get_tertiary_palette. + SchemeFidelity(source, is_dark, 0.0, spec_version=spec_version) + + +check_tone_extremes() + if os.name == "nt": exit(0)