クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【p5.js】Generative Design with p5.js「形 P_2_0_01」のコードを読み解く

p5.jsの続きです。前回までは「Generative Design with p5.js」の「P.1. 色」編を見ていましたが、他の機能も知りたくなってきたので「P.2. 形」「P.3. 文字」「P.4. 画像」*1も並行しようと思います*2。今回は「P.2. 形」の「01」を見ていきます*3。それではいきましょう!

【目次】

※参考:【p5.js】Generative Design with p5.js「色 P_1_0_03」のコードを読み解く - クモのようにコツコツと

※p5.jsを習得するためにやったことまとめ
qiita.com

完成品

書籍「Generative Design with p5.js」より

ソースコード一覧
※参考:Generative Design with p5.js[p5.js版ジェネラティブデザイン] ―ウェブでのクリエイティブ・コーディング

「P_2_0_01」のソースコード

※参考:p5.js Web Editor

中心の円がある。円に見えているが実際には線の集合体。マウスの左右の動きで円のサイズが変わり、上下の動きで線の太さと本数が変わる。

JSコード

// P_2_0_01
//
// Generative Gestaltung – Creative Coding im Web
// ISBN: 978-3-87439-902-9, First Edition, Hermann Schmidt, Mainz, 2018
// Benedikt Groß, Hartmut Bohnacker, Julia Laub, Claudius Lazzeroni
// with contributions by Joey Lee and Niels Poldervaart
// Copyright 2018
//
// http://www.generative-gestaltung.de
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * drawing a filled circle with lines.
 *
 * MOUSE
 * position x          : length
 * position y          : thickness and number of lines
 *
 * KEYS
 * s                   : save png
 */
'use strict';

function setup() {
  createCanvas(550, 550);
  strokeCap(SQUARE);
}

function draw() {
  background(255);
  translate(width / 2, height / 2);

  var circleResolution = int(map(mouseY, 0, height, 2, 80));
  var radius = mouseX - width / 2;
  var angle = TAU / circleResolution;

  strokeWeight(mouseY / 20);

  for (var i = 0; i <= circleResolution; i++) {
    var x = cos(angle * i) * radius;
    var y = sin(angle * i) * radius;
    line(0, 0, x, y);
  }
}

function keyPressed() {
  if (key == 's' || key == 'S') saveCanvas(gd.timestamp(), 'png');
}

前半のコメント部分はクレジット。

その次の概要部分の訳。

塗りつぶされた円を線で描画します。

 マウス
位置x:長さ
位置y:太さと線の数    キー
s:pngを保存

全体構成

まずは大枠から見ていく。

function setup() {
  // ページ読み込み時の処理
}

function draw() {
  // 一定時間ごとに繰り返し実行
}

function keyPressed() {
  // キーを押した時に実行
}
  • setup()メソッドはページ読み込み時の処理
  • draw()メソッドは一定時間ごとに繰り返し実行
  • keyPressed()メソッドはキーを押した時に実行

大枠は前回までと変わらない。

※参考:【p5.js】Generative Design with p5.js「色 P_1_0_03」のコードを読み解く - クモのようにコツコツと

setup()の中身

setup()メソッドはページ読み込み時の処理。

function setup() {
  createCanvas(550, 550);
  strokeCap(SQUARE);
}
  • createCanvas()メソッドの第一引数、第二引数共に550
  • strokeCap()メソッドの引数をSQUARE

createCanvas()メソッドで幅550px、高さ550pxのキャンバスを作成する。

strokeCap()メソッドは線の先端の形状を設定するようだ。初期値はROUNDで角丸だがSQUAREだと四角になる。

Sets the style for rendering line endings. These ends are either squared, extended, or rounded, each of which specified with the corresponding parameters: SQUARE, PROJECT, and ROUND. The default cap is ROUND.

行末をレンダリングするためのスタイルを設定します。 これらの端は、2乗、拡張、または丸められており、それぞれが対応するパラメータSQUARE、PROJECT、およびROUNDで指定されています。 デフォルトの上限はROUNDです。

※参考:reference | p5.js

setup()メソッドキャンバスと線の先端の形状を設定している。

draw()の中身

次、draw()メソッドは一定時間ごとに繰り返し実行。

function draw() {
  // 一定時間ごとに繰り返し実行
}

中の処理はボリュームがあるので順番に見ていく。

背景色(background())と移動量(translate())の設定

  background(255);
  translate(width / 2, height / 2);
  • background()メソッドの引数は255
  • translate()メソッドの第一引数はキャンバス幅の半分、第二引数はキャンバス高さの半分(キャンバス中心)

背景色は引数一つだと全て同じ値になり、初期値はRGBなので255は真っ黒。

※参考:reference | p5.js

translate()メソッドは移動量の設定

Specifies an amount to displace objects within the display window. The x parameter specifies left/right translation, the y parameter specifies up/down translation.

表示ウィンドウ内のオブジェクトを移動する量を指定します。 xパラメータは左/右への移動を指定し、yパラメータは上/下への移動を指定します。

引数は下記の意味になる

translate(x, y, [z])

※参考:reference | p5.js

引数は横、縦の順で、そこにキャンバス幅の半分、キャンバス高さの半分が入るのでキャンバスの中心になる。

マウスの縦位置を「2〜80」の範囲に関連付ける

  var circleResolution = int(map(mouseY, 0, height, 2, 80));
  • 変数circleResolutionの値はint()メソッド
    int()メソッドの引数はmap()メソッド
    map()メソッドの引数は左からマウス縦位置、0、キャンバス高さ、2、80

変数名のcircleResolutionは「サークル解像度」と訳されるが、数学でのResolutionは「分解 (ホモロジー代数)」という意味になるようだ。

数学のホモロジー代数において,分解(ぶんかい,英: resolution)(あるいは左分解 (left resolution); 双対の余分解 (coresolution) あるいは右分解 (right resolution)[1])は加群(あるいはより一般に,アーベル圏の対象)の完全列であり,加群あるいはこの圏の対象の構造を特徴づける不変量を定義するために用いられる.

※参考:分解 (ホモロジー代数) - Wikipedia

うーむ、読んでもよくわからない。。

int()メソッドは少数を切り捨てて整数にするようだ。

Converts a boolean, string, or float to its integer representation. When an array of values is passed in, then an int array of the same length is returned.

ブール値、文字列、または浮動小数点数をその整数表現に変換します。 値の配列が渡されると、同じ長さのint配列が返されます。

※参考:reference | p5.js

なお、「ブール値」は真偽値(truefalse)のこと。int()trueを1、falseを0に変換する。

※参考:https://wa3.i-3-i.info/word16183.html

map()は以前こちらの記事でも使ったが、ある範囲とある範囲を関連付けるメソッド。

※参考:【p5.js】クリエイティブコーディングに挑戦(その2) - クモのようにコツコツと

map()の引数は下記の意味になる。

map(value, start1, stop1, start2, stop2, [withinBounds])

value Number: the incoming value to be converted
start1 Number: lower bound of the value's current range
stop1 Number: upper bound of the value's current range
start2 Number: lower bound of the value's target range
stop2 Number: upper bound of the value's target range
withinBounds Boolean: constrain the value to the newly mapped range (Optional)

value数値:変換される入力値
start1数値:値の現在の範囲の下限
stop1数値:値の現在の範囲の上限
start2数値:値のターゲット範囲の下限
stop2数値:値のターゲット範囲の上限
withinBoundsブール値:値を新しくマップされた範囲に制限します(オプション)

※参考:reference | p5.js

そうするとmap(mouseY, 0, height, 2, 80)は下記ということになる。

  • マウスの縦位置が入力値
  • 値の現在の範囲は0からキャンバス高さ
  • 値のターゲット範囲の下限は2、上限は80

マウスの縦位置の0〜キャンバス縦位置までの数字が2〜80までの数値に変換される。

半径の設定

次、変数radiusの設定。

  var radius = mouseX - width / 2;
  • 変数radiusの値はマウス横位置引くキャンバス幅の半分(キャンバス横中心)

変数のradiusは半径という意味。

※参考:【p5.js】Generative Design with p5.js「色 P_1_0_03」のコードを読み解く - クモのようにコツコツと

マウスの横位置からキャンバスの半分を引くということは、キャンバスの横中心からマウスの横位置までの距離が半径になる。

角度の設定

次は変数angle、角度の設定か。

  var angle = TAU / circleResolution;
  • 変数angleの値はTAU割るcircleResolution

また見慣れないプロパティ が…TAUってなんぞ? タウ?

TAU is an alias for TWO_PI, a mathematical constant with the value 6.28318530717958647693. It is twice the ratio of the circumference of a circle to its diameter. It is useful in combination with the trigonometric functions sin() and cos().

TAUは、値6.28318530717958647693の数学定数TWO_PIのエイリアスです。 これは、円の円周と直径の比の2倍です。 三角関数sin()およびcos()と組み合わせると便利です。

※参考:reference | p5.js

これまたすごい桁数の値だなー。TWO_PI? 円の円周と直径の比の2倍?

τ(タウ)は、一部の研究者により、現在の円周率 π に代わるべき数学定数として提唱されている数であり、円の半径に対する周長の比として定義される定数である。その値は 2π に等しい。

※参考:τ (数学定数) - Wikipedia

あー、2πか!円周率の2倍で約6.28ってことか。

でもって前回も出てきた「ラジアン(弧度法)」の「2π」は角度でいうと「360°」になる。

※参考:ラジアン(弧度法)の意味と「度」への変換方法

この2πをcircleResolution(2〜80)の数字で割った数字をangle(角度)にすると。マウスの上下で変わる線の本数に関係していそう。

strokeWeight()メソッドで線の太さを変更

次はstrokeWeight()メソッドを実行。

  strokeWeight(mouseY / 20);
  • strokeWeight()メソッドを実行。引数はマウス縦位置割る20

strokeWeight()メソッドは文字通り線の太さを変えるメソッド。

Sets the width of the stroke used for lines, points, and the border around shapes. All widths are set in units of pixels.

線、点、および図形の周囲の境界線に使用されるストロークの幅を設定します。 すべての幅はピクセル単位で設定されます。

※参考:reference | p5.js

マウスの縦位置を20で割った数字で、左上基準の数値のため、マウスが下に行くほど線が太くなる。

for文でline()メソッド線の描画をループ

最後にfor文がある。

  for (var i = 0; i <= circleResolution; i++) {
    var x = cos(angle * i) * radius;
    var y = sin(angle * i) * radius;
    line(0, 0, x, y);
  }
  • 変数iは0から開始、circleResolutionの回数繰り返す、1ずつ加算
  • 変数xcos()の引数はangleiを掛ける。さらに半径radiusを掛ける。
  • 変数ysin()の引数はangleiを掛ける。さらに半径radiusを掛ける。
  • line()メソッドを実行。引数は左から0、0、xy

for文はcircleResolutionの2〜80の回数繰り返す。

変数xと変数yに出てくるコサイン、サインは三角関数か!

cos()sin()の引数、angleはラジアンの2π=360°をcircleResolutionで割った数。そこに回数iを掛ける(最大はcircleResolutionなので360になる)

そしてコサイン、サイン半径を掛けると前回の三角関数「角度から座標(位置)を割り出す」の公式になる!

  • 半径の長さrとコサインを掛けるとX(横)位置
  • 半径の長さrとサインを掛けるとY(縦)位置

※参考:【p5.js】Generative Design with p5.js「色 P_1_0_03」のコードを読み解く - クモのようにコツコツと

そこから割り出されたxyはその下のline()メソッドの引数に使われる。

line()メソッドは線の描画のメソッドで引数は下記の意味になる。

line(x1, y1, x2, y2)

引数は左から点1の横、縦、点2の横、縦。

※参考:reference | p5.js

そうすると

line(0, 0, x, y);

点1は縦、横ともに0、点2がxyになる。

これがfor文でcircleResolutionの回数分angleの角度で繰り返される。

keyPressed()の中身

keyPressed()メソッドはキーを押した時に実行。

function keyPressed() {
  if (key == 's' || key == 'S') saveCanvas(gd.timestamp(), 'png');
}
  • もしsSが押されたらキャンバスをpngの画像で保存する。

これはいつもと同じ保存設定。

最後に

f:id:idr_zz:20200429154254j:plain

ということで今回は「形」編の最初のコードを読み解きました。今回もサイン、コサイン(三角関数)やTAU(2π)などの数学用語が当たり前のように出てきて、やはりインタラクション系の制作には数学知識はあったほうがいいんだなと感じられました。自分自身、数学のやり直しにもなるので、とても勉強になります。

次回は「文字」編のコードを見ていきたく思います。それではまた!


※p5.jsを習得するためにやったことまとめ
qiita.com

*1:「M.1. 乱数とノイズ」以降は応用編になるので保留

*2:書籍の順番にはQiitaのまとめ記事で並べ替えます

*3:「P.1. 色」編は3つほど見てきたので他が追いつくまでは中断しようかと思ってます