circuitjson-toolkit 1.0.3 → 1.0.16
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/AGENTS.md +5 -3
- package/README.md +21 -2
- package/docs/api.md +50 -4
- package/docs/model-format.md +21 -3
- package/package.json +3 -2
- package/spec/library-scope.md +4 -1
- package/src/core/CircuitJsonBomBuilder.mjs +143 -0
- package/src/core/CircuitJsonDocument.mjs +46 -13
- package/src/core/CircuitJsonElementValidator.mjs +990 -0
- package/src/core/CircuitJsonIndexer.mjs +773 -4
- package/src/core/CircuitJsonManufacturingBuilder.mjs +898 -0
- package/src/core/CircuitJsonManufacturingDownloadBuilder.mjs +196 -0
- package/src/core/CircuitJsonParser.mjs +22 -6
- package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +329 -0
- package/src/core/CircuitJsonPcbCopperGeometry.mjs +503 -0
- package/src/core/CircuitJsonPcbDrawingStyle.mjs +88 -0
- package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +172 -0
- package/src/core/CircuitJsonPcbNetMetadata.mjs +247 -0
- package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +70 -0
- package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +992 -0
- package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +872 -0
- package/src/core/CircuitJsonPcbPrimitiveFields.mjs +233 -0
- package/src/core/CircuitJsonPcbPrimitiveGeometry.mjs +142 -0
- package/src/core/CircuitJsonPcbPrimitiveGroups.mjs +305 -0
- package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +65 -0
- package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +895 -0
- package/src/core/CircuitJsonPcbTraceLengthModel.mjs +257 -0
- package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +683 -0
- package/src/core/CircuitJsonSourceMetadata.mjs +233 -0
- package/src/core/CircuitJsonSupportMatrixBuilder.mjs +481 -0
- package/src/core/CircuitJsonUnits.mjs +133 -8
- package/src/core/PcbBoundsSelectionModel.mjs +250 -0
- package/src/core/PcbCandidateSelectionModel.mjs +77 -0
- package/src/core/PcbDiagnosticFocusModel.mjs +423 -0
- package/src/core/PcbInteractionPrimitiveModel.mjs +560 -0
- package/src/core/SelectedPartCircuitJsonExportAdapter.mjs +335 -0
- package/src/core/spice/SpiceCompatibilityPreprocessor.mjs +139 -0
- package/src/core/spice/SpiceDirectiveParser.mjs +231 -0
- package/src/core/spice/SpiceFallbackSimulationEngine.mjs +168 -0
- package/src/core/spice/SpiceSimulationDiagnostics.mjs +234 -0
- package/src/core/spice/SpiceSimulationGraphBuilder.mjs +421 -0
- package/src/core/spice/SpiceSimulationGraphSummary.mjs +90 -0
- package/src/core/spice/SpiceSimulationService.mjs +92 -0
- package/src/core/spice/SpiceTimeSeriesNormalizer.mjs +132 -0
- package/src/index.mjs +8 -0
- package/src/renderers.mjs +29 -0
- package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +128 -0
- package/src/ui/CircuitJsonPcbSvgRenderer.mjs +964 -0
- package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +168 -0
- package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +138 -0
- package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +114 -0
- package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +130 -0
- package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +994 -0
- package/src/ui/CircuitJsonSchematicTableSvgRenderer.mjs +439 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { CircuitJsonUnits } from './CircuitJsonUnits.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds routed-trace length inspection rows from PCB primitives.
|
|
5
|
+
*/
|
|
6
|
+
export class CircuitJsonPcbTraceLengthModel {
|
|
7
|
+
/**
|
|
8
|
+
* Builds total routed trace length labels.
|
|
9
|
+
* @param {object[]} primitives Primitive rows.
|
|
10
|
+
* @param {{ elementsByType: Map<string, object[]> }} [index] Element index.
|
|
11
|
+
* @returns {object[]}
|
|
12
|
+
*/
|
|
13
|
+
static build(primitives, index = null) {
|
|
14
|
+
const traceRules = CircuitJsonPcbTraceLengthModel.#traceRules(index)
|
|
15
|
+
const groups = new Map()
|
|
16
|
+
for (const primitive of Array.isArray(primitives) ? primitives : []) {
|
|
17
|
+
if (primitive.kind !== 'track') continue
|
|
18
|
+
const id = String(
|
|
19
|
+
primitive.source?.pcb_trace_id || primitive.id || ''
|
|
20
|
+
).split(':')[0]
|
|
21
|
+
if (!id) continue
|
|
22
|
+
|
|
23
|
+
const length = Math.hypot(
|
|
24
|
+
primitive.x2 - primitive.x1,
|
|
25
|
+
primitive.y2 - primitive.y1
|
|
26
|
+
)
|
|
27
|
+
const current = groups.get(id) || {
|
|
28
|
+
id,
|
|
29
|
+
netName: String(primitive.netName || ''),
|
|
30
|
+
length: 0,
|
|
31
|
+
point: { x: primitive.x1, y: primitive.y1 },
|
|
32
|
+
layer: primitive.layer,
|
|
33
|
+
side: primitive.side,
|
|
34
|
+
sourceTraceId: String(
|
|
35
|
+
primitive.sourceTraceId ||
|
|
36
|
+
primitive.source?.source_trace_id ||
|
|
37
|
+
''
|
|
38
|
+
),
|
|
39
|
+
longest: -1
|
|
40
|
+
}
|
|
41
|
+
current.length += length
|
|
42
|
+
if (length > current.longest) {
|
|
43
|
+
current.longest = length
|
|
44
|
+
current.point = {
|
|
45
|
+
x: (primitive.x1 + primitive.x2) / 2,
|
|
46
|
+
y: (primitive.y1 + primitive.y2) / 2
|
|
47
|
+
}
|
|
48
|
+
current.layer = primitive.layer
|
|
49
|
+
current.side = primitive.side
|
|
50
|
+
}
|
|
51
|
+
groups.set(id, current)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return [...groups.values()].map((group) =>
|
|
55
|
+
CircuitJsonPcbTraceLengthModel.#traceLengthRow(group, traceRules)
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolves source trace display rules by source id.
|
|
61
|
+
* @param {{ elementsByType: Map<string, object[]> } | null} index Element index.
|
|
62
|
+
* @returns {Map<string, object>}
|
|
63
|
+
*/
|
|
64
|
+
static #traceRules(index) {
|
|
65
|
+
const rules = new Map()
|
|
66
|
+
if (!index) return rules
|
|
67
|
+
for (const sourceTrace of CircuitJsonPcbTraceLengthModel.#all(
|
|
68
|
+
index,
|
|
69
|
+
'source_trace'
|
|
70
|
+
)) {
|
|
71
|
+
const id = String(sourceTrace.source_trace_id || '').trim()
|
|
72
|
+
if (!id) continue
|
|
73
|
+
const maxLength = CircuitJsonUnits.optionalLength(
|
|
74
|
+
sourceTrace.max_length ?? sourceTrace.maxLength
|
|
75
|
+
)
|
|
76
|
+
rules.set(id, {
|
|
77
|
+
maxLength,
|
|
78
|
+
displayName: String(
|
|
79
|
+
sourceTrace.display_name ||
|
|
80
|
+
sourceTrace.displayName ||
|
|
81
|
+
sourceTrace.name ||
|
|
82
|
+
id
|
|
83
|
+
).trim(),
|
|
84
|
+
connectedSourcePortIds:
|
|
85
|
+
CircuitJsonPcbTraceLengthModel.#connectedIds(
|
|
86
|
+
index,
|
|
87
|
+
id,
|
|
88
|
+
sourceTrace,
|
|
89
|
+
'connectedSourcePortIds',
|
|
90
|
+
[
|
|
91
|
+
sourceTrace.connected_source_port_id,
|
|
92
|
+
sourceTrace.source_port_id,
|
|
93
|
+
sourceTrace.connected_source_port_ids,
|
|
94
|
+
sourceTrace.source_port_ids
|
|
95
|
+
]
|
|
96
|
+
),
|
|
97
|
+
connectedSourceNetIds:
|
|
98
|
+
CircuitJsonPcbTraceLengthModel.#connectedIds(
|
|
99
|
+
index,
|
|
100
|
+
id,
|
|
101
|
+
sourceTrace,
|
|
102
|
+
'connectedSourceNetIds',
|
|
103
|
+
[
|
|
104
|
+
sourceTrace.connected_source_net_id,
|
|
105
|
+
sourceTrace.source_net_id,
|
|
106
|
+
sourceTrace.connected_source_net_ids,
|
|
107
|
+
sourceTrace.source_net_ids
|
|
108
|
+
]
|
|
109
|
+
)
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
return rules
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Resolves connected source IDs from index metadata or source trace fields.
|
|
117
|
+
* @param {{ sourceTraceConnectivity?: Map<string, object> }} index Element index.
|
|
118
|
+
* @param {string} sourceTraceId Source trace id.
|
|
119
|
+
* @param {object} sourceTrace Source trace row.
|
|
120
|
+
* @param {string} field Connectivity field.
|
|
121
|
+
* @param {unknown[]} fallbackValues Fallback source fields.
|
|
122
|
+
* @returns {string[]}
|
|
123
|
+
*/
|
|
124
|
+
static #connectedIds(
|
|
125
|
+
index,
|
|
126
|
+
sourceTraceId,
|
|
127
|
+
sourceTrace,
|
|
128
|
+
field,
|
|
129
|
+
fallbackValues
|
|
130
|
+
) {
|
|
131
|
+
const connectivity = index?.sourceTraceConnectivity?.get(sourceTraceId)
|
|
132
|
+
return CircuitJsonPcbTraceLengthModel.#uniqueStrings(
|
|
133
|
+
connectivity?.[field] || fallbackValues || sourceTrace?.[field]
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Builds one trace length row.
|
|
139
|
+
* @param {object} group Grouped trace data.
|
|
140
|
+
* @param {Map<string, object>} traceRules Source trace rules.
|
|
141
|
+
* @returns {object}
|
|
142
|
+
*/
|
|
143
|
+
static #traceLengthRow(group, traceRules) {
|
|
144
|
+
const length = CircuitJsonPcbTraceLengthModel.#rounded(group.length)
|
|
145
|
+
const rule = traceRules.get(group.sourceTraceId) || {}
|
|
146
|
+
const maxLength =
|
|
147
|
+
rule.maxLength === null || rule.maxLength === undefined
|
|
148
|
+
? null
|
|
149
|
+
: CircuitJsonPcbTraceLengthModel.#rounded(rule.maxLength)
|
|
150
|
+
const row = {
|
|
151
|
+
id: group.id,
|
|
152
|
+
netName: group.netName,
|
|
153
|
+
length,
|
|
154
|
+
point: {
|
|
155
|
+
x: CircuitJsonPcbTraceLengthModel.#rounded(group.point.x),
|
|
156
|
+
y: CircuitJsonPcbTraceLengthModel.#rounded(group.point.y)
|
|
157
|
+
},
|
|
158
|
+
layer: group.layer,
|
|
159
|
+
side: group.side
|
|
160
|
+
}
|
|
161
|
+
if (group.sourceTraceId) {
|
|
162
|
+
row.sourceTraceId = group.sourceTraceId
|
|
163
|
+
}
|
|
164
|
+
if (maxLength !== null) {
|
|
165
|
+
row.maxLength = maxLength
|
|
166
|
+
row.displayName = String(rule.displayName || '').trim()
|
|
167
|
+
row.overLimit = length > maxLength
|
|
168
|
+
}
|
|
169
|
+
if (rule.displayName && !row.displayName) {
|
|
170
|
+
row.displayName = String(rule.displayName || '').trim()
|
|
171
|
+
}
|
|
172
|
+
const connectedSourcePortIds =
|
|
173
|
+
CircuitJsonPcbTraceLengthModel.#uniqueStrings(
|
|
174
|
+
rule.connectedSourcePortIds
|
|
175
|
+
)
|
|
176
|
+
const connectedSourceNetIds =
|
|
177
|
+
CircuitJsonPcbTraceLengthModel.#uniqueStrings(
|
|
178
|
+
rule.connectedSourceNetIds
|
|
179
|
+
)
|
|
180
|
+
if (connectedSourcePortIds.length) {
|
|
181
|
+
row.connectedSourcePortIds = connectedSourcePortIds
|
|
182
|
+
}
|
|
183
|
+
if (connectedSourceNetIds.length) {
|
|
184
|
+
row.connectedSourceNetIds = connectedSourceNetIds
|
|
185
|
+
}
|
|
186
|
+
row.label = CircuitJsonPcbTraceLengthModel.#traceLengthLabel(row)
|
|
187
|
+
return row
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Builds a visible trace length label.
|
|
192
|
+
* @param {object} row Trace length row.
|
|
193
|
+
* @returns {string}
|
|
194
|
+
*/
|
|
195
|
+
static #traceLengthLabel(row) {
|
|
196
|
+
const length = CircuitJsonPcbTraceLengthModel.#formatLength(row.length)
|
|
197
|
+
if (row.maxLength === undefined) return length + ' mm'
|
|
198
|
+
|
|
199
|
+
const maxLength = CircuitJsonPcbTraceLengthModel.#formatLength(
|
|
200
|
+
row.maxLength
|
|
201
|
+
)
|
|
202
|
+
const displayName = String(row.displayName || '').trim()
|
|
203
|
+
return (
|
|
204
|
+
length +
|
|
205
|
+
' / ' +
|
|
206
|
+
maxLength +
|
|
207
|
+
' mm' +
|
|
208
|
+
(displayName ? ' (' + displayName + ')' : '')
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Returns indexed element rows.
|
|
214
|
+
* @param {{ elementsByType: Map<string, object[]> }} index Element index.
|
|
215
|
+
* @param {string} type Element type.
|
|
216
|
+
* @returns {object[]}
|
|
217
|
+
*/
|
|
218
|
+
static #all(index, type) {
|
|
219
|
+
return index.elementsByType.get(type) || []
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Normalizes scalar, array, and nested array string fields.
|
|
224
|
+
* @param {unknown} values Candidate values.
|
|
225
|
+
* @returns {string[]}
|
|
226
|
+
*/
|
|
227
|
+
static #uniqueStrings(values) {
|
|
228
|
+
return [
|
|
229
|
+
...new Set(
|
|
230
|
+
(Array.isArray(values) ? values : [values])
|
|
231
|
+
.flatMap((value) =>
|
|
232
|
+
Array.isArray(value) ? value : [value]
|
|
233
|
+
)
|
|
234
|
+
.map((value) => String(value || '').trim())
|
|
235
|
+
.filter(Boolean)
|
|
236
|
+
)
|
|
237
|
+
]
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Formats one length in millimeters.
|
|
242
|
+
* @param {number} value Numeric value.
|
|
243
|
+
* @returns {string}
|
|
244
|
+
*/
|
|
245
|
+
static #formatLength(value) {
|
|
246
|
+
return Number(value).toFixed(2)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Rounds a numeric value for deterministic model rows.
|
|
251
|
+
* @param {number} value Numeric value.
|
|
252
|
+
* @returns {number}
|
|
253
|
+
*/
|
|
254
|
+
static #rounded(value) {
|
|
255
|
+
return Number(Number(value).toFixed(6))
|
|
256
|
+
}
|
|
257
|
+
}
|