1use super::locate::TierMap;
17use super::stats::CellRef;
18use super::{Footprint, Frame, BLOCK, CELL, TIER_CLEAN, TIER_VEIL};
19
20pub const SIDE: usize = BLOCK / CELL;
22pub const CELLS: usize = SIDE * SIDE;
23const _: () = assert!(CELL == 2);
25const PIXELS: usize = BLOCK * BLOCK;
26
27#[derive(Clone, Copy)]
28pub struct BlockVote {
29 pub tier: i8,
31 pub clean_views: u16,
32 pub changed_views: u16,
33}
34
35#[derive(Clone, Copy, Debug)]
37pub struct Grid {
38 pub gx: i64,
40 pub gy: i64,
41 pub bw: usize,
42 pub bh: usize,
43}
44
45impl Grid {
46 pub fn covering(x: i64, y: i64, width: usize, height: usize) -> Self {
48 let b = BLOCK as i64;
49 let (gx, gy) = (x.div_euclid(b), y.div_euclid(b));
50 let (gx1, gy1) = (
51 (x + width as i64 + b - 1).div_euclid(b),
52 (y + height as i64 + b - 1).div_euclid(b),
53 );
54 Self {
55 gx,
56 gy,
57 bw: (gx1 - gx).max(0) as usize,
58 bh: (gy1 - gy).max(0) as usize,
59 }
60 }
61
62 #[inline]
64 pub fn origin(&self, b: usize) -> (i64, i64) {
65 (
66 (self.gx + (b % self.bw) as i64) * BLOCK as i64,
67 (self.gy + (b / self.bw) as i64) * BLOCK as i64,
68 )
69 }
70
71 fn span(&self, footprint: &Footprint<'_>, pose: (i64, i64)) -> (usize, usize, usize, usize) {
73 let b = BLOCK as i64;
74 let (x0, y0) = (footprint.x + pose.0, footprint.y + pose.1);
75 let (x1, y1) = (x0 + footprint.width as i64, y0 + footprint.height as i64);
76 let clamp = |v: i64, n: usize| v.clamp(0, n as i64) as usize;
77 (
78 clamp((x0 + b - 1).div_euclid(b) - self.gx, self.bw),
79 clamp((y0 + b - 1).div_euclid(b) - self.gy, self.bh),
80 clamp(x1.div_euclid(b) - self.gx, self.bw),
81 clamp(y1.div_euclid(b) - self.gy, self.bh),
82 )
83 }
84}
85
86fn view(
90 frame: &Frame<'_>,
91 footprint: &Footprint<'_>,
92 tiers: &TierMap<'_>,
93 pose: (i64, i64),
94 wx0: i64,
95 wy0: i64,
96 out: &mut [[u8; 3]; CELLS],
97) -> Option<u8> {
98 let (sx0, sy0) = (wx0 - pose.0, wy0 - pose.1);
99 let (lx0, ly0) = (sx0 - footprint.x, sy0 - footprint.y);
100 let b = BLOCK as i64;
101 if sx0 < 0 || sy0 < 0 || sx0 + b > frame.width as i64 || sy0 + b > frame.height as i64 {
102 return None;
103 }
104 if lx0 < 0 || ly0 < 0 || lx0 + b > footprint.width as i64 || ly0 + b > footprint.height as i64 {
105 return None;
106 }
107 debug_assert_eq!((tiers.x, tiers.y), (footprint.x, footprint.y));
108 let (w, fw) = (frame.width, footprint.width);
109 let (lx0, ly0, sx0, sy0) = (lx0 as usize, ly0 as usize, sx0 as usize, sy0 as usize);
110 if (0..BLOCK).any(|yy| footprint.mask[(ly0 + yy) * fw + lx0..][..BLOCK].contains(&0)) {
111 return None;
112 }
113 let q = (CELL * CELL) as u32;
115 for (cy, cells) in out.chunks_exact_mut(SIDE).enumerate() {
116 let top = &frame.rgba[((sy0 + cy * CELL) * w + sx0) * 4..][..BLOCK * 4];
117 let bottom = &frame.rgba[((sy0 + cy * CELL + 1) * w + sx0) * 4..][..BLOCK * 4];
118 for ((cell, t), b) in cells
119 .iter_mut()
120 .zip(top.chunks_exact(8))
121 .zip(bottom.chunks_exact(8))
122 {
123 let mean = |c: usize| {
124 ((t[c] as u32 + t[c + 4] as u32 + b[c] as u32 + b[c + 4] as u32 + q / 2) / q) as u8
125 };
126 *cell = [mean(0), mean(1), mean(2)];
127 }
128 }
129 let mut worst = TIER_CLEAN;
131 for ty in ly0 / CELL..=(ly0 + BLOCK - 1) / CELL {
132 let row = &tiers.tier[ty * tiers.cw..(ty + 1) * tiers.cw];
133 for &t in &row[lx0 / CELL..=(lx0 + BLOCK - 1) / CELL] {
134 worst = worst.min(t);
135 }
136 }
137 Some(worst)
138}
139
140const OUTLIER: i32 = 48;
144
145#[inline]
146fn cell_luma(c: [u8; 3]) -> i32 {
147 super::luma(c[0], c[1], c[2]) as i32
148}
149
150fn compare(a: &[[u8; 3]], b: &[[u8; 3]]) -> (u32, u32, u32) {
152 let (mut sum, mut bias, mut off) = (0u32, 0i32, 0u32);
153 for (p, q) in a.iter().zip(b) {
154 for c in 0..3 {
155 let d = p[c] as i32 - q[c] as i32;
156 sum += d.unsigned_abs();
157 bias += d;
158 }
159 off += ((cell_luma(*p) - cell_luma(*q)).abs() > OUTLIER) as u32;
160 }
161 let n = (a.len() * 3) as u32;
162 (sum / n, bias.unsigned_abs() / n, off)
163}
164
165#[inline]
168fn agrees(d: (u32, u32, u32), tau: i32) -> bool {
169 d.0 <= (tau as u32 / 4).max(2) && d.1 <= 2 && d.2 <= 2
170}
171
172#[inline]
175fn agrees_strictly(
176 d: (u32, u32, u32),
177 tau: i32,
178 observed: &BlockSignature,
179 reference: &impl CellRef,
180 world: (i64, i64),
181) -> bool {
182 if !agrees(d, tau) || d.2 != 0 {
183 return false;
184 }
185 let noise = (tau / 4).max(2);
189 let quiet_limit = 2 * noise;
190 if quiet_limit >= OUTLIER {
191 return true;
192 }
193 let (cx, cy) = (
194 world.0.div_euclid(CELL as i64),
195 world.1.div_euclid(CELL as i64),
196 );
197 for (k, sample) in observed.iter().enumerate() {
198 let (x, y) = (cx + (k % SIDE) as i64, cy + (k / SIDE) as i64);
199 let Some(value) = reference.cell(x, y) else {
200 return false;
201 };
202 let value = cell_luma(value);
203 let residual = (cell_luma(*sample) - value).abs();
204 if residual <= quiet_limit {
205 continue;
206 }
207 let mut contrast = 0;
208 'neighbours: for yy in y - 1..=y + 1 {
211 for xx in x - 1..=x + 1 {
212 let Some(neighbour) = reference.cell(xx, yy) else {
213 contrast = OUTLIER;
214 break 'neighbours;
215 };
216 contrast = contrast.max((cell_luma(neighbour) - value).abs());
217 }
218 }
219 if residual > quiet_limit.max(contrast + noise) {
220 return false;
221 }
222 }
223 true
224}
225
226const CHANGED: u32 = 8;
228const SHIFTED: u32 = 3;
229
230pub struct Consensus {
233 pub grid: Grid,
234 pub sig: Vec<[u8; 3]>,
235 count: Vec<i16>,
236 pub vote: Vec<BlockVote>,
237 phase_explained: Vec<u16>,
239 viewport: (usize, usize),
242}
243
244impl Consensus {
245 pub fn new(grid: Grid) -> Self {
246 let n = grid.bw * grid.bh;
247 Self {
248 grid,
249 sig: vec![[0; 3]; n * CELLS],
250 count: vec![0; n * CELLS],
251 phase_explained: vec![0; n],
252 viewport: (0, 0),
253 vote: vec![
254 BlockVote {
255 tier: -1,
256 clean_views: 0,
257 changed_views: 0
258 };
259 n
260 ],
261 }
262 }
263
264 pub fn observe(
266 &mut self,
267 frame: &Frame<'_>,
268 footprint: &Footprint<'_>,
269 tiers: &TierMap<'_>,
270 pose: (i64, i64),
271 ) {
272 let width = ((footprint.x + footprint.width as i64).min(frame.width as i64)
273 - footprint.x.max(0))
274 .max(0) as usize;
275 let height = ((footprint.y + footprint.height as i64).min(frame.height as i64)
276 - footprint.y.max(0))
277 .max(0) as usize;
278 self.viewport.0 = self.viewport.0.max(width);
279 self.viewport.1 = self.viewport.1.max(height);
280 let (bx0, by0, bx1, by1) = self.grid.span(footprint, pose);
281 if bx1 <= bx0 || by1 <= by0 {
282 return;
283 }
284 let rows = by1 - by0;
285 let chunks = crate::kernels::pool::chunks_for(rows * (bx1 - bx0), 64);
286 let grid = self.grid;
287 let (sig_p, count_p, vote_p, phase_p) = (
288 crate::kernels::pool::SyncPtr(self.sig.as_mut_ptr()),
289 crate::kernels::pool::SyncPtr(self.count.as_mut_ptr()),
290 crate::kernels::pool::SyncPtr(self.vote.as_mut_ptr()),
291 crate::kernels::pool::SyncPtr(self.phase_explained.as_mut_ptr()),
292 );
293 crate::kernels::pool::par_for(chunks, |k| {
294 let mut sig = [[0u8; 3]; CELLS];
295 for by in by0 + crate::kernels::pool::split(rows, chunks, k)
296 ..by0 + crate::kernels::pool::split(rows, chunks, k + 1)
297 {
298 for bx in bx0..bx1 {
299 let b = by * grid.bw + bx;
300 let (wx0, wy0) = grid.origin(b);
301 let Some(tier) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) else {
302 continue;
303 };
304 let (cand, count, v) = unsafe {
306 (
307 std::slice::from_raw_parts_mut(sig_p.get().add(b * CELLS), CELLS),
308 std::slice::from_raw_parts_mut(count_p.get().add(b * CELLS), CELLS),
309 &mut *vote_p.get().add(b),
310 )
311 };
312 if vote(cand, count, v, &sig, tier) {
313 let explained = unsafe { &mut *phase_p.get().add(b) };
315 *explained = explained.saturating_add(1);
316 }
317 }
318 }
319 });
320 }
321}
322
323fn small_phase_agrees(a: &[[u8; 3]; CELLS], b: &[[u8; 3]]) -> bool {
328 for dy in -1isize..=1 {
329 for dx in -1isize..=1 {
330 if dx == 0 && dy == 0 {
331 continue;
332 }
333 let x0 = (-dx).max(0) as usize;
334 let y0 = (-dy).max(0) as usize;
335 let x1 = (SIDE as isize - dx).min(SIDE as isize) as usize;
336 let y1 = (SIDE as isize - dy).min(SIDE as isize) as usize;
337 let n = ((x1 - x0) * (y1 - y0) * 3) as u32;
338 let mut sum = 0u32;
339 let mut bias = 0i32;
340 let mut unshifted = 0u32;
341 'rows: for y in y0..y1 {
342 for x in x0..x1 {
343 let p = a[y * SIDE + x];
344 let q = b[((y as isize + dy) as usize) * SIDE + (x as isize + dx) as usize];
345 for k in 0..3 {
346 let d = p[k] as i32 - q[k] as i32;
347 sum += d.unsigned_abs();
348 bias += d;
349 unshifted += (p[k] as i32 - b[y * SIDE + x][k] as i32).unsigned_abs();
350 }
351 if sum > CHANGED * n {
352 break 'rows;
353 }
354 }
355 }
356 if sum <= CHANGED * n && bias.unsigned_abs() <= SHIFTED * n && unshifted > CHANGED * n {
357 return true;
358 }
359 }
360 }
361 false
362}
363
364pub(super) fn contradicts(
372 sig: &[[u8; 3]; CELLS],
373 reference: &[[u8; 3]],
374 tau: i32,
375 settled: impl FnOnce() -> bool,
376) -> bool {
377 let d = compare(sig, reference);
378 if d.2 > 0 {
379 return true;
380 }
381 let (lo, hi) = reference.iter().fold((i32::MAX, i32::MIN), |(lo, hi), &c| {
384 let l = cell_luma(c);
385 (lo.min(l), hi.max(l))
386 });
387 hi - lo <= 2 * (tau / 4).max(2)
388 && (d.0 > CHANGED || d.1 > SHIFTED)
389 && !small_phase_agrees(sig, reference)
390 && !settled()
391}
392
393#[allow(clippy::too_many_arguments)]
395pub(super) fn settled(
396 sig: &[[u8; 3]; CELLS],
397 previous: Option<&Frame<'_>>,
398 footprint: &Footprint<'_>,
399 tiers: &TierMap<'_>,
400 pose: (i64, i64),
401 wx0: i64,
402 wy0: i64,
403 tau: i32,
404) -> bool {
405 let mut prior = [[0u8; 3]; CELLS];
406 previous
407 .and_then(|before| view(before, footprint, tiers, pose, wx0, wy0, &mut prior))
408 .is_some_and(|t| t >= TIER_VEIL && agrees(compare(sig, &prior), tau))
409}
410
411fn vote(
413 cand: &mut [[u8; 3]],
414 count: &mut [i16],
415 v: &mut BlockVote,
416 sig: &[[u8; 3]; CELLS],
417 tier: u8,
418) -> bool {
419 let mut phase_explained = false;
420 if tier == TIER_CLEAN {
421 if v.tier == TIER_CLEAN as i8 {
422 let d = compare(sig, cand);
423 if d.0 > CHANGED || d.1 > SHIFTED {
424 phase_explained = small_phase_agrees(sig, cand);
425 v.changed_views = v.changed_views.saturating_add(1);
426 }
427 }
428 v.clean_views = v.clean_views.saturating_add(1);
429 }
430 let t = tier as i8;
431 if t > v.tier {
432 cand.copy_from_slice(sig);
433 count.fill(1);
434 v.tier = t;
435 } else if t == v.tier {
436 for k in 0..CELLS {
439 if (cell_luma(sig[k]) - cell_luma(cand[k])).abs() <= OUTLIER {
440 count[k] = count[k].saturating_add(1);
441 } else if count[k] <= 1 {
442 cand[k] = sig[k];
443 count[k] = 1;
444 } else {
445 count[k] -= 1;
446 }
447 }
448 }
449 phase_explained
450}
451
452impl CellRef for Consensus {
453 #[inline]
454 fn cell(&self, cx: i64, cy: i64) -> Option<[u8; 3]> {
455 let (wx, wy) = (cx * CELL as i64, cy * CELL as i64);
456 let (bx, by) = (
457 wx.div_euclid(BLOCK as i64) - self.grid.gx,
458 wy.div_euclid(BLOCK as i64) - self.grid.gy,
459 );
460 if bx < 0 || by < 0 || bx >= self.grid.bw as i64 || by >= self.grid.bh as i64 {
461 return None;
462 }
463 let b = by as usize * self.grid.bw + bx as usize;
464 if self.vote[b].tier < 0 {
465 return None;
466 }
467 let (ix, iy) = (
468 (wx.rem_euclid(BLOCK as i64) as usize) / CELL,
469 (wy.rem_euclid(BLOCK as i64) as usize) / CELL,
470 );
471 Some(self.sig[b * CELLS + iy * SIDE + ix])
472 }
473 fn cells(&self) -> (i64, i64, i64, i64) {
474 let s = SIDE as i64;
475 (
476 self.grid.gx * s,
477 self.grid.gy * s,
478 (self.grid.gx + self.grid.bw as i64) * s,
479 (self.grid.gy + self.grid.bh as i64) * s,
480 )
481 }
482}
483
484mod moments;
485
486type BlockSignature = [[u8; 3]; CELLS];
487
488#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
490pub struct MomentRank {
491 complete: bool,
492 untainted: bool,
493 tier: u8,
494 corroborated: bool,
495 interior: bool,
496 preference: usize,
498}
499
500pub struct Component {
502 pub bx: usize,
503 pub by: usize,
504 pub bw: usize,
505 pub bh: usize,
506 pub mask: Vec<bool>,
507 pub dynamic: Vec<bool>,
509 pub best: Option<MomentRank>,
511 last_complete: Vec<BlockSignature>,
513}
514
515pub fn components(c: &Consensus) -> Vec<Component> {
519 let changing =
520 |v: &BlockVote, changed: u16| changed >= 2 && changed as u32 * 4 >= v.clean_views as u32;
521 let raw: Vec<bool> = c
522 .vote
523 .iter()
524 .map(|v| changing(v, v.changed_views))
525 .collect();
526 let groups = partition_components(c, &raw, None);
527 if c.viewport.0 == 0 || c.viewport.1 == 0 {
528 return groups;
529 }
530 let structural: Vec<bool> = c
531 .vote
532 .iter()
533 .zip(&c.phase_explained)
534 .map(|(v, &phase)| changing(v, v.changed_views.saturating_sub(phase)))
535 .collect();
536 if raw == structural {
537 return groups;
538 }
539 let mut owners = vec![-1i32; raw.len()];
540 let mut split = vec![false; groups.len()];
541 for (id, group) in groups.iter().enumerate() {
542 let (mut left, mut top, mut right, mut bottom) = (group.bw, group.bh, 0, 0);
543 let mut ambiguous = false;
544 for y in 0..group.bh {
545 for x in 0..group.bw {
546 let k = y * group.bw + x;
547 if group.mask[k] {
548 let b = (group.by + y) * c.grid.bw + group.bx + x;
549 owners[b] = id as i32;
550 if group.dynamic[k] {
551 left = left.min(x);
552 top = top.min(y);
553 right = right.max(x + 1);
554 bottom = bottom.max(y + 1);
555 ambiguous |= !structural[b];
556 }
557 }
558 }
559 }
560 let edge_uncertainty = 2 * (BLOCK - 1);
564 let min_width = ((right - left) * BLOCK).saturating_sub(edge_uncertainty);
565 let min_height = ((bottom - top) * BLOCK).saturating_sub(edge_uncertainty);
566 split[id] = ambiguous && (min_width > c.viewport.0 || min_height > c.viewport.1);
567 }
568 if !split.iter().any(|&v| v) {
569 return groups;
570 }
571 let fallback: Vec<bool> = structural
572 .iter()
573 .zip(&owners)
574 .map(|(&dynamic, &owner)| dynamic && owner >= 0 && split[owner as usize])
575 .collect();
576 let mut result: Vec<Component> = groups
577 .into_iter()
578 .enumerate()
579 .filter_map(|(id, group)| (!split[id]).then_some(group))
580 .collect();
581 result.extend(partition_components(c, &fallback, Some(&owners)));
582 result
583}
584
585fn partition_components(
587 c: &Consensus,
588 dynamic: &[bool],
589 parents: Option<&[i32]>,
590) -> Vec<Component> {
591 let (bw, bh) = (c.grid.bw, c.grid.bh);
592 let mut labels = vec![0usize; bw * bh];
593 let mut count = 0;
594 for start in 0..labels.len() {
595 if !dynamic[start] || labels[start] != 0 {
596 continue;
597 }
598 count += 1;
599 labels[start] = count;
600 let mut stack = vec![start];
601 while let Some(i) = stack.pop() {
602 let (x, y) = (i % bw, i / bw);
603 for yy in y.saturating_sub(1)..=(y + 1).min(bh - 1) {
604 for xx in x.saturating_sub(1)..=(x + 1).min(bw - 1) {
605 let j = yy * bw + xx;
606 if dynamic[j] && labels[j] == 0 {
607 labels[j] = count;
608 stack.push(j);
609 }
610 }
611 }
612 }
613 }
614 let mut owners = labels.clone();
618 for (i, &id) in labels.iter().enumerate() {
619 if id == 0 {
620 continue;
621 }
622 let (x, y) = (i % bw, i / bw);
623 for yy in y.saturating_sub(1)..=(y + 1).min(bh - 1) {
624 for xx in x.saturating_sub(1)..=(x + 1).min(bw - 1) {
625 let j = yy * bw + xx;
626 if labels[j] == 0
627 && c.vote[j].tier >= 0
628 && (owners[j] == 0 || id < owners[j])
629 && parents.is_none_or(|parents| parents[i] == parents[j])
630 {
631 owners[j] = id;
632 }
633 }
634 }
635 }
636 let mut members = vec![Vec::new(); count];
637 for (i, &id) in owners.iter().enumerate() {
638 if id > 0 {
639 members[id - 1].push(i);
640 }
641 }
642 members
643 .into_iter()
644 .map(|members| {
645 let x0 = members.iter().map(|&i| i % bw).min().unwrap();
646 let x1 = members.iter().map(|&i| i % bw).max().unwrap();
647 let y0 = members.iter().map(|&i| i / bw).min().unwrap();
648 let y1 = members.iter().map(|&i| i / bw).max().unwrap();
649 let (cw, ch) = (x1 - x0 + 1, y1 - y0 + 1);
650 let mut mask = vec![false; cw * ch];
651 let mut changing = vec![false; cw * ch];
652 for i in members {
653 let k = (i / bw - y0) * cw + i % bw - x0;
654 mask[k] = true;
655 changing[k] = dynamic[i];
656 }
657 Component {
658 bx: x0,
659 by: y0,
660 bw: cw,
661 bh: ch,
662 mask,
663 dynamic: changing,
664 best: None,
665 last_complete: Vec::new(),
666 }
667 })
668 .collect()
669}
670
671#[derive(Clone, Copy, Debug, PartialEq, Eq)]
673#[repr(u8)]
674pub enum Kind {
675 None = 0,
677 Coherent = 1,
679 Component = 2,
681 Provisional = 3,
683 Fallback = 4,
686 Edge = 5,
689}
690
691pub const CENTRAL: i64 = 48;
694
695#[derive(Clone, Copy, Debug, PartialEq, Eq)]
697pub struct BlockWrite {
698 pub canvas: u32,
700 pub gx: i64,
701 pub gy: i64,
702 pub sx: i64,
703 pub sy: i64,
704 pub frame: u32,
705 pub kind: Kind,
706}
707
708pub const UNTOUCHED: i32 = -1;
711pub const MIXED: i32 = -2;
713
714pub struct Deferred {
715 pub gx: i64,
716 pub gy: i64,
717 pub frame: u32,
718 pub kind: Kind,
719 pub rgba: Box<[u8; PIXELS * 4]>,
720}
721
722struct Fallback {
723 flat_witness: Option<[u8; 3]>,
725 cand: [[u8; 3]; PIXELS],
726 count: [i16; PIXELS],
727 tier: [i8; PIXELS],
728 main: [[u8; 4]; PIXELS],
730 main_tier: [i8; PIXELS],
731}
732
733#[derive(Clone, Copy, Default)]
734enum WitnessAction {
735 #[default]
736 Keep,
737 Store,
738 Clear,
739}
740
741#[derive(Clone, Copy, Default)]
743struct Act {
744 corroborated_flat: bool,
745 witness: WitnessAction,
746 seen: bool,
747 tier: u8,
748 strict: bool,
750 settled: bool,
752 loose: bool,
753 inset: i16,
754 main: bool,
756}
757
758fn fallback_view(
762 fb: &mut Fallback,
763 frame: &Frame<'_>,
764 footprint: &Footprint<'_>,
765 tiers: &TierMap<'_>,
766 pose: (i64, i64),
767 origin: (i64, i64),
768 tau: i32,
769) {
770 let (w, near) = (frame.width, (tau / 2).max(2));
771 for j in 0..PIXELS {
772 let (sx, sy) = (
773 origin.0 + (j % BLOCK) as i64 - pose.0,
774 origin.1 + (j / BLOCK) as i64 - pose.1,
775 );
776 if sx < 0
777 || sy < 0
778 || sx >= w as i64
779 || sy >= frame.height as i64
780 || !footprint.contains(sx, sy)
781 {
782 continue;
783 }
784 let fi = (sy as usize * w + sx as usize) * 4;
785 let obs = [frame.rgba[fi], frame.rgba[fi + 1], frame.rgba[fi + 2]];
786 let t = tiers.at(sx, sy) as i8;
787 let m = fb.main[j];
788 if m[3] == 255 && (0..3).all(|k| (obs[k] as i32 - m[k] as i32).abs() <= near) {
789 fb.main_tier[j] = fb.main_tier[j].max(t);
790 }
791 if t > fb.tier[j] {
792 fb.cand[j] = obs;
793 fb.count[j] = 1;
794 fb.tier[j] = t;
795 } else if t == fb.tier[j] {
796 if (0..3).all(|k| (obs[k] as i32 - fb.cand[j][k] as i32).abs() <= tau) {
797 fb.cand[j] = obs;
798 fb.count[j] = fb.count[j].saturating_add(1);
799 } else if fb.count[j] <= 1 {
800 fb.cand[j] = obs;
801 fb.count[j] = 1;
802 } else {
803 fb.count[j] -= 1;
804 }
805 }
806 }
807}
808
809fn whole_block_is_clean(tiers: &TierMap<'_>, sx: i64, sy: i64) -> bool {
810 let (x0, y0) = (
811 (sx - tiers.x).div_euclid(CELL as i64),
812 (sy - tiers.y).div_euclid(CELL as i64),
813 );
814 let (x1, y1) = (
815 (sx + BLOCK as i64 - 1 - tiers.x).div_euclid(CELL as i64),
816 (sy + BLOCK as i64 - 1 - tiers.y).div_euclid(CELL as i64),
817 );
818 if x0 < 0 || y0 < 0 || x1 >= tiers.cw as i64 || y1 >= tiers.ch as i64 {
819 return false;
820 }
821 (y0..=y1).all(|y| {
822 tiers.tier[y as usize * tiers.cw + x0 as usize..=y as usize * tiers.cw + x1 as usize]
823 .iter()
824 .all(|&t| t == TIER_CLEAN)
825 })
826}
827
828fn flat_neighborhood_sample(
830 frame: &Frame<'_>,
831 footprint: &Footprint<'_>,
832 sx: i64,
833 sy: i64,
834 tolerance: i32,
835) -> Option<[u8; 3]> {
836 let radius = BLOCK as i64;
837 let (left, top, right, bottom) = (sx - radius, sy - radius, sx + 2 * radius, sy + 2 * radius);
838 if left < 0 || top < 0 || right > frame.width as i64 || bottom > frame.height as i64 {
839 return None;
840 }
841 let mut low = [255u8; 3];
842 let mut high = [0u8; 3];
843 for y in top..bottom {
844 for x in left..right {
845 if !footprint.contains(x, y) {
846 return None;
847 }
848 let p = (y as usize * frame.width + x as usize) * 4;
849 for c in 0..3 {
850 low[c] = low[c].min(frame.rgba[p + c]);
851 high[c] = high[c].max(frame.rgba[p + c]);
852 if high[c] as i32 - low[c] as i32 > tolerance {
853 return None;
854 }
855 }
856 }
857 }
858 let p = (sy as usize * frame.width + sx as usize) * 4;
859 Some([frame.rgba[p], frame.rgba[p + 1], frame.rgba[p + 2]])
860}
861
862pub struct Output {
864 acts: Vec<Act>,
865 pub kind: Vec<Kind>,
866 pub frame: Vec<i32>,
867 component: Vec<i32>,
868 provisional: Vec<i8>,
869 rank: Vec<(i8, bool, bool, i16)>,
871 held: Vec<Option<Box<[u8; PIXELS * 3]>>>,
872 weak_witness: Vec<Option<Box<BlockSignature>>>,
874 main_tier: Vec<i8>,
875 fallback: Vec<Option<Box<Fallback>>>,
876 pub components: Vec<Component>,
877}
878
879pub const CONSENSUS_BLOCK_BYTES: usize = CELLS
881 * (std::mem::size_of::<[u8; 3]>() + std::mem::size_of::<i16>())
882 + std::mem::size_of::<BlockVote>()
883 + std::mem::size_of::<u16>();
884pub const OUTPUT_BLOCK_BYTES: usize = std::mem::size_of::<Act>()
887 + std::mem::size_of::<Kind>()
888 + 2 * std::mem::size_of::<i32>()
889 + std::mem::size_of::<i8>()
890 + std::mem::size_of::<(i8, bool, bool, i16)>()
891 + std::mem::size_of::<Option<Box<[u8; PIXELS * 3]>>>()
892 + PIXELS * 3
893 + std::mem::size_of::<Option<Box<BlockSignature>>>()
894 + CELLS * std::mem::size_of::<[u8; 3]>()
896 + std::mem::size_of::<i8>()
897 + std::mem::size_of::<Option<Box<Fallback>>>();
898pub const FALLBACK_BYTES: usize = std::mem::size_of::<Fallback>();
900
901impl Consensus {
902 pub fn unseen_blocks(&self) -> usize {
905 self.vote.iter().filter(|v| v.clean_views == 0).count()
906 }
907}
908
909impl Output {
910 pub fn new(c: &Consensus, components: Vec<Component>) -> Self {
913 let n = c.grid.bw * c.grid.bh;
914 let mut component = vec![-1; n];
915 for (k, comp) in components.iter().enumerate() {
916 for yy in 0..comp.bh {
917 for xx in 0..comp.bw {
918 if comp.mask[yy * comp.bw + xx] {
919 component[(comp.by + yy) * c.grid.bw + comp.bx + xx] = k as i32;
920 }
921 }
922 }
923 }
924 let fallback = c
925 .vote
926 .iter()
927 .zip(&component)
928 .map(|(v, &k)| {
929 (v.clean_views == 0 && k < 0).then(|| {
930 Box::new(Fallback {
931 flat_witness: None,
932 cand: [[0; 3]; PIXELS],
933 count: [0; PIXELS],
934 tier: [-1; PIXELS],
935 main: [[0; 4]; PIXELS],
936 main_tier: [-1; PIXELS],
937 })
938 })
939 })
940 .collect();
941 Self {
942 acts: vec![Act::default(); n],
943 kind: vec![Kind::None; n],
944 frame: vec![-1; n],
945 component,
946 provisional: vec![-1; n],
947 rank: vec![(-1, false, false, -1); n],
948 held: (0..n).map(|_| None).collect(),
949 weak_witness: (0..n).map(|_| None).collect(),
950 main_tier: vec![-1; n],
951 fallback,
952 components,
953 }
954 }
955
956 pub fn fallback_blocks(&self) -> Vec<usize> {
958 self.fallback
959 .iter()
960 .enumerate()
961 .filter(|(_, f)| f.is_some())
962 .map(|(b, _)| b)
963 .collect()
964 }
965
966 pub fn set_main(&mut self, b: usize, rgba: &[u8]) {
968 if let Some(fb) = self.fallback.get_mut(b).and_then(|f| f.as_mut()) {
969 for (j, px) in rgba.chunks_exact(4).take(PIXELS).enumerate() {
970 fb.main[j] = [px[0], px[1], px[2], px[3]];
971 }
972 }
973 }
974
975 #[allow(clippy::too_many_arguments)]
979 pub fn emit(
980 &mut self,
981 c: &Consensus,
982 frame: &Frame<'_>,
983 still_previous: Option<&Frame<'_>>,
984 footprint: &Footprint<'_>,
985 tiers: &TierMap<'_>,
986 pose: (i64, i64),
987 tau: i32,
988 frame_index: u32,
989 main: &(dyn CellRef + Sync),
990 writes: &mut Vec<BlockWrite>,
991 ) {
992 self.coobserve(
993 c,
994 &moments::View {
995 frame,
996 previous: still_previous,
997 footprint,
998 tiers,
999 pose,
1000 tau,
1001 index: frame_index,
1002 },
1003 );
1004 let grid = c.grid;
1005 let mut sig = [[0u8; 3]; CELLS];
1006 let block_write = |b: usize, kind: Kind| {
1007 let (wx0, wy0) = grid.origin(b);
1008 BlockWrite {
1009 canvas: 0,
1010 gx: grid.gx + (b % grid.bw) as i64,
1011 gy: grid.gy + (b / grid.bw) as i64,
1012 sx: wx0 - pose.0,
1013 sy: wy0 - pose.1,
1014 frame: frame_index,
1015 kind,
1016 }
1017 };
1018 for comp in self.components.iter_mut() {
1022 let (x, y) = grid.origin(comp.by * grid.bw + comp.bx);
1023 if x + (comp.bw * BLOCK) as i64 <= footprint.x + pose.0
1024 || y + (comp.bh * BLOCK) as i64 <= footprint.y + pose.1
1025 || x >= footprint.x + pose.0 + footprint.width as i64
1026 || y >= footprint.y + pose.1 + footprint.height as i64
1027 {
1028 continue;
1029 }
1030 let mut covered = Vec::new();
1031 let may_complete = x >= footprint.x + pose.0
1032 && y >= footprint.y + pose.1
1033 && x + (comp.bw * BLOCK) as i64 <= footprint.x + pose.0 + footprint.width as i64
1034 && y + (comp.bh * BLOCK) as i64 <= footprint.y + pose.1 + footprint.height as i64
1035 && x >= pose.0
1036 && y >= pose.1
1037 && x + (comp.bw * BLOCK) as i64 <= pose.0 + frame.width as i64
1038 && y + (comp.bh * BLOCK) as i64 <= pose.1 + frame.height as i64;
1039 let mut moment = Vec::with_capacity(if may_complete {
1040 comp.dynamic.iter().filter(|&&v| v).count()
1041 } else {
1042 0
1043 });
1044 let mut dynamic_index = 0;
1045 let mut compared = 0usize;
1046 let mut stable = true;
1047 let mut tier = TIER_CLEAN;
1048 let mut inset = i64::MAX;
1049 let mut tainted = false;
1054 for yy in 0..comp.bh {
1055 for xx in 0..comp.bw {
1056 let k = yy * comp.bw + xx;
1057 if !comp.mask[k] {
1058 continue;
1059 }
1060 let b = (comp.by + yy) * grid.bw + comp.bx + xx;
1061 let (wx0, wy0) = grid.origin(b);
1062 let previous_index = dynamic_index;
1063 if comp.dynamic[k] {
1064 dynamic_index += 1;
1065 }
1066 if let Some(t) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) {
1067 if t >= TIER_VEIL {
1068 tier = tier.min(t);
1069 if !comp.dynamic[k] && t as i8 <= c.vote[b].tier {
1071 tainted |= contradicts(
1072 &sig,
1073 &c.sig[b * CELLS..(b + 1) * CELLS],
1074 tau,
1075 || {
1076 settled(
1077 &sig,
1078 still_previous,
1079 footprint,
1080 tiers,
1081 pose,
1082 wx0,
1083 wy0,
1084 tau,
1085 )
1086 },
1087 );
1088 }
1089 if comp.dynamic[k] && may_complete {
1090 let mut prior = [[0u8; 3]; CELLS];
1091 let reference = if let Some(before) = still_previous {
1092 view(before, footprint, tiers, pose, wx0, wy0, &mut prior)
1093 .map(|_| &prior)
1094 } else {
1095 comp.last_complete.get(previous_index)
1096 };
1097 if let Some(reference) = reference {
1098 let difference = compare(&sig, reference);
1099 compared += 1;
1100 stable &=
1101 difference.0 <= tau as u32 && difference.1 <= tau as u32;
1102 }
1103 moment.push(sig);
1104 }
1105 let (sx, sy) = (wx0 - pose.0, wy0 - pose.1);
1106 inset = inset
1107 .min(sx - footprint.x)
1108 .min(sy - footprint.y)
1109 .min(footprint.x + footprint.width as i64 - sx - BLOCK as i64)
1110 .min(footprint.y + footprint.height as i64 - sy - BLOCK as i64);
1111 covered.push(b);
1112 }
1113 }
1114 }
1115 }
1116 if covered.is_empty() {
1117 continue;
1118 }
1119 let blocks = comp.mask.iter().filter(|&&m| m).count();
1120 let eligible = covered.len() == blocks;
1121 let confirmed = eligible && compared == moment.len() && compared > 0 && stable;
1122 if eligible {
1123 comp.last_complete = moment;
1124 }
1125 let score = if eligible {
1128 MomentRank {
1129 complete: true,
1130 untainted: !tainted,
1131 tier,
1132 corroborated: confirmed,
1133 interior: confirmed && inset >= CENTRAL,
1134 preference: frame_index as usize,
1135 }
1136 } else {
1137 MomentRank {
1138 complete: false,
1139 untainted: false,
1140 tier: 0,
1141 corroborated: false,
1142 interior: false,
1143 preference: covered.len(),
1144 }
1145 };
1146 if comp.best.is_some_and(|best| best > score) {
1147 continue;
1148 }
1149 comp.best = Some(score);
1150 for b in covered {
1151 self.kind[b] = Kind::Component;
1152 self.frame[b] = frame_index as i32;
1153 writes.push(block_write(b, Kind::Component));
1154 }
1155 }
1156 let (bx0, by0, bx1, by1) = grid.span(footprint, pose);
1157 let w = frame.width;
1158 let (fx0, fy0, fx1, fy1) = {
1160 let b = BLOCK as i64;
1161 let (x0, y0) = (footprint.x + pose.0, footprint.y + pose.1);
1162 let (x1, y1) = (x0 + footprint.width as i64, y0 + footprint.height as i64);
1163 let clamp = |v: i64, n: usize| v.clamp(0, n as i64) as usize;
1164 (
1165 clamp(x0.div_euclid(b) - grid.gx, grid.bw),
1166 clamp(y0.div_euclid(b) - grid.gy, grid.bh),
1167 clamp((x1 + b - 1).div_euclid(b) - grid.gx, grid.bw),
1168 clamp((y1 + b - 1).div_euclid(b) - grid.gy, grid.bh),
1169 )
1170 };
1171 let (kind_p, fallback_base) = (
1173 crate::kernels::pool::SyncPtr(self.kind.as_mut_ptr()),
1174 self.fallback.as_mut_ptr() as usize,
1175 );
1176 if fy1 > fy0 && fx1 > fx0 {
1177 let rows = fy1 - fy0;
1178 let chunks = crate::kernels::pool::chunks_for(rows * (fx1 - fx0), 64);
1179 crate::kernels::pool::par_for(chunks, |k| {
1180 for by in fy0 + crate::kernels::pool::split(rows, chunks, k)
1181 ..fy0 + crate::kernels::pool::split(rows, chunks, k + 1)
1182 {
1183 for bx in fx0..fx1 {
1184 let b = by * grid.bw + bx;
1185 let (slot, kind) = unsafe {
1187 (
1188 &mut *(fallback_base as *mut Option<Box<Fallback>>).add(b),
1189 &mut *kind_p.get().add(b),
1190 )
1191 };
1192 let Some(fb) = slot.as_mut() else { continue };
1193 *kind = Kind::Fallback;
1194 fallback_view(fb, frame, footprint, tiers, pose, grid.origin(b), tau);
1195 }
1196 }
1197 });
1198 }
1199 if by1 <= by0 || bx1 <= bx0 {
1200 return;
1201 }
1202 let rows = by1 - by0;
1204 let chunks = crate::kernels::pool::chunks_for(rows * (bx1 - bx0), 64);
1205 let acts = crate::kernels::pool::SyncPtr(self.acts.as_mut_ptr());
1206 let (component, kind, witnesses) = (&self.component, &self.kind, &self.weak_witness);
1207 crate::kernels::pool::par_for(chunks, |k| {
1208 let mut sig = [[0u8; 3]; CELLS];
1209 for by in by0 + crate::kernels::pool::split(rows, chunks, k)
1210 ..by0 + crate::kernels::pool::split(rows, chunks, k + 1)
1211 {
1212 for bx in bx0..bx1 {
1213 let b = by * grid.bw + bx;
1214 let act = unsafe { &mut *acts.get().add(b) };
1216 *act = Act::default();
1217 if component[b] >= 0 || kind[b] == Kind::Coherent {
1218 continue;
1219 }
1220 let (wx0, wy0) = grid.origin(b);
1221 let fb = unsafe { &mut *(fallback_base as *mut Option<Box<Fallback>>).add(b) };
1223 if fb.is_some()
1224 && (c.vote[b].tier >= TIER_CLEAN as i8
1225 || !whole_block_is_clean(tiers, wx0 - pose.0, wy0 - pose.1))
1226 {
1227 continue;
1228 }
1229 let Some(tier) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) else {
1230 continue;
1231 };
1232 let d = compare(&sig, &c.sig[b * CELLS..(b + 1) * CELLS]);
1233 let (sx0, sy0) = (wx0 - pose.0, wy0 - pose.1);
1234 let inset = (sx0 - footprint.x)
1235 .min(sy0 - footprint.y)
1236 .min(footprint.x + footprint.width as i64 - sx0 - BLOCK as i64)
1237 .min(footprint.y + footprint.height as i64 - sy0 - BLOCK as i64);
1238 if let Some(fb) = fb.as_mut() {
1239 if tier == TIER_CLEAN && inset >= CENTRAL {
1240 let tolerance = (tau / 4).max(1);
1241 if let Some(sample) =
1242 flat_neighborhood_sample(frame, footprint, sx0, sy0, tolerance)
1243 {
1244 let consistent = |prior: [u8; 3]| {
1245 (0..3).all(|c| {
1246 (sample[c] as i32 - prior[c] as i32).abs() <= tolerance
1247 })
1248 };
1249 let prior = still_previous.and_then(|f| {
1250 flat_neighborhood_sample(f, footprint, sx0, sy0, tolerance)
1251 });
1252 act.seen = true;
1253 act.corroborated_flat =
1254 prior.or(fb.flat_witness).is_some_and(consistent);
1255 fb.flat_witness = Some(sample);
1256 }
1257 }
1258 continue;
1259 }
1260 let (cx0, cy0) = (wx0.div_euclid(CELL as i64), wy0.div_euclid(CELL as i64));
1262 let mut msig = [[0u8; 3]; CELLS];
1263 let known = (0..CELLS).all(|k| {
1264 match main.cell(cx0 + (k % SIDE) as i64, cy0 + (k / SIDE) as i64) {
1265 Some(v) => {
1266 msig[k] = v;
1267 true
1268 }
1269 None => false,
1270 }
1271 });
1272 let mut strict = agrees_strictly(d, tau, &sig, c, (wx0, wy0));
1273 let quiet_limit = 2 * (tau / 4).max(2);
1274 let mut settled = strict
1277 && sig
1278 .iter()
1279 .zip(&c.sig[b * CELLS..(b + 1) * CELLS])
1280 .all(|(a, b)| (cell_luma(*a) - cell_luma(*b)).abs() <= quiet_limit);
1281 let mut witness = WitnessAction::Keep;
1282 if tier == TIER_CLEAN {
1283 witness = WitnessAction::Clear;
1284 if !settled && inset >= CENTRAL && agrees(d, tau) && d.2 == 0 {
1285 let quiet_limit = 2 * (tau / 4).max(2);
1289 let mut previous_signature = [[0u8; 3]; CELLS];
1290 let previous = if let Some(before) = still_previous {
1294 view(
1295 before,
1296 footprint,
1297 tiers,
1298 pose,
1299 wx0,
1300 wy0,
1301 &mut previous_signature,
1302 )
1303 .map(|_| &previous_signature)
1304 } else {
1305 witnesses[b].as_deref()
1306 };
1307 settled = previous.is_some_and(|previous| {
1308 agrees(compare(&sig, previous), tau)
1309 && sig.iter().zip(previous).all(|(a, b)| {
1310 (cell_luma(*a) - cell_luma(*b)).abs() <= quiet_limit
1311 })
1312 });
1313 strict |= settled;
1314 if !settled {
1315 witness = WitnessAction::Store;
1316 }
1317 }
1318 }
1319 *act = Act {
1320 corroborated_flat: false,
1321 witness,
1322 seen: true,
1323 tier,
1324 strict,
1325 settled,
1326 loose: agrees(d, tau),
1327 inset: inset.min(CENTRAL) as i16,
1328 main: known && agrees(compare(&sig, &msig), tau),
1329 };
1330 }
1331 }
1332 });
1333 for by in by0..by1 {
1334 for bx in bx0..bx1 {
1335 let b = by * grid.bw + bx;
1336 let a = self.acts[b];
1337 if !a.seen {
1338 continue;
1339 }
1340 match a.witness {
1341 WitnessAction::Keep => {}
1342 WitnessAction::Clear => self.weak_witness[b] = None,
1343 WitnessAction::Store => {
1344 let (wx, wy) = grid.origin(b);
1347 if view(frame, footprint, tiers, pose, wx, wy, &mut sig).is_some() {
1348 if let Some(previous) = &mut self.weak_witness[b] {
1349 **previous = sig;
1350 } else {
1351 self.weak_witness[b] = Some(Box::new(sig));
1352 }
1353 }
1354 }
1355 }
1356 if self.fallback[b].is_some() {
1357 if a.corroborated_flat {
1358 self.kind[b] = Kind::Coherent;
1359 self.frame[b] = frame_index as i32;
1360 self.fallback[b] = None;
1361 writes.push(block_write(b, Kind::Coherent));
1362 }
1363 continue;
1364 }
1365 if a.tier == TIER_CLEAN && a.settled && a.inset >= CENTRAL as i16 {
1366 self.kind[b] = Kind::Coherent;
1367 self.frame[b] = frame_index as i32;
1368 self.held[b] = None;
1369 writes.push(block_write(b, Kind::Coherent));
1370 continue;
1371 }
1372 if a.main {
1373 self.main_tier[b] = self.main_tier[b].max(a.tier as i8);
1374 }
1375 let rank = (
1377 a.tier as i8,
1378 a.tier == TIER_CLEAN && a.strict,
1379 a.tier == TIER_CLEAN && a.loose,
1380 a.inset,
1381 );
1382 if rank >= self.rank[b] {
1383 let held = self.held[b].get_or_insert_with(|| Box::new([0u8; PIXELS * 3]));
1384 let (wx0, wy0) = grid.origin(b);
1385 for j in 0..PIXELS {
1386 let (sx, sy) = (
1387 wx0 + (j % BLOCK) as i64 - pose.0,
1388 wy0 + (j / BLOCK) as i64 - pose.1,
1389 );
1390 let fi = (sy as usize * w + sx as usize) * 4;
1391 held[j * 3..j * 3 + 3].copy_from_slice(&frame.rgba[fi..fi + 3]);
1392 }
1393 self.rank[b] = rank;
1394 self.kind[b] = if rank.2 {
1395 Kind::Edge
1396 } else {
1397 Kind::Provisional
1398 };
1399 self.provisional[b] = a.tier as i8;
1400 self.frame[b] = frame_index as i32;
1401 }
1402 }
1403 }
1404 }
1405
1406 pub fn sources(&self, c: &Consensus, deferred: &[Deferred]) -> Vec<i32> {
1410 let grid = c.grid;
1411 let mut out: Vec<i32> = self
1412 .kind
1413 .iter()
1414 .zip(&self.frame)
1415 .map(|(k, &f)| match k {
1416 Kind::Coherent | Kind::Component => f,
1417 _ => UNTOUCHED,
1418 })
1419 .collect();
1420 for d in deferred {
1421 let b = (d.gy - grid.gy) as usize * grid.bw + (d.gx - grid.gx) as usize;
1422 out[b] = if d.kind == Kind::Fallback {
1423 MIXED
1424 } else {
1425 d.frame as i32
1426 };
1427 }
1428 out
1429 }
1430
1431 pub fn finish(&self, c: &Consensus, tau: i32) -> Vec<Deferred> {
1434 let grid = c.grid;
1435 let near = (tau / 2).max(2);
1436 let mut out = Vec::new();
1437 for b in 0..grid.bw * grid.bh {
1438 let (gx, gy) = (
1439 grid.gx + (b % grid.bw) as i64,
1440 grid.gy + (b / grid.bw) as i64,
1441 );
1442 match self.kind[b] {
1443 Kind::Edge => {
1444 let Some(h) = &self.held[b] else { continue };
1445 if c.vote[b].clean_views < 2 && self.main_tier[b] >= self.provisional[b] {
1449 continue;
1450 }
1451 let mut rgba = Box::new([0u8; PIXELS * 4]);
1452 for j in 0..PIXELS {
1453 rgba[j * 4..j * 4 + 3].copy_from_slice(&h[j * 3..j * 3 + 3]);
1454 rgba[j * 4 + 3] = 255;
1455 }
1456 out.push(Deferred {
1457 gx,
1458 gy,
1459 frame: self.frame[b] as u32,
1460 kind: Kind::Edge,
1461 rgba,
1462 });
1463 }
1464 Kind::Provisional => {
1465 let Some(h) = &self.held[b] else { continue };
1466 if self.main_tier[b] >= self.provisional[b] {
1467 continue;
1468 }
1469 let mut rgba = Box::new([0u8; PIXELS * 4]);
1470 for j in 0..PIXELS {
1471 rgba[j * 4..j * 4 + 3].copy_from_slice(&h[j * 3..j * 3 + 3]);
1472 rgba[j * 4 + 3] = 255;
1473 }
1474 out.push(Deferred {
1475 gx,
1476 gy,
1477 frame: self.frame[b] as u32,
1478 kind: Kind::Provisional,
1479 rgba,
1480 });
1481 }
1482 Kind::Fallback => {
1483 let Some(fb) = &self.fallback[b] else {
1484 continue;
1485 };
1486 let mut rgba = Box::new([0u8; PIXELS * 4]);
1487 let mut any = false;
1488 let same = |j: usize| {
1493 let m = fb.main[j];
1494 m[3] == 255
1495 && (0..3).all(|k| (fb.cand[j][k] as i32 - m[k] as i32).abs() <= near)
1496 };
1497 if !(0..PIXELS)
1498 .any(|j| fb.tier[j] >= 0 && fb.tier[j] > fb.main_tier[j] && !same(j))
1499 {
1500 continue;
1501 }
1502 for j in 0..PIXELS {
1503 if fb.tier[j] < 0 || (fb.main_tier[j] >= fb.tier[j] && !same(j)) {
1504 continue;
1505 }
1506 rgba[j * 4..j * 4 + 3].copy_from_slice(&fb.cand[j]);
1507 rgba[j * 4 + 3] = 255;
1508 any = true;
1509 }
1510 if any {
1511 out.push(Deferred {
1512 gx,
1513 gy,
1514 frame: 0,
1515 kind: Kind::Fallback,
1516 rgba,
1517 });
1518 }
1519 }
1520 _ => {}
1521 }
1522 }
1523 out
1524 }
1525}
1526
1527#[cfg(test)]
1528mod tests {
1529 use super::*;
1530 use crate::kernels::reveal::stats::CellMap;
1531 use crate::kernels::reveal::CanvasView;
1532
1533 fn solid_frame(width: usize, height: usize, value: u8) -> Vec<u8> {
1534 let mut rgba = vec![value; width * height * 4];
1535 for pixel in rgba.chunks_exact_mut(4) {
1536 pixel[3] = 255;
1537 }
1538 rgba
1539 }
1540
1541 const W: usize = 64;
1542 const H: usize = 64;
1543
1544 fn frame(k: usize, transient: bool) -> Vec<u8> {
1548 let mut rgba = vec![255u8; W * H * 4];
1549 for y in 0..H {
1550 for x in 0..W {
1551 let v = if (16..32).contains(&x) && (16..32).contains(&y) {
1552 ((k * 60 + x * 3) % 256) as u8
1553 } else if transient && x < 16 && y < 16 {
1554 40
1555 } else if y % 16 == 7 && x % 16 < 10 {
1556 30
1557 } else {
1558 250
1559 };
1560 rgba[(y * W + x) * 4..(y * W + x) * 4 + 3].fill(v);
1561 }
1562 }
1563 rgba
1564 }
1565
1566 fn moments(frames: &[Vec<u8>]) -> Vec<i32> {
1568 let mask = vec![1u8; W * H];
1569 let footprint = Footprint {
1570 x: 0,
1571 y: 0,
1572 width: W,
1573 height: H,
1574 mask: &mask,
1575 };
1576 let clean = vec![TIER_CLEAN; (W / CELL) * (H / CELL)];
1577 let tiers = TierMap {
1578 x: 0,
1579 y: 0,
1580 cw: W / CELL,
1581 ch: H / CELL,
1582 tier: &clean,
1583 };
1584 let mut c = Consensus::new(Grid::covering(0, 0, W, H));
1585 for f in frames {
1586 let f = Frame {
1587 rgba: f,
1588 width: W,
1589 height: H,
1590 };
1591 c.observe(&f, &footprint, &tiers, (0, 0));
1592 }
1593 let comps = components(&c);
1594 assert_eq!(comps.len(), 1, "one dynamic component");
1595 let mut out = Output::new(&c, comps);
1596 let coverage = vec![255u8; W * H / 8];
1597 let main = CellMap::from_canvas(&CanvasView {
1598 x: 0,
1599 y: 0,
1600 width: W,
1601 height: H,
1602 rgba: &frames[0],
1603 coverage: &coverage,
1604 });
1605 let mut writes = Vec::new();
1606 for (i, f) in frames.iter().enumerate() {
1607 let f = Frame {
1608 rgba: f,
1609 width: W,
1610 height: H,
1611 };
1612 out.emit(
1613 &c,
1614 &f,
1615 None,
1616 &footprint,
1617 &tiers,
1618 (0, 0),
1619 24,
1620 i as u32,
1621 &main,
1622 &mut writes,
1623 );
1624 }
1625 out.frame.clone()
1626 }
1627
1628 #[test]
1634 fn a_dynamic_component_does_not_take_a_moment_with_a_whole_block_transient() {
1635 let frames: Vec<Vec<u8>> = (0..8).map(|k| frame(k, k == 7)).collect();
1636 let m = moments(&frames);
1637 assert_eq!(m[0], 6, "the bubble's block");
1638 assert_eq!(m[4 + 1], 6, "the dynamic block");
1639 }
1640
1641 #[test]
1644 fn a_dynamic_component_takes_its_latest_moment() {
1645 let frames: Vec<Vec<u8>> = (0..8).map(|k| frame(k, false)).collect();
1646 let m = moments(&frames);
1647 assert_eq!(m[4 + 1], 7);
1648 assert_eq!(m[0], 7);
1649 }
1650 #[test]
1651 fn small_rigid_phase_is_distinct_from_structural_change() {
1652 let pattern = |dx: usize| -> BlockSignature {
1653 std::array::from_fn(|k| {
1654 let x = k % SIDE + dx;
1655 let y = k / SIDE;
1656 [20 + ((x * 19 + y * 31) % 11) as u8 * 17; 3]
1657 })
1658 };
1659 let mut cand = pattern(0);
1660 let mut count = [1; CELLS];
1661 let mut v = BlockVote {
1662 tier: TIER_CLEAN as i8,
1663 clean_views: 1,
1664 changed_views: 0,
1665 };
1666 let mut explained = 0;
1667 for dx in [1, 0, 1, 0, 1, 0] {
1668 explained += vote(&mut cand, &mut count, &mut v, &pattern(dx), TIER_CLEAN) as u16;
1669 }
1670 assert_eq!(v.changed_views - explained, 0);
1671 let replacement = [[220; 3]; CELLS];
1672 vote(&mut cand, &mut count, &mut v, &replacement, TIER_CLEAN);
1673 assert_eq!(
1674 v.changed_views - explained,
1675 1,
1676 "a real replacement is not phase noise"
1677 );
1678 }
1679
1680 #[test]
1681 fn a_new_edge_mark_cannot_hide_in_the_uncompared_phase_strip() {
1682 for side in 0..4 {
1683 let mut cand = [[80; 3]; CELLS];
1684 let mut count = [1; CELLS];
1685 let mut v = BlockVote {
1686 tier: TIER_CLEAN as i8,
1687 clean_views: 1,
1688 changed_views: 0,
1689 };
1690 let mut observed = cand;
1691 for k in 0..SIDE {
1692 let cell = match side {
1693 0 => k * SIDE,
1694 1 => k * SIDE + SIDE - 1,
1695 2 => k,
1696 _ => (SIDE - 1) * SIDE + k,
1697 };
1698 observed[cell] = [230; 3];
1699 }
1700 assert!(!vote(&mut cand, &mut count, &mut v, &observed, TIER_CLEAN));
1701 assert_eq!(
1702 v.changed_views, 1,
1703 "a uniform overlap cannot establish a displacement or dismiss new edge content"
1704 );
1705 }
1706 }
1707
1708 #[test]
1709 fn phase_evidence_requires_a_small_rigid_displacement() {
1710 let pattern = |dx: i32, dy: i32| -> BlockSignature {
1711 std::array::from_fn(|k| {
1712 let x = (k % SIDE) as i32 + 8 + dx;
1713 let y = (k / SIDE) as i32 + 8 + dy;
1714 let seed =
1715 (x as u32).wrapping_mul(0x9e3779b9) ^ (y as u32).wrapping_mul(0x85ebca6b);
1716 let value = (seed ^ (seed >> 13)).wrapping_mul(0xc2b2ae35);
1717 [(value >> 24) as u8; 3]
1718 })
1719 };
1720 let reference = pattern(0, 0);
1721 for dy in -1..=1 {
1722 for dx in -1..=1 {
1723 if dx != 0 || dy != 0 {
1724 assert!(small_phase_agrees(&pattern(dx, dy), &reference));
1725 }
1726 }
1727 }
1728 assert!(!small_phase_agrees(&pattern(3, 0), &reference));
1729 let mut changed = pattern(1, 0);
1730 changed[3 * SIDE..5 * SIDE].fill([255; 3]);
1731 assert!(
1732 !small_phase_agrees(&changed, &reference),
1733 "phase cannot explain a changed interior"
1734 );
1735 }
1736
1737 fn phase_ambiguous_widget(clipped_context: bool) {
1741 const WORLD: usize = 416;
1742 const SIZE: usize = 192;
1743 let world = |shift: usize| {
1744 let mut pixels = solid_frame(WORLD, SIZE, 240);
1745 for y in 64..128 {
1746 for x in 144 + shift..240 + shift {
1747 let value = if (x - 144 - shift) % 8 < 4 { 80 } else { 180 };
1748 pixels[(y * WORLD + x) * 4..(y * WORLD + x) * 4 + 3].fill(value);
1749 }
1750 }
1751 pixels
1752 };
1753 let epochs = [world(0), world(2), world(4)];
1754 let poses: Vec<(usize, usize)> = (0..8)
1755 .map(|x| (x, 0))
1756 .chain((0..8).map(|x| (if clipped_context { 176 + x } else { 192 + x }, 1)))
1757 .chain((0..3).map(|i| {
1758 if clipped_context {
1759 (64 + i, 2)
1760 } else {
1761 (96 + i, 1)
1762 }
1763 }))
1764 .collect();
1765 let frames: Vec<Vec<u8>> = poses
1766 .iter()
1767 .map(|&(x, epoch)| {
1768 let mut frame = vec![0; SIZE * SIZE * 4];
1769 for y in 0..SIZE {
1770 frame[y * SIZE * 4..(y + 1) * SIZE * 4].copy_from_slice(
1771 &epochs[epoch][(y * WORLD + x) * 4..(y * WORLD + x + SIZE) * 4],
1772 );
1773 }
1774 frame
1775 })
1776 .collect();
1777 let mask = vec![1; SIZE * SIZE];
1778 let clean = vec![TIER_CLEAN; SIZE * SIZE / 4];
1779 let fp = Footprint {
1780 x: 0,
1781 y: 0,
1782 width: SIZE,
1783 height: SIZE,
1784 mask: &mask,
1785 };
1786 let tm = TierMap {
1787 x: 0,
1788 y: 0,
1789 cw: SIZE / 2,
1790 ch: SIZE / 2,
1791 tier: &clean,
1792 };
1793 let mut c = Consensus::new(Grid::covering(0, 0, WORLD, SIZE));
1794 for (i, &(x, _)) in poses.iter().enumerate() {
1795 c.observe(
1796 &Frame {
1797 rgba: &frames[i],
1798 width: SIZE,
1799 height: SIZE,
1800 },
1801 &fp,
1802 &tm,
1803 (x as i64, 0),
1804 );
1805 }
1806 let mut result = epochs[0].clone();
1807 for y in 0..SIZE {
1808 result[(y * WORLD + 192) * 4..(y + 1) * WORLD * 4]
1809 .copy_from_slice(&epochs[1][(y * WORLD + 192) * 4..(y + 1) * WORLD * 4]);
1810 }
1811 let initial = result.clone();
1812 let coverage = vec![255; WORLD * SIZE / 8];
1813 let main = CellMap::from_canvas(&CanvasView {
1814 x: 0,
1815 y: 0,
1816 width: WORLD,
1817 height: SIZE,
1818 rgba: &initial,
1819 coverage: &coverage,
1820 });
1821 let mut out = Output::new(&c, components(&c));
1822 for b in out.fallback_blocks() {
1823 let (x, y) = c.grid.origin(b);
1824 let mut block = vec![0; PIXELS * 4];
1825 for row in 0..BLOCK {
1826 let from = ((y as usize + row) * WORLD + x as usize) * 4;
1827 block[row * BLOCK * 4..(row + 1) * BLOCK * 4]
1828 .copy_from_slice(&initial[from..from + BLOCK * 4]);
1829 }
1830 out.set_main(b, &block);
1831 }
1832 for (i, &(x, _)) in poses.iter().enumerate() {
1833 let mut writes = Vec::new();
1834 out.emit(
1835 &c,
1836 &Frame {
1837 rgba: &frames[i],
1838 width: SIZE,
1839 height: SIZE,
1840 },
1841 None,
1842 &fp,
1843 &tm,
1844 (x as i64, 0),
1845 24,
1846 i as u32,
1847 &main,
1848 &mut writes,
1849 );
1850 for w in writes {
1851 for row in 0..BLOCK {
1852 let from = ((w.sy as usize + row) * SIZE + w.sx as usize) * 4;
1853 let to = ((w.gy as usize * BLOCK + row) * WORLD + w.gx as usize * BLOCK) * 4;
1854 result[to..to + BLOCK * 4].copy_from_slice(&frames[i][from..from + BLOCK * 4]);
1855 }
1856 }
1857 }
1858 for block in out.finish(&c, 24) {
1859 for row in 0..BLOCK {
1860 let to =
1861 ((block.gy as usize * BLOCK + row) * WORLD + block.gx as usize * BLOCK) * 4;
1862 result[to..to + BLOCK * 4]
1863 .copy_from_slice(&block.rgba[row * BLOCK * 4..(row + 1) * BLOCK * 4]);
1864 }
1865 }
1866 let mut wrong = 0;
1867 for y in 64..128 {
1868 for x in 144..242 {
1869 let p = (y * WORLD + x) * 4;
1870 wrong += (result[p..p + 3] != epochs[if clipped_context { 2 } else { 1 }][p..p + 3])
1871 as usize;
1872 }
1873 }
1874 assert_eq!(
1875 wrong, 0,
1876 "a complete observed widget must not retain the seam between partial epochs"
1877 );
1878 }
1879
1880 #[test]
1881 fn a_complete_widget_moment_repairs_phase_ambiguous_partial_references() {
1882 phase_ambiguous_widget(false);
1883 }
1884 #[test]
1885 fn a_widget_can_stay_coherent_when_only_its_static_margin_is_clipped() {
1886 phase_ambiguous_widget(true);
1887 }
1888
1889 #[test]
1890 fn phase_partition_is_a_fallback_for_groups_larger_than_the_observed_viewport() {
1891 let mut c = Consensus::new(Grid::covering(0, 0, 320, 128));
1892 c.viewport = (192, 128);
1893 c.sig.fill([80; 3]);
1894 for v in &mut c.vote {
1895 v.tier = TIER_CLEAN as i8;
1896 v.clean_views = 8;
1897 }
1898 for x in 1..19 {
1899 let b = 3 * c.grid.bw + x;
1900 c.vote[b].changed_views = 8;
1901 if x != 4 && x != 14 {
1902 c.phase_explained[b] = 8;
1903 }
1904 }
1905 let pieces = components(&c);
1906 assert_eq!(
1907 pieces.len(),
1908 2,
1909 "the phase bridge cannot force two observable objects into an impossible whole"
1910 );
1911 assert!(pieces
1912 .iter()
1913 .all(|p| p.bw * BLOCK <= 192 && p.bh * BLOCK <= 128));
1914 c.viewport = (320, 128);
1917 assert_eq!(components(&c).len(), 1);
1918 c.viewport = (0, 0);
1919 assert_eq!(components(&c).len(), 1);
1920 }
1921
1922 #[test]
1923 fn static_context_outside_a_viewport_does_not_make_a_fitting_object_unobservable() {
1924 let mut c = Consensus::new(Grid::covering(0, 0, 224, 128));
1925 c.viewport = (192, 128);
1926 c.sig.fill([80; 3]);
1927 for v in &mut c.vote {
1928 v.tier = TIER_CLEAN as i8;
1929 v.clean_views = 8;
1930 }
1931 for x in 1..13 {
1932 let b = 3 * c.grid.bw + x;
1933 c.vote[b].changed_views = 8;
1934 if x != 4 && x != 10 {
1935 c.phase_explained[b] = 8;
1936 }
1937 }
1938 let groups = components(&c);
1939 assert_eq!(
1940 groups.len(),
1941 1,
1942 "the changing core fits; its wider static margin is support, not object extent"
1943 );
1944 c.viewport = (176, 128);
1945 assert_eq!(
1946 components(&c).len(),
1947 1,
1948 "block quantization cannot prove the object exceeds this viewport"
1949 );
1950 c.viewport = (128, 128);
1951 assert_eq!(
1952 components(&c).len(),
1953 2,
1954 "a smaller viewport makes the changing extent provably unobservable"
1955 );
1956 }
1957
1958 #[test]
1959 fn a_partial_later_view_does_not_split_a_complete_component_moment() {
1960 let (width, height) = (80, 64);
1963 let mut c = Consensus::new(Grid::covering(0, 0, width, height));
1964 for v in &mut c.vote {
1965 v.tier = TIER_CLEAN as i8;
1966 v.clean_views = 4;
1967 v.changed_views = 4;
1968 }
1969 let mut out = Output::new(&c, components(&c));
1970 assert_eq!(out.components.len(), 1);
1971 let mask = vec![1; width * height];
1972 let fp = Footprint {
1973 x: 0,
1974 y: 0,
1975 width,
1976 height,
1977 mask: &mask,
1978 };
1979 let old = solid_frame(width, height, 80);
1980 let new = solid_frame(width, height, 180);
1981 let cov = vec![255; width * height / 8];
1982 let main = CellMap::from_canvas(&CanvasView {
1983 x: 0,
1984 y: 0,
1985 width,
1986 height,
1987 rgba: &old,
1988 coverage: &cov,
1989 });
1990 let mut tiers = vec![TIER_CLEAN; width * height / 4];
1991 let mut writes = Vec::new();
1992 for (i, rgba) in [&old, &new].into_iter().enumerate() {
1993 if i == 1 {
1994 for y in 0..8 {
1995 tiers[y * (width / 2)..y * (width / 2) + 8].fill(0);
1996 }
1997 }
1998 let tm = TierMap {
1999 x: 0,
2000 y: 0,
2001 cw: width / 2,
2002 ch: height / 2,
2003 tier: &tiers,
2004 };
2005 out.emit(
2006 &c,
2007 &Frame {
2008 rgba,
2009 width,
2010 height,
2011 },
2012 None,
2013 &fp,
2014 &tm,
2015 (0, 0),
2016 24,
2017 i as u32,
2018 &main,
2019 &mut writes,
2020 );
2021 }
2022 assert!(
2023 out.frame.iter().all(|&f| f == 0),
2024 "a component must retain one complete moment: {:?}",
2025 out.frame
2026 );
2027 }
2028 #[test]
2029 fn a_clean_component_view_outranks_a_more_central_veiled_view() {
2030 let size = 192;
2031 let c = Consensus::new(Grid::covering(0, 0, size, size));
2032 let part = Component {
2033 bx: 5,
2034 by: 5,
2035 bw: 1,
2036 bh: 1,
2037 mask: vec![true],
2038 dynamic: vec![true],
2039 best: None,
2040 last_complete: Vec::new(),
2041 };
2042 let mut out = Output::new(&c, vec![part]);
2043 let pixels = solid_frame(size, size, 80);
2044 let mask = vec![1; size * size];
2045 let fp = Footprint {
2046 x: 0,
2047 y: 0,
2048 width: size,
2049 height: size,
2050 mask: &mask,
2051 };
2052 let clear = vec![TIER_CLEAN; size * size / 4];
2053 let veil = vec![TIER_VEIL; size * size / 4];
2054 let veiled = TierMap {
2055 x: 0,
2056 y: 0,
2057 cw: size / 2,
2058 ch: size / 2,
2059 tier: &veil,
2060 };
2061 let tm = TierMap {
2062 x: 0,
2063 y: 0,
2064 cw: size / 2,
2065 ch: size / 2,
2066 tier: &clear,
2067 };
2068 let coverage = vec![255; size * size / 8];
2069 let main = CellMap::from_canvas(&CanvasView {
2070 x: 0,
2071 y: 0,
2072 width: size,
2073 height: size,
2074 rgba: &pixels,
2075 coverage: &coverage,
2076 });
2077 let frame = Frame {
2078 rgba: &pixels,
2079 width: size,
2080 height: size,
2081 };
2082 let mut writes = Vec::new();
2083 out.emit(
2084 &c,
2085 &frame,
2086 None,
2087 &fp,
2088 &veiled,
2089 (0, 0),
2090 24,
2091 0,
2092 &main,
2093 &mut writes,
2094 );
2095 out.emit(
2096 &c,
2097 &frame,
2098 None,
2099 &fp,
2100 &tm,
2101 (0, 72),
2102 24,
2103 1,
2104 &main,
2105 &mut writes,
2106 );
2107 let b = 5 * c.grid.bw + 5;
2108 assert_eq!(
2109 out.frame[b], 1,
2110 "a known clean view outranks a more central veil"
2111 );
2112 out.emit(
2113 &c,
2114 &frame,
2115 None,
2116 &fp,
2117 &tm,
2118 (0, 0),
2119 24,
2120 2,
2121 &main,
2122 &mut writes,
2123 );
2124 assert_eq!(
2125 out.frame[b], 2,
2126 "genuine later content in the interior must still update"
2127 );
2128 }
2129 #[test]
2130 fn component_moments_reject_a_single_pulse_but_accept_a_confirmed_update() {
2131 let size = 128;
2132 let c = Consensus::new(Grid::covering(0, 0, size, size));
2133 let part = Component {
2134 bx: 3,
2135 by: 3,
2136 bw: 1,
2137 bh: 1,
2138 mask: vec![true],
2139 dynamic: vec![true],
2140 best: None,
2141 last_complete: Vec::new(),
2142 };
2143 let mut out = Output::new(&c, vec![part]);
2144 let mask = vec![1; size * size];
2145 let fp = Footprint {
2146 x: 0,
2147 y: 0,
2148 width: size,
2149 height: size,
2150 mask: &mask,
2151 };
2152 let clean = vec![TIER_CLEAN; size * size / 4];
2153 let tm = TierMap {
2154 x: 0,
2155 y: 0,
2156 cw: size / 2,
2157 ch: size / 2,
2158 tier: &clean,
2159 };
2160 let initial = solid_frame(size, size, 80);
2161 let coverage = vec![255; size * size / 8];
2162 let main = CellMap::from_canvas(&CanvasView {
2163 x: 0,
2164 y: 0,
2165 width: size,
2166 height: size,
2167 rgba: &initial,
2168 coverage: &coverage,
2169 });
2170 let mut writes = Vec::new();
2171 for (i, v) in [80, 80, 200, 80, 80, 200, 200].into_iter().enumerate() {
2172 let rgba = solid_frame(size, size, v);
2173 let f = Frame {
2174 rgba: &rgba,
2175 width: size,
2176 height: size,
2177 };
2178 out.emit(
2179 &c,
2180 &f,
2181 None,
2182 &fp,
2183 &tm,
2184 (0, 0),
2185 24,
2186 i as u32,
2187 &main,
2188 &mut writes,
2189 );
2190 assert_eq!(
2191 out.frame[3 * c.grid.bw + 3],
2192 [0, 1, 1, 1, 4, 4, 6][i],
2193 "moment {i}: an uncorroborated pulse is not a new content version"
2194 );
2195 }
2196 let final_pixels = solid_frame(size, size, 120);
2197 let final_frame = Frame {
2198 rgba: &final_pixels,
2199 width: size,
2200 height: size,
2201 };
2202 out.emit(
2203 &c,
2204 &final_frame,
2205 Some(&final_frame),
2206 &fp,
2207 &tm,
2208 (0, 0),
2209 24,
2210 7,
2211 &main,
2212 &mut writes,
2213 );
2214 assert_eq!(out.frame[3*c.grid.bw+3],7,"the final stable state of a still run is corroborated without counting duplicate poses as votes");
2215 }
2216 #[test]
2217 fn equally_corroborated_moments_prefer_visibility_but_an_unconfirmed_interior_pulse_does_not() {
2218 let size = 192;
2219 let c = Consensus::new(Grid::covering(0, 0, size, size));
2220 let part = Component {
2221 bx: 5,
2222 by: 5,
2223 bw: 1,
2224 bh: 1,
2225 mask: vec![true],
2226 dynamic: vec![true],
2227 best: None,
2228 last_complete: Vec::new(),
2229 };
2230 let mut out = Output::new(&c, vec![part]);
2231 let mask = vec![1; size * size];
2232 let clean = vec![TIER_CLEAN; size * size / 4];
2233 let fp = Footprint {
2234 x: 0,
2235 y: 0,
2236 width: size,
2237 height: size,
2238 mask: &mask,
2239 };
2240 let tm = TierMap {
2241 x: 0,
2242 y: 0,
2243 cw: size / 2,
2244 ch: size / 2,
2245 tier: &clean,
2246 };
2247 let pixels = solid_frame(size, size, 80);
2248 let coverage = vec![255; size * size / 8];
2249 let main = CellMap::from_canvas(&CanvasView {
2250 x: 0,
2251 y: 0,
2252 width: size,
2253 height: size,
2254 rgba: &pixels,
2255 coverage: &coverage,
2256 });
2257 let frame = Frame {
2258 rgba: &pixels,
2259 width: size,
2260 height: size,
2261 };
2262 let mut writes = Vec::new();
2263 out.emit(
2264 &c,
2265 &frame,
2266 Some(&frame),
2267 &fp,
2268 &tm,
2269 (0, 0),
2270 24,
2271 0,
2272 &main,
2273 &mut writes,
2274 );
2275 let faded = solid_frame(size, size, 30);
2276 let edge = Frame {
2277 rgba: &faded,
2278 width: size,
2279 height: size,
2280 };
2281 out.emit(
2282 &c,
2283 &edge,
2284 Some(&edge),
2285 &fp,
2286 &tm,
2287 (0, 72),
2288 24,
2289 1,
2290 &main,
2291 &mut writes,
2292 );
2293 let b = 5 * c.grid.bw + 5;
2294 assert_eq!(
2295 out.frame[b], 0,
2296 "a stable border fade is still a worse view than a stable interior one"
2297 );
2298 let pulse = solid_frame(size, size, 200);
2299 let changed = Frame {
2300 rgba: &pulse,
2301 width: size,
2302 height: size,
2303 };
2304 out.emit(
2305 &c,
2306 &changed,
2307 None,
2308 &fp,
2309 &tm,
2310 (0, 0),
2311 24,
2312 2,
2313 &main,
2314 &mut writes,
2315 );
2316 assert_eq!(
2317 out.frame[b], 0,
2318 "position cannot make an uncorroborated pulse win"
2319 );
2320 out.emit(
2321 &c,
2322 &changed,
2323 Some(&changed),
2324 &fp,
2325 &tm,
2326 (0, 0),
2327 24,
2328 3,
2329 &main,
2330 &mut writes,
2331 );
2332 assert_eq!(
2333 out.frame[b], 3,
2334 "a corroborated newer interior state still replaces the old one"
2335 );
2336 }
2337 #[test]
2338 fn two_clean_flat_observations_can_replace_a_veiled_consensus_but_nearby_structure_cannot() {
2339 let size = 128;
2340 for nearby_line in [false, true] {
2341 let mut c = Consensus::new(Grid::covering(0, 0, size, size));
2342 c.sig.fill([140; 3]);
2343 for v in &mut c.vote {
2344 v.tier = TIER_VEIL as i8;
2345 }
2346 let mut out = Output::new(&c, components(&c));
2347 let target = 3 * c.grid.bw + 3;
2348 let mask = vec![1; size * size];
2349 let clean = vec![TIER_CLEAN; size * size / 4];
2350 let fp = Footprint {
2351 x: 0,
2352 y: 0,
2353 width: size,
2354 height: size,
2355 mask: &mask,
2356 };
2357 let tm = TierMap {
2358 x: 0,
2359 y: 0,
2360 cw: size / 2,
2361 ch: size / 2,
2362 tier: &clean,
2363 };
2364 let main_pixels = solid_frame(size, size, 140);
2365 let coverage = vec![255; size * size / 8];
2366 let main = CellMap::from_canvas(&CanvasView {
2367 x: 0,
2368 y: 0,
2369 width: size,
2370 height: size,
2371 rgba: &main_pixels,
2372 coverage: &coverage,
2373 });
2374 let mut writes = Vec::new();
2375 for (i, value) in [200, 80, 80].into_iter().enumerate() {
2376 let mut rgba = solid_frame(size, size, value);
2377 if nearby_line {
2378 for y in 32..80 {
2379 rgba[(y * size + 66) * 4..(y * size + 66) * 4 + 3].fill(0);
2380 }
2381 }
2382 let f = Frame {
2383 rgba: &rgba,
2384 width: size,
2385 height: size,
2386 };
2387 out.emit(
2388 &c,
2389 &f,
2390 None,
2391 &fp,
2392 &tm,
2393 (0, 0),
2394 24,
2395 i as u32,
2396 &main,
2397 &mut writes,
2398 );
2399 if i < 2 {
2400 assert!(
2401 !writes.iter().any(|w| w.gx == 3 && w.gy == 3),
2402 "one observation cannot overturn a consensus"
2403 );
2404 }
2405 }
2406 assert_eq!(writes.iter().any(|w|w.gx==3&&w.gy==3&&w.frame==2),!nearby_line,"a nearby line may have moved across the block boundary; do not treat it as a flat background");
2407 if !nearby_line {
2408 assert_eq!(out.kind[target], Kind::Coherent);
2409 }
2410 }
2411 }
2412 #[test]
2413 fn context_rings_do_not_merge_independent_changing_objects_or_claim_unseen_blocks() {
2414 let mut c = Consensus::new(Grid::covering(0, 0, 128, 64));
2415 for v in &mut c.vote {
2416 v.tier = TIER_CLEAN as i8;
2417 v.clean_views = 4;
2418 }
2419 for i in [10, 12] {
2420 c.vote[i].changed_views = 4;
2421 }
2422 let parts = components(&c);
2423 assert_eq!(
2424 parts.len(),
2425 2,
2426 "an unchanged separator is context, not evidence of one changing object"
2427 );
2428 let mut ownership = vec![0u8; c.vote.len()];
2429 for part in &parts {
2430 for y in 0..part.bh {
2431 for x in 0..part.bw {
2432 if part.mask[y * part.bw + x] {
2433 ownership[(part.by + y) * c.grid.bw + part.bx + x] += 1;
2434 }
2435 }
2436 }
2437 }
2438 assert!(
2439 ownership.iter().all(|&n| n <= 1),
2440 "shared context must not be overwritten by two moments"
2441 );
2442 c.vote[11].changed_views = 4;
2443 assert_eq!(
2444 components(&c).len(),
2445 1,
2446 "truly touching dynamic blocks still form one object"
2447 );
2448 let mut c = Consensus::new(Grid::covering(0, 0, 64, 64));
2449 c.vote[0] = BlockVote {
2450 tier: TIER_CLEAN as i8,
2451 clean_views: 4,
2452 changed_views: 4,
2453 };
2454 let parts = components(&c);
2455 assert_eq!(
2456 parts[0].mask.iter().filter(|&&v| v).count(),
2457 1,
2458 "never observed context cannot make a complete moment impossible"
2459 );
2460 }
2461
2462 #[test]
2463 fn directly_contradicted_context_outranks_a_heuristic_clean_label() {
2464 let mask = vec![1; W * H];
2465 let coverage = vec![255; W * H / 8];
2466 let fp = Footprint {
2467 x: 0,
2468 y: 0,
2469 width: W,
2470 height: H,
2471 mask: &mask,
2472 };
2473 let clean = vec![TIER_CLEAN; W * H / 4];
2474 let veil = vec![TIER_VEIL; W * H / 4];
2475 let tm = TierMap {
2476 x: 0,
2477 y: 0,
2478 cw: W / 2,
2479 ch: H / 2,
2480 tier: &clean,
2481 };
2482 let vt = TierMap {
2483 x: 0,
2484 y: 0,
2485 cw: W / 2,
2486 ch: H / 2,
2487 tier: &veil,
2488 };
2489 let old = frame(0, false);
2490 let bad = frame(1, true);
2491 let old = Frame {
2492 rgba: &old,
2493 width: W,
2494 height: H,
2495 };
2496 let bad = Frame {
2497 rgba: &bad,
2498 width: W,
2499 height: H,
2500 };
2501 let mut c = Consensus::new(Grid::covering(0, 0, W, H));
2502 c.observe(&old, &fp, &tm, (0, 0));
2503 let part = Component {
2504 bx: 0,
2505 by: 0,
2506 bw: 3,
2507 bh: 3,
2508 mask: vec![true; 9],
2509 dynamic: (0..9).map(|i| i == 4).collect(),
2510 best: None,
2511 last_complete: Vec::new(),
2512 };
2513 let mut out = Output::new(&c, vec![part]);
2514 let main = CellMap::from_canvas(&CanvasView {
2515 x: 0,
2516 y: 0,
2517 width: W,
2518 height: H,
2519 rgba: old.rgba,
2520 coverage: &coverage,
2521 });
2522 let mut writes = Vec::new();
2523 out.emit(
2524 &c,
2525 &old,
2526 Some(&old),
2527 &fp,
2528 &vt,
2529 (0, 0),
2530 24,
2531 0,
2532 &main,
2533 &mut writes,
2534 );
2535 out.emit(
2536 &c,
2537 &bad,
2538 Some(&bad),
2539 &fp,
2540 &tm,
2541 (0, 0),
2542 24,
2543 1,
2544 &main,
2545 &mut writes,
2546 );
2547 assert_eq!(
2548 out.frame[5], 0,
2549 "a visible transient in unchanged context cannot be excused by a clean-tier prediction"
2550 );
2551 }
2552 #[test]
2553 fn observed_low_tier_context_remains_available_for_relearning() {
2554 let mut c = Consensus::new(Grid::covering(0, 0, 64, 64));
2555 for v in &mut c.vote {
2556 v.tier = 0;
2557 }
2558 c.vote[5] = BlockVote {
2559 tier: TIER_CLEAN as i8,
2560 clean_views: 4,
2561 changed_views: 4,
2562 };
2563 let parts = components(&c);
2564 assert_eq!(parts.len(), 1);
2565 assert_eq!(parts[0].mask.iter().filter(|&&v|v).count(),9,"observed context can become clean in the second model; only unseen context is unavailable");
2566 }
2567
2568 #[test]
2569 fn lower_tier_context_cannot_veto_a_cleaner_complete_observation() {
2570 let mask = vec![1; W * H];
2571 let coverage = vec![255; W * H / 8];
2572 let fp = Footprint {
2573 x: 0,
2574 y: 0,
2575 width: W,
2576 height: H,
2577 mask: &mask,
2578 };
2579 let clean = vec![TIER_CLEAN; W * H / 4];
2580 let veil = vec![TIER_VEIL; W * H / 4];
2581 let opaque = vec![0; W * H / 4];
2582 let tm = TierMap {
2583 x: 0,
2584 y: 0,
2585 cw: W / 2,
2586 ch: H / 2,
2587 tier: &clean,
2588 };
2589 let vt = TierMap {
2590 x: 0,
2591 y: 0,
2592 cw: W / 2,
2593 ch: H / 2,
2594 tier: &veil,
2595 };
2596 let ot = TierMap {
2597 x: 0,
2598 y: 0,
2599 cw: W / 2,
2600 ch: H / 2,
2601 tier: &opaque,
2602 };
2603 let old_pixels = frame(0, true);
2604 let clean_pixels = frame(1, false);
2605 let old = Frame {
2606 rgba: &old_pixels,
2607 width: W,
2608 height: H,
2609 };
2610 let later = Frame {
2611 rgba: &clean_pixels,
2612 width: W,
2613 height: H,
2614 };
2615 let mut c = Consensus::new(Grid::covering(0, 0, W, H));
2616 c.observe(&old, &fp, &ot, (0, 0));
2617 let part = Component {
2618 bx: 0,
2619 by: 0,
2620 bw: 3,
2621 bh: 3,
2622 mask: vec![true; 9],
2623 dynamic: (0..9).map(|i| i == 4).collect(),
2624 best: None,
2625 last_complete: Vec::new(),
2626 };
2627 let mut out = Output::new(&c, vec![part]);
2628 let main = CellMap::from_canvas(&CanvasView {
2629 x: 0,
2630 y: 0,
2631 width: W,
2632 height: H,
2633 rgba: old.rgba,
2634 coverage: &coverage,
2635 });
2636 let mut writes = Vec::new();
2637 out.emit(
2638 &c,
2639 &old,
2640 Some(&old),
2641 &fp,
2642 &vt,
2643 (0, 0),
2644 24,
2645 0,
2646 &main,
2647 &mut writes,
2648 );
2649 out.emit(
2650 &c,
2651 &later,
2652 Some(&later),
2653 &fp,
2654 &tm,
2655 (0, 0),
2656 24,
2657 1,
2658 &main,
2659 &mut writes,
2660 );
2661 assert_eq!(
2662 out.frame[5], 1,
2663 "an opaque first-pass reference cannot label a genuinely cleaner boundary transient"
2664 );
2665 }
2666 struct LocalReference(fn(i64, i64) -> [u8; 3]);
2667 impl CellRef for LocalReference {
2668 fn cell(&self, x: i64, y: i64) -> Option<[u8; 3]> {
2669 Some((self.0)(x, y))
2670 }
2671 fn cells(&self) -> (i64, i64, i64, i64) {
2672 (-1000, -1000, 1000, 1000)
2673 }
2674 }
2675 fn signature(reference: &impl CellRef) -> BlockSignature {
2676 std::array::from_fn(|k| {
2677 reference
2678 .cell((k % SIDE) as i64, (k / SIDE) as i64)
2679 .unwrap()
2680 })
2681 }
2682 #[test]
2683 fn strict_agreement_rejects_structure_absent_from_a_smooth_reference() {
2684 let reference = LocalReference(|_, _| [230; 3]);
2685 let expected = signature(&reference);
2686 let mut observed = expected;
2687 observed[3 * SIDE + 3] = [190; 3];
2688 let difference = compare(&observed, &expected);
2689 assert!(agrees(difference, 24));
2690 assert_eq!(
2691 difference.2, 0,
2692 "the legacy 48-level outlier threshold misses this mark"
2693 );
2694 assert!(
2695 !agrees_strictly(difference, 24, &observed, &reference, (0, 0)),
2696 "an isolated low-contrast mark is not codec phase on a flat page"
2697 );
2698 }
2699 #[test]
2700 fn strict_agreement_keeps_phase_changes_at_real_edges_including_block_boundaries() {
2701 for edge in [0, 4] {
2702 let reference = LocalReference(if edge == 0 {
2703 |x, y| [if x < 0 && y < 4 { 140 } else { 80 }; 3]
2704 } else {
2705 |x, y| [if x >= 4 && y < 4 { 140 } else { 80 }; 3]
2706 });
2707 let expected = signature(&reference);
2708 let mut observed = expected;
2709 let column = if edge == 0 { 0 } else { 3 };
2711 for y in 0..4 {
2712 observed[y * SIDE + column] = [110; 3];
2713 }
2714 let difference = compare(&observed, &expected);
2715 assert!(
2716 agrees_strictly(difference, 24, &observed, &reference, (0, 0)),
2717 "real contrast across a block boundary must remain available as phase evidence"
2718 );
2719 }
2720 }
2721 #[test]
2722 fn a_small_transient_cannot_freeze_an_otherwise_static_block() {
2723 let (width, height) = (160, 160);
2724 let mut clean = solid_frame(width, height, 230);
2725 for y in 0..height {
2726 for x in 0..width {
2727 let value = if (x / 8 + y / 8) % 2 == 0 { 230 } else { 248 };
2728 clean[(y * width + x) * 4..(y * width + x) * 4 + 3].fill(value);
2729 }
2730 }
2731 let mut transient = clean.clone();
2732 for y in 66..68 {
2733 for x in 66..68 {
2734 transient[(y * width + x) * 4..(y * width + x) * 4 + 3].fill(190);
2735 }
2736 }
2737 let mask = vec![1; width * height];
2738 let footprint = Footprint {
2739 x: 0,
2740 y: 0,
2741 width,
2742 height,
2743 mask: &mask,
2744 };
2745 let tier = vec![TIER_CLEAN; width * height / (CELL * CELL)];
2746 let tiers = TierMap {
2747 x: 0,
2748 y: 0,
2749 cw: width / CELL,
2750 ch: height / CELL,
2751 tier: &tier,
2752 };
2753 let clean_frame = Frame {
2754 rgba: &clean,
2755 width,
2756 height,
2757 };
2758 let mut consensus = Consensus::new(Grid::covering(0, 0, width, height));
2759 for x in [0, 16, 32] {
2761 consensus.observe(&clean_frame, &footprint, &tiers, (x, 0));
2762 }
2763 let mut output = Output::new(&consensus, components(&consensus));
2764 let coverage = vec![255; width * height / 8];
2765 let main = CellMap::from_canvas(&CanvasView {
2766 x: 0,
2767 y: 0,
2768 width,
2769 height,
2770 rgba: &clean,
2771 coverage: &coverage,
2772 });
2773 let mut writes = Vec::new();
2774 output.emit(
2775 &consensus,
2776 &Frame {
2777 rgba: &transient,
2778 width,
2779 height,
2780 },
2781 None,
2782 &footprint,
2783 &tiers,
2784 (0, 0),
2785 24,
2786 0,
2787 &main,
2788 &mut writes,
2789 );
2790 output.emit(
2791 &consensus,
2792 &clean_frame,
2793 None,
2794 &footprint,
2795 &tiers,
2796 (16, 0),
2797 24,
2798 1,
2799 &main,
2800 &mut writes,
2801 );
2802 let block = 4 * consensus.grid.bw + 4;
2803 assert_eq!(
2804 output.frame[block], 1,
2805 "the later clean view must replace the small transient"
2806 );
2807 assert_eq!(output.kind[block], Kind::Coherent);
2808 }
2809 #[test]
2810 fn local_contrast_allows_decode_noise_on_top_of_a_phase_shift() {
2811 let reference = LocalReference(|x, y| [if x < 0 && y < 4 { 120 } else { 100 }; 3]);
2812 let expected = signature(&reference);
2813 let mut observed = expected;
2814 for y in 0..4 {
2815 observed[y * SIDE] = [124; 3];
2816 }
2817 let difference = compare(&observed, &expected);
2818 assert!(agrees_strictly(difference,24,&observed,&reference,(0,0)),"a real 20-level edge plus four levels of decode noise is still a valid phase observation");
2819 }
2820 #[derive(Clone, Copy, PartialEq, Eq)]
2821 enum WeakUpdate {
2822 Persistent,
2823 Moving,
2824 SettledStill,
2825 LastFramePulse,
2826 }
2827
2828 fn weak_update_source(mode: WeakUpdate) -> (i32, Kind) {
2829 let persistent = mode != WeakUpdate::Moving;
2830 let views = if mode == WeakUpdate::SettledStill {
2831 2
2832 } else {
2833 3
2834 };
2835 let (width, height) = (160, 160);
2836 let old = solid_frame(width, height, 230);
2837 let mut first = old.clone();
2838 let mut second = old.clone();
2839 for y in 66..68 {
2840 for x in 66..68 {
2841 first[(y * width + x) * 4..(y * width + x) * 4 + 3].fill(190);
2842 }
2843 }
2844 let second_x = if persistent { 50 } else { 54 };
2845 for y in 66..68 {
2846 for x in second_x..second_x + 2 {
2847 second[(y * width + x) * 4..(y * width + x) * 4 + 3].fill(190);
2848 }
2849 }
2850 let mask = vec![1; width * height];
2851 let footprint = Footprint {
2852 x: 0,
2853 y: 0,
2854 width,
2855 height,
2856 mask: &mask,
2857 };
2858 let tier = vec![TIER_CLEAN; width * height / (CELL * CELL)];
2859 let tiers = TierMap {
2860 x: 0,
2861 y: 0,
2862 cw: width / CELL,
2863 ch: height / CELL,
2864 tier: &tier,
2865 };
2866 let frames = [
2867 Frame {
2868 rgba: &old,
2869 width,
2870 height,
2871 },
2872 Frame {
2873 rgba: &first,
2874 width,
2875 height,
2876 },
2877 Frame {
2878 rgba: &second,
2879 width,
2880 height,
2881 },
2882 ];
2883 let poses = [(-80, 0), (0, 0), (16, 0)];
2884 let mut consensus = Consensus::new(Grid::covering(-80, 0, 256, height));
2885 for (frame, pose) in frames.iter().zip(poses).take(views) {
2886 consensus.observe(frame, &footprint, &tiers, pose);
2887 }
2888 let mut output = Output::new(&consensus, components(&consensus));
2889 let main_pixels = solid_frame(256, height, 230);
2890 let coverage = vec![255; 256 * height / 8];
2891 let main = CellMap::from_canvas(&CanvasView {
2892 x: -80,
2893 y: 0,
2894 width: 256,
2895 height,
2896 rgba: &main_pixels,
2897 coverage: &coverage,
2898 });
2899 let mut writes = Vec::new();
2900 for (index, (frame, pose)) in frames.iter().zip(poses).take(views).enumerate() {
2901 let before = match (mode, index) {
2902 (WeakUpdate::SettledStill, 1) => Some(&frames[1]),
2903 (WeakUpdate::LastFramePulse, 2) => Some(&frames[0]),
2904 _ => None,
2905 };
2906 output.emit(
2907 &consensus,
2908 frame,
2909 before,
2910 &footprint,
2911 &tiers,
2912 pose,
2913 24,
2914 index as u32,
2915 &main,
2916 &mut writes,
2917 );
2918 }
2919 let block = 4 * consensus.grid.bw + (4 - consensus.grid.gx) as usize;
2920 (output.frame[block], output.kind[block])
2921 }
2922 #[test]
2923 fn persistent_weak_content_can_replace_an_earlier_edge_view() {
2924 let (source, kind) = weak_update_source(WeakUpdate::Persistent);
2925 assert!(
2926 source == 1 || source == 2,
2927 "a newly loaded weak mark must not be lost merely because the early reference is blank"
2928 );
2929 assert_eq!(kind, Kind::Coherent);
2930 }
2931 #[test]
2932 fn different_weak_marks_do_not_corroborate_each_other() {
2933 let (source, kind) = weak_update_source(WeakUpdate::Moving);
2934 assert_eq!(
2935 source, 0,
2936 "two moving marks that merely both resemble the reference are not corroboration"
2937 );
2938 assert_ne!(kind, Kind::Coherent);
2939 }
2940 #[test]
2941 fn weak_content_can_settle_at_the_end_of_one_pose() {
2942 let (source, kind) = weak_update_source(WeakUpdate::SettledStill);
2943 assert_eq!(
2944 source, 1,
2945 "the actual previous still frame can corroborate freshly loaded content"
2946 );
2947 assert_eq!(kind, Kind::Coherent);
2948 }
2949 #[test]
2950 fn an_old_witness_cannot_confirm_a_new_pulse_at_the_end_of_a_still_run() {
2951 let (source, kind) = weak_update_source(WeakUpdate::LastFramePulse);
2952 assert_eq!(
2953 source, 0,
2954 "a contradictory immediate predecessor overrides the older witness"
2955 );
2956 assert_ne!(kind, Kind::Coherent);
2957 }
2958 #[test]
2962 fn phase_compatible_edges_need_temporal_evidence_before_becoming_permanent() {
2963 let size = 128;
2964 let edge = |at: usize| {
2965 let mut rgba = solid_frame(size, size, 190);
2966 for y in 0..size {
2967 for x in at..size {
2968 rgba[(y * size + x) * 4..(y * size + x) * 4 + 3].fill(210);
2969 }
2970 }
2971 rgba
2972 };
2973 let base = edge(66);
2974 let coverage = vec![255; size * size / 8];
2975 let main = CellMap::from_canvas(&CanvasView {
2976 x: 0,
2977 y: 0,
2978 width: size,
2979 height: size,
2980 rgba: &base,
2981 coverage: &coverage,
2982 });
2983 let mask = vec![1; size * size];
2984 let clean = vec![TIER_CLEAN; size * size / 4];
2985 let fp = Footprint {
2986 x: 0,
2987 y: 0,
2988 width: size,
2989 height: size,
2990 mask: &mask,
2991 };
2992 let tiers = TierMap {
2993 x: 0,
2994 y: 0,
2995 cw: size / 2,
2996 ch: size / 2,
2997 tier: &clean,
2998 };
2999 let reference = Frame {
3000 rgba: &base,
3001 width: size,
3002 height: size,
3003 };
3004 for persistent in [false, true] {
3005 let mut consensus = Consensus::new(Grid::covering(0, 0, size, size));
3006 for _ in 0..4 {
3007 consensus.observe(&reference, &fp, &tiers, (0, 0));
3008 }
3009 let target = 4 * consensus.grid.bw + 4;
3010 let mut output = Output::new(&consensus, components(&consensus));
3011 let mut first = if persistent { edge(64) } else { base.clone() };
3012 if !persistent {
3013 for y in 66..78 {
3014 for x in 64..66 {
3015 first[(y * size + x) * 4..(y * size + x) * 4 + 3].fill(210);
3016 }
3017 }
3018 }
3019 output.emit(
3020 &consensus,
3021 &Frame {
3022 rgba: &first,
3023 width: size,
3024 height: size,
3025 },
3026 None,
3027 &fp,
3028 &tiers,
3029 (0, 0),
3030 24,
3031 0,
3032 &main,
3033 &mut Vec::new(),
3034 );
3035 assert_ne!(
3036 output.kind[target],
3037 Kind::Coherent,
3038 "a phase-sized protrusion is not yet independently confirmed"
3039 );
3040 if persistent {
3041 assert!(output.finish(&consensus,24).iter().any(|part|part.gx==4 && part.gy==4 && part.frame==0),
3042 "a lone valid phase view stays available; deferring a lock must not discard its pixels");
3043 }
3044 let second = edge(if persistent { 63 } else { 65 });
3045 output.emit(
3046 &consensus,
3047 &Frame {
3048 rgba: &second,
3049 width: size,
3050 height: size,
3051 },
3052 None,
3053 &fp,
3054 &tiers,
3055 (1, 0),
3056 24,
3057 1,
3058 &main,
3059 &mut Vec::new(),
3060 );
3061 assert_eq!(
3062 output.kind[target],
3063 Kind::Coherent,
3064 "a clean reference match or a corroborated phase is accepted"
3065 );
3066 assert_eq!(output.frame[target], 1);
3067 }
3068 }
3069
3070 #[test]
3074 fn context_contradiction_cases() {
3075 let page = [[239u8, 235, 227]; CELLS];
3076 let card = [[250u8, 248, 244]; CELLS];
3077 let noisy = [[241u8, 236, 228]; CELLS];
3078 let mut cursor = page;
3079 cursor[9] = [0, 0, 0];
3080 assert!(!contradicts(&page, &page, 24, || false));
3081 assert!(!contradicts(&noisy, &page, 24, || false), "codec noise");
3082 assert!(
3083 contradicts(&card, &page, 24, || false),
3084 "a low-contrast card edge in a context block"
3085 );
3086 assert!(
3087 !contradicts(&card, &page, 24, || true),
3088 "the same difference confirmed by the stationary run's previous frame"
3089 );
3090 assert!(contradicts(&cursor, &page, 24, || true), "a luma outlier");
3091 let mut texture = [[0u8; 3]; CELLS];
3092 let mut refined = [[0u8; 3]; CELLS];
3093 for k in 0..CELLS {
3094 let v = if (k % SIDE + k / SIDE).is_multiple_of(2) {
3095 100
3096 } else {
3097 140
3098 };
3099 texture[k] = [v; 3];
3100 refined[k] = [v + 12; 3];
3101 }
3102 assert!(
3103 !contradicts(&refined, &texture, 24, || false),
3104 "a sub-outlier change of textured context (map imagery refining)"
3105 );
3106 }
3107}