diff --git a/src/fn/sot457.ts b/src/fn/sot457.ts
index 18937b3e..ed4ca7f3 100644
--- a/src/fn/sot457.ts
+++ b/src/fn/sot457.ts
@@ -175,7 +175,12 @@ const generateSot457Elements = (
const pin1Position = getCcwSot457Coords({ pitch, width, pinNumber: 1 })
const triangleHeight = params.wave ? 1 : 0.5
const triangleWidth = params.wave ? 0.7 : 0.3
- pin1Position.x -= params.wave ? padWidth : padWidth * 1.7
+ // Place the marker outboard of pad 1's actual edge. Offsetting by a multiple
+ // of the pad *width* instead let the triangle land on the pad whenever pl and
+ // pw are set independently (e.g. `sot457_w2_pl1.5_pw0.6`, where it overlapped
+ // by 0.005mm).
+ const pin1PadOuterEdgeX = pin1Position.x - padLength / 2
+ pin1Position.x = pin1PadOuterEdgeX - triangleHeight / 2 - 0.2 - 0.05 / 2
const pin1Indicator: PcbSilkscreenPath = {
type: "pcb_silkscreen_path",
diff --git a/src/fn/tssop.ts b/src/fn/tssop.ts
index bd2d67e9..3a21c58b 100644
--- a/src/fn/tssop.ts
+++ b/src/fn/tssop.ts
@@ -66,6 +66,9 @@ export const tssop = (
? parameters.w - length.parse("0.15mm")
: parameters.w
+ /** Distance from the origin to the inner edge of the pad columns. */
+ let padInnerHalfX = Number.POSITIVE_INFINITY
+ let padOuterHalfY = 0
for (let i = 0; i < parameters.num_pins; i++) {
const { x, y } = getTssopCoords({
num_pins: parameters.num_pins,
@@ -75,6 +78,8 @@ export const tssop = (
pl: parameters.pl,
legsoutside: parameters.legsoutside,
})
+ padInnerHalfX = Math.min(padInnerHalfX, Math.abs(x) - parameters.pl / 2)
+ padOuterHalfY = Math.max(padOuterHalfY, Math.abs(y) + parameters.pw / 2)
pads.push(rectpad(i + 1, x, y, parameters.pl, parameters.pw, cornerRadius))
}
@@ -83,9 +88,26 @@ export const tssop = (
}
const m = Math.min(1, parameters.p / 2)
- const sw =
- parameters.w - (parameters.legsoutside ? 0 : parameters.pl * 2) - 0.2
- const sh = (parameters.num_pins / 2 - 1) * parameters.p + parameters.pw + m
+ const silkscreenStrokeWidth = parameters.silkscreen_stroke_width ?? 0.1
+ // Inset the body outline from the pad edges by IPC's silk-to-pad clearance
+ // plus half the stroke. The previous flat `- 0.2` was a total inset of 0.1
+ // per side, which is less than the stroke half-width alone once the pads
+ // reach the body (e.g. `tssop10_w3mm_p0.5mm` overlapped by 0.025mm).
+ const silkPadClearance = 0.2 + silkscreenStrokeWidth / 2
+ // Derive both from the pads that were actually placed, rather than from `w`.
+ // Fine-pitch parts shift the pads inward (`wForPads`), so a width-based
+ // formula drifts from the real copper — that drift is what put the outline on
+ // the pads for `tssop10_w3mm_p0.5mm`.
+ const sw = Math.min(
+ parameters.w - (parameters.legsoutside ? 0 : parameters.pl * 2) - 0.2,
+ (padInnerHalfX - silkPadClearance) * 2,
+ )
+ // The outline's horizontal runs pass above and below the outermost pads, so
+ // the height needs the same clearance the width gets.
+ const sh = Math.max(
+ (parameters.num_pins / 2 - 1) * parameters.p + parameters.pw + m,
+ (padOuterHalfY + silkPadClearance) * 2,
+ )
const silkscreenRefText: SilkscreenRef = silkscreenRef(
0,
sh / 2 + 0.4,
@@ -96,7 +118,7 @@ export const tssop = (
layer: "top",
pcb_component_id: "",
pcb_silkscreen_path_id: "silkscreen_path_1",
- stroke_width: parameters.silkscreen_stroke_width ?? 0.1,
+ stroke_width: silkscreenStrokeWidth,
route: [
{ x: -sw / 2, y: -sh / 2 },
{ x: -sw / 2, y: sh / 2 },
diff --git a/tests/__snapshots__/sot457.snap.svg b/tests/__snapshots__/sot457.snap.svg
index 25cdcc41..9eb2211e 100644
--- a/tests/__snapshots__/sot457.snap.svg
+++ b/tests/__snapshots__/sot457.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/sot457_wave.snap.svg b/tests/__snapshots__/sot457_wave.snap.svg
index da07d7ce..f7831161 100644
--- a/tests/__snapshots__/sot457_wave.snap.svg
+++ b/tests/__snapshots__/sot457_wave.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/tssop20_w6.5mm_p0.65mm.snap.svg b/tests/__snapshots__/tssop20_w6.5mm_p0.65mm.snap.svg
index 569ca803..01f042af 100644
--- a/tests/__snapshots__/tssop20_w6.5mm_p0.65mm.snap.svg
+++ b/tests/__snapshots__/tssop20_w6.5mm_p0.65mm.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/tssop8.snap.svg b/tests/__snapshots__/tssop8.snap.svg
index 7ec8ea1c..7c1e6df9 100644
--- a/tests/__snapshots__/tssop8.snap.svg
+++ b/tests/__snapshots__/tssop8.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/tssop8_thermalpad2x2.4mm.snap.svg b/tests/__snapshots__/tssop8_thermalpad2x2.4mm.snap.svg
index 33308023..bddf18ec 100644
--- a/tests/__snapshots__/tssop8_thermalpad2x2.4mm.snap.svg
+++ b/tests/__snapshots__/tssop8_thermalpad2x2.4mm.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/tssop8_w5.3mm_p1.27mm.snap.svg b/tests/__snapshots__/tssop8_w5.3mm_p1.27mm.snap.svg
index 361e4396..ea27bf99 100644
--- a/tests/__snapshots__/tssop8_w5.3mm_p1.27mm.snap.svg
+++ b/tests/__snapshots__/tssop8_w5.3mm_p1.27mm.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/kicad-parity/__snapshots__/sot457.snap.svg b/tests/kicad-parity/__snapshots__/sot457.snap.svg
index 4f6387bc..79f2e780 100644
--- a/tests/kicad-parity/__snapshots__/sot457.snap.svg
+++ b/tests/kicad-parity/__snapshots__/sot457.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/kicad-parity/__snapshots__/tssop10.snap.svg b/tests/kicad-parity/__snapshots__/tssop10.snap.svg
index 7f14ac14..8f714b95 100644
--- a/tests/kicad-parity/__snapshots__/tssop10.snap.svg
+++ b/tests/kicad-parity/__snapshots__/tssop10.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/tssop.test.ts b/tests/tssop.test.ts
index 73d5facf..65bdf811 100644
--- a/tests/tssop.test.ts
+++ b/tests/tssop.test.ts
@@ -25,3 +25,71 @@ test("tssop8", () => {
const svgContent = convertCircuitJsonToPcbSvg(soup)
expect(svgContent).toMatchSvgSnapshot(import.meta.path, "tssop8")
})
+
+test("tssop and sot457 silkscreen keeps clear of every pad", () => {
+ // Both outlines used to be derived from `w` rather than from the pads that
+ // were actually placed. Fine-pitch tssop shifts its pads inward, so the
+ // outline drifted onto the copper (tssop10_w3mm_p0.5mm overlapped by
+ // 0.025mm), and sot457's pin 1 marker was offset by a multiple of the pad
+ // *width*, which lands on the pad when pl and pw are set independently.
+ const footprints = [
+ "tssop10_w3mm_p0.5mm",
+ "tssop8",
+ "tssop14",
+ "tssop16",
+ "tssop20",
+ "tssop28",
+ "tssop20_w6.5mm_p0.65mm",
+ "sot457",
+ "sot457_w2_pl1.5_pw0.6",
+ ]
+
+ const distanceToPad = (point: { x: number; y: number }, pad: any) => {
+ const dx = Math.max(
+ pad.x - pad.width / 2 - point.x,
+ 0,
+ point.x - (pad.x + pad.width / 2),
+ )
+ const dy = Math.max(
+ pad.y - pad.height / 2 - point.y,
+ 0,
+ point.y - (pad.y + pad.height / 2),
+ )
+ return Math.sqrt(dx * dx + dy * dy)
+ }
+
+ for (const name of footprints) {
+ const circuitJson = fp.string(name).circuitJson()
+ const pads = circuitJson.filter((e) => e.type === "pcb_smtpad") as any[]
+ const paths = circuitJson.filter(
+ (e) => e.type === "pcb_silkscreen_path",
+ ) as any[]
+
+ expect(paths.length).toBeGreaterThan(0)
+
+ let minClearance = Number.POSITIVE_INFINITY
+ for (const path of paths) {
+ const strokeHalfWidth = (path.stroke_width ?? 0) / 2
+ for (let i = 0; i < path.route.length - 1; i++) {
+ const a = path.route[i]
+ const b = path.route[i + 1]
+ for (let t = 0; t <= 1; t += 0.01) {
+ const point = { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t }
+ for (const pad of pads) {
+ minClearance = Math.min(
+ minClearance,
+ distanceToPad(point, pad) - strokeHalfWidth,
+ )
+ }
+ }
+ }
+ }
+
+ expect(Number.isFinite(minClearance)).toBe(true)
+ // IPC silkscreen-to-pad clearance, the same 0.2mm KiCad leaves.
+ expect({ name, clears: minClearance >= 0.2 - 1e-6 }).toEqual({
+ name,
+ clears: true,
+ })
+ }
+})