pcb-scene3d-viewer 1.2.1 → 1.2.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
@@ -24,7 +24,7 @@ browser-based ECAD tools.
24
24
 
25
25
  ## CircuitJSON 1.1 convergence
26
26
 
27
- Version 1.2.1 accepts the common document and prepared-context shapes returned
27
+ Version 1.2.2 accepts the common document and prepared-context shapes returned
28
28
  by CircuitJSON, Gerber, Altium, and KiCad Toolkit 1.1-compatible APIs. The
29
29
  adapter requests the shared `elements` index once and reuses it across repeated
30
30
  scene builds. `PcbScene3dCircuitJsonAdapter.prepare()` exposes that proof-aware
@@ -47,6 +47,13 @@ Legal rectangular and square CircuitJSON drill apertures retain their exact
47
47
  width, height, and board-space rotation through substrate, pad, and assembly
48
48
  export meshes.
49
49
 
50
+ Canonical documents retain their exact `source.format` as the scene
51
+ `sourceFormat`; raw element arrays continue to use `circuitjson`. Routed traces
52
+ and copper pours with no authored solder-mask coverage value remain covered,
53
+ while `covered_with_solder_mask: false` keeps an explicit opening exposed.
54
+ Standard vias likewise default to tented and honor `is_tented: false` as an
55
+ explicit opening.
56
+
50
57
  The live runtime loads STEP/STP, WRL/VRML, STL, OBJ, GLTF/GLB, and 3MF from
51
58
  canonical text/bytes or browser files. Referenced GLTF buffers, OBJ material
52
59
  libraries, and WRL textures are attached from matching document/session assets
@@ -123,6 +130,7 @@ const controller = new PcbScene3dController(viewportNode, document)
123
130
 
124
131
  - [API](docs/api.md)
125
132
  - [CircuitJSON usage](docs/circuitjson.md)
133
+ - [1.2.2 release notes](docs/release-notes-v1.2.2.md)
126
134
  - [1.2.1 release notes](docs/release-notes-v1.2.1.md)
127
135
  - [1.2.0 release notes](docs/release-notes-v1.2.0.md)
128
136
  - [Model format](docs/model-format.md)
package/docs/api.md CHANGED
@@ -108,6 +108,14 @@ Methods:
108
108
  and courtyard artwork as silkscreen detail. `showPcbPaste: true` renders
109
109
  direct solder-paste artwork as a separate top/bottom overlay.
110
110
 
111
+ For canonical document and prepared-context inputs, the returned scene's
112
+ `sourceFormat` is the exact canonical `source.format`. Dense element arrays,
113
+ which do not carry source metadata, retain the `circuitjson` fallback. Routed
114
+ traces and copper pours default to covered when their coverage property is
115
+ omitted; an explicit `covered_with_solder_mask: false` remains an exposed
116
+ opening. Standard vias default to tented and honor `is_tented: false` as an
117
+ explicit opening.
118
+
111
119
  `PcbScene3dController` and `PcbScene3dRuntime` call this adapter automatically
112
120
  when they receive direct CircuitJSON input. See
113
121
  [CircuitJSON usage](circuitjson.md) for supported elements, units, and examples.
@@ -14,6 +14,10 @@ KiCad, Gerber, or other format-specific scene builder.
14
14
  Prepared contexts are the fastest repeated-render path because validation and
15
15
  the adapter's `elements` index are built at most once.
16
16
 
17
+ Canonical document envelopes and prepared contexts retain `source.format` as
18
+ the normalized scene's `sourceFormat`. Dense element arrays have no canonical
19
+ source metadata and therefore use `circuitjson`.
20
+
17
21
  ## Direct Controller Input
18
22
 
19
23
  Pass any accepted CircuitJSON shape as the `documentModel`. The controller
@@ -403,13 +407,15 @@ expected capsule outline:
403
407
 
404
408
  SMT pads, plated holes, and vias honor `is_covered_with_solder_mask` and
405
409
  `covered_with_solder_mask` when present. `true` keeps the copper under solder
406
- mask, while `false` exposes the copper on the applicable board side. For vias,
407
- covered values map to tenting metadata on both sides.
410
+ mask, while `false` exposes the copper on the applicable board side. Standard
411
+ vias also honor `is_tented`; omitted via tenting defaults to covered on both
412
+ sides, while `is_tented: false` retains an explicit opening.
408
413
 
409
414
  Copper pours can use rectangular, polygon, or B-Rep geometry. Rectangular pours
410
415
  support `rotation` or `ccw_rotation`; polygon and B-Rep point coordinates are
411
- converted from millimeters to mils. `covered_with_solder_mask: true` renders the
412
- zone under solder mask, while false or omitted values expose the copper:
416
+ converted from millimeters to mils. Omitted or true
417
+ `covered_with_solder_mask` renders the zone under solder mask, while an
418
+ explicit false value exposes the copper:
413
419
 
414
420
  ```js
415
421
  {
@@ -450,6 +456,11 @@ one track segment:
450
456
  }
451
457
  ```
452
458
 
459
+ Trace-level or route-entry `covered_with_solder_mask` values control the
460
+ rendered opening. Omitted values default to covered; an explicit false value
461
+ remains exposed. A route-entry value takes precedence over its trace-level
462
+ value.
463
+
453
464
  Route entries with `route_type: 'via'` produce via primitives when their
454
465
  `from_layer`, `to_layer`, or `layer` touches the top or bottom surface. Adjacent
455
466
  surface wire segments remain visible through those vias. Inner-only vias and
@@ -0,0 +1,33 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 André Fiedler
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # PCB Scene3D Viewer 1.2.2
7
+
8
+ Version 1.2.2 preserves canonical source identity and aligns solder-mask
9
+ coverage with the converged CircuitJSON contract.
10
+
11
+ ## API and behavior changes
12
+
13
+ - Canonical document and prepared-context inputs now retain their exact
14
+ `source.format` as the rendered scene's `sourceFormat`. Dense CircuitJSON
15
+ arrays continue to use the `circuitjson` fallback because they carry no
16
+ canonical source metadata.
17
+ - Routed traces and copper pours now default omitted
18
+ `covered_with_solder_mask` values to covered. Explicit false values remain
19
+ exposed through the solder mask.
20
+ - Standard vias now honor canonical `is_tented` metadata. Omitted values
21
+ default to tented, while `is_tented: false` remains exposed.
22
+ - Covered canonical copper follows the existing solder-mask material palette,
23
+ including tracks, pours, and via annuli.
24
+
25
+ Consumers that used the generic `circuitjson` marker for canonical documents
26
+ must now handle the retained source identity such as `gerber`, `altium`, or
27
+ `kicad`.
28
+
29
+ ## Dependencies and validation
30
+
31
+ - Requires `circuitjson-toolkit ^1.1.2` and Node.js 20 or newer.
32
+ - The full test suite and both owned performance benchmarks cover the canonical
33
+ source, coverage, material, exact-geometry, and prepared-context paths.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pcb-scene3d-viewer",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Reusable Three.js PCB 3D scene viewer for normalized ECAD and CircuitJSON scene descriptions",
5
5
  "keywords": [
6
6
  "pcb",
@@ -35,6 +35,7 @@
35
35
  "docs/circuitjson.md",
36
36
  "docs/release-notes-v1.2.0.md",
37
37
  "docs/release-notes-v1.2.1.md",
38
+ "docs/release-notes-v1.2.2.md",
38
39
  "docs/model-format.md",
39
40
  "docs/testing.md",
40
41
  "spec",
@@ -56,7 +57,7 @@
56
57
  },
57
58
  "dependencies": {
58
59
  "@sunbox/occt-import-js": "^0.0.28",
59
- "circuitjson-toolkit": "^1.1.0",
60
+ "circuitjson-toolkit": "^1.1.2",
60
61
  "earcut": "3.0.2",
61
62
  "fflate": "^0.8.2",
62
63
  "polygon-clipping": "^0.15.7",
@@ -131,7 +131,7 @@ export class PcbScene3dCircuitJsonAdapter {
131
131
  )
132
132
 
133
133
  return {
134
- sourceFormat: 'circuitjson',
134
+ sourceFormat: context.source?.format || 'circuitjson',
135
135
  coordinateSystem: 'circuitjson-mm',
136
136
  board,
137
137
  components,
@@ -257,7 +257,12 @@ export class PcbScene3dCircuitJsonCopperPourBuilder {
257
257
  */
258
258
  static #isCoveredWithMask(pour) {
259
259
  const value = pour?.covered_with_solder_mask
260
- return value === true || String(value).toLowerCase() === 'true'
260
+ if (typeof value === 'boolean') return value
261
+ if (value === undefined || value === null || value === '') return true
262
+
263
+ const text = String(value).trim().toLowerCase()
264
+ if (text === 'false') return false
265
+ return true
261
266
  }
262
267
 
263
268
  /**
@@ -85,7 +85,11 @@ export class PcbScene3dCircuitJsonTraceRouteBuilder {
85
85
  0.1524
86
86
  ),
87
87
  layerId: PcbScene3dCircuitJsonLayer.layerId(side),
88
- solderMaskOpening: true
88
+ solderMaskOpening:
89
+ PcbScene3dCircuitJsonTraceRouteBuilder.#solderMaskOpening(
90
+ entry,
91
+ trace
92
+ )
89
93
  }
90
94
  }
91
95
 
@@ -199,7 +203,12 @@ export class PcbScene3dCircuitJsonTraceRouteBuilder {
199
203
  0.1524
200
204
  ),
201
205
  layerId: PcbScene3dCircuitJsonLayer.layerId(side),
202
- solderMaskOpening: true
206
+ solderMaskOpening:
207
+ PcbScene3dCircuitJsonTraceRouteBuilder.#solderMaskOpening(
208
+ start,
209
+ end,
210
+ trace
211
+ )
203
212
  }
204
213
  }
205
214
 
@@ -303,19 +312,50 @@ export class PcbScene3dCircuitJsonTraceRouteBuilder {
303
312
  * @returns {boolean}
304
313
  */
305
314
  static #isSolderMaskCovered(via) {
306
- const value =
307
- via?.is_covered_with_solder_mask ?? via?.covered_with_solder_mask
308
- if (typeof value === 'boolean') {
309
- return value
315
+ if (typeof via?.is_tented === 'boolean') {
316
+ return via.is_tented
310
317
  }
311
-
312
318
  return (
313
- String(value || '')
314
- .trim()
315
- .toLowerCase() === 'true'
319
+ PcbScene3dCircuitJsonTraceRouteBuilder.#solderMaskCoveredValue(
320
+ via
321
+ ) ?? true
316
322
  )
317
323
  }
318
324
 
325
+ /**
326
+ * Resolves whether trace-like copper has an explicitly authored opening.
327
+ * @param {...object} elements Route entries followed by their trace.
328
+ * @returns {boolean}
329
+ */
330
+ static #solderMaskOpening(...elements) {
331
+ for (const element of elements) {
332
+ const covered =
333
+ PcbScene3dCircuitJsonTraceRouteBuilder.#solderMaskCoveredValue(
334
+ element
335
+ )
336
+ if (covered !== null) return !covered
337
+ }
338
+ return false
339
+ }
340
+
341
+ /**
342
+ * Reads one explicit solder-mask coverage value.
343
+ * @param {object} element CircuitJSON copper element.
344
+ * @returns {boolean | null}
345
+ */
346
+ static #solderMaskCoveredValue(element) {
347
+ const value =
348
+ element?.is_covered_with_solder_mask ??
349
+ element?.covered_with_solder_mask
350
+ if (typeof value === 'boolean') return value
351
+ if (value === undefined || value === null || value === '') return null
352
+
353
+ const text = String(value).trim().toLowerCase()
354
+ if (text === 'true') return true
355
+ if (text === 'false') return false
356
+ return null
357
+ }
358
+
319
359
  /**
320
360
  * Converts a route point from millimeters to mils.
321
361
  * @param {object | undefined} point Source point.