qemu: structural hash relative to the background, taps that land on the pixel

imgtools.structural() marked a cell occupied when its grey exceeded an
absolute 10/255, and the WardenOS page background is grey 16: every cell
of every region read occupied and no structural check could ever fail
(#19). Occupancy is now grey deviating from the crop's own median by more
than DEVIATION_THRESHOLD, or edge energy above EDGE_THRESHOLD. Measured on
real captures: a switch knob left/right differs in 240 of 256 cells (was
0), a dark card reads its icon and text and nothing else. Every committed
reference is recaptured with flare-edge tools/flow-run-all.sh --capture.

qmp.py to_axis() truncated the pixel-to-axis conversion and LVGL's
evdev calibration truncates on the way back, so many pixels landed one
short (130 -> 5924 -> 129) and a tap could miss the control hit had just
confirmed at that pixel (#21). It now rounds up to the smallest axis value
that truncates to the requested pixel; test_qmp_drive.py asserts the round
trip for every pixel at three panel sizes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3G6m9Aw5RyVY4ZowtKzEj
This commit is contained in:
Noah
2026-09-08 13:53:59 -06:00
co-authored by Claude Fable 5.1
parent f809c6e5a3
commit bd5c5af9db
3 changed files with 56 additions and 8 deletions
+11
View File
@@ -85,6 +85,17 @@ def run_script(text, refs=None):
class PureHelpers(unittest.TestCase):
def test_every_pixel_round_trips_through_lvgl_calibration(self):
# lv_evdev.c _evdev_calibrate: px = axis * (width - 1) / AXIS_MAX,
# integer division. A tap requested at px must land at px, for every
# px, or a hit-confirmed target can be missed by one pixel.
for size in (720, 480, 1024):
for px in range(size):
axis = qmp.to_axis(px, size)
self.assertTrue(0 <= axis <= qmp.AXIS_MAX)
back = axis * (size - 1) // qmp.AXIS_MAX
self.assertEqual(back, px, f"size {size}: px {px} -> axis {axis} -> {back}")
def test_resolve_path_and_ops(self):
doc = {"a": {"b": [5, 6]}, "s": "connected"}
self.assertEqual(qmp.resolve_path(doc, "a.b[1]"), (6, None))