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,293 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { Layer } from "./Layer"
4
+ import { Stroke } from "./Stroke"
5
+ import { Uuid } from "./Uuid"
6
+ import { Width } from "./Width"
7
+ import { toNumberValue } from "../utils/toNumberValue"
8
+ import { toStringValue } from "../utils/toStringValue"
9
+
10
+ export class FpCircle extends SxClass {
11
+ static override token = "fp_circle"
12
+ token = "fp_circle"
13
+
14
+ private _sxCenter?: FpCircleCenter
15
+ private _sxEnd?: FpCircleEnd
16
+ private _sxLayer?: Layer
17
+ private _sxWidth?: Width
18
+ private _sxStroke?: Stroke
19
+ private _sxFill?: FpCircleFill
20
+ private _sxUuid?: Uuid
21
+ private _locked = false
22
+
23
+ static override fromSexprPrimitives(
24
+ primitiveSexprs: PrimitiveSExpr[],
25
+ ): FpCircle {
26
+ const circle = new FpCircle()
27
+
28
+ const { propertyMap } = SxClass.parsePrimitivesToClassProperties(
29
+ primitiveSexprs,
30
+ this.token,
31
+ )
32
+
33
+ circle._sxCenter = propertyMap.center as FpCircleCenter | undefined
34
+ circle._sxEnd = propertyMap.end as FpCircleEnd | undefined
35
+ circle._sxLayer = propertyMap.layer as Layer | undefined
36
+ circle._sxWidth = propertyMap.width as Width | undefined
37
+ circle._sxStroke = propertyMap.stroke as Stroke | undefined
38
+ circle._sxFill = propertyMap.fill as FpCircleFill | undefined
39
+ circle._sxUuid = propertyMap.uuid as Uuid | undefined
40
+
41
+ for (const primitive of primitiveSexprs) {
42
+ if (primitive === "locked") {
43
+ circle._locked = true
44
+ }
45
+ }
46
+
47
+ return circle
48
+ }
49
+
50
+ get center(): FpCircleCenter | undefined {
51
+ return this._sxCenter
52
+ }
53
+
54
+ set center(
55
+ value: FpCircleCenter | { x: number; y: number } | undefined,
56
+ ) {
57
+ if (value === undefined) {
58
+ this._sxCenter = undefined
59
+ return
60
+ }
61
+ if (value instanceof FpCircleCenter) {
62
+ this._sxCenter = value
63
+ return
64
+ }
65
+ this._sxCenter = new FpCircleCenter(value.x, value.y)
66
+ }
67
+
68
+ get end(): FpCircleEnd | undefined {
69
+ return this._sxEnd
70
+ }
71
+
72
+ set end(value: FpCircleEnd | { x: number; y: number } | undefined) {
73
+ if (value === undefined) {
74
+ this._sxEnd = undefined
75
+ return
76
+ }
77
+ if (value instanceof FpCircleEnd) {
78
+ this._sxEnd = value
79
+ return
80
+ }
81
+ this._sxEnd = new FpCircleEnd(value.x, value.y)
82
+ }
83
+
84
+ get layer(): Layer | undefined {
85
+ return this._sxLayer
86
+ }
87
+
88
+ set layer(value: Layer | string | string[] | undefined) {
89
+ if (value === undefined) {
90
+ this._sxLayer = undefined
91
+ return
92
+ }
93
+ if (value instanceof Layer) {
94
+ this._sxLayer = value
95
+ return
96
+ }
97
+ const names = Array.isArray(value) ? value : [value]
98
+ this._sxLayer = new Layer(names)
99
+ }
100
+
101
+ get width(): number | undefined {
102
+ return this._sxWidth?.value
103
+ }
104
+
105
+ set width(value: number | Width | undefined) {
106
+ if (value === undefined) {
107
+ this._sxWidth = undefined
108
+ return
109
+ }
110
+ if (value instanceof Width) {
111
+ this._sxWidth = value
112
+ return
113
+ }
114
+ this._sxWidth = new Width(value)
115
+ }
116
+
117
+ get widthClass(): Width | undefined {
118
+ return this._sxWidth
119
+ }
120
+
121
+ get stroke(): Stroke | undefined {
122
+ return this._sxStroke
123
+ }
124
+
125
+ set stroke(value: Stroke | undefined) {
126
+ this._sxStroke = value
127
+ }
128
+
129
+ get fill(): boolean | undefined {
130
+ return this._sxFill?.filled
131
+ }
132
+
133
+ set fill(value: boolean | FpCircleFill | undefined) {
134
+ if (value === undefined) {
135
+ this._sxFill = undefined
136
+ return
137
+ }
138
+ if (value instanceof FpCircleFill) {
139
+ this._sxFill = value
140
+ return
141
+ }
142
+ this._sxFill = new FpCircleFill(value)
143
+ }
144
+
145
+ get fillClass(): FpCircleFill | undefined {
146
+ return this._sxFill
147
+ }
148
+
149
+ get uuid(): string | undefined {
150
+ return this._sxUuid?.value
151
+ }
152
+
153
+ set uuid(value: string | Uuid | undefined) {
154
+ if (value === undefined) {
155
+ this._sxUuid = undefined
156
+ return
157
+ }
158
+ if (value instanceof Uuid) {
159
+ this._sxUuid = value
160
+ return
161
+ }
162
+ this._sxUuid = new Uuid(value)
163
+ }
164
+
165
+ get uuidClass(): Uuid | undefined {
166
+ return this._sxUuid
167
+ }
168
+
169
+ get locked(): boolean {
170
+ return this._locked
171
+ }
172
+
173
+ set locked(value: boolean) {
174
+ this._locked = value
175
+ }
176
+
177
+ override getChildren(): SxClass[] {
178
+ const children: SxClass[] = []
179
+ if (this._sxCenter) children.push(this._sxCenter)
180
+ if (this._sxEnd) children.push(this._sxEnd)
181
+ if (this._sxLayer) children.push(this._sxLayer)
182
+ if (this._sxWidth) children.push(this._sxWidth)
183
+ if (this._sxStroke) children.push(this._sxStroke)
184
+ if (this._sxFill) children.push(this._sxFill)
185
+ if (this._sxUuid) children.push(this._sxUuid)
186
+ return children
187
+ }
188
+
189
+ override getString(): string {
190
+ const lines = ["(fp_circle"]
191
+ const push = (value?: SxClass) => {
192
+ if (!value) return
193
+ lines.push(value.getStringIndented())
194
+ }
195
+
196
+ push(this._sxCenter)
197
+ push(this._sxEnd)
198
+ push(this._sxLayer)
199
+ push(this._sxWidth)
200
+ push(this._sxStroke)
201
+ push(this._sxFill)
202
+ push(this._sxUuid)
203
+
204
+ if (this._locked) {
205
+ lines.push(" locked")
206
+ }
207
+
208
+ lines.push(")")
209
+ return lines.join("\n")
210
+ }
211
+ }
212
+ SxClass.register(FpCircle)
213
+
214
+ export class FpCircleCenter extends SxClass {
215
+ static override token = "center"
216
+ static override parentToken = "fp_circle"
217
+ token = "center"
218
+
219
+ x: number
220
+ y: number
221
+
222
+ constructor(x: number, y: number) {
223
+ super()
224
+ this.x = x
225
+ this.y = y
226
+ }
227
+
228
+ static override fromSexprPrimitives(
229
+ primitiveSexprs: PrimitiveSExpr[],
230
+ ): FpCircleCenter {
231
+ const x = toNumberValue(primitiveSexprs[0]) ?? 0
232
+ const y = toNumberValue(primitiveSexprs[1]) ?? 0
233
+ return new FpCircleCenter(x, y)
234
+ }
235
+
236
+ override getString(): string {
237
+ return `(center ${this.x} ${this.y})`
238
+ }
239
+ }
240
+ SxClass.register(FpCircleCenter)
241
+
242
+ export class FpCircleEnd extends SxClass {
243
+ static override token = "end"
244
+ static override parentToken = "fp_circle"
245
+ token = "end"
246
+
247
+ x: number
248
+ y: number
249
+
250
+ constructor(x: number, y: number) {
251
+ super()
252
+ this.x = x
253
+ this.y = y
254
+ }
255
+
256
+ static override fromSexprPrimitives(
257
+ primitiveSexprs: PrimitiveSExpr[],
258
+ ): FpCircleEnd {
259
+ const x = toNumberValue(primitiveSexprs[0]) ?? 0
260
+ const y = toNumberValue(primitiveSexprs[1]) ?? 0
261
+ return new FpCircleEnd(x, y)
262
+ }
263
+
264
+ override getString(): string {
265
+ return `(end ${this.x} ${this.y})`
266
+ }
267
+ }
268
+ SxClass.register(FpCircleEnd)
269
+
270
+ export class FpCircleFill extends SxClass {
271
+ static override token = "fill"
272
+ static override parentToken = "fp_circle"
273
+ token = "fill"
274
+
275
+ filled: boolean
276
+
277
+ constructor(filled: boolean) {
278
+ super()
279
+ this.filled = filled
280
+ }
281
+
282
+ static override fromSexprPrimitives(
283
+ primitiveSexprs: PrimitiveSExpr[],
284
+ ): FpCircleFill {
285
+ const state = toStringValue(primitiveSexprs[0])
286
+ return new FpCircleFill(state === "yes")
287
+ }
288
+
289
+ override getString(): string {
290
+ return `(fill ${this.filled ? "yes" : "no"})`
291
+ }
292
+ }
293
+ SxClass.register(FpCircleFill)
@@ -0,0 +1,288 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { Layer } from "./Layer"
4
+ import { Stroke } from "./Stroke"
5
+ import { Uuid } from "./Uuid"
6
+ import { Width } from "./Width"
7
+
8
+ const SUPPORTED_SINGLE_TOKENS = new Set([
9
+ "start",
10
+ "end",
11
+ "layer",
12
+ "width",
13
+ "stroke",
14
+ "uuid",
15
+ ])
16
+
17
+ export class FpLine extends SxClass {
18
+ static override token = "fp_line"
19
+ static override parentToken = "footprint"
20
+ override token = "fp_line"
21
+
22
+ private _sxStart?: FpLineStart
23
+ private _sxEnd?: FpLineEnd
24
+ private _sxLayer?: Layer
25
+ private _sxWidth?: Width
26
+ private _sxStroke?: Stroke
27
+ private _sxUuid?: Uuid
28
+ private _locked = false
29
+
30
+ static override fromSexprPrimitives(
31
+ primitiveSexprs: PrimitiveSExpr[],
32
+ ): FpLine {
33
+ const fpLine = new FpLine()
34
+
35
+ const structuredPrimitives: PrimitiveSExpr[] = []
36
+
37
+ for (const primitive of primitiveSexprs) {
38
+ if (typeof primitive === "string") {
39
+ if (primitive === "locked") {
40
+ if (fpLine._locked) {
41
+ throw new Error("fp_line encountered duplicate locked flags")
42
+ }
43
+ fpLine._locked = true
44
+ continue
45
+ }
46
+ throw new Error(`fp_line encountered unsupported flag "${primitive}"`)
47
+ }
48
+
49
+ if (!Array.isArray(primitive) || primitive.length === 0) {
50
+ throw new Error(
51
+ `fp_line encountered invalid child expression: ${JSON.stringify(primitive)}`,
52
+ )
53
+ }
54
+
55
+ structuredPrimitives.push(primitive)
56
+ }
57
+
58
+ const { propertyMap, arrayPropertyMap } =
59
+ SxClass.parsePrimitivesToClassProperties(structuredPrimitives, this.token)
60
+
61
+ for (const token of Object.keys(propertyMap)) {
62
+ if (!SUPPORTED_SINGLE_TOKENS.has(token)) {
63
+ throw new Error(
64
+ `fp_line encountered unsupported child token "${token}"`,
65
+ )
66
+ }
67
+ }
68
+
69
+ for (const [token, entries] of Object.entries(arrayPropertyMap)) {
70
+ if (!SUPPORTED_SINGLE_TOKENS.has(token)) {
71
+ throw new Error(
72
+ `fp_line encountered unsupported child token "${token}"`,
73
+ )
74
+ }
75
+ if (entries.length > 1) {
76
+ throw new Error(
77
+ `fp_line does not support repeated child token "${token}"`,
78
+ )
79
+ }
80
+ }
81
+
82
+ fpLine._sxStart =
83
+ (arrayPropertyMap.start?.[0] as FpLineStart | undefined) ??
84
+ (propertyMap.start as FpLineStart | undefined)
85
+ fpLine._sxEnd =
86
+ (arrayPropertyMap.end?.[0] as FpLineEnd | undefined) ??
87
+ (propertyMap.end as FpLineEnd | undefined)
88
+ fpLine._sxLayer =
89
+ (arrayPropertyMap.layer?.[0] as Layer | undefined) ??
90
+ (propertyMap.layer as Layer | undefined)
91
+ fpLine._sxWidth =
92
+ (arrayPropertyMap.width?.[0] as Width | undefined) ??
93
+ (propertyMap.width as Width | undefined)
94
+ fpLine._sxStroke =
95
+ (arrayPropertyMap.stroke?.[0] as Stroke | undefined) ??
96
+ (propertyMap.stroke as Stroke | undefined)
97
+ fpLine._sxUuid =
98
+ (arrayPropertyMap.uuid?.[0] as Uuid | undefined) ??
99
+ (propertyMap.uuid as Uuid | undefined)
100
+
101
+ if (!fpLine._sxStart) {
102
+ throw new Error("fp_line requires a start child token")
103
+ }
104
+ if (!fpLine._sxEnd) {
105
+ throw new Error("fp_line requires an end child token")
106
+ }
107
+ if (!fpLine._sxLayer) {
108
+ throw new Error("fp_line requires a layer child token")
109
+ }
110
+
111
+ return fpLine
112
+ }
113
+
114
+ get start(): FpLineStart | undefined {
115
+ return this._sxStart
116
+ }
117
+
118
+ set start(value: FpLineStart | { x: number; y: number } | undefined) {
119
+ if (value === undefined) {
120
+ this._sxStart = undefined
121
+ return
122
+ }
123
+ this._sxStart =
124
+ value instanceof FpLineStart ? value : new FpLineStart(value.x, value.y)
125
+ }
126
+
127
+ get end(): FpLineEnd | undefined {
128
+ return this._sxEnd
129
+ }
130
+
131
+ set end(value: FpLineEnd | { x: number; y: number } | undefined) {
132
+ if (value === undefined) {
133
+ this._sxEnd = undefined
134
+ return
135
+ }
136
+ this._sxEnd = value instanceof FpLineEnd ? value : new FpLineEnd(value.x, value.y)
137
+ }
138
+
139
+ get layer(): Layer | undefined {
140
+ return this._sxLayer
141
+ }
142
+
143
+ set layer(value: Layer | string | Array<string | number> | undefined) {
144
+ if (value === undefined) {
145
+ this._sxLayer = undefined
146
+ return
147
+ }
148
+ if (value instanceof Layer) {
149
+ this._sxLayer = value
150
+ return
151
+ }
152
+ const names = Array.isArray(value) ? value : [value]
153
+ this._sxLayer = new Layer(names)
154
+ }
155
+
156
+ get width(): number | undefined {
157
+ return this._sxWidth?.value
158
+ }
159
+
160
+ set width(value: number | undefined) {
161
+ this._sxWidth = value === undefined ? undefined : new Width(value)
162
+ }
163
+
164
+ get stroke(): Stroke | undefined {
165
+ return this._sxStroke
166
+ }
167
+
168
+ set stroke(value: Stroke | undefined) {
169
+ this._sxStroke = value
170
+ }
171
+
172
+ get uuid(): Uuid | undefined {
173
+ return this._sxUuid
174
+ }
175
+
176
+ set uuid(value: Uuid | string | undefined) {
177
+ if (value === undefined) {
178
+ this._sxUuid = undefined
179
+ return
180
+ }
181
+ this._sxUuid = value instanceof Uuid ? value : new Uuid(value)
182
+ }
183
+
184
+ get locked(): boolean {
185
+ return this._locked
186
+ }
187
+
188
+ set locked(value: boolean) {
189
+ this._locked = value
190
+ }
191
+
192
+ override getChildren(): SxClass[] {
193
+ const children: SxClass[] = []
194
+ if (this._sxStart) children.push(this._sxStart)
195
+ if (this._sxEnd) children.push(this._sxEnd)
196
+ if (this._sxStroke) {
197
+ children.push(this._sxStroke)
198
+ } else if (this._sxWidth) {
199
+ children.push(this._sxWidth)
200
+ }
201
+ if (this._sxLayer) children.push(this._sxLayer)
202
+ if (this._sxUuid) children.push(this._sxUuid)
203
+ if (this._locked) children.push(new FpLineLocked())
204
+ return children
205
+ }
206
+ }
207
+ SxClass.register(FpLine)
208
+
209
+ export class FpLineStart extends SxClass {
210
+ static override token = "start"
211
+ static override parentToken = "fp_line"
212
+ override token = "start"
213
+
214
+ constructor(public x: number, public y: number) {
215
+ super()
216
+ }
217
+
218
+ static override fromSexprPrimitives(
219
+ primitiveSexprs: PrimitiveSExpr[],
220
+ ): FpLineStart {
221
+ const [rawX, rawY] = primitiveSexprs
222
+ const x = Number(rawX)
223
+ const y = Number(rawY)
224
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
225
+ throw new Error("fp_line start expects numeric coordinates")
226
+ }
227
+ return new FpLineStart(x, y)
228
+ }
229
+
230
+ override getChildren(): SxClass[] {
231
+ return []
232
+ }
233
+
234
+ override getString(): string {
235
+ return `(start ${this.x} ${this.y})`
236
+ }
237
+ }
238
+ SxClass.register(FpLineStart)
239
+
240
+ export class FpLineEnd extends SxClass {
241
+ static override token = "end"
242
+ static override parentToken = "fp_line"
243
+ override token = "end"
244
+
245
+ constructor(public x: number, public y: number) {
246
+ super()
247
+ }
248
+
249
+ static override fromSexprPrimitives(
250
+ primitiveSexprs: PrimitiveSExpr[],
251
+ ): FpLineEnd {
252
+ const [rawX, rawY] = primitiveSexprs
253
+ const x = Number(rawX)
254
+ const y = Number(rawY)
255
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
256
+ throw new Error("fp_line end expects numeric coordinates")
257
+ }
258
+ return new FpLineEnd(x, y)
259
+ }
260
+
261
+ override getChildren(): SxClass[] {
262
+ return []
263
+ }
264
+
265
+ override getString(): string {
266
+ return `(end ${this.x} ${this.y})`
267
+ }
268
+ }
269
+ SxClass.register(FpLineEnd)
270
+
271
+ class FpLineLocked extends SxClass {
272
+ static override token = "locked"
273
+ static override parentToken = "fp_line"
274
+ override token = "locked"
275
+
276
+ static override fromSexprPrimitives(): FpLineLocked {
277
+ return new FpLineLocked()
278
+ }
279
+
280
+ override getChildren(): SxClass[] {
281
+ return []
282
+ }
283
+
284
+ override getString(): string {
285
+ return "locked"
286
+ }
287
+ }
288
+ SxClass.register(FpLineLocked)