nx_raster2d_lib.nx source
↩ module page · 194 lines · 7266 B
1// nx_raster2d_lib.nx -- shared 2D raster primitives on an i64 framebuffer (one i64/pixel, packed
2// R + G*256 + B*65536, the nx_png.write_png contract). Extracted VERBATIM from the gate-proven
3// nx_eco_graph_png (2026-07-16, byte-identical output gate) so the lineage renderer and the
4// whole-forest renderer share ONE implementation. Composes nx_font_bitmap_5x7 for text.
5// license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_eco_graph.nx"
8import "nx_font_bitmap_5x7.nx"
9const K_MAGIC_65536: i64 = 65536
10const K_MAGIC_6000: i64 = 6000
11
12func r2_px(fb: *i64, W: i64, H: i64, x: i64, y: i64, r: i64, gg: i64, b: i64) -> i64 {
13 if x < 0 { return 0 }
14 if y < 0 { return 0 }
15 if x >= W { return 0 }
16 if y >= H { return 0 }
17 fb[y*W + x] = r + gg*256 + b*K_MAGIC_65536
18 return 0
19}
20func r2_fill_rect(fb: *i64, W: i64, H: i64, x0: i64, y0: i64, w: i64, h: i64, r: i64, gg: i64, b: i64) -> i64 {
21 var yy: i64 = y0
22 while yy < y0 + h { var xx: i64 = x0; while xx < x0 + w { r2_px(fb,W,H,xx,yy,r,gg,b); xx = xx + 1 } yy = yy + 1 }
23 return 0
24}
25func r2_fill_disk(fb: *i64, W: i64, H: i64, cx: i64, cy: i64, rad: i64, r: i64, gg: i64, b: i64) -> i64 {
26 var dy: i64 = 0 - rad
27 while dy <= rad {
28 var dx: i64 = 0 - rad
29 while dx <= rad {
30 if dx*dx + dy*dy <= rad*rad { r2_px(fb,W,H,cx+dx,cy+dy,r,gg,b) }
31 dx = dx + 1
32 }
33 dy = dy + 1
34 }
35 return 0
36}
37func r2_ring(fb: *i64, W: i64, H: i64, cx: i64, cy: i64, rad: i64, r: i64, gg: i64, b: i64) -> i64 {
38 let r2hi: i64 = rad*rad
39 let r2lo: i64 = (rad-2)*(rad-2)
40 var dy: i64 = 0 - rad
41 while dy <= rad {
42 var dx: i64 = 0 - rad
43 while dx <= rad {
44 let d: i64 = dx*dx + dy*dy
45 if d <= r2hi { if d >= r2lo { r2_px(fb,W,H,cx+dx,cy+dy,r,gg,b) } }
46 dx = dx + 1
47 }
48 dy = dy + 1
49 }
50 return 0
51}
52func r2_line(fb: *i64, W: i64, H: i64, x0: i64, y0: i64, x1: i64, y1: i64, r: i64, gg: i64, b: i64) -> i64 {
53 var x: i64 = x0; var y: i64 = y0
54 var dx: i64 = x1 - x0; if dx < 0 { dx = 0 - dx }
55 var dy: i64 = y1 - y0; if dy < 0 { dy = 0 - dy }
56 var sx: i64 = 1; if x1 < x0 { sx = 0 - 1 }
57 var sy: i64 = 1; if y1 < y0 { sy = 0 - 1 }
58 var err: i64 = dx - dy
59 var guard: i64 = 0
60 while guard < K_MAGIC_6000 {
61 r2_px(fb,W,H,x,y,r,gg,b)
62 if x == x1 { if y == y1 { guard = K_MAGIC_6000 } }
63 if guard < K_MAGIC_6000 {
64 let e2: i64 = err + err
65 if e2 > (0 - dy) { err = err - dy; x = x + sx }
66 if e2 < dx { err = err + dx; y = y + sy }
67 guard = guard + 1
68 }
69 }
70 return 0
71}
72// ---- ADDITIVE variants: accumulate brightness (clamped) instead of overwriting. The hairball cure
73// for dense graphs: one edge is faint, a bundle glows -- an edge-density map, no alpha channel needed. ----
74func r2_px_add(fb: *i64, W: i64, H: i64, x: i64, y: i64, dr: i64, dg: i64, db: i64) -> i64 {
75 if x < 0 { return 0 }
76 if y < 0 { return 0 }
77 if x >= W { return 0 }
78 if y >= H { return 0 }
79 let c: i64 = fb[y*W + x]
80 var r: i64 = c % 256 + dr
81 var g: i64 = (c / 256) % 256 + dg
82 var b: i64 = (c / K_MAGIC_65536) % 256 + db
83 if r > 255 { r = 255 }
84 if g > 255 { g = 255 }
85 if b > 255 { b = 255 }
86 fb[y*W + x] = r + g*256 + b*K_MAGIC_65536
87 return 0
88}
89func r2_line_add(fb: *i64, W: i64, H: i64, x0: i64, y0: i64, x1: i64, y1: i64, dr: i64, dg: i64, db: i64) -> i64 {
90 var x: i64 = x0; var y: i64 = y0
91 var dx: i64 = x1 - x0; if dx < 0 { dx = 0 - dx }
92 var dy: i64 = y1 - y0; if dy < 0 { dy = 0 - dy }
93 var sx: i64 = 1; if x1 < x0 { sx = 0 - 1 }
94 var sy: i64 = 1; if y1 < y0 { sy = 0 - 1 }
95 var err: i64 = dx - dy
96 var guard: i64 = 0
97 while guard < K_MAGIC_6000 {
98 r2_px_add(fb,W,H,x,y,dr,dg,db)
99 if x == x1 { if y == y1 { guard = K_MAGIC_6000 } }
100 if guard < K_MAGIC_6000 {
101 let e2: i64 = err + err
102 if e2 > (0 - dy) { err = err - dy; x = x + sx }
103 if e2 < dx { err = err + dx; y = y + sy }
104 guard = guard + 1
105 }
106 }
107 return 0
108}
109func r2_ribbon_add(fb: *i64, W: i64, H: i64, x1: i64, y1: i64, x2: i64, y2: i64, dr: i64, dg: i64, db: i64) -> i64 {
110 let my: i64 = (y1 + y2) / 2
111 var px: i64 = x1; var py: i64 = y1
112 var i: i64 = 1
113 while i <= 18 {
114 let t: i64 = i * 1000 / 18
115 let u: i64 = 1000 - t
116 let uu: i64 = u*u/1000
117 let tt: i64 = t*t/1000
118 let uuu: i64 = uu*u/1000
119 let ttt: i64 = tt*t/1000
120 let c0: i64 = uuu
121 let c1: i64 = 3*uu*t/1000
122 let c2: i64 = 3*u*tt/1000
123 let c3: i64 = ttt
124 let bx: i64 = (c0*x1 + c1*x1 + c2*x2 + c3*x2) / 1000
125 let by: i64 = (c0*y1 + c1*my + c2*my + c3*y2) / 1000
126 r2_line_add(fb,W,H,px,py,bx,by,dr,dg,db)
127 px = bx; py = by
128 i = i + 1
129 }
130 return 0
131}
132
133// cubic bezier ribbon P0(x1,y1) C(x1,my)(x2,my) P3(x2,y2) sampled into 18 segments
134func r2_ribbon(fb: *i64, W: i64, H: i64, x1: i64, y1: i64, x2: i64, y2: i64, r: i64, gg: i64, b: i64) -> i64 {
135 let my: i64 = (y1 + y2) / 2
136 var px: i64 = x1; var py: i64 = y1
137 var i: i64 = 1
138 while i <= 18 {
139 let t: i64 = i * 1000 / 18
140 let u: i64 = 1000 - t
141 let uu: i64 = u*u/1000
142 let tt: i64 = t*t/1000
143 let uuu: i64 = uu*u/1000
144 let ttt: i64 = tt*t/1000
145 let c0: i64 = uuu
146 let c1: i64 = 3*uu*t/1000
147 let c2: i64 = 3*u*tt/1000
148 let c3: i64 = ttt
149 let bx: i64 = (c0*x1 + c1*x1 + c2*x2 + c3*x2) / 1000
150 let by: i64 = (c0*y1 + c1*my + c2*my + c3*y2) / 1000
151 r2_line(fb,W,H,px,py,bx,by,r,gg,b)
152 px = bx; py = by
153 i = i + 1
154 }
155 return 0
156}
157// 5x7 glyph blit at scale; leftmost pixel = bit4. '_' not in font -> synth bottom row.
158func r2_char(fb: *i64, W: i64, H: i64, x: i64, y: i64, ch: i64, r: i64, gg: i64, b: i64, scale: i64) -> i64 {
159 var row: i64 = 0
160 while row < 7 {
161 var bits: i64 = nx_font_5x7_glyph_row(ch, row)
162 if ch == 95 { if row == 6 { bits = 31 } else { bits = 0 } }
163 if bits < 0 { bits = 0 }
164 var mask: i64 = 16
165 var col: i64 = 0
166 while col < 5 {
167 if (bits / mask) % 2 == 1 {
168 var sy: i64 = 0
169 while sy < scale { var sx: i64 = 0; while sx < scale { r2_px(fb,W,H,x+col*scale+sx,y+row*scale+sy,r,gg,b); sx = sx + 1 } sy = sy + 1 }
170 }
171 mask = mask / 2
172 col = col + 1
173 }
174 row = row + 1
175 }
176 return 0
177}
178// draw a graph node NAME (no trailing .nx) centered at (cx, top y). scale px cell.
179func r2_name(fb: *i64, W: i64, H: i64, g: *EcoGraph, idx: i64, cx: i64, y: i64, r: i64, gg: i64, b: i64, scale: i64) -> i64 {
180 let off: i64 = g.node_off[idx]
181 var n: i64 = 0
182 while g.arena[off+n] != (0 as u8) { n = n + 1 }
183 var e: i64 = n
184 if n > 3 { if g.arena[off+n-3]==(46 as u8) { if g.arena[off+n-2]==(110 as u8) { if g.arena[off+n-1]==(120 as u8) { e = n - 3 } } } }
185 let wpx: i64 = e * 6 * scale
186 var x: i64 = cx - wpx/2
187 var k: i64 = 0
188 while k < e {
189 r2_char(fb,W,H,x,y,g.arena[off+k] as i64,r,gg,b,scale)
190 x = x + 6*scale
191 k = k + 1
192 }
193 return 0
194}