kicadts 0.0.1 → 0.0.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.
Files changed (78) hide show
  1. package/.github/workflows/bun-formatcheck.yml +26 -0
  2. package/.github/workflows/bun-pver-release.yml +70 -0
  3. package/.github/workflows/bun-test.yml +32 -0
  4. package/.github/workflows/bun-typecheck.yml +26 -0
  5. package/.vscode/settings.json +1 -1
  6. package/AGENTS.md +1 -0
  7. package/LICENSE +21 -0
  8. package/README.md +101 -91
  9. package/TODO.md +46 -0
  10. package/bunfig.toml +2 -2
  11. package/lib/sexpr/classes/At.ts +15 -0
  12. package/lib/sexpr/classes/Bus.ts +23 -3
  13. package/lib/sexpr/classes/BusEntry.ts +30 -3
  14. package/lib/sexpr/classes/EmbeddedFonts.ts +1 -3
  15. package/lib/sexpr/classes/Footprint.ts +157 -27
  16. package/lib/sexpr/classes/FootprintAttr.ts +3 -1
  17. package/lib/sexpr/classes/FootprintModel.ts +1 -4
  18. package/lib/sexpr/classes/FootprintNetTiePadGroups.ts +3 -1
  19. package/lib/sexpr/classes/FootprintPad.ts +206 -54
  20. package/lib/sexpr/classes/FpArc.ts +23 -0
  21. package/lib/sexpr/classes/FpCircle.ts +24 -3
  22. package/lib/sexpr/classes/FpLine.ts +31 -3
  23. package/lib/sexpr/classes/FpPoly.ts +24 -4
  24. package/lib/sexpr/classes/FpRect.ts +24 -3
  25. package/lib/sexpr/classes/FpText.ts +43 -9
  26. package/lib/sexpr/classes/FpTextBox.ts +43 -5
  27. package/lib/sexpr/classes/GrLine.ts +20 -1
  28. package/lib/sexpr/classes/GrText.ts +38 -12
  29. package/lib/sexpr/classes/Image.ts +38 -11
  30. package/lib/sexpr/classes/Junction.ts +36 -4
  31. package/lib/sexpr/classes/KicadPcb.ts +49 -1
  32. package/lib/sexpr/classes/KicadSch.ts +119 -25
  33. package/lib/sexpr/classes/Label.ts +45 -5
  34. package/lib/sexpr/classes/NoConnect.ts +20 -3
  35. package/lib/sexpr/classes/PadLayers.ts +13 -1
  36. package/lib/sexpr/classes/PadOptions.ts +4 -5
  37. package/lib/sexpr/classes/PadPrimitiveGrArc.ts +22 -4
  38. package/lib/sexpr/classes/PadPrimitiveGrCircle.ts +23 -4
  39. package/lib/sexpr/classes/PadPrimitives.ts +3 -1
  40. package/lib/sexpr/classes/PadSize.ts +15 -0
  41. package/lib/sexpr/classes/PadTeardrops.ts +3 -1
  42. package/lib/sexpr/classes/PcbGeneral.ts +14 -7
  43. package/lib/sexpr/classes/PcbLayerDefinition.ts +5 -1
  44. package/lib/sexpr/classes/Property.ts +64 -9
  45. package/lib/sexpr/classes/Pts.ts +7 -5
  46. package/lib/sexpr/classes/SchematicText.ts +39 -9
  47. package/lib/sexpr/classes/Segment.ts +21 -0
  48. package/lib/sexpr/classes/SegmentNet.ts +3 -1
  49. package/lib/sexpr/classes/Setup/PcbPlotParams.ts +10 -50
  50. package/lib/sexpr/classes/Setup/Setup.ts +12 -11
  51. package/lib/sexpr/classes/Setup/Stackup.ts +14 -19
  52. package/lib/sexpr/classes/Setup/StackupLayerProperties.ts +3 -1
  53. package/lib/sexpr/classes/Setup/StackupProperties.ts +0 -1
  54. package/lib/sexpr/classes/Setup/base.ts +1 -3
  55. package/lib/sexpr/classes/Setup/setupMultiValueProperties.ts +0 -1
  56. package/lib/sexpr/classes/Sheet.ts +85 -3
  57. package/lib/sexpr/classes/SheetPin.ts +4 -1
  58. package/lib/sexpr/classes/Symbol.ts +176 -51
  59. package/lib/sexpr/classes/TextEffects.ts +25 -8
  60. package/lib/sexpr/classes/TitleBlock.ts +21 -4
  61. package/lib/sexpr/classes/Via.ts +38 -3
  62. package/lib/sexpr/classes/ViaNet.ts +2 -1
  63. package/lib/sexpr/classes/Wire.ts +23 -3
  64. package/lib/sexpr/classes/Xy.ts +1 -3
  65. package/lib/sexpr/classes/Zone.ts +1 -3
  66. package/lib/sexpr/parseToPrimitiveSExpr.ts +6 -1
  67. package/lib/sexpr/utils/strokeFromArgs.ts +5 -6
  68. package/lib/sexpr/utils/toStringValue.ts +2 -1
  69. package/package.json +2 -1
  70. package/scripts/download-references.ts +24 -22
  71. package/tests/fixtures/expectEqualPrimitiveSExpr.ts +6 -7
  72. package/tests/fixtures/png-matcher.ts +109 -0
  73. package/tests/fixtures/preload.ts +1 -0
  74. package/tests/sexpr/classes/FootprintPad.test.ts +8 -1
  75. package/tests/sexpr/classes/Image.test.ts +9 -1
  76. package/tests/sexpr/classes/KicadSch.test.ts +1 -3
  77. package/tests/sexpr/classes/Setup.test.ts +0 -1
  78. package/bun.lock +0 -48
@@ -1,7 +1,7 @@
1
1
  import { SxClass } from "../base-classes/SxClass"
2
2
  import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
3
  import { quoteSExprString } from "../utils/quoteSExprString"
4
- import { At } from "./At"
4
+ import { At, type AtInput } from "./At"
5
5
  import { FootprintAttr } from "./FootprintAttr"
6
6
  import { FootprintAutoplaceCost180 } from "./FootprintAutoplaceCost180"
7
7
  import { FootprintAutoplaceCost90 } from "./FootprintAutoplaceCost90"
@@ -80,6 +80,43 @@ const MULTI_TOKENS = new Set([
80
80
 
81
81
  const SUPPORTED_TOKENS = new Set([...SINGLE_TOKENS, ...MULTI_TOKENS])
82
82
 
83
+ export interface FootprintConstructorParams {
84
+ libraryLink?: string
85
+ locked?: boolean
86
+ placed?: boolean
87
+ layer?: Layer | string | string[]
88
+ uuid?: Uuid | string
89
+ at?: AtInput | Xy
90
+ descr?: string | FootprintDescr
91
+ tags?: string | string[] | FootprintTags
92
+ path?: string | FootprintPath
93
+ autoplaceCost90?: number | FootprintAutoplaceCost90
94
+ autoplaceCost180?: number | FootprintAutoplaceCost180
95
+ solderMaskMargin?: number | FootprintSolderMaskMargin
96
+ solderPasteMargin?: number | FootprintSolderPasteMargin
97
+ solderPasteRatio?: number | FootprintSolderPasteRatio
98
+ clearance?: number | FootprintClearance
99
+ zoneConnect?: number | FootprintZoneConnect
100
+ thermalWidth?: number | FootprintThermalWidth
101
+ thermalGap?: number | FootprintThermalGap
102
+ attr?: FootprintAttr
103
+ privateLayers?: FootprintPrivateLayers
104
+ netTiePadGroups?: FootprintNetTiePadGroups
105
+ sheetname?: string | FootprintSheetname
106
+ sheetfile?: string | FootprintSheetfile
107
+ embeddedFonts?: EmbeddedFonts
108
+ properties?: Property[]
109
+ fpTexts?: FpText[]
110
+ fpTextBoxes?: FpTextBox[]
111
+ fpLines?: FpLine[]
112
+ fpRects?: FpRect[]
113
+ fpCircles?: FpCircle[]
114
+ fpArcs?: FpArc[]
115
+ fpPolys?: FpPoly[]
116
+ pads?: FootprintPad[]
117
+ models?: FootprintModel[]
118
+ }
119
+
83
120
  export class Footprint extends SxClass {
84
121
  static override token = "footprint"
85
122
  token = "footprint"
@@ -87,7 +124,7 @@ export class Footprint extends SxClass {
87
124
  private _libraryLink?: string
88
125
  private _sxLocked?: FootprintLocked
89
126
  private _sxPlaced?: FootprintPlaced
90
-
127
+
91
128
  private _sxLayer?: Layer
92
129
  private _sxTedit?: FootprintTedit
93
130
  private _sxUuid?: Uuid
@@ -123,6 +160,53 @@ export class Footprint extends SxClass {
123
160
  private _fpPads: FootprintPad[] = []
124
161
  private _models: FootprintModel[] = []
125
162
 
163
+ constructor(params: FootprintConstructorParams = {}) {
164
+ super()
165
+ if (params.libraryLink !== undefined) this.libraryLink = params.libraryLink
166
+ if (params.locked !== undefined) this.locked = params.locked
167
+ if (params.placed !== undefined) this.placed = params.placed
168
+ if (params.layer !== undefined) this.layer = params.layer
169
+ if (params.uuid !== undefined) this.uuid = params.uuid
170
+ if (params.at !== undefined) this.position = params.at
171
+ if (params.descr !== undefined) this.descr = params.descr
172
+ if (params.tags !== undefined) this.tags = params.tags
173
+ if (params.path !== undefined) this.path = params.path
174
+ if (params.autoplaceCost90 !== undefined)
175
+ this.autoplaceCost90 = params.autoplaceCost90
176
+ if (params.autoplaceCost180 !== undefined)
177
+ this.autoplaceCost180 = params.autoplaceCost180
178
+ if (params.solderMaskMargin !== undefined)
179
+ this.solderMaskMargin = params.solderMaskMargin
180
+ if (params.solderPasteMargin !== undefined)
181
+ this.solderPasteMargin = params.solderPasteMargin
182
+ if (params.solderPasteRatio !== undefined)
183
+ this.solderPasteRatio = params.solderPasteRatio
184
+ if (params.clearance !== undefined) this.clearance = params.clearance
185
+ if (params.zoneConnect !== undefined) this.zoneConnect = params.zoneConnect
186
+ if (params.thermalWidth !== undefined)
187
+ this.thermalWidth = params.thermalWidth
188
+ if (params.thermalGap !== undefined) this.thermalGap = params.thermalGap
189
+ if (params.attr !== undefined) this.attr = params.attr
190
+ if (params.privateLayers !== undefined)
191
+ this.privateLayers = params.privateLayers
192
+ if (params.netTiePadGroups !== undefined)
193
+ this.netTiePadGroups = params.netTiePadGroups
194
+ if (params.sheetname !== undefined) this.sheetname = params.sheetname
195
+ if (params.sheetfile !== undefined) this.sheetfile = params.sheetfile
196
+ if (params.embeddedFonts !== undefined)
197
+ this.embeddedFonts = params.embeddedFonts
198
+ if (params.properties !== undefined) this.properties = params.properties
199
+ if (params.fpTexts !== undefined) this.fpTexts = params.fpTexts
200
+ if (params.fpTextBoxes !== undefined) this.fpTextBoxes = params.fpTextBoxes
201
+ if (params.fpLines !== undefined) this.fpLines = params.fpLines
202
+ if (params.fpRects !== undefined) this.fpRects = params.fpRects
203
+ if (params.fpCircles !== undefined) this.fpCircles = params.fpCircles
204
+ if (params.fpArcs !== undefined) this.fpArcs = params.fpArcs
205
+ if (params.fpPolys !== undefined) this.fpPolys = params.fpPolys
206
+ if (params.pads !== undefined) this.fpPads = params.pads
207
+ if (params.models !== undefined) this.models = params.models
208
+ }
209
+
126
210
  static override fromSexprPrimitives(
127
211
  primitiveSexprs: PrimitiveSExpr[],
128
212
  ): Footprint {
@@ -156,16 +240,22 @@ export class Footprint extends SxClass {
156
240
 
157
241
  for (const token of Object.keys(propertyMap)) {
158
242
  if (!SUPPORTED_TOKENS.has(token)) {
159
- throw new Error(`footprint encountered unsupported child token "${token}"`)
243
+ throw new Error(
244
+ `footprint encountered unsupported child token "${token}"`,
245
+ )
160
246
  }
161
247
  }
162
248
 
163
249
  for (const [token, entries] of Object.entries(arrayPropertyMap)) {
164
250
  if (!SUPPORTED_TOKENS.has(token)) {
165
- throw new Error(`footprint encountered unsupported child token "${token}"`)
251
+ throw new Error(
252
+ `footprint encountered unsupported child token "${token}"`,
253
+ )
166
254
  }
167
255
  if (!MULTI_TOKENS.has(token) && entries.length > 1) {
168
- throw new Error(`footprint does not support repeated child token "${token}"`)
256
+ throw new Error(
257
+ `footprint does not support repeated child token "${token}"`,
258
+ )
169
259
  }
170
260
  }
171
261
 
@@ -203,14 +293,18 @@ export class Footprint extends SxClass {
203
293
  footprint._sxSolderPasteRatio = propertyMap.solder_paste_ratio as
204
294
  | FootprintSolderPasteRatio
205
295
  | undefined
206
- footprint._sxClearance = propertyMap.clearance as FootprintClearance | undefined
296
+ footprint._sxClearance = propertyMap.clearance as
297
+ | FootprintClearance
298
+ | undefined
207
299
  footprint._sxZoneConnect = propertyMap.zone_connect as
208
300
  | FootprintZoneConnect
209
301
  | undefined
210
302
  footprint._sxThermalWidth = propertyMap.thermal_width as
211
303
  | FootprintThermalWidth
212
304
  | undefined
213
- footprint._sxThermalGap = propertyMap.thermal_gap as FootprintThermalGap | undefined
305
+ footprint._sxThermalGap = propertyMap.thermal_gap as
306
+ | FootprintThermalGap
307
+ | undefined
214
308
  footprint._sxAttr = propertyMap.attr as FootprintAttr | undefined
215
309
  footprint._sxPrivateLayers = propertyMap.private_layers as
216
310
  | FootprintPrivateLayers
@@ -218,9 +312,15 @@ export class Footprint extends SxClass {
218
312
  footprint._sxNetTiePadGroups = propertyMap.net_tie_pad_groups as
219
313
  | FootprintNetTiePadGroups
220
314
  | undefined
221
- footprint._sxSheetname = propertyMap.sheetname as FootprintSheetname | undefined
222
- footprint._sxSheetfile = propertyMap.sheetfile as FootprintSheetfile | undefined
223
- footprint._sxEmbeddedFonts = propertyMap.embedded_fonts as EmbeddedFonts | undefined
315
+ footprint._sxSheetname = propertyMap.sheetname as
316
+ | FootprintSheetname
317
+ | undefined
318
+ footprint._sxSheetfile = propertyMap.sheetfile as
319
+ | FootprintSheetfile
320
+ | undefined
321
+ footprint._sxEmbeddedFonts = propertyMap.embedded_fonts as
322
+ | EmbeddedFonts
323
+ | undefined
224
324
 
225
325
  footprint._properties = (arrayPropertyMap.property as Property[]) ?? []
226
326
  footprint._fpTexts = (arrayPropertyMap["fp_text"] as FpText[]) ?? []
@@ -321,7 +421,8 @@ export class Footprint extends SxClass {
321
421
  this._sxTedit = undefined
322
422
  return
323
423
  }
324
- this._sxTedit = value instanceof FootprintTedit ? value : new FootprintTedit(value)
424
+ this._sxTedit =
425
+ value instanceof FootprintTedit ? value : new FootprintTedit(value)
325
426
  }
326
427
 
327
428
  get uuid(): Uuid | undefined {
@@ -340,9 +441,9 @@ export class Footprint extends SxClass {
340
441
  return this._sxAt ?? this._sxXy
341
442
  }
342
443
 
343
- set position(value: At | Xy | undefined) {
344
- if (value instanceof At) {
345
- this._sxAt = value
444
+ set position(value: AtInput | Xy | undefined) {
445
+ if (value === undefined) {
446
+ this._sxAt = undefined
346
447
  this._sxXy = undefined
347
448
  return
348
449
  }
@@ -351,7 +452,8 @@ export class Footprint extends SxClass {
351
452
  this._sxAt = undefined
352
453
  return
353
454
  }
354
- this._sxAt = undefined
455
+ // Handle AtInput (At, array, or object)
456
+ this._sxAt = At.from(value as AtInput)
355
457
  this._sxXy = undefined
356
458
  }
357
459
 
@@ -364,19 +466,25 @@ export class Footprint extends SxClass {
364
466
  this._sxDescr = undefined
365
467
  return
366
468
  }
367
- this._sxDescr = value instanceof FootprintDescr ? value : new FootprintDescr(value)
469
+ this._sxDescr =
470
+ value instanceof FootprintDescr ? value : new FootprintDescr(value)
368
471
  }
369
472
 
370
473
  get tags(): FootprintTags | undefined {
371
474
  return this._sxTags
372
475
  }
373
476
 
374
- set tags(value: FootprintTags | string | undefined) {
477
+ set tags(value: string | string[] | FootprintTags | undefined) {
375
478
  if (value === undefined) {
376
479
  this._sxTags = undefined
377
480
  return
378
481
  }
379
- this._sxTags = value instanceof FootprintTags ? value : new FootprintTags(value)
482
+ if (value instanceof FootprintTags) {
483
+ this._sxTags = value
484
+ return
485
+ }
486
+ const tagString = Array.isArray(value) ? value.join(" ") : value
487
+ this._sxTags = new FootprintTags(tagString)
380
488
  }
381
489
 
382
490
  get path(): FootprintPath | undefined {
@@ -388,7 +496,8 @@ export class Footprint extends SxClass {
388
496
  this._sxPath = undefined
389
497
  return
390
498
  }
391
- this._sxPath = value instanceof FootprintPath ? value : new FootprintPath(value)
499
+ this._sxPath =
500
+ value instanceof FootprintPath ? value : new FootprintPath(value)
392
501
  }
393
502
 
394
503
  get autoplaceCost90(): FootprintAutoplaceCost90 | undefined {
@@ -440,7 +549,10 @@ export class Footprint extends SxClass {
440
549
  return this._sxSolderPasteMargin
441
550
  }
442
551
 
443
- set solderPasteMargin(value: FootprintSolderPasteMargin | number | undefined) {
552
+ set solderPasteMargin(value:
553
+ | FootprintSolderPasteMargin
554
+ | number
555
+ | undefined) {
444
556
  if (value === undefined) {
445
557
  this._sxSolderPasteMargin = undefined
446
558
  return
@@ -476,7 +588,9 @@ export class Footprint extends SxClass {
476
588
  return
477
589
  }
478
590
  this._sxClearance =
479
- value instanceof FootprintClearance ? value : new FootprintClearance(value)
591
+ value instanceof FootprintClearance
592
+ ? value
593
+ : new FootprintClearance(value)
480
594
  }
481
595
 
482
596
  get zoneConnect(): FootprintZoneConnect | undefined {
@@ -489,7 +603,9 @@ export class Footprint extends SxClass {
489
603
  return
490
604
  }
491
605
  this._sxZoneConnect =
492
- value instanceof FootprintZoneConnect ? value : new FootprintZoneConnect(value)
606
+ value instanceof FootprintZoneConnect
607
+ ? value
608
+ : new FootprintZoneConnect(value)
493
609
  }
494
610
 
495
611
  get thermalWidth(): FootprintThermalWidth | undefined {
@@ -517,7 +633,9 @@ export class Footprint extends SxClass {
517
633
  return
518
634
  }
519
635
  this._sxThermalGap =
520
- value instanceof FootprintThermalGap ? value : new FootprintThermalGap(value)
636
+ value instanceof FootprintThermalGap
637
+ ? value
638
+ : new FootprintThermalGap(value)
521
639
  }
522
640
 
523
641
  get attr(): FootprintAttr | undefined {
@@ -562,18 +680,30 @@ export class Footprint extends SxClass {
562
680
  return this._sxSheetname?.value
563
681
  }
564
682
 
565
- set sheetname(value: string | undefined) {
683
+ set sheetname(value: string | FootprintSheetname | undefined) {
684
+ if (value === undefined) {
685
+ this._sxSheetname = undefined
686
+ return
687
+ }
566
688
  this._sxSheetname =
567
- value === undefined ? undefined : new FootprintSheetname(value)
689
+ value instanceof FootprintSheetname
690
+ ? value
691
+ : new FootprintSheetname(value)
568
692
  }
569
693
 
570
694
  get sheetfile(): string | undefined {
571
695
  return this._sxSheetfile?.value
572
696
  }
573
697
 
574
- set sheetfile(value: string | undefined) {
698
+ set sheetfile(value: string | FootprintSheetfile | undefined) {
699
+ if (value === undefined) {
700
+ this._sxSheetfile = undefined
701
+ return
702
+ }
575
703
  this._sxSheetfile =
576
- value === undefined ? undefined : new FootprintSheetfile(value)
704
+ value instanceof FootprintSheetfile
705
+ ? value
706
+ : new FootprintSheetfile(value)
577
707
  }
578
708
 
579
709
  get embeddedFonts(): EmbeddedFonts | undefined {
@@ -35,7 +35,9 @@ export class FootprintAttr extends SxClass {
35
35
  attr._type = primitive
36
36
  continue
37
37
  }
38
- throw new Error(`attr encountered duplicate or unknown token "${primitive}"`)
38
+ throw new Error(
39
+ `attr encountered duplicate or unknown token "${primitive}"`,
40
+ )
39
41
  }
40
42
  return attr
41
43
  }
@@ -120,10 +120,7 @@ export class FootprintModel extends SxClass {
120
120
  }
121
121
  SxClass.register(FootprintModel)
122
122
 
123
- function parseVectorArgs(
124
- args: PrimitiveSExpr[],
125
- token: string,
126
- ): ModelVector {
123
+ function parseVectorArgs(args: PrimitiveSExpr[], token: string): ModelVector {
127
124
  if (args.length !== 1) {
128
125
  throw new Error(`model ${token} expects a single xyz child`)
129
126
  }
@@ -43,7 +43,9 @@ export class FootprintNetTiePadGroups extends SxClass {
43
43
  }
44
44
 
45
45
  override getString(): string {
46
- const rendered = this._groups.map((group) => quoteSExprString(group)).join(" ")
46
+ const rendered = this._groups
47
+ .map((group) => quoteSExprString(group))
48
+ .join(" ")
47
49
  return `(net_tie_pad_groups ${rendered})`
48
50
  }
49
51
  }