@tscircuit/footprinter 0.0.16 → 0.0.18

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/biome.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
3
+ "organizeImports": {
4
+ "enabled": true
5
+ },
6
+ "linter": {
7
+ "enabled": true,
8
+ "rules": {
9
+ "recommended": true,
10
+ "style": {
11
+ "noNonNullAssertion": "off"
12
+ }
13
+ }
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/footprinter",
3
3
  "type": "module",
4
- "version": "0.0.16",
4
+ "version": "0.0.18",
5
5
  "description": "",
6
6
  "main": "dist/index.cjs",
7
7
  "scripts": {
@@ -12,6 +12,7 @@
12
12
  "author": "",
13
13
  "license": "ISC",
14
14
  "devDependencies": {
15
+ "@biomejs/biome": "^1.7.3",
15
16
  "@tscircuit/log-soup": "^1.0.1",
16
17
  "@tscircuit/soup": "^0.0.17",
17
18
  "@tscircuit/soup-util": "^0.0.11",
package/src/fn/quad.ts CHANGED
@@ -1,10 +1,11 @@
1
- import type { AnySoupElement } from "@tscircuit/soup"
1
+ import type { AnySoupElement, PcbSilkscreenPath } from "@tscircuit/soup"
2
2
  import { z } from "zod"
3
3
  import { length } from "@tscircuit/soup"
4
4
  import type { NowDefined } from "../helpers/zod/now-defined"
5
5
  import { rectpad } from "../helpers/rectpad"
6
6
  import { pin_order_specifier } from "src/helpers/zod/pin-order-specifier"
7
7
  import { getQuadPinMap } from "src/helpers/get-quad-pin-map"
8
+ import { dim2d } from "src/helpers/zod/dim-2d"
8
9
 
9
10
  const base_quad_def = z.object({
10
11
  quad: z.literal(true),
@@ -17,6 +18,7 @@ const base_quad_def = z.object({
17
18
  p: length.default(length.parse("0.5mm")),
18
19
  pw: length.optional(),
19
20
  pl: length.optional(),
21
+ thermalpad: z.union([z.literal(true), dim2d]).optional(),
20
22
  })
21
23
 
22
24
  const quad_def = base_quad_def.transform((v) => {
@@ -58,15 +60,13 @@ export const getQuadCoords = (
58
60
  pn: number, // pin number
59
61
  w: number, // width of the package
60
62
  h: number, // height (length) of the package
61
- p: number // pitch between pins
63
+ p: number, // pitch between pins
64
+ pl: number // length of the pin
62
65
  ) => {
63
66
  const sidePinCount = pinCount / 4
64
67
  const side = SIDES_CCW[Math.floor((pn - 1) / sidePinCount)]
65
68
  const pos = (pn - 1) % sidePinCount
66
69
 
67
- const halfW = w / 2
68
- const halfH = h / 2
69
-
70
70
  /** inner box width */
71
71
  const ibw = p * (sidePinCount - 1)
72
72
  /** inner box height */
@@ -74,27 +74,21 @@ export const getQuadCoords = (
74
74
 
75
75
  switch (side) {
76
76
  case "left":
77
- return { x: -halfW / 2, y: ibh / 2 - pos * p, o: "vert" }
77
+ return { x: -w / 2 + pl / 2, y: ibh / 2 - pos * p, o: "vert" }
78
78
  case "bottom":
79
- return { x: -ibw / 2 + pos * p, y: -halfH / 2, o: "horz" }
79
+ return { x: -ibw / 2 + pos * p, y: -h / 2 + pl / 2, o: "horz" }
80
80
  case "right":
81
- return { x: halfW / 2, y: -ibh / 2 + pos * p, o: "vert" }
81
+ return { x: w / 2 - pl / 2, y: -ibh / 2 + pos * p, o: "vert" }
82
82
  case "top":
83
- return { x: ibw / 2 - pos * p, y: halfH / 2, o: "horz" }
83
+ return { x: ibw / 2 - pos * p, y: h / 2 - pl / 2, o: "horz" }
84
84
  default:
85
85
  throw new Error("Invalid pin number")
86
86
  }
87
87
  }
88
88
 
89
- export const quad = (raw_params: {
90
- quad: true
91
- num_pins: number
92
- w: number
93
- l: number
94
- p?: number
95
- id?: string | number
96
- od?: string | number
97
- }): AnySoupElement[] => {
89
+ export const quad = (
90
+ raw_params: z.input<typeof quad_def>
91
+ ): AnySoupElement[] => {
98
92
  const params = quad_def.parse(raw_params)
99
93
  const pads: AnySoupElement[] = []
100
94
  const pin_map = getQuadPinMap(params)
@@ -108,17 +102,61 @@ export const quad = (raw_params: {
108
102
  i + 1,
109
103
  params.w,
110
104
  params.h,
111
- params.p ?? 0.5
105
+ params.p ?? 0.5,
106
+ params.pl
112
107
  )
113
108
 
114
- let pw = params.pw,
115
- pl = params.pl
109
+ let pw = params.pw
110
+ let pl = params.pl
116
111
  if (orientation === "vert") {
117
112
  ;[pw, pl] = [pl, pw]
118
113
  }
119
114
 
120
- const pn = pin_map[i + 1]
121
- pads.push(rectpad(pn!, x, y, pw, pl))
115
+ const pn = pin_map[i + 1]!
116
+ pads.push(rectpad(pn, x, y, pw, pl))
117
+ }
118
+
119
+ if (params.thermalpad) {
120
+ if (typeof params.thermalpad === "boolean") {
121
+ const sidePinCount = params.num_pins / 4
122
+ const ibw = params.p * (sidePinCount - 1) + params.pw
123
+ const ibh = params.p * (sidePinCount - 1) + params.pw
124
+ pads.push(rectpad(["thermalpad"], 0, 0, ibw, ibh))
125
+ } else {
126
+ pads.push(
127
+ rectpad(["thermalpad"], 0, 0, params.thermalpad.x, params.thermalpad.y)
128
+ )
129
+ }
122
130
  }
123
- return pads
131
+
132
+ // Silkscreen corners
133
+ const silkscreen_corners: PcbSilkscreenPath[] = []
134
+ for (let corner_index = 0; corner_index < 4; corner_index++) {
135
+ const dx = Math.floor(corner_index / 2) * 2 - 1
136
+ const dy = (corner_index % 2) * 2 - 1
137
+ const corner_x = (params.w / 2) * dx
138
+ const corner_y = (params.h / 2) * dy
139
+ silkscreen_corners.push({
140
+ layer: "top",
141
+ pcb_component_id: "",
142
+ pcb_silkscreen_path_id: `pcb_silkscreen_path_${corner_index}`,
143
+ route: [
144
+ {
145
+ x: corner_x - params.pw * dx,
146
+ y: corner_y,
147
+ },
148
+ {
149
+ x: corner_x,
150
+ y: corner_y,
151
+ },
152
+ {
153
+ x: corner_x,
154
+ y: corner_y - params.pw * dy,
155
+ },
156
+ ],
157
+ type: "pcb_silkscreen_path",
158
+ })
159
+ }
160
+
161
+ return [...pads, ...silkscreen_corners]
124
162
  }
@@ -89,7 +89,7 @@ export const getQuadPinMap = ({
89
89
  // decrementing
90
90
  for (let i = 0; i < num_pins; i++) {
91
91
  pin_map.push(current_position_ccw_normal)
92
- if (ccw) {
92
+ if (ccw || !cw) {
93
93
  current_position_ccw_normal++
94
94
  if (current_position_ccw_normal > num_pins) {
95
95
  current_position_ccw_normal = 1
@@ -11,6 +11,14 @@ test("quad16_w4_l4_p0.4_pw0.25_pl0.4", async (t) => {
11
11
  t.pass()
12
12
  })
13
13
 
14
+ test("quad16_w4_l4_p0.4_pw0.25_pl0.4_thermalpad", async (t) => {
15
+ const { fp, logSoup } = await getTestFixture(t)
16
+ const soup = fp.string("quad16_w4_l4_p0.4_pw0.25_pl0.4_thermalpad").soup()
17
+
18
+ await logSoup(soup)
19
+ t.pass()
20
+ })
21
+
14
22
  // const ps = toPinPositionString(soup)
15
23
 
16
24
  // t.is(