feat(design): M65 真 HCT 色彩 —— CAM16 + ColorSpec2021 + 官方逐角色对齐
把 M61 的「OKLCh 近似 CAM16 + gamut 硬裁」换成官方的 HCT 算法,与 material-color-utilities(Apache-2.0)逐角色对上:baseline 49 角色 × light/dark、8 个种子 × 12 个强调族角色 × 2 模式,**实测每通道误差 0** (判据写入测试:≤1)。 实现(逐函数对照官方 Java 重写为 Rust,f64 内部精度): - color/cam16.rs + viewing_conditions.rs:CAM16 色相/彩度(D65、200 lux、 背景 L* 50),默认观察条件的中间量与 Java 实测逐位对齐 - color/hct_solver.rs + critical_planes.rs:官方 gamut 映射(Newton 迭代 + 色调平面上的色相二分,255 项关键平面表原样搬运),只压彩度不硬裁 - color/hct.rs:HCT 坐标(hue/chroma/tone),公开给工具与测试 - color/space.rs:官方 ColorUtils 的 sRGB↔线性↔XYZ↔L* 换算(矩阵、阈值、 取整口径一致) - color/palettes.rs:Tonal Spot 官方彩度常数 36/16/24/6/8,error 25°/84 - color/scheme.rs:tone 表换 ColorSpec2021(on-*container 10→30、 background 99/10→98/6) 两个旧缺陷关门: - primary-container 与 secondary-container 不再撞色(彩度 36 vs 16, tone 90 装得下,无需「共享基准压彩度」补丁);reference_chroma / tone_scaled 随之删除 - 强调色色相不再被 gamut 硬裁(只压彩度,实测偏移 <1°) 对齐证据:tests/official_baseline.rs(98 项 + 12 个 HCT 探针)、 tests/official_accent.rs(192 项)。期望值是把官方 Java 实现本机编译运行 的输出抄本,并用 Flutter 的 ColorScheme.fromSeed 用例交叉验证 (#00FF00 → #406836、#000000 → #8C4A60、#FFFFFF → #006874 一致)。 诚实口径:只做 Tonal Spot 变体 + contrastLevel 0;对比度三档、其他变体、 壁纸多色取色、非默认观察条件都在模块文档的 gap 表里登记。 注:token_pins 的 tone 值变化来自官方算法(ColorSpec2021),不是为过测试而改。 NOTICE 增「Google Material Color Utilities」归属(Apache-2.0 要求, 含逐文件映射与对齐方法)。
This commit is contained in:
1 parent
958ced2d7e
commit
29f65b8e6f
16 files changed
+1786
-373
No files matched your search
@@ -8,7 +8,7 @@ use unlucky_design::icons::IconKind;
|
||||
use unlucky_design::motion::{DurationToken, Easing};
|
||||
use unlucky_design::shape::ShapeScale;
|
||||
|
||||
fn expect_tone(role: Role, light: f32, dark: f32) {
|
||||
fn expect_tone(role: Role, light: f64, dark: f64) {
|
||||
assert_eq!(
|
||||
Scheme::tone_of(role, SchemeFlavor::Light),
|
||||
light,
|
||||
@@ -28,24 +28,24 @@ fn all_forty_nine_tones_are_pinned() {
|
||||
expect_tone(Role::Primary, 40.0, 80.0);
|
||||
expect_tone(Role::OnPrimary, 100.0, 20.0);
|
||||
expect_tone(Role::PrimaryContainer, 90.0, 30.0);
|
||||
expect_tone(Role::OnPrimaryContainer, 10.0, 90.0);
|
||||
expect_tone(Role::OnPrimaryContainer, 30.0, 90.0);
|
||||
expect_tone(Role::Secondary, 40.0, 80.0);
|
||||
expect_tone(Role::OnSecondary, 100.0, 20.0);
|
||||
expect_tone(Role::SecondaryContainer, 90.0, 30.0);
|
||||
expect_tone(Role::OnSecondaryContainer, 10.0, 90.0);
|
||||
expect_tone(Role::OnSecondaryContainer, 30.0, 90.0);
|
||||
expect_tone(Role::Tertiary, 40.0, 80.0);
|
||||
expect_tone(Role::OnTertiary, 100.0, 20.0);
|
||||
expect_tone(Role::TertiaryContainer, 90.0, 30.0);
|
||||
expect_tone(Role::OnTertiaryContainer, 10.0, 90.0);
|
||||
expect_tone(Role::OnTertiaryContainer, 30.0, 90.0);
|
||||
expect_tone(Role::Error, 40.0, 80.0);
|
||||
expect_tone(Role::OnError, 100.0, 20.0);
|
||||
expect_tone(Role::ErrorContainer, 90.0, 30.0);
|
||||
expect_tone(Role::OnErrorContainer, 10.0, 90.0);
|
||||
expect_tone(Role::OnErrorContainer, 30.0, 90.0);
|
||||
expect_tone(Role::Surface, 98.0, 6.0);
|
||||
expect_tone(Role::OnSurface, 10.0, 90.0);
|
||||
expect_tone(Role::SurfaceVariant, 90.0, 30.0);
|
||||
expect_tone(Role::OnSurfaceVariant, 30.0, 80.0);
|
||||
expect_tone(Role::Background, 99.0, 10.0);
|
||||
expect_tone(Role::Background, 98.0, 6.0);
|
||||
expect_tone(Role::OnBackground, 10.0, 90.0);
|
||||
expect_tone(Role::SurfaceDim, 87.0, 6.0);
|
||||
expect_tone(Role::SurfaceBright, 98.0, 24.0);
|
||||
@@ -241,8 +241,8 @@ fn same_family_containers_do_not_collide() {
|
||||
"同族容器撞色(彩度压缩没生效):{primary:?}"
|
||||
);
|
||||
// secondary-container 必须比 primary-container 更灰(M3 的「次级」语义)。
|
||||
let c_primary = unlucky_design::color::space::Oklch::from_color(primary).chroma;
|
||||
let c_secondary = unlucky_design::color::space::Oklch::from_color(secondary).chroma;
|
||||
let c_primary = unlucky_design::color::Hct::from_color(primary).chroma;
|
||||
let c_secondary = unlucky_design::color::Hct::from_color(secondary).chroma;
|
||||
assert!(
|
||||
c_secondary < c_primary,
|
||||
"secondary-container 彩度 {c_secondary} 应低于 primary-container {c_primary}"
|
||||
|
||||
Reference in new issue
Block a user