/**/

チュートリアル 47:
JavaScriptでのJitterオブジェクト・コールバックの使用

前の2つのチュートリアルでは、JavaScript コードでJitter オブジェクトやマトリックスを使うことによる可能性や優れている点について見てきました。このチュートリアルでは、すでに見てきたテクニックの多くを使って、Jitter の OpenGL パッチ全体を JavaScript の中にカプセル化します。しかし、最も重要なことは、すでにJavaScript にカプセル化されているJitter オブジェクトの出力を「リスン(listen) - 待ち受け」して、そのオブジェクトにインタラクティブに応答する関数を設計する方法を学ぶことです。

いくつかの (jit.qt.moviejit.pwindowjit.windowのような)Jitter オブジェクトは、ステータス(右側の)アウトレットから、Max パッチャー内での、ユーザ主導の処理に応答してメッセージを出力します。例えば、ムービーファイルを jit.qt.movie に読み込んだ場合、このオブジェクトはメッセージ read の後にムービーのファイル名とステータスナンバを続けて、ステータスアウトレットから出力します。同様に、jit.pwindowjit.window オブジェクトは、ウィンドウ内のマウスイベントに応答して、ステータスアウトレットからメッセージを送信することができます。JavaScript の中でインスタンス化される Jitter オブジェクトは、それ自身のインレットやアウトレットを持たないため、これらのメッセージをキャッチし、それに応答して関数(これを「コールバック」関数といいます)を呼び出すためのオブジェクト(JitterListener と呼ばれます)を明示的に作る必要があります。

このチュートリアルは、これまでの JavaScript チュートリアルや、最初の OpenGL チュートリアルであるチュートリアル30:「3D テキストの描画」に目を通していることを前提としています。

・Jitter チュートリアルフォルダにある、47jJitterLitener.pat を開いて下さい。

このチュートリアルパッチで最初に気づくのは、その中にある Max オブジェクトが非常に少ないということです。実質的に、パッチャー内で行われるすべての作業は、このパッチにある js オブジェクトの内部で行われています。


チュートリアルパッチ:ほとんど見るべきものはありません

・Display と表示された toggle ボックスをクリックして下さい。qmetro オブジェクトが js オブジェクトへの bangメッセージの送信を開始し、jit.windowオブジェクトは、黒を背景として多数のシェイプ(1つの大きな球と2?3ダースの小さな球)で満たされているはずです。

js オブジェクトは、OpenGL レンダリングを実行する ( jit.window オブジェクトを含む) Jitter オブジェクトの完全なセットを持っています。

・マウスを jit.window の上に移動させて下さい。大きな球がマウスに「付いて」きて、ウィンドウ内を追いかけてきます。大きな球が小さな球に触れると、まるで固形物質が衝突するかのように小さい球を「押しのけ」ます。大きな球で小さな球をあちこち動かして、その感じをつかんで下さい。

js ファイルは OpenGL シーンを描画するだけでなく、マウスからのインタラクティブなイベントを扱います。これは、リスニングおよびコールバックというメカニズム(JitterListener と呼びます)によって行われています。このJavaScript コードでは、これについて学んでいきます。


球を回りに押しのける

JavaScript での OpenGL オブジェクトの生成

・チュートリアルパッチの js オブジェクトをダブルクリックして下さい。このパッチの js オブジェクトのためのソースコードが書き込まれたテキストエディタが表示されます。コードは、チュートリアルパッチと同じフォルダに‘47jMarbles.js’というファイルとして保存されています。ファイルの最初にあるグローバルコードブロックを見て下さい。

この JavaScript コードは、このシーンを描くため必要な、全ての JitterObject オブジェクトを生成します。描画コンテクストを表示するための jit.window オブジェクト、レンダリングを実行する jit.gl.render オブジェクト、そして、シーンの中で球を描き、動かすための多くの jit.gridshape オブジェクトがそれです。これらのオブジェクトは JavaScript ファイル の最初にあるグローバルコードブロックでインスタンス化されます。

var OBJECT_COUNT = 32; // 小さな球の数

この行では、シーンの中の小さな球の数を決定する変数(OBJECT_COUNT)が宣言されています。

// 表示用の[jit.window] オブジェクトを生成
// (私たちがリスン(待ち受け)するのはこのオブジェクトです): var mywindow = new JitterObject("jit.window","ListenWindow"); // デプステストをオフにします...代わりにブレンドを使います mywindow.depthbuffer = 0; // アイドルマウス(訳注:クリックがない状態でマウスカーソルを追跡する)をオンにします。 // これをリスン(待ち受け)します mywindow.idlemouse = 1;

次に、すべてを表示する jit.windowオブジェクトを作ります。JavaScript コードの中では、変数 mywindow によってこれを参照します。Max パッチの中と同じように、jit.windowには名前(“ListenWindow”)が必要です。これはOpenGL 描画コンテキストの中で使う名前と一致していなければなりません。ここでは、depthbuffer アトリビュートをオフにし、 idlemouse アトリビュートをオンにしています。idolmouse アトリビュートをオンにすることにより、jit.window オブジェクトはマウスをクリックしているかどうかにかかわらず、ウィンドウの中のマウスイベントを追跡することができます。

// ウィンドウの中に描画するために、[jit.gl.render] オブジェクトを作ります var myrender = new JitterObject("jit.gl.render","ListenWindow"); // 2次元投影を使います myrender.ortho = 2; // 不透明で完全な消去を行う(軌跡を残さない)ようにして、背景を黒にセットします myrender.erase_color = [0,0,0,1];

この jit.gl.renderオブジェクト(変数 myrender に代入されています)は "LitenWindow" という描画コンテキストを定義します。jit.window オブジェクト(上記)と、jit.gl.gridshapeオブジェクト(下記)はこの名前を共有します。ここでは、平行投影を使うことに決めています( ortho アトリビュートを 2 にセットしています)。これは、仮想カメラからの距離に基づいてオブジェクトのサイズを決定する場合に、描画コンテキストの z 軸が無視されることを意味します。これにより、基本的にシーンの2次元のレンダリングが作られます。erase_color の設定を 0 0 0 1 にすることによって(連続した描画の間で完全に消去するように設定し)、背景は黒にセットしています。

// マウスによるコントロールのために使用する[jit.gl.gridshape] オブジェクトを作ります var mywidget = new JitterObject("jit.gl.gridshape","ListenWindow"); mywidget.shape = "sphere"; mywidget.lighting_enable = 1; mywidget.smooth_shading = 1; mywidget.scale = [0.1,0.1,0.1]; mywidget.color = [1,1,1,0.5] ; mywidget.blend_enable = 1; mywidget.position = [0,0]; // 平行投影では z は必要ありません projection

ここで作られる第1の jit.gl.gridshape オブジェクト(変数 mywidget に代入されています)は、jit.window の中でマウスによって動かされる大きな球に相当します。これを作った後、Max パッチャーで行うのと同様に、この球が持っていて欲しい、全ての関連したアトリビュートをセットします。セッティングの中で position アトリビュートが2つのアーギュメント(x と y)しか持たない点に注意して下さい。平行投影では、どのようにシェイプが描画されるかは z 軸はには無関係です。

// [jit.gl.gridshape] オブジェクトの配列を作ります // ランダムに、ウィンドウ全体に渡って配置されます var mysphere = new Array(OBJECT_COUNT);

ここでは、小さな球の個々に名前をつけるのではなく、それらを Array (配列:mysphereという名前です)として扱っています。この配列の個々の要素は、別々の jit.gl.gridshape オブジェクトになっていて、配列の記法によってそれにアクセすることができます(例えば、mysphere[5] の場合、インデックスは 0 から数えるため 6 番目の球になります)

// 小さな球をランダムな色と、(x,y)位置によって初期化します for(var i=0;i<OBJECT_COUNT;i++) { mysphere[i] = new JitterObject("jit.gl.gridshape","ListenWindow"); mysphere[i].shape = "sphere"; mysphere[i].lighting_enable = 1; mysphere[i].smooth_shading = 1; mysphere[i].scale = [0.05,0.05,0.05]; mysphere[i].color = [Math.random(),Math.random(),Math.random(),0.5]; mysphere[i].position = [Math.random()*2.-1, Math.random()*2.-1]; mysphere[i].blend_enable = 1; }

このコードでは、実際に、個々の jit.gl.gridshape オブジェクトを、mysphere という Array のメンバになっている個別のJitterObject として生成しています。ここでは、JavaScript の for() ループを使い、コードの1ブロックでこれらのオブジェクト全てのアトリビュートの初期値を設定しています。color および position アトリビュートにはランダムな初期値を与えることによって、球をスクリーン全体に分散させ、それぞれ異なった色にしています。

// [jit.window] オブジェクトのための JitterListener を生成します var mylistener = new JitterListener( mywindow.getregisteredname(),thecallback);

最後に、JitterListener オブジェクトを代入する mylistener という変数を作っています。JitterListener オブジェクトは2つのアーギュメントを取ります。アーギュメントは、「リスン(待ち受け)」の対象となるオブジェクト、および、オブジェクトがイベントをトリガした際に呼び出される関数の2つです。この JitterListener オブジェクトは、jit.windowオブジェクト(mywindow)をリスンするように設定されています。JitterObject オブジェクトの getregistername() プロパティは、JitterListener によってアクセスすることのできるオブジェクトの名前(このケースでは、jit.window オブジェクトの名前で、これは描画コンテキストの名前と同じものです)を返します。このjit.window オブジェクトがイベントを生成するたび毎に、thecallback() という関数がJavaScript コードの中でトリガされます。これで、JitterListener のインスタンス化が完了しました。あとは、この JitterListener を(たいていの場合)単独でそのままにしておき、単純に、リスンされているオブジェクトからのイベントに応答してJitterListener がトリガするコールバック関数のメカニズムを扱うことができます。

・チュートリアルパッチャーに戻り、randomize と書かれたメッセージボックスをクリックするか、コンピュータキーボードのスペースバーを押して下さい。小さな球は色を変えて再度分散されます。js ファイルにある、randomize() 関数のコードを見てみましょう。

function randomize() // 小さな球を再設定します { for(var i=0;i<32;i++) { mysphere[i].color = [Math.random(),Math.random(),Math.random(),0.5]; mysphere[i].position = [Math.random()*2.-1,Math.random()*2.-1]; } }

randomize() 関数は小さな球を対象として、その色を変え、ランダムに分散させます。これによって、基本的に、いつでも好きな時にシミュレーションを再スタートさせることができます。

コールバック関数(The callback function)

jit.window オブジェクトは様々なマウスイベントに応答します。idelmouse アトリビュートが (このコードのように)1 に設定されている場合には、jit.window オブジェクトはウィンドウ内でマウスポインタが移動する時にマウス情報( mouseidle メッセージ)を出力します。ポインタがウィンドウ領域外へ出た場合にもマウス情報( mouseidleout メッセージ)を出力します。idlemouse アトリビュートの設定に関わらず、jit.window オブジェクトはウィンドウ内でのマウスクリックに対して応答します( mouse メッセージ)。

・チュートリアルパッチで、ポインタを jit.window の中へ移動させてマウスをクリックして下さい。白い球が緑色のトーラス(ドーナツ型)に変わります。マウスボタンを離すとトーラスは赤い球に変わります。ポインタを移動させると球は再び白に変わります。


球はこのようにマウスクリック・イベント(マウスボタンを押した場合と離した場合)に応答します

・JavaScript コードの thecoallback() という関数のコードを見て下さい。このコードは、JitterListener オブジェクトが jit.winodw オブジェクトからイベントを受け取るごとに実行されます。

function thecallback(event) // [jit.window] 内でマウスによってトリガされるイベントを扱うコールバック関数 { var x,y,button; // ローカル変数

パッチの中で、event コンストラクタは jit.window オブジェクトによって JitterListener オブジェクトに送信されたメッセージを格納します。ここでは、event メッセージのアーギュメントによって提供されるポインタについての情報を格納するローカル変数(xybutton )も宣言されています。

その後、JavaScript の if() 構文を使い、event の eventname プロパティによってコールバック関数がトリガされます。eventname プロパティは、関数をトリガするメッセージ名(mouse、mouseidle 等)を持っています。

if (event.eventname=="mouse") { // ウィンドウの中で、ドラッグ、または「マウスクリック」をしたままのイベント

コードのこの部分では、マウスクリックイベント(マウスボタンの押し下げ、ドラッグ、マウスボタンを離す)を扱っています。

// アーギュメントは(x,y,button,cmd,shift,capslock,option,ctrl)... // ここでは最初の3つだけに注目します x = event.args[0]; y = event.args[1]; button = event.args[2];

マウスイベントの最初の3つのアーギュメントには、マウス位置(x、y)およびマウスボタンが押し下げられているか(1) 押し下げられていないか(0)を示すフラグが収められています。これらのセッティングは、後々使用するため、ローカル変数(xybutton )に格納します。

if (button) // ボタンが押し下げられている場合 { mywidget.color = [0,1,0,1]; // コントロールオブジェクトの色を緑にします mywidget.shape = "torus"; // 形をドーナツ型に変えます } else // ボタンをすでに放している場合 { mywidget.color = [1,0,0,1]; //  オブジェクトの色を赤にします mywidget.shape = "sphere"; // 形をもとに戻します } }

ボタンが押し下げられている場合、jit.gl.gridshape オブジェクト mywidget を白い球から緑のトーラスに変化させます。マウスボタンを放したときには、それを赤い球に変えます。

else if (event.eventname=="mouseidle") { // マウスボタンを離した状態でウィンドウ上を動いている場合 x = event.args[0]; y = event.args[1]; mywidget.color = [1,1,1,1] ; // オブジェクトの色を白にします }

このコードは、クリックをしないままでマウスポインタをjit.windowオブジェクト上で移動させている時には、常に実行されます。ここで行われているのは、変数 xy にマウスポインタ位置を格納することだけですが、同時に、前のコールバックによって jit.gridshape オブジェクト( mywidget )の色が白以外のものに変更されている場合には、これを白に戻しています。

else if (event.eventname=="mouseidleout") { //マウスが、もはやウィンドウ上にはない場合 x = event.args[0]; y = event.args[1]; mywidget.color = [1,1,1,0.5] ; // オブジェクトを半透明にしています }

マウスポインタが jit.winodow を外れた時、コールバックの eventname プロパティは、「mouseidleout」になります。ここでは、このプロパティを「mouseidle」イベントと同様に取り扱います。必要であれば、これを別の方法で取り扱うこともできます(例えば、白い球を消す、あるいは、球をランダマイズする)。

// コントロールオブジェクトを、描画コンテキスト上の、マウスイベントが起こった場所と // 同じ位置に移動します。: mywidget.position = myrender.screentoworld(x,y);

最後に、マウスによってコントロールされる jit.gl.gridshape オブジェクトの位置を、JitterObject mywidgetposition アトリビュートを更新することによって変更します。描画コンテキストは、OpenGL ワールド座標(空間の、デフォルトの中心位置が0.0 で、位置は10進の値で表現されます)で動作するため、x および y の値を、描画コンテキストが正しく解釈できる数値に変換する必要があります。jit.gl.render オブジェクトの screenworld() メソッドは、ディスプレイのピクセル座標によるこれらの値を、レンダリングのためのワールド座標に変換します。

} // この関数を Max からの呼び出せないようにします。 thecallback.local = 1;

この thecallback() 関数は、JitterListener オブジェクトによって実行されることを目的としているため、この local プロパティを 1 にして、js オブジェクトに送られる Max メッセージによって直接実行されるのを防ぎます。

シーンの描画(Drawing the scene)

ここまでで、マウスイベントに応答し、それによって mywdget オブジェクトの位置、色、形を更新する方法について見てきました。そこで、今度は Max パッチャー内の qmetro オブジェクトからの bang メッセージに応答して実際のシーンのレンダリングを行う方法を見て行く必要があります。この JavaScript コードの bang() 関数は描画を行うと同時に、大きな球の新しい位置に応じた小さな球のアニメーションを取り扱います。

bang() 関数のコードを見て下さい。

bang() 関数の大部分は、全ての小さな球の位置を大きな球の位置と比較するための繰り返し処理です。その間の距離が球が重なってしまうくらい小さい場合には、球どうしが接触している状態にするために最低限必要な距離だけ小さい球を移動させます。この種の処理は、衝突検出として知られているもので、コンピュータアニメーションの世界ではいたる所で利用されるテクニックです。

function bang() // メイン描画ループ... どの小さい球を動かすべきかを算出し、レンダラを駆動します { // 衝突検出ブロック。小さな球からコントロールオブジェクトまでの距離のチェックのため、 // 最後まで繰り返し処理を行う必要があります。接触している場合には、正しい接触角に従って // 小さい球を遠ざけます for(var i = 0;i<OBJECT_COUNT;i++) {

ここから、全ての小さい球1つずつに対して大きな球の位置をチェックする JavaScript の for() ループに入ります。

// x および y 軸方向のデカルト座標距離 var distx = mywidget.position[0]-mysphere[i].position[0]; var disty = mywidget.position[1]-mysphere[i].position[1]; // 2つのオブジェクトの極座標距離 var r = Math.sqrt(distx*distx+disty*disty); // コントロールオブジェクトに対する小さい球の角度 var theta = Math.atan2(disty,distx);

2つの球(大きい球と小さい球)の一方の xy の位置から、他方の位置を引くことによって、そのデカルト座標距離を求められます。これを、斜辺の距離を導くピタゴラスの定理を適用することによって極座標距離(絶対値)に変換することができます(この値は変数 r に格納されます)。その後、大きな球に対する小さな球の角度を、垂直方向(y)/水平方向(x) のアークタンジェントによって導きます。

訳注:原文は arctangent of the rise(x) over the run(y) となっていますが、コードから判断して、rise(y)、run(x) と思われます。

// 衝突のチェック... if(r<0.15) // コントロールオブジェクトのサイズは 0.1、小さな球は 0.05 のため、 // 0.15より小さければ衝突... {

大きい球は 0.1, 0.1, 0.1 という scale アトリビュートを持ち、小さい球は 0.05, 0.05. 0.05 という scale アトリビュートを持っているため、2つの距離が 0.15 より小さい場合には重なり合うと推測できます。これによって、衝突を処理するコードブロックをトリガします。

// 置換するx と y を求めるために極座標->デカルト座標に変換します var movex = (0.15-r)*Math.cos(theta); var movey = (0.15-r)*Math.sin(theta); // 小さな球を新しい位置にオフセットします。これは、接触した時の角度で、ちょうど // 接するような位置でなければなりません。その結果、小さな球を「押しのけた」よう // に見えるはずです mysphere[i].position = [mysphere[i].position[0] -movex,mysphere[i].position[1]-movey]; } }

2つのオブジェクトの可能な最小の距離(0.15)と組み合わせて極座標の距離と角度を使うことにより、大きな球と重なり合わないようにするために、小さな球を x 軸、および y 軸方向にどのくらい「押しのける」必要があるかを求めます。この値を求めるために、極座標をデカルト座標の値に再変換し、変数 movexmovey に格納します。そして、これらの変数の値を小さい球の現在の位置から引くことによって、球を押しやります。

// レンダリングブロック... myrender.erase(); // 描画コンテキストの消去 myrender.drawclients(); // クライアントオブジェクトの描画 myrender.swap(); // 新しい描画と取り替える }

このコードの最後のブロックは実際にレンダリングを行なうものです。Jitter の OpenGL オブジェクトで行なう場合と全く同様に、erase メッセージを jit.gl.renderオブジェクト(myrender)に送ります。jit.gl.renderdrawclients() メソッドは、描画コンテキストに接続された OpenGL オブジェクトから、関連した全ての情報を集め、それを描画します。 automatic アトリビュートが 0 に設定されている任意の OpenGL オブジェクトは、ここで手動で描画されなければなりません。swap() メソッドは、jit.window オブジェクトの古いレンダリングを新しいもので置き換え、更新されたシーンを表示させます。Max パッチャー内の jit.gl.render オブジェクトに bang メッセージを送ったとき、drawclients()swap() メソッドは1つの操作として合体されます。

・チュートリアルパッチに戻り、fullscreen $1 と書かれたメッセージボックスに接続された toggle ボックスをクリックしてください(または、コンピュータキーボードの [ESC]キーを押してください)。jit.window オブジェクトがフルスクリーンで表示されます。マウスで大きい球をスクリーン上で移動させるための座標変換がちゃんと動作していることに注目して下さい。キーボードの [ESC] キーを押して、フルスクリーンモードから抜けて下さい。JavaScript コードの中の fullscreen() 関数を見て下さい。

function fullscreen(v) // [jit.window]をフルスクリーンモードにするための関数 { mywindow.fullscreen = v; }

jit.window JitterObject の fullscreen() アトリビュートは、Max パッチャー内で jit.windowオブジェクトに fullscreen メッセージを送った場合と全く同じように動作します。

まとめ

OpenGL Jitter パッチ全体を、JavaScript でカプセル化することができます。これは、js オブジェクトのために書かれた手続き型コードの中で jit.gl.renderjit.window、および他の OpenGL オブジェクトをインスタンス化することによって可能になります。これらのオブジェクトは対応するメソッドやプロパティと同様に、すべてのメッセージとアトリビュートを公開します。JitterListener オブジェクトを使って、JavaScript 内の Jitterオブジェクトによってトリガされたイベントに応答することができます。その後、JitterListener はコールバック関数を実行し、呼出しメッセージをアーギュメントとして渡します。これによって、jit.window オブジェクトの中でのマウスのインタラクティブ機能や、jit.qt.mvie オブジェクトのファイル読み込みに応答する JavaScript 関数を書くことができます。また、Max パッチャーの中で、Jitter オブジェクトのステータスアウトレットから送られるメッセージによってトリガされるイベントに応答したいような何らかの状況がある場合、これに応答するJavaScript 関数を書くこともできます。

コードリスト

// 47jMarbles.js // [js] 内でインスタンス化された Jitter オブジェクトの出力をリスン(待ち受け)するように // JitterListener オブジェクトを設定する方法に関するデモンストレーション。 // // rld, 7.05 // var OBJECT_COUNT = 32; // 小さい球の数 // 表示用の[jit.window] オブジェクトを生成 // (私たちがリスン(待ち受け)するのはこのオブジェクトです。): var mywindow = new JitterObject("jit.window","ListenWindow"); // デプステストをオフにします...代わりにブレンドを使います mywindow.depthbuffer = 0; // アイドルマウス(訳注:クリックがない状態でマウスカーソルを追跡する)をオンにします // これをリスン(待ち受け)します mywindow.idlemouse = 1; // ウィンドウの中に描画するために、 [jit.gl.render] オブジェクトを作ります var myrender = new JitterObject("jit.gl.render","ListenWindow"); // 2次元投影を使います myrender.ortho = 2; // 不透明で完全な消去を行う(軌跡を残さない)ようにして、背景を黒にセットします myrender.erase_color = [0,0,0,1]; // マウスによるコントロールのために使用する[jit.gl.gridshape] オブジェクトを作ります var mywidget = new JitterObject("jit.gl.gridshape","ListenWindow"); mywidget.shape = "sphere"; mywidget.lighting_enable = 1; mywidget.smooth_shading = 1; mywidget.scale = [0.1,0.1,0.1]; mywidget.color = [1,1,1,0.5] ; mywidget.blend_enable = 1; mywidget.position = [0,0]; // 平行投影では z は必要ありません // ランダムに、ウィンドウ全体に渡って配置される [jit.gl.gridshape] オブジェクトの // 配列を作ります var mysphere = new Array(OBJECT_COUNT); // 小さな球をランダムな色と、(x,y)位置によって初期化します for(var i=0;i<OBJECT_COUNT;i++) { mysphere[i] = new JitterObject("jit.gl.gridshape","ListenWindow"); mysphere[i].shape = "sphere"; mysphere[i].lighting_enable = 1; mysphere[i].smooth_shading = 1; mysphere[i].scale = [0.05,0.05,0.05]; mysphere[i].color = [Math.random(),Math.random(),Math.random(),0.5]; mysphere[i].position = [Math.random()*2.-1, Math.random()*2.-1]; mysphere[i].blend_enable = 1; } // [jit.window] オブジェクトのための JitterListener を生成します var mylistener = new JitterListener(mywindow.getregisteredname(), thecallback); function randomize() // 小さな球を再設定します { for(var i=0;i<32;i++) { mysphere[i].color = [Math.random(),Math.random(),Math.random(),0.5]; mysphere[i].position = [Math.random()*2.-1,Math.random()*2.-1]; } } function thecallback(event) // [jit.window] 内でマウスによってトリガされるイベントを扱うコールバック関数 { var x,y,button; // ローカル変数 if (event.eventname=="mouse") { // ウィンドウの中で、ドラッグ、または「マウスクリック」をしたままのイベント // アーギュメントは(x、y、button、cmd、shift、capslock、option、ctrl)... // ここでは最初の3つだけに注目します x = event.args[0]; y = event.args[1]; button = event.args[2]; if (button) {// ボタンが押し下げられている場合 mywidget.color = [0,1,0,1]; // コントロールオブジェクトの色を緑にします mywidget.shape = "torus"; // 形をドーナツ型に変えます } else {// ボタンをすでに放している場合 mywidget.color = [1,0,0,1]; // オブジェクトの色を赤にします mywidget.shape = "sphere"; // 形をもとに戻します } } else if (event.eventname=="mouseidle") { // マウスボタンを離した状態でウィンドウ上を動いている場合 x = event.args[0]; y = event.args[1]; mywidget.color = [1,1,1,1] ; // オブジェクトの色を白にします } else if (event.eventname=="mouseidleout") { // マウスが、もはやウィンドウ上にはない場合 x = event.args[0]; y = event.args[1]; mywidget.color = [1,1,1,0.5] ; // オブジェクトを半透明にしています } // コントロールオブジェクトを、描画コンテキスト上の、マウスイベントが起こった場所と //同じ位置に移動します。: mywidget.position = myrender.screentoworld(x,y); } // この関数を Max から呼び出せないようにします thecallback.local = 1; function bang() // メイン描画ループ... どの小さい球を動かすべきかを算出し、レンダラを駆動します { // 衝突検出ブロック。小さな球からコントロールオブジェクトまでの距離のチェックのため、 // 最後まで繰り返し処理を行う必要があります。接触している場合には、正しい接触角に従って // 小さい球を遠ざけます for(var i = 0;i<OBJECT_COUNT;i++) { // x および y 軸方向のデカルト座標距離 var distx = mywidget.position[0]-mysphere[i].position[0]; var disty = mywidget.position[1]-mysphere[i].position[1]; // 2つのオブジェクトの極座標距離 var r = Math.sqrt(distx*distx+disty*disty);
// コントロールオブジェクトに対する小さい球の角度 var theta = Math.atan2(disty,distx); // 衝突のチェック... if(r<0.15)
// コントロールオブジェクトのサイズは 0.1、小さな球は 0.05 のため、 // 0.15より小さければ衝突... { // 置換するx と y を求めるために極座標->デカルト座標に変換します var movex = (0.15-r)*Math.cos(theta); var movey = (0.15-r)*Math.sin(theta); //小さな球を新しい位置にオフセットします。これは、接触した時の角度で、ちょうど //接するような位置でなければなりません。その結果、小さな球を「押しのけた」よう //に見えるはずです mysphere[i].position = [mysphere[i].position[0]-movex, mysphere[i].position[1]-movey]; } } // レンダリングブロック... myrender.erase(); // 描画コンテキストの消去 myrender.drawclients(); // クライアントオブジェクトの描画 myrender.swap(); // 新しい描画と取り替える } function fullscreen(v) // [jit.window]をフルスクリーンモードにするための関数 { mywindow.fullscreen = v; }