@things-factory/figure-ui 10.1.83 → 10.1.85
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/command-preview.ts +4 -2
- package/client/modeller/drawn-shapes.ts +29 -0
- package/client/modeller/editor.ts +17 -1
- package/client/modeller/figure-material-panel.ts +2 -3
- package/client/modeller/figure-parts.ts +13 -15
- package/client/modeller/figure-section-panel.ts +304 -0
- package/client/modeller/gizmo.ts +26 -16
- package/client/modeller/joint-fan.ts +4 -3
- package/client/modeller/links.ts +24 -24
- package/client/modeller/live.ts +8 -4
- package/client/modeller/maturity.ts +12 -6
- package/client/modeller/motion-ends.ts +2 -1
- package/client/modeller/refusals.ts +20 -0
- package/client/modeller/renderer.ts +3 -2
- package/client/modeller/section-controls.ts +115 -0
- package/client/modeller/starting-shape.ts +34 -0
- package/dist-client/modeller/command-preview.js +4 -2
- package/dist-client/modeller/command-preview.js.map +1 -1
- package/dist-client/modeller/drawn-shapes.d.ts +44 -0
- package/dist-client/modeller/drawn-shapes.js +12 -0
- package/dist-client/modeller/drawn-shapes.js.map +1 -0
- package/dist-client/modeller/editor.d.ts +3 -0
- package/dist-client/modeller/editor.js +16 -1
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-capability-panel.d.ts +1 -1
- package/dist-client/modeller/figure-clips.d.ts +1 -1
- package/dist-client/modeller/figure-controls.d.ts +1 -1
- package/dist-client/modeller/figure-links.d.ts +1 -1
- package/dist-client/modeller/figure-material-panel.d.ts +1 -1
- package/dist-client/modeller/figure-material-panel.js +2 -3
- package/dist-client/modeller/figure-material-panel.js.map +1 -1
- package/dist-client/modeller/figure-parts.d.ts +3 -2
- package/dist-client/modeller/figure-parts.js +7 -12
- package/dist-client/modeller/figure-parts.js.map +1 -1
- package/dist-client/modeller/figure-properties.d.ts +1 -1
- package/dist-client/modeller/figure-section-panel.d.ts +42 -0
- package/dist-client/modeller/figure-section-panel.js +289 -0
- package/dist-client/modeller/figure-section-panel.js.map +1 -0
- package/dist-client/modeller/gizmo.d.ts +3 -3
- package/dist-client/modeller/gizmo.js +24 -11
- package/dist-client/modeller/gizmo.js.map +1 -1
- package/dist-client/modeller/joint-fan.js +4 -3
- package/dist-client/modeller/joint-fan.js.map +1 -1
- package/dist-client/modeller/links.d.ts +4 -1
- package/dist-client/modeller/links.js +23 -25
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/modeller/live.d.ts +3 -1
- package/dist-client/modeller/live.js +6 -3
- package/dist-client/modeller/live.js.map +1 -1
- package/dist-client/modeller/maturity.d.ts +3 -2
- package/dist-client/modeller/maturity.js +9 -6
- package/dist-client/modeller/maturity.js.map +1 -1
- package/dist-client/modeller/motion-ends.js +2 -1
- package/dist-client/modeller/motion-ends.js.map +1 -1
- package/dist-client/modeller/refusals.d.ts +0 -5
- package/dist-client/modeller/refusals.js +25 -0
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/modeller/renderer.d.ts +3 -4
- package/dist-client/modeller/renderer.js +3 -2
- package/dist-client/modeller/renderer.js.map +1 -1
- package/dist-client/modeller/section-controls.d.ts +44 -0
- package/dist-client/modeller/section-controls.js +91 -0
- package/dist-client/modeller/section-controls.js.map +1 -0
- package/dist-client/modeller/starting-shape.d.ts +32 -0
- package/dist-client/modeller/starting-shape.js +37 -0
- package/dist-client/modeller/starting-shape.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/test/browser/acceptance-page.js +8 -0
- package/test/command-preview.test.ts +3 -2
- package/test/fixtures/joint-column.json +110 -15
- package/test/fixtures/joint-roller.json +66 -9
- package/test/fixtures/robot-arm-6dof.json +203 -27
- package/test/joint-fan.test.ts +10 -7
- package/test/links.test.ts +4 -4
- package/test/material.test.ts +7 -2
- package/test/palette-icons.test.ts +1 -1
- package/test/parts-wiring.test.ts +6 -2
- package/test/renderer.test.ts +2 -1
- package/test/resize-with-placement.test.ts +3 -3
- package/test/scene-canvas.test.ts +2 -1
- package/test/section-panel.test.ts +192 -0
- package/translations/en.json +24 -0
- package/translations/ja.json +24 -0
- package/translations/ko.json +24 -0
- package/translations/ms.json +24 -0
- package/translations/zh.json +24 -0
- package/dist-client/tsconfig.localkernel.tsbuildinfo +0 -1
|
@@ -45,8 +45,8 @@ for (const mode of ['floor', 'ceiling', 'wall', 'center'] as const) {
|
|
|
45
45
|
/* At twice the height the kept face is where it was. */
|
|
46
46
|
const asset = parseFigureAsset(session.source())
|
|
47
47
|
asset.designInputs['size.y'] = asset.designInputs['size.y']! * 2
|
|
48
|
-
const g = compileFigureAsset(asset).evaluate().geometry.find(g => g.id === 'box')! as unknown as { pose: { t: number[] }; shape: { dimensions:
|
|
49
|
-
const y = g.pose.t[1]!, h = g.shape.dimensions.
|
|
48
|
+
const g = compileFigureAsset(asset).evaluate().geometry.find(g => g.id === 'box')! as unknown as { pose: { t: number[] }; shape: { dimensions: Record<string, number> } }
|
|
49
|
+
const y = g.pose.t[1]!, h = g.shape.dimensions.length!
|
|
50
50
|
if (mode === 'floor') assert.equal(y - h / 2, 0)
|
|
51
51
|
if (mode === 'ceiling') assert.equal(y + h / 2, 0)
|
|
52
52
|
if (mode === 'center') assert.equal(y, 50)
|
|
@@ -81,7 +81,7 @@ test('fresh, width only, and all set back to fixed are three different answers',
|
|
|
81
81
|
const resized = applyFigureProposal(a, planFigureResize(a, { part: 'box', axes: only.axes }).steps)
|
|
82
82
|
for (const axis of ['y', 'z'] as const) assert.equal(figureFollowOf(resized, 'box', axis).of, 'own', `${axis} stays fixed`)
|
|
83
83
|
const g = compileFigureAsset({ ...resized, designInputs: { ...resized.designInputs, 'size.x': 200, 'size.y': 400, 'size.z': 400 } }).evaluate().geometry.find(g => g.id === 'box') as unknown as { shape: { dimensions: Record<string, number> }; pose: { t: number[] } }
|
|
84
|
-
assert.deepEqual(g.shape.dimensions,
|
|
84
|
+
assert.deepEqual([g.shape.dimensions['size.x'], g.shape.dimensions.length, g.shape.dimensions['size.z']], [200, 100, 100], 'width 200; height and depth kept')
|
|
85
85
|
assert.equal(g.pose.t[1]! - 50, 0, 'and it stands on the floor')
|
|
86
86
|
/* All set back to fixed: nothing is taken for "all". */
|
|
87
87
|
let fixed = applyFigureAuthoring(a, { kind: 'follow-size', part: 'box', axes: ['x'] })
|
|
@@ -12,6 +12,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
12
12
|
|
|
13
13
|
import { changedKeys, componentOf, gizmoResultOf } from '../client/modeller/stage-model.ts'
|
|
14
14
|
import type { FigureRenderPart } from '../client/modeller/renderer.ts'
|
|
15
|
+
import { boxShape } from '../client/modeller/drawn-shapes.ts'
|
|
15
16
|
|
|
16
17
|
const read = (path: string) => readFileSync(fileURLToPath(new URL(path, import.meta.url)), 'utf8')
|
|
17
18
|
const canvas = read('../client/modeller/canvas.ts')
|
|
@@ -27,7 +28,7 @@ const turned = (deg: number) => {
|
|
|
27
28
|
}
|
|
28
29
|
const box = (t: number[], r = turned(0)): FigureRenderPart => ({
|
|
29
30
|
id: 'block',
|
|
30
|
-
shape:
|
|
31
|
+
shape: boxShape(200, 100, 60),
|
|
31
32
|
pose: { r, t },
|
|
32
33
|
material: { token: 'palette.primary' }
|
|
33
34
|
})
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* **단면 칸 — 모서리와 속 공간** (ADR-0100 1단계, 2026-09-29).
|
|
3
|
+
*
|
|
4
|
+
* 칸이 보내는 명령이 figure-model 에서 정말로 받아들여져 그려지는 몸체를 바꾸는지, 받아들여지지 않을 때 화면이 그 까닭을
|
|
5
|
+
* 사람의 말로 하는지를 잰다.
|
|
6
|
+
*/
|
|
7
|
+
import { test } from 'node:test'
|
|
8
|
+
import assert from 'node:assert/strict'
|
|
9
|
+
import { readFileSync } from 'node:fs'
|
|
10
|
+
import { fileURLToPath } from 'node:url'
|
|
11
|
+
|
|
12
|
+
import { addFigureBox, applyFigureAuthoring, compileFigureAsset, createFigureAsset } from '@hatiolab/figure-model'
|
|
13
|
+
import type { FigureAsset, FigureEvaluatedShape } from '@hatiolab/figure-model'
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
cavityDepthOf,
|
|
17
|
+
cavityRefusalOf,
|
|
18
|
+
cavityRequestOf,
|
|
19
|
+
cavityResultOf,
|
|
20
|
+
cornerLimitOf,
|
|
21
|
+
cornerRefusalOf,
|
|
22
|
+
defaultWallOf,
|
|
23
|
+
wallLimitOf
|
|
24
|
+
} from '../client/modeller/section-controls.ts'
|
|
25
|
+
import { livePartsOf } from '../client/modeller/live.ts'
|
|
26
|
+
import { editRefusalOf } from '../client/modeller/refusals.ts'
|
|
27
|
+
|
|
28
|
+
const read = (path: string) => readFileSync(fileURLToPath(new URL(path, import.meta.url)), 'utf8')
|
|
29
|
+
const panel = read('../client/modeller/figure-section-panel.ts')
|
|
30
|
+
const editor = read('../client/modeller/editor.ts')
|
|
31
|
+
const ko = JSON.parse(read('../translations/ko.json')) as Record<string, string>
|
|
32
|
+
|
|
33
|
+
const probe = () => addFigureBox(createFigureAsset('Probe'), { id: 'body', size: { x: 400, y: 200, z: 300 }, position: { x: 0, y: 100, z: 0 } })
|
|
34
|
+
const shapeOf = (asset: FigureAsset, id = 'body') =>
|
|
35
|
+
(compileFigureAsset(asset).evaluate().geometry.find(g => (g as { id: string }).id === id) as unknown as { shape: FigureEvaluatedShape }).shape
|
|
36
|
+
const say = (key: string, params: Record<string, string>) => Object.entries(params).reduce((s, [k, v]) => s.replace(`{${k}}`, v), ko[key] ?? key)
|
|
37
|
+
const refusal = (run: () => unknown) => {
|
|
38
|
+
try {
|
|
39
|
+
run()
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return editRefusalOf(error, say)
|
|
42
|
+
}
|
|
43
|
+
assert.fail('the command was taken')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
test('모서리 반지름은 네 모서리를 한 값으로 둥글게 하고, 0 은 둥글림을 없앤다', () => {
|
|
47
|
+
const round = applyFigureAuthoring(probe(), { kind: 'set-section-corner', part: 'body', corner: 40 })
|
|
48
|
+
const shape = shapeOf(round)
|
|
49
|
+
assert.deepEqual(shape.extrusion!.section, { kind: 'rectangle', size: { x: 400, z: 300 }, corner: { x: 40, z: 40 } })
|
|
50
|
+
const sharp = applyFigureAuthoring(round, { kind: 'set-section-corner', part: 'body', corner: null })
|
|
51
|
+
assert.equal(shapeOf(sharp).extrusion!.section.kind, 'rectangle')
|
|
52
|
+
assert.equal('corner' in shapeOf(sharp).extrusion!.section, false)
|
|
53
|
+
assert.match(panel, /corner: value > 0 \? value : null/, 'the field sends 0 as no rounding')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('속을 비우면 기본 벽·막힌 바닥·열린 윗면의 쟁반이 되고, 끝과 벽을 하나씩 바꾼다', () => {
|
|
57
|
+
const before = shapeOf(probe())
|
|
58
|
+
assert.equal(defaultWallOf(before), 30, 'a tenth of the narrower side (300)')
|
|
59
|
+
const tray = applyFigureAuthoring(probe(), { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(before) })
|
|
60
|
+
const shape = shapeOf(tray)
|
|
61
|
+
assert.deepEqual(shape.cavity!.start, { closed: 30 })
|
|
62
|
+
assert.equal(shape.cavity!.end, 'open')
|
|
63
|
+
assert.deepEqual(Object.values(shape.cavity!.wall), [30, 30, 30, 30])
|
|
64
|
+
const thin = applyFigureAuthoring(tray, { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shape, { wall: 5 }) })
|
|
65
|
+
assert.deepEqual(Object.values(shapeOf(thin).cavity!.wall), [5, 5, 5, 5])
|
|
66
|
+
assert.deepEqual(shapeOf(thin).cavity!.start, { closed: 30 }, 'the ends stay as they were')
|
|
67
|
+
const lidded = applyFigureAuthoring(thin, { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shapeOf(thin), { end: { closed: 8 } }) })
|
|
68
|
+
assert.deepEqual(shapeOf(lidded).cavity!.end, { closed: 8 })
|
|
69
|
+
const solid = applyFigureAuthoring(lidded, { kind: 'set-cavity', part: 'body', cavity: null })
|
|
70
|
+
assert.equal(shapeOf(solid).cavity, undefined)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('벽이 단면을 다 채우거나 모서리가 변의 절반을 넘으면, 그 까닭을 말로 한다', () => {
|
|
74
|
+
const tray = applyFigureAuthoring(probe(), { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shapeOf(probe())) })
|
|
75
|
+
assert.equal(
|
|
76
|
+
refusal(() => applyFigureAuthoring(tray, { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shapeOf(tray), { wall: 150 }) })),
|
|
77
|
+
ko['figure.text.section-wall-too-thick']
|
|
78
|
+
)
|
|
79
|
+
assert.equal(
|
|
80
|
+
refusal(() => applyFigureAuthoring(tray, { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shapeOf(tray), { start: { closed: 120 }, end: { closed: 90 } }) })),
|
|
81
|
+
ko['figure.text.section-ends-too-thick']
|
|
82
|
+
)
|
|
83
|
+
assert.equal(refusal(() => applyFigureAuthoring(probe(), { kind: 'set-section-corner', part: 'body', corner: 160 })), ko['figure.text.section-corner-too-large'])
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('편집기는 단면 칸의 명령을 세션의 이력 하나로 돌리고, 치는 동안의 값은 그리기만 한다', () => {
|
|
87
|
+
assert.match(editor, /<figure-section-panel[\s\S]*?@command=\$\{\(e: CustomEvent\) => this\.linkCommand\(e\.detail\.action\)\}/)
|
|
88
|
+
assert.match(editor, /@command-live=\$\{\(e: CustomEvent\) => this\.holdAction\(e\.detail\.action\)\}/)
|
|
89
|
+
assert.match(editor, /private linkCommand\(action: FigureAuthoringAction\) \{\s*\/\*[^*]*\*\/\s*this\.holdAction\(null\)/, 'a command sent lets go of the one in hand')
|
|
90
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
91
|
+
const words = JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>
|
|
92
|
+
const used = [...new Set([...panel.matchAll(/'(figure\.(?:label|text)\.section-[\w-]+|figure\.label\.group-section)'/g)].map(m => m[1]!))]
|
|
93
|
+
/* The keys the panel builds: each result, and the wall's and the ends' over-limit words. */
|
|
94
|
+
used.push(...['pipe', 'container', 'sealed'].map(r => `figure.label.section-result-${r}`), 'figure.text.section-wall-over', 'figure.text.section-ends-over')
|
|
95
|
+
assert.ok(used.length >= 16)
|
|
96
|
+
for (const key of used) assert.ok(words[key], `${lang} has ${key}`)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('화면에 커널 말을 쓰지 않는다 — 「속 비우기」·「중공」·「공동」이 아니라 「속 공간」 (ADR-0100 §2.1)', () => {
|
|
101
|
+
const words = Object.entries(ko).filter(([k]) => k.startsWith('figure.label.section-') || k.startsWith('figure.text.section-'))
|
|
102
|
+
for (const [key, text] of words) assert.doesNotMatch(text, /속\s*비우|속을 비움|중공|공동/, key)
|
|
103
|
+
assert.equal(ko['figure.label.section-cavity'], '속 공간 만들기')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('모서리 반지름의 허용값은 커널이 정한 그대로다 — 짧은 변의 절반까지', () => {
|
|
107
|
+
const shape = shapeOf(probe())
|
|
108
|
+
assert.equal(cornerLimitOf(shape), 150)
|
|
109
|
+
assert.equal(cornerRefusalOf(shape, 150), null)
|
|
110
|
+
assert.deepEqual(cornerRefusalOf(shape, 151), { max: 150 })
|
|
111
|
+
/* The screen's limit is the kernel's: the command takes 150 and refuses 151. */
|
|
112
|
+
assert.doesNotThrow(() => shapeOf(applyFigureAuthoring(probe(), { kind: 'set-section-corner', part: 'body', corner: 150 })))
|
|
113
|
+
assert.throws(() => applyFigureAuthoring(probe(), { kind: 'set-section-corner', part: 'body', corner: 151 }))
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('벽과 막힌 끝의 허용값도 커널이 정한 그대로다 — 칸은 고칠 값을 말하고 보내지 않는다', () => {
|
|
117
|
+
const tray = applyFigureAuthoring(probe(), { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shapeOf(probe())) })
|
|
118
|
+
const shape = shapeOf(tray)
|
|
119
|
+
assert.equal(wallLimitOf(shape), 150)
|
|
120
|
+
assert.deepEqual(cavityRefusalOf(shape, cavityRequestOf(shape, { wall: 150 })), { code: 'wall-too-thick', max: 150 })
|
|
121
|
+
assert.equal(cavityRefusalOf(shape, cavityRequestOf(shape, { wall: 149 })), null)
|
|
122
|
+
assert.doesNotThrow(() => applyFigureAuthoring(tray, { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shape, { wall: 149 }) }))
|
|
123
|
+
assert.throws(() => applyFigureAuthoring(tray, { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shape, { wall: 150 }) }))
|
|
124
|
+
assert.deepEqual(cavityRefusalOf(shape, cavityRequestOf(shape, { start: { closed: 120 }, end: { closed: 80 } })), { code: 'ends-too-thick', max: 200 })
|
|
125
|
+
/* One side thicker than the rest: the kernel still says whether the inside has room. */
|
|
126
|
+
assert.equal(cavityRefusalOf(shape, cavityRequestOf(shape, { wall: { 'x-': 396, 'x+': 5, 'z-': 5, 'z+': 5 } })).code, 'wall-too-thick')
|
|
127
|
+
assert.equal(cavityRefusalOf(shape, cavityRequestOf(shape, { wall: { 'x-': 100, 'x+': 5, 'z-': 5, 'z+': 5 } })), null)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('속 공간은 무엇이 되었는지와 깊이로 보인다 — 한쪽이 열리면 용기, 양쪽이면 관, 양쪽이 막히면 닫힌 속 공간', () => {
|
|
131
|
+
const tray = shapeOf(applyFigureAuthoring(probe(), { kind: 'set-cavity', part: 'body', cavity: cavityRequestOf(shapeOf(probe())) }))
|
|
132
|
+
assert.equal(cavityResultOf(tray.cavity!), 'container')
|
|
133
|
+
assert.equal(cavityDepthOf(tray), 170, 'the 200 mm push less its 30 mm floor')
|
|
134
|
+
assert.equal(cavityResultOf({ ...tray.cavity!, start: 'open' }), 'pipe')
|
|
135
|
+
assert.equal(cavityResultOf({ ...tray.cavity!, end: { closed: 5 } }), 'sealed')
|
|
136
|
+
assert.match(panel, /figure\.text\.section-depth/)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('치는 동안의 값은 사본에 그려지고 원본은 그대로다 — 받을 수 없는 값은 그리지 않는다', () => {
|
|
140
|
+
const asset = probe()
|
|
141
|
+
const drawn = livePartsOf(asset, { values: {}, action: { kind: 'set-section-corner', part: 'body', corner: 40 } })!
|
|
142
|
+
const body = drawn.find(p => p.id === 'body')! as unknown as { shape: FigureEvaluatedShape }
|
|
143
|
+
assert.deepEqual(body.shape.extrusion!.section, { kind: 'rectangle', size: { x: 400, z: 300 }, corner: { x: 40, z: 40 } })
|
|
144
|
+
assert.equal('corner' in shapeOf(asset).extrusion!.section, false, 'the asset itself is not changed')
|
|
145
|
+
assert.equal(livePartsOf(asset, { values: {}, action: { kind: 'set-section-corner', part: 'body', corner: 999 } }), null)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('다각 단면에는 속 공간을 만들 수 없다고 그 자리에서 말한다', () => {
|
|
149
|
+
assert.match(panel, /kind === 'rectangle' \|\| kind === 'ellipse'\s*\? this\.renderCavity\(body, e\.axis\)\s*: html`[\s\S]*?figure\.text\.section-cavity-unavailable/)
|
|
150
|
+
assert.match(ko['figure.text.section-cavity-unavailable']!, /이 윤곽에는 속 공간을 만들 수 없습니다/)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('붙어 있는 것이 기대는 면을 없애는 편집은 거절되고, 그 면을 사람의 말로 부른다', () => {
|
|
154
|
+
let asset = applyFigureAuthoring(probe(), { kind: 'set-cavity', part: 'body', cavity: { wall: 10, start: { closed: 15 }, end: 'open' } })
|
|
155
|
+
asset = addFigureBox(asset, { id: 'divider', size: { x: 10, y: 100, z: 200 }, position: { x: 0, y: 0, z: 0 } })
|
|
156
|
+
asset = applyFigureAuthoring(asset, { kind: 'attach', part: 'divider', face: 'bottom', to: { part: 'body', face: 'inside-bottom' as never } })
|
|
157
|
+
const said = refusal(() => applyFigureAuthoring(asset, { kind: 'set-cavity', part: 'body', cavity: { wall: 10, start: 'open', end: 'open' } }))
|
|
158
|
+
assert.equal(said, say('figure.text.section-face-in-use', { faces: ko['figure.label.face-inside-bottom']! }))
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('치는 동안 그려진 도형이 칸의 글자를 덮어쓰지 않는다 — 친 것은 보낼 때까지 칸이 든다', () => {
|
|
162
|
+
assert.match(panel, /\.value=\$\{live\(this\.typed\[key\] \?\? shown\)\}/)
|
|
163
|
+
assert.match(panel, /this\.typed = \{ \.\.\.this\.typed, \[key\]: \(ev\.target as HTMLInputElement\)\.value \}/)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('면마다 벽 두께를 치는 동안에도 그려진다 — 명령이 없앤 입력(한 벽 두께)의 지금 값은 미리보기를 막지 않는다', async () => {
|
|
167
|
+
const { liveOverSession } = await import('../client/modeller/live.ts')
|
|
168
|
+
const tray = applyFigureAuthoring(probe(), { kind: 'set-cavity', part: 'body', cavity: { wall: 30, start: { closed: 30 }, end: 'open' } })
|
|
169
|
+
/* The editor draws over every value as it stands: the one wall is among them. */
|
|
170
|
+
const current = { ...tray.designInputs }
|
|
171
|
+
assert.ok('body.dimension.wall' in current)
|
|
172
|
+
const drawn = liveOverSession(tray, current, {
|
|
173
|
+
values: {},
|
|
174
|
+
action: { kind: 'set-cavity', part: 'body', cavity: { wall: { 'x-': 100, 'x+': 30, 'z-': 30, 'z+': 30 }, start: { closed: 30 }, end: 'open' } }
|
|
175
|
+
})
|
|
176
|
+
assert.ok(drawn, 'the preview is drawn')
|
|
177
|
+
const body = drawn!.find(p => p.id === 'body') as unknown as { shape: FigureEvaluatedShape }
|
|
178
|
+
assert.equal(body.shape.cavity!.wall['x-'], 100)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test('한 면의 벽 한도는 그 방향의 너비에서 맞은편 벽을 뺀 값이다 — 네 면이 같을 때의 한도가 아니다', async () => {
|
|
182
|
+
const { sideWallLimitOf } = await import('../client/modeller/section-controls.ts')
|
|
183
|
+
const tray = applyFigureAuthoring(probe(), { kind: 'set-cavity', part: 'body', cavity: { wall: 30, start: { closed: 30 }, end: 'open' } })
|
|
184
|
+
const shape = shapeOf(tray)
|
|
185
|
+
const walls = { 'x-': 30, 'x+': 40, 'z-': 30, 'z+': 30 }
|
|
186
|
+
assert.equal(sideWallLimitOf(shape, walls, 'x-'), 360, 'the 400 mm width less the 40 mm wall opposite')
|
|
187
|
+
assert.equal(sideWallLimitOf(shape, walls, 'z+'), 270)
|
|
188
|
+
/* The kernel agrees: just under takes, the limit refuses. */
|
|
189
|
+
assert.equal(cavityRefusalOf(shape, cavityRequestOf(shape, { wall: { ...walls, 'x-': 359 } })), null)
|
|
190
|
+
assert.equal(cavityRefusalOf(shape, cavityRequestOf(shape, { wall: { ...walls, 'x-': 360 } }))!.code, 'wall-too-thick')
|
|
191
|
+
assert.match(panel, /sideWallLimitOf\(body\.shape, cavity\.wall, side\)/)
|
|
192
|
+
})
|
package/translations/en.json
CHANGED
|
@@ -188,6 +188,30 @@
|
|
|
188
188
|
"figure.label.palette-token": "palette token",
|
|
189
189
|
"figure.label.part": "part",
|
|
190
190
|
"figure.label.part-material": "material",
|
|
191
|
+
"figure.text.section-cavity-unavailable": "This outline cannot have an inside. An inside is made in a rectangle or circle section.",
|
|
192
|
+
"figure.text.section-corner-over": "The corner radius can be at most {max} mm.",
|
|
193
|
+
"figure.text.section-at-most": "at most {max} mm",
|
|
194
|
+
"figure.text.section-not-positive": "The thickness must be more than 0.",
|
|
195
|
+
"figure.text.section-wall-over": "The wall must be thinner than {max} mm.",
|
|
196
|
+
"figure.text.section-ends-over": "The two closed ends together must be thinner than {max} mm.",
|
|
197
|
+
"figure.text.section-depth": "{result} · {depth} mm deep",
|
|
198
|
+
"figure.label.section-result-pipe": "pipe",
|
|
199
|
+
"figure.label.section-result-container": "container",
|
|
200
|
+
"figure.label.section-result-sealed": "closed inside",
|
|
201
|
+
"figure.text.section-wall-at-most": "thinner than {max} mm",
|
|
202
|
+
"figure.label.section-wall-per-side": "per side",
|
|
203
|
+
"figure.label.group-section": "section",
|
|
204
|
+
"figure.label.section-corner": "corner radius",
|
|
205
|
+
"figure.text.section-corner-differs": "differs by axis",
|
|
206
|
+
"figure.label.section-cavity": "make an inside",
|
|
207
|
+
"figure.label.section-wall": "wall thickness",
|
|
208
|
+
"figure.label.section-end-closed": "closed",
|
|
209
|
+
"figure.label.section-end-open": "open",
|
|
210
|
+
"figure.text.section-face-in-use": "Not changed: something is attached to {faces}. Detach it first.",
|
|
211
|
+
"figure.text.section-dimension-linked": "This dimension follows another value. Unlink it before changing it.",
|
|
212
|
+
"figure.text.section-corner-too-large": "The corner radius is more than half the side.",
|
|
213
|
+
"figure.text.section-wall-too-thick": "The walls are too thick to leave an inside.",
|
|
214
|
+
"figure.text.section-ends-too-thick": "The closed ends are too thick to leave an inside.",
|
|
191
215
|
"figure.label.part-position": "position (centre)",
|
|
192
216
|
"figure.label.part-size": "size",
|
|
193
217
|
"figure.label.parts": "parts",
|
package/translations/ja.json
CHANGED
|
@@ -188,6 +188,30 @@
|
|
|
188
188
|
"figure.label.palette-token": "palette token",
|
|
189
189
|
"figure.label.part": "part",
|
|
190
190
|
"figure.label.part-material": "material",
|
|
191
|
+
"figure.text.section-cavity-unavailable": "この輪郭には内部の空間をつくれません。内部の空間は四角形と円の断面につくります。",
|
|
192
|
+
"figure.text.section-corner-over": "角の半径は {max} mm までです。",
|
|
193
|
+
"figure.text.section-at-most": "最大 {max} mm",
|
|
194
|
+
"figure.text.section-not-positive": "厚さは 0 より大きくしてください。",
|
|
195
|
+
"figure.text.section-wall-over": "壁の厚さは {max} mm より薄くしてください。",
|
|
196
|
+
"figure.text.section-ends-over": "閉じた両端の厚さの合計は {max} mm より薄くしてください。",
|
|
197
|
+
"figure.text.section-depth": "{result} · 深さ {depth} mm",
|
|
198
|
+
"figure.label.section-result-pipe": "管",
|
|
199
|
+
"figure.label.section-result-container": "容器",
|
|
200
|
+
"figure.label.section-result-sealed": "閉じた内部",
|
|
201
|
+
"figure.text.section-wall-at-most": "{max} mm より薄く",
|
|
202
|
+
"figure.label.section-wall-per-side": "面ごとに",
|
|
203
|
+
"figure.label.group-section": "断面",
|
|
204
|
+
"figure.label.section-corner": "角の半径",
|
|
205
|
+
"figure.text.section-corner-differs": "軸ごとに異なる",
|
|
206
|
+
"figure.label.section-cavity": "内部の空間をつくる",
|
|
207
|
+
"figure.label.section-wall": "壁の厚さ",
|
|
208
|
+
"figure.label.section-end-closed": "閉じる",
|
|
209
|
+
"figure.label.section-end-open": "開く",
|
|
210
|
+
"figure.text.section-face-in-use": "変更しませんでした: {faces} に取り付けられたものがあります。先に外してください。",
|
|
211
|
+
"figure.text.section-dimension-linked": "この寸法は他の値に従います。連動を解除してから変更してください。",
|
|
212
|
+
"figure.text.section-corner-too-large": "角の半径が辺の半分より大きいです。",
|
|
213
|
+
"figure.text.section-wall-too-thick": "壁が厚く、内部の空間が残りません。",
|
|
214
|
+
"figure.text.section-ends-too-thick": "閉じた端が厚く、内部の空間が残りません。",
|
|
191
215
|
"figure.label.part-position": "position (centre)",
|
|
192
216
|
"figure.label.part-size": "size",
|
|
193
217
|
"figure.label.parts": "parts",
|
package/translations/ko.json
CHANGED
|
@@ -188,6 +188,30 @@
|
|
|
188
188
|
"figure.label.palette-token": "팔레트 토큰",
|
|
189
189
|
"figure.label.part": "부품",
|
|
190
190
|
"figure.label.part-material": "재질",
|
|
191
|
+
"figure.text.section-cavity-unavailable": "이 윤곽에는 속 공간을 만들 수 없습니다. 속 공간은 사각형과 원 단면에 만듭니다.",
|
|
192
|
+
"figure.text.section-corner-over": "모서리 반지름은 {max} mm 까지입니다.",
|
|
193
|
+
"figure.text.section-at-most": "최대 {max} mm",
|
|
194
|
+
"figure.text.section-not-positive": "두께는 0 보다 커야 합니다.",
|
|
195
|
+
"figure.text.section-wall-over": "벽 두께는 {max} mm 보다 얇아야 합니다.",
|
|
196
|
+
"figure.text.section-ends-over": "막힌 두 끝의 두께를 더해 {max} mm 보다 얇아야 합니다.",
|
|
197
|
+
"figure.text.section-depth": "{result} · 깊이 {depth} mm",
|
|
198
|
+
"figure.label.section-result-pipe": "관",
|
|
199
|
+
"figure.label.section-result-container": "용기",
|
|
200
|
+
"figure.label.section-result-sealed": "닫힌 속 공간",
|
|
201
|
+
"figure.text.section-wall-at-most": "{max} mm 보다 얇게",
|
|
202
|
+
"figure.label.section-wall-per-side": "면마다 따로",
|
|
203
|
+
"figure.label.group-section": "단면",
|
|
204
|
+
"figure.label.section-corner": "모서리 반지름",
|
|
205
|
+
"figure.text.section-corner-differs": "축마다 다름",
|
|
206
|
+
"figure.label.section-cavity": "속 공간 만들기",
|
|
207
|
+
"figure.label.section-wall": "벽 두께",
|
|
208
|
+
"figure.label.section-end-closed": "막힘",
|
|
209
|
+
"figure.label.section-end-open": "열림",
|
|
210
|
+
"figure.text.section-face-in-use": "이 면에 붙어 있는 것이 있어 바꾸지 않았습니다: {faces}. 먼저 떼어 냅니다.",
|
|
211
|
+
"figure.text.section-dimension-linked": "이 치수는 다른 값을 따릅니다. 따르기를 풀고 고칩니다.",
|
|
212
|
+
"figure.text.section-corner-too-large": "모서리 반지름이 변 길이의 절반보다 큽니다.",
|
|
213
|
+
"figure.text.section-wall-too-thick": "벽이 두꺼워 속 공간이 남지 않습니다.",
|
|
214
|
+
"figure.text.section-ends-too-thick": "막힌 끝이 두꺼워 속 공간이 남지 않습니다.",
|
|
191
215
|
"figure.label.part-position": "위치 (중심)",
|
|
192
216
|
"figure.label.part-size": "크기",
|
|
193
217
|
"figure.label.parts": "부품",
|
package/translations/ms.json
CHANGED
|
@@ -188,6 +188,30 @@
|
|
|
188
188
|
"figure.label.palette-token": "palette token",
|
|
189
189
|
"figure.label.part": "part",
|
|
190
190
|
"figure.label.part-material": "material",
|
|
191
|
+
"figure.text.section-cavity-unavailable": "Garis luar ini tidak boleh mempunyai ruang dalam. Ruang dalam dibuat dalam keratan segi empat atau bulatan.",
|
|
192
|
+
"figure.text.section-corner-over": "Jejari sudut paling banyak {max} mm.",
|
|
193
|
+
"figure.text.section-at-most": "paling banyak {max} mm",
|
|
194
|
+
"figure.text.section-not-positive": "Ketebalan mesti lebih daripada 0.",
|
|
195
|
+
"figure.text.section-wall-over": "Dinding mesti lebih nipis daripada {max} mm.",
|
|
196
|
+
"figure.text.section-ends-over": "Jumlah ketebalan dua hujung tertutup mesti kurang daripada {max} mm.",
|
|
197
|
+
"figure.text.section-depth": "{result} · dalam {depth} mm",
|
|
198
|
+
"figure.label.section-result-pipe": "paip",
|
|
199
|
+
"figure.label.section-result-container": "bekas",
|
|
200
|
+
"figure.label.section-result-sealed": "ruang dalam tertutup",
|
|
201
|
+
"figure.text.section-wall-at-most": "lebih nipis daripada {max} mm",
|
|
202
|
+
"figure.label.section-wall-per-side": "setiap sisi",
|
|
203
|
+
"figure.label.group-section": "keratan",
|
|
204
|
+
"figure.label.section-corner": "jejari sudut",
|
|
205
|
+
"figure.text.section-corner-differs": "berbeza mengikut paksi",
|
|
206
|
+
"figure.label.section-cavity": "buat ruang dalam",
|
|
207
|
+
"figure.label.section-wall": "ketebalan dinding",
|
|
208
|
+
"figure.label.section-end-closed": "tertutup",
|
|
209
|
+
"figure.label.section-end-open": "terbuka",
|
|
210
|
+
"figure.text.section-face-in-use": "Tidak diubah: ada sesuatu dipasang pada {faces}. Tanggalkannya dahulu.",
|
|
211
|
+
"figure.text.section-dimension-linked": "Dimensi ini mengikut nilai lain. Nyahpaut sebelum mengubahnya.",
|
|
212
|
+
"figure.text.section-corner-too-large": "Jejari sudut melebihi separuh panjang sisi.",
|
|
213
|
+
"figure.text.section-wall-too-thick": "Dinding terlalu tebal sehingga tiada ruang di dalam.",
|
|
214
|
+
"figure.text.section-ends-too-thick": "Hujung tertutup terlalu tebal sehingga tiada ruang di dalam.",
|
|
191
215
|
"figure.label.part-position": "position (centre)",
|
|
192
216
|
"figure.label.part-size": "size",
|
|
193
217
|
"figure.label.parts": "parts",
|
package/translations/zh.json
CHANGED
|
@@ -188,6 +188,30 @@
|
|
|
188
188
|
"figure.label.palette-token": "palette token",
|
|
189
189
|
"figure.label.part": "part",
|
|
190
190
|
"figure.label.part-material": "material",
|
|
191
|
+
"figure.text.section-cavity-unavailable": "此轮廓不能创建内部空间。内部空间只能在矩形和圆形截面中创建。",
|
|
192
|
+
"figure.text.section-corner-over": "圆角半径最大为 {max} mm。",
|
|
193
|
+
"figure.text.section-at-most": "最大 {max} mm",
|
|
194
|
+
"figure.text.section-not-positive": "厚度必须大于 0。",
|
|
195
|
+
"figure.text.section-wall-over": "壁厚必须小于 {max} mm。",
|
|
196
|
+
"figure.text.section-ends-over": "两个封闭端的厚度之和必须小于 {max} mm。",
|
|
197
|
+
"figure.text.section-depth": "{result} · 深 {depth} mm",
|
|
198
|
+
"figure.label.section-result-pipe": "管",
|
|
199
|
+
"figure.label.section-result-container": "容器",
|
|
200
|
+
"figure.label.section-result-sealed": "封闭内部",
|
|
201
|
+
"figure.text.section-wall-at-most": "小于 {max} mm",
|
|
202
|
+
"figure.label.section-wall-per-side": "逐面设置",
|
|
203
|
+
"figure.label.group-section": "截面",
|
|
204
|
+
"figure.label.section-corner": "圆角半径",
|
|
205
|
+
"figure.text.section-corner-differs": "各轴不同",
|
|
206
|
+
"figure.label.section-cavity": "创建内部空间",
|
|
207
|
+
"figure.label.section-wall": "壁厚",
|
|
208
|
+
"figure.label.section-end-closed": "封闭",
|
|
209
|
+
"figure.label.section-end-open": "开口",
|
|
210
|
+
"figure.text.section-face-in-use": "未更改:{faces} 上附着有其他部件。请先拆下。",
|
|
211
|
+
"figure.text.section-dimension-linked": "此尺寸跟随其他值。请先解除关联再修改。",
|
|
212
|
+
"figure.text.section-corner-too-large": "圆角半径超过边长的一半。",
|
|
213
|
+
"figure.text.section-wall-too-thick": "壁太厚,内部没有空间。",
|
|
214
|
+
"figure.text.section-ends-too-thick": "封闭端太厚,内部没有空间。",
|
|
191
215
|
"figure.label.part-position": "position (centre)",
|
|
192
216
|
"figure.label.part-size": "size",
|
|
193
217
|
"figure.label.parts": "parts",
|