@hatiolab/figure-model 0.1.92 → 0.1.94
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/CHANGELOG.md +12 -12
- package/README.md +25 -25
- package/dist/{v3-asset-types.d.ts → asset-types.d.ts} +73 -67
- package/dist/asset-types.d.ts.map +1 -0
- package/dist/asset-types.js +2 -0
- package/dist/asset-types.js.map +1 -0
- package/dist/asset.d.ts +20 -0
- package/dist/asset.d.ts.map +1 -0
- package/dist/{v3-asset.js → asset.js} +49 -28
- package/dist/asset.js.map +1 -0
- package/dist/{v3-authoring-actions.d.ts → authoring-actions.d.ts} +77 -66
- package/dist/authoring-actions.d.ts.map +1 -0
- package/dist/{v3-authoring-actions.js → authoring-actions.js} +129 -104
- package/dist/authoring-actions.js.map +1 -0
- package/dist/{v3-authoring-session.d.ts → authoring-session.d.ts} +23 -21
- package/dist/authoring-session.d.ts.map +1 -0
- package/dist/{v3-authoring-session.js → authoring-session.js} +4 -4
- package/dist/authoring-session.js.map +1 -0
- package/dist/capabilities.d.ts +3 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/{v3-capabilities.js → capabilities.js} +8 -8
- package/dist/capabilities.js.map +1 -0
- package/dist/clip-keeping.d.ts +30 -0
- package/dist/clip-keeping.d.ts.map +1 -0
- package/dist/clip-keeping.js +77 -0
- package/dist/clip-keeping.js.map +1 -0
- package/dist/command-preview.d.ts +125 -0
- package/dist/command-preview.d.ts.map +1 -0
- package/dist/command-preview.js +246 -0
- package/dist/command-preview.js.map +1 -0
- package/dist/contract.d.ts +7 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +12 -0
- package/dist/contract.js.map +1 -0
- package/dist/{v3-cost.d.ts → cost.d.ts} +5 -5
- package/dist/cost.d.ts.map +1 -0
- package/dist/{v3-cost.js → cost.js} +11 -11
- package/dist/cost.js.map +1 -0
- package/dist/{v3-create.d.ts → create.d.ts} +8 -8
- package/dist/{v3-create.d.ts.map → create.d.ts.map} +1 -1
- package/dist/{v3-create.js → create.js} +26 -26
- package/dist/create.js.map +1 -0
- package/dist/document.d.ts +5 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +31 -0
- package/dist/document.js.map +1 -0
- package/dist/{v3-driver.d.ts → driver.d.ts} +10 -10
- package/dist/driver.d.ts.map +1 -0
- package/dist/{v3-driver.js → driver.js} +10 -10
- package/dist/driver.js.map +1 -0
- package/dist/{v3-editor-workspace.d.ts → editor-workspace.d.ts} +99 -73
- package/dist/editor-workspace.d.ts.map +1 -0
- package/dist/{v3-editor-workspace.js → editor-workspace.js} +48 -32
- package/dist/editor-workspace.js.map +1 -0
- package/dist/{v3-examples.d.ts → examples.d.ts} +5 -5
- package/dist/examples.d.ts.map +1 -0
- package/dist/{v3-examples.js → examples.js} +11 -11
- package/dist/examples.js.map +1 -0
- package/dist/{v3-follow.d.ts → follow.d.ts} +5 -5
- package/dist/follow.d.ts.map +1 -0
- package/dist/{v3-follow.js → follow.js} +2 -2
- package/dist/follow.js.map +1 -0
- package/dist/{v3-gate.d.ts → gate.d.ts} +36 -28
- package/dist/gate.d.ts.map +1 -0
- package/dist/{v3-gate.js → gate.js} +54 -48
- package/dist/gate.js.map +1 -0
- package/dist/{v3-graph-types.d.ts → graph-types.d.ts} +33 -33
- package/dist/graph-types.d.ts.map +1 -0
- package/dist/graph-types.js +2 -0
- package/dist/graph-types.js.map +1 -0
- package/dist/{v3-graph.d.ts → graph.d.ts} +14 -14
- package/dist/graph.d.ts.map +1 -0
- package/dist/{v3-graph.js → graph.js} +16 -16
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +39 -33
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -33
- package/dist/index.js.map +1 -1
- package/dist/{v3-instance-size.d.ts → instance-size.d.ts} +14 -14
- package/dist/instance-size.d.ts.map +1 -0
- package/dist/{v3-instance-size.js → instance-size.js} +13 -13
- package/dist/instance-size.js.map +1 -0
- package/dist/json.d.ts +12 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/{v3-json.js → json.js} +13 -13
- package/dist/json.js.map +1 -0
- package/dist/kernel-version.d.ts +8 -0
- package/dist/kernel-version.d.ts.map +1 -0
- package/dist/{v3-kernel-version.js → kernel-version.js} +4 -4
- package/dist/kernel-version.js.map +1 -0
- package/dist/{v3-part-edit.d.ts → part-edit.d.ts} +12 -11
- package/dist/part-edit.d.ts.map +1 -0
- package/dist/{v3-part-edit.js → part-edit.js} +84 -68
- package/dist/part-edit.js.map +1 -0
- package/dist/proposal.d.ts +55 -0
- package/dist/proposal.d.ts.map +1 -0
- package/dist/{v3-proposal.js → proposal.js} +29 -21
- package/dist/proposal.js.map +1 -0
- package/dist/{v3-public-inputs.d.ts → public-inputs.d.ts} +4 -4
- package/dist/public-inputs.d.ts.map +1 -0
- package/dist/{v3-public-inputs.js → public-inputs.js} +3 -3
- package/dist/public-inputs.js.map +1 -0
- package/dist/recipe-build.d.ts +27 -0
- package/dist/recipe-build.d.ts.map +1 -0
- package/dist/{v3-recipe-build.js → recipe-build.js} +9 -9
- package/dist/recipe-build.js.map +1 -0
- package/dist/recipe-from-parts.d.ts +8 -0
- package/dist/recipe-from-parts.d.ts.map +1 -0
- package/dist/{v3-recipe-from-parts.js → recipe-from-parts.js} +9 -9
- package/dist/recipe-from-parts.js.map +1 -0
- package/dist/{v3-recipe.d.ts → recipe.d.ts} +25 -25
- package/dist/recipe.d.ts.map +1 -0
- package/dist/{v3-recipe.js → recipe.js} +30 -30
- package/dist/recipe.js.map +1 -0
- package/dist/release-contract.d.ts +19 -0
- package/dist/release-contract.d.ts.map +1 -0
- package/dist/{v3-release-contract.js → release-contract.js} +8 -7
- package/dist/release-contract.js.map +1 -0
- package/dist/{v3-render-batches.d.ts → render-batches.d.ts} +8 -8
- package/dist/render-batches.d.ts.map +1 -0
- package/dist/{v3-render-batches.js → render-batches.js} +4 -4
- package/dist/render-batches.js.map +1 -0
- package/dist/resize-plan.d.ts +61 -0
- package/dist/resize-plan.d.ts.map +1 -0
- package/dist/resize-plan.js +189 -0
- package/dist/resize-plan.js.map +1 -0
- package/dist/room.d.ts +53 -0
- package/dist/room.d.ts.map +1 -0
- package/dist/room.js +90 -0
- package/dist/room.js.map +1 -0
- package/dist/{v3-score.d.ts → score.d.ts} +19 -19
- package/dist/score.d.ts.map +1 -0
- package/dist/{v3-score.js → score.js} +32 -32
- package/dist/score.js.map +1 -0
- package/dist/shape-provider.d.ts +37 -0
- package/dist/shape-provider.d.ts.map +1 -0
- package/dist/shape-provider.js +69 -0
- package/dist/shape-provider.js.map +1 -0
- package/dist/{v3-shape-sampling.d.ts → shape-sampling.d.ts} +4 -4
- package/dist/shape-sampling.d.ts.map +1 -0
- package/dist/{v3-shape-sampling.js → shape-sampling.js} +3 -3
- package/dist/shape-sampling.js.map +1 -0
- package/dist/size-edit.d.ts +38 -0
- package/dist/size-edit.d.ts.map +1 -0
- package/dist/size-edit.js +178 -0
- package/dist/size-edit.js.map +1 -0
- package/dist/{v3-surface.d.ts → surface.d.ts} +5 -5
- package/dist/surface.d.ts.map +1 -0
- package/dist/{v3-surface.js → surface.js} +7 -7
- package/dist/surface.js.map +1 -0
- package/dist/{v3-words.d.ts → words.d.ts} +3 -3
- package/dist/words.d.ts.map +1 -0
- package/dist/{v3-words.js → words.js} +3 -3
- package/dist/words.js.map +1 -0
- package/docs/{assembly-constraints-v3.md → assembly-constraints.md} +1 -1
- package/docs/{v3-asset-persistence.md → asset-persistence.md} +8 -8
- package/docs/{v3-authoring-transactions.md → authoring-transactions.md} +2 -2
- package/docs/{v3-capability-bindings.md → capability-bindings.md} +8 -8
- package/docs/{v3-common-kernel-experiment.md → common-kernel-experiment.md} +4 -4
- package/docs/{v3-core-semantics.md → core-semantics.md} +5 -5
- package/docs/{v3-cutover.md → cutover.md} +4 -4
- package/docs/{v3-design.md → design.md} +11 -11
- package/docs/{v3-document-contract.md → document-contract.md} +9 -9
- package/docs/{v3-dryroom-profile-validation.md → dryroom-profile-validation.md} +4 -4
- package/docs/{v3-editor-integration.md → editor-integration.md} +6 -6
- package/docs/{v3-full-conveyor-validation.md → full-conveyor-validation.md} +4 -4
- package/docs/{v3-full-dryroom-validation.md → full-dryroom-validation.md} +4 -4
- package/docs/{v3-full-forklift-validation.md → full-forklift-validation.md} +5 -5
- package/docs/{v3-full-robot-validation.md → full-robot-validation.md} +4 -4
- package/docs/{v3-layout-system.md → layout-system.md} +4 -4
- package/docs/{v3-legacy-risk-audit-2026-09-23.md → legacy-risk-audit-2026-09-23.md} +5 -5
- package/docs/{v3-minimal-model.md → minimal-model.md} +6 -6
- package/docs/{v3-motion-contract.md → motion-contract.md} +3 -3
- package/docs/{v3-openusd-review.md → openusd-review.md} +8 -8
- package/docs/{v3-operator-contracts.md → operator-contracts.md} +6 -6
- package/docs/{v3-payload-collision-validation.md → payload-collision-validation.md} +4 -4
- package/docs/{v3-payload-contact-validation.md → payload-contact-validation.md} +5 -5
- package/docs/prototypes/README.md +10 -10
- package/docs/prototypes/{v3-asset-samples.mjs → asset-samples.mjs} +6 -6
- package/docs/prototypes/{v3-asset-store.mjs → asset-store.mjs} +3 -3
- package/docs/prototypes/{v3-asset-store.test.mjs → asset-store.test.mjs} +6 -6
- package/docs/prototypes/asset.mjs +1 -0
- package/docs/prototypes/{v3-asset.schema.json → asset.schema.json} +2 -2
- package/docs/prototypes/{v3-asset.test.mjs → asset.test.mjs} +10 -10
- package/docs/prototypes/authoring-session.mjs +1 -0
- package/docs/prototypes/{v3-authoring-session.test.mjs → authoring-session.test.mjs} +6 -6
- package/docs/prototypes/capability-plan.mjs +1 -0
- package/docs/prototypes/{v3-capability-plan.test.mjs → capability-plan.test.mjs} +5 -5
- package/docs/prototypes/{v3-capability-samples.mjs → capability-samples.mjs} +2 -2
- package/docs/prototypes/document.mjs +2 -0
- package/docs/prototypes/{v3-document.schema.json → document.schema.json} +1 -1
- package/docs/prototypes/{v3-document.test.mjs → document.test.mjs} +7 -7
- package/docs/prototypes/{v3-dryroom-mesh.mjs → dryroom-mesh.mjs} +1 -1
- package/docs/prototypes/{v3-dryroom-mesh.test.mjs → dryroom-mesh.test.mjs} +1 -1
- package/docs/prototypes/{v3-editor-browser.mjs → editor-browser.mjs} +13 -13
- package/docs/prototypes/editor-workspace.mjs +1 -0
- package/docs/prototypes/{v3-editor-workspace.test.mjs → editor-workspace.test.mjs} +3 -3
- package/docs/prototypes/{v3-feature-pattern.test.mjs → feature-pattern.test.mjs} +1 -1
- package/docs/prototypes/{v3-full-conveyor.mjs → full-conveyor.mjs} +2 -2
- package/docs/prototypes/{v3-full-conveyor.test.mjs → full-conveyor.test.mjs} +3 -3
- package/docs/prototypes/{v3-full-dryroom.mjs → full-dryroom.mjs} +2 -2
- package/docs/prototypes/{v3-full-dryroom.test.mjs → full-dryroom.test.mjs} +3 -3
- package/docs/prototypes/{v3-full-forklift.mjs → full-forklift.mjs} +2 -2
- package/docs/prototypes/{v3-full-forklift.test.mjs → full-forklift.test.mjs} +3 -3
- package/docs/prototypes/{v3-full-robot.mjs → full-robot.mjs} +2 -2
- package/docs/prototypes/{v3-full-robot.test.mjs → full-robot.test.mjs} +4 -4
- package/docs/prototypes/{v3-kernel.test.mjs → kernel.test.mjs} +1 -1
- package/docs/prototypes/{v3-layout.test.mjs → layout.test.mjs} +1 -1
- package/docs/prototypes/{v3-more-samples.test.mjs → more-samples.test.mjs} +2 -2
- package/docs/prototypes/{v3-payload-collision.mjs → payload-collision.mjs} +2 -2
- package/docs/prototypes/{v3-payload-collision.test.mjs → payload-collision.test.mjs} +3 -3
- package/docs/prototypes/{v3-payload-fit.mjs → payload-fit.mjs} +2 -2
- package/docs/prototypes/{v3-payload-fit.test.mjs → payload-fit.test.mjs} +4 -4
- package/docs/prototypes/render-full-dryroom.mjs +12 -12
- package/docs/prototypes/{v3-repeated-binding.mjs → repeated-binding.mjs} +1 -1
- package/docs/prototypes/{v3-repeated-binding.test.mjs → repeated-binding.test.mjs} +6 -6
- package/docs/prototypes/{v3-runtime-browser.mjs → runtime-browser.mjs} +26 -26
- package/docs/prototypes/{v3-samples.test.mjs → samples.test.mjs} +1 -1
- package/docs/prototypes/{v3-spatial-plan.test.mjs → spatial-plan.test.mjs} +1 -1
- package/docs/prototypes/typed-plan.mjs +3 -0
- package/docs/prototypes/{v3-typed-plan.test.mjs → typed-plan.test.mjs} +1 -1
- package/docs/prototypes/{v3-typed-repeat.test.mjs → typed-repeat.test.mjs} +2 -2
- package/docs/{v3-repeated-binding-validation.md → repeated-binding-validation.md} +4 -4
- package/docs/{v3-research-review.md → research-review.md} +2 -2
- package/docs/{v3-review-2026-09-22.md → review-2026-09-22.md} +10 -10
- package/docs/{v3-runtime-extraction.md → runtime-extraction.md} +16 -16
- package/docs/{v3-sample-validation-expanded.md → sample-validation-expanded.md} +5 -5
- package/docs/{v3-sample-validation.md → sample-validation.md} +3 -3
- package/docs/{v3-shape-dimension-contract.md → shape-dimension-contract.md} +12 -12
- package/docs/{v3-shapeassembly-reproduction.md → shapeassembly-reproduction.md} +4 -4
- package/docs/{v3-spatial-contracts.md → spatial-contracts.md} +5 -5
- package/docs/status.md +12 -0
- package/docs/{v3-storage-integration.md → storage-integration.md} +5 -5
- package/docs/{v3-typed-repeat-contracts.md → typed-repeat-contracts.md} +3 -3
- package/package.json +3 -3
- package/dist/v3-asset-types.d.ts.map +0 -1
- package/dist/v3-asset-types.js +0 -2
- package/dist/v3-asset-types.js.map +0 -1
- package/dist/v3-asset.d.ts +0 -8
- package/dist/v3-asset.d.ts.map +0 -1
- package/dist/v3-asset.js.map +0 -1
- package/dist/v3-authoring-actions.d.ts.map +0 -1
- package/dist/v3-authoring-actions.js.map +0 -1
- package/dist/v3-authoring-session.d.ts.map +0 -1
- package/dist/v3-authoring-session.js.map +0 -1
- package/dist/v3-capabilities.d.ts +0 -3
- package/dist/v3-capabilities.d.ts.map +0 -1
- package/dist/v3-capabilities.js.map +0 -1
- package/dist/v3-contract.d.ts +0 -7
- package/dist/v3-contract.d.ts.map +0 -1
- package/dist/v3-contract.js +0 -12
- package/dist/v3-contract.js.map +0 -1
- package/dist/v3-cost.d.ts.map +0 -1
- package/dist/v3-cost.js.map +0 -1
- package/dist/v3-create.js.map +0 -1
- package/dist/v3-document.d.ts +0 -5
- package/dist/v3-document.d.ts.map +0 -1
- package/dist/v3-document.js +0 -31
- package/dist/v3-document.js.map +0 -1
- package/dist/v3-driver.d.ts.map +0 -1
- package/dist/v3-driver.js.map +0 -1
- package/dist/v3-editor-workspace.d.ts.map +0 -1
- package/dist/v3-editor-workspace.js.map +0 -1
- package/dist/v3-examples.d.ts.map +0 -1
- package/dist/v3-examples.js.map +0 -1
- package/dist/v3-follow.d.ts.map +0 -1
- package/dist/v3-follow.js.map +0 -1
- package/dist/v3-gate.d.ts.map +0 -1
- package/dist/v3-gate.js.map +0 -1
- package/dist/v3-graph-types.d.ts.map +0 -1
- package/dist/v3-graph-types.js +0 -2
- package/dist/v3-graph-types.js.map +0 -1
- package/dist/v3-graph.d.ts.map +0 -1
- package/dist/v3-graph.js.map +0 -1
- package/dist/v3-instance-size.d.ts.map +0 -1
- package/dist/v3-instance-size.js.map +0 -1
- package/dist/v3-json.d.ts +0 -12
- package/dist/v3-json.d.ts.map +0 -1
- package/dist/v3-json.js.map +0 -1
- package/dist/v3-kernel-version.d.ts +0 -8
- package/dist/v3-kernel-version.d.ts.map +0 -1
- package/dist/v3-kernel-version.js.map +0 -1
- package/dist/v3-part-edit.d.ts.map +0 -1
- package/dist/v3-part-edit.js.map +0 -1
- package/dist/v3-proposal.d.ts +0 -42
- package/dist/v3-proposal.d.ts.map +0 -1
- package/dist/v3-proposal.js.map +0 -1
- package/dist/v3-public-inputs.d.ts.map +0 -1
- package/dist/v3-public-inputs.js.map +0 -1
- package/dist/v3-recipe-build.d.ts +0 -27
- package/dist/v3-recipe-build.d.ts.map +0 -1
- package/dist/v3-recipe-build.js.map +0 -1
- package/dist/v3-recipe-from-parts.d.ts +0 -8
- package/dist/v3-recipe-from-parts.d.ts.map +0 -1
- package/dist/v3-recipe-from-parts.js.map +0 -1
- package/dist/v3-recipe.d.ts.map +0 -1
- package/dist/v3-recipe.js.map +0 -1
- package/dist/v3-release-contract.d.ts +0 -19
- package/dist/v3-release-contract.d.ts.map +0 -1
- package/dist/v3-release-contract.js.map +0 -1
- package/dist/v3-render-batches.d.ts.map +0 -1
- package/dist/v3-render-batches.js.map +0 -1
- package/dist/v3-score.d.ts.map +0 -1
- package/dist/v3-score.js.map +0 -1
- package/dist/v3-shape-sampling.d.ts.map +0 -1
- package/dist/v3-shape-sampling.js.map +0 -1
- package/dist/v3-surface.d.ts.map +0 -1
- package/dist/v3-surface.js.map +0 -1
- package/dist/v3-words.d.ts.map +0 -1
- package/dist/v3-words.js.map +0 -1
- package/docs/prototypes/v3-asset.mjs +0 -1
- package/docs/prototypes/v3-authoring-session.mjs +0 -1
- package/docs/prototypes/v3-capability-plan.mjs +0 -1
- package/docs/prototypes/v3-document.mjs +0 -2
- package/docs/prototypes/v3-editor-workspace.mjs +0 -1
- package/docs/prototypes/v3-typed-plan.mjs +0 -3
- package/docs/v3-status.md +0 -50
- /package/docs/prototypes/{v3-kernel.mjs → kernel.mjs} +0 -0
- /package/docs/prototypes/{v3-linear-comparison.py → linear-comparison.py} +0 -0
- /package/docs/prototypes/{v3-semantics.test.mjs → semantics.test.mjs} +0 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
# v3 레이아웃 설계 검증 예제
|
|
2
2
|
|
|
3
|
-
`node --test docs/prototypes/
|
|
4
|
-
[배치 의도 변경과 검증 한계](../
|
|
3
|
+
`node --test docs/prototypes/typed-repeat.test.mjs`: 단위/좌표계 그래프와 반복 조립체 통합.
|
|
4
|
+
[배치 의도 변경과 검증 한계](../typed-repeat-contracts.md)를 함께 확인한다.
|
|
5
5
|
|
|
6
|
-
`node --test docs/prototypes/
|
|
7
|
-
[연산·형상 계약과 미지원 범위](../
|
|
6
|
+
`node --test docs/prototypes/typed-plan.test.mjs`: 단위가 있는 scalar 컴파일러.
|
|
7
|
+
[연산·형상 계약과 미지원 범위](../operator-contracts.md)를 참조한다.
|
|
8
8
|
|
|
9
|
-
현재 설계 방향: [r3 핵심 의미론](../
|
|
10
|
-
`node --test docs/prototypes/
|
|
9
|
+
현재 설계 방향: [r3 핵심 의미론](../core-semantics.md).
|
|
10
|
+
`node --test docs/prototypes/semantics.test.mjs`는 부착식과 반복식을 독립 검사한다.
|
|
11
11
|
기존 커널을 r3에 맞게 구현했다는 의미가 아니다.
|
|
12
12
|
|
|
13
13
|
## 공통 커널 실험
|
|
14
14
|
|
|
15
|
-
`node --test docs/prototypes/
|
|
15
|
+
`node --test docs/prototypes/kernel.test.mjs`
|
|
16
16
|
|
|
17
17
|
모델별 분기 없는 실행기와 실제 샘플에서 유도한 선언을 분리했다.
|
|
18
18
|
범위·213개 크기/상태 조합·반례·미지원 항목은
|
|
19
|
-
[공통 커널 실험 기록](../
|
|
19
|
+
[공통 커널 실험 기록](../common-kernel-experiment.md)을 따른다.
|
|
20
20
|
제품 v3 구현이나 전체 샘플 변환 완료가 아니다.
|
|
21
21
|
|
|
22
22
|
## 외부 연구 구현 probe
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
`shapeassembly-probe.py`는 제품 코드가 아니라 외부 고정 revision의 interpreter 호출기다.
|
|
25
25
|
외부 소스/라이브러리를 이 저장소에 포함하지 않는다. 단독 실행에는 별도의 연구용 checkout과
|
|
26
26
|
가상환경이 필요하다. 입력·출력은 `shapeassembly-probe-results.json`, 해석·라이선스 경계·
|
|
27
|
-
재현 명령은 [실행 기록](../
|
|
27
|
+
재현 명령은 [실행 기록](../shapeassembly-reproduction.md)을 따른다.
|
|
28
28
|
|
|
29
29
|
아래 내용은 그와 별개인 자체 r1 수치 예제다.
|
|
30
30
|
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
package.json, 의존성, 서버, 소비자 runtime을 변경하지 않는다.
|
|
33
33
|
|
|
34
34
|
```sh
|
|
35
|
-
node --test docs/prototypes/
|
|
35
|
+
node --test docs/prototypes/layout.test.mjs
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
2026-09-20 실행 결과: 18개 테스트 통과.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import * as dryroom from './
|
|
2
|
-
import * as conveyor from './
|
|
3
|
-
import * as robot from './
|
|
4
|
-
import * as forklift from './
|
|
5
|
-
import {robotBinding,forkliftBindings} from './
|
|
1
|
+
import * as dryroom from './full-dryroom.mjs'
|
|
2
|
+
import * as conveyor from './full-conveyor.mjs'
|
|
3
|
+
import * as robot from './full-robot.mjs'
|
|
4
|
+
import * as forklift from './full-forklift.mjs'
|
|
5
|
+
import {robotBinding,forkliftBindings} from './capability-samples.mjs'
|
|
6
6
|
export function sampleAsset(kind){
|
|
7
7
|
const modules={dryroom,conveyor,robot,forklift},sample=modules[kind]
|
|
8
8
|
if(!sample)throw Error('unknown sample')
|
|
9
9
|
const pair=kind==='robot'?robotBinding():kind==='forklift'?forkliftBindings():{model:sample.fullModel(),declaration:{version:'r3-capability-3',assetFrame:'world',bindings:[]}}
|
|
10
10
|
const designInputs={},stateDefaults={}
|
|
11
11
|
for(const i of pair.model.inputs)(i.role==='state'?stateDefaults:designInputs)[i.id]=sample.defaults[i.id]
|
|
12
|
-
return {version:'figure-
|
|
12
|
+
return {version:'figure-asset-1',name:sample.seed.type,document:{version:'figure-prototype-1',model:pair.model,capabilities:pair.declaration},designInputs,stateDefaults,
|
|
13
13
|
palette:{'palette.primary':'#154473','palette.accent':'#f79c40','palette.neutral':'#96989b','palette.dark':'#27292c','palette.light':'#d6d7d9','palette.surface':'#56585b','palette.status-danger':'#f61b30','palette.status-warning':'#e68d1a','palette.status-info':'#158bde','palette.status-success':'#1cb648'},
|
|
14
14
|
appearance:sample.seed.parts.map(p=>({target:p.name,material:structuredClone(p.material??{token:'palette.neutral'}),...(p.primitive==='cylinder'?{segments:p.segments??8}:{}),...(p.capability?{legacyCapability:structuredClone(p.capability)}:{})}))}
|
|
15
15
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {readFile, open, rename, unlink} from 'node:fs/promises'
|
|
3
3
|
import {join, resolve} from 'node:path'
|
|
4
4
|
import {randomUUID, createHash} from 'node:crypto'
|
|
5
|
-
import {parseAsset, serializeAsset} from './
|
|
5
|
+
import {parseAsset, serializeAsset} from './asset.mjs'
|
|
6
6
|
|
|
7
7
|
export class StoreError extends Error {
|
|
8
8
|
constructor(code,message){super(message);this.code=code}
|
|
@@ -24,7 +24,7 @@ export function createAssetStore(directory){
|
|
|
24
24
|
async function load(path,scope,id){
|
|
25
25
|
let raw;try{raw=await readFile(path,'utf8')}catch(e){if(e.code==='ENOENT')return null;throw e}
|
|
26
26
|
let record;try{record=JSON.parse(raw)}catch{fail('CORRUPT','stored record is not JSON')}
|
|
27
|
-
if(record?.format!=='
|
|
27
|
+
if(record?.format!=='store-experiment-1'||record.scope!==scope||record.id!==id||typeof record.revision!=='string'||!record.revision)fail('CORRUPT','stored record identity or revision invalid')
|
|
28
28
|
// Stored content must pass the same semantic validation as incoming content.
|
|
29
29
|
parseAsset(record.text)
|
|
30
30
|
return record
|
|
@@ -37,7 +37,7 @@ export function createAssetStore(directory){
|
|
|
37
37
|
return exclusive(path,async()=>{
|
|
38
38
|
const current=await load(path,scope,id)
|
|
39
39
|
if((current?.revision??null)!==expectedRevision)fail('CONFLICT','asset changed; reload before saving')
|
|
40
|
-
const record={format:'
|
|
40
|
+
const record={format:'store-experiment-1',scope,id,revision:randomUUID(),text:canonical}
|
|
41
41
|
const temporary=path+'.'+randomUUID()+'.tmp';let handle
|
|
42
42
|
try{
|
|
43
43
|
handle=await open(temporary,'wx',0o600);await handle.writeFile(JSON.stringify(record));await handle.sync();await handle.close();handle=null
|
|
@@ -5,11 +5,11 @@ import {tmpdir} from 'node:os'
|
|
|
5
5
|
import {join} from 'node:path'
|
|
6
6
|
import {execFile} from 'node:child_process'
|
|
7
7
|
import {promisify} from 'node:util'
|
|
8
|
-
import {createAssetStore} from './
|
|
9
|
-
import {sampleAsset} from './
|
|
10
|
-
import {serializeAsset,parseAsset} from './
|
|
11
|
-
import {createEditorWorkspace} from './
|
|
12
|
-
async function fixture(t){const dir=await mkdtemp(join(tmpdir(),'
|
|
8
|
+
import {createAssetStore} from './asset-store.mjs'
|
|
9
|
+
import {sampleAsset} from './asset-samples.mjs'
|
|
10
|
+
import {serializeAsset,parseAsset} from './asset.mjs'
|
|
11
|
+
import {createEditorWorkspace} from './editor-workspace.mjs'
|
|
12
|
+
async function fixture(t){const dir=await mkdtemp(join(tmpdir(),'store-'));t.after(()=>rm(dir,{recursive:true,force:true}));return {dir,store:createAssetStore(dir)}}
|
|
13
13
|
const body=()=>serializeAsset(sampleAsset('forklift'))
|
|
14
14
|
const key={scope:'test-domain',id:'forklift'}
|
|
15
15
|
test('disk save survives independent store handle; explicit create and scoped identity',async t=>{
|
|
@@ -51,7 +51,7 @@ test('failed persistence does not clear editor dirty state',async t=>{
|
|
|
51
51
|
test('a separate Node process can restore the saved asset without editor memory',async t=>{
|
|
52
52
|
const {dir,store}=await fixture(t),asset=parseAsset(body());asset.designInputs.forkLength=1275
|
|
53
53
|
const saved=await store.save({...key,expectedRevision:null,text:serializeAsset(asset)})
|
|
54
|
-
const script=`import {createAssetStore} from ${JSON.stringify(new URL('./
|
|
54
|
+
const script=`import {createAssetStore} from ${JSON.stringify(new URL('./asset-store.mjs',import.meta.url).href)};
|
|
55
55
|
const record=await createAssetStore(process.argv[1]).read(${JSON.stringify(key)});
|
|
56
56
|
console.log(JSON.stringify({revision:record.revision,length:JSON.parse(record.text).designInputs.forkLength}));`
|
|
57
57
|
const {stdout}=await promisify(execFile)(process.execPath,['--input-type=module','-e',script,dir])
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {compileFigureAsset as compileAsset,parseFigureAsset as parseAsset,serializeFigureAsset as serializeAsset,captureFigureAuthoredInputs as captureAuthoredInputs} from '../../src/asset.ts'
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"type": "object", "additionalProperties": false,
|
|
5
5
|
"required": ["version", "name", "document", "designInputs", "stateDefaults", "palette", "appearance"],
|
|
6
6
|
"properties": {
|
|
7
|
-
"version": {"const": "figure-
|
|
7
|
+
"version": {"const": "figure-asset-1"},
|
|
8
8
|
"name": {"type": "string", "minLength": 1},
|
|
9
|
-
"document": {"$ref": "
|
|
9
|
+
"document": {"$ref": "document.schema.json"},
|
|
10
10
|
"designInputs": {"type": "object", "additionalProperties": {"type": "number"}},
|
|
11
11
|
"stateDefaults": {"type": "object", "additionalProperties": {"type": "number"}},
|
|
12
12
|
"palette": {"type": "object", "additionalProperties": {"type": "string", "pattern": "^#[0-9a-fA-F]{6}$"}},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {compileAsset,serializeAsset,parseAsset,captureAuthoredInputs} from './
|
|
4
|
-
import {sampleAsset} from './
|
|
5
|
-
import {createAuthoringSession} from './
|
|
6
|
-
import * as dryroom from './
|
|
7
|
-
import * as conveyor from './
|
|
8
|
-
import * as robot from './
|
|
9
|
-
import * as forklift from './
|
|
3
|
+
import {compileAsset,serializeAsset,parseAsset,captureAuthoredInputs} from './asset.mjs'
|
|
4
|
+
import {sampleAsset} from './asset-samples.mjs'
|
|
5
|
+
import {createAuthoringSession} from './authoring-session.mjs'
|
|
6
|
+
import * as dryroom from './full-dryroom.mjs'
|
|
7
|
+
import * as conveyor from './full-conveyor.mjs'
|
|
8
|
+
import * as robot from './full-robot.mjs'
|
|
9
|
+
import * as forklift from './full-forklift.mjs'
|
|
10
10
|
import fs from 'node:fs/promises'
|
|
11
11
|
import os from 'node:os'
|
|
12
12
|
import path from 'node:path'
|
|
@@ -55,9 +55,9 @@ test('asset parser retains duplicate-key and invalid JSON protections',()=>{
|
|
|
55
55
|
const a=sampleAsset('forklift');a.stateDefaults.lift=NaN;assert.throws(()=>serializeAsset(a),e=>e.code==='JSON_VALUE')
|
|
56
56
|
})
|
|
57
57
|
test('schema validation and real temporary JSON file round-trip, without seed lookups on reload',async()=>{
|
|
58
|
-
const ajv=new Ajv({allErrors:true});ajv.addSchema(JSON.parse(await fs.readFile(new URL('./
|
|
59
|
-
const validate=ajv.compile(JSON.parse(await fs.readFile(new URL('./
|
|
60
|
-
const directory=await fs.mkdtemp(path.join(os.tmpdir(),'figure-
|
|
58
|
+
const ajv=new Ajv({allErrors:true});ajv.addSchema(JSON.parse(await fs.readFile(new URL('./document.schema.json',import.meta.url))),'document.schema.json')
|
|
59
|
+
const validate=ajv.compile(JSON.parse(await fs.readFile(new URL('./asset.schema.json',import.meta.url))))
|
|
60
|
+
const directory=await fs.mkdtemp(path.join(os.tmpdir(),'figure-asset-'))
|
|
61
61
|
try{
|
|
62
62
|
for(const kind of Object.keys(samples)){
|
|
63
63
|
const asset=sampleAsset(kind);assert.ok(validate(asset),JSON.stringify(validate.errors))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {createFigureAuthoringSession as createAuthoringSession} from '../../src/authoring-session.ts'
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {createAuthoringSession} from './
|
|
4
|
-
import {repeatedBinding} from './
|
|
5
|
-
import {forkliftBindings} from './
|
|
6
|
-
import {defaults as conveyorDefaults} from './
|
|
7
|
-
import {defaults as forkliftDefaults} from './
|
|
8
|
-
const doc=s=>({version:'figure-
|
|
3
|
+
import {createAuthoringSession} from './authoring-session.mjs'
|
|
4
|
+
import {repeatedBinding} from './repeated-binding.mjs'
|
|
5
|
+
import {forkliftBindings} from './capability-samples.mjs'
|
|
6
|
+
import {defaults as conveyorDefaults} from './full-conveyor.mjs'
|
|
7
|
+
import {defaults as forkliftDefaults} from './full-forklift.mjs'
|
|
8
|
+
const doc=s=>({version:'figure-prototype-1',model:s.model,capabilities:s.declaration})
|
|
9
9
|
const edit=(input,value,unit='mm',kind='set-design')=>({kind,input,value,unit})
|
|
10
10
|
const request=(changes,expectedRevision=0,origin='ui')=>({origin,expectedRevision,changes})
|
|
11
11
|
const session=()=>createAuthoringSession(doc(repeatedBinding()),{...conveyorDefaults,selectedIndex:13})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {compileFigureCapabilities as compileCapabilities} from '../../src/capabilities.ts'
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {compileCapabilities} from './
|
|
4
|
-
import {robotBinding,forkliftBindings} from './
|
|
5
|
-
import {defaults as robotDefaults} from './
|
|
6
|
-
import {defaults as forkDefaults} from './
|
|
7
|
-
import {compile} from './
|
|
3
|
+
import {compileCapabilities} from './capability-plan.mjs'
|
|
4
|
+
import {robotBinding,forkliftBindings} from './capability-samples.mjs'
|
|
5
|
+
import {defaults as robotDefaults} from './full-robot.mjs'
|
|
6
|
+
import {defaults as forkDefaults} from './full-forklift.mjs'
|
|
7
|
+
import {compile} from './typed-plan.mjs'
|
|
8
8
|
const near=(a,b)=>assert.ok(Math.abs(a-b)<1e-7)
|
|
9
9
|
const run=s=>compileCapabilities(s.model,s.declaration)
|
|
10
10
|
test('robot grip follows changing links and six joints without creating or hiding solids',()=>{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {fullModel as robotModel} from './
|
|
2
|
-
import {fullModel as forkliftModel} from './
|
|
1
|
+
import {fullModel as robotModel} from './full-robot.mjs'
|
|
2
|
+
import {fullModel as forkliftModel} from './full-forklift.mjs'
|
|
3
3
|
export function robotBinding(){
|
|
4
4
|
const model=robotModel()
|
|
5
5
|
// Explicit experimental grip reference. Original tool-seat solid is NOT removed.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export {compileFigureDocument as compileDocument,parseFigureDocument as parseDocument,serializeFigureDocument as serializeDocument} from '../../src/document.ts'
|
|
2
|
+
export {assertFigureJson as jsonOnly,parseFigureJson as parseStrictJson,FIGURE_JSON_MAX_BYTES as MAX_BYTES,figureUtf8ByteLength} from '../../src/json.ts'
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"additionalProperties": false,
|
|
6
6
|
"required": ["version", "model", "capabilities"],
|
|
7
7
|
"properties": {
|
|
8
|
-
"version": {"const": "figure-
|
|
8
|
+
"version": {"const": "figure-prototype-1"},
|
|
9
9
|
"model": {"$ref": "#/$defs/model"},
|
|
10
10
|
"capabilities": {"$ref": "#/$defs/capabilities"}
|
|
11
11
|
},
|
|
@@ -2,13 +2,13 @@ import {test} from 'node:test'
|
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
3
|
import fs from 'node:fs'
|
|
4
4
|
import Ajv from '../../../things-factory/node_modules/ajv/lib/ajv.js'
|
|
5
|
-
import {compileDocument,serializeDocument,parseDocument,MAX_BYTES} from './
|
|
6
|
-
import {compileCapabilities} from './
|
|
7
|
-
import {forkliftBindings,robotBinding} from './
|
|
8
|
-
import {defaults as forkDefaults} from './
|
|
9
|
-
import {defaults as robotDefaults} from './
|
|
10
|
-
const envelope=s=>({version:'figure-
|
|
11
|
-
const schema=JSON.parse(fs.readFileSync(new URL('./
|
|
5
|
+
import {compileDocument,serializeDocument,parseDocument,MAX_BYTES} from './document.mjs'
|
|
6
|
+
import {compileCapabilities} from './capability-plan.mjs'
|
|
7
|
+
import {forkliftBindings,robotBinding} from './capability-samples.mjs'
|
|
8
|
+
import {defaults as forkDefaults} from './full-forklift.mjs'
|
|
9
|
+
import {defaults as robotDefaults} from './full-robot.mjs'
|
|
10
|
+
const envelope=s=>({version:'figure-prototype-1',model:s.model,capabilities:s.declaration})
|
|
11
|
+
const schema=JSON.parse(fs.readFileSync(new URL('./document.schema.json',import.meta.url)))
|
|
12
12
|
const validate=new Ajv({allErrors:true}).compile(schema)
|
|
13
13
|
test('JSON Schema and semantic compiler: serialize/parse/recompile both complete models',()=>{
|
|
14
14
|
for(const [sample,args] of [[forkliftBindings(),{...forkDefaults,lift:1000,reach:200}],[robotBinding(),{...robotDefaults,'shoulder-lift':40}]]){
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs from 'node:fs'
|
|
3
3
|
import {fileURLToPath} from 'node:url'
|
|
4
4
|
import * as THREE from '../../../things-scene/node_modules/three/build/three.module.js'
|
|
5
|
-
import {compile} from './
|
|
5
|
+
import {compile} from './typed-plan.mjs'
|
|
6
6
|
export const seed=JSON.parse(fs.readFileSync(new URL('../../../operato-application/packages/operato-figure/server/seed/figure-samples.json',import.meta.url))).find(s=>s.type==='DRY_ROOM').source
|
|
7
7
|
const c=(id,value,unit='mm')=>({id,value,unit})
|
|
8
8
|
const n=(id,op,args,outputs,params)=>({id,op,args,outputs,...(params?{params}:{})})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {evaluatePortal,meshOf,meshAudit,rayHit,seed} from './
|
|
3
|
+
import {evaluatePortal,meshOf,meshAudit,rayHit,seed} from './dryroom-mesh.mjs'
|
|
4
4
|
const near=(a,b)=>assert.ok(Math.abs(a-b)<1e-5,`${a} != ${b}`)
|
|
5
5
|
test('both actual portal profiles: baseline matches seed, bottom trim preserves top/opening/lamp',()=>{
|
|
6
6
|
for(const name of ['portal-in','portal-out']){
|
|
@@ -6,14 +6,14 @@ import {join} from 'node:path'
|
|
|
6
6
|
import {randomUUID} from 'node:crypto'
|
|
7
7
|
import {chromium} from '/Users/super/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/index.mjs'
|
|
8
8
|
import assert from 'node:assert/strict'
|
|
9
|
-
import {createEditorWorkspace} from './
|
|
10
|
-
import {sampleAsset} from './
|
|
11
|
-
import {serializeAsset} from './
|
|
12
|
-
import {
|
|
13
|
-
import {createAssetStore} from './
|
|
14
|
-
import {geometryOf as ordinary} from './
|
|
15
|
-
import {geometryOf as portal} from './
|
|
16
|
-
const directory=await mkdtemp(join(tmpdir(),'
|
|
9
|
+
import {createEditorWorkspace} from './editor-workspace.mjs'
|
|
10
|
+
import {sampleAsset} from './asset-samples.mjs'
|
|
11
|
+
import {serializeAsset} from './asset.mjs'
|
|
12
|
+
import {createFigureAsset} from '../../src/create.ts'
|
|
13
|
+
import {createAssetStore} from './asset-store.mjs'
|
|
14
|
+
import {geometryOf as ordinary} from './full-conveyor.mjs'
|
|
15
|
+
import {geometryOf as portal} from './full-dryroom.mjs'
|
|
16
|
+
const directory=await mkdtemp(join(tmpdir(),'editor-store-'))
|
|
17
17
|
const store=createAssetStore(directory),workspace=createEditorWorkspace();let saved=null,storage=null
|
|
18
18
|
const view=()=>{const v=workspace.read();return {...v,render:v.geometry.map(g=>{const mesh=(g.shape.provider==='portal'?portal:ordinary)(g);const json=mesh.toJSON();mesh.dispose();return {id:g.id,geometry:json,material:g.material}})}}
|
|
19
19
|
const html=`<!doctype html><meta charset="utf-8"><title>V3 편집 연결 검증</title>
|
|
@@ -49,7 +49,7 @@ const server=createServer(async(req,res)=>{try{
|
|
|
49
49
|
let body='';for await(const chunk of req){body+=chunk;if(body.length>20000)throw Error('request too large')}
|
|
50
50
|
const q=JSON.parse(body)
|
|
51
51
|
if(q.action==='open'){workspace.open(serializeAsset(sampleAsset(q.kind)));storage={scope:'isolated-browser',id:randomUUID(),expectedRevision:null}}
|
|
52
|
-
else if(q.action==='new'){workspace.open(serializeAsset(
|
|
52
|
+
else if(q.action==='new'){workspace.open(serializeAsset(createFigureAsset('새 Figure')));storage={scope:'isolated-browser',id:randomUUID(),expectedRevision:null}}
|
|
53
53
|
else if(q.action==='add'){const id='box'+(workspace.read().revision+1);workspace.addBox({context:q.context,origin:'ui',expectedRevision:q.revision,box:{id,size:{x:800,y:1000,z:600},position:{x:0,y:500,z:0}}})}
|
|
54
54
|
else if(q.action==='redo')workspace.redo({context:q.context,origin:'ui',expectedRevision:q.revision})
|
|
55
55
|
else if(q.action==='apply')workspace.apply(q.request)
|
|
@@ -74,12 +74,12 @@ try{
|
|
|
74
74
|
await page.locator('#forkLength').fill('1275');await page.locator('button[data-input="forkLength"]').click();await page.waitForFunction(()=>lastView.revision===1)
|
|
75
75
|
assert.equal(await page.evaluate(()=>lastView.geometry.find(g=>g.id==='fork-low-left').shape.dimensions.depth),1275)
|
|
76
76
|
await page.locator('#save').click();await page.waitForFunction(()=>!lastView.dirty)
|
|
77
|
-
await page.screenshot({path:'/private/tmp/figure-
|
|
77
|
+
await page.screenshot({path:'/private/tmp/figure-editor.png',fullPage:true})
|
|
78
78
|
await page.locator('#model').selectOption('robot');await page.locator('#open').click();await page.waitForFunction(()=>window.meshCount===10)
|
|
79
79
|
const stale=await page.evaluate(async old=>{const r=await fetch('/api',{method:'POST',body:JSON.stringify({action:'apply',request:{context:old.context,expectedRevision:0,origin:'ai',changes:[{kind:'set-design',input:'forkLength',unit:'mm',value:1500}]}})});return r.json()},old);assert.match(stale.error,/CONTEXT_CONFLICT/)
|
|
80
80
|
await page.locator('#reload').click();await page.waitForFunction(()=>window.meshCount===20);assert.equal(await page.locator('#forkLength').inputValue(),'1275')
|
|
81
81
|
await page.locator('#close').click();await page.waitForFunction(()=>window.meshCount===0);assert.equal(await page.locator('#fields input').count(),0)
|
|
82
|
-
await page.screenshot({path:'/private/tmp/figure-
|
|
82
|
+
await page.screenshot({path:'/private/tmp/figure-editor-empty.png',fullPage:true})
|
|
83
83
|
await page.locator('#new').click();await page.waitForFunction(()=>lastView.name==='새 Figure'&&meshCount===0)
|
|
84
84
|
await page.locator('#add').click();await page.waitForFunction(()=>meshCount===1&&lastView.dirty)
|
|
85
85
|
await page.locator('[id="box1.size.y"]').fill('1500');await page.locator('button[data-input="box1.size.y"]').click();await page.waitForFunction(()=>lastView.revision===2)
|
|
@@ -91,7 +91,7 @@ try{
|
|
|
91
91
|
await page.locator('#save').click();await page.waitForFunction(()=>!lastView.dirty)
|
|
92
92
|
await page.locator('#reload').click();await page.waitForFunction(()=>lastView.revision===0)
|
|
93
93
|
assert.equal(await page.locator('[id="box1.size.y"]').inputValue(),'1500')
|
|
94
|
-
await page.screenshot({path:'/private/tmp/figure-
|
|
94
|
+
await page.screenshot({path:'/private/tmp/figure-new-authoring.png',fullPage:true})
|
|
95
95
|
console.log('new empty asset -> add box -> edit -> undo edit -> undo creation -> redo both -> disk save/reload: passed')
|
|
96
|
-
assert.deepEqual(errors,[]);console.log(JSON.stringify({checks:['open asset','edit changes actual rendered geometry','save through HTTP to disk','switch 20 meshes to 10 without leftovers','reject stale AI context','reload edited design from independent disk store handle','close clears meshes and fields'],pageErrors:errors,screenshot:'/private/tmp/figure-
|
|
96
|
+
assert.deepEqual(errors,[]);console.log(JSON.stringify({checks:['open asset','edit changes actual rendered geometry','save through HTTP to disk','switch 20 meshes to 10 without leftovers','reject stale AI context','reload edited design from independent disk store handle','close clears meshes and fields'],pageErrors:errors,screenshot:'/private/tmp/figure-editor.png'},null,2))
|
|
97
97
|
}finally{await browser?.close();await new Promise(r=>server.close(r));await rm(directory,{recursive:true,force:true})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {createFigureEditorWorkspace as createEditorWorkspace} from '../../src/editor-workspace.ts'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {createEditorWorkspace} from './
|
|
4
|
-
import {sampleAsset} from './
|
|
5
|
-
import {serializeAsset} from './
|
|
3
|
+
import {createEditorWorkspace} from './editor-workspace.mjs'
|
|
4
|
+
import {sampleAsset} from './asset-samples.mjs'
|
|
5
|
+
import {serializeAsset} from './asset.mjs'
|
|
6
6
|
const text=kind=>serializeAsset(sampleAsset(kind))
|
|
7
7
|
const edit=(view,id,value,kind='set-design')=>({context:view.context,origin:'ui',expectedRevision:view.revision,changes:[{kind,input:id,value,unit:'mm'}]})
|
|
8
8
|
test('saved asset opens with fields/render appearance; edits and undo share the same session',()=>{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
3
|
import {test} from 'node:test'
|
|
4
|
-
import {resolve,point,DesignError} from './
|
|
4
|
+
import {resolve,point,DesignError} from './kernel.mjs'
|
|
5
5
|
|
|
6
6
|
const seeds=JSON.parse(fs.readFileSync(new URL('../../../operato-application/packages/operato-figure/server/seed/figure-samples.json',import.meta.url)))
|
|
7
7
|
const src=t=>seeds.find(s=>s.type===t).source
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
-
import {compile} from './
|
|
3
|
-
import {THREE,rounded} from './
|
|
2
|
+
import {compile} from './typed-plan.mjs'
|
|
3
|
+
import {THREE,rounded} from './full-dryroom.mjs'
|
|
4
4
|
export const seed=JSON.parse(fs.readFileSync(new URL('../../../operato-application/packages/operato-figure/server/seed/figure-samples.json',import.meta.url))).find(s=>s.type==='CONVEYOR').source
|
|
5
5
|
export const defaults={length:2400,width:600,rollerHeight:500,angle:0}
|
|
6
6
|
export function fullModel(){
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {seed,defaults,fullModel,evaluateFull,geometryOf,sourceGeometry} from './
|
|
4
|
-
import {compile} from './
|
|
5
|
-
import {meshAudit} from './
|
|
3
|
+
import {seed,defaults,fullModel,evaluateFull,geometryOf,sourceGeometry} from './full-conveyor.mjs'
|
|
4
|
+
import {compile} from './typed-plan.mjs'
|
|
5
|
+
import {meshAudit} from './dryroom-mesh.mjs'
|
|
6
6
|
const near=(a,b)=>assert.ok(Math.abs(a-b)<.001,`${a} != ${b}`)
|
|
7
7
|
const vertices=g=>{const a=g.attributes.position;return Array.from({length:a.count},(_,i)=>[a.getX(i),a.getY(i),a.getZ(i)])}
|
|
8
8
|
const deviation=(a,b)=>Math.max(...a.map(p=>Math.min(...b.map(q=>Math.hypot(...p.map((v,i)=>v-q[i]))))))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as THREE from '../../../things-scene/node_modules/three/build/three.module.js'
|
|
2
|
-
import {seed} from './
|
|
3
|
-
import {compile} from './
|
|
2
|
+
import {seed} from './dryroom-mesh.mjs'
|
|
3
|
+
import {compile} from './typed-plan.mjs'
|
|
4
4
|
export {seed,THREE}
|
|
5
5
|
const c=(id,value,unit='mm')=>({id,value,unit})
|
|
6
6
|
const n=(id,op,args,outputs,params)=>({id,op,args,outputs,...(params?{params}:{})})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
2
|
import {test} from 'node:test'
|
|
3
3
|
import {performance} from 'node:perf_hooks'
|
|
4
|
-
import {fullModel,evaluateFull,geometryOf,sourceGeometry,seed,defaults} from './
|
|
5
|
-
import {compile} from './
|
|
6
|
-
import {meshAudit} from './
|
|
4
|
+
import {fullModel,evaluateFull,geometryOf,sourceGeometry,seed,defaults} from './full-dryroom.mjs'
|
|
5
|
+
import {compile} from './typed-plan.mjs'
|
|
6
|
+
import {meshAudit} from './dryroom-mesh.mjs'
|
|
7
7
|
const near=(a,b,tol=1e-3)=>assert.ok(Math.abs(a-b)<tol,`${a} != ${b}`)
|
|
8
8
|
const vertices=g=>{const a=g.attributes.position;return Array.from({length:a.count},(_,i)=>[a.getX(i),a.getY(i),a.getZ(i)])}
|
|
9
9
|
function deviation(a,b){return Math.max(...a.map(p=>Math.min(...b.map(q=>Math.hypot(...p.map((x,i)=>x-q[i]))))))}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
-
import {compile} from './
|
|
3
|
-
import {geometryOf,sourceGeometry as primitiveGeometry} from './
|
|
2
|
+
import {compile} from './typed-plan.mjs'
|
|
3
|
+
import {geometryOf,sourceGeometry as primitiveGeometry} from './full-conveyor.mjs'
|
|
4
4
|
export {geometryOf}
|
|
5
5
|
export const seed=JSON.parse(fs.readFileSync(new URL('../../../operato-application/packages/operato-figure/server/seed/figure-samples.json',import.meta.url))).find(s=>s.type==='FORKLIFT').source
|
|
6
6
|
export const defaults={forkLength:850,lift:0,reach:0,drive:0}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {seed,defaults,fullModel,evaluateFull,geometryOf,sourceGeometry} from './
|
|
4
|
-
import {compile} from './
|
|
5
|
-
import {meshAudit} from './
|
|
3
|
+
import {seed,defaults,fullModel,evaluateFull,geometryOf,sourceGeometry} from './full-forklift.mjs'
|
|
4
|
+
import {compile} from './typed-plan.mjs'
|
|
5
|
+
import {meshAudit} from './dryroom-mesh.mjs'
|
|
6
6
|
const near=(a,b,tol=1e-6)=>assert.ok(Math.abs(a-b)<tol,`${a} != ${b}`)
|
|
7
7
|
const vertices=g=>{const a=g.attributes.position;return Array.from({length:a.count},(_,i)=>[a.getX(i),a.getY(i),a.getZ(i)])}
|
|
8
8
|
const deviation=(a,b)=>Math.max(...a.map(p=>Math.min(...b.map(q=>Math.hypot(...p.map((v,i)=>v-q[i]))))))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
-
import {compile} from './
|
|
3
|
-
import {geometryOf,sourceGeometry as primitiveGeometry} from './
|
|
2
|
+
import {compile} from './typed-plan.mjs'
|
|
3
|
+
import {geometryOf,sourceGeometry as primitiveGeometry} from './full-conveyor.mjs'
|
|
4
4
|
export {geometryOf}
|
|
5
5
|
export const seed=JSON.parse(fs.readFileSync(new URL('../../src/fixtures/robot-arm-6dof.json',import.meta.url)))
|
|
6
6
|
export const defaults={upperLength:420,foreLength:380,gripper:0,...Object.fromEntries(seed.joints.map(j=>[j.name,0]))}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {seed,defaults,fullModel,evaluateFull,geometryOf,sourceGeometry} from './
|
|
4
|
-
import {THREE} from './
|
|
5
|
-
import {compile} from './
|
|
6
|
-
import {meshAudit} from './
|
|
3
|
+
import {seed,defaults,fullModel,evaluateFull,geometryOf,sourceGeometry} from './full-robot.mjs'
|
|
4
|
+
import {THREE} from './full-dryroom.mjs'
|
|
5
|
+
import {compile} from './typed-plan.mjs'
|
|
6
|
+
import {meshAudit} from './dryroom-mesh.mjs'
|
|
7
7
|
const near=(a,b,tol=1e-7)=>assert.ok(Math.abs(a-b)<tol,`${a} != ${b}`)
|
|
8
8
|
const vec=(a,b)=>a.forEach((v,i)=>near(v,b[i]))
|
|
9
9
|
const vertices=g=>{const a=g.attributes.position;return Array.from({length:a.count},(_,i)=>[a.getX(i),a.getY(i),a.getZ(i)])}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
3
|
import {test} from 'node:test'
|
|
4
|
-
import {resolve,point,DesignError} from './
|
|
4
|
+
import {resolve,point,DesignError} from './kernel.mjs'
|
|
5
5
|
|
|
6
6
|
const seeds=JSON.parse(fs.readFileSync(new URL('../../../operato-application/packages/operato-figure/server/seed/figure-samples.json',import.meta.url)))
|
|
7
7
|
const robot=JSON.parse(fs.readFileSync(new URL('../../src/fixtures/robot-arm-6dof.json',import.meta.url)))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Design probes, not a
|
|
1
|
+
// Design probes, not a implementation. No source assets are modified.
|
|
2
2
|
import fs from 'node:fs'
|
|
3
3
|
import assert from 'node:assert/strict'
|
|
4
4
|
import { test } from 'node:test'
|
|
@@ -106,7 +106,7 @@ for(const type of ['CELL_TRAY','FORMATION_TRAY'])test(`${type}: fixed count, wal
|
|
|
106
106
|
const plan=repeatPlanOfPart(cell,{x:scale,y:1,z:1},s.base)
|
|
107
107
|
assert.equal(plan.count,8)
|
|
108
108
|
const edge=(repeatOffset(plan.count-1,plan.count,plan.pitch)+cell.transform.size.x/2)*scale
|
|
109
|
-
// Explicit
|
|
109
|
+
// Explicit candidate: wall thickness remains 30mm. Geometry fits only above a bound.
|
|
110
110
|
const clearance=s.base.x*scale/2-tub.shape.hollow.wall-edge
|
|
111
111
|
near(clearance,36*scale-30)
|
|
112
112
|
if(type==='FORMATION_TRAY') {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {compilePayloadInspection} from './
|
|
2
|
-
import {ContractError} from './
|
|
1
|
+
import {compilePayloadInspection} from './payload-fit.mjs'
|
|
2
|
+
import {ContractError} from './typed-plan.mjs'
|
|
3
3
|
const dot=(a,b)=>a.reduce((s,v,i)=>s+v*b[i],0)
|
|
4
4
|
const cross=(a,b)=>[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]
|
|
5
5
|
const columns=r=>[0,1,2].map(i=>r.map(row=>row[i]))
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {boxRelation,compileCollisionInspection} from './
|
|
4
|
-
import {forkliftBindings} from './
|
|
5
|
-
import {defaults} from './
|
|
3
|
+
import {boxRelation,compileCollisionInspection} from './payload-collision.mjs'
|
|
4
|
+
import {forkliftBindings} from './capability-samples.mjs'
|
|
5
|
+
import {defaults} from './full-forklift.mjs'
|
|
6
6
|
import {OBB} from '../../../things-scene/node_modules/three/examples/jsm/math/OBB.js'
|
|
7
7
|
import * as THREE from '../../../things-scene/node_modules/three/build/three.module.js'
|
|
8
8
|
const identity=[[1,0,0],[0,1,0],[0,0,1]],box=(center,half=[1,1,1],axes=identity)=>({center,half,axes})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Bounded geometric inspection, not a load-bearing or collision-safety certificate.
|
|
2
|
-
import {compileCapabilities} from './
|
|
3
|
-
import {ContractError} from './
|
|
2
|
+
import {compileCapabilities} from './capability-plan.mjs'
|
|
3
|
+
import {ContractError} from './typed-plan.mjs'
|
|
4
4
|
const fail=(code,message)=>{throw new ContractError(code,'payload',message)}
|
|
5
5
|
const dot=(a,b)=>a.reduce((s,v,i)=>s+v*b[i],0)
|
|
6
6
|
const mv=(r,v)=>r.map(row=>dot(row,v))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {test} from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {compilePayloadInspection} from './
|
|
4
|
-
import {forkliftBindings,robotBinding} from './
|
|
5
|
-
import {defaults} from './
|
|
6
|
-
import {defaults as robotDefaults} from './
|
|
3
|
+
import {compilePayloadInspection} from './payload-fit.mjs'
|
|
4
|
+
import {forkliftBindings,robotBinding} from './capability-samples.mjs'
|
|
5
|
+
import {defaults} from './full-forklift.mjs'
|
|
6
|
+
import {defaults as robotDefaults} from './full-robot.mjs'
|
|
7
7
|
const payload={bindingId:'load-low',type:'PALLET',occupied:0,dimensions:[620,100,850],center:[260,50,0],yawDeg:0}
|
|
8
8
|
const plan=s=>compilePayloadInspection(s.model,s.declaration)
|
|
9
9
|
const near=(a,b)=>assert.ok(Math.abs(a-b)<1e-5,`${a} != ${b}`)
|