code wiki / (root) / nx_render_svg.nx

nx_render_svg.nx source

↩ module page · 156 lines · 5453 B

1// nx_render_svg.nx -- native SVG (browser-visual) emitter. 2// 3// Per user 2026-05-15: "build the visual in cli, browser, etc it 4// should be native". 5// 6// Emits SVG markup as text -- standard XML. No JS dependency, no 7// canvas runtime. Output can be: 8// 1. piped to a .svg file and opened in any browser 9// 2. served by the sovereign nishi_web HTTP server (memory: Sovereign 10// nishi-web project) for live in-browser viewing 11// 12// Pure stdout writes. Caller redirects to a file via shell. 13 14// nx_safety_envelope: 15// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 16// sil_target: SIL1 17// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 18// verdict: NOT_YET_EVALUATED 19 20import "nx_kernel_v2.nx" 21 22const NX_SVG_W: nx_int = 600 23const NX_SVG_H: nx_int = 400 24 25func nx_svg_header(width: nx_int, height: nx_int) -> nx_int { 26 print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" as *u8); println("" as *u8) 27 print("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" as *u8) 28 print_i64(width) 29 print("\" height=\"" as *u8) 30 print_i64(height) 31 print("\" viewBox=\"0 0 " as *u8) 32 print_i64(width) 33 print(" " as *u8) 34 print_i64(height) 35 print("\">" as *u8); println("" as *u8) 36 return 0 37} 38 39func nx_svg_footer() -> nx_int { 40 println("</svg>" as *u8) 41 return 0 42} 43 44func nx_svg_rect(x: nx_int, y: nx_int, w: nx_int, h: nx_int, fill: *u8) -> nx_int { 45 print("<rect x=\"" as *u8); print_i64(x) 46 print("\" y=\"" as *u8); print_i64(y) 47 print("\" width=\"" as *u8); print_i64(w) 48 print("\" height=\"" as *u8); print_i64(h) 49 print("\" fill=\"" as *u8); print(fill) 50 print("\"/>" as *u8); println("" as *u8) 51 return 0 52} 53 54func nx_svg_line(x1: nx_int, y1: nx_int, x2: nx_int, y2: nx_int, stroke: *u8) -> nx_int { 55 print("<line x1=\"" as *u8); print_i64(x1) 56 print("\" y1=\"" as *u8); print_i64(y1) 57 print("\" x2=\"" as *u8); print_i64(x2) 58 print("\" y2=\"" as *u8); print_i64(y2) 59 print("\" stroke=\"" as *u8); print(stroke) 60 print("\" stroke-width=\"2\"/>" as *u8); println("" as *u8) 61 return 0 62} 63 64func nx_svg_circle(cx: nx_int, cy: nx_int, r: nx_int, fill: *u8) -> nx_int { 65 print("<circle cx=\"" as *u8); print_i64(cx) 66 print("\" cy=\"" as *u8); print_i64(cy) 67 print("\" r=\"" as *u8); print_i64(r) 68 print("\" fill=\"" as *u8); print(fill) 69 print("\"/>" as *u8); println("" as *u8) 70 return 0 71} 72 73func nx_svg_text(x: nx_int, y: nx_int, s: *u8) -> nx_int { 74 print("<text x=\"" as *u8); print_i64(x) 75 print("\" y=\"" as *u8); print_i64(y) 76 print("\" font-family=\"monospace\" font-size=\"14\">" as *u8) 77 print(s) 78 print("</text>" as *u8); println("" as *u8) 79 return 0 80} 81 82// ===== Bar chart -- emits a complete SVG document =================== 83func nx_svg_bar_chart(values: *nx_int, n: nx_int, title: *u8) -> nx_int { 84 let _h: nx_int = nx_svg_header(NX_SVG_W, NX_SVG_H) 85 let _t: nx_int = nx_svg_text(20, 24, title) 86 // Find max 87 var max_v: nx_int = 0 88 var i: nx_int = 0 89 while i < n { 90 let p: *nx_int = ((values as nx_int) + (i * 8)) as *nx_int 91 if p[0] > max_v { max_v = p[0] } 92 i = i + 1 93 } 94 if max_v <= 0 { max_v = 1 } 95 let bar_w: nx_int = (NX_SVG_W - 60) / n 96 let chart_h: nx_int = NX_SVG_H - 80 97 var k: nx_int = 0 98 while k < n { 99 let pv: *nx_int = ((values as nx_int) + (k * 8)) as *nx_int 100 let h: nx_int = (pv[0] * chart_h) / max_v 101 let x: nx_int = 30 + k * bar_w 102 let y: nx_int = NX_SVG_H - 40 - h 103 let _r: nx_int = nx_svg_rect(x, y, bar_w - 4, h, "steelblue" as *u8) 104 i = i + 1 105 k = k + 1 106 } 107 // Axis line 108 let _ax: nx_int = nx_svg_line(20, NX_SVG_H - 40, NX_SVG_W - 20, NX_SVG_H - 40, "black" as *u8) 109 let _f: nx_int = nx_svg_footer() 110 return n 111} 112 113// ===== Line plot -- complete SVG ==================================== 114func nx_svg_line_plot(values: *nx_int, n: nx_int, title: *u8) -> nx_int { 115 if n < 2 { return 0 } 116 let _h: nx_int = nx_svg_header(NX_SVG_W, NX_SVG_H) 117 let _t: nx_int = nx_svg_text(20, 24, title) 118 // Find min/max 119 var p0: *nx_int = values 120 var min_v: nx_int = p0[0] 121 var max_v: nx_int = p0[0] 122 var i: nx_int = 1 123 while i < n { 124 let p: *nx_int = ((values as nx_int) + (i * 8)) as *nx_int 125 if p[0] < min_v { min_v = p[0] } 126 if p[0] > max_v { max_v = p[0] } 127 i = i + 1 128 } 129 var range: nx_int = max_v - min_v 130 if range <= 0 { range = 1 } 131 let chart_w: nx_int = NX_SVG_W - 60 132 let chart_h: nx_int = NX_SVG_H - 80 133 134 // Axes 135 let _xax: nx_int = nx_svg_line(40, NX_SVG_H - 40, NX_SVG_W - 20, NX_SVG_H - 40, "black" as *u8) 136 let _yax: nx_int = nx_svg_line(40, 30, 40, NX_SVG_H - 40, "black" as *u8) 137 138 // Polyline as a series of line segments + circles. 139 var k: nx_int = 0 140 var prev_x: nx_int = 0 141 var prev_y: nx_int = 0 142 while k < n { 143 let pv: *nx_int = ((values as nx_int) + (k * 8)) as *nx_int 144 let x: nx_int = 40 + (k * chart_w) / (n - 1) 145 let y: nx_int = NX_SVG_H - 40 - ((pv[0] - min_v) * chart_h) / range 146 if k > 0 { 147 let _ln: nx_int = nx_svg_line(prev_x, prev_y, x, y, "crimson" as *u8) 148 } 149 let _ci: nx_int = nx_svg_circle(x, y, 3, "crimson" as *u8) 150 prev_x = x 151 prev_y = y 152 k = k + 1 153 } 154 let _f: nx_int = nx_svg_footer() 155 return n 156}