From d18be9254c8f0c4402ce1560b4da3626fa974dfd Mon Sep 17 00:00:00 2001 From: seveibar Date: Fri, 20 Sep 2024 12:03:18 -0700 Subject: [PATCH 1/3] add reference docs for better aider --- docs/CIRCUIT_JSON_PCB_ELEMENTS.md | 293 ++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 docs/CIRCUIT_JSON_PCB_ELEMENTS.md diff --git a/docs/CIRCUIT_JSON_PCB_ELEMENTS.md b/docs/CIRCUIT_JSON_PCB_ELEMENTS.md new file mode 100644 index 0000000..9541881 --- /dev/null +++ b/docs/CIRCUIT_JSON_PCB_ELEMENTS.md @@ -0,0 +1,293 @@ +# Circuit JSON Specification: PCB Component Overview + +> Created at 2024-09-20T18:37:19.158Z +> Latest Version: https://github.com/tscircuit/circuit-json/blob/main/docs/PCB_COMPONENT_OVERVIEW.md + +Any type below can be imported from `circuit-json`. Every type has a corresponding +snake_case version which is a zod type that can be used to parse unknown json, +for example `PcbComponent` has a `pcb_component.parse` function that you +can also import. + +```ts +export interface PcbFabricationNotePath { + type: "pcb_fabrication_note_path" + pcb_fabrication_note_path_id: string + pcb_component_id: string + layer: LayerRef + route: Point[] + stroke_width: Length + color?: string +} + +export interface PcbComponent { + type: "pcb_component" + pcb_component_id: string + source_component_id: string + center: Point + layer: LayerRef + rotation: Rotation + width: Length + height: Length +} + +export interface PcbPortNotMatchedError { + type: "pcb_port_not_matched_error" + pcb_error_id: string + message: string + pcb_component_ids: string[] +} + +export interface PcbSilkscreenText { + type: "pcb_silkscreen_text" + pcb_silkscreen_text_id: string + font: "tscircuit2024" + font_size: Length + pcb_component_id: string + text: string + layer: LayerRef + anchor_position: Point + anchor_alignment: + | "center" + | "top_left" + | "top_right" + | "bottom_left" + | "bottom_right" +} + +export interface PcbSilkscreenPill { + type: "pcb_silkscreen_pill" + pcb_silkscreen_pill_id: string + pcb_component_id: string + center: Point + width: Length + height: Length + layer: LayerRef +} + +export interface PcbPlatedHoleCircle { + type: "pcb_plated_hole" + shape: "circle" + outer_diameter: number + hole_diameter: number + x: Distance + y: Distance + layers: LayerRef[] + port_hints?: string[] + pcb_component_id?: string + pcb_port_id?: string + pcb_plated_hole_id: string +} + +export interface PcbPlatedHoleOval { + type: "pcb_plated_hole" + shape: "oval" | "pill" + outer_width: number + outer_height: number + hole_width: number + hole_height: number + x: Distance + y: Distance + layers: LayerRef[] + port_hints?: string[] + pcb_component_id?: string + pcb_port_id?: string + pcb_plated_hole_id: string +} + +export type PcbPlatedHole = PcbPlatedHoleCircle | PcbPlatedHoleOval + +export interface PcbFabricationNoteText { + type: "pcb_fabrication_note_text" + pcb_fabrication_note_text_id: string + font: "tscircuit2024" + font_size: Length + pcb_component_id: string + text: string + layer: VisibleLayer + anchor_position: Point + anchor_alignment: + | "center" + | "top_left" + | "top_right" + | "bottom_left" + | "bottom_right" + color?: string +} + +export interface PcbSilkscreenCircle { + type: "pcb_silkscreen_circle" + pcb_silkscreen_circle_id: string + pcb_component_id: string + center: Point + radius: Length + layer: VisibleLayer +} + +export interface PcbSilkscreenPath { + type: "pcb_silkscreen_path" + pcb_silkscreen_path_id: string + pcb_component_id: string + layer: VisibleLayerRef + route: Point[] + stroke_width: Length +} + +export interface PcbText { + type: "pcb_text" + pcb_text_id: string + text: string + center: Point + layer: LayerRef + width: Length + height: Length + lines: number + align: "bottom-left" +} + +export type PCBKeepout = z.infer + +export interface PcbVia { + type: "pcb_via" + pcb_via_id: string + x: Distance + y: Distance + outer_diameter: Distance + hole_diameter: Distance + layers: LayerRef[] +} + +export interface PcbSilkscreenOval { + type: "pcb_silkscreen_oval" + pcb_silkscreen_oval_id: string + pcb_component_id: string + center: Point + radius_x: Distance + radius_y: Distance + layer: VisibleLayer +} + +export interface PcbPlacementError { + type: "pcb_placement_error" + pcb_placement_error_id: string + message: string +} + +export interface PcbPort { + type: "pcb_port" + pcb_port_id: string + source_port_id: string + pcb_component_id: string + x: Distance + y: Distance + layers: LayerRef[] +} + +export interface PcbTraceHint { + type: "pcb_trace_hint" + pcb_trace_hint_id: string + pcb_port_id: string + pcb_component_id: string + route: RouteHintPoint[] +} + +export interface PcbSmtPadCircle { + type: "pcb_smtpad" + shape: "circle" + pcb_smtpad_id: string + x: Distance + y: Distance + radius: number + layer: LayerRef + port_hints?: string[] + pcb_component_id?: string + pcb_port_id?: string +} + +export interface PcbSmtPadRect { + type: "pcb_smtpad" + shape: "rect" + pcb_smtpad_id: string + x: Distance + y: Distance + width: number + height: number + layer: LayerRef + port_hints?: string[] + pcb_component_id?: string + pcb_port_id?: string +} + +export type PcbSmtPad = PcbSmtPadCircle | PcbSmtPadRect + +export interface PcbSilkscreenLine { + type: "pcb_silkscreen_line" + pcb_silkscreen_line_id: string + pcb_component_id: string + stroke_width: Distance + x1: Distance + y1: Distance + x2: Distance + y2: Distance + layer: VisibleLayer +} + +export interface PcbHoleCircleOrSquare { + type: "pcb_hole" + pcb_hole_id: string + hole_shape: "circle" | "square" + hole_diameter: number + x: Distance + y: Distance +} + +export interface PcbHoleOval { + type: "pcb_hole" + pcb_hole_id: string + hole_shape: "oval" + hole_width: number + hole_height: number + x: Distance + y: Distance +} + +export type PcbHole = PcbHoleCircleOrSquare | PcbHoleOval + +export interface PcbTraceRoutePointWire { + route_type: "wire" + x: Distance + y: Distance + width: Distance + start_pcb_port_id?: string + end_pcb_port_id?: string + layer: LayerRef +} + +export interface PcbTraceRoutePointVia { + route_type: "via" + x: Distance + y: Distance + from_layer: string + to_layer: string +} + +export type PcbTraceRoutePoint = PcbTraceRoutePointWire | PcbTraceRoutePointVia + +export interface PcbTrace { + type: "pcb_trace" + source_trace_id?: string + pcb_component_id?: string + pcb_trace_id: string + route_thickness_mode?: "constant" | "interpolated" + should_round_corners?: boolean + route: Array +} + +export interface PcbBoard { + type: "pcb_board" + pcb_board_id: string + width: Length + height: Length + center: Point + outline?: Point[] +} +``` From 27c941d2bc0cbcb0d1ffd2e1520b315e5e306b0d Mon Sep 17 00:00:00 2001 From: seveibar Date: Fri, 20 Sep 2024 12:19:57 -0700 Subject: [PATCH 2/3] add get bounds of pcb elements --- bun.lockb | Bin 135546 -> 139080 bytes index.ts | 1 + lib/get-bounds-of-pcb-elements.ts | 42 +++++++++++++++++++++++ package.json | 3 +- tests/get-bounds-of-pcb-elements.test.ts | 39 +++++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 lib/get-bounds-of-pcb-elements.ts create mode 100644 tests/get-bounds-of-pcb-elements.test.ts diff --git a/bun.lockb b/bun.lockb index c79b990b12da5f852bd8d13cfd740f9a4ea3e8fb..2a485bce81e4ea09689d854fd2f410dbae987ada 100755 GIT binary patch delta 24035 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 diff --git a/index.ts b/index.ts index a1ae596..3f3b12b 100644 --- a/index.ts +++ b/index.ts @@ -5,5 +5,6 @@ export * from "./lib/apply-selector" export * from "./lib/get-element-by-id" export * from "./lib/get-element-id" export * from "./lib/readable-name-functions/get-readable-name-for-element" +export * from "./lib/get-bounds-of-pcb-elements" export { default as su } from "./lib/su" diff --git a/lib/get-bounds-of-pcb-elements.ts b/lib/get-bounds-of-pcb-elements.ts new file mode 100644 index 0000000..c893ae0 --- /dev/null +++ b/lib/get-bounds-of-pcb-elements.ts @@ -0,0 +1,42 @@ +import type { AnyCircuitElement } from "@tscircuit/soup" + +export const getBoundsOfPcbElements = ( + elements: AnyCircuitElement[], +): { minX: number; minY: number; maxX: number; maxY: number } => { + let minX = Number.POSITIVE_INFINITY + let minY = Number.POSITIVE_INFINITY + let maxX = Number.NEGATIVE_INFINITY + let maxY = Number.NEGATIVE_INFINITY + + for (const elm of elements) { + if (!elm.type.startsWith("pcb_")) continue + if ("x" in elm && "y" in elm) { + minX = Math.min(minX, elm.x) + minY = Math.min(minY, elm.y) + maxX = Math.max(maxX, elm.x) + maxY = Math.max(maxY, elm.y) + + if ("width" in elm) { + maxX = Math.max(maxX, elm.x + elm.width) + } + if ("height" in elm) { + maxY = Math.max(maxY, elm.y + elm.height) + } + if ("radius" in elm) { + minX = Math.min(minX, elm.x - elm.radius) + minY = Math.min(minY, elm.y - elm.radius) + maxX = Math.max(maxX, elm.x + elm.radius) + maxY = Math.max(maxY, elm.y + elm.radius) + } + } else if (elm.type === "pcb_trace") { + for (const point of elm.route) { + minX = Math.min(minX, point.x) + minY = Math.min(minY, point.y) + maxX = Math.max(maxX, point.x) + maxY = Math.max(maxY, point.y) + } + } + } + + return { minX, minY, maxX, maxY } +} diff --git a/package.json b/package.json index 3c2f442..a9110bb 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,12 @@ "zod": "*" }, "devDependencies": { + "@biomejs/biome": "^1.9.2", "@tscircuit/soup": "^0.0.71", - "transformation-matrix": "^2.16.1", "ava": "^6.1.2", "esbuild": "^0.20.2", "esbuild-register": "^3.5.0", + "transformation-matrix": "^2.16.1", "tsup": "^8.0.2", "typescript": "^5.4.5", "zod": "^3.23.6" diff --git a/tests/get-bounds-of-pcb-elements.test.ts b/tests/get-bounds-of-pcb-elements.test.ts new file mode 100644 index 0000000..ee56b6f --- /dev/null +++ b/tests/get-bounds-of-pcb-elements.test.ts @@ -0,0 +1,39 @@ +import test from "ava" +import { getBoundsOfPcbElements } from "../lib/get-bounds-of-pcb-elements" + +test("getBoundsOfPcbElements", (t) => { + const elements: AnySoupElement[] = [ + { + type: "pcb_component", + pcb_component_id: "comp1", + source_component_id: "source_comp1", + center: { x: 0, y: 0 }, + width: 10, + height: 5, + rotation: 0, + layer: "top", + }, + { + type: "pcb_smtpad", + pcb_smtpad_id: "pad1", + x: 15, + y: 15, + width: 2, + height: 2, + layer: "top", + shape: "rect", + }, + { + type: "pcb_trace", + pcb_trace_id: "trace1", + route: [ + { x: -5, y: -5, width: 1, layer: "top", route_type: "wire" }, + { x: 20, y: 20, width: 1, layer: "top", route_type: "wire" }, + ], + }, + ] + + const bounds = getBoundsOfPcbElements(elements) + + t.deepEqual(bounds, { minX: -5, minY: -5, maxX: 20, maxY: 20 }) +}) From bbe159fb9cef7ea9ebca3c6a9f3d4a7f3d55a4d9 Mon Sep 17 00:00:00 2001 From: seveibar Date: Fri, 20 Sep 2024 12:55:22 -0700 Subject: [PATCH 3/3] run tests with ava on ci --- .github/workflows/npm-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml index 432a764..2ac8508 100644 --- a/.github/workflows/npm-test.yml +++ b/.github/workflows/npm-test.yml @@ -22,4 +22,4 @@ jobs: run: bun install - name: Run tests - run: bun test \ No newline at end of file + run: bun run test \ No newline at end of file