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 +9 -1
- package/docs/api.md +8 -0
- package/docs/circuitjson.md +15 -4
- package/docs/release-notes-v1.2.2.md +33 -0
- package/package.json +3 -2
- package/src/PcbScene3dCircuitJsonAdapter.mjs +1 -1
- package/src/PcbScene3dCircuitJsonCopperPourBuilder.mjs +6 -1
- package/src/PcbScene3dCircuitJsonTraceRouteBuilder.mjs +50 -10
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.
|
|
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.
|
package/docs/circuitjson.md
CHANGED
|
@@ -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.
|
|
407
|
-
|
|
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.
|
|
412
|
-
zone under solder mask, while
|
|
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.
|
|
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.
|
|
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",
|
|
@@ -257,7 +257,12 @@ export class PcbScene3dCircuitJsonCopperPourBuilder {
|
|
|
257
257
|
*/
|
|
258
258
|
static #isCoveredWithMask(pour) {
|
|
259
259
|
const value = pour?.covered_with_solder_mask
|
|
260
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
307
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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.
|