pcb-scene3d-viewer 1.3.2 → 1.3.4
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 +6 -0
- package/docs/api.md +15 -0
- package/docs/model-format.md +12 -0
- package/docs/release-notes-v1.3.3.md +28 -0
- package/package.json +3 -2
- package/src/PcbAssemblyFillGeometryResolver.mjs +16 -0
- package/src/PcbScene3dCopperOcclusionClipper.mjs +4 -5
- package/src/PcbScene3dCopperOcclusionGeometry.mjs +182 -0
- package/src/PcbScene3dCopperOcclusionPlanes.mjs +145 -0
- package/src/PcbScene3dCopperTextFactory.mjs +8 -10
- package/src/PcbScene3dCutoutGeometryFilter.mjs +40 -8
- package/src/PcbScene3dExternalModelRepeatedOwnerPackageCenterRepair.mjs +78 -32
- package/src/PcbScene3dExternalModelSourceOriginPolicy.mjs +3 -2
- package/src/PcbScene3dExternalModels.mjs +77 -1
- package/src/PcbScene3dGeneratedGeometryBuilder.mjs +87 -0
- package/src/PcbScene3dGeometryTransfer.mjs +398 -0
- package/src/PcbScene3dGeometryWorker.mjs +45 -0
- package/src/PcbScene3dGeometryWorkerClient.mjs +182 -0
- package/src/PcbScene3dMaskCoveredCopperSurfaceFilter.mjs +17 -27
- package/src/PcbScene3dRuntime.mjs +33 -54
- package/src/PcbScene3dSilkscreenChunkedFactory.mjs +15 -3
- package/src/PcbScene3dSilkscreenCutoutContext.mjs +35 -0
- package/src/PcbScene3dSilkscreenFactory.mjs +58 -22
- package/src/PcbScene3dSilkscreenFillSeamBuilder.mjs +4 -3
- package/src/PcbScene3dWorkerClient.mjs +12 -6
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { PcbScene3dBoardSolderMaskFactory } from './PcbScene3dBoardSolderMaskFactory.mjs'
|
|
2
1
|
import { PcbScene3dCameraRig } from './PcbScene3dCameraRig.mjs'
|
|
3
2
|
import { PcbScene3dCircuitJsonAdapter } from './PcbScene3dCircuitJsonAdapter.mjs'
|
|
4
3
|
import { PcbScene3dComponentVisibility } from './PcbScene3dComponentVisibility.mjs'
|
|
5
4
|
import { PcbScene3dComponentAdjustment } from './PcbScene3dComponentAdjustment.mjs'
|
|
6
5
|
import { PcbScene3dComponentAdjustmentRegistry } from './PcbScene3dComponentAdjustmentRegistry.mjs'
|
|
7
6
|
import { PcbScene3dCompanionBasePlacementAdjuster } from './PcbScene3dCompanionBasePlacementAdjuster.mjs'
|
|
8
|
-
import { PcbScene3dCopperDetailGroupBuilder } from './PcbScene3dCopperDetailGroupBuilder.mjs'
|
|
9
7
|
import { PcbScene3dCopperFactory } from './PcbScene3dCopperFactory.mjs'
|
|
10
8
|
import { PcbScene3dDetailCoordinateNormalizer } from './PcbScene3dDetailCoordinateNormalizer.mjs'
|
|
11
9
|
import { PcbScene3dDeferredModelFinalizer } from './PcbScene3dDeferredModelFinalizer.mjs'
|
|
12
|
-
import {
|
|
10
|
+
import { PcbScene3dGeometryTransfer } from './PcbScene3dGeometryTransfer.mjs'
|
|
11
|
+
import { PcbScene3dGeometryWorkerClient } from './PcbScene3dGeometryWorkerClient.mjs'
|
|
13
12
|
import { PcbScene3dExternalCompanionFallback } from './PcbScene3dExternalCompanionFallback.mjs'
|
|
14
13
|
import { PcbScene3dExternalModels } from './PcbScene3dExternalModels.mjs'
|
|
15
14
|
import { PcbScene3dExternalPlacementDefaults } from './PcbScene3dExternalPlacementDefaults.mjs'
|
|
@@ -75,10 +74,11 @@ export class PcbScene3dRuntime {
|
|
|
75
74
|
#hasSettledReady
|
|
76
75
|
#renderScheduler
|
|
77
76
|
#visibilityGraph
|
|
77
|
+
#geometryWorker
|
|
78
78
|
/**
|
|
79
79
|
* @param {HTMLElement} viewportNode
|
|
80
80
|
* @param {any} sceneDescription Scene description or CircuitJSON model.
|
|
81
|
-
* @param {{ setDiagnostics?: (messages: string[]) => void, setSelection?: (selection: any | null) => void, loadRuntimeModules?: () => Promise<{ THREE: any, OrbitControls: any }>, translate?: ((key: string) => string) | null, modelLoaderOptions?: object }} [hooks]
|
|
81
|
+
* @param {{ setDiagnostics?: (messages: string[]) => void, setSelection?: (selection: any | null) => void, loadRuntimeModules?: () => Promise<{ THREE: any, OrbitControls: any }>, translate?: ((key: string) => string) | null, modelLoaderOptions?: object, geometryWorkerOptions?: object }} [hooks]
|
|
82
82
|
*/
|
|
83
83
|
constructor(viewportNode, sceneDescription, hooks = {}) {
|
|
84
84
|
const renderModel =
|
|
@@ -129,6 +129,9 @@ export class PcbScene3dRuntime {
|
|
|
129
129
|
}
|
|
130
130
|
})
|
|
131
131
|
this.#visibilityGraph = new PcbScene3dVisibilityGraph()
|
|
132
|
+
this.#geometryWorker = new PcbScene3dGeometryWorkerClient(
|
|
133
|
+
hooks.geometryWorkerOptions
|
|
134
|
+
)
|
|
132
135
|
this.#resolveReadyPromise = null
|
|
133
136
|
this.#readyPromise = new Promise((resolve) => {
|
|
134
137
|
this.#resolveReadyPromise = resolve
|
|
@@ -218,6 +221,10 @@ export class PcbScene3dRuntime {
|
|
|
218
221
|
/** @returns {void} */
|
|
219
222
|
dispose() {
|
|
220
223
|
this.#isDisposed = true
|
|
224
|
+
this.#geometryWorker.dispose()
|
|
225
|
+
;['board', 'copper'].forEach((kind) =>
|
|
226
|
+
PcbScene3dGeometryTransfer.dispose(this.#groups.get(kind))
|
|
227
|
+
)
|
|
221
228
|
this.#renderScheduler.cancel()
|
|
222
229
|
this.#listeners.forEach(({ node, type, listener }) => {
|
|
223
230
|
node.removeEventListener?.(type, listener)
|
|
@@ -283,9 +290,13 @@ export class PcbScene3dRuntime {
|
|
|
283
290
|
this.#bindSelectionInteraction()
|
|
284
291
|
this.#applyToggleVisibility()
|
|
285
292
|
this.#render()
|
|
293
|
+
await this.#loadGeneratedGeometry('board')
|
|
294
|
+
if (this.#isDisposed) return
|
|
295
|
+
this.#render()
|
|
286
296
|
await this.#loadDeferredDetail()
|
|
287
297
|
this.#settleReady()
|
|
288
298
|
} catch (error) {
|
|
299
|
+
if (this.#isDisposed) return
|
|
289
300
|
this.#hooks.setDiagnostics?.([
|
|
290
301
|
'3D preview could not start: ' +
|
|
291
302
|
String(error?.message || error || 'Unknown error.')
|
|
@@ -342,44 +353,6 @@ export class PcbScene3dRuntime {
|
|
|
342
353
|
.multiplyScalar(this.#initialRadius * 0.8)
|
|
343
354
|
this.#scene.add(ambientLight, keyLight, fillLight)
|
|
344
355
|
const boardGroup = new THREE.Group()
|
|
345
|
-
boardGroup.add(
|
|
346
|
-
PcbScene3dRuntimeBoardMeshes.buildBoardMesh(
|
|
347
|
-
THREE,
|
|
348
|
-
this.#sceneDescription,
|
|
349
|
-
(x, y) => this.#normalizeDetailPoint(x, y)
|
|
350
|
-
)
|
|
351
|
-
)
|
|
352
|
-
boardGroup.add(
|
|
353
|
-
PcbScene3dBoardSolderMaskFactory.buildGroup(
|
|
354
|
-
THREE,
|
|
355
|
-
this.#sceneDescription,
|
|
356
|
-
(x, y) => this.#normalizeDetailPoint(x, y)
|
|
357
|
-
)
|
|
358
|
-
)
|
|
359
|
-
boardGroup.add(
|
|
360
|
-
PcbScene3dDrillVoidFactory.buildGroup(
|
|
361
|
-
THREE,
|
|
362
|
-
this.#sceneDescription.detail,
|
|
363
|
-
board.thicknessMil / 2,
|
|
364
|
-
-board.thicknessMil / 2,
|
|
365
|
-
(x, y) => this.#normalizeDetailPoint(x, y),
|
|
366
|
-
{
|
|
367
|
-
enabled: true,
|
|
368
|
-
board,
|
|
369
|
-
hasBoardAssemblyModel: Boolean(
|
|
370
|
-
this.#sceneDescription.boardAssemblyModel
|
|
371
|
-
),
|
|
372
|
-
sourceFormat: this.#sceneDescription.sourceFormat
|
|
373
|
-
}
|
|
374
|
-
)
|
|
375
|
-
)
|
|
376
|
-
boardGroup.add(
|
|
377
|
-
PcbScene3dRuntimeBoardMeshes.buildBoardOutline(
|
|
378
|
-
THREE,
|
|
379
|
-
this.#sceneDescription,
|
|
380
|
-
(x, y) => this.#normalizeDetailPoint(x, y)
|
|
381
|
-
)
|
|
382
|
-
)
|
|
383
356
|
this.#groups.set('board', boardGroup)
|
|
384
357
|
PcbScene3dRuntimeBoardMeshes.applyBoardFaceSide(
|
|
385
358
|
this.#three,
|
|
@@ -536,7 +509,8 @@ export class PcbScene3dRuntime {
|
|
|
536
509
|
if (this.#isDisposed) {
|
|
537
510
|
return
|
|
538
511
|
}
|
|
539
|
-
this.#
|
|
512
|
+
await this.#loadGeneratedGeometry('copper')
|
|
513
|
+
if (this.#isDisposed) return
|
|
540
514
|
this.#applyToggleVisibility()
|
|
541
515
|
this.#render()
|
|
542
516
|
this.#settleReady()
|
|
@@ -596,24 +570,29 @@ export class PcbScene3dRuntime {
|
|
|
596
570
|
}
|
|
597
571
|
|
|
598
572
|
/**
|
|
599
|
-
*
|
|
600
|
-
* @
|
|
573
|
+
* Attaches exact worker-built geometry only while its runtime is alive.
|
|
574
|
+
* @param {'board' | 'copper'} kind Generated detail stage.
|
|
575
|
+
* @returns {Promise<void>}
|
|
601
576
|
*/
|
|
602
|
-
#
|
|
603
|
-
const
|
|
604
|
-
if (!
|
|
577
|
+
async #loadGeneratedGeometry(kind) {
|
|
578
|
+
const group = this.#groups.get(kind)
|
|
579
|
+
if (!group || group.children.length || this.#isDisposed) {
|
|
605
580
|
return
|
|
606
581
|
}
|
|
607
|
-
const
|
|
608
|
-
const detailGroup = PcbScene3dCopperDetailGroupBuilder.build(
|
|
582
|
+
const detailGroup = await this.#geometryWorker.build(
|
|
609
583
|
this.#three,
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
(x, y) => this.#normalizeDetailPoint(x, y)
|
|
584
|
+
kind,
|
|
585
|
+
this.#sceneDescription
|
|
613
586
|
)
|
|
587
|
+
if (this.#isDisposed) {
|
|
588
|
+
PcbScene3dGeometryTransfer.dispose(detailGroup)
|
|
589
|
+
return
|
|
590
|
+
}
|
|
614
591
|
if (detailGroup.children.length) {
|
|
615
|
-
|
|
592
|
+
if (kind === 'board') group.add(...[...detailGroup.children])
|
|
593
|
+
else group.add(detailGroup)
|
|
616
594
|
this.#applyViewScale(this.#presetState.get())
|
|
595
|
+
this.#applyToggleVisibility()
|
|
617
596
|
}
|
|
618
597
|
}
|
|
619
598
|
/**
|
|
@@ -41,6 +41,7 @@ export class PcbScene3dSilkscreenChunkedFactory {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const group = new THREE.Group()
|
|
44
|
+
const cutoutContexts = {}
|
|
44
45
|
for (const side of ['top', 'bottom']) {
|
|
45
46
|
for (const recordKey of PcbScene3dSilkscreenChunkedFactory
|
|
46
47
|
.#RECORD_KEYS) {
|
|
@@ -53,7 +54,8 @@ export class PcbScene3dSilkscreenChunkedFactory {
|
|
|
53
54
|
topZ,
|
|
54
55
|
bottomZ,
|
|
55
56
|
normalizeBoardPoint,
|
|
56
|
-
options
|
|
57
|
+
options,
|
|
58
|
+
cutoutContexts
|
|
57
59
|
)
|
|
58
60
|
}
|
|
59
61
|
}
|
|
@@ -72,6 +74,7 @@ export class PcbScene3dSilkscreenChunkedFactory {
|
|
|
72
74
|
* @param {number} bottomZ
|
|
73
75
|
* @param {(x: number, y: number) => { x: number, y: number }} normalizeBoardPoint
|
|
74
76
|
* @param {{ yieldToMain?: () => Promise<void> | void, shouldContinue?: () => boolean }} options
|
|
77
|
+
* @param {object} cutoutContexts Build-scoped side preparations.
|
|
75
78
|
* @returns {Promise<void>}
|
|
76
79
|
*/
|
|
77
80
|
static async #appendRecordChunks(
|
|
@@ -83,7 +86,8 @@ export class PcbScene3dSilkscreenChunkedFactory {
|
|
|
83
86
|
topZ,
|
|
84
87
|
bottomZ,
|
|
85
88
|
normalizeBoardPoint,
|
|
86
|
-
options
|
|
89
|
+
options,
|
|
90
|
+
cutoutContexts
|
|
87
91
|
) {
|
|
88
92
|
const sideSilkscreen = silkscreen?.[side] || {}
|
|
89
93
|
const records = Array.isArray(sideSilkscreen[recordKey])
|
|
@@ -98,6 +102,13 @@ export class PcbScene3dSilkscreenChunkedFactory {
|
|
|
98
102
|
PcbScene3dSilkscreenChunkedFactory.#shouldContinue(options);
|
|
99
103
|
index += batchSize
|
|
100
104
|
) {
|
|
105
|
+
// Prepare lazily so cancelled or empty sides incur no cutout work.
|
|
106
|
+
cutoutContexts[side] ||=
|
|
107
|
+
PcbScene3dSilkscreenFactory.prepareCutoutContext(
|
|
108
|
+
sideSilkscreen,
|
|
109
|
+
normalizeBoardPoint,
|
|
110
|
+
side === 'bottom'
|
|
111
|
+
)
|
|
101
112
|
const chunkGroup = PcbScene3dSilkscreenFactory.buildGroup(
|
|
102
113
|
THREE,
|
|
103
114
|
PcbScene3dSilkscreenChunkedFactory.#buildChunkSilkscreen(
|
|
@@ -108,7 +119,8 @@ export class PcbScene3dSilkscreenChunkedFactory {
|
|
|
108
119
|
),
|
|
109
120
|
topZ,
|
|
110
121
|
bottomZ,
|
|
111
|
-
normalizeBoardPoint
|
|
122
|
+
normalizeBoardPoint,
|
|
123
|
+
cutoutContexts
|
|
112
124
|
)
|
|
113
125
|
if (chunkGroup.children.length) {
|
|
114
126
|
group.add(chunkGroup)
|
|
@@ -66,6 +66,41 @@ export class PcbScene3dSilkscreenCutoutContext {
|
|
|
66
66
|
this.#prepareNormalizedCutout(cutout)
|
|
67
67
|
)
|
|
68
68
|
|
|
69
|
+
/** @type {{ x: number, y: number }[][]} */
|
|
70
|
+
#surfaceCutouts
|
|
71
|
+
|
|
72
|
+
/** @type {WeakMap<any[], object>} */
|
|
73
|
+
#preparedCutoutCache = new WeakMap()
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Owns detached normalized cutouts for one side of a build. Callers must
|
|
77
|
+
* keep these coordinates unchanged for the lifetime of this context.
|
|
78
|
+
* @param {{ x: number, y: number }[][]} [surfaceCutouts] Normalized cutouts.
|
|
79
|
+
*/
|
|
80
|
+
constructor(surfaceCutouts = []) {
|
|
81
|
+
this.#surfaceCutouts = surfaceCutouts
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Returns the normalized surface cutouts shared by every side batch.
|
|
86
|
+
* @returns {{ x: number, y: number }[][]}
|
|
87
|
+
*/
|
|
88
|
+
get surfaceCutouts() {
|
|
89
|
+
return this.#surfaceCutouts
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns build-scoped caches for exact clipping consumers. Weak keys
|
|
94
|
+
* allow temporary fill-specific cutout lists to be released after use.
|
|
95
|
+
* @returns {{ preparedPolygonCache: Map, preparedCutoutCache: WeakMap }}
|
|
96
|
+
*/
|
|
97
|
+
get geometryFilterOptions() {
|
|
98
|
+
return {
|
|
99
|
+
preparedPolygonCache: this.#preparedPolygonCache,
|
|
100
|
+
preparedCutoutCache: this.#preparedCutoutCache
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
69
104
|
/**
|
|
70
105
|
* Returns the lazy cache shared by exact geometry consumers.
|
|
71
106
|
* @returns {Map<any, PcbScene3dPreparedPolygon>}
|
|
@@ -31,23 +31,33 @@ export class PcbScene3dSilkscreenFactory {
|
|
|
31
31
|
* @param {number} topZ
|
|
32
32
|
* @param {number} bottomZ
|
|
33
33
|
* @param {(x: number, y: number) => { x: number, y: number }} normalizeBoardPoint
|
|
34
|
+
* @param {{ top?: PcbScene3dSilkscreenCutoutContext, bottom?: PcbScene3dSilkscreenCutoutContext }} [cutoutContexts] Internal build-scoped side preparations.
|
|
34
35
|
* @returns {any}
|
|
35
36
|
*/
|
|
36
|
-
static buildGroup(
|
|
37
|
+
static buildGroup(
|
|
38
|
+
THREE,
|
|
39
|
+
silkscreen,
|
|
40
|
+
topZ,
|
|
41
|
+
bottomZ,
|
|
42
|
+
normalizeBoardPoint,
|
|
43
|
+
cutoutContexts = {}
|
|
44
|
+
) {
|
|
37
45
|
const group = new THREE.Group()
|
|
38
46
|
const topGroup = PcbScene3dSilkscreenFactory.#buildSideGroup(
|
|
39
47
|
THREE,
|
|
40
48
|
silkscreen?.top,
|
|
41
49
|
Math.abs(Number(topZ || 0)),
|
|
42
50
|
normalizeBoardPoint,
|
|
43
|
-
false
|
|
51
|
+
false,
|
|
52
|
+
cutoutContexts.top
|
|
44
53
|
)
|
|
45
54
|
const bottomGroup = PcbScene3dSilkscreenFactory.#buildSideGroup(
|
|
46
55
|
THREE,
|
|
47
56
|
silkscreen?.bottom,
|
|
48
57
|
Math.abs(Number(bottomZ || 0)),
|
|
49
58
|
normalizeBoardPoint,
|
|
50
|
-
true
|
|
59
|
+
true,
|
|
60
|
+
cutoutContexts.bottom
|
|
51
61
|
)
|
|
52
62
|
|
|
53
63
|
if (topGroup.children.length) {
|
|
@@ -60,6 +70,30 @@ export class PcbScene3dSilkscreenFactory {
|
|
|
60
70
|
return group
|
|
61
71
|
}
|
|
62
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Normalizes detached cutouts once for all batches on one board side.
|
|
75
|
+
* The returned context must not be reused across separate scene builds.
|
|
76
|
+
* @param {{ drillCutouts?: object[][], copperCutouts?: object[][] } | undefined} silkscreen Side artwork.
|
|
77
|
+
* @param {(x: number, y: number) => { x: number, y: number }} normalizeBoardPoint Board normalizer.
|
|
78
|
+
* @param {boolean} mirrorY Whether the side is mirrored.
|
|
79
|
+
* @returns {PcbScene3dSilkscreenCutoutContext}
|
|
80
|
+
*/
|
|
81
|
+
static prepareCutoutContext(silkscreen, normalizeBoardPoint, mirrorY) {
|
|
82
|
+
const drillCutouts = PcbScene3dSilkscreenFactory.#normalizeCutouts(
|
|
83
|
+
silkscreen?.drillCutouts,
|
|
84
|
+
normalizeBoardPoint,
|
|
85
|
+
mirrorY
|
|
86
|
+
)
|
|
87
|
+
const copperCutouts = PcbScene3dSilkscreenFactory.#normalizeCutouts(
|
|
88
|
+
silkscreen?.copperCutouts,
|
|
89
|
+
normalizeBoardPoint,
|
|
90
|
+
mirrorY
|
|
91
|
+
)
|
|
92
|
+
return new PcbScene3dSilkscreenCutoutContext(
|
|
93
|
+
drillCutouts.concat(copperCutouts)
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
63
97
|
/**
|
|
64
98
|
* Builds one side-specific silkscreen group.
|
|
65
99
|
* @param {any} THREE
|
|
@@ -67,9 +101,17 @@ export class PcbScene3dSilkscreenFactory {
|
|
|
67
101
|
* @param {number} z
|
|
68
102
|
* @param {(x: number, y: number) => { x: number, y: number }} normalizeBoardPoint
|
|
69
103
|
* @param {boolean} mirrorY
|
|
104
|
+
* @param {PcbScene3dSilkscreenCutoutContext} [preparedCutoutContext] Shared side preparation.
|
|
70
105
|
* @returns {any}
|
|
71
106
|
*/
|
|
72
|
-
static #buildSideGroup(
|
|
107
|
+
static #buildSideGroup(
|
|
108
|
+
THREE,
|
|
109
|
+
silkscreen,
|
|
110
|
+
z,
|
|
111
|
+
normalizeBoardPoint,
|
|
112
|
+
mirrorY,
|
|
113
|
+
preparedCutoutContext
|
|
114
|
+
) {
|
|
73
115
|
const group = new THREE.Group()
|
|
74
116
|
const strokeColor = PcbScene3dSilkscreenFactory.#resolveMaterialColor(
|
|
75
117
|
silkscreen?.strokeColor
|
|
@@ -89,18 +131,14 @@ export class PcbScene3dSilkscreenFactory {
|
|
|
89
131
|
? fillColor
|
|
90
132
|
: textMaterialColor
|
|
91
133
|
const strokeZ = z + PcbScene3dSilkscreenFactory.#STROKE_Z_OFFSET
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
mirrorY
|
|
101
|
-
)
|
|
102
|
-
const surfaceCutouts = drillCutouts.concat(copperCutouts)
|
|
103
|
-
const cutoutContext = new PcbScene3dSilkscreenCutoutContext()
|
|
134
|
+
const cutoutContext =
|
|
135
|
+
preparedCutoutContext ||
|
|
136
|
+
PcbScene3dSilkscreenFactory.prepareCutoutContext(
|
|
137
|
+
silkscreen,
|
|
138
|
+
normalizeBoardPoint,
|
|
139
|
+
mirrorY
|
|
140
|
+
)
|
|
141
|
+
const surfaceCutouts = cutoutContext.surfaceCutouts
|
|
104
142
|
const strokeMaterial = PcbScene3dSilkscreenFactory.#buildMaterial(
|
|
105
143
|
THREE,
|
|
106
144
|
strokeColor
|
|
@@ -147,9 +185,7 @@ export class PcbScene3dSilkscreenFactory {
|
|
|
147
185
|
mirrorY,
|
|
148
186
|
fillMaterial,
|
|
149
187
|
surfaceCutouts,
|
|
150
|
-
|
|
151
|
-
preparedPolygonCache: cutoutContext.preparedPolygonCache
|
|
152
|
-
}
|
|
188
|
+
cutoutContext.geometryFilterOptions
|
|
153
189
|
)
|
|
154
190
|
const texts = Array.isArray(silkscreen?.texts) ? silkscreen.texts : []
|
|
155
191
|
const renderableTexts = texts.filter(
|
|
@@ -179,7 +215,7 @@ export class PcbScene3dSilkscreenFactory {
|
|
|
179
215
|
fog: false
|
|
180
216
|
},
|
|
181
217
|
mirrorY,
|
|
182
|
-
|
|
218
|
+
...cutoutContext.geometryFilterOptions,
|
|
183
219
|
side: mirrorY ? 'bottom' : 'top'
|
|
184
220
|
}
|
|
185
221
|
)
|
|
@@ -534,7 +570,7 @@ export class PcbScene3dSilkscreenFactory {
|
|
|
534
570
|
maxDepth: 12,
|
|
535
571
|
maxEdgeLength:
|
|
536
572
|
PcbScene3dSilkscreenFactory.#CUTOUT_MAX_EDGE_LENGTH,
|
|
537
|
-
|
|
573
|
+
...cutoutContext.geometryFilterOptions
|
|
538
574
|
}
|
|
539
575
|
)
|
|
540
576
|
const mesh = new THREE.Mesh(geometry, material)
|
|
@@ -823,7 +859,7 @@ export class PcbScene3dSilkscreenFactory {
|
|
|
823
859
|
maxDepth: 12,
|
|
824
860
|
maxEdgeLength:
|
|
825
861
|
PcbScene3dSilkscreenFactory.#CUTOUT_MAX_EDGE_LENGTH,
|
|
826
|
-
|
|
862
|
+
...cutoutContext?.geometryFilterOptions
|
|
827
863
|
}
|
|
828
864
|
),
|
|
829
865
|
material
|
|
@@ -22,7 +22,7 @@ export class PcbScene3dSilkscreenFillSeamBuilder {
|
|
|
22
22
|
* @param {boolean} mirrorY Whether the side is mirrored.
|
|
23
23
|
* @param {any} material Shared fill material.
|
|
24
24
|
* @param {{ x: number, y: number }[][]} [cutouts] Cutouts that should remain uncovered.
|
|
25
|
-
* @param {{ preparedPolygonCache?: Map }} [options] Request-scoped options.
|
|
25
|
+
* @param {{ preparedPolygonCache?: Map, preparedCutoutCache?: WeakMap }} [options] Request-scoped options.
|
|
26
26
|
* @returns {any[]}
|
|
27
27
|
*/
|
|
28
28
|
static buildMeshes(
|
|
@@ -117,7 +117,7 @@ export class PcbScene3dSilkscreenFillSeamBuilder {
|
|
|
117
117
|
* @param {number[]} positions Position buffer.
|
|
118
118
|
* @param {any} material Shared material.
|
|
119
119
|
* @param {{ x: number, y: number }[][]} cutouts Cutout polygons.
|
|
120
|
-
* @param {{ preparedPolygonCache?: Map }} options Request-scoped options.
|
|
120
|
+
* @param {{ preparedPolygonCache?: Map, preparedCutoutCache?: WeakMap }} options Request-scoped options.
|
|
121
121
|
* @returns {void}
|
|
122
122
|
*/
|
|
123
123
|
static #appendMesh(meshes, THREE, positions, material, cutouts, options) {
|
|
@@ -139,7 +139,8 @@ export class PcbScene3dSilkscreenFillSeamBuilder {
|
|
|
139
139
|
PcbScene3dCutoutGeometryFilter.filter(THREE, geometry, cutouts, {
|
|
140
140
|
maxDepth: 12,
|
|
141
141
|
maxEdgeLength: 2,
|
|
142
|
-
preparedPolygonCache: options?.preparedPolygonCache
|
|
142
|
+
preparedPolygonCache: options?.preparedPolygonCache,
|
|
143
|
+
preparedCutoutCache: options?.preparedCutoutCache
|
|
143
144
|
}),
|
|
144
145
|
material
|
|
145
146
|
)
|
|
@@ -40,12 +40,18 @@ export class PcbScene3dWorkerClient {
|
|
|
40
40
|
|
|
41
41
|
const requestId = 'scene3d-request-' + this.#requestSequence++
|
|
42
42
|
this.#pendingRequests.set(requestId, { resolve, reject })
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
try {
|
|
44
|
+
this.#worker.postMessage({
|
|
45
|
+
type: 'scene3d:prepare',
|
|
46
|
+
requestId,
|
|
47
|
+
documentModel,
|
|
48
|
+
sessionAssets
|
|
49
|
+
})
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// A synchronous clone failure will never receive a response.
|
|
52
|
+
this.#pendingRequests.delete(requestId)
|
|
53
|
+
reject(error)
|
|
54
|
+
}
|
|
49
55
|
})
|
|
50
56
|
}
|
|
51
57
|
|