本記事は、以下の記事から続いています。
Truchet トルシェット タイル
正方形を斜めに分け、白黒に塗り分けたタイルを用意する。これを敷き詰める際に向きをランダムにしたり、規則的にすることで、また別の模様が見えてくる。
int(random(4))*90;
乱数0~3.999を発生させ、intで整数に切り捨てる(0,1,2,3)。
その後90を掛けて、0,90,180,270を生成する。
radians(th);
度をラジアンに単位変換する命令。
PImage img;
size(640, 640);
img = loadImage("truchet.png");
for (int y=0; y<640; y+=64) {
for (int x=0; x<640; x+=64) {
float th = int(random(4))*90;
push();
translate(x+32, y+32);
rotate(radians(th));
translate(-img.width/2, -img.height/2);
image(img, 0, 0);
pop();
}
}
六角形充填
正方形と同様に、六角形を敷き詰める事を考える。
画像は、イラレから書き出す際に、背景を透過色pngで保存する。
画像サイズは、64x64として、画像の中心が六角形の中心になるようにする。

int(random(6))*60
これは0,60,120,180,240,300を生成する式である。
((x/(int)w)%2*(h*0.5))
これは、xをwで割ったときに、奇数か偶数かで、上下にずらす値を計算する式である。
xを整数で割りたいので、intでwを変換し、
%2で、余りが、0か1を求める。
その結果に、ずらしたい縦の移動量(h*0.5)を掛けている。
h/2だと結果が、整数になるかもしれないので、避けている。
「55.4256」この値は、イラレで表示された値です。
PImage img;
float w=64-16;
float h=55.4256;
size(640, 640);
img = loadImage("truchetHex.png");
for (int y=0; y<640; y+=h) {
for (int x=0; x<640; x+=w) {
float th = int(random(6))*60;
push();
translate(x, y+((x/(int)w)%2*(h*0.5)));
rotate(radians(th));
translate(-img.width/2, -img.height/2);
image(img, 0, 0);
pop();
}
}
改良
truchetパターンは多く考案されているので、正方形や六角形の画像を差し替えて、違うバリエーションを試してほしい。
スクショを撮るか、save("xxx.png");で保存提出。
おまけ
おまけ2、AIにプログラムを依頼
// Truchet Aesthetic Playground
// Processing (Java) 3.x/4.x
int tile = 64; // タイルサイズ
int style = 1; // 1..6
int paletteId = 0; // 0..3
boolean showGrid = false;
boolean mirrorSym = false; // ミラー対称ON/OFF
boolean showHelp = true;
float sw = 3.0; // 線幅
int seedBase = (int)random(1<<30);
void setup() {
size(960, 960);
smooth(8);
frameRate(60);
reseed();
}
void reseed() {
seedBase = (int)random(1<<30);
randomSeed(seedBase);
noiseSeed(seedBase);
redraw();
}
void draw() {
background(bgColor());
strokeWeight(sw);
noFill();
int cols = ceil(width / (float)tile);
int rows = ceil(height / (float)tile);
// 中心を基準にノイズの位相
float t = millis()*0.0003; // わずかにアニメ的な“呼吸”もOKに(止めたい時はt=0に)
for (int j = 0; j < rows; j++) {
for (int i = 0; i < cols; i++) {
pushMatrix();
translate(i*tile, j*tile);
int rot = pickRotation(i, j, t); // 0,1,2,3 のいずれか
// ミラー対称(左右/上下)を入れると“柄としてのまとまり”が出る
if (mirrorSym) {
int mi = min(i, cols-1-i);
int mj = min(j, rows-1-j);
rot = pickRotation(mi, mj, t);
}
// タイルごとに色を切り替える(パレットと連動)
stroke(colLine(i, j));
// 中心回転しやすいようにローカル座標へ
translate(tile*0.5, tile*0.5);
rotate(HALF_PI * rot);
translate(-tile*0.5, -tile*0.5);
// 選択スタイル
switch(style) {
case 1: drawQuarterArcs(); break; // 定番:四分円
case 2: drawDiagonalCurves(); break; // 対角を結ぶ曲線(エレガント)
case 3: drawTriCuts(); break; // 三角分割+反転(幾何学的)
case 4: drawHatchLines(); break; // ハッチング線(密度差)
case 5: drawRibbon(); break; // リボン状ベジェ(柔らかさ)
case 6: drawDots(); break; // ドット接続(軽やか)
}
if (showGrid) {
stroke(0, 30);
noFill();
rect(0, 0, tile, tile);
}
popMatrix();
}
}
if (showHelp) drawHUD();
}
// ==== タイル描画バリエーション ====
// 1) Truchet定番:四分円(角Rを大きめに)
void drawQuarterArcs() {
float r = tile;
strokeCap(SQUARE);
arc(0, 0, r, r, 0, HALF_PI);
arc(tile, tile, r, r, PI, PI+HALF_PI);
}
// 2) 対角を結ぶ曲線(近接端をC曲線で繋ぐ)
void drawDiagonalCurves() {
strokeCap(ROUND);
float m = tile*0.15;
// 左上→右下
beginShape();
vertex(0, m);
quadraticVertex(tile*0.5, tile*0.5, tile-m, tile);
endShape();
// 左下→右上(対称にもう1本)
beginShape();
vertex(m, tile);
quadraticVertex(tile*0.5, tile*0.5, tile, m);
endShape();
}
// 3) 三角分割+反転(陰影=線の太さ差で)
void drawTriCuts() {
pushStyle();
float m = tile*0.08;
strokeWeight(sw*0.9);
line(0, 0, tile, tile);
strokeWeight(sw*0.45);
line(0, tile, tile, 0);
// 端に少し“返し”
strokeWeight(sw*0.7);
line(m, 0, m, tile);
popStyle();
}
// 4) ハッチング(密度差で模様感)
void drawHatchLines() {
pushStyle();
float step = tile/8.0;
for (float y=0; y<=tile; y+=step) {
line(0, y, tile, y);
}
// 反対方向に薄く
stroke(0, 60);
for (float x=0; x<=tile; x+=step*2.0) {
line(x, 0, x, tile);
}
popStyle();
}
// 5) リボン状ベジェ(端点と接線方向を揃える)
void drawRibbon() {
pushStyle();
noFill();
float k = tile*0.25;
float inset = tile*0.12;
// 太線→細線で奥行き感
strokeWeight(sw*1.1);
drawRibbonCore(inset, k);
strokeWeight(sw*0.6);
drawRibbonCore(inset*1.5, k*0.9);
popStyle();
}
void drawRibbonCore(float inset, float k) {
beginShape();
vertex(inset, inset);
bezierVertex(tile*0.5, inset-k, tile-inset+k, tile*0.5, tile-inset, tile-inset);
endShape();
}
// 6) ドット接続(モアレを避けつつ軽やか)
void drawDots() {
float s = tile/6.0;
float r = max(1.2, sw*0.55);
for (int y=0; y<6; y++) {
for (int x=0; x<6; x++) {
if ( (x+y)%2==0 ) {
ellipse((x+0.5)*s, (y+0.5)*s, r, r);
}
}
}
}
// ==== 配色(落ち着き/華やかを切替) ====
int bgColor() {
switch (paletteId) {
case 0: return color(246, 245, 240); // 生成り
case 1: return color(9, 13, 20); // 墨
case 2: return color(252, 249, 244); // 和紙
default:return color(244, 248, 252); // 淡い青
}
}
int colLine(int i, int j) {
// 行列の偶奇+ノイズでうっすら揺らぎ
float n = noise(i*0.17, j*0.17);
switch (paletteId) {
case 0: { // 紺・朱・生成り
color[] cs = { color(23, 43, 77), color(186, 62, 38), color(64, 112, 153) };
return lerpColor(cs[(i+j)%3], color(0,0,0), 0.05 + 0.2*n);
}
case 1: { // 墨背景用:藍〜薄灰
color[] cs = { color(179, 196, 219), color(118, 150, 188), color(210, 216, 226) };
return lerpColor(cs[(i+j)%3], color(255), 0.15*n);
}
case 2: { // 萌黄色・山吹
color[] cs = { color(21, 101, 112), color(234, 146, 37), color(160, 77, 58) };
return lerpColor(cs[(i+j)%3], color(30), 0.1*n);
}
default: { // 北欧っぽい淡配色
color[] cs = { color(33, 86, 117), color(235, 87, 87), color(244, 210, 74) };
return lerpColor(cs[(i+j)%3], color(20), 0.1*n);
}
}
}
// ==== 回転選択(“離散化されたランダム”) ====
// 0/90/180/270°を、ノイズと偶奇でバランス良く配る
int pickRotation(int i, int j, float t) {
float k = 0.23;
float n = noise(i*k, j*k, t*0.35); // 0..1
// 偶奇で“地合い”を作る
int base = ((i+j)&1)==0 ? 0 : 2;
// ノイズで近傍の連続性をもたせつつ離散化
int r = base + floor(constrain(map(n, 0, 1, 0, 2.999), 0, 3));
return r % 4;
}
// ==== UI / 入出力 ====
void keyPressed() {
if (key>='1' && key<='6') { style = key - '0'; redraw(); }
if (key=='r' || key=='R') { reseed(); }
if (key=='p' || key=='P') { paletteId = (paletteId+1)%4; redraw(); }
if (key=='g' || key=='G') { showGrid = !showGrid; redraw(); }
if (key=='m' || key=='M') { mirrorSym = !mirrorSym; redraw(); }
if (key=='h' || key=='H') { showHelp = !showHelp; }
if (key=='[') { tile = max(24, tile-8); redraw(); }
if (key==']') { tile = min(160, tile+8); redraw(); }
if (key==',') { sw = max(0.5, sw-0.5); redraw(); }
if (key=='.') { sw = min(12, sw+0.5); redraw(); }
if (key=='s' || key=='S') {
String fn = String.format("truchet_%d_style%d_pal%d.png", seedBase, style, paletteId);
save(fn);
println("saved:", fn);
}
}
void drawHUD() {
pushStyle();
fill(0, 150);
noStroke();
rect(16, 16, 420, 136, 10);
fill(255);
textSize(12);
text(
"Truchet Aesthetic Playground\n" +
"[1..6]=Style R=Reseed P=Palette G=Grid M=Mirror\n" +
"[ / ]=Tile , / .=Stroke S=Save PNG H=Help\n" +
"tile="+tile+" sw="+nf(sw,1,1)+" style="+style+" palette="+paletteId+
" seed="+seedBase, 28, 40);
popStyle();
}



