だらっと学習帳

Processingとか p5.jsとか 好きです

カテゴリ:Processing > Processing.js

Web上でp5.jsやProcessing.jsのスケッチを動かしたり、
改良したりできるエディタの中から特に面白いなぁと思ったものを3つ紹介します。
どれも手軽にコードの仕組みを理解したり学んだりできるので、
初心者から上級者まで幅広い層にオススメです。

p5.playground
23
エディタページ : p5PlayGround
関連記事 : p5Playground.: an WYSIWYG Interactive programming Tool for p5.js — Yining Shi

「ライブコーディング」と「プレイグラウンド」の2つのモードを持つp5.jsエディタです。
エディタページ上部の切り替えスイッチからモードを変更できます。

「ライブコーディング」モードは、右部エディタに書いたコードが
左部表示スペースにすぐに反映されるというもの。
「この行はどの部分の見た目や動きに関わっているのか」という疑問をすぐに解決してくれます。

そしてこのエディタの最大の特徴は「プレイグラウンド」モード。
左部表示スペースに描画されている図形を「つかんで」移動させたり大きさを変えたりできます。
また、表示スペース上部にある図形名の書かれたスイッチを押して、
表示スペース上でダブルクリックすると、新しい図形を生成することもできます。
個人的には、ベジェ曲線を直感的に描画できるのが嬉しいですね。


まほうのハコ - P5 Visual Editor - (alpha)
28
エディタページ : まほうのハコ -P5 Visual Editor- (alpha)
関連記事 : 備忘録:よく知りもしないJavaScriptでライブラリとか作る男の記録 - 遊んで航海記

命令の書かれたブロックを組み合わせてスケッチを作成するビジュアルp5.jsプログラミングエディタです。
文字がひらがなで書かれていたり、言葉のチョイスがやわらかいので、
小さな子どもさんとも取り組める内容になっています。

ブロックの内容も、基本的なものから応用的なものまで、幅広く用意されています。
特に「うごき」や「そうさ」あたりのブロックを上手く組み合わせれば、
簡単に見栄えのする作品やインタラクティブ性のある作品が作れそうです。


pictureAlive
18
エディタページ : pictureAlive
関連記事 : processingを使ってweb上で画像を加工してTwitterで画像とソースコードを共有できる #pictureAlive を作った - matablo

コードのローカル保存や使用画像のアップロード等、
充実した機能を備えているProcessing.jsエディタです。
関数の挿入機能を使えば様々な図形を扱うこともできます。

このエディタの一番のポイントは強力なツイート機能にあります。
スケッチをキャプチャして画像付きツイートしてくれるだけでなく、
コード自体の共有リンクも発行してくれます。
Processingのスケッチは見た目もコードもどちらも大事なので、
両方ともソーシャルにシェアできるのは嬉しいですね。

01

Vapor_Sphere- OpenProcessing

Processing.jsで背景色としてグラデーションを描いてみました。
Processing.jsだとget()やset()あたりの処理に時間がかかるようなので、
下記ページを参考に、線を並べるように書いてみました。

参考ページ :
画像処理についてあれこれ: Processingでグラデーションを描画する

0226230213

color colorA;
color colorB;

void setup() {
size(200, 200);

colorA = color(#FD9DFF);
colorB = color(#62C6D8);
}

void draw() {
float dr = (red(colorB) - red(colorA))/height;
float dg = (green(colorB) - green(colorA))/height;
float db = (blue(colorB) - blue(colorA))/height;

for (int i = 0; i < height; i++) {
color lineColor = color(
(red(colorA) + i*dr),
(green(colorA) + i*dg),
(blue(colorA) + i*db));

stroke(lineColor);
line(0, i, width, i);
}
}

36

このブログのPC版のメニュー欄に、SUZURIのオススメ商品を表示するブログパーツを追加しました。
カーソルを合わせると、上記画像のようにタイトルと左右の矢印が表示されます。
矢印を押すとページめくりのように商品の画像を切り替えることができます。
また、矢印以外の部分をクリックすると、商品の販売ページを開きます。
カーソルを合わせていない時は、画像を自動で切り替えながら表示します。

制作にあたって詰まったところがいくつかありました。
PDEのJavaモードで作ってからJSモードに切り替えて動作を確認していたのですが、
このライブドアブログに埋め込んだところ、
タイトルバー(タイトルテキスト・矢印)が表示されませんでした。
さらに、tint()が動作せず、画像が切り替わる時にぼかしが効かずに紙芝居のような状態になってしまいました。

一つ目の問題であるタイトルバーが表示されなかった問題については、
タイトルバーを表示する条件として、画像の大きさや画面の大きさとマウスの座標位置を比べて判定させていたのですが、
この画像の大きさを取得するために [PImage名].width と書いていたことが原因だったようです。
これの代わりに整数を直接与えることで解決しました。

tint()が動作しなかった問題については、
別のWebページ上に同じスケッチを埋め込んだところ、
そちらでは正常に動作したため、原因がよくわかりませんでした。
しかし、@n_ryotaさんが以下のページで公開されているライブラリを使用することでライブドアブログ上でも正常に動作させることができました。
Processing.jsの画像の透過処理を高速化 : Processingで遊ぼう - Writing Cafe
ライブドアブログには画像のアップロード機能の他に、
JSファイル等をアップロードするための機能があり、そちらを利用しました。

[宣伝]
db427f9c
レオナ ( reona396 ) のアイテム一覧 ∞ SUZURI
こちらのページで私がProcessingで制作したグラフィックのグッズを販売しています。
今回のブログパーツではiPhoneケースしか掲載していませんが、
それ以外にもTシャツやマグカップ、バッグなども販売しています。
デザインも色々あるので、ぜひご覧ください!

なお、SUZURIについてはこちらもご覧ください。
SUZURIでProcessingスケッチをグッズ化してみた : だらっと学習帳

ブログパーツのソースコード
int pictures_val = 8;

PImage[] pictures = new PImage[pictures_val];

String[] title = {
"Stars", "Rose Curve", "Camouflage",
"Colorful Photo", "Random Paint", "Yagasuri",
"Polka Dots", "Game of Life"
};

String[] id = {
"185038", "152475", "37191",
"80562", "71569", "183520",
"191680", "63786"
};

String[] pict_id = {
"a/f/af832daa.png", "c/6/c6d7b988.png", "4/4/4419e974.png",
"c/a/cac64365.png", "c/b/cbbcec09.png", "0/8/0869b9fb.png",
"2/c/2c698dab.png", "0/f/0f652f2a.png"
};

float alp_theta;
int now_pict_num = 0;

int margin;

int arrowX_start;
int arrowX_goal;
int arrowY_start;
int arrowY_goal;
int arrow_margin;

int pict_size;

void setup() {
size(180, 180);
colorMode(HSB, 360, 100, 100);
background(0, 0, 95);

for (int i = 0; i < pictures_val; i++) {
pictures[i] = loadImage("http://livedoor.blogimg.jp/reona396/imgs/" + pict_id[i]);
}

imageMode(CENTER);
rectMode(CENTER);
textAlign(CENTER, CENTER);

pict_size = 150; //pictures[0].width;
margin = (width - pict_size)/2;

arrowX_start = margin + 5;
arrowX_goal = margin + 15;
arrowY_start = height/2 - 5;
arrowY_goal = height/2 + 5;

arrow_margin = 8;
}


void draw() {
//画像表示
showImage();

//画像上にオンマウスか判断
if (onPicture()) {
//タイトルパネル表示
showTitle();
} else {
//画像切り替え
switchImage();
}
}

void showImage() {
tint(360, 255*abs(sin(radians(alp_theta))));
image(pictures[now_pict_num], width/2, height/2);
}

boolean onPicture() {
if (mouseX > margin && mouseX < margin + pict_size
&& mouseY > margin && mouseY < margin + pict_size) {
return true;
} else {
return false;
}
}

boolean onLeftArrow() {
if (mouseX > arrowX_start - arrow_margin && mouseX < arrowX_goal + arrow_margin
&& mouseY > arrowY_start - arrow_margin && mouseY < arrowY_goal + arrow_margin) {
return true;
} else {
return false;
}
}

boolean onRightArrow() {
if (mouseX > width - arrowX_goal - arrow_margin && mouseX < width - arrowX_start + arrow_margin
&& mouseY > arrowY_start - arrow_margin && mouseY < arrowY_goal + arrow_margin) {
return true;
} else {
return false;
}
}

void showTitle() {
//タイトル表示
noStroke();
fill(180, 150);
rect(width/2, height/2, pict_size, height/5);
fill(360);
text(title[now_pict_num], width/2, height/2);

//矢印の色変え - 左
strokeWeight(2);
if (onLeftArrow()) {
stroke(119, 100, 100);
} else {
stroke(360);
}
line(arrowX_start, height/2, arrowX_goal, arrowY_start);
line(arrowX_start, height/2, arrowX_goal, arrowY_goal);

//矢印の色変え - 右
if (onRightArrow()) {
stroke(119, 100, 100);
} else {
stroke(360);
}
line(width - arrowX_start, height/2, width - arrowX_goal, arrowY_start);
line(width - arrowX_start, height/2, width - arrowX_goal, arrowY_goal);
}

void switchImage() {
//画像切替効果
alp_theta += 2;

//次の画像へ
if (alp_theta > 360) {
alp_theta = 0;
now_pict_num++;
//画像が一巡したら最初の画像へ
if (now_pict_num > pictures.length - 1) {
now_pict_num = 0;
}
}
}

void mousePressed() {
//左矢印をクリック
if (onLeftArrow()) {
stroke(119, 100, 100);
if (mousePressed == true) {
now_pict_num--;
if (now_pict_num < 0) {
now_pict_num = pictures.length - 1;
}
}
}
//右矢印をクリック
else if (onRightArrow()) {
stroke(119, 100, 100);
if (mousePressed == true) {
now_pict_num++;
if (now_pict_num > pictures.length - 1) {
now_pict_num = 0;
}
}
}
//画像上にオンマウス
//リンクを開く
else if (onPicture()) {
link("https://suzuri.jp/reona396/" + id[now_pict_num] + "/smartphone-case/iphone6/white", "_new");
}
else{
//なにもしない><
}
}


関連記事
Processing.jsでブログパーツをつくってみた : だらっと学習帳

2015/04/29 - 追記
いくつかアドバイスをいただいたので、コードを修正しました。
@n_ryotaさん、@ayato_p氏、ありがとうございます!

pict03

上の画像はProcessingで生成したものです。
私の中では「部分的ノイズ」と呼んでいます。
明度によってノイズのように色を飛ばすかどうかを決めています。
そのため、枝のように黒っぽい部分は輪郭がしっかりしています。

コードは以下のとおりです。
PImage img;

color[][] c;

void setup() {
img = loadImage("./pict.JPG");

size(img.width, img.height);

colorMode(HSB, 360, 100, 100);
background(360);

image(img, 0, 0);

c = new color[img.width][img.height];

for (int i = 0; i < img.width; i++) {
for (int j = 0; j < img.height; j++) {
c[i][j] = get(i, j);
}
}
noStroke();
}

void draw() {
background(360);

for (int i = 0; i < img.width; i++) {
for (int j = 0; j < img.height; j++) {
if (random(100) > brightness(c[i][j])) {
fill(hue(c[i][j]), saturation(c[i][j]), brightness(c[i][j]));
}
rect(i, j, 1, 1);
}
}
}

このスケッチはget()でピクセルごとに色を調べています。
このスケッチをProcessing.jsに変換して OpenProcessing に投稿しようとしたのですが、
うまく動作しませんでした。
そこで、色々と検証してみました。

画像の使用について
そもそも画像が呼び出せるかについて検証しました。
35
Alien_on_the_Sea- OpenProcessing

小さなpng画像をDropboxのPublicフォルダにアップロードし、
公開リンクからロードすることで呼び出しています。
また、OpenProcessingに投稿する際は圧縮ファイルをアップロードするのではなく、
「create new sketch」から投稿しました。

スケッチの動作自体は特に問題がなかったため、
画像のサイズを小さくすると良さそうだと考えました。
なお、このスケッチをOpenProcessingに投稿する際、
サムネイル用の画像が正常にキャプチャできませんでした。
image() をコメントアウトすることでキャプチャ自体はできるようになりました。
もちろん画像は表示されない状態ではありますが…。

pixelsによる代用
pixels[]がget()と同じような動きができると知り、
画像サイズを小さく調整したうえで、置き換えてみることにしました。
pict03
PImage img;

float[] H;
float[] S;
float[] B;

void setup() {
img = loadImage("./pict.JPG");

size(img.width, img.height);

colorMode(HSB, 360, 100, 100);
background(360);

image(img, 0, 0);

H = new float[width*height];
S = new float[width*height];
B = new float[width*height];

loadPixels();
for (int i = 0; i < width * height; i++) {
H[i] = hue(pixels[i]);
S[i] = saturation(pixels[i]);
B[i] = brightness(pixels[i]);
}

noStroke();
}

void draw() {
background(360);

for (int i = 0; i < width * height; i++) {
if (random(100) > B[i]) {
fill(H[i], S[i], B[i]);
}
rect((i%width)-1, i/width, 1, 1);
}
}

ノイズが横向きになりましたが…。
この状態で再びOpenProcessingに投稿しようとしたところ、
やはり投稿に失敗してしまいました。

get()およびpixels[]の動きの確認
pixels[]やget()がProcessing.js内でうまく動いていないのかもしれないと考え、
それぞれ検証してみることにしました。
画像を使用せず、pixels[]やget()で色を調べる部分だけ共通のしくみを利用しました。
39
Partial_Noise- OpenProcessing
pixels[]について検証しました。
03
Partial_Noise02- OpenProcessing
get()について検証しました。

こうして比べてみると、ノイズが動き出す前に、単に円が描画されているだけの状態が表示される時があります。
つまり、setup内の動きに時間がかかっているようです。
ということで、Processing.jsではこれらの処理は時間がかかるということが推測されます。

結論
これらのことから、Processing.jsで作成されたスケッチをOpenProcessingに投稿する際、
image()の使用はサイズに注意すれば動作自体は特に問題はなく、
pixels[]やget()を利用して色のチェックを行うのは時間がかかってしまうということがわかりました。
これらの処理をした上でWebで公開したい場合は、
gifに変換するのが一番良い方法だと思われます。

0410150243

merge_sort01(ランダムな数字をソート)
merge_sort02(降順の数字をソート)

Processingでマージソートを描きました。
クリックするか、適当なキーを押すことでソートが進行します。
進行するたびに、値のコピー及び交換が可視化され、
ソートが完了するとまた未ソートの状態からやりなおします。
右下に表示される数字が、クリックまたはキーを押した回数です。

構成としては、最初にsetup関数内で固定長の配列のマージソートを実行し、
その中で値の変化があるたびにArrayListにその配列のデータをコピーしておきます。
そして、クリックされたりキーが押されたりすると、
その回数に応じたArrayListが呼び出され、描画されます。

最初はJavaモードで書き、その後JavaScriptモード(Processing.js)になおしました。
その際、clone()の扱いと計算方法の違いで詰まってしまいました。
clone()については、以下のページを参考にしました。
JavaScriptでオブジェクトをクローンする方法。: 俺の砂箱
上で解説されているclone.jsを利用させてもらいました。
/lang/javascript/clone/trunk/clone.js – CodeRepos::Share – Trac
また、マージソートにおいて配列の区間内の中間点を求める計算をする際、
以下のようにfloor()をつけました。
mid = floor((left + right) / 2);   
これについては以下のページを参考にしました。
配列内のデータをソートする(マージソート)

↑このページのトップヘ