code wiki / _hdl_build / nx_articulate_gate.nx
nx_articulate_gate.nx source
↩ module page · 132 lines · 8014 B
1// nx_articulate_gate.nx -- ★R9: ARTICULATED (rigged) assets. A 2-bone-per-leg skeleton with forward kinematics,
2// proven by the defining articulation property + a walk animation:
3// T1 ★HIERARCHY (FK): rotating the HIP (thigh) moves the whole leg (knee AND foot); rotating the KNEE (shin)
4// moves ONLY the lower bone (the foot moves, the knee joint does NOT) -- parent drives subtree, child doesn't
5// T2 POSE CONTROL: the same creature in a bent pose renders visibly different from standing
6// T3 ★WALK CYCLE: animating the joints produces distinct, PERIODIC frames (a gait -- skeletal animation)
7// T4 determinism + a walk-cycle strip knowledge/nx_articulate.png (6 frames of a walking creature)
8// license_tier: ORIGINAL expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_itrig.nx"
11import "nx_png.nx"
12import "nx_creaturegen.nx"
13
14func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
15func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 }
16func d3(a: *i64, ai: i64, b: *i64, bi: i64) -> i64 { let dx: i64=a[ai]-b[bi]; let dy: i64=a[ai+1]-b[bi+1]; let dz: i64=a[ai+2]-b[bi+2]; var s: i64=dx*dx+dy*dy+dz*dz; if s<=0 {return 0} var r: i64=s; var q: i64=(r+1)/2; while q<r {r=q; q=(r+s/r)/2} return r }
17func L1i(a: *i64, b: *i64, n: i64) -> i64 { var s: i64=0; var i: i64=0; while i<n { var d: i64=(a[i]&255)-(b[i]&255); if d<0{d=0-d} s=s+d; i=i+1 } return s }
18
19const YAW: i64 = 6434 // ~90deg side view: leg swing (fore/aft in Z) is visible
20const THA: i64 = 1500 // thigh swing amplitude (it4096)
21const SHA: i64 = 1100 // shin bend amplitude
22
23func main() -> i64 {
24 hw("=== nx_articulate_gate -- R9: skeletal rig + forward kinematics + walk cycle ===\n" as *u8)
25 var fails: i64 = 0
26
27 // ---- T1 FK hierarchy ----
28 let fk0: *i64 = sys_mmap(8*8) as *i64
29 let fkT: *i64 = sys_mmap(8*8) as *i64
30 let fkS: *i64 = sys_mmap(8*8) as *i64
31 cg_fk_leg(0, 800, 0, 0, 0, 132, 120, fk0) // rest
32 cg_fk_leg(0, 800, 0, 2000, 0, 132, 120, fkT) // thigh only
33 cg_fk_leg(0, 800, 0, 0, 2000, 132, 120, fkS) // shin only
34 let knee_moves_shin: i64 = d3(fkS, 0, fk0, 0) // should be 0 (shin doesn't move the knee joint)
35 let foot_moves_shin: i64 = d3(fkS, 3, fk0, 3) // should be > 0 (shin moves the foot)
36 let knee_moves_thigh: i64 = d3(fkT, 0, fk0, 0) // should be > 0 (thigh moves the knee)
37 let foot_moves_thigh: i64 = d3(fkT, 3, fk0, 3) // should be > 0
38 hw(" FK: shin->knee-disp="); pn(knee_moves_shin); hw(" shin->foot-disp="); pn(foot_moves_shin); hw(" thigh->knee-disp="); pn(knee_moves_thigh); hw("\n" as *u8)
39 var t1: i64 = 0
40 if knee_moves_shin == 0 { if foot_moves_shin > 40 { if knee_moves_thigh > 40 { if foot_moves_thigh > 40 { t1 = 1 } } } }
41 if t1 == 1 { hw("T1 PASS HIERARCHY: hip rotates the whole leg; knee rotates only the lower bone (real FK)\n" as *u8) }
42 else { fails=fails+1; hw("T1 FAIL FK hierarchy\n" as *u8) }
43
44 // ---- render setup ----
45 let W: i64 = as_w(); let H: i64 = as_h(); let npx: i64 = W*H
46 let AP: *i64 = sys_mmap(96*AS_STRIDE*8) as *i64
47 let pose: *i64 = sys_mmap(8*8) as *i64
48 let stand: *i64 = sys_mmap(npx*8) as *i64
49 let bent: *i64 = sys_mmap(npx*8) as *i64
50
51 // T2 standing vs bent
52 var k: i64 = 0
53 while k < 8 { pose[k]=0; k=k+1 }
54 let ns: i64 = creaturegen_build_posed(AP, 4, pose)
55 anatstack_render(AP, ns, YAW, 3, 1, 45, 150, 116, 84, stand)
56 // a dramatic crouch: front legs fold forward-under, back legs splay back + deep knee bend
57 pose[0]=2600; pose[1]=0-3200; pose[2]=0-2600; pose[3]=0-3200; pose[4]=2800; pose[5]=0-3400; pose[6]=0-2800; pose[7]=0-3400
58 let nb: i64 = creaturegen_build_posed(AP, 4, pose)
59 anatstack_render(AP, nb, YAW, 3, 1, 45, 150, 116, 84, bent)
60 let dpose: i64 = L1i(stand, bent, npx)
61 hw(" pose: parts="); pn(ns); hw(" stand-vs-crouch L1="); pn(dpose); hw("\n" as *u8)
62 var t2: i64 = 0
63 if dpose > 120000 { t2 = 1 }
64 if t2 == 1 { hw("T2 PASS POSE CONTROL: the rig reposes the same creature (standing != bent)\n" as *u8) }
65 else { fails=fails+1; hw("T2 FAIL pose\n" as *u8) }
66
67 // ---- T3 walk cycle: 6 frames + a 7th at full-period (== frame 0) ----
68 let NF: i64 = 6
69 let frames: *i64 = sys_mmap(NF*npx*8) as *i64
70 var f: i64 = 0
71 while f < NF {
72 let ph: i64 = f*25736/NF // 0..2pi over the cycle
73 let phd: i64 = ph + 12868 // diagonal pair = half-cycle offset
74 // FL,BR in phase ; FR,BL opposite
75 pose[0]=THA*it_sin4096(ph)/4096; pose[1]=SHA*it_sin4096(ph+6434)/4096
76 pose[2]=THA*it_sin4096(phd)/4096; pose[3]=SHA*it_sin4096(phd+6434)/4096
77 pose[4]=THA*it_sin4096(phd)/4096; pose[5]=SHA*it_sin4096(phd+6434)/4096
78 pose[6]=THA*it_sin4096(ph)/4096; pose[7]=SHA*it_sin4096(ph+6434)/4096
79 let nf: i64 = creaturegen_build_posed(AP, 4, pose)
80 anatstack_render(AP, nf, YAW, 3, 1, 45, 150, 116, 84, (frames as i64 + f*npx*8) as *i64)
81 f = f + 1
82 }
83 // full-period frame (phase 2pi) -> should match frame 0
84 var k2: i64 = 0
85 while k2 < 8 { pose[k2] = 0; k2 = k2 + 1 }
86 pose[0]=THA*it_sin4096(25736)/4096; pose[1]=SHA*it_sin4096(25736+6434)/4096
87 pose[2]=THA*it_sin4096(25736+12868)/4096; pose[3]=SHA*it_sin4096(25736+12868+6434)/4096
88 pose[4]=pose[2]; pose[5]=pose[3]; pose[6]=pose[0]; pose[7]=pose[1]
89 let nfw: i64 = creaturegen_build_posed(AP, 4, pose)
90 let wrap: *i64 = sys_mmap(npx*8) as *i64
91 anatstack_render(AP, nfw, YAW, 3, 1, 45, 150, 116, 84, wrap)
92 // adjacent-frame motion (min over pairs) + periodicity (frame0 vs wrap)
93 var minadj: i64 = 2000000000
94 var ff: i64 = 0
95 while ff < NF-1 {
96 let d: i64 = L1i((frames as i64 + ff*npx*8) as *i64, (frames as i64 + (ff+1)*npx*8) as *i64, npx)
97 if d < minadj { minadj = d }
98 ff = ff + 1
99 }
100 let perr: i64 = L1i(frames, wrap, npx)
101 hw(" walk: min adjacent-frame L1="); pn(minadj); hw(" periodicity err(frame0 vs 2pi)="); pn(perr); hw("\n" as *u8)
102 var t3: i64 = 0
103 if minadj > 40000 { if perr < 20000 { t3 = 1 } } // frames move, and the cycle closes (periodic gait)
104 if t3 == 1 { hw("T3 PASS WALK CYCLE: joints animate into distinct, periodic frames (skeletal animation)\n" as *u8) }
105 else { fails=fails+1; hw("T3 FAIL walk\n" as *u8) }
106
107 // ---- T4 determinism + strip ----
108 let chk: *i64 = sys_mmap(npx*8) as *i64
109 var k3: i64 = 0
110 while k3 < 8 { pose[k3]=0; k3=k3+1 }
111 let ncd: i64 = creaturegen_build_posed(AP, 4, pose)
112 anatstack_render(AP, ncd, YAW, 3, 1, 45, 150, 116, 84, chk)
113 var ddet: i64 = 0
114 var i2: i64 = 0
115 while i2 < npx { if chk[i2] != stand[i2] { ddet = ddet + 1 } i2 = i2 + 1 }
116 var t4: i64 = 0
117 if ddet == 0 { t4 = 1 }
118 if t4 == 1 { hw("T4 PASS deterministic\n" as *u8) }
119 else { fails=fails+1; hw("T4 FAIL nondeterministic ddet="); pn(ddet); hw("\n" as *u8) }
120
121 let GW: i64 = W*NF
122 let gal: *i64 = sys_mmap(GW*H*8) as *i64
123 var y: i64 = 0
124 while y < H { var x: i64=0; while x<W { var c: i64=0; while c<NF { gal[y*GW+c*W+x] = frames[c*npx + y*W+x]; c=c+1 } x=x+1 } y=y+1 }
125 write_png(gal, GW, H, "knowledge/nx_articulate.png" as *u8)
126 hw("T5 strip -> knowledge/nx_articulate.png (6-frame walk cycle)\n" as *u8)
127
128 if fails == 0 { hw("ARTICULATE-GATE GREEN -- R9: a skeletal rig with forward kinematics (hierarchy-correct) + pose control + a periodic walk cycle = articulated, animatable assets, sovereign. Residual vs their sim-ready: joint limits + physics/URDF export.\n" as *u8); sys_exit(0); return 0 }
129 hw("ARTICULATE-GATE RED fails="); pn(fails); hw("\n" as *u8)
130 sys_exit(1)
131 return 1
132}