' '

Difficulty

2

Prerequisites

patterns/tiling

Reading material

design/transformations

In this extension, we define the usual transformations on patterns:

Translation

Scaling

Rotation

1. 2D Implementation

Let’s start by creating the files that will host the new code.

Create files patterns/pattern-transformations.cpp and patterns/pattern-transformations.h. Prepare a namespace declarations named patterns.

Include patterns/pattern-transformation.h in patterns/patterns.h.

1.1. transform

We’ll start with a very general transform function. The more specific transformations (translation, scaling, rotation) will be able to rely on this function, making their implementation very short and simple.

Implement a function with signature

Pattern2D patterns::transform(const Transformation2D& transformation,
                              Pattern2D pattern)
Tip
  • To simplify the implementation, rely on make_pattern.

  • You will need to call pattern→at(p) with some position p. Think carefully about what this p should be.

1.2. Transformations

Once you defined the transform function, it’s trivial to define the others.

Define

Pattern2D translate(math::Vector2D displacement, Pattern2D pattern)
Pattern2D rotate(math::Angle angle, Pattern2D pattern)
Pattern2D scale(double sx, double sy, Pattern2D pattern)

Rely on the transformations defined in math/transformation2d.h.

2. 3D Implementation

The 3D variants of the transformations are nearly identical.

Define the following functions:

Pattern3D transform(const Transformation3D& transformation, Pattern3D pattern)

Pattern3D translate(Vector3D displacement, Pattern3D pattern)

Pattern3D rotate_x(Angle angle, Pattern3D pattern)
Pattern3D rotate_y(Angle angle, Pattern3D pattern)
Pattern3D rotate_z(Angle angle, Pattern3D pattern)

Pattern3D scale(double sx, double sy, double sz, Pattern3D pattern)

3. Finishing Touches

Expose all functions (except the transform overloads) to the scripting world.

4. Evaluation

Render the following script:

global white = Materials.uniform([ "ambient": Colors.white() ])
global black = Materials.uniform([ "ambient": Colors.black() ])

def material(pattern)
{
  Materials.from_pattern(pattern, white, black)
}

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


  var t = Animations.animate(0, 1, seconds(5))[now]
  var pattern = Patterns.lines(0.04, 0.08)

  var root = union( [
  	decorate(
          material(Patterns.rotate(degrees(360 * t), pattern)),
          sphere()),
  	decorate(
          material(Patterns.translate(vec(t, t), pattern)),
          translate(vec(-3, 0, 0), sphere())),
  	decorate(
          material(Patterns.scale(5*t+1, 5*t+1, pattern)),
          translate(vec(3, 0, 0), sphere()))
  ] )

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

  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(5)),
          [ Pipeline.animation(30),
            Pipeline.renderer(renderer),
            Pipeline.studio() ] )
  • Show that transform is implemented using a lambda pattern.

  • Write tests for each of the transformations.

  • Make use of a simple Pattern2D/Pattern3D that makes checking your transformations easy.

  • Make sure that translation and scaling work in the correct direction, i.e., that scale(2, 2, p) enlarges the pattern and not shrink it.