Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions src/fn/to220.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@ import {
type PcbSilkscreenPath,
length,
} from "circuit-json"
import { platedhole } from "src/helpers/platedhole"
import { mm } from "@tscircuit/mm"
import { platedHolePill } from "src/helpers/platedHolePill"
import { platedHoleWithRectPad } from "src/helpers/platedHoleWithRectPad"
import { z } from "zod"
import { type SilkscreenRef, silkscreenRef } from "../helpers/silkscreenRef"
import { base_def } from "../helpers/zod/base_def"

export const to220_def = base_def.extend({
fn: z.string(),
p: length.optional().default("5.0mm"),
id: length.optional().default("1.0mm"),
od: length.optional().default("1.9mm"),
// JEDEC TO-220 lead pitch is a fixed 0.1" (2.54mm), the same value the
// to220f sibling and KiCad TO-220-3_Vertical use.
p: length.optional().default("2.54mm"),
id: length.optional().default("1.1mm"),
od: length.optional().default("1.905mm"),
ph: length.optional().default("2mm"),
w: length.optional().default("13mm"),
h: length.optional().default("7mm"),
num_pins: z.number().optional(),
Expand All @@ -26,7 +31,7 @@ export const to220 = (
raw_params: To220Def,
): { circuitJson: AnyCircuitElement[]; parameters: any } => {
const parameters = to220_def.parse(raw_params)
const { id, od, w, h, string } = parameters
const { p, id, od, ph, w, h, string } = parameters

const numPins =
parameters.num_pins ??
Expand All @@ -36,17 +41,29 @@ export const to220 = (
const halfWidth = w / 2
const halfHeight = h / 2

const minPitch = 2.5
const maxHoleWidth = w * 0.4
const computedPitch = Math.max(minPitch, maxHoleWidth / (numPins - 1))

const plated_holes = Array.from({ length: numPins }, (_, i) => {
const x =
numPins % 2 === 0
? (i - numPins / 2 + 0.5) * computedPitch
: (i - Math.floor(numPins / 2)) * computedPitch
return platedhole(i + 1, x, holeY, id, od)
})
// Pins sit on the fixed 2.54mm pitch, not a value derived from the body
// width. Pin 1 gets a rectangular pad (polarity marker) and the rest are
// pill pads, mirroring KiCad's TO-220-3_Vertical land pattern.
const plated_holes: AnyCircuitElement[] = Array.from(
{ length: numPins },
(_, i) => {
const x =
numPins % 2 === 0
? (i - numPins / 2 + 0.5) * p
: (i - Math.floor(numPins / 2)) * p
if (i === 0) {
return platedHoleWithRectPad({
pn: 1,
x,
y: holeY,
holeDiameter: id,
rectPadWidth: od,
rectPadHeight: ph,
})
}
return platedHolePill(i + 1, x, holeY, mm(id), mm(od), mm(ph))
},
)

const silkscreenBody: PcbSilkscreenPath = {
type: "pcb_silkscreen_path",
Expand Down Expand Up @@ -103,7 +120,10 @@ export const to220 = (

const silkscreenRefText: SilkscreenRef = silkscreenRef(0, h / 2 + 0.6, 0.5)
const pinToeHalfSpanX =
Math.max(...plated_holes.map((hole) => Math.abs(hole.x))) + od / 2
Math.max(
...plated_holes.map((hole) => Math.abs((hole as { x: number }).x)),
) +
od / 2
const pinToeTopY = holeY + od / 2
const pinToeBottomY = holeY - od / 2
const courtyardHalfWidth = Math.max(
Expand Down Expand Up @@ -135,6 +155,6 @@ export const to220 = (
silkscreenRefText as AnyCircuitElement,
courtyard,
],
parameters: { ...parameters, p: computedPitch },
parameters: { ...parameters, p },
}
}
2 changes: 1 addition & 1 deletion tests/__snapshots__/to220_2.snap.svg

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please add KiCad tests for the footprints you changed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Added tests/kicad-parity/to220_4_kicad_parity.test.ts in db11e19, matching the to220_3 parity that was already in the PR. Against Package_TO_SOT_THT.pretty/TO-220-4_Vertical the copper matches KiCad exactly (avgRelDiff 0.00%) with courtyardDiffPercent 11.77%, the same residual body-outline delta as to220_3, so it reuses the same < 13 courtyard threshold and ships the SVG plus boolean-difference snapshots.

That gives KiCad parity for both fixed 2.54mm-grid variants (to220_3 and to220_4). I deliberately did not add parity for the other two changed footprints, because their KiCad references are not on a 2.54mm pitch and a passing assertion there would need a threshold that certifies nothing:

  • TO-220-2_Vertical is a 5.08mm land (the two outer leads of the frame), so the fixed 2.54mm part measures avgRelDiff 29.40% / courtyardDiffPercent 17.15% against it.
  • TO-220-5_Vertical is the 1.7mm Multiwatt land, so 2.54mm measures avgRelDiff 36.03% / courtyardDiffPercent 24.30%.

Both keep their render snapshots in tests/to220.test.ts. If you want real KiCad parity across every pin count I can follow up with a per-pin-count pitch (5.08mm for the 2-pin, 1.7mm for the 5-pin) so to220_2 and to220_5 land on their KiCad packages too. That is wider than #790, which is why I scoped this PR to the 2.54mm land. Happy to fold it in here or open a separate PR, your call.

bun test on the to220 files is green (9 pass) and bun run build succeeds.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion tests/__snapshots__/to220_3.snap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading