code wiki / _hdl_build / nx_cube3d_spin_gate.nx

nx_cube3d_spin_gate.nx source

↩ module page · 162 lines · 7499 B

1// nx_cube3d_spin_gate.nx -- ANIMATED 3D: the cube SPINS. Adds an integer sin/cos primitive (Bhaskara Q10) so 2// the rotation is a function of TIME, then renders 6 successive Y-rotation angles as a FILMSTRIP (6 cubes side 3// by side) -- the "video" axis for 3D scenes, all depth-sorted. Proves motion + reusable trig for every later 4// animation. Composes nx_depth_tri. license_tier: ORIGINAL expect_exit: 0 5import "nx_syscalls.nx" 6import "nx_image.nx" 7import "nx_depth_tri.nx" 8import "nx_png_write.nx" 9 10func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 11func pn(v: i64) -> i64 { let t: *u8=sys_mmap(24); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} 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} let b: *u8=sys_mmap(24); var j: i64=0; while j<k{b[j]=t[k-1-j];j=j+1} sys_write(1,b,k); return 0 } 12 13// integer sine, Q10 (scaled by 1024). Bhaskara I approximation: sin(d) ~= 4d(180-d)/(40500 - d(180-d)) on [0,180]. 14func isin(deg: i64) -> i64 { 15 var d: i64 = deg % 360 16 if d < 0 { d = d + 360 } 17 var sign: i64 = 1 18 if d >= 180 { sign = 0 - 1; d = d - 180 } 19 let hh: i64 = d * (180 - d) 20 return sign * (4 * hh * 1024 / (40500 - hh)) 21} 22func icos(deg: i64) -> i64 { return isin(deg + 90) } 23 24// rotate (Y by ay, X by ax) + perspective project, centered at (cx,cy). out[0]=sx out[1]=sy out[2]=depth 25func projA(vx: i64, vy: i64, vz: i64, ay: i64, ax: i64, cx: i64, cy: i64, out: *i64) -> i64 { 26 let SA: i64 = isin(ay) 27 let CA: i64 = icos(ay) 28 let SB: i64 = isin(ax) 29 let CB: i64 = icos(ax) 30 let x1: i64 = (vx * CA + vz * SA) / 1024 31 let z1: i64 = (0 - (vx * SA) + vz * CA) / 1024 32 let y2: i64 = (vy * CB - z1 * SB) / 1024 33 let z2: i64 = (vy * SB + z1 * CB) / 1024 34 let zc: i64 = z2 + 560 35 out[0] = cx + (x1 * 330) / zc 36 out[1] = cy - (y2 * 330) / zc 37 out[2] = zc 38 return 0 39} 40 41func face(fb: *Image, zb: *i64, px: *i64, py: *i64, pz: *i64, ia: i64, ib: i64, ic: i64, id: i64, r: i64, g: i64, b: i64) -> i64 { 42 depth_tri_render(fb, zb, px[ia], py[ia], pz[ia], px[ib], py[ib], pz[ib], px[ic], py[ic], pz[ic], r, g, b, 1) 43 depth_tri_render(fb, zb, px[ia], py[ia], pz[ia], px[ic], py[ic], pz[ic], px[id], py[id], pz[id], r, g, b, 1) 44 return 0 45} 46 47// render one cube at Y-angle `ay`, centered (cx,cy), into the shared fb/zbuf 48func draw_cube(fb: *Image, zb: *i64, cvx: *i64, cvy: *i64, cvz: *i64, px: *i64, py: *i64, pz: *i64, o: *i64, ay: i64, cx: i64, cy: i64) -> i64 { 49 var i: i64 = 0 50 while i < 8 { projA(cvx[i], cvy[i], cvz[i], ay, 25, cx, cy, o); px[i]=o[0]; py[i]=o[1]; pz[i]=o[2]; i = i + 1 } 51 face(fb, zb, px, py, pz, 4, 5, 6, 7, 0, 0, 255) // +Z blue 52 face(fb, zb, px, py, pz, 0, 1, 2, 3, 235, 235, 0) // -Z yellow 53 face(fb, zb, px, py, pz, 1, 5, 6, 2, 255, 0, 0) // +X red 54 face(fb, zb, px, py, pz, 0, 4, 7, 3, 0, 235, 235) // -X cyan 55 face(fb, zb, px, py, pz, 3, 2, 6, 7, 0, 235, 0) // +Y green 56 face(fb, zb, px, py, pz, 0, 1, 5, 4, 235, 0, 235) // -Y magenta 57 return 0 58} 59 60// count differing bytes between two CELL-wide vertical slices at x=xa and x=xb 61func cell_diff(fb: *Image, xa: i64, xb: i64, cw: i64, h: i64) -> i64 { 62 let p: *u8 = fb.pixels 63 var d: i64 = 0 64 var y: i64 = 0 65 while y < h { 66 var x: i64 = 0 67 while x < cw { 68 let oa: i64 = y * fb.stride + (xa + x) * 3 69 let ob: i64 = y * fb.stride + (xb + x) * 3 70 if p[oa] != p[ob] { d = d + 1 } 71 if p[oa+1] != p[ob+1] { d = d + 1 } 72 if p[oa+2] != p[ob+2] { d = d + 1 } 73 x = x + 1 74 } 75 y = y + 1 76 } 77 return d 78} 79 80func main() -> i64 { 81 hw("=== nx_cube3d_spin_gate -- ANIMATED 3D: a spinning cube filmstrip (integer sin/cos + Z-buffer) ===\n" as *u8) 82 var fails: i64 = 0 83 84 // sanity on the trig primitive first (Bhaskara Q10): sin90=1024, sin30~512, cos0=1024 85 hw(" isin(90)=" as *u8); pn(isin(90)); hw(" isin(30)=" as *u8); pn(isin(30)); hw(" icos(0)=" as *u8); pn(icos(0)); hw(" isin(0)=" as *u8); pn(isin(0)); hw("\n" as *u8) 86 var t0: i64 = 0 87 if isin(90) == 1024 { if isin(0) == 0 { if icos(0) == 1024 { if isin(30) > 500 { if isin(30) < 525 { t0 = 1 } } } } } 88 if t0 == 1 { hw("T0 PASS integer sin/cos primitive correct (sin90=1024, sin0=0, cos0=1024, sin30~512)\n" as *u8) } else { fails=fails+1; hw("T0 FAIL trig\n" as *u8) } 89 90 let CELL: i64 = 160 91 let NC: i64 = 6 92 let W: i64 = CELL * NC 93 let H: i64 = 170 94 let npix: i64 = W * H 95 let S: i64 = 48 96 97 let cvx: *i64 = sys_mmap(64) as *i64 98 let cvy: *i64 = sys_mmap(64) as *i64 99 let cvz: *i64 = sys_mmap(64) as *i64 100 cvx[0]=0-S; cvy[0]=0-S; cvz[0]=0-S 101 cvx[1]=S; cvy[1]=0-S; cvz[1]=0-S 102 cvx[2]=S; cvy[2]=S; cvz[2]=0-S 103 cvx[3]=0-S; cvy[3]=S; cvz[3]=0-S 104 cvx[4]=0-S; cvy[4]=0-S; cvz[4]=S 105 cvx[5]=S; cvy[5]=0-S; cvz[5]=S 106 cvx[6]=S; cvy[6]=S; cvz[6]=S 107 cvx[7]=0-S; cvy[7]=S; cvz[7]=S 108 109 let px: *i64 = sys_mmap(64) as *i64 110 let py: *i64 = sys_mmap(64) as *i64 111 let pz: *i64 = sys_mmap(64) as *i64 112 let o: *i64 = sys_mmap(32) as *i64 113 114 let fb: *Image = nx_image_alloc(W, H, 3) 115 let pbk: *u8 = fb.pixels 116 var bi: i64 = 0 117 while bi < npix * 3 { pbk[bi] = 8 as u8; bi = bi + 1 } // dark background 118 let zb: *i64 = sys_mmap(npix * 8) as *i64 119 depth_clear(zb, npix, 1000000000) 120 121 // 6 successive rotation angles across the strip (each cell = the next animation frame) 122 var c: i64 = 0 123 while c < NC { 124 let cx: i64 = c * CELL + CELL / 2 125 let ay: i64 = c * 18 // 0,18,36,54,72,90 -- a quarter turn 126 draw_cube(fb, zb, cvx, cvy, cvz, px, py, pz, o, ay, cx, 88) 127 c = c + 1 128 } 129 130 // T1: each successive frame differs from the previous -> real MOTION (not a static repeat) 131 var minmove: i64 = 1000000000 132 var pair: i64 = 0 133 while pair < NC - 1 { 134 let d: i64 = cell_diff(fb, pair * CELL, (pair+1) * CELL, CELL, H) 135 if d < minmove { minmove = d } 136 pair = pair + 1 137 } 138 hw(" min adjacent-frame diff bytes=" as *u8); pn(minmove); hw("\n" as *u8) 139 var t1: i64 = 0 140 if minmove > 200 { t1 = 1 } 141 if t1 == 1 { hw("T1 PASS every successive frame moves (all 5 adjacent frame-pairs differ) -> the cube SPINS\n" as *u8) } else { fails=fails+1; hw("T1 FAIL some frames identical (no motion)\n" as *u8) } 142 143 // T2: a cube is present in EVERY cell (center non-black) 144 var allpresent: i64 = 1 145 var cc: i64 = 0 146 while cc < NC { 147 let cx: i64 = cc * CELL + CELL / 2 148 let co: i64 = 88 * fb.stride + cx * 3 149 var nb: i64 = 0 150 if pbk[co] > 40 { nb = 1 } else { if pbk[co+1] > 40 { nb = 1 } else { if pbk[co+2] > 40 { nb = 1 } } } 151 if nb == 0 { allpresent = 0 } 152 cc = cc + 1 153 } 154 if allpresent == 1 { hw("T2 PASS a solid cube rendered in every one of the 6 frames\n" as *u8) } else { fails=fails+1; hw("T2 FAIL a frame is empty\n" as *u8) } 155 156 nx_png_write_rgb("knowledge/nx_cube3d_spin.png\x00" as *u8, fb.pixels, W, H) 157 hw("artifact -> knowledge/nx_cube3d_spin.png (6-frame spin filmstrip, left->right = a quarter turn)\n" as *u8) 158 159 if fails == 0 { hw("NX-CUBE3D-SPIN GREEN -- animated 3D: integer trig + spinning depth-sorted cube (the video axis for 3D scenes)\n" as *u8); sys_exit(0); return 0 } 160 hw("NX-CUBE3D-SPIN RED fails=" as *u8); pn(fails); hw("\n" as *u8) 161 sys_exit(1); return 1 162}