code wiki / _hdl_build / nx_pcb_render.nx

nx_pcb_render.nx source

↩ module page · 112 lines · 4504 B

1// nx_pcb_render.nx -- LIB: mechanistically-ACCURATE PCB visual. Renders the routed board to an RGB raster at TRUE 2// dimensions: 1 mil = `scale` pixels, so a trace of width Wt mils draws at EXACTLY Wt*scale pixels and a grid pitch 3// of P mils at P*scale pixels. It is a scale drawing of the real board (the visual twin), not a stylized cartoon -- 4// deterministic, integer, sovereign. Driven by the ACTUAL routed geometry (usedby[] from nx_pcb_autoroute). This is 5// the EXACT engineering visual; the loosened/stylized render for games is a later derivative of it. (R2 = route this 6// through the shared nx_gfx/nx_game_raster kernel + nx_png so PCB/CAD/games/metahumans share one viz engine.) 7// never-brick #26: pure memory, bounded, deterministic, zero hardware writes. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9 10func pr_putpx(img: *u8, IMGW: i64, IMGH: i64, x: i64, y: i64, r: i64, g: i64, b: i64) -> i64 { 11 if x < 0 { return 0 } 12 if y < 0 { return 0 } 13 if x >= IMGW { return 0 } 14 if y >= IMGH { return 0 } 15 let o: i64 = (y*IMGW + x)*3 16 img[o] = r as u8 17 img[o+1] = g as u8 18 img[o+2] = b as u8 19 return 0 20} 21 22func pr_fill(img: *u8, IMGW: i64, IMGH: i64, r: i64, g: i64, b: i64) -> i64 { 23 let n: i64 = IMGW*IMGH 24 var i: i64 = 0 25 while i < n { img[i*3]=r as u8; img[i*3+1]=g as u8; img[i*3+2]=b as u8; i=i+1 } 26 return 0 27} 28 29func pr_rect(img: *u8, IMGW: i64, IMGH: i64, x0: i64, y0: i64, ww: i64, hh: i64, r: i64, g: i64, b: i64) -> i64 { 30 var yy: i64 = 0 31 while yy < hh { 32 var xx: i64 = 0 33 while xx < ww { pr_putpx(img, IMGW, IMGH, x0+xx, y0+yy, r, g, b); xx = xx + 1 } 34 yy = yy + 1 35 } 36 return 0 37} 38 39// is the pixel the copper color (210,150,60)? -- the verifier reads back the render to prove dimensional accuracy. 40func pr_is_copper(img: *u8, IMGW: i64, x: i64, y: i64) -> i64 { 41 let o: i64 = (y*IMGW + x)*3 42 if (img[o] as i64) == 210 { if (img[o+1] as i64) == 150 { if (img[o+2] as i64) == 60 { return 1 } } } 43 return 0 44} 45 46func pr_count_copper(img: *u8, IMGW: i64, IMGH: i64) -> i64 { 47 var c: i64 = 0 48 var y: i64 = 0 49 while y < IMGH { 50 var x: i64 = 0 51 while x < IMGW { if pr_is_copper(img, IMGW, x, y) == 1 { c = c + 1 } x = x + 1 } 52 y = y + 1 53 } 54 return c 55} 56 57// render the routed board: FR4 green background + copper traces of REAL width. pitch/tw are in PIXELS 58// (= mils * scale), so the drawing is dimensionally exact. Adjacent same-net cells are joined by a real-width trace. 59func pr_render_board(img: *u8, IMGW: i64, IMGH: i64, usedby: *i64, W: i64, H: i64, pitch: i64, tw: i64) -> i64 { 60 pr_fill(img, IMGW, IMGH, 12, 92, 40) 61 let half: i64 = tw/2 62 var cy: i64 = 0 63 while cy < H { 64 var cx: i64 = 0 65 while cx < W { 66 let c: i64 = cy*W + cx 67 let net: i64 = usedby[c] 68 if net >= 0 { 69 let xc: i64 = cx*pitch + pitch/2 70 let yc: i64 = cy*pitch + pitch/2 71 pr_rect(img, IMGW, IMGH, xc-half, yc-half, tw, tw, 210, 150, 60) 72 if cx+1 < W { let c2: i64 = cy*W + (cx+1); if usedby[c2] == net { pr_rect(img, IMGW, IMGH, xc, yc-half, pitch, tw, 210, 150, 60) } } 73 if cy+1 < H { let c2: i64 = (cy+1)*W + cx; if usedby[c2] == net { pr_rect(img, IMGW, IMGH, xc-half, yc, tw, pitch, 210, 150, 60) } } 74 } 75 cx = cx + 1 76 } 77 cy = cy + 1 78 } 79 return 0 80} 81 82func pr_putdec(buf: *u8, pos: i64, val: i64) -> i64 { 83 if val == 0 { buf[pos] = 48 as u8; return 1 } 84 let t: *u8 = sys_mmap(24) 85 var m: i64 = val 86 var k: i64 = 0 87 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 88 var i: i64 = 0 89 while i < k { buf[pos + i] = t[k - 1 - i]; i = i + 1 } 90 return k 91} 92 93// write a binary PPM (P6) so the operator can eyeball the real-scale board. 94func pr_write_ppm(path: *u8, img: *u8, IMGW: i64, IMGH: i64) -> i64 { 95 let fd: i64 = sys_openat_wr(path, 420) 96 if fd < 0 { return 0 - 1 } 97 let hdr: *u8 = sys_mmap(64) 98 var p: i64 = 0 99 hdr[p] = 80 as u8; p = p + 1 100 hdr[p] = 54 as u8; p = p + 1 101 hdr[p] = 10 as u8; p = p + 1 102 p = p + pr_putdec(hdr, p, IMGW) 103 hdr[p] = 32 as u8; p = p + 1 104 p = p + pr_putdec(hdr, p, IMGH) 105 hdr[p] = 10 as u8; p = p + 1 106 hdr[p] = 50 as u8; p = p + 1; hdr[p] = 53 as u8; p = p + 1; hdr[p] = 53 as u8; p = p + 1 107 hdr[p] = 10 as u8; p = p + 1 108 sys_write(fd, hdr, p) 109 sys_write(fd, img, IMGW*IMGH*3) 110 sys_close(fd) 111 return 0 112}