@things-factory/figure-ui 10.1.206 → 10.1.207
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/editor.ts +11 -97
- package/client/modeller/figure-facts.ts +1 -1
- package/client/modeller/figure-history-panel.ts +1 -29
- package/client/modeller/figure-preview.ts +5 -7
- package/client/modeller/figure-verdict.ts +2 -17
- package/client/modeller/maturity.ts +11 -27
- package/client/modeller/refusals.ts +1 -7
- package/client/pages/figure-modeller-page.ts +7 -22
- package/dist-client/modeller/editor.d.ts +1 -8
- package/dist-client/modeller/editor.js +12 -98
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-facts.js +1 -1
- package/dist-client/modeller/figure-facts.js.map +1 -1
- package/dist-client/modeller/figure-fasten-dialog.d.ts +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +0 -16
- package/dist-client/modeller/figure-history-panel.js +1 -27
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-links.d.ts +1 -1
- package/dist-client/modeller/figure-preview.js +5 -8
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-verdict.d.ts +0 -5
- package/dist-client/modeller/figure-verdict.js +2 -21
- package/dist-client/modeller/figure-verdict.js.map +1 -1
- package/dist-client/modeller/maturity.d.ts +2 -4
- package/dist-client/modeller/maturity.js +12 -26
- package/dist-client/modeller/maturity.js.map +1 -1
- package/dist-client/modeller/refusals.d.ts +1 -3
- package/dist-client/modeller/refusals.js +1 -7
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +1 -3
- package/dist-client/pages/figure-modeller-page.js +7 -23
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/test/browser/acceptance-page.js +3 -3
- package/test/browser/detail-page.js +1 -3
- package/test/browser/detail-tabs-page.js +1 -2
- package/test/browser/links.check.mjs +2 -37
- package/test/browser/modeller.check.mjs +1 -3
- package/test/browser/parts-page.js +2 -2
- package/test/browser/parts.check.mjs +2 -12
- package/test/browser/product-page.check.mjs +6 -9
- package/test/browser/product-page.js +1 -2
- package/test/browser/screen-page.js +2 -6
- package/test/browser/screen.check.mjs +8 -65
- package/test/first-minute.test.ts +2 -3
- package/test/fixtures/joint-column.json +1 -66
- package/test/fixtures/joint-roller.json +1 -66
- package/test/fixtures/robot-arm-6dof.json +2 -67
- package/test/links.test.ts +1 -1
- package/test/parts-wiring.test.ts +1 -1
- package/test/properties.test.ts +9 -12
- package/test/refusals.test.ts +3 -21
- package/test/resize-with-placement.test.ts +13 -15
- package/test/revision.test.ts +1 -2
- package/test/verdict.test.ts +1 -15
- package/translations/en.json +0 -19
- package/translations/es.json +0 -19
- package/translations/ja.json +0 -19
- package/translations/ko.json +0 -19
- package/translations/ms.json +0 -19
- package/translations/zh.json +0 -19
- package/client/modeller/occupancy.ts +0 -51
- package/dist-client/modeller/occupancy.d.ts +0 -31
- package/dist-client/modeller/occupancy.js +0 -36
- package/dist-client/modeller/occupancy.js.map +0 -1
- package/test/release-panel.test.ts +0 -53
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.207",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"clean": "npm run clean:server && npm run clean:client"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@hatiolab/figure-model": "^0.
|
|
30
|
+
"@hatiolab/figure-model": "^0.10.0",
|
|
31
31
|
"@hatiolab/things-scene": "^10.1.95",
|
|
32
32
|
"@material/web": "^2.0.0",
|
|
33
33
|
"@operato/data-grist": "^10.29.43",
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
"@operato/slot-layout-editor": "^10.29.43",
|
|
43
43
|
"@operato/styles": "^10.0.0",
|
|
44
44
|
"@operato/utils": "^10.29.43",
|
|
45
|
-
"@things-factory/ai-assistant": "^10.1.
|
|
46
|
-
"@things-factory/figure-service": "^10.1.
|
|
47
|
-
"@things-factory/figure-viewer": "^10.1.
|
|
45
|
+
"@things-factory/ai-assistant": "^10.1.207",
|
|
46
|
+
"@things-factory/figure-service": "^10.1.207",
|
|
47
|
+
"@things-factory/figure-viewer": "^10.1.207",
|
|
48
48
|
"@things-factory/setting-base": "^10.1.206",
|
|
49
49
|
"three": "^0.185.1"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "e6e3fbf5c353629adbeb8850093150b14254e059"
|
|
52
52
|
}
|
|
@@ -10,7 +10,7 @@ import 'figure-ui-client/modeller/figure-properties.js'
|
|
|
10
10
|
import { createFigureProductSession } from 'figure-ui-client/modeller/product-session.js'
|
|
11
11
|
import { runFigureProposal, takeFigureOffer } from 'figure-ui-client/modeller/ai-proposal.js'
|
|
12
12
|
import { i18next } from '@operato/i18n'
|
|
13
|
-
import { addFigureBox, addFigurePrimitive, applyFigureAuthoring, createFigureAsset, editFigurePart,
|
|
13
|
+
import { addFigureBox, addFigurePrimitive, applyFigureAuthoring, createFigureAsset, editFigurePart, serializeFigureAsset } from '@hatiolab/figure-model'
|
|
14
14
|
|
|
15
15
|
const PALETTE = { 'palette.dark': '#2b2b2b', 'palette.neutral': '#9a9a9a', 'palette.primary': '#477b9e', 'palette.accent': '#f79c40', 'palette.light': '#dddddd' }
|
|
16
16
|
|
|
@@ -19,7 +19,7 @@ function box(placement, position) {
|
|
|
19
19
|
let a = createFigureAsset(`Probe ${placement}`)
|
|
20
20
|
a.palette = { ...a.palette, ...PALETTE }
|
|
21
21
|
a = addFigureBox(a, { id: 'box', size: { x: 400, y: 400, z: 400 }, position })
|
|
22
|
-
return serializeFigureAsset(applyFigureAuthoring(a, { kind: '
|
|
22
|
+
return serializeFigureAsset(applyFigureAuthoring(a, { kind: 'set-placement', placement }))
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function turned(rotation, placement = 'floor', position = { x: 0, y: 400, z: 0 }) {
|
|
@@ -91,7 +91,7 @@ export const MODELS = {
|
|
|
91
91
|
a.palette = { ...a.palette, ...PALETTE }
|
|
92
92
|
a = addFigureBox(a, { id: 'leg', size: { x: 60, y: 700, z: 60 }, position: { x: -400, y: 350, z: 0 } })
|
|
93
93
|
a = addFigureBox(a, { id: 'top', size: { x: 1200, y: 40, z: 600 }, position: { x: 0, y: 720, z: 0 } })
|
|
94
|
-
return serializeFigureAsset(applyFigureAuthoring(a, { kind: '
|
|
94
|
+
return serializeFigureAsset(applyFigureAuthoring(a, { kind: 'set-placement', placement: 'floor' }))
|
|
95
95
|
},
|
|
96
96
|
/* A ceiling figure's part off its ceiling, for the one-off align (to the ceiling, by its top). */
|
|
97
97
|
'ceiling-off': () => box('ceiling', { x: 0, y: -600, z: 0 }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Isolated product detail: fixtures are in memory, never saved to the running app. */
|
|
2
2
|
import 'figure-ui-client/viewparts/figure-detail-dock.js'
|
|
3
3
|
import { i18next } from '@operato/i18n'
|
|
4
|
-
import { addFigureBox, createFigureAsset, editFigurePart, applyFigureAuthoring,
|
|
4
|
+
import { addFigureBox, createFigureAsset, editFigurePart, applyFigureAuthoring, serializeFigureAsset } from '@hatiolab/figure-model'
|
|
5
5
|
|
|
6
6
|
async function start() {
|
|
7
7
|
i18next.addResourceBundle('ko', 'translations', await fetch('/ko.json').then(r => r.json()), true, true)
|
|
@@ -14,8 +14,6 @@ async function start() {
|
|
|
14
14
|
id: 'block', type: 'prismatic', body0: { plane: 'mounting-plane' }, body1: { part: 'block' }, axis: 'X', lowerLimit: 0, upperLimit: 200, state: { id: 'shift', start: 0 }
|
|
15
15
|
} })
|
|
16
16
|
asset = editFigurePart(asset, { kind: 'set-driver', state: 'shift', clip: 'cycle', duration: 4, keys: [{ seconds: 0, value: 0 }, { seconds: 2, value: 200 }, { seconds: 4, value: 0 }] })
|
|
17
|
-
/* Its room declared from its parts over the whole motion, as the modeller does before a release. */
|
|
18
|
-
asset = applyFigureAuthoring(asset, { kind: 'declare-occupancy', proposal: proposeFigureOccupancy(asset, { over: 'range' }) })
|
|
19
17
|
const source = serializeFigureAsset(asset)
|
|
20
18
|
const dock = document.createElement('figure-detail-dock')
|
|
21
19
|
let fail = true
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* are in memory; nothing is saved.
|
|
5
5
|
*/
|
|
6
6
|
import 'figure-ui-client/viewparts/figure-detail-dock.js'
|
|
7
|
-
import { addFigureBox, applyFigureAuthoring, createFigureAsset, editFigurePart,
|
|
7
|
+
import { addFigureBox, applyFigureAuthoring, createFigureAsset, editFigurePart, robotDogFigure, serializeFigureAsset } from '@hatiolab/figure-model'
|
|
8
8
|
|
|
9
9
|
const PARTS = 12
|
|
10
10
|
|
|
@@ -17,7 +17,6 @@ for (let i = 0; i < PARTS; i++) {
|
|
|
17
17
|
} })
|
|
18
18
|
asset = editFigurePart(asset, { kind: 'set-driver', state: `${id}-z`, clip: `cycle-${i}`, duration: 2, keys: [{ seconds: 0, value: 0 }, { seconds: 1, value: 100 }, { seconds: 2, value: 0 }] })
|
|
19
19
|
}
|
|
20
|
-
asset = applyFigureAuthoring(asset, { kind: 'declare-occupancy', proposal: proposeFigureOccupancy(asset, { over: 'range' }) })
|
|
21
20
|
const source = serializeFigureAsset(asset)
|
|
22
21
|
|
|
23
22
|
const dock = document.createElement('figure-detail-dock')
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
applyFigureProposal,
|
|
18
18
|
createFigureAsset,
|
|
19
19
|
planFigureResize,
|
|
20
|
-
proposeFigureOccupancy,
|
|
21
20
|
serializeFigureAsset
|
|
22
21
|
} from '@hatiolab/figure-model'
|
|
23
22
|
|
|
@@ -683,52 +682,18 @@ describe('a part fastened twisted is sized by a drag: its size alone, its fasten
|
|
|
683
682
|
describe('a box made to follow the figure size on the floor', () => {
|
|
684
683
|
before(async () => {
|
|
685
684
|
let a = box(createFigureAsset('Floor'), 'box', [100, 100, 100], [20, 50, 30])
|
|
686
|
-
a = applyFigureAuthoring(a, { kind: '
|
|
685
|
+
a = applyFigureAuthoring(a, { kind: 'set-placement', placement: 'floor' })
|
|
687
686
|
await mount('floor', applyFigureProposal(a, planFigureResize(a, { part: 'box' }).steps))
|
|
688
687
|
await pick('box')
|
|
689
688
|
})
|
|
690
689
|
|
|
691
|
-
test('its
|
|
692
|
-
const roomOnly = await all('input[aria-label="box.position.y"]')
|
|
693
|
-
assert.equal(roomOnly.length, 1, 'one field for it, apart from the position row')
|
|
694
|
-
assert.equal(roomOnly[0].disabled, true, 'not typed into')
|
|
695
|
-
assert.equal((await all('section[room-only] input[aria-label="box.position.y"]')).length, 1, 'in the room\'s section')
|
|
690
|
+
test('its row shows the number the floor gives; a size drag is drawn by the modeller', async () => {
|
|
696
691
|
const held = (await all('div[vector-row] input[readonly]')).filter(i => i.label?.startsWith('position.y'))
|
|
697
692
|
assert.deepEqual(held.map(i => i.value), ['50'], 'the height the floor gives, read-only')
|
|
698
693
|
assert.equal(await read(() => look.scalePlacement('box')), 'owner', 'its place in a size drag is the modeller\'s (ADR-0140 4.4)')
|
|
699
694
|
})
|
|
700
|
-
|
|
701
|
-
test('the room is declared again only when the author confirms', async () => {
|
|
702
|
-
const prompt = id =>
|
|
703
|
-
read(id => {
|
|
704
|
-
const deep = (root = document) => [...root.querySelectorAll('ox-prompt')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
705
|
-
const button = deep().at(-1)?.shadowRoot?.querySelector(`#${id}`)
|
|
706
|
-
if (!button) return null
|
|
707
|
-
const r = button.getBoundingClientRect()
|
|
708
|
-
return r.width ? { x: r.left + r.width / 2, y: r.top + r.height / 2 } : null
|
|
709
|
-
}, id)
|
|
710
|
-
const start = await state()
|
|
711
|
-
await click(await at('button[room-refit]'), 'declare the room again')
|
|
712
|
-
await until(() => !!document.querySelector('ox-prompt'), [], 'asked')
|
|
713
|
-
await click(await poll(() => prompt('cancel')), 'cancel')
|
|
714
|
-
await until(() => !document.querySelector('ox-prompt'), [], 'the question gone')
|
|
715
|
-
assert.equal((await state()).revision, start.revision, 'cancelled: nothing written')
|
|
716
|
-
await click(await at('button[room-refit]'), 'declare the room again')
|
|
717
|
-
await click(await poll(() => prompt('confirm')), 'confirm')
|
|
718
|
-
await until(r => probe.state().revision === r + 1, [start.revision], 'confirmed: one step')
|
|
719
|
-
})
|
|
720
695
|
})
|
|
721
696
|
|
|
722
|
-
/** Asks until the answer is something, for a few seconds. */
|
|
723
|
-
async function poll(ask, ms = 5000) {
|
|
724
|
-
const deadline = Date.now() + ms
|
|
725
|
-
for (;;) {
|
|
726
|
-
const answer = await ask()
|
|
727
|
-
if (answer || Date.now() > deadline) return answer
|
|
728
|
-
await sleep(50)
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
697
|
/*
|
|
733
698
|
* The fastening window writes once (user 2026-10-17): whatever is tried in it is a preview, 「적용」 is one step of the
|
|
734
699
|
* history with all of it, and 「취소」 or Esc leave the history as it was.
|
|
@@ -769,9 +769,7 @@ describe('a part follows the figure size in one step (follow-size, 2026-09-26)',
|
|
|
769
769
|
assert.equal(after.revision, start.revision + 1, 'one step')
|
|
770
770
|
assert.deepEqual(await read(() => probe.fields('size.')), { 'size.x': 400, 'size.y': 400, 'size.z': 400 }, 'the size is the box as it stands')
|
|
771
771
|
const notice = await read(() => (window.__told ?? []).filter(n => n.level === 'info').at(-1)?.message ?? null)
|
|
772
|
-
|
|
773
|
-
assert.ok(notice?.includes('figure.text.size-and-space-declared-to-follow'), `a line says so: ${notice}`)
|
|
774
|
-
assert.equal(await read(() => !!document.querySelector('figure-editor').session.revisionNow().asset.occupancy), true, 'the space declared by the same step')
|
|
772
|
+
assert.ok(notice?.includes('figure.text.size-declared-to-follow'), `a line says so: ${notice}`)
|
|
775
773
|
const grown = await read(() => {
|
|
776
774
|
const editor = document.querySelector('figure-editor')
|
|
777
775
|
editor.session.apply([{ kind: 'set-design', input: 'size.x', unit: 'mm', value: 800 }])
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Only reading: every change the check makes goes through the mouse and the keyboard.
|
|
6
6
|
*/
|
|
7
7
|
import './editing-page.js'
|
|
8
|
-
import { addFigureBox, applyFigureAuthoring, createFigureAsset,
|
|
8
|
+
import { addFigureBox, applyFigureAuthoring, createFigureAsset, serializeFigureAsset } from '@hatiolab/figure-model'
|
|
9
9
|
|
|
10
10
|
const PALETTE = {
|
|
11
11
|
'palette.dark': '#2b2b2b',
|
|
@@ -42,7 +42,7 @@ Object.assign(window.figures, {
|
|
|
42
42
|
a = addFigureBox(a, { id: 'housing', size: { x: 300, y: 1000, z: 300 }, position: { x: 0, y: 500, z: 0 } })
|
|
43
43
|
a = addFigureBox(a, { id: 'stray', size: { x: 100, y: 100, z: 100 }, position: { x: 600, y: 50, z: 0 } })
|
|
44
44
|
for (const part of ['housing', 'stray']) a = applyFigureAuthoring(a, { kind: 'attach', part, to: { plane: 'mounting-plane' } })
|
|
45
|
-
return serializeFigureAsset(applyFigureAuthoring(a, { kind: '
|
|
45
|
+
return serializeFigureAsset(applyFigureAuthoring(a, { kind: 'set-placement', placement: 'floor' }))
|
|
46
46
|
},
|
|
47
47
|
rounded: () => {
|
|
48
48
|
let a = figure('Rounded')
|
|
@@ -288,25 +288,15 @@ describe('a part renamed in its name field: one step, still selected; a name ref
|
|
|
288
288
|
})
|
|
289
289
|
})
|
|
290
290
|
|
|
291
|
-
describe('a part
|
|
292
|
-
test('one step, nothing asked
|
|
291
|
+
describe('a part on the plane beside another removed: at once (2026-09-27)', () => {
|
|
292
|
+
test('one step, nothing asked', async () => {
|
|
293
293
|
await mount('box', 'gate')
|
|
294
294
|
await pick('stray')
|
|
295
295
|
const before = await revision()
|
|
296
|
-
const space = () => read(() => JSON.parse(document.querySelector('figure-editor').session.source()).occupancy ?? null)
|
|
297
|
-
const was = await space()
|
|
298
|
-
assert.ok(was, 'declared to begin with')
|
|
299
296
|
await click(await read(() => parts.op('remove-part')), 'remove')
|
|
300
297
|
assert.deepEqual(await ids(), ['housing'])
|
|
301
298
|
assert.equal(await revision(), before + 1, 'one step')
|
|
302
299
|
assert.equal(await read(() => editing.prompt()), null, 'nothing asked')
|
|
303
|
-
const now = await space()
|
|
304
|
-
assert.ok(now, 'the space is still declared')
|
|
305
|
-
assert.notDeepEqual(now, was, 'fitted to the part left')
|
|
306
|
-
const told = await read(() => editing.told())
|
|
307
|
-
assert.deepEqual(told.map(t => t.level), ['info'], JSON.stringify(told))
|
|
308
|
-
/* The page loads no translations: the words are the key. */
|
|
309
|
-
assert.ok(told[0].message.includes('figure.text.removal-space-refitted'), told[0].message)
|
|
310
300
|
})
|
|
311
301
|
})
|
|
312
302
|
|
|
@@ -285,7 +285,7 @@ describe('the history panel on the page', () => {
|
|
|
285
285
|
product.answer('figureVersions', () => [VERSION_1])
|
|
286
286
|
product.answer('inspectFigure', () => ({
|
|
287
287
|
blocked: true,
|
|
288
|
-
findings: ['
|
|
288
|
+
findings: ['off-mounting-plane', 'parts-off-placement-face', 'type-is-placeholder'].map(code => ({ code, message: code, why: null, how: null, at: null, blocking: true }))
|
|
289
289
|
}))
|
|
290
290
|
}, VERSION_1)
|
|
291
291
|
await open('h')
|
|
@@ -305,13 +305,12 @@ describe('the history panel on the page', () => {
|
|
|
305
305
|
assert.deepEqual(link, { href: '/figure-package?refs=acme%2Fagv%401', download: true })
|
|
306
306
|
})
|
|
307
307
|
|
|
308
|
-
test('
|
|
309
|
-
const
|
|
310
|
-
assert.deepEqual(
|
|
308
|
+
test('the findings stand in the history panel; an edit beside it is one step', async () => {
|
|
309
|
+
const shown = await until(p => { const found = product.deep(`${p} >>> li[finding]`); return found.length === 3 && found.map(li => li.querySelector('[msg]').textContent).sort() }, 'the findings', PANEL)
|
|
310
|
+
assert.deepEqual(shown, ['off-mounting-plane', 'parts-off-placement-face', 'type-is-placeholder'])
|
|
311
311
|
const before = await read(() => product.view())
|
|
312
|
-
await
|
|
312
|
+
await rename('H2')
|
|
313
313
|
await until(r => product.view().revision === r + 1, 'one step', before.revision)
|
|
314
|
-
await until(() => !!JSON.parse(product.view().source).occupancy, 'the room declared')
|
|
315
314
|
await openHistory()
|
|
316
315
|
})
|
|
317
316
|
|
|
@@ -557,7 +556,6 @@ describe('the page around the figure: the shell, the dock, the properties', () =
|
|
|
557
556
|
})
|
|
558
557
|
await until(r => product.view().revision === r + 1, 'one step', before.revision)
|
|
559
558
|
assert.equal(JSON.parse((await read(() => product.view())).source).placement, 'ceiling')
|
|
560
|
-
assert.equal(JSON.parse((await read(() => product.view())).source).occupancy, undefined, 'no room declared on the way')
|
|
561
559
|
})
|
|
562
560
|
|
|
563
561
|
test('unsaved: closing or reloading the tab is asked about', async () => {
|
|
@@ -806,8 +804,7 @@ describe('a mounting face named already', () => {
|
|
|
806
804
|
const before = await read(() => product.view())
|
|
807
805
|
await press('figure-properties >>> button[clear-mounting-face]', 'take the mounting face off')
|
|
808
806
|
await until(r => product.view().revision === r + 1, 'one step', before.revision)
|
|
809
|
-
assert.equal(JSON.parse((await read(() => product.view())).source).
|
|
810
|
-
assert.ok(JSON.parse((await read(() => product.view())).source).occupancy.bounds, 'the room stays')
|
|
807
|
+
assert.equal(JSON.parse((await read(() => product.view())).source).mountingFace, undefined, 'taken off')
|
|
811
808
|
await until(() => !product.deep('figure-modeller-page >>> figure-properties')[0].shadowRoot.querySelector('div[mounting-face]'), 'the line gone')
|
|
812
809
|
})
|
|
813
810
|
|
|
@@ -20,7 +20,7 @@ import { store } from '@operato/shell'
|
|
|
20
20
|
import { addressController } from '@operato/utils/address.js'
|
|
21
21
|
import './popup-stand-in.js'
|
|
22
22
|
import { html, render } from 'lit'
|
|
23
|
-
import { addFigureBox, applyFigureAuthoring, createFigureAsset, editFigurePart,
|
|
23
|
+
import { addFigureBox, applyFigureAuthoring, createFigureAsset, editFigurePart, serializeFigureAsset } from '@hatiolab/figure-model'
|
|
24
24
|
|
|
25
25
|
const PALETTE = { 'palette.dark': '#2b2b2b', 'palette.neutral': '#9a9a9a', 'palette.primary': '#477b9e', 'palette.accent': '#f79c40', 'palette.light': '#dddddd' }
|
|
26
26
|
|
|
@@ -35,7 +35,6 @@ export function mountedSource(placement = 'floor') {
|
|
|
35
35
|
let a = applyFigureAuthoring(createFigureAsset('Mounted'), { kind: 'set-placement', placement })
|
|
36
36
|
a.palette = { ...a.palette, ...PALETTE }
|
|
37
37
|
a = addFigureBox(a, { id: 'box', size: { x: 400, y: 400, z: 400 }, position: { x: 0, y: 200, z: 0 } })
|
|
38
|
-
a = applyFigureAuthoring(a, { kind: 'declare-occupancy', proposal: proposeFigureOccupancy(a) })
|
|
39
38
|
return serializeFigureAsset(applyFigureAuthoring(a, { kind: 'set-mounting-face', part: 'box', face: 'bottom' }))
|
|
40
39
|
}
|
|
41
40
|
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
compileFigureAsset,
|
|
17
17
|
createFigureAsset,
|
|
18
18
|
editFigurePart,
|
|
19
|
-
proposeFigureOccupancy,
|
|
20
19
|
robotArmFigure,
|
|
21
20
|
serializeFigureAsset
|
|
22
21
|
} from '@hatiolab/figure-model'
|
|
@@ -67,18 +66,15 @@ const FIGURES = {
|
|
|
67
66
|
let a = addFigureBox(named('Stack'), { id: 'base', size: { x: 1000, y: 200, z: 600 }, position: { x: 0, y: 100, z: 0 } })
|
|
68
67
|
a = addFigureBox(a, { id: 'top', size: { x: 200, y: 200, z: 200 }, position: { x: 0, y: 300, z: 0 } })
|
|
69
68
|
a = applyFigureAuthoring(a, { kind: 'attach', part: 'top', to: { part: 'base', face: 'top' } })
|
|
70
|
-
a = applyFigureAuthoring(a, { kind: 'declare-occupancy', proposal: proposeFigureOccupancy(a) })
|
|
71
69
|
return applyFigureAuthoring(a, { kind: 'set-mounting-face', part: 'base', face: 'bottom' })
|
|
72
70
|
},
|
|
73
|
-
/* A conveyor that follows its width, a roller every 200 mm
|
|
71
|
+
/* A conveyor that follows its width, a roller every 200 mm: the one figure stretched. */
|
|
74
72
|
stretch: () => {
|
|
75
73
|
let a = addFigureBox(named('Stretch'), { id: 'frame', size: { x: 2000, y: 100, z: 600 }, position: { x: 0, y: 50, z: 0 } })
|
|
76
74
|
a = addFigurePrimitive(a, { id: 'roller', kind: 'cylinder', dimensions: { radius: 30, length: 560 }, position: { x: 0, y: 0, z: 0 }, rotation: { x: 90, y: 0, z: 0 } })
|
|
77
75
|
a = applyFigureAuthoring(a, { kind: 'attach', part: 'roller', to: { part: 'frame', face: 'top' } })
|
|
78
76
|
a = applyFigureAuthoring(a, { kind: 'follow-size', part: 'frame', axes: ['x'] })
|
|
79
|
-
|
|
80
|
-
/* A space wider than the parts, as a spin swept or a padding gives: the box is the space, not the size (2026-10-16). */
|
|
81
|
-
return applyFigureAuthoring(a, { kind: 'declare-occupancy', proposal: proposeFigureOccupancy(a, { over: 'range' }), padding: { x: 100, y: 0, z: 0 } })
|
|
77
|
+
return applyFigureAuthoring(a, { kind: 'make-repeat', part: 'roller', axis: 'x', pitch: 200 })
|
|
82
78
|
},
|
|
83
79
|
/* The robot arm with a clip that turns its base: controls and a clip in the preview. */
|
|
84
80
|
'arm-clip': () => editFigurePart(robotArmFigure(), { kind: 'set-driver', state: 'j1-base', clip: 'cycle', duration: 4, keys: [{ seconds: 0, value: 0 }, { seconds: 4, value: 60 }] })
|
|
@@ -129,31 +129,10 @@ describe('the verdict tab over a figure, as the modeller page mounts it', () =>
|
|
|
129
129
|
await until('the answer is shown', () => !!alone.verdict.shadowRoot.querySelector('p[clear]'))
|
|
130
130
|
})
|
|
131
131
|
|
|
132
|
-
test('
|
|
133
|
-
const
|
|
134
|
-
await read((text, findings) => alone.mount(text, { findings }), await read(() => figureText('empty')), undeclared)
|
|
135
|
-
await until('the finding is shown', () => !!alone.verdict.shadowRoot.querySelector('li[finding]'))
|
|
136
|
-
assert.equal(await read(() => alone.verdict.shadowRoot.querySelectorAll('button[fit]').length), 0, 'nothing to measure, nothing offered')
|
|
137
|
-
await read((text, findings) => alone.mount(text, { findings }), await read(() => figureText('two')), undeclared)
|
|
138
|
-
await until('the finding is shown', () => !!alone.verdict.shadowRoot.querySelector('li[finding]'))
|
|
139
|
-
assert.equal(await read(() => alone.verdict.shadowRoot.querySelectorAll('li[finding] button[fit]').length), 1)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
test('the verdict tab and the release history offer the fit for the same findings, from questions that ask the same fields', async () => {
|
|
143
|
-
const findings = await read(() => ['part-outside-occupancy', 'occupancy-undeclared', 'mounting-face-floating'].map(code => alone.finding(code)))
|
|
132
|
+
test('the verdict tab and the release history show the same findings, from questions that ask the same fields', async () => {
|
|
133
|
+
const findings = await read(() => ['off-mounting-plane', 'type-is-placeholder', 'mounting-face-floating'].map(code => alone.finding(code)))
|
|
144
134
|
await read((text, findings) => alone.mount(text, { figureId: 'fig-1', findings, history: true }), await read(() => figureText('two')), findings)
|
|
145
|
-
const offered = (host, button) =>
|
|
146
|
-
read(
|
|
147
|
-
(host, button) => {
|
|
148
|
-
const root = document.querySelector(host)?.shadowRoot
|
|
149
|
-
return root ? [...root.querySelectorAll('li[finding]')].filter(li => li.querySelector(button)).map(li => li.querySelector('span[msg]').textContent.trim()).sort() : null
|
|
150
|
-
},
|
|
151
|
-
host,
|
|
152
|
-
button
|
|
153
|
-
)
|
|
154
135
|
await until('both judged', () => ['figure-verdict', 'figure-history-panel'].every(host => document.querySelector(host)?.shadowRoot?.querySelectorAll('li[finding]').length === 3))
|
|
155
|
-
assert.deepEqual(await offered('figure-verdict', 'button[fit]'), ['occupancy-undeclared', 'part-outside-occupancy'])
|
|
156
|
-
assert.deepEqual(await offered('figure-history-panel', 'button[fit-base]'), await offered('figure-verdict', 'button[fit]'), 'the same two, in both places')
|
|
157
136
|
const draft = await read(() => alone.fieldsAsked('inspectFigureDraft'))
|
|
158
137
|
assert.ok(draft?.includes('why') && draft.includes('how'), `the draft's question asks why and how: ${draft}`)
|
|
159
138
|
assert.equal(draft, await read(() => alone.fieldsAsked('inspectFigure')), 'the saved figure\'s question asks the same fields')
|
|
@@ -783,8 +762,8 @@ describe('the preview stands one figure the author stretches (first 15 minutes,
|
|
|
783
762
|
slider.dispatchEvent(new Event('input'))
|
|
784
763
|
slider.dispatchEvent(new Event('change'))
|
|
785
764
|
})
|
|
786
|
-
/* The box is the
|
|
787
|
-
await until('the figure 6 m wide,
|
|
765
|
+
/* The box is the figure's body at 6 m: the frame, as wide as asked (ADR-0145). */
|
|
766
|
+
await until('the figure 6 m wide, its body', () => Math.round(screenCheck.deep('figure-preview')[0].scene.root.components[0].model.width) === 6000)
|
|
788
767
|
const said = await read(() => screenCheck.deep('figure-preview')[0].shadowRoot.querySelector('div[sizes] p[said]').textContent.trim())
|
|
789
768
|
assert.ok(said.startsWith('6,000 × '), said)
|
|
790
769
|
const box = await read(() => { const m = screenCheck.deep('figure-preview')[0].scene.root.components[0].model; return m.left + m.width / 2 })
|
|
@@ -988,9 +967,9 @@ describe('the modeller page: the editor with the page\'s figure panel, verdict a
|
|
|
988
967
|
await open('fig-a')
|
|
989
968
|
})
|
|
990
969
|
|
|
991
|
-
/* First 15 minutes (2026-10-16):
|
|
992
|
-
test('what blocks release is said beside 「발행」,
|
|
993
|
-
const answer = blocked => read(blocked => product.answer('inspectFigureDraft', () => ({ __typename: 'FigureInspection', blocked, findings: blocked ? [alone.finding('
|
|
970
|
+
/* First 15 minutes (2026-10-16): what refused release was said only at the foot of the verdict tab. */
|
|
971
|
+
test('what blocks release is said beside 「발행」, the list under the bar', async () => {
|
|
972
|
+
const answer = blocked => read(blocked => product.answer('inspectFigureDraft', () => ({ __typename: 'FigureInspection', blocked, findings: blocked ? [alone.finding('type-is-placeholder')] : [] })), blocked)
|
|
994
973
|
const bar = () => read(() => [...screenCheck.editor().shadowRoot.querySelectorAll('ox-canvas-bar > button[slot=commit]')].map(b => (b.hasAttribute('bar-overlay') ? 'overlay' : b.hasAttribute('release-blocked') ? 'blocked' : b.hasAttribute('publish') ? 'publish' : 'save')))
|
|
995
974
|
await answer(true)
|
|
996
975
|
await open('fig-block', 'two', 'probe-block')
|
|
@@ -998,12 +977,7 @@ describe('the modeller page: the editor with the page\'s figure panel, verdict a
|
|
|
998
977
|
assert.deepEqual(await bar(), ['blocked', 'publish', 'save'])
|
|
999
978
|
await press('what blocks release', () => screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar button[release-blocked]'))
|
|
1000
979
|
await until('the list under the bar', () => !!screenCheck.editor().shadowRoot.querySelector('div[release-blockers] li[finding]'))
|
|
1001
|
-
assert.equal(await read(() => !!screenCheck.editor().session.revisionNow().asset.occupancy), false)
|
|
1002
980
|
await answer(false)
|
|
1003
|
-
await press('fit to the parts', () => screenCheck.editor().shadowRoot.querySelector('div[release-blockers] button[act]'))
|
|
1004
|
-
await until('the space declared, in one step', () => !!screenCheck.editor().session.revisionNow().asset.occupancy)
|
|
1005
|
-
await until('nothing blocks: the word is gone', () => !screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar button[release-blocked]'))
|
|
1006
|
-
assert.deepEqual(await bar(), ['publish', 'save'])
|
|
1007
981
|
await read(() => product.answer('inspectFigureDraft'))
|
|
1008
982
|
await saved()
|
|
1009
983
|
await open('fig-a')
|
|
@@ -1011,7 +985,7 @@ describe('the modeller page: the editor with the page\'s figure panel, verdict a
|
|
|
1011
985
|
|
|
1012
986
|
/* First 15 minutes, again (2026-10-16 evening): a new figure, nothing in it yet, showed 「발행 막힘 1」 in red from the start. */
|
|
1013
987
|
test('a figure with no parts has nothing red beside 「발행」; the verdict still holds what blocks it', async () => {
|
|
1014
|
-
const answer = blocked => read(blocked => product.answer('inspectFigureDraft', () => ({ __typename: 'FigureInspection', blocked, findings: blocked ? [alone.finding('
|
|
988
|
+
const answer = blocked => read(blocked => product.answer('inspectFigureDraft', () => ({ __typename: 'FigureInspection', blocked, findings: blocked ? [alone.finding('type-is-placeholder')] : [] })), blocked)
|
|
1015
989
|
await answer(true)
|
|
1016
990
|
try {
|
|
1017
991
|
await open('fig-empty-bar', 'empty', 'probe-empty-bar')
|
|
@@ -1120,37 +1094,6 @@ describe('the modeller page: the editor with the page\'s figure panel, verdict a
|
|
|
1120
1094
|
await until('saved', () => !screenCheck.editor().session.read().dirty)
|
|
1121
1095
|
})
|
|
1122
1096
|
|
|
1123
|
-
test('the verdict\'s 「부품에 맞추기」 declares the space from the parts, in one step', async () => {
|
|
1124
|
-
await read(finding => product.answer('inspectFigureDraft', () => ({ __typename: 'FigureInspection', blocked: true, findings: [finding] })), await read(() => alone.finding('occupancy-undeclared')))
|
|
1125
|
-
try {
|
|
1126
|
-
await open('fig-b', 'two')
|
|
1127
|
-
assert.equal(await read(() => JSON.parse(screenCheck.editor().session.source()).occupancy), undefined, 'no space declared yet')
|
|
1128
|
-
await pressTab(TAB('verdict'))
|
|
1129
|
-
await until('offered', () => !!screenCheck.deep('figure-verdict')[0]?.shadowRoot?.querySelector('button[fit]'))
|
|
1130
|
-
const before = await revision()
|
|
1131
|
-
await press('fit to the parts', () => screenCheck.deep('figure-verdict')[0].shadowRoot.querySelector('button[fit]'))
|
|
1132
|
-
await until('one step', was => screenCheck.editor().session.read().revision === was + 1, before)
|
|
1133
|
-
assert.ok(await read(() => !!JSON.parse(screenCheck.editor().session.source()).occupancy), 'the space is declared')
|
|
1134
|
-
await saved()
|
|
1135
|
-
} finally {
|
|
1136
|
-
await read(() => product.answer('inspectFigureDraft'))
|
|
1137
|
-
}
|
|
1138
|
-
})
|
|
1139
|
-
|
|
1140
|
-
test('fitting a figure with no parts says so in the app\'s words and leaves the editor standing', async () => {
|
|
1141
|
-
await read(finding => product.answer('inspectFigure', () => ({ __typename: 'FigureInspection', blocked: true, findings: [finding] })), await read(() => alone.finding('occupancy-undeclared')))
|
|
1142
|
-
try {
|
|
1143
|
-
await open('fig-empty', 'empty')
|
|
1144
|
-
await openOverlay('history')
|
|
1145
|
-
await until('offered', () => !!screenCheck.deep('figure-history-panel')[0]?.shadowRoot?.querySelector('button[fit-base]'))
|
|
1146
|
-
await press('fit to the parts', () => screenCheck.deep('figure-history-panel')[0].shadowRoot.querySelector('button[fit-base]'))
|
|
1147
|
-
await until('said', () => screenCheck.deep('p[fit-error]')[0]?.textContent.trim() === 'figure.text.fit-needs-parts')
|
|
1148
|
-
assert.equal(await read(() => !!screenCheck.editor()?.isConnected), true, 'the editor stands')
|
|
1149
|
-
} finally {
|
|
1150
|
-
await read(() => product.answer('inspectFigure'))
|
|
1151
|
-
}
|
|
1152
|
-
})
|
|
1153
|
-
|
|
1154
1097
|
test('the view set in the bar\'s menu holds for the next figure opened: its board is stood up with it', async () => {
|
|
1155
1098
|
await openOverlay('view')
|
|
1156
1099
|
const colour = await element('the sky colour', () => screenCheck.setting('figure.label.sky-color').querySelector('input[type=color]'))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { test } from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
3
|
import { readFileSync } from 'node:fs'
|
|
4
|
-
import { addFigureBox, applyFigureAuthoring, applyFigureProposal, createFigureAsset, planFigureResize
|
|
4
|
+
import { addFigureBox, applyFigureAuthoring, applyFigureProposal, createFigureAsset, planFigureResize } from '@hatiolab/figure-model'
|
|
5
5
|
import { followWordsOf, partSummaryOf } from '../client/modeller/links.js'
|
|
6
6
|
|
|
7
7
|
/*
|
|
@@ -18,7 +18,7 @@ test('a selected part at the head: its kind, and what its size follows only when
|
|
|
18
18
|
const ko = JSON.parse(read('../translations/ko.json')) as Record<string, string>
|
|
19
19
|
const sayKo = (key: string, params: Record<string, string> = {}) => Object.entries(params).reduce((t, [k, v]) => t.split(`{${k}}`).join(v), ko[key] ?? key)
|
|
20
20
|
let a = addFigureBox(createFigureAsset('UX'), { id: 'box', size: { x: 100, y: 100, z: 100 }, position: { x: 0, y: 50, z: 0 } })
|
|
21
|
-
a = applyFigureAuthoring(a, { kind: '
|
|
21
|
+
a = applyFigureAuthoring(a, { kind: 'set-placement', placement: 'floor' })
|
|
22
22
|
/* As usual -- a size of its own, loose: nothing but what it is. */
|
|
23
23
|
assert.deepEqual(partSummaryOf(a, 'box', say), { kind: 'figure.label.primitive-cube', size: '' })
|
|
24
24
|
const only = applyFigureProposal(a, planFigureResize(a, { part: 'box', axes: ['x'] }).steps)
|
|
@@ -47,7 +47,6 @@ test('the start card and the figure panel choose the placement as the figure\'s
|
|
|
47
47
|
const { createFigureAsset, applyFigureAuthoring } = await import('@hatiolab/figure-model')
|
|
48
48
|
const hung = applyFigureAuthoring(createFigureAsset('New'), { kind: 'set-placement', placement: 'ceiling' })
|
|
49
49
|
assert.equal(hung.placement, 'ceiling')
|
|
50
|
-
assert.equal(hung.occupancy, undefined)
|
|
51
50
|
/* Chosen on the start card and in the figure panel, before any part: the browser check's (test/browser/screen.check.mjs). */
|
|
52
51
|
})
|
|
53
52
|
|
|
@@ -88,11 +88,6 @@
|
|
|
88
88
|
"unit": "ratio",
|
|
89
89
|
"value": 0.3
|
|
90
90
|
},
|
|
91
|
-
{
|
|
92
|
-
"id": "k.minusHalf",
|
|
93
|
-
"unit": "ratio",
|
|
94
|
-
"value": -0.5
|
|
95
|
-
},
|
|
96
91
|
{
|
|
97
92
|
"id": "k.zero",
|
|
98
93
|
"unit": "mm",
|
|
@@ -379,50 +374,6 @@
|
|
|
379
374
|
"value": "badge.y.fromBottom.value"
|
|
380
375
|
}
|
|
381
376
|
},
|
|
382
|
-
{
|
|
383
|
-
"id": "occupancy.x.min",
|
|
384
|
-
"op": "mul@1",
|
|
385
|
-
"args": [
|
|
386
|
-
"size.x",
|
|
387
|
-
"k.minusHalf"
|
|
388
|
-
],
|
|
389
|
-
"outputs": {
|
|
390
|
-
"value": "occupancy.x.min.value"
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
{
|
|
394
|
-
"id": "occupancy.x.max",
|
|
395
|
-
"op": "mul@1",
|
|
396
|
-
"args": [
|
|
397
|
-
"size.x",
|
|
398
|
-
"k.half"
|
|
399
|
-
],
|
|
400
|
-
"outputs": {
|
|
401
|
-
"value": "occupancy.x.max.value"
|
|
402
|
-
}
|
|
403
|
-
},
|
|
404
|
-
{
|
|
405
|
-
"id": "occupancy.z.min",
|
|
406
|
-
"op": "mul@1",
|
|
407
|
-
"args": [
|
|
408
|
-
"size.z",
|
|
409
|
-
"k.minusHalf"
|
|
410
|
-
],
|
|
411
|
-
"outputs": {
|
|
412
|
-
"value": "occupancy.z.min.value"
|
|
413
|
-
}
|
|
414
|
-
},
|
|
415
|
-
{
|
|
416
|
-
"id": "occupancy.z.max",
|
|
417
|
-
"op": "mul@1",
|
|
418
|
-
"args": [
|
|
419
|
-
"size.z",
|
|
420
|
-
"k.half"
|
|
421
|
-
],
|
|
422
|
-
"outputs": {
|
|
423
|
-
"value": "occupancy.z.max.value"
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
377
|
{
|
|
427
378
|
"id": "hinge.origin.x",
|
|
428
379
|
"op": "add@1",
|
|
@@ -1213,21 +1164,5 @@
|
|
|
1213
1164
|
}
|
|
1214
1165
|
}
|
|
1215
1166
|
],
|
|
1216
|
-
"
|
|
1217
|
-
"bounds": {
|
|
1218
|
-
"x": {
|
|
1219
|
-
"min": "occupancy.x.min.value",
|
|
1220
|
-
"max": "occupancy.x.max.value"
|
|
1221
|
-
},
|
|
1222
|
-
"y": {
|
|
1223
|
-
"min": "k.zero",
|
|
1224
|
-
"max": "size.y"
|
|
1225
|
-
},
|
|
1226
|
-
"z": {
|
|
1227
|
-
"min": "occupancy.z.min.value",
|
|
1228
|
-
"max": "occupancy.z.max.value"
|
|
1229
|
-
}
|
|
1230
|
-
},
|
|
1231
|
-
"placement": "floor"
|
|
1232
|
-
}
|
|
1167
|
+
"placement": "floor"
|
|
1233
1168
|
}
|
|
@@ -83,11 +83,6 @@
|
|
|
83
83
|
"unit": "ratio",
|
|
84
84
|
"value": 0.25
|
|
85
85
|
},
|
|
86
|
-
{
|
|
87
|
-
"id": "k.minusHalf",
|
|
88
|
-
"unit": "ratio",
|
|
89
|
-
"value": -0.5
|
|
90
|
-
},
|
|
91
86
|
{
|
|
92
87
|
"id": "k.zero",
|
|
93
88
|
"unit": "mm",
|
|
@@ -293,50 +288,6 @@
|
|
|
293
288
|
"value": "roller.y.fromBottom.value"
|
|
294
289
|
}
|
|
295
290
|
},
|
|
296
|
-
{
|
|
297
|
-
"id": "occupancy.x.min",
|
|
298
|
-
"op": "mul@1",
|
|
299
|
-
"args": [
|
|
300
|
-
"size.x",
|
|
301
|
-
"k.minusHalf"
|
|
302
|
-
],
|
|
303
|
-
"outputs": {
|
|
304
|
-
"value": "occupancy.x.min.value"
|
|
305
|
-
}
|
|
306
|
-
},
|
|
307
|
-
{
|
|
308
|
-
"id": "occupancy.x.max",
|
|
309
|
-
"op": "mul@1",
|
|
310
|
-
"args": [
|
|
311
|
-
"size.x",
|
|
312
|
-
"k.half"
|
|
313
|
-
],
|
|
314
|
-
"outputs": {
|
|
315
|
-
"value": "occupancy.x.max.value"
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
{
|
|
319
|
-
"id": "occupancy.z.min",
|
|
320
|
-
"op": "mul@1",
|
|
321
|
-
"args": [
|
|
322
|
-
"size.z",
|
|
323
|
-
"k.minusHalf"
|
|
324
|
-
],
|
|
325
|
-
"outputs": {
|
|
326
|
-
"value": "occupancy.z.min.value"
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
"id": "occupancy.z.max",
|
|
331
|
-
"op": "mul@1",
|
|
332
|
-
"args": [
|
|
333
|
-
"size.z",
|
|
334
|
-
"k.half"
|
|
335
|
-
],
|
|
336
|
-
"outputs": {
|
|
337
|
-
"value": "occupancy.z.max.value"
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
291
|
{
|
|
341
292
|
"id": "hinge.origin.x",
|
|
342
293
|
"op": "add@1",
|
|
@@ -909,21 +860,5 @@
|
|
|
909
860
|
}
|
|
910
861
|
}
|
|
911
862
|
],
|
|
912
|
-
"
|
|
913
|
-
"bounds": {
|
|
914
|
-
"x": {
|
|
915
|
-
"min": "occupancy.x.min.value",
|
|
916
|
-
"max": "occupancy.x.max.value"
|
|
917
|
-
},
|
|
918
|
-
"y": {
|
|
919
|
-
"min": "k.zero",
|
|
920
|
-
"max": "size.y"
|
|
921
|
-
},
|
|
922
|
-
"z": {
|
|
923
|
-
"min": "occupancy.z.min.value",
|
|
924
|
-
"max": "occupancy.z.max.value"
|
|
925
|
-
}
|
|
926
|
-
},
|
|
927
|
-
"placement": "floor"
|
|
928
|
-
}
|
|
863
|
+
"placement": "floor"
|
|
929
864
|
}
|