def test_assigning_a_transformation(): # Given s = MockShape() # When s.transform = translation(2, 3, 4) # Then assert s.transform == translation(2, 3, 4)
def test_assigning_a_transformation(): # Given pattern = MockPattern() # When pattern.transform = translation(1, 2, 3) # Then assert pattern.transform == translation(1, 2, 3)
def test_finding_n1_and_n2_at_various_intersections(): # Given A = glass_sphere() A.transform = scaling(2, 2, 2) A.material.refractive_index = 1.5 B = glass_sphere() B.transform = translation(0, 0, -0.25) B.material.refractive_index = 2.0 C = glass_sphere() C.transform = translation(0, 0, 0.25) C.material.refractive_index = 2.5 r = Ray(Point(0, 0, -4), Vector(0, 0, 1)) xs = Intersections(Intersection(2, A), Intersection(2.75, B), Intersection(3.25, C), Intersection(4.75, B), Intersection(5.25, C), Intersection(6, A)) EXAMPLES = [ { 'n1': 1.0, 'n2': 1.5 }, # index: 0 { 'n1': 1.5, 'n2': 2.0 }, # index: 1 { 'n1': 2.0, 'n2': 2.5 }, # index: 2 { 'n1': 2.5, 'n2': 2.5 }, # index: 3 { 'n1': 2.5, 'n2': 1.5 }, # index: 4 { 'n1': 1.5, 'n2': 1.0 }, # index: 5 ] for index in range(len(EXAMPLES)): # When comps = xs[index].prepare_computations(r, xs=xs) # Then assert comps.n1 == EXAMPLES[index]['n1'] assert comps.n2 == EXAMPLES[index]['n2']
def test_multiplying_by_the_inverse_of_a_translation_matrix(): # Given transform = translation(5, -3, 2) inv = transform.inverse() p = Point(-3, 4, 5) # Then assert inv * p == Point(-8, 7, 3)
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)
def test_constructing_a_ray_when_the_camera_is_transformed(): # Given c = Camera(201, 101, pi / 2) # When c.transform = rotation_y(pi / 4) * translation(0, -2, 5) r = c.ray_for_pixel(100, 50) # Then assert r.origin == Point(0, 2, -5) assert r.direction == Vector(sqrt(2) / 2, 0, -sqrt(2) / 2)
def test_the_view_transformation_moves_the_world(): # Given f = Point(0, 0, 8) to = Point(0, 0, 0) up = Vector(0, 1, 0) # When t = view_transform(f, to, up) # Then assert t == translation(0, 0, -8)
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 test__color_at__with_mutually_reflective_surfaces(): # Given w = World() w.light = PointLight(Point(0, 0, 0), Color(1, 1, 1)) lower = Plane() lower.material.reflective = 1 lower.transform = translation(0, -1, 0) w.objects.append(lower) upper = Plane() upper.material.reflective = 1 upper.transform = translation(0, 1, 0) w.objects.append(upper) r = Ray(Point(0, 0, 0), Vector(0, 1, 0)) # Then try: w.color_at(r) except RecursionError: pytest.fail("Unexpected RecursionError ...")
def test_intersecting_a_translated_sphere_with_a_ray(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) s = Sphere() # When s.transform = translation(5, 0, 0) xs = s.intersect(r) # Then assert len(xs) == 0
def test_chained_transformations_must_be_applied_in_reverse_order(): # Given p = Point(1, 0, 1) A = rotation_x(pi / 2) B = scaling(5, 5, 5) C = translation(10, 5, 7) # When T = C * B * A # Then assert T * p == Point(15, 0, 7)
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_a_pattern_with_both_an_object_and_a_pattern_transformation(): # Given shape = Sphere() shape.transform = scaling(2, 2, 2) pattern = MockPattern() pattern.transform = translation(0.5, 1, 1.5) # When c = pattern.pattern_at_shape(shape, Point(2.5, 3, 3.5)) # Then assert c == Color(0.75, 0.5, 0.25)
def test_intersecting_a_ray_with_a_nonempty_group(): # Given g = Group() s1 = Sphere() s2 = Sphere() s2.transform = translation(0, 0, -3) s3 = Sphere() s3.transform = translation(5, 0, 0) g.add_child(s1) g.add_child(s2) g.add_child(s3) # When r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) xs = g.local_intersect(r) # Then assert len(xs) == 4 assert xs[0].object == s2 assert xs[1].object == s2 assert xs[2].object == s1 assert xs[3].object == s1
def test_the_hit_should_offset_the_point(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = Sphere() shape.transform = translation(0, 0, 1) i = Intersection(5, shape) # When comps = i.prepare_computations(r) # Then assert comps.over_point.z < -EPSILON / 2 assert comps.point.z > comps.over_point.z
def test_the_under_point_is_offset_below_the_surface(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = glass_sphere() shape.transform = translation(0, 0, 1) i = Intersection(5, shape) xs = Intersections(i) # When comps = i.prepare_computations(r, xs) # Then assert comps.under_point.z > EPSILON / 2 assert comps.point.z < comps.under_point.z
def test_intersecting_a_transformed_group(): # Given g = Group() g.transform = scaling(2, 2, 2) s = Sphere() s.transform = translation(5, 0, 0) g.add_child(s) # When r = Ray(Point(10, 0, -10), Vector(0, 0, 1)) xs = g.intersect(r) # Then assert len(xs) == 2
def test_finding_the_normal_on_a_child_object(): # 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_at(Point(1.7321, 1.1547, -5.5774)) # Then assert n == Vector(0.2857, 0.42854, -0.85716)
def test_converting_a_point_from_world_to_object_space(): # Given g1 = Group() g1.transform = rotation_y(pi / 2) g2 = Group() g2.transform = scaling(2, 2, 2) g1.add_child(g2) s = Sphere() s.transform = translation(5, 0, 0) g2.add_child(s) # When p = s.world_to_object(Point(-2, 0, -10)) # Then assert p == Point(0, 0, -1)
def test__shade_hit__with_a_reflective_material(): # Given w = default_world() shape = Plane() shape.material.reflective = 0.5 shape.transform = translation(0, -1, 0) w.objects.append(shape) r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2) / 2, sqrt(2) / 2)) i = Intersection(sqrt(2), shape) # When comps = i.prepare_computations(r) color = w.shade_hit(comps) # Then assert color == Color(0.87675, 0.92434, 0.82918)
def test__shade_hit__with_a_transparent_material(): # Given w = default_world() floor = Plane() floor.transform = translation(0, -1, 0) floor.material.transparency = 0.5 floor.material.refractive_index = 1.5 w.objects.append(floor) ball = Sphere() ball.material.color = Color(1, 0, 0) ball.material.ambient = 0.5 ball.transform = translation(0, -3.5, -0.5) w.objects.append(ball) r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2) / 2, sqrt(2) / 2)) xs = Intersections(Intersection(sqrt(2), floor)) # When comps = xs[0].prepare_computations(r, xs) color = w.shade_hit(comps, 5) # Then assert color == Color(0.93642, 0.68642, 0.68642)
def test_the_reflected_color_at_the_maximum_recursive_depth(): # Given w = default_world() shape = Plane() shape.material.reflective = 0.5 shape.transform = translation(0, -1, 0) w.objects.append(shape) r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2) / 2, sqrt(2) / 2)) i = Intersection(sqrt(2), shape) # When comps = i.prepare_computations(r) color = w.reflected_color(comps, 0) # Then assert color == Color(0, 0, 0)
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__shade_hit__is_given_an_intersection_in_shadow(): # Given w = World() w.light = PointLight(Point(0, 0, -10), Color(1, 1, 1)) s1 = Sphere() w.objects.append(s1) s2 = Sphere() s2.transform = translation(0, 0, 10) w.objects.append(s2) r = Ray(Point(0, 0, 5), Vector(0, 0, 1)) i = Intersection(4, s2) # When comps = i.prepare_computations(r) c = w.shade_hit(comps) # Then assert c == Color(0.1, 0.1, 0.1)
def test_individual_transformations_are_applied_in_sequence(): # Given p = Point(1, 0, 1) A = rotation_x(pi / 2) B = scaling(5, 5, 5) C = translation(10, 5, 7) # apply rotation first # When p2 = A * p # Then assert p2 == Point(1, -1, 0) # then apply scaling # When p3 = B * p2 # Then assert p3 == Point(5, -5, 0) # then apply translation # When p4 = C * p3 # Then assert p4 == Point(15, 0, 7)
def test_translation_does_not_affect_vectors(): # Given transform = translation(5, -3, 2) v = Vector(-3, 4, 5) # Then assert transform * v == v
if __name__ == "__main__": # pattern = StripePattern(Color(1, 0, 0), Color(1, 1, 1)) # pattern = CheckersPattern(Color(1, 0, 0), Color(1, 1, 1)) pattern = GradientPattern(Color(1, 0, 0), Color(1, 1, 1)) # pattern = RingPattern(Color(1, 0, 0), Color(1, 1, 1)) pattern.transform = scaling(0.2, 0.2, 0.2) floor = Plane() floor.material = Material() floor.material.color = Color(1, 0.9, 0.9) # floor.material.pattern = pattern backdrop = Plane() backdrop.transform = \ rotation_y(pi / 3) * translation(0, 0, 5) * rotation_x(pi / 2) backdrop2 = Plane() backdrop2.transform = \ rotation_y(-pi / 3) * translation(0, 0, 5) * rotation_x(pi / 2) middle = Sphere() # middle = glass_sphere() middle.transform = translation(-0.5, 1, 0.5) # middle.reflective = 1.0 middle.material = Material() middle.material.color = Color(0.1, 1, 0.5) # middle.material.pattern = pattern middle.material.diffuse = 0.7 middle.material.specular = 0.3 # middle.material.reflective = 1.0
def test_multiplying_by_a_translation_matrix(): # Given transform = translation(5, -3, 2) p = Point(-3, 4, 5) # Then assert transform * p == Point(2, 1, 7)