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.
Files changed (54) hide show
  1. package/AGENTS.md +5 -3
  2. package/README.md +21 -2
  3. package/docs/api.md +50 -4
  4. package/docs/model-format.md +21 -3
  5. package/package.json +3 -2
  6. package/spec/library-scope.md +4 -1
  7. package/src/core/CircuitJsonBomBuilder.mjs +143 -0
  8. package/src/core/CircuitJsonDocument.mjs +46 -13
  9. package/src/core/CircuitJsonElementValidator.mjs +990 -0
  10. package/src/core/CircuitJsonIndexer.mjs +773 -4
  11. package/src/core/CircuitJsonManufacturingBuilder.mjs +898 -0
  12. package/src/core/CircuitJsonManufacturingDownloadBuilder.mjs +196 -0
  13. package/src/core/CircuitJsonParser.mjs +22 -6
  14. package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +329 -0
  15. package/src/core/CircuitJsonPcbCopperGeometry.mjs +503 -0
  16. package/src/core/CircuitJsonPcbDrawingStyle.mjs +88 -0
  17. package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +172 -0
  18. package/src/core/CircuitJsonPcbNetMetadata.mjs +247 -0
  19. package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +70 -0
  20. package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +992 -0
  21. package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +872 -0
  22. package/src/core/CircuitJsonPcbPrimitiveFields.mjs +233 -0
  23. package/src/core/CircuitJsonPcbPrimitiveGeometry.mjs +142 -0
  24. package/src/core/CircuitJsonPcbPrimitiveGroups.mjs +305 -0
  25. package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +65 -0
  26. package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +895 -0
  27. package/src/core/CircuitJsonPcbTraceLengthModel.mjs +257 -0
  28. package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +683 -0
  29. package/src/core/CircuitJsonSourceMetadata.mjs +233 -0
  30. package/src/core/CircuitJsonSupportMatrixBuilder.mjs +481 -0
  31. package/src/core/CircuitJsonUnits.mjs +133 -8
  32. package/src/core/PcbBoundsSelectionModel.mjs +250 -0
  33. package/src/core/PcbCandidateSelectionModel.mjs +77 -0
  34. package/src/core/PcbDiagnosticFocusModel.mjs +423 -0
  35. package/src/core/PcbInteractionPrimitiveModel.mjs +560 -0
  36. package/src/core/SelectedPartCircuitJsonExportAdapter.mjs +335 -0
  37. package/src/core/spice/SpiceCompatibilityPreprocessor.mjs +139 -0
  38. package/src/core/spice/SpiceDirectiveParser.mjs +231 -0
  39. package/src/core/spice/SpiceFallbackSimulationEngine.mjs +168 -0
  40. package/src/core/spice/SpiceSimulationDiagnostics.mjs +234 -0
  41. package/src/core/spice/SpiceSimulationGraphBuilder.mjs +421 -0
  42. package/src/core/spice/SpiceSimulationGraphSummary.mjs +90 -0
  43. package/src/core/spice/SpiceSimulationService.mjs +92 -0
  44. package/src/core/spice/SpiceTimeSeriesNormalizer.mjs +132 -0
  45. package/src/index.mjs +8 -0
  46. package/src/renderers.mjs +29 -0
  47. package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +128 -0
  48. package/src/ui/CircuitJsonPcbSvgRenderer.mjs +964 -0
  49. package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +168 -0
  50. package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +138 -0
  51. package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +114 -0
  52. package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +130 -0
  53. package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +994 -0
  54. 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
+ }