mieki256's diary



2014/05/01(木) [n年前の日記]

#1 [nitijyou] 自宅サーバ止めてました

雷が鳴ったので、12:50-16:00の間、自宅サーバを止めてました。申し訳ないです。

天気が荒れるとニュースで流れてたけど、案の定…。

#2 [nitijyou] 弟が仙台に戻った

PM1:30頃出発してPM4:00頃に着いたらしいとメモ。

#3 [pc] 録画用PCのHDDを増設

弟が、「録画用PCに増設したまえ」と2TBのHDDを置いていってくれたので早速増設。

HDDは、WESTERN DIGITAL WD20EARS。SATA接続、2TB。弟曰く、速度が遅い点が気に入らなくて違うHDDに置き換えたので余った、とのこと。まあ、遅い分、省電力気味のHDDだったはずだけど。

使ってる MicroATXケース ―― ハードオフで中古で買ったケース ―― の3.5インチベイに差し込んでみたら、ネジ穴の位置が合わず。コレ、In Winのケースと思い込んでたけど、それにしちゃ作りが雑だなと…。細かい部分はIn Winのソレにしか見えないのだけど、自分の勘違いなのかな。

仕方ないので、3.5インチ→5インチにする金具をつけて5インチベイ部分に入れてみたり。

OS側ではHDDを素直に認識してくれた。ドライブ文字をEからDに変更。

2014/05/02(金) [n年前の日記]

#1 [pc] ケースをぐぐってたり

ビデオカードはオンボードビデオでいいとして、HDDは数台積みたいな、それでいて省スペースなMicroATXケースってないのかな。と思ってググってみたのだけど。そういうケースは無いですな。

ビデオカードは積まないよ、となると、スリムケースにする方向でしか省スペース化はされないし。となるとHDDは1台、光学ドライブ1台しか積めない設計になるし。

まあ、今時HDDを数台積むより、大容量HDDを1台積めば済んじゃうよ、という話になりそうだし。数台積んでどうこうとなると、NASにしてRAIDにして、という話にもなりそうだし。あるいは、ATXケース使えや、山ほど積めますぜ、という話にもなりそうだし。

2014/05/03() [n年前の日記]

#1 [pc] WD20EARSのIntelliPark設定を変更した

録画用PCに、HDD WD20EARS を増設したわけだけど。スペックについてググってたら、IntelliPark問題てのがあったことを思い出してきたわけで。

_FAQ - WesternDigital製HDD友の会 Wiki*
_適当えんじにあ( ´д`): WesternDigital 製の HDD は IntelliPark がある限り購入しない

_WDIDLE3 for Windows - スモキンカゴーン から Windows上で動作するらしい wdidle3.exe を使わせてもらったり。ありがたや。もちろん、自己責任で。万が一、中身が消えても覚悟の上で。

接続されてるドライブ番号の一覧を表示。
wdidle3 /r
パッと見では、デフォルトの8秒設定になっているように見えた。

300秒で働くようにしてみた。
wdidle3 /s300 ドライブ番号
一旦シャットダウン。電源を切らないと反映されないらしい。再度電源を入れて確認したところ、300秒=5分で働くはずの設定値になった。と思う。たぶん。これでしばらく様子を見てみようかなと。

2014/05/04() [n年前の日記]

#1 [nitijyou] 録画してたアニメを消化

HDDレコーダの空き容量が無いので、ひたすら見て消し。というかもはや流し消しのような気もするけど。各作品のスタッフさんに対してなんだか申し訳ない気分に。

でも、本当に面白い作品は、そっちに目が釘付けになったり、「今のシーンが気になる」と巻き戻しするし…。流し消しになってしまう作品は、本来自分の趣味趣向と合ってない作品なんだろうな、とも。

2014/05/05(月) [n年前の日記]

#1 [nitijyou] 朝に地震

メインPC上で動かしてるカエル(SignalNow Express)が早朝に反応。震源地の予測が東京方面、かつ、短い時間で2回揺れたので不安に。まさか、前々から言われてたソレがいよいよ来てしまったのか…。と思ったけどNHKをつけたらそれほどでもなかったようでホッとしたり。

#2 [web][zatta] サービス内容のバランスってあるのだろうな

ask.fmというサービスが気になっているのです。とあるアニメ監督さんが演出論について質疑応答していて、これは勉強になるなー、ありがたやありがたやと拝みつつ眺めているのですけど。しかし時々極めてごく稀に、自分ですら首を捻ってしまう回答が出現する瞬間があって。

そんな時、回答内容にツッコミを入れる機能がついてたら便利になるのかなあ、と思ってしまったのですけど。直後、それってサービス内容としてマズイよなと思い直して。質問者と回答者がやり取りしてる中に、第三者が「それは違うぞ!(バーン!)」と割り込んでくるのってどうなんだろうと。回答者をそっちのけにして、第三者達があーでもないこーでもないと議論を深めていったら、たぶん回答者は嫌になっちゃってアカウント閉じちゃうよなと。

つまり、そんな機能をつけた瞬間、ask.fmというサービスが提供していたシンプルさは崩れ、そのサービスが受け入れられていた理由・本質部分を失うような気がしてきたわけで。

例えば、twitter には140文字という制限があるけれど、140文字では語り切れないからもっと文字数を増やそう、なんてことをしたら、それはもう twitter じゃないわけで。そして、ask.fmにツッコミ機能を、なんて要望もソレに近い気がするわけですよ。

例えば、iPhone は全てをタッチパネルで操作するという制限があるけれど、タイピングが面倒だからキーボードをつけよう、なんてことをしたら、それはもう iPhone じゃないだろうと。

てなわけで、その手のWebサービスにしろ、デジタル機器にしろ、機能面の策定って、絶妙なバランス感覚が必要なんだろうなーとぼんやり思いましたとメモ。

議論に向いたサービスを生み出せる余地がありそうな気もする。 :

とにかく正しい状況把握をしたいなら、第三者からのツッコミを一切期待できない ask.fm なんて使うべきではないのだろう、てなことも思ったり。ask.fm を使ってる時点で、「俺の言うことに口を挟むな。俺が正しいんだ」という姿を見せたい、てな願望が回答者側にもビミョーにあったりするのかもしれないなと。 *1

これがもし、「間違ったことを言ってたら指摘よろしく」てなスタイルでやりたいなら、それに相応しいサービスがあったりするのだろうな。と思ったけれど、そんなサービスあるのかな。掲示板か、Wikiシステムぐらいしか思いつかないけど、掲示板は情報が分散しがちで閲覧者に厳しいシステムだし、Wikiシステムはそのままだとspam書き込みで壊滅するし…。blogのtrackbackも、本来は議論を深めるための機能だろうけど、spamで機能不全に陥ったし。

そう考えると、Web上で議論をしやすいサービスを今からでも発明できる余地があったりするのかなー、と思えてきたけど自分如きでは具体的なイメージが浮かんできませんです。そのうちアイデアマンがそういうサービスを生み出してくれることを期待。

/.Jのスコアという仕組みは、そういった試みだったのかしら。あるいは、2chのスレを転載してたまとめサイトも、結果的にはそういう仕組みに近づいてた気もする。ノイズを如何にして排除するか、どうやって議論のまとめを作り出すか、その仕組みがポイントになるのかしらん。考えたみたら、/.Jのスコアも、まとめサイトも、人力フィルタだな…。自動化したら、また違った展開が見えてくるのかな…。

*1: もっとも、件の事例は、それはそれでという気もしていて。日頃からそういうポーズを常に取れる人じゃないと、監督さんなんてやれるもんじゃないよなと…。周囲が何か言うたびにフラフラしてたのでは仕事にならない予感。それに、 _「監督とは問いに答え続ける仕事」 らしいし。件の事例は、その監督さんの訓練の場でもあるのかもしれないと思って眺めていたり。間違っていてもとにかく即座に答えるのが重要なのかもしれず。そのように捉えると、ask.fm が怖い場所に思えてきたり。精神的な鍛錬の場というか。修行僧の荒行というか。

#3 [anime] ナレーションについてもやもや考えたり

HUNTERxHUNTERというTVアニメを視聴し続けているのですが。蟻がどうこうの話になってから、ずっとナレーションが多用されていて、そこがなんだか気になっていたり。おそらく原作漫画がそういう作りなのかなと想像してますが。 *1

一般的に、映像作品においては、ナレーションの多用は避けるべき、という雰囲気があるように感じているのですけど。どうしてそれがNGなのか、自分には理由が分からんのですが。しかし、件のアニメを見ていると、ナレーションの多用が作品を面白くする方向に働く場合もあるのだなと思えてきて、その作りに毎回感心しているのでした。

件のアニメにおけるナレーションの担当は、バトル間の静寂時における登場人物の心理描写、あるいはバトル中のシミュレーション内容の補足になっていて。例えば…。

ナレーション 「キルアは、今までの戦いの中で感じたことのないプレッシャーを受けた」

てな感じの説明が入ったりする。これを絵で伝えようとしても無理があるわけですよ。

一応、プレッシャーを受けていること「だけ」を絵で伝えるのは可能ですけど。 そんなこんなで色々手はあるわけですが。しかしどう頑張っても、「今までの戦いの中で〜」という情報は全く伝えられない。そこが伝えられないと緊迫感が弱くなる。かなりのピンチであることが分からないから、お客さんがハラハラしてくれない。ナレーションを使わずにそこまで伝えたければ、人物に説明台詞として吐かせるぐらいしか思いつかない。

そこでようやく、自分は今頃になって、「ナレーションとは、文学的表現・詩的表現・時間情報の圧縮まで担うことができる役者の一人、登場人物の一人」であることに気づいたりしたわけですが。

視聴者に情報を伝達する際、言葉で説明したほうが分かりやすいものと、絵で説明したほうが分かりやすいものがあって。この見極めを間違えると、状況が上手く伝わらなくて、作品にのめり込んでもらえない。アニメにしろ実写にしろ総合芸術ですので、言葉だけに頼って説明するのもNGだし、絵だけに頼って説明するのもNGなわけで。両方活用できないならアニメで作る意味が無いよなと自分は思うわけですけど。

そして、言葉で伝える場合によくある手として、説明台詞を吐かせるわけですが。説明台詞ばかりだと、リアルから乖離するわけですよ。

「ナウシカは独り言が多いね」と時々揶揄されますが、独り言として説明させないと設定が伝わらないから仕方ない。だけどその分、不自然なシーンになって、そこを視聴者からツッコまれてしまう。ツッコまれるのは、視聴者が冷めた状態で見ているからで。のめり込んで見ているなら、ツッコまれないはずで。なので、あまり良い状態ではない。

対して高畑アニメは、ナレーションを頻繁に使う。それはおそらく、会話シーンの中に登場する説明台詞を、極力ナレーションへと追いやることで、シーンそのものは自然にしよう、リアルにしようと試みてるからではないのかと。高畑監督は、リアル志向ですから…。

また、ナレーションとは神の視点を持つ存在ですが、その存在がどこに位置しているかは作品によって異なるなと。HUNTERxHUNTERは、おそらくバトルをしてる人物たちのすぐ傍に居て解説してる、そんな距離感のように思えたり。高畑アニメは、人物達からもっと離れた高いところで語っている印象。宮崎アニメは、そもそもナレーションなんて役者は居ないし、入れるつもりもないように見えますが。

何にせよ、ナレーションも役者の一人なんだと捉えることができれば、どこに立たせて、どこからどこまで語ってもらうか、そのあたりも見えてきて上手に活用できたりするのかなー、どうなんだろうなー、てなことをもやもや考えてしまったので一応メモ。

そういえば、ラノベ原作アニメの場合は、モノローグがナレーション代わりになってるなと。ナレーションの多用と、モノローグの多用では、印象が違ってくるのだろうか…? ちょっとよく分からないな…。

バラエティ番組では、ナレーションが「天の声」などと呼ばれてキャラ化してたりしますわな。そういう使い方をアニメでも…。いや、タイムボカンシリーズあたりで既にやってた気もするかな…。

関係ありそうな記事をメモ。 :


*1: もっとも自分は原作未読なので、もしかするとアニメ版だけの作りかもしれませんが。

2014/05/06(火) [n年前の日記]

#1 [anime] キルラキル最終話1話前まで視聴

スゲー。とにかくスゲー。シチュエーションだけで燃えてくる。

それはそれとして。このアニメ、本編映像の品質というか、線の見え方が気になっていたり。なんだかぼやけてるというか、荒れているというか。解像度が低い状態で制作してアプコン?して放送しているのかな。それともBD版とTV放送版で画質を変えている? 綺麗な映像はBD版で見れますよ、てな付加価値をつけようとしてるとか。それとも、フィルムアニメに近い荒れた感じを再現しようと撮影段階で何かフィルタをかけているのか。あるいは、昭和アニメの粗い感じを再現するために太い鉛筆でゴリゴリ描いてるとか。何にせよ、他のアニメとは見た目が違う点が気になっていたり。理由があるのだろうとは思うのだけど。

2014/05/07(水) [n年前の日記]

#1 [anime] キルラキル最終回を視聴

ようやく見れた。いやはやスゴイ…。まさしくこれは、なんだかわかんないけど熱さが胸を焦がすアニメだったなと…。TRIGGERスゴイわ…。

2014/05/08(木) [n年前の日記]

#1 [anime] 「ゴムのバリアーだよ!」に感心

キルラキル最終回を見ていて感心したシーンがあったことを急に思い出したのでメモ。

電波塔を生命繊維が覆って 攻撃を跳ね返すシーンで、メガネをかけたインテリキャラが延々と仕組みについて解説するのだけど。途中でマコが「わあ。ゴムのバリアーだよ!」の一言でまとめちゃって、笑いながらも感心を。キルラキルにはそういうシーンが各所にあって、これも中島かずき脚本の技なのかしらと思って見てたのですが。

考えてみれば、見てるこっち側にとって、そこにある細かい設定なんて結構どうでもいいことだよなと。ただ、何も設定が無さそうに見えてしまうと、御都合主義を感じてしまって冷めてしまうので、ちゃんと設定はあるっぽいと少しは匂わせておかないといけなくて。かといって、延々と本気で説明しちゃうと、それもそれで見てる側は飽きてしまう。

キルラキルの上手いところは、本気で長々と説明するのかなと一旦思わせておきながら、「そろそろ見てる人飽きてくるかな?」と思える的確なタイミングで「要するに○○だよ!」とまとめてしまうあたりかなと。それって、脚本家、もしくはコンテや演出さんが、客がそのシーンをどう捉えているか想像しながら作っていないとできないことで。客の反応を予測して先手を打って見事に当ててるあたり、なんてことのないシーンのように見えるけど、かなりの技じゃないのかなと。

例えば、ラノベ原作のアニメなどは、本気で長々と説明するけどやっぱり設定がよく分からない、なんて場面が頻繁にあって。おそらく、そういった作品の脚本家さんは、見てる側はこのシーンをどう捉えるだろうか、てなあたりをさほど考えずに書いてるんだろうなと想像するわけですよ。神山監督が言うところの、「情報量のコントロール」ができてない状態とでもいうか。

もしかすると、中島かずき脚本のそういった部分は、演劇という、お客さんの反応をその場でチェックできる表現方法に接していたからこそ身に着いた技、だったりするのかな。とも思ったけれど、あまり関係ないのかな。どうなんだろう。

何にせよ、キルラキル、面白かったです。脚本も、演出も、作画も、技の満漢全席、だったような印象も。

2014/05/09(金) [n年前の日記]

#1 [nitijyou] 自宅サーバ止めてました

雷が鳴ったので、14:24-19:00の間、自宅サーバ止めてました。申し訳ないです。

天気予報では午後から雨が降ることになってたけれど、雷まで鳴るとは…。

#2 [anime] スポッティングシートについて

アニメ関係のページを眺めていたら、「スポッティングシート」なる用語を見かけて、「ナニソレ?」と。なんでも、OP・ED映像を作る際に渡されるもので、どの時間にどんな音が鳴っているか書かれているものらしく。…そんな虎の巻があったのか!

ググってみたら、色々知ることができた。用語を一つ知るだけで、こんなに情報が…。用語をポロッと紹介してくれた方に、改めて感謝…。

_スポッティング - Wikipedia
_スタジオ近況報告:アニメ制作あれこれ・スポッティング編
_アニメ残ギャグ物語(TV『地球へ…』OP秘話+α) - Togetterまとめ
_板垣伸のいきあたりバッタリ!第295回 アニメ『てーきゅう』の作り方(8) | WEBアニメスタイル
_ふしぎ星の☆ふたご姫 Gyu! オープニングの事 その12: 種と仕掛けのコラムノート2
_妄想の十「笑う人、まどろむ人」 -その1- - KON'S TONE
_WEBアニメスタイル | β運動の岸辺で[片渕須直]第23回 ABCは知ってても

まとめると…。 OP・ED曲に映像を合わせる作業は演出家さんがやってるのかなとずっと思い込んでいたけれど。実はそのあたりの担当は編集さんだったと知り、「そうだったのか…」てな気分に。編集さん、スゴイ。さすが時間制御のプロ。

しかし近年、デジタル制作のための機材が普及したことで、やっぱり演出家さんがやるようになってきた・作業分担の範囲に変化が出てきたあたり、なんだか興味深かったり。道具の存在によって色々変わるんだなー、道具の有無って大事だなー、と。おそらく今後も、違う道具が普及することで、作業分担が変わったりするんだろうなと。

スポッティングシートはいつからあるのだろう。 :

どの時代からそういうシートが出始めたのか気になったのだけど。
  • 今敏監督の、「妄想代理人」制作メモ内に、「スポッティングシート」という単語が出てくるので、少なくとも2003年頃には「あるのが当たり前」になっていたらしい。
  • 片渕須直監督が、テレコムで「リトルズ」(1983-1985年)を作ってた頃の回顧録中に、「スポッティングシート」という単語は無いものの、「スポッティング」という単語が出てきている。そこから想像するに、どうやら1980年代ですら似たようなものが存在していた可能性がありそう。
  • そもそも、オープンリールテープを使って作ってた時期があるらしいから…。
てなわけで、どうも昔から存在してたらしいなと。

もしかすると、スタジオによって呼び方が違っている可能性もあるのかしら。

スポッティングシートは演出家さんが作るべきだろうなと。 :

色々眺めてるうちに、スポッティングシートは本来演出家さんが作るべきだよなー、と思えてきたり。

どこでどんな映像を流すかは、演出・コンテ担当者しか分からないし。編集さんにシート作成をお願いすると、演出家さんが使いそうなタイミングを勝手に想像しながら全部羅列する羽目になりそうだなと。だけど、実際に使われるのは、その中の極一部だろうと。どう考えても無駄な作業が発生しそう。

演出家さんが直接作業すれば、本当に必要な最低限のタイミングだけを抽出することができる。無駄が発生しない。効率がいい。間違いがない。

もっとも、PCが使えないオールドタイプの演出家さんも居るだろうから…。そういう場合は編集さんにシート作成をお願いすることになるのだろうけど。その際も、どこのタイミングの時間が欲しいのか指示しておけば、無駄な作業は発生しないだろうなと。

ということで、「スポッティングシートぐらい用意しろよ」と怒り出す演出家さんが居たとしたら、なんだかちょっと危ないなと。トータルで見て無駄な作業を無意味に増やしてしまうことに危機感を持たないタイプじゃないかしら。正確には、自分の担当範囲では無駄な作業を嫌うくせに、別作業の担当者には無駄な作業を強要して、しかしそれをなんとも思ってないタイプというか。

対して、おそらく、デキる演出家さんは…。
  • ストップウォッチ片手にさっさと作業を始めて、見事な神業を披露。
  • 「PCとソフト貸して。こっちで調べるから」と、ニュータイプぶりを発揮。
  • 「ココとココとココの時間だけシートにして」と、実に監督らしく的確な指示を出してくる。
  • 「編集さんのほうでイイ感じに合わせちゃって」と丸投げ。ていうかスタッフの力を完全に信頼。
といった人達だったりするんだろうか、などと勝手な想像を。

なんでもそうだろうけど、苦労が成果物にちゃんと反映されると信じているから、ツライ作業もこなせるわけで。無駄な作業をさせられたら、誰だって怒るし、やる気も下がるし、「アイツとは仕事したくない」と思われちゃうよなと。なので、こういう部分でも、無駄は減らせるなら減らせたほうが望ましいのだろうなと。

などと書いてしまったけれど。自分もなんだか色んな人に改めて謝罪したくなってきた…。自分が無駄な作業をしちゃったこともあるけど、自分が無駄な作業をさせちゃったこともあるので…。

それはそれとして、ふと思ったのですけど。もしも各カットの長さが頓珍漢な状態で上がってきたら、最後に苦労するのは編集さんだから…。「あーもう、俺の方で事前に長さ測っておくからコレに合わせろ」てな経緯で編集さんからスポッティングシートが出てきたのではないかと邪推したのですけど、実際はどうなんでしょうかね。

#3 [anime] 「ダイショーグン」なるアニメが気になっていたり

夜中に、「風雲維新ダイ☆ショーグン」なるアニメが放送されているのですけど。なんだか最近気になっているのです。

全然動かないんです。ほとんど止め絵なんです。なのに、どのカットもカメラワークが頑張っていて、動いてるように見えちゃうんです。しかも見ていて結構笑える。なかなか面白いのです。

コンテ・演出の実力を見せつけられてる気がします。毎回、話の展開に笑いながらも、「これはこれでスゴイぞ…」と感心しております。アニメとはなんぞや、映像作品とはなんぞや、アニメとして成立する肝は何なのか、考えさせてくれる作品だなと…。「アニメは動いてナンボ」とよく言われるけれど、それって本当にそうなの? 記号にしかなってない動きならそもそも要らないんじゃないの? 件の作品を見ていると、そんな疑問すら湧いてくるのです。

時々思うのだけど、どんな見せ方も小馬鹿にしたりせず本気で取り組めばモノに出来るんだなと…。「ダイ☆ショーグン」も、何がどうとは具体的に言えないけれど、おそらく結構モノにできてる作品、のような気がします。勉強になるなあ…。何の勉強だかわかんないけど。

2014/05/10() [n年前の日記]

#1 [dtm] フリーの波形編集ソフトとスポッティングシート作成

今時はPC+波形編集ソフトでスポッティングシートを作るという話を知って、フリーの波形編集ソフトでもそういう作業はできるのかなとちょっと気になったので試してみたり。Windows環境で、 _SoundEngine Free_Audacity を使ってテスト。

SoundEngine Free は…。 ただ、各マークにラベルをつけることはできないので、どこでどんな音が鳴ってるかをメモするのは難しい感じ。

Audacity は…。 ただ、ラベル位置から何度も音を再生し直して位置が合ってるか確認するのはちょっと面倒な感じ。また、ラベル位置を変更する時にミスをして、何度もうっかり選択範囲状態のラベルにしてしまった。機能面はバッチリだけど、結構操作しづらい印象。

てなわけで、フリーで使える波形編集ソフトでも、曲を再生しながらバシバシとキーを叩くだけで、リアルタイムで時間を測定できそうだなと。

出力されるテキストファイルには時間しか書いてないので、映像制作に使う場合は24・30コマ/秒に変換しないといかんのだろうけど。そのあたりは Perl、Ruby、Python あたりで数行スクリプトを書けば ―― 下手するとワンライナーでも変換できそう。ただ、24・30コマ/秒に変換した際の誤差をどう扱うかで悩みそうな気もする。

Wavosaurも試用。 :

_Wavosaur - 窓の杜ライブラリ

他のソフトのほうが使いやすかったりしないかなと思えてきたので、試しに Wavosaur を試用。
  • Mキーを押すとマーカーをつけられる。
  • TABキーで次のマーカー位置、Shift+TABキーで前のマーカー位置に再生開始位置を動かせる。
  • Tools → Slicing/Region → Set maker at * で、BPM指定その他で自動でマーカーをつけられる。
ただ、マーカーをテキスト等で保存する方法が分からず。BPM(Beats Per Minute)の計算ができるあたりはヨサゲなのだけど。

2014/05/11() [n年前の日記]

#1 [cg_tools] タイムシートの標準的なフォーマットってないのかな

時間その他がずらずら羅列してあるテキストデータを元にして、.fla(Flashの編集用ファイルフォーマット)を作れないかなー、と夢想したのです。

色々ググってみたのだけど、なかなか難しいなと。

で、そのあたり調べてるうちに、タイムシートの標準的なフォーマットってないのかなー、と思えてきたわけで。

そういうフォーマットが存在すれば、AfterEffect や RETAS や Flash CS* や AviUtl やその他諸々の映像編集ソフトでインポートして云々、なんてこともできるようになるのかもしれないなと。何かツールを作る時も、ひとまずそのフォーマットに対応しておけば楽になるのだろうなと。DTMで言えばMIDIファイル、3DCGで言えば .dxf みたいなもんで。そういうフォーマットは無いのかなと。

まあ、ググってみたけど、やっぱりそんなフォーマットは無いようで。一般的に使われるものではないから、なかなか登場しないのだろうな…。

flaファイルについてググって分かった範囲をメモ。 :

少しググってみたけれど。
  • .fla の中身がさっぱり分からず。.fla はバイナリ形式の上に、フォーマット仕様も公開されてない。解析作業とかやってられない。
  • 最近は、.fla をxml化してzipでまとめただけの .xfl というファイルフォーマットもある。これも資料が少ない・ほとんど公開されてない。
  • テキストデータ(.xml)から .swf(Flashの再生用ファイルフォーマット)を作ることはできる。ming や swfmill を使えばいい。
  • しかし、.swf は、Flash CS* 等で編集できない。編集するためには、.swf から .fla を作る、デコンパイラが必要。
  • そもそも、Flash CS*でしか読み込めない .fla や .xfl を作ってもアレかなという気もしてきた。フリーで使えるFlash作成ソフトの、ParaFla や Suzuka で読めないと、使い道が限定されそう。
ということで、色々難しそうです。

タイムシート関連のフォーマットについて調べたり。 :

RETAS のタイムシートは、.tsf ファイルとして保存される模様。 _バージョンがいくつか存在する らしく。.tsf はバイナリファイルで、中身をそこそこ解析した方もいらっしゃるのですけど…。

_OpenSpace Info:RETAS!(レタス)のファイル解析 - livedoor Blog(ブログ)
_OpenSpace更新情報: RETAS! tsf format

うむ。何がなんだか分からん…。

IG作品その他で、AE(AfterEffect)を活用してきたことで有名な江面久氏は、 _TSフォーマット なるものを策定して、仕事でも使ってるらしいのですけど。ただ、いくらググってもその正体が分からず。おそらくスタジオ内、もしくは御当人しか使わない、門外不出のフォーマットなのかなと。

アニメ制作現場におけるデジタル撮影についての第一人者であろう、ねこまたや氏も、りまぴんというツールを通じて xpsフォーマットを作られたようで。ただ、これも、フォーマット仕様が分からない。フォーマットについての説明ページが404だったり。

PC用アニメ映像制作関連ツールを多々公開しているねこら氏が、 _タイムシートレンダラ(tms2avi) というツールを通じて、.tms というフォーマットを作っていたようで。ただ、このフォーマットも、ネット上で全て公開されておらず。概要ページは開けるけれど、そこから先が存在しない。

てなわけで、どのフォーマットも、これを普及させて色んなツールでインポートできるように〜てな展開は期待できない・フォーマット仕様すら分からない状態のようで。まあ、そういったフォーマットを作って公開しても、結局は Adobe が乗ってこないと普及しないので公開する意味も無いのだろうなと思うのですが。

画像編集ソフトも、3DCGソフトも、編集用ファイルフォーマットは各アプリの機能とがっちり絡んでしまっているから、標準化できない・仕様を公開しないのが常ですし。下手すると、そのフォーマットをサポートした他社アプリに対して妨害行為をするためにファイル仕様を変更することすらあるわけで。おそらく、タイムシートも同じなんだろうな、と思えてきました。

_OpenDocumentフォーマット に比べると、なんだか不自由だなと。Office関連アプリと違って、画像だの3DCGだの動画だのは、利用される場面が少ないことが影響してるのかしらん。規格を作っても、サポートしても、「コレ、ホントに使う人居るのかな?」という疑問がついて回りそうだし。なかなか難しいよなと。

2014/05/12(月) [n年前の日記]

#1 [nitijyou] 自転車で買い物に

親父さんの電動自転車を借りて以下略。もう電動自転車じゃないと走る気にならないです。人力オンリーはツライ。須賀川市は坂が多いので。

カワチで夜食その他を購入。帰る頃にちょっと雨が降ってきて危ないところだった。

カワチのレジ周辺に初音ミクが描かれたポスターが2枚ほどあって。1枚は赤い羽根がどうとか。もう1枚は、高校の吹奏楽の発表会がどうとか。たぶんその高校の学生さんが描いたのかなと思うけど。初音ミクって若い人に人気あるんだな、スゴイなー、と再認識です。

#2 [neta] 世間様は漫画に何を求めてるんだろう

「美味しんぼ」の原発回があちこちで話題になってるらしいのだけど。個人的には、世間様の反応のほうが、怖いというか異常だなと思えていて。

「野望の王国」の原作者に一体何を求めてるんだ。馬鹿馬鹿しい…。以上。

もうちょっと愚痴る。 :

「美味しんぼ」って、今までも結構なトンデモを書いてきた漫画じゃないですか。料理をネタにした、ほんわか絵柄の「MMR」みたいなもんでしょ。「な、なんだってー」と反応してあげるべき場面で、マジ顔で「けしからん」と怒り出すなんて、なんたる無粋。「ああ、またか」「相変わらずだなー」とニヤニヤしながら読むのが妥当じゃないのかなあ、と。

それとも世間様は、あの漫画には事実しか書いてないと信じ切って読んでたのかと。もしそうなら怖すぎる。頭おかしいんじゃないの? あの原作者をなんだと思ってんだ。まともな知識人として見ているのだとしたら、それはある意味あの御方に対して大変失礼過ぎますぞ! いや、失礼なのは自分か…。

そもそも、漫画なんてもんは、基本的に *1 作者のトンデモ妄想が描かれてるメディアじゃないですか。例えば、今では名作扱いされてる手塚治虫の「ブラックジャック」だってトンデモばかり描いてありますよね。漫画≒トンデモ、なわけですよ。

しかし、トンデモを堂々と描いて、ゲラゲラ笑ったりドキドキしたり考え込んだりできちゃうのが漫画のいいところ。…そこを叩いてどうするの。否定してどうするの。世間様は漫画を何だと思ってるんだ。漫画を過剰に崇高なものと思い込んでないか。だとしたら危ないよ。ソレってヤバいよ。と思うわけで。

「漫画には本当のことだけを描け」と要求するのではなく、「漫画にはトンデモばかり描かれてるから本気にするなよー」と読み手側に啓蒙していく方が大事だと思うのですよ。漫画に書かれたことを事実と思い込んでる読者が仮に居るとしたら、そっちのほうがマズい。その漫画が無くなっても別の何かに騙され放題じゃないですか。

てなわけで、漫画内のトンデモを叩き潰していけば問題解決と思ってる人達がどうやら存在するらしいあたりが、社会にとってははるかに問題かもしれんなあ、などと、おそらくはズレてることを思いながら、その手の話をチラチラ眺めているのでした。

なんだかロリコンエロ漫画規制の話を思い出したりもして。その漫画を無くしたら問題解決、では無いわけで。世間様はちと短絡的ではないのかなあ。

それはともかく個人的には、お爺ちゃんの鼻血より、子供の甲状腺がんの話のほうが気になるのですけど…。どうせ騒ぐならそういう方面で騒いでほしい気もしたり。 *2

*1: あくまで基本的には。例外も多々ありますが。
*2: ググってみたけど、福島県以外でも調査したデータが出てきてたのね…。一応福島県民を、モルモット程度の扱い = 他と比較して影響の有無を考えられる状態にはしてくれるようで、少しホッとしたり。まあ、「福島県は安全」という結論を導きやすいようにデータを集めてるという話も見かけたのでアレですけど。なんにせよ、チェルノブイリでは事故後4〜5年後から増加が見られたらしいので、今後も検査を続けて観察していかないといかんのだろうなと。

この記事へのツッコミ

Re: 世間様は漫画に何を求めてるんだろう by 名無しさん    2014/06/19 21:03
個人的には実名を挙げているから問題になったのだと思いますよ。
これが架空の被爆都市ネオフクシマを舞台にしてたとしたら体のどこから血が噴き出そうとおっしゃる通り目くじらをたてるべきではないと思いますが。
やっぱりよくよく考えてみると事実誤認をさせてだれかに直接迷惑をかける行為は文章で表現してもダメだし映画で表現してもダメだし、やはり漫画で表現してもダメなんじゃないでしょうか。

2014/05/13(火) [n年前の日記]

#1 [anime][neta] アニメにおける3DCGって第二原画相当だったりしないのかな

あちこちのアニメ制作関連記事を読んでいたら、3DCGで人体モデルを動かす時は、手描きアニメーターさんがラフな動きを描いて、それをトレースする形で動かすことが多い、てな話を見かけたのです。

ということは、もしかすると…。手描きアニメーターさんが第一原画で、3DCGアニメーターさんが第二原画、みたいな状態と言えるのかなー、と思えてきて。

仮にそういう捉え方がそこそこ合ってるのだとしたら…。

手描きアニメーターさんの中には、3DCGは敵だ、俺達の仕事を奪う存在だ、みたいなことを仰る方も居るのだけど。これが手描きアニメなら、第一原画の方が第二原画の方を敵扱いはしないだろうと。しかし3DCGという道具が入ってくると、似たようなことをしていても、急に敵扱いされちゃう。なんだかおかしい話だなと。

更に、おそらく3DCGアニメーターにも、第二原画相当の作業をしているけれど、手描きアニメーターで言うところの第一原画レベルの方、第二原画レベルの方、動画マンレベルの方が混在してるんじゃないのかなと。しかし、そういう分類を意識しながら、各人の能力評価をして仕事を割り振っているのかどうか、てな疑問が湧いてきたり。手描きアニメーター相手なら、「この新人は、今、このレベル」「この人はベテランだからここまでお願いできる」と適切な判断・評価ができていても、3DCGアニメーター相手になると「3DCG」で一括りにしちゃって、過大評価や過小評価をしたまま仕事をお願いしちゃって、「3DCG使えねえ」と言ってたりしないのかなと。 *1

欧米の手描きアニメでは、動きを意識したラフ原画を描く人と、原画を清書する人に分かれて作業してたという話も思い出したり。ディズニーの有名な原画マンを日本に読んで実演してもらったら、全部ラフ絵だった、てな話があったような気も。そういう分担を意識した3DCGの導入は、アリなのかどうか、みたいな。

「機械を使って清書してくれる人達」とか「機械を使って中割りしてくれる人達」とか、手描きアニメで言うところの○○の部分を代行してくれる存在、みたいな感じで捉えることができたら、もうちょっと何かが…。いや、どうなんだろう。わかりませんが。

とりあえず、手描きアニメーターさんが3DCGを敵視してるのは、何か違うんじゃないかなと思えてきた、とメモ。敵視されるべきは3DCGじゃなくて、使われ方、制作システムへの投入の仕方じゃないのかと。
*1: もちろん、3DCGをずっとやってるスタジオ内では、各人の作業能力・得意分野を適切に見極めて作業分担してるはず、と思うのですけど。

2014/05/14(水) [n年前の日記]

#1 [cg_tools] Suzukaが不正終了する

無料で使えるFLASH作成ソフト、 _Suzuka 0.8.1.0 (SonEditX版) を、Windows7 x64上で使おうとして、ちと悩んだり。一応起動はするのだけど、昔作ったらしいプロジェクトファイル(.csf)を開こうとすると不正終了してしまう。全てのプロジェクトがダメというわけではなく、開けるプロジェクトもある。さて、なんでだろ。

_RegisterSonEditX は管理者権限で実行して、レジストリに登録できてるはず。SonEditX が原因というわけではないのだろうな。

ググっていたら、どうも mp3 を含んだプロジェクトを Windows7 + Suzuka で読み込もうとすると落ちる、という話が。もしかすると、自分が過去に作ったそのデータは、mp3 を含んでいるのかもしれない。同フォルダに .mp3 もあるし…。

mp3を読むと落ちる問題は、Suzuka.exe のプロパティで互換性設定を有効にして、Windows98/ME を選ぶと解決する場合もあるそうで。しかし、自分の環境でソレをすると、Suzuka のファイル選択ダイアログ等が表示されなくなってしまう。

Suzuka には SonEditXを使わない リッチエディット版とやらもあるらしいので、そちらも試してみた。が、やはり落ちる。うーん。

仕方ないので、XPモードで Suzuka をインストールして開いてみたらすんなり開けた。やはり mp3 を含んだプロジェクトファイルだった。しかし、困ったな。Windows7上では mp3 を含んだプロジェクトを作れないのか…。

#2 [dxruby][cg_tools] swfmillを試していたり

以前、「DXRubyに動画再生機能をつけてほしい」という要望を目にした際に、「そんな機能、必要なのかな」「せっかく60FPSで画像をグリグリ動かせるライブラリなのだから、リアルタイムで動画っぽく見せたほうがよかったりしないか」と思ったのですけど。しかし、再生用の元データをどうやって作るかで悩みそうだなと。

そこでふと、FLASH関連ツールを使えないかなとひらめいたのです。

_ParaFla_Suzuka 等、無料で使えるFLASH作成ソフトで .swf を作って。

_swfmill で swf → xml 変換。Ruby なら xmlの解析もしやすいだろう、おそらく _Nokogiri とやらを使えそう。みたいな。

てなわけで、少し実験。

まずは、Suzuka で作った .swf を swfmill で xml にしてみようと。以下のような感じで xml に変換。
swfmill swf2xml hoge.swf hoge.xml

変換後の xml を眺めてみたのだけど。base64らしい文字列がずらりと並んでるのが気になって。.swf に埋め込まれた画像データが、そのまま xml に吐き出されているのだろうなと。画像は別にして保存できないのかな。せめてファイル名でも書いてあれば…。いや、難しいか。swf の再生時は、ファイル名なんか必要としないので、含まれるわけがないよな。

とりあえず、想像だけど。

2014/05/15(木) [n年前の日記]

#1 [cg_tools][prog] Blender 2.70aをインストール

Blender 2.70a を Windows7 x64 上にインストール。 _GraphicAll.org から、 _Blender 2.70.5 - Fastest build: CUDA + OSL - GraphicAll.org の 11138_blender-2.70---a8eb95c---cuda-and-osl---fastest.7z をDLして解凍。

最初の起動時、スプラッシュ画面に、古い版の設定をコピーするリンクが表示されているのでクリック。

ウインドウ → システムコンソール切替、でコンソールウインドウが表示される。眺めたら numpy が無いと表示されていた。対処できないものかなと。

Python 3.4 をインストール。 :

_Our Downloads | Python.org から、python-3.4.0.msi をDLしてインストール。

_Numerical Python - Browse /NumPy/1.8.1 at SourceForge.net から、numpy-1.8.1-win32-superpack-python3.4.exe をDLしてインストール。

_Blender2.62でnumpyを使う。 - koropicot's blog を参考に、Python 3.4インストールフォルダ\Lib\site-packages\numpy フォルダを、blenderインストールフォルダ\2.70\scripts\modules\ 以下にコピーしてみたのだけど。相変わらず blenderのコンソールウインドウには numpy が無いと表示されてる…。Pythonコンソールにして、import numpy をしてもエラーが出る。どうすりゃいいんだろう…。うーん。

調べてみたら、blender 2.69 でも numpy が無いと表示されてた。だけど今まで特に問題無く使えてたわけで。だったら、このままでもいいかな…。

#2 [python] Python 2.7.6にアップデートしたつもりがアップデートしてなかった

_Our Downloads | Python.org から、python-2.7.6.msi をDLしてインストールしたつもりだったけど、元々 Python 2.7.6 が入ってたことに後から気がついた。何をやってるんだ、自分は。

ちなみに普段は GIMP 2.6.x を使う関係で、Python 2.6.6 を使っているのですけど。GIMP 2.6.x は Python 2.6.x じゃないと Python-fu が動かないので。

特定アプリに、特定バージョンのPythonをセットにして、動かす仕組みとかないのかなあ。最近の Python はアプリと一緒に同梱できるらしいので、最近のアプリは特に問題無いけれど。昔の Python を要求してくる古いアプリを使う際には困る。

2014/05/16(金) [n年前の日記]

#1 [cg_tools] ImageMagickをインストールし直し

ImageMagick 6.8.7 Q16 x86 static 版をアンインストールして、ImageMagick-6.8.7-8-Q16-x86-dll.exe をインストールし直し。

RMagick をインストールしたいと思ったのですが…。 _RMagick を Windows 7 にインストールする方法 - 君の瞳はまるでルビー - Ruby 関連まとめサイト によると、RMagick を Windows にインストールする際は ImageMagick の特定バージョンが必要だそうで。Image Magick 6.8.7-8 までならインストールできると書いてあったので、一応その版を入れておこうかなと。

#2 [ruby] Rubyでswfを解析できるらしい

ググっていたら、swf を Ruby で解析するためのライブラリがあったようで。

_SwfRuby - SWFバイナリを直接操作して構造解析/リソース置換するためのRubyライブラリをリリースしました
_swf_ruby | RubyGems.org | your community gem host
_tmtysk/swf_ruby - GitHub

ただし、RMagick が必要…。

Windows環境の場合、 _RubyForge: RMagick に、rmagick-win32 というライブラリがあるのだけど。これは Ruby 1.8.6 までしか対応してなくて。ということで、swf_ruby を使いたくても要求ライブラリをインストールするところでハマりそうだなと。

swfmill を利用する _swfmill_ruby というライブラリもあるらしいのだけど、これも RMagick を要求する。厳しい。

#3 [cg_tools] HugFlashを使ってswfから画像を取りだしてみた

swfmill で swf から xml に変換すると、中に base64文字列がずらずら並ぶ。おそらくは画像ファイルのデータだと思うのだけど。

それぞれの base64文字列を別ファイルにコピーして、base64 からバイナリに変換してみたのだけど。元々jpegファイルだったデータは、画像ビューワで開けるものの。元々 RGBA のpngだったデータは、ビューワで開くことができなくて。どうも png として扱うために必要な情報が欠落しているらしい。

_Flash SWF Spec - Flash SWF Spec を眺めて、swf内でどういう扱いをされてるデータなのかを予想。jpeg は、 _DefineBitsJPEG2 で記述されてるらしい。RGBAのpngは、 _DefineBitsLossless2 で記述されているのだけど、画像サイズやインデックスカラー等の情報が別に記述されてるようで、それらの情報も反映しないと画像ファイルとしては扱えないのだろうなと。

swfから画像ファイルの形で取り出すことはできないのかなと思ったけれど、 _HugFlash というツールがあったことを思い出した。起動して、swf をD&Dしてみたところ、IDをファイル名にした状態で、画像ファイルを取り出すことができた。

ということは…。HugFlash を使って画像ファイルを取り出した後、swfmill で変換した xml の中から、base64部分をごっそり削って、「この部分は "ID番号.jpg" だよ」みたいな記述で置き換えれば、xml はスッキリした形になりそうな予感。

2014/05/17() [n年前の日記]

#1 [dxruby][cg_tools] swfmillの出力したxmlをRubyで読んで試していたり

swfmill で、swf → xml 変換したモノを、Ruby + Nokogiri で読み込んで色々試しているところ。

とりあえず、DXRuby で、位置、拡大縮小率、不透明度を再現できたけど。回転が分からない…。xmlには、skewX、skewYという属性が書かれているのだけど、どういう意味を持った値なのかがさっぱり分からず。

例えば、90度回転した時のxmlが以下で。
      <PlaceObject2 replace="0" depth="16383" objectID="2">
        <transform>
          <Transform scaleX="0.0000000000000000" scaleY="0.0000000000000000" skewX="1.000000000000000" skewY="-1.000000000000000" transX="8980" transY="1560"/>
        </transform>
        <colorTransform>
          <ColorTransform2/>
        </colorTransform>
      </PlaceObject2>
1度回転した時のxmlが以下。
      <PlaceObject2 replace="1" depth="16383">
        <transform>
          <Transform scaleX="0.9998474121093750" scaleY="0.9998474121093750" skewX="0.01745605468750000" skewY="-0.01745605468750000" transX="8980" transY="1560"/>
        </transform>
      </PlaceObject2>
scaleX と scaleY も指定されてるあたり、両方組み合わせて回転してるのかなという気もするけど…。

とりあえず、swfで出力された際、制御点の位置情報は、表示位置その他に反映された状態で値が出てくるあたりは助かった。これなら、表示側で一々計算しなくて済む。マジ助かる。

HguFlashの出力するpngのガンマ値について。 :

DXRuby 上で HugFlash が出力した png を読み込んで表示させたら画像が白っぽくなってしまって。なんでだろうと。

ガンマ値の問題だった。元画像は Gamma: 0.454545 = 1/2.2 だけど、HugFlash が出力した png は Gamma: 1 だった。

DXRuby の画像ロードは、png のガンマ値も反映してくれるのか…。ありがたいけど困る…。png の gAMAチャンクを削除するツールを使わないといかんなと。

ちなみに、ガンマ値を調べる作業には、ImageMagick の identify を使用。
identify -verbose hoge.png

1ファイルずつpngを開いてgAMAチャンクを削除しても構わないなら、 _TweakPNG なるツールが使えるらしい。でも、複数のpngに対して一括して処理したいところ。

_PNGGauntlet という、pngを最適化してファイルサイズを小さくできるツールを使えば、複数のpngに対し、一括してgAMAチャンク削除もできる模様。

と、ここまで書いたところで知ったのだけど。HugFlash にそもそもガンマ値を指定する設定項目があった…。そこが「1」になってたから、ガンマ値が1になって出力されたらしい。「1/2.2」を選んでおけば良かったのか…。

2014/05/18() [n年前の日記]

#1 [dxruby][cg_tools] swfmillの出力について

Flash CS5 でパブリッシュした swf を swfmill で xml にしてみたら、ちと気になる動作が。途中に、<UnknownTag id="0x08"> というタグが出てきてしまう。Suzuka で書き出した swf では、こんなことは起きなかったのだけど。

_SWFTOOLS の swfdump で swf を読んでテキスト出力させてみたり。以下のように指定。
swfdump.exe -D hoge.swf > hoge.txt

swfmill と swfdump の出力結果を見比べてみたけど。どうやら JPEGTABLES なる命令が入ってると、swfmill の出力がおかしくなる模様。

jpegファイルは使わない・jpegで書き出さないように制限することで誤魔化そうかな…。それとも、swfmill じゃなくて swfdump の出力結果を使って再生するようにしてしまおうか…。

DEFINESPRITE なるタグが入ってるあたりも気になる。ビットマップ画像の表示しかしてないのに。トゥイーン指定をすると MovieClip が作られてしまう、のかな。

DEFINESPRITE は、内部にもタイムラインが入ってるので、どうしたもんか。

2014/05/19(月) [n年前の日記]

#1 [dxruby][cg_tools] swfmillの出力したxmlを表示するテストをまだやってたり

skewXとskewYの値が何なのかは分かってきた。Matrix情報だったらしい。以下が参考になりました。ありがたや。

_FN0808001 - Matrixクラス - Flash : テクニカルノート

以下のような式に当てはめて使えるらしい。
x = (x * scaleX) + (y * skewY) + transX
y = (x * skewX) + (y * scaleY) + transY

DXRuby には矩形の4点を指定して変形描画する Window.drawMorph というメソッドがあるので、上記の式で4点を変換して表示してみたところ、回転も変形も、見た目それっぽくなってくれた。

DEFINESPRITE はどうしたもんか…。とりあえず、タイムラインを内包するクラスを作らないといかん気がしてきた。xmlのノードを渡したら、配列やハッシュにタイムライン情報を記録するメソッドを作って試していたり。これをスプライト相当のクラスが持てば、どうにかなるんじゃないかな…。

#2 [cg_tools] Sweet Home 3Dを試用

_SweetHome3Dでのまんが背景作成手順 - Togetterまとめ というページを目にして興味が湧いたので、 _Sweet Home 3D を試用してみようかなと。

インストール時に hao123 をインストールされそうになる…。コイツはやべえ…。いや、チェックを外してインストールを拒否すればいいんだけど、うっかりポンポンとクリックしていくと酷いことになる予感。

Sweet Home 3D自体の操作は、結構簡単な感じ。静止画作成時に「球体」を選ぶと、360度をカバーした画像が得られるあたりはなんだかイイ感じかもしれず。もしかするとコレを使って環境マップ?画像を作れるかもしれない。

2014/05/20(火) [n年前の日記]

#1 [dxruby][cg_tools] Flash CS5の出力するswfが結構違う

Flash CS5上で、pngをライブラリに登録して使ってるのだけど。swf出力した際に、DefineBitsLossless と DefineBitsLossless2、ClippedBitmap とClippedBitmap2 が入っているようで。おそらく、アルファチャンネルが必要な画像と不要な画像で分けているような予感。

Nokogiri の xpath で両方取りたい場合は、doc.xpath(".//ClippedBitmap|.//ClippedBitmap2") みたいな書き方をすればいいらしい。

DefineShape 中の ClippedBitmap について、<ClippedBitmap objectID="65535"> というタグが出てきて首を捻ったり。objectID="65535" って何だろう…。ググってみても正体が分からず。

DefineSpriteでハマった。 :

DefineSprite の中にタイムライン情報があるのだけど、再生されるようにソースを書いたつもりが全く表示されず。

色々調べてたら、PlaceObject2 が replace="1" の属性を持っている時にも objectID が指定されていて。replace="1" の時は depth だけが指定されて、そこに既にあるオブジェクトの位置その他が変更されるものと思い込んでた。objectID が指定されている=別のオブジェクトと差し替えろ、と指定されてる場面もあるのだな…。

#2 [nitijyou] 自転車で買い物に

明日は雨になるらしいので、今のうちにと親父さんの電動自転車を借りて以下略。リオンドールで夜食その他を購入。

2014/05/21(水) [n年前の日記]

#1 [dxruby] swfを作成中

DXRubyでそこそこswfが再生できるようになってきたので、動作確認するためのサンプルswfを Suzuka と Flash CS5 で作成中。

キャラクターがぐるっと回る画像があったほうが、スプライト(MovieClip)の再生テストになるかなと思えてきたので、MMD(MikuMikuDance)を使ってそういう画像を作ってみようと。コーデックとして Ut Video Codec を選び、RGBA のaviとして保存。AviUtl + PNGエクスポータープラグインを使って、連番png として保存。

G-Tuneちゃんなるキャラがデビューしていたらしい。 :

MMDを使う際に困るのは、モデルデータ。どれもライセンスが厳しくて。

モブ子さん以外に自由に使えるモデルデータは居ないのかしらとググってみたら、つい最近、 _G-Tune 公式オリジナルキャラクター G-Tuneちゃん がデビューしたと知ったわけで。マウスコンピュータの G-Tuneシリーズ ―― 比較的強力なスペックのPC製品群の公式キャラ、らしい。

利用制限として、宗教や政治関係で使うのはダメ。イメージ悪化もダメ。その他色々ダメ。また、G-Tuneちゃんの名称と公式サイトへのリンクをどこかに書くことを義務付け、という感じだけど。それでも、条件を満たせば、商用利用可、改造可、使用報告も不要、てなあたりは太っ腹で素晴らしいなと。要するに、マウスコンピュータやG-Tuneシリーズを宣伝してくれるなら使ってOK、ということかしら。

せっかくこういったモデルデータが公開されたのだから、今回は G-Tuneちゃんにモデル役を頼もうかなと。てなわけでDLして、カッコイイポーズを取らせようと四苦八苦していたり。

Depth情報でちとハマった。 :

今まで単純に、Depth値 = Windows.draw() 時の z値として使ってたけど。スプライトの中にスプライトがある、てな場面では、z値が適切に割り当てられないなと。そういう場面は少ないだろうと踏んでたけど甘かった。少し凝ったアニメを作ろうとすると、入れ子は必須だろうし。

結局、z値は指定せず、Depth値の配列をソートして、その順番で処理をすることで描画順を決める、てな感じにしたり。ソートの処理時間がちと怖い気もするけど、数が少なければ、たぶん、どうにか…なってくれるといいなあ。

シェイプ内のビットマップ画像もMATRIXを持つらしい。 :

Flash CS5 で出力した swf が正常に再生できず。シェイプ内のビットマップ画像関連タグを眺めたら、各ビットマップ画像の Matrixタグ内の属性が、それぞれ標準値とは違っていて。これも反映させないとダメか…。

となると、スプライトもシェイプも、MATRIX を持ってることになるから…。画像、シェイプ、スプライトを似たようなクラスで保持したほうがスッキリするのかな…。

2014/05/22(木) [n年前の日記]

#1 [ruby] クラスに分けたりバグを修正したり

swfを再生するDXRubyスクリプトをまだ弄ってたり。

今まで、1つのクラスの中で種類を持つ変数を用意して処理を分ける、みたいなことをしていたのだけど。考えてみたら別クラスにして、クラスの種類で判別したほうがいいよなと。

しかし、クラスの種類を判別する方法が分からず。if文なら、if a.class == ClassA みたいな書き方で判別できたのだけど。case 〜 when を使うと妙なことに。

以下のようなソースを書いてテスト。
class TestA

  class TestB
  end

  class TestC
  end

  def check
    t = TestC.new

    puts "[#{t.class}]"

    # OK
    if t.class == TestA::TestB
      puts " t is TestB"
    elsif t.class == TestA::TestC
      puts " t is TestC"
    else
      puts " t is unknown"
    end

    # OK
    if TestA::TestB === t
      puts " t is TestB"
    elsif TestA::TestC === t
      puts " t is TestC"
    else
      puts " t is unknown"
    end

    # NG
    case t.class
    when TestA::TestB
      puts " t is TestB"
    when TestA::TestC
      puts " t is TestC"
    else
      puts " t is unknown"
        end
        
    # OK
    case t
    when TestA::TestB
      puts " t is TestB"
    when TestA::TestC
      puts " t is TestC"
    else
      puts " t is unknown"
    end
  end
end

chk = TestA.new
chk.check
[TestA::TestC]
 t is TestC
 t is TestC
 t is unknown
 t is TestC
case 〜 when を使ってクラス判別をする場合は、=== が使われるという説明記事も見かけた。だったらわざわざ case 〜 when を使わずに if文で書いてしまってもいいのかも。

2014/05/23(金) [n年前の日記]

#1 [dxruby][cg_tools] 色変更処理も追加できた

swfをDXRubyで再生するスクリプトをまだ修正中。

色変更処理までは手が回らないかも、と思ってたけど。Shader を使ってどうにか実装できた。

ただし、Window.drawMorph() には Shader を渡すことができないので…。
  1. ウインドウサイズの RenderTarget を作成。
  2. RenderTarget に drawMorph で描画。
  3. Window.drawEx で Image の代わりに RenderTarget を渡しながら、Shader を使って色変更。
という状態に。

しかし、RenderTarget の生成と破棄を頻繁に行ったせいか、しばらく再生を続けていると DXRuby が何かの拍子に不正終了してしまう。

また、アルファチャンネルを持っている画像を描画した際に、Flash Player 上の見た目と比べて、明らかに薄く描画されてしまった。おそらく、Shader内のアルファチャンネル計算式が違うんじゃないかと…。

仕方ないので、以下のような感じにして緩和することに。

処理時間を測定。 :

スクリプト起動時、明らかに待たされるようになってきたので、ベンチマークを測ったり。
                      user     system      total        real
xml parse:        0.016000   0.000000   0.016000 (  0.023001)
get fps           0.000000   0.000000   0.000000 (  0.002000)
load image:       0.952000   0.172000   1.124000 (  1.161067)
load shape:       0.015000   0.000000   0.015000 (  0.006000)
load sprite:      0.000000   0.000000   0.000000 (  0.008000)
root tmline:      0.031000   0.000000   0.031000 (  0.031002)
てっきり Nokogiri で xmlを解析してタイムライン情報を作っていくあたりが遅いのかなと思ったけど、そこは 0.03秒ぐらいだったのでたいしたことはなかった。

スクリプト開始時に必要な画像を全てロードしているのだけど、そこで1秒以上かかってるなと。しかし、画像を読み込まないと表示できないし…。これはもう仕方ないかな…。

以下、参考ページ。

_RubyでのBenchmarkの取り方をば。 -

#2 [nitijyou] 喉が痛い

数日前から、喉というか、食道のあたりが、なんだか変な感じで。異物が絶えずそこにあるというか…。なんだろうコレ…。

寝ている時に違和感を覚えて目が覚める、てなことが数回あったのだけど。とうとう今日は、起きてる時ですら違和感が。何かを流し込めば、しばらくは気にならなくなるけど…。

2014/05/24() [n年前の日記]

#1 [windows] ConEmuのオプション

最近、ConEmu というDOS窓をちょっとだけヨサゲにしてくれるアプリを使ってるのだけど。あふから ConEmu の新規ウインドウを作成する際、既に ConEmu が起動してるなら、そのConEmu にタブとして追加、てなことをしたいなと。

ConEmu.exe /Single でいいらしい。とメモ。

#2 [dxruby][cg_tools] タイムライン情報を事前に作らないようにした

swfをDXRubyで再生する実験をしているわけだけど。

再生開始前に、xmlを解析してタイムライン情報を配列やハッシュに格納してから再生しているのだけど。処理に1秒もかかってないとはいえ、事前に待たされるのはなんかやだなと。想像していたより Nokogiri の処理時間も少なくて済みそうなので、毎フレーム、xmlを少しずつ解析して再生するような作りにしたり。

本来、xmlを渡さずに、DXRubyで再生処理がしやすい別の形に変換・ファイル保存して、ソレを読み込んだほうがいいのかなという気もするのだけど。どうせ xml解析は最初の1回だけで済むはずで、毎フレーム、解析させる必要はないよなと。ただ、何で書き出せばいいのかで悩んでしまって。json のほうが良かったりするのかな。

#3 [zatta] なんでもかんでも福島県に押しつけようとしやがって、と、ちょっとプンスカ状態

愚痴です。

録画してたNHKの番組を見ていたら、仙台市民が、「宮城県内の指定廃棄物(放射性物質)を福島に持っていけ」「どうせ福島は汚れてるし」と主張している映像が紹介されていて。

なんだか泣きたい気分になりました。「アイツはレイプされたから、また2〜3回レイプしたって構いやしないさ」みたいなこと平然と言ってんじゃねえよ。みたいな。

少しは逆の立場で考えろと。もし、福一じゃなくて女川原発が爆発していて、「指定廃棄物は仙台に持っていけ」「どうせ仙台は汚れてるし」って他県から平然と言われたら、仙台市民はどんな気持ちになりますかと。「ふざけんな。ただでさえこんなに酷い目に合ってるのに、もっと放射能まみれになれと言いやがるのか」とブチ切れるはずで。

てなわけで、見ていて泣きたい気分でした。コイツラ、人の皮を被った鬼だ。とすら思ってしまったり。

完全に赤の他人と思ってるからそういうことを言えるんだろうな。 :

自分は今、福島に住んでるけど。仙台の大学に通ってたので、学生時代の友人達が仙台にも住んでいて。なので、大変複雑な気分ですよ。仙台の友人達が、「アイツの住んでる福島にコレを押し付けちまえ」とか思ってたらどうしよう、みたいな。まあ、そんなこと言い出しそうな友人は一人も居ないけど。居ないよな…たぶん…。

「福島に持っていくべき」と主張してる人は、福島に友人知人が居ないのだろう、福島県民を完全に赤の他人と思えているからそういうことを言えるのだろうと想像したりもして。もしも福島に友人知人が居たら、「アイツの住んでるところに押し付けることになるのか…。それもそれで困ったな。どうしたもんか」と少しは考え込んでしまって、もうちょっと口が重くなるはずで。

「福島に戻せ」という捉え方はおかしいよなあ。 :

そういえば、「放射性物質を福島県に戻せ」てな主張も見かけたような気もするけど。放射性物質は「福島県」が出したわけじゃないのに「戻せ」ってのはおかしい捉え方だよなあ…。頭が回ってないよな…。

ここでもし、「え? 福島県が放射性物質出したんだろ?」とうっかり思ってしまう人は、東電の工作員の口車に乗せられて戦犯の存在を見事にコロッと忘れて敵を見誤ってる頭が弱い人だと思います。何が爆発したからこうなってるんですか。それってどこの会社の持ち物ですか。そういう持ち物をあちこちに作らせたのはどの国のどんな政策ですか。よく考えろと。

福島県の土地に眠ってた物質が、福島県民の振舞いで飛散して、みたいな話じゃ全然ないので、「戻せ」てな言い方はおかしいよなと思ったり。

もう少し愚痴る。 :

番組内では、「今まで一番電気を使ってきた・恩恵を受けてきた東京こそがこの手の負担を引き受けるべき」てな話も紹介されていて。

たしかに、自分が東京暮らしをしていた頃の感覚を思い返すと…。どこでどうやって電気を作っているのか全く気にせずに電気を使ってたし。また、今現在ですら原発再稼働を主張する人達が東京にはゴロゴロしてるようでもあるし。仮に、そんな感覚の都民が未だにほとんどなのだとしたら、せめてこのくらいは引き受けてもいいんじゃないの、てな気分にもなってくるなと。

しかし、東京にも友人知人が住んでるわけで。なので、やっぱり、それもそれで複雑な気分。

福島県は東北電力の管轄だけど、東北電力と言えば宮城県の女川原発があるわけで、それもそれで複雑な気分。原発事故が起きた福島県は、宮城県内で動いていた原発の恩恵を受けていたという図式が、なんだかややこしくて。まあ、東京の電気をなんで福島で作ってんだよ、福島は東京電力の管轄外だろうが、てな状況に比べたらマシなほうかもしれんけど。

都市圏の電気は、できるだけ都市圏の近くで発電すべきだよなあ…。送電ロスや、災害時の需要供給バランスを考えたら、電気を馬鹿食いしてる都市の近くで発電したほうが効率がいいのだし。高層ビルの窓ガラスで発電するソレが現実的になればいいのに。

もうしばらく愚痴る。 :

自分が住んでる地域も、近所に須賀川市のゴミ焼却施設があって。要するに、「市の放射性物質が全て集まってくる場所」に、自分は既に住んでいる状態なわけでして。

ゴミを燃やしたときの煙に放射性物質がどの程度混じっているのか、てなあたりもちと不安だけど。 *1 何よりも、指定廃棄物(放射性物質が濃縮された灰)をどこにも持っていきようがなくて、袋に詰めてひたすら広場に積み重ねてる現状が問題で。

そういう状況なので、「ゴミを引き受ける側」の気持ちも、「ゴミを押し付けたくなる側」の気持ちも、両方なんとなく分かるのですが。

ウチの地域は、ゴミ焼却施設のすぐ裏に小学校や保育園があって。ソレってジジイの鼻血より実は問題じゃないのかと思ってるのですけど。小学校を移転するか、焼却施設を移転するか、どちらかやらないといかんのかもしれんけど、金が無いんだろうな…。

やっぱり金だよな。 :

ということで、皆貧乏が悪いんや、やっぱり全ては金だよなと。なので、「福島に引き受けてもらおう」と言い出すなら、せめてその分の金をくれ、と言いたい気分にもなってきたり。

危険なものは金を払って他所に押し付ける。それが日本の原発の歴史。だったら今回もソレでいいじゃん。近所に危険なブツを置きたくない、他所に追い出したいなら、札束で貧乏人達の頬を叩いて汚物を押し付けちまえばいいですよ。今までと同様に。

別にこんなの、原発に限らず、何でもそうだと思うのですけど。
  • 金を払って他所に苦労を押し付けるか。
  • 金を惜しんで自分で苦労を背負うか。
フツーはどちらかを選ぶもので。

金すら払わずに苦労・苦痛だけを他所に押し付けたいなんてのは流石にサイテー過ぎるので、せめて金ぐらいは払うこと前提でその手の主張をしないといかんよなと思えてきたり。だけどその金がどこから出てくるのかと考えると、うーん。

以上、ただの愚痴でした。できるだけこの手の話題は書かないようにと思ってたけど、あの映像見てプツンと来ちゃったもので…。スイマセン。

*1: 年々線量が上がっていったり、下がり方がおかしかったら、煙に混ざってた放射性物質が原因だろう、てな話になりそうだけど。これは様子を見て毎年線量を測っていくしかないよなと。

2014/05/25() [n年前の日記]

#1 [cg_tools] 蒼姫ラピス嬢の3DCGモデルデータがCC0で公開されてたらしい

_蒼姫ラピス | Arc Project | クラウドを利用した3DCGデータ利活用実験
_「Ark Project」サイトにて「蒼姫ラピス」3D-CGモデルデータの著作権を開放しました。 | VOCALOID3 i-style Project オフィシャルブログ

2014/02月末頃にそのような展開があったとは…。知らなかった…。

どこかの時点でニュースにはなっていたけど自分は見落としてしまったのだろうか、などと気になったのでGoogleのニュース検索でググってみたけど。どのIT系ニュースサイトでも完全に無視されていて…。アレゲな人々が集うと言われる _/.J ですら検索しても見つからず。これでは自分が知らなかったのも仕方ないなと思えてきました。

それはともかく。 _大崎一番太郎 とセットで使えば、魔法少女+UMA の見た目が再現できそうな予感。素晴らしい。

せっかくだから、他に把握してるモデルデータもメモ。

モブ子さん。 :

_mato.sus304さんのぶろぐ PMCAv0.0.6について
■利用について

PMCAによって生成されたモデルの利用許諾は、モデルの組み立てに使われたパーツの利用許諾に準じます。

PMCAに付属しているパーツは全てパブリックドメイン相当とし、その利用に制限を求めません。また、作者はパーツを利用した事によって生じた損害に対し一切の責任を負いません。

mato.sus304さんのぶろぐ PMCAv0.0.6について より

全裸おじさん。 :

_Makehuman | Open source tool for making 3D characters
_Can I create a commercial closed source game with models generated by MakeHuman? | Makehuman

MakeHuman で作成されたモデルデータは CC0 扱いになる、と書いてあるのかな。たぶん。

G-Tuneちゃん。 :

以下、独自ライセンスがついて回るけど、利用に関しては比較的自由度が高いモデルデータ。

_TuneちゃんMMDモデルデータ無償配布開始!|G-Tune −ハイエンド ゲームPC ブランド−
TuneちゃんMMDモデルの使用要件は簡単! 作品紹介で「G-Tuneちゃん」を紹介するだけ! 商用・非商用フリー!改造・改変制限なし! 使用報告も必要なし!

TuneちゃんMMDモデルデータ無償配布開始!|G-Tune −ハイエンド ゲームPC ブランド− より

■禁止事項
・反社会的な行為や目的、特定の信条や宗教、政治的発言のために弊社キャラクターを使用することを禁止します。
・弊社キャラクター、弊社、弊社が提供する製品およびサービスの名誉と品位を傷つける目的で使用することを禁止します。
・弊社による別途認可を受けた場合を除き、弊社公式製品のような誤解を招く利用をすることを禁止します。
・第三者の知的財産権およびその他一切の権利を損なう行為、またはかかる権利を損なう目的のために使用することを禁止します。

TuneちゃんMMDモデルデータ無償配布開始!|G-Tune −ハイエンド ゲームPC ブランド− より

要するに、「マウスコンピューターのG-Tuneシリーズ宣伝に繋がるなら使ってもいいよ」というお話なのだろうなと。

UNITY-CHAN。 :

_UNITY-CHAN!
_UNITY-CHAN! キャラクター利用のガイドライン 1.01バージョン
_ユニティちゃんライセンス条項 1.01バージョン

比較的制限は緩いほうらしいという噂をどこかで見かけた記憶があるのですが、条項が膨大で読む気がしない…。とりあえず、トレードマーク画像を必ずどこかで表示しないといかんのでしたっけか。

ゲーム関係って、昔アメリカで変な裁判を多々起こされて日本企業がボコボコにされていたそんな印象があるので、自衛のためにはこのくらい必要になってしまうのかなと想像していますが。

珠音嬢。 :

_HSP3オフィシャル3D素材 "珠音(たまね)"
一般的な組み込みでの使用については、特に制限を行なうことは考えていません。元データの改変、改造、キャラクターの2次創作作品なども自由に公開して頂いて構いません。ルールを守って、ぜひ楽しいオリジナル作品を製作してください。

この素材は、Hot Soup Processor(HSP)とともに使用され、HSPの普及と発展に寄与することを願っています。意図的にイメージを悪化させることを目的とした使用、違法行為、迷惑行為、不正行為を助長する目的に使用しないでください。 また、3D素材 "珠音(たまね)"データのみを販売するなど、著作権を侵害する形での使用は禁止致します。

HSP3オフィシャル3D素材 "珠音(たまね)" より

HSP利用、かつ、HSPの普及発展に貢献する範囲であれば制限は緩い、という認識でいいのかしらん。

#2 [dxruby][cg_tools] テキスト描画にも対応させた

swfをDXRubyで再生する実験をしているわけだけど。

一応テキストの描画にも対応。ただ、swfのテキスト描画は機能が豊富なので、イイ感じの対応は無理。あくまで間に合わせで、一応表示できるかな、といった感じで。 テキストも画像と同様に凝った描画をしたいなら、事前にテキストを画像として用意しちゃうほうが楽だろうなと。

2014/05/26(月) [n年前の日記]

#1 [anime] シドニアの騎士、7話まで視聴

宇宙怪獣と巨大ロボットがバトルするアニメ。人類は絶滅寸前の危機的状況。SFアニメだ…。と思ったけど、SFって言っちゃうとなんか色々文句言い出す人が居そうだし言わない方がいいのかな。とりあえず、巨大ロボットで見事なスペースランナウェイなのでこういう作品は好きです。

キャラクターも3DCGで描かれてるのだけど、なんだか見た目がちょっと…。とりあえず、キャラの区別がつかないあたりはどうにかならなかったのかなと思うけど、漫画原作からしてそうなのかしら。もしかすると見た目が似てしまうのは設定上仕方ないのかもしれんし。 *1
*1: 全員クローンなのかもしれず。

#2 [dxruby][cg_tools] DXRubyでswfのベクターデータを描画

ビットマップ画像をグリグリ動かす swf は再生できるようになったので、調子に乗ってベクターデータの描画もできないかなと実験を始めたり。

直線は描画できるようになったけど…。ソレ以外が全然…。 これができないと swfのソレは再現できそうにないけれど、どうやって実装したらいいのやら。

凹多角形の塗り潰しは、凹多角形を三角形で分割するか、全ての線分をスキャンラインで見て交点を求めるか…。どっちがいいんだろう…。後者は前者より描画が遅くなるだろうけど、グラデーションやビットマップ塗りにも対応できそうだし。

しかしコレ、事前にCGツールでビットマップ画像にしておいたほうがいいんじゃないのかな…。わざわざベクターデータを持っておいて毎回描画するメリットって、ちょっと思いつかないし…。いや、容量削減には繋がるだろうけど。

一応ハマったところをメモ。

2014/05/27(火) [n年前の日記]

#1 [dxruby][cg_tools] 虎を描画して実験中

_File:Ghostscript Tiger.svg - Wikimedia Commons を swf に変換して、DXRuby上で描画して確認中。

svg から swf への変換は、 _svg2pdr と、 _ParaDraw を利用。
  1. svg2pdr を使って、.svg → .pdr (ParaDraw形式) に変換。
  2. .pdr を ParaDraw で開いて、修正を加えたり、swf で保存したり。
  3. あるいは、.pdr を Suzuka にアイテムとして登録して、swf で書き出したり。

さすがに虎は重い…。Imageに描画し終わるまで数秒かかる。これでは絶対に、毎フレーム、ベクターデータを再描画なんて無理だなと。一旦 Image に描画したら、その後はずっとその Image を使い続ける、みたいな書き方をしないと。ソレに比べると、Flash Player の処理速度はスゴイ。虎ですら一瞬で表示されるわけで。

参考ページをメモ。 :

凹多角形の塗りつぶしは、 _多角形塗りつぶし(1) を参考に、というかそのまま Ruby で書き直して試していたり。ところどころ描画がおかしくなって悩んだけど、こちらから渡す頂点配列にそもそも問題があったようで、そこのバグを取ったら、まともな結果を出してくれるようになってきた。

直線描画と、多角形塗りつぶしのエッジ部分がずれていたので、計算誤差でもあるのかなと。 _DDAによる線・円の描画3 を参考に、1ドットずつDDAで直線描画するメソッドも書いてみたり。しかし、結果は変わらず。ちなみに、件のページで紹介されてるDDAのソレのサンプルは、水平線か垂直線の場合に変な線になりそうな気がする。たぶん。

線幅を持った直線描画は、 _アンチエイリアス付きの自由線描画(1) を参考にさせてもらいました。しかし、さすがに Ruby では処理が遅く、線を一本描くだけでも十数フレームかかってる感じ。1ドット描くたびに64回ループ回してるから仕方ないのだけど。アンチエイリアス描画の実現方法については、もっと調べてみないといかんなと…。

二次ベジェ曲線は、 _新・C言語 〜ゲームプログラミングの館〜 [DXライブラリ] に書かれてた式を参考にして、最後は全部直線データとして吐き出すようにしてみたり。ただ、適切な分割数をどうやって求めるか…。結局、分割数は固定にしたまま試しているところ。

#2 [zatta] NHKスペシャル「メルトダウン File.4 放射能"大量放出"の真相」を視聴

録画してた分を消化。

電気が無くても蒸気の圧力で冷却してくれるはずのRCICなる装置、の軸の部分で、圧力が高まって汚染された蒸気が建屋内に出てしまって、建屋内での作業ができずに格納容器が圧力で壊れていくるのを黙って見ているしかなかった。てな状況だったとの推測映像が紹介されていて。実際に、同じ構造の軸+パッキン部分を作って実験したら勢いよく蒸気がシューシューと。パッキン部分の圧力を減らすための仕組みも存在してたけど、そこは結局電気が無いと動かなかったのです、てなあたりが…。たぶんコレ、電源喪失を想定しないまま、電源喪失時の対策を設計してたのだろうな。

圧力容器の壁に溶けた燃料が触れなくても、温度が上がると圧力容器の壁が熱膨張して、しかし圧力容器の周辺のコンクリートの材質が違う部分があるので、そこが飛び出して亀裂が入って壊れたんじゃね? てな話も紹介されていて。その予想が当たっているとしたら、「この容器は設計上、ここまでの圧力に耐えられるから、この原発は安全です」という話は全部通らなくなるよなと。周囲を固めてるコンクリートまでチェックして、違いがある部分の強度を確認しないと…。熱で壁が膨張した時にどうなるか、シミュレーションが必須だよなと。

サプチャンとやらを蒸気が通っていくからベントしても放射性物質は少なくなるよ、と事故時にTVカメラの前で東電は主張してたけど。実際は、サプチャン内の水温が高くなってる状態では50%程度しか減らせずに放射性物質がそのまま出ていく、てな実験結果が。

てなあたりを、各電力会社や規制委員会に質問して、どんな対策を考えているかと尋ねてみても、具体的な対策について答えることもなく、「それが本当なら対策します」「指摘内容を検討中です」「できるだけ頑張ってます」しか返ってこない、とのオチがついて。

せっかくこれだけの事故を起こしたのだから、その気になれば事故のメカニズムも調べられるだろうし、安全性を高めるための策だって色々出せそうだけど。これじゃやっぱりまた事故を起こしちゃうだろうなと思えてきたり。

2014/05/28(水) [n年前の日記]

#1 [dxruby][cg_tools] アンチエイリアス有りの直線描画について調べていたり

直線を描く際にアンチエイリアス有りで描きたいなと。

まずは直線を描く際のアルゴリズムについてググってたのだけど。 _ブレゼンハムのアルゴリズム - Wikipedia の中に、
アンチエイリアスをサポートした直線描画アルゴリズム(例えば、Xiaolin Wu's line algorithm)もあるが、

ブレゼンハムのアルゴリズム - Wikipedia より

という記述を見かけて、ソレは何だろうと興味が湧いて。

_Xiaolin Wu's line algorithm - Wikipedia, the free encyclopedia に、説明とソースがあったものの、英文なので何がなんだか。とりあえず 1991年頃に発表されたアルゴリズムらしいけど。

_Source code for Xiaolin Wu's line algorithm in C? - Stack Overflow 経由で、以下のページに遭遇。

_Wu Anti-aliased Lines
_Xiaolin Wu's line algorithm - Rosetta Code
_Antialiasing: Wu Algorithm - CodeProject

ソースをコピペして Ruby + DXRuby で描画実験中。しかしちゃんと描画されない。どうもアルファチャンネル値の計算で失敗してる予感。

どのページも英文ページなので、英語がさっぱりな自分にはチンプンカンプンだけど。図を眺めた感じでは、「直線を描く際の1点分は、えてして2ドットにまたがるのだから、2ドットのそれぞれの割合を求めて塗る濃さを変えればいいんじゃね?」てな方法に思えたり。サンプルソース中でも必ず2ドットを1セット扱いで描画しているようだし。

線幅を持った直線の描画についても悩む。 :

事前にブラシ画像を作って、1ドット描く代わりにブラシ画像で描画、てなやり方ならそこそこ速く描画できたけど。もうちょっと速くできないかなと。

線幅を持った直線を、回転した矩形と考えれば、4点を求めて三角形の塗りつぶしでなんとかなるかもしれないなと。試してみたら上手くできた。始点と終点がアレだけど、そこは円で塗り潰してお茶を濁したり。

ただ、この方法だと、アンチエイリアスはかかってないわけで。何かいい手はないかなと。

#2 [nitijyou] 自転車で買い物に

ホーマック、Seria、ヨークベニマルまで自転車で。犬の糞を取るための袋を購入。犬の鎖も買いたかったのだけど、ヨサゲな長さ・価格のものが見つからず。常時繋いでおくために使うわけじゃなくて、少し場所を変えて繋いでおきたいときに使いたいので、ガッチリしたものじゃなくてもいいのだけど。

2014/05/29(木) [n年前の日記]

#1 [dxruby][cg_tools] DXRubyでswfのシェイプを描画する実験

とりあえず、今現在動いてるところまでメモしておこうかなと。

二次ベジェ曲線。 :

まずは、二次ベジェ曲線の描画を実験。
二次ベジェ曲線の描画テスト

_bezierdraw.rb
require 'dxruby'

# 二次ベジェ曲線を描画するクラス
class BezierDraw
  DBG = true

  # 二次ベジェ曲線を描画するための頂点リストを生成
  #
  # @param [Number] x1 始点 x 座標
  # @param [Number] y1 始点 y 座標
  # @param [Number] x2 制御点 x 座標
  # @param [Number] y2 制御点 y 座標
  # @param [Number] x3 終点 x 座標
  # @param [Number] y3 終点 y 座標
  # @return [Array] ベジェ曲線を描画するための頂点座標配列
  #
  def BezierDraw.calc(x1, y1, x2, y2, x3, y3)
    p = []
    p.push([x1, y1])
    t = 0.05
    while t < 1.0
      v = 1.0 - t
      a = v * v
      b = 2 * v * t
      c = t * t
      x = a * x1 + b * x2 + c * x3
      y = a * y1 + b * y2 + c * y3
      p.push([x, y])
      t += 0.05
    end
    p.push([x3, y3])
    return p
  end

  # ベジェ曲線を描画
  #
  # @param [Object] img Imageオブジェクト
  # @param [Array] p 頂点座標配列
  # @param [Array] lcol 色配列。A,R,G,B が入ってる。
  #
  def BezierDraw.draw(img, p, lcol)
    (p.size - 1).times do |i|
      j = i + 1
      img.line(p[i][0], p[i][1], p[j][0], p[j][1], lcol)
    end
  end
end

if $0 == __FILE__
  # ----------------------------------------
  # 動作テスト

  img = Image.new(640, 480)
  p = [[30, 240], [300, 10], [600, 400]]
  lcol = [255, 255, 0, 0]

  Window.loop do
    break if Input.keyPush?(K_ESCAPE)

    p[1] = Input.mousePosX, Input.mousePosY

    x0, y0 = p[0]
    for i in 1..2 do
      x1, y1 = p[i]
      Window.drawLine(x0, y0, x1, y1, [128, 128, 128])
      x0, y0 = x1, y1
    end

    img.clear
    x1, y1 = p[0]
    x2, y2 = p[1]
    x3, y3 = p[2]
    np = BezierDraw.calc(x1, y1, x2, y2, x3, y3)
    BezierDraw.draw(img, np, lcol)
    Window.draw(0, 0, img)
  end
end
マウスカーソルを動かすと制御点を移動できます。

凹多角形の塗りつぶし。 :

凹多角形の塗りつぶし

_polyfill.rb
require 'dxruby'

# 与えられた多角形で塗りつぶしをするクラス
#
# @note 以下のページを参考にしました。
#       多角形塗りつぶし(1)
#       http://azskyetc.client.jp/paintprog/031_polygon1.html
#
class PolyFill

  # 塗り潰しをする
  #
  # @param [Object] img Imageオブジェクト
  # @param [Array] np 頂点座標配列 ((x,y), (x,y), ...)の形で入っている。
  # @param [Array] f0col 塗り潰しに使う色配列
  # @param [Array] f1col 塗り潰しに使う色配列
  # @param [Boolean] clip_enable trueなら画面サイズを超えた部分の描画をしない
  #
  def PolyFill.fill(img, np, f0col, f1col, clip_enable = true)

    return if np.size < 3

    # y座標の最小値と最大値を求める
    ymin = ymax = np[0][1]
    np.each do |p|
      ymin = p[1] if ymin > p[1]
      ymax = p[1] if ymax < p[1]
    end

    if clip_enable
      ymin = 0 if ymin < 0
      ymax = Window.height if ymax > Window.height
    end

    if clip_enable
      # 1ラインずつスキャンしていく
      ymin.step(ymax, 1.0) do |y|
        xlst = PolyFill.get_intersection_point(y.to_f, np) # 交点のx座標を求める
        next if xlst.empty? # 交点が無い
        next if xlst.size % 2 != 0 # 交点数が奇数
        i = 0
        while i < xlst.size
          x0, x1 = xlst[i], xlst[i+1]
          unless x1 < 0 or x0 > Window.width
            img.line(x0, y, x1, y, f0col)
          end
          i += 2
        end
      end
    else
      ymin.step(ymax, 1.0) do |y|
        xlst = PolyFill.get_intersection_point(y.to_f, np)
        next if xlst.empty? or xlst.size % 2 != 0
        i = 0
        while i < xlst.size
          img.line(xlst[i], y, xlst[i+1], y, f0col)
          i += 2
        end
      end
    end
  end

  # 与えられた頂点座標配列を元にして交点リストを求める
  #
  # @param [Number] y スキャンするy座標
  # @param [Array] lst 頂点座標配列
  # @return [Array] 交点のx座標を配列で返す
  #
  def PolyFill.get_intersection_point(y, lst)
    xlst = []
    len = lst.size
    len.times do |i|
      x1, y1 = lst[i]
      x2, y2 = lst[(i + 1) % len]

      next if y1 == y2 # 水平線なら処理しない

      # yが、現在の辺の終点と、次辺の始点であり、
      # 両辺が上方向、または下方向の場合は交点から除外
      if y == y2
        y3 = lst[(i + 2) % len][1]
        next if (y2 - y1 < 0 and y3 - y2 < 0)
        next if (y2 - y1 > 0 and y3 - y2 > 0)
      end

      # yが下方向になるように入れ替え
      if y1 > y2
        x1, x2 = x2, x1
        y1, y2 = y2, y1
      end

      if y1 <= y and y <= y2
        # 交点を追加
        x = ((y - y1) * (x2 - x1)).to_f / (y2 - y1) + x1
        xlst.push(x)
      end
    end

    # 交点を小さい順にソートして返す
    return xlst.sort
  end
end

if $0 == __FILE__
  # ----------------------------------------
  # 動作テスト

  img = Image.new(640, 480)
  lst = [
    [200, 30],
    [300, 130],
    [350, 230],
    [300, 330],
    [200, 230],
    [50, 330],
    [100, 130],
    [200, 30]
  ]
  f0col = [255, 0, 255, 0]
  f1col = [255, 255, 0, 0]

  Window.loop do
    break if Input.keyPush?(K_ESCAPE)
    lst[4] = Input.mousePosX, Input.mousePosY
    img.clear
    PolyFill.fill(img, lst, f0col, f1col, false)
    Window.draw(0, 0, img)
  end
end
マウスカーソルを動かすと1点だけ移動できます。

_多角形塗りつぶし(1) をそのまま DXRuby で動くように書き直しただけですが…。

アンチエイリアス付きの直線描画。 :

_Xiaolin Wu's line algorithm - Wikipedia, the free encyclopedia を手元でも実験。

_Wu Anti-aliased Lines
_Xiaolin Wu's line algorithm - Rosetta Code
_Antialiasing: Wu Algorithm - CodeProject

アンチエイリアス付きの直線描画

ちなみに、アンチエイリアス無し(DXRuby の Image#line)で描画すると以下のような感じに。
アンチエイリアス無しの直線描画

_xiaolinwu.rb
# Xiaolin Wu's line algorithm - Rosetta Code
# http://rosettacode.org/wiki/Xiaolin%5FWu%27s%5Fline%5Falgorithm

require 'dxruby'

class XiaolinWuDraw

  def XiaolinWuDraw.draw_line(img, x1, y1, x2, y2, colour)
    if x1 == x2 and y1 == y2
      # not line. this is a dot.
      img[x1, y1] = colour
      return
    end

    dx, dy = x2 - x1, y2 - y1
    steep = (dy.abs > dx.abs)? true : false
    x1, y1, x2, y2, dx, dy = y1, x1, y2, x2, dy, dx if steep
    x1, y1, x2, y2 = x2, y2, x1, y1 if x1 > x2
    gradient = dy.to_f / dx.to_f

    # handle the first endpoint
    xend = x1.round
    yend = y1 + gradient * (xend - x1)
    fp = (x1 + 0.5) - (x1 + 0.5).truncate
    rfp = 1.0 - fp
    xgap = rfp
    xpxl1 = xend
    ypxl1 = yend.truncate
    fp = yend - yend.truncate
    rfp = 1.0 - fp
    XiaolinWuDraw.put_colour(img, xpxl1, ypxl1,     colour, steep, rfp * xgap)
    XiaolinWuDraw.put_colour(img, xpxl1, ypxl1 + 1, colour, steep,  fp * xgap)
    itery = yend + gradient

    # handle the second endpoint
    xend = x2.round
    yend = y2 + gradient * (xend - x2)
    fp = (x2 + 0.5) - (x2 + 0.5).truncate
    rfp = 1.0 - fp
    xgap = rfp
    xpxl2 = xend
    ypxl2 = yend.truncate
    fp = yend - yend.truncate
    rfp = 1.0 - fp
    XiaolinWuDraw.put_colour(img, xpxl2, ypxl2,     colour, steep, rfp * xgap)
    XiaolinWuDraw.put_colour(img, xpxl2, ypxl2 + 1, colour, steep,  fp * xgap)

    # in between
    (xpxl1 + 1).upto(xpxl2 - 1).each do |x|
      itt = itery.truncate
      fp = itery - itery.truncate
      rfp = 1.0 - fp
      XiaolinWuDraw.put_colour(img, x, itt,     colour, steep, rfp)
      XiaolinWuDraw.put_colour(img, x, itt + 1, colour, steep,  fp)
      itery = itery + gradient
    end
  end

  def XiaolinWuDraw.put_colour(img, x, y, col, steep, c)
    return if c == 0.0 or col[0] == 0
    x, y = y, x if steep
    o = img[x, y]
    n = Array.new(4)
    if o[0] >= 255
      # Bg Alpha = 255
      n[0] = 255
      for i in 1..3 do
        n[i] = (col[i] * c + o[i] * (1.0 - c)).round
      end
    elsif o[0] == 0
      # Bg Alpha = 0
      n[0] = (255 * c).round
      for i in 1..3 do
        n[i] = col[i]
      end
    else
      na = (255 * c).round
      n[0] = (na > o[0])? na : o[0]
      for i in 1..3 do
        n[i] = (col[i] * c + o[i] * (1.0 - c)).round
      end
    end
    img[x, y] = n
  end
end

# ----------------------------------------
# test

if $0 == __FILE__
  font = Font.new(14)

  w, h = 500, 500
  bgcol = [0, 0, 255] # blue (R,G,B)
  fgcol = [255, 255, 255, 0] # Yellow (A,R,G,B)
  fgcol2 = [255, 0, 255, 0] # Green (A,R,G,B)

  p = []
  10.step(460, 30) do |a|
    p.push([10, 10, 490, a, fgcol])
    p.push([10, 10, a, 490, fgcol])
  end
  p.push([10, 10, 490, 490, fgcol])

  img0 = Image.new(w, h)
  # img0.fill(bgcol)
  p.each do |d|
    x1, y1, x2, y2, col = d
    XiaolinWuDraw.draw_line(img0, x1, y1, x2, y2, col)
  end
  # img0.save("test.png")

  img1 = Image.new(w, h)
  p.each do |d|
    x1, y1, x2, y2, col = d
    img1.line(x1, y1, x2, y2, col)
  end

  img2 = Image.new(640, 480)

  Window.bgcolor = bgcol
  Window.loop do
    break if Input.keyPush?(K_ESCAPE)

    # Push A key
    aa_enable = (Input.keyDown?(K_A))? false : true

    img2.clear
    x1, y1 = 320, 240
    x2, y2 = Input.mousePosX, Input.mousePosY

    if aa_enable
      XiaolinWuDraw.draw_line(img2, x1, y1, x2, y2, fgcol2)
      Window.draw(0, 0, img0)
      Window.draw(0, 0, img2)
    else
      img2.line(x1, y1, x2, y2, fgcol2)
      Window.draw(0, 0, img1)
      Window.draw(0, 0, img2)
    end

    s = "#{Window.real_fps} FPS  CPU: #{Window.getLoad.to_i} %"
    Window.drawFont(640 - 140, 2, s, font)
    s = "Push A key"
    Window.drawFont(640 - 140, 20, s, font)
  end
end
一見それらしく描画できたものの、アルファチャンネル値の扱いに関してバグが残ってるような気がします…。

直線描画。 :

色々なやり方で直線を描画してみたり。

直線描画

_linedraw.rb
require 'dxruby'
require_relative 'xiaolinwu'

#
# 直線を描画するクラス
#
class LineDraw

  # 直線を描画
  #
  # @param [Object] img 描画対象のImageオブジェクト
  # @param [Number] x1 直線始点 x
  # @param [Number] y1 直線始点 y
  # @param [Number] x2 直線終点 x
  # @param [Number] y2 直線終点 y
  # @param [Array] col 描画色配列 A,R,G,B
  # @param [Number] linew 線幅
  # @param [Number] aa アンチエイリアス処理の分割数
  # @param [Number] last_t 前回描画時の余った値
  # @param [Boolean] aa2 trueなら線幅が太い場合もアンチエイリアスをかけるが激しく遅い
  # @param [Boolean] edge_only trueならエッジのみ描画。動作確認用
  # @return [Number] 描画時の余った値
  #
  def LineDraw.draw(img, x1, y1, x2, y2, col, linew = 1,
                    aa = 1, last_t = 0, aa2 = false, edge_only = false)
    ret = 0
    if linew <= 1
      # 線幅 = 1 の時

      if aa <= 1
        # アンチエイリアス無し
        LineDraw.draw_dda(img, x1, y1, x2, y2, col)
        # img.line(x1, y1, x2, y2, col)
      else
        # アンチエイリアス有り
        XiaolinWuDraw.draw_line(img, x1, y1,x2, y2, col)
      end
    else
      # 線幅 > 1 の時

      if aa2
        # アンチエイリアス有りで描画。激しく遅い
        ret = LineDraw.draw_aa(img, x1, y1, x2, y2, col, linew, aa, last_t)
      else
        # アンチエイリアス無しで描画

        # 三角形ポリゴン+円塗りつぶしで描画
        ret = LineDraw.draw_poly(img, x1, y1, x2, y2, col, linew, edge_only)

        # ブラシで描画
        # ret = LineDraw.draw_brush(img, x1, y1, x2, y2, col, linew, last_t)
      end
    end
    return ret
  end

  # ----------------------------------------
  # 直線をDDAで描画。アンチエイリアス無し
  #
  # @note 以下参考ページ
  #       DDAによる線・円の描画3
  #       http://www7.plala.or.jp/nekogrammer/dda/DDA3.htm
  #
  # @param [Object] img 描画対象のImageオブジェクト
  # @param [Number] x1 直線始点 x
  # @param [Number] y1 直線始点 y
  # @param [Number] x2 直線終点 x
  # @param [Number] y2 直線終点 y
  # @param [Array] col 描画色配列。A,R,G,B
  #
  def LineDraw.draw_dda(img, x1, y1, x2, y2, col)
    x1 = x1.to_i
    y1 = y1.to_i
    x2 = x2.to_i
    y2 = y2.to_i
    dx = x2 - x1
    dy = y2 - y1
    a = dx.abs
    b = dy.abs
    xadd = LineDraw.get_sign(dx)
    yadd = LineDraw.get_sign(dy)
    x, y = x1, y1
    if a >= b
      # x方向のほうが大きい
      e = dy.abs * -1
      fg = true
      while fg
        img[x, y] = col
        break if x == x2
        x += xadd
        e += 2 * b
        if e >= 0
          y += yadd
          e -= 2 * a
        end
      end
    else
      # y方向のほうが大きい
      e = dx.abs * -1
      fg = true
      while fg
        img[x, y] = col
        break if y == y2
        y += yadd
        e += 2 * a
        if e >= 0
          x += xadd
          e -= 2 * b
        end
      end
    end
    return 0
  end

  def LineDraw.get_sign(x)
    return 0 if x == 0
    return -1 if x < 0
    return 1
  end

  # ----------------------------------------
  # 直線をブラシ画像で描画
  #
  # @param [Object] img 描画対象のImageオブジェクト
  # @param [Number] x1 直線始点 x
  # @param [Number] y1 直線始点 y
  # @param [Number] x2 直線終点 x
  # @param [Number] y2 直線終点 y
  # @param [Array] col 描画色配列 A,R,G,B
  # @param [Number] linew 線幅
  # @param [Number] last_t 前回描画時の余った値
  # @param [Number] line_interval 描画間隔
  # @return [Number] 描画時の余った値
  #
  def LineDraw.draw_brush(img, x1, y1, x2, y2, col, linew,
                          last_t = 0, line_interval = 0.2)

    brush = LineDraw.get_brush_image(col, linew, aa = 4)
    dx = (x2 - x1).to_f
    dy = (y2 - y1).to_f
    len = Math.sqrt(dx * dx + dy * dy)
    return if len == 0
    ti = line_interval / len
    t = last_t / len
    r = brush.width / 2
    while t < 1.0
      x = dx * t + x1 - r
      y = dy * t + y1 - r
      img.draw(x, y, brush)
      dt = r * ti
      dt = 0.0001 if dt < 0.0001
      t += dt
    end
    last_t = len * (t - 1.0)
    brush.dispose
    return last_t
  end

  # 描画用のブラシImageを取得
  #
  # @param [Array] col ブラシ色
  # @param [Number] linew 線幅
  # @param [Number] aa アンチエイリアス値
  # @return [Object] ブラシ用Image
  #
  def LineDraw.get_brush_image(col, linew, aa = 9)
    w, h = linew, linew
    r = linew / 2.0
    if w <= 0 or h <= 0
      w, h = 1, 1
      r = 0.5
    end
    img = Image.new(w, h)
    rr = r * r
    aad = aa * aa
    0.step(h, 1.0) do |y|
      0.step(w, 1.0) do |x|
        v = 0
        aa.times do |jy|
          yy = jy * (1.0 / aa) + y - r
          aa.times do |jx|
            xx = jx * (1.0 / aa) + x - r
            d = (xx * xx + yy * yy) / rr
            v += (1.0 - d) if d < 1.0
          end
        end
        a = col[0] * v.to_f / aad
        img[x, y] = [a, col[1], col[2], col[3]]
      end
    end
    return img
  end

  # ----------------------------------------
  # 直線をアンチエイリアス有りで描画
  #
  # @note 以下参考ページ
  #       アンチエイリアス付きの自由線描画(1)
  #       http://azskyetc.client.jp/paintprog/025_aaline1.html
  #
  # @param [Object] img 描画対象のImageオブジェクト
  # @param [Number] x1 直線始点 x
  # @param [Number] y1 直線始点 y
  # @param [Number] x2 直線終点 x
  # @param [Number] y2 直線終点 y
  # @param [Array] col 描画色配列 A,R,G,B
  # @param [Number] linew 線幅
  # @param [Number] aa アンチエイリアス処理の分割数
  # @param [Number] last_t 前回描画時の余った値
  # @param [Number] line_interval 描画間隔
  # @return [Number] 描画時の余った値
  #
  def LineDraw.draw_aa(img, x1, y1, x2, y2, col, linew,
                       aa = 4, last_t = 0, line_interval = 0.25)
    dx = (x2 - x1).to_f
    dy = (y2 - y1).to_f
    len = Math.sqrt(dx * dx + dy * dy)
    return if len == 0
    ti = line_interval / len
    t = last_t / len
    while t < 1.0
      x = dx * t + x1
      y = dy * t + y1
      LineDraw.draw_point(img, x, y, col, linew, aa)
      dt = linew * ti
      dt = 0.0001 if dt < 0.0001
      t += dt
    end
    last_t = len * (t - 1.0)
    return last_t
  end

  # アンチエイリアス付きで円を描画
  #
  # @param [Object] img Imageオブジェクト
  # @param [Number] px x座標
  # @param [Number] py y座標
  # @param [Array] col 描画色配列。A,R,G,B
  # @param [Number] linew 線幅
  # @param [Number] aa アンチエイリアス処理の分割数
  #
  def LineDraw.draw_point(img, px, py, col, linew, aa = 4)
    r = linew / 2.0
    x1, y1 = (px - r).floor, (py - r).floor
    x2, y2 = (px + r).ceil, (py + r).ceil
    rr = r * r
    aad = aa * aa
    cc = [255, col[1].to_i, col[2].to_i, col[3].to_i]
    for y in y1..y2
      for x in x1..x2
        next if img.compare(x,y,cc)

        v = 0.0
        xa = x - px
        ya = y - py
        for jy in 0..aa
          yy = ya + (jy.to_f / aa)
          for jx in 0..aa
            xx = xa + (jx.to_f / aa)
            d = (xx * xx + yy * yy) / rr
            v += (1.0 - d) if d < 1.0
          end
        end
        ao = col[0] * v / aad
        ao = 0 if ao < 0
        ao = 255.0 if ao > 255.0

        s = img[x,y]
        s[0] = (s[0] > ao)? s[0] : ao
        a = ao / 255.0
        ma = 1.0 - a
        s[1] = s[1] * ma + cc[1] * a
        s[2] = s[2] * ma + cc[2] * a
        s[3] = s[3] * ma + cc[3] * a
        img[x,y] = s
      end
    end
  end

  # ----------------------------------------
  # 三角形塗りつぶしと円塗りつぶしを使って直線を描画
  #
  # @note 線本体を三角形塗りつぶしを2回使って描く。始点と終点は円塗りつぶしで描く。
  #
  # @param [Object] img 描画対象のImageオブジェクト
  # @param [Number] x1 直線始点 x
  # @param [Number] y1 直線始点 y
  # @param [Number] x2 直線終点 x
  # @param [Number] y2 直線終点 y
  # @param [Array] col 描画色配列 A,R,G,B
  # @param [Number] linew 線幅
  # @param [Boolean] edge_only trueならエッジのみ描画。動作確認用
  # @return [Number] 描画で余った値
  #
  def LineDraw.draw_poly(img, x1, y1, x2, y2, col, linew, edge_only = false)
    r = linew / 2.0
    dx = x2 - x1
    dy = y2 - y1
    rad = Math.atan2(dy, dx) + (Math::PI / 2.0)
    xd = r * Math.cos(rad)
    yd = r * Math.sin(rad)

    # 線幅を持った線、つまり、矩形の4点を求める
    np = []
    np.push([x1 + xd, y1 + yd])
    np.push([x2 + xd, y2 + yd])
    np.push([x2 - xd, y2 - yd])
    np.push([x1 - xd, y1 - yd])

    if edge_only
      # エッジだけ描画して動作確認
      np.size.times do |i|
        x3, y3 = np[i]
        x4, y4 = np[(i+1) % np.size]
        img.line(x3, y3, x4, y4, col)
      end
      img.circle(x1, y1, r, col)
      img.circle(x2, y2, r, col)
    else
      # 塗り潰し

      # 三角形塗りつぶしを2回
      img.triangleFill(np[0][0], np[0][1], np[1][0], np[1][1],
                       np[2][0], np[2][1], col)
      img.triangleFill(np[2][0], np[2][1], np[3][0], np[3][1],
                       np[0][0], np[0][1], col)
      # 始点と終点を円で塗りつぶし
      img.circleFill(x1, y1, r, col)
      img.circleFill(x2, y2, r, col)
    end

    return 0
  end

end

if $0 == __FILE__
  # ----------------------------------------
  # 動作テスト

  font = Font.new(14)
  img = Image.new(640, 480)
  col = [255, 0, 255, 0]
  p = [
    [
      [58.15, 39.0],
      [58.15, 230.1],
      [171.1, 230.1],
      [171.1, 39.0],
      [58.15, 39.0]
    ],
    [
      [20.0, 240.0],
      [100.0, 300.0],
      [600.0, 400.0]
    ]
  ]

  linew = 1
  aa = 1
  aa2 = false
  list_kind = 0
  point_mv = false
  edge_only = false

  # Window.scale = 2
  Window.bgcolor = [128, 128, 128]

  Window.loop do
    break if Input.keyPush?(K_ESCAPE)

    # Eキーで塗りつぶし/エッジのみ描画を切替
    edge_only = !edge_only if Input.keyPush?(K_E)

    # 上下キーで線幅を変更
    linew += 0.5 if Input.keyPush?(K_UP)
    linew -= 0.5 if Input.keyPush?(K_DOWN)
    linew = 1 if linew < 1

    # Aキーで、ブラシ描画/アンチエイリアス描画の切り替え
    aa = ((aa == 1)? 3 : 1) if Input.keyPush?(K_A)

    # Sキーで、線幅が太い時もアンチエイリアス描画
    aa2 = !aa2 if Input.keyPush?(K_S)

    # Kキーで、座標配列を切替
    list_kind = (list_kind + 1) % p.size if Input.keyPush?(K_K)

    # Mキーで、座標のマウス移動を切替
    point_mv = !point_mv if Input.keyPush?(K_M)
    if point_mv
      p[list_kind][1][0] = Input.mousePosX
      p[list_kind][1][1] = Input.mousePosY
    end

    img.clear
    (p[list_kind].size - 1).times do |i|
      x0, y0 = p[list_kind][i]
      x1, y1 = p[list_kind][(i+1) % p[list_kind].size]
      LineDraw.draw(img, x0, y0, x1, y1, col, linew,
                    aa, 0, aa2, edge_only)
    end

    Window.draw(0, 0, img)

    s = "LineWidth=#{linew}  #{Window.real_fps} FPS  CPU: #{Window.getLoad.to_i}%"
    Window.drawFont(8, 2, s, font)
    s = "A , S , K , E , M , up , down key"
    Window.drawFont(8, 16, s, font)
  end
end
  • 上下キー : 線幅の増減
  • Kキー : 描画する形状を変更
  • Mキー : 1点だけマウスカーソルで移動
  • Aキー : (線幅が1の時) アンチエイリアスの有効無効
  • Eキー : (線幅が1より大きい時) エッジだけ描画するか、塗り潰すか
  • Sキー : (線幅が1より大きい時) アンチエイリアスの有効無効 (※ 滅茶苦茶重い)
線幅有りでアンチエイリアスを有効にすると、フリーズしたかと思うぐらいに遅くなります。ここをどうにかしたいけど…。

ソース中で、以下の描画方法を試してます。
  • DDA : 1ドット幅、アンチエイリアス無。
  • Xiaolin Wu's line algorithm : 1ドット幅、アンチエイリアス有。(xiaolinwu.rb を利用)
  • 三角形塗りつぶし+円塗りつぶし : 線幅有、アンチエイリアス無。
  • ブラシ画像 : 線幅有、なんちゃってアンチエイリアス有。
  • オーバーサンプリング : 線幅有、アンチエイリアス有。

オーバーサンプリングを使った描画は、 _アンチエイリアス付きの自由線描画(1) をそのまま Ruby で書いてみました。さすがに Ruby でやると遅い…。

swfmillでswf→xml変換したxmlを読み込んでシェイプを描画。 :

xmlの解析に、 _Nokogiri が必要です。

(※ 2014/05/30追記。Nokogiri をインストールする前に、最新版の mini_portile をインストールしないと、Nokogiri のインストールに失敗する模様 )

シェイプを描画

_shapeparse.rb
require 'nokogiri'
require 'dxruby'
require_relative 'polyfill'
require_relative 'bezierdraw'
require_relative 'linedraw'

# swfのシェイプを解析してImageに描画するクラス
class ShapeParse
  DBG = false
  DBG2 = true
  TWIP = 20.0
  attr_accessor :id
  attr_accessor :width
  attr_accessor :left
  attr_accessor :top
  attr_accessor :height
  attr_accessor :image
  attr_accessor :draw_data
  attr_accessor :clip_enable

  def initialize(node, clip_enable = true)
    self.clip_enable = clip_enable
    self.id = node["objectID"].to_i
    puts "id[#{self.id}] #{node.name}" if DBG

    n = node.at(".//bounds/Rectangle")
    self.left = n["left"].to_i / ShapeParse::TWIP
    self.top = n["top"].to_i / ShapeParse::TWIP
    self.width = n["right"].to_i / ShapeParse::TWIP
    self.height = n["bottom"].to_i / ShapeParse::TWIP
    puts "size : (x,y)=#{self.left},#{self.top} (w,h)=#{self.width},#{self.height}" if DBG

    self.width = Window.width if self.width < Window.width
    self.height = Window.height if self.height < Window.height

    self.image = Image.new(self.width, self.height)

    x0, y0 = 0, 0
    x1, y1 = 0, 0
    x2, y2 = 0, 0
    f0_enable = true
    f1_enable = true
    l_enable = true
    fc = [[0, 0, 0, 0], [0, 0, 0, 0]]
    lc = [0, 0, 0, 0]
    lwidth = ShapeParse::TWIP
    lines = []
    self.draw_data = []

    n = node.at(".//shapes/Shape/edges").child

    while n != nil
      case n.name
      when "ShapeSetup"
        unless lines.empty?
          save_draw_data(lines, f0_enable, f1_enable, l_enable,
                         lwidth, fc[0], fc[1], lc)
          lines = []
        end

        s = "#{n.name} "
        x0 = n["x"].to_f if n.key?("x")
        y0 = n["y"].to_f if n.key?("y")
        f0_enable = (n["fillStyle0"] == "1")? true : false
        f1_enable = (n["fillStyle1"] == "1")? true : false
        l_enable =  (n["lineStyle"] == "1")? true : false

        if false
          s += "(x,y)=#{x0/TWIP}, #{y0/TWIP} ("
          s += (f0_enable)? "f0 " : "   "
          s += (f1_enable)? "f1 " : "   "
          s += (l_enable)? "l" : " "
          s += ")"
          puts s
        end

        n.xpath(".//fillStyles").each do |nn|
          nn.xpath(".//Solid/color/Color").each_with_index do |m, i|
            fc[i] = get_color(m)
          end
        end

        n.xpath(".//lineStyles").each do |nn|
          nn.xpath(".//LineStyle").each do |m|
            lwidth = m["width"].to_i if m.key?("width")
            m.xpath(".//color//Color").each do |c|
              lc = get_color(c)
            end
          end
        end

        if n.key?("x") and n.key?("y")
          lines.push([x0, y0])
        end

      when "CurveTo"
        # 二次ベジェ曲線
        x1 = x0 + n["x1"].to_f
        y1 = y0 + n["y1"].to_f
        x2 = x1 + n["x2"].to_f
        y2 = y1 + n["y2"].to_f
        lst = BezierDraw.calc(x0, y0, x1, y1, x2, y2)
        lines.concat(lst.slice(1..-1))
        x0 = x2
        y0 = y2

      when "LineTo"
        # 直線
        x1 = x0 + n["x"].to_f
        y1 = y0 + n["y"].to_f
        lines.push([x1, y1])
        x0 = x1
        y0 = y1

      else
        puts "!! Unknown Tag : #{n.name}"
      end
      n = n.next
    end

    draw(false)
  end

  # 色配列を取得
  #
  # @param [Object] node 「Color」ノード
  # @return [Array] A,R,G,Bが入った配列
  #
  def get_color(node)
    col = [255, 0, 0, 0] # ARGB
    ["alpha", "red", "green", "blue"].each_with_index do |s,i|
      col[i] = node[s].to_f if node.key?(s)
    end
    return col
  end

  # 描画用の直線データを記録
  #
  # @param [Array] lines 直線データ配列
  # @param [Boolean] fe0 塗り潰し0をするか否か
  # @param [Boolean] fe1 塗り潰し1をするか否か
  # @param [Boolean] le 線を描くか否か
  # @param [Number] lw 線幅
  # @param [Array] fc0 塗り潰し0の色配列
  # @param [Array] fc1 塗り潰し1の色配列
  # @param [Array] lc1 線描画の色配列
  #
  def save_draw_data(lines, fe0, fe1, le, lw, fc0, fc1, lc)
    np = []
    ox, oy = nil, nil
    lines.each do |p|
      x, y = p
      x /= TWIP
      y /= TWIP
      np.push([x, y]) if x != ox or y != oy
      ox, oy = x, y
    end

    # 記録
    self.draw_data.push([np, fe0, fe1, le, lw, fc0.dup, fc1.dup, lc.dup])
  end

  # Imageオブジェクトに描画
  #
  # @param [Boolean] clear_enable trueならImageをクリアしてから描画
  #
  def draw(clear_enable = true, scale_x = 1.0, scale_y = 1.0)
    self.image.clear if clear_enable

    self.draw_data.each do |d|
      p, fe0, fe1, le, lw, fc0, fc1, lc = d
      np = []
      p.each { |q| np.push([q[0] * scale_x, q[1] * scale_y]) }
      draw_line(self.image, np, fe0, fe1, le, lw, fc0, fc1, lc)
    end
  end

  # 多角形を描画
  #
  # @param [Object] img Imageオブジェクト
  # @param [Array] p 頂点座標が入った配列。
  # @param [Boolean] f0_enable trueなら塗り潰し有、falseなら塗り潰し無
  # @param [Boolean] f1_enable trueなら塗り潰し有、falseなら塗り潰し無
  # @param [Boolean] l_enable trueなら線を描く、falseなら線を描かない
  # @param [Number] lwidth 線幅
  # @param [Array] fc0 色配列。A,R,G,B
  # @param [Array] fc1 色配列。A,R,G,B
  # @param [Array] lc 色配列。A,R,G,B
  #
  def draw_line(img, p, f0_enable, f1_enable, l_enable,
                lwidth, fc0, fc1, lc)
    if f0_enable and p.size >= 3
      # 塗り潰しを行う
      PolyFill.fill(img, p, fc0, fc1, self.clip_enable)
    end

    if l_enable
      # 線描画を行う
      (p.size - 1).times do |i|
        x0, y0 = p[i]
        x1, y1 = p[(i+1) % p.size]
        LineDraw.draw(img, x0, y0, x1, y1, lc, lwidth / TWIP,
                      3, 0, false, false)
      end
    end
    lines = []
  end
end

if $0 == __FILE__
  # ----------------------------------------
  # 動作テスト

  font = Font.new(14)

  # swfmillで swf → xml変換したxmlを解析

  # infile = "swfdata/test_pdr1.xml"
  infile = "swfdata/test_pdr2.xml"

  doc = Nokogiri::XML(File.open(infile)) {|cfg| cfg.noblanks}

  objs = Hash.new
  doc.xpath("//DefineShape3").each do |node|
    pdr = ShapeParse.new(node, false) # ベクターデータを解析して画像化
    objs[pdr.id] = pdr
  end

  redraw_fg = false
  ang = 0
  scale = 1.0

  Window.bgcolor = [0, 190, 118]
  Window.fps = 60

  # メインループ
  Window.loop do
    break if Input.keyPush?(K_ESCAPE)

    # Rキー押しでスケールを変えて再描画
    redraw_fg = false
    redraw_fg = true if Input.keyDown?(K_R)

    objs.each_value do |o|
      # ベクターデータを再描画できなくもないが、遅くて実用にならない
      o.draw(true, scale, scale) if redraw_fg

      Window.draw(0, 0, o.image)
    end

    s = "#{Window.real_fps} FPS / CPU: #{Window.getLoad.to_i}%"
    Window.drawFont(4, 4, s, font)

    ang += 10
    scale = Math.cos(ang * Math::PI / 180.0) + 1.5
  end
end
  • Rキー : 押してる間、ベクターデータ(シェイプ)を、大きさを変えて再描画。数秒無反応になるぐらいに遅い。
虎のxml (test_pdr2.xml) を使うと、描画されるまで数秒待たされます…。 test_pdr1.xml なら、すぐに描画されますが、それでも、60FPSでリアルタイム描画するのは全然無理でした。

一応、データ群も一緒にまとめて zip で置いときます。

_swf_shape_parse_20140529.zip (432KB)

ここまで書いといてアレだけど。 :

swfのベクターデータを描画する処理に関しては、Ruby で書いてる限り、速度の問題で、実用性は無いなと痛感していたり。Flash Player なら一瞬で表示されるものが、Ruby + DXRuby では数秒かかるわけで。自分の実装がヒドイだけかもしれませんが。

でも、描画アルゴリズムの勉強・動作確認としてはアリかなと思いつつ。

#2 [nitijyou] 自転車で買い物に

リオンドールで夜食等を購入。

ホームセンター サンデーで犬を繋ぐためのワイヤーを購入。1.3mもあればなんとかなるかなと思ったけど、帰宅後封を切ってみたら意外と短かった。

2014/05/30(金) [n年前の日記]

#1 [dxruby][cg_tools] アンチエイリアスつきで直線描画する方法で悩んでたり

_GeoGebra や Inkscape で作図しながら、どうすればアンチエイリアスつきで描画できそうか考え込んでたり。以下のような図を描いて眺めて、唸ってます。

aa_calc1.png

aa_calc4.png

aa_calc4_a.png

aa_calc4_b.png

1ドット内に含まれる形状の面積を求めて濃度にしていけばなんとかなりそうだけど、色んな形がありそうで、すっきり書けそうな気がしなくて。

図を描いてるうちに、先日試した Xiaolin Wu's line algorithm は、正確なアンチエイリアスではないように思えてきたり。例えば1ドット幅の直線なら、上下方向に3ドットまたがる時があるだろうから、Xiaolin Wu's line algorithm の上下2ドット単位の描画は合ってないよなと。もっとも、件のアルゴリズムは、高速にパッと見それらしく描画できるところがミソなのだろうと想像しているけど。

そのあたりを考えてるうちに、「そういや cairo ライブラリってあったな…。アレが使えたら悩まなくていいんじゃないか」と思いついたのだけど。導入作業だけでハマってしまったのでした。

#2 [ruby] cairo + Ruby Windows版が使えない

Windows7 x64 + Ruby 1.9.3 p545 (i386-mingw32) + cairo 1.12.9 x86-mingw32 で cairo を試してみようとしたら、cairo.so がロードできないとエラーが。
> ruby --version
ruby 1.9.3p545 (2014-02-24) [i386-mingw32]

> gem install cairo --platform x86-mingw32

> gem list | grep cairo
cairo (1.12.9 x86-mingw32)

> ruby -r cairo -e "p Cairo::VERSION"

C:/Ruby/Ruby193mingw/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- cairo.so (LoadError)
        from C:/Ruby/Ruby193mingw/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from C:/Ruby/Ruby193mingw/lib/ruby/gems/1.9.1/gems/cairo-1.12.9-x86-mingw32/lib/cairo.rb:46:in `rescue in <top (required)>'
        from C:/Ruby/Ruby193mingw/lib/ruby/gems/1.9.1/gems/cairo-1.12.9-x86-mingw32/lib/cairo.rb:42:in `<top (required)>'
        from C:/Ruby/Ruby193mingw/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:60:in `require'
        from C:/Ruby/Ruby193mingw/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:60:in `rescue in require'
        from C:/Ruby/Ruby193mingw/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:35:in `require'

ややこしいことに、Ruby 2.0.0 p451 (i386-mingw32) + cairo 1.12.9 x86-mingw32 なら使えるようで。
> pik 200
Select which Ruby you want:
1. 200: ruby 2.0.0p451 (2014-02-24) [i386-mingw32]
2. 200: ruby 2.0.0p353 (2013-11-22) [i386-mswin32_100]
?  1

> ruby --version
ruby 2.0.0p451 (2014-02-24) [i386-mingw32]

> ruby -r cairo -e "p Cairo::VERSION"
[1, 12, 16]


_[ruby-list:49399] Re: cairo 1.12.5 x86-mingw32 で cairo.so が見つからない が関係ありそうな気がする。

Rubyインストールフォルダ\lib\ruby\gems\2.0.0\gems\cairo-1.12.9-x86-mingw32\lib\ のファイル構成は以下のようになっていて。
C:\RUBY\RUBY200MINGW\LIB\RUBY\GEMS\2.0.0\GEMS\CAIRO-1.12.9-X86-MINGW32\LIB
│  cairo.rb
│
├─2.0
│      cairo.so
│
├─2.1
│      cairo.so
│
└─cairo
    │  color.rb
    │  colors.rb
    │  constants.rb
    │  context.rb
    │  device.rb
    │  paper.rb
    │  papers.rb
    │  path.rb
    │  pattern.rb
    │  point.rb
    │  surface.rb
    │
    └─context
            blur.rb
            circle.rb
            color.rb
            path.rb
            rectangle.rb
            triangle.rb

cairo.rb の中で以下の処理を通るのだけど。 Ruby 2.0.0 の場合は、2.0/cairo.so が require されるのでエラーが出ない。
begin
  major, minor, _ = RUBY_VERSION.split(/\./)
  require "#{major}.#{minor}/cairo.so"
rescue LoadError
  require 'cairo.so'
end

しかし Ruby 1.9.3 の場合は、1.9/cairo.so なんて存在しないので、require 'cairo.so' を通ってしまって、「そんなもんねえよ!」と言われるらしい。

1.9/cairo.so が入ってるバージョンが、かつては存在していたはず…と予想して、試しに古いバージョンを入れていったら、昔はたしかに 1.9/cairo.so が存在していたらしい。

過去バージョンの一覧は、 _all versions of cairo | RubyGems.org | your community gem host で見れる。色々インストールして確認してみたけど、lib/以下のフォルダの有無は以下の通り。
cairo 1.10.2 : 1.8 / 1.9
cairo 1.12.3 : 1.8 / 1.9
cairo 1.12.4 :       1.9 / 2.0

cairo 1.12.5 :       1.9 / 2.0        ※ Ruby 1.9 で動かない。
cairo 1.12.6 :       1.9 / 2.0        ※ Ruby 1.9 で動かない。
cairo 1.12.7 :       1.9 / 2.0 / 2.1  ※ Ruby 1.9 で動かない。
cairo 1.12.8 :       1.9 / 2.0 / 2.1  ※ Ruby 1.9 で動かない。
cairo 1.12.9 :           / 2.0 / 2.1  ※ Ruby 1.9 で動かない。

つまり、Ruby 1.9.x で cairo を使いたかったら、1.12.4 より前のバージョンを指定してインストールしないといかんらしいです。
gem uninstall cairo
gem install cairo --platform x86-mingw32 -v=1.12.4

_Comparing v1.12.5...master - rcairo/rcairo - GitHub を眺めていたら、Drop Ruby 1.8 support なるテキストがあった。Dec 17, 2013 の時点で Ruby 1.8非対応になった、ということだろうか…。それ以前から Ruby 1.8 用の cairo.so は同梱されてないみたいではあるけど。

#3 [ruby] うっかりRuby 1.9.3 をアンインストールしてしまった

cairo のテスト中、もしかして pik の登録状況がおかしいのかなと思えてきて。Ruby の特定バージョンをリストから削除する方法をググったのだけど、見つからず。

巷の解説ページに pik uninstall なる指定があったので試してみたら、Ruby 1.9.3 のインストールフォルダが全部削除された。やっちまった。そうじゃないんだ。違うんだ。選択リストから登録解除したかっただけで、アンインストールしたかったわけじゃない…。

てなわけで、Ruby 1.9.3 を再インストールする羽目に。

一応、pik のサブコマンド?についてメモ。
## 登録内容確認
pik list

## インストールしたRubyを登録
pik add C:\Ruby\Ruby193mingw\bin

## Rubyのバージョンを切替
pik 193

## Ruby の特定バージョンをリストから登録解除
pik remove 200
pik uninstall じゃなくて、pik remove を使うべきだった。失敗した。

Ruby/SDL、opengl、ruby-opengl をインストール :

rubysdl-2.1.1.1-mswin32-1.9.1-p243.zip をDLして解凍後、中に入って、以下を実行。
ruby install_rubysdl.rb

Ruby/SDL についてくる opengl.so が入ってると、他のライブラリを使った時に問題が出るので、以下のファイルをリネーム。
C:/Ruby/Ruby193mingw/lib/ruby/site_ruby/1.9.1/i386-msvcrt/opengl.so
opengl.so.bak とか opengl.so.prg とかそんな感じで。

opengl関係をインストール。
gem install opengl -v=0.8.0
gem install ruby-opengl -v=0.61.0
> gem list | grep "opengl\|ruby-opengl"

opengl (0.8.0 x86-mingw32)
ruby-opengl (0.61.0)
opengl は 0.9.0 が公開されてるようだけど、バージョンを 0.8.0 に決め打ちしてインストールしないと動かなかった。

#4 [ruby] Nokogiriがインストールできない

Windows7 x64 + Ruby 1.9.3 p545 (i386-mingw32) 上で Nokogiri をインストールしようとしたらエラーが出るようで。
C:\home\Downloads\DLprog\ruby\gem>gem install nokogiri --platform x86-mingw32
Temporarily enhancing PATH to include DevKit...
Building native extensions.  This could take a while...
ERROR:  Error installing nokogiri:
        ERROR: Failed to build gem native extension.

        C:/Ruby/Ruby193mingw/bin/ruby.exe extconf.rb
checking for libxml/parser.h... no
-----
libxml2 is missing.  please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.
-----
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

色々試してみたけれど、どうやら事前に mini_portile 0.6.0 をインストールしておかないとエラーがでる模様。
gem install mini_portile
gem install nokogiri
> gem list | grep "mini_portile\|nokogiri"

mini_portile (0.6.0, 0.5.3)
nokogiri (1.6.2.1 x86-mingw32)

> ruby -r nokogiri -e "p Nokogiri::VERSION"

"1.6.2.1"

#5 [anime] ライダー鎧武をそこそこ視聴

4人ライダーが出てくるあたりまで消化。まだ10数本溜まってる…。

エアーバイクが飛び交うシーンを見て感心。仮面ライダーと言えばバイク、ではあるけれど。平成ライダーは、馬や電車等、色んなモノにライドするヒーローになったわけで、もはやバイクにこだわらなくてもOKと言えばOKなのだけど。しかし、バイクに乗るという制約内でも更なる工夫のしようがあるはずだ、例えばこんなバイクとかどうよ? てな考え方ができそうな設定・シーンだなと思えたわけで。まあ、今までのライダーでもこういう設定の乗り物は存在していて、それを発展させたのではないかと想像しているのですが。何にせよ、こういうバイク設定もカッコいいなと…。

そういえば、フツーのバイクも高速走行するとワープする、てな設定だったっけ。実はこの作品、バイクというアイテムの使い方に再度こだわってみた仮面ライダー、だったりするのだろうか。見た目は、フルーツ+武者でアレだけど、バイクの活用という面では正統派ライダー、かもしれず。

病院から退院した仲間の背中を冗談めかしてポンと叩いて「痛ッ」てなアクションを取るシーンで、そのキャラの痛がるポーズだけを一瞬拡大表示して強調、かつ2回繰り返す見せ方に感心。漫画的というかアニメ的ではあるんだけど、実写でもこんな見せ方ができるんだなと。自分、そういうなんてことのないシーンでそんな表現もできるとは全然思いつかなかったわけで。何かの作品にインスパイアされた見せ方かもしれないけど、やっぱり東映ヒーロー番組のスタッフさんはアイデアマンが多くてスゲエなと…。

2014/05/31() [n年前の日記]

#1 [dxruby][cg_tools] DXRubyでswfアニメを再生する

DXRubyでswf(Flashアニメ)を再生してみたり。ビットマップ画像を表示するswfしか再生できないけど、ある程度動いてるので一応アップロード。

スクリーンショット。 :

以下が、 _元swf を、DXRuby上で再生した際のスクリーンショット。(GIFアニメにするために、減色・フレームレート・画像サイズを落としてあります)

swfparse_demo.gif
雰囲気ぐらいは伝わるかな…。

実験の動機。 :

「DXRubyに動画再生機能をつけてほしい」という要望をどこかで目にして、「動画ファイル再生は無理でも、swf再生ならできるんじゃないかな…。60FPSで画像をグリグリ動かせるんだし…」と思ったのがキッカケ。

実際使うかどうかは別にして、自分の思いつきが、本当に実現可能かどうか、なんだか気になったわけで。

処理の概要。 :

  • swfmill で swf を xml に変換。
  • Ruby + Nokogiri を使って xml を解析しつつ、DXRuby で描画。

制限事項。 :

以下は対応済み。
  • ビットマップ画像の移動・拡大縮小・回転・傾斜・半透明表示
  • 輝度変更 (明度変更)、色変更
  • 簡素な階層のスプライト (MovieClip)
  • 静止テキスト (デバイスフォント "_ゴシック", "_明朝", "_等幅" 等を指定していて、入れ子になってない簡素なもの)

以下は未対応。
  • シェイプ(ベクターデータ)
  • サウンド
  • ActionScript
  • 複雑な階層のスプライト(MovieClip)
  • テキストの拡大縮小・回転・傾斜、入れ子構造

ソースやサンプルデータ一式。ライセンス。 :

zip にして置いときます。 swfや画像のファイルサイズが大き過ぎたので、Dropbox に置いてみました。 Dropboxのサービス内容が一部変わったので自宅サーバに置き直しました。(2017/03/19)


_swf_parse_20140531.zip (67MB)

自分が書いたソースに関しては、Public Domain ってことでよろしくです。

ソース。 :

_playxmlmovie.rb
require "nokogiri"
require "dxruby"
require "find"
require 'benchmark'

# swfから変換したxmlを読み込んでDXRubyで再生するクラス
#
# @note swf から xml への変換は、swfmill を利用。
# @note DXRuby、Nokogiri が必要。
# @note License : Public domain
#
# @example
#   require "playxmlmove"
#   m = PlayXmlMovie.new("hoge.xml")
#   m.init
#   Window.loop do
#     break unless m.play
#   end
#
class PlayXmlMovie
  DBG = false
  TMDBG = false
  BMDBG = false

  # 論理ピクセル値
  TWIP = 20.0

  # ----------------------------------------
  # 色変更をするためのShader
  #
  class SpriteShader < Shader

    hlsl = <<EOS
  float4 g_mul;
  float4 g_ofs;
  texture tex0;

  sampler Samp0 = sampler_state
  {
   Texture =<tex0>;
  };

  float4 PS(float2 input : TEXCOORD0) : COLOR0
  {
    float4 output;

    output = tex2D( Samp0, input );
    output.r = (output.r * g_mul[0]) + g_ofs[0];
    output.g = (output.g * g_mul[1]) + g_ofs[1];
    output.b = (output.b * g_mul[2]) + g_ofs[2];
    output.a = (output.a * g_mul[3]) + g_ofs[3];
    return output;
  }

  technique
  {
   pass
   {
    PixelShader = compile ps_2_0 PS();
   }
  }
EOS

    @@core = Shader::Core.new(hlsl, {:g_mul => :float, :g_ofs => :float})

    def initialize
      super(@@core, "TShader")
      self.g_mul = [1.0, 1.0, 1.0, 1.0]
      self.g_ofs = [0.0, 0.0, 0.0, 0.0]
      @col = [1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]
      class << self
        protected :g_mul, :g_mul=, :g_ofs, :g_ofs=
      end
    end

    def set_color(a)
      v = 256.0
      self.g_mul = [ a[0] / v, a[1] / v, a[2] / v, a[3] / v]
      v = 255.0
      self.g_ofs = [ a[4] / v, a[5] / v, a[6] / v, a[7] / v]
      @col = a
    end
  end

  # ----------------------------------------
  # MATRIX情報格納用クラス
  #
  class TMatrix
    attr_accessor :trans_x, :trans_y
    attr_accessor :scale_x, :scale_y
    attr_accessor :skew_x, :skew_y
    attr_accessor :color

    def initialize(trans_x = 0, trans_y = 0,
                   scale_x = 1, scale_y = 1,
                   skew_x = 0, skew_y = 0,
                   mul_r = 256, mul_g = 256, mul_b = 256, mul_a = 256,
                   ofs_r = 0, ofs_g = 0, ofs_b = 0, ofs_a = 0)
      self.trans_x, self.trans_y = trans_x,trans_y
      self.scale_x, self.scale_y = scale_x, scale_y
      self.skew_x, self.skew_y = skew_x, skew_y
      self.color = [ mul_r, mul_g, mul_b, mul_a, ofs_r, ofs_g, ofs_b, ofs_a]
    end

    #
    # 属性値を更新
    #
    # @param [Hash] v 属性と値のハッシュ
    #
    def set_value(v)
      self.trans_x = v["transX"].to_f if v.has_key?("transX")
      self.trans_y = v["transY"].to_f if v.has_key?("transY")
      self.scale_x = v["scaleX"].to_f if v.has_key?("scaleX")
      self.scale_y = v["scaleY"].to_f if v.has_key?("scaleY")
      self.skew_x = (v.has_key?("skewX"))? v["skewX"].to_f : 0
      self.skew_y = (v.has_key?("skewY"))? v["skewY"].to_f : 0

      [
        "factorRed", "factorGreen", "factorBlue", "factorAlpha",
        "offsetRed", "offsetGreen", "offsetBlue", "offsetAlpha"
      ].each_with_index do |s, i|
        self.color[i] = v[s].to_f if v.has_key?(s)
      end
    end

    #
    # 座標変換
    #
    # @param [Number] x x座標
    # @param [Number] y y座標
    # @return [Array] 座標変換後のx,y座標
    #
    def trans(x, y)
      nx = (x * self.scale_x) + (y * self.skew_y) + self.trans_x
      ny = (x * self.skew_x) + (y * self.scale_y) + self.trans_y
      return nx, ny
    end

    #
    # 色変換パラメータを更新
    #
    # @param [Array] c 色変更情報の配列
    # @return [Array] 変更後の色変更情報配列
    #
    def trans_color(c)
      nc = Array.new(8)
      for i in 0..3 do
        nc[i] = c[i] * self.color[i] / 256.0
        nc[i] = 512 if nc[i] > 512
        nc[i] = -512 if nc[i] < -512
      end
      for i in 4..7 do
        nc[i] = c[i] + self.color[i]
        nc[i] = 255 if nc[i] > 255
        nc[i] = -256 if nc[i] < -256
      end
      return nc
    end

    #
    # 色変換が必要かどうかを返す
    #
    # @param [Array] c 色変換情報配列
    # @return [Boolean] trueなら色変換が必要。falseなら色変換は不要。
    #
    def TMatrix.trans_color?(c)
      [
        [0, 256.0], [1, 256.0], [2, 256.0],
        [4, 0.0], [5, 0.0], [6, 0.0]
      ].each do |n|
        return true if c[n[0]] != n[1]
      end
      return false
    end

    #
    # 色変換情報を格納するハッシュを初期化する
    #
    # @param [Hash] v 色変換情報格納用ハッシュ
    # @return [Hash] 初期化したハッシュを返す
    #
    def TMatrix.init_color(v)
      v.merge!({ "factorRed" => 256, "factorGreen" => 256,
          "factorBlue" => 256, "factorAlpha" => 256,
          "offsetRed" => 0, "offsetGreen" => 0,
          "offsetBlue" => 0, "offsetAlpha" => 0 })
      return v
    end
  end

  # ----------------------------------------
  # 画像を格納するキャラobjクラス
  #
  class CharaObjImage
    attr_accessor :id, :src, :image

    def initialize(id, fname)
      self.id = id
      self.src = fname
      self.image = Image.load(fname)
      # puts "id[#{id}] Load Image ok #{fname}"
    end
  end

  # ----------------------------------------
  # シェイプを格納するクラス
  #
  class CharaObjShape
    attr_accessor :id, :imgs

    def initialize(id, imgs)
      self.id = id
      self.imgs = imgs
    end
  end

  # ----------------------------------------
  # スプライト(MovieClip)を格納するクラス
  #
  class CharaObjSprite
    attr_accessor :id
    attr_accessor :start_node

    def initialize(id, start_node)
      self.id = id
      self.start_node = start_node
    end
  end

  # ----------------------------------------
  # フォント名を格納するクラス
  #
  class CharaObjFont
    attr_accessor :id, :fontname

    def initialize(id, fontname)
      self.id = id
      self.fontname = fontname
      puts "id[#{id}] font [#{fontname}]" if PlayXmlMovie::DBG
    end
  end

  # ----------------------------------------
  # テキストを格納するクラス
  #
  class CharaObjText
    attr_accessor :id, :font, :text, :size, :fontname, :col

    def initialize(id, text, size, fontname, col = [0, 0, 0, 255])
      self.id = id
      self.text = text
      self.size = size
      self.fontname = fontname
      self.font = Font.new(self.size, fontname)
      self.col = col
      if PlayXmlMovie::DBG
        puts "id[#{id}] DefineEditText \"#{text}\",#{size},\"#{fontname}\""
      end
    end
  end

  # ----------------------------------------
  # 描画用クラス
  #
  class MySprite

    attr_accessor :objs
    attr_accessor :cobj
    attr_accessor :pobj
    attr_accessor :z
    attr_accessor :mat
    attr_accessor :sprs
    attr_accessor :rt
    attr_accessor :shader
    attr_accessor :enable_loop

    # コンストラクタ
    #
    # @param [Hash] objs キャラobj情報が入ったハッシュ
    # @param [Number] cid 発生させたいキャラobjのID
    # @param [Object] pobj 親スプライト。nilなら親は居ない
    # @param [Number] depth 描画深度
    # @param [Hash] v MATRIX情報+半透明度を格納したハッシュ
    #
    def initialize(objs, cid, pobj, depth, v)
      self.objs = objs
      self.cobj = self.objs[cid]
      self.pobj = pobj
      self.z = depth
      self.mat = TMatrix.new
      self.sprs = Hash.new
      self.rt = nil
      self.shader = nil
      update(depth, v)
    end

    # 描画に必要な値を設定
    #
    # @param [depth] depth 描画深度
    # @param [Hash] v MATRIX情報と半透明度情報を格納したハッシュ
    #
    def update(depth, v)
      self.z = depth
      self.mat.set_value(v)
    end

    # RenderTargetを解放
    #
    def dispose
      if self.sprs != nil
        self.sprs.keys {|k| self.sprs[k].dispose }
      end

      if self.rt != nil
        self.rt.dispose unless self.rt.disposed?
      end
    end

    # 拡大縮小、半透明、回転描画
    #
    # @param [Object] img Imageクラス
    # @param [Object] im_mat TMatrixクラス
    #
    def draw_image(img, img_mat)
      pobj = self.pobj

      # 矩形の4点を配列にする
      w = img.width
      h = img.height
      p = [0, 0, w, 0, w, h, 0, h]

      c = Marshal.load(Marshal.dump(self.mat.color))

      # Shapeに割り当てられたMATRIXで変形
      4.times do |i|
        p[i * 2], p[i * 2 + 1] = img_mat.trans(p[i * 2], p[i * 2 + 1])
      end

      # 自身のMATRIXで変形
      4.times do |i|
        p[i * 2], p[i * 2 + 1] = self.mat.trans(p[i * 2], p[i * 2 + 1])
      end

      # 親スプライトのMATRIXで変形
      while pobj != nil
        4.times do |i|
          p[i * 2], p[i * 2 + 1] = pobj.mat.trans(p[i * 2], p[i * 2 + 1])
        end
        c = pobj.mat.trans_color(c)
        pobj = pobj.pobj
      end

      # 論理ピクセル値から画面座標に変換
      8.times {|i| p[i] /= PlayXmlMovie::TWIP }

      if TMatrix.trans_color?(c)
        # 色変換が必要
        # 色変更用のShaderはアルファチャンネル付き画像描画がおかしいので、
        # 色変更が必要な時だけ使うことにする。
        self.rt = RenderTarget.new(Window.width, Window.height) if self.rt == nil
        self.shader = SpriteShader.new if self.shader == nil
        self.rt.drawMorph(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
                          img, :dividex => 3, :dividey => 3)
        self.rt.update
        self.shader.set_color(c)
        Window.drawEx(0, 0, self.rt, :shader => self.shader)
      else
        # 色変換は不要
        a = (255 * c[3] / 256.0).to_i
        if a != 255
          # 半透明描画
          Window.drawMorph(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
                           img, :dividex => 3, :dividey => 3,
                           :alpha => a, :blend => :alpha)
        else
          # 不透明描画
          Window.drawMorph(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
                           img, :dividex => 3, :dividey => 3)
        end
      end
    end

    # フォントを描画
    #
    # @param [String] text 描画文字列
    # @param [Object] font Fontオブジェクト
    # @param [Array] c RGBA配列
    #
    def draw_font(text, font, c)
      x = self.mat.trans_x / PlayXmlMovie::TWIP
      y = self.mat.trans_y / PlayXmlMovie::TWIP
      Window.drawFontEx(x, y, text, font,
                        :color => [c[0], c[1], c[2]],
                        :alpha => c[3], :blend => :alpha, :aa => true)
    end

    # 再生描画処理
    #
    # @param [Hash] objs キャラobj情報が入ったハッシュ
    # @return [Boolean] trueなら再生中, falseなら再生終了
    #
    def play
      return true
    end
  end

  # ----------------------------------------
  # 画像描画obj
  #
  class ImageObj < MySprite

    # コンストラクタ
    #
    # @param [Hash] objs キャラobj情報が入ったハッシュ
    # @param [Number] cid 発生させたいキャラobjのID
    # @param [Object] pobj 親スプライト。nilなら親は居ない
    # @param [Number] depth 描画深度
    # @param [Hash] v MATRIX情報+半透明度を格納したハッシュ
    #
    def initialize(objs, cid, pobj, depth, v)
      super(objs, cid, pobj, depth, v)
    end

    # 再生描画処理
    #
    # @return [Boolean] trueなら再生中, falseなら再生終了
    #
    def play
      draw_image(self.cobj.image, TMatrix.new)
      return true
    end
  end

  # ----------------------------------------
  # シェイプ描画obj
  #
  class ShapeObj < MySprite

    # コンストラクタ
    #
    # @param [Hash] objs キャラobj情報が入ったハッシュ
    # @param [Number] cid 発生させたいキャラobjのID
    # @param [Object] pobj 親スプライト。nilなら親は居ない
    # @param [Number] depth 描画深度
    # @param [Hash] v MATRIX情報+半透明度を格納したハッシュ
    #
    def initialize(objs, cid, pobj, depth, v)
      super(objs, cid, pobj, depth, v)
    end

    # 再生描画処理
    #
    # @return [Boolean] trueなら再生中, falseなら再生終了
    #
    def play
      self.cobj.imgs.each do |v|
        draw_image(v[0].image, v[1])
      end
      return true
    end
  end

  # ----------------------------------------
  # スプライト描画obj
  #
  class SpriteObj < MySprite
    attr_accessor :node
    attr_accessor :now_frame
    attr_accessor :enable_loop

    # コンストラクタ
    #
    # @param [Hash] objs キャラobj情報が入ったハッシュ
    # @param [Number] cid 発生させたいキャラobjのID
    # @param [Object] cobj 発生させたいキャラobj
    # @param [Object] pobj 親スプライト。nilなら親は居ない
    # @param [Number] depth 描画深度
    # @param [Hash] v MATRIX情報+半透明度を格納したハッシュ
    # @param [Boolean] looped trueならループ再生
    #
    def initialize(objs, cid, pobj, depth, v, looped = true)
      super(objs, cid, pobj, depth, v)
      self.node = cobj.start_node
      self.now_frame = 0
      self.enable_loop = looped
    end

    # 再生描画処理
    #
    # @return [Boolean] trueなら再生中, falseなら再生終了
    #
    def play
      pobj = self
      endfg = false

      until endfg

        case self.node.name
        when "PlaceObject2"
          # シェイプやスプライトを配置、もしくは移動
          nn = self.node
          rn = nn["replace"].to_i
          depth = nn["depth"].to_i
          id = nn["objectID"]
          id = id.to_i if id != nil

          # 属性を取り出す
          v = {}
          nn.children.each do |n|
            n.children.each do |m|
              if m.name == "Transform"
                # 変形情報
                m.keys.each do |k|
                  a = m.attribute(k)
                  v[a.name] = a.value.to_f
                end
              elsif m.name == "ColorTransform2"
                # 色変換情報
                v = TMatrix.init_color(v)
                m.keys.each do |k|
                  a = m.attribute(k)
                  v[a.name] = a.value.to_f
                end
              end
            end
          end

          if id != nil
            abort "Error: Not found ChrObj[#{id}]" unless self.objs.has_key?(id)

            case self.objs[id]
            when PlayXmlMovie::CharaObjImage
              self.sprs[depth] = ImageObj.new(self.objs, id, pobj, depth, v)
            when PlayXmlMovie::CharaObjShape
              self.sprs[depth] = ShapeObj.new(self.objs, id, pobj, depth, v)
            when PlayXmlMovie::CharaObjSprite
              self.sprs[depth] = SpriteObj.new(self.objs, id, pobj, depth, v)
            when PlayXmlMovie::CharaObjText
              self.sprs[depth] = TextObj.new(self.objs, id, pobj, depth, v)
            end
          elsif rn == 1
            self.sprs[depth].update(depth, v) if self.sprs.has_key?(depth)
          end

          if TMDBG
            s = "PLACE, replace=#{rn}, depth=#{depth}, id=#{id}, {"
            v.each_key {|k| s += "#{k} => #{v[k]}, "}
            s += "}"
            puts s
          end

        when "RemoveObject2"
          # シェイプやスプライトを削除
          depth = node["depth"].to_i
          if self.sprs.has_key?(depth)
            self.sprs[depth].dispose
            self.sprs.delete(depth)
          end
          puts "REMOVE, depth=#{depth}" if TMDBG

        when "ShowFrame"
          # 現在フレームを描画
          self.now_frame += 1
          puts "ShowFrame #{now_frame}" if TMDBG
          endfg = true

        when "End"
          # タグが無くなった
          endfg = true
          puts "End" if TMDBG
          break

        else
          # puts node_name
        end

        # 次のノードを読む
        self.node = self.node.next
      end

      # ウインドウに描画
      key_list = self.sprs.keys.sort # depthでソート
      key_list.each {|k| self.sprs[k].play }

      return true if self.node.name != "End"

      # 再生終了
      sprs = Hash.new
      if self.enable_loop
        self.node = self.cobj.start_node
        self.now_frame = 0
      end
      return false
    end
  end

  # ----------------------------------------
  # テキスト描画obj
  #
  class TextObj < MySprite

    # コンストラクタ
    #
    # @param [Hash] objs キャラobj情報が入ったハッシュ
    # @param [Number] cid 発生させたいキャラobjのID
    # @param [Object] pobj 親スプライト。nilなら親は居ない
    # @param [Number] depth 描画深度
    # @param [Hash] v MATRIX情報+半透明度を格納したハッシュ
    #
    def initialize(objs, cid, pobj, depth, v)
      super(objs, cid, pobj, depth, v)
    end

    # 再生描画処理
    #
    # @return [Boolean] trueなら再生中, falseなら再生終了
    #
    def play
      o = self.cobj
      draw_font(o.text, o.font, o.col)
      return true
    end
  end

  # ----------------------------------------
  # コンストラクタ
  #
  # @param [String] infile xmlファイルのパス
  #
  def initialize(infile)
    @infile = infile
    abort "Error : Not found #{infile}" unless File.exist?(infile)

    if BMDBG
      Benchmark.bm(15) do |x|
        x.report("xml parse:") {
          @doc = Nokogiri::XML(File.open(infile)) {|config| config.noblanks}
        }

        x.report("get fps") {
          @framerate, @frames = get_framerate(@doc)
          @wdw_w, @wdw_h = get_widnow_size(@doc)
          @bgcol = get_bgcolor(@doc)
        }

        @objs = Hash.new

        x.report("load image:") { load_image(@doc, infile) }
        x.report("load font:") { load_font(@doc) }
        x.report("load shape:") { load_shape(@doc) }

        x.report("load sprite:") {
          load_sprite(@doc)
          n = @doc.at("//swf/Header/tags/PlaceObject2")
          @objs[0] = CharaObjSprite.new(0, n)
        }

      end
    else
      # xmlを解析
      @doc = Nokogiri::XML(File.open(infile)) {|config| config.noblanks}
      @framerate, @frames = get_framerate(@doc)
      @wdw_w, @wdw_h = get_widnow_size(@doc)
      @bgcol = get_bgcolor(@doc)

      # 画像、シェイプ、スプライトを登録
      @objs = Hash.new
      load_image(@doc, infile)
      load_font(@doc)
      load_shape(@doc)
      load_sprite(@doc)

      # rootスプライトを登録
      puts "\nid[0] DefineSprite" if DBG
      n = @doc.at("//swf/Header/tags/PlaceObject2")
      @objs[0] = CharaObjSprite.new(0, n)
    end

  end

  #
  # フレームレート、フレーム数を取得
  #
  # @param [Object] doc xmlノード情報
  # @return [Array] フレームレート, フレーム数を配列で返す
  #
  def get_framerate(doc)
    n = doc.at("//Header")
    fpsv = n["framerate"].to_i
    frms = n["frames"].to_i
    puts "framerate=#{fpsv} , frames=#{frms}" if DBG
    return fpsv, frms
  end

  #
  # 画面サイズを取得
  #
  # @param [Object] doc xmlノード情報
  # @return [Array] 画面横幅, 画面縦幅を配列で返す
  #
  def get_widnow_size(doc)
    n = doc.at("//Header/size/Rectangle")
    # x = n["left"].to_i / TWIP
    # y = n["top"].to_i / TWIP
    w = n["right"].to_i / TWIP
    h = n["bottom"].to_i / TWIP
    puts "Window size : #{w}, #{h}" if DBG
    return w, h
  end

  #
  # 背景色を取得
  #
  # @param [Object] doc xmlノード情報
  # @return [Array] 背景色red, green, blueを配列で返す
  #
  def get_bgcolor(doc)
    n = doc.at("//SetBackgroundColor/color/Color")
    c = []
    ["red", "green", "blue"].each {|k| c.push(n[k].to_i)}
    return c
  end

  #
  # 画像をロード
  #
  # @param [Object] doc xmlノード情報
  # @param [String] infile xmlファイルパス
  #
  def load_image(doc, infile)
    imglist = get_img_file_list(infile) # 画像ファイルパスの一覧を取得

    # 検索するタグ名を列挙
    taglst = "//DefineBitsJPEG2|//DefineBitsLossless2|//DefineBitsLossless"

    doc.xpath(taglst).each do |n|
      id = n["objectID"].to_i
      n.content = ""

      unless imglist.has_key?(id)
        # IDと一致する画像ファイルパスが存在しない
        puts "Not found image file : id = #{id}"
        next
      end

      # IDと一致する画像ファイルパスが存在する
      fn = imglist[id]
      n["src"] = fn
      @objs[id] = CharaObjImage.new(id, fn)
      puts "id[#{id}] Load Image : #{fn}" if DBG
    end
  end

  #
  # xmlファイルパスを元にして、全画像ファイルのパス一覧を取得
  #
  # @param [String] xmlファイルパス
  # @return [Hash] 画像ファイルパスを格納したハッシュ
  #
  def get_img_file_list(infile)
    bsn = File.basename(infile, ".xml") # ファイルの拡張子を取り除いた文字列を取得
    dir = File::dirname(infile) # ファイルのフォルダパスを取得
    imglist = {}

    # サブフォルダ以下も探索して、拡張子が画像ファイル形式なら記録していく
    Find.find(dir) do |f|
      next unless FileTest.file?(f)
      imglist[$1.to_i] = f if /#{bsn}\[(\d+)\]\.(png|jpg|bmp|gif)/ =~ f
    end

    return imglist
  end

  #
  # フォント情報やテキスト情報を読み込み
  #
  # @param [Object] doc xmlノード情報
  #
  def load_font(doc)
    lst = Hash.new
    lst.merge!(
      {
        "_ゴシック" => "MS UI Gothic",
        "_明朝" => "MS P明朝",
        "_等幅" => "MS ゴシック",
        "_sans" => "Arial",
        "_serif" => "Times New Roman",
        "_typewriter" => "Courier"
      })

    # 使用している全フォント名を取得
    doc.xpath("//DefineFont2|//DefineFont3").each do |n|
      id = n["objectID"].to_i
      fnt = n["name"].encode("Windows-31J") # xmlはutf-8なのでsjisに変換
      if lst.has_key?(fnt)
        fnt = lst[fnt]
        @objs[id] = CharaObjFont.new(id, fnt)
      else
        @objs[id] = CharaObjFont.new(id, fnt)
      end
    end

    # 使用している静止テキスト情報を取得
    doc.xpath("//DefineEditText").each do |n|
      id = n["objectID"].to_i
      fid = n["fontRef"].to_i # フォント種類ID
      abort "Error: Not found font [#{fid}]" unless @objs.has_key?(fid)

      size = n["fontHeight"].to_f / PlayXmlMovie::TWIP

      # Flash CS5で作成したswfは、initialText に html が入っている。
      # htmlタグを除去して描画文字列を取得する
      nm = Nokogiri::HTML(n["initialText"])
      text = nm.content.encode("Windows-31J")

      col = [0, 0, 0, 255]
      c = n.at(".//color/Color")
      if c != nil
        col[0] = c["red"].to_i
        col[1] = c["green"].to_i
        col[2] = c["blue"].to_i
        col[3] = c["alpha"].to_i
      end
      fontname = @objs[fid].fontname
      @objs[id] = CharaObjText.new(id, text, size, fontname, col)
    end
  end

  #
  # 画像を内包するShapeを生成
  #
  # @param [Object] doc xmlノード情報
  #
  def load_shape(doc)
    doc.xpath("//DefineShape|//DefineShape2").each do |n|
      id = n["objectID"].to_i
      sn = n.xpath(".//bounds/Rectangle")[0]
      # x = sn["left"].to_i / TWIP
      # y = sn["top"].to_i / TWIP
      w = sn["right"].to_i / TWIP
      h = sn["bottom"].to_i / TWIP

      imgs = []
      bn = n.xpath(".//styles/StyleList/fillStyles")
      bn.xpath(".//ClippedBitmap|.//ClippedBitmap2").each do |m|
        img_id = m["objectID"].to_i
        if img_id == 65535
          # objectID が 65535 の時がある。正体不明
          puts "id[#{id}] Unknown Image [#{img_id}]" if DBG
          next
        end

        unless @objs.has_key?(img_id)
          # 画像が存在しない
          abort "Error : id[#{id}] Not found Image [#{img_id}] #{w} x #{h}"
        end

        # 画像が存在するので、シェイプに割り当てる
        # MATRXI情報も反映させる
        puts "id[#{id}] Shape = bitmap[#{img_id}]" if DBG

        dic = {}
        mn = m.at(".//matrix/Transform")
        mn.keys.each do |k|
          atr = mn.attribute(k)
          dic[atr.name] = atr.value.to_f
        end
        v = TMatrix.new
        v.set_value(dic)
        imgs.push([@objs[img_id], v])
      end
      @objs[id] = CharaObjShape.new(id, imgs)
    end
  end

  #
  # スプライトを登録
  #
  def load_sprite(doc)
    doc.xpath("//DefineSprite").each do |n|
      id = n["objectID"].to_i
      frames = n["frames"].to_i
      puts "\nid[#{id}] DefineSprite, frames=#{frames}" if DBG
      node = n.at(".//PlaceObject2")
      @objs[id] = CharaObjSprite.new(id, node)
    end
  end

  # 再生開始のための初期化処理
  #
  # @note fpsや背景色が変更されるので、再生が終わったら本来のfps・背景色に戻すこと
  #
  # @param [Boolean] init_size ウインドウサイズをxmlに従って変更するかどうか
  # @param [Boolean] init_fps フレームレートをxmlに従って変更するかどうか
  # @param [Boolean] init_bgcol 背景色をxmlに従って変更するかどうか
  #
  def init(init_size = false, init_fps = true, init_bgcol = true)
    @now_frame = 0
    @root_sprite = SpriteObj.new(@objs, 0, nil, 0, {})

    Window.resize(@wdw_w, @wdw_h) if init_size
    Window.fps = @framerate if init_fps
    Window.bgcolor = [@bgcol[0], @bgcol[1], @bgcol[2]] if init_bgcol
  end

  # 再生描画処理
  #
  # @note メインループから毎フレーム呼ぶこと
  #
  # @return [Boolean] trueなら再生中, falseなら再生終了
  #
  def play
    return @root_sprite.play
  end

  #
  # 多少整理したxmlを標準出力にダンプする
  #
  def dump
    puts @doc.to_xml
  end

end

if $0 == __FILE__
  # ----------------------------------------
  # 使い方の例

  m = PlayXmlMovie.new("swfdata/movie0.xml")
  # m = PlayXmlMovie.new("swfdata/movie1.xml")
  # m = PlayXmlMovie.new("swfdata/movie2.xml")
  # m = PlayXmlMovie.new("swfdata/movie3.xml")
  # m = PlayXmlMovie.new("swfdata/movie4.xml")

  # 再生開始のための初期化
  # fpsや背景色が変更されるので、
  # 再生が終わったら本来のfps・背景色に戻すこと
  m.init

  # メインループ
  Window.loop do
    break if Input.keyPush?(K_ESCAPE) # ESCキーで終了

    # 1フレーム分描画
    unless m.play
      m.init # 再生が終了したら、再度頭から再生
    end

  end
end

使い方の例。
require_relative "playxmlmovie"

m = PlayXmlMovie.new("hoge.xml")
m.init

Window.loop do
    break unless m.play
end

その他詳細は、 _readme.html を読んでください。

備考。 :

サンプルswf内の静止画像作成については、 _マウスコンピューター G-Tuneシリーズ公式キャラクター G-Tuneちゃん (Tuneちゃん?) を使わせてもらいました。…と書いておけばいいのかな? リンクも貼ってあるから、これで利用条件は満たしてるはず。たぶん。

ここまで書いといてアレだけど。 :

自分でコレを使うかというと、たぶん使わないような気がする…。たしかにグリグリ動いたけど…。

ただ、コレを書いてみたことで、巷で見かける swf → HTML5 + JavaScript 変換ツールが、大体どんな感じのことをしているのか、そのあたりぼんやり分かってきたように思います。結構勉強になったというか。

Ruby でSWFバイナリを直接扱える、 _SwfRuby なるライブラリも使えたら、swfmill で一旦xmlに変換しなくてもいいのかもしれんですけど。
  • SwfRuby は RMagick が必要。
  • しかし、Windows で RMagick を導入するのは面倒。ImageMagick の特定バージョンを要求される。
という問題があるので…。そう考えると、xml に変換しといてソレを渡す、こういうやり方もアリかなと。xmlをエディタで開けば、どこで何の指定がされてるか調べられるというメリットもありますし。

2017/03/19追記。 :

DropboxのPublicフォルダが死んだのでファイルを置き直し。

#2 [flash][neta] Flashについて(余計な)一言言っとくか

「今頃Flashかよwww」と言い出す人が居そうな気もするので、一応、今の自分の考えをメモ。

Flashにも色々あって。 :

Flashにも色々あって。
  • 最終的な再生ファイル形式としての Flash (swf)
  • 中間ファイルとしての Flash (swf)
  • アニメ制作アプリとしての Flash
  • デスクトップアプリを作成できる基幹技術としての Flash (Adobe AIR)
  • その他色々。
複数の定義があり得るので、分けて考えないといかんよなと。

Webブラウザ上で再生するための Flash (swf) は、たしかに死滅寸前だと自分も思っているのですが。
  • 皆が持ってるスマホのブラウザ上で再生できないってのは、やっぱり痛い。
  • アニメを再生したいなら、そもそも動画ファイルを再生しちゃえばいい。
だから、そういう定義の Flash なら、たしかに「今頃」だよなと。

しかし、「簡易アニメ制作ソフト」としての Flash や、別の最終フォーマットに変換するための「中間ファイル」としての Flash (swf)は、今でも使えるわけで。

例えばの話。psdファイルはブラウザで開けないけど。だからと言って「Photoshopは死んだ」と口走ってたら馬鹿丸出し。同様に、制作環境としての Flash は、今後も(しばらくは)選択肢としてフツーにアリで。Webブラウザで開けないからソレは死んだ、てな結論は、あまりに視野が狭過ぎる。

それに、昔は Adobe製の有償製品しか無かったですけど、今はフリーで使えるFlash制作ソフトも存在してますから、お金の無い学生さんにも優しいし。これがもし、HTML5 + JavaScript用の何かを吐き出してくれる簡易アニメ制作ソフトだったら、Flash と比べたら、まだちょっと弱いよなと。

とは言え、ならばFlashがアニメ制作ツールとして至高の存在かと問われたら、自分は首を捻ってしまうのですけど。どうしてあんなに使いづらいのですかね…。たぶん、絵描きさんの視点で作ってないよな…。古い版しか触ったことないけど、最新版は改良されてるのだろうか…。

何が作れるか云々。 :

「Flashでろくなアニメなんか作れるわけねえだろwww」とdisる人も居るかもしれないなと。そういう人は、URA氏がディレクターをした「猫物語」OPや「ニセコイ」の2番目のOPを見るべき。 *1 有名アニメーターのりょーちも氏の仕事ぶりも確認すべき。

「Flashでろくなアニメなんか」発言は、「Photoshopを買えばボクもイラストレーターになれる」発言と大差ないよなと。どのツールを使うかで作品の中身が劇的に変わるようなら誰も苦労しねえんだよクソが! と思うわけですよ。ツールの選択で変わってくるのは、作業効率ぐらいなもんでして。

これがもし、「Flashでアニメ作るの結構シンドイよ」という発言だったら、自分もちょっと同意するかも。なんだかサクサクと作業できないというか、トライ&エラーが面倒臭いUI仕様があちこちに点在していて…。

JavaScriptも死にかけてましたな。 :

JavaScript の歴史を思い返せば、「今頃Flashかよ」発言って危ないよなと。一時期、JavaScript って死滅寸前でしたよ。まさかこういう使い方もできたとは。当時 JavaScript をdisってた人達って、一体何なんだろうと…。自分もその中に含まれてますけど。

なので、Flash も、「え? そんな場所で、そんな使い方が?」てな展開があるかもしれないなと。そもそも、最初はアニメ制作ソフトだったはずが、どうして ActionScript が云々、てな話に…。既に現状ですら、俺の知ってる Flash と違う…。

それにしても、HTML5 + JavaScript で書いた何かしらが、ブラウザの互換性の問題で大変なことになってる話を見かけるたびに、「Flash が使えたら、こんなことにはならなかったのに…」と思えてしまって。Adobeだけが互換性問題で苦労して、こっちは楽ができたのになあ。ジョブズを筆頭に、無意味に Flash をdisってた人達のせいで、より多くのプログラマー・デザイナーが地獄を見る展開に。

*1: 今もFlash使ってるか分からんし、もしかするとAEなのかなと思わないでもないけど、動かし方やシルエットで見せていくソレはどう見てもFlashアニメ文化だよなと。

この記事へのツッコミ

はじめまして by hoshi    2014/06/04 13:54
hoshi(またはhoshi-sano)と申します。
「DXRubyで動画が再生できたら云々」などとこっそり発言していた一人です。
いつも日記の更新を楽しみにさせていただいています。

Flashは一時期結構使っていただけに、衰退しつつあるのは私も残念に思います。
はやりの「艦これ」がFlashで動作したりとまだ完全に死に向かっているわけではなさそうですが、
だからといって第一線に戻れそうな空気でもないですよね。

DXRubyでのswf再生の一連の日記、まだ細部は読めていませんがすごくワクワクしました。
特に何か意見があるというわけでないのですが、この一連の日記ですごい喜んだ奴がいるということをお伝えしたかった次第でした。

#3 [cg_tools] Google Web Designer、Adobe Edge Animate を試用

前々からどういうツールか気になってたので試用してみようかなと。ちなみに環境は、Windows7 x64。

結論から先に書く。 :

個人的に、この手のツールは以下の2点が気になっていて。
  • 連番画像をレンダリングできるか。これができれば、動画編集ソフト等に渡す素材や、スプライトシートが作れる。
  • 出力したファイルを、Webブラウザ以外でも再生することができそうか。
要するに、他の何かと連携できるかどうかが気になるわけで。

連番画像を出力する機能はどちらも持ってなかった。しかしこれは、ツールの性質からして仕方ないのかも。

これらのツールは、Flashと違って、タイムラインには時間しか表示されてなくて。つまり、「フレーム」「コマ」という概念が無い。フレームが無いのだから、フレームを連番画像として出力できるはずもなく。 *1 なので、現状では本質的に、既存のアニメ制作ツールと異なる存在かもしれないなと。「アニメ制作ツール」として紹介されることが多いけど、コレは、アニメ映像ではないアニメを作るツールではないかと。

また、出力ファイルは html だったり js だったりするのだけど…。出力ファイルは難読化がされていたりして、さっぱり読めないのだけど。作業ファイル自体は難読化がされてないので、まだなんとか頑張れば解析できそうな気もしたり。

Google Web Designerを試用した際のメモ。 :

_Google Web Designer から、googlewebdesigner_win.exe をDL。897KBのファイルなので、たぶんダウンローダーなのだと思う。実行したら、おそらくは本体のダウンロードとインストールが行われた。

インストール直後の現在のバージョンは、1.0.6.0428。

アニメ作成作業は簡単。
  • 下のサムネイル横の「+」ボタンを押していけばキーフレームを増やせる。
  • そのキーフレームでオブジェクトの位置変更等をすれば、その間の位置移動等を補間してくれる。
  • サムネイルとサムネイルの間のボタンを押せば、何秒間、どんな動きをさせるのかグラフ表示してくれる。
  • 秒数の変更もできるし、グラフ部分をマウスでクリックすれば動きの種類も順々に変わっていく。
最初はクイックモード?とやらで作業してたけど、下の方に小さく「詳細」と書かれたボタンをクリックしたら、クイックモードが解除されてタイムライン表示がされるようになった。各オブジェクトがレイヤーのような状態でタイムラインに表示されてるので、このほうが分かりやすいような気もする。
  • タイムライン上で右クリックすれば、キーフレームの挿入、動きの種類の変更などができる。
  • タイムライン上でキーフレームのマークをドラッグすれば、キーフレーム位置を変更できる。

ファイル → 公開を選べば、htmlファイルとして保存できる。何を出力してくれるのか、そこがそもそも分かってなかったのだけど。どうやらhtmlファイルを1つ出力するツールだった模様。

出力されたhtmlを眺めてゲロを吐きそうに。チンプンカンプン。

作業用に保存される html ファイルをぼんやり眺めていたら、なんだかちょっと分かってきた。
  • @-webkit-keyframes なる記述がキーフレームの情報だと予想。
  • キーフレームに、12.7451% とか 49.0196% とか書いてあるのは、全体時間に対してどこにキーフレームがあるのかを割合で示しているのではないか。
  • html本体部分の canvas てのがシェイプに相当するらしい。シェイプの形状はcssで定義。
よくよく見てみると、「公開」で出力されたhtmlファイルも、css部分に、アニメをするために必要な情報が列挙されているように思えてきた。css内がデータみたいなものなのかな…。

解析してどうにかしようという気にはならないけれど、やろうと思えばどうにかできるのかもしれない。バイナリファイルではなくテキストファイルで保存されてると、こういう時にありがたい。今時、バイナリファイルで出力することは極力避けるべきなのだと再認識しました。

連番画像の出力機能は無し。

現状、広告制作ツールのベータ版として公開されているのだし、CGツールとして捉えるほうが間違ってる気もしてきたり。見た目それっぽいけど、ヨチヨチ歩きみたいなものだよな…。どこかのまとめサイトで、「GoogleがAdobeとWebデザイナー殺しに来た」なんて煽りタイトル記事を見かけたけど。仮にそうだとしても、幼児が水鉄砲でピュピュッと撃ってる段階のような…。今後の成長に期待、みたいな。

Adobe Edge Animate を試用した際のメモ。 :

最新版の Adobe Edge Animate CC はおそらく有償ソフトらしいのだけど。Adobe Edge Animate 1.5 までは、Adobe ID を持ってる人なら無料で使えるらしいので、試しにDLしてインストールしてみたり。ちなみに、そのうちDLできなくなるという話も見かけました。

インストール時に、「PCの再起動が要求されてる」云々のエラーメッセージが表示されて。OS再起動しないとダメなのかなと再起動してから試したけれど、やはり同じメッセージが。とりあえず「無視」ボタンを押したら、インストールが先に進んだ。何だったんだろう…。まあ、以前 Flash CS5 をインストールした時も、謎のエラーが出てなかなか解決できなかったし。 *2 Adobe製だから、どうせこんなもんだろう…。

Google Web Designer に比べると、さすがに随分作り込んである印象。操作方法は Flash のノリに近いので、戸惑う部分はほとんど無かった。

こちらも、連番画像の出力機能は無し。

パブリッシュされたファイルを確認したら、1つの html と、複数のjsになっていた。jsファイルは難読化されていて何が何だか。

作業用ファイルは難読化がされてなかったので、なんとか理解できそう。hoge_edge.js を開くと、timelines なる記述が見えたので、たぶんこのファイルにアニメさせるための情報が入っているのではないかなと。もしかして、これって json だったりするのかな?

今思い出したけど、もしかして Adobe は、この手のファイルフォーマットの解析作業を禁止してたりしないかな…。禁止してるのだとしたら、そういう面から、他のソフトとの連携は無理ってことになりそうな。

*1: 別途フレームレートを指定して書き出すことは可能だろうけど。それは、数式によってオブジェクトが連続で動いてる光景を、フレームレートに従ってサンプリングすることになるので、アニメ映像じゃなくて実写映像に近いはず。
*2: Flash CS5のインストール時にエラーが出る問題は、Flash Player の古いセットアッププログラムがテンポラリフォルダに残ってると発生、したようなおぼろげな記憶が。

#4 [anime] ライダー鎧武を視聴

今日も何話か消化。

ドリアンライダーの人が生身で変なポーズを取りながらジャンプして出現したり、弟子の人が気をつけポーズで放物線を描いていく図の突飛さに笑いました。どうしてこんな光景を思いついたのか…。素晴らしい。子供さんの反応が気になるところ。

森について解説する回で、それぞれ別の場所に居るはずのキャラクター達が、謎についての説明を繋げていくあたりに感心。こういう見せ方は、自分、なんだか好きでして。たしかコードギアスでもやっていて当時感心した記憶が。こういうことができるあたりが ―― 場所も時間も跳躍して一つに繋げることができてしまうのが映像作品の持つ面白さ、のような気がしたり。…まあ、映像作品において、あらゆるシーンは、異なるカットの繋がりで構成されているわけで。それを脚本レベルでもやってみた事例の一つ、かもしれないのですが。

以上、31 日分です。

過去ログ表示

Prev - 2014/05 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project