mruby から Lua を呼び出すための mruby-lua を書きました

mruby は、こちらでも述べられているように組み込み言語として広く使われている Lua を強く意識した実装になっています。

mruby も Lua もそれぞれ、他システムに組み込むのが簡単ということだけあって、多からず相互利用の試みは mruby が登場した当初からあるようです。

Lua から mruby を呼ぶのは、ngx_mruby などでちらほらお名前を見かける松本亮介さんが「Lua上でmrubyを動かすための禁断のLuaライブラリを作った」という記事を3年以上前に 書かれています。

人間とウェブの未来 – Lua上でmrubyを動かすための禁断のLuaライブラリを作った

matsumoto-r/mruby-on-Lua

禁断とされている(勝手な思い)Lua上でmrubyを動かす

と、なんだか言葉を濁しつつ書かれているところを見ると、mruby 界隈に暗に「打倒 Lua」の風潮があるのではないかと邪推しちゃいます…(笑)。

また、

RyanScottLewis/lua-mruby

というのもあるようです。(これも3年以上前)

時間をかけて調べたわけではないですが、mruby 側から Lua を呼び出す形のちゃんとしたものはなかったようなので、試しに書いてみました。

それまで Lua といえば、漠然と「オンラインゲームの AI スクリプトとして使われている軽量言語」というイメージしかありませんでした。昨日、Lua の具体的な構文も知らないまま、とりあえず C と C# から Lua を使うデモコードを書いてみたら、なるほど簡単。自分なんかでも、2つの言語環境のライブラリのダウンロードからコーディング、実行まで数十分でできてしまいました。

言語仕様もコンパクトに抑えられ、API ではスタックを使って(ちょっと煩雑になる部分はあるにしろ、考え方そのものは)シンプルにコーディングできます。組み込みスクリプト環境に求めるものや規模にもよるとは思いますが、大抵の場合 Lua で事足りるのも納得できます。

拡張性が高く、ポータビリティもよく、言語の中身自体も簡単で敷居が低い Lua は魅力的ではありますが、それはあくまでもダイナミックに保持しなければならないリライタブルなコード部分を限定する場合に限るんじゃないかな、とも思います。というのも mruby を使うと、「スクリプト機能」という部分的なものだけではなく「システム全体を Ruby の上で書く」ことも、より容易になると考えているからです。柔軟で高次元な言語基盤を持つ mruby をグルー言語として使い、その上で各種アプリが動作する形がメンテナンスの上でも生産性の高さでも強みになりそうです。

mruby も Lua も Windows や組み込み機器といった「スクリプト環境の砂漠地帯」に持っていくための良いツールになります。 目的の用途にあった形でお互いを上手く利用することができたらいいな、と思いました。

mruby-lua について

Github で公開しています。Lua5.2 を使っています。

dyama/mruby-lua

README.md に書いてあること以上でも以下でもないので、ここであんまり書くこともないんですが、mruby から Lua VM を呼び出して実行することができます。

Lua#dostring は Lua スクリプトを含む文字列として、 Lua#dofile は Lua スクリプトファイルのパスを受けて実行します。 Lua#[]Lua#[]= は、Lua 上のグローバルなオブジェクトを取得・設定することができます。昨日 Lua を触りはじめて mruby-lua を書いてみて今日なので…まだ mruby と Lua 上の型を相互変換している箇所において一部対応していない型があります(2015/12/8現在)。

より高級な mruby が動く環境で Lua スクリプトを呼び出すことにどれだけの意味があるかは分かりませんが、Lua スクリプトをデータ記述言語として使う既存のシステムとのやり取りとか…何かに使えるのかな…。

C++のクラス宣言の文法について

C++ビギナー向けに書いた文章です。

C++の入門書では、クラスの書き方を次のように教えているものが多いと思います。

class MyClass
{
  // ...
};

int main()
{
  MyClass foo = new MyClass();
  // ...
}

クラス定義と呼び出しを分けて記載されています。 これ、初めて見た時に文法が覚えにくい気がしていました。 クラス定義の一番最後の「;」を忘れてコンパイルエラーになることもしばしば。

C#では、同じクラス定義でも

class MyClass
{
  // ...
}

と、一番最後の「;」は必要ありません。それどころか付けているとエラーになります。

C++ ではなぜこのような文法なんでしょうか。

答えは C++ の文法は C を踏襲しており、C にこそその本質があります。 C ではクラスは存在しないため、同じメンバを持つ入れ物である構造体の宣言文法と置き換えて説明します。

メンバを持つ型定義の基本形

C においても、多くの入門書では構造体の宣言を次のように複数行に分けて書くことが多いと思います。

struct {
  int x;
} foo;

これだと文法が分かりづらいため、一行にしてみましょう。

struct { int x; } foo;

はい!一行になりました。それぞれの変数名などをより一般的な用語に置き替えると次のようになります。

struct { メンバ定義 } 変数名;

さらにこの宣言文を抽象的に書くと

型 変数名;

という形に集約することができます。お気付きでしょうか?

int n;

などの通常の変数と同じ文法です。つまり、struct { メンバ定義 } の部分はただの「型」なのです。

struct { メンバ定義 } 変数名;
~~~~~~~~~~~~~~~~~~~~~
    ↑ ただの型

この形をよく覚えてください。これを基本形だと考えればいいでしょう。

コード例

基本形を用いたコードの例です。

int main()
{
  struct { int x; } foo;
  foo.x = 123;
  printf("x = %d\n", foo.x);
  return 0;
}

struct { int x; } の部分はまるごと型を表しているため、struct { int x; } 型の変数 foo が使われているのが分かると思います。

タグ名

さて、C の文法ではこの宣言した構造体に別名(タグ名)を付与することができます。

struct Hoge { int x; } foo;

Hoge の部分がタグ名です。この宣言以降は

struct Hoge bar;

という文法で、{ から } までの定義部分を書かずに用いることができます。

さらに、変数を宣言せずに型のみを宣言する場合は、

struct Hoge { int x; };

とし、変数名を省略します。

コード例

タグ名を用いたコードの例です。

int main()
{
  struct Hoge { int x; };
  struct Hoge foo;
  foo.x = 123;
  printf("x = %d\n", foo.x);
  return 0;
}

3行目で構造体の宣言と定義のみを行い、Hoge というタグをつけています。 4行目でさきほどつけたタグを元にして、変数 foo を宣言しています。

typedef

少しよりみちになりますが C は文法上、型名 struct Hogestruct を省略することができません。つまり、

struct Hoge foo;

は正しい変数宣言。

Hoge foo;

はあやまった変数宣言となります。そこでよく用いられるのが型名に別名を与える typedef です。

typedef int abc;

と書いておくと、それ以降は

abc n;

int n;

として解釈されます。一見、#define マクロのように名前が置換されただけのように見えますが、typedef はプリプロセッサレベルではなくコンパイラ(構文解析器)レベルで正しく「型」として認識されるため、より安全なコードを書くことができます。

さて、この typedef を用いて struct を書かずに済む宣言をしてみましょう。

typedef struct { int x; } Hoge;

先ほどの基本形と似ていますが、typedef の構文であるため、別名は末尾になります。(変数名と混同しないようにしましょう) これでコード中で

Hoge foo;

と書くと、

struct { int x; } foo;

として解釈されるようになります。 なお、C++では typedef を行わなくても struct を省略する事が可能です。

C++ で

C++では、C文法の上位互換がありますので、

struct Hoge { int x; };

という構造体宣言・定義がそのまま使えます。

この文法をそのままクラス定義にも用い、

class Hoge { int x; };

といった記法を行うわけです。末尾にちゃんと「;」がいますね。 構造体と同じ文法である以上、クラスも

class { int x; } foo;

という具合に無名クラス、変数宣言付きの記述も可能になっています。

C/C++の構造体・クラスの宣言は、ひとつの「文」として記述しているわけです。

C# では

C#ではCやC++に似た文法を採用しつつ、構文解析がより具体化しています。

class { int x; } foo;

のようにクラスの定義と変数の宣言を同時に行うことはできず、

class Hoge { int x; }

というクラス名・タグを持つ宣言のみに限定されています。 変数を宣言する必要がないという理由からか、末尾の「;」も付けません。C/C++ では「1文」だったものが、C# では「1節」になっています。

CやC++では煩雑になりがちな型の宣言や変数の定義を文法上制約して、できるだけすっきりとした構造にすることが目的だと考えられます。

ただ、C# では

int Main()
{
  class Hoge { int x; }
  // ...
}

という具合に、メソッド内でのみ利用したい「使い捨て」クラスを文法上定義することができなくなる弊害が出てしまいました。

これが実際に問題になっていたのか、C# 3.0 から「匿名型」という文法が導入されました。

var foo = new { x = 0 };

明示的にメンバの型を指定することができなかったり、型を決定するために初期値を指定しなければならなかったりと色々と残念な状態になっています。

また、

int x = 0;
var foo = new { x };

という初期化、型決定、メンバ名の決定というもはやカオスな機能を提供していたり、動的にヌルヌルしているかと思えば一方で

var foo = new { x = 0, y = 0 };
var bar = new { y = 0, x = 0 };
// foo と bar は宣言(定義?)順が違っているので別の型

という C の構造体かよ!と勢いよくツッコミを入れたくなる仕様だったりします。

文法という側面だけを見ても、ラムダ式や Func 型など、他にもいろいろと首をかしげる部分が多い言語です。 Java を駆逐し、Ruby や Python のような柔軟性を模索していると言えば聞こえがいいですが、他の言語に比べても後手後手にまわっている Microsoft の「商売戦略仕様」が見え隠れしているのは気持ち良いものではありません。

FORTRANコードをCで使う

fortran

仕事で古い FORTRAN77 のコードを .NET Framework (C# アプリ)から呼ぶことになり、ビルドと呼ぶ手順について検証しました。それまで漠然としたイメージしかなかった FORTRAN を触れる良い機会だったので、覚え書き程度に一般的な情報の部分を記事に残しておきたいと思います。

導入

GNU Compiler Collection が動く UNIX 系の OS であれば、ディストリごとの公式リポジトリに GFortran が準備されていると思います。

$ sudo apt-get install gfortran

APT システムの環境だと、上のコマンドを実行するだけで一式入ります。今回のターゲットは FORTRAN77 で、GFortran は 9x 対応のようですが、9x は 77 の互換性を維持しているらしいので、特にバージョンを意識する必要はなさそうです。60年近い FORTRAN の歴史の中では比較的に新しい規格である 2003、2008 についての対応状況は、他のコンパイラを合わせてもあまり芳しくなさそうな印象を受けました。

なお、Microsoft Windows でも Microsoft Visual Studio と Intel Parallel Studio for Fortran (有償)を導入すれば開発が可能なようですが、UNIX 開発ツール群の Windows 移植版 MSYS/MinGW に GFortran が入っており、そちらで Win32 な DLL や EXE も生成できてしまいますので、FORTRAN ベースでリソースをガリガリいじったり Windows 特有の機能をバリバリ呼び出したりしない限り、さくっと UNIX 互換ツール環境を入れてしまった方が賢明そうです。

ビルド

次にテストの FORTRAN、C それぞれのコードを示します。

FORTRAN 側のサブルーチン twice は与えられた整数値を 2 倍して、実数値で返す簡単なものです。呼び出しもとの C 側では、この関数を引数 10 を与えて呼び出し、結果を印字しているだけです。

今回は FORTRAN で書いたコードをオブジェクトファイルにして、C で書いたコードと静的リンクして C からサブルーチンを呼び出すので、次のようにビルドします。

$ gfortran -c my-test-lib.f

-c オプションでオブジェクトファイルを生成します。GFortran は GCC のコンパイラの一種なので、C/C++ での開発時のオプションがそのまま流用できて便利です。これを実行すると、オブジェクトファイル my-test-lib.o が生成されます。 次に、

$ gcc -lgfortran my-test-lib.o sample.c

を実行すると、sample.c をコンパイルしたのち、my-test-lib.o とリンクを行い、実行可能形式の a.out が生成されます。見てのとおりですが、libgfortran にもリンクしなければなりません。

実行

$ ./a.out
result: 20.000000

ちゃんと動いているようです。C# アプリから呼び出す場合は、MinGW で EXE としてビルドして外部プロセスとして呼び出すか、Win32 DLL としてビルドして [DllImport()] してやればいいと思います。(クラスを呼び出すわけじゃないから、C++/CLI でラップする必要もなさそう)

コード解説

FORTRAN を書くのは初めてなので、解説…という立派なものではありませんが、ちょっと触ってみてポイントになるところをメモしておきます。

  • オブジェクトファイルは C で関数単位でコンパイルしておくのと同様、サブルーチン単位(もしくは複数のサブルーチン単位)でコンパイルして利用することが可能。
  • サブルーチン宣言行の末尾に bind(C) を付与しておくと、サブルーチン名で C 側からアクセスできるようになる。
  • bind(C) を付与すると、FORTRAN 内からのサブルーチンコールができなくなる?
  • implicit 文で仮引数 n, r の型を宣言している。
  • C の関数のような戻り値の記法はない。そのため、C 側から見たサブルーチンはすべて戻り値が void となる。
  • サブルーチンから値を返したい場合は、C 側からポインタを渡してその中に書き込ませる。
  • 戻り値以外であっても、値はポインタとして渡す必要がある。

2015/8/30 追記

Microsoft Windows でも MinGW にて、mingw32-basemingw32-gcc-fortran パッケージをインストールすると上と同じコードをビルド・実行することができました。

gfortran-on-mingw

OpenCASCADE 6.9.0 を LMDE でビルド

先日、自宅サーバの Debian GNU/Linux Jessie 32bit 版で OpenCASCADE 6.9.0sirenビルドしました

自宅サーバは、SSH で外部からアクセスして開発ができるので便利である一方、オンボード Atom 搭載の省電力サーバなのでコンパイルにはもの凄く時間がかかります。そこで、Intel Core i7 を搭載した普段使い用のラップトップでもビルドしようと思います。

ラップトップは、LMDE(Linux Mint Debian Edition) の無印、あえて言うなら version 1 です。先日、LMDE 2 がリリースされましたが環境を壊すかもしれないのが怖くてまだ移行していません。なお、こちらは 64 bit です。

OCCT 6.8.0 ベースの siren をビルドして開発に使っていたので、こちらも難なくビルドできると思いきや、VTK を指定しないと Visualization 以下のモジュールが上手くビルドすることができませんでした。

configure の出力では、VTK は optional 扱いになっていたのですが、必須なのかなあ…?たしかに先日の Debian の記事でも明示的に VTK を指定していました。なお、VTK は自分の記憶が正しければ 6.8.0 の時点で既に OCCT に利用されていたようですが、その時は確実に optional だったはずです。(VTK をインストールしなくても Visualization 以下のビルドが通っていました)

ないなら入れてしまえと apt-cache search vtk するも、バージョン 5.8 しか公式リポジトリにはありませんでした。試しに libvtk5.8, libvtk5-dev を install して OCCT 6.9.0 をビルドしてみても、やはりビルドが通りません。 なお、Debian の Jessie には公式リポジトリに VTK 6.1 があったため、APT 経由で入れたらビルドが通りました。

ということで、ソースコードを取得して自分でビルドしましょう。VTK の公式サイトのダウンロードページから、対象となる 6.1 の tar ボールをダウンロードします。

cd /tmp
wget http://www.vtk.org/files/release/6.1/VTK-6.1.0.tar.gz
tar zxvf VTK-6.1.0.tar.gz
cd VTK-6.1.0

README.html にビルド方法が載っているので、それに従ってビルドします。

cmake.
make

README.html では cmake ではなく curses 版の cmake である ccmake と書いてありますが、cmake でも通ります。

ビルドにはもちろん CMake が必要です。また、依存しているライブラリもいくつかあると思いますが、幸運なことに自分の環境では追加ライブラリを指定することなく全てのビルドが成功しました。VTK を Debian で APT 経由からインストールした際、200 MB 前後の依存ライブラリがどっと降ってきたので心配でした。ヨカッタ。

例に漏れず、インストールは次のようにします。

sudo make install

インストールによって、ヘッダファイルは /usr/local/include/vtk-6.1 以下に、ライブラリファイルは /usr/local/lib 以下に設置されました。インストールパスは cmake オプションで指定できるはずです。

さて、これで OCCT 6.9.0 のビルド環境は整ったはずです。前回の手順どおりに、一気にやっちゃいます。

cd /tmp
wget http://files.opencascade.com/OCCT/OCC_6.9.0_release/opencascade-6.9.0.tgz
tar zxvf opencascade-6.9.0.tgz
cd opencascade-6.9.0/
./build_configure
./configure -prefix=/opt/occ690 \
-with-vtk-include=/usr/local/include/vtk-6.1 \
-with-vtk-library=/usr/local/lib

configure の出力で Visualization 以下のパッケージもすべて「Yes」になっていることを確認し、

make -j8 install

で、ビルド&インストール。 ビルド時間を計測すると次のとおりになりました。

real  17m11.411s
user  97m46.908s
sys   5m26.720s

前回、3時間以上かかっていたビルドは17分で終わりました。これは強い。

6.9.0 を用いて siren をビルドし直すとこちらでもちゃんと動いているようです。ひとまず安心です。

OpenCASCADE 6.9.0 を Debian でビルド

先月の12日にリリースされた Open CASCADE の 6.9.0 をビルドしました。公式のロードマップでは 6.x 系列は 6.8 が最後で、次は 7.0 になるとの話だったはずですが、何だかんだで 6.9 がリリースされた模様

v6.9 のリリースノートを読んではいましたが、ここのところ忙しくて対応ができなかったので出遅れてしまいました。

ここ数バージョンは、ビルドの仕方マニュアルがいろいろと整備されてきて、時代に合わせてなのか、ビルド環境ごとの Markdown なドキュメントも付属しています。このブログで改めて紹介する必要性もあまりなくなってきました。詳しい内容は公式ドキュメントに任せるとして、要点だけかい摘まんで紹介したいと思います。

公式ドキュメンテーション

v6.9.0 の内容は次のドキュメントで網羅されています。

v6.9.0 のリファレンスマニュアルは、ソースコード tar ボールや Windows インストーラー内に同梱されています。最も最新バージョン(開発版)のリファレンスマニュアルであればこちらで参照できます。

v6.9.0 リリースの要点

モデリング・アルゴリズム

ファジー ブール演算

これまでのバージョンでは、ソリッドモデル等で行うブール演算時に対象と対象がちょうど同一の位置にある面や線を共有して存在していた場合、意図した演算結果にならない事が多くありました。意図した結果にならない場合はまだ良かったんですが、いつまで経っても計算が帰ってこない、または例外を吐いてお亡くなりになるケースもありました。この点が改善されているようです。

複数の引数を取るブール演算

これまでのブール演算では TopoDS_Shape を二つ用いて、Fuse, Common, Cut 等の演算を実行していました。あんまり詳しく見てませんが、同時に複数の TopoDS_Shape を演算させることができるようになるようです。これまで数回の処理で書いていたものが一度にできるわけですが、複数のソリッド群の一つに reverse させたソリッドを混ぜて Common に投げて、結果的にその部分だけは Cut させる動作にするなど、より「ブーリアン」っぽい演算としてコーディングできるようになりそうです。

ビジュアライゼーション

選択処理を再設計して、より良いパフォーマンスに

OCCT のビジュアライゼーションは長いことほったらかしになっていた様子で、ちょっと貧弱でした。これは、あくまでも OCCT は三次元幾何演算とトポロジックな形状の演算がメインであり、代替ライブラリーを用いて解決できるビジュアライゼーションは二の次でも大丈夫!という背景もあります。 実際、OCCT を採用している有名なオープンソース三次元 CAD ソフトの FreeCAD は、OCCT 提供のビジュアライゼーションを使用せずに別のライブラリで三次元ビューを提供しています。 ただ、ここ数バージョンでは、前述の幾何を扱うことろがだいぶ安定してきたのか、二の次だったビジュライゼーションが次々と強化されており、パフォーマンスも改善しています。まだ実験的な印象ではあるものの、レイトレーシングレンダリングの採用、GLSLシェーダが読み込めるようになっていることなど、単なる計算ライブラリから本当に「CAD」として必要な機能の強化が見てとれます。 後述する VTK の採用も注目すべき点です。

OpenGL ES 2.0 互換

ビジュアライゼーションの改良の特記すべきもう一つの点は、iOS, Android などのモバイル端末への対応です。OpenGL ES はモバイル端末向けの OpenGL のサブセットです。OCCT の幾何ライブラリの部分は、早い段階から Android にも移植されて動作していましたが、ビジュアライゼーションはそのままではビルドできませんでした。ES 互換となることにより、これらの環境へもシームレスな移植が可能となりそうです。

シェイプを表示する際の三角形メッシュ化の制御

コンテキストにシェイプを追加する際、これまでは必要に応じて自動的にシェイプの描画データである三角形メッシュを内部で生成していました。これを外側から無効にするオプションが導入されたようです。 形状の変形はなく、大量のシェイプを回転させるだけでも再三角形メッシュ化が走って表示速度が落ちていましたが、それらを動的に制御することができそうです。(※回転も行列演算している意味では変形(トランスフォーメーション)ですが、自由曲面の式まで遡って、形状の三角形メッシュを再構築する必要すらない場合も往々にあります)

その他

Intel TBB ライブラリを使わない並列処理のサポート

こちらも詳しく追っていませんが、自前で並列処理を行う環境が整いつつあるようです。

サンプルに AndroidQt, JniViewer for Android が追加

Android 環境のサンプルも充実してきました。

JniViewer

JniViewer for Android

AndroidQt

GUI は QML を使って書いてあるみたいです。本当に WPF/.NET Framework なんて使ってる場合じゃないです。

AndroidQt

その他のサンプルに、Qtデスクトップ版、MFC、C# があります。C# では WPF ベースのものと System.Windows.Forms 版があり、今のご時世でいうと半ばどうでもいいですが、Direct3D デモも追加されています。

v6.9.0 の全体的な感想

幾何演算部分の改良は喜ばしい限りで、ビジュアライゼーションの強化も頑張ってほしいところです。ようやくモバイル端末のサポートも視野に入れてきているようですが、個人的にはいっそのこと三次元ビューアは WebGL 環境へシフトしていくのもアリなんじゃないかなぁと考えています。OCCT をサーバ上で並列処理させて、クライアントの WWW ブラウザ上で表現する…素晴しいじゃないですか。最近は NW.js (Node.js + Webkit)のようなツールキットも存在しますので、ライトウェイトなビューアーからワークステーションで動かす CAD まで環境に依存しないビジュアライゼーション機能になっていけばいいな、と思っています。え?お前が Context や Viewer, View を JavaScript で再実装しろって?それが仕事になればいいんだけどなぁ…;)

v.6.9.0 のビルド

ビルド方法は次のドキュメントに詳しく書いてあります。

なお、このドキュメントはソースコード tar ボールを解凍して生成されるdoxディレクトリ以下に markdown ファイルとして収録されています。(逆に言うと、上記の HTML はそれらのファイルを doxygen にかけたものと思います。)

ビルド環境

OCCT のビルド環境もかなり充実してきており、以前のようにチューニングしながらあれやこれやする必要もほとんどなくなりました。公式サポートしているのは次の環境です。

  • Automake
  • CMake
  • CMake + ADT (Android SDK)
  • Code::Blocks (Mac OSX)
  • Xcode (Mac OSX)
  • Microsoft Visual C++ (Windows)

また、パフォーマンスチューニングレポートでは Linux 版の Clang/LLVM でも検証されているようです。

今回は一番馴染みがある Automake でいきたいと思います。

ビルド手順

作業ディレクトリに入り、tar ボールを落としてきます。ソースコードは前バージョンまではユーザー登録&ログインしないとアクセスできなかった気がしますが、今回から下記の URL で落とせるようです。なお、git での clone は登録が必須です。

cd /tmp
wget http://files.opencascade.com/OCCT/OCC_6.9.0_release/opencascade-6.9.0.tgz

解凍してディレクトリにはいります。

tar zxvf opencascade-6.9.0.tgz
cd opencascade-6.9.0/

configue をビルドして configure します。オプションについては後述します。

./build_configure
./configure -prefix=/opt/occ690 \
-with-vtk-include=/usr/include/vtk-6.1 \
-with-vtk-library=/usr/lib/i386-linux-gnu/

make して install します。

sudo make -j2 install

とりあえずこれだけです。便利。

貧弱な自宅サーバ(Debian GNU/Linux、32bit環境、Intel Atom)では、make で

real    183m57.121s
user    336m24.168s
sys     16m36.896s

となりました。

configue オプション

-prefix=

最終的にビルドしたファイルをインストールする場所です。自分は /opt/occ690 に指定しています。

-with--include=, -with--library=

ヘッダのインクルードパスとライブラリパスを追記しています。VTK が見えてなかったようなので、明示的に指定しました。

その他のオプションはこちらを参考にしてください。

ビルド時のテクニック

configure では、ビルドする環境にインストールされているライブラリの有無によって OCCT のモジュールとよばれるライブラリ群の Makefile を生成します。

モジュール > ツールキット > パッケージ > クラスや列挙型など

OCCT は上記のように階層構造を持ったライブラリです。ツールキットの部分が共有ライブラリファイル(.so)に当たります。Windows ではダイナミックリンクライブラリ(.dll)になります。モジュールはそれらをまとめて用途別にグループにしたものだと思ってください。

モジュールには次のものがあります。

  • FoundationClasses … 共通関数などを提供
  • ModelingData … 幾何形状をデータとして表現する機能
  • ModelingAlgorithms … 幾何形状を構築する機能
  • Visualization … 幾何形状を画面に描画する機能
  • ApplicationFramework … OCCT を使ったアプリのための有用な機能
  • DataExchange … 幾何データファイルのサポート(IGES,STEP,STLなど)
  • Draw … テスト環境

ざっくりと説明すると上に行くほど依存関係が少なくてすみます。詳細な依存関係はリファレンスマニュアルの図のとおりです。

occt modules

さらに、個々のモジュール内のツールキットの中で依存関係があります。なお、IGES などのデータファイルをサポートするには、GUI が必要がない場合でもビジュアライゼーションやアプリケーションフレームワークの各ツールキットを全て背負い込まなければならないかというと、実はそうではありません。具体的にはモジュール同士の依存関係の奥にはツールキット同士の依存関係があり、それを満たしていればこの図の限りではありません。今のところ、モジュールを越えた関係図はないので、リファレンスマニュアルを見て何が必要なのかを調べる必要があります。

さて、ビジュアライゼーション以下のモジュールは、さらに多くの別の人(サードパーティ)が開発しているグラフィック系のライブラリに依存しています。

そのため、グラフィック系のライブラリがない環境で configue すると、ビジュアライゼーション以下のモジュールが無効になった Makefile が生成されます。

configure の出力に各モジュール名と Yes/No といった表示が出力されるので、それでモジュールが有効になっているか無効になっているか、無効の場合はどのライブラリが足りていないのかを表示してくれます。以下に私の環境の configure 出力を示します。

3rdparty mandatory products       
---------------------------------
freetype      : yes 
tcltk         : yes 

3rdparty optional products       
---------------------------------
gl2ps         : no (--with-gl2ps=DIR option was not defined)
freeimage     : no (--with-freeimage=DIR option was not defined)
tbb includes  : no (--with-tbb-include=DIR option was not defined)
tbb libraries : no (--with-tbb-library=DIR option was not defined)
opencl        : no 
qt            : no (--with-qt=DIR option was not defined)
vtk           : yes 

Component                   Build
--------------------------  -----
FoundationClasses           yes 
ModelingData                yes 
ModelingAlgorithms          yes 
Visualization               yes 
ApplicationFramework        yes 
DataExchange                yes 
Draw                        yes 

「3rdparty mandatory products」は必須ライブラリ、「optional」は指定があったら使うライブラリです。必要なライブラリとそのバージョンは、以下にまとめてあります。

Open CASCADE Technology: Overview

Debian GNU/Linux jessie 32bit 版では、すべてのサードパーティ・ライブラリを標準リポジトリからインストールすることができました。(ソースコードの取得とビルドは不要)

また、テスト環境である Draw や Qt のサンプルプロジェクトをビルドする必要がなければ、 Tcl/Tk や Qt のインストールは必要ありません。 上記の「mondatory products」に Tcl/Tk が入っているのは、すべてのモジュールをビルドする際に必須、という意味だと思います。

siren をビルド

OCCT を使った siren というソフトを作ってます。OCCT が提供する三次元幾何演算機能を Ruby で簡単に記述することができ、ちょっとした演算なら手軽にすることができます。

上記でビルドした OCCT 6.9.0 を使って siren の最新リビジョン(OCCT6.8.0ベースで記述)をビルドし直してみると、あっけなくビルドが通りました。クラス名が変更されたり、siren で使っている機能が変更されることがなかったようです。

ひとまず安心しました。


グラフィックLCD風ディスプレイシミュレータ

去年の11月、フレームバッファに直接値を書き込んでプログラムからお絵描きがするような、手軽なプログラミングをやってみたい人向けに LCD 風ディスプレイシミュレータを書きました。

ある通常ファイルを /dev/fb0 のようなデバイスファイルに見立て、ファイルを開いてシークして値を書き込むことによって、疑似画面上にドットを打つことができます。

ダウンロード

Gist に Base64 エンコードした display.zip を置いておきました。Linux や Mac OS の場合は、base64 コマンドに -d オプションをつけることによって、元のバイナリにデコードできます。Windows の場合は、Lhaplus などのアーカイバでバイナリに戻せます。

使い方

display.exe はコマンドラインからオプション付きで実行します。

display 横ドット数 縦ドット数 1ドットあたりの大きさ 中間ファイルパス

次のように実行します。

display 64 24 5 fb0.bin

上の例では、解像度 64×24 ドットのグラフィック LCD ディスプレイとしてシミュレートし、1ドットあたりの表示ピクセル数は5、ファイルに fb0.bin を用いて起動しています。

仕組み

プログラム側から、

  1. ファイルを開く
  2. ビット列を書き込む
  3. ファイルを閉じる

の一連の作業を行います。アニメーションさせる場合は、1 から 3 を sleep() を入れて繰りかえします。

サンプルプログラム

次のサンプルでは、ファイル fb0.bin を中間ファイルとして display.exe から監視させています。

このサンプルを実行しながら display.exe で表示を行うと、次のような絵が表示されて、波がアニメーションします。

anime

VTL (Very Tiny Language)の実装を七行プログラミングで挑戦してみました

1970年代の中盤に開発された極小インタプリタ言語 VTL (Very Tiny Language) の基本的な演算構文を7行で書いてみました。

VTL に関しては、以下に詳しい記述があります。

一つめのリンクで公開されている実装とほぼ同じ挙動をするものを C# で書いてみましたが、こういう小さな実装はポインタをガリガリ動かしながら式を解釈していくのが面白そうだと思い、C で七行プログラミングに挑戦してみました。

コード

結果からいうと、自分の力ではプログラミングモードや GOTO を7行内に実装することができませんでした。次のコードは、ダイレクトモードで VTL の基本演算処理を行うものです(これは一応7行)。

ビルドと実行

※ 七行プログラミングということもあり、エラーチェックなどはほぼ省いてあります。変な値を入力するとすぐにセグフォ吐いて死にますので、自己責任で実行してください。

gcc -o vtl7 vtl7.c
./vtl7

本家 VTL は実装の小ささが売りです。手元の環境の 32 bit Debian + GCC 4.7.2 で -Os をつけて strip したところ、3.7 キロバイトでした。うちの子はそこまで小さくないけど、昨今の ELF 形式ならこんなもんかな…?

実行すると、対話型シェル(というほどたいそうなものではありませんが…)が起動します。

OK
* A=123
OK
* B=2
OK
* ?=A*B+4
250
OK
*

上の例では、変数 A123 を、変数 B2 を代入し、A*B+4 の式を計算して印字しています。 なお、OK だろうが ERR だろうが、OK と出てきます。本当にエラーが発生した場合は「Segmentation fault」と出て死にます。こわい。

シェルを終了するには、C-d を押します。

サポートしているもの

  • 変数 … A から Z の25文字が使えます。
  • 演算子 … 二項演算子 +, -, *, / に加え、比較演算子 =, >, < が使えます。
  • 代入構文 … A=100B=A*2 のように変数に値や式の評価結果を代入することができます。
  • 印字 … ?=A?=1+2+3 のように変数の内容や式の評価結果を端末に印字することができます。

また、

  • 任意の場所に空白文字を入れることはできません。
  • 2文字目が = ではないものは、すべてコメントとして読み飛ばされます。
  • 演算の優先順位はなく、式を左から解釈していきます。1+2*37 ではなく、9 になります。
  • 比較演算子を式に用いると、真の時は 1 を返し、偽の時は 0 を返します。
  • 比較演算子の >, < はそれぞれ、C でいう >=, <= として解釈されます。
  • 式の中に負数は使えませんが、A=0-100 とすると変数 A-100 を代入でき、式で使えます。
  • 端末からの入力、文字の印字、配列やジャンプ構文などは実装していません。
  • 浮動小数点数はサポートしていません。また、剰余 % もサポートしません。

という感じです。

ジャンプ構文がないと、ただの「変数が使える電卓」レベルですね。 あと4、5行余裕があれば実装できるかもしれません。

圧縮前のコード

元のコードも一応貼りつけておきます。


gets() を使っているところは論外ですが、getval() 内の strtol() の引数 p&p も本来ならばやっちゃいけない投げ方だと思います。

strtol() は文字列を数値にするのはもちろんですが、演算子と数値部分のパースに使っています。 ポインタ p が進んでいれば数値化もできて文字列の式もその分だけパース完了、進んでいなければ *p は変数名であるという解釈をしています。

Open CASCADE 6.7.1 をビルドする

開発環境の準備

g++(GNUコンパイラコレクションのC++コンパイラ一式)は既にインストール済みであることを前提としています。

aptitude install automake libtool

nginx を動かして小さなウェブサイトをホスティングしているくらいのうちの Debian wheezy では、上記の二つだけ入れれば良いみたいでした。

Open CASCADE の準備

まず、作業ディレクトリを/tmp以下に作って移動します。

cd /tmp
mkdir occ
cd occ

作業ディレクトリは権限があればどこでも構いません。以後、このページでは/tmp/occを作業ディレクトリのルートにします。適宜読み替えてください。

OCCT の開発版は、開発サイトに登録して、印刷した契約書にサインして、スキャンした PDF を返送しなければ手に入りません。開発版は git で clone すれば良いですが、大抵の場合は正式版で十分ですので、公式サイトから入手することになります。OCCT の公式サイトでは Windows 環境ではビルド済みのバイナリとソースコード、Linux 環境でも大手ディストリならばディストリごとの apt や yum リポジトリにパッケージが準備されていますが、後者は最新版パッケージがなかったり、最近では Debian の公式リポジトリから無くなってしまったりとアレですので、ソースコードからビルドします。ちょっと手間がかかりますが、一度慣れてしまうと、必要な機能だけ切り分けてビルドすることができるようになりますので、エンドユーザ向けアプリケーションや WWW サーバ上で動かす場合など、用途に応じた最適化ができます。また、その気になれば Android といった携帯端末向けにビルドすることもできます。

wget -O occ671.tar.gz http://...
tar xvf occ671.tar.gz

解凍すると/tmp/occ/opencascade-6.7.1というディレクトリが生成されます。

ビルド方法については以下のディレクトリにドキュメントがあります。

/tmp/occ/opencascade-6.7.1/dox/dev_guides/building

ここ数バージョンでOCCTのリファレンスは doxygen に統一されました。ここにあるドキュメンテーションは markdown です。

最小構成ビルド

最低限の機能のみしか使わない場合は、次のコマンドだけでいけます。ビルド自体は automake 以外にも CMake、MSVC、XCode 環境でもできます。 また、100%かどうかは確認していませんが Clang/LLVM としてもビルドできるようです。

./build_configure 
./configure
make

OCCT には1万を超えるクラス群が存在しており、フルビルドは数十分、場合によっては数時間は平気でかかります。 就寝前や外出前に make していくことをオススメします。

なお、これから OCCT を触ってみたい人には、この最小構成ビルドはオススメしません

mruby: C++のクラスをラッピングしてRubyクラスとして使えるようにしてる途中

sirenの改修を行なっています。これはもともと、Open CASCADE の C# アプリケーションサンプルに含まれていた OCCTViewer クラスを参考にして組んだものがベースで、C# アプリケーションのコードとネイティブな DLL の間を取り継いで、機能を提供する sirenenv が動いています。

sirenenv の内部をさらに細かく分けると、C++/CLI の構文で書かれたインターフェイス部分と C++ 構文のみで書かれたコア部分に分けることができます。C++ 部分も含め、結局 /clr オプションでコンパイルしているので C++/CLI として解釈されるんですが、将来の「脱 .NET」を目指して切り分けを進めています。(厳密に構文が分かれてない部分もまだあると思われます)

C++部分をさらに分けると、Open CASCADE のオブジェクトやビューを扱う描画&幾何演算部と mruby インタプリタを扱うスクリプト演算部の2種類に分かれています。