code wiki / (root) / nx_ocr_assemble.nx

nx_ocr_assemble.nx source

↩ module page · 85 lines · 3410 B

1// nx_ocr_assemble.nx -- R5 of the sovereign scanning stack: recognized glyphs -> text. Groups glyphs into lines by 2// vertical overlap, orders each line left-to-right, inserts a space where the horizontal gap is wide, and breaks 3// lines top-to-bottom -> a plain text string. That text is what feeds nx_money_ledger extraction -> money-OS, 4// closing the loop: scanned document -> recognized text -> the money system. Integer-exact, zero deps. Each glyph 5// is [minx, miny, maxx, maxy, char]. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7 8const GLYPH_MINX: i64 = 0 9const GLYPH_MINY: i64 = 1 10const GLYPH_MAXX: i64 = 2 11const GLYPH_MAXY: i64 = 3 12const GLYPH_CHAR: i64 = 4 13const GLYPH_FIELDS: i64 = 5 14 15// assemble glyphs into text in out[]; a horizontal gap > space_gap inserts a space. Returns text length. 16func asm_assemble(glyphs: *i64, ng: i64, space_gap: i64, out: *u8) -> i64 { 17 if ng <= 0 { return 0 } 18 let idx: *i64 = sys_mmap((ng + 1) * 8) as *i64 19 var i: i64 = 0 20 while i < ng { idx[i] = i; i = i + 1 } 21 22 // 1. insertion sort idx by miny (top -> bottom), stable 23 var a: i64 = 1 24 while a < ng { 25 let key: i64 = idx[a] 26 let kv: i64 = glyphs[key * GLYPH_FIELDS + GLYPH_MINY] 27 var j: i64 = a - 1 28 var run: i64 = 1 29 while run == 1 { 30 if j < 0 { run = 0 } 31 else { if glyphs[idx[j] * GLYPH_FIELDS + GLYPH_MINY] > kv { idx[j+1] = idx[j]; j = j - 1 } else { run = 0 } } 32 } 33 idx[j+1] = key 34 a = a + 1 35 } 36 37 var o: i64 = 0 38 var g: i64 = 0 39 while g < ng { 40 // 2. gather this line: consecutive (in miny order) glyphs whose miny <= the line's running bottom 41 var line_maxy: i64 = glyphs[idx[g] * GLYPH_FIELDS + GLYPH_MAXY] 42 var g2: i64 = g + 1 43 var run2: i64 = 1 44 while run2 == 1 { 45 if g2 >= ng { run2 = 0 } 46 else { 47 if glyphs[idx[g2] * GLYPH_FIELDS + GLYPH_MINY] <= line_maxy { 48 if glyphs[idx[g2] * GLYPH_FIELDS + GLYPH_MAXY] > line_maxy { line_maxy = glyphs[idx[g2] * GLYPH_FIELDS + GLYPH_MAXY] } 49 g2 = g2 + 1 50 } else { run2 = 0 } 51 } 52 } 53 // 3. insertion sort idx[g..g2) by minx (left -> right) 54 var b: i64 = g + 1 55 while b < g2 { 56 let key2: i64 = idx[b] 57 let kv2: i64 = glyphs[key2 * GLYPH_FIELDS + GLYPH_MINX] 58 var j2: i64 = b - 1 59 var run3: i64 = 1 60 while run3 == 1 { 61 if j2 < g { run3 = 0 } 62 else { if glyphs[idx[j2] * GLYPH_FIELDS + GLYPH_MINX] > kv2 { idx[j2+1] = idx[j2]; j2 = j2 - 1 } else { run3 = 0 } } 63 } 64 idx[j2+1] = key2 65 b = b + 1 66 } 67 // 4. emit the line, inserting spaces on wide gaps 68 var k: i64 = g 69 var prev_maxx: i64 = 0 - 1 70 while k < g2 { 71 let gi: i64 = idx[k] 72 if prev_maxx >= 0 { 73 let gap: i64 = glyphs[gi * GLYPH_FIELDS + GLYPH_MINX] - prev_maxx 74 if gap > space_gap { out[o] = 32 as u8; o = o + 1 } 75 } 76 out[o] = glyphs[gi * GLYPH_FIELDS + GLYPH_CHAR] as u8 77 o = o + 1 78 prev_maxx = glyphs[gi * GLYPH_FIELDS + GLYPH_MAXX] 79 k = k + 1 80 } 81 if g2 < ng { out[o] = 10 as u8; o = o + 1 } // newline between lines 82 g = g2 83 } 84 return o 85}