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}