pcb-scene3d-viewer 1.3.1 → 1.3.2

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/README.md CHANGED
@@ -34,6 +34,14 @@ annulus, mixed top/bottom states remain independent, and fully open Gerber vias
34
34
  retain the exposed-copper path. See the
35
35
  [1.3.1 release notes](docs/release-notes-v1.3.1.md).
36
36
 
37
+ Version 1.3.2 applies render-group and component visibility through a
38
+ self-adjusting dependency graph, so persistent runtimes repair only the stage
39
+ affected by a known toggle or structural revision. It also keeps tented copper
40
+ below mask surfaces, avoids duplicate pad barrels for filtered vias, and
41
+ short-circuits non-subdividing copper-fill clipping without changing its
42
+ observable geometry. See the
43
+ [1.3.2 release notes](docs/release-notes-v1.3.2.md).
44
+
37
45
  ## CircuitJSON 1.1 convergence
38
46
 
39
47
  Version 1.2.2 accepts the common document and prepared-context shapes returned
@@ -142,6 +150,7 @@ const controller = new PcbScene3dController(viewportNode, document)
142
150
 
143
151
  - [API](docs/api.md)
144
152
  - [CircuitJSON usage](docs/circuitjson.md)
153
+ - [1.3.2 release notes](docs/release-notes-v1.3.2.md)
145
154
  - [1.3.1 release notes](docs/release-notes-v1.3.1.md)
146
155
  - [1.2.2 release notes](docs/release-notes-v1.2.2.md)
147
156
  - [1.3.0 release notes](docs/release-notes-v1.3.0.md)
package/docs/api.md CHANGED
@@ -165,10 +165,26 @@ traversal, absolute paths, and URL schemes are never attached implicitly.
165
165
 
166
166
  ## Runtime
167
167
 
168
+ ### `PcbScene3dVisibilityGraph`
169
+
170
+ Applies render-group visibility and per-component visibility as two ordered
171
+ self-adjusting computations. `apply(state, changedPaths)` starts at readers of
172
+ the supplied toggle or revision roots, reuses unaffected effects, and returns
173
+ the stage result map with each stage's `recomputed` flag. A null change set is
174
+ conservative and repairs both stages. `clear()` reclaims stored traces.
175
+
176
+ The public root and `/scene3d` entrypoint also re-export the canonical
177
+ `SelfAdjustingComputation` identity from `circuitjson-toolkit`.
178
+
168
179
  ### `new PcbScene3dRuntime(viewportNode, sceneDescription, hooks?)`
169
180
 
170
181
  Creates the Three.js scene in a browser viewport.
171
182
 
183
+ The persistent runtime routes toggle, selection, hidden-component, and model
184
+ topology changes through `PcbScene3dVisibilityGraph`. Changes within mutable
185
+ maps and sets use explicit structural revision roots so identity-stable
186
+ containers cannot cause stale visibility reuse.
187
+
172
188
  Hooks:
173
189
 
174
190
  - `setDiagnostics(messages)`: receives user-facing diagnostics.
@@ -0,0 +1,39 @@
1
+ # pcb-scene3d-viewer 1.3.2
2
+
3
+ Version 1.3.2 adds persistent self-adjusting visibility propagation and fixes
4
+ several general copper, via, and solder-mask interactions. The changes derive
5
+ from scene topology and authored surface state rather than source filenames or
6
+ project-specific rules.
7
+
8
+ ## Incremental visibility
9
+
10
+ - `PcbScene3dVisibilityGraph` applies render-group visibility and component
11
+ visibility as two ordered computations backed by the canonical
12
+ `SelfAdjustingComputation` runtime from `circuitjson-toolkit` 1.4.1.
13
+ - Known toggle paths repair only render-group effects. Selection,
14
+ hidden-component, and identity-stable map/set mutations advance an explicit
15
+ component revision and repair only component effects.
16
+ - Unknown topology changes conservatively repair both stages, and runtime
17
+ disposal explicitly reclaims retained traces and reverse-reader edges.
18
+ - The package root and `/scene3d` entrypoint export both the canonical runtime
19
+ identity and the visibility graph.
20
+
21
+ ## Copper and mask correctness
22
+
23
+ - Tented via faces no longer carry exposed copper geometry through the rendered
24
+ solder-mask surface. Fully open vias keep their full span, one-sided tenting
25
+ retains only the exposed half, and fully tented vias retain mask treatment
26
+ without a visible face-spanning copper annulus.
27
+ - Filtered tented vias still participate in plated-hole identity checks, so a
28
+ matching through-hole pad cannot create a duplicate copper barrel.
29
+ - Copper-fill clipping with partial subdivision disabled returns as soon as an
30
+ uncovered sample proves that a triangle must be kept, while fully sampled
31
+ triangles still use exact boundary crossing checks.
32
+
33
+ ## Verification
34
+
35
+ - Tests cover stage-level recomputation, conservative invalidation,
36
+ from-scratch visibility consistency, one- and two-sided tenting, duplicate
37
+ barrel prevention, and non-subdividing fill overlap behavior.
38
+ - Release gates include the complete package suite, exact-geometry and context
39
+ asset benchmarks, formatting, and npm package dry run.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pcb-scene3d-viewer",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "Reusable Three.js PCB 3D scene viewer for normalized ECAD and CircuitJSON scene descriptions",
5
5
  "keywords": [
6
6
  "pcb",
@@ -38,6 +38,7 @@
38
38
  "docs/release-notes-v1.2.2.md",
39
39
  "docs/release-notes-v1.3.0.md",
40
40
  "docs/release-notes-v1.3.1.md",
41
+ "docs/release-notes-v1.3.2.md",
41
42
  "docs/model-format.md",
42
43
  "docs/testing.md",
43
44
  "spec",
@@ -59,7 +60,7 @@
59
60
  },
60
61
  "dependencies": {
61
62
  "@sunbox/occt-import-js": "^0.0.28",
62
- "circuitjson-toolkit": "^1.2.0",
63
+ "circuitjson-toolkit": "^1.4.1",
63
64
  "earcut": "3.0.2",
64
65
  "fflate": "^0.8.2",
65
66
  "polygon-clipping": "^0.15.7",
@@ -12,6 +12,8 @@ scene descriptions.
12
12
  from canonical bytes, session files, or explicitly enabled URLs.
13
13
  - Camera presets, view compensation, selection styling, picking, and visibility
14
14
  toggles.
15
+ - Ordered self-adjusting render-group and per-component visibility propagation
16
+ backed by the canonical `circuitjson-toolkit` runtime.
15
17
  - Optional DOM shell/controller helpers for hosts that want ready-made scene
16
18
  chrome.
17
19
  - ZIP export of resolved component model assets.
@@ -140,7 +140,8 @@ export class PcbScene3dCopperDetailFilter {
140
140
 
141
141
  return PcbScene3dCopperDetailFilter.#appendPadBarrelSpecs(
142
142
  vias,
143
- detail.pads
143
+ detail.pads,
144
+ detail.vias
144
145
  )
145
146
  }
146
147
 
@@ -322,12 +323,13 @@ export class PcbScene3dCopperDetailFilter {
322
323
  * Appends copper barrels for through-hole pads with copper annuli.
323
324
  * @param {any[]} vias Visible via list.
324
325
  * @param {any[] | undefined} pads Pad list.
326
+ * @param {any[] | undefined} sourceVias All parsed vias, including tented vias.
325
327
  * @returns {any[]}
326
328
  */
327
- static #appendPadBarrelSpecs(vias, pads) {
329
+ static #appendPadBarrelSpecs(vias, pads, sourceVias) {
328
330
  const output = [...(vias || [])]
329
331
  const seen = new Set(
330
- output.map((via) =>
332
+ [...(sourceVias || []), ...output].map((via) =>
331
333
  PcbScene3dCopperDetailFilter.#platedHoleKey(via)
332
334
  )
333
335
  )
@@ -127,10 +127,17 @@ export class PcbScene3dCopperFillAreaClipper {
127
127
  options
128
128
  )
129
129
  const coverage =
130
- PcbScene3dCopperFillAreaClipper.#resolveTriangleCoverage(
131
- triangle,
132
- areas
130
+ PcbScene3dCopperFillAreaClipper.#shouldSubdividePartialTriangles(
131
+ options
133
132
  )
133
+ ? PcbScene3dCopperFillAreaClipper.#resolveTriangleCoverage(
134
+ triangle,
135
+ areas
136
+ )
137
+ : PcbScene3dCopperFillAreaClipper.#resolveNonSubdividingCoverage(
138
+ triangle,
139
+ areas
140
+ )
134
141
 
135
142
  if (coverage === 'none') {
136
143
  PcbScene3dCopperFillAreaClipper.#appendTriangle(positions, triangle)
@@ -194,6 +201,39 @@ export class PcbScene3dCopperFillAreaClipper {
194
201
  return options?.subdividePartialTriangles !== false
195
202
  }
196
203
 
204
+ /**
205
+ * Resolves coverage when partial triangles are kept without subdivision.
206
+ * Partial and uncovered triangles have the same observable keep result in
207
+ * this mode, so the first uncovered sample can return without querying area
208
+ * boundaries. Fully sampled triangles still require the exact boundary
209
+ * predicate to distinguish complete coverage from a crossing.
210
+ * @param {{ x: number, y: number }[]} triangle Triangle points.
211
+ * @param {object[]} areas Filled copper areas.
212
+ * @returns {'none' | 'full'}
213
+ */
214
+ static #resolveNonSubdividingCoverage(triangle, areas) {
215
+ const samples = [
216
+ triangle[0],
217
+ triangle[1],
218
+ triangle[2],
219
+ PcbScene3dCopperFillAreaClipper.#centroid(triangle)
220
+ ]
221
+ for (const point of samples) {
222
+ if (
223
+ !PcbScene3dCopperFillAreaClipper.#isPointInAnyArea(point, areas)
224
+ ) {
225
+ return 'none'
226
+ }
227
+ }
228
+
229
+ return PcbScene3dCopperFillAreaClipper.#triangleCrossesAnyBoundary(
230
+ triangle,
231
+ areas
232
+ )
233
+ ? 'none'
234
+ : 'full'
235
+ }
236
+
197
237
  /**
198
238
  * Resolves whether a triangle is outside, inside, or crossing filled areas.
199
239
  * @param {{ x: number, y: number }[]} triangle Triangle points.
@@ -18,7 +18,6 @@ import { PcbScene3dFallbackVisibility } from './PcbScene3dFallbackVisibility.mjs
18
18
  import { PcbScene3dInteractionHints } from './PcbScene3dInteractionHints.mjs'
19
19
  import { PcbScene3dModelSearchPlacement } from './PcbScene3dModelSearchPlacement.mjs'
20
20
  import { PcbScene3dPresetState } from './PcbScene3dPresetState.mjs'
21
- import { PcbScene3dRenderGroupVisibility } from './PcbScene3dRenderGroupVisibility.mjs'
22
21
  import { PcbScene3dRenderScheduler } from './PcbScene3dRenderScheduler.mjs'
23
22
  import { PcbScene3dRuntimeBoardMeshes } from './PcbScene3dRuntimeBoardMeshes.mjs'
24
23
  import { PcbScene3dRuntimeHelpers } from './PcbScene3dRuntimeHelpers.mjs'
@@ -29,6 +28,7 @@ import { PcbScene3dSelectionStyler } from './PcbScene3dSelectionStyler.mjs'
29
28
  import { PcbScene3dStaticBodyFactory } from './PcbScene3dStaticBodyFactory.mjs'
30
29
  import { PcbScene3dViewportResize } from './PcbScene3dViewportResize.mjs'
31
30
  import { PcbScene3dViewScale } from './PcbScene3dViewScale.mjs'
31
+ import { PcbScene3dVisibilityGraph } from './PcbScene3dVisibilityGraph.mjs'
32
32
  const COPPER_HALF_MIL = PcbScene3dCopperFactory.visualHalfThicknessMil()
33
33
  const Z_MIL = {
34
34
  paste: COPPER_HALF_MIL + 0.28,
@@ -74,6 +74,7 @@ export class PcbScene3dRuntime {
74
74
  #resolveReadyPromise
75
75
  #hasSettledReady
76
76
  #renderScheduler
77
+ #visibilityGraph
77
78
  /**
78
79
  * @param {HTMLElement} viewportNode
79
80
  * @param {any} sceneDescription Scene description or CircuitJSON model.
@@ -127,6 +128,7 @@ export class PcbScene3dRuntime {
127
128
  this.#render()
128
129
  }
129
130
  })
131
+ this.#visibilityGraph = new PcbScene3dVisibilityGraph()
130
132
  this.#resolveReadyPromise = null
131
133
  this.#readyPromise = new Promise((resolve) => {
132
134
  this.#resolveReadyPromise = resolve
@@ -165,7 +167,7 @@ export class PcbScene3dRuntime {
165
167
  return
166
168
  }
167
169
  this.#toggles[toggleName] = enabled
168
- this.#applyToggleVisibility()
170
+ this.#applyToggleVisibility([['toggles', toggleName]])
169
171
  this.#render()
170
172
  }
171
173
  /** @param {string} designator */
@@ -183,7 +185,7 @@ export class PcbScene3dRuntime {
183
185
  ) {
184
186
  return
185
187
  }
186
- this.#applyToggleVisibility()
188
+ this.#applyToggleVisibility([['componentRevision']])
187
189
  this.#updateSelectionMarker()
188
190
  this.#render()
189
191
  }
@@ -243,6 +245,7 @@ export class PcbScene3dRuntime {
243
245
  this.#selectionRoots.clear()
244
246
  this.#hiddenComponentDesignators.clear()
245
247
  this.#componentAdjustmentRegistry.clear()
248
+ this.#visibilityGraph.clear()
246
249
  this.#fallbackBodyRoots.clear()
247
250
  this.#loadedExternalModelDesignators.clear()
248
251
  this.#modelSearchExternalModelRoots.clear()
@@ -771,28 +774,24 @@ export class PcbScene3dRuntime {
771
774
  this.#render()
772
775
  }
773
776
 
774
- /** @returns {void} */
775
- #applyToggleVisibility() {
776
- PcbScene3dRenderGroupVisibility.apply({
777
- groups: this.#groups,
778
- toggles: this.#toggles,
779
- fallbackBodyRoots: this.#fallbackBodyRoots,
780
- loadedExternalModelDesignators:
781
- this.#loadedExternalModelDesignators,
782
- modelSearchExternalModelRoots: this.#modelSearchExternalModelRoots,
783
- hasLoadedBoardAssemblyModel: this.#hasLoadedBoardAssemblyModel
784
- })
785
- PcbScene3dComponentVisibility.apply({
786
- selectionRoots: this.#selectionRoots,
787
- selectedDesignator: this.#selectedDesignator,
788
- hiddenDesignators: this.#hiddenComponentDesignators,
789
- fallbackBodyRoots: this.#fallbackBodyRoots,
790
- loadedExternalModelDesignators:
791
- this.#loadedExternalModelDesignators,
792
- modelSearchExternalModelRoots: this.#modelSearchExternalModelRoots,
793
- toggles: this.#toggles,
794
- hasLoadedBoardAssemblyModel: this.#hasLoadedBoardAssemblyModel
795
- })
777
+ /** @param {PropertyKey[][] | null} [changedPaths] @returns {void} */
778
+ #applyToggleVisibility(changedPaths = null) {
779
+ this.#visibilityGraph.apply(
780
+ {
781
+ groups: this.#groups,
782
+ toggles: this.#toggles,
783
+ fallbackBodyRoots: this.#fallbackBodyRoots,
784
+ loadedExternalModelDesignators:
785
+ this.#loadedExternalModelDesignators,
786
+ modelSearchExternalModelRoots:
787
+ this.#modelSearchExternalModelRoots,
788
+ hasLoadedBoardAssemblyModel: this.#hasLoadedBoardAssemblyModel,
789
+ selectionRoots: this.#selectionRoots,
790
+ selectedDesignator: this.#selectedDesignator,
791
+ hiddenDesignators: this.#hiddenComponentDesignators
792
+ },
793
+ changedPaths
794
+ )
796
795
  }
797
796
 
798
797
  /**
@@ -949,7 +948,7 @@ export class PcbScene3dRuntime {
949
948
  SELECTION_HIGHLIGHT_COLOR
950
949
  )
951
950
  this.#selectedDesignator = normalizedDesignator
952
- this.#applyToggleVisibility()
951
+ this.#applyToggleVisibility([['componentRevision']])
953
952
  this.#updateSelectionMarker()
954
953
  this.#render()
955
954
  }
@@ -39,27 +39,30 @@ export class PcbScene3dViaFactory {
39
39
  const renderMode = PcbScene3dViaLayerSpan.renderMode(via)
40
40
  if (!renderMode) return
41
41
 
42
- const geometry = PcbScene3dViaFactory.#resolveGeometry(
43
- THREE,
44
- geometryCache,
45
- via,
46
- thicknessMil,
47
- renderMode
48
- )
49
- const mesh = new THREE.Mesh(geometry, copperMaterial)
50
42
  const point = normalizeBoardPoint(
51
43
  Number(via?.x || 0),
52
44
  Number(via?.y || 0)
53
45
  )
54
- mesh.position.set(
55
- point.x,
56
- point.y,
57
- PcbScene3dViaFactory.#centerZ(renderMode, thicknessMil)
46
+ const copperSpans = PcbScene3dViaFactory.#resolveCopperSpans(
47
+ via,
48
+ renderMode,
49
+ thicknessMil,
50
+ Boolean(options?.surfaceMaterial)
58
51
  )
59
- if (geometry.type === 'CylinderGeometry') {
60
- mesh.rotation.x = Math.PI / 2
52
+ for (const copperSpan of copperSpans) {
53
+ const geometry = PcbScene3dViaFactory.#resolveGeometry(
54
+ THREE,
55
+ geometryCache,
56
+ via,
57
+ copperSpan.depth
58
+ )
59
+ const mesh = new THREE.Mesh(geometry, copperMaterial)
60
+ mesh.position.set(point.x, point.y, copperSpan.centerZ)
61
+ if (geometry.type === 'CylinderGeometry') {
62
+ mesh.rotation.x = Math.PI / 2
63
+ }
64
+ group.add(mesh)
61
65
  }
62
- group.add(mesh)
63
66
  PcbScene3dViaFactory.#appendMaskSurfaceMeshes(
64
67
  THREE,
65
68
  group,
@@ -75,6 +78,57 @@ export class PcbScene3dViaFactory {
75
78
  return group
76
79
  }
77
80
 
81
+ /**
82
+ * Resolves visible copper spans without carrying an open-side barrel
83
+ * through a solder-mask-covered board face.
84
+ * @param {object} via Via primitive.
85
+ * @param {'through' | 'top' | 'bottom'} renderMode Via geometry mode.
86
+ * @param {number} thicknessMil Board thickness in mil.
87
+ * @param {boolean} hasSurfaceMask Whether mask surface geometry is rendered.
88
+ * @returns {{ depth: number, centerZ: number }[]}
89
+ */
90
+ static #resolveCopperSpans(via, renderMode, thicknessMil, hasSurfaceMask) {
91
+ const fullSpan = {
92
+ depth: PcbScene3dViaFactory.#geometryDepth(
93
+ renderMode,
94
+ thicknessMil
95
+ ),
96
+ centerZ: PcbScene3dViaFactory.#centerZ(renderMode, thicknessMil)
97
+ }
98
+ if (!hasSurfaceMask) {
99
+ return [fullSpan]
100
+ }
101
+
102
+ if (renderMode === 'top') {
103
+ return PcbScene3dViaFactory.#isSideTented(via, 'top')
104
+ ? []
105
+ : [fullSpan]
106
+ }
107
+ if (renderMode === 'bottom') {
108
+ return PcbScene3dViaFactory.#isSideTented(via, 'bottom')
109
+ ? []
110
+ : [fullSpan]
111
+ }
112
+
113
+ const isTopTented = PcbScene3dViaFactory.#isSideTented(via, 'top')
114
+ const isBottomTented = PcbScene3dViaFactory.#isSideTented(via, 'bottom')
115
+ if (isTopTented && isBottomTented) {
116
+ return []
117
+ }
118
+ if (!isTopTented && !isBottomTented) {
119
+ return [fullSpan]
120
+ }
121
+
122
+ const halfDepth = fullSpan.depth / 2
123
+ const centerDistance = halfDepth / 2
124
+ return [
125
+ {
126
+ depth: halfDepth,
127
+ centerZ: isTopTented ? -centerDistance : centerDistance
128
+ }
129
+ ]
130
+ }
131
+
78
132
  /**
79
133
  * Resolves the via material.
80
134
  * @param {any} THREE
@@ -222,23 +276,12 @@ export class PcbScene3dViaFactory {
222
276
  * @param {any} THREE
223
277
  * @param {Map<string, any>} geometryCache
224
278
  * @param {{ diameter?: number, holeDiameter?: number, barrelOnly?: boolean }} via
225
- * @param {number} thicknessMil
226
- * @param {'through' | 'top' | 'bottom'} renderMode Via geometry mode.
279
+ * @param {number} depth Copper span depth in mil.
227
280
  * @returns {any}
228
281
  */
229
- static #resolveGeometry(
230
- THREE,
231
- geometryCache,
232
- via,
233
- thicknessMil,
234
- renderMode
235
- ) {
282
+ static #resolveGeometry(THREE, geometryCache, via, depth) {
236
283
  const outerRadius = Math.max(Number(via?.diameter || 0) / 2, 1.2)
237
284
  const holeDiameter = Math.max(Number(via?.holeDiameter || 0), 0)
238
- const depth = PcbScene3dViaFactory.#geometryDepth(
239
- renderMode,
240
- thicknessMil
241
- )
242
285
  const isBarrelOnly = Boolean(via?.barrelOnly)
243
286
  const cacheKey = [
244
287
  isBarrelOnly ? 'barrel' : 'annulus',
@@ -0,0 +1,102 @@
1
+ import { SelfAdjustingComputation } from 'circuitjson-toolkit'
2
+ import { PcbScene3dComponentVisibility } from './PcbScene3dComponentVisibility.mjs'
3
+ import { PcbScene3dRenderGroupVisibility } from './PcbScene3dRenderGroupVisibility.mjs'
4
+
5
+ /**
6
+ * Repairs ordered PCB visibility effects from explicit mutable-state changes.
7
+ */
8
+ export class PcbScene3dVisibilityGraph {
9
+ /** @type {SelfAdjustingComputation} */
10
+ #computation
11
+
12
+ /** @type {number} */
13
+ #groupRevision
14
+
15
+ /** @type {number} */
16
+ #componentRevision
17
+
18
+ /**
19
+ * Creates an empty visibility trace graph.
20
+ */
21
+ constructor() {
22
+ this.#computation = new SelfAdjustingComputation()
23
+ this.#groupRevision = 0
24
+ this.#componentRevision = 0
25
+ }
26
+
27
+ /**
28
+ * Applies affected visibility computations in observable effect order.
29
+ * Revision paths represent mutations inside otherwise atomic maps and sets.
30
+ * A null change set conservatively advances both structural revisions.
31
+ * @param {object} state Current visibility state.
32
+ * @param {PropertyKey[][] | null} changedPaths Changed roots or null when unknown.
33
+ * @returns {Map<string, { value: any, recomputed: boolean }>} Stage results.
34
+ */
35
+ apply(state, changedPaths = null) {
36
+ const normalizedPaths = this.#advanceRevisions(changedPaths)
37
+ const input = {
38
+ ...state,
39
+ groupRevision: this.#groupRevision,
40
+ componentRevision: this.#componentRevision
41
+ }
42
+ return this.#computation.propagate(input, normalizedPaths, [
43
+ {
44
+ name: 'render-groups',
45
+ computation: (current) => {
46
+ void current.groupRevision
47
+ PcbScene3dRenderGroupVisibility.apply(current)
48
+ }
49
+ },
50
+ {
51
+ name: 'components',
52
+ computation: (current) => {
53
+ void current.componentRevision
54
+ PcbScene3dComponentVisibility.apply(current)
55
+ }
56
+ }
57
+ ])
58
+ }
59
+
60
+ /**
61
+ * Removes all stored visibility traces and reverse-reader edges.
62
+ * @returns {void}
63
+ */
64
+ clear() {
65
+ this.#computation.clear()
66
+ this.#groupRevision = 0
67
+ this.#componentRevision = 0
68
+ }
69
+
70
+ /**
71
+ * Advances revision roots named by a visibility change set.
72
+ * @param {PropertyKey[][] | null} changedPaths Changed roots or null.
73
+ * @returns {PropertyKey[][]} Conservative normalized change paths.
74
+ */
75
+ #advanceRevisions(changedPaths) {
76
+ const normalizedPaths =
77
+ changedPaths === null
78
+ ? [['groupRevision'], ['componentRevision']]
79
+ : changedPaths
80
+ if (!Array.isArray(normalizedPaths)) {
81
+ throw new TypeError(
82
+ 'Visibility change sets must be arrays of property paths.'
83
+ )
84
+ }
85
+ const revisionRoots = new Set()
86
+ normalizedPaths.forEach((path) => {
87
+ if (!Array.isArray(path)) {
88
+ throw new TypeError(
89
+ 'Visibility change-set entries must be property paths.'
90
+ )
91
+ }
92
+ revisionRoots.add(path[0])
93
+ })
94
+ if (revisionRoots.has('groupRevision')) {
95
+ this.#groupRevision += 1
96
+ }
97
+ if (revisionRoots.has('componentRevision')) {
98
+ this.#componentRevision += 1
99
+ }
100
+ return normalizedPaths
101
+ }
102
+ }
package/src/scene3d.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ export { SelfAdjustingComputation } from 'circuitjson-toolkit'
1
2
  export { PcbModelArchiveExporter } from './PcbModelArchiveExporter.mjs'
2
3
  export { PcbAssemblyBoardSubstrateBuilder } from './PcbAssemblyBoardSubstrateBuilder.mjs'
3
4
  export { PcbAssemblyExportCoordinateFrame } from './PcbAssemblyExportCoordinateFrame.mjs'
@@ -57,4 +58,5 @@ export { PcbScene3dViaFactory } from './PcbScene3dViaFactory.mjs'
57
58
  export { PcbScene3dViewCompensation } from './PcbScene3dViewCompensation.mjs'
58
59
  export { PcbScene3dViewScale } from './PcbScene3dViewScale.mjs'
59
60
  export { PcbScene3dViewportResize } from './PcbScene3dViewportResize.mjs'
61
+ export { PcbScene3dVisibilityGraph } from './PcbScene3dVisibilityGraph.mjs'
60
62
  export { PcbScene3dWorkerClient } from './PcbScene3dWorkerClient.mjs'