diff options
| author | Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> | 2026-10-04 00:43:33 +0900 |
|---|---|---|
| committer | Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> | 2026-10-04 00:43:33 +0900 |
| commit | 8c430aefc24545c100b34acea2824d837f5e0337 (patch) | |
| tree | 6e6c823b57936511438379cfed0ce3fbffe90ef3 /public/bluebey-studio/src/faceArt.js | |
| parent | 3c7fc9271cdaf7b8ee11609d3c4e73df7ba9e0d9 (diff) | |
bluebey-studio: 顔タブの並び替えと髪の形を追加
顔タブを プリセット→髪→眉→目→見る先→…の順に。頭の模様を「髪」としてほっぺから分離。見る先を目の直下へ。なみを削除し、わけめを7:3分けに、まえがみをとがった形に、まえがみ(逆)を「はちわれ」に改名し、単純なカーブの形を追加。髪を目の前に出すか後ろに回すかを選べるようにした。
Diffstat (limited to 'public/bluebey-studio/src/faceArt.js')
| -rw-r--r-- | public/bluebey-studio/src/faceArt.js | 46 |
1 files changed, 21 insertions, 25 deletions
diff --git a/public/bluebey-studio/src/faceArt.js b/public/bluebey-studio/src/faceArt.js index c4940d4..7b5f7de 100644 --- a/public/bluebey-studio/src/faceArt.js +++ b/public/bluebey-studio/src/faceArt.js @@ -796,7 +796,7 @@ function drawCheeks(ctx, spec) { */ function drawHeadCover(ctx, spec, limits) { const shape = spec?.shape ?? 'off'; - if (shape === 'off' || shape === 'none') return; + if (shape === 'off' || shape === 'none' || shape === 'wave') return; const size = clamp(spec.size ?? 1, 0.02, 3); const left = limits.left; @@ -828,23 +828,14 @@ function drawHeadCover(ctx, spec, limits) { const halfSpan = width / 2; const boundary = (x) => { const t = clamp((x - cx) / halfSpan, -1, 1); - if (shape === 'fringe') { - // Bangs: a soft point dipping in the middle, with side lobes. - return base + 150 * (1 - Math.abs(t)) - 55 * Math.abs(Math.sin(t * Math.PI)); - } - if (shape === 'fringe-up') { - // The mirror image: a fringe that peaks *up* in the middle (a widow's peak - // or centre part), so more forehead shows in the middle than at the sides. - return base - 150 * (1 - Math.abs(t)) + 55 * Math.abs(Math.sin(t * Math.PI)); - } - if (shape === 'wave') { - // A single smooth sweep across the forehead. - return base + 120 * Math.sin(t * Math.PI * 0.9 + 0.5); - } - if (shape === 'side') { - // A side part: high on one side, low on the other. - return base + 260 * t; - } + // まえがみ: a narrow, deep V pointing down the middle (Atom / Mickey). + if (shape === 'fringe') return base + 175 * Math.max(0, 1 - Math.abs(t) * 1.6); + // はちわれ: the same V upside down - a widow's peak. + if (shape === 'fringe-up') return base - 320 * Math.max(0, 1 - Math.abs(t) * 1.5); + // カーブ: a plain shallow curve, higher (more hair) round the sides. + if (shape === 'curve') return base + 160 * t * t; + // わけめ: a 7:3 parting - a step at 30% from the left. + if (shape === 'side') return base + 60 - 320 / (1 + Math.exp(-(t + 0.4) * 10)); // `cap`: a rounded helmet, lowest in the middle. return base - 100 * (1 - t * t); }; @@ -864,7 +855,7 @@ function drawHeadCover(ctx, spec, limits) { if (shape === 'side') { // わけめ: cut a real parting in the hair, so the two halves read as combed // apart rather than as one smooth sweep. - const partX = cx - halfSpan * 0.45; + const partX = cx - halfSpan * 0.4; const gw = Math.max(5, width * 0.02) * (0.6 + size); ctx.save(); ctx.globalCompositeOperation = 'destination-out'; @@ -894,8 +885,8 @@ function drawHeadCover(ctx, spec, limits) { */ export function drawHeadCoverWrap(ctx, spec, layout) { const shape = spec?.shape ?? 'off'; - // `split` is a retired front-plate shape; it has no wrap equivalent. - if (shape === 'off' || shape === 'none' || shape === 'split') return; + // `split` and `wave` are retired front-plate shapes; they have no wrap equal. + if (shape === 'off' || shape === 'none' || shape === 'split' || shape === 'wave') return; const size = clamp(spec.size ?? 1, 0.02, 3); const W = layout.width; @@ -924,10 +915,15 @@ export function drawHeadCoverWrap(ctx, spec, layout) { const cx = layout.offsetX + span * 0.5 + clamp(spec.offsetX ?? 0, -600, 600); const vBound = (x) => { const t = clamp((x - cx) / half, -1, 1); - if (shape === 'fringe') return base + 0.14 * (1 - Math.abs(t)) - 0.05 * Math.abs(Math.sin(t * Math.PI)); - if (shape === 'fringe-up') return base - 0.14 * (1 - Math.abs(t)) + 0.05 * Math.abs(Math.sin(t * Math.PI)); - if (shape === 'wave') return base + 0.10 * Math.sin(t * Math.PI * 0.9 + 0.5); - if (shape === 'side') return base + 0.20 * t; + // まえがみ: a narrow, deep V pointing down the middle (Atom / Mickey). + 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)); return base - 0.07 * (1 - t * t); // `cap` }; |
