"""Tests der geglätteten Wurfgeschwindigkeit.""" import random import pytest from sandbox.interaction import VelocityTracker from sandbox.physics import Vector2 WINDOW = 0.1 SMOOTHING = 0.05 def _track(velocity: Vector2, fps: float, seconds: float) -> VelocityTracker: """Bewegt einen Zeiger mit konstanter Geschwindigkeit und liefert den Tracker.""" tracker = VelocityTracker(WINDOW, SMOOTHING) tracker.reset(0.0, Vector2(0, 0)) frames = round(seconds * fps) for frame in range(1, frames + 1): t = frame / fps tracker.add_sample(t, velocity * t) return tracker def test_constant_motion_is_measured_exactly() -> None: tracker = _track(Vector2(600, -200), fps=60, seconds=0.5) assert tracker.velocity.is_close(Vector2(600, -200), tolerance=1.0) def test_result_does_not_depend_on_the_frame_rate() -> None: slow = _track(Vector2(500, 0), fps=30, seconds=0.5) fast = _track(Vector2(500, 0), fps=144, seconds=0.5) assert slow.velocity.x == pytest.approx(fast.velocity.x, rel=0.01) def test_velocity_decays_when_the_pointer_stops() -> None: tracker = _track(Vector2(800, 0), fps=60, seconds=0.3) resting = Vector2(800 * 0.3, 0) for frame in range(1, 31): # eine halbe Sekunde stillhalten tracker.add_sample(0.3 + frame / 60, resting) if frame == 12: # nach 0,2 s ist der Schwung auf wenige Prozent abgeklungen assert tracker.velocity.length() < 0.1 * 800 assert tracker.velocity.length() < 1.0 def test_jitter_is_smoothed() -> None: rng = random.Random(0) tracker = VelocityTracker(WINDOW, SMOOTHING) tracker.reset(0.0, Vector2(0, 0)) for frame in range(1, 61): t = frame / 60 noise = Vector2(rng.uniform(-3, 3), rng.uniform(-3, 3)) # zittrige Hand tracker.add_sample(t, Vector2(400 * t, 0) + noise) assert tracker.velocity.x == pytest.approx(400, rel=0.1) assert abs(tracker.velocity.y) < 60 def test_samples_with_the_same_timestamp_replace_each_other() -> None: tracker = VelocityTracker(WINDOW, SMOOTHING) tracker.reset(0.0, Vector2(0, 0)) tracker.add_sample(0.0, Vector2(50, 0)) # gleicher Frame: keine Division durch 0 assert tracker.velocity == Vector2(0, 0) tracker.add_sample(0.1, Vector2(50, 0)) assert tracker.velocity.x == 0 # die ersetzte Position ist der neue Bezugspunkt def test_first_sample_without_reset_starts_a_measurement() -> None: tracker = VelocityTracker(WINDOW, SMOOTHING) tracker.add_sample(1.0, Vector2(5, 5)) assert tracker.velocity == Vector2(0, 0) def test_invalid_parameters_are_rejected() -> None: with pytest.raises(ValueError, match="positiv"): VelocityTracker(0, SMOOTHING)