エッシャーの「爬虫類」 - 平面を敷き詰める -

 先日、色々調べ物をしているうちに、英語のWikipediaで「壁紙の分類」というのを見つけてしまいました。平面を埋め尽くせる図形は、ここに挙げた17パターンのいずれかに属するとのこと。ほう。
 これを見ていて、「ゲーデル・エッシャー・バッハ」に育てられた感のある僕は、「それではあの図形がこの中のどれに属するのか」という疑問を持ったのです。あの図形とエッシャーの「爬虫類」です。
 この幾何学模様に魅せられたのは中学校ぐらいの時でしたが、その体験が後になって大学の図書館で前述の本を手に取るきっかけになり、毎日図書館通いをした挙句大学も中退し、さらにいろいろあって今の僕があるのですから人生わかりません。とにかく当時中学生の僕は何時間もこのトカゲを見続けたにもかかわらず、その時はその構造を見抜くことができませんでした。まぁその程度の中学生だったわけですが。
 しかし今は違います。このWikipediaの記事は、この「爬虫類」も、この17種類のいずれかに属すると言いきっているのです。今回はそれを手掛かりにこのパターンの構造を探り、単位図形から全体を再現していくことにします。


二重の繰り返し構造 - 三匹のトカゲの構造 -

 大人に成長した僕がこの絵をじっと見ていると、そもそもこの絵の繰り返し構造がひとつではないことに気付きます。ひとつは色を考慮に入れた繰り返し構造。もう一つは色を無視した繰り返し構造です。
 色を考慮した繰り返し構造は、三色のトカゲを一カタマリにしてとらえた場合に見えてくるものです。
 この場合の繰り返し単位をさがしてみます。その単位とは、例の17種類のうちのどれかに当てはまっていて、なおかつそのひとつのカタマリの中に赤、黄、緑の三体分のトカゲの体の部分がすべて含まれている必要があります。
 その二つを頭に置いてブロックを探していくと・・・ありました。次の図のような菱形のエリアです。



 これは先の17種類では「p1」という名前で分類されている最もシンプルなものです。この中に各色のトカゲの部品が全て含まれていることを確認してください。

 試しにこの範囲を切り出し、繰り返し配置してみましょう。

open System
open System.IO
open System.Drawing
open System.Drawing.Imaging

let sourceImage = Image.FromFile(@".\lizard01.png")
let sourceBitmap = new Bitmap(sourceImage)

let PatternBitmap = new Bitmap(1024, 768, sourceBitmap.PixelFormat)

let g = Graphics.FromImage(PatternBitmap);
let shift = sourceImage.Width / 3
for x in [-13..15] do
  for y in [0..15] do
    g.DrawImage(sourceBitmap, x * (sourceImage.Width - shift) - shift + y * shift, y * sourceImage.Height, 
                              sourceImage.Width, sourceImage.Height);

PatternBitmap.Save(@".\colorpattern.png", ImageFormat.Png)

g.Dispose()

画像はこちらの小さいものを使用します。



 繰り返しの回数がマジックナンバーだったりしますが、まぁ現物合わせというところです。
 このプログラムで出力されるのが、以下のようなビットマップです。



 うまくいっているようですね。色を意識してみた場合の繰り返しパターンは「p1」で間違いなさそうです。

二重の繰り返し構造 - 一匹のトカゲの構造 -

 それでは、全てのトカゲが同じ色だと考えるとどうでしょうか。トカゲ単体で考えると少なくとも3種類の向きがありますから、「p1」ほど簡単でないことはわかります。
 それでも、繰り返しが30度の正三角形をベースにしていることを考えれば、別の菱形がみえてきます。この菱形の中にも、1匹分のトカゲの部品がすべて含まれていることを確認してください。


 これがどの繰り返しパターンにはまるかを考えてみると「p3」であることがわかります。よく考えると、このp3の中心の黄色の部分が上のビットマップになっていて、この全体の繰り返しが「p1」となっていることがわかります。



 というわけで、この繰り返しパターンで壁紙を作ってみましょう。今度は平行移動だけでは無理で120度単位の回転が必要になってきます。

open System.IO
open System.Drawing
open System.Drawing.Imaging

// ソース読み込み
let sourceImage = Image.FromFile(@".\lizard02.png")
let sourceBitmap = new Bitmap(sourceImage)
let w, h = sourceBitmap.Width, sourceBitmap.Height

// ソースを三つ組み合わせてp1相当の繰り返し(ただし六角形)を作成
let PatternBitmap = new Bitmap(1024, 768, sourceBitmap.PixelFormat)
let tempBitmap = new Bitmap(w * 2, h + w)

let tg = Graphics.FromImage(tempBitmap);

let drawOne x y sx sy a =
  tg.ResetTransform();
  tg.TranslateTransform(sx, sy);
  tg.RotateTransform(a);
  tg.DrawImage(sourceBitmap, x, y, w, h);

drawOne w 0 0.0f 0.0f 0.0f
drawOne 0 0 1.0f (float32 (h + 1)) -120.0f
drawOne 0 0 (float32 (2 * w)) (float32 h) 120.0f

tg.Dispose()

// 壁紙全体を作成
let g = Graphics.FromImage(PatternBitmap);

for x in [0..10] do
  for y in [0..10] do
    let zx, zy = x * w * 2 + (y % 2) * w - w, y * h - ( h / 3 * 2)
    g.DrawImage(tempBitmap, zx, zy, w * 2, h + w);

PatternBitmap.Save(@".\monopattern.png", ImageFormat.Png)

g.Dispose()

 今回もソースのビットマップは小さいものを使います。



 色々考えたのですが、面倒なので一度ソースを3枚組み合わせて六角形の図形をひとつ作り、それを繰り返し貼り込んでいます。



 途中で意味もなく1を足したりしていますが、これは元のソースがあくまで手書きのもので、しかもビットマップのサイズが実数をとれるわけもなく(繰り返し画像の縦のピクセル数が整数なら、横のサイズは実際には整数にならない)、仕方なく現物合わせしているためです。あまり気にしないでください。

 こうして完成したのが、以下の画像です。



 回転していない画像と、回転している画像で画質が異なるのはまぁ、お許しください。

まとめ

 こうして実際に爬虫類壁紙を生成していると、「これを手書きで作成するのは、アーティストというよりも職人に近い作業だなぁ」と思います。コンピュータがなければ、本当に修行のような地道な作業だったのではないでしょうか。こういうことがそもそも嫌いでなく、しかも人を驚かせるのが心底好きでないとこの作業はなかなか最後までできないでしょう。
 今回はF#のプログラムとしてはかなり小ネタですが、こういうプログラムを思い立ったらfsxファイルをサッと作って実行できるのがF#のいいところではないかと思います。

参考サイト

 どちらのサイトも、全面的に面白いです。

追記

 Windowsストアプリ選手権の素材を使って、パターンを作ってみました。これでもっていろいろ選手権参加ネタを構想中です。



 このパターンの元ネタはこれです。

(文責:片山 功士  2013/02/22)

今日: -
昨日: -
トータル: -
最終更新:2013年04月27日 18:50