Skip to content

add support for pcb cutouts#14

Merged
seveibar merged 2 commits into
mainfrom
pcb-cutouts
Oct 16, 2025
Merged

add support for pcb cutouts#14
seveibar merged 2 commits into
mainfrom
pcb-cutouts

Conversation

@seveibar

Copy link
Copy Markdown
Contributor

No description provided.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting

Comment on lines +82 to +90
if (cutout.shape === "rect") {
const pcbX = cutout.center.x
const pcbY = cutout.center.y
const width = mmStr(cutout.width)
const height = mmStr(cutout.height)
const rotation = cutout.rotation !== undefined ? ` pcbRotation="${mmStr(cutout.rotation)}"` : ""

elementStrings.push(
`<pcbcutout shape="rect" pcbX="${mmStr(pcbX)}" pcbY="${mmStr(pcbY)}" width="${width}" height="${height}"${rotation} />`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Emit rotation in millimeters instead of degrees

Rectangular cutouts format pcbRotation with mmStr, producing values such as pcbRotation="45mm". Rotation is an angular quantity, so appending mm makes the attribute nonsensical and will cause any consumer that expects degrees to misinterpret or ignore the rotation. Use the raw angle (or a degree helper) rather than a millimeter string.

Useful? React with 👍 / 👎.

Comment on lines +11 to +137
test("test pcb_cutout conversion - all shapes", async () => {
const circuitJson: AnyCircuitElement[] = [
{
type: "pcb_cutout",
pcb_cutout_id: "cutout1",
shape: "rect",
center: { x: 0, y: 0 },
width: 5,
height: 3,
rotation: 45,
},
{
type: "pcb_cutout",
pcb_cutout_id: "cutout2",
shape: "circle",
center: { x: 10, y: 10 },
radius: 2.5,
},
{
type: "pcb_cutout",
pcb_cutout_id: "cutout3",
shape: "polygon",
points: [
{ x: 0, y: 0 },
{ x: 5, y: 0 },
{ x: 5, y: 5 },
{ x: 0, y: 5 },
],
},
]

const tscircuit = convertCircuitJsonToTscircuit(circuitJson, {
componentName: "ComponentWithCutouts",
})

expect(tscircuit).toMatchInlineSnapshot(`
"import { type ChipProps } from "tscircuit"
export const ComponentWithCutouts = (props: ChipProps) => (
<chip
footprint={<footprint>
<pcbcutout shape="rect" pcbX="0mm" pcbY="0mm" width="5mm" height="3mm" pcbRotation="45mm" />
<pcbcutout shape="circle" pcbX="10mm" pcbY="10mm" radius="2.5mm" />
<pcbcutout shape="polygon" points={[{"x":0,"y":0},{"x":5,"y":0},{"x":5,"y":5},{"x":0,"y":5}]} />
</footprint>}
{...props}
/>
)"
`)
})

test("test pcb_cutout conversion - rect without rotation", async () => {
const circuitJson: AnyCircuitElement[] = [
{
type: "pcb_cutout",
pcb_cutout_id: "cutout1",
shape: "rect",
center: { x: 2, y: 3 },
width: 10,
height: 5,
},
]

const tscircuit = convertCircuitJsonToTscircuit(circuitJson, {
componentName: "ComponentWithRectCutout",
})

expect(tscircuit).toMatchInlineSnapshot(`
"import { type ChipProps } from "tscircuit"
export const ComponentWithRectCutout = (props: ChipProps) => (
<chip
footprint={<footprint>
<pcbcutout shape="rect" pcbX="2mm" pcbY="3mm" width="10mm" height="5mm" />
</footprint>}
{...props}
/>
)"
`)
})

test("test pcb_cutout conversion - mixed with other elements", async () => {
const circuitJson: AnyCircuitElement[] = [
{
type: "pcb_smtpad",
pcb_smtpad_id: "pad1",
shape: "rect",
x: 0,
y: 0,
width: 1,
height: 1,
layer: "top",
port_hints: ["1"],
},
{
type: "pcb_cutout",
pcb_cutout_id: "cutout1",
shape: "circle",
center: { x: 5, y: 5 },
radius: 1.5,
},
{
type: "pcb_hole",
pcb_hole_id: "hole1",
hole_shape: "circle",
x: 10,
y: 10,
hole_diameter: 0.5,
},
]

const tscircuit = convertCircuitJsonToTscircuit(circuitJson, {
componentName: "ComponentWithMixedElements",
})

expect(tscircuit).toMatchInlineSnapshot(`
"import { type ChipProps } from "tscircuit"
export const ComponentWithMixedElements = (props: ChipProps) => (
<chip
footprint={<footprint>
<hole pcbX="10mm" pcbY="10mm" diameter="0.5mm" />
<smtpad portHints={["1"]} pcbX="0mm" pcbY="0mm" width="1mm" height="1mm" shape="rect" />
<pcbcutout shape="circle" pcbX="5mm" pcbY="5mm" radius="1.5mm" />
</footprint>}
{...props}
/>
)"
`)
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test file violates the project rule limiting *.test.ts files to at most one test() function. The file contains three test functions (lines 11, 61, and 90) and must be split into separate numbered files: test5-pcb-cutout1.test.tsx, test5-pcb-cutout2.test.tsx, and test5-pcb-cutout3.test.tsx, with each file containing only one test function.

Spotted by Graphite Agent (based on custom rule: Custom rule)

Fix in Graphite


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

@seveibar seveibar merged commit 6c28f56 into main Oct 16, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant