2019年6月15日土曜日

Python - Triangles - Intersecting a Ray with a Triangle - ray strikes

The Ray Tracer Challenge: A Test-Driven Guide to Your First 3D Renderer (Jamis Buck(著)、Pragmatic Bookshelf)、Chapter 15(Triangles)のTriangles、Test #3 to 7(Intersecting a Ray with a Triangle)、ray strikesを取り組んでみる。

コード

triangles_test.py

```#!/usr/bin/env python3
from unittest import TestCase, main
from tuples import Point, Vector
from triangles import Triangle
from rays import Ray

class TriangleTest(TestCase):
def setUp(self):
pass

def tearDown(self):
pass

def test_init(self):
point1 = Point(0, 1, 0)
point2 = Point(-1, 0, 0)
point3 = Point(1, 0, 0)
triangle = Triangle(point1, point2, point3)
self.assertEqual(triangle.point1, point1)
self.assertEqual(triangle.point2, point2)
self.assertEqual(triangle.point3, point3)
self.assertEqual(triangle.edge1, Vector(-1, -1, 0))
self.assertEqual(triangle.edge2, Vector(1, -1, 0))
self.assertEqual(triangle.normal, Vector(0, 0, -1))

def test_finding_normal_vector(self):
triangle = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0))
normal_vector1 = triangle.local_normal_at(Point(0, 0.5, 0))
normal_vector2 = triangle.local_normal_at(Point(-0.5, 0.75, 0))
normal_vector3 = triangle.local_normal_at(Point(0.5, 0.25, 0))
self.assertEqual(normal_vector1, triangle.normal)
self.assertEqual(normal_vector2, triangle.normal)
self.assertEqual(normal_vector3, triangle.normal)

def test_intersecting_ray_parallel(self):
triangle = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0))
ray = Ray(Point(0, -1, -2), Vector(0, 1, 0))
intersections = triangle.intersect(ray)
self.assertEqual(len(intersections), 0)

def test_ray_misses_p1_p3_edge(self):
triangle = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0))
ray = Ray(Point(1, 1, -2), Vector(0, 0, 1))
intersections = triangle.intersect(ray)
self.assertEqual(len(intersections), 0)

def test_ray_misses_p1_p2_edge(self):
triangle = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0))
ray = Ray(Point(-1, 1, -2), Vector(0, 0, 1))
intersections = triangle.intersect(ray)
self.assertEqual(len(intersections), 0)

def test_ray_misses_p2_p3_edge(self):
triangle = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0))
ray = Ray(Point(0, -1, -2), Vector(0, 0, 1))
intersections = triangle.intersect(ray)
self.assertEqual(len(intersections), 0)

def test_ray_strikes(self):
triangle = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0))
ray = Ray(Point(0, 0.5, -2), Vector(0, 0, 1))
intersections = triangle.intersect(ray)
self.assertEqual(len(intersections), 1)
self.assertEqual(intersections[0].t, 2)

if __name__ == '__main__':
main()
```

triangles.py

```from tuples import EPSILON
from intersections import Intersections, Intersection

class Triangle:
def __init__(self, point1, point2, point3):
self.point1 = point1
self.point2 = point2
self.point3 = point3
self.edge1 = point2 - point1
self.edge2 = point3 - point1
self.normal = self.edge2.cross(self.edge1).normalize()

def local_normal_at(self, point):
return self.normal

def intersect(self, ray):
dir_cross_edge2 = ray.direction.cross(self.edge2)
det = self.edge1.dot(dir_cross_edge2)
if abs(det) < EPSILON:
return Intersections()
f = 1.0 / det
point1_to_origin = ray.origin - self.point1
u = f * point1_to_origin.dot(dir_cross_edge2)
if u < 0 or u > 1:
return Intersections()
origin_cross_edge1 = point1_to_origin.cross(self.edge1)
v = f * ray.direction.dot(origin_cross_edge1)
if v < 0 or (u + v) > 1:
return Intersections()
t = f * self.edge2.dot(origin_cross_edge1)
return Intersections(Intersection(t, self))
```

```C:\Users\...>py triangles_test.py
.......
----------------------------------------------------------------------
Ran 7 tests in 0.001s

OK

C:\Users\...>
```