クモのようにコツコツと

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

【p5.js】Generative Design with p5.js「画像 P_4_0_01」のコードを読み解く

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

【目次】

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

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

完成品

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

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

「画像 P_4_0_01」のコード

最初は1枚の画像が表示されている。キャンバス上にカーソルを置くと画像サイズが変わり、分割して並ぶ表示になる。カーソルを右に動かすと画像の幅が縮まり、下に動かすと画像の高さが縮まる。

JSコード全体

// P_4_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.

/**
 * draw a grid of streched copies of an image
 *
 * MOUSE
 * position x           : tile count horizontally
 * position y           : tile count vertically
 *
 * KEYS
 * s                    : save png
 */
'use strict';

var img;

function preload() {
  img = loadImage('data/image.jpg');
}

function setup() {
  createCanvas(650, 450);
}

function draw() {
  var tileCountX = mouseX / 3 + 1;
  var tileCountY = mouseY / 3 + 1;
  var stepX = width / tileCountX;
  var stepY = height / tileCountY;
  for (var gridY = 0; gridY < height; gridY += stepY) {
    for (var gridX = 0; gridX < width; gridX += stepX) {
      image(img, gridX, gridY, stepX, stepY);
    }
  }
}

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

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

その次の概要部分の訳。

画像の引き伸ばされたコピーのグリッドを描画

マウス
位置x:水平方向のタイル数
位置y:垂直方向のタイル数

キー
s:保存png

全体構成

まずは大枠部分

var img;

function preload() {
  // ページ読み込み前の処理()
}

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

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

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

冒頭にグローバル変数でimgが宣言されている。値はまだない。

var img;

setup()メソッドはページ読み込みの前に行われる処理で、ここの処理が終わるまでsetup()の中に非同期な処理が書かれていても実行されない。

Called directly before setup(), the preload() function is used to handle asynchronous loading of external files in a blocking way. If a preload function is defined, setup() will wait until any load calls within have finished.

setup()の直前に呼び出されるpreload()関数は、外部ファイルの非同期ロードをブロックする方法で処理するために使用されます。 プリロード関数が定義されている場合、setup()は内部のロード呼び出しが完了するまで待機します。

※参考:reference | p5.js

それ以外のメソッドはこれまでも出てきたもの。

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

preload()の中身

ページ読み込み前の処理

function preload() {
  img = loadImage('data/image.jpg');
}
  • 変数imgにてloadImage()メソッドで画像image.jpgを読み込む

loadImage()メソッドは画像を読み込んで「p5.Image」にする。

Loads an image from a path and creates a p5.Image from it.

パスから画像を読み込み、そこからp5.Imageを作成します。

※参考:reference | p5.js

では「p5.Image」とは何か?

Creates a new p5.Image. A p5.Image is a canvas backed representation of an image.

新しいp5.Imageを作成します。 p5.Imageは、キャンバスに裏打ちされた画像の表現です。

「裏打ち」ってなんだ(笑)canvas要素の画像に変換すると言った意味のようだ。

※参考:reference | p5.js

なるほど、事前にこの作業を行ってからsetup()の処理を実行するわけだ。

setup()の中身

ページ読み込み時の処理

function setup() {
  createCanvas(650, 450);
}
  • 幅650px、高さ450pxのキャンバスを作る

setup()メソッドはとても簡素な内容。

draw()の中身

draw()の中身(全体)

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

function draw() {
  var tileCountX = mouseX / 3 + 1;
  var tileCountY = mouseY / 3 + 1;
  var stepX = width / tileCountX;
  var stepY = height / tileCountY;
  for (var gridY = 0; gridY < height; gridY += stepY) {
    for (var gridX = 0; gridX < width; gridX += stepX) {
      image(img, gridX, gridY, stepX, stepY);
    }
  }
}

ここが今回のメインになるので部分ごとにみていく。

変数設定

まず変数をいくつか設定している。

  var tileCountX = mouseX / 3 + 1;
  var tileCountY = mouseY / 3 + 1;
  var stepX = width / tileCountX;
  var stepY = height / tileCountY;
  • 変数tileCountXはマウスの横位置を3で割って1を足す
  • 変数tileCountYはマウスの縦位置を3で割って1を足す
  • 変数stepXは全体幅をtileCountXで割る
  • 変数stepYは全体高さをtileCountYで割る

マウスを上下左右に動かすと画像サイズが変わる動きの元になる値だな。tileCountXtileCountY1を足すのは最小値を0にしないためだな。そしてマウスの横位置が全体の1/3くらいの場所にあったらそれがさらに3に割られて1/9のサイズになる。

多重ループ(外側の条件)

次からは多重ループになっている。外側から見ていく。

  for (var gridY = 0; gridY < height; gridY += stepY) {
    // 処理
  }
  • 条件1:変数gridYの初期値は0
  • 条件2:gridYを全体高さの値に達するまで繰り返す
  • 条件3:gridYstepYを加算する

最初に画像の高さに関わるループの条件。1回ごとに冒頭の変数で設定されたstepYを全体高さに達するまで足していく。

多重ループ(内側の条件)

次、内側のループを見る

  for (var gridY = 0; gridY < height; gridY += stepY) {
    for (var gridX = 0; gridX < width; gridX += stepX) {
      // 処理
    }
  }
  • 条件1:変数gridXの初期値は0
  • 条件2:gridXを全体幅の値に達するまで繰り返す
  • 条件3:gridXstepXを加算する

画像の幅に関わるループの条件。1回ごとに冒頭の変数で設定されたstepXを全体幅に達するまで足していく。

多重ループ(処理部分)

最後にループの中身の処理部分を見る。

  for (var gridY = 0; gridY < height; gridY += stepY) {
    for (var gridX = 0; gridX < width; gridX += stepX) {
      image(img, gridX, gridY, stepX, stepY);
    }
  }
  • image()メソッドを実行。引数は左からimggridXgridYstepXstepY

image()メソッドは画像編で初めて出てきた。画像をp5.jsキャンバスに描画するメソッド。

image(img, x, y, [width], [height])

引数の意味

  • imgは表示する画像
  • xは左上からの横位置
  • yは左上からの縦位置
  • widthは画像の幅(オプション)
  • heightは画像の高さ(オプション)

※参考:reference | p5.js

imgの画像がループで追加される。その位置とサイズがここで決まる。gridXgridYは画像の位置になる。stepXstepYは画像のサイズになる。

一連の処理がマウスの動き合わせてリアルタイムに行われる。それによって格子状の画像配置になるわけだ!

keyReleased()の中身

キーが離れた時の処理

function keyReleased() {
  if (key == 's' || key == 'S') saveCanvas(gd.timestamp(), 'png');
}
  • もし「s」または「S」のキーボードだったらpngの画像を保存する

keyReleased()メソッドもいつもの画像保存の内容だ。

最後に

f:id:idr_zz:20200514065638j:plain

ということで、Generative Designの最後の1章「画像」編にも入りました。章によって使われるメソッドは異なるんですが、最初に進めた「色編」の「3」より他の章の「1」の方が内容がシンプルに感じました。各章、内容が進むに従ってだんだん高度な内容になっていくと思われます。

ということで引き続き「色」「動き」「文字」「画像」の4章を並行して読み解いて行きたく思います。(色は3番目まで進んでいるので、しばらくは他の3章がそこに追いつくまで進めます)

それではまた!


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