diff --git a/README.md b/README.md index 57ee539d..b8d4e60a 100644 --- a/README.md +++ b/README.md @@ -342,6 +342,11 @@ export interface AutoroutingPhaseProps extends RoutingTolerances { connection?: string; connections?: string[]; reroute?: boolean; + /** + * Fanout direction for each named bus in this phase. `center` leaves the + * direction unconstrained. + */ + busFanoutDirections?: Record; } ``` diff --git a/generated/COMPONENT_TYPES.md b/generated/COMPONENT_TYPES.md index 0b9f15d3..80e94180 100644 --- a/generated/COMPONENT_TYPES.md +++ b/generated/COMPONENT_TYPES.md @@ -1127,6 +1127,11 @@ export const analogTransientSimulationProps = z ### autoroutingphase ```typescript +export type BusFanoutDirection = + | NinePointAnchor + | { + direction: NinePointAnchor + } export interface AutoroutingPhaseProps extends RoutingTolerances { key?: any name?: string @@ -1142,7 +1147,12 @@ export interface AutoroutingPhaseProps extends RoutingTolerances { connection?: string connections?: string[] reroute?: boolean + busFanoutDirections?: Record } +/** + * Fanout direction for each named bus in this phase. `center` leaves the + * direction unconstrained. + */ export const autoroutingPhaseProps = z .object({ key: z.any().optional(), @@ -1162,6 +1172,7 @@ export const autoroutingPhaseProps = z connection: z.string().optional(), connections: z.array(z.string()).optional(), reroute: z.boolean().optional(), + busFanoutDirections: z.record(busFanoutDirection).optional(), }) ``` @@ -2347,6 +2358,8 @@ export interface AutorouterConfig { | "krt" | "freerouting" | "laser_prefab" // Prefabricated PCB with laser copper ablation + | "single_layer_fanout" + | "fanout" | /** @deprecated Use "auto_jumper" */ "auto-jumper" | /** @deprecated Use "sequential_trace" */ "sequential-trace" | /** @deprecated Use "auto_local" */ "auto-local" @@ -2392,6 +2405,8 @@ export const autorouterConfig = z.object({ "krt", "freerouting", "laser_prefab", + "single_layer_fanout", + "fanout", "auto-jumper", "sequential-trace", "auto-local", diff --git a/generated/PROPS_OVERVIEW.md b/generated/PROPS_OVERVIEW.md index a1a77650..2d98edd9 100644 --- a/generated/PROPS_OVERVIEW.md +++ b/generated/PROPS_OVERVIEW.md @@ -166,6 +166,8 @@ export interface AutorouterConfig { | "krt" | "freerouting" | "laser_prefab" // Prefabricated PCB with laser copper ablation + | "single_layer_fanout" + | "fanout" | /** @deprecated Use "auto_jumper" */ "auto-jumper" | /** @deprecated Use "sequential_trace" */ "sequential-trace" | /** @deprecated Use "auto_local" */ "auto-local" @@ -202,6 +204,11 @@ export interface AutoroutingPhaseProps extends RoutingTolerances { connection?: string connections?: string[] reroute?: boolean + /** + * Fanout direction for each named bus in this phase. `center` leaves the + * direction unconstrained. + */ + busFanoutDirections?: Record } diff --git a/lib/common/ninePointAnchor.ts b/lib/common/ninePointAnchor.ts index ff7660fd..35df06a8 100644 --- a/lib/common/ninePointAnchor.ts +++ b/lib/common/ninePointAnchor.ts @@ -11,3 +11,5 @@ export const ninePointAnchor = z.enum([ "bottom_center", "bottom_right", ]) + +export type NinePointAnchor = z.infer diff --git a/lib/components/autoroutingphase.ts b/lib/components/autoroutingphase.ts index e662aa17..968b6743 100644 --- a/lib/components/autoroutingphase.ts +++ b/lib/components/autoroutingphase.ts @@ -1,5 +1,10 @@ import { expectTypesMatch } from "lib/typecheck" import { z } from "zod" +import { + type NinePointAnchor, + ninePointAnchor, +} from "../common/ninePointAnchor" +import type { BusName } from "./bus" import { type AutorouterProp, type RoutingTolerances, @@ -7,6 +12,12 @@ import { routingTolerances, } from "./group" +export type BusFanoutDirection = + | NinePointAnchor + | { + direction: NinePointAnchor + } + export interface AutoroutingPhaseProps extends RoutingTolerances { key?: any name?: string @@ -22,8 +33,18 @@ export interface AutoroutingPhaseProps extends RoutingTolerances { connection?: string connections?: string[] reroute?: boolean + /** + * Fanout direction for each named bus in this phase. `center` leaves the + * direction unconstrained. + */ + busFanoutDirections?: Record } +const busFanoutDirection = z.union([ + ninePointAnchor, + z.object({ direction: ninePointAnchor }), +]) + export const autoroutingPhaseProps = z .object({ key: z.any().optional(), @@ -43,6 +64,7 @@ export const autoroutingPhaseProps = z connection: z.string().optional(), connections: z.array(z.string()).optional(), reroute: z.boolean().optional(), + busFanoutDirections: z.record(busFanoutDirection).optional(), }) .superRefine((value, ctx) => { if ( diff --git a/lib/components/bus.ts b/lib/components/bus.ts index 0eed3577..242be75e 100644 --- a/lib/components/bus.ts +++ b/lib/components/bus.ts @@ -1,6 +1,8 @@ import { expectTypesMatch } from "lib/typecheck" import { z } from "zod" +export type BusName = string + /** * Declares a group of connections that an autorouter should keep together. * Each connection may be a trace name or a port selector. diff --git a/lib/components/group.ts b/lib/components/group.ts index e08d4b17..744e592e 100644 --- a/lib/components/group.ts +++ b/lib/components/group.ts @@ -353,6 +353,8 @@ export interface AutorouterConfig { | "krt" | "freerouting" | "laser_prefab" // Prefabricated PCB with laser copper ablation + | "single_layer_fanout" + | "fanout" | /** @deprecated Use "auto_jumper" */ "auto-jumper" | /** @deprecated Use "sequential_trace" */ "sequential-trace" | /** @deprecated Use "auto_local" */ "auto-local" @@ -371,6 +373,8 @@ export type AutorouterPreset = | "krt" | "freerouting" | "laser_prefab" + | "single_layer_fanout" + | "fanout" | "auto-jumper" | "sequential-trace" | "auto-local" @@ -425,6 +429,8 @@ export const autorouterConfig = z.object({ "krt", "freerouting", "laser_prefab", + "single_layer_fanout", + "fanout", "auto-jumper", "sequential-trace", "auto-local", @@ -446,6 +452,8 @@ export const autorouterPreset = z.union([ z.literal("krt"), z.literal("freerouting"), z.literal("laser_prefab"), // Prefabricated PCB with laser copper ablation + z.literal("single_layer_fanout"), + z.literal("fanout"), z.literal("auto-jumper"), z.literal("sequential-trace"), z.literal("auto-local"), diff --git a/package.json b/package.json index 9ae11697..a8db698e 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@types/node": "^20.12.11", "@types/react": "^18.3.2", "ava": "^6.1.3", - "circuit-json": "^0.0.450", + "circuit-json": "^0.0.455", "expect-type": "^1.3.0", "glob": "^11.0.0", "madge": "^8.0.0", diff --git a/tests/autorouter.test.ts b/tests/autorouter.test.ts index a8792994..ba2dfa6e 100644 --- a/tests/autorouter.test.ts +++ b/tests/autorouter.test.ts @@ -44,6 +44,16 @@ test("supports krt preset", () => { expect(result).toBe("krt") }) +test("supports fanout presets", () => { + expect(autorouterProp.parse("single_layer_fanout")).toBe( + "single_layer_fanout", + ) + expect(autorouterProp.parse("fanout")).toBe("fanout") + expect(autorouterProp.parse({ preset: "fanout" })).toMatchObject({ + preset: "fanout", + }) +}) + test("still supports deprecated kebab-case presets", () => { const result = autorouterProp.parse("auto-cloud") expect(result).toBe("auto-cloud") diff --git a/tests/autoroutingphase.test.ts b/tests/autoroutingphase.test.ts index 0761a917..afa5b980 100644 --- a/tests/autoroutingphase.test.ts +++ b/tests/autoroutingphase.test.ts @@ -30,6 +30,18 @@ test("autorouting phase accepts autorouter and phase index", () => { expect(parsed.phaseIndex).toBe(1) }) +test("autorouting phase accepts per-bus fanout directions", () => { + const raw = { + autorouter: "fanout", + busFanoutDirections: { + DATA: "top_right", + CONTROL: { direction: "center_left" }, + }, + } satisfies AutoroutingPhaseProps + + expect(autoroutingPhaseProps.parse(raw)).toEqual(raw) +}) + test("autorouting phase accepts a single connection", () => { const raw: AutoroutingPhaseProps = { phaseIndex: 2,