@faicad/faijs 0.13.1 → 0.14.1
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.map +1 -1
- package/dist/api/api-namespace.js +30 -4
- 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 +51 -10
- package/dist/api/boolean.js.map +1 -1
- package/dist/api/chamfer.d.ts.map +1 -1
- package/dist/api/chamfer.js +8 -7
- 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/compound.d.ts +2 -0
- package/dist/api/compound.d.ts.map +1 -1
- package/dist/api/compound.js +2 -0
- package/dist/api/compound.js.map +1 -1
- package/dist/api/copy.d.ts +1 -0
- package/dist/api/copy.d.ts.map +1 -1
- package/dist/api/copy.js +4 -2
- package/dist/api/copy.js.map +1 -1
- package/dist/api/edge-ref.d.ts +3 -2
- package/dist/api/edge-ref.d.ts.map +1 -1
- package/dist/api/edge-ref.js +10 -6
- 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/fai_drill.d.ts +1 -1
- package/dist/api/fai_drill.d.ts.map +1 -1
- package/dist/api/fai_drill.js +31 -18
- package/dist/api/fai_drill.js.map +1 -1
- package/dist/api/fai_extrude.d.ts +1 -1
- package/dist/api/fai_extrude.d.ts.map +1 -1
- package/dist/api/fai_extrude.js +2 -1
- package/dist/api/fai_extrude.js.map +1 -1
- package/dist/api/fai_split.d.ts +1 -1
- package/dist/api/fai_split.d.ts.map +1 -1
- package/dist/api/fai_split.js +2 -1
- package/dist/api/fai_split.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/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 -1
- package/dist/api/generated/script-face-manifest.js.map +1 -1
- package/dist/api/generated/script-face.d.ts +1 -4
- package/dist/api/generated/script-face.d.ts.map +1 -1
- package/dist/api/generated/script-face.js +2 -3
- 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 -48
- package/dist/api/generated/topology.d.ts.map +1 -1
- package/dist/api/generated/topology.js +20 -62
- 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 +7 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +19 -5
- package/dist/api/index.js.map +1 -1
- package/dist/api/internal/compat-op.d.ts +2 -0
- package/dist/api/internal/compat-op.d.ts.map +1 -1
- package/dist/api/internal/compat-op.js +10 -2
- package/dist/api/internal/compat-op.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/load.d.ts +3 -1
- package/dist/api/load.d.ts.map +1 -1
- package/dist/api/load.js +3 -1
- package/dist/api/load.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 +121 -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/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 +9 -0
- package/dist/api/surface/arg-spec.d.ts.map +1 -1
- package/dist/api/surface/arg-spec.js +49 -30
- package/dist/api/surface/arg-spec.js.map +1 -1
- package/dist/api/svgExtrude.d.ts.map +1 -1
- package/dist/api/svgExtrude.js +1 -0
- package/dist/api/svgExtrude.js.map +1 -1
- package/dist/api/text.d.ts.map +1 -1
- package/dist/api/text.js +1 -0
- package/dist/api/text.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/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 +8 -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 +6 -1
- package/dist/brep/engine/adapters/brepkit.js.map +1 -1
- package/dist/brep/engine/adapters/occt.d.ts.map +1 -1
- package/dist/brep/engine/adapters/occt.js +24 -1
- package/dist/brep/engine/adapters/occt.js.map +1 -1
- package/dist/brep/engine/primitives.d.ts +17 -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 +49 -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 +27 -1
- package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
- package/dist/browser.d.ts +0 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +2 -2
- package/dist/browser.js.map +1 -1
- package/dist/cad-runtime/admit-compat-lib.d.ts +19 -1
- package/dist/cad-runtime/admit-compat-lib.d.ts.map +1 -1
- package/dist/cad-runtime/admit-compat-lib.js +21 -1
- package/dist/cad-runtime/admit-compat-lib.js.map +1 -1
- package/dist/cad-runtime/backend-dispatch.d.ts +44 -3
- package/dist/cad-runtime/backend-dispatch.d.ts.map +1 -1
- package/dist/cad-runtime/backend-dispatch.js +46 -3
- package/dist/cad-runtime/backend-dispatch.js.map +1 -1
- package/dist/cad-runtime/browser-lib-loader.d.ts +2 -0
- package/dist/cad-runtime/browser-lib-loader.d.ts.map +1 -1
- package/dist/cad-runtime/browser-lib-loader.js +9 -1
- 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/ports.d.ts +7 -0
- package/dist/cad-runtime/ports.d.ts.map +1 -1
- package/dist/cad-runtime/ports.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 +54 -2
- package/dist/cad-runtime/runtime.d.ts.map +1 -1
- package/dist/cad-runtime/runtime.js +186 -10
- 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/fcstd/attachment.d.ts +72 -0
- package/dist/fcstd/attachment.d.ts.map +1 -0
- package/dist/fcstd/attachment.js +93 -0
- package/dist/fcstd/attachment.js.map +1 -0
- package/dist/fcstd/build-fai-zip.d.ts +11 -0
- package/dist/fcstd/build-fai-zip.d.ts.map +1 -1
- package/dist/fcstd/build-fai-zip.js +21 -8
- package/dist/fcstd/build-fai-zip.js.map +1 -1
- package/dist/fcstd/cli.d.ts +3 -0
- package/dist/fcstd/cli.d.ts.map +1 -0
- package/dist/fcstd/cli.js +37 -0
- package/dist/fcstd/cli.js.map +1 -0
- package/dist/fcstd/codegen.d.ts +23 -1
- package/dist/fcstd/codegen.d.ts.map +1 -1
- package/dist/fcstd/codegen.js +159 -48
- package/dist/fcstd/codegen.js.map +1 -1
- package/dist/fcstd/container.d.ts +16 -1
- package/dist/fcstd/container.d.ts.map +1 -1
- package/dist/fcstd/container.js +12 -1
- package/dist/fcstd/container.js.map +1 -1
- package/dist/fcstd/contour.d.ts +9 -1
- package/dist/fcstd/contour.d.ts.map +1 -1
- package/dist/fcstd/contour.js +28 -18
- package/dist/fcstd/contour.js.map +1 -1
- package/dist/fcstd/convert.d.ts +31 -2
- package/dist/fcstd/convert.d.ts.map +1 -1
- package/dist/fcstd/convert.js +73 -22
- package/dist/fcstd/convert.js.map +1 -1
- package/dist/fcstd/document.d.ts +8 -1
- package/dist/fcstd/document.d.ts.map +1 -1
- package/dist/fcstd/document.js +4 -1
- package/dist/fcstd/document.js.map +1 -1
- package/dist/fcstd/expressions.d.ts +14 -1
- package/dist/fcstd/expressions.d.ts.map +1 -1
- package/dist/fcstd/expressions.js +11 -1
- package/dist/fcstd/expressions.js.map +1 -1
- package/dist/fcstd/external-geo.d.ts +27 -2
- package/dist/fcstd/external-geo.d.ts.map +1 -1
- package/dist/fcstd/external-geo.js +23 -2
- package/dist/fcstd/external-geo.js.map +1 -1
- package/dist/fcstd/feature-translate.d.ts +85 -3
- package/dist/fcstd/feature-translate.d.ts.map +1 -1
- package/dist/fcstd/feature-translate.js +189 -9
- package/dist/fcstd/feature-translate.js.map +1 -1
- package/dist/fcstd/index.d.ts +23 -0
- package/dist/fcstd/index.d.ts.map +1 -0
- package/dist/fcstd/index.js +19 -0
- package/dist/fcstd/index.js.map +1 -0
- package/dist/fcstd/placement.d.ts +49 -6
- package/dist/fcstd/placement.d.ts.map +1 -1
- package/dist/fcstd/placement.js +46 -6
- package/dist/fcstd/placement.js.map +1 -1
- package/dist/fcstd/planegcs-backend.d.ts +16 -1
- package/dist/fcstd/planegcs-backend.d.ts.map +1 -1
- package/dist/fcstd/planegcs-backend.js +16 -1
- package/dist/fcstd/planegcs-backend.js.map +1 -1
- package/dist/fcstd/sketch-parse.d.ts +43 -0
- package/dist/fcstd/sketch-parse.d.ts.map +1 -1
- package/dist/fcstd/sketch-parse.js +26 -0
- package/dist/fcstd/sketch-parse.js.map +1 -1
- package/dist/fcstd/sketch-solver.d.ts +20 -2
- package/dist/fcstd/sketch-solver.d.ts.map +1 -1
- package/dist/fcstd/sketch-solver.js +6 -1
- package/dist/fcstd/sketch-solver.js.map +1 -1
- package/dist/fcstd/sketch-verify.d.ts +19 -1
- package/dist/fcstd/sketch-verify.d.ts.map +1 -1
- package/dist/fcstd/sketch-verify.js +14 -1
- package/dist/fcstd/sketch-verify.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 +116 -0
- package/dist/fcstd/structural-types.js.map +1 -0
- package/dist/fcstd/unpack.d.ts +21 -2
- package/dist/fcstd/unpack.d.ts.map +1 -1
- package/dist/fcstd/unpack.js +11 -2
- package/dist/fcstd/unpack.js.map +1 -1
- package/dist/fcstd-convert.d.ts +30 -0
- package/dist/fcstd-convert.d.ts.map +1 -0
- package/dist/fcstd-convert.js +27 -0
- package/dist/fcstd-convert.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lang/determinism-scanner.d.ts +62 -0
- package/dist/lang/determinism-scanner.d.ts.map +1 -0
- package/dist/lang/determinism-scanner.js +619 -0
- package/dist/lang/determinism-scanner.js.map +1 -0
- package/dist/lang/symbol-table.generated.d.ts +7 -3
- package/dist/lang/symbol-table.generated.d.ts.map +1 -1
- package/dist/lang/symbol-table.generated.js +7 -3
- package/dist/lang/symbol-table.generated.js.map +1 -1
- package/dist/mesh/index.d.ts.map +1 -1
- package/dist/mesh/index.js +3 -2
- package/dist/mesh/index.js.map +1 -1
- package/dist/node-host/cli.d.ts.map +1 -1
- package/dist/node-host/cli.js +56 -2
- package/dist/node-host/cli.js.map +1 -1
- 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/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/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/weapp.d.ts +22 -0
- package/dist/weapp.d.ts.map +1 -0
- package/dist/weapp.js +23 -0
- package/dist/weapp.js.map +1 -0
- package/package.json +18 -5
package/dist/fcstd/convert.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* B0 — THE FCStd → .fai.zip conversion pipeline (batch CLI and tests share it;
|
|
3
|
+
* `src/fcstd/cli.ts` is its only CLI). Returns a structured summary; the caller
|
|
4
|
+
* decides exit codes and output formatting (C4: batch driver must never absorb
|
|
5
|
+
* failures).
|
|
6
|
+
*
|
|
7
|
+
* 2026-09-20: the M5.4-era dev script `scripts/fcstd-to-fai-zip.ts` was a second
|
|
8
|
+
* implementation of this same pipeline (no C4 audit, no exit-code contract) and
|
|
9
|
+
* has been deleted. Whether the conversion entry is published or not, this file
|
|
10
|
+
* is the single implementation.
|
|
6
11
|
*
|
|
7
12
|
* Dispositions (plan §2, C4): translated | python-baked | preserved-only.
|
|
8
13
|
* Any other baked reason = translation gap → result.ok = false, no zip
|
|
@@ -17,23 +22,20 @@ import { classifySketch } from './sketch-verify.js';
|
|
|
17
22
|
import { resolveExternalGeometry } from './external-geo.js';
|
|
18
23
|
import { extractContours } from './contour.js';
|
|
19
24
|
import { generateModel } from './codegen.js';
|
|
20
|
-
import {
|
|
25
|
+
import { effectivePlacement } from './attachment.js';
|
|
21
26
|
import { buildFaiZip } from './build-fai-zip.js';
|
|
22
|
-
import { isOk } from '../
|
|
27
|
+
import { isOk } from '../api/result.js';
|
|
23
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 };
|
|
24
35
|
/** V2 tolerance: solver must reproduce stored geometry (single source). */
|
|
25
36
|
export const SKETCH_T1 = 1e-6;
|
|
26
37
|
/** Dispositions allowed in a conforming container (C4). */
|
|
27
38
|
export const ALLOWED_DISPOSITIONS = new Set(['translated', 'python-baked', 'preserved-only']);
|
|
28
|
-
/** Non-modeling structural/datum types: never translation gaps. They carry
|
|
29
|
-
* no feature semantics (containers, datum planes/lines/origins, groups) —
|
|
30
|
-
* their semantics are consumed by the translator (Body.Group ordering,
|
|
31
|
-
* datum-plane UpToFace anchors), not emitted as cad calls. */
|
|
32
|
-
const STRUCTURAL_TYPES = new Set([
|
|
33
|
-
'PartDesign::Body', 'App::Origin', 'App::Plane', 'App::Line',
|
|
34
|
-
'App::DocumentObjectGroup', 'App::Part', 'PartDesign::Plane', 'PartDesign::Line',
|
|
35
|
-
'PartDesign::CoordinateSystem',
|
|
36
|
-
]);
|
|
37
39
|
/**
|
|
38
40
|
* C4 final check: reclassify `baked` entries. A baked disposition is only
|
|
39
41
|
* legitimate for Python-opaque objects (python-baked), structural/datum
|
|
@@ -48,9 +50,11 @@ function auditMapping(mapping) {
|
|
|
48
50
|
if (o.reason === 'python-opaque') {
|
|
49
51
|
o.disposition = 'python-baked';
|
|
50
52
|
}
|
|
51
|
-
else if (STRUCTURAL_TYPES.has(o.type)) {
|
|
53
|
+
else if (STRUCTURAL_TYPES.has(o.type) || isFemStructural(o.type) || STRUCTURAL_TYPES_EXTENDED.has(o.type)) {
|
|
52
54
|
o.disposition = 'preserved-only';
|
|
53
|
-
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');
|
|
54
58
|
}
|
|
55
59
|
else {
|
|
56
60
|
gaps.push({ name: o.name, type: o.type, reason: o.reason ?? 'unspecified' });
|
|
@@ -59,7 +63,14 @@ function auditMapping(mapping) {
|
|
|
59
63
|
}
|
|
60
64
|
return gaps;
|
|
61
65
|
}
|
|
62
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Convert one FCStd file through the full pipeline (unpack → parse → sketch
|
|
68
|
+
* solving → codegen → container build) and return a structured summary.
|
|
69
|
+
* @param input path to the .FCStd file to convert
|
|
70
|
+
* @param opts optional knobs (see {@link ConvertOptions}); defaults keep the C4 contract
|
|
71
|
+
* @returns a summary with ok=false (and no zip, unless `keepGappedContainer`) on any pipeline failure or translation gap
|
|
72
|
+
*/
|
|
73
|
+
export async function convertFcstdFile(input, opts) {
|
|
63
74
|
const t0 = Date.now();
|
|
64
75
|
const baseName = input.replace(/^.*[/\\]/, '').replace(/\.fcstd$/i, '');
|
|
65
76
|
const fail = (error) => ({
|
|
@@ -134,13 +145,48 @@ export async function convertFcstdFile(input) {
|
|
|
134
145
|
}
|
|
135
146
|
}
|
|
136
147
|
// M4/M5: translate + codegen
|
|
148
|
+
// H3: attachment-resolved placements — an attached sketch's stored Placement
|
|
149
|
+
// is recomputed by FreeCAD from Support ∘ AttachmentOffset; resolve the chain
|
|
150
|
+
// so non-XY-plane sketches land on their support frame (fall back to the
|
|
151
|
+
// stored value when the attachment is deactivated or unresolvable).
|
|
137
152
|
const placements = new Map();
|
|
138
153
|
for (const obj of doc.value.objects) {
|
|
139
|
-
placements.set(obj.name,
|
|
154
|
+
placements.set(obj.name, effectivePlacement(obj, placements));
|
|
155
|
+
}
|
|
156
|
+
// H7: objects whose Shape is stored as a ZIP .brp member (pure-Shape
|
|
157
|
+
// carriers, e.g. Part::Feature) — collected from the archive members so the
|
|
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.
|
|
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();
|
|
175
|
+
for (const obj of doc.value.objects) {
|
|
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)
|
|
183
|
+
shapeCarriers.add(obj.name);
|
|
184
|
+
else if (shapeFile !== undefined || subShapeFile !== undefined)
|
|
185
|
+
brokenShapeAssets.add(obj.name);
|
|
140
186
|
}
|
|
141
187
|
let gen;
|
|
142
188
|
try {
|
|
143
|
-
gen = generateModel(doc.value, sketchVerdict, sketchContours, baseName, placements);
|
|
189
|
+
gen = generateModel(doc.value, sketchVerdict, sketchContours, baseName, placements, shapeCarriers, brokenShapeAssets);
|
|
144
190
|
}
|
|
145
191
|
catch (e) {
|
|
146
192
|
return fail(`codegen failed: ${e.message}`);
|
|
@@ -214,8 +260,13 @@ export async function convertFcstdFile(input) {
|
|
|
214
260
|
for (const v of sketchVerdict.values())
|
|
215
261
|
sketches[v.level.toLowerCase()]++;
|
|
216
262
|
if (gaps.length > 0) {
|
|
217
|
-
// C4: translation gaps → no container produced (exit contract: 2)
|
|
218
|
-
|
|
263
|
+
// C4: translation gaps → no container produced (exit contract: 2).
|
|
264
|
+
// `keepGappedContainer` is a diagnostics-only escape hatch (see ConvertOptions).
|
|
265
|
+
if (!opts?.keepGappedContainer) {
|
|
266
|
+
return { file: input, ok: false, gaps, counts, sketches, elapsedMs: Date.now() - t0 };
|
|
267
|
+
}
|
|
268
|
+
const gappedZip = zipSync(members, { level: 6 });
|
|
269
|
+
return { file: input, ok: false, gaps, counts, sketches, zip: gappedZip, elapsedMs: Date.now() - t0 };
|
|
219
270
|
}
|
|
220
271
|
const zip = zipSync(members, { level: 6 });
|
|
221
272
|
return { file: input, ok: true, gaps, counts, sketches, zip, elapsedMs: Date.now() - t0 };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/fcstd/convert.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * fcstd-port B0 — reusable FCStd → .fai.zip conversion (extracted from\n * scripts/fcstd-to-fai-zip.ts so the batch CLI and the dev script share one\n * pipeline). Returns a structured summary; the caller decides exit codes and\n * output formatting (C4: batch driver must never absorb failures).\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 { buildFaiZip } from './build-fai-zip.js';\nimport { isOk } from '../vendored/brepjs/core/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\nexport interface ConvertSummary {\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: no zip produced) */\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]);\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)) {\n o.disposition = 'preserved-only';\n o.reason = o.reason ?? 'structural';\n } else {\n gaps.push({ name: o.name, type: o.type, reason: o.reason ?? 'unspecified' });\n }\n }\n }\n return gaps;\n}\n\nexport async function convertFcstdFile(input: string): 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 const placements = new Map<string, Placement>();\n for (const obj of doc.value.objects) {\n placements.set(obj.name, placementOf(obj));\n }\n let gen;\n try {\n gen = generateModel(doc.value, sketchVerdict, sketchContours, baseName, placements);\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 return { file: input, ok: false, gaps, counts, sketches, 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"]}
|
package/dist/fcstd/document.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type Result } from '../
|
|
1
|
+
import { type Result } from '../api/result.js';
|
|
2
|
+
/** One parsed XML node from Document.xml: a property element (or nested value element) with its children. */
|
|
2
3
|
export interface FcstdProperty {
|
|
3
4
|
/** property name attribute ('' for non-property elements like <Geometry>) */
|
|
4
5
|
name: string;
|
|
@@ -14,6 +15,7 @@ export interface FcstdProperty {
|
|
|
14
15
|
children: FcstdProperty[];
|
|
15
16
|
attributes: Record<string, string>;
|
|
16
17
|
}
|
|
18
|
+
/** One object from <ObjectData>: name, resolved type and its property map. */
|
|
17
19
|
export interface FcstdObject {
|
|
18
20
|
/** object name, e.g. "Sketch" or "Pad" */
|
|
19
21
|
name: string;
|
|
@@ -21,6 +23,7 @@ export interface FcstdObject {
|
|
|
21
23
|
type: string;
|
|
22
24
|
properties: Map<string, FcstdProperty>;
|
|
23
25
|
}
|
|
26
|
+
/** Parsed Document.xml: objects, the <Objects> type index and document-level meta. */
|
|
24
27
|
export interface FcstdDocument {
|
|
25
28
|
objects: FcstdObject[];
|
|
26
29
|
/** from <Objects>: name → type (index only, not authoritative) */
|
|
@@ -28,6 +31,7 @@ export interface FcstdDocument {
|
|
|
28
31
|
/** document-level properties (Creator, LastModifiedDate, ...) */
|
|
29
32
|
meta: Map<string, FcstdProperty>;
|
|
30
33
|
}
|
|
34
|
+
/** Parse failure detail; currently only XML-level errors (`kind: 'xml'`). */
|
|
31
35
|
export type ParseError = {
|
|
32
36
|
kind: 'xml';
|
|
33
37
|
message: string;
|
|
@@ -36,6 +40,9 @@ export type ParseError = {
|
|
|
36
40
|
* Parse Document.xml into the object graph. Only `<ObjectData>` entries are
|
|
37
41
|
* returned as objects; `<Objects>` feeds `typeIndex`; document meta from
|
|
38
42
|
* `<Document ...>` attributes' sibling properties.
|
|
43
|
+
*
|
|
44
|
+
* @param xml raw Document.xml content
|
|
45
|
+
* @returns the parsed object graph, or an xml ParseError
|
|
39
46
|
*/
|
|
40
47
|
export declare function parseDocumentXml(xml: string): Result<FcstdDocument, ParseError>;
|
|
41
48
|
//# sourceMappingURL=document.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/fcstd/document.ts"],"names":[],"mappings":"AAQA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/fcstd/document.ts"],"names":[],"mappings":"AAQA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAExD,6GAA6G;AAC7G,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACxC;AAED,sFAAsF;AACtF,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,kEAAkE;IAClE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,iEAAiE;IACjE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAClC;AAED,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AA6C1D;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,aAAa,EAAE,UAAU,CAAC,CAuE/E"}
|
package/dist/fcstd/document.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `_Property` elements are skipped.
|
|
7
7
|
*/
|
|
8
8
|
import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
|
|
9
|
-
import { err, ok } from '../
|
|
9
|
+
import { err, ok } from '../api/result.js';
|
|
10
10
|
function attrs(el) {
|
|
11
11
|
const out = {};
|
|
12
12
|
for (let i = 0; i < el.attributes.length; i++) {
|
|
@@ -49,6 +49,9 @@ function parseProperty(propEl) {
|
|
|
49
49
|
* Parse Document.xml into the object graph. Only `<ObjectData>` entries are
|
|
50
50
|
* returned as objects; `<Objects>` feeds `typeIndex`; document meta from
|
|
51
51
|
* `<Document ...>` attributes' sibling properties.
|
|
52
|
+
*
|
|
53
|
+
* @param xml raw Document.xml content
|
|
54
|
+
* @returns the parsed object graph, or an xml ParseError
|
|
52
55
|
*/
|
|
53
56
|
export function parseDocumentXml(xml) {
|
|
54
57
|
let doc;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/fcstd/document.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * M1.2 — Document.xml → object graph.\n *\n * Contract per plan §5.5 / 前文 A §3.3: `<ObjectData>` is authoritative\n * (objects + properties); `<Objects>` is only a type index. Transient\n * `_Property` elements are skipped.\n */\nimport { DOMParser, XMLSerializer } from '@xmldom/xmldom';\nimport { err, ok, type Result } from '../vendored/brepjs/core/result.js';\n\nexport interface FcstdProperty {\n /** property name attribute ('' for non-property elements like <Geometry>) */\n name: string;\n /** e.g. \"App::PropertyString\", \"Part::PropertyPartShape\" */\n type: string;\n /** serialized element tag, e.g. \"Geometry\", \"Constrain\", \"LineSegment\" */\n tagName: string;\n /** raw nested XML content (first element child), serialized */\n valueXml?: string;\n /** for simple value properties, the text content */\n valueText?: string;\n /** named sub-elements (e.g. <Python .../>, <ExpressionEngine> children) */\n children: FcstdProperty[];\n attributes: Record<string, string>;\n}\n\nexport interface FcstdObject {\n /** object name, e.g. \"Sketch\" or \"Pad\" */\n name: string;\n /** e.g. \"Sketcher::SketchObject\", \"PartDesign::Pad\" */\n type: string;\n properties: Map<string, FcstdProperty>;\n}\n\nexport interface FcstdDocument {\n objects: FcstdObject[];\n /** from <Objects>: name → type (index only, not authoritative) */\n typeIndex: Map<string, string>;\n /** document-level properties (Creator, LastModifiedDate, ...) */\n meta: Map<string, FcstdProperty>;\n}\n\nexport type ParseError = { kind: 'xml'; message: string };\n\nfunction attrs(el: Element): Record<string, string> {\n const out: Record<string, string> = {};\n for (let i = 0; i < el.attributes.length; i++) {\n const a = el.attributes.item(i);\n if (a) out[a.name] = a.value;\n }\n return out;\n}\n\nfunction firstElementChild(el: Element): Element | undefined {\n for (let c = el.firstChild; c; c = c.nextSibling) {\n if (c.nodeType === 1) return c as Element;\n }\n return undefined;\n}\n\nfunction serialize(el: Element): string {\n // @xmldom/xmldom ships its own Node/Element types that conflict with lib: DOM;\n // the parser/serializer boundary is the only place the two universes meet.\n type SerNode = Parameters<XMLSerializer['serializeToString']>[0];\n return new XMLSerializer().serializeToString(el as unknown as SerNode);\n}\n\nfunction parseProperty(propEl: Element): FcstdProperty {\n const children: FcstdProperty[] = [];\n for (let c = propEl.firstChild; c; c = c.nextSibling) {\n if (c.nodeType === 1) {\n const child = c as Element;\n children.push(parseProperty(child));\n }\n }\n const valueEl = firstElementChild(propEl);\n return {\n name: propEl.getAttribute('name') ?? '',\n type: propEl.getAttribute('type') ?? '',\n tagName: propEl.tagName,\n attributes: attrs(propEl),\n valueXml: valueEl ? serialize(valueEl) : undefined,\n valueText: valueEl?.textContent ?? propEl.textContent ?? '',\n children,\n };\n}\n\n/**\n * Parse Document.xml into the object graph. Only `<ObjectData>` entries are\n * returned as objects; `<Objects>` feeds `typeIndex`; document meta from\n * `<Document ...>` attributes' sibling properties.\n */\nexport function parseDocumentXml(xml: string): Result<FcstdDocument, ParseError> {\n let doc: Document;\n try {\n // xmldom's Document type conflicts with lib: DOM; cast across the boundary.\n doc = new DOMParser().parseFromString(xml, 'application/xml') as unknown as Document;\n } catch (e) {\n return err({ kind: 'xml', message: `Document.xml parse failed: ${e instanceof Error ? e.message : String(e)}` });\n }\n const root = doc.documentElement;\n if (!root || root.tagName !== 'Document') {\n return err({ kind: 'xml', message: `root element is <${root?.tagName ?? 'none'}>, expected <Document>` });\n }\n\n const objects: FcstdObject[] = [];\n const typeIndex = new Map<string, string>();\n const meta = new Map<string, FcstdProperty>();\n\n for (let n = root.firstChild; n; n = n.nextSibling) {\n if (n.nodeType !== 1) continue;\n const el = n as Element;\n switch (el.tagName) {\n case 'Objects': {\n for (let c = el.firstChild; c; c = c.nextSibling) {\n if (c.nodeType !== 1) continue;\n const oe = c as Element;\n if (oe.tagName !== 'Object') continue;\n const name = oe.getAttribute('name');\n const type = oe.getAttribute('type');\n if (name && type) typeIndex.set(name, type);\n }\n break;\n }\n case 'ObjectData': {\n for (let c = el.firstChild; c; c = c.nextSibling) {\n if (c.nodeType !== 1) continue;\n const oe = c as Element;\n if (oe.tagName !== 'Object') continue;\n const name = oe.getAttribute('name') ?? '';\n // ObjectData <Object> carries only `name`; the type lives in the\n // <Objects> index (plan §3.3: Objects is the type index).\n const type = oe.getAttribute('type') ?? typeIndex.get(name) ?? '';\n const properties = new Map<string, FcstdProperty>();\n // properties live under a <Properties Count=\"N\"> wrapper element\n for (let p = oe.firstChild; p; p = p.nextSibling) {\n if (p.nodeType !== 1) continue;\n const pe = p as Element;\n if (pe.tagName === 'Properties') {\n for (let q = pe.firstChild; q; q = q.nextSibling) {\n if (q.nodeType !== 1) continue;\n const qe = q as Element;\n if (qe.tagName === '_Property') continue; // transient\n const prop = parseProperty(qe);\n if (prop.name) properties.set(prop.name, prop);\n }\n } else if (pe.tagName !== '_Property') {\n const prop = parseProperty(pe);\n if (prop.name) properties.set(prop.name, prop);\n }\n }\n objects.push({ name, type, properties });\n }\n break;\n }\n default: {\n // document meta properties (Creator, Uuid, LastModifiedDate, ...)\n const prop = parseProperty(el);\n if (prop.name) meta.set(prop.name, prop);\n }\n }\n }\n return ok({ objects, typeIndex, meta });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/fcstd/document.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * M1.2 — Document.xml → object graph.\n *\n * Contract per plan §5.5 / 前文 A §3.3: `<ObjectData>` is authoritative\n * (objects + properties); `<Objects>` is only a type index. Transient\n * `_Property` elements are skipped.\n */\nimport { DOMParser, XMLSerializer } from '@xmldom/xmldom';\nimport { err, ok, type Result } from '../api/result.js';\n\n/** One parsed XML node from Document.xml: a property element (or nested value element) with its children. */\nexport interface FcstdProperty {\n /** property name attribute ('' for non-property elements like <Geometry>) */\n name: string;\n /** e.g. \"App::PropertyString\", \"Part::PropertyPartShape\" */\n type: string;\n /** serialized element tag, e.g. \"Geometry\", \"Constrain\", \"LineSegment\" */\n tagName: string;\n /** raw nested XML content (first element child), serialized */\n valueXml?: string;\n /** for simple value properties, the text content */\n valueText?: string;\n /** named sub-elements (e.g. <Python .../>, <ExpressionEngine> children) */\n children: FcstdProperty[];\n attributes: Record<string, string>;\n}\n\n/** One object from <ObjectData>: name, resolved type and its property map. */\nexport interface FcstdObject {\n /** object name, e.g. \"Sketch\" or \"Pad\" */\n name: string;\n /** e.g. \"Sketcher::SketchObject\", \"PartDesign::Pad\" */\n type: string;\n properties: Map<string, FcstdProperty>;\n}\n\n/** Parsed Document.xml: objects, the <Objects> type index and document-level meta. */\nexport interface FcstdDocument {\n objects: FcstdObject[];\n /** from <Objects>: name → type (index only, not authoritative) */\n typeIndex: Map<string, string>;\n /** document-level properties (Creator, LastModifiedDate, ...) */\n meta: Map<string, FcstdProperty>;\n}\n\n/** Parse failure detail; currently only XML-level errors (`kind: 'xml'`). */\nexport type ParseError = { kind: 'xml'; message: string };\n\nfunction attrs(el: Element): Record<string, string> {\n const out: Record<string, string> = {};\n for (let i = 0; i < el.attributes.length; i++) {\n const a = el.attributes.item(i);\n if (a) out[a.name] = a.value;\n }\n return out;\n}\n\nfunction firstElementChild(el: Element): Element | undefined {\n for (let c = el.firstChild; c; c = c.nextSibling) {\n if (c.nodeType === 1) return c as Element;\n }\n return undefined;\n}\n\nfunction serialize(el: Element): string {\n // @xmldom/xmldom ships its own Node/Element types that conflict with lib: DOM;\n // the parser/serializer boundary is the only place the two universes meet.\n type SerNode = Parameters<XMLSerializer['serializeToString']>[0];\n return new XMLSerializer().serializeToString(el as unknown as SerNode);\n}\n\nfunction parseProperty(propEl: Element): FcstdProperty {\n const children: FcstdProperty[] = [];\n for (let c = propEl.firstChild; c; c = c.nextSibling) {\n if (c.nodeType === 1) {\n const child = c as Element;\n children.push(parseProperty(child));\n }\n }\n const valueEl = firstElementChild(propEl);\n return {\n name: propEl.getAttribute('name') ?? '',\n type: propEl.getAttribute('type') ?? '',\n tagName: propEl.tagName,\n attributes: attrs(propEl),\n valueXml: valueEl ? serialize(valueEl) : undefined,\n valueText: valueEl?.textContent ?? propEl.textContent ?? '',\n children,\n };\n}\n\n/**\n * Parse Document.xml into the object graph. Only `<ObjectData>` entries are\n * returned as objects; `<Objects>` feeds `typeIndex`; document meta from\n * `<Document ...>` attributes' sibling properties.\n *\n * @param xml raw Document.xml content\n * @returns the parsed object graph, or an xml ParseError\n */\nexport function parseDocumentXml(xml: string): Result<FcstdDocument, ParseError> {\n let doc: Document;\n try {\n // xmldom's Document type conflicts with lib: DOM; cast across the boundary.\n doc = new DOMParser().parseFromString(xml, 'application/xml') as unknown as Document;\n } catch (e) {\n return err({ kind: 'xml', message: `Document.xml parse failed: ${e instanceof Error ? e.message : String(e)}` });\n }\n const root = doc.documentElement;\n if (!root || root.tagName !== 'Document') {\n return err({ kind: 'xml', message: `root element is <${root?.tagName ?? 'none'}>, expected <Document>` });\n }\n\n const objects: FcstdObject[] = [];\n const typeIndex = new Map<string, string>();\n const meta = new Map<string, FcstdProperty>();\n\n for (let n = root.firstChild; n; n = n.nextSibling) {\n if (n.nodeType !== 1) continue;\n const el = n as Element;\n switch (el.tagName) {\n case 'Objects': {\n for (let c = el.firstChild; c; c = c.nextSibling) {\n if (c.nodeType !== 1) continue;\n const oe = c as Element;\n if (oe.tagName !== 'Object') continue;\n const name = oe.getAttribute('name');\n const type = oe.getAttribute('type');\n if (name && type) typeIndex.set(name, type);\n }\n break;\n }\n case 'ObjectData': {\n for (let c = el.firstChild; c; c = c.nextSibling) {\n if (c.nodeType !== 1) continue;\n const oe = c as Element;\n if (oe.tagName !== 'Object') continue;\n const name = oe.getAttribute('name') ?? '';\n // ObjectData <Object> carries only `name`; the type lives in the\n // <Objects> index (plan §3.3: Objects is the type index).\n const type = oe.getAttribute('type') ?? typeIndex.get(name) ?? '';\n const properties = new Map<string, FcstdProperty>();\n // properties live under a <Properties Count=\"N\"> wrapper element\n for (let p = oe.firstChild; p; p = p.nextSibling) {\n if (p.nodeType !== 1) continue;\n const pe = p as Element;\n if (pe.tagName === 'Properties') {\n for (let q = pe.firstChild; q; q = q.nextSibling) {\n if (q.nodeType !== 1) continue;\n const qe = q as Element;\n if (qe.tagName === '_Property') continue; // transient\n const prop = parseProperty(qe);\n if (prop.name) properties.set(prop.name, prop);\n }\n } else if (pe.tagName !== '_Property') {\n const prop = parseProperty(pe);\n if (prop.name) properties.set(prop.name, prop);\n }\n }\n objects.push({ name, type, properties });\n }\n break;\n }\n default: {\n // document meta properties (Creator, Uuid, LastModifiedDate, ...)\n const prop = parseProperty(el);\n if (prop.name) meta.set(prop.name, prop);\n }\n }\n }\n return ok({ objects, typeIndex, meta });\n}\n"]}
|
|
@@ -12,14 +12,20 @@
|
|
|
12
12
|
* treat the property as unknown and degrade (no heuristic fallback, plan §12).
|
|
13
13
|
*/
|
|
14
14
|
import type { FcstdProperty } from './document.js';
|
|
15
|
+
/** Evaluated expression result in mm (angles keep their own unit): a number, or undefined when unsupported. */
|
|
15
16
|
export type ExprValue = number | undefined;
|
|
16
17
|
/**
|
|
17
18
|
* Evaluate a constant expression. Returns undefined when the expression is
|
|
18
19
|
* not a bare constant (reference/arithmetic/function) — explicit unsupported,
|
|
19
20
|
* no guessing.
|
|
21
|
+
*
|
|
22
|
+
* @param expr raw expression string (e.g. "10 mm")
|
|
23
|
+
* @returns the value in mm, or undefined when not a constant expression
|
|
20
24
|
*/
|
|
21
25
|
export declare function evalConstantExpression(expr: string): ExprValue;
|
|
26
|
+
/** One <Expression path=... expression=...> binding with its optional constant value. */
|
|
22
27
|
export interface ExpressionBinding {
|
|
28
|
+
/** property path the expression drives (e.g. "Length") */
|
|
23
29
|
path: string;
|
|
24
30
|
/** raw expression string */
|
|
25
31
|
expression: string;
|
|
@@ -30,8 +36,15 @@ export interface ExpressionBinding {
|
|
|
30
36
|
* Extract expression bindings from a parsed ExpressionEngine property.
|
|
31
37
|
* `bindable` = every expression evaluated to a constant (then the property
|
|
32
38
|
* values can be overridden); otherwise the engine is only partially readable.
|
|
39
|
+
*
|
|
40
|
+
* @param prop the parsed <ExpressionEngine> property (undefined → no bindings)
|
|
41
|
+
* @returns one binding per <Expression> child, values evaluated
|
|
33
42
|
*/
|
|
34
43
|
export declare function parseExpressionEngine(prop: FcstdProperty | undefined): ExpressionBinding[];
|
|
35
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* True when all bindings are constants → safe to override property values.
|
|
46
|
+
* @param bindings bindings extracted by parseExpressionEngine
|
|
47
|
+
* @returns true when there is at least one binding and every value is defined
|
|
48
|
+
*/
|
|
36
49
|
export declare function allConstant(bindings: ExpressionBinding[]): boolean;
|
|
37
50
|
//# sourceMappingURL=expressions.d.ts.map
|
|
@@ -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,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAa3C
|
|
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;AAGD,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"}
|
|
@@ -12,6 +12,9 @@ const UNIT_TO_MM = {
|
|
|
12
12
|
* Evaluate a constant expression. Returns undefined when the expression is
|
|
13
13
|
* not a bare constant (reference/arithmetic/function) — explicit unsupported,
|
|
14
14
|
* no guessing.
|
|
15
|
+
*
|
|
16
|
+
* @param expr raw expression string (e.g. "10 mm")
|
|
17
|
+
* @returns the value in mm, or undefined when not a constant expression
|
|
15
18
|
*/
|
|
16
19
|
export function evalConstantExpression(expr) {
|
|
17
20
|
const s = expr.trim();
|
|
@@ -34,6 +37,9 @@ export function evalConstantExpression(expr) {
|
|
|
34
37
|
* Extract expression bindings from a parsed ExpressionEngine property.
|
|
35
38
|
* `bindable` = every expression evaluated to a constant (then the property
|
|
36
39
|
* values can be overridden); otherwise the engine is only partially readable.
|
|
40
|
+
*
|
|
41
|
+
* @param prop the parsed <ExpressionEngine> property (undefined → no bindings)
|
|
42
|
+
* @returns one binding per <Expression> child, values evaluated
|
|
37
43
|
*/
|
|
38
44
|
export function parseExpressionEngine(prop) {
|
|
39
45
|
const engineEl = prop?.children[0];
|
|
@@ -49,7 +55,11 @@ export function parseExpressionEngine(prop) {
|
|
|
49
55
|
}
|
|
50
56
|
return out;
|
|
51
57
|
}
|
|
52
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* True when all bindings are constants → safe to override property values.
|
|
60
|
+
* @param bindings bindings extracted by parseExpressionEngine
|
|
61
|
+
* @returns true when there is at least one binding and every value is defined
|
|
62
|
+
*/
|
|
53
63
|
export function allConstant(bindings) {
|
|
54
64
|
return bindings.length > 0 && bindings.every((b) => b.value !== undefined);
|
|
55
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expressions.js","sourceRoot":"","sources":["../../src/fcstd/expressions.ts"],"names":[],"mappings":"
|
|
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,5 +1,8 @@
|
|
|
1
1
|
import { type FcstdArchive } from './unpack.js';
|
|
2
|
-
import type { FcstdDocument, FcstdProperty } from './document.js';
|
|
2
|
+
import type { FcstdDocument, FcstdObject, FcstdProperty } from './document.js';
|
|
3
|
+
/**
|
|
4
|
+
* One resolved external-geometry link of a sketch.
|
|
5
|
+
*/
|
|
3
6
|
export interface ExternalLink {
|
|
4
7
|
/** source object name, e.g. "Chamfer002" */
|
|
5
8
|
obj: string;
|
|
@@ -8,6 +11,10 @@ export interface ExternalLink {
|
|
|
8
11
|
/** resolved sketch-local 2D polyline */
|
|
9
12
|
polyline: [number, number][];
|
|
10
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of external-geometry resolution: successfully projected links plus
|
|
16
|
+
* per-link failures (with a human-readable reason) for the caller to report.
|
|
17
|
+
*/
|
|
11
18
|
export interface ExternalGeoResult {
|
|
12
19
|
links: ExternalLink[];
|
|
13
20
|
failures: {
|
|
@@ -16,9 +23,27 @@ export interface ExternalGeoResult {
|
|
|
16
23
|
reason: string;
|
|
17
24
|
}[];
|
|
18
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* The `.brp` member holding an object's shape: the `Shape` property when
|
|
28
|
+
* present, else `SubShape` (GOTCHA, hole_puzzle corpus 2026-09-20: PartDesign
|
|
29
|
+
* features like Chamfer002/Pocket002 store their result cache in SubShape and
|
|
30
|
+
* have NO Shape property — external links pointing at them failed with
|
|
31
|
+
* "source shape not loadable" before the fallback existed).
|
|
32
|
+
*
|
|
33
|
+
* @param obj - the FCStd object to inspect.
|
|
34
|
+
* @returns the .brp member file name, or undefined when neither Shape nor SubShape carries one.
|
|
35
|
+
*/
|
|
36
|
+
export declare function shapeBrpFile(obj: FcstdObject): string | undefined;
|
|
19
37
|
/**
|
|
20
38
|
* Resolve every external link of a sketch to sketch-local 2D polylines.
|
|
21
|
-
*
|
|
39
|
+
*
|
|
40
|
+
* @param externalGeoProp - the sketch's ExternalGeometry property.
|
|
41
|
+
* @param doc - the parsed FCStd document, used to locate source objects and their shapes.
|
|
42
|
+
* @param archive - the FCStd container, used to read the source `.brp` shape files.
|
|
43
|
+
* @param sketchPlacement - the sketch's Placement property, whose inverse maps
|
|
44
|
+
* source geometry into sketch-local coordinates.
|
|
45
|
+
* @returns the resolved sketch-local polylines per link, plus a failure entry
|
|
46
|
+
* (with reason) for every link that could not be resolved.
|
|
22
47
|
*/
|
|
23
48
|
export declare function resolveExternalGeometry(externalGeoProp: FcstdProperty | undefined, doc: FcstdDocument, archive: FcstdArchive, sketchPlacement: FcstdProperty | undefined): Promise<ExternalGeoResult>;
|
|
24
49
|
//# sourceMappingURL=external-geo.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-geo.d.ts","sourceRoot":"","sources":["../../src/fcstd/external-geo.ts"],"names":[],"mappings":"AAWA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"external-geo.d.ts","sourceRoot":"","sources":["../../src/fcstd/external-geo.ts"],"names":[],"mappings":"AAWA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE/E;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAiCD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAGjE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,eAAe,EAAE,aAAa,GAAG,SAAS,EAC1C,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,aAAa,GAAG,SAAS,GACzC,OAAO,CAAC,iBAAiB,CAAC,CA4E5B"}
|
|
@@ -39,9 +39,30 @@ function placementOf(obj) {
|
|
|
39
39
|
q: [Number(a['Q0'] ?? 0), Number(a['Q1'] ?? 0), Number(a['Q2'] ?? 0), Number(a['Q3'] ?? 1)],
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* The `.brp` member holding an object's shape: the `Shape` property when
|
|
44
|
+
* present, else `SubShape` (GOTCHA, hole_puzzle corpus 2026-09-20: PartDesign
|
|
45
|
+
* features like Chamfer002/Pocket002 store their result cache in SubShape and
|
|
46
|
+
* have NO Shape property — external links pointing at them failed with
|
|
47
|
+
* "source shape not loadable" before the fallback existed).
|
|
48
|
+
*
|
|
49
|
+
* @param obj - the FCStd object to inspect.
|
|
50
|
+
* @returns the .brp member file name, or undefined when neither Shape nor SubShape carries one.
|
|
51
|
+
*/
|
|
52
|
+
export function shapeBrpFile(obj) {
|
|
53
|
+
return obj.properties.get('Shape')?.children[0]?.attributes['file']
|
|
54
|
+
?? obj.properties.get('SubShape')?.children[0]?.attributes['file'];
|
|
55
|
+
}
|
|
42
56
|
/**
|
|
43
57
|
* Resolve every external link of a sketch to sketch-local 2D polylines.
|
|
44
|
-
*
|
|
58
|
+
*
|
|
59
|
+
* @param externalGeoProp - the sketch's ExternalGeometry property.
|
|
60
|
+
* @param doc - the parsed FCStd document, used to locate source objects and their shapes.
|
|
61
|
+
* @param archive - the FCStd container, used to read the source `.brp` shape files.
|
|
62
|
+
* @param sketchPlacement - the sketch's Placement property, whose inverse maps
|
|
63
|
+
* source geometry into sketch-local coordinates.
|
|
64
|
+
* @returns the resolved sketch-local polylines per link, plus a failure entry
|
|
65
|
+
* (with reason) for every link that could not be resolved.
|
|
45
66
|
*/
|
|
46
67
|
export async function resolveExternalGeometry(externalGeoProp, doc, archive, sketchPlacement) {
|
|
47
68
|
const links = [];
|
|
@@ -57,7 +78,7 @@ export async function resolveExternalGeometry(externalGeoProp, doc, archive, ske
|
|
|
57
78
|
if (brpCache.has(objName))
|
|
58
79
|
return brpCache.get(objName);
|
|
59
80
|
const src = doc.objects.find((o) => o.name === objName);
|
|
60
|
-
const brpFile = src
|
|
81
|
+
const brpFile = src ? shapeBrpFile(src) : undefined;
|
|
61
82
|
if (!brpFile) {
|
|
62
83
|
brpCache.set(objName, undefined);
|
|
63
84
|
return undefined;
|