Mark every below-threshold finger sample with an idle dot
The selector now returns candidate locations with an activity flag, so the quiet half of the finger's sampling grid can be drawn at last: one radius-1 dot per candidate, in the idle tint and off the flow ramp, on no sample that also carries an arrow. The dots are `DRAW_IDLE_GRID_DOTS` (on) and `draw_arrow_display(idle_dots=...)`; box and mini keep their own SDK path.
This commit is contained in:
@@ -97,6 +97,46 @@ def test_the_location_cap_is_a_location_budget() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_the_threshold_decides_the_marker_and_never_the_location() -> None:
|
||||
"""The two markers are one grid read two ways: only the verdict moves with a threshold.
|
||||
|
||||
The 2D canvas draws an arrow for an ``active`` sample and a quiet dot for an inactive
|
||||
one, both at the candidate location this selector returns. So the candidate set — the
|
||||
marker set, once each sample carries exactly one marker — is the policy's grid and
|
||||
nothing else, while ``flow_on_px`` decides how many of them are arrows. A threshold
|
||||
that moved a location would move the idle grid with it, which is what makes the dots
|
||||
comparable from frame to frame.
|
||||
"""
|
||||
h, w = 30, 30
|
||||
flow = np.zeros((h, w, 2), dtype=np.float64)
|
||||
# 6 px displacements of 0.1 .. 1.5 px, each on the 5 px grid (2 + 5k) the policy
|
||||
# samples at, so each threshold step moves exactly one sample across.
|
||||
for index, value in enumerate((0.05, 0.1, 0.2, 0.35, 0.5, 0.75)):
|
||||
flow[2 + 5 * index, 2 + 5 * index] = (value, 0.0)
|
||||
|
||||
locations: list[tuple[int, int]] | None = None
|
||||
arrows: list[int] = []
|
||||
for on_px in (0.2, 0.4, 0.6, 0.9, 1.2):
|
||||
policy = VisualPolicy(
|
||||
flow_on_px=on_px,
|
||||
flow_off_px=max(0.0, on_px - 0.1),
|
||||
flow_grid_spacing=5,
|
||||
flow_edge_margin=0,
|
||||
).normalised()
|
||||
samples = select_flow_samples(flow, None, policy)
|
||||
here = list(zip(samples.rows.tolist(), samples.cols.tolist()))
|
||||
if locations is None:
|
||||
locations = here
|
||||
assert here == locations, (
|
||||
f"the candidate locations moved with the threshold at {on_px} px"
|
||||
)
|
||||
# One marker per sample, and which marker is the threshold's whole decision.
|
||||
assert samples.active.size == len(here)
|
||||
assert samples.active_count + int((~samples.active).sum()) == len(here)
|
||||
arrows.append(samples.active_count)
|
||||
assert arrows == [5, 4, 3, 2, 1], f"the sweep did not move one sample per step: {arrows}"
|
||||
|
||||
|
||||
def test_paint_domain_background_colours() -> None:
|
||||
mask = np.zeros((4, 4), dtype=bool)
|
||||
mask[1:3, 1:3] = True
|
||||
|
||||
Reference in New Issue
Block a user