fix(iced/feedback): 逐环改固定窗判据 + 贴顶边距 + Dialog 下限单测(复审 r3 P2-3/P2-1/P2-2)

D45 封顶轮的最后一改,只动这三条:

- **P2-3 逐环断言(假绿)**:环本来就在固定 x 栅格(24 + 56·i、宽 40),判据从「主色连通簇数
  非空」改成**六个窗逐个测**「窗内墨迹(轨道 ∪ 激活并集)外接成 40(±2)×40(±2)、且不越窗」,
  外加「1..5 号窗必须有激活色弧」。簇数只打印不再当判据。G18-i 从「欠账」改成**已收口**,
  并删掉模块文档里「要么把 6 个环摆成固定 x 栅格」那句错话(它们本来就在栅格上)。
  环带定位也换过两版(「每行墨点 4..20」会把 40 切成 32;「首行..末行」在深色档扩到 274 行),
  最终用「第一个窗里连续有墨、高度≈40 的行组」。
  变异:涂掉 4 个环 → `第 1 个窗里没有任何环墨迹:环丢了` 红;25% 环缩成 20×20 →
  `第 1 个窗里的环应 40×40(±2),实测 20×20` 红。
- **P2-1 贴顶边距**:新增「框顶行 → 条顶行 = 36±1」与「取景框高 = 96±1」两条。
  变异(`HOST_SHORT` 96 → 72,重编重拍):`D:贴底 ⇒ 框顶到条顶应为 36(96 − 48 − 12),实测 12` 红;
  还原后绿。—— 之前只量「条底距框底」,宿高回退成 72 时确实抓不住。
  顺带修了取景框判据本身:条自身(深色)比画布更接近框色,会把条自己认成框(实测差值 1),
  现在要求「框色连续段比条本身更宽」。
- **P2-2 Dialog 下限**:补单测 `effective_width(200.0) == 280.0`(360/800 两点一并钉住),
  并把「不低于官方 280」写进 `Dialog::width` 的文档。
- 欠账登记(只写表、不改代码):G18-j(动作色用全图合计,单卡漂移可能被别卡盖住)、
  G18-k(再 4 处文档残留,不为凑数扩大 diff)、G18-l(取景框判据会把条的抗锯齿底行算成框,
  余量 0.0008,方向是误报;加了「框高 96」后触发概率下降)。
This commit is contained in:
AskaEth committed 2026-11-02 15:56:48 +08:00
1 parent 11d7b64a5e
commit d4e73aacdd
5 files changed
+198 -48

No files matched your search

@@ -275,8 +275,9 @@ fn check_snackbar(shot: &Shot, ui: &Ui) {
if !(near(width, 300) || near(width, 344)) {
continue;
}
// 1px 描边常落在半像素上(两行各 ~50% 覆盖),精确色匹配抓不住;
// 用「更靠近框色而不是画布」判(这一带除了框没有别的灰线)。
// 1px 描边常落在半像素上(两行各 ~50% 覆盖),精确色匹配抓不住 ⇒ 用「更靠近框色
// 而不是画布」判。但条自身(深色)比画布**更接近**框色,所以还要一条:这一行的
// 框色连续段必须**比条本身更宽**(框横贯整个宿主),否则会认成条自己(实测过:差值 1)。
let distance = |pixel: [u8; 3], target: [u8; 3]| -> u32 {
pixel
.iter()
@@ -284,19 +285,44 @@ fn check_snackbar(shot: &Shot, ui: &Ui) {
.map(|(a, b)| u32::from(a.abs_diff(b)))
.sum()
};
let surface_rgb = rgb(surface);
let frame_run = |y: usize| -> usize {
let mut longest = 0;
let mut current = 0;
for x in 0..shot.width {
let pixel = shot.at(x, y);
if distance(pixel, frame) < distance(pixel, surface_rgb) {
current += 1;
longest = longest.max(current);
} else {
current = 0;
}
}
longest
};
// 框的横贯长度 > 条宽 + 10 ⇒ 只有框那一两行满足
let is_frame_row = |y: usize| frame_run(y) >= width + 10;
let frame_row = ((bottom + 1)..shot.height)
.find(|y| {
(0..shot.width)
.filter(|x| {
let pixel = shot.at(*x, *y);
distance(pixel, frame) < distance(pixel, rgb(surface))
})
.count()
>= 200
})
.find(|y| is_frame_row(*y))
.unwrap_or_else(|| {
panic!("D:窄条下方没找到宿主取景框(条 y {top}..{bottom},宽 {width})")
});
let frame_top_row = (0..*top)
.rev()
.find(|y| is_frame_row(*y))
.unwrap_or_else(|| panic!("D:窄条上方没找到宿主取景框(条顶 y {top})"));
// 复审 P2-1:宿高回退到 72(上下留白相等)时这条会红。
assert!(
(*top as i64 - frame_top_row as i64 - 36).abs() <= 1,
"D:贴底 ⇒ 框顶到条顶应为 36(96 − 48 − 12),实测 {}",
*top as i64 - frame_top_row as i64
);
let frame_height = frame_row - frame_top_row + 1;
assert!(
frame_height.abs_diff(96) <= 1,
"D:取景框高应 96(宿主高),实测 {frame_height}"
);
margins.push(frame_row - bottom);
}
assert!(