@things-factory/figure-ui 10.1.43 → 10.1.45

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.
Files changed (128) hide show
  1. package/client/modeller/figure-v3-links.ts +303 -0
  2. package/client/modeller/modeller-route.ts +6 -21
  3. package/client/modeller/v3-editor.ts +44 -3
  4. package/client/modeller/v3-gizmo.ts +13 -2
  5. package/client/modeller/v3-links.ts +138 -0
  6. package/client/pages/figure-v3-modeller-page.ts +2 -5
  7. package/client/route.ts +2 -6
  8. package/dist-client/modeller/figure-v3-links.d.ts +42 -0
  9. package/dist-client/modeller/figure-v3-links.js +306 -0
  10. package/dist-client/modeller/figure-v3-links.js.map +1 -0
  11. package/dist-client/modeller/modeller-route.d.ts +4 -11
  12. package/dist-client/modeller/modeller-route.js +5 -22
  13. package/dist-client/modeller/modeller-route.js.map +1 -1
  14. package/dist-client/modeller/v3-editor.d.ts +5 -0
  15. package/dist-client/modeller/v3-editor.js +46 -2
  16. package/dist-client/modeller/v3-editor.js.map +1 -1
  17. package/dist-client/modeller/v3-gizmo.js +11 -1
  18. package/dist-client/modeller/v3-gizmo.js.map +1 -1
  19. package/dist-client/modeller/v3-links.d.ts +54 -0
  20. package/dist-client/modeller/v3-links.js +110 -0
  21. package/dist-client/modeller/v3-links.js.map +1 -0
  22. package/dist-client/pages/figure-v3-modeller-page.d.ts +2 -5
  23. package/dist-client/pages/figure-v3-modeller-page.js +2 -5
  24. package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
  25. package/dist-client/route.d.ts +1 -1
  26. package/dist-client/route.js +2 -5
  27. package/dist-client/route.js.map +1 -1
  28. package/dist-client/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +4 -4
  30. package/test/ai-proposal-contract.test.ts +0 -88
  31. package/test/figure-source.test.ts +0 -102
  32. package/test/i18n-prefix-guard.test.ts +0 -24
  33. package/test/modeller-route.test.ts +14 -25
  34. package/test/part-edits.test.ts +0 -7
  35. package/test/scene-budget.test.ts +0 -7
  36. package/test/v3-gizmo.test.ts +10 -0
  37. package/test/v3-links.test.ts +123 -0
  38. package/things-factory.config.js +2 -5
  39. package/translations/en.json +6 -0
  40. package/translations/ja.json +11 -1
  41. package/translations/ko.json +6 -0
  42. package/translations/ms.json +11 -1
  43. package/translations/zh.json +11 -1
  44. package/client/modeller/angle-dial.ts +0 -70
  45. package/client/modeller/base-box.ts +0 -175
  46. package/client/modeller/figure-animations.ts +0 -1249
  47. package/client/modeller/figure-canvas.ts +0 -567
  48. package/client/modeller/figure-capabilities.ts +0 -379
  49. package/client/modeller/figure-inspector.ts +0 -2208
  50. package/client/modeller/figure-parts.ts +0 -479
  51. package/client/modeller/figure-preview.ts +0 -598
  52. package/client/modeller/figure-report.ts +0 -966
  53. package/client/modeller/figure-settings.ts +0 -328
  54. package/client/modeller/figure-side.ts +0 -272
  55. package/client/modeller/figure-thumbnail.ts +0 -123
  56. package/client/modeller/figure-view.ts +0 -152
  57. package/client/modeller/joint-edits.ts +0 -262
  58. package/client/modeller/joint-gizmo.ts +0 -180
  59. package/client/modeller/maturity.ts +0 -288
  60. package/client/modeller/new-figure-entry.ts +0 -4
  61. package/client/modeller/pose-defaults.ts +0 -67
  62. package/client/pages/figure-modeller-page.ts +0 -2082
  63. package/dist-client/modeller/angle-dial.d.ts +0 -32
  64. package/dist-client/modeller/angle-dial.js +0 -41
  65. package/dist-client/modeller/angle-dial.js.map +0 -1
  66. package/dist-client/modeller/base-box.d.ts +0 -71
  67. package/dist-client/modeller/base-box.js +0 -110
  68. package/dist-client/modeller/base-box.js.map +0 -1
  69. package/dist-client/modeller/figure-animations.d.ts +0 -159
  70. package/dist-client/modeller/figure-animations.js +0 -1132
  71. package/dist-client/modeller/figure-animations.js.map +0 -1
  72. package/dist-client/modeller/figure-canvas.d.ts +0 -150
  73. package/dist-client/modeller/figure-canvas.js +0 -506
  74. package/dist-client/modeller/figure-canvas.js.map +0 -1
  75. package/dist-client/modeller/figure-capabilities.d.ts +0 -42
  76. package/dist-client/modeller/figure-capabilities.js +0 -349
  77. package/dist-client/modeller/figure-capabilities.js.map +0 -1
  78. package/dist-client/modeller/figure-inspector.d.ts +0 -329
  79. package/dist-client/modeller/figure-inspector.js +0 -2095
  80. package/dist-client/modeller/figure-inspector.js.map +0 -1
  81. package/dist-client/modeller/figure-parts.d.ts +0 -64
  82. package/dist-client/modeller/figure-parts.js +0 -478
  83. package/dist-client/modeller/figure-parts.js.map +0 -1
  84. package/dist-client/modeller/figure-preview.d.ts +0 -138
  85. package/dist-client/modeller/figure-preview.js +0 -549
  86. package/dist-client/modeller/figure-preview.js.map +0 -1
  87. package/dist-client/modeller/figure-report.d.ts +0 -165
  88. package/dist-client/modeller/figure-report.js +0 -952
  89. package/dist-client/modeller/figure-report.js.map +0 -1
  90. package/dist-client/modeller/figure-settings.d.ts +0 -39
  91. package/dist-client/modeller/figure-settings.js +0 -317
  92. package/dist-client/modeller/figure-settings.js.map +0 -1
  93. package/dist-client/modeller/figure-side.d.ts +0 -78
  94. package/dist-client/modeller/figure-side.js +0 -317
  95. package/dist-client/modeller/figure-side.js.map +0 -1
  96. package/dist-client/modeller/figure-thumbnail.d.ts +0 -3
  97. package/dist-client/modeller/figure-thumbnail.js +0 -95
  98. package/dist-client/modeller/figure-thumbnail.js.map +0 -1
  99. package/dist-client/modeller/figure-view.d.ts +0 -118
  100. package/dist-client/modeller/figure-view.js +0 -78
  101. package/dist-client/modeller/figure-view.js.map +0 -1
  102. package/dist-client/modeller/joint-edits.d.ts +0 -49
  103. package/dist-client/modeller/joint-edits.js +0 -223
  104. package/dist-client/modeller/joint-edits.js.map +0 -1
  105. package/dist-client/modeller/joint-gizmo.d.ts +0 -34
  106. package/dist-client/modeller/joint-gizmo.js +0 -158
  107. package/dist-client/modeller/joint-gizmo.js.map +0 -1
  108. package/dist-client/modeller/maturity.d.ts +0 -55
  109. package/dist-client/modeller/maturity.js +0 -212
  110. package/dist-client/modeller/maturity.js.map +0 -1
  111. package/dist-client/modeller/new-figure-entry.d.ts +0 -2
  112. package/dist-client/modeller/new-figure-entry.js +0 -5
  113. package/dist-client/modeller/new-figure-entry.js.map +0 -1
  114. package/dist-client/modeller/pose-defaults.d.ts +0 -9
  115. package/dist-client/modeller/pose-defaults.js +0 -35
  116. package/dist-client/modeller/pose-defaults.js.map +0 -1
  117. package/dist-client/pages/figure-modeller-page.d.ts +0 -419
  118. package/dist-client/pages/figure-modeller-page.js +0 -2060
  119. package/dist-client/pages/figure-modeller-page.js.map +0 -1
  120. package/test/angle-dial.test.ts +0 -78
  121. package/test/base-box.test.ts +0 -235
  122. package/test/joint-edits.test.ts +0 -165
  123. package/test/joint-gizmo.test.ts +0 -174
  124. package/test/joint-needle.test.ts +0 -162
  125. package/test/modeller-undo-redo.test.ts +0 -33
  126. package/test/new-figure-entry.test.ts +0 -15
  127. package/test/pose-defaults.test.ts +0 -71
  128. package/test/preview-board-depth.test.ts +0 -27
@@ -1,32 +0,0 @@
1
- /**
2
- * 막대 옆에 서는 **각도 눈**. 지금 각이 범위의 어디인지를 한 눈에 보인다.
3
- *
4
- * ## 왜 3D 로는 모자랐나
5
- *
6
- * 관절이 돌아도 화면이 그대로인 경우가 있다 — 제 축에서 대칭인 부품이 그렇다. 그래서 관절이
7
- * 모는 부품에 바늘을 달아 미리보기에서 함께 돌게 했는데(`joint-gizmo.ts` 의 `jointNeedles`),
8
- * **미리보기에서는 그 바늘이 4 × 2 픽셀이다**(잰 값: 부품 100mm · 바늘 60 × 12mm · 여덟 대를
9
- * 한 화면에 세운 상태). 미리보기는 크기 반응을 보려고 여덟 대를 나란히 놓는 자리라 한 대가
10
- * 20 픽셀쯤이고, 그 위의 어떤 표시도 몇 픽셀을 넘지 못한다.
11
- *
12
- * 각을 읽는 일은 3D 의 배율에서 떼어 놓는다. 눈은 막대 옆 24 픽셀 자리에 서고, 도형이 아무리
13
- * 작게 서 있어도 같은 크기로 읽힌다.
14
- */
15
- export interface DialShape {
16
- /** 가동 범위를 채운 부채꼴. SVG path 의 `d`. */
17
- sector: string;
18
- /** 지금 값이 가리키는 끝점. */
19
- needle: {
20
- x: number;
21
- y: number;
22
- };
23
- centre: {
24
- x: number;
25
- y: number;
26
- };
27
- radius: number;
28
- }
29
- /**
30
- * 눈의 모양. 값이 범위를 벗어나면 범위 끝에 세운다 — 밖을 가리키는 바늘은 없는 값을 말한다.
31
- */
32
- export declare function dialShape(min: number, max: number, value: number, radius?: number): DialShape;
@@ -1,41 +0,0 @@
1
- /*
2
- * Copyright © HatioLab Inc. All rights reserved.
3
- */
4
- /** 0 도가 위를 가리키고, 값이 커지면 시계 방향으로 돈다 — 화면의 각과 같은 방향이다. */
5
- function at(centre, radius, degrees) {
6
- const rad = (degrees * Math.PI) / 180;
7
- return {
8
- x: +(centre.x + radius * Math.sin(rad)).toFixed(2),
9
- y: +(centre.y - radius * Math.cos(rad)).toFixed(2)
10
- };
11
- }
12
- /** 한 바퀴에 가까우면 부채꼴 대신 온전한 원이다. */
13
- const FULL = 359.9;
14
- /**
15
- * 눈의 모양. 값이 범위를 벗어나면 범위 끝에 세운다 — 밖을 가리키는 바늘은 없는 값을 말한다.
16
- */
17
- export function dialShape(min, max, value, radius = 9) {
18
- const centre = { x: radius + 1, y: radius + 1 };
19
- const span = Math.abs(max - min);
20
- const held = Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max));
21
- if (span >= FULL) {
22
- const top = at(centre, radius, 0);
23
- const bottom = at(centre, radius, 180);
24
- return {
25
- sector: `M ${top.x} ${top.y} A ${radius} ${radius} 0 1 1 ${bottom.x} ${bottom.y} A ${radius} ${radius} 0 1 1 ${top.x} ${top.y} Z`,
26
- needle: at(centre, radius, held),
27
- centre,
28
- radius
29
- };
30
- }
31
- const from = at(centre, radius, min);
32
- const to = at(centre, radius, max);
33
- const large = span > 180 ? 1 : 0;
34
- return {
35
- sector: `M ${centre.x} ${centre.y} L ${from.x} ${from.y} A ${radius} ${radius} 0 ${large} 1 ${to.x} ${to.y} Z`,
36
- needle: at(centre, radius, held),
37
- centre,
38
- radius
39
- };
40
- }
41
- //# sourceMappingURL=angle-dial.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"angle-dial.js","sourceRoot":"","sources":["../../client/modeller/angle-dial.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0BH,wDAAwD;AACxD,SAAS,EAAE,CAAC,MAAgC,EAAE,MAAc,EAAE,OAAe;IAC3E,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;IACrC,OAAO;QACL,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAClD,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;KACnD,CAAA;AACH,CAAC;AAED,iCAAiC;AACjC,MAAM,IAAI,GAAG,KAAK,CAAA;AAElB;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,GAAW,EAAE,KAAa,EAAE,MAAM,GAAG,CAAC;IAC3E,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAA;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IAE9E,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;QACjC,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;QACtC,OAAO;YACL,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,UAAU,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI;YACjI,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC;YAChC,MAAM;YACN,MAAM;SACP,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEhC,OAAO;QACL,MAAM,EAAE,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI;QAC9G,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC;QAChC,MAAM;QACN,MAAM;KACP,CAAA;AACH,CAAC","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\n/**\n * 막대 옆에 서는 **각도 눈**. 지금 각이 범위의 어디인지를 한 눈에 보인다.\n *\n * ## 왜 3D 로는 모자랐나\n *\n * 관절이 돌아도 화면이 그대로인 경우가 있다 — 제 축에서 대칭인 부품이 그렇다. 그래서 관절이\n * 모는 부품에 바늘을 달아 미리보기에서 함께 돌게 했는데(`joint-gizmo.ts` 의 `jointNeedles`),\n * **미리보기에서는 그 바늘이 4 × 2 픽셀이다**(잰 값: 부품 100mm · 바늘 60 × 12mm · 여덟 대를\n * 한 화면에 세운 상태). 미리보기는 크기 반응을 보려고 여덟 대를 나란히 놓는 자리라 한 대가\n * 20 픽셀쯤이고, 그 위의 어떤 표시도 몇 픽셀을 넘지 못한다.\n *\n * 각을 읽는 일은 3D 의 배율에서 떼어 놓는다. 눈은 막대 옆 24 픽셀 자리에 서고, 도형이 아무리\n * 작게 서 있어도 같은 크기로 읽힌다.\n */\n\nexport interface DialShape {\n /** 가동 범위를 채운 부채꼴. SVG path 의 `d`. */\n sector: string\n /** 지금 값이 가리키는 끝점. */\n needle: { x: number; y: number }\n centre: { x: number; y: number }\n radius: number\n}\n\n/** 0 도가 위를 가리키고, 값이 커지면 시계 방향으로 돈다 — 화면의 각과 같은 방향이다. */\nfunction at(centre: { x: number; y: number }, radius: number, degrees: number) {\n const rad = (degrees * Math.PI) / 180\n return {\n x: +(centre.x + radius * Math.sin(rad)).toFixed(2),\n y: +(centre.y - radius * Math.cos(rad)).toFixed(2)\n }\n}\n\n/** 한 바퀴에 가까우면 부채꼴 대신 온전한 원이다. */\nconst FULL = 359.9\n\n/**\n * 눈의 모양. 값이 범위를 벗어나면 범위 끝에 세운다 — 밖을 가리키는 바늘은 없는 값을 말한다.\n */\nexport function dialShape(min: number, max: number, value: number, radius = 9): DialShape {\n const centre = { x: radius + 1, y: radius + 1 }\n const span = Math.abs(max - min)\n const held = Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max))\n\n if (span >= FULL) {\n const top = at(centre, radius, 0)\n const bottom = at(centre, radius, 180)\n return {\n sector: `M ${top.x} ${top.y} A ${radius} ${radius} 0 1 1 ${bottom.x} ${bottom.y} A ${radius} ${radius} 0 1 1 ${top.x} ${top.y} Z`,\n needle: at(centre, radius, held),\n centre,\n radius\n }\n }\n\n const from = at(centre, radius, min)\n const to = at(centre, radius, max)\n const large = span > 180 ? 1 : 0\n\n return {\n sector: `M ${centre.x} ${centre.y} L ${from.x} ${from.y} A ${radius} ${radius} 0 ${large} 1 ${to.x} ${to.y} Z`,\n needle: at(centre, radius, held),\n centre,\n radius\n }\n}\n"]}
@@ -1,71 +0,0 @@
1
- import type { FigureDraft, PartModel } from './figure-source.js';
2
- /**
3
- * 기준 상자를 고친다 — **부품은 제자리에 둔 채.**
4
- *
5
- * ## 왜 화면에 있어야 하나
6
- *
7
- * 기준 상자는 도면에 놓일 때의 배율을 정하는 수다(ADR-0044 ①). 부품이 그 상자를 넘으면 발행이
8
- * 막히고, 고치는 길은 「상자 숫자 하나를 키우는 것」인데 **화면에 그 칸이 없었다.** 저작자는
9
- * 넘쳤다는 말만 듣고 고칠 자리를 못 찾았다.
10
- *
11
- * ## 상자를 키우면 부품이 움직인다 — 그것을 되돌린다
12
- *
13
- * 저작면의 부품 자리는 판의 왼쪽 위에서 잰 값이고, 저장 형식의 자리는 **상자의 한가운데**에서 잰
14
- * 값이다(`toFigureSource` 가 절반을 뺀다). 그래서 상자만 키우면 한가운데가 옮겨 가고 부품이 전부
15
- * 반대로 밀린다. 여기서 늘어난 절반만큼 부품을 함께 옮겨, 저장되는 자리가 한 값도 안 바뀌게 한다.
16
- *
17
- * ## 「부품에 맞추기」는 한가운데를 지킨다
18
- *
19
- * 부품을 감싸는 가장 작은 상자를 만들 때, 부품 쪽을 **옮기지 않는다.** 한가운데에서 가장 먼
20
- * 부품까지의 거리를 양쪽으로 잡는다. 부품이 한쪽으로 치우쳐 있으면 반대쪽에 여유가 남는데,
21
- * 그것이 사실이다 — 치우친 것을 가운데로 끌어오면 저작자가 놓은 자리가 소리 없이 달라지고,
22
- * 발행된 도형에서는 문(port)의 자리가 함께 움직인다.
23
- *
24
- * 높이는 밑면이 바닥이라 한쪽만 늘린다. 바닥 아래로 내려간 부품이 있으면 그 부품은 상자 밖에
25
- * 남는다 — 그것을 담으려면 부품을 올려야 하고, 그것은 저작자가 정할 일이다. 발행 관문이 이미
26
- * `part-outside-base` 로 말한다.
27
- *
28
- * ## 돌아간 부품은 돌아간 부피로 잰다
29
- *
30
- * 45 도 돌린 판은 제 `size` 보다 넓게 자리를 차지한다. 발행 관문은 그것을 `rotatedExtentOf` 로
31
- * 재는데 여기서는 `width`·`height` 를 그대로 썼다. 그래서 **회전한 부품이 있는 도형은 「부품에
32
- * 맞추기」를 눌러도 발행이 막힌 채로 남았다** — 표본에서 CONVEYOR(부품 17 중 4 가 회전)와
33
- * ROLL_CRADLE(20 중 13)이 그랬다(2026-09-19 확인). 같은 함수로 재서 둘이 어긋나지 않게 한다.
34
- */
35
- export interface BaseSize {
36
- /** 저작면의 가로 — 형식의 `base.x`. */
37
- width: number;
38
- /** 저작면의 세로 — 형식의 `base.z`. */
39
- height: number;
40
- /** 높이 — 형식의 `base.y`. */
41
- depth: number;
42
- }
43
- export declare const baseOf: (draft: FigureDraft) => BaseSize;
44
- /**
45
- * 부품 전부를 **바닥에 앉힌다** — 가장 낮은 것이 상자 밑면에 닿도록 전부를 같은 값만큼 옮긴다.
46
- *
47
- * ## 왜 전부를 옮기나
48
- *
49
- * 관문이 막는 것은 「가장 낮은 부품이 밑면에 안 닿는다」다(`parts-off-placement-face`). 그 부품
50
- * 하나만 내리면 관문은 통과하지만 도형이 달라진다 — 프레임에 붙어 있던 발이 떨어진다. 부품 사이의
51
- * 자리는 저작자가 정한 것이므로 그것을 지키고 전체를 옮긴다.
52
- *
53
- * ## 위로도 옮긴다
54
- *
55
- * 바닥 **아래로** 내려간 부품이 있으면 올린다. 그때는 위쪽이 상자를 넘을 수 있으므로 저작자가
56
- * 「부품에 맞추기」를 이어서 누르게 된다 — 두 조치가 각자 한 가지만 한다.
57
- *
58
- * 앉힐 것이 없으면 **받은 것을 그대로 돌려준다** — 고친 것이 없는데 「저장 안 됨」이 붙지 않게.
59
- */
60
- export declare function partsSettledOnFloor(parts: readonly PartModel[]): readonly PartModel[];
61
- /** 부품 전부를 담는 가장 작은 상자. 부품이 없으면 잴 것이 없다. */
62
- export declare function baseFittingParts(draft: FigureDraft, parts: readonly PartModel[]): BaseSize | undefined;
63
- /**
64
- * 상자를 이 크기로. 부품은 저장되는 자리가 그대로이도록 함께 옮긴다.
65
- *
66
- * 달라질 것이 없으면 **받은 것을 그대로 돌려준다** — 고친 것이 없는데 「저장 안 됨」이 붙지 않게.
67
- */
68
- export declare function resizeBase(draft: FigureDraft, parts: readonly PartModel[], next: BaseSize): {
69
- draft: FigureDraft;
70
- parts: readonly PartModel[];
71
- };
@@ -1,110 +0,0 @@
1
- /*
2
- * Copyright © HatioLab Inc. All rights reserved.
3
- */
4
- import { rotatedExtentOf } from '@hatiolab/figure-model';
5
- /** 상자는 밀리미터 하나보다 작아질 수 없다. 0 이 되면 배율이 0 으로 나뉜다. */
6
- const LEAST = 1;
7
- const round = (value) => Math.round(value);
8
- export const baseOf = (draft) => ({
9
- width: draft.width,
10
- height: draft.height,
11
- depth: draft.depth
12
- });
13
- const DEG = 180 / Math.PI;
14
- /**
15
- * 이 부품이 차지하는 부피 — **형식의 축으로**.
16
- *
17
- * 저작면과 형식의 축 이름이 엇갈린다(`figure-source.ts` 의 표): 저작면의 `depth` 가 형식의 y 이고
18
- * `height` 가 형식의 z 다. 회전 이름도 엇갈린다(`rotationY` → 형식의 z). 그 표를 여기서 다시
19
- * 적지 않고 같은 모양으로 옮겨, `rotatedExtentOf` 가 관문에서와 같은 답을 내게 한다.
20
- */
21
- function extentOf(part) {
22
- return rotatedExtentOf({
23
- transform: {
24
- position: { x: 0, y: 0, z: 0 },
25
- size: { x: part.width, y: part.depth, z: part.height },
26
- rotation: {
27
- x: part.rotationX ? part.rotationX * DEG : 0,
28
- y: part.rotation ? -part.rotation * DEG : 0,
29
- z: part.rotationY ? part.rotationY * DEG : 0
30
- }
31
- }
32
- });
33
- }
34
- /**
35
- * 부품 전부를 **바닥에 앉힌다** — 가장 낮은 것이 상자 밑면에 닿도록 전부를 같은 값만큼 옮긴다.
36
- *
37
- * ## 왜 전부를 옮기나
38
- *
39
- * 관문이 막는 것은 「가장 낮은 부품이 밑면에 안 닿는다」다(`parts-off-placement-face`). 그 부품
40
- * 하나만 내리면 관문은 통과하지만 도형이 달라진다 — 프레임에 붙어 있던 발이 떨어진다. 부품 사이의
41
- * 자리는 저작자가 정한 것이므로 그것을 지키고 전체를 옮긴다.
42
- *
43
- * ## 위로도 옮긴다
44
- *
45
- * 바닥 **아래로** 내려간 부품이 있으면 올린다. 그때는 위쪽이 상자를 넘을 수 있으므로 저작자가
46
- * 「부품에 맞추기」를 이어서 누르게 된다 — 두 조치가 각자 한 가지만 한다.
47
- *
48
- * 앉힐 것이 없으면 **받은 것을 그대로 돌려준다** — 고친 것이 없는데 「저장 안 됨」이 붙지 않게.
49
- */
50
- export function partsSettledOnFloor(parts) {
51
- if (parts.length === 0)
52
- return parts;
53
- let lowest = Infinity;
54
- for (const part of parts) {
55
- const extent = extentOf(part);
56
- lowest = Math.min(lowest, part.zPos + part.depth / 2 - extent.y / 2);
57
- }
58
- const shift = round(lowest);
59
- if (shift === 0)
60
- return parts;
61
- return parts.map(part => ({ ...part, zPos: round(part.zPos - shift) }));
62
- }
63
- /** 부품 전부를 담는 가장 작은 상자. 부품이 없으면 잴 것이 없다. */
64
- export function baseFittingParts(draft, parts) {
65
- if (parts.length === 0)
66
- return undefined;
67
- const middle = { x: draft.width / 2, y: draft.height / 2 };
68
- let half = { x: 0, y: 0 };
69
- let tallest = 0;
70
- for (const part of parts) {
71
- /* 돌아간 부피. 관문이 재는 것과 같은 함수로 잰다 — 다르게 재면 눌러도 막힌 채로 남는다. */
72
- const extent = extentOf(part);
73
- const centre = {
74
- x: part.left + part.width / 2,
75
- y: part.top + part.height / 2,
76
- z: part.zPos + part.depth / 2
77
- };
78
- half = {
79
- x: Math.max(half.x, Math.abs(centre.x - extent.x / 2 - middle.x), Math.abs(centre.x + extent.x / 2 - middle.x)),
80
- y: Math.max(half.y, Math.abs(centre.y - extent.z / 2 - middle.y), Math.abs(centre.y + extent.z / 2 - middle.y))
81
- };
82
- tallest = Math.max(tallest, centre.z + extent.y / 2);
83
- }
84
- return {
85
- width: Math.max(LEAST, round(half.x * 2)),
86
- height: Math.max(LEAST, round(half.y * 2)),
87
- depth: Math.max(LEAST, round(tallest))
88
- };
89
- }
90
- /**
91
- * 상자를 이 크기로. 부품은 저장되는 자리가 그대로이도록 함께 옮긴다.
92
- *
93
- * 달라질 것이 없으면 **받은 것을 그대로 돌려준다** — 고친 것이 없는데 「저장 안 됨」이 붙지 않게.
94
- */
95
- export function resizeBase(draft, parts, next) {
96
- const size = {
97
- width: Math.max(LEAST, round(next.width)),
98
- height: Math.max(LEAST, round(next.height)),
99
- depth: Math.max(LEAST, round(next.depth))
100
- };
101
- if (size.width === draft.width && size.height === draft.height && size.depth === draft.depth) {
102
- return { draft, parts };
103
- }
104
- const shift = { x: (size.width - draft.width) / 2, y: (size.height - draft.height) / 2 };
105
- const moved = shift.x === 0 && shift.y === 0
106
- ? parts
107
- : parts.map(part => ({ ...part, left: part.left + shift.x, top: part.top + shift.y }));
108
- return { draft: { ...draft, ...size }, parts: moved };
109
- }
110
- //# sourceMappingURL=base-box.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"base-box.js","sourceRoot":"","sources":["../../client/modeller/base-box.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { rotatedExtentOf } from '@hatiolab/figure-model'\n\nimport type { FigureDraft, PartModel } from './figure-source.js'\n\n/**\n * 기준 상자를 고친다 — **부품은 제자리에 둔 채.**\n *\n * ## 왜 화면에 있어야 하나\n *\n * 기준 상자는 도면에 놓일 때의 배율을 정하는 수다(ADR-0044 ①). 부품이 그 상자를 넘으면 발행이\n * 막히고, 고치는 길은 「상자 숫자 하나를 키우는 것」인데 **화면에 그 칸이 없었다.** 저작자는\n * 넘쳤다는 말만 듣고 고칠 자리를 못 찾았다.\n *\n * ## 상자를 키우면 부품이 움직인다 — 그것을 되돌린다\n *\n * 저작면의 부품 자리는 판의 왼쪽 위에서 잰 값이고, 저장 형식의 자리는 **상자의 한가운데**에서 잰\n * 값이다(`toFigureSource` 가 절반을 뺀다). 그래서 상자만 키우면 한가운데가 옮겨 가고 부품이 전부\n * 반대로 밀린다. 여기서 늘어난 절반만큼 부품을 함께 옮겨, 저장되는 자리가 한 값도 안 바뀌게 한다.\n *\n * ## 「부품에 맞추기」는 한가운데를 지킨다\n *\n * 부품을 감싸는 가장 작은 상자를 만들 때, 부품 쪽을 **옮기지 않는다.** 한가운데에서 가장 먼\n * 부품까지의 거리를 양쪽으로 잡는다. 부품이 한쪽으로 치우쳐 있으면 반대쪽에 여유가 남는데,\n * 그것이 사실이다 — 치우친 것을 가운데로 끌어오면 저작자가 놓은 자리가 소리 없이 달라지고,\n * 발행된 도형에서는 문(port)의 자리가 함께 움직인다.\n *\n * 높이는 밑면이 바닥이라 한쪽만 늘린다. 바닥 아래로 내려간 부품이 있으면 그 부품은 상자 밖에\n * 남는다 — 그것을 담으려면 부품을 올려야 하고, 그것은 저작자가 정할 일이다. 발행 관문이 이미\n * `part-outside-base` 로 말한다.\n *\n * ## 돌아간 부품은 돌아간 부피로 잰다\n *\n * 45 도 돌린 판은 제 `size` 보다 넓게 자리를 차지한다. 발행 관문은 그것을 `rotatedExtentOf` 로\n * 재는데 여기서는 `width`·`height` 를 그대로 썼다. 그래서 **회전한 부품이 있는 도형은 「부품에\n * 맞추기」를 눌러도 발행이 막힌 채로 남았다** — 표본에서 CONVEYOR(부품 17 중 4 가 회전)와\n * ROLL_CRADLE(20 중 13)이 그랬다(2026-09-19 확인). 같은 함수로 재서 둘이 어긋나지 않게 한다.\n */\n\nexport interface BaseSize {\n /** 저작면의 가로 — 형식의 `base.x`. */\n width: number\n /** 저작면의 세로 — 형식의 `base.z`. */\n height: number\n /** 높이 — 형식의 `base.y`. */\n depth: number\n}\n\n/** 상자는 밀리미터 하나보다 작아질 수 없다. 0 이 되면 배율이 0 으로 나뉜다. */\nconst LEAST = 1\n\nconst round = (value: number) => Math.round(value)\n\nexport const baseOf = (draft: FigureDraft): BaseSize => ({\n width: draft.width,\n height: draft.height,\n depth: draft.depth\n})\n\nconst DEG = 180 / Math.PI\n\n/**\n * 이 부품이 차지하는 부피 — **형식의 축으로**.\n *\n * 저작면과 형식의 축 이름이 엇갈린다(`figure-source.ts` 의 표): 저작면의 `depth` 가 형식의 y 이고\n * `height` 가 형식의 z 다. 회전 이름도 엇갈린다(`rotationY` → 형식의 z). 그 표를 여기서 다시\n * 적지 않고 같은 모양으로 옮겨, `rotatedExtentOf` 가 관문에서와 같은 답을 내게 한다.\n */\nfunction extentOf(part: PartModel): { x: number; y: number; z: number } {\n return rotatedExtentOf({\n transform: {\n position: { x: 0, y: 0, z: 0 },\n size: { x: part.width, y: part.depth, z: part.height },\n rotation: {\n x: part.rotationX ? part.rotationX * DEG : 0,\n y: part.rotation ? -part.rotation * DEG : 0,\n z: part.rotationY ? part.rotationY * DEG : 0\n }\n }\n } as never)\n}\n\n/**\n * 부품 전부를 **바닥에 앉힌다** — 가장 낮은 것이 상자 밑면에 닿도록 전부를 같은 값만큼 옮긴다.\n *\n * ## 왜 전부를 옮기나\n *\n * 관문이 막는 것은 「가장 낮은 부품이 밑면에 안 닿는다」다(`parts-off-placement-face`). 그 부품\n * 하나만 내리면 관문은 통과하지만 도형이 달라진다 — 프레임에 붙어 있던 발이 떨어진다. 부품 사이의\n * 자리는 저작자가 정한 것이므로 그것을 지키고 전체를 옮긴다.\n *\n * ## 위로도 옮긴다\n *\n * 바닥 **아래로** 내려간 부품이 있으면 올린다. 그때는 위쪽이 상자를 넘을 수 있으므로 저작자가\n * 「부품에 맞추기」를 이어서 누르게 된다 — 두 조치가 각자 한 가지만 한다.\n *\n * 앉힐 것이 없으면 **받은 것을 그대로 돌려준다** — 고친 것이 없는데 「저장 안 됨」이 붙지 않게.\n */\nexport function partsSettledOnFloor(parts: readonly PartModel[]): readonly PartModel[] {\n if (parts.length === 0) return parts\n\n let lowest = Infinity\n for (const part of parts) {\n const extent = extentOf(part)\n lowest = Math.min(lowest, part.zPos + part.depth / 2 - extent.y / 2)\n }\n\n const shift = round(lowest)\n if (shift === 0) return parts\n\n return parts.map(part => ({ ...part, zPos: round(part.zPos - shift) }))\n}\n\n/** 부품 전부를 담는 가장 작은 상자. 부품이 없으면 잴 것이 없다. */\nexport function baseFittingParts(draft: FigureDraft, parts: readonly PartModel[]): BaseSize | undefined {\n if (parts.length === 0) return undefined\n\n const middle = { x: draft.width / 2, y: draft.height / 2 }\n let half = { x: 0, y: 0 }\n let tallest = 0\n\n for (const part of parts) {\n /* 돌아간 부피. 관문이 재는 것과 같은 함수로 잰다 — 다르게 재면 눌러도 막힌 채로 남는다. */\n const extent = extentOf(part)\n const centre = {\n x: part.left + part.width / 2,\n y: part.top + part.height / 2,\n z: part.zPos + part.depth / 2\n }\n\n half = {\n x: Math.max(half.x, Math.abs(centre.x - extent.x / 2 - middle.x), Math.abs(centre.x + extent.x / 2 - middle.x)),\n y: Math.max(half.y, Math.abs(centre.y - extent.z / 2 - middle.y), Math.abs(centre.y + extent.z / 2 - middle.y))\n }\n tallest = Math.max(tallest, centre.z + extent.y / 2)\n }\n\n return {\n width: Math.max(LEAST, round(half.x * 2)),\n height: Math.max(LEAST, round(half.y * 2)),\n depth: Math.max(LEAST, round(tallest))\n }\n}\n\n/**\n * 상자를 이 크기로. 부품은 저장되는 자리가 그대로이도록 함께 옮긴다.\n *\n * 달라질 것이 없으면 **받은 것을 그대로 돌려준다** — 고친 것이 없는데 「저장 안 됨」이 붙지 않게.\n */\nexport function resizeBase(\n draft: FigureDraft,\n parts: readonly PartModel[],\n next: BaseSize\n): { draft: FigureDraft; parts: readonly PartModel[] } {\n const size = {\n width: Math.max(LEAST, round(next.width)),\n height: Math.max(LEAST, round(next.height)),\n depth: Math.max(LEAST, round(next.depth))\n }\n\n if (size.width === draft.width && size.height === draft.height && size.depth === draft.depth) {\n return { draft, parts }\n }\n\n const shift = { x: (size.width - draft.width) / 2, y: (size.height - draft.height) / 2 }\n const moved =\n shift.x === 0 && shift.y === 0\n ? parts\n : parts.map(part => ({ ...part, left: part.left + shift.x, top: part.top + shift.y }))\n\n return { draft: { ...draft, ...size }, parts: moved }\n}\n"]}
@@ -1,159 +0,0 @@
1
- import { LitElement, type TemplateResult } from 'lit';
2
- import type { FigureDraft, PartModel } from './figure-source.js';
3
- declare const FigureAnimations_base: (new (...args: any[]) => LitElement) & typeof LitElement;
4
- export declare class FigureAnimations extends FigureAnimations_base {
5
- static styles: import("lit").CSSResult[];
6
- draft?: FigureDraft;
7
- parts: PartModel[];
8
- /**
9
- * 펼쳐 둔 것. 이름이 아니라 **목록과 자리**로 잡는다 — 이름은 편집 중에 바뀐다.
10
- *
11
- * 목록이 둘이므로 자리 하나로는 모자란다. `clip:0` 과 `parameter:0` 은 다른 것이다.
12
- */
13
- private open;
14
- private recipeTarget;
15
- /** 단위를 손으로 적는 중인 파라미터. 목록에 없는 말을 적으려고 연 칸이다. */
16
- private typingUnit;
17
- private get clips();
18
- private get parameters();
19
- /** 두 목록의 이름을 한자리에서 본다 — 인스턴스가 이름 하나로 값을 준다. */
20
- private get allNames();
21
- private channelsOf;
22
- /** 부품 이름 — 채널이 이것을 가리킨다. */
23
- private get partNames();
24
- /** Joints a channel can drive instead of a part (ADR-0066). */
25
- private get joints();
26
- /**
27
- * 몇 부품이 움직이나 — 채널이 가리키는 **서로 다른 부품**의 수.
28
- *
29
- * clip 수가 아니다. 한 부품을 세 clip 이 움직여도 그 부품 하나가 단독 묶음이 될 뿐이다.
30
- * 청사진이 묶음을 가르는 기준과 같은 셈이어야 화면의 수와 판정의 수가 맞는다.
31
- */
32
- private get movingParts();
33
- render(): TemplateResult;
34
- /**
35
- * 빈 화면 — **이 탭이 무엇인지 가르치는 자리다.**
36
- *
37
- * 「clip 을 추가하세요」라고만 쓰면 처음 여는 사람은 clip 이 무엇인지 모른 채 단추를 누른다.
38
- * 그래서 무엇을 만들 수 있는지 예를 들고, 그것이 어떻게 움직이는지(사실이 들어온다)를 적는다.
39
- */
40
- private renderRecipes;
41
- /** 이름이 다른 것과 겹치나. 두 목록을 함께 본다 — 인스턴스는 이름 하나로 값을 준다. */
42
- private taken;
43
- private renderClip;
44
- /**
45
- * 파라미터 하나 — **이름·범위·자세.**
46
- *
47
- * 범위가 화면에 있어야 하는 이유는 그것이 **인스턴스와 주고받는 말**이기 때문이다.
48
- * 곡선은 0~1 이고 인스턴스는 mm 로 준다. 그 사이를 런타임이 맵하는데, 무엇부터 무엇
49
- * 까지인지 저작자가 정하지 않으면 값을 줄 수가 없다.
50
- */
51
- private renderParameter;
52
- private renderChannels;
53
- /**
54
- * 채널 하나 — 어느 부품의 어느 속성을 움직이나.
55
- *
56
- * 없는 부품을 가리키는 채널은 붉게 낸다. 부품 이름을 바꾸면 그 순간 이렇게 되는데,
57
- * 검증은 저장할 때 말하고 여기서는 **그 자리에서** 보여 준다.
58
- */
59
- private renderChannel;
60
- /**
61
- * 회전 중심 — **`rotation` 에서만 뜻이 있다.**
62
- *
63
- * 그래서 경로가 rotation 일 때만 낸다. 언제나 내면 저작자가 옮김·배율에도 적고, 검증이
64
- * 저장할 때 「쓰이지 않는다」고 알린다. 안 보이면 적을 일이 없다.
65
- */
66
- private renderPivot;
67
- /**
68
- * 키프레임 표.
69
- *
70
- * **둘 미만으로 못 내려간다.** 하나뿐이면 움직이는 것이 아니라 자세 하나이고, 형식이
71
- * 오류로 막는다. 저장하고 나서 알려 주는 것보다 지우는 단추를 죽이는 것이 낫다.
72
- *
73
- * 시각이 오름차순이어야 하는 것도 형식의 규칙이다. 여기서는 어긋난 줄을 붉게 내고 값을
74
- * 고치지는 않는다 — 저작자가 3 을 1 로 고치던 중일 수 있다.
75
- */
76
- /**
77
- * Keys of a joint channel: a position and one joint coordinate each. A value outside the joint's limits
78
- * is marked and left as typed; the format refuses it on save.
79
- */
80
- private renderJointKeys;
81
- private renderKeys;
82
- /**
83
- * 따로 움직이는 부품 예산.
84
- *
85
- * **넘어도 막지 않는다.** 제 변환을 지는 부품은 저마다 단독 묶음이라 draw call 이 늘고,
86
- * 그만큼 한 화면에 함께 세울 수 있는 대수가 준다 — 그 대가가 수로 보이면 저작자가 아낀다.
87
- * 랜드마크 설비 하나는 정교해도 되고, 그 값이 도면 예산에 드러나면 된다.
88
- */
89
- private renderBudget;
90
- /**
91
- * 초안에 실어 올려보낸다.
92
- *
93
- * `animations` 는 도형 전체의 것이므로 부품 편집(`update-part`)이 아니라 `update-draft` 로
94
- * 간다. 빈 배열은 칸을 지운다 — clip 을 다 지운 도형은 `animations: []` 가 아니라 없는 것이다.
95
- */
96
- private tell;
97
- private patchClip;
98
- private patchParameter;
99
- /** 채널 묶음을 통째로 얹는다. **어느 목록이든 같은 자리에 닿는다.** */
100
- private setChannels;
101
- private patchChannel;
102
- private addClip;
103
- private addParameter;
104
- /**
105
- * Recipe creates a valid, editable two-pose motion; it never saves or publishes the Figure.
106
- *
107
- * 어느 목록으로 가는지는 레시피가 안다 — 도는 롤러는 애니메이션이고 열리는 문은
108
- * 파라미터다. 저작자에게 그 갈래를 먼저 고르게 하지 않는 이유가 이것이다.
109
- */
110
- private addRecipe;
111
- private renameClip;
112
- private renameParameter;
113
- /**
114
- * 단위 칸 — **목록이 보이는 채로, 적는 길도 열어 둔다.**
115
- *
116
- * 처음에는 `datalist` 를 붙였다. 그것은 브라우저가 쥐고 있는 기능이라 **눌러 보기 전에는 목록이
117
- * 있는지조차 안 보인다** — 화살표도 없고, 어디에 어떻게 뜨는지도 브라우저가 정한다. 고를 수
118
- * 있다는 것을 알리는 것이 이 칸의 목적이므로 `select` 로 바꿨다.
119
- *
120
- * 형식은 단위를 규칙으로 쓰지 않으므로(표시용 말이다) 목록에 가두지 않는다. 마지막 줄이
121
- * 「직접 적기」이고, 목록에 없는 말이 이미 적혀 있으면 처음부터 적는 칸으로 연다.
122
- */
123
- private renderUnit;
124
- /** 고른 것을 넣는다. 「직접 적기」는 값이 아니라 **칸을 바꾸는 것**이다. */
125
- private chooseUnit;
126
- private setRange;
127
- /**
128
- * Transition time. Clearing the input removes the field, which means instant. 0 is instant too, but
129
- * a stored 0 and a missing field are different sources, so an empty input is never saved as 0.
130
- */
131
- private setDuration;
132
- private setDefault;
133
- private removeClip;
134
- private removeParameter;
135
- private addChannel;
136
- /**
137
- * Points a channel at another part or joint. Crossing between the two changes the channel's shape: a
138
- * joint channel has no path and one number per key, a part channel a path and a vector. The key positions
139
- * are kept, since they are the author's timing; values restart at the rest pose.
140
- */
141
- private retarget;
142
- private setChannel;
143
- /**
144
- * 경로를 바꾼다 — **값까지 함께 옮긴다.**
145
- *
146
- * 옮김·회전의 0 은 「제자리」인데 배율의 0 은 **부품을 지운다.** 경로만 바꾸고 값을 그대로
147
- * 두면 저작자는 부품이 사라진 것을 보고 도구가 고장 난 줄 안다. 그래서 옮김·회전 ↔ 배율을
148
- * 건널 때 시작값을 그 경로의 제자리로 되돌린다. 시각은 지킨다 — 저작자가 짠 박자다.
149
- */
150
- private setPath;
151
- private removeChannel;
152
- /** 마지막 키에서 1 초 뒤. 값은 그 키를 그대로 받는다 — 붙여 놓고 고치는 것이 짜기 쉽다. */
153
- private addKey;
154
- private setJointKey;
155
- private setKey;
156
- /** 둘 미만으로 내려가지 않는다 — 단추가 이미 죽어 있지만 밖에서 불릴 수 있다. */
157
- private removeKey;
158
- }
159
- export {};