0hmX/esp32-s3-usb-webcam-ov2640
This code defines and integrates a variety of surface-mount, through-hole, and connector hardware components (including microcontrollers, regulators, resistors, capacitors, and connectors) into a PCB design framework, enabling schematic symbol creation, footprint definition, and automated routing within an electronic circuit layout.
- Version
- 1.0.7
- License
- unset
- Stars
- 0
src/proven-route-autorouter.ts
PCB
Schematic
import type {
AutorouterCompleteEvent,
AutorouterErrorEvent,
AutorouterProgressEvent,
GenericLocalAutorouter,
SimpleRouteJson,
SimplifiedPcbTrace,
} from "tscircuit"
import provenCircuitJson from "./proven-routing.circuit.json"
type EventHandlers = {
complete: Array<(event: AutorouterCompleteEvent) => void>
error: Array<(event: AutorouterErrorEvent) => void>
progress: Array<(event: AutorouterProgressEvent) => void>
}
const provenTraces = provenCircuitJson
.filter(
(element): element is (typeof provenCircuitJson)[number] & {
connection_name: string
route: SimplifiedPcbTrace["route"]
} =>
element.type === "pcb_trace" &&
"connection_name" in element &&
typeof element.connection_name === "string" &&
"route" in element &&
Array.isArray(element.route),
)
.map(
(element) =>
({
type: "pcb_trace",
pcb_trace_id: element.pcb_trace_id,
connection_name: element.connection_name,
connectsTo: element.connectsTo,
route: element.route,
}) as unknown as SimplifiedPcbTrace,
)
if (provenTraces.length !== 128) {
throw new Error(
`Expected 128 proven autorouted traces, found ${provenTraces.length}`,
)
}
/**
* The compact board keeps every electrical pad at the validated 68 x 45 mm
* design coordinate. Replaying its short-checked route is therefore safe only
* while every routed endpoint still has the same port id and position. This
* guard makes later placement edits fail loudly instead of stretching fixed
* copper to a moved pad.
*/
function assertProvenEndpointsMatchInput(
input: SimpleRouteJson,
traces: SimplifiedPcbTrace[],
) {
const inputPoints = new Map<
string,
{ x: number; y: number; layer?: string }
>()
for (const connection of input.connections) {
for (const point of connection.pointsToConnect) {
const pointId = point.pointId ?? point.pcb_port_id
if (pointId) inputPoints.set(pointId, point)
}
}
for (const trace of traces) {
for (const portId of trace.connectsTo ?? []) {
if (!portId.startsWith("pcb_port_")) continue
const inputPoint = inputPoints.get(portId)
if (!inputPoint) {
throw new Error(
`Proven route endpoint ${portId} is absent from the current routing input`,
)
}
const routePoint = trace.route.find(
(point) =>
("start_pcb_port_id" in point &&
point.start_pcb_port_id === portId) ||
("end_pcb_port_id" in point && point.end_pcb_port_id === portId),
)
if (
!routePoint ||
!("x" in routePoint) ||
!("y" in routePoint) ||
Math.abs(routePoint.x - inputPoint.x) > 1e-6 ||
Math.abs(routePoint.y - inputPoint.y) > 1e-6
) {
throw new Error(`Proven route endpoint ${portId} moved from its baseline`)
}
}
}
}
function tracesForInput(input: SimpleRouteJson): SimplifiedPcbTrace[] {
const connectionNames = new Set<string>()
for (const connection of input.connections) {
for (const name of [
connection.name,
connection.rootConnectionName,
...(connection.mergedConnectionNames ?? []),
]) {
if (name) connectionNames.add(name)
}
}
const selected = provenTraces
.filter(
(trace) =>
trace.connection_name !== undefined &&
connectionNames.has(trace.connection_name),
)
.map((trace) => ({
...trace,
route: trace.route.map((point) => ({ ...point })),
}))
assertProvenEndpointsMatchInput(input, selected)
return selected
}
class ProvenRouteAutorouter implements GenericLocalAutorouter {
readonly input: SimpleRouteJson
isRouting = false
private readonly handlers: EventHandlers = {
complete: [],
error: [],
progress: [],
}
constructor(input: SimpleRouteJson) {
this.input = input
}
start() {
if (this.isRouting) return
this.isRouting = true
queueMicrotask(() => {
if (!this.isRouting) return
this.isRouting = false
const traces = tracesForInput(this.input)
for (const handler of this.handlers.complete) {
handler({ type: "complete", traces })
}
})
}
stop() {
this.isRouting = false
}
on(event: "complete", callback: (event: AutorouterCompleteEvent) => void): void
on(event: "error", callback: (event: AutorouterErrorEvent) => void): void
on(event: "progress", callback: (event: AutorouterProgressEvent) => void): void
on(
event: keyof EventHandlers,
callback:
| ((event: AutorouterCompleteEvent) => void)
| ((event: AutorouterErrorEvent) => void)
| ((event: AutorouterProgressEvent) => void),
) {
;(this.handlers[event] as Array<(event: never) => void>).push(
callback as (event: never) => void,
)
}
solveSync() {
return tracesForInput(this.input)
}
}
export const createProvenRouteAutorouter = async (
input: SimpleRouteJson,
): Promise<GenericLocalAutorouter> => new ProvenRouteAutorouter(input)