code wiki / (root) / nx_cdmap_referee_lib.nx

nx_cdmap_referee_lib.nx source

↩ module page · 298 lines · 13959 B

1// nx_cdmap_referee_lib.nx -- THE SECOND RULER for the position map (datavis DV2a, 2026-09-15): re-derives the readability 2// grade of an EMITTED cdmap figure from the published SVG alone, then re-measures every label with REAL TrueType advances 3// (nx_ttf_fontlib over a real sans face -- Liberation Sans, SIL-OFL data under knowledge/fonts/) in place of the emitter's 4// 560-permil-em estimate, and runs the same critic (nx_chartlay_lib cl_critic) over both. The emitter's self-grade 5// (api.json readability_cut) is a CLAIM; this lib is the independent method class that can refute it: the heuristic critic 6// must REPRODUCE the published grade from the SVG (else the page and its api.json disagree), and the metric critic says 7// whether real glyph widths would have produced overlaps the estimate hid. 8// It is NOT the browser-pixel referee (DV2, crg_judge): the viewer's font is unknown to us and Liberation Sans is a stand-in 9// of the same class; a browser render judged on pixels stays the contracted rung above this one and is not claimed here. 10// Scope stated: label boxes keep the emitter's 1.25-em height so the ONE variable under test is width; kerning is not applied 11// (a sum of hmtx advances is the width the emitter would need to reserve); entity bytes in a label (a rare &amp;) are 12// measured as their literal characters. license_tier: ORIGINAL 13import "nx_syscalls.nx" 14import "nx_chartlay_lib.nx" 15import "nx_ttf_fontlib.nx" 16 17const CR_MAXN: i64 = 64 // marks or labels one cdmap figure can carry 18const CR_NAME_W: i64 = 64 // bytes per label name slot (zero-terminated) 19const CR_I64_BYTES: i64 = 8 20const CR_ABSENT: i64 = 0 - 1 21const CR_CH_GT: i64 = 62 22const CR_CH_MINUS: i64 = 45 23const CR_CH_0: i64 = 48 24const CR_CH_9: i64 = 57 25const CR_CH_SPACE: i64 = 32 26const CR_CH_COLON: i64 = 58 27const CR_LABEL_BASE: i64 = 12 // the emitter's CDP_LABEL_BASE: a label's text baseline sits this far below its box top 28const CR_FRAME_W: i64 = 4 29const CR_F_X0: i64 = 0 30const CR_F_Y0: i64 = 1 31const CR_F_X1: i64 = 2 32const CR_F_Y1: i64 = 3 33const CR_BASE10: i64 = 10 34const CR_PERMIL: i64 = 1000 35// THE ANNOTATION LAYER (datavis DV5): the emitter wraps every note in <g class='cdnote'> ... </g>; its text lines carry the 36// foreground fill like a label does, so the scanner tells them apart by the GROUP, never by the fill 37const CR_NOTE_OPEN: *u8 = "<g class='cdnote'>" 38const CR_G_CLOSE: *u8 = "</g>" 39 40func cr_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 41func cr_streq(a: *u8, b: *u8) -> i64 { 42 var i: i64 = 0 43 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 44 if b[i] != (0 as u8) { return 0 } 45 return 1 46} 47// bounded substring search: the index of needle inside h[from..to), or CR_ABSENT 48func cr_find(h: *u8, from: i64, to: i64, needle: *u8) -> i64 { 49 let nl: i64 = cr_slen(needle) 50 if nl == 0 { return CR_ABSENT } 51 var i: i64 = from 52 while i + nl <= to { 53 var k: i64 = 0 54 var ok: i64 = 1 55 while k < nl { 56 if h[i + k] != needle[k] { ok = 0; k = nl } else { k = k + 1 } 57 } 58 if ok == 1 { return i } 59 i = i + 1 60 } 61 return CR_ABSENT 62} 63// the integer at h[i] (an optional minus, then digits) read no further than `to`; CR_ABSENT when no digit is there 64func cr_int_at(h: *u8, i: i64, to: i64) -> i64 { 65 var p: i64 = i 66 var neg: i64 = 0 67 if p < to { if (h[p] as i64) == CR_CH_MINUS { neg = 1; p = p + 1 } } 68 var v: i64 = 0 69 var nd: i64 = 0 70 var go: i64 = 1 71 while go == 1 { 72 if p >= to { go = 0 } else { 73 let c: i64 = h[p] as i64 74 if c < CR_CH_0 { go = 0 } else { 75 if c > CR_CH_9 { go = 0 } else { v = v * CR_BASE10 + (c - CR_CH_0); nd = nd + 1; p = p + 1 } 76 } 77 } 78 } 79 if nd == 0 { return CR_ABSENT } 80 if neg == 1 { return 0 - v } 81 return v 82} 83// an integer attribute inside ONE tag [tag0, tag1): the needle is the attribute spelled WITH its leading space and quote 84// (" cx='"), so " x='" can never match inside " cx='" 85func cr_attr(h: *u8, tag0: i64, tag1: i64, needle: *u8) -> i64 { 86 let at: i64 = cr_find(h, tag0, tag1, needle) 87 if at == CR_ABSENT { return CR_ABSENT } 88 return cr_int_at(h, at + cr_slen(needle), tag1) 89} 90func cr_tag_end(h: *u8, from: i64, to: i64) -> i64 { 91 var i: i64 = from 92 while i < to { if (h[i] as i64) == CR_CH_GT { return i } i = i + 1 } 93 return CR_ABSENT 94} 95func cr_name_at(names: *u8, i: i64) -> *u8 { return (names as i64 + i * CR_NAME_W) as *u8 } 96// the LAST index of needle inside h[from..to), or CR_ABSENT 97func cr_rfind(h: *u8, from: i64, to: i64, needle: *u8) -> i64 { 98 var last: i64 = CR_ABSENT 99 var p: i64 = from 100 var go: i64 = 1 101 while go == 1 { 102 let at: i64 = cr_find(h, p, to, needle) 103 if at == CR_ABSENT { go = 0 } else { last = at; p = at + 1 } 104 } 105 return last 106} 107// 1 when offset t sits inside a note group: the nearest group opener before t is not yet closed 108func cr_in_note(h: *u8, s0: i64, t: i64) -> i64 { 109 let g0: i64 = cr_rfind(h, s0, t, CR_NOTE_OPEN) 110 if g0 == CR_ABSENT { return 0 } 111 let g1: i64 = cr_rfind(h, g0, t, CR_G_CLOSE) 112 if g1 == CR_ABSENT { return 1 } 113 return 0 114} 115// copy a text element's content [t1+1, </text>) into names slot i (zero-terminated, CR_NAME_W bound) 116func cr_text_into(h: *u8, t1: i64, s1: i64, names: *u8, i: i64) -> i64 { 117 let e: i64 = cr_find(h, t1, s1, "</text>" as *u8) 118 var q: i64 = t1 + 1 119 var k: i64 = 0 120 let slot: i64 = i * CR_NAME_W 121 while q < e { if k < CR_NAME_W - 1 { names[slot + k] = h[q]; k = k + 1 } q = q + 1 } 122 names[slot + k] = 0 as u8 123 return k 124} 125// parse the NOTE LINES of the cdmap figure: every <text> inside a <g class='cdnote'> group, in document order, with the 126// group's index in nlnote (0-based), x, the baseline y as emitted, and the font size; names in CR_NAME_W slots. 127// out[0] = the number of note groups. Returns the lines found, or CR_ABSENT when the page carries no cdmap svg. 128func cr_scan_notes(h: *u8, n: i64, nlx: *i64, nly: *i64, nlfont: *i64, nlnote: *i64, names: *u8, out: *i64) -> i64 { 129 let s0: i64 = cr_find(h, 0, n, "<svg class='cdmap'" as *u8) 130 if s0 == CR_ABSENT { return CR_ABSENT } 131 var s1: i64 = cr_find(h, s0, n, "</svg>" as *u8) 132 if s1 == CR_ABSENT { s1 = n } 133 var ng: i64 = 0 134 var nl: i64 = 0 135 var p: i64 = s0 136 var go: i64 = 1 137 while go == 1 { 138 let g0: i64 = cr_find(h, p, s1, CR_NOTE_OPEN) 139 if g0 == CR_ABSENT { go = 0 } else { 140 var g1: i64 = cr_find(h, g0, s1, CR_G_CLOSE) 141 if g1 == CR_ABSENT { g1 = s1 } 142 var q: i64 = g0 143 var inner: i64 = 1 144 while inner == 1 { 145 let t0: i64 = cr_find(h, q, g1, "<text" as *u8) 146 if t0 == CR_ABSENT { inner = 0 } else { 147 let t1: i64 = cr_tag_end(h, t0, g1) 148 if t1 == CR_ABSENT { inner = 0 } else { 149 if nl < CR_MAXN { 150 nlx[nl] = cr_attr(h, t0, t1, " x='" as *u8) 151 nly[nl] = cr_attr(h, t0, t1, " y='" as *u8) 152 nlfont[nl] = cr_attr(h, t0, t1, " font-size='" as *u8) 153 nlnote[nl] = ng 154 cr_text_into(h, t1, g1, names, nl) 155 nl = nl + 1 156 } 157 q = t1 158 } 159 } 160 } 161 ng = ng + 1 162 p = g1 163 } 164 } 165 out[0] = ng 166 return nl 167} 168// parse the cdmap figure out of an emitted page: marks (mx, my, mr), labels (lx = box left, ly = box top, lfont, lbold, 169// names in CR_NAME_W slots) and the plot frame from the panel rect (x0, y0, x1, y1). out[0] = marks found. 170// Returns the labels found, or CR_ABSENT when the page carries no cdmap svg at all. 171func cr_scan(h: *u8, n: i64, mx: *i64, my: *i64, mr: *i64, lx: *i64, ly: *i64, lfont: *i64, lbold: *i64, names: *u8, frame: *i64, out: *i64) -> i64 { 172 let s0: i64 = cr_find(h, 0, n, "<svg class='cdmap'" as *u8) 173 if s0 == CR_ABSENT { return CR_ABSENT } 174 var s1: i64 = cr_find(h, s0, n, "</svg>" as *u8) 175 if s1 == CR_ABSENT { s1 = n } 176 frame[CR_F_X0] = CR_ABSENT; frame[CR_F_Y0] = CR_ABSENT; frame[CR_F_X1] = CR_ABSENT; frame[CR_F_Y1] = CR_ABSENT 177 let r0: i64 = cr_find(h, s0, s1, "<rect" as *u8) 178 if r0 != CR_ABSENT { 179 let r1: i64 = cr_tag_end(h, r0, s1) 180 if r1 != CR_ABSENT { 181 frame[CR_F_X0] = cr_attr(h, r0, r1, " x='" as *u8) 182 frame[CR_F_Y0] = cr_attr(h, r0, r1, " y='" as *u8) 183 frame[CR_F_X1] = frame[CR_F_X0] + cr_attr(h, r0, r1, " width='" as *u8) 184 frame[CR_F_Y1] = frame[CR_F_Y0] + cr_attr(h, r0, r1, " height='" as *u8) 185 } 186 } 187 var nm: i64 = 0 188 var p: i64 = s0 189 var go: i64 = 1 190 while go == 1 { 191 let c0: i64 = cr_find(h, p, s1, "<circle" as *u8) 192 if c0 == CR_ABSENT { go = 0 } else { 193 let c1: i64 = cr_tag_end(h, c0, s1) 194 if c1 == CR_ABSENT { go = 0 } else { 195 if nm < CR_MAXN { 196 mx[nm] = cr_attr(h, c0, c1, " cx='" as *u8) 197 my[nm] = cr_attr(h, c0, c1, " cy='" as *u8) 198 mr[nm] = cr_attr(h, c0, c1, " r='" as *u8) 199 nm = nm + 1 200 } 201 p = c1 202 } 203 } 204 } 205 out[0] = nm 206 var nl: i64 = 0 207 p = s0 208 go = 1 209 while go == 1 { 210 let t0: i64 = cr_find(h, p, s1, "<text" as *u8) 211 if t0 == CR_ABSENT { go = 0 } else { 212 let t1: i64 = cr_tag_end(h, t0, s1) 213 if t1 == CR_ABSENT { go = 0 } else { 214 // a player label is the only text the emitter fills with the foreground token; ticks, means and quadrants are muted, 215 // and a note's lines (also foreground) sit inside a cdnote group, which cr_scan_notes reads instead 216 var is_label: i64 = 0 217 if cr_find(h, t0, t1, "fill='var(--nx-color-fg)'" as *u8) != CR_ABSENT { if cr_in_note(h, s0, t0) == 0 { is_label = 1 } } 218 if is_label == 1 { 219 if nl < CR_MAXN { 220 lx[nl] = cr_attr(h, t0, t1, " x='" as *u8) 221 ly[nl] = cr_attr(h, t0, t1, " y='" as *u8) - CR_LABEL_BASE 222 lfont[nl] = cr_attr(h, t0, t1, " font-size='" as *u8) 223 lbold[nl] = 0 224 if cr_find(h, t0, t1, " font-weight='700'" as *u8) != CR_ABSENT { lbold[nl] = 1 } 225 let e: i64 = cr_find(h, t1, s1, "</text>" as *u8) 226 var q: i64 = t1 + 1 227 var k: i64 = 0 228 let slot: i64 = nl * CR_NAME_W 229 while q < e { if k < CR_NAME_W - 1 { names[slot + k] = h[q]; k = k + 1 } q = q + 1 } 230 names[slot + k] = 0 as u8 231 nl = nl + 1 232 } 233 } 234 p = t1 235 } 236 } 237 } 238 return nl 239} 240// the width the emitter RESERVED for a label: its own estimate, so the heuristic critic re-derives the published grade 241func cr_w_heuristic(names: *u8, i: i64, font: i64) -> i64 { return cl_text_w(cr_slen(cr_name_at(names, i)), font) } 242// the width from real hmtx advances at this pixel size (no kerning: the width to reserve, not the width to draw) 243func cr_w_metric(fh: *i64, s: *u8, ph: i64) -> i64 { 244 var i: i64 = 0 245 var wpx: i64 = 0 246 while s[i] != (0 as u8) { wpx = wpx + tf_char_adv(fh, s[i] as i64, ph); i = i + 1 } 247 return wpx 248} 249// fill the label boxes (lw, lh) from either ruler: fhr/fhb = the regular and bold faces for the metric ruler, or 0 for the 250// heuristic. The height stays the emitter's 1.25 em either way so width is the one variable under test. 251func cr_boxes(n: i64, names: *u8, lfont: *i64, lbold: *i64, fhr: *i64, fhb: *i64, lw: *i64, lh: *i64) -> i64 { 252 var i: i64 = 0 253 while i < n { 254 lh[i] = cl_text_h(lfont[i]) 255 if (fhr as i64) == 0 { lw[i] = cr_w_heuristic(names, i, lfont[i]) } else { 256 var fh: *i64 = fhr 257 if lbold[i] == 1 { if (fhb as i64) != 0 { fh = fhb } } 258 lw[i] = cr_w_metric(fh, cr_name_at(names, i), lfont[i]) 259 } 260 i = i + 1 261 } 262 return 0 263} 264// the critic over the scanned figure with the given boxes; out = CL_CRIT_W slots, the same vector the emitter prints 265func cr_critic(n: i64, mx: *i64, my: *i64, mr: *i64, lx: *i64, ly: *i64, lw: *i64, lh: *i64, frame: *i64, out: *i64) -> i64 { 266 return cl_critic(n, mx, my, mr, lx, ly, lw, lh, frame[CR_F_X0], frame[CR_F_Y0], frame[CR_F_X1], frame[CR_F_Y1], out) 267} 268// the integer after `key` inside the named JSON block (the block name and key are matched as bare text so no double 269// quote has to be spelled in a literal); CR_ABSENT when the block or the key is missing 270func cr_json_int(j: *u8, n: i64, block: *u8, key: *u8) -> i64 { 271 let b: i64 = cr_find(j, 0, n, block) 272 if b == CR_ABSENT { return CR_ABSENT } 273 var bend: i64 = cr_find(j, b + cr_slen(block), n, "}" as *u8) 274 if bend == CR_ABSENT { bend = n } 275 let k: i64 = cr_find(j, b, bend, key) 276 if k == CR_ABSENT { return CR_ABSENT } 277 var p: i64 = k + cr_slen(key) 278 var go: i64 = 1 279 while go == 1 { 280 if p >= bend { go = 0 } else { 281 if (j[p] as i64) == CR_CH_COLON { go = 0 } else { p = p + 1 } 282 } 283 } 284 if p >= bend { return CR_ABSENT } 285 p = p + 1 286 go = 1 287 while go == 1 { 288 if p >= bend { go = 0 } else { 289 if (j[p] as i64) == CR_CH_SPACE { p = p + 1 } else { go = 0 } 290 } 291 } 292 return cr_int_at(j, p, bend) 293} 294// permil delta of the metric width against the heuristic one (positive = the real glyphs are wider than the reserve) 295func cr_delta_permil(heur: i64, metric: i64) -> i64 { 296 if heur <= 0 { return 0 } 297 return (metric - heur) * CR_PERMIL / heur 298}