From 6d90787c1308c891bb869908c4c9207cac7b8043 Mon Sep 17 00:00:00 2001 From: Admin Date: Wed, 19 Aug 2026 00:06:21 +0330 Subject: [PATCH] improvement: FDI tooth chart overhauled. --- .cursor/rules/treatment-workspace.mdc | 2 +- .cursor/skills/treatment-workspace/SKILL.md | 7 +- AGENTS.md | 2 +- frontend/messages/en.json | 6 +- frontend/messages/fa.json | 6 +- frontend/messages/nl.json | 6 +- frontend/public/FDI_11.svg | 39 +++ frontend/public/FDI_12.svg | 33 ++ frontend/public/FDI_13.svg | 39 +++ frontend/public/FDI_14.svg | 39 +++ frontend/public/FDI_15.svg | 39 +++ frontend/public/FDI_16.svg | 1 + frontend/public/FDI_17.svg | 27 ++ frontend/public/FDI_18.svg | 27 ++ frontend/public/FDI_21.svg | 39 +++ frontend/public/FDI_22.svg | 33 ++ frontend/public/FDI_23.svg | 39 +++ frontend/public/FDI_24.svg | 39 +++ frontend/public/FDI_25.svg | 39 +++ frontend/public/FDI_26.svg | 1 + frontend/public/FDI_27.svg | 27 ++ frontend/public/FDI_28.svg | 27 ++ frontend/public/FDI_31.svg | 27 ++ frontend/public/FDI_32.svg | 27 ++ frontend/public/FDI_33.svg | 27 ++ frontend/public/FDI_34.svg | 27 ++ frontend/public/FDI_35.svg | 27 ++ frontend/public/FDI_36.svg | 27 ++ frontend/public/FDI_37.svg | 27 ++ frontend/public/FDI_38.svg | 27 ++ frontend/public/FDI_41.svg | 27 ++ frontend/public/FDI_42.svg | 27 ++ frontend/public/FDI_43.svg | 27 ++ frontend/public/FDI_44.svg | 27 ++ frontend/public/FDI_45.svg | 27 ++ frontend/public/FDI_46.svg | 27 ++ frontend/public/FDI_47.svg | 27 ++ frontend/public/FDI_48.svg | 27 ++ frontend/scripts/extract-tooth-svgs.mjs | 54 ++++ .../src/components/treatment/fdiToothScale.ts | 75 +++++ .../treatment/realisticToothAssets.ts | 261 +++++++++++++++ .../treatment/toothSelectionGroups.ts | 118 ++++++- .../components/ui/lab/CaseSheetFdiChart.tsx | 8 +- .../components/ui/treatment/FdiToothChart.tsx | 306 ++++++++++++------ .../components/ui/treatment/ToothGlyph.tsx | 130 +++++++- .../ui/treatment/TreatmentWorkspace.tsx | 56 ++++ frontend/src/styles/globals.css | 28 ++ 47 files changed, 1839 insertions(+), 146 deletions(-) create mode 100644 frontend/public/FDI_11.svg create mode 100644 frontend/public/FDI_12.svg create mode 100644 frontend/public/FDI_13.svg create mode 100644 frontend/public/FDI_14.svg create mode 100644 frontend/public/FDI_15.svg create mode 100644 frontend/public/FDI_16.svg create mode 100644 frontend/public/FDI_17.svg create mode 100644 frontend/public/FDI_18.svg create mode 100644 frontend/public/FDI_21.svg create mode 100644 frontend/public/FDI_22.svg create mode 100644 frontend/public/FDI_23.svg create mode 100644 frontend/public/FDI_24.svg create mode 100644 frontend/public/FDI_25.svg create mode 100644 frontend/public/FDI_26.svg create mode 100644 frontend/public/FDI_27.svg create mode 100644 frontend/public/FDI_28.svg create mode 100644 frontend/public/FDI_31.svg create mode 100644 frontend/public/FDI_32.svg create mode 100644 frontend/public/FDI_33.svg create mode 100644 frontend/public/FDI_34.svg create mode 100644 frontend/public/FDI_35.svg create mode 100644 frontend/public/FDI_36.svg create mode 100644 frontend/public/FDI_37.svg create mode 100644 frontend/public/FDI_38.svg create mode 100644 frontend/public/FDI_41.svg create mode 100644 frontend/public/FDI_42.svg create mode 100644 frontend/public/FDI_43.svg create mode 100644 frontend/public/FDI_44.svg create mode 100644 frontend/public/FDI_45.svg create mode 100644 frontend/public/FDI_46.svg create mode 100644 frontend/public/FDI_47.svg create mode 100644 frontend/public/FDI_48.svg create mode 100644 frontend/scripts/extract-tooth-svgs.mjs create mode 100644 frontend/src/components/treatment/fdiToothScale.ts create mode 100644 frontend/src/components/treatment/realisticToothAssets.ts diff --git a/.cursor/rules/treatment-workspace.mdc b/.cursor/rules/treatment-workspace.mdc index a22441a..c8fb388 100644 --- a/.cursor/rules/treatment-workspace.mdc +++ b/.cursor/rules/treatment-workspace.mdc @@ -9,7 +9,7 @@ alwaysApply: false - **Browse mode** (`selectedPreviewId` set): preview only; banner + **Load into workspace**; form unchanged until load. - **Current draft** preview: heading “Current draft”; no load button while editing live. - **Entry wizard:** Teeth → Content → Lab via `WizardStepper` (not detail chips). Lab step only for lab-dependent (prosthesis) active detail; entering Lab auto-opens shipment draft. Add detail ungated. Appointment purpose does not gate type or Next. Detail-chip switches reset to Teeth unless `pendingEntryStepRef` requests Lab (shipments rail open). -- **Tooth groups:** Shift+same-arch range = connected bridge (linked dots, one shipment row); overlapping Shift ranges merge by union. Plain click = add/remove singles; click in a bridge cuts that tooth out as a selected single (end → remainder stays connected; middle → two bridges). Never 1-tooth connected. `toothSelectionGroups.ts`; prune lab `toothProsthesis` on change. Cases/Tasks merge by prosthesis type after send. +- **Tooth groups:** Adjacent selected teeth show an empty circle between them — click to connect / filled to disconnect; Shift+range selects only (empty circles; overlap absorbs as singles; midline OK). Plain click select/deselect (deselect splits bridges). Never 1-tooth connected. `toothSelectionGroups.ts`; prune lab `toothProsthesis` on change. Cases/Tasks merge by prosthesis type after send. - **Lab dispatch UI:** due date end-aligned beside title (`sm:flex-row` + `justify-between`; stacks on mobile). Prosthesis type: stacked below `md`, 50/50 same-row from `md`. Content clinical field = **Notes** (not case comments). - **Lab comments:** shared `LabCaseCommentsPanel` (newest-first; sent=`justify-start`, received=`justify-end`; `viewerSide`) across Treatment / Cases / Tasks / share. Use logical `text-start`/`text-end`, not left/right. - **Detail chrome:** chips + Add at top; **Remove** = trash on chip (unsent, >1 detail; disabled when day-locked / no edit / uploading). No delete in Content step. diff --git a/.cursor/skills/treatment-workspace/SKILL.md b/.cursor/skills/treatment-workspace/SKILL.md index 2b37d1a..984b8ef 100644 --- a/.cursor/skills/treatment-workspace/SKILL.md +++ b/.cursor/skills/treatment-workspace/SKILL.md @@ -40,7 +40,7 @@ Right-column entry uses `WizardStepper` (`components/ui/shared/WizardStepper.tsx | Step | UI | Notes | |------|-----|--------| -| **Teeth** | `FdiToothChart` | Shift+click connected bridge (union if overlap); click cuts tooth from bridge as selected single | +| **Teeth** | `FdiToothChart` | Neighbor circles connect/disconnect; Shift+range selects (empty circles); plain click select/deselect | | **Content** | `TreatmentDetailsEditor` fields only (`showChrome={false}`) | Type / **Notes** / attachments — **no delete button** | | **Lab** | `LabCasesDispatchPanel` | Shown in stepper **only** when active detail type is lab-dependent (`labDependentCodes`). Entering Lab **auto-ensures** a shipment draft (no “Add lab shipment” click). Add detail stays ungated. Due date beside title (RTL via logical end). Prosthesis dropdown same-row from `md`. | @@ -56,8 +56,9 @@ Right-column entry uses `WizardStepper` (`components/ui/shared/WizardStepper.tsx Helpers: `frontend/src/components/treatment/toothSelectionGroups.ts`. Persisted as `toothSelectionGroups` on the detail. Lab **dispatch** rows are 1:1 with selection groups; after send, Cases/Tasks **merge** teeth by prosthesis type (not by selection group). -- **Plain click:** add/remove single; if tooth is in a **connected** bridge → cut it out as a selected **single** (second click deselects). End of bridge → remainder stays connected (≥2) or demotes to single. Middle → left and right become separate groups. Never a 1-tooth connected (no lone dots). -- **Shift+click:** inclusive same-arch range → one **connected** bridge; if the range overlaps existing bridges, **union** them into one bridge. Non-overlapping bridges stay separate. +- **Plain click:** add/remove single; deselecting a tooth in a bridge removes it and splits/shrinks remaining sides (never a 1-tooth connected group). +- **Neighbor circles:** when two arch-adjacent teeth are both selected, an empty circle appears **between** them (not per-tooth). Click empty → connect; click filled → disconnect (teeth stay selected). Transitive A–B + B–C = one bridge. +- **Shift+click:** inclusive same-arch range → all selected as **singles** (empty circles); overlapping existing bridges are absorbed as singles too. Midline neighbors (11–21, 41–31) allowed. - Prevent browser selection artifacts (`select-none`, Shift `preventDefault` on mousedown). - On group change, prune/remap `labCase.toothProsthesis` via `pruneToothProsthesisForGroups`. - Connected UI label: `ConnectedSelectionBadge` (shared `Badge` + primary tint) in dispatch + lab case lists. diff --git a/AGENTS.md b/AGENTS.md index 6c672fd..f84d92d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,7 +46,7 @@ frontend/src/ - Day/mode gate: editable only for live draft on today/future (`canEditTreatmentForDay`). Past day / historical load → read-only form. - Sent-to-lab detail locks that line; **Add detail** still OK same day; **Remove detail** = trash icon on each detail chip (not in the wizard Content step) — only unsent and not the last line. Attachment upload blocked when sent (`TREATMENT_DETAIL_SENT`). - **Entry wizard:** `WizardStepper` — Teeth → Content → Lab; Lab step only when active detail type is lab-dependent (prosthesis); entering Lab auto-opens shipment draft (no Add-shipment CTA). Content notes field label is **Notes** (clinical). Detail chips ≠ wizard chrome. Switching detail chips resets wizard to Teeth unless `pendingEntryStepRef` requests Lab (e.g. opening a case from the lab shipments rail). -- **Tooth selection:** Shift+click same-arch range = connected bridge (one lab row); overlapping Shift bridges merge by union. Plain click add/remove singles; click in a bridge cuts that tooth out as a selected single (ends keep one bridge; middle splits into two). Never a 1-tooth connected (no lone dots). Helpers: `toothSelectionGroups.ts`. Connected label: `ConnectedSelectionBadge`. After send, Cases/Tasks merge teeth by prosthesis type. +- **Tooth selection:** Neighbor empty/filled circles between selected adjacent teeth connect/disconnect bridges; Shift+range selects only (empty circles; overlap absorbs as singles); midline 11–21 / 41–31 allowed. Plain click selects/deselects (deselect splits bridges). Never a 1-tooth connected. Helpers: `toothSelectionGroups.ts`. Connected label: `ConnectedSelectionBadge`. After send, Cases/Tasks merge teeth by prosthesis type. - **Lab dispatch layout:** due date beside title (`justify-between`, logical start/end for RTL); prosthesis type on same row as teeth from `md:` up (stacked on mobile). **Treatment lab rules (quick ref):** diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 760b32c..7370ecd 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -830,8 +830,12 @@ "entryStepNext": "Next", "entryStepLabUnavailable": "Lab dispatch is available after you save a lab-dependent detail with teeth selected.", "toothChartTitleCompact": "Tooth chart", - "toothChartHint": "Tap teeth to select or deselect. Shift-click another tooth in the same arch for a connected bridge. Click a bridge tooth to cut it out (still selected as a single); ends keep one bridge, middle splits into two.", + "toothChartHint": "Tap teeth to select or deselect. When two neighbors are selected, an empty circle appears between them — tap it to connect (or tap a filled circle to disconnect). Shift-click a range to select all in between (not connected until you fill the circles).", "toothConnectedHint": "Connected selection", + "toothLinkHint": "Connect these teeth", + "toothUnlinkHint": "Disconnect these teeth", + "toothLinkAria": "Connect teeth {a} and {b}", + "toothUnlinkAria": "Disconnect teeth {a} and {b}", "toothConnectedSuffix": " (connected)", "connectedBadge": "Connected", "prosthesisConnectedLabel": "Connected teeth prosthesis type", diff --git a/frontend/messages/fa.json b/frontend/messages/fa.json index 17ea3d7..0de9a92 100644 --- a/frontend/messages/fa.json +++ b/frontend/messages/fa.json @@ -824,8 +824,12 @@ "confirmSend": "تأیید ارسال", "toothChartTitle": "نمودار دندان‌ها FDI", "toothChartTitleCompact": "نمودار دندان", - "toothChartHint": "برای انتخاب یا لغو انتخاب روی دندان ضربه بزنید. Shift+کلیک روی دندان دیگر در همان قوس، یک پل متصل می‌سازد. کلیک روی دندان داخل پل آن را جدا می‌کند (همچنان انتخاب‌شده به‌صورت تک)؛ انتها یک پل می‌ماند، وسط به دو پل تقسیم می‌شود.", + "toothChartHint": "برای انتخاب یا لغو انتخاب روی دندان ضربه بزنید. وقتی دو دندان همسایه انتخاب شوند، دایرهٔ خالی بینشان ظاهر می‌شود — برای اتصال روی آن بزنید (یا روی دایرهٔ پر برای قطع اتصال). Shift+کلیک روی یک بازه، همه را انتخاب می‌کند (تا وقتی دایره‌ها را پر نکنید متصل نیستند).", "toothConnectedHint": "انتخاب متصل", + "toothLinkHint": "اتصال این دندان‌ها", + "toothUnlinkHint": "قطع اتصال این دندان‌ها", + "toothLinkAria": "اتصال دندان‌های {a} و {b}", + "toothUnlinkAria": "قطع اتصال دندان‌های {a} و {b}", "toothConnectedSuffix": " (متصل)", "connectedBadge": "متصل", "prosthesisConnectedLabel": "نوع پروتز دندان‌های متصل", diff --git a/frontend/messages/nl.json b/frontend/messages/nl.json index bc00c67..a7ee63f 100644 --- a/frontend/messages/nl.json +++ b/frontend/messages/nl.json @@ -823,8 +823,12 @@ "confirmSend": "Bevestig verzending", "toothChartTitle": "FDI-tanddiagram", "toothChartTitleCompact": "Tanddiagram", - "toothChartHint": "Tik op tanden om te selecteren of deselecteren. Shift-klik op een andere tand in dezelfde boog voor een verbonden brug. Klik op een brugtand om die eruit te snijden (blijft geselecteerd als single); uiteinden houden één brug, midden splitst in twee.", + "toothChartHint": "Tik op tanden om te selecteren of deselecteren. Bij twee geselecteerde buren verschijnt een lege cirkel ertussen — tik om te verbinden (of tik op een gevulde cirkel om te ontkoppelen). Shift-klik een bereik om alles ertussen te selecteren (nog niet verbonden tot je de cirkels vult).", "toothConnectedHint": "Verbonden selectie", + "toothLinkHint": "Deze tanden verbinden", + "toothUnlinkHint": "Deze tanden ontkoppelen", + "toothLinkAria": "Tanden {a} en {b} verbinden", + "toothUnlinkAria": "Tanden {a} en {b} ontkoppelen", "toothConnectedSuffix": " (verbonden)", "connectedBadge": "Verbonden", "prosthesisConnectedLabel": "Prothesetype voor verbonden tanden", diff --git a/frontend/public/FDI_11.svg b/frontend/public/FDI_11.svg new file mode 100644 index 0000000..a5d3918 --- /dev/null +++ b/frontend/public/FDI_11.svg @@ -0,0 +1,39 @@ + + + FDI 11 maxillary central incisor + Buccal-view tooth asset with independently selectable crown and root groups. + + + + + + + + + + + + diff --git a/frontend/public/FDI_12.svg b/frontend/public/FDI_12.svg new file mode 100644 index 0000000..f554838 --- /dev/null +++ b/frontend/public/FDI_12.svg @@ -0,0 +1,33 @@ + + + FDI 12 + Selectable crown and root SVG tooth asset. + + + + + + + + + + diff --git a/frontend/public/FDI_13.svg b/frontend/public/FDI_13.svg new file mode 100644 index 0000000..d75f0fc --- /dev/null +++ b/frontend/public/FDI_13.svg @@ -0,0 +1,39 @@ + + + FDI 13 + Selectable crown and root SVG tooth asset. + + + + + + + + + + + + diff --git a/frontend/public/FDI_14.svg b/frontend/public/FDI_14.svg new file mode 100644 index 0000000..320c39f --- /dev/null +++ b/frontend/public/FDI_14.svg @@ -0,0 +1,39 @@ + + + FDI 14 + Selectable crown and root SVG tooth asset. + + + + + + + + + + + + diff --git a/frontend/public/FDI_15.svg b/frontend/public/FDI_15.svg new file mode 100644 index 0000000..dc8f1a6 --- /dev/null +++ b/frontend/public/FDI_15.svg @@ -0,0 +1,39 @@ + + + FDI 15 + Selectable crown and root SVG tooth asset. + + + + + + + + + + + + diff --git a/frontend/public/FDI_16.svg b/frontend/public/FDI_16.svg new file mode 100644 index 0000000..ad6312c --- /dev/null +++ b/frontend/public/FDI_16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/FDI_17.svg b/frontend/public/FDI_17.svg new file mode 100644 index 0000000..542cf47 --- /dev/null +++ b/frontend/public/FDI_17.svg @@ -0,0 +1,27 @@ + + + FDI 17 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_18.svg b/frontend/public/FDI_18.svg new file mode 100644 index 0000000..b34bd4f --- /dev/null +++ b/frontend/public/FDI_18.svg @@ -0,0 +1,27 @@ + + + FDI 18 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_21.svg b/frontend/public/FDI_21.svg new file mode 100644 index 0000000..dbaf83b --- /dev/null +++ b/frontend/public/FDI_21.svg @@ -0,0 +1,39 @@ + + + FDI 21 maxillary central incisor + Buccal-view tooth asset with independently selectable crown and root groups. + + + + + + + + + + + + diff --git a/frontend/public/FDI_22.svg b/frontend/public/FDI_22.svg new file mode 100644 index 0000000..ab560b8 --- /dev/null +++ b/frontend/public/FDI_22.svg @@ -0,0 +1,33 @@ + + + FDI 22 + Selectable crown and root SVG tooth asset. + + + + + + + + + + diff --git a/frontend/public/FDI_23.svg b/frontend/public/FDI_23.svg new file mode 100644 index 0000000..fd37ff7 --- /dev/null +++ b/frontend/public/FDI_23.svg @@ -0,0 +1,39 @@ + + + FDI 23 + Selectable crown and root SVG tooth asset. + + + + + + + + + + + + diff --git a/frontend/public/FDI_24.svg b/frontend/public/FDI_24.svg new file mode 100644 index 0000000..f526384 --- /dev/null +++ b/frontend/public/FDI_24.svg @@ -0,0 +1,39 @@ + + + FDI 24 + Selectable crown and root SVG tooth asset. + + + + + + + + + + + + diff --git a/frontend/public/FDI_25.svg b/frontend/public/FDI_25.svg new file mode 100644 index 0000000..99a9294 --- /dev/null +++ b/frontend/public/FDI_25.svg @@ -0,0 +1,39 @@ + + + FDI 25 + Selectable crown and root SVG tooth asset. + + + + + + + + + + + + diff --git a/frontend/public/FDI_26.svg b/frontend/public/FDI_26.svg new file mode 100644 index 0000000..37f1315 --- /dev/null +++ b/frontend/public/FDI_26.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/FDI_27.svg b/frontend/public/FDI_27.svg new file mode 100644 index 0000000..e6c173e --- /dev/null +++ b/frontend/public/FDI_27.svg @@ -0,0 +1,27 @@ + + + FDI 27 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_28.svg b/frontend/public/FDI_28.svg new file mode 100644 index 0000000..94de056 --- /dev/null +++ b/frontend/public/FDI_28.svg @@ -0,0 +1,27 @@ + + + FDI 28 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_31.svg b/frontend/public/FDI_31.svg new file mode 100644 index 0000000..083a845 --- /dev/null +++ b/frontend/public/FDI_31.svg @@ -0,0 +1,27 @@ + + + FDI 31 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_32.svg b/frontend/public/FDI_32.svg new file mode 100644 index 0000000..842d975 --- /dev/null +++ b/frontend/public/FDI_32.svg @@ -0,0 +1,27 @@ + + + FDI 32 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_33.svg b/frontend/public/FDI_33.svg new file mode 100644 index 0000000..657e30e --- /dev/null +++ b/frontend/public/FDI_33.svg @@ -0,0 +1,27 @@ + + + FDI 33 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_34.svg b/frontend/public/FDI_34.svg new file mode 100644 index 0000000..e52f6c6 --- /dev/null +++ b/frontend/public/FDI_34.svg @@ -0,0 +1,27 @@ + + + FDI 34 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_35.svg b/frontend/public/FDI_35.svg new file mode 100644 index 0000000..07b5992 --- /dev/null +++ b/frontend/public/FDI_35.svg @@ -0,0 +1,27 @@ + + + FDI 35 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_36.svg b/frontend/public/FDI_36.svg new file mode 100644 index 0000000..7eb4fc4 --- /dev/null +++ b/frontend/public/FDI_36.svg @@ -0,0 +1,27 @@ + + + FDI 36 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_37.svg b/frontend/public/FDI_37.svg new file mode 100644 index 0000000..c36e27a --- /dev/null +++ b/frontend/public/FDI_37.svg @@ -0,0 +1,27 @@ + + + FDI 37 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_38.svg b/frontend/public/FDI_38.svg new file mode 100644 index 0000000..5288756 --- /dev/null +++ b/frontend/public/FDI_38.svg @@ -0,0 +1,27 @@ + + + FDI 38 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_41.svg b/frontend/public/FDI_41.svg new file mode 100644 index 0000000..8c87e2f --- /dev/null +++ b/frontend/public/FDI_41.svg @@ -0,0 +1,27 @@ + + + FDI 41 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_42.svg b/frontend/public/FDI_42.svg new file mode 100644 index 0000000..48f7d1c --- /dev/null +++ b/frontend/public/FDI_42.svg @@ -0,0 +1,27 @@ + + + FDI 42 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_43.svg b/frontend/public/FDI_43.svg new file mode 100644 index 0000000..e83c361 --- /dev/null +++ b/frontend/public/FDI_43.svg @@ -0,0 +1,27 @@ + + + FDI 43 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_44.svg b/frontend/public/FDI_44.svg new file mode 100644 index 0000000..0fcea54 --- /dev/null +++ b/frontend/public/FDI_44.svg @@ -0,0 +1,27 @@ + + + FDI 44 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_45.svg b/frontend/public/FDI_45.svg new file mode 100644 index 0000000..86ed8f0 --- /dev/null +++ b/frontend/public/FDI_45.svg @@ -0,0 +1,27 @@ + + + FDI 45 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_46.svg b/frontend/public/FDI_46.svg new file mode 100644 index 0000000..1c7d968 --- /dev/null +++ b/frontend/public/FDI_46.svg @@ -0,0 +1,27 @@ + + + FDI 46 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_47.svg b/frontend/public/FDI_47.svg new file mode 100644 index 0000000..721db18 --- /dev/null +++ b/frontend/public/FDI_47.svg @@ -0,0 +1,27 @@ + + + FDI 47 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/FDI_48.svg b/frontend/public/FDI_48.svg new file mode 100644 index 0000000..59c4e66 --- /dev/null +++ b/frontend/public/FDI_48.svg @@ -0,0 +1,27 @@ + + + FDI 48 + Selectable crown and root SVG tooth asset. + + + + + + + + + + \ No newline at end of file diff --git a/frontend/scripts/extract-tooth-svgs.mjs b/frontend/scripts/extract-tooth-svgs.mjs new file mode 100644 index 0000000..bd4726c --- /dev/null +++ b/frontend/scripts/extract-tooth-svgs.mjs @@ -0,0 +1,54 @@ +/** + * Regenerates realisticToothAssets.ts from FDI_*.svg files in public/. + * Usage (from frontend/): node scripts/extract-tooth-svgs.mjs + */ +import fs from 'node:fs'; +import path from 'node:path'; + +const dir = path.resolve('public'); +const files = fs.readdirSync(dir).filter((f) => /^FDI_\d{2}\.svg$/i.test(f)); +const out = {}; + +for (const file of files.sort()) { + const id = file.match(/FDI_(\d{2})/i)?.[1]; + if (!id) continue; + const xml = fs.readFileSync(path.join(dir, file), 'utf8'); + const vb = xml.match(/viewBox="([^"]+)"/)?.[1]; + const toothG = xml.match(/]*>/)?.[0] ?? ''; + const transform = toothG.match(/transform="([^"]+)"/)?.[1]; + const rootD = xml.match(/id="root-path"[\s\S]*?d="([^"]+)"/)?.[1]; + const crownD = xml.match(/id="crown-path"[\s\S]*?d="([^"]+)"/)?.[1]; + if (!vb || !rootD || !crownD) throw new Error(`Failed to parse ${file}`); + out[id] = { + viewBox: vb, + ...(transform ? { groupTransform: transform } : {}), + crown: crownD, + roots: [rootD], + }; + console.log(id, transform ? 'mirrored' : 'plain'); +} + +const ts = `import type { FdiToothId } from '@/types/treatment'; + +/** + * Realistic buccal-view assets from public/FDI_*.svg. + * Upper (1x/2x): root-up. Lower (3x/4x): crown-up. + * Left chart side (Q1/Q4) includes horizontal flip via groupTransform. + */ +export interface RealisticToothAsset { + viewBox: string; + /** Applied inside the svg (e.g. Q1/Q4 mirror baked in source files). */ + groupTransform?: string; + crown: string; + roots: string[]; +} + +export const REALISTIC_TOOTH_ASSETS: Partial> = ${JSON.stringify(out, null, 2)}; + +export function getRealisticToothAsset(fdi: FdiToothId): RealisticToothAsset | null { + return REALISTIC_TOOTH_ASSETS[fdi] ?? null; +} +`; + +fs.writeFileSync(path.resolve('src/components/treatment/realisticToothAssets.ts'), ts); +console.log('Wrote realisticToothAssets.ts for', Object.keys(out).length, 'teeth'); diff --git a/frontend/src/components/treatment/fdiToothScale.ts b/frontend/src/components/treatment/fdiToothScale.ts new file mode 100644 index 0000000..d2d94f6 --- /dev/null +++ b/frontend/src/components/treatment/fdiToothScale.ts @@ -0,0 +1,75 @@ +import type { FdiToothId } from '@/types/treatment'; + +/** + * Mean mesiodistal crown widths (mm) — buccal chart relative scale. + * + * Anchored on classic Wheeler's Dental Anatomy values (widely used teaching + * standard). Population studies vary slightly, but ratios are stable: + * maxillary central ≈ 1.3× lateral (8.5 / 6.5). + * + * Sources: Wheeler's Dental Anatomy; consistent with published odontometric + * means (e.g. maxillary CI ~8.5–9.2, LI ~6.5–7.4, C ~7.5–8.0, P ~7.0–7.4, + * M1 ~10.0–10.5 mm). + */ +export const FDI_MESIODISTAL_MM: Record = { + // Maxillary + '11': 8.5, + '21': 8.5, + '12': 6.5, + '22': 6.5, + '13': 7.5, + '23': 7.5, + '14': 7.0, + '24': 7.0, + '15': 7.0, + '25': 7.0, + '16': 10.0, + '26': 10.0, + '17': 9.0, + '27': 9.0, + '18': 8.5, + '28': 8.5, + // Mandibular + '41': 5.0, + '31': 5.0, + '42': 5.5, + '32': 5.5, + '43': 7.0, + '33': 7.0, + '44': 7.0, + '34': 7.0, + '45': 7.0, + '35': 7.0, + '46': 11.0, + '36': 11.0, + '47': 10.5, + '37': 10.5, + '48': 10.0, + '38': 10.0, +}; + +/** Reference tooth for relative scale (maxillary lateral). */ +export const FDI_SCALE_REFERENCE: FdiToothId = '12'; + +/** CSS rem per mm of mesiodistal crown width (overall chart size). */ +export const TOOTH_REM_PER_MM = 0.34; + +/** Extra column padding beyond glyph width (keep small so teeth sit close). */ +export const TOOTH_COLUMN_PAD_REM = 0.04; + +export function getToothMesiodistalMm(fdi: FdiToothId): number { + return FDI_MESIODISTAL_MM[fdi]; +} + +/** Width relative to maxillary lateral (12). e.g. 11 → ~1.31 */ +export function getToothRelativeWidth(fdi: FdiToothId): number { + return FDI_MESIODISTAL_MM[fdi] / FDI_MESIODISTAL_MM[FDI_SCALE_REFERENCE]; +} + +export function getToothGlyphWidthRem(fdi: FdiToothId): number { + return Number((FDI_MESIODISTAL_MM[fdi] * TOOTH_REM_PER_MM).toFixed(3)); +} + +export function getToothColumnWidthRem(fdi: FdiToothId): number { + return Number((getToothGlyphWidthRem(fdi) + TOOTH_COLUMN_PAD_REM).toFixed(3)); +} diff --git a/frontend/src/components/treatment/realisticToothAssets.ts b/frontend/src/components/treatment/realisticToothAssets.ts new file mode 100644 index 0000000..34fe199 --- /dev/null +++ b/frontend/src/components/treatment/realisticToothAssets.ts @@ -0,0 +1,261 @@ +import type { FdiToothId } from '@/types/treatment'; + +/** + * Realistic buccal-view assets from public/FDI_*.svg. + * Upper (1x/2x): root-up. Lower (3x/4x): crown-up. + * Left chart side (Q1/Q4) includes horizontal flip via groupTransform. + */ +export interface RealisticToothAsset { + viewBox: string; + /** Applied inside the svg (e.g. Q1/Q4 mirror baked in source files). */ + groupTransform?: string; + crown: string; + roots: string[]; +} + +export const REALISTIC_TOOTH_ASSETS: Partial> = { + "11": { + "viewBox": "51.660933 83.146350 38.944857 87.264388", + "groupTransform": "translate(142.266723 0) scale(-1 1)", + "crown": "m 62.647814,135.73384 c 0.341028,-0.92433 0.636863,-1.87523 1.110468,-2.73917 0.55297,-1.00871 1.16079,-2.02522 1.99886,-2.8132 0.78325,-0.73643 1.67713,-1.53569 2.73916,-1.70272 1.36726,-0.21503 3.01579,-0.28218 4.51592,-0.14808 0.70385,0.0629 1.41612,0.18341 2.07289,0.4442 0.88501,0.35142 1.77486,0.80219 2.44303,1.48064 0.82263,0.83529 1.25279,1.98653 1.77676,3.03529 0.37075,0.74207 0.69884,1.50842 0.96241,2.29497 0.41266,1.23148 0.63257,2.51941 0.96241,3.77561 0.4804,1.82961 0.9268,3.66959 1.48063,5.47833 0.26732,0.87304 0.62604,1.71654 0.88838,2.5911 0.227,0.75674 0.45137,1.51759 0.59225,2.29498 0.20797,1.14768 0.33655,2.31359 0.37016,3.47948 0.0441,1.53114 0.0671,3.08573 -0.2221,4.58995 -0.30059,1.56338 -0.62675,3.22229 -1.55466,4.51593 -0.39989,0.55751 -0.99264,1.00132 -1.62869,1.25853 -0.82866,0.33509 -1.77525,0.21211 -2.66514,0.29613 -2.21778,0.2094 -4.4354,0.48651 -6.66282,0.51822 -3.01395,0.0429 -6.04047,-0.0736 -9.031859,-0.44426 -1.248744,-0.15472 -2.734205,0.0644 -3.701573,-0.74031 -0.817326,-0.67987 -0.988842,-1.9163 -1.184503,-2.96127 -0.440565,-2.35292 -0.154978,-4.79822 0.07403,-7.18106 0.144009,-1.4984 0.495914,-2.97064 0.814345,-4.44188 0.38742,-1.78999 0.853156,-3.56269 1.332568,-5.33026 0.27596,-1.01745 0.551791,-2.03627 0.888379,-3.03531 0.286792,-0.85123 0.659911,-1.67125 0.96241,-2.51703 0.236518,-0.6613 0.42318,-1.33994 0.666282,-1.99885 z", + "roots": [ + "m 62.943939,135.95593 c 0,0 0.08367,-2.37479 0.222095,-3.55351 0.30378,-2.58681 0.909078,-5.12936 1.332568,-7.69928 0.46704,-2.8342 0.85124,-5.68181 1.33257,-8.51362 0.45826,-2.69605 1.23284,-5.34597 1.48063,-8.06943 0.14534,-1.59742 -0.11495,-3.21216 0,-4.81205 0.16082,-2.23823 0.29166,-4.521436 0.96241,-6.662838 0.47832,-1.527054 1.26926,-2.955761 2.14691,-4.293825 0.65331,-0.99603 1.2756,-2.122935 2.29498,-2.739168 0.49706,-0.300483 1.16538,-0.590669 1.70272,-0.370157 0.62688,0.257255 0.87646,1.066803 1.11047,1.702726 2.1479,5.836971 1.78491,12.315732 2.36901,18.507872 0.53401,5.66112 0.47036,11.36924 1.03644,17.02725 0.31998,3.19822 1.33257,9.55006 1.33257,9.55006 0,0 -1.11923,-3.66392 -2.369,-5.03414 -0.98396,-1.07879 -2.37532,-1.80722 -3.77561,-2.22094 -1.65813,-0.4899 -3.51706,-0.68761 -5.18221,-0.22213 -1.19966,0.33535 -2.2202,1.20048 -3.10933,2.07288 -0.77202,0.7575 -1.85078,2.66514 -1.85078,2.66514 l -1.036442,1.70272" + ] + }, + "12": { + "viewBox": "93.685783 101.132481 28.706127 78.417208", + "groupTransform": "translate(216.077694 0) scale(-1 1)", + "crown": "m 106.17348,144.89797 c 0,0 3.4638,-0.0314 5.03413,0.59222 0.99998,0.3971 1.91519,1.0877 2.59111,1.92482 0.56195,0.69598 0.86436,1.58304 1.11047,2.44304 0.26022,0.90931 0.23314,1.87736 0.37015,2.8132 0.33674,2.30011 0.8184,4.57871 1.11048,6.88492 0.2024,1.59807 0.37007,3.20293 0.44419,4.81206 0.0704,1.52836 0.29878,3.08942 3e-5,4.58995 -0.21387,1.07421 -0.57809,2.17701 -1.25854,3.03529 -0.60414,0.76206 -1.45491,1.37082 -2.369,1.70272 -1.58155,0.57426 -3.35233,0.31948 -5.03415,0.37016 -1.87478,0.0565 -3.76637,0.16741 -5.6264,-0.074 -0.9081,-0.11786 -2.05115,0.0131 -2.665126,-0.66628 -1.15359,-1.27648 -0.68012,-3.38871 -0.74031,-5.10817 -0.0468,-1.3354 0.12,-2.67314 0.29612,-3.9977 0.27493,-2.06772 0.744106,-4.10572 1.184496,-6.14462 0.38036,-1.76099 0.80338,-3.51308 1.25854,-5.25624 0.34341,-1.31517 0.65674,-2.64237 1.11048,-3.92366 0.31368,-0.88578 1.11046,-2.59111 1.11046,-2.59111", + "roots": [ + "m 103.65639,146.97086 c 0.28824,-1.47746 0.58952,-2.95347 0.81432,-4.4419 0.31175,-2.06411 0.4672,-4.14906 0.74031,-6.21864 0.4502,-3.41145 1.03766,-6.80395 1.48063,-10.21635 0.3901,-3.00516 0.78791,-6.01168 1.03644,-9.03185 0.16211,-1.96998 0.0603,-3.95999 0.29612,-5.92252 0.14088,-1.17246 -0.0183,-2.51724 0.66629,-3.47948 0.4294,-0.60357 1.1866,-1.04961 1.92482,-1.11047 0.84051,-0.0693 1.80618,0.26028 2.369,0.88838 0.93463,1.04303 0.78133,2.69464 1.03645,4.07173 0.46167,2.49203 0.74006,5.02113 0.88838,7.55121 0.35959,6.13409 0.0737,12.28968 0,18.43385 -0.037,3.08531 0.15869,6.19199 -0.2221,9.25394 -0.0687,0.55248 0.18433,1.57826 -0.37012,1.62869 -0.18717,0.017 -0.11387,-0.36457 -0.22209,-0.51822 -0.54298,-0.77088 -1.24391,-1.47551 -2.07289,-1.92482 -0.89452,-0.48483 -1.46818,-0.63743 -2.96125,-0.74032 -1.15735,-0.0798 -2.21081,-0.13961 -3.18335,0.29613 -0.89232,0.3998 -1.62374,1.1728 -2.14692,1.99885 -0.30484,0.48131 -0.51825,1.62871 -0.51825,1.62871 0,0 0.30428,-1.42965 0.44421,-2.14692 z" + ] + }, + "13": { + "viewBox": "92.847964 96.106573 35.728809 97.202657", + "groupTransform": "translate(221.424738 0) scale(-1 1)", + "crown": "m 99.551769,176.26584 c -0.0141,-1.55533 0.583831,-3.0579 0.977691,-4.5626 0.27842,-1.06369 0.69205,-2.08858 0.97771,-3.15036 0.33776,-1.25543 0.58103,-2.5344 0.86905,-3.80217 0.25495,-1.12221 0.43819,-2.26287 0.76044,-3.36763 0.26943,-0.92366 0.51784,-1.87069 0.9777,-2.71583 0.45692,-0.83974 1.05465,-1.61288 1.73813,-2.2813 0.70425,-0.68873 1.499,-1.31796 2.38994,-1.73813 1.04856,-0.49451 2.20982,-0.92825 3.36762,-0.86907 1.04125,0.0532 2.04158,0.54576 2.9331,1.08633 0.87688,0.53169 1.66722,1.24275 2.28131,2.06404 0.6498,0.86905 1.01396,1.9237 1.41223,2.93309 0.44557,1.1293 0.73398,2.31451 1.08633,3.47627 0.37246,1.22809 0.74648,2.45602 1.08633,3.69353 0.46516,1.69381 1.12021,3.35885 1.3036,5.10577 0.1854,1.76611 0.31102,3.62761 -0.21726,5.32303 -0.48415,1.55381 -1.62154,2.833 -2.60721,4.12806 -0.65205,0.85672 -1.34836,1.69736 -2.17266,2.38994 -0.9845,0.82718 -2.06044,1.59826 -3.259,2.06402 -0.96001,0.37306 -2.01622,0.63873 -3.04172,0.54317 -1.11828,-0.10421 -2.17801,-0.63288 -3.15037,-1.19496 -1.3367,-0.77271 -2.40515,-1.93711 -3.58491,-2.9331 -1.13747,-0.96029 -2.60004,-1.65763 -3.36762,-2.9331 -0.575191,-0.95578 -0.750311,-2.14353 -0.760431,-3.259 z", + "roots": [ + "m 117.25898,158.99315 c 0.0309,-5.1946 -0.605,-6.99748 -0.54316,-9.23383 0.11896,-4.30188 -0.1936,-7.83304 -0.54316,-11.73239 -0.34249,-3.82049 -0.99928,-7.60631 -1.52087,-11.4065 -0.42268,-3.07959 -1.02386,-6.13798 -1.3036,-9.23383 -0.2446,-2.70697 -0.51897,-5.43636 -0.3259,-8.14749 0.0994,-1.39564 0.99506,-2.74871 0.76043,-4.12807 -0.14687,-0.86342 -0.44168,-2.12583 -1.3036,-2.2813 -1.11331,-0.20081 -2.04194,1.19274 -2.6072,2.17267 -1.70742,2.95995 -1.32174,6.71125 -1.73813,10.1029 -0.83624,6.8115 -0.84156,13.70274 -1.52086,20.53168 -0.78024,7.84376 -1.61721,15.69294 -2.9331,23.46479 -0.0866,0.51119 -0.80686,1.32726 -0.3259,1.52087 0.82965,0.33397 0.90805,-1.54261 1.41223,-2.2813 0.51034,-0.74772 0.95883,-1.56461 1.6295,-2.17267 0.87875,-0.79672 1.90738,-1.50006 3.04173,-1.84676 0.72805,-0.22252 1.52279,-0.17368 2.2813,-0.10864 0.66526,0.057 1.35819,0.13593 1.9554,0.43454 0.89581,0.44791 1.65094,1.1771 2.2813,1.9554 0.57113,0.70517 1.2982,3.29735 1.30359,2.38993 z" + ] + }, + "14": { + "viewBox": "78.778460 104.429020 32.880110 81.849457", + "groupTransform": "translate(190.437029 0) scale(-1 1)", + "crown": "m 103.54613,162.08292 c 0,0 1.51697,3.7883 1.90985,5.77856 0.31375,1.5894 0.86374,3.3141 0.34277,4.84812 -0.68048,2.00371 -2.6583,3.313 -4.21149,4.75018 -1.11188,1.02883 -2.195824,2.2369 -3.623854,2.74237 -1.41645,0.50138 -3.06843,0.58631 -4.50532,0.14691 -2.15763,-0.65981 -3.32378,-1.98281 -5.48474,-3.96664 -1.74427,-1.60129 -3.27631,-3.95329 -3.52591,-6.36622 -0.21622,-2.09029 1.06703,-4.0777 1.8609,-6.02342 1.19972,-2.94044 2.39588,-5.37003 4.3584,-8.47196 0.49409,-0.78095 1.36295,-1.36623 2.25267,-1.61604 1.26081,-0.35399 2.66615,-0.0917 3.91767,0.29382 1.06231,0.32724 2.01728,0.97567 2.88928,1.66501 0.914954,0.7233 1.778634,1.55922 2.399584,2.54649 0.36314,0.57738 0.73456,1.90987 0.73456,1.90987", + "roots": [ + "m 88.707936,158.80187 c 0,0 0.8366,-12.32924 1.56707,-18.46203 0.64849,-5.44453 1.82049,-10.81728 2.49752,-16.25833 0.44545,-3.57992 0.0522,-6.93841 1.02838,-10.7736 0.26861,-1.05527 1.24173,-1.83478 2.10575,-2.49752 0.45775,-0.35111 0.99122,-0.76923 1.56707,-0.73456 0.61487,0.037 1.15915,0.51785 1.56706,0.97942 1.025294,1.16012 2.080354,2.66802 1.958854,4.2115 -0.40846,5.18879 -1.867514,9.88427 -1.909874,14.88715 -0.0826,9.75556 2.747834,21.07548 3.232084,29.08871 0.0654,1.08192 -1.14123,-1.88388 -1.95884,-2.59546 -1.240224,-1.07939 -2.540834,-1.9818 -4.309434,-2.39957 -1.53319,-0.36216 -3.32305,-0.0131 -4.65224,0.8325 -1.14637,0.7293 -2.3506,3.33002 -2.3506,3.33002" + ] + }, + "15": { + "viewBox": "80.851597 94.968970 32.298645 79.507376", + "groupTransform": "translate(194.001840 0) scale(-1 1)", + "crown": "m 86.514392,158.88671 c -0.36012,-1.61219 -0.13293,-3.34785 0.27746,-4.94799 0.29289,-1.142 1.02772,-2.12227 1.52601,-3.19076 0.86591,-1.85678 1.56594,-3.922 2.54337,-5.59539 0.66552,-1.1394 1.92472,-1.90141 3.14452,-2.40463 1.32204,-0.5454 2.84482,-0.79679 4.25435,-0.55491 1.14144,0.19587 2.171618,0.87758 3.098268,1.57225 0.82892,0.62141 1.52639,1.42193 2.12718,2.2659 1.21154,1.70196 2.12254,3.61546 2.91329,5.54916 0.38392,0.93884 0.72328,1.91096 0.87862,2.9133 0.32345,2.08707 0.63944,4.29535 0.0925,6.33529 -0.35805,1.3354 -1.26504,2.47987 -2.12718,3.5607 -1.19654,1.50005 -2.38722,3.24719 -4.16187,3.97689 -1.625458,0.66835 -3.737098,0.94912 -5.271688,0.0925 -1.46829,-0.81963 -2.85268,-2.38304 -4.20811,-3.65318 -1.43957,-1.34899 -2.98731,-2.623 -4.16186,-4.20811 -0.38598,-0.5209 -0.78352,-1.07827 -0.92486,-1.711 z", + "roots": [ + "m 89.057752,149.36068 c 0.25498,-4.87107 1.46284,-15.84832 2.5896,-23.72263 1.0378,-7.25259 2.08507,-14.0731 3.83817,-21.64169 0.32031,-1.38284 1.03251,-3.15094 2.40463,-3.51446 0.61309,-0.16243 1.27063,0.37592 1.71099,0.83237 0.819338,0.84928 1.430568,2.05694 1.433528,3.23701 0.0207,8.23338 -0.44305,15.33744 0.0462,22.98273 0.33736,5.27183 1.20807,10.49795 1.98845,15.7226 0.20103,1.3459 0.95394,2.42655 0.69364,4.02313 -0.20581,1.26235 -1.51695,-2.09162 -2.49712,-2.9133 -0.8367,-0.70141 -1.753488,-1.43483 -2.820808,-1.66475 -1.80384,-0.38857 -3.90767,-0.32621 -5.50291,0.60116 -1.25017,0.72677 -1.78034,2.28608 -2.54336,3.51447 -0.50571,0.81414 -1.39111,3.50046 -1.34101,2.54336 z" + ] + }, + "16": { + "viewBox": "79.665565 107.085659 40.940990 77.253994", + "groupTransform": "translate(200.272121 0) scale(-1 1)", + "crown": "m 85.018788,170.22955 c -0.10444,-1.05469 0.132,-2.12813 0.39437,-3.15499 0.2659,-1.04067 0.85792,-1.97355 1.18312,-2.99724 0.33594,-1.05747 0.45041,-2.17714 0.78875,-3.23385 0.43174,-1.34841 0.79,-2.7671 1.57749,-3.94374 0.7549,-1.12796 1.73062,-2.18264 2.91836,-2.83948 1.75222,-0.969 3.86631,-1.06379 5.83672,-1.41975 1.74579,-0.31538 3.516682,-0.77832 5.284602,-0.631 1.58357,0.13196 3.13698,0.66411 4.57473,1.34087 0.97647,0.45963 1.92565,1.044 2.68174,1.81412 0.87874,0.89504 1.43679,2.06122 2.05074,3.15499 1.01307,1.80481 2.19378,3.58874 2.68174,5.60009 0.42168,1.73818 0.29217,3.57994 0.15775,5.36348 -0.0663,0.87935 -0.16474,1.77674 -0.47325,2.60286 -0.57735,1.54602 -1.42207,3.0307 -2.52399,4.25923 -0.87775,0.9786 -1.87201,2.07969 -3.15498,2.36624 -0.75988,0.16972 -1.56,-0.19547 -2.28737,-0.47325 -1.41968,-0.54217 -2.90294,-1.18002 -3.94373,-2.28736 -0.56369,-0.59973 -3.668372,-4.97137 -1.10424,-2.20849 0.30562,0.32931 0.0508,1.36853 -0.31551,1.893 -0.7618,1.09084 -2.279702,1.37829 -3.470482,1.97186 -0.94853,0.47281 -1.88578,1.02314 -2.91836,1.26199 -1.10264,0.25505 -2.3254,0.53733 -3.39161,0.15775 -1.59619,-0.56825 -2.68695,-2.10208 -3.78598,-3.39161 -0.85151,-0.9991 -1.56271,-2.12872 -2.12962,-3.31274 -0.28723,-0.59991 -0.56545,-1.23109 -0.63099,-1.89298 z", + "roots": [ + "m 89.356888,157.05748 c -0.71027,-1.59601 0.25839,-4.25788 0.23663,-6.38884 -0.0842,-8.24242 -1.44025,-16.44494 -1.41975,-24.68776 0.004,-1.68594 -0.21115,-3.4464 0.3155,-5.04798 0.30299,-0.9214 0.74069,-1.95467 1.5775,-2.44511 0.36363,-0.21312 0.88273,-0.26274 1.26199,-0.0789 0.74813,0.36269 1.16545,1.25917 1.41974,2.05074 0.49863,1.55216 -0.0948,3.26922 0.0789,4.89022 0.35714,3.33245 3.00459,16.48154 1.97186,9.85933 -0.56834,-3.64441 -0.0248,-8.90158 0.78875,-13.25094 0.53814,-2.87686 0.69686,-6.20985 2.68174,-8.3607 0.65562,-0.71044 1.770482,-1.42702 2.681732,-1.10425 0.62106,0.21998 0.83873,1.08525 0.9465,1.73524 0.25311,1.52657 -0.71551,3.02905 -0.78875,4.57473 -0.13103,2.76551 0.0579,5.57319 0.631,8.28183 0.35804,1.69219 1.61658,3.16465 1.73524,4.89023 0.59314,8.62585 2.07317,-4.86052 2.36624,-7.57196 0.22745,-2.10435 0.65605,-3.9413 1.34087,-5.67897 0.3279,-0.83202 2.07826,-2.07759 2.44511,-1.262 0.96594,2.14749 1.52585,3.3853 1.262,5.04798 -0.77771,4.90094 -1.16037,8.65698 -1.34087,13.01431 -0.16435,3.9674 -0.087,7.95248 0.23662,11.91007 0.21003,2.56813 0.68268,5.10885 1.10425,7.65084 0.14004,0.84445 0.47325,2.52398 0.47325,2.52398 0,0 -1.13641,-2.32189 -2.12962,-3.07611 -1.66208,-1.26215 -3.84373,-1.79992 -5.9156,-2.05074 -2.25437,-0.27291 -4.572412,0.11232 -6.783212,0.631 -1.92042,0.45055 -3.89261,1.01668 -5.52123,2.12962 -0.67607,0.462 -1.32346,2.56222 -1.65639,1.81411 z" + ] + }, + "17": { + "viewBox": "91.953264 97.168466 39.515047 67.713889", + "groupTransform": "translate(223.421575 0) scale(-1 1)", + "crown": "m 97.070075,155.08798 c -0.92333,-2.4489 -0.24057,-5.31833 0.46883,-7.83753 0.61491,-2.18365 1.50631,-4.49623 3.198315,-6.00739 1.51426,-1.35242 3.63744,-2.12252 5.66437,-2.23901 3.77096,-0.21673 6.80248,-0.48668 10.17875,-0.14744 2.07565,0.20856 4.43161,0.10106 6.11453,1.33378 1.15142,0.84341 1.54182,2.41377 2.13189,3.71335 0.81502,1.79502 1.75272,3.83923 1.9235,5.59261 0.25806,2.64949 -0.54068,5.03001 -2.0041,7.10627 -1.50821,2.1398 -4.30164,4.04289 -6.52147,2.80318 -2.01998,-1.1281 -3.99602,-1.17302 -5.97592,-3.9286 -1.52384,-2.12085 5.43467,-3.64985 0.17397,0.95929 -1.89016,1.65606 -5.29398,3.84301 -8.34665,3.77427 -1.45093,-0.0327 -2.70955,-1.11585 -3.88239,-1.97067 -1.195415,-0.87126 -2.601765,-1.768 -3.123625,-3.15211 z", + "roots": [ + "m 99.772075,143.52968 c -0.11891,-1.2184 0.518805,-2.39652 0.663455,-3.61212 0.57866,-4.86303 0.81088,-14.66962 0.81088,-14.66962 0,0 0.28083,-8.03734 1.54805,-11.79467 1.05876,-3.13924 2.60832,-6.27037 4.93902,-8.62485 0.5811,-0.58703 2.13778,-1.25319 2.13778,-1.25319 0,0 0.85977,0.51744 0.95832,0.95832 0.28486,1.27435 -0.99458,2.42303 -1.3269,3.68583 -0.52021,1.97678 -0.97598,4.00146 -1.03203,6.04477 -0.0827,3.01635 0.68328,5.99617 1.03203,8.99344 0.26658,2.29109 -0.74624,-4.52648 -0.51602,-6.78194 0.31669,-3.10262 1.57683,-6.04047 2.58009,-8.99343 0.60329,-1.77571 0.76213,-3.45401 2.06407,-5.23389 0.32439,-0.44347 1.11302,-0.5024 1.62177,-0.29487 0.66915,0.27297 1.06128,1.07246 1.25318,1.76921 0.24871,0.90302 -0.14224,1.86791 -0.22115,2.80123 -0.2536,2.99927 -0.52101,5.98127 -0.81089,8.99343 -0.18465,1.91877 1.16174,-3.67649 1.69549,-5.52875 0.44398,-1.54072 -0.0198,-3.66917 1.25318,-4.64415 0.33163,-0.254 0.86068,-0.14297 1.25318,0 0.75382,0.2746 1.47964,0.83274 1.84292,1.54805 0.65797,1.29559 0.34508,2.89707 0.29487,4.34929 -0.11829,3.42091 -0.91089,6.67837 -1.40062,10.1729 -0.41355,2.95089 0.17483,5.96901 0.58973,8.91972 0.4201,2.98769 1.1285,5.93368 1.91664,8.846 0.31057,1.14762 1.58098,4.63572 1.10575,3.39097 -0.36285,-0.95039 -1.02429,-1.8287 -1.84292,-2.43265 -0.69433,-0.51224 -1.58726,-0.71201 -2.43265,-0.8846 -1.7842,-0.36425 -3.63422,-0.26934 -5.45504,-0.29487 -1.89251,-0.0265 -3.78935,-0.001 -5.67618,0.14744 -1.68025,0.13264 -3.41721,0.12016 -5.01273,0.66345 -1.02136,0.34777 -2.06747,0.83075 -2.80124,1.62176 -0.53814,0.58012 -0.943305,3.04692 -1.032035,2.13779 z" + ] + }, + "18": { + "viewBox": "91.007827 100.243109 35.063725 66.119088", + "groupTransform": "translate(217.079380 0) scale(-1 1)", + "crown": "m 95.988161,150.67528 c 0.89896,-2.56651 0.79697,-2.63779 1.32689,-3.90699 0.16936,-0.40562 0.27525,-0.87237 0.58974,-1.17946 1.32125,-1.29017 3.237479,-1.85028 5.012729,-2.35894 2.40258,-0.6884 4.94672,-0.93771 7.44541,-0.8846 1.84167,0.0392 3.68695,0.36765 5.45501,0.88461 0.95074,0.27798 1.99732,0.51016 2.72753,1.17946 1.02933,0.94347 1.5145,2.37312 1.99036,3.68583 0.54689,1.50867 1.08008,3.11776 0.9583,4.71787 -0.13509,1.77513 -0.83926,3.54236 -1.84291,5.01274 -0.8145,1.19327 -2.10462,2.01897 -3.24353,2.87495 -1.06145,0.79776 -2.92202,0.65398 -3.98072,-0.14744 -1.21254,-0.91787 -3.85545,-1.09853 -3.53839,-2.94866 0.32982,-1.92457 0.72444,0.33508 -1.76921,2.28521 -1.35265,1.05782 -3.49184,2.63026 -5.74989,1.54805 -1.478669,-0.70868 -3.224909,-1.17376 -4.275579,-2.43265 -0.9511,-1.13959 -1.29045,-2.72895 -1.47433,-4.20185 -0.17115,-1.37088 0.11605,-1.98149 0.3686,-4.12813 0.14743,-1.25319 -10e-6,0 -10e-6,0 z", + "roots": [ + "m 97.978521,145.73626 c 1.75986,-3.28835 2.943009,-5.6697 3.906989,-8.69857 0.88859,-2.79198 1.18665,-5.7394 1.69549,-8.62485 0.69106,-3.9188 1.2332,-7.8624 1.84289,-11.79467 0.29711,-1.91629 0.19041,-3.93923 0.8846,-5.7499 0.57823,-1.5082 1.52959,-2.89462 2.6538,-4.05442 0.62629,-0.64612 1.35867,-1.26109 2.21152,-1.54805 0.60596,-0.2039 1.46911,-0.88759 1.91662,-0.0737 0.50538,0.91912 0.92189,1.86273 1.03203,2.87495 0.18761,1.72403 -0.48713,4.56965 -0.66343,5.16017 -1.88596,6.31702 1.05935,4.92393 0.81088,10.24662 -0.23651,5.06643 -5.73733,5.34567 0.81087,-14.22732 0.12178,-0.36401 0.54563,-1.82199 1.32691,-2.06407 0.48387,-0.14992 1.01657,0.25939 1.4006,0.58973 0.39297,0.33803 0.66014,0.83095 0.8109,1.3269 0.34333,1.12947 0.33159,2.49914 0.14742,3.5384 -0.4766,2.68944 -0.61534,3.64099 -0.95832,5.45504 -0.36796,1.94614 -0.91051,3.86134 -1.17946,5.82362 -0.18401,1.34249 -0.32301,2.69967 -0.29485,4.05441 0.0807,3.88228 0.48087,7.7837 1.32689,11.57352 0.50281,2.25239 1.3697,4.64426 2.21149,6.56079 0.94772,2.15771 -1.97494,-1.84424 -3.16981,-2.43265 -1.02665,-0.50558 -2.18562,-0.71413 -3.31724,-0.8846 -1.89665,-0.28573 -3.83951,-0.39245 -5.7499,-0.22115 -1.92989,0.17304 -3.82318,0.68682 -5.67619,1.25318 -0.96002,0.29343 -1.91509,0.63408 -2.801249,1.10575 -0.42116,0.22417 -0.95734,0.38864 -1.17945,0.81089 0,0 0,0 0,0 z" + ] + }, + "21": { + "viewBox": "51.660933 83.146350 38.944857 87.264388", + "crown": "m 62.647814,135.73384 c 0.341028,-0.92433 0.636863,-1.87523 1.110468,-2.73917 0.55297,-1.00871 1.16079,-2.02522 1.99886,-2.8132 0.78325,-0.73643 1.67713,-1.53569 2.73916,-1.70272 1.36726,-0.21503 3.01579,-0.28218 4.51592,-0.14808 0.70385,0.0629 1.41612,0.18341 2.07289,0.4442 0.88501,0.35142 1.77486,0.80219 2.44303,1.48064 0.82263,0.83529 1.25279,1.98653 1.77676,3.03529 0.37075,0.74207 0.69884,1.50842 0.96241,2.29497 0.41266,1.23148 0.63257,2.51941 0.96241,3.77561 0.4804,1.82961 0.9268,3.66959 1.48063,5.47833 0.26732,0.87304 0.62604,1.71654 0.88838,2.5911 0.227,0.75674 0.45137,1.51759 0.59225,2.29498 0.20797,1.14768 0.33655,2.31359 0.37016,3.47948 0.0441,1.53114 0.0671,3.08573 -0.2221,4.58995 -0.30059,1.56338 -0.62675,3.22229 -1.55466,4.51593 -0.39989,0.55751 -0.99264,1.00132 -1.62869,1.25853 -0.82866,0.33509 -1.77525,0.21211 -2.66514,0.29613 -2.21778,0.2094 -4.4354,0.48651 -6.66282,0.51822 -3.01395,0.0429 -6.04047,-0.0736 -9.031859,-0.44426 -1.248744,-0.15472 -2.734205,0.0644 -3.701573,-0.74031 -0.817326,-0.67987 -0.988842,-1.9163 -1.184503,-2.96127 -0.440565,-2.35292 -0.154978,-4.79822 0.07403,-7.18106 0.144009,-1.4984 0.495914,-2.97064 0.814345,-4.44188 0.38742,-1.78999 0.853156,-3.56269 1.332568,-5.33026 0.27596,-1.01745 0.551791,-2.03627 0.888379,-3.03531 0.286792,-0.85123 0.659911,-1.67125 0.96241,-2.51703 0.236518,-0.6613 0.42318,-1.33994 0.666282,-1.99885 z", + "roots": [ + "m 62.943939,135.95593 c 0,0 0.08367,-2.37479 0.222095,-3.55351 0.30378,-2.58681 0.909078,-5.12936 1.332568,-7.69928 0.46704,-2.8342 0.85124,-5.68181 1.33257,-8.51362 0.45826,-2.69605 1.23284,-5.34597 1.48063,-8.06943 0.14534,-1.59742 -0.11495,-3.21216 0,-4.81205 0.16082,-2.23823 0.29166,-4.521436 0.96241,-6.662838 0.47832,-1.527054 1.26926,-2.955761 2.14691,-4.293825 0.65331,-0.99603 1.2756,-2.122935 2.29498,-2.739168 0.49706,-0.300483 1.16538,-0.590669 1.70272,-0.370157 0.62688,0.257255 0.87646,1.066803 1.11047,1.702726 2.1479,5.836971 1.78491,12.315732 2.36901,18.507872 0.53401,5.66112 0.47036,11.36924 1.03644,17.02725 0.31998,3.19822 1.33257,9.55006 1.33257,9.55006 0,0 -1.11923,-3.66392 -2.369,-5.03414 -0.98396,-1.07879 -2.37532,-1.80722 -3.77561,-2.22094 -1.65813,-0.4899 -3.51706,-0.68761 -5.18221,-0.22213 -1.19966,0.33535 -2.2202,1.20048 -3.10933,2.07288 -0.77202,0.7575 -1.85078,2.66514 -1.85078,2.66514 l -1.036442,1.70272" + ] + }, + "22": { + "viewBox": "93.685783 101.132481 28.706127 78.417208", + "crown": "m 106.17348,144.89797 c 0,0 3.4638,-0.0314 5.03413,0.59222 0.99998,0.3971 1.91519,1.0877 2.59111,1.92482 0.56195,0.69598 0.86436,1.58304 1.11047,2.44304 0.26022,0.90931 0.23314,1.87736 0.37015,2.8132 0.33674,2.30011 0.8184,4.57871 1.11048,6.88492 0.2024,1.59807 0.37007,3.20293 0.44419,4.81206 0.0704,1.52836 0.29878,3.08942 3e-5,4.58995 -0.21387,1.07421 -0.57809,2.17701 -1.25854,3.03529 -0.60414,0.76206 -1.45491,1.37082 -2.369,1.70272 -1.58155,0.57426 -3.35233,0.31948 -5.03415,0.37016 -1.87478,0.0565 -3.76637,0.16741 -5.6264,-0.074 -0.9081,-0.11786 -2.05115,0.0131 -2.665126,-0.66628 -1.15359,-1.27648 -0.68012,-3.38871 -0.74031,-5.10817 -0.0468,-1.3354 0.12,-2.67314 0.29612,-3.9977 0.27493,-2.06772 0.744106,-4.10572 1.184496,-6.14462 0.38036,-1.76099 0.80338,-3.51308 1.25854,-5.25624 0.34341,-1.31517 0.65674,-2.64237 1.11048,-3.92366 0.31368,-0.88578 1.11046,-2.59111 1.11046,-2.59111", + "roots": [ + "m 103.65639,146.97086 c 0.28824,-1.47746 0.58952,-2.95347 0.81432,-4.4419 0.31175,-2.06411 0.4672,-4.14906 0.74031,-6.21864 0.4502,-3.41145 1.03766,-6.80395 1.48063,-10.21635 0.3901,-3.00516 0.78791,-6.01168 1.03644,-9.03185 0.16211,-1.96998 0.0603,-3.95999 0.29612,-5.92252 0.14088,-1.17246 -0.0183,-2.51724 0.66629,-3.47948 0.4294,-0.60357 1.1866,-1.04961 1.92482,-1.11047 0.84051,-0.0693 1.80618,0.26028 2.369,0.88838 0.93463,1.04303 0.78133,2.69464 1.03645,4.07173 0.46167,2.49203 0.74006,5.02113 0.88838,7.55121 0.35959,6.13409 0.0737,12.28968 0,18.43385 -0.037,3.08531 0.15869,6.19199 -0.2221,9.25394 -0.0687,0.55248 0.18433,1.57826 -0.37012,1.62869 -0.18717,0.017 -0.11387,-0.36457 -0.22209,-0.51822 -0.54298,-0.77088 -1.24391,-1.47551 -2.07289,-1.92482 -0.89452,-0.48483 -1.46818,-0.63743 -2.96125,-0.74032 -1.15735,-0.0798 -2.21081,-0.13961 -3.18335,0.29613 -0.89232,0.3998 -1.62374,1.1728 -2.14692,1.99885 -0.30484,0.48131 -0.51825,1.62871 -0.51825,1.62871 0,0 0.30428,-1.42965 0.44421,-2.14692 z" + ] + }, + "23": { + "viewBox": "92.847964 96.106573 35.728809 97.202657", + "crown": "m 99.551769,176.26584 c -0.0141,-1.55533 0.583831,-3.0579 0.977691,-4.5626 0.27842,-1.06369 0.69205,-2.08858 0.97771,-3.15036 0.33776,-1.25543 0.58103,-2.5344 0.86905,-3.80217 0.25495,-1.12221 0.43819,-2.26287 0.76044,-3.36763 0.26943,-0.92366 0.51784,-1.87069 0.9777,-2.71583 0.45692,-0.83974 1.05465,-1.61288 1.73813,-2.2813 0.70425,-0.68873 1.499,-1.31796 2.38994,-1.73813 1.04856,-0.49451 2.20982,-0.92825 3.36762,-0.86907 1.04125,0.0532 2.04158,0.54576 2.9331,1.08633 0.87688,0.53169 1.66722,1.24275 2.28131,2.06404 0.6498,0.86905 1.01396,1.9237 1.41223,2.93309 0.44557,1.1293 0.73398,2.31451 1.08633,3.47627 0.37246,1.22809 0.74648,2.45602 1.08633,3.69353 0.46516,1.69381 1.12021,3.35885 1.3036,5.10577 0.1854,1.76611 0.31102,3.62761 -0.21726,5.32303 -0.48415,1.55381 -1.62154,2.833 -2.60721,4.12806 -0.65205,0.85672 -1.34836,1.69736 -2.17266,2.38994 -0.9845,0.82718 -2.06044,1.59826 -3.259,2.06402 -0.96001,0.37306 -2.01622,0.63873 -3.04172,0.54317 -1.11828,-0.10421 -2.17801,-0.63288 -3.15037,-1.19496 -1.3367,-0.77271 -2.40515,-1.93711 -3.58491,-2.9331 -1.13747,-0.96029 -2.60004,-1.65763 -3.36762,-2.9331 -0.575191,-0.95578 -0.750311,-2.14353 -0.760431,-3.259 z", + "roots": [ + "m 117.25898,158.99315 c 0.0309,-5.1946 -0.605,-6.99748 -0.54316,-9.23383 0.11896,-4.30188 -0.1936,-7.83304 -0.54316,-11.73239 -0.34249,-3.82049 -0.99928,-7.60631 -1.52087,-11.4065 -0.42268,-3.07959 -1.02386,-6.13798 -1.3036,-9.23383 -0.2446,-2.70697 -0.51897,-5.43636 -0.3259,-8.14749 0.0994,-1.39564 0.99506,-2.74871 0.76043,-4.12807 -0.14687,-0.86342 -0.44168,-2.12583 -1.3036,-2.2813 -1.11331,-0.20081 -2.04194,1.19274 -2.6072,2.17267 -1.70742,2.95995 -1.32174,6.71125 -1.73813,10.1029 -0.83624,6.8115 -0.84156,13.70274 -1.52086,20.53168 -0.78024,7.84376 -1.61721,15.69294 -2.9331,23.46479 -0.0866,0.51119 -0.80686,1.32726 -0.3259,1.52087 0.82965,0.33397 0.90805,-1.54261 1.41223,-2.2813 0.51034,-0.74772 0.95883,-1.56461 1.6295,-2.17267 0.87875,-0.79672 1.90738,-1.50006 3.04173,-1.84676 0.72805,-0.22252 1.52279,-0.17368 2.2813,-0.10864 0.66526,0.057 1.35819,0.13593 1.9554,0.43454 0.89581,0.44791 1.65094,1.1771 2.2813,1.9554 0.57113,0.70517 1.2982,3.29735 1.30359,2.38993 z" + ] + }, + "24": { + "viewBox": "78.778460 104.429020 32.880110 81.849457", + "crown": "m 103.54613,162.08292 c 0,0 1.51697,3.7883 1.90985,5.77856 0.31375,1.5894 0.86374,3.3141 0.34277,4.84812 -0.68048,2.00371 -2.6583,3.313 -4.21149,4.75018 -1.11188,1.02883 -2.195824,2.2369 -3.623854,2.74237 -1.41645,0.50138 -3.06843,0.58631 -4.50532,0.14691 -2.15763,-0.65981 -3.32378,-1.98281 -5.48474,-3.96664 -1.74427,-1.60129 -3.27631,-3.95329 -3.52591,-6.36622 -0.21622,-2.09029 1.06703,-4.0777 1.8609,-6.02342 1.19972,-2.94044 2.39588,-5.37003 4.3584,-8.47196 0.49409,-0.78095 1.36295,-1.36623 2.25267,-1.61604 1.26081,-0.35399 2.66615,-0.0917 3.91767,0.29382 1.06231,0.32724 2.01728,0.97567 2.88928,1.66501 0.914954,0.7233 1.778634,1.55922 2.399584,2.54649 0.36314,0.57738 0.73456,1.90987 0.73456,1.90987", + "roots": [ + "m 88.707936,158.80187 c 0,0 0.8366,-12.32924 1.56707,-18.46203 0.64849,-5.44453 1.82049,-10.81728 2.49752,-16.25833 0.44545,-3.57992 0.0522,-6.93841 1.02838,-10.7736 0.26861,-1.05527 1.24173,-1.83478 2.10575,-2.49752 0.45775,-0.35111 0.99122,-0.76923 1.56707,-0.73456 0.61487,0.037 1.15915,0.51785 1.56706,0.97942 1.025294,1.16012 2.080354,2.66802 1.958854,4.2115 -0.40846,5.18879 -1.867514,9.88427 -1.909874,14.88715 -0.0826,9.75556 2.747834,21.07548 3.232084,29.08871 0.0654,1.08192 -1.14123,-1.88388 -1.95884,-2.59546 -1.240224,-1.07939 -2.540834,-1.9818 -4.309434,-2.39957 -1.53319,-0.36216 -3.32305,-0.0131 -4.65224,0.8325 -1.14637,0.7293 -2.3506,3.33002 -2.3506,3.33002" + ] + }, + "25": { + "viewBox": "80.851597 94.968970 32.298645 79.507376", + "crown": "m 86.514392,158.88671 c -0.36012,-1.61219 -0.13293,-3.34785 0.27746,-4.94799 0.29289,-1.142 1.02772,-2.12227 1.52601,-3.19076 0.86591,-1.85678 1.56594,-3.922 2.54337,-5.59539 0.66552,-1.1394 1.92472,-1.90141 3.14452,-2.40463 1.32204,-0.5454 2.84482,-0.79679 4.25435,-0.55491 1.14144,0.19587 2.171618,0.87758 3.098268,1.57225 0.82892,0.62141 1.52639,1.42193 2.12718,2.2659 1.21154,1.70196 2.12254,3.61546 2.91329,5.54916 0.38392,0.93884 0.72328,1.91096 0.87862,2.9133 0.32345,2.08707 0.63944,4.29535 0.0925,6.33529 -0.35805,1.3354 -1.26504,2.47987 -2.12718,3.5607 -1.19654,1.50005 -2.38722,3.24719 -4.16187,3.97689 -1.625458,0.66835 -3.737098,0.94912 -5.271688,0.0925 -1.46829,-0.81963 -2.85268,-2.38304 -4.20811,-3.65318 -1.43957,-1.34899 -2.98731,-2.623 -4.16186,-4.20811 -0.38598,-0.5209 -0.78352,-1.07827 -0.92486,-1.711 z", + "roots": [ + "m 89.057752,149.36068 c 0.25498,-4.87107 1.46284,-15.84832 2.5896,-23.72263 1.0378,-7.25259 2.08507,-14.0731 3.83817,-21.64169 0.32031,-1.38284 1.03251,-3.15094 2.40463,-3.51446 0.61309,-0.16243 1.27063,0.37592 1.71099,0.83237 0.819338,0.84928 1.430568,2.05694 1.433528,3.23701 0.0207,8.23338 -0.44305,15.33744 0.0462,22.98273 0.33736,5.27183 1.20807,10.49795 1.98845,15.7226 0.20103,1.3459 0.95394,2.42655 0.69364,4.02313 -0.20581,1.26235 -1.51695,-2.09162 -2.49712,-2.9133 -0.8367,-0.70141 -1.753488,-1.43483 -2.820808,-1.66475 -1.80384,-0.38857 -3.90767,-0.32621 -5.50291,0.60116 -1.25017,0.72677 -1.78034,2.28608 -2.54336,3.51447 -0.50571,0.81414 -1.39111,3.50046 -1.34101,2.54336 z" + ] + }, + "26": { + "viewBox": "79.665565 107.085659 40.940990 77.253994", + "crown": "m 85.018788,170.22955 c -0.10444,-1.05469 0.132,-2.12813 0.39437,-3.15499 0.2659,-1.04067 0.85792,-1.97355 1.18312,-2.99724 0.33594,-1.05747 0.45041,-2.17714 0.78875,-3.23385 0.43174,-1.34841 0.79,-2.7671 1.57749,-3.94374 0.7549,-1.12796 1.73062,-2.18264 2.91836,-2.83948 1.75222,-0.969 3.86631,-1.06379 5.83672,-1.41975 1.74579,-0.31538 3.516682,-0.77832 5.284602,-0.631 1.58357,0.13196 3.13698,0.66411 4.57473,1.34087 0.97647,0.45963 1.92565,1.044 2.68174,1.81412 0.87874,0.89504 1.43679,2.06122 2.05074,3.15499 1.01307,1.80481 2.19378,3.58874 2.68174,5.60009 0.42168,1.73818 0.29217,3.57994 0.15775,5.36348 -0.0663,0.87935 -0.16474,1.77674 -0.47325,2.60286 -0.57735,1.54602 -1.42207,3.0307 -2.52399,4.25923 -0.87775,0.9786 -1.87201,2.07969 -3.15498,2.36624 -0.75988,0.16972 -1.56,-0.19547 -2.28737,-0.47325 -1.41968,-0.54217 -2.90294,-1.18002 -3.94373,-2.28736 -0.56369,-0.59973 -3.668372,-4.97137 -1.10424,-2.20849 0.30562,0.32931 0.0508,1.36853 -0.31551,1.893 -0.7618,1.09084 -2.279702,1.37829 -3.470482,1.97186 -0.94853,0.47281 -1.88578,1.02314 -2.91836,1.26199 -1.10264,0.25505 -2.3254,0.53733 -3.39161,0.15775 -1.59619,-0.56825 -2.68695,-2.10208 -3.78598,-3.39161 -0.85151,-0.9991 -1.56271,-2.12872 -2.12962,-3.31274 -0.28723,-0.59991 -0.56545,-1.23109 -0.63099,-1.89298 z", + "roots": [ + "m 89.356888,157.05748 c -0.71027,-1.59601 0.25839,-4.25788 0.23663,-6.38884 -0.0842,-8.24242 -1.44025,-16.44494 -1.41975,-24.68776 0.004,-1.68594 -0.21115,-3.4464 0.3155,-5.04798 0.30299,-0.9214 0.74069,-1.95467 1.5775,-2.44511 0.36363,-0.21312 0.88273,-0.26274 1.26199,-0.0789 0.74813,0.36269 1.16545,1.25917 1.41974,2.05074 0.49863,1.55216 -0.0948,3.26922 0.0789,4.89022 0.35714,3.33245 3.00459,16.48154 1.97186,9.85933 -0.56834,-3.64441 -0.0248,-8.90158 0.78875,-13.25094 0.53814,-2.87686 0.69686,-6.20985 2.68174,-8.3607 0.65562,-0.71044 1.770482,-1.42702 2.681732,-1.10425 0.62106,0.21998 0.83873,1.08525 0.9465,1.73524 0.25311,1.52657 -0.71551,3.02905 -0.78875,4.57473 -0.13103,2.76551 0.0579,5.57319 0.631,8.28183 0.35804,1.69219 1.61658,3.16465 1.73524,4.89023 0.59314,8.62585 2.07317,-4.86052 2.36624,-7.57196 0.22745,-2.10435 0.65605,-3.9413 1.34087,-5.67897 0.3279,-0.83202 2.07826,-2.07759 2.44511,-1.262 0.96594,2.14749 1.52585,3.3853 1.262,5.04798 -0.77771,4.90094 -1.16037,8.65698 -1.34087,13.01431 -0.16435,3.9674 -0.087,7.95248 0.23662,11.91007 0.21003,2.56813 0.68268,5.10885 1.10425,7.65084 0.14004,0.84445 0.47325,2.52398 0.47325,2.52398 0,0 -1.13641,-2.32189 -2.12962,-3.07611 -1.66208,-1.26215 -3.84373,-1.79992 -5.9156,-2.05074 -2.25437,-0.27291 -4.572412,0.11232 -6.783212,0.631 -1.92042,0.45055 -3.89261,1.01668 -5.52123,2.12962 -0.67607,0.462 -1.32346,2.56222 -1.65639,1.81411 z" + ] + }, + "27": { + "viewBox": "91.953264 97.168466 39.515047 67.713889", + "crown": "m 97.070075,155.08798 c -0.92333,-2.4489 -0.24057,-5.31833 0.46883,-7.83753 0.61491,-2.18365 1.50631,-4.49623 3.198315,-6.00739 1.51426,-1.35242 3.63744,-2.12252 5.66437,-2.23901 3.77096,-0.21673 6.80248,-0.48668 10.17875,-0.14744 2.07565,0.20856 4.43161,0.10106 6.11453,1.33378 1.15142,0.84341 1.54182,2.41377 2.13189,3.71335 0.81502,1.79502 1.75272,3.83923 1.9235,5.59261 0.25806,2.64949 -0.54068,5.03001 -2.0041,7.10627 -1.50821,2.1398 -4.30164,4.04289 -6.52147,2.80318 -2.01998,-1.1281 -3.99602,-1.17302 -5.97592,-3.9286 -1.52384,-2.12085 5.43467,-3.64985 0.17397,0.95929 -1.89016,1.65606 -5.29398,3.84301 -8.34665,3.77427 -1.45093,-0.0327 -2.70955,-1.11585 -3.88239,-1.97067 -1.195415,-0.87126 -2.601765,-1.768 -3.123625,-3.15211 z", + "roots": [ + "m 99.772075,143.52968 c -0.11891,-1.2184 0.518805,-2.39652 0.663455,-3.61212 0.57866,-4.86303 0.81088,-14.66962 0.81088,-14.66962 0,0 0.28083,-8.03734 1.54805,-11.79467 1.05876,-3.13924 2.60832,-6.27037 4.93902,-8.62485 0.5811,-0.58703 2.13778,-1.25319 2.13778,-1.25319 0,0 0.85977,0.51744 0.95832,0.95832 0.28486,1.27435 -0.99458,2.42303 -1.3269,3.68583 -0.52021,1.97678 -0.97598,4.00146 -1.03203,6.04477 -0.0827,3.01635 0.68328,5.99617 1.03203,8.99344 0.26658,2.29109 -0.74624,-4.52648 -0.51602,-6.78194 0.31669,-3.10262 1.57683,-6.04047 2.58009,-8.99343 0.60329,-1.77571 0.76213,-3.45401 2.06407,-5.23389 0.32439,-0.44347 1.11302,-0.5024 1.62177,-0.29487 0.66915,0.27297 1.06128,1.07246 1.25318,1.76921 0.24871,0.90302 -0.14224,1.86791 -0.22115,2.80123 -0.2536,2.99927 -0.52101,5.98127 -0.81089,8.99343 -0.18465,1.91877 1.16174,-3.67649 1.69549,-5.52875 0.44398,-1.54072 -0.0198,-3.66917 1.25318,-4.64415 0.33163,-0.254 0.86068,-0.14297 1.25318,0 0.75382,0.2746 1.47964,0.83274 1.84292,1.54805 0.65797,1.29559 0.34508,2.89707 0.29487,4.34929 -0.11829,3.42091 -0.91089,6.67837 -1.40062,10.1729 -0.41355,2.95089 0.17483,5.96901 0.58973,8.91972 0.4201,2.98769 1.1285,5.93368 1.91664,8.846 0.31057,1.14762 1.58098,4.63572 1.10575,3.39097 -0.36285,-0.95039 -1.02429,-1.8287 -1.84292,-2.43265 -0.69433,-0.51224 -1.58726,-0.71201 -2.43265,-0.8846 -1.7842,-0.36425 -3.63422,-0.26934 -5.45504,-0.29487 -1.89251,-0.0265 -3.78935,-0.001 -5.67618,0.14744 -1.68025,0.13264 -3.41721,0.12016 -5.01273,0.66345 -1.02136,0.34777 -2.06747,0.83075 -2.80124,1.62176 -0.53814,0.58012 -0.943305,3.04692 -1.032035,2.13779 z" + ] + }, + "28": { + "viewBox": "91.007827 100.243109 35.063725 66.119088", + "crown": "m 95.988161,150.67528 c 0.89896,-2.56651 0.79697,-2.63779 1.32689,-3.90699 0.16936,-0.40562 0.27525,-0.87237 0.58974,-1.17946 1.32125,-1.29017 3.237479,-1.85028 5.012729,-2.35894 2.40258,-0.6884 4.94672,-0.93771 7.44541,-0.8846 1.84167,0.0392 3.68695,0.36765 5.45501,0.88461 0.95074,0.27798 1.99732,0.51016 2.72753,1.17946 1.02933,0.94347 1.5145,2.37312 1.99036,3.68583 0.54689,1.50867 1.08008,3.11776 0.9583,4.71787 -0.13509,1.77513 -0.83926,3.54236 -1.84291,5.01274 -0.8145,1.19327 -2.10462,2.01897 -3.24353,2.87495 -1.06145,0.79776 -2.92202,0.65398 -3.98072,-0.14744 -1.21254,-0.91787 -3.85545,-1.09853 -3.53839,-2.94866 0.32982,-1.92457 0.72444,0.33508 -1.76921,2.28521 -1.35265,1.05782 -3.49184,2.63026 -5.74989,1.54805 -1.478669,-0.70868 -3.224909,-1.17376 -4.275579,-2.43265 -0.9511,-1.13959 -1.29045,-2.72895 -1.47433,-4.20185 -0.17115,-1.37088 0.11605,-1.98149 0.3686,-4.12813 0.14743,-1.25319 -10e-6,0 -10e-6,0 z", + "roots": [ + "m 97.978521,145.73626 c 1.75986,-3.28835 2.943009,-5.6697 3.906989,-8.69857 0.88859,-2.79198 1.18665,-5.7394 1.69549,-8.62485 0.69106,-3.9188 1.2332,-7.8624 1.84289,-11.79467 0.29711,-1.91629 0.19041,-3.93923 0.8846,-5.7499 0.57823,-1.5082 1.52959,-2.89462 2.6538,-4.05442 0.62629,-0.64612 1.35867,-1.26109 2.21152,-1.54805 0.60596,-0.2039 1.46911,-0.88759 1.91662,-0.0737 0.50538,0.91912 0.92189,1.86273 1.03203,2.87495 0.18761,1.72403 -0.48713,4.56965 -0.66343,5.16017 -1.88596,6.31702 1.05935,4.92393 0.81088,10.24662 -0.23651,5.06643 -5.73733,5.34567 0.81087,-14.22732 0.12178,-0.36401 0.54563,-1.82199 1.32691,-2.06407 0.48387,-0.14992 1.01657,0.25939 1.4006,0.58973 0.39297,0.33803 0.66014,0.83095 0.8109,1.3269 0.34333,1.12947 0.33159,2.49914 0.14742,3.5384 -0.4766,2.68944 -0.61534,3.64099 -0.95832,5.45504 -0.36796,1.94614 -0.91051,3.86134 -1.17946,5.82362 -0.18401,1.34249 -0.32301,2.69967 -0.29485,4.05441 0.0807,3.88228 0.48087,7.7837 1.32689,11.57352 0.50281,2.25239 1.3697,4.64426 2.21149,6.56079 0.94772,2.15771 -1.97494,-1.84424 -3.16981,-2.43265 -1.02665,-0.50558 -2.18562,-0.71413 -3.31724,-0.8846 -1.89665,-0.28573 -3.83951,-0.39245 -5.7499,-0.22115 -1.92989,0.17304 -3.82318,0.68682 -5.67619,1.25318 -0.96002,0.29343 -1.91509,0.63408 -2.801249,1.10575 -0.42116,0.22417 -0.95734,0.38864 -1.17945,0.81089 0,0 0,0 0,0 z" + ] + }, + "31": { + "viewBox": "96.120373 98.955384 26.355569 77.981387", + "crown": "m 101.53441,106.02036 c -0.004,-0.90793 7.9e-4,-1.91402 1.4152,-1.54385 1.66002,0.43445 3.43039,0.10721 5.14619,0.12865 1.97255,0.0247 4.04665,-0.62383 5.91812,0 0.61026,0.20342 1.13168,0.66402 1.54385,1.15789 0.61444,0.73623 1.113,1.63 1.28655,2.5731 0.85803,4.66257 -0.6772,9.46894 -1.4152,14.15201 -0.50543,3.20724 -0.0832,6.94363 -2.05848,9.52045 -0.96851,1.26344 -2.65465,2.13078 -4.2456,2.18713 -1.63199,0.0578 -3.4372,-0.6925 -4.50292,-1.92982 -1.21511,-1.41076 -0.95265,-2.79984 -1.4152,-5.4035 -0.93526,-5.26444 -1.39338,-10.6136 -1.67251,-15.95318 -0.0851,-1.6274 0.008,-3.25927 0,-4.88888 z", + "roots": [ + "m 103.46423,130.72207 c 0.15401,3.18195 0.66236,7.37849 1.02924,11.0643 0.56384,5.66456 0.5816,11.42204 1.80116,16.98242 0.65952,3.00699 1.69054,5.94356 2.95906,8.74852 0.53043,1.1729 0.88055,2.59932 1.92982,3.34503 0.74728,0.53109 1.89545,0.95091 2.70175,0.51462 0.69343,-0.37521 0.81524,-1.40333 0.90059,-2.18713 0.26866,-2.46719 -1.08752,-4.85954 -1.28656,-7.33332 -0.78768,-9.78985 0.0492,-19.64622 0.38597,-29.46193 0.0265,-0.77267 0.86562,-2.08212 0.12866,-2.31579 -0.77131,-0.24456 -0.72287,1.47797 -1.28655,2.05848 -0.6463,0.66559 -1.42266,1.29282 -2.31579,1.54386 -1.03295,0.29034 -2.18083,0.15233 -3.21636,-0.12866 -0.7036,-0.19092 -1.34405,-0.59522 -1.92982,-1.02924 -0.68222,-0.50548 -1.88499,-3.5329 -1.80117,-1.80116 z" + ] + }, + "32": { + "viewBox": "90.716566 83.433116 30.144529 84.042377", + "crown": "m 96.586049,90.087282 c 0.099,-1.32852 3.15696,-0.65578 4.760231,-0.77193 2.65226,-0.19214 5.32184,-0.0252 7.97659,0.12866 1.2908,0.0748 2.73919,-0.25928 3.85964,0.38596 0.82516,0.47518 1.39149,1.40599 1.67251,2.31578 0.48111,1.55758 0.003,3.26402 -0.12865,4.88888 -0.2309,2.84725 -0.62663,5.684448 -1.1579,8.491208 -0.30371,1.60452 -0.62823,2.51235 -1.1579,4.76023 -0.49901,2.11779 -1.72922,5.17181 -2.70174,6.81869 -0.95492,1.61708 -1.57917,1.9001 -2.70174,2.18714 -1.39357,0.35634 -3.01996,-0.0191 -4.24561,-0.77193 -1.33015,-0.81703 -2.12986,-2.33597 -2.830401,-3.73099 -0.76134,-1.5161 -1.06626,-3.22862 -1.41521,-4.88888 -0.80089,-3.81054 -1.19506,-7.70078 -1.54385,-11.578928 -0.24612,-2.7366 -0.61873,-5.10979 -0.38597,-8.23389 z", + "roots": [ + "m 98.644529,110.67203 c 0.30392,5.31017 0.51318,10.2125 0.90058,15.30992 0.38515,5.06775 0.781401,10.13857 1.415201,15.18126 0.54688,4.35115 0.7191,8.81828 2.05848,12.99412 0.70291,2.19148 1.35687,4.65858 3.08771,6.17543 0.98936,0.86705 2.44386,1.55841 3.73099,1.28654 0.80163,-0.16932 1.52773,-0.90017 1.80116,-1.67251 0.40481,-1.14342 -0.3716,-2.39782 -0.51461,-3.60233 -0.48248,-4.06377 -0.97672,-8.1339 -1.15789,-12.2222 -0.32277,-7.28341 -0.27489,-14.59192 0.12865,-21.87131 0.13605,-2.45417 0.34416,-4.91289 0.77193,-7.33332 0.18464,-1.04472 0.4727,-2.01275 0.77192,-3.08771 0.79704,-2.86339 -0.63975,3.11989 -1.54385,4.37426 -0.85771,1.19 -2.04603,2.36473 -3.47368,2.70176 -1.27693,0.30144 -2.76313,-0.0515 -3.85964,-0.77194 -1.13338,-0.7447 -1.65496,-2.16078 -2.31579,-3.34502 -0.729911,-1.30803 -1.886751,-5.61241 -1.801161,-4.11695 z" + ] + }, + "33": { + "viewBox": "88.435127 109.232700 36.367350 100.346404", + "crown": "m 97.481714,139.06536 c -1.02952,-3.60735 -1.86235,-7.36964 -2.05848,-11.0643 -0.13281,-2.5018 -0.25389,-4.89725 1.54387,-6.69005 1.37473,-1.37094 2.62145,-1.85901 3.988286,-2.70174 1.42179,-0.87661 2.70826,-2.32441 4.37425,-2.44444 2.15277,-0.1551 4.16929,1.2511 6.04678,2.31578 1.99255,1.12993 4.18335,2.30699 5.40349,4.24561 0.84242,1.33848 0.89162,3.05605 1.02924,4.63157 0.16809,1.9244 0.03,3.87922 -0.25731,5.78946 -0.50738,3.37377 -1.41144,6.69856 -2.57309,9.90641 -0.85567,2.36291 -1.36536,5.11891 -3.21637,6.8187 -1.17934,1.083 -2.90498,1.77402 -4.50292,1.67251 -2.02921,-0.12891 -3.96258,-1.3958 -5.40349,-2.8304 -1.16915,-1.16403 -1.6288,-2.87428 -2.315786,-4.37426 -0.78593,-1.71601 -1.54049,-3.45989 -2.05847,-5.27485 z", + "roots": [ + "m 98.768264,141.89577 c 0.33837,4.62951 -0.008,8.41296 0.25731,12.60816 0.51203,8.09167 1.536686,16.14575 2.573086,24.18709 0.5489,4.25883 0.76347,8.60419 1.92982,12.73681 0.89783,3.18121 1.61363,6.70928 3.85964,9.13449 1.01655,1.09765 2.517,2.32957 3.9883,2.05847 1.07525,-0.19812 1.91388,-1.38577 2.18713,-2.44443 0.38479,-1.49082 -0.82342,-2.97706 -1.02924,-4.50292 -0.92386,-6.84896 -1.05905,-13.80423 -0.90058,-20.71341 0.13229,-5.76806 1.1424,-11.48414 1.54385,-17.23973 0.23901,-3.42675 -0.0569,-6.0074 0.51462,-10.29238 0.079,-0.59208 -0.24708,-0.81225 0.12865,-2.31578 1.10138,-4.40734 -0.33356,2.48653 -1.02923,3.47368 -0.79552,1.12885 -2.01307,2.07967 -3.34503,2.44443 -1.28821,0.35278 -2.76085,0.14065 -3.98829,-0.38596 -1.56457,-0.67125 -2.80294,-2.01017 -3.85964,-3.34502 -1.26203,-1.59424 -2.984186,-7.50755 -2.830396,-5.4035 z" + ] + }, + "34": { + "viewBox": "104.357943 105.031043 33.984660 82.365998", + "crown": "m 110.75011,118.8249 c 1.11967,-2.53106 3.21024,-4.61602 5.4035,-6.30408 1.09439,-0.84231 2.35205,-1.72568 3.73099,-1.80117 2.1805,-0.11937 4.30005,1.0683 6.17542,2.18714 2.08595,1.24446 4.13999,2.8144 5.4035,4.88887 0.78459,1.28818 1.03803,2.87073 1.1579,4.37426 0.17124,2.14813 -0.2266,4.31847 -0.64328,6.43274 -0.40666,2.06342 -1.4599,4.00504 -1.80117,6.04677 -0.28726,1.71858 -0.79531,3.04869 -1.28654,5.14619 -0.46386,1.98062 -3.01392,3.12222 -5.01754,3.47368 -2.31551,0.40617 -4.99997,-0.31162 -6.8187,-1.80117 -2.10698,-1.72563 -2.57377,-4.81157 -3.60233,-7.33332 -1.34702,-3.30253 -3.00032,-6.61374 -3.34502,-10.16372 -0.16708,-1.72065 -0.0561,-3.56523 0.64327,-5.14619 z", + "roots": [ + "m 114.35244,136.83656 c 0.71601,2.82716 0.97674,5.47329 1.28655,8.2339 0.48899,4.3572 0.7504,8.7408 0.90058,13.12278 0.0705,2.05727 -0.049,4.11754 0,6.17543 0.0643,2.70404 -0.18427,5.46122 0.38597,8.10524 0.51542,2.38987 1.23178,4.86516 2.70175,6.8187 0.7169,0.95273 1.68571,1.85348 2.8304,2.18713 0.90951,0.2651 2.20107,0.45082 2.8304,-0.2573 1.05446,-1.18647 -0.1341,-3.17291 -0.12865,-4.76023 0.009,-2.53165 0.094,-5.06424 0.2573,-7.59063 0.43641,-6.7495 0.95117,-13.50638 1.92983,-20.19879 0.54398,-3.71991 0.3556,-3.66457 2.18713,-11.06431 0.39773,-1.60689 -1.32091,1.8542 -2.18713,2.5731 -0.79817,0.66242 -1.72315,1.20003 -2.70175,1.54386 -0.77827,0.27344 -1.61967,0.37063 -2.44444,0.38596 -1.07963,0.0201 -2.2103,0.006 -3.21637,-0.38596 -1.23088,-0.47986 -2.34048,-1.32678 -3.21636,-2.31579 -0.64898,-0.7328 -1.65553,-3.522 -1.41521,-2.57309 z" + ] + }, + "35": { + "viewBox": "91.431724 93.169243 33.984634 82.365998", + "crown": "m 97.823805,106.96313 c 1.11967,-2.53106 3.210235,-4.61602 5.403495,-6.30408 1.09439,-0.842306 2.35205,-1.725676 3.73099,-1.801166 2.1805,-0.11937 4.30005,1.0683 6.17542,2.187136 2.08595,1.24446 4.13999,2.8144 5.4035,4.88887 0.78459,1.28818 1.03803,2.87073 1.1579,4.37426 0.17124,2.14813 -0.2266,4.31847 -0.64328,6.43274 -0.40666,2.06342 -1.4599,4.00504 -1.80117,6.04677 -0.28726,1.71858 -0.79531,3.04869 -1.28654,5.14619 -0.46386,1.98062 -3.01392,3.12222 -5.01754,3.47368 -2.31551,0.40617 -4.99997,-0.31162 -6.8187,-1.80117 -2.10698,-1.72563 -2.57377,-4.81157 -3.60233,-7.33332 -1.347015,-3.30253 -3.000315,-6.61374 -3.345015,-10.16372 -0.16708,-1.72065 -0.0561,-3.56523 0.64327,-5.14619 z", + "roots": [ + "m 101.42613,124.97479 c 0.71601,2.82716 0.97674,5.47329 1.28655,8.2339 0.48899,4.3572 0.7504,8.7408 0.90058,13.12278 0.0705,2.05727 -0.049,4.11754 0,6.17543 0.0643,2.70404 -0.18427,5.46122 0.38597,8.10524 0.51542,2.38987 1.23178,4.86516 2.70175,6.8187 0.7169,0.95273 1.68571,1.85348 2.8304,2.18713 0.90951,0.2651 2.20107,0.45082 2.8304,-0.2573 1.05446,-1.18647 -0.1341,-3.17291 -0.12865,-4.76023 0.009,-2.53165 0.094,-5.06424 0.2573,-7.59063 0.43641,-6.7495 0.95117,-13.50638 1.92983,-20.19879 0.54398,-3.71991 0.3556,-3.66457 2.18713,-11.06431 0.39773,-1.60689 -1.32091,1.8542 -2.18713,2.5731 -0.79817,0.66242 -1.72315,1.20003 -2.70175,1.54386 -0.77827,0.27344 -1.61967,0.37063 -2.44444,0.38596 -1.07963,0.0201 -2.2103,0.006 -3.21637,-0.38596 -1.23088,-0.47986 -2.34048,-1.32678 -3.21636,-2.31579 -0.64898,-0.7328 -1.65553,-3.522 -1.41521,-2.57309 z" + ] + }, + "36": { + "viewBox": "79.343303 98.222382 50.187913 81.374350", + "crown": "m 85.920764,119.68536 c -0.70467,-3.05779 -1.38674,-6.34321 -0.64328,-9.3918 0.27358,-1.12183 1.02287,-2.10606 1.80117,-2.95906 1.11575,-1.22285 2.4241,-2.41725 3.9883,-2.95905 1.41888,-0.49146 3.01428,-0.32546 4.50291,-0.12865 0.88774,0.11737 1.7672,0.38154 2.57309,0.77192 0.94223,0.45642 1.7203,1.19382 2.573086,1.80117 5.98906,4.26535 -3.522546,11.13085 -2.315776,7.46197 1.07805,-3.27756 2.957866,-5.8215 5.403496,-7.84795 1.55237,-1.2863 3.5522,-2.06463 5.53215,-2.44443 1.13794,-0.21828 2.37276,-0.23263 3.47368,0.12866 1.23187,0.40427 2.16141,1.43345 3.21636,2.18713 0.52342,0.37394 0.9336,0.95447 1.54386,1.15789 0.69163,0.23054 1.48069,-0.18014 2.18713,0 1.04137,0.26554 2.15227,0.71013 2.8304,1.54386 0.93309,1.14719 1.17402,2.77113 1.28654,4.24559 0.20768,2.72127 -0.32263,5.50703 -1.15788,8.10526 -0.71031,2.20957 -1.66604,4.44821 -3.21637,6.17542 -1.44599,1.61096 -3.35992,2.88827 -5.40349,3.60233 -2.88466,1.00795 -6.07985,0.6924 -9.1345,0.77193 -2.65934,0.0692 -4.65149,-0.32016 -7.976576,-0.25731 -2.06969,0.0391 -3.95875,-0.87706 -5.53216,-2.05847 -1.49522,-1.1227 -2.42935,-2.87266 -3.34501,-4.50291 -0.95157,-1.69418 -1.75078,-3.51001 -2.18713,-5.4035 z", + "roots": [ + "m 87.850574,124.83154 c 0.36309,3.20871 0.89909,6.93557 1.15789,10.42103 0.42265,5.69205 -0.0967,11.45153 0.64328,17.11108 0.47882,3.66217 0.84786,7.47658 2.44444,10.807 0.92358,1.92657 2.44143,3.5438 3.98829,5.01753 1.2236,1.16575 2.52424,2.3939 4.116956,2.95906 0.68826,0.24422 1.55488,0.49419 2.18712,0.12866 0.74716,-0.43197 1.0672,-1.45752 1.1579,-2.31579 0.27,-2.55493 -1.3381,-4.96189 -1.92982,-7.46198 -1.081,-4.56731 -2.192886,-8.6882 -2.959066,-13.76605 -0.3629,-2.40515 -4e-5,-5.0656 1.1579,-7.20466 0.303496,-0.56065 0.778536,-1.3199 1.415196,-1.28655 2.12325,0.11121 3.10435,2.96632 4.24561,4.76022 1.57779,2.48007 2.52185,5.32421 3.47367,8.10525 1.04751,3.06061 1.5317,6.28834 2.44445,9.3918 0.89157,3.03144 1.44848,6.23045 2.95905,9.00583 0.68518,1.25889 1.34484,2.8834 2.70175,3.34502 1.04382,0.35511 2.41444,-0.0153 3.21637,-0.77193 1.40118,-1.32213 1.5219,-3.61157 1.67251,-5.53215 0.29446,-3.75478 -0.92789,-7.47736 -1.54386,-11.19296 -0.70014,-4.22334 -2.19212,-8.33462 -2.44444,-12.60816 -0.24078,-4.07801 0.47431,-8.15939 0.90059,-12.22219 0.19872,-1.89401 4.64449,-10.69235 0.77192,-5.66082 -0.77769,1.01043 -1.76248,1.87645 -2.8304,2.5731 -1.06125,0.6923 -2.25291,1.2043 -3.47367,1.54385 -1.99862,0.55591 -4.10403,0.65871 -6.17543,0.77193 -2.82651,0.15449 -5.66347,0.001 -8.491206,-0.12865 -1.67568,-0.0771 -3.4477,0.20517 -5.01754,-0.38597 -1.80602,-0.68007 -3.33909,-2.0406 -4.63157,-3.47368 -0.50242,-0.55708 -1.37323,-3.83286 -1.15789,-1.92982 z" + ] + }, + "37": { + "viewBox": "92.485877 107.302543 48.543676 80.673353", + "crown": "m 99.521483,128.39777 c -0.69224,-2.8651 -1.94444,-5.87534 -1.28655,-8.74853 0.32389,-1.41452 1.44017,-2.55484 2.444437,-3.60233 1.14562,-1.19492 2.40234,-2.48471 3.9883,-2.95905 1.44038,-0.4308 3.06234,-0.17285 4.50291,0.25731 1.23344,0.36832 2.23156,1.28387 3.34502,1.92982 1.54601,0.89688 4.41346,0.92778 4.63156,2.70175 0.29235,2.3779 -6.35295,8.68641 -5.14618,5.01753 1.07805,-3.27756 4.14593,-5.25792 6.8187,-7.20467 1.76755,-1.28742 3.73141,-2.83539 5.91812,-2.83041 2.95376,0.007 5.9363,1.72378 7.97659,3.85966 0.90239,0.94467 1.23347,2.33333 1.54385,3.60233 0.40869,1.67096 0.49129,3.4292 0.38596,5.14618 -0.13389,2.18261 -0.70111,4.32585 -1.28653,6.43274 -0.47573,1.71214 -0.73804,3.59363 -1.80118,5.01753 -0.95449,1.27838 -2.36567,2.27058 -3.85963,2.8304 -2.86139,1.07223 -6.07985,0.6924 -9.1345,0.77193 -2.65934,0.0692 -4.65149,-0.32016 -7.97658,-0.25731 -2.06969,0.0391 -3.90504,-0.95221 -5.53216,-2.05847 -1.43137,-0.97318 -2.59796,-2.36664 -3.47366,-3.85964 -1.07724,-1.83657 -1.55844,-3.97714 -2.058477,-6.04677 z", + "roots": [ + "m 101.45129,133.54395 c 0.36309,3.20871 0.89909,6.93557 1.15789,10.42103 0.42265,5.69205 -0.0967,11.45153 0.64328,17.11108 0.47882,3.66217 0.84786,7.47658 2.44444,10.807 0.92358,1.92657 2.44143,3.5438 3.98829,5.01753 1.2236,1.16575 2.52424,2.3939 4.11696,2.95906 0.68826,0.24422 1.55488,0.49419 2.18712,0.12866 0.74716,-0.43197 1.12819,-1.45326 1.1579,-2.31579 0.0959,-2.78587 -2.43109,-5.04467 -3.21637,-7.71929 -1.23943,-4.22143 -2.32154,-7.40165 -2.31579,-12.99412 0.002,-2.43237 0.46292,-5.44104 1.80117,-7.71928 0.3229,-0.54971 0.77854,-1.3199 1.4152,-1.28655 2.12325,0.11121 3.10435,2.96632 4.24561,4.76022 1.57779,2.48007 2.36076,5.38466 3.47367,8.10525 1.0282,2.5135 2.01719,5.04353 2.95907,7.59063 0.94551,2.55693 2.04774,5.07275 2.70174,7.71929 0.48887,1.97831 -0.85882,5.01856 0.90058,6.04677 1.34777,0.78763 3.29086,-0.54866 4.37427,-1.67252 1.3836,-1.43526 1.74586,-3.67574 1.92982,-5.6608 0.30388,-3.27916 -0.86578,-6.5307 -1.41521,-9.77776 -0.71422,-4.22098 -2.01068,-8.34921 -2.44444,-12.60816 -0.37806,-3.71203 -0.4391,-7.47466 -0.12865,-11.19295 0.18128,-2.17116 0.58802,-2.99488 1.15789,-6.43276 0.16146,-0.97399 -1.32056,1.70225 -2.18713,2.3158 -1.03413,0.7322 -2.25291,1.2043 -3.47367,1.54385 -1.99862,0.55591 -4.10403,0.65871 -6.17543,0.77193 -2.82651,0.15449 -5.66347,10e-4 -8.49121,-0.12865 -1.67568,-0.0771 -3.4477,0.20517 -5.01754,-0.38597 -1.80602,-0.68007 -3.33909,-2.0406 -4.63157,-3.47368 -0.50242,-0.55708 -1.37323,-3.83286 -1.15789,-1.92982 z" + ] + }, + "38": { + "viewBox": "50.508239 85.168655 40.738012 73.286789", + "crown": "m 56.98151,106.29271 c 0,0 -1.93361,-5.44732 -1.28653,-8.105263 0.71822,-2.95019 2.6609,-6.03036 5.40349,-7.33331 1.59005,-0.7554 3.32936,-1.01362 5.27484,0.25731 1.28701,0.84077 3.03687,2.50997 4.88888,2.70174 1.16039,0.12016 2.23576,-0.66803 3.34502,-1.02924 1.37982,-0.4493 2.66638,-1.45566 4.11695,-1.41519 1.24981,0.0349 2.39542,0.78226 3.47367,1.4152 0.94726,0.55603 1.97738,1.13566 2.57311,2.05847 1.03236,1.59916 1.36173,3.62947 1.41521,5.532163 0.0749,2.66506 -0.57629,5.36359 -1.54387,7.84794 -0.65663,1.68596 -1.5122,3.39223 -2.8304,4.63157 -1.202,1.13009 -2.78269,1.88127 -4.37426,2.31578 -2.69197,0.73492 -5.57767,0.56267 -8.36256,0.38596 -2.73712,-0.17368 -5.77615,0.033 -8.10524,-1.4152 -1.17278,-0.72922 -1.8149,-2.11586 -2.44445,-3.34502 -1.00772,-1.96749 -2.05846,-6.30409 -2.05846,-6.30409", + "roots": [ + "m 58.78268,111.56754 c 0.80601,3.78491 1.53446,6.86372 2.31579,10.29238 1.14413,5.02069 1.61552,10.25015 3.47367,15.05261 2.02314,5.22887 3.88724,11.12536 8.23391,14.66663 1.53516,1.25071 3.68665,1.95506 5.66081,1.80117 1.14724,-0.0894 2.45722,-0.58123 3.08771,-1.54385 1.34119,-2.0477 0.68247,-4.90353 0.38596,-7.33333 -0.24978,-2.04686 -1.41028,-3.89346 -1.80117,-5.91812 -0.47326,-2.45128 -0.60927,-4.96565 -0.64327,-7.46197 -0.0398,-2.92094 0.0844,-5.85916 0.51462,-8.74852 0.37681,-2.53086 1.02382,-5.02415 1.80117,-7.46197 0.42096,-1.32016 0.81639,-1.43989 1.54385,-3.85964 0.65064,-2.16423 -3.51498,3.02198 -5.6608,3.73098 -1.46817,0.4851 -3.08593,0.21441 -4.63157,0.25731 -1.84334,0.0512 -5.01754,0.12866 -5.53216,0 -0.51461,-0.12865 -3.61823,-0.45349 -5.27484,-1.15789 -1.28065,-0.54454 -3.7376,-3.5551 -3.47368,-2.31579 z" + ] + }, + "41": { + "viewBox": "96.120373 98.955384 26.355569 77.981387", + "groupTransform": "translate(218.596316 0) scale(-1 1)", + "crown": "m 101.53441,106.02036 c -0.004,-0.90793 7.9e-4,-1.91402 1.4152,-1.54385 1.66002,0.43445 3.43039,0.10721 5.14619,0.12865 1.97255,0.0247 4.04665,-0.62383 5.91812,0 0.61026,0.20342 1.13168,0.66402 1.54385,1.15789 0.61444,0.73623 1.113,1.63 1.28655,2.5731 0.85803,4.66257 -0.6772,9.46894 -1.4152,14.15201 -0.50543,3.20724 -0.0832,6.94363 -2.05848,9.52045 -0.96851,1.26344 -2.65465,2.13078 -4.2456,2.18713 -1.63199,0.0578 -3.4372,-0.6925 -4.50292,-1.92982 -1.21511,-1.41076 -0.95265,-2.79984 -1.4152,-5.4035 -0.93526,-5.26444 -1.39338,-10.6136 -1.67251,-15.95318 -0.0851,-1.6274 0.008,-3.25927 0,-4.88888 z", + "roots": [ + "m 103.46423,130.72207 c 0.15401,3.18195 0.66236,7.37849 1.02924,11.0643 0.56384,5.66456 0.5816,11.42204 1.80116,16.98242 0.65952,3.00699 1.69054,5.94356 2.95906,8.74852 0.53043,1.1729 0.88055,2.59932 1.92982,3.34503 0.74728,0.53109 1.89545,0.95091 2.70175,0.51462 0.69343,-0.37521 0.81524,-1.40333 0.90059,-2.18713 0.26866,-2.46719 -1.08752,-4.85954 -1.28656,-7.33332 -0.78768,-9.78985 0.0492,-19.64622 0.38597,-29.46193 0.0265,-0.77267 0.86562,-2.08212 0.12866,-2.31579 -0.77131,-0.24456 -0.72287,1.47797 -1.28655,2.05848 -0.6463,0.66559 -1.42266,1.29282 -2.31579,1.54386 -1.03295,0.29034 -2.18083,0.15233 -3.21636,-0.12866 -0.7036,-0.19092 -1.34405,-0.59522 -1.92982,-1.02924 -0.68222,-0.50548 -1.88499,-3.5329 -1.80117,-1.80116 z" + ] + }, + "42": { + "viewBox": "90.716566 83.433116 30.144529 84.042377", + "groupTransform": "translate(211.577661 0) scale(-1 1)", + "crown": "m 96.586049,90.087282 c 0.099,-1.32852 3.15696,-0.65578 4.760231,-0.77193 2.65226,-0.19214 5.32184,-0.0252 7.97659,0.12866 1.2908,0.0748 2.73919,-0.25928 3.85964,0.38596 0.82516,0.47518 1.39149,1.40599 1.67251,2.31578 0.48111,1.55758 0.003,3.26402 -0.12865,4.88888 -0.2309,2.84725 -0.62663,5.684448 -1.1579,8.491208 -0.30371,1.60452 -0.62823,2.51235 -1.1579,4.76023 -0.49901,2.11779 -1.72922,5.17181 -2.70174,6.81869 -0.95492,1.61708 -1.57917,1.9001 -2.70174,2.18714 -1.39357,0.35634 -3.01996,-0.0191 -4.24561,-0.77193 -1.33015,-0.81703 -2.12986,-2.33597 -2.830401,-3.73099 -0.76134,-1.5161 -1.06626,-3.22862 -1.41521,-4.88888 -0.80089,-3.81054 -1.19506,-7.70078 -1.54385,-11.578928 -0.24612,-2.7366 -0.61873,-5.10979 -0.38597,-8.23389 z", + "roots": [ + "m 98.644529,110.67203 c 0.30392,5.31017 0.51318,10.2125 0.90058,15.30992 0.38515,5.06775 0.781401,10.13857 1.415201,15.18126 0.54688,4.35115 0.7191,8.81828 2.05848,12.99412 0.70291,2.19148 1.35687,4.65858 3.08771,6.17543 0.98936,0.86705 2.44386,1.55841 3.73099,1.28654 0.80163,-0.16932 1.52773,-0.90017 1.80116,-1.67251 0.40481,-1.14342 -0.3716,-2.39782 -0.51461,-3.60233 -0.48248,-4.06377 -0.97672,-8.1339 -1.15789,-12.2222 -0.32277,-7.28341 -0.27489,-14.59192 0.12865,-21.87131 0.13605,-2.45417 0.34416,-4.91289 0.77193,-7.33332 0.18464,-1.04472 0.4727,-2.01275 0.77192,-3.08771 0.79704,-2.86339 -0.63975,3.11989 -1.54385,4.37426 -0.85771,1.19 -2.04603,2.36473 -3.47368,2.70176 -1.27693,0.30144 -2.76313,-0.0515 -3.85964,-0.77194 -1.13338,-0.7447 -1.65496,-2.16078 -2.31579,-3.34502 -0.729911,-1.30803 -1.886751,-5.61241 -1.801161,-4.11695 z" + ] + }, + "43": { + "viewBox": "88.435127 109.232700 36.367350 100.346404", + "groupTransform": "translate(213.237604 0) scale(-1 1)", + "crown": "m 97.481714,139.06536 c -1.02952,-3.60735 -1.86235,-7.36964 -2.05848,-11.0643 -0.13281,-2.5018 -0.25389,-4.89725 1.54387,-6.69005 1.37473,-1.37094 2.62145,-1.85901 3.988286,-2.70174 1.42179,-0.87661 2.70826,-2.32441 4.37425,-2.44444 2.15277,-0.1551 4.16929,1.2511 6.04678,2.31578 1.99255,1.12993 4.18335,2.30699 5.40349,4.24561 0.84242,1.33848 0.89162,3.05605 1.02924,4.63157 0.16809,1.9244 0.03,3.87922 -0.25731,5.78946 -0.50738,3.37377 -1.41144,6.69856 -2.57309,9.90641 -0.85567,2.36291 -1.36536,5.11891 -3.21637,6.8187 -1.17934,1.083 -2.90498,1.77402 -4.50292,1.67251 -2.02921,-0.12891 -3.96258,-1.3958 -5.40349,-2.8304 -1.16915,-1.16403 -1.6288,-2.87428 -2.315786,-4.37426 -0.78593,-1.71601 -1.54049,-3.45989 -2.05847,-5.27485 z", + "roots": [ + "m 98.768264,141.89577 c 0.33837,4.62951 -0.008,8.41296 0.25731,12.60816 0.51203,8.09167 1.536686,16.14575 2.573086,24.18709 0.5489,4.25883 0.76347,8.60419 1.92982,12.73681 0.89783,3.18121 1.61363,6.70928 3.85964,9.13449 1.01655,1.09765 2.517,2.32957 3.9883,2.05847 1.07525,-0.19812 1.91388,-1.38577 2.18713,-2.44443 0.38479,-1.49082 -0.82342,-2.97706 -1.02924,-4.50292 -0.92386,-6.84896 -1.05905,-13.80423 -0.90058,-20.71341 0.13229,-5.76806 1.1424,-11.48414 1.54385,-17.23973 0.23901,-3.42675 -0.0569,-6.0074 0.51462,-10.29238 0.079,-0.59208 -0.24708,-0.81225 0.12865,-2.31578 1.10138,-4.40734 -0.33356,2.48653 -1.02923,3.47368 -0.79552,1.12885 -2.01307,2.07967 -3.34503,2.44443 -1.28821,0.35278 -2.76085,0.14065 -3.98829,-0.38596 -1.56457,-0.67125 -2.80294,-2.01017 -3.85964,-3.34502 -1.26203,-1.59424 -2.984186,-7.50755 -2.830396,-5.4035 z" + ] + }, + "44": { + "viewBox": "104.357943 105.031043 33.984660 82.365998", + "groupTransform": "translate(242.700545 0) scale(-1 1)", + "crown": "m 110.75011,118.8249 c 1.11967,-2.53106 3.21024,-4.61602 5.4035,-6.30408 1.09439,-0.84231 2.35205,-1.72568 3.73099,-1.80117 2.1805,-0.11937 4.30005,1.0683 6.17542,2.18714 2.08595,1.24446 4.13999,2.8144 5.4035,4.88887 0.78459,1.28818 1.03803,2.87073 1.1579,4.37426 0.17124,2.14813 -0.2266,4.31847 -0.64328,6.43274 -0.40666,2.06342 -1.4599,4.00504 -1.80117,6.04677 -0.28726,1.71858 -0.79531,3.04869 -1.28654,5.14619 -0.46386,1.98062 -3.01392,3.12222 -5.01754,3.47368 -2.31551,0.40617 -4.99997,-0.31162 -6.8187,-1.80117 -2.10698,-1.72563 -2.57377,-4.81157 -3.60233,-7.33332 -1.34702,-3.30253 -3.00032,-6.61374 -3.34502,-10.16372 -0.16708,-1.72065 -0.0561,-3.56523 0.64327,-5.14619 z", + "roots": [ + "m 114.35244,136.83656 c 0.71601,2.82716 0.97674,5.47329 1.28655,8.2339 0.48899,4.3572 0.7504,8.7408 0.90058,13.12278 0.0705,2.05727 -0.049,4.11754 0,6.17543 0.0643,2.70404 -0.18427,5.46122 0.38597,8.10524 0.51542,2.38987 1.23178,4.86516 2.70175,6.8187 0.7169,0.95273 1.68571,1.85348 2.8304,2.18713 0.90951,0.2651 2.20107,0.45082 2.8304,-0.2573 1.05446,-1.18647 -0.1341,-3.17291 -0.12865,-4.76023 0.009,-2.53165 0.094,-5.06424 0.2573,-7.59063 0.43641,-6.7495 0.95117,-13.50638 1.92983,-20.19879 0.54398,-3.71991 0.3556,-3.66457 2.18713,-11.06431 0.39773,-1.60689 -1.32091,1.8542 -2.18713,2.5731 -0.79817,0.66242 -1.72315,1.20003 -2.70175,1.54386 -0.77827,0.27344 -1.61967,0.37063 -2.44444,0.38596 -1.07963,0.0201 -2.2103,0.006 -3.21637,-0.38596 -1.23088,-0.47986 -2.34048,-1.32678 -3.21636,-2.31579 -0.64898,-0.7328 -1.65553,-3.522 -1.41521,-2.57309 z" + ] + }, + "45": { + "viewBox": "91.431724 93.169243 33.984634 82.365998", + "groupTransform": "translate(216.848081 0) scale(-1 1)", + "crown": "m 97.823805,106.96313 c 1.11967,-2.53106 3.210235,-4.61602 5.403495,-6.30408 1.09439,-0.842306 2.35205,-1.725676 3.73099,-1.801166 2.1805,-0.11937 4.30005,1.0683 6.17542,2.187136 2.08595,1.24446 4.13999,2.8144 5.4035,4.88887 0.78459,1.28818 1.03803,2.87073 1.1579,4.37426 0.17124,2.14813 -0.2266,4.31847 -0.64328,6.43274 -0.40666,2.06342 -1.4599,4.00504 -1.80117,6.04677 -0.28726,1.71858 -0.79531,3.04869 -1.28654,5.14619 -0.46386,1.98062 -3.01392,3.12222 -5.01754,3.47368 -2.31551,0.40617 -4.99997,-0.31162 -6.8187,-1.80117 -2.10698,-1.72563 -2.57377,-4.81157 -3.60233,-7.33332 -1.347015,-3.30253 -3.000315,-6.61374 -3.345015,-10.16372 -0.16708,-1.72065 -0.0561,-3.56523 0.64327,-5.14619 z", + "roots": [ + "m 101.42613,124.97479 c 0.71601,2.82716 0.97674,5.47329 1.28655,8.2339 0.48899,4.3572 0.7504,8.7408 0.90058,13.12278 0.0705,2.05727 -0.049,4.11754 0,6.17543 0.0643,2.70404 -0.18427,5.46122 0.38597,8.10524 0.51542,2.38987 1.23178,4.86516 2.70175,6.8187 0.7169,0.95273 1.68571,1.85348 2.8304,2.18713 0.90951,0.2651 2.20107,0.45082 2.8304,-0.2573 1.05446,-1.18647 -0.1341,-3.17291 -0.12865,-4.76023 0.009,-2.53165 0.094,-5.06424 0.2573,-7.59063 0.43641,-6.7495 0.95117,-13.50638 1.92983,-20.19879 0.54398,-3.71991 0.3556,-3.66457 2.18713,-11.06431 0.39773,-1.60689 -1.32091,1.8542 -2.18713,2.5731 -0.79817,0.66242 -1.72315,1.20003 -2.70175,1.54386 -0.77827,0.27344 -1.61967,0.37063 -2.44444,0.38596 -1.07963,0.0201 -2.2103,0.006 -3.21637,-0.38596 -1.23088,-0.47986 -2.34048,-1.32678 -3.21636,-2.31579 -0.64898,-0.7328 -1.65553,-3.522 -1.41521,-2.57309 z" + ] + }, + "46": { + "viewBox": "79.343303 98.222382 50.187913 81.374350", + "groupTransform": "translate(208.874519 0) scale(-1 1)", + "crown": "m 85.920764,119.68536 c -0.70467,-3.05779 -1.38674,-6.34321 -0.64328,-9.3918 0.27358,-1.12183 1.02287,-2.10606 1.80117,-2.95906 1.11575,-1.22285 2.4241,-2.41725 3.9883,-2.95905 1.41888,-0.49146 3.01428,-0.32546 4.50291,-0.12865 0.88774,0.11737 1.7672,0.38154 2.57309,0.77192 0.94223,0.45642 1.7203,1.19382 2.573086,1.80117 5.98906,4.26535 -3.522546,11.13085 -2.315776,7.46197 1.07805,-3.27756 2.957866,-5.8215 5.403496,-7.84795 1.55237,-1.2863 3.5522,-2.06463 5.53215,-2.44443 1.13794,-0.21828 2.37276,-0.23263 3.47368,0.12866 1.23187,0.40427 2.16141,1.43345 3.21636,2.18713 0.52342,0.37394 0.9336,0.95447 1.54386,1.15789 0.69163,0.23054 1.48069,-0.18014 2.18713,0 1.04137,0.26554 2.15227,0.71013 2.8304,1.54386 0.93309,1.14719 1.17402,2.77113 1.28654,4.24559 0.20768,2.72127 -0.32263,5.50703 -1.15788,8.10526 -0.71031,2.20957 -1.66604,4.44821 -3.21637,6.17542 -1.44599,1.61096 -3.35992,2.88827 -5.40349,3.60233 -2.88466,1.00795 -6.07985,0.6924 -9.1345,0.77193 -2.65934,0.0692 -4.65149,-0.32016 -7.976576,-0.25731 -2.06969,0.0391 -3.95875,-0.87706 -5.53216,-2.05847 -1.49522,-1.1227 -2.42935,-2.87266 -3.34501,-4.50291 -0.95157,-1.69418 -1.75078,-3.51001 -2.18713,-5.4035 z", + "roots": [ + "m 87.850574,124.83154 c 0.36309,3.20871 0.89909,6.93557 1.15789,10.42103 0.42265,5.69205 -0.0967,11.45153 0.64328,17.11108 0.47882,3.66217 0.84786,7.47658 2.44444,10.807 0.92358,1.92657 2.44143,3.5438 3.98829,5.01753 1.2236,1.16575 2.52424,2.3939 4.116956,2.95906 0.68826,0.24422 1.55488,0.49419 2.18712,0.12866 0.74716,-0.43197 1.0672,-1.45752 1.1579,-2.31579 0.27,-2.55493 -1.3381,-4.96189 -1.92982,-7.46198 -1.081,-4.56731 -2.192886,-8.6882 -2.959066,-13.76605 -0.3629,-2.40515 -4e-5,-5.0656 1.1579,-7.20466 0.303496,-0.56065 0.778536,-1.3199 1.415196,-1.28655 2.12325,0.11121 3.10435,2.96632 4.24561,4.76022 1.57779,2.48007 2.52185,5.32421 3.47367,8.10525 1.04751,3.06061 1.5317,6.28834 2.44445,9.3918 0.89157,3.03144 1.44848,6.23045 2.95905,9.00583 0.68518,1.25889 1.34484,2.8834 2.70175,3.34502 1.04382,0.35511 2.41444,-0.0153 3.21637,-0.77193 1.40118,-1.32213 1.5219,-3.61157 1.67251,-5.53215 0.29446,-3.75478 -0.92789,-7.47736 -1.54386,-11.19296 -0.70014,-4.22334 -2.19212,-8.33462 -2.44444,-12.60816 -0.24078,-4.07801 0.47431,-8.15939 0.90059,-12.22219 0.19872,-1.89401 4.64449,-10.69235 0.77192,-5.66082 -0.77769,1.01043 -1.76248,1.87645 -2.8304,2.5731 -1.06125,0.6923 -2.25291,1.2043 -3.47367,1.54385 -1.99862,0.55591 -4.10403,0.65871 -6.17543,0.77193 -2.82651,0.15449 -5.66347,0.001 -8.491206,-0.12865 -1.67568,-0.0771 -3.4477,0.20517 -5.01754,-0.38597 -1.80602,-0.68007 -3.33909,-2.0406 -4.63157,-3.47368 -0.50242,-0.55708 -1.37323,-3.83286 -1.15789,-1.92982 z" + ] + }, + "47": { + "viewBox": "92.485877 107.302543 48.543676 80.673353", + "groupTransform": "translate(233.515429 0) scale(-1 1)", + "crown": "m 99.521483,128.39777 c -0.69224,-2.8651 -1.94444,-5.87534 -1.28655,-8.74853 0.32389,-1.41452 1.44017,-2.55484 2.444437,-3.60233 1.14562,-1.19492 2.40234,-2.48471 3.9883,-2.95905 1.44038,-0.4308 3.06234,-0.17285 4.50291,0.25731 1.23344,0.36832 2.23156,1.28387 3.34502,1.92982 1.54601,0.89688 4.41346,0.92778 4.63156,2.70175 0.29235,2.3779 -6.35295,8.68641 -5.14618,5.01753 1.07805,-3.27756 4.14593,-5.25792 6.8187,-7.20467 1.76755,-1.28742 3.73141,-2.83539 5.91812,-2.83041 2.95376,0.007 5.9363,1.72378 7.97659,3.85966 0.90239,0.94467 1.23347,2.33333 1.54385,3.60233 0.40869,1.67096 0.49129,3.4292 0.38596,5.14618 -0.13389,2.18261 -0.70111,4.32585 -1.28653,6.43274 -0.47573,1.71214 -0.73804,3.59363 -1.80118,5.01753 -0.95449,1.27838 -2.36567,2.27058 -3.85963,2.8304 -2.86139,1.07223 -6.07985,0.6924 -9.1345,0.77193 -2.65934,0.0692 -4.65149,-0.32016 -7.97658,-0.25731 -2.06969,0.0391 -3.90504,-0.95221 -5.53216,-2.05847 -1.43137,-0.97318 -2.59796,-2.36664 -3.47366,-3.85964 -1.07724,-1.83657 -1.55844,-3.97714 -2.058477,-6.04677 z", + "roots": [ + "m 101.45129,133.54395 c 0.36309,3.20871 0.89909,6.93557 1.15789,10.42103 0.42265,5.69205 -0.0967,11.45153 0.64328,17.11108 0.47882,3.66217 0.84786,7.47658 2.44444,10.807 0.92358,1.92657 2.44143,3.5438 3.98829,5.01753 1.2236,1.16575 2.52424,2.3939 4.11696,2.95906 0.68826,0.24422 1.55488,0.49419 2.18712,0.12866 0.74716,-0.43197 1.12819,-1.45326 1.1579,-2.31579 0.0959,-2.78587 -2.43109,-5.04467 -3.21637,-7.71929 -1.23943,-4.22143 -2.32154,-7.40165 -2.31579,-12.99412 0.002,-2.43237 0.46292,-5.44104 1.80117,-7.71928 0.3229,-0.54971 0.77854,-1.3199 1.4152,-1.28655 2.12325,0.11121 3.10435,2.96632 4.24561,4.76022 1.57779,2.48007 2.36076,5.38466 3.47367,8.10525 1.0282,2.5135 2.01719,5.04353 2.95907,7.59063 0.94551,2.55693 2.04774,5.07275 2.70174,7.71929 0.48887,1.97831 -0.85882,5.01856 0.90058,6.04677 1.34777,0.78763 3.29086,-0.54866 4.37427,-1.67252 1.3836,-1.43526 1.74586,-3.67574 1.92982,-5.6608 0.30388,-3.27916 -0.86578,-6.5307 -1.41521,-9.77776 -0.71422,-4.22098 -2.01068,-8.34921 -2.44444,-12.60816 -0.37806,-3.71203 -0.4391,-7.47466 -0.12865,-11.19295 0.18128,-2.17116 0.58802,-2.99488 1.15789,-6.43276 0.16146,-0.97399 -1.32056,1.70225 -2.18713,2.3158 -1.03413,0.7322 -2.25291,1.2043 -3.47367,1.54385 -1.99862,0.55591 -4.10403,0.65871 -6.17543,0.77193 -2.82651,0.15449 -5.66347,10e-4 -8.49121,-0.12865 -1.67568,-0.0771 -3.4477,0.20517 -5.01754,-0.38597 -1.80602,-0.68007 -3.33909,-2.0406 -4.63157,-3.47368 -0.50242,-0.55708 -1.37323,-3.83286 -1.15789,-1.92982 z" + ] + }, + "48": { + "viewBox": "50.508239 85.168655 40.738012 73.286789", + "groupTransform": "translate(141.754490 0) scale(-1 1)", + "crown": "m 56.98151,106.29271 c 0,0 -1.93361,-5.44732 -1.28653,-8.105263 0.71822,-2.95019 2.6609,-6.03036 5.40349,-7.33331 1.59005,-0.7554 3.32936,-1.01362 5.27484,0.25731 1.28701,0.84077 3.03687,2.50997 4.88888,2.70174 1.16039,0.12016 2.23576,-0.66803 3.34502,-1.02924 1.37982,-0.4493 2.66638,-1.45566 4.11695,-1.41519 1.24981,0.0349 2.39542,0.78226 3.47367,1.4152 0.94726,0.55603 1.97738,1.13566 2.57311,2.05847 1.03236,1.59916 1.36173,3.62947 1.41521,5.532163 0.0749,2.66506 -0.57629,5.36359 -1.54387,7.84794 -0.65663,1.68596 -1.5122,3.39223 -2.8304,4.63157 -1.202,1.13009 -2.78269,1.88127 -4.37426,2.31578 -2.69197,0.73492 -5.57767,0.56267 -8.36256,0.38596 -2.73712,-0.17368 -5.77615,0.033 -8.10524,-1.4152 -1.17278,-0.72922 -1.8149,-2.11586 -2.44445,-3.34502 -1.00772,-1.96749 -2.05846,-6.30409 -2.05846,-6.30409", + "roots": [ + "m 58.78268,111.56754 c 0.80601,3.78491 1.53446,6.86372 2.31579,10.29238 1.14413,5.02069 1.61552,10.25015 3.47367,15.05261 2.02314,5.22887 3.88724,11.12536 8.23391,14.66663 1.53516,1.25071 3.68665,1.95506 5.66081,1.80117 1.14724,-0.0894 2.45722,-0.58123 3.08771,-1.54385 1.34119,-2.0477 0.68247,-4.90353 0.38596,-7.33333 -0.24978,-2.04686 -1.41028,-3.89346 -1.80117,-5.91812 -0.47326,-2.45128 -0.60927,-4.96565 -0.64327,-7.46197 -0.0398,-2.92094 0.0844,-5.85916 0.51462,-8.74852 0.37681,-2.53086 1.02382,-5.02415 1.80117,-7.46197 0.42096,-1.32016 0.81639,-1.43989 1.54385,-3.85964 0.65064,-2.16423 -3.51498,3.02198 -5.6608,3.73098 -1.46817,0.4851 -3.08593,0.21441 -4.63157,0.25731 -1.84334,0.0512 -5.01754,0.12866 -5.53216,0 -0.51461,-0.12865 -3.61823,-0.45349 -5.27484,-1.15789 -1.28065,-0.54454 -3.7376,-3.5551 -3.47368,-2.31579 z" + ] + } +}; + +export function getRealisticToothAsset(fdi: FdiToothId): RealisticToothAsset | null { + return REALISTIC_TOOTH_ASSETS[fdi] ?? null; +} diff --git a/frontend/src/components/treatment/toothSelectionGroups.ts b/frontend/src/components/treatment/toothSelectionGroups.ts index 42483c1..1fbcccd 100644 --- a/frontend/src/components/treatment/toothSelectionGroups.ts +++ b/frontend/src/components/treatment/toothSelectionGroups.ts @@ -23,7 +23,7 @@ export function deriveTeethFromGroups(groups: ToothSelectionGroup[]): FdiToothId return [...set].sort() as FdiToothId[]; } -/** Never allow a 1-tooth connected group (no lone connected dots). */ +/** Never allow a 1-tooth connected group (no lone connection marks). */ function normalizeGroupKind(kind: ToothSelectionKind, teeth: FdiToothId[]): ToothSelectionKind { if (kind === 'connected' && teeth.length < 2) return 'single'; return kind; @@ -34,7 +34,7 @@ function makeGroup(kind: ToothSelectionKind, teeth: FdiToothId[]): ToothSelectio return { groupId: newGroupId(), kind: normalizeGroupKind(kind, teeth), - teeth, + teeth: sortInArchOrder(teeth), }; } @@ -61,6 +61,7 @@ export function groupsFromFlatTeeth( .map((g) => ({ ...g, kind: normalizeGroupKind(g.kind, g.teeth), + teeth: sortInArchOrder(g.teeth), })); const covered = new Set(next.flatMap((g) => g.teeth)); for (const tooth of teeth) { @@ -86,6 +87,40 @@ export function connectedTeethSet(groups: ToothSelectionGroup[]): Set { + const edges = new Set(); + for (const group of groups) { + if (group.kind !== 'connected' || group.teeth.length < 2) continue; + const ordered = sortInArchOrder(group.teeth); + for (let i = 0; i < ordered.length - 1; i++) { + const a = ordered[i]; + const b = ordered[i + 1]; + if (areArchNeighbors(a, b)) { + edges.add(toothEdgeKey(a, b)); + } + } + } + return edges; +} + +export function areArchNeighbors(a: FdiToothId, b: FdiToothId): boolean { + const arch = archOrder(a); + if (!arch || !sameArch(a, b)) return false; + return Math.abs(arch.indexOf(a) - arch.indexOf(b)) === 1; +} + function archOrder(tooth: FdiToothId): FdiToothId[] | null { if ((FDI_UPPER_LEFT_TO_RIGHT as readonly string[]).includes(tooth)) { return FDI_UPPER_LEFT_TO_RIGHT; @@ -115,11 +150,7 @@ export function teethBetweenInclusive(a: FdiToothId, b: FdiToothId): FdiToothId[ /** * Plain click: * - unselected → add as single - * - selected single → remove (deselect) - * - in connected bridge → cut from bridge, keep selected as single: - * - end tooth → remainder stays one bridge (or demotes if 1 left) - * - middle tooth → left and right become separate bridges/singles - * Never leaves a 1-tooth connected group (no lone dots). + * - selected → deselect (remove). If it was in a bridge, split/shrink remaining sides. */ export function toggleToothInGroups( groups: ToothSelectionGroup[], @@ -146,14 +177,69 @@ export function toggleToothInGroups( const rightGroup = makeGroup(right.length >= 2 ? 'connected' : 'single', right); if (leftGroup) next.push(leftGroup); if (rightGroup) next.push(rightGroup); - next.push({ groupId: newGroupId(), kind: 'single', teeth: [tooth] }); + return next; +} + +/** Connect two adjacent selected teeth (merge into one connected bridge). */ +export function linkAdjacentTeeth( + groups: ToothSelectionGroup[], + a: FdiToothId, + b: FdiToothId, +): ToothSelectionGroup[] | null { + if (!areArchNeighbors(a, b)) return null; + const groupA = groups.find((g) => g.teeth.includes(a)); + const groupB = groups.find((g) => g.teeth.includes(b)); + if (!groupA || !groupB) return null; + + if (groupA.groupId === groupB.groupId) { + if (groupA.kind === 'connected') return groups; + return groups.map((g) => + g.groupId === groupA.groupId + ? { ...g, kind: 'connected' as const, teeth: sortInArchOrder(g.teeth) } + : g, + ); + } + + const mergedTeeth = sortInArchOrder([...groupA.teeth, ...groupB.teeth]); + const next = groups.filter((g) => g.groupId !== groupA.groupId && g.groupId !== groupB.groupId); + const merged = makeGroup('connected', mergedTeeth); + if (merged) next.push(merged); + return next; +} + +/** Disconnect one edge inside a bridge; teeth stay selected. */ +export function unlinkAdjacentTeeth( + groups: ToothSelectionGroup[], + a: FdiToothId, + b: FdiToothId, +): ToothSelectionGroup[] | null { + if (!areArchNeighbors(a, b)) return null; + const owning = groups.find( + (g) => g.kind === 'connected' && g.teeth.includes(a) && g.teeth.includes(b), + ); + if (!owning) return null; + + const ordered = sortInArchOrder(owning.teeth); + const i = ordered.indexOf(a); + const j = ordered.indexOf(b); + if (i < 0 || j < 0 || Math.abs(i - j) !== 1) return null; + + const [leftIdx, rightIdx] = i < j ? [i, j] : [j, i]; + const left = ordered.slice(0, rightIdx); + const right = ordered.slice(rightIdx); + const next = groups.filter((g) => g.groupId !== owning.groupId); + const leftGroup = makeGroup(left.length >= 2 ? 'connected' : 'single', left); + const rightGroup = makeGroup(right.length >= 2 ? 'connected' : 'single', right); + if (leftGroup) next.push(leftGroup); + if (rightGroup) next.push(rightGroup); return next; } /** - * Shift-click creates a connected span between anchor and target on the same arch. - * Overlapping existing bridges are merged (union) into one connected bridge. - * Non-overlapping bridges are left alone. Singles inside the union are absorbed. + * Shift-click selects an inclusive same-arch span as singles (empty circles between + * neighbors — not connected). Overlapping existing bridges are absorbed into that + * selection and become singles too (one selected range, not connected). + * Midline pairs (11–21, 41–31) are allowed — they are neighbors in arch order. */ export function applyShiftRange( groups: ToothSelectionGroup[], @@ -194,11 +280,9 @@ export function applyShiftRange( kind: normalizeGroupKind(g.kind, g.teeth), })); - next.push({ - groupId: newGroupId(), - kind: 'connected', - teeth: unionTeeth, - }); + for (const tooth of unionTeeth) { + next.push({ groupId: newGroupId(), kind: 'single', teeth: [tooth] }); + } return next; } @@ -238,7 +322,7 @@ export function normalizeToothSelectionGroups(value: unknown): ToothSelectionGro out.push({ groupId, kind: normalizeGroupKind(kind, teeth), - teeth, + teeth: sortInArchOrder(teeth), }); } return out; diff --git a/frontend/src/components/ui/lab/CaseSheetFdiChart.tsx b/frontend/src/components/ui/lab/CaseSheetFdiChart.tsx index 2fae32b..fa52558 100644 --- a/frontend/src/components/ui/lab/CaseSheetFdiChart.tsx +++ b/frontend/src/components/ui/lab/CaseSheetFdiChart.tsx @@ -127,9 +127,9 @@ function ArchRow({ {linkToNext ? ( {fdi} - {isConnected && isSelected ? ( - + {linkToNext ? ( + ) : null} ); diff --git a/frontend/src/components/ui/treatment/FdiToothChart.tsx b/frontend/src/components/ui/treatment/FdiToothChart.tsx index e621fd6..473cf9d 100644 --- a/frontend/src/components/ui/treatment/FdiToothChart.tsx +++ b/frontend/src/components/ui/treatment/FdiToothChart.tsx @@ -3,27 +3,63 @@ import { useId, type CSSProperties, type ReactNode } from 'react'; import { useTranslations } from 'next-intl'; import { FDI_LOWER_LEFT_TO_RIGHT, FDI_UPPER_LEFT_TO_RIGHT, getToothShapeKind } from '@/components/treatment/fdiToothMeta'; +import { + getToothColumnWidthRem, + getToothGlyphWidthRem, +} from '@/components/treatment/fdiToothScale'; +import { getRealisticToothAsset } from '@/components/treatment/realisticToothAssets'; +import { toothEdgeKey } from '@/components/treatment/toothSelectionGroups'; import { ToothGlyph } from '@/components/ui/treatment/ToothGlyph'; import type { FdiToothId } from '@/types/treatment'; const TOOTH_NUMBER_GAP = 'mt-1'; - -/** Six teeth each side of the midline (anteriors): slightly larger glyph + wrapper. */ -const MIDLINE_SIX = new Set([ - '13', '12', '11', '21', '22', '23', - '43', '42', '41', '31', '32', '33', -]); +/** Extra crown↔number space for realistic SVGs (outside arch transform so it is not cancelled). */ +const REALISTIC_NUMBER_GAP = '2mm'; +/** Tight interproximal gap between tooth columns. */ +const TOOTH_GAP = 'gap-x-px'; +/** Fixed row cell height — must fit tallest scaled canine/root. */ +const TOOTH_CELL_H = 'h-[7.25rem]'; function quadrantMirrored(fdi: FdiToothId): boolean { const q = fdi[0]; return q === '2' || q === '3'; } +/** Cartoon glyph height factor vs width (realistic assets use aspect ratio from viewBox). */ +function cartoonHeightRem(fdi: FdiToothId, widthRem: number): number { + const kind = getToothShapeKind(fdi); + const factor = kind === 'canine' ? 2.85 : kind === 'molar' ? 2.45 : 2.65; + return Number((widthRem * factor).toFixed(3)); +} + +function toothSize(fdi: FdiToothId): { + columnWidthRem: number; + glyphWidthRem: number; + glyphHeightRem: number | 'auto'; +} { + const glyphWidthRem = getToothGlyphWidthRem(fdi); + const columnWidthRem = getToothColumnWidthRem(fdi); + const realistic = Boolean(getRealisticToothAsset(fdi)); + return { + columnWidthRem, + glyphWidthRem, + glyphHeightRem: realistic ? 'auto' : cartoonHeightRem(fdi, glyphWidthRem), + }; +} + interface FdiToothChartProps { selected: ReadonlySet; - /** Teeth that belong to a connected selection span (dots above/below). */ + /** + * Legacy / read-only: teeth in a connected bridge. + * When `linkedEdges` is omitted, filled marks are shown between adjacent + * selected teeth that both appear in this set. + */ connectedTeeth?: ReadonlySet; + /** Filled connection edges (`toothEdgeKey(a,b)`). Preferred over `connectedTeeth` for edit mode. */ + linkedEdges?: ReadonlySet; onToggle?: (fdi: FdiToothId, event: { shiftKey: boolean }) => void; + /** Toggle connect/disconnect for an adjacent selected pair. */ + onToggleLink?: (a: FdiToothId, b: FdiToothId) => void; disabled?: boolean; /** Non-interactive display (Cases / connection history). */ readOnly?: boolean; @@ -38,10 +74,52 @@ interface FdiToothChartProps { className?: string; } +/** + * Distal tip for arch look — gentler than pre-SVG angles so realistic glyphs stay readable. + * Positive rotate = clockwise. + */ +const TOOTH_TWEAKS: Partial> = { + '18': { offset: 5, rotate: -7 }, + '17': { offset: 6, rotate: -5 }, + '16': { offset: 6, rotate: -3.5 }, + '15': { offset: 6, rotate: -2 }, + '14': { offset: 5, rotate: -1 }, + '13': { offset: 5.5, rotate: -0.5 }, + '12': { offset: 4, rotate: -0.25 }, + '11': { offset: 5, rotate: 0 }, + '21': { offset: 5, rotate: 0 }, + '22': { offset: 4, rotate: 0.25 }, + '23': { offset: 5.5, rotate: 0.5 }, + '24': { offset: 5, rotate: 1 }, + '25': { offset: 6, rotate: 2 }, + '26': { offset: 6, rotate: 3.5 }, + '27': { offset: 6, rotate: 5 }, + '28': { offset: 5, rotate: 7 }, + + '48': { offset: -5, rotate: -4 }, + '47': { offset: -6, rotate: -3 }, + '46': { offset: -6, rotate: -2 }, + '45': { offset: -6, rotate: -2 }, + '44': { offset: -5, rotate: -1 }, + '43': { offset: -4, rotate: -0.5 }, + '42': { offset: -4, rotate: -0.25 }, + '41': { offset: -3, rotate: 0 }, + '31': { offset: -3, rotate: 0 }, + '32': { offset: -4, rotate: 0.25 }, + '33': { offset: -4, rotate: 0.5 }, + '34': { offset: -5, rotate: 1 }, + '35': { offset: -6, rotate: 2 }, + '36': { offset: -6, rotate: 2 }, + '37': { offset: -6, rotate: 3 }, + '38': { offset: -5, rotate: 4 }, +}; + export function FdiToothChart({ selected, connectedTeeth, + linkedEdges, onToggle, + onToggleLink, disabled, readOnly = false, scale = 1, @@ -52,70 +130,12 @@ export function FdiToothChart({ }: FdiToothChartProps) { const t = useTranslations('treatment'); const uid = useId().replace(/:/g, ''); - const archPeak = 10; + const archPeak = 8; const interactive = !readOnly && Boolean(onToggle); + const linkInteractive = interactive && Boolean(onToggleLink); const isDisabled = disabled || readOnly; - const TOOTH_TWEAKS: Record = { - '18': { glyph: 'w-[1.98rem] h-[4.65rem]', offset: 7, rotate: -11 }, - '17': { glyph: 'w-[2rem] h-[4.75rem]', offset: 8, rotate: -8 }, - '16': { glyph: 'w-[2.1rem] h-[4.95rem]', offset: 9, rotate: -6 }, - '15': { glyph: 'w-[1.8rem] h-[4.8rem]', offset: 9, rotate: -4 }, - '14': { glyph: 'w-[1.8rem] h-[4.85rem]', offset: 9, rotate: -2 }, - '13': { glyph: 'w-[1.9rem] h-[5.28rem]', offset: 8, rotate: -1 }, - '12': { glyph: 'w-[1.8rem] h-[5.02rem]', offset: 7, rotate: -1 }, - '11': { glyph: 'w-[1.8rem] h-[5.08rem]', offset: 7, rotate: -0.5 }, - '21': { glyph: 'w-[1.8rem] h-[5.08rem]', offset: 7, rotate: 0.5 }, - '22': { glyph: 'w-[1.8rem] h-[5.02rem]', offset: 7, rotate: 1 }, - '23': { glyph: 'w-[1.9rem] h-[5.28rem]', offset: 8, rotate: 1 }, - '24': { glyph: 'w-[1.8rem] h-[4.85rem]', offset: 9, rotate: 2 }, - '25': { glyph: 'w-[1.8rem] h-[4.8rem]', offset: 9, rotate: 4 }, - '26': { glyph: 'w-[2.1rem] h-[4.95rem]', offset: 9, rotate: 6 }, - '27': { glyph: 'w-[2rem] h-[4.75rem]', offset: 8, rotate: 8 }, - '28': { glyph: 'w-[1.98rem] h-[4.65rem]', offset: 7, rotate: 11 }, - - '48': { glyph: 'w-[1.98rem] h-[4.7rem]', offset: -7, rotate: -11 }, - '47': { glyph: 'w-[2rem] h-[4.8rem]', offset: -8, rotate: -8 }, - '46': { glyph: 'w-[2.15rem] h-[5rem]', offset: -9, rotate: -6 }, - '45': { glyph: 'w-[1.82rem] h-[4.85rem]', offset: -9, rotate: -4 }, - '44': { glyph: 'w-[1.82rem] h-[4.9rem]', offset: -9, rotate: -2 }, - '43': { glyph: 'w-[1.88rem] h-[5.34rem]', offset: -8, rotate: -1 }, - '42': { glyph: 'w-[1.76rem] h-[5.04rem]', offset: -7, rotate: -1 }, - '41': { glyph: 'w-[1.74rem] h-[4.98rem]', offset: -7, rotate: -0.5 }, - '31': { glyph: 'w-[1.74rem] h-[4.98rem]', offset: -7, rotate: 0.5 }, - '32': { glyph: 'w-[1.76rem] h-[5.04rem]', offset: -7, rotate: 1 }, - '33': { glyph: 'w-[1.88rem] h-[5.34rem]', offset: -8, rotate: 1 }, - '34': { glyph: 'w-[1.82rem] h-[4.9rem]', offset: -9, rotate: 2 }, - '35': { glyph: 'w-[1.82rem] h-[4.85rem]', offset: -9, rotate: 4 }, - '36': { glyph: 'w-[2.15rem] h-[5rem]', offset: -9, rotate: 6 }, - '37': { glyph: 'w-[2rem] h-[4.8rem]', offset: -8, rotate: 8 }, - '38': { glyph: 'w-[1.98rem] h-[4.7rem]', offset: -7, rotate: 11 }, - }; - - const toothSizeClass = (fdi: FdiToothId) => { - const kind = getToothShapeKind(fdi); - const mid = MIDLINE_SIX.has(fdi); - switch (kind) { - case 'incisor': - return mid - ? { wrapper: 'w-[2.1rem]', glyph: 'w-[1.68rem] h-[4.78rem]' } - : { wrapper: 'w-[2rem]', glyph: 'w-[1.6rem] h-[4.6rem]' }; - case 'canine': - return mid - ? { wrapper: 'w-[2.2rem]', glyph: 'w-[1.78rem] h-[5.05rem]' } - : { wrapper: 'w-[2.1rem]', glyph: 'w-[1.7rem] h-[4.9rem]' }; - case 'premolar': - return mid - ? { wrapper: 'w-[2.34rem]', glyph: 'w-[1.93rem] h-[5.02rem]' } - : { wrapper: 'w-[2.25rem]', glyph: 'w-[1.85rem] h-[4.85rem]' }; - case 'molar': - return mid - ? { wrapper: 'w-[2.52rem]', glyph: 'w-[2.12rem] h-[5.1rem]' } - : { wrapper: 'w-[2.45rem]', glyph: 'w-[2.05rem] h-[4.95rem]' }; - default: - return { wrapper: 'w-[2.25rem]', glyph: 'w-[1.85rem] h-[4.85rem]' }; - } - }; + const toothSizeClass = (fdi: FdiToothId) => toothSize(fdi); const archOffset = (index: number, count: number, upper?: boolean) => { const center = (count - 1) / 2; @@ -127,7 +147,7 @@ export function FdiToothChart({ const archRotate = (index: number, count: number) => { const center = (count - 1) / 2; const normalized = (index - center) / center; - return normalized * 6; + return normalized * 4; }; const toothAccent = (fdi: FdiToothId) => toothColors?.[fdi]; @@ -144,38 +164,93 @@ export function FdiToothChart({ return accent ? { color: accent } : undefined; }; - /** Dots + thin links — only for Shift-connected spans (not plain singles). */ - const ConnectedRail = ({ teeth, upper }: { teeth: FdiToothId[]; upper?: boolean }) => { - const hasConnected = teeth.some((fdi) => connectedTeeth?.has(fdi)); - if (!hasConnected) return null; + const edgeIsLinked = (a: FdiToothId, b: FdiToothId): boolean => { + const key = toothEdgeKey(a, b); + if (linkedEdges) return linkedEdges.has(key); + // Read-only fallback: both teeth marked connected. + return Boolean(connectedTeeth?.has(a) && connectedTeeth?.has(b)); + }; + + /** Circles sit between neighbors, on the crown side (toward FDI numbers). */ + const EdgeRail = ({ teeth }: { teeth: FdiToothId[] }) => { + const anyVisible = teeth.some((fdi, i) => { + const next = teeth[i + 1]; + if (!next || !selected.has(fdi) || !selected.has(next)) return false; + const linked = edgeIsLinked(fdi, next); + return linkInteractive || linked; + }); + if (!anyVisible) return null; return (
{teeth.map((fdi, i) => { const size = toothSizeClass(fdi); - const isConnected = Boolean(connectedTeeth?.has(fdi)); + const prev = i > 0 ? teeth[i - 1] : undefined; const next = teeth[i + 1]; - const linkToNext = Boolean(isConnected && next && connectedTeeth?.has(next)); + const showEdge = Boolean(next && selected.has(fdi) && selected.has(next)); + const linked = showEdge ? edgeIsLinked(fdi, next!) : false; + const renderEdge = showEdge && (linkInteractive || linked); + // Narrow link across this column when both neighboring edges are filled. + const bridgeLine = Boolean( + prev && + next && + selected.has(prev) && + selected.has(fdi) && + selected.has(next) && + edgeIsLinked(prev, fdi) && + edgeIsLinked(fdi, next), + ); + return (
- {linkToNext ? ( + {bridgeLine ? ( ) : null} - {isConnected ? ( - - ) : ( - - )} + {renderEdge ? ( + linkInteractive ? ( +
); })} @@ -184,20 +259,21 @@ export function FdiToothChart({ }; const Row = ({ teeth, upper }: { teeth: FdiToothId[]; upper?: boolean }) => ( -
- {upper ? : null} -
+
+ {/* Lower: crowns face numbers above → rail above teeth */} + {!upper ? : null} +
{teeth.map((fdi, i) => { const kind = getToothShapeKind(fdi); const gid = `${uid}-g-${fdi}-${i}`; const isSel = selected.has(fdi); - const isConnected = Boolean(connectedTeeth?.has(fdi)); const size = toothSizeClass(fdi); const tweak = TOOTH_TWEAKS[fdi]; const offsetY = tweak ? tweak.offset : archOffset(i, teeth.length, upper); const rotate = tweak ? tweak.rotate : archRotate(i, teeth.length); const alignItems = upper ? 'items-end' : 'items-start'; const accent = toothAccent(fdi); + const realistic = Boolean(getRealisticToothAsset(fdi)); const glyph = ( ); return ( -
-
+
+ {!upper && realistic ? ( +
+ ) : null} +
{interactive ? (
)}
+ {upper && realistic ? ( +
+ ) : null}
); })}
- {!upper ? : null} + {/* Upper: crowns face numbers below → rail below teeth */} + {upper ? : null}
); @@ -279,15 +367,18 @@ export function FdiToothChart({ />
-
+
{FDI_UPPER_LEFT_TO_RIGHT.map((fdi) => { const size = toothSizeClass(fdi); const isSel = selected.has(fdi); return ( {fdi} @@ -298,15 +389,18 @@ export function FdiToothChart({
-
+
{FDI_LOWER_LEFT_TO_RIGHT.map((fdi) => { const size = toothSizeClass(fdi); const isSel = selected.has(fdi); return ( {fdi} diff --git a/frontend/src/components/ui/treatment/ToothGlyph.tsx b/frontend/src/components/ui/treatment/ToothGlyph.tsx index 2e0f7be..f4ea2ec 100644 --- a/frontend/src/components/ui/treatment/ToothGlyph.tsx +++ b/frontend/src/components/ui/treatment/ToothGlyph.tsx @@ -1,9 +1,13 @@ 'use client'; -import { memo, type ReactNode } from 'react'; +import { memo, type CSSProperties, type ReactNode } from 'react'; import type { FdiToothId } from '@/types/treatment'; import type { ToothShapeKind } from '@/components/treatment/fdiToothMeta'; import { getToothPathModel, type ToothPathModel } from '@/components/treatment/toothPathModel'; +import { + getRealisticToothAsset, + type RealisticToothAsset, +} from '@/components/treatment/realisticToothAssets'; interface ToothGlyphProps { fdi: FdiToothId; @@ -14,6 +18,7 @@ interface ToothGlyphProps { mirrored?: boolean; upsideDown?: boolean; className?: string; + style?: CSSProperties; /** When set, selected tooth glow + fill use this color instead of primary. */ accentColor?: string; } @@ -34,19 +39,40 @@ function rgbaFromHex(hex: string, alpha: number): string { return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`; } +function mixHex(hex: string, toward: string, amount: number): string { + const a = hexToRgb(hex); + const b = hexToRgb(toward); + if (!a || !b) return hex; + const t = Math.min(1, Math.max(0, amount)); + const r = Math.round(a.r + (b.r - a.r) * t); + const g = Math.round(a.g + (b.g - a.g) * t); + const bl = Math.round(a.b + (b.b - a.b) * t); + return `#${[r, g, bl].map((n) => n.toString(16).padStart(2, '0')).join('')}`; +} + function filledSilhouette( - model: ToothPathModel, + model: Pick, crownFill: string, rootFill: string, stroke: string, strokeW: number, + rootsFirst = false, ): ReactNode { - return ( + const crown = ( + + ); + const roots = model.roots.map((d, i) => ( + + )); + return rootsFirst ? ( <> - - {model.roots.map((d, i) => ( - - ))} + {roots} + {crown} + + ) : ( + <> + {crown} + {roots} ); } @@ -74,6 +100,27 @@ function renderClinicalFill( ); } +function renderRealisticFill( + asset: RealisticToothAsset, + selected: boolean, + crownGradId: string, + rootGradId: string, + accentColor?: string, +): ReactNode { + const stroke = selected + ? (accentColor ?? 'var(--color-primary)') + : 'var(--tooth-stroke)'; + const strokeW = selected ? 1.05 : 0.55; + return filledSilhouette( + asset, + `url(#${crownGradId})`, + `url(#${rootGradId})`, + stroke, + strokeW, + true, + ); +} + /** FDI chart tooth: clinical gradient + roots only. */ export const ToothGlyph = memo(function ToothGlyph({ fdi, @@ -85,17 +132,78 @@ export const ToothGlyph = memo(function ToothGlyph({ upsideDown, className = 'w-8 h-[4.85rem]', accentColor, + style, }: ToothGlyphProps) { - const model: ToothPathModel | null = getToothPathModel(fdi, kind, upper); + const realistic = getRealisticToothAsset(fdi); const filter = selected ? accentColor ? `drop-shadow(0 0 6px ${rgbaFromHex(accentColor, 0.75)})` : 'drop-shadow(0 0 6px rgba(9, 169, 188, 0.65))' - : undefined; + : 'drop-shadow(0 1px 1.5px rgba(15, 23, 42, 0.18))'; + const svgStyle: CSSProperties = { filter, ...style }; + + if (realistic) { + const crownGradId = `${gradientId}-crown`; + const rootGradId = `${gradientId}-root`; + // Align crown edge toward the FDI numbers (upper: bottom of box, lower: top). + const preserveAspectRatio = upper ? 'xMidYMax meet' : 'xMidYMin meet'; + + const crownStops = selected + ? accentColor + ? { hi: '#ffffff', mid: accentColor, shade: mixHex(accentColor, '#0f172a', 0.22) } + : { hi: '#ffffff', mid: '#5eead4', shade: '#0e7490' } + : { + hi: 'var(--tooth-crown-hi)', + mid: 'var(--tooth-crown-mid)', + shade: 'var(--tooth-crown-shade)', + }; + + const rootStops = selected + ? accentColor + ? { + hi: mixHex(accentColor, '#ffffff', 0.2), + mid: mixHex(accentColor, '#0f172a', 0.12), + shade: mixHex(accentColor, '#0f172a', 0.32), + } + : { hi: '#99f6e4', mid: '#14b8a6', shade: '#0f766e' } + : { + hi: 'var(--tooth-root-hi)', + mid: 'var(--tooth-root-mid)', + shade: 'var(--tooth-root-shade)', + }; + + return ( + + + + + + + + + + + + + + + {renderRealisticFill(realistic, selected, crownGradId, rootGradId, accentColor)} + + + ); + } + + const model: ToothPathModel | null = getToothPathModel(fdi, kind, upper); if (!model) { return ( - + ? @@ -121,7 +229,7 @@ export const ToothGlyph = memo(function ToothGlyph({ diff --git a/frontend/src/components/ui/treatment/TreatmentWorkspace.tsx b/frontend/src/components/ui/treatment/TreatmentWorkspace.tsx index 811e118..f9dccf7 100644 --- a/frontend/src/components/ui/treatment/TreatmentWorkspace.tsx +++ b/frontend/src/components/ui/treatment/TreatmentWorkspace.tsx @@ -45,8 +45,12 @@ import { connectedTeethSet, deriveTeethFromGroups, groupsFromFlatTeeth, + linkAdjacentTeeth, + linkedEdgesFromGroups, pruneToothProsthesisForGroups, + toothEdgeKey, toggleToothInGroups, + unlinkAdjacentTeeth, } from '@/components/treatment/toothSelectionGroups'; import type { LabDispatchAttentionItem } from '@/components/treatment/labDispatchAttention'; import { collectLabDispatchAttention } from '@/components/treatment/labDispatchAttention'; @@ -614,6 +618,10 @@ export function TreatmentWorkspace({ () => connectedTeethSet(activeDetail?.toothSelectionGroups ?? []), [activeDetail?.toothSelectionGroups], ); + const linkedToothEdges = useMemo( + () => linkedEdgesFromGroups(activeDetail?.toothSelectionGroups ?? []), + [activeDetail?.toothSelectionGroups], + ); const rangeAnchorRef = useRef(null); const wholePlanTeethSet = useMemo(() => { @@ -2016,6 +2024,7 @@ export function TreatmentWorkspace({ {entryStep === 'teeth' ? ( { + if ( + !canEditTreatmentForDay || + isDetailLocked(activeDetail) || + showWholeTreatmentPlan || + !activeDetailId + ) { + return; + } + const detail = details.find((d) => d.clientId === activeDetailId); + if (!detail) return; + const currentGroups = + detail.toothSelectionGroups.length > 0 + ? detail.toothSelectionGroups + : groupsFromFlatTeeth(detail.teeth); + const edgeLinked = linkedEdgesFromGroups(currentGroups).has(toothEdgeKey(a, b)); + const nextGroups = edgeLinked + ? unlinkAdjacentTeeth(currentGroups, a, b) + : linkAdjacentTeeth(currentGroups, a, b); + if (!nextGroups) return; + + setDetails((prev) => + prev.map((d) => + d.clientId !== activeDetailId + ? d + : { + ...d, + toothSelectionGroups: nextGroups, + teeth: deriveTeethFromGroups(nextGroups), + }, + ), + ); + + setLabCaseDrafts((prev) => + prev.map((lc) => { + if (lc.detailClientId !== activeDetailId) return lc; + return { + ...lc, + toothProsthesis: pruneToothProsthesisForGroups( + lc.toothProsthesis, + activeDetailId, + nextGroups, + ), + }; + }), + ); + }} disabled={!canEditTreatmentForDay || isDetailLocked(activeDetail)} /> ) : null} diff --git a/frontend/src/styles/globals.css b/frontend/src/styles/globals.css index 970d26f..94d04ae 100644 --- a/frontend/src/styles/globals.css +++ b/frontend/src/styles/globals.css @@ -57,6 +57,16 @@ --color-primary-soft: rgba(0, 156, 174, 0.16); --color-icon: #e1bc72; + /* Realistic tooth: white–blue crown, soft pink root */ + --tooth-crown-hi: #ffffff; + --tooth-crown-mid: #e0f2fe; + --tooth-crown-shade: #93c5fd; + --tooth-root-hi: #f6efe6; + --tooth-root-mid: #e4d4bf; + --tooth-root-shade: #c9b396; + --tooth-stroke: #94a3b8; + --tooth-hover: #cfe0f5; + --web-opacity: 0.12; --web-line: rgba(0, 188, 255, 0.32); --web-glow: rgba(0, 188, 255, 0.42); @@ -114,6 +124,15 @@ --color-primary-contrast: #001117; --color-primary-soft: rgba(9, 169, 188, 0.2); --color-icon: #e1bc72; + + --tooth-crown-hi: #ffffff; + --tooth-crown-mid: #dbeafe; + --tooth-crown-shade: #7dd3fc; + --tooth-root-hi: #f3ebe0; + --tooth-root-mid: #dcc9ae; + --tooth-root-shade: #bba280; + --tooth-stroke: #94a3b8; + --tooth-hover: #bdd0e0; } /* Default theme = dark */ @@ -173,6 +192,15 @@ --color-primary-soft: rgba(9, 169, 188, 0.2); --color-icon: #f3bb4b; + --tooth-crown-hi: #ffffff; + --tooth-crown-mid: #dbeafe; + --tooth-crown-shade: #7dd3fc; + --tooth-root-hi: #f3ebe0; + --tooth-root-mid: #dcc9ae; + --tooth-root-shade: #bba280; + --tooth-stroke: #94a3b8; + --tooth-hover: #bdd0e0; + --web-opacity: 0.14; --web-line: rgba(59, 153, 220, 0.34); --web-line-strong: rgba(73, 214, 255, 0.78);