abse/smartwatch-flex-top-only
Three-panel flex smartwatch with two parallel R3.9 bends, all components flat-top, low-profile JST connector harnesses, split heartbeat optical windows and screw-retained PCB mounting. Modeled 39 × 50.5 × 14.5mm prototype; 100% routing, zero native DRC and shorts. Hardware qualification pending.
- Version
- 0.2.20
- License
- unset
- Stars
- 0
scripts/fix-oled-clock-style.mjs
import fs from 'node:fs'
import {checkPcbTraceStyle} from '@tscircuit/checks'
import {analyzePcbStyle} from '../tools/pcb-style/analyzer.mjs'
import {copperModel,SpatialCopper,traceShapes} from './copper-model.mjs'
import {ptSeg} from './copper-geometry.mjs'
import {candidates,boundarySafe,anchors,len} from './style-candidates.mjs'
// Search only real 45-degree simplifications, with unchanged trace ends, width,
// layer, junction anchors, design rules and every manufacturing via.
const cache=JSON.parse(fs.readFileSync('routing/final/cache.json'))
const circuit=JSON.parse(fs.readFileSync('dist/index/circuit.json'))
const id='source_net_57_grid11170_material_0'
const styleCircuit=circuit.filter(e=>e.type!=='pcb_trace'||e.pcb_trace_id===id)
const t=cache.traces.find(t=>t.pcb_trace_id===id),original=structuredClone(t)
const fixed=anchors(t).map(i=>t.route[i])
const spatial=new SpatialCopper(copperModel(cache,circuit))
const warnings=route=>checkPcbTraceStyle(styleCircuit.map(e=>e.type==='pcb_trace'&&e.pcb_trace_id===id?{...e,route}:e)).filter(e=>e.pcb_trace_id===id)
const score=route=>[warnings(route).length,analyzePcbStyle([{...t,route}]).issues.length,route.length,len(route)]
let best=score(t.route),tested=0,valid=0;const changes=[]
for(let pass=0;pass<4;pass++){
let chosen
search: for(let lo=14;lo<Math.min(t.route.length-2,65);lo++)for(let hi=Math.min(t.route.length-2,70);hi>=lo+2;hi--){
const old=t.route.slice(lo,hi+1)
if(old.some(p=>p.route_type!=='wire'||p.layer!==old[0].layer||p.width!==old[0].width))continue
for(const path of candidates(old)){
if(path.length>=old.length||path.some((p,i)=>i&&Math.abs(Math.sin(Math.atan2(p.y-path[i-1].y,p.x-path[i-1].x)*4))>1e-6))continue
tested++
const shapes=traceShapes({...t,route:path})
if(shapes.some(s=>!boundarySafe(s)||spatial.violations(s).length))continue
const route=[...t.route.slice(0,lo),...path,...t.route.slice(hi+1)]
const all=traceShapes({...t,route})
if(fixed.some(p=>!all.some(s=>s.kind==='segment'&&s.layers.includes(p.layer)&&ptSeg(p,s.a,s.b)<1e-7)))continue
let drift=0
for(const s of shapes)for(let j=0;j<=10;j++){
const p={x:s.a.x+(s.b.x-s.a.x)*j/10,y:s.a.y+(s.b.y-s.a.y)*j/10}
drift=Math.max(drift,Math.min(...old.slice(1).map((q,i)=>ptSeg(p,old[i],q))))
}
if(drift>.25)continue
valid++
const value=score(route)
const better=(a,b)=>a.some((x,i)=>x<b[i]-1e-8&&a.slice(0,i).every((y,j)=>Math.abs(y-b[j])<1e-8))
if(better(value,chosen?.value??best))chosen={route,value,lo,hi,drift,oldPoints:old.length,newPoints:path.length}
if(chosen?.value[0]===0)break search
}
}
if(!chosen)break
t.route=chosen.route;best=chosen.value;changes.push({...chosen,route:undefined});console.log({pass,best,tested,valid,change:changes.at(-1)})
if(best[0]===0)break
}
fs.mkdirSync('reports/oled-clock-style',{recursive:true})
const report={tested,valid,before:score(original.route),after:score(t.route),changes,nativeWarnings:warnings(t.route),changedTrace:id}
fs.writeFileSync('reports/oled-clock-style/search.json',JSON.stringify(report,null,2)+'\n')
if(changes.length){fs.writeFileSync('routing/final/cache.json',JSON.stringify(cache,null,2)+'\n')}
console.log(JSON.stringify(report,null,2))