pcb-scene3d-viewer 1.1.49 → 1.2.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.
Files changed (63) hide show
  1. package/README.md +46 -21
  2. package/docs/api.md +109 -8
  3. package/docs/circuitjson.md +143 -29
  4. package/docs/model-format.md +50 -7
  5. package/docs/release-notes-v1.2.0.md +122 -0
  6. package/docs/testing.md +30 -1
  7. package/package.json +6 -3
  8. package/spec/library-scope.md +10 -2
  9. package/src/CircuitJsonCadModelAssetResolver.mjs +697 -83
  10. package/src/PcbAssemblyBoardSubstrateBuilder.mjs +43 -0
  11. package/src/PcbAssemblyGeometryBuilder.mjs +40 -18
  12. package/src/PcbAssemblyModelMeshLoader.mjs +101 -150
  13. package/src/PcbAssemblyPadMeshBuilder.mjs +22 -0
  14. package/src/PcbModelArchiveExporter.mjs +28 -112
  15. package/src/PcbModelArchiveSourceBundle.mjs +326 -0
  16. package/src/PcbScene3dAabbIndex.mjs +464 -0
  17. package/src/PcbScene3dBoardAssemblyPresentation.mjs +1 -1
  18. package/src/PcbScene3dBoardEdgeCutoutBuilder.mjs +9 -5
  19. package/src/PcbScene3dBoardMaterialPalette.mjs +29 -0
  20. package/src/PcbScene3dBoardShapeFactory.mjs +11 -118
  21. package/src/PcbScene3dBoardSolderMaskFactory.mjs +65 -39
  22. package/src/PcbScene3dCircuitJsonAdapter.mjs +151 -48
  23. package/src/PcbScene3dCircuitJsonDrillDetail.mjs +31 -0
  24. package/src/PcbScene3dCircuitJsonGeometry.mjs +184 -33
  25. package/src/PcbScene3dCircuitJsonInput.mjs +132 -0
  26. package/src/PcbScene3dCircuitJsonModelAsset.mjs +40 -0
  27. package/src/PcbScene3dController.mjs +40 -34
  28. package/src/PcbScene3dCopperFactory.mjs +36 -22
  29. package/src/PcbScene3dCopperFillAreaClipper.mjs +133 -235
  30. package/src/PcbScene3dCopperFillCoverageContext.mjs +204 -0
  31. package/src/PcbScene3dCopperFillLoopSetResolver.mjs +192 -0
  32. package/src/PcbScene3dCopperFillMeshBuilder.mjs +77 -295
  33. package/src/PcbScene3dCopperTextFactory.mjs +12 -4
  34. package/src/PcbScene3dCutoutCircleDetector.mjs +34 -17
  35. package/src/PcbScene3dCutoutGeometryFilter.mjs +104 -269
  36. package/src/PcbScene3dCutoutGridIndex.mjs +184 -0
  37. package/src/PcbScene3dDeferredModelFinalizer.mjs +52 -0
  38. package/src/PcbScene3dDescriptorSafeRecord.mjs +38 -0
  39. package/src/PcbScene3dDrillCutoutFilter.mjs +149 -143
  40. package/src/PcbScene3dDrillPathFactory.mjs +86 -16
  41. package/src/PcbScene3dDrillVoidFactory.mjs +35 -10
  42. package/src/PcbScene3dExternalModelGroupLoader.mjs +472 -31
  43. package/src/PcbScene3dExternalModels.mjs +23 -24
  44. package/src/PcbScene3dFacetedModelGroupBuilder.mjs +217 -0
  45. package/src/PcbScene3dGeometryZCompressor.mjs +4 -2
  46. package/src/PcbScene3dMaskCoveredCopperSideGroupBuilder.mjs +18 -4
  47. package/src/PcbScene3dMaskCoveredCopperSurfaceFilter.mjs +75 -10
  48. package/src/PcbScene3dModelContent.mjs +236 -0
  49. package/src/PcbScene3dModelFetchPolicy.mjs +304 -0
  50. package/src/PcbScene3dModelIdentity.mjs +106 -0
  51. package/src/PcbScene3dPlatedDrillSpecResolver.mjs +141 -0
  52. package/src/PcbScene3dPreparedPolygon.mjs +709 -0
  53. package/src/PcbScene3dPreparedPolygonSet.mjs +70 -0
  54. package/src/PcbScene3dRuntime.mjs +45 -45
  55. package/src/PcbScene3dRuntimeBoardMeshes.mjs +85 -1
  56. package/src/PcbScene3dShapeHoleGeometryCleaner.mjs +4 -2
  57. package/src/PcbScene3dShellRenderer.mjs +75 -6
  58. package/src/PcbScene3dSilkscreenCutoutContext.mjs +255 -0
  59. package/src/PcbScene3dSilkscreenFactory.mjs +87 -127
  60. package/src/PcbScene3dSilkscreenFillSeamBuilder.mjs +11 -5
  61. package/src/PcbScene3dStepLoader.mjs +11 -10
  62. package/src/PcbScene3dText.mjs +1 -1
  63. package/src/PcbScene3dTriangleVertexQueryBounds.mjs +302 -0
@@ -0,0 +1,122 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 André Fiedler
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # PCB Scene3D Viewer 1.2.0
7
+
8
+ Version 1.2.0 aligns the viewer boundary with the converged ECAD toolkit APIs.
9
+
10
+ ## API changes
11
+
12
+ - `PcbScene3dCircuitJsonAdapter.isCircuitJsonModel()` and `build()` now accept
13
+ `ecad-toolkit.document.v1` results and prepared
14
+ `CircuitJsonDocumentContext` instances in addition to CircuitJSON arrays.
15
+ - `PcbScene3dCircuitJsonAdapter.prepare()` exposes the proof-aware shared
16
+ normalization/index boundary. Predicates stay non-mutating and no longer
17
+ reject a shared-normalizable legacy row before preparation.
18
+ - `PcbScene3dController` and `PcbScene3dRuntime` automatically route canonical
19
+ document envelopes through the direct CircuitJSON path without requiring a
20
+ source-format scene builder.
21
+ - Controller preparation now consistently prefers an explicit
22
+ `sceneDescription`, then `scenePrepClient`, then canonical CircuitJSON, and
23
+ finally the legacy source builder. Adapter options and session assets survive
24
+ asynchronous preparation fallback.
25
+ - `PcbScene3dShellRenderer` accepts the same legacy, raw CircuitJSON, canonical
26
+ document, and prepared-context inputs as the controller, including
27
+ component-only `drawFauxBoard` scenes.
28
+ - Legacy hybrid arrays that carry `pcb`, `schematic`, or `bom` compatibility
29
+ fields still use the host-provided source-format builder.
30
+ - Live external model loading now matches every adapter-advertised format:
31
+ STEP/STP, WRL/VRML, STL, OBJ, GLTF/GLB, and 3MF. Canonical bytes, session
32
+ files, and explicitly enabled model URLs use one consistent runtime policy.
33
+ - Text-capable loaders accept canonical `text`, `payloadText`, and string
34
+ `data`; raw ZIP export accepts `text`, `payloadText`, `data`, `bytes`,
35
+ `payloadBytes`, files, and explicitly enabled URLs. Archive extensions now
36
+ preserve every advertised format, including 3MF.
37
+ - `modelLoaderOptions` is forwarded from `PcbScene3dController` to runtime and
38
+ archive export. Controller archive names now recognize canonical
39
+ `source.fileName` on documents and prepared contexts.
40
+ - Resolved GLTF BIN, OBJ MTL, and WRL texture companions are attached from safe
41
+ project-relative session/document assets. WRL textures never trigger an
42
+ implicit Three.js network load; local or explicitly fetched bytes are embedded
43
+ as data URIs first.
44
+ - Injected WRL loaders now receive sanitized source with an empty resource base
45
+ path. Hosts that previously relied on Three.js resolving relative textures
46
+ implicitly must supply local resources or an explicit `modelLoaderOptions`
47
+ fetch policy.
48
+ - Static `authHeaders` no longer cross the main model origin. The new
49
+ `authHeadersForUrl` callback is the explicit cross-origin authorization path.
50
+ - URL fetch scopes enforce safe defaults of 128 MiB per resource, 256 resources,
51
+ and 512 MiB aggregate across main sources and sidecars. The limits are
52
+ configurable with `maxModelBytes`, `maxModelResources`, and
53
+ `maxModelTotalBytes`.
54
+ - Raw ZIP entries now use unique pattern directories and original source
55
+ basenames. Safe relative GLTF buffers/images, OBJ resources, and WRL textures
56
+ are included beside the main source; return rows expose `bundleDirectory` and
57
+ `companionPaths`.
58
+ - Polygon-plated holes now consume the shared CircuitJSON hole primitive model.
59
+ `pad_outline` determines rotation-local copper extents, polygon pads stay
60
+ non-circular, and pill drill width/height survive as slot geometry. A 2.6 by
61
+ 0.6 mm Gerber routed slot no longer collapses to a 1 by 1 mm circular pad.
62
+ - Slot drill angles are board-space and applied exactly once; diagonal and
63
+ vertical routed slots no longer double-rotate with their outer pads. Separate
64
+ rectangular-pad and drill rotations remain independent.
65
+ - Plated-wall classification uses that same board-space drill rotation instead
66
+ of adding the outer pad rotation again. Legal rectangular and square holes
67
+ retain exact aperture width, height, and rotation through substrate and pad
68
+ geometry and assembly export.
69
+ - Every disjoint CircuitJSON board or panel contour now produces its own board
70
+ body, outline, solder-mask faces, and assembly-export substrate mesh. Panel
71
+ rows take physical precedence over their child board rows without data loss.
72
+
73
+ ## Performance and validation
74
+
75
+ - Scene adaptation uses `CircuitJsonDocumentContext` as the validation boundary
76
+ and requests only the shared `elements` index.
77
+ - Repeated builds from one prepared context reuse that index instead of
78
+ validating and indexing the model again.
79
+ - Controller routing prepares a canonical document once and passes the context
80
+ forward, eliminating duplicate full-model predicate validation.
81
+ - Descriptor-safe CircuitJSON normalization keeps legacy hidden metadata from
82
+ bypassing or breaking the immutable shared model boundary.
83
+ - CircuitJSON detection predicates validate without freezing or otherwise
84
+ mutating caller-owned arrays and unprepared document envelopes.
85
+ - `CircuitJsonCadModelAssetResolver.withModelAssetUrls()` now preserves common
86
+ document envelopes and prepared-context return shapes while deriving explicit
87
+ model URL fields from retained `model_asset` metadata.
88
+ - The adapter consumes canonical `model_asset` records directly and resolves
89
+ canonical document assets plus session assets through one descriptor-safe
90
+ alias index. Documents without model references skip asset indexing,
91
+ prepared contexts cache the canonical index across builds, and payload copies
92
+ stay lazy until a matching model is used. Resolver wrappers, hostile session
93
+ arrays, option proxies, and metadata accessors cannot execute caller accessors.
94
+ ECAD Forge no longer needs an app-side document transform or resolver wrapper.
95
+ - Canonical accessor-backed `ToolkitAsset` session payloads are materialized
96
+ lazily through the shared asset contract before descriptor-safe viewer
97
+ copying. Exact STEP and other model bytes now flow from converged project
98
+ loaders without weakening hostile-accessor rejection.
99
+ - Restored route-via, legacy layer, silkscreen/courtyard, oval, copper-pour, and
100
+ default-via fixtures now rely on structural normalization in
101
+ `circuitjson-toolkit` instead of viewer-side compatibility workarounds.
102
+ - A context-asset benchmark guards the one-index-build repeated-render path.
103
+ - Model group, STEP parse, request-cache, and archive identities prefer exact
104
+ canonical paths, source streams, and asset IDs. Same-basename files in
105
+ different directories remain distinct, identical sources are reused, and
106
+ rejected shared requests are evicted for retry.
107
+ - Asset aliases preserve exact case-sensitive paths. Case-insensitive fallback
108
+ resolves only one unique owner and refuses ambiguous case-fold collisions.
109
+ - Canonical shell BOM counts now use `CircuitJsonBomBuilder`, matching toolkit
110
+ grouping behavior instead of counting raw source-component rows.
111
+ - Relative GLTF sidecars resolve beside relative as well as absolute main model
112
+ paths. Existing local buffers are reused before any explicitly enabled fetch.
113
+ - The empty archive diagnostic is format-neutral because export is no longer
114
+ limited to STEP and WRL.
115
+
116
+ ## Dependencies
117
+
118
+ - Requires `circuitjson-toolkit ^1.1.0` and Node.js 20 or newer.
119
+ - Pins `earcut` 3.0.2 so npm deduplication cannot change deterministic triangle
120
+ ordering while the CircuitJSON dependency graph is upgraded.
121
+ - The package version advances from 1.1.50 to 1.2.0 because accepted input
122
+ shapes and direct-routing behavior changed incompatibly.
package/docs/testing.md CHANGED
@@ -11,13 +11,42 @@ The test suite covers:
11
11
  - geometry factories for board solids, pads, vias, drills, solder mask,
12
12
  copper, and silkscreen;
13
13
  - runtime camera, preset, resizing, selection, and visibility behavior;
14
- - external STEP/WRL placement and load ordering;
14
+ - external STEP/STP, WRL/VRML, STL, OBJ, GLTF/GLB, and 3MF live loading;
15
15
  - model ZIP archive export;
16
16
  - optional shell renderer and CSS contract;
17
17
  - worker-client request routing.
18
+ - canonical document/context/array parity and prepared-index reuse.
19
+ - direct canonical `model_asset`, document-asset, and session-asset resolution.
20
+ - descriptor-safe asset alias and resolved-metadata handling.
21
+ - lazy, context-cached canonical asset indexing and exact documentation samples.
22
+ - path-exact model/cache/archive identities and same-basename collision cases.
23
+ - local-only companion attachment, WRL texture network blocking, explicit URL
24
+ fetch/cache retries, and project-relative GLTF sidecars.
25
+ - same-origin static authentication, explicit per-URL headers, and per-resource,
26
+ resource-count, and aggregate fetch limits shared with sidecars.
27
+ - raw archive parity for canonical text, bytes, data, files, URLs, and 3MF,
28
+ including source basenames and safe GLTF/OBJ/WRL companion subtrees.
29
+ - shared-normalizable legacy routing, proof-aware preparation, exact-case asset
30
+ selection, ambiguity-safe folded lookup, and faux-board shell parity.
31
+ - canonical polygon-plated pill slots, including rotation-local pad extents,
32
+ independent outer/drill angles, and 45/90-degree board-space regressions;
33
+ - legal rectangular and square drill apertures through adapter, substrate,
34
+ pad-local, plating, drill-void, and assembly-export boundaries;
35
+ - disjoint multi-board and multi-panel substrate, outline, solder-mask, and
36
+ export geometry, plus the real Gerber project-to-viewer path in ECAD Forge.
18
37
 
19
38
  Tests use fake scene descriptions and fake model payloads only. Do not add
20
39
  customer, vendor, or source-derived fixture identifiers.
21
40
 
22
41
  Use focused tests for behavior changes. Parser and scene-description builder
23
42
  tests belong in the format-specific toolkits, not in this viewer package.
43
+
44
+ Run performance guards with:
45
+
46
+ ```bash
47
+ npm run benchmark:exact-geometry
48
+ npm run benchmark:context-assets
49
+ ```
50
+
51
+ The context-asset benchmark verifies that unreferenced assets are never indexed
52
+ and repeated builds reuse one context-owned canonical alias index.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pcb-scene3d-viewer",
3
- "version": "1.1.49",
3
+ "version": "1.2.0",
4
4
  "description": "Reusable Three.js PCB 3D scene viewer for normalized ECAD and CircuitJSON scene descriptions",
5
5
  "keywords": [
6
6
  "pcb",
@@ -33,6 +33,7 @@
33
33
  "src",
34
34
  "docs/api.md",
35
35
  "docs/circuitjson.md",
36
+ "docs/release-notes-v1.2.0.md",
36
37
  "docs/model-format.md",
37
38
  "docs/testing.md",
38
39
  "spec",
@@ -47,13 +48,15 @@
47
48
  ],
48
49
  "scripts": {
49
50
  "test": "node --test",
51
+ "benchmark:exact-geometry": "node scripts/benchmark-exact-geometry.mjs",
52
+ "benchmark:context-assets": "node scripts/benchmark-context-model-assets.mjs",
50
53
  "format": "prettier --write .",
51
54
  "check:format": "prettier --check ."
52
55
  },
53
56
  "dependencies": {
54
57
  "@sunbox/occt-import-js": "^0.0.25",
55
- "circuitjson-toolkit": "^1.0.10",
56
- "earcut": "^3.0.2",
58
+ "circuitjson-toolkit": "^1.1.0",
59
+ "earcut": "3.0.2",
57
60
  "fflate": "^0.8.2",
58
61
  "polygon-clipping": "^0.15.7",
59
62
  "three": "^0.183.2"
@@ -8,13 +8,20 @@ scene descriptions.
8
8
  - Three.js runtime orchestration for PCB scenes.
9
9
  - Board, copper, via, drill, silkscreen, solder-mask, and fallback package mesh
10
10
  factories.
11
- - STEP, WRL, GLB, GLTF, STL, and OBJ model loading and placement.
11
+ - STEP/STP, WRL/VRML, 3MF, GLB/GLTF, STL, and OBJ model loading and placement
12
+ from canonical bytes, session files, or explicitly enabled URLs.
12
13
  - Camera presets, view compensation, selection styling, picking, and visibility
13
14
  toggles.
14
15
  - Optional DOM shell/controller helpers for hosts that want ready-made scene
15
16
  chrome.
16
17
  - ZIP export of resolved component model assets.
18
+ - Self-contained raw model bundle export with safe relative GLTF, OBJ, and WRL
19
+ companions.
17
20
  - CSS for the optional scene shell.
21
+ - Direct common CircuitJSON document, prepared context, and element-array
22
+ adaptation with shared index reuse.
23
+ - Direct canonical CAD `model_asset` and document/session asset resolution.
24
+ - Explicit origin-aware and bounded network model loading.
18
25
 
19
26
  ## Out of Scope
20
27
 
@@ -22,12 +29,13 @@ scene descriptions.
22
29
  - Building format-specific 3D scene descriptions from source documents.
23
30
  - Host application state, routing, file pickers, drag/drop handling, analytics,
24
31
  localization storage, or app navigation.
25
- - Server-side upload or network fetch behavior.
32
+ - Server-side upload behavior or host-owned URL authorization/proxy policy.
26
33
 
27
34
  ## Host Contract
28
35
 
29
36
  Hosts provide either:
30
37
 
38
+ - a common CircuitJSON `DocumentResult`, prepared context, or element array;
31
39
  - a prepared scene description through `sceneDescription`;
32
40
  - a `scenePrepClient` with `prepareScene(documentModel, sessionAssets)`; or
33
41
  - `buildScene(documentModel, { modelRegistry })` plus an optional