@things-factory/figure-ui 10.1.44 → 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.
- package/client/modeller/figure-v3-links.ts +303 -0
- package/client/modeller/modeller-route.ts +6 -21
- package/client/modeller/v3-editor.ts +44 -3
- package/client/modeller/v3-gizmo.ts +13 -2
- package/client/modeller/v3-links.ts +138 -0
- package/client/pages/figure-v3-modeller-page.ts +2 -5
- package/client/route.ts +2 -6
- package/dist-client/modeller/figure-v3-links.d.ts +42 -0
- package/dist-client/modeller/figure-v3-links.js +306 -0
- package/dist-client/modeller/figure-v3-links.js.map +1 -0
- package/dist-client/modeller/modeller-route.d.ts +4 -11
- package/dist-client/modeller/modeller-route.js +5 -22
- package/dist-client/modeller/modeller-route.js.map +1 -1
- package/dist-client/modeller/v3-editor.d.ts +5 -0
- package/dist-client/modeller/v3-editor.js +46 -2
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-gizmo.js +11 -1
- package/dist-client/modeller/v3-gizmo.js.map +1 -1
- package/dist-client/modeller/v3-links.d.ts +54 -0
- package/dist-client/modeller/v3-links.js +110 -0
- package/dist-client/modeller/v3-links.js.map +1 -0
- package/dist-client/pages/figure-v3-modeller-page.d.ts +2 -5
- package/dist-client/pages/figure-v3-modeller-page.js +2 -5
- package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +2 -5
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/test/ai-proposal-contract.test.ts +0 -88
- package/test/figure-source.test.ts +0 -102
- package/test/i18n-prefix-guard.test.ts +0 -24
- package/test/modeller-route.test.ts +14 -25
- package/test/part-edits.test.ts +0 -7
- package/test/scene-budget.test.ts +0 -7
- package/test/v3-gizmo.test.ts +10 -0
- package/test/v3-links.test.ts +123 -0
- package/things-factory.config.js +2 -5
- package/translations/en.json +6 -0
- package/translations/ja.json +6 -0
- package/translations/ko.json +6 -0
- package/translations/ms.json +6 -0
- package/translations/zh.json +6 -0
- package/client/modeller/angle-dial.ts +0 -70
- package/client/modeller/base-box.ts +0 -175
- package/client/modeller/figure-animations.ts +0 -1249
- package/client/modeller/figure-canvas.ts +0 -567
- package/client/modeller/figure-capabilities.ts +0 -379
- package/client/modeller/figure-inspector.ts +0 -2208
- package/client/modeller/figure-parts.ts +0 -479
- package/client/modeller/figure-preview.ts +0 -598
- package/client/modeller/figure-report.ts +0 -966
- package/client/modeller/figure-settings.ts +0 -328
- package/client/modeller/figure-side.ts +0 -272
- package/client/modeller/figure-thumbnail.ts +0 -123
- package/client/modeller/figure-view.ts +0 -152
- package/client/modeller/joint-edits.ts +0 -262
- package/client/modeller/joint-gizmo.ts +0 -180
- package/client/modeller/maturity.ts +0 -288
- package/client/modeller/new-figure-entry.ts +0 -4
- package/client/modeller/pose-defaults.ts +0 -67
- package/client/pages/figure-modeller-page.ts +0 -2082
- package/dist-client/modeller/angle-dial.d.ts +0 -32
- package/dist-client/modeller/angle-dial.js +0 -41
- package/dist-client/modeller/angle-dial.js.map +0 -1
- package/dist-client/modeller/base-box.d.ts +0 -71
- package/dist-client/modeller/base-box.js +0 -110
- package/dist-client/modeller/base-box.js.map +0 -1
- package/dist-client/modeller/figure-animations.d.ts +0 -159
- package/dist-client/modeller/figure-animations.js +0 -1132
- package/dist-client/modeller/figure-animations.js.map +0 -1
- package/dist-client/modeller/figure-canvas.d.ts +0 -150
- package/dist-client/modeller/figure-canvas.js +0 -506
- package/dist-client/modeller/figure-canvas.js.map +0 -1
- package/dist-client/modeller/figure-capabilities.d.ts +0 -42
- package/dist-client/modeller/figure-capabilities.js +0 -349
- package/dist-client/modeller/figure-capabilities.js.map +0 -1
- package/dist-client/modeller/figure-inspector.d.ts +0 -329
- package/dist-client/modeller/figure-inspector.js +0 -2095
- package/dist-client/modeller/figure-inspector.js.map +0 -1
- package/dist-client/modeller/figure-parts.d.ts +0 -64
- package/dist-client/modeller/figure-parts.js +0 -478
- package/dist-client/modeller/figure-parts.js.map +0 -1
- package/dist-client/modeller/figure-preview.d.ts +0 -138
- package/dist-client/modeller/figure-preview.js +0 -549
- package/dist-client/modeller/figure-preview.js.map +0 -1
- package/dist-client/modeller/figure-report.d.ts +0 -165
- package/dist-client/modeller/figure-report.js +0 -952
- package/dist-client/modeller/figure-report.js.map +0 -1
- package/dist-client/modeller/figure-settings.d.ts +0 -39
- package/dist-client/modeller/figure-settings.js +0 -317
- package/dist-client/modeller/figure-settings.js.map +0 -1
- package/dist-client/modeller/figure-side.d.ts +0 -78
- package/dist-client/modeller/figure-side.js +0 -317
- package/dist-client/modeller/figure-side.js.map +0 -1
- package/dist-client/modeller/figure-thumbnail.d.ts +0 -3
- package/dist-client/modeller/figure-thumbnail.js +0 -95
- package/dist-client/modeller/figure-thumbnail.js.map +0 -1
- package/dist-client/modeller/figure-view.d.ts +0 -118
- package/dist-client/modeller/figure-view.js +0 -78
- package/dist-client/modeller/figure-view.js.map +0 -1
- package/dist-client/modeller/joint-edits.d.ts +0 -49
- package/dist-client/modeller/joint-edits.js +0 -223
- package/dist-client/modeller/joint-edits.js.map +0 -1
- package/dist-client/modeller/joint-gizmo.d.ts +0 -34
- package/dist-client/modeller/joint-gizmo.js +0 -158
- package/dist-client/modeller/joint-gizmo.js.map +0 -1
- package/dist-client/modeller/maturity.d.ts +0 -55
- package/dist-client/modeller/maturity.js +0 -212
- package/dist-client/modeller/maturity.js.map +0 -1
- package/dist-client/modeller/new-figure-entry.d.ts +0 -2
- package/dist-client/modeller/new-figure-entry.js +0 -5
- package/dist-client/modeller/new-figure-entry.js.map +0 -1
- package/dist-client/modeller/pose-defaults.d.ts +0 -9
- package/dist-client/modeller/pose-defaults.js +0 -35
- package/dist-client/modeller/pose-defaults.js.map +0 -1
- package/dist-client/pages/figure-modeller-page.d.ts +0 -419
- package/dist-client/pages/figure-modeller-page.js +0 -2060
- package/dist-client/pages/figure-modeller-page.js.map +0 -1
- package/test/angle-dial.test.ts +0 -78
- package/test/base-box.test.ts +0 -235
- package/test/joint-edits.test.ts +0 -165
- package/test/joint-gizmo.test.ts +0 -174
- package/test/joint-needle.test.ts +0 -162
- package/test/modeller-undo-redo.test.ts +0 -33
- package/test/new-figure-entry.test.ts +0 -15
- package/test/pose-defaults.test.ts +0 -71
- package/test/preview-board-depth.test.ts +0 -27
package/translations/ms.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"figure.button.discard": "discard",
|
|
15
15
|
"figure.button.duplicate-part": "duplicate",
|
|
16
16
|
"figure.button.fit-base-to-parts": "muatkan pada bahagian",
|
|
17
|
+
"figure.button.fix-position": "tetapkan di sini",
|
|
17
18
|
"figure.button.mirror-x": "mirror X",
|
|
18
19
|
"figure.button.mirror-z": "mirror Z",
|
|
19
20
|
"figure.button.move-part-down": "move down",
|
|
@@ -35,13 +36,18 @@
|
|
|
35
36
|
"figure.button.snap": "snap",
|
|
36
37
|
"figure.button.take-n-changes": "apply {n}",
|
|
37
38
|
"figure.label.declared-space": "ruang yang diisytiharkan",
|
|
39
|
+
"figure.label.follows-expression": "ditetapkan oleh ungkapan",
|
|
38
40
|
"figure.label.gizmo-rotate": "putar",
|
|
39
41
|
"figure.label.gizmo-scale": "saiz",
|
|
40
42
|
"figure.label.gizmo-translate": "alih",
|
|
43
|
+
"figure.label.position-worked-out": "bergerak mengikut saiz bentuk",
|
|
41
44
|
"figure.label.primitive-rounded-box": "kotak bucu bulat",
|
|
42
45
|
"figure.text.catalog-needs-a-save": "boleh ditetapkan selepas disimpan.",
|
|
43
46
|
"figure.text.declared-space-declared": "diisytiharkan daripada bahagian.",
|
|
44
47
|
"figure.text.declared-space-none": "belum diisytiharkan.",
|
|
48
|
+
"figure.text.fasten-would-loop": "bahagian itu sudah dipasang pada bahagian ini, jadi ia tidak boleh dipasang sebaliknya.",
|
|
49
|
+
"figure.text.follow-would-loop": "dimensi itu sudah mengikut bahagian ini, jadi kedua-duanya tidak boleh saling mengikut.",
|
|
50
|
+
"figure.text.links-need-a-box": "ikut saiz untuk bahagian berbentuk kotak, pasangan untuk kotak dan silinder.",
|
|
45
51
|
"figure.text.mounting-face-needs-space": "isytiharkan ruang dahulu untuk memilih.",
|
|
46
52
|
"figure.text.part-is-referenced": "'{part}' tidak boleh dipadam: bahagian lain dipasang padanya atau mengikut saiznya. Leraikan pautan itu dahulu.",
|
|
47
53
|
"figure.text.part-name-invalid": "'{name}' tidak boleh menjadi nama bahagian. Mulakan dengan huruf dan gunakan huruf, digit, - dan _.",
|
package/translations/zh.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"figure.button.discard": "discard",
|
|
15
15
|
"figure.button.duplicate-part": "duplicate",
|
|
16
16
|
"figure.button.fit-base-to-parts": "适配部件",
|
|
17
|
+
"figure.button.fix-position": "固定在此",
|
|
17
18
|
"figure.button.mirror-x": "mirror X",
|
|
18
19
|
"figure.button.mirror-z": "mirror Z",
|
|
19
20
|
"figure.button.move-part-down": "move down",
|
|
@@ -35,13 +36,18 @@
|
|
|
35
36
|
"figure.button.snap": "snap",
|
|
36
37
|
"figure.button.take-n-changes": "apply {n}",
|
|
37
38
|
"figure.label.declared-space": "空间声明",
|
|
39
|
+
"figure.label.follows-expression": "由表达式决定",
|
|
38
40
|
"figure.label.gizmo-rotate": "旋转",
|
|
39
41
|
"figure.label.gizmo-scale": "尺寸",
|
|
40
42
|
"figure.label.gizmo-translate": "移动",
|
|
43
|
+
"figure.label.position-worked-out": "随图形尺寸移动",
|
|
41
44
|
"figure.label.primitive-rounded-box": "圆角盒",
|
|
42
45
|
"figure.text.catalog-needs-a-save": "保存后即可设置。",
|
|
43
46
|
"figure.text.declared-space-declared": "已根据部件声明。",
|
|
44
47
|
"figure.text.declared-space-none": "尚未声明。",
|
|
48
|
+
"figure.text.fasten-would-loop": "该部件已固定在此部件上,不能反向固定。",
|
|
49
|
+
"figure.text.follow-would-loop": "该尺寸已跟随此部件,二者不能互相跟随。",
|
|
50
|
+
"figure.text.links-need-a-box": "尺寸跟随适用于盒形部件,固定适用于盒形和圆柱部件。",
|
|
45
51
|
"figure.text.mounting-face-needs-space": "请先声明空间,然后才能选择。",
|
|
46
52
|
"figure.text.part-is-referenced": "无法删除 '{part}':有其他部件固定在它上面或跟随它的尺寸。请先解除该连接。",
|
|
47
53
|
"figure.text.part-name-invalid": "'{name}' 不能作为部件名称。请以字母开头,只使用字母、数字、- 和 _。",
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright © HatioLab Inc. All rights reserved.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 막대 옆에 서는 **각도 눈**. 지금 각이 범위의 어디인지를 한 눈에 보인다.
|
|
7
|
-
*
|
|
8
|
-
* ## 왜 3D 로는 모자랐나
|
|
9
|
-
*
|
|
10
|
-
* 관절이 돌아도 화면이 그대로인 경우가 있다 — 제 축에서 대칭인 부품이 그렇다. 그래서 관절이
|
|
11
|
-
* 모는 부품에 바늘을 달아 미리보기에서 함께 돌게 했는데(`joint-gizmo.ts` 의 `jointNeedles`),
|
|
12
|
-
* **미리보기에서는 그 바늘이 4 × 2 픽셀이다**(잰 값: 부품 100mm · 바늘 60 × 12mm · 여덟 대를
|
|
13
|
-
* 한 화면에 세운 상태). 미리보기는 크기 반응을 보려고 여덟 대를 나란히 놓는 자리라 한 대가
|
|
14
|
-
* 20 픽셀쯤이고, 그 위의 어떤 표시도 몇 픽셀을 넘지 못한다.
|
|
15
|
-
*
|
|
16
|
-
* 각을 읽는 일은 3D 의 배율에서 떼어 놓는다. 눈은 막대 옆 24 픽셀 자리에 서고, 도형이 아무리
|
|
17
|
-
* 작게 서 있어도 같은 크기로 읽힌다.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
export interface DialShape {
|
|
21
|
-
/** 가동 범위를 채운 부채꼴. SVG path 의 `d`. */
|
|
22
|
-
sector: string
|
|
23
|
-
/** 지금 값이 가리키는 끝점. */
|
|
24
|
-
needle: { x: number; y: number }
|
|
25
|
-
centre: { x: number; y: number }
|
|
26
|
-
radius: number
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** 0 도가 위를 가리키고, 값이 커지면 시계 방향으로 돈다 — 화면의 각과 같은 방향이다. */
|
|
30
|
-
function at(centre: { x: number; y: number }, radius: number, degrees: number) {
|
|
31
|
-
const rad = (degrees * Math.PI) / 180
|
|
32
|
-
return {
|
|
33
|
-
x: +(centre.x + radius * Math.sin(rad)).toFixed(2),
|
|
34
|
-
y: +(centre.y - radius * Math.cos(rad)).toFixed(2)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** 한 바퀴에 가까우면 부채꼴 대신 온전한 원이다. */
|
|
39
|
-
const FULL = 359.9
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 눈의 모양. 값이 범위를 벗어나면 범위 끝에 세운다 — 밖을 가리키는 바늘은 없는 값을 말한다.
|
|
43
|
-
*/
|
|
44
|
-
export function dialShape(min: number, max: number, value: number, radius = 9): DialShape {
|
|
45
|
-
const centre = { x: radius + 1, y: radius + 1 }
|
|
46
|
-
const span = Math.abs(max - min)
|
|
47
|
-
const held = Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max))
|
|
48
|
-
|
|
49
|
-
if (span >= FULL) {
|
|
50
|
-
const top = at(centre, radius, 0)
|
|
51
|
-
const bottom = at(centre, radius, 180)
|
|
52
|
-
return {
|
|
53
|
-
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`,
|
|
54
|
-
needle: at(centre, radius, held),
|
|
55
|
-
centre,
|
|
56
|
-
radius
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const from = at(centre, radius, min)
|
|
61
|
-
const to = at(centre, radius, max)
|
|
62
|
-
const large = span > 180 ? 1 : 0
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
sector: `M ${centre.x} ${centre.y} L ${from.x} ${from.y} A ${radius} ${radius} 0 ${large} 1 ${to.x} ${to.y} Z`,
|
|
66
|
-
needle: at(centre, radius, held),
|
|
67
|
-
centre,
|
|
68
|
-
radius
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright © HatioLab Inc. All rights reserved.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { rotatedExtentOf } from '@hatiolab/figure-model'
|
|
6
|
-
|
|
7
|
-
import type { FigureDraft, PartModel } from './figure-source.js'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 기준 상자를 고친다 — **부품은 제자리에 둔 채.**
|
|
11
|
-
*
|
|
12
|
-
* ## 왜 화면에 있어야 하나
|
|
13
|
-
*
|
|
14
|
-
* 기준 상자는 도면에 놓일 때의 배율을 정하는 수다(ADR-0044 ①). 부품이 그 상자를 넘으면 발행이
|
|
15
|
-
* 막히고, 고치는 길은 「상자 숫자 하나를 키우는 것」인데 **화면에 그 칸이 없었다.** 저작자는
|
|
16
|
-
* 넘쳤다는 말만 듣고 고칠 자리를 못 찾았다.
|
|
17
|
-
*
|
|
18
|
-
* ## 상자를 키우면 부품이 움직인다 — 그것을 되돌린다
|
|
19
|
-
*
|
|
20
|
-
* 저작면의 부품 자리는 판의 왼쪽 위에서 잰 값이고, 저장 형식의 자리는 **상자의 한가운데**에서 잰
|
|
21
|
-
* 값이다(`toFigureSource` 가 절반을 뺀다). 그래서 상자만 키우면 한가운데가 옮겨 가고 부품이 전부
|
|
22
|
-
* 반대로 밀린다. 여기서 늘어난 절반만큼 부품을 함께 옮겨, 저장되는 자리가 한 값도 안 바뀌게 한다.
|
|
23
|
-
*
|
|
24
|
-
* ## 「부품에 맞추기」는 한가운데를 지킨다
|
|
25
|
-
*
|
|
26
|
-
* 부품을 감싸는 가장 작은 상자를 만들 때, 부품 쪽을 **옮기지 않는다.** 한가운데에서 가장 먼
|
|
27
|
-
* 부품까지의 거리를 양쪽으로 잡는다. 부품이 한쪽으로 치우쳐 있으면 반대쪽에 여유가 남는데,
|
|
28
|
-
* 그것이 사실이다 — 치우친 것을 가운데로 끌어오면 저작자가 놓은 자리가 소리 없이 달라지고,
|
|
29
|
-
* 발행된 도형에서는 문(port)의 자리가 함께 움직인다.
|
|
30
|
-
*
|
|
31
|
-
* 높이는 밑면이 바닥이라 한쪽만 늘린다. 바닥 아래로 내려간 부품이 있으면 그 부품은 상자 밖에
|
|
32
|
-
* 남는다 — 그것을 담으려면 부품을 올려야 하고, 그것은 저작자가 정할 일이다. 발행 관문이 이미
|
|
33
|
-
* `part-outside-base` 로 말한다.
|
|
34
|
-
*
|
|
35
|
-
* ## 돌아간 부품은 돌아간 부피로 잰다
|
|
36
|
-
*
|
|
37
|
-
* 45 도 돌린 판은 제 `size` 보다 넓게 자리를 차지한다. 발행 관문은 그것을 `rotatedExtentOf` 로
|
|
38
|
-
* 재는데 여기서는 `width`·`height` 를 그대로 썼다. 그래서 **회전한 부품이 있는 도형은 「부품에
|
|
39
|
-
* 맞추기」를 눌러도 발행이 막힌 채로 남았다** — 표본에서 CONVEYOR(부품 17 중 4 가 회전)와
|
|
40
|
-
* ROLL_CRADLE(20 중 13)이 그랬다(2026-09-19 확인). 같은 함수로 재서 둘이 어긋나지 않게 한다.
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
export interface BaseSize {
|
|
44
|
-
/** 저작면의 가로 — 형식의 `base.x`. */
|
|
45
|
-
width: number
|
|
46
|
-
/** 저작면의 세로 — 형식의 `base.z`. */
|
|
47
|
-
height: number
|
|
48
|
-
/** 높이 — 형식의 `base.y`. */
|
|
49
|
-
depth: number
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** 상자는 밀리미터 하나보다 작아질 수 없다. 0 이 되면 배율이 0 으로 나뉜다. */
|
|
53
|
-
const LEAST = 1
|
|
54
|
-
|
|
55
|
-
const round = (value: number) => Math.round(value)
|
|
56
|
-
|
|
57
|
-
export const baseOf = (draft: FigureDraft): BaseSize => ({
|
|
58
|
-
width: draft.width,
|
|
59
|
-
height: draft.height,
|
|
60
|
-
depth: draft.depth
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
const DEG = 180 / Math.PI
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 이 부품이 차지하는 부피 — **형식의 축으로**.
|
|
67
|
-
*
|
|
68
|
-
* 저작면과 형식의 축 이름이 엇갈린다(`figure-source.ts` 의 표): 저작면의 `depth` 가 형식의 y 이고
|
|
69
|
-
* `height` 가 형식의 z 다. 회전 이름도 엇갈린다(`rotationY` → 형식의 z). 그 표를 여기서 다시
|
|
70
|
-
* 적지 않고 같은 모양으로 옮겨, `rotatedExtentOf` 가 관문에서와 같은 답을 내게 한다.
|
|
71
|
-
*/
|
|
72
|
-
function extentOf(part: PartModel): { x: number; y: number; z: number } {
|
|
73
|
-
return rotatedExtentOf({
|
|
74
|
-
transform: {
|
|
75
|
-
position: { x: 0, y: 0, z: 0 },
|
|
76
|
-
size: { x: part.width, y: part.depth, z: part.height },
|
|
77
|
-
rotation: {
|
|
78
|
-
x: part.rotationX ? part.rotationX * DEG : 0,
|
|
79
|
-
y: part.rotation ? -part.rotation * DEG : 0,
|
|
80
|
-
z: part.rotationY ? part.rotationY * DEG : 0
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
} as never)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 부품 전부를 **바닥에 앉힌다** — 가장 낮은 것이 상자 밑면에 닿도록 전부를 같은 값만큼 옮긴다.
|
|
88
|
-
*
|
|
89
|
-
* ## 왜 전부를 옮기나
|
|
90
|
-
*
|
|
91
|
-
* 관문이 막는 것은 「가장 낮은 부품이 밑면에 안 닿는다」다(`parts-off-placement-face`). 그 부품
|
|
92
|
-
* 하나만 내리면 관문은 통과하지만 도형이 달라진다 — 프레임에 붙어 있던 발이 떨어진다. 부품 사이의
|
|
93
|
-
* 자리는 저작자가 정한 것이므로 그것을 지키고 전체를 옮긴다.
|
|
94
|
-
*
|
|
95
|
-
* ## 위로도 옮긴다
|
|
96
|
-
*
|
|
97
|
-
* 바닥 **아래로** 내려간 부품이 있으면 올린다. 그때는 위쪽이 상자를 넘을 수 있으므로 저작자가
|
|
98
|
-
* 「부품에 맞추기」를 이어서 누르게 된다 — 두 조치가 각자 한 가지만 한다.
|
|
99
|
-
*
|
|
100
|
-
* 앉힐 것이 없으면 **받은 것을 그대로 돌려준다** — 고친 것이 없는데 「저장 안 됨」이 붙지 않게.
|
|
101
|
-
*/
|
|
102
|
-
export function partsSettledOnFloor(parts: readonly PartModel[]): readonly PartModel[] {
|
|
103
|
-
if (parts.length === 0) return parts
|
|
104
|
-
|
|
105
|
-
let lowest = Infinity
|
|
106
|
-
for (const part of parts) {
|
|
107
|
-
const extent = extentOf(part)
|
|
108
|
-
lowest = Math.min(lowest, part.zPos + part.depth / 2 - extent.y / 2)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const shift = round(lowest)
|
|
112
|
-
if (shift === 0) return parts
|
|
113
|
-
|
|
114
|
-
return parts.map(part => ({ ...part, zPos: round(part.zPos - shift) }))
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** 부품 전부를 담는 가장 작은 상자. 부품이 없으면 잴 것이 없다. */
|
|
118
|
-
export function baseFittingParts(draft: FigureDraft, parts: readonly PartModel[]): BaseSize | undefined {
|
|
119
|
-
if (parts.length === 0) return undefined
|
|
120
|
-
|
|
121
|
-
const middle = { x: draft.width / 2, y: draft.height / 2 }
|
|
122
|
-
let half = { x: 0, y: 0 }
|
|
123
|
-
let tallest = 0
|
|
124
|
-
|
|
125
|
-
for (const part of parts) {
|
|
126
|
-
/* 돌아간 부피. 관문이 재는 것과 같은 함수로 잰다 — 다르게 재면 눌러도 막힌 채로 남는다. */
|
|
127
|
-
const extent = extentOf(part)
|
|
128
|
-
const centre = {
|
|
129
|
-
x: part.left + part.width / 2,
|
|
130
|
-
y: part.top + part.height / 2,
|
|
131
|
-
z: part.zPos + part.depth / 2
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
half = {
|
|
135
|
-
x: Math.max(half.x, Math.abs(centre.x - extent.x / 2 - middle.x), Math.abs(centre.x + extent.x / 2 - middle.x)),
|
|
136
|
-
y: Math.max(half.y, Math.abs(centre.y - extent.z / 2 - middle.y), Math.abs(centre.y + extent.z / 2 - middle.y))
|
|
137
|
-
}
|
|
138
|
-
tallest = Math.max(tallest, centre.z + extent.y / 2)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
width: Math.max(LEAST, round(half.x * 2)),
|
|
143
|
-
height: Math.max(LEAST, round(half.y * 2)),
|
|
144
|
-
depth: Math.max(LEAST, round(tallest))
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* 상자를 이 크기로. 부품은 저장되는 자리가 그대로이도록 함께 옮긴다.
|
|
150
|
-
*
|
|
151
|
-
* 달라질 것이 없으면 **받은 것을 그대로 돌려준다** — 고친 것이 없는데 「저장 안 됨」이 붙지 않게.
|
|
152
|
-
*/
|
|
153
|
-
export function resizeBase(
|
|
154
|
-
draft: FigureDraft,
|
|
155
|
-
parts: readonly PartModel[],
|
|
156
|
-
next: BaseSize
|
|
157
|
-
): { draft: FigureDraft; parts: readonly PartModel[] } {
|
|
158
|
-
const size = {
|
|
159
|
-
width: Math.max(LEAST, round(next.width)),
|
|
160
|
-
height: Math.max(LEAST, round(next.height)),
|
|
161
|
-
depth: Math.max(LEAST, round(next.depth))
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (size.width === draft.width && size.height === draft.height && size.depth === draft.depth) {
|
|
165
|
-
return { draft, parts }
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const shift = { x: (size.width - draft.width) / 2, y: (size.height - draft.height) / 2 }
|
|
169
|
-
const moved =
|
|
170
|
-
shift.x === 0 && shift.y === 0
|
|
171
|
-
? parts
|
|
172
|
-
: parts.map(part => ({ ...part, left: part.left + shift.x, top: part.top + shift.y }))
|
|
173
|
-
|
|
174
|
-
return { draft: { ...draft, ...size }, parts: moved }
|
|
175
|
-
}
|