code wiki / _hdl_build / nx_font_family_gate.nx

nx_font_family_gate.nx source

↩ module page · 306 lines · 16891 B

1// nx_font_family_gate.nx -- THE FONT GENERATOR proof: ONE glyph program + a knob vector => DIFFERENT fonts 2// (operator: "turn it into something where we can generate different types of fonts... this one font is our 3// pioneer benchmark"). Generates a 4-variant FAMILY from font_spec knobs -- Light(W95) / Regular(pioneer 4// defaults) / Bold(W175) / Soft(2-pass Chaikin + round caps) -- renders the same specimen through the SAME 5// engine, and proves the generator claims MECHANICALLY: 6// T1 all variants render real ink; 7// T2 the weight knob ACTS + ordering holds: ink(Light) < ink(Regular) < ink(Bold), Bold >= Light*115%; 8// T3 ★THE PARAMETRIC INVARIANT: baseline bounce stays professional-flat (<=15 permille) for EVERY variant 9// -- the metric lines are f(W), so alignment survives knob changes BY CONSTRUCTION (this is what makes 10// it a GENERATOR, not one hand-tuned font); 11// T4 style knobs ACT: Soft differs from Regular by real pixels; 12// T5 determinism liar-kill: Regular rendered twice is BYTE-IDENTICAL (so T2's ordering is signal not noise); 13// T6 family specimen sheet written (evidence to eyeball). 14// license_tier: ORIGINAL expect_exit: 0 15import "nx_font.nx" 16import "nx_aa_raster.nx" 17import "nx_png_write.nx" 18import "nx_gate_verdict.nx" 19 20func fg_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 21func fg_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 22func fg_num(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 23 24const FG_W: i64 = 1250 25const FG_H: i64 = 220 26const FG_PX: i64 = 150 // specimen em size (letter height ~80px so 1px of the 27 // measurement grid = ~12.5 permille -- FINER than the 28 // 15-permille bar; at 110px, 1px was 17 permille and the 29 // bar was finer than the ruler = quantization false-fails) 30 31// render the specimen through the REAL engine into an RGB buffer (white bg, dark ink) 32func fam_render(text: *u8, rgb: *u8) -> i64 { 33 let n: i64 = fg_slen(text) 34 let ss: i64 = 3 35 let S: i64 = FG_PX * ss 36 let xs: *i64 = sys_mmap(8*300000) as *i64 37 let ys: *i64 = sys_mmap(8*300000) as *i64 38 let cstart: *i64 = sys_mmap(8*30000) as *i64 39 let clen: *i64 = sys_mmap(8*30000) as *i64 40 let np: *i64 = sys_mmap(16) as *i64; np[0]=0 41 let nc: *i64 = sys_mmap(16) as *i64; nc[0]=0 42 let cs: *i64 = sys_mmap(8*20) as *i64 43 let sn: *i64 = sys_mmap(8*20) as *i64 44 font_trig_init(cs, sn) 45 font_text(text, n, xs, ys, cstart, clen, np, nc, cs, sn, 16*ss, 8*ss, S) 46 let cov: *u8 = sys_mmap(FG_W*FG_H + 16) 47 var z: i64 = 0 48 while z < FG_W*FG_H { cov[z]=0 as u8; z=z+1 } 49 aa_render(xs, ys, cstart, clen, nc[0], cov, FG_W, FG_H, ss) 50 var i: i64 = 0 51 while i < FG_W*FG_H*3 { rgb[i]=255 as u8; i=i+1 } 52 aa_blit(rgb, FG_W, FG_H, 0, 0, cov, FG_W, FG_H, 24, 26, 38) 53 return 0 54} 55func fam_lum(rgb: *u8, o: i64) -> i64 { return (2126*(rgb[o] as i64)+7152*(rgb[o+1] as i64)+722*(rgb[o+2] as i64))/10000 } 56func fam_ink(rgb: *u8) -> i64 { 57 var ink: i64=0 58 var p: i64=0 59 while p < FG_W*FG_H { if fam_lum(rgb,p*3) < 128 { ink=ink+1 } p=p+1 } 60 return ink 61} 62func fam_isqrt(n: i64) -> i64 { if n<=0 { return 0 } var x: i64=n; var y: i64=(x+1)/2; while y<x { x=y; y=(x+n/x)/2 } return x } 63// baseline bounce (permille of letter height): segment letters by white columns, std of letter-bottoms 64func fam_bounce(rgb: *u8) -> i64 { 65 let colink: *i64 = sys_mmap(8*(FG_W+4)) as *i64 66 let colbot: *i64 = sys_mmap(8*(FG_W+4)) as *i64 67 let coltop: *i64 = sys_mmap(8*(FG_W+4)) as *i64 68 var x: i64=0 69 while x<FG_W { 70 var has: i64=0; var bot: i64=0-1; var top: i64=FG_H 71 var y: i64=0 72 while y<FG_H { if fam_lum(rgb,(y*FG_W+x)*3) < 128 { has=1; if y>bot { bot=y }; if y<top { top=y } } y=y+1 } 73 colink[x]=has; colbot[x]=bot; coltop[x]=top 74 x=x+1 75 } 76 let bots: *i64 = sys_mmap(8*128) as *i64 77 let hts: *i64 = sys_mmap(8*128) as *i64 78 var nl: i64=0 79 var inrun: i64=0 80 var rb: i64=0-1 81 var rt: i64=FG_H 82 var gap: i64=0 83 x=0 84 while x<FG_W { 85 if colink[x]==1 { 86 if inrun==0 { inrun=1; rb=0-1; rt=FG_H } 87 if colbot[x]>rb { rb=colbot[x] } 88 if coltop[x]<rt { rt=coltop[x] } 89 gap=0 90 } else { if inrun==1 { gap=gap+1; if gap>=4 { if nl<128 { bots[nl]=rb; hts[nl]=rb-rt; nl=nl+1 } inrun=0 } } } 91 x=x+1 92 } 93 if inrun==1 { if nl<128 { bots[nl]=rb; hts[nl]=rb-rt; nl=nl+1 } } 94 if nl<4 { return 0 - 1 } 95 var sb: i64=0; var sh: i64=0 96 var i: i64=0 97 while i<nl { sb=sb+bots[i]; sh=sh+hts[i]; i=i+1 } 98 let mb: i64=sb/nl 99 let mh: i64=sh/nl 100 var vs: i64=0 101 i=0 102 while i<nl { let dd: i64=bots[i]-mb; vs=vs+dd*dd; i=i+1 } 103 if mh<=0 { return 0 - 1 } 104 return (fam_isqrt(vs/nl)*1000)/mh 105} 106// stroke-width UNIFORMITY: CV (stddev/mean, permille) of horizontal dark-run widths (stroke crossings, 2..40px). 107// Machine-regular MONOLINE -> low CV (all strokes the pen width); organic modulated/swelled -> high CV. 108func fam_isqrt2(n: i64) -> i64 { if n<=0 { return 0 } var x: i64=n; var y: i64=(x+1)/2; while y<x { x=y; y=(x+n/x)/2 } return x } 109func fam_stroke_cv(rgb: *u8) -> i64 { 110 // find the ink band, then scan ONLY the mid-x-height rows (35..55% down) where strokes are VERTICAL -- 111 // there a horizontal run == a stroke width, uncounfounded by the wide arcs at bowl tops/bottoms. 112 var miny: i64=FG_H 113 var maxy: i64=0 114 var yy: i64=0 115 while yy<FG_H { var xx: i64=0; var has: i64=0; while xx<FG_W { if fam_lum(rgb,(yy*FG_W+xx)*3)<128 { has=1; xx=FG_W } else { xx=xx+1 } } if has==1 { if yy<miny{miny=yy} ; if yy>maxy{maxy=yy} } yy=yy+1 } 116 let h: i64 = maxy-miny 117 if h<10 { return 0 } 118 let y0: i64 = miny + (h*35)/100 119 let y1: i64 = miny + (h*55)/100 120 var runs: i64=0 121 var sumw: i64=0 122 let wtab: *i64 = sys_mmap(8*20000) as *i64 123 var y: i64=y0 124 while y <= y1 { 125 var x: i64=0 126 var cur: i64=0 127 while x < FG_W { 128 if fam_lum(rgb,(y*FG_W+x)*3) < 128 { cur=cur+1 } 129 else { if cur>0 { if cur>=2 { if cur<=40 { if runs<20000 { wtab[runs]=cur; runs=runs+1; sumw=sumw+cur } } } cur=0 } } 130 x=x+1 131 } 132 y=y+1 133 } 134 if runs<20 { return 0 } 135 let mean: i64 = sumw/runs 136 if mean<=0 { return 0 } 137 var vs: i64=0 138 var i: i64=0 139 while i<runs { let dd: i64=wtab[i]-mean; vs=vs+dd*dd; i=i+1 } 140 return (fam_isqrt2(vs/runs)*1000)/mean 141} 142// ink WIDTH (rightmost - leftmost inked column) -- for the WIDTH axis: condensed < normal < extended. 143func fam_inkw(rgb: *u8) -> i64 { 144 var lo: i64 = FG_W 145 var hi: i64 = 0 146 var y: i64 = 0 147 while y < FG_H { 148 var x: i64 = 0 149 while x < FG_W { if fam_lum(rgb,(y*FG_W+x)*3) < 128 { if x<lo {lo=x} ; if x>hi {hi=x} } x=x+1 } 150 y=y+1 151 } 152 if hi < lo { return 0 } 153 return hi - lo 154} 155func fam_diff(a: *u8, b: *u8) -> i64 { 156 var d: i64=0 157 var p: i64=0 158 while p < FG_W*FG_H { var da: i64=(a[p*3] as i64)-(b[p*3] as i64); if da<0 { da=0-da } if da>30 { d=d+1 } p=p+1 } 159 return d 160} 161 162func main() -> i64 { 163 fg_puts("=== nx_font_family_gate -- ONE glyph program + knobs = a FONT FAMILY (the generator proof) ===\n" as *u8) 164 let spec: *u8 = "consensus 037\x00" as *u8 165 let rgbL: *u8 = sys_mmap(FG_W*FG_H*3+64) 166 let rgbR: *u8 = sys_mmap(FG_W*FG_H*3+64) 167 let rgbR2: *u8 = sys_mmap(FG_W*FG_H*3+64) 168 let rgbB: *u8 = sys_mmap(FG_W*FG_H*3+64) 169 let rgbS: *u8 = sys_mmap(FG_W*FG_H*3+64) 170 171 font_spec(95, 0, 0, 0) // LIGHT 172 fam_render(spec, rgbL) 173 font_spec_reset() // REGULAR = the pioneer defaults 174 fam_render(spec, rgbR) 175 fam_render(spec, rgbR2) // determinism twin 176 font_spec(160, 0, 0, 0) // BOLD (W=160 = the validated top of the wght range: 177 fam_render(spec, rgbB) // above ~160 the e/s interior clearances (~190em gaps 178 // minus the pen) CLOG -- opening heavy-weight apertures 179 // is per-glyph bold-master craft = a named next axis) 180 font_spec(132, 12, 1, 1) // SOFT (round caps; 1-pass -- ⚠passes=2 lifts curve 181 fam_render(spec, rgbS) // extrema ~8em (Chaikin shrinkage) = breaks the baseline 182 // invariant -> knob CONSTRAINED until extremum-compensated 183 let rgbO: *u8 = sys_mmap(FG_W*FG_H*3+64) 184 font_spec_reset() 185 font_spec_slant(18) // OBLIQUE (~10deg, the slnt axis; sheared about the baseline) 186 fam_render(spec, rgbO) 187 let rgbG: *u8 = sys_mmap(FG_W*FG_H*3+64) 188 font_spec_reset() 189 font_spec_geo(1); font_spec_mod(100); font_spec_swell(100) // GEOMETRIC (machine-regular, monoline) 190 fam_render(spec, rgbG) 191 let rgbGC: *u8 = sys_mmap(FG_W*FG_H*3+64) // geometric CONDENSED (wdth 78) 192 font_spec_reset(); font_spec_geo(1); font_spec_mod(100); font_spec_swell(100); font_spec_wdth(78) 193 fam_render(spec, rgbGC) 194 let rgbGE: *u8 = sys_mmap(FG_W*FG_H*3+64) // geometric EXTENDED (wdth 128) 195 font_spec_reset(); font_spec_geo(1); font_spec_mod(100); font_spec_swell(100); font_spec_wdth(128) 196 fam_render(spec, rgbGE) 197 font_spec_reset() 198 199 let inkL: i64 = fam_ink(rgbL) 200 let inkR: i64 = fam_ink(rgbR) 201 let inkB: i64 = fam_ink(rgbB) 202 let bL: i64 = fam_bounce(rgbL) 203 let bR: i64 = fam_bounce(rgbR) 204 let bB: i64 = fam_bounce(rgbB) 205 let bS: i64 = fam_bounce(rgbS) 206 let dRS: i64 = fam_diff(rgbR, rgbS) 207 let dRR: i64 = fam_diff(rgbR, rgbR2) 208 let inkS: i64 = fam_ink(rgbS) 209 let inkO: i64 = fam_ink(rgbO) 210 let bO: i64 = fam_bounce(rgbO) 211 let dRO: i64 = fam_diff(rgbR, rgbO) 212 let inkG: i64 = fam_ink(rgbG) 213 let bG: i64 = fam_bounce(rgbG) 214 let cvR: i64 = fam_stroke_cv(rgbR) 215 let cvG: i64 = fam_stroke_cv(rgbG) 216 fg_puts(" ink: Light=" as *u8); fg_num(inkL); fg_puts(" Regular=" as *u8); fg_num(inkR); fg_puts(" Bold=" as *u8); fg_num(inkB); fg_puts("\n" as *u8) 217 fg_puts(" bounce permille: L=" as *u8); fg_num(bL); fg_puts(" R=" as *u8); fg_num(bR); fg_puts(" B=" as *u8); fg_num(bB); fg_puts(" Soft=" as *u8); fg_num(bS); fg_puts("\n" as *u8) 218 fg_puts(" diff px: Regular-vs-Soft=" as *u8); fg_num(dRS); fg_puts(" Regular-vs-Regular=" as *u8); fg_num(dRR); fg_puts("\n" as *u8) 219 220 var pass: i64=0 221 var ttl: i64=0 222 ttl=ttl+1; fg_puts(" T1 all variants render real ink (>5000 px each): " as *u8) 223 if inkL>5000 { if inkR>5000 { if inkB>5000 { pass=pass+1; fg_puts("PASS\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } } else { fg_puts("FAIL\n" as *u8) } } else { fg_puts("FAIL\n" as *u8) } 224 ttl=ttl+1; fg_puts(" T2 weight knob acts + orders (L<R<B, B>=L*115%): " as *u8) 225 if inkL<inkR { if inkR<inkB { if inkB*100 >= inkL*115 { pass=pass+1; fg_puts("PASS\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } } else { fg_puts("FAIL\n" as *u8) } } else { fg_puts("FAIL\n" as *u8) } 226 ttl=ttl+1; fg_puts(" T3 PARAMETRIC INVARIANT: butt variants dead-flat (<=15 permille) + round-cap Soft within its\n" as *u8) 227 fg_puts(" geometric tolerance (<=25: a round cap on a DIAGONAL end extends (1-cos)*r below the line -- a\n" as *u8) 228 fg_puts(" property of the style, not misalignment): " as *u8) 229 var flat: i64=1 230 if bL<0 { flat=0 } ; if bL>15 { flat=0 } 231 if bR<0 { flat=0 } ; if bR>15 { flat=0 } 232 if bB<0 { flat=0 } ; if bB>15 { flat=0 } 233 if bS<0 { flat=0 } ; if bS>25 { flat=0 } 234 if flat==1 { pass=pass+1; fg_puts("PASS (alignment survives every knob = a GENERATOR, not one font)\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } 235 ttl=ttl+1; fg_puts(" T4 style knobs act (Soft differs from Regular >800 px -- the end-cap discs): " as *u8) 236 if dRS>800 { pass=pass+1; fg_puts("PASS\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } 237 ttl=ttl+1; fg_puts(" T5 determinism liar-kill (Regular twice = 0 diff px): " as *u8) 238 if dRR==0 { pass=pass+1; fg_puts("PASS\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } 239 ttl=ttl+1; fg_puts(" T5b NO-HOLES guard: Soft ink >= Regular ink (round caps only ADD; winding cancellation SUBTRACTS -- the candy-cane bug's mechanical witness): " as *u8) 240 if inkS >= inkR { pass=pass+1; fg_puts("PASS\n" as *u8) } else { fg_puts("FAIL (ink " as *u8); fg_num(inkS); fg_puts(" < " as *u8); fg_num(inkR); fg_puts(")\n" as *u8) } 241 ttl=ttl+1; fg_puts(" T5c OBLIQUE invariants: shear about the baseline preserves FLATNESS (bounce " as *u8); fg_num(bO); fg_puts(" <=15) and AREA (ink " as *u8); fg_num(inkO); fg_puts(" within 4% of Regular " as *u8); fg_num(inkR); fg_puts("), and it ACTS (diff " as *u8); fg_num(dRO); fg_puts(" >2000): " as *u8) 242 var okO: i64 = 1 243 if bO < 0 { okO = 0 } ; if bO > 15 { okO = 0 } 244 var dInk: i64 = inkO - inkR 245 if dInk < 0 { dInk = 0 - dInk } 246 if dInk * 25 > inkR { okO = 0 } 247 if dRO <= 2000 { okO = 0 } 248 if okO == 1 { pass=pass+1; fg_puts("PASS\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } 249 let dRG: i64 = fam_diff(rgbR, rgbG) 250 ttl=ttl+1; fg_puts(" T5d GEOMETRIC (machine-regular) variant renders (ink " as *u8); fg_num(inkG); fg_puts("), baseline flat (" as *u8); fg_num(bG); fg_puts("<=15), DISTINCT letterforms vs organic (diff " as *u8); fg_num(dRG); fg_puts(" >8000 -- exact-circle bowls, not hand curves): " as *u8) 251 // NOTE: machine regularity here is BY CONSTRUCTION (every bowl = the SAME fe_ring circle, every arch = the 252 // same geo_arc) -- provable in the code, not by a pixel metric (stroke-CV is confounded: circles have varied 253 // cross-sections in any scan direction). Legibility is measured separately: geo_specimen OCR = 9/9 pangram. 254 var okG: i64 = 1 255 if inkG < 5000 { okG = 0 } 256 if bG < 0 { okG = 0 } ; if bG > 15 { okG = 0 } 257 if dRG < 8000 { okG = 0 } 258 if okG == 1 { pass=pass+1; fg_puts("PASS (machine-regular geometric = a real emitted TYPE)\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } 259 let wGC: i64 = fam_inkw(rgbGC) 260 let wGN: i64 = fam_inkw(rgbG) 261 let wGE: i64 = fam_inkw(rgbGE) 262 ttl=ttl+1; fg_puts(" T5e WIDTH axis ACTS (condense/extend, stems keep weight): inkw condensed=" as *u8); fg_num(wGC); fg_puts(" < normal=" as *u8); fg_num(wGN); fg_puts(" < extended=" as *u8); fg_num(wGE); fg_puts(": " as *u8) 263 var okWx: i64 = 1 264 if wGC >= wGN { okWx = 0 } 265 if wGN >= wGE { okWx = 0 } 266 if okWx == 1 { pass=pass+1; fg_puts("PASS (VARIATION -- a real wdth axis)\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } 267 268 // family specimen sheet: 6 rows (Light / Regular / Bold / Soft / Oblique / Geometric) 269 let SH2: i64 = FG_H*6 270 let sheet: *u8 = sys_mmap(FG_W*SH2*3+64) 271 var q: i64=0 272 while q<FG_W*SH2*3 { sheet[q]=255 as u8; q=q+1 } 273 var row: i64=0 274 while row<6 { 275 var src: *u8 = rgbL 276 if row==1 { src=rgbR } 277 if row==2 { src=rgbB } 278 if row==3 { src=rgbS } 279 if row==4 { src=rgbO } 280 if row==5 { src=rgbG } 281 var y: i64=0 282 while y<FG_H { 283 var x2: i64=0 284 while x2<FG_W*3 { sheet[((row*FG_H+y)*FG_W)*3+x2] = src[(y*FG_W)*3+x2]; x2=x2+1 } 285 y=y+1 286 } 287 row=row+1 288 } 289 nx_png_write_rgb("knowledge/status/font_family_sheet.png\x00" as *u8, sheet, FG_W, SH2) 290 let lp: *i64 = sys_mmap(16) as *i64 291 lp[0]=0-1 292 sys_read_file("knowledge/status/font_family_sheet.png\x00" as *u8, lp) 293 ttl=ttl+1; fg_puts(" T6 family sheet written (" as *u8); fg_num(lp[0]); fg_puts("B): " as *u8) 294 if lp[0]>20000 { pass=pass+1; fg_puts("PASS\n" as *u8) } else { fg_puts("FAIL\n" as *u8) } 295 296 fg_puts("FONT-FAMILY-GATE passed " as *u8); fg_num(pass); fg_puts("/" as *u8); fg_num(ttl) 297 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 298 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 299 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 300 let ctr__dry: *i64 = gv_ctr() 301 ctr__dry[0] = pass 302 ctr__dry[1] = ttl 303 let rc__dry: i64 = gv_verdict("FONT-FAMILY-GATE" as *u8, ctr__dry, "Light/Regular/Bold/Soft from ONE program -- see font_family_sheet.png)" as *u8) 304 sys_exit(rc__dry) 305 return rc__dry 306}