muse/book-reading-clip-lamp

A Wi‑Fi/USB‑C ESP32‑S3 reading lamp with OV5640 camera, microphone, speaker amplifier, rechargeable Li‑Po power management, six-LED boost light, USB protection/charging detection, and a clip/gooseneck enclosure with external FlexPIFA antenna.

Version
0.4.3
License
unset
Stars
0

schematic-sheet-bounds.mjs

import { getSchematicElementBounds } from '@tscircuit/circuit-json-util'

// Core and circuit-to-svg use 1.1 schematic units for a 10.16 mm symbol.
export const schematicUnitToMm = 10.16 / 1.1
export const sheetInnerMarginMm = 5
const supported = new Set(['schematic_component', 'schematic_net_label', 'schematic_trace', 'schematic_text', 'schematic_port', 'schematic_line', 'schematic_rect', 'schematic_circle'])
const boundsOf = points => ({minX: Math.min(...points.map(p => p.x)), maxX: Math.max(...points.map(p => p.x)), minY: Math.min(...points.map(p => p.y)), maxY: Math.max(...points.map(p => p.y))})

export function textBounds(text) {
  // Same conservative 2/3-em character metric as core's schematic text layout,
  // extended to respect font size, all nine anchors, newlines and rotation.
  const font = text.font_size ?? 0.18
  const lines = text.text.split('\n')
  const width = Math.max(...lines.map(line => line.length)) * font * 2 / 3
  const height = font * (1 + (lines.length - 1) * 1.2)
  const anchor = text.anchor ?? 'center'
  const left = anchor.includes('left') ? 0 : anchor.includes('right') ? -width : -width / 2
  const bottom = anchor.includes('top') ? -height : anchor.includes('bottom') ? 0 : -height / 2
  const angle = (text.rotation ?? 0) * Math.PI / 180
  return boundsOf([ [left,bottom], [left+width,bottom], [left,bottom+height], [left+width,bottom+height] ].map(([x,y]) => ({x:text.position.x+x*Math.cos(angle)-y*Math.sin(angle),y:text.position.y+x*Math.sin(angle)+y*Math.cos(angle)})))
}

export function elementBounds(element) {
  if (['schematic_component','schematic_net_label','schematic_trace'].includes(element.type)) return getSchematicElementBounds(element)
  if (element.type === 'schematic_text') return textBounds(element)
  if (element.type === 'schematic_port') return {minX:element.center.x-.1,maxX:element.center.x+.1,minY:element.center.y-.1,maxY:element.center.y+.1}
  if (element.type === 'schematic_line') return boundsOf([{x:element.x1,y:element.y1},{x:element.x2,y:element.y2}])
  if (element.type === 'schematic_circle') return {minX:element.center.x-element.radius,maxX:element.center.x+element.radius,minY:element.center.y-element.radius,maxY:element.center.y+element.radius}
  if (element.type === 'schematic_rect') {
    const angle=(element.rotation??0)*Math.PI/180
    return boundsOf([[-1,-1],[-1,1],[1,-1],[1,1]].map(([x,y])=>({x:element.center.x+x*element.width/2*Math.cos(angle)-y*element.height/2*Math.sin(angle),y:element.center.y+x*element.width/2*Math.sin(angle)+y*element.height/2*Math.cos(angle)})))
  }
  return null
}

export function checkSheetBounds(circuit) {
  const sheets = new Map(circuit.filter(e=>e.type==='schematic_sheet').map(e=>[e.schematic_sheet_id,e]))
  const issues=[]
  for (const element of circuit) {
    if (!supported.has(element.type)) continue
    const sheet=sheets.get(element.schematic_sheet_id)
    const id=element[`${element.type}_id`]
    if (!sheet) {issues.push({type:'MissingSchematicSheet',elementId:id});continue}
    const b=elementBounds(element)
    if (!b) continue
    if (!['minX','maxX','minY','maxY'].every(key=>Number.isFinite(b[key]))) throw new Error(`Non-finite schematic bounds: ${id}`)
    const center=sheet.center??{x:0,y:0}
    const halfWidth=(sheet.sheet_width/2-sheetInnerMarginMm)/schematicUnitToMm
    const halfHeight=(sheet.sheet_height/2-sheetInnerMarginMm)/schematicUnitToMm
    const page={minX:center.x-halfWidth,maxX:center.x+halfWidth,minY:center.y-halfHeight,maxY:center.y+halfHeight}
    const overflow={left:page.minX-b.minX,right:b.maxX-page.maxX,bottom:page.minY-b.minY,top:b.maxY-page.maxY}
    const sides=Object.entries(overflow).filter(([,value])=>value>1e-6)
    if (sides.length) issues.push({type:'SchematicElementOutsideSheet',sheetName:sheet.display_name??sheet.name,schematicSheetId:sheet.schematic_sheet_id,elementId:id,elementType:element.type,text:element.text,bounds:b,drawingArea:page,overflowMm:Object.fromEntries(sides.map(([side,value])=>[side,value*schematicUnitToMm]))})
  }
  return issues
}