code wiki / (root) / nx_cdmap_pixjudge_lib.nx

nx_cdmap_pixjudge_lib.nx source

↩ module page · 430 lines · 19528 B

1// nx_cdmap_pixjudge_lib.nx -- the position map's PIXEL REFEREE (datavis DV2, 2026-09-15): the emitted cdmap SVG is re-drawn 2// as INK and the readability defects are counted on the pixels. Every player label is rasterised glyph by glyph through 3// the sovereign TrueType engine (nx_ttf_fontlib, the browser's own font renderer: real outlines, real hmtx advances, the 4// pen walk tf_draw_text walks) and every mark is a filled disk; then: two labels whose ink shares a pixel (LL), a label 5// whose ink enters ANOTHER player's disk (LM), a label whose ink leaves the plot frame (OFF), marks whose disks overlap 6// (MM, the centre-distance rule cl_critic uses, a data property). DEFECTS = LL + LM + OFF, the same sum the emitter 7// publishes, so the pixel vector and the published vector compare slot for slot. SPREAD is a data property, not an ink 8// property, and is not re-derived here. 9// WHY: the self-grade counts BOXES whose width is a heuristic (CL_TEXT_W_PERMIL per glyph); a face wider than the 10// heuristic, or a run of wide glyphs, overlaps in ink while the boxes stay apart -- a font-metric drift the box 11// arithmetic cannot see. This ruler is the in-estate half of the liar-killer: it renders through a second path (glyph 12// coverage, not box arithmetic) and disagrees when the picture does. A foreign browser stays an oracle outside the path. 13// A label's OWN mark is skipped by design, exactly as cl_critic skips it (a halo label sits on its mark on purpose). 14// Ink = coverage at or above CRG_INK_MIN of 255: an anti-aliased fringe is not ink a reader collides on. 15// The figure grammar is cr_scan's (composed, never re-implemented). license_tier: ORIGINAL. No hw writes (Rule 26). 16import "nx_syscalls.nx" 17import "nx_cdmap_referee_lib.nx" 18 19const CRG_INK_MIN: i64 = 128 // half coverage: below this a pixel is fringe, not ink 20const CRG_VB_W_DEFAULT: i64 = 760 // the emitter's CDP_SVG_W, used only when the svg carries no viewBox 21const CRG_VB_H_DEFAULT: i64 = 520 // the emitter's CDP_SVG_H 22const CRG_VB_MAX: i64 = 4096 // a viewBox axis past this is refused (one mask would exceed 16 MiB) 23const CRG_I64_BYTES: i64 = 8 24const CRG_CH_SPACE: i64 = 32 25const CRG_BBOX_W: i64 = 4 // minx miny maxx maxy per label, in canvas coordinates, UNCLIPPED 26const CRG_B_X0: i64 = 0 27const CRG_B_Y0: i64 = 1 28const CRG_B_X1: i64 = 2 29const CRG_B_Y1: i64 = 3 30// the record a scan fills: a slot is a pointer or a count 31const CRG_R_MX: i64 = 0 32const CRG_R_MY: i64 = 1 33const CRG_R_MR: i64 = 2 34const CRG_R_LX: i64 = 3 35const CRG_R_LY: i64 = 4 36const CRG_R_LFONT: i64 = 5 37const CRG_R_LBOLD: i64 = 6 38const CRG_R_FRAME: i64 = 7 39const CRG_R_OUT: i64 = 8 40const CRG_R_NAMES: i64 = 9 41const CRG_R_NL: i64 = 10 42const CRG_R_NM: i64 = 11 43const CRG_R_W: i64 = 12 44const CRG_R_H: i64 = 13 45const CRG_R_MASKS: i64 = 14 46const CRG_R_INK: i64 = 15 47const CRG_R_BBOX: i64 = 16 48// THE ANNOTATION LAYER (datavis DV5): note lines scanned from the cdnote groups, ONE mask per note (its lines united), so a 49// note is one reading item exactly as the emitter's critic treats it (its box is one label with no player of its own) 50const CRG_R_NNL: i64 = 17 // note lines found 51const CRG_R_NN: i64 = 18 // notes (groups) found 52const CRG_R_NLX: i64 = 19 53const CRG_R_NLY: i64 = 20 // the baseline as emitted 54const CRG_R_NLFONT: i64 = 21 55const CRG_R_NLNOTE: i64 = 22 // the note each line belongs to 56const CRG_R_NNAMES: i64 = 23 57const CRG_R_NMASKS: i64 = 24 // one mask per note 58const CRG_R_NINK: i64 = 25 59const CRG_R_NBBOX: i64 = 26 60const CRG_R_SLOTS: i64 = 27 61// the pixel vector, slot for slot with CL_C_* for the first four, then the two this ruler owns 62const CRG_V_LL: i64 = 0 63const CRG_V_LM: i64 = 1 64const CRG_V_OFF: i64 = 2 65const CRG_V_MM: i64 = 3 66const CRG_V_INKPX: i64 = 4 // ink pixels over every label: a scale field, 0 means nothing was drawn 67const CRG_V_DEFECTS: i64 = 5 68const CRG_V_W: i64 = 6 69// crg_judge results: every negative names WHY the ruler could not judge, never a silent zero vector 70const CRG_OK: i64 = 0 71const CRG_NO_FIGURE: i64 = 0 - 1 72const CRG_NO_LABELS: i64 = 0 - 2 73const CRG_UNPAIRED: i64 = 0 - 3 74const CRG_NO_FRAME: i64 = 0 - 4 75const CRG_NO_FONT: i64 = 0 - 5 76const CRG_NO_INK: i64 = 0 - 6 77const CRG_CANVAS: i64 = 0 - 7 78 79func crg_i64(n: i64) -> *i64 { return sys_mmap(n * CRG_I64_BYTES) as *i64 } 80func crg_rec_new() -> *i64 { 81 let r: *i64 = crg_i64(CRG_R_SLOTS) 82 r[CRG_R_MX] = crg_i64(CR_MAXN) as i64 83 r[CRG_R_MY] = crg_i64(CR_MAXN) as i64 84 r[CRG_R_MR] = crg_i64(CR_MAXN) as i64 85 r[CRG_R_LX] = crg_i64(CR_MAXN) as i64 86 r[CRG_R_LY] = crg_i64(CR_MAXN) as i64 87 r[CRG_R_LFONT] = crg_i64(CR_MAXN) as i64 88 r[CRG_R_LBOLD] = crg_i64(CR_MAXN) as i64 89 r[CRG_R_FRAME] = crg_i64(CR_FRAME_W) as i64 90 r[CRG_R_OUT] = crg_i64(1) as i64 91 r[CRG_R_NAMES] = sys_mmap(CR_MAXN * CR_NAME_W) as i64 92 r[CRG_R_MASKS] = crg_i64(CR_MAXN) as i64 93 r[CRG_R_INK] = crg_i64(CR_MAXN) as i64 94 r[CRG_R_BBOX] = crg_i64(CR_MAXN * CRG_BBOX_W) as i64 95 r[CRG_R_NNL] = 0 96 r[CRG_R_NN] = 0 97 r[CRG_R_NLX] = crg_i64(CR_MAXN) as i64 98 r[CRG_R_NLY] = crg_i64(CR_MAXN) as i64 99 r[CRG_R_NLFONT] = crg_i64(CR_MAXN) as i64 100 r[CRG_R_NLNOTE] = crg_i64(CR_MAXN) as i64 101 r[CRG_R_NNAMES] = sys_mmap(CR_MAXN * CR_NAME_W) as i64 102 r[CRG_R_NMASKS] = crg_i64(CR_MAXN) as i64 103 r[CRG_R_NINK] = crg_i64(CR_MAXN) as i64 104 r[CRG_R_NBBOX] = crg_i64(CR_MAXN * CRG_BBOX_W) as i64 105 return r 106} 107func crg_nbbox_at(r: *i64, g: i64) -> *i64 { return ((r[CRG_R_NBBOX] as i64) + g * CRG_BBOX_W * CRG_I64_BYTES) as *i64 } 108// union bb2 into bb (either may be ABSENT = no ink) 109func crg_bbox_union(bb: *i64, bb2: *i64) -> i64 { 110 if bb2[CRG_B_X0] == CR_ABSENT { return 0 } 111 if bb[CRG_B_X0] == CR_ABSENT { bb[CRG_B_X0] = bb2[CRG_B_X0]; bb[CRG_B_Y0] = bb2[CRG_B_Y0]; bb[CRG_B_X1] = bb2[CRG_B_X1]; bb[CRG_B_Y1] = bb2[CRG_B_Y1]; return 0 } 112 if bb2[CRG_B_X0] < bb[CRG_B_X0] { bb[CRG_B_X0] = bb2[CRG_B_X0] } 113 if bb2[CRG_B_Y0] < bb[CRG_B_Y0] { bb[CRG_B_Y0] = bb2[CRG_B_Y0] } 114 if bb2[CRG_B_X1] > bb[CRG_B_X1] { bb[CRG_B_X1] = bb2[CRG_B_X1] } 115 if bb2[CRG_B_Y1] > bb[CRG_B_Y1] { bb[CRG_B_Y1] = bb2[CRG_B_Y1] } 116 return 0 117} 118func crg_skip_num(h: *u8, p: i64, to: i64) -> i64 { 119 var q: i64 = p 120 if q < to { if (h[q] as i64) == CR_CH_MINUS { q = q + 1 } } 121 var go: i64 = 1 122 while go == 1 { 123 if q >= to { go = 0 } else { 124 let c: i64 = h[q] as i64 125 if c < CR_CH_0 { go = 0 } else { if c > CR_CH_9 { go = 0 } else { q = q + 1 } } 126 } 127 } 128 return q 129} 130func crg_skip_sp(h: *u8, p: i64, to: i64) -> i64 { 131 var q: i64 = p 132 var go: i64 = 1 133 while go == 1 { 134 if q >= to { go = 0 } else { if (h[q] as i64) == CRG_CH_SPACE { q = q + 1 } else { go = 0 } } 135 } 136 return q 137} 138// the cdmap svg's viewBox width and height into wh[0], wh[1]: 1 read from the tag, 0 the emitter's defaults (no 139// viewBox on the tag), CR_ABSENT when the figure or an axis is unreadable or an axis is beyond CRG_VB_MAX 140func crg_viewbox(h: *u8, n: i64, wh: *i64) -> i64 { 141 wh[0] = CRG_VB_W_DEFAULT 142 wh[1] = CRG_VB_H_DEFAULT 143 let s0: i64 = cr_find(h, 0, n, "<svg class='cdmap'" as *u8) 144 if s0 == CR_ABSENT { return CR_ABSENT } 145 let s1: i64 = cr_tag_end(h, s0, n) 146 if s1 == CR_ABSENT { return CR_ABSENT } 147 let key: *u8 = " viewBox='" as *u8 148 let vb: i64 = cr_find(h, s0, s1, key) 149 if vb == CR_ABSENT { return 0 } 150 var p: i64 = vb + cr_slen(key) 151 p = crg_skip_num(h, p, s1) 152 p = crg_skip_sp(h, p, s1) 153 p = crg_skip_num(h, p, s1) 154 p = crg_skip_sp(h, p, s1) 155 let w: i64 = cr_int_at(h, p, s1) 156 p = crg_skip_num(h, p, s1) 157 p = crg_skip_sp(h, p, s1) 158 let ht: i64 = cr_int_at(h, p, s1) 159 if w == CR_ABSENT { return CR_ABSENT } 160 if ht == CR_ABSENT { return CR_ABSENT } 161 if w <= 0 { return CR_ABSENT } 162 if ht <= 0 { return CR_ABSENT } 163 if w > CRG_VB_MAX { return CR_ABSENT } 164 if ht > CRG_VB_MAX { return CR_ABSENT } 165 wh[0] = w 166 wh[1] = ht 167 return 1 168} 169// scan the figure into the record (cr_scan composed) and read the canvas; the result names the first thing that 170// stops a judgement, in the order a reader would hit it 171func crg_scan(h: *u8, n: i64, r: *i64) -> i64 { 172 let nl: i64 = cr_scan(h, n, r[CRG_R_MX] as *i64, r[CRG_R_MY] as *i64, r[CRG_R_MR] as *i64, r[CRG_R_LX] as *i64, r[CRG_R_LY] as *i64, r[CRG_R_LFONT] as *i64, r[CRG_R_LBOLD] as *i64, r[CRG_R_NAMES] as *u8, r[CRG_R_FRAME] as *i64, r[CRG_R_OUT] as *i64) 173 if nl == CR_ABSENT { return CRG_NO_FIGURE } 174 r[CRG_R_NL] = nl 175 let outp: *i64 = r[CRG_R_OUT] as *i64 176 r[CRG_R_NM] = outp[0] 177 // the notes: a figure without any is 0 groups, never a refusal 178 let nout: *i64 = crg_i64(1) 179 nout[0] = 0 180 var nnl: i64 = cr_scan_notes(h, n, r[CRG_R_NLX] as *i64, r[CRG_R_NLY] as *i64, r[CRG_R_NLFONT] as *i64, r[CRG_R_NLNOTE] as *i64, r[CRG_R_NNAMES] as *u8, nout) 181 if nnl < 0 { nnl = 0 } 182 r[CRG_R_NNL] = nnl 183 r[CRG_R_NN] = nout[0] 184 let wh: *i64 = crg_i64(2) 185 if crg_viewbox(h, n, wh) == CR_ABSENT { return CRG_CANVAS } 186 r[CRG_R_W] = wh[0] 187 r[CRG_R_H] = wh[1] 188 if nl == 0 { return CRG_NO_LABELS } 189 if nl != r[CRG_R_NM] { return CRG_UNPAIRED } 190 let fr: *i64 = r[CRG_R_FRAME] as *i64 191 if fr[CR_F_X0] == CR_ABSENT { return CRG_NO_FRAME } 192 return CRG_OK 193} 194// rasterise one label's ink into mask (W x H bytes, 1 = ink) through the font engine: the pen walks from x along the 195// baseline ybase, each glyph's coverage cell sits lm left of the pen and asc_px above the baseline, exactly where 196// tf_draw_text puts it. bb receives the UNCLIPPED ink bounding box (so ink past the canvas still counts as off-frame). 197// Returns the ink pixel count inside the canvas (0 when the face drew nothing at this size). 198func crg_mask_label(fh: *i64, s: *u8, ph: i64, x: i64, ybase: i64, W: i64, H: i64, mask: *u8, bb: *i64) -> i64 { 199 var pen: i64 = x 200 let y0: i64 = ybase - tf_asc_px(fh, ph) 201 var ink: i64 = 0 202 var seen: i64 = 0 203 var i: i64 = 0 204 while s[i] != (0 as u8) { 205 let ch: i64 = s[i] as i64 206 let rec: *i64 = tf_glyph(fh, ch, ph) 207 if (rec as i64) == 0 { pen = pen + tf_char_adv(fh, ch, ph) } else { 208 let cov: *u8 = rec[TF_R_COV] as *u8 209 let cw: i64 = rec[TF_R_CW] 210 let chh: i64 = rec[TF_R_CHH] 211 let x0: i64 = pen - rec[TF_R_LM] 212 var gy: i64 = 0 213 while gy < chh { 214 let fy: i64 = y0 + gy 215 var gx: i64 = 0 216 while gx < cw { 217 if (cov[gy * cw + gx] as i64) >= CRG_INK_MIN { 218 let fx: i64 = x0 + gx 219 if seen == 0 { bb[CRG_B_X0] = fx; bb[CRG_B_Y0] = fy; bb[CRG_B_X1] = fx; bb[CRG_B_Y1] = fy; seen = 1 } else { 220 if fx < bb[CRG_B_X0] { bb[CRG_B_X0] = fx } 221 if fy < bb[CRG_B_Y0] { bb[CRG_B_Y0] = fy } 222 if fx > bb[CRG_B_X1] { bb[CRG_B_X1] = fx } 223 if fy > bb[CRG_B_Y1] { bb[CRG_B_Y1] = fy } 224 } 225 if fy >= 0 { if fy < H { if fx >= 0 { if fx < W { 226 if mask[fy * W + fx] == (0 as u8) { mask[fy * W + fx] = 1 as u8; ink = ink + 1 } 227 } } } } 228 } 229 gx = gx + 1 230 } 231 gy = gy + 1 232 } 233 pen = pen + rec[TF_R_ADV] 234 } 235 i = i + 1 236 } 237 if seen == 0 { bb[CRG_B_X0] = CR_ABSENT; bb[CRG_B_Y0] = CR_ABSENT; bb[CRG_B_X1] = CR_ABSENT; bb[CRG_B_Y1] = CR_ABSENT } 238 return ink 239} 240// ink pixels two masks share 241func crg_overlap(a: *u8, b: *u8, npx: i64) -> i64 { 242 var i: i64 = 0 243 var c: i64 = 0 244 while i < npx { if a[i] != (0 as u8) { if b[i] != (0 as u8) { c = c + 1 } } i = i + 1 } 245 return c 246} 247// ink pixels of a mask inside the disk (cx, cy, rad) 248func crg_ink_over_disk(mask: *u8, W: i64, H: i64, cx: i64, cy: i64, rad: i64) -> i64 { 249 var c: i64 = 0 250 var y: i64 = cy - rad 251 while y <= cy + rad { 252 if y >= 0 { if y < H { 253 var x: i64 = cx - rad 254 while x <= cx + rad { 255 if x >= 0 { if x < W { 256 if mask[y * W + x] != (0 as u8) { 257 let dx: i64 = x - cx 258 let dy: i64 = y - cy 259 if dx * dx + dy * dy <= rad * rad { c = c + 1 } 260 } 261 } } 262 x = x + 1 263 } 264 } } 265 y = y + 1 266 } 267 return c 268} 269// 1 when the unclipped ink box leaves the frame on any side (a label with no ink has no box and is not off) 270func crg_bbox_off(bb: *i64, fr: *i64) -> i64 { 271 if bb[CRG_B_X0] == CR_ABSENT { return 0 } 272 if bb[CRG_B_X0] < fr[CR_F_X0] { return 1 } 273 if bb[CRG_B_Y0] < fr[CR_F_Y0] { return 1 } 274 if bb[CRG_B_X1] > fr[CR_F_X1] { return 1 } 275 if bb[CRG_B_Y1] > fr[CR_F_Y1] { return 1 } 276 return 0 277} 278// draw every label's ink: one mask per label, the regular face unless the label is bold and a bold face was given 279func crg_ink(r: *i64, fhr: *i64, fhb: *i64) -> i64 { 280 let nl: i64 = r[CRG_R_NL] 281 let W: i64 = r[CRG_R_W] 282 let H: i64 = r[CRG_R_H] 283 let masks: *i64 = r[CRG_R_MASKS] as *i64 284 let inkv: *i64 = r[CRG_R_INK] as *i64 285 let bbs: *i64 = r[CRG_R_BBOX] as *i64 286 let lx: *i64 = r[CRG_R_LX] as *i64 287 let ly: *i64 = r[CRG_R_LY] as *i64 288 let lfont: *i64 = r[CRG_R_LFONT] as *i64 289 let lbold: *i64 = r[CRG_R_LBOLD] as *i64 290 let names: *u8 = r[CRG_R_NAMES] as *u8 291 var total: i64 = 0 292 var i: i64 = 0 293 while i < nl { 294 let m: *u8 = sys_mmap(W * H + 1) 295 masks[i] = m as i64 296 var fh: *i64 = fhr 297 if lbold[i] == 1 { if (fhb as i64) != 0 { fh = fhb } } 298 let bb: *i64 = ((bbs as i64) + i * CRG_BBOX_W * CRG_I64_BYTES) as *i64 299 inkv[i] = crg_mask_label(fh, cr_name_at(names, i), lfont[i], lx[i], ly[i] + CR_LABEL_BASE, W, H, m, bb) 300 total = total + inkv[i] 301 i = i + 1 302 } 303 // the notes: every line of a note drawn into that note's ONE mask (regular face; the emitter never bolds a note), 304 // the note's ink box the union of its lines' boxes 305 let nn: i64 = r[CRG_R_NN] 306 let nnl: i64 = r[CRG_R_NNL] 307 let nmasks: *i64 = r[CRG_R_NMASKS] as *i64 308 let ninkv: *i64 = r[CRG_R_NINK] as *i64 309 let nlx: *i64 = r[CRG_R_NLX] as *i64 310 let nly: *i64 = r[CRG_R_NLY] as *i64 311 let nlfont: *i64 = r[CRG_R_NLFONT] as *i64 312 let nlnote: *i64 = r[CRG_R_NLNOTE] as *i64 313 let nnames: *u8 = r[CRG_R_NNAMES] as *u8 314 let tbb: *i64 = crg_i64(CRG_BBOX_W) 315 var g: i64 = 0 316 while g < nn { 317 let gm: *u8 = sys_mmap(W * H + 1) 318 nmasks[g] = gm as i64 319 ninkv[g] = 0 320 let gbb: *i64 = crg_nbbox_at(r, g) 321 gbb[CRG_B_X0] = CR_ABSENT; gbb[CRG_B_Y0] = CR_ABSENT; gbb[CRG_B_X1] = CR_ABSENT; gbb[CRG_B_Y1] = CR_ABSENT 322 var l: i64 = 0 323 while l < nnl { 324 if nlnote[l] == g { 325 ninkv[g] = ninkv[g] + crg_mask_label(fhr, cr_name_at(nnames, l), nlfont[l], nlx[l], nly[l], W, H, gm, tbb) 326 crg_bbox_union(gbb, tbb) 327 } 328 l = l + 1 329 } 330 total = total + ninkv[g] 331 g = g + 1 332 } 333 return total 334} 335// the pixel critic over the drawn record: vec = CRG_V_W slots 336func crg_vector(r: *i64, vec: *i64) -> i64 { 337 let nl: i64 = r[CRG_R_NL] 338 let W: i64 = r[CRG_R_W] 339 let H: i64 = r[CRG_R_H] 340 let masks: *i64 = r[CRG_R_MASKS] as *i64 341 let inkv: *i64 = r[CRG_R_INK] as *i64 342 let bbs: *i64 = r[CRG_R_BBOX] as *i64 343 let mx: *i64 = r[CRG_R_MX] as *i64 344 let my: *i64 = r[CRG_R_MY] as *i64 345 let mr: *i64 = r[CRG_R_MR] as *i64 346 let fr: *i64 = r[CRG_R_FRAME] as *i64 347 var k: i64 = 0 348 while k < CRG_V_W { vec[k] = 0; k = k + 1 } 349 var i: i64 = 0 350 while i < nl { 351 let mi: *u8 = masks[i] as *u8 352 var j: i64 = i + 1 353 while j < nl { 354 if crg_overlap(mi, masks[j] as *u8, W * H) > 0 { vec[CRG_V_LL] = vec[CRG_V_LL] + 1 } 355 j = j + 1 356 } 357 j = 0 358 while j < nl { 359 if j != i { if crg_ink_over_disk(mi, W, H, mx[j], my[j], mr[j]) > 0 { vec[CRG_V_LM] = vec[CRG_V_LM] + 1 } } 360 j = j + 1 361 } 362 let bb: *i64 = ((bbs as i64) + i * CRG_BBOX_W * CRG_I64_BYTES) as *i64 363 vec[CRG_V_OFF] = vec[CRG_V_OFF] + crg_bbox_off(bb, fr) 364 vec[CRG_V_INKPX] = vec[CRG_V_INKPX] + inkv[i] 365 j = i + 1 366 while j < nl { 367 let dx: i64 = mx[i] - mx[j] 368 let dy: i64 = my[i] - my[j] 369 let rr: i64 = mr[i] + mr[j] 370 if dx * dx + dy * dy < rr * rr { vec[CRG_V_MM] = vec[CRG_V_MM] + 1 } 371 j = j + 1 372 } 373 i = i + 1 374 } 375 // the notes, the reading terms only (a note's anchor is not a data point, so MM stays the marks'): a note whose ink 376 // shares a pixel with a label or another note is LL, note ink inside ANY mark is LM (a note owns no mark), a note 377 // whose ink leaves the frame is OFF -- slot for slot with the emitter's cl_critic over players and notes 378 let nn: i64 = r[CRG_R_NN] 379 let nmasks: *i64 = r[CRG_R_NMASKS] as *i64 380 let ninkv: *i64 = r[CRG_R_NINK] as *i64 381 var g: i64 = 0 382 while g < nn { 383 let gm: *u8 = nmasks[g] as *u8 384 var q: i64 = 0 385 while q < nl { 386 if crg_overlap(gm, masks[q] as *u8, W * H) > 0 { vec[CRG_V_LL] = vec[CRG_V_LL] + 1 } 387 if crg_ink_over_disk(gm, W, H, mx[q], my[q], mr[q]) > 0 { vec[CRG_V_LM] = vec[CRG_V_LM] + 1 } 388 q = q + 1 389 } 390 var g2: i64 = g + 1 391 while g2 < nn { 392 if crg_overlap(gm, nmasks[g2] as *u8, W * H) > 0 { vec[CRG_V_LL] = vec[CRG_V_LL] + 1 } 393 g2 = g2 + 1 394 } 395 vec[CRG_V_OFF] = vec[CRG_V_OFF] + crg_bbox_off(crg_nbbox_at(r, g), fr) 396 vec[CRG_V_INKPX] = vec[CRG_V_INKPX] + ninkv[g] 397 g = g + 1 398 } 399 vec[CRG_V_DEFECTS] = vec[CRG_V_LL] + vec[CRG_V_LM] + vec[CRG_V_OFF] 400 return 0 401} 402// the whole judgement: scan, draw, count. Returns CRG_OK or the named reason the ruler could not judge. 403func crg_judge(h: *u8, n: i64, fhr: *i64, fhb: *i64, r: *i64, vec: *i64) -> i64 { 404 let rc: i64 = crg_scan(h, n, r) 405 if rc != CRG_OK { return rc } 406 if (fhr as i64) == 0 { return CRG_NO_FONT } 407 let total: i64 = crg_ink(r, fhr, fhb) 408 if total == 0 { return CRG_NO_INK } 409 crg_vector(r, vec) 410 return CRG_OK 411} 412// 1 when the pixel defect count equals the published one 413func crg_agree(vec: *i64, published_defects: i64) -> i64 { 414 if published_defects == CR_ABSENT { return 0 } 415 if vec[CRG_V_DEFECTS] == published_defects { return 1 } 416 return 0 417} 418func crg_rc_name(rc: i64) -> *u8 { 419 if rc == CRG_OK { return "OK" as *u8 } 420 if rc == CRG_NO_FIGURE { return "the page carries no cdmap svg" as *u8 } 421 if rc == CRG_NO_LABELS { return "no player labels on the figure" as *u8 } 422 if rc == CRG_UNPAIRED { return "labels and marks do not pair one to one" as *u8 } 423 if rc == CRG_NO_FRAME { return "the plot frame rect is unreadable" as *u8 } 424 if rc == CRG_NO_FONT { return "the regular face could not be read" as *u8 } 425 if rc == CRG_NO_INK { return "the face drew no ink at the label sizes" as *u8 } 426 if rc == CRG_CANVAS { return "the svg viewBox is unreadable or beyond CRG_VB_MAX" as *u8 } 427 return "unknown result" as *u8 428} 429// the per-label bounding box slot 430func crg_bbox_at(r: *i64, i: i64) -> *i64 { return ((r[CRG_R_BBOX] as i64) + i * CRG_BBOX_W * CRG_I64_BYTES) as *i64 }