2019年6月3日月曜日

開発環境

The Ray Tracer Challenge: A Test-Driven Guide to Your First 3D Renderer (Jamis Buck(著)、Pragmatic Bookshelf)、Chapter 14(Groups)のImplementing Groupsを取り組んでみる。

コード

groups_test.py

#!/usr/bin/env python3
from unittest import TestCase, main
from shapes import Shape
from spheres import Sphere
from groups import Group
from matrices import IDENTITY_MATRIX
from rays import Ray
from tuples import Point, Vector
from transformations import translation, scaling


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

    def tearDown(self):
        pass

    def test_group(self):
        group = Group()
        self.assertEqual(group.transform, IDENTITY_MATRIX)
        self.assertEqual(len(group), 0)

    def test_adding_child_to(self):
        group = Group()
        shape = Shape()
        group.add_child(shape)
        self.assertIn(shape, group)
        self.assertEqual(shape.parent, group)

    def test_intersecting_ray_with_empty_group(self):
        group = Group()
        ray = Ray(Point(0, 0, 0), Vector(0, 0, 1))
        intersections = group.intersect(ray)
        self.assertEqual(len(intersections), 0)

    def test_intersecting_ray_nonempty_group(self):
        group = Group()
        s1 = Sphere()
        s2 = Sphere(transform=translation(0, 0, -3))
        s3 = Sphere(transform=translation(5, 0, 0))
        for s in [s1, s2, s3]:
            group.add_child(s)
        ray = Ray(Point(0, 0, -5), Vector(0, 0, 1))
        intersections = group.intersect(ray)
        self.assertEqual(len(intersections), 4)
        for intersection, shape in zip(intersections, [s2, s2, s1, s1]):
            self.assertEqual(intersection.obj, shape)

    def test_intersecting_transformed(self):
        group = Group(transform=scaling(2, 2, 2))
        sphere = Sphere(transform=translation(5, 0, 0))
        group.add_child(sphere)
        ray = Ray(Point(10, 0, -10), Vector(0, 0, 1))
        intersections = group.intersect(ray)
        self.assertEqual(len(intersections), 2)


if __name__ == '__main__':
    main()

groups.py

from shapes import Shape
from intersections import Intersections


class Group(Shape):
    def __init__(self, transform=None, material=None):
        super().__init__(transform=transform, material=material)
        self.shapes = []

    def __len__(self):
        return len(self.shapes)

    def __getitem__(self, y):
        return self.shapes[y]

    def add_child(self, shape):
        shape.parent = self
        self.shapes.append(shape)

    def intersect(self, ray):
        ray = ray.transform(self.transform.inverse())
        intersections = []
        for shape in self.shapes:
            intersections += shape.intersect(ray).xs
        return Intersections(*intersections)

shapes_test.py

#!/usr/bin/env python3
from unittest import TestCase, main
from shapes import Shape
from transformations import translation
from matrices import IDENTITY_MATRIX, Matrix
from materials import Material
from rays import Ray
from tuples import Point, Vector
from transformations import scaling


class ShapeTest(TestCase):
    def setUp(self):
        self.shape = Shape(material=Material())

    def tearDown(self):
        pass

    def test_default_transformation(self):
        self.assertEqual(self.shape.transform, IDENTITY_MATRIX)

    def test_material(self):
        self.assertEqual(self.shape.material.ambient, 0.1)
        self.assertEqual(self.shape.material, Material())

    def test_transform(self):
        self.assertEqual(Shape().transform,
                         IDENTITY_MATRIX)
        s = Shape()
        t = translation(2, 3, 4)
        s.transform = t
        self.assertEqual(s.transform, t)

    def test_parent_attribute(self):
        s = Shape()
        self.assertIsNone(s.parent)


if __name__ == '__main__':
    main()

shapes.py

from matrices import Matrix, IDENTITY_MATRIX
from materials import Material


class Shape:
    def __init__(self, transform=None, material=None, parent=None):
        if transform is None:
            self.transform = IDENTITY_MATRIX
        else:
            self.transform = transform
        if material is None:
            self.material = Material()
        else:
            self.material = material
        self.parent = None

    def __repr__(self):
        return f'{self.__class__.__name__}({self.transform},{self.material})'

    def intersect(self, ray):
        raise NotImplementedError()

    def normal_at(self, point):
        raise NotImplementedError()

入出力結果(Bash、cmd.exe(コマンドプロンプト)、Terminal、Jupyter(IPython))

C:\Users\...>py groups_test.py
.....
----------------------------------------------------------------------
Ran 5 tests in 0.005s

OK

C:\Users\...>py shapes_test.py
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

C:\Users\...>

0 コメント:

コメントを投稿