260419
const W = 800;
const H = 800;
const { animate } = anime;
let animeValue = {
count: 0,
};
let cycle = 0;
let palette = [];
let currentSeed = 0;
const SIZE = 700;
const MIN_SIZE = 120;
const STROKE_W = 2;
const FONT_URL = "fonts/WDXLLubrifontJPN-Regular.ttf";
let myFont;
const REGION_TEXTS = ["焼", "肉", "定", "食"];
async function setup() {
createCanvas(W, H, WEBGL);
myFont = await loadFont(FONT_URL);
strokeCap(SQUARE);
strokeWeight(STROKE_W);
palette = colorArray[0].colors;
background(palette[4]);
animate(animeValue, {
count: 1,
duration: 1500,
ease: "inOutExpo",
loop: true,
onLoop: () => {
cycle++;
},
});
currentSeed = int(random(10000));
}
function draw() {
background(palette[4]);
ortho();
// orbitControl();
rotateX(PI / 10);
rotateY(-PI / 10);
let rawCur = rawForCycle(cycle);
let x1, y1, x2, y2;
if (cycle === 0) {
x1 = rawCur[0];
y1 = rawCur[1];
x2 = rawCur[2];
y2 = rawCur[3];
} else {
let rawPrev = rawForCycle(cycle - 1);
x1 = rawPrev[2];
y1 = rawPrev[3];
x2 = rawCur[2];
y2 = rawCur[3];
}
let lineFrame = animeValue.count;
let x = lerp(x1, x2, lineFrame);
let y = lerp(y1, y2, lineFrame);
const L = -SIZE / 2 - STROKE_W;
const R = SIZE / 2 - STROKE_W;
const B = -SIZE / 2 - STROKE_W;
const T = SIZE / 2 - STROKE_W;
const regions = [
{ cx: (L + x) / 2, cy: (B + y) / 2, w: x - L, h: y - B },
{ cx: (x + R) / 2, cy: (B + y) / 2, w: R - x, h: y - B },
{ cx: (L + x) / 2, cy: (y + T) / 2, w: x - L, h: T - y },
{ cx: (x + R) / 2, cy: (y + T) / 2, w: R - x, h: T - y },
];
for (let i = 0; i < regions.length; i++) {
const r = regions[i];
drawTextFitCell(REGION_TEXTS[i], r.cx, r.cy, r.w, r.h);
}
}
function drawTextFitCell(str, cx, cy, cellW, cellH) {
push();
textFont(myFont);
const innerW = max(1, cellW - 2 * STROKE_W);
const innerH = max(1, cellH - 2 * STROKE_W);
const ts = sqrt(innerW * innerH);
textSize(ts);
const gb = glyphPathAndBounds(str);
if (!gb) {
pop();
return;
}
const { commands, g } = gb;
const cxBox = g.x + g.w / 2;
const cyBox = g.y + g.h / 2;
translate(cx, cy, 0.01);
scale(innerW / g.w, innerH / g.h);
translate(-cxBox, -cyBox);
push();
fill(palette[2]);
noStroke();
drawP5PathCommandsFilled(commands);
pop();
pop();
}
function rawForCycle(cycle) {
randomSeed(currentSeed + cycle);
return [
random(-SIZE / 2 + MIN_SIZE, SIZE / 2 - MIN_SIZE),
random(-SIZE / 2 + MIN_SIZE, SIZE / 2 - MIN_SIZE),
random(-SIZE / 2 + MIN_SIZE, SIZE / 2 - MIN_SIZE),
random(-SIZE / 2 + MIN_SIZE, SIZE / 2 - MIN_SIZE),
];
}
// The following code is generated.
/*
* 【何をしているか】
* loadFont したフォントから textToPaths で輪郭データを取り、WEBGL の beginShape で塗りつぶす。
* p5 v2(beta)の Typography API を使う(stable 1.x には textToPaths がない)。
*
* 【textToPaths の形】
* フラットな配列。各要素は [種別, 数値…]。
* 種別は M / L / Q / C / Z(SVG のパスに近い)。Z は「ここまでがひとかたまりの閉じた線」。
*
* 【セルに合わせるための箱】
* グリフ全体の幅・高さがほしいので、すべての座標から軸に平行な外接矩形(AABB)を求める。
* ベジェの制御点だけを見ているので、曲線がはみ出す場合は箱が実線より小さくなることがある。
*
* 【塗り方】
* Z までを 1 輪郭として順に描く。輪郭が複数ある文字(「口」の内側など)は、2 本目から
* beginContour / endContour で「穴」として追加する。
* beginShape() に TESS などモードを付けると bezierVertex が使えないので、モードなしだけ使う。
*
* 参考:
* textToPaths — https://beta.p5js.org/reference/p5.Font/textToPaths
* beginShape — https://beta.p5js.org/reference/p5/beginshape/
* beginContour — https://beta.p5js.org/reference/p5/beginContour/
* bezierOrder — https://beta.p5js.org/reference/p5/bezierorder/
* bezierVertex — https://beta.p5js.org/reference/p5/bezierVertex/
*/
// textToPaths の結果と、セルに合わせる用の外接矩形(各コマンドの数値を (x,y) ペアとして走査)
function glyphPathAndBounds(str) {
if (!myFont || typeof myFont.textToPaths !== "function") return null;
const commands = myFont.textToPaths(str, 0, 0);
if (!commands?.length) return null;
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for (const cmd of commands) {
for (let i = 1; i + 1 < cmd.length; i += 2) {
const x = cmd[i];
const y = cmd[i + 1];
if (!isFinite(x) || !isFinite(y)) continue;
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
}
if (!isFinite(minX) || maxX <= minX || maxY <= minY) return null;
const w = maxX - minX;
const h = maxY - minY;
if (!w || !h) return null;
return { commands, g: { x: minX, y: minY, w, h } };
}
// パス配列をそのまま読み、Z または最後までで輪郭を切り替えて塗る(中間配列は作らない)
function drawP5PathCommandsFilled(commands) {
const n = commands.length;
if (!n) return;
const prevOrder =
typeof bezierOrder === "function" ? bezierOrder() : undefined;
let outer = true;
beginShape();
for (let i = 0; i < n; ) {
let j = i;
while (j < n && commands[j][0] !== "Z") j++;
const closed = j < n;
const end = closed ? j + 1 : n;
if (!outer) beginContour();
for (let k = i; k < end; k++) {
const cmd = commands[k];
switch (cmd[0]) {
case "M":
case "L":
vertex(cmd[1], cmd[2]);
break;
case "C":
bezierOrder(3);
bezierVertex(cmd[1], cmd[2]);
bezierVertex(cmd[3], cmd[4]);
bezierVertex(cmd[5], cmd[6]);
break;
case "Q":
bezierOrder(2);
bezierVertex(cmd[1], cmd[2]);
bezierVertex(cmd[3], cmd[4]);
break;
default:
break;
}
}
if (!outer) endContour();
outer = false;
i = closed ? j + 1 : n;
}
endShape(CLOSE);
if (typeof bezierOrder === "function" && prevOrder !== undefined) {
bezierOrder(prevOrder);
}
}
const colorArray = [
{
id: 0,
colors: [
"#9dbdba",
"#f8b042",
"#e47763",
"#253276",
"#dfdad3",
"#FFFFFF",
"#000000",
],
},
]; textToPoints()で何ができるかを模索中。
const W = 800;
const H = 800;
const { animate } = anime;
let animeValue = {
count: 0,
};
let cycle = 0;
let palette = [];
let currentSeed = 0;
const SIZE = 700;
const MIN_SIZE = 120;
const STROKE_W = 2;
const FONT_URL = "fonts/WDXLLubrifontJPN-Regular.ttf";
let myFont;
const REGION_TEXTS = ["焼", "肉", "定", "食"];
async function setup() {
createCanvas(W, H, WEBGL);
myFont = await loadFont(FONT_URL);
strokeCap(SQUARE);
strokeWeight(STROKE_W);
palette = colorArray[0].colors;
background(palette[4]);
animate(animeValue, {
count: 1,
duration: 1500,
ease: "inOutExpo",
loop: true,
onLoop: () => {
cycle++;
},
});
currentSeed = int(random(10000));
}
function draw() {
background(palette[4]);
ortho();
// orbitControl();
rotateX(PI / 10);
rotateY(-PI / 10);
let rawCur = rawForCycle(cycle);
let x1, y1, x2, y2;
if (cycle === 0) {
x1 = rawCur[0];
y1 = rawCur[1];
x2 = rawCur[2];
y2 = rawCur[3];
} else {
let rawPrev = rawForCycle(cycle - 1);
x1 = rawPrev[2];
y1 = rawPrev[3];
x2 = rawCur[2];
y2 = rawCur[3];
}
let lineFrame = animeValue.count;
let x = lerp(x1, x2, lineFrame);
let y = lerp(y1, y2, lineFrame);
const L = -SIZE / 2 - STROKE_W;
const R = SIZE / 2 - STROKE_W;
const B = -SIZE / 2 - STROKE_W;
const T = SIZE / 2 - STROKE_W;
const regions = [
{ cx: (L + x) / 2, cy: (B + y) / 2, w: x - L, h: y - B },
{ cx: (x + R) / 2, cy: (B + y) / 2, w: R - x, h: y - B },
{ cx: (L + x) / 2, cy: (y + T) / 2, w: x - L, h: T - y },
{ cx: (x + R) / 2, cy: (y + T) / 2, w: R - x, h: T - y },
];
for (let i = 0; i < regions.length; i++) {
const r = regions[i];
drawTextFitCell(REGION_TEXTS[i], r.cx, r.cy, r.w, r.h);
}
}
function drawTextFitCell(str, cx, cy, cellW, cellH) {
push();
textFont(myFont);
const innerW = max(1, cellW - 2 * STROKE_W);
const innerH = max(1, cellH - 2 * STROKE_W);
const ts = sqrt(innerW * innerH);
textSize(ts);
const gb = glyphPathAndBounds(str);
if (!gb) {
pop();
return;
}
const { commands, g } = gb;
const cxBox = g.x + g.w / 2;
const cyBox = g.y + g.h / 2;
translate(cx, cy, 0.01);
scale(innerW / g.w, innerH / g.h);
translate(-cxBox, -cyBox);
push();
fill(palette[2]);
noStroke();
drawP5PathCommandsFilled(commands);
pop();
pop();
}
function rawForCycle(cycle) {
randomSeed(currentSeed + cycle);
return [
random(-SIZE / 2 + MIN_SIZE, SIZE / 2 - MIN_SIZE),
random(-SIZE / 2 + MIN_SIZE, SIZE / 2 - MIN_SIZE),
random(-SIZE / 2 + MIN_SIZE, SIZE / 2 - MIN_SIZE),
random(-SIZE / 2 + MIN_SIZE, SIZE / 2 - MIN_SIZE),
];
}
// The following code is generated.
/*
* 【何をしているか】
* loadFont したフォントから textToPaths で輪郭データを取り、WEBGL の beginShape で塗りつぶす。
* p5 v2(beta)の Typography API を使う(stable 1.x には textToPaths がない)。
*
* 【textToPaths の形】
* フラットな配列。各要素は [種別, 数値…]。
* 種別は M / L / Q / C / Z(SVG のパスに近い)。Z は「ここまでがひとかたまりの閉じた線」。
*
* 【セルに合わせるための箱】
* グリフ全体の幅・高さがほしいので、すべての座標から軸に平行な外接矩形(AABB)を求める。
* ベジェの制御点だけを見ているので、曲線がはみ出す場合は箱が実線より小さくなることがある。
*
* 【塗り方】
* Z までを 1 輪郭として順に描く。輪郭が複数ある文字(「口」の内側など)は、2 本目から
* beginContour / endContour で「穴」として追加する。
* beginShape() に TESS などモードを付けると bezierVertex が使えないので、モードなしだけ使う。
*
* 参考:
* textToPaths — https://beta.p5js.org/reference/p5.Font/textToPaths
* beginShape — https://beta.p5js.org/reference/p5/beginshape/
* beginContour — https://beta.p5js.org/reference/p5/beginContour/
* bezierOrder — https://beta.p5js.org/reference/p5/bezierorder/
* bezierVertex — https://beta.p5js.org/reference/p5/bezierVertex/
*/
// textToPaths の結果と、セルに合わせる用の外接矩形(各コマンドの数値を (x,y) ペアとして走査)
function glyphPathAndBounds(str) {
if (!myFont || typeof myFont.textToPaths !== "function") return null;
const commands = myFont.textToPaths(str, 0, 0);
if (!commands?.length) return null;
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for (const cmd of commands) {
for (let i = 1; i + 1 < cmd.length; i += 2) {
const x = cmd[i];
const y = cmd[i + 1];
if (!isFinite(x) || !isFinite(y)) continue;
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
}
if (!isFinite(minX) || maxX <= minX || maxY <= minY) return null;
const w = maxX - minX;
const h = maxY - minY;
if (!w || !h) return null;
return { commands, g: { x: minX, y: minY, w, h } };
}
// パス配列をそのまま読み、Z または最後までで輪郭を切り替えて塗る(中間配列は作らない)
function drawP5PathCommandsFilled(commands) {
const n = commands.length;
if (!n) return;
const prevOrder =
typeof bezierOrder === "function" ? bezierOrder() : undefined;
let outer = true;
beginShape();
for (let i = 0; i < n; ) {
let j = i;
while (j < n && commands[j][0] !== "Z") j++;
const closed = j < n;
const end = closed ? j + 1 : n;
if (!outer) beginContour();
for (let k = i; k < end; k++) {
const cmd = commands[k];
switch (cmd[0]) {
case "M":
case "L":
vertex(cmd[1], cmd[2]);
break;
case "C":
bezierOrder(3);
bezierVertex(cmd[1], cmd[2]);
bezierVertex(cmd[3], cmd[4]);
bezierVertex(cmd[5], cmd[6]);
break;
case "Q":
bezierOrder(2);
bezierVertex(cmd[1], cmd[2]);
bezierVertex(cmd[3], cmd[4]);
break;
default:
break;
}
}
if (!outer) endContour();
outer = false;
i = closed ? j + 1 : n;
}
endShape(CLOSE);
if (typeof bezierOrder === "function" && prevOrder !== undefined) {
bezierOrder(prevOrder);
}
}
const colorArray = [
{
id: 0,
colors: [
"#9dbdba",
"#f8b042",
"#e47763",
"#253276",
"#dfdad3",
"#FFFFFF",
"#000000",
],
},
];