def test_reflect_angle(initial: Vector, normal: Vector): """Test angle-related properties of Vector.reflect: * initial.reflect(normal) * normal == - initial * normal * normal.angle(initial) == 180 - normal.angle(reflected) """ # Exclude initial vectors that are very small or very close to the surface. assume(not angle_isclose(initial.angle(normal) % 180, 90, epsilon=10)) assume(initial.length > 1e-10) reflected = initial.reflect(normal) assert isclose((initial * normal), -(reflected * normal)) assert angle_isclose(normal.angle(initial), 180 - normal.angle(reflected))
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_angle_aligned(x: Vector, scalar: float): """x.angle(scalar * x) is 0 or 180, depending on whether scalar > 0""" assume(scalar != 0) y = scalar * x assert angle_isclose(x.angle(y), 0 if scalar > 0 else 180)
def test_angle(left, right, expected): left, right = Vector(left), Vector(right) lr = left.angle(right) rl = right.angle(left) assert angle_isclose(lr, expected) assert angle_isclose(rl, -expected)