アフィン変換
sample ps
数回前にやったpostscriptでの回転に付いて、分かったようで分からなかったので、少し探ってみる事にした。その過程で見つけた、面白い資料を挙げておく。
これ、無料のグラフ用紙ですって。コクヨさん、御免なさい。
ghostscriptは、C語から呼び出すライブラリィーが提供されていないようなので、C語で書くなら、postscrit語を出力する事になる。だったら、機動性が良いrubyとかで書くのがよさそう。
技術評論社で出している WEB+DB PRESS の次号特集は ruby3についてだぞ。python嫌いのオイラーは、たまに買ってもいいかも。
回転するぞ
肝はCTM
いよいよ本丸に迫ります。
アフィン変換なんてのが出て来た。これがキーになりそうだな。
段々とgostscriptに近づいてきた。
concatなんてのが出て来た。これが統一理論かな。
そして、遂に本丸へ到達した。
maximaで行列の記号解
行列の演算なんて超苦手なオイラーは、maximaに救いを求めてみます。
ghostscriptの心臓部であるCTMが下記のようだったとします。
[a b c d h v]
これを正式な行列で表現。
(%i7) R : matrix([a,b,h],[c,d,v],[0,0,1]); [ a b h ] [ ] (%o7) [ c d v ] [ ] [ 0 0 1 ] (%i8) R . matrix([x,y,1]); [ b y + a x + h ] [ ] (%o8) [ d y + c x + v ] [ ] [ 1 ]
そして、点(x,y)をワープと言うか変換します。これがアフィン変換なのね。
これ、記号的な表現。maximaでも、数値に置き換えてあげれば、ちゃんと数値で答えてくれるが、もう一つの専門家に依頼して、数値解を出してもらいましょう。
行列の専門家octave
入力が取っても簡単なのでお勧め。AIにも通じてる。
octave:5> a = [3,0 ; 0,0.5] a = 3.00000 0.00000 0.00000 0.50000 octave:7> a * [2;1] ans = 6.00000 0.50000
rubyでも行列する
rubyにもMatrixなクラスが有ったので、(無理して)使ってみた。
ob$ ruby -v ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-openbsd6.8] ob$ irb irb(main):001:0> require 'matrix' => true irb(main):002:0> R = Matrix[[3, 0], [0, 2]] => Matrix[[3, 0], [0, 2]] irb(main):003:0> p = Matrix[[1], [1]] => Matrix[[1], [1]] irb(main):004:0> R * p => Matrix[[3], [2]]
一応は使えるな。でも、醜いな。
irb(main):027:0> r.each_slice(r.column_size) {|row| puts row.inspect} [3, 0] [0, 2] => nil irb(main):032:0> q.each {|val| puts val} 1 1
そんな時には、さっと整形するんだそうです。
gaucheにも行列有った
rubyにもmatrixが有るなら、scheme界のrubyことgaucheにも有るに違いない。
gosh> (use gauche.array) gosh> (array-mul (array (shape 0 2 0 2) 1 0 0 1) (array (shape 0 2 0 1) 3 2)) #,(<array> (0 2 0 1) 3 2)
shapeなんて言う変なリストを添付してarrayを作る約束になってる。2個1組で、インデックスを表すとな。最初は、エレメントのスタート・インデックス。後の奴は終了インデックス+1を指定する。
上の例だと、最初の配列は 2x2、後の配列は 2x1 とみなされる。結果は行列界のお約束で、2x1になる。何だか、配列を脳内加工して、行列に見立てているんだな。こんな考え方はnumpyでも使ってたな。
ghostscriptで見える化
いよいよ実戦です。
GS>(grid.ps)run GS>grid GS>300 300 org GS>0 0 100 200 rectstroke GS>45 rotate GS>0 0 100 200 rectfill
gridは、数回前にやった目盛り用のやつ。原点を300,300に移動。左下を原点にした箱を書きます。幅は100で高さが200のやつ。rectfillを使うと中が塗りつぶされます。
次にrotateを使って、反時計方向に45度傾くように設定。これで次回からの描画は傾くはず。 そして、同じ設定で黒塗りの箱を描きます。見事に45度傾いた箱が出てきました。