はじめに
Processingのリファレンス+αとして、Generative Artっぽいものを目指したサンプルです。
リファレンス参照
https://processing.org/reference#shape-2d-primitives
リファレンス(abc)順にサンプル載せます。
円弧 arc
これらの円弧の長さは、すべて同じ。
size(800, 800);
background(255);
float r = 390;
noFill();
for (float n=0.5; n<2.01; n+=0.1) {
arc(400, 400, r/n, r/n, 0, PI*n);
}
size(800, 800);
background(0);
float r = 400;
fill(255, 255, 0);
arc(400, 400, r, r, PI/4, PI*7/4);
円circle
乱数でたくさん描く。
void setup(){
size(800, 800);
}
void draw() {
float x, y, r;
x = random(width);
y = random(height);
r = random(50, 100);
fill(random(255));
circle(x, y, r);
}
再帰処理で描く。
void setup() {
size(800, 800);
func(400, 400, 800, 8);
}
void func(float x, float y, float r, int n) {
if (0<n) {
fill(n%2*255);
circle(x, y, r);
func(x-r/4, y, r/2, n-1);
func(x+r/4, y, r/2, n-1);
}
}
楕円ellipse
乱数でたくさん描く。
void setup(){
size(800, 800);
}
void draw() {
float x, y, w, h;
x = random(width);
y = random(height);
w = random(50, 200);
h = random(50, 200);
fill(random(255));
ellipse(x, y, w, h);
}
回転させながら、縮小する。
size(800, 800);
translate(400, 400);
for (int n=0; n<50; n++) {
fill(255-n*4);
ellipse(0, 0, 800, 400);
scale(0.9);
rotate(PI/22);
}
線line
size(800, 400);
for (int y=0; y<400; y+=10) {
line( 0, y, 800-y*2, 0);
line(800, y, 800-y*2, 400);
}
点point
1ピクセルの点描ですが、直径1の円として描かれるため、希望の色になりません。noSmooth()を指定することで、希望の色で点描できます。
※set()やpixels[]使うと速くできます。
size(800, 400);
noSmooth();
for (int y=0; y<400; y+=2) {
for (int x=0; x<800; x+=2) {
stroke(x/4,y/2,0);
point(x,y);
}
}
四角quad
return to squareをオマージュ
size(800, 800);
noFill();
float a=200, b=600;
for (int n=0; n<150; n+=5) {
quad(a+n, a+n,
b+n, a-n,
b-n, b-n,
a-n, b+n);
quad(a-n, a+n,
b-n, a-n,
b-n, b+n,
a+n, b-n);
}
長方形、矩形rect
長方形が描けて、角丸にする機能もあります。
参考までに、正円を赤で書きました。
角丸の値は、長方形の短辺の半分より大きくすることはできず、無視されます。以下のサンプルの場合、幅400に対して、角丸の値は200までしか認識されません。
size(600, 600);
float x=100, y=100, w=400, h=400;
rect(x,y,w,h);
rect(x,y,w,h,50);
rect(x,y,w,h,100);
rect(x,y,w,h,150);
rect(x,y,w,h,200);
stroke(255,0,0);
circle(300,300,400);
size(800, 800);
translate(400, 400);
fill(255,255,255,10);
for (int x=0; x<=400; x+=10) {
stroke(x, 0, 255);
int y = 400-x;
rect(-x, -y, x*2, y*2);
}
正方形square
比は1/√2です。
size(800, 800);
translate(400, 400);
for (int n=0; n<20; n++) {
square(-400, -400, 800);
rotate(PI/4);
scale(1/sqrt(2));
}
三角形triangle
void setup() {
size(800, 800);
noStroke();
func(800, 800, 800, 8);
}
void func(float x, float y, float h, int n) {
if (0<n) {
fill(0);
triangle(x, y, x, y-h, x-h, y);
func(x-h/2, y+h/2, h/2, n-1);
func(x+h/2, y-h/2, h/2, n-1);
func(x-h/2, y-h/2, h/2, n-1);
}
}
多角形beginShape,endShape,vertex
beginShape
vertex(頂点1)
vertex(頂点2)
...
endShape
のように使う。
このとき、頂点をどのように描画するか、指示ができる。
指定なし:多角形
POINTS 複数の点
LINES 複数の線
TRIANGLES 複数の三角形
TRIANGLE_STRIP 帯状の連続した三角形
TRIANGLE_FAN 扇形の三角形
QUADS 複数の四角形
QUAD_STRIP 帯状の連続した四角形
※それぞれ対応する数字がある。
println(POINTS);//3
println(LINES);//5
println(TRIANGLES);//9
println(TRIANGLE_STRIP);//10
println(TRIANGLE_FAN);//11
println(QUADS);//17
println(QUAD_STRIP);//18
多角形
size(300, 300);
translate(150, 150);
beginShape();
for (float t=0; t<2*PI; t+=PI/6) {
vertex(100*cos(t), 100*sin(t));
}
endShape();
LINES
size(300, 300);
translate(150, 150);
beginShape(LINES);
for (float t=0; t<2*PI; t+=PI/6) {
vertex(100*cos(t), 100*sin(t));
}
endShape();
TRIANGLES
size(300, 300);
translate(150, 150);
beginShape(TRIANGLES);
for (float t=0; t<2*PI; t+=PI/6) {
vertex(100*cos(t), 100*sin(t));
}
endShape();
TRIANGLE_STRIP
size(300, 300);
translate(150, 150);
beginShape(TRIANGLE_STRIP);
for (float t=0; t<2*PI; t+=PI/6) {
vertex(100*cos(t), 100*sin(t));
}
endShape();
TRIANGLE_FAN
size(300, 300);
translate(150, 150);
beginShape(TRIANGLE_FAN);
for (float t=0; t<2*PI; t+=PI/6) {
vertex(100*cos(t), 100*sin(t));
}
endShape();
QUADS
size(300, 300);
translate(150, 150);
beginShape(QUADS);
for (float t=0; t<2*PI; t+=PI/6) {
vertex(100*cos(t), 100*sin(t));
}
endShape();
QUAD_STRIP
size(300, 300);
translate(150, 150);
beginShape(QUAD_STRIP);
for (float t=0; t<2*PI; t+=PI/6) {
vertex(100*cos(t), 100*sin(t));
}
endShape();
輪郭beginContour,endContour
多角形を描く際に、中をくりぬく。飛び出た場合は、図のようになる。
size(400,400);
translate(200, 200);
beginShape();
// 外側時計回り
vertex( 0, -170);
vertex(150, 90);
vertex(-150, 90);
// 内側反時計回り
beginContour();
vertex(-150, -90);
vertex(0, 170);
vertex(150, -90);
endContour();
endShape(CLOSE);
二次ベジェ曲線quadraticVertex
制御点は3つ必要です。最初の制御点は、直前の命令vertexの終点(0,200)になるので、quadraticVertexでは、2点(制御点と終点)の指定をします。サンプルでは、視点を終点を同一にし、制御点をずらして描画しています。
モアレ軽減のため、半透明使ってます。
size(400, 400);
noFill();
stroke(0,0,0,128);
beginShape();
vertex(0, 200);
for(float y=-200; y<600;y+=20){
quadraticVertex(200, y , 400, 200);
quadraticVertex(200, y+10, 0, 200);
}
endShape();
二次ベジェ曲線はlineで示したサンプルと一致します。
size(800, 400);
noFill();
for (int y=0; y<400; y+=10) {
line( 0, y, 800-y*2, 0);
line(800, y, 800-y*2, 400);
}
stroke(255,0,0);
beginShape();
vertex(0, 400);
quadraticVertex(0, 0, 800, 0);
quadraticVertex(800, 400, 0, 400);
endShape();
三次ベジェ曲線bezier
制御点は4つ必要です。始点と終点を固定し、1つの制御点を、ずらして描いています。
size(800, 400);
noFill();
stroke(0,0,0,128);
for(int y=-300; y<500; y+=20) {
bezier( 0, 200,
300, y,
500, 400,
800, 200);
}
bezierVertex
4つの制御点が必要なところを、始点を直前の命令(vertexなど)とすることで、3点を指定すれば良い方法です。イラレのような、連続したベジェ曲線に使えそうです。
ここでは、2本のベジェ曲線でハートを描きました。
size(800, 600);
translate(400, 150);
beginShape();
vertex(0, 0);
bezierVertex(300, -200, 400, 200, 0, 400);
bezierVertex(-400, 200, -300, -200, 0, 0);
endShape();
分割数制御bezierDetail
1~20の範囲で分割数を制御。P3Dのみ対応。出番は低そう。
size(800, 600, P3D);
translate(400, 150);
bezierDetail(20);
beginShape();
vertex(0, 0);
bezierVertex(300, -200, 400, 200, 0, 400);
bezierVertex(-400, 200, -300, -200, 0, 0);
endShape();
bezierDetail(5);
scale(0.8);
beginShape();
vertex(0, 0);
bezierVertex(300, -200, 400, 200, 0, 400);
bezierVertex(-400, 200, -300, -200, 0, 0);
endShape();
bezierDetail(3);
scale(0.8);
beginShape();
vertex(0, 0);
bezierVertex(300, -200, 400, 200, 0, 400);
bezierVertex(-400, 200, -300, -200, 0, 0);
endShape();
bezierPoint
0<t<1で
tを変化させたときに、どうなるか、
ベジェ曲線の理解を深めるのに使えそうです。
size(800, 600);
translate(400, 150);
int x1= 0, y1= 0;
int x2=300, y2=-200;
int x3=400, y3= 200;
int x4= 0, y4= 400;
bezier(x1, y1, x2, y2, x3, y3, x4, y4);
bezier(x4, y4, -x3, y3, -x2, y2, x1, y1);
float x,y;
strokeWeight(20);
for(float t=0; t<=1.001; t+=0.1){
x = bezierPoint(x1, x2, x3, x4, t);
y = bezierPoint(y1, y2, y3, y4, t);
point(x,y);
}
接線bezierTangent
tan正接、接線の向きが得られます。
ここでは、正規化(normalize)してから使いました。
また、接線を90度回転して法線が描けます。
(x,y)ベクトルを90度回すということは,
(y,-x)に変換することです。
size(800, 600);
translate(400, 150);
int x1= 0, y1= 0;
int x2=300, y2=-200;
int x3=400, y3= 200;
int x4= 0, y4= 400;
bezier(x1, y1, x2, y2, x3, y3, x4, y4);
bezier(x4, y4, -x3, y3, -x2, y2, x1, y1);
float x,y,tanx,tany;
for(float t=0; t<=1.001; t+=0.1){
x = bezierPoint(x1, x2, x3, x4, t);
y = bezierPoint(y1, y2, y3, y4, t);
tanx = bezierTangent(x1, x2, x3, x4, t);
tany = bezierTangent(y1, y2, y3, y4, t);
PVector v = new PVector(tanx,tany);
v.normalize();
stroke(255,0,0);
line(x-v.x*50,
y-v.y*50,
x+v.x*50,
y+v.y*50);
stroke(0,0,255);
line(x,
y,
x+v.y*50,
y-v.x*50);
}
Catmull-Romスプラインcurve
スプライン曲線とは指定した点の上を通る曲線のことです。
曲線の引き方にも種類があり、ProcessingではCatmull-Romをcurve命令で実装されています。
以下では、5点を指定し、これら5点を通る曲線を描いています。
通常1-2-3-4と点を指定した場合、2-3の間しか曲線が引けませんが、1-1-2-3と指定したり、3-4-5-5と重ねて指定することで、始点と終点を通す曲線が描けます。
size(600, 400);
noFill();
int x1=100, y1=100;
int x2=100, y2=300;
int x3=300, y3=300;
int x4=300, y4=100;
int x5=500, y5=100;
line(x1, y1, x2, y2);
line(x2, y2, x3, y3);
line(x3, y3, x4, y4);
line(x4, y4, x5, y5);
stroke(255, 0, 0);
curve(x1, y1, x1, y1, x2, y2, x3, y3);
stroke(0);
curve(x1, y1, x2, y2, x3, y3, x4, y4);
stroke(0, 0, 255);
curve(x2, y2, x3, y3, x4, y4, x5, y5);
stroke(0, 255, 0);
curve(x3, y3, x4, y4, x5, y5, x5, y5);
下はbezierのサンプル座標と同じ座標でcurveを使って、描いたものです。
bezierとcurveの指定点を通る通らないの特徴がでています。
size(800, 500);
noFill();
for(int y=-300; y<500; y+=20) {
stroke(0,0,0,128);
curve( 0, 200,
0, 200,
300, y,
500, 400);
stroke(255,0,0,128);
curve( 0, 200,
300, y,
500, 400,
800, 200);
stroke(0,0,255,128);
curve( 300, y,
500, 400,
800, 200,
800, 200);
}
curveVertex
長いスプライン曲線を描くなら、こっちの書き方がシンプルでしょう。
size(600, 400);
noFill();
int x1=100, y1=100;
int x2=100, y2=300;
int x3=300, y3=300;
int x4=300, y4=100;
int x5=500, y5=100;
line(x1, y1, x2, y2);
line(x2, y2, x3, y3);
line(x3, y3, x4, y4);
line(x4, y4, x5, y5);
stroke(255, 0, 0);
beginShape();
curveVertex(x1, y1);
curveVertex(x1, y1);
curveVertex(x2, y2);
curveVertex(x3, y3);
curveVertex(x4, y4);
curveVertex(x5, y5);
curveVertex(x5, y5);
endShape();
curveTightness
スプライン曲線を変えることができる。仕組みは未調査。引数はいくつでも良いが、リファレンスでは-5~5を使っていた。使う場面が思いつかないが、-1~1あたりなら丸みの制御で使えそう。
-1は大きく膨らむ
0は無指定と同じ
1は直線と同じ
2は曲線がねじれる
int x1=100, y1=100;
int x2=100, y2=300;
int x3=300, y3=300;
int x4=300, y4=100;
int x5=500, y5=100;
void setup(){
size(600, 500);
noFill();
for(int t=-5; t<=5; t++){
stroke(128+20*t, 0, 0);
func(t);
}
stroke(255,255,255);
line(x1, y1, x2, y2);
line(x2, y2, x3, y3);
line(x3, y3, x4, y4);
line(x4, y4, x5, y5);
}
void func(float t){
curveTightness(t);
beginShape();
curveVertex(x1, y1);
curveVertex(x1, y1);
curveVertex(x2, y2);
curveVertex(x3, y3);
curveVertex(x4, y4);
curveVertex(x5, y5);
curveVertex(x5, y5);
endShape();
}
for(float t=-1; t<=1; t+=0.4){
curveDetail
1~20の範囲で分割数を制御。P2Dのみ対応。出番は低そう。
int x1=100, y1=100;
int x2=100, y2=300;
int x3=300, y3=300;
int x4=300, y4=100;
int x5=500, y5=100;
void setup(){
size(600, 500, P2D);
noFill();
stroke(0, 0, 0);
func(1);
stroke(255, 0, 0);
func(2);
stroke(0, 0, 255);
func(4);
}
void func(int d){
curveDetail(d);
beginShape();
curveVertex(x1, y1);
curveVertex(x1, y1);
curveVertex(x2, y2);
curveVertex(x3, y3);
curveVertex(x4, y4);
curveVertex(x5, y5);
curveVertex(x5, y5);
endShape();
}
curvePoint
bezierと同じなので割愛
curveTangent
bezierと同じなので割愛
直方体box
void setup() {
size(400, 400, P3D);//OPENGLでも動く
}
void draw() {
background(255);
translate(200, 200, 0);
rotateY(frameCount*0.01);
noFill();
box(150);
box(200,100,200);
}
球sphere
sphereDetailのデフォルトは30
sphereDetail(水平分割数,垂直分割数)でも使える。
void setup() {
size(800, 800, P3D);
}
void draw() {
background(255);
translate(400, 400, 0);
rotateY(frameCount*0.01);
noFill();
sphereDetail(16);
sphere(300);
}
3D
void setup() {
size(800, 800, P3D);
noStroke();
}
void draw() {
background(255);
camera(0, -100, 200,
0, 0, 0,
0, 1, 0);
lights();
for (float z=-100; z<101; z+=50) {
for (float x=-100; x<101; x+=50) {
fill(random(255),random(255),random(255));
pushMatrix();
translate(x, 0, z);
sphere(20);
popMatrix();
}
}
}
ノイズnoise
randomとは異なり、滑らかに変化する乱数(0~1)が得られる。
実装はPerlin関数。
引数の数で1次元,2次元,3次元が選べる。
float th;
void setup() {
size(800, 800);
}
void draw() {
background(255);
for (int y=0; y<800; y+=10) {
for (int x=0; x<800; x+=10) {
th = noise(x*0.01, y*0.01, frameCount*0.02);
th *= 4*PI;
line(x, y, x+20*cos(th), y+20*sin(th));
}
}
}
その他
rect命令の角丸機能は、もしかして、iPhoneの角かも!と思いましたが、違いました。
rect(100,100,400,400,169);
の時一番近くなりますが、一致はしません。
Processingの入出力について
書き出し | 読み込み | 備考 | |
---|---|---|---|
JPG | o | o | save("a.jpg") loadImage() |
PNG | o | o | save("a.png") loadImage() |
SVG | o | o | import processing.svg.*; loadShape() |
OBJ | △ | o | import nervoussystem.obj.* loadShape() |
o | x | import processing.pdf.*; |
Sprine系の曲線をSVGやPDFで保存した場合でも、内部でベジェに変換されて保存されます。円もしかりです。