@things-factory/figure-ui 10.1.22 → 10.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/modeller/figure-canvas.ts +5 -5
- package/client/modeller/figure-inspector.ts +2 -2
- package/client/modeller/figure-preview.ts +7 -7
- package/client/modeller/figure-settings.ts +4 -4
- package/client/modeller/figure-thumbnail.ts +1 -1
- package/client/modeller/figure-view.ts +6 -6
- package/client/modeller/part-edits.ts +1 -1
- package/client/modeller/scene-budget.ts +1 -1
- package/client/pages/figure-list-page.ts +1 -1
- package/client/pages/figure-modeller-page.ts +4 -4
- package/client/types.ts +2 -2
- package/dist-client/modeller/figure-canvas.d.ts +1 -1
- package/dist-client/modeller/figure-canvas.js +5 -5
- package/dist-client/modeller/figure-canvas.js.map +1 -1
- package/dist-client/modeller/figure-inspector.d.ts +1 -1
- package/dist-client/modeller/figure-inspector.js +2 -2
- package/dist-client/modeller/figure-inspector.js.map +1 -1
- package/dist-client/modeller/figure-preview.d.ts +3 -3
- package/dist-client/modeller/figure-preview.js +5 -5
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-settings.js +4 -4
- package/dist-client/modeller/figure-settings.js.map +1 -1
- package/dist-client/modeller/figure-thumbnail.js +1 -1
- package/dist-client/modeller/figure-thumbnail.js.map +1 -1
- package/dist-client/modeller/figure-view.d.ts +6 -6
- package/dist-client/modeller/figure-view.js.map +1 -1
- package/dist-client/modeller/part-edits.js +1 -1
- package/dist-client/modeller/part-edits.js.map +1 -1
- package/dist-client/modeller/scene-budget.d.ts +1 -1
- package/dist-client/modeller/scene-budget.js +1 -1
- package/dist-client/modeller/scene-budget.js.map +1 -1
- package/dist-client/pages/figure-list-page.js +1 -1
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +3 -3
- package/dist-client/pages/figure-modeller-page.js +4 -4
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +2 -2
- package/dist-client/types.js.map +1 -1
- package/package.json +3 -3
- package/test/figure-source.test.ts +3 -3
- package/test/i18n-prefix-guard.test.ts +19 -1
- package/translations/en.json +25 -14
- package/translations/ja.json +8 -8
- package/translations/ko.json +13 -13
- package/translations/ms.json +8 -8
- package/translations/zh.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-inspector.js","sourceRoot":"","sources":["../../client/modeller/figure-inspector.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport {\n EXTRUDE_PRIMITIVES,\n MATERIAL_PRESETS,\n PRIMITIVE_KINDS,\n SEGMENTED_PRIMITIVES,\n SEGMENT_PRESETS,\n SIZING_RULES,\n FIGURE_PLACEMENTS,\n trianglesOf\n} from '@hatiolab/figure-model'\nimport { ANCHOR_RULES, PART_ROLES, REPEAT_LIMIT, anchorOfPart, repeatPlanOfPart } from '@hatiolab/figure-model'\nimport type { AnchorRule, Axis, FigurePart, FigurePlacement, MaterialPreset, PartRole, PrimitiveKind } from '@hatiolab/figure-model'\nimport { activePalette } from '@hatiolab/things-scene'\n\nimport { partToFigure, partFromFigure } from './figure-source.js'\nimport type { FigureDraft, PartModel } from './figure-source.js'\nimport { canMirror } from './part-edits.js'\n\n/**\n * 고른 부품의 속성.\n *\n * ## 여기가 사용자 용어로 보여 주는 자리다\n *\n * 모델러 페이지가 갖고 있는 것은 **씬 모델**이다 — 기준 상자 위에 부품 컴포넌트가 놓인 상태.\n * 그런데 사용자에게 보여야 하는 것은 씬 속성 이름(`left`·`top`·`zPos`)이 아니라\n * **X·Y·Z** 다. 다른 3D 도구가 모두 그렇게 부르고, 위치도 부품의 **중심**으로 읽는다.\n *\n * 그래서 이 패널은 받은 부품을 FigureSource 형태로 **변환해서 보여 주고, 수정한 것을\n * 다시 씬 모델로 되돌려 알린다.** 변환은 `figure-source.ts` 가 한다 — 여기서 축을 다시 계산하지\n * 않는다.\n *\n * ## 있는 것만 보여 준다\n *\n * 분할 수는 곡면이 있는 도형에만, 모서리 굴림은 단면을 뽑는 도형에만 뜻이 있다.\n * 뜻 없는 칸을 회색으로 늘어놓으면 저작자가 「왜 안 먹지」를 고민하게 된다 —\n * 아예 내지 않는다.\n *\n * ## 이름은 저장되는 식별자다\n *\n * `FigurePart.name` 은 바인딩·애니메이션·슬롯이 가리키는 이름이다. 고치면 배치된\n * 인스턴스의 바인딩이 **조용히** 끊긴다. 그래서 이름 칸에만 경고를 붙인다.\n *\n * ## 「상태 색이 붙는 자리」를 걷었다\n *\n * `FigurePart.materialSlot`(당시 이름은 `slot`) 을 고르는 칸이 있었는데 **아무 일도 하지 않았다.** things-scene 의\n * figure 런타임이 `slot` 을 읽지 않는다 — 선언 한 줄이 전부다. 되는 줄 알고 만들었고,\n * 확인하지 않은 것이 잘못이다.\n *\n * 그리고 되게 만들더라도 이 모양이 아니다. 상태 색은 특별한 것이 아니라 **속성 하나**이고,\n * 툴팁·라벨·표시 여부와 같은 자리에서 다뤄져야 한다. 그 설계가 정해지면(figure-model#2)\n * 이 자리에 **속성 선언과 부품 바인딩** 편집기가 들어온다.\n *\n * 그때까지 빈 약속을 두지 않는다. 아무 일도 안 하는 칸은 없느니만 못하다.\n *\n * ## 고치지 않는다\n *\n * 정본 한 벌은 저작면이 갖는다. 여기서는 바뀐 부품을 알릴 뿐이다.\n */\n@customElement('figure-inspector')\nexport class FigureInspector extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n --figure-control-height: 30px;\n overflow-y: auto;\n background-color: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n /*\n 이 패널의 본문 크기를 여기서 한 번 정한다.\n\n 전에는 규칙마다 따로 적었다. 그러면 빠뜨린 규칙이 본문 기본 크기(1rem)를\n 물려받아 그 줄만 제목처럼 커진다. 오류가 아니라서 화면을 띄워 봐야 안다.\n 이 파일에서만 두 번 났다.\n\n form 요소(input · select · button)는 글꼴을 물려받지 않으므로 각자 적어야 한다.\n 그것 말고는 여기 값을 따른다.\n */\n font-size: 0.76rem;\n }\n\n /*\n ── 자리를 아끼는 판 ──────────────────────────────────────────────────────\n 속성은 앞으로 더 늘어난다(능력 · 포트 · 애니메이션 · 기준정보). 지금 여백을 그대로 두면\n 그것들이 들어올 자리가 없다. 절 여백을 6/10 으로, 제목 아래를 4px 로 줄였다 — 글자 크기와\n 칸 높이는 건드리지 않는다(작게 만드는 것이 아니라 **빈 곳을 줄이는 것**이다).\n */\n section {\n padding: 8px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n /* 입력 행은 촘촘하게, 서로 다른 편집 의도 사이에는 호흡을 둔다. */\n section + section {\n margin-top: 6px;\n }\n section[collapsible] {\n padding: 0;\n }\n details[group] > summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: 34px;\n padding: 0 10px;\n cursor: pointer;\n list-style: none;\n }\n details[group] > summary::-webkit-details-marker {\n display: none;\n }\n details[group] > summary h3 {\n margin: 0;\n }\n details[group] > summary md-icon {\n --md-icon-size: 16px;\n color: var(--md-sys-color-on-surface-variant);\n }\n details[group] > div[group-content] {\n padding: 4px 10px 8px;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n h3 {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0 0 4px 0;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /* 자산 메타데이터는 부품을 고친다는 현재 문맥에서는 요약으로만 둔다. */\n details[asset] {\n margin: -1px 0;\n }\n details[asset] summary {\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 26px;\n cursor: pointer;\n list-style: none;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n details[asset] summary::-webkit-details-marker {\n display: none;\n }\n details[asset] summary::before {\n content: '›';\n font-size: 1.1rem;\n line-height: 1;\n transition: transform 120ms ease;\n }\n details[asset][open] summary::before {\n transform: rotate(90deg);\n }\n details[asset] summary b {\n font-size: var(--ui-t-micro, 11px);\n letter-spacing: 0.04em;\n }\n details[asset] summary small {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 0.7rem;\n }\n\n label {\n display: flex;\n align-items: center;\n gap: 3px;\n margin-bottom: 2px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /*\n 도움말 물음표. **글자 줄을 하나 먹지 않는다** — 라벨·제목 줄의 남는 폭에 얹힌다.\n 아이콘만 두고 테두리를 안 두는 이유는 이것이 누를 것이라기보다 「더 있다」는 표시라서다.\n */\n button[help] {\n display: inline-flex;\n align-items: center;\n padding: 0;\n margin: 0;\n color: var(--md-sys-color-outline);\n background: none;\n border: none;\n cursor: pointer;\n }\n button[help] md-icon {\n --md-icon-size: 13px;\n font-size: 13px;\n }\n button[help]:hover,\n button[help][aria-expanded='true'] {\n color: var(--md-sys-color-primary);\n }\n\n /* 편 도움말. 라벨 아래 한 칸 들여 붙인다 — 무엇에 딸린 말인지 보이게. */\n p[hint] {\n margin: 2px 0 4px 0;\n padding-left: 6px;\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n border-left: 2px solid var(--md-sys-color-outline-variant);\n }\n\n input,\n select {\n width: 100%;\n min-height: var(--figure-control-height);\n box-sizing: border-box;\n padding: 4px 7px;\n font: var(--input-field-font, inherit);\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n input:focus,\n select:focus {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: -1px;\n }\n\n /*\n 숫자 칸 — 라벨을 칸 **안** 왼쪽에 둔다.\n\n 벡터 저작도구(Sketch·Figma)가 X·Y·W·H 를 이렇게 둔다. 라벨을 위에 얹으면 줄마다\n 두 줄을 먹어서, 자리·크기·회전 아홉 칸이 화면 절반을 차지한다. 안에 두면 한 줄에\n 셋이 들어가고 무엇이 무엇인지도 붙어 읽힌다.\n */\n div[cell] {\n display: flex;\n box-sizing: border-box;\n height: var(--figure-control-height);\n min-height: var(--figure-control-height);\n align-items: center;\n gap: 2px;\n padding-left: 6px;\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n div[cell]:focus-within {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: -1px;\n border-color: transparent;\n }\n div[cell] span[axis] {\n flex: none;\n font-size: 0.64rem;\n letter-spacing: 0.02em;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n div[cell] input {\n min-width: 0;\n min-height: 0;\n height: calc(var(--figure-control-height) - 2px);\n padding: 4px 6px 4px 2px;\n font-family: var(--mono-font, monospace);\n text-align: right;\n background: none;\n border: none;\n border-radius: 0;\n }\n div[cell] input:focus {\n outline: none;\n }\n\n /* 축 셋을 한 줄에 — 좌표는 셋이 같이 읽혀야 한다 */\n div[triple] {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 4px;\n }\n div[field] + div[field],\n div[triple] + div[field],\n div[field] + div[triple],\n div[inline-field] + div[inline-field],\n div[inline-field] + div[field],\n div[field] + div[inline-field],\n div[vector-row] + div[vector-row],\n div[vector-row] + div[field],\n div[field] + div[vector-row] {\n /* 칸 사이 7 → 5. 칸 자체는 그대로 두고 사이만 좁힌다. */\n margin-top: 5px;\n }\n\n /* 짧은 라벨과 단일 값은 같은 줄에서 읽는다. 복잡한 선택기에는 쓰지 않는다. */\n div[inline-field] {\n display: grid;\n /* 같은 편집 밀도 안에서는 입력 시작선을 맞춘다. 라벨 길이가 칸 폭을 흔들면 안 된다. */\n grid-template-columns: 64px minmax(0, 1fr);\n align-items: center;\n gap: 7px;\n }\n div[inline-field] > label {\n margin: 0;\n white-space: nowrap;\n }\n /* 한 줄 입력의 설명은 입력칸을 밀어내지 않고 바로 아래 전폭에서 읽는다. */\n div[inline-field] > p[hint] {\n grid-column: 1 / -1;\n margin: -2px 0 1px;\n }\n div[inline-field] > code[fixed] {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* X·Y·Z는 함께 비교하는 값이므로, 묶음 제목만 왼쪽에 두고 입력은 한 행에 둔다. */\n div[vector-row] {\n display: grid;\n grid-template-columns: 64px minmax(0, 1fr);\n align-items: center;\n gap: 7px;\n }\n div[vector-row] > label {\n margin: 0;\n white-space: nowrap;\n }\n div[vector-row] > p[hint] {\n grid-column: 1 / -1;\n margin: -2px 0 1px;\n }\n div[sizing-choices] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 5px;\n }\n button[sizing-choice] {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n min-height: 52px;\n padding: 7px 8px;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 7px;\n text-align: left;\n cursor: pointer;\n }\n button[sizing-choice][on] {\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n button[sizing-choice] b {\n font-size: 0.74rem;\n }\n button[sizing-choice] small {\n font-size: 0.64rem;\n line-height: 1.35;\n opacity: 0.8;\n }\n div[anchor-rows] {\n display: grid;\n gap: 6px;\n }\n div[anchor-row] {\n display: grid;\n grid-template-columns: 46px minmax(0, 1fr);\n align-items: start;\n gap: 8px;\n padding: 6px 7px;\n background: var(--md-sys-color-surface-container);\n border-radius: 7px;\n }\n div[anchor-row] label {\n margin: 0;\n font-size: 0.7rem;\n font-weight: 700;\n }\n div[anchor-options] {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n }\n button[anchor-choice] {\n min-width: 0;\n height: var(--figure-control-height);\n box-sizing: border-box;\n padding: 4px 3px;\n overflow: hidden;\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n font-size: 0.63rem;\n line-height: 1.2;\n text-overflow: ellipsis;\n white-space: nowrap;\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 5px;\n cursor: pointer;\n }\n button[anchor-choice][on] {\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n details[anchor-advanced] {\n grid-column: 2;\n margin-top: 4px;\n }\n details[anchor-advanced] summary {\n cursor: pointer;\n font-size: 0.62rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n details[anchor-advanced] div[anchor-options] {\n margin-top: 4px;\n }\n div[palette-meaning] {\n display: grid;\n gap: 2px;\n margin-top: 7px;\n padding: 6px 8px;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container);\n border-radius: 7px;\n }\n div[palette-meaning] b {\n font-size: 0.72rem;\n }\n div[palette-meaning] small {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.64rem;\n line-height: 1.35;\n }\n\n /*\n 정밀 제작 도구.\n\n 보드 편집기는 자산을 배치하는 곳이라 대충 놓아도 되지만, 여기는 자산을 만드는\n 곳이다. 「몸통 한가운데」·「바닥에 붙이기」·「반대쪽에도 하나」를 손으로 계산하면\n 늘 조금씩 어긋나고, 기준 치수를 바꾸면 전부 다시 해야 한다.\n */\n div[tools] {\n display: grid;\n grid-template-columns: repeat(6, minmax(0, 1fr));\n gap: 4px;\n }\n div[tools] button {\n display: grid;\n place-items: center;\n min-width: 0;\n height: var(--figure-control-height);\n box-sizing: border-box;\n padding: 0;\n font: var(--label-font, inherit);\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n div[tools] button md-icon {\n font-size: 17px;\n }\n div[tools] button[disabled] {\n opacity: 0.35;\n cursor: default;\n }\n div[tools] button:hover:not([disabled]) {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n\n /* 지금 어느 축이 늘어나나 — 크기를 고치면 바뀔 수 있어서 값으로 적는다 */\n p[axis-now] {\n margin: 4px 0 0 0;\n font-size: 0.7rem;\n color: var(--md-sys-color-primary);\n }\n /* 이 선택이 무엇을 무는지 — 성숙도의 묶음 수에 그대로 잡힌다 */\n p[cost] {\n margin: 4px 0 0 0;\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /* 이름을 바꾸면 무엇이 끊기는지 말한다 */\n p[warn] {\n margin: 4px 0 0 0;\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-tertiary);\n }\n\n /* 분할 수 프리셋 — 자유 입력보다 이 셋을 권한다 */\n div[presets] {\n display: flex;\n gap: 4px;\n margin-top: 4px;\n }\n div[presets] button {\n flex: 1;\n padding: 3px 0;\n font: var(--label-font, inherit);\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n div[presets] button[on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n\n /*\n 분할 수 선택 — 모양과 삼각형 수를 **함께** 보여 준다.\n\n 숫자만 늘어놓으면 8 과 16 이 무엇이 다른지 알 수 없다. 로우폴리에서 이 선택이\n 곧 「몇 개까지 편하게 놓을 수 있나」를 정하므로, 각지는 정도와 그 값이 무는\n 삼각형 수를 한자리에서 보아야 한다.\n\n 실루엣은 지어낸 그림이 아니다 — 곡면 프리미티브를 위에서 본 윤곽이 정확히\n 정n각형이다. 삼각형 수는 화면이 셈하지 않고 figure-model 의 trianglesOf 가 낸다.\n */\n div[segments] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 4px;\n }\n /* 원 유지 · 끄기 — 고르는 값이라 분할 수와 같은 모양으로 둔다 */\n div[choices] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 4px;\n }\n button[choice] {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 1px;\n padding: 6px 8px;\n text-align: left;\n font: var(--label-font, inherit);\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[choice]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n button[choice][on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n button[choice] small {\n opacity: 0.75;\n }\n button[seg] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1px;\n padding: 6px 2px 4px 2px;\n font: var(--label-font, inherit);\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[seg]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n button[seg][on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n button[seg] svg {\n display: block;\n /* 작으면 12 와 16 이 둘 다 동그라미로 보인다 — 각이 읽힐 만큼은 키운다 */\n width: 32px;\n height: 32px;\n }\n button[seg] svg polygon {\n fill: var(--md-sys-color-surface-container-highest);\n stroke: currentColor;\n stroke-width: 1.1;\n stroke-linejoin: miter;\n }\n button[seg][on] svg polygon {\n fill: var(--md-sys-color-surface-container-lowest);\n }\n button[seg] b {\n font-size: 0.8rem;\n font-weight: 700;\n line-height: 1.1;\n }\n button[seg] small {\n font-size: 0.62rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n button[seg][on] small {\n color: inherit;\n }\n /* 성능·실루엣 검증을 마친 프리셋만 고른다. */\n details[free] {\n margin-top: 6px;\n }\n details[free] summary {\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n cursor: pointer;\n }\n details[free] div[cell] {\n margin-top: 4px;\n }\n\n /*\n 색 견본.\n\n 토큰을 글자로 받지 않는다. things-scene 의 팔레트 선언이 「여기 든 이름이 저작에서\n 고를 수 있는 전부이고, 자유색을 허용하지 않는 것이 감각적인 로우폴리의 첫 규칙」\n 이라고 못박고 있다. 글자 칸으로 두면 그 규칙이 화면에서 사라지고, 오타 하나로\n resolveToken 이 던진다.\n */\n div[swatches] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n }\n /*\n 두 무리의 경계. 「칠한 색」과 「켜진 색」이 한 줄에 섞이면 accent(주황)와\n status-warning(주황)이 나란히 놓여 무엇이 무엇인지 알 수 없다.\n */\n label[group] {\n margin-top: 8px;\n padding-top: 8px;\n font-size: var(--ui-t-micro, 11px);\n letter-spacing: 0.03em;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n\n /* 값이 옆에 붙어야 끌면서 얼마인지 안다 */\n div[slider] {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n div[slider] input[type='range'] {\n flex: 1;\n accent-color: var(--md-sys-color-primary);\n cursor: pointer;\n }\n div[slider] output {\n flex: none;\n width: 2.6em;\n text-align: right;\n font: var(--mono-font, ui-monospace, monospace);\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n button[swatch] {\n width: 26px;\n height: 26px;\n padding: 0;\n background-color: var(--swatch);\n border: 1px solid rgba(0, 0, 0, 0.16);\n border-radius: 50%;\n cursor: pointer;\n }\n button[swatch][on] {\n /* 고른 것은 테두리가 아니라 고리로 표시한다 — 색 자체를 가리지 않으려고 */\n box-shadow:\n 0 0 0 2px var(--md-sys-color-surface-container-lowest),\n 0 0 0 4px var(--md-sys-color-primary);\n }\n span[token] {\n display: block;\n margin-top: 6px;\n font-family: var(--mono-font, monospace);\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n label[toggle] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 7px 0 0 0;\n font-size: 0.74rem;\n color: var(--md-sys-color-on-surface);\n cursor: pointer;\n }\n label[toggle] input {\n width: auto;\n }\n div[role-choices] {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n }\n div[role-choices] label[toggle] {\n justify-content: center;\n min-height: 30px;\n margin: 0;\n padding: 2px 4px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n font-size: 0.68rem;\n text-align: center;\n }\n div[role-choices] label[toggle]:has(input:checked) {\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n\n p[quiet] {\n margin: 0;\n font-size: 0.76rem;\n line-height: 1.6;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /*\n 고칠 수 없는 값 — 입력칸처럼 보이지 않게 한다.\n\n 전에는 그냥 글자였고, 옆의 이름 칸과 나란히 있으니 눌러도 되는 것처럼 보였다. 자물쇠와\n 칸 없는 바탕이 「읽는 값」이라고 먼저 말한다.\n */\n code[fixed] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n border-radius: 6px;\n background-color: var(--md-sys-color-surface-container-high, rgba(0, 0, 0, 0.05));\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n font-family: var(--mono-font, monospace);\n font-size: 0.76rem;\n }\n\n code[fixed] md-icon {\n --md-icon-size: 13px;\n opacity: 0.7;\n }\n `\n ]\n\n /** 도형의 DB 식별자. 비어 있으면 미저장 신규 모델이다. */\n @property({ type: String }) figureId = ''\n /** 도형의 표시 이름. 정본은 저작면이 들고, 여기서는 보여 주고 바꾼다(`figure-rename` 으로 알린다). */\n @property({ type: String }) figureName = ''\n /** 분류 하나 — 팔레트에서 묶는 기준. */\n @property({ type: String }) figureCategory?: string\n /** 태그 여럿 — 분류로는 못 하는 찾기. */\n @property({ type: Array }) figureTags?: string[]\n\n /** 펴 둔 도움말. 부품을 바꿔도 유지된다 — 매번 다시 펴게 하지 않는다. */\n @state() private opened = new Set<string>()\n\n @property({ type: Object }) draft?: FigureDraft\n /** 초안 위의 부품들. 씬 모델 그대로다. */\n @property({ type: Array }) parts: PartModel[] = []\n @property({ type: Number }) selected = -1\n\n /** `part` 는 HTMLElement 의 속성(DOMTokenList)이다 — 가리면 형이 어긋난다. */\n private get chosen(): PartModel | undefined {\n return this.parts?.[this.selected]\n }\n\n render() {\n const part = this.chosen\n if (!part || !this.draft) {\n return html`\n ${this.renderFigure(false)}\n <section>\n <h3>${i18next.t('figure.label.properties')}</h3>\n <p quiet>${i18next.t('figure.text.select-a-part-to-edit')}</p>\n </section>\n `\n }\n\n // 여기부터는 사용자 용어다 — X·Y·Z, 위치는 부품 중심, 회전은 도(degree).\n const shown = partToFigure(part, this.draft)\n\n /*\n 역할을 먼저 묻는다.\n\n 자리(slot)로 지정한 부품은 **그려지지 않는다.** 그 사실을 모른 채 재질부터 고르면 안 쓰일\n 색을 고르게 된다. 그리고 자리는 도형이 능력을 질 때만 뜻이 있으므로, 능력이 없으면 이\n 묶음은 아예 나오지 않는다 — 해 둬도 아무 일도 안 일어나는 칸을 보여 주지 않는다.\n */\n return html`\n ${this.renderFigure(true)} ${this.renderIdentity(shown)} ${this.renderRole(shown)}\n ${this.renderPlacement(shown)} ${this.renderShape(shown)} ${this.renderMaterial(shown)}\n ${this.renderBehaviour(shown)}\n `\n }\n\n /**\n * ── 이 부품이 능력에서 맡는 자리 ────────────────────────────────────────────\n *\n * 「능력」 탭이 도형 전체가 무엇을 하는지 정하고, 어느 부품이 그것을 받쳐 주는지는 여기서\n * 정한다. **부품을 고치는 곳이 한 군데여야** 하기 때문이다 — 목록을 따로 두면 같은 부품을\n * 두 화면에서 고치게 되고, 저작자는 어느 쪽이 이기는지 매번 생각해야 한다.\n *\n * 칸 수는 `repeat` 가 있으면 받지 않는다. 반복하는 자리의 칸 수는 **인스턴스 크기**가 정한다 —\n * 컨베이어가 길어지면 실을 자리가 늘어나는 것이 그것이고, 여기 적은 값은 안 쓰인다.\n */\n private renderRole(part: FigurePart) {\n if (!this.draft?.capabilities?.length) return nothing\n\n const roles = part.capability?.roles ?? []\n const repeats = part.sizing === 'repeat' && !!part.repeat\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.role-in-capability')}${this.help('figure.text.anchors-are-not-drawn')}</h3>\n\n <div role-choices>\n ${(PART_ROLES as readonly PartRole[]).map(\n one => html`\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${live(roles.includes(one))}\n @change=${(e: Event) => this.toggleRole(one, (e.target as HTMLInputElement).checked)}\n />\n ${i18next.t('figure.label.role-' + one)}\n </label>\n `\n )}\n </div>\n ${roles.length === 0 ? html`<p quiet>${i18next.t('figure.text.role-none-means-drawn')}</p>` : nothing}\n\n ${roles.includes('slot')\n ? html`\n <div field>\n <label>${i18next.t('figure.label.accepts')}${this.help('figure.text.accepts-explained')}</label>\n <input\n .value=${(part.capability?.accepts ?? []).join(', ')}\n placeholder=${i18next.t('figure.text.accepts-anything')}\n @change=${(e: Event) => this.setAccepts((e.target as HTMLInputElement).value)}\n />\n </div>\n <div field>\n <label>${i18next.t('figure.label.slot-capacity')}</label>\n ${repeats\n ? html`<p quiet>${i18next.t('figure.text.capacity-from-repeat')}</p>`\n : this.number('', part.capability?.capacity ?? 1, value => this.setCapacity(value), 1)}\n </div>\n `\n : nothing}\n </section>\n `\n }\n\n /**\n * 역할 하나를 켜고 끈다.\n *\n * **여럿을 함께 맡을 수 있다.** AGV 의 상판은 물건을 놓는 자리이면서 그 자리로 물건이\n * 들어오고 나간다. 그래서 하나를 고르는 선택 상자가 아니라 확인 상자다.\n *\n * 마지막 역할을 끄면 `capability` 를 통째로 지운다. 빈 배열을 남기면 형식이 오류로 보고,\n * 저작자가 보기에는 아무 역할도 없는 평범한 부품인데 발행이 막힌다.\n */\n private toggleRole(role: PartRole, on: boolean) {\n this.change(part => {\n const roles = (part.capability?.roles ?? []).filter(one => one !== role)\n if (on) roles.push(role)\n\n if (roles.length === 0) {\n const { capability, ...rest } = part\n return rest as FigurePart\n }\n\n // 형식이 적은 순서를 지킨다 — 켠 순서대로 두면 같은 뜻이 다른 값으로 저장된다.\n const ordered = (PART_ROLES as readonly PartRole[]).filter(one => roles.includes(one))\n return { ...part, capability: { ...part.capability, roles: ordered } }\n })\n }\n\n private setAccepts(raw: string) {\n const accepts = raw\n .split(',')\n .map(one => one.trim())\n .filter(one => one.length > 0)\n\n this.change(part => {\n const spec = { ...part.capability, roles: part.capability?.roles ?? ['slot' as PartRole] }\n // 빈 목록은 제한하지 않는다는 뜻이다. 빈 배열을 저장하면 아무것도 못 받는 것으로 읽힌다.\n if (accepts.length > 0) spec.accepts = accepts\n else delete spec.accepts\n return { ...part, capability: spec as NonNullable<FigurePart['capability']> }\n })\n }\n\n private setCapacity(value: number) {\n this.change(part => {\n const spec = { ...part.capability, roles: part.capability?.roles ?? ['slot' as PartRole] }\n if (Number.isFinite(value) && value >= 1) spec.capacity = Math.floor(value)\n else delete spec.capacity\n return { ...part, capability: spec as NonNullable<FigurePart['capability']> }\n })\n }\n\n /**\n * 도형 자체 — 이름과 타입.\n *\n * ## 왜 머리줄이 아니라 여기인가\n *\n * 저작면 위에 줄을 하나 더 두고 이름 칸을 놓았었다. 앱에서는 셸이 페이지 제목으로 그 이름을\n * 이미 그리므로 같은 글자가 두 번 나오고, 캔버스가 주인공인 화면에서 그 줄만큼 높이를 잃었다.\n * 속성 패널은 「이 도형이 무엇인가」를 다루는 자리이므로 이름도 여기 산다.\n *\n * ## 타입은 읽기만 한다\n *\n * 저장되는 식별자다. 배치된 보드가 이 이름으로 타입을 찾으므로 만든 뒤에는 못 바꾼다.\n */\n private renderFigure(compact = false) {\n const fields = html`\n <div inline-field>\n <label>${i18next.t('figure.label.name')}</label>\n <input\n .value=${this.figureName}\n placeholder=${i18next.t('figure.text.figure-display-name')}\n @change=${(e: Event) =>\n this.dispatchEvent(\n new CustomEvent('figure-rename', {\n detail: { name: (e.target as HTMLInputElement).value },\n bubbles: true,\n composed: true\n })\n )}\n />\n </div>\n <div inline-field>\n <label>${i18next.t('figure.label.figure-type')}${this.figureId ? this.helpButton('figure.text.type-name-cannot-change') : nothing}</label>\n ${!this.figureId\n ? html`<input\n .value=${this.draft?.figureType ?? ''}\n placeholder=${i18next.t('figure.text.figure-type-placeholder', { defaultValue: '도형 식별 타입 (예: OHT)' })}\n @change=${(e: Event) =>\n this.dispatchEvent(\n new CustomEvent('figure-type-change', {\n detail: { type: (e.target as HTMLInputElement).value.trim().toUpperCase() },\n bubbles: true,\n composed: true\n })\n )}\n />`\n : html`<code fixed title=${i18next.t('figure.text.type-name-cannot-change')}>\n <md-icon>lock</md-icon>${this.draft?.figureType ?? ''}\n </code>\n ${this.helpHint('figure.text.type-name-cannot-change')}`}\n </div>\n <!--\n 왜 못 고치는지 **적어 둔다.** 전에는 tooltip 에만 있었고, 그러면 눌러 보고 안 되는 것을\n 알게 된다 — 화면이 안 되는 이유를 말하지 않으면 사용자가 자기 실수라고 생각한다.\n -->\n\n <div inline-field>\n <label>${i18next.t('figure.label.figure-placement')}${this.helpButton('figure.text.placement-explained')}</label>\n <select\n .value=${this.draft?.placement ?? 'floor'}\n @change=${(e: Event) => this.setPlacement((e.target as HTMLSelectElement).value as FigurePlacement)}\n >\n <option value=\"floor\" ?selected=${(this.draft?.placement ?? 'floor') === 'floor'}>\n ${i18next.t('figure.label.placement-floor')}\n </option>\n <option value=\"ceiling\" ?selected=${this.draft?.placement === 'ceiling'}>\n ${i18next.t('figure.label.placement-ceiling')}\n </option>\n <option value=\"center\" ?selected=${this.draft?.placement === 'center'}>\n ${i18next.t('figure.label.placement-center')}\n </option>\n </select>\n ${this.helpHint('figure.text.placement-explained')}\n </div>\n\n <div inline-field>\n <label>${i18next.t('figure.label.category')}${this.helpButton('figure.text.category-explained')}</label>\n <input\n .value=${this.figureCategory ?? ''}\n placeholder=${i18next.t('figure.text.category-placeholder')}\n @change=${(e: Event) => this.tellCatalog({ category: (e.target as HTMLInputElement).value.trim() || undefined })}\n />\n ${this.helpHint('figure.text.category-explained')}\n </div>\n\n <div inline-field>\n <label>${i18next.t('figure.label.tags')}${this.helpButton('figure.text.tags-explained')}</label>\n <input\n .value=${(this.figureTags ?? []).join(', ')}\n placeholder=${i18next.t('figure.text.tags-placeholder')}\n @change=${(e: Event) => this.tellTags((e.target as HTMLInputElement).value)}\n />\n ${this.helpHint('figure.text.tags-explained')}\n </div>\n `\n\n if (compact) {\n return html`\n <section>\n <details asset>\n <summary>\n <b>${i18next.t('figure.label.figure')}</b>\n <small>${this.figureName || this.draft?.figureType || ''}</small>\n </summary>\n ${fields}\n </details>\n </section>\n `\n }\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.figure')}</h3>\n ${fields}\n </section>\n `\n }\n\n /**\n * 태그 줄을 목록으로.\n *\n * 쉼표로 가르고 `#` 은 떼어 낸다 — 사람은 `#반출` 이라고 적고, 저장되는 것은 `반출` 이다.\n * 겹친 것은 한 번만 남긴다. 빈 줄은 **빈 배열**이지 `undefined` 가 아니다 — 전부 지우려는\n * 저작자가 지울 방법을 잃지 않게.\n */\n private tellTags(raw: string) {\n const tags = [\n ...new Set(\n raw\n .split(',')\n .map(one => one.trim().replace(/^#+/, '').trim())\n .filter(one => one.length > 0)\n )\n ]\n\n this.tellCatalog({ tags })\n }\n\n /** 카탈로그 값이 바뀌었다고 알린다. 정본은 저작면이 든다 — 여기서는 알리기만 한다. */\n private tellCatalog(patch: { category?: string; tags?: string[] }) {\n this.dispatchEvent(new CustomEvent('figure-catalog', { detail: patch, bubbles: true, composed: true }))\n }\n\n /**\n * 3D 배치 기준면을 바꾼다 ('floor' | 'ceiling' | 'center').\n *\n * **부품은 건드리지 않는다.** 이 값은 「이 종류는 어느 면에 붙는가」를 말할 뿐이고,\n * 저작한 모양은 그것과 무관하다. 형식이 부품을 상자 중심에서 재므로, 배치가 달라도\n * `parts` 는 한 글자도 안 바뀐다.\n *\n * 한동안 여기서 `zPos` 를 다시 계산했다. 편집 캔버스가 이 값을 씬의 좌표 모드로\n * 흘려보내던 때의 일인데, 그러면 배치를 고를 때마다 저작자가 적어 둔 높이가 전부\n * 다른 숫자로 보였다 — 저장되는 것은 그대로인데. 좌표 모드는 이제 보기 설정이고\n * (`figure-view.ts`), 편집 캔버스는 늘 상자 기준이다.\n */\n private setPlacement(placement: FigurePlacement) {\n if (!this.draft) return\n const draft = { ...this.draft, placement }\n this.dispatchEvent(new CustomEvent('update-draft', { detail: { draft }, bubbles: true, composed: true }))\n }\n\n private renderIdentity(part: FigurePart) {\n return html`\n <section>\n <h3>${i18next.t('figure.label.part')}</h3>\n <div inline-field>\n <label>${i18next.t('figure.label.name')}${this.helpButton('figure.text.renaming-a-part-breaks-bindings')}</label>\n <input\n .value=${part.name}\n @change=${(e: Event) => this.put({ name: (e.target as HTMLInputElement).value })}\n />\n ${this.helpHint('figure.text.renaming-a-part-breaks-bindings')}\n </div>\n <div inline-field>\n <label>${i18next.t('figure.label.shape')}</label>\n <select @change=${(e: Event) => this.changePrimitive((e.target as HTMLSelectElement).value as PrimitiveKind)}>\n ${PRIMITIVE_KINDS.map(\n kind => html`\n <option value=${kind} ?selected=${kind === part.primitive}>\n ${i18next.t('figure.label.primitive-' + kind)}\n </option>\n `\n )}\n </select>\n </div>\n </section>\n `\n }\n\n private renderPlacement(part: FigurePart) {\n const t = part.transform\n const rotation = t.rotation ?? {}\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.placement')}</h3>\n\n <div vector-row>\n <label>${i18next.t('figure.label.part-position')}</label>\n <div triple>\n ${(['x', 'y', 'z'] as const).map(axis =>\n this.number(axis.toUpperCase(), t.position[axis], value =>\n this.putTransform({ position: { ...t.position, [axis]: value } })\n )\n )}\n </div>\n </div>\n\n <div vector-row>\n <label>${i18next.t('figure.label.part-size')}</label>\n <div triple>\n ${(['x', 'y', 'z'] as const).map(axis =>\n this.number(\n axis.toUpperCase(),\n t.size[axis],\n value => this.putTransform({ size: { ...t.size, [axis]: value } }),\n 1\n )\n )}\n </div>\n </div>\n\n <div vector-row>\n <label>${i18next.t('figure.label.rotation-degrees')}</label>\n <div triple>\n ${(['x', 'y', 'z'] as const).map(axis =>\n this.number(axis.toUpperCase(), rotation[axis] ?? 0, value =>\n this.putTransform({ rotation: { ...rotation, [axis]: value } })\n )\n )}\n </div>\n </div>\n\n <div vector-row>\n <label>${i18next.t('figure.label.tools')}${this.helpButton('figure.text.tools-explained')}</label>\n <div tools>\n <button\n aria-label=${i18next.t('figure.button.center-x')}\n title=${i18next.t('figure.text.center-on-x')}\n @click=${() => this.ask('center', 'x')}\n >\n <md-icon>horizontal_distribute</md-icon>\n </button>\n <button\n aria-label=${i18next.t('figure.button.center-z')}\n title=${i18next.t('figure.text.center-on-z')}\n @click=${() => this.ask('center', 'z')}\n >\n <md-icon>vertical_align_center</md-icon>\n </button>\n <button\n aria-label=${i18next.t('figure.button.to-floor')}\n title=${i18next.t('figure.text.put-on-floor')}\n @click=${() => this.ask('floor', 'y')}\n >\n <md-icon>vertical_align_bottom</md-icon>\n </button>\n ${(['x', 'z'] as const).map(axis => {\n // 이미 가운데면 대칭이 제자리다 — 겹친 부품이 조용히 생기지 않게 막는다\n const able = this.draft ? canMirror(this.draft, this.parts, this.selected, axis) : false\n return html`\n <button\n ?disabled=${!able}\n aria-label=${i18next.t('figure.button.mirror-' + axis)}\n title=${i18next.t(able ? 'figure.text.mirror-explained' : 'figure.text.mirror-would-overlap')}\n @click=${() => this.ask('mirror', axis)}\n >\n <md-icon>${axis === 'x' ? 'flip' : 'flip_camera_android'}</md-icon>\n </button>\n `\n })}\n <button\n aria-label=${i18next.t('figure.button.snap')}\n title=${i18next.t('figure.text.snap-explained')}\n @click=${() => this.ask('snap', 'x')}\n >\n <md-icon>grid_on</md-icon>\n </button>\n </div>\n ${this.helpHint('figure.text.tools-explained')}\n </div>\n </section>\n `\n }\n\n /** 분할 수와 모서리 굴림 — 해당하는 도형에만 낸다. */\n private renderShape(part: FigurePart) {\n const segmented = SEGMENTED_PRIMITIVES.includes(part.primitive)\n const extruded = EXTRUDE_PRIMITIVES.includes(part.primitive)\n if (!segmented && !extruded) return nothing\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.shape-detail')}</h3>\n ${segmented ? this.renderSegments(part) : nothing}\n ${segmented ? this.renderKeepRound(part) : nothing}\n ${extruded ? this.renderExtruded(part) : nothing}\n </section>\n `\n }\n\n /**\n * 민 단면 — 모서리와 **속을 파는 두께.**\n *\n * 벽 두께를 0 으로 두면 통짜다. 0 보다 크면 그만큼 안으로 들어온 테가 생기고, 그것이\n * 트레이 · 토트 · 통 · 케이스가 되는 자리다. 바닥은 비워 두면 벽과 같고, 0 으로 두면\n * 위아래가 다 뚫린 테두리가 된다.\n */\n private renderExtruded(part: FigurePart) {\n const hollow = part.shape?.hollow\n const wall = hollow?.wall ?? 0\n\n const reshape = (over: Record<string, unknown>) =>\n this.put({ shape: { ...(part.shape ?? {}), ...over } })\n\n return html`\n <div inline-field>\n <label>${i18next.t('figure.label.corner-round')}</label>\n ${this.number('', part.shape?.round ?? 0, value => reshape({ round: value }))}\n </div>\n <div inline-field>\n <label>${i18next.t('figure.label.hollow-wall')}</label>\n ${this.number('', wall, value =>\n reshape({ hollow: value > 0 ? { ...(hollow ?? {}), wall: value } : undefined })\n )}\n </div>\n ${wall > 0\n ? html`\n <div inline-field>\n <label>${i18next.t('figure.label.hollow-floor')}</label>\n ${this.number('', hollow?.floor ?? wall, value => reshape({ hollow: { ...hollow, wall, floor: value } }))}\n </div>\n `\n : nothing}\n `\n }\n\n /**\n * 분할 수 — 모양과 무게를 함께 고른다.\n *\n * 삼각형 수는 화면이 셈하지 않는다. `trianglesOf` 에 그 분할 수를 가진 부품을 주고\n * 물어본다 — 화면이 제 나름의 공식을 들면 figure-model 이 규칙을 바꿔도 화면만\n * 옛 답을 계속 낸다.\n */\n /**\n * 원통을 원통으로 둘까 — **고르는 값이고, 값을 치른다.**\n *\n * 인스턴스가 축마다 다른 배율을 받으면 원기둥이 타원 기둥이 된다. 지키려면 제 변환이\n * 있어야 하고, 합친 형상은 변환을 하나만 받으므로 지키는 곡면 부품 하나가 draw call\n * 하나다. 저작자가 그 대가를 모르고 고르면 안 되므로 그 자리에 적는다.\n */\n private renderKeepRound(part: FigurePart) {\n const on = part.keepRound !== false\n\n return html`\n <div field>\n <label>${i18next.t('figure.label.keep-round')}</label>\n <div choices>\n <button choice ?on=${on} @click=${() => this.put({ keepRound: undefined })}>\n <b>${i18next.t('figure.text.keep-round-on')}</b>\n <small>${i18next.t('figure.text.keep-round-cost')}</small>\n </button>\n <button choice ?on=${!on} @click=${() => this.put({ keepRound: false })}>\n <b>${i18next.t('figure.text.keep-round-off')}</b>\n <small>${i18next.t('figure.text.keep-round-merged')}</small>\n </button>\n </div>\n </div>\n `\n }\n\n private renderSegments(part: FigurePart) {\n const current = part.segments ?? SEGMENT_PRESETS[1]\n const common = SEGMENT_PRESETS.slice(0, 4)\n const advanced = SEGMENT_PRESETS.slice(4)\n\n return html`\n <div field>\n <label>${i18next.t('figure.label.segments')}</label>\n <div segments>\n ${common.map(preset => {\n const triangles = trianglesOf({ ...part, segments: preset })\n return html`\n <button seg ?on=${current === preset} @click=${() => this.put({ segments: preset })}>\n ${this.silhouette(preset)}\n <b>${preset}</b>\n <small>${i18next.t('figure.text.n-triangles', { count: triangles })}</small>\n </button>\n `\n })}\n </div>\n\n <details free ?open=${advanced.includes(current)}>\n <summary>${i18next.t('figure.text.more-segment-options')}</summary>\n <div presets>\n ${advanced.map(preset => html`\n <button ?on=${current === preset} @click=${() => this.put({ segments: preset })}>${preset}</button>\n `)}\n </div>\n </details>\n </div>\n `\n }\n\n /**\n * 분할 수의 실루엣.\n *\n * 곡면 프리미티브를 위에서 본 윤곽이 정확히 정n각형이다 — 원기둥의 단면도, 구의\n * 적도도. 그림을 지어내는 것이 아니라 실제로 만들어지는 모양이다.\n */\n private silhouette(segments: number) {\n const points: string[] = []\n for (let i = 0; i < segments; i++) {\n // 꼭짓점 하나가 위로 오게 -90도에서 시작한다\n const angle = -Math.PI / 2 + (i * 2 * Math.PI) / segments\n points.push(`${(16 + 13 * Math.cos(angle)).toFixed(2)},${(16 + 13 * Math.sin(angle)).toFixed(2)}`)\n }\n\n return svg`\n <svg viewBox=\"0 0 32 32\" aria-hidden=\"true\">\n <polygon points=${points.join(' ')}></polygon>\n </svg>\n `\n }\n\n /** 견본 한 줄. 무리마다 부른다. */\n private palettePurpose(token?: string) {\n const purposes: Record<string, string> = {\n 'palette.primary': 'primary',\n 'palette.accent': 'accent',\n 'palette.neutral': 'neutral',\n 'palette.dark': 'dark',\n 'palette.light': 'light',\n 'palette.surface': 'surface',\n 'palette.status-danger': 'danger',\n 'palette.status-warning': 'warning',\n 'palette.status-info': 'info',\n 'palette.status-success': 'success'\n }\n\n return i18next.t(`figure.label.palette-purpose-${purposes[token ?? ''] ?? 'registered'}`)\n }\n\n private swatches(entries: [string, string][], chosen?: string) {\n return entries.map(\n ([token, color]) => {\n const purpose = this.palettePurpose(token)\n return html`\n <button\n swatch\n ?on=${token === chosen}\n aria-label=${purpose}\n title=${purpose}\n style=\"--swatch:${color}\"\n @click=${() => this.putMaterial({ token })}\n ></button>\n `\n }\n )\n }\n\n private renderMaterial(part: FigurePart) {\n const material = part.material ?? {}\n const palette = activePalette() as Record<string, string>\n\n /*\n 두 무리로 나눠 보인다 — **칠한 색**과 **켜진 색**.\n\n `palette.status-*` 는 램프 · 표시등에 쓰라고 있는 색이다. 한 줄에 섞어 두면\n `accent`(주황)와 `status-warning`(주황)이 나란히 놓여 무엇이 무엇인지 알 수\n 없다. 값을 비틀어 벌리는 대신 **고르는 자리에서 경계를 긋는다.**\n */\n const entries = Object.entries(palette)\n const painted = entries.filter(([token]) => !token.startsWith('palette.status-'))\n const lit = entries.filter(([token]) => token.startsWith('palette.status-'))\n\n /** 자체발광. 없으면 안 빛나는 부품이다. */\n const glow = material.emissive\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.part-material')}</h3>\n\n <div field>\n <label>${i18next.t('figure.label.palette-role')}</label>\n <div swatches>\n ${this.swatches(painted, material.token)}\n </div>\n ${lit.length\n ? html`\n <label group>${i18next.t('figure.label.status-colors')}</label>\n <div swatches>\n ${this.swatches(lit, material.token)}\n </div>\n `\n : ''}\n <div palette-meaning>\n <b>${material.token ? this.palettePurpose(material.token) : i18next.t('figure.label.no-color-chosen')}</b>\n <small>${i18next.t('figure.text.palette-purpose-explained')}</small>\n </div>\n </div>\n\n <div field>\n <label>${i18next.t('figure.label.preset')}</label>\n <select\n @change=${(e: Event) => {\n const value = (e.target as HTMLSelectElement).value\n this.putMaterial({ preset: (value || undefined) as MaterialPreset | undefined })\n }}\n >\n <option value=\"\" ?selected=${!material.preset}>${i18next.t('figure.label.none')}</option>\n ${MATERIAL_PRESETS.map(\n preset => html`\n <option value=${preset} ?selected=${preset === material.preset}>\n ${i18next.t('figure.label.preset-' + preset)}\n </option>\n `\n )}\n </select>\n </div>\n\n <!-- 값이 없으면 각지게 그린다 — 형식이 그렇게 못 박았다. 체크상자도 그대로 보인다 -->\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${material.flatShading ?? true}\n @change=${(e: Event) => this.putMaterial({ flatShading: (e.target as HTMLInputElement).checked })}\n />\n ${i18next.t('figure.label.flat-shading')}\n </label>\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${!!material.transparent}\n @change=${(e: Event) => this.putMaterial({ transparent: (e.target as HTMLInputElement).checked })}\n />\n ${i18next.t('figure.label.transparent')}\n </label>\n\n <div field>\n <label>${i18next.t('figure.label.emissive')}${this.help('figure.text.emissive-explained')}</label>\n\n <div slider>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n .value=${String(glow?.intensity ?? 0)}\n @input=${(e: Event) => this.putEmissive(Number((e.target as HTMLInputElement).value))}\n />\n <output>${(glow?.intensity ?? 0).toFixed(2)}</output>\n </div>\n </div>\n\n ${glow\n ? html`\n <div field>\n <label>${i18next.t('figure.label.emissive-color')}</label>\n <div swatches>\n <button\n swatch\n same\n ?on=${!glow.token}\n title=${i18next.t('figure.label.same-as-off')}\n style=\"--swatch:${(material.token && palette[material.token]) || palette['palette.neutral']}\"\n @click=${() => this.putMaterial({ emissive: { ...glow, token: undefined } })}\n ></button>\n ${lit.map(\n ([token, color]) => html`\n <button\n swatch\n ?on=${token === glow.token}\n title=${token}\n style=\"--swatch:${color}\"\n @click=${() => this.putMaterial({ emissive: { ...glow, token } })}\n ></button>\n `\n )}\n </div>\n <span token>${glow.token ?? i18next.t('figure.label.same-as-off')}</span>\n </div>\n\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${!!glow.on}\n @change=${(e: Event) =>\n this.putMaterial({ emissive: { ...glow, on: (e.target as HTMLInputElement).checked } })}\n />\n ${i18next.t('figure.label.on-by-default')}\n </label>\n ${this.help('figure.text.on-by-default-explained')}\n `\n : ''}\n\n </section>\n `\n }\n\n /**\n * 인스턴스 크기가 바뀔 때 이 부품이 어떻게 변하나.\n *\n * ## 늘어나는 축은 아직 못 보여 준다\n *\n * `stretch` 는 형식에 축을 적는 자리가 없어서 그리는 쪽이 **가장 길게 뻗은 축**으로\n * 정한다. 그 답을 화면에 적어 주려면 규칙을 가진 쪽에 물어야 하는데, 그 함수\n * (`stretchAxisOf` · `stretchAxis`)가 아직 배포된 판에 없다.\n *\n * 화면이 제 나름대로 계산하지 않는다. 규칙이 두 벌이 되면 보이는 축과 실제로 늘어나는\n * 축이 갈리고, 그 어긋남은 화면을 믿게 만든 다음에 드러난다. 배포되면 잇는다.\n *\n * ## 값이 있는 것을 밝힌다\n *\n * `scale` 이 아닌 부품은 합쳐 그릴 수 없어 draw call 을 하나 더 쓴다. 그 비용이\n * 성숙도의 묶음 수에 그대로 잡힌다. 고르는 자리에서 미리 말해 준다.\n */\n private renderBehaviour(part: FigurePart) {\n const sizing = part.sizing ?? 'scale'\n const repeat = part.repeat ?? { axis: 'x' as Axis, pitch: 0 }\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.sizing-rule')}${this.help('figure.text.sizing-' + sizing + '-explained')}</h3>\n <div sizing-choices>\n ${SIZING_RULES.map(\n rule => html`\n <button sizing-choice ?on=${rule === sizing} @click=${() => this.put({ sizing: rule })}>\n <b>${i18next.t('figure.label.sizing-' + rule)}</b>\n <small>${i18next.t('figure.text.sizing-' + rule + '-explained')}</small>\n </button>\n `\n )}\n </div>\n ${sizing === 'scale'\n ? nothing\n : html`<p cost>${i18next.t('figure.text.sizing-costs-one-draw-call')}</p>`}\n\n ${this.renderAnchors(part)}\n\n ${sizing === 'repeat'\n ? html`\n <div inline-field>\n <label>${i18next.t('figure.label.repeat-axis')}</label>\n <select\n @change=${(e: Event) =>\n this.put({ repeat: { ...repeat, axis: (e.target as HTMLSelectElement).value as Axis } })}\n >\n ${(['x', 'y', 'z'] as const).map(\n axis => html`<option value=${axis} ?selected=${axis === repeat.axis}>${axis}</option>`\n )}\n </select>\n </div>\n <div inline-field>\n <label>${i18next.t('figure.label.repeat-pitch')}${this.helpButton('figure.text.repeat-pitch-explained')}</label>\n ${this.number('', repeat.pitch, value => this.put({ repeat: { ...repeat, pitch: value } }), 1)}\n ${this.helpHint('figure.text.repeat-pitch-explained')}\n </div>\n ${this.renderRepeatCount(part)}\n `\n : nothing}\n </section>\n `\n }\n\n /**\n * ── 몇 개가 되나 ──────────────────────────────────────────────────────────\n *\n * 저작 화면은 축과 피치만 받고 있었다. 형식이 가진 반복 설정이 그 둘뿐이므로 편집할 것은\n * 다 있었는데, **결과인 개수를 어디에도 표시하지 않았다.** 피치 300 을 적은 저작자가 몇 개가\n * 되는지 알려면 미리보기를 세어야 했다.\n *\n * 개수는 그리는 쪽이 쓰는 `repeatPlan` 을 그대로 불러 얻는다. 여기서 다시 계산하면 화면이\n * 말하는 개수와 실제로 그려지는 개수가 달라진다.\n *\n * ## 두 가지 반복이 있다\n *\n * 컨베이어의 롤러는 길어지면 **개수가 늘고** 한 칸 간격은 그대로다. 셀트레이의 셀은 커지면\n * **셀이 커지고** 개수는 그대로다 — 몇 셀짜리 트레이냐는 화면 크기와 무관하다. 그 갈림을\n * 이 축의 anchor 가 정하는데 화면에는 그 말이 없었다. 그래서 어느 쪽인지 함께 적는다.\n */\n private renderRepeatCount(part: FigurePart) {\n const draft = this.draft\n if (!draft) return nothing\n\n const base = { x: draft.width, y: draft.depth, z: draft.height }\n const plan = repeatPlanOfPart(part, { x: 1, y: 1, z: 1 }, base)\n if (!plan) return nothing\n\n const grows = anchorOfPart(part, base, plan.axis) !== 'scale'\n\n return html`\n <div field>\n <label>${i18next.t('figure.label.repeat-count')}</label>\n <p quiet>\n ${i18next.t('figure.text.repeat-count-now', { count: plan.count })}\n ${plan.count >= REPEAT_LIMIT\n ? html`<br />${i18next.t('figure.text.repeat-count-capped', { limit: REPEAT_LIMIT })}`\n : ''}\n <br />${i18next.t(grows ? 'figure.text.repeat-grows-with-instance' : 'figure.text.repeat-keeps-count')}\n </p>\n </div>\n `\n }\n\n /**\n * ── 어느 면을 붙잡나 ──────────────────────────────────────────────────────\n *\n * **이 자리가 없었다.** 형식은 축마다 `anchor` 를 지고 다니고(`figure-source.ts` 가 왕복시킨다),\n * 발행 게이트는 「valve-wheel 의 anchor.z 를 max 로 두라」고 사유까지 말하는데, 저작면에는 그\n * 값을 바꿀 자리가 없었다 — 고치라는 말만 있고 고칠 문이 없었다.\n *\n * ## 「자동」이 기본이다\n *\n * 형식에서 `anchor` 는 적지 않아도 된다. 안 적으면 **가까운 면**을 붙잡고, 표본 열넷 중 어느\n * 것도 이 값을 적지 않고 제대로 선다. 그래서 축마다 첫 항목이 「자동」이고, 지금 자동이 무엇으로\n * 읽히는지를 **함께 보여 준다** — 그것을 모르면 무엇을 바꾸는지 모른 채 고르게 된다.\n *\n * 자동이 무엇인지는 형식에게 묻는다(`anchorOfPart`). 여기서 「위 절반이면 max」를 다시 세면\n * 화면이 말하는 면과 실제로 그려지는 면이 갈린다.\n *\n * ## `scale` 인 부품에도 보여 준다\n *\n * 처음에는 감췄다 — 형식 주석에 「`sizing` 이 `scale` 인 부품에는 뜻이 없다」고 적혀 있어서다.\n * **그 문장은 기본값에 대한 것이다.** `anchorOf` 는 적힌 값을 가장 먼저 보고(`if (said) return\n * said`), 그 뒤에야 `sizing` 을 본다. 즉 `scale` 부품에도 면을 적으면 그것이 이긴다.\n *\n * 감춰 두면 어긋남이 난다: 발행 게이트가 막는 MIXER 의 `valve-wheel` 이 `scale` 부품이고,\n * 게이트의 사유는 「그 부품의 anchor.z 를 max 로 두라」다. 고치라는 자리에 문을 닫아 둔 셈이었다.\n *\n * 대신 `scale` 일 때는 자동이 무엇인지가 「함께 커진다」로 보이므로, 저작자가 그것을 바꾸는\n * 것임을 알 수 있다.\n */\n /**\n * ── 도움말은 자리를 먹지 않는다 ─────────────────────────────────────────────\n *\n * 설명 문단이 열 줄 있었고 그 줄들이 패널 높이의 상당 부분이었다. 속성은 앞으로 더 늘어난다\n * (능력 · 포트 · 애니메이션 · 기준정보) — 늘 보이는 설명은 그 자리를 먼저 먹는다.\n *\n * 그래서 물음표로 접는다. **지우지 않는다** — 그 문장들은 「이걸 고르면 무엇이 달라지나」를\n * 말하는 것이고, 없으면 닫힌 목록을 고를 수 없다. 한 번 읽으면 되는 것이라 접어 두는 것이 맞다.\n *\n * 편 것은 **부품을 바꿔도 그대로 둔다**(`opened` 는 부품에 딸린 상태가 아니다). 같은 것을\n * 계속 고치는 사람이 매번 다시 펴야 하면 접은 것이 되레 방해가 된다.\n */\n private help(key: string) {\n return html`${this.helpButton(key)}${this.helpHint(key)}`\n }\n\n private helpButton(key: string) {\n const shown = this.opened.has(key)\n\n return html`\n <button\n help\n aria-label=${i18next.t('figure.label.help')}\n aria-expanded=${shown ? 'true' : 'false'}\n @click=${() => {\n shown ? this.opened.delete(key) : this.opened.add(key)\n this.requestUpdate()\n }}\n >\n <md-icon>${shown ? 'help' : 'help_outline'}</md-icon>\n </button>\n `\n }\n\n private helpHint(key: string) {\n return this.opened.has(key) ? html`<p hint>${i18next.t(key)}</p>` : nothing\n }\n\n private renderAnchors(part: FigurePart) {\n const base = this.draft\n ? { x: this.draft.width, y: this.draft.height, z: this.draft.depth }\n : { x: 0, y: 0, z: 0 }\n const nameOf = (rule: string, axis: Axis) =>\n `${i18next.t(rule === 'min' || rule === 'max' ? `figure.label.anchor-${rule}-${axis}` : `figure.label.anchor-${rule}`)} (${rule})`\n const setAnchor = (axis: Axis, rule?: AnchorRule) => {\n const anchor = { ...(part.anchor ?? {}) }\n /* 「자동」은 칸을 지우는 것이다 — 빈 값을 넣어 두면 형식이 그것을 값으로 읽는다. */\n if (rule) anchor[axis] = rule\n else delete anchor[axis]\n this.put({ anchor: Object.keys(anchor).length ? anchor : undefined })\n }\n const shortNameOf = (rule: AnchorRule | 'auto', axis: Axis) => {\n if (rule === 'auto') return i18next.t('figure.label.anchor-choice-auto')\n if (rule === 'min' || rule === 'max') return i18next.t(`figure.label.anchor-choice-${rule}-${axis}`)\n return i18next.t(`figure.label.anchor-choice-${rule}`)\n }\n\n return html`\n <div field>\n <!-- 저장 키(anchor)는 남기되, 사용자는 축마다 어느 면을 기준으로 삼는지 읽는다. -->\n <label>${i18next.t('figure.label.anchor')} (anchor)${this.help('figure.text.anchor-explained')}</label>\n <div anchor-rows>\n ${(['x', 'y', 'z'] as const).map(axis => {\n const said = part.anchor?.[axis]\n const auto = anchorOfPart(part, base, axis)\n\n /*\n **면 이름은 축마다 다르다.** 처음에는 min 을 「아래·왼쪽·뒤 면」으로 한 줄에 다 적었는데,\n x 줄에서 「아래」와 「뒤」는 관계가 없어 읽는 사람이 무엇을 고르는지 몰랐다.\n\n 그리고 이름씨가 아니라 **동작**으로 적는다 — 「오른쪽 면」이 아니라 「오른쪽 면에 붙어\n 있는다」다. 「anchor.x 를 max 로 둔다」가 무슨 뜻인지 모르겠다는 말이 그 자리에서 나왔다.\n */\n /*\n **값 이름을 함께 보인다.** 동작만 적었더니(「오른쪽 면에 붙어 있는다») 발행 게이트가\n 「anchor.x 를 max 로 두라」고 말할 때 화면에서 그 낱말을 찾을 수 없었다 — 사용자가\n 그 자리에서 지적했다.\n\n `max` 는 정본에 저장되는 이름이고 서버 오류·형식 문서·게이트 사유가 다 그 이름을 쓴다.\n 숨기면 안 되는 종류다. 동작이 먼저고 이름이 괄호로 따라온다.\n */\n return html`\n <div anchor-row>\n <label>${axis.toUpperCase()}축</label>\n <div anchor-options>\n ${(['auto', 'scale', 'min', 'center', 'max', 'span'] as const).map(rule => {\n const selected = rule === 'auto' ? !said : rule === said\n const fullName = rule === 'auto' ? i18next.t('figure.label.anchor-auto', { rule: nameOf(auto, axis) }) : nameOf(rule, axis)\n return html`\n <button\n anchor-choice\n ?on=${selected}\n aria-pressed=${selected ? 'true' : 'false'}\n title=${fullName}\n @click=${() => setAnchor(axis, rule === 'auto' ? undefined : rule)}\n >${shortNameOf(rule, axis)}</button>\n `\n })}\n </div>\n <details anchor-advanced>\n <summary>${i18next.t('figure.label.anchor-advanced')}</summary>\n <div anchor-options>\n ${ANCHOR_RULES.filter(rule => rule.startsWith('aspect-')).map(rule => html`\n <button\n anchor-choice\n ?on=${rule === said}\n aria-pressed=${rule === said ? 'true' : 'false'}\n title=${nameOf(rule, axis)}\n @click=${() => setAnchor(axis, rule)}\n >${i18next.t(`figure.label.anchor-choice-${rule}`)}</button>\n `)}\n </div>\n </details>\n </div>\n `\n })}\n </div>\n </div>\n `\n }\n\n /**\n * 숫자 칸.\n *\n * 빈 칸이나 글자를 넣었을 때 0 으로 바꿔치지 않는다 — 저작자가 지우는 중일 수 있고,\n * 조용히 0 이 되면 부품이 사라진 것처럼 보인다. 숫자가 아니면 그냥 안 받는다.\n */\n private number(label: string, value: number, set: (value: number) => void, min?: number) {\n return html`\n <div cell>\n ${label ? html`<span axis>${label}</span>` : nothing}\n <input\n type=\"number\"\n .value=${String(value)}\n min=${min ?? nothing}\n @change=${(e: Event) => {\n const raw = (e.target as HTMLInputElement).value\n const parsed = Number(raw)\n if (raw === '' || !Number.isFinite(parsed)) {\n // 되돌려 놓는다. 안 그러면 화면과 정본이 갈린다.\n ;(e.target as HTMLInputElement).value = String(value)\n return\n }\n set(parsed)\n }}\n />\n </div>\n `\n }\n\n /**\n * 도형을 바꾼다.\n *\n * 도형이 바뀌면 그 도형에 뜻 없는 값이 남는다. 남겨 두면 검증이 「무시됨」 위반을\n * 내므로 여기서 걷는다.\n */\n private changePrimitive(primitive: PrimitiveKind) {\n this.change(part => {\n const next: FigurePart = { ...part, primitive }\n if (!SEGMENTED_PRIMITIVES.includes(primitive)) delete next.segments\n if (!EXTRUDE_PRIMITIVES.includes(primitive)) delete next.shape\n if (SEGMENTED_PRIMITIVES.includes(primitive) && next.segments === undefined) {\n next.segments = SEGMENT_PRESETS[1]\n }\n return next\n })\n }\n\n private put(patch: Partial<FigurePart>) {\n this.change(part => ({ ...part, ...patch }))\n }\n\n private putTransform(patch: Record<string, unknown>) {\n this.change(part => ({ ...part, transform: { ...part.transform, ...patch } as FigurePart['transform'] }))\n }\n\n /**\n * 발광 세기를 고친다. **0 이면 블록을 통째로 지운다.**\n *\n * 세기 0 인 발광을 남겨 두면 「안 빛나는데 램프인 것」이 되어, 재질 키가 갈려 묶음이\n * 헛되이 하나 는다. 0 은 「램프가 아니다」로 읽는 것이 맞다.\n *\n * `Number(value) || undefined` 를 쓰면 안 된다 — 0 이 falsy 라 변경으로 안 잡혀 옛\n * 값이 그대로 남는다. 실제로 램프가 안 꺼졌다.\n */\n private putEmissive(intensity: number) {\n // `chosen` 은 씬 모델이다. 형식으로 옮긴 값은 `partToFigure` 이 만든 것이라 여기서 다시 꺼낸다.\n const glow = this.draft && this.chosen ? partToFigure(this.chosen, this.draft).material?.emissive : undefined\n\n this.putMaterial({ emissive: intensity > 0 ? { ...glow, intensity } : undefined })\n }\n\n private putMaterial(patch: Record<string, unknown>) {\n this.change(part => ({\n ...part,\n material: { ...(part.material ?? {}), ...patch } as FigurePart['material']\n }))\n }\n\n /**\n * 수정한 것을 씬 모델로 되돌려 알린다.\n *\n * 사용자는 X·Y·Z 로 수정하고, 모델러 페이지는 씬 모델을 갖는다. 그 사이를 여기서\n * 잇는다 — 변환해서 수정하고 다시 되돌린다. **왕복 변환이 무손실이라야** 위치 하나\n * 고쳤을 뿐인데 회전값이 조금씩 밀리는 일이 없다(`fold.test.ts` 가 이를 확인한다).\n */\n private change(edit: (part: FigurePart) => FigurePart) {\n const part = this.chosen\n const draft = this.draft\n if (!part || !draft) return\n\n this.emit(partFromFigure(edit(partToFigure(part, draft)), draft))\n }\n\n /** 정밀 도구를 시킨다. 정본은 저작면이 갖는다 — 여기서는 알리기만 한다. */\n private ask(what: string, axis: string) {\n this.dispatchEvent(\n new CustomEvent('part-tool', {\n detail: { index: this.selected, tool: what, axis },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private emit(part: PartModel) {\n this.dispatchEvent(\n new CustomEvent('update-part', { detail: { index: this.selected, part }, bubbles: true, composed: true })\n )\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"figure-inspector.js","sourceRoot":"","sources":["../../client/modeller/figure-inspector.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport {\n EXTRUDE_PRIMITIVES,\n MATERIAL_PRESETS,\n PRIMITIVE_KINDS,\n SEGMENTED_PRIMITIVES,\n SEGMENT_PRESETS,\n SIZING_RULES,\n FIGURE_PLACEMENTS,\n trianglesOf\n} from '@hatiolab/figure-model'\nimport { ANCHOR_RULES, PART_ROLES, REPEAT_LIMIT, anchorOfPart, repeatPlanOfPart } from '@hatiolab/figure-model'\nimport type { AnchorRule, Axis, FigurePart, FigurePlacement, MaterialPreset, PartRole, PrimitiveKind } from '@hatiolab/figure-model'\nimport { activePalette } from '@hatiolab/things-scene'\n\nimport { partToFigure, partFromFigure } from './figure-source.js'\nimport type { FigureDraft, PartModel } from './figure-source.js'\nimport { canMirror } from './part-edits.js'\n\n/**\n * 고른 부품의 속성.\n *\n * ## 여기가 사용자 용어로 보여 주는 자리다\n *\n * 모델러 페이지가 갖고 있는 것은 **씬 모델**이다 — 기준 상자 위에 부품 컴포넌트가 놓인 상태.\n * 그런데 사용자에게 보여야 하는 것은 씬 속성 이름(`left`·`top`·`zPos`)이 아니라\n * **X·Y·Z** 다. 다른 3D 도구가 모두 그렇게 부르고, 위치도 부품의 **중심**으로 읽는다.\n *\n * 그래서 이 패널은 받은 부품을 FigureSource 형태로 **변환해서 보여 주고, 수정한 것을\n * 다시 씬 모델로 되돌려 알린다.** 변환은 `figure-source.ts` 가 한다 — 여기서 축을 다시 계산하지\n * 않는다.\n *\n * ## 있는 것만 보여 준다\n *\n * 분할 수는 곡면이 있는 도형에만, 모서리 굴림은 단면을 뽑는 도형에만 뜻이 있다.\n * 뜻 없는 칸을 회색으로 늘어놓으면 저작자가 「왜 안 먹지」를 고민하게 된다 —\n * 아예 내지 않는다.\n *\n * ## 이름은 저장되는 식별자다\n *\n * `FigurePart.name` 은 바인딩·애니메이션·슬롯이 가리키는 이름이다. 고치면 배치된\n * 인스턴스의 바인딩이 **조용히** 끊긴다. 그래서 이름 칸에만 경고를 붙인다.\n *\n * ## 「상태 색이 붙는 자리」를 걷었다\n *\n * `FigurePart.materialSlot`(당시 이름은 `slot`) 을 고르는 칸이 있었는데 **아무 일도 하지 않았다.** things-scene 의\n * figure 런타임이 `slot` 을 읽지 않는다 — 선언 한 줄이 전부다. 되는 줄 알고 만들었고,\n * 확인하지 않은 것이 잘못이다.\n *\n * 그리고 되게 만들더라도 이 모양이 아니다. 상태 색은 특별한 것이 아니라 **속성 하나**이고,\n * 툴팁·라벨·표시 여부와 같은 자리에서 다뤄져야 한다. 그 설계가 정해지면(figure-model#2)\n * 이 자리에 **속성 선언과 부품 바인딩** 편집기가 들어온다.\n *\n * 그때까지 빈 약속을 두지 않는다. 아무 일도 안 하는 칸은 없느니만 못하다.\n *\n * ## 고치지 않는다\n *\n * 정본 한 벌은 저작면이 갖는다. 여기서는 바뀐 부품을 알릴 뿐이다.\n */\n@customElement('figure-inspector')\nexport class FigureInspector extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n --figure-control-height: 30px;\n overflow-y: auto;\n background-color: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n /*\n 이 패널의 본문 크기를 여기서 한 번 정한다.\n\n 전에는 규칙마다 따로 적었다. 그러면 빠뜨린 규칙이 본문 기본 크기(1rem)를\n 물려받아 그 줄만 제목처럼 커진다. 오류가 아니라서 화면을 띄워 봐야 안다.\n 이 파일에서만 두 번 났다.\n\n form 요소(input · select · button)는 글꼴을 물려받지 않으므로 각자 적어야 한다.\n 그것 말고는 여기 값을 따른다.\n */\n font-size: 0.76rem;\n }\n\n /*\n ── 자리를 아끼는 판 ──────────────────────────────────────────────────────\n 속성은 앞으로 더 늘어난다(능력 · 포트 · 애니메이션 · 기준정보). 지금 여백을 그대로 두면\n 그것들이 들어올 자리가 없다. 절 여백을 6/10 으로, 제목 아래를 4px 로 줄였다 — 글자 크기와\n 칸 높이는 건드리지 않는다(작게 만드는 것이 아니라 **빈 곳을 줄이는 것**이다).\n */\n section {\n padding: 8px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n /* 입력 행은 촘촘하게, 서로 다른 편집 의도 사이에는 호흡을 둔다. */\n section + section {\n margin-top: 6px;\n }\n section[collapsible] {\n padding: 0;\n }\n details[group] > summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: 34px;\n padding: 0 10px;\n cursor: pointer;\n list-style: none;\n }\n details[group] > summary::-webkit-details-marker {\n display: none;\n }\n details[group] > summary h3 {\n margin: 0;\n }\n details[group] > summary md-icon {\n --md-icon-size: 16px;\n color: var(--md-sys-color-on-surface-variant);\n }\n details[group] > div[group-content] {\n padding: 4px 10px 8px;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n h3 {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0 0 4px 0;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /* 자산 메타데이터는 부품을 고친다는 현재 문맥에서는 요약으로만 둔다. */\n details[asset] {\n margin: -1px 0;\n }\n details[asset] summary {\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 26px;\n cursor: pointer;\n list-style: none;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n details[asset] summary::-webkit-details-marker {\n display: none;\n }\n details[asset] summary::before {\n content: '›';\n font-size: 1.1rem;\n line-height: 1;\n transition: transform 120ms ease;\n }\n details[asset][open] summary::before {\n transform: rotate(90deg);\n }\n details[asset] summary b {\n font-size: var(--ui-t-micro, 11px);\n letter-spacing: 0.04em;\n }\n details[asset] summary small {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 0.7rem;\n }\n\n label {\n display: flex;\n align-items: center;\n gap: 3px;\n margin-bottom: 2px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /*\n 도움말 물음표. **글자 줄을 하나 먹지 않는다** — 라벨·제목 줄의 남는 폭에 얹힌다.\n 아이콘만 두고 테두리를 안 두는 이유는 이것이 누를 것이라기보다 「더 있다」는 표시라서다.\n */\n button[help] {\n display: inline-flex;\n align-items: center;\n padding: 0;\n margin: 0;\n color: var(--md-sys-color-outline);\n background: none;\n border: none;\n cursor: pointer;\n }\n button[help] md-icon {\n --md-icon-size: 13px;\n font-size: 13px;\n }\n button[help]:hover,\n button[help][aria-expanded='true'] {\n color: var(--md-sys-color-primary);\n }\n\n /* 편 도움말. 라벨 아래 한 칸 들여 붙인다 — 무엇에 딸린 말인지 보이게. */\n p[hint] {\n margin: 2px 0 4px 0;\n padding-left: 6px;\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n border-left: 2px solid var(--md-sys-color-outline-variant);\n }\n\n input,\n select {\n width: 100%;\n min-height: var(--figure-control-height);\n box-sizing: border-box;\n padding: 4px 7px;\n font: var(--input-field-font, inherit);\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n input:focus,\n select:focus {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: -1px;\n }\n\n /*\n 숫자 칸 — 라벨을 칸 **안** 왼쪽에 둔다.\n\n 벡터 저작도구(Sketch·Figma)가 X·Y·W·H 를 이렇게 둔다. 라벨을 위에 얹으면 줄마다\n 두 줄을 먹어서, 자리·크기·회전 아홉 칸이 화면 절반을 차지한다. 안에 두면 한 줄에\n 셋이 들어가고 무엇이 무엇인지도 붙어 읽힌다.\n */\n div[cell] {\n display: flex;\n box-sizing: border-box;\n height: var(--figure-control-height);\n min-height: var(--figure-control-height);\n align-items: center;\n gap: 2px;\n padding-left: 6px;\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n div[cell]:focus-within {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: -1px;\n border-color: transparent;\n }\n div[cell] span[axis] {\n flex: none;\n font-size: 0.64rem;\n letter-spacing: 0.02em;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n div[cell] input {\n min-width: 0;\n min-height: 0;\n height: calc(var(--figure-control-height) - 2px);\n padding: 4px 6px 4px 2px;\n font-family: var(--mono-font, monospace);\n text-align: right;\n background: none;\n border: none;\n border-radius: 0;\n }\n div[cell] input:focus {\n outline: none;\n }\n\n /* 축 셋을 한 줄에 — 좌표는 셋이 같이 읽혀야 한다 */\n div[triple] {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 4px;\n }\n div[field] + div[field],\n div[triple] + div[field],\n div[field] + div[triple],\n div[inline-field] + div[inline-field],\n div[inline-field] + div[field],\n div[field] + div[inline-field],\n div[vector-row] + div[vector-row],\n div[vector-row] + div[field],\n div[field] + div[vector-row] {\n /* 칸 사이 7 → 5. 칸 자체는 그대로 두고 사이만 좁힌다. */\n margin-top: 5px;\n }\n\n /* 짧은 라벨과 단일 값은 같은 줄에서 읽는다. 복잡한 선택기에는 쓰지 않는다. */\n div[inline-field] {\n display: grid;\n /* 같은 편집 밀도 안에서는 입력 시작선을 맞춘다. 라벨 길이가 칸 폭을 흔들면 안 된다. */\n grid-template-columns: 64px minmax(0, 1fr);\n align-items: center;\n gap: 7px;\n }\n div[inline-field] > label {\n margin: 0;\n white-space: nowrap;\n }\n /* 한 줄 입력의 설명은 입력칸을 밀어내지 않고 바로 아래 전폭에서 읽는다. */\n div[inline-field] > p[hint] {\n grid-column: 1 / -1;\n margin: -2px 0 1px;\n }\n div[inline-field] > code[fixed] {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* X·Y·Z는 함께 비교하는 값이므로, 묶음 제목만 왼쪽에 두고 입력은 한 행에 둔다. */\n div[vector-row] {\n display: grid;\n grid-template-columns: 64px minmax(0, 1fr);\n align-items: center;\n gap: 7px;\n }\n div[vector-row] > label {\n margin: 0;\n white-space: nowrap;\n }\n div[vector-row] > p[hint] {\n grid-column: 1 / -1;\n margin: -2px 0 1px;\n }\n div[sizing-choices] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 5px;\n }\n button[sizing-choice] {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n min-height: 52px;\n padding: 7px 8px;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 7px;\n text-align: left;\n cursor: pointer;\n }\n button[sizing-choice][on] {\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n button[sizing-choice] b {\n font-size: 0.74rem;\n }\n button[sizing-choice] small {\n font-size: 0.64rem;\n line-height: 1.35;\n opacity: 0.8;\n }\n div[anchor-rows] {\n display: grid;\n gap: 6px;\n }\n div[anchor-row] {\n display: grid;\n grid-template-columns: 46px minmax(0, 1fr);\n align-items: start;\n gap: 8px;\n padding: 6px 7px;\n background: var(--md-sys-color-surface-container);\n border-radius: 7px;\n }\n div[anchor-row] label {\n margin: 0;\n font-size: 0.7rem;\n font-weight: 700;\n }\n div[anchor-options] {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n }\n button[anchor-choice] {\n min-width: 0;\n height: var(--figure-control-height);\n box-sizing: border-box;\n padding: 4px 3px;\n overflow: hidden;\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n font-size: 0.63rem;\n line-height: 1.2;\n text-overflow: ellipsis;\n white-space: nowrap;\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 5px;\n cursor: pointer;\n }\n button[anchor-choice][on] {\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n details[anchor-advanced] {\n grid-column: 2;\n margin-top: 4px;\n }\n details[anchor-advanced] summary {\n cursor: pointer;\n font-size: 0.62rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n details[anchor-advanced] div[anchor-options] {\n margin-top: 4px;\n }\n div[palette-meaning] {\n display: grid;\n gap: 2px;\n margin-top: 7px;\n padding: 6px 8px;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container);\n border-radius: 7px;\n }\n div[palette-meaning] b {\n font-size: 0.72rem;\n }\n div[palette-meaning] small {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.64rem;\n line-height: 1.35;\n }\n\n /*\n 정밀 제작 도구.\n\n 도면 편집기는 자산을 배치하는 곳이라 대충 놓아도 되지만, 여기는 자산을 만드는\n 곳이다. 「몸통 한가운데」·「바닥에 붙이기」·「반대쪽에도 하나」를 손으로 계산하면\n 늘 조금씩 어긋나고, 기준 치수를 바꾸면 전부 다시 해야 한다.\n */\n div[tools] {\n display: grid;\n grid-template-columns: repeat(6, minmax(0, 1fr));\n gap: 4px;\n }\n div[tools] button {\n display: grid;\n place-items: center;\n min-width: 0;\n height: var(--figure-control-height);\n box-sizing: border-box;\n padding: 0;\n font: var(--label-font, inherit);\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n div[tools] button md-icon {\n font-size: 17px;\n }\n div[tools] button[disabled] {\n opacity: 0.35;\n cursor: default;\n }\n div[tools] button:hover:not([disabled]) {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n\n /* 지금 어느 축이 늘어나나 — 크기를 고치면 바뀔 수 있어서 값으로 적는다 */\n p[axis-now] {\n margin: 4px 0 0 0;\n font-size: 0.7rem;\n color: var(--md-sys-color-primary);\n }\n /* 이 선택이 무엇을 무는지 — 성숙도의 묶음 수에 그대로 잡힌다 */\n p[cost] {\n margin: 4px 0 0 0;\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /* 이름을 바꾸면 무엇이 끊기는지 말한다 */\n p[warn] {\n margin: 4px 0 0 0;\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-tertiary);\n }\n\n /* 분할 수 프리셋 — 자유 입력보다 이 셋을 권한다 */\n div[presets] {\n display: flex;\n gap: 4px;\n margin-top: 4px;\n }\n div[presets] button {\n flex: 1;\n padding: 3px 0;\n font: var(--label-font, inherit);\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n div[presets] button[on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n\n /*\n 분할 수 선택 — 모양과 삼각형 수를 **함께** 보여 준다.\n\n 숫자만 늘어놓으면 8 과 16 이 무엇이 다른지 알 수 없다. 로우폴리에서 이 선택이\n 곧 「몇 개까지 편하게 놓을 수 있나」를 정하므로, 각지는 정도와 그 값이 무는\n 삼각형 수를 한자리에서 보아야 한다.\n\n 실루엣은 지어낸 그림이 아니다 — 곡면 프리미티브를 위에서 본 윤곽이 정확히\n 정n각형이다. 삼각형 수는 화면이 셈하지 않고 figure-model 의 trianglesOf 가 낸다.\n */\n div[segments] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 4px;\n }\n /* 원 유지 · 끄기 — 고르는 값이라 분할 수와 같은 모양으로 둔다 */\n div[choices] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 4px;\n }\n button[choice] {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 1px;\n padding: 6px 8px;\n text-align: left;\n font: var(--label-font, inherit);\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[choice]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n button[choice][on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n button[choice] small {\n opacity: 0.75;\n }\n button[seg] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1px;\n padding: 6px 2px 4px 2px;\n font: var(--label-font, inherit);\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[seg]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n button[seg][on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n button[seg] svg {\n display: block;\n /* 작으면 12 와 16 이 둘 다 동그라미로 보인다 — 각이 읽힐 만큼은 키운다 */\n width: 32px;\n height: 32px;\n }\n button[seg] svg polygon {\n fill: var(--md-sys-color-surface-container-highest);\n stroke: currentColor;\n stroke-width: 1.1;\n stroke-linejoin: miter;\n }\n button[seg][on] svg polygon {\n fill: var(--md-sys-color-surface-container-lowest);\n }\n button[seg] b {\n font-size: 0.8rem;\n font-weight: 700;\n line-height: 1.1;\n }\n button[seg] small {\n font-size: 0.62rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n button[seg][on] small {\n color: inherit;\n }\n /* 성능·실루엣 검증을 마친 프리셋만 고른다. */\n details[free] {\n margin-top: 6px;\n }\n details[free] summary {\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n cursor: pointer;\n }\n details[free] div[cell] {\n margin-top: 4px;\n }\n\n /*\n 색 견본.\n\n 토큰을 글자로 받지 않는다. things-scene 의 팔레트 선언이 「여기 든 이름이 저작에서\n 고를 수 있는 전부이고, 자유색을 허용하지 않는 것이 감각적인 로우폴리의 첫 규칙」\n 이라고 못박고 있다. 글자 칸으로 두면 그 규칙이 화면에서 사라지고, 오타 하나로\n resolveToken 이 던진다.\n */\n div[swatches] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n }\n /*\n 두 무리의 경계. 「칠한 색」과 「켜진 색」이 한 줄에 섞이면 accent(주황)와\n status-warning(주황)이 나란히 놓여 무엇이 무엇인지 알 수 없다.\n */\n label[group] {\n margin-top: 8px;\n padding-top: 8px;\n font-size: var(--ui-t-micro, 11px);\n letter-spacing: 0.03em;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n\n /* 값이 옆에 붙어야 끌면서 얼마인지 안다 */\n div[slider] {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n div[slider] input[type='range'] {\n flex: 1;\n accent-color: var(--md-sys-color-primary);\n cursor: pointer;\n }\n div[slider] output {\n flex: none;\n width: 2.6em;\n text-align: right;\n font: var(--mono-font, ui-monospace, monospace);\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n button[swatch] {\n width: 26px;\n height: 26px;\n padding: 0;\n background-color: var(--swatch);\n border: 1px solid rgba(0, 0, 0, 0.16);\n border-radius: 50%;\n cursor: pointer;\n }\n button[swatch][on] {\n /* 고른 것은 테두리가 아니라 고리로 표시한다 — 색 자체를 가리지 않으려고 */\n box-shadow:\n 0 0 0 2px var(--md-sys-color-surface-container-lowest),\n 0 0 0 4px var(--md-sys-color-primary);\n }\n span[token] {\n display: block;\n margin-top: 6px;\n font-family: var(--mono-font, monospace);\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n label[toggle] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 7px 0 0 0;\n font-size: 0.74rem;\n color: var(--md-sys-color-on-surface);\n cursor: pointer;\n }\n label[toggle] input {\n width: auto;\n }\n div[role-choices] {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n }\n div[role-choices] label[toggle] {\n justify-content: center;\n min-height: 30px;\n margin: 0;\n padding: 2px 4px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n font-size: 0.68rem;\n text-align: center;\n }\n div[role-choices] label[toggle]:has(input:checked) {\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n\n p[quiet] {\n margin: 0;\n font-size: 0.76rem;\n line-height: 1.6;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /*\n 고칠 수 없는 값 — 입력칸처럼 보이지 않게 한다.\n\n 전에는 그냥 글자였고, 옆의 이름 칸과 나란히 있으니 눌러도 되는 것처럼 보였다. 자물쇠와\n 칸 없는 바탕이 「읽는 값」이라고 먼저 말한다.\n */\n code[fixed] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n border-radius: 6px;\n background-color: var(--md-sys-color-surface-container-high, rgba(0, 0, 0, 0.05));\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n font-family: var(--mono-font, monospace);\n font-size: 0.76rem;\n }\n\n code[fixed] md-icon {\n --md-icon-size: 13px;\n opacity: 0.7;\n }\n `\n ]\n\n /** 도형의 DB 식별자. 비어 있으면 미저장 신규 모델이다. */\n @property({ type: String }) figureId = ''\n /** 도형의 표시 이름. 정본은 저작면이 들고, 여기서는 보여 주고 바꾼다(`figure-rename` 으로 알린다). */\n @property({ type: String }) figureName = ''\n /** 분류 하나 — 팔레트에서 묶는 기준. */\n @property({ type: String }) figureCategory?: string\n /** 태그 여럿 — 분류로는 못 하는 찾기. */\n @property({ type: Array }) figureTags?: string[]\n\n /** 펴 둔 도움말. 부품을 바꿔도 유지된다 — 매번 다시 펴게 하지 않는다. */\n @state() private opened = new Set<string>()\n\n @property({ type: Object }) draft?: FigureDraft\n /** 초안 위의 부품들. 씬 모델 그대로다. */\n @property({ type: Array }) parts: PartModel[] = []\n @property({ type: Number }) selected = -1\n\n /** `part` 는 HTMLElement 의 속성(DOMTokenList)이다 — 가리면 형이 어긋난다. */\n private get chosen(): PartModel | undefined {\n return this.parts?.[this.selected]\n }\n\n render() {\n const part = this.chosen\n if (!part || !this.draft) {\n return html`\n ${this.renderFigure(false)}\n <section>\n <h3>${i18next.t('figure.label.properties')}</h3>\n <p quiet>${i18next.t('figure.text.select-a-part-to-edit')}</p>\n </section>\n `\n }\n\n // 여기부터는 사용자 용어다 — X·Y·Z, 위치는 부품 중심, 회전은 도(degree).\n const shown = partToFigure(part, this.draft)\n\n /*\n 역할을 먼저 묻는다.\n\n 자리(slot)로 지정한 부품은 **그려지지 않는다.** 그 사실을 모른 채 재질부터 고르면 안 쓰일\n 색을 고르게 된다. 그리고 자리는 도형이 능력을 질 때만 뜻이 있으므로, 능력이 없으면 이\n 묶음은 아예 나오지 않는다 — 해 둬도 아무 일도 안 일어나는 칸을 보여 주지 않는다.\n */\n return html`\n ${this.renderFigure(true)} ${this.renderIdentity(shown)} ${this.renderRole(shown)}\n ${this.renderPlacement(shown)} ${this.renderShape(shown)} ${this.renderMaterial(shown)}\n ${this.renderBehaviour(shown)}\n `\n }\n\n /**\n * ── 이 부품이 능력에서 맡는 자리 ────────────────────────────────────────────\n *\n * 「능력」 탭이 도형 전체가 무엇을 하는지 정하고, 어느 부품이 그것을 받쳐 주는지는 여기서\n * 정한다. **부품을 고치는 곳이 한 군데여야** 하기 때문이다 — 목록을 따로 두면 같은 부품을\n * 두 화면에서 고치게 되고, 저작자는 어느 쪽이 이기는지 매번 생각해야 한다.\n *\n * 칸 수는 `repeat` 가 있으면 받지 않는다. 반복하는 자리의 칸 수는 **인스턴스 크기**가 정한다 —\n * 컨베이어가 길어지면 실을 자리가 늘어나는 것이 그것이고, 여기 적은 값은 안 쓰인다.\n */\n private renderRole(part: FigurePart) {\n if (!this.draft?.capabilities?.length) return nothing\n\n const roles = part.capability?.roles ?? []\n const repeats = part.sizing === 'repeat' && !!part.repeat\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.role-in-capability')}${this.help('figure.text.anchors-are-not-drawn')}</h3>\n\n <div role-choices>\n ${(PART_ROLES as readonly PartRole[]).map(\n one => html`\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${live(roles.includes(one))}\n @change=${(e: Event) => this.toggleRole(one, (e.target as HTMLInputElement).checked)}\n />\n ${i18next.t('figure.label.role-' + one)}\n </label>\n `\n )}\n </div>\n ${roles.length === 0 ? html`<p quiet>${i18next.t('figure.text.role-none-means-drawn')}</p>` : nothing}\n\n ${roles.includes('slot')\n ? html`\n <div field>\n <label>${i18next.t('figure.label.accepts')}${this.help('figure.text.accepts-explained')}</label>\n <input\n .value=${(part.capability?.accepts ?? []).join(', ')}\n placeholder=${i18next.t('figure.text.accepts-anything')}\n @change=${(e: Event) => this.setAccepts((e.target as HTMLInputElement).value)}\n />\n </div>\n <div field>\n <label>${i18next.t('figure.label.slot-capacity')}</label>\n ${repeats\n ? html`<p quiet>${i18next.t('figure.text.capacity-from-repeat')}</p>`\n : this.number('', part.capability?.capacity ?? 1, value => this.setCapacity(value), 1)}\n </div>\n `\n : nothing}\n </section>\n `\n }\n\n /**\n * 역할 하나를 켜고 끈다.\n *\n * **여럿을 함께 맡을 수 있다.** AGV 의 상판은 물건을 놓는 자리이면서 그 자리로 물건이\n * 들어오고 나간다. 그래서 하나를 고르는 선택 상자가 아니라 확인 상자다.\n *\n * 마지막 역할을 끄면 `capability` 를 통째로 지운다. 빈 배열을 남기면 형식이 오류로 보고,\n * 저작자가 보기에는 아무 역할도 없는 평범한 부품인데 발행이 막힌다.\n */\n private toggleRole(role: PartRole, on: boolean) {\n this.change(part => {\n const roles = (part.capability?.roles ?? []).filter(one => one !== role)\n if (on) roles.push(role)\n\n if (roles.length === 0) {\n const { capability, ...rest } = part\n return rest as FigurePart\n }\n\n // 형식이 적은 순서를 지킨다 — 켠 순서대로 두면 같은 뜻이 다른 값으로 저장된다.\n const ordered = (PART_ROLES as readonly PartRole[]).filter(one => roles.includes(one))\n return { ...part, capability: { ...part.capability, roles: ordered } }\n })\n }\n\n private setAccepts(raw: string) {\n const accepts = raw\n .split(',')\n .map(one => one.trim())\n .filter(one => one.length > 0)\n\n this.change(part => {\n const spec = { ...part.capability, roles: part.capability?.roles ?? ['slot' as PartRole] }\n // 빈 목록은 제한하지 않는다는 뜻이다. 빈 배열을 저장하면 아무것도 못 받는 것으로 읽힌다.\n if (accepts.length > 0) spec.accepts = accepts\n else delete spec.accepts\n return { ...part, capability: spec as NonNullable<FigurePart['capability']> }\n })\n }\n\n private setCapacity(value: number) {\n this.change(part => {\n const spec = { ...part.capability, roles: part.capability?.roles ?? ['slot' as PartRole] }\n if (Number.isFinite(value) && value >= 1) spec.capacity = Math.floor(value)\n else delete spec.capacity\n return { ...part, capability: spec as NonNullable<FigurePart['capability']> }\n })\n }\n\n /**\n * 도형 자체 — 이름과 타입.\n *\n * ## 왜 머리줄이 아니라 여기인가\n *\n * 저작면 위에 줄을 하나 더 두고 이름 칸을 놓았었다. 앱에서는 셸이 페이지 제목으로 그 이름을\n * 이미 그리므로 같은 글자가 두 번 나오고, 캔버스가 주인공인 화면에서 그 줄만큼 높이를 잃었다.\n * 속성 패널은 「이 도형이 무엇인가」를 다루는 자리이므로 이름도 여기 산다.\n *\n * ## 타입은 읽기만 한다\n *\n * 저장되는 식별자다. 배치된 도면이 이 이름으로 타입을 찾으므로 만든 뒤에는 못 바꾼다.\n */\n private renderFigure(compact = false) {\n const fields = html`\n <div inline-field>\n <label>${i18next.t('figure.label.name')}</label>\n <input\n .value=${this.figureName}\n placeholder=${i18next.t('figure.text.figure-display-name')}\n @change=${(e: Event) =>\n this.dispatchEvent(\n new CustomEvent('figure-rename', {\n detail: { name: (e.target as HTMLInputElement).value },\n bubbles: true,\n composed: true\n })\n )}\n />\n </div>\n <div inline-field>\n <label>${i18next.t('figure.label.figure-type')}${this.figureId ? this.helpButton('figure.text.type-name-cannot-change') : nothing}</label>\n ${!this.figureId\n ? html`<input\n .value=${this.draft?.figureType ?? ''}\n placeholder=${i18next.t('figure.text.figure-type-placeholder', { defaultValue: '도형 식별 타입 (예: OHT)' })}\n @change=${(e: Event) =>\n this.dispatchEvent(\n new CustomEvent('figure-type-change', {\n detail: { type: (e.target as HTMLInputElement).value.trim().toUpperCase() },\n bubbles: true,\n composed: true\n })\n )}\n />`\n : html`<code fixed title=${i18next.t('figure.text.type-name-cannot-change')}>\n <md-icon>lock</md-icon>${this.draft?.figureType ?? ''}\n </code>\n ${this.helpHint('figure.text.type-name-cannot-change')}`}\n </div>\n <!--\n 왜 못 고치는지 **적어 둔다.** 전에는 tooltip 에만 있었고, 그러면 눌러 보고 안 되는 것을\n 알게 된다 — 화면이 안 되는 이유를 말하지 않으면 사용자가 자기 실수라고 생각한다.\n -->\n\n <div inline-field>\n <label>${i18next.t('figure.label.figure-placement')}${this.helpButton('figure.text.placement-explained')}</label>\n <select\n .value=${this.draft?.placement ?? 'floor'}\n @change=${(e: Event) => this.setPlacement((e.target as HTMLSelectElement).value as FigurePlacement)}\n >\n <option value=\"floor\" ?selected=${(this.draft?.placement ?? 'floor') === 'floor'}>\n ${i18next.t('figure.label.placement-floor')}\n </option>\n <option value=\"ceiling\" ?selected=${this.draft?.placement === 'ceiling'}>\n ${i18next.t('figure.label.placement-ceiling')}\n </option>\n <option value=\"center\" ?selected=${this.draft?.placement === 'center'}>\n ${i18next.t('figure.label.placement-center')}\n </option>\n </select>\n ${this.helpHint('figure.text.placement-explained')}\n </div>\n\n <div inline-field>\n <label>${i18next.t('figure.label.category')}${this.helpButton('figure.text.category-explained')}</label>\n <input\n .value=${this.figureCategory ?? ''}\n placeholder=${i18next.t('figure.text.category-placeholder')}\n @change=${(e: Event) => this.tellCatalog({ category: (e.target as HTMLInputElement).value.trim() || undefined })}\n />\n ${this.helpHint('figure.text.category-explained')}\n </div>\n\n <div inline-field>\n <label>${i18next.t('figure.label.tags')}${this.helpButton('figure.text.tags-explained')}</label>\n <input\n .value=${(this.figureTags ?? []).join(', ')}\n placeholder=${i18next.t('figure.text.tags-placeholder')}\n @change=${(e: Event) => this.tellTags((e.target as HTMLInputElement).value)}\n />\n ${this.helpHint('figure.text.tags-explained')}\n </div>\n `\n\n if (compact) {\n return html`\n <section>\n <details asset>\n <summary>\n <b>${i18next.t('figure.label.figure')}</b>\n <small>${this.figureName || this.draft?.figureType || ''}</small>\n </summary>\n ${fields}\n </details>\n </section>\n `\n }\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.figure')}</h3>\n ${fields}\n </section>\n `\n }\n\n /**\n * 태그 줄을 목록으로.\n *\n * 쉼표로 가르고 `#` 은 떼어 낸다 — 사람은 `#반출` 이라고 적고, 저장되는 것은 `반출` 이다.\n * 겹친 것은 한 번만 남긴다. 빈 줄은 **빈 배열**이지 `undefined` 가 아니다 — 전부 지우려는\n * 저작자가 지울 방법을 잃지 않게.\n */\n private tellTags(raw: string) {\n const tags = [\n ...new Set(\n raw\n .split(',')\n .map(one => one.trim().replace(/^#+/, '').trim())\n .filter(one => one.length > 0)\n )\n ]\n\n this.tellCatalog({ tags })\n }\n\n /** 카탈로그 값이 바뀌었다고 알린다. 정본은 저작면이 든다 — 여기서는 알리기만 한다. */\n private tellCatalog(patch: { category?: string; tags?: string[] }) {\n this.dispatchEvent(new CustomEvent('figure-catalog', { detail: patch, bubbles: true, composed: true }))\n }\n\n /**\n * 3D 배치 기준면을 바꾼다 ('floor' | 'ceiling' | 'center').\n *\n * **부품은 건드리지 않는다.** 이 값은 「이 종류는 어느 면에 붙는가」를 말할 뿐이고,\n * 저작한 모양은 그것과 무관하다. 형식이 부품을 상자 중심에서 재므로, 배치가 달라도\n * `parts` 는 한 글자도 안 바뀐다.\n *\n * 한동안 여기서 `zPos` 를 다시 계산했다. 편집 캔버스가 이 값을 씬의 좌표 모드로\n * 흘려보내던 때의 일인데, 그러면 배치를 고를 때마다 저작자가 적어 둔 높이가 전부\n * 다른 숫자로 보였다 — 저장되는 것은 그대로인데. 좌표 모드는 이제 보기 설정이고\n * (`figure-view.ts`), 편집 캔버스는 늘 상자 기준이다.\n */\n private setPlacement(placement: FigurePlacement) {\n if (!this.draft) return\n const draft = { ...this.draft, placement }\n this.dispatchEvent(new CustomEvent('update-draft', { detail: { draft }, bubbles: true, composed: true }))\n }\n\n private renderIdentity(part: FigurePart) {\n return html`\n <section>\n <h3>${i18next.t('figure.label.part')}</h3>\n <div inline-field>\n <label>${i18next.t('figure.label.name')}${this.helpButton('figure.text.renaming-a-part-breaks-bindings')}</label>\n <input\n .value=${part.name}\n @change=${(e: Event) => this.put({ name: (e.target as HTMLInputElement).value })}\n />\n ${this.helpHint('figure.text.renaming-a-part-breaks-bindings')}\n </div>\n <div inline-field>\n <label>${i18next.t('figure.label.shape')}</label>\n <select @change=${(e: Event) => this.changePrimitive((e.target as HTMLSelectElement).value as PrimitiveKind)}>\n ${PRIMITIVE_KINDS.map(\n kind => html`\n <option value=${kind} ?selected=${kind === part.primitive}>\n ${i18next.t('figure.label.primitive-' + kind)}\n </option>\n `\n )}\n </select>\n </div>\n </section>\n `\n }\n\n private renderPlacement(part: FigurePart) {\n const t = part.transform\n const rotation = t.rotation ?? {}\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.placement')}</h3>\n\n <div vector-row>\n <label>${i18next.t('figure.label.part-position')}</label>\n <div triple>\n ${(['x', 'y', 'z'] as const).map(axis =>\n this.number(axis.toUpperCase(), t.position[axis], value =>\n this.putTransform({ position: { ...t.position, [axis]: value } })\n )\n )}\n </div>\n </div>\n\n <div vector-row>\n <label>${i18next.t('figure.label.part-size')}</label>\n <div triple>\n ${(['x', 'y', 'z'] as const).map(axis =>\n this.number(\n axis.toUpperCase(),\n t.size[axis],\n value => this.putTransform({ size: { ...t.size, [axis]: value } }),\n 1\n )\n )}\n </div>\n </div>\n\n <div vector-row>\n <label>${i18next.t('figure.label.rotation-degrees')}</label>\n <div triple>\n ${(['x', 'y', 'z'] as const).map(axis =>\n this.number(axis.toUpperCase(), rotation[axis] ?? 0, value =>\n this.putTransform({ rotation: { ...rotation, [axis]: value } })\n )\n )}\n </div>\n </div>\n\n <div vector-row>\n <label>${i18next.t('figure.label.tools')}${this.helpButton('figure.text.tools-explained')}</label>\n <div tools>\n <button\n aria-label=${i18next.t('figure.button.center-x')}\n title=${i18next.t('figure.text.center-on-x')}\n @click=${() => this.ask('center', 'x')}\n >\n <md-icon>horizontal_distribute</md-icon>\n </button>\n <button\n aria-label=${i18next.t('figure.button.center-z')}\n title=${i18next.t('figure.text.center-on-z')}\n @click=${() => this.ask('center', 'z')}\n >\n <md-icon>vertical_align_center</md-icon>\n </button>\n <button\n aria-label=${i18next.t('figure.button.to-floor')}\n title=${i18next.t('figure.text.put-on-floor')}\n @click=${() => this.ask('floor', 'y')}\n >\n <md-icon>vertical_align_bottom</md-icon>\n </button>\n ${(['x', 'z'] as const).map(axis => {\n // 이미 가운데면 대칭이 제자리다 — 겹친 부품이 조용히 생기지 않게 막는다\n const able = this.draft ? canMirror(this.draft, this.parts, this.selected, axis) : false\n return html`\n <button\n ?disabled=${!able}\n aria-label=${i18next.t('figure.button.mirror-' + axis)}\n title=${i18next.t(able ? 'figure.text.mirror-explained' : 'figure.text.mirror-would-overlap')}\n @click=${() => this.ask('mirror', axis)}\n >\n <md-icon>${axis === 'x' ? 'flip' : 'flip_camera_android'}</md-icon>\n </button>\n `\n })}\n <button\n aria-label=${i18next.t('figure.button.snap')}\n title=${i18next.t('figure.text.snap-explained')}\n @click=${() => this.ask('snap', 'x')}\n >\n <md-icon>grid_on</md-icon>\n </button>\n </div>\n ${this.helpHint('figure.text.tools-explained')}\n </div>\n </section>\n `\n }\n\n /** 분할 수와 모서리 굴림 — 해당하는 도형에만 낸다. */\n private renderShape(part: FigurePart) {\n const segmented = SEGMENTED_PRIMITIVES.includes(part.primitive)\n const extruded = EXTRUDE_PRIMITIVES.includes(part.primitive)\n if (!segmented && !extruded) return nothing\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.shape-detail')}</h3>\n ${segmented ? this.renderSegments(part) : nothing}\n ${segmented ? this.renderKeepRound(part) : nothing}\n ${extruded ? this.renderExtruded(part) : nothing}\n </section>\n `\n }\n\n /**\n * 민 단면 — 모서리와 **속을 파는 두께.**\n *\n * 벽 두께를 0 으로 두면 통짜다. 0 보다 크면 그만큼 안으로 들어온 테가 생기고, 그것이\n * 트레이 · 토트 · 통 · 케이스가 되는 자리다. 바닥은 비워 두면 벽과 같고, 0 으로 두면\n * 위아래가 다 뚫린 테두리가 된다.\n */\n private renderExtruded(part: FigurePart) {\n const hollow = part.shape?.hollow\n const wall = hollow?.wall ?? 0\n\n const reshape = (over: Record<string, unknown>) =>\n this.put({ shape: { ...(part.shape ?? {}), ...over } })\n\n return html`\n <div inline-field>\n <label>${i18next.t('figure.label.corner-round')}</label>\n ${this.number('', part.shape?.round ?? 0, value => reshape({ round: value }))}\n </div>\n <div inline-field>\n <label>${i18next.t('figure.label.hollow-wall')}</label>\n ${this.number('', wall, value =>\n reshape({ hollow: value > 0 ? { ...(hollow ?? {}), wall: value } : undefined })\n )}\n </div>\n ${wall > 0\n ? html`\n <div inline-field>\n <label>${i18next.t('figure.label.hollow-floor')}</label>\n ${this.number('', hollow?.floor ?? wall, value => reshape({ hollow: { ...hollow, wall, floor: value } }))}\n </div>\n `\n : nothing}\n `\n }\n\n /**\n * 분할 수 — 모양과 무게를 함께 고른다.\n *\n * 삼각형 수는 화면이 셈하지 않는다. `trianglesOf` 에 그 분할 수를 가진 부품을 주고\n * 물어본다 — 화면이 제 나름의 공식을 들면 figure-model 이 규칙을 바꿔도 화면만\n * 옛 답을 계속 낸다.\n */\n /**\n * 원통을 원통으로 둘까 — **고르는 값이고, 값을 치른다.**\n *\n * 인스턴스가 축마다 다른 배율을 받으면 원기둥이 타원 기둥이 된다. 지키려면 제 변환이\n * 있어야 하고, 합친 형상은 변환을 하나만 받으므로 지키는 곡면 부품 하나가 draw call\n * 하나다. 저작자가 그 대가를 모르고 고르면 안 되므로 그 자리에 적는다.\n */\n private renderKeepRound(part: FigurePart) {\n const on = part.keepRound !== false\n\n return html`\n <div field>\n <label>${i18next.t('figure.label.keep-round')}</label>\n <div choices>\n <button choice ?on=${on} @click=${() => this.put({ keepRound: undefined })}>\n <b>${i18next.t('figure.text.keep-round-on')}</b>\n <small>${i18next.t('figure.text.keep-round-cost')}</small>\n </button>\n <button choice ?on=${!on} @click=${() => this.put({ keepRound: false })}>\n <b>${i18next.t('figure.text.keep-round-off')}</b>\n <small>${i18next.t('figure.text.keep-round-merged')}</small>\n </button>\n </div>\n </div>\n `\n }\n\n private renderSegments(part: FigurePart) {\n const current = part.segments ?? SEGMENT_PRESETS[1]\n const common = SEGMENT_PRESETS.slice(0, 4)\n const advanced = SEGMENT_PRESETS.slice(4)\n\n return html`\n <div field>\n <label>${i18next.t('figure.label.segments')}</label>\n <div segments>\n ${common.map(preset => {\n const triangles = trianglesOf({ ...part, segments: preset })\n return html`\n <button seg ?on=${current === preset} @click=${() => this.put({ segments: preset })}>\n ${this.silhouette(preset)}\n <b>${preset}</b>\n <small>${i18next.t('figure.text.n-triangles', { count: triangles })}</small>\n </button>\n `\n })}\n </div>\n\n <details free ?open=${advanced.includes(current)}>\n <summary>${i18next.t('figure.text.more-segment-options')}</summary>\n <div presets>\n ${advanced.map(preset => html`\n <button ?on=${current === preset} @click=${() => this.put({ segments: preset })}>${preset}</button>\n `)}\n </div>\n </details>\n </div>\n `\n }\n\n /**\n * 분할 수의 실루엣.\n *\n * 곡면 프리미티브를 위에서 본 윤곽이 정확히 정n각형이다 — 원기둥의 단면도, 구의\n * 적도도. 그림을 지어내는 것이 아니라 실제로 만들어지는 모양이다.\n */\n private silhouette(segments: number) {\n const points: string[] = []\n for (let i = 0; i < segments; i++) {\n // 꼭짓점 하나가 위로 오게 -90도에서 시작한다\n const angle = -Math.PI / 2 + (i * 2 * Math.PI) / segments\n points.push(`${(16 + 13 * Math.cos(angle)).toFixed(2)},${(16 + 13 * Math.sin(angle)).toFixed(2)}`)\n }\n\n return svg`\n <svg viewBox=\"0 0 32 32\" aria-hidden=\"true\">\n <polygon points=${points.join(' ')}></polygon>\n </svg>\n `\n }\n\n /** 견본 한 줄. 무리마다 부른다. */\n private palettePurpose(token?: string) {\n const purposes: Record<string, string> = {\n 'palette.primary': 'primary',\n 'palette.accent': 'accent',\n 'palette.neutral': 'neutral',\n 'palette.dark': 'dark',\n 'palette.light': 'light',\n 'palette.surface': 'surface',\n 'palette.status-danger': 'danger',\n 'palette.status-warning': 'warning',\n 'palette.status-info': 'info',\n 'palette.status-success': 'success'\n }\n\n return i18next.t(`figure.label.palette-purpose-${purposes[token ?? ''] ?? 'registered'}`)\n }\n\n private swatches(entries: [string, string][], chosen?: string) {\n return entries.map(\n ([token, color]) => {\n const purpose = this.palettePurpose(token)\n return html`\n <button\n swatch\n ?on=${token === chosen}\n aria-label=${purpose}\n title=${purpose}\n style=\"--swatch:${color}\"\n @click=${() => this.putMaterial({ token })}\n ></button>\n `\n }\n )\n }\n\n private renderMaterial(part: FigurePart) {\n const material = part.material ?? {}\n const palette = activePalette() as Record<string, string>\n\n /*\n 두 무리로 나눠 보인다 — **칠한 색**과 **켜진 색**.\n\n `palette.status-*` 는 램프 · 표시등에 쓰라고 있는 색이다. 한 줄에 섞어 두면\n `accent`(주황)와 `status-warning`(주황)이 나란히 놓여 무엇이 무엇인지 알 수\n 없다. 값을 비틀어 벌리는 대신 **고르는 자리에서 경계를 긋는다.**\n */\n const entries = Object.entries(palette)\n const painted = entries.filter(([token]) => !token.startsWith('palette.status-'))\n const lit = entries.filter(([token]) => token.startsWith('palette.status-'))\n\n /** 자체발광. 없으면 안 빛나는 부품이다. */\n const glow = material.emissive\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.part-material')}</h3>\n\n <div field>\n <label>${i18next.t('figure.label.palette-role')}</label>\n <div swatches>\n ${this.swatches(painted, material.token)}\n </div>\n ${lit.length\n ? html`\n <label group>${i18next.t('figure.label.status-colors')}</label>\n <div swatches>\n ${this.swatches(lit, material.token)}\n </div>\n `\n : ''}\n <div palette-meaning>\n <b>${material.token ? this.palettePurpose(material.token) : i18next.t('figure.label.no-color-chosen')}</b>\n <small>${i18next.t('figure.text.palette-purpose-explained')}</small>\n </div>\n </div>\n\n <div field>\n <label>${i18next.t('figure.label.preset')}</label>\n <select\n @change=${(e: Event) => {\n const value = (e.target as HTMLSelectElement).value\n this.putMaterial({ preset: (value || undefined) as MaterialPreset | undefined })\n }}\n >\n <option value=\"\" ?selected=${!material.preset}>${i18next.t('figure.label.none')}</option>\n ${MATERIAL_PRESETS.map(\n preset => html`\n <option value=${preset} ?selected=${preset === material.preset}>\n ${i18next.t('figure.label.preset-' + preset)}\n </option>\n `\n )}\n </select>\n </div>\n\n <!-- 값이 없으면 각지게 그린다 — 형식이 그렇게 못 박았다. 체크상자도 그대로 보인다 -->\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${material.flatShading ?? true}\n @change=${(e: Event) => this.putMaterial({ flatShading: (e.target as HTMLInputElement).checked })}\n />\n ${i18next.t('figure.label.flat-shading')}\n </label>\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${!!material.transparent}\n @change=${(e: Event) => this.putMaterial({ transparent: (e.target as HTMLInputElement).checked })}\n />\n ${i18next.t('figure.label.transparent')}\n </label>\n\n <div field>\n <label>${i18next.t('figure.label.emissive')}${this.help('figure.text.emissive-explained')}</label>\n\n <div slider>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n .value=${String(glow?.intensity ?? 0)}\n @input=${(e: Event) => this.putEmissive(Number((e.target as HTMLInputElement).value))}\n />\n <output>${(glow?.intensity ?? 0).toFixed(2)}</output>\n </div>\n </div>\n\n ${glow\n ? html`\n <div field>\n <label>${i18next.t('figure.label.emissive-color')}</label>\n <div swatches>\n <button\n swatch\n same\n ?on=${!glow.token}\n title=${i18next.t('figure.label.same-as-off')}\n style=\"--swatch:${(material.token && palette[material.token]) || palette['palette.neutral']}\"\n @click=${() => this.putMaterial({ emissive: { ...glow, token: undefined } })}\n ></button>\n ${lit.map(\n ([token, color]) => html`\n <button\n swatch\n ?on=${token === glow.token}\n title=${token}\n style=\"--swatch:${color}\"\n @click=${() => this.putMaterial({ emissive: { ...glow, token } })}\n ></button>\n `\n )}\n </div>\n <span token>${glow.token ?? i18next.t('figure.label.same-as-off')}</span>\n </div>\n\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${!!glow.on}\n @change=${(e: Event) =>\n this.putMaterial({ emissive: { ...glow, on: (e.target as HTMLInputElement).checked } })}\n />\n ${i18next.t('figure.label.on-by-default')}\n </label>\n ${this.help('figure.text.on-by-default-explained')}\n `\n : ''}\n\n </section>\n `\n }\n\n /**\n * 인스턴스 크기가 바뀔 때 이 부품이 어떻게 변하나.\n *\n * ## 늘어나는 축은 아직 못 보여 준다\n *\n * `stretch` 는 형식에 축을 적는 자리가 없어서 그리는 쪽이 **가장 길게 뻗은 축**으로\n * 정한다. 그 답을 화면에 적어 주려면 규칙을 가진 쪽에 물어야 하는데, 그 함수\n * (`stretchAxisOf` · `stretchAxis`)가 아직 배포된 판에 없다.\n *\n * 화면이 제 나름대로 계산하지 않는다. 규칙이 두 벌이 되면 보이는 축과 실제로 늘어나는\n * 축이 갈리고, 그 어긋남은 화면을 믿게 만든 다음에 드러난다. 배포되면 잇는다.\n *\n * ## 값이 있는 것을 밝힌다\n *\n * `scale` 이 아닌 부품은 합쳐 그릴 수 없어 draw call 을 하나 더 쓴다. 그 비용이\n * 성숙도의 묶음 수에 그대로 잡힌다. 고르는 자리에서 미리 말해 준다.\n */\n private renderBehaviour(part: FigurePart) {\n const sizing = part.sizing ?? 'scale'\n const repeat = part.repeat ?? { axis: 'x' as Axis, pitch: 0 }\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.sizing-rule')}${this.help('figure.text.sizing-' + sizing + '-explained')}</h3>\n <div sizing-choices>\n ${SIZING_RULES.map(\n rule => html`\n <button sizing-choice ?on=${rule === sizing} @click=${() => this.put({ sizing: rule })}>\n <b>${i18next.t('figure.label.sizing-' + rule)}</b>\n <small>${i18next.t('figure.text.sizing-' + rule + '-explained')}</small>\n </button>\n `\n )}\n </div>\n ${sizing === 'scale'\n ? nothing\n : html`<p cost>${i18next.t('figure.text.sizing-costs-one-draw-call')}</p>`}\n\n ${this.renderAnchors(part)}\n\n ${sizing === 'repeat'\n ? html`\n <div inline-field>\n <label>${i18next.t('figure.label.repeat-axis')}</label>\n <select\n @change=${(e: Event) =>\n this.put({ repeat: { ...repeat, axis: (e.target as HTMLSelectElement).value as Axis } })}\n >\n ${(['x', 'y', 'z'] as const).map(\n axis => html`<option value=${axis} ?selected=${axis === repeat.axis}>${axis}</option>`\n )}\n </select>\n </div>\n <div inline-field>\n <label>${i18next.t('figure.label.repeat-pitch')}${this.helpButton('figure.text.repeat-pitch-explained')}</label>\n ${this.number('', repeat.pitch, value => this.put({ repeat: { ...repeat, pitch: value } }), 1)}\n ${this.helpHint('figure.text.repeat-pitch-explained')}\n </div>\n ${this.renderRepeatCount(part)}\n `\n : nothing}\n </section>\n `\n }\n\n /**\n * ── 몇 개가 되나 ──────────────────────────────────────────────────────────\n *\n * 저작 화면은 축과 피치만 받고 있었다. 형식이 가진 반복 설정이 그 둘뿐이므로 편집할 것은\n * 다 있었는데, **결과인 개수를 어디에도 표시하지 않았다.** 피치 300 을 적은 저작자가 몇 개가\n * 되는지 알려면 미리보기를 세어야 했다.\n *\n * 개수는 그리는 쪽이 쓰는 `repeatPlan` 을 그대로 불러 얻는다. 여기서 다시 계산하면 화면이\n * 말하는 개수와 실제로 그려지는 개수가 달라진다.\n *\n * ## 두 가지 반복이 있다\n *\n * 컨베이어의 롤러는 길어지면 **개수가 늘고** 한 칸 간격은 그대로다. 셀트레이의 셀은 커지면\n * **셀이 커지고** 개수는 그대로다 — 몇 셀짜리 트레이냐는 화면 크기와 무관하다. 그 갈림을\n * 이 축의 anchor 가 정하는데 화면에는 그 말이 없었다. 그래서 어느 쪽인지 함께 적는다.\n */\n private renderRepeatCount(part: FigurePart) {\n const draft = this.draft\n if (!draft) return nothing\n\n const base = { x: draft.width, y: draft.depth, z: draft.height }\n const plan = repeatPlanOfPart(part, { x: 1, y: 1, z: 1 }, base)\n if (!plan) return nothing\n\n const grows = anchorOfPart(part, base, plan.axis) !== 'scale'\n\n return html`\n <div field>\n <label>${i18next.t('figure.label.repeat-count')}</label>\n <p quiet>\n ${i18next.t('figure.text.repeat-count-now', { count: plan.count })}\n ${plan.count >= REPEAT_LIMIT\n ? html`<br />${i18next.t('figure.text.repeat-count-capped', { limit: REPEAT_LIMIT })}`\n : ''}\n <br />${i18next.t(grows ? 'figure.text.repeat-grows-with-instance' : 'figure.text.repeat-keeps-count')}\n </p>\n </div>\n `\n }\n\n /**\n * ── 어느 면을 붙잡나 ──────────────────────────────────────────────────────\n *\n * **이 자리가 없었다.** 형식은 축마다 `anchor` 를 지고 다니고(`figure-source.ts` 가 왕복시킨다),\n * 발행 게이트는 「valve-wheel 의 anchor.z 를 max 로 두라」고 사유까지 말하는데, 저작면에는 그\n * 값을 바꿀 자리가 없었다 — 고치라는 말만 있고 고칠 문이 없었다.\n *\n * ## 「자동」이 기본이다\n *\n * 형식에서 `anchor` 는 적지 않아도 된다. 안 적으면 **가까운 면**을 붙잡고, 표본 열넷 중 어느\n * 것도 이 값을 적지 않고 제대로 선다. 그래서 축마다 첫 항목이 「자동」이고, 지금 자동이 무엇으로\n * 읽히는지를 **함께 보여 준다** — 그것을 모르면 무엇을 바꾸는지 모른 채 고르게 된다.\n *\n * 자동이 무엇인지는 형식에게 묻는다(`anchorOfPart`). 여기서 「위 절반이면 max」를 다시 세면\n * 화면이 말하는 면과 실제로 그려지는 면이 갈린다.\n *\n * ## `scale` 인 부품에도 보여 준다\n *\n * 처음에는 감췄다 — 형식 주석에 「`sizing` 이 `scale` 인 부품에는 뜻이 없다」고 적혀 있어서다.\n * **그 문장은 기본값에 대한 것이다.** `anchorOf` 는 적힌 값을 가장 먼저 보고(`if (said) return\n * said`), 그 뒤에야 `sizing` 을 본다. 즉 `scale` 부품에도 면을 적으면 그것이 이긴다.\n *\n * 감춰 두면 어긋남이 난다: 발행 게이트가 막는 MIXER 의 `valve-wheel` 이 `scale` 부품이고,\n * 게이트의 사유는 「그 부품의 anchor.z 를 max 로 두라」다. 고치라는 자리에 문을 닫아 둔 셈이었다.\n *\n * 대신 `scale` 일 때는 자동이 무엇인지가 「함께 커진다」로 보이므로, 저작자가 그것을 바꾸는\n * 것임을 알 수 있다.\n */\n /**\n * ── 도움말은 자리를 먹지 않는다 ─────────────────────────────────────────────\n *\n * 설명 문단이 열 줄 있었고 그 줄들이 패널 높이의 상당 부분이었다. 속성은 앞으로 더 늘어난다\n * (능력 · 포트 · 애니메이션 · 기준정보) — 늘 보이는 설명은 그 자리를 먼저 먹는다.\n *\n * 그래서 물음표로 접는다. **지우지 않는다** — 그 문장들은 「이걸 고르면 무엇이 달라지나」를\n * 말하는 것이고, 없으면 닫힌 목록을 고를 수 없다. 한 번 읽으면 되는 것이라 접어 두는 것이 맞다.\n *\n * 편 것은 **부품을 바꿔도 그대로 둔다**(`opened` 는 부품에 딸린 상태가 아니다). 같은 것을\n * 계속 고치는 사람이 매번 다시 펴야 하면 접은 것이 되레 방해가 된다.\n */\n private help(key: string) {\n return html`${this.helpButton(key)}${this.helpHint(key)}`\n }\n\n private helpButton(key: string) {\n const shown = this.opened.has(key)\n\n return html`\n <button\n help\n aria-label=${i18next.t('figure.label.help')}\n aria-expanded=${shown ? 'true' : 'false'}\n @click=${() => {\n shown ? this.opened.delete(key) : this.opened.add(key)\n this.requestUpdate()\n }}\n >\n <md-icon>${shown ? 'help' : 'help_outline'}</md-icon>\n </button>\n `\n }\n\n private helpHint(key: string) {\n return this.opened.has(key) ? html`<p hint>${i18next.t(key)}</p>` : nothing\n }\n\n private renderAnchors(part: FigurePart) {\n const base = this.draft\n ? { x: this.draft.width, y: this.draft.height, z: this.draft.depth }\n : { x: 0, y: 0, z: 0 }\n const nameOf = (rule: string, axis: Axis) =>\n `${i18next.t(rule === 'min' || rule === 'max' ? `figure.label.anchor-${rule}-${axis}` : `figure.label.anchor-${rule}`)} (${rule})`\n const setAnchor = (axis: Axis, rule?: AnchorRule) => {\n const anchor = { ...(part.anchor ?? {}) }\n /* 「자동」은 칸을 지우는 것이다 — 빈 값을 넣어 두면 형식이 그것을 값으로 읽는다. */\n if (rule) anchor[axis] = rule\n else delete anchor[axis]\n this.put({ anchor: Object.keys(anchor).length ? anchor : undefined })\n }\n const shortNameOf = (rule: AnchorRule | 'auto', axis: Axis) => {\n if (rule === 'auto') return i18next.t('figure.label.anchor-choice-auto')\n if (rule === 'min' || rule === 'max') return i18next.t(`figure.label.anchor-choice-${rule}-${axis}`)\n return i18next.t(`figure.label.anchor-choice-${rule}`)\n }\n\n return html`\n <div field>\n <!-- 저장 키(anchor)는 남기되, 사용자는 축마다 어느 면을 기준으로 삼는지 읽는다. -->\n <label>${i18next.t('figure.label.anchor')} (anchor)${this.help('figure.text.anchor-explained')}</label>\n <div anchor-rows>\n ${(['x', 'y', 'z'] as const).map(axis => {\n const said = part.anchor?.[axis]\n const auto = anchorOfPart(part, base, axis)\n\n /*\n **면 이름은 축마다 다르다.** 처음에는 min 을 「아래·왼쪽·뒤 면」으로 한 줄에 다 적었는데,\n x 줄에서 「아래」와 「뒤」는 관계가 없어 읽는 사람이 무엇을 고르는지 몰랐다.\n\n 그리고 이름씨가 아니라 **동작**으로 적는다 — 「오른쪽 면」이 아니라 「오른쪽 면에 붙어\n 있는다」다. 「anchor.x 를 max 로 둔다」가 무슨 뜻인지 모르겠다는 말이 그 자리에서 나왔다.\n */\n /*\n **값 이름을 함께 보인다.** 동작만 적었더니(「오른쪽 면에 붙어 있는다») 발행 게이트가\n 「anchor.x 를 max 로 두라」고 말할 때 화면에서 그 낱말을 찾을 수 없었다 — 사용자가\n 그 자리에서 지적했다.\n\n `max` 는 정본에 저장되는 이름이고 서버 오류·형식 문서·게이트 사유가 다 그 이름을 쓴다.\n 숨기면 안 되는 종류다. 동작이 먼저고 이름이 괄호로 따라온다.\n */\n return html`\n <div anchor-row>\n <label>${axis.toUpperCase()}축</label>\n <div anchor-options>\n ${(['auto', 'scale', 'min', 'center', 'max', 'span'] as const).map(rule => {\n const selected = rule === 'auto' ? !said : rule === said\n const fullName = rule === 'auto' ? i18next.t('figure.label.anchor-auto', { rule: nameOf(auto, axis) }) : nameOf(rule, axis)\n return html`\n <button\n anchor-choice\n ?on=${selected}\n aria-pressed=${selected ? 'true' : 'false'}\n title=${fullName}\n @click=${() => setAnchor(axis, rule === 'auto' ? undefined : rule)}\n >${shortNameOf(rule, axis)}</button>\n `\n })}\n </div>\n <details anchor-advanced>\n <summary>${i18next.t('figure.label.anchor-advanced')}</summary>\n <div anchor-options>\n ${ANCHOR_RULES.filter(rule => rule.startsWith('aspect-')).map(rule => html`\n <button\n anchor-choice\n ?on=${rule === said}\n aria-pressed=${rule === said ? 'true' : 'false'}\n title=${nameOf(rule, axis)}\n @click=${() => setAnchor(axis, rule)}\n >${i18next.t(`figure.label.anchor-choice-${rule}`)}</button>\n `)}\n </div>\n </details>\n </div>\n `\n })}\n </div>\n </div>\n `\n }\n\n /**\n * 숫자 칸.\n *\n * 빈 칸이나 글자를 넣었을 때 0 으로 바꿔치지 않는다 — 저작자가 지우는 중일 수 있고,\n * 조용히 0 이 되면 부품이 사라진 것처럼 보인다. 숫자가 아니면 그냥 안 받는다.\n */\n private number(label: string, value: number, set: (value: number) => void, min?: number) {\n return html`\n <div cell>\n ${label ? html`<span axis>${label}</span>` : nothing}\n <input\n type=\"number\"\n .value=${String(value)}\n min=${min ?? nothing}\n @change=${(e: Event) => {\n const raw = (e.target as HTMLInputElement).value\n const parsed = Number(raw)\n if (raw === '' || !Number.isFinite(parsed)) {\n // 되돌려 놓는다. 안 그러면 화면과 정본이 갈린다.\n ;(e.target as HTMLInputElement).value = String(value)\n return\n }\n set(parsed)\n }}\n />\n </div>\n `\n }\n\n /**\n * 도형을 바꾼다.\n *\n * 도형이 바뀌면 그 도형에 뜻 없는 값이 남는다. 남겨 두면 검증이 「무시됨」 위반을\n * 내므로 여기서 걷는다.\n */\n private changePrimitive(primitive: PrimitiveKind) {\n this.change(part => {\n const next: FigurePart = { ...part, primitive }\n if (!SEGMENTED_PRIMITIVES.includes(primitive)) delete next.segments\n if (!EXTRUDE_PRIMITIVES.includes(primitive)) delete next.shape\n if (SEGMENTED_PRIMITIVES.includes(primitive) && next.segments === undefined) {\n next.segments = SEGMENT_PRESETS[1]\n }\n return next\n })\n }\n\n private put(patch: Partial<FigurePart>) {\n this.change(part => ({ ...part, ...patch }))\n }\n\n private putTransform(patch: Record<string, unknown>) {\n this.change(part => ({ ...part, transform: { ...part.transform, ...patch } as FigurePart['transform'] }))\n }\n\n /**\n * 발광 세기를 고친다. **0 이면 블록을 통째로 지운다.**\n *\n * 세기 0 인 발광을 남겨 두면 「안 빛나는데 램프인 것」이 되어, 재질 키가 갈려 묶음이\n * 헛되이 하나 는다. 0 은 「램프가 아니다」로 읽는 것이 맞다.\n *\n * `Number(value) || undefined` 를 쓰면 안 된다 — 0 이 falsy 라 변경으로 안 잡혀 옛\n * 값이 그대로 남는다. 실제로 램프가 안 꺼졌다.\n */\n private putEmissive(intensity: number) {\n // `chosen` 은 씬 모델이다. 형식으로 옮긴 값은 `partToFigure` 이 만든 것이라 여기서 다시 꺼낸다.\n const glow = this.draft && this.chosen ? partToFigure(this.chosen, this.draft).material?.emissive : undefined\n\n this.putMaterial({ emissive: intensity > 0 ? { ...glow, intensity } : undefined })\n }\n\n private putMaterial(patch: Record<string, unknown>) {\n this.change(part => ({\n ...part,\n material: { ...(part.material ?? {}), ...patch } as FigurePart['material']\n }))\n }\n\n /**\n * 수정한 것을 씬 모델로 되돌려 알린다.\n *\n * 사용자는 X·Y·Z 로 수정하고, 모델러 페이지는 씬 모델을 갖는다. 그 사이를 여기서\n * 잇는다 — 변환해서 수정하고 다시 되돌린다. **왕복 변환이 무손실이라야** 위치 하나\n * 고쳤을 뿐인데 회전값이 조금씩 밀리는 일이 없다(`fold.test.ts` 가 이를 확인한다).\n */\n private change(edit: (part: FigurePart) => FigurePart) {\n const part = this.chosen\n const draft = this.draft\n if (!part || !draft) return\n\n this.emit(partFromFigure(edit(partToFigure(part, draft)), draft))\n }\n\n /** 정밀 도구를 시킨다. 정본은 저작면이 갖는다 — 여기서는 알리기만 한다. */\n private ask(what: string, axis: string) {\n this.dispatchEvent(\n new CustomEvent('part-tool', {\n detail: { index: this.selected, tool: what, axis },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private emit(part: PartModel) {\n this.dispatchEvent(\n new CustomEvent('update-part', { detail: { index: this.selected, part }, bubbles: true, composed: true })\n )\n }\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { LitElement } from 'lit';
|
|
|
2
2
|
import type { FigureSource } from '@hatiolab/figure-model';
|
|
3
3
|
import type { ViewSettings } from './figure-view.js';
|
|
4
4
|
/**
|
|
5
|
-
* 저작 중인 것을
|
|
5
|
+
* 저작 중인 것을 **도면에 놓았을 때**의 모습.
|
|
6
6
|
*
|
|
7
7
|
* ## 편집 캔버스로는 볼 수 없다
|
|
8
8
|
*
|
|
@@ -10,7 +10,7 @@ import type { ViewSettings } from './figure-view.js';
|
|
|
10
10
|
* 값 절반은 **크기가 달라졌을 때 어떻게 반응하는가**에 있다. 기둥만 늘어나는 시그널타워,
|
|
11
11
|
* 롤러 개수가 느는 컨베이어는 기준 크기에서는 아무것도 보여 주지 못한다.
|
|
12
12
|
*
|
|
13
|
-
* 그래서 여기서는 저작한 것을 실제로 컴파일해 `FigureInstance` 로 세운다.
|
|
13
|
+
* 그래서 여기서는 저작한 것을 실제로 컴파일해 `FigureInstance` 로 세운다. 도면이 하는
|
|
14
14
|
* 일을 그대로 한다 — 청사진을 등록하고, 타입 이름만 적은 컴포넌트를 놓는다.
|
|
15
15
|
*
|
|
16
16
|
* ## 여러 크기를 한 번에 놓는다
|
|
@@ -43,7 +43,7 @@ export declare const PREVIEW_WAYS: readonly {
|
|
|
43
43
|
declare const FigurePreview_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
44
44
|
export declare class FigurePreview extends FigurePreview_base {
|
|
45
45
|
static styles: import("lit").CSSResult;
|
|
46
|
-
/** 저작 중인 것. 저장 형식으로 받는다 —
|
|
46
|
+
/** 저작 중인 것. 저장 형식으로 받는다 — 도면이 소비하는 것이 이 형식이다. */
|
|
47
47
|
source?: FigureSource;
|
|
48
48
|
/**
|
|
49
49
|
* 보기 설정. 저장되지 않는다(`figure-view.ts`).
|
|
@@ -6,7 +6,7 @@ import { compile, validate } from '@hatiolab/figure-model';
|
|
|
6
6
|
import { create, registerFigure } from '@hatiolab/things-scene';
|
|
7
7
|
import { DEFAULT_VIEW, sceneLook, WORKBENCH } from './figure-view.js';
|
|
8
8
|
/**
|
|
9
|
-
* 저작 중인 것을
|
|
9
|
+
* 저작 중인 것을 **도면에 놓았을 때**의 모습.
|
|
10
10
|
*
|
|
11
11
|
* ## 편집 캔버스로는 볼 수 없다
|
|
12
12
|
*
|
|
@@ -14,7 +14,7 @@ import { DEFAULT_VIEW, sceneLook, WORKBENCH } from './figure-view.js';
|
|
|
14
14
|
* 값 절반은 **크기가 달라졌을 때 어떻게 반응하는가**에 있다. 기둥만 늘어나는 시그널타워,
|
|
15
15
|
* 롤러 개수가 느는 컨베이어는 기준 크기에서는 아무것도 보여 주지 못한다.
|
|
16
16
|
*
|
|
17
|
-
* 그래서 여기서는 저작한 것을 실제로 컴파일해 `FigureInstance` 로 세운다.
|
|
17
|
+
* 그래서 여기서는 저작한 것을 실제로 컴파일해 `FigureInstance` 로 세운다. 도면이 하는
|
|
18
18
|
* 일을 그대로 한다 — 청사진을 등록하고, 타입 이름만 적은 컴포넌트를 놓는다.
|
|
19
19
|
*
|
|
20
20
|
* ## 여러 크기를 한 번에 놓는다
|
|
@@ -309,8 +309,8 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
|
|
|
309
309
|
try {
|
|
310
310
|
registerFigure(compile(source));
|
|
311
311
|
/*
|
|
312
|
-
|
|
313
|
-
청사진도 원본도
|
|
312
|
+
도면이 하는 일을 그대로 한다. 컴포넌트에는 타입 이름과 자리·크기만 적는다 —
|
|
313
|
+
청사진도 원본도 도면에 들어가지 않는다.
|
|
314
314
|
|
|
315
315
|
가장 큰 것이 들어갈 만큼 판을 잡고, 세로로 줄을 나눠 나란히 놓는다.
|
|
316
316
|
*/
|
|
@@ -394,7 +394,7 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
|
|
|
394
394
|
placement: view.placement,
|
|
395
395
|
/*
|
|
396
396
|
부착면의 높이. 씬이 이 자리를 갖고(`real-object.ts` 의 `sceneHeights`), 천정 기반
|
|
397
|
-
도형이 여기서 제 높이를 얻는다.
|
|
397
|
+
도형이 여기서 제 높이를 얻는다. 도면에서는 현장이 이 값을 준다.
|
|
398
398
|
*/
|
|
399
399
|
heights: { floor: 0, ceiling: view.ceilingHeight },
|
|
400
400
|
pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),
|