' '

Difficulty

3

Prerequisites

ray-tracers/v1

patterns/basics

patterns/lambda

As mentioned before, a pattern can be seen as a black and white image. But not just any black and white image, but rather an infinitely large one. For any position \((x, y)\) or \((x, y, z)\), the pattern must decide whether that spot is black or white.

In order to fill up this infinite space, we generally rely on repetition: one simple patterns gets copied an infinite number of times. In this extension, you will have to create the "for loop" of patterns: the tiling operation takes a "child pattern", a width and a height and will then copy this ad infinitum.

example

We start off we with simple pattern:

single

Tiling will lead to this:

tiling

1. Implementation

Create files patterns/pattern-tiling.cpp and patterns/pattern-tiling.h.

Also add the header file to patterns/patterns.h.

1.1. Tiling in 2D

Given a point \((x, y)\), you need to "remap" it to \([0,0] \times [w, h]\). This remapped point can then be fed to the child pattern.

remapping

Implement tiling as a function

Pattern2D tiling(Pattern2D pattern, double width, double height)

Rely on make_pattern from the lambda pattern extension.

Important

The remapping must be done by an operation known to you that does this in a single step, i.e., don’t use loops.

Important

You might have some trouble to get the area around \((0,0)\) right. Say you are copying this pattern:

checker
Correct Wrong
right
wrong

1.2. tiling_x, tiling_y

tiling takes a rectangular pattern and copies it both horizontally and vertically. What if we already have an infinitely wide pattern and only need to copy it vertically, or vice versa?

Implement horizontal and vertical tiling as two functions

Pattern2D tiling_x(Pattern2D pattern, double width)
Pattern2D tiling_y(Pattern2D pattern, double height)

Rely on make_pattern from the lambda pattern extension.

1.3. Tiling in 3D

The same trick can be applied in 3D. We get a "pattern box" which is copied along all three axes.

Implement 3D tiling as

Pattern3D tiling(Pattern3D pattern,
                 double x_size,
                 double y_size,
                 double z_size)

Rely on make_pattern from the lambda pattern extension.

1.4. Finishing Touches

Add bindings to expose this new functionality to the scripting language.

2. Evaluation

Use tiling_y to create a pattern lines(thickness, spacing) for horizontal lines together with xsplit or ysplit from the lambda pattern extension.

  • thickness represents the thickness of each line.

  • spacing represents the space between lines.

lines
hlines
def m(color)
{
  Materials.uniform( [ "ambient": color ] )
}

def scene_at(now)
{
  var camera = Cameras.perspective( [ "eye": pos(0,0,7),
                                      "look_at": pos(0,0,0) ] )

  var mat1 = Materials.from_pattern(Patterns.lines(0.1, 0.1),
                                    m(Colors.white()), m(Colors.black()))
  var mat2 = Materials.from_pattern(Patterns.lines(0.1, 0.2),
                                    m(Colors.red()), m(Colors.yellow()))
  var mat3 = Materials.from_pattern(Patterns.lines(0.2, 0.1),
                                    m(Colors.magenta()), m(Colors.green()))
  var mat4 = Materials.from_pattern(Patterns.lines(0.2, 0.2),
                                    m(Colors.blue()), m(Colors.red()))
  var mat5 = Materials.from_pattern(Patterns.lines(1, 1),
                                    m(Colors.white() * 0.5), m(Colors.white() * 0.25))

  var root = union([
      decorate(mat1, translate(vec(-1.5,1.5,0), sphere())),
      decorate(mat2, translate(vec(1.5,1.5,0), sphere())),
      decorate(mat3, translate(vec(-1.5,-1.5,0), sphere())),
      decorate(mat4, translate(vec(1.5,-1.5,0), sphere())),
      decorate(mat5, translate(vec(0,0,-5), xy_plane()))
  ])

  var lights = [ Lights.omnidirectional( pos(5,5,5), Colors.white() ) ]

  var scene = create_scene(camera, root, lights)
}

var raytracer   = Raytracers.v1()
var renderer    = Renderers.standard( [ "width": 500,
                                        "height": 500,
                                        "sampler": Samplers.single(),
                                        "ray_tracer": raytracer ] )

pipeline( scene_animation(scene_at, seconds(1)),
          [ Pipeline.animation(1),
            Pipeline.renderer(renderer),
            Pipeline.wif(),
            Pipeline.base64(),
            Pipeline.stdout() ] )

Show that your code for tiling contains no loops, as this would be a very inefficient solution.