クモのようにコツコツと

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

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

p5.jsの続きです。前回は「Generative Design with p5.js」の「P.4. 画像」の「01」を見ました。今回は画像編「P.2. 形」の「02」「03」を見ていきます*1。それでは行きましょう!

【目次】

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

※p5.jsを習得するためにやったことまとめ
【ビジュアルコーディング】p5.jsを習得するためにやった事 まとめ(随時更新) - Qiita

形 P_2_0_02

完成品

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

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

形 P_2_0_02

最初は何もないが、キャンバス上をクリックすると図形が現れる。ドラッグで連続描写。左右で多角形の角数が変わる。左右で線の太さと数が変わる。

JSコード全体

// P_2_0_02
//
// 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 with a changing shape by draging the mouse.
 *
 * MOUSE
 * position x          : length
 * position y          : thickness and number of lines
 * drag                : draw
 *
 * KEYS
 * del, backspace      : erase
 * s                   : save png
 */
'use strict';

function setup() {
  createCanvas(720, 720);
  noFill();
  background(255);
  strokeWeight(2);
  stroke(0, 25);
}

function draw() {
  if (mouseIsPressed && mouseButton == LEFT) {
    push();
    translate(width / 2, height / 2);

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

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

    pop();
  }
}

function keyReleased() {
  if (keyCode == DELETE || keyCode == BACKSPACE) background(255);
  if (key == 's' || key == 'S') saveCanvas(gd.timestamp(), 'png');
}

冒頭コメントの前半はクレジット。 後半の概要の訳。

マウスをドラッグして、形を変えながら描画します。

マウス  位置x:長さ  位置y:太さと線の数  ドラッグ:描画

キー del、backspace:消去  s:pngを保存

全体構成

まずは大枠部分

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

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

function keyReleased() {
  // キーが離れた時の処理
}

この3つのメソッドは基本的にこれまでと同じ構成。

※参考:【ビジュアルコーディング】p5.jsを習得するためにやった事 まとめ(随時更新) - Qiita

setup()の中身

ページ読み込み時の処理

function setup() {
  createCanvas(720, 720);
  noFill();
  background(255);
  strokeWeight(2);
  stroke(0, 25);
}
  • createCanvas()でキャンバス作成。720px幅、720px高さ
  • noFill()で塗りを無しに
  • background()で背景色設定。RGB共に#255
  • stroke()でグレースケールを0、透明度を25%

noFill()は塗りなしの設定

Disables filling geometry. If both noStroke() and noFill() are called, nothing will be drawn to the screen.

ジオメトリの塗りつぶしを無効にします。 noStroke()とnoFill()の両方が呼び出された場合、画面には何も描画されません。

※参考:reference | p5.js

stroke()は線の設定で引数が2つの場合はグレースケース、透明度の設定になる。

stroke(gray, [alpha])

※参考:reference | p5.js

これらが読み込み時の初期設定

draw()の中身

draw()全体

一定時間ごとに繰り返し実行

function draw() {
  if (mouseIsPressed && mouseButton == LEFT) {
    push();
    translate(width / 2, height / 2);

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

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

    pop();
  }
}

ここが本体なので部分ごとに見ていく

外側のif文

まず処理の全体をif文が囲っている。

  if (mouseIsPressed && mouseButton == LEFT) {
    // 処理
  }
  • もしマウスが押されていて、かつ左クリックだったら、処理を実行する

mouseIsPressedはマウスが押されているかいないかを判定

The boolean system variable mouseIsPressed is true if the mouse is pressed and false if not.

ブールシステム変数mouseIsPressedは、マウスが押されている場合はtrue、押されていない場合はfalseです。

※参考:reference | p5.js

mouseButtonはどのボタンが押されているかをお判定

Processing automatically tracks if the mouse button is pressed and which button is pressed.

処理は、マウスボタンが押されたかどうか、およびどのボタンが押されたかを自動的に追跡します。

右クリックの場合は実行したくないようだ。自分の環境はMacなのでControlを押しながらクリックしたら右クリックメニューが立ち上がった。この動きが想定通りなのかな?

メソッド実行(push()、translate())

処理の冒頭でメソッドを実行している

    push();
    translate(width / 2, height / 2);
  • push()メソッド実行
  • translate()メソッド実行。引数は第1引数は幅の半分、第2引数は高さの半分

push()メソッドはpop()メソッドとセットで使うようだ。

The push() function saves the current drawing style settings and transformations, while pop() restores these settings. Note that these functions are always used together.

push()関数は現在の描画スタイル設定と変換を保存し、pop()はこれらの設定を復元します。 これらの関数は常に一緒に使用されることに注意してください。

※参考:reference | p5.js

translate()メソッドは「形 P_2_0_01」でも出てきた、オブジェクトの移動量の設定をするメソッド。

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

それぞれ、幅、高さ共にキャンバスの半分に設定している。

変数circleResolution

次に変数がいくつか設定されている。だんだん数学的になってきたので細かく見る。

    var circleResolution = int(map(mouseY + 100, 0, height, 2, 10));
  • 変数circleResolutionint()メソッド実行。引数はmap()メソッドで、その引数は左からマウスの縦位置+100px、0、高さ、2、10

メソッドが入れ子になってて直感的ではないw

circleResolutionは「サークル解像度」という意味。

値のint()メソッドは「形 P_2_0_01」でも出てきた。少数を切り捨てて整数にするメソッド。

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

で、その中のmap()も同記事にあるが、値と値を関連づける(マッピング)メソッド。これで何と何を関連付けているのか

map(value, start1, stop1, start2, stop2, [withinBounds])
  • 第1引数は入力値
  • 第2、3引数は値の現在の範囲
  • 第4、第5引数は値のターゲットの範囲

※参考:reference | p5.js

改めてmap()メソッド部分をみると

map(mouseY + 100, 0, height, 2, 10)
  • マウスの縦位置に100px足した数値が入力値
  • 値の現在の範囲は0からキャンバス高さ
  • 値のターゲット範囲の下限は2、上限は10

マウスの縦位置に100pxを足した数値のうち、0〜キャンバス高さまでの範囲が2〜10に変換される。

変数radius
    var radius = mouseX - width / 2;
  • 変数radiusの値はマウスの横位置から幅の半分を引いた数値

radiusは半径という意味。

変数angle
    var angle = TAU / circleResolution;
  • 変数angleTAUcircleResolutionで割った数値

angleは角度。

TAUも「形 P_2_0_01」で出てきた。「2π」と同じ意味。circleResolutionで割るのも前回と共通。

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

beginShape()〜endShape()

そのつぎはbeginShape()からendShape()に囲われたfor文がある

    beginShape();
    // for文
    endShape();
  • beginShape()で形状を作成
  • 中にはfor文
  • endShape()で形状作成を終了

beginShape()endShape()は前回出てきた。形状を作成。

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

for文

for文の中にサインコ・サイン!また三角関数だ!

    for (var i = 0; i <= circleResolution; i++) {
      var x = cos(angle * i) * radius;
      var y = sin(angle * i) * radius;
      vertex(x, y);
    }
  • for文の条件1:変数iの初期値は0、条件2:icircleResolutionまで繰り返す、条件3:iを1ずつ加算
  • 変数xangleiを掛けたコサインとradiusを掛ける
  • 変数yangleiを掛けたサインとradiusを掛ける
  • vertex()メソッド実行。第1引数はx、第2引数はy

変数xyは前回の「形 P_2_0_01」と共通している

三角関数「角度から座標(位置)を割り出す」の公式になる!

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

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

vertex()は「色 P_1_0_03」にも出てきた。頂点を定めるメソッド。

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

この頂点をfor文でcircleResolutionの回数繰り返して描画する。

keyReleased()の中身

キーが離れた時の処理

function keyReleased() {
  if (keyCode == DELETE || keyCode == BACKSPACE) background(255);
  if (key == 's' || key == 'S') saveCanvas(gd.timestamp(), 'png');
}
  • もしDELETEまたはBACKSPACEを押したら背景をRGB共255
  • もしsまたはSキーを押したらキャンバスをpng画像で保存する

DELETEまたはBACKSPACEを押すとこれまで描画された図形がなくなりリセットされる。

画像の保存設定はいつもの内容。

※参考:【ビジュアルコーディング】p5.jsを習得するためにやった事 まとめ(随時更新) - Qiita

形 P_2_0_03

その次の「形 P_2_0_02」も似た内容なので一緒に見る。

完成品

形 P_2_0_02

先程の図形描画で、1〜3のキーを押すと色が変わる。

JSコード全体

// P_2_0_03
//
// 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 with a changing shape by draging the mouse.
 *
 * MOUSE
 * position x          : length
 * position y          : thickness and number of lines
 * drag                : draw
 *
 * KEYS
 * 1-3                 : stroke color
 * del, backspace      : erase
 * s                   : save png
 */
'use strict';

var strokeColor;

function setup() {
  createCanvas(720, 720);
  colorMode(HSB, 360, 100, 100, 100);
  noFill();
  strokeWeight(2);
  strokeColor = color(0, 10);
}

function draw() {
  if (mouseIsPressed && mouseButton == LEFT) {
    push();
    translate(width / 2, height / 2);

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

    stroke(strokeColor);

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

    pop();
  }
}

function keyReleased() {
  if (keyCode == DELETE || keyCode == BACKSPACE) background(0, 0, 100);
  if (key == 's' || key == 'S') saveCanvas(gd.timestamp(), 'png');

  if (key == '1') strokeColor = color(0, 10);
  if (key == '2') strokeColor = color(192, 100, 64, 10);
  if (key == '3') strokeColor = color(52, 100, 71, 10);
}

概要部分の訳

マウスをドラッグして、形を変えながら描画します。

マウス 位置x:長さ 位置y:太さと線の数 ドラッグ:描画

キー 1-3:ストロークの色 del、backspace:消去 s:pngを保存

以下、「形 P_2_0_02」と違う部分を見ていく

変数strokeColor

冒頭で変数strokeColorを定義

var strokeColor;

値はまだない。

setup()の変更点

次、setup()関数

function setup() {
  createCanvas(720, 720);
  colorMode(HSB, 360, 100, 100, 100);
  noFill();
  strokeWeight(2);
  strokeColor = color(0, 10);
}
  • colorMode()でカラーモード設定。HSBでHが360、Sが100、Bが100、不透明度100
  • strokeColor()color()で線の色を設定。第1引数は0、第2引数は10

colorMode()の第4引数は不透明度

colorMode(mode, max1, max2, max3, [maxA])

maxA Number: range for the alpha (Optional)

※参考:reference | p5.js

color()は引数が二つの場合はstroke()と同じでグレースケールと透明度。

color(gray, [alpha])

※参考:reference | p5.js

「形 P_2_0_02」にあったbackground()stroke()は削除。

draw()関数の変更点

stroke()が追加されている

function draw() {
  if (mouseIsPressed && mouseButton == LEFT) {
    // 中略
    stroke(strokeColor);
     // 中略
  }
}
  • stroke()の引数をstrokeColor

strokeColorは何もしないとsetup()関数の初期値になっている。変更設定は次のkeyReleased()にある。

keyReleased()の変更点

function keyReleased() {
  if (keyCode == DELETE || keyCode == BACKSPACE) background(0, 0, 100);
  if (key == 's' || key == 'S') saveCanvas(gd.timestamp(), 'png');

  if (key == '1') strokeColor = color(0, 10);
  if (key == '2') strokeColor = color(192, 100, 64, 10);
  if (key == '3') strokeColor = color(52, 100, 71, 10);
}
  • もしDELETEまたはBACKSPACEを押したら背景のH0、S0、B100
  • もしsまたはSキーを押したらキャンバスをpng画像で保存する
  • もし1を押したらstrokeColorcolor()をグレースケール0、不透明度10%に
  • もし2を押したらstrokeColorcolor()をH192、S100、B64、不透明度10%に
  • もし3を押したらstrokeColorcolor()をH52、S100、B71、不透明度10%に

ここでキーボードの1〜3を押した時にstrokeColorの色を変更する設定が書かれている。それがdraw()関数に反映される。

最後に

f:id:idr_zz:20200519190621j:plain

ということで形編、今回は2つ見ていきました。形編は数が多いですが、今後も似ている事例は同時に見ることができそうです。

メソッドや三角関数も過去に出てきたものが結構あって、まだ見た瞬間には理解できないがとにかく数多く見ていって、コードに慣れていきたいと思います。それではまた!!


※p5.jsを習得するためにやったことまとめ
【ビジュアルコーディング】p5.jsを習得するためにやった事 まとめ(随時更新) - Qiita

*1:似た内容だったので同時に行けた