[perl]scalar=$calar、array=@rray、hash=%演算?

カテゴリ: perl / author: uechoco / 2011年12月24日 16:10:27
この記事を読む時間:216くらい

ふと、perlのスカラ変数、配列、ハッシュの記号について思ったのが、

  • scalar = $calar(ドル記号がSに似ている)
  • array=@array(アットマーク記号がaに似ている)
  • hash=%演算(ハッシュを求める代表的な演算は、%記号で余りを求める方法)

なんじゃないかなーっと。Perlなんて時々スクリプトを見る程度だけど、昔(8年前くらいに)CGIゲームとかyybbsとか弄ってたときは、それぞれの記号が何を示しているのか、理解してなかったよなーって懐かしむ。

モイッチョおまけに、配列の最後の添字を取得する$#arrっていう書き方もあった気がするけど、

  • $#arr = arrという変数の#(番号=ここでは添字)を$(スカラー値)で返す
  • $#arr = arrという変数の$#(添字番号)を返す

という感じで#が番号っていう意味を知っていれば、すんなり受け入れらるよなーって。

ついでに配列やハッシュが@や%記号で扱ったり$記号で扱ったりしてこんがらがっていた時代もあったけど、これも簡単で、その時点でのコンテキスト(文脈)でどう扱っているかを記号で表しているだけじゃないかと。

ちなみに、ここで書いたのは勝手な想像であって、どこかで調べたわけじゃなくて、もしかしたら間違っているかもしれません。(それでも覚えやすいからいいかなっとw)

プログラミング歴が遊びも含めて10年を超えてくると、いろいろな知識も増えてきて、昔分からなかったことをすんなり分かるようになったりしてきます。自分の成長を実感できた瞬間でしたw

 

P.S. (2012/01/02 13:40):

今、初めてのPerl 第5版(通称:リャマ本)を読んでいるのですが、p53の脚注に、「Larryは、それぞれ$calar(scalar)と@rray(array)だから、ドル記号とアットマークを選んだと主張しています。」ということが書いてあり、公式見解として正しいようです。


[processing]魔方陣とかSF円とか(Processing Advent Calendar 2011 – 20日目)

カテゴリ: processing / author: uechoco / 2011年12月20日 01:02:20
この記事を読む時間:1954くらい

Processing Advent Calendar 2011の2回目の登場です。
20日目の担当ということで、最近Processingと疎遠でphpやらpythonやらと戯れている私は既にネタ切れですw

Processingは何かのプロダクトのプロトタイプを作ったり、自分が作りたいアニメーションを作ったりするのに向いていると言われています。
今回は私の趣味で、魔方陣とかSFちっくな円とか、そういうものを2種類公開いたします。

ミッドチルダ式魔方陣

Processing:
  1. /**
  2.  * ミッドチルダ式魔法陣 (元ネタ:魔法少女リリカルなのは)
  3.  *
  4.  * @title mugityax | 背景作成
  5.  * @link http://www.mugityax.com/weblog/2006/06/post_1.html
  6.  *
  7.  * @title ミッドチルダフォント置き場
  8.  * @link http://midfont.refy.net/
  9.  */
  10.  
  11. float rot1, rot2;
  12. PFont font1, font2;
  13.  
  14.   size(400, 400);
  15.   colorMode(HSB, 100);
  16.   frameRate(30);
  17.   smooth();
  18.   background(0);
  19.  
  20.   textAlign(CENTER, CENTER);
  21.  
  22.   font1 = loadFont("MID-CHILDA_Regular-30.vlw");
  23.   font2 = loadFont("MID-CHILDA_Regular-15.vlw");
  24.   //font1 = createFont("mid-childa_R.ttf", 30, true);
  25.   //font2 = createFont("mid-childa_R.ttf", 15, true);
  26.  
  27.   rot1 = rot2 = 0;
  28. }
  29.  
  30.   background(0);
  31.   {
  32.     noFill();
  33.     translate(width / 2, height / 2);
  34.     final float rl = 120 / sqrt(2); // 84.85281
  35.     final float ro = 149;
  36.    
  37.     // 固定円
  38.     {
  39.       drawLightEllipse(0, 0, 180*2, 180*2); // 外周円-二重外円
  40.       drawLightEllipse(0, 0, 175*2, 175*2); // 外周円-二重外円
  41.       drawLightEllipse(0, 0, 120*2, 120*2); // 外周円-内円
  42.       drawLightEllipse(0, 0, rl*2, rl*2); // 内周方形内-外円
  43.       drawLightEllipse(0, 0, 60*2, 60*2); // 内周方形内-内円
  44.     }
  45.    
  46.     // 外周小円・内周方形1
  47.     pushMatrix();
  48.     {
  49.       rotate(rot1);
  50.       // 外周小円
  51.       final float[] rm = {50, 45};
  52.       for (int i = 0; i <rm.length; i++) {
  53.         drawLightEllipse(0, -ro, rm[i], rm[i]);
  54.         drawLightEllipse(ro, 0, rm[i], rm[i]);
  55.         drawLightEllipse(0, ro, rm[i], rm[i]);
  56.         drawLightEllipse(-ro, 0, rm[i], rm[i]);
  57.       }
  58.       // 内周方形1
  59.       drawLightQuad(0,-120,120,0,0,120,-120,0);
  60.     }
  61.     popMatrix();
  62.    
  63.     // 内周方形2
  64.     pushMatrix();
  65.     {
  66.       rotate(rot2);
  67.       drawLightQuad(-rl,-rl,rl,-rl,rl,rl,-rl,rl);
  68.     }
  69.     popMatrix();
  70.    
  71.     final String[][] ot = {
  72.       {"Y","S","T","D","U","X","K","j","L","z","D","K","F","A","M","B","Y","o","h","Z","O","T","U","V","i"},
  73.       {"J","Z","p","V","A","Y","F","r","j","s","v","W","T","h","G","S","c","H","X","K","k","o","d","B","u"},
  74.       {"b","F","M","r","g","R","V","y","N","B","A","x","q","C","T","p","E","L","K","a","w","j","k","J","P"},
  75.       {"H","a","G","d","W","S","n","M","X","s","E","x","A","o","Z","C","b","f","k","U","V","i","Y","B","D"},
  76.       {"E","y","t","H","L","o","Q","W","T","h","a","d","C","B","b","w","v","D","s","Y","z","e","R","n","S"},
  77.       {"K","B","f","T","k","R","e","d","w","z","S","m","D","J","P","F","j","o","n","r","u","x","g","v","b"},
  78.       {"z","G","q","e","u","w","W","a","U","X","i","r","A","Y","D","t","N","V","k","P","Q","p","g","E","K"},
  79.       {"b","v","d","B","g","z","D","h","j","Q","n","U","G","Z","q","u","F","r","T","X","a","m","W","t","Y"},
  80.       {"Z","K","U","T","e","A","G","c","X","i","Q","o","R","F","k","t","p","D","J","d","r","q","n","V","E"},
  81.     };
  82.     final int[][] a_set = {
  83.       {14, 78, 4},
  84.       {102, 170, 4},
  85.       {192, 260, 4},
  86.       {282, 350, 4}
  87.     };
  88.  
  89.     // 外周文字
  90.     pushMatrix();
  91.     {
  92.       rotate(rot1);
  93.       fill(100);
  94.       textFont(font1);
  95.       for (int i = 0; i <4; i++) {
  96.         int c = 0;
  97.         for (int a = a_set[i][0]; a <a_set[i][1]; a+=a_set[i][2]) {
  98.           pushMatrix();
  99.           rotate(radians(a));
  100.           translate(ro, 0);
  101.           rotate(HALF_PI);
  102.           text(ot[i][c++], 0, 0);
  103.           popMatrix();
  104.         }
  105.       }
  106.     }
  107.     popMatrix();
  108.  
  109.     // 内周文字
  110.     pushMatrix();
  111.     {
  112.       rotate(rot2);
  113.       fill(100);
  114.       textFont(font2);
  115.       for (int i = 0; i <4; i++) {
  116.         int c = 0;
  117.         for (int a = a_set[i][0]; a <a_set[i][1]; a+=a_set[i][2]) {
  118.           pushMatrix();
  119.           rotate(radians(a));
  120.           translate(70, 0);
  121.           rotate(HALF_PI);
  122.           text(ot[i][c++], 0, 0);
  123.           popMatrix();
  124.         }
  125.       }
  126.     }
  127.     popMatrix();
  128.    
  129.   }
  130.   popMatrix();
  131.   update();
  132. }
  133.  
  134. void update() {
  135.   rot1 += 0.04;
  136.   rot2 -= 0.04;
  137. }
  138.  
  139. void drawLightEllipse(float a, float b, float c, float d) {
  140.   stroke(100, 100, 80, 40);
  141.   ellipse(a, b, c, d);
  142.   stroke(95, 100, 80, 60);
  143.   ellipse(a, b, c, d);
  144.   stroke(90, 100, 80, 80);
  145.   ellipse(a, b, c, d);
  146.   strokeWeight(1.5);
  147.   stroke(100);
  148.   ellipse(a, b, c, d);
  149. }
  150.  
  151. void drawLightQuad(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
  152.   stroke(100, 100, 80, 40);
  153.   quad(x1, y1, x2, y2, x3, y3, x4, y4);
  154.   stroke(95, 100, 80, 60);
  155.   quad(x1, y1, x2, y2, x3, y3, x4, y4);
  156.   stroke(90, 100, 80, 80);
  157.   quad(x1, y1, x2, y2, x3, y3, x4, y4);
  158.   strokeWeight(1.5);
  159.   stroke(100);
  160.   quad(x1, y1, x2, y2, x3, y3, x4, y4);
  161. }
  162.  
  163.   save("mid_tilde_magic_circle.jpg");
  164. }

まずは、魔方陣です。魔法少女リリカルなのはというアニメの中に出てくるミッドチルダ式魔方陣というものです。元ネタは全く知らないのですが、魔方陣的に描きやすかったので、参考にしました。専用のフォントも使用しています。プログラムは4つの構成要素になっていて、外周や内周の固定や回転する円、内周の正方形、外周円の文字、内周円の文字となっています。rotate()とmatrixを上手く使用して、座標軸を回転させることで描画する方式をとっています。仕組みさえわかってしまえば簡単にバリエーションが作成できます。

SF円

Processing:
  1. float cx, cy;
  2. int ringCount = 0;
  3. Ring[] rings = new Ring[1];
  4.  
  5.   size(400, 400);
  6.   background(0);
  7.   noFill();
  8.   smooth();
  9.   strokeCap(SQUARE);
  10.   ellipseMode(RADIUS);
  11.  
  12.   cx = width / 2;
  13.   cy = height / 2;
  14.  
  15.   float pRadius = 20, pWeight = 2;
  16.   while (pRadius <180) {
  17.     Ring r = addRing();
  18.     r.setWeight(random(2, 25));
  19.     r.setPosition(cx, cy);
  20.     r.setRadius(pRadius + (pWeight + r.weight) / 2 + 1);
  21.     r.setRadians(random(TWO_PI)+PI/3, random(TWO_PI)+PI/3);
  22.     r.setColor(#ffffff);
  23.     r.setRot(random(-0.05, 0.05));
  24.     pRadius = r.r;
  25.     pWeight = r.weight;
  26.   }
  27. }
  28.  
  29.   background(0);
  30.   for (int i = 0; i <ringCount; ++i) {
  31.     rings[i].draw();
  32.     rings[i].move();
  33.   }
  34. }
  35.  
  36. Ring addRing() {
  37.   if (rings.length == ringCount) {
  38.     rings = (Ring[]) expand(rings);
  39.   }
  40.   Ring r = new Ring();
  41.   rings[ringCount++] = r;
  42.   return r;
  43. }
  44.  
  45. class Ring {
  46.   float x, y, r;
  47.   float weight;
  48.   float startRad, endRad;
  49.   float rot;
  50.   color c;
  51.  
  52.   void draw() {
  53.     stroke(c);
  54.     strokeWeight(weight);
  55.     arc(x, y, r, r, startRad, endRad);
  56.   }
  57.  
  58.   void move() {
  59.     startRad += rot;
  60.     endRad += rot;
  61.     if (startRad> TWO_PI && endRad> TWO_PI) {
  62.       startRad -= TWO_PI;
  63.       endRad -= TWO_PI;
  64.     }
  65.   }
  66.  
  67.   void setPosition(float x, float y) {
  68.     this.x = x;
  69.     this.y = y;
  70.   }
  71.  
  72.   void setRadius(float r) {
  73.     this.r = r;
  74.   }
  75.  
  76.   void setRadians(float start, float end) {
  77.     this.startRad = start;
  78.     this.endRad = end;
  79.   }
  80.  
  81.   void setWeight(float weight) {
  82.     this.weight = weight;
  83.   }
  84.  
  85.   void setColor(color c) {
  86.     this.c = c;
  87.   }
  88.  
  89.   void setRot(float rot) {
  90.     this.rot = rot;
  91.   }
  92. }
  93.  
  94.   save("sf_arc_ring_magic_circle.jpg");
  95. }

正式に何といったらよいのかわかりませんが、SF映画とかのコンソール画面とかの後ろ側で無駄にかっこ良く回ってるアレですw プログラムの仕組みとしては、円弧(arc)を何十にも重ねてそれぞれ別々の弧の長さや回転速度を持たせているだけです。プログラム上の工夫としては、円弧の部分をRingというクラスにまとめ、draw()ループから呼び出すメソッドはRing.draw()やRing.move()に限定することで、draw()ループを見やすくしています。このように、似たような動作をするオブジェクトをクラスとして定義して、パラメータの違いによって動作を変え、描画や移動の時はdraw()やmove()などのメソッドを作成して呼ばせるような手法は、プログラムをすっきりとさせるし、動作の塊がオブジェクトとしてまとまっているので、大変見やすくなっています。

最後に

と、今回は2つの作品の紹介で終わりでちょっと物足りなかったかと思います。

私はCGクリエイターと言うよりはCGに興味があるプログラマーなので、自分が作りたいものが作れれば良いという考えではなく、「この作品を作るにはどういうプログラムにすると上手く書けるか」という視点でProcessingのコードを書くことが多いです。前回の伝統模様の記事でも、いろんな書き方で伝統模様を書いていたのをご覧になっているかと思います。私の目標としては"CG自体の美"の次くらいに"プログラムコードの美"を追求していきたいと思っています。

めちゃめちゃなコードでもその場限りで描ければ良しとする考えもあるのですが、数カ月後とか、1年後とかにProcessingのコードを見て、何やっているかわからないと困る場合もありますよね。その点、プログラムコードの美しさも最初から追求していれば、後から読んでも読みやすいプログラムなので、理解がしやすいかと思います。

とまぁ、作品紹介ついでに本職プログラマーならではのプログラミングの視点をご紹介してみました。


[Django]ChoiceFieldのchoicesに関数を指定したい

カテゴリ: python / author: uechoco / 2011年12月14日 17:23:14
この記事を読む時間:313くらい

Djangoのformで、ChoiceFieldのchoicesには固定のtupleを指定したり、ModelForm任せだったりすることがほとんどだったのですが、独自に加工した一覧を出したくて、choicesに関数を指定しようとしました。

ただ、素直に関数名をforms.ChoiceField(choices=func_name)と指定しただけだと、
'function' object is not iterable
と怒られてしまいました。

どうしたものかとググっていたら、見つけました

python - Lazy choices in Django form - Stack Overflow

djangoのlazyというユーティリティを使って関数を与える方法が書いてありました!

PYTHON:
  1. from django.utils.functional import lazy
  2.  
  3.     forms.ChoiceField(choices=lazy(func_name, tuple)())

こんな感じですね。これは使える!

とか思ってたけど、やりたいことこれじゃないよ!あと1歩だった。
ModelFormにinstanceをセットして、そのinstanceの中身によって変動するchoicesって作れるのかな?(こっちが知りたかった

P.S.
結局やりたい事はModelChoiceFieldでした。知らなかった。querysetを適切に与えてやるためにFormの__init__()を上書きして必要なインスタンスを渡してあげています。だいたい以下のような感じ

PYTHON:
  1. class HogeForm(forms.ModelForm):
  2.     foo = forms.ChoiceField(choices=())
  3.     bar = None
  4.  
  5.     class Meta:
  6.         model = Hoge
  7.         fields = ('foo', )
  8.  
  9.     def __init__(self, bar, *args, **kwargs):
  10.         super(HogeForm, self).__init__(*args, **kwargs)
  11.         self.bar = bar
  12.         self.fields['foo'] = forms.ModelChoiceField(queryset=Foo.objects.filter(bar=bar))
  13.  
  14.     def save(self, commit=False):
  15.         hoge = super(HogeForm, self).save(commit=False)
  16.         hoge.bar = self.bar
  17.         return hoge.save()


[processing]伝統模様を描いてみよう(Processing Advent Calendar 2011 – 9日目)

カテゴリ: processing / author: uechoco / 2011年12月09日 00:01:56
この記事を読む時間:2213くらい

Processingの記事を書くのは本当に久しぶりです。
Processing Advent Calendar 2011に参加しておりまして、9日目の担当ということで執筆いたします。

みなさんは、伝統模様と聞いてパッとイメージが浮かぶでしょうか?
たぶん着物やハンカチなどの絵柄に使われているのを見たことがあるかもしれません。

伝統模様は

  • 同じパターンが繰り返し使われている
  • 幾何学的な模様が多い

といった観点からとても不思議な模様で、またProcessingの練習で描いてみる題材としても適していると思っています。

百聞は一見にしかず、いくつか私の描いたものをコード付きでご紹介します。

青海波(せいがいは)

Processing:
  1. // 青海波(せいがいは)
  2.  
  3. int r = 80;
  4. int r2 = r * 2;
  5.  
  6.   size(400, 400);
  7.   background(255, 255, 255);
  8.   smooth();
  9.   noLoop();
  10. }
  11.  
  12.   noStroke();
  13.   background(255, 255, 255);
  14.  
  15.   boolean flag = true;
  16.   for (int y = 0; y <height + r2; y += r) {
  17.     int x_base = flag ? 0 : r / 2 ;
  18.     for (int x = x_base; x <width + r2; x += r) {
  19.       drawPattern(x, y);
  20.     }
  21.     flag = !flag;
  22.   }
  23. }
  24.  
  25. void drawPattern(float cx, float cy) {
  26.   ellipseMode(CENTER_RADIUS);
  27.   int script[][] = {
  28.     {80, 0, 0, 0},
  29.     {75, 255, 255, 255},
  30.     {65, 0, 0, 0},
  31.     {60, 255, 255, 255},
  32.     {50, 0, 0, 0},
  33.     {45, 255, 255, 255},
  34.     {35, 0, 0, 0},
  35.     {30, 255, 255, 255},
  36.     {20, 0, 0, 0},
  37.     {15, 255, 255, 255},
  38.     {5, 0, 0, 0}
  39.   };
  40.   for (int i = 0; i <script.length; i++) {
  41.     int len = script[i][0];
  42.     fill(script[i][1], script[i][2], script[i][3]);
  43.     ellipse(cx, cy, len, len);
  44.   }
  45. }

青海波は文字通り波をイメージした模様です。私のコードでは、2重のfor文でx, y座標をずらしながら、
パターン化した白黒の円を上から塗り重ねることで、青海波っぽく描いています。

星七宝繋ぎ(ほししっぽう)

Processing:
  1. // 星七宝繋ぎ(ほししっぽう)
  2.  
  3. int r = 80;
  4. int r2 = r * 2;
  5.  
  6. float ct[] = new float[360];
  7. float st[] = new float[360];
  8.  
  9.   size(400, 400);
  10.   background(0, 0, 0);
  11.   smooth();
  12.   noLoop();
  13.  
  14.   ellipseMode(CENTER_RADIUS);
  15.  
  16.   for (int d = 0; d <360; d++) {
  17.     ct[d] = cos(radians(d));
  18.     st[d] = sin(radians(d));
  19.   }
  20. }
  21.  
  22.   background(0, 0, 0);
  23.  
  24.   stroke(255, 255, 255);
  25.  
  26.   noFill();
  27.   boolean flag = true;
  28.   for (int y = -20; y <height + r2; y += r) {
  29.     int x_base = -20 + (flag ? 0 : r);
  30.     for (int x = x_base; x <width + r2; x += r2) {
  31.       drawPattern(x, y);
  32.     }
  33.     flag = !flag;
  34.   }
  35.  
  36.   fill(0, 0, 0);
  37.   flag = true;
  38.   for (int y = -20; y <height + r2; y += r) {
  39.     int x_base = -20 + (flag ? 0 : r);
  40.     for (int x = x_base; x <width + r2; x += r2) {
  41.       drawPattern2(x, y);
  42.     }
  43.     flag = !flag;
  44.   }
  45. }
  46.  
  47. void drawPattern(float cx, float cy) {
  48.  
  49.   float p[][] = {
  50. //    {cx + r, cy - r, 0, 90, 180, 270},
  51. //    {cx - r, cy - r, 90, 180, 270, 360},
  52.     {cx - r, cy + r, 180, 270, 0, 90},
  53.     {cx + r, cy + r, 270, 360, 90, 180}
  54.   };
  55.  
  56.   for (int i = 0; i <p.length; i++) {
  57.     float tx = p[i][0];
  58.     float ty = p[i][1];
  59.     int crad_begin = (int)p[i][2];
  60.     int crad_end = (int)p[i][3];
  61.     int trad_begin = (int)p[i][4];
  62.     int trad_end = (int)p[i][5];
  63.  
  64.     beginShape(LINES);
  65.     for (int d = crad_begin; d <crad_end; d++) {
  66.       vertex(cx + ct[d] * r, cy - st[d] * r);
  67.     }
  68.     for (int d = trad_begin; d <trad_end; d++) {
  69.       vertex(tx + ct[d] * r, ty - st[d] * r);
  70.     }
  71.     endShape(CLOSE);
  72.   }
  73. }
  74.  
  75. void drawPattern2(float cx, float cy) {
  76.   ellipse(cx + r, cy, 10, 10);
  77.   ellipse(cx, cy + r, 10, 10);
  78. }

七宝模様は、円を重ね合わせることで、円の弧が星のように見える模様です。
私のコードはちょっと複雑ですね。たしか円を並べただけだと七宝模様に近づかなかったので、
円弧をvertex()で描いていったのだと記憶しています。

花亀甲(はなきっこう)

Processing:
  1. // 花亀甲(はなきっこう)
  2.  
  3. float ROOT_3 = (float)Math.sqrt(3);
  4. float ROOT_3_DIV_2 = ROOT_3 / 2.0;
  5.  
  6. float ROOT_2 = (float)Math.sqrt(2);
  7. float ONE_DIV_ROOT_2 = 1 / ROOT_2;
  8.  
  9. int ix = -20;
  10. int iy = -20;
  11. int r = 50;
  12. int r2 = r * 2;
  13. int in_r = r - 8;
  14.  
  15. float ct[] = new float[360];
  16. float st[] = new float[360];
  17.  
  18.   size(400, 400);
  19.   background(255, 255, 255);
  20.   smooth();
  21.   noLoop();
  22.  
  23.   for (int d = 0; d <360; d++) {
  24.     ct[d] = cos(radians(d));
  25.     st[d] = sin(radians(d));
  26.   }
  27. }
  28.  
  29.   background(255, 255, 255);
  30.  
  31.   boolean flag = true;
  32.   for (float y = iy; y <height + r2; y += r * 3 / 2) {
  33.     float x_base = ix + (flag ? 0 : r * ROOT_3_DIV_2);
  34.     for (float x = x_base; x <width + r2; x += r * ROOT_3) {
  35.       drawPattern(x, y);
  36.     }
  37.     flag = !flag;
  38.   }
  39. }
  40.  
  41. void drawPattern(float cx, float cy) {
  42.  
  43.   float out_len = r;
  44.   float in_len = r - 5;
  45.  
  46.   int deg_list[] = {30, 330, 270, 210};
  47.   int deg_list2[] = {30, 330, 270, 210, 150, 90, 30};
  48.  
  49.   // 外側の亀甲
  50.   stroke(0, 0, 0);
  51.   noFill();
  52.   drawTortoiseshell(cx, cy, r, deg_list);
  53.  
  54.   // 内側の亀甲
  55.   drawTortoiseshell(cx, cy, in_r, deg_list2);
  56.  
  57.   // 中央文様
  58.   drawFlower(cx, cy, 7, 30, 20);
  59. }
  60.  
  61. void drawTortoiseshell(float cx, float cy, float r, int[] deg_list) {
  62.   float ox = ct[deg_list[0]] * r;
  63.   float oy = -st[deg_list[0]] * r;
  64.   for (int i = 1; i <deg_list.length; i++) {
  65.     float nx = ct[deg_list[i]] * r;
  66.     float ny = -st[deg_list[i]] * r;
  67.     line(cx + ox, cy + oy, cx + nx, cy + ny);
  68.     ox = nx;
  69.     oy = ny;
  70.   }
  71. }
  72. void drawFlower(float cx, float cy, int thin, int len, int len2) {
  73.   noStroke();
  74.   fill(0, 0, 0);
  75.  
  76.   // 上の葉(90〜270)
  77.   // 下の葉(-90〜90)
  78.   for (int i = 0; i <360; i++) {
  79.     float x = - st[safe_degrees(i * 2)] * len / thin;
  80.     float y = ct[safe_degrees(i)] * len;
  81.     vertex(cx + x, cy + y);
  82.   }
  83.   endShape(CLOSE);
  84.  
  85.   // 右の葉(-90〜90)
  86.   // 左の葉(90〜270)
  87.   for (int i = 0; i <360; i++) {
  88.     float x = ct[safe_degrees(i)] * len;
  89.     float y = - st[safe_degrees(i * 2)] * len / thin;
  90.     vertex(cx + x, cy + y);
  91.   }
  92.   endShape(CLOSE);
  93.  
  94.   // 左下の葉(90〜270)
  95.   // 右上の葉(-90〜90)
  96.   for (int i = 0; i <360; i++) {
  97.     float x = - st[safe_degrees(i * 2)] * len2 / thin;
  98.     float y = ct[safe_degrees(i)] * len2;
  99.     float dx = -1 * ONE_DIV_ROOT_2 * (x - y);
  100.     float dy = -1 * ONE_DIV_ROOT_2 * (x + y);
  101.     vertex(cx + dx, cy + dy);
  102.   }
  103.   endShape(CLOSE);
  104.  
  105.   // 右下の葉(90〜270)
  106.   // 左上の葉(-90〜90)
  107.   for (int i = 0; i <360; i++) {
  108.     float x = ct[safe_degrees(i)] * len2;
  109.     float y = - st[safe_degrees(i * 2)] * len2 / thin;
  110.     float dx = -1 * ONE_DIV_ROOT_2 * (x - y);
  111.     float dy = -1 * ONE_DIV_ROOT_2 * (x + y);
  112.     vertex(cx + dx, cy + dy);
  113.   }
  114.   endShape(CLOSE);
  115. }
  116.  
  117. int safe_degrees(int deg) {
  118.   while (deg>= 360) deg-=360;
  119.   while (deg <0) deg += 360;
  120.   return deg;
  121. }

亀甲模様に花柄がついた、花亀甲という模様です。コードもだいぶ複雑になって来ました。プログラムは太い亀甲を描くルーチン、細い内側の亀甲を描くルーチン、そして花柄模様を描くルーチンの3種類に分かれています。そしてそれらを1つのぱたーんとしてまとめあげて、亀甲の中心点を移動させることでパターン化しています。花柄を描くときやcos()やsin()に係数を掛けて、円を歪ませることで花びらの1枚1枚を描いています。

先ほどの星七宝のコードでも出てきたのですが、私のコードでは、cos()とsin()を0〜359度分、予め計算した状態で配列に確保しています。
これはコードの内部で使用する角度をを0〜359の整数に限定することで、描画中にcos()関数、sin()関数の結果がそれぞれ360通りに限定されるので、予め計算しておくことが出来るようになっています。小さな静止画を描くときはそこまで効力を発揮しないのですが、動く物体を描くときや、大量にcos()、sin()関数を使うときは非常に高速に計算ができます(関数を呼び出して計算させるより、予め計算結果を溜めておいたものを使用したほうが速のです)。

武田菱(たけだびし)

武田氏の家紋である「四つ割菱」の入った模様です。

Processing:
  1. // 武田菱(たけだびし)
  2.  
  3. float ROOT_3 = (float)Math.sqrt(3);
  4. float ROOT_3_DIV_2 = ROOT_3 / 2.0;
  5.  
  6. int ix = -60;
  7. int iy = -60;
  8. int block_w = 100;
  9. int block_h = 60;
  10. float ey = 10;
  11. float ex = ey * ROOT_3;
  12.  
  13. float ct[] = new float[360];
  14. float st[] = new float[360];
  15.  
  16.   size(400, 400);
  17.   background(0, 0, 0);
  18.   smooth();
  19.   noLoop();
  20.  
  21.   for (int d = 0; d <360; d++) {
  22.     ct[d] = cos(radians(d));
  23.     st[d] = sin(radians(d));
  24.   }
  25. }
  26.  
  27.   background(0, 0, 0);
  28.  
  29.   strokeJoin(MITER);
  30.  
  31.   boolean flag = true;
  32.   for (float y = iy; y <height + block_h; y += (block_h + ey) / 2) {
  33.     float x_base = ix + (flag ? (block_w + ex) / 2 : 0);
  34.     for (float x = x_base; x <width + block_w; x += block_w + ex) {
  35.       drawPattern(x, y, block_w, block_h);
  36.     }
  37.     flag = !flag;
  38.   }
  39. }
  40.  
  41. void drawPattern(float x, float y, float block_w, float block_h) {
  42.  
  43.   float subblock_w = block_w / 2;
  44.   float subblock_h = block_h / 2;
  45.   float cx = x + subblock_w;
  46.   float cy = y + subblock_h;
  47.   int m = 8;
  48.  
  49.   stroke(255, 255, 255);
  50.   noFill();
  51.   for (int i = m; i>= 4; i-=2) {
  52.     float w = lerp(0, subblock_w, i / (float)m);
  53.     float h = lerp(0, subblock_h, i / (float)m);
  54.     quad(
  55.       cx, cy - h,
  56.       cx - w, cy,
  57.       cx, cy + h,
  58.       cx + w, cy
  59.     );
  60.   }
  61.   float w = lerp(0, subblock_w, 4 / (float)m);
  62.   float h = lerp(0, subblock_h, 4 / (float)m);
  63.   line(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2);
  64.   line(cx + w / 2, cy - h / 2, cx - w / 2, cy + h / 2);
  65. }
  66.  
  67. int safe_degrees(int deg) {
  68.   while (deg>= 360) deg-=360;
  69.   while (deg <0) deg += 360;
  70.   return deg;
  71. }

こちらのコードはそこまで複雑ではありません。四角形を描くquad()関数を多用して、上手く菱形を描いています。

おわりに

簡単ではありますが、伝統模様をお見せしました。
これ以外にも伝統模様はたくさんありますので、調べてみて、「この模様はどうやってProcessingで描いたら上手く書けるかな?」なんて考えてみてはいかがでしょうか。伝統模様に限らず、身近なデザインでも「Processingでどうやって書いたらいいかな?」って考えてみると、Processingの腕が上達するかもしれません。

それでは、次回は@reona396さんです。よろしくお願いします!


[Symfony2]入出力の文字エンコードを変換してみよう Symfony Advent Calender JP 2011 – 6日目-

カテゴリ: php,Symfony2 / author: uechoco / 2011年12月06日 10:00:00
この記事を読む時間:358くらい

@uechocoです。Symfony Advent Calendar JP 2011の6日目の記事です。@fivestrさんからのバトンタッチです。

日本Symfonyユーザー会に所属するスタッフとして、勢いで申し込んでみたものの、
普段職場でSymfonyを使ってないのでネタ探しに苦労しました。

そういえば去年のSymfony Advent 2010 : ATNDでは[Symfony2]PEAR::Net_UserAgent_MobileをDIコンテナから呼び出すという記事を書きました。
今年も似たような領域で、ガラケー向けのWebサイト開発で使えるかもしれないテクニックとして、入出力される値やコンテンツの文字コードを変換する方法をご紹介します。

symfony 1.x系ではMojavi由来のフィルタチェインの機構がありましたので、
MobileEncodingFilterなどのクラスを作って、入出力の前後でエンコードの変換をかませるというのが一般的だったかと思います。

Syfmony2系ではフィルタチェインという概念はなくなってしまいました。
代わりに処理の所々にイベントが定義されていて、リスナーを登録しておくと自動的にディスパッチしてくれるようなイベントドリブン方式に変わりました。

今回はSymfony2のHttpKernel機構に標準で設定されているkernel.requestイベントとkernel.responseイベントのリスナークラスとしてMobileEncodingListenerを作り、入出力のエンコード処理を行なっています。今回はSymfonyに標準で同梱されているAcme\DemoBundleの中に作成する前提です。

MobileEncodingListenerクラスの作成

まずはイベントリスナークラスを作成します。Symfony本体のFrameworkBundleやHttpKernelコンポーネントのディレクトリ構造の慣習に従ってAcme\DemoBundleの下にEventListenerディレクトリを作成し、その下にイベントリスナークラスを作成します。以下のようなMobileEncodingListnerクラスを作成してください。まだ中身は実装しません。

php:
  1. <?php
  2.  
  3. namespace Acme\DemoBundle\EventListener;
  4.  
  5. /**
  6.  * Convert input encoding or output encoding.
  7.  * @author uechoco <uechoco at gmail.com>
  8.  *
  9.  */
  10. class MobileEncodingListener
  11. {
  12. }

kernel.responseイベント

まずは簡単なkernel.responseイベントに対するメソッドを記述します。クラスの使用宣言を追加して、MobileEncodingListenerクラスにonKernelResponse()メソッドを実装します。

php:
  1. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  2. use Symfony\Component\HttpKernel\HttpKernelInterface;

php:
  1. /**
  2.      * Convert output encoding of response.
  3.      * @param FilterResponseEvent $event
  4.      */
  5.     public function onKernelResponse(FilterResponseEvent $event)
  6.     {
  7.         if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  8.             return;
  9.         }
  10.  
  11.         $response = $event->getResponse();
  12.  
  13.         $response->setContent(mb_convert_encoding($response->getContent(), 'SJIS-win', 'UTF-8'));
  14.     }

中身の処理としては単純で、Resopnseオブジェクトを取得し、その中のコンテンツの文字コードを変換してまたセットしなおしているだけです。今回は説明の簡略化のために文字コードの部分を直書きしています。最初の行のマスターリクエスト以外なら何もしないという条件文がありますが、HttpKernelではリクエストの種別が2種類あり、マスターリクエストとサブリクエストと呼ばれています。そのマスターリクエストの時だけ処理したいので条件式を記述しています。FilterResponseEventクラスを受け取るというのはkernel.responseイベントとの仕様として決められています。

kernel.requestイベント

次にkernel.requestイベントに対するメソッドを記述します。先ほどと同様にクラスの使用宣言を追加して、MobileEncodingListenerクラスにonKernelRequest()メソッドを実装します。

php:
  1. use Symfony\Component\HttpKernel\Event\GetResponseEvent;

php:
  1. /**
  2.      * Convert input encoding of request.
  3.      * @param GetResponseEvent $event
  4.      */
  5.     public function onKernelRequest(GetResponseEvent $event)
  6.     {
  7.         if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  8.             return;
  9.         }
  10.  
  11.         $request = $event->getRequest();
  12.  
  13.         // GET
  14.         $get = $request->query->all();
  15.         mb_convert_variables('UTF-8', 'SJIS-win', $get);
  16.         $request->query->replace($get);
  17.         // POST
  18.         $post = $request->request->all();
  19.         mb_convert_variables('UTF-8', 'SJIS-win', $post);
  20.         $request->request->replace($post);
  21.     }

こちらもマスターリクエストのみ処理する記述が最初にあります。次にRequestオブジェクトを取得し、今回は$_GETと$_POSTに相当する変数だけを文字コード変換しました。$_GETと$POSTはRequestオブジェクトの$queryと$requestというパブリック変数に代入されています。形式としてはParameterBagというクラスでラッピングされて代入されています。ParameterBagクラスのインターフェースとしてall()メソッドで全配列を取得し、replace()メソッドで全配列を総取替しています。

イベントリスナーとして登録する

最後に、先ほど作ったクラスとメソッドをイベントリスナーとして登録します。登録手順はDIコンテナにサービスとして登録するのと同じですが、特別なタグを定義することでイベントリスナーとして認識されるようになっています。src/Acme/DemoBundle/Resources/config/services.xmlを開き、以下のように追記してください。

XML:
  1. <!-- 省略 -->
  2.  
  3.     <parameters>
  4.         <parameter key="mobile_encoding_listener.class">Acme\DemoBundle\EventListener\MobileEncodingListener</parameter>
  5.     </parameters>
  6.  
  7.     <services>
  8.         <!-- 省略 -->
  9.         <service id="acme.demo.mobile_encoding_listener" class="%mobile_encoding_listener.class%">
  10.             <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
  11.             <tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" />
  12.         </service>
  13.     </services>
  14.  
  15.     <!-- 省略 -->

確認する

何事もなければデモ画面が正常に動作すると思います。プロファイラのEvent項を見てみると、リスナーが登録されていることが確認できます。

今回直書きだった文字コードの指定は、config.ymlなどの設定ファイルから取ってくるような仕様にしたり、UserAgentを判別して決定するような仕様にしたほうが汎用性が高いでしょう。

余談

今回の記事の参考にするために他のイベントリスナークラスを見ていましたが、イベントリスナークラスの実装は様々なパターンが存在しているようです。例えば1つのリスナークラスに2つ以上のイベントメソッドをもたせているものや、1クラス1イベントメソッドのもの、他の役割のあるクラスにイベントメソッドを持たせてリスナークラスを兼任させているものなどです。

SymfonyのHttpKernelにどのようなイベントがあるか知りたい方は、Symfonyの内部構造のドキュメントの一節にイベントについて書かれている部分がありますので、そちらを参照してください。

最後に

7日目はMongoDB JPにも所属している@madapajaさんです。よろしくお願いします。


« 前ページへ次ページへ »

Copyright © 2012 うえちょこ@ぼろぐ. WP Theme created by Web Top.