def test_reflective_transparent_material(self): """Test shade_hit with a reflective, transparent material""" world = copy.deepcopy(self.default_scene) floor = shapes.Plane(material=materials.Material( reflective=0.5, transparency=0.5, refractive_index=1.5)) floor.set_transform(transforms.Translate(0, -1, 0)) ball = shapes.Sphere(material=materials.Material( color=colors.Color(1, 0, 0), ambient=0.5)) ball.set_transform(transforms.Translate(0, -3.5, -0.5)) r = rays.Ray(points.Point(0, 0, -3), vectors.Vector(0, -math.sqrt(2) / 2, math.sqrt(2) / 2)) xs = intersections.Intersections( intersections.Intersection(floor, math.sqrt(2))) world.add_object(floor) world.add_object(ball) comps = xs.intersections[0].precompute(r, all_intersections=xs) color, _ = world.shade_hit(comps, remaining=5) self.assertEqual(color, colors.Color(0.93391, 0.69643, 0.69243))
def test_refractive_index_intersections(self): """Test we can calculate the refractive indices between intersections""" # Set up a scene with three glass spheres. One at the origin with size # 2 then inside of that 2 that are offset along z by different amounts A = shapes.Sphere(material=materials.Material(refractive_index=1.5, transparency=1.0)) B = shapes.Sphere(material=materials.Material(refractive_index=2.0, transparency=1.0)) C = shapes.Sphere(material=materials.Material(refractive_index=2.5, transparency=1.0)) A.set_transform(transforms.Scale(2, 2, 2)) B.set_transform(transforms.Translate(0, 0, -0.25)) C.set_transform(transforms.Translate(0, 0, 0.25)) r = rays.Ray(points.Point(0, 0, -4), vectors.Vector(0, 0, 1)) xs = intersections.Intersections(intersections.Intersection(A, 2), intersections.Intersection(B, 2.75), intersections.Intersection(C, 3.25), intersections.Intersection(B, 4.75), intersections.Intersection(C, 5.25), intersections.Intersection(A, 6)) expected_results = [ { "n1": 1.0, "n2": 1.5 }, { "n1": 1.5, "n2": 2.0 }, { "n1": 2.0, "n2": 2.5 }, { "n1": 2.5, "n2": 2.5 }, { "n1": 2.5, "n2": 1.5 }, { "n1": 1.5, "n2": 1.0 }, ] for index, expected in enumerate(expected_results): comps = xs.intersections[index].precompute(r, all_intersections=xs) self.assertDictEqual(expected, {"n1": comps.n1, "n2": comps.n2})
def test_reflection__reflective(self): """Test the reflection color of a reflective material is not black""" p = shapes.Plane(material=materials.Material(reflective=0.5)) p.set_transform(transforms.Translate(0, -1, 0)) world = copy.deepcopy(self.default_scene) world.objects.append(p) r = rays.Ray(points.Point(0, 0, -3), vectors.Vector(0, -math.sqrt(2) / 2, math.sqrt(2) / 2)) i = intersections.Intersection(p, math.sqrt(2)) comps = i.precompute(r) # Test reflected color alone result, _ = world.reflected_color(comps) self.assertEqual(result, colors.Color(0.19032, 0.2379, 0.14274)) # Now test the reflected color is added to the surface color result, _ = world.shade_hit(comps) self.assertEqual(result, colors.Color(0.87677, 0.92436, 0.82918))
def test_shadows__full_scene(self): """Test that we identify a shadow in a full scene""" # First sphere is at z=10 s1 = shapes.Sphere() s1.set_transform(transforms.Translate(0, 0, 10)) # Second sphere is at the origin s2 = shapes.Sphere() s2.material = materials.Material() # Light is at z=-10 l1 = lights.Light(position=points.Point(0, 0, -10), intensity=colors.Color(1, 1, 1)) scene = scenes.Scene(objects=[s1, s2], lights=[l1]) # The ray is at z=5 (i.e. between the spheres), pointing at the further # out sphere ray = rays.Ray(points.Point(0, 0, 5), vectors.Vector(0, 0, 1)) isection = intersections.Intersection(s2, 4) comps = isection.precompute(ray) result, _ = scene.shade_hit(comps) self.assertEqual(result, colors.Color(0.1, 0.1, 0.1))
def test_translate_vector_is_noop(self): """Verify that translating a vector has no effect""" v = vectors.Vector(1, 2, 3) T = transforms.Translate(5, -3, 2) v2 = T * v self.assertEqual(v, v2)
def test_move_camera(self): """Test we can move the camera (well... move the world)""" from_point = points.Point(0, 0, 9) to_point = points.Point(0, 0, 0) up = vectors.Vector(0, 1, 0) result = transforms.ViewTransform(from_point, to_point, up) self.assertEqual(result, transforms.Translate(0, 0, -9))
def test_chained_transforms(self): """Test we can chain together transforms with the apply function""" point = points.Point(1, 0, 1) p2 = point.apply(transforms.RotateX(math.pi/2)) \ .apply(transforms.Scale(5, 5, 5)) \ .apply(transforms.Translate(10, 5, 7)) self.assertEqual(p2, points.Point(15, 0, 7))
def test_pattern_object_transformation(self): """Test that pattern is affected by pattern and object transforms""" shape = shapes.Sphere() shape.set_transform(transforms.Scale(2, 2, 2)) p = patterns.StripePattern(WHITE, BLACK) p.set_transform(transforms.Translate(0.5, 0, 0)) self.assertEqual(p.pattern_at_shape(shape, points.Point(2.5, 0, 0)), WHITE)
def test_reflection__infinite_recursion(self): """Test that we don't break if there is infinite recursion""" # Two parallel planes s1 = shapes.Plane(material=materials.Material(reflective=1)) s1.set_transform(transforms.Translate(0, -1, 0)) # Second sphere is at the origin s2 = shapes.Plane(material=materials.Material(reflective=1)) s2.set_transform(transforms.Translate(0, 1, 0)) # Light is at z=-10 l1 = lights.Light(position=points.Point(0, 0, 0), intensity=colors.Color(1, 1, 1)) scene = scenes.Scene(objects=[s1, s2], lights=[l1]) r = rays.Ray(points.Point(0, 0, 0), vectors.Vector(0, 1, 0)) # If this is working the following will NOT cause a stack trace scene.color_at(r)
def test_intersections_with_transformed_ray__translation(self): """Test we get the correct intersections after adding a translation to a shape """ s = shapes.Sphere() s.set_transform(transforms.Translate(5, 0, 0)) r = rays.Ray(points.Point(0, 0, -5), vectors.Vector(0, 0, 1)) result = s.intersect(r) self.assertTrue(len(result.intersections) == 0)
def test_translate_point(self): """Test that if we multiply a translate matrix by a point it moves it""" p = points.Point(-3, 4, 5) T = transforms.Translate(5, -3, 2) p2 = T * p self.assertEqual(p2, points.Point(2, 1, 7)) # Translating by the inverse of the translate matrix moves the # opposite direction p3 = T.inverse() * p self.assertEqual(p3, points.Point(-8, 7, 3))
def test_ray_transforms(self): """Test that we can transform a ray""" origin = points.Point(1, 2, 3) direction = vectors.Vector(0, 1, 0) r = rays.Ray(origin, direction) r2 = r.transform(transforms.Translate(3, 4, 5)) self.assertEqual(r2.origin, points.Point(4, 6, 8)) self.assertEqual(r2.direction, vectors.Vector(0, 1, 0)) r3 = r.transform(transforms.Scale(2, 3, 4)) self.assertEqual(r3.origin, points.Point(2, 6, 12)) self.assertEqual(r3.direction, vectors.Vector(0, 3, 0))
def test_normal_at__transformed(self): """Test we can calculate normal vectors on a transformed sphere""" s = shapes.Sphere() s.set_transform(transforms.Translate(0, 1, 0)) n = s.normal_at(points.Point(0, 1.70711, -0.70711)) self.assertEqual(n, vectors.Vector(0, 0.70711, -0.70711)) s.set_transform( transforms.Scale(1, 0.5, 1) * transforms.RotateZ(math.pi / 5)) n = s.normal_at(points.Point(0, math.sqrt(2) / 2, -math.sqrt(2) / 2)) self.assertEqual(n, vectors.Vector(0, 0.97014, -0.24254))
def test_precompute__over_vector(self): """Test that we calculate a vector just inside of the surface of a shape """ r = rays.Ray(points.Point(0, 0, -5), vectors.Vector(0, 0, 1)) s = shapes.Sphere() s.set_transform(transforms.Translate(0, 0, 1)) i = intersections.Intersection(s, 5) computations = i.precompute(r) self.assertTrue(computations.under_point.z > computations.point.z) self.assertTrue(computations.under_point.z > intersections.EPSILON / 2)
def test_changed_transform(self): """Test that we can change the transform on a shape""" s = shapes.Sphere() s.set_transform(transforms.Translate(6, 7, 8)) self.assertEqual(s.transform, transforms.Translate(6, 7, 8))