@things-factory/figure-ui 10.1.50 → 10.1.52
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 +94 -11
- package/client/modeller/v3-editor.ts +24 -3
- package/client/modeller/v3-links.ts +70 -11
- package/client/pages/figure-v3-modeller-page.ts +11 -1
- package/dist-client/modeller/figure-v3-capability.d.ts +1 -1
- package/dist-client/modeller/figure-v3-clips.d.ts +1 -1
- package/dist-client/modeller/figure-v3-controls.d.ts +1 -1
- package/dist-client/modeller/figure-v3-links.d.ts +8 -0
- package/dist-client/modeller/figure-v3-links.js +95 -12
- package/dist-client/modeller/figure-v3-links.js.map +1 -1
- package/dist-client/modeller/figure-v3-material.d.ts +1 -1
- package/dist-client/modeller/figure-v3-properties.d.ts +1 -1
- package/dist-client/modeller/v3-clips.d.ts +1 -1
- package/dist-client/modeller/v3-editor.js +24 -2
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-links.d.ts +41 -5
- package/dist-client/modeller/v3-links.js +61 -11
- package/dist-client/modeller/v3-links.js.map +1 -1
- package/dist-client/pages/figure-v3-modeller-page.js +11 -1
- package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/test/browser/modeller.check.mjs +49 -0
- package/test/v3-first-save.test.ts +9 -0
- package/test/v3-links.test.ts +78 -7
- package/translations/en.json +10 -1
- package/translations/ja.json +10 -1
- package/translations/ko.json +10 -1
- package/translations/ms.json +10 -1
- package/translations/zh.json +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/figure-ui",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.52",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"clean": "npm run clean:server && npm run clean:client"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@hatiolab/figure-model": "^0.1.
|
|
31
|
-
"@hatiolab/things-scene": "^10.1.
|
|
30
|
+
"@hatiolab/figure-model": "^0.1.60",
|
|
31
|
+
"@hatiolab/things-scene": "^10.1.37",
|
|
32
32
|
"@material/web": "^2.0.0",
|
|
33
33
|
"@operato/data-grist": "^10.0.0",
|
|
34
34
|
"@operato/graphql": "^10.0.0",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"@operato/shell": "^10.0.0",
|
|
40
40
|
"@operato/styles": "^10.0.0",
|
|
41
41
|
"@operato/utils": "^10.0.0",
|
|
42
|
-
"@things-factory/figure-service": "^10.1.
|
|
42
|
+
"@things-factory/figure-service": "^10.1.52",
|
|
43
43
|
"three": "^0.185.1"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "b3ec4f0a38f0759d396f49b1f47c547bff4657be"
|
|
46
46
|
}
|
|
@@ -329,6 +329,55 @@ describe('the V3 robot arm: a drag on a part edits its joint (ADR-0093)', () =>
|
|
|
329
329
|
})
|
|
330
330
|
})
|
|
331
331
|
|
|
332
|
+
describe('the fastening form belongs to the part it is for', () => {
|
|
333
|
+
before(async () => {
|
|
334
|
+
await mount('arm')
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
/* The select whose label (the i18n key: the check loads no translations) is `label`, in the parts' links form. */
|
|
338
|
+
const alignSelect = label =>
|
|
339
|
+
page().evaluateHandle(label => {
|
|
340
|
+
const deep = (root = document) => [...root.querySelectorAll('select')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
341
|
+
return deep().find(s => s.previousElementSibling?.textContent?.trim() === label) ?? null
|
|
342
|
+
}, label)
|
|
343
|
+
|
|
344
|
+
test('a choice made for one part is not carried to the next part picked (셔터 커튼, 2026-09-25)', async () => {
|
|
345
|
+
await click(await read(() => probe.row('forearm')))
|
|
346
|
+
const select = await alignSelect('figure.label.align-x')
|
|
347
|
+
assert.ok(await select.evaluate(s => !!s), 'the form offers left-right placing for the forearm')
|
|
348
|
+
await select.select('max')
|
|
349
|
+
assert.equal(await select.evaluate(s => s.value), 'max', 'chosen for the forearm')
|
|
350
|
+
await click(await read(() => probe.row('elbow')))
|
|
351
|
+
const next = await alignSelect('figure.label.align-x')
|
|
352
|
+
assert.equal(await next.evaluate(s => s.value), 'centre', 'the elbow starts from the default')
|
|
353
|
+
})
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
describe('a figure made in the modeller is given its size from its parts (user 2026-09-25)', () => {
|
|
357
|
+
before(async () => {
|
|
358
|
+
await mount('box')
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
test('with nothing picked, the figure offers to declare its size; clicked, its size fields appear', async () => {
|
|
362
|
+
const button = await page().evaluateHandle(() => {
|
|
363
|
+
const deep = (root = document) => [...root.querySelectorAll('button')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
364
|
+
return deep().find(b => b.textContent.includes('figure.button.declare-instance-size')) ?? null
|
|
365
|
+
})
|
|
366
|
+
assert.ok(await button.evaluate(b => !!b), 'the button is offered on a figure with no size')
|
|
367
|
+
const box = await button.evaluate(b => {
|
|
368
|
+
b.scrollIntoView({ block: 'center' })
|
|
369
|
+
const r = b.getBoundingClientRect()
|
|
370
|
+
return { x: r.left + r.width / 2, y: r.top + r.height / 2 }
|
|
371
|
+
})
|
|
372
|
+
const start = await state()
|
|
373
|
+
await click(box)
|
|
374
|
+
const after = await state()
|
|
375
|
+
assert.equal(after.error, '', 'not refused')
|
|
376
|
+
assert.equal(after.revision, start.revision + 1, 'one step')
|
|
377
|
+
assert.deepEqual(await read(() => probe.fields('size.')), { 'size.x': 400, 'size.y': 400, 'size.z': 400 }, 'the size it is authored at: its parts now')
|
|
378
|
+
})
|
|
379
|
+
})
|
|
380
|
+
|
|
332
381
|
test('the page raised no error', () => {
|
|
333
382
|
assert.deepEqual(opened.errors, [])
|
|
334
383
|
})
|
|
@@ -73,3 +73,12 @@ test('저장 전에 적은 타입은 처음 저장의 물음에 실려 온다
|
|
|
73
73
|
assert.match(rename, /if \(!id\) \{\s*this\.row = \{ \.\.\.this\.row, type: wanted \|\| undefined \}/, '저장 전에는 행 대신 화면이 타입을 쥔다')
|
|
74
74
|
assert.match(page, /<figure-v3-first-save \.type=\$\{this\.row\.type \?\? ''\}/, '물음은 그 타입으로 열린다')
|
|
75
75
|
})
|
|
76
|
+
|
|
77
|
+
test('처음 저장한 도형은 주소가 제 것이 된다 — 모델러를 다시 열면 새 도형이 열린다 (2026-09-25)', async () => {
|
|
78
|
+
const { readFileSync } = await import('node:fs')
|
|
79
|
+
const page = readFileSync(new URL('../client/pages/figure-v3-modeller-page.ts', import.meta.url), 'utf8')
|
|
80
|
+
const changed = page.slice(page.indexOf('private changed = () => {'), page.indexOf('private async readRow'))
|
|
81
|
+
assert.match(changed, /if \(view\.id && !this\.routeId && !view\.saving\) \{\s*this\.routeId = view\.id\s*navigate\(modellerPathOf\(\{ id: view\.id \}\), true\)/, '저장으로 id 를 얻으면 그 주소로 바꾼다(기록은 덮어쓴다)')
|
|
82
|
+
/* The early return that kept the old figure reads the route id; it now matches only the figure the address names. */
|
|
83
|
+
assert.match(page, /if \(this\.session\.read\(\)\.context && id === this\.routeId && !changes\.active\) return/)
|
|
84
|
+
})
|
package/test/v3-links.test.ts
CHANGED
|
@@ -240,20 +240,24 @@ test('관절이 있는 도형에는 한 비율로 키우기를 권하고, 선언
|
|
|
240
240
|
|
|
241
241
|
test('붙일 때 면 위 어디에 — 그 면을 가로지르는 두 축만, 바닥에는 모서리가 없다 (문을 틀 모서리에 달기, 09-25)', async () => {
|
|
242
242
|
const { alignAxesOf, alignOf } = await import('../client/modeller/v3-links.ts')
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
let two = createV3Asset('Two')
|
|
244
|
+
two = addV3Box(two, { id: 'jamb', size: { x: 100, y: 2100, z: 150 }, position: { x: 0, y: 1050, z: 0 } })
|
|
245
|
+
two = addV3Box(two, { id: 'leaf', size: { x: 900, y: 2000, z: 40 }, position: { x: 600, y: 1000, z: 0 } })
|
|
246
|
+
const right = { part: 'jamb', face: 'right' }
|
|
247
|
+
assert.deepEqual(alignAxesOf(two, 'leaf', right), [{ axis: 'y', edges: true }, { axis: 'z', edges: true }])
|
|
248
|
+
assert.deepEqual(alignAxesOf(two, 'leaf', 'plane'), [{ axis: 'x', edges: false }, { axis: 'z', edges: false }])
|
|
245
249
|
/* A door on the jamb's right face: bottoms level, fronts level. The x choice is along the face and is not sent. */
|
|
246
|
-
assert.deepEqual(alignOf('
|
|
247
|
-
assert.deepEqual(alignOf('top', { x: 'mm', z: 'centre' }, { x: 120 }), { x: { mm: 120 } })
|
|
248
|
-
assert.equal(alignOf('plane', { x: 'min' }, {}), undefined, '바닥에는 맞출 모서리가 없다')
|
|
249
|
-
assert.equal(alignOf('top', {}, {}), undefined)
|
|
250
|
+
assert.deepEqual(alignOf(two, 'leaf', right, { x: 'max', y: 'min', z: 'max' }, {}), { y: 'min', z: 'max' })
|
|
251
|
+
assert.deepEqual(alignOf(two, 'leaf', { part: 'jamb', face: 'top' }, { x: 'mm', z: 'centre' }, { x: 120 }), { x: { mm: 120 } })
|
|
252
|
+
assert.equal(alignOf(two, 'leaf', 'plane', { x: 'min' }, {}), undefined, '바닥에는 맞출 모서리가 없다')
|
|
253
|
+
assert.equal(alignOf(two, 'leaf', { part: 'jamb', face: 'top' }, {}, {}), undefined)
|
|
250
254
|
|
|
251
255
|
/* What the form sends is what hangs the part on the edge: the door's pivot is on the jamb's front edge line. */
|
|
252
256
|
let asset = createV3Asset('Door')
|
|
253
257
|
asset = addV3Box(asset, { id: 'jamb', size: { x: 100, y: 2100, z: 150 }, position: { x: 0, y: 1050, z: 0 } })
|
|
254
258
|
asset = addV3Box(asset, { id: 'leaf', size: { x: 900, y: 2000, z: 40 }, position: { x: 600, y: 1000, z: 0 } })
|
|
255
259
|
asset = applyV3Authoring(asset, { kind: 'attach', part: 'jamb', face: 'bottom', to: { plane: 'mounting-plane' } })
|
|
256
|
-
asset = applyV3Authoring(asset, { kind: 'attach', part: 'leaf', face: 'left', to: { part: 'jamb', face: 'right' }, align: alignOf('right', { y: 'min', z: 'max' }, {}) })
|
|
260
|
+
asset = applyV3Authoring(asset, { kind: 'attach', part: 'leaf', face: 'left', to: { part: 'jamb', face: 'right' }, align: alignOf(asset, 'leaf', { part: 'jamb', face: 'right' }, { y: 'min', z: 'max' }, {}) })
|
|
257
261
|
const { compileV3Asset } = await import('@hatiolab/figure-model')
|
|
258
262
|
const leaf = compileV3Asset(asset).evaluate().geometry.find(g => g.id === 'leaf')!
|
|
259
263
|
assert.deepEqual(leaf.pose.t.map(Math.round), [500, 1000, 55], '문짝 아래가 문틀 아래에, 앞이 문틀 앞에')
|
|
@@ -292,3 +296,70 @@ test('따라 움직이는 관절 — 같은 방식으로 움직이고 제 조작
|
|
|
292
296
|
const { motionOf } = await import('../client/modeller/v3-links.ts')
|
|
293
297
|
assert.deepEqual(motionOf(asset, 'right')?.follows, { joint: 'left', multiplier: -1 }, '화면은 무엇을 따르는지 읽는다')
|
|
294
298
|
})
|
|
299
|
+
|
|
300
|
+
test('면은 도형이 정한다 — 문틀(portal)은 바깥 여섯에 개구부 셋, 공은 없음, 원뿔대는 위아래뿐 (2026-09-25)', async () => {
|
|
301
|
+
const { facesOf, faceAxisOf, canFasten, alignAxesOf } = await import('../client/modeller/v3-links.ts')
|
|
302
|
+
let asset = createV3Asset('Doorway')
|
|
303
|
+
asset = addV3Primitive(asset, {
|
|
304
|
+
id: 'frame',
|
|
305
|
+
kind: 'portal',
|
|
306
|
+
dimensions: { left: -550, right: 550, bottom: 0, top: 2200, openingLeft: -450, openingRight: 450, openingTop: 2100, depth: 150 },
|
|
307
|
+
position: { x: 0, y: 0, z: 0 }
|
|
308
|
+
})
|
|
309
|
+
asset = addV3Primitive(asset, { id: 'ball', kind: 'sphere', dimensions: { radiusX: 50, radiusY: 50, radiusZ: 50 }, position: { x: 0, y: 0, z: 0 } })
|
|
310
|
+
asset = addV3Primitive(asset, { id: 'cone', kind: 'frustum', dimensions: { radiusTop: 20, radiusBottom: 60, height: 100 }, position: { x: 0, y: 0, z: 0 } })
|
|
311
|
+
asset = addV3Box(asset, { id: 'leaf', size: { x: 890, y: 2095, z: 40 }, position: { x: 0, y: 0, z: 0 } })
|
|
312
|
+
assert.deepEqual(facesOf(asset, 'frame'), ['left', 'right', 'bottom', 'top', 'back', 'front', 'opening-left', 'opening-right', 'opening-top'])
|
|
313
|
+
assert.equal(faceAxisOf(asset, 'frame', 'opening-top'), 'y')
|
|
314
|
+
assert.deepEqual(facesOf(asset, 'ball'), [])
|
|
315
|
+
assert.equal(canFasten(asset, 'ball'), false, '공은 붙일 면이 없다')
|
|
316
|
+
assert.equal(canFasten(asset, 'frame'), true)
|
|
317
|
+
assert.deepEqual(facesOf(asset, 'cone'), ['bottom', 'top'])
|
|
318
|
+
assert.deepEqual(alignAxesOf(asset, 'leaf', { part: 'frame', face: 'opening-left' }), [{ axis: 'y', edges: true }, { axis: 'z', edges: true }])
|
|
319
|
+
assert.deepEqual(alignAxesOf(asset, 'leaf', { part: 'cone', face: 'top' }), [{ axis: 'x', edges: false }, { axis: 'z', edges: false }], '옆면이 평면이 아니면 모서리도 없다')
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
test('되풀이는 붙어 있고 움직이지 않고 아무것도 매달지 않은 부품에 — 붙인 면을 가로지르는 축으로 (빈틈 B, 2026-09-25)', async () => {
|
|
323
|
+
const { rowAxesOf, rowOf } = await import('../client/modeller/v3-links.ts')
|
|
324
|
+
let asset = createV3Asset('Conveyor')
|
|
325
|
+
asset = addV3Box(asset, { id: 'frame', size: { x: 2000, y: 100, z: 600 }, position: { x: 0, y: 0, z: 0 } })
|
|
326
|
+
asset = addV3Box(asset, { id: 'slat', size: { x: 60, y: 20, z: 560 }, position: { x: 0, y: 0, z: 0 } })
|
|
327
|
+
assert.deepEqual(rowAxesOf(asset, 'slat'), [], '붙지 않은 부품은 되풀이를 권하지 않는다')
|
|
328
|
+
asset = applyV3Authoring(asset, { kind: 'attach', part: 'frame', face: 'bottom', to: { plane: 'mounting-plane' } })
|
|
329
|
+
asset = applyV3Authoring(asset, { kind: 'attach', part: 'slat', face: 'bottom', to: { part: 'frame', face: 'top' } })
|
|
330
|
+
assert.deepEqual(rowAxesOf(asset, 'frame'), [], '바닥에 선 부품은 채울 길이가 없다')
|
|
331
|
+
assert.deepEqual(rowAxesOf(asset, 'slat'), ['x', 'z'], '윗면을 가로지르는 두 축')
|
|
332
|
+
asset = applyV3Authoring(asset, { kind: 'make-repeat', part: 'slat', axis: 'x', pitch: 100 })
|
|
333
|
+
assert.deepEqual(rowOf(asset, 'slat'), { axis: 'X', pitch: 100 })
|
|
334
|
+
assert.deepEqual(rowAxesOf(asset, 'slat'), [], '이미 되풀이면 다시 권하지 않는다')
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
test('비튼 부품에는 모서리 맞춤을 내놓지도 보내지도 않는다 — 가운데서 거리만 (빈틈 F, 2026-09-25)', async () => {
|
|
338
|
+
const { alignAxesOf, alignOf } = await import('../client/modeller/v3-links.ts')
|
|
339
|
+
let asset = createV3Asset('Fan')
|
|
340
|
+
asset = addV3Box(asset, { id: 'hub', size: { x: 120, y: 120, z: 80 }, position: { x: 0, y: 0, z: 0 } })
|
|
341
|
+
asset = addV3Box(asset, { id: 'blade', size: { x: 300, y: 60, z: 8 }, position: { x: 0, y: 0, z: 0 } })
|
|
342
|
+
const front = { part: 'hub', face: 'front' }
|
|
343
|
+
assert.deepEqual(alignAxesOf(asset, 'blade', front, true), [{ axis: 'x', edges: false }, { axis: 'y', edges: false }])
|
|
344
|
+
assert.deepEqual(alignOf(asset, 'blade', front, { x: 'max', y: 'mm' }, { y: 50 }, true), { y: { mm: 50 } }, '끝 맞춤은 빠지고 거리만 간다')
|
|
345
|
+
assert.deepEqual(alignOf(asset, 'blade', front, { x: 'max' }, {}), { x: 'max' }, '비틀지 않으면 그대로')
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
test('모델러에서 만든 도형은 부품에서 재어 도형 크기를 선언한다 — 그 뒤에야 부품이 도형 크기를 따를 수 있다 (사용자 09-25)', async () => {
|
|
349
|
+
const { instanceRangeToDeclare, instanceRangeOf, hasInstanceSize } = await import('../client/modeller/v3-links.ts')
|
|
350
|
+
const { compileV3Asset } = await import('@hatiolab/figure-model')
|
|
351
|
+
let asset = createV3Asset('Conveyor')
|
|
352
|
+
assert.equal(instanceRangeToDeclare(asset), undefined, '부품이 없으면 잴 것이 없다')
|
|
353
|
+
asset = addV3Box(asset, { id: 'frame', size: { x: 2000, y: 100, z: 600 }, position: { x: 0, y: 0, z: 0 } })
|
|
354
|
+
asset = applyV3Authoring(asset, { kind: 'attach', part: 'frame', face: 'bottom', to: { plane: 'mounting-plane' } })
|
|
355
|
+
assert.equal(hasInstanceSize(asset, 'x'), false, '새 도형에는 도형 크기가 없다')
|
|
356
|
+
const toDeclare = instanceRangeToDeclare(asset)!
|
|
357
|
+
assert.deepEqual(toDeclare.at, { x: 2000, y: 100, z: 600 }, '지금 부품의 크기가 선언할 크기')
|
|
358
|
+
asset = applyV3Authoring(asset, { kind: 'declare-instance-size', range: toDeclare.range, at: toDeclare.at })
|
|
359
|
+
assert.ok(instanceRangeOf(asset), '선언 뒤에는 놓일 수 있는 크기가 보인다')
|
|
360
|
+
assert.equal(instanceRangeToDeclare(asset), undefined, '이미 있으면 다시 권하지 않는다')
|
|
361
|
+
assert.equal(hasInstanceSize(asset, 'x'), true, '부품이 도형 크기를 따르는 선택이 열린다')
|
|
362
|
+
asset = applyV3Authoring(asset, { kind: 'link-dimension', part: 'frame', axis: 'x', source: { of: 'instance', axis: 'x' } } as never)
|
|
363
|
+
const at = (size: number) => (compileV3Asset({ ...asset, designInputs: { ...asset.designInputs, 'size.x': size } }).evaluate().geometry.find(g => g.id === 'frame')!.shape.dimensions as { width: number }).width
|
|
364
|
+
assert.equal(at(3000), 3000, '보드가 3000 으로 놓으면 틀도 3000')
|
|
365
|
+
})
|
package/translations/en.json
CHANGED
|
@@ -701,5 +701,14 @@
|
|
|
701
701
|
"figure.label.motion-own-control": "its own control",
|
|
702
702
|
"figure.label.motion-multiplier": "multiplier",
|
|
703
703
|
"figure.label.motion-offset": "offset",
|
|
704
|
-
"figure.label.motion-follows": "moves with {joint} × {multiplier} + {offset}"
|
|
704
|
+
"figure.label.motion-follows": "moves with {joint} × {multiplier} + {offset}",
|
|
705
|
+
"figure.label.face-opening-left": "opening left",
|
|
706
|
+
"figure.label.face-opening-right": "opening right",
|
|
707
|
+
"figure.label.face-opening-top": "opening top",
|
|
708
|
+
"figure.label.repeat-fills": "as many as fill the length",
|
|
709
|
+
"figure.button.make-repeat": "make a row",
|
|
710
|
+
"figure.button.remove-repeat": "remove the row",
|
|
711
|
+
"figure.label.twist": "twist (°)",
|
|
712
|
+
"figure.text.instance-size-undeclared": "the figure has no size, so a board cannot resize it.",
|
|
713
|
+
"figure.button.declare-instance-size": "declare the figure size"
|
|
705
714
|
}
|
package/translations/ja.json
CHANGED
|
@@ -701,5 +701,14 @@
|
|
|
701
701
|
"figure.label.motion-own-control": "自分の操作値",
|
|
702
702
|
"figure.label.motion-multiplier": "倍率",
|
|
703
703
|
"figure.label.motion-offset": "オフセット",
|
|
704
|
-
"figure.label.motion-follows": "{joint} に連動 × {multiplier} + {offset}"
|
|
704
|
+
"figure.label.motion-follows": "{joint} に連動 × {multiplier} + {offset}",
|
|
705
|
+
"figure.label.face-opening-left": "開口部 左",
|
|
706
|
+
"figure.label.face-opening-right": "開口部 右",
|
|
707
|
+
"figure.label.face-opening-top": "開口部 上",
|
|
708
|
+
"figure.label.repeat-fills": "長さを満たすだけ",
|
|
709
|
+
"figure.button.make-repeat": "繰り返しを作る",
|
|
710
|
+
"figure.button.remove-repeat": "繰り返しをなくす",
|
|
711
|
+
"figure.label.twist": "ひねり (°)",
|
|
712
|
+
"figure.text.instance-size-undeclared": "図形サイズがないため、ボードでサイズを変えられません。",
|
|
713
|
+
"figure.button.declare-instance-size": "図形サイズを宣言"
|
|
705
714
|
}
|
package/translations/ko.json
CHANGED
|
@@ -701,5 +701,14 @@
|
|
|
701
701
|
"figure.label.motion-own-control": "제 조작값으로",
|
|
702
702
|
"figure.label.motion-multiplier": "배율",
|
|
703
703
|
"figure.label.motion-offset": "더하기",
|
|
704
|
-
"figure.label.motion-follows": "따라 움직임: {joint} × {multiplier} + {offset}"
|
|
704
|
+
"figure.label.motion-follows": "따라 움직임: {joint} × {multiplier} + {offset}",
|
|
705
|
+
"figure.label.face-opening-left": "개구부 왼쪽",
|
|
706
|
+
"figure.label.face-opening-right": "개구부 오른쪽",
|
|
707
|
+
"figure.label.face-opening-top": "개구부 위",
|
|
708
|
+
"figure.label.repeat-fills": "길이를 채우는 만큼",
|
|
709
|
+
"figure.button.make-repeat": "되풀이 만들기",
|
|
710
|
+
"figure.button.remove-repeat": "되풀이 없애기",
|
|
711
|
+
"figure.label.twist": "비틀기 (°)",
|
|
712
|
+
"figure.text.instance-size-undeclared": "도형 크기가 없어 보드에서 크기를 바꿀 수 없습니다.",
|
|
713
|
+
"figure.button.declare-instance-size": "도형 크기 선언"
|
|
705
714
|
}
|
package/translations/ms.json
CHANGED
|
@@ -701,5 +701,14 @@
|
|
|
701
701
|
"figure.label.motion-own-control": "kawalan sendiri",
|
|
702
702
|
"figure.label.motion-multiplier": "pendarab",
|
|
703
703
|
"figure.label.motion-offset": "ofset",
|
|
704
|
-
"figure.label.motion-follows": "bergerak bersama {joint} × {multiplier} + {offset}"
|
|
704
|
+
"figure.label.motion-follows": "bergerak bersama {joint} × {multiplier} + {offset}",
|
|
705
|
+
"figure.label.face-opening-left": "bukaan kiri",
|
|
706
|
+
"figure.label.face-opening-right": "bukaan kanan",
|
|
707
|
+
"figure.label.face-opening-top": "bukaan atas",
|
|
708
|
+
"figure.label.repeat-fills": "sebanyak yang memenuhi panjang",
|
|
709
|
+
"figure.button.make-repeat": "buat baris",
|
|
710
|
+
"figure.button.remove-repeat": "buang baris",
|
|
711
|
+
"figure.label.twist": "pusingan (°)",
|
|
712
|
+
"figure.text.instance-size-undeclared": "rajah tiada saiz, jadi papan tidak boleh mengubah saiznya.",
|
|
713
|
+
"figure.button.declare-instance-size": "isytihar saiz rajah"
|
|
705
714
|
}
|
package/translations/zh.json
CHANGED
|
@@ -701,5 +701,14 @@
|
|
|
701
701
|
"figure.label.motion-own-control": "自身控制值",
|
|
702
702
|
"figure.label.motion-multiplier": "倍率",
|
|
703
703
|
"figure.label.motion-offset": "偏移",
|
|
704
|
-
"figure.label.motion-follows": "跟随 {joint} × {multiplier} + {offset}"
|
|
704
|
+
"figure.label.motion-follows": "跟随 {joint} × {multiplier} + {offset}",
|
|
705
|
+
"figure.label.face-opening-left": "开口 左侧",
|
|
706
|
+
"figure.label.face-opening-right": "开口 右侧",
|
|
707
|
+
"figure.label.face-opening-top": "开口 顶部",
|
|
708
|
+
"figure.label.repeat-fills": "填满长度的数量",
|
|
709
|
+
"figure.button.make-repeat": "生成重复",
|
|
710
|
+
"figure.button.remove-repeat": "取消重复",
|
|
711
|
+
"figure.label.twist": "扭转 (°)",
|
|
712
|
+
"figure.text.instance-size-undeclared": "图形没有尺寸,看板上无法调整大小。",
|
|
713
|
+
"figure.button.declare-instance-size": "声明图形尺寸"
|
|
705
714
|
}
|