pcb-scene3d-viewer 1.0.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.
Files changed (65) hide show
  1. package/AGENTS.md +67 -0
  2. package/COMMERCIAL-LICENSE.md +15 -0
  3. package/CONTRIBUTING.md +14 -0
  4. package/LICENSE +19 -0
  5. package/LICENSES/AGPL-3.0-or-later.txt +235 -0
  6. package/LICENSES/CC-BY-SA-4.0.txt +170 -0
  7. package/LICENSES/LGPL-2.1-or-later.txt +176 -0
  8. package/LICENSES/LicenseRef-PolyForm-Noncommercial-1.0.0.txt +131 -0
  9. package/NOTICE.md +36 -0
  10. package/README.md +128 -0
  11. package/REUSE.toml +16 -0
  12. package/docs/api.md +148 -0
  13. package/docs/circuitjson.md +190 -0
  14. package/docs/model-format.md +117 -0
  15. package/docs/testing.md +23 -0
  16. package/package.json +65 -0
  17. package/spec/library-scope.md +36 -0
  18. package/src/PcbModelArchiveExporter.mjs +320 -0
  19. package/src/PcbScene3dArcUtils.mjs +27 -0
  20. package/src/PcbScene3dBoardAssemblyPlacement.mjs +36 -0
  21. package/src/PcbScene3dBoardAssemblyPresentation.mjs +859 -0
  22. package/src/PcbScene3dBoardEdgeCutoutBuilder.mjs +537 -0
  23. package/src/PcbScene3dBoardMaterialPalette.mjs +40 -0
  24. package/src/PcbScene3dBoardShapeFactory.mjs +895 -0
  25. package/src/PcbScene3dBoardSolderMaskFactory.mjs +613 -0
  26. package/src/PcbScene3dCameraRig.mjs +168 -0
  27. package/src/PcbScene3dCircuitJsonAdapter.mjs +545 -0
  28. package/src/PcbScene3dController.mjs +956 -0
  29. package/src/PcbScene3dCopperDetailFilter.mjs +490 -0
  30. package/src/PcbScene3dCopperFactory.mjs +559 -0
  31. package/src/PcbScene3dCopperTextFactory.mjs +534 -0
  32. package/src/PcbScene3dCutoutGeometryFilter.mjs +873 -0
  33. package/src/PcbScene3dDetailCoordinateNormalizer.mjs +65 -0
  34. package/src/PcbScene3dDrillCutoutFilter.mjs +224 -0
  35. package/src/PcbScene3dDrillPathFactory.mjs +362 -0
  36. package/src/PcbScene3dDrillVoidFactory.mjs +268 -0
  37. package/src/PcbScene3dExternalModelLoadOrder.mjs +54 -0
  38. package/src/PcbScene3dExternalModels.mjs +968 -0
  39. package/src/PcbScene3dFallbackVisibility.mjs +82 -0
  40. package/src/PcbScene3dInteractionHints.mjs +56 -0
  41. package/src/PcbScene3dMountRig.mjs +53 -0
  42. package/src/PcbScene3dOutlineBuilder.mjs +210 -0
  43. package/src/PcbScene3dPadFactory.mjs +553 -0
  44. package/src/PcbScene3dPresetState.mjs +48 -0
  45. package/src/PcbScene3dRenderGroupVisibility.mjs +134 -0
  46. package/src/PcbScene3dRuntime.mjs +996 -0
  47. package/src/PcbScene3dRuntimeBoardMeshes.mjs +99 -0
  48. package/src/PcbScene3dSelectionStyler.mjs +252 -0
  49. package/src/PcbScene3dShapePathFactory.mjs +220 -0
  50. package/src/PcbScene3dShellRenderer.mjs +131 -0
  51. package/src/PcbScene3dSilkscreenFactory.mjs +854 -0
  52. package/src/PcbScene3dSilkscreenStrokeWidthResolver.mjs +81 -0
  53. package/src/PcbScene3dStepLoader.mjs +611 -0
  54. package/src/PcbScene3dStrokeFont.mjs +671 -0
  55. package/src/PcbScene3dStrokeGeometryBuilder.mjs +322 -0
  56. package/src/PcbScene3dText.mjs +99 -0
  57. package/src/PcbScene3dTrueTypeTextFactory.mjs +885 -0
  58. package/src/PcbScene3dViaFactory.mjs +176 -0
  59. package/src/PcbScene3dViewCompensation.mjs +109 -0
  60. package/src/PcbScene3dViewScale.mjs +24 -0
  61. package/src/PcbScene3dViewportResize.mjs +35 -0
  62. package/src/PcbScene3dWorkerClient.mjs +123 -0
  63. package/src/index.mjs +1 -0
  64. package/src/scene3d.mjs +44 -0
  65. package/src/styles/scene3d.css +295 -0
@@ -0,0 +1,176 @@
1
+ import { PcbScene3dDrillPathFactory } from './PcbScene3dDrillPathFactory.mjs'
2
+
3
+ /**
4
+ * Builds annular via barrels for the interactive 3D PCB scene.
5
+ */
6
+ export class PcbScene3dViaFactory {
7
+ static #PAD_BARREL_OUTER_RADIUS_SCALE = 0.98
8
+ static #PAD_BARREL_MIN_WALL_MIL = 1.2
9
+ static #PAD_BARREL_WALL_FRACTION = 0.09
10
+
11
+ /**
12
+ * Builds the via mesh group for one scene.
13
+ * @param {any} THREE
14
+ * @param {{ diameter?: number, holeDiameter?: number, x?: number, y?: number, barrelOnly?: boolean }[]} vias
15
+ * @param {number} thicknessMil
16
+ * @param {(x: number, y: number) => { x: number, y: number }} normalizeBoardPoint
17
+ * @returns {any}
18
+ */
19
+ static buildGroup(THREE, vias, thicknessMil, normalizeBoardPoint) {
20
+ const group = new THREE.Group()
21
+ const material = new THREE.MeshStandardMaterial({
22
+ color: 0xcaa24e,
23
+ roughness: 0.48,
24
+ metalness: 0.42,
25
+ side: THREE.DoubleSide
26
+ })
27
+ const geometryCache = new Map()
28
+
29
+ ;(vias || []).forEach((via) => {
30
+ const geometry = PcbScene3dViaFactory.#resolveGeometry(
31
+ THREE,
32
+ geometryCache,
33
+ via,
34
+ thicknessMil
35
+ )
36
+ const mesh = new THREE.Mesh(geometry, material)
37
+ const point = normalizeBoardPoint(
38
+ Number(via?.x || 0),
39
+ Number(via?.y || 0)
40
+ )
41
+ mesh.position.set(point.x, point.y, 0)
42
+ if (geometry.type === 'CylinderGeometry') {
43
+ mesh.rotation.x = Math.PI / 2
44
+ }
45
+ group.add(mesh)
46
+ })
47
+
48
+ return group
49
+ }
50
+
51
+ /**
52
+ * Resolves one reusable via geometry from the via drill spec.
53
+ * @param {any} THREE
54
+ * @param {Map<string, any>} geometryCache
55
+ * @param {{ diameter?: number, holeDiameter?: number, barrelOnly?: boolean }} via
56
+ * @param {number} thicknessMil
57
+ * @returns {any}
58
+ */
59
+ static #resolveGeometry(THREE, geometryCache, via, thicknessMil) {
60
+ const outerRadius = Math.max(Number(via?.diameter || 0) / 2, 1.2)
61
+ const holeDiameter = Math.max(Number(via?.holeDiameter || 0), 0)
62
+ const depth = thicknessMil + 2
63
+ const isBarrelOnly = Boolean(via?.barrelOnly)
64
+ const cacheKey = [
65
+ isBarrelOnly ? 'barrel' : 'annulus',
66
+ outerRadius.toFixed(4),
67
+ holeDiameter.toFixed(4),
68
+ depth.toFixed(4)
69
+ ].join(':')
70
+ const cached = geometryCache.get(cacheKey)
71
+ if (cached) {
72
+ return cached
73
+ }
74
+
75
+ let geometry
76
+ if (isBarrelOnly && holeDiameter > 0) {
77
+ geometry = PcbScene3dViaFactory.#buildPadBarrelGeometry(
78
+ THREE,
79
+ holeDiameter,
80
+ depth
81
+ )
82
+ } else if (holeDiameter > 0 && holeDiameter < outerRadius * 2 - 0.001) {
83
+ const shape = new THREE.Shape()
84
+ shape.moveTo(outerRadius, 0)
85
+ shape.absarc(0, 0, outerRadius, 0, Math.PI, false)
86
+ shape.absarc(0, 0, outerRadius, Math.PI, Math.PI * 2, false)
87
+ const drillHole = PcbScene3dDrillPathFactory.buildViaHolePath(
88
+ THREE,
89
+ via
90
+ )
91
+ if (drillHole) {
92
+ shape.holes.push(drillHole)
93
+ }
94
+ geometry = new THREE.ExtrudeGeometry(shape, {
95
+ depth,
96
+ bevelEnabled: false,
97
+ curveSegments: 24,
98
+ steps: 1
99
+ })
100
+ geometry.translate?.(0, 0, -depth / 2)
101
+ } else {
102
+ geometry = new THREE.CylinderGeometry(
103
+ outerRadius,
104
+ outerRadius,
105
+ depth,
106
+ 18
107
+ )
108
+ }
109
+
110
+ geometryCache.set(cacheKey, geometry)
111
+ return geometry
112
+ }
113
+
114
+ /**
115
+ * Builds a visible copper sleeve for through-hole pads.
116
+ * @param {any} THREE
117
+ * @param {number} holeDiameter
118
+ * @param {number} depth
119
+ * @returns {any}
120
+ */
121
+ static #buildPadBarrelGeometry(THREE, holeDiameter, depth) {
122
+ const holeRadius = Math.max(Number(holeDiameter || 0) / 2, 0.8)
123
+ const outerRadius = Math.max(
124
+ holeRadius * PcbScene3dViaFactory.#PAD_BARREL_OUTER_RADIUS_SCALE,
125
+ 0.8
126
+ )
127
+ const wallThickness = Math.max(
128
+ Number(holeDiameter || 0) *
129
+ PcbScene3dViaFactory.#PAD_BARREL_WALL_FRACTION,
130
+ PcbScene3dViaFactory.#PAD_BARREL_MIN_WALL_MIL
131
+ )
132
+ const innerRadius = Math.max(outerRadius - wallThickness, 0.6)
133
+ const shape = PcbScene3dViaFactory.#buildCircleShape(THREE, outerRadius)
134
+ shape.holes.push(
135
+ PcbScene3dViaFactory.#buildCirclePath(THREE, innerRadius)
136
+ )
137
+
138
+ const geometry = new THREE.ExtrudeGeometry(shape, {
139
+ depth,
140
+ bevelEnabled: false,
141
+ curveSegments: 24,
142
+ steps: 1
143
+ })
144
+ geometry.translate?.(0, 0, -depth / 2)
145
+ return geometry
146
+ }
147
+
148
+ /**
149
+ * Builds one filled circular shape centered on the origin.
150
+ * @param {any} THREE
151
+ * @param {number} radius
152
+ * @returns {any}
153
+ */
154
+ static #buildCircleShape(THREE, radius) {
155
+ const shape = new THREE.Shape()
156
+ shape.moveTo(radius, 0)
157
+ shape.absarc(0, 0, radius, 0, Math.PI, false)
158
+ shape.absarc(0, 0, radius, Math.PI, Math.PI * 2, false)
159
+ return shape
160
+ }
161
+
162
+ /**
163
+ * Builds one circular hole path centered on the origin.
164
+ * @param {any} THREE
165
+ * @param {number} radius
166
+ * @returns {any}
167
+ */
168
+ static #buildCirclePath(THREE, radius) {
169
+ const path = new THREE.Path()
170
+ path.moveTo(radius, 0)
171
+ path.absarc(0, 0, radius, 0, Math.PI, false)
172
+ path.absarc(0, 0, radius, Math.PI, Math.PI * 2, false)
173
+ path.closePath()
174
+ return path
175
+ }
176
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Applies scene-view mirror compensation to selected 3D detail nodes.
3
+ */
4
+ export class PcbScene3dViewCompensation {
5
+ /**
6
+ * Applies the active view mirror compensation throughout one scene branch.
7
+ * @param {any} root Root group or mesh.
8
+ * @param {{ x?: number, y?: number, z?: number } | null | undefined} viewScale Active scene view scale.
9
+ * @returns {void}
10
+ */
11
+ static apply(root, viewScale) {
12
+ const pendingGroups = [root]
13
+
14
+ while (pendingGroups.length) {
15
+ const group = pendingGroups.pop()
16
+ if (!group) {
17
+ continue
18
+ }
19
+
20
+ if (group.userData?.scene3dViewCompensation) {
21
+ PcbScene3dViewCompensation.applyToGroup(group, viewScale)
22
+ }
23
+
24
+ if (Array.isArray(group.children)) {
25
+ group.children.forEach((child) => pendingGroups.push(child))
26
+ }
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Applies the active view mirror compensation to one marked object.
32
+ * @param {any} group View compensation target.
33
+ * @param {{ x?: number, y?: number, z?: number } | null | undefined} viewScale Active scene view scale.
34
+ * @returns {void}
35
+ */
36
+ static applyToGroup(group, viewScale) {
37
+ const axes = group?.userData?.scene3dViewCompensationAxes || {}
38
+ const axisSources =
39
+ group?.userData?.scene3dViewCompensationAxisSources || {}
40
+ const sourceScale = group?.userData?.scene3dSourceFrameScale || {}
41
+
42
+ group?.scale?.set?.(
43
+ PcbScene3dViewCompensation.#resolveScaleMultiplier(sourceScale?.x) *
44
+ (axes.x === false
45
+ ? 1
46
+ : PcbScene3dViewCompensation.#resolveCompensationSign(
47
+ viewScale,
48
+ 'x',
49
+ axisSources?.x
50
+ )),
51
+ PcbScene3dViewCompensation.#resolveScaleMultiplier(sourceScale?.y) *
52
+ (axes.y === false
53
+ ? 1
54
+ : PcbScene3dViewCompensation.#resolveCompensationSign(
55
+ viewScale,
56
+ 'y',
57
+ axisSources?.y
58
+ )),
59
+ PcbScene3dViewCompensation.#resolveScaleMultiplier(sourceScale?.z) *
60
+ (axes.z === false
61
+ ? 1
62
+ : PcbScene3dViewCompensation.#resolveCompensationSign(
63
+ viewScale,
64
+ 'z',
65
+ axisSources?.z
66
+ ))
67
+ )
68
+ }
69
+
70
+ /**
71
+ * Resolves a source-frame scale multiplier.
72
+ * @param {number | string | undefined} value Source scale value.
73
+ * @returns {number}
74
+ */
75
+ static #resolveScaleMultiplier(value) {
76
+ const scale = Number(value)
77
+
78
+ return Number.isFinite(scale) && scale !== 0 ? scale : 1
79
+ }
80
+
81
+ /**
82
+ * Resolves the compensation sign for one view axis.
83
+ * @param {{ x?: number, y?: number, z?: number } | null | undefined} viewScale Active scene view scale.
84
+ * @param {'x' | 'y' | 'z'} axis Target scale axis.
85
+ * @param {string | undefined} source Axis source mode.
86
+ * @returns {number}
87
+ */
88
+ static #resolveCompensationSign(viewScale, axis, source) {
89
+ if (source === 'board-mirror') {
90
+ return (
91
+ PcbScene3dViewCompensation.#resolveViewScaleSign(viewScale?.x) *
92
+ PcbScene3dViewCompensation.#resolveViewScaleSign(viewScale?.y)
93
+ )
94
+ }
95
+
96
+ return PcbScene3dViewCompensation.#resolveViewScaleSign(
97
+ viewScale?.[axis]
98
+ )
99
+ }
100
+
101
+ /**
102
+ * Converts one view scale axis into the matching mirror compensation sign.
103
+ * @param {number | string | undefined} value View scale axis.
104
+ * @returns {1 | -1}
105
+ */
106
+ static #resolveViewScaleSign(value) {
107
+ return Number(value) < 0 ? -1 : 1
108
+ }
109
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Resolves per-view scene scale transforms for PCB 3D presets.
3
+ */
4
+ export class PcbScene3dViewScale {
5
+ /**
6
+ * Resolves the scene-scale transform for one named preset.
7
+ * @param {string} preset Preset name.
8
+ * @param {{ coordinateSystem?: string } | null} [sceneDescription] Scene coordinate metadata.
9
+ * @returns {{ x: number, y: number, z: number }}
10
+ */
11
+ static resolve(preset, sceneDescription = null) {
12
+ const normalizedPreset = String(preset || 'isometric').toLowerCase()
13
+
14
+ if (sceneDescription?.coordinateSystem === 'kicad-3d-y-up') {
15
+ return { x: 1, y: 1, z: 1 }
16
+ }
17
+
18
+ return normalizedPreset === 'top'
19
+ ? { x: 1, y: -1, z: 1 }
20
+ : normalizedPreset === 'bottom'
21
+ ? { x: -1, y: 1, z: 1 }
22
+ : { x: 1, y: -1, z: 1 }
23
+ }
24
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Viewport sizing helpers for the 3D runtime.
3
+ */
4
+ export class PcbScene3dViewportResize {
5
+ /**
6
+ * Observes element-level viewport size changes.
7
+ * @param {typeof globalThis} globalRef Runtime global object.
8
+ * @param {HTMLElement | null} viewportNode Viewport mount node.
9
+ * @param {() => void} onResize Resize callback.
10
+ * @returns {{ disconnect?: () => void } | null}
11
+ */
12
+ static observe(globalRef, viewportNode, onResize) {
13
+ const ResizeObserverCtor = globalRef?.ResizeObserver
14
+ if (typeof ResizeObserverCtor !== 'function' || !viewportNode) {
15
+ return null
16
+ }
17
+
18
+ const observer = new ResizeObserverCtor(() => onResize())
19
+ observer.observe(viewportNode)
20
+ return observer
21
+ }
22
+
23
+ /**
24
+ * Resolves the viewport size while guarding against zero-sized mounts.
25
+ * @param {HTMLElement | null} viewportNode Viewport mount node.
26
+ * @returns {{ width: number, height: number }}
27
+ */
28
+ static resolveSize(viewportNode) {
29
+ const rect = viewportNode?.getBoundingClientRect?.()
30
+ return {
31
+ width: Math.max(Math.round(Number(rect?.width || 960)), 320),
32
+ height: Math.max(Math.round(Number(rect?.height || 560)), 320)
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Browser-side client wrapper for the dedicated 3D scene prep worker.
3
+ */
4
+ export class PcbScene3dWorkerClient {
5
+ /** @type {(() => Worker) | null} */
6
+ #createWorker
7
+
8
+ /** @type {Worker | null} */
9
+ #worker
10
+
11
+ /** @type {number} */
12
+ #requestSequence
13
+
14
+ /** @type {Map<string, { resolve: (sceneDescription: any) => void, reject: (error: Error) => void }>} */
15
+ #pendingRequests
16
+
17
+ /**
18
+ * @param {(() => Worker) | null} workerFactory
19
+ */
20
+ constructor(workerFactory) {
21
+ this.#createWorker = workerFactory || null
22
+ this.#worker = null
23
+ this.#requestSequence = 1
24
+ this.#pendingRequests = new Map()
25
+ this.#ensureWorker()
26
+ }
27
+
28
+ /**
29
+ * Sends one 3D scene prep request through the worker.
30
+ * @param {any} documentModel
31
+ * @param {any[]} [sessionAssets]
32
+ * @returns {Promise<any>}
33
+ */
34
+ prepareScene(documentModel, sessionAssets = []) {
35
+ return new Promise((resolve, reject) => {
36
+ if (!this.#worker) {
37
+ reject(new Error('3D scene worker is unavailable.'))
38
+ return
39
+ }
40
+
41
+ const requestId = 'scene3d-request-' + this.#requestSequence++
42
+ this.#pendingRequests.set(requestId, { resolve, reject })
43
+ this.#worker.postMessage({
44
+ type: 'scene3d:prepare',
45
+ requestId,
46
+ documentModel,
47
+ sessionAssets
48
+ })
49
+ })
50
+ }
51
+
52
+ /**
53
+ * Releases the worker and rejects any pending requests.
54
+ * @returns {void}
55
+ */
56
+ dispose() {
57
+ this.#pendingRequests.forEach(({ reject }) => {
58
+ reject(new Error('3D scene worker terminated.'))
59
+ })
60
+ this.#pendingRequests.clear()
61
+ this.#worker?.terminate()
62
+ this.#worker = null
63
+ this.#createWorker = null
64
+ }
65
+
66
+ /**
67
+ * Creates the backing worker and event listeners once.
68
+ * @returns {void}
69
+ */
70
+ #ensureWorker() {
71
+ if (!this.#createWorker || this.#worker) {
72
+ return
73
+ }
74
+
75
+ this.#worker = this.#createWorker()
76
+ this.#worker.addEventListener('message', (event) => {
77
+ this.#handleWorkerMessage(event?.data || {})
78
+ })
79
+ this.#worker.addEventListener('error', (event) => {
80
+ this.#rejectAllPending(
81
+ new Error(
82
+ '3D scene worker failed: ' +
83
+ String(event?.message || 'Unknown error.')
84
+ )
85
+ )
86
+ })
87
+ }
88
+
89
+ /**
90
+ * Routes one worker payload to the matching pending request.
91
+ * @param {{ type?: string, requestId?: string, sceneDescription?: any, message?: string }} payload
92
+ * @returns {void}
93
+ */
94
+ #handleWorkerMessage(payload) {
95
+ const requestId = String(payload?.requestId || '')
96
+ const pendingRequest = this.#pendingRequests.get(requestId) || null
97
+ if (!pendingRequest) {
98
+ return
99
+ }
100
+
101
+ this.#pendingRequests.delete(requestId)
102
+ if (payload?.type === 'scene3d:success') {
103
+ pendingRequest.resolve(payload.sceneDescription || {})
104
+ return
105
+ }
106
+
107
+ pendingRequest.reject(
108
+ new Error(payload?.message || '3D scene worker failed.')
109
+ )
110
+ }
111
+
112
+ /**
113
+ * Rejects every unresolved worker request with one shared error.
114
+ * @param {Error} error
115
+ * @returns {void}
116
+ */
117
+ #rejectAllPending(error) {
118
+ this.#pendingRequests.forEach(({ reject }) => {
119
+ reject(error)
120
+ })
121
+ this.#pendingRequests.clear()
122
+ }
123
+ }
package/src/index.mjs ADDED
@@ -0,0 +1 @@
1
+ export * from './scene3d.mjs'
@@ -0,0 +1,44 @@
1
+ export { PcbModelArchiveExporter } from './PcbModelArchiveExporter.mjs'
2
+ export { PcbScene3dBoardAssemblyPlacement } from './PcbScene3dBoardAssemblyPlacement.mjs'
3
+ export { PcbScene3dBoardAssemblyPresentation } from './PcbScene3dBoardAssemblyPresentation.mjs'
4
+ export { PcbScene3dBoardEdgeCutoutBuilder } from './PcbScene3dBoardEdgeCutoutBuilder.mjs'
5
+ export { PcbScene3dBoardMaterialPalette } from './PcbScene3dBoardMaterialPalette.mjs'
6
+ export { PcbScene3dBoardShapeFactory } from './PcbScene3dBoardShapeFactory.mjs'
7
+ export { PcbScene3dBoardSolderMaskFactory } from './PcbScene3dBoardSolderMaskFactory.mjs'
8
+ export { PcbScene3dCameraRig } from './PcbScene3dCameraRig.mjs'
9
+ export { PcbScene3dController } from './PcbScene3dController.mjs'
10
+ export { PcbScene3dCircuitJsonAdapter } from './PcbScene3dCircuitJsonAdapter.mjs'
11
+ export { PcbScene3dCopperDetailFilter } from './PcbScene3dCopperDetailFilter.mjs'
12
+ export { PcbScene3dCopperFactory } from './PcbScene3dCopperFactory.mjs'
13
+ export { PcbScene3dCopperTextFactory } from './PcbScene3dCopperTextFactory.mjs'
14
+ export { PcbScene3dCutoutGeometryFilter } from './PcbScene3dCutoutGeometryFilter.mjs'
15
+ export { PcbScene3dDetailCoordinateNormalizer } from './PcbScene3dDetailCoordinateNormalizer.mjs'
16
+ export { PcbScene3dDrillCutoutFilter } from './PcbScene3dDrillCutoutFilter.mjs'
17
+ export { PcbScene3dDrillPathFactory } from './PcbScene3dDrillPathFactory.mjs'
18
+ export { PcbScene3dDrillVoidFactory } from './PcbScene3dDrillVoidFactory.mjs'
19
+ export { PcbScene3dExternalModelLoadOrder } from './PcbScene3dExternalModelLoadOrder.mjs'
20
+ export { PcbScene3dExternalModels } from './PcbScene3dExternalModels.mjs'
21
+ export { PcbScene3dFallbackVisibility } from './PcbScene3dFallbackVisibility.mjs'
22
+ export { PcbScene3dInteractionHints } from './PcbScene3dInteractionHints.mjs'
23
+ export { PcbScene3dMountRig } from './PcbScene3dMountRig.mjs'
24
+ export { PcbScene3dOutlineBuilder } from './PcbScene3dOutlineBuilder.mjs'
25
+ export { PcbScene3dPadFactory } from './PcbScene3dPadFactory.mjs'
26
+ export { PcbScene3dPresetState } from './PcbScene3dPresetState.mjs'
27
+ export { PcbScene3dRenderGroupVisibility } from './PcbScene3dRenderGroupVisibility.mjs'
28
+ export { PcbScene3dRuntime } from './PcbScene3dRuntime.mjs'
29
+ export { PcbScene3dRuntimeBoardMeshes } from './PcbScene3dRuntimeBoardMeshes.mjs'
30
+ export { PcbScene3dSelectionStyler } from './PcbScene3dSelectionStyler.mjs'
31
+ export { PcbScene3dShapePathFactory } from './PcbScene3dShapePathFactory.mjs'
32
+ export { PcbScene3dShellRenderer } from './PcbScene3dShellRenderer.mjs'
33
+ export { PcbScene3dSilkscreenFactory } from './PcbScene3dSilkscreenFactory.mjs'
34
+ export { PcbScene3dSilkscreenStrokeWidthResolver } from './PcbScene3dSilkscreenStrokeWidthResolver.mjs'
35
+ export { PcbScene3dStepLoader } from './PcbScene3dStepLoader.mjs'
36
+ export { PcbScene3dStrokeFont } from './PcbScene3dStrokeFont.mjs'
37
+ export { PcbScene3dStrokeGeometryBuilder } from './PcbScene3dStrokeGeometryBuilder.mjs'
38
+ export { PcbScene3dText } from './PcbScene3dText.mjs'
39
+ export { PcbScene3dTrueTypeTextFactory } from './PcbScene3dTrueTypeTextFactory.mjs'
40
+ export { PcbScene3dViaFactory } from './PcbScene3dViaFactory.mjs'
41
+ export { PcbScene3dViewCompensation } from './PcbScene3dViewCompensation.mjs'
42
+ export { PcbScene3dViewScale } from './PcbScene3dViewScale.mjs'
43
+ export { PcbScene3dViewportResize } from './PcbScene3dViewportResize.mjs'
44
+ export { PcbScene3dWorkerClient } from './PcbScene3dWorkerClient.mjs'