diff options
Diffstat (limited to 'public')
| -rw-r--r-- | public/bluebey-studio/src/faceArt.js | 24 | ||||
| -rw-r--r-- | public/bluebey-studio/src/panel.js | 31 |
2 files changed, 27 insertions, 28 deletions
diff --git a/public/bluebey-studio/src/faceArt.js b/public/bluebey-studio/src/faceArt.js index 9ed6c83..3eb106f 100644 --- a/public/bluebey-studio/src/faceArt.js +++ b/public/bluebey-studio/src/faceArt.js @@ -1399,12 +1399,27 @@ export function drawEyes(ctx, p) { ctx.restore(); }; + // Where the pupil is looking. Every eye shape shares it: a shut eye's line, a + // 3 or an arch shift with the gaze too, not just the round eyeball. + const eyeTravel = isHeart ? heartTravel * lookMax : travel * lookMax; + let dx = clamp(spec.lookX ?? 0, -1, 1) * eyeTravel; + let dy = -clamp(spec.lookY ?? 0, -1, 1) * eyeTravel; + const dist = Math.hypot(dx, dy); + if (dist > eyeTravel && dist > 0) { + dx = (dx / dist) * eyeTravel; + dy = (dy / dist) * eyeTravel; + } + if (open <= 0.02 || open <= lowerLid + 0.02) { // A shut eye can still be given a white backing (the "白目" switch), which // reads as a line drawn across an eyeball rather than a bare line. This also // catches a lower lid raised past the upper one, which reads as shut too. if (!line && showWhite) fillCircle(ctx, eye.cx, eye.cy, eye.r, white); + // 目線 moves the drawn shape as well, so a shut eye can glance sideways. + ctx.save(); + ctx.translate(dx, dy); drawShutEye(ctx, eye, spec, lineStyle, shapeScale, shapeHalfLimit); + ctx.restore(); // The glasses sit over the shut-eye artwork too, but under the tear below. drawGlasses(ctx, eye, line, style, limits); drawTear(); @@ -1418,15 +1433,6 @@ export function drawEyes(ctx, p) { // iris. While the lid is partly closed the clip is still needed. if (!isHeart || open < 0.999) clipEye(ctx, eye, open, isHeart ? 0 : lowerLid, lidFlat, lidTilt * eye.towardNose); - const eyeTravel = isHeart ? heartTravel * lookMax : travel * lookMax; - let dx = clamp(spec.lookX ?? 0, -1, 1) * eyeTravel; - let dy = -clamp(spec.lookY ?? 0, -1, 1) * eyeTravel; - const dist = Math.hypot(dx, dy); - if (dist > eyeTravel && dist > 0) { - dx = (dx / dist) * eyeTravel; - dy = (dy / dist) * eyeTravel; - } - // The white of the eye (see `showWhite` above). if (!line && showWhite) fillCircle(ctx, eye.cx, eye.cy, eye.r, white); const irisX = eye.cx + dx; diff --git a/public/bluebey-studio/src/panel.js b/public/bluebey-studio/src/panel.js index 2809269..b942c22 100644 --- a/public/bluebey-studio/src/panel.js +++ b/public/bluebey-studio/src/panel.js @@ -476,12 +476,11 @@ export function buildPanel(app, root) { // one menu. `open`/`closed`/`irisShape` stay the stored fields; this reads and // writes them together so the menu always shows the current look. const eyeShapeOf = (eye) => { - // A shut eye wins over a heart: dragging the 開き slider down leaves a shut - // line (or 3/わらう), even if the heart pupil is still selected underneath. if ((eye.open ?? 1) <= 0.02) { if (eye.closed === 'three') return 'three'; if (eye.closed === 'arch') return 'arch'; - return 'line'; + // The shut line's stroke count is part of the shape itself. + return `line${Math.min(3, Math.max(1, Math.round(eye.closedLines ?? 1)))}`; } return (eye.irisShape ?? 'circle') === 'heart' ? 'heart' : 'open'; }; @@ -492,6 +491,11 @@ export function buildPanel(app, root) { return; } eye.open = 0; + if (value.startsWith('line')) { + eye.closed = 'line'; + eye.closedLines = Number(value.slice(4)) || 1; + return; + } eye.closed = value; }; const eyeWhiteOf = (eye) => { @@ -550,7 +554,9 @@ export function buildPanel(app, root) { label: '目の形', options: [ { value: 'open', label: 'ふつう' }, - { value: 'line', label: '線' }, + { value: 'line1', label: '1線' }, + { value: 'line2', label: '2線' }, + { value: 'line3', label: '3線' }, { value: 'three', label: '3の目' }, { value: 'arch', label: 'わらう' }, { value: 'heart', label: 'ハート' }, @@ -562,17 +568,6 @@ export function buildPanel(app, root) { label: '形の大きさ', min: 0.5, max: 1.8, step: 0.01, value: 1, format: (v) => v.toFixed(2), onInput: (v) => { write((e) => { e.shapeScale = v; }); touched(); }, }); - const closedLines = segmented({ - label: '線の数', - options: [ - { value: '1', label: '1本' }, - { value: '2', label: '2本' }, - { value: '3', label: '3本' }, - ], - value: '1', - hint: '2本以上は端点がつながった形になります', - onChange: (v) => { write((e) => { e.closedLines = Number(v); applyEyeShape(e, 'line'); }); touched(); }, - }); const threeFlip = segmented({ label: '3の向き', options: [ @@ -637,7 +632,6 @@ export function buildPanel(app, root) { group.add(lidTilt); group.add(shape); group.add(shapeScale); - group.add(closedLines); group.add(threeFlip); group.add(controlRow(null, white.el, { wide: true })); group.add(controlRow(null, highlight.el, { wide: true })); @@ -658,8 +652,6 @@ export function buildPanel(app, root) { lidTilt.set(state.face.eyes.lidTilt ?? 0); shape.set(shapeValue); shapeScale.set(e.shapeScale ?? 1); - closedLines.set(String(e.closedLines ?? 1)); - closedLines.el.style.display = shapeValue === 'line' ? '' : 'none'; threeFlip.set(e.threeFlip ? 'flip' : 'normal'); threeFlip.el.style.display = shapeValue === 'three' ? '' : 'none'; white.set(eyeWhiteOf(e)); @@ -687,7 +679,8 @@ export function buildPanel(app, root) { faceSection.add(leftControls.group); faceSection.add(rightControls.group); faceSection.add(hint('「まぶたの形」を「直線(平ら)」にすると、まぶたが平らに下りてきます。' - + '「まぶたの傾き」で、細めた目を左右反転で傾けられます。')); + + '「まぶたの傾き」で、細めた目を左右反転で傾けられます。' + + '「目の形」の「2線」「3線」は、端点がつながった形になります。')); // 横位置 (per eye) rarely changes, so it lives in こまかい設定. const eyeAdvancedWidgets = {}; |
