ShiboSoftwareDev/nema8-20mm-dual-usbc-stepper-controller

A compact 20 mm four-layer USB‑C stepper-controller PCB combining CH32X035 MCU control, TMC2209 motor driving, USB‑C PD/data interfaces, protection/power regulation, temperature/LED status hardware, and NEMA8 motor connectivity.

Version
1.0.34
License
unset
Stars
0

scripts/find-route-safe-placement.mjs

import fs from "node:fs"
import {
  runAllPlacementChecks,
  checkPadPadClearance,
  checkPadTraceClearance,
  checkViaPadClearance,
} from "@tscircuit/checks"

const [targetName, xMinText, xMaxText, yMinText, yMaxText, stepText = "0.25", requestedLayer] = process.argv.slice(2)
if (!targetName) {
  throw new Error("usage: node scripts/find-route-safe-placement.mjs COMPONENT X_MIN X_MAX Y_MIN Y_MAX [STEP]")
}

const [xMin, xMax, yMin, yMax, step] = [xMinText, xMaxText, yMinText, yMaxText, stepText].map(Number)
const rawCircuit = JSON.parse(fs.readFileSync("dist/index/circuit.json", "utf8"))
  .filter((element) => !String(element.type).endsWith("error"))
const sourceById = new Map(rawCircuit
  .filter((element) => element.type === "source_component")
  .map((element) => [element.source_component_id, element]))
const componentById = new Map(rawCircuit
  .filter((element) => element.type === "pcb_component")
  .map((element) => [element.pcb_component_id, element]))
const nameForPcbId = (id) => sourceById.get(componentById.get(id)?.source_component_id)?.name
const target = [...componentById.values()].find((component) => nameForPcbId(component.pcb_component_id) === targetName)
if (!target) throw new Error(`missing ${targetName}`)

const accessoryTraceIds = new Set(rawCircuit
  .filter((element) => element.type === "pcb_trace")
  .filter((trace) => {
    const indexes = `${trace.source_trace_id ?? ""} ${trace.pcb_trace_id ?? ""}`
      .match(/source_trace_(\d+)/g)?.map((id) => Number(id.slice("source_trace_".length))) ?? []
    return indexes.some((index) => index >= 122)
  })
  .map((trace) => trace.pcb_trace_id))
const circuit = rawCircuit.filter((element) =>
  !accessoryTraceIds.has(element.pcb_trace_id))
const targetPadIds = new Set(circuit
  .filter((element) => element.pcb_component_id === target.pcb_component_id)
  .map((element) => element.pcb_smtpad_id ?? element.pcb_plated_hole_id)
  .filter(Boolean))

const moved = (x, y) => circuit.map((element) => {
  if (element.pcb_component_id !== target.pcb_component_id) return element
  const dx = x - target.center.x
  const dy = y - target.center.y
  const layerPatch = requestedLayer
    ? {
      ...(element.layer === "top" || element.layer === "bottom" ? { layer: requestedLayer } : {}),
      ...(Array.isArray(element.layers) && element.layers.length === 1 ? { layers: [requestedLayer] } : {}),
    }
    : {}
  if (element.type === "pcb_component") return { ...element, ...layerPatch, center: { x, y } }
  if (typeof element.x === "number" && typeof element.y === "number") {
    return { ...element, ...layerPatch, x: element.x + dx, y: element.y + dy }
  }
  if (element.center) {
    return { ...element, ...layerPatch, center: { x: element.center.x + dx, y: element.center.y + dy } }
  }
  if (Array.isArray(element.points)) {
    return {
      ...element, ...layerPatch,
      points: element.points.map((point) => ({ ...point, x: point.x + dx, y: point.y + dy })),
    }
  }
  if (Array.isArray(element.outline)) {
    return {
      ...element, ...layerPatch,
      outline: element.outline.map((point) => ({ ...point, x: point.x + dx, y: point.y + dy })),
    }
  }
  return element
})

const touchesTarget = (error) =>
  error.pcb_component_id === target.pcb_component_id ||
  error.pcb_component_ids?.includes(target.pcb_component_id) ||
  error.pcb_pad_ids?.some((id) => targetPadIds.has(id)) ||
  error.pcb_smtpad_ids?.some((id) => targetPadIds.has(id)) ||
  error.pcb_plated_hole_ids?.some((id) => targetPadIds.has(id)) ||
  JSON.stringify(error).includes(targetName)

const legal = []
for (let y = yMin; y <= yMax + 1e-9; y += step) {
  for (let x = xMin; x <= xMax + 1e-9; x += step) {
    const cx = Number(x.toFixed(3))
    const cy = Number(y.toFixed(3))
    const candidate = moved(cx, cy)
    const errors = [
      ...await runAllPlacementChecks(candidate),
      ...checkPadPadClearance(candidate),
      ...checkPadTraceClearance(candidate),
      ...checkViaPadClearance(candidate),
    ]
    if (!errors.some(touchesTarget)) legal.push([cx, cy])
  }
}

console.log(JSON.stringify({ targetName, layer: requestedLayer ?? target.layer, rotation: target.rotation, legal }))