def testTransformation(self): sphere = Sphere(transformation=translation(Vec(10.0, 0.0, 0.0))) ray1 = Ray(origin=Point(10, 0, 2), dir=-VEC_Z) intersection1 = sphere.ray_intersection(ray1) assert intersection1 assert HitRecord( world_point=Point(10.0, 0.0, 1.0), normal=Normal(0.0, 0.0, 1.0), surface_point=Vec2d(0.0, 0.0), t=1.0, ray=ray1, material=sphere.material, ).is_close(intersection1) ray2 = Ray(origin=Point(13, 0, 0), dir=-VEC_X) intersection2 = sphere.ray_intersection(ray2) assert intersection2 assert HitRecord( world_point=Point(11.0, 0.0, 0.0), normal=Normal(1.0, 0.0, 0.0), surface_point=Vec2d(0.0, 0.5), t=2.0, ray=ray2, material=sphere.material, ).is_close(intersection2) # Check if the sphere failed to move by trying to hit the untransformed shape assert not sphere.ray_intersection( Ray(origin=Point(0, 0, 2), dir=-VEC_Z)) # Check if the *inverse* transformation was wrongly applied assert not sphere.ray_intersection( Ray(origin=Point(-10, 0, 0), dir=-VEC_Z))
def testCheckeredPigment(self): color1 = Color(1.0, 2.0, 3.0) color2 = Color(10.0, 20.0, 30.0) pigment = CheckeredPigment(color1=color1, color2=color2, num_of_steps=2) # With num_of_steps == 2, the pattern should be the following: # # (0.5, 0) # (0, 0) +------+------+ (1, 0) # | | | # | col1 | col2 | # | | | # (0, 0.5) +------+------+ (1, 0.5) # | | | # | col2 | col1 | # | | | # (0, 1) +------+------+ (1, 1) # (0.5, 1) assert pigment.get_color(Vec2d(0.25, 0.25)).is_close(color1) assert pigment.get_color(Vec2d(0.75, 0.25)).is_close(color2) assert pigment.get_color(Vec2d(0.25, 0.75)).is_close(color2) assert pigment.get_color(Vec2d(0.75, 0.75)).is_close(color1)
def testHit(self): sphere = Sphere() ray1 = Ray(origin=Point(0, 0, 2), dir=-VEC_Z) intersection1 = sphere.ray_intersection(ray1) assert intersection1 assert HitRecord( world_point=Point(0.0, 0.0, 1.0), normal=Normal(0.0, 0.0, 1.0), surface_point=Vec2d(0.0, 0.0), t=1.0, ray=ray1, material=sphere.material, ).is_close(intersection1) ray2 = Ray(origin=Point(3, 0, 0), dir=-VEC_X) intersection2 = sphere.ray_intersection(ray2) assert intersection2 assert HitRecord( world_point=Point(1.0, 0.0, 0.0), normal=Normal(1.0, 0.0, 0.0), surface_point=Vec2d(0.0, 0.5), t=2.0, ray=ray2, material=sphere.material, ).is_close(intersection2) assert not sphere.ray_intersection( Ray(origin=Point(0, 10, 2), dir=-VEC_Z))
def testUniformPigment(self): color = Color(1.0, 2.0, 3.0) pigment = UniformPigment(color=color) assert pigment.get_color(Vec2d(0.0, 0.0)).is_close(color) assert pigment.get_color(Vec2d(1.0, 0.0)).is_close(color) assert pigment.get_color(Vec2d(0.0, 1.0)).is_close(color) assert pigment.get_color(Vec2d(1.0, 1.0)).is_close(color)
def testUVCoordinates(self): plane = Plane() ray1 = Ray(origin=Point(0, 0, 1), dir=-VEC_Z) intersection1 = plane.ray_intersection(ray1) assert intersection1.surface_point.is_close(Vec2d(0.0, 0.0)) ray2 = Ray(origin=Point(0.25, 0.75, 1), dir=-VEC_Z) intersection2 = plane.ray_intersection(ray2) assert intersection2.surface_point.is_close(Vec2d(0.25, 0.75)) ray3 = Ray(origin=Point(4.25, 7.75, 1), dir=-VEC_Z) intersection3 = plane.ray_intersection(ray3) assert intersection3.surface_point.is_close(Vec2d(0.25, 0.75))
def testTransformation(self): plane = Plane(transformation=rotation_y(angle_deg=90.0)) ray1 = Ray(origin=Point(1, 0, 0), dir=-VEC_X) intersection1 = plane.ray_intersection(ray1) assert intersection1 assert HitRecord( world_point=Point(0.0, 0.0, 0.0), normal=Normal(1.0, 0.0, 0.0), surface_point=Vec2d(0.0, 0.0), t=1.0, ray=ray1, material=plane.material, ).is_close(intersection1) ray2 = Ray(origin=Point(0, 0, 1), dir=VEC_Z) intersection2 = plane.ray_intersection(ray2) assert not intersection2 ray3 = Ray(origin=Point(0, 0, 1), dir=VEC_X) intersection3 = plane.ray_intersection(ray3) assert not intersection3 ray4 = Ray(origin=Point(0, 0, 1), dir=VEC_Y) intersection4 = plane.ray_intersection(ray4) assert not intersection4
def testImagePigment(self): image = HdrImage(width=2, height=2) image.set_pixel(0, 0, Color(1.0, 2.0, 3.0)) image.set_pixel(1, 0, Color(2.0, 3.0, 1.0)) image.set_pixel(0, 1, Color(2.0, 1.0, 3.0)) image.set_pixel(1, 1, Color(3.0, 2.0, 1.0)) pigment = ImagePigment(image) assert pigment.get_color(Vec2d(0.0, 0.0)).is_close(Color(1.0, 2.0, 3.0)) assert pigment.get_color(Vec2d(1.0, 0.0)).is_close(Color(2.0, 3.0, 1.0)) assert pigment.get_color(Vec2d(0.0, 1.0)).is_close(Color(2.0, 1.0, 3.0)) assert pigment.get_color(Vec2d(1.0, 1.0)).is_close(Color(3.0, 2.0, 1.0))
def _sphere_point_to_uv(point: Point) -> Vec2d: """Convert a 3D point on the surface of the unit sphere into a (u, v) 2D point""" u = atan2(point.y, point.x) / (2.0 * pi) return Vec2d( u=u if u >= 0.0 else u + 1.0, v=acos(point.z) / pi, )
def ray_intersection(self, ray: Ray) -> Union[HitRecord, None]: """Checks if a ray intersects the plane Return a `HitRecord`, or `None` if no intersection was found. """ inv_ray = ray.transform(self.transformation.inverse()) if abs(inv_ray.dir.z) < 1e-5: return None t = -inv_ray.origin.z / inv_ray.dir.z if (t <= inv_ray.tmin) or (t >= inv_ray.tmax): return None hit_point = inv_ray.at(t) return HitRecord( world_point=self.transformation * hit_point, normal=self.transformation * Normal(0.0, 0.0, 1.0 if inv_ray.dir.z < 0.0 else -1.0), surface_point=Vec2d(hit_point.x - floor(hit_point.x), hit_point.y - floor(hit_point.y)), t=t, ray=ray, material=self.material, )
def testInnerHit(self): sphere = Sphere() ray = Ray(origin=Point(0, 0, 0), dir=VEC_X) intersection = sphere.ray_intersection(ray) assert intersection assert HitRecord( world_point=Point(1.0, 0.0, 0.0), normal=Normal(-1.0, 0.0, 0.0), surface_point=Vec2d(0.0, 0.5), t=1.0, ray=ray, ).is_close(intersection)
def testUVCoordinates(self): sphere = Sphere() # The first four rays hit the unit sphere at the # points P1, P2, P3, and P4. # # ^ y # | P2 # , - ~ * ~ - , # , ' | ' , # , | , # , | , # , | , P1 # -----*-------------+-------------*---------> x # P3 , | , # , | , # , | , # , | , ' # ' - , _ * _ , ' # | P4 # # P5 and P6 are aligned along the x axis and are displaced # along z (ray5 in the positive direction, ray6 in the negative # direction). ray1 = Ray(origin=Point(2.0, 0.0, 0.0), dir=-VEC_X) assert sphere.ray_intersection(ray1).surface_point.is_close( Vec2d(0.0, 0.5)) ray2 = Ray(origin=Point(0.0, 2.0, 0.0), dir=-VEC_Y) assert sphere.ray_intersection(ray2).surface_point.is_close( Vec2d(0.25, 0.5)) ray3 = Ray(origin=Point(-2.0, 0.0, 0.0), dir=VEC_X) assert sphere.ray_intersection(ray3).surface_point.is_close( Vec2d(0.5, 0.5)) ray4 = Ray(origin=Point(0.0, -2.0, 0.0), dir=VEC_Y) assert sphere.ray_intersection(ray4).surface_point.is_close( Vec2d(0.75, 0.5)) ray5 = Ray(origin=Point(2.0, 0.0, 0.5), dir=-VEC_X) assert sphere.ray_intersection(ray5).surface_point.is_close( Vec2d(0.0, 1 / 3)) ray6 = Ray(origin=Point(2.0, 0.0, -0.5), dir=-VEC_X) assert sphere.ray_intersection(ray6).surface_point.is_close( Vec2d(0.0, 2 / 3))