Return flow sample locations with an active flag and a fixed colour ramp

The selector returned only the arrows it would draw, so a view could not draw
anything at a quiet sample and the below-threshold branch was unreachable. It
now returns every candidate location plus an `active` flag, both callers filter
on it, and the magnitude colour ramp is fixed so one magnitude is one colour
across frames. Verified by pixel parity against HEAD for the 2D canvas and the
3D overlay, and by the shared and studio suites.
This commit is contained in:
ykcheungab
2026-10-04 10:35:56 +08:00
parent dda6c74fb7
commit ca28d788c2
7 changed files with 426 additions and 45 deletions
@@ -249,7 +249,9 @@ def _canvas_and_arrow(assets, orientation: str, direction: tuple[float, float]):
mask = np.asarray(mapping.valid, dtype=bool)
flow, (row, col) = _patch_flow(mask, *direction)
policy = _policy()
samples = select_flow_samples(flow, mask, policy)
# The selector returns every candidate grid location with an ``active`` flag; this test
# compares the direction of the single *active* sample, so take the active view explicitly.
samples = select_flow_samples(flow, mask, policy).active_only()
assert samples.rows.size == 1, f"expected one sample, got {samples.rows.size}"
frame = ImageFrame(
+8 -2
View File
@@ -94,6 +94,11 @@ def test_2d_3d_flow_sample_points_match() -> None:
above the old hysteresis floor, below the threshold the canvas applies. Both views
must agree on frame B, which is what fails if the composer re-acquires state the
canvas does not have.
The comparison is over the **active** samples, because that is what each view draws:
the selector now returns every candidate location and an ``active`` flag, and both
callers act on the flag. Comparing raw candidate counts would compare two things
neither figure draws.
"""
assets = poc_scene()
h, w = assets.shape_hw
@@ -117,9 +122,10 @@ def test_2d_3d_flow_sample_points_match() -> None:
0 if composed.flow_arrows is None else int(composed.flow_arrows.origins.shape[0])
)
# Exactly what the 2D canvas draws from: ``draw_arrow_display`` calls
# ``select_flow_samples`` with the policy the window passes it.
# ``select_flow_samples`` with the policy the window passes it, and draws the
# samples the flag marks active.
samples_2d = select_flow_samples(flow, mask, policy)
return arrows_3d, int(samples_2d.rows.size)
return arrows_3d, samples_2d.active_only().rows.size
above, above_2d = both_views(0.30) # |2*flow| = 0.6 px, clearly on
assert above == 1 and above_2d == 1, (above, above_2d)