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

A compact PCB component set comprising a pushbutton, logic buffer, Schottky diodes, USB connector, resettable fuse, microcontroller, voltage regulator, and polarized capacitors.

Version
1.0.35
License
unset
Stars
0

scripts/find-free-via-sites.mjs

import fs from "node:fs"
const option = (name, fallback) => Number(
  process.argv.find((arg) => arg.startsWith(`--${name}=`))?.split("=")[1] ?? fallback,
)
const step = option("step", 0.2)
const excludeTraceFrom = option("exclude-traces-from", 122)
const ignoredSourceIndex = option("ignore-source", -1)
const circuit = JSON.parse(fs.readFileSync("dist/index/circuit.json", "utf8"))
  .filter((element) => !String(element.type).endsWith("error"))
const sourceIndex = (element) => {
  const ids = `${element.source_trace_id ?? ""} ${element.pcb_trace_id ?? ""}`
    .match(/source_trace_(\d+)/g) ?? []
  return ids.map((id) => Number(id.slice("source_trace_".length)))
}
const accessoryTraceIds = new Set(circuit
  .filter((element) => element.type === "pcb_trace" &&
    sourceIndex(element).some((index) => index >= excludeTraceFrom))
  .map((element) => element.pcb_trace_id))
const ignoredTraceIds = new Set(circuit
  .filter((element) => element.type === "pcb_trace" &&
    sourceIndex(element).includes(ignoredSourceIndex))
  .map((element) => element.pcb_trace_id))
const base = circuit.filter((element) =>
  !accessoryTraceIds.has(element.pcb_trace_id) &&
  !ignoredTraceIds.has(element.pcb_trace_id) &&
  !(element.type === "pcb_via" && (accessoryTraceIds.has(element.pcb_trace_id) ||
    ignoredTraceIds.has(element.pcb_trace_id))))

const pointSegmentDistance = (px, py, ax, ay, bx, by) => {
  const dx = bx - ax
  const dy = by - ay
  const lengthSquared = dx * dx + dy * dy
  const t = lengthSquared === 0 ? 0 : Math.max(0, Math.min(1,
    ((px - ax) * dx + (py - ay) * dy) / lengthSquared))
  return Math.hypot(px - (ax + t * dx), py - (ay + t * dy))
}
const pointRectDistance = (x, y, element) => {
  const angle = -(((element.ccw_rotation ?? element.rotation ?? 0) * Math.PI) / 180)
  const dx = x - element.x
  const dy = y - element.y
  const localX = dx * Math.cos(angle) - dy * Math.sin(angle)
  const localY = dx * Math.sin(angle) + dy * Math.cos(angle)
  const outsideX = Math.max(Math.abs(localX) -
    (element.width ?? element.outer_width ?? element.outer_diameter ?? 0) / 2, 0)
  const outsideY = Math.max(Math.abs(localY) -
    (element.height ?? element.outer_height ?? element.outer_diameter ?? 0) / 2, 0)
  return Math.hypot(outsideX, outsideY)
}
const fixedVias = base.filter((element) => element.type === "pcb_via")
const pads = base.filter((element) => ["pcb_smtpad", "pcb_plated_hole"].includes(element.type))
const holes = base.filter((element) => element.type === "pcb_hole")
const traceSegments = base.filter((element) => element.type === "pcb_trace").flatMap((trace) => {
  const segments = []
  let previous
  for (const point of trace.route ?? []) {
    if (point.route_type === "via") {
      previous = undefined
      continue
    }
    if (point.route_type !== "wire") continue
    if (previous?.layer === point.layer) segments.push({
      ax: previous.x, ay: previous.y, bx: point.x, by: point.y,
      width: Math.max(previous.width ?? 0.15, point.width ?? 0.15),
      pcb_trace_id: trace.pcb_trace_id,
    })
    previous = point
  }
  return segments
})
const viaRadius = 0.225
const blockers = (x, y) => {
  const found = []
  if (Math.abs(x) > 9.575 || Math.abs(y) > 9.575) found.push("board-edge")
  for (const via of fixedVias) if (Math.hypot(x - via.x, y - via.y) <
    viaRadius + (via.outer_diameter ?? 0.45) / 2 + 0.1) found.push(`via:${via.pcb_via_id}`)
  for (const pad of pads) if (pointRectDistance(x, y, pad) < viaRadius + 0.1)
    found.push(`pad:${pad.pcb_smtpad_id ?? pad.pcb_plated_hole_id}`)
  for (const hole of holes) if (Math.hypot(x - hole.x, y - hole.y) <
    viaRadius + (hole.diameter ?? hole.hole_diameter ?? 0) / 2 + 0.2) found.push(`hole:${hole.pcb_hole_id}`)
  for (const segment of traceSegments) if (pointSegmentDistance(x, y,
    segment.ax, segment.ay, segment.bx, segment.by) < viaRadius + segment.width / 2 + 0.1)
    found.push(`trace:${segment.pcb_trace_id ?? "unknown"}`)
  return found
}

const legal = []
const scored = []
for (let y = option("ymin", -9); y <= option("ymax", 9) + 1e-9; y += step) {
  for (let x = option("xmin", -9); x <= option("xmax", 9) + 1e-9; x += step) {
    const px = Number(x.toFixed(3))
    const py = Number(y.toFixed(3))
    const pointBlockers = blockers(px, py)
    if (pointBlockers.length === 0) legal.push([px, py])
    scored.push({ x: px, y: py, blockers: pointBlockers })
  }
}
const bestCount = option("best", 0)
console.log(JSON.stringify({ count: legal.length, legal,
  best: bestCount > 0 ? scored.sort((a, b) => a.blockers.length - b.blockers.length).slice(0, bestCount) : undefined }))