code wiki / _hdl_build / nx_curve_smoothness_census.nx
nx_curve_smoothness_census.nx source
↩ module page · 226 lines · 12306 B
1// nx_curve_smoothness_census.nx -- a WEIGHT-INVARIANT smoothness metric that actually isolates FACETING
2// (the thing edge-complexity confounded). Insight: faceting is about the CONTOUR'S TURNING, not its
3// thickness. A smooth curve turns UNIFORMLY (a circle's curvature is constant); a faceted polygon
4// concentrates all its turning into a few sharp vertices. So along the traced outer contour:
5// facet_index = n * Sum(T_i^2) / (Sum|T_i|)^2 where T_i = per-step turning of the tangent
6// For a perfectly-uniform (smooth) contour this = 1.0; for turning concentrated at k corners it -> ~n/k.
7// Scale-free, weight-free, trig-free (turning via integer cross-product of normalized tangent vectors).
8// SELF-VALIDATING LIAR-KILL: a synthetic CIRCLE must read near 1 (smooth) and a HEXAGON must read MUCH
9// higher (6 corners) -- if the metric can't tell a circle from a hexagon it declares ITSELF broken. Only
10// then does it report our "O" vs Chrome's "O". license_tier: ORIGINAL expect_exit: 0
11import "nx_img_bytes_to_rgb.nx"
12const K_MAGIC_2126: i64 = 2126
13const K_MAGIC_7152: i64 = 7152
14const K_MAGIC_10000: i64 = 10000
15const K_MAGIC_1024: i64 = 1024
16const K_MAGIC_40000: i64 = 40000
17
18func cs_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
19func cs_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 }
20func cs_lum(rgb: *u8, o: i64) -> i64 { return (K_MAGIC_2126*(rgb[o] as i64)+K_MAGIC_7152*(rgb[o+1] as i64)+722*(rgb[o+2] as i64))/K_MAGIC_10000 }
21func cs_ink(rgb: *u8, w: i64, h: i64, x: i64, y: i64) -> i64 {
22 if x<0 { return 0 } ; if y<0 { return 0 } ; if x>=w { return 0 } ; if y>=h { return 0 }
23 if cs_lum(rgb,(y*w+x)*3) < 128 { return 1 }
24 return 0
25}
26func cs_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 }
27
28// synthetic shapes rasterized into an RGB buffer (white bg, dark shape) -- same path as a decoded PNG.
29func cs_fill_white(rgb: *u8, w: i64, h: i64) -> i64 { var i: i64=0; while i<w*h*3 { rgb[i]=255 as u8; i=i+1 } return 0 }
30func cs_dot(rgb: *u8, w: i64, x: i64, y: i64) -> i64 { let o: i64=(y*w+x)*3; rgb[o]=20 as u8; rgb[o+1]=20 as u8; rgb[o+2]=43 as u8; return 0 }
31func cs_make_circle(rgb: *u8, w: i64, h: i64, cx: i64, cy: i64, r: i64) -> i64 {
32 cs_fill_white(rgb,w,h)
33 var y: i64=0
34 while y<h { var x: i64=0; while x<w { let dx: i64=x-cx; let dy: i64=y-cy; if dx*dx+dy*dy <= r*r { cs_dot(rgb,w,x,y) } x=x+1 } y=y+1 }
35 return 0
36}
37// regular hexagon via 6 half-plane tests (point inside iff inside all 6 edges)
38func cs_make_hexagon(rgb: *u8, w: i64, h: i64, cx: i64, cy: i64, r: i64, vx: *i64, vy: *i64) -> i64 {
39 cs_fill_white(rgb,w,h)
40 // 6 vertices (cos/sin at 0,60,...300 deg, x1000): (1000,0)(500,866)(-500,866)(-1000,0)(-500,-866)(500,-866)
41 let cxx: *i64 = sys_mmap(8*8) as *i64
42 let cyy: *i64 = sys_mmap(8*8) as *i64
43 cxx[0]=1000; cyy[0]=0 ; cxx[1]=500; cyy[1]=866 ; cxx[2]=0-500; cyy[2]=866
44 cxx[3]=0-1000; cyy[3]=0 ; cxx[4]=0-500; cyy[4]=0-866 ; cxx[5]=500; cyy[5]=0-866
45 var k: i64=0
46 while k<6 { vx[k]=cx+(r*cxx[k])/1000; vy[k]=cy+(r*cyy[k])/1000; k=k+1 }
47 var y: i64=0
48 while y<h {
49 var x: i64=0
50 while x<w {
51 var inside: i64=1
52 var e: i64=0
53 while e<6 {
54 let a: i64=e; let bb: i64=(e+1)%6
55 let cr: i64=(vx[bb]-vx[a])*(y-vy[a]) - (vy[bb]-vy[a])*(x-vx[a]) // cross: sign tells side
56 if cr < 0 { inside=0; e=6 } else { e=e+1 }
57 }
58 if inside==1 { cs_dot(rgb,w,x,y) }
59 x=x+1
60 }
61 y=y+1
62 }
63 return 0
64}
65
66// Moore-neighbour boundary trace (clockwise) of the outer contour containing the first ink pixel.
67// Fills px[],py[]; returns point count n (or 0 if none / overflow).
68func cs_trace(rgb: *u8, w: i64, h: i64, px: *i64, py: *i64, maxn: i64) -> i64 {
69 // find start = first ink pixel in raster order
70 var sx: i64=0 - 1
71 var sy: i64=0
72 var yy: i64=0
73 while yy<h { var xx: i64=0; while xx<w { if cs_ink(rgb,w,h,xx,yy)==1 { sx=xx; sy=yy; xx=w; yy=h } else { xx=xx+1 } } if sx<0 { yy=yy+1 } }
74 if sx<0 { return 0 }
75 // 8-neighbour offsets, clockwise starting from E
76 let ox: *i64 = sys_mmap(8*8) as *i64
77 let oy: *i64 = sys_mmap(8*8) as *i64
78 ox[0]=1;oy[0]=0 ; ox[1]=1;oy[1]=1 ; ox[2]=0;oy[2]=1 ; ox[3]=0-1;oy[3]=1
79 ox[4]=0-1;oy[4]=0 ; ox[5]=0-1;oy[5]=0-1 ; ox[6]=0;oy[6]=0-1 ; ox[7]=1;oy[7]=0-1
80 var cx: i64=sx
81 var cy: i64=sy
82 var n: i64=0
83 var backdir: i64=4 // came from the west of the start
84 var guard: i64=0
85 var run: i64=1
86 while run==1 {
87 px[n]=cx; py[n]=cy; n=n+1
88 if n>=maxn { return n }
89 // search neighbours clockwise starting just after the back-direction
90 var found: i64=0
91 var s: i64=0
92 while s<8 {
93 let d: i64=(backdir+1+s)%8
94 let nx: i64=cx+ox[d]; let ny: i64=cy+oy[d]
95 if cs_ink(rgb,w,h,nx,ny)==1 {
96 backdir=(d+4)%8 // we entered new pixel from opposite side
97 cx=nx; cy=ny; found=1; s=8
98 } else { s=s+1 }
99 }
100 if found==0 { run=0 } // isolated pixel
101 else { if cx==sx { if cy==sy { run=0 } } } // returned to start
102 guard=guard+1
103 if guard > maxn { run=0 }
104 }
105 return n
106}
107
108// facet_index x1000 : n * Sum(T^2) / (Sum|T|)^2, times 1000. window w = tangent half-span (px).
109func cs_facet(px: *i64, py: *i64, n: i64, w: i64) -> i64 {
110 if n < 4*w+4 { return 0 }
111 let ndx: *i64 = sys_mmap(8*(n+8)) as *i64
112 let ndy: *i64 = sys_mmap(8*(n+8)) as *i64
113 let MAG: i64 = K_MAGIC_1024
114 var i: i64=0
115 while i<n {
116 let ia: i64=(i+w)%n
117 let ib: i64=(i-w+n)%n
118 let dx: i64=px[ia]-px[ib]
119 let dy: i64=py[ia]-py[ib]
120 let mag: i64=cs_isqrt(dx*dx+dy*dy)
121 if mag>0 { ndx[i]=(dx*MAG)/mag; ndy[i]=(dy*MAG)/mag } else { ndx[i]=0; ndy[i]=0 }
122 i=i+1
123 }
124 var sumAbs: i64=0
125 var sumSq: i64=0
126 i=0
127 while i<n {
128 let j: i64=(i+1)%n
129 var t: i64=(ndx[i]*ndy[j] - ndy[i]*ndx[j])/MAG // ~ MAG*sin(turn)
130 var ta: i64=t
131 if ta<0 { ta=0-ta }
132 sumAbs=sumAbs+ta
133 sumSq=sumSq+ta*ta // T^2 (abs^2 == t^2)
134 i=i+1
135 }
136 if sumAbs<=0 { return 0 }
137 return (n * sumSq * 1000) / (sumAbs * sumAbs)
138}
139
140func cs_load(path: *u8, wh: *i64) -> *u8 {
141 let lp: *i64 = sys_mmap(16) as *i64
142 lp[0]=0-1
143 let raw: *u8 = sys_read_file(path, lp)
144 if lp[0]<=0 { return 0 as *u8 }
145 return nx_img_bytes_to_rgb(raw, lp[0], wh)
146}
147func cs_measure_png(path: *u8) -> i64 {
148 let wh: *i64 = sys_mmap(16) as *i64
149 let rgb: *u8 = cs_load(path, wh)
150 if rgb==(0 as *u8) { return 0 - 1 }
151 let px: *i64 = sys_mmap(8*K_MAGIC_40000) as *i64
152 let py: *i64 = sys_mmap(8*K_MAGIC_40000) as *i64
153 let n: i64 = cs_trace(rgb, wh[0], wh[1], px, py, K_MAGIC_40000)
154 if n<40 { return 0 - 2 }
155 return cs_facet(px, py, n, 6)
156}
157
158func main() -> i64 {
159 cs_puts("=== nx_curve_smoothness_census -- weight-invariant FACETING metric (contour turning) ===\n" as *u8)
160 let W: i64=400
161 let H: i64=400
162 let rgb: *u8 = sys_mmap(W*H*3+64)
163 let px: *i64 = sys_mmap(8*K_MAGIC_40000) as *i64
164 let py: *i64 = sys_mmap(8*K_MAGIC_40000) as *i64
165 let vx: *i64 = sys_mmap(8*8) as *i64
166 let vy: *i64 = sys_mmap(8*8) as *i64
167
168 // ---- LIAR-KILL: circle (smooth floor) vs hexagon (6 corners) ----
169 cs_make_circle(rgb, W, H, 200, 200, 150)
170 let nc: i64 = cs_trace(rgb, W, H, px, py, K_MAGIC_40000)
171 let circ: i64 = cs_facet(px, py, nc, 6)
172 cs_make_hexagon(rgb, W, H, 200, 200, 160, vx, vy)
173 let nh: i64 = cs_trace(rgb, W, H, px, py, K_MAGIC_40000)
174 let hexf: i64 = cs_facet(px, py, nh, 6)
175 cs_puts(" synthetic CIRCLE facet_index(x1000)=" as *u8); cs_num(circ); cs_puts(" (smooth floor) contour pts=" as *u8); cs_num(nc); cs_puts("\n" as *u8)
176 cs_puts(" synthetic HEXAGON facet_index(x1000)=" as *u8); cs_num(hexf); cs_puts(" (6 corners) contour pts=" as *u8); cs_num(nh); cs_puts("\n" as *u8)
177 var metric_ok: i64=0
178 if hexf > circ*2 { metric_ok=1 } // hexagon MUST read at least 2x rougher than a circle
179 if metric_ok==0 {
180 cs_puts(" LIAR-KILL FIRED: the metric cannot tell a circle from a hexagon -> INVALID\n" as *u8)
181 cs_puts("NX-CURVE-SMOOTHNESS: BROKEN\n" as *u8); sys_exit(1); return 1
182 }
183 cs_puts(" metric liar-kill: PASS (hexagon reads >2x rougher than circle -> the metric isolates faceting)\n" as *u8)
184
185 // ---- the real question: our "O" vs Chrome's "O" (vs the circle floor) ----
186 let oursO: i64 = cs_measure_png("knowledge/status/typeq_O_ours.png\x00" as *u8)
187 let chrO: i64 = cs_measure_png("knowledge/status/typeq_O_chrome.png\x00" as *u8)
188 cs_puts(" our 'O' facet_index(x1000)=" as *u8); cs_num(oursO); cs_puts("\n" as *u8)
189 cs_puts(" chrome 'O' facet_index(x1000)=" as *u8); cs_num(chrO); cs_puts("\n" as *u8)
190 var pass: i64=0
191 var ttl: i64=0
192 ttl=ttl+1; if metric_ok==1 { pass=pass+1 }
193 ttl=ttl+1; cs_puts(" C2 both O's measured: " as *u8); if oursO>0 { if chrO>0 { pass=pass+1; cs_puts("PASS\n" as *u8) } else { cs_puts("FAIL\n" as *u8) } } else { cs_puts("FAIL\n" as *u8) }
194 // The RIGHT reference for "is it faceted" is the CIRCLE FLOOR (+ HEXAGON ceiling), NOT Chrome -- because a
195 // professional font is intentionally MODULATED (Arial's O isn't a pure circle), so scoring nearer the circle
196 // than Chrome means "more circular / blander", NOT "better". So we ground ONLY the faceting claim here.
197 if oursO>0 {
198 let above_floor: i64 = ((oursO - circ) * 1000) / circ // how far our O sits above the smooth floor
199 let span: i64 = hexf - circ
200 let toward_hex: i64 = ((oursO - circ) * 1000) / span // 0 = at circle floor, 1000 = at hexagon
201 cs_puts(" >>> FACETING: our O sits " as *u8); cs_num(above_floor); cs_puts(" permille above the CIRCLE floor, " as *u8); cs_num(toward_hex); cs_puts(" permille of the way to the HEXAGON ceiling\n" as *u8)
202 if toward_hex < 100 { cs_puts(" GROUNDED: our O is at the smooth-circle floor (<10% toward faceted) -> FACETING ELIMINATED (Chaikin worked), MEASURED weight-invariantly.\n" as *u8) }
203 else { cs_puts(" HONEST: our O is measurably faceted (well above the circle floor) -> curves NOT yet smooth.\n" as *u8) }
204 }
205 if oursO>0 { if chrO>0 {
206 var rr: i64=0
207 rr=(oursO*1000)/chrO
208 cs_puts(" ~ vs Chrome's O = " as *u8); cs_num(rr); cs_puts(" permille -- ★NOT a quality verdict: Chrome's Arial O is intentionally MODULATED (design), so 'more circular than Chrome' != 'better'. Eyeball: Chrome's O is the better-drawn letter (bolder, modulated); this metric ONLY proves faceting.\n" as *u8)
209 } }
210 // benchmark log line (composed by nx_font_pioneer_benchmark): faceting = permille toward the hexagon
211 if oursO>0 {
212 let span2: i64 = hexf - circ
213 var th: i64 = 0
214 if span2>0 { th = ((oursO - circ) * 1000) / span2 }
215 if th<0 { th=0 }
216 let lg: *u8 = sys_mmap(96); var lo: i64=0
217 let pfx: *u8 = "FONT-FACET toward_hex=\x00" as *u8; while pfx[lo]!=(0 as u8) { lg[lo]=pfx[lo]; lo=lo+1 }
218 let t: *u8=sys_mmap(24); var k: i64=0; var m: i64=th; 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 z: i64=0; while z<k{lg[lo]=t[k-1-z];lo=lo+1;z=z+1}
219 lg[lo]=10 as u8; lo=lo+1
220 let fd: i64=sys_openat_wr("knowledge/status/font_facet.log\x00" as *u8, 0x1a4)
221 if fd>0 { sys_write(fd, lg, lo); sys_close(fd) }
222 }
223 cs_puts("CURVE-SMOOTHNESS-CENSUS-GATE passed " as *u8); cs_num(pass); cs_puts("/" as *u8); cs_num(ttl)
224 if pass==ttl { cs_puts(" verdict=GREEN (faceting MEASURED weight-invariantly, liar-killed)\n" as *u8); sys_exit(0); return 0 }
225 cs_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1
226}