@faicad/faijs 0.13.2 → 0.16.0
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/dist/api/api-namespace.d.ts +7 -0
- package/dist/api/api-namespace.d.ts.map +1 -1
- package/dist/api/api-namespace.js +44 -17
- package/dist/api/api-namespace.js.map +1 -1
- package/dist/api/boolean.d.ts +19 -0
- package/dist/api/boolean.d.ts.map +1 -1
- package/dist/api/boolean.js +71 -12
- package/dist/api/boolean.js.map +1 -1
- package/dist/api/chamfer.d.ts.map +1 -1
- package/dist/api/chamfer.js +12 -8
- package/dist/api/chamfer.js.map +1 -1
- package/dist/api/compound-geom.d.ts +45 -0
- package/dist/api/compound-geom.d.ts.map +1 -0
- package/dist/api/compound-geom.js +85 -0
- package/dist/api/compound-geom.js.map +1 -0
- package/dist/api/edge-ref.d.ts.map +1 -1
- package/dist/api/edge-ref.js +7 -4
- package/dist/api/edge-ref.js.map +1 -1
- package/dist/api/engrave.d.ts.map +1 -1
- package/dist/api/engrave.js +1 -0
- package/dist/api/engrave.js.map +1 -1
- package/dist/api/extrude.d.ts.map +1 -1
- package/dist/api/extrude.js +131 -3
- package/dist/api/extrude.js.map +1 -1
- package/dist/api/face-ref.d.ts.map +1 -1
- package/dist/api/face-ref.js +7 -4
- package/dist/api/face-ref.js.map +1 -1
- package/dist/api/fillet.d.ts.map +1 -1
- package/dist/api/fillet.js +6 -5
- package/dist/api/fillet.js.map +1 -1
- package/dist/api/generated/cad/script-face.d.ts +1 -1
- package/dist/api/generated/cad/script-face.d.ts.map +1 -1
- package/dist/api/generated/cad/script-face.js +1 -2
- package/dist/api/generated/cad/script-face.js.map +1 -1
- package/dist/api/generated/operations.js +16 -16
- package/dist/api/generated/operations.js.map +1 -1
- package/dist/api/generated/script-face-manifest.d.ts.map +1 -1
- package/dist/api/generated/script-face-manifest.js +0 -2
- package/dist/api/generated/script-face-manifest.js.map +1 -1
- package/dist/api/generated/script-face.d.ts +1 -7
- package/dist/api/generated/script-face.d.ts.map +1 -1
- package/dist/api/generated/script-face.js +2 -4
- package/dist/api/generated/script-face.js.map +1 -1
- package/dist/api/generated/sketching.js +1 -1
- package/dist/api/generated/sketching.js.map +1 -1
- package/dist/api/generated/topology.d.ts +0 -56
- package/dist/api/generated/topology.d.ts.map +1 -1
- package/dist/api/generated/topology.js +19 -68
- package/dist/api/generated/topology.js.map +1 -1
- package/dist/api/import-brep.d.ts +36 -0
- package/dist/api/import-brep.d.ts.map +1 -0
- package/dist/api/import-brep.js +72 -0
- package/dist/api/import-brep.js.map +1 -0
- package/dist/api/import-step.d.ts +28 -0
- package/dist/api/import-step.d.ts.map +1 -0
- package/dist/api/import-step.js +57 -0
- package/dist/api/import-step.js.map +1 -0
- package/dist/api/index.d.ts +8 -12
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +22 -16
- package/dist/api/index.js.map +1 -1
- package/dist/api/internal/compat-op.d.ts.map +1 -1
- package/dist/api/internal/compat-op.js +2 -1
- package/dist/api/internal/compat-op.js.map +1 -1
- package/dist/api/internal/compat-projection.d.ts +3 -1
- package/dist/api/internal/compat-projection.d.ts.map +1 -1
- package/dist/api/internal/compat-projection.js +8 -5
- package/dist/api/internal/compat-projection.js.map +1 -1
- package/dist/api/internal/replica-role-table.d.ts +36 -0
- package/dist/api/internal/replica-role-table.d.ts.map +1 -0
- package/dist/api/internal/replica-role-table.js +73 -0
- package/dist/api/internal/replica-role-table.js.map +1 -0
- package/dist/api/internal/result-unwrap.d.ts +3 -1
- package/dist/api/internal/result-unwrap.d.ts.map +1 -1
- package/dist/api/internal/result-unwrap.js +2 -2
- package/dist/api/internal/result-unwrap.js.map +1 -1
- package/dist/api/knurl.d.ts.map +1 -1
- package/dist/api/knurl.js +1 -0
- package/dist/api/knurl.js.map +1 -1
- package/dist/api/occt-kernel-bridge.d.ts +105 -27
- package/dist/api/occt-kernel-bridge.d.ts.map +1 -1
- package/dist/api/occt-kernel-bridge.js +255 -41
- package/dist/api/occt-kernel-bridge.js.map +1 -1
- package/dist/api/pattern.d.ts +32 -0
- package/dist/api/pattern.d.ts.map +1 -0
- package/dist/api/pattern.js +85 -0
- package/dist/api/pattern.js.map +1 -0
- package/dist/api/place.d.ts +35 -0
- package/dist/api/place.d.ts.map +1 -0
- package/dist/api/place.js +113 -0
- package/dist/api/place.js.map +1 -0
- package/dist/api/primitives.d.ts.map +1 -1
- package/dist/api/primitives.js +10 -3
- package/dist/api/primitives.js.map +1 -1
- package/dist/api/replicate.d.ts +145 -0
- package/dist/api/replicate.d.ts.map +1 -0
- package/dist/api/replicate.js +351 -0
- package/dist/api/replicate.js.map +1 -0
- package/dist/api/revolve.d.ts +36 -0
- package/dist/api/revolve.d.ts.map +1 -0
- package/dist/api/revolve.js +165 -0
- package/dist/api/revolve.js.map +1 -0
- package/dist/api/screw.d.ts.map +1 -1
- package/dist/api/screw.js +1 -0
- package/dist/api/screw.js.map +1 -1
- package/dist/api/sdf.d.ts.map +1 -1
- package/dist/api/sdf.js +1 -0
- package/dist/api/sdf.js.map +1 -1
- package/dist/api/sketch.d.ts.map +1 -1
- package/dist/api/sketch.js +1 -0
- package/dist/api/sketch.js.map +1 -1
- package/dist/api/split.d.ts +31 -0
- package/dist/api/split.d.ts.map +1 -0
- package/dist/api/split.js +96 -0
- package/dist/api/split.js.map +1 -0
- package/dist/api/surface/arg-spec.d.ts +17 -0
- package/dist/api/surface/arg-spec.d.ts.map +1 -1
- package/dist/api/surface/arg-spec.js +86 -66
- package/dist/api/surface/arg-spec.js.map +1 -1
- package/dist/api/surface/projection-overrides.d.ts.map +1 -1
- package/dist/api/surface/projection-overrides.js +6 -0
- package/dist/api/surface/projection-overrides.js.map +1 -1
- package/dist/api/topo-resolve.d.ts.map +1 -1
- package/dist/api/topo-resolve.js +65 -3
- package/dist/api/topo-resolve.js.map +1 -1
- package/dist/api/transform.d.ts +4 -0
- package/dist/api/transform.d.ts.map +1 -1
- package/dist/api/transform.js +44 -12
- package/dist/api/transform.js.map +1 -1
- package/dist/boolean/deriveNormals.d.ts +5 -4
- package/dist/boolean/deriveNormals.d.ts.map +1 -1
- package/dist/boolean/deriveNormals.js +24 -7
- package/dist/boolean/deriveNormals.js.map +1 -1
- package/dist/brep/brep-ops.d.ts +34 -1
- package/dist/brep/brep-ops.d.ts.map +1 -1
- package/dist/brep/brep-ops.js +62 -22
- package/dist/brep/brep-ops.js.map +1 -1
- package/dist/brep/engine/adapters/brep-mock.d.ts.map +1 -1
- package/dist/brep/engine/adapters/brep-mock.js +66 -0
- package/dist/brep/engine/adapters/brep-mock.js.map +1 -1
- package/dist/brep/engine/adapters/brepkit.d.ts.map +1 -1
- package/dist/brep/engine/adapters/brepkit.js +44 -1
- package/dist/brep/engine/adapters/brepkit.js.map +1 -1
- package/dist/brep/engine/adapters/occt.d.ts +1 -5
- package/dist/brep/engine/adapters/occt.d.ts.map +1 -1
- package/dist/brep/engine/adapters/occt.js +246 -9
- package/dist/brep/engine/adapters/occt.js.map +1 -1
- package/dist/brep/engine/primitives.d.ts +96 -0
- package/dist/brep/engine/primitives.d.ts.map +1 -1
- package/dist/brep/engine/primitives.js.map +1 -1
- package/dist/brep/engine/types.d.ts +66 -6
- package/dist/brep/engine/types.d.ts.map +1 -1
- package/dist/brep/engine/types.js.map +1 -1
- package/dist/brep/export/step.d.ts.map +1 -1
- package/dist/brep/export/step.js +6 -1
- package/dist/brep/export/step.js.map +1 -1
- package/dist/brep/face-evolution.d.ts +78 -8
- package/dist/brep/face-evolution.d.ts.map +1 -1
- package/dist/brep/face-evolution.js +130 -17
- package/dist/brep/face-evolution.js.map +1 -1
- package/dist/brepkit-kernel/brepkitKernel.d.ts +1 -1
- package/dist/brepkit-kernel/brepkitKernel.d.ts.map +1 -1
- package/dist/brepkit-kernel/brepkitKernel.js +108 -12
- package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
- package/dist/browser.d.ts +6 -28
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +6 -17
- package/dist/browser.js.map +1 -1
- package/dist/cad-runtime/admit-compat-lib.d.ts +13 -0
- package/dist/cad-runtime/admit-compat-lib.d.ts.map +1 -1
- package/dist/cad-runtime/admit-compat-lib.js +27 -0
- package/dist/cad-runtime/admit-compat-lib.js.map +1 -1
- package/dist/cad-runtime/backend-dispatch.d.ts +46 -3
- package/dist/cad-runtime/backend-dispatch.d.ts.map +1 -1
- package/dist/cad-runtime/backend-dispatch.js +48 -3
- package/dist/cad-runtime/backend-dispatch.js.map +1 -1
- package/dist/cad-runtime/browser-lib-loader.d.ts +4 -4
- package/dist/cad-runtime/browser-lib-loader.d.ts.map +1 -1
- package/dist/cad-runtime/browser-lib-loader.js +7 -4
- package/dist/cad-runtime/browser-lib-loader.js.map +1 -1
- package/dist/cad-runtime/direct-executor.d.ts.map +1 -1
- package/dist/cad-runtime/direct-executor.js +14 -4
- package/dist/cad-runtime/direct-executor.js.map +1 -1
- package/dist/cad-runtime/module-registry.d.ts +3 -1
- package/dist/cad-runtime/module-registry.d.ts.map +1 -1
- package/dist/cad-runtime/module-registry.js +13 -1
- package/dist/cad-runtime/module-registry.js.map +1 -1
- package/dist/cad-runtime/runtime-concurrent-error.d.ts +38 -0
- package/dist/cad-runtime/runtime-concurrent-error.d.ts.map +1 -0
- package/dist/cad-runtime/runtime-concurrent-error.js +43 -0
- package/dist/cad-runtime/runtime-concurrent-error.js.map +1 -0
- package/dist/cad-runtime/runtime.d.ts +52 -2
- package/dist/cad-runtime/runtime.d.ts.map +1 -1
- package/dist/cad-runtime/runtime.js +180 -9
- package/dist/cad-runtime/runtime.js.map +1 -1
- package/dist/define-op.d.ts +22 -0
- package/dist/define-op.d.ts.map +1 -1
- package/dist/define-op.js +82 -12
- package/dist/define-op.js.map +1 -1
- package/dist/env-agnostic.d.ts +38 -0
- package/dist/env-agnostic.d.ts.map +1 -0
- package/dist/env-agnostic.js +29 -0
- package/dist/env-agnostic.js.map +1 -0
- package/dist/fcstd/attachment.d.ts +8 -1
- package/dist/fcstd/attachment.d.ts.map +1 -1
- package/dist/fcstd/attachment.js +7 -1
- package/dist/fcstd/attachment.js.map +1 -1
- package/dist/fcstd/build-fai-zip.d.ts.map +1 -1
- package/dist/fcstd/build-fai-zip.js +12 -7
- package/dist/fcstd/build-fai-zip.js.map +1 -1
- package/dist/fcstd/codegen.d.ts +10 -1
- package/dist/fcstd/codegen.d.ts.map +1 -1
- package/dist/fcstd/codegen.js +158 -49
- package/dist/fcstd/codegen.js.map +1 -1
- package/dist/fcstd/contour.d.ts.map +1 -1
- package/dist/fcstd/contour.js +23 -24
- package/dist/fcstd/contour.js.map +1 -1
- package/dist/fcstd/convert.d.ts +2 -18
- package/dist/fcstd/convert.d.ts.map +1 -1
- package/dist/fcstd/convert.js +35 -53
- package/dist/fcstd/convert.js.map +1 -1
- package/dist/fcstd/expressions.d.ts +24 -0
- package/dist/fcstd/expressions.d.ts.map +1 -1
- package/dist/fcstd/expressions.js +91 -0
- package/dist/fcstd/expressions.js.map +1 -1
- package/dist/fcstd/external-geo.d.ts +12 -1
- package/dist/fcstd/external-geo.d.ts.map +1 -1
- package/dist/fcstd/external-geo.js +15 -1
- package/dist/fcstd/external-geo.js.map +1 -1
- package/dist/fcstd/feature-translate.d.ts +21 -1
- package/dist/fcstd/feature-translate.d.ts.map +1 -1
- package/dist/fcstd/feature-translate.js +124 -8
- package/dist/fcstd/feature-translate.js.map +1 -1
- package/dist/fcstd/structural-types.d.ts +83 -0
- package/dist/fcstd/structural-types.d.ts.map +1 -0
- package/dist/fcstd/structural-types.js +121 -0
- package/dist/fcstd/structural-types.js.map +1 -0
- package/dist/index.d.ts +7 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -16
- package/dist/index.js.map +1 -1
- package/dist/lang/determinism-scanner.d.ts +71 -0
- package/dist/lang/determinism-scanner.d.ts.map +1 -0
- package/dist/lang/determinism-scanner.js +625 -0
- package/dist/lang/determinism-scanner.js.map +1 -0
- package/dist/lang/symbol-table.d.ts +31 -0
- package/dist/lang/symbol-table.d.ts.map +1 -1
- package/dist/lang/symbol-table.generated.d.ts +13 -19
- package/dist/lang/symbol-table.generated.d.ts.map +1 -1
- package/dist/lang/symbol-table.generated.js +13 -19
- package/dist/lang/symbol-table.generated.js.map +1 -1
- package/dist/lang/symbol-table.js +50 -1
- package/dist/lang/symbol-table.js.map +1 -1
- package/dist/mesh/creased-normals.d.ts +50 -0
- package/dist/mesh/creased-normals.d.ts.map +1 -0
- package/dist/mesh/creased-normals.js +114 -0
- package/dist/mesh/creased-normals.js.map +1 -0
- package/dist/mesh/decoration-provider.d.ts +53 -0
- package/dist/mesh/decoration-provider.d.ts.map +1 -0
- package/dist/mesh/decoration-provider.js +21 -0
- package/dist/mesh/decoration-provider.js.map +1 -0
- package/dist/mesh/engrave.d.ts +4 -0
- package/dist/mesh/engrave.d.ts.map +1 -1
- package/dist/mesh/engrave.js +22 -31
- package/dist/mesh/engrave.js.map +1 -1
- package/dist/mesh/index.d.ts +5 -14
- package/dist/mesh/index.d.ts.map +1 -1
- package/dist/mesh/index.js +7 -18
- package/dist/mesh/index.js.map +1 -1
- package/dist/mesh/io.d.ts +2 -1
- package/dist/mesh/io.d.ts.map +1 -1
- package/dist/mesh/io.js +5 -7
- package/dist/mesh/io.js.map +1 -1
- package/dist/mesh/primitives.d.ts +5 -18
- package/dist/mesh/primitives.d.ts.map +1 -1
- package/dist/mesh/primitives.js +4 -48
- package/dist/mesh/primitives.js.map +1 -1
- package/dist/mesh/stl-loader.d.ts +23 -0
- package/dist/mesh/stl-loader.d.ts.map +1 -0
- package/dist/mesh/stl-loader.js +139 -0
- package/dist/mesh/stl-loader.js.map +1 -0
- package/dist/module-resolver/parse-imports.d.ts +37 -0
- package/dist/module-resolver/parse-imports.d.ts.map +1 -0
- package/dist/module-resolver/parse-imports.js +39 -0
- package/dist/module-resolver/parse-imports.js.map +1 -0
- package/dist/module-resolver/resolver.d.ts +1 -1
- package/dist/module-resolver/resolver.js +2 -2
- package/dist/module-resolver/resolver.js.map +1 -1
- package/dist/node-host/cli.d.ts.map +1 -1
- package/dist/node-host/cli.js +37 -4
- package/dist/node-host/cli.js.map +1 -1
- package/dist/primitives/text/cjk-font.d.ts +42 -0
- package/dist/primitives/text/cjk-font.d.ts.map +1 -0
- package/dist/primitives/text/cjk-font.js +86 -0
- package/dist/primitives/text/cjk-font.js.map +1 -0
- package/dist/runtime-state.d.ts +18 -6
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +9 -1
- package/dist/runtime-state.js.map +1 -1
- package/dist/sdk.d.ts +2 -2
- package/dist/sdk.js +2 -2
- package/dist/sdk.js.map +1 -1
- package/dist/shape.d.ts +15 -0
- package/dist/shape.d.ts.map +1 -1
- package/dist/shape.js +26 -3
- package/dist/shape.js.map +1 -1
- package/dist/symbol-table.d.ts +15 -0
- package/dist/symbol-table.d.ts.map +1 -0
- package/dist/symbol-table.js +14 -0
- package/dist/symbol-table.js.map +1 -0
- package/dist/topology/naming/build-naming.d.ts +14 -30
- package/dist/topology/naming/build-naming.d.ts.map +1 -1
- package/dist/topology/naming/build-naming.js +21 -54
- package/dist/topology/naming/build-naming.js.map +1 -1
- package/dist/topology/naming/capture-topo-ref.d.ts.map +1 -1
- package/dist/topology/naming/capture-topo-ref.js +5 -0
- package/dist/topology/naming/capture-topo-ref.js.map +1 -1
- package/dist/topology/naming/index.d.ts +3 -0
- package/dist/topology/naming/index.d.ts.map +1 -1
- package/dist/topology/naming/index.js +3 -0
- package/dist/topology/naming/index.js.map +1 -1
- package/dist/topology/naming/lineage-resolve.d.ts +80 -0
- package/dist/topology/naming/lineage-resolve.d.ts.map +1 -0
- package/dist/topology/naming/lineage-resolve.js +120 -0
- package/dist/topology/naming/lineage-resolve.js.map +1 -0
- package/dist/topology/naming/lineage.d.ts +349 -0
- package/dist/topology/naming/lineage.d.ts.map +1 -0
- package/dist/topology/naming/lineage.js +398 -0
- package/dist/topology/naming/lineage.js.map +1 -0
- package/dist/topology/naming/migrate.d.ts +41 -0
- package/dist/topology/naming/migrate.d.ts.map +1 -0
- package/dist/topology/naming/migrate.js +88 -0
- package/dist/topology/naming/migrate.js.map +1 -0
- package/dist/topology/naming/role-name.d.ts +176 -0
- package/dist/topology/naming/role-name.d.ts.map +1 -0
- package/dist/topology/naming/role-name.js +344 -0
- package/dist/topology/naming/role-name.js.map +1 -0
- package/dist/topology/naming/roles.d.ts +20 -22
- package/dist/topology/naming/roles.d.ts.map +1 -1
- package/dist/topology/naming/roles.js +51 -49
- package/dist/topology/naming/roles.js.map +1 -1
- package/dist/topology/naming/types.d.ts +65 -18
- package/dist/topology/naming/types.d.ts.map +1 -1
- package/dist/topology/naming/types.js.map +1 -1
- package/dist/vendored/brepjs/kernel/index.d.ts +5 -0
- package/dist/vendored/brepjs/kernel/index.d.ts.map +1 -1
- package/dist/vendored/brepjs/kernel/index.js +11 -0
- package/dist/vendored/brepjs/kernel/index.js.map +1 -1
- package/dist/weapp.d.ts +4 -4
- package/dist/weapp.d.ts.map +1 -1
- package/dist/weapp.js +7 -4
- package/dist/weapp.js.map +1 -1
- package/package.json +16 -14
- package/dist/api/compat/index.d.ts +0 -173
- package/dist/api/compat/index.d.ts.map +0 -1
- package/dist/api/compat/index.js +0 -222
- package/dist/api/compat/index.js.map +0 -1
- package/dist/api/compound.d.ts +0 -108
- package/dist/api/compound.d.ts.map +0 -1
- package/dist/api/compound.js +0 -162
- package/dist/api/compound.js.map +0 -1
- package/dist/api/copy.d.ts +0 -39
- package/dist/api/copy.d.ts.map +0 -1
- package/dist/api/copy.js +0 -81
- package/dist/api/copy.js.map +0 -1
- package/dist/api/fai_drill.d.ts +0 -46
- package/dist/api/fai_drill.d.ts.map +0 -1
- package/dist/api/fai_drill.js +0 -230
- package/dist/api/fai_drill.js.map +0 -1
- package/dist/api/fai_extrude.d.ts +0 -36
- package/dist/api/fai_extrude.d.ts.map +0 -1
- package/dist/api/fai_extrude.js +0 -78
- package/dist/api/fai_extrude.js.map +0 -1
- package/dist/api/fai_split.d.ts +0 -46
- package/dist/api/fai_split.d.ts.map +0 -1
- package/dist/api/fai_split.js +0 -253
- package/dist/api/fai_split.js.map +0 -1
- package/dist/api/load.d.ts +0 -39
- package/dist/api/load.d.ts.map +0 -1
- package/dist/api/load.js +0 -82
- package/dist/api/load.js.map +0 -1
- package/dist/api/svgExtrude.d.ts +0 -32
- package/dist/api/svgExtrude.d.ts.map +0 -1
- package/dist/api/svgExtrude.js +0 -73
- package/dist/api/svgExtrude.js.map +0 -1
- package/dist/api/text.d.ts +0 -34
- package/dist/api/text.d.ts.map +0 -1
- package/dist/api/text.js +0 -100
- package/dist/api/text.js.map +0 -1
- package/dist/brep/text/fontTestHelper.d.ts +0 -30
- package/dist/brep/text/fontTestHelper.d.ts.map +0 -1
- package/dist/brep/text/fontTestHelper.js +0 -57
- package/dist/brep/text/fontTestHelper.js.map +0 -1
- package/dist/cad-runtime/module-executor.d.ts +0 -271
- package/dist/cad-runtime/module-executor.d.ts.map +0 -1
- package/dist/cad-runtime/module-executor.js +0 -630
- package/dist/cad-runtime/module-executor.js.map +0 -1
- package/dist/cad-runtime/terminal-dag.d.ts +0 -65
- package/dist/cad-runtime/terminal-dag.d.ts.map +0 -1
- package/dist/cad-runtime/terminal-dag.js +0 -167
- package/dist/cad-runtime/terminal-dag.js.map +0 -1
- package/dist/faqts/browser.d.ts +0 -16
- package/dist/faqts/browser.d.ts.map +0 -1
- package/dist/faqts/browser.js +0 -13
- package/dist/faqts/browser.js.map +0 -1
- package/dist/faqts/exec-blob.d.ts +0 -14
- package/dist/faqts/exec-blob.d.ts.map +0 -1
- package/dist/faqts/exec-blob.js +0 -22
- package/dist/faqts/exec-blob.js.map +0 -1
- package/dist/faqts/exec-node.d.ts +0 -15
- package/dist/faqts/exec-node.d.ts.map +0 -1
- package/dist/faqts/exec-node.js +0 -29
- package/dist/faqts/exec-node.js.map +0 -1
- package/dist/faqts/imports.d.ts +0 -74
- package/dist/faqts/imports.d.ts.map +0 -1
- package/dist/faqts/imports.js +0 -95
- package/dist/faqts/imports.js.map +0 -1
- package/dist/faqts/index.d.ts +0 -22
- package/dist/faqts/index.d.ts.map +0 -1
- package/dist/faqts/index.js +0 -21
- package/dist/faqts/index.js.map +0 -1
- package/dist/faqts/node.d.ts +0 -23
- package/dist/faqts/node.d.ts.map +0 -1
- package/dist/faqts/node.js +0 -23
- package/dist/faqts/node.js.map +0 -1
- package/dist/faqts/run.d.ts +0 -62
- package/dist/faqts/run.d.ts.map +0 -1
- package/dist/faqts/run.js +0 -62
- package/dist/faqts/run.js.map +0 -1
- package/dist/faqts/transform.d.ts +0 -43
- package/dist/faqts/transform.d.ts.map +0 -1
- package/dist/faqts/transform.js +0 -35
- package/dist/faqts/transform.js.map +0 -1
- package/dist/lang/compile.d.ts +0 -50
- package/dist/lang/compile.d.ts.map +0 -1
- package/dist/lang/compile.js +0 -368
- package/dist/lang/compile.js.map +0 -1
- package/dist/lang/keep.d.ts +0 -109
- package/dist/lang/keep.d.ts.map +0 -1
- package/dist/lang/keep.js +0 -226
- package/dist/lang/keep.js.map +0 -1
- package/dist/lang/parser.d.ts +0 -91
- package/dist/lang/parser.d.ts.map +0 -1
- package/dist/lang/parser.js +0 -1594
- package/dist/lang/parser.js.map +0 -1
- package/dist/mesh/drill-hole/DrillHoleCore.d.ts +0 -90
- package/dist/mesh/drill-hole/DrillHoleCore.d.ts.map +0 -1
- package/dist/mesh/drill-hole/DrillHoleCore.js +0 -287
- package/dist/mesh/drill-hole/DrillHoleCore.js.map +0 -1
- package/dist/mesh/drill-hole/drill-types.d.ts +0 -34
- package/dist/mesh/drill-hole/drill-types.d.ts.map +0 -1
- package/dist/mesh/drill-hole/drill-types.js +0 -7
- package/dist/mesh/drill-hole/drill-types.js.map +0 -1
- package/dist/mesh/fai_drill.d.ts +0 -21
- package/dist/mesh/fai_drill.d.ts.map +0 -1
- package/dist/mesh/fai_drill.js +0 -92
- package/dist/mesh/fai_drill.js.map +0 -1
- package/dist/mesh/fai_extrude.d.ts +0 -24
- package/dist/mesh/fai_extrude.d.ts.map +0 -1
- package/dist/mesh/fai_extrude.js +0 -42
- package/dist/mesh/fai_extrude.js.map +0 -1
- package/dist/mesh/fai_split.d.ts +0 -164
- package/dist/mesh/fai_split.d.ts.map +0 -1
- package/dist/mesh/fai_split.js +0 -269
- package/dist/mesh/fai_split.js.map +0 -1
- package/dist/primitives/svg-extrude.d.ts +0 -43
- package/dist/primitives/svg-extrude.d.ts.map +0 -1
- package/dist/primitives/svg-extrude.js +0 -78
- package/dist/primitives/svg-extrude.js.map +0 -1
- package/dist/primitives/text/cjk.d.ts +0 -61
- package/dist/primitives/text/cjk.d.ts.map +0 -1
- package/dist/primitives/text/cjk.js +0 -215
- package/dist/primitives/text/cjk.js.map +0 -1
- package/dist/primitives/text-geometry.d.ts +0 -47
- package/dist/primitives/text-geometry.d.ts.map +0 -1
- package/dist/primitives/text-geometry.js +0 -127
- package/dist/primitives/text-geometry.js.map +0 -1
- package/dist/test-helpers.d.ts +0 -31
- package/dist/test-helpers.d.ts.map +0 -1
- package/dist/test-helpers.js +0 -55
- package/dist/test-helpers.js.map +0 -1
- package/dist/vendored/brepjs/core/kernelReentrancy.d.ts +0 -41
- package/dist/vendored/brepjs/core/kernelReentrancy.d.ts.map +0 -1
- package/dist/vendored/brepjs/core/kernelReentrancy.js +0 -68
- package/dist/vendored/brepjs/core/kernelReentrancy.js.map +0 -1
package/dist/fcstd/contour.js
CHANGED
|
@@ -39,39 +39,38 @@ export function extractContours(geoms) {
|
|
|
39
39
|
if (start.used)
|
|
40
40
|
continue;
|
|
41
41
|
start.used = true;
|
|
42
|
-
const segments = [start.seg];
|
|
43
|
-
let tail = start.b;
|
|
44
42
|
const head = start.a;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
// GOTCHA (slittingsaw corpus, 2026-09-20): junction points with free
|
|
44
|
+
// branches (three+ segments sharing an endpoint) defeat first-come
|
|
45
|
+
// chaining — a branch to a dead end consumed the segments and the real
|
|
46
|
+
// loop never closed. DFS with backtracking: try each candidate
|
|
47
|
+
// continuation; the FIRST chain that closes at the head wins; a dead
|
|
48
|
+
// branch returns its segments to the pool. Closure-stop (tap GOTCHA) and
|
|
49
|
+
// pool-restart are both subsumed by the DFS ordering.
|
|
50
|
+
const dfs = (tail, walk) => {
|
|
51
|
+
if (walk.length > 0 && near(tail, head))
|
|
52
|
+
return walk;
|
|
53
53
|
for (const cand of pool) {
|
|
54
54
|
if (cand.used)
|
|
55
55
|
continue;
|
|
56
|
+
cand.used = true;
|
|
56
57
|
if (near(tail, cand.a)) {
|
|
57
|
-
cand.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
extended = true;
|
|
61
|
-
break;
|
|
58
|
+
const r = dfs(cand.b, [...walk, cand.seg]);
|
|
59
|
+
if (r)
|
|
60
|
+
return r;
|
|
62
61
|
}
|
|
63
62
|
else if (near(tail, cand.b)) {
|
|
64
|
-
cand.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
extended = true;
|
|
68
|
-
break;
|
|
63
|
+
const r = dfs(cand.a, [...walk, reverseSeg(cand.seg)]);
|
|
64
|
+
if (r)
|
|
65
|
+
return r;
|
|
69
66
|
}
|
|
67
|
+
cand.used = false; // dead branch — return the segment to the pool
|
|
70
68
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
return undefined;
|
|
70
|
+
};
|
|
71
|
+
const solved = dfs(start.b, [start.seg]);
|
|
72
|
+
if (solved && solved.length > 1) {
|
|
73
|
+
contours.push({ segments: solved, closed: true });
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
// circles are self-closed contours
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contour.js","sourceRoot":"","sources":["../../src/fcstd/contour.ts"],"names":[],"mappings":"AAmCA,MAAM,QAAQ,GAAG,IAAI,CAAC;AAEtB,SAAS,OAAO,CAAC,CAAa;IAC5B,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO;gBACL,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE;gBACxD,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;gBACpB,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;aACrB,CAAC;QACJ,KAAK,KAAK;YACR,OAAO;gBACL;oBACE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM;oBACjD,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI;oBACzD,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE;iBACvC;gBACD,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;gBACpB,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;aACrB,CAAC;QACJ;YACE,OAAO,SAAS,CAAC,CAAC,qDAAqD;IAC3E,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAmB;IACjD,MAAM,IAAI,GAAmG,EAAE,CAAC;IAChH,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,CAA2B,EAAE,CAA2B,EAAW,EAAE,CACjF,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;IAE/C,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,IAAI;YAAE,SAAS;QACzB,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;QAClB,MAAM,
|
|
1
|
+
{"version":3,"file":"contour.js","sourceRoot":"","sources":["../../src/fcstd/contour.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * M3.6 — contour extraction: solved sketch geometry → closed loops → 2D\n * contour (R6: pure data, no OCCT dependency; the cad-face wiring that turns\n * contours into Blueprint/extrude inputs happens in M4.6/M5).\n *\n * Extraction: collect endpoints of every non-construction geometry, chain\n * segments sharing endpoints (tolerance-based), keep closed loops.\n */\nimport type { SketchGeom } from './sketch-parse.js';\n\n/** One segment of a 2D contour: either a straight line or a circular arc (planar, sketch-local coordinates). */\nexport type ContourSeg =\n | { kind: 'line'; x1: number; y1: number; x2: number; y2: number }\n | {\n kind: 'arc';\n cx: number;\n cy: number;\n radius: number;\n startAngle: number;\n endAngle: number;\n ccw: boolean;\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n };\n\n/** A chained run of segments; `closed` when the chain's ends meet (R6: pure data, no OCCT dependency). */\nexport interface Contour {\n /** chained segments in traversal order */\n segments: ContourSeg[];\n /** true when the chain's endpoints meet within JOIN_TOL */\n closed: boolean;\n}\n\nconst JOIN_TOL = 1e-7;\n\nfunction segEnds(g: SketchGeom): [ContourSeg, { x: number; y: number }, { x: number; y: number }] | undefined {\n switch (g.kind) {\n case 'line':\n return [\n { kind: 'line', x1: g.x1, y1: g.y1, x2: g.x2, y2: g.y2 },\n { x: g.x1, y: g.y1 },\n { x: g.x2, y: g.y2 },\n ];\n case 'arc':\n return [\n {\n kind: 'arc', cx: g.cx, cy: g.cy, radius: g.radius,\n startAngle: g.startAngle, endAngle: g.endAngle, ccw: true,\n x1: g.x1, y1: g.y1, x2: g.x2, y2: g.y2,\n },\n { x: g.x1, y: g.y1 },\n { x: g.x2, y: g.y2 },\n ];\n default:\n return undefined; // circles are closed on their own; points don't join\n }\n}\n\n/**\n * Extract closed contours (chains of segments whose endpoints meet).\n * @param geoms solved sketch geometry to chain (non-construction segments only)\n * @returns closed loops plus self-closed circles (open chains are dropped)\n */\nexport function extractContours(geoms: SketchGeom[]): Contour[] {\n const pool: { seg: ContourSeg; a: { x: number; y: number }; b: { x: number; y: number }; used: boolean }[] = [];\n for (const g of geoms) {\n const s = segEnds(g);\n if (s) pool.push({ seg: s[0], a: s[1], b: s[2], used: false });\n }\n\n const near = (p: { x: number; y: number }, q: { x: number; y: number }): boolean =>\n Math.hypot(p.x - q.x, p.y - q.y) <= JOIN_TOL;\n\n const contours: Contour[] = [];\n for (const start of pool) {\n if (start.used) continue;\n start.used = true;\n const head = start.a;\n // GOTCHA (slittingsaw corpus, 2026-09-20): junction points with free\n // branches (three+ segments sharing an endpoint) defeat first-come\n // chaining — a branch to a dead end consumed the segments and the real\n // loop never closed. DFS with backtracking: try each candidate\n // continuation; the FIRST chain that closes at the head wins; a dead\n // branch returns its segments to the pool. Closure-stop (tap GOTCHA) and\n // pool-restart are both subsumed by the DFS ordering.\n const dfs = (tail: { x: number; y: number }, walk: ContourSeg[]): ContourSeg[] | undefined => {\n if (walk.length > 0 && near(tail, head)) return walk;\n for (const cand of pool) {\n if (cand.used) continue;\n cand.used = true;\n if (near(tail, cand.a)) {\n const r = dfs(cand.b, [...walk, cand.seg]);\n if (r) return r;\n } else if (near(tail, cand.b)) {\n const r = dfs(cand.a, [...walk, reverseSeg(cand.seg)]);\n if (r) return r;\n }\n cand.used = false; // dead branch — return the segment to the pool\n }\n return undefined;\n };\n const solved = dfs(start.b, [start.seg]);\n if (solved && solved.length > 1) {\n contours.push({ segments: solved, closed: true });\n }\n }\n\n // circles are self-closed contours\n for (const g of geoms) {\n if (g.kind === 'circle' && g.radius > 0) {\n contours.push({\n segments: [{\n kind: 'arc', cx: g.cx, cy: g.cy, radius: g.radius,\n startAngle: 0, endAngle: Math.PI * 2, ccw: true,\n x1: g.cx + g.radius, y1: g.cy, x2: g.cx + g.radius, y2: g.cy,\n }],\n closed: true,\n });\n }\n }\n return contours;\n}\n\nfunction reverseSeg(s: ContourSeg): ContourSeg {\n if (s.kind === 'line') return { kind: 'line', x1: s.x2, y1: s.y2, x2: s.x1, y2: s.y1 };\n return { ...s, startAngle: s.endAngle, endAngle: s.startAngle, ccw: !s.ccw, x1: s.x2, y1: s.y2, x2: s.x1, y2: s.y1 };\n}\n"]}
|
package/dist/fcstd/convert.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { STRUCTURAL_TYPES, STRUCTURAL_TYPES_EXTENDED, isFemStructural } from './structural-types.js';
|
|
2
|
+
export { STRUCTURAL_TYPES, STRUCTURAL_TYPES_EXTENDED, isFemStructural };
|
|
1
3
|
/** V2 tolerance: solver must reproduce stored geometry (single source). */
|
|
2
4
|
export declare const SKETCH_T1 = 0.000001;
|
|
3
5
|
/** Dispositions allowed in a conforming container (C4). */
|
|
@@ -50,24 +52,6 @@ export interface ConvertSummary {
|
|
|
50
52
|
error?: string;
|
|
51
53
|
elapsedMs: number;
|
|
52
54
|
}
|
|
53
|
-
/**
|
|
54
|
-
* H7 companion (2026-09-20): FEM workbench objects carry SIMULATION semantics
|
|
55
|
-
* (analysis containers, meshes, solver settings, boundary conditions, result
|
|
56
|
-
* pipelines) — no modeling geometry. Native classes without a Proxy property
|
|
57
|
-
* used to fall into `type-not-whitelisted` and block whole files whose
|
|
58
|
-
* modeling part was a single box. Like structural types they are
|
|
59
|
-
* `preserved-only`; their regenerable field data (FemMesh / result Data
|
|
60
|
-
* properties) is deliberately NOT carried into the container.
|
|
61
|
-
*
|
|
62
|
-
* Remote task on record (user decision, 2026-09-20): faijs will port
|
|
63
|
-
* FreeCAD's FEM analysis capability in the future — separate feature; this
|
|
64
|
-
* classification only stops simulation objects from blocking conversion.
|
|
65
|
-
* Python subclasses (*Python with Proxy) never reach this set — H10's
|
|
66
|
-
* python-opaque handles them.
|
|
67
|
-
*/
|
|
68
|
-
export declare const FEM_STRUCTURAL_TYPES: Set<string>;
|
|
69
|
-
/** True when the type is a FEM simulation object (structured non-modeling). */
|
|
70
|
-
export declare function isFemStructural(type: string): boolean;
|
|
71
55
|
/**
|
|
72
56
|
* Convert one FCStd file through the full pipeline (unpack → parse → sketch
|
|
73
57
|
* solving → codegen → container build) and return a structured summary.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/fcstd/convert.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/fcstd/convert.ts"],"names":[],"mappings":"AA8BA,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAM/B,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,eAAe,EAAE,CAAC;AAExE,2EAA2E;AAC3E,eAAO,MAAM,SAAS,WAAO,CAAC;AAE9B,2DAA2D;AAC3D,eAAO,MAAM,oBAAoB,aAA4D,CAAC;AAE9F;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;OAWG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,0GAA0G;AAC1G,MAAM,WAAW,cAAc;IAC7B,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,EAAE,EAAE,OAAO,CAAC;IACZ,0DAA0D;IAC1D,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1F,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,8EAA8E;IAC9E,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AA8BD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAiMpG"}
|
package/dist/fcstd/convert.js
CHANGED
|
@@ -26,58 +26,16 @@ import { effectivePlacement } from './attachment.js';
|
|
|
26
26
|
import { buildFaiZip } from './build-fai-zip.js';
|
|
27
27
|
import { isOk } from '../api/result.js';
|
|
28
28
|
import { zipSync, unzipSync, strToU8 } from 'fflate';
|
|
29
|
+
import { STRUCTURAL_TYPES, STRUCTURAL_TYPES_EXTENDED, isFemStructural, isNonModelingType, } from './structural-types.js';
|
|
30
|
+
// Kept on this module's public surface: these sets are part of the
|
|
31
|
+
// `./fcstd-convert` contract that the batch project and its tests consume. The
|
|
32
|
+
// definitions live in `structural-types.ts` so codegen can share them without
|
|
33
|
+
// a circular import.
|
|
34
|
+
export { STRUCTURAL_TYPES, STRUCTURAL_TYPES_EXTENDED, isFemStructural };
|
|
29
35
|
/** V2 tolerance: solver must reproduce stored geometry (single source). */
|
|
30
36
|
export const SKETCH_T1 = 1e-6;
|
|
31
37
|
/** Dispositions allowed in a conforming container (C4). */
|
|
32
38
|
export const ALLOWED_DISPOSITIONS = new Set(['translated', 'python-baked', 'preserved-only']);
|
|
33
|
-
/** Non-modeling structural/datum types: never translation gaps. They carry
|
|
34
|
-
* no feature semantics (containers, datum planes/lines/origins, groups) —
|
|
35
|
-
* their semantics are consumed by the translator (Body.Group ordering,
|
|
36
|
-
* datum-plane UpToFace anchors), not emitted as cad calls. */
|
|
37
|
-
const STRUCTURAL_TYPES = new Set([
|
|
38
|
-
'PartDesign::Body', 'App::Origin', 'App::Plane', 'App::Line',
|
|
39
|
-
'App::DocumentObjectGroup', 'App::Part', 'PartDesign::Plane', 'PartDesign::Line',
|
|
40
|
-
'PartDesign::CoordinateSystem',
|
|
41
|
-
// H10 companion fix (plan §3.5): datum/annotation types, not modeling
|
|
42
|
-
// features — previously misclassified as translation gaps.
|
|
43
|
-
'App::Point', 'App::Annotation',
|
|
44
|
-
]);
|
|
45
|
-
/**
|
|
46
|
-
* H7 companion (2026-09-20): FEM workbench objects carry SIMULATION semantics
|
|
47
|
-
* (analysis containers, meshes, solver settings, boundary conditions, result
|
|
48
|
-
* pipelines) — no modeling geometry. Native classes without a Proxy property
|
|
49
|
-
* used to fall into `type-not-whitelisted` and block whole files whose
|
|
50
|
-
* modeling part was a single box. Like structural types they are
|
|
51
|
-
* `preserved-only`; their regenerable field data (FemMesh / result Data
|
|
52
|
-
* properties) is deliberately NOT carried into the container.
|
|
53
|
-
*
|
|
54
|
-
* Remote task on record (user decision, 2026-09-20): faijs will port
|
|
55
|
-
* FreeCAD's FEM analysis capability in the future — separate feature; this
|
|
56
|
-
* classification only stops simulation objects from blocking conversion.
|
|
57
|
-
* Python subclasses (*Python with Proxy) never reach this set — H10's
|
|
58
|
-
* python-opaque handles them.
|
|
59
|
-
*/
|
|
60
|
-
export const FEM_STRUCTURAL_TYPES = new Set([
|
|
61
|
-
'Fem::FemAnalysis',
|
|
62
|
-
'Fem::FemMeshObject',
|
|
63
|
-
'Fem::FemResultObject',
|
|
64
|
-
'Fem::FemPostPipeline',
|
|
65
|
-
'Fem::ConstraintFixed',
|
|
66
|
-
'Fem::ConstraintForce',
|
|
67
|
-
'Fem::ConstraintPressure',
|
|
68
|
-
'Fem::ConstraintContact',
|
|
69
|
-
'Fem::ConstraintBearing',
|
|
70
|
-
'Fem::ConstraintDisplacement',
|
|
71
|
-
'Fem::FemAnalysisPython',
|
|
72
|
-
'Fem::FemMeshObjectPython',
|
|
73
|
-
'Fem::FemResultObjectPython',
|
|
74
|
-
'Fem::FemSolverObjectPython',
|
|
75
|
-
'Fem::FemMeshShapeBaseObjectPython',
|
|
76
|
-
]);
|
|
77
|
-
/** True when the type is a FEM simulation object (structured non-modeling). */
|
|
78
|
-
export function isFemStructural(type) {
|
|
79
|
-
return FEM_STRUCTURAL_TYPES.has(type);
|
|
80
|
-
}
|
|
81
39
|
/**
|
|
82
40
|
* C4 final check: reclassify `baked` entries. A baked disposition is only
|
|
83
41
|
* legitimate for Python-opaque objects (python-baked), structural/datum
|
|
@@ -92,9 +50,11 @@ function auditMapping(mapping) {
|
|
|
92
50
|
if (o.reason === 'python-opaque') {
|
|
93
51
|
o.disposition = 'python-baked';
|
|
94
52
|
}
|
|
95
|
-
else if (STRUCTURAL_TYPES.has(o.type) ||
|
|
53
|
+
else if (STRUCTURAL_TYPES.has(o.type) || isFemStructural(o.type) || STRUCTURAL_TYPES_EXTENDED.has(o.type)) {
|
|
96
54
|
o.disposition = 'preserved-only';
|
|
97
|
-
o.reason = o.reason
|
|
55
|
+
o.reason = o.reason
|
|
56
|
+
?? (isFemStructural(o.type) ? 'fem-simulation'
|
|
57
|
+
: STRUCTURAL_TYPES_EXTENDED.has(o.type) ? 'container-link' : 'structural');
|
|
98
58
|
}
|
|
99
59
|
else {
|
|
100
60
|
gaps.push({ name: o.name, type: o.type, reason: o.reason ?? 'unspecified' });
|
|
@@ -196,15 +156,37 @@ export async function convertFcstdFile(input, opts) {
|
|
|
196
156
|
// H7: objects whose Shape is stored as a ZIP .brp member (pure-Shape
|
|
197
157
|
// carriers, e.g. Part::Feature) — collected from the archive members so the
|
|
198
158
|
// translator can record `shape-asset` instead of a translation gap.
|
|
159
|
+
// H7 follow-up: SubShape carriers (feature result caches in Body-less
|
|
160
|
+
// PartDesign files) join the same set — the translator only honors SubShape
|
|
161
|
+
// evidence for features whose SubShape .brp member exists.
|
|
162
|
+
// GOTCHA (PadTest, 2026-09-21): non-modeling types are EXCLUDED. A
|
|
163
|
+
// `PartDesign::Plane` also has a Shape .brp — the plane face — so the
|
|
164
|
+
// generic rule imported it as a solid and the Body chain unioned the datum
|
|
165
|
+
// plane into the part (a bare face cannot take part in the boolean). A
|
|
166
|
+
// datum's shape is support geometry for attachment/up-to resolution.
|
|
199
167
|
const shapeCarriers = new Set();
|
|
168
|
+
// E4: objects whose Shape/SubShape `file` attribute points at a missing or
|
|
169
|
+
// ZERO-BYTE member (FC_site_simple-102: `Site.Shape.brp` exists but is 0
|
|
170
|
+
// bytes). They must surface as an explicit convert-time gap — without this
|
|
171
|
+
// the object falls through to python-opaque and the broken-asset fact is
|
|
172
|
+
// silently swallowed into `python-baked` (the product then reports no
|
|
173
|
+
// geometry instead of naming the defect).
|
|
174
|
+
const brokenShapeAssets = new Set();
|
|
200
175
|
for (const obj of doc.value.objects) {
|
|
201
|
-
|
|
202
|
-
|
|
176
|
+
if (isNonModelingType(obj.type))
|
|
177
|
+
continue;
|
|
178
|
+
const shapeFile = obj.properties.get('Shape')?.children[0]?.attributes['file'];
|
|
179
|
+
const subShapeFile = obj.properties.get('SubShape')?.children[0]?.attributes['file'];
|
|
180
|
+
const shapeOk = shapeFile !== undefined && !!memberText(unpacked.value, shapeFile);
|
|
181
|
+
const subShapeOk = subShapeFile !== undefined && !!memberText(unpacked.value, subShapeFile);
|
|
182
|
+
if (shapeOk || subShapeOk)
|
|
203
183
|
shapeCarriers.add(obj.name);
|
|
184
|
+
else if (shapeFile !== undefined || subShapeFile !== undefined)
|
|
185
|
+
brokenShapeAssets.add(obj.name);
|
|
204
186
|
}
|
|
205
187
|
let gen;
|
|
206
188
|
try {
|
|
207
|
-
gen = generateModel(doc.value, sketchVerdict, sketchContours, baseName, placements, shapeCarriers);
|
|
189
|
+
gen = generateModel(doc.value, sketchVerdict, sketchContours, baseName, placements, shapeCarriers, brokenShapeAssets);
|
|
208
190
|
}
|
|
209
191
|
catch (e) {
|
|
210
192
|
return fail(`codegen failed: ${e.message}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/fcstd/convert.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * B0 — THE FCStd → .fai.zip conversion pipeline (batch CLI and tests share it;\n * `src/fcstd/cli.ts` is its only CLI). Returns a structured summary; the caller\n * decides exit codes and output formatting (C4: batch driver must never absorb\n * failures).\n *\n * 2026-09-20: the M5.4-era dev script `scripts/fcstd-to-fai-zip.ts` was a second\n * implementation of this same pipeline (no C4 audit, no exit-code contract) and\n * has been deleted. Whether the conversion entry is published or not, this file\n * is the single implementation.\n *\n * Dispositions (plan §2, C4): translated | python-baked | preserved-only.\n * Any other baked reason = translation gap → result.ok = false, no zip\n * written (final check `auditMapping`).\n */\nimport { readFileSync } from 'node:fs';\nimport { unpackFcstd, memberText } from './unpack.js';\nimport { parseDocumentXml } from './document.js';\nimport { parseSketchObject } from './sketch-parse.js';\nimport { createPlanegcsSolver } from './planegcs-backend.js';\nimport { classifySketch } from './sketch-verify.js';\nimport { resolveExternalGeometry } from './external-geo.js';\nimport { extractContours } from './contour.js';\nimport type { Contour } from './contour.js';\nimport { generateModel } from './codegen.js';\nimport { placementOf, type Placement } from './placement.js';\nimport { effectivePlacement } from './attachment.js';\nimport { buildFaiZip } from './build-fai-zip.js';\nimport { isOk } from '../api/result.js';\nimport { zipSync, unzipSync, strToU8 } from 'fflate';\n\n/** V2 tolerance: solver must reproduce stored geometry (single source). */\nexport const SKETCH_T1 = 1e-6;\n\n/** Dispositions allowed in a conforming container (C4). */\nexport const ALLOWED_DISPOSITIONS = new Set(['translated', 'python-baked', 'preserved-only']);\n\n/**\n * Optional knobs for `convertFcstdFile`.\n */\nexport interface ConvertOptions {\n /**\n * Return the container bytes even when the mapping audit reports gaps.\n *\n * Diagnostics only. C4 requires that a gapped document produces **no\n * product**, and the CLI never sets this — `ok` stays false and the exit\n * code stays 2 regardless. It exists because everything the pipeline built\n * before the audit is otherwise unobservable for exactly the documents that\n * need triage: gaps and non-L0 sketches usually co-occur (a sketch the\n * solver cannot handle usually starves the features built on it), so\n * \"which contour assets / mapping entries would this document have had\"\n * cannot be answered from `gaps[]` alone.\n */\n keepGappedContainer?: boolean;\n}\n\n/** Structured result of one FCStd → .fai.zip conversion; the caller decides exit codes and formatting. */\nexport interface ConvertSummary {\n /** input file path */\n file: string;\n /** conversion succeeded AND mapping final check passed */\n ok: boolean;\n /** translation gaps (non-Python baked) — empty when ok */\n gaps: { name: string; type: string; reason: string }[];\n counts: { translated: number; pythonBaked: number; preservedOnly: number; baked: number };\n sketches: { total: number; l0: number; l1: number; l2: number };\n /** container bytes (undefined when ok=false, unless `keepGappedContainer`) */\n zip?: Uint8Array;\n /** human-readable failure when the pipeline itself failed */\n error?: string;\n elapsedMs: number;\n}\n\n/** Non-modeling structural/datum types: never translation gaps. They carry\n * no feature semantics (containers, datum planes/lines/origins, groups) —\n * their semantics are consumed by the translator (Body.Group ordering,\n * datum-plane UpToFace anchors), not emitted as cad calls. */\nconst STRUCTURAL_TYPES = new Set([\n 'PartDesign::Body', 'App::Origin', 'App::Plane', 'App::Line',\n 'App::DocumentObjectGroup', 'App::Part', 'PartDesign::Plane', 'PartDesign::Line',\n 'PartDesign::CoordinateSystem',\n // H10 companion fix (plan §3.5): datum/annotation types, not modeling\n // features — previously misclassified as translation gaps.\n 'App::Point', 'App::Annotation',\n]);\n\n/**\n * H7 companion (2026-09-20): FEM workbench objects carry SIMULATION semantics\n * (analysis containers, meshes, solver settings, boundary conditions, result\n * pipelines) — no modeling geometry. Native classes without a Proxy property\n * used to fall into `type-not-whitelisted` and block whole files whose\n * modeling part was a single box. Like structural types they are\n * `preserved-only`; their regenerable field data (FemMesh / result Data\n * properties) is deliberately NOT carried into the container.\n *\n * Remote task on record (user decision, 2026-09-20): faijs will port\n * FreeCAD's FEM analysis capability in the future — separate feature; this\n * classification only stops simulation objects from blocking conversion.\n * Python subclasses (*Python with Proxy) never reach this set — H10's\n * python-opaque handles them.\n */\nexport const FEM_STRUCTURAL_TYPES = new Set([\n 'Fem::FemAnalysis',\n 'Fem::FemMeshObject',\n 'Fem::FemResultObject',\n 'Fem::FemPostPipeline',\n 'Fem::ConstraintFixed',\n 'Fem::ConstraintForce',\n 'Fem::ConstraintPressure',\n 'Fem::ConstraintContact',\n 'Fem::ConstraintBearing',\n 'Fem::ConstraintDisplacement',\n 'Fem::FemAnalysisPython',\n 'Fem::FemMeshObjectPython',\n 'Fem::FemResultObjectPython',\n 'Fem::FemSolverObjectPython',\n 'Fem::FemMeshShapeBaseObjectPython',\n]);\n\n/** True when the type is a FEM simulation object (structured non-modeling). */\nexport function isFemStructural(type: string): boolean {\n return FEM_STRUCTURAL_TYPES.has(type);\n}\n\n/**\n * C4 final check: reclassify `baked` entries. A baked disposition is only\n * legitimate for Python-opaque objects (python-baked), structural/datum\n * containers (preserved-only), or preserved display members. Everything\n * else is a translation gap.\n */\nfunction auditMapping(\n mapping: { objects: { name: string; type: string; disposition: string; reason?: string }[] },\n): ConvertSummary['gaps'] {\n const gaps: ConvertSummary['gaps'] = [];\n for (const o of mapping.objects) {\n if (o.disposition === 'baked') {\n // python-opaque stays legitimate but is renamed for the ledger\n if (o.reason === 'python-opaque') {\n o.disposition = 'python-baked';\n } else if (STRUCTURAL_TYPES.has(o.type) || FEM_STRUCTURAL_TYPES.has(o.type)) {\n o.disposition = 'preserved-only';\n o.reason = o.reason ?? (FEM_STRUCTURAL_TYPES.has(o.type) ? 'fem-simulation' : 'structural');\n } else {\n gaps.push({ name: o.name, type: o.type, reason: o.reason ?? 'unspecified' });\n }\n }\n }\n return gaps;\n}\n\n/**\n * Convert one FCStd file through the full pipeline (unpack → parse → sketch\n * solving → codegen → container build) and return a structured summary.\n * @param input path to the .FCStd file to convert\n * @param opts optional knobs (see {@link ConvertOptions}); defaults keep the C4 contract\n * @returns a summary with ok=false (and no zip, unless `keepGappedContainer`) on any pipeline failure or translation gap\n */\nexport async function convertFcstdFile(input: string, opts?: ConvertOptions): Promise<ConvertSummary> {\n const t0 = Date.now();\n const baseName = input.replace(/^.*[/\\\\]/, '').replace(/\\.fcstd$/i, '');\n const fail = (error: string): ConvertSummary => ({\n file: input, ok: false, gaps: [],\n counts: { translated: 0, pythonBaked: 0, preservedOnly: 0, baked: 0 },\n sketches: { total: 0, l0: 0, l1: 0, l2: 0 }, error, elapsedMs: Date.now() - t0,\n });\n\n let raw: Uint8Array;\n try {\n raw = new Uint8Array(readFileSync(input));\n } catch (e) {\n return fail(`read failed: ${(e as Error).message}`);\n }\n const unpacked = unpackFcstd(raw);\n if (!isOk(unpacked)) return fail(`unpack failed: ${JSON.stringify(unpacked.error)}`);\n const xml = memberText(unpacked.value, 'Document.xml');\n if (xml === undefined) return fail('Document.xml missing');\n const doc = parseDocumentXml(xml);\n if (!isOk(doc)) return fail(`parse failed: ${doc.error.message}`);\n\n // M3: solve every sketch (same pipeline as the dev script)\n const solver = await createPlanegcsSolver();\n const sketchVerdict = new Map<string, { level: 'L0' | 'L1' | 'L2'; reason?: string; loopCount?: number }>();\n const sketchContours = new Map<string, Contour[]>();\n for (const obj of doc.value.objects) {\n if (obj.type !== 'Sketcher::SketchObject') continue;\n const sk = parseSketchObject(obj.properties.get('Geometry'), obj.properties.get('Constraints'), false);\n const offPlane = sk.geoms.some((g) => {\n const zs = g.kind === 'point' ? [g.z]\n : g.kind === 'line' ? [g.z1, g.z2]\n : [g.cz];\n return zs.some((z) => Math.abs(z) > 1e-9);\n });\n const preBlocked =\n offPlane ? 'sketch-geometry-off-plane'\n : sk.geoms.some((g) => !Number.isFinite((g as { x?: number }).x ?? 0)) ? 'unsupported-geometry'\n : undefined;\n if (preBlocked) {\n sketchVerdict.set(obj.name, { level: 'L2', reason: preBlocked });\n continue;\n }\n let external: { geoId: number; polyline: [number, number][] }[] | undefined;\n if (sk.externalGeoIds.length > 0) {\n const ext = await resolveExternalGeometry(\n obj.properties.get('ExternalGeometry'), doc.value, unpacked.value, obj.properties.get('Placement'),\n );\n const usable = ext.links.filter((l) => l.polyline.length === 2);\n if (usable.length === 0) {\n sketchVerdict.set(obj.name, {\n level: 'L2',\n reason: `external-geometry-unresolved: ${ext.failures[0]?.reason ?? 'no links'}`,\n });\n continue;\n }\n external = usable.map((l, i) => ({ geoId: -3 - i, polyline: l.polyline }));\n }\n try {\n const r = await solver.solve(sk.geoms, sk.constraints, external);\n if (!isOk(r)) {\n sketchVerdict.set(obj.name, { level: 'L2', reason: 'solver-error' });\n continue;\n }\n const verdict = classifySketch(r.value, sk.geoms, SKETCH_T1);\n const contours = verdict.level === 'L0' ? extractContours(r.value.geoms) : [];\n sketchVerdict.set(obj.name, { ...verdict, loopCount: contours.length });\n if (verdict.level === 'L0') sketchContours.set(obj.name, contours);\n } catch (e) {\n sketchVerdict.set(obj.name, { level: 'L2', reason: `solver-throw: ${(e as Error).message.slice(0, 60)}` });\n }\n }\n\n // M4/M5: translate + codegen\n // H3: attachment-resolved placements — an attached sketch's stored Placement\n // is recomputed by FreeCAD from Support ∘ AttachmentOffset; resolve the chain\n // so non-XY-plane sketches land on their support frame (fall back to the\n // stored value when the attachment is deactivated or unresolvable).\n const placements = new Map<string, Placement>();\n for (const obj of doc.value.objects) {\n placements.set(obj.name, effectivePlacement(obj, placements));\n }\n // H7: objects whose Shape is stored as a ZIP .brp member (pure-Shape\n // carriers, e.g. Part::Feature) — collected from the archive members so the\n // translator can record `shape-asset` instead of a translation gap.\n const shapeCarriers = new Set<string>();\n for (const obj of doc.value.objects) {\n const fileAttr = obj.properties.get('Shape')?.children[0]?.attributes['file'];\n if (fileAttr && memberText(unpacked.value, fileAttr) !== undefined) shapeCarriers.add(obj.name);\n }\n\n let gen;\n try {\n gen = generateModel(doc.value, sketchVerdict, sketchContours, baseName, placements, shapeCarriers);\n } catch (e) {\n return fail(`codegen failed: ${(e as Error).message}`);\n }\n\n // M2: container with shadow, then inject model/ + updated mapping\n const built = buildFaiZip(unpacked.value, baseName + '.FCStd');\n if (built.error || !built.result) return fail(`container build failed: ${built.error ?? 'unknown'}`);\n const members: Record<string, Uint8Array> = {};\n for (const [k, v] of Object.entries(unzipSync(built.result.zip))) members[k] = v;\n members['model/main.fai.js'] = strToU8(gen.code);\n for (const f of gen.files) members[f.path] = strToU8(f.code);\n\n const mapping = built.result.mapping;\n for (const o of gen.objects) {\n const entry = mapping.objects.find((e) => e.name === o.name);\n if (!entry) continue;\n entry.disposition = o.disposition;\n if (o.reason) entry.reason = o.reason;\n if (o.sketch) {\n entry.sketch = {\n level: o.sketch.level === 'L0' ? 'solved' : o.sketch.level === 'L1' ? 'initial-value' : 'baked',\n reason: o.sketch.reason,\n gcs: o.sketch,\n };\n }\n if (o.disposition === 'translated' && !entry.artifacts.includes('model/main.fai.js')) {\n entry.artifacts.push('model/main.fai.js');\n }\n }\n\n // M11.4 (G9): L1/L2 sketches persist raw geometry as a contour asset\n for (const obj of doc.value.objects) {\n if (obj.type !== 'Sketcher::SketchObject') continue;\n const verdict = sketchVerdict.get(obj.name);\n if (!verdict || verdict.level === 'L0') continue;\n const sk = parseSketchObject(obj.properties.get('Geometry'), obj.properties.get('Constraints'), false);\n const asset = {\n sketch: obj.name,\n level: verdict.level,\n reason: verdict.reason,\n geoms: sk.geoms,\n constraints: sk.constraints.map((c) => ({ index: c.index, type: c.type, refs: c.refs, value: c.value, isDriving: c.isDriving })),\n };\n const path = `assets/${obj.name}.contour.json`;\n members[path] = strToU8(JSON.stringify(asset, null, 2));\n const entry = mapping.objects.find((e) => e.name === obj.name);\n if (entry && !entry.artifacts.includes(path)) entry.artifacts.push(path);\n }\n\n // C4 final check: reclassify python-opaque; everything else baked = gap\n const gaps = auditMapping(mapping);\n members['mapping.json'] = strToU8(JSON.stringify(mapping, null, 2));\n\n const counts = { translated: 0, pythonBaked: 0, preservedOnly: 0, baked: 0 };\n for (const o of mapping.objects) {\n const d = o.disposition as string; // auditMapping may rename baked -> python-baked\n if (d === 'translated') counts.translated++;\n else if (d === 'python-baked') counts.pythonBaked++;\n else if (d === 'preserved-only') counts.preservedOnly++;\n else counts.baked++;\n }\n const sketches = { total: sketchVerdict.size, l0: 0, l1: 0, l2: 0 };\n for (const v of sketchVerdict.values()) sketches[v.level.toLowerCase() as 'l0' | 'l1' | 'l2']++;\n\n if (gaps.length > 0) {\n // C4: translation gaps → no container produced (exit contract: 2).\n // `keepGappedContainer` is a diagnostics-only escape hatch (see ConvertOptions).\n if (!opts?.keepGappedContainer) {\n return { file: input, ok: false, gaps, counts, sketches, elapsedMs: Date.now() - t0 };\n }\n const gappedZip = zipSync(members, { level: 6 });\n return { file: input, ok: false, gaps, counts, sketches, zip: gappedZip, elapsedMs: Date.now() - t0 };\n }\n\n const zip = zipSync(members, { level: 6 });\n return { file: input, ok: true, gaps, counts, sketches, zip, elapsedMs: Date.now() - t0 };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/fcstd/convert.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * B0 — THE FCStd → .fai.zip conversion pipeline (batch CLI and tests share it;\n * `src/fcstd/cli.ts` is its only CLI). Returns a structured summary; the caller\n * decides exit codes and output formatting (C4: batch driver must never absorb\n * failures).\n *\n * 2026-09-20: the M5.4-era dev script `scripts/fcstd-to-fai-zip.ts` was a second\n * implementation of this same pipeline (no C4 audit, no exit-code contract) and\n * has been deleted. Whether the conversion entry is published or not, this file\n * is the single implementation.\n *\n * Dispositions (plan §2, C4): translated | python-baked | preserved-only.\n * Any other baked reason = translation gap → result.ok = false, no zip\n * written (final check `auditMapping`).\n */\nimport { readFileSync } from 'node:fs';\nimport { unpackFcstd, memberText } from './unpack.js';\nimport { parseDocumentXml } from './document.js';\nimport { parseSketchObject } from './sketch-parse.js';\nimport { createPlanegcsSolver } from './planegcs-backend.js';\nimport { classifySketch } from './sketch-verify.js';\nimport { resolveExternalGeometry } from './external-geo.js';\nimport { extractContours } from './contour.js';\nimport type { Contour } from './contour.js';\nimport { generateModel } from './codegen.js';\nimport type { Placement } from './placement.js';\nimport { effectivePlacement } from './attachment.js';\nimport { buildFaiZip } from './build-fai-zip.js';\nimport { isOk } from '../api/result.js';\nimport { zipSync, unzipSync, strToU8 } from 'fflate';\nimport {\n STRUCTURAL_TYPES,\n STRUCTURAL_TYPES_EXTENDED,\n isFemStructural,\n isNonModelingType,\n} from './structural-types.js';\n\n// Kept on this module's public surface: these sets are part of the\n// `./fcstd-convert` contract that the batch project and its tests consume. The\n// definitions live in `structural-types.ts` so codegen can share them without\n// a circular import.\nexport { STRUCTURAL_TYPES, STRUCTURAL_TYPES_EXTENDED, isFemStructural };\n\n/** V2 tolerance: solver must reproduce stored geometry (single source). */\nexport const SKETCH_T1 = 1e-6;\n\n/** Dispositions allowed in a conforming container (C4). */\nexport const ALLOWED_DISPOSITIONS = new Set(['translated', 'python-baked', 'preserved-only']);\n\n/**\n * Optional knobs for `convertFcstdFile`.\n */\nexport interface ConvertOptions {\n /**\n * Return the container bytes even when the mapping audit reports gaps.\n *\n * Diagnostics only. C4 requires that a gapped document produces **no\n * product**, and the CLI never sets this — `ok` stays false and the exit\n * code stays 2 regardless. It exists because everything the pipeline built\n * before the audit is otherwise unobservable for exactly the documents that\n * need triage: gaps and non-L0 sketches usually co-occur (a sketch the\n * solver cannot handle usually starves the features built on it), so\n * \"which contour assets / mapping entries would this document have had\"\n * cannot be answered from `gaps[]` alone.\n */\n keepGappedContainer?: boolean;\n}\n\n/** Structured result of one FCStd → .fai.zip conversion; the caller decides exit codes and formatting. */\nexport interface ConvertSummary {\n /** input file path */\n file: string;\n /** conversion succeeded AND mapping final check passed */\n ok: boolean;\n /** translation gaps (non-Python baked) — empty when ok */\n gaps: { name: string; type: string; reason: string }[];\n counts: { translated: number; pythonBaked: number; preservedOnly: number; baked: number };\n sketches: { total: number; l0: number; l1: number; l2: number };\n /** container bytes (undefined when ok=false, unless `keepGappedContainer`) */\n zip?: Uint8Array;\n /** human-readable failure when the pipeline itself failed */\n error?: string;\n elapsedMs: number;\n}\n\n/**\n * C4 final check: reclassify `baked` entries. A baked disposition is only\n * legitimate for Python-opaque objects (python-baked), structural/datum\n * containers (preserved-only), or preserved display members. Everything\n * else is a translation gap.\n */\nfunction auditMapping(\n mapping: { objects: { name: string; type: string; disposition: string; reason?: string }[] },\n): ConvertSummary['gaps'] {\n const gaps: ConvertSummary['gaps'] = [];\n for (const o of mapping.objects) {\n if (o.disposition === 'baked') {\n // python-opaque stays legitimate but is renamed for the ledger\n if (o.reason === 'python-opaque') {\n o.disposition = 'python-baked';\n } else if (STRUCTURAL_TYPES.has(o.type) || isFemStructural(o.type) || STRUCTURAL_TYPES_EXTENDED.has(o.type)) {\n o.disposition = 'preserved-only';\n o.reason = o.reason\n ?? (isFemStructural(o.type) ? 'fem-simulation'\n : STRUCTURAL_TYPES_EXTENDED.has(o.type) ? 'container-link' : 'structural');\n } else {\n gaps.push({ name: o.name, type: o.type, reason: o.reason ?? 'unspecified' });\n }\n }\n }\n return gaps;\n}\n\n/**\n * Convert one FCStd file through the full pipeline (unpack → parse → sketch\n * solving → codegen → container build) and return a structured summary.\n * @param input path to the .FCStd file to convert\n * @param opts optional knobs (see {@link ConvertOptions}); defaults keep the C4 contract\n * @returns a summary with ok=false (and no zip, unless `keepGappedContainer`) on any pipeline failure or translation gap\n */\nexport async function convertFcstdFile(input: string, opts?: ConvertOptions): Promise<ConvertSummary> {\n const t0 = Date.now();\n const baseName = input.replace(/^.*[/\\\\]/, '').replace(/\\.fcstd$/i, '');\n const fail = (error: string): ConvertSummary => ({\n file: input, ok: false, gaps: [],\n counts: { translated: 0, pythonBaked: 0, preservedOnly: 0, baked: 0 },\n sketches: { total: 0, l0: 0, l1: 0, l2: 0 }, error, elapsedMs: Date.now() - t0,\n });\n\n let raw: Uint8Array;\n try {\n raw = new Uint8Array(readFileSync(input));\n } catch (e) {\n return fail(`read failed: ${(e as Error).message}`);\n }\n const unpacked = unpackFcstd(raw);\n if (!isOk(unpacked)) return fail(`unpack failed: ${JSON.stringify(unpacked.error)}`);\n const xml = memberText(unpacked.value, 'Document.xml');\n if (xml === undefined) return fail('Document.xml missing');\n const doc = parseDocumentXml(xml);\n if (!isOk(doc)) return fail(`parse failed: ${doc.error.message}`);\n\n // M3: solve every sketch (same pipeline as the dev script)\n const solver = await createPlanegcsSolver();\n const sketchVerdict = new Map<string, { level: 'L0' | 'L1' | 'L2'; reason?: string; loopCount?: number }>();\n const sketchContours = new Map<string, Contour[]>();\n for (const obj of doc.value.objects) {\n if (obj.type !== 'Sketcher::SketchObject') continue;\n const sk = parseSketchObject(obj.properties.get('Geometry'), obj.properties.get('Constraints'), false);\n const offPlane = sk.geoms.some((g) => {\n const zs = g.kind === 'point' ? [g.z]\n : g.kind === 'line' ? [g.z1, g.z2]\n : [g.cz];\n return zs.some((z) => Math.abs(z) > 1e-9);\n });\n const preBlocked =\n offPlane ? 'sketch-geometry-off-plane'\n : sk.geoms.some((g) => !Number.isFinite((g as { x?: number }).x ?? 0)) ? 'unsupported-geometry'\n : undefined;\n if (preBlocked) {\n sketchVerdict.set(obj.name, { level: 'L2', reason: preBlocked });\n continue;\n }\n let external: { geoId: number; polyline: [number, number][] }[] | undefined;\n if (sk.externalGeoIds.length > 0) {\n const ext = await resolveExternalGeometry(\n obj.properties.get('ExternalGeometry'), doc.value, unpacked.value, obj.properties.get('Placement'),\n );\n const usable = ext.links.filter((l) => l.polyline.length === 2);\n if (usable.length === 0) {\n sketchVerdict.set(obj.name, {\n level: 'L2',\n reason: `external-geometry-unresolved: ${ext.failures[0]?.reason ?? 'no links'}`,\n });\n continue;\n }\n external = usable.map((l, i) => ({ geoId: -3 - i, polyline: l.polyline }));\n }\n try {\n const r = await solver.solve(sk.geoms, sk.constraints, external);\n if (!isOk(r)) {\n sketchVerdict.set(obj.name, { level: 'L2', reason: 'solver-error' });\n continue;\n }\n const verdict = classifySketch(r.value, sk.geoms, SKETCH_T1);\n const contours = verdict.level === 'L0' ? extractContours(r.value.geoms) : [];\n sketchVerdict.set(obj.name, { ...verdict, loopCount: contours.length });\n if (verdict.level === 'L0') sketchContours.set(obj.name, contours);\n } catch (e) {\n sketchVerdict.set(obj.name, { level: 'L2', reason: `solver-throw: ${(e as Error).message.slice(0, 60)}` });\n }\n }\n\n // M4/M5: translate + codegen\n // H3: attachment-resolved placements — an attached sketch's stored Placement\n // is recomputed by FreeCAD from Support ∘ AttachmentOffset; resolve the chain\n // so non-XY-plane sketches land on their support frame (fall back to the\n // stored value when the attachment is deactivated or unresolvable).\n const placements = new Map<string, Placement>();\n for (const obj of doc.value.objects) {\n placements.set(obj.name, effectivePlacement(obj, placements));\n }\n // H7: objects whose Shape is stored as a ZIP .brp member (pure-Shape\n // carriers, e.g. Part::Feature) — collected from the archive members so the\n // translator can record `shape-asset` instead of a translation gap.\n // H7 follow-up: SubShape carriers (feature result caches in Body-less\n // PartDesign files) join the same set — the translator only honors SubShape\n // evidence for features whose SubShape .brp member exists.\n // GOTCHA (PadTest, 2026-09-21): non-modeling types are EXCLUDED. A\n // `PartDesign::Plane` also has a Shape .brp — the plane face — so the\n // generic rule imported it as a solid and the Body chain unioned the datum\n // plane into the part (a bare face cannot take part in the boolean). A\n // datum's shape is support geometry for attachment/up-to resolution.\n const shapeCarriers = new Set<string>();\n // E4: objects whose Shape/SubShape `file` attribute points at a missing or\n // ZERO-BYTE member (FC_site_simple-102: `Site.Shape.brp` exists but is 0\n // bytes). They must surface as an explicit convert-time gap — without this\n // the object falls through to python-opaque and the broken-asset fact is\n // silently swallowed into `python-baked` (the product then reports no\n // geometry instead of naming the defect).\n const brokenShapeAssets = new Set<string>();\n for (const obj of doc.value.objects) {\n if (isNonModelingType(obj.type)) continue;\n const shapeFile = obj.properties.get('Shape')?.children[0]?.attributes['file'];\n const subShapeFile = obj.properties.get('SubShape')?.children[0]?.attributes['file'];\n const shapeOk = shapeFile !== undefined && !!memberText(unpacked.value, shapeFile);\n const subShapeOk = subShapeFile !== undefined && !!memberText(unpacked.value, subShapeFile);\n if (shapeOk || subShapeOk) shapeCarriers.add(obj.name);\n else if (shapeFile !== undefined || subShapeFile !== undefined) brokenShapeAssets.add(obj.name);\n }\n\n let gen;\n try {\n gen = generateModel(\n doc.value, sketchVerdict, sketchContours, baseName, placements, shapeCarriers,\n brokenShapeAssets,\n );\n } catch (e) {\n return fail(`codegen failed: ${(e as Error).message}`);\n }\n\n // M2: container with shadow, then inject model/ + updated mapping\n const built = buildFaiZip(unpacked.value, baseName + '.FCStd');\n if (built.error || !built.result) return fail(`container build failed: ${built.error ?? 'unknown'}`);\n const members: Record<string, Uint8Array> = {};\n for (const [k, v] of Object.entries(unzipSync(built.result.zip))) members[k] = v;\n members['model/main.fai.js'] = strToU8(gen.code);\n for (const f of gen.files) members[f.path] = strToU8(f.code);\n\n const mapping = built.result.mapping;\n for (const o of gen.objects) {\n const entry = mapping.objects.find((e) => e.name === o.name);\n if (!entry) continue;\n entry.disposition = o.disposition;\n if (o.reason) entry.reason = o.reason;\n if (o.sketch) {\n entry.sketch = {\n level: o.sketch.level === 'L0' ? 'solved' : o.sketch.level === 'L1' ? 'initial-value' : 'baked',\n reason: o.sketch.reason,\n gcs: o.sketch,\n };\n }\n if (o.disposition === 'translated' && !entry.artifacts.includes('model/main.fai.js')) {\n entry.artifacts.push('model/main.fai.js');\n }\n }\n\n // M11.4 (G9): L1/L2 sketches persist raw geometry as a contour asset\n for (const obj of doc.value.objects) {\n if (obj.type !== 'Sketcher::SketchObject') continue;\n const verdict = sketchVerdict.get(obj.name);\n if (!verdict || verdict.level === 'L0') continue;\n const sk = parseSketchObject(obj.properties.get('Geometry'), obj.properties.get('Constraints'), false);\n const asset = {\n sketch: obj.name,\n level: verdict.level,\n reason: verdict.reason,\n geoms: sk.geoms,\n constraints: sk.constraints.map((c) => ({ index: c.index, type: c.type, refs: c.refs, value: c.value, isDriving: c.isDriving })),\n };\n const path = `assets/${obj.name}.contour.json`;\n members[path] = strToU8(JSON.stringify(asset, null, 2));\n const entry = mapping.objects.find((e) => e.name === obj.name);\n if (entry && !entry.artifacts.includes(path)) entry.artifacts.push(path);\n }\n\n // C4 final check: reclassify python-opaque; everything else baked = gap\n const gaps = auditMapping(mapping);\n members['mapping.json'] = strToU8(JSON.stringify(mapping, null, 2));\n\n const counts = { translated: 0, pythonBaked: 0, preservedOnly: 0, baked: 0 };\n for (const o of mapping.objects) {\n const d = o.disposition as string; // auditMapping may rename baked -> python-baked\n if (d === 'translated') counts.translated++;\n else if (d === 'python-baked') counts.pythonBaked++;\n else if (d === 'preserved-only') counts.preservedOnly++;\n else counts.baked++;\n }\n const sketches = { total: sketchVerdict.size, l0: 0, l1: 0, l2: 0 };\n for (const v of sketchVerdict.values()) sketches[v.level.toLowerCase() as 'l0' | 'l1' | 'l2']++;\n\n if (gaps.length > 0) {\n // C4: translation gaps → no container produced (exit contract: 2).\n // `keepGappedContainer` is a diagnostics-only escape hatch (see ConvertOptions).\n if (!opts?.keepGappedContainer) {\n return { file: input, ok: false, gaps, counts, sketches, elapsedMs: Date.now() - t0 };\n }\n const gappedZip = zipSync(members, { level: 6 });\n return { file: input, ok: false, gaps, counts, sketches, zip: gappedZip, elapsedMs: Date.now() - t0 };\n }\n\n const zip = zipSync(members, { level: 6 });\n return { file: input, ok: true, gaps, counts, sketches, zip, elapsedMs: Date.now() - t0 };\n}\n"]}
|
|
@@ -23,6 +23,30 @@ export type ExprValue = number | undefined;
|
|
|
23
23
|
* @returns the value in mm, or undefined when not a constant expression
|
|
24
24
|
*/
|
|
25
25
|
export declare function evalConstantExpression(expr: string): ExprValue;
|
|
26
|
+
import type { FcstdObject } from './document.js';
|
|
27
|
+
/**
|
|
28
|
+
* 三跳解析:`<<Label>>.Alias` / `Object.Alias` → 数据源对象 → 别名单元格 → 去掉
|
|
29
|
+
* 前导 `=` 后交给 evalConstantExpression。同表地址(`=B2*2` 里的 `B2`)再跳一次。
|
|
30
|
+
*
|
|
31
|
+
* @param docObjects 全文档对象(按 Label 或 name 匹配数据源)
|
|
32
|
+
* @param label 数据源标识(`<<Label>>` 内的 Label,或裸对象名)
|
|
33
|
+
* @param alias 单元格别名
|
|
34
|
+
* @returns 单元格值(mm),不可解析 → undefined
|
|
35
|
+
*/
|
|
36
|
+
export declare function spreadsheetAliasValue(docObjects: readonly FcstdObject[], label: string, alias: string): ExprValue;
|
|
37
|
+
/**
|
|
38
|
+
* 带文档上下文的表达式求值:引用(`<<L>>.A` / `L.A` / 同表地址 `B2`)替换为
|
|
39
|
+
* 数值后,求值仅含常数与 + - * / ( ) 的算术。任何残留标识符 / 函数 → undefined
|
|
40
|
+
* (no heuristic fallback)。返回值单位跟随单元格(mm 语境,角度单元格调用方解释)。
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Evaluate a FreeCAD expression against the document objects.
|
|
44
|
+
* @param expr - the expression text (without the leading `=`).
|
|
45
|
+
* @param docObjects - the document objects visible to the expression.
|
|
46
|
+
* @param self - the object the expression is evaluated on, if any.
|
|
47
|
+
* @returns the evaluated expression value.
|
|
48
|
+
*/
|
|
49
|
+
export declare function evalWithDoc(expr: string, docObjects: readonly FcstdObject[], self?: FcstdObject): ExprValue;
|
|
26
50
|
/** One <Expression path=... expression=...> binding with its optional constant value. */
|
|
27
51
|
export interface ExpressionBinding {
|
|
28
52
|
/** property path the expression drives (e.g. "Length") */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expressions.d.ts","sourceRoot":"","sources":["../../src/fcstd/expressions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,+GAA+G;AAC/G,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAa3C;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAY9D;
|
|
1
|
+
{"version":3,"file":"expressions.d.ts","sourceRoot":"","sources":["../../src/fcstd/expressions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,+GAA+G;AAC/G,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAa3C;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAY9D;AAUD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,SAAS,WAAW,EAAE,EAClC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,SAAS,CAiBX;AAQD;;;;GAIG;AACH;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,WAAW,EAAE,EAClC,IAAI,CAAC,EAAE,WAAW,GACjB,SAAS,CAuBX;AA6BD,yFAAyF;AACzF,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,aAAa,GAAG,SAAS,GAC9B,iBAAiB,EAAE,CAWrB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAElE"}
|
|
@@ -33,6 +33,97 @@ export function evalConstantExpression(expr) {
|
|
|
33
33
|
return undefined; // unknown unit → unsupported
|
|
34
34
|
return value * factor;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* 三跳解析:`<<Label>>.Alias` / `Object.Alias` → 数据源对象 → 别名单元格 → 去掉
|
|
38
|
+
* 前导 `=` 后交给 evalConstantExpression。同表地址(`=B2*2` 里的 `B2`)再跳一次。
|
|
39
|
+
*
|
|
40
|
+
* @param docObjects 全文档对象(按 Label 或 name 匹配数据源)
|
|
41
|
+
* @param label 数据源标识(`<<Label>>` 内的 Label,或裸对象名)
|
|
42
|
+
* @param alias 单元格别名
|
|
43
|
+
* @returns 单元格值(mm),不可解析 → undefined
|
|
44
|
+
*/
|
|
45
|
+
export function spreadsheetAliasValue(docObjects, label, alias) {
|
|
46
|
+
const src = docObjects.find((o) => o.type === 'Spreadsheet::Sheet' &&
|
|
47
|
+
(objectLabel(o) === label || o.name === label));
|
|
48
|
+
if (!src)
|
|
49
|
+
return undefined;
|
|
50
|
+
// cells 属性(Spreadsheet::PropertySheet)的 <Cell address alias content/> 子元素
|
|
51
|
+
const cells = src.properties.get('cells') ?? [...src.properties.values()].find((p) => p.children.some((c) => c.tagName === 'Cell' || c.children.some((g) => g.tagName === 'Cell')));
|
|
52
|
+
if (!cells)
|
|
53
|
+
return undefined;
|
|
54
|
+
const cell = [...cells.children].flatMap((c) => (c.tagName === 'Cell' ? [c] : c.children))
|
|
55
|
+
.find((c) => c.tagName === 'Cell' && c.attributes['alias'] === alias);
|
|
56
|
+
if (!cell)
|
|
57
|
+
return undefined;
|
|
58
|
+
const content = cell.attributes['content'] ?? '';
|
|
59
|
+
// 去掉前导 =(同表地址/算术再走一次带上下文的求值,一跳深度足够语料头部)
|
|
60
|
+
return evalWithDoc(content.startsWith('=') ? content.slice(1) : content, docObjects, src);
|
|
61
|
+
}
|
|
62
|
+
/** 对象的 Label 属性(FreeCAD 引用 `<<Label>>` 用的是它,不是 name)。 */
|
|
63
|
+
function objectLabel(o) {
|
|
64
|
+
const el = o.properties.get('Label')?.children[0];
|
|
65
|
+
return el?.attributes['value'] ?? (el?.valueText || undefined);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 带文档上下文的表达式求值:引用(`<<L>>.A` / `L.A` / 同表地址 `B2`)替换为
|
|
69
|
+
* 数值后,求值仅含常数与 + - * / ( ) 的算术。任何残留标识符 / 函数 → undefined
|
|
70
|
+
* (no heuristic fallback)。返回值单位跟随单元格(mm 语境,角度单元格调用方解释)。
|
|
71
|
+
*/
|
|
72
|
+
/**
|
|
73
|
+
* Evaluate a FreeCAD expression against the document objects.
|
|
74
|
+
* @param expr - the expression text (without the leading `=`).
|
|
75
|
+
* @param docObjects - the document objects visible to the expression.
|
|
76
|
+
* @param self - the object the expression is evaluated on, if any.
|
|
77
|
+
* @returns the evaluated expression value.
|
|
78
|
+
*/
|
|
79
|
+
export function evalWithDoc(expr, docObjects, self) {
|
|
80
|
+
let s = expr.trim();
|
|
81
|
+
// 引用替换:<<Label>>.Alias | Label.Alias | Object.Alias(标识符.标识符)
|
|
82
|
+
s = s.replace(/<<([^>]+)>>\.([A-Za-z_][A-Za-z0-9_]*)|([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)/g, (whole, brLabel, brAlias, plLabel, plAlias) => {
|
|
83
|
+
const label = brLabel ?? plLabel;
|
|
84
|
+
const alias = brAlias ?? plAlias;
|
|
85
|
+
const v = spreadsheetAliasValue(docObjects, label, alias);
|
|
86
|
+
return v === undefined ? whole : `(${v})`;
|
|
87
|
+
});
|
|
88
|
+
// 同表地址(self 表内 address→alias 值,如 `B2` / `B2*2`):仅当 self 是表
|
|
89
|
+
if (self?.type === 'Spreadsheet::Sheet') {
|
|
90
|
+
s = s.replace(/\b([A-Z]+[0-9]+)\b/g, (whole, addr) => {
|
|
91
|
+
const v = spreadsheetAddressValue(self, addr, docObjects);
|
|
92
|
+
return v === undefined ? whole : `(${v})`;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// 单元格值自带单位(=5.9mm)——先按「常数+单位」求值(evalConstantExpression
|
|
96
|
+
// 的既有口径),再进纯算术;单位记号在算术前剥离(返回值跟随 mm 语境)。
|
|
97
|
+
const constant = evalConstantExpression(s);
|
|
98
|
+
if (constant !== undefined)
|
|
99
|
+
return constant;
|
|
100
|
+
s = s.replace(/\b(mm|millimeter|cm|centimeter|m|meter|in|inch|"|ft|foot|deg|degree|°|rad|radian)\b/g, '');
|
|
101
|
+
return evalArithmetic(s);
|
|
102
|
+
}
|
|
103
|
+
/** 同表地址取值:address → 该单元格 content(递归经 evalWithDoc,一跳深度)。 */
|
|
104
|
+
function spreadsheetAddressValue(sheet, address, docObjects) {
|
|
105
|
+
const cells = sheet.properties.get('cells');
|
|
106
|
+
if (!cells)
|
|
107
|
+
return undefined;
|
|
108
|
+
const cell = [...cells.children].flatMap((c) => (c.tagName === 'Cell' ? [c] : c.children))
|
|
109
|
+
.find((c) => c.tagName === 'Cell' && c.attributes['address'] === address);
|
|
110
|
+
if (!cell)
|
|
111
|
+
return undefined;
|
|
112
|
+
const content = cell.attributes['content'] ?? '';
|
|
113
|
+
return evalWithDoc(content.startsWith('=') ? content.slice(1) : content, docObjects, sheet);
|
|
114
|
+
}
|
|
115
|
+
/** 仅含数字与 + - * / ( ) 空格的算术求值;任何其它字符 → undefined。 */
|
|
116
|
+
function evalArithmetic(s) {
|
|
117
|
+
if (!/^[-+*/(). 0-9eE]*$/.test(s))
|
|
118
|
+
return undefined;
|
|
119
|
+
try {
|
|
120
|
+
const v = Function(`"use strict"; return (${s});`)();
|
|
121
|
+
return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
36
127
|
/**
|
|
37
128
|
* Extract expression bindings from a parsed ExpressionEngine property.
|
|
38
129
|
* `bindable` = every expression evaluated to a constant (then the property
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expressions.js","sourceRoot":"","sources":["../../src/fcstd/expressions.ts"],"names":[],"mappings":"AAkBA,MAAM,UAAU,GAA2B;IACzC,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC;IACpB,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE;IACtB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI;IACpB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI;IAC/B,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;IACtB,qEAAqE;IACrE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;IACzB,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACtB,gBAAgB;IAChB,MAAM,CAAC,GAAG,+DAA+D,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClF,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACnB,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC9B,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC,CAAC,6BAA6B;IACzE,OAAO,KAAK,GAAG,MAAM,CAAC;AACxB,CAAC;AAaD;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAA+B;IAE/B,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,EAAE,CAAC,OAAO,KAAK,YAAY;YAAE,SAAS;QAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,sBAAsB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,QAA6B;IACvD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["/**\r\n * M6.2 — expression downgrade: <ExpressionEngine> → concrete values.\r\n *\r\n * Format (verified on TwoLengthsPadWithExpression.FCStd):\r\n * <ExpressionEngine count=\"N\">\r\n * <Expression path=\"Length\" expression=\"10 mm\" />\r\n * </ExpressionEngine>\r\n *\r\n * Scope: constant expressions only — number literals with optional unit\r\n * (mm/m/cm/in/deg/rad). Anything else (object references like Sketch.Constraints[3],\r\n * arithmetic with identifiers, functions) is NOT evaluated: the consumer must\r\n * treat the property as unknown and degrade (no heuristic fallback, plan §12).\r\n */\r\nimport type { FcstdProperty } from './document.js';\r\n\r\n/** Evaluated expression result in mm (angles keep their own unit): a number, or undefined when unsupported. */\nexport type ExprValue = number | undefined;\r\n\r\nconst UNIT_TO_MM: Record<string, number> = {\r\n mm: 1, millimeter: 1,\r\n cm: 10, centimeter: 10,\r\n m: 1000, meter: 1000,\r\n in: 25.4, inch: 25.4, '\"': 25.4,\r\n ft: 304.8, foot: 304.8,\r\n // angles: value kept in the expression's own unit; callers interpret\r\n deg: 1, degree: 1, '°': 1,\r\n rad: 1, radian: 1,\r\n};\r\n\r\n/**\r\n * Evaluate a constant expression. Returns undefined when the expression is\r\n * not a bare constant (reference/arithmetic/function) — explicit unsupported,\r\n * no guessing.\r\n *\r\n * @param expr raw expression string (e.g. \"10 mm\")\r\n * @returns the value in mm, or undefined when not a constant expression\r\n */\r\nexport function evalConstantExpression(expr: string): ExprValue {\r\n const s = expr.trim();\r\n // number [unit]\r\n const m = /^([-+]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][-+]?\\d+)?)\\s*([A-Za-z°\"]*)$/.exec(s);\r\n if (!m) return undefined;\r\n const value = Number(m[1]);\r\n if (!Number.isFinite(value)) return undefined;\r\n const unit = m[2]!;\r\n if (unit === '') return value;\r\n const factor = UNIT_TO_MM[unit.toLowerCase()];\r\n if (factor === undefined) return undefined; // unknown unit → unsupported\r\n return value * factor;\r\n}\r\n\r\n\r\n/** One <Expression path=... expression=...> binding with its optional constant value. */\r\nexport interface ExpressionBinding {\r\n /** property path the expression drives (e.g. \"Length\") */\r\n path: string;\r\n /** raw expression string */\r\n expression: string;\r\n /** evaluated constant; undefined when not a constant expression */\r\n value?: number;\r\n}\r\n\r\n/**\r\n * Extract expression bindings from a parsed ExpressionEngine property.\r\n * `bindable` = every expression evaluated to a constant (then the property\r\n * values can be overridden); otherwise the engine is only partially readable.\r\n *\r\n * @param prop the parsed <ExpressionEngine> property (undefined → no bindings)\r\n * @returns one binding per <Expression> child, values evaluated\r\n */\r\nexport function parseExpressionEngine(\r\n prop: FcstdProperty | undefined,\r\n): ExpressionBinding[] {\r\n const engineEl = prop?.children[0];\r\n if (!engineEl) return [];\r\n const out: ExpressionBinding[] = [];\r\n for (const el of engineEl.children) {\r\n if (el.tagName !== 'Expression') continue;\r\n const path = el.attributes['path'] ?? '';\r\n const expression = el.attributes['expression'] ?? '';\r\n out.push({ path, expression, value: evalConstantExpression(expression) });\r\n }\r\n return out;\r\n}\r\n\r\n/**\n * True when all bindings are constants → safe to override property values.\r\n * @param bindings bindings extracted by parseExpressionEngine\r\n * @returns true when there is at least one binding and every value is defined\r\n */\r\nexport function allConstant(bindings: ExpressionBinding[]): boolean {\r\n return bindings.length > 0 && bindings.every((b) => b.value !== undefined);\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"expressions.js","sourceRoot":"","sources":["../../src/fcstd/expressions.ts"],"names":[],"mappings":"AAkBA,MAAM,UAAU,GAA2B;IACzC,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC;IACpB,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE;IACtB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI;IACpB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI;IAC/B,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;IACtB,qEAAqE;IACrE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;IACzB,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACtB,gBAAgB;IAChB,MAAM,CAAC,GAAG,+DAA+D,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClF,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACnB,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC9B,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC,CAAC,6BAA6B;IACzE,OAAO,KAAK,GAAG,MAAM,CAAC;AACxB,CAAC;AAYD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAkC,EAClC,KAAa,EACb,KAAa;IAEb,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CACzB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,oBAAoB;QACpC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CACjD,CAAC;IACF,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,0EAA0E;IAC1E,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACnF,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAC7F,CAAC;IACF,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;SACvF,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC;IACxE,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACjD,wCAAwC;IACxC,OAAO,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;AAC5F,CAAC;AAED,yDAAyD;AACzD,SAAS,WAAW,CAAC,CAAc;IACjC,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClD,OAAO,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,IAAI,SAAS,CAAC,CAAC;AACjE,CAAC;AAED;;;;GAIG;AACH;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,UAAkC,EAClC,IAAkB;IAElB,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACpB,6DAA6D;IAC7D,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,2FAA2F,EACvG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QAC5C,MAAM,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC;QACjC,MAAM,CAAC,GAAG,qBAAqB,CAAC,UAAU,EAAE,KAAM,EAAE,KAAM,CAAC,CAAC;QAC5D,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;IAC5C,CAAC,CAAC,CAAC;IACL,2DAA2D;IAC3D,IAAI,IAAI,EAAE,IAAI,KAAK,oBAAoB,EAAE,CAAC;QACxC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;YAC3D,MAAM,CAAC,GAAG,uBAAuB,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC;IACD,uDAAuD;IACvD,wCAAwC;IACxC,MAAM,QAAQ,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC5C,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,sFAAsF,EAAE,EAAE,CAAC,CAAC;IAC1G,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED,2DAA2D;AAC3D,SAAS,uBAAuB,CAC9B,KAAkB,EAClB,OAAe,EACf,UAAkC;IAElC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;SACvF,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,OAAO,CAAC,CAAC;IAC5E,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACjD,OAAO,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;AAC9F,CAAC;AAED,oDAAoD;AACpD,SAAS,cAAc,CAAC,CAAS;IAC/B,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACpD,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,QAAQ,CAAC,yBAAyB,CAAC,IAAI,CAAC,EAAa,CAAC;QAChE,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAaD;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAA+B;IAE/B,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,EAAE,CAAC,OAAO,KAAK,YAAY;YAAE,SAAS;QAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,sBAAsB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,QAA6B;IACvD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["/**\n * M6.2 — expression downgrade: <ExpressionEngine> → concrete values.\n *\n * Format (verified on TwoLengthsPadWithExpression.FCStd):\n * <ExpressionEngine count=\"N\">\n * <Expression path=\"Length\" expression=\"10 mm\" />\n * </ExpressionEngine>\n *\n * Scope: constant expressions only — number literals with optional unit\n * (mm/m/cm/in/deg/rad). Anything else (object references like Sketch.Constraints[3],\n * arithmetic with identifiers, functions) is NOT evaluated: the consumer must\n * treat the property as unknown and degrade (no heuristic fallback, plan §12).\n */\nimport type { FcstdProperty } from './document.js';\n\n/** Evaluated expression result in mm (angles keep their own unit): a number, or undefined when unsupported. */\nexport type ExprValue = number | undefined;\n\nconst UNIT_TO_MM: Record<string, number> = {\n mm: 1, millimeter: 1,\n cm: 10, centimeter: 10,\n m: 1000, meter: 1000,\n in: 25.4, inch: 25.4, '\"': 25.4,\n ft: 304.8, foot: 304.8,\n // angles: value kept in the expression's own unit; callers interpret\n deg: 1, degree: 1, '°': 1,\n rad: 1, radian: 1,\n};\n\n/**\n * Evaluate a constant expression. Returns undefined when the expression is\n * not a bare constant (reference/arithmetic/function) — explicit unsupported,\n * no guessing.\n *\n * @param expr raw expression string (e.g. \"10 mm\")\n * @returns the value in mm, or undefined when not a constant expression\n */\nexport function evalConstantExpression(expr: string): ExprValue {\n const s = expr.trim();\n // number [unit]\n const m = /^([-+]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][-+]?\\d+)?)\\s*([A-Za-z°\"]*)$/.exec(s);\n if (!m) return undefined;\n const value = Number(m[1]);\n if (!Number.isFinite(value)) return undefined;\n const unit = m[2]!;\n if (unit === '') return value;\n const factor = UNIT_TO_MM[unit.toLowerCase()];\n if (factor === undefined) return undefined; // unknown unit → unsupported\n return value * factor;\n}\n\n// ── P1-1 参数载体(2026-09-23):Spreadsheet 别名三跳解析 + 引用算术 ──\n//\n// 语料实测(B2 / probe-param-cells,见 2026-09-21 方案 §3.9):<Expression> 的\n// 绝对头部是 `<<Label>>.Alias`(1,377 条)与引用参与的算术(657 条),而带别名\n// 的参数单元格 84.8% 就是「=5.9mm」形态——现有 evalConstantExpression 已能算值,\n// 缺的只是三跳:<<Data>> → 按 Label 找对象 → 按 alias 找单元格 → 去掉前导 =。\n// 函数族 / cells[...] 区间仍不猜(no heuristic fallback,方案 §12)。\n\nimport type { FcstdObject } from './document.js';\n\n/**\n * 三跳解析:`<<Label>>.Alias` / `Object.Alias` → 数据源对象 → 别名单元格 → 去掉\n * 前导 `=` 后交给 evalConstantExpression。同表地址(`=B2*2` 里的 `B2`)再跳一次。\n *\n * @param docObjects 全文档对象(按 Label 或 name 匹配数据源)\n * @param label 数据源标识(`<<Label>>` 内的 Label,或裸对象名)\n * @param alias 单元格别名\n * @returns 单元格值(mm),不可解析 → undefined\n */\nexport function spreadsheetAliasValue(\n docObjects: readonly FcstdObject[],\n label: string,\n alias: string,\n): ExprValue {\n const src = docObjects.find(\n (o) => o.type === 'Spreadsheet::Sheet' &&\n (objectLabel(o) === label || o.name === label),\n );\n if (!src) return undefined;\n // cells 属性(Spreadsheet::PropertySheet)的 <Cell address alias content/> 子元素\n const cells = src.properties.get('cells') ?? [...src.properties.values()].find((p) =>\n p.children.some((c) => c.tagName === 'Cell' || c.children.some((g) => g.tagName === 'Cell')),\n );\n if (!cells) return undefined;\n const cell = [...cells.children].flatMap((c) => (c.tagName === 'Cell' ? [c] : c.children))\n .find((c) => c.tagName === 'Cell' && c.attributes['alias'] === alias);\n if (!cell) return undefined;\n const content = cell.attributes['content'] ?? '';\n // 去掉前导 =(同表地址/算术再走一次带上下文的求值,一跳深度足够语料头部)\n return evalWithDoc(content.startsWith('=') ? content.slice(1) : content, docObjects, src);\n}\n\n/** 对象的 Label 属性(FreeCAD 引用 `<<Label>>` 用的是它,不是 name)。 */\nfunction objectLabel(o: FcstdObject): string | undefined {\n const el = o.properties.get('Label')?.children[0];\n return el?.attributes['value'] ?? (el?.valueText || undefined);\n}\n\n/**\n * 带文档上下文的表达式求值:引用(`<<L>>.A` / `L.A` / 同表地址 `B2`)替换为\n * 数值后,求值仅含常数与 + - * / ( ) 的算术。任何残留标识符 / 函数 → undefined\n * (no heuristic fallback)。返回值单位跟随单元格(mm 语境,角度单元格调用方解释)。\n */\n/**\n * Evaluate a FreeCAD expression against the document objects.\n * @param expr - the expression text (without the leading `=`).\n * @param docObjects - the document objects visible to the expression.\n * @param self - the object the expression is evaluated on, if any.\n * @returns the evaluated expression value.\n */\nexport function evalWithDoc(\n expr: string,\n docObjects: readonly FcstdObject[],\n self?: FcstdObject,\n): ExprValue {\n let s = expr.trim();\n // 引用替换:<<Label>>.Alias | Label.Alias | Object.Alias(标识符.标识符)\n s = s.replace(/<<([^>]+)>>\\.([A-Za-z_][A-Za-z0-9_]*)|([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)/g,\n (whole, brLabel, brAlias, plLabel, plAlias) => {\n const label = brLabel ?? plLabel;\n const alias = brAlias ?? plAlias;\n const v = spreadsheetAliasValue(docObjects, label!, alias!);\n return v === undefined ? whole : `(${v})`;\n });\n // 同表地址(self 表内 address→alias 值,如 `B2` / `B2*2`):仅当 self 是表\n if (self?.type === 'Spreadsheet::Sheet') {\n s = s.replace(/\\b([A-Z]+[0-9]+)\\b/g, (whole, addr: string) => {\n const v = spreadsheetAddressValue(self, addr, docObjects);\n return v === undefined ? whole : `(${v})`;\n });\n }\n // 单元格值自带单位(=5.9mm)——先按「常数+单位」求值(evalConstantExpression\n // 的既有口径),再进纯算术;单位记号在算术前剥离(返回值跟随 mm 语境)。\n const constant = evalConstantExpression(s);\n if (constant !== undefined) return constant;\n s = s.replace(/\\b(mm|millimeter|cm|centimeter|m|meter|in|inch|\"|ft|foot|deg|degree|°|rad|radian)\\b/g, '');\n return evalArithmetic(s);\n}\n\n/** 同表地址取值:address → 该单元格 content(递归经 evalWithDoc,一跳深度)。 */\nfunction spreadsheetAddressValue(\n sheet: FcstdObject,\n address: string,\n docObjects: readonly FcstdObject[],\n): ExprValue {\n const cells = sheet.properties.get('cells');\n if (!cells) return undefined;\n const cell = [...cells.children].flatMap((c) => (c.tagName === 'Cell' ? [c] : c.children))\n .find((c) => c.tagName === 'Cell' && c.attributes['address'] === address);\n if (!cell) return undefined;\n const content = cell.attributes['content'] ?? '';\n return evalWithDoc(content.startsWith('=') ? content.slice(1) : content, docObjects, sheet);\n}\n\n/** 仅含数字与 + - * / ( ) 空格的算术求值;任何其它字符 → undefined。 */\nfunction evalArithmetic(s: string): ExprValue {\n if (!/^[-+*/(). 0-9eE]*$/.test(s)) return undefined;\n try {\n const v = Function(`\"use strict\"; return (${s});`)() as unknown;\n return typeof v === 'number' && Number.isFinite(v) ? v : undefined;\n } catch {\n return undefined;\n }\n}\n\n\n/** One <Expression path=... expression=...> binding with its optional constant value. */\nexport interface ExpressionBinding {\n /** property path the expression drives (e.g. \"Length\") */\n path: string;\n /** raw expression string */\n expression: string;\n /** evaluated constant; undefined when not a constant expression */\n value?: number;\n}\n\n/**\n * Extract expression bindings from a parsed ExpressionEngine property.\n * `bindable` = every expression evaluated to a constant (then the property\n * values can be overridden); otherwise the engine is only partially readable.\n *\n * @param prop the parsed <ExpressionEngine> property (undefined → no bindings)\n * @returns one binding per <Expression> child, values evaluated\n */\nexport function parseExpressionEngine(\n prop: FcstdProperty | undefined,\n): ExpressionBinding[] {\n const engineEl = prop?.children[0];\n if (!engineEl) return [];\n const out: ExpressionBinding[] = [];\n for (const el of engineEl.children) {\n if (el.tagName !== 'Expression') continue;\n const path = el.attributes['path'] ?? '';\n const expression = el.attributes['expression'] ?? '';\n out.push({ path, expression, value: evalConstantExpression(expression) });\n }\n return out;\n}\n\n/**\n * True when all bindings are constants → safe to override property values.\n * @param bindings bindings extracted by parseExpressionEngine\n * @returns true when there is at least one binding and every value is defined\n */\nexport function allConstant(bindings: ExpressionBinding[]): boolean {\n return bindings.length > 0 && bindings.every((b) => b.value !== undefined);\n}\n"]}
|