チュートリアル 11:
手続き的な描画

イントロダクション

チュートリアルを開いて下さい。

このチュートリアルでは、lcd オブジェクトに手続き的な描画を行なうために、いくつか新しいオブジェクトを使用します。counter オブジェクトは1つずつカウントする処理を行ないます。line オブジェクトはスムーズな出力を作成します。そして、scale オブジェクトでは、出力の範囲を変更することができます。最後に、簡単な三角法を使って一般的な図形を作る場合に、これを補助する演算オブジェクトをいくつか紹介します。

私たちは、多くの場合、計画的、手続き的な考え方を使って、プログラムの中での処理の流れを構築します。ここでは、グラフィック表示の新しい可能性を示すために、このような方法を使用しますが、同時に、あらゆる処理の中で、数値の数え上げが必要な場合、数値の変化の軌跡の作成が必要な場合、値のスケールが必要な場合などにこれらを利用することができます。オーディオのビジュアライゼーション(視覚化)、コントローラによるフィードバック、あるいは興味深いアート作品など、どのような場面で使用されるときでも、手続き的なプログラムは役に立つツールとインスピレーションの両方を提供することができます。

counter を使った描画

チュートリアルパッチを見て下さい。このパッチには新しいオブジェクトの理解を助けるための部分がいくつかあり、それとは別に描画を行なう大きなパッチがあります。左上の小さなパッチは、counter オブジェクト動作を示すものです。左側の button をクリックすると、このオブジェクトは数値を出力します。この値はbang メッセージを受け取るごとに1ずつ増加していきます。この counter のアーギュメントは出力の最小値と最大値を指定するものです。オブジェクトは最大値に達すると、最小値の値に戻って再び出力を繰り返します。このようにして、counter に値のループを出力されることができます。

counter オブジェクトは、通常とはやや違った方法で、可変アーギュメントを受け取ります。
・アーギュメントが1個の場合、この値はcounter の最大値を設定します。
・アーギュメントが2個の場合、これらの値はそれぞれcounter の最小値、最大値を設定します。
・アーギュメントが3個の場合、これらの値はそれぞれ counter の方向、最小値、最大値を設定します。

このcounter をテストするパッチには、もう1つの button (青色)があり、さらに、オブジェクト値を設定するための2つのナンバーボックスがあります。この機会に、インレットに対してアシスタンスを使い、そのインレットの動作を調べてみましょう。パッチをアンロックし、第3インレット(button とナンバーボックスの両方が接続されているインレット)にマウスを置くと、アシスタンスは、「次のクロック時に、受信した値にcounter をリセット("Resets Counter to Number on Next Clock")」と表示します。カウンタの動作中に、カレント(その時点)のこの値を変更してみましょう。

ナンバーボックスの値を5に変更し、最初の button をクリックすると、その前の値が何であるかにかかわらず、counter の出力が5になることがわかります。動作中のcounter のステート(状態)の設定はこのように行ないます。2つめの(青い)button をクリックすると、最小値がリセットされます。この場合、0 になります。

右側のナンバーボックスでは、実行中に最大値を変更することができます。これは、描画領域のサイズを頻繁に変更する必要があったり、1つの効果を連続して適用していて、そのステップ数を変更する必要があるような描画処理の場合に役に立ちます。この値を変更してbutton を繰り返しクリックすると、新しい最大値が適用されているのを見ることができます。

描画パッチには1 と表示されたセクションがあります。ここでは、counter オブジェクトを使って、lcd オブジェクトに手続き的な描画を行なっています。metro を(接続された toggle オブジェクトによって)スタートさせると、パッチが lcd に小さな円の描画を始めることがわかります。この円の色は徐々に変化していきます。円の描画がlcd の最も下まで達すると、ふたたび上に戻って描画を再開します。

このパッチでは、それぞれ異なったcounter オブジェクトが4個使用されています。1個は描画を行なう位置とサイズをセットし、他の3個は色を変更します。左端 counter の動作を理解することは最も重要です。この counter は0 から767 までカウントし、合計 768 ステップの値を出力します。描画を行なうX/Y の位置は %(モジュロ)オブジェクトと /(除算)オブジェクトによって計算され、lcd オブジェクトの領域を 24 x 24 のグリッドに分割しています。counterオブジェクト、% オブジェクト、/ オブジェクトのそれぞれの出力を見ると、このシステムが0から23までの2つのループによって動作していることがわかります。1つのループ(%オブジェクトの出力)は速く進み、もう1つのループ(/ オブジェクトの出力)は最初のループが繰り返されるごとにインクリメント(1 ずつ増加)しています。

訳注:この説明で 「24 x 24 のグリッドに分割」という記述がありますが、実際のサンプルパッチでは、32 x 24 に分割しています。また、2つのループのうち最初のもの(%の出力)は 0 から 31 までの値を取り、もう1つのものは説明どおり 0 から 23 の値を取ります。

この値は、(左、上のピクセル位置を得るために)10 を掛けられ、その後(下、右のピクセル位置を得るために)10 を加えられます。これによって、この描画プログラムで用いる、lcd を満たす円のグリッドを構成しています。

これ以外の3つの counter オブジェクトは3つの異なった数値範囲を循環し、絶えず変化するカラーパレットを提供してくれます。これらの counter オブジェクトは3つの初期化アーギュメントを持っています。最初の 2 はcounter に対して、最大値まで順に増加し、その後、最小値まで順に減少するような動作を命じるものです。これによって、描画が行なわれるたびにその色がゆっくりと変化します。"reset" と書かれた button をクリックすると、ループの最初からシミュレーションを再スタートさせます。

scale を使った描画

パッチの左上に戻ります。2つめの小さなパッチ例はscale オブジェクトの使い方を示すものです。scale オブジェクトでは、入力する値の範囲と、出力する値の範囲を設定することができます。このオブジェクトは入力された値を正しくスケールし、出力する値の範囲にマップ(写像)します。この例では、入力される値0 から 100  が、-1.0 から 1.0 の範囲のマップされます。値の範囲はオブジェクトのアーギュメントによって決定されます。入力を行なうナンバーボックスの値を変更すると、値 0 では -1.0 が出力され、値 50 では 0.0 が、値 100 では 1.0 が出力されることがわかるでしょう。これらの数値は、指定した範囲全体にわたって、線形にマップされます。

入力範囲を超える値を選んだ場合、出力範囲を計算する計算式が使い続けられていることに気がつくと思います。これにより、設定した入力範囲を超える値を入力した場合でも、エラーが生じることはありません。そのため、値 150 は 2.0 に変換して出力され、値 -100 では -3 が出力されます。オブジェクトのアーギュメントは、変換に用いる数式を決定するために用いられています。

 描画パッチの 2 と書かれたセクションでは、scale を使って、sin波の計算に基づいた図形を描画しています。counter オブジェクトは 0 から最大値(metro オブジェクトの右側にある2個のナンバーボックスを使って設定されます)の範囲の値を出力します。この値は 0.0 から 6.183185(2*pi)スケールされ、入力値のサイン(sin)の値を計算するsin オブジェクトに送信されます。これで、-1.0 から 1.0 の範囲で変化するサイン波が作られます。この値が別のscale オブジェクトに送られ、-1.0 から 1.0 という範囲から、整数 0 から 319 の範囲にマップされます。この範囲は lcd オブジェクトのサイズと等しい値に設定されています。計算された値は出力され、直線の X位置として使用されます。これと同様な一組の関数群が Y位置を設定するために用いられます。これらの数値は(pack を使って)リストに挿入され、(prepend を使って)先頭に lineto メッセージを付加されてから lcd に送信されます。

clear というメッセージを書かれたメッセージボックスを使って、lcd をクリアして下さい。toggle を使って metro をオンにし、結果に注目して下さい。この結果、lcd 全体に、サインの値に基づいた描画が行なわれます。counter オブジェクトに接続されたナンバーボックスの値を変更することによって、この処理の最大値を設定することができます。ナンバーボックスを変更した場合、counter オブジェクトのパラメータだけでなく、すぐ下にあるscale オブジェクトが予測する入力範囲も変更している点に注意して下さい。

2つの counter オブジェクトは、異なる最大値を設定された場合、異なる動きを持つサイン派を生成し、これをlcd 全体に渡って描画します。この曲線はリサージュパターンと呼ばれ、位相が異なるオシレータをオシロスコープのX/Y 入力に送った場合の現象をシミュレートしています。これは、簡単な数学関数を使って複雑な描画を行なっている良い例です。

line を使った描画

パッチの左上に戻ります。3番目の例では、line オブジェクトの動作を見ることができます。line オブジェクトは2つの入力値のペア(値/時間のペアで構成されます)を取り、ある値から次の値へゆっくりと値を動かします。1つの値だけを受信した場合には、その値に直ちにジャンプします。しかし、2番目の値が提供されると、これを新しい値へ移動する時間をミリ秒単位で表すものとして扱います。

最初のメッセージボックス(100 1000 と書かれたもの)をクリックすると、line の値はスタートの値 0 から目標の値(100)まで 1000 秒で移動します。同じメッセージボックスを再度クリックしても何も変化は起きません。これは、カレントの値(この時点では 100 のままです)が目標の値と同じためです(これは、行き先を持たないのと同じことです)。line オブジェクトは「ステートアウェア」です。これは、オブジェクトがカレントの値を理解し、その値のステート(状態)に基づいてアクション(動作)を調節することを意味します。

2番目のメッセージボックス(0 5000)をクリックすると、line の出力結果は、5000 ミリ秒(5秒)かけて 0 に戻ります。最初のボックスをクリックすると再び 100 に戻ります。

3番目のメッセージボックスは、ちょっと面白いものです。これは、メッセージボックスの特別な機能を使って複数のメッセージを作成します。値がカンマ区切られている場合、それぞれが別々のメッセージとして扱われます。そして、2つのメッセージが、同じパッチコードで、非常に短い時間の間で相前後して送信されたかのように機能します。このケースでは、値20 が最初のメッセージとして送信されます。時間を指定する値がないため、line から出力されるのカレントの値は直ちに20 にジャンプします。その直後に 50 2500 という値のペアが送信され、出力は 20 から 50 まで 2.5秒かけて変化します。ナンバーボックスを使って、line を直ちに新しい値にジャンプさせることも可能です。時間を指定する値が与えられない他m、lineはカレントのステートを入力された値にジャンプさせます。

描画パッチの 3 と表示されたセクションは、これまで見てきた中で最も複雑な手続き(プロシージャ)になっています。このパッチは、手続き的な描画を行なうために lcd オブジェクトの idel メカニズムを使っています。idle メッセージが(idle 1 メッセージによって)オンになると、マウスを移動させたときに、lcd オブジェクトはカレント(現時点)のマウス位置を出力します。このマウス位置はlcd オブジェクトのカンバスを基準にした相対座標で与えられ、そのX/Y 座標がリストとして lcd の第2アウトレットから出力されます。

lcd から出ているライトブルーのパッチコードは、最も上にあるunpack オブジェクトへマウス座標を送信します。この値が描画プログラムに送られます。描画プログラムは値のセットを作り、これを使ってマウスカーソルを追尾する2つの丸い図形を描画します。line オブジェクトは図形の変化がゆっくり行なわれるようにするために使用され、これによって図形はカレントのカーソル位置にゆっくり照準を合わせます。toggle ボックスを使ってidle メッセージをオンにし、マウスを lcd オブジェクトの中で移動させて、どのような反応があるかに注目して下さい。

この手続きには、2つの非常に重要なポイントがあります。第1に、line オブジェクトの使用によって、出力チを1秒(1000ミリ秒)の時間フレームで回転させていることです。これにより時間をかけた補間が行なわれるため、結果として生じる動作により自然な感じを与えています。第2に、cartopol(デカルト座標を極座標に変換します)オブジェクトを使って、カーソルのX/Y(デカルト座標)位置を、距離/角度(極座標)という座標値のペアに変換していることです。これは、円領域で描画を行なう場合に役に立ちます。ひとたび極座標を計算しておけば、これをスケールして円を描く領域のサイズに合わせ、その後で、複雑なマルチ・メッセージを持ったメッセージボックスの一部として使用することができます。この複雑なメッセージボックスは、lcd オブジェクトの中央に表示される2つの「目」を生成するものです。

プロブラムの下部にあるメッセージボックスは4つの描画コマンドを順番に lcd に送信します。最初の2つのコマンドは、背景になる2つの円を描画するためのもので、固定された位置(100 100 140 140 と 180 100 220 240)を引数として paintoval コマンドを使っています。paintoval への最後の3つのアーギュメントは色を黒(0 0 0)に設定するためのものです。次の2つのメッセージは、pack オブジェクトからの値を使って、目の中にある緑色の「瞳」を描画するものです。4つの値は、瞳の描画をスタートさせる角度と終了する角度を指定するもので、paintarc メッセージによって使用されます。

このプロシージャの機能のすべてを理解することは難しいかもしれませんが、それだけの価値はあります。このプログラムは、数学的なプログラミングを使用し、複雑で、生物のような感じを与える結果を作り出している1つの例です。シンプルなリアルタイムグラフィックスはこのような手続き的プログラムのブロックの積み重ねによって作られています。入力された値を使用し、これをスケールして、あなたのニーズに合わせることは、多くのMax アプリケーションで非常に重要な意味を持ちます。

結び

ここでは、lcdオブジェクトの中で、コントロールされた描画機能を提供する、かなり複雑なプログラミングについて見てきました。これは、ループの作成(counter)、入力値の出力値へのスケーリング(scale)、時間内での数値補間(line)などによるものでした。また、三角法を計算するオブジェクトもいくつか見てきました。これらの新しいオブジェクトを組み合わせて使用することにより、プログラムで実現したいものに対して、数多くの手続き的なコントロールを確立することができます。

参照

counter 左インレットへの bang をカウントします。
scale 値の入力範囲を、出力範囲へマップします。
line ランプや線形セグメントを生成します。
sin sin(x)
cartopol デカルト座標を極座標に変換します。