p5.jsの続きです。前回は「Generative Design with p5.js」の「色 P_2_1_1_01、02」を見ました。今回は「文字 P_3_1_2_01」を見ていきます。それでは行きましょう!
【目次】
- 文字 P_3_1_2_01
- JSコード全体
- 全体構成
- グローバル変数
- preload():ページ読み込み前の処理
- setup():ページ読み込み時の処理
- windowResized():ウィンドウサイズが変更された時の処理
- draw():一定時間ごとに繰り返し実行
- mousePressed():マウスボタンを押した時の処理
- keyReleased():文字キーを離した時の処理
- keyPressed():文字キーを押した時の処理
- keyTyped():文字キーを押した時の処理
- 最後に
※参考:前回記事
【p5.js】Generative Design with p5.js「色 P_2_1_1_01、02」のコードを読み解く - クモのようにコツコツと
※参考:p5.jsを習得するためにやったことまとめ
qiita.com
文字 P_3_1_2_01
書籍「Generative Design with p5.js」より
Generative Design with p5.js - [p5.js版ジェネラティブデザイン] ―ウェブでのクリエイティブ・コーディング
- 作者:Benedikt Gross,Hartmut Bohnacker,Julia Laub
- 発売日: 2018/06/22
- メディア: 単行本
ソースコード一覧
※参考:Generative Design with p5.js[p5.js版ジェネラティブデザイン] ―ウェブでのクリエイティブ・コーディング
P_3_1_2_01
文字を打つと路線図の続きが伸びる。
,
, .
, !
, ?
, スペースなどを打つと方向が変えられる。
※参考:p5.js Web Editor
JSコード全体
// // 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. /** * typewriter. uses input (text) as blueprint for a visual composition. * * MOUSE * click + drag : move canvas * * KEYS * a-z : text input (keyboard) * ,.!? and return : curves * space : small curve with random direction * del, backspace : remove last letter * arrow up : zoom canvas + * arrow down : zoom canvas - * alt : new random layout * ctrl : save png */ 'use strict'; var textTyped = ''; var font; var shapeSpace; var shapeSpace2; var shapePeriod; var shapeComma; var shapeQuestionmark; var shapeExclamationmark; var shapeReturn; var centerX; var centerY; var offsetX; var offsetY; var zoom; var actRandomSeed; function preload() { font = loadFont('data/miso-bold.ttf'); shapeSpace = loadImage('data/space.svg'); shapeSpace2 = loadImage('data/space2.svg'); shapePeriod = loadImage('data/period.svg'); shapeComma = loadImage('data/comma.svg'); shapeExclamationmark = loadImage('data/exclamationmark.svg'); shapeQuestionmark = loadImage('data/questionmark.svg'); shapeReturn = loadImage('data/return.svg'); } function setup() { createCanvas(windowWidth, windowHeight); textTyped += 'Ich bin der Musikant mit Taschenrechner in der Hand!\n\n'; textTyped += 'Ich addiere\n'; textTyped += 'Und subtrahiere, \n\n'; textTyped += 'Kontrolliere\nUnd komponiere\nUnd wenn ich diese Taste drück,\nSpielt er ein kleines Musikstück?\n\n'; textTyped += 'Ich bin der Musikant mit Taschenrechner in der Hand!\n\n'; textTyped += 'Ich addiere\n'; textTyped += 'Und subtrahiere, \n\n'; textTyped += 'Kontrolliere\nUnd komponiere\nUnd wenn ich diese Taste drück,\nSpielt er ein kleines Musikstück?\n\n'; centerX = width / 2; centerY = height / 2; offsetX = 0; offsetY = 0; zoom = 0.75; actRandomSeed = 6; cursor(HAND); textFont(font, 25); textAlign(LEFT, BASELINE); noStroke(); fill(0); } function windowResized() { resizeCanvas(windowWidth, windowHeight); } function draw() { background(255); if (mouseIsPressed && mouseButton == LEFT) { centerX = mouseX - offsetX; centerY = mouseY - offsetY; } // allways produce the same sequence of random numbers randomSeed(actRandomSeed); translate(centerX, centerY); scale(zoom); for (var i = 0; i < textTyped.length; i++) { var letter = textTyped.charAt(i); var letterWidth = textWidth(letter); // ------ letter rule table ------ switch (letter) { case ' ': // space // 50% left, 50% right var dir = floor(random(0, 2)); if (dir == 0) { image(shapeSpace, 1, -15); translate(4, 1); rotate(QUARTER_PI); } if (dir == 1) { image(shapeSpace2, 1, -15); translate(14, -5); rotate(-QUARTER_PI); } break; case ',': image(shapeComma, 1, -15); translate(35, 15); rotate(QUARTER_PI); break; case '.': image(shapePeriod, 1, -55); translate(56, -56); rotate(-HALF_PI); break; case '!': image(shapeExclamationmark, 1, -27); translate(42.5, -17.5); rotate(-QUARTER_PI); break; case '?': image(shapeQuestionmark, 1, -27); translate(42.5, -17.5); rotate(-QUARTER_PI); break; case '\n': // return image(shapeReturn, 1, -15); translate(1, 10); rotate(PI); break; default: // all others text(letter, 0, 0); translate(letterWidth, 0); } } // blink cursor after text if (frameCount / 6 % 2 == 0) rect(0, 0, 15, 2); } function mousePressed() { offsetX = mouseX - centerX; offsetY = mouseY - centerY; } function keyReleased() { // export png if (keyCode == CONTROL) saveCanvas(gd.timestamp(), 'png'); if (keyCode == ALT) actRandomSeed++; } function keyPressed() { switch (keyCode) { case DELETE: case BACKSPACE: textTyped = textTyped.substring(0, max(0, textTyped.length - 1)); print(textTyped); break; case ENTER: case RETURN: // enable linebreaks textTyped += '\n'; break; case UP_ARROW: zoom += 0.05; break; case DOWN_ARROW: zoom -= 0.05; break; } } function keyTyped() { if (keyCode >= 32) { textTyped += key; print(textTyped); } }
冒頭コメントの前半はクレジット。 後半の概要の訳。
タイプライター。 入力(テキスト)を視覚的構成の青写真として使用します。
マウス
クリック+ドラッグ:キャンバスを移動キー
a-z:テキスト入力(キーボード)
、。!? と戻り:曲線
スペース:ランダムな方向の小さな曲線
del、backspace:最後の文字を削除
上矢印:キャンバスのズーム+
下矢印:キャンバスのズーム-
alt:新しいランダムレイアウト
ctrl:pngを保存
全体構成
// グローバル変数 function preload() { // ページ読み込み前の処理 } function setup() { // ページ読み込み時の処理 } function windowResized() { // ウィンドウサイズが変更された時の処理 } function draw() { // 一定時間ごとに繰り返し実行 } function mousePressed() { // マウスボタンを押した時の処理 } function keyReleased() { // 文字キーを離した時の処理 } function keyPressed() { // 文字キーを押した時の処理 } function keyTyped() { // 文字キーを押した時の処理 }
windowResized()
メソッド は新しく出てきたメソッド 。ウィンドウサイズが変更された時の処理。
The windowResized() function is called once every time the browser window is resized.
windowResized()関数は、ブラウザウィンドウのサイズが変更されるたびに1回呼び出されます。
グローバル変数
今回は冒頭のグローバル変数の設定が多い。改行が入って3グループに分かれている。
var textTyped = ''; var font; var shapeSpace; var shapeSpace2; var shapePeriod; var shapeComma; var shapeQuestionmark; var shapeExclamationmark; var shapeReturn; var centerX; var centerY; var offsetX; var offsetY; var zoom; var actRandomSeed;
textTyped
は空の文字列だが、あとの変数は宣言のみ(値なし)。
内容によって4つの塊に分かれているようだ。
preload():ページ読み込み前の処理
preload()
はページ読み込み前の処理
function preload() { font = loadFont('data/miso-bold.ttf'); shapeSpace = loadImage('data/space.svg'); shapeSpace2 = loadImage('data/space2.svg'); shapePeriod = loadImage('data/period.svg'); shapeComma = loadImage('data/comma.svg'); shapeExclamationmark = loadImage('data/exclamationmark.svg'); shapeQuestionmark = loadImage('data/questionmark.svg'); shapeReturn = loadImage('data/return.svg'); }
冒頭の変数の値がここで設定されている。一つ目のfont
はloadFont()
でそれ以外はloadImage()
。
loadFont()
はフォントを読み込むメソッド。
loadImage()
は画像を読み込むメソッド。
※参考:【p5.js】Generative Design with p5.js「画像 P_4_0_01」のコードを読み解く - クモのようにコツコツと
setup():ページ読み込み時の処理
setup()
はページ読み込み時の処理
function setup() { createCanvas(windowWidth, windowHeight); textTyped += 'Ich bin der Musikant mit Taschenrechner in der Hand!\n\n'; textTyped += 'Ich addiere\n'; textTyped += 'Und subtrahiere, \n\n'; textTyped += 'Kontrolliere\nUnd komponiere\nUnd wenn ich diese Taste drück,\nSpielt er ein kleines Musikstück?\n\n'; textTyped += 'Ich bin der Musikant mit Taschenrechner in der Hand!\n\n'; textTyped += 'Ich addiere\n'; textTyped += 'Und subtrahiere, \n\n'; textTyped += 'Kontrolliere\nUnd komponiere\nUnd wenn ich diese Taste drück,\nSpielt er ein kleines Musikstück?\n\n'; centerX = width / 2; centerY = height / 2; offsetX = 0; offsetY = 0; zoom = 0.75; actRandomSeed = 6; cursor(HAND); textFont(font, 25); textAlign(LEFT, BASELINE); noStroke(); fill(0); }
内容が多いのでブロックごとに見ていく
createCanvas(windowWidth, windowHeight);
まずキャンバスを作成。今回は固定値ではなくウィンドウサイズのようだ。
textTyped += 'Ich bin der Musikant mit Taschenrechner in der Hand!\n\n'; textTyped += 'Ich addiere\n'; textTyped += 'Und subtrahiere, \n\n'; textTyped += 'Kontrolliere\nUnd komponiere\nUnd wenn ich diese Taste drück,\nSpielt er ein kleines Musikstück?\n\n'; textTyped += 'Ich bin der Musikant mit Taschenrechner in der Hand!\n\n'; textTyped += 'Ich addiere\n'; textTyped += 'Und subtrahiere, \n\n'; textTyped += 'Kontrolliere\nUnd komponiere\nUnd wenn ich diese Taste drück,\nSpielt er ein kleines Musikstück?\n\n';
空の文字列だったtextTyped
に文章が追加されている。同じ文章を2回。\n
は改行で後述する方向転換の曲線になる部分。
centerX = width / 2; centerY = height / 2; offsetX = 0; offsetY = 0; zoom = 0.75;
centerX
は幅の半分、centerY
は高さの半分。offsetX
とoffsetY
は0。zoom
は0.75。
actRandomSeed = 6;
actRandomSeed
は6。
cursor(HAND); textFont(font, 25); textAlign(LEFT, BASELINE); noStroke(); fill(0);
cursor()
メソッドはカーソルアイコンの設定(変更)
Sets the cursor to a predefined symbol or an image, or makes it visible if already hidden.
カーソルを定義済みの記号または画像に設定するか、すでに非表示になっている場合はそれを表示します。
引数はHAND
で手(指)の形になる。
textFont()
はfont
のフォントを25pxで。
textAlign()
は左寄せでベースライン。
noStroke()
で線無し、fill(0)
で塗りは真っ黒。
windowResized():ウィンドウサイズが変更された時の処理
windowResized()
はウィンドウサイズが変更された時の処理
function windowResized() { resizeCanvas(windowWidth, windowHeight); }
resizeCanvas()
でキャンバスのサイズをウィンドウサイズにリサイズ。
draw():一定時間ごとに繰り返し実行
draw()
は一定時間ごとに繰り返し実行
function draw() { background(255); if (mouseIsPressed && mouseButton == LEFT) { centerX = mouseX - offsetX; centerY = mouseY - offsetY; } // allways produce the same sequence of random numbers randomSeed(actRandomSeed); translate(centerX, centerY); scale(zoom); for (var i = 0; i < textTyped.length; i++) { var letter = textTyped.charAt(i); var letterWidth = textWidth(letter); // ------ letter rule table ------ switch (letter) { case ' ': // space // 50% left, 50% right var dir = floor(random(0, 2)); if (dir == 0) { image(shapeSpace, 1, -15); translate(4, 1); rotate(QUARTER_PI); } if (dir == 1) { image(shapeSpace2, 1, -15); translate(14, -5); rotate(-QUARTER_PI); } break; case ',': image(shapeComma, 1, -15); translate(35, 15); rotate(QUARTER_PI); break; case '.': image(shapePeriod, 1, -55); translate(56, -56); rotate(-HALF_PI); break; case '!': image(shapeExclamationmark, 1, -27); translate(42.5, -17.5); rotate(-QUARTER_PI); break; case '?': image(shapeQuestionmark, 1, -27); translate(42.5, -17.5); rotate(-QUARTER_PI); break; case '\n': // return image(shapeReturn, 1, -15); translate(1, 10); rotate(PI); break; default: // all others text(letter, 0, 0); translate(letterWidth, 0); } } // blink cursor after text if (frameCount / 6 % 2 == 0) rect(0, 0, 15, 2); }
ボリュームがあるのでブロックごとに見ていく。
background(255);
背景は255
で真っ白。
if (mouseIsPressed && mouseButton == LEFT) { centerX = mouseX - offsetX; centerY = mouseY - offsetY; }
条件は「もしマウスが押されて、かつ左クリックだったら」
※参考:【p5.js】Generative Design with p5.js「形 P_2_0_02、03」のコードを読み解く - クモのようにコツコツと
横のセンター位置centerX
をマウスの横位置mouseX
引くoffsetX
、
縦のセンター位置centerY
をマウスの縦位置mouseY
引くoffsetY
に。
offsetX
とoffsetY
の初期値は0だった。
// allways produce the same sequence of random numbers
randomSeed(actRandomSeed);
コメントの和訳
常に同じ乱数列を生成する
randomSeed()
はシード値(乱数を作成する時の最初の設定値)を設定するメソッド。actRandomSeed
の初期値は6。
※参考:【p5.js】Generative Design with p5.js「色 P_2_1_1_01、02」のコードを読み解く - クモのようにコツコツと
translate(centerX, centerY); scale(zoom);
translate()
はオブジェクトの移動量の設定をするメソッド。開始点がcenterX
で終了点はcenterY
。
※参考:【p5.js】Generative Design with p5.js「文字 P_3_1_1_01」のコードを読み解く - クモのようにコツコツと
scale()
はシェイプの頂点を拡大縮小するメソッド。縮尺はzoom
で初期値は0.75。
Increases or decreases the size of a shape by expanding and contracting vertices.
頂点を拡大および縮小することにより、シェイプのサイズを拡大または縮小します。
for (var i = 0; i < textTyped.length; i++) { var letter = textTyped.charAt(i); var letterWidth = textWidth(letter); // ------ letter rule table ------ switch (letter) { // 分岐処理(後述) } }
for文の条件:変数i
の初期値は0、i
をtextTyped
の数の回数繰り返す、i
に1つずつ加算。
textTyped
の初期値はsetup()
にあったあの文章。
変数letter
はtextTyped
のcharAt()
のi
番目。
charAt()
はJSの組み込みメソッドで文字を左から1文字ずつ取得。
変数letterWidth
は文字幅(textWidth()
)と文字間(tracking
)の合計。
※参考:【p5.js】Generative Design with p5.js「文字 P_3_1_1_01」のコードを読み解く - クモのようにコツコツと
その後にSwitch文の分岐処理があるが、ボリュームがあるので、ブロックごとに見ていく。
Switch文の中の処理(スペースの場合)
case ' ': // space // 50% left, 50% right var dir = floor(random(0, 2)); if (dir == 0) { image(shapeSpace, 1, -15); translate(4, 1); rotate(QUARTER_PI); } if (dir == 1) { image(shapeSpace2, 1, -15); translate(14, -5); rotate(-QUARTER_PI); } break;
変数dir
は0以上2未満(floor()
で少数切り捨て)
dir
がもし0ならimage()
実行。shapeSpace
の画像を横1、縦-15の位置に。
※参考:【p5.js】Generative Design with p5.js「画像 P_4_1_1_01」のコードを読み解く - クモのようにコツコツと
translate()
はオブジェクトの移動量の設定をするメソッド。開始点が15で終了点が-5。
※参考:【p5.js】Generative Design with p5.js「文字 P_3_1_1_01」のコードを読み解く - クモのようにコツコツと
rotate()
は回転させるメソッド。
Rotates a shape the amount specified by the angle parameter.
角度パラメーターで指定された量だけ形状を回転させます。
引数は-QUARTER_PI
。QUARTER_PI
とは円の円周と直径の比率の4分の1(0.7853982)なので-0.7853982。
QUARTER_PI is a mathematical constant with the value 0.7853982. It is one quarter the ratio of the circumference of a circle to its diameter.
QUARTER_PIは、0.7853982の値を持つ数学定数です。 これは、円の円周と直径の比率の4分の1です。
Switch文の中の処理(,
の場合)
case ',': image(shapeComma, 1, -15); translate(35, 15); rotate(QUARTER_PI); break;
image()
:画像shapeComma
の横位置1、縦位置-15。translate()
:オブジェクトの移動量、開始点が35で終了点が15。rotate()
:QUARTER_PI
で0.7853982
回転
Switch文の中の処理(.
の場合)
case '.': image(shapePeriod, 1, -55); translate(56, -56); rotate(-HALF_PI); break;
image()
:画像shapePeriod
の横位置1、縦位置-55。translate()
:オブジェクトの移動量、開始点が56で終了点が-56。rotate()
:-HALF_PI
で-1.570...
回転
HALF_PI
は円周率PIの半分の角度で1.570...。-HALF_PI
なので-1.570...
。
HALF_PI is a mathematical constant with the value 1.57079632679489661923.
HALF_PIは、1.57079632679489661923の値を持つ数学定数です。
Switch文の中の処理(!
の場合)
case '!': image(shapeExclamationmark, 1, -27); translate(42.5, -17.5); rotate(-QUARTER_PI); break;
image()
:画像shapeExclamationmark
の横位置1、縦位置-27。translate()
:オブジェクトの移動量、開始点が42.5で終了点が-17.5。rotate()
:-QUARTER_PI
で-0.7853982
回転
Switch文の中の処理(?
の場合)
case '?': image(shapeQuestionmark, 1, -27); translate(42.5, -17.5); rotate(-QUARTER_PI); break;
image()
:画像shapeQuestionmark
の横位置1、縦位置-27。translate()
:オブジェクトの移動量、開始点が42.5で終了点が-17.5。rotate()
:-QUARTER_PI
で-0.7853982
回転
Switch文の中の処理(改行の場合)
case '\n': // return image(shapeReturn, 1, -15); translate(1, 10); rotate(PI); break;
image()
:画像shapeReturn
の横位置1、縦位置-15。translate()
:オブジェクトの移動量、開始点が1で終了点が10。rotate()
:PI
で3.14...
回転
PI
は円周率の3.14。
Switch文の中の処理(その他)
default: // all others text(letter, 0, 0); translate(letterWidth, 0);
text()
:letter
の内容。横位置0、縦位置0。translate()
:開始点がletterWidth
で終了点が0。
text()
はメソッドはテキストの描画。
※参考:【p5.js】Generative Design with p5.js「文字 P_3_1_1_01」のコードを読み解く - クモのようにコツコツと
// blink cursor after text if (frameCount / 6 % 2 == 0) rect(0, 0, 15, 2);
コメントの和訳
blink cursor after text
テキストの後にカーソルを点滅させる
もしframeCount
が6で割り切れたら横0、縦0の位置に幅15、高さ2の四角を描画する。
frameCount
はフレーム数で、rect()
メソッドは四角の描画。
※参考:【p5.js】Generative Design with p5.js「文字 P_3_1_1_01」のコードを読み解く - クモのようにコツコツと
mousePressed():マウスボタンを押した時の処理
mousePressed()
はマウスボタンを押した時の処理
function mousePressed() { offsetX = mouseX - centerX; offsetY = mouseY - centerY; }
offsetX
をmouseX
引くcenterX
にoffsetY
をmouseY
引くcenterY
に
クリックしても何も起こらない。自分の環境はマウスではなくノートPCのトラックパッドだからかな?
オフセット(英:offset)とは位置を基準点からの距離で表した値のこと。
※参考:https://wa3.i-3-i.info/word11923.html
あー、なるほど。この処理は単にクリックした場所とセンターの距離を取得するだけのようだ。
offsetX
とoffsetY
の初期値0で、draw()
の中のif文で使われる。そこにこの関数で上書きされた設定値が使われるわけだ。
keyReleased():文字キーを離した時の処理
keyReleased()
は文字キーを離した時の処理
function keyReleased() { // export png if (keyCode == CONTROL) saveCanvas(gd.timestamp(), 'png'); if (keyCode == ALT) actRandomSeed++; }
- もしキーが
CONTROL
だったらキャンバスをpng画像で保存 - もしキーが
ALT
だったらactRandomSeed
の値を1つ加算
一つ目はいつもの画像保存。2つ目のactRandomSeed
はdraw()
の中のrandomSeed()
の引数に入る
keyPressed():文字キーを押した時の処理
keyPressed()
は文字キーを押した時の処理
function keyPressed() { switch (keyCode) { case DELETE: case BACKSPACE: textTyped = textTyped.substring(0, max(0, textTyped.length - 1)); print(textTyped); break; case ENTER: case RETURN: // enable linebreaks textTyped += '\n'; break; case UP_ARROW: zoom += 0.05; break; case DOWN_ARROW: zoom -= 0.05; break; } }
中身はswitch文でいくつかの条件に分岐している。
case DELETE: case BACKSPACE: textTyped = textTyped.substring(0, max(0, textTyped.length - 1)); print(textTyped); break;
DELETE
またはBACKSPACE
の時はtextTyped
のsubstring()
を実行。
substring()
はJS組み込みメソッドで文字を取り出す。第1引数は0、第2引数のmax()
は最大値を取得。0からtextTyped.length
から1引く。これで全ての文字を取得する。
※参考:【p5.js】Generative Design with p5.js「文字 P_3_1_1_01」のコードを読み解く - クモのようにコツコツと
print()
を実行、引数は先ほどのtextTyped
。print()
はコンソールにデータを書くメソッド。
The print() function writes to the console area of your browser. This function is often helpful for looking at the data a program is producing.
print()関数は、ブラウザーのコンソール領域に書き込みます。 この関数は、プログラムが生成しているデータを調べるのに役立ちます。
デベロッパーツールのコンソールを見ると確かにテキストが書かれている。
case ENTER: case RETURN: // enable linebreaks textTyped += '\n'; break;
ENTER
またはRETURN
の時はtextTyped
に改行を追加する
case UP_ARROW: zoom += 0.05; break;
UP_ARROW
(上矢印)の時はzoom
に0.05加算する。
case DOWN_ARROW: zoom -= 0.05; break;
DOWN_ARROW
(下矢印)の時はzoom
に0.05減産する。
keyTyped():文字キーを押した時の処理
keyTyped()
は文字キーを押した時の処理
function keyTyped() { if (keyCode >= 32) { textTyped += key; print(textTyped); } }
もしキーコードが32以上だったらtextTyped
にkey
を追加。print(textTyped)
を実行。
キーコードが32より少ない場合は違う処理をさせるが、32以上の場合はtextTyped
に追加してprint()
でコンソールに文字を入力する。
※参考:キーコード一覧 【JavaScript 動的サンプル】
最後に
今回新たに出会ったp5.jsのメソッドなど
- windowResized()
- loadFont()
- HAND
- scale()
- rotate()
- QUARTER_PI
- HALF_PI
- PI
- print()
組み込みJSメソッド
- charAt()
- substring()
次回は「画像編」の続きを読み解きます。それではまた!
※参考:p5.jsを習得するためにやったことまとめ
qiita.com