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 }