-
Notifications
You must be signed in to change notification settings - Fork 11
Support courtyard elements + silkscreen circles in footprint TSX #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
b560fe8
5944f32
6a80ccf
95a26f6
b989b80
6c2f0a2
38be375
941b205
d36c819
015b922
b5114c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,8 +9,19 @@ export const generateFootprintTsx = ( | |||||||||||||||||||||||||||
| const platedHoles = su(circuitJson).pcb_plated_hole.list() | ||||||||||||||||||||||||||||
| const smtPads = su(circuitJson).pcb_smtpad.list() | ||||||||||||||||||||||||||||
| const silkscreenPaths = su(circuitJson).pcb_silkscreen_path.list() | ||||||||||||||||||||||||||||
| const silkscreenCircles = su(circuitJson).pcb_silkscreen_circle.list() | ||||||||||||||||||||||||||||
| const fabricationNotePaths = su(circuitJson).pcb_fabrication_note_path.list() | ||||||||||||||||||||||||||||
| const silkscreenTexts = su(circuitJson).pcb_silkscreen_text.list() | ||||||||||||||||||||||||||||
| // NOTE: soup-util doesn't currently expose all courtyard helpers, so we filter manually | ||||||||||||||||||||||||||||
| const courtyardCircles = circuitJson.filter( | ||||||||||||||||||||||||||||
| (e: any) => e?.type === "pcb_courtyard_circle", | ||||||||||||||||||||||||||||
| ) as any[] | ||||||||||||||||||||||||||||
| const courtyardRects = circuitJson.filter( | ||||||||||||||||||||||||||||
| (e: any) => e?.type === "pcb_courtyard_rect", | ||||||||||||||||||||||||||||
| ) as any[] | ||||||||||||||||||||||||||||
| const courtyardOutlines = circuitJson.filter( | ||||||||||||||||||||||||||||
| (e: any) => e?.type === "pcb_courtyard_outline", | ||||||||||||||||||||||||||||
| ) as any[] | ||||||||||||||||||||||||||||
| const pcbCutouts = su(circuitJson).pcb_cutout.list() | ||||||||||||||||||||||||||||
| const noteTexts = su(circuitJson).pcb_note_text.list() | ||||||||||||||||||||||||||||
| const noteRects = su(circuitJson).pcb_note_rect.list() | ||||||||||||||||||||||||||||
|
|
@@ -60,6 +71,19 @@ export const generateFootprintTsx = ( | |||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| for (const silkscreenCircle of silkscreenCircles) { | ||||||||||||||||||||||||||||
| const pcbX = silkscreenCircle.center?.x ?? 0 | ||||||||||||||||||||||||||||
| const pcbY = silkscreenCircle.center?.y ?? 0 | ||||||||||||||||||||||||||||
| const strokeWidth = | ||||||||||||||||||||||||||||
| silkscreenCircle.stroke_width !== undefined | ||||||||||||||||||||||||||||
| ? ` strokeWidth="${mmStr(silkscreenCircle.stroke_width)}"` | ||||||||||||||||||||||||||||
| : "" | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| elementStrings.push( | ||||||||||||||||||||||||||||
| `<silkscreencircle pcbX="${mmStr(pcbX)}" pcbY="${mmStr(pcbY)}" radius="${mmStr(silkscreenCircle.radius)}"${strokeWidth} />`, | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Map fabrication note paths to silkscreen paths in footprints | ||||||||||||||||||||||||||||
| for (const fabPath of fabricationNotePaths) { | ||||||||||||||||||||||||||||
| elementStrings.push( | ||||||||||||||||||||||||||||
|
|
@@ -82,6 +106,26 @@ export const generateFootprintTsx = ( | |||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Add courtyard elements | ||||||||||||||||||||||||||||
| for (const courtyardCircle of courtyardCircles) { | ||||||||||||||||||||||||||||
| elementStrings.push( | ||||||||||||||||||||||||||||
| `<courtyardcircle pcbX="${mmStr(courtyardCircle.center.x)}" pcbY="${mmStr(courtyardCircle.center.y)}" radius="${mmStr(courtyardCircle.radius)}" />`, | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| for (const courtyardRect of courtyardRects) { | ||||||||||||||||||||||||||||
| const colorAttr = courtyardRect.color ? ` color="${courtyardRect.color}"` : "" | ||||||||||||||||||||||||||||
| elementStrings.push( | ||||||||||||||||||||||||||||
| `<courtyardrect pcbX="${mmStr(courtyardRect.center.x)}" pcbY="${mmStr(courtyardRect.center.y)}" width="${mmStr(courtyardRect.width)}" height="${mmStr(courtyardRect.height)}"${colorAttr} />`, | ||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing null-safety checks on If Fix: const pcbX = courtyardRect.center?.x ?? 0
const pcbY = courtyardRect.center?.y ?? 0
const width = mmStr(courtyardRect.width ?? 0)
const height = mmStr(courtyardRect.height ?? 0)
const colorAttr = courtyardRect.color
? ` color="${courtyardRect.color}"`
: ""
elementStrings.push(
`<courtyardrect pcbX="${mmStr(pcbX)}" pcbY="${mmStr(pcbY)}" width="${width}" height="${height}"${colorAttr} />`,
)
Suggested change
Spotted by Graphite Agent |
||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| for (const courtyardOutline of courtyardOutlines) { | ||||||||||||||||||||||||||||
| elementStrings.push( | ||||||||||||||||||||||||||||
| `<courtyardoutline outline={${JSON.stringify(courtyardOutline.outline)}} />`, | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Add cutout elements | ||||||||||||||||||||||||||||
| for (const cutout of pcbCutouts) { | ||||||||||||||||||||||||||||
| if (cutout.shape === "rect") { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { expect, test } from "bun:test" | ||
| import { convertCircuitJsonToTscircuit } from "lib" | ||
|
|
||
| test("test7 support courtyards + silkscreen circles", async () => { | ||
| const tscircuit = convertCircuitJsonToTscircuit(circuitJson, { | ||
| componentName: "Test7Component", | ||
| }) | ||
|
|
||
| expect(tscircuit).toContain("<silkscreencircle") | ||
| expect(tscircuit).toContain('radius="2mm"') | ||
| expect(tscircuit).toContain('strokeWidth="0.15mm"') | ||
|
|
||
| expect(tscircuit).toContain("<courtyardcircle") | ||
| expect(tscircuit).toContain('radius="3mm"') | ||
|
|
||
| expect(tscircuit).toContain("<courtyardrect") | ||
| expect(tscircuit).toContain('width="4mm"') | ||
| expect(tscircuit).toContain('height="5mm"') | ||
| expect(tscircuit).toContain('color="#ff0000"') | ||
|
|
||
| expect(tscircuit).toContain("<courtyardoutline") | ||
| }) | ||
|
|
||
| const circuitJson: any = [ | ||
| { | ||
| type: "pcb_silkscreen_circle", | ||
| pcb_silkscreen_circle_id: "pcb_silkscreen_circle_0", | ||
| pcb_component_id: "pcb_component_0", | ||
| layer: "top", | ||
| center: { x: 1, y: 2 }, | ||
| radius: 2, | ||
| stroke_width: 0.15, | ||
| }, | ||
| { | ||
| type: "pcb_courtyard_circle", | ||
| pcb_courtyard_circle_id: "pcb_courtyard_circle_0", | ||
| pcb_component_id: "pcb_component_0", | ||
| layer: "top", | ||
| center: { x: 0, y: 0 }, | ||
| radius: 3, | ||
| }, | ||
| { | ||
| type: "pcb_courtyard_rect", | ||
| pcb_courtyard_rect_id: "pcb_courtyard_rect_0", | ||
| pcb_component_id: "pcb_component_0", | ||
| layer: "top", | ||
| center: { x: 0, y: 0 }, | ||
| width: 4, | ||
| height: 5, | ||
| color: "#ff0000", | ||
| }, | ||
| { | ||
| type: "pcb_courtyard_outline", | ||
| pcb_courtyard_outline_id: "pcb_courtyard_outline_0", | ||
| pcb_component_id: "pcb_component_0", | ||
| layer: "top", | ||
| outline: [ | ||
| { x: -1, y: -1 }, | ||
| { x: 1, y: -1 }, | ||
| { x: 1, y: 1 }, | ||
| { x: -1, y: 1 }, | ||
| ], | ||
| }, | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing null safety for
courtyardCircle.center. The code directly accessescourtyardCircle.center.xandcourtyardCircle.center.ywithout optional chaining, which will throw a runtime error ifcenteris undefined or null.This is inconsistent with the silkscreen circle implementation (lines 75-76) which safely uses
silkscreenCircle.center?.x ?? 0.Fix:
Spotted by Graphite Agent

Is this helpful? React 👍 or 👎 to let us know.