def test_findinf_n1_n2_at_various_intersections(self): a = GlassSphere() a.transform = Transformations.scaling(2, 2, 2) a.material.refractive_index = 1.5 b = GlassSphere() b.transform = Transformations.translation(0, 0, -0.25) b.material.refractive_index = 2.0 c = GlassSphere() c.transform = Transformations.translation(0, 0, 0.25) c.material.refractive_index = 2.5 r = Ray(Point(0, 0, -4), Vector(0, 0, 1)) xs = Intersection.intersections(Intersection(2, a), Intersection(2.75, b), Intersection(3.25, c), Intersection(4.75, b), Intersection(5.25, c), Intersection(6, a)) RefractiveIndices = namedtuple("RefractiveIndices", ["n1", "n2"]) refractive_indices_list = [ RefractiveIndices(1.0, 1.5), RefractiveIndices(1.5, 2.0), RefractiveIndices(2.0, 2.5), RefractiveIndices(2.5, 2.5), RefractiveIndices(2.5, 1.5), RefractiveIndices(1.5, 1.0) ] for index, refractive_index in enumerate(refractive_indices_list): comps = Computations.prepare_computations(xs[index], r, xs) print(comps.n1) print(comps.n2) self.assertEqual(comps.n1, refractive_index.n1) self.assertEqual(comps.n2, refractive_index.n2)
def test_subdivide_csg_subdivide_children(self): s1 = Sphere() s1.transform = Transformations.translation(-1.5, 0, 0) s2 = Sphere() s2.transform = Transformations.translation(1.5, 0, 0) left = Group() left.add_child(s1) left.add_child(s2) s3 = Sphere() s3.transform = Transformations.translation(0, 0, -1.5) s4 = Sphere() s4.transform = Transformations.translation(0, 0, 1.5) right = Group() right.add_child(s3) right.add_child(s4) shape = CSG("difference", left, right) shape.divide(1) self.assertIsInstance(left.members[0], Group) self.assertEqual(left.members[0].members, [s1]) self.assertIsInstance(left.members[1], Group) self.assertEqual(left.members[1].members, [s2]) self.assertIsInstance(right.members[0], Group) self.assertEqual(right.members[0].members, [s3]) self.assertIsInstance(right.members[1], Group) self.assertEqual(right.members[1].members, [s4])
def test_color_at_with_mutually_reflective_surfaces(self): w = World() w.light = PointLight(Point(0, 0, 0), Color(1, 1, 1)) lower = Plane() lower.material.reflective = 1 lower.transform = Transformations.translation(0, -1, 0) w.objects.append(lower) upper = Plane() upper.material.reflective = 1 upper.transform = Transformations.translation(0, 1, 0) w.objects.append(upper) r = Ray(Point(0, 0, 0), Vector(0, 1, 0)) self.assertIsNotNone(World.color_at(w, r))
def test_partitioning_group_children(self): s1 = Sphere() s1.transform = Transformations.translation(-2, 0, 0) s2 = Sphere() s2.transform = Transformations.translation(2, 0, 0) s3 = Sphere() g = Group() g.add_child(s1) g.add_child(s2) g.add_child(s3) (left, right) = g.partition_children() self.assertEqual(g.members, [s3]) self.assertEqual(left, [s1]) self.assertEqual(right, [s2])
def test_group_bounding_box_contains_children(self): s = Sphere() s.transform = Transformations.translation(2, 5, -3).dot( Transformations.scaling(2, 2, 2)) c = Cylinder() c.minimum = -2 c.maximum = 2 c.transform = Transformations.translation(-4, -1, 4).dot( Transformations.scaling(0.5, 1, 0.5)) shape = Group() shape.add_child(s) shape.add_child(c) box = shape.bounds_of() self.assertEqual(box.min, Point(-4.5, -3, -5)) self.assertEqual(box.max, Point(4, 7, 4.5))
def test_pattern_object_and_pattern_transformation(self): shape = Sphere() shape.transform = Transformations.scaling(2, 2, 2) pattern = Pattern.test_pattern() pattern.transform = Transformations.translation(0.5, 1, 1.5) c = pattern.pattern_at_shape(shape, Point(2.5, 3, 3.5)) self.assertEqual(c, Color(0.75, 0.5, 0.25))
def test_chained_transformations(self): p = Point(1, 0, 1) a = Transformations.rotation_x(math.pi / 2) b = Transformations.scaling(5, 5, 5) c = Transformations.translation(10, 5, 7) t = c.dot(b.dot(a)) self.assertEqual(Matrix.multiply_tuple(t, p), Point(15, 0, 7))
def test_intersect_translated_shape_with_ray(self): r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) s = Shape.test_shape() s.transform = Transformations.translation(5, 0, 0) xs = s.intersect(r) self.assertEqual(s.saved_ray.origin, Point(-5, 0, -5)) self.assertEqual(s.saved_ray.direction, Vector(0, 0, 1))
def test_transformation_move_world(self): from_point = Point(0, 0, 8) to = Point(0, 0, 0) up = Vector(0, 1, 0) t = World.view_transform(from_point, to, up) self.assertTrue( np.array_equal(t, Transformations.translation(0, 0, -8)))
def test_hit_offset_point(self): r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = Sphere() shape.transform = Transformations.translation(0, 0, 1) i = Intersection(5, shape) comps = Computations.prepare_computations(i, r) self.assertLess(comps.over_point.z, -Constants.epsilon / 2) self.assertGreater(comps.point.z, comps.over_point.z)
def test_csg_bounding_box(self): left = Sphere() right = Sphere() right.transform = Transformations.translation(2, 3, 4) shape = CSG("difference", left, right) box = shape.bounds_of() self.assertEqual(box.min, Point(-1, -1, -1)) self.assertEqual(box.max, Point(3, 4, 5))
def test_intersecting_ray_with_nonempty_group(self): g = Group() s1 = Sphere() s2 = Sphere() s2.transform = Transformations.translation(0, 0, -3) s3 = Sphere() s3.transform = Transformations.translation(5, 0, 0) g.add_child(s1) g.add_child(s2) g.add_child(s3) r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) xs = g.local_intersect(r) self.assertEqual(len(xs), 4) self.assertEqual(xs[0].object, s2) self.assertEqual(xs[1].object, s2) self.assertEqual(xs[2].object, s1) self.assertEqual(xs[3].object, s1)
def test_ray_camera_transformed(self): c = Camera(201, 101, math.pi / 2) c.transform = Transformations.rotation_y(math.pi / 4).dot( Transformations.translation(0, -2, 5)) r = Camera.ray_for_pixel(c, 100, 50) self.assertEqual(r.origin, Point(0, 2, -5)) self.assertEqual(r.direction, Vector(math.sqrt(2) / 2, 0, -math.sqrt(2) / 2))
def test_group_partitions_children(self): s1 = Sphere() s1.transform = Transformations.translation(-2, -2, 0) s2 = Sphere() s2.transform = Transformations.translation(-2, 2, 0) s3 = Sphere() s3.transform = Transformations.scaling(4, 4, 4) g = Group() g.add_child(s1) g.add_child(s2) g.add_child(s3) g.divide(1) self.assertEqual(g.members[0], s3) subgroup = g.members[1] self.assertIsInstance(subgroup, Group) self.assertEqual(len(subgroup.members), 2) self.assertEqual(subgroup.members[0].members, [s1]) self.assertEqual(subgroup.members[1].members, [s2])
def test_intersecting_transformed_group(self): g = Group() g.transform = Transformations.scaling(2, 2, 2) s = Sphere() s.transform = Transformations.translation(5, 0, 0) g.add_child(s) r = Ray(Point(10, 0, -10), Vector(0, 0, 1)) xs = g.intersect(r) self.assertEqual(len(xs), 2)
def test_under_point_offset_below_surface(self): r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = GlassSphere() shape.transform = Transformations.translation(0, 0, 1) i = Intersection(5, shape) xs = Intersection.intersections(i) comps = Computations.prepare_computations(i, r, xs) self.assertGreater(comps.under_point.z, Constants.epsilon / 2) self.assertLess(comps.point.z, comps.under_point.z)
def test_shade_hit_with_transparent_material(self): w = World.default_world() floor = Plane() floor.transform = Transformations.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 = Transformations.translation(0, -3.5, -0.5) w.objects.append(ball) r = Ray(Point(0, 0, -3), Vector(0, -math.sqrt(2) / 2, math.sqrt(2) / 2)) xs = Intersection.intersections(Intersection(math.sqrt(2), floor)) comps = Computations.prepare_computations(xs[0], r, xs) color = World.shade_hit(w, comps, 5) self.assertEqual(color, Color(0.93642, 0.68642, 0.68642))
def test_converting_normal_from_object_to_world_space(self): g1 = Group() g1.transform = Transformations.rotation_y(math.pi / 2) g2 = Group() g2.transform = Transformations.scaling(1, 2, 3) g1.add_child(g2) s = Sphere() s.transform = Transformations.translation(5, 0, 0) g2.add_child(s) n = s.normal_to_world(Vector(math.sqrt(3) /3, math.sqrt(3) / 3, math.sqrt(3) / 3)) self.assertEqual(n, Vector(0.2857, 0.4286, -0.8571))
def test_converting_point_from_world_to_object_space(self): g1 = Group() g1.transform = Transformations.rotation_y(math.pi / 2) g2 = Group() g2.transform = Transformations.scaling(2, 2, 2) g1.add_child(g2) s = Sphere() s.transform = Transformations.translation(5, 0, 0) g2.add_child(s) p = s.world_to_object(Point(-2, 0, -10)) self.assertEqual(p, Point(0, 0, -1))
def test_finding_normal_on_child_object(self): g1 = Group() g1.transform = Transformations.rotation_y(math.pi / 2) g2 = Group() g2.transform = Transformations.scaling(1, 2, 3) g1.add_child(g2) s = Sphere() s.transform = Transformations.translation(5, 0, 0) g2.add_child(s) n = s.normal_at(Point(1.7321, 1.1547, -5.5774)) self.assertEqual(n, Vector(0.2857, 0.4286, -0.8571))
def test_ray_hits_csg_object(self): s1 = Sphere() s2 = Sphere() s2.transform = Transformations.translation(0, 0, 0.5) c = CSG("union", s1, s2) r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) xs = c.local_intersect(r) self.assertEqual(len(xs), 2) self.assertEqual(xs[0].t, 4) self.assertEqual(xs[0].object, s1) self.assertEqual(xs[1].t, 6.5) self.assertEqual(xs[1].object, s2)
def test_reflected_color_at_maximum_recursive_depth(self): w = World.default_world() shape = Plane() shape.material.reflective = 0.5 shape.transform = Transformations.translation(0, -1, 0) w.objects.append(shape) r = Ray(Point(0, 0, -3), Vector(0, -math.sqrt(2) / 2, math.sqrt(2) / 2)) i = Intersection(math.sqrt(2), shape) comps = Computations.prepare_computations(i, r) color = World.reflected_color(w, comps, 0) self.assertEqual(color, Color(0, 0, 0))
def test_subdividing_group_too_few_children(self): s1 = Sphere() s1.transform = Transformations.translation(-2, 0, 0) s2 = Sphere() s2.transform = Transformations.translation(2, 1, 0) s3 = Sphere() s3.transform = Transformations.translation(2, -1, 0) subgroup = Group() subgroup.add_child(s1) subgroup.add_child(s2) subgroup.add_child(s3) s4 = Sphere() g = Group() g.add_child(subgroup) g.add_child(s4) g.divide(3) self.assertEqual(g.members[0], subgroup) self.assertEqual(g.members[1], s4) self.assertEqual(len(subgroup.members), 2) self.assertEqual(subgroup.members[0].members, [s1]) self.assertEqual(subgroup.members[1].members, [s2, s3])
def test_shade_hit_reflective_material(self): w = World.default_world() shape = Plane() shape.material.reflective = 0.5 shape.transform = Transformations.translation(0, -1, 0) w.objects.append(shape) r = Ray(Point(0, 0, -3), Vector(0, -math.sqrt(2) / 2, math.sqrt(2) / 2)) i = Intersection(math.sqrt(2), shape) comps = Computations.prepare_computations(i, r) color = World.shade_hit(w, comps) self.assertEqual(color, Color(0.87677, 0.92436, 0.82918))
def test_shade_hit_intersection_in_shadow(self): w = World() w.light = PointLight(Point(0, 0, -10), Color(1, 1, 1)) s1 = Sphere() w.objects.append(s1) s2 = Sphere() s2.transform = Transformations.translation(0, 0, 10) w.objects.append(s2) r = Ray(Point(0, 0, 5), Vector(0, 0, 1)) i = Intersection(4, s2) comps = Computations.prepare_computations(i, r) c = World.shade_hit(w, comps) self.assertEqual(c, Color(0.1, 0.1, 0.1))
def test_individual_transformations(self): p = Point(1, 0, 1) a = Transformations.rotation_x(math.pi / 2) b = Transformations.scaling(5, 5, 5) c = Transformations.translation(10, 5, 7) # rotate first p2 = Matrix.multiply_tuple(a, p) self.assertEqual(p2, Point(1, -1, 0)) # apply scaling p3 = Matrix.multiply_tuple(b, p2) self.assertEqual(p3, Point(5, -5, 0)) # apply translation p4 = Matrix.multiply_tuple(c, p3) self.assertEqual(p4, Point(15, 0, 7))
class Clock: if __name__ == '__main__': canvas_size = 300 canvas = Canvas(canvas_size, canvas_size) color = Color(1, 1, 1) radius = canvas_size * 3 / 8 scale = Transformations.scaling(radius, radius, 0) origin = Point(0, 0, 0) center_translation = Transformations.translation( canvas_size / 2, canvas_size / 2, 0) center = Matrix.multiply_tuple(center_translation, origin) hour = Point(0, 1, 0) # twelve for _ in range(12): scaled_hour = Matrix.multiply_tuple(scale, hour) positioned_hour = scaled_hour + center canvas.write_pixel(round(positioned_hour.x), round(canvas.height - positioned_hour.y), color) hour_rotation = Transformations.rotation_z(math.pi / 6) hour = Matrix.multiply_tuple(hour_rotation, hour) canvas.canvas_to_ppm()
from sphere import Sphere from transformations import Transformations from tuple import Point, Vector from world import World if __name__ == '__main__': # The floor is an extremely flattened sphere with a matte texture floor = Sphere() floor.transform = Transformations.scaling(10, 0.1, 10) floor.material = Material() floor.material.color = Color(1, 0.9, 0.9) floor.material.specular = 0 # The wall on the left has the same scale and color as the floor, but is also rotated and translated into place left_wall = Sphere() left_wall.transform = Transformations.translation(0, 0, 5) left_wall.transform = left_wall.transform.dot( Transformations.rotation_y(-math.pi / 4)) left_wall.transform = left_wall.transform.dot( Transformations.rotation_x(math.pi / 2)) left_wall.transform = left_wall.transform.dot( Transformations.scaling(10, 0.1, 10)) left_wall.material = floor.material # The wall on the right is identical to the left wall, but is rotated the opposite direction in y right_wall = Sphere() right_wall.transform = Transformations.translation(0, 0, 5) right_wall.transform = right_wall.transform.dot( Transformations.rotation_y(math.pi / 4)) right_wall.transform = right_wall.transform.dot( Transformations.rotation_x(math.pi / 2))
def test_intersect_translated_sphere(self): r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) s = Sphere() s.transform = Transformations.translation(5, 0, 0) xs = s.intersect(r) self.assertEqual(len(xs), 0)
def test_normal_translated_sphere(self): s = Sphere() s.transform = Transformations.translation(0, 1, 0) n = s.normal_at(Point(0, 1.70711, -0.70711)) self.assertEqual(n, Vector(0, 0.70711, -0.70711))