pcb-scene3d-viewer 1.1.11 → 1.1.18
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/package.json
CHANGED
|
@@ -29,7 +29,10 @@ export class PcbScene3dPadFactory {
|
|
|
29
29
|
color: 0xd9a61d,
|
|
30
30
|
roughness: 0.38,
|
|
31
31
|
metalness: 0.55,
|
|
32
|
-
side: THREE.DoubleSide
|
|
32
|
+
side: THREE.DoubleSide,
|
|
33
|
+
polygonOffset: true,
|
|
34
|
+
polygonOffsetFactor: -2,
|
|
35
|
+
polygonOffsetUnits: -2
|
|
33
36
|
})
|
|
34
37
|
const geometryCache = new Map()
|
|
35
38
|
const side = PcbScene3dPadFactory.#normalizeSide(options?.side)
|
|
@@ -500,7 +503,7 @@ export class PcbScene3dPadFactory {
|
|
|
500
503
|
* @returns {{ shape: any, shapeHoleCutouts: { x: number, y: number, diameter: number, slotLength?: number | null, rotationDeg?: number | null }[], clippingCutouts: { x: number, y: number }[][] }}
|
|
501
504
|
*/
|
|
502
505
|
static #buildShapeCutoutPlan(THREE, spec, drillCutouts) {
|
|
503
|
-
|
|
506
|
+
let baseShape = PcbScene3dPadFactory.#buildOuterShape(THREE, spec)
|
|
504
507
|
if (!drillCutouts.length) {
|
|
505
508
|
return {
|
|
506
509
|
shape: baseShape,
|
|
@@ -509,7 +512,7 @@ export class PcbScene3dPadFactory {
|
|
|
509
512
|
}
|
|
510
513
|
}
|
|
511
514
|
|
|
512
|
-
|
|
515
|
+
let contourPoints =
|
|
513
516
|
PcbScene3dBoardEdgeCutoutBuilder.resolveShapePoints(baseShape)
|
|
514
517
|
const shapeHoleCutouts = []
|
|
515
518
|
const clippingCutouts = []
|
|
@@ -529,6 +532,30 @@ export class PcbScene3dPadFactory {
|
|
|
529
532
|
continue
|
|
530
533
|
}
|
|
531
534
|
|
|
535
|
+
const circularCutout =
|
|
536
|
+
PcbScene3dPadFactory.#resolveCircularDrillCutout(drillCutout)
|
|
537
|
+
if (circularCutout) {
|
|
538
|
+
const notchedContour =
|
|
539
|
+
PcbScene3dBoardEdgeCutoutBuilder.applyCircularEdgeCutouts(
|
|
540
|
+
contourPoints,
|
|
541
|
+
[circularCutout]
|
|
542
|
+
)
|
|
543
|
+
if (
|
|
544
|
+
!PcbScene3dPadFactory.#sameContourPoints(
|
|
545
|
+
contourPoints,
|
|
546
|
+
notchedContour
|
|
547
|
+
)
|
|
548
|
+
) {
|
|
549
|
+
contourPoints = notchedContour
|
|
550
|
+
baseShape =
|
|
551
|
+
PcbScene3dBoardEdgeCutoutBuilder.buildShapeFromPoints(
|
|
552
|
+
THREE,
|
|
553
|
+
contourPoints
|
|
554
|
+
)
|
|
555
|
+
continue
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
532
559
|
clippingCutouts.push(cutoutPoints)
|
|
533
560
|
}
|
|
534
561
|
|
|
@@ -559,6 +586,30 @@ export class PcbScene3dPadFactory {
|
|
|
559
586
|
}
|
|
560
587
|
}
|
|
561
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Checks whether two sampled contours have the same vertices.
|
|
591
|
+
* @param {{ x: number, y: number }[]} first
|
|
592
|
+
* @param {{ x: number, y: number }[]} second
|
|
593
|
+
* @returns {boolean}
|
|
594
|
+
*/
|
|
595
|
+
static #sameContourPoints(first, second) {
|
|
596
|
+
if (!Array.isArray(first) || !Array.isArray(second)) {
|
|
597
|
+
return false
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (first.length !== second.length) {
|
|
601
|
+
return false
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return first.every(
|
|
605
|
+
(point, index) =>
|
|
606
|
+
Math.hypot(
|
|
607
|
+
Number(point?.x || 0) - Number(second[index]?.x || 0),
|
|
608
|
+
Number(point?.y || 0) - Number(second[index]?.y || 0)
|
|
609
|
+
) <= 0.001
|
|
610
|
+
)
|
|
611
|
+
}
|
|
612
|
+
|
|
562
613
|
/**
|
|
563
614
|
* Resolves polygon points for one local drill cutout.
|
|
564
615
|
* @param {any} THREE
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export class PcbScene3dPadSurfaceStack {
|
|
5
5
|
static #COPLANAR_PAD_LIFT_MIL = 0.04
|
|
6
|
+
static #SURFACE_Z_PRIORITY_DEFAULT = 0
|
|
7
|
+
static #SURFACE_Z_PRIORITY_DRILLED = 1
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Resolves a small lift for a pad face that overlaps previous pad faces.
|
|
9
|
-
* @param {{ bounds: { minX: number, maxX: number, minY: number, maxY: number }, zIndex: number }[]} stack
|
|
11
|
+
* @param {{ bounds: { minX: number, maxX: number, minY: number, maxY: number }, zIndex: number, priority?: number }[]} stack
|
|
10
12
|
* @param {{ x: number, y: number }} point Normalized pad anchor.
|
|
11
|
-
* @param {{ rotation?: number | null }} pad Source pad.
|
|
12
|
-
* @param {{ width: number, height: number, kind: 'circle' | 'rect' | 'rounded-rect', radius: number, offsetX: number, offsetY: number }} spec Pad face spec.
|
|
13
|
+
* @param {{ rotation?: number | null, holeDiameter?: number | null }} pad Source pad.
|
|
14
|
+
* @param {{ width: number, height: number, kind: 'circle' | 'rect' | 'rounded-rect', radius: number, offsetX: number, offsetY: number, hasHole?: boolean, holeDiameter?: number | null }} spec Pad face spec.
|
|
13
15
|
* @param {boolean} mirrorY Whether underside coordinates are mirrored.
|
|
14
16
|
* @returns {number}
|
|
15
17
|
*/
|
|
@@ -20,23 +22,50 @@ export class PcbScene3dPadSurfaceStack {
|
|
|
20
22
|
spec,
|
|
21
23
|
mirrorY
|
|
22
24
|
)
|
|
23
|
-
|
|
25
|
+
const priority = PcbScene3dPadSurfaceStack.#resolveSurfacePriority(
|
|
26
|
+
pad,
|
|
27
|
+
spec
|
|
28
|
+
)
|
|
29
|
+
let zIndex = priority
|
|
24
30
|
|
|
25
31
|
for (const previous of stack) {
|
|
26
32
|
if (
|
|
27
33
|
PcbScene3dPadSurfaceStack.#boundsOverlap(
|
|
28
34
|
surface.bounds,
|
|
29
35
|
previous.bounds
|
|
30
|
-
)
|
|
36
|
+
) &&
|
|
37
|
+
priority >=
|
|
38
|
+
Number(
|
|
39
|
+
previous.priority ??
|
|
40
|
+
PcbScene3dPadSurfaceStack
|
|
41
|
+
.#SURFACE_Z_PRIORITY_DEFAULT
|
|
42
|
+
)
|
|
31
43
|
) {
|
|
32
44
|
zIndex = Math.max(zIndex, previous.zIndex + 1)
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
|
|
36
|
-
stack.push({ bounds: surface.bounds, zIndex })
|
|
48
|
+
stack.push({ bounds: surface.bounds, zIndex, priority })
|
|
37
49
|
return zIndex * PcbScene3dPadSurfaceStack.#COPLANAR_PAD_LIFT_MIL
|
|
38
50
|
}
|
|
39
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Resolves stable visual priority for overlapping pad surfaces.
|
|
54
|
+
* @param {{ holeDiameter?: number | null }} pad Source pad.
|
|
55
|
+
* @param {{ hasHole?: boolean, holeDiameter?: number | null }} spec Pad face spec.
|
|
56
|
+
* @returns {number}
|
|
57
|
+
*/
|
|
58
|
+
static #resolveSurfacePriority(pad, spec) {
|
|
59
|
+
if (
|
|
60
|
+
spec?.hasHole === true ||
|
|
61
|
+
Number(spec?.holeDiameter || pad?.holeDiameter || 0) > 0
|
|
62
|
+
) {
|
|
63
|
+
return PcbScene3dPadSurfaceStack.#SURFACE_Z_PRIORITY_DRILLED
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return PcbScene3dPadSurfaceStack.#SURFACE_Z_PRIORITY_DEFAULT
|
|
67
|
+
}
|
|
68
|
+
|
|
40
69
|
/**
|
|
41
70
|
* Resolves the projected board-space footprint for one pad face.
|
|
42
71
|
* @param {{ x: number, y: number }} point Normalized pad anchor.
|
|
@@ -205,8 +205,8 @@ export class PcbScene3dShapeHoleMerger {
|
|
|
205
205
|
|
|
206
206
|
for (const point of Array.isArray(points) ? points : []) {
|
|
207
207
|
const normalizedPoint = {
|
|
208
|
-
x:
|
|
209
|
-
y:
|
|
208
|
+
x: PcbScene3dShapeHoleMerger.#snapCoordinate(point?.x),
|
|
209
|
+
y: PcbScene3dShapeHoleMerger.#snapCoordinate(point?.y)
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
if (
|
|
@@ -228,6 +228,17 @@ export class PcbScene3dShapeHoleMerger {
|
|
|
228
228
|
return uniquePoints
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Snaps one coordinate to the duplicate-detection precision.
|
|
233
|
+
* @param {unknown} value Coordinate value.
|
|
234
|
+
* @returns {number}
|
|
235
|
+
*/
|
|
236
|
+
static #snapCoordinate(value) {
|
|
237
|
+
const number = Number(value)
|
|
238
|
+
|
|
239
|
+
return Number.isFinite(number) ? Number(number.toFixed(6)) : number
|
|
240
|
+
}
|
|
241
|
+
|
|
231
242
|
/**
|
|
232
243
|
* Builds an axis-aligned polygon bounds box.
|
|
233
244
|
* @param {{ x: number, y: number }[]} polygon Source polygon.
|