@tscircuit/footprinter 0.0.23 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/index.cjs +12112 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +37 -0
  4. package/package.json +4 -1
  5. package/.github/workflows/release.yml +0 -19
  6. package/ava.config.js +0 -5
  7. package/biome.json +0 -15
  8. package/src/fn/bga.ts +0 -138
  9. package/src/fn/cap.ts +0 -7
  10. package/src/fn/diode.ts +0 -8
  11. package/src/fn/dip.ts +0 -118
  12. package/src/fn/led.ts +0 -6
  13. package/src/fn/mlp.ts +0 -13
  14. package/src/fn/qfn.ts +0 -10
  15. package/src/fn/qfp.ts +0 -19
  16. package/src/fn/quad.ts +0 -285
  17. package/src/fn/res.ts +0 -6
  18. package/src/fn/soic.ts +0 -106
  19. package/src/footprinter.ts +0 -131
  20. package/src/helpers/get-quad-pin-map.ts +0 -106
  21. package/src/helpers/is-not-null.ts +0 -3
  22. package/src/helpers/passive-fn.ts +0 -175
  23. package/src/helpers/platedhole.ts +0 -21
  24. package/src/helpers/rectpad.ts +0 -22
  25. package/src/helpers/u-curve.ts +0 -6
  26. package/src/helpers/zod/ALPHABET.ts +0 -1
  27. package/src/helpers/zod/dim-2d.ts +0 -17
  28. package/src/helpers/zod/function-call.ts +0 -16
  29. package/src/helpers/zod/now-defined.ts +0 -2
  30. package/src/helpers/zod/pin-order-specifier.ts +0 -13
  31. package/src/index.ts +0 -1
  32. package/tests/bga.test.ts +0 -49
  33. package/tests/cap.test.ts +0 -30
  34. package/tests/dip.test.ts +0 -45
  35. package/tests/fixtures/get-test-fixture.ts +0 -20
  36. package/tests/fixtures/index.ts +0 -32
  37. package/tests/mlp.test.ts +0 -10
  38. package/tests/qfn.test.ts +0 -10
  39. package/tests/qfp.test.ts +0 -18
  40. package/tests/quad.test.ts +0 -24
  41. package/tests/res.test.ts +0 -11
  42. package/tests/slop/slop1.test.ts +0 -46
  43. package/tests/soic.test.ts +0 -11
  44. package/tsconfig.json +0 -26
@@ -1,175 +0,0 @@
1
- import type { AnySoupElement } from "@tscircuit/soup"
2
- import { rectpad } from "../helpers/rectpad"
3
- import mm from "@tscircuit/mm"
4
- import { platedhole } from "./platedhole"
5
-
6
- type StandardSize = {
7
- imperial: string
8
- metric: string
9
- Z_mm_min: number
10
- G_mm_min: number
11
- X_mm_min: number
12
- C_mm_ref: number
13
- }
14
-
15
- // https://www.worthingtonassembly.com/perfect-0201-footprint
16
- // https://static1.squarespace.com/static/54982a02e4b02e9f5e5d9ca7/t/660c692f69a0d83a4afecdf0/1712089391915/Discrete+Component+Footprints.pdf
17
- // https://page.venkel.com/hubfs/Resources/Technical/Resistors%20Landing%20Pattern.pdf
18
- const sizes = [
19
- {
20
- imperial: "01005",
21
- metric: "0402",
22
- Z_mm_min: 0.58,
23
- G_mm_min: 0.18,
24
- X_mm_min: 0.21,
25
- C_mm_ref: 0.038,
26
- },
27
- {
28
- imperial: "0201",
29
- metric: "0603",
30
- Z_mm_min: 0.9,
31
- G_mm_min: 0.3,
32
- X_mm_min: 0.3,
33
- C_mm_ref: 0.6,
34
- },
35
- {
36
- imperial: "0402",
37
- metric: "1005",
38
- Z_mm_min: 1.6,
39
- G_mm_min: 0.4,
40
- X_mm_min: 0.7,
41
- C_mm_ref: 1,
42
- },
43
- {
44
- imperial: "0603",
45
- metric: "1608",
46
- Z_mm_min: 2.6,
47
- G_mm_min: 0.6,
48
- X_mm_min: 1.0,
49
- C_mm_ref: 1.7,
50
- },
51
- {
52
- imperial: "0805",
53
- metric: "2012",
54
- Z_mm_min: 3.0,
55
- G_mm_min: 0.6,
56
- X_mm_min: 1.2,
57
- C_mm_ref: 1.9,
58
- },
59
- {
60
- imperial: "1206",
61
- metric: "3216",
62
- Z_mm_min: 4.2,
63
- G_mm_min: 1.2,
64
- X_mm_min: 1.4,
65
- C_mm_ref: 2.8,
66
- },
67
- {
68
- imperial: "1210",
69
- metric: "3225",
70
- Z_mm_min: 4.2,
71
- G_mm_min: 1.2,
72
- X_mm_min: 2.4,
73
- C_mm_ref: 2.8,
74
- },
75
- {
76
- imperial: "2010",
77
- metric: "5025",
78
- Z_mm_min: 6.0,
79
- G_mm_min: 2.6,
80
- X_mm_min: 2.4,
81
- C_mm_ref: 4.4,
82
- },
83
- {
84
- imperial: "2512",
85
- metric: "6332",
86
- Z_mm_min: 7.2,
87
- G_mm_min: 3.8,
88
- X_mm_min: 3.0,
89
- C_mm_ref: 5.6,
90
- },
91
- ]
92
- const metricMap: Record<string, StandardSize> = sizes.reduce((acc: any, s) => {
93
- acc[s.metric] = s
94
- return acc
95
- }, {})
96
- const imperialMap: Record<string, StandardSize> = sizes.reduce(
97
- (acc: any, s) => {
98
- acc[s.imperial] = s
99
- return acc
100
- },
101
- {}
102
- )
103
-
104
- export type PassiveDef = {
105
- tht: boolean
106
- p: number
107
- pw?: number
108
- ph?: number
109
- metric?: string
110
- imperial?: string
111
- w?: number
112
- h?: number
113
- }
114
-
115
- const deriveXFromH = (h: number) => 0.079 * h ** 2 + 0.94 * h - 0.009
116
- const deriveZFromW = (w: number) => 1.09 * w + 0.6
117
- const deriveGFromW = (w: number) => 0.59 * w - 0.31
118
- const deriveCFromW = (w: number) => -0.01 * w ** 2 + 0.94 * w + 0.03
119
-
120
- export const passive = (params: PassiveDef): AnySoupElement[] => {
121
- let { tht, p, pw, ph, metric, imperial, w, h } = params
122
-
123
- if (typeof w === "string") w = mm(w)
124
- if (typeof h === "string") h = mm(h)
125
- if (typeof p === "string") p = mm(p)
126
- if (typeof pw === "string") pw = mm(pw)
127
- if (typeof ph === "string") ph = mm(ph)
128
-
129
- if (h! > w!) {
130
- throw new Error(
131
- "height cannot be greater than width (rotated footprint not yet implemented)"
132
- )
133
- }
134
-
135
- /** standard size */
136
- let sz: StandardSize | undefined
137
- if (metric) {
138
- sz = metricMap[metric]
139
- }
140
-
141
- if (imperial) {
142
- sz = imperialMap[imperial]
143
- }
144
-
145
- if (!sz && w && h && !pw && !ph) {
146
- sz = {
147
- imperial: "custom",
148
- metric: "custom",
149
- Z_mm_min: deriveZFromW(w),
150
- G_mm_min: deriveGFromW(w),
151
- X_mm_min: deriveXFromH(h),
152
- C_mm_ref: deriveCFromW(w),
153
- }
154
- }
155
-
156
- if (sz) {
157
- w = sz.Z_mm_min
158
- h = sz.X_mm_min
159
- p = sz.C_mm_ref
160
- pw = (sz.Z_mm_min - sz.G_mm_min) / 2
161
- ph = (sz.Z_mm_min - sz.G_mm_min) / 2
162
- }
163
-
164
- if (pw === undefined) throw new Error(`could not infer pad width`)
165
- if (ph === undefined) throw new Error(`could not infer pad width`)
166
-
167
- if (tht) {
168
- return [
169
- platedhole(1, -p / 2, 0, pw, (pw * 1) / 0.8),
170
- platedhole(2, -p / 2, 0, pw, (pw * 1) / 0.8),
171
- ]
172
- } else {
173
- return [rectpad(1, -p / 2, 0, pw, ph), rectpad(2, p / 2, 0, pw, ph)]
174
- }
175
- }
@@ -1,21 +0,0 @@
1
- import type { PCBPlatedHole, PCBSMTPad } from "@tscircuit/soup"
2
- import { mm } from "@tscircuit/mm"
3
-
4
- export const platedhole = (
5
- pn: number,
6
- x: number,
7
- y: number,
8
- id: number | string,
9
- od: number | string
10
- ): PCBPlatedHole => {
11
- return {
12
- type: "pcb_plated_hole",
13
- x,
14
- y,
15
- hole_diameter: mm(id),
16
- outer_diameter: mm(od),
17
- pcb_port_id: "",
18
- layers: ["top", "bottom"],
19
- port_hints: [pn.toString()],
20
- }
21
- }
@@ -1,22 +0,0 @@
1
- import type { PCBSMTPad } from "@tscircuit/soup"
2
- export const rectpad = (
3
- pn: number | Array<string | number>,
4
- x: number,
5
- y: number,
6
- w: number,
7
- h: number
8
- ): PCBSMTPad => {
9
- return {
10
- type: "pcb_smtpad",
11
- x,
12
- y,
13
- width: w,
14
- height: h,
15
- layer: "top",
16
- shape: "rect",
17
- pcb_smtpad_id: "",
18
- port_hints: Array.isArray(pn)
19
- ? pn.map((item) => item.toString())
20
- : [pn.toString()],
21
- }
22
- }
@@ -1,6 +0,0 @@
1
- export const u_curve = Array.from({ length: 9 }, (_, i) =>
2
- Math.cos((i / 8) * Math.PI - Math.PI)
3
- ).map((x) => ({
4
- x,
5
- y: -Math.sqrt(1 - x ** 2),
6
- }))
@@ -1 +0,0 @@
1
- export const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -1,17 +0,0 @@
1
- import { z } from "zod"
2
-
3
- export const dim2d = z
4
- .string()
5
- .transform((a) => {
6
- const [x, y] = a.split(/[x ]/)
7
- return {
8
- x: parseFloat(x),
9
- y: parseFloat(y),
10
- }
11
- })
12
- .pipe(
13
- z.object({
14
- x: z.number(),
15
- y: z.number(),
16
- })
17
- )
@@ -1,16 +0,0 @@
1
- import { z } from "zod"
2
-
3
- export const function_call = z
4
- .string()
5
- .or(z.array(z.any()))
6
- .transform((a) => {
7
- if (Array.isArray(a)) return a
8
- if (a.startsWith("(") && a.endsWith(")")) {
9
- a = a.slice(1, -1)
10
- }
11
- return a.split(",").map((v) => {
12
- const numVal = Number(v)
13
- return isNaN(numVal) ? v : numVal
14
- })
15
- })
16
- .pipe(z.array(z.string().or(z.number())))
@@ -1,2 +0,0 @@
1
- /* Define a utility type to make certain keys required */
2
- export type NowDefined<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
@@ -1,13 +0,0 @@
1
- import { z } from "zod"
2
- export const pin_order_specifier = z.enum([
3
- "leftside",
4
- "topside",
5
- "rightside",
6
- "bottomside",
7
- "toppin",
8
- "bottompin",
9
- "leftpin",
10
- "rightpin",
11
- ])
12
-
13
- export type PinOrderSpecifier = z.infer<typeof pin_order_specifier>
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./footprinter"
package/tests/bga.test.ts DELETED
@@ -1,49 +0,0 @@
1
- import test from "ava"
2
- import { fp } from "../src/footprinter"
3
- import { AnySoupElement } from "@tscircuit/soup"
4
- import { toPinPositionString } from "./fixtures"
5
-
6
- test("bga footprint", (t) => {
7
- const soup = fp()
8
- .bga(8)
9
- .w("4mm")
10
- .h("4mm")
11
- .grid("3x3")
12
- .missing("center")
13
- .p(1)
14
- .soup()
15
- // 16pins, 4mm x 4mm, 8x8 grid, 1.27mm pitch
16
- const ps = toPinPositionString(soup)
17
-
18
- t.is(
19
- ps,
20
- `
21
- 1 : -1.00 1.00
22
- 2 : 0.00 1.00
23
- 3 : 1.00 1.00
24
- 4 : -1.00 0.00
25
- 5 : 1.00 0.00
26
- 6 : -1.00 -1.00
27
- 7 : 0.00 -1.00
28
- 8 : 1.00 -1.00
29
- `.trim()
30
- )
31
- })
32
-
33
- test("bga7_w8_h8_grid3x3_p1_missing(center,B1)", (t) => {
34
- const soup = fp.string("bga7_w8_h8_grid3x3_p1_missing(center,B1)").soup()
35
- const ps = toPinPositionString(soup)
36
-
37
- t.is(
38
- ps,
39
- `
40
- 1 : -1.00 1.00
41
- 2 : 0.00 1.00
42
- 3 : 1.00 1.00
43
- 4 : 1.00 0.00
44
- 5 : -1.00 -1.00
45
- 6 : 0.00 -1.00
46
- 7 : 1.00 -1.00
47
- `.trim()
48
- )
49
- })
package/tests/cap.test.ts DELETED
@@ -1,30 +0,0 @@
1
- import test from "ava"
2
- import { fp } from "../src/footprinter"
3
- import { AnySoupElement } from "@tscircuit/soup"
4
- import { toPinPositionString } from "./fixtures"
5
-
6
- test("cap footprint", (t) => {
7
- const soup = fp().cap().imperial("0402").soup()
8
- const ps = toPinPositionString(soup)
9
-
10
- t.is(
11
- ps,
12
- `
13
- 1 : -0.50 0.00
14
- 2 : 0.50 0.00
15
- `.trim()
16
- )
17
- })
18
-
19
- test("cap_imperial0402", (t) => {
20
- const soup = fp.string("cap_imperial0402").soup()
21
- const ps = toPinPositionString(soup)
22
-
23
- t.is(
24
- ps,
25
- `
26
- 1 : -0.50 0.00
27
- 2 : 0.50 0.00
28
- `.trim()
29
- )
30
- })
package/tests/dip.test.ts DELETED
@@ -1,45 +0,0 @@
1
- import test from "ava"
2
- import { fp } from "../src/footprinter"
3
- import type { AnySoupElement } from "@tscircuit/soup"
4
- import { getTestFixture, toPinPositionString } from "./fixtures"
5
-
6
- test("dip params", (t) => {
7
- t.deepEqual(fp().dip(4).w(7.62).params(), {
8
- dip: true,
9
- num_pins: 4,
10
- w: 7.62,
11
- })
12
- })
13
-
14
- test("dip footprint", (t) => {
15
- const soup = fp().dip(4).w(4).p(2).soup()
16
- const ps = toPinPositionString(soup)
17
-
18
- t.is(
19
- ps,
20
- `
21
- 1 : -2.00 1.00
22
- 2 : -2.00 -1.00
23
- 3 : 2.00 -1.00
24
- 4 : 2.00 1.00
25
- `.trim()
26
- )
27
- })
28
-
29
- test("dip4_w3.00mm", async (t) => {
30
- const { fp, logSoup } = await getTestFixture(t)
31
- const soup = fp.string("dip4_w3.00mm").soup()
32
- const ps = toPinPositionString(soup)
33
-
34
- t.is(
35
- ps,
36
- `
37
- 1 : -1.50 1.27
38
- 2 : -1.50 -1.27
39
- 3 : 1.50 -1.27
40
- 4 : 1.50 1.27
41
- `.trim()
42
- )
43
-
44
- await logSoup(soup)
45
- })
@@ -1,20 +0,0 @@
1
- import type { ExecutionContext } from "ava"
2
- import { fp } from "../../src"
3
- import { logSoup } from "@tscircuit/log-soup"
4
- import type { AnySoupElement } from "@tscircuit/soup"
5
-
6
- export const getTestFixture = async (t: ExecutionContext) => {
7
- return {
8
- fp,
9
- logSoup: (soup: AnySoupElement[]) => {
10
- if (process.env.CI) return
11
- if (process.env.FULL_RUN) return
12
- return logSoup(`footprinter: ${t.title}`, soup)
13
- },
14
- logSoupWithPrefix: (prefix: string, soup: AnySoupElement[]) => {
15
- if (process.env.CI) return
16
- if (process.env.FULL_RUN) return
17
- return logSoup(`footprinter: ${t.title} ${prefix}`, soup)
18
- },
19
- }
20
- }
@@ -1,32 +0,0 @@
1
- import type { AnySoupElement } from "@tscircuit/soup"
2
-
3
- export { getTestFixture } from "./get-test-fixture"
4
-
5
- export const toPinPositionString = (soup: AnySoupElement[]) => {
6
- return soup
7
- .map((e: AnySoupElement) => {
8
- if (e.type === "pcb_plated_hole") {
9
- return {
10
- x: e.x,
11
- y: e.y,
12
- pn: e.port_hints?.[0],
13
- }
14
- } else if (e.type === "pcb_smtpad") {
15
- return {
16
- x: e.x,
17
- y: e.y,
18
- pn: e.port_hints?.[0],
19
- }
20
- }
21
- // TODO other types
22
- })
23
- .filter(Boolean)
24
- .sort((a: any, b: any) => a.pn - b.pn)
25
- .map(
26
- (e: any) =>
27
- `${e.pn.padEnd(2)}: ${e.x.toFixed(2).padStart(5)} ${e.y
28
- .toFixed(2)
29
- .padStart(5)}`
30
- )
31
- .join("\n")
32
- }
package/tests/mlp.test.ts DELETED
@@ -1,10 +0,0 @@
1
- import test from "ava"
2
- import { getTestFixture, toPinPositionString } from "./fixtures"
3
-
4
- test("mlp16_w4_h4_p0.5mm", async (t) => {
5
- const { fp, logSoup } = await getTestFixture(t)
6
- const soup = fp.string("mlp16_w4_h4_p0.5mm").soup()
7
-
8
- await logSoup(soup)
9
- t.pass()
10
- })
package/tests/qfn.test.ts DELETED
@@ -1,10 +0,0 @@
1
- import test from "ava"
2
- import { getTestFixture, toPinPositionString } from "./fixtures"
3
-
4
- test("qfn16_w4_h4_p0.65mm", async (t) => {
5
- const { fp, logSoup } = await getTestFixture(t)
6
- const soup = fp.string("qfn16_w4_h4_p0.65mm").soup()
7
-
8
- await logSoup(soup)
9
- t.pass()
10
- })
package/tests/qfp.test.ts DELETED
@@ -1,18 +0,0 @@
1
- import test from "ava"
2
- import { getTestFixture, toPinPositionString } from "./fixtures"
3
-
4
- test("qfp48_w14_p1mm", async (t) => {
5
- const { fp, logSoup } = await getTestFixture(t)
6
- const soup = fp.string("qfp48_w14_p1mm").soup()
7
-
8
- await logSoup(soup)
9
- t.pass()
10
- })
11
-
12
- test("qfp48_w14_p1mm_startingpin(topside,leftpin)", async (t) => {
13
- const { fp, logSoup } = await getTestFixture(t)
14
- const soup = fp.string("qfp48_w14_p1mm_startingpin(topside,leftpin)").soup()
15
-
16
- await logSoup(soup)
17
- t.pass()
18
- })
@@ -1,24 +0,0 @@
1
- import test from "ava"
2
- import { fp } from "../src/footprinter"
3
- import type { AnySoupElement } from "@tscircuit/soup"
4
- import { getTestFixture, toPinPositionString } from "./fixtures"
5
-
6
- test("quad16_w4_l4_p0.4_pw0.25_pl0.4", async (t) => {
7
- const { fp, logSoup } = await getTestFixture(t)
8
- const soup = fp.string("quad16_w4_l4_p0.4_pw0.25_pl0.4").soup()
9
-
10
- await logSoup(soup)
11
- t.pass()
12
- })
13
-
14
- test("quad16_w4_l4_p0.4_pw0.25_pl0.4_thermalpad_startingpin(bottomside,leftpin)", async (t) => {
15
- const { fp, logSoup } = await getTestFixture(t)
16
- const soup = fp
17
- .string(
18
- "quad16_w4_l4_p0.4_pw0.25_pl0.4_thermalpad_startingpin(bottomside,leftpin)"
19
- )
20
- .soup()
21
-
22
- await logSoup(soup)
23
- t.pass()
24
- })
package/tests/res.test.ts DELETED
@@ -1,11 +0,0 @@
1
- import test from "ava"
2
- import { getTestFixture } from "tests/fixtures/get-test-fixture"
3
- import { su } from "@tscircuit/soup-util"
4
-
5
- test("res_imperial0402", async (t) => {
6
- const { fp } = await getTestFixture(t)
7
- const soup = fp.string("res_imperial0402").soup()
8
-
9
- t.is(su(soup).pcb_smtpad.list().length, 2)
10
- t.is(su(soup).pcb_plated_hole.list().length, 0)
11
- })
@@ -1,46 +0,0 @@
1
- import test from "ava"
2
- import { getTestFixture } from "../fixtures"
3
- import type { AnySoupElement } from "@tscircuit/soup"
4
-
5
- /**
6
- * Slop is an underdefined definition.
7
- */
8
- export const SLOP_LIST = [
9
- "dip3",
10
- "bga64",
11
- "bga48",
12
- "bga48_grid8x8",
13
- "bga48_p2_pad0.2",
14
- ]
15
-
16
- test("slop1", async (t) => {
17
- const { fp, logSoupWithPrefix } = await getTestFixture(t)
18
-
19
- const soups: AnySoupElement[][] = []
20
- const failures: Array<{
21
- slop_string: string
22
- error: any
23
- }> = []
24
-
25
- for (const slop of SLOP_LIST) {
26
- try {
27
- const soup = fp.string(slop).soup()
28
- soups.push(soup)
29
- if (slop === SLOP_LIST[SLOP_LIST.length - 1]) {
30
- await logSoupWithPrefix(slop, soup)
31
- }
32
- } catch (e: any) {
33
- failures.push({
34
- slop_string: slop,
35
- error: e,
36
- })
37
- throw e
38
- }
39
- }
40
-
41
- if (failures.length > 0) {
42
- t.fail(`Failures:\n${failures.map((f) => f.slop_string).join("\n")}`)
43
- } else {
44
- t.pass()
45
- }
46
- })
@@ -1,11 +0,0 @@
1
- import test from "ava"
2
- import { getTestFixture } from "tests/fixtures/get-test-fixture"
3
- import { su } from "@tscircuit/soup-util"
4
-
5
- test("soic8_w5.3mm_p1.27mm", async (t) => {
6
- const { fp, logSoup } = await getTestFixture(t)
7
- const soup = fp.string("soic8_w5.3mm_p1.27mm").soup()
8
-
9
- t.is(su(soup).pcb_plated_hole.list().length, 8)
10
- await logSoup(soup)
11
- })
package/tsconfig.json DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Base Options recommended for all projects
4
- "esModuleInterop": true,
5
- "skipLibCheck": true,
6
- "target": "es2022",
7
- "allowJs": true,
8
- "baseUrl": ".",
9
- "resolveJsonModule": true,
10
- "moduleDetection": "force",
11
- "isolatedModules": true,
12
- "verbatimModuleSyntax": true,
13
- // Enable strict type checking so you can catch bugs early
14
- "strict": true,
15
- "noImplicitAny": false,
16
- "noUncheckedIndexedAccess": true,
17
- "noImplicitOverride": true,
18
- // We are not transpiling, so preserve our source code and do not emit files
19
- "module": "preserve",
20
- "noEmit": true,
21
- "lib": ["es2022"]
22
- },
23
- // Include the necessary files for your project
24
- "include": ["**/*.ts", "**/*.tsx"],
25
- "exclude": ["node_modules"]
26
- }