def test_trig_invariance(angle: float, n: int): """Test that cos(θ), sin(θ) ≃ cos(θ + n*360°), sin(θ + n*360°)""" r_cos, r_sin = Vector._trig(angle) n_cos, n_sin = Vector._trig(angle + 360 * n) note(f"δcos: {r_cos - n_cos}") assert isclose(r_cos, n_cos, rel_to=[n / 1e9]) note(f"δsin: {r_sin - n_sin}") assert isclose(r_sin, n_sin, rel_to=[n / 1e9])
def test_trig_stability(angle): """cos² + sin² == 1 We are testing that this equation holds, as otherwise rotations would (slightly) change the length of vectors they are applied to. """ r_cos, r_sin = Vector._trig(angle) # Don't use exponents here. Multiplication is generally more stable. assert math.isclose(r_cos * r_cos + r_sin * r_sin, 1, rel_tol=1e-18)
def test_remarkable_angles(angle, trig): """Test that our table of remarkable angles agrees with Vector._trig. This is useful both as a consistency test of the table, and as a test of Vector._trig (which Vector.rotate uses). """ cos_t, sin_t = trig cos_m, sin_m = Vector._trig(angle) assert isclose(sin_t, sin_m, abs_tol=0, rel_tol=1e-14) assert isclose(cos_t, cos_m, abs_tol=0, rel_tol=1e-14)
def test_dot_rotational_invariance(x: Vector, y: Vector, angle: float): """Test that rotating vectors doesn't change their dot product.""" t = x.angle(y) cos_t, _ = Vector._trig(t) note(f"θ: {t}") note(f"cos θ: {cos_t}") # Exclude near-orthogonal test inputs assume(abs(cos_t) > 1e-6) assert isclose(x * y, x.rotate(angle) * y.rotate(angle), rel_to=(x, y), rel_exp=2)
def test_dot_from_angle(x: Vector, y: Vector): """Test x · y == |x| · |y| · cos(θ)""" t = x.angle(y) cos_t, _ = Vector._trig(t) # Dismiss near-othogonal test inputs assume(abs(cos_t) > 1e-6) min_len, max_len = sorted((x.length, y.length)) geometric = min_len * (max_len * cos_t) note(f"θ: {t}") note(f"cos θ: {cos_t}") note(f"algebraic: {x * y}") note(f"geometric: {geometric}") assert isclose(x * y, geometric, rel_to=(x, y), rel_exp=2)
def test_trig_stability(angle): """cos² + sin² == 1 We are testing that this equation holds, as otherwise rotations would (slightly) change the length of vectors they are applied to. Moreover, Vector._trig should get closer to fulfilling it than math.{cos,sin}. """ r_cos, r_sin = Vector._trig(angle) r_len = r_cos * r_cos + r_sin * r_sin # Don't use exponents here. Multiplication is generally more stable. assert math.isclose(r_len, 1, rel_tol=1e-18) t_cos, t_sin = cos(radians(angle)), sin(radians(angle)) t_len = t_cos * t_cos + t_sin * t_sin assert fabs(1 - r_len) <= fabs(1 - t_len)