美大生のためのプログラミング入門:直線を描く


参考:Qiita では印刷がうまくできません。プリントしたい人や PDF 化したい人は http://gurakura.sakura.ne.jp/hellomondrian/line/ を見て下さい(同じ内容です)。

今回と次回のセクションで目指すもの


 上の画像は Piet Mondrian の作品 New York City I (1942 年)で、
https://uploads5.wikiart.org/images/piet-mondrian/new-york-city-i-1942.jpg!Large.jpg
で見ることができます。

今回と次回のセクションにて目指すものは、このモンドリアンの作品のような画像を、プログラムにより描くことです。前の文章までで、キャンバスを準備できるようになったので、ここからは、いよいよ描画についての方法を説明していきます。

直線描画の基本

 Processing では、line 関数にて直線を描画できます。line 関数も複数の使い方がありますが、このシリーズでは 3D CG は扱いませんので、2D の(つまり平面への描画としての)line 関数の使い方を説明します。

プログラムによる直線描画とは、計算機に(=コンピュータに)、直線を描画させるのですが、そのためには、どのような直線であるかを計算機に伝えなければなりません。直線を表現する方法には、様々なものがあります。
ここで使用する方法は、直線の(正確には線分の)始点と終点の位置を指定するものです。

 始点と終点は、それぞれの座標を用いて表現します。具体的には、line(x1,y1,x2,y2) という 4 つの引数を伴う使用方法となります。それぞれの引数は、(x1,y1) と (x2,y2) という組に分けられ、それぞれはキャンバス上の始点 (x1,y1) と終点 (x2,y2) の位置を表しています。

例えば、line(20,10,450,400) というプログラム片は、「位置 (20,10) から位置 (450,400) まで直線を描画せよ」と Processing に指示を出すことに他なりません。実際のプログラムは次のようになります:

// canvas setup ← コメントです
background(250,250,250);
size(500,500);

// draw a line ← コメントです
line(20,10,450,400);

 1 行目と 5 行目に見慣れない記述があります。これはコメントと呼ばれるもので、コンピュータのためではなく人間のためのメモ(注釈文)です。

2 つのスラッシュ// 以降はコメントですので、canvas setup とか draw a line という文字列は、プログラム的には意味がありません(なので気にしない)。もちろん、皆さんがこれから Processing でプログラムを書いていく場合において、メモしておきたいものがあったら、// と書いて、その後にメモを記していくことが可能です(ただし、現在のバージョン(3.5.4) では日本語を入力すると文字化けするようです)。

 同様に 4 行目の空行も人間が読みやすくするための工夫です。空白同様に空行も読み飛ばされるので、4 行目も Processing にとっては(というよりもむしろ Java というプログラム言語にとっては)意味がありません。しかし、この空行により、キャンバスを準備している部分と、準備したキャンバスに線を描く部分を視覚的に分離できます。プログラムを書く場合は、このような機能を活用し、読みやすいコード(プログラムリスト)を記述していきます。

 閑話休題。上のプログラムリストを実行すると、次の図のような結果が得られます。

座標系

 さて、先程のプログラムにおける line 関数に与えた 2 つの位置、(20,10) とか (450,400) というのは、具体的にはどのような場所を
指すのでしょうか。これを理解するためには、座標系を理解する必要があります。「座標系」というと、なんだか難しそうですが、そんなに難しい話でもありません。

座標軸とは

 座標系の説明では、x 軸や y 軸という話がでてくることがあります。今回の説明でも出てきてしまいます。これらは一体何なのかというと、横方向や縦方向についての呼び方だと理解すれば、ここでは十分です。一般的に横方向のことを x 軸と呼びます Processing における x 軸も横方向です。

 ちなみに、座標軸の向きをどのようにしようか自由です。数学的には、x 軸が縦方向でも何ら問題ありません。実際、リモートセンシング分野のとある領域では、x 軸が縦方向を示すという文化もあります。

座標軸の向き

 この x 軸には向きがあって、x 軸が右向きであれば、x の値が増えると、より右側を意味することとなります。Processing でも x 軸の向きは右側です。なので位置 (20,10) は x の座標(= x 軸における位置)が 20 であり、位置 (450,400) の x 座標は 450 なので、(450,400) の方が (20,10) よりも右側の位置となります。

 次に y 軸について説明します。中学・高校における数学の授業では、グラフを描く場合、y 座標は値が増えれば増えるほど、上の方の位置を表していたかと思います。例えば、y 軸を温度として棒グラフを書くと、温度が高ければ高いほど、上方向に伸びる棒グラフが描かれたかと思います。

しかし、Processing においては、皆さんが学校で学んだ数学のときとは異なり、y 軸は下方向を向いています。そのため、y 座標の値が増えれば増えるほど、画面の下の方の位置となります。

座標の原点とスケール

 最後に原点 ー つまり座標 (0,0) ー の話と、座標の大きさの話をします。これまでの話より、Processing の x 軸と y 軸の向きと方向は分かりました。x 軸は右向きであり、y 軸は下向きです。

 しかし、座標の向きだけでは、位置が決まりません。少なくとも座標内の 1 点が、Proessing の画面のどこに位置するのか?また、x,y それぞれの座標が 1 増えた時、どれくらいの位置の変化があるのか?それが分からなければ具体的に位置を示すことができません。

 x 軸や y 軸の方向は既に分かっていますので、例えば、Processing の画面(ウィンドウ)の左上の座標と右下の座標が分かれば、座標と画面上の位置の対応付ができそうです。実は Processing における原点 ー (x,y)=(0,0) の位置のことです ー はキャンバスの左上になります。

では、右下の座標は何になるのでしょうか?これは、キャンバスのサイズからそれぞれ 1 を引いた値となります。例えば、size(400,300) とした場合、右下の位置は (399,299) となります。

 なぜ、size(400,300) とした場合に、右下の座標は幅・高さより 1 ひいた数になるのでしょうか?これは、横に 400 個の画素があり、縦には 300 個の画素がある、と考えると分かりやすいと思います。それぞれの画素の位置を示すのが座標であり、一番左側の画素が 0 番目の画素、その次が 1 番目の画素、... と続けて行くと 400 個目の画素は 399 番目の画素となります(0,1,2, ... 399 で 400 個の数となります)。

よって、左上から右下に対角線を引くプログラムは次のようになります:

// canvas setup
background(250,250,250);
size(400,300);

// draw a line
line(0,0,399,299);

プログラムで絵を描く

 ここまでの説明で、line 関数により最低限の描画ができるようになりました。もちろん、line 関数は何度でも使うことができますし、呼び出す際に引数を変えて呼び出すこともできます。

 では、実際にプログラムで絵を描いてみましょう。モンドリアンによる New York City I (このページの一番上にある画像)には程遠いですが、次のプログラムを実行すると、

// canvas setup
background(250,250,250);
size(500,500);

// draw lines
line(  0, 30,500, 30);
line( 20,  0, 20,500);
line(400,  0,400,500);
line(  0,300,500,300);

次のような表示が得られます:

 どの line 関数がどの線を描いているのかについては、x 座標の値が同じであるならば垂直な線が描かれる、y 座標の値が同じであるならば水平な線が描かれる、ということをヒントにすると良いでしょう。

上のプログラムリストの 6 行目では、line(0,30,500,30) として line 関数を呼び出しています。これは、座標 (0,30) と (500,30) を結ぶ線です。y 座標はどちらの位置も 30 ですので、y 座標が 30 の水平線を引くことに他なりません。注意して見てみると、9 行目にも同様のコード(プログラム中のコード片のこと)があり、合計 2 本の水平線を引いているこおが分かります。

同様に、x 座標が同じであれば、垂直な線を描画します。プログラムリストで言えば 7 行目と 8 行目が始点と終点の x 座標が同じです。7 行目は x 座標が 20 の垂直な線を、8 行目では x 座標が 400 の垂直な直線を描くよう line 関数を呼び出しています。

 今回はここまでです。非常に簡単なものですが、プログラムにより絵がつくれるようになりました。次回は、これらの線をもっと魅力的にする方法について学んでいきます。


コラムその1:line 関数における座標は自由?

 上のプログラムリストでは、キャンバスのサイズは縦横 500 であるのに対し、line 関数で与える座標値が 500 になっているものがありました。この文書をよく読んでいる人は「あれ?」と思ったかもしれません。

このプログラムの場合、キャバスの右下の座標は (499,499) です。となると、例えば 7 行目の line(20,0,20,500) というコードは、この原則を破っているように感じます。

でも、これは全く正常な Processing のプログラムです。というのも、右下の座標は確かに (499,499) なのですが、x 座標や y 座標が 500 を超えてはいけない、という規則とはなりません。それどころか、x や y の座標値がマイナスの値を取ることも許されています。

ある意味、非常に大きな平面があり、その平面の一部を---具体的には左上を (0,0) とし、右下を (499,499) とする四角形にて---ウィンドウ内に表示していると考えてもよいでしょう。なので、座標値が 499 より大きかろうが、マイナスの値となろうが、全く問題ありません。キャンバスには大きな平面に描かれたこれらの直線の一部が切り取られて表示されるだけです。ちなみに、このように一部の領域のみを表示することをクリッピングと呼びます。


コラムその2:プログラムにおける空白は自由?

 自由というテーマでもうひとつ。上のプログラムリストでは、line 関数の引数部分に空白が挿入されていて見やすくなっています。Processing で使われている Java という言語を始め、多くのプログラミング言語では、適当な場所での空白は無視されます。つまり、空白はあっても良いし、無くても良いです。

 もちろん、line 関数の名前を l i n e などと空白を入れて書くことはできません。Processing でも、多くのプログラミング言語と同様に、
関数名の途中に空白を入れることはできません。また、400 という数値を 4 0 0 と書くこともできません。しかし、引数として指定している 400,0 を 400 , 0 などと空白を入れて書くことはできます。

 適宜、空白を入れることにより、より見やすいプログラムを書くことができます。プログラムというと論理が支配する世界のように感じられますが、そうではなく、文学作品同様(というと言い過ぎかもしれませんが)、味わいのあるプログラムリストや簡潔で切れの良いプログラムリストなどが存在します。