OSXのユーザ辞書の作り方

投稿者 nanki 2007-11-26 04:20:00 GMT

Leopardからは、小学館の辞書データとWikipediaが追加されて、とっても便利なDictionary.app.

たぶん、キーボード使用を前提とした小気味よいインターフェースがいいんだと思う。 慣れてしまうと、もう生のWikipediaを使う気はなかなか起きない。

こうなると、なんでもかんでも、同じインターフェースで済ませたくなるもので、そんな時に、こんな検索しにくいページを見たら、「Dictionary.appで検索できて然るべき」と思ってしまうのが自然な流れ。

というわけで、ライフゲームの辞書、Life LexiconをDictionary.app用に変換します。

まずは、テンプレートをコピーしてくる。 試しにmakeすると、あっさり辞書が作られる。 make installすれば、~/Library/Dictionaries 以下にコピーされ、Dictionary.appから検索できる状態になる。(Dictionary.appの環境設定で有効にしないとだめかも)

$ cp -r /Developer/Examples/Dictionary Development Kit/project_templates dict
$ cd dict
$ make
"""/Developer/Extras/Dictionary Development Kit"/bin"/build_dict.sh"  "My Dictionary" MyDictionary.xml MyDictionary.css MyInfo.plist
- Building My Dictionary.dictionary.
- Cleaning objects directory.
- Preparing dictionary template.
- Preprocessing dictionary sources.
- Extracting index data.
- Preparing dictionary bundle.
- Adding body data.
- Preparing index data.
- Building key_text index.
- Building reference index.
- Fixing dictionary property.
- Copying CSS.
- Copying other resources.
- Finished building ./objects/My Dictionary.dictionary.
echo "Done."
Done.

重要なのは、MyDictionary.xmlファイル。 辞書の元となるデータをパースして、XMLファイルを作ってやる。

<?xml version="1.0" encoding="UTF-8"?>                                                                               
<d:dictionary xmlns="http://www.w3.org/1999/xhtml" xmlns:d="http://www.apple.com/DTDs/DictionaryService-1.0.rng">    
  <d:entry id="blinker puffer" d:title="blinker puffer">                                                             
    <d:index d:value="blinker puffer" d:title="blinker puffer" />                                                    
    <d:index d:value="puffer" d:title="blinker puffer" />                                                            
    <h1><span class="headword">blinker puffer</span></h1>                                                            
    <span class="meaning">Any <a href="x-dictionary:r:puffer">puffer</a> whose output is <a href="x-dictionary:r:blinker">blinker</a>s.  However, the term is particularly used for p8 c/2 puffers.  ....
    </span>
  </d:entry>
  <!-- 以下d:entry の山 -->
</d:dictionary>

意味はほとんど見た目通り。
それぞれの項目は、インデックスと内容のXHTMLで構成される。 他の単語を参照する、href=”x-dictionary:r:ID”とかが特徴的か。 上の例では「blinker puffer」という単語に対して、pufferでも検索に引っかかって欲しいので、indexに加えてある。

パースしてXMLを吐くコードは、汎用性がないのでここには載せないが、今回は以下のようなLifeを表す図を、rcairoでpng画像に変換もしている。

OO..
O..O
..OO

画像などのリソースは、dict/OtherResources 以下の適当な場所に置いておき、普通のHTMLのように、<img>タグを使って埋め込む。

<p><img src="Images/blinker%20puffer_0.png"/></p>

これで、

$ make
$ make install

すれば、ユーザ辞書の完成。

続き

さて、これだけで終わりではもったいないので、先日作ったQuartz Compositionを組み込めないか、頑張ってみる。

HTMLにQuartz Compositionを組み込むのは簡単で、EMBEDタグを使って、

<embed id="composition" type="application/x-quartzcomposer" src="LifeLexicon.qtz" width="300px" height="300px" />

とする。

これで、普通のムービーならスタートするのだけど、ライフゲームなので第0世代の画像を渡してやらないといけない。

Quartz ComposerのWebKitプラグインにはJavaScript APIが用意されていて、publishしたパラメータを設定できるようになっている。 が、文字列しか設定できないようだ。画像はどうやって渡すのだろう。

調べてみると、Compositionの中で、Image Downloaderというパッチを使うらしい。 Image Downloaderは指定されたURLの画像をImageとして出力するパッチ。 なるほど。 ImageDownloaderの入力を、imageLocationという名前でPublishして・・・

var composition = document.getElementById("composition");
composition.setInputValue("imageLocation", "fullpath.png");

こんな感じになるのかな。 しかし、フルパス指定じゃないといけないのが面倒だなぁ・・・

先生、こちらにできあがったものが用意してあります。

結局、location.href と、EMBEDに勝手に付け加えたimage属性から、フルパスを取得する仕組みに。 文中のSpaceはCompositionというか、Universeというか。

//LifeLexicon.js
(function () {
  var waitForSpaces = function (spaces) {
    if (spaces.length == 0) return;

    if (spaces[0].loaded && spaces[0].loaded()) {
      var space = spaces.shift();
      var base = location.href.substr(0, location.href.lastIndexOf("/"))
      space.setInputValue("imageLocation", base + space.getAttribute('image'));
    } 
      
    with ({callee: arguments.callee}) {
      setTimeout(function () {callee(spaces)}, 100);
    }
  };

  var arrayFromNodeList = function (nodelist) {
    var result = [];
    for (var i = 0; i < nodelist.length; i++) {
      result.push(nodelist.item(i));
    }
    return result;
  };

  waitForSpaces(arrayFromNodeList(document.getElementsByTagName('EMBED'))); 
})();

なぜか、<script src="LifeLexicon.js">だと読み込まれないので、

<script>
  var tag = document.createElement("SCRIPT");
  tag.src = "./LifeLexicon.js";
  document.getElementsByTagName("HEAD")[0].appendChild(tag);
</script>

というコードも、<d:entry>内に埋め込むように。

これで「生きた」LifeLexiconが完成。

LifeLexicon

CREDITをどこかに入れないと配布できないらしいので、欲しい人には直接送ります。 いないだろうけど。

誰か、Ruby Reference Manualをこれで。

参考:

Quartz Composer - Conway's Game of Life - 宇宙大決戦

投稿者 nanki 2007-11-18 04:17:00 GMT

9月から半分Macユーザになったので、Macの開発環境も触ってみよう思い、まずは楽しそうなQuartz Composerに手を出してみた。

基本的には、Patchと呼ばれる部品をつなぎ合わせてプログラミングしていくものだが、ProgrammableなPatchも三種類(JavaScript, GLSL, Kernel Language?)用意されていて、後者二つは最近流行?のベクトル指向な言語。

以前から、GPUのシェーダ言語でライフゲームを書いて見たかったので、テーマはライフゲームに決定。 KOF2007の懇親会で、小波先生、池上さんとライフゲームの話題で盛り上がったのも理由。

メインとなるKernel Languageのコードは以下。 画像のある一点の色を決めるコードを書けば、あとは勝手にベクトル化してくれているはず。
(たぶん、ベクトル化のために)普通のif文は使えないので、compare関数を使う。

kernel __color conway(sampler image)
{
    float self = sample(image, samplerCoord(image)).r;

    float lives;
    lives  = sample(image, samplerCoord(image) + vec2( 1, -1)).r;
    lives += sample(image, samplerCoord(image) + vec2( 0, -1)).r;
    lives += sample(image, samplerCoord(image) + vec2(-1, -1)).r;
    lives += sample(image, samplerCoord(image) + vec2( 1,  0)).r;
    lives += sample(image, samplerCoord(image) + vec2(-1,  0)).r;
    lives += sample(image, samplerCoord(image) + vec2( 1,  1)).r;
    lives += sample(image, samplerCoord(image) + vec2( 0,  1)).r;
    lives += sample(image, samplerCoord(image) + vec2(-1,  1)).r;

    const float dead  = 0.0;
    const float alive = 1.0;
    float compare4 = compare(lives - 3.5, alive, dead);

    float r = compare(
        self - 0.5,
        compare(lives - 2.5, dead, compare4),
        compare(lives - 1.5, dead, compare4)
    );
    
    return __color(r, r, r, 1);
}

それから、1step前の状態が必要なので、JavaScriptで以下のようなPatchも作成。 フレームをまたがって値を保持するには、ObjectかMathのプロパティに値を入れておけばよい。

function (__image output) main (__image input, __image initial, __number dummy) {
  var result = {output: Object.prev || initial};
  Object.prev = input;
  return result;
}

後は適当にPatchを組み合わせていけば、完成。
Apple Remoteでコントロールしたり、スクリーンセーバにしたり、1フレームで3step進めたりしなければ、もうちょっと単純になる。
quartz composer screenshot

ライフゲームのルールについてはWikipediaを参照していただくとして、自分の周囲9マスから次の状態を決定する、という非常にローカルで単純なルールから、あまりに複雑で大域的な発展が繰り広げられる様は、宇宙や生命といった壮大なものを連想させる。

max vs glider guns
ライフゲーマー必見!ものすごい勢いで宇宙を浸食するmaxと、MITが開発したグライダーガンの、宇宙を賭けた闘い。

参考:

ライフゲーム - Wikipedia


CGAL - Computational Geometry Algorithms Library

投稿者 nanki 2006-11-24 05:48:00 GMT

CGAL

すごいの一言。幾何計算に関するアルゴリズムライブラリ。

あこがれの、あんなアルゴリズム、こんなアルゴリズムがいっぱい。

Pythonバインディングしかないか・・・

demo


Hough変換

投稿者 nanki 2005-12-30 07:11:00 GMT

画像から直線っぽいところを検出する手法。

画像上のある点(x,y)を通る直線は無数にあり、

y = a * x + b

を満たす a,b の集合が得られるが、この(x,y)→(a,b)への変換がHough変換。(他にもsin,cosを使った表記法はあるけど本質は同じだろう)

xy平面上の一点はHough変換により、a,b上では直線となる。 この変換を、全ての点(直線の候補点)について行い、a,b平面上で積算する。

ところで、Hough平面(ていうのかな?)上の一点はxy平面ではある直線を表すので、ある直線上の点が多ければ、Hough平面上のある点における積算値は高い値を示すはず。

なので、Hough平面上の積算値の高い点を、直線っぽい、とみなしてxy平面上の直線に戻してあげる、という仕組み。

円の場合も同様に処理できるが、こちらはパラメータの数が多いので、積算する空間が三次元になってしまう。

参考:

CodeZine:Hough変換による画像からの直線や円の検出