diff --git a/crates/design/src/color/palettes.rs b/crates/design/src/color/palettes.rs index c1aa192..e2a492e 100644 --- a/crates/design/src/color/palettes.rs +++ b/crates/design/src/color/palettes.rs @@ -66,24 +66,23 @@ pub const DEFAULT_NEUTRAL_CHROMA: f64 = 12.0; /// /// ## 出处:这就是「天蓝」档当前用的那个中性色相 /// -/// 值等于 [`DEFAULT_SEED`](`#3C8DBC`)的 CAM16 色相 **239.51420577630955°**。天蓝档的 -/// 中性调色板本来就是这个色相(`Palettes::from_seed_with_neutral_chroma` 让中性色跟着种子走), -/// 所以它是**沿用**,不是「另选一个冷调」:`Card::scheme` 换用它之后,天蓝卡的 49 个角色 -/// 必须逐值不变(`palette::card` 与 `tests/card_neutral.rs` 都钉了这条)。 +/// 值等于 [`DEFAULT_SEED`](`#3C8DBC`)的 CAM16 色相 **239.51420577630955°**。天蓝档的中性 +/// 调色板本来就是这个色相(`from_seed_with_neutral_chroma` 让中性色跟着种子走),所以它是 +/// **沿用**,不是「另选一个冷调」:`Card::scheme` 换用它之后天蓝卡的 49 个角色必须逐值不变 +/// (`tests/card_neutral.rs` 钉着)。 /// /// ## 为什么要有这个常量(D43) /// -/// 八张内置色卡原先走 TonalSpot 的「中性色也跟种子染色」,于是切卡连窗口底色一起换调子: -/// 浅色档 `surface` 的实测 CAM16 彩度从 **1.29(rose)跨到 12.03(teal)**——tone 98 处暖相 -/// 色域装不下彩度 12,同一个「请求值 12」在暖相上只能兑现 1.29。差 9 倍的底色跳变让用户在 -/// 设置页逐张试色时觉得「每张卡都像换了个应用」。D43 的处置是:**内置色卡固定中性色相, -/// 切卡只换强调色**;动态取色(背景图 / 壁纸)保留逐色染色,那才是用户要的 Monet 体验。 +/// 八张卡原先走 TonalSpot 的「中性色也跟种子染色」,切卡连窗口底色一起换调子:浅色档 +/// `surface` 的实测 CAM16 彩度从 **1.29(rose)跨到 12.03(teal)**——tone 98 处暖相色域装 +/// 不下彩度 12,「请求值 12」在暖相上只能兑现 1.29,9 倍跳变让用户觉得「每张卡都像换了个 +/// 应用」。D43 的处置:**色卡固定中性色相、只换强调色**;动态取色(背景图 / 壁纸)保留逐色 +/// 染色,那才是用户要的 Monet 体验。所以本常量只服务色卡路径,**不要**传给 `Seed::scheme`。 /// -/// 所以这个常量只服务于「色卡路径」,**不要**把它传给取色路径(`Seed::scheme`)。 -/// -/// ⚠️ 换 [`DEFAULT_SEED`] 时本常量会「过期」:`neutral_hue_is_the_default_seed_hue` 用位相等 -/// (`to_bits`)钉住两者的绑定,改了种子它就当场红——那时要想清楚是「色卡跟着新默认色走」 -/// (照抄新色相)还是「色卡保持这条冷调」(把测试改成显式例外),别静默漂移。 +/// ⚠️ 换 [`DEFAULT_SEED`] 时本常量会「过期」:`neutral_hue_is_the_default_seed_hue` 用 +/// 1e-9° 容差钉住两者的绑定(**不用 `to_bits` 位相等**——色相来自跨平台有末位差的 `atan2`), +/// 改了种子它就红——那时要想清楚是「色卡跟着新默认色走」(照抄新色相)还是「色卡保持这条 +/// 冷调」(改成显式例外),别静默漂移。 pub const NEUTRAL_HUE_DEG: f64 = 239.514_205_776_309_55; /// 调色板槽位(M3 的六条 tonal palette)。 @@ -359,14 +358,16 @@ mod tests { #[test] fn neutral_hue_is_the_default_seed_hue() { - // D43 的绑定器:`NEUTRAL_HUE_DEG` 必须是**出厂默认种子的中性色相**,一位不差 - // (位相等,不是近似)。天蓝卡的 49 个角色因此逐值不变;这条一旦红,说明种子换了而 - // 常量没跟着复核——要么照抄新色相(色卡跟着新默认色走),要么把这里改成显式例外。 + // D43 的绑定器。用 1e-9° 容差而非 `to_bits()` 位相等:色相来自 `atan2()`,glibc 与 + // UCRT 理论可差 1 ulp(~1e-14°),位相等会在另一个 OS 的 CI 上红;这条要抓的是「换了 + // `DEFAULT_SEED` 却没复核常量」(偏几十度)。「逐值不变」的位相等钉子在同平台的 + // `card_neutral.rs::sky_card_is_bit_identical_to_the_product_baseline`(两个算出的值 + // 互比,不涉及跨平台字面量)。 let seed_hue = super::super::Hct::from_color(DEFAULT_SEED).hue; - assert_eq!( - NEUTRAL_HUE_DEG.to_bits(), - seed_hue.to_bits(), - "NEUTRAL_HUE_DEG = {NEUTRAL_HUE_DEG:?} 与 DEFAULT_SEED 的色相 {seed_hue:?} 不再相等" + assert!( + (NEUTRAL_HUE_DEG - seed_hue).abs() < 1.0e-9, + "NEUTRAL_HUE_DEG = {NEUTRAL_HUE_DEG:?} 与 DEFAULT_SEED 的色相 {seed_hue:?} \ + 相差超过 1e-9°(换默认种子时:要么照抄新色相,要么把这里改成显式例外)" ); } diff --git a/crates/design/src/palette/resolve/mod.rs b/crates/design/src/palette/resolve/mod.rs index 226f02e..917b6da 100644 --- a/crates/design/src/palette/resolve/mod.rs +++ b/crates/design/src/palette/resolve/mod.rs @@ -180,7 +180,9 @@ fn try_level(input: Option>, card: Card, flavor: SchemeFlavor) -> // 取色方案的表面色**跟着图像种子染色**,只借当前色卡的中性彩度定浓淡(D43): // 这是与色卡路径刻意不同的一处——色卡固定中性色相、只换强调色,取色保留整幅染色 - // (那是用户要的 Monet 体验)。别把两条路径「统一处理」,`tests/card_neutral.rs` 有反证。 + // (那是用户要的 Monet 体验)。别把两条路径「统一处理」:这条接线的守护测试是 + // `tests/card_neutral.rs::resolved_image_scheme_keeps_tinting_from_the_seed`(走的就是 + // 本函数所在的 `resolve_source`;只测 `Seed::scheme` 的那条覆盖不到这里,复审 r1 p1-1)。 let (scheme, adjustments) = converge(seed.scheme(flavor, card.neutral_chroma)); let report = check(&scheme); if !report.is_clean() {