#!/usr/bin/env python #-*- coding:utf-8 -*- import Image, math from normImage import NormImage class Escher: """ エッシャーの騙し絵のような画像を生成します。 """ def __init__(self, img, R1, R2, size=(640, 480)): """ Escherクラスのコンストラクタです。 引数: img: 変形する対象のオブジェクト。Imageクラスを指定。 R1: 変形範囲として用いられる円環の内径(px) R2: 変形範囲として用いられる円環の外径(px) size: レンダリングする画像のサイズをタプルで指定。指定されない場合VGA(640x480)が用いられる """ # グローバル変数の初期化 self.img = NormImage( img ) #TODO:R1,R2に関しては別途アクセッサメソッドを用意して管理。 self.R1 = R1 self.R2 = R2 self.width = size[0] self.height = size[1] # 各種プライベート変数の初期化 self._alpha = math.atan( math.log(float(R2)/float(R1)) / (2.0*math.pi) ) self._cosAlpha = math.cos( self._alpha ) self._sinAlpha = math.sin( self._alpha ) # MEMO:計算的には意味がないけど、一応区別するため。 self._f = self._cosAlpha self._logR2R1 = math.log( float(R2)/float(R1) ) def makeImage(self, center=(0,0), scale=1.0, rotale=0.0): """ 新しく画像を生成します。 """ im = Image.new( "RGB", (self.width, self.height) ) # 各種ベクトル、ポイントの定義 (cosTheta, sinTheta) = math.cos(rotale), math.sin(rotale) vecX = (cosTheta*scale, sinTheta*scale) vecY = (sinTheta*scale, -cosTheta*scale) (width_half, height_half) = self.width/2.0, self.height/2.0 startX = center[0] - (width_half*cosTheta + height_half*sinTheta)*scale startY = center[1] + (-width_half*sinTheta + height_half*cosTheta)*scale im.putdata([self.escherPlot( startX + vecX[0]*i + vecY[0]*j, startY + vecX[1]*i + vecY[1]*j) for j in range(self.height) for i in range(self.width)]) return im def escherPlot(self, x, y): # 各種座標変換 try: logR = math.log( math.hypot(x,y) ) except: return (256,256,256) theta = math.atan2(y,x) + math.pi s = ( logR*self._cosAlpha + theta*self._sinAlpha )/self._f t = ( theta*self._cosAlpha - logR*self._sinAlpha )/self._f # s,tを読める範囲に修正 ms = s % self._logR2R1 mt = t % (math.pi*2.0) # 画像での座標に変換 p = self.R1 * math.exp(ms) * math.cos(mt) q = self.R1 * math.exp(ms) * math.sin(mt) return self.img.getpixel(p,q)