pixalynx/usb3-hub-10g
A USB 3.2 Gen 2 six-port hub board with USB-C PD power input, regulated 5 V/3.3 V/1.15 V rails, one upstream and four protected downstream USB-A/USB-C ports, SuperSpeed muxes, ESD protection, and controlled port power.
- Version
- 0.0.1
- License
- unset
- Stars
- 0
lib/geometry.ts
/** Small PCB geometry helpers shared by the three boards.
*
* tscircuit `<trace pcbPath>` points are interpreted as offsets from the centre
* of the component that owns the trace's anchor pad (the `from` pad unless
* `pcbPathRelativeTo` names another port), rotated by that component's
* `pcbRotation`. Board-frame coordinates therefore have to be converted before
* they are handed to a trace. `toLocal` does that conversion.
*/
export type Pt = { x: number; y: number }
export function toLocal(p: Pt, centre: Pt, rotationDeg = 0): Pt {
const dx = p.x - centre.x, dy = p.y - centre.y
const a = (-rotationDeg * Math.PI) / 180
const c = Math.cos(a), s = Math.sin(a)
return { x: +(dx * c - dy * s).toFixed(4), y: +(dx * s + dy * c).toFixed(4) }
}
type Layer = "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4"
type PathPt = Pt & { via?: boolean; fromLayer?: Layer; toLayer?: Layer }
/** Two tscircuit copper-pour traps, both of which leave a pour flooding over foreign copper (a short):
* 1. a pcbPath via only gets the layers between its from and to layer, and the pour solver cuts antipads on those
* layers alone, yet every via here is drilled through: a bottom->inner2 signal via would sit bare in the top and
* inner1 GND pours. So each via is authored as a full top->bottom via, followed (same spot) by a bottom->target
* step when the trace continues on another layer.
* 2. the pour solver only treats runs of consecutive *wire* points as trace obstacles; a via point ends the run, so a
* pad->via or via->next-point segment is invisible to it. A wire point is therefore added on the via, on both
* sides (zero-length hops, same copper). */
export function throughVia<T extends PathPt>(p: T): PathPt[] {
if (!p.via) return [p]
const out: PathPt[] = [{ x: p.x, y: p.y }, { x: p.x, y: p.y, via: true, fromLayer: "top", toLayer: "bottom" }]
if (p.toLayer && p.toLayer !== "bottom") out.push({ x: p.x, y: p.y, via: true, fromLayer: "bottom", toLayer: p.toLayer })
out.push({ x: p.x, y: p.y })
return out
}
export const throughVias = (points: PathPt[]) => points.flatMap(throughVia)
/** Convert a board-frame polyline (optionally with layer changes) into pcbPath entries (vias drilled through). */
export function localPath(points: (Pt & { via?: boolean; toLayer?: Layer })[], centre: Pt, rotationDeg = 0) {
return points.flatMap((p) => throughVia({ ...toLocal(p, centre, rotationDeg), ...(p.via ? { via: true, toLayer: p.toLayer } : {}) }))
}
export const round = (v: number, d = 3) => +v.toFixed(d)