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
}