diff options
Diffstat (limited to 'public/bluebey-studio/src/faceArt.js')
| -rw-r--r-- | public/bluebey-studio/src/faceArt.js | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/public/bluebey-studio/src/faceArt.js b/public/bluebey-studio/src/faceArt.js index 7b5f7de..9d5e20d 100644 --- a/public/bluebey-studio/src/faceArt.js +++ b/public/bluebey-studio/src/faceArt.js @@ -919,15 +919,21 @@ export function drawHeadCoverWrap(ctx, spec, layout) { if (shape === 'fringe') return base + 0.15 * Math.max(0, 1 - Math.abs(t) * 1.6); // はちわれ: the same V upside down - a widow's peak. if (shape === 'fringe-up') return base - 0.18 * Math.max(0, 1 - Math.abs(t) * 1.5); - // カーブ: a plain shallow curve, lower (more hair) round the sides. - if (shape === 'curve') return base + 0.10 * t * t; - // わけめ: a 7:3 parting - a step at 30% from the left, so the larger side - // sits lower (more hair). - if (shape === 'side') return base + 0.08 - 0.16 / (1 + Math.exp(-(t + 0.4) * 10)); + // カーブ: a deep round cover - low at the sides, high in the middle, like the + // blue of a certain robot cat's head. + if (shape === 'curve') return base + 0.42 * t * t; + // わけめ: a 7:3 part. The hairline is asymmetric across the *front* only and + // comes back to the base level at the sides, so it is continuous at the back + // seam (an angle-varying hairline there would ride the seam and step). The + // parting line is the gap cut out below. + if (shape === 'side') { + const w = Math.max(0, 1 - t * t); // 0 at the sides, 1 at the front + return base + 0.18 * w * (0.5 + 0.5 * t); + } return base - 0.07 * (1 - t * t); // `cap` }; - const steps = 96; + const steps = Math.max(96, Math.ceil(W / 2)); ctx.beginPath(); ctx.moveTo(0, 0); for (let i = 0; i <= steps; i += 1) { @@ -938,6 +944,23 @@ export function drawHeadCoverWrap(ctx, spec, layout) { ctx.closePath(); ctx.fillStyle = color; ctx.fill(); + + if (shape === 'side') { + // The parting: a thin wedge cut out at 30% (a 7:3 split), running from the + // top of the head down to the hairline. + const partX = span * 0.4; + const gw = Math.max(8, span * 0.016) * (0.6 + size); + ctx.save(); + ctx.globalCompositeOperation = 'destination-out'; + ctx.beginPath(); + ctx.moveTo(partX - gw * 0.5, 0); + ctx.lineTo(partX + gw * 0.5, 0); + ctx.lineTo(partX + gw * 1.2, clamp(vBound(partX), 0, 1) * H + 2); + ctx.lineTo(partX - gw * 1.2, clamp(vBound(partX), 0, 1) * H + 2); + ctx.closePath(); + ctx.fill(); + ctx.restore(); + } ctx.restore(); } |
