『住所一覧からマックと吉野家の光の地図を作ってみた』
の続きです。今回はデータとして、郵便局の住所一覧を用いました。
また、データは『全国郵便局名一覧』からダウンロードしました。ありがとうございます。








当たり前ですけど、郵便局はやっぱり多いですね。解析にまる3,4日くらいかかったような気がします。
しかも日本全国に散らばっているあたり、郵政事業がいかに重要なのか思い知らされます。
ちょっと映像関係でてきました








「マックまでの近さ」が光で表された米国地図 | WIRED JAPANで、ブックマークのコメントを見たらこんなものが。
この地図を作成したのは、Steven Von Worley氏。ロサンゼルス盆地の、本当に何もないようなところで目にしたマクドナルドに刺激されてこの地図の作成を思い立ったという。
はてなブックマーク - 「マックまでの近さ」が光で表された米国地図 | WIRED VISIONやってみようじゃないの。
Layzie food, ネタ 東と西でクッキリ分れてるねえ。誰か、日本で「吉野家」バージョンで作らないだろうか。





void draw(){
background( 0.0 );
perspective( PI/3.0, (float)width/(float)height, 1, 5000 );
gl.glDepthMask(false);
gl.glEnable( GL.GL_BLEND );
gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE );
pgl.beginGL();
emitter.exist();
pgl.endGL();
if( mousePressed ){
emitter.addParticles(20);
}
}
class Emitter {
Vec3D loc;
Vec3D vel;
Vec3D velToMouse;
color myColor;
ArrayList particles;
Emitter(){
loc = new Vec3D();
vel = new Vec3D();
velToMouse = new Vec3D();
myColor = color( 1, 1, 1 );
particles = new ArrayList();
}
void exist(){
setVelToMouse();
findVelocity();
setPosition();
//rendering Emitter
gl.glEnable( GL.GL_TEXTURE_2D ); //Enable texture mapping
iterateListExist();
render();
gl.glDisable( GL.GL_TEXTURE_2D );
}
void setVelToMouse(){
velToMouse.set( mouseX - loc.x, mouseY - loc.y, 0 );
}
void findVelocity(){
vel.interpolateToSelf( velToMouse, 0.2 );
}
void setPosition(){
loc.addSelf( vel );
}
void iterateListExist(){
pgl.bindTexture( particleImg );
for( Iterator it = particles.iterator(); it.hasNext(); ){
Particle p = (Particle)it.next();
if(!p.ISDEAD){
p.exist();
} else {
it.remove();
}
}
}
void render(){
pgl.bindTexture( emitterImg );
renderImage( loc, 200, myColor, 1.0 );
}
void addParticles( int _amt ){
for( int i=0; i<_amt; i++ ){
particles.add( new Particle( loc, vel) );
}
}
}
class Particle {
int len;
Vec3D[] loc;
Vec3D startLoc;
Vec3D vel;
float radius;
float age;
int lifeSpan;
float agePer;
boolean ISDEAD;
Particle( Vec3D _loc, Vec3D _vel ){
radius = random( 20, 60 );
len = (int)radius;
loc = new Vec3D[len];
startLoc = new Vec3D( _loc.add( new Vec3D().randomVector().scaleSelf( random(5.0) ) ) );
for( int i=0; i
loc[i] = new Vec3D( startLoc );
}
vel = new Vec3D( _vel.scale( 0.5 ).addSelf( new Vec3D().randomVector().scaleSelf( random( 10.0 ) ) ) );
age = 0;
lifeSpan = (int)radius;
agePer = 1.0;
}
void exist(){
setPosition();
render();
setAge();
}
void setPosition(){
for ( int i=len-1; i>0; i-- ){
loc[i].set(loc[i-1]);
}
loc[0].addSelf( vel );
}
void render(){
color c = color( agePer, agePer*0.75, 1.0 - agePer );
renderImage( loc[0], radius*agePer, c, 1.0 );
}
void setAge(){
age++;
if( age > lifeSpan ){
ISDEAD = true;
}else{
agePer = 1.0 - age/(float)lifeSpan;
}
}
}

前回は前準備の段階で終わったので、次はパーティクルを放出させるエミッタを表示してみる。エミッタはマウスに追随するようにばね運動をする。_gl.pdeは前回と変わっていないので省略する。コードは以下。
Particle2.pde
import toxi.geom.*;
import processing.opengl.*;
import javax.media.opengl.*;
// Settings about OPENGL
PGraphicsOpenGL pgl;
GL gl;
// Settings about Emitter
Emitter emitter;
// Settings about images
PImage particleImg;
PImage emitterImg;
void setup(){
size( 600, 600, OPENGL );
colorMode( RGB, 1.0);
hint( ENABLE_OPENGL_4X_SMOOTH );
pgl = (PGraphicsOpenGL) g;
gl = pgl.gl;
gl.setSwapInterval(1);
initGL();
particleImg = loadImage( "particle.png" );
emitterImg = loadImage( "emitter.png");
emitter = new Emitter();
}
void draw(){
background( 0.0 );
perspective( PI/3.0, (float)width/(float)height, 1, 5000 );
gl.glDepthMask(false);
gl.glEnable( GL.GL_BLEND );
gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE );
pgl.beginGL();
emitter.exist();
pgl.endGL();
}
emitter.pde
class Emitter {
Vec3D loc;
Vec3D vel;
Vec3D velToMouse;
color myColor;
Emitter(){
loc = new Vec3D();
vel = new Vec3D();
velToMouse = new Vec3D();
myColor = color( 1, 1, 1 );
}
void exist(){
setVelToMouse();
findVelocity();
setPosition();
//rendering Emitter
gl.glEnable( GL.GL_TEXTURE_2D ); //Enable texture mapping
render();
gl.glDisable( GL.GL_TEXTURE_2D );
}
void setVelToMouse(){
velToMouse.set( mouseX - loc.x, mouseY - loc.y, 0 );
}
void findVelocity(){
vel.interpolateToSelf( velToMouse, 0.2 );
}
void setPosition(){
loc.addSelf( vel );
}
void render(){
pgl.bindTexture( emitterImg );
renderImage( loc, 200, myColor, 1.0 );
}
}Particle2.pdeについては、Emitterクラスと画像の初期化、並びにemitter.exist()メソッドを付け加えただけ。主な処理はemitter.pdeで行っている。
実際のレンダリングはrender()が担当している。まずbindTexture()で貼り付けるテクスチャを指定して、_gl.pdeで定義したrenderImage()で描画している。その他のメソッドではエミッタの位置をマウスに追随させるようにしている。
interpolateToSelf()はtoxi.geom.Vec3Dで定義されている、自身のベクトルを他のベクトルに置き換えるメソッド。要はvelToMouseを0.2倍したベクトルに置き換えているだけ。この処理によって、エミッタはマウスの周辺でばね運動のような動きをする。
おーし次からが本番だ。
Flight404のサンプルではOpenGLを使用して平面に別途用意したパーティクルの画像を貼り付けて、表示するという手法をとっている。そのためP3Dと比べて、非常に高速で高精度な3D画像を描画できるのだが、その分生のOpenGLを叩かないといけないため、コードはいささか面倒になってくる。そこでとりあえずソースコードの中から何も表示されないアプリケーションを作って、コードの流れについて調べてみることにした。コードは以下。
particle1.pde
import toxi.geom.*;
import processing.opengl.*;
import javax.media.opengl.*;
// Settings about OPENGL
PGraphicsOpenGL pgl;
GL gl;
// Settings about some functions
void setup(){
size( 600, 600, OPENGL );
colorMode( RGB, 1.0);
hint( ENABLE_OPENGL_4X_SMOOTH );
pgl = (PGraphicsOpenGL) g;
gl = pgl.gl;
gl.setSwapInterval(1);
initGL();
}
void draw(){
background( 0.0 );
perspective( PI/3.0, (float)width/(float)height, 1, 5000 );
gl.glDepthMask(false);
gl.glEnable( GL.GL_BLEND );
gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE );
pgl.beginGL();
// write something...
pgl.endGL();
}
_gl.pde
int squareList;
void initGL(){
pgl.beginGL();
squareList = gl.glGenLists(1);
gl.glNewList(squareList, GL.GL_COMPILE);
gl.glBegin(GL.GL_POLYGON);
gl.glTexCoord2f(0, 0); gl.glVertex2f(-0.5, -0.5);
gl.glTexCoord2f(1, 0); gl.glVertex2f( 0.5, -0.5);
gl.glTexCoord2f(1, 1); gl.glVertex2f( 0.5, 0.5);
gl.glTexCoord2f(0, 1); gl.glVertex2f(-0.5, 0.5);
gl.glEnd();
gl.glEndList();
pgl.endGL();
}
void renderImage( Vec3D _loc, float _diam, color _col, float _alpha){
gl.glPushMatrix();
gl.glTranslatef( _loc.x, _loc.y, _loc.z );
gl.glScalef( _diam, _diam, _diam );
gl.glColor4f( red(_col), green(_col), blue(_col), _alpha );
gl.glCallList( squareList );
gl.glPopMatrix();
}
まずhint( ENABLE_OPENGL_4X_SMOOTH )を指定して、4xのオーバーサンプリングを許可。setSwapInterval()については、主に画面がちらつく現象を防ぐ目的のようだ。その後initGL()を呼び出して、_gl.pdeで書いたOpenGL周辺のセットアップを開始する。
initGL()では、正方形のポリゴンを作って画像を貼り付けるという作業が何回も繰り返されるので、ディスプレイリストの機能を利用する。テクスチャのUV座標と頂点をそれぞれglTexCoord2fとgl.glVertex2fで指定して、正方形を描画している。
renderImage()で実際に画像の描画を行う。glPushMatrix()とglPopMatrix()で行列スタックを取得、開放し、その間に平行移動やスケールをもってくることで、ローカル座標が狂わないようにしているようだ。
次にdraw()の項を見てみる。glDepthMask(false)で陰線消去を行わないようにして、glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE )で画像の加算処理を指定している。そしてbeginGL()とendGL()の間にOpenGLの描画命令を組み込むことで、実際にOpenGLで描画処理が行われるようになるという次第。
Processing(Proce55ing)界で知らない人は皆無と言われる(と勝手に思っている)Flight404のRobert Hodgin。とりあえず彼のことについて説明しますと、こんな感じの映像をProcessingで作ってしまうような人です↓
Weird Fishes: Arpeggi from flight404 on Vimeo.
(悔しいことに、ルックスもイケメンだ!)
で、実は彼はブログで「簡単」な(彼にとっては簡単なだけで他の人にとっては難しいです)Proce55ingのソースコードを公開しています。が、ワールドワイドではともかく、日本語のブログで彼のコードについて言及しているサイトやブログが全くない。それで自分の丁度いい勉強にもなりますんで、適当にFlight404のSRC: Particle Emitterで気づいたことを書き留めておくことにします。
もしかしたら飽きて、中途半端なところでやめるかもしれませんが、とりあえずそんときはそんときで。てきとーにやっていきます。

タイトルほどそんな大層なことはしていないんですが、Proce55ingと流体力学を使用して、川のような流れの中にまるーい物体を入れたときの水流の動きをアートっぽく可視化してみました。結構綺麗な模様ができて、自分としては満足です。
左から右に水が流れているもんだと思ってください。こんな感じに水流は円柱を避けて進んでいきます。
今度は円柱が時計回りに回転して、渦が発生しているようなときの水流の流れ。この渦の流れが強くなっていくと…
こんな感じにどんどんねじ曲がっていって…
最終的にはこんなかんじの、歪んだ水流になってしまいました。
本当は上の画像、アニメーションしてもっと面白い感じの映像になるんですけど、動画のキャプチャの仕方が分からないのでとりあえず画像だけ。
コードはhTakaさんのStreamDrawingの速度場を流体力学のに書き換えただけですし、しかもめっちゃ汚いのでとりあえず非公開です。興味のある方はそちらのコードを参照してください。hTakaさん、どうも有難う御座います。
それにしてもProce55ingはぱぱぱっといろんなことが出来て面白いです。なんかこれ使ってもっと有り得ないような映像できないかな。