## 2019年5月20日月曜日

### Python - Patterns - Perturbed patterns - Jittering

The Ray Tracer Challenge: A Test-Driven Guide to Your First 3D Renderer (Jamis Buck(著)、Pragmatic Bookshelf)、Chapter 10(Patterns)のPut It Together(138)、Perturbed patternsを取り組んでみる。

コード

Python 3

patterns.py

```import math
from matrices import IDENTITY_MATRIX
from tuples import is_equal, Point, Color
import random

class Pattern:
def __init__(self, transform=IDENTITY_MATRIX):
self.transform = transform

def at(self, point):
return Color(point.x, point.y, point.z)

def at_shape(self, shape, world_point):
obj_point = shape.transform.inverse() * world_point
point = self.transform.inverse() * obj_point
return self.at(point)

class Solid(Pattern):
def __init__(self, color):
super().__init__()
self.color = color

def at(self, point):
return self.color

class Stripe(Pattern):
def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX):
super().__init__(transform)
self.a = pattern_a
self.b = pattern_b

def at(self, point):
point = self.transform.inverse() * point
if math.floor(point.x) % 2 == 0:
return self.a.at(point)
return self.b.at(point)

def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX):
super().__init__(transform)
self.a = pattern_a
self.b = pattern_b

def at(self, point):
point = self.transform.inverse() * point
distance = self.b.at(point) - self.a.at(point)
fraction = point.x - math.floor(point.x)
return self.a.at(point) + distance * fraction

class Ring(Pattern):
def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX):
super().__init__(transform)
self.a = pattern_a
self.b = pattern_b

def at(self, point):
point = self.transform.inverse() * point
if is_equal(math.floor(math.sqrt(point.x ** 2 + point.z ** 2)) % 2, 0):
return self.a.at(point)
return self.b.at(point)

class Checkers(Pattern):
def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX):
super().__init__(transform)
self.a = pattern_a
self.b = pattern_b

def at(self, point):
point = self.transform.inverse() * point
if is_equal(sum([math.floor(p)
for p in [point.x, point.y, point.z]]) % 2,
0):
return self.a.at(point)
return self.b.at(point)

def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX):
super().__init__(transform)
self.a = pattern_a
self.b = pattern_b

def at(self, point):
color_distance = self.b - self.a
distance = math.sqrt(point.x ** 2 + point.z ** 2)
fraction = distance - math.floor(distance)
return self.a + color_distance * fraction

class Blend(Pattern):
def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX):
super().__init__(transform)
self.a = pattern_a
self.b = pattern_b

def at(self, point):
point = self.transform.inverse() * point
color_a = self.a.at(point)
color_b = self.b.at(point)
return (color_a + color_b) / 2

class Perturb(Pattern):
def __init__(self, pattern, transform=IDENTITY_MATRIX,
range_of_values=None):
super().__init__(transform)
self.pattern = pattern
if range_of_values is None:
self.range_of_values = Point(*[random.random() for _ in range(3)])
else:
self.range_of_values = range_of_values

def at(self, point):
point += self.range_of_values * 0.2 * random.random()
point = self.transform.inverse() * point
return self.pattern.at(point)
```

sample4.py

```#!/usr/bin/env python3
import math
import time
from tuples import Point, Vector, Color
from planes import Plane
from materials import Material
from patterns import Solid, Stripe, Blend, Perturb
from camera import Camera
from lights import Light
from world import World
from transformations import view_transform, rotation_y
print('ファイル名, rendering time(秒)')

width = 250
height = 125
stripe1 = Stripe(Solid(Color(1, 0, 0)), Solid(Color(1, 1, 1)),
transform=rotation_y(math.pi / 4))
stripe2 = Stripe(Solid(Color(1, 0, 0)), Solid(Color(1, 1, 1)),
transform=rotation_y(-math.pi / 4))
blend = Blend(stripe1, stripe2)
perturb = Perturb(blend, range_of_values=Point(1, 1, 1))
patterns = [blend, perturb]
colors = [Color(0, 1, 0), Color(1, 0, 0)]
material = Material(specular=0)
plane = Plane(material=material)
camera = Camera(width, height, math.pi / 2,
transform=view_transform(Point(0, 1.5, -5), Point(0, 1, 0),
Vector(0, 1, 0)))
world = World([plane], Light(Point(-10, 10, -10), Color(1, 1, 1)))

for i, (pattern, color) in enumerate(zip(patterns, colors), 6):
material.pattern = pattern
blend.a.a.color = color
blend.b.a.color = color
start = time.time()
canvas = camera.render(world)
s = time.time() - start

with open(f'sample{i}.ppm', 'w') as f:
canvas.to_ppm(f)
print(f'sample{i}.ppm,{s}')
```

```C:\Users\...>py materials_test.py
........
----------------------------------------------------------------------
Ran 8 tests in 0.003s

OK

C:\Users\...>py patterns_test.py
................
----------------------------------------------------------------------
Ran 16 tests in 0.007s

OK

C:\Users\...>py sample4.py
ファイル名, rendering time(秒)
sample6.ppm,132.31091618537903
sample7.ppm,143.2693841457367

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