Skip to content

Commit da8da47

Browse files
Kabi10claude
andcommitted
feat: add pcb_courtyard_polygon and pcb_courtyard_circle to footprint TSX generation
Resolves tscircuit/tscircuit#1081 — make sure kicad component components create courtyards. Adds <courtyardpolygon> and <courtyardcircle> JSX element generation to generate-footprint-tsx.tsx, covering all 4 courtyard circuit-json types: - pcb_courtyard_outline → <courtyardoutline> (existing) - pcb_courtyard_rect → <courtyardrect> (existing) - pcb_courtyard_polygon → <courtyardpolygon> (new) - pcb_courtyard_circle → <courtyardcircle> (new) Tests added to verify both new element types are emitted correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7f981fb commit da8da47

2 files changed

Lines changed: 115 additions & 0 deletions

File tree

lib/generate-footprint-tsx.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,51 @@ export const generateFootprintTsx = (
282282
elementStrings.push(`<courtyardrect ${attrs.join(" ")} />`)
283283
}
284284

285+
const courtyardPolygons = (su(circuitJson) as any).pcb_courtyard_polygon?.list() ?? []
286+
for (const courtyardPolygon of courtyardPolygons) {
287+
const courtyardPolygonAny = courtyardPolygon as any
288+
const attrs = [
289+
`points={${JSON.stringify(courtyardPolygonAny.points ?? [])}}`,
290+
]
291+
292+
if (courtyardPolygonAny.stroke_width !== undefined) {
293+
attrs.push(`strokeWidth={${courtyardPolygonAny.stroke_width}}`)
294+
}
295+
if (courtyardPolygonAny.is_filled !== undefined) {
296+
attrs.push(`isFilled={${courtyardPolygonAny.is_filled}}`)
297+
}
298+
if (courtyardPolygonAny.has_stroke !== undefined) {
299+
attrs.push(`hasStroke={${courtyardPolygonAny.has_stroke}}`)
300+
}
301+
if (courtyardPolygonAny.is_stroke_dashed !== undefined) {
302+
attrs.push(`isStrokeDashed={${courtyardPolygonAny.is_stroke_dashed}}`)
303+
}
304+
if (courtyardPolygonAny.color !== undefined) {
305+
attrs.push(`color="${courtyardPolygonAny.color}"`)
306+
}
307+
308+
elementStrings.push(`<courtyardpolygon ${attrs.join(" ")} />`)
309+
}
310+
311+
const courtyardCircles = (su(circuitJson) as any).pcb_courtyard_circle?.list() ?? []
312+
for (const courtyardCircle of courtyardCircles) {
313+
const courtyardCircleAny = courtyardCircle as any
314+
const attrs = [
315+
`pcbX={${courtyardCircleAny.center?.x ?? 0}}`,
316+
`pcbY={${courtyardCircleAny.center?.y ?? 0}}`,
317+
`radius={${courtyardCircleAny.radius ?? 0}}`,
318+
]
319+
320+
if (courtyardCircleAny.stroke_width !== undefined) {
321+
attrs.push(`strokeWidth={${courtyardCircleAny.stroke_width}}`)
322+
}
323+
if (courtyardCircleAny.color !== undefined) {
324+
attrs.push(`color="${courtyardCircleAny.color}"`)
325+
}
326+
327+
elementStrings.push(`<courtyardcircle ${attrs.join(" ")} />`)
328+
}
329+
285330
if (elementStrings.length === 0) {
286331
return null
287332
}

tests/test8-support-courtyard.test.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,76 @@ const projectCourtyardToFabricationNotes = (courtyardElements: any[]) => {
9898
return projected
9999
}
100100

101+
test("test8 support courtyard polygon and circle", () => {
102+
const tscircuit = convertCircuitJsonToTscircuit(circuitJson2, {
103+
componentName: "Test8bComponent",
104+
})
105+
106+
expect(tscircuit).toContain("<courtyardpolygon")
107+
expect(tscircuit).toContain(`points={[{"x":-1,"y":-1},{"x":1,"y":-1},{"x":0,"y":1}]}`)
108+
expect(tscircuit).toContain("<courtyardcircle")
109+
expect(tscircuit).toContain("pcbX={2}")
110+
expect(tscircuit).toContain("pcbY={1}")
111+
expect(tscircuit).toContain("radius={0.8}")
112+
})
113+
114+
const circuitJson2: any = [
115+
{
116+
type: "source_component",
117+
source_component_id: "generic_1",
118+
supplier_part_numbers: {},
119+
},
120+
{
121+
type: "schematic_component",
122+
schematic_component_id: "schematic_generic_component_1",
123+
source_component_id: "generic_1",
124+
center: { x: 0, y: 0 },
125+
rotation: 0,
126+
size: { width: 0, height: 0 },
127+
},
128+
{
129+
type: "pcb_component",
130+
source_component_id: "generic_1",
131+
pcb_component_id: "pcb_generic_component_1",
132+
layer: "top",
133+
center: { x: 0, y: 0 },
134+
rotation: 0,
135+
width: 2,
136+
height: 1,
137+
},
138+
{
139+
type: "pcb_smtpad",
140+
pcb_smtpad_id: "pcb_smtpad_1",
141+
shape: "rect",
142+
x: 0,
143+
y: 0,
144+
width: 0.5,
145+
height: 0.5,
146+
layer: "top",
147+
pcb_component_id: "pcb_generic_component_1",
148+
port_hints: ["1"],
149+
},
150+
{
151+
type: "pcb_courtyard_polygon",
152+
pcb_courtyard_polygon_id: "pcb_courtyard_polygon_0",
153+
pcb_component_id: "pcb_generic_component_1",
154+
layer: "top",
155+
points: [
156+
{ x: -1, y: -1 },
157+
{ x: 1, y: -1 },
158+
{ x: 0, y: 1 },
159+
],
160+
},
161+
{
162+
type: "pcb_courtyard_circle",
163+
pcb_courtyard_circle_id: "pcb_courtyard_circle_0",
164+
pcb_component_id: "pcb_generic_component_1",
165+
layer: "top",
166+
center: { x: 2, y: 1 },
167+
radius: 0.8,
168+
},
169+
]
170+
101171
const circuitJson: any = [
102172
{
103173
type: "source_component",

0 commit comments

Comments
 (0)