code wiki / (root) / nx_manga_charsheet.nx

nx_manga_charsheet.nx source

↩ module page · 320 lines · 14753 B

1// nx_manga_charsheet.nx -- R4 of the mangagen lane (comparewatch- contract mangagen_mcs_sheet_emit): 2// a CHARACTER SHEET. Composes four incumbents by forking them, and writes no renderer, no compositor and 3// no judge of its own: 4// nx_anat_sov one view per row <mesh> <mode> <eye C|L|R> <W> <H> <out.png> 5// nx_contactsheet the grid <dir> <out.png> <tile> <cols> 6// nx_charjudge per-cell identity <cell.png> <label> 7// nx_headcrop (caller's choice) bands a mesh to the head before the sweep 8// THE DONE-RULE, pre-declared on /compare/mangagen before this organ existed: 9// "Legality-checked pose sweep, renders, sheet grid; identity judged per cell." 10// HONEST REPORTING: face_axis is PRINTED for every cell, never suppressed and never floored away. 11// ⚠RENDER SIZE IS A JUDGE PARAMETER, MEASURED 2026-08-14, AND IT CORRECTED ME. I first saw face_axis 0 12// at 320x480, at skin 512x768 and on a head-cropped mesh, and concluded the model had no face. All three 13// shared a confound -- every one was at least 320 wide. At 256x384 the SAME mesh judges face_axis 1000 14// and CHARJUDGE 294. The face was there; the judge stops reporting it above roughly 320 px wide. 15// So MCS_RENDER_W/H sit inside the range where the judge actually measures, and that is a deliberate, 16// written-down choice rather than a lucky default. Filed as a judge-scale debt. 17// usage: nx_manga_charsheet <mesh.nxmesh> <sheet.conf> <outdir> <out.png> [tile] [cols] 18// sheet.conf rows: view|<mode>|<eye C|L|R>|<label> 19// license_tier: ORIGINAL expect_exit: 0 20import "nx_str.nx" 21import "nx_syscalls.nx" 22import "nx_tool_run.nx" 23const MCS_MAGIC_1024: i64 = 1024 24const MCS_CAP: i64 = 65536 25const MCS_MAXVIEW: i64 = 32 26const MCS_RENDER_W: i64 = 256 27const MCS_RENDER_H: i64 = 384 28const MCS_TILE_DEFAULT: i64 = 192 29const MCS_COLS_DEFAULT: i64 = 4 30const MCS_ANAT: *u8 = "/volume1/homes/elderwesto/nishihost/nx_anat_sov.elf" 31const MCS_SHEET: *u8 = "/volume1/homes/elderwesto/nishihost/nx_contactsheet.elf" 32const MCS_JUDGE: *u8 = "/volume1/homes/elderwesto/nishihost/nx_charjudge.elf" 33func mcs_puts(s: *u8) -> i64 { sys_write(1, s, nx_str_len(s)); return 0 } 34func mcs_pi(v: i64) -> i64 { 35 let t: *u8 = sys_mmap(32) 36 let o: *u8 = sys_mmap(32) 37 var m: i64 = v 38 var k: i64 = 0 39 if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } 40 if m == 0 { t[0] = 48; k = 1 } 41 while m > 0 { t[k] = 48 + (m % 10); m = m / 10; k = k + 1 } 42 var i: i64 = 0 43 while i < k { o[i] = t[k - 1 - i]; i = i + 1 } 44 sys_write(1, o, k) 45 return 0 46} 47func mcs_atoi(s: *u8) -> i64 { 48 var i: i64 = 0 49 var v: i64 = 0 50 var go: i64 = 1 51 while go == 1 { 52 let c: i64 = s[i] as i64 53 if c < 48 { go = 0 } else { if c > 57 { go = 0 } else { v = v * 10 + (c - 48); i = i + 1 } } 54 } 55 return v 56} 57func mcs_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 58func mcs_num(dst: *u8, off: i64, v: i64) -> i64 { 59 var m: i64 = v 60 let t: *u8 = sys_mmap(32) 61 var k: i64 = 0 62 if m == 0 { t[0] = 48 as u8; k = 1 } 63 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 64 var o: i64 = off 65 var z: i64 = 0 66 while z < k { dst[o] = t[k - 1 - z]; o = o + 1; z = z + 1 } 67 return o 68} 69// The renderer ACCEPTS an unknown mode and draws something anyway, so "the render failed" never fires for 70// a typo -- the sheet would compose a cell nobody asked for. Found by the gate's neg-control-hole tooth on 71// its first run. Validate the declared mode HERE, fail-closed, so the guarantee is real rather than 72// inherited from a downstream organ that does not make it. 73func mcs_mode_ok(m: *u8) -> i64 { 74 if nx_str_eq(m, "intact" as *u8) == 1 { return 1 } 75 if nx_str_eq(m, "skin" as *u8) == 1 { return 1 } 76 if nx_str_eq(m, "explode" as *u8) == 1 { return 1 } 77 // layerN: the prefix plus at least one digit 78 if m[0] == (108 as u8) { if m[1] == (97 as u8) { if m[2] == (121 as u8) { if m[3] == (101 as u8) { if m[4] == (114 as u8) { 79 let d: i64 = m[5] as i64 80 if d >= 48 { if d <= 57 { return 1 } } 81 } } } } } 82 return 0 83} 84func mcs_exists(p: *u8) -> i64 { 85 let fd: i64 = sys_openat_rd(p) 86 if fd < 0 { return 0 } 87 sys_close(fd) 88 return 1 89} 90func mcs_read(path: *u8, buf: *u8, cap: i64) -> i64 { 91 let fd: i64 = sys_openat_rd(path) 92 if fd < 0 { return 0 - 1 } 93 var tot: i64 = 0 94 while tot < cap { let r: i64 = sys_read(fd, (buf as i64 + tot) as *u8, cap - tot); if r <= 0 { break } tot = tot + r } 95 sys_close(fd) 96 return tot 97} 98// pull an integer field out of the judge's JSON: find "<key>": then read the number after it. 99// Returns the value, or the caller's miss value when the key is absent -- an ABSENT axis and an axis that 100// measured zero are different facts, so they must not collapse to the same number. 101func mcs_json_int(buf: *u8, n: i64, key: *u8, miss: i64) -> i64 { 102 let m: i64 = nx_str_len(key) 103 var i: i64 = 0 104 while i + m < n { 105 var k: i64 = 0 106 var hit: i64 = 1 107 while k < m { if buf[i+k] != key[k] { hit = 0; k = m } else { k = k + 1 } } 108 if hit == 1 { 109 var j: i64 = i + m 110 var guard: i64 = 1 111 while guard == 1 { 112 if j >= n { guard = 0 } else { 113 let c: i64 = buf[j] as i64 114 if c == 34 { j = j + 1 } else { if c == 58 { j = j + 1 } else { if c == 32 { j = j + 1 } else { guard = 0 } } } 115 } 116 } 117 var v: i64 = 0 118 var any: i64 = 0 119 var neg: i64 = 0 120 if j < n { if buf[j] == (45 as u8) { neg = 1; j = j + 1 } } 121 var go2: i64 = 1 122 while go2 == 1 { 123 if j >= n { go2 = 0 } else { 124 let d: i64 = buf[j] as i64 125 if d < 48 { go2 = 0 } else { if d > 57 { go2 = 0 } else { v = v * 10 + (d - 48); any = 1; j = j + 1 } } 126 } 127 } 128 if any == 1 { if neg == 1 { return 0 - v } return v } 129 return miss 130 } 131 i = i + 1 132 } 133 return miss 134} 135// THE CONTRACT (watched as mangagen_mcs_sheet_emit): render every declared view of one character, compose 136// them into a sheet, and judge each cell -- or REFUSE. Returns 0, or a named non-zero. 137func mcs_sheet_emit(mesh: *u8, nviews: i64, modes: *i64, eyes: *i64, labels: *i64, dir: *u8, outp: *u8, tile: i64, cols: i64) -> i64 { 138 let out: *u8 = sys_mmap(MCS_CAP) 139 let ol: *i64 = sys_mmap(16) as *i64 140 let av: *i64 = sys_mmap(128) as *i64 141 let cell: *u8 = sys_mmap(MCS_MAGIC_1024) 142 let wbuf: *u8 = sys_mmap(64) 143 let hbuf: *u8 = sys_mmap(64) 144 let tbuf: *u8 = sys_mmap(64) 145 let cbuf: *u8 = sys_mmap(64) 146 var q: i64 = mcs_num(wbuf, 0, MCS_RENDER_W); wbuf[q] = 0 as u8 147 q = mcs_num(hbuf, 0, MCS_RENDER_H); hbuf[q] = 0 as u8 148 q = mcs_num(tbuf, 0, tile); tbuf[q] = 0 as u8 149 q = mcs_num(cbuf, 0, cols); cbuf[q] = 0 as u8 150 sys_mkdir(dir, 493) 151 // ---- RENDER PASS: one view per declared row ------------------------------------------------ 152 var i: i64 = 0 153 while i < nviews { 154 if mcs_mode_ok(modes[i] as *u8) == 0 { 155 mcs_puts("MANGA-CHARSHEET-REFUSE view " as *u8); mcs_pi(i) 156 mcs_puts(" declares mode '" as *u8); mcs_puts(modes[i] as *u8) 157 mcs_puts("', which is not one of intact, skin, explode or layerN. The renderer would draw\n" as *u8) 158 mcs_puts(" something anyway, so the sheet refuses here rather than composing a cell nobody asked for.\n" as *u8) 159 return 3 160 } 161 var o: i64 = mcs_cat(cell, 0, dir) 162 o = mcs_cat(cell, o, "/" as *u8) 163 o = mcs_num(cell, o, i) 164 o = mcs_cat(cell, o, ".png" as *u8) 165 cell[o] = 0 as u8 166 av[0] = MCS_ANAT as i64 167 av[1] = mesh as i64 168 av[2] = modes[i] 169 av[3] = eyes[i] 170 av[4] = wbuf as i64 171 av[5] = hbuf as i64 172 av[6] = cell as i64 173 av[7] = 0 174 let rc: i64 = tr_run_capture(MCS_ANAT, av, out, MCS_CAP, ol) 175 if rc != 0 { 176 mcs_puts("MANGA-CHARSHEET-REFUSE renderer failed on view " as *u8); mcs_pi(i); mcs_puts("\n" as *u8) 177 return 4 178 } 179 if mcs_exists(cell) == 0 { 180 mcs_puts("MANGA-CHARSHEET-REFUSE view " as *u8); mcs_pi(i) 181 mcs_puts(" produced no image -- refusing rather than composing a sheet with a hole in it\n" as *u8) 182 return 4 183 } 184 i = i + 1 185 } 186 // ---- GRID PASS: the incumbent contact sheet composes them -------------------------------- 187 av[0] = MCS_SHEET as i64 188 av[1] = dir as i64 189 av[2] = outp as i64 190 av[3] = tbuf as i64 191 av[4] = cbuf as i64 192 av[5] = 0 193 let src: i64 = tr_run_capture(MCS_SHEET, av, out, MCS_CAP, ol) 194 if src != 0 { mcs_puts("MANGA-CHARSHEET-REFUSE sheet composition failed\n" as *u8); return 5 } 195 if mcs_exists(outp) == 0 { mcs_puts("MANGA-CHARSHEET-REFUSE no sheet reached disk\n" as *u8); return 5 } 196 // ---- JUDGE PASS: every cell, and the zero is PRINTED --------------------------------------- 197 var judged: i64 = 0 198 var faceless: i64 = 0 199 i = 0 200 while i < nviews { 201 var o2: i64 = mcs_cat(cell, 0, dir) 202 o2 = mcs_cat(cell, o2, "/" as *u8) 203 o2 = mcs_num(cell, o2, i) 204 o2 = mcs_cat(cell, o2, ".png" as *u8) 205 cell[o2] = 0 as u8 206 av[0] = MCS_JUDGE as i64 207 av[1] = cell as i64 208 av[2] = labels[i] 209 av[3] = 0 210 let jrc: i64 = tr_run_capture(MCS_JUDGE, av, out, MCS_CAP, ol) 211 if jrc != 0 { mcs_puts("MANGA-CHARSHEET-REFUSE judge failed on cell " as *u8); mcs_pi(i); mcs_puts("\n" as *u8); return 6 } 212 let n: i64 = ol[0] 213 let contour: i64 = mcs_json_int(out, n, "contour_axis" as *u8, 0 - 1) 214 let comp: i64 = mcs_json_int(out, n, "composition" as *u8, 0 - 1) 215 let pal: i64 = mcs_json_int(out, n, "palette_axis" as *u8, 0 - 1) 216 let face: i64 = mcs_json_int(out, n, "face_axis" as *u8, 0 - 1) 217 if contour < 0 { mcs_puts("MANGA-CHARSHEET-REFUSE judge returned no contour axis for cell " as *u8); mcs_pi(i); mcs_puts("\n" as *u8); return 6 } 218 mcs_puts(" cell " as *u8); mcs_pi(i); mcs_puts(" " as *u8); mcs_puts(labels[i] as *u8) 219 mcs_puts(" contour=" as *u8); mcs_pi(contour) 220 mcs_puts(" composition=" as *u8); mcs_pi(comp) 221 mcs_puts(" palette=" as *u8); mcs_pi(pal) 222 mcs_puts(" face=" as *u8); mcs_pi(face) 223 if face == 0 { faceless = faceless + 1; mcs_puts(" (honest zero: no facial geometry to measure)" as *u8) } 224 mcs_puts("\n" as *u8) 225 judged = judged + 1 226 i = i + 1 227 } 228 mcs_puts(" partition: views=" as *u8); mcs_pi(nviews) 229 mcs_puts(" rendered=" as *u8); mcs_pi(nviews) 230 mcs_puts(" judged=" as *u8); mcs_pi(judged) 231 if judged != nviews { mcs_puts(" PARTITION-LEAK -- refusing\n" as *u8); return 7 } 232 mcs_puts(" (parts sum)\n" as *u8) 233 // Report the face axis either way, and only EXPLAIN a zero when there is one. The first cut printed 234 // the explanation unconditionally, so a sheet with zero faceless cells still argued about faceless 235 // cells. A summary that narrates a condition it did not observe is a lie with good intentions. 236 if faceless > 0 { 237 mcs_puts(" face_axis reads zero on " as *u8); mcs_pi(faceless) 238 mcs_puts(" of " as *u8); mcs_pi(nviews) 239 mcs_puts(" cells -- REPORTED, never floored away.\n" as *u8) 240 } else { 241 mcs_puts(" face_axis measured on all " as *u8); mcs_pi(nviews) 242 mcs_puts(" cells.\n" as *u8) 243 } 244 return 0 245} 246func main(argc: i64, argv: *i64) -> i64 { 247 if argc < 5 { 248 mcs_puts("usage: nx_manga_charsheet <mesh.nxmesh> <sheet.conf> <outdir> <out.png> [tile] [cols]\n" as *u8) 249 mcs_puts(" sheet.conf rows: view|<mode intact|skin|explode|layerN>|<eye C|L|R>|<label>\n" as *u8) 250 mcs_puts(" every declared view must render or the sheet REFUSES -- it never composes a hole.\n" as *u8) 251 sys_exit(2); return 2 252 } 253 let mesh: *u8 = argv[1] as *u8 254 if mcs_exists(mesh) == 0 { 255 mcs_puts("MANGA-CHARSHEET-REFUSE cannot read mesh: " as *u8); mcs_puts(mesh); mcs_puts("\n" as *u8) 256 sys_exit(3); return 3 257 } 258 let buf: *u8 = sys_mmap(MCS_CAP) 259 let n: i64 = mcs_read(argv[2] as *u8, buf, MCS_CAP - 2) 260 if n <= 0 { 261 mcs_puts("MANGA-CHARSHEET-REFUSE cannot read sheet spec: " as *u8); mcs_puts(argv[2] as *u8); mcs_puts("\n" as *u8) 262 sys_exit(3); return 3 263 } 264 buf[n] = 0 as u8 265 let modes: *i64 = sys_mmap(8 * MCS_MAXVIEW) as *i64 266 let eyes: *i64 = sys_mmap(8 * MCS_MAXVIEW) as *i64 267 let labels: *i64 = sys_mmap(8 * MCS_MAXVIEW) as *i64 268 var nv: i64 = 0 269 var p: i64 = 0 270 while p < n { 271 var e: i64 = p 272 while e < n { if buf[e] == (10 as u8) { break } e = e + 1 } 273 buf[e] = 0 as u8 274 let line: *u8 = (buf as i64 + p) as *u8 275 p = e + 1 276 var skip: i64 = 0 277 if line[0] == (0 as u8) { skip = 1 } 278 if line[0] == (35 as u8) { skip = 1 } 279 if line[0] == (13 as u8) { skip = 1 } 280 if skip == 0 { 281 // view|<mode>|<eye>|<label> 282 var f1: i64 = 0 - 1 283 var f2: i64 = 0 - 1 284 var f3: i64 = 0 - 1 285 var x: i64 = 0 286 while line[x] != (0 as u8) { 287 if line[x] == (124 as u8) { 288 if f1 < 0 { f1 = x } else { if f2 < 0 { f2 = x } else { if f3 < 0 { f3 = x } } } 289 } 290 x = x + 1 291 } 292 if f3 > 0 { 293 line[f1] = 0 as u8 294 line[f2] = 0 as u8 295 line[f3] = 0 as u8 296 if nv < MCS_MAXVIEW { 297 modes[nv] = (line as i64) + f1 + 1 298 eyes[nv] = (line as i64) + f2 + 1 299 labels[nv] = (line as i64) + f3 + 1 300 nv = nv + 1 301 } 302 } 303 } 304 } 305 if nv <= 0 { 306 mcs_puts("MANGA-CHARSHEET-REFUSE the sheet spec declares no views\n" as *u8) 307 sys_exit(3); return 3 308 } 309 var tile: i64 = MCS_TILE_DEFAULT 310 var cols: i64 = MCS_COLS_DEFAULT 311 if argc >= 6 { tile = mcs_atoi(argv[5] as *u8) } 312 if argc >= 7 { cols = mcs_atoi(argv[6] as *u8) } 313 mcs_puts("=== nx_manga_charsheet views=" as *u8); mcs_pi(nv) 314 mcs_puts(" tile=" as *u8); mcs_pi(tile); mcs_puts(" cols=" as *u8); mcs_pi(cols); mcs_puts("\n" as *u8) 315 let rc: i64 = mcs_sheet_emit(mesh, nv, modes, eyes, labels, argv[3] as *u8, argv[4] as *u8, tile, cols) 316 if rc != 0 { sys_exit(rc); return rc } 317 mcs_puts("MANGA-CHARSHEET-OK " as *u8); mcs_puts(argv[4] as *u8); mcs_puts("\n" as *u8) 318 sys_exit(0) 319 return 0 320}