JavaScriptによる二進数時計

以前書いたコードが出てきたので貼っておきます。今見直してみると、ちょっとムダがあるコードですね。 JavaScriptは書けません、でお茶を濁しておきましょう。

// 2010/01/08 jbClock by dyama
// for Firefox, Opera and Google Chrome

var BgColor = '#F00000';
var BaseColor = '#351309';
var ActiveColor = '#FF470F';
var Height = 8;
var Width = 16;

// 文字列を指定した桁数kになるまで頭にmを挿入する
function kt( s, k, m ){ while( s.length < k ){ s = m + s;} return s;}

function bclock()
{
    var n = new Date();
    // toString(2) ... 二進数文字列にする
    // kt で頭に文字列"0"を挿入して返す
    // 0と1の文字列だと、後でHTMLを一括置換した時に置換対象以外も
    // 置換してしまうので一時的に文字0を文字列$zに、文字1を文字列$oにする
    var h = kt(n.getHours().toString(2),5,0).replace(/0/g,'$z').replace(/1/g,'$o');
    var m = kt(n.getMinutes().toString(2),6,0).replace(/0/g,'$z').replace(/1/g,'$o');
    var s = kt(n.getSeconds().toString(2),6,0).replace(/0/g,'$z').replace(/1/g,'$o');
    // 0だった場合と1だった場合のHTMLタグを準備
    var t = '<td bgcolor="' + ActiveColor + '" height="' + Height + '" width="' + Width + '"></td>';
    var f = '<td bgcolor="' + BaseColor + '" height="' + Height + '" width="' + Width + '"></td>';
    var o = document.getElementById("area");
    // "$z$z$o$z$o" のような文字列のままタグ挿入
    var v = '<tr><td bgcolor="$a" height="' + Height + '" width="' + Width + '"></td>' + h + "</tr><tr>" + m + "</tr><tr>" + s + "</tr>";
    var a = (n.getSeconds()%2)?BgColor:BaseColor;
    // 最後に$zを0用のHTMLに、$oを1用のタグにHTML全体を一括置換
    o.innerHTML = v.replace(/\$z/g,f).replace(/\$o/g,t).replace(/\$a/,a);
    // 1秒ごとに実行する
    setTimeout("bclock()", 1000);
}

これを実行すると、次のように見えます。

OpenCASCADE 6.7.0 のアナウンス

OpenCASCADEの最新バージョン6.7.0が開発者サイトでアナウンスされています。
記事によると、6.7.0では特に visualization が強化されたようで、簡単かつ高速に高品位のレイトレーシング・レンダリングがビューで可能になったとのことです。

現行リリースの最新版である6.6.0までは、正直に言うと、お世辞でも「綺麗な見た目」は提供されていませんでした。OCCT自体、三次元幾何演算部分が中核となっているライブラリですので画面表現については後まわしになっていたのかもしれません。

結果的にCATIARhinocerosといった商用CADに、第一印象(見た目)で劣るように思われがちになっている感触がありましたが、今回の6.7.0では、商用CADにも負けない美しいビューを表現できるようになったようです。

OpenCASCADE 6.7.0 の新機能

レイトレーシングによるフォンシェーディング・鏡面反射のサンプル
レイトレーシングによるフォンシェーディング・鏡面反射のサンプル
光源と陰を正確に演算しているため、小さな溝やエッジまでよりリアルかつ自然に表現が出来ている。 光源と陰を正確に演算しているため、小さな溝やエッジまでよりリアルかつ自然に表現が出来ている。
半透明処理では、重なり合う物体同士がうまく表現できている。 半透明処理では、重なり合う物体同士がうまく表現できている。
金属表現もこのとおり。 金属表現もこのとおり。

記事をかいつまんで要約しますと、

  • phong シェーディングに対応

三角形ピクセルベースの法線補完と高度な照明モデルを使用して、高品質なシェーディングができるようになったらしいです。これまでは、近似などでごまかしながらシェーディングを行なっていたみたいですが、今回はしっかりとポイントライト(点光源)とディレクショナルライト(平行光源)を使って陰が出来るようになったことで、見た目が大幅に向上したとのことです。

  • 鏡面反射と環境マッピングの対応

レンダリング画面の品質を大きく左右する鏡面反射環境マッピングもサポートされたようです。レイトレーシングがまともに実装されて初めて実現できる機能です。完全な鏡を表現しなくとも、プラスチックや塗装された金属にうっすらと映り込む周りの風景は、より直感的に利用者にイメージを伝えることができるようになると思います。

  • アンチエイリアス処理の改善

6.6.0までは、アンチエイリアス処理を有効にすると、アンチエイリアスであるにも関わらずジャギー(物体の境界のギザギザ)が目立つようになっていました。全然美しくない上に、無駄な処理をするわけで全く使っていませんでしたが、今回の改善でまともに使えるようになった模様です。

  • 半透明オブジェクトの表現の改善

これまでは、半透明オブジェクトを重ね合わせると物体の順序がめちゃくちゃに表現されていました。これは、まともにレイトレーシングをしていなかったため、視点から、手前から順に並んでいるはずの物体の位置を上手く表現できていない事が原因です。今回、これも改善されたようで、CADでなくてはならないこの表現も上手く行きそうです。

  • OpenCLでの処理

レイトレーシングを実装するにあたり、画面の描画コストはこれまで以上に大きくなることは明白です。そのため、並列処理を行うOpenCLをサポートして描画速度の改善を図っています。

レイトレーシングを有効にするコーディング

レイトレーシングモードでの描画を有効にするには、従来のコードをほとんど書き換えることなく、簡単にできるようです。
記事では、V3d_View::SetRaytrasingMode() を実行するだけで有効にできると書いてあります。また、レイトレーシング実装に伴なって DRAW コマンドも追加されており、利用方法もソースコードレベルで示されているみたいです。

今のところの制約事項

6.7.0では、レイトレーシングに関する次のいくつかの機能がまだ制限されているとのことですが、次回以降のリリースで順次利用できるようになっていくそうです。

  • テクスチャーマッピングは今のところ未サポート、従来のテクスチャもレイトレーシングには移植されていない。
  • レイトレーシングが使えるのはポリゴン形状のみで、点や曲線はサポートされていません。(つまり、ワイヤフレームなどもレイトレーシングが使えません)ここで言うポリゴン形状とは、平面分割されたポリゴンメッシュではなく、OpenGL 描画時における面、すなわち自由曲面も含まれているものだと思います。
  • 三角形メッシュの一致問題(OpenGLと同様)。描画時におけるポリゴンメッシュ分割による幾何誤差の事だろうと思います。分割精度を上げれば実用範囲において回避できるだろうとの注釈も。
  • CPU(つまり、ソフトウェアレンダリング)とIntelのGPU(Intel HD Graphicsなど)でテストされていません。
  • また、MacOS Xでもテストされてません。ユーザがこれらの環境に関するパッチ(やレポート)を提供することを求めています。
  • レイトレーシングさせる形状は、今のところOpenGLのVBOを利用せずメインメモリ上から生成するため、メモリ使用量が多くなってます。
    ** 2倍から3倍以上のメモリ使用量になってます。2GBのVRAMで、3,000,000ポリゴン程度。

これらの事は次期リリースに期待しつつも、現状だけでも充分に表現力がアップすると思われます。

DirectXのサポート!?

また、本記事とは別にOCCTのDirectXサポートについての議論も記事になっていました。
曰く、

  • OpenGL に加えて Direct3D レンダラを搭載するのは面白そう。
  • ただ、DirectX を大規模な開発をしてサポートするなら、それなりのメリットが必要。
  • Direct3D レンダラは特定のプラットフォームでしか動かない。
  • レンダラが二つになるということは、バグも倍増されてメンテが大変になる。

とのことでした。ここでは、OCCT本体に実装するのではなく、サードパーティ製の visualization ライブラリとして開発ターゲットにするのは良い方法とも述べています。
個人的には、DirectX の需要はまるでないのですが、90年代後半から今まで培われてきた技術があることは否定しません。終焉を迎えつつある技術でも選択肢が多くなることは良いことだと思います。

Draw Test Harness 日本語 Wiki 作りました

OpenCASCADE の Draw Test Harness の(ほぼ自分用の)日本語 Wiki を立ち上げるために、以前自分の個人サイトでも利用していた UseMod Wiki を導入することにしました。

UseMode Wiki は、Perl で書かれたシンプルな Wiki システムで、煩雑な設定が少なく、依存関係も少ないので重宝していました。基本部は wiki.pl に全て詰まっており、設定ファイルも一つだけ。
見た目に凝ったり、いろんなことをしようと思えば MediaWiki なんかが良いのでしょうが、そんなに時間も割きたくない、簡単な Wiki 構文が使えればいい、程度でしたら、なかなかオススメの Wiki エンジンです。

そんな UseMod Wiki ですが、Wiki 内のユーザ登録が上手く動作しませんでした。エラー内容を調べてみると、Cookie がどうもちゃんとやり取りできていない模様。公式のヘルプを読んでいると、どうも Cookie の期限が「2013 年 9 月まで」とハードコーディングされているようで、思いがけないところでひっかかっていたようです。(しかも2009年あたりからメンテナンスもされていないみたい)

晴れてユーザ登録し、古い記事を新しい Wiki に移植している途中、今度は一部の記事テキストで、ロリポの 403 エラーが出るようになりました。まるで投稿ができない。
最初は何が原因だか分からなかったので、記事名を変更してみたり、いろいろと試行錯誤していたのですが、どうもロリポが(勝手に)ウェブアプリケーションファイアウォール(WAF)を有効にしていたようで、そこにPOST内容がひっかかっていたみたいです。
ロリポは例の乗っ取り事件を起こしてから、ディレクトリのフルパスを強引に変更したり、トップがアレだったりと、あまり良いイメージではありません。他のところだったら、「ああ、そうか、気付かなかったなー」程度で終わるんでしょうが、ロリポの場合ちょっとだけアレな気分になります。
さくらのVPSも持っているので、WordPressのDBまわりの整理ができたら、dyama.orgの参照先をロリポからさくらに切り替えようと思ってます。(なかなかWordPressを気合い入れて調べる時間がない…。)
さくらのクラウドも石狩のデータセンターが稼動し始めたころ、ちょっと不具合があったみたいですが、それ以前から利用している信頼性はやっぱり高いと思います。

WordPressと言えば、WP-Markdown というプラグインを試してみました。こちらも、「そんな高度なレイアウトや見た目を求めないから、さくさくドキュメントが書ける書式で…」という理由で入れてみたのですが、過去の非 Markdown で書いた記事の Syntax Highlight と相性が悪いみたいで、掲載しているソースコードが HTML 混在の表示になってしまいました。
ひょっとしたら設定がマズいのかもしれませんが、かえって手間がかかるかもしれないので導入を見送りました。

Markdownで記事を書き、gistにコードを貼って、記事からはそれを参照…というのも考えましたが、やっぱりバックアップや以前の記事の取り扱いを考えるとムリかなぁ…。(いや、でも引っ越しは楽そう。githubが潰れない限り!)

screenをtscreenからtmuxへ乗り換えた

普段、端末経由で作業をする際、GNU Screen ライクな tscreen を使っていました。tscreen は Screen 系の中ではマイナーな類のもので、Debian の公式リポジトリにも入っていません。自前でソースコードを取ってきてビルドして使っていました。 というのも、自分が Screen を使い始めた当初は今のようにあまり選択肢がなくて、GNU Screen 以外と言えば数えるほどもなかった気がします。

tscreen を使い始めた大きな(覚えている)理由としては、本家ではできなかった画面の縦分割をサポートしていた事でしょうか。本家のキーとも互換性が高かったのも理由の一つだったと思います。

tscreen を常駐させ、中でIRCメーラー、作業環境一式を立ち上げていましたが、cgdb などのターミナル制御をガリガリやっている一部のソフトとの相性が悪かった為、今さらながら tmux に乗り替えることにしました。

tscreen と比べて気付いたこと

画面ロック

tscreen ではスクリーンセイバーとして bsdgames の worms を表示させていました。tmux でも同じように画面ロック機能を用いて、任意のコマンドを実行できましたが、実装に差があるようです。

  • tscreen 画面ロック中もキー入力は tscreen がしっかり管理しており、任意のキーを押すだけで画面ロックが解除される。

  • ** tmux** 画面ロック中のキー入力は、スクリーンセイバーとして実行されたコマンドに移ってしまい、そのコマンドを終了させない限り、tmux はキー入力を感知しない。

worms は C-c で終了しない限り終了しないコマンドです。tscreen では任意のキーを押せば、tscreen が責任をもって worms を殺してくれますが、tmux では自分で殺す必要があるようです。 セキュリティロックさせる意味では、tmux のようにキー入力を完全に画面ロックコマンドに任せてしまう方が良い気がしますが、単にスクリーンセイバーとして使いたいだけだったら、画面ロックコマンドの使い勝手に依存しない tscreen の方が利便性は高いです。 どちらも一長一短なので、まあいいんじゃないかな、と思います。

Unicode 文字の扱い

IRC では UTF-8 のチャンネルに入っていて、最近 Twitter などで良く見る Unicode 文字を駆使したキモい顔文字なんかも表示できるようになっています。 tscreen では、意図的かどうか分かりませんが、とにかく表示しようとしてレイアウト(特に文字幅)が崩れる事がありました。tmux では、文字幅の扱いに不安がありそうな文字は全て半角の「?」で表示されているようで、レイアウトの崩れが少なくなっています。レイアウトが崩れても表示させたいもの、レイアウトが崩れてまで表示させたくないもの、いろいろあるかもしれませんが、Emacs でミニバッファを開きまくっている自分としては、tmux のほうが合っている気がしました。

# 多分、意図的にやっているわけじゃないんだろうな。curses 依存のような。
# ついでに言うと、未だに ISO-2022-JP のチャンネルで CP932 依存の半角カナを垂れ流す奴ら滅びろ。
# 往々にしてそういう発言は読めなくても困ることがないけれど。

ちなみに、tmux のほうが崩れない「気がする」程度で、崩れるタイミングではやっぱり崩れます。ただ、本来の目的であった cgdb がちゃんと表示できているから及第点は超えていますね。

ウィンドウとペインの概念

本家 GNU Screen から tmux へ乗り換えた人のブログ記事でもよく目につきますが、tscreen もウィンドウとペインの概念がなく、単にウィンドウだけです。 最初は戸惑いましたが、これも慣れれば大丈夫そうです。

デザイン比較

ガリガリとカスタマイズをした tscreen と、ほとんどまっさらな設定の tmux を比べるのはちょっとナンセンスですが、なかなかダサくなってしまいました。(tmux はこれから手を入れて行こうと思います。)

tscreen

tscreen

tmux

tmux

緑一色っておま…!

tmux のペインには、tscreen のウィンドウのタイトルバーみたいなものがないので、視覚的にどのペインがアクティブになっているか分かりづらいです。あくまでもデフォルト設定なので、カスタマイズする方法はあると思いますが。ちなみに、tscreen ではアクティブウィンドウのタイトルバーを赤く、ノンアクティブを薄い緑色で表現しています。tmux 側に立って言うと、タイトルバーがない分 1 行だけ広く使えるメリットもありますね。

tmux が使っている色が緑一色というのは、「ひょっとしてANSI 256 colorをサポートしていない端末向け…」と思ったりもしますが、このご時世、あんまりメリットがない気がします。ステータスバーのアクティブウィンドウ表示も「*」で表現していますが、背景色を変えたほうがよっぽど分かりやすいです。 かと言って、画面ダンプのモノクロ印字に耐えられるほどカラー表示にレイアウトが依存していないわけでもなさそうなので、今どき「4ビットカラー対応だぜ」っていうのもなんだか。

「最初からやっといてくれる」の感覚が、もうちょっとおせっかい気味でも良い気がしました。結局、RC ファイルをいじるなら量が減ろうが増えようが、普段使いにはあんまり関係なくなります。

その他 tmux の良いところ

他の方もたくさん書いていますが、軽い気がしたり、レイアウトがそこまで崩れなかったり、そんなに設定しなくてもそこそこ使えたり、とメリットもたくさんありそうです。

下のステータスバーには、ログイン名、ホスト名、カレントパスが常に表示されているので、bash のプロンプトは「$」だけにしても良さそうですね。tscreen でも設定をガリガリ書けば出来そうですが、地味に便利です。

時刻表示は、まあ当たり前として…あとはやはり慣れでしょうかね。

tscreen では日頃の惰性から C-a をコマンドキーにしていたのですが、bash や emacs で行頭に移動したい場合や、vim で数値をインクリメントしたい場合 C-a a とタイプしなければならず、少し手間でした。コマンドキーを変更すれば良かったんですが、慣れもあってついつい使い続けていました。 tmux のデフォルトは C-b なので、vim のバックスクロールにぶち当たりはしますが、こちらに「慣れ」を変更してもいいかもしれません。ちなみに ratpoison ではコマンドキーは C-t でした。

総評

ダサさは自分で改善できるので良いとして、全体的なメリットを考えても乗り換える理由には充分だと感じました。tscreen は、公式サイトも消えてしまっているようで、多分メンテナンスも行なわれていないと思います。代わって tmux は、今もっともナウい Screen 系ソフトらしいので、ユーザ数や開発頻度、これからの改善にも期待が持てます。

さっさと調教して慣れていきたいと思います。

オマケ

全く需要がないと思いますが、これまで使っていた tscreenrc を貼っておきます。

[perl] startup_message off autodetach on

backtick 0 5 5 sh /home/dyama/bin/stat

shelltitle “$ |shell”

caption always “%{= mW} %n %{-}%?%F%{= rW}%:%{= Gk}%? %t %=” defhstatus “%n:%t” hardstatus alwayslastline “%{= bW}%-w%{= Wk}%n:%t%{-}%+w%= $USER %0`(%m/%d %02c)”

msgwait 10

defscrollback 2000

bind r eval ‘echo “Resize Window”‘ ‘command -c resize’ bind -c resize ^] command bind -c resize j eval ‘resize +1’ ‘command -c resize’ bind -c resize k eval ‘resize -1’ ‘command -c resize’

defutf8 on encoding utf-8 utf-8 defencoding utf-8

cjkwidth on

defkanji utf-7

color

defbce “on”

do not use ‘screen-bce’

term rxvt-256color termcapinfo rxvt-256color ‘Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm:hs:ts=\E]0;:fs=\007:ds=\E]0;\007’

idle 1800 blankerprg /home/dyama/bin/worms2

↑たしか /usr/games/worms に上手く引数を渡せなかったので

シェルスクリプトを指定しています。

[/perl]

オックスフォード白熱教室

小学生の頃に家族旅行でイギリスに行った時、母の友人がいるオックスフォード大学を訪ずれました。今でこそ世界有数の大学という事を知っていますが、子供だったので、広大な緑地帯で放し飼いされていたクジャクを追い回した記憶しかありません。
今日、仕事から帰ってきて夕食に一人鍋をしていると、テレビで「NHK オックスフォード白熱教室 第4回 数学が教える“知の限界”」が放送されていました。同大学マーカス・デュ・ソートイ教授によるカオス理論を中心にした、とっても分かりやすい内容の講義(もちろん、日本語吹き替え)で、いくつも興味深い内容のお話をやっていました。かなり一般的に楽しめる事を意識した講義だとは思いますが、難しい数式どころか、黒板すら使わずに話していて、数学が分からない自分にでも理解ができるものでした。残念ながら途中からしか見れなかったので、再放送などがあれば最初から見直してみたいと思います。

レミングの集団自殺

ある海外のドキュメンタリー番組で、レミングというネズミの一種が、4年に1度、崖から海へと一斉に飛び降りて自殺するという事が紹介されました。結局、後から番組関係者の証言によりヤラセであった事が分かったのですが、レミングの個体数が周期的に増減しているのは事実らしいです。
講義では、レミングの世代ごとの個体数をある式で表して、数学的な予測が可能な範囲と予測ができない範囲、つまりカオス状態に突入するところを分かりやすく説明しています。
ある世代の個体数を [math]N_{1}[/math] とした場合、次世代の個体数 [math]N_{2}[/math] は、次のように定義されるらしいです。

[math]N_{2} = ( N_{1} \cdot PN_{1} ) – \frac{ N_{1} \cdot PN_{1} }{T}[/math]
※ただし、[math]N_{2} \geqq 0[/math]

[math]\frac{ N_{1} \cdot PN_{1} }{T}[/math] の項が減少個体数を表し、[math]T[/math] の値が小さくなるにつれ総個体数は少なくなります。また [math]P[/math] は、ある世代と次世代に生まれた個体の総数の増加率を示す係数です。
[math]T[/math], [math]P[/math] をそれぞれ、[math]T = 10[/math], [math]P = 2[/math] とした場合、最初の世代 [math]N_{1}[/math] の値が [math]0 < N_{1} < T[/math] の時、世代を重ねるごとに収束して [math]N_{2} \fallingdotseq 5[/math] という結果に安定するらしいです。

分かりづらいので、最初の個体数が 2、 3、 8、 10 だった場合のグラフを書いてみました。

Screenshot_from_2013-10-26 11:16:45

2、3、8 のいずれの場合も、最終的には 5 に収束して安定期に入ります。8 の場合、一度減った個体数が徐々に増えていくことが分かります。[math]T[/math] と同じ値の 10 は、増えることなく絶滅してしまいました。10 以上のどのような実数を与えても絶滅します。この式での最初の世代の上限は [math]N < T_{(10)}[/math] のようです。

次に、係数を [math]P = 2.5[/math] にして、最初の個体数を 2、3、9.9 だった場合のグラフを書きました。

Screenshot_from_2013-10-26 11:40:30

2 は 2 世代後には 6 に収束します。 3 は、6 の周辺に辿りついたあと増えたり減ったりしながら、やがて振れ幅が小さくなって 6 になります。9.9 の場合は、1 世代後に極端に値が減りますが、その後は徐々に回復していき、やがて 6 に収束しました。9.9 の場合の 1 世代後以降の折れ線は、[math]N = 0.1[/math] の時と同じものになります。同様に 9.5 の場合の 1 世代以降の折れ線は、0.5 の時と同じものになります。[math]T = N_{(9.5)} + N_{(0.5)}[/math] のようです。

さて、最終的に収束する値以上の値については、[math]T[/math] を超えない限り、1 世代以降でそれ以外の値と同じ折れ線になることが分かったので、2 と 3 の場合について、[math]P[/math] の値を変えて遊んでみましょう。
次のグラフは[math]P = 3[/math]の場合です。

Screenshot_from_2013-10-26 11:55:55

半ば予想どおりですが、今度は振れ幅がかなりゆっくりと小さくなっていっているようです。1 世代おきの増減が顕著に見ることができました。ここで、[math]P[/math] の値をどんどん上げることにより、振れ幅の衰退がどんどん小さくなっていき、限りなく周期的に増減をくりかえすグラフになるのかも!という期待が出てきます。
次に[math]P = 3.5[/math]の場合です。

Screenshot_from_2013-10-26 12:04:57

いきなり期待が裏切られました。3 の場合、最初の頃の振れ幅よりも途中から大きくなっています。
さらに[math]P = 3.7[/math]の場合、

Screenshot_from_2013-10-26 12:08:37

かろうじて 1 世代おきに増減をくりかえしているものの、振れ幅の大きさがめちゃくちゃになりました。
そして[math]P = 4[/math]、

Screenshot_from_2013-10-26 12:11:15

1 世代おきにくりかえしていた増減すらなくなり、完全に予測できないグラフとなりました。

番組では、4 年に一度、周期的に個体数を極端に減らすレミング題材にして、このようなグラフを紹介していました。[math]P[/math] と最初の個体数によって、レミングの個体数に似たグラフを表現できるということです。さらに、上で試してみたとおり、この式では [math]P[/math] が 3.5 から周期性が失なわれ始め、4 になると予測不可能なめちゃくちゃな状態、つまり「カオス状態」に突入することも紹介していました。

このレミングの話の前に、重力がお互いに影響し合う惑星の軌道や、複数の磁石を用いた振り子、二重振り子の話がありました。そこで、ささいな初期状態の変化が結果的には、予測不可能なカオス状態へ突入しかねないバタフライ効果についても触れていました。

余談ですが、番組では 4 までしか触れていなかったので、「[math]P[/math] の値を上げれば上げるほど、折れ線はどんどんカオス状態になっていくんだろうなあ」という淡い期待をしてしまいましたが、実際には…

Screenshot_from_2013-10-26 12:22:56

[math]P = 5[/math]の場合、2 が 8 に収束するわ、3 が [math]T[/math] を超えていないにも関わらず、絶滅してしまうわ、グラフの折れ線的にはグラフの後の世代を予測できる非カオス状態になりましたが、これまでの考え自体を否定する結果となりました。

そもそも、そういう挙動をする式だと言ってしまえばそれまでですが、初期状態の小さな変化によって、結果が予測不可能なものになる分かりやすい例えだったと思います。

mrubyがやってきた!

OpenCASCADE の勉強のために、仕事とは関係ないところで、自分なりの簡易三次元 CAD ビューアを作成しています。
形状をパラメトリックに定義したり、対話的に制御したり、一連の処理をモジュール化する上で必要になってくるのがマクロ環境。マクロと言ってしまうと、使い勝手の悪いバッチ処理的なイメージがそこはかとなくありますが、強力なインタプリタ環境を組み込みたいと思いました。

アプリケーションを組む上でもトライアンドエラーのデバッグ効率は上がりますし、CAD ソフトにおける純粋な三次元幾何演算をハード的な部分であるとすれば、「ある決まった工業製品の組み立てモデルをシミュレーションする」といったより論理的な、ソフト的な役割のライブラリ化にも役立つと思っています。

さて、OpenCASCADE にはこのブログでも何回か書いたとおり、DRAW TEST HARNESS というコマンド・ドリヴンな対話型デバッグシェルが含まれています。単機能評価だけで言うと、Tk による最低限の UI とシェルっていう組み合わせは別に捨てたもんじゃないのですが、ユーザに提供するマクロ環境として Tcl/tk を選択するのはいささか時代遅れな感じは否めません。シェルスクリプトや Perl、AWK に馴染みのある人ならば、あんまり気にせずに使い込なせると思うんですが、本質とは違うところでの労力を強いるのは極力避けたいところでもあります。

対話的にも動作することができ、モジュール化しても軽快に動いてくれるインタプリタ環境を探していました。
最低限、

  • 基本的な計算(四則演算、数学関数の呼び出し)
  • 変数の保持と解釈
  • 条件式、繰り返し構文

を備えているものだったら何でも良いと思っていました。ほぼ全ての言語にあるようなものばかりですね。もちろん、書き易さやメンテナンスのやり易さも条件のひとつです。

さて、最初に思いついたのは Python です。Python のコーディングは、私自身、累計でも1万行も書いていないと思いますが、欧米製の大手 CAD ソフトでは Python をマクロ環境として組み込むものが増えてきているようです。フランスのパリに Open CASADE の勉強会に行った時に開発メンバーが見せてくれたデモでも、OpenCASCADE の C++ コードを Python から呼んでいたようです。また、2011年から OpenCASCADE のコミュニティ・エディションをメンテナンスしている Thomas Paviot 氏も PythonOCC という Python ラッパーのプロジェクトを立ち上げています。

大手 CAD や OpenCASCADE との連結でも実績のある Python ですが、組み込み関連の情報を漁っていると、 mruby というものを見つけました。

mruby とは、Ruby の開発者 Matz 氏が最近精力的に開発を進めている組み込み向けの Ruby 実装らしいです。本家 CRuby に比べて依存関係が少なく、小さな端末でも動きやすいよう設計されているようで、Vim 関連の記事でよく参考にさせていただいている mattn 氏頻繁にコミットされている事がブログからも伺えます。

私はガチガチの関数手続き型の頭と、シェルスクリプトのようなゆるゆるのシェル展開ありきの頭の2ビットでコーディングをしていたので、Ruby のような「ちょうど良い」言語には向いていないんだろうなーと食わず嫌いをしていて手をつけなかったような気がします。いや、嫌っていたワケではないのですが、あれ楽しい!これ楽しい!と目移りしている間に、いつの間にか、ちゃんとやってみる機会を逃したというのが正しいかもしれません。少なくとも、ちゃんとやりたい言語の一つであった事は間違いありません。

# 余談ですが、Lisp もちゃんと時間をとってやりたいです。Emacs の設定ファイルのために elisp をごちょごちょ書いたような記憶しかありせん。
# オライリーの「初めての Ruby(オーム社刊、Yasugi 著、2008年)」で紹介されている Ruby の系図に、Ruby が Lisp の考え方も引き継いでいるような図があったので楽しみ。

これは好機とばかりに、mruby を試してみることにしました。

Hello, mruby!

mruby を make するのは簡単でした。他所でも紹介されているように、git で clone して make するだけです。Ruby の環境をまともに触るのも初めてでしたので、初めて知ることがたくさんありました。

まず、Makefile は存在しているのですが、Makefile の中で呼ばれているのは ruby minirake というコマンド。make から minirake が呼ばれて、最終的に gcc がコンパイルする形になってます。コンパイルの動作を処理している minirake は全て Ruby で実装してあり、Ruby 初心者が最初に見た時は「?」マークの連続でした。(でも、それほど時間をかけずに読んでいけました。Ruby すごい!)

頭に mini と付いているのは、きっと mruby のための小さな仕組みであり、本家 CRuby では Rake という Ruby のための Make 環境があるんだろうな、とも理解できました。

Ruby には gem というモジュール管理機構があるようです。Perl で言う CPAN のようなものだと思いますが、rake ファイルにターゲットを追記しておくだけで新しい gem を作ることができました。本家 Perl と軽量目的の mruby を比べるとアレかもしれませんが、分かりやすさ・透明性と簡単さは驚きです。(これなら自分もできるかも!と思わせてくれるのにはテンションが上がります)

さてはて、自分のアプリケーションに組み込む場合は Windows 環境で動かさなければならないので、Windows 環境、とりわけ Visual Studio でビルドできるかというのは大きな問題でした。そして、結果から言うと杞憂でした。
Windows では TortoiseGit と MSYSGIT を使っていますので、Windows に Ruby を入れて PATH を通したあと、MSYS 環境に clone して make するだけでした。途中、bison がないぞって怒られたので、bison.exe を入れたらすんなりと mruby.exe(インタプリタ本体)、mirb.exe(mruby.exeに対話シェル機能を入れたもの)、mrbc.exe(Rubyコードをバイトコードにコンパイルするやつ) が出来てしまいました。これらバイナリは libmruby.a をスタティックリンクしているようで、スタンドアロンで動作可能っていう嬉しさ。さすが組み込み向けです。

コーディングをほとんどしていないのに、こんな側面から mruby がどんどん好きになってしまいます。

Visual Studio で

自分のアプリケーションを Windows 以外のプラットフォームでも提供するんだったら、Qt なんかでチャキっと組んだ方がいいと思うんですが、私は Qt を使ったことがありません。Qt はすごくやりたいのですが、それはまた別の機会として、当面の目標である Windows でそこそこ動くものを作らねばなりません。

そういう理由で、ユーザ・フロントエンドを C#、OpenCASCADE や mruby のラッパー DLL を C++/CLI で実装しよう(※)と思いました。ラッパー DLL も C++/CLI 依存の部分とネイティブ C++ 準拠の部分を分けてコーディングする事により、最悪、別のプラットフォームになった場合、ネイティブ C++ の部分だけ抜き出してビルドし直せばいいと思いました。MFC を使った COM 実装に比べれば、C++/CLI の制約なんて目をつぶる事ができそうですし、そんな高度な事もする予定がないので、当面はこれで問題がないはずです。

※Microsoft の公式推薦の C++/CLI 入門書籍でも、「C++マネージド拡張の失敗という前例もあるし、特に理由がなければ C++/CLI なんて使うな」と前書きに書いてありましたね。あれは声出して笑いました。

さて、さっそく MSYS環境で make した mruby.dll を Visual Studio で作成した C++/CLI の wmruby.exe (デバッグの為、とりあえずEXEを作成)からリンクしてみて実行しました。コンパイル、リンクとも正常に完了しているものの、実行時には 0xc0000007b というエラーが出て、実行できません。調べてみると、どうもバイナリイメージが CIL とネイティブの間で上手く取りつげていないようです。

ここは mruby ではなく、MSYS と、Windows の目紛しく仕組みが変わるバイナリの取り扱いの不一致だろうって事で、あまり深く調べずに考え方を変えました。

mruby.dll を make するために rake ファイルを読み漁ってた時に vs2010 や vs2012 といった、いかにも Visual Studio で mruby をビルドするオプションっぽいのを発見していました。

openssl とか、gem が他のオープンソースライブラリを使う時、Visual Studio ではなく MSYS 環境のほうが色々と苦労が少なそうだなーと思っていたので、あえて MSYS で libmruby.a を make していましたが、そこが問題になりそうだったら選択肢がありません。

vs2010 を使って、ビルドし直してみると、Visual Studio によって libmruby.a ではなく libmruby.lib が生成されました。加えて、MSYS でビルドしていた自前の mruby.dll も Visual Studio で C++/CLI として libmruby.lib にリンクして再ビルドすると、拍子抜けするほど上手く行きました。

libmruby.lib さえビルドできてしまったら、あとはこっちのもんですね!

IIJ 版

これまで触っていた本家の mruby では、require やシェルコマンド実行のクオートなど、Kernel でサポートしているものもほとんどありませんでした。開発途上という側面と、最低限のものを提供するというスタンスがあると思われます。

本家 mruby のほか、IIJ さんも精力的に mruby の開発に手を出しているようで、本家から folk したリポジトリになっています。mattn 氏のブログを拝読していたら、先程述べた require やクオート、正規表現など「モバイル端末などの局所的な組み込みには要らなかもしれない!でもPCで実行する対話的アプリのモジュールなら要るよね!」的な機能を IIJ 版にどんどん追加されていっている様子が伺えました。記事にもありましたが、CRuby にも遜色がないほどの基本機能を提供できるようになっているようです。本当に凄いし、夢が広がります!

という事で早速、IIJ 版を clone して make してみました。linux では何ごともなくすんなり。
./mirb して

[ruby]
ls.each_line do |item|
puts item
end
[/ruby]

すると、ちゃんと実行できました。楽しい。

さて、Windows 環境ではどうかと言うと、ソケットを扱ってそうな gem があったり、コードを見てみると Windows 向けのコーディングがされていなかったりと、半ば予想はしていましたが、Visual Studio でビルドしようとしても失敗しました。当たり前だよネ。

Visual Studio っていう巨大なイレギュラー環境が立ちふさがって、作業がストップしてしまいましたが、丹念に乗り越えなければならない道だろうなぁと思います。今はビルド方法をまとめるくらいしか能力がないですが、Ruby をガリガリ使えるようになって、実装の事も理解できるようになったら issue 飛ばしたり、コーディング面でも協力できるようになれればなあ、と思いました。

OpenCASCADE の Draw Test Harness を活用する その3

前回までの記事では、OpenCASCADE の Draw Test Harness を利用し、独自コマンドを組み込むところまでを紹介しました。今回は、実例を用いながらコーディングしていきたいと思います。

目的

任意の半径を持つ球体を 1/8 にした形状を作成し、その容積を印字したいと思います。

想定しているコマンドの書式は次のとおり。

[bash]
sphere1p8 result_name radius_of_sphere
[/bash]

アプローチとしては、まず原点(0, 0, 0)に半径 r の sphere を作り、同じ位置に大きさ(r, r, r) の box を作成し、二つをコモン演算します。その結果に対して容積を取得しようと思います。

ソースコード

まず、前回のサンプルコードをベースに、プリミティブ、ソリッド、プロパティを扱うためのヘッダを追加してあげます。次に、hello() 関数を sphere1p8() 関数に書き換え、コマンド定義も変更してやります。

[cpp]

include <iostream>

include "/opt/occ660/ros/config.h"

include <Draw.hxx>

include <Draw_Interpretor.hxx>

include <DBRep.hxx>

/* コマンドで利用するヘッダ */

include <TopoDS_Solid.hxx>

include <BRepPrimAPI_MakeBox.hxx>

include <BRepPrimAPI_MakeSphere.hxx>

include <BRepAlgoAPI_Common.hxx>

include <BRepGProp.hxx>

include <GProp_GProps.hxx>

include <Standard_PrimitiveTypes.hxx>

/* 1/8球を作成し、ボリュームを印字する */
static int sphere1p8(Draw_Interpretor& di, Standard_Integer argc, const char** argv)
{
if (argc != 3)
return 1;

// 半径
double r = atof(argv[2]);

// 球と箱を作る
BRepPrimAPI_MakeSphere sphere(gp_Pnt(0, 0, 0), r);
BRepPrimAPI_MakeBox box(gp_Pnt(0, 0, 0), r, r, r);

// ブール演算
BRepAlgoAPI_Common bo(box.Solid(), sphere.Solid());
bo.SetOperation(BOPAlgo_COMMON); // 6.6.0 から BOP_* が BOPAlgo_* になってる
bo.Build();

if (bo.ErrorStatus())
    return bo.ErrorStatus();

// 結果を取り出して、登録
TopoDS_Shape shape = bo.Shape();
DBRep::Set(argv[1], shape);

// 容積を印字
GProp_GProps gpr;
BRepGProp::VolumeProperties(shape, gpr);
std::cout &lt;&lt; std::endl &lt;&lt; &quot;Volume: &quot; &lt;&lt; gpr.Mass() &lt;&lt; std::endl;

return 0;

}

void Draw_InitAppli(Draw_Interpretor& di)
{
Draw::Commands(di);
di.Add("sphere1p8", "sphere1p8 result r", FILE, sphere1p8);
}

include <Draw_Main.hxx>

DRAW_MAIN
[/cpp]

sphere1p8() 内の処理は、コメントを参考にすれば説明は不要だと思います。1点だけ、補足をしておきますと、40行目の DBRep::Set() 関数で DBRep という Draw の幾何オブジェクト管理構造にオブジェクトを登録しています。この DBRep に登録する事により、AXO ビューアにオブジェクトが表示されることになります。また、Draw インタプリタ上からオブジェクトの名前をキーにして扱うことも出来るようになります。

Draw 内だけで使われるもので、有用な関数を次に示します。

  • Draw_Interpretor::Eval … 関数内から Draw コマンドを実行する
    例)

    di.Eval("pload ALL; axo");
  • Draw_Interpretor::EvalFile … 関数内からファイルパスを指定し、Tcl スクリプトファイルを実行する
    例)

    di.EvalFile("script.tcl");
  • DBRep::Set … オブジェクト名をキーにして、オブジェクトを管理構造に追加する
    例)

    DBRep::Set(objname, myObject);
  • DBRep::Get … オブジェクト名をキーにして、オブジェクトを管理構造から取得する
    例)

    TopoDS_Shape myObject = DBRep::Get(objname);

上記の例のように Eval() の文字列はコマンド単位ではなく、一連のスクリプトの内容として食わせることが可能です。(コマンドはセミコロン区切りで1行に連結できます。)
また、DBRep はオブジェクトを静的に管理しているようなので、static 呼び出しで使います。 コマンドの引数として指定されたオブジェクト名で、オブジェクトを取得・設定する際に利用します。
これだけ知っていれば充分ですが、さらに詳しい情報は、Draw_InterpretorDBRep のリファレンスを参考にしてください。

ビルドと実行

前回のサンプルファイルの時から、利用しているライブラリファイルが増えましたので、ビルドコマンドライン、とりわけ Makefile も書き換える必要があります。

[cpp]
g++ -I/usr/local/inc -lTKernel -lTKDraw -lTKMath -lTKBRep -lTKTopAlgo -lTKPrim -lTKBO -lTKG2d drawtest.cpp
[/cpp]

沢山増えましたね。リンカエラーで「クラス○○の参照が見つかりません!」と怒られた場合は、クラスのリファレンスマニュアルを参照すると、ライブラリ名を特定することができます。例えば、Draw_interpretor クラスの参照エラーとなった場合、リファレンスページの画面上部に「Open CASCADE Technology > Module Draw > Toolkit TKDraw > Package Draw」と表示されています。この Toolkit の部分がライブラリ名になるので、この場合ですと「TKDraw」というライブラリをリンクしてあげればいい事になります。
OpenCASCADE ライブラリは、ほとんどに接頭辞 TK が付いていますが、Toolkit の略です。Tcl/Tk の Tk とおんなじですね。

ビルドが成功したら、実行バイナリを起動して、pload ALL、axo の順にタイプします。その後、追加したコマンド sphere1p8 obj 10.2、 fit コマンドを実行すると、次のようになりました。

dth_volume_sample容積は、約 555 らしいです。ちゃんと動いている模様。

あとは、コンパイラに -g して GDB などに投げて、ガリガリとトライ&エラーが楽しめます。

OpenCASCADE の Draw Test Harness を活用する その2

前回の記事で、OpenCASCADE に同梱されているコマンド駆動型の効率的なデバッグ環境 Draw Test Harness の使い方を紹介しました。今回は、いよいよデバッグ&プレビュー環境として活躍させるための、独自コマンドを追加する方法を書きたいと思います。

Draw Test Harness に独自コマンドを追加する

Draw Test Harness の環境に自前のコマンド(コード)を追加する方法は簡単です。どちらかと言うと、自分のデバッグしたいコードを取り出して、Tcl ないし Draw インタプリタを組み込むイメージです。何はともあれ、まずはコードを見てみましょう。

[cpp]
// File: drawtest.cpp

include <iostream>

// このプログラムを Draw インタプリタプログラムにするために必要なヘッダ

include "/opt/occ660/ros/config.h"

include <Draw.hxx>

include <Draw_Interpretor.hxx>

include <DBRep.hxx>

static int hello(Draw_Interpretor& di, // 1. コマンド実装部
Standard_Integer argc, const char** argv) //
{ //
std::cout << "hello, draw!" << std::endl; //
return 0; //
} //

void Draw_InitAppli(Draw_Interpretor& di) // 2. コマンド定義部
{ //
Draw::Commands(di); //
di.Add("hello", "print hello string for test", //
FILE, hello, "Test command"); //
} //

include <Draw_Main.hxx> // 3. アプリケーション

DRAW_MAIN // 実行部

[/cpp]

こちらの手順で OpenCASCADE を導入した場合を前提にして書いていますが、5行目の CASROOT ディレクトリ(ros ディレクトリ)の直下にある config.h のパス文字列を、お使いの環境での config.h のパスに変更するだけで正常に動くと思います。

このプログラムは、1. コマンド実装部、2. コマンド定義部、3. アプリケーション実行部に分かれています。一つ一つはすごく簡単なので、ちゃっちゃか解説しちゃいましょう。

1. コマンド実装部

この関数がコマンドの実際の処理となります。上のサンプルでは、文字列を標準出力に印字する hello() 関数として実装しています。Draw インタプリタとコマンドの実行引数を取る静的関数であれば、名前は何でも構いません。複数のコマンドを実装したい場合は、コマンド毎に、コマンド用インタフェイスを持つ関数を追加してあげます。

[cpp]
/* コマンド用インタフェイス */
static int // 戻り値: コマンド正常終了時は0。
funcname (
Draw_Interpretor&, // Draw インタプリタのアドレス
Standard_Integer, // コマンドの引数数
const char** // コマンドの引数文字列
)
[/cpp]

2. コマンド定義部

Draw_InitAppli() はアプリケーションが初期化される際に一度だけ自動的に呼ばれる関数です。ここで、Draw::Commands() を呼び出した後、Draw_Interpretor::Add() 関数を使って、上記で追加した関数とコマンドの関連付けを Draw インタプリタに宣言します。

[cpp]
Draw_Interpretor::Add(
const Standard_CString Command, // コマンド名
const Standard_CString Help, // コマンドのヘルプ文字列
const Standard_CString Filename, // コマンドの関数がコーディングされているファイルパス
const Draw_CommandFunction Function, // コマンドの関数ポインタ
const Standard_CString Group=&quot;User Commands&quot; // コマンドのグループ文字列
)
[/cpp]

コマンド名と関数さえしっかり定義できていれば、あとは適当で構いません。ヘルプ文字列は、Draw インタプリタで help commandname を実行した際に、表示されるテキストを指定します。普通はコマンドの使い方を書いておくためのものです。

3. アプリケーション実行部

DRAW_MAIN 定数により、アプリケーションに必要な main() 関数や Draw インタプリタのその他の機能の呼び出しなど、面倒なコーディングはプリプロセッサが全部やってくれます。最後にこの2行を書いておけば、とりあえずOK。

ビルドと実行

ビルドは次のとおりです。TKDraw ライブラリを忘れずにリンクしましょう。

[bash]
g++ -lTKernel -I/usr/local/inc -lTKDraw drawtest.cpp
[/bash]

ここで生成された a.out を実行し、「hello」とタイプすると、hello() 内で定義したコードが実行されます。※ a.out を実行した際に「the CASROOT variable is mandatory to Run OpenCascade」というメッセージが表示されたら、環境変数 CASROOT が上手く定義されていません。

「hello, draw!」と印字されれば成功です。「exit」 とタイプして終了させましょう。

今回のサンプルでは、hello() 関数内で OpenCASCADE を用いた処理は一切行なっていなかった為、a.out の実行直後に hello コマンドを実行しても大丈夫でしたが、今後、OpenCASCADE を用いた処理を書くようになれば、必ず「pload ALL」を実行しなければなりません。

次回は、もう少し意味のあるデバッグコマンドを実装しながら、コマンド内でよく使うであろう機能を紹介したいと思います。

onnanoko

本日のまとめ

  1. Draw,Draw_Interpretor,DBRep ヘッダを読み込み、Draw_InitAppli() と DRAW_MAIN を持つソースコードを準備する。
  2. コマンド用インタフェイスを持った関数を作り、Draw_InitAppli() 内で、コマンドと関連付けする。
  3. コンパイルし、libTKDraw にリンクして、実行する。

OpenCASCADE の Draw Test Harness を活用する その1

OpenCASCADE には、コマンド駆動型の効率的なデバッグ環境 Draw Test Harness というものが付属しています。 OpenCASCADE のコードを、単機能ごとに評価できるツールとして開発には外せないものであり、非常に有用なものです。快適なデバッグするのには、グラフィカルなユーザインタフェイスやイベント、コマンド実行の仕組み、簡単な繰りかえし処理や変数、数式の解釈など、かなり高次元なフレームワークを構築しないと、難しいものがあります。その「本質以外の部分」を担ってくれるのが Draw Test Harness です。

Draw Test Harness の簡単な説明

Draw Test Harness は Tcl 言語のインタプリタを内包しており、Tcl 言語のモジュールとして OpenCASCADE を利用された様々なコマンドを実行することができます。また、Viewer や簡単なマウス処理も提供されているので、対話的にコマンドを実行し、結果を三次元ビューで確認することができます。

Tcl 言語をご存知ない方にとっては、「今から新しい言語を覚えるのか…」と逃げ腰になる必要はありません。確かに Tcl はレトロな言語なのですが、インタプリタとしては使い古されており、あまり高度なことをしようとしない限り、充分に、クイックリーに動いてくれる環境です。基本的には、デバッグを自動化してくれるマクロという位置付けですので、バッチファイルやシェルスクリプトを書いたことがある方ならば、充分に使いこなせると思います。基本はコマンドの羅列です。

Draw Test Harness では GUI のメニューも提供されていますが、こちらは Tcl 言語の相棒といっても過言ではない GUI フレームワーク Tk で実装されています。こちらに関しては、Tk を用いて GUI を構築しようとしない限り、まったく知識は要りません。

Draw Test Harness を実行する

前項の手順で OpenCASCADE をインストールし、ros ディレクトリを指す環境変数 CASROOT が定義された状態を前提としています。X サーバが動いている端末または、Xを飛ばしたクライアント上から、次のコマンドをタイプします。

[bash] DRAWEXE [/bash]

GUIのメニューが表示され、端末には「Draw[n]>」というプロンプトが表示されたと思います。これが Tcl コンソールです。プロンプトに続けて、

[bash] pload ALL axo box b 10 10 10 fit [/bash]

とタイプしていくと、平行投影ビューにボックスが表示され、ビューにフィットすると思います。dth

1行目の「pload ALL」は、モジュールファイルを全て読み込むコマンドです。このコマンドを最初に実行しておかないと、Tcl コンソールから OpenCASCADE モジュールをコールすることができません。(ちなみに、OpenCASCADE モジュールは、環境変数 CASROOT で定義された ros ディレクトリからパスを追っていき読み込んでいます。) 2行目の「axo」で平行投影ビューを表示して、3行目の「box b 10 10 10」で「(原点位置に)大きさ10,10,10のbというboxを作る」というコマンドです。最後の「fit」コマンドで、平行投影ビューいっぱいにboxの表示をフィットさせています。

最後に、「exit」とタイプすると Tcl コンソールが終了します。

このような具合で、コマンドを対話的にタイプしていって IGES を読み込んだり、オブジェクトの容積を出したりすることができるツールが Draw Test Harness です。 Tcl コンソール自体がインタプリタになっているので、上でタイプしたコマンドをテキストファイル box.tcl に保存しておき、

[bash] DRAWEXE < box.tcl [/bash]

することにより、一連の作業を自動的に処理させることもできます。 次の Tcl スクリプトは、ボックスと平面の交線(エッジを含むコンパウンド)を水平レベルごとに取得し、表示するサンプルです。

[perl]

!/usr/bin/env tclsh

pload ALL

ビューの作成

axo

ボックスの作成

box b 10 10 12 fit

ボックスを傾ける

trotate b 0 0 0 1 0 0 14 trotate b 0 0 0 0 1 0 -21 fit

水平面で交線を求める

for {set i 1} {$i <= 14} {incr i} { plane p$i 0 0 $i 0 0 1 psection c$i b p$i unset p$i } [/perl]

簡単な制御構文を用いています。私自信、Tcl 言語はそれほど書いたことあるわけではないのですが、以前のブログ記事を見てみるとこんなモノも書いていたようです。全然覚えてないな・・・。

Windows 環境では、CASROOT ディレクトリ(ros)直下にある draw.bat をダブルクリックすると、Tcl コンソールが開きます。

Draw Test Harness を使った応用

Draw Test Harness には、OpenCASCADE モジュールとして提供されているコマンドが数百種類もあります。ここでは、一つ一つに関しての説明は省きますが、これらのコマンドを組み合わせることで、ちょっとした非対話的な CAD ないし、三次元データコンバータくらいなら書けてしまいます。また、DRAWEXE の標準入出力をつないだアプリケーションを書けば、コマンド駆動型、かつしっかりとしたGUIを積んだアプリケーションを書けないことはありません。ちょっとした IGES ファイルの形状のチェックや、形状を反転・拡大縮小したい場合などにも力を発揮します。

また、提供されているコマンドの実装はソースコードレベルで公開されていますので、例えば、三次元オブジェクトの重心位置を取得するコードを書きたい場合、Draw コマンド vprops の実装を調べれば良いわけです。Draw インタプリタ内で「getsourcefile vprops」とタイプすると、ソースコードのファイル名を教えてくれます。そのファイル名を基に $CASROOT/ros/src から目的のソースコードを探し出すことができます。Draw コマンドは、基本的には単機能ごとに機能を提供しているものなので、Draw コマンドの実装方法が「最強のサンプル集」となることでしょう。

さらに、Draw Test Harness では、既存のコマンドに加え、ユーザが C++ を用いてコーディングした OpenCASCADE のコードも簡単に呼び出すことができます。OpenCASCADE を使う、つまり C++ で OpenCASCADE を使う場合、Draw Test Harness は最強のデバッグビューアとなるのです。

次回は、Draw Test Harness に自分で書いた OpenCASCADE コードを独自のコマンドとして登録し、実行してみます。

onnanoko本日のまとめ

  • Draw Test Harness は、コマンド駆動型の強力な OpenCASCADE インタプリタ。
  • 面倒な GUI 周りが最初から準備されているので、三次元幾何演算のトライアンドエラー、プレビューがかんたんにできる。
  • 対話的に利用するもよし、数百種類あるコマンドを組み合わせてバッチ処理をさせてもよし。
  • それぞれのコマンドは、source commandname によって C++ のソースコードを参照できる。

OpenCASCADE における幾何オブジェクトの管理

OpenCASCADE で、幾何オブジェクトを管理するにはいくつも方法があるようですが、付属のコマンド実行環境 Draw Test Harness では、幾何オブジェクトに対して文字列の名前を与え、それで管理できる仕組みを提供しています。
具体的には、Standard_CString 文字列をキーとする AIS コンテキストに登録された Shape との関連付けマップ構造を持っているようです。Draw Test Harness 上でコーディングする際、次のようなコードをしょっちゅう書きます。

[cpp]
{
// …

// オブジェクトを取得
TopoDS_Shape S = DBRep::Get(shapename);

// ...

// オブジェクトを追加
TopoDS_Shape SS = ...
DBRep::Set(SS, shapename2);

}
[/cpp]

DBRep クラス
のソースを追ってみると、名前文字列とインデックスキーの関連付けに Tcl のマップが利用されており、その後で Draw クラスの Set(), Get() がコールされており、Draw クラス内で Draw_VMap 型のマップが宣言されていました。さらに、Draw_VMap は TCollection_BasicMap クラスの派生クラスとなっており、このコレクション・マップを利用すれば幾何オブジェクトを管理することができるようです。

誘惑に負けて std::map を使いそうになってしまいますが、特にプログラミング上の制約の多い Windows で幾何オブジェクトを取り扱うには、TCollection_BasicMap を利用するのが無難なようです。