pcb-scene3d-viewer 1.1.1 → 1.1.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/docs/api.md +10 -0
- package/package.json +2 -2
- package/src/PcbScene3dAdjustmentControlBinder.mjs +667 -0
- package/src/PcbScene3dComponentAdjustment.mjs +174 -0
- package/src/PcbScene3dComponentAdjustmentRegistry.mjs +95 -0
- package/src/PcbScene3dController.mjs +214 -197
- package/src/PcbScene3dCopperFactory.mjs +4 -1
- package/src/PcbScene3dCutoutGeometryFilter.mjs +9 -3
- package/src/PcbScene3dExternalModels.mjs +56 -71
- package/src/PcbScene3dGeometryBoundsResolver.mjs +78 -0
- package/src/PcbScene3dModelBounds.mjs +267 -0
- package/src/PcbScene3dModelSearchPlacement.mjs +40 -0
- package/src/PcbScene3dPadFactory.mjs +221 -20
- package/src/PcbScene3dRenderGroupVisibility.mjs +23 -1
- package/src/PcbScene3dRuntime.mjs +62 -51
- package/src/PcbScene3dSelectionInspectorRenderer.mjs +581 -0
- package/src/PcbScene3dSelectionResolver.mjs +42 -0
- package/src/PcbScene3dSilkscreenFactory.mjs +4 -1
- package/src/PcbScene3dText.mjs +3 -0
- package/src/styles/scene3d.css +142 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes and applies live 3D component transform adjustments.
|
|
3
|
+
*/
|
|
4
|
+
export class PcbScene3dComponentAdjustment {
|
|
5
|
+
/**
|
|
6
|
+
* Finds transform adjustment targets beneath one scene object.
|
|
7
|
+
* @param {any} rootObject Root object.
|
|
8
|
+
* @returns {any[]}
|
|
9
|
+
*/
|
|
10
|
+
static findTargets(rootObject) {
|
|
11
|
+
if (!rootObject) {
|
|
12
|
+
return []
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const targets = rootObject?.userData?.scene3dAdjustmentTarget
|
|
16
|
+
? [rootObject]
|
|
17
|
+
: []
|
|
18
|
+
;(rootObject?.children || []).forEach((child) => {
|
|
19
|
+
targets.push(...PcbScene3dComponentAdjustment.findTargets(child))
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return targets
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Applies one live transform to a target relative to its original baseline.
|
|
27
|
+
* @param {any} THREE Three.js namespace.
|
|
28
|
+
* @param {any} target Target object.
|
|
29
|
+
* @param {{ scale?: { x?: number, y?: number, z?: number }, rotationDeg?: { x?: number, y?: number, z?: number }, offsetMil?: { x?: number, y?: number, z?: number } }} adjustment Adjustment.
|
|
30
|
+
* @returns {void}
|
|
31
|
+
*/
|
|
32
|
+
static applyToTarget(THREE, target, adjustment) {
|
|
33
|
+
const current = PcbScene3dComponentAdjustment.normalize(adjustment)
|
|
34
|
+
const baseline = PcbScene3dComponentAdjustment.normalize(
|
|
35
|
+
target?.userData?.scene3dAdjustmentBaseline ||
|
|
36
|
+
PcbScene3dComponentAdjustment.neutral()
|
|
37
|
+
)
|
|
38
|
+
const delta = {
|
|
39
|
+
scale: {
|
|
40
|
+
x: PcbScene3dComponentAdjustment.#scaleRatio(
|
|
41
|
+
current.scale.x,
|
|
42
|
+
baseline.scale.x
|
|
43
|
+
),
|
|
44
|
+
y: PcbScene3dComponentAdjustment.#scaleRatio(
|
|
45
|
+
current.scale.y,
|
|
46
|
+
baseline.scale.y
|
|
47
|
+
),
|
|
48
|
+
z: PcbScene3dComponentAdjustment.#scaleRatio(
|
|
49
|
+
current.scale.z,
|
|
50
|
+
baseline.scale.z
|
|
51
|
+
)
|
|
52
|
+
},
|
|
53
|
+
rotationDeg: {
|
|
54
|
+
x: current.rotationDeg.x - baseline.rotationDeg.x,
|
|
55
|
+
y: current.rotationDeg.y - baseline.rotationDeg.y,
|
|
56
|
+
z: current.rotationDeg.z - baseline.rotationDeg.z
|
|
57
|
+
},
|
|
58
|
+
offsetMil: {
|
|
59
|
+
x: current.offsetMil.x - baseline.offsetMil.x,
|
|
60
|
+
y: current.offsetMil.y - baseline.offsetMil.y,
|
|
61
|
+
z: current.offsetMil.z - baseline.offsetMil.z
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
target?.position?.set?.(
|
|
66
|
+
delta.offsetMil.x,
|
|
67
|
+
delta.offsetMil.y,
|
|
68
|
+
delta.offsetMil.z
|
|
69
|
+
)
|
|
70
|
+
target?.scale?.set?.(delta.scale.x, delta.scale.y, delta.scale.z)
|
|
71
|
+
PcbScene3dComponentAdjustment.#applyRotation(THREE, target, delta)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Normalizes one adjustment object.
|
|
76
|
+
* @param {{ scale?: { x?: number, y?: number, z?: number }, rotationDeg?: { x?: number, y?: number, z?: number }, offsetMil?: { x?: number, y?: number, z?: number }, dxMil?: number, dyMil?: number, dzMil?: number } | null | undefined} adjustment Raw adjustment.
|
|
77
|
+
* @returns {{ scale: { x: number, y: number, z: number }, rotationDeg: { x: number, y: number, z: number }, offsetMil: { x: number, y: number, z: number } }}
|
|
78
|
+
*/
|
|
79
|
+
static normalize(adjustment) {
|
|
80
|
+
const scale = adjustment?.scale || {}
|
|
81
|
+
const rotationDeg = adjustment?.rotationDeg || {}
|
|
82
|
+
const offsetMil = adjustment?.offsetMil || {}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
scale: {
|
|
86
|
+
x: PcbScene3dComponentAdjustment.#numberOr(scale.x, 1),
|
|
87
|
+
y: PcbScene3dComponentAdjustment.#numberOr(scale.y, 1),
|
|
88
|
+
z: PcbScene3dComponentAdjustment.#numberOr(scale.z, 1)
|
|
89
|
+
},
|
|
90
|
+
rotationDeg: {
|
|
91
|
+
x: PcbScene3dComponentAdjustment.#numberOr(rotationDeg.x, 0),
|
|
92
|
+
y: PcbScene3dComponentAdjustment.#numberOr(rotationDeg.y, 0),
|
|
93
|
+
z: PcbScene3dComponentAdjustment.#numberOr(rotationDeg.z, 0)
|
|
94
|
+
},
|
|
95
|
+
offsetMil: {
|
|
96
|
+
x: PcbScene3dComponentAdjustment.#numberOr(
|
|
97
|
+
offsetMil.x ?? adjustment?.dxMil,
|
|
98
|
+
0
|
|
99
|
+
),
|
|
100
|
+
y: PcbScene3dComponentAdjustment.#numberOr(
|
|
101
|
+
offsetMil.y ?? adjustment?.dyMil,
|
|
102
|
+
0
|
|
103
|
+
),
|
|
104
|
+
z: PcbScene3dComponentAdjustment.#numberOr(
|
|
105
|
+
offsetMil.z ?? adjustment?.dzMil,
|
|
106
|
+
0
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Returns a neutral transform adjustment.
|
|
114
|
+
* @returns {{ scale: { x: number, y: number, z: number }, rotationDeg: { x: number, y: number, z: number }, offsetMil: { x: number, y: number, z: number } }}
|
|
115
|
+
*/
|
|
116
|
+
static neutral() {
|
|
117
|
+
return {
|
|
118
|
+
scale: { x: 1, y: 1, z: 1 },
|
|
119
|
+
rotationDeg: { x: 0, y: 0, z: 0 },
|
|
120
|
+
offsetMil: { x: 0, y: 0, z: 0 }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Applies KiCad-style model-local rotation order to an adjustment target.
|
|
126
|
+
* @param {any} THREE Three.js namespace.
|
|
127
|
+
* @param {any} target Target object.
|
|
128
|
+
* @param {{ rotationDeg: { x: number, y: number, z: number } }} adjustment Rotation adjustment.
|
|
129
|
+
* @returns {void}
|
|
130
|
+
*/
|
|
131
|
+
static #applyRotation(THREE, target, adjustment) {
|
|
132
|
+
const x = (-Number(adjustment.rotationDeg.x || 0) * Math.PI) / 180
|
|
133
|
+
const y = (-Number(adjustment.rotationDeg.y || 0) * Math.PI) / 180
|
|
134
|
+
const z = (-Number(adjustment.rotationDeg.z || 0) * Math.PI) / 180
|
|
135
|
+
|
|
136
|
+
if (THREE?.Matrix4 && target?.quaternion?.setFromRotationMatrix) {
|
|
137
|
+
const rotationMatrix = new THREE.Matrix4()
|
|
138
|
+
.makeRotationZ(z)
|
|
139
|
+
.multiply(new THREE.Matrix4().makeRotationY(y))
|
|
140
|
+
.multiply(new THREE.Matrix4().makeRotationX(x))
|
|
141
|
+
target.quaternion.setFromRotationMatrix(rotationMatrix)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!target?.rotation) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
target.rotation.x = x
|
|
150
|
+
target.rotation.y = y
|
|
151
|
+
target.rotation.z = z
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Returns a finite number or a fallback.
|
|
156
|
+
* @param {unknown} value Source value.
|
|
157
|
+
* @param {number} fallback Fallback value.
|
|
158
|
+
* @returns {number}
|
|
159
|
+
*/
|
|
160
|
+
static #numberOr(value, fallback) {
|
|
161
|
+
const numericValue = Number(value)
|
|
162
|
+
return Number.isFinite(numericValue) ? numericValue : fallback
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Computes a safe scale ratio.
|
|
167
|
+
* @param {number} value Current value.
|
|
168
|
+
* @param {number} baseline Baseline value.
|
|
169
|
+
* @returns {number}
|
|
170
|
+
*/
|
|
171
|
+
static #scaleRatio(value, baseline) {
|
|
172
|
+
return baseline === 0 ? value : value / baseline
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { PcbScene3dComponentAdjustment } from './PcbScene3dComponentAdjustment.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tracks live transform adjustments and applies them to registered targets.
|
|
5
|
+
*/
|
|
6
|
+
export class PcbScene3dComponentAdjustmentRegistry {
|
|
7
|
+
/** @type {() => any} */
|
|
8
|
+
#resolveThree
|
|
9
|
+
/** @type {Map<string, Set<any>>} */
|
|
10
|
+
#targets
|
|
11
|
+
/** @type {Map<string, { scale: { x: number, y: number, z: number }, rotationDeg: { x: number, y: number, z: number }, offsetMil: { x: number, y: number, z: number } }>} */
|
|
12
|
+
#adjustments
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {() => any} resolveThree Three.js namespace resolver.
|
|
16
|
+
*/
|
|
17
|
+
constructor(resolveThree) {
|
|
18
|
+
this.#resolveThree = resolveThree
|
|
19
|
+
this.#targets = new Map()
|
|
20
|
+
this.#adjustments = new Map()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @returns {void} */
|
|
24
|
+
clear() {
|
|
25
|
+
this.#targets.clear()
|
|
26
|
+
this.#adjustments.clear()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Applies a live model-local adjustment to one component.
|
|
31
|
+
* @param {string} designator Component designator.
|
|
32
|
+
* @param {{ scale?: { x?: number, y?: number, z?: number }, rotationDeg?: { x?: number, y?: number, z?: number }, offsetMil?: { x?: number, y?: number, z?: number } }} adjustment Transform adjustment.
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
set(designator, adjustment) {
|
|
36
|
+
const normalizedDesignator = String(designator || '').trim()
|
|
37
|
+
if (!normalizedDesignator) {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const normalizedAdjustment =
|
|
42
|
+
PcbScene3dComponentAdjustment.normalize(adjustment)
|
|
43
|
+
this.#adjustments.set(normalizedDesignator, normalizedAdjustment)
|
|
44
|
+
this.#apply(normalizedDesignator, normalizedAdjustment)
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Registers one model-local transform adjustment target.
|
|
50
|
+
* @param {string | undefined} designator Component designator.
|
|
51
|
+
* @param {any} target Target object.
|
|
52
|
+
* @returns {void}
|
|
53
|
+
*/
|
|
54
|
+
register(designator, target) {
|
|
55
|
+
const normalizedDesignator = String(designator || '').trim()
|
|
56
|
+
if (!normalizedDesignator || !target) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!this.#targets.has(normalizedDesignator)) {
|
|
61
|
+
this.#targets.set(normalizedDesignator, new Set())
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.#targets.get(normalizedDesignator)?.add(target)
|
|
65
|
+
const pendingAdjustment = this.#adjustments.get(normalizedDesignator)
|
|
66
|
+
if (pendingAdjustment) {
|
|
67
|
+
PcbScene3dComponentAdjustment.applyToTarget(
|
|
68
|
+
this.#resolveThree(),
|
|
69
|
+
target,
|
|
70
|
+
pendingAdjustment
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Applies one adjustment to all known targets for a component.
|
|
77
|
+
* @param {string} designator Component designator.
|
|
78
|
+
* @param {{ scale: { x: number, y: number, z: number }, rotationDeg: { x: number, y: number, z: number }, offsetMil: { x: number, y: number, z: number } }} adjustment Adjustment.
|
|
79
|
+
* @returns {void}
|
|
80
|
+
*/
|
|
81
|
+
#apply(designator, adjustment) {
|
|
82
|
+
const targets = this.#targets.get(designator)
|
|
83
|
+
if (!targets) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
targets.forEach((target) => {
|
|
88
|
+
PcbScene3dComponentAdjustment.applyToTarget(
|
|
89
|
+
this.#resolveThree(),
|
|
90
|
+
target,
|
|
91
|
+
adjustment
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|