@tscircuit/footprinter 0.0.11 → 0.0.13

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/footprinter",
3
3
  "type": "module",
4
- "version": "0.0.11",
4
+ "version": "0.0.13",
5
5
  "description": "",
6
6
  "main": "dist/index.cjs",
7
7
  "scripts": {
package/src/fn/dip.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { AnySoupElement, PcbSilkscreenPath } from "@tscircuit/soup"
2
+ import { u_curve } from "../helpers/u-curve"
2
3
  import { platedhole } from "../helpers/platedhole"
3
4
  import { z } from "zod"
4
5
  import { length } from "@tscircuit/soup"
@@ -91,5 +92,27 @@ export const dip = (raw_params: {
91
92
  platedhole(i + 1, x, y, params.id ?? "0.8mm", params.od ?? "1mm")
92
93
  )
93
94
  }
94
- return platedHoles
95
+ /** silkscreen width */
96
+ const sw = params.w - params.od - 0.4
97
+ const sh = (params.num_pins / 2 - 1) * params.p + params.od + 0.4
98
+ const silkscreenBorder: PcbSilkscreenPath = {
99
+ layer: "top",
100
+ pcb_component_id: "",
101
+ pcb_silkscreen_path_id: "silkscreen_path_1",
102
+ route: [
103
+ { x: -sw / 2, y: -sh / 2 },
104
+ { x: -sw / 2, y: sh / 2 },
105
+ // Little U shape at the top
106
+ ...u_curve.map(({ x, y }) => ({
107
+ x: (x * sw) / 6,
108
+ y: (y * sw) / 6 + sh / 2,
109
+ })),
110
+ { x: sw / 2, y: sh / 2 },
111
+ { x: sw / 2, y: -sh / 2 },
112
+ { x: -sw / 2, y: -sh / 2 },
113
+ ],
114
+ type: "pcb_silkscreen_path",
115
+ }
116
+
117
+ return [...platedHoles, silkscreenBorder]
95
118
  }
package/src/fn/quad.ts ADDED
@@ -0,0 +1,131 @@
1
+ import type { AnySoupElement } from "@tscircuit/soup"
2
+ import { z } from "zod"
3
+ import { length } from "@tscircuit/soup"
4
+ import type { NowDefined } from "../helpers/zod/now-defined"
5
+ import { rectpad } from "../helpers/rectpad"
6
+
7
+ const pin_order_specifier = z.enum([
8
+ "leftside",
9
+ "topside",
10
+ "rightside",
11
+ "bottomside",
12
+ "toppin",
13
+ "bottompin",
14
+ "leftpin",
15
+ "rightpin",
16
+ ])
17
+
18
+ const base_quad_def = z.object({
19
+ quad: z.literal(true),
20
+ cc: z.literal(true).optional(),
21
+ ccw: z.literal(true).optional(),
22
+ startingpin: z.array(pin_order_specifier).optional(),
23
+ num_pins: z.number(),
24
+ w: length.optional(),
25
+ h: length.optional(),
26
+ p: length.default(length.parse("0.5mm")),
27
+ pw: length.optional(),
28
+ pl: length.optional(),
29
+ })
30
+
31
+ const quad_def = base_quad_def.transform((v) => {
32
+ if (v.w && !v.h) {
33
+ v.h = v.w
34
+ } else if (!v.w && v.h) {
35
+ v.w = v.h
36
+ }
37
+
38
+ const side_pin_count = v.num_pins / 4
39
+
40
+ if (!v.p && !v.pw && !v.pl && v.w) {
41
+ // HACK: This is wayyy underspecified
42
+ const approx_pin_size_of_side = side_pin_count + 4
43
+ v.p = v.w / approx_pin_size_of_side
44
+ }
45
+
46
+ if (!v.p && v.w && v.h && v.pw && v.pl) {
47
+ // HACK: This is wayyy underspecified
48
+ v.p = (v.w - v.pl * 2) / (side_pin_count - 1)
49
+ }
50
+
51
+ if (v.p && !v.pw && !v.pl) {
52
+ v.pw = v.p / 2
53
+ v.pl = v.p / 2
54
+ } else if (!v.pw) {
55
+ v.pw = v.pl! * (0.6 / 1.0)
56
+ } else if (!v.pl) {
57
+ v.pl = v.pw! * (1.0 / 0.6)
58
+ }
59
+
60
+ return v as NowDefined<typeof v, "w" | "h" | "p" | "pw" | "pl">
61
+ })
62
+
63
+ const SIDES_CCW = ["left", "bottom", "right", "top"] as const
64
+
65
+ export const getQuadCoords = (
66
+ pinCount: number,
67
+ pn: number, // pin number
68
+ w: number, // width of the package
69
+ h: number, // height (length) of the package
70
+ p: number // pitch between pins
71
+ ) => {
72
+ const sidePinCount = pinCount / 4
73
+ const side = SIDES_CCW[Math.floor((pn - 1) / sidePinCount)]
74
+ const pos = (pn - 1) % sidePinCount
75
+
76
+ const halfW = w / 2
77
+ const halfH = h / 2
78
+
79
+ /** inner box width */
80
+ const ibw = p * (sidePinCount - 1)
81
+ /** inner box height */
82
+ const ibh = p * (sidePinCount - 1)
83
+
84
+ switch (side) {
85
+ case "left":
86
+ return { x: -halfW / 2, y: ibh / 2 - pos * p, o: "vert" }
87
+ case "bottom":
88
+ return { x: -ibw / 2 + pos * p, y: -halfH / 2, o: "horz" }
89
+ case "right":
90
+ return { x: halfW / 2, y: -ibh / 2 + pos * p, o: "vert" }
91
+ case "top":
92
+ return { x: ibw / 2 - pos * p, y: halfH / 2, o: "horz" }
93
+ default:
94
+ throw new Error("Invalid pin number")
95
+ }
96
+ }
97
+
98
+ export const quad = (raw_params: {
99
+ quad: true
100
+ num_pins: number
101
+ w: number
102
+ l: number
103
+ p?: number
104
+ id?: string | number
105
+ od?: string | number
106
+ }): AnySoupElement[] => {
107
+ const params = quad_def.parse(raw_params)
108
+ const pads: AnySoupElement[] = []
109
+ for (let i = 0; i < params.num_pins; i++) {
110
+ const {
111
+ x,
112
+ y,
113
+ o: orientation,
114
+ } = getQuadCoords(
115
+ params.num_pins,
116
+ i + 1,
117
+ params.w,
118
+ params.h,
119
+ params.p ?? 0.5
120
+ )
121
+
122
+ let pw = params.pw,
123
+ pl = params.pl
124
+ if (orientation === "vert") {
125
+ ;[pw, pl] = [pl, pw]
126
+ }
127
+
128
+ pads.push(rectpad(i + 1, x, y, pw, pl))
129
+ }
130
+ return pads
131
+ }
@@ -5,6 +5,7 @@ import { led } from "./fn/led"
5
5
  import { res } from "./fn/res"
6
6
  import { bga } from "./fn/bga"
7
7
  import { soic } from "./fn/soic"
8
+ import { quad } from "./fn/quad"
8
9
  import type { AnySoupElement } from "@tscircuit/soup"
9
10
  import { isNotNull } from "./helpers/is-not-null"
10
11
 
@@ -81,6 +82,7 @@ export const footprinter = (): Footprinter & { string: typeof string } => {
81
82
  if ("res" in target) return () => res(target)
82
83
  if ("bga" in target) return () => bga(target)
83
84
  if ("soic" in target) return () => soic(target)
85
+ if ("quad" in target) return () => quad(target)
84
86
 
85
87
  return () => {
86
88
  // TODO improve error
@@ -0,0 +1,37 @@
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
+ // const ps = toPinPositionString(soup)
15
+
16
+ // t.is(
17
+ // ps,
18
+ // `
19
+ // 1 : -1.75 -2.00
20
+ // 2 : -1.25 -2.00
21
+ // 3 : -0.75 -2.00
22
+ // 4 : -0.25 -2.00
23
+ // 5 : 0.25 -2.00
24
+ // 6 : 0.75 -2.00
25
+ // 7 : 1.25 -2.00
26
+ // 8 : 1.75 -2.00
27
+ // 9 : 2.00 -1.75
28
+ // 10: 2.00 -1.25
29
+ // 11: 2.00 -0.75
30
+ // 12: 2.00 -0.25
31
+ // 13: 2.00 0.25
32
+ // 14: 2.00 0.75
33
+ // 15: 2.00 1.25
34
+ // 16: 2.00 1.75
35
+ // `.trim()
36
+ // )
37
+ // })