tscircuit/autorouting-dataset-01

This code manages the hardware and electronic components for circuit design and PCB layout, defining components like resistors, capacitors, transistors, connectors, and footprints, and providing tools for component placement, PCB topology, and electrical connections.

Version
1.0.102
License
unset
Stars
4

scripts/random-circuits/placeComponentsDeterministically.ts

import type { Bounds } from "lib/maths/box"
import { boundsAreaOverlap, boundsDistance } from "lib/maths/box"
import { shuffleInPlace } from "lib/maths/random/shuffleInPlace"
import { buildGridPositions } from "scripts/random-circuits/buildGridPositions"
import { getBoardBoundsWithPadding } from "scripts/random-circuits/getBoardBoundsWithPadding"
import type { ComponentSpecification } from "types/ComponentSpecification"
import type { ComponentType } from "types/ComponentType"
import type { GenerationContext } from "types/GenerationContext"

const placementOrder: ComponentType[] = [
  "resistor",
  "capacitor",
  "inductor",
  "diode",
  "transistor",
  "chip",
  "pinhead",
]

const gridGapByType: Record<ComponentType, number> = {
  resistor: 1,
  capacitor: 1,
  inductor: 2,
  diode: 1,
  transistor: 2,
  chip: 4,
  pinhead: 2,
}

/**
 * Places components on the board such that they do not overlap.
 */
export const placeComponentsDeterministically = (
  options: {
    rng: () => number
    components: ComponentSpecification[]
    boardSize: { width: number; height: number }
  },
  ctx: GenerationContext,
): ComponentSpecification[] => {
  const { rng, components, boardSize } = options
  const placed: ComponentSpecification[] = []
  const boundsByLayer: Record<"top" | "bottom", Bounds[]> = {
    top: [],
    bottom: [],
  }
  const padding = Math.max(2, ctx.configuration.maxGapBetweenParts)
  const inner = getBoardBoundsWithPadding(boardSize, padding)

  for (const type of placementOrder) {
    const gap = gridGapByType[type]
    const typeComponents = components.filter(
      (component) => component.type === type,
    )

    for (const component of typeComponents) {
      const positions = buildGridPositions(inner, gap)
      shuffleInPlace(positions, rng)

      let placedHere = false
      for (const position of positions) {
        const rotationRadians = (component.pcbRotation * Math.PI) / 180
        const rotatedWidth =
          Math.abs(Math.cos(rotationRadians)) * component.width +
          Math.abs(Math.sin(rotationRadians)) * component.height
        const rotatedHeight =
          Math.abs(Math.sin(rotationRadians)) * component.width +
          Math.abs(Math.cos(rotationRadians)) * component.height
        const candidate: Bounds = {
          minX: position.pcbX - rotatedWidth / 2,
          maxX: position.pcbX + rotatedWidth / 2,
          minY: position.pcbY - rotatedHeight / 2,
          maxY: position.pcbY + rotatedHeight / 2,
        }

        let collision = false
        const layerBounds = boundsByLayer[component.layer]
        for (const existing of layerBounds) {
          const overlapArea = boundsAreaOverlap(candidate, existing)
          const distance = boundsDistance(candidate, existing)
          if (overlapArea > 0 || distance < gap) {
            collision = true
            break
          }
        }
        if (collision) continue

        component.pcbX = position.pcbX
        component.pcbY = position.pcbY
        boundsByLayer[component.layer].push(candidate)
        placed.push(component)
        placedHere = true
        break
      }

      if (!placedHere) {
      }
    }
  }

  return placed
}