Skip to content

Commit d639023

Browse files
authored
feat: add polyline class and extend that to be used as SymbolPolyline (#16)
1 parent dfbddeb commit d639023

File tree

5 files changed

+226
-60
lines changed

5 files changed

+226
-60
lines changed

lib/sexpr/classes/KicadSch.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { Uuid } from "./Uuid"
1919
import { Wire } from "./Wire"
2020
import { Junction } from "./Junction"
2121
import { NoConnect } from "./NoConnect"
22+
import { Polyline } from "./Polyline"
2223

2324
const SINGLE_CHILD_TOKENS = new Set([
2425
"version",
@@ -43,6 +44,7 @@ const MULTI_CHILD_TOKENS = new Set([
4344
"wire",
4445
"no_connect",
4546
"sheet_instances",
47+
"polyline",
4648
])
4749

4850
const SUPPORTED_CHILD_TOKENS = new Set([
@@ -70,6 +72,7 @@ export interface KicadSchConstructorParams {
7072
wires?: Wire[]
7173
junctions?: Junction[]
7274
noConnects?: NoConnect[]
75+
polylines?: Polyline[]
7376
}
7477

7578
export class KicadSch extends SxClass {
@@ -95,6 +98,7 @@ export class KicadSch extends SxClass {
9598
private _wires: Wire[] = []
9699
private _junctions: Junction[] = []
97100
private _noConnects: NoConnect[] = []
101+
private _polylines: Polyline[] = []
98102

99103
constructor(params: KicadSchConstructorParams = {}) {
100104
super()
@@ -180,6 +184,10 @@ export class KicadSch extends SxClass {
180184
if (params.noConnects !== undefined) {
181185
this.noConnects = params.noConnects
182186
}
187+
188+
if (params.polylines !== undefined) {
189+
this.polylines = params.polylines
190+
}
183191
}
184192

185193
static override fromSexprPrimitives(
@@ -246,6 +254,7 @@ export class KicadSch extends SxClass {
246254
junctions: (arrayPropertyMap.junction as Junction[]) ?? [],
247255
wires: (arrayPropertyMap.wire as Wire[]) ?? [],
248256
noConnects: (arrayPropertyMap.no_connect as NoConnect[]) ?? [],
257+
polylines: (arrayPropertyMap.polyline as Polyline[]) ?? [],
249258
})
250259
}
251260

@@ -412,6 +421,14 @@ export class KicadSch extends SxClass {
412421
this._noConnects = [...value]
413422
}
414423

424+
get polylines(): Polyline[] {
425+
return [...this._polylines]
426+
}
427+
428+
set polylines(value: Polyline[]) {
429+
this._polylines = [...value]
430+
}
431+
415432
override getChildren(): SxClass[] {
416433
const children: SxClass[] = []
417434
if (this._sxVersion) children.push(this._sxVersion)
@@ -433,6 +450,7 @@ export class KicadSch extends SxClass {
433450
children.push(...this._junctions)
434451
children.push(...this._wires)
435452
children.push(...this._noConnects)
453+
children.push(...this._polylines)
436454
return children
437455
}
438456
}

lib/sexpr/classes/Polyline.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { SxClass } from "../base-classes/SxClass"
2+
import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3+
import { Pts } from "./Pts"
4+
import { Stroke } from "./Stroke"
5+
import { Uuid } from "./Uuid"
6+
7+
const SUPPORTED_TOKENS_KICAD_SCH = new Set(["pts", "stroke", "uuid"])
8+
const SUPPORTED_TOKENS_SYMBOL = new Set(["pts", "stroke", "fill"])
9+
10+
// Forward declaration for SymbolPolylineFill to avoid circular dependency
11+
export interface SymbolPolylineFillLike extends SxClass {
12+
type?: string
13+
}
14+
15+
export interface PolylineConstructorParams {
16+
points?: Pts
17+
stroke?: Stroke
18+
uuid?: string | Uuid
19+
fill?: SymbolPolylineFillLike
20+
}
21+
22+
/**
23+
* Base Polyline class that can be used in both kicad_sch and symbol contexts.
24+
* - When parent is "kicad_sch": supports pts, stroke, uuid
25+
* - When parent is "symbol": supports pts, stroke, fill
26+
*/
27+
export class Polyline extends SxClass {
28+
static override token = "polyline"
29+
override token = "polyline"
30+
31+
private _sxPts?: Pts
32+
private _sxStroke?: Stroke
33+
private _sxUuid?: Uuid
34+
private _sxFill?: SymbolPolylineFillLike
35+
36+
constructor(params: PolylineConstructorParams = {}) {
37+
super()
38+
39+
if (params.points !== undefined) {
40+
this.points = params.points
41+
}
42+
43+
if (params.stroke !== undefined) {
44+
this.stroke = params.stroke
45+
}
46+
47+
if (params.uuid !== undefined) {
48+
this.uuid = params.uuid
49+
}
50+
51+
if (params.fill !== undefined) {
52+
this.fill = params.fill
53+
}
54+
}
55+
56+
static override fromSexprPrimitives(
57+
primitiveSexprs: PrimitiveSExpr[],
58+
): Polyline {
59+
const polyline = new Polyline()
60+
61+
const { propertyMap, arrayPropertyMap } =
62+
SxClass.parsePrimitivesToClassProperties(primitiveSexprs, this.token)
63+
64+
// Determine which tokens are supported based on parent context
65+
// Check the static parentToken property of the class being instantiated
66+
const supportedTokens =
67+
this.parentToken === "symbol"
68+
? SUPPORTED_TOKENS_SYMBOL
69+
: SUPPORTED_TOKENS_KICAD_SCH
70+
71+
for (const [token, entries] of Object.entries(arrayPropertyMap)) {
72+
if (!supportedTokens.has(token)) {
73+
throw new Error(
74+
`Unsupported child tokens inside polyline expression: ${token}`,
75+
)
76+
}
77+
if (entries.length > 1) {
78+
throw new Error(
79+
`polyline does not support repeated child tokens: ${token}`,
80+
)
81+
}
82+
}
83+
84+
const unsupportedTokens = Object.keys(propertyMap).filter(
85+
(token) => !supportedTokens.has(token),
86+
)
87+
if (unsupportedTokens.length > 0) {
88+
throw new Error(
89+
`Unsupported child tokens inside polyline expression: ${unsupportedTokens.join(", ")}`,
90+
)
91+
}
92+
93+
polyline._sxPts =
94+
(arrayPropertyMap.pts?.[0] as Pts | undefined) ??
95+
(propertyMap.pts as Pts | undefined)
96+
polyline._sxStroke = propertyMap.stroke as Stroke | undefined
97+
polyline._sxUuid = propertyMap.uuid as Uuid | undefined
98+
polyline._sxFill = propertyMap.fill
99+
100+
return polyline
101+
}
102+
103+
get points(): Pts | undefined {
104+
return this._sxPts
105+
}
106+
107+
set points(value: Pts | undefined) {
108+
this._sxPts = value
109+
}
110+
111+
get stroke(): Stroke | undefined {
112+
return this._sxStroke
113+
}
114+
115+
set stroke(value: Stroke | undefined) {
116+
this._sxStroke = value
117+
}
118+
119+
get uuid(): Uuid | undefined {
120+
return this._sxUuid
121+
}
122+
123+
set uuid(value: Uuid | string | undefined) {
124+
if (value === undefined) {
125+
this._sxUuid = undefined
126+
return
127+
}
128+
this._sxUuid = value instanceof Uuid ? value : new Uuid(value)
129+
}
130+
131+
get fill(): SymbolPolylineFillLike | undefined {
132+
return this._sxFill
133+
}
134+
135+
set fill(value: SymbolPolylineFillLike | undefined) {
136+
this._sxFill = value
137+
}
138+
139+
override getChildren(): SxClass[] {
140+
const children: SxClass[] = []
141+
if (this._sxPts) children.push(this._sxPts)
142+
if (this._sxStroke) children.push(this._sxStroke)
143+
if (this._sxFill) children.push(this._sxFill)
144+
if (this._sxUuid) children.push(this._sxUuid)
145+
return children
146+
}
147+
}
148+
149+
// Register for both kicad_sch and symbol parents
150+
SxClass.register(Polyline)
151+
152+
// Create a class that explicitly sets parentToken for kicad_sch
153+
export class SchematicPolyline extends Polyline {
154+
static override parentToken = "kicad_sch"
155+
}
156+
SxClass.register(SchematicPolyline)
157+
158+
// Create a class that explicitly sets parentToken for symbol
159+
export class SymbolPolyline extends Polyline {
160+
static override parentToken = "symbol"
161+
}
162+
SxClass.register(SymbolPolyline)

lib/sexpr/classes/Symbol.ts

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import { OnBoard } from "./OnBoard"
1515
import { FieldsAutoplaced } from "./FieldsAutoplaced"
1616
import { TextEffects } from "./TextEffects"
1717
import { Uuid } from "./Uuid"
18-
import { Pts } from "./Pts"
1918
import { Stroke } from "./Stroke"
19+
import { SymbolPolyline } from "./Polyline"
2020

2121
export class SymbolUnit extends SxPrimitiveNumber {
2222
static override token = "unit"
@@ -437,65 +437,6 @@ export class SymbolFillType extends SxClass {
437437
}
438438
SxClass.register(SymbolFillType)
439439

440-
export class SymbolPolyline extends SxClass {
441-
static override token = "polyline"
442-
static override parentToken = "symbol"
443-
token = "polyline"
444-
445-
private _sxPts?: Pts
446-
private _sxStroke?: Stroke
447-
private _sxFill?: SymbolPolylineFill
448-
449-
static override fromSexprPrimitives(
450-
primitiveSexprs: PrimitiveSExpr[],
451-
): SymbolPolyline {
452-
const polyline = new SymbolPolyline()
453-
const { propertyMap } = SxClass.parsePrimitivesToClassProperties(
454-
primitiveSexprs,
455-
this.token,
456-
)
457-
458-
polyline._sxPts = propertyMap.pts as Pts
459-
polyline._sxStroke = propertyMap.stroke as Stroke
460-
polyline._sxFill = propertyMap.fill as SymbolPolylineFill
461-
462-
return polyline
463-
}
464-
465-
get points(): Pts | undefined {
466-
return this._sxPts
467-
}
468-
469-
set points(value: Pts | undefined) {
470-
this._sxPts = value
471-
}
472-
473-
get stroke(): Stroke | undefined {
474-
return this._sxStroke
475-
}
476-
477-
set stroke(value: Stroke | undefined) {
478-
this._sxStroke = value
479-
}
480-
481-
get fill(): SymbolPolylineFill | undefined {
482-
return this._sxFill
483-
}
484-
485-
set fill(value: SymbolPolylineFill | undefined) {
486-
this._sxFill = value
487-
}
488-
489-
override getChildren(): SxClass[] {
490-
const children: SxClass[] = []
491-
if (this._sxPts) children.push(this._sxPts)
492-
if (this._sxStroke) children.push(this._sxStroke)
493-
if (this._sxFill) children.push(this._sxFill)
494-
return children
495-
}
496-
}
497-
SxClass.register(SymbolPolyline)
498-
499440
export class SymbolRectangle extends SxClass {
500441
static override token = "rectangle"
501442
static override parentToken = "symbol"

lib/sexpr/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export * from "./classes/Wire"
2424
export * from "./classes/Bus"
2525
export * from "./classes/Junction"
2626
export * from "./classes/NoConnect"
27+
export * from "./classes/Polyline" // Exports Polyline, SchematicPolyline, SymbolPolyline
2728
export * from "./classes/BusEntry"
2829
export * from "./classes/Label"
2930
export * from "./classes/GlobalLabel"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { SxClass, Polyline, KicadSch, Pts, Stroke, Uuid } from "lib/sexpr"
2+
import { expect, test } from "bun:test"
3+
4+
test("Polyline", () => {
5+
const [parsed] = SxClass.parse(`
6+
(kicad_sch
7+
(polyline
8+
(pts
9+
(xy 172.72 196.85) (xy 148.59 196.85)
10+
)
11+
(stroke
12+
(width 0)
13+
(type dash)
14+
)
15+
(uuid "088f800b-89af-4049-b7bd-bc3ee6bc7fd7")
16+
)
17+
)
18+
`)
19+
20+
expect(parsed).toBeInstanceOf(KicadSch)
21+
const kicadSch = parsed as KicadSch
22+
expect(kicadSch.polylines).toHaveLength(1)
23+
24+
const polyline = kicadSch.polylines[0]
25+
expect(polyline).toBeInstanceOf(Polyline)
26+
const pl = polyline as Polyline
27+
expect(pl.points).toBeInstanceOf(Pts)
28+
expect(pl.stroke).toBeInstanceOf(Stroke)
29+
expect(pl.uuid).toBeInstanceOf(Uuid)
30+
31+
expect(pl.getString()).toMatchInlineSnapshot(`
32+
"(polyline
33+
(pts
34+
(xy 172.72 196.85)
35+
(xy 148.59 196.85)
36+
)
37+
(stroke
38+
(width 0)
39+
(type dash)
40+
)
41+
(uuid 088f800b-89af-4049-b7bd-bc3ee6bc7fd7)
42+
)"
43+
`)
44+
})

0 commit comments

Comments
 (0)