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}