From 85bd079d18fd5cf32315320f58d7cc89801f300f Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Thu, 1 Oct 2026 09:58:00 +0900 Subject: [PATCH] =?UTF-8?q?=EC=95=84=EB=B0=94=ED=83=80=20v3=20=ED=91=9C?= =?UTF-8?q?=ED=98=84=20=EC=97=94=EC=A7=84=C2=B7=EB=A6=AC=EB=85=B8=EC=BB=B7?= =?UTF-8?q?=20=EB=A0=8C=EB=8D=94=EB=9F=AC=C2=B7=EB=A6=BD=EC=8B=B1=ED=81=AC?= =?UTF-8?q?=EC=99=80=20Lab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 표현 엔진: 채널 34개(mouthRound 추가), 표정 28종·강도 곡선·반응 클립 26종·지문 cue, 미세표정 누설 - 발화층: 한글 자모 비짐 9종, TTS 오디오 선분석 정렬, 60ms 앞당김·모음 간 비닫힘 - 발화 동반층: 억양·강세에 맞춘 고개 표류·끄덕임·질문 갸웃·들숨·눈썹 박·시선 회피·쉼 깜빡임 - P1 서연 리노컷 렌더러: 원화 픽셀 입술 띠 변형, 하관 띠 변형(턱·코 들썩), 볼 불룩, 작은 크기 선 보정, 모티프·배경 색면 - /dev/avatar-lab, check:avatar-presets·check:avatar-lipsync, avatar-lab E2E 19개 --- apps/web/e2e/avatar-lab.spec.ts | 316 ++++ apps/web/package.json | 2 + apps/web/public/avatar/v3/p1/body.webp | Bin 0 -> 130646 bytes apps/web/public/avatar/v3/p1/face-detail.webp | Bin 0 -> 35128 bytes apps/web/public/avatar/v3/p1/hair-front.webp | Bin 0 -> 28314 bytes apps/web/public/avatar/v3/p1/head.webp | Bin 0 -> 142134 bytes apps/web/public/avatar/v3/p1/jaw-detail.webp | Bin 0 -> 15488 bytes apps/web/public/avatar/v3/p1/jaw-head.webp | Bin 0 -> 24580 bytes apps/web/public/avatar/v3/p1/lip-lower.webp | Bin 0 -> 14630 bytes apps/web/public/avatar/v3/p1/lip-shadow.webp | Bin 0 -> 14868 bytes apps/web/public/avatar/v3/p1/lip-upper.webp | Bin 0 -> 15904 bytes .../public/avatar/v3/p1/motif/bud-closed.webp | Bin 0 -> 14576 bytes .../public/avatar/v3/p1/motif/bud-droop.webp | Bin 0 -> 14102 bytes .../public/avatar/v3/p1/motif/bud-half.webp | Bin 0 -> 14932 bytes .../public/avatar/v3/p1/motif/bud-open.webp | Bin 0 -> 18278 bytes .../avatar/v3/p1/motif/weather-cognitive.webp | Bin 0 -> 7790 bytes .../avatar/v3/p1/motif/weather-defensive.webp | Bin 0 -> 14886 bytes .../avatar/v3/p1/motif/weather-energy.webp | Bin 0 -> 7132 bytes .../avatar/v3/p1/motif/weather-negative.webp | Bin 0 -> 13262 bytes .../avatar/v3/p1/motif/weather-positive.webp | Bin 0 -> 7594 bytes apps/web/public/avatar/v3/p1/paper-grain.webp | Bin 0 -> 1428 bytes apps/web/scripts/check-avatar-lipsync.mjs | 288 +++ apps/web/scripts/check-avatar-presets.mjs | 267 +++ apps/web/src/App.tsx | 4 + .../src/components/avatar/engine/channels.ts | 138 ++ .../components/avatar/engine/clipCatalog.ts | 349 ++++ .../components/avatar/engine/clipPlayer.ts | 78 + .../src/components/avatar/engine/coSpeech.ts | 452 +++++ .../avatar/engine/demeanorDefaults.ts | 198 ++ .../src/components/avatar/engine/engine.ts | 596 ++++++ .../avatar/engine/expressionPresets.ts | 68 + .../src/components/avatar/engine/lipsync.ts | 681 +++++++ .../components/avatar/engine/performance.ts | 203 +++ apps/web/src/components/avatar/engine/rng.ts | 47 + .../avatar/engine/speechEnvelope.ts | 106 ++ .../avatar/engine/stageDirectionLexicon.ts | 69 + .../src/components/avatar/v3/DebugFace.tsx | 215 +++ .../components/avatar/v3/LinocutAvatar.tsx | 1089 +++++++++++ .../src/components/avatar/v3/LinocutMotif.tsx | 100 + .../src/components/avatar/v3/debug-face.css | 83 + .../components/avatar/v3/linocut-avatar.css | 83 + .../components/avatar/v3/linocut-motif.css | 94 + .../components/avatar/v3/linocutGeometry.ts | 1622 +++++++++++++++++ .../src/components/avatar/v3/linocutRig.ts | 138 ++ .../src/components/avatar/v3/rigs/p1Rig.ts | 104 ++ apps/web/src/pages/AvatarLab.tsx | 723 ++++++++ apps/web/src/pages/avatar-lab.css | 197 ++ 47 files changed, 8310 insertions(+) create mode 100644 apps/web/e2e/avatar-lab.spec.ts create mode 100644 apps/web/public/avatar/v3/p1/body.webp create mode 100644 apps/web/public/avatar/v3/p1/face-detail.webp create mode 100644 apps/web/public/avatar/v3/p1/hair-front.webp create mode 100644 apps/web/public/avatar/v3/p1/head.webp create mode 100644 apps/web/public/avatar/v3/p1/jaw-detail.webp create mode 100644 apps/web/public/avatar/v3/p1/jaw-head.webp create mode 100644 apps/web/public/avatar/v3/p1/lip-lower.webp create mode 100644 apps/web/public/avatar/v3/p1/lip-shadow.webp create mode 100644 apps/web/public/avatar/v3/p1/lip-upper.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/bud-closed.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/bud-droop.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/bud-half.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/bud-open.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/weather-cognitive.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/weather-defensive.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/weather-energy.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/weather-negative.webp create mode 100644 apps/web/public/avatar/v3/p1/motif/weather-positive.webp create mode 100644 apps/web/public/avatar/v3/p1/paper-grain.webp create mode 100644 apps/web/scripts/check-avatar-lipsync.mjs create mode 100644 apps/web/scripts/check-avatar-presets.mjs create mode 100644 apps/web/src/components/avatar/engine/channels.ts create mode 100644 apps/web/src/components/avatar/engine/clipCatalog.ts create mode 100644 apps/web/src/components/avatar/engine/clipPlayer.ts create mode 100644 apps/web/src/components/avatar/engine/coSpeech.ts create mode 100644 apps/web/src/components/avatar/engine/demeanorDefaults.ts create mode 100644 apps/web/src/components/avatar/engine/engine.ts create mode 100644 apps/web/src/components/avatar/engine/expressionPresets.ts create mode 100644 apps/web/src/components/avatar/engine/lipsync.ts create mode 100644 apps/web/src/components/avatar/engine/performance.ts create mode 100644 apps/web/src/components/avatar/engine/rng.ts create mode 100644 apps/web/src/components/avatar/engine/speechEnvelope.ts create mode 100644 apps/web/src/components/avatar/engine/stageDirectionLexicon.ts create mode 100644 apps/web/src/components/avatar/v3/DebugFace.tsx create mode 100644 apps/web/src/components/avatar/v3/LinocutAvatar.tsx create mode 100644 apps/web/src/components/avatar/v3/LinocutMotif.tsx create mode 100644 apps/web/src/components/avatar/v3/debug-face.css create mode 100644 apps/web/src/components/avatar/v3/linocut-avatar.css create mode 100644 apps/web/src/components/avatar/v3/linocut-motif.css create mode 100644 apps/web/src/components/avatar/v3/linocutGeometry.ts create mode 100644 apps/web/src/components/avatar/v3/linocutRig.ts create mode 100644 apps/web/src/components/avatar/v3/rigs/p1Rig.ts create mode 100644 apps/web/src/pages/AvatarLab.tsx create mode 100644 apps/web/src/pages/avatar-lab.css diff --git a/apps/web/e2e/avatar-lab.spec.ts b/apps/web/e2e/avatar-lab.spec.ts new file mode 100644 index 0000000..7aefafe --- /dev/null +++ b/apps/web/e2e/avatar-lab.spec.ts @@ -0,0 +1,316 @@ +/* ===================================================================== + avatar-lab.spec.ts — 아바타 v3 표현 엔진 dev Lab(/dev/avatar-lab) 검증 스펙. + 무인증 dev 페이지라 auth mock이 필요 없다(AvatarPreview 패턴과 동일). + ===================================================================== */ + +import { expect, test } from "@playwright/test"; + +function channelCell(page: import("@playwright/test").Page, channel: string) { + return page.locator(`tr[data-channel="${channel}"] td[data-value]`); +} + +test.describe("avatar v3 lab", () => { + /* 이 페이지는 무인증 dev 라우트지만, AuthProvider가 마운트 시 항상 /auth/me를 + 호출한다. 실 백엔드에 세션이 없으면 401이 그대로 콘솔 에러로 찍혀 + "콘솔 error 0" 판정을 이 페이지와 무관한 원인으로 깨뜨린다 + (avatar-expression-lab.spec.ts / admin-affect.spec.ts와 같은 mock 패턴). */ + test.beforeEach(async ({ page }) => { + await page.route("**/api/auth/me", (route) => + route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + user_id: "avatar-lab-learner", + email: "learner@hs.ac.kr", + display_name: "Avatar Lab Learner", + role: "learner", + cohort_ids: [], + onboarding_completed_at: Math.floor(Date.now() / 1000), + }), + }), + ); + }); + + + test("콘솔 에러 없이 로드된다", async ({ page }) => { + const errors: string[] = []; + page.on("console", (msg) => { + if (msg.type() === "error") errors.push(msg.text()); + }); + + await page.goto("/dev/avatar-lab?seed=1"); + await expect(page.locator('[data-avatar-lab="true"]')).toBeVisible(); + await page.waitForTimeout(300); + + expect(errors).toEqual([]); + }); + + test("표정 sad 강도 1 선택 후 채널이 뚜렷해진다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + await page.locator('[data-testid="expression-sad"]').click(); + const intensitySlider = page.locator(".al__intensity input[type='range']"); + await intensitySlider.focus(); + await intensitySlider.press("End"); + + const browLAngleCell = channelCell(page, "browLAngle"); + const mouthFormCell = channelCell(page, "mouthForm"); + + await expect.poll(async () => Number(await browLAngleCell.innerText()), { timeout: 3000 }).toBeGreaterThan(0.3); + await expect.poll(async () => Number(await mouthFormCell.innerText()), { timeout: 3000 }).toBeLessThan(-0.2); + }); + + test("nod_small 클릭 시 headPitch가 일시적으로 낮아진다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + const headPitchCell = channelCell(page, "headPitch"); + const before = Number(await headPitchCell.innerText()); + + await page.locator('[data-testid="clip-nod_small"]').click(); + + await expect + .poll(async () => before - Number(await headPitchCell.innerText()), { timeout: 1500, intervals: [50] }) + .toBeGreaterThanOrEqual(0.05); + }); + + test("괄호 지문이 cue로 파싱되고 미대응 지문이 표시된다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + await page.locator('[data-testid="speech-text"]').fill("(한숨) 그냥요. (작은 목소리로)"); + await page.locator('[data-testid="play-speech"]').click(); + + const firstCue = page.locator('[data-testid="parsed-cues"] li').first(); + await expect(firstCue).toHaveAttribute("data-clip", "sigh"); + await expect(firstCue).toHaveAttribute("data-anchor", "pre"); + await expect(firstCue).toHaveAttribute("data-source", "direction"); + + await expect(page.locator('[data-testid="unmatched-directions"] li')).toHaveText("작은 목소리로"); + }); + + test("누설 테스트 시 browLAngle이 일시적으로 커졌다가 다시 작아진다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + const browLAngleCell = channelCell(page, "browLAngle"); + const before = Number(await browLAngleCell.innerText()); + + await page.locator('[data-testid="play-leak-test"]').click(); + + await expect + .poll(async () => Number(await browLAngleCell.innerText()) - before, { timeout: 1500, intervals: [50] }) + .toBeGreaterThanOrEqual(0.15); + + await page.waitForTimeout(1500); + const after = Number(await browLAngleCell.innerText()); + expect(after - before).toBeLessThan(0.15); + }); + + test("reduced motion 토글 시 반응 클립이 비활성화된다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + await page.locator('[data-testid="reduced-motion-toggle"]').click(); + await page.waitForTimeout(150); + await expect(page.locator('[data-testid="active-clips"] li')).toHaveCount(0); + + await page.locator('[data-testid="clip-nod_small"]').click(); + await page.waitForTimeout(200); + await expect(page.locator('[data-testid="active-clips"] li')).toHaveCount(0); + }); + + /* 발화층(립싱크) — 결정문 §5.4. 비짐별 입 모양이 채널에 실제로 합성되는지 채널 표로 확인한다. */ + + test("'우우우우' 발화 중 mouthRound이 커진다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + await page.locator('[data-testid="speech-text"]').fill("우우우우"); + await page.locator('[data-testid="play-speech"]').click(); + + const mouthRoundCell = channelCell(page, "mouthRound"); + await expect + .poll(async () => Number(await mouthRoundCell.innerText()), { timeout: 3000, intervals: [50] }) + .toBeGreaterThan(0.4); + }); + + test("'아아아아' 발화 중 mouthOpen이 커진다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + await page.locator('[data-testid="speech-text"]').fill("아아아아"); + await page.locator('[data-testid="play-speech"]').click(); + + const mouthOpenCell = channelCell(page, "mouthOpen"); + await expect + .poll(async () => Number(await mouthOpenCell.innerText()), { timeout: 3000, intervals: [50] }) + .toBeGreaterThan(0.3); + }); + + test("'음 음 음' 발화 중 mouthPress가 커진다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + await page.locator('[data-testid="speech-text"]').fill("음 음 음"); + await page.locator('[data-testid="play-speech"]').click(); + + const mouthPressCell = channelCell(page, "mouthPress"); + await expect + .poll(async () => Number(await mouthPressCell.innerText()), { timeout: 3000, intervals: [50] }) + .toBeGreaterThan(0.2); + }); + + /* 발화 동반층(§5.5) — 말할 때 머리·몸·눈썹·시선·깜빡임. */ + + test("'정말 그렇게 생각하세요?' 발화 중 headRoll 절댓값이 0.03을 넘는 순간이 있다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + await page.locator('[data-testid="speech-text"]').fill("정말 그렇게 생각하세요?"); + await page.locator('[data-testid="play-speech"]').click(); + + const headRollCell = channelCell(page, "headRoll"); + await expect + .poll(async () => Math.abs(Number(await headRollCell.innerText())), { timeout: 4000, intervals: [50] }) + .toBeGreaterThan(0.03); + }); + + test("평서문 발화 중 headPitch가 -0.05 아래로 내려가는 순간이 있다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + + await page.locator('[data-testid="speech-text"]').fill("그냥요."); + await page.locator('[data-testid="play-speech"]').click(); + + const headPitchCell = channelCell(page, "headPitch"); + await expect + .poll(async () => Number(await headPitchCell.innerText()), { timeout: 4000, intervals: [50] }) + .toBeLessThan(-0.05); + }); + + test("reduced motion에서는 발화 중 headYaw가 기본 자세에서 벗어나지 않는다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + const headYawCell = channelCell(page, "headYaw"); + const baseline = Number(await headYawCell.innerText()); + + await page.locator('[data-testid="reduced-motion-toggle"]').click(); + await page.locator('[data-testid="speech-text"]').fill("정말 그렇게 생각하세요?"); + await page.locator('[data-testid="play-speech"]').click(); + + await page.waitForTimeout(500); + expect(Number(await headYawCell.innerText())).toBeCloseTo(baseline, 2); + await page.waitForTimeout(800); + expect(Number(await headYawCell.innerText())).toBeCloseTo(baseline, 2); + }); + + /* 2단계-B — 리노컷 렌더러(§8.4). 기본 렌더러는 linocut(P1)이라 위 6개는 그대로 + 통과한다(채널 미터·클립·지문 파서는 렌더러와 무관하게 동작한다). */ + + test("linocut 메인 루트가 ready가 되고 레이어 이미지 요청이 전부 200이다", async ({ page }) => { + const imageStatuses: number[] = []; + page.on("response", (res) => { + if (/\/avatar\/v3\/p1\/.*\.webp$/.test(res.url())) imageStatuses.push(res.status()); + }); + + await page.goto("/dev/avatar-lab?seed=1"); + const mainSvg = page.locator('.al__linocut-main svg[data-render-mode="linocut-v3"]'); + await expect(mainSvg).toHaveAttribute("data-load-state", "ready", { timeout: 5000 }); + + expect(imageStatuses.length).toBeGreaterThanOrEqual(3); + expect(imageStatuses.every((s) => s === 200)).toBe(true); + }); + + test("joy 전환 후 mouth-line d가 바뀌고 grief에서 tear 부위가 보인다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + const mainSvg = page.locator('.al__linocut-main svg[data-render-mode="linocut-v3"]'); + await expect(mainSvg).toHaveAttribute("data-load-state", "ready", { timeout: 5000 }); + + const mouthLine = mainSvg.locator('[data-part="mouth-line"]'); + const neutralD = await mouthLine.getAttribute("d"); + + await page.locator('[data-testid="expression-joy"]').click(); + const intensitySlider = page.locator(".al__intensity input[type='range']"); + await intensitySlider.focus(); + await intensitySlider.press("End"); + await expect.poll(async () => mouthLine.getAttribute("d"), { timeout: 2000 }).not.toBe(neutralD); + + await page.locator('[data-testid="expression-grief"]').click(); + await expect + .poll(async () => Number(await mainSvg.locator('[data-part="tear-left"]').getAttribute("opacity")), { + timeout: 2000, + }) + .toBeGreaterThan(0); + }); + + test("sad 선택 시 배경·모티프 그룹이 negative가 된다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + const mainSvg = page.locator('.al__linocut-main svg[data-render-mode="linocut-v3"]'); + await expect(mainSvg).toHaveAttribute("data-load-state", "ready", { timeout: 5000 }); + + await page.locator('[data-testid="expression-sad"]').click(); + await expect(mainSvg).toHaveAttribute("data-backdrop-group", "negative"); + await expect(page.locator('[data-motif="bud-weather"]')).toHaveAttribute("data-motif-weather", "negative"); + }); + + test("피드백 모드에 따라 모티프 봉오리 개방도가 바뀐다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + const motif = page.locator('[data-motif="bud-weather"]'); + const feedbackSelect = page.locator('[data-testid="feedback-mode"]'); + + await feedbackSelect.selectOption("immersive"); + await expect(motif).toHaveAttribute("data-motif-bud", "static"); + + await feedbackSelect.selectOption("coached"); + const opennessSlider = page.locator('[data-testid="openness-slider"]'); + await opennessSlider.focus(); + await opennessSlider.press("End"); + await expect(motif).toHaveAttribute("data-motif-bud", "1.00"); + }); + + test("공연을 연달아 재생하면 이전 공연의 미발동 cue가 취소된다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + await page.locator('[data-testid="pause-toggle"]').click(); + + await page.locator('[data-testid="speech-text"]').fill("(한숨) 첫 번째 문장이다"); + await page.locator('[data-testid="play-speech"]').click(); + await page.waitForTimeout(150); + const firstScheduled = await page.locator('[data-testid="scheduled-cues"] li').allTextContents(); + expect(firstScheduled.some((t) => t.includes("sigh"))).toBe(true); + + await page.locator('[data-testid="speech-text"]').fill("(시선 피함) 두 번째는 다른 지문이다"); + await page.locator('[data-testid="play-speech"]').click(); + await page.waitForTimeout(150); + const secondScheduled = await page.locator('[data-testid="scheduled-cues"] li').allTextContents(); + + expect(secondScheduled.some((t) => t.includes("sigh"))).toBe(false); + expect(secondScheduled.some((t) => t.includes("look_away_side"))).toBe(true); + }); + + test("reduced motion에서도 linocut 루트는 ready를 유지하고 모티프 애니메이션이 멈춘다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + const mainSvg = page.locator('.al__linocut-main svg[data-render-mode="linocut-v3"]'); + await expect(mainSvg).toHaveAttribute("data-load-state", "ready", { timeout: 5000 }); + + await page.locator('[data-testid="expression-sad"]').click(); + await expect(page.locator('[data-motif="bud-weather"]')).toHaveAttribute("data-motif-weather", "negative"); + + await page.locator('[data-testid="reduced-motion-toggle"]').click(); + await page.waitForTimeout(200); + + await expect(mainSvg).toHaveAttribute("data-load-state", "ready"); + const rainSprite = page.locator('[data-motif="bud-weather"] .linocut-motif__weather-negative').first(); + const animationName = await rainSprite.evaluate((el) => getComputedStyle(el).animationName); + expect(animationName === "none" || animationName === "").toBe(true); + }); + + test("sad + immersive는 봉오리가 숙이고, coached + 개방도 0.8 + sad는 곧게 선다", async ({ page }) => { + await page.goto("/dev/avatar-lab?seed=1"); + const motif = page.locator('[data-motif="bud-weather"]'); + const feedbackSelect = page.locator('[data-testid="feedback-mode"]'); + + await feedbackSelect.selectOption("immersive"); + await page.locator('[data-testid="expression-sad"]').click(); + await expect(motif).toHaveAttribute("data-motif-pose", "droop"); + + await feedbackSelect.selectOption("coached"); + const opennessSlider = page.locator('[data-testid="openness-slider"]'); + await opennessSlider.evaluate((el) => { + const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")!.set!; + setter.call(el, "0.8"); + el.dispatchEvent(new Event("input", { bubbles: true })); + }); + await expect(motif).toHaveAttribute("data-motif-pose", "upright"); + }); +}); diff --git a/apps/web/package.json b/apps/web/package.json index ce0ed7e..aef1bfe 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,6 +17,8 @@ "check:dead-code": "knip --include files,dependencies,unlisted,unresolved,binaries --treat-config-hints-as-errors", "check:duplication": "jscpd src ../api/app --min-lines 8 --min-tokens 80 --ignore **/test_*.py,**/*_test.py,**/api.gen.ts,**/*.css,**/*.d.ts --threshold 0.05 --reporters console,threshold --no-tips", "generate:live2d-assets": "node scripts/generate-live2d-assets.mjs", + "check:avatar-presets": "node scripts/check-avatar-presets.mjs", + "check:avatar-lipsync": "node scripts/check-avatar-lipsync.mjs", "preview": "vite preview", "typecheck": "tsc -b", "lint": "npm run check:cosmetic-filter-safety && tsc -b", diff --git a/apps/web/public/avatar/v3/p1/body.webp b/apps/web/public/avatar/v3/p1/body.webp new file mode 100644 index 0000000000000000000000000000000000000000..5c49c593279e9bb44eefdd76e861f82d6e640338 GIT binary patch literal 130646 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 z%0D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 Pj7yddOr?u2ojL#jrb9cn literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/face-detail.webp b/apps/web/public/avatar/v3/p1/face-detail.webp new file mode 100644 index 0000000000000000000000000000000000000000..057708c380681b9a2419395633c2edc065c0c3f2 GIT binary patch literal 35128 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 literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/hair-front.webp b/apps/web/public/avatar/v3/p1/hair-front.webp new file mode 100644 index 0000000000000000000000000000000000000000..b85a4ee429afa80ead89805172291bbf025ddb31 GIT binary patch literal 28314 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 SQnCQHYlv}Rca@0700028$4KP> literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/head.webp b/apps/web/public/avatar/v3/p1/head.webp new file mode 100644 index 0000000000000000000000000000000000000000..346f41bd485afbd16065a5be7bfdb82e5d72d182 GIT binary patch literal 142134 zcmV)SK(fD5Nk&F4D*^ylMM6+kP&il$0000G0000`0{|EV06|PpNT^c)00EDMpl#bc z9)HyPk0B!R^w%v_($dnUR8gTqHx(^ns1U@!!1!0FBuT0C=P+p7NK(`CM}70`?h%NH z32+H2(fR0?ww*`}2PL3RL$_Lbxsc6tBWjlu+i7Y66cSs_FR*`4BhIV#64Y@}rwc%m z_g8E;?*qT_oK|I%5>oJi1~PXTB>py@r@T^op0UqQJTCwLN^1Xq_nBRFc5N$NDhAD4 zDTo;KEk@oJq#%L@Gzf(jp-_S`c8$iakytQxO=6B7iM86Gl1BF=(TXFyC? z$2hZAbX9jn5&fS4?A!LKjpXY0>m2a!x0_^oKAo!_=&FFK1Kk_!s({ML*ntTrJqzsx zFVpZc3{$cgre)sA(!778WLfPlca7(KiRk|Xuwgg!pVQ8+|D62)Q0@Hh$^Y;F|F8A9 znV$pnj{C33R@}4o<*VN>9b7W~KO7C3_Ll1tYwGD&=if9rpR)kH3fJNN%L9j{&Rzqi1v46V3GsK0WNSC> z{yh_h%N*C|;HMl80nAs3SEd7)AdNQQ2>;4EqtmMa;{A7bD|5$w3$A1WSSd-PiFe2( z^X-wt?i>H!=Z(~;R+m(tx#jdrp!dm*GY3{qavUV8CLT9nrH@dOwh--;B>cVBzgL=O z0l#)9Lc_QGMw35bFI1Bk7?4Sl_7FWUF$8A)fh0{afNROmH~kFer~6hmN2(i4i~)Qp zX(K~zG6N`V^agM`-5Iv*H~yt{&BA~3^ZKmM3~5@x3zwvk}7 zvdc@QZb%kjgi|hffE_8sJ~G}@lu9lsLM%M#fhV=pfs9>}RHw>IvN+|Dblef942$rO zm()S!mt=rX(zu@;JWdw&;U7oKP!?GvjgP^wG+{a|&rO-6g)vAGdT4>($R=%w@mpf> z87(K5|PF?P1o=6@ppxcB6 zq<#z9TE7t;kXfgtd@+-jMrdDsPV}DaV>s0cf=5}T2xHn*M2{)jOI)H={4tCAEhe|4 z>e#Ck$0^ZD&Y(jU%iB*EF(=E3Q?!`h`ZUYpSi9+4!U^&cA4Us$jY}OCzqXUMA_05k zrq$i;k$@cs;8XhuTYg&LlRN?@0H4}KHNsx``)Q4Pgx%K`dQ14Z+`DL*?{>)I&-PCj zru?+TU#t(nA&ZCYp2dVaMCAhfwaLw9eOQX8^k*0v2C@AKE#D=i~+$ z-YSE&GN8wr4a@|NwQm6alKU!vCb!OD_a*}jt-@Z_rkMk9Pi}yj60P-1I}EM{I%Cn< zmgxsLE$?LjNwnB*B1!t3R=>2IP>OuN1NfyK@)D2}pi`30(E8sl1xOYj0D9UW zz&OW0fWltdf>Hpo?DhvRTJcAKLfVE>faJN{?$`tq6yF0Vq>U(b5X!;Q_QvOMUh$ED zB-LpzN&!-gw#IxwPC$}=q1`A2pcrk8^MIUyq~o+9B?Txpp>44Vs5tMK+#UxrW0f#*xhyj7DwLEPSsdT&~A7H$lOMoRl@=Vv=>GJGHKWD5`5QI zAks_wRwtmH&;>}kL0ebwMH`_Xmgy*M-cN!y0wCJMZ3EiKoBn%3o1mVd{vVLf3BO+( zY5TSV1_+4q9$cfmKAMrNBno z!8JMfy0r!jTMckRK?fk(!yO*5v|s4gmjKdUkO0vx?r?F$&W=cLf!mS*0UFjeqQk|Q zjs3n{5-=c1z(PQxLeo20Jqv@~63`(@8!;u?*yeQ*7;zVnNrblcJ+=j`S^!*-`Hk^Z zw7F-su*L!74*{cOV~V!-!Iss2;1htNKN+30!GCH|J;i>iIN?;<;(aZtpRvDCoKUpM zKeV8xV9R+icuU*-QS0f0CFgKj{*qg`&~j36Q(oHdM_NvAVUxVH<6pO$V79z_tD8nv zE;*oe#6{`O6Zxs?qp9E{<)NcR1f1R9ke{kPfl4lUn9>p=+$TS6b{}7~ej1qaKWMjQ z-O%dk#*}|6ZFl)N*y6#Ieq`PkLc0i6bAr2WUr77lE?5A_VR=aqrh%>cCvP?w)} zoifm>0SP+gztFBD6RjCQut@%SwC|LKR?KfO9(t9LHh#7_t(UF90Z>NT`pQD<1ydPG z3uv`qDkH$yHXh1EYX$R0hHHRoZ9M^U@n=IhZppurfVO^sTzp|Cn94{3CS7e+Gmdazi2!e7+xLxx-ro5+l-dJY%mCwwZB03>-Z5ls1egWp90C`%p;1XKP! zgd!C{l039LW*PD?Arxt#BJ$AcARO>5B|JtdI1>&A^3dA&){uJ!iJ<^vzUNp=gK&Y5 z_{2njrLZW_O9!ot-G}^>*3bwh90U{KYTflaK z^|OPP1);pd01=8xwq=epN4*wvS`K6&fwX>xF%en~!ve)gAr~0} z6uI4Ec)bHI#hxS)ST*=b#zTg+6xIMJx){Hbg}R|jpgBR`Cbbj+9>?&DbkP!^fQhXK zMe&RDVJuJ!sB3oX;EoHwXeB=19IymjcpPlmaj$g%Ap59K<5~(EWZe>=fN7j+TOx^!%mAiDXb~vHJyE2Qot6N-kKC$$ z&mt>ax0Ff{nFT09^&^5?B9%O4DcBWR+zPlN3Ac2ueZ$%i>4dKobcn0107NhvoqN0_ik4mIuRwql zqeXC1MYI!d4}K2rR=$ZDB#A_`CP}bOicPI*i+qywIMu*HdL2WYLZK)@(TB%uPNb%23EHQHz>rex&e z96++pffiRMLTMjN>xM&;NJIr3&;Wcc3Dzj+s6vDt5+Fd*=v0JZfCLyAR3O6riuFq( z89n|0Ub-e$qceu2iBJyS}J4^>e^;h1e3w%FEy?P-(`}3n(06 zozdKMWjM^10ICe3%w3j5qIy*l+^Rf;bFJTjb)Yq=3I@^u(5NhX4B5b0fk;*dA`PIw zvJg(SCIOmE(kg8W1W;87U&|)AnYJKRoiT>&4ub?{lXMY&s3?SrlEffDnhvU_CxUIh zxn+o*XZEQ;aJ`xkD*7Z)8QOylE>w}nf#Mj4%M$xehjap{A^?hy4)%$X%+7ON8eUD7 z0gD{ZgJf1xHMt58d);Rh13+G={g~XC+p5D)K+UriM9Xm1)~F6Zue`Jp)BJY^fLayS{-`Q6fbY|t zhOeV40LZ;2t;wj0I13o*Be9k1Kt%w4mHS=FLp7KObogiyp0+dO+FuC(yn3`OdDgc1oq_{rH-!fpGYj4YE+GgaU^a03C>oYf9~Tx1k2)lM_I2Bi)_UNWl1;&t1O9J7Ivci7XL zi5BZmTB1$xFcLPFHvl;&kWnp{I!{Th>iYR5R0T3hv|P`bj(;Xl)&Q0{X#IRi#ixuAl6 zEMov8W2XS&l&Ip1-xK+ZrZlCbDGn?f=c0-a_YwUWQ=)v$0mj7uUg8v0d~^~C2g-T? zioO6u#UDuxh;YG_C}WpmfGS>gZg&O|r`xS)Trj$19RL(1pwh1`v(P33KUq;@DnQ91 zi)K{1rG3)<2zjD6utg(5<*X3_{uEVyZXdc4^Phnw&7=WPs+Iw=7XW%z<>t2#^@CJU ztQZL@UBJxd+ty1DN zUbLL33;~pFgTvwg;7NFpy3i3j_?5r5?wN zJXEc!lLPW_HXxeM*vEYdK&yeW^cH@%s2ntP4Fewwv>wz@hkYO&3j}OZ5>PY9Q=&?D zR#sdUDDa6c6A^%B0D4k5Cl6Ju=Bkr8aJU$Huh_>aX@4N#sEQ88$WaoN`nx)u=oQo2 zU>R!-Bmo4d2v`%4qpDWlv6LH{i|HS+jJjh>P%lxE)X0fSeRKq=M~f(Wx!E$h{@^9B zN)mv8w4%sS5|z5|JH*}%hE~e}Isqi@0+tC-&;eL0>3Tj?>sJosVknp%<4{BZ!Dspb zB#9g)s&&s%1WyN(v5wCo2Y}Oy04>o1s6zC)Kp3!%Z35Ow5`chLbhIiTDz+zEz_Yf2 z-U7ikfJs0*-@=~~t?$u5JnQI*D9C+OIlvL!&rQ{5#~qd{#m-02PebG_s_iT+r5tk0 zZZ-#(1C=LH(UxI>4R;hfQN)OC5MX>8c}_JQz(iNKOBH7~!`@&flkRp?u`4B7Gzdkq zs+k3U;r%Ed#meg3fr)iUrX02J&pP$9W0ZwzR+3RJ70v2;E6@e|9XPr^6vI|9c_A>@ zP~;)aoX#M9bWv@G$-6w}D!&Z@uxtd^JAj`pc z`oS+PaE+=qyPorsg_sV25gWjl$k(kTNw}!C!`@0B6&?>_&mzv|K}X7o2uu7nwwebGsah4=Q;*vlZG>1tIXfMa zbjG7vwb?A@3mkvPW>%Flk??omMu3Y-z3VzFbsWuuQgBMriprHNaDel>jO7{40KL}S z0B@Zt>Wnifzx zv_FsuXrPkJQ}p`8>XZ*nj=-LIjPuE$OaOC)uO{rV)uM#mlXu2d(t?31m+|F718`4F z3eJLJsuZc>yw;)d+jA3rc~&%$`)lL)n5AJ*9=|8bMyJ)r zp)4vJ*pDTiNUDKE)mbHf&&}TPn|WGvk%81pwz10b&ge5vl!dtwNg!!}&tv1O%Wwg1 zj#QJI9n?kcQ?`Lah78IAwqhXFEm2id`0P28=MwsSz&LOXbJWYhMr%dzH_`~sNJotW z)n&*JGdamK3VFv$R>L$&R}IPm=sKYA04oHNFjZYjSesVJ;SJ;18QBG2NK%Qo&NTsx zjSgq64WiN4jf2P+uqU#uB!D&pvS9>QW2#W99P@G9NJhcQU=sWTTT8%oP6!`J z>IjaBmi$J#5R1k!*6M^yfq*4Y0zd#)B8MymYSCq``#5PFSPi%wyHo}~@y-|>5loSE z2v{Xr^XVoZ<2Et@ZW@;fmlq!FCRFrKkR$-L>JB=5a1hXe5zK;Dh75|1cnlX6a3#13 zjnM9=DWEfZaqq10B@C2{^BFp=AA#|!E5HH)55l2JYWS*9UqH22;vU9kz}jLgE4JrE zzl>~vHIe`}QF(j%M}J0}vpMHlj}#&k3{w*(u+brdV#7QMioOR$s^CIE6YagNBkK4t z=XA~AuN0~ah_P(U+ZC*af6BvY<1;)MhpR%sqrE*t738Kh@k-96%EpfiO$SAP%p=$Y ze@BiRUl$psGCY?=Ma%?~9B%LLtIwm)*2>nCTLnTmZUEcieMH9D!m9~hM;fq7YE*kG~CbQBMt}fq2jqDX53)F;w&4U<+q#P)! zMsQBr2!|p|KoV8B=JJpAf3DA&L8apbngQMl0icO6B#C#1pt+-{6*hxIg%%f)uu1!zWPs?1(@N1V z15bq`#%ps16<7_2T%T7bw7Zax_rW`28@F!pz8q`;o?0J$)p=Guz?q!;fyZnEI9mw2mvj9RUUO2VYo7cGfT6O-KHzQ+ zRPBBgJAK#B>DMvOEKh>lg);ys^02ZUv}$IRtPc|QF+9W8fWkQezsFX_2%lG|5i~o2 z@9XwK1{IS)W)CRZVN_||%tE&h+RG~IJXG!NLDk&NkoD6UuB^=szTL^H z6HG3?70^?yhEeW6qeH&|C{Up;1+HWRRH=5Bw6hQ)T4R*=T*18sl*bW`tWvAQ*{K+% z7&(k86f*Q0eIThq9T0CbCa|Z#RvA>M-aKXFGVxDjG&F)apo)}Am6uRXy}+iTnG=}} z3Ne1GLi3cX@Ci6tG>V>v>DB>BRN%uh+SMZIaRE~$9Rx|^tI#fL?iD737hA^WcfCkH zWEmj|`Xy0?5A@W(QKF@&?81IbLHXS+60%4d4k~-;&?veGVRzUq-9cQrRxo?A^c%Dn zXG_)=6}Ffy8I3XJr-D%y=xaZ$mn^zodUvN}={42$C_<(OMK@LIEpuCOq_k%uMV31y zzsn+Re%q+zKP6G8tvY=Sx+ja<4va&5iv*{oXd;ruX1k!y?ky?gZ1@_GkAj-UMejW zb-ujNEF5i*2TdOU?|(1XqRy0$ifRF{U7}4rztjLs#R2XGIBF9YT@>2)CF?e{ z_(&1nzdi?WAr@qFs%_$RX;}LxCzX`}@O%t}=;*sZns_*aIZ76#A@|bKSdGt4!rOUd z9=I42@Pz7)89x`-Kr<+(E4|lheDsCr`WM8>V?vE7j@aVJDOg-wJ4)A7PsK!`MnZfE zU^X_;Arozylhq#qI*R1`3~EAIUG1$Tl6P{<^IQzTQ!A1$O5W}E8-+%~bQcrtx)m!_ zq*F`YK;LAcP^WSZ&4V*~qu>Nxo&}|=r!8GzAyj#<+DEJAYMCAs%{~uhdw->kDDLO6 z&=$qX8W;PqXkIz$#_uI+aJV)SBRRbm(x|su7?HhTBjd|@lk2C#83wN+n6l(h znYkL7I~Zx-fY$I)I0CF^#t7_MY?~gHm%olh=1VJSo^4RxH0xSnVagJ5iPBo}MrTyE zvBlI{>5_vjSNz>plnku(MYX|Dwhv|I<0CauX*33=mrF)f_cecOTANxlYa&>fjq$NY znR($JFDmAkZONAG+ReHeUv#Sp&n8ge!a|24BCay?;e(jt&kmLe^mp)1wNmvuQQe6#(Osx16=&Pe`J1fNlZ+k-HWd}2gel@zOHE~DQgP;bRG4Zl zRe#GZe;ilS_tu_3Lwr4tE6}ZHxpkJB$fmK$&JuC4vxrSKp?ATLMUZO)j*0L)ValU& zbQrSfcrzUx=1Wx5R)nIZ0NpAi!E;M7d>cy8M9AjiO{6oWNmpu(2%IgZXdb^y+FgRi z!ZnFmc9e$8t?E+|Uv*##=VVD6%8y65N9}^{(rjH&+mYb6f>K0TYJeo&DnF~?I$Sdh zMoYqJt!-2Q-NlAaL@2Yz@`QX3%bXr9$(Yu9BP!-aS?qH3&nOt@KuI{Ru&n^rm4ez} zWeqNsofk6ZRN^xVlV@x}VUEq9V3?#vnR%TEuE(Qm!tk2E9UUzXehqdMnzDQaNYd^y zb2@5XFE*FBH5>sy=oDikE&pqQ`yi1f$#|DaKaX#;~2Ursv zC^5G!WLcE?>#o_TI=e@t6r*VF1LJhbpG48WW|#C$Y*n!UJk%bCJR{1^8~oS9h}ga2k?>&D0hOKK zhpyRRddr?-6q+K-%g~k3nXEE>r>WV6H5)jlx(90?EH-+|OS2cYMU$C!ILpUoMg1U+ zDyHjz^g|AhLFTsdaupL-7H+_hBX**Pw5^yhh%3@f?0k87$HrES%QDwQqa!4gUN}Y; z%yu94LC1&kQe|VPF9R27&Nr_ioCga=nh@)b0UIsK>$Z`hES zCP{X{=2gI|LXn2YG%^E7XRAW1B{eTfw}hYr+65N`>@SQ7#f?x zj?-3nAAC_5JK=^zwmX0VDnnhT?QGAqBbu<%LLi_pj)Ob`5a>zdVRfj&c8}l9GNh?7 zg+nkZhrURIsyNnUJRHa(eTN`>rCMxTK}<7#izrY)M3upQ58up^lFS<)5JZxKLjVPF z(jZ2F^^37I72xoj44!06B9ICMxSORjw!u&`uEcnKN>Ygp@A#rG8@2N634>!E@U|%Axm8)7F(M}I$>FLvBZwt{LRcHjvfc~W zm~CDan8J!L&wiKfia7FSsTBbMwgP3lRRs+B^CQ{L*6M0W;p~CeIgl1E?$31cs-9j0 zw~OJm2Tn@ORvCwZTI?^+r?Ph$qy8B<<4d>2)X8^DFO7wO;+w2)NhFFx04KEcdICHj8}iK- z=0#tp#UR@(tMLWUr=@`etocAbMSzJ8IckF_GrN{4r%k0%bOOemQFHtOD8oKriOvtA z{~pb>%kvOa<@*IP!U^=H5oQr^R3wszqTHR6=QVgG-)F%0@_mZU&h!)xB@?n{8{~Nk z5(R;zbWcQFnFQ=faj2E&jUuwLjbL|E21p|twfW`dz9&Ia6gBHv0fIM#7cKWTL4b1)uhn6>T7L)MZaDKRZFRTAPmF_vML3bW9E^bwgs^g%gz1t#XrE z%Jp8d8O=({Ypy4fooUMD#0b5XJexIp>-ltIbYXwR<>=*vlo z*;>yT!PZAe;y!pz3wC)>CE%Wwf-P!NSE5#fNrEZzOHP{-wOJs^w^N&oa#^$uHoNSbTybCT`5m^FThprb^#{wQmfU^KWo9V!=@vQGr-MA+}s5DtsGjZcWUCX%@`Qk7H>8bslyV-u~g;eWcYlx=k!EL2DKq={)*W7 zK!9T)_NiPPlz%FRL$Xwos+_3$ei^!k!;+2|hG0^R_?~i+F8_3lgkzqPwn@RzH3jC* z%z+#_%7s_{X}cMY68C!%>L`Y#uu{n=D5%Dq__ZHv$^M0lxh+K{y#o?;1h zTK6EIbEyb0R288}AMqU+vcAT)umsPokG`1+NqeQ6e)hc{vi1TOBG()O3!f5_BvGD> z(0d<4)-K>4^atZSFCo8UNrXOjZ@@JzF}p%IWc>qspwH-Z3GmcBNq5X1vVeWiZ^$QDV4MJyyiB>~ zQZQo(yAD_bJn_^xY4jv9uB=Py3N{DlhpJvoYYsFVs>@J}balxYizH15W(5pYU;6|b zJ<`vLhwEhI3!U1i+2!Bl|3bdXY&$tO3jPO+h5Giz0iZZ9~}GW-ZlrV|*GmT09KCkrZ^v zbjO;F$xS6}lGuY)wsBs&L_heiW>aY|Hc+ z>;&)#utvaY5z2m?vN+S|iLA;bn3m}{3^N4VGLW*)b+|;5P6LRwYH1b$v3$c5zeq_2uG zH;m8nluWll^MMQkEF~=izN$xLQYHm`j!K!1g9OaV&;gH)L!J@oiT}=iS2W*Aq1Kcvc0+8tdpx5v|i(WzJMC+zS@;M*O<#AU$h6_e* z=u#jclW-nLMFPHzD}V!r3SdemIW`&{iA$|ZmU~{7=_yPN2C^wO-8h)INbM3ZV2t&= zDbodbryW2J-I0UF+Z3$dkmp{RmID$nESpYQ7^IztO2GF7cv2?C$f3hHkc8ZImD`%^ z`3gu$_D4Q4WTzcoMh(Y3qX2|@3&S-Tn3GrCSe15UQ3LQkBEiSk1xWwof^Cp`%hBn3LM9oFG;RtY-|Lbok7JPu3E9s@@dTzz>tJ&hqR%DmNyGZ_ z{S$afsO|7Z5|E)gh9uIkaJ?e|ToS4VNP3j%0r1UW=}M_OGqeaLOEBLDY?(h;ls7_Ce* z+CQk2cFfDCf)bn#c7p{>3T~$_4$Pt=ArK726xQM8AQ=9igs&qtdvBo#Mh80pk}((U8xv?RdTom-erowGh> zY61Qz-bh1|mOw3_nIf5J+dpkoO^QC>?+#7)%>e zDS+;v<^dQK?ijGsxEeXrj9;u}DV2gnSrWnnNYYG*(9BKN#>j+}X}9%{F||AIlBt7$j4_VWz~3bUP{zn6kBIIA#s*5JaL5U8k6^n2Wu0QZ z&t(I!IJx?ePeO2DAd@WbMaKeQQ*u?4G~Lx0;(h^?y(g0#UjaW11`R_O0!cdO`W-kF z3{Z~ioA3;Hmr@bK1@MVnz>j&5^{&Sp1RV)cm`_6Nv494FG-j|VE!tizz}UgDBj_7M z-D#0B{Rr$AYc_mRo{NId9Y0A=0%ucd21!w0)9ACzk#TW!>aUQ#e ztb;TLhav+?xfW#5kUdO!uK~_&%;DbP7?u(#@8s!?&(_=~NGXwXY|!wm9-4Lziq7D+c%Y}jJP*0arG%(Ako^o$@dm>H z1swtrD#PlgDJ8-MG={1vO&A`W1ttX22x_^!$?bXK)Cp*a-mfO^2Hparu!Nn3+-4_9 z-#Zw>n)(j71$4#~``t032}+H+0;Y1svAKW%)&gsTDZzutamS=2sgaKi=rQgADyRcQ zWd={BFB~_U@mIQ|oQ@j3GJ(BC<@BtVIsh3oW3e?_QW_*F{786>3C~091JItwS*ZubX$$N`;q5-JY+DZ6J7PgcpnHi(GKr0s^H|NK#D2ywla2!>C{c`Oiq4&KJ}j*Mb}b zOjQ#e8b=Zx4FVN3Zb|C8Xbm{}n}N|8Q988HSX36oDdU;-1dy7g^hm~=((nS~duv?E zMQK<>XRzH8(5Iu#`ZgJQ19@^myoMp!^KYa6ah##D_1t1nd=9;wBIjK{%FcvVPs>!WY4FuMluahQ zgqg)7U@>+~p5cb_*%_G@yiRetMRq9*&{s_HEMtOE&3F@e6*N+uVR?|D7EmTeN&@R+ zwjU(ofl{1WKDcCbm56TRSL1ND+t~&Z95rK-^?UGri5MkmD;N{9A$^Sf8N6~xDNppg zrGUbz1uv9kSujbGV0)lk*2V}vdTI#}g|;PFU(Zqt<_5ok<0s!-$spB!*9^kBqsI8^Qg8>GO=sNAO7c6!?RgiPFR1azp_I^T{Lc1|Jgn$Wcpw zYMv>m>bA3 z(={@gAt!R7So(lQURWa6E5_s4pRSc@jy!vVj@X)DeW5%CWUTl7<&sYphGMw(CqD|7 z#1v2{&mALuGkn`z@!v9Sbeu_p4j0&)pcX9mDVQ74t2`JU+>Py5q)&I#95UT>Yz#&g z$*o{^us4TiG37!qB1UYU22!8#MxR5$`yyE!90_#KynW;ZLWLpUQP$)lw-UiAVEQ<`whhRhur95>3%1`iGg*poKCbxeg zQ-JStl4E3KSwhnpQ!d>;I8h{mr{qRt0eqtz{s|t%q9Vo3VsG`_0Vo(d(%0R<@d!TKB)*76{iG*`edeVKtwXaQ>)Oz+C&fuvmt(49b1`Pmh0 zFzV3Vo#j-3BqcyST}>Mu{7wNAgLj5t|0Rcl1IE-eNblgKq>mA0Q*hfMzvrfanAc&4Yp*dq`I)&Thd+Re zE(P~0KmkEdE?>Y$!3XI>>LcJ=-xBQu$ac~p-PZ6NR>c}w6zDo=F-S^*Mi_G}v?(&& zx9$*G>vB-6p6PucXy^qsUxOhBA4mF0$1HLAk73xxQm|gqPw+7HIp|Uo1RQnF=)C^Hd()UM zsSiLJ2CPecG>C9bT|h2%*FVM#NyFj(uhRs){n=QS?p)1pLgB5}xt!#(7f2GE(<*?E zB)|E6U3fbrxRHx42AhICo=2eZKtJ})gy+xP?q`^C(D8tLbPZS=$XFhXgpN1B@?hzG zl3v$s7a2Zw&`xOq{E*KuqIwXl3WT|NMxZ%q51z@fi`f#ujyuK#-$?@UQEbW5;47D2 z1HY>~@j2>HEX)CSDEPs;)|`_H_8Zp%31GXF_)t=iR0o=a@uRT^c&ZfvQg7lzc@c0b zkMFX)GwuT;M5HJ6`8_t!gdPWtl_Ve!vS0voIF$M@BXXe$uQ=#zgq&{XR0}jpswF=} z<{Q5lJ2NDJd);v-AW(iXM!k$Z3hrG4PD#?==?|6+8RYPLcZ~A9&+Cw81$KZ_l1PGm z@A7)d9r6gsbPlLVo^jI26baX|ydH4VTbP;!K)xLkz+s~!A;P1)Qf?BE!%0bp9dGn7 z(DguJZz4qVaLrfNukbpX!eCaw=KwVU)QvT%5Yo40;ZE?u63~S4MtDNCK<2n)2-V8M zq<}~VJa=`#S;yk*G3gO-*it(5P{A;*4Q3aad;(mzVYsmiIH`yvfSUC-9pXlS6ns-2 zuEGNkARBlXk+eUMw3$R$mThgzmU7OCh=AO9VH}gRFGz{II&xT*4VSDBp2vEC*l4)t zvL($UIaUtJIOiAz!~~p=+=LC0+m7QWNRj0`ox_K26EMyKmPAH7Qf0XWjLop)n!cd! z7zQ|2MU*@>6vMcX0mwS#>)^3Ncj05O38*DgNHKyN*=C$FD!?OPy(Hidna4??rPyQO zw`@aBQ8oc%3`wJ(lSoU7gm1DnI_@E$6)>Pa5^8n0-evo{TaE`?BWo_UBz3Pyw&dY23%J)!cDL;A6^Ry-2Vx25j`93&m!6i^0cT7l#}(c3 z*J`z{+5huD^ZqZTB7(Fp2oGIBo-4YQW35)Jbwv)FWw> z{?&p0?Fw}a8j%gD8Qj#FKx2Y_-42kXIr_JQ{(aNfBdabMNQ%*;X0V%ZrPK!?1xO8* zK+QuCF2x4xf-eC+*9HKULMvlW{9mFugtq~DC4e%ZP)8v-WN9!+-8dzH1tnk_goqHx zK+Qwp!w3@Le+J?{sc8h#Rg2Gc2%zxhAxQ?3n$~MbcMN{j0)V;2W@R+OP)yC@fF-RD zKwkr#E;5BWI!R4pLPTl=91Wnzrtt^@1sKXrqXN2O(ipt|EdV5Wlo#p?X*f>Iouh$ttd0G|}&X+b^xm!^Oz>% zc{T+Mb$A}pkOFd0Flwm7^O&aKQ%($dcpA`<<^b%=reOGxhlTl*;SA|0Ku!#O=qiq9 zEK=|?n}Xp(A6OaB__zchhgZPUBG5db*%z}3pb(~>xRlPMU7D4b1IXb)3+B(yrV>ei zc1Hf?vH;ms6rm^ju1KScDT}U{iK{o@KPGVpb`$_sI$~vk6R82faFB?>VALd9;hDH# zf#ViHHUUMTxg(-bDm4HY^agdqYkJ#f&>Ns>P^Q_)1>zsqm~>Ki75&fYub%gXjZZTR(d&h=%Wy(j&MmKuFCSVuoxHXS}&N0n3=^C zMNxdQC@awwW8G>jgl49c==WfZJ7NPZmncc8XC@+Mv2p8axn4|B7~!${xd?QbX9OEy zfXk0#p1SM;!|55%i}9!Zwcgf^XJ)2)Ru3R98Y45J0EU+#@UfbITz+vWvjBS645y~4 z*a!fF9^iE*NQwKA!K25AXDX*=MCgeq>5>v#X|_T=ntGW5Y>>x$zXJdf-G=kC04p5< z!>MA6i7#CQKKiJ);+!)bYF`7Ie~M>-%cP$^rl3@Z4jshWOwimDE?H7eRy$mC&RL=M zHK1v4t^BHyW#(lE|b(k<2QEK($9ZMY?7D@H>!4N5bx)BS4xP4`6_>VhUsp>83vj;)PC z?Q*4gP@0Jg?(zj`s05|7O^LumxVIMe%6r z7mX2`AujW>^Ig10AUt@~kG$8c=ywZ$i zgIo?uwVp3zmqe4w!dyDxr{R+%u7+k_nQApq!*yvuVhSTPb6hS3rLmrCt=VZ<$#Wk7 zmc=3UO}tXQcxv3`2E@!Xb0ua^bP<48YUrsZ=l|qmG69}Cp%i&YOyOdsv_kdVrTTqD z(V1OcX)ue~B6CYCET%9*jqPYOuZ6kv0L(q|5HpJ@3L{MQ%wn8#tNs4B;;M5iCA`iu zDS=_WL}!GFh?!|Z(ZG_$(gB4;Rc2-y+c8DqoLlJuz&_M^on35z-Zj}0l$mz)^3-}G zl#-}hE7ZIHsn(Bey&SOC%a!c3zs1seFq}MF?)K^1f2j4SwUQF(Ayc!{gYwjL0E*=4 zSnvLiT2nZ%(rkf#KU0d+i58~0w46&Gw%o1OK16zl!Y!!-Ke4FfI#n}K(OW+k`=&ard?S?KV>~MPXZxH4ArsS+T7l5?@KFX1)H?Y)V zhbyH}x^>O@;_`e}=3uP5lql69Q~LwY4OZ(oU6O?bAh|r%C7Q&#vjLpOxe1;dtw@)J zX^Kzds|8XpJDOO1E&yvWKf%kk;0a*<$^6zQe}^E^fKsB=VxFSdW+qjZIR68egJ?iD zK(XNFDcI6jqe>J?vyK2Ib2%AJPyj~bTt%_51{KGN!V>)P*|R@h$W9YViBb&!yfin# zN@EQG6v@I9l;3ckoF-%o6bl7_cR!q;Xl^Sc4o_Sx(P%>{0khx$oWUF=G}bMN!_!%I z(S~e*QX~Sed^AVFQd?D%h9^i)E3yOJf(38_^AtR{)d+A&!$L5cQA)sQ3A~6*oqGk( ztrazCNHilGpi~RsZ@E&eSMc0YRWC_vNp^tK61aqH4eAvxOnn*`_q=&fKra-)|f8UbC-A1P7lL~#|sS}z}p z(ZQOV2q=ZJmkr>qicQ}CBk(x~ppSf<)kRYy*@X;pz-mC#cRu~kcva=688P)yXra;G z{#Q*Wdn5t_G=BT5=iH~h3ZOq{!BPX&8qORx0EW%}YC=JKfUpW+G*7{EBf0Exzv{9*ulUJ|_!$j2S3h^=- zhinYpq`7_22krv8oGon8@P&W<$8|eYWg=N8vyu(P=%&y>djV_|9m-aXO)mV=3)5IO z@VjF+%<3jAwa+cMAF!M)8Zh=l;EAZzP)e+R2iZ7v6rP(0zy{VOS2SS!3&4Yx8?Xg7 zF_*z}^K2ppTH-trkA^P;rBFkSs2-lj;JJ0K3AD%W$`ctheD^)Tasv&>%g(~{g;H8& z&?6$Rc~Ufa42VMG#iM$7UW2E)%)u;7(_9(^*w-iWk|n0n>s9Un3LeJ>x!GOV(Ik*0 zi%Wx`7l{mK2;zbGx&PY$-o5>ISZ-3xeGs>lg}F4wCa~gZIZH^4D-nUdGrPJSYVbwe zGVi&gJCvvVDc}~sEif_7uK>?YG3WtaIa5eH02rp`Tp%|$06ez@a5vyJ zS4cfj3eCUp)D*ZI@RBQ}9){;(SeWM04!9dIoGT>V4$Z}o|_?49^XKeE@z*OPot2alVD;1{m~!XghSJ7@pS;E1xUjHJjKXJLjzd&zJtaVsUo_ zyylXW`LWp6faeRV9s$5>9#dG$7d{eagjk}^XUV|(s!ei@yOF;Y@1%z4DiYs!v=YposCm*-4Ly0%rb7sk?d@qmevjM znl04%)LLtQPgo~;+1WrnuN;DA3u$cYZ%8<$E<2m3=aoayTw#k@d?{_&*+@OF9D-(w z#+j7%>};lHTev$LUkSvf>2`Jn;r_Xa5=N<}Gm%g&anxORY7&I}D`KV0R~ zHcUa8gISp7a;xNQtG28jq7~+h(17-Xa?)=4&f4}{qDdgh!Zeo)OEflC#q|TCGs2t+ z8qj`JmNH>y<5h0EEz!iyk}OQq6ijTcmKp#+bVisnKm*$UgJm{I_)B)7*j8l@Chn?p zwphvepr~!iR=rmw!R|D`Jmzsu_iHJ39kL+yDn%4@r^v8eU zjeDMIfS@}#4cOuhptOrIQC_H4!*OCjQM$6KtZqYMGWbCMZ~18b=zML{}6pZqX8r zZQAo{-0;wK*FKIxZ<6{|TNK4|hj!#*BPM?2jswm11BN?G9d_x7+F9yQL-jS$6-Jnu z#l|gIqKU2A^J?5mH{X1NyDGF&EHE?EPMp=F#it&<&u_fS@nW>gF z+}9FKY}lSx0zbZ%hzx6J!!hm9#YRkc@q6C}USC7gA>fs&5|a~!CT1~4sIlgIS)z$; z+tW&1`TEv4%PQdc z0YxPu>B@rp1%S6}J^)~N=fx~GqWMj~xbwcV_Z|Z<8rNI#^mU;+mmU~yp+!%3NiMc< zd0qvayq1D85fhtV-vhjQvhvz1zr*rQi&>0|X#CVqZhhkH$tRB-MWcOdp1vkjFEK&I zRA6zrsmxO_wsKFafZvZOS|K75|NUoxhb{mp?XGTzcGhBCMDyFfck2UZ&YpbW$rnGY zjJ+Sg*M;iYBB?){CS-9rTcSx~JGWRK-^<$o|GMq5vtEga#JCcrD+(h_Ogn4wXd2_e zAAI9yqazO-J#gWl(YZkr0A8q;Ny?;r`}rRq6Uf3e1rr;)JUzNS6a8TtfSVsU>xGDj zS-P^o%(UZXgNFZ(DyA53gf-ShR}@8IglaoWVv*8+{WCu+wbt-xK$6(tEtW?%7}Wjy zwK}{r6}at@-y5b{?koxIz{g$s?GNwRABDInaTm2gxt3~qnTnVcvMbb@jNMaS9#?Oz zZgVZxP3x@>xN%GLHQ9kt#}q}ey1DPB{pU_S(q(EcWhE#z=VdC9(ig?y5Q4FTDn2gs zfLj3Ib86!>;Xu?nl66{_9T+h$EPU#T#~*q0u}4aw3Aq5^l_u?cYBeSQWfmZ@i&}bI zgFgC|Qdq+@;UO<|Y>2wN1EbDTN6Ooe9e?~mDApW=Lc~MG?tl}h>67%>$Jo~;-8!%L@PTvVj^Ou zsns~=!}CKJ9@msvmO4a48eW(p!0S$K%Gsj&Jdzpq7d+ozDbzAkt$N_Y=|Rw3(pd9) zGKxvN3!d)Ha$DoYM*Mbq5R|h>JgToEnX`v**j=zN&E-D4(3bNq-=88xE1W-Uk=hs_ zXm>%9#pO;xX$qFehA9F{E7>Du%`53xe5W~=#nLP&O=D-NqhXo=ubek*QN4t8#w!cq zz+Nl&O@h*NGLgoqLbQ@K8f)H2M|pZYNbI;`T&xR9)64d=Qw4Zs=3Ehv)V`fLiCvdj zYy=RLrdj-yA-ci{X9-&*_ct)@JVeCm;gzOa!<1D^E1V%xCg+n+b|E6^3QfC)=>tmB z$qu%dwC`NpW*G>35HU@^#;MtN>OW%u!?_`4qYnVs1>U`}t;&SOd26*HMz#>%~_-c>U5ppP%6PBad8c2H@r&->{;_l~~T0_g?q_ zh{_tUtruh0*9ZV7U-ZaG8}Wd@ zN^Oby((aDiX7K<=?Rz91fL&m=Pdrn>lK^0w5S!x<-#*$OT~QRKL;1-D2)k{G`qJuE z0oykE!mo|JUn3BeNc@SKrz)j1p^hnv!U$I>JDIh_=2}F3X@xal3WvbqHWm*MwfnIJ z;Dt!T0~I_90EU|oS1C`WyBJXaJCx1{vj$Az@JDDjUY+>n3H!gLT5Aw^`rp~7pVnHZ z+jXp^`o~%y7Q4a=%o;GA`&%i7z00-sKP)cwB*0>+wf^SST5ISa3B`JObJYklbxh$R zrZ&1b(JZj*10$g9c;H-KYOSy4nvCwX5X_eg`ybw87l?=rru8rYudH#BAV3U);<93` zb$NO;y4Mq=xz<` z9SuNTV;=*_g@oqk&m2Gf@X_cB7t!+RTmSgN3oqA&s~i9?G+FV(^?^ZAARVr)V|0Wi zx;VGxp*tUZ{K2CSTz3X7ANuxzV}HBwx5q3049W)ZN-abo#>GPjC*HMw;cyCOr_%w@ z@ju5Nxp#E%u}9GE#Rq$lf#P%EIaUVX3WNJb(P`3#Aois%L{*FGNh^#g8e@z<9Ve z6rsEdWbc_nSm3>SpC6t(c;wh$qZOux>;S~V2u-M=E^1)VHSd~if!}-zB=NnQ3ATm1K;J_Ac+5BJd}GqxxlnU2a57?TZ1@-UC>S4rf*mc-3`U7rK(v5)jp6L?p%tk#vQw zEcHY(dYp-fnTUw#N>e);J>FUB(3PvA$9ajC_{x<|?<{rb|C=&h76n#NI3Qrx0RYe= z2Au&aQ3DwSK5a0QN2DUUBd8S&13(RlWp4JSUfVA{% zdHv{qqqNf>A&^=sQ3c@dHv)5@A*G;|Kb1a@x%QG{U81x@jotp!N011l>eRnr~Oy$AJZrK zpZ)*df4cqe_s#kL`(OTl?0?(*hW|JI?f;|vkG5a@|Ih!Zf3*Mm{m6e?zxaNCpKJTy zD@Ol&`)UBKQ?{+;Y_bsQeumSIITDqCJ;H|xaP2as0F|5(E`#Y@6n~f6oAU`G^0y` zB72h@diZ_My_~h2n$xcFWiv>g-bY|Z!l*K}TT3_Wss&kXw&T9?H=kkiIq)z0RIw4- zK3U)m0taoGlKI!81woyY(lc2H40e&i{#=pF8vu$PfZWiQ+qKGL8~}x8N9`m{@xq;_ z0vU(AqJ1$XuKSvV1BygNIXQ8#xV;)&5l9Rj{S_Xr6+9YO)NiOOSr1clLJ$4akgTWX zTw#G9|0E7J;zU6mk}`MA8@4y%hw*>MOUEcL;yZZZ29D#*CxySTKg?>vIjk(OQN-|< zoLVudLfB>H6F0fskc#0y(1nv;1u|2PABo$U1$pmDfdUX3mYqy|$z`M&wx6&HEc z0;21as7-|f_!C^&v{B3>MtD?-yI+?_VGlVO^;#>H(w)59g9#d6=gcZh%9qR_LP7+u z8>%O3{T66IAdg~3{Vnm8`K+T{xaNlKW0ICMWWv=L;4E^$U;B1NkQ4%EDSC|iBu5{w zvZij6CQ^>a2JZbTRa#s*DKT!+SIQy|xKXgUy&7B*6Wpp?$0ThQg_z85|Vqu=YixxS&K&g z4%huYM`MSF1p)te6R&7JR8_nNggK#zvSH0(R^Lqe#Umz6nfuRyHlRNY?rI!c2qYFTct zPCVl*?7H$Gy!XjPE$^03c+~eWY!?LZ1l?MAkKpTn6U7foO?TjTx_Zu{sU6>N45g z_X1kF{gK4olWaluUCI!;qVygdjN-th7F;11(;J%(Rs}du@#$T}0Lh=Er!6Mjy|vm# zo=G7^6A#^@)`_Mu^H*k5Vuq|#Hc1k`p$0Tb?hVy|F_+ysPX+xm9j)HD(v2<%qa7~L z;s6HC~5SXJ?~{waaAv@z;yCQ5Inf2J>WJ1;%#Yr3^ayC4XV9DtnOnfZW~k`1 zk>xtsOhM=zXVq9&rW%ik{H3uS%bO#00+$Uz@Q^-&xrNcWZ7=CH7ina70gy>H`sU9A z2oK!|%^M4_xV;)%XuyBAZG9(WvdA_&p34t5{E>*+xM1gdq4ECi5A-U?H z+z=MD(QYFtOQb?#1-;q#3x;D*{$+_X)Tjwu)~5F3s%MnA)|cb?;lx~5cDFKq-V^an zD5+wFxFffWAOamA!9ue`!?PAh2Fgp&$Ifg-zb!AAeoz~I#MA9vh$XytzK=V>KM%aB zd&!IbM5Q%3KIcGZ4cmYP27vBQR7(Ks7}3j;BwmdqjIW(=X}8Ek`pzjtWvhu?eQ>yW zE)&lo79Ta4%V|kGp-GTjyniE_T(oP$0q^8tl2rB!K~Fz;`ewX8dce$onB{;h8+>U& zLH|`p*BP4k0WraneG5gjqu^QKBDc#$;jlw?ELU5FqwSXD@ecV;>rPum^{!tW6DUW& zOZzCkE!)I#TBFf`EEGh1af{Xo2Q5&+0B9WS8~#wHjt3lMz5@F&wt?2qFx_xA&LFC) z7^7U8^Hk>XB9IwV=Y(D5J9D$(8syE~JYTfEKd*e|xqh|Icg`1(yq}g}W$=Uwl6^r= z2663fp5S2(UbO`Goxvvm8C-c&@p%?EKwg8A|0is{ZBs1Bl6JLq5G3Tqp4ik+!|m}I ziPcYBi@gy?;elwy-}7oG+(BO8+qfag=oy89`_rNK<(LFM=w+noGsu!=Z?Qp9|R9ji8`f-()V|)}N2R~)V)f_GQE^U?ivWcHx;F^5%iz*vr!+VQP@#1)-{mv#;Z%!5lyXO~}A1d1jwhW#kYqh+8Rv#%bH~)q0X`QnOP<^jb+m&x^ zKT$Px4Mfbn*o~H+!dZLbLk{CugclBpWW>|bx4Oa20W%fqgp7VYYYIZj0FE|BE#&XF znnepaq@>Ai z0HpmC3w#u5DtvMRfd#SXB!zYd-kJ36wqa@%fXeM@t>0)(cWJqD%rk6)2~UdAX5Ceh zK{@)`<=#P{dfA1r;~H3>!+f>^D9Y8hP)(+-n61AABm|1tHOF2CNKi0&`5RGE3*e2y z!@?&`k>*Hyz9yLmgCt#r0B*u{`gO)UH2nCGH33|9J$j81LHD1w71S7l6dt7t1K9HDD1`EHbij zwLHr!^aOlXTIrh~Cl%5N*J#%w( zKy~sjSi1ZJU!}xitO&k_PT&UW9{82jH4Ce;ny32Ic^1HnN}!1TjDXoV41KunzZJTg`g z)a*)I{^>`6foNoZ_8b$HWN)USJrslCn2UGaFXRlb12fiv8KBt|E^ZM%!f^4%nZETA zNQM+y1*D>s^l-e>O9nrKgh@&KPE^X5lvca@n$MeKM5b5aM(_QLs;!MQA3~#BrX`5;b_#>1LteVgk2+9a1 ztJ$7wvE5zEG}gsY66rDPN-QAf>Y0s814gdP?43Y9lqUHzcghMQYaR~%V!HKSvj5(t zb2G+yzZCX9&=J^{r@7wn`qYpf)yQzApmd|R`)y}Eer89mmT(xJ{YQ0{a^bj8{3~6s z``UeY&N~K^XZmp$e!Tz^p2N!9WL*1=2jQbR$MPSCuLP1g7eH}KhbMblv-t_)1f1|) z!yOtZkPe3f3a$?N$~MSsB*D?jygPjCC9?ZRw>Qy%Zp*#1PP^SJ{6|h~mn)jLA3;zz z*7x`h-h(36E-6NkNox%f7UzW?m)MwM8uY?&%y@@0L9~#>*xA{cY2#zfG;q7~S_CzLs_r5F{-FR8N?{A3HJ^%T<^aiKZYsm>y`#SqoWyFxrP=n-%{&%k(sH$q0T*6ze!67E+)y2>FATkJ_BxFsiC zGwq|`YNbuR`rD16Kk!tTSOjptP(m|y!+LZ?TLWQ5IisgE6PTyX)&fd1$2XjT(K3%z zr0GHC;mihDF7-&Mt=lmM@H6gKPB?~D&lRe0?urK2yz+1g(lH+ShU&PK!^XuX&PG}n zyO?JI$+u2N-TJ1DGu+kOV_w-44qw2tw0&y-FR$`Oh0mUX^%V%+4;E9F7Y2S|;{)h1 z9#Rk?^T8(7symB6#mcySs;Sz3VZcS$8B=(&Tf2Vf0|hraMwmiXemhU*8pLp%o=h5K z6d=KyyuA)ZICFSGd%)_ltm%;Iz0iHdx;9icR-sI*voMqTlQ@&o^J!D9U2qwn|x;O5;DL!5OxVxVQn6@ggY$Bs+o=G7xD)1vGtf2hB z7zTxAHWy|fDLE)J9G^3c!x=JqvHrm|^|{IqL)&`OELW=D-MP2TK87n;O&&wMunS`@ z5A2TixjxRoRC|l&BqIp9YWRZvitIiG*93;xcg(m{uWeBRsk+L~^4wl1k17|H;Pu%G z#F#qDd<)Kq);=lpuP*N0zKFV{Ww#~Wk;?655$E!_{s-ejYDzHPyg_;C*5B4X^mmr+ zAJ&o_N4EKmLD+$Uh}+%c(1pQPSA#a(7q?W(fapGfW3wnByz0A^yue!>0Tkx0f}v6`5fz4<93-D-m{c%KUrWGw${B`+WlW+ zD#{P|jW*FkeYB0D4ATYLp4MC^N%e^;hOg!;I9{n{>?xcJyE5gyFkxN;02C(@*G8M3sVw6TObp*M0 zL)yOMUHd%5H4G-L2`Tv!JC0_!!^q-VW#aV#au2+x{hnrWF8xOBJ~gKtm9^4pkq1WN zXq4#fkqY;~M$L{S&8Kx{8SSh|LqdcCZoFmaY^g%|EQRGHY zU=EELs*B*wGwXh593h@q{+9EY;hA0cRN$u9GVv}ku>^8>AN_Sy_VeaTR_CNwDx!|P zf492-mVsA#JS>8DgH&lJR`hpWfN;&dIjLk23?1W?Q-$DOwbns8Xs}UT*_H`3fzIvZ zub$#7Ch%wY{HBywh|p*dXB4z37yEY-La0wPa`*+unMd=lrv#RIXULo%$%xI@yb{E> zzh1q1+qQr%!GF1SvT%&cSBG`i!4O=ldVzL9y;E02`SL6Dhn9K&umn*=dveK7NHy{o zwUV`rAwb6M1zRy@vs0wSK3NjZMjLQ^VIas?nid1hxhq>$65*cF8%pXrXVHFSLY4!b zxbs`In*i1=QBQ52_&PCOF$Tf8^9*~T(RTyONDVKcK^T&i zAM~kwo@~;1%fDhf{r7*gwo7H1h-Qd2lGi2#^HFQi7s( zj!oNFJ=e;yWNOLg4{T=E04?M(wMj-9QDG^8=nCEi%bQ+~1MHg9!j`umZ5@L+lUdnyaQ4&Y4=?5?JMx)IqKyKM{|(d*EuJD(Njpjy)`|e4NA8#!A3HsJPZa#N*tnO83%x0 zw4J8>ikXlYjFLp@CiTF0qq8u~JVBQmYjF$~4DoRRsk=A`UYu9~s&@opDbJAtxrS;W zx24}n7xKa7z%RfCn_63DFdhV8F3~;5}1J>|j?AQ)QUOE7(nf8J-l?D@jUy8*; ztFc{fR4TSQ6m+W-%N3fqkV<1XyxZ1(Umh&}%w$!Jy_5Rp3ULNzg{nn8NQQbr8NyamKNW9*=sjP|s?taH1GJD@Pn?VF0<4N)y)?F4UgDw?+||ApNu3l`O>^=nAr z2}B0H>2BnbNrbU+#N)0*XbvH801*ZEp>$YS-#yC0ig^;KsBc?17%yRjuec?9TKf=fRBeT&gK*QL=@fHxeyP3?ox{hVT zcu~eJ9_!6lpnwFFu}JB>2E^`8$%L!Y#80wZrwF%lT|w<(3Ksc1eW$?{dmgJkyX@__ zhd(BuTrnC`LC$;XOw%H|m*4eSk=CO=PFg1YebZD4UT|K6Z9FMFsK7ibk@}1-dl?}q z!l+gns6DY5w~Q^&UbS)MP)JCmH3H1J|`-yIqem)e>x6n^GpK45ia15 zX0sxr0cF|=4gPJ=JJG!%c^6|!GQ=A@EYz+UqF&PZhP1ms5oU) zu(Il0w8k}?EzuREtd0#KRi8tX25Ljgy`*XyEE8P}cU1^hzKZd{+*j2;O(n6cKl$zY zuv47Flmw-o^_kJy|M05AKn58z4njJA>6;hu`}2-4y?D^%?j4Di;5254KIV3yU`unQ zT6*;?19zL0uZYK0{G;0sbr z-X)60>>=7BjH;8P5c*m3HrZXVBRcEiqbZbpqN>dK@TjAu0&q_Rx86NyWezMC59Fl3 z1%~SZo7pI}aiS_i)1827dpVd9?Pl^_hp34=P;x?>VEQ>@EJ|7OwC0Lzh2{R8>?2-m zTn%NZo-fo{)q^P^Y1!2be7ftq)Z&8!PfzLW2cL8tOK&LR63*REgphF%F50VT7}X;k z;Z_+AYC~?P6Y!Oevyml})FWNzKlVi@N=-Fo_h@d5LvZ&^MF&^?g|0&Z9*%`jA?f?D z0q*FaE%XSCdF528>n$OEXyZrs>c@I@gUlKc_sX~530zhZ30?8-lUeCbF( zxf@A6e2X>^`)NNaSP~kN$H%yUW@UL@0|VlIR)Yut9#KxhriRhEb9xa$KCxbj@2^;WrOsftV!a%3|G4vOB!``IA-Tvw~*6vxz zyyFitjn-f9z+lg1m-57C;c~&IWel_Rdz0eU@jdgxCDz(w8;|wz$kf@_`;ckavU~}= zdW=UiuUq~H+`*U0sl9YxN7RjJwblb4CL~PsN5}Y3A~`%yFj0>=yQ2_!YU`V?=R>~#4w7-VW` z{QMtbl0oXeXXoVWjrZRV90}=E`)$H7$32(pZCqH`EIW{S^s|%Veot@cNJ?`&NUC|! zBT8j*+lQ`gE3Vl>uDSydk-<@OxrAb!e1YsY2a$Kaw`6p7Xvk2*qg-?>sFpdUP9=gr z=WgrdD7E@r#zDs*_RKYG0%@GQ?3($hQ*?qD=6Y7$y9EObO6Q@<_t|aX2i$Gr-rkN# zs@|KVwG@CGyD53Cv(cU+hx@|h$)F32ytFC<=&PJ*mMLzYXM7NvOA0~C?9)iJ`qkCgxKuifMlio>Uab0M}!luFiADtMY8mpL3E=ZWMQL#pV?Q7`hECF*a*b- zR6@IY4t;L+_j*hv4UE%ly^#fC3d-YN_%-G;8GryjgP9-dd$eM|?8c&q8_S3&ZSi;P zi}J1L$URbpGVtt|7LCVPHv8&mmRa9^8D8EgNbVMUy&;oMIPH{U;&ZY z8=WQ(DFsCWmAf(2xG5u(!*-{6^R%F3oyc;o&=Pz&nju^B-z|-oL7)Op{?O0|0g#Pn zWedmWwRzHMWO$PoStNZ2{yEo}XcPj?3l-(US2L`zdaT<&fEPe@;S?06^*OZi_0s25V57UZ?B~VmEmNp2tcqN9=8~Q zHFYJ2Xy(A|he<~5=!%-g2e8ka)i-yT>~5nVlNT$GsX|^vhO+ckuu6u|9ZVtx+|2jb zy(VCv(=~-UCqnxw?n9Hf3_2WTgM;3M#-uRkbG)He488#!Zs|)RM8c{ZMf$Kl6A!*C zsWtueRn3)Hk~+k`5t{67KxY%WyX>Ux4XQt@D5m}F6W`iV2XrQxXFmQ_wqyTvAzM4)(XUh22cn>6d2E6Vx(6Lft#Og#o3nrC zMzk!8oiHV;z;kdZm7S??KW_IIC$rHVT%9smot@VO+I-P=4+bQqP28yN#GIU6(tXepuuiKKjy=?m- za8AF}q#CGG#KhewTw4*o<^I!MEz7~4h!hFHP?)#ZNTKLL45zrPjeg#SnBXU{jqL+&^0d^2bgMPzMCyZ{ZrA3tVASO5|45i&J9fmr{6C>zBd;bPwPL~l$R`#Pzn|cAw3$`lfp_MOM8E*1JQ>PBJo0;5O#P{0 z7}I@^07R%cnUE%oOPN7h#>^i=VzC!Uf-N4k+oIe}>I9>+cleJ?o70hJU0jF|FI==) z@XZD*j}0`uR>c8h)MAN^$S%?(ACBWIUB68|@g{WiI|(1@cypnCQIq}Pa?0y^TC%*d!*WvW<&7igFLyA(@Y%a?Lw|T(!e%T2OwaZGnD3B-Btp zH2F%3hfT`Tj2v{!k#c`v)=9j<-U|B+b3&NrmV0_n!sp!@ejc!yg!ZAtw!iaD#avp? znX)3dN1=)R3Gwo+bH^kj4sI*lu1JRTJY4xRF4PN^-Rc)S7*C|B8xc-y+?5345Bl7z zo92+do%rGqwLmM=VdhBhNMs*`W}aC@8B1FwHU1YC*Z?2XlfVnp+@#UNb6Sl5=|(^u zpY>wlpPL6RP)m9s;}O%o=lzhG`j zmXd&Fu2;%6$ioa@N9xxlbPj#eLT&N!!t{W)5N zZ5J^pkHdbYJO!eVFj3q+8`(CBl(NmMDbcLafoN%H$;;<5{(BGeBo;cqy z-NnRzp{)x~e4k*krxReCYBi#N6y-m*_wDB}9A^JIrS_upaC~W1>djJ2U)n#7bkkTe z6-S$d0L#A0L;H_9EcCS!1Ra#d3Qc=3oWXkZ}Pw5NnsSF$nbmn~(AW;R}9_H2{ zjoIt@iTgdL{uFWC`hLbe>D@yP@wQWdovr)r>Ll~?`z9K2{`e< z#1DuD3st6KnZz5PTk=&U=i118gqp{JP}$}zax3V&)Ig(eC4FyNfI6VPnWu>v?=2fB z%S_>zwpm^p^1Ik)m?6L0hX`i7d^ojEwvm3D8y`L5YW&8B6Yo%Adav`Ng#-kVVuo2NVLI41XuhZ6)3 zGXV5G|H@(^>Y)uRU0dG*$Q_oci7X8YJ7ci{?r#E7D@#5VRa3*3p6gzVH+jm9+`neCxGjug2zz{|bw*oYOx6?an?a4t@IU_AVQ%N=`E5?)G71}AgtzBK9U#&padvPkRQynG%d}XA67xyHRb>`JLuKhq zRAi6WD*!u~Mpod%&Nq3}`C?B{sa z@3B;=b-5+Pad2r0N`N__>E33F-=KsJo#_;!mRuv+70c9)<2$i?X9Ft0%JN3vRrmD9 zjA9$!$H^#Rf^MqmTlB3eJ~8Z7aYI&Ss`OpT(>Q=yyN;--rayqx^s9CLI=l@B;9}Ig*hs1I90iC@5Pg$z_J5%*8s* zzuDHmrLt?l*}WQNZ%AZUKwc{az8v(C#&%QCAvnFtymjxSahq@mZEqvB00W<;ZS?5o zjEhz3kVg(U-?o*gq1mh`+BG2s4kvR8!SV~qg2fHjSek}^fQAl2~x0o|j zHF8IzB<#5ehx%fzd5Vg0D4se<4ZImD7sL4)bWUIRm>AZgZi|blco^0$_ekv@5CYAL zz8X7Q7wEZ^p5)|5qL%gsj#9A^2p3^8;PV`$-HSTjIBO>tV-tS3zR)C|zQ@3=u!{nd zsj8;yw-oLe4mW>$$nbR`O@*1S`*-n7XL`S`1>+1*%*^l2ln*<>Zi2)y+U#y)bSQ@g zbD!576WbttzA%#2-sg$6NG)rb`E_se;Ry`T&<4c0L5R(6%76%Bo&i}SxyHz@drUX@ zeVe7{Qg~J5j5XaZ)9o;iKN&$R?v z$h=BV!_4pK@twy}SoV`F?MAvi%63tMEra-hNzTy+!5dNix-s!rR*7q*3=mV1vEcmk z5wvEM;fM9S7YuTC{+oLIRnT$xBNdL9uA0ec1uzAb@XKeUcvhr~!#sei#{I5NF6 z$l8{-S=Crydp`4FCjShhj!-r^wid`6&T%n(;dE@eiP62dl!(+7*M&ewBWy5$ zTpoCvgO97f_`l)>GU76n<_^3vqv!ND))on2pY@|%NF?x$rz;@b>Xe3vks;TQi1SS( zz6VV~=#BFTcf{auv8KJ>En)+uj|XDv*){R&ygFU4Q1oYd-5H0ClA#q9U>i)@0MsgK zM1!u{EuG;Jod9=0h`%uP-rO_}(B#yP6AFk^8i#vla36;RPe>96C;5i2&&Gqc-(45D zCYSon&nQHIW>4+(r^ztmWR}g>xWWKW^2s`;9#uT6JQqLp;k3-V{qYW+sB9JXe!!kz z2L?J~T}*hq#)@lRX6=7d5fLyqzE)Fv-g9ur?LW--c=1)Lt^Sv%T@>0BMMFXm<;y|N zRb6>cpK7a2@)nU?uP1*-_l4mDvFAfr5H?hBQ61zf;W)Lf&qRBj#B=nLxfy{Iz2Bw{ z64kReX8+6pTA-H$0MB8H0*;E3x|Mu9sh&kSQjb=+SW-i+x=%D9;Y@3tP%19X`r$?+ zq5=$4O9W`}7H@gbCc*inkLbEhjk$XF2@a_xlHW#-qF$~nT{WUn0+an4-WP-p#)iry z)MYU&WjXe&_uo1Af^1`QcdN%n_L)w0n)ilp!V=^Lu9&sEaH*-nWBaD1&G5+*xqTwG z)q+lBamTCvVuDfr_sY?~-VS2`{g-=8q$E{qs$Bxcqh&*0=QSxK0CMVGh6BkepTv4~ zEr#j0!{}?cyGbrV>!SAZ3i2$ve$ZvNLSQb9@5}kJb5(m73?hl=3H}M$iGkssg3WPj zaF|C%oy#u$e){L(hai_BQU-_!SPbB^i_-oA0N(jpH~V+-N3!+NhVywcaH+}>mlcZF zK&RxN>s?ouDYIVjz5Co(N%pDi%liE5If(P)B8pAiZ+Ko1J05fPVyKT7^9z3?3Ib>~ zK*2c{dUS69@j?a{gYvNw*H5jy+el$dcALJGbduqu+XAtgDEgMsXrWfJVb5TywLJZl zf)_1foEG8T5=#N?Td4dFFn>?gF*0aJnV)sR<~MbJ+Qt+vY8+{-KRU9j@-?zRhDEf@ zhF)m^7wf@-+sn0;XC}Tk=Gguj_KL|UJM$2+7fb${ajDshuH17Vs$JW(i89r+nhKPE zTy_w=+#b#oXVOkz@9H2@f1`WC@Atq6wkmu2EZVydxyx3&Ue+hFgy_t{tu(-1-npMr z1v(58{vvhxw0daGss4|c&8h#e>~N#FltM?Bb=7!iLS01|pEqU72fytu-C%M>^VjI; zZGE5C@pn-VS20SiY_Hx^^XegHzV&5mBjmiwWETpK5Uy7BJ3d!m5w?a7duHt(b^D-;&Y zTm^`&Q@*XCN5!#IywUVZs5AX|Jq389p(HP7dRL7R@qeXrtbeP(*Gu!ZoF$J0Ml1Hn zI%?Q+mbePZyYcGPWuv9uF~v%2DRHbsM(pDE^?-uQf@E2qCe3TBNWGk`V&=Sk6kT zZ)uExfBFI=&v~Gw<`BKDzD)rXLoyc)(_E{3`FNrut%LWqSTNodq=+ST2}K@2PDqcSGl ztH=7=*Fq%>K&ffkq)0-?QwDD=Uetp}P8TS(XXs9D?_|4GQ(ziI`97X%byqCDp!07` ztuIem7r@&G9h@Oxpk)=-=m{TsJ6qS6T?ZBtv;-+V;!3o4uc{mGq~Y~6PWcTX$^RhbT06mu6%a@JX{)WuOoIQah0L=>slMJZB+5I{K{Kv5=-TdnA9I9}5>Ffz)G@C6dg|CU zv3c#dZAx+@3@AW1AQ$M1;srV;g*t>O842gRMX;mE)$d<8#U}*UJ7sw=1aK5oyq>FD@dXhhZf!KqQB^kDwK?FnDT>bluFzCFvuWICb-^p9q@ z$|gi0KDP%_?e$b>6+Jmv8|CG3EH%gHn6I`#wOo@JV zy8lcY6tq8lnLP%MA7AeJc07qer~m)}0005SKv6{N2B&oUV161o?}rp0jSMc1G^tDz zNy=N4BucolpJbWV#$Gab-LujX(c|p&r+yF-m!ZN`1Cj{ghK7@cI~B9s7>WwYe!GFRJT_1q@qtGAHcb1kbXQo zo5yqNL9EXp2QpsfLy41M-_@WLP4eW@yJke~ky3kPx<>8T3tfwF#7hSNEV;OKLx%zC z3Ap{aF$tLoti@C8_$DJs0ZEC@SkW4Oe`9lcb(5UjaSA6s9YeK&3&*;L5Nx)%MdJwy zlx9br>Oa0c`%#Adfd`Vjt?{4bL~c%RjXzE4>s_B_XXakVpn^hquPntcc+w_?pELii zBh>2J3Pm>a>6+Ls>@pEsNF2$pO$kWUC!7Z!#a-^#(t7MXxf(v+=bu|-9nL=XJDxb@ zr<*#`w^QPlOWI+U8{8yPVIzM!hcPCU26!Ff!|*-~7f_bulXYyqpD)R0!jR_in%+7t zI1!WIkQ&Z2Ght;t?y*FzOiIVNwy@U1xyo~0s7SmWT>f4PCi^Z%KO3ka-1CY6Ci$_r z!L)lXCcL5Q9}%*#tCA2RyJkdcV^H9gb&z*OprHn=(5>42-69<^d?9?_@L++AtFTlb zc_E|s-w9mBgg5Ptj0@o7TyA!E!KkYnay%X-|6!j4#7`=A&)wmN0p2l%oZK5|aUH2T z;>hhhYUsIecXQXLw@*cPYg63RI&2SwiAtcos-3*5;CYqI6g>oRn^<9qpE>qPPdO$K zAGL2EwFkCDk>X^#g1UCR?lC@-Wp0Y{7lf*KsP`Dp-CiQGcQ0|O`Dlr6-#t9+sEZXb~#X-57q_pz0mOX2PoeCL$ts3 zY~ac05BRaHU&LDok2c5o1Ui6{kbd>P!0 zcK16v2hKZP0iSnrFy*#qfn;Y_xG!z%`w-u>TM(_&vF$ho3 zTs1JWFH}X18jJCNYfG=O)3VG#c1-ow3iWp3tx!7ixXAJ{GNIECZW3Og000092yZH7 zPJZ~2++pcBRlUOY1`$VVDTt%Bl%sgwE!7(U@!_gB7v$$8KA}P|AtK}O3~_LJ{xy^V zYm#d+pGg8Qv()_49`ddp-cc;FFxmNWo%h4SySi*6bXU$)dzop6lZHCTs_i%&U3B=8zJh`E&~HEORz=|UHxfziDBo8 zQGGLXT8wGA%|4wF45j0Nvk_w;Z$kZLqEDc{=>{)Pa762sc%aP(z9r0}%_M zF6|Poo?Xjlg-A)_5L91~$2{R$2Umpnz8{lPth+LMo=wFFM)DkVN)W&eb#4WEImnp=F&aK& z-|kePmA@&O-r&wK$_X9O=<_WRaGF;&>W%xq#TD5@owtzJShSJ?MMEb;iEz}oYiKSKB{WRZp z2@#%pT~|mt!!j*!P`(9R2tg=dhmCSD%#c`(y;k28lFtdB$S*76PwQ<0io+Gp@<6P~ z*qHjnlZvP93rdDy*cS1QP6(ZeXW+=#&e#YJSJ9^6Banl~6sQ5n5T({>x>4U86oBAL zQoT=G$2M7Wn#~1}*-tJ4eoXaoCt$eij42qz>8y1d*giK|yaJk-N&o-=000r-lmGw# z0k@73Fa1(6fFLMlb^iNemT_+`7=Bb7`Q>YzW|it4!N;L}PgI+Sjxa-8PzU?+0*{r# z^Nuf@&<*KE$cs=(grzl9D~qWJAESG|pKLl~^4Q}63yY5*NDPdpG+0B+Rn?^lGjE{ji|Xg zu-}Z|<_;`j`~&zEnNn5#`|bV5g*IS8Ex9PmB}a;9Jmf=L0{Euxg{g=X9=~7ia%@VY zA1x2TjO^PB2Lf3?m9gI;g%n#>XnqM;;C;%Lv{BfbD9!}?kB<^(xW zi4V~mowUXPRsO8OB5SJ&uiuc6r565SL_KQpa6fJ?y+fhHJavp-vUp4a+o49%gzAKN zTPbbouYyOk@v2!>d#v_xtB99*%nAVdCLri)ABA*CZ{r|6n;Sd9IYK&U-w#@sJA{dn z%0qqfE5>m>zvQfdq?#^6(Bl;#onMZ0=PvIc@;s-_L&Q08glbFRD+zZJe?VApZ^)1j ziegCY?y|9jd7pLSjEk`m4*f7e9`w_;0pb4%VcQ1rSGGF zadXZSL4sPh8E}RGrw>~N>UMM|x-x!$Yb@aBR|J;gXVsg(n(lHf6mHjRtY^#1sL?^V zoWu>|SNLRpm2xTlNlXDk-gu3Pb>05V>7n*;9144izVxPoPc&Ck>VR?cR(2wxm&B=O zHNY?ZHngdNY-b-0x*Lx-BVjZkr%KwBXrKmEaluzp!R7Ee=ZO2r0a3VwwjtY1wHU%% zjZ`>`pS-9|394lAi^!e=yrGS!kl_Wqn6~QOG=UCoHS=j={2tIh{ZSc0{jadg2go(| zP%o6SnE`%K5h6A=A(pVHr6N(<27T4^dG0c}zWFGNY4Y&73FxcKL$P{CO zaOgnCfd@Ru*fC%__z|nyu>zpG(!^;WUauX25@TW1r&%-j5DWa{hwLm)a#SQdfY<29!-<6MT3PwTCHN=Z4VC{6lGq-ok0f!?-x zK5>?0&AI3K;~4y@>1+k<<#=@MxC;y zPW4eC9?9Y)=%PA2F7z+Yehkm0R55%%G!4U(f-}fr;Dr^cmdhWq(Hj@m9`I2u6UW+q zz!#tnd%!3O;G~ya!zF+K0075-*4bt@u{<#o06aN}NF>X;n8-ua#zG#l^>6Hrzy&JE z5&ba$`&8y*EmJ_0{iX9+fdBZy$bsmt;H%9F?R?XH-*@iRVVh0o{MK`umIFgmnq@zO z%MNUF-0k&IJS;}aVBrsN_BlzH6c)hOqjLc@h)CrPs8Fv{9Az@R>*Rs)S3)$s|FuzR zs5%;{xdp$T3uQ@pGfb$!Npvl8deU)^a{y)J#<5U{+A%0!l}Jwa@EX^4Jx=0BmhwHI zPCW4oL>SFuD=q6$zmX3M`R)E%wX8jX2Ol_qv%On$+`d0GQnEh^U68g7_5D_)3eQSh z32~moWjBW6HVGpj1xA3jqS0|il*4MD~_kY zwK$NW8{KMe966$e-0Ifqa&E~WiJpIZqQP%(76{}Zcw^XjM!MLS>lnurIh&;lOSECU z-(-6n#8Z+X>DC6{0~s39wz_)LMa3JK)5l$oN-%$i0Dni>FZ-sK=rC1NE?Yp{Y1fNL z2D1;kW*71+m3UW#cPXsS-9V{0$OqrM+7d}yEOnCkjkH>pqTHqDy|IKf{)#J%j^B(O zEnZ%M2k^o|mtfw$?`om`P>W0GrlFg8k6>K2Rxp4v%;iA#S5@b|nmMeh1q^heS_jB# ztNo9j%{l-VIHem0W0m%hKra#^%1Mg`R{sX~UP|C#o3d@eTY}tS{8?PwOJET0)tZ?j zAMr)BW}7u^J!LG?;21v4MsI8crzdQ5mOZCGDrE3wEM)hxf*>;?3xFv1E`7@PDR^ik z-xxmw{UhSP4(5-bSAey>BMk^k64q#p=mGf5{Tx*0iWsUaW;soJKgR>S%Fu<1;$Ekb zq;9IS#uEH?!@~t66F%a;8x~4~)RPAe>?Zd29U_>u!JJF0J&b4;fL3O>lwow_j144M zcFQX(z&3GamRr0j1<5wWiQ;Agd{rF`T%hsZ#tUJ?-sx|ljM`m>exxn6HSA&>~l^MEj z0CnIcRYx{y{Q*i5JEWx(SO)~6% zQPBG|nbcd;nX%xDm)z5aYQD#RhrD0ps<9cRT%YPp*$T{3~q>2nI$YV0w@q)BYiqD8DU$uoE;F-Afcwa ztj3%QnbX`d&!VG1Bp4;qDz2=Dyw*qo)uPg8DQqo4dH2!GQzXl})*nZVFU&%Ag7m+W z0apl5-<7MC1_d46{K(mE#(ck%-Q!(|4`YQRvTQFoN0a);eiqk>MWCP?9aF8Njj8hU z2`McFjUOp-SWp5MXe7rofpF;6J=#pKO}fLXyjHtE%a;qWPsjiO0A2%=5Ob6E;yoP) z>Ip(6#_Z7*s*IJO4HOU0ParC3y!~&E*j?umDY_PC;Z$zU9+Kb;?19M4zeWZ+GTk)IVVgQ$ehLgRZE(QLM?5u^oMT8{(Nk{ROjRAl;_5*y)~h4<`& zRLU;uGx~mF=MxIM##Two968FiD2r`Bv&%?~^RZYT`EXMY=Kg=wG55v%X*NU-y;h#( zok~6(uyZHRDiVvoC|K21#FRdVfb3fHLNq4uUo4OA1bwBVZNoUhTMXd!<1C6+ghxP; z1SjY--Rca(7me~8x|j_A@6u}qynzXMRmO8A)67dI^t4QmF%3SmTO9g32B75@EG``Q z_|vMWvww=T2zKD1Btw;jBVnjg3#mJg_-?JN`IcNm%2QwG>CO-N$o695n7mpor28Tx zZ7-=vto}bAKQx(7))NPu3uX743mCUVcp~hkC^)=Z2Bge}gVKff6Y0--ijFYP85*zHZV3J|Io zP6^e_W1)z&e1`AxK6k*pRBhuFAN{@MH>SRP2f0M9s07%yUZ%@cUj=!feDj|P_7X!1 zxN$~rv_wfWA6w(3Zt_ztP;gARb0Llyf5sr&5Lf~|U;5JkkX^7h#ST3c{}skpSV@Fu z`7#H+4)^+{9h6qK`H(PekCX5cLYDz1+0CZP`o=oOAy=YZYF-na8RPk#AA&2c@lf%| z<98a;9za-nbne-N|BO;y9+R<|4ac^&TM#JfGjOS#&j-a+BmhD%Mk}}R8kTsm@ymX0 zQk-Jz#Mf?notxjl8CGfIkTK7w@QRJ#Xw#W4J0eXO0ta#K!*buXKI<=BV7Gw)fJ6OFz~Fbp|1&#LpxZ&$(FP51?Ja)d%rz|hf3 zOjvp4PQwgyhd~QeCz`XF-Z$!}_5*|8=Fk>m~}FG`49`I;|z+OXlRwkSM9E zndXe2+fN&@)EKbFc;vQ|q>FnV^@SjGb#X(e}Acvz33#Af5R+z-B|W(BuUkrJ9t@SALVj89qW$c ztL?y9Cio*^61)b0Yt%DYHOzw* zrJ3O;Jz#NoPfNNAgPiZ5eU_D{Do~tzOht2f>uAT=66!ce0Tkm*>qOx8D4%I&lNk{~ z0AQARAB1Ujj(KZh>FM%oY4?H>GCsmS<(J019{){TTBfu?I(@gtlQ>XQU5MYC9cS>} zbCBq_)GurAv^Zd>DFTcWVf*!dIzQ@fvO~aZM=NTX#D?4{Lrk(Kn&b(90qkV*jnm0o z&)re(u^&!l>n7d%kQmP1a;MaIvm0%Qj>l^)3oFFm*)c8Q!LJk#{guon(4# z6TQlJ)OC&Yx6Dp@mr?S-RAVq_mPaT>YNQLv?jj`$qgvHJ*s}*)kR`6q7DAt_@8K6o zni|Ujl}s6FrTgRV&9nFeNFh}<^mRvagHi!g4!gpzhH`22Pkn3uc5a&ceg>`{%Do4s zLOxDy2Ig;224FR6?F$Xv4YrWx)r{Jn*an@qt3@^M9Gw~i<5YIGGLW;suKWaQbDxan zW86CpKjtrEzxD>Oi@Y>t?Zw*sh^Ot|#(-5XpoE84s7;dzwFAPivk4wHWr|9dEiQWp z3~hv9AN>OkcEAi@(Ij3G;8n5Tl6*s6yX+mX6@0NL@fFz6>8d5;o_!YBJ6$=q=gqFJ z551W$;lHIQ=&9qt^7pWa!M!2xhN^9(l$PCb?hI%FD{aN^PzhJDV$Fd&RasRx1^76a zicEJ^oH;uPe}%xDFc9*>V*_$ql<8Bufe9NJWu+83^go7a>%zG9C}FTH;|TT)i~qFz z78gPE`$Cg(HgdaGx*glfG$8v}O&)KSQ$Xg3g|-VJUB(jd zzl%)(Mzdh8hvJLvl)dYhdAxj31eWd#(kHxrmj5wITVa|=CLOefAbJS9kO0?EuqGWSK{LOpnMO9zfH^ir{oG^(}DOG8cY*k+T!t`;QPFOfX{xG8E%U zzvBjy%?8M;MBVodleoGL`WMTPwoQQ65pdWyVLe2l0n^ovl7KZz)!x-&0ttc?I0mtK z-3F8^^2hBpE|dBY`#8CVx}pf-xaFhnvD$M->pppV7N1@XH}8=+v<(m&d7)s_7}~BA zOU8wG`Q#JMZIMk8t);U{Ln!f6P3cM4E*Dtd_?=C6S=Z046i;-R44 z@dNuPls$P-F`S48 z7^|doLz|Bsz#BcAbjhZ#$ZfFaz?PaKK+4assGz3r0Z;8n{+S5^0#*A|3Uh-DWN7r- zkkD)W%J zoZGNH-!z}PfVzh&x;*9;FlGA|WW2eaGw~V*Sl1`?Wf5y-neUf%PjHP8(>>3 zhk4EVt95r!lv_M|Vo%=PNVJrLfBPo;jF)tgp?tU&L`=Ec)#BYi@Rw4GOwSlMl}REe z!T{%cEIvwF&f{dQ2;KaCOxH1f;QCp@$o*$*Q?!({j&pW6)~eNZwpO5YC&t#b(z$4hI z(~`2tNsy_i-bqiUh*_5OliT8UPNM_UkuOj#;)$X zd7GQymY&#LifZO+zkYWfcx`4IQ;xlTe^rIK=k1_@jhB{vp89w!JCb@F*{~&rP@jjT zMyz_&2utff=@sLbb3EIgUvnIa0?Ju&bDz*P9kuc$I=XQpnYB;&uG)rtp>f%30GILP z*2?CX$^j}QAw`8Tf-yH;NCPG%KBJ)Dyfi34c`PFEpx4@C^4+}yhMKS89df6@ zJ0b`*0ieYTgfSTsAnFBhtokkA6Bp>{6{BD?5-h{uGXs@?l+?p7&C`47!k$V)>y`lv zdZ{I={$fHXAg+_wDIy^*MnywGVS_6Q z4L*qom3=`m6UHnIn)T^a9hCEzWo`mw6EKST#Bu3dDA!md8lPk_TvLnf8V|@6QW>OL z>UA~(s+zR&wvbJxsFf-`(Mv-Hn-Q3oRN{Quce2av^D)EN>QPFhh8j-yp1o>?RD@8O5#YJs0O6?bChO zaH2Eqx0h_nCrWGlpOba^Sab2N--|R476w#26EIa{HV^bi=$7k=j*tSv;J~rx2ear8 zvf7+AN@96qI}<|cs#SY0jB3V!7qg#8qW1X6EHjzV_GSR1th8sr8LgV*h?9i2Tc`@P zFrmmn7SbQc>!Q=pfSQnKVP~veTy%i+A)-dY>-a*nMz!s8{gB13@*iWgV9$)A`{oA* z2e@=LJx1ENO%M*^NuupHf$|2G48JUK12!p05BUg8!=Sv?TnYod`QpoR9Rz(dev64 z``Kq~jD(Y;T!1n-$ur5&mM#;VHFTpxoo=6|O^dkXDlcaP+!!X<^ax}n1lUBN78TyBhR}Z zKP>)Q;IUos)`GG+l!Q*=DxeWBX?4kd@Q1j+*x>4fz5oY+V+ktKr)`_y)b8I+E{shz zHTMt1#78kouR48YS=jDqTsamDhadpvmxzF)8)WH)b{jI!`(yxBwf0{`e6w{-kitqf zU)M|@Sf!l8KJQB3SS11Nhxx#fi?f((&1@gBCAoO5e0w-17KgZ2AFR3yrz~B_E5Q`R zCHU$2bvfpMx)F5(GHzaE{+&ZGspQ3a97Y_{0OKZw;r`}Dqn$S8VzSY-(IeZHZJaS) zl@Uee5=l(*We1FI3%bs;Yus4$OOX;zcyB*%P71leDKz`w006N2+AB%X`-1=g1-?`l zyMeU)_>n9T>R-yL>+Z(e{KRQ~Y4kLtkZF&pLg=4Nu@x>pK$+1qasy7%lgi6X46jSM zNVlW`%byW1$Bm*w3%|yuhw)+km?pad_!iuGy;Rd9%Q)DkFY1xI?za}SZ5P34422nq zX~D*?UuKYwWtfcdJ#G;t`VzTG$-A{XD?gp95=FEfU~K8Ip*4@=Bc}y;b4&!1*S2`2 z)!5jqegoMWlSb>L$$QO;4z$RUC0RhNwOyxpcNw7agcr}abHz6ObFh4eVBXa+44^&?D}<8%It|M$=MoU`ohub(jO%{ve6)+;8Z{}g$vEk9q^k2uLm>4H(6 z0R$u6+Kq}C$NLN9>IQ=dIWS9r^*gfSJ64LH=YO=IuicK&QuGUQUL7PFifP(=7C=50 z2|B1ns+~T7$d72G7IMj;ussOi1H{@Jl|MMJ0*_e-fOv)ZKc?rj{xRjl%Mz6s*k#vZ zyp=2OJa=2aYo(>lgf|m^MLM8BRxX}i0@^vwTWzj=|Iw6!cwKK-k5yY)>mn#ETy zX^c}EzC%Ww_j5LcyrY+PCU%<e)uxLk*`x9C083)TJUMmEzolZk0C8*3 z(Zw2!i5u+j2#Df(jsy-Fa|Dv)ToA&=^AF}-{GT3Y;PmNhA!F$ za;fkQUNf9TMR>gn#0$2-#)3Sx1Ud7170*fCu%;MH-LFo>j%sR(Vygp|E^ll4S!;7j zGrVs$v>yvX{{j=)Z9%bTsMdz*rULUc2}TWOzE$oWLoGvaOD`&a&Y#9_dExwXsGtz< z^GC-(V}(-NTsWx+03Q0oUp6Y2iK`24D12^wnIboso2lUIy~<@r)a+%HC34RCfy=yz zQdzCM1)8Kb38xjU8O827-YK3HgJszX0lpjXvbux%jYKn@s*%# zyY*KNs|}h*a{${XLM=w8ix5)P0b|-t{`(ar+z*+y60zUfw=+`6Zz=J;&soDj3dx@w;ae)9hkt>c)-59aICQdi{lg#|6)LNPr1kgkT ztXTO4HKT=VBKQ6dQTVRPH-U#*R2H=ed8}EDEMAG>(`#9neCc{uLykYU#U4EA#&hq4 zfVWJcB%IRs>zNLg!Z6F5O!-yZ6J0A_z z?VNq~RXvU(f*GC?{$!ZF+Ye$GgGYz_hx@-&K$-J`EL%afSvU_jlU;r|Y!SgLcI zFM`I@Zc0$wTYA`zU+vPGXi>|R1IN*wU*~z8&#pUQ!G}f$oJ-@HyVSq-TVx;wS2yK_ zT3f>Zjwe^a2~%XA(@<-l8n}R48N<`_8bq(w_<$ zz=a%;zRhyrY4WphYdg4(LeNc+I$-zEVZBD0WyS3c1F4AUU**e9YL+Fl4**0Peu*y1 zcvq#F#drQ1ZndS5s!pfO`j^8vd!GfBUlsbD@I6dPlUpI9^yaJ3 z>7i~|OQeixdGVKH^F4Qv%_)qOYf*?qcGiPsNg!Q^SR!JCUKxVMatO~AX#LZyHHlD! zka(O6^PT;m`o4;O*z#`Hd^)!{itltcFCYZPss<2vwQ&=?RT$XDX{+>*oc4qepmO3G zQ}Vl<4CsKSES#kyVF9tWH1zluUjN@997;@1_<+q1+W&lHl}c@*!Rk+=S7_`#f8shz zDSof-&D894pnI77%4{9FJ=AZ{#|;^O+uhv8eL0wK#hE~-XE5wQVFcj~bgub!dV{1F zr}eL`kEDl;|A`*H+n7-N1&SXb>SsP1Ngc`zCn^df`#|t7@kbRpJ-wzl>ZRV40p`Ux zE5Cg>n2A`HN+fo(``2cmDaqy54&*Ut3N2V)o-pt?&R~!e8db- zE9{en{ZI{Z^UKSeQOr1VoD`iy@sr1ODp$0yD4fbde;5EL;V!%1l4G$8zjIH0+im{C zx1}OmETo|rg}=?{{5%bWativtWR$D7Wn_QJ?Llw~?Ay?(tHf9-9VT5(hM1A?+BRqR zi`Xi3PvbHQC1it{Wl+iJk748#A%SuxQJVG+OrdH4dtBaf>6zrnZzWa&<=iKGa=Uaa z>6{kRiV9ogK*U9R%WTMC!dZWD5}?5>D#g8P^;{vsR;Q+pj|_O%8+X#9T4~~6=lKm7!$mA*Ka%;gdGN1MokR&JyMB}gi6wH;5rRItMj`$h?zfn?KiJ7F6RhT zDl)MStzxsJ|8OvCKAc@ao)5Hz;%qq+OLJoSQM!DGBVRE0snLRtoA@cF{W%{0$v6N2 z09;DZtm+91ON1|IowMu1$N3h?(nX88N}IvM@XZ#)FZtik#U+^B+vt0ndLkM2$Sif3 z5KmEO_GjOI`|+owt0%;kIlRoI)5t8i;Z)n&mza51F2rIo`zdSlQ!1^iG#W+ z@iw}j9*)TSU^)||r>$It7om#e5Sn@}&GBzgq{qqico|tAkC{;nTL>h$8%=N`0dmtG z#P-|qyiHu_fDc*jdr1bwFezKJfS8+cXzE8T;3w7cZijd85e#FH*cF8(O8sNoY$UTk z8}vdt$ntfpo4x8nY(y$u&YGRxP4S9GiY>oH^q1*dRHdD#<{-^SYz4PMb71AnJk(~3 zRV=A`w1jA4|k#EpON}eR76d!kh`4{wY|l`#yO5cjM>={u?UI z%LmKb?B*_>Yy2Gplu%I9rfC6pch^Tc+Kd?ba@*xEEYky1o!iNRCBvhlg!ZMRp*%Z0 zYn&o|GfF8+Row_wod3(VVS0d+Y}#CNQ#&1jx z-snZ@D8~~fGIYB3@V4Qje0<8l#Twa1iBUbSrkHF^$U$~!(7A!Q6qcR$={!}Vhg^$O z-1(>A#Ac^FYJjun7K^tO$?6gW6S9hs>#(GZW#0EnB1af2RxfMy`H?r{{chP954lpmZ6E!H3eiAak7j(JEmp{L*!FpSO zXp-TV(3UCA+yd@(S+_x~$HnMHUCA@@939r7crBP+JcQpz7AUik+xftGuLrp+oaUI# z)7D^Miuu$j&ffgS+$MAtFNdm;a1!G(M$7`LEJM%v_iU86>wQ8L^$<`$_n$ zY*>0G;N>9`Griv2Hhqu3SyYEdU}vn%93q#r>d~L1d%qZ1k)ZMQnj1^>Vz#GHo;?GZ z6!_NjAvcSa7mcvMQcA`~U-Qc3?Fn7*j|OPTU9W5W!n&FYNQV*=*M$z8Rv{#iRZ8Gm z&wi-NuY_O!FA4)rUmPnQbJ59@j8as|Qs7uP_euht1wT>Gsd&I$L9db>YAbac(0w9s zhSjVRvx7rkGH*Y{W9?=iYLFTlQ&Qm+Xw9;g(4=h^$W>wg$Zn*;#lTeXZFcX8-M~?) zhR&RnPBX3}`d&V>b1L?)k@|*QZ{xXG*~$a?0?AVb%YGTuwFSF{#^S6HOqSoXX<|re zyCK2JP__Yr)>luq0qNz7Q(TE>`e~1(3!FpM{^`jl+0i6mDPIp}&f!Equ0R3#@xMR? zvpYB#BPudzgs8Y7hL?ni@5s$q(Aj#ymBT{}ea~P1RzDQg>Z$`kL!}~|4f!I#9QR7q zH@8PxSf&`M8rtXJz_;09EAf2O&U$+>_w#VrIFc{0dUeLVBsh~A(d3-lMOTy5K=aGF zim}Z$Ml1AS0x*J*lSaaU8!Q!b1}*l0Y6D#cTWA*K2CVhHB~=g`<$6OgTJa3_r0oBa zj`PH7*l7^(jf_ppe0Ann;ibk51)60Hb0X;!e~@f70CVcIf9P5poA#@*7&m&yPMod= z)Hx=#-!|`07egZldi7rl1F0I}c8T{I5?=DSdzg=-F8)|R{C87|4!Dcsxq7i(;68Pl ziuLW%Z>)hy>S3@Om6fcZ@7>3&kd;-bwO3d0(Gp-VK9^e=@;Pv3b-Z|y9^|Mq2=3o!2#-+ZFpbPPmX+oK zafc6S`Qo0+=PgcxV5JG~0kUQFe>WJwN z3RZGM477q4RNMXf9>djrp{Gdxm@Nt8`a#ba>?lx0Lid=w+c05GMtl$mI_Y^5M4TM{ zlgrtds)kzFAr_(9Qyh8oo_fVig!{o(A2j@<36t%fp}SvUv2RV_6M3e)BhFRh}W#;JXa< zX6M1CPmpr@9(j8{;wdmCNE%;N1tzn94YZNG)p z9Q?&V{vufU>ZMVQ4frheUUlKsoM-9!rRZ1xrWk&C&SO6_JBFPO+aHyVdC9O|EaIj4 zhAcxu_Si(DdqvGtdV9&_K<~j*0Fkn46C+zbg)BG4Rmd>2CM8E0Ms4{nu8X&Cc1X>> z`5Q+#hw{;(Fj*%+PWuK8M8}dXgEW<5bQCK@rV>WkVp|n`6F2P6LWU84yqHei`(!kn z;w01!2|ofB&qngnQHSXxWWTg0r%LqsQF9inp`|N&6gZW5s`~B|aTxcKK{KV=heO5=s z8W5$UB>OVybu!lM+8-63=5$6_sDv6Y88g2Yi?DouwV`2Wt+MIo#77>u#Air4J@7-O zGjF)o^|`Hpv;PoBeNTJCNL_0cf*T`C!anbuZZrSDo746mHesD)%sMjR=8Mb?^+H{$ zjT}*M9Bo3QSd6q#w*K9>BGU_Hg`sbYa(mmSpv%Ur0EmNK80!_+MF8{}L^z1hyn?FjsOSc#{p3X^kIJ6w* zjU5-F#BqgMO6bGEUDM|QIlcx%1Gr70xj3>4Z#Y7Xz!Aw@6N`H)C{>-tTp-F6bT*!l zs$r1q`~*}(WTcFrWxz%(91x68Z`p6p;XY~DsV0nf!w(gJdX%+)JuRDl`m6E)cT8cVM75r>-iimla_98H6KPMj3G|6~a~KM6hBIYKuC zS8=*gkDl_?oiWNwsZ>IA8e27j@XyPJ`XJHoKc4OTC2FN}Fp_G;LYtE!r|(ofYcjlT zy%E^*R!y4|zEf1p?9y+W<8VHMg#W?2-ZpuPn0^Lv0`8~9isp72b$3sWlY7lLp@#{y zifq*wQ!weQFN|`36Ab&7x=Z|vQ3PiaJMS$30=?m@Vr|$0i=BDGKen5ir!fVR8lBTl zZByBK%Utv-O6yNaE`*6fLXTYENsN7K*NP4IJ2AuefTg@ESp;+&{qe6BYHgZvRd<4I zWf$(r?bp#oL{zw(lxQa*ygD$+reTo!e;kNakKhd)7UO3jy3>E_a+|Jl|1zByrZ2%9 z#U?r{unZmaXnjP6VH8;z9d6iZj7zo0&$%bi&BvKKDb^Z4ZQ~MjBN0CX02t1yeOslt za@X|#q5+^H(B8~u1yvA8xa!?6xwJy255k!#$nNz+j&g1Rn?t*DJ|V({rWgRnItL~2 z1XLANFY8Ho4&nSV#;Fjt(4l@Fs95^ezcjO` zp~0(4HwByU`guKd<)DL0kuy6g+`OSl8JzbQAV+0YoW*ktwpeY67xLKKRFbK@HAzoU ze{XkSU<cdzlhIkGGI(BO9l7vIWvkVQUhK-+7mlGVedT^T0aXA^G6`D(j;%&4|_54wBp`Zf@X2X zHfwi&k|jv13GlTm%Bpo>lAGNzNbWl0nbxD7M&r#V^E=m)K`V@Q1ZpT^ZqpmcZ@SVZ zGL=J;J5wrN!(BfQxLJ>dLL;yVasN?FRc$?+h2L0KCDN@SO zT~poort5R)X80LZvk|E<7PB2zjdLk-)+Z}7DACotA&T2Yt~k7LWUz!BLI4jTcm$4& z0RoTH?jcdWJ|gU zI|8N|)bB}E>5K_0=Mdhzi8>0d6c>&dK8}hF?@mq?osT{$CG5AF-5x~<{}2CK7p`WS zC z*Oq^Fo;fEcunV)_Hg7i}6+h+Bv)n!?G07~@x@--Js36or7)6{--QS5PIek8uk2}rF z-~P2M0Vx2%JA0k3Vxu4uvC;Q#(Up!r!NAvC#M%y+g$i09oTIs|nl5Zh^aye&_S>>C zyBeu0QjB&7qxucPzybE35&>~23DjYnTmt0CR7_lVbSzO95fo0$!9n?-+aHMb1Za_q zUXE+h1&(4o9Yb;URb1yrvyrrGswZy*XgyHLG(%yoSe_#&iRcnFAx7Xiq85%9W=bd8 zh>R{Liw~?NM4SQLH*djux!l?Rhf>$P+!4Gs)vXW4CB(yIzSfD`yxk!Q#(plu+?nFe z^NBa48$K@)77@5v5F3EP z_;6%70ji#u622DP;9!=m!VmQ*g!S zSnMgua}DiK-D+3c=Ss^^a#Cf6rQlbGg}+R|nS5>fK;NO1^FeYObZ ztT^~Ltg^S(*oFhtzWH~Zg(j%PDfFlkXCHV7TnbWwWD>qipmm&wh=%(ba4$#I{(zHIp$Whw zJdW`QIOH~LDqm8Onw zsZ%v&!a!jIE55v-!UmC$x#Hvt+|TR;RO97VPs1@rF7^>-(@Ltn8w3el zO~A|{#H1lmlvLF5D<)?bv70eT?4&ZwrE0AvKx)OYcI_l!NH3UxXj`(%Ap8CKl@8QZ zB#%#@yt&wIDT9q#{F1EEL`QxQsNP_6yt?HiNm1lJNQC<4-CI1^;8;^|eiT{j$@giA?d`y#)qQVGwfG|5sKxpQuEAZI0ekPU zo*?AT?23h%n>WuG&Oz|X|KLiaGXZny^vIwyq?5xaOR)}qcxyODZ(ZIBUh)~)B`$t@ z!TO-Ch=|!4(>8lX43O~0Xn!p;(c?i=-2Jj%s%0T?(N3@q}p*w2qw^UqRJyl>{O?_T+1B#us68gi|&`{5jq8+-LM zae-`akbft%tO*zBL=}zQvf(d!x>iO$dUUW}LtdBbTiR@pZ~K2{Hufs>{OK+sB}oqTufz{cF-I2c~f~F@iH7Hffgdl*Prs%+yjNywp3N)agNIBcz3 z>_8qBtP;$0h-fQFziY;~jdJI-^dC^9)6y|p}4+8f;)XUZhD!4H-b*v5{>{u2gR1d_lo zc^=F?nahn(;cDu1H=Eo@@Nlcx>m7!OeG;Xq>mC0Us*y@zORhu&uB^74jqgduThz)) zNE0X}bS!JU+f*Cj`TP-F<;l0zAlzn!|s6QUeR!xa24oDEX5akFVG)9>7WHZ z9o+^1{dYqxK0t-{STNob@}H*!+~P#f0IFu>>0|w7TPq+IgSI&(!8Oox6B3qG;niBj z+ehWgewNd+@~?I}1x7d7X;gex12Kw9?xN~Mc=g@6_p%0!P!{g$&_fFJc|ME+PW8LV z`@FZr-T>kTp%N%BqDnuPEPn0>?p%&PQh17FondhE2w(7#GU7@y7x-{0QHtEm)V7jF z@$1P>ULvXSLD&FF-wlS8fu6qBc@=}Y(nO-RA;*2N=8xBi?t$?vp9|H6uht>_IBMC(gn1q1`J6>1YPOoRAEQ;hKU*gUg zuzXT?B*N1kr(f{dOl|NHUHg~qkLDR&i_(jB^oH}bXFtuXE>x;8hak3nCD(UQyu4pP zqA2A)9H<}HoHrhcoFT$QyIU#8M{Ec4gD=-9ScJi?Z^UPNK~rkXQRdhcEf^J6D-Wl@ zo9`ij{w0OcQw(DNyf&9pJ8l(o$LQ3Hv#or9K4d;z#g4)+Ltp0O|w!Abx=-m_MuOB2HqE6f}8UEf^FFIJvHHJz%F?*SWZ zU;(b{*BV|2#%b#5C`c*PIi9&QavPhkf&^+Ftmpt;`MoyOBL{K9?HDE>?Hc3KcNe#v zD6L=0*!~1TFs#tpYdu$1?gzIap>wV^H>b-LXG%<76yV+7v9jcloIOUZP51xQFpx<< zuPNdorPT=9V-(Y726WRez*=EXM(4*616fhlBnJtCv)dkG;7M6W^jsOr4gn`1?8v6G z_$C#nVBmpu!@U9R-xV@~gUYKQ z&a7B6DK@EPzckeL1*Yau&J$8k(bZ+G!{4cTdFpZTv+9&2-s9lXYYYP@H2}CY?4nQ* zOG$`*%hv@8VFj}la^dwM=*wAPcr<|%#Fg>b=w~#cWF`xArEnqMOKDJd(JRJqkXD0w z6(+`n_xaqa`9mGCS<59T1f>eA=j?T*e=Ng^%0*t%(Bq7*)& zAJmQZv}3a3@zhGE7gppE4Cc4Q1@e&DSRiMv35cche7 z^WH-_W-X+g9l;zPWuzaR){z6)CQ^MNWgp zgChRmjNxZWj3GMc=s_`31Y;uB;azD! zfl?Lc9QTpZcs{cQ;iz5`^xu~)9NZ)~5UKCCprLL)*e@otCi7-lgDpqr%K!6c6KUhAQ}VN;-UVUReXI#QMwD09x1lf2RA0pT z0VubozW%~~&nUVe!wl`Z!$cBPYCmL!25&7`anBkmsOl;v@9qbL!>U)vlvJ?}W0fJW zFT1cVW+tB)$pxc))y(ik_57?NWHahiA9@$;83-EIby?bRJ=8@+TK*tUqnZH!d1RV% zSlX@l9TWgaXC58$OdMQnj{vKww1?=}7oA*)Li|gOzX5o5k5^SJ)&2^#ru3j>%Y;#Npub z)#29{(y_P3Wri1+#=!SnN(n!gX-IM8O)n7ZXos!4EZ z3A*X90++D>MoFCCCvV49b;I~$F46@m4FFuB_;l!dMCsN#mV2wua60h zT-#62P`80Z3|i?P0owC9`i4X9aZOlXkLf5B`b^YVr-UhE?k*ualHg9b6$@m1`PYx2I1in&VAfMBuc7MQ_F37Z(LvGb^&z0yi@F>IhY zZ%)Ja=b{U1QACfFu&IXE)2ZfK>UQn&;^e{1U5_If(JFI=t|w1!psr2_g^%R--Gtyk z`M7`xH=f_WsUh1oQjgW=7Z<0RC|i>4HtKXB=-oit6RbT}Io0aNn_Wdp0G9v|+}I%p z0erQMpy1XVARN`jel3k8n`cHS$q;P;k1)emQMZRh4Z4$kidmArSyhR9jI}!RBxI_IK_Wj&CBg2I@?L(@79jvOQB}0=ixhHgg+K3axO$EveKR<*#Hw01G_> zKl7|eMAl=|kA!k0mxy~Rq~DQ$Z?-QXrVspwHc$->ns(O6cRA2@AUh>_(vY{A;*%f8 z4hkw4lESM3rPxMdA0LXu#HK_#Y#Jc*)I+QBgXW?~vGbiF8M+-4Rmv-Q6EjUFAO}f_Vo$n$#vR@rXG7OU_XZZZ=%$2Y&qu13kF_A*UDINjx>H_ zj7E}jXrxEa{dlQm3baj9Lu~|N7NbJt=>BAvG}m5njGkZyE1J>10VCNTvDg>Q(9=LZ z-4!=6RiKab-a~_*62((d#&3Li^de`c@d`^KRA4owGwpdE0QlOQ2sC!dJZwBHN*+CZ zK6#>@ghK=3tn%99nrWkJwl82XUN3b$3-X0J%LD)g^bw5#s71_ycLdYLPbgxSJY#encmg>cB5P0IoS@3vcf3sgOvY6Q z!J6@V0FLAk`HwTe zMcZ=`IMihv*yTVz4~;Ig5JpjGlOS`*Qh2SohjQd&1I4P!#n*H`)s3G@l?R=RO2_!ai^G)8 zJJPKo$~n)j25HNhl$DJJ_Q8*2h?&`wg7TM-NYijcR|uiCc8)HbR5-igbfuAzdT4(RSqr zqhRKhQolu|Q&|pn9f6P0$0FLh;M8_6wA@O=7j9Wk&+_L&3s@w1{z+tHJCFttrX#(R zUZY#tPVE|WAA^NN?WGj&R?Nt#_x@xAiP+p~maOUo7|xUON9KJ_c%>?)CBUf z0w$R@3})FlSzT4;rlZn7!cZ%SX=E%$RoG`4H2B2}ngipMY^{XCet9A8rjK5hn2txT zTnhHEJ}**E5!K!KkOEp4G|WYUoeO)55bV<@R*sl@m_;hfVQ2op{)x7l(U!}U!K^ST z`Iv|oOOT}pP?hN(ID-gwFeAazXhe)FEGPm$54w+RBVET`5w@^{5#EN5&`;@~5&7?2 zh9W!ONMDi!tbrKP7Y;noeE~GLSAsmeku>0;p&A%VqtQ5eJ>Cajv+u~V*TA{#SjvV+ z!ci9l`i$!WOs2C5a|3D*GZtg3EAF|Enrr{0CAJea1u5*7&9CDpILEDCQzJZTqW}i& z4@v6)jG%Flgv)2wnRLSSkP!=4`5?}m;!4HHGHNBMX)zLo?zr^ zZ}4%E)2af3n7^%>EISmcm)mGYBlaN%R!i&MSM*E5J;{P}cE7SA}MNAKs`!J{*P&N#d8_r3x9F1uQZjP9khcML~X5 zVX(FY_Kia-v+H5Hv&tf#`5+qU+)`qlvqsl@N_NO z2O_$*@aF++?Gx3Z`n%KLi1G9n`Z~f;SW0aZfegS~M94MtE zV&pEyQdEGk&8(p36SRfjly4M2eXH=&NMoBZ+SB;MvX5KyH`a+a|y7_67MN)dotj9)ZLD~XC1(n zHcY-=vkh% z+T#YY8{Z`h?RoC~HI7c)k_?s6+_MDXTpLf;)PDF{<@+DUtdG*DY z{gm_ZY)*)IfNQ3A(!3-piaVII1p;|ZAh-pXFQ?A|6uTB%K@E0MgMIQ&4%6oDehA5- zavPQj8Y_;Y_9~IFEne|G4m4dSZ6^NOY)6#72AIa}ek_@jZejuJUsPz~wJB^aPpQ1W z1R`F#YdzAm@%HE4MyZ0W=r#cfiRn>0qG`NtT7P%w!j(R{s zx6k<}Qk=B^G?QRtSa5Rp!Wv+wwy9>{<3hz0yBweH}-m_ z4G?N82imfp-F5#c=NXqobj`A>Ub7#(SuP`t9%0^IZdI@YsZxrq-yY12d%u&IZB`@& zQu#rg)f@FT&fb5+>&Ji4pDPQ`a96l0bRcT&iK7mU2?}A6S`I=7w`qZPnB2d}+COcv z&M#{u{H4Zx8pJ4aB&%L7z$hXdE7w;HmhkY?WG}`Kz$BO%Q@@{m?11Y9*_jk7d{x>iTBE zIOAMYe~`P5jlQK?C>|(#Zn()He8%v2P7tyGZIIn)%yI9EPPtUa;8?^t$_<(tL3Ngw z^LjmER4HkoG1+{$bVX4DVNQQ7nNxKFqtcLlG9lV|r(y37ra#V9$OF zn)3kDvxY{DTp;O)mKDkFrI}*WK%*L_!dL@#?-iE7=p(8A%2nZZy?J%?-S1bE73SR* zBhA{g`s2dEcO7^0;(G_WBqKY&g^8nXss~(T*_;}oz%~0cXI)}P{hLntFm)Es;-S>! zI>1eoIoM?KtWsvQe=tV!3{ydudTb()pyOC!I_V$I{_%)uxbgODF_8wQ8SyR!L%bqO5g87BMO5EwZjRtpw{vYv7bnbdmX>dH9CI605)H}KxDVC0{*}R6=%w5~ zcpnHIOlROkqn%i1TMC|VFNyWqiLM&TI>>&GwL=4^7XPWC4J&&s8&~eO|1hF7QN5s>G*b$bjRDTT4TEN zmD=Ub;7|sJ{OXdAd0J8oTPePn3i0ot?jVZd^|#ZkioEiZWYq-uTAX7-PbpV2{kY3l zGeYQp>xMHM?oenkfnnm9P{R)A@u6ecztPaGHW9aXpE|f^sJe*2MMsulUm&n*Hs??3iG0r%<|tS6 zue{Rbd9?1&ij&%^K;J-Y4F-UFT1;r&4oR?-pQt3EQ1-X>i7z_NkZ{KrGmF_nX_@@x3$I+78RyswS3+%fj$ zCYDHycDbZNn6E{akn#OIgjO{Cr=cmwlX7v& zi|4gTul^dbp(Wr<_)vod6O&Cw#PgR?Cy9iX!UYGW(anu2GR@KZ_J4FWnVnB+^=QM14NV&9$r}$|f1)PA%n_@d_G=x$R+&8_#KqnaC1pTMMOzM&9`=1wP`>A0Y$r>4%Ez1 zPM^h^MSY6;BYnKCrs+uAoEu#o^SZ`r3Wb|Xwyf%*%Bxzy-3O{#HP?7|JW82=PZVVfx8dicg~DRBow=QisF1842pu*}#Wnt%RDqkTGb{^23J zNcRz#NoXTaMTa8t<9IB8KGV%*PlkbqR+(#PfS0Z!Ye`i3CBkzHzTt$pWjY#OT^9{} zLmBxnX6QPZE?O5scMz4p=q@|Ezzs%57drAg*dy_JETZas(Rp3zLL8=Tk8VhlX_NOq z+m;}sKW8TLS?myvmAG!p-~Eer^-!h{H%B>SSbMo3oB39eb2+=UO3SJ6hN|A9#2Mhj zSkcTjYUboAbZYzkZ7gcO^%>U*RGY2F*>=5S!Smx_3d@-}|M#{3BS1tm zm@E>CRx)W&vuArZ#tmxKidm}oTuoV@wE$H@e3BiIgaKO$YdJy|C~LyBmmC7GVe55+ zeeEa z@wzgXNk4Y{T>pPBsz-aAfQVrs!bPO)>!DiYe{4R(37+y%#8J7P|BfQP1)?L%R8_khQ1V!@M`Vmfff60jQk|4*1}j|{p@tJ*>wDf_z0qfXh}w0>OD6^E*QaYOukJRy zU*%UfEvVPK9>_;L)y(6bnOczG$eaBuI*=82%-wn7_pU~AWrpLQly2t|Nqj0`l5Xc$4U%0X!wp-c4V4WPkr7 zBj_iq$4a`+#4Rc?6+kVrNbpDnGL@`o5-xqQ0=$TgW{x)ELSg-LR8-n`Vrl=mXRG0m zYrM!i<@H~V%efORX`7o*H*obCWf%kTNBIM%@lN&UWA0oFT{#ENu#D=sA${s?i6pXG zvvg&sNM4uE8Ae|)CE7j$ueltEnnV)Ajg?`DLd>%x#sE|0Z0UNJ;+t_Cm$Hf z@Cit>w^%kbMKJ+(cfp4Y%U)-0;1X=Ts%J@0<)i4a52r|CZZWJ?6dD@{Z*ngMk;d#%ces%y~QQf$9vut{FJE3|59QNJ?WI*Wixa;l`T3>tw2i zTmevA?>v4hF)pzW3{p zaDj}DXasIlS~&7m4GL*ShqN73y4zKm^u18V+PR+bB4lg7n(WsoJOgq!iDFX?lK zEUtUmcyWL{&9@*aqnt03F)c)%Yp4K>)bW2oVYwCLO8>R ziz$SgEcN!AlzYsGCV2rql5()rA&Ad3Mk*GqwI+5w916BG&bWyWDS;zfRQ`^9=Ag?u_WT?mNDG>TjJL{(OwZ75^ypY}xYpz=bQZ0sh~2 zAMMEq=FBRCW~x>YHqIhOj(Bt!)-ue(uS#+9-WnD4&J9z^PJ%s1ApQ=|{Lc_jDj-0I zm&heVDaL!3G%uE`91|I(E?optDu>it=fXitqPhZbwYA-6b?W}bJTgbdDS~LPON!9J z8g;?WXwVe3Xe7WtZ-qoI?MtpsyttiCxv#z&psKL9UEBaUzST7!LZ!WMM|C9DPE(X? z?Df$D*(ZRgYzGH_1mDA{jue$;Wuojv7w)N$`T|pibPeefM&%yI;EkoWR}vx?N6Ub5 z+_)z2tQErPOM7tU>r>G^W-2EC<|;jy7o&Ys_x!EIdf@5Ig?iuP0+esb*wEIa0{!B0 zw`Z`QbdT?Lk=M{VQK0iOSG7h}0_x8y)w?W9#6qC5(5amwd+ zY{(s0wuJF$=Ys~x6VXeW?n8!;c2A8KVnky&Hz5R*Iq0cVtzLY{#9Qn{{%v~&y;RyH zntyFg7GHD5Jcp`yGeEHk7!3FT1{gHdh0x?+>zXEv&K#xaV((|1q#jl%e^k=3Ly{jY zv}#-H>;dcz<}ZY;R-slv7S3IRRjLk+(2!Kn%S{d~j|iZ8_X$FVga=@>?N#+M;II=} z<$Dlt(@~))Gjo!5q1f7jESznT1M?DzZNVJujint7fKrF(4TBgIZoDFuBE zL}%DqeKBY4>kU4_+kag_6}x!$_M_nR@#CI&#yd~sYfErSyU-4 zEo+w`Gt>Fu=p*O$kO6NDu0<885qta1YDJUos=f2*$Y^OWfc69X4S&+)ArNA_lm2|Q zh0q6Bg=^{WL=>Jua*A`0#y>jF*^+y^RZ7b?6_n+E_7GG-xZT7+L)zdyKz4SPizs3&5w_bF0s1)TsDqvUvWx&2x ztHD}Rb{+Pw%R&OrKy)M$dR|odkk9Q}P`ug+nK2$8rA4N(Q+Gzed%^zo^Idkni!VF| zxD&q-OgfTB%xwT_+s#&N+b4ZsKF*G;_Kq!db}OW6noMiw>&=9Tt8R0vn_lOVOll+F z&fJx& zj1fRCmv+TSozSWs&swG_c8fCh8|Y%08)Ky; z2tqd3w^-5qcA+EChDR`7eMLH?B*rO5rAJ^jPk%1`td485*NbT8vw~KBdc>4dQS&_N z`Ym8aTO~O`QHxZ%?~*ose7g?bt<(4mn(eD2Mf4>BM8{s>Mqqzryx25qM4`31y%HrX zmh^-eGN2qof6ic5-tdW1p!6MTx3&hJBBVg*`w4qQ6kln@M(c9=oXMovLn{Xv1e@(| z5aQ)~!tXaY+Jx!?1@GbnK|U40Fh}vue@`80MtA?oUy1F&-PAC-*svShvBE0nWXv40 zGEhlS%~z(?fbwZV)07|Z#tfxpfCn80H}CQV0fP}uT`~t@@i6Z!l&G0(&eaHmK1a%q zXJ^@D;8uM_0l|qiPyyGyE&pavEHFS+k7<=mz!93<(u)vNBqi5^Sc0bGWqYb=iv>%B z&FEbDdov_;Xk~CtR|QR7_G+C>#-r-P4j3DG;CpC;LC+g@Iv&3e-0p(M*}~qiynnBk zXpVV=xNC9nJ&AT6>IYwXim;#e*vfp2VD(x0bOAzCswJfGJ!5pGfQjPsi_+8%kpj8* z83=HZGra*PW^lva8(kGJk{F=Xmib174h24ThAmQKu8~F&K%`o`uA^lc);-X`BA+4a zHdd(d4AqH^%A+I}t6zjr+5suY(iCaLM4S6nt8`cAYRqKp}8ie2Mo8CNds2Lh+24&4@FX{*NW-F^8HlKa>*y44c_4T6`AK*@UuHQw>&gy zRDHnXfy7UGn}nRGjauB*l=o1%v#d+egOR$U-XSHthLgF?@qW|!3_kk1U9sTZGDH&* z*n1l)>+gnhCzK9c+0=dDH7~3)9)@IM-;K+b>#(y(N|Rbqgt}+({h^xCaVBbtAKITlfb2QM+$} zHhMGG7FdLQinwvEiq7Yz&sOF!W!N30RrzM75KU9ZErKzvDSE`a`i|`gUV2)05JUD3 z4bo4{eYYLNsJ{hsdk{?pis)^Vz8cUnG@-LSq~xxzaWeq444I=Y0UhB?h2SBc)not? z3smQa%Tud0G%;?HOD<*e$=4ZfG!lrR0kaI(MsUP39QkL|>NB0Xbji<$9ZCgY_-CA`Rw!5$rlv-aJzC#Ai?xx z(x9oM;Kg}-UZr_C_4SefKMg5YBlyq*^A8}%_J^)~)KUpC zJ#DQf3q(VIEOz6a2HYO3T&X4UNKaZln~|3^8O|i4SCYAZWivzsPCJ5WpFlSATb<4z zqwk0af%`7cusrXOnZUbD2vH6GF<@@Y{Xk4WnTp!$U&z*q8J;5+SaRs~6M3?s)yA8q zd?vo=IxL4!9)LzOoLWJgO}^0(D53Ys49D*QDOP@QPMqHWQ4ZVVT_gnNU?f; ztE*}vt0ew~c!0d4%RSWDkN}nL=QKj+aBc?4Pgk|iN14K;F32G^)6?59x5lqHWu>;M zLJ-=HbNUhLQJ>mx%Pi$lmv-od7;d!SjUX1h3L>t8<;bY7igFeX)Q3-p%^m+4^!#(uI{@@3sGmSA_h zka0m?`n|ecn1P$cG(uH1>vn^vF|y2X_6yFTkr2v&ibReYSt_w^Zw$J7LCE8V<0OnG zgpD7mAG?K}ka64sW&92riO}_H!W_;}Hn-pyT4a!(IdCTAy8aamMamMj4zG(y&CV<)N(-EMY;XagK^ zjkc%;OGd)6n$hIX^9VnD@|oujl>&D;*2Bp?729EQSn%J^sMRV)!6Djt{%`Q{NoLkH z`_{1EG{mmi`^|klqX$`{#;k%}*`Nl0Zy#y%plq23YpF_B1+e*qTc*OQvU59iadUZ1 z)aqt|t>cc66Yql1=uy*UvT^Z02`W22gH%Z9o&vk@bAYP118j(VD~HB55Jm}_@;-P- zr5rLVg;cq6%_E4{5L+5C$p&FT+%}ZvAu5o6m5@Qom8->w0l4SZR;)Vw+;1#QY1Uca zZX5cTgfMP8udkXki#MZwU8D|Ss>N}2A;(v5FekOQo9eVxZfxTl$ZdlK3 zID|&Q%3!T72So!-l5QRe0Mi1t4~_foCuiyI{Ctw}e7|ZieF!0)_a=&!FM_uUrF!S1 zYk1mgw3m40z)-A}2+~0NcD8J})CC&o{4y7Ym4B{ntgp4j2&0U&`3tS{4 zmv@laOACL+%wXH~w1yX{t;lw^CJ*G@0vIydA)Am0s_#2HPu%*pzRODd%$<7w3lQY; zgI()jM4MF8$jk5_{tMYU3LnbpUR5SM!k5J!>)4n}A3b2MN_OZ2%_JzVN@r}su^UsC zVm_W=LCHmvu-8V#=Gvzb2lGdjY1G(=S@DOI25!}2O zJ^l%q_q{*4?-Zn7iLmz+KP2Pw2Wtw9eOlR|Nyk{=dIumabKS4$r=Eh2)T^^Nbbv4N ze(qbSL}BW~P(#O;W-RHyS$_idPJzfAux5-F=TLtx`)nIlRI@o-RTy zsU*21&AK1@l!?$Qqb=zh+t$2gV;6-|xSGQ`^C5PbO)2I>W%WeZJBcdMInG0+NO#pr zfp#fWq33XZ?K}euT}Nvn87^_Q!}I&B{rX6*OF+#U>LSnM?55gW?8Tt6_jew-P=+%44LM zznjg#CC06Q`*%SU^b;ASeaZ+ZMV&rKE!Q1^*AFLqlm9D)D( z`D~mqWAre9vSd;+`x~6gn;Oag`6b$P&1!awOJc$E`c^JJkA?S(`0Kc8{@mRS*se02W4ga6pYVM77VIBcnqp!j8Ee(6_Mp}5 zzxB#w@Zncr8u{EQwwPIujk7|I1(TQOOUWjN&&$3^D~MQw&Jz)jnH!MryNoh08+UBt znb#{v9FPE`dv0-y+7;ky%z{qZCkJ6_u#OXj?t_%|@nAc-eR%hH9<(u#BHeSX5YZgd zgeEOK<2#vQ%MX-$1{9m9Lj7C7cZ)x{Mm|fmc6EC#klEaZMpH@=5u*&>!?tNNs$zyC zG=c;|w7a@X6q-m=O`;tRT+cGF)dU;dh+M^Z#gL2N@1I z9gAjNX-o76O892UBA4atpepX~x&_2e(a?$MR-A(~A^>L32JJm$nljH6X+Y191;T66 z>5&s%wh?_iuXi*z4f-nKZG|QC-a!djgO4qqtV3>pc-G-r;D_wFh(;CCS-3-2~HX|RW#fRTebnIDow5%Rf$&4dI> z?<=oBsetD&2W1OqiQ*C-Ws@Inj6<8ajfKPY6voUt4D<7!@O)SJ1KGF+VKe&DQO3XS zw@+N@xTg!kqk<-Zd8aD-fn+UMgpi9#yj{)swr0q^dIqDnaq`bAi=S4}E*le}Awds~ zOpu2kOF{yx$e%>byc&$T9d@TV6%q?nfil4ut-*O3{6DU~0y1}^{zxiczyo`GI@*RU z_QK8wH3E4~mrP7smxadUkGN`v6Pejmq4hP6V(_=l7Iid%dUzKCHgpBKpND3Cq0%Y+z{?$y>Vf5wlUrxRb zAmQ4W9cXA}T*p(EIf5AT$z4|dT@$wniDfF)#C}|l-ZdeW1+1l%? zlvBIKrc6Wjcr4+k)5!#40X;*}y z^&>9kjf8h7N;Nln0ymKM&}{TF1yfa!1D&zZSx+!C-M z1-3G>DTFn{=gS%Ktb_-QfL;uSq~NpGWt#d>UVs*v@oxrhJrIT2z8JBIKX*rA^$Qmi zLlJO#1`G74Wf<6obtiO)M9@X+ff%g0lW^nNJ{3r+xQ8~QpZP89EY3z!a&L2;`~3Yh zH7X6`)YI456^SvQ+kDzFzxx-r2(F85j3BsW?5SVN2T8Dc4{T|!vhVp?^h|m|;!wWa z1-C&1G<=8u>a02g$H+A-#nk(nqv~)jC-t+PQVhXFyi=;=XnP3G?#j~jY&`d*TaxY4Co9u# z&@_qdMZ2%>FfCZ3-NdhfVe8rZ2l!F^OH9`8gPX6Wh_^_SJbg_y7Gvk+wm7@Qa^{eA zSQu6w9vRRMy8Nw*o|2$yJ8V&*uqaRaRJ$0#frNsE}H0j(^iYcf9@F$ z#vCY}>&S%!M{?Ml@VFMTOuNDcw5%@HD0&YEuLiNrq~r@8KAelV?Y7k_e!YO5)j{B9 z3ztLS#o78`GPr6uca7*4u?0AuVMrBvNTwiwaJa3q%Wv!mEpNS_Snmw8d zcW@~wbU%%{HD2+`R==*GKsxR=7K)3rv+s;wFci&k7u788^z3r(5_qX3YHv?=RyIYF zV$PgsdC#NquBKPTUP*T?C7iv=2tiAerYi}UNq8&j1{u}A>1C{7#uYmOVKqw2hy7WK z=kUzgW7wyIxzQ!R!m)=3{DN`n=LIBC!P0mUC$(eL%q2nrj^MaS-#A;9 zFfg6WhKaCPKMxHqtDkujZ zF!KH#yZ;Z)ChU}bLXccOylg&`|D~5Yaq(U&qKRYSdH4E*SoVty0)y}crk7RGCbzr}++u!1?km87F*~JhzyTpKea6 zsay#d;ld%y4Y@sYT*;ki2uD=Y&Je%Y6_~c}zo{8lqvvQg#;|xLeV+YVF~uuJI{?vm z!9eAC?`0*9FOM?Z>~PruESG6e{U5ZT?sjDw;y-+`2&$U{R4$OC#haxs z`t#qNzfHU%msUmhV=0x8$#OM#6S1_G)u{Kka1_z=6;$=OWT>4B&hRg4kKq52(-(8% zd!SR)7tfHEC_JoX_EFrQT9t+v6;ClV^Vu!cPj*f!qV78$MX6 zSFnqw=0E5&o6~dS4K>|x30bsemu&!j(_mIZ^sf`-@$Zd!WCzbt|LDA_S@(34X%v$- z`01$4Rg|cOTZtqD)Ve!WujLS9*U)U$LsUpyp4rLMIH{Ttt!5Zts-uJz5~4gk61bGn zJcIXWkZ+qC-eJ#3fWk(cO8fiMZCvxn5yo}5=eTk?qndTiz-H|KHpB_+uS|_u?jJ-c z#~`_*>H>+li>fb-RQ*1_A{Yii(rNY`q_RAh^;RYMyW3@||K|y*V@=pWATdqGWekl` zTwy;AjQPB>hsqzYm-QcIGj*tw$L$3{J}SGJ6Td4(naf+&DEW@~m2;{Ts5FLd$h zOovM{JjfYwjK$o>h-(3SWAkOWq3=6RD)%0ish+?v0Qa!XsT5>}Ls&XxST5_mMZ=k% zs&-({C-$WCJ$vc&E_>FY@Ujht4}KnPLVEm|{v+#L6GaT0>wpAW&Cu$ItqCsZtr0|G zqqf> z6N#sxwcsdk$OJPfdW_Y;OV@qt(TM91_!m3vhal_MN z2_RN96V9DP1_J=gC{3iqVRnP*CLb4;)`~&uGJl`vrlLl~eucjLt)KQ-=E2Q@mg(4g zZhI=}0VRZ*Dh!!A5)Sa5wWEZiR=Qdwp;q3nN}qbXZ(~geC+83rA5kAxRO{V!As*hL-J|Op5K$4MNU5H!F8+^`p+VV9gI|DFflrZUJBsM zk@d#j&Qyq%LN|_R4<73Y?s~QQAY({i)CoSDsQ84VOZ>O(j z+UTrqfMxCU(%wCRH@_Xf{_E=AZCu7byF;LUxkT*!1puh6E#|)V%7*|!Os8TX%ThW> z6O)b{7(!f;Tpuq~H6gCx=X&)V7mp>#R*xlFW;A}g!Z>{$J9}h>#R#dKdt4YT0ZK>1 zy_yF?IY+u-G|&Hb$;~|&xJ$c{pNajUVG4=9s&9MbGPrBUrG!TK=_ZX8RK3~rsQl~P zv_|?1R)uoK?wz4woo~_)(tpky9caQ}-XhAZYKS(4<|G_ZCy48rBJ81$eM}sCAcO4c zh*W@FC+-g?8l(n5g1{;!THM^8d5+xp9!B+iH@%Pz?DspqkBdiL7f6!F_}4A{O*tPj z52xeyM@>W@>GvC_MKH;qy%==7aULL&H(UNHRtet}i5w;8-j8yqe-M!D$$h0x^fgLL zNUJX$nm3qUn0jt5HS;H#W&hT>(GJ*w`564SIzpC@+R~JEuzUc^V3E2r;vk%1b{>aG z>aubKsAD|`_9m;%b=&>xQ+N7nU)PqnxjVa6$I~+R>QwP&zz?alLNHFAu3sB(#NJ(d z8RGlBT?1GJz=n@fhj4sL<%>?OE2_;=dZX z$`>jc`h*(Y(A9baKKF{x<5h?z92DoHZ;s?}%z6%uLEQX%H2|uN!$KG>_Z&ydI%aTM zsWShG3upw*_O-Y&f2yp0Vx2lC!O9WZt@yu91|>l_JXlmTdQlN=mZeGvvy`z$DK+BAQ6od+jERp_U!ymS9b`+0+G zrEfj4|0pfb9*4C|-EGwAwZ`ymZ>AYn_cUd2VdaR|p;3`<%O=5rjlLg0!n~bu{0NDE zSe7rH2F^!`4-`5V_&q!+M&5}^DQhp!?;qwb*7^6EQ}0;Hd-ia%fKq)DOE}{q1Wcg> z$x*#oXHw2UGxF!DalRIF=?{?)t?uM^tnQAMiz zzt6cL$M^C>m_ectiP*q@J!5F+U|}V@`?&l(V&YDu=}sxLp}bGf5tSb7kWEc6&31_O z7XWbImZy9;Y{h66O<*>?tL({p9JXay{WG2D;HsdM_wZ zTvFwaK`e%kVfKITpq8fU$depYV!sDC52}kc{Q;Ia01BU6)@ol-l3$;ls52D{|NerM z8ck@P79i0Ym$Pfwq^>fMMeRfsWo91l5^wq%8Si*5S7T`ef*@lIIqC&zp?wtR3Up{# ze@d@qvkcbQM&?c+G&$HHSr<{m8^m}3X%a4|JvQ6RoT69f?YERM+#wH|5LV-7-xAC>{28Bo@yBWH|OP+N`(_8CKB z+9ik$@Uwt;pa6tv3wT>VICUohOc~nm$RhRnywJx#93(aGA6wG&wj*CN zLld^X^|dq%OT`1)7HOsVn-aHDkar$bnqmT3gS4;q9j<^GxXS1Na_}f!b{csj_!xBA zlW}e(D?bb+ug-t$;X@27Y`={6MbV_Z5f&3wX|B?BR616d5xZX)d@wUUgZKL4OA3_! z$qiv(8>9HgQeVzz1~JbP(HDH3v6xwrGj#E{6#Ubw&5Prpx>O{`;ZO1w0jHU)ya#%t zaDV^}!CB*`CCjKcaMg;X5e>LP5@3nfbC&IqyS|Xv$EoCQLN$RYw5`!ZJH*f%9%R*3BXf@jfPwJ#`S4PIvm-c}|@Axw4jA{YN-z&LBza;QS)X1YdoL79vQxl%P4I>AYJma)RF*EL+`WKfktcJ-A- zq`EUPm97^`WkC!$f-`?L!ot+ikQ<=vGn}rh!m5UEDT|w$wekAME0;sf2RckYi@Xpm z$(TAP&azGz@W#fGAwok~$h<&lBV+5pgRO+UhozuL-GO@*#+i=!chPX>NMAt7 zhxx?{-GX6sgbX2DvHgC#S$}mQ)GRnhSTnpD65{gUJEim(C29dR``W<$C3?{Vhlh@D z?aB&OxE#;@lq=wfa+?1a_IQaws&*AdV;I0Xf{jU45&x@NSp}P8Q~PRLQ){T`B?nx% zF|!ZWc}+slO${SZ7PhVJLgaDA@sZ8ebsrucuMpR0xaB;1o2dr(XPbMvQBQv!){dLM zY#lh>FyrLMk4ahcy=gxBbjkiM=#hSiR(s_C5OYlS~(_HmnQ{k|{z zJPm|*p7!=nqQQSXa-+YU1&z&M^d-Xq!X4|Jz= zbk5TlVk{mMsbQ4K_5({3GOrQs>u~O~7?8eDltnT0HUr8=C#`X~z%icB$%`Uqg$_RI zoHu^(0$3RMA;|@ra&gjOWCDksy??Lt&tH;g*JOQe{(tVp##?C9(#RnchaCV*RF9{K z*d>cbG4?)b(5&wKxxv=A*`C?4KJ!t{?>w&|o8e63N+SBvCPJKM%%O-(@cC43|Behj zT9%#m;PB>}4TLq-*rs4;AnLe+&wcr3zL}O5AR?*!xk^r?F9nB<#r(Zgw)(Y9a1W90 z4=z@R@B%@x22zih&JQXX_KvkUq-W``f%4h#pRXCeW&GN#QmEI!IR}X{e^y z6h=?_5|LEF5){|MsncGT<%!T5=T9!GG=8m-jvwci;%O@c#Fel{YmvJInhu+s{XRkU zu)#=Fxz>)YB~rZpqFi2s^M2Yc_i;KE5XqOTgZcm1bQei?`aBPg?k&WFZYbD8O4eF_ zkU40$#QS1^UkgL7E%AK8PL042(Z_Np#-#GCA0_A%lP-!59*8SX>&BhpTRpx^p=YAM zdZbO@$J;RVA6s`kuHf!*g~9@7RTU;qCPg|HoTv#oYt_nh;SHq(BnY;ij^=&K18<{_ z`3^lQtH}<91r_dofivH-4INf%=~DA_I7r?>Xkq5S08%qa-7$3i+PYQusR?4vZ2qQt zSF3wqNRF3mZdd)zFtk?0>eo-a*9o0Mh$a7Dum}ynw{{*UK{ej$^4H|8P8t_)9hTZv z4B|5Ww2hWv5VB=9;UVP-ey9f&E#_@BI zx;1g+#9kpKO71PrrZFR)RXp`dXUfW26?1%2cB%s6nB&@BT%}GTWX6l+Fas=H>;Vpw zyMc01_2!-Kc>+w|dXrTPLBM|C44B7F!_CVXwDAjEuP&ez)ER7!jqZB5iXYz{3kUtu znH6qy2a%z78_OSnlxU!R<<;%J))Gk=08n%`6vIn0 zOV7`V->?uw10_2E%eWJ7*bSbZ=GSU=QE>PgbVp<`C$5{XF5JA|Rmj zCnXubHhqIbI{6cUw&wyDYA?|IqI-tAl1oYBzuh+NT&D}HXY5?B+yFMoK#QxAvzX_%oYngu_*Q^3NRu^&Jin`{yR4I0 zG@3juLOdI$(`AuBznL9L>!)^TB=XD5j0V83OF5K92sWd@L_tWkb(?E35JSl6$SO=$ zjv?uO)5r~l{FA3;kOM;rKUQT8wT#Gy$BkcKDg>QMh@3o}`a~17U;pLD=Q7lwui&F9 z>v?^V8>&5dJai^!fv)YvM0W2kvlvU|rZy&`)b+0N@EuL%rM&HzB1;8(rqTDxsRD;S zf)w8S-Zmpbi{3IG{QelB;}6?ZE5Z5^;8^yUqe$8JZl8-Pr&o9RGWcDT9rk$Q_lT6O z`s2Fq(E|J089H{EByzCzTnC8{62w0UhcFOooHciyH!l)+WA7xoSV}vQe}+ERvDBuD z@YmdX5JF&le{zISCtSE?!J6>a5&eYB%Sgg2EWUrp!HdwkFZ4|-0fX;y`r)ub4LV}I zc0hVsYtI%p*wgdi1-)OL_o&uzD0dn3tFQG93A*6+5M>W-!)!isvfay45=vyu?(`)f z&zod1JasR=bs_2cPox>$E;8owGtg0x1g%)+QG{dS)9R6Osknb#N`r}jW zL`?E{#??|OL~d07L_omEKl`Pw#pS1GWT}^?hHR)aN+?H-g9vjMo(>4hsciawn8Hd|b@0L7$T=c|@~ zFX77f!5rzCQsWeFfy(LARChgFkNWi%XyOA>HN`BC0jxOrl!1sgB^Z#b~1I>`-bxKCXabUE#ZJ;3t{^wvFq@uJJR9MO<` z@QGTMtG*Bo(r1#jy#^!zx8YrYMdO-MW|~saQFcv(FFrT?; zpAH$@k=Go4Ti%G=5DI!F#1wtQ7l1eg{|B2?g|8QRw(zq%5IO}WzM6Sp+3l2v++U__ zz$JDx$fTv%)r2C#&I3lBMw&a$FqO{!@QS;hvR&I)TCn>Z7^enCLL3d@NrwMs=&}C$ zzIrihV-n~NiU+#@zR#V%K1pE6#t{_f5>f(y(&CL7+Kt^RPwD=yrRP&nPe4$(pYu-b z_>%|1k^_u6FbqOb51+-@M%-5<{NiSTah)`otsbw4h-#C{WBP}}L@XcJDtb3?2WjnQ z91^dVb)v%*;Z%*@;joy;#B2ZD~FK%pBM;)>|mV5pQrJ5R-HDSQ&$M+TCX44Mb2LN$jes|h)(}aAzA#nBN zV?6fH)0+et2K+!CLGWq@(oXAdyJEya086?AHM<08SMMgmzn4wa%bkA6D*oD!K0Ce^ zzd3_ySAYR|vghd3!p|+i!ohO1Fju$ZicHB4*GZe`4sl1XZ;>b}%91}M%g;1c8uJ_cWN$y@o%3n)zBwsccDWg`y>wm-D3u{{K+pDLV)e7f= z*`1*RKV!8~8e)qB{es5=bi=L*ix~RWMrr7sC9KJTZ0FCR>qxX^CM!DD7cFQ|OQg^D zt01WArz{W#x;O zfe@YnwjeWCpIPrVX-f_5u2?%qu9KY%ua*UaXhs|Un##mRY<0IVKsoyJz%c;NMo(!@ z<;v3R8K%9#(HpU^V3A-oCGb)IAlbp>5P*r*UfNcGK{jQOtW2=Y;nhE32I73wo(9&3 zHKsW8K+5OM4G2VIB{nAsC#)Q}R9X$0m|!tQSqIO?FhTI2aL$2^5|M%+OJL|bdMY_m z3Z)LVC=1}avKsd;T_dWdptrE4;pGPt@h1+<2beTJDnX=D&lh^&kKOjNe2Gz zN?Ny>t8g^pEM^bpaEV~dPHt=-RBHtL-QfrJc=`!na~%tnvl~07uLb48ym}f>UR5Prh;8q>o z?LkHSMWu|jFxveFfg^%#m74plRMDV^m61x+Y65|E=(i%$hLNl^99;tZoMjQmoQ`9FylgE={qZ5FoSu5hw}l5pF3%&>bU zB=0E=suHkI!%?ES?Eibmas!B3>84PW;i6-p{>!j*j_&`BUY1l;pF{_f_;d&dZjjPI zO7bHiyA6sp>)2WxzuIF~L_}6atF9t4-i-^vfnN7j*V{@atCMh&-Zn^6ejeK>Fn-mq zc|3Nz4GC6%9FpDbIe=+d%CSv{OtZ`eICT&xyl~%jyrI+*0x=f>4wXx2>p_QNZHn)6 zBOaZ}ELd8p&jfDW@rc873l5d&UaTlar>|*CaaJlowaIFKRrdmq4Sn>dDZ*`cn2|3T zCCF`w2WD9%tf=fx8eNeofgtqshW7f}rz7>;lN&3>0>Ix#VKhREZ3wBeU}o z3)_Ri^QD}U>$))B%_x|+_vDjgLhjupdV9@xPU@J25Y3H#nEQZY)`d6=#!;G!DkhJ094%htDH= zZ{42AQ$O81uGVoz6?@+!t@X{$ zKI`s-z@mL|pp*Uzh!P)(IXb3Hb3Iql2jDHRLzKPOH6;9%4rv4a*`bH#v9_75i7ML+ zseFswL$vcnF2)DD+U7J4>|va|W5Dkh>na8HuZ9R&e^#r!ld~tw`bt8-9}}+I7F7tIu%Omgh{ak zGL1DX6JC|gpyxpFSFKoIJea6_u)R!yKv=G^XMqmziC~Umq%Aow3q92LZ}_Dg0FnxT zsArsD40+IF)X}KoUTN}XRgZ0)y2_^lIre%(KZ)ZC?FqqSDqBegH~`VWGB*h@?~AwH zu=RCM_|oCq?Ve2FFDv4m$&eF-2YDZ*a^o~;!BbN6$-3YS*VifrglFfuJmTLerWWWb zjOYpD5qfG)jhzV+j+HkU(Gz~@zpwrhIsg=^^%&d@8m@mg|NA(PX_Ys_$}RJ+mkobX zGj!U9xKvGH`E@@C79@e-NmNk^Ul1O77pe%EWtP8hQV3Q%u~c$8Ot7;hu9_X5YpMOn z`2XiWaO0mth&jaC)E%w$P6O3sjQ)!*@+-($p1qSeAiOWk!{A(L)TI2ZVk?Fg41QL) zw9r4SN#z-w7JZ*+q`#oI_|Q=>PZUnU>P z<$ktW%5yZT=5|7C3_2mwo41&$`B1ZDH61)Dy-$bFeN2yFT&=`vSyF{lMd)BIid&kV zz&JBG@jUvvdr){8krY{k#-!Dh1}YiCk~jx$t#AVf)8-U@DfC`56)$fT7iZ$CU{{G zVpzib1ZXNL{WX5}Fn0RB-dW9?mCz@K0gb6Kpd=Xez>&vK@1tYBOqCaFrkrebAPf@s(&U+_)2-8=83%+I5to!;X~z+bTg=Y zL*tYQhGXpb?taPx&yB3g(_Gr;skg{ED^u`Ceyz2S&E zx$;eIcPk`|aB#;jH}In59x;(jEU)8Sr*dT7j|F|(3s@vjicoFw`4aUs8n6fR=^ozs zKhzwzF>BGgWbN6%(wP%tDIB_KP-B_`3K9VvgIw6G>~j)2ML#>{$qq}RIBet=xwCPi zB{328YR~eAO&bM8(>8LqAFIE6ooq>0o`&C4vSlficP?Y?k(bM#+aHawyqlyg_hh}- zJ(~$b$k!umRnGa#9H<&4EZX?xG{lEdOn)W?z-t?Nn4GF3Q9idUQEf_0BS4g@9ZzMIsS(+I!L?2@t$O*}IVM85lpI#mUjI9f z4m$l9FygX(R4Mn3mI9y{TOD$8E)E$(=CM^sbd@d}ov*I(TQGV(5<-|b_W*S))&6Kk zdGgD7X3mZ-EQ3IjEY`u%iH8a7>CP2u`W6|g(j0^8wt?px+51x_83=7^G2MSi1gPmJ z2|Q;`+2&(e4dCgJ@O zX>MP*VXf&`Io;HgHMV>UYc?a7FlBhp-e?a&axI7%S^%l{&S932vl$UzJVjEPGejhh z{yP5VSy4hNh{J#^1<{@Q!3|5TXxusO+K&yfVT|0vA;14%8l`|8$m5J1&9K77GLCEt zeh@8LI!ARhy|ac2`<01&2eq#>jJ=5ylVDso+jNxf^#@MY74XwKG5OPTh*G+Jj{V7~Anr z3u*pRM|aS{{VheI=i^LvqcTo+iUMUX=Jyom!`eAl@`lyjOsZ#dtUk^48LA~apfz~B%?WUzMBKwUJU;{!f8r<~i)rOjn7BWp48Q78BwI9?H0gti*%ZatFZEd9H5 zBfynB*(~s=KnLt;3bC+nrSm@|`J5`fx;O(KJ~rN--xoF@}u4q@ql>0Sa0-S;kO<2NJN-`o$kFPu)W zdqjZS&(@LB*T7-4(|DS_1r4+*v9Mk%6?x5_5;HyRGjqWG@4r)S;F=&YXvbHF+A@ne zxME{l>FAnCt(OmuU*~5X=koHt+o9_4UQ&pz*>ex^`FX4eh(xl5BOlOYe+7d%!HzmLu9MLeabw%KDUg{it5oj8UY+E>dz*M*5vgVQ$!m8smV2=9 zgT30zq`QtAYRa+zMYF3xR5hUJ7+-+E-rI;^shpUPnTawo9W8+-UG+_m7}^q zDi+66HS*s*{(*#10!qyjTW)465`Lc6Et@v+`~e*k(%$1JZ{vPMEN(ZTm$ipeKlA^n zM6eFvqMh7tsRr@_7nQ!iCwAT0W6-Qo1|wl8_n3b8JDYfYdB3zeqsHcJ6ZuXm#dqJOWf ziy;0t8{sdLtqp*M{TKz{Ck``h>e!`xpo8dKndaQ5dCdTtkI>@z{BLwn@`A?TW5PB~ zvy?$FPZv_2Wt*52s?6m5nk_WdDe8ld#$$ds%7+u|4!CF@2T|n69V{+Y9+$@Bm`yZa zX(->9W39Nk}qGx>u2mlj*EDb zT5~Z(PZ5VM<0Wk7JRv&IT8%rL@cPq85+wjXwhCgj$(`iVDxRIRD+Ddx9VspLXvcN7 zH0+A6rjhf}%ad=6b83bGPZP(_M>u_SODjw=MNwoVx6d8kdxH0?mU^#SgP9%WxuA-w zn*&~RBaY_&cA>?~;+IHI5Q24Pi$lY89=%pZ)x$_m0@c}|J8(~eKWa|!XD1l$^{Zbp zWF(`hl+(VcbWk3vXi8EG1Wg^I${M)*I;V}+Y5ZTk-s6f60nMVZ6oQk-F2F<`q?o}I z)cf@^qIqR85H41A&~~1l!8fY__^FQ%rd-QYG}V7ASJZr`weDK306EKZJlfSM$Z%DR z);vn|58WisN3mC=Nlx*Bk}l9jcsue-9D2-;4{(sYtd)8AClwHRA%N8DKTco&c-t|( zHS9>>EG^EvGx5pw;bWvaN?kXU#X>P0zVXNnjQ%h8Fh(Pu1i6-wDcw-(9$2|diCS(?|2OOkFn<|Q zJ;wwJPE1sSo;rC&O5!B`NJ|A)`%(~8e%_x}BV?XkwOs!u{*j`MoU8WXH(EI5@k>~1 zJkO;{5`i0RH;(dfakuZeKx2o&0yY2Xb=P&rq5Iz~y)-w9izeDH0TQ`kx@95dH>IG2 z!%2_(dMK-5%D<)ani={Q#)c^Mu(5+*2k*lcRP3=r1k*5$Zv@04V9fs zqL?E#bBjG6!XW(1(Zpy9DpdqK2^^O!sz4zWQU;z?;gC7vnVdDB76;Pso1bqfT|&_v z4Y$A)4pcMk*BecX9q&i9w9^)21EyU1?ce$!9H&RU+T(FDk1rQVu$_gQML3KJm;V`# z)fF&w&xaTp#Yq{;P@L#Q2H8A(s_QW@V7makyHIe!5qA7@Napd_h0O>&XIV0RZG*<(h(8n&uG2eO+NDc3VC>;K6nN-O3ZYrSzyrlt#l znc>Q-%P0}8K-qQ69%#_o*%LlQB7B~I9kW7_me*>Wa$fCtq~ZyJT%rt3$%`XbdD;S4 zm-}iISg<<3>_%1FMN7_xE7?5DjLI{vmNy1swgXka~{k8#zdAGA{z=-5PGu08nY` zGRU5+>@z}v1@Kd_2$ezaXA*p(D;_E=~7ZOk&N0) z!*?QSqun6wIL>fo!v8m|GXfOchy#I&jI9})p#+}YX%K6cKcN|C5peNZ$ps*CluMa4&4c*azq7rpp;u9SEEy< zZ}bx1X>&YPQkTB;1QdQd?9B7&%FkG(GZT8#I&pz#X)YjtcI%#hrkS+iU~>&O z+xjnT*Ke0F{xj{aYze3aIXNelQ{|y^Ya&|~Wb~yGu8A|^_a!RWJ#8;p^Q4dw-1)*< zCb~3mlUhk9xk?3Gl>Rspnr(#lhIx)cw;t71xBZFSL>{hoBH-y@ADa zO6f}EaoJsKXSacO#ZD}63?z6gj2gp|39S}Jjq{M7xUssx4#=imm2Tc#CD!C5K*X$k z?kGWMW8ZlQ_VfYg`qsmGD*&&S!>C}&6b!D~LmU!U#wSytYgR4kL3J7jT%O`I0U$)B znRRXRg#TOR?Ar{(i+2CtWsp^YFbGC>!zj~SL$;ptp-@6+51v%WVk8o5_JdS06hrnU zcbn5~a`}1y6Cx=p5=^cgUK~MJC8}RagRPUYKfGZ) z1tY?1m<&QlJqe=L%1&v~50J8mLyqImMQ6QIdNiOYiqhOCIXtRltH~ZPz|I2w-MdBJ zHE3WmpepN)=0<<016q#{l{NK?eDf~t`fH(22ADR@zvG2CU2D^eY9n4SHQ6n7EaNDg zYL>ej=nE!~Qm0xR^K_pm%PHzpe3i8hu~Lq+auVS|Q;E8G6}marL5!kW{(d>~YYCw9 zK>+Y2WoW~V?> zgvf3Fh!;Ry<6skO49d)Fu^Z@=Gne8NsDTng^9oO z!~j)Ew_km%2adi#@js42k?06@T_#R*mEbF008;3fs6&N>c+Bp2P~CPrMtYI2@V%W2 z6hih<>oKyQ=y3v|RZ(3-0_fSd+gW+QVcB;|jkp)Vp4AWt5M@Qw`9JmaV0X9KS@04S zM{w`4XotD;*%X5F`RZzP{NYG)TNvBfr7Pt7UkB!Y=X?EpZe@*Jz-`|mMf9=A%cV5r$#jIfv=P1h32c= zJe=igpJR!`gn2K!zv8zu_w@-@jTPfg{;!BEs_4Bij_+eD-!7Y7=$?l&Nn2Y`yO+;X z8Jm_~N{-;~I|_oE`)HWHloQG`{o4rT>ijqrpvjYEhj`14XAw82r`0>>0zMj!{Pl$F zQGMGJDir{w0o$$Xu-}d?QO-g~u)3Mokc9oCD+f4&dA*~-1b766v&qzF{9>Ekz>6`B zOmyprKT8$1eV#rGSS>3``RL?!p{z&qz*B%dZay}tu)5OY7oY{Tnihk3)ucHty-9#NvGkFpJ9rRgf;6GV z3m8Vgz<<*AL%|G9TtfdGk$6pu2NE3A%Z~pMD}cY(tl1XkoS0Ve*$!}B`P+rbSpe4k z!@1le;994cT)4+U0|j#xkDdj7OeT92!y=y<8`1tIlMShvOX?vmIpg#F0Ot`bl*mIK zulz{_a|vAGDn#>S3reJ1$F^<7@~L2K_>SfKNPwQ68wItUr)Q|f(kBcl@7Ip&?)l9a z=-n+|_4xK^DqVWZjLc~!|2;q@s4qkBxzqe>CIteV+|1y?zGOWo@bY2QajFo)!IP>a zbw|Y4>lZnbwwg!EHQsu2!zQfeb-Eyl<#tuPIGFy3$q5tTnzc&&*wbu&sb1>}p_bye8NJZT7)U*E&6&qg7OKxlOJVdcQQI4)# z0dLsj2gB~gX_|0zlLWf|Hfm4^OY}jeXE_*Ds;

$u6A|#izvI@8C1?u;zrkX)?*h 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%0D`sWp_ljWM=f=$ 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 Q*yWU_zy2%(07h8=0LY0*wEzGB literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/jaw-detail.webp b/apps/web/public/avatar/v3/p1/jaw-detail.webp new file mode 100644 index 0000000000000000000000000000000000000000..2da44686a95b203f07488670c0b5f9c6bc747a7d GIT binary patch literal 15488 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 literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/lip-shadow.webp b/apps/web/public/avatar/v3/p1/lip-shadow.webp new file mode 100644 index 0000000000000000000000000000000000000000..c8999a7d80b823b655a9242a1609b34c571aca2c GIT binary patch literal 14868 zcmV+vI_t$!Nk&EtIsgDyMM6+kP&iEgIRF4Jp#TjK4M1$bHf#ebw&;}P{|86Oc0xq| zC!k6aa9w1cof&98ZCemeQP%zbCBbkGV1renIrvVJ{WmDY*HK-dV0ZAJmIOOm>)!n^TcSsVp1lS&~vS0Rq(xp`|whaKC@B`Oo zcUzJT3^YELf&H@g?gk_q03?$C!U63>l3d$XUipOoM(X#Pq9}@{nV;>1Y}K}%uo}ol z4swu<93(x5JmvpCCQrBblHQ9136f;jZPS3BNyL@h|DOK9FYp7{{|T68ra>T>&&j_P zAxRVwJTWGkv7TjVq1$ZN9pq_7uy<2RDSxo9Z_5(Gp4(t)j9ji}G}RCazEIf6(}S?} zJgdE0PC#IHR; znDU>s&bKV-{n)$tpw@W6E-!u6>702*71pLMf$t znsqy+ok!ou7UH-Aa)gLx%NP@c7=}_+*s(kA1OO%gfMm6LmC#kw{_4rdK(=(Hq(r7g z8Z}a)n1&#iFy@G&7p3gWzG8m-&XHSpYbmAYoI2bY!sGBX>J{(*aZbg5O`k87p+PYr zj_Q^7i}O)#a$6CFdiVR;PeOfE0&EELEW|k$Yi_rfaE7O_M|mr6mq?9iN>NklU+_3I z^6Jh z_VrZ+AhGZZn6P^6q^%M=_w{!c){5rag;5RF;ljdc>d^ZMuJ#qDAaW58 zz{FfVxs$wbXnOi>uJ&TwbZ+V`a6Q63>$RERHk1;h+wlB{FPB$cZ6&rNk&qy{JRV?@ zl6H1sA4MIv^gG}K4j0kq=yNopKp@-(dajhx`>R`PXgD@(+5`_+^Q$VQlmG!x2VcMt zo~Wp3M1fDfBzzV=_y;fm0GRMAKw!|eji4m_S#P_42oW&>pxTf2s8RsEvrQ;w0NHns z2~?5lgFis2R;pK8rf;sPz%_+V%jX6RDh5l+QLz_jR%t8%n-vW@?PR8QLG@4$D*(@J zR!|58Z9R3s0u)d{_0(cR1!uI?i&cO#%^8}fX8{1ywzjs-LX(W)q>wH?%rHA=d@l9e z#+jYbW>{F<4VpogEa^6A31C~dkpPf#I*E7f2PrYIh zl$`CKoJNlo43;w>u&|gGnTO&8y{cc%(c?`zxk>N(8pW!&Bb5dBi-CpMP#$#TaqM8k|f(AVvVe-zW)EWR;39^ zY}-hXBuTyZ`3L+{mg5Lu+(uKUbmw|+>JjG8_G|PHXgi{O?!6{pKyvOxQ4_wkZCl

+
+

아바타 v3 표현 엔진 Lab

+

채널·표정·반응 클립·지문 파서를 렌더러와 분리해 확인하는 개발 도구다(실 서비스 아님).

+
+ +
+ + + + + + + + +
+ 렌더러 + + +
+ + + + +
+ +
+ {rendererMode === "linocut" && rig ? ( + <> +
+ +
+ + + ) : ( + + )} +
+ + {rendererMode === "linocut" && rig ? ( + <> +

로드 상태: {linocutLoadState}

+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + ) : null} + +
+

표정 (28개)

+ +
+ {AVATAR_EXPRESSION_LIBRARY.map((expr) => ( + + ))} +
+
+ +
+

반응 클립 (26개)

+ +
+ {REACTION_CLIP_IDS.map((id) => ( + + ))} +
+
+ +
+

내담자 발화 · 공연 재생

+

3pfA9r0bz;T?;b*vOSgsUiZN1yhbUa>0 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 C%~7EM literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/motif/bud-closed.webp b/apps/web/public/avatar/v3/p1/motif/bud-closed.webp new file mode 100644 index 0000000000000000000000000000000000000000..cb01ec2f76cd68d5311913466159718ae2d2c2f4 GIT binary patch literal 14576 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 a_t%_K%)ox*b5z`c*bEnFiYfvHxBvhy%-ux* literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/motif/bud-droop.webp b/apps/web/public/avatar/v3/p1/motif/bud-droop.webp new file mode 100644 index 0000000000000000000000000000000000000000..a77445317c4d098bb72f4e071ff92185f3fe1344 GIT binary patch literal 14102 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 UayEKfayEKfayEKfay5_u0I2W}_y7O^ literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/motif/bud-half.webp b/apps/web/public/avatar/v3/p1/motif/bud-half.webp new file mode 100644 index 0000000000000000000000000000000000000000..2ba1e43efc745f4d6f245eee7cfd258e5e94b481 GIT binary patch literal 14932 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�^L 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 SqTwyg1)mM1pHphJs2~81=5A^L literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/motif/bud-open.webp b/apps/web/public/avatar/v3/p1/motif/bud-open.webp new file mode 100644 index 0000000000000000000000000000000000000000..f25d2e6fa602c842f94f223f8e5e1a68ef0d5906 GIT binary patch literal 18278 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 A_5c6? literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/motif/weather-defensive.webp b/apps/web/public/avatar/v3/p1/motif/weather-defensive.webp new file mode 100644 index 0000000000000000000000000000000000000000..816543789298613915f7f1665b60cd06798b2bf9 GIT binary patch literal 14886 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 literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/motif/weather-energy.webp b/apps/web/public/avatar/v3/p1/motif/weather-energy.webp new file mode 100644 index 0000000000000000000000000000000000000000..67edd2e14f67bce48d70666467cbf9931850f626 GIT binary patch literal 7132 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 SaSfkLXNcMm000000001STzUck literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/motif/weather-negative.webp b/apps/web/public/avatar/v3/p1/motif/weather-negative.webp new file mode 100644 index 0000000000000000000000000000000000000000..04237ca918012ed85593081b099802a1cd75f4aa GIT binary patch literal 13262 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 literal 0 HcmV?d00001 diff --git a/apps/web/public/avatar/v3/p1/motif/weather-positive.webp b/apps/web/public/avatar/v3/p1/motif/weather-positive.webp new file mode 100644 index 0000000000000000000000000000000000000000..202d3da3fc2ef78d8872cc6e53f2bc621809f983 GIT binary patch literal 7594 zcmV;b9aZ8|Nk&GZ9RL7VMM6+kP&il$0000G0000#0RYDU06|PpNYM!Z00FQ@+qS7m z(*C~qBGN{=D($je(OI@_+qP}nwr$(C?KI-89~bZ5_r>_`Ye&Qc=>MNT9!ehm4J<2t zCe+HFPn*gfmAl4HH6bM36LA20gV=0|mVeM#pglUWN=^G0r)Bqsba!ZD&5MxdD zg*cEBIueLf*@LkG6b0a?C?tj^0OI2a0yImJ#u|#)2Uu({DY1v~EsIBJo&*$WB%p{b zKu?hr0uV&dxdkIqPmn-q2_T37uy1jeh{XXCC^0VsXn}Bl0u%?}>2Lrop!5XT1f=d^ zW{UGi9!v!XnEWzIQS5>silZW7&W}=*<7^0mEgE8>5L1nbftoQ-L}}vQ@l&=5MTpKJ z;CGlI5OyW1i$M_LaHE=UX@vn47Q2$w#R(8V@D$`&{|}kjGk&#Sjmbf(*p;rMU1ose zQLfOK3W(meDP>RgIys+UMouU|UzoyK0hllZtJ3yMA5IOSVm>KQeyCxsB+i0fERn0g z%M8m0KVn_}ro@@ckNOGtI8O0w;wT60#w<7mO5PmAxdSrpZ-t4MM#DK7l)%Lx!JM;8~v*V3qXp=3{AbV0cAD z{^U~uJliAy55WW_{JJ0s)GP|9Eii|x4e(Hl68;8|b?grmvbyFAm=Z9`6`N@d+Kl{F zgmIwGourF+75n{>kSv@n;GrUH0nnh$4G;~`99Iq#jC;#b+)oAk7!PZMrpo~$T?`WD z{GU>%w?)DElNxuBpm(@;-~eIjDEz-EN*mB8+&G+S>?@eth641GM!&B8HkR0GQ0kWRJR8iLz z0X4rF@l>-Cac>u+J*t5E!+JI9`zQ&zdbQz|jvmb80&W+aPJLemE!q=&@V+l0qy-(a zcX|ToCNk>w4NG=3NPw%!XHO;PH^40p0qR2d9}4ipn+BRdU&%)GXQr z2IjYLB7lxgu2IzeZID?+7npxnPD(65b@moq;4phZS^)=l<=eu2bzU+wl!Cnm}(dwRf|TL5fa2Q)j*ay7X+XSg#-^I|^sYljA^^O^fE(IWL; zbMp`2iYpJ!^(gMAGML{utSHxdjd$25!QEYKMEx8<1IQYgo`)0oeA^<>>S3KgyIC!Q zb#s7*;|1bBZ2JO~ryZ4jIKPsG`I@Qgm^9!| zct(fGY*80As@Sgy^uF}wj=U=;MSiYc)ZkT7RKmze{qaxdjft+84 zc`H)!d)QRfnIlFXg6|#bBUM_pOSNJ7!uV~rID~0$mvInqT0b?q{nS~K;>=%P(~8j) zU=f*{WvG!}Js6d`a(@>`NxJCot_RgFL{1Mp|ESF+C-7tYqg2rz!xRu`3-zGj#X4~& zNZ9GbRnL6imJTP!cm{aw`&>X)lq`{yXC<#)0Z9Q0D9gNQmS3Z(UzG!OolIAkK5Ca< z1m-Fs4l7U-pmhGDIBpd{X{92yxfI|IubC&p_%h;aqMqv|Kq)>$}jPj`%z}d-hEvDRFZS@572WAH3Jl;xTX z7v(=QZMYXthYXPa1OuUVK>guCbGz>r`pa;6i2twRBn@O@5p$O9gkF^a!d#gr$c)=Y4*Lyqp> zLi29L*yerAsqzF!s*ad0Jz-(?G4NF1=>htlo6D{q9%BqNXDyj9vlU#DC9C22w-?a4 z8bIDGX#lB&b;w%a(#in8`-FdUqOGrdf#<3=(g@#+UD7+iE%63meO~_fcS|6=<$nxc z$C*tVV^`k2{WvXo@a~;M(r%%Sk)$T93Ymk~69<2VoGHKwG6!R33R}s+n(3s_$NF6? zb4P%M6@K;{Bu$H#qTceHie1YwF=cRm1ZrO&^y`)|I0ed0oym{%5hyoMB>oN{Ka%Ag zn=+KU8TgQK#hAjp5JHf7kSOiyVJ;F%>XqiYtVBHCVWrp$tRE;BZeQ#VJMF2`jtV2M-iJ&kth$Db> zz^&jH8>a@E*Z3meTm=wNNrjEz!WzL?)_GQiurH_o(Qm2!kxzv$_%3V;o z3-GT!>iiG=pUVJNP&go}6952EQ~;d;DnJ3q06t+ZlSd>Xp`j*|s-S=j31e>Vj!*;6 zP{4Qt_yd@6N&Eo(GtU5mXZ-{40_N%&`)0Y9qyE3?U10xP{jZV$`u8=RFY`X5zgT+! zbC2@@=HvfY)ld0h*?72pKy zJ3{SF)KV-@2v5HM-S!3kTmA>qXE|Dee=`2_)?fE?*aP+CxtW8^9#>>CGBPqUGBPqx z8s8T2XV>ilv{=XC8(e&2EWRj>A1@HsRp2tbCg>~H0qfe-tCu8Pbn8G!`|=_=5yWEn zJZQe2DsL3BKtV1an-T{;WOEFxGhjPDh$;cIs|Z>9TXV_^OCdS(uaul+Zgw0y>Z}Ve zWmpv+&y2n(h4F77s5GU-IpZ|S$1qOGmDnR+5nZ$3Y*rRausTLT0`V|uQBPrv42s35 zeJwZk?`NlZ>-L`3zoGpO#k>710gI( zC0rh06z=+m7Nz!27#(q~A=n86-z{Y`04PvW3WJ_|Vq;*^w1I7vrmZz6Ls^Fhc%ZS~ zBb)k}_(*V|zd#hp&l%>z!qJhDk&%&+kYE7*z6}6>9P;1)_<=vUtcLIF0FivUj3!O) zjq(U{vZ?&<_jijYbbvow8w@yZ<|3RI37A;Y`T$}63Hc#$Z&;rvvyg|QEH?QIH#dGP zTf#qpYsYlh1!FouZxl<~99H1NbGilK)2=Yq0!`VEAV7u7)MyS+vGv=o`?2YdOwE%) zngf@F@*jtw!DWFavwuY(`Q;}OrJFK{tU~p}Nk&;@N7igc)Y2(aS4-?MaV!ra!p5Yt zC$H;|B-G!UpB+JBcS(0ps(Mdxp6@XB5kdb=uEq6UZZugG*d#K0KXz4)1oiqQzsFcV zjkkHFp+lPFNvufEYyK#x@Yyv^7PwF)c=+7!9GD2FKP)e-tq}EUZTzGSEkxmnQmFs% zyN-1AZzIW-gwxXBUzCG_BFu@ru-{a)fyLAzM5;+0^)z5XVO1*KgD9>>dzcytRv{@q zI*aS&D~reci6c=xx}9zbkLVx!dVM7BYa)f-eI9btgj-`Zg9WuxeWu<1dEeXLMDGe{ zdyXw>Ji+lE%rui}sr+GmHRd719a#=pE|hxm@;k=UL-ic|t+_Od6?P1H1Zm-kdh6U9 zq-`n|<#J4u!Xg2gKR?KSPxFVde6J*n<0%OoO0&CJjOXj2@`l>kZ}fxk&X6zhu?tpI zbvLy!#BczPp_6g)I4{~;in=jYqno53D)IXcv%HD4Xy$!kZ03?2yg!p6+>-u^rDnyt z`#}&@z=N#r7mL@^=nA2z>mNh49OQk>#LUQa9| zd5xuo^tl|2nkN9>bNW{fh@f1x{pc7J?NOF*b+9L#G%~_k8s>5;vWU*(fA6!;Yp~0h zb$^jXFs6$L#FFlbNK(XyYlS!@l)-S*_3>-oFD6^ccN0aOiL}w_-F=ZrqR({HwlM&S z$%k5a#zt($1AkIZ8(*3kL%^Hp5!P4~P}!V*Rs+`-L536b)9ON}lq%KI->hY;l(s`m zCJM;uH@N!%u*I@{lu}Gf)gZ4w%os?}?33r&ryidju-JE|W;Xmzo@J-x@Aw}bJhio9 zKW8-(ZZ7_3xq8s7#aV zyaI0i9ZIX*q~#HO&E#R)A{3qoG5(7@fn=v;(8Y!*5N(MRJxgRdkU~)~Z}lGi#I`$= zXKqIybMATTJ5+sOR#(m2AK}6TqX}?k;a)k>%BMcxc5v`;@BXObWGr1==}Hs4sZ$O z2$n{TzSt`jgMY~leaENAn%I2*g)92XzyDaYmWIeABKxWyhOC@BfbT;Y#R38-$6X$- zT&6VtC{<5E$4+d5PUlWr2;j?0^9bEl1Q13fT0La0%$y(sK zjM{V2%|wk8MGF%iU_3$5NYE_j5tSEXdLCeaE!sfgmGDs{hudHV!`z-a$BvhdN%JUA z%6H099NuzuVQi}z?enCjbGJN0ezNM8F6T@B4(t)|r1|OyFHbjNq(n$uZu=K8p>4Y{ zg$Hha?XjHqzsm^g!n!Y9m}#S0X*DIX_m_dPMaGUn>Dosm09*TIebj7KDQFJi={x*; zsr6Q*31JlqISI}I$U)jr50`uLjST!cTC#MOLbl8v<0hwgBpN)3`z+1<1=yR+Ub|mc z!5pMYbb@}+b}pc7wZh^~sJaRjs14-cd-xz8qV{)*5+(r>g;BLQjrILyZ%~Xl#}E{B zP$ByoZ{`CH_5)gUJ$;!`rHiNQq-RWcVxoU!CCFh5Pi*#QW@wI`j4Q#1Y5}) zH|dA)Kf3GVcndgrmp&aO7d@`YMZoJ1sEt@)i(E8>=kmZ(lsjOrvtdWAmRS)846^_r zF0Wg|E{-G?&T*Y}GS&o4{bVFbl|>Kp$x`UbDG%w%HnY}CJvGbEuZfC|>u!01khmgBQwLh50!_)8i zvksV3=IqRD7mOi>TDS)GDSB5<`z+17gtQ;c<`2rq{f8qa=b&df%7lq_#69D0BK``bB)UnNQ zhJ+|z3#u@{XR|AcvMnJE6to6%1W@3OZ~;%)N67%<+b%&=50?_vlX%zTTC8eCb@_@7K=lqVm ziRx!|=5AUdYiSw@qj)cpGpNPE;aIS>TEx?b7!v%{zyO@xpXCuMNKVyCbl*6b1Z{;| z@J1O zN_@BTl$_>U-#<^YuYo!d62t~p+(Im`{}~8^EEUVctT;}=vzX`^w(Q38B+E9PQ%cUy zj_;OAmf)}5*7E&gKXs>bBg-H|y3B@E@6jCR1f!OR{^zGAD!OZmD$13zD>gx4P0KEU zFMgf^e_pnJb0EKBRdk+VnextY|0ny$kf(#ldyZ09mScn^2&a|1egzDF`jTJp=B|ll zp4Gi2q66NRC;00a*q8NpZ_(aA?`+}wV1aF3GfRDlAd3Tu%sR)(ID|ATA@(BJMH456 zR>%`y>BCq7LeKZ z3p=LKiGCa;;Km;lGLEWN(_{gDxkj&}Rr+54^45oNi{#PcPn5cCzqo8LPVLswS}5w^ zly+bSjg#!5dfgz(L(~qc**vcOa(?Ev78zTl&-ov&{!5P0Qv+F@LQ<}>XPNezQqFsKs5R6}VVv_M3*wTxe z0VmlYxo3rbb@jFa+9Aipq7#5jPrr7LySiZ&j2hjvO32Y*yEnW{@4Tt7_06vvaHQd4L1A9jhEw69IFWzHxnOuB1iZ7)ZPDp z!peEeOGo>N1!Q9VAAP?oy1x-QjeVuH|H7TLr5A2_md3uN{^KDIfGl4`qEixxRJv>L zwb>DjNXBT~Ado~?{KYefHkXW3=~u^egCJWSky99c%vt$0f&?73FseD<;YF^0b3Nb%S>J7}bwF}xO!YFp4DX(~u9O!6(q3xeJT^*~%Y(y~jk zP2d=vBCKsv*03NSI`e3Fh}wV8&nd^B-?-irNb*5u-{(pP%@X4c`*yC7%ltFDPzIfq9Cv;2U>&=qq+ z4fOU~hebc%e@=kI_%v+S6Wil&!)XrV)>FV+i5wRZ>6OP{PH5R_{aJx!PLCCtUmB99 zau~lAHnsez3mdr|WdV;%t2<(b(C3ly^T9iM$ZS49aBZn3OdMG1ee!w9hcglkAdzv?iOVqxV-a6g9rj-G)7Dah zMkN}~KTTNXJiI&s{M?L!ep%j!vhtkq{jS*p=V80KqP76;fB({y{_0oFQ6ddi{pPDk z;D(JunAI2H6w|Cyu#r_eO$p1+7b1-sFP~75=)4S7>QUy-cH(38{4~+kt z%gkTIF{CGp5*iZGpMQT`mg(Qbf#%Tvt0*?mh(`I8hM*y_hYy=!v$DRTS zll;0uCHHYSYrVsWM3(120T=)()zN9OfWyOpYYJo3^jtPhuqk)GURC!!oe6@xjehN8Ew$c*|GDe#A{m06G z;yOUR`g6?Gk`xjhs*mvaaf1vLG%9;Shx z;?W;;l(P&YUE~JH1nwrWp*B@93oS+U?rfRbVWnfu%Yc#uAd+X;maU@yr@VuGbqpo5 zz)iL->?NO_7HF3e@qVdN3LC5QpdqKH@^y-@p-^ws>^pmH)yN|qcuKn2q@P${4SsO% zWn>Qvoi&Qlvl$`&LvEwQQ9{;T|H(M(RQB{-#hXGBgWS~n>F{}bwo)yFGUxXDy&M8O z5%=14I~U!6VeUJ@S|e1hQM_Ui#I;&HoL-5V0u3x$Mf4x^pT#(<8jMa;1kxxnh9~08&aK?H)_u z+pr;~W53{YrrdKa~zKwMVzztriPn%e!(7`m$hmoXhc#CftSZc0BOad~IAYh%-? zJfJ(KwdI1>{6F|R>$dbl$Lc^j8mXf&`HK{muo9&!D6`%84 zSq$pt*Quchdt`j`dPUV9)zyD>zhFTU;N9Q*aMa!_iR;Ue=`e=ZBND#l2CJSlwq5B0 zm;z+cI*U;$t&xHRc)J$#B{m;{eQy&*ezYWn8S@Qd(tSw4wusB0F{B~NHQ2`Gf?n|e zgwx^4IJ@H*`GA#MZP8v19t7j%D+-%b9`C#@>GgEx(4Cxac{I(Rf+3HGq0j?s|C1*E zgrew^x2x7H6O}2>B4< iVDUl);yiV&+3ksEw%bYN{*zJ+a}j70Sq!(=SbzZ8r@|Eg literal 0 HcmV?d00001 diff --git a/apps/web/scripts/check-avatar-lipsync.mjs b/apps/web/scripts/check-avatar-lipsync.mjs new file mode 100644 index 0000000..b8f2200 --- /dev/null +++ b/apps/web/scripts/check-avatar-lipsync.mjs @@ -0,0 +1,288 @@ +#!/usr/bin/env node +/** + * check-avatar-lipsync — 아바타 v3 발화층(립싱크) 데이터 정합성 검사. + * docs/decisions/avatar-expression-engine-v3.md §5.4(발화층) "검증" 항목을 값·코드에 대해 검증한다. + * engine 데이터 파일은 순수 TS라 esbuild로 node ESM으로 번들해(check-avatar-presets.mjs 패턴) + * 동적 import로 값을 읽는다. 임시 산출물은 끝나면 지운다. + */ +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; +import { build } from "esbuild"; + +const appRoot = process.cwd(); +const engineDir = path.join(appRoot, "src", "components", "avatar", "engine"); + +const ENTRY_FILES = ["lipsync.ts", "speechEnvelope.ts", "demeanorDefaults.ts", "coSpeech.ts", "engine.ts"]; + +const failures = []; +function fail(message) { + failures.push(message); +} + +async function loadModules() { + const tempDir = await mkdtemp(path.join(os.tmpdir(), "vignette-avatar-lipsync-check-")); + try { + await writeFile(path.join(tempDir, "package.json"), JSON.stringify({ type: "module" }), "utf8"); + await build({ + entryPoints: ENTRY_FILES.map((f) => path.join(engineDir, f)), + outdir: tempDir, + bundle: true, + platform: "node", + format: "esm", + logLevel: "silent", + }); + + const modules = {}; + for (const f of ENTRY_FILES) { + const outFile = path.join(tempDir, f.replace(/\.ts$/, ".js")); + const url = `${pathToFileURL(outFile).href}?t=${Date.now()}`; + modules[f] = await import(url); + } + return modules; + } finally { + await rm(tempDir, { recursive: true, force: true }); + } +} + +function nonSilentSequence(seq) { + return seq.filter((v) => v !== "X"); +} + +/* (a) 분해·매핑 표본(§5.4 검증: "음"은 끝이 M, "우리"는 U→I, "봐요"는 M→U→A→I→O). */ +function checkVisemeSamples(buildSpeechTimeline, visemeSequence) { + const cases = [ + ["음", ["EU", "M"]], + ["우리", ["U", "I"]], + ["봐요", ["M", "U", "A", "I", "O"]], + ]; + for (const [text, expected] of cases) { + const timeline = buildSpeechTimeline({ text, syllablesPerSec: 5.0 }); + const seq = nonSilentSequence(visemeSequence(timeline)); + if (JSON.stringify(seq) !== JSON.stringify(expected)) { + fail(`비짐 표본 "${text}": 기대 [${expected.join(",")}], 실제 [${seq.join(",")}]`); + } + } +} + +/* (b) 최소 유지 60ms, M·X 밖에서는 open > 0(§5.4 검증). */ +function checkTimingInvariants(buildSpeechTimeline) { + const timeline = buildSpeechTimeline({ text: "음… 그냥 요즘 잠을 잘 못 자요.", syllablesPerSec: 5.0 }); + for (let i = 1; i < timeline.keys.length; i++) { + const gap = timeline.keys[i].atMs - timeline.keys[i - 1].atMs; + if (gap < 59.99) fail(`최소 유지: 키 ${i - 1}→${i} 간격 ${gap.toFixed(2)}ms < 60ms`); + } + for (const key of timeline.keys) { + if (key.viseme === "M" || key.viseme === "X") continue; + if (!(key.shape.open > 0)) fail(`open>0 불변량: 비짐 ${key.viseme}의 open=${key.shape.open}`); + } +} + +/* (c) 텍스트 타이밍에서 syllablesPerSec와 발화 길이의 일치(받침·휴지 없는 표본으로 가중치를 1.0 고정). */ +function checkTextTimingRate(buildSpeechTimeline) { + const text = "가나다라마바사"; + const syllablesPerSec = 5.0; + const timeline = buildSpeechTimeline({ text, syllablesPerSec }); + const expectedMs = 7 * (1000 / syllablesPerSec); + if (Math.abs(timeline.totalDurationMs - expectedMs) > 0.01) { + fail(`텍스트 타이밍: 기대 ${expectedMs}ms, 실제 ${timeline.totalDurationMs}ms`); + } +} + +/* (d) 합성 포락선(구 3개·무음 2개) 정렬: 구 경계 오차 ≤ 30ms(§5.4 검증). */ +function checkAudioAlignment(buildSpeechTimeline) { + const hopMs = 10; + const totalHops = 150; + const rms = new Float32Array(totalHops).fill(0.01); + const loudSpans = [ + [0, 20], + [50, 70], + [100, 120], + ]; + for (const [a, b] of loudSpans) for (let i = a; i < b; i++) rms[i] = 1.0; + const sorted = Array.from(rms).sort((a, b) => a - b); + const pct = (p) => sorted[Math.min(sorted.length - 1, Math.max(0, Math.ceil(p * sorted.length) - 1))]; + const envelope = { hopMs, rms, p90: pct(0.9), p95: pct(0.95) }; + + const timeline = buildSpeechTimeline({ text: "가. 나. 다", syllablesPerSec: 5.0, envelope }); + const expected = loudSpans.map(([a, b]) => ({ startMs: a * hopMs, endMs: b * hopMs })); + const windows = timeline.phraseWindows ?? []; + if (windows.length !== expected.length) { + fail(`구 경계: 기대 구 ${expected.length}개, 실제 ${windows.length}개`); + return; + } + windows.forEach((w, i) => { + const startErr = Math.abs(w.startMs - expected[i].startMs); + const endErr = Math.abs(w.endMs - expected[i].endMs); + if (startErr > 30) fail(`구 경계 ${i}: 시작 오차 ${startErr}ms > 30ms`); + if (endErr > 30) fail(`구 경계 ${i}: 끝 오차 ${endErr}ms > 30ms`); + }); +} + +/* ── §5.5 발화 동반층 검증 ─────────────────────────────────────────── */ + +/* (e) 같은 시드면 계획이 같다. */ +function checkPlanDeterminism(buildSpeechTimeline, buildCoSpeechPlan) { + const timeline = buildSpeechTimeline({ text: "음… 그냥 요즘 잠을 잘 못 자요. 정말 그렇게 생각하세요?", syllablesPerSec: 5.0 }); + const style = { gesture: 0.55, gazeAvertProb: 0.7 }; + const a = buildCoSpeechPlan(timeline, style, 7); + const b = buildCoSpeechPlan(timeline, style, 7); + if (JSON.stringify(a) !== JSON.stringify(b)) fail("계획 결정성: 같은 시드인데 계획이 다르다"); +} + +/* (f) 강세 박 정점 = 모음 목표 − 80ms(±1ms). */ +function checkStressPulsePeaks(buildSpeechTimeline, buildCoSpeechPlan) { + const timeline = buildSpeechTimeline({ text: "음… 그냥 요즘 잠을 잘 못 자요. 정말 그렇게 생각하세요?", syllablesPerSec: 5.0 }); + const plan = buildCoSpeechPlan(timeline, { gesture: 0.55, gazeAvertProb: 0.7 }, 7); + + const expectedPeaks = []; + for (const ph of timeline.phrases) { + for (const idx of ph.stressedIndices) { + const vowelMs = ph.syllableTargetsMs[idx]; + if (vowelMs !== undefined) expectedPeaks.push(vowelMs - 80); + } + } + const actualPeaks = plan.stressPulses.map((p) => p.peakMs); + if (actualPeaks.length !== expectedPeaks.length) { + fail(`강세 박 개수: 기대 ${expectedPeaks.length}, 실제 ${actualPeaks.length}`); + return; + } + expectedPeaks.forEach((exp, i) => { + const err = Math.abs(actualPeaks[i] - exp); + if (err > 1) fail(`강세 박 ${i} 정점: 기대 ${exp}ms, 실제 ${actualPeaks[i]}ms(오차 ${err.toFixed(2)}ms)`); + }); +} + +/* (g) 질문 구에서 headRoll·headPitch가 들린다. */ +function checkQuestionLift(buildSpeechTimeline, buildCoSpeechPlan, sampleCoSpeech) { + const timeline = buildSpeechTimeline({ text: "정말 그렇게 생각하세요?", syllablesPerSec: 5.0 }); + const plan = buildCoSpeechPlan(timeline, { gesture: 0.55, gazeAvertProb: 0.7 }, 7); + const ph = timeline.phrases[0]; + const holdMs = ph.endMs + 150; /* 300ms 유지 구간 중간 */ + const sample = sampleCoSpeech(plan, holdMs, holdMs - 16, undefined); + if (!(sample.delta.headPitch > 0)) fail(`질문 구 headPitch: ${holdMs}ms에 ${sample.delta.headPitch}(양수 기대)`); + if (!(Math.abs(sample.delta.headRoll) > 0)) fail(`질문 구 headRoll: ${holdMs}ms에 ${sample.delta.headRoll}(0이 아닌 값 기대)`); +} + +/* (h) 발화 마지막 400ms에는 시선 회피가 없다. */ +function checkNoGazeAvertInTail(buildSpeechTimeline, buildCoSpeechPlan) { + const timeline = buildSpeechTimeline({ text: "가나다라마바사아자차카, 나", syllablesPerSec: 5.0 }); + const lastPhrase = timeline.phrases[timeline.phrases.length - 1]; + if (!(lastPhrase.startMs >= timeline.totalDurationMs - 400)) { + fail("시선 회피 표본 설계: 마지막 구가 발화 마지막 400ms 안에서 시작하지 않는다(표본 텍스트 재조정 필요)"); + return; + } + const plan = buildCoSpeechPlan(timeline, { gesture: 1, gazeAvertProb: 1 }, 3); + if (plan.gazePulses.length !== timeline.phrases.length - 1) { + fail(`시선 회피 발동 수: 기대 ${timeline.phrases.length - 1}(마지막 구 제외), 실제 ${plan.gazePulses.length}`); + } + for (const g of plan.gazePulses) { + const startMs = g.breakpoints[0].t; + if (startMs >= timeline.totalDurationMs - 400) fail(`시선 회피가 발화 마지막 400ms 안(${startMs}ms)에서 시작했다`); + } +} + +/* (h-2) 평서 구 끝 끄덕임 headPitch < 0. */ +function checkStatementNodPitch(buildSpeechTimeline, buildCoSpeechPlan, sampleCoSpeech) { + const timeline = buildSpeechTimeline({ text: "그냥요.", syllablesPerSec: 5.0 }); + const plan = buildCoSpeechPlan(timeline, { gesture: 0.55, gazeAvertProb: 0.7 }, 7); + const ph = timeline.phrases[0]; + const lastTargetMs = ph.syllableTargetsMs[ph.syllableTargetsMs.length - 1]; + const sample = sampleCoSpeech(plan, lastTargetMs, lastTargetMs - 16, undefined); + if (!(sample.delta.headPitch < 0)) fail(`평서 구 끝 끄덕임: ${lastTargetMs}ms에 headPitch=${sample.delta.headPitch}(음수 기대)`); +} + +/* (h-3) 엔진 통과: speaking 중 강세 박은 스프링 없이 그대로 통과해 정점 시각·크기를 지킨다 + (팀 리드 수정 요청, 2026-10-01). reduced motion 끔, 표정 neutral, 고정 시계 10ms 간격. */ +function checkEngineStressPulsePassthrough(buildSpeechTimeline, buildCoSpeechPlan, sampleCoSpeech, demeanorFor, createAvatarEngine) { + const timeline = buildSpeechTimeline({ text: "그냥요.", syllablesPerSec: 5.0 }); + const style = { gesture: 0.55, gazeAvertProb: 0.7 }; + const plan = buildCoSpeechPlan(timeline, style, 7); + const stress = plan.stressPulses[0]; + if (!stress) { + fail("엔진 통과 검증: 강세 박이 없다"); + return; + } + + const demeanor = demeanorFor("P1"); + const engine = createAvatarEngine({ demeanor, seed: 1, reducedMotion: false }); + engine.setState("speaking", 0); + engine.setSurface("neutral", 0); + + const scanStart = Math.max(0, stress.peakMs - stress.riseMs - 50); + const scanEnd = stress.peakMs + stress.fallMs + 50; + const stepMs = 10; + + const baseline = engine.evaluate(scanStart); + const baseHeadPitch = baseline.headPitch; + + let prevLocalMs = scanStart; + let bestT = null; + let bestDelta = 0; + for (let t = scanStart; t <= scanEnd; t += stepMs) { + const sample = sampleCoSpeech(plan, t, prevLocalMs); + engine.setSpeechMotion(sample.delta); + prevLocalMs = t; + const v = engine.evaluate(t); + const coHeadPitch = v.headPitch - baseHeadPitch; + if (bestT === null || coHeadPitch < bestDelta) { + bestDelta = coHeadPitch; + bestT = t; + } + } + + const timeErr = Math.abs(bestT - stress.peakMs); + if (timeErr > 10) fail(`엔진 통과: 강세 박 정점 시각 오차 ${timeErr}ms > 10ms(계획 ${stress.peakMs}ms, 엔진 ${bestT}ms)`); + const expectedMag = Math.abs(stress.headPitch); + const actualMag = Math.abs(bestDelta); + if (actualMag < expectedMag * 0.9) { + fail(`엔진 통과: 강세 박 크기 ${actualMag.toFixed(4)} < 계획 값의 90%(${(expectedMag * 0.9).toFixed(4)})`); + } +} + +/* (i) reduced motion에서는 엔진 delta가 0. */ +function checkReducedMotionZero(demeanorFor, createAvatarEngine) { + const demeanor = demeanorFor("P1"); + const engine = createAvatarEngine({ demeanor, seed: 1, reducedMotion: true }); + engine.setState("speaking", 0); + engine.setSpeechMotion({ headYaw: 0.9, headRoll: 0.9, browLY: 0.9, browRY: 0.9, gazeX: 0.9 }); + const v = engine.evaluate(50); + /* P1 basePose는 이 채널들을 건드리지 않아 중립값이 정확히 0이다(demeanorDefaults.ts 기준). */ + for (const id of ["headYaw", "headRoll", "browLY", "browRY", "gazeX"]) { + if (v[id] !== 0) fail(`reduced motion delta: ${id} 기대 0, 실제 ${v[id]}(setSpeechMotion이 반영되면 안 된다)`); + } +} + +const modules = await loadModules(); +const { buildSpeechTimeline, visemeSequence } = modules["lipsync.ts"]; +const { demeanorFor } = modules["demeanorDefaults.ts"]; +const { buildCoSpeechPlan, sampleCoSpeech } = modules["coSpeech.ts"]; +const { createAvatarEngine } = modules["engine.ts"]; + +const p1Rate = demeanorFor("P1").speech.syllablesPerSec; +if (p1Rate !== 5.0) fail(`P1 syllablesPerSec 기대 5.0, 실제 ${p1Rate}`); + +checkVisemeSamples(buildSpeechTimeline, visemeSequence); +checkTimingInvariants(buildSpeechTimeline); +checkTextTimingRate(buildSpeechTimeline); +checkAudioAlignment(buildSpeechTimeline); + +checkPlanDeterminism(buildSpeechTimeline, buildCoSpeechPlan); +checkStressPulsePeaks(buildSpeechTimeline, buildCoSpeechPlan); +checkQuestionLift(buildSpeechTimeline, buildCoSpeechPlan, sampleCoSpeech); +checkNoGazeAvertInTail(buildSpeechTimeline, buildCoSpeechPlan); +checkStatementNodPitch(buildSpeechTimeline, buildCoSpeechPlan, sampleCoSpeech); +checkEngineStressPulsePassthrough(buildSpeechTimeline, buildCoSpeechPlan, sampleCoSpeech, demeanorFor, createAvatarEngine); +checkReducedMotionZero(demeanorFor, createAvatarEngine); + +if (failures.length > 0) { + for (const message of failures) console.error(`FAIL: ${message}`); + console.error(`check-avatar-lipsync: 실패 ${failures.length}건`); + process.exit(1); +} + +console.log( + "check-avatar-lipsync: 통과 (비짐 표본 3건, 최소 유지·open>0 불변량, 텍스트 발화 속도 일치, 구 경계 정렬 ≤30ms, " + + "발화 동반층 결정성·강세 박 정점·질문 들림·시선 회피 꼬리 금지·평서 구 끝 끄덕임·엔진 통과·reduced motion 0)", +); diff --git a/apps/web/scripts/check-avatar-presets.mjs b/apps/web/scripts/check-avatar-presets.mjs new file mode 100644 index 0000000..64ffefa --- /dev/null +++ b/apps/web/scripts/check-avatar-presets.mjs @@ -0,0 +1,267 @@ +#!/usr/bin/env node +/** + * check-avatar-presets — 아바타 v3 표현 엔진 데이터 정합성 검사. + * docs/decisions/avatar-expression-engine-v3.md §5.1(구분 가능성 불변량)· + * §6.1(클립 형식)·§7.3(지문 파서) 규칙을 값·코드에 대해 검증한다. + * + * engine 데이터 파일은 순수 TS라 esbuild로 각각 node ESM으로 번들해(임시 디렉터리, + * generate-live2d-assets.mjs 패턴) 동적 import로 값을 읽는다. 임시 산출물은 끝나면 지운다. + */ +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; +import { build } from "esbuild"; + +const appRoot = process.cwd(); +const engineDir = path.join(appRoot, "src", "components", "avatar", "engine"); + +const ENTRY_FILES = [ + "channels.ts", + "expressionPresets.ts", + "clipCatalog.ts", + "demeanorDefaults.ts", + "stageDirectionLexicon.ts", + "performance.ts", +]; + +/* 저작용 대칭 키 — channels.ts SYMMETRIC_KEYS 와 같은 쌍. 구분 가능성 불변량 계산에서 + 좌우를 평균 1개 채널로 접는다(결정문 §5.1). */ +const SYMMETRIC_PAIRS = [ + ["eyeOpenL", "eyeOpenR"], + ["eyeSmileL", "eyeSmileR"], + ["browLY", "browRY"], + ["browLAngle", "browRAngle"], + ["browLX", "browRX"], +]; + +const failures = []; + +function fail(message) { + failures.push(message); +} + +async function loadModules() { + const tempDir = await mkdtemp(path.join(os.tmpdir(), "vignette-avatar-check-")); + try { + await writeFile(path.join(tempDir, "package.json"), JSON.stringify({ type: "module" }), "utf8"); + await build({ + entryPoints: ENTRY_FILES.map((f) => path.join(engineDir, f)), + outdir: tempDir, + bundle: true, + platform: "node", + format: "esm", + logLevel: "silent", + }); + + const modules = {}; + for (const f of ENTRY_FILES) { + const outFile = path.join(tempDir, f.replace(/\.ts$/, ".js")); + const url = `${pathToFileURL(outFile).href}?t=${Date.now()}`; + modules[f] = await import(url); + } + return modules; + } finally { + await rm(tempDir, { recursive: true, force: true }); + } +} + +function effectiveChannelVector(delta, channelIds) { + const used = new Set(SYMMETRIC_PAIRS.flat()); + const out = {}; + for (const [l, r] of SYMMETRIC_PAIRS) { + const lv = delta[l] ?? 0; + const rv = delta[r] ?? 0; + out[`${l}/${r}`] = (lv + rv) / 2; + } + for (const id of channelIds) { + if (used.has(id)) continue; + out[id] = delta[id] ?? 0; + } + return out; +} + +function l1Distance(a, b) { + let sum = 0; + let max = 0; + for (const key of Object.keys(a)) { + const d = Math.abs((a[key] ?? 0) - (b[key] ?? 0)); + sum += d; + if (d > max) max = d; + } + return { sum, max }; +} + +/* (a) 구분 가능성 불변량: 378쌍 L1 ≥ 0.6, 최대 단일 채널 차이 ≥ 0.25. */ +function checkDiscriminability(CHANNEL_IDS, EXPRESSION_PRESETS) { + const ids = Object.keys(EXPRESSION_PRESETS); + const vectors = new Map(ids.map((id) => [id, effectiveChannelVector(EXPRESSION_PRESETS[id], CHANNEL_IDS)])); + let pairCount = 0; + let minL1 = Infinity; + let minL1Pair = ""; + for (let i = 0; i < ids.length; i++) { + for (let j = i + 1; j < ids.length; j++) { + pairCount++; + const { sum, max } = l1Distance(vectors.get(ids[i]), vectors.get(ids[j])); + if (sum < minL1) { + minL1 = sum; + minL1Pair = `${ids[i]}/${ids[j]}`; + } + if (sum < 0.6) fail(`구분 가능성: ${ids[i]}/${ids[j]} L1=${sum.toFixed(3)} < 0.6`); + if (max < 0.25) fail(`구분 가능성: ${ids[i]}/${ids[j]} 최대 단일 채널 차이=${max.toFixed(3)} < 0.25`); + } + } + const expectedPairs = (ids.length * (ids.length - 1)) / 2; + if (pairCount !== expectedPairs) fail(`구분 가능성: 쌍 개수 ${pairCount} != 기대 ${expectedPairs}`); + return { pairCount, minL1, minL1Pair }; +} + +/* (b) 모든 프리셋·클립·basePose 키가 CHANNEL_IDS에 속함. */ +function checkChannelKeys(CHANNEL_IDS, EXPRESSION_PRESETS, REACTION_CLIPS, DEFAULT_DEMEANOR, personaDemeanors) { + const validIds = new Set(CHANNEL_IDS); + for (const [exprId, delta] of Object.entries(EXPRESSION_PRESETS)) { + for (const key of Object.keys(delta)) { + if (!validIds.has(key)) fail(`채널 키: 프리셋 ${exprId}의 ${key}가 CHANNEL_IDS에 없음`); + } + } + for (const [clipId, clip] of Object.entries(REACTION_CLIPS)) { + for (const key of Object.keys(clip.tracks)) { + if (!validIds.has(key)) fail(`채널 키: 클립 ${clipId}의 트랙 ${key}가 CHANNEL_IDS에 없음`); + } + } + const demeanors = [["DEFAULT", DEFAULT_DEMEANOR], ...personaDemeanors]; + for (const [label, demeanor] of demeanors) { + for (const key of Object.keys(demeanor.basePose)) { + if (!validIds.has(key)) fail(`채널 키: demeanor ${label}의 basePose ${key}가 CHANNEL_IDS에 없음`); + } + } +} + +/* (c)(d) 클립 키프레임 형식. */ +function checkClipKeyframes(REACTION_CLIPS) { + for (const [clipId, clip] of Object.entries(REACTION_CLIPS)) { + if (clip.fadeInMs + clip.fadeOutMs > clip.durationMs) { + fail(`클립 ${clipId}: fadeInMs(${clip.fadeInMs})+fadeOutMs(${clip.fadeOutMs}) > durationMs(${clip.durationMs})`); + } + for (const [channelId, frames] of Object.entries(clip.tracks)) { + if (!frames || frames.length === 0) continue; + const [t0, v0] = frames[0]; + if (t0 !== 0) fail(`클립 ${clipId}.${channelId}: 첫 키프레임 시각이 0이 아님(${t0})`); + if (v0 !== 0) fail(`클립 ${clipId}.${channelId}: 첫 키프레임 값이 0이 아님(${v0})`); + for (let i = 1; i < frames.length; i++) { + if (frames[i][0] <= frames[i - 1][0]) { + fail(`클립 ${clipId}.${channelId}: 시각이 오름차순이 아님(${frames[i - 1][0]} -> ${frames[i][0]})`); + } + } + const last = frames[frames.length - 1]; + if (last[0] > clip.durationMs) { + fail(`클립 ${clipId}.${channelId}: 마지막 키프레임 시각(${last[0]}) > durationMs(${clip.durationMs})`); + } + if (last[1] !== 0 && clip.fadeOutMs < 300) { + fail(`클립 ${clipId}.${channelId}: 마지막 값(${last[1]})이 0이 아닌데 fadeOutMs(${clip.fadeOutMs}) < 300`); + } + } + } +} + +/* (e) demeanor idleClips·lexicon의 clip id가 REACTION_CLIP_IDS에 존재. */ +function checkClipReferences(REACTION_CLIP_IDS, DEFAULT_DEMEANOR, personaDemeanors, STAGE_DIRECTION_RULES) { + const validIds = new Set(REACTION_CLIP_IDS); + const demeanors = [["DEFAULT", DEFAULT_DEMEANOR], ...personaDemeanors]; + for (const [label, demeanor] of demeanors) { + for (const rule of demeanor.idleClips) { + if (!validIds.has(rule.clip)) fail(`demeanor ${label}의 idleClips 참조 ${rule.clip}가 REACTION_CLIP_IDS에 없음`); + } + } + for (const rule of STAGE_DIRECTION_RULES) { + if (!validIds.has(rule.clip)) fail(`stageDirectionLexicon 규칙의 ${rule.clip}가 REACTION_CLIP_IDS에 없음`); + } +} + +/* (f) 표본 지문 대응표(결정문 §7.3, 앵커 보정은 오케스트레이터 2026-09-30 정정). */ +function checkStageDirectionSamples(parseStageDirections) { + const singleClipCases = [ + ["(한숨)", "sigh", undefined], + ["(옅은 한숨)", "sigh", 0.6], + ["(잠시 멈춤)", "look_down", undefined], + ["(잠깐 침묵)", "silence_hold", undefined], + ["(고개 살짝 돌림)", "look_away_side", undefined], + ["(어깨 으쓱)", "shrug", undefined], + ["(피식)", "scoff", undefined], + ["(어색한 웃음)", "nervous_laugh", undefined], + ["(긴장한 웃음)", "nervous_laugh", undefined], + ["(머뭇)", "lip_press", undefined], + ["(손톱 만지작)", "fidget_sway", undefined], + ["(시선 피함)", "look_away_side", undefined], + ["(쓴웃음)", "scoff", undefined], + ]; + + for (const [text, expectedClip, expectedWeight] of singleClipCases) { + const { cues } = parseStageDirections(text); + if (cues.length !== 1 || cues[0].clip !== expectedClip) { + fail(`지문 "${text}": 기대 클립 ${expectedClip}, 실제 ${cues.map((c) => c.clip).join(",") || "(없음)"}`); + continue; + } + if (expectedWeight !== undefined && cues[0].weight !== expectedWeight) { + fail(`지문 "${text}": 기대 weight ${expectedWeight}, 실제 ${cues[0].weight}`); + } + } + + const { cues: multi } = parseStageDirections("(한숨, 침묵 10초)"); + const multiClips = multi.map((c) => c.clip).join(","); + if (multiClips !== "sigh,silence_hold") { + fail(`지문 "(한숨, 침묵 10초)": 기대 [sigh, silence_hold], 실제 [${multiClips}]`); + } + + const unmatchedCases = ["(작은 목소리로)", "('지침 vs 게으름' 재구성)"]; + for (const text of unmatchedCases) { + const { cues, unmatched } = parseStageDirections(text); + if (cues.length !== 0 || unmatched.length !== 1) { + fail(`지문 "${text}": 미대응이어야 하는데 cues=${cues.length}, unmatched=${unmatched.length}`); + } + } + + const preCase = parseStageDirections("(한숨) 그냥요."); + if (preCase.cues[0]?.anchor !== "pre") { + fail(`앵커 "(한숨) 그냥요.": 기대 pre, 실제 ${preCase.cues[0]?.anchor}`); + } + + const inlineCase = parseStageDirections("몰라요. (한숨) 다 귀찮아요."); + const inlineCue = inlineCase.cues[0]; + if (inlineCue?.anchor !== "inline") { + fail(`앵커 "몰라요. (한숨) 다 귀찮아요.": 기대 inline, 실제 ${inlineCue?.anchor}`); + } else if (inlineCue.at < 0.36 || inlineCue.at > 0.39) { + fail(`앵커 "몰라요. (한숨) 다 귀찮아요.": at=${inlineCue.at.toFixed(4)}가 [0.36, 0.39] 밖`); + } + + const postCase = parseStageDirections("그냥요 (시선 피함)"); + if (postCase.cues[0]?.anchor !== "post") { + fail(`앵커 "그냥요 (시선 피함)": 기대 post, 실제 ${postCase.cues[0]?.anchor}`); + } +} + +const modules = await loadModules(); +const { CHANNEL_IDS } = modules["channels.ts"]; +const { EXPRESSION_PRESETS } = modules["expressionPresets.ts"]; +const { REACTION_CLIPS, REACTION_CLIP_IDS } = modules["clipCatalog.ts"]; +const { DEFAULT_DEMEANOR, demeanorFor } = modules["demeanorDefaults.ts"]; +const { STAGE_DIRECTION_RULES } = modules["stageDirectionLexicon.ts"]; +const { parseStageDirections } = modules["performance.ts"]; + +const personaDemeanors = ["P1", "P2", "P3", "P4", "P5", "P6", "P7"].map((code) => [code, demeanorFor(code)]); + +const { pairCount, minL1, minL1Pair } = checkDiscriminability(CHANNEL_IDS, EXPRESSION_PRESETS); +checkChannelKeys(CHANNEL_IDS, EXPRESSION_PRESETS, REACTION_CLIPS, DEFAULT_DEMEANOR, personaDemeanors); +checkClipKeyframes(REACTION_CLIPS); +checkClipReferences(REACTION_CLIP_IDS, DEFAULT_DEMEANOR, personaDemeanors, STAGE_DIRECTION_RULES); +checkStageDirectionSamples(parseStageDirections); + +if (failures.length > 0) { + for (const message of failures) console.error(`FAIL: ${message}`); + console.error(`check-avatar-presets: 실패 ${failures.length}건`); + process.exit(1); +} + +console.log( + `check-avatar-presets: 통과 (프리셋 쌍 ${pairCount}개 전부 구분 가능, 최소 L1 ${minL1.toFixed(2)}[${minL1Pair}], 클립 ${REACTION_CLIP_IDS.length}개, 지문 표본 검증 완료)`, +); diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index c9a8f8a..628d032 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -50,6 +50,7 @@ const PendingApproval = lazy(() => import("./pages/PendingApproval")); const LearnerHome = lazy(() => import("./pages/LearnerHome")); const AvatarExpressionLab = lazy(() => import("./pages/AvatarExpressionLab")); const AvatarPreview = lazy(() => import("./pages/AvatarPreview")); +const AvatarLab = lazy(() => import("./pages/AvatarLab")); const Session = lazy(() => import("./pages/Session")); const SessionReview = lazy(() => import("./pages/SessionReview")); const Professor = lazy(() => import("./pages/Professor")); @@ -484,6 +485,9 @@ function AppRoutes() { {/* dev: 인증 없는 아바타 컴포지션 튜닝 페이지 (실서비스 아님) */} } /> + {/* dev: 인증 없는 아바타 v3 표현 엔진 Lab (실서비스 아님) */} + } /> + ; + +export type ChannelId = keyof typeof CHANNEL_SPECS; +export type ChannelVector = Record; +export type ChannelDelta = Partial>; + +export const CHANNEL_IDS = Object.keys(CHANNEL_SPECS) as ChannelId[]; + +/** 강도에 따라 켜지는 오버레이 채널과 최소 강도(결정문 §5.2). */ +export const OVERLAY_MIN_INTENSITY: Partial> = { + tear: 0.7, + cheek: 0.5, + pallor: 0.7, + sweat: 0.7, +}; + +/* ── 저작용 대칭 키: 좌우를 같은 값으로 펼친다 ─────────────────────── */ +const SYMMETRIC_KEYS = { + eyeOpen: ["eyeOpenL", "eyeOpenR"], + eyeSmile: ["eyeSmileL", "eyeSmileR"], + browY: ["browLY", "browRY"], + browAngle: ["browLAngle", "browRAngle"], + browX: ["browLX", "browRX"], +} as const satisfies Record; + +export type SymmetricKey = keyof typeof SYMMETRIC_KEYS; +export type AuthoringKey = ChannelId | SymmetricKey; +export type AuthoringDelta = Partial>; + +/** 대칭 키를 L/R 채널로 펼친다. 같은 채널을 직접 키와 대칭 키로 동시에 주면 직접 키가 이긴다. */ +export function expandAuthoring(delta: AuthoringDelta): ChannelDelta { + const out: ChannelDelta = {}; + for (const [key, value] of Object.entries(delta) as Array<[AuthoringKey, number]>) { + if (key in SYMMETRIC_KEYS) { + for (const id of SYMMETRIC_KEYS[key as SymmetricKey]) { + if (!(id in delta)) out[id] = value; + } + } else { + out[key as ChannelId] = value; + } + } + return out; +} + +export function neutralVector(): ChannelVector { + const out = {} as ChannelVector; + for (const id of CHANNEL_IDS) out[id] = CHANNEL_SPECS[id].neutral; + return out; +} + +export function clampChannel(id: ChannelId, value: number): number { + const { min, max } = CHANNEL_SPECS[id]; + return value < min ? min : value > max ? max : value; +} + +/* ── 좌우 반전(미러) — 한쪽 방향성이 있는 클립을 반대로 재생할 때 ──── */ +const MIRROR_NEGATE: readonly ChannelId[] = ["headYaw", "headRoll", "bodyYaw", "bodyRoll", "gazeX", "mouthAsym"]; +const MIRROR_SWAP: ReadonlyArray = [ + ["eyeOpenL", "eyeOpenR"], + ["eyeSmileL", "eyeSmileR"], + ["browLY", "browRY"], + ["browLAngle", "browRAngle"], + ["browLX", "browRX"], +]; + +export function mirrorDelta(delta: ChannelDelta): ChannelDelta { + const out: ChannelDelta = { ...delta }; + for (const id of MIRROR_NEGATE) { + if (out[id] !== undefined) out[id] = -(out[id] as number); + } + for (const [l, r] of MIRROR_SWAP) { + const lv = delta[l]; + const rv = delta[r]; + if (lv === undefined) delete out[r]; + else out[r] = lv; + if (rv === undefined) delete out[l]; + else out[l] = rv; + } + return out; +} diff --git a/apps/web/src/components/avatar/engine/clipCatalog.ts b/apps/web/src/components/avatar/engine/clipCatalog.ts new file mode 100644 index 0000000..baf337d --- /dev/null +++ b/apps/web/src/components/avatar/engine/clipCatalog.ts @@ -0,0 +1,349 @@ +/* ===================================================================== + 아바타 v3 반응 클립 카탈로그 — docs/decisions/avatar-expression-engine-v3.md §6 정본 값. + 트랙 값은 표정층 결과에 더하는 delta다. 키프레임 [tMs, value, ease]에서 ease는 + 직전 키프레임에서 이 키프레임으로 가는 구간의 곡선이며 "hold"는 계단(직전 값 유지 후 도달)이다. + mirrorable 클립은 스케줄러가 시드 난수로 좌우 반전해 재생할 수 있다. + 키프레임 값 변경은 오케스트레이터 설계 결정이다. + ===================================================================== */ + +import { expandAuthoring, type AuthoringKey, type ChannelId } from "./channels"; + +export type ClipEase = "linear" | "in" | "out" | "inOut" | "hold"; +export type Keyframe = readonly [tMs: number, value: number, ease?: ClipEase]; +export type IdleSuppress = "blink" | "saccade" | "lipsync" | "sway"; + +export const REACTION_CLIP_IDS = [ + "nod_small", + "nod_deep", + "head_shake", + "head_drop", + "look_away_side", + "look_down", + "glance_up", + "flinch", + "freeze", + "sigh", + "deep_breath", + "swallow", + "lip_press", + "lip_bite", + "scoff", + "eye_roll_small", + "shrug", + "lean_back", + "lean_forward", + "tear_up", + "blink_flurry", + "fidget_sway", + "forced_smile", + "nervous_laugh", + "jaw_clench", + "silence_hold", +] as const; + +export type ReactionClipId = (typeof REACTION_CLIP_IDS)[number]; + +interface ClipSpec { + label: string; + durationMs: number; + fadeInMs: number; + fadeOutMs: number; + priority: 1 | 2 | 3; + cooldownMs: number; + mirrorable?: boolean; + suppress?: readonly IdleSuppress[]; + tracks: Partial>; +} + +export interface ReactionClip extends Omit { + id: ReactionClipId; + tracks: Partial>; +} + +/** 시작 0 → peak 도달 → holdUntil까지 유지 → end에 0 복귀하는 사다리꼴 트랙. */ +function plateau(peak: number, riseMs: number, holdUntilMs: number, endMs: number, rise: ClipEase = "out"): readonly Keyframe[] { + return [ + [0, 0], + [riseMs, peak, rise], + [holdUntilMs, peak, "hold"], + [endMs, 0, "inOut"], + ]; +} + +const CLIP_SPECS: Record = { + nod_small: { + label: "작게 끄덕", + durationMs: 900, fadeInMs: 80, fadeOutMs: 150, priority: 1, cooldownMs: 1500, + tracks: { + headPitch: [[0, 0], [180, -0.15, "out"], [360, 0, "inOut"], [540, -0.12, "out"], [760, 0, "inOut"]], + }, + }, + nod_deep: { + label: "깊게 끄덕", + durationMs: 1400, fadeInMs: 100, fadeOutMs: 200, priority: 2, cooldownMs: 3000, + tracks: { + headPitch: plateau(-0.35, 450, 800, 1300, "inOut"), + eyeOpen: plateau(-0.2, 450, 800, 1300, "inOut"), + }, + }, + head_shake: { + label: "고개 젓기", + durationMs: 1100, fadeInMs: 80, fadeOutMs: 150, priority: 2, cooldownMs: 2500, + tracks: { + headYaw: [[0, 0], [180, 0.2, "inOut"], [420, -0.2, "inOut"], [660, 0.14, "inOut"], [880, -0.06, "inOut"], [1100, 0, "inOut"]], + }, + }, + head_drop: { + label: "고개 떨굼", + durationMs: 3200, fadeInMs: 150, fadeOutMs: 400, priority: 2, cooldownMs: 6000, + suppress: ["saccade"], + tracks: { + headPitch: plateau(-0.45, 600, 2600, 3200), + gazeY: plateau(-0.6, 400, 2700, 3200), + shoulderRaise: plateau(-0.3, 700, 2600, 3200), + eyeOpen: plateau(-0.2, 600, 2600, 3200), + }, + }, + look_away_side: { + label: "옆으로 시선 회피", + durationMs: 2400, fadeInMs: 100, fadeOutMs: 300, priority: 1, cooldownMs: 4000, mirrorable: true, + suppress: ["saccade"], + tracks: { + gazeX: plateau(0.7, 200, 2000, 2400), + headYaw: plateau(0.15, 450, 2000, 2400), + }, + }, + look_down: { + label: "시선 내림", + durationMs: 2200, fadeInMs: 100, fadeOutMs: 300, priority: 1, cooldownMs: 3000, + suppress: ["saccade"], + tracks: { + gazeY: plateau(-0.7, 250, 1800, 2200), + headPitch: plateau(-0.1, 400, 1800, 2200), + eyeOpen: plateau(-0.25, 250, 1800, 2200), + }, + }, + glance_up: { + label: "힐끗 봄", + durationMs: 1100, fadeInMs: 60, fadeOutMs: 200, priority: 1, cooldownMs: 3000, + suppress: ["saccade"], + tracks: { + gazeY: plateau(0.35, 150, 650, 1100), + eyeOpen: plateau(0.1, 150, 650, 1100), + }, + }, + flinch: { + label: "움찔", + durationMs: 700, fadeInMs: 30, fadeOutMs: 250, priority: 3, cooldownMs: 2000, + suppress: ["blink"], + tracks: { + headPitch: [[0, 0], [90, 0.15, "out"], [700, 0, "inOut"]], + bodyLean: [[0, 0], [100, -0.25, "out"], [700, 0, "inOut"]], + shoulderRaise: [[0, 0], [90, 0.5, "out"], [700, 0, "inOut"]], + eyeOpen: [[0, 0], [60, -0.8, "out"], [160, 0.15, "out"], [700, 0, "inOut"]], + browY: [[0, 0], [100, 0.3, "out"], [700, 0, "inOut"]], + }, + }, + freeze: { + label: "얼어붙음", + durationMs: 1800, fadeInMs: 60, fadeOutMs: 400, priority: 3, cooldownMs: 4000, + suppress: ["blink", "saccade", "sway"], + tracks: { + eyeOpen: plateau(0.25, 120, 1500, 1800), + browY: plateau(0.35, 120, 1500, 1800), + mouthOpen: plateau(0.15, 150, 1500, 1800), + shoulderRaise: plateau(0.3, 150, 1500, 1800), + }, + }, + sigh: { + label: "한숨", + durationMs: 2600, fadeInMs: 100, fadeOutMs: 300, priority: 2, cooldownMs: 8000, + suppress: ["blink"], + tracks: { + shoulderRaise: [[0, 0], [800, 0.35, "inOut"], [1000, 0.35, "hold"], [2000, -0.3, "inOut"], [2600, 0, "inOut"]], + headPitch: [[0, 0], [800, 0.08, "inOut"], [2000, -0.15, "inOut"], [2600, 0, "inOut"]], + eyeOpen: [[0, 0], [900, 0, "hold"], [1500, -0.5, "inOut"], [2300, -0.1, "inOut"], [2600, 0, "inOut"]], + mouthOpen: [[0, 0], [1000, 0, "hold"], [1300, 0.2, "out"], [1900, 0.05, "inOut"], [2200, 0, "inOut"]], + bodyLean: [[0, 0], [800, 0.05, "inOut"], [2000, -0.1, "inOut"], [2600, 0, "inOut"]], + }, + }, + deep_breath: { + label: "심호흡", + durationMs: 3600, fadeInMs: 150, fadeOutMs: 300, priority: 1, cooldownMs: 10000, + tracks: { + shoulderRaise: plateau(0.4, 1500, 1800, 3400, "inOut"), + bodyLean: [[0, 0], [1500, 0.08, "inOut"], [3400, 0, "inOut"]], + eyeOpen: [[0, 0], [1500, -0.3, "inOut"], [3000, -0.1, "inOut"], [3600, 0, "inOut"]], + headPitch: [[0, 0], [1500, 0.1, "inOut"], [3400, 0, "inOut"]], + }, + }, + swallow: { + label: "침 삼킴", + durationMs: 800, fadeInMs: 50, fadeOutMs: 150, priority: 1, cooldownMs: 3000, + tracks: { + chinRaise: [[0, 0], [200, 0.4, "out"], [450, 0, "inOut"]], + mouthPress: [[0, 0], [150, 0.5, "out"], [500, 0.2, "inOut"], [800, 0, "inOut"]], + headPitch: [[0, 0], [250, -0.05, "inOut"], [800, 0, "inOut"]], + }, + }, + lip_press: { + label: "입술 누름", + durationMs: 1800, fadeInMs: 80, fadeOutMs: 250, priority: 1, cooldownMs: 3000, + tracks: { + mouthPress: plateau(0.7, 200, 1400, 1800), + mouthForm: plateau(-0.15, 200, 1400, 1800), + chinRaise: plateau(0.2, 200, 1400, 1800), + }, + }, + lip_bite: { + label: "입술 깨묾", + durationMs: 1600, fadeInMs: 80, fadeOutMs: 250, priority: 1, cooldownMs: 4000, mirrorable: true, + tracks: { + mouthPress: plateau(0.6, 180, 1200, 1600), + mouthAsym: plateau(0.35, 180, 1200, 1600), + gazeY: plateau(-0.3, 250, 1200, 1600), + }, + }, + scoff: { + label: "피식·코웃음", + durationMs: 1200, fadeInMs: 60, fadeOutMs: 250, priority: 2, cooldownMs: 4000, mirrorable: true, + tracks: { + mouthAsym: plateau(0.6, 150, 800, 1200), + headYaw: plateau(0.1, 200, 800, 1200), + gazeY: plateau(0.3, 200, 700, 1100), + headPitch: [[0, 0], [120, 0.08, "out"], [600, 0, "inOut"]], + mouthOpen: [[0, 0], [100, 0.08, "out"], [250, 0, "inOut"]], + }, + }, + eye_roll_small: { + label: "눈 굴림", + durationMs: 1100, fadeInMs: 60, fadeOutMs: 200, priority: 2, cooldownMs: 6000, mirrorable: true, + suppress: ["saccade"], + tracks: { + gazeY: [[0, 0], [250, 0.6, "out"], [550, 0.5, "inOut"], [900, 0.1, "inOut"], [1100, 0, "inOut"]], + gazeX: [[0, 0], [250, 0.1, "out"], [550, 0.5, "inOut"], [900, 0.2, "inOut"], [1100, 0, "inOut"]], + eyeOpen: [[0, 0], [250, -0.1, "out"], [1100, 0, "inOut"]], + }, + }, + shrug: { + label: "어깨 으쓱", + durationMs: 1300, fadeInMs: 80, fadeOutMs: 200, priority: 1, cooldownMs: 3000, mirrorable: true, + tracks: { + shoulderRaise: plateau(0.6, 300, 700, 1300), + browY: plateau(0.3, 300, 700, 1300), + headRoll: [[0, 0], [300, 0.1, "out"], [1000, 0, "inOut"]], + mouthForm: [[0, 0], [300, -0.2, "out"], [1000, 0, "inOut"]], + }, + }, + lean_back: { + label: "물러남", + durationMs: 3000, fadeInMs: 150, fadeOutMs: 400, priority: 1, cooldownMs: 5000, + tracks: { + bodyLean: plateau(-0.4, 500, 2400, 3000), + headPitch: plateau(0.08, 500, 2400, 3000), + shoulderRaise: plateau(0.1, 500, 2400, 3000), + }, + }, + lean_forward: { + label: "다가옴", + durationMs: 3000, fadeInMs: 150, fadeOutMs: 400, priority: 1, cooldownMs: 5000, + tracks: { + bodyLean: plateau(0.3, 600, 2400, 3000), + eyeOpen: plateau(0.08, 600, 2400, 3000), + browY: plateau(0.1, 600, 2400, 3000), + }, + }, + tear_up: { + label: "눈물 고임", + durationMs: 4000, fadeInMs: 200, fadeOutMs: 800, priority: 2, cooldownMs: 12000, + tracks: { + tear: [[0, 0], [1500, 0.4, "inOut"], [3600, 0.4, "hold"], [4000, 0.25, "inOut"]], + chinRaise: plateau(0.35, 800, 3000, 4000), + browAngle: plateau(0.35, 800, 3000, 4000), + eyeOpen: plateau(-0.15, 800, 3000, 4000), + mouthPress: plateau(0.3, 800, 3000, 4000), + }, + }, + blink_flurry: { + label: "잦은 깜빡임", + durationMs: 1200, fadeInMs: 0, fadeOutMs: 0, priority: 1, cooldownMs: 3000, + suppress: ["blink"], + tracks: { + eyeOpen: [ + [0, 0], [60, -1, "in"], [130, 0, "out"], + [330, -1, "in"], [400, 0, "out"], + [620, -1, "in"], [690, 0, "out"], + [900, -1, "in"], [970, 0, "out"], + [1200, 0, "hold"], + ], + }, + }, + fidget_sway: { + label: "몸 흔들림", + durationMs: 2400, fadeInMs: 150, fadeOutMs: 300, priority: 1, cooldownMs: 5000, mirrorable: true, + suppress: ["sway"], + tracks: { + bodyRoll: [[0, 0], [400, 0.12, "inOut"], [900, -0.1, "inOut"], [1400, 0.08, "inOut"], [1900, -0.05, "inOut"], [2400, 0, "inOut"]], + headRoll: [[0, 0], [450, -0.06, "inOut"], [950, 0.05, "inOut"], [1500, -0.03, "inOut"], [2400, 0, "inOut"]], + gazeX: [[0, 0], [500, 0.3, "inOut"], [1100, -0.2, "inOut"], [1700, 0.1, "inOut"], [2400, 0, "inOut"]], + }, + }, + forced_smile: { + label: "억지 미소", + durationMs: 2200, fadeInMs: 100, fadeOutMs: 300, priority: 2, cooldownMs: 3000, + tracks: { + mouthForm: plateau(0.4, 250, 1800, 2200), + mouthPress: plateau(0.2, 250, 1800, 2200), + browAngle: plateau(0.15, 250, 1800, 2200), + }, + }, + nervous_laugh: { + label: "어색한 웃음", + durationMs: 1400, fadeInMs: 60, fadeOutMs: 250, priority: 2, cooldownMs: 5000, mirrorable: true, + tracks: { + mouthForm: [[0, 0], [150, 0.45, "out"], [900, 0.3, "inOut"], [1400, 0, "inOut"]], + mouthOpen: [[0, 0], [150, 0.25, "out"], [300, 0.1, "inOut"], [450, 0.25, "out"], [600, 0.1, "inOut"], [900, 0, "inOut"]], + eyeSmile: [[0, 0], [150, 0.3, "out"], [900, 0.1, "inOut"], [1400, 0, "inOut"]], + gazeX: plateau(0.4, 200, 1100, 1400), + headPitch: [[0, 0], [150, -0.08, "out"], [300, 0, "inOut"], [450, -0.08, "out"], [600, 0, "inOut"]], + shoulderRaise: [[0, 0], [150, 0.15, "out"], [600, 0.15, "hold"], [1000, 0, "inOut"]], + browAngle: plateau(0.2, 150, 900, 1400), + }, + }, + jaw_clench: { + label: "이 악묾", + durationMs: 2000, fadeInMs: 80, fadeOutMs: 300, priority: 2, cooldownMs: 4000, + tracks: { + mouthPress: plateau(0.8, 200, 1600, 2000), + browX: plateau(-0.4, 200, 1600, 2000), + browY: plateau(-0.2, 200, 1600, 2000), + lidTension: plateau(0.4, 200, 1600, 2000), + headPitch: plateau(-0.05, 200, 1600, 2000), + }, + }, + silence_hold: { + label: "침묵 유지", + durationMs: 4000, fadeInMs: 200, fadeOutMs: 400, priority: 2, cooldownMs: 6000, + suppress: ["blink", "saccade", "sway", "lipsync"], + tracks: { + gazeY: plateau(-0.55, 500, 3500, 4000), + headPitch: plateau(-0.12, 500, 3500, 4000), + eyeOpen: [[0, 0], [500, -0.2, "out"], [1800, -0.2, "hold"], [2000, -0.9, "inOut"], [2250, -0.2, "inOut"], [3500, -0.2, "hold"], [4000, 0, "inOut"]], + }, + }, +}; + +function expandTracks(tracks: ClipSpec["tracks"]): ReactionClip["tracks"] { + const out: Partial> = {}; + for (const [key, frames] of Object.entries(tracks) as Array<[AuthoringKey, readonly Keyframe[]]>) { + for (const id of Object.keys(expandAuthoring({ [key]: 1 })) as ChannelId[]) out[id] = frames; + } + return out; +} + +export const REACTION_CLIPS: Record = Object.fromEntries( + REACTION_CLIP_IDS.map((id) => { + const { tracks, ...rest } = CLIP_SPECS[id]; + return [id, { id, ...rest, tracks: expandTracks(tracks) }]; + }), +) as Record; diff --git a/apps/web/src/components/avatar/engine/clipPlayer.ts b/apps/web/src/components/avatar/engine/clipPlayer.ts new file mode 100644 index 0000000..43d6956 --- /dev/null +++ b/apps/web/src/components/avatar/engine/clipPlayer.ts @@ -0,0 +1,78 @@ +/* ===================================================================== + 아바타 v3 반응 클립 재생기 — 결정문 §6.1 키프레임 보간·엔벨로프·합성. + 순수 함수뿐이다. 시간은 인자로만 받는다. + ===================================================================== */ + +import { mirrorDelta, type ChannelDelta, type ChannelId } from "./channels"; +import type { ClipEase, Keyframe, ReactionClip } from "./clipCatalog"; + +function ease(kind: ClipEase, t: number): number { + switch (kind) { + case "in": + return t * t; + case "out": + return 1 - (1 - t) * (1 - t); + case "inOut": + return 3 * t * t - 2 * t * t * t; + case "hold": + return t >= 1 ? 1 : 0; + case "linear": + default: + return t; + } +} + +function sampleTrack(frames: readonly Keyframe[], tMs: number): number { + const first = frames[0]; + if (tMs <= first[0]) return first[1]; + const last = frames[frames.length - 1]; + if (tMs >= last[0]) return last[1]; + for (let i = 1; i < frames.length; i++) { + const [t1, v1, arrivalEase] = frames[i]; + if (tMs > t1) continue; + const [t0, v0] = frames[i - 1]; + const span = t1 - t0; + const localT = span <= 0 ? 1 : (tMs - t0) / span; + const eased = ease(arrivalEase ?? "linear", localT); + return v0 + (v1 - v0) * eased; + } + return last[1]; +} + +/** 클립의 tMs 시점 채널 delta(가산값). t가 구간 밖이면 빈 delta. */ +export function sampleClip(clip: ReactionClip, tMs: number): ChannelDelta { + if (tMs < 0 || tMs > clip.durationMs) return {}; + const out: ChannelDelta = {}; + for (const id of Object.keys(clip.tracks) as ChannelId[]) { + const frames = clip.tracks[id]; + if (!frames || frames.length === 0) continue; + out[id] = sampleTrack(frames, tMs); + } + return out; +} + +/** fadeIn→hold→fadeOut 엔벨로프(0..1). 구간 밖은 0. */ +export function envelope(clip: ReactionClip, tMs: number): number { + if (tMs < 0 || tMs > clip.durationMs) return 0; + const { fadeInMs, fadeOutMs, durationMs } = clip; + if (fadeInMs > 0 && tMs < fadeInMs) return tMs / fadeInMs; + const fadeOutStart = durationMs - fadeOutMs; + if (fadeOutMs > 0 && tMs > fadeOutStart) return (durationMs - tMs) / fadeOutMs; + return 1; +} + +export interface ResolveClipOptions { + weight?: number; + mirror?: boolean; +} + +/** sampleClip × envelope × weight, mirror면 좌우 반전까지 적용한 최종 delta. */ +export function resolveClipDelta(clip: ReactionClip, tMs: number, opts: ResolveClipOptions = {}): ChannelDelta { + const raw = sampleClip(clip, tMs); + const scale = envelope(clip, tMs) * (opts.weight ?? 1); + const out: ChannelDelta = {}; + for (const id of Object.keys(raw) as ChannelId[]) { + out[id] = (raw[id] as number) * scale; + } + return opts.mirror ? mirrorDelta(out) : out; +} diff --git a/apps/web/src/components/avatar/engine/coSpeech.ts b/apps/web/src/components/avatar/engine/coSpeech.ts new file mode 100644 index 0000000..4d28d8c --- /dev/null +++ b/apps/web/src/components/avatar/engine/coSpeech.ts @@ -0,0 +1,452 @@ +/* ===================================================================== + 아바타 v3 발화 동반층 — 결정문 §5.5 정본. 말할 때 머리·몸·눈썹·시선·깜빡임을 + 만드는 순수 모듈이다. 발화 타임라인(§5.4, lipsync.ts)의 구·모음 목표·강세·휴지를 + 입력으로 시드 난수 1회로 계획(CoSpeechPlan)을 만들고, 매 프레임 그 계획을 + 표본해 채널 delta와 깜빡임 요청을 낸다. 렌더러·DOM과 무관하다. + 스프링(진입·이탈 시 매끄럽게 잇기)은 여기 두지 않는다 — engine.ts가 이 결과를 + 목표값으로 스프링한다(발화층 lipsync 패턴과 동일, §5.4 "표본·평활" 주석 참고). + ===================================================================== */ + +import type { ChannelDelta } from "./channels"; +import { createRng } from "./rng"; +import { sampleEnvelope, type SpeechEnvelope } from "./speechEnvelope"; +import type { SpeechPauseWindow, SpeechPhraseInfo, SpeechTimeline } from "./lipsync"; + +/** demeanor.speech에서 발화 동반층이 쓰는 부분만 뽑은 것(순환 참조 방지 — demeanorDefaults.ts를 import하지 않는다). */ +export interface CoSpeechStyle { + /** 모든 크기(깜빡임 제외)에 곱하는 배율(§5.5). */ + gesture: number; + /** 구를 시작할 때 시선을 피할 확률. */ + gazeAvertProb: number; +} + +interface Breakpoint { + t: number; + v: number; +} + +interface StressPulseSpec { + peakMs: number; + riseMs: number; + fallMs: number; + headPitch: number; + bodyLean: number; + shoulderRaise: number; +} + +interface PhraseEndCosineSpec { + kind: "cosine"; + peakMs: number; + riseMs: number; + fallMs: number; + headPitch: number; + headRoll: number; +} + +interface PhraseEndTrapezoidSpec { + kind: "trapezoid"; + breakpoints: Breakpoint[]; + headPitch: number; + headRoll: number; +} + +type PhraseEndSpec = PhraseEndCosineSpec | PhraseEndTrapezoidSpec; + +interface BreathSpec { + breakpoints: Breakpoint[]; + shoulderRaise: number; + bodyLean: number; +} + +interface BrowSpec { + breakpoints: Breakpoint[]; + browLY: number; + browRY: number; +} + +interface GazeSpec { + breakpoints: Breakpoint[]; + gazeX: number; + gazeY: number; +} + +export interface CoSpeechPlan { + totalDurationMs: number; + /** 구 표류(§5.5) — 발화 전체를 잇는 연속 곡선. 스프레드 순서: [진입 전 0, 각 구 진입·유지, 마지막 구 뒤 700ms 복귀]. */ + driftYaw: Breakpoint[]; + driftRoll: Breakpoint[]; + driftPitch: Breakpoint[]; + /** 강세 박(§5.5) — 올림 코사인 펄스 목록. */ + stressPulses: StressPulseSpec[]; + /** 구 끝(§5.5) — 평서는 코사인 끄덕임, 질문은 사다리꼴 들림. 이어짐은 항목 없음. */ + phraseEndPulses: PhraseEndSpec[]; + /** 들숨(§5.5). */ + breathPulses: BreathSpec[]; + /** 눈썹 박(§5.5). */ + browPulses: BrowSpec[]; + /** 시선 회피(§5.5). */ + gazePulses: GazeSpec[]; + /** 깜빡임 요청 시각(발화 시작 기준 ms, 확률 필터링 완료, 오름차순). */ + blinkRequests: number[]; +} + +export interface CoSpeechSample { + delta: ChannelDelta; + blinkNow?: boolean; +} + +function clamp01(v: number): number { + return v < 0 ? 0 : v > 1 ? 1 : v; +} + +function smoothstep(u: number): number { + const c = clamp01(u); + return c * c * (3 - 2 * c); +} + +/** 올림 코사인 b(t)(§5.5): 정점 전 riseMs 동안 0→1, 정점 뒤 fallMs 동안 1→0. */ +function raisedCosine(t: number, peakMs: number, riseMs: number, fallMs: number): number { + if (t < peakMs) { + if (riseMs <= 0) return 0; + const u = (t - (peakMs - riseMs)) / riseMs; + if (u <= 0) return 0; + if (u >= 1) return 1; + return 0.5 * (1 - Math.cos(Math.PI * u)); + } + if (fallMs <= 0) return t === peakMs ? 1 : 0; + const u = (t - peakMs) / fallMs; + if (u >= 1) return 0; + return 0.5 * (1 + Math.cos(Math.PI * u)); +} + +/** breakpoints를 smoothstep으로 잇는 구간별 표본(구 표류·사다리꼴 펄스 공용). 구간 밖은 양끝 값으로 고정된다. */ +function sampleBreakpoints(bps: readonly Breakpoint[], t: number): number { + if (bps.length === 0) return 0; + if (t <= bps[0].t) return bps[0].v; + const lastBp = bps[bps.length - 1]; + if (t >= lastBp.t) return lastBp.v; + let i = 0; + while (i < bps.length - 1 && bps[i + 1].t <= t) i++; + const a = bps[i]; + const b = bps[i + 1]; + const span = b.t - a.t; + const u = span > 0 ? (t - a.t) / span : 1; + return a.v + (b.v - a.v) * smoothstep(u); +} + +/** 뒤 breakpoint의 t가 앞보다 앞서지 않게 고정한다(§5.5 "구마다 새로 뽑아" 곡선을 이어붙일 때, + 아주 짧은 구가 연달아 나와 램프 구간이 겹치는 드문 경우의 근사 — 작업 보고 참고). */ +function pushBp(arr: Breakpoint[], t: number, v: number): void { + const last = arr[arr.length - 1]; + arr.push({ t: last ? Math.max(t, last.t) : t, v }); +} + +interface PerPhraseDraw { + yawSign: number; + yawMag: number; + rollSign: number; + rollMag: number; + pitchVal: number; + browOn: boolean; + gazeOn: boolean; + gazeHoldMs: number; +} + +const DRIFT_RAMP_LEAD_MS = 100; +const DRIFT_RAMP_MS = 450; +const DRIFT_RELEASE_MS = 700; + +function buildDrift( + phrases: readonly SpeechPhraseInfo[], + draws: readonly PerPhraseDraw[], + g: number, +): { yaw: Breakpoint[]; roll: Breakpoint[]; pitch: Breakpoint[] } { + const yaw: Breakpoint[] = []; + const roll: Breakpoint[] = []; + const pitch: Breakpoint[] = []; + let lastYaw = 0; + let lastRoll = 0; + let lastPitch = 0; + let lastEndMs: number | null = null; + + phrases.forEach((ph, i) => { + if (ph.syllableTargetsMs.length === 0) return; + const draw = draws[i]; + const rampStart = ph.startMs - DRIFT_RAMP_LEAD_MS; + const rampEnd = rampStart + DRIFT_RAMP_MS; + const targetYaw = draw.yawSign * draw.yawMag * g; + const targetRoll = draw.rollSign * draw.rollMag * g; + const targetPitch = draw.pitchVal * g; + + pushBp(yaw, rampStart, lastYaw); + pushBp(yaw, rampEnd, targetYaw); + pushBp(roll, rampStart, lastRoll); + pushBp(roll, rampEnd, targetRoll); + pushBp(pitch, rampStart, lastPitch); + pushBp(pitch, rampEnd, targetPitch); + + lastYaw = targetYaw; + lastRoll = targetRoll; + lastPitch = targetPitch; + lastEndMs = ph.endMs; + }); + + if (lastEndMs !== null) { + pushBp(yaw, lastEndMs, lastYaw); + pushBp(yaw, lastEndMs + DRIFT_RELEASE_MS, 0); + pushBp(roll, lastEndMs, lastRoll); + pushBp(roll, lastEndMs + DRIFT_RELEASE_MS, 0); + pushBp(pitch, lastEndMs, lastPitch); + pushBp(pitch, lastEndMs + DRIFT_RELEASE_MS, 0); + } + + return { yaw, roll, pitch }; +} + +function buildStressPulses(phrases: readonly SpeechPhraseInfo[], g: number): StressPulseSpec[] { + const out: StressPulseSpec[] = []; + for (const ph of phrases) { + const mul = (ph.kind === "exclaim" ? 1.4 : 1) * g; + for (const idx of ph.stressedIndices) { + const vowelMs = ph.syllableTargetsMs[idx]; + if (vowelMs === undefined) continue; + out.push({ + peakMs: vowelMs - 80, + riseMs: 120, + fallMs: 260, + headPitch: -0.25 * mul, + bodyLean: 0.12 * mul, + shoulderRaise: 0.08 * mul, + }); + } + } + return out; +} + +function buildPhraseEndPulses(phrases: readonly SpeechPhraseInfo[], draws: readonly PerPhraseDraw[], g: number): PhraseEndSpec[] { + const out: PhraseEndSpec[] = []; + phrases.forEach((ph, i) => { + const n = ph.syllableTargetsMs.length; + if (n === 0) return; + const lastMs = ph.syllableTargetsMs[n - 1]; + if (ph.kind === "statement") { + out.push({ kind: "cosine", peakMs: lastMs, riseMs: 150, fallMs: 400, headPitch: -0.3 * g, headRoll: 0 }); + return; + } + if (ph.kind === "question") { + /* "마지막 두 음절에 걸쳐" — 1음절 구는 음절 하나 분량(150ms)을 앞당겨 근사한다. */ + const riseStart = n >= 2 ? ph.syllableTargetsMs[n - 2] : lastMs - 150; + const holdEnd = ph.endMs + 300; + const releaseEnd = holdEnd + 400; + out.push({ + kind: "trapezoid", + breakpoints: [ + { t: riseStart, v: 0 }, + { t: lastMs, v: 1 }, + { t: holdEnd, v: 1 }, + { t: releaseEnd, v: 0 }, + ], + headPitch: 0.12 * g, + headRoll: 0.15 * g * draws[i].rollSign, + }); + } + /* 이어짐(",")은 구 끝 동작이 없다(§5.5). */ + }); + return out; +} + +const BREATH_LEAD_MS = 250; +const BREATH_PAUSE_MIN_MS = 200; +const BREATH_EXHALE_FRACTION = 0.4; + +function buildBreathPulses(phrases: readonly SpeechPhraseInfo[], g: number): BreathSpec[] { + const out: BreathSpec[] = []; + phrases.forEach((ph, i) => { + if (ph.syllableTargetsMs.length === 0) return; + const prev = phrases[i - 1]; + const gapMs = i === 0 ? Infinity : ph.startMs - (prev?.endMs ?? ph.startMs); + if (i !== 0 && gapMs < BREATH_PAUSE_MIN_MS) return; + const dur = Math.max(1, ph.endMs - ph.startMs); + out.push({ + breakpoints: [ + { t: ph.startMs - BREATH_LEAD_MS, v: 0 }, + { t: ph.startMs, v: 1 }, + { t: ph.startMs + dur * BREATH_EXHALE_FRACTION, v: 0 }, + ], + shoulderRaise: 0.25 * g, + bodyLean: -0.05 * g, + }); + }); + return out; +} + +function buildBrowPulses(phrases: readonly SpeechPhraseInfo[], draws: readonly PerPhraseDraw[], g: number): BrowSpec[] { + const out: BrowSpec[] = []; + phrases.forEach((ph, i) => { + if (!draws[i].browOn) return; + const firstStressIdx = ph.stressedIndices[0]; + const targetMs = firstStressIdx === undefined ? undefined : ph.syllableTargetsMs[firstStressIdx]; + if (targetMs === undefined) return; + out.push({ + breakpoints: [ + { t: targetMs - 100, v: 0 }, + { t: targetMs, v: 1 }, + { t: targetMs + 150, v: 1 }, + { t: targetMs + 400, v: 0 }, + ], + browLY: 0.15 * g, + browRY: 0.15 * g, + }); + }); + return out; +} + +const GAZE_AVERT_TAIL_MS = 400; + +function buildGazePulses( + phrases: readonly SpeechPhraseInfo[], + draws: readonly PerPhraseDraw[], + g: number, + totalDurationMs: number, +): GazeSpec[] { + const out: GazeSpec[] = []; + phrases.forEach((ph, i) => { + const draw = draws[i]; + if (!draw.gazeOn) return; + if (ph.syllableTargetsMs.length === 0) return; + if (ph.startMs >= totalDurationMs - GAZE_AVERT_TAIL_MS) return; + out.push({ + breakpoints: [ + { t: ph.startMs, v: 0 }, + { t: ph.startMs + 150, v: 1 }, + { t: ph.startMs + 150 + draw.gazeHoldMs, v: 1 }, + { t: ph.startMs + 150 + draw.gazeHoldMs + 250, v: 0 }, + ], + gazeX: 0.25 * draw.yawSign * g, + gazeY: -0.15 * g, + }); + }); + return out; +} + +const BLINK_PAUSE_MIN_MS = 250; +const BLINK_REQUEST_OFFSET_MS = 50; +const BLINK_PROB = 0.6; + +function buildBlinkRequests(pauses: readonly SpeechPauseWindow[], rng: ReturnType): number[] { + const out: number[] = []; + for (const pause of pauses) { + if (pause.endMs - pause.startMs < BLINK_PAUSE_MIN_MS) continue; + if (rng.next() < BLINK_PROB) out.push(pause.startMs + BLINK_REQUEST_OFFSET_MS); + } + return out; +} + +/** + * 발화 타임라인(§5.4)과 시드로 발화 동반 계획을 한 번 만든다(§5.5). + * rng 소비 순서(같은 시드 재현성의 근거): 구마다 순서대로 + * [yaw 크기, roll 크기, pitch 값, (첫 구만) yaw 부호, roll 부호, 눈썹 박 확률, 시선 회피 확률, + * (시선 회피 발동 시) 유지 시간] → 이후 휴지 구간마다 순서대로 [깜빡임 확률]. + */ +export function buildCoSpeechPlan(timeline: SpeechTimeline, style: CoSpeechStyle, seed: number): CoSpeechPlan { + const rng = createRng(seed); + const g = style.gesture; + const phrases = timeline.phrases; + + let prevYawSign = 1; + const draws: PerPhraseDraw[] = phrases.map((ph, i) => { + const yawMag = rng.range(0.2, 0.4); + const rollMag = rng.range(0.1, 0.2); + const pitchVal = rng.range(-0.1, 0.05); + const yawSign = i === 0 ? (rng.next() < 0.5 ? -1 : 1) : -prevYawSign; + prevYawSign = yawSign; + const rollSign = rng.next() < 0.5 ? -1 : 1; + const browOn = ph.kind === "question" ? true : rng.next() < 0.5; + const gazeOn = rng.next() < style.gazeAvertProb; + const gazeHoldMs = gazeOn ? rng.range(500, 900) : 0; + return { yawSign, yawMag, rollSign, rollMag, pitchVal, browOn, gazeOn, gazeHoldMs }; + }); + + const drift = buildDrift(phrases, draws, g); + + return { + totalDurationMs: timeline.totalDurationMs, + driftYaw: drift.yaw, + driftRoll: drift.roll, + driftPitch: drift.pitch, + stressPulses: buildStressPulses(phrases, g), + phraseEndPulses: buildPhraseEndPulses(phrases, draws, g), + breathPulses: buildBreathPulses(phrases, g), + browPulses: buildBrowPulses(phrases, draws, g), + gazePulses: buildGazePulses(phrases, draws, g, timeline.totalDurationMs), + blinkRequests: buildBlinkRequests(timeline.pauses, rng), + }; +} + +/** + * 계획을 tMs(발화 시작 기준 ms) 시점으로 표본한다. prevTMs는 직전 프레임의 같은 기준 + * 시각이며, (prevTMs, tMs] 구간을 지난 깜빡임 요청이 있으면 blinkNow를 세운다(§5.5). + * 음량 들썩임(§5.5)은 envelope가 있을 때만 shoulderRaise에 더한다. + */ +export function sampleCoSpeech(plan: CoSpeechPlan, tMs: number, prevTMs: number, envelope?: SpeechEnvelope): CoSpeechSample { + let headPitch = sampleBreakpoints(plan.driftPitch, tMs); + let headRoll = sampleBreakpoints(plan.driftRoll, tMs); + const headYaw = sampleBreakpoints(plan.driftYaw, tMs); + let bodyLean = 0; + let shoulderRaise = 0; + let browLY = 0; + let browRY = 0; + let gazeX = 0; + let gazeY = 0; + + for (const p of plan.stressPulses) { + const b = raisedCosine(tMs, p.peakMs, p.riseMs, p.fallMs); + if (b === 0) continue; + headPitch += p.headPitch * b; + bodyLean += p.bodyLean * b; + shoulderRaise += p.shoulderRaise * b; + } + + for (const p of plan.phraseEndPulses) { + const b = p.kind === "cosine" ? raisedCosine(tMs, p.peakMs, p.riseMs, p.fallMs) : sampleBreakpoints(p.breakpoints, tMs); + if (b === 0) continue; + headPitch += p.headPitch * b; + headRoll += p.headRoll * b; + } + + for (const p of plan.breathPulses) { + const b = sampleBreakpoints(p.breakpoints, tMs); + if (b === 0) continue; + shoulderRaise += p.shoulderRaise * b; + bodyLean += p.bodyLean * b; + } + + if (envelope) { + const p90 = envelope.p90 || 1e-6; + const env = sampleEnvelope(envelope, tMs); + shoulderRaise += 0.1 * Math.max(0, env / p90 - 0.5); + } + + for (const p of plan.browPulses) { + const b = sampleBreakpoints(p.breakpoints, tMs); + if (b === 0) continue; + browLY += p.browLY * b; + browRY += p.browRY * b; + } + + for (const p of plan.gazePulses) { + const b = sampleBreakpoints(p.breakpoints, tMs); + if (b === 0) continue; + gazeX += p.gazeX * b; + gazeY += p.gazeY * b; + } + + const delta: ChannelDelta = { headYaw, headPitch, headRoll, bodyLean, shoulderRaise, browLY, browRY, gazeX, gazeY }; + const blinkNow = plan.blinkRequests.some((e) => e > prevTMs && e <= tMs); + return blinkNow ? { delta, blinkNow: true } : { delta }; +} + +/** 표시용: tMs 시점에 강세 박이 활성인지(rise~fall 구간 안인지). Lab 채널 표 옆 표시에 쓴다. */ +export function isStressPulseActive(plan: CoSpeechPlan, tMs: number): boolean { + return plan.stressPulses.some((p) => tMs >= p.peakMs - p.riseMs && tMs <= p.peakMs + p.fallMs); +} diff --git a/apps/web/src/components/avatar/engine/demeanorDefaults.ts b/apps/web/src/components/avatar/engine/demeanorDefaults.ts new file mode 100644 index 0000000..658309c --- /dev/null +++ b/apps/web/src/components/avatar/engine/demeanorDefaults.ts @@ -0,0 +1,198 @@ +/* ===================================================================== + 아바타 v3 페르소나 성향(demeanor) 기본값 — 결정문 §7.2 ⑤ / §8.3. + 기본 자세·표현 증폭·깜빡임·시선·idle 클립 빈도를 캐릭터 카드 설정 + (data/personas/P*.json, apps/api/app/services/persona.py SEED_PERSONAS)에서 읽어 정했다. + 4단계에서 캐릭터 카드 avatar.demeanor 블록으로 옮긴다. 값 변경은 설계 결정이다. + ===================================================================== */ + +import type { AvatarState } from "../persona"; +import { expandAuthoring, type AuthoringDelta, type ChannelDelta } from "./channels"; +import type { ReactionClipId } from "./clipCatalog"; + +export interface IdleClipRule { + clip: ReactionClipId; + /** 다음 발동까지 간격 범위(ms). 시드 난수로 고른다. */ + everyMs: readonly [number, number]; + /** 이 상태에서만 발동. 생략하면 idle·listening 모두. speaking·thinking에는 idle 클립을 쓰지 않는다. */ + when?: Extract; + /** 개방도가 이 값 미만일 때만 발동. 생략하면 제한 없음. */ + maxOpenness?: number; +} + +export interface Demeanor { + basePose: ChannelDelta; + /** 표정 delta 증폭. 억누르는 캐릭터는 1보다 작다. */ + expressivity: number; + blinkIntervalMs: readonly [number, number]; + blinkDurationMs: number; + saccadeIntervalMs: readonly [number, number]; + /** 시선 미세 이동 크기(gaze 채널 단위). */ + saccadeAmplitude: number; + /** 호흡 외 미세 몸 흔들림 크기(0..1 배율). */ + swayAmplitude: number; + idleClips: readonly IdleClipRule[]; + speech: SpeechStyle; +} + +/** 발화층(립싱크) 성향 — 결정문 §5.4. */ +export interface SpeechStyle { + /** 조음 폭: 발화 입 모양(open·wide·round)에 곱한다. 웅얼거리는 캐릭터는 작다. */ + articulation: number; + /** 오디오가 없을 때 텍스트만으로 타이밍을 만들 때의 발화 속도(음절/초). 한국어 대화체 평균 약 5.9. */ + syllablesPerSec: number; + /** 발화 동반 움직임(고개 표류·강세 박·들숨·눈썹 박, 결정문 §5.5) 크기 배율. 위축된 캐릭터는 작다. */ + gesture: number; + /** 구를 시작할 때 시선을 피할 확률(생각하며 말하기). */ + gazeAvertProb: number; +} + +function demeanor(spec: Omit & { basePose: AuthoringDelta }): Demeanor { + return { ...spec, basePose: expandAuthoring(spec.basePose) }; +} + +/* P1 서연 — 고2, 우울·자살사고, 비자발 내방. N 0.85·E 0.25, "감정 억누르고 무덤덤한 척", 불면. */ +const P1 = demeanor({ + basePose: { headPitch: -0.12, shoulderRaise: -0.2, eyeOpen: -0.12, gazeY: -0.15, bodyLean: -0.05 }, + /* 절제는 강도 곡선(낮은 강도 억제)과 기본 자세가 맡는다. 0.75는 강도 1에서도 표정이 안 읽혀 0.9로 올렸다(2단계-B 소유자 관찰). */ + expressivity: 0.9, + blinkIntervalMs: [2600, 5200], + blinkDurationMs: 150, + saccadeIntervalMs: [3000, 7000], + saccadeAmplitude: 0.12, + swayAmplitude: 0.3, + idleClips: [ + { clip: "sigh", everyMs: [25000, 45000], maxOpenness: 0.5 }, + { clip: "look_down", everyMs: [9000, 16000], when: "listening", maxOpenness: 0.6 }, + { clip: "look_away_side", everyMs: [12000, 20000], when: "listening", maxOpenness: 0.4 }, + { clip: "lip_press", everyMs: [15000, 30000] }, + ], + /* 작게 웅얼거리고 느리다(무덤덤한 척·에너지 낮음). */ + speech: { articulation: 0.6, syllablesPerSec: 5.0, gesture: 0.55, gazeAvertProb: 0.7 }, +}); + +/* P2 민재 — 32세 직장인, 범불안·신체화, 논리적·장황. */ +const P2 = demeanor({ + basePose: { shoulderRaise: 0.2, lidTension: 0.15, browAngle: 0.1 }, + expressivity: 0.85, + blinkIntervalMs: [1800, 3600], + blinkDurationMs: 110, + saccadeIntervalMs: [1800, 4200], + saccadeAmplitude: 0.18, + swayAmplitude: 0.4, + idleClips: [ + { clip: "swallow", everyMs: [10000, 18000] }, + { clip: "blink_flurry", everyMs: [14000, 26000], maxOpenness: 0.6 }, + { clip: "deep_breath", everyMs: [30000, 50000] }, + ], + /* 장황하고 빠르다. */ + speech: { articulation: 0.85, syllablesPerSec: 6.4, gesture: 0.9, gazeAvertProb: 0.5 }, +}); + +/* P3 지우 — 28세 미혼모, 역할 부담, 따뜻하지만 지친 톤. */ +const P3 = demeanor({ + basePose: { eyeOpen: -0.15, eyeSmile: 0.08, shoulderRaise: -0.15 }, + expressivity: 0.9, + blinkIntervalMs: [2200, 4600], + blinkDurationMs: 140, + saccadeIntervalMs: [2600, 5600], + saccadeAmplitude: 0.14, + swayAmplitude: 0.35, + idleClips: [ + { clip: "deep_breath", everyMs: [25000, 40000] }, + { clip: "sigh", everyMs: [30000, 50000], maxOpenness: 0.6 }, + { clip: "look_down", everyMs: [12000, 20000], when: "listening" }, + ], + /* 따뜻하지만 지친 톤. */ + speech: { articulation: 0.8, syllablesPerSec: 5.6, gesture: 0.7, gazeAvertProb: 0.45 }, +}); + +/* P4 하늘 — 고2, 시험불안·완벽주의, 또박또박·자기검열. */ +const P4 = demeanor({ + basePose: { shoulderRaise: 0.15, lidTension: 0.15, eyeOpen: 0.05 }, + expressivity: 1.0, + blinkIntervalMs: [1400, 3000], + blinkDurationMs: 100, + saccadeIntervalMs: [1500, 3500], + saccadeAmplitude: 0.2, + swayAmplitude: 0.5, + idleClips: [ + { clip: "blink_flurry", everyMs: [9000, 16000], maxOpenness: 0.6 }, + { clip: "fidget_sway", everyMs: [12000, 22000] }, + { clip: "swallow", everyMs: [14000, 24000] }, + { clip: "lip_bite", everyMs: [18000, 30000], maxOpenness: 0.5 }, + ], + /* 또박또박. */ + speech: { articulation: 0.95, syllablesPerSec: 6.2, gesture: 0.65, gazeAvertProb: 0.55 }, +}); + +/* P5 도윤 — 중3, 또래관계 소외, 무던한 척·반존대. */ +const P5 = demeanor({ + basePose: { bodyLean: -0.1, gazeX: 0.12, headPitch: -0.05 }, + expressivity: 0.7, + blinkIntervalMs: [2400, 4800], + blinkDurationMs: 120, + saccadeIntervalMs: [2500, 5500], + saccadeAmplitude: 0.15, + swayAmplitude: 0.35, + idleClips: [ + { clip: "look_away_side", everyMs: [8000, 14000], when: "listening", maxOpenness: 0.6 }, + { clip: "shrug", everyMs: [20000, 35000] }, + { clip: "look_down", everyMs: [15000, 25000] }, + ], + /* 무던한 척, 입을 크게 안 벌린다. */ + speech: { articulation: 0.65, syllablesPerSec: 5.8, gesture: 0.6, gazeAvertProb: 0.6 }, +}); + +/* P6 하린 — 고3, 진로갈등, 똑부러지지만 지친 톤. */ +const P6 = demeanor({ + basePose: { eyeOpen: -0.08 }, + expressivity: 0.9, + blinkIntervalMs: [2000, 4200], + blinkDurationMs: 120, + saccadeIntervalMs: [2200, 5000], + saccadeAmplitude: 0.14, + swayAmplitude: 0.3, + idleClips: [ + { clip: "lip_press", everyMs: [12000, 22000] }, + { clip: "deep_breath", everyMs: [25000, 40000] }, + { clip: "look_away_side", everyMs: [16000, 28000], maxOpenness: 0.5 }, + ], + speech: { articulation: 0.85, syllablesPerSec: 6.0, gesture: 0.75, gazeAvertProb: 0.45 }, +}); + +/* P7 도현 — 고3, 입시 번아웃, 무덤덤·자조. */ +const P7 = demeanor({ + basePose: { eyeOpen: -0.25, headPitch: -0.18, shoulderRaise: -0.3, headRoll: 0.05 }, + expressivity: 0.65, + blinkIntervalMs: [3000, 6000], + blinkDurationMs: 190, + saccadeIntervalMs: [3500, 8000], + saccadeAmplitude: 0.1, + swayAmplitude: 0.2, + idleClips: [ + { clip: "sigh", everyMs: [20000, 35000], maxOpenness: 0.6 }, + { clip: "look_down", everyMs: [10000, 18000] }, + { clip: "scoff", everyMs: [30000, 50000], maxOpenness: 0.4 }, + ], + /* 무덤덤·자조, 가장 작게 웅얼거린다. */ + speech: { articulation: 0.55, syllablesPerSec: 5.2, gesture: 0.4, gazeAvertProb: 0.65 }, +}); + +/* 성향 정의가 없는 페르소나(스튜디오 신규 등)의 기본값. */ +export const DEFAULT_DEMEANOR = demeanor({ + basePose: {}, + expressivity: 0.9, + blinkIntervalMs: [2200, 4800], + blinkDurationMs: 120, + saccadeIntervalMs: [2500, 6000], + saccadeAmplitude: 0.14, + swayAmplitude: 0.35, + idleClips: [{ clip: "look_down", everyMs: [15000, 25000], when: "listening" }], + speech: { articulation: 0.8, syllablesPerSec: 5.9, gesture: 0.75, gazeAvertProb: 0.5 }, +}); + +const DEMEANORS: Record = { P1, P2, P3, P4, P5, P6, P7 }; + +export function demeanorFor(personaCode: string | null | undefined): Demeanor { + return (personaCode && DEMEANORS[personaCode.toUpperCase()]) || DEFAULT_DEMEANOR; +} diff --git a/apps/web/src/components/avatar/engine/engine.ts b/apps/web/src/components/avatar/engine/engine.ts new file mode 100644 index 0000000..33269c3 --- /dev/null +++ b/apps/web/src/components/avatar/engine/engine.ts @@ -0,0 +1,596 @@ +/* ===================================================================== + 아바타 v3 표현 엔진 — 결정문 §3 구조·§5 표정층·§6 반응층·§7 공연 스크립트. + 렌더러와 무관한 순수 상태머신이다. 시계(nowMs)와 난수(rng)는 모두 주입되며 + 내부에서 Date/performance.now/Math.random을 직접 호출하지 않는다. + 렌더러는 evaluate(nowMs)가 돌려주는 채널 벡터만 읽는다. + ===================================================================== */ + +import { + CHANNEL_IDS, + CHANNEL_SPECS, + OVERLAY_MIN_INTENSITY, + clampChannel, + neutralVector, + type ChannelDelta, + type ChannelId, + type ChannelVector, +} from "./channels"; +import { EXPRESSION_PRESETS, intensityGain } from "./expressionPresets"; +import { REACTION_CLIPS, type IdleSuppress, type ReactionClipId } from "./clipCatalog"; +import { resolveClipDelta } from "./clipPlayer"; +import type { Demeanor } from "./demeanorDefaults"; +import { createRng, type Rng } from "./rng"; +import { SILENT_SHAPE, type SpeechShape } from "./lipsync"; +import type { AvatarExpression, AvatarState } from "../persona"; +import type { Performance } from "./performance"; + +export interface AvatarEngineOptions { + demeanor: Demeanor; + seed: number; + reducedMotion?: boolean; +} + +export interface TriggerClipOptions { + weight?: number; + mirror?: boolean; +} + +export interface PerformanceTimeline { + speechStartMs: number; + speechDurationMs: number; +} + +export interface DebugActiveClip { + id: ReactionClipId; + startedMs: number; + weight: number; + mirror: boolean; +} + +export interface DebugScheduledCue { + clip: ReactionClipId; + atMs: number; + source: string; +} + +export interface DebugSnapshot { + activeClips: DebugActiveClip[]; + scheduled: DebugScheduledCue[]; + surface: { expression: AvatarExpression; intensity: number }; + leakActive: boolean; +} + +export interface AvatarEngine { + setState(state: AvatarState, nowMs: number): void; + setOpenness(openness: number): void; + setSurface(expression: AvatarExpression, intensity: number): void; + /** 발화층(§5.4) 목표 입 모양. null이면 목표가 X(닫힘)로 스프링 수렴한다. */ + setSpeechShape(shape: SpeechShape | null): void; + /** 발화 동반층(§5.5) 목표 채널 delta(coSpeech.ts sampleCoSpeech 결과). speaking 중이면 + 그대로 통과시킨다(강세 박 타이밍을 지키기 위해 스프링을 걸지 않는다). speaking이 아니거나 + null이면 그 값에서 τ150ms 스프링으로 0으로 되돌아간다. reduced motion이면 적용하지 않는다. */ + setSpeechMotion(delta: ChannelDelta | null): void; + /** 발화 동반 깜빡임 요청(§5.5) — idle 깜빡임과 같은 경로로 지금 재생하고 idle 타이머를 다시 잡는다. */ + requestSpeechBlink(nowMs: number): void; + triggerClip(id: ReactionClipId, nowMs: number, opts?: TriggerClipOptions): boolean; + playPerformance(perf: Performance, timeline: PerformanceTimeline, nowMs: number): void; + evaluate(nowMs: number): ChannelVector; + debugSnapshot(): DebugSnapshot; +} + +interface ActiveClipEntry { + clipId: ReactionClipId; + startedMs: number; + weight: number; + mirror: boolean; + priority: 1 | 2 | 3; + evictAt?: number; +} + +interface ScheduledCue { + clip: ReactionClipId; + atMs: number; + weight: number; + source: string; +} + +interface LeakState { + expression: AvatarExpression; + intensity: number; + startAbsMs: number; + durationMs: number; +} + +interface IdleClipState { + nextAtMs: number; +} + +interface BlinkState { + nextAt: number; + startedAt: number | null; +} + +interface SaccadeState { + nextAt: number; + targetX: number; + targetY: number; + curX: number; + curY: number; +} + +const EVICT_FADE_MS = 150; + +/* ── 발화층 스프링(결정문 §5.4 "임계 감쇠 스프링 τ 45ms") ────────────────── + 표정층 입 스프링(160ms, channels.ts)과 별도로 여기서만 쓴다. sampleSpeech가 + 돌려주는 목표값(SpeechShape)을 매 프레임 이 스프링으로 평활해 채널에 합성한다. */ +const SPEECH_SPRING_TAU_MS = 45; + +/* ── 발화 동반층 스프링(결정문 §5.5 "발화가 끝나면 delta가 튀지 않게... 짧은 스프링으로 + 되돌린다") ── coSpeech.ts의 순수 표본은 진입·이탈 스프링을 갖지 않으므로, speaking이 아니거나 + reduced motion이면 목표를 0으로 두고 여기서 짧게 수렴시킨다. */ +const CO_SPEECH_SPRING_TAU_MS = 150; +const CO_SPEECH_CHANNELS: readonly ChannelId[] = [ + "headYaw", + "headPitch", + "headRoll", + "bodyLean", + "shoulderRaise", + "browLY", + "browRY", + "gazeX", + "gazeY", +]; + +interface SpringState { + value: number; + velocity: number; +} + +function zeroSpring(): SpringState { + return { value: 0, velocity: 0 }; +} + +/** 임계 감쇠 2차 시스템의 정확해. omega = 1/tauMs. */ +function springTowards(state: SpringState, target: number, dt: number, tauMs: number): SpringState { + if (tauMs <= 0 || dt <= 0) return { value: target, velocity: 0 }; + const omega = 1 / tauMs; + const diff = state.value - target; + const temp = (state.velocity + omega * diff) * dt; + const decay = Math.exp(-omega * dt); + return { + value: target + (diff + temp) * decay, + velocity: (state.velocity - omega * temp) * decay, + }; +} + +function filteredPresetDelta(expression: AvatarExpression, intensity: number, demeanor: Demeanor): ChannelDelta { + const preset = EXPRESSION_PRESETS[expression]; + const gain = intensityGain(intensity) * demeanor.expressivity; + const out: ChannelDelta = {}; + for (const id of Object.keys(preset) as ChannelId[]) { + const threshold = OVERLAY_MIN_INTENSITY[id]; + if (threshold !== undefined && intensity < threshold) continue; + out[id] = (preset[id] as number) * gain; + } + return out; +} + +function blendDelta(a: ChannelDelta, b: ChannelDelta, w: number): ChannelDelta { + const out: ChannelDelta = { ...a }; + for (const id of CHANNEL_IDS) { + const av = a[id] ?? 0; + const bv = b[id] ?? 0; + const v = av * (1 - w) + bv * w; + if (v !== 0 || av !== 0 || bv !== 0) out[id] = v; + } + return out; +} + +export function createAvatarEngine(options: AvatarEngineOptions): AvatarEngine { + const { demeanor } = options; + const reducedMotion = options.reducedMotion ?? false; + const rng: Rng = createRng(options.seed); + + let currentState: AvatarState = "idle"; + let openness = 0; + let surfaceExpr: AvatarExpression = "neutral"; + let surfaceIntensity = 0; + let pendingSpeechShape: SpeechShape | null = null; + let speechSpring = { open: zeroSpring(), wide: zeroSpring(), round: zeroSpring(), press: zeroSpring() }; + + let pendingSpeechMotion: ChannelDelta | null = null; + const coSpeechSpring: Partial> = {}; + for (const id of CO_SPEECH_CHANNELS) coSpeechSpring[id] = zeroSpring(); + + let leak: LeakState | null = null; + + const baseVector: ChannelVector = (() => { + const out = neutralVector(); + for (const id of Object.keys(demeanor.basePose) as ChannelId[]) { + out[id] += demeanor.basePose[id] as number; + } + return out; + })(); + + let smoothed: ChannelVector | null = null; + let lastEvalMs: number | null = null; + + let activeClips: ActiveClipEntry[] = []; + const cooldownMap = new Map(); + let scheduledCues: ScheduledCue[] = []; + + let idleClipStates: IdleClipState[] | null = null; + let blinkState: BlinkState | null = null; + let saccadeState: SaccadeState | null = null; + + function leakWeight(nowMs: number): number { + if (!leak) return 0; + const t = nowMs - leak.startAbsMs; + if (t < 0 || t > leak.durationMs) return 0; + const fade = Math.min(60, leak.durationMs / 2); + if (fade <= 0) return 1; + if (t < fade) return t / fade; + if (t > leak.durationMs - fade) return (leak.durationMs - t) / fade; + return 1; + } + + function activeSuppressions(nowMs: number): Set { + const out = new Set(); + for (const entry of activeClips) { + const clip = REACTION_CLIPS[entry.clipId]; + const t = nowMs - entry.startedMs; + if (t < 0 || t > clip.durationMs) continue; + for (const s of clip.suppress ?? []) out.add(s); + } + return out; + } + + function triggerClip(id: ReactionClipId, nowMs: number, opts: TriggerClipOptions = {}): boolean { + if (reducedMotion) return false; + const clip = REACTION_CLIPS[id]; + const last = cooldownMap.get(id); + if (last !== undefined && nowMs - last < clip.cooldownMs) return false; + cooldownMap.set(id, nowMs); + + let mirror = opts.mirror ?? false; + if (clip.mirrorable && opts.mirror === undefined) { + mirror = rng.next() < 0.5; + } + + const nonEvicting = activeClips.filter((e) => e.evictAt === undefined); + if (nonEvicting.length >= 2) { + let victim = nonEvicting[0]; + for (const e of nonEvicting) { + const vClip = REACTION_CLIPS[victim.clipId]; + const eClip = REACTION_CLIPS[e.clipId]; + if (eClip.priority < vClip.priority || (eClip.priority === vClip.priority && e.startedMs < victim.startedMs)) { + victim = e; + } + } + victim.evictAt = nowMs; + } + + activeClips.push({ + clipId: id, + startedMs: nowMs, + weight: opts.weight ?? 1, + mirror, + priority: clip.priority, + }); + return true; + } + + function setState(state: AvatarState, nowMs: number): void { + const prev = currentState; + currentState = state; + if (idleClipStates && (state === "idle" || state === "listening") && prev !== state) { + demeanor.idleClips.forEach((rule, i) => { + if (idleClipStates![i].nextAtMs < nowMs) { + idleClipStates![i] = { nextAtMs: nowMs + rng.range(rule.everyMs[0], rule.everyMs[1]) }; + } + }); + } + } + + function setOpenness(o: number): void { + openness = o; + } + + function setSurface(expression: AvatarExpression, intensity: number): void { + surfaceExpr = expression; + surfaceIntensity = intensity; + } + + function setSpeechShape(shape: SpeechShape | null): void { + pendingSpeechShape = shape; + } + + function setSpeechMotion(delta: ChannelDelta | null): void { + pendingSpeechMotion = delta; + } + + function requestSpeechBlink(nowMs: number): void { + if (reducedMotion) return; + if (blinkState === null) blinkState = { nextAt: nowMs, startedAt: null }; + /* 지금 깜빡임을 재생한다. 자연 완료 시 applyBlink가 다음 idle 타이머를 다시 뽑으므로 + (§5.5 "idle 깜빡임 타이머를 다시 잡는다") 여기서 nextAt을 따로 계산하지 않는다. */ + blinkState.startedAt = nowMs; + } + + function playPerformance(perf: Performance, timeline: PerformanceTimeline, nowMs: number): void { + scheduledCues = []; + setSurface(perf.surface.expression, perf.surface.intensity); + leak = perf.leak + ? { + expression: perf.leak.expression, + intensity: perf.leak.intensity, + startAbsMs: timeline.speechStartMs + perf.leak.atMs, + durationMs: perf.leak.durationMs, + } + : null; + + for (const cue of perf.cues) { + let atAbs: number; + switch (cue.anchor) { + case "pre": + atAbs = timeline.speechStartMs + cue.at; + break; + case "post": + atAbs = timeline.speechStartMs + timeline.speechDurationMs + cue.at; + break; + case "inline": + atAbs = timeline.speechStartMs + timeline.speechDurationMs * cue.at; + break; + case "listen": + default: + atAbs = nowMs + cue.at; + break; + } + scheduledCues.push({ clip: cue.clip, atMs: atAbs, weight: cue.weight ?? 1, source: cue.source }); + } + } + + function computeExpressionTarget(nowMs: number): ChannelVector { + const surfaceDelta = filteredPresetDelta(surfaceExpr, surfaceIntensity, demeanor); + const w = leakWeight(nowMs); + const delta = w > 0 && leak ? blendDelta(surfaceDelta, filteredPresetDelta(leak.expression, leak.intensity, demeanor), w) : surfaceDelta; + + const out: ChannelVector = { ...baseVector }; + for (const id of Object.keys(delta) as ChannelId[]) { + out[id] += delta[id] as number; + } + return out; + } + + function updateSmoothing(nowMs: number): void { + const target = computeExpressionTarget(nowMs); + if (smoothed === null || reducedMotion) { + smoothed = target; + return; + } + const dt = Math.max(0, nowMs - (lastEvalMs ?? nowMs)); + const next = {} as ChannelVector; + for (const id of CHANNEL_IDS) { + const tau = CHANNEL_SPECS[id].tauMs; + if (tau <= 0) { + next[id] = target[id]; + continue; + } + const alpha = 1 - Math.exp(-dt / tau); + next[id] = smoothed[id] + (target[id] - smoothed[id]) * alpha; + } + smoothed = next; + } + + function processScheduledCues(nowMs: number): void { + const remaining: ScheduledCue[] = []; + for (const sc of scheduledCues) { + if (nowMs >= sc.atMs) { + triggerClip(sc.clip, nowMs, { weight: sc.weight }); + } else { + remaining.push(sc); + } + } + scheduledCues = remaining; + } + + function reactionLayerDelta(nowMs: number): ChannelDelta { + const out: ChannelDelta = {}; + for (const entry of activeClips) { + const clip = REACTION_CLIPS[entry.clipId]; + const t = nowMs - entry.startedMs; + if (t < 0 || t > clip.durationMs) continue; + let mult = 1; + if (entry.evictAt !== undefined) { + const et = nowMs - entry.evictAt; + mult = et >= EVICT_FADE_MS ? 0 : 1 - et / EVICT_FADE_MS; + } + const delta = resolveClipDelta(clip, t, { weight: entry.weight * mult, mirror: entry.mirror }); + for (const id of Object.keys(delta) as ChannelId[]) { + out[id] = (out[id] ?? 0) + (delta[id] as number); + } + } + + activeClips = activeClips.filter((entry) => { + const clip = REACTION_CLIPS[entry.clipId]; + const t = nowMs - entry.startedMs; + if (entry.evictAt !== undefined && nowMs - entry.evictAt >= EVICT_FADE_MS) return false; + if (t > clip.durationMs) return false; + return true; + }); + + return out; + } + + function processIdleClips(nowMs: number): void { + if (currentState !== "idle" && currentState !== "listening") return; + if (idleClipStates === null) { + idleClipStates = demeanor.idleClips.map((rule) => ({ + nextAtMs: nowMs + rng.range(rule.everyMs[0], rule.everyMs[1]), + })); + } + demeanor.idleClips.forEach((rule, i) => { + const st = idleClipStates![i]; + if (nowMs < st.nextAtMs) return; + const stateOk = rule.when === undefined || rule.when === currentState; + const opennessOk = rule.maxOpenness === undefined || openness < rule.maxOpenness; + if (stateOk && opennessOk) { + triggerClip(rule.clip, nowMs, { weight: 1 }); + } + idleClipStates![i] = { nextAtMs: nowMs + rng.range(rule.everyMs[0], rule.everyMs[1]) }; + }); + } + + function applyBlink(out: ChannelVector, nowMs: number, suppressed: boolean): void { + if (suppressed) return; + if (blinkState === null) { + blinkState = { nextAt: nowMs + rng.range(demeanor.blinkIntervalMs[0], demeanor.blinkIntervalMs[1]), startedAt: null }; + } + if (blinkState.startedAt === null && nowMs >= blinkState.nextAt) { + blinkState.startedAt = nowMs; + } + let mult = 1; + if (blinkState.startedAt !== null) { + const t = nowMs - blinkState.startedAt; + if (t >= demeanor.blinkDurationMs) { + blinkState.startedAt = null; + blinkState.nextAt = nowMs + rng.range(demeanor.blinkIntervalMs[0], demeanor.blinkIntervalMs[1]); + } else { + const phase = 1 - Math.abs(t / demeanor.blinkDurationMs - 0.5) * 2; + mult = 1 - phase * 0.95; + if (mult < 0.05) mult = 0.05; + } + } + out.eyeOpenL *= mult; + out.eyeOpenR *= mult; + } + + function applySaccade(out: ChannelVector, nowMs: number, suppressed: boolean): void { + if (suppressed) return; + if (saccadeState === null) { + saccadeState = { nextAt: nowMs, targetX: 0, targetY: 0, curX: 0, curY: 0 }; + } + const intervalMul = currentState === "thinking" ? 0.5 : 1; + if (nowMs >= saccadeState.nextAt) { + const amp = demeanor.saccadeAmplitude * (currentState === "listening" ? 0.5 : 1); + saccadeState.targetX = rng.range(-amp, amp); + saccadeState.targetY = rng.range(-amp * 0.6, amp * 0.6); + saccadeState.nextAt = + nowMs + rng.range(demeanor.saccadeIntervalMs[0] * intervalMul, demeanor.saccadeIntervalMs[1] * intervalMul); + } + const dt = Math.max(0, nowMs - (lastEvalMs ?? nowMs)); + const alpha = 1 - Math.exp(-dt / 90); + saccadeState.curX += (saccadeState.targetX - saccadeState.curX) * alpha; + saccadeState.curY += (saccadeState.targetY - saccadeState.curY) * alpha; + out.gazeX += saccadeState.curX; + out.gazeY += saccadeState.curY; + } + + function applyBreath(out: ChannelVector, nowMs: number): void { + const period = + currentState === "listening" ? 4800 : currentState === "speaking" ? 3600 : currentState === "thinking" ? 5200 : 4200; + const breath = 0.5 + 0.5 * Math.sin((2 * Math.PI * nowMs) / period); + out.breath = breath; + out.shoulderRaise += (breath - 0.5) * 0.06; + } + + function applySway(out: ChannelVector, nowMs: number, suppressed: boolean): void { + if (suppressed) return; + const roll = demeanor.swayAmplitude * 0.04 * (Math.sin(nowMs / 2300) + 0.6 * Math.sin(nowMs / 3700)); + out.bodyRoll += roll; + out.headRoll += roll / 2; + } + + function applyIdleLayer(out: ChannelVector, nowMs: number): void { + processIdleClips(nowMs); + const suppress = activeSuppressions(nowMs); + applyBlink(out, nowMs, suppress.has("blink")); + applySaccade(out, nowMs, suppress.has("saccade")); + applyBreath(out, nowMs); + applySway(out, nowMs, suppress.has("sway")); + } + + function evaluate(nowMs: number): ChannelVector { + processScheduledCues(nowMs); + updateSmoothing(nowMs); + + const out: ChannelVector = { ...(smoothed as ChannelVector) }; + const reaction = reducedMotion ? {} : reactionLayerDelta(nowMs); + for (const id of Object.keys(reaction) as ChannelId[]) { + out[id] += reaction[id] as number; + } + + if (!reducedMotion) applyIdleLayer(out, nowMs); + + /* 발화층(결정문 §5.4 "엔진 합성"). 스프링은 speaking이 아닐 때도 X(닫힘)로 + 계속 수렴시켜 상태 전환 시 값이 멈춰 남지 않게 한다. reduced motion이어도 + 발화 입은 움직인다(기존 동작 유지 — idle·반응층만 끄는 대상이다). */ + const suppressLipsync = activeSuppressions(nowMs).has("lipsync"); + const speechTarget = currentState === "speaking" && pendingSpeechShape && !suppressLipsync ? pendingSpeechShape : SILENT_SHAPE; + const speechDt = Math.max(0, nowMs - (lastEvalMs ?? nowMs)); + speechSpring = { + open: springTowards(speechSpring.open, speechTarget.open, speechDt, SPEECH_SPRING_TAU_MS), + wide: springTowards(speechSpring.wide, speechTarget.wide, speechDt, SPEECH_SPRING_TAU_MS), + round: springTowards(speechSpring.round, speechTarget.round, speechDt, SPEECH_SPRING_TAU_MS), + press: springTowards(speechSpring.press, speechTarget.press, speechDt, SPEECH_SPRING_TAU_MS), + }; + if (currentState === "speaking") { + const s = speechSpring; + out.mouthOpen = s.open.value + 0.25 * out.mouthOpen; + out.mouthStretch += s.wide.value; + out.mouthRound = Math.max(out.mouthRound, s.round.value); + out.mouthPress = Math.max(0.5 * out.mouthPress, s.press.value); + out.mouthForm *= 1 - 0.5 * s.round.value; + } + + /* 발화 동반층(결정문 §5.5). reduced motion이면 아예 끈다(입·턱만 남는다). speaking이고 + pending delta가 있으면 계획 곡선을 그대로 통과시킨다 — coSpeech.ts의 강세 박(rise 120· + fall 260)은 이미 매끄러운 올림 코사인이라, 여기서 또 τ150ms 스프링을 걸면 정점이 깎이고 + 100ms 넘게 늦어져 "모음 목표 − 80ms 정점"이 무너진다(팀 리드 수정 요청, 2026-10-01). + 스프링 상태는 통과 중에도 값만 동기화해 두어(속도 0), speaking이 꺼지거나 delta가 null이 + 되는 순간부터는 그 값에서 이어서 τ150ms로 0으로 되돌아간다(발화 종료 시 튀지 않게). */ + if (!reducedMotion) { + const coSpeaking = currentState === "speaking"; + const coDt = Math.max(0, nowMs - (lastEvalMs ?? nowMs)); + for (const id of CO_SPEECH_CHANNELS) { + if (coSpeaking && pendingSpeechMotion) { + const v = pendingSpeechMotion[id] ?? 0; + coSpeechSpring[id] = { value: v, velocity: 0 }; + out[id] += v; + } else { + const next = springTowards(coSpeechSpring[id] as SpringState, 0, coDt, CO_SPEECH_SPRING_TAU_MS); + coSpeechSpring[id] = next; + out[id] += next.value; + } + } + } + + for (const id of CHANNEL_IDS) out[id] = clampChannel(id, out[id]); + + lastEvalMs = nowMs; + return out; + } + + function debugSnapshot(): DebugSnapshot { + const leakActive = leak !== null && lastEvalMs !== null && lastEvalMs >= leak.startAbsMs && lastEvalMs <= leak.startAbsMs + leak.durationMs; + return { + activeClips: activeClips.map((e) => ({ id: e.clipId, startedMs: e.startedMs, weight: e.weight, mirror: e.mirror })), + scheduled: scheduledCues.map((sc) => ({ clip: sc.clip, atMs: sc.atMs, source: sc.source })), + surface: { expression: surfaceExpr, intensity: surfaceIntensity }, + leakActive, + }; + } + + return { + setState, + setOpenness, + setSurface, + setSpeechShape, + setSpeechMotion, + requestSpeechBlink, + triggerClip, + playPerformance, + evaluate, + debugSnapshot, + }; +} diff --git a/apps/web/src/components/avatar/engine/expressionPresets.ts b/apps/web/src/components/avatar/engine/expressionPresets.ts new file mode 100644 index 0000000..f79583a --- /dev/null +++ b/apps/web/src/components/avatar/engine/expressionPresets.ts @@ -0,0 +1,68 @@ +/* ===================================================================== + 아바타 v3 표정 프리셋 — docs/decisions/avatar-expression-engine-v3.md §5 정본 값. + 각 값은 강도 1.0에서 페르소나 기본 자세 대비 delta다. FACS AU를 참고해 + 눈썹(AU1/2/4)·눈꺼풀(AU5/6/7/43)·입(AU12/14/15/17/20/23/24/25/26)과 + 머리·몸 자세를 함께 정한다. 오버레이(tear·cheek·pallor·sweat)는 강도 게이트를 따른다. + 값 변경은 오케스트레이터 설계 결정이다. 구분 가능성 불변량은 + scripts/check-avatar-presets.mjs 가 검증한다. + ===================================================================== */ + +import type { AvatarExpression } from "../persona"; +import { expandAuthoring, type AuthoringDelta, type ChannelDelta } from "./channels"; + +const PRESET_SPECS: Record = { + neutral: {}, + + /* ── positive ─────────────────────────────────────────────────── */ + calm: { eyeSmile: 0.25, eyeOpen: -0.1, mouthForm: 0.2, shoulderRaise: -0.25, bodyLean: -0.05, browForm: 0.1 }, + warm: { eyeSmile: 0.45, mouthForm: 0.45, browY: 0.1, headRoll: 0.12, bodyLean: 0.15 }, + joy: { eyeSmile: 0.75, mouthForm: 0.85, mouthOpen: 0.35, browY: 0.2, cheek: 0.4, headPitch: 0.08, bodyLean: 0.1 }, + delight: { eyeOpen: 0.2, eyeSmile: 0.5, mouthForm: 0.75, mouthOpen: 0.5, browY: 0.45, bodyLean: 0.2, headPitch: 0.1, cheek: 0.3 }, + relief: { eyeOpen: -0.4, eyeSmile: 0.3, mouthForm: 0.35, browY: 0.15, browAngle: 0.2, shoulderRaise: -0.45, headPitch: -0.05 }, + hopeful: { eyeOpen: 0.08, browY: 0.3, browAngle: 0.25, mouthForm: 0.25, gazeY: 0.25, headPitch: 0.12, bodyLean: 0.1 }, + determined: { browY: -0.15, browX: -0.2, lidTension: 0.3, eyeOpen: 0.05, mouthPress: 0.35, mouthForm: 0.1, headPitch: -0.05, bodyLean: 0.2, shoulderRaise: -0.1 }, + + /* ── negative ─────────────────────────────────────────────────── */ + sad: { eyeOpen: -0.25, browAngle: 0.6, browY: 0.1, mouthForm: -0.45, gazeY: -0.35, headPitch: -0.2, shoulderRaise: -0.2, chinRaise: 0.2, tear: 0.5 }, + /* 비탄 눈썹은 AU1+AU4: 안쪽만 오르고 눈썹 전체는 눌려 모이며 바깥이 처진다. browY +0.2였을 때 + 소유자가 "눈썹이 너무 위, 두 배는 더 내려와야"라고 봐서 안쪽 올림을 절반(약 57 → 27px)으로 줄였다. */ + grief: { eyeOpen: -0.45, browAngle: 0.8, browY: -0.4, browX: -0.25, browForm: 0.3, mouthForm: -0.7, mouthOpen: 0.2, chinRaise: 0.55, gazeY: -0.5, headPitch: -0.35, shoulderRaise: 0.2, tear: 0.9 }, + lonely: { eyeOpen: -0.2, browAngle: 0.4, mouthForm: -0.2, gazeX: 0.45, gazeY: -0.2, headYaw: 0.18, headPitch: -0.12, shoulderRaise: -0.3 }, + /* 불안은 렌더 구분도가 가장 낮았다(2단계-B-1d out4 face96 G의 0.44). 눈썹을 더 올려 모으고 + 눈을 더 뜨고 입술을 누르며 턱을 긴장시키고 고개를 움츠린다(AU1+2+4·5·24·17). */ + anxious: { eyeOpen: 0.22, lidTension: 0.3, browAngle: 0.6, browX: -0.45, browY: 0.15, mouthPress: 0.45, mouthStretch: 0.25, chinRaise: 0.2, gazeX: -0.2, pupilSize: 0.25, headPitch: -0.06, shoulderRaise: 0.35, bodyLean: -0.1 }, + panic: { eyeOpen: 0.3, browAngle: 0.55, browY: 0.45, browX: -0.2, mouthStretch: 0.6, mouthOpen: 0.3, pupilSize: 0.5, shoulderRaise: 0.6, bodyLean: -0.25, pallor: 0.6, sweat: 0.5 }, + startled: { eyeOpen: 0.3, browY: 0.6, browAngle: 0.1, mouthOpen: 0.45, pupilSize: 0.3, headPitch: 0.12, bodyLean: -0.2, shoulderRaise: 0.4 }, + embarrassed: { eyeSmile: 0.2, mouthForm: 0.25, mouthAsym: 0.3, gazeX: 0.5, gazeY: -0.35, headPitch: -0.18, headRoll: -0.1, cheek: 0.7 }, + ashamed: { eyeOpen: -0.35, browAngle: 0.35, gazeY: -0.75, headPitch: -0.45, mouthPress: 0.35, mouthForm: -0.2, shoulderRaise: 0.3, bodyLean: -0.1, cheek: 0.45 }, + guilty: { eyeOpen: -0.25, browAngle: 0.5, browX: -0.15, gazeY: -0.55, gazeX: -0.25, headPitch: -0.25, mouthPress: 0.45, mouthForm: -0.25, chinRaise: 0.25 }, + overwhelmed: { eyeOpen: -0.3, lidTension: 0.25, browAngle: 0.7, browX: -0.3, mouthForm: -0.4, mouthOpen: 0.12, chinRaise: 0.35, headPitch: -0.3, shoulderRaise: 0.45, gazeY: -0.3, tear: 0.4 }, + + /* ── defensive ────────────────────────────────────────────────── */ + resistant: { eyeOpen: -0.12, lidTension: 0.3, browY: -0.2, browX: -0.25, mouthPress: 0.5, mouthAsym: 0.2, headYaw: -0.18, bodyLean: -0.3, headPitch: 0.08, gazeX: 0.3 }, + guarded: { eyeOpen: -0.08, lidTension: 0.4, browY: -0.1, mouthPress: 0.4, bodyLean: -0.2, shoulderRaise: 0.25, gazeX: 0.2, headPitch: -0.05 }, + skeptical: { browLY: 0.45, browRY: -0.25, lidTension: 0.35, mouthAsym: 0.45, mouthPress: 0.2, headRoll: -0.15, headPitch: 0.1, bodyLean: -0.1 }, + irritated: { browY: -0.35, browX: -0.45, browAngle: -0.3, lidTension: 0.4, mouthPress: 0.45, mouthForm: -0.15, gazeX: 0.25, headPitch: 0.05 }, + angry: { browY: -0.6, browX: -0.65, browAngle: -0.6, lidTension: 0.65, eyeOpen: 0.1, mouthPress: 0.6, mouthForm: -0.3, headPitch: -0.12, bodyLean: 0.15, cheek: 0.2 }, + rage: { browY: -0.8, browX: -0.8, browAngle: -0.8, lidTension: 0.5, eyeOpen: 0.25, mouthOpen: 0.45, mouthStretch: 0.35, mouthForm: -0.45, headPitch: -0.1, bodyLean: 0.3, shoulderRaise: 0.3, cheek: 0.55 }, + + /* ── cognitive ────────────────────────────────────────────────── */ + confused: { browLY: 0.4, browRY: -0.2, browX: -0.2, eyeOpen: -0.05, headRoll: 0.2, mouthAsym: 0.25, mouthPress: 0.15, gazeY: 0.15 }, + conflicted: { browX: -0.35, browAngle: 0.3, mouthPress: 0.4, mouthAsym: -0.2, gazeX: 0.3, gazeY: -0.2, headPitch: -0.08, lidTension: 0.2 }, + + /* ── energy ───────────────────────────────────────────────────── */ + tired: { eyeOpen: -0.45, browY: -0.1, browAngle: 0.1, mouthForm: -0.1, mouthOpen: 0.05, headPitch: -0.2, headRoll: 0.1, shoulderRaise: -0.45, gazeY: -0.2 }, + bored: { eyeOpen: -0.35, gazeX: 0.55, gazeY: 0.25, mouthForm: -0.15, mouthAsym: 0.15, headRoll: 0.18, bodyLean: -0.15, browY: -0.05 }, +}; + +export const EXPRESSION_PRESETS: Record = Object.fromEntries( + Object.entries(PRESET_SPECS).map(([id, spec]) => [id, expandAuthoring(spec)]), +) as Record; + +/** 강도 → 적용 비율(결정문 §5.2). 0~0.35 절제, 0.35~0.7 중간, 0.7~1.0 뚜렷. */ +export function intensityGain(intensity: number): number { + const x = intensity < 0 ? 0 : intensity > 1 ? 1 : intensity; + if (x <= 0.35) return (x / 0.35) * 0.3; + if (x <= 0.7) return 0.3 + ((x - 0.35) / 0.35) * 0.45; + return 0.75 + ((x - 0.7) / 0.3) * 0.25; +} diff --git a/apps/web/src/components/avatar/engine/lipsync.ts b/apps/web/src/components/avatar/engine/lipsync.ts new file mode 100644 index 0000000..4c74ca1 --- /dev/null +++ b/apps/web/src/components/avatar/engine/lipsync.ts @@ -0,0 +1,681 @@ +/* ===================================================================== + 아바타 v3 발화층 — 한글 비짐 립싱크. 결정문 §5.4 발화층 정본. + 렌더러·엔진과 무관한 순수 모듈이다. 한글 분해는 유니코드 산술로 하고 + 음운 규칙(g2p)은 쓰지 않는다(구분은 양순음 여부뿐, §5.4). + 흐름: buildSpeechTimeline(텍스트 [+포락선]) → SpeechTimeline(키 목록) + → 재생 중 매 프레임 sampleSpeech(timeline, t)로 표본. + 임계 감쇠 스프링(τ 45ms)은 여기 두지 않는다 — 엔진의 발화층이 프레임 dt를 + 가지고 있으므로 engine.ts가 sampleSpeech 결과를 스프링으로 평활한다. + ===================================================================== */ + +import { detectVoicedSegments, sampleEnvelope, type SpeechEnvelope, type VoicedSegment } from "./speechEnvelope"; + +export type VisemeId = "X" | "M" | "A" | "EO" | "O" | "U" | "EU" | "I" | "E"; + +export interface SpeechShape { + open: number; + wide: number; + round: number; + press: number; +} + +export interface SpeechKey { + /** 발화 시작 기준 절대 ms. */ + atMs: number; + shape: SpeechShape; + /** 디버그·검증용 비짐 이름. 렌더러는 shape만 쓴다. */ + viseme: VisemeId; +} + +export interface SpeechTimeline { + keys: SpeechKey[]; + totalDurationMs: number; + /** 오디오 선분석 경로에서만 채워진다(표본 시 진폭 조절·무음 판정에 쓴다). */ + envelope?: SpeechEnvelope; + voicedSegments?: VoicedSegment[]; + /** 구 단위로 정렬한 창(진단·검증용, §5.4 "구 경계"). */ + phraseWindows?: VoicedSegment[]; + /** 발화 동반층(§5.5) 입력 — 구 목록(시작·끝·종류·음절별 모음 목표·강세 음절). */ + phrases: SpeechPhraseInfo[]; + /** 발화 동반층(§5.5) 입력 — 구 사이 휴지 구간. */ + pauses: SpeechPauseWindow[]; +} + +/** 구 종류(§5.5): 마침표·구두점 없음 = 평서, `?` = 질문, `!` = 감탄, `,` = 이어짐. */ +export type PhraseKind = "statement" | "question" | "exclaim" | "continuation"; + +export interface SpeechPhraseInfo { + index: number; + /** 구 시작·끝(휴지 제외) 절대 ms. */ + startMs: number; + endMs: number; + kind: PhraseKind; + /** 이 구 안 음절(비한글 단위 포함)의 모음 목표 시각(§5.4 "모음 목표"), 순서대로. */ + syllableTargetsMs: number[]; + /** 강세 음절 인덱스(syllableTargetsMs 기준). 오름차순이며 첫 원소는 항상 0(구의 첫 음절, §5.5). */ + stressedIndices: number[]; +} + +export interface SpeechPauseWindow { + startMs: number; + endMs: number; +} + +/* ── 비짐 9종 목표값(결정문 §5.4 표) ──────────────────────────────── */ +export const VISEME_SHAPE: Readonly> = { + X: { open: 0, wide: 0, round: 0, press: 0 }, + M: { open: 0, wide: 0, round: 0, press: 0.35 }, + A: { open: 0.75, wide: 0.1, round: 0, press: 0 }, + EO: { open: 0.55, wide: 0, round: 0.1, press: 0 }, + O: { open: 0.4, wide: 0, round: 0.75, press: 0 }, + U: { open: 0.2, wide: 0, round: 1.0, press: 0 }, + EU: { open: 0.15, wide: 0.4, round: 0, press: 0 }, + I: { open: 0.2, wide: 0.65, round: 0, press: 0 }, + E: { open: 0.45, wide: 0.45, round: 0, press: 0 }, +}; + +/** X(쉼) 목표 — 무음 판정·타임라인 끝맺음에 쓴다. */ +export const SILENT_SHAPE: SpeechShape = VISEME_SHAPE.X; + +function cloneShape(s: SpeechShape): SpeechShape { + return { ...s }; +} + +function lerpShape(a: SpeechShape, b: SpeechShape, t: number): SpeechShape { + return { + open: a.open + (b.open - a.open) * t, + wide: a.wide + (b.wide - a.wide) * t, + round: a.round + (b.round - a.round) * t, + press: a.press + (b.press - a.press) * t, + }; +} + +function clamp(v: number, lo: number, hi: number): number { + return v < lo ? lo : v > hi ? hi : v; +} + +function smoothstep(u: number): number { + const c = clamp(u, 0, 1); + return c * c * (3 - 2 * c); +} + +/* ── 한글 분해(유니코드 산술) — 결정문 §5.4 "한글 분해는 유니코드 산술로 한다" ──── */ +const HANGUL_BASE = 0xac00; +const HANGUL_LAST = 0xd7a3; +const JUNGSEONG_COUNT = 21; +const JONGSEONG_COUNT = 28; + +function isHangulSyllable(code: number): boolean { + return code >= HANGUL_BASE && code <= HANGUL_LAST; +} + +function decompose(code: number): { cho: number; jung: number; jong: number } { + const sIndex = code - HANGUL_BASE; + const jong = sIndex % JONGSEONG_COUNT; + const jung = Math.floor(sIndex / JONGSEONG_COUNT) % JUNGSEONG_COUNT; + const cho = Math.floor(sIndex / JONGSEONG_COUNT / JUNGSEONG_COUNT); + return { cho, jung, jong }; +} + +/* 초성 19종(유니코드 순서: ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ). */ +const BILABIAL_CHOSEONG = new Set([6, 7, 8, 17]); // ㅁㅂㅃㅍ +const SIBILANT_CHOSEONG = new Set([9, 10, 12, 13, 14]); // ㅅㅆㅈㅉㅊ +const SILENT_CHOSEONG = 11; // ㅇ + +/* 종성 28종(0=받침 없음). 끝소리가 양순음(ㅁㅂㅍ)인 것 — 겹받침 ㄻ(ㅁ)·ㄿ(ㅍ)·ㅄ(ㅂ) 포함. */ +const BILABIAL_JONGSEONG = new Set([16, 17, 26, 10, 14, 18]); + +/* 중성 21종: 단모음은 비짐 하나, 이중모음은 [활음, 뒤 모음] 쌍(결정문 §5.4). */ +type JungseongEntry = VisemeId | readonly [VisemeId, VisemeId]; +const JUNGSEONG_TABLE: readonly JungseongEntry[] = [ + "A", // 0 ㅏ + "E", // 1 ㅐ + ["I", "A"], // 2 ㅑ + ["I", "E"], // 3 ㅒ + "EO", // 4 ㅓ + "E", // 5 ㅔ + ["I", "EO"], // 6 ㅕ + ["I", "E"], // 7 ㅖ + "O", // 8 ㅗ + ["U", "A"], // 9 ㅘ + ["U", "E"], // 10 ㅙ + ["U", "E"], // 11 ㅚ(특칙: U→E) + ["I", "O"], // 12 ㅛ + "U", // 13 ㅜ + ["U", "EO"], // 14 ㅝ + ["U", "E"], // 15 ㅞ + ["U", "I"], // 16 ㅟ(특칙: U→I) + ["I", "U"], // 17 ㅠ + "EU", // 18 ㅡ + ["EU", "I"], // 19 ㅢ(특칙: EU→I) + "I", // 20 ㅣ +]; + +function jungseongVisemes(jung: number): { glide: VisemeId | null; target: VisemeId } { + const entry = JUNGSEONG_TABLE[jung]; + return typeof entry === "string" ? { glide: null, target: entry } : { glide: entry[0], target: entry[1] }; +} + +/* ── 음절 내부 키(비짐 프랙션) 생성 ────────────────────────────────── */ +interface LocalKey { + frac: number; + shape: SpeechShape; + viseme: VisemeId; + isTarget?: boolean; +} + +/* 양순음 초성 + 활음이 겹칠 때 활음 키를 두는 지점(폐쇄 해제 직후). 문서에 정확한 수치가 없어 + 오케스트레이터 확인 전까지 쓰는 값 — "봐요" 표본(M→U→A)을 만족하는 최소치다. */ +const BILABIAL_GLIDE_FRAC = 0.15; + +function buildSyllableKeys(cho: number, jung: number, jong: number): LocalKey[] { + const { glide, target } = jungseongVisemes(jung); + const hasGlide = glide !== null; + const targetShape = cloneShape(VISEME_SHAPE[target]); + const targetFrac = hasGlide ? 0.55 : 0.35; + const keys: LocalKey[] = []; + + if (BILABIAL_CHOSEONG.has(cho)) { + keys.push({ frac: 0, shape: cloneShape(VISEME_SHAPE.M), viseme: "M" }); + if (hasGlide) { + const g = glide as VisemeId; + keys.push({ frac: BILABIAL_GLIDE_FRAC, shape: cloneShape(VISEME_SHAPE[g]), viseme: g }); + } + } else if (cho === SILENT_CHOSEONG) { + if (hasGlide) { + const g = glide as VisemeId; + keys.push({ frac: 0, shape: cloneShape(VISEME_SHAPE[g]), viseme: g }); + } + /* 무성 초성 + 활음 없음: 시작 키 없이 선행 음절에서 바로 이어진다(동시조음). */ + } else { + const baseId = hasGlide ? (glide as VisemeId) : target; + const baseShape = VISEME_SHAPE[baseId]; + let shape: SpeechShape = { ...baseShape, open: baseShape.open * 0.5 }; + if (SIBILANT_CHOSEONG.has(cho)) { + shape = { ...shape, open: Math.min(shape.open, 0.15), wide: shape.wide + 0.2 }; + } + keys.push({ frac: 0, shape, viseme: baseId }); + } + + keys.push({ frac: targetFrac, shape: targetShape, viseme: target, isTarget: true }); + + if (jong !== 0) { + if (BILABIAL_JONGSEONG.has(jong)) { + keys.push({ frac: 0.85, shape: cloneShape(VISEME_SHAPE.M), viseme: "M" }); + } else { + keys.push({ frac: 0.85, shape: { ...targetShape, open: targetShape.open * 0.6 }, viseme: target }); + } + } + + return keys; +} + +function otherUnitKeys(): LocalKey[] { + /* 한글이 아닌 글자(숫자·영문)는 한 음절 EO로 친다(결정문 §5.4). */ + return [{ frac: 0.35, shape: cloneShape(VISEME_SHAPE.EO), viseme: "EO", isTarget: true }]; +} + +/* ── 텍스트 → 구 단위 토큰화(휴지 구분, 결정문 §5.4 타이밍) ────────────── */ +type Unit = + | { kind: "syllable"; cho: number; jung: number; jong: number; weight: number } + | { kind: "other"; weight: number }; + +interface Phrase { + units: Unit[]; + /** 이 구 다음에 오는 휴지(ms). 마지막 구가 구두점으로 끝나지 않으면 0. */ + restMs: number; + kind: PhraseKind; +} + +/* voice.py _STAGE_DIRECTION_RE / performance.ts BRACKET_RE 와 같은 괄호 문자 집합. */ +const BRACKET_RE = /[(([【][^))\]】]*[))\]】]/g; + +const PAUSE_COMMA_MS = 250; +const PAUSE_SENTENCE_MS = 450; +const PAUSE_ELLIPSIS_MS = 600; + +function tokenizeToPhrases( + text: string, +): Array<{ units: Array<{ kind: "syllable" | "other"; cho?: number; jung?: number; jong?: number }>; restMs: number; kind: PhraseKind }> { + const stripped = text.replace(BRACKET_RE, " "); + const normalized = stripped.replace(/\.{2,}/g, "…"); + + const phrases: Array<{ + units: Array<{ kind: "syllable" | "other"; cho?: number; jung?: number; jong?: number }>; + restMs: number; + kind: PhraseKind; + }> = []; + let current: Array<{ kind: "syllable" | "other"; cho?: number; jung?: number; jong?: number }> = []; + + const flush = (restMs: number, kind: PhraseKind) => { + phrases.push({ units: current, restMs, kind }); + current = []; + }; + + for (const ch of normalized) { + if (/\s/.test(ch)) continue; + const code = ch.codePointAt(0) ?? 0; + if (isHangulSyllable(code)) { + const { cho, jung, jong } = decompose(code); + current.push({ kind: "syllable", cho, jung, jong }); + continue; + } + if (ch === "…") { + /* 말줄임표는 문장을 맺지 않는 망설임이라 이어짐(§5.5 구 끝 규칙에서 제외)으로 다룬다. + §5.5 표는 이 경우를 명시하지 않아 오케스트레이터 확인 전까지 쓰는 값이다. */ + flush(PAUSE_ELLIPSIS_MS, "continuation"); + continue; + } + if (ch === "," || ch === ",") { + flush(PAUSE_COMMA_MS, "continuation"); + continue; + } + if (ch === ".") { + flush(PAUSE_SENTENCE_MS, "statement"); + continue; + } + if (ch === "?") { + flush(PAUSE_SENTENCE_MS, "question"); + continue; + } + if (ch === "!") { + flush(PAUSE_SENTENCE_MS, "exclaim"); + continue; + } + if (/[0-9A-Za-z]/.test(ch)) { + current.push({ kind: "other" }); + continue; + } + /* 그 외 기호(물결표 등)는 발화 타이밍에 영향을 주지 않는다. */ + } + if (current.length > 0) phrases.push({ units: current, restMs: 0, kind: "statement" }); + return phrases; +} + +function syllableWeight(jong: number, isPhraseFinal: boolean): number { + let w = 1.0; + if (jong !== 0) w += 0.25; + if (isPhraseFinal) w += 0.5; + return w; +} + +function otherWeight(isPhraseFinal: boolean): number { + return isPhraseFinal ? 1.5 : 1.0; +} + +function withWeights( + raw: Array<{ + units: Array<{ kind: "syllable" | "other"; cho?: number; jung?: number; jong?: number }>; + restMs: number; + kind: PhraseKind; + }>, +): Phrase[] { + return raw.map(({ units, restMs, kind }) => { + const withW: Unit[] = units.map((u, i) => { + const isLast = i === units.length - 1; + const phraseFinal = isLast && restMs > 0; + if (u.kind === "syllable") { + return { kind: "syllable", cho: u.cho as number, jung: u.jung as number, jong: u.jong as number, weight: syllableWeight(u.jong as number, phraseFinal) }; + } + return { kind: "other", weight: otherWeight(phraseFinal) }; + }); + return { units: withW, restMs, kind }; + }); +} + +/** + * 강세 음절 선택(§5.5): 구의 첫 음절 + 구 안에서 값이 가장 큰 음절(오디오는 포락선 피크, + * 텍스트는 가중치). 구가 6음절 이상이면 이웃하지 않은 상위 2개를 추가로 고른다. + * 반환은 오름차순 인덱스이며 첫 원소는 항상 0이다. + */ +function selectStressedIndices(weights: number[]): number[] { + const n = weights.length; + if (n === 0) return []; + const selected = [0]; + if (n === 1) return selected; + const extraCount = n >= 6 ? 2 : 1; + const ranked = weights + .map((w, i) => ({ i, w })) + .filter((e) => e.i !== 0) + .sort((a, b) => b.w - a.w || a.i - b.i); + for (const { i } of ranked) { + if (selected.length - 1 >= extraCount) break; + if (selected.every((s) => Math.abs(s - i) >= 2)) selected.push(i); + } + selected.sort((a, b) => a - b); + return selected; +} + +function localKeysFor(unit: Unit): LocalKey[] { + return unit.kind === "syllable" ? buildSyllableKeys(unit.cho, unit.jung, unit.jong) : otherUnitKeys(); +} + +/* ── 레이아웃 → 최종 키 목록 ──────────────────────────────────────── */ +interface LaidUnit { + startMs: number; + durMs: number; + localKeys: LocalKey[]; +} + +const SHORT_SYLLABLE_MS = 90; +const SHORT_SYLLABLE_PULL = 0.4; +const MIN_HOLD_MS = 60; + +/** 짧은 음절(<90ms) 목표를 앞뒤 음절 평균 쪽으로 40% 끌어당긴다(결정문 §5.4). */ +function applyShortSyllableAveraging(laidOut: LaidUnit[]): void { + const targets = laidOut.map((lu) => lu.localKeys.find((k) => k.isTarget) ?? null); + const originals = targets.map((k) => (k ? cloneShape(k.shape) : null)); + for (let i = 0; i < laidOut.length; i++) { + if (laidOut[i].durMs >= SHORT_SYLLABLE_MS) continue; + const key = targets[i]; + if (!key) continue; + const prev = i > 0 ? originals[i - 1] : null; + const next = i < laidOut.length - 1 ? originals[i + 1] : null; + if (!prev && !next) continue; + const avg = prev && next ? lerpShape(prev, next, 0.5) : ((prev ?? next) as SpeechShape); + key.shape = lerpShape(originals[i] as SpeechShape, avg, SHORT_SYLLABLE_PULL); + } +} + +/** 인접 키 간격이 60ms 미만이면 뒤 키를 밀어 최소 유지를 보장한다(결정문 §5.4). */ +function enforceMinHold(keys: SpeechKey[]): void { + for (let i = 1; i < keys.length; i++) { + if (keys[i].atMs - keys[i - 1].atMs < MIN_HOLD_MS) { + keys[i].atMs = keys[i - 1].atMs + MIN_HOLD_MS; + } + } +} + +function finalize( + laidOut: LaidUnit[], + pauseAtMs: number[], + totalMs: number, + envelope: SpeechEnvelope | undefined, + voicedSegments: VoicedSegment[] | undefined, + phraseWindows: VoicedSegment[] | undefined, + phrases: SpeechPhraseInfo[], + pauses: SpeechPauseWindow[], +): SpeechTimeline { + applyShortSyllableAveraging(laidOut); + + const keys: SpeechKey[] = []; + for (const lu of laidOut) { + for (const lk of lu.localKeys) { + keys.push({ atMs: lu.startMs + lk.frac * lu.durMs, shape: lk.shape, viseme: lk.viseme }); + } + } + for (const at of pauseAtMs) { + keys.push({ atMs: at, shape: cloneShape(SILENT_SHAPE), viseme: "X" }); + } + keys.sort((a, b) => a.atMs - b.atMs); + + const last = keys[keys.length - 1]; + if (!last || last.viseme !== "X" || last.atMs < totalMs) { + keys.push({ atMs: totalMs, shape: cloneShape(SILENT_SHAPE), viseme: "X" }); + } + + enforceMinHold(keys); + + const finalTotal = Math.max(totalMs, keys[keys.length - 1].atMs); + return { keys, totalDurationMs: finalTotal, envelope, voicedSegments, phraseWindows, phrases, pauses }; +} + +/* ── 텍스트만으로 타이밍(오디오 없음, 결정문 §5.4) ────────────────────── */ +function buildTextOnlyTimeline(phrases: Phrase[], syllablesPerSec: number): SpeechTimeline { + const baseDurMs = 1000 / syllablesPerSec; + const laidOut: LaidUnit[] = []; + const pauseAtMs: number[] = []; + const phraseInfos: SpeechPhraseInfo[] = []; + const pauses: SpeechPauseWindow[] = []; + let tMs = 0; + + phrases.forEach((phrase, pIdx) => { + const startMs = tMs; + const syllableTargetsMs: number[] = []; + const weights: number[] = []; + for (const unit of phrase.units) { + const durMs = unit.weight * baseDurMs; + const localKeys = localKeysFor(unit); + laidOut.push({ startMs: tMs, durMs, localKeys }); + const targetKey = localKeys.find((k) => k.isTarget); + syllableTargetsMs.push(tMs + (targetKey?.frac ?? 0.35) * durMs); + weights.push(unit.weight); + tMs += durMs; + } + phraseInfos.push({ + index: pIdx, + startMs, + endMs: tMs, + kind: phrase.kind, + syllableTargetsMs, + stressedIndices: selectStressedIndices(weights), + }); + if (phrase.restMs > 0) { + pauseAtMs.push(tMs); + pauses.push({ startMs: tMs, endMs: tMs + phrase.restMs }); + tMs += phrase.restMs; + } + }); + + return finalize(laidOut, pauseAtMs, tMs, undefined, undefined, undefined, phraseInfos, pauses); +} + +/* ── 오디오 선분석 정렬(결정문 §5.4) ─────────────────────────────────── */ +const CORE_SNAP_RATIO = 0.35; + +function findEnvelopePeakMs(envelope: SpeechEnvelope, fromMs: number, toMs: number): number | null { + if (toMs <= fromMs) return null; + const i0 = Math.max(0, Math.floor(fromMs / envelope.hopMs)); + const i1 = Math.min(envelope.rms.length - 1, Math.ceil(toMs / envelope.hopMs)); + let bestI = -1; + let bestV = -Infinity; + for (let i = i0; i <= i1; i++) { + if (envelope.rms[i] > bestV) { + bestV = envelope.rms[i]; + bestI = i; + } + } + return bestI >= 0 ? bestI * envelope.hopMs : null; +} + +/** + * 구 수(N)와 유성 구간 수(M)를 맞춘다(결정문 §5.4). + * M > N이면 가장 짧은 틈부터 합친다. M < N이면 가장 긴 구간을 분할한다(중간점 분할로 단순화 — + * "구 가중치 비율로 나눈다"의 근사치. 여러 구가 한 구간에 몰리는 경우까지 정확히 풀려면 + * 별도의 배정 문제가 필요해 단순화했다. 자세한 사유는 작업 보고 참고). + */ +function reconcileSegments(segments: VoicedSegment[], phraseCount: number): VoicedSegment[] { + if (phraseCount === 0) return []; + let segs = segments.map((s) => ({ ...s })); + if (segs.length === 0) { + return Array.from({ length: phraseCount }, (_, i) => ({ startMs: i * 200, endMs: i * 200 + 150 })); + } + + while (segs.length > phraseCount) { + let minGap = Infinity; + let idx = 0; + for (let i = 0; i < segs.length - 1; i++) { + const gap = segs[i + 1].startMs - segs[i].endMs; + if (gap < minGap) { + minGap = gap; + idx = i; + } + } + segs[idx] = { startMs: segs[idx].startMs, endMs: segs[idx + 1].endMs }; + segs.splice(idx + 1, 1); + } + + while (segs.length < phraseCount) { + let maxDur = -1; + let idx = 0; + for (let i = 0; i < segs.length; i++) { + const dur = segs[i].endMs - segs[i].startMs; + if (dur > maxDur) { + maxDur = dur; + idx = i; + } + } + const seg = segs[idx]; + const mid = seg.startMs + (seg.endMs - seg.startMs) / 2; + segs.splice(idx, 1, { startMs: seg.startMs, endMs: mid }, { startMs: mid, endMs: seg.endMs }); + } + + return segs; +} + +function buildAudioAlignedTimeline(phrases: Phrase[], envelope: SpeechEnvelope): SpeechTimeline { + const voicedSegments = detectVoicedSegments(envelope); + const phraseWeights = phrases.map((p) => p.units.reduce((s, u) => s + u.weight, 0)); + const windows = reconcileSegments(voicedSegments, phrases.length); + + const laidOut: LaidUnit[] = []; + const pauseAtMs: number[] = []; + const phraseInfos: SpeechPhraseInfo[] = []; + const pauses: SpeechPauseWindow[] = []; + + for (let i = 0; i < phrases.length; i++) { + const phrase = phrases[i]; + const win = windows[i]; + if (!win || phrase.units.length === 0) { + phraseInfos.push({ + index: i, + startMs: win?.startMs ?? 0, + endMs: win?.endMs ?? 0, + kind: phrase.kind, + syllableTargetsMs: [], + stressedIndices: [], + }); + continue; + } + const totalWeight = phraseWeights[i] || 1; + const winDur = Math.max(1, win.endMs - win.startMs); + let acc = win.startMs; + const syllableTargetsMs: number[] = []; + const ampWeights: number[] = []; + for (const unit of phrase.units) { + const slotDur = winDur * (unit.weight / totalWeight); + const slotStart = acc; + acc += slotDur; + const nominalCenter = slotStart + slotDur / 2; + const searchLo = Math.max(win.startMs, nominalCenter - slotDur * CORE_SNAP_RATIO); + const searchHi = Math.min(win.endMs, nominalCenter + slotDur * CORE_SNAP_RATIO); + const peakMs = findEnvelopePeakMs(envelope, searchLo, searchHi); + const startMs = peakMs === null ? slotStart : slotStart + (peakMs - nominalCenter); + const localKeys = localKeysFor(unit); + laidOut.push({ startMs, durMs: slotDur, localKeys }); + const targetKey = localKeys.find((k) => k.isTarget); + const targetMs = startMs + (targetKey?.frac ?? 0.35) * slotDur; + syllableTargetsMs.push(targetMs); + ampWeights.push(sampleEnvelope(envelope, targetMs)); + } + phraseInfos.push({ + index: i, + startMs: win.startMs, + endMs: win.endMs, + kind: phrase.kind, + syllableTargetsMs, + stressedIndices: selectStressedIndices(ampWeights), + }); + if (i < windows.length - 1) { + pauseAtMs.push(win.endMs); + pauses.push({ startMs: win.endMs, endMs: windows[i + 1].startMs }); + } + } + + const totalMs = windows.length > 0 ? windows[windows.length - 1].endMs : 0; + return finalize(laidOut, pauseAtMs, totalMs, envelope, voicedSegments, windows, phraseInfos, pauses); +} + +export interface BuildSpeechTimelineParams { + text: string; + /** 오디오가 없을 때 쓰는 발화 속도(음절/초). demeanor.speech.syllablesPerSec. */ + syllablesPerSec: number; + /** 있으면 오디오 선분석 정렬 경로를 쓴다. */ + envelope?: SpeechEnvelope; +} + +export function buildSpeechTimeline(params: BuildSpeechTimelineParams): SpeechTimeline { + const phrases = withWeights(tokenizeToPhrases(params.text)); + if (params.envelope) return buildAudioAlignedTimeline(phrases, params.envelope); + return buildTextOnlyTimeline(phrases, params.syllablesPerSec); +} + +/* ── 표본(재생 중 매 프레임, 결정문 §5.4 "표본·평활") ──────────────────── */ +const LOOKAHEAD_MS = 60; +const SILENCE_CLOSE_MS = 100; +const MAX_SPEAKING_OPEN = 0.6; + +function isDeepSilence(segments: VoicedSegment[], tMs: number, minMs: number): boolean { + let prevEnd = 0; + for (const seg of segments) { + if (tMs >= seg.startMs && tMs <= seg.endMs) return false; + if (seg.startMs > tMs) break; + prevEnd = seg.endMs; + } + return tMs - prevEnd > minMs; +} + +/** + * timeline을 tMs(재생 위치, 오디오 시계 또는 엔진 시계) 기준으로 표본한다. + * 60ms 앞당김·smoothstep 보간·짧은 음절 평균화(타임라인 빌드 시 반영됨)·크기 조절식· + * 무음 100ms 닫힘·articulation 곱·open 상한 0.6까지 여기서 끝낸다. + * 임계 감쇠 스프링(τ 45ms)은 포함하지 않는다 — engine.ts가 이 결과를 목표값으로 스프링한다. + */ +export function sampleSpeech(timeline: SpeechTimeline, tMs: number, articulation = 1): SpeechShape { + const keys = timeline.keys; + if (keys.length === 0) return cloneShape(SILENT_SHAPE); + + const tQuery = tMs + LOOKAHEAD_MS; + let shape: SpeechShape; + if (tQuery <= keys[0].atMs) { + shape = cloneShape(keys[0].shape); + } else if (tQuery >= keys[keys.length - 1].atMs) { + shape = cloneShape(keys[keys.length - 1].shape); + } else { + let i = 0; + while (i < keys.length - 1 && keys[i + 1].atMs <= tQuery) i++; + const a = keys[i]; + const b = keys[i + 1]; + const span = b.atMs - a.atMs; + const u = span > 0 ? (tQuery - a.atMs) / span : 1; + shape = lerpShape(a.shape, b.shape, smoothstep(u)); + } + + if (timeline.envelope) { + const env = sampleEnvelope(timeline.envelope, tQuery); + const p90 = timeline.envelope.p90 || 1e-6; + const scale = clamp(0.45 + 0.75 * (env / p90), 0.45, 1.1); + shape = { ...shape, open: shape.open * scale }; + if (timeline.voicedSegments && isDeepSilence(timeline.voicedSegments, tQuery, SILENCE_CLOSE_MS)) { + shape = cloneShape(SILENT_SHAPE); + } + } + + const art = Number.isFinite(articulation) ? articulation : 1; + return { + open: Math.min(shape.open * art, MAX_SPEAKING_OPEN), + wide: shape.wide * art, + round: shape.round * art, + press: shape.press, + }; +} + +/** tMs 시점의 "현재 비짐" 이름(가장 최근에 지난 키). Lab 표시·디버그용. */ +export function currentViseme(timeline: SpeechTimeline, tMs: number): VisemeId { + const keys = timeline.keys; + if (keys.length === 0) return "X"; + const tQuery = tMs + LOOKAHEAD_MS; + if (tQuery <= keys[0].atMs) return keys[0].viseme; + let i = 0; + while (i < keys.length - 1 && keys[i + 1].atMs <= tQuery) i++; + return keys[i].viseme; +} + +/** 타임라인의 비짐 이름을 순서대로(연속 중복 제거) 뽑는다. 검증·디버그용. */ +export function visemeSequence(timeline: SpeechTimeline): VisemeId[] { + const out: VisemeId[] = []; + for (const k of timeline.keys) { + if (out[out.length - 1] !== k.viseme) out.push(k.viseme); + } + return out; +} diff --git a/apps/web/src/components/avatar/engine/performance.ts b/apps/web/src/components/avatar/engine/performance.ts new file mode 100644 index 0000000..1156495 --- /dev/null +++ b/apps/web/src/components/avatar/engine/performance.ts @@ -0,0 +1,203 @@ +/* ===================================================================== + 아바타 v3 신호 → 공연 스크립트 — 결정문 §7 정본 규칙. + 괄호 지문 파서(§7.3), 상태 유도 보조 cue(§7.4), 겉표정 강도(§7.5)를 + 여기서 구현한다. 렌더러는 이 모듈을 몰라도 되고, 엔진은 Performance + 타입만 소비한다. + ===================================================================== */ + +import { AVATAR_EXPRESSION_LIBRARY, type AvatarExpression } from "../persona"; +import { STAGE_DIRECTION_MAX_LENGTH, STAGE_DIRECTION_RULES } from "./stageDirectionLexicon"; +import { createRng, hashString } from "./rng"; +import type { ReactionClipId } from "./clipCatalog"; + +export type PerformanceAnchor = "listen" | "pre" | "inline" | "post"; +export type PerformanceCueSource = "direction" | "state" | "jev"; + +export interface PerformanceCue { + clip: ReactionClipId; + anchor: PerformanceAnchor; + /** anchor=inline이면 발화 텍스트 내 비율(0..1), 그 외에는 앵커 기준 지연 ms. */ + at: number; + /** 클립 delta 배율(0..1). 생략하면 1. */ + weight?: number; + source: PerformanceCueSource; +} + +export interface Performance { + surface: { expression: AvatarExpression; intensity: number }; + leak?: { expression: AvatarExpression; intensity: number; atMs: number; durationMs: number }; + cues: PerformanceCue[]; +} + +/* voice.py _STAGE_DIRECTION_RE 와 같은 괄호 문자 집합(반각·전각 소괄호·대괄호). */ +const BRACKET_RE = /[(([【][^))\]】]*[))\]】]/g; +/* 복합 지문 분리 구분자. */ +const SPLIT_RE = /[,,·、]/; +/* 발화 가능 글자에서 제외하는 구두점(공백은 별도로 제외). */ +const SPEAKABLE_PUNCTUATION = new Set(["…", ".", ",", "?", "!", "~"]); + +function countSpeakable(text: string): number { + let count = 0; + for (const ch of text) { + if (/\s/.test(ch)) continue; + if (SPEAKABLE_PUNCTUATION.has(ch)) continue; + count++; + } + return count; +} + +/** + * 괄호 지문을 cue로 파싱한다(결정문 §7.3). + * 대상은 내담자 발화 원문 전체다. 발화 가능 텍스트에서 지문 조각은 제거된 채로 남는다. + */ +export function parseStageDirections(text: string): { + cues: PerformanceCue[]; + unmatched: string[]; + speakableText: string; +} { + const rawCues: PerformanceCue[] = []; + const unmatched: string[] = []; + + for (const match of text.matchAll(BRACKET_RE)) { + const full = match[0]; + const start = match.index ?? 0; + const end = start + full.length; + const inner = full.slice(1, -1).trim(); + if (inner.length > STAGE_DIRECTION_MAX_LENGTH) continue; + + const beforeText = text.slice(0, start).replace(BRACKET_RE, ""); + const afterText = text.slice(end).replace(BRACKET_RE, ""); + const beforeCount = countSpeakable(beforeText); + const afterCount = countSpeakable(afterText); + + let anchor: PerformanceAnchor; + let at: number; + if (beforeCount === 0) { + anchor = "pre"; + at = 0; + } else if (afterCount === 0) { + anchor = "post"; + at = 0; + } else { + anchor = "inline"; + at = beforeCount / (beforeCount + afterCount); + } + + const pieces = inner + .split(SPLIT_RE) + .map((piece) => piece.trim()) + .filter((piece) => piece.length > 0); + + for (const piece of pieces) { + const rule = STAGE_DIRECTION_RULES.find((candidate) => candidate.pattern.test(piece)); + if (rule) { + rawCues.push({ clip: rule.clip, anchor, at, weight: rule.weight, source: "direction" }); + } else { + unmatched.push(piece); + } + } + } + + /* 같은 클립이 연달아 나오면 하나로 합친다. */ + const merged: PerformanceCue[] = []; + for (const cue of rawCues) { + const prev = merged[merged.length - 1]; + if (prev && prev.clip === cue.clip) continue; + merged.push(cue); + } + + return { + cues: merged.slice(0, 3), + unmatched, + speakableText: text.replace(BRACKET_RE, "").trim(), + }; +} + +interface AuxCandidate { + clip: ReactionClipId; + anchor: PerformanceAnchor; + at: number; + weight?: number; +} + +function auxCandidatesFor(group: string | undefined, intensity: number): AuxCandidate[] { + switch (group) { + case "negative": + if (intensity < 0.5) return []; + return [ + { clip: "look_down", anchor: "pre", at: 200 }, + { clip: "lip_press", anchor: "post", at: 0 }, + ]; + case "defensive": + return [ + { clip: "look_away_side", anchor: "inline", at: 0.3 }, + { clip: "lean_back", anchor: "pre", at: 0 }, + ]; + case "positive": + return [{ clip: "nod_small", anchor: "inline", at: 0.4 }]; + case "energy": + return [{ clip: "look_down", anchor: "pre", at: 0, weight: 0.6 }]; + case "cognitive": + default: + return []; + } +} + +/** 겉표정 강도(결정문 §7.5, legacy 경로). */ +export function surfaceIntensityFor(params: { + expression: AvatarExpression; + openness: number; + safety: boolean; + paused: boolean; +}): number { + const { expression, openness, safety, paused } = params; + if (safety) return 0.9; + if (paused) return 0.6; + const group = AVATAR_EXPRESSION_LIBRARY.find((item) => item.id === expression)?.group; + const o = openness < 0 ? 0 : openness > 1 ? 1 : openness; + switch (group) { + case "negative": + case "defensive": + return 0.35 + 0.5 * (1 - o); + case "positive": + return 0.3 + 0.6 * o; + default: + return 0.5; + } +} + +/** 지문 파싱 + 상태 유도 보조 cue(§7.4)까지 합쳐 공연 스크립트를 만든다. */ +export function buildPerformance(params: { + text: string; + expression: AvatarExpression; + intensity: number; + openness: number; + seed: number; +}): { performance: Performance; unmatched: string[]; speakableText: string } { + const { text, expression, intensity, seed } = params; + const { cues, unmatched, speakableText } = parseStageDirections(text); + + let finalCues = cues; + if (cues.length === 0) { + const rng = createRng(hashString(text) ^ seed); + if (rng.next() < 0.6) { + const group = AVATAR_EXPRESSION_LIBRARY.find((item) => item.id === expression)?.group; + const candidates = auxCandidatesFor(group, intensity); + if (candidates.length > 0) { + const picked = rng.pick(candidates); + finalCues = [{ ...picked, source: "state" }]; + } + } + } + + return { + performance: { surface: { expression, intensity }, cues: finalCues }, + unmatched, + speakableText, + }; +} + +/** 발화 시간 추정: max(800, 발화 가능 글자 수 × 90)ms. */ +export function estimateSpeechDurationMs(speakableText: string): number { + return Math.max(800, countSpeakable(speakableText) * 90); +} diff --git a/apps/web/src/components/avatar/engine/rng.ts b/apps/web/src/components/avatar/engine/rng.ts new file mode 100644 index 0000000..b599330 --- /dev/null +++ b/apps/web/src/components/avatar/engine/rng.ts @@ -0,0 +1,47 @@ +/* ===================================================================== + 아바타 v3 엔진 전용 시드 난수 — mulberry32. + 엔진은 순수 함수여야 하므로(결정문 §3) Math.random을 직접 쓰지 않고 + 이 모듈이 주입하는 rng를 통해서만 난수를 소비한다. + ===================================================================== */ + +export interface Rng { + /** [0,1) 균등 분포. */ + next(): number; + /** [a,b) 균등 분포. */ + range(a: number, b: number): number; + /** 배열에서 균등하게 하나 고른다. */ + pick(arr: readonly T[]): T; +} + +export function createRng(seed: number): Rng { + let state = seed >>> 0; + + function next(): number { + state = (state + 0x6d2b79f5) | 0; + let t = state; + t = Math.imul(t ^ (t >>> 15), t | 1); + t ^= t + Math.imul(t ^ (t >>> 7), t | 61); + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + } + + return { + next, + range(a: number, b: number): number { + return a + (b - a) * next(); + }, + pick(arr: readonly T[]): T { + const idx = Math.min(arr.length - 1, Math.floor(next() * arr.length)); + return arr[idx]; + }, + }; +} + +/** FNV-1a 32bit 문자열 해시. */ +export function hashString(text: string): number { + let hash = 0x811c9dc5; + for (let i = 0; i < text.length; i++) { + hash ^= text.charCodeAt(i); + hash = Math.imul(hash, 0x01000193); + } + return hash >>> 0; +} diff --git a/apps/web/src/components/avatar/engine/speechEnvelope.ts b/apps/web/src/components/avatar/engine/speechEnvelope.ts new file mode 100644 index 0000000..cc32ccb --- /dev/null +++ b/apps/web/src/components/avatar/engine/speechEnvelope.ts @@ -0,0 +1,106 @@ +/* ===================================================================== + 아바타 v3 발화 포락선 — 결정문 §5.4 발화층 "타이밍(오디오가 있으면 선분석)". + AudioBuffer 등 DOM 타입에 의존하지 않는 순수 함수만 둔다(테스트 가능해야 한다). + 호출부(Lab·Session)가 AudioBuffer에서 채널 Float32Array를 꺼내 넘긴다. + ===================================================================== */ + +export interface SpeechEnvelope { + /** RMS 표본 간격(ms). 결정문 §5.4는 10ms. */ + hopMs: number; + /** hop마다 하나씩, 모노 합성 RMS. */ + rms: Float32Array; + /** 진폭 조절식(§5.4 표본·평활)에 쓰는 90퍼센타일. */ + p90: number; + /** 유성 구간 임계값(§5.4 타이밍)에 쓰는 95퍼센타일. */ + p95: number; +} + +export interface VoicedSegment { + startMs: number; + endMs: number; +} + +function percentile(sortedAscending: ArrayLike, p: number): number { + const n = sortedAscending.length; + if (n === 0) return 0; + const idx = Math.min(n - 1, Math.max(0, Math.ceil(p * n) - 1)); + return sortedAscending[idx]; +} + +/** + * 오디오 채널 데이터(모노 Float32Array 1개 또는 채널별 배열)에서 10ms 간격 RMS 포락선을 만든다. + * 다채널이면 표본마다 채널 평균으로 모노 합성한다(결정문 §5.4 "AudioBuffer 모노 합성"). + */ +export function computeEnvelope( + channelData: Float32Array | Float32Array[], + sampleRate: number, + hopMs = 10, +): SpeechEnvelope { + const channels = Array.isArray(channelData) ? channelData : [channelData]; + const length = channels[0]?.length ?? 0; + const hopSamples = Math.max(1, Math.round((hopMs / 1000) * sampleRate)); + const hopCount = Math.max(1, Math.ceil(length / hopSamples)); + const rms = new Float32Array(hopCount); + + for (let h = 0; h < hopCount; h++) { + const start = h * hopSamples; + const end = Math.min(length, start + hopSamples); + let sumSq = 0; + let n = 0; + for (let i = start; i < end; i++) { + let mono = 0; + for (const ch of channels) mono += ch[i] ?? 0; + mono /= channels.length; + sumSq += mono * mono; + n++; + } + rms[h] = n > 0 ? Math.sqrt(sumSq / n) : 0; + } + + const sorted = Array.from(rms).sort((a, b) => a - b); + return { hopMs, rms, p90: percentile(sorted, 0.9), p95: percentile(sorted, 0.95) }; +} + +/** + * 유성 구간 검출(결정문 §5.4): 포락선 > max(0.02, 0.12·P95)인 구간. + * 120ms 미만 틈은 메우고, 60ms 미만 구간은 버린다. + */ +export function detectVoicedSegments(envelope: SpeechEnvelope): VoicedSegment[] { + const { hopMs, rms, p95 } = envelope; + const threshold = Math.max(0.02, 0.12 * p95); + + const raw: VoicedSegment[] = []; + let curStart = -1; + for (let i = 0; i < rms.length; i++) { + const above = rms[i] > threshold; + if (above && curStart < 0) curStart = i; + if (!above && curStart >= 0) { + raw.push({ startMs: curStart * hopMs, endMs: i * hopMs }); + curStart = -1; + } + } + if (curStart >= 0) raw.push({ startMs: curStart * hopMs, endMs: rms.length * hopMs }); + + const filled: VoicedSegment[] = []; + for (const seg of raw) { + const prev = filled[filled.length - 1]; + if (prev && seg.startMs - prev.endMs < 120) { + prev.endMs = seg.endMs; + } else { + filled.push({ ...seg }); + } + } + + return filled.filter((seg) => seg.endMs - seg.startMs >= 60); +} + +/** tMs 시점의 포락선 값(선형 보간). */ +export function sampleEnvelope(envelope: SpeechEnvelope, tMs: number): number { + const { hopMs, rms } = envelope; + if (rms.length === 0) return 0; + const idx = tMs / hopMs; + const i0 = Math.max(0, Math.min(rms.length - 1, Math.floor(idx))); + const i1 = Math.min(rms.length - 1, i0 + 1); + const frac = idx - i0; + return rms[i0] + (rms[i1] - rms[i0]) * frac; +} diff --git a/apps/web/src/components/avatar/engine/stageDirectionLexicon.ts b/apps/web/src/components/avatar/engine/stageDirectionLexicon.ts new file mode 100644 index 0000000..b5a7aec --- /dev/null +++ b/apps/web/src/components/avatar/engine/stageDirectionLexicon.ts @@ -0,0 +1,69 @@ +/* ===================================================================== + 아바타 v3 괄호 지문 어휘집 — 결정문 §7.3 정본. + 근거: 실제 LLM 발화 표본(scratch/jev, P1 40턴)·golden 합성 대화(data/golden/P4~P7)· + 캐릭터 카드 verbal_tics. 괄호는 지문 외 용도(인용·메모)로도 쓰이므로 + 어휘집에 맞는 괄호만 반응으로 바꾸고 나머지는 무시한다. + 규칙은 위에서부터 검사해 처음 맞는 것 하나만 쓴다(구체 → 일반 순서 유지). + 어휘 추가·순서 변경은 설계 결정이다. + ===================================================================== */ + +import type { ReactionClipId } from "./clipCatalog"; + +export interface StageDirectionRule { + /** 괄호 안쪽 텍스트(앞뒤 공백 제거)에 대한 패턴. */ + pattern: RegExp; + clip: ReactionClipId; + /** 클립 delta 배율(0..1). 생략하면 1. */ + weight?: number; +} + +/** 괄호 안쪽이 이 길이를 넘으면 지문이 아닌 것으로 보고 무시한다. */ +export const STAGE_DIRECTION_MAX_LENGTH = 16; + +export const STAGE_DIRECTION_RULES: readonly StageDirectionRule[] = [ + /* 눈물·울음 */ + { pattern: /눈물|울먹|글썽|훌쩍|목이\s*메/, clip: "tear_up" }, + + /* 한숨·호흡 */ + { pattern: /(옅은|작은|짧은)\s*한숨/, clip: "sigh", weight: 0.6 }, + { pattern: /한숨/, clip: "sigh" }, + { pattern: /심호흡|숨을\s*(고르|크게|깊이)|크게\s*숨/, clip: "deep_breath" }, + { pattern: /침을\s*삼|마른침|꿀꺽/, clip: "swallow" }, + + /* 침묵·멈춤·주저 */ + { pattern: /침묵/, clip: "silence_hold" }, + { pattern: /멈칫|굳어|얼어/, clip: "freeze", weight: 0.7 }, + { pattern: /(잠시|잠깐)?\s*멈춤|말끝을?\s*흐리/, clip: "look_down", weight: 0.7 }, + { pattern: /머뭇|망설/, clip: "lip_press", weight: 0.6 }, + + /* 웃음 */ + { pattern: /피식|코웃음|쓴웃음|헛웃음|비웃/, clip: "scoff" }, + { pattern: /억지\s*(로\s*)?웃|억지\s*미소/, clip: "forced_smile" }, + { pattern: /(어색|긴장|멋쩍|머쓱)[한은]?\s*웃음|웃음/, clip: "nervous_laugh" }, + + /* 고개 */ + { pattern: /(크게|깊이|깊게)\s*끄덕/, clip: "nod_deep" }, + { pattern: /끄덕/, clip: "nod_small" }, + { pattern: /고개를?\s*(젓|저어|흔들)|도리도리/, clip: "head_shake" }, + { pattern: /고개를?\s*(숙|떨|푹)|고개\s*떨굼/, clip: "head_drop" }, + { pattern: /고개를?\s*(살짝\s*)?돌/, clip: "look_away_side" }, + + /* 시선 */ + { pattern: /시선을?\s*(피|돌)|눈을?\s*(피|돌)|딴\s*곳|창밖/, clip: "look_away_side" }, + { pattern: /시선을?\s*(내리|아래)|바닥을?\s*(보|내려)|내려다/, clip: "look_down" }, + { pattern: /힐끗|흘끗|쳐다|눈을?\s*맞/, clip: "glance_up" }, + { pattern: /눈을?\s*굴리/, clip: "eye_roll_small" }, + { pattern: /눈을?\s*(여러\s*번\s*)?깜빡/, clip: "blink_flurry" }, + + /* 몸 */ + { pattern: /움찔|흠칫|놀라/, clip: "flinch" }, + { pattern: /으쓱/, clip: "shrug" }, + { pattern: /뒤로\s*기대|등을?\s*기대|몸을?\s*뒤로|팔짱/, clip: "lean_back" }, + { pattern: /몸을?\s*앞으로|다가앉|앞으로\s*기울/, clip: "lean_forward" }, + { pattern: /만지작|꼼지락|안절부절|다리를?\s*떨|손톱/, clip: "fidget_sway" }, + + /* 입 */ + { pattern: /입술을?\s*깨물|입술\s*깨묾/, clip: "lip_bite" }, + { pattern: /이를?\s*악물|주먹을?\s*쥐/, clip: "jaw_clench" }, + { pattern: /입술을?\s*(꾹\s*)?다물|입을?\s*(꾹\s*)?다물/, clip: "lip_press" }, +]; diff --git a/apps/web/src/components/avatar/v3/DebugFace.tsx b/apps/web/src/components/avatar/v3/DebugFace.tsx new file mode 100644 index 0000000..7ad2955 --- /dev/null +++ b/apps/web/src/components/avatar/v3/DebugFace.tsx @@ -0,0 +1,215 @@ +/* ===================================================================== + DebugFace — 아바타 v3 엔진 채널 33개를 전부 눈으로 검증하는 선 기반 SVG 리그. + 아트 품질이 목적이 아니라 채널→시각 매핑 확인용이다(결정문 §9 1단계). + 매 프레임 React state를 갱신하지 않고 ref로 SVG 속성을 직접 쓴다. + ===================================================================== */ + +import { useEffect, useRef } from "react"; +import type { AvatarEngine } from "../engine/engine"; +import type { ChannelVector } from "../engine/channels"; +import "./debug-face.css"; + +export interface DebugFaceProps { + engine: AvatarEngine; + running: boolean; + nowMs?: () => number; +} + +type Refs = Record; + +const CX = 130; +const HEAD_CY = 150; + +function eyePathD(cx: number, cy: number, openness: number, smile: number, lidTension: number): string { + const rx = 20; + const ry = 14; + const o = Math.max(0, Math.min(1.3, openness)); + const tensionAdj = 1 - lidTension * 0.4; + const upperY = cy - ry * 1.05 * o * tensionAdj; + const lowerY = cy + ry * 0.5 * o * (1 - Math.max(0, smile) * 0.7) * tensionAdj; + return `M ${cx - rx} ${cy} Q ${cx} ${upperY} ${cx + rx} ${cy} Q ${cx} ${lowerY} ${cx - rx} ${cy} Z`; +} + +function browPathD(innerX: number, outerX: number, baseY: number, y: number, angle: number, x: number, form: number): string { + const innerY = baseY - y * 18 - angle * 14; + const outerY = baseY - y * 18 + angle * 8; + const innerXAdj = innerX - x * 6; + const midX = (innerXAdj + outerX) / 2; + const midY = (innerY + outerY) / 2 - form * 10; + return `M ${outerX} ${outerY} Q ${midX} ${midY} ${innerXAdj} ${innerY}`; +} + +function mouthPathD(v: ChannelVector): string { + const cy = 192; + /* mouthRound(§4 — 2026-09-30 립싱크 설계 채널): 오므린 입("ㅗ·ㅜ")을 입 폭을 줄이고 + 세로로 둥글려 간단히 반영한다. 정밀한 리그 규칙(§8.4)은 LinocutAvatar가 맡는다. */ + const round = Math.max(0, Math.min(1, v.mouthRound)); + const halfWidth = Math.max(6, 26 + v.mouthStretch * 16 - round * 14); + const leftX = CX - halfWidth; + const rightX = CX + halfWidth; + const cornerLift = v.mouthForm * 18; + const asym = v.mouthAsym * 12; + const leftCornerY = cy - cornerLift - asym; + const rightCornerY = cy - cornerLift + asym; + const restGap = Math.max(1.5, 5 - v.mouthPress * 3.2) + round * 6; + const openGap = v.mouthOpen * 26; + const upperCtrlY = cy - restGap - openGap * 0.5; + const lowerCtrlY = cy + restGap + openGap; + return `M ${leftX} ${leftCornerY} Q ${CX} ${upperCtrlY} ${rightX} ${rightCornerY} Q ${CX} ${lowerCtrlY} ${leftX} ${leftCornerY} Z`; +} + +function renderFrame(refs: Refs, v: ChannelVector): void { + const body = refs.body; + if (body) { + const scaleY = 1 + v.bodyLean * 0.04; + const translateX = v.bodyYaw * 6; + body.setAttribute( + "transform", + `translate(${translateX} ${-v.bodyLean * 6}) rotate(${v.bodyRoll * 6} ${CX} 300) scale(1 ${scaleY})`, + ); + } + + const shoulder = refs.shoulder; + if (shoulder) shoulder.setAttribute("transform", `translate(0 ${-v.shoulderRaise * 12})`); + + const head = refs.head; + if (head) { + const translateX = v.headYaw * 14; + const translateY = -v.headPitch * 14; + head.setAttribute("transform", `translate(${translateX} ${translateY}) rotate(${v.headRoll * 10} ${CX} ${HEAD_CY})`); + } + + const face = refs.face; + if (face) { + const squish = 1 - Math.abs(v.headYaw) * 0.15; + face.setAttribute("transform", `translate(${v.headYaw * 4} 0) scale(${squish} 1)`); + } + + const eyeLeft = refs["eye-left"] as SVGPathElement | null; + if (eyeLeft) eyeLeft.setAttribute("d", eyePathD(96, 140, v.eyeOpenL, v.eyeSmileL, v.lidTension)); + const eyeRight = refs["eye-right"] as SVGPathElement | null; + if (eyeRight) eyeRight.setAttribute("d", eyePathD(164, 140, v.eyeOpenR, v.eyeSmileR, v.lidTension)); + + const irisLeft = refs["iris-left"]; + if (irisLeft) irisLeft.setAttribute("transform", `translate(${v.gazeX * 8} ${v.gazeY * 6})`); + const irisRight = refs["iris-right"]; + if (irisRight) irisRight.setAttribute("transform", `translate(${v.gazeX * 8} ${v.gazeY * 6})`); + + const pupilLeft = refs["pupil-left"] as SVGCircleElement | null; + if (pupilLeft) pupilLeft.setAttribute("r", String(Math.max(1, 3 * (1 + v.pupilSize * 0.5)))); + const pupilRight = refs["pupil-right"] as SVGCircleElement | null; + if (pupilRight) pupilRight.setAttribute("r", String(Math.max(1, 3 * (1 + v.pupilSize * 0.5)))); + + const browLeft = refs["brow-left"] as SVGPathElement | null; + if (browLeft) browLeft.setAttribute("d", browPathD(112, 72, 110, v.browLY, v.browLAngle, v.browLX, v.browForm)); + const browRight = refs["brow-right"] as SVGPathElement | null; + if (browRight) browRight.setAttribute("d", browPathD(148, 188, 110, v.browRY, v.browRAngle, -v.browRX, v.browForm)); + + const mouth = refs.mouth as SVGPathElement | null; + if (mouth) mouth.setAttribute("d", mouthPathD(v)); + + const chin = refs.chin; + if (chin) chin.setAttribute("opacity", String(Math.max(0, Math.min(1, v.chinRaise)))); + + const cheekLeft = refs["cheek-left"]; + if (cheekLeft) cheekLeft.setAttribute("opacity", String(Math.max(0, Math.min(1, v.cheek)) * 0.85)); + const cheekRight = refs["cheek-right"]; + if (cheekRight) cheekRight.setAttribute("opacity", String(Math.max(0, Math.min(1, v.cheek)) * 0.85)); + + const pallor = refs.pallor; + if (pallor) pallor.setAttribute("opacity", String(Math.max(0, Math.min(1, v.pallor)) * 0.55)); + + const tearLeft = refs["tear-left"]; + if (tearLeft) { + const pool = Math.max(0, Math.min(1, v.tear / 0.3)); + const flow = Math.max(0, (v.tear - 0.3) / 0.7); + tearLeft.setAttribute("opacity", String(pool)); + tearLeft.setAttribute("transform", `translate(0 0) scale(${1 + flow * 1.6})`); + } + const tearRight = refs["tear-right"]; + if (tearRight) { + const pool = Math.max(0, Math.min(1, v.tear / 0.3)); + const flow = Math.max(0, (v.tear - 0.3) / 0.7); + tearRight.setAttribute("opacity", String(pool)); + tearRight.setAttribute("transform", `translate(0 0) scale(${1 + flow * 1.6})`); + } + + const sweat = refs.sweat; + if (sweat) sweat.setAttribute("opacity", String(Math.max(0, Math.min(1, v.sweat)))); + + const breath = refs.breath; + if (breath) breath.setAttribute("transform", `scale(${0.85 + v.breath * 0.3})`); +} + +function setRef(refs: Refs, name: string) { + return (el: SVGGraphicsElement | null) => { + refs[name] = el; + }; +} + +export default function DebugFace({ engine, running, nowMs }: DebugFaceProps) { + const refs = useRef({}); + const rafRef = useRef(0); + + useEffect(() => { + if (!running) return; + const clock = nowMs ?? (() => performance.now()); + const loop = () => { + const v = engine.evaluate(clock()); + renderFrame(refs.current, v); + rafRef.current = requestAnimationFrame(loop); + }; + rafRef.current = requestAnimationFrame(loop); + return () => cancelAnimationFrame(rafRef.current); + }, [engine, running, nowMs]); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/apps/web/src/components/avatar/v3/LinocutAvatar.tsx b/apps/web/src/components/avatar/v3/LinocutAvatar.tsx new file mode 100644 index 0000000..8352635 --- /dev/null +++ b/apps/web/src/components/avatar/v3/LinocutAvatar.tsx @@ -0,0 +1,1089 @@ +/* ===================================================================== + LinocutAvatar — P1 리노컷 렌더러(결정문 §8.4). 단일 SVG, 레이어 이미지와 + 벡터 부위가 rig 좌표계(canvas)를 공유한다. rAF에서 engine.evaluate()를 + 불러 linocutGeometry의 순수 함수로 프레임을 계산하고 ref로 SVG 속성만 + 직접 쓴다(DebugFace와 같은 방식, 매 프레임 React state 갱신 없음). + 로드 상태만 React state로 관리한다(§8.4 로드 실패 규칙). + ===================================================================== */ + +import { useEffect, useId, useMemo, useRef, useState } from "react"; +import type { AvatarEngine } from "../engine/engine"; +import { AVATAR_EXPRESSION_LIBRARY, type AvatarExpression } from "../persona"; +import type { BackdropGroup, LinocutRig, RigCrop } from "./linocutRig"; +import { + computeLinocutFrame, + faceOvalPointsAttr, + shrunkFaceOvalPointsAttr, + lipStripClipRect, + LIP_STRIP_COUNT, + jawWarpStripRects, + jawWarpStripClipPolygons, + jawWarpHeadHoleClipD, + jawWarpUpperBoundaryY, + type LinocutFrame, + type JawWarpLayer, +} from "./linocutGeometry"; +import "./linocut-avatar.css"; + +export interface LinocutAvatarProps { + rig: LinocutRig; + engine: AvatarEngine; + running: boolean; + nowMs?: () => number; + crop?: RigCrop; + backdropExpression?: AvatarExpression; + texture?: boolean; + label?: string; + className?: string; + onLoadStateChange?: (state: "loading" | "ready" | "error") => void; +} + +type Refs = Record; +type LoadState = "loading" | "ready" | "error"; + +function setRef(refs: Refs, name: string) { + return (el: SVGGraphicsElement | null) => { + refs[name] = el; + }; +} + +function groupFor(expression: AvatarExpression): BackdropGroup { + return AVATAR_EXPRESSION_LIBRARY.find((e) => e.id === expression)?.group ?? "cognitive"; +} + +/** 혀 그늘 색(§5.4 렌더러 규칙: "mouthInner를 밝게 섞어 정한 뒤 보고"). mouthInner를 + 흰색 쪽으로 35% 섞은 밝은 적갈색 — 2단계-B-1d-R6 선택, 보고 완료. */ +function lightenHex(hex: string, amount: number): string { + const n = parseInt(hex.replace("#", ""), 16); + const r = (n >> 16) & 0xff; + const g = (n >> 8) & 0xff; + const b = n & 0xff; + const mix = (c: number) => Math.round(c + (255 - c) * amount); + return `rgb(${mix(r)}, ${mix(g)}, ${mix(b)})`; +} + +function applyEye(refs: Refs, side: "left" | "right", eye: LinocutFrame["eyeLeft"]): void { + const p = `eye-${side}`; + const outline = refs[`${p}-outline`] as SVGPathElement | null; + if (outline) outline.setAttribute("d", eye.outlineD); + const clipD = refs[`${side}-clip-d`] as SVGPathElement | null; + if (clipD) clipD.setAttribute("d", eye.outlineD); + const upperLid = refs[`${p}-upper-lid`] as SVGPathElement | null; + if (upperLid) upperLid.setAttribute("d", eye.upperLidD); + const crease = refs[`${p}-crease`] as SVGPathElement | null; + if (crease) { + crease.setAttribute("d", eye.creaseD); + crease.setAttribute("opacity", String(eye.creaseOpacity)); + } + const lowerLid = refs[`${p}-lower-lid`] as SVGPathElement | null; + if (lowerLid) lowerLid.setAttribute("d", eye.lowerLidD); + const shadow = refs[`${p}-shadow`] as SVGPathElement | null; + if (shadow) shadow.setAttribute("d", eye.shadowD); + + const irisRing = refs[`${p}-iris-ring`] as SVGCircleElement | null; + if (irisRing) { + irisRing.setAttribute("cx", String(eye.irisCx)); + irisRing.setAttribute("cy", String(eye.irisCy)); + irisRing.setAttribute("r", String(Math.max(0, eye.irisRadius - eye.irisRingWidth / 2))); + irisRing.setAttribute("stroke-width", String(eye.irisRingWidth)); + } + const pupil = refs[`${p}-pupil`] as SVGCircleElement | null; + if (pupil) { + pupil.setAttribute("cx", String(eye.irisCx)); + pupil.setAttribute("cy", String(eye.irisCy)); + pupil.setAttribute("r", String(eye.pupilRadius)); + } + const highlight = refs[`${p}-highlight`] as SVGCircleElement | null; + if (highlight) { + highlight.setAttribute("cx", String(eye.highlightCx)); + highlight.setAttribute("cy", String(eye.highlightCy)); + highlight.setAttribute("r", String(eye.highlightRadius)); + } + const radial = refs[`${p}-radial`] as SVGPathElement | null; + if (radial) radial.setAttribute("d", eye.radialStrokesD); + + const tearGroup = refs[`tear-${side}`]; + if (tearGroup) tearGroup.setAttribute("opacity", String(eye.tearPoolOpacity)); + const tearPool = refs[`${p}-tear-pool`] as SVGPathElement | null; + if (tearPool) tearPool.setAttribute("d", eye.tearPoolD); + const tearDrop = refs[`${p}-tear-drop`] as SVGEllipseElement | null; + if (tearDrop) { + tearDrop.setAttribute("cx", String(eye.tearDropCx)); + tearDrop.setAttribute("cy", String(eye.tearDropCy)); + tearDrop.setAttribute("rx", String(eye.tearDropR)); + tearDrop.setAttribute("ry", String(eye.tearDropR * 1.3)); + } + const tearFlow = refs[`${p}-tear-flow`] as SVGPathElement | null; + if (tearFlow) { + tearFlow.setAttribute("d", eye.tearFlowD); + tearFlow.setAttribute("opacity", String(eye.tearFlowOpacity)); + } + const tearFlowDrop = refs[`${p}-tear-flow-drop`] as SVGEllipseElement | null; + if (tearFlowDrop) { + tearFlowDrop.setAttribute("cx", String(eye.tearFlowDropCx)); + tearFlowDrop.setAttribute("cy", String(eye.tearFlowDropCy)); + tearFlowDrop.setAttribute("rx", String(eye.tearFlowDropR)); + tearFlowDrop.setAttribute("ry", String(eye.tearFlowDropR * 1.2)); + tearFlowDrop.setAttribute("opacity", String(eye.tearFlowOpacity)); + } +} + +/** 하관 띠 변형(§8.4) 레이어 하나(head 또는 faceDetail) 적용. max|f|<0.3px(negligible)이면 + 그룹을 숨기고 개별 띠 속성은 건드리지 않는다("쉬는 동안 비용 0"). */ +function applyJawWarpLayer(refs: Refs, prefix: string, layer: JawWarpLayer | null): void { + const group = refs[`${prefix}-group`]; + if (!group) return; + if (!layer || layer.negligible) { + group.setAttribute("opacity", "0"); + return; + } + group.setAttribute("opacity", "1"); + layer.strips.forEach((strip, i) => { + const img = refs[`${prefix}-strip-${i}`] as SVGImageElement | null; + if (img) img.setAttribute("transform", `matrix(1 0 0 ${strip.scaleY} 0 ${strip.translateY})`); + }); +} + +/** R10(§8.4 개정, out4·out5 반려 대응): faceDetail은 대부분 투명한 덧칠 레이어라, + 하관 띠가 켜진 동안 원본 전체를 그대로 두면 변형된 사본과 겹쳐 턱선이 원래 + 자리와 내려간 자리에 두 겹(점선)으로 보인다. negligible(쉬는 동안)이면 원본 + 전체(face-detail-full)를 보이고, 아니면 변형 영역(y > y_n)을 뺀 사본 + (face-detail-upper)으로 바꾼다. */ +function applyFaceDetailSplit(refs: Refs, negligible: boolean): void { + const full = refs["face-detail-full"]; + if (full) full.setAttribute("opacity", negligible ? "1" : "0"); + const upper = refs["face-detail-upper"]; + if (upper) upper.setAttribute("opacity", negligible ? "0" : "1"); +} + +/** R13(§8.4 개정, out9 jaw-zoom 반려 대응): head-image도 faceDetail과 같은 이유로 + 원본 전체(head-image-full)/띠가 덮는 영역만 뚫은 사본(head-image-masked) 중 + negligible 여부로 하나만 보인다. */ +function applyHeadImageSplit(refs: Refs, negligible: boolean): void { + const full = refs["head-image-full"]; + if (full) full.setAttribute("opacity", negligible ? "1" : "0"); + const masked = refs["head-image-masked"]; + if (masked) masked.setAttribute("opacity", negligible ? "0" : "1"); +} + +function applyFrame(refs: Refs, frame: LinocutFrame): void { + const figure = refs.figure; + if (figure) figure.setAttribute("transform", frame.transforms.figure); + const body = refs.body; + if (body) body.setAttribute("transform", frame.transforms.body); + const head = refs.head; + if (head) head.setAttribute("transform", frame.transforms.head); + const face = refs.face; + if (face) face.setAttribute("transform", frame.transforms.face); + const hairFront = refs["hair-front"]; + if (hairFront) hairFront.setAttribute("transform", frame.transforms.hairFront); + + applyEye(refs, "left", frame.eyeLeft); + applyEye(refs, "right", frame.eyeRight); + + const browLeft = refs["brow-left"] as SVGPathElement | null; + if (browLeft) browLeft.setAttribute("d", frame.browLeft.bodyD); + const browLeftHair = refs["brow-left-hair"] as SVGPathElement | null; + if (browLeftHair) { + browLeftHair.setAttribute("d", frame.browLeft.hairStrokesD); + browLeftHair.setAttribute("stroke-width", String(frame.browLeft.hairStrokeWidth)); + } + const browRight = refs["brow-right"] as SVGPathElement | null; + if (browRight) browRight.setAttribute("d", frame.browRight.bodyD); + const browRightHair = refs["brow-right-hair"] as SVGPathElement | null; + if (browRightHair) { + browRightHair.setAttribute("d", frame.browRight.hairStrokesD); + browRightHair.setAttribute("stroke-width", String(frame.browRight.hairStrokeWidth)); + } + const browLeftShadow = refs["brow-left-shadow"] as SVGEllipseElement | null; + if (browLeftShadow) { + browLeftShadow.setAttribute("cx", String(frame.browLeft.shadowCx)); + browLeftShadow.setAttribute("cy", String(frame.browLeft.shadowCy)); + browLeftShadow.setAttribute("opacity", String(frame.browLeft.shadowOpacity)); + } + const browRightShadow = refs["brow-right-shadow"] as SVGEllipseElement | null; + if (browRightShadow) { + browRightShadow.setAttribute("cx", String(frame.browRight.shadowCx)); + browRightShadow.setAttribute("cy", String(frame.browRight.shadowCy)); + browRightShadow.setAttribute("opacity", String(frame.browRight.shadowOpacity)); + } + + const glabella = refs.glabella as SVGPathElement | null; + if (glabella) { + glabella.setAttribute("d", frame.glabella.paths.join(" ")); + glabella.setAttribute("opacity", String(frame.glabella.opacity)); + if (frame.glabella.strokeWidth !== undefined) glabella.setAttribute("stroke-width", String(frame.glabella.strokeWidth)); + } + const foreheadInner = refs["forehead-inner"] as SVGPathElement | null; + if (foreheadInner) { + foreheadInner.setAttribute("d", frame.foreheadInner.paths.join(" ")); + foreheadInner.setAttribute("opacity", String(frame.foreheadInner.opacity)); + } + const foreheadWhole = refs["forehead-whole"] as SVGPathElement | null; + if (foreheadWhole) { + foreheadWhole.setAttribute("d", frame.foreheadWhole.paths.join(" ")); + foreheadWhole.setAttribute("opacity", String(frame.foreheadWhole.opacity)); + } + const nasolabial = refs.nasolabial as SVGPathElement | null; + if (nasolabial) { + nasolabial.setAttribute("d", frame.nasolabial.paths.join(" ")); + nasolabial.setAttribute("opacity", String(frame.nasolabial.opacity)); + } + const chinCrease = refs["chin-crease"] as SVGPathElement | null; + if (chinCrease) { + chinCrease.setAttribute("d", frame.chinCrease.paths.join(" ")); + chinCrease.setAttribute("opacity", String(frame.chinCrease.opacity)); + if (frame.chinCrease.strokeWidth !== undefined) chinCrease.setAttribute("stroke-width", String(frame.chinCrease.strokeWidth)); + } + + applyJawWarpLayer(refs, "jaw-head", frame.jawWarp.head); + applyJawWarpLayer(refs, "jaw-face", frame.jawWarp.faceDetail); + applyHeadImageSplit(refs, frame.jawWarp.head?.negligible ?? true); + applyFaceDetailSplit(refs, frame.jawWarp.faceDetail?.negligible ?? true); + + /* R10(§8.4 개정): 얼굴 그룹 clip 다각형·창백 마스크 다각형도 하관 띠 변위를 + 따라간다(negligible이면 frame이 이미 원본과 같은 값을 낸다). */ + const faceOvalClip = refs["face-oval-clip"] as SVGPolygonElement | null; + if (faceOvalClip) faceOvalClip.setAttribute("points", frame.faceOvalWarpedPointsAttr); + const pallorMaskOval = refs["pallor-mask-oval"] as SVGPolygonElement | null; + if (pallorMaskOval) pallorMaskOval.setAttribute("points", frame.pallorMaskWarpedPointsAttr); + + const cheekPuffLeft = refs["cheek-puff-left"] as SVGEllipseElement | null; + const cheekPuffLeftHi = refs["cheek-puff-left-highlight"] as SVGEllipseElement | null; + if (cheekPuffLeft) { + cheekPuffLeft.setAttribute("cx", String(frame.mouth.cheekPuffLeft.cx)); + cheekPuffLeft.setAttribute("cy", String(frame.mouth.cheekPuffLeft.cy)); + cheekPuffLeft.setAttribute("opacity", String(frame.mouth.cheekPuffLeft.opacity)); + } + if (cheekPuffLeftHi) { + cheekPuffLeftHi.setAttribute("cx", String(frame.mouth.cheekPuffLeft.highlightCx)); + cheekPuffLeftHi.setAttribute("cy", String(frame.mouth.cheekPuffLeft.highlightCy)); + cheekPuffLeftHi.setAttribute("opacity", String(frame.mouth.cheekPuffLeft.opacity)); + } + const cheekPuffRight = refs["cheek-puff-right"] as SVGEllipseElement | null; + const cheekPuffRightHi = refs["cheek-puff-right-highlight"] as SVGEllipseElement | null; + if (cheekPuffRight) { + cheekPuffRight.setAttribute("cx", String(frame.mouth.cheekPuffRight.cx)); + cheekPuffRight.setAttribute("cy", String(frame.mouth.cheekPuffRight.cy)); + cheekPuffRight.setAttribute("opacity", String(frame.mouth.cheekPuffRight.opacity)); + } + if (cheekPuffRightHi) { + cheekPuffRightHi.setAttribute("cx", String(frame.mouth.cheekPuffRight.highlightCx)); + cheekPuffRightHi.setAttribute("cy", String(frame.mouth.cheekPuffRight.highlightCy)); + cheekPuffRightHi.setAttribute("opacity", String(frame.mouth.cheekPuffRight.opacity)); + } + + /* R8(2026-10-01): 입 모양은 lipTexture 스프라이트 알파가 정한다 — 띠는 세로 아핀 + 변환만 받고, upperD·lowerD로 만드는 벡터 clip은 없다(띠별 정적 사각형 clip만 + 남는다, JSX의 stripClipId). */ + /* 아랫입술 아래 그늘 조각(§8.4, rig.lipTexture.shadow): 턱 변위만 세로로 받는다 + (가로 배율 없음). shadow가 없는 리그는 ref가 null이라 그냥 건너뛴다. */ + const lipShadow = refs["lip-shadow"] as SVGImageElement | null; + if (lipShadow) lipShadow.setAttribute("transform", `translate(0 ${frame.lipShadowTranslateY})`); + + for (let i = 0; i < LIP_STRIP_COUNT; i++) { + const upperStrip = refs[`lip-upper-strip-${i}`] as SVGImageElement | null; + const um = frame.mouth.lipStripsUpper[i]; + if (upperStrip && um) upperStrip.setAttribute("transform", `matrix(${um.a} 0 0 ${um.d} ${um.e} ${um.f})`); + const lowerStrip = refs[`lip-lower-strip-${i}`] as SVGImageElement | null; + const lm = frame.mouth.lipStripsLower[i]; + if (lowerStrip && lm) lowerStrip.setAttribute("transform", `matrix(${lm.a} 0 0 ${lm.d} ${lm.e} ${lm.f})`); + } + const mouthLine = refs["mouth-line"] as SVGPathElement | null; + if (mouthLine) { + mouthLine.setAttribute("d", frame.mouth.lineD); + mouthLine.setAttribute("opacity", String(frame.mouth.lineOpacity)); + } + /* rig.lipTexture가 없는 리그 전용(R8 이전 벡터 입술 경로) — 엘리먼트가 DOM에 + 없으면(lipTexture 있는 리그) 아래 ref는 모두 null이라 그냥 건너뛴다. */ + const fallback = frame.mouth.vectorLipFallback; + if (fallback) { + const mouthUpper = refs["mouth-upper"] as SVGPathElement | null; + if (mouthUpper) mouthUpper.setAttribute("d", fallback.upperD); + const mouthLower = refs["mouth-lower"] as SVGPathElement | null; + if (mouthLower) mouthLower.setAttribute("d", fallback.lowerD); + const mouthCornerStrokes = refs["mouth-corner-strokes"] as SVGPathElement | null; + if (mouthCornerStrokes) mouthCornerStrokes.setAttribute("d", fallback.cornerStrokesD); + const mouthLineSecondary = refs["mouth-line-secondary"] as SVGPathElement | null; + if (mouthLineSecondary) { + mouthLineSecondary.setAttribute("d", fallback.lineSecondaryD); + mouthLineSecondary.setAttribute("opacity", String(fallback.lineSecondaryOpacity)); + } + const lowerShadowCrescent = refs["mouth-lower-shadow-crescent"] as SVGPathElement | null; + if (lowerShadowCrescent) lowerShadowCrescent.setAttribute("d", fallback.lowerShadowCrescentD); + } + const mouthInner = refs["mouth-inner"] as SVGPathElement | null; + if (mouthInner) { + mouthInner.setAttribute("d", frame.mouth.innerD); + mouthInner.setAttribute("opacity", String(frame.mouth.innerOpacity)); + } + const mouthInnerClip = refs["mouth-inner-clip-d"] as SVGPathElement | null; + if (mouthInnerClip) mouthInnerClip.setAttribute("d", frame.mouth.innerD); + const teeth = refs.teeth as SVGPathElement | null; + if (teeth) { + teeth.setAttribute("d", frame.mouth.teethD); + teeth.setAttribute("fill", frame.mouth.teethFillColor); + } + const teethShadow = refs["teeth-shadow"] as SVGPathElement | null; + if (teethShadow) teethShadow.setAttribute("d", frame.mouth.teethShadowD); + const tongueShadow = refs["tongue-shadow"] as SVGPathElement | null; + if (tongueShadow) { + tongueShadow.setAttribute("d", frame.mouth.tongueShadowD); + tongueShadow.setAttribute("opacity", String(frame.mouth.tongueShadowOpacity)); + } + + const cheekLeft = refs["cheek-left"]; + if (cheekLeft) cheekLeft.setAttribute("opacity", String(frame.cheekLeftOpacity)); + const cheekRight = refs["cheek-right"]; + if (cheekRight) cheekRight.setAttribute("opacity", String(frame.cheekRightOpacity)); + + const pallorColor = refs["pallor-color"]; + if (pallorColor) pallorColor.setAttribute("opacity", String(frame.pallorColorOpacity)); + const pallorWhite = refs["pallor-white"]; + if (pallorWhite) pallorWhite.setAttribute("opacity", String(frame.pallorWhiteOpacity)); + + const sweatGroup = refs.sweat; + if (sweatGroup) sweatGroup.setAttribute("opacity", String(frame.sweatOpacity)); + frame.sweatDrops.forEach((drop, i) => { + const el = refs[`sweat-${i}`] as SVGEllipseElement | null; + if (el) { + el.setAttribute("cx", String(drop.cx)); + el.setAttribute("cy", String(drop.cy)); + el.setAttribute("rx", String(drop.r)); + el.setAttribute("ry", String(drop.r * 1.3)); + } + const highlight = refs[`sweat-${i}-highlight`] as SVGEllipseElement | null; + if (highlight) { + highlight.setAttribute("cx", String(drop.cx - drop.r * 0.3)); + highlight.setAttribute("cy", String(drop.cy - drop.r * 0.35)); + highlight.setAttribute("rx", String(drop.r * 0.32)); + highlight.setAttribute("ry", String(drop.r * 0.4)); + } + }); +} + +export default function LinocutAvatar({ + rig, + engine, + running, + nowMs, + crop = "bust", + backdropExpression = "neutral", + texture = true, + label = "내담자 아바타", + className, + onLoadStateChange, +}: LinocutAvatarProps) { + const uid = useId(); + const refs = useRef({}); + const rafRef = useRef(0); + const backdropRef = useRef(null); + const svgRef = useRef(null); + /* 작은 크기 선 보정(§8.4 2단계-B-1d): 크기가 바뀔 때만(ResizeObserver, 프레임마다 + 아님) k = 렌더 폭 / viewBox 폭을 재고 s = clamp(0.3/k, 1, 2)를 기하 계산에 넘긴다. + rAF에서 매 프레임 읽지만 React state로 두면 리렌더가 생기므로 ref에 둔다. */ + const sizeScaleRef = useRef(1); + + const group = groupFor(backdropExpression); + + const expectedHrefs = useMemo(() => { + const hrefs = [rig.layers.body.href, rig.layers.head.href, rig.layers.hairFront.href]; + if (rig.layers.faceDetail) hrefs.push(rig.layers.faceDetail.href); + if (texture && rig.grain) hrefs.push(rig.grain.href); + if (rig.lipTexture) hrefs.push(rig.lipTexture.upper.href, rig.lipTexture.lower.href); + if (rig.lipTexture?.shadow) hrefs.push(rig.lipTexture.shadow.href); + if (rig.jaw) hrefs.push(rig.jaw.head.href, rig.jaw.detail.href); + return hrefs; + }, [rig, texture]); + + const [loadState, setLoadState] = useState("loading"); + + /* 이 환경에서 SVG 의 load·error 이벤트가 발동하지 않아(원인 미확인, + 2단계-B-1b 관찰) 같은 href를 HTMLImageElement로 미리 불러와 로드 상태를 + 집계한다. 화면 표시용 는 정상 로드·렌더되며, 이 프리로드는 로드 상태 + 추적 전용이다(§8.4 로드 실패 규칙은 그대로 지킨다). */ + useEffect(() => { + let cancelled = false; + const loaded = new Set(); + const errored = new Set(); + setLoadState("loading"); + onLoadStateChange?.("loading"); + + expectedHrefs.forEach((href) => { + const probe = new window.Image(); + probe.onload = () => { + if (cancelled) return; + loaded.add(href); + if (errored.size === 0 && loaded.size >= expectedHrefs.length) { + setLoadState("ready"); + onLoadStateChange?.("ready"); + } + }; + probe.onerror = () => { + if (cancelled) return; + errored.add(href); + setLoadState("error"); + onLoadStateChange?.("error"); + }; + probe.src = href; + }); + + return () => { + cancelled = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [expectedHrefs]); + + useEffect(() => { + if (backdropRef.current) backdropRef.current.style.fill = rig.backdrop[group]; + }, [group, rig]); + + useEffect(() => { + const el = svgRef.current; + if (!el) return; + const viewBoxWidth = rig.crops[crop][2]; + const update = (widthPx: number) => { + if (widthPx <= 0) return; + const k = widthPx / viewBoxWidth; + sizeScaleRef.current = Math.min(2, Math.max(1, 0.3 / k)); + }; + update(el.getBoundingClientRect().width); + const observer = new ResizeObserver((entries) => { + for (const entry of entries) { + const w = entry.contentBoxSize?.[0]?.inlineSize ?? entry.contentRect.width; + update(w); + } + }); + observer.observe(el); + return () => observer.disconnect(); + }, [rig, crop]); + + useEffect(() => { + if (!running) return; + const clock = nowMs ?? (() => performance.now()); + const loop = () => { + const v = engine.evaluate(clock()); + const frame = computeLinocutFrame(rig, v, sizeScaleRef.current); + applyFrame(refs.current, frame); + rafRef.current = requestAnimationFrame(loop); + }; + rafRef.current = requestAnimationFrame(loop); + return () => cancelAnimationFrame(rafRef.current); + }, [engine, running, nowMs, rig]); + + const viewBox = rig.crops[crop].join(" "); + const faceClipId = `${uid}-face-clip`; + const eyeLeftClipId = `${uid}-eye-left-clip`; + const eyeRightClipId = `${uid}-eye-right-clip`; + const mouthInnerClipId = `${uid}-mouth-inner-clip`; + const inkFilterId = `${uid}-ink-filter`; + const blushPatternId = `${uid}-blush-pattern`; + const blushMaskId = `${uid}-blush-mask`; + const grainPatternId = `${uid}-grain-pattern`; + const pallorBlurId = `${uid}-pallor-blur`; + const pallorMaskId = `${uid}-pallor-mask`; + const lipShadowPatternId = `${uid}-lip-shadow-pattern`; + const browShadowBlurId = `${uid}-brow-shadow-blur`; + const cheekPuffBlurId = `${uid}-cheek-puff-blur`; + /* R10(§8.4 개정, out4·out5 반려 대응): 원본 faceDetail을 "변형 영역(y > y_n)을 + 빼고" 그릴 때 쓰는 clip — y=0..y_n의 위쪽 절반만 남긴다(face-oval clip과 + 중첩해 교집합으로 쓴다, applyFaceDetailSplit). */ + const faceDetailUpperClipId = `${uid}-face-detail-upper-clip`; + /* R13(§8.4 개정, out9 jaw-zoom 반려 대응): 원본 head-image에서 띠가 덮는 영역만 + evenodd로 뚫어낸 clip(applyHeadImageSplit). */ + const headJawHoleClipId = `${uid}-head-jaw-hole-clip`; + + const cheekLeftPos = useMemo(() => { + const c = rig.landmarks.eyeLeft.iris.center; + return { cx: c[0], cy: c[1] + 85, rx: 55, ry: 32 }; + }, [rig]); + const cheekRightPos = useMemo(() => { + const c = rig.landmarks.eyeRight.iris.center; + return { cx: c[0], cy: c[1] + 85, rx: 55, ry: 32 }; + }, [rig]); + + const pal = rig.palette; + const tongueColor = useMemo(() => lightenHex(pal.mouthInner, 0.35), [pal.mouthInner]); + + /* 하관 띠 변형(§8.4): 정적 사각형(채널과 무관, lipStripClipRect와 같은 역할)은 + 마운트 시 한 번만 계산한다. 매 프레임 세로 변환만 rAF에서 ref로 쓴다. R10: 띠의 + 원본은 rig.jaw의 잘라 낸 조각이다 — 없는 리그는 기존처럼 전체 head·faceDetail을 + 쓴다(대체 경로). */ + const jawHeadImage = rig.jaw?.head ?? rig.layers.head; + const jawFaceDetailImage = rig.jaw?.detail ?? rig.layers.faceDetail; + const jawHeadRects = useMemo(() => jawWarpStripRects(rig, jawHeadImage), [rig, jawHeadImage]); + const jawFaceRects = useMemo( + () => (jawFaceDetailImage ? jawWarpStripRects(rig, jawFaceDetailImage) : null), + [rig, jawFaceDetailImage], + ); + /* R11(§8.4 개정, out6 jaw-zoom 반려 대응): 띠마다 clip = 띠 사각형 ∩ 확장 얼굴 + 윤곽(10px)을 띠의 로컬(변환 전) 좌표로 미리 계산한다. 이 다각형을 각 띠의 + (자신의 transform을 받는 요소) 자체의 clip으로 걸어야 경계가 내용과 + 함께 움직인다 — 바깥 그룹에 정적 clip을 걸면(out6) 화면에 고정된 경계가 + 내려간 턱선을 잘라 점선처럼 보였다. */ + const jawHeadStripClips = useMemo( + () => jawWarpStripClipPolygons(rig, jawHeadImage, jawHeadRects ?? []), + [rig, jawHeadImage, jawHeadRects], + ); + const jawFaceStripClips = useMemo( + () => (jawFaceDetailImage && jawFaceRects ? jawWarpStripClipPolygons(rig, jawFaceDetailImage, jawFaceRects) : null), + [rig, jawFaceDetailImage, jawFaceRects], + ); + const jawUpperBoundaryY = useMemo(() => jawWarpUpperBoundaryY(rig), [rig]); + /* R13(§8.4 개정, out9 jaw-zoom 반려 대응): head-image(원본, rig.layers.head)에서 + 띠가 덮는 영역만 구멍을 뚫는다. 구멍 범위는 jawHeadRects(띠 원본인 jawHeadImage + 기준, 캔버스 좌표 공유라 head-image에도 그대로 쓸 수 있다)를 넘겨 주지만, + jawWarpHeadHoleClipD가 위쪽 경계를 입 중심(mouthCenter.y)으로 다시 내려 + 잡는다(R14, out10 hair-edge 반려 대응) — 코~인중(f(y) 음수 구간)은 원본을 + 남겨 볼 가장자리 흰 틈을 막는다. */ + const headJawHoleD = useMemo(() => jawWarpHeadHoleClipD(rig, jawHeadRects ?? []), [rig, jawHeadRects]); + + return ( + + + {/* R10: points는 초기값만 정적이고, 하관 띠 변위 f(y)를 rAF에서 이 polygon에 + 직접 쓴다(applyFrame → face-oval-clip, negligible이면 원본과 같다). */} + + + + {/* R10(§8.4 개정): 원본 faceDetail을 "변형 영역을 빼고" 그릴 때 쓰는 위쪽 + 절반 clip(y=0..y_n). faceClipId와 중첩해 교집합으로 쓴다. */} + + + + {/* R13(§8.4 개정, out9 jaw-zoom 반려 대응): 원본 head-image에서 띠가 덮는 + 영역(확장 얼굴 윤곽 ∩ 띠 y-범위)만 구멍 낸 clip. 캔버스 전체 사각형과 + 구멍 다각형을 evenodd 한 path에 함께 넣어야 "사각형 − 구멍"이 된다 + (다각형 두 개를 따로 넣으면 합집합이 되어 구멍이 안 뚫린다). */} + {headJawHoleD ? ( + + + + ) : null} + + + + + + + + + + + + + + {/* 안쪽 올림 그늘(§8.4): 눈썹 머리 위 흐린 잉크 그늘 전용 가우시안 흐림. */} + + + + {/* 볼 불룩(§8.4 2026-10-01 추가): 입꼬리 옆 흐린 잉크 타원 전용 가우시안 흐림. */} + + + + + + + + + + + + + + + + + + + + + + {rig.grain && texture ? ( + + + + ) : null} + {/* 창백 마스크: 얼굴 윤곽을 4% 줄인 다각형을 가우시안 흐림(표준편차 16)해 경계가 + 칼같이 떨어지지 않게 한다(2단계-B-1b 1차 반려 대응). */} + + + + + {/* R10: points 초기값만 정적, 하관 띠 변위는 rAF에서 이 polygon에 직접 쓴다 + (applyFrame → pallor-mask-oval). */} + + + + + + + + + + + + + {/* R13(§8.4 개정, out9 jaw-zoom 반려 대응): 원본 head-image를 clip 없이 + 그대로 깔아 두면, 하관 띠가 켜진 동안 원본 턱선(제자리)과 띠가 옮겨 + 그린 턱선(내려간 자리)이 겹쳐 턱선이 두 겹으로 보인다(faceDetail이 + R10에서 이미 겪은 문제와 같다 — head만 그 조치가 빠져 있었다). head는 + 얼굴 윤곽 밖(귀·머리카락·목)도 담고 있어 faceDetail처럼 "y>y_n 전체"를 + 빼면 그 부분까지 잘려 나가므로, 띠가 실제로 덮는 영역(확장 얼굴 윤곽 ∩ + 띠 y-범위, jawWarpHeadHoleClipD)만 구멍을 뚫은 사본(head-image-masked)을 + 따로 둔다. applyHeadImageSplit이 head 띠의 negligible로 두 장 중 하나만 + 보인다(쉴 때는 -full, 띠가 켜지면 -masked + 아래 jaw-warp-head 띠). */} + {headJawHoleD ? ( + + + + ) : null} + {/* 하관 띠 변형(§8.4 2026-10-01 추가, R11 개정) — head 레이어. 각 띠의 + clip = 띠 사각형 ∩ 확장 윤곽(10px, 로컬/변환 전 좌표) — 확장 윤곽 밖 + (머리카락·귀·목)은 이 clip으로 그대로 가려져 움직이지 않는다. clip을 + 띠 자신(transform을 받는 )에 걸어야 경계가 내용과 함께 움직인다 + (R11: 바깥 그룹에 정적 clip을 걸면 화면에 고정된 경계가 내려간 턱선을 + 경계마다 잘라 점선처럼 보였다 — out6 반려 사유). 띠는 위 head-image-masked + 구멍 위에 겹쳐 그려 그 자리를 대체한다. 띠 원본은 jawHeadImage(rig.jaw.head + 조각, 없으면 전체 head)이고, 교집합이 없는 띠는 그리지 않는다. */} + {jawHeadRects ? ( + + {jawHeadStripClips.map((clipPoints, i) => { + if (!clipPoints) return null; + const stripClipId = `${uid}-jaw-head-strip-clip-${i}`; + return ( + + + + + + + ); + })} + + ) : null} + {/* R9: face의 clip을 3단(좁게→넓게→좁게)으로 나눈다. faceDetail 원본과 + 벡터 부위(눈·눈썹·입 등)는 그대로 얼굴 윤곽에 딱 맞게 clip하고, 그 + 사이(하관 띠 변형)만 띠별 확장 clip(R11, 10px)을 쓴다 — 원화 턱선 + 잉크가 좁은 윤곽 한가운데를 지나 입을 벌리면 턱선이 반만 내려가 + 점선처럼 끊기던 문제(§8.4 R9)를 고친다. transform은 face 자신이 + 그대로 받는다. */} + + {/* R10(§8.4 개정, out4·out5 반려 대응): faceDetail은 대부분 투명한 + 덧칠 레이어라, 아래 jaw-warp-face 띠가 켜진 동안 원본 전체를 같이 + 그리면 변형된 사본과 겹쳐 턱선이 두 겹(점선)으로 보인다. 원본 전체 + (face-detail-full)와 변형 영역(y>y_n)을 뺀 사본(face-detail-upper)을 + 모두 그려 두고, applyFaceDetailSplit이 negligible 여부로 opacity를 + 토글한다(쉴 때는 -full만 보임, 띠가 켜지면 -upper만 보임). */} + + {rig.layers.faceDetail ? ( + <> + + + + + + ) : null} + + {/* 하관 띠 변형(§8.4, R11 개정) — faceDetail 레이어. head와 같은 방식으로 + 각 띠의 clip = 띠 사각형 ∩ 확장 윤곽(10px, 로컬 좌표)을 띠 자신에 + 건다(R11: 바깥 그룹 정적 clip은 out6에서 점선 결함을 냈다). 띠 원본은 + jawFaceDetailImage(rig.jaw.detail 조각, 없으면 전체 faceDetail), + 교집합이 없는 띠는 그리지 않는다. */} + {rig.layers.faceDetail && jawFaceRects && jawFaceStripClips ? ( + + {jawFaceStripClips.map((clipPoints, i) => { + if (!clipPoints) return null; + const stripClipId = `${uid}-jaw-face-strip-clip-${i}`; + return ( + + + + + + + ); + })} + + ) : null} + + + + + + + + + + + + + + {/* 볼 불룩(§8.4 2026-10-01 추가): 입꼬리마다 바깥 흐린 잉크 타원 + 종이색 + 하이라이트. b = max(mouthPress, mouthRound) — ㅁ·ㅂ·ㅗ·ㅜ에서 볼록해진다. */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* R8(2026-10-01, §8.4 입 "질감" 재개정): lipTexture가 있으면 입 모양은 + 스프라이트 알파가 정한다(원칙: 중립은 원화와 픽셀 단위로 같고, 움직이면 + 원화 픽셀이 변형된다) — 벡터 단색 채움·그늘 초승달·입꼬리 쐐기를 그리지 + 않는다. 입 안(틈)·윗니·혀 그늘은 스프라이트 "아래"에 그려 벌어진 입에서 + 스프라이트의 투명한 부분(치아·구강) 사이로 비친다. lipTexture가 없는 + 리그는 예전 벡터 경로(vectorLipFallback)를 그대로 쓴다. */} + {!rig.lipTexture ? ( + + ) : null} + {!rig.lipTexture ? ( + + ) : null} + {!rig.lipTexture ? ( + + ) : null} + + + + {/* R9: 윗니 위 35% 잉크 그늘(불투명도 0.35 고정). */} + + + + {/* 아랫입술 아래 그늘 조각(§8.4 입 "질감" 개정, rig.lipTexture.shadow): + 원화 픽셀 그대로, 아랫입술 스프라이트 "아래"·입 안보다 "위" 순서로 + 그린다. 턱 변위 f(그늘 중심 y)만 세로로 받고 가로로는 줄지 않는다 + (out5 판정: 아랫입술 스프라이트에 붙여 두자 ㅗ에서 입 폭과 함께 + 압축되어 짙은 직사각형이 됐다 — 턱 피부 그늘은 입이 좁아져도 줄지 + 않는다). shadow가 없으면 그리지 않는다. */} + {rig.lipTexture?.shadow ? ( + + ) : null} + + {/* 입술 결 띠 변형(§8.4 2단계-B-1d-R7, R8 개정): 스프라이트를 세로 띠 + LIP_STRIP_COUNT(24)개로 나눠 띠마다 독립 matrix로 입 선 곡률을 + 따라간다. 띠 사각형 clip(스프라이트 폭 기준 등분, 정적)만 남고, + R8부터는 입술 모양으로 한 번 더 clip하지 않는다 — 스프라이트 자신의 + 알파가 입술 바깥 경계다. */} + {rig.lipTexture + ? Array.from({ length: LIP_STRIP_COUNT }, (_, i) => { + const rect = lipStripClipRect(rig.lipTexture!.upper, i); + const stripClipId = `${uid}-lip-upper-strip-clip-${i}`; + return ( + + + + + + + ); + }) + : null} + {rig.lipTexture + ? Array.from({ length: LIP_STRIP_COUNT }, (_, i) => { + const rect = lipStripClipRect(rig.lipTexture!.lower, i); + const stripClipId = `${uid}-lip-lower-strip-clip-${i}`; + return ( + + + + + + + ); + }) + : null} + + {/* 입 선(§8.4 R8): lipTexture가 있으면 작은 화면 보조일 뿐이라 opacity를 + 매 프레임 sizeScale로 조절한다(face 300px에서 0). 없으면 예전대로 + 0.95 고정(applyFrame이 매 프레임 같은 값을 다시 쓴다). */} + + {!rig.lipTexture ? ( + + ) : null} + {!rig.lipTexture ? ( + + ) : null} + + + + + + + + + + + + + + + + + + + + {rig.grain && texture ? ( + + ) : null} + + ); +} diff --git a/apps/web/src/components/avatar/v3/LinocutMotif.tsx b/apps/web/src/components/avatar/v3/LinocutMotif.tsx new file mode 100644 index 0000000..14b562b --- /dev/null +++ b/apps/web/src/components/avatar/v3/LinocutMotif.tsx @@ -0,0 +1,100 @@ +/* ===================================================================== + LinocutMotif — 결정문 §8.2 상징 모티프층·§8.4 배경 색면·모티프(2단계-B-1b + 판정으로 벡터→래스터 스프라이트 전환). 아바타 옆 작은 층으로 둔다. + immersive는 개방도를 반영하지 않는다(budOpenness=null). 정적 SVG + CSS + 크로스페이드/keyframes로 구성해 React state를 프레임마다 갱신하지 않는다. + ===================================================================== */ + +import type { BackdropGroup, LinocutRig } from "./linocutRig"; +import "./linocut-motif.css"; + +export type MotifFeedbackMode = "immersive" | "ambient" | "coached"; + +export interface LinocutMotifProps { + rig: LinocutRig; + group: BackdropGroup; + /** null이면 immersive 고정(0.25). ambient·coached는 실제 개방도 값. */ + budOpenness: number | null; + /** 고개 숙임 조건이 모드마다 달라 필요하다. */ + feedbackMode: MotifFeedbackMode; + reducedMotion: boolean; + /** px 너비. 기본 96. */ + size?: number; + className?: string; +} + +const WEATHER_GROUPS: readonly BackdropGroup[] = ["positive", "negative", "defensive", "cognitive", "energy"]; + +/** 개방도 o(0..1)를 닫힘·반쯤·활짝 가중치로 섞는다. */ +function budWeights(o: number): { closed: number; half: number; open: number } { + if (o <= 0.5) return { closed: 1 - 2 * o, half: 2 * o, open: 0 }; + return { closed: 0, half: 2 - 2 * o, open: 2 * o - 1 }; +} + +export default function LinocutMotif({ + rig, + group, + budOpenness, + feedbackMode, + reducedMotion, + size = 96, + className, +}: LinocutMotifProps) { + const motif = rig.motif; + const openness = budOpenness ?? 0.25; + const budAttr = budOpenness === null ? "static" : openness.toFixed(2); + + /* immersive: negative면 숙인다. ambient·coached: negative이면서 개방도<0.35일 때만 + 숙인다(우울한 P1은 negative가 잦아, 조건 없이 숙이면 개방도 신호가 가려진다). */ + const droop = feedbackMode === "immersive" ? group === "negative" : group === "negative" && openness < 0.35; + const pose: "upright" | "droop" = droop ? "droop" : "upright"; + + if (!motif) return null; + + const weights = budWeights(openness); + const weatherCanvas = motif.weather.canvas; + const budCanvas = motif.bud.canvas; + const totalW = Math.max(weatherCanvas.w, budCanvas.w); + const totalH = weatherCanvas.h + budCanvas.h; + const budX = (totalW - budCanvas.w) / 2; + const weatherX = (totalW - weatherCanvas.w) / 2; + + return ( + + ); +} diff --git a/apps/web/src/components/avatar/v3/debug-face.css b/apps/web/src/components/avatar/v3/debug-face.css new file mode 100644 index 0000000..a34b1a6 --- /dev/null +++ b/apps/web/src/components/avatar/v3/debug-face.css @@ -0,0 +1,83 @@ +/* ===================================================================== + DebugFace 스타일 — 아바타 v3 채널 디버그 리그(아트 품질 아님, 도식용). + ===================================================================== */ + +.df { + width: 100%; + max-width: 320px; + aspect-ratio: 260 / 340; + background: #f4efe6; + border: 1px solid #d8cfbf; + border-radius: 16px; +} + +.df__torso { + fill: #7c8a92; +} + +.df__shoulder { + fill: #6b7a82; +} + +.df__skull { + fill: #f0ddc4; + stroke: #cbb494; + stroke-width: 1; +} + +.df__brow { + fill: none; + stroke: #4a3626; + stroke-width: 4; + stroke-linecap: round; +} + +.df__eye { + fill: #ffffff; + stroke: #4a3626; + stroke-width: 1.5; +} + +.df__iris { + fill: #7a5a3c; +} + +.df__pupil { + fill: #241a12; +} + +.df__mouth { + fill: #a5473f; + stroke: #6c2c26; + stroke-width: 1; +} + +.df__chin { + stroke: #cbb494; + stroke-width: 2; + stroke-linecap: round; +} + +.df__cheek { + fill: #e78a8a; +} + +.df__sweat { + fill: #bfe0f5; + stroke: #8fbede; + stroke-width: 0.5; +} + +.df__tear { + fill: #bfe0f5; + transform-origin: center; +} + +.df__pallor { + fill: #d9d9df; +} + +.df__breath { + fill: #9fb7a8; + transform-origin: center; +} diff --git a/apps/web/src/components/avatar/v3/linocut-avatar.css b/apps/web/src/components/avatar/v3/linocut-avatar.css new file mode 100644 index 0000000..89a3e41 --- /dev/null +++ b/apps/web/src/components/avatar/v3/linocut-avatar.css @@ -0,0 +1,83 @@ +/* ===================================================================== + linocut-avatar.css — LinocutAvatar 렌더러 스타일. 결정문 §8.4. + 배경 전환 1.8s·figure 페이드 0.2s는 reduced motion에서 즉시 전환한다. + ===================================================================== */ + +.linocut-avatar { + display: block; + width: 100%; + height: 100%; +} + +.linocut-avatar__backdrop { + transition: fill 1.8s ease; +} + +.linocut-avatar__figure { + opacity: 0; + transition: opacity 0.2s ease; +} + +.linocut-avatar__figure[data-ready="true"] { + opacity: 1; +} + +@media (prefers-reduced-motion: reduce) { + .linocut-avatar__backdrop { + transition: none; + } + .linocut-avatar__figure { + transition: none; + } +} + +/* 결·주름 등 순수 스트로크 요소 — 불투명도는 채널값에 따라 JS가 직접 쓴다. */ +.linocut-avatar__wrinkle, +.linocut-avatar__crease, +.linocut-avatar__lower-lid, +.linocut-avatar__tear-pool, +.linocut-avatar__iris-radial { + fill: none; + stroke-width: 1.2; + stroke-linecap: round; +} + +/* 이마·팔자 주름 — 두께가 변하는 리본(채운 다각형). 불투명도는 JS가 직접 쓴다. */ +.linocut-avatar__wrinkle-ribbon { + stroke: none; +} + +.linocut-avatar__tear-flow { + stroke-width: 1; + stroke-linejoin: round; +} + +/* 눈썹 몸체 위로 삐져나온 털 획 — 정적 불투명도(채널 무관). */ +.linocut-avatar__brow-hair { + fill: none; + stroke-width: 1.3; + stroke-linecap: round; + opacity: 0.7; +} + +/* 아랫입술 아래 그늘 초승달 해칭(입체감). */ +.linocut-avatar__lip-shadow-crescent { + opacity: 0.3; +} + +/* 벌어졌을 때(g>0.5) 아랫입술 쪽에 보이는 가는 보조 선. */ +.linocut-avatar__mouth-line-secondary { + fill: none; + stroke-width: 1; + stroke-linecap: round; +} + +.linocut-avatar__pallor-color { + mix-blend-mode: color; +} + +.linocut-avatar__grain { + mix-blend-mode: multiply; + opacity: 0.35; + pointer-events: none; +} diff --git a/apps/web/src/components/avatar/v3/linocut-motif.css b/apps/web/src/components/avatar/v3/linocut-motif.css new file mode 100644 index 0000000..2f93d97 --- /dev/null +++ b/apps/web/src/components/avatar/v3/linocut-motif.css @@ -0,0 +1,94 @@ +/* ===================================================================== + linocut-motif.css — LinocutMotif 스타일(2단계-B-1b 판정으로 래스터 + 스프라이트 전환). 날씨 1.5s 크로스페이드 + 약한 움직임(해 회전, 비구름· + 안개 가로 흔들림), 봉오리는 개방도 3장 크로스페이드 + 자세(곧음/숙임) + 순차 페이드. reduced motion(prop 또는 OS 설정)에서는 전부 정지한다. + ===================================================================== */ + +.linocut-motif { + display: block; + overflow: visible; +} + +.linocut-motif__weather-sprite { + opacity: 0; + transition: opacity 1.5s ease; +} + +/* 봉오리 개방도 3장(닫힘·반쯤·활짝) — 가중치 크로스페이드. */ +.linocut-motif__bud-upright image { + transition: opacity 0.3s ease; +} + +/* 자세(곧음/숙임) 순차 페이드: 사라지는 쪽 0.6s, 나타나는 쪽 0.4s부터 0.9s까지. */ +.linocut-motif__bud-upright, +.linocut-motif__bud-droop { + opacity: 0; + transition: opacity 0.6s ease; +} + +.linocut-motif[data-motif-pose="upright"] .linocut-motif__bud-upright, +.linocut-motif[data-motif-pose="droop"] .linocut-motif__bud-droop { + opacity: 1; + transition: opacity 0.5s ease 0.4s; +} + +@keyframes linocut-motif-sun-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.linocut-motif__weather-positive { + transform-box: fill-box; + transform-origin: center; + animation: linocut-motif-sun-spin 60s linear infinite; +} + +@keyframes linocut-motif-weather-sway { + 0%, + 100% { + transform: translateX(-2.5px); + } + 50% { + transform: translateX(2.5px); + } +} + +.linocut-motif__weather-negative { + animation: linocut-motif-weather-sway 7s ease-in-out infinite; +} + +.linocut-motif__weather-defensive { + animation: linocut-motif-weather-sway 8s ease-in-out infinite; +} + +/* energy(초승달·별)·cognitive(옅은 안개결)는 정지. */ + +.linocut-motif[data-reduced-motion="true"] .linocut-motif__weather-sprite, +.linocut-motif[data-reduced-motion="true"] .linocut-motif__bud-upright, +.linocut-motif[data-reduced-motion="true"] .linocut-motif__bud-droop { + transition: none; +} + +.linocut-motif[data-reduced-motion="true"] .linocut-motif__weather-positive, +.linocut-motif[data-reduced-motion="true"] .linocut-motif__weather-negative, +.linocut-motif[data-reduced-motion="true"] .linocut-motif__weather-defensive { + animation: none; +} + +@media (prefers-reduced-motion: reduce) { + .linocut-motif__weather-sprite, + .linocut-motif__bud-upright, + .linocut-motif__bud-droop { + transition: none; + } + .linocut-motif__weather-positive, + .linocut-motif__weather-negative, + .linocut-motif__weather-defensive { + animation: none; + } +} diff --git a/apps/web/src/components/avatar/v3/linocutGeometry.ts b/apps/web/src/components/avatar/v3/linocutGeometry.ts new file mode 100644 index 0000000..4f0b037 --- /dev/null +++ b/apps/web/src/components/avatar/v3/linocutGeometry.ts @@ -0,0 +1,1622 @@ +/* ===================================================================== + 리노컷 렌더러 기하 — 결정문 §8.4 P1 리노컷 렌더러 명세의 정본 구현 + (2단계-B-1b 1차 반려 후 개정본). + React·DOM에 의존하지 않는 순수 함수만 둔다. 입력은 (rig, ChannelVector), + 출력은 각 부위의 path d·transform·불투명도다. 렌더러(LinocutAvatar.tsx)가 + rAF에서 이 함수를 호출해 SVG 속성을 직접 쓴다(DebugFace와 같은 방식). + 좌우 부호는 DebugFace와 같다: 오른쪽 채널의 가로(X) 성분만 부호를 반전한다. + ===================================================================== */ + +import type { ChannelVector } from "../engine/channels"; +import type { LinocutRig, RigBrow, RigEye, RigLayer, RigPoint } from "./linocutRig"; + +type Point = readonly [number, number]; + +/* ── 공용 수학 ─────────────────────────────────────────────────────── */ + +function clamp(v: number, lo = 0, hi = 1): number { + return v < lo ? lo : v > hi ? hi : v; +} + +function lerp(a: number, b: number, t: number): number { + return a + (b - a) * t; +} + +function smoothstep(edge0: number, edge1: number, x: number): number { + const t = clamp((x - edge0) / (edge1 - edge0), 0, 1); + return t * t * (3 - 2 * t); +} + +/** O–I 직선 위 x 지점의 y(직선 보간). */ +function lineY(p0: Point, p1: Point, x: number): number { + if (p1[0] === p0[0]) return (p0[1] + p1[1]) / 2; + const t = (x - p0[0]) / (p1[0] - p0[0]); + return p0[1] + (p1[1] - p0[1]) * t; +} + +/** 세 점 P0·Mid·P2를 지나는 2차 베지어 제어점(t=0.5에서 Mid를 지나도록 역산). */ +function quadControlThrough(p0: Point, mid: Point, p2: Point): Point { + return [2 * mid[0] - 0.5 * (p0[0] + p2[0]), 2 * mid[1] - 0.5 * (p0[1] + p2[1])]; +} + +function quadPointAt(p0: Point, control: Point, p2: Point, t: number): Point { + const mt = 1 - t; + return [ + mt * mt * p0[0] + 2 * mt * t * control[0] + t * t * p2[0], + mt * mt * p0[1] + 2 * mt * t * control[1] + t * t * p2[1], + ]; +} + +function pathQ(p0: Point, control: Point, p2: Point): string { + return `M ${p0[0]} ${p0[1]} Q ${control[0]} ${control[1]} ${p2[0]} ${p2[1]}`; +} + +function sampleQuad(p0: Point, control: Point, p2: Point, tFrom: number, tTo: number, steps: number): Point[] { + const out: Point[] = []; + for (let i = 0; i < steps; i++) { + const t = lerp(tFrom, tTo, steps === 1 ? 0 : i / (steps - 1)); + out.push(quadPointAt(p0, control, p2, t)); + } + return out; +} + +/** 결정적 의사난수(0..1) — "시드 고정" 텍스처 획 배치용. Math.random 미사용. */ +function hash(n: number): number { + const x = Math.sin(n * 12.9898) * 43758.5453; + return x - Math.floor(x); +} + +function toPoint(p: RigPoint): Point { + return [p[0], p[1]]; +} + +/** 16진 색 두 개를 weightA:1-weightA로 섞는다(R9 윗니 색 등). */ +function mixHex(hexA: string, hexB: string, weightA: number): string { + const na = parseInt(hexA.replace("#", ""), 16); + const nb = parseInt(hexB.replace("#", ""), 16); + const ra = (na >> 16) & 0xff, ga = (na >> 8) & 0xff, ba = na & 0xff; + const rb = (nb >> 16) & 0xff, gb = (nb >> 8) & 0xff, bb = nb & 0xff; + const mix = (a: number, b: number) => Math.round(a * weightA + b * (1 - weightA)); + return `rgb(${mix(ra, rb)}, ${mix(ga, gb)}, ${mix(ba, bb)})`; +} + +/** p0→p1 진행 방향의 왼쪽 법선(부호는 방향에 종속, 리본 두께 방향에만 쓴다). */ +function normal(p0: Point, p1: Point): Point { + const dx = p1[0] - p0[0]; + const dy = p1[1] - p0[1]; + const len = Math.hypot(dx, dy) || 1; + return [-dy / len, dx / len]; +} + +/* ── Catmull-Rom(클램프) → 3차 베지어 ───────────────────────────────── + 눈썹·입술 바깥선 등 "곡선(꺾인 직선 금지)" 요구를 만족하는 공용 도구다. + 양끝은 이웃 점을 복제해(clamped) 자연스러운 끝 접선을 만든다. */ + +type CubicSeg = readonly [Point, Point, Point, Point]; + +function catmullRomSegments(points: Point[]): CubicSeg[] { + const n = points.length; + const get = (i: number): Point => points[Math.min(n - 1, Math.max(0, i))]; + const segs: CubicSeg[] = []; + for (let i = 0; i < n - 1; i++) { + const p0 = get(i - 1); + const p1 = get(i); + const p2 = get(i + 1); + const p3 = get(i + 2); + const c1: Point = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6]; + const c2: Point = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6]; + segs.push([p1, c1, c2, p2]); + } + return segs; +} + +function cubicPointAt(p0: Point, c1: Point, c2: Point, p3: Point, t: number): Point { + const mt = 1 - t; + const a = mt * mt * mt; + const b = 3 * mt * mt * t; + const c = 3 * mt * t * t; + const d = t * t * t; + return [ + a * p0[0] + b * c1[0] + c * c2[0] + d * p3[0], + a * p0[1] + b * c1[1] + c * c2[1] + d * p3[1], + ]; +} + +/** 선두 M 없이 이어붙일 수 있는 "C ..." 명령만. */ +function catmullRomCommands(points: Point[]): string { + return catmullRomSegments(points) + .map(([, c1, c2, p3]) => `C ${c1[0]} ${c1[1]} ${c2[0]} ${c2[1]} ${p3[0]} ${p3[1]}`) + .join(" "); +} + +function catmullRomPathD(points: Point[]): string { + return `M ${points[0][0]} ${points[0][1]} ${catmullRomCommands(points)}`; +} + +/** 전역 매개변수 t(0..1)를 함께 내는 표본. 리본 반두께 보간에 쓴다. */ +function sampleCatmullRomWithT(points: Point[], samplesPerSeg: number): Array<{ point: Point; t: number }> { + const segs = catmullRomSegments(points); + const segCount = segs.length; + const out: Array<{ point: Point; t: number }> = []; + segs.forEach(([p0, c1, c2, p3], segIndex) => { + const start = segIndex === 0 ? 0 : 1; + for (let i = start; i <= samplesPerSeg; i++) { + const localT = i / samplesPerSeg; + out.push({ point: cubicPointAt(p0, c1, c2, p3, localT), t: (segIndex + localT) / segCount }); + } + }); + return out; +} + +/** t에서의 구간 선형 보간값(정지점 배열 [t, value]). 리본 반두께·주름 두께에 쓴다. */ +function valueAtStops(t: number, stops: Array): number { + for (let i = 0; i < stops.length - 1; i++) { + const [t0, v0] = stops[i]; + const [t1, v1] = stops[i + 1]; + if (t <= t1 || i === stops.length - 2) { + const localT = t1 === t0 ? 0 : clamp((t - t0) / (t1 - t0)); + return lerp(v0, v1, localT); + } + } + return stops[stops.length - 1][1]; +} + +/** 곡선(3점 이상)을 표본한 뒤 정지점 기반 반두께를 붙인 리본용 데이터. */ +function curveRibbonData(anchors: Point[], stops: Array, samplesPerSeg: number) { + const withT = sampleCatmullRomWithT(anchors, samplesPerSeg); + return { samples: withT.map((s) => s.point), halfWidths: withT.map((s) => valueAtStops(s.t, stops)) }; +} + +/** 정점별 반두께로 폴리라인(또는 곡선 표본)을 채운 리본(다각형 path)으로 만든다. */ +function polylineRibbon(points: Point[], halfWidths: number[]): string { + const n = points.length; + const normals: Point[] = points.map((p, i) => { + const a = i > 0 ? normal(points[i - 1], p) : normal(p, points[i + 1]); + const b = i < n - 1 ? normal(p, points[i + 1]) : normal(points[i - 1], p); + return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]; + }); + const top = points.map((p, i) => [p[0] + normals[i][0] * halfWidths[i], p[1] + normals[i][1] * halfWidths[i]] as Point); + const bot = points.map((p, i) => [p[0] - normals[i][0] * halfWidths[i], p[1] - normals[i][1] * halfWidths[i]] as Point); + const topStr = top.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`).join(" "); + const botStr = [...bot].reverse().map((p) => `L ${p[0]} ${p[1]}`).join(" "); + return `${topStr} ${botStr} Z`; +} + +/** 폴리라인 위 매개변수 t(0..1, 구간마다 등분)의 점·법선·보간값. */ +function alongPolyline(points: Point[], values: number[] | null, t: number): { point: Point; normal: Point; value: number } { + const segCount = points.length - 1; + const scaled = clamp(t, 0, 1) * segCount; + const segIndex = Math.min(segCount - 1, Math.floor(scaled)); + const localT = scaled - segIndex; + const p0 = points[segIndex]; + const p1 = points[segIndex + 1]; + const point: Point = [lerp(p0[0], p1[0], localT), lerp(p0[1], p1[1], localT)]; + const value = values ? lerp(values[segIndex], values[segIndex + 1], localT) : 0; + return { point, normal: normal(p0, p1), value }; +} + +/** 접선과, 화면 기준 위쪽을 향하도록 부호를 맞춘 법선(눈썹 털 획 방향용). */ +function tangentAndUpNormal(points: Point[], t: number): { tangent: Point; up: Point } { + const segCount = points.length - 1; + const scaled = clamp(t, 0, 1) * segCount; + const segIndex = Math.min(segCount - 1, Math.floor(scaled)); + const p0 = points[segIndex]; + const p1 = points[segIndex + 1]; + const dx = p1[0] - p0[0]; + const dy = p1[1] - p0[1]; + const len = Math.hypot(dx, dy) || 1; + const tangent: Point = [dx / len, dy / len]; + const raw: Point = [-tangent[1], tangent[0]]; + const up: Point = raw[1] > 0 ? [-raw[0], -raw[1]] : raw; + return { tangent, up }; +} + +/** 눈썹 몸체 위로 삐져나오는 털 획 n개(잉크, 뿌리는 리본 폭 ±60% 안에서 흩뿌림). + 작은 화면 보정(§8.4)으로 길이에 sizeScale을 곱한다. */ +function browHairStrokes(samples: Point[], halfWidths: number[], count: number, seedBase: number, sizeScale: number): string { + const segments: string[] = []; + for (let i = 0; i < count; i++) { + const t = (i + 0.5) / count; + const { point, value: halfW } = alongPolyline(samples, halfWidths, t); + const { tangent, up } = tangentAndUpNormal(samples, t); + const rootFrac = (hash(seedBase + i * 3) * 2 - 1) * 0.6; + const root: Point = [point[0] + up[0] * rootFrac * halfW, point[1] + up[1] * rootFrac * halfW]; + const angleDeg = t < 1 / 3 ? 35 : 15; + const angleRad = (angleDeg * Math.PI) / 180; + const dirX = up[0] * Math.cos(angleRad) + tangent[0] * Math.sin(angleRad); + const dirY = up[1] * Math.cos(angleRad) + tangent[1] * Math.sin(angleRad); + const len = (9 + hash(seedBase + i * 3 + 1) * 5) * sizeScale; + const tip: Point = [root[0] + dirX * len, root[1] + dirY * len]; + segments.push(`M ${root[0]} ${root[1]} L ${tip[0]} ${tip[1]}`); + } + return segments.join(" "); +} + +/** 타원 안에 짧게 굽은 획 n개(턱 오돌토돌함). 시드 고정, 위치·각도·굽음 모두 결정적. */ +function ellipseCurvedStrokes(center: Point, rx: number, ry: number, count: number, seedBase: number): string[] { + const out: string[] = []; + for (let i = 0; i < count; i++) { + const angle = hash(seedBase + i * 4) * Math.PI * 2; + const radiusFrac = Math.sqrt(hash(seedBase + i * 4 + 1)) * 0.7; + const cx = center[0] + Math.cos(angle) * rx * radiusFrac; + const cy = center[1] + Math.sin(angle) * ry * radiusFrac; + const strokeAngle = hash(seedBase + i * 4 + 2) * Math.PI * 2; + const len = 8 + hash(seedBase + i * 4 + 3) * 4; + const dx = (Math.cos(strokeAngle) * len) / 2; + const dy = (Math.sin(strokeAngle) * len) / 2; + const p0: Point = [cx - dx, cy - dy]; + const p2: Point = [cx + dx, cy + dy]; + const half = len / 2 || 1; + const bendNormal: Point = [-dy / half, dx / half]; + const bend = 2.5; + const mid: Point = [cx + bendNormal[0] * bend, cy + bendNormal[1] * bend]; + out.push(pathQ(p0, quadControlThrough(p0, mid, p2), p2)); + } + return out; +} + +/** 사인 물결(§8.4 이마 주름: 진폭 1.5px, 획당 1.5주기, 시드 고정 위상)을 얹은 표본점. + t는 anchors 곡선 전체 기준 0..1(토막으로 끊어도 재정규화하지 않는다 — 끊긴 자리를 + 지우기 전의 연속된 리본처럼 두께·물결이 이어지게 한다). */ +function wavyRibbonPoints( + anchors: Point[], + samplesPerSeg: number, + seed: number, + amplitude: number, + cycles: number, +): Array<{ point: Point; t: number }> { + const withT = sampleCatmullRomWithT(anchors, samplesPerSeg); + const phase = hash(seed) * Math.PI * 2; + const n = withT.length; + return withT.map((s, i) => { + const prevPoint = i > 0 ? withT[i - 1].point : s.point; + const nextPoint = i < n - 1 ? withT[i + 1].point : s.point; + const nrm = normal(prevPoint, nextPoint); + const off = Math.sin(s.t * Math.PI * 2 * cycles + phase) * amplitude; + return { point: [s.point[0] + nrm[0] * off, s.point[1] + nrm[1] * off] as Point, t: s.t }; + }); +} + +function ribbonFromWavyPoints(pts: Array<{ point: Point; t: number }>, stops: Array): string { + if (pts.length < 2) return ""; + return polylineRibbon(pts.map((p) => p.point), pts.map((p) => valueAtStops(p.t, stops))); +} + +/** 긴 호를 2~3토막으로 끊는다(틈 6~12%, 시드 고정). 원래 t(0..1) 구간을 그대로 쓴다. */ +function seededSegmentRanges(seed: number): Array<[number, number]> { + const segCount = 2 + Math.round(hash(seed)); + const gaps: number[] = []; + for (let i = 0; i < segCount - 1; i++) gaps.push(lerp(0.06, 0.12, hash(seed + i + 1))); + const gapTotal = gaps.reduce((sum, g) => sum + g, 0); + const segLen = (1 - gapTotal) / segCount; + const ranges: Array<[number, number]> = []; + let cursor = 0; + for (let i = 0; i < segCount; i++) { + ranges.push([cursor, cursor + segLen]); + cursor += segLen + (gaps[i] ?? 0); + } + return ranges; +} + +/* ── 그룹 변환(§8.4 변환 표, 2단계-B-1b 개정) ──────────────────────── */ + +export interface LinocutTransforms { + figure: string; + body: string; + head: string; + face: string; + hairFront: string; +} + +function pivotWrap(cx: number, cy: number, inner: string): string { + return `translate(${cx} ${cy}) ${inner} translate(${-cx} ${-cy})`; +} + +function computeTransforms(rig: LinocutRig, v: ChannelVector): LinocutTransforms { + const bodyPivot = toPoint(rig.pivots.body); + const neckPivot = toPoint(rig.pivots.neck); + const facePivot = toPoint(rig.pivots.face); + + const figureScale = 1 + v.bodyLean * 0.03; + const figure = [ + `translate(${v.bodyYaw * 8} ${-v.bodyLean * 6})`, + `rotate(${v.bodyRoll * 3} ${bodyPivot[0]} ${bodyPivot[1]})`, + pivotWrap(bodyPivot[0], bodyPivot[1], `scale(${figureScale} ${figureScale})`), + ].join(" "); + + const body = [ + `translate(0 ${-v.shoulderRaise * 10})`, + pivotWrap(bodyPivot[0], bodyPivot[1], `scale(1 ${1 + v.breath * 0.006})`), + ].join(" "); + + const pitchCoef = v.headPitch < 0 ? 20 : 14; + const head = [ + `translate(${v.headYaw * 14} ${-v.headPitch * pitchCoef - v.shoulderRaise * 4 - v.breath * 2.5})`, + `rotate(${v.headRoll * 7} ${neckPivot[0]} ${neckPivot[1]})`, + ].join(" "); + + const faceSx = 1 - Math.abs(v.headYaw) * 0.08; + const faceSy = 1 + v.headPitch * 0.03; + const face = [ + `translate(${v.headYaw * 6} ${-v.headPitch * 4})`, + pivotWrap(facePivot[0], facePivot[1], `scale(${faceSx} ${faceSy})`), + ].join(" "); + + const hairFront = `translate(${v.headYaw * 10} ${-v.headPitch * 3})`; + + return { figure, body, head, face, hairFront }; +} + +/* ── 눈 ────────────────────────────────────────────────────────────── */ + +export interface EyeGeometry { + outlineD: string; + upperLidD: string; + creaseD: string; + creaseOpacity: number; + lowerLidD: string; + shadowD: string; + irisCx: number; + irisCy: number; + irisRadius: number; + irisRingWidth: number; + pupilRadius: number; + highlightCx: number; + highlightCy: number; + highlightRadius: number; + radialStrokesD: string; + tearPoolD: string; + tearPoolOpacity: number; + tearDropCx: number; + tearDropCy: number; + tearDropR: number; + tearFlowD: string; + tearFlowOpacity: number; + tearFlowDropCx: number; + tearFlowDropCy: number; + tearFlowDropR: number; +} + +interface EyeChannels { + open: number; + smile: number; + lidTension: number; + gazeX: number; + gazeY: number; + pupilSize: number; + tear: number; +} + +const CRESCENT_SAMPLES = 14; +const CREASE_SAMPLES = 8; +const LOWER_LID_SAMPLES = 10; + +function buildEye(eye: RigEye, ch: EyeChannels, isScreenRight: boolean, sizeScale: number): EyeGeometry { + const O = toPoint(eye.outer); + const I = toPoint(eye.inner); + const U = toPoint(eye.upperLidTop); + const D = toPoint(eye.lowerLidBottom); + const C = toPoint(eye.iris.center); + const r = eye.iris.radius; + + /* 크게 뜬 눈(eyeOpen>1) 증폭: 그대로 두면 놀람이 잘 안 읽힌다(1차 반려 "표정 간 차이가 + 작다"와 연결). e는 eyeOpen<=1이면 그대로, 넘으면 1.8배 증폭(최대 1.5)한다. */ + const e = ch.open <= 1 ? Math.max(0, ch.open) : Math.min(1.5, 1 + (ch.open - 1) * 1.8); + + const openD = (1 - 0.75 * ch.smile) * (1 - 0.3 * ch.lidTension) * Math.min(1, 0.4 + 0.6 * clamp(ch.open, 0, 1)); + const openU = e * (1 - 0.25 * ch.lidTension) * (1 - 0.15 * ch.smile); + + const midD = lineY(O, I, D[0]); + const hD = D[1] - midD; + + const yD = midD + hD * openD; + /* yU는 "닫힘(openU=0)일 때 yD와 만나고, 열림(openU=1)일 때 랜드마크 U를 정확히 + 지난다"는 두 경계 조건을 만족해야 한다(중립 채널 눈꺼풀 곡선이 랜드마크를 + ≤0.5px로 지나야 한다는 요구, 2단계-B-1b 증거 요구 a). O-I 직선을 U·D 각각의 + x에서 따로 보간해 빼는 이전 방식은 U.x≠D.x일 때 오차가 생겨 이 조건을 만족하지 + 못했다. yD를 축으로 직접 선형보간하면 항상 정확하다. */ + const yU = yD + (U[1] - yD) * openU; + + const upperApex: Point = [U[0], yU]; + const lowerApex: Point = [D[0], yD]; + const upperControl = quadControlThrough(O, upperApex, I); + const lowerControl = quadControlThrough(O, lowerApex, I); + + const outlineD = `${pathQ(O, upperControl, I)} Q ${lowerControl[0]} ${lowerControl[1]} ${O[0]} ${O[1]} Z`; + + /* 윗눈꺼풀 선: s=0(바깥 O)→1(안쪽 I), t(s) = 1.5 + 4·(1 − smoothstep(0.45,1,s)). */ + const crescentTop = sampleQuad(O, upperControl, I, 0, 1, CRESCENT_SAMPLES); + const crescentBottom = crescentTop.map((p, i) => { + const s = i / (CRESCENT_SAMPLES - 1); + const t = sizeScale * (1.5 + 4 * (1 - smoothstep(0.45, 1, s))); + return [p[0], p[1] + t] as Point; + }); + const tailDir = (() => { + const dx = O[0] - upperControl[0]; + const dy = O[1] - upperControl[1]; + const len = Math.hypot(dx, dy) || 1; + return [dx / len, dy / len] as Point; + })(); + const tailTip: Point = [O[0] + tailDir[0] * 8, O[1] + tailDir[1] * 8]; + const upperLidD = [ + `M ${tailTip[0]} ${tailTip[1]}`, + `L ${O[0]} ${O[1]}`, + ...crescentTop.slice(1).map((p) => `L ${p[0]} ${p[1]}`), + ...[...crescentBottom].reverse().map((p) => `L ${p[0]} ${p[1]}`), + `L ${tailTip[0]} ${tailTip[1] + 1}`, + "Z", + ].join(" "); + + /* 쌍꺼풀 주름: s 0.15~0.85, 윗곡선 위로 9 + 5·max(0, openU−1)만큼, 불투명도 0.6 기준. */ + const creaseOffset = 9 + 5 * Math.max(0, openU - 1); + const creasePts = sampleQuad(O, upperControl, I, 0.15, 0.85, CREASE_SAMPLES).map((p) => [p[0], p[1] - creaseOffset] as Point); + const creaseD = creasePts.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`).join(" "); + const creaseOpacity = (openU < 0.35 ? clamp(openU / 0.35) : 1) * 0.6; + + /* 아랫눈꺼풀: s 0~0.8 가는 선. */ + const lowerPts = sampleQuad(O, lowerControl, I, 0, 0.8, LOWER_LID_SAMPLES); + const lowerLidD = lowerPts.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`).join(" "); + + /* 윗눈꺼풀 아래 3.5px 그늘 띠. */ + const shadowD = [ + ...crescentBottom.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`), + ...[...crescentBottom].reverse().map((p) => `L ${p[0]} ${p[1] + 3.5}`), + "Z", + ].join(" "); + + const irisCx = C[0] + ch.gazeX * 0.85 * r; + const irisCy = C[1] + ch.gazeY * 0.5 * r; + const pupilRadius = 0.42 * r * (1 + 0.3 * ch.pupilSize); + const highlightCx = irisCx - 0.35 * r; + const highlightCy = irisCy - 0.4 * r; + + const radialSegments: string[] = []; + const innerR = pupilRadius + 1; + const outerR = r * 0.85; + for (let k = 0; k < 8; k++) { + const angle = (k / 8) * Math.PI * 2; + const cos = Math.cos(angle); + const sin = Math.sin(angle); + radialSegments.push( + `M ${irisCx + cos * innerR} ${irisCy + sin * innerR} L ${irisCx + cos * outerR} ${irisCy + sin * outerR}`, + ); + } + + const pool = clamp(ch.tear / 0.3); + const flow = clamp((ch.tear - 0.3) / 0.7); + const tearPoolPts = sampleQuad(O, lowerControl, I, 0.6, 1, 6); + const tearPoolD = tearPoolPts.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`).join(" "); + const tearDropCx = I[0]; + const tearDropCy = I[1] + 2; + const tearDropR = 2.5 * pool; + + const flowStart = quadPointAt(O, lowerControl, I, 0.65); + const flowLen = flow * 120 * (isScreenRight ? 0.7 : 1); + const flowEnd: Point = [flowStart[0] + 6, flowStart[1] + flowLen]; + const flowControl: Point = [flowStart[0] + 3, flowStart[1] + flowLen * 0.5]; + const flowHalfTop = 1.5; + const flowHalfBottom = 1; + const flowNormal = normal(flowStart, flowEnd); + const tearFlowD = + flowLen > 0.5 + ? [ + `M ${flowStart[0] + flowNormal[0] * flowHalfTop} ${flowStart[1] + flowNormal[1] * flowHalfTop}`, + `Q ${flowControl[0] + flowNormal[0] * flowHalfBottom} ${flowControl[1] + flowNormal[1] * flowHalfBottom} ${flowEnd[0] + flowNormal[0] * flowHalfBottom} ${flowEnd[1] + flowNormal[1] * flowHalfBottom}`, + `L ${flowEnd[0] - flowNormal[0] * flowHalfBottom} ${flowEnd[1] - flowNormal[1] * flowHalfBottom}`, + `Q ${flowControl[0] - flowNormal[0] * flowHalfBottom} ${flowControl[1] - flowNormal[1] * flowHalfBottom} ${flowStart[0] - flowNormal[0] * flowHalfTop} ${flowStart[1] - flowNormal[1] * flowHalfTop}`, + "Z", + ].join(" ") + : ""; + + return { + outlineD, + upperLidD, + creaseD, + creaseOpacity, + lowerLidD, + shadowD, + irisCx, + irisCy, + irisRadius: r, + irisRingWidth: 0.14 * r, + pupilRadius, + highlightCx, + highlightCy, + highlightRadius: 0.13 * r, + radialStrokesD: radialSegments.join(" "), + tearPoolD, + tearPoolOpacity: pool, + tearDropCx, + tearDropCy, + tearDropR, + tearFlowD, + tearFlowOpacity: flow, + tearFlowDropCx: flowEnd[0], + tearFlowDropCy: flowEnd[1], + tearFlowDropR: flowLen > 0.5 ? 2.2 : 0, + }; +} + +/* ── 눈썹(2단계-B-1b 개정: 곡선·리본·삐져나온 털 획) ──────────────── */ + +export interface BrowGeometry { + bodyD: string; + hairStrokesD: string; + hairStrokeWidth: number; + /** 채널 반영 후 실제 안쪽 끝·꼭짓점(이마 주름·그늘이 따라간다). */ + innerP: Point; + peakP: Point; + shadowCx: number; + shadowCy: number; + shadowOpacity: number; +} + +interface BrowChannels { + y: number; + angle: number; + x: number; + form: number; +} + +const BROW_STOPS: Array = [ + [0, 4.5], + [0.5, 3.2], + [1, 1.0], +]; + +function buildBrow(brow: RigBrow, ch: BrowChannels, side: "left" | "right", sizeScale: number): BrowGeometry { + const inner = toPoint(brow.inner); + const peak = toPoint(brow.peak); + const outer = toPoint(brow.outer); + + /* browAngle a는 부호에 따라 다른 계수를 쓴다(AU1 안쪽 올림은 바깥이 따라 오르지 않는다, + §8.4 2단계-B-1d 개정): a>0이면 안쪽 −60a·꼭짓점 −4a·바깥 +18a, a<0(AU4)이면 + 안쪽 −50a·꼭짓점 −12a·바깥 +14a. */ + const dyAll = -ch.y * 45; + const a = ch.angle; + const innerCoef = a > 0 ? -60 : -50; + const peakCoef = a > 0 ? -4 : -12; + const outerCoef = a > 0 ? 18 : 14; + const innerDy = dyAll + innerCoef * a; + const outerDy = dyAll + outerCoef * a; + const peakDy = dyAll + peakCoef * a - ch.form * 10; + const innerDx = side === "left" ? -ch.x * 16 : ch.x * 16; + + const innerP: Point = [inner[0] + innerDx, inner[1] + innerDy]; + const peakP: Point = [peak[0], peak[1] + peakDy]; + const outerP: Point = [outer[0], outer[1] + outerDy]; + + /* "세 점을 지나는 곡선(12점 이상 표본)", 꺾인 직선 금지(1차 반려 사유). 작은 화면 + 보정(§8.4 2단계-B-1d)으로 반두께·털 획 길이·굵기에 sizeScale을 곱한다. */ + const scaledStops = BROW_STOPS.map(([t, hw]) => [t, hw * sizeScale] as const); + const { samples, halfWidths } = curveRibbonData([innerP, peakP, outerP], scaledStops, 6); + const bodyD = polylineRibbon(samples, halfWidths); + const seedBase = side === "left" ? 0 : 100; + const hairStrokesD = browHairStrokes(samples, halfWidths, 14, seedBase, sizeScale); + const hairStrokeWidth = 1.3 * sizeScale; + + /* 안쪽 올림 그늘(§8.4): a>0.3이면 눈썹 머리 위에 흐린 잉크 그늘(가우시안 흐림은 + 렌더러의 SVG 필터가 맡는다, 여기서는 위치·불투명도만). */ + const shadowOpacity = 0.18 * clamp((a - 0.3) / 0.7); + const shadowCx = innerP[0]; + const shadowCy = innerP[1] - 18; + + return { bodyD, hairStrokesD, hairStrokeWidth, innerP, peakP, shadowCx, shadowCy, shadowOpacity }; +} + +/* ── 보조 주름 ─────────────────────────────────────────────────────── */ + +export interface WrinkleSet { + paths: string[]; + opacity: number; + /** 순수 stroke(리본이 아닌) 주름만 쓴다(미간·턱). 작은 화면 보정(sizeScale)이 반영된 값. */ + strokeWidth?: number; +} + +/* ── 입(2단계-B-1b 전면 교체: U/∩ 곡선·Catmull-Rom 바깥선) ──────────── */ + +export interface MouthGeometry { + lineD: string; + /** R8(2026-10-01, §8.4 입 "질감" 재개정, lipTexture 있는 리그 한정): 벡터 입 선은 + 이제 작은 화면 보조일 뿐이다 — 0.95·clamp((sizeScale−1)/0.5, 0, 1). face + 300px(sizeScale=1)에서는 0. lipTexture가 없는 리그는 기존대로 0.95 고정. */ + lineOpacity: number; + innerD: string; + innerOpacity: number; + teethD: string; + /** R9: 윗니 색(teeth·mouthInner 3:1 혼합, 새하얀 막대로 안 튀게). */ + teethFillColor: string; + /** R9: 윗니 위 35% 잉크 그늘(불투명도 0.35 고정, 렌더러가 적용). teethD가 비면 같이 빈다. */ + teethShadowD: string; + /** 혀 그늘(§5.4): open>0.45에서 나타나는 입 안 아래쪽 반달. */ + tongueShadowD: string; + tongueShadowOpacity: number; + /** rig.lipTexture가 없는 리그의 대체 경로 전용(R8: lipTexture 있으면 스프라이트 + 알파가 입 모양을 정하므로 이 벡터 채움·그늘·쐐기를 안 쓴다). */ + vectorLipFallback?: VectorLipFallback; + /** 입술 결 띠 변형(§8.4 입 "질감" 개정, 2단계-B-1d-R7). rig.lipTexture가 없으면 빈 배열. */ + lipStripsUpper: StripMatrix[]; + lipStripsLower: StripMatrix[]; + /** 채널 반영 후 실제 입꼬리 좌표(팔자 주름이 따라간다). */ + cornerLeft: Point; + cornerRight: Point; + /** 틈 높이 G(§5.4 R6b). §8.4 하관 띠 변형의 "현재 입 틈 높이"로 그대로 쓴다. */ + gapHeight: number; + /** 볼 불룩(§8.4, 2026-10-01 추가): 입꼬리마다 바깥 잉크 타원 + 종이색 하이라이트. */ + cheekPuffLeft: CheekPuff; + cheekPuffRight: CheekPuff; +} + +/** 볼 불룩 타원 한 쪽(§8.4): 흐린 잉크 타원(반지름 16×24, 가우시안 stdDev 6) + 종이색 + 하이라이트. 반지름은 렌더러 상수(고정 16×24)라 여기서는 중심·불투명도만 낸다. */ +export interface CheekPuff { + cx: number; + cy: number; + highlightCx: number; + highlightCy: number; + opacity: number; +} + +/** 입술 결 띠 하나의 아핀 변환(회전·기울임 없음: matrix(a,0,0,d,e,f)). */ +export interface StripMatrix { + a: number; + d: number; + e: number; + f: number; +} + +/** R8(2026-10-01, §8.4 입 "질감" 재개정): 16 → 24로 늘려 곡선 추종 오차를 줄인다 + (verify_r7_tracking.mjs 실측: joy 3.85→2.60px, grief 4.86→3.19px, U 6.90→4.55px). */ +export const LIP_STRIP_COUNT = 24; + +const MOUTH_LINE_STOPS: Array = [ + [0, 1.4], + [0.5, 2.6], + [1, 1.4], +]; + +/** 입술 결 띠 변형(§8.4 입 "질감" 개정, 2단계-B-1d-R7)의 중립 기준값. rig당 1회만 + 계산하면 되므로 캐시한다: W0·tU0·tL0(채널 반영 전 랜드마크 값)와 중립 Cu0(x). */ +interface NeutralMouthRef { + M0x: number; + W0: number; + tU0: number; + tL0: number; + cuAt0: (x: number) => number; +} +const neutralMouthRefCache = new WeakMap(); +function neutralMouthRef(rig: LinocutRig): NeutralMouthRef { + const cached = neutralMouthRefCache.get(rig); + if (cached) return cached; + const Lc0 = toPoint(rig.landmarks.mouthCornerLeft); + const Rc0 = toPoint(rig.landmarks.mouthCornerRight); + const T = toPoint(rig.landmarks.upperLipTop); + const B = toPoint(rig.landmarks.lowerLipBottom); + const M0 = toPoint(rig.landmarks.mouthCenter); + const control0 = quadControlThrough(Lc0, M0, Rc0); + const xToT0 = (x: number) => clamp((x - Lc0[0]) / (Rc0[0] - Lc0[0] || 1)); + const cuAt0 = (x: number) => quadPointAt(Lc0, control0, Rc0, xToT0(x))[1]; + const ref: NeutralMouthRef = { M0x: M0[0], W0: Rc0[0] - Lc0[0], tU0: M0[1] - T[1], tL0: B[1] - M0[1], cuAt0 }; + neutralMouthRefCache.set(rig, ref); + return ref; +} + +/** 입 모양의 채널 반영 핵심값(입술 결 띠 변형·바깥선 계산이 함께 쓴다). */ +interface MouthShape { + Lc: Point; + Rc: Point; + Mc: Point; + /** 닫힌 입(틈 없음) 기준 곡선의 2차 베지어 제어점 — 모서리 tilt(미소·찡그림)만 반영하고 + 벌림·오므림과는 무관하다. R6b 틈 타원(mouthGapCurves)이 이 위에 얹힌다. */ + seamControl: Point; + W: number; + tU: number; + tL: number; + /** 틈 반폭 a(§5.4 렌더러 입 모양, R6b: a = (W/2)(1−0.65r)). */ + gapHalfWidth: number; + /** 틈 높이 G(§5.4 렌더러 입 모양, R6b: G = g(1+0.8r)). §8.4 하관 띠 변형의 + "현재 입 틈 높이"가 바로 이 값이다. */ + gapHeight: number; +} + +function computeMouthShape(rig: LinocutRig, v: ChannelVector): MouthShape { + const Lc0 = toPoint(rig.landmarks.mouthCornerLeft); + const Rc0 = toPoint(rig.landmarks.mouthCornerRight); + const T = toPoint(rig.landmarks.upperLipTop); + const B = toPoint(rig.landmarks.lowerLipBottom); + const M0 = toPoint(rig.landmarks.mouthCenter); + + const w = 26 * v.mouthStretch + 12 * Math.max(0, v.mouthForm) - 10 * Math.max(0, -v.mouthForm); + const roundInset = 32 * v.mouthRound; + const Lc: Point = [Lc0[0] - w + roundInset, Lc0[1] - 36 * v.mouthForm - 22 * v.mouthAsym + 6 * v.chinRaise]; + const Rc: Point = [Rc0[0] + w - roundInset, Rc0[1] - 36 * v.mouthForm + 22 * v.mouthAsym + 6 * v.chinRaise]; + const Mc: Point = [M0[0], M0[1] - 6 * v.mouthForm - 4 * v.chinRaise]; + + /* 닫힌 입 기준 곡선: apex를 Mc에 정확히 두어(g 오프셋 없음) 미소·찡그림의 모서리↔중앙 + tilt만 담는다. mouthOpen=0이면 이것이 바로 예전 Cu=Cl이다. */ + const seamControl = quadControlThrough(Lc, Mc, Rc); + + const r = clamp(v.mouthRound); + const gapBase = v.mouthOpen * 44 * (1 - 0.3 * v.mouthStretch); + const gapHeight = gapBase * (1 + 0.8 * r); + const gapHalfWidth = ((Rc[0] - Lc[0]) / 2) * (1 - 0.65 * r); + + const roundThicken = 1 + 0.3 * r; + const tU = (M0[1] - T[1]) * (1 - 0.6 * v.mouthPress) * (1 - 0.25 * Math.max(0, v.mouthForm)) * roundThicken; + const tL = (B[1] - M0[1]) * (1 - 0.55 * v.mouthPress) * (1 - 0.2 * Math.max(0, v.mouthForm)) * (1 - 0.3 * v.chinRaise) * roundThicken; + const W = Rc[0] - Lc[0]; + + return { Lc, Rc, Mc, seamControl, W, tU, tL, gapHalfWidth, gapHeight }; +} + +/** 입 틈의 기준 곡선 Cu·Cl(§5.4 렌더러 입 모양 R6b 개정, 2단계-B-1d-R6b). 닫힌 입 + 베이스라인(seamControl) 위에 가운데 타원 틈(반폭 a, 높이 G)을 얹는다: Mc 중심으로 + 위 0.25G·아래 0.75G인 타원이며, gapFrac이 틈 경계(|x−Mc.x|=a)에서 정확히 0으로 + 수렴해 이음매 없이 베이스라인과 만난다 — "틈 밖의 입 선 구간은 닫힌 채 입 선만 + 그린다", "틈 밖에서는 Cl=Cu다"를 만족한다. r=0이면 a=W/2가 모서리(Lc·Rc)에 정확히 + 닿아 기존 렌즈(모서리~모서리)와 사실상 같아진다. */ +function mouthGapCurves(shape: MouthShape): { CuAt: (x: number) => number; ClAt: (x: number) => number } { + const xToT = (x: number) => clamp((x - shape.Lc[0]) / (shape.Rc[0] - shape.Lc[0] || 1)); + const seamAt = (x: number) => quadPointAt(shape.Lc, shape.seamControl, shape.Rc, xToT(x))[1]; + const gapFrac = (x: number) => { + if (shape.gapHalfWidth <= 0) return 0; + const nx = clamp((x - shape.Mc[0]) / shape.gapHalfWidth, -1, 1); + return Math.sqrt(Math.max(0, 1 - nx * nx)); + }; + return { + CuAt: (x: number) => seamAt(x) - 0.25 * shape.gapHeight * gapFrac(x), + ClAt: (x: number) => seamAt(x) + 0.75 * shape.gapHeight * gapFrac(x), + }; +} + +/** 검증용 공개 API(R6b·R7 수치 검증 스크립트 전용): 주어진 채널의 입 틈 기준 곡선 + Cu·Cl을 직접 낸다. 렌더러(buildMouth)는 이 함수를 안 거치고 내부에서 같은 계산을 + 한다 — 중복 계산이 아니라 외부 검증이 내부 비공개 함수(mouthGapCurves)에 닿을 + 길이 없어 만든 얇은 래퍼다. */ +export function mouthCurvesAt(rig: LinocutRig, v: ChannelVector): { CuAt: (x: number) => number; ClAt: (x: number) => number } { + return mouthGapCurves(computeMouthShape(rig, v)); +} + +/** xFrom..xTo를 count개로 균등 표본한 곡선 점(§5.4 R6b: CuAt/ClAt은 x를 받는 함수라 + quadPointAt/sampleQuad의 t 표본을 대체한다). */ +function sampleCurveX(xFrom: number, xTo: number, count: number, fn: (x: number) => number): Point[] { + const out: Point[] = []; + for (let i = 0; i < count; i++) { + const x = lerp(xFrom, xTo, count === 1 ? 0 : i / (count - 1)); + out.push([x, fn(x)]); + } + return out; +} + +/** 입술 결 띠 변형(§8.4 2단계-B-1d-R7, 순수 함수라 채널 벡터만으로 테스트할 수 있다). + 스프라이트를 가로 stripCount개 세로 띠로 나누고(스프라이트 폭 기준 등분, 이웃 띠와 + 1px 겹침), 띠 중심 x' = Mc.x + (x−M0.x)·W/W0, 윗입술 띠 y' = Cu(x')+(y−Cu0(x))·tU/tU0, + 아랫입술 띠 y' = Cl(x')+(y−Cu0(x))·tL/tL0(스펙대로 아래도 Cu0 기준 — 중립에서 Cu0=Cl0). */ +export function computeLipStripTransforms( + rig: LinocutRig, + v: ChannelVector, + stripCount: number = LIP_STRIP_COUNT, +): { upper: StripMatrix[]; lower: StripMatrix[] } | null { + if (!rig.lipTexture) return null; + const shape = computeMouthShape(rig, v); + const neutral = neutralMouthRef(rig); + const scaleX = shape.W / (neutral.W0 || 1); + const translateX = shape.Mc[0] - neutral.M0x * scaleX; + /* R7(§8.4 "입술 띠 변형의 기준 곡선(Cu·Cl)도 새 틈을 쓴다"): 띠 변형 기준도 R6b의 + 타원 틈 곡선을 그대로 쓴다. 틈 밖(mouthOpen=0 포함)에서는 Cl=Cu라 위·아래 입술 + 띠가 같은 닫힌 곡선을 따라간다. */ + const { CuAt, ClAt } = mouthGapCurves(shape); + + function stripsFor(sprite: RigLayer, scaleY: number, curveAt: (x: number) => number): StripMatrix[] { + const out: StripMatrix[] = []; + const stripW = sprite.w / stripCount; + for (let i = 0; i < stripCount; i++) { + const cx = sprite.x + (i + 0.5) * stripW; + const cxPrime = shape.Mc[0] + (cx - neutral.M0x) * scaleX; + const f = curveAt(cxPrime) - neutral.cuAt0(cx) * scaleY; + out.push({ a: scaleX, d: scaleY, e: translateX, f }); + } + return out; + } + + const scaleYUpper = shape.tU / (neutral.tU0 || 1); + const scaleYLower = shape.tL / (neutral.tL0 || 1); + return { + upper: stripsFor(rig.lipTexture.upper, scaleYUpper, CuAt), + lower: stripsFor(rig.lipTexture.lower, scaleYLower, ClAt), + }; +} + +/** 띠 사각형 clip의 정적 기하(스프라이트 폭 기준 등분, 이웃과 1px 겹침). 채널과 무관해 + 렌더러가 마운트 시 한 번만 계산해 에 쓴다. */ +export function lipStripClipRect(sprite: RigLayer, index: number, stripCount: number = LIP_STRIP_COUNT) { + const stripW = sprite.w / stripCount; + return { x: sprite.x + index * stripW - 0.5, y: sprite.y, w: stripW + 1, h: sprite.h }; +} + +/** rig.lipTexture가 없는 리그의 대체 경로(R8 이전 벡터 입술 그대로): 단색 입술 면 + (upperD·lowerD), 입꼬리 잉크 쐐기, 아랫입술 안쪽 보조선, 아랫입술 아래 그늘 + 초승달. R8은 lipTexture가 있는 리그에만 적용되므로(오케스트레이터 지시), 없는 + 리그는 이 함수로 기존 동작을 그대로 낸다. */ +export interface VectorLipFallback { + upperD: string; + lowerD: string; + cornerStrokesD: string; + lineSecondaryD: string; + lineSecondaryOpacity: number; + lowerShadowCrescentD: string; +} + +function cornerWedge(corner: Point, control: Point, length: number, halfWidth: number): string { + const dx = corner[0] - control[0]; + const dy = corner[1] - control[1]; + const len = Math.hypot(dx, dy) || 1; + const tip: Point = [corner[0] + (dx / len) * length, corner[1] + (dy / len) * length]; + return polylineRibbon([corner, tip], [halfWidth, 0]); +} + +function buildVectorLipFallback( + shape: MouthShape, + CuAt: (x: number) => number, + ClAt: (x: number) => number, + sizeScale: number, + chinRaise: number, +): VectorLipFallback { + const { Lc, Rc, Mc, seamControl, W, tU, tL, gapHeight } = shape; + + const upperValleyY = CuAt(Mc[0]) - tU + 4; + const lowerValleyY = ClAt(Mc[0]) + tL - 8 * chinRaise; + + const upperOuterAnchors: Point[] = [ + Lc, + [Lc[0] + 0.22 * W, CuAt(Lc[0] + 0.22 * W) - 0.55 * tU], + [Mc[0] - 0.19 * W, CuAt(Mc[0] - 0.19 * W) - tU], + [Mc[0], upperValleyY], + [Mc[0] + 0.19 * W, CuAt(Mc[0] + 0.19 * W) - tU], + [Rc[0] - 0.22 * W, CuAt(Rc[0] - 0.22 * W) - 0.55 * tU], + Rc, + ]; + const lowerOuterAnchors: Point[] = [ + Lc, + [Lc[0] + 0.25 * W, ClAt(Lc[0] + 0.25 * W) + 0.7 * tL], + [Mc[0], lowerValleyY], + [Rc[0] - 0.25 * W, ClAt(Rc[0] - 0.25 * W) + 0.7 * tL], + Rc, + ]; + + const upperOutlineD = catmullRomPathD(upperOuterAnchors); + const CLOSE_SAMPLES = 16; + const cuSamplesRcToLc = [...sampleCurveX(Lc[0], Rc[0], CLOSE_SAMPLES, CuAt)].reverse(); + const clSamplesLcToRc = sampleCurveX(Lc[0], Rc[0], CLOSE_SAMPLES, ClAt); + const upperD = `${upperOutlineD} ${catmullRomCommands(cuSamplesRcToLc)} Z`; + const lowerD = `M ${Lc[0]} ${Lc[1]} ${catmullRomCommands(clSamplesLcToRc)} ${catmullRomCommands([...lowerOuterAnchors].reverse())} Z`; + + const cornerStrokesD = [ + cornerWedge(Lc, seamControl, 10, 2.2 * sizeScale), + cornerWedge(Rc, seamControl, 10, 2.2 * sizeScale), + ].join(" "); + + const lineSecondaryD = catmullRomPathD(sampleCurveX(Lc[0] + 0.1 * W, Rc[0] - 0.1 * W, 8, ClAt)); + const lineSecondaryOpacity = gapHeight > 0.5 ? 0.6 : 0; + + const shadowBand = sampleCatmullRomWithT(lowerOuterAnchors, 8).filter((s) => s.t >= 0.15 && s.t <= 0.85); + const shadowTop = shadowBand.map((s) => [s.point[0], s.point[1] + 3] as Point); + const shadowBottom = shadowBand.map((s) => [s.point[0], s.point[1] + 9] as Point); + const lowerShadowCrescentD = [ + ...shadowTop.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`), + ...[...shadowBottom].reverse().map((p) => `L ${p[0]} ${p[1]}`), + "Z", + ].join(" "); + + return { upperD, lowerD, cornerStrokesD, lineSecondaryD, lineSecondaryOpacity, lowerShadowCrescentD }; +} + +function buildMouth(rig: LinocutRig, v: ChannelVector, sizeScale: number): MouthGeometry { + /* 찡그리면 입이 좁아진다(3차 판정: 비탄 입이 넓은 ∩자 만화 입이 되었다, §8.4 2단계-B-1d). + mouthRound(§5.4 발화층, 2B-1d-R6)는 모서리를 안쪽으로 추가 32r 옮긴다(ㅗ·ㅜ 오므림). + mouthStretch(넓힘)는 벌림 높이를 낮춘다(ㅣ·ㅡ처럼 옆으로 넓고 얕게). chinRaise(AU17)는 + 아랫입술을 밀어 올려 입을 삐죽 모은다. mouthRound는 입술을 도톰하게 한다(두께 ×(1+0.3r)). + 이 핵심값은 computeMouthShape로 뽑아 입술 결 띠 변형(computeLipStripTransforms)과 + 공유한다(중복 계산 방지, R7). */ + const shape = computeMouthShape(rig, v); + const { Lc, Rc, W, gapHeight } = shape; + const { CuAt, ClAt } = mouthGapCurves(shape); + + /* R8(2026-10-01, §8.4 입 "질감" 재개정): 입 모양은 이제 lipTexture 스프라이트 + 알파가 정한다(§8.4 원칙 "중립에서 렌더된 입은 원화와 픽셀 단위로 같고, 움직이면 + 원화 픽셀이 변형된다"). upperD·lowerD(벡터 입술 바깥선·면)는 더는 만들지 않는다 + — 이전에는 (a) 입술 단색 채움 (b) 입술 결 띠의 clip 도형으로 썼는데, 둘 다 이제 + 스프라이트 알파가 대신한다(clip은 띠별 사각형만 남는다, computeLipStripTransforms + 참고). 입 안(틈)·윗니·혀 그늘은 그대로 계산해 스프라이트 "아래"에 그린다(렌더러가 + 그리는 순서를 정한다). */ + + /* 벌어진 틈(§5.4 렌더러 입 모양 R6b): 오므린 입은 모서리부터 붙고 가운데만 둥글게 + 열린다(가운데 타원, mouthGapCurves). 틈 밖에서는 CuAt=ClAt(닫힘)이라 Lc~Rc 전체를 + 표본해도 틈 밖 구간은 저절로 높이 0인 얇은 선이 된다. */ + const GAP_SAMPLES = 16; + const innerUpper = sampleCurveX(Lc[0], Rc[0], GAP_SAMPLES, CuAt); + const innerLowerRev = [...sampleCurveX(Lc[0], Rc[0], GAP_SAMPLES, ClAt)].reverse(); + const innerD = `${catmullRomPathD([...innerUpper, ...innerLowerRev])} Z`; + const innerOpacity = gapHeight > 0.5 ? 1 : 0; + + /* 윗니(§5.4 렌더러 입 모양, R9 개정): open>0.08이고 (mouthStretch>0.2 또는 + open>0.3)이면 보인다. 높이는 min(6, 3+8·open)·(1−0.8·mouthRound)이고 틈 + 높이(G) 이하로 자른다(R9: 새하얀 막대로 튀고 ㅗ에서도 이가 도드라지던 문제 — + mouthRound가 높을수록 낮아져 오므린 입에서는 거의 안 보인다). 윗니 y는 새 + 틈(CuAt)을 따른다. */ + const teethVisible = v.mouthOpen > 0.08 && (v.mouthStretch > 0.2 || v.mouthOpen > 0.3); + const teethHeightRaw = teethVisible ? Math.min(6, 3 + 8 * v.mouthOpen) * (1 - 0.8 * clamp(v.mouthRound)) : 0; + const teethHeight = Math.min(teethHeightRaw, Math.max(0, gapHeight)); + const insetX = W * 0.12; + const teethTop = sampleCurveX(Lc[0] + insetX, Rc[0] - insetX, 6, CuAt); + const teethBottom = teethTop.map((p) => [p[0], p[1] + teethHeight] as Point); + const teethD = + teethHeight > 0 + ? [ + ...teethTop.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`), + ...[...teethBottom].reverse().map((p) => `L ${p[0]} ${p[1]}`), + "Z", + ].join(" ") + : ""; + /* R9: 윗니 색은 teeth·mouthInner를 3:1로 섞은 종이색(새하얀 막대로 안 튀게), + 위 35%에 잉크 0.35 그늘. */ + const teethFillColor = mixHex(rig.palette.teeth, rig.palette.mouthInner, 0.75); + const teethShadowD = + teethHeight > 0 + ? [ + ...teethTop.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`), + ...[...teethTop].reverse().map((p) => `L ${p[0]} ${p[1] + teethHeight * 0.35}`), + "Z", + ].join(" ") + : ""; + + /* R8: 벡터 입 선은 이제 작은 화면 보조일 뿐이다(§8.4 입 "질감" 재개정) — 큰 화면 + (face 300px, sizeScale=1)에서는 스프라이트가 결을 보여주므로 0, 작아질수록 + 0.95까지 올라간다. d(모양)는 여전히 Cu(R6b 새 틈 반영)를 따라 매 프레임 갱신한다 + (opacity가 0이어도 d는 계산해 둬야 화면이 커질 때 바로 보인다). */ + const lineSamples = sampleCurveX(Lc[0], Rc[0], 12, CuAt); + const lineHalfWidths = lineSamples.map((_, i) => valueAtStops(i / 11, MOUTH_LINE_STOPS) * sizeScale); + const accentBoost = clamp(Math.max(0, v.mouthPress - 0.3) * 1.5 + Math.max(0, -v.mouthForm - 0.3) * 1.2); + lineHalfWidths[0] += accentBoost * 0.8 * sizeScale; + lineHalfWidths[lineHalfWidths.length - 1] += accentBoost * 0.8 * sizeScale; + const lineD = polylineRibbon(lineSamples, lineHalfWidths); + /* R8은 lipTexture가 있는 리그에만 적용된다(오케스트레이터 지시). 없으면 기존대로 + 0.95 고정(벡터 선이 유일한 입 모양 전달자이므로 작은 화면 보조로 낮추면 안 된다). */ + const lineOpacity = rig.lipTexture ? 0.95 * clamp((sizeScale - 1) / 0.5, 0, 1) : 0.95; + + /* 혀 그늘(§5.4 렌더러 규칙): open>0.45이면 입 안 아래쪽에 밝은 적갈색 반달을 둔다. + Cl(아랫입술 안쪽 경계) 바로 위 얇은 띠(모서리 제외), 입 안 clip 안에서만 보인다. */ + const tongueOpacity = clamp((v.mouthOpen - 0.45) / 0.15); + const clBand = sampleCurveX(Lc[0] + 0.2 * W, Rc[0] - 0.2 * W, 8, ClAt); + const tongueTop = clBand.map((p) => [p[0], p[1] - 0.3 * gapHeight] as Point); + const tongueBottom = clBand.map((p) => [p[0], p[1] - 0.04 * gapHeight] as Point); + const tongueShadowD = + tongueOpacity > 0 + ? [ + ...tongueTop.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`), + ...[...tongueBottom].reverse().map((p) => `L ${p[0]} ${p[1]}`), + "Z", + ].join(" ") + : ""; + + /* 입술 결 띠 변형(§8.4 2단계-B-1d-R7). rig.lipTexture가 없으면 빈 배열(렌더러가 + 대체 경로로 단색만 쓴다). */ + const lipStrips = computeLipStripTransforms(rig, v); + + /* 볼 불룩(§8.4, 2026-10-01 추가): b = max(mouthPress, mouthRound) — ㅁ·ㅂ·ㅗ·ㅜ에서 + 입 옆 볼이 볼록해진다. 입꼬리마다 바깥 (±16,−4)에 흐린 잉크 타원, 그 위 (±4,−8)에 + 종이색 하이라이트. */ + const puffB = clamp(Math.max(v.mouthPress, v.mouthRound)); + const puffOpacity = 0.1 * puffB; + const cheekPuffLeft: CheekPuff = { + cx: Lc[0] - 16, + cy: Lc[1] - 4, + highlightCx: Lc[0] - 4, + highlightCy: Lc[1] - 8, + opacity: puffOpacity, + }; + const cheekPuffRight: CheekPuff = { + cx: Rc[0] + 16, + cy: Rc[1] - 4, + highlightCx: Rc[0] + 4, + highlightCy: Rc[1] - 8, + opacity: puffOpacity, + }; + + /* R8: lipTexture가 없는 리그만 예전 벡터 입술 채움·그늘·쐐기 경로를 쓴다. */ + const vectorLipFallback = rig.lipTexture ? undefined : buildVectorLipFallback(shape, CuAt, ClAt, sizeScale, v.chinRaise); + + return { + lineD, + lineOpacity, + innerD, + innerOpacity, + teethD, + teethFillColor, + teethShadowD, + tongueShadowD, + tongueShadowOpacity: tongueOpacity, + lipStripsUpper: lipStrips?.upper ?? [], + lipStripsLower: lipStrips?.lower ?? [], + cornerLeft: Lc, + cornerRight: Rc, + gapHeight, + cheekPuffLeft, + cheekPuffRight, + vectorLipFallback, + }; +} + +/* ── 하관 띠 변형(§8.4 2026-10-01 추가: "말하면 코도 들썩이고... 턱이 만든다") ───── + 입이 벌어지는 양의 대부분은 턱이 만든다. 리그 jaw(head·faceDetail에서 잘라 낸 + 조각)를 y_n부터 chinTip.y+30까지 가로 띠 JAW_STRIP_COUNT개로 나눠 세로 이동· + 배율만 준다(가로는 항등, 이웃 띠와 경계가 이어져 찢어지지 않는다). 렌더러가 각 + 띠를 faceOval과 교차한 영역에만 그린다("띠 clip = 띠 사각형 ∩ 얼굴 윤곽"). */ + +/** R10(§8.4 개정, out5 반려 대응): 20 → 12. 잘라 낸 작은 조각(rig.jaw)을 쓰면서 + 전체 이미지를 띠마다 다시 그리던 비용이 사라져 띠 수를 줄여도 경계가 덜 보인다. */ +export const JAW_STRIP_COUNT = 12; + +/** 띠 하나의 세로 아핀 변환(가로는 항등이라 렌더러는 matrix(1 0 0 scaleY 0 translateY)로 + 쓴다). 정적 사각형(경계 겹침 포함)은 jawWarpStripRects가 채널과 무관하게 따로 낸다 + (lipStripClipRect·computeLipStripTransforms의 정적/매 프레임 분리와 같은 패턴). */ +export interface JawWarpStrip { + scaleY: number; + translateY: number; +} + +export interface JawWarpLayer { + strips: JawWarpStrip[]; + /** max|f| < 0.3px(§8.4)이면 true — 렌더러가 띠를 숨기고 원본만 그린다(쉬는 동안 비용 0). */ + negligible: boolean; +} + +export interface JawWarpFrame { + head: JawWarpLayer | null; + faceDetail: JawWarpLayer | null; + /** 보고용: 턱 끝 변위 Jc(px, 아래가 +). */ + chinShiftPx: number; +} + +/** 띠 범위(y_n..chinTip.y+30)는 rig 랜드마크만으로 정해지고 채널과 무관하다. */ +function jawWarpRange(rig: LinocutRig): { yFrom: number; yTo: number } { + return { yFrom: rig.landmarks.noseTip[1] - 50, yTo: rig.landmarks.chinTip[1] + 30 }; +} + +/** 하관 띠 변형의 위쪽 경계 y_n(§8.4). 이 아래(y > y_n)의 얼굴 벡터 점·faceDetail만 + 변위장 f(y)를 받는다 — 렌더러가 faceDetail 원본을 "변형 영역을 빼고" 그릴 때 + 쓰는 상수 경계이기도 하다(R10). */ +export function jawWarpUpperBoundaryY(rig: LinocutRig): number { + return jawWarpRange(rig).yFrom; +} + +/** R10(§8.4 개정, out5 jaw-zoom 반려 대응): 띠 하나당 위아래로 이만큼 넓혀 이웃 + 띠와 겹친다(겹침 총량 JAW_STRIP_OVERLAP, lipStripClipRect와 같은 "1px 겹침" + 패턴). 직전 라운드의 흐린 마스크(feGaussianBlur stdDeviation 4, margin 12)는 + 경계 이음매를 고치지 못했고 비용만 컸다(가로세로 canvas 전체 크기의 흐림 필터를 + 띠마다 새로 태운다) — 원인은 이음매가 아니라 원본 faceDetail이 변형 영역에서 + 그대로 비쳐 보이는 것(아래 buildJawWarpLayer 주석)이었으므로, 흐림을 걷어내고 + 1px 겹침 딱딱한 clip으로 되돌린다. */ +const JAW_STRIP_OVERLAP = 1; + +/** 레이어(rig.jaw.head·rig.jaw.detail 조각, 또는 rig.jaw가 없는 리그의 대체 경로로 + 전체 head·faceDetail) 하나의 띠 JAW_STRIP_COUNT개의 정적 사각형(레이어 로컬 + 좌표, 위아래로 JAW_STRIP_OVERLAP만큼 넓혀 이웃 띠와 겹친다). 채널과 무관해 + 렌더러가 마운트 시 한 번만 계산해 에 쓴다(lipStripClipRect와 같은 + 역할). 레이어 범위 밖(원본 픽셀이 없는 범위)이면 null. */ +export function jawWarpStripRects(rig: LinocutRig, layer: RigLayer): Array<{ y: number; h: number }> | null { + const { yFrom, yTo } = jawWarpRange(rig); + const top = Math.max(yFrom, layer.y); + const bottom = Math.min(yTo, layer.y + layer.h); + if (bottom <= top) return null; + const sliceH = (bottom - top) / JAW_STRIP_COUNT; + const overlap = JAW_STRIP_OVERLAP; + const out: Array<{ y: number; h: number }> = []; + for (let i = 0; i < JAW_STRIP_COUNT; i++) { + const y0 = top + i * sliceH; + out.push({ y: y0 - overlap / 2, h: sliceH + overlap }); + } + return out; +} + +/** 세로 변위장 f(y)(§8.4): y≤y_n은 0, y_n→T.y는 smoothstep 0→−U, T.y→B.y는 + smoothstep −U→+Jl, B.y→C.y+20은 smoothstep Jl→Jc, 그 아래는 Jc. */ +function jawDisplacementField(yNose: number, tY: number, bY: number, cY: number, u: number, jl: number, jc: number) { + const bottomEdge = cY + 20; + return (y: number): number => { + if (y <= yNose) return 0; + if (y <= tY) return lerp(0, -u, smoothstep(yNose, tY, y)); + if (y <= bY) return lerp(-u, jl, smoothstep(tY, bY, y)); + if (y <= bottomEdge) return lerp(jl, jc, smoothstep(bY, bottomEdge, y)); + return jc; + }; +} + +/** 레이어 하나의 띠 JAW_STRIP_COUNT개의 매 프레임 세로 변환. 범위는 + jawWarpStripRects와 같은 top/bottom(레이어로 자른 y_n..chinTip.y+30)이다. */ +function buildJawWarpLayer(layer: RigLayer, f: (y: number) => number, yFrom: number, yTo: number): JawWarpLayer | null { + const top = Math.max(yFrom, layer.y); + const bottom = Math.min(yTo, layer.y + layer.h); + if (bottom <= top) return null; + const sliceH = (bottom - top) / JAW_STRIP_COUNT; + const strips: JawWarpStrip[] = []; + let maxAbsF = 0; + for (let i = 0; i < JAW_STRIP_COUNT; i++) { + const y0 = top + i * sliceH; + const y1 = y0 + sliceH; + const f0 = f(y0); + const f1 = f(y1); + maxAbsF = Math.max(maxAbsF, Math.abs(f0), Math.abs(f1)); + const scaleY = (y1 + f1 - (y0 + f0)) / (y1 - y0 || 1); + const translateY = y0 + f0 - y0 * scaleY; + strips.push({ scaleY, translateY }); + } + return { strips, negligible: maxAbsF < 0.3 }; +} + +/** §8.4 하관 띠 변형: 변위장 f와 head·faceDetail 띠 변환을 함께 낸다. gapHeight는 + buildMouth의 현재 틈 높이(§5.4 R6b 반영 값, "g는 현재 입 틈 높이")를 그대로 + 받는다(중복 계산 방지). R10: 띠의 원본은 rig.jaw의 잘라 낸 조각(head·detail)을 + 쓴다 — 전체 1005×1566 이미지를 띠마다 다시 그리던 out5 비용 문제(long task + 14/7.3s)의 원인이었다. rig.jaw가 없는 리그는 기존처럼 전체 head·faceDetail을 + 쓴다(대체 경로). faceDetail은 대부분 투명한 덧칠 레이어라 이 띠가 켜진 동안 + 원본 faceDetail을 그대로 함께 그리면 변형된 사본과 겹쳐 턱선이 원래 자리와 + 내려간 자리에 두 겹(점선)으로 보인다(out4·out5 반려 사유) — 이 값(negligible)을 + 렌더러가 읽어 원본 faceDetail을 "변형 영역(y > y_n, jawWarpUpperBoundaryY 아래)을 + 빼고" 그릴지 전체를 그릴지 고른다(렌더러 쪽 책임, 이 함수는 띠 변환만 낸다). */ +function computeJawWarp(rig: LinocutRig, v: ChannelVector, gapHeight: number): { frame: JawWarpFrame; f: (y: number) => number } { + const T = toPoint(rig.landmarks.upperLipTop); + const B = toPoint(rig.landmarks.lowerLipBottom); + const C = toPoint(rig.landmarks.chinTip); + const { yFrom: yNose, yTo } = jawWarpRange(rig); + const u = 0.7 * 0.25 * gapHeight + 1.5 * v.mouthStretch; + const jl = 0.8 * 0.75 * gapHeight; + const jc = 1.3 * jl; + const f = jawDisplacementField(yNose, T[1], B[1], C[1], u, jl, jc); + const headSource = rig.jaw?.head ?? rig.layers.head; + const faceDetailLayer = rig.layers.faceDetail; + const faceDetailSource = rig.jaw?.detail ?? faceDetailLayer; + const frame: JawWarpFrame = { + head: buildJawWarpLayer(headSource, f, yNose, yTo), + faceDetail: faceDetailLayer && faceDetailSource ? buildJawWarpLayer(faceDetailSource, f, yNose, yTo) : null, + chinShiftPx: jc, + }; + return { frame, f }; +} + +/* ── 프레임 조립 ───────────────────────────────────────────────────── */ + +export interface SweatDrop { + cx: number; + cy: number; + r: number; +} + +export interface LinocutFrame { + transforms: LinocutTransforms; + eyeLeft: EyeGeometry; + eyeRight: EyeGeometry; + browLeft: BrowGeometry; + browRight: BrowGeometry; + glabella: WrinkleSet; + /** 안쪽 올림 주름(AU1, 비탄·슬픔·불안) — 좌우 각 3획 무리. */ + foreheadInner: WrinkleSet; + /** 전체 올림 주름(AU1+2, 놀람·기대) — 두 꼭짓점을 가로지르는 긴 호 3획. */ + foreheadWhole: WrinkleSet; + mouth: MouthGeometry; + nasolabial: WrinkleSet; + chinCrease: WrinkleSet; + /** 하관 띠 변형(§8.4 2026-10-01 추가). */ + jawWarp: JawWarpFrame; + /** 얼굴 그룹 clip 다각형 points(R10: y>y_n 점에 하관 띠 변위 f(y) 반영, negligible이면 + 원본 얼굴 윤곽과 같은 값). */ + faceOvalWarpedPointsAttr: string; + /** 창백 마스크 다각형 points(위와 같은 이유로 f(y) 반영). */ + pallorMaskWarpedPointsAttr: string; + /** 아랫입술 아래 그늘 조각(rig.lipTexture.shadow)의 세로 이동값(§8.4: 턱 변위 + f(그늘 중심 y)만 받는다, 가로 배율 없음). shadow가 없는 리그는 0(렌더러가 + 애초에 그리지 않아 값이 쓰이지 않는다). */ + lipShadowTranslateY: number; + cheekLeftOpacity: number; + cheekRightOpacity: number; + pallorColorOpacity: number; + pallorWhiteOpacity: number; + sweatOpacity: number; + sweatDrops: [SweatDrop, SweatDrop]; +} + +const FOREHEAD_STOPS: Array = [ + [0, 0.4], + [0.5, 1.1], + [1, 0.4], +]; + +/** R10(§8.4 팔자 주름 개정, out4·out5 반려 대응): 반두께 1.1 → 0.3(이전 1.3 → 0.4는 + 코 옆 높은 곳에서 시작하는 균일한 가는 선이 입을 감싸는 괄호 아치로 떠 보였다). */ +const NASOLABIAL_STOPS: Array = [ + [0, 1.1], + [1, 0.3], +]; + +/** 안쪽 올림 주름 한쪽 3획(§8.4 2단계-B-1d): 현재 안쪽 끝·꼭짓점 사이(안쪽 65%·꼭짓점 + 35%) 위, 맨 아래가 안쪽 끝보다 22px 위·간격 11px·길이 58/48/38(아래가 김), 미간 + 쪽 끝이 4px 더 높다. */ +function browInnerClusterPaths(innerP: Point, peakP: Point, side: "left" | "right", sizeScale: number, seedBase: number): string[] { + const anchorX = innerP[0] * 0.65 + peakP[0] * 0.35; + const stops = FOREHEAD_STOPS.map(([t, hw]) => [t, hw * sizeScale] as const); + return [58, 48, 38].map((len, idx) => { + const baseY = innerP[1] - 22 - idx * 11; + const half = len / 2; + const leftY = side === "left" ? baseY : baseY - 4; + const rightY = side === "left" ? baseY - 4 : baseY; + const anchors: Point[] = [ + [anchorX - half, leftY], + [anchorX, baseY - 2], + [anchorX + half, rightY], + ]; + const wavy = wavyRibbonPoints(anchors, 6, seedBase + idx * 7, 1.5, 1.5); + return ribbonFromWavyPoints(wavy, stops); + }); +} + +/** 전체 올림 주름 한 획(§8.4 2단계-B-1d): 두 꼭짓점을 가로지르는 가운데 5px 솟은 호, + 2~3토막(틈 6~12%, 시드 고정). */ +function wholeRaiseArcPaths(peakLeft: Point, peakRight: Point, dy: number, sizeScale: number, seed: number): string[] { + const cx = (peakLeft[0] + peakRight[0]) / 2; + const halfW = Math.abs(peakRight[0] - peakLeft[0]) * 0.42; + const baseY = Math.min(peakLeft[1], peakRight[1]) - dy; + const anchors: Point[] = [ + [cx - halfW, baseY], + [cx, baseY - 5], + [cx + halfW, baseY], + ]; + const wavy = wavyRibbonPoints(anchors, 8, seed, 1.5, 1.5); + const stops = FOREHEAD_STOPS.map(([t, hw]) => [t, hw * sizeScale] as const); + return seededSegmentRanges(seed + 1).map(([tFrom, tTo]) => + ribbonFromWavyPoints( + wavy.filter((p) => p.t >= tFrom && p.t <= tTo), + stops, + ), + ); +} + +export function computeLinocutFrame(rig: LinocutRig, v: ChannelVector, sizeScale = 1): LinocutFrame { + const transforms = computeTransforms(rig, v); + + const eyeLeft = buildEye( + rig.landmarks.eyeLeft, + { open: v.eyeOpenL, smile: v.eyeSmileL, lidTension: v.lidTension, gazeX: v.gazeX, gazeY: v.gazeY, pupilSize: v.pupilSize, tear: v.tear }, + false, + sizeScale, + ); + const eyeRight = buildEye( + rig.landmarks.eyeRight, + { open: v.eyeOpenR, smile: v.eyeSmileR, lidTension: v.lidTension, gazeX: v.gazeX, gazeY: v.gazeY, pupilSize: v.pupilSize, tear: v.tear }, + true, + sizeScale, + ); + + const browLeft = buildBrow(rig.landmarks.browLeft, { y: v.browLY, angle: v.browLAngle, x: v.browLX, form: v.browForm }, "left", sizeScale); + const browRight = buildBrow(rig.landmarks.browRight, { y: v.browRY, angle: v.browRAngle, x: v.browRX, form: v.browForm }, "right", sizeScale); + + /* 미간 주름: 눈썹 안쪽 끝 중점 위, 짧은 수직 획 2개(변경 없음). */ + const browInnerLeft = toPoint(rig.landmarks.browLeft.inner); + const browInnerRight = toPoint(rig.landmarks.browRight.inner); + const glabellaCx = (browInnerLeft[0] + browInnerRight[0]) / 2; + const glabellaCy = (browInnerLeft[1] + browInnerRight[1]) / 2 - 6; + const glabellaOpacity = clamp( + Math.max(0, -v.browLX, -v.browRX) * 1.1 + Math.max(0, -v.browLAngle, -v.browRAngle) * 0.6, + 0, + 0.85, + ); + const glabella: WrinkleSet = { + paths: [ + `M ${glabellaCx - 5} ${glabellaCy - 7} L ${glabellaCx - 4} ${glabellaCy + 7}`, + `M ${glabellaCx + 5} ${glabellaCy - 7} L ${glabellaCx + 4} ${glabellaCy + 7}`, + ], + opacity: glabellaOpacity, + strokeWidth: 1.2 * sizeScale, + }; + + /* 이마 주름 두 종류(§8.4 2단계-B-1d 개정: 같은 높이 곧은 평행선 3줄이 바코드처럼 + 보인 3차 판정 대응). 안쪽 올림(AU1)은 좌우 각 3획 무리, 전체 올림(AU1+2)은 + 두 꼭짓점을 가로지르는 긴 호 3획을 2~3토막. 둘 다 현재(채널 반영 후) 눈썹 + 좌표를 따라가고, 모든 획에 사인 물결을 준다. */ + const browAngleAvg = (v.browLAngle + v.browRAngle) / 2; + const browYAvg = (v.browLY + v.browRY) / 2; + const foreheadInnerOpacity = clamp((browAngleAvg - 0.3) * 1.4, 0, 0.75); + const foreheadInner: WrinkleSet = { + paths: [ + ...browInnerClusterPaths(browLeft.innerP, browLeft.peakP, "left", sizeScale, 0), + ...browInnerClusterPaths(browRight.innerP, browRight.peakP, "right", sizeScale, 500), + ], + opacity: foreheadInnerOpacity, + }; + + const foreheadWholeOpacity = clamp((browYAvg - 0.35) * 1.6, 0, 0.7); + const foreheadWhole: WrinkleSet = { + paths: [30, 42, 54].flatMap((dy, idx) => wholeRaiseArcPaths(browLeft.peakP, browRight.peakP, dy, sizeScale, 700 + idx * 20)), + opacity: foreheadWholeOpacity, + }; + + const mouth = buildMouth(rig, v, sizeScale); + + /* 하관 띠 변형(§8.4 2026-10-01 추가): 입 틈 높이(mouth.gapHeight, §5.4 R6b 반영 + 값)로 변위장 f(y)를 정한다. 입을 제외한 얼굴 벡터 점 중 y>y_n인 점(팔자·턱 + 주름 등)에 f(y)를 더한다. */ + const jawWarp = computeJawWarp(rig, v, mouth.gapHeight); + const shiftY = (p: Point): Point => [p[0], p[1] + jawWarp.f(p[1])]; + + /* R10(§8.4 개정): 얼굴 그룹 clip 다각형·창백 마스크 다각형의 점에도 f(y)를 + 더한다 — 얼굴 윤곽이 턱과 함께 내려가야 위 shiftY로 이미 옮겨진 벡터 부위 + (팔자·턱 주름 등)와 faceDetail 띠가 원래(정지) 윤곽에서 잘리지 않는다. + head 띠의 negligible을 전체 대표값으로 쓴다(head는 항상 존재하는 필수 + 레이어라 buildJawWarpLayer가 null을 내는 경우가 사실상 없다). negligible이면 + 원본 점을 그대로 쓴다(§8.4 "negligible이면 원래 값을 쓴다"). */ + const jawNegligible = jawWarp.frame.head?.negligible ?? true; + const faceOvalBase = rig.faceOval.map(toPoint); + const faceOvalWarpedPointsAttr = formatPointsAttr(jawNegligible ? faceOvalBase : warpPointsY(faceOvalBase, jawWarp.f)); + const pallorMaskBase = shrinkPointsToward(rig.faceOval, 0.96); + const pallorMaskWarpedPointsAttr = formatPointsAttr(jawNegligible ? pallorMaskBase : warpPointsY(pallorMaskBase, jawWarp.f)); + + /* 아랫입술 아래 그늘 조각(§8.4 "질감" 개정, rig.lipTexture.shadow): 턱 변위 + f(그늘 중심 y)만큼만 세로로 옮긴다(가로 배율 없음 — out5 판정: 아랫입술 + 스프라이트에 붙여 두자 ㅗ에서 W/W0로 함께 압축되어 짙은 직사각형이 됐다. + 턱 피부 그늘은 입이 좁아져도 줄지 않는다). shadow가 없으면 값은 쓰이지 않는다. */ + const lipShadowTranslateY = rig.lipTexture?.shadow + ? jawWarp.f(rig.lipTexture.shadow.y + rig.lipTexture.shadow.h / 2) + : 0; + + /* 팔자 주름: 코 날개 옆 → 현재 입꼬리, 바깥으로 10px 부푼 호(리본, 반두께 1.3→0.4). + 수평 직선 금지(1차 반려 대응). 발화 반응(§8.4 2026-10-01): mouthStretch·mouthOpen도 + 불투명도에 더한다(ㅣ·ㅔ와 크게 벌린 입에서 깊어진다). */ + const noseTip = toPoint(rig.landmarks.noseTip); + /* R10(§8.4 팔자 주름 개정, out4·out5 반려 대응): 불투명도 상한 0.8 → 0.5로 낮춘다. */ + const nasolabialOpacity = clamp((v.mouthForm - 0.15) * 1.6 + 0.35 * v.mouthStretch + 0.2 * v.mouthOpen, 0, 0.5); + /* R10 개정: 시작 (noseTip.x∓40, noseTip.y+28), 끝 (corner.x에서 바깥으로 12, + corner.y+2), 바깥으로 6px 부푼 호. R9 좌표(시작 노즈팁 옆 38·8, 끝 입꼬리 밖 + 10·6, 부풂 8)는 여전히 joy·warm에서 입을 감싸는 괄호 아치로 떠 보였다(out4·out5 + 반려). 시작점을 코 옆 더 낮고 안쪽으로, 끝점을 입꼬리에 더 가깝게 당겨 짧고 + 낮은 주름으로 바꾼다. */ + const nasoLeftStart: Point = shiftY([noseTip[0] - 40, noseTip[1] + 28]); + const nasoLeftEnd: Point = shiftY([mouth.cornerLeft[0] - 12, mouth.cornerLeft[1] + 2]); + const nasoLeftMid: Point = [(nasoLeftStart[0] + nasoLeftEnd[0]) / 2 - 6, (nasoLeftStart[1] + nasoLeftEnd[1]) / 2]; + const nasoRightStart: Point = shiftY([noseTip[0] + 40, noseTip[1] + 28]); + const nasoRightEnd: Point = shiftY([mouth.cornerRight[0] + 12, mouth.cornerRight[1] + 2]); + const nasoRightMid: Point = [(nasoRightStart[0] + nasoRightEnd[0]) / 2 + 6, (nasoRightStart[1] + nasoRightEnd[1]) / 2]; + const nasolabialStops = NASOLABIAL_STOPS.map(([t, hw]) => [t, hw * sizeScale] as const); + const nasoLeft = curveRibbonData([nasoLeftStart, nasoLeftMid, nasoLeftEnd], nasolabialStops, 6); + const nasoRight = curveRibbonData([nasoRightStart, nasoRightMid, nasoRightEnd], nasolabialStops, 6); + const nasolabial: WrinkleSet = { + paths: [polylineRibbon(nasoLeft.samples, nasoLeft.halfWidths), polylineRibbon(nasoRight.samples, nasoRight.halfWidths)], + opacity: nasolabialOpacity, + }; + + /* 턱 주름: (B.x, B.y+26) 중심 타원(가로 0.3W, 세로 12) 안에 짧게 굽은 획 6개. 중심에도 + 하관 띠 변형 f(y)를 더한다(§8.4). */ + const lowerLipBottom = toPoint(rig.landmarks.lowerLipBottom); + const chinCreaseCenter = shiftY([lowerLipBottom[0], lowerLipBottom[1] + 26]); + const chinW = Math.abs(mouth.cornerRight[0] - mouth.cornerLeft[0]); + const chinCreaseOpacity = clamp(v.chinRaise * 0.8, 0, 0.8); + const chinCrease: WrinkleSet = { + paths: ellipseCurvedStrokes(chinCreaseCenter, chinW * 0.3, 12, 6, 400), + opacity: chinCreaseOpacity, + strokeWidth: 1.2 * sizeScale, + }; + + const cheekOpacity = clamp(v.cheek) * 0.8; + + /* 창백: 얼굴 윤곽을 4% 줄이고 흐린 마스크를 컴포넌트가 적용한다(§8.4 개정). + 여기서는 불투명도만 낸다. */ + const pallorClamped = clamp(v.pallor); + const pallorColorOpacity = pallorClamped * 0.45; + const pallorWhiteOpacity = pallorClamped * 0.12; + + const sweatOpacity = clamp(v.sweat * 1.3); + const dropY = v.sweat * 8; + const browOuterLeft = toPoint(rig.landmarks.browLeft.outer); + const browOuterRight = toPoint(rig.landmarks.browRight.outer); + const sweatDrops: [SweatDrop, SweatDrop] = [ + { cx: browOuterLeft[0] - 15, cy: browOuterLeft[1] - 25 + dropY, r: 4 }, + { cx: browOuterRight[0] + 15, cy: browOuterRight[1] - 25 + dropY, r: 4 }, + ]; + + return { + transforms, + eyeLeft, + eyeRight, + browLeft, + browRight, + glabella, + foreheadInner, + foreheadWhole, + mouth, + nasolabial, + chinCrease, + jawWarp: jawWarp.frame, + faceOvalWarpedPointsAttr, + pallorMaskWarpedPointsAttr, + lipShadowTranslateY, + cheekLeftOpacity: cheekOpacity, + cheekRightOpacity: cheekOpacity, + pallorColorOpacity, + pallorWhiteOpacity, + sweatOpacity, + sweatDrops, + }; +} + +/** 점 배열을 SVG points/polygon용 문자열로. */ +function formatPointsAttr(points: Point[]): string { + return points.map((p) => `${p[0]},${p[1]}`).join(" "); +} + +/** 다각형을 중심 기준 factor배로 줄인 점 배열(창백 마스크용). */ +function shrinkPointsToward(points: readonly RigPoint[], factor: number): Point[] { + const cx = points.reduce((s, p) => s + p[0], 0) / points.length; + const cy = points.reduce((s, p) => s + p[1], 0) / points.length; + return points.map((p) => [cx + (p[0] - cx) * factor, cy + (p[1] - cy) * factor] as Point); +} + +/** R10(§8.4 하관 띠 변형 개정): 점의 y에 변위장 f(y)를 더한다(y≤y_n인 점은 + f(y)=0이라 그대로다). 얼굴 그룹 clip 다각형·창백 마스크 다각형에 쓴다 — + 얼굴 윤곽이 턱과 함께 내려가야 이미 f(y)로 옮겨진 벡터 부위(팔자·턱 주름 등)와 + faceDetail 띠가 원래(정지) 윤곽에서 잘리지 않는다. */ +function warpPointsY(points: Point[], f: (y: number) => number): Point[] { + return points.map((p) => [p[0], p[1] + f(p[1])] as Point); +} + +/** 얼굴 윤곽 다각형을 SVG points/polygon용 문자열로. */ +export function faceOvalPointsAttr(rig: LinocutRig): string { + return formatPointsAttr(rig.faceOval.map(toPoint)); +} + +/** 창백 마스크용으로 얼굴 중심 기준 4% 줄인 다각형 points 문자열. */ +export function shrunkFaceOvalPointsAttr(rig: LinocutRig, factor = 0.96): string { + return formatPointsAttr(shrinkPointsToward(rig.faceOval, factor)); +} + +/** 얼굴 윤곽을 바깥으로 distance만큼 넓힌 다각형(꼭짓점 법선 오프셋, R9 하관 띠 + clip 확장). 각 꼭짓점에서 인접한 두 변의 법선 평균 방향으로 밀어낸다. 오목한 + 지점에서도 "바깥"이 확실하도록 중심에서 멀어지는 쪽으로 부호를 맞춘다(다각형 + 감김 방향에 기대지 않는다). 얼굴 윤곽처럼 매끈하고 대체로 볼록한 도형에서는 + 이 근사로 충분하다. */ +function offsetPolygonOutward(points: readonly RigPoint[], distance: number): Point[] { + const n = points.length; + const cx = points.reduce((s, p) => s + p[0], 0) / n; + const cy = points.reduce((s, p) => s + p[1], 0) / n; + return points.map((p, i) => { + const prev = points[(i - 1 + n) % n]; + const next = points[(i + 1) % n]; + const n1 = normal(toPoint(prev), toPoint(p)); + const n2 = normal(toPoint(p), toPoint(next)); + let nx = n1[0] + n2[0]; + let ny = n1[1] + n2[1]; + const len = Math.hypot(nx, ny) || 1; + nx /= len; + ny /= len; + const dot = nx * (p[0] - cx) + ny * (p[1] - cy); + if (dot < 0) { + nx = -nx; + ny = -ny; + } + return [p[0] + nx * distance, p[1] + ny * distance]; + }); +} + +/** R11(§8.4 개정, out6 jaw-zoom 반려 대응): 띠 하나의 clip 다각형 = 띠 사각형 + (레이어 로컬 좌표, jawWarpStripRects가 낸 y·h) ∩ 얼굴 윤곽을 바깥으로 + distance만큼 넓힌 다각형(R9 offsetPolygonOutward, rig 정적). 이 다각형을 각 + 띠의 (자신의 세로 이동·배율 transform을 받는 바로 그 요소)에 clip으로 + 걸면 교집합 경계가 띠 내용과 함께 움직인다 — R9처럼 그룹 전체에 정적 clip을 + 걸면(out6 반려 사유) 화면에 고정된 경계가 내려간 턱선을 경계마다 가로질러 잘라 + 점선처럼 보였다. 여백은 처음 18px에서 10px로 줄였다(넓으면 목 해칭까지 함께 + 내려가 목에 이음매가 생긴다 — 원화 턱선 잉크 두께 약 6px만 들어가면 된다). + Sutherland–Hodgman으로 확장 얼굴 다각형을 띠 사각형(축 정렬, 볼록)으로 잘라 + 교집합 하나만 낸다(볼록 clip 창이라 조각나지 않는다). 채널과 무관해 마운트 시 + 한 번만 계산한다(jawWarpStripRects와 같은 "정적/매 프레임 분리" 패턴). 교집합이 + 없는 자리는 null(렌더러가 그 띠를 그리지 않는다). */ +export function jawWarpStripClipPolygons( + rig: LinocutRig, + layer: RigLayer, + rects: Array<{ y: number; h: number }>, + distance = 10, +): Array { + const expanded = offsetPolygonOutward(rig.faceOval, distance); + return rects.map((rect) => { + const clipped = clipPolygonToRect(expanded, layer.x, rect.y, layer.x + layer.w, rect.y + rect.h); + return clipped.length < 3 ? null : formatPointsAttr(clipped); + }); +} + +/** 닫힌 다각형을 "M x,y L x,y ... Z" path 부분경로로. evenodd 구멍 뚫기(아래 + jawWarpHeadHoleClipD)의 외곽·구멍 부분경로 조립에 쓴다. */ +function pointsToClosedPathD(points: Point[]): string { + return points.map((p, i) => `${i === 0 ? "M" : "L"} ${p[0]} ${p[1]}`).join(" ") + " Z"; +} + +/** R13(§8.4 개정, out9 jaw-zoom 반려 대응): 원본 head-image(미변형, 정적)는 지금까지 + clip 없이 항상 전체가 보였다 — 하관 띠(jaw-warp-head)가 켜진 동안에도 원본이 + 그대로 깔려 있어, 원본 턱선(제자리)과 띠가 옮겨 그린 턱선(내려간 자리)이 동시에 + 보여 턱선이 두 겹으로 갈라졌다(측정: DOM에서 head-image의 clip-path가 애초에 + null, 정적 전용/전체 전용 두 상태로 캡처하니 "제자리 끊긴 조각"과 "내려간 매끈한 + 곡선"이 각각 따로 나왔다). faceDetail(R10)이 이미 같은 문제를 "원본 전체 + (negligible)/변형 영역을 뺀 사본(그 외)" 두 장으로 나눠 풀었으니, head도 같은 + 패턴을 쓴다 — 다만 faceDetail과 달리 head-image는 얼굴 윤곽 밖(귀·머리카락·목)도 + 담고 있어 "y>y_n 전체를 뺀다"(faceDetail의 단순한 사각형 clip)를 그대로 쓰면 그 + 부분까지 잘려 나간다. 그래서 구멍은 "띠가 실제로 덮는 영역"(rects 합집합 y-범위 + ∩ 확장 얼굴 윤곽, jawWarpStripClipPolygons와 같은 확장 거리)만큼만 뚫는다 — + 귀·머리카락·목은 얼굴 윤곽 밖이라 구멍에 들지 않고 원본 그대로 남는다. evenodd + 한 path에 캔버스 전체 사각형(바깥 부분경로)과 구멍 다각형(안쪽 부분경로)을 함께 + 넣어 "사각형 − 구멍" 모양 하나를 낸다(다각형 두 개를 clipPath에 따로 넣으면 + 합집합이 되어 구멍이 뚫리지 않는다). rects가 비어 있으면(레이어가 하관 띠 범위 + 밖) null — 렌더러는 이때 구멍 없는 원본만 그린다. 채널과 무관해 마운트 시 한 번만 + 계산한다. + R14(§8.4 개정, out10 hair-edge 반려 대응): 구멍 위쪽 경계를 rects 최상단(y_n)이 + 아니라 입 중심(mouthCenter.y)으로 내린다. jawDisplacementField는 y_n부터 + upperLipTop까지 f(y)가 음수(콧날~인중이 위로 들린다) — 이 구간은 띠 내용이 + 아래(얼굴 윤곽이 좁아지는 자리)에서 끌어올려지므로, 띠의 확장 clip이 얼굴 윤곽 + 가장자리를 다 덮지 못하고 배경이 비치는 가는 틈이 남는다(볼 가장자리 흰 선). + 원본 head-image는 불투명 피부라 이 구간을 그대로 남겨 둬도 띠와 겹쳐 보이지 + 않는다(faceDetail처럼 잉크 선이 겹쳐 두 겹으로 보이는 레이어가 아니다) — 그래서 + 입 중심보다 위(코~인중)는 구멍을 뚫지 않고 원본을 남겨 틈을 메운다. 입 중심보다 + 아래(턱, f(y)가 양수로 전환하는 구간)만 여전히 구멍을 뚫어 두 겹 턱선을 막는다. */ +export function jawWarpHeadHoleClipD( + rig: LinocutRig, + rects: Array<{ y: number; h: number }>, + distance = 10, +): string | null { + if (rects.length === 0) return null; + const top = Math.max(Math.min(...rects.map((r) => r.y)), rig.landmarks.mouthCenter[1]); + const bottom = Math.max(...rects.map((r) => r.y + r.h)); + const expanded = offsetPolygonOutward(rig.faceOval, distance); + const hole = clipPolygonToRect(expanded, 0, top, rig.canvas.w, bottom); + if (hole.length < 3) return null; + const outer: Point[] = [ + [0, 0], + [rig.canvas.w, 0], + [rig.canvas.w, rig.canvas.h], + [0, rig.canvas.h], + ]; + return `${pointsToClosedPathD(outer)} ${pointsToClosedPathD(hole)}`; +} + +/** 볼록 다각형 clip 창(축 정렬 사각형)으로 다각형을 잘라 교집합 하나를 낸다 + (Sutherland–Hodgman). clip 창이 볼록이라 subject(얼굴 윤곽)가 오목해도 + 조각나지 않고 결과는 항상 단일 폐다각형이다(교집합이 없으면 빈 배열). */ +function clipPolygonToRect(poly: Point[], x0: number, y0: number, x1: number, y1: number): Point[] { + let out = poly; + out = clipHalfPlane(out, (p) => p[0] >= x0, (a, b) => intersectAtX(a, b, x0)); + out = clipHalfPlane(out, (p) => p[0] <= x1, (a, b) => intersectAtX(a, b, x1)); + out = clipHalfPlane(out, (p) => p[1] >= y0, (a, b) => intersectAtY(a, b, y0)); + out = clipHalfPlane(out, (p) => p[1] <= y1, (a, b) => intersectAtY(a, b, y1)); + return out; +} + +function clipHalfPlane(poly: Point[], inside: (p: Point) => boolean, intersect: (a: Point, b: Point) => Point): Point[] { + if (poly.length === 0) return poly; + const out: Point[] = []; + for (let i = 0; i < poly.length; i++) { + const curr = poly[i]; + const prev = poly[(i - 1 + poly.length) % poly.length]; + const currIn = inside(curr); + const prevIn = inside(prev); + if (currIn) { + if (!prevIn) out.push(intersect(prev, curr)); + out.push(curr); + } else if (prevIn) { + out.push(intersect(prev, curr)); + } + } + return out; +} + +function intersectAtX(a: Point, b: Point, x: number): Point { + const t = (x - a[0]) / (b[0] - a[0]); + return [x, a[1] + t * (b[1] - a[1])]; +} + +function intersectAtY(a: Point, b: Point, y: number): Point { + const t = (y - a[1]) / (b[1] - a[1]); + return [a[0] + t * (b[0] - a[0]), y]; +} diff --git a/apps/web/src/components/avatar/v3/linocutRig.ts b/apps/web/src/components/avatar/v3/linocutRig.ts new file mode 100644 index 0000000..885a144 --- /dev/null +++ b/apps/web/src/components/avatar/v3/linocutRig.ts @@ -0,0 +1,138 @@ +/* ===================================================================== + 리노컷 리그 v3 데이터 계약 — 결정문 §8.2. 오케스트레이터 작성(워커 수정 금지). + 페르소나 리그 데이터(`rigs/*Rig.ts`)는 자산 파이프라인 스크립트가 생성하고, + 렌더러는 이 타입만 읽는다. + 좌표는 모두 캔버스 픽셀(원점 좌상단, y 아래로 증가)이다. + 좌우 표기는 화면 기준이다: Left = 작은 x. 엔진 채널의 L/R(eyeOpenL 등)도 + 화면 기준이며 DebugFace와 같다. + ===================================================================== */ + +export type RigPoint = readonly [number, number]; + +/** [x, y, w, h] — 캔버스 좌표 사각형. */ +export type RigRect = readonly [number, number, number, number]; + +export interface RigLayer { + /** public 기준 URL(예: `/avatar/v3/p1/head.webp`). */ + href: string; + /** 이미지가 놓이는 캔버스 사각형. 파일 해상도와 무관하게 이 크기로 그린다. */ + x: number; + y: number; + w: number; + h: number; +} + +export interface RigEye { + inner: RigPoint; + outer: RigPoint; + upperLidTop: RigPoint; + lowerLidBottom: RigPoint; + iris: { center: RigPoint; radius: number }; +} + +/** 눈썹 잉크 띠의 **중심선** 위 세 점(결정문 §8.4). 띠의 윗가장자리·아래 모서리를 찍으면 + 수평에 가까운 원화 눈썹이 사선으로 그려져 중립이 화난 얼굴이 된다(2단계-B 3차 판정). */ +export interface RigBrow { + inner: RigPoint; + peak: RigPoint; + outer: RigPoint; +} + +export interface RigLandmarks { + eyeLeft: RigEye; + eyeRight: RigEye; + browLeft: RigBrow; + browRight: RigBrow; + noseTip: RigPoint; + mouthCornerLeft: RigPoint; + mouthCornerRight: RigPoint; + /** 윗입술 윗선 중앙. */ + upperLipTop: RigPoint; + /** 아랫입술 아랫선 중앙. */ + lowerLipBottom: RigPoint; + /** 두 입술이 만나는 선의 중앙. */ + mouthCenter: RigPoint; + chinTip: RigPoint; +} + +export type RigCrop = "portrait" | "bust" | "face"; + +/** 배경 색면·날씨 모티프 그룹 = 겉표정 그룹(`persona.ts` AVATAR_EXPRESSION_LIBRARY group). */ +export type BackdropGroup = "positive" | "negative" | "defensive" | "cognitive" | "energy"; + +export interface RigPalette { + ink: string; + sclera: string; + iris: string; + irisRing: string; + lipUpper: string; + lipLower: string; + mouthLine: string; + mouthInner: string; + teeth: string; + blush: string; + tear: string; + pallor: string; + paper: string; + motifPetal: string; + motifLeaf: string; +} + +/** 상징 모티프 스프라이트(결정문 §8.4). 좌표는 각 스프라이트 캔버스 픽셀이다. */ +export interface RigMotif { + bud: { + /** 봉오리 스프라이트 공통 캔버스. 4장 모두 줄기 밑동이 base에 맞춰져 있다. */ + canvas: { w: number; h: number }; + base: RigPoint; + closed: string; + half: string; + open: string; + droop: string; + }; + weather: { + canvas: { w: number; h: number }; + sprites: Record; + }; +} + +export interface LinocutRig { + schemaVersion: "vignette.avatar.v3.rig.v1"; + persona: string; + canvas: { w: number; h: number }; + layers: { + body: RigLayer; + head: RigLayer; + hairFront: RigLayer; + /** 눈두덩·다크서클·입가 음영과 점. 표정 부위 구멍은 피부로 메워져 있다. */ + faceDetail?: RigLayer; + }; + /** 종이결 타일(곱하기 합성). size는 캔버스 단위 타일 한 변. */ + grain?: { href: string; size: number }; + /** 원화 입술 결 스프라이트(결정문 §8.4 입). 입 선·입꼬리 잉크를 입술 결로 메우고 가장자리를 + 24px 연장했다. 렌더러가 현재 입술 면으로 clip하고 입술 기준점 이동·입 폭 비율만큼 옮기고 늘린다. + 없으면 입술을 palette 단색으로 채운다. */ + lipTexture?: { + upper: RigLayer; + lower: RigLayer; + /** 아랫입술 아래 그늘(원화 픽셀). 입 모양을 따라 가로로 줄지 않고 턱 변위만 따른다. */ + shadow?: RigLayer; + }; + /** 하관 띠 변형용으로 잘라 낸 head·faceDetail 조각(결정문 §8.4). 원본 레이어와 픽셀이 같다. */ + jaw?: { head: RigLayer; detail: RigLayer }; + pivots: { + /** 고개 회전 중심(목). */ + neck: RigPoint; + /** 몸 기울임·호흡 중심(하단 중앙). */ + body: RigPoint; + /** 얼굴 부위 시차·압축 중심. */ + face: RigPoint; + }; + crops: Record; + /** 얼굴 윤곽 다각형(표정 부위 clip). */ + faceOval: readonly RigPoint[]; + landmarks: RigLandmarks; + palette: RigPalette; + backdrop: Record; + /** 없으면 모티프를 그리지 않는다. */ + motif?: RigMotif; +} diff --git a/apps/web/src/components/avatar/v3/rigs/p1Rig.ts b/apps/web/src/components/avatar/v3/rigs/p1Rig.ts new file mode 100644 index 0000000..b3e1277 --- /dev/null +++ b/apps/web/src/components/avatar/v3/rigs/p1Rig.ts @@ -0,0 +1,104 @@ +/* 생성 파일 — docs/avatar-art/p1-linocut/scripts/export_rig.py 가 만든다. 손으로 고치지 않는다. */ +import type { LinocutRig } from "../linocutRig"; + +export const P1_LINOCUT_RIG: LinocutRig = { + schemaVersion: "vignette.avatar.v3.rig.v1", + persona: "P1", + canvas: { w: 1005, h: 1566 }, + layers: { + body: { href: "/avatar/v3/p1/body.webp", x: 0, y: 807, w: 1005, h: 759 }, + head: { href: "/avatar/v3/p1/head.webp", x: 111, y: 72, w: 849, h: 1049 }, + hairFront: { href: "/avatar/v3/p1/hair-front.webp", x: 260, y: 391, w: 491, h: 516 }, + faceDetail: { href: "/avatar/v3/p1/face-detail.webp", x: 284, y: 444, w: 437, h: 478 }, + }, + grain: { href: "/avatar/v3/p1/paper-grain.webp", size: 256 }, + lipTexture: { + upper: { href: "/avatar/v3/p1/lip-upper.webp", x: 404, y: 758, w: 182, h: 54 }, + lower: { href: "/avatar/v3/p1/lip-lower.webp", x: 404, y: 798, w: 182, h: 49 }, + shadow: { href: "/avatar/v3/p1/lip-shadow.webp", x: 414, y: 802, w: 162, h: 53 }, + }, + jaw: { + head: { href: "/avatar/v3/p1/jaw-head.webp", x: 260, y: 653, w: 500, h: 307 }, + detail: { href: "/avatar/v3/p1/jaw-detail.webp", x: 260, y: 653, w: 500, h: 307 }, + }, + motif: { + bud: { + canvas: { w: 280, h: 420 }, + base: [134.2, 399], + closed: "/avatar/v3/p1/motif/bud-closed.webp", + half: "/avatar/v3/p1/motif/bud-half.webp", + open: "/avatar/v3/p1/motif/bud-open.webp", + droop: "/avatar/v3/p1/motif/bud-droop.webp", + }, + weather: { + canvas: { w: 320, h: 200 }, + sprites: { + positive: "/avatar/v3/p1/motif/weather-positive.webp", + negative: "/avatar/v3/p1/motif/weather-negative.webp", + defensive: "/avatar/v3/p1/motif/weather-defensive.webp", + cognitive: "/avatar/v3/p1/motif/weather-cognitive.webp", + energy: "/avatar/v3/p1/motif/weather-energy.webp", + }, + }, + }, + pivots: { + neck: [500, 990], + body: [502, 1566], + face: [490, 660], + }, + crops: { + portrait: [0, 0, 1005, 1566], + bust: [0, 40, 1005, 1005], + face: [204.7, 360.6, 601.4, 601.4], + }, + faceOval: [[481, 392.8], [551.8, 393.6], [612.4, 402.3], [669.7, 421.4], [707.5, 450.3], [731.2, 487.1], [743.8, 526.1], [748.5, 573.7], [745.6, 619.2], [739.1, 666.9], [727.3, 716.7], [709.7, 769.5], [687, 813.6], [661.3, 846.5], [629.6, 874.9], [603.3, 894.4], [574.9, 912.1], [542.1, 926], [500.6, 929.7], [461.5, 924.7], [432.5, 910.1], [408, 891.9], [384.7, 872.6], [356.2, 844.9], [333.7, 812.9], [312.5, 770.3], [294.2, 718.1], [281, 668.9], [273, 622.3], [265.1, 577.6], [262.2, 530.6], [267.6, 491.4], [282.1, 455.1], [310, 426.1], [357.1, 405.8], [412.2, 395.7]], + landmarks: { + eyeLeft: { + inner: [430.9, 592.2], + outer: [339.1, 578.8], + upperLidTop: [376.3, 564.3], + lowerLidBottom: [381.1, 598.2], + iris: { center: [387.3, 579.1], radius: 21.4 }, + }, + eyeRight: { + inner: [552.8, 590.9], + outer: [648, 575.5], + upperLidTop: [607.1, 562.1], + lowerLidBottom: [603.9, 595], + iris: { center: [601.9, 576.2], radius: 21.3 }, + }, + browLeft: { inner: [450.6, 525.1], peak: [360.1, 510.4], outer: [313.4, 513.3] }, + browRight: { inner: [518.2, 521.7], peak: [626.8, 508.2], outer: [682.8, 510.7] }, + noseTip: [486.6, 713], + mouthCornerLeft: [422.1, 804.7], + mouthCornerRight: [567.5, 805.8], + upperLipTop: [489.8, 773.4], + lowerLipBottom: [493, 831.5], + mouthCenter: [491.6, 797.5], + chinTip: [500.6, 919.5], + }, + palette: { + ink: "#1E1F1F", + sclera: "#D8CEBD", + iris: "#4F3B2C", + irisRing: "#1E1F1F", + lipUpper: "#97684B", + lipLower: "#AB7857", + mouthLine: "#292421", + mouthInner: "#3B201B", + teeth: "#E9E0CF", + blush: "#C0624A", + tear: "#EEE5D3", + pallor: "#9AA3A6", + paper: "#EEE5D3", + motifPetal: "#D0A362", + motifLeaf: "#53626C", + }, + backdrop: { + cognitive: "#ECE3D1", + positive: "#F1DEC2", + negative: "#DCE0E2", + defensive: "#E6DAD3", + energy: "#E2E0D0", + }, +}; diff --git a/apps/web/src/pages/AvatarLab.tsx b/apps/web/src/pages/AvatarLab.tsx new file mode 100644 index 0000000..758393e --- /dev/null +++ b/apps/web/src/pages/AvatarLab.tsx @@ -0,0 +1,723 @@ +/* ===================================================================== + AvatarLab — 아바타 v3 표현 엔진 dev Lab (무인증). /dev/avatar-lab. + 실 서비스 라우트 아님 — 채널·표정·반응 클립·지문 파싱·엔진 상태를 + 눈으로 확인하는 도구다(결정문 §9 1단계 완료 기준). + ===================================================================== */ + +import { useEffect, useMemo, useRef, useState, type ChangeEvent } from "react"; +import DebugFace from "../components/avatar/v3/DebugFace"; +import LinocutAvatar from "../components/avatar/v3/LinocutAvatar"; +import LinocutMotif from "../components/avatar/v3/LinocutMotif"; +import { P1_LINOCUT_RIG } from "../components/avatar/v3/rigs/p1Rig"; +import type { LinocutRig } from "../components/avatar/v3/linocutRig"; +import { CHANNEL_IDS } from "../components/avatar/engine/channels"; +import { createAvatarEngine, type AvatarEngine, type DebugSnapshot } from "../components/avatar/engine/engine"; +import { REACTION_CLIPS, REACTION_CLIP_IDS, type ReactionClipId } from "../components/avatar/engine/clipCatalog"; +import { demeanorFor } from "../components/avatar/engine/demeanorDefaults"; +import { buildPerformance, type Performance, type PerformanceCue } from "../components/avatar/engine/performance"; +import { + buildSpeechTimeline, + currentViseme, + sampleSpeech, + type PhraseKind, + type SpeechShape, + type SpeechTimeline, + type VisemeId, +} from "../components/avatar/engine/lipsync"; +import { buildCoSpeechPlan, isStressPulseActive, sampleCoSpeech, type CoSpeechPlan } from "../components/avatar/engine/coSpeech"; +import { computeEnvelope, type SpeechEnvelope } from "../components/avatar/engine/speechEnvelope"; +import { AVATAR_EXPRESSION_LIBRARY, type AvatarExpression, type AvatarState } from "../components/avatar/persona"; +import "./avatar-lab.css"; + +const PERSONA_OPTIONS: Array<{ code: string; label: string }> = [ + { code: "P1", label: "P1 서연" }, + { code: "P2", label: "P2 민재" }, + { code: "P3", label: "P3 지우" }, + { code: "P4", label: "P4 하늘" }, + { code: "P5", label: "P5 도윤" }, + { code: "P6", label: "P6 하린" }, + { code: "P7", label: "P7 도현" }, +]; + +/** 리노컷 리그가 있는 페르소나만. 없는 페르소나는 debug 렌더러를 강제한다. */ +const LINOCUT_RIGS: Partial> = { P1: P1_LINOCUT_RIG }; + +type RendererMode = "linocut" | "debug"; +type FeedbackMode = "immersive" | "ambient" | "coached"; + +const STATE_OPTIONS: Array<{ id: AvatarState; label: string }> = [ + { id: "idle", label: "대기" }, + { id: "listening", label: "경청" }, + { id: "thinking", label: "생각" }, + { id: "speaking", label: "발화" }, +]; + +interface Clock { + now(): number; + pause(): void; + resume(): void; + isPaused(): boolean; +} + +function createClock(): Clock { + let offset = 0; + let paused = false; + let frozen = 0; + return { + now(): number { + return paused ? frozen : performance.now() - offset; + }, + pause(): void { + if (paused) return; + frozen = performance.now() - offset; + paused = true; + }, + resume(): void { + if (!paused) return; + offset = performance.now() - frozen; + paused = false; + }, + isPaused(): boolean { + return paused; + }, + }; +} + +/** 표시용: localMs 시점까지 시작한 가장 최근 구의 종류. 구 사이 휴지 중에는 그 직전 구를 보인다. */ +function currentPhraseKind(timeline: SpeechTimeline, localMs: number): PhraseKind | null { + let kind: PhraseKind | null = null; + for (const ph of timeline.phrases) { + if (ph.startMs > localMs) break; + kind = ph.kind; + } + return kind; +} + +function seedFromQuery(): number { + const raw = new URLSearchParams(window.location.search).get("seed"); + const parsed = raw === null ? NaN : Number(raw); + return Number.isFinite(parsed) ? parsed : 1; +} + +export default function AvatarLab() { + const seed = useMemo(seedFromQuery, []); + const clock = useRef(createClock()).current; + + const [personaCode, setPersonaCode] = useState("P1"); + const [avatarState, setAvatarState] = useState("idle"); + const [openness, setOpenness] = useState(0.4); + const [reducedMotion, setReducedMotion] = useState(false); + const [paused, setPaused] = useState(false); + + const [selectedExpression, setSelectedExpression] = useState("neutral"); + const [intensity, setIntensity] = useState(0.7); + const [mirrorClip, setMirrorClip] = useState(false); + + const rig = LINOCUT_RIGS[personaCode]; + const [rendererMode, setRendererMode] = useState(rig ? "linocut" : "debug"); + const [feedbackMode, setFeedbackMode] = useState("coached"); + const [linocutLoadState, setLinocutLoadState] = useState<"loading" | "ready" | "error">("loading"); + + useEffect(() => { + if (!rig) setRendererMode("debug"); + }, [rig]); + + const backdropGroup = useMemo( + () => AVATAR_EXPRESSION_LIBRARY.find((e) => e.id === selectedExpression)?.group ?? "cognitive", + [selectedExpression], + ); + + const [speechText, setSpeechText] = useState("(한숨) 그냥요. 별로 할 말 없어요."); + const [parsedCues, setParsedCues] = useState([]); + const [unmatched, setUnmatched] = useState([]); + const [snapshot, setSnapshot] = useState(null); + + const speechRafRef = useRef(0); + const meterRefs = useRef>({}); + + /* 발화층(립싱크) — 원시 SpeechShape·비짐 표시용. 채널 미터와 달리 매 프레임 갱신하지 + 않고(React state 금지, 결정문 §3) 아래 100ms interval에서 ref로 직접 쓴다. */ + const speechDisplayRef = useRef<{ viseme: VisemeId; shape: SpeechShape }>({ + viseme: "X", + shape: { open: 0, wide: 0, round: 0, press: 0 }, + }); + const speechMeterRefs = useRef<{ + viseme: HTMLElement | null; + open: HTMLElement | null; + wide: HTMLElement | null; + round: HTMLElement | null; + press: HTMLElement | null; + }>({ viseme: null, open: null, wide: null, round: null, press: null }); + + /* 발화 동반층(§5.5) 표시용 — 현재 구 종류·강세 박 여부. speechDisplayRef와 같은 이유로 + React state가 아니라 ref로 갱신하고 100ms interval에서 DOM에 직접 쓴다. */ + const coSpeechDisplayRef = useRef<{ phraseKind: PhraseKind | null; stressed: boolean }>({ phraseKind: null, stressed: false }); + const coSpeechMeterRefs = useRef<{ phraseKind: HTMLElement | null; stressed: HTMLElement | null }>({ + phraseKind: null, + stressed: null, + }); + + const audioCtxRef = useRef(null); + const audioSourceRef = useRef(null); + const audioFileInputRef = useRef(null); + const [audioFileName, setAudioFileName] = useState(null); + + const engine: AvatarEngine = useMemo( + () => createAvatarEngine({ demeanor: demeanorFor(personaCode), seed, reducedMotion }), + [personaCode, seed, reducedMotion], + ); + + useEffect(() => { + engine.setState(avatarState, clock.now()); + engine.setOpenness(openness); + engine.setSurface(selectedExpression, intensity); + }, [engine]); // eslint-disable-line react-hooks/exhaustive-deps + + useEffect(() => { + engine.setState(avatarState, clock.now()); + }, [engine, avatarState]); // eslint-disable-line react-hooks/exhaustive-deps + + useEffect(() => { + engine.setOpenness(openness); + }, [engine, openness]); + + useEffect(() => { + engine.setSurface(selectedExpression, intensity); + }, [engine, selectedExpression, intensity]); + + useEffect(() => { + setSnapshot(engine.debugSnapshot()); + const interval = window.setInterval(() => { + const v = engine.evaluate(clock.now()); + for (const id of CHANNEL_IDS) { + const cell = meterRefs.current[id]; + if (cell) cell.textContent = v[id].toFixed(2); + } + setSnapshot(engine.debugSnapshot()); + + const sd = speechDisplayRef.current; + const sm = speechMeterRefs.current; + if (sm.viseme) sm.viseme.textContent = sd.viseme; + if (sm.open) sm.open.textContent = sd.shape.open.toFixed(2); + if (sm.wide) sm.wide.textContent = sd.shape.wide.toFixed(2); + if (sm.round) sm.round.textContent = sd.shape.round.toFixed(2); + if (sm.press) sm.press.textContent = sd.shape.press.toFixed(2); + + const cd = coSpeechDisplayRef.current; + const cm = coSpeechMeterRefs.current; + if (cm.phraseKind) cm.phraseKind.textContent = cd.phraseKind ?? "-"; + if (cm.stressed) cm.stressed.textContent = cd.stressed ? "예" : "아니오"; + }, 100); + return () => window.clearInterval(interval); + }, [engine]); // eslint-disable-line react-hooks/exhaustive-deps + + useEffect(() => { + return () => { + cancelAnimationFrame(speechRafRef.current); + try { + audioSourceRef.current?.stop(); + } catch { + /* 이미 정지된 소스면 무시한다. */ + } + }; + }, []); + + function togglePause(): void { + if (clock.isPaused()) { + clock.resume(); + setPaused(false); + } else { + clock.pause(); + setPaused(true); + } + } + + function handleClipClick(id: ReactionClipId): void { + engine.triggerClip(id, clock.now(), { mirror: mirrorClip }); + } + + const ZERO_SPEECH_SHAPE: SpeechShape = { open: 0, wide: 0, round: 0, press: 0 }; + + function endSpeech(nowMs: number): void { + engine.setSpeechShape(null); + engine.setSpeechMotion(null); + engine.setState("listening", nowMs); + setAvatarState("listening"); + speechDisplayRef.current = { viseme: "X", shape: ZERO_SPEECH_SHAPE }; + coSpeechDisplayRef.current = { phraseKind: null, stressed: false }; + } + + /** timeline을 nowFn() 시계로 매 프레임 표본해 engine.setSpeechShape·setSpeechMotion에 흘려보낸다. + coSpeechPlan이 있으면 발화 동반층(§5.5)도 같은 프레임에 표본한다. */ + function runSpeechTimeline( + timeline: SpeechTimeline, + speechStartMs: number, + nowFn: () => number, + articulation: number, + coSpeechPlan: CoSpeechPlan | null, + envelope?: SpeechEnvelope, + ): void { + cancelAnimationFrame(speechRafRef.current); + const endMs = speechStartMs + timeline.totalDurationMs; + let prevLocalMs = -Infinity; + const step = () => { + const t = nowFn(); + if (t < speechStartMs) { + speechRafRef.current = requestAnimationFrame(step); + return; + } + if (t >= endMs) { + endSpeech(clock.now()); + return; + } + const localMs = t - speechStartMs; + const shape = sampleSpeech(timeline, localMs, articulation); + engine.setSpeechShape(shape); + speechDisplayRef.current = { viseme: currentViseme(timeline, localMs), shape }; + + if (coSpeechPlan) { + const sample = sampleCoSpeech(coSpeechPlan, localMs, prevLocalMs, envelope); + engine.setSpeechMotion(sample.delta); + if (sample.blinkNow) engine.requestSpeechBlink(clock.now()); + coSpeechDisplayRef.current = { + phraseKind: currentPhraseKind(timeline, localMs), + stressed: isStressPulseActive(coSpeechPlan, localMs), + }; + } + prevLocalMs = localMs; + + speechRafRef.current = requestAnimationFrame(step); + }; + speechRafRef.current = requestAnimationFrame(step); + } + + function playSpeech(): void { + const nowMs = clock.now(); + const { performance: perf, unmatched: um } = buildPerformance({ + text: speechText, + expression: selectedExpression, + intensity, + openness, + seed, + }); + const speech = demeanorFor(personaCode).speech; + const timeline = buildSpeechTimeline({ text: speechText, syllablesPerSec: speech.syllablesPerSec }); + const coSpeechPlan = buildCoSpeechPlan(timeline, speech, seed); + const speechStartMs = nowMs + 300; + setAvatarState("speaking"); + engine.setState("speaking", nowMs); + engine.playPerformance(perf, { speechStartMs, speechDurationMs: timeline.totalDurationMs }, nowMs); + setParsedCues(perf.cues); + setUnmatched(um); + runSpeechTimeline(timeline, speechStartMs, clock.now, speech.articulation, coSpeechPlan); + } + + function handleAudioFileChange(e: ChangeEvent): void { + setAudioFileName(e.target.files?.[0]?.name ?? null); + } + + /** "오디오 파일로 말하기" — 로컬 오디오를 디코드해 포락선을 만들고, 오디오 시계로 표본한다. */ + async function playSpeechWithAudioFile(): Promise { + const file = audioFileInputRef.current?.files?.[0]; + if (!file) return; + + const AudioContextCtor = + window.AudioContext ?? (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext; + if (!AudioContextCtor) return; + const ctx = audioCtxRef.current ?? new AudioContextCtor(); + audioCtxRef.current = ctx; + if (ctx.state === "suspended") await ctx.resume(); + + const arrayBuffer = await file.arrayBuffer(); + const audioBuffer = await ctx.decodeAudioData(arrayBuffer.slice(0)); + const channels: Float32Array[] = []; + for (let c = 0; c < audioBuffer.numberOfChannels; c++) channels.push(audioBuffer.getChannelData(c)); + const envelope = computeEnvelope(channels, audioBuffer.sampleRate); + + const nowMs = clock.now(); + const { performance: perf, unmatched: um } = buildPerformance({ + text: speechText, + expression: selectedExpression, + intensity, + openness, + seed, + }); + const speech = demeanorFor(personaCode).speech; + const timeline = buildSpeechTimeline({ text: speechText, syllablesPerSec: speech.syllablesPerSec, envelope }); + const coSpeechPlan = buildCoSpeechPlan(timeline, speech, seed); + + try { + audioSourceRef.current?.stop(); + } catch { + /* 이미 정지된 소스면 무시한다. */ + } + const source = ctx.createBufferSource(); + source.buffer = audioBuffer; + source.connect(ctx.destination); + audioSourceRef.current = source; + + const speechStartMs = nowMs + 300; + setAvatarState("speaking"); + engine.setState("speaking", nowMs); + engine.playPerformance(perf, { speechStartMs, speechDurationMs: timeline.totalDurationMs }, nowMs); + setParsedCues(perf.cues); + setUnmatched(um); + + const startAtCtx = ctx.currentTime + 0.3; + source.start(startAtCtx); + /* 오디오 시계 기준(초 → ms). Lab의 clock(performance.now() 기반)과는 별개 시계다 — + 재생 시작을 같은 300ms로 맞췄지만 독립 시계라 아주 긴 발화에서는 드리프트가 있을 수 있다. */ + const nowFn = () => (ctx.currentTime - startAtCtx) * 1000 + speechStartMs; + runSpeechTimeline(timeline, speechStartMs, nowFn, speech.articulation, coSpeechPlan, envelope); + } + + function playLeakTest(): void { + const nowMs = clock.now(); + const perf: Performance = { + surface: { expression: "neutral", intensity: 0.3 }, + leak: { expression: "sad", intensity: 0.8, atMs: 500, durationMs: 350 }, + cues: [], + }; + setAvatarState("listening"); + engine.setState("listening", nowMs); + engine.playPerformance(perf, { speechStartMs: nowMs, speechDurationMs: 1200 }, nowMs); + setParsedCues([]); + setUnmatched([]); + } + + return ( +