diff options
Diffstat (limited to 'public/bluebey-studio')
| -rw-r--r-- | public/bluebey-studio/assets/beards/kaiser-right.png | bin | 0 -> 665 bytes | |||
| -rw-r--r-- | public/bluebey-studio/assets/beards/kaiser.png | bin | 2612 -> 0 bytes | |||
| -rw-r--r-- | public/bluebey-studio/src/faceArt.js | 24 | ||||
| -rw-r--r-- | public/bluebey-studio/src/main.js | 22 |
4 files changed, 35 insertions, 11 deletions
diff --git a/public/bluebey-studio/assets/beards/kaiser-right.png b/public/bluebey-studio/assets/beards/kaiser-right.png Binary files differnew file mode 100644 index 0000000..03a9cfc --- /dev/null +++ b/public/bluebey-studio/assets/beards/kaiser-right.png diff --git a/public/bluebey-studio/assets/beards/kaiser.png b/public/bluebey-studio/assets/beards/kaiser.png Binary files differdeleted file mode 100644 index 617fde5..0000000 --- a/public/bluebey-studio/assets/beards/kaiser.png +++ /dev/null diff --git a/public/bluebey-studio/src/faceArt.js b/public/bluebey-studio/src/faceArt.js index 2e1572a..38df333 100644 --- a/public/bluebey-studio/src/faceArt.js +++ b/public/bluebey-studio/src/faceArt.js @@ -888,13 +888,29 @@ function drawBeard(ctx, spec, lineOnly = false, textures = null) { // A supplied drawing for this kind (assets/beards/<shape>.png) replaces the // built-in strokes. The drawings are trimmed to their ink, so `w` is the // beard's real width; it is centred on the nose and scaled by `size`. - const texture = textures?.[shape]; - if (texture && texture.width) { + const entry = textures?.[shape]; + if (entry?.image) { + const { image, half } = entry; // Slightly wider than the built-in base: a moustache drawn to the same width // as a beard's stroke sat mostly behind the nose. const drawW = w * 1.4; - const dh = (drawW * (texture.height || 1)) / (texture.width || 1); - ctx.drawImage(texture, cx - drawW / 2, cy - dh / 2, drawW, dh); + if (half) { + // One side, mirrored to make the pair. The drawing is the model's own right + // side, which sits in the artwork's left half; `spacing` opens the middle. + const halfW = drawW / 2; + const dh = (halfW * (image.height || 1)) / (image.width || 1); + const gap = clamp(spec.spacing ?? 0, 0, 800); + const y = cy - dh / 2; + ctx.drawImage(image, cx - gap - halfW, y, halfW, dh); + ctx.save(); + ctx.translate(cx + gap, cy); + ctx.scale(-1, 1); + ctx.drawImage(image, 0, -dh / 2, halfW, dh); + ctx.restore(); + return; + } + const dh = (drawW * (image.height || 1)) / (image.width || 1); + ctx.drawImage(image, cx - drawW / 2, cy - dh / 2, drawW, dh); return; } diff --git a/public/bluebey-studio/src/main.js b/public/bluebey-studio/src/main.js index f90dfc8..9514ca4 100644 --- a/public/bluebey-studio/src/main.js +++ b/public/bluebey-studio/src/main.js @@ -393,17 +393,25 @@ async function init() { // one is drawn from the file instead of the built-in strokes. A missing file is // skipped, so the app works before any are added. void (async () => { - // Only the kinds that actually ship a drawing. Add a name here when its file is - // added to assets/beards/, so no 404s are logged for the ones without one. - const kinds = ['scotch', 'kaiser', 'apron']; + // Each entry: a beard kind and the drawing in `assets/beards/`. A file ending + // `-right.png` is one side of a moustache; it is drawn twice (mirrored) with a + // `spacing` gap between them. A kind with no entry uses the built-in strokes. + const files = [ + { kind: 'scotch', file: 'scotch.png' }, + { kind: 'kaiser', file: 'kaiser-right.png' }, + { kind: 'apron', file: 'apron.png' }, + ]; const loaded = {}; - await Promise.all(kinds.map(async (kind) => { + await Promise.all(files.map(async ({ kind, file }) => { try { - const response = await fetch(assetUrl(`${kind}.png`, 'beard')); + const response = await fetch(assetUrl(file, 'beard')); if (!response.ok) return; - loaded[kind] = await createImageBitmap(await response.blob()); + loaded[kind] = { + image: await createImageBitmap(await response.blob()), + half: file.endsWith('-right.png'), + }; } catch { - /* no drawing for this kind: the built-in one is used */ + /* a missing drawing falls back to the built-in strokes */ } })); if (Object.keys(loaded).length) { |
