はじめてのMorphicチュートリアル(第5回)「メソッドの登録」

前回はモーフを拡張したMyMorphクラスを作成した。実際には何も機能は追加せず設定も変えなかったため、元のモーフと(見た目は)全く同じである。
今回はMyMorphに変更を加え、元のモーフとは異なる大きさに変化させる。
Morphを生成すると、幅が50ピクセル、高さが40ピクセルの青で塗りつぶされた長方形が表示される。これを縦横16ピクセルの正方形に変えよう。
第2回のように、生成したモーフに後から設定を行って大きさを変えることは可能だが、モーフを生成した時点からそのような大きさにする場合、初期化のプロセスを修正する必要がある。モーフ(に限らずどのオブジェクトも)は生成の途中で、自分に対してinitializeメッセージを送る。(Behaviorクラスのnewで定義されている)
そのため、初期化を担うinitializeメソッドに修正を加えたいのだがモーフ全体が変わってしまうと困るので、MyMorphに新たにinitializeメソッドを追加して対応する。
initializeメソッドを追加するには、ワールドメニューからSystem Browserを起動する。System Browserで前回のMyMorphクラスを探すのだが、手っ取り早く見つけるために左上の小さな入力欄にクラスカテゴリの一部を入力する。
設定したクラスカテゴリは’Hajimeteno Morphic Tutorial’だったので、入力欄に haji と入力するとそのクラスカテゴリが現れる。クラスカテゴリ一覧から’Hajimeteno Morphic Tutorial’を選ぶと、上段の左から2番目のペインにMyMorphが現れる。これは、選んだクラスカテゴリに属するクラスの一覧である。クラスペインからMyMorphを選ぶと、その右のペインにno messagesが現れる。上段の右から2番目のペインには、選択したクラスのプロトコルの一覧が現れる。プロトコルとは、これから説明するメソッドの分類を表したものである。ここまでの操作を行い、プロトコルペインでno messagesを選ぶと以下のような画面が表示される。
MyMorphクラス
コードペインには以下のようなテキストが選択された状態になっている。

messageSelectorAndArgumentNames
    "comment stating purpose of message"
    | temporary variable names |
    statements

これはメソッドのテンプレートである。メソッドとはオブジェクトがメッセージを受け取った際、オブジェクトがどんな動作を行うかを定義したものである。メソッドは、オブジェクトが属するクラスに登録することで定義する。上のテンプレートが登録の形式を示している。
1行目は受け取るメッセージを記述する。
2行目にはメソッドのコメントを記述する。コメントの記述は省略することができる。なお、Smalltalkではコメントの両端をダブルクオーテーション(”)で囲む。
3行目にはメソッド内で使用する一時変数を記述する。Smalltalkではメソッド内で利用する一時変数は必ず宣言しておく必要がある。複数の一時変数はスペースで区切り、全体をバーティカルバー(|)で囲む。一時変数を使用しない場合は、2本のバーティカルバーだけを残すこともできるし、それ自体の記述を省略することもできる。
4行目以降にメソッドの内容を記述していくことになる。メソッドの内容を省略することも可能で、その場合はメッセージを受け取っても何もしないことになる。
Smalltalkでは、3種類のメッセージの形式があるのだが、ここでは単項メッセージとよばれる形式のメソッドの書き方だけを学ぼう。先ほど作成しようとしたinitializeメッセージが単項メッセージにあたり、オブジェクトに最も単純なメッセージを送ることができる。先ほどのメソッドテンプレートを書き換えて、以下のように入力する。

initialize
    super initialize.
    self extent: 16@16

入力が完了した時点で赤く表示される部分があれば何か問題があるので入力内容をチェックする。問題なさそうであれば、右クリックでAcceptを選ぶか、Command-S(Alt-S)を押す。すると以下のような画面が現れる。
署名ダイアログ
これはコードを登録した人を記録するためのダイアログウィンドウである。適当な名前やイニシャルを入力してOKを押すと、次回からは表示されなくなる。
さて、メソッドを追加したMyMorphを試したいのだが、今の時点でたくさんの青い四角が画面に溢れているだろう。これらのモーフを削除するには、ShiftとOptionを押した状態でモーフをクリックする。モーフの周囲にたくさんのボタン(Haloと呼ばれる)が現れたら×印のボタンを押してモーフを削除する。
Halo
画面が奇麗になったところで、Playgroundを使って以下のように入力する。

MyMorph new openInWorld.

画面の左上に小さなモーフが表示されるのがわかるだろう。
今回は長くなったのでここまでとし、次回は登録したメソッドの内容について学び、更に追加を行う。
(第5回おわり)

はじめてのMorphicチュートリアル(第4回)「クラス定義」

ここまでモーフの状態を取得したり、設定を行う方法について学んできた。今回は基本的なモーフを拡張して、新たな機能を持ったモーフの作り方について学ぶ。
モーフを拡張して新たなモーフを作るには、Morphクラスのサブクラスを作る必要がある。
そのために、デスクトップをクリックしてワールドメニューを出してSystem Browserを選ぶ。
System Browser
System Browser(システムブラウザ)は、古くからあるSmalltalk上の開発ツールの一つで、コードを作成する場合はたいていこれを使う。5つの大きなペインを持ち、その他にも入力欄やボタンなどが配置されている。これらは重要な機能を持っているので少しずつ紹介していく。
下側の一番大きなペインをコードペインと呼ぶ。ここには、こんな内容が表示されているはずだ。

Object subclass: #NameOfSubclass
	instanceVariableNames: ''
	classVariableNames: ''
	category: ''

これは新たなクラスを生成する際のテンプレートである。新しいクラスを作りたいなら、このテンプレートを修正して使うことができる。新しいモーフを作るために、上の内容を以下のように書き換える。

Morph subclass: #MyMorph
	instanceVariableNames: ''
	classVariableNames: ''
	category: 'Hajimeteno Morphic Tutorial'

書き換えたのは3カ所で、1カ所目は Object を Morph に変えた。Morphを新しく作成するクラスの親(基底)クラスとして、新たなクラスがMorphの性質を受け継ぐようにする。
2カ所目は #NameOfSubclass を #MyMorph に変えた。これは新たなクラスの名前(MyMorph)を表している。名前の前に#(シャープ)記号が付けられているのは、この文字列がSymbol(シンボル)であることを表している。Symbolとは環境内で唯一性を持つ文字列のことである。クラス名は環境内で一意に決まるので命名にはSymbolを用いる。
最後は、category:の後の ” を ‘Hajimeteno Morphic Tutorial’ に変えた。Smalltalkではカテゴリを用いてコードを管理する。カテゴリにはクラスカテゴリとメソッドカテゴリ(プロトコル)があり、ここで指定するのはクラスカテゴリである。後から参照しやすいようなカテゴリ名を付けておく。
ところでSmalltalkでは両端をシングルクオート(’)で囲むことで文字列を表す。だから、 ” は空の文字列ということになる。
クラスを実際に定義するには、入力したコードペインで右クリックして Accept を選ぶかCommand-S(Alt-S)を押す。すると、System Browserの左上のペインが変化して Hajimeteno Morph Tutorial という項目が現れる。
このペインはクラスカテゴリペインと呼ばれ、さまざまに分類されたクラスカテゴリの一覧が表示される。
ワールドメニューでPlaygroundを開き、左側のペインで以下を入力する。

MyMorph new openInWorld.

すると、今までと同じモーフが画面左上に表示されるのがわかる。
さて、今まではMorphにnewというメッセージを送っていた。これは、Morphクラスに属する新しいモーフを生成することを意味している。
今回、MyMorphにnewメッセージを送ることで、MyMorphクラスに属する新しいモーフを生成することになる。
このようにSmalltalkでは、定義したクラスにnewというメッセージを送ることで新たなモーフを生成する。生成されたモーフを定義したクラスのインスタンスとも呼ぶので覚えておこう。
次回、このクラスを拡張する。
(第4回おわり)

はじめてのMorphicチュートリアル(第3回)「モーフの位置」

第2回ではモーフの大きさの取得や設定の方法について学んだ。今回はモーフの位置などを取得・設定する方法について学ぶ。
モーフの位置の取得・設定はさまざまな手段が用意されている。まずモーフの四隅の座標値は以下のメッセージで取得することができる。

topLeft
左上の座標
topRight
右上の座標
bottomLeft
左下の座標
bottomRight
右下の座標

前回の例と同様、上のメッセージにコロン(:)をつけて座標値とともにキーワードメッセージを送ると、それぞれの値を設定することができる。当然のことながら、どのやり方で座標値を設定してもモーフの位置が変化するだけで大きさが変わるわけではない。
取得と設定の応用として、二つを組み合わせた例を以下に示す。これをPlaygroundの右下のペインに入力し、Command-D(Alt-D)で評価してみよう。

self topLeft: (self topLeft + (2@0))

モーフが少し右に移動したのがわかるだろうか。
この式の意味は、まずself topLeftによってモーフの左上の座標値を得る。おそらく最初はモーフ自体が画面の左上にあるため、得られる座標値は0@0となる。
(たいていのコンピューターグラフィックスシステム同様、Morphicでも画面左上が原点(0,0)となっている)
この座標値に対して、2@0という座標値を加える。座標値同士の加減算ではx成分・y成分それぞれについて計算が行われ、新たな座標値が生成される。この場合、(0+2)@(0+0)のような計算によって、2@0という座標値が得られる。
得られた2@0という座標値が、topLeft:というキーワードメッセージによってモーフに伝えられ、モーフの位置が元あった場所から右に2ピクセルほど変化する。これを連続的に用いればアニメーションのような効果が得られるので、後の回でゲームを作るのに利用するつもりである。
さて、座標値の計算式を書く上で間違えやすいのは、以下のように書いてしまうことである。

0@0 + 2@0

Smalltalkにおける@は、+や-と同様の中置演算子である。また、Smalltalkでは同じ種類の演算子において結合の優先順位は定められておらず、単に左から結合されていく。
つまり、上の式は以下のように解釈される。

((0 @ 0) + 2) @ 0

この場合、0@0という座標値に2が加えられる。座標値と普通の数値が加減算される場合、x成分・y成分それぞれに対して数値が加減算されるので、その答えは 2@2 となる。
さらに (2@2) @ 0 という計算が行われるにあたって、「座標値は@というメッセージを知らない」といったエラーが発生する。
MessageNotUnderstood
Smalltalkで実行時エラーが起こると、上のようなNotifier(ノーティファイア)が現れる。エラーについてはいずれ詳しく説明をするが、Notifierを見ると最上段にPoint(Object)とdoesNotUnderstand:が現れている。Notifierで最上段に表示されたものが、このNotifierを表示させている場所であり、この場合ObjectクラスのdoesNotUnderstand:メッセージとなっている。発生したエラーの具体的な意味は、座標値を表すPointクラスから上位のObjectクラスに至るまで、指定されたメッセージ(この場合は@)が見つからなかったということである。
話はどんどん脱線するが、先ほどの間違った式で発生するのはコンパイル時ではなく実行時だということに注意してほしい。つまり、式としては正しい形を成しているのだが、結果的に意味を持たないため実行時にエラーが発生する。Smalltalkはシンプルな文法と柔軟な仕組みで動作しているので、(慣れれば慣れるほど)コンパイル時エラーを出すのは他の言語ほど容易ではない。いずれ、そういったこともチュートリアルの中で説明しよう。
(第3回おわり)

はじめてのMorphicチュートリアル(第2回)「モーフの大きさ」

第1回ではモーフを生成する方法について学んだ。今回はモーフの属性の取得や変更について学ぶ。
Playgroundで以下を入力し緑色のボタンを押すと画面の左上に青色のモーフが現れる。

Morph new openInWorld.

モーフ
モーフの属性を取得したり変更するために、モーフに対してメッセージを送る。
モーフの大きさを取得したい場合には、extentメッセージを送る。Playgroundの右下のペインにselfとだけ表示されている。ここで以下のように入力する。

self extent.

入力したところで、Command-P(Alt-P)を押すと、以下のように表示される。
extent
画面上に 50@40 と表示されているのが見える。これは、モーフの幅が50ピクセル、高さが40ピクセルであることを示している。
Smalltalkでは、歴史的にx@yの形式でXY座標値を表す。しかし、2個の成分を持ったベクトルデータは様々な使い道があり、特に成分が数値の場合には座標値を別の用途に用いることが多い。そのためモーフの幅と高さといったデータも座標値として表されている。
ちなみに、50@40から50を取り出したい場合には座標値にxというメッセージを送る。次のコードを見てみよう。

(50@40) x.

入力できそうなところならどこでも構わない(起動時に開かれる説明画面でもよい)ので上の2行を入力したあと選択し、Command-P(Alt-P)を押すと50という値が表示される。40を取り出すには、xの代わりにyというメッセージを送る。
Playgroundで入力した式を使ってモーフの幅だけを取得したいなら以下のように書けばよいが、後述するようにそのための特別なメッセージも用意されている。

self extent x.

モーフの大きさを設定したい場合には、extent:メッセージを送る。extent:はキーワードメッセージと呼ばれるもので、座標値で表した大きさを引数としてメッセージを送ればモーフの大きさを変えることができる。
Playgroundの右下のペインで入力したものを以下のように変える。

self extent: 100@100.

入力後にCommand-D(Alt-D)を押すと、モーフの大きさが変わる。
幅や高さを個別に取得したり指定したい場合は、それぞれ以下のようなメッセージを送ればよい。

width
幅の取得
width: 数値
幅の設定
height
高さの取得
height: 数値
高さの設定

キーワードメッセージの場合は、対応する値とともにメッセージを送る。
なお、Morphicではモーフの幅や高さを個別に管理しているわけではない。モーフの大きさは次回説明する位置情報とともに一括して管理されている。
(第2回おわり)

はじめてのMorphicチュートリアル(第1回)「Morphicとは」

Smalltalkの初心者に対してMorphicの使い方を紹介していく。なるべく簡易に少しずつMorphicについて学べるような内容を心がけていきたい。
SmalltalkではMVCモデルによるGUIが有名であるが、Morphicは最近の実装(Squeak, Pharo, Cuis等のSmalltalk)において、GUIを構築するフレームワークとして用いられている技術である。
このチュートリアルでは実装としてPharoを用いる。PharoはオープンなSmalltalk実装で、活発に開発が行われているものの1つである。
試しながら学ぶためにPharoのサイトでDownloadのページへ移り、Windows/Mac/LinuxなどのOSに応じた最新版をダウンロードしよう。
ダウンロードしたら適当なフォルダに展開してPharoのアプリケーションを起動する。
起動すると下のような画面が現れる。
起動画面
執筆時点の最新版は4.0である。毎年更新されて内容も大きく変わるが、Pharoはシンプルがモットーなので起動画面のイメージはバージョン間でそれほど変わらない。
まずは起動したPharoでモーフ(Morph)と呼ばれるオブジェクトを生成してみよう。背景を適当にクリックしてWorldメニューを出し、上から2つ目のPlaygroundを選ぶ。
Playground
Playgroundとは4.0からWorkspaceの代わりになったインスタントな操作環境である。Smalltalkの適当な式を評価することができるだけでなく、評価結果の詳細な分析もしやすくなっている。
モーフを生成するために、Playgroundに以下の式を入力してみよう。

Morph new openInWorld.

入力したらPlaygroundのタイトル右下にある緑色のボタン「Do it all and go」を押す。
すると下のような画面になる。
Morph
PharoのロゴのPに掛かるように現れた青い四角形が生成されたモーフである。
先ほど入力した式では、Morphというクラスに対してnewというメッセージを送ってモーフを生成し、更に生成されたモーフに対してopenInWorldというメッセージを送ってモーフを画面に表示させている。(Smalltalkでは連続したメッセージ式は左結合となる)
これがモーフの基本形であり、ここから様々なモーフを派生させていく。こんなシンプルなものでGUIまで作り上げていくんだからSmalltalkerはこわい。
(第1回おわり)
補足
チュートリアルを試している途中でやめたくなった場合、2通りの終了方法がある。
1つ目は今までやったことを残す方法で、デスクトップをクリックして現れるWorldメニューでSaveを選ぶ。
2つ目は起動後に行った行為を残さない方法で、デスクトップをクリックして現れるWorldメニューでQuitを選ぶ。
Pharoに限らずSmalltalkの多くの処理系では、ゲームに似た感覚で作業を残すか残さないかを決めることができる。

BeagleBone Blackへのubuntu 14.04インストール

ずっと以前に購入したBeagleBone Black(Rev A5B)にubuntu 14.04をインストールした。
手順はここを参考にした。
「Get prebuilt image:」の下に書かれた
wget https://rcn-ee.net/rootfs/2015-02-19/microsd/bone-ubuntu-14.04.2-console-armhf-2015-02-19-2gb.img.xz
を参考に、bone-ubuntu…img.xzというファイル(ファイル名は変わる可能性あり)をMac(Mavericks)にダウンロードし、Terminalで
unxz bone-ubuntu-14.04.2-console-armhf-2015-02-19-2gb.img.xz
を実行した後で、適当なMicroSDカードに書き込んだ。
BeagleBone BlackにMicroSDカードを挿入して起動したらubuntu 14.04が起動した。(ユーザー名はubuntu、パスワードはtemppwdでログイン)
ログインしたら、パーティションを拡張するスクリプトを実行するのだが、なぜかネームサーバーの設定がうまくない。
/etc/resolvconf/resolv.conf.d/original ファイルの中身を修正し、一番最後の行を以下のようにして再起動した。
nameserver 8.8.8.8
再起動後、以下を実行する。
cd /opt/scripts/tools
git pull
sudo ./grow_partition.sh
sudo reboot

更なる再起動の後でパーティションが拡張された。

第71回Smalltalk勉強会でのLT内容(後編)

12/25に年末恒例のSmalltalk勉強会があり今年も参加してきた。昨年はビデオ参加で他の方の発表がちゃんと聞けなかったのだけど、今年はいろんな方の発表が聞けて多いに楽しんだ。
せっかくのチャンスなので自分も発表をしたけど、他の方の真面目な取り組みに対してかなり緊張感のない内容で申し訳なかった。
パワポなども一切持って行かずにその場でのぶっつけデモだったので、せめて発表内容についての簡単な説明をしてお詫びしたいと思う。
なお、記事が長くなりすぎたので、前編・後編に分けて公開する。
LTでは前半にRoassal3dのデモを行い、後半はLeapMotionと接続するデモを行った。
別にLeapMotionでなくとも良かったのだが、他にネタが思いつかなかったのとデバイスが小さく持ち運びやすかったから選んだに過ぎない。
LeapMotionはKinectのように人体の位置を測定する入力デバイスであるが、Kinectと異なる点は身体全体ではなく、肘から指までの測定に特化していることである。
LeapMotionはSDKが公開されており、自分のアプリケーションと連携させることができる。

Roassal3dの準備とFluoのインストール

今回の内容を再現するには、第71回Smalltalk勉強会でのLT内容(前編)に従って必要なパッケージをインストールする必要がある。

LeapMotionとの接続

Pharoとの接続については、前回の記事でインストールしたFluo DevicesにNativeBoost経由でデータを得るパッケージを組み込んであるが、NariveBoost用の共有ライブラリは別途インストールする必要がある。
まだ開発途中でMac OS X版しか作成していないため、ここから直接共有ライブラリ(NBLeapMotion.dylib)をダウンロードする。
ダウンロードしたらMac版のPharo VMのフォルダを開き、Contents/MacOS/Pluginsフォルダの中にダウンロードしたNBLeapMotion.dylibと、LeapMotion SDKに含まれているlibLeap.dylibをコピーしておく。

動作確認

LeapMotionをPCに接続し、メニューバー上のインジケータが緑色に変わったことを確認したら、Fluo上での動作確認を行う。
左側のメニューからloadをクリックしてNBLeapMotionTest1を選ぶ。

NBLeapMotionTest1
NBLeapMotionTest1

左側のメニューからopen allをクリックしてノードをスタートさせてLeapMotionの上に手をかざすと、LeapMotionFingerBehaviorのプロパティボックスに読み取った指や掌の位置が表示される。
LeapMotionFingerBehavior
LeapMotionFingerBehavior

手の動きにあわせて立方体を動かす

後半のデモのハイライトは、LeapMotionで読み取った手の動きに従って、Roassal3dの3Dビュー上の立方体を動かすというものであった。
このデモはRoassal3dのサンプルを改造しながら行っていった。
まず、Roassal3dDemo1をloadしておく。次に、LeapMotionに関係するプロキシであるNBLeapMotionProxyを追加し、さらに階層的なデータを取得するトピックであるNBLeapMotionTopicを追加する。トピックからどんなデータが得られるかを調べるためにInspectBehaviorも追加して下図のように接続しておく。

LeapMotionFingerBehavior
LeapMotionFingerBehavior

NBLeapMotionProxyのチェックボックスをオンにするとデータの取り込みが始まる。InspectBehaviorのInspectボタンを押すと最初に到着したデータをInspectorウィンドウで調べることができる。dataをダブルクリックすると上図のようにDictionary形式のデータがあり、#id, #left, #rightの3つのキーがあることがわかる。
このトピックはLeapMotionから得られたデータを階層的なDictionaryデータとして送信する機能を持つ。
デモでは右手の掌の座標を用いたので、#right から #palm へとデータを掘り下げる必要がある。
これをFluoのノードで実現するには、DataUnpackBehaviorを用いる。このビヘイビアはDictionaryを分解して、キーの名前のついたアークにデータを送信するというものである。
なお、アークに名前を付けるにはアーク上で右クリックしてadd labelを選べば良い。
DataUnpackBehaviorを用いてグラフを下図のように変更する。
DataUnpackBehavior
DataUnpackBehavior

InspectBehaviorでClearを押して再度データを取り込み直すと、x,y,zをキーとするDictionaryデータが得られることがわかる。
後はこの3次元座標をRoassalに送れば良いのだが、LeapMotionから得られたデータはcm単位で大きいためデータを小さくする。この役割はScaleBehaviroが担う。これは得られた数値データに一定の倍率を掛けて送信するという単純なものである。
不要となったTimerBehaviorやJoystickBehaviorを取り除き、ScaleBehaviorを通してFluoR3AbsolutePositionに接続したグラフは以下のようになる。
LeapMotion to Roassal3d
LeapMotion to Roassal3d

以上のデモが動いたところで発表を終えた。ということで、この記事もおしまい。
(Smalltalk勉強会ではご清聴ありがとうございました)

第71回Smalltalk勉強会でのLT内容(前編)

12/25に年末恒例のSmalltalk勉強会があり今年も参加してきた。昨年はビデオ参加で他の方の発表がちゃんと聞けなかったのだけど、今年はいろんな方の発表が聞けて多いに楽しんだ。
せっかくのチャンスなので自分も発表をしたけど、他の方の真面目な取り組みに対してかなり緊張感のない内容で申し訳なかった。
パワポなども一切持って行かずにその場でのぶっつけデモだったので、せめて発表内容についての簡単な説明をしてお詫びしたいと思う。

「オトナのためのVPL」

こんなタイトルの発表だったのだが、開始から2時間後の発表で、ビールですっかり酔っぱらってしまい1割も言えていなかったが趣旨としてはこんな感じだった。

Scratchびすけっとを始めとしてさまざまなビジュアルプログラミング言語(VPL)が流行している。テキスト系の言語に対してVPLを卑下する見方もあるけど、プロトタイピングなど使う場面によっては生産性を高める場合もあるだろう。
過去10年くらいグラフベースのVPLに取り組んできたが、2年前のSmalltalk勉強会の帰宅途中の会話にヒントを得て、Pharo Smalltalkの素晴らしい開発環境にシームレスにつながるVPLが作れないか試行錯誤した。
実用的な段階にはまだまだ遠いが、現状でどのような感じなのかを見てもらいたい。

Roassal3dの準備とFluoのインストール

デモではインストールから始めて格好よく見せようと思ったのだが、実は事前にRoassal3dのインストールを行っていた。

MetacelloConfigurationBrowser open.

でRoassal3dを選んでInstall Stable Versionを押し、インストールを済ませておく。
次に公開されているFluoの最新バージョンをインストールする。今回はデモでRoassalとLeapMotionを使ったのでそのパッケージを追加する。

Gofer new
  url: 'http://smalltalkhub.com/mc/oohito/Fluo/main';
  package: 'Grafeo';
  package: 'Fluo Core';
  package: 'Fluo Devices';
  package: 'Fluo UI';
  package: 'Fluo LeapMotion';
  package: 'Fluo Roassal3d';
  load.

上記をWorkspaceにコピペしてDo itすればよい。
なお、利用しているPharoのバージョンは3.0である。環境に合わせてダウンロードしておく。
小田さんの発表で「Jun」のPharo移植が述べられていたので、将来的には3D表示系としてそちらを採用する可能性が高いと思う。

Roassal3dのデモ

Fluo new openInWorld.

Workspaceで上記をDo itすると、以下のような画面が現れる。

Fluo起動画面
Fluo起動画面

左側のボタンでLoadを選んでRoassal3dDemo1を選ぶと以下のような画面になる。
Roassal3dDemo1
Roassal3dDemo1

これはRoassal3dの3Dビューに立方体を表示させ、それを黄色に赤のジョイスティックを模したMorphで上下左右に動かすデモである。ジョイスティックは赤いノブをマウスでドラッグすることで、上下左右に動かすことができるので試してみる。

ノードの説明

このデモには5つのノードがある。ノードの右下のプロパティボックスに小さな文字で書かれているのがノードの名前である。列挙すると以下の通り。

  • Roassal3dProxy
  • FluoR3AbsolutePosition
  • MonitorBehavior
  • JoystickBehavior
  • TimerBehavior

ノードは3種類あり、円の中央の図形で区別する。中央の図形が正方形のものはプロキシといってFluoの外の世界をつなぐノードである(このデモだとRoassal3dProxy)。円のものはビヘイビアと言ってFluo内でさまざまなデータ処理を行うノードである。三角形のものはトピックといってプロキシとビヘイビアをつなぐノードである(このデモだとFluo3dAbsolutePosition)。ノード間は一方向のアークで結ばれ、その向きでデータが流れる。
Roassal3dProxyはその名の通り、Roassal3dを用いるためのノードである。このノードは接続されたトピックに従ってRoassal3dのエレメントを生成して表示する。
FluoR3AbsolutePositionは、プロパティで指定した形状のRoassal3dエレメントを操作するトピックである。入力として3次元座標を受け取ると、その位置にエレメントを配置する。
MonitorBehaviorは受け取ったデータをプロパティボックスに表示するだけのデバッグ用ノードである。他には何もしない。
JoystickBehaviorは、プロパティボックスに表示したジョイスティックの位置の2次元座標(-1から+1の範囲)を送信するノードである。ただし、tickメッセージが来た時だけデータを送信する。
TimerBehaviorは、指定した間隔でtickメッセージを送信するノードである。
これらのノード間でデータは、(デバッグ用ノードであるMonitorBehaviorを除くと)TimerBehavior -> JoystickBehavior -> FluoR3AbsolutePosition -> Roassal3dProxyのように流れる。つまり、TimerBehaviorで生成されたtickメッセージがJoystickBehaviorに送られると、ジョイスティックの位置データがFluoR3AbsolutePositionに送られ、それが形状データとともにRoassal3dProxyに送られて3Dビューが更新される。

動かしてみる

TimerBehaviorのプロパティボックスにあるチェックボックスをクリックすると、tickメッセージの送信が100ms間隔で始まる。データを受け取ったノードは中央が白くなるのがわかる。ジョイスティックを動かすと座標がMonitorBehaviorに表示されることがわかる。
Roassal3dProxyのプロパティボックスにあるチェックボックスをクリックすると、下図のようにRoassal3dの3Dビューが現れ、立方体が中央に表示される。

Roassal3dの3Dビュー
Roassal3dの3Dビュー

ウィンドウをずらしてジョイスティックを操作すると、操作に応じて立方体が上下左右に動くのがわかる。

修正

このデモでは、上下の向きがジョイスティックと立方体の動きとで逆になってしまう。
発表時に南谷さんからその修正ができないかと問われたのだが、酔っぱらっていたためにその場で対応できなかった。
これに対応するにはデータを修正するノードを追加すれば良い。新しいノードを作るのは手間がかかるので、Smalltalk式を設定できるExpressionBehaviorを追加する。
Fluo画面の上部にあるnew behaviorというノードのプレースホルダをドラッグして、適当な場所でドロップすると、ポップアップメニューが現れる。
その中のdata convertサブメニューからExpressionBehaviorを選ぶとそのノードが追加される。

ExpressionBehaviorの追加
ExpressionBehaviorの追加

expression=の右の空欄をクリックすると入力欄が現れるので以下をコピペする。

data at: #y put: (data at: #y) negated. data

受け取ったDicionaryデータ(data)のyキーの数値の正負を反転させるというものである。ExpressionBehaviorはこのようにインスタントにSmalltalk式を埋め込むことができる。
後は下図のようにアークを繋いでデータの流れを変える。

流れを変える
流れを変える

アークを繋ぎ変えるには、アークをクリックして丸いハンドルを出し、それをドラッグ&ドロップすることで行う。
以上でジョイスティックの上下の向きが立方体の動きと合うようになった。
記事が長くなったので、いったん打ち切ってLeapMotion関係は別記事にまとまる。

クリスマスのコード

クリスマスの夜はコーディングに限りますね。
ちょっと思いついたプログラムを公開しました。
ネタはちょっと古くなった感の強い美人時計です。
参考にさせて頂いたのはこちら
現在時刻から画像を入手して背景画像に設定します。コードはこんな感じ。

updateBackground
    | url |
    DateAndTime now
        in: [ :now |
            url := 'http://bijint.com/jp/img/clk/{h}{m}.jpg'
                format:
                    {(#h -> (now hour printPaddedWith: $0 to: 2)).
                    (#m -> (now minutes printPaddedWith: $0 to: 2))} asDictionary ].
    (PluginBasedJPEGReadWriter on: url asUrl retrieveContents readStream) nextImage setAsBackground.
    self currentWorld backgroundMorph layout: #scaledAspect

これを30秒おきに呼び出しています。もっと効率の良いコードはかけますが、どうせ使い捨てなのでこの程度で。
以下をWorkspaceでDo itすればインストールできます。

Gofer new
  url: 'http://smalltalkhub.com/mc/oohito/Fluo/main';
  package: 'BijinTokei';
  load.

起動は以下で。

BijinTokei start.

終了は以下で。

BijinTokei stop.

表示された方が美人かどうかはさておき、お楽しみください。
メリークリスマス!

Pharoで3D表示

これはSmalltalk Advent Calendar 2014の12/23の記事です。
以前からPharoで3D表示させたいと考えていた。NativeBoost経由でOpenGLを使うNBOpenGLを試してみたのだが、どうやらメンテされていないらしくうまく動作しない。
そんなところ、第67回Smalltalk勉強会でRoassalが取り上げられた。あいにく仕事で参加できなかったのだが、プレゼン資料でRoassal3dについても紹介されていたので、遊んでみることにした。

Roassal3dの導入

MetacelloConfigurationBrowser open.

上記をWorkspaceでDo itしてConfiguration Browserを起動する。一覧からRoassal3dを選んでInstall stable versionをクリックすると自動的にRoassal3dがインストールされる。

サンプルの実行

Roassal3dの機能をひととおり試してみるには以下をWorkspaceでDo itする。

R3RoassalExample new open.

いろんなアイコンが並んでいるのでマウスで左クリックするとそれぞれのデモウィンドウが現れる。右クリックをするとBrowserが現れ、対応するメソッドが表示される。
デモウィンドウの操作はキーおよびマウスで行う。キー操作では、Wは前進、Sは後退、Aは左移動、Dは右移動、Cが下移動、スペースが上移動できる。マウスではドラッグにより向きを変えることができる。
デモの中にはアニメーションを表示したり、マウスの動きに反応するものがある。

3Dビューの作成

R3Viewのインスタンスに対して、R3Elementのインスタンスを追加することで3Dビューを作成する。
R3Elementには形や位置、向きなどの様々な属性がある。立方体や球体など一般的な物体は、R3Elementを直接生成せず、R3Shapeの該当するサブクラスを経由して生成する。
以下は最も簡単なサンプルで3Dビューの中心に立方体を置くものである。

| view |
view := R3View new.
view add: R3CubeShape new element.
view open.

上記で生成した3Dビューをinspectしてelementsを調べると2個の要素がある一方は生成した立方体で、もう一方は照明である。
立方体の位置を変えるには、R3Vectorオブジェクトを引数としてposition:メッセージを送れば良い。

view elements last position: (R3Vector x: -1 y: -1 z: 0); signalUpdate

先ほどのコードに続けて上記をDo itすれば、立方体は中央から左下に移動する。
position:を送っただけでは3Dビューに変化は現れないので、signalUpdateを送って3Dビューを更新する。

アニメーション

3Dビュー上の物体を動かす手段としてアニメーションがある。R3ViewのインスタンスにR3Animationのインスタンスをアニメーションとして登録すると、3Dビューの更新にあわせてアニメーションが動作する。R3AnimationはR3Elementに作用して位置や向きを変えることができる。
以下のコードをDo itすると、立方体が中央から左下に移動する。

| view element animation |
view := R3View new.
element := R3CubeShape new.
view add: element element.
animation := R3LinearMove new on: element by: (R3Vector3 x: -1 y: -1 z: 0).
view addAnimation: animation.
view open.

複数のアニメーションを連結させて、連続したアニメーションを作ることもできる。
以下のコードでは、立方体が4隅を連続して移動する。

| view element a1 a2 a3 a4 |
view := R3View new.
element := R3CubeShape new element.
view add: element.
a1 := R3LinearMove new on: element by: (R3Vector3 x: -1 y: -1 z: 0).
a2 := R3LinearMove new on: element by: (R3Vector3 x: 1 y: -1 z: 0).
a3 := R3LinearMove new on: element by: (R3Vector3 x: 1 y: 1 z: 0).
a4 := R3LinearMove new on: element by: (R3Vector3 x: -1 y: 1 z: 0).
a1 followedBy: a2.
a2 followedBy: a3.
a3 followedBy: a4.
a4 followedBy: a1.
view addAnimation: a1.
view open.

インタラクション

今までの例では3Dビューは固定されたカメラからの画像しか得ることができなかったが、冒頭のサンプルのようにキーボードやマウスを用いてカメラの位置を変えることができる。
そのためには以下のコードを追加すればよい。

view addInteraction: R3MKControl.

以上概観したように、Roassal3dはPharo上で簡単に3D表示することのできる優れたパッケージである。
この記事では一つの物体のみ操作したが、少ない労力で多数の物体の操作を行うこともできる。
Roassal3dに関する情報はObject Profileのwebサイトから得ることができる。