kicadts 0.0.1

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 (188) hide show
  1. package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
  2. package/.vscode/settings.json +16 -0
  3. package/AGENTS.md +30 -0
  4. package/README.md +206 -0
  5. package/biome.json +93 -0
  6. package/bun.lock +48 -0
  7. package/bunfig.toml +5 -0
  8. package/lib/index.ts +1 -0
  9. package/lib/sexpr/base-classes/SxClass.ts +164 -0
  10. package/lib/sexpr/base-classes/SxPrimitiveBoolean.ts +35 -0
  11. package/lib/sexpr/base-classes/SxPrimitiveNumber.ts +26 -0
  12. package/lib/sexpr/base-classes/SxPrimitiveString.ts +26 -0
  13. package/lib/sexpr/classes/At.ts +38 -0
  14. package/lib/sexpr/classes/Bus.ts +83 -0
  15. package/lib/sexpr/classes/BusEntry.ts +142 -0
  16. package/lib/sexpr/classes/Color.ts +29 -0
  17. package/lib/sexpr/classes/Dnp.ts +8 -0
  18. package/lib/sexpr/classes/EmbeddedFonts.ts +70 -0
  19. package/lib/sexpr/classes/ExcludeFromSim.ts +8 -0
  20. package/lib/sexpr/classes/FieldsAutoplaced.ts +8 -0
  21. package/lib/sexpr/classes/Footprint.ts +719 -0
  22. package/lib/sexpr/classes/FootprintAttr.ts +102 -0
  23. package/lib/sexpr/classes/FootprintAutoplaceCost180.ts +9 -0
  24. package/lib/sexpr/classes/FootprintAutoplaceCost90.ts +9 -0
  25. package/lib/sexpr/classes/FootprintClearance.ts +9 -0
  26. package/lib/sexpr/classes/FootprintDescr.ts +44 -0
  27. package/lib/sexpr/classes/FootprintLocked.ts +32 -0
  28. package/lib/sexpr/classes/FootprintModel.ts +145 -0
  29. package/lib/sexpr/classes/FootprintNetTiePadGroups.ts +50 -0
  30. package/lib/sexpr/classes/FootprintPad.ts +705 -0
  31. package/lib/sexpr/classes/FootprintPath.ts +44 -0
  32. package/lib/sexpr/classes/FootprintPlaced.ts +32 -0
  33. package/lib/sexpr/classes/FootprintPrivateLayers.ts +56 -0
  34. package/lib/sexpr/classes/FootprintSheetfile.ts +44 -0
  35. package/lib/sexpr/classes/FootprintSheetname.ts +44 -0
  36. package/lib/sexpr/classes/FootprintSolderMaskMargin.ts +9 -0
  37. package/lib/sexpr/classes/FootprintSolderPasteMargin.ts +9 -0
  38. package/lib/sexpr/classes/FootprintSolderPasteRatio.ts +9 -0
  39. package/lib/sexpr/classes/FootprintTags.ts +44 -0
  40. package/lib/sexpr/classes/FootprintTedit.ts +21 -0
  41. package/lib/sexpr/classes/FootprintThermalGap.ts +9 -0
  42. package/lib/sexpr/classes/FootprintThermalWidth.ts +9 -0
  43. package/lib/sexpr/classes/FootprintZoneConnect.ts +9 -0
  44. package/lib/sexpr/classes/FpArc.ts +289 -0
  45. package/lib/sexpr/classes/FpCircle.ts +293 -0
  46. package/lib/sexpr/classes/FpLine.ts +288 -0
  47. package/lib/sexpr/classes/FpPoly.ts +266 -0
  48. package/lib/sexpr/classes/FpPolyFill.ts +48 -0
  49. package/lib/sexpr/classes/FpPolyLocked.ts +40 -0
  50. package/lib/sexpr/classes/FpRect.ts +293 -0
  51. package/lib/sexpr/classes/FpText.ts +341 -0
  52. package/lib/sexpr/classes/FpTextBox.ts +412 -0
  53. package/lib/sexpr/classes/GrLine.ts +245 -0
  54. package/lib/sexpr/classes/GrLineAngle.ts +32 -0
  55. package/lib/sexpr/classes/GrLineEnd.ts +61 -0
  56. package/lib/sexpr/classes/GrLineLocked.ts +40 -0
  57. package/lib/sexpr/classes/GrLineStart.ts +61 -0
  58. package/lib/sexpr/classes/GrText.ts +202 -0
  59. package/lib/sexpr/classes/Image.ts +256 -0
  60. package/lib/sexpr/classes/InBom.ts +8 -0
  61. package/lib/sexpr/classes/Junction.ts +134 -0
  62. package/lib/sexpr/classes/KicadPcb.ts +313 -0
  63. package/lib/sexpr/classes/KicadSch.ts +303 -0
  64. package/lib/sexpr/classes/KicadSchGenerator.ts +32 -0
  65. package/lib/sexpr/classes/KicadSchGeneratorVersion.ts +30 -0
  66. package/lib/sexpr/classes/KicadSchVersion.ts +22 -0
  67. package/lib/sexpr/classes/Label.ts +136 -0
  68. package/lib/sexpr/classes/Layer.ts +51 -0
  69. package/lib/sexpr/classes/Layers.ts +47 -0
  70. package/lib/sexpr/classes/LibSymbols.ts +61 -0
  71. package/lib/sexpr/classes/NoConnect.ts +73 -0
  72. package/lib/sexpr/classes/OnBoard.ts +8 -0
  73. package/lib/sexpr/classes/PadChamfer.ts +50 -0
  74. package/lib/sexpr/classes/PadChamferRatio.ts +9 -0
  75. package/lib/sexpr/classes/PadClearance.ts +9 -0
  76. package/lib/sexpr/classes/PadDieLength.ts +9 -0
  77. package/lib/sexpr/classes/PadDrill.ts +145 -0
  78. package/lib/sexpr/classes/PadDrillOffset.ts +54 -0
  79. package/lib/sexpr/classes/PadLayers.ts +59 -0
  80. package/lib/sexpr/classes/PadNet.ts +56 -0
  81. package/lib/sexpr/classes/PadOptions.ts +182 -0
  82. package/lib/sexpr/classes/PadPinFunction.ts +9 -0
  83. package/lib/sexpr/classes/PadPinType.ts +9 -0
  84. package/lib/sexpr/classes/PadPrimitiveGrArc.ts +254 -0
  85. package/lib/sexpr/classes/PadPrimitiveGrCircle.ts +279 -0
  86. package/lib/sexpr/classes/PadPrimitiveGrLine.ts +126 -0
  87. package/lib/sexpr/classes/PadPrimitives.ts +289 -0
  88. package/lib/sexpr/classes/PadRectDelta.ts +57 -0
  89. package/lib/sexpr/classes/PadRoundrectRratio.ts +9 -0
  90. package/lib/sexpr/classes/PadSize.ts +54 -0
  91. package/lib/sexpr/classes/PadSolderMaskMargin.ts +9 -0
  92. package/lib/sexpr/classes/PadSolderPasteMargin.ts +9 -0
  93. package/lib/sexpr/classes/PadSolderPasteMarginRatio.ts +9 -0
  94. package/lib/sexpr/classes/PadTeardrops.ts +208 -0
  95. package/lib/sexpr/classes/PadThermalBridgeAngle.ts +9 -0
  96. package/lib/sexpr/classes/PadThermalGap.ts +9 -0
  97. package/lib/sexpr/classes/PadThermalWidth.ts +9 -0
  98. package/lib/sexpr/classes/PadZoneConnect.ts +9 -0
  99. package/lib/sexpr/classes/Paper.ts +119 -0
  100. package/lib/sexpr/classes/PcbGeneral.ts +75 -0
  101. package/lib/sexpr/classes/PcbGeneralLegacyTeardrops.ts +44 -0
  102. package/lib/sexpr/classes/PcbGeneralThickness.ts +9 -0
  103. package/lib/sexpr/classes/PcbGenerator.ts +16 -0
  104. package/lib/sexpr/classes/PcbGeneratorVersion.ts +16 -0
  105. package/lib/sexpr/classes/PcbLayerDefinition.ts +102 -0
  106. package/lib/sexpr/classes/PcbLayers.ts +34 -0
  107. package/lib/sexpr/classes/PcbNet.ts +56 -0
  108. package/lib/sexpr/classes/PcbVersion.ts +9 -0
  109. package/lib/sexpr/classes/Property.ts +246 -0
  110. package/lib/sexpr/classes/PropertyHide.ts +9 -0
  111. package/lib/sexpr/classes/PropertyUnlocked.ts +9 -0
  112. package/lib/sexpr/classes/Pts.ts +65 -0
  113. package/lib/sexpr/classes/RenderCache.ts +221 -0
  114. package/lib/sexpr/classes/SchematicText.ts +141 -0
  115. package/lib/sexpr/classes/Segment.ts +222 -0
  116. package/lib/sexpr/classes/SegmentEnd.ts +59 -0
  117. package/lib/sexpr/classes/SegmentLocked.ts +33 -0
  118. package/lib/sexpr/classes/SegmentNet.ts +62 -0
  119. package/lib/sexpr/classes/SegmentStart.ts +59 -0
  120. package/lib/sexpr/classes/Setup/PcbPlotParams.ts +729 -0
  121. package/lib/sexpr/classes/Setup/PcbPlotParamsBase.ts +9 -0
  122. package/lib/sexpr/classes/Setup/PcbPlotParamsNumericProperties.ts +105 -0
  123. package/lib/sexpr/classes/Setup/PcbPlotParamsStringPropertiesA.ts +104 -0
  124. package/lib/sexpr/classes/Setup/PcbPlotParamsStringPropertiesB.ts +105 -0
  125. package/lib/sexpr/classes/Setup/Setup.ts +573 -0
  126. package/lib/sexpr/classes/Setup/SetupPropertyTypes.ts +119 -0
  127. package/lib/sexpr/classes/Setup/Stackup.ts +140 -0
  128. package/lib/sexpr/classes/Setup/StackupLayer.ts +233 -0
  129. package/lib/sexpr/classes/Setup/StackupLayerProperties.ts +78 -0
  130. package/lib/sexpr/classes/Setup/StackupProperties.ts +41 -0
  131. package/lib/sexpr/classes/Setup/base.ts +167 -0
  132. package/lib/sexpr/classes/Setup/index.ts +14 -0
  133. package/lib/sexpr/classes/Setup/setupMultiValueProperties.ts +54 -0
  134. package/lib/sexpr/classes/Setup/setupNumericProperties.ts +151 -0
  135. package/lib/sexpr/classes/Setup/setupPropertyHandlers.ts +90 -0
  136. package/lib/sexpr/classes/Setup/setupStringProperties.ts +75 -0
  137. package/lib/sexpr/classes/Sheet.ts +205 -0
  138. package/lib/sexpr/classes/SheetFill.ts +44 -0
  139. package/lib/sexpr/classes/SheetInstances.ts +168 -0
  140. package/lib/sexpr/classes/SheetInstancesRoot.ts +165 -0
  141. package/lib/sexpr/classes/SheetPin.ts +122 -0
  142. package/lib/sexpr/classes/SheetProperty.ts +115 -0
  143. package/lib/sexpr/classes/SheetSize.ts +44 -0
  144. package/lib/sexpr/classes/Stroke.ts +58 -0
  145. package/lib/sexpr/classes/StrokeType.ts +34 -0
  146. package/lib/sexpr/classes/Symbol.ts +1541 -0
  147. package/lib/sexpr/classes/TextEffects.ts +444 -0
  148. package/lib/sexpr/classes/TitleBlock.ts +352 -0
  149. package/lib/sexpr/classes/Unit.ts +28 -0
  150. package/lib/sexpr/classes/Uuid.ts +8 -0
  151. package/lib/sexpr/classes/Via.ts +328 -0
  152. package/lib/sexpr/classes/ViaNet.ts +59 -0
  153. package/lib/sexpr/classes/Width.ts +8 -0
  154. package/lib/sexpr/classes/Wire.ts +91 -0
  155. package/lib/sexpr/classes/Xy.ts +35 -0
  156. package/lib/sexpr/classes/Zone.ts +41 -0
  157. package/lib/sexpr/index.ts +130 -0
  158. package/lib/sexpr/parseKicadSexpr.ts +5 -0
  159. package/lib/sexpr/parseToPrimitiveSExpr.ts +240 -0
  160. package/lib/sexpr/utils/indentLines.ts +3 -0
  161. package/lib/sexpr/utils/parseYesNo.ts +12 -0
  162. package/lib/sexpr/utils/quoteSExprString.ts +8 -0
  163. package/lib/sexpr/utils/strokeFromArgs.ts +19 -0
  164. package/lib/sexpr/utils/toNumberValue.ts +13 -0
  165. package/lib/sexpr/utils/toStringValue.ts +10 -0
  166. package/package.json +26 -0
  167. package/scripts/download-references.ts +66 -0
  168. package/tests/fixtures/expectEqualPrimitiveSExpr.ts +200 -0
  169. package/tests/sexpr/KicadPcbDemos.test.ts +48 -0
  170. package/tests/sexpr/KicadSchDemos.test.ts +49 -0
  171. package/tests/sexpr/classes/Footprint.test.ts +277 -0
  172. package/tests/sexpr/classes/FootprintPad.test.ts +71 -0
  173. package/tests/sexpr/classes/FpArc.test.ts +45 -0
  174. package/tests/sexpr/classes/FpCircle.test.ts +39 -0
  175. package/tests/sexpr/classes/FpPoly.test.ts +43 -0
  176. package/tests/sexpr/classes/FpRect.test.ts +40 -0
  177. package/tests/sexpr/classes/FpTextBox.test.ts +84 -0
  178. package/tests/sexpr/classes/Image.test.ts +50 -0
  179. package/tests/sexpr/classes/KicadSch.test.ts +97 -0
  180. package/tests/sexpr/classes/Paper.test.ts +30 -0
  181. package/tests/sexpr/classes/Property.test.ts +48 -0
  182. package/tests/sexpr/classes/Setup.test.ts +189 -0
  183. package/tests/sexpr/classes/Sheet.test.ts +107 -0
  184. package/tests/sexpr/classes/Stroke.test.ts +15 -0
  185. package/tests/sexpr/classes/Symbol.test.ts +96 -0
  186. package/tests/sexpr/classes/TextEffects.test.ts +56 -0
  187. package/tests/sexpr/classes/TitleBlock.test.ts +40 -0
  188. package/tsconfig.json +35 -0
@@ -0,0 +1,246 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { printSExpr } from "../parseToPrimitiveSExpr"
4
+ import { quoteSExprString } from "../utils/quoteSExprString"
5
+ import { toStringValue } from "../utils/toStringValue"
6
+ import { At } from "./At"
7
+ import { Layer } from "./Layer"
8
+ import { PropertyHide } from "./PropertyHide"
9
+ import { PropertyUnlocked } from "./PropertyUnlocked"
10
+ import { TextEffects } from "./TextEffects"
11
+ import { Uuid } from "./Uuid"
12
+ import { Xy } from "./Xy"
13
+
14
+ const SUPPORTED_SINGLE_CHILDREN = new Set([
15
+ "at",
16
+ "xy",
17
+ "layer",
18
+ "uuid",
19
+ "effects",
20
+ "unlocked",
21
+ "hide",
22
+ ])
23
+
24
+ const ensureSingleChild = (
25
+ arrayPropertyMap: Record<string, SxClass[]>,
26
+ token: string,
27
+ ) => {
28
+ const entries = arrayPropertyMap[token]
29
+ if (entries && entries.length > 1) {
30
+ throw new Error(`property does not support repeated "${token}" children`)
31
+ }
32
+ }
33
+
34
+ const primitiveToString = (value: PrimitiveSExpr | undefined): string | undefined => {
35
+ if (value === undefined) {
36
+ return undefined
37
+ }
38
+ const asString = toStringValue(value)
39
+ if (asString !== undefined) {
40
+ return asString
41
+ }
42
+ return printSExpr(value)
43
+ }
44
+
45
+ export class Property extends SxClass {
46
+ static override token = "property"
47
+ token = "property"
48
+
49
+ private _key = ""
50
+ private _value = ""
51
+ private _sxAt?: At
52
+ private _sxXy?: Xy
53
+ private _sxLayer?: Layer
54
+ private _sxUuid?: Uuid
55
+ private _sxEffects?: TextEffects
56
+ private _sxUnlocked?: PropertyUnlocked
57
+ private _sxHide?: PropertyHide
58
+
59
+ constructor(key = "", value = "") {
60
+ super()
61
+ this._key = key
62
+ this._value = value
63
+ }
64
+
65
+ static override fromSexprPrimitives(
66
+ primitiveSexprs: PrimitiveSExpr[],
67
+ ): Property {
68
+ if (primitiveSexprs.length < 2) {
69
+ throw new Error("property requires key and value arguments")
70
+ }
71
+
72
+ const [rawKey, rawValue, ...rest] = primitiveSexprs
73
+
74
+ const key = primitiveToString(rawKey)
75
+ const value = primitiveToString(rawValue)
76
+ if (key === undefined) {
77
+ throw new Error("property key must be a printable value")
78
+ }
79
+ if (value === undefined) {
80
+ throw new Error("property value must be a printable value")
81
+ }
82
+
83
+ for (const primitive of rest) {
84
+ if (!Array.isArray(primitive)) {
85
+ throw new Error(
86
+ `property encountered unexpected primitive child: ${JSON.stringify(primitive)}`,
87
+ )
88
+ }
89
+ if (primitive.length === 0 || typeof primitive[0] !== "string") {
90
+ throw new Error(
91
+ `property encountered invalid child expression: ${JSON.stringify(primitive)}`,
92
+ )
93
+ }
94
+ }
95
+
96
+ const property = new Property(key, value)
97
+
98
+ const { propertyMap, arrayPropertyMap } =
99
+ SxClass.parsePrimitivesToClassProperties(rest, this.token)
100
+
101
+ for (const token of Object.keys(propertyMap)) {
102
+ if (!SUPPORTED_SINGLE_CHILDREN.has(token)) {
103
+ throw new Error(
104
+ `property encountered unsupported child token "${token}"`,
105
+ )
106
+ }
107
+ }
108
+
109
+ for (const token of Object.keys(arrayPropertyMap)) {
110
+ if (!SUPPORTED_SINGLE_CHILDREN.has(token)) {
111
+ throw new Error(
112
+ `property encountered unsupported child token "${token}"`,
113
+ )
114
+ }
115
+ ensureSingleChild(arrayPropertyMap, token)
116
+ }
117
+
118
+ const at = propertyMap.at as At | undefined
119
+ const xy = propertyMap.xy as Xy | undefined
120
+ if (at && xy) {
121
+ throw new Error("property can't include both at and xy children")
122
+ }
123
+
124
+ property._sxAt = at
125
+ property._sxXy = xy
126
+ property._sxLayer = propertyMap.layer as Layer | undefined
127
+ property._sxUuid = propertyMap.uuid as Uuid | undefined
128
+ property._sxEffects = propertyMap.effects as TextEffects | undefined
129
+ property._sxUnlocked = propertyMap.unlocked as PropertyUnlocked | undefined
130
+ property._sxHide = propertyMap.hide as PropertyHide | undefined
131
+
132
+ return property
133
+ }
134
+
135
+ get key(): string {
136
+ return this._key
137
+ }
138
+
139
+ set key(value: string) {
140
+ this._key = value
141
+ }
142
+
143
+ get value(): string {
144
+ return this._value
145
+ }
146
+
147
+ set value(value: string) {
148
+ this._value = value
149
+ }
150
+
151
+ get position(): At | Xy | undefined {
152
+ return this._sxAt ?? this._sxXy
153
+ }
154
+
155
+ set position(value: At | Xy | undefined) {
156
+ if (value instanceof At) {
157
+ this._sxAt = value
158
+ this._sxXy = undefined
159
+ return
160
+ }
161
+ if (value instanceof Xy) {
162
+ this._sxXy = value
163
+ this._sxAt = undefined
164
+ return
165
+ }
166
+ this._sxAt = undefined
167
+ this._sxXy = undefined
168
+ }
169
+
170
+ get layer(): Layer | undefined {
171
+ return this._sxLayer
172
+ }
173
+
174
+ set layer(value: Layer | Array<string | number> | string | undefined) {
175
+ if (value === undefined) {
176
+ this._sxLayer = undefined
177
+ return
178
+ }
179
+ if (value instanceof Layer) {
180
+ this._sxLayer = value
181
+ return
182
+ }
183
+ const names = Array.isArray(value) ? value : [value]
184
+ this._sxLayer = new Layer(names)
185
+ }
186
+
187
+ get uuid(): Uuid | undefined {
188
+ return this._sxUuid
189
+ }
190
+
191
+ set uuid(value: Uuid | string | undefined) {
192
+ if (value === undefined) {
193
+ this._sxUuid = undefined
194
+ return
195
+ }
196
+ this._sxUuid = value instanceof Uuid ? value : new Uuid(value)
197
+ }
198
+
199
+ get effects(): TextEffects | undefined {
200
+ return this._sxEffects
201
+ }
202
+
203
+ set effects(value: TextEffects | undefined) {
204
+ this._sxEffects = value
205
+ }
206
+
207
+ get unlocked(): boolean {
208
+ return this._sxUnlocked?.value ?? false
209
+ }
210
+
211
+ set unlocked(value: boolean) {
212
+ this._sxUnlocked = new PropertyUnlocked(value)
213
+ }
214
+
215
+ get hidden(): boolean {
216
+ return this._sxHide?.value ?? false
217
+ }
218
+
219
+ set hidden(value: boolean) {
220
+ this._sxHide = new PropertyHide(value)
221
+ }
222
+
223
+ override getChildren(): SxClass[] {
224
+ const children: SxClass[] = []
225
+ if (this._sxAt) children.push(this._sxAt)
226
+ if (this._sxXy) children.push(this._sxXy)
227
+ if (this._sxUnlocked) children.push(this._sxUnlocked)
228
+ if (this._sxLayer) children.push(this._sxLayer)
229
+ if (this._sxHide) children.push(this._sxHide)
230
+ if (this._sxUuid) children.push(this._sxUuid)
231
+ if (this._sxEffects) children.push(this._sxEffects)
232
+ return children
233
+ }
234
+
235
+ override getString(): string {
236
+ const lines = [
237
+ `(property ${quoteSExprString(this._key)} ${quoteSExprString(this._value)}`,
238
+ ]
239
+ for (const child of this.getChildren()) {
240
+ lines.push(child.getStringIndented())
241
+ }
242
+ lines.push(")")
243
+ return lines.join("\n")
244
+ }
245
+ }
246
+ SxClass.register(Property)
@@ -0,0 +1,9 @@
1
+ import { SxPrimitiveBoolean } from "../base-classes/SxPrimitiveBoolean"
2
+ import { SxClass } from "../base-classes/SxClass"
3
+
4
+ export class PropertyHide extends SxPrimitiveBoolean {
5
+ static override token = "hide"
6
+ static override parentToken = "property"
7
+ token = "hide"
8
+ }
9
+ SxClass.register(PropertyHide)
@@ -0,0 +1,9 @@
1
+ import { SxPrimitiveBoolean } from "../base-classes/SxPrimitiveBoolean"
2
+ import { SxClass } from "../base-classes/SxClass"
3
+
4
+ export class PropertyUnlocked extends SxPrimitiveBoolean {
5
+ static override token = "unlocked"
6
+ static override parentToken = "property"
7
+ token = "unlocked"
8
+ }
9
+ SxClass.register(PropertyUnlocked)
@@ -0,0 +1,65 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { printSExpr } from "../parseToPrimitiveSExpr"
4
+ import { Xy } from "./Xy"
5
+
6
+ export class Pts extends SxClass {
7
+ static override token = "pts"
8
+ token = "pts"
9
+
10
+ points: Array<Xy>
11
+
12
+ constructor(points: Array<Xy> = []) {
13
+ super()
14
+ this.points = points
15
+ }
16
+
17
+ static override fromSexprPrimitives(
18
+ primitiveSexprs: PrimitiveSExpr[],
19
+ ): Pts {
20
+ const points: Xy[] = []
21
+
22
+ for (const primitive of primitiveSexprs) {
23
+ if (!Array.isArray(primitive) || primitive.length === 0) {
24
+ throw new Error(`Unexpected primitive inside pts: ${printSExpr(primitive)}`)
25
+ }
26
+
27
+ const parsed = SxClass.parsePrimitiveSexpr(primitive, {
28
+ parentToken: this.token,
29
+ })
30
+
31
+ if (parsed instanceof Xy) {
32
+ points.push(parsed)
33
+ continue
34
+ }
35
+
36
+ if (parsed instanceof SxClass) {
37
+ throw new Error(
38
+ `Unsupported child "${parsed.token}" inside pts expression`,
39
+ )
40
+ }
41
+
42
+ throw new Error(`Unable to parse child inside pts: ${printSExpr(primitive)}`)
43
+ }
44
+
45
+ return new Pts(points)
46
+ }
47
+
48
+ override getChildren(): SxClass[] {
49
+ return [...this.points]
50
+ }
51
+
52
+ override getString(): string {
53
+ const lines = ["(pts"]
54
+ for (const point of this.points) {
55
+ const pointString = point.getString()
56
+ const segments = pointString.split("\n")
57
+ for (const segment of segments) {
58
+ lines.push(` ${segment}`)
59
+ }
60
+ }
61
+ lines.push(")")
62
+ return lines.join("\n")
63
+ }
64
+ }
65
+ SxClass.register(Pts)
@@ -0,0 +1,221 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { quoteSExprString } from "../utils/quoteSExprString"
4
+ import { toNumberValue } from "../utils/toNumberValue"
5
+ import { toStringValue } from "../utils/toStringValue"
6
+ import { Pts } from "./Pts"
7
+
8
+ type RenderCacheElement = RenderCachePolygon
9
+
10
+ export class RenderCache extends SxClass {
11
+ static override token = "render_cache"
12
+ override token = "render_cache"
13
+
14
+ private _text: string
15
+ private _angle?: number
16
+ private _offsetX?: number
17
+ private _offsetY?: number
18
+ private _elements: RenderCacheElement[] = []
19
+
20
+ constructor(text = "") {
21
+ super()
22
+ this._text = text
23
+ }
24
+
25
+ static override fromSexprPrimitives(
26
+ primitiveSexprs: PrimitiveSExpr[],
27
+ ): RenderCache {
28
+ if (primitiveSexprs.length === 0) {
29
+ throw new Error("render_cache requires a text argument")
30
+ }
31
+
32
+ const [rawText, ...rest] = primitiveSexprs
33
+ const text = toStringValue(rawText)
34
+ if (text === undefined) {
35
+ throw new Error(
36
+ `render_cache text must be a string value, received ${JSON.stringify(rawText)}`,
37
+ )
38
+ }
39
+
40
+ const renderCache = new RenderCache(text)
41
+
42
+ const numericParams: number[] = []
43
+
44
+ for (const primitive of rest) {
45
+ if (Array.isArray(primitive)) {
46
+ const parsed = SxClass.parsePrimitiveSexpr(primitive, {
47
+ parentToken: this.token,
48
+ })
49
+ if (!(parsed instanceof SxClass)) {
50
+ throw new Error(
51
+ `render_cache child did not resolve to an SxClass: ${JSON.stringify(primitive)}`,
52
+ )
53
+ }
54
+
55
+ if (!(parsed instanceof RenderCachePolygon)) {
56
+ throw new Error(
57
+ `render_cache encountered unsupported child token "${parsed.token}"`,
58
+ )
59
+ }
60
+
61
+ renderCache._elements.push(parsed)
62
+ continue
63
+ }
64
+
65
+ const numeric = toNumberValue(primitive)
66
+ if (numeric === undefined) {
67
+ throw new Error(
68
+ `render_cache encountered non-numeric parameter ${JSON.stringify(primitive)}`,
69
+ )
70
+ }
71
+ numericParams.push(numeric)
72
+ }
73
+
74
+ if (numericParams.length > 0) {
75
+ renderCache._angle = numericParams[0]
76
+ }
77
+ if (numericParams.length > 1) {
78
+ renderCache._offsetX = numericParams[1]
79
+ }
80
+ if (numericParams.length > 2) {
81
+ renderCache._offsetY = numericParams[2]
82
+ }
83
+ if (numericParams.length > 3) {
84
+ throw new Error("render_cache supports at most three numeric parameters")
85
+ }
86
+
87
+ return renderCache
88
+ }
89
+
90
+ get text(): string {
91
+ return this._text
92
+ }
93
+
94
+ set text(value: string) {
95
+ this._text = value
96
+ }
97
+
98
+ get angle(): number | undefined {
99
+ return this._angle
100
+ }
101
+
102
+ set angle(value: number | undefined) {
103
+ this._angle = value
104
+ }
105
+
106
+ get offsetX(): number | undefined {
107
+ return this._offsetX
108
+ }
109
+
110
+ set offsetX(value: number | undefined) {
111
+ this._offsetX = value
112
+ }
113
+
114
+ get offsetY(): number | undefined {
115
+ return this._offsetY
116
+ }
117
+
118
+ set offsetY(value: number | undefined) {
119
+ this._offsetY = value
120
+ }
121
+
122
+ get elements(): RenderCacheElement[] {
123
+ return [...this._elements]
124
+ }
125
+
126
+ set elements(elements: RenderCacheElement[]) {
127
+ this._elements = [...elements]
128
+ }
129
+
130
+ addElement(element: RenderCacheElement) {
131
+ this._elements.push(element)
132
+ }
133
+
134
+ override getChildren(): SxClass[] {
135
+ return [...this._elements]
136
+ }
137
+
138
+ override getString(): string {
139
+ const headerParts: string[] = [quoteSExprString(this._text)]
140
+ if (this._angle !== undefined) {
141
+ headerParts.push(String(this._angle))
142
+ }
143
+ if (this._offsetX !== undefined) {
144
+ headerParts.push(String(this._offsetX))
145
+ }
146
+ if (this._offsetY !== undefined) {
147
+ headerParts.push(String(this._offsetY))
148
+ }
149
+
150
+ if (this._elements.length === 0) {
151
+ return `(render_cache ${headerParts.join(" ")})`
152
+ }
153
+
154
+ const lines = [`(render_cache ${headerParts.join(" ")}`]
155
+ for (const element of this._elements) {
156
+ lines.push(element.getStringIndented())
157
+ }
158
+ lines.push(")")
159
+ return lines.join("\n")
160
+ }
161
+ }
162
+ SxClass.register(RenderCache)
163
+
164
+ class RenderCachePolygon extends SxClass {
165
+ static override token = "polygon"
166
+ static override parentToken = "render_cache"
167
+ override token = "polygon"
168
+
169
+ private _contours: Pts[] = []
170
+
171
+ static override fromSexprPrimitives(
172
+ primitiveSexprs: PrimitiveSExpr[],
173
+ ): RenderCachePolygon {
174
+ const polygon = new RenderCachePolygon()
175
+
176
+ for (const primitive of primitiveSexprs) {
177
+ if (!Array.isArray(primitive) || primitive.length === 0) {
178
+ throw new Error(
179
+ `render_cache polygon encountered invalid child: ${JSON.stringify(primitive)}`,
180
+ )
181
+ }
182
+
183
+ const parsed = SxClass.parsePrimitiveSexpr(primitive, {
184
+ parentToken: this.token,
185
+ })
186
+
187
+ if (!(parsed instanceof SxClass)) {
188
+ throw new Error(
189
+ `render_cache polygon child did not resolve to an SxClass: ${JSON.stringify(primitive)}`,
190
+ )
191
+ }
192
+
193
+ if (!(parsed instanceof Pts)) {
194
+ throw new Error(
195
+ `render_cache polygon encountered unsupported token "${parsed.token}"`,
196
+ )
197
+ }
198
+
199
+ polygon._contours.push(parsed)
200
+ }
201
+
202
+ if (polygon._contours.length === 0) {
203
+ throw new Error("render_cache polygon requires at least one pts child")
204
+ }
205
+
206
+ return polygon
207
+ }
208
+
209
+ get contours(): Pts[] {
210
+ return [...this._contours]
211
+ }
212
+
213
+ set contours(value: Pts[]) {
214
+ this._contours = [...value]
215
+ }
216
+
217
+ override getChildren(): SxClass[] {
218
+ return [...this._contours]
219
+ }
220
+ }
221
+ SxClass.register(RenderCachePolygon)
@@ -0,0 +1,141 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { quoteSExprString } from "../utils/quoteSExprString"
4
+ import { toStringValue } from "../utils/toStringValue"
5
+ import { At } from "./At"
6
+ import { ExcludeFromSim } from "./ExcludeFromSim"
7
+ import { TextEffects } from "./TextEffects"
8
+ import { Uuid } from "./Uuid"
9
+
10
+ const SUPPORTED_TOKENS = new Set([
11
+ "exclude_from_sim",
12
+ "at",
13
+ "effects",
14
+ "uuid",
15
+ ])
16
+
17
+ export class SchematicText extends SxClass {
18
+ static override token = "text"
19
+ static override parentToken = "kicad_sch"
20
+ override token = "text"
21
+
22
+ private _value = ""
23
+ private _sxExcludeFromSim?: ExcludeFromSim
24
+ private _sxAt?: At
25
+ private _sxEffects?: TextEffects
26
+ private _sxUuid?: Uuid
27
+
28
+ static override fromSexprPrimitives(
29
+ primitiveSexprs: PrimitiveSExpr[],
30
+ ): SchematicText {
31
+ const [textPrimitive, ...rest] = primitiveSexprs
32
+ const value = toStringValue(textPrimitive)
33
+ if (value === undefined) {
34
+ throw new Error("text expects a string value")
35
+ }
36
+
37
+ const text = new SchematicText()
38
+ text._value = value
39
+
40
+ const { propertyMap, arrayPropertyMap } =
41
+ SxClass.parsePrimitivesToClassProperties(rest, this.token)
42
+
43
+ const unsupportedTokens = Object.keys(propertyMap).filter(
44
+ (token) => !SUPPORTED_TOKENS.has(token),
45
+ )
46
+ if (unsupportedTokens.length > 0) {
47
+ throw new Error(
48
+ `Unsupported child tokens inside text expression: ${unsupportedTokens.join(", ")}`,
49
+ )
50
+ }
51
+
52
+ for (const [token, entries] of Object.entries(arrayPropertyMap)) {
53
+ if (!SUPPORTED_TOKENS.has(token)) {
54
+ throw new Error(
55
+ `Unsupported child tokens inside text expression: ${token}`,
56
+ )
57
+ }
58
+ if (entries.length > 1) {
59
+ throw new Error(`text does not support repeated child tokens: ${token}`)
60
+ }
61
+ }
62
+
63
+ text._sxExcludeFromSim =
64
+ (arrayPropertyMap.exclude_from_sim?.[0] as ExcludeFromSim | undefined) ??
65
+ (propertyMap.exclude_from_sim as ExcludeFromSim | undefined)
66
+ text._sxAt =
67
+ (arrayPropertyMap.at?.[0] as At | undefined) ??
68
+ (propertyMap.at as At | undefined)
69
+ text._sxEffects =
70
+ (arrayPropertyMap.effects?.[0] as TextEffects | undefined) ??
71
+ (propertyMap.effects as TextEffects | undefined)
72
+ text._sxUuid =
73
+ (arrayPropertyMap.uuid?.[0] as Uuid | undefined) ??
74
+ (propertyMap.uuid as Uuid | undefined)
75
+
76
+ return text
77
+ }
78
+
79
+ get value(): string {
80
+ return this._value
81
+ }
82
+
83
+ set value(newValue: string) {
84
+ this._value = newValue
85
+ }
86
+
87
+ get excludeFromSim(): boolean {
88
+ return this._sxExcludeFromSim?.value ?? false
89
+ }
90
+
91
+ set excludeFromSim(value: boolean) {
92
+ this._sxExcludeFromSim = value ? new ExcludeFromSim(true) : undefined
93
+ }
94
+
95
+ get at(): At | undefined {
96
+ return this._sxAt
97
+ }
98
+
99
+ set at(value: At | undefined) {
100
+ this._sxAt = value
101
+ }
102
+
103
+ get effects(): TextEffects | undefined {
104
+ return this._sxEffects
105
+ }
106
+
107
+ set effects(value: TextEffects | undefined) {
108
+ this._sxEffects = value
109
+ }
110
+
111
+ get uuid(): Uuid | undefined {
112
+ return this._sxUuid
113
+ }
114
+
115
+ set uuid(value: Uuid | string | undefined) {
116
+ if (value === undefined) {
117
+ this._sxUuid = undefined
118
+ return
119
+ }
120
+ this._sxUuid = value instanceof Uuid ? value : new Uuid(value)
121
+ }
122
+
123
+ override getChildren(): SxClass[] {
124
+ const children: SxClass[] = []
125
+ if (this._sxExcludeFromSim) children.push(this._sxExcludeFromSim)
126
+ if (this._sxAt) children.push(this._sxAt)
127
+ if (this._sxEffects) children.push(this._sxEffects)
128
+ if (this._sxUuid) children.push(this._sxUuid)
129
+ return children
130
+ }
131
+
132
+ override getString(): string {
133
+ const lines = [`(text ${quoteSExprString(this._value)}`]
134
+ for (const child of this.getChildren()) {
135
+ lines.push(child.getStringIndented())
136
+ }
137
+ lines.push(")")
138
+ return lines.join("\n")
139
+ }
140
+ }
141
+ SxClass.register(SchematicText)