def test_magnitude_of_a_normalized_vector(): # Given v = Vector(1, 2, 3) # When norm = v.normalize() # Then assert norm.magnitude() == 1
def test_multiplying_by_the_inverse_of_a_scaling_matrix(): # Given transform = scaling(2, 3, 4) inv = transform.inverse() v = Vector(-4, 6, 8) # Then assert inv * v == Vector(-2, 2, 2)
def test_reflecting_a_vector_approaching_at_45_degree(): # Given v = Vector(1, -1, 0) n = Vector(0, 1, 0) # When r = v.reflect(n) # Then assert r == Vector(1, 1, 0)
def test_reflecting_a_vector_off_a_slanted_surface(): # Given v = Vector(0, -1, 0) n = Vector(sqrt(2) / 2, sqrt(2) / 2, 0) # When r = v.reflect(n) # Then assert r == Vector(1, 0, 0)
def test_scaling_a_ray(): # Given r = Ray(Point(1, 2, 3), Vector(0, 1, 0)) m = scaling(2, 3, 4) # When r2 = r.transform(m) # Then assert r2.origin == Point(2, 6, 12) assert r2.direction == Vector(0, 3, 0)
def test_translating_a_ray(): # Given r = Ray(Point(1, 2, 3), Vector(0, 1, 0)) m = translation(3, 4, 5) # When r2 = r.transform(m) # Then assert r2.origin == Point(4, 6, 8) assert r2.direction == Vector(0, 1, 0)
def precomputing_the_reflection_vector(): # Given shape = Plane() r = Ray(Point(0, 1, -1), Vector(0, -sqrt(2) / 2, sqrt(2) / 2)) i = Intersection(sqrt(2), shape) # When comps = i.prepare_computations(r) # Then assert comps.reflectv == Vector(0, sqrt(2) / 2, sqrt(2) / 2)
def local_normal_at(self, point): dist = point.x ** 2 + point.z ** 2 if dist < 1 and point.y >= self.maximum - EPSILON: return Vector(0, 1, 0) elif dist < 1 and point.y <= self.minimum + EPSILON: return Vector(0, -1, 0) else: return Vector(point.x, 0, point.z)
def local_normal_at(self, point): maxc = max(abs(point.x), abs(point.y), abs(point.z)) if maxc == abs(point.x): return Vector(point.x, 0, 0) elif maxc == abs(point.y): return Vector(0, point.y, 0) else: return Vector(0, 0, point.z)
def test_intersecting_a_scaled_shape_with_a_ray(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) s = MockShape() # When s.transform = scaling(2, 2, 2) _ = s.intersect(r) # Then assert s.saved_ray.origin == Point(0, 0, -2.5) assert s.saved_ray.direction == Vector(0, 0, 0.5)
def test_intersecting_a_translated_shape_with_a_ray(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) s = MockShape() # When s.transform = translation(5, 0, 0) _ = s.intersect(r) # Then assert s.saved_ray.origin == Point(-5, 0, -5) assert s.saved_ray.direction == Vector(0, 0, 1)
def test_the_normal_of_a_plane_is_constant_everywhere(): # Given p = Plane() # When n1 = p.local_normal_at(Point(0, 0, 0)) n2 = p.local_normal_at(Point(10, 0, -10)) n3 = p.local_normal_at(Point(-5, 0, 150)) # Then assert n1 == Vector(0, 1, 0) assert n2 == Vector(0, 1, 0) assert n3 == Vector(0, 1, 0)
def local_normal_at(self, point): dist = point.x**2 + point.z**2 if dist < abs(point.y) and point.y >= self.maximum - EPSILON: return Vector(0, 1, 0) elif dist < abs(point.y) and point.y <= self.minimum + EPSILON: return Vector(0, -1, 0) y = sqrt(point.x**2 + point.z**2) if point.y > 0: y = -y return Vector(point.x, y, point.z)
def test_lighting_with_the_eye_opposite_surface_light_offset_45degree(): # Given m = Material() position = Point(0, 0, 0) eyev = Vector(0, 0, -1) normalv = Vector(0, 0, -1) light = PointLight(Point(0, 10, -10), Color(1, 1, 1)) object = Sphere() # When result = m.lighting(object, light, position, eyev, normalv) # Then assert result == Color(0.7364, 0.7364, 0.7364)
def test_lighting_with_the_eye_in_the_path_of_the_reflection_vector(): # Given m = Material() position = Point(0, 0, 0) eyev = Vector(0, -sqrt(2) / 2, -sqrt(2) / 2) normalv = Vector(0, 0, -1) light = PointLight(Point(0, 10, -10), Color(1, 1, 1)) object = Sphere() # When result = m.lighting(object, light, position, eyev, normalv) # Then assert result == Color(1.6364, 1.6364, 1.6364)
def test_lighting_with_the_light_behind_the_surface(): # Given m = Material() position = Point(0, 0, 0) eyev = Vector(0, 0, -1) normalv = Vector(0, 0, -1) light = PointLight(Point(0, 0, 10), Color(1, 1, 1)) object = Sphere() # When result = m.lighting(object, light, position, eyev, normalv) # Then assert result == Color(0.1, 0.1, 0.1)
def test_the_hit__when_an_intersection_occurs_on_the_inside(): # Given r = Ray(Point(0, 0, 0), Vector(0, 0, 1)) shape = Sphere() i = Intersection(1, shape) # When comps = i.prepare_computations(r) # Then assert comps.point == Point(0, 0, 1) assert comps.eyev == Vector(0, 0, -1) assert comps.inside is True assert comps.normalv == Vector(0, 0, -1)
def test_lighting_with_the_eye_between_light_and_surface_eye_offset_45degree(): # Given m = Material() position = Point(0, 0, 0) eyev = Vector(0, sqrt(2) / 2, sqrt(2) / 2) normalv = Vector(0, 0, -1) light = PointLight(Point(0, 0, -10), Color(1, 1, 1)) object = Sphere() # When result = m.lighting(object, light, position, eyev, normalv) # Then assert result == Color(1.0, 1.0, 1.0)
def test_precomputing_the_state_of_an_intersection(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = Sphere() i = Intersection(4, shape) # When comps = i.prepare_computations(r) # Then assert comps.t == i.t assert comps.object == i.object assert comps.point == Point(0, 0, -1) assert comps.eyev == Vector(0, 0, -1) assert comps.normalv == Vector(0, 0, -1)
def test_converting_a_normal_from_object_to_world_space(): # Given g1 = Group() g1.transform = rotation_y(pi / 2) g2 = Group() g2.transform = scaling(1, 2, 3) g1.add_child(g2) s = Sphere() s.transform = translation(5, 0, 0) g2.add_child(s) # When n = s.normal_to_world(Vector(sqrt(3) / 3, sqrt(3) / 3, sqrt(3) / 3)) # Then assert n == Vector(0.28571, 0.42857, -0.85714)
def test_computing_the_normal_vector_on_a_cone(): EXAMPLES = [ # point normal (Point(0, 0, 0), Vector(0, 0, 0)), (Point(1, 1, 1), Vector(1, -sqrt(2), 1)), (Point(-1, -1, 0), Vector(-1, 1, 0)), ] for point, normal in EXAMPLES: # Given shape = Cone() # When n = shape.local_normal_at(point) # Then assert n == normal
def test_the_cross_product_of_two_vectors(): # Given a = Vector(1, 2, 3) b = Vector(2, 3, 4) # Then assert a.cross(b) == Vector(-1, 2, -1) assert b.cross(a) == Vector(1, -2, 1)
def test_normal_vector_on_a_cylinder(): EXAMPLES = [ # point normal (Point(1, 0, 0), Vector(1, 0, 0)), (Point(0, 5, -1), Vector(0, 0, -1)), (Point(0, -2, 1), Vector(0, 0, 1)), (Point(-1, 1, 0), Vector(-1, 0, 0)), ] for point, normal in EXAMPLES: # Given cyl = Cylinder() # When n = cyl.local_normal_at(point) # Then assert n == normal
def test_a_ray_misses_a_cone(): EXAMPLES = [ # origin direction (Vector(1, 1, 0), Point(1, 1, 0)), (Vector(0, 1.5, -5), Point(0.574427, 0.183397, 1.023327)), ] for origin, direction in EXAMPLES: # Given shape = Cone() dir = direction.normalize() r = Ray(origin, dir) # When xs = shape.local_intersect(r) # Then assert len(xs) == 0
def test_the_normal_on_a_sphere_at_a_point_at_a_nonaxial_axis(): # Given s = Sphere() # When n = s.normal_at(Point(sqrt(3) / 3, sqrt(3) / 3, sqrt(3) / 3)) # Then assert n == Vector(sqrt(3) / 3, sqrt(3) / 3, sqrt(3) / 3)
def test_the_normal_on_a_sphere_at_a_point_on_the_z_axis(): # Given s = Sphere() # When n = s.normal_at(Point(0, 0, 1)) # Then assert n == Vector(0, 0, 1)
def test_a_ray_misses_a_cylinder(): EXAMPLES = [ # origin direction (Point(1, 0, 0), Vector(0, 1, 0)), (Point(0, 0, 0), Vector(0, 1, 0)), (Point(0, 0, -5), Vector(1, 1, 1)), ] for origin, direction in EXAMPLES: # Given cyl = Cylinder() dir = direction.normalize() r = Ray(origin, dir) # When xs = cyl.local_intersect(r) # Then assert len(xs) == 0
def test_the_color_when_a_ray_misses__only_negative_intersection_t(): w = World() w.light = PointLight(Point(-10, 10, -10), Color(1, 1, 1)) w.objects = [Plane()] r = Ray(Point(0, 10, 0), Vector(0, 1, 1).normalize()) c = w.color_at(r) assert c == Color(0, 0, 0)
def test_a_ray_misses_s_sphere(): # Given r = Ray(Point(0, 2, -5), Vector(0, 0, 1)) s = Sphere() # When xs = s.intersect(r) # Then assert len(xs) == 0
def test_computing_the_normal_on_a_translated_sphere(): # Given s = Sphere() s.transform = translation(0, 1, 0) # When n = s.normal_at(Point(0, 1.70711, -0.70711)) # Then assert n == Vector(0, 0.70711, -0.70711)