76 lines
2.7 KiB
Python
76 lines
2.7 KiB
Python
"""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)
|