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

PCBPCB preview for src/proven-route-autorouter.ts
SchematicSchematic preview for src/proven-route-autorouter.ts
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)