| 12 | let prevTxt = '' |
| 13 | let prevTxtTime = 0 |
| 14 | function draw() { |
| 15 | if (txt !== prevTxt) { |
| 16 | prevTxt = txt |
| 17 | prevTxtTime = millis() |
| 18 | } |
| 19 | const progress = pow(map(millis(), prevTxtTime, prevTxtTime + 2000, 0, 1, true), 0.5) |
| 20 | |
| 21 | const contours = font.textToContours(txt, 0, 0, { sampleFactor: 1 }) |
| 22 | |
| 23 | background(0) |
| 24 | textAlign(CENTER, CENTER) |
| 25 | textSize(120) |
| 26 | textFont(font) |
| 27 | translate(width/2, height/2) |
| 28 | scale(min(width, height)/300) |
| 29 | const w = max(10, fontWidth(txt)) |
| 30 | scale((width * 0.2) / w) |
| 31 | |
| 32 | beginShape() |
| 33 | for (const contour of contours) { |
| 34 | beginContour() |
| 35 | for (const pt of contour) { |
| 36 | vertex(pt.x, pt.y) |
| 37 | } |
| 38 | endContour(CLOSE) |
| 39 | } |
| 40 | endShape() |
| 41 | |
| 42 | push() |
| 43 | strokeWeight(0.5) |
| 44 | stroke('rgb(255,127,228)') |
| 45 | noFill() |
| 46 | |
| 47 | beginShape(LINES) |
| 48 | for (const contour of contours) { |
| 49 | const pts = contour.map((v) => createVector(v.x, v.y)) |
| 50 | if (pts[0].dist(pts.at(-1)) === 0) pts.pop() |
| 51 | const dists = pts.map((pt, i) => max(1e-6, pt.dist(pts[(i+1)%pts.length]))) |
| 52 | |
| 53 | let tangents = pts.map((v, i) => pts[(i+1)%pts.length].copy().sub(v).div(dists[i])) |
| 54 | for (let it = 0; it < 2; it++) { |
| 55 | tangents = tangents.map( |
| 56 | (tangent, i) => |
| 57 | tangent.copy() |
| 58 | .add(tangents[(i-1+pts.length)%pts.length]) |
| 59 | .add(tangents[(i+1)%pts.length]) |
| 60 | .mult(1/3) |
| 61 | ) |
| 62 | } |
| 63 | |
| 64 | const ks = tangents.map((t, i) => tangents[(i+1)%pts.length].copy().sub(t)) |
| 65 | |
| 66 | pts.forEach((pt, i) => { |
| 67 | vertex(pt.x, pt.y) |
| 68 | vertex(pt.x + ks[i].x * -120 * progress, pt.y + ks[i].y * -120 * progress) |
| 69 | }) |
| 70 | } |
| 71 | endShape() |