imrishabh18/nema-23-stepper-controller

A 57 mm four-layer RP2040 NEMA23 stepper-controller PCB with USB‑C/USB‑PD power input, protected motor rail, DRV8452 dual-winding driver, bidirectional current sensing, temperature/enable interlock, optional magnetic encoder, RGB LED and buzzer status indicators, and motor/test connectors.

Version
1.2.3
License
unset
Stars
0

schematic-section-notes.ts

import { Group, SchematicSection, SchematicSheet } from "tscircuit"
// Conservative bounds for the native net-label stems and text. Keeping this
// small helper local also makes the design work in the browser evaluator.
function labelBounds(label: any) {
  const p = label.anchor_position ?? label.center
  const length = (label.text.length + 1) * 0.12
  const box = { minX: p.x - 0.1, maxX: p.x + 0.1, minY: p.y - 0.1, maxY: p.y + 0.1 }
  if (label.anchor_side === "left") box.maxX = p.x + length
  else if (label.anchor_side === "right") box.minX = p.x - length
  else if (label.anchor_side === "top") box.minY = p.y - length
  else box.maxY = p.y + length
  return box
}

// Core 0.0.2050 bases section titles on component bodies, which can put them
// over rail labels. Include the rendered labels, then wrap the explanation to
// the available section width. Only annotation text moves here; component and
// wire placement remains native tscircuit autolayout.
const prototype = SchematicSection.prototype as any
const marker = Symbol.for("nema23.section-note-layout")
if (!prototype[marker]) {
  const bounds = prototype._computeSectionBounds
  prototype._computeSectionBounds = function (board: any, name: string, sheet: string) {
    const result = bounds.call(this, board, name, sheet)
    if (!result) return result
    const db = this.root.db
    const members = new Set(board.getDescendants()
      .filter((c: any) => c.getSchematicSectionName() === name)
      .map((c: any) => c.schematic_component_id).filter(Boolean))
    const components = db.schematic_component.list()
      .filter((c: any) => c.schematic_sheet_id === sheet)
    const distance = (label: any, component: any) => {
      const dx = Math.max(0, Math.abs(label.center.x - component.center.x) - component.size.width / 2)
      const dy = Math.max(0, Math.abs(label.center.y - component.center.y) - component.size.height / 2)
      return dx * dx + dy * dy
    }
    const labels = db.schematic_net_label.list().filter((label: any) => {
      if (label.schematic_sheet_id !== sheet || !label.center) return false
      const closest = components.reduce((a: any, b: any) => !a || distance(label, b) < distance(label, a) ? b : a, null)
      return closest && members.has(closest.schematic_component_id)
    })
    for (const label of labels) {
      const box = labelBounds(label)
      if (!box) continue
      result.minX = Math.min(result.minX, box.minX)
      result.maxX = Math.max(result.maxX, box.maxX)
      result.minY = Math.min(result.minY, box.minY)
      result.maxY = Math.max(result.maxY, box.maxY)
    }
    for (const text of db.schematic_text.list()) {
      if (text.schematic_sheet_id !== sheet || !text.source_trace_id) continue
      const location = { center: text.position }
      const closest = components.reduce((a: any, b: any) => !a || distance(location, b) < distance(location, a) ? b : a, null)
      if (!closest || !members.has(closest.schematic_component_id)) continue
      const width = text.text.length * text.font_size * 0.7
      const height = text.font_size * 1.2
      const left = text.anchor.includes("left") ? 0 : text.anchor.includes("right") ? -width : -width / 2
      const angle = -(text.rotation ?? 0) * Math.PI / 180
      for (const x of [left, left + width]) for (const y of [-height / 2, height / 2]) {
        const px = text.position.x + x * Math.cos(angle) - y * Math.sin(angle)
        const py = text.position.y + x * Math.sin(angle) + y * Math.cos(angle)
        result.minX = Math.min(result.minX, px)
        result.maxX = Math.max(result.maxX, px)
        result.minY = Math.min(result.minY, py)
        result.maxY = Math.max(result.maxY, py)
      }
    }
    // Space for native chip references and power-label artwork.
    result.maxY += 0.45
    return result
  }
  const sheetPrototype = SchematicSheet.prototype as any
  const render = sheetPrototype.doInitialSchematicSheetRender
  const arrangeNotes = function (this: any) {
    if (this.root?.schematicDisabled) return
    const db = this.root.db
    const board = this.root._getBoard()
    const sections = board.getDescendants().filter((c: any) => c instanceof SchematicSection)
    const added = db.schematic_text.list().filter((t: any) => t.schematic_sheet_id === (this.schematic_sheet_id ?? this._resolveSchematicSheetId()))
    for (const text of added) {
      const section = sections.find((s: any) => s._parsedProps.displayName === text.text.replace(/\s+/g, " ") && s._resolveSchematicSheetId() === text.schematic_sheet_id)
      if (!section) continue
      const box = section._computeSectionBounds(board, section._parsedProps.name, text.schematic_sheet_id)
      if (!box) continue
      const font = 0.22
      const columns = Math.max(16, Math.floor((box.maxX - box.minX) / (font * 0.62)))
      const lines: string[] = []
      for (const word of section._parsedProps.displayName.split(/\s+/)) {
        if (!lines.length || lines[lines.length - 1].length + word.length + 1 > columns) lines.push(word)
        else lines[lines.length - 1] += " " + word
      }
      db.schematic_text.update(text.schematic_text_id, {
        text: lines.join("\n"), font_size: font,
        position: { x: box.minX, y: box.maxY + 0.3 + lines.length * font },
      })
    }
  }
  const sectionRender = prototype.doInitialSchematicSectionRender
  prototype.doInitialSchematicSectionRender = function () {
    sectionRender.call(this)
    arrangeNotes.call(this)
  }
  sheetPrototype.doInitialSchematicSheetRender = function () {
    arrangeNotes.call(this)
    render.call(this)
  }
  prototype[marker] = true
}

// Core 0.0.2050 otherwise replaces an explicit native grid with match-pack
// whenever its children have section names. Respect the chosen automatic grid
// while retaining section membership for captions and wire routing.
const groupPrototype = Group.prototype as any
const gridMarker = Symbol.for("nema23.section-grid-layout")
if (!groupPrototype[gridMarker]) {
  const sections = groupPrototype._doInitialSchematicLayoutSections
  groupPrototype._doInitialSchematicLayoutSections = function () {
    if (this._parsedProps.schGrid) this._doInitialSchematicLayoutGrid()
    else sections.call(this)
  }
  groupPrototype[gridMarker] = true
}