code wiki / (root) / nx_nxa_surface_anchor_t140.nx

nx_nxa_surface_anchor_t140.nx source

↩ module page · 94 lines · 6799 B

1// Private candidate: generic surface query composed from canonical NXA attachment validation. 2// Returns existing face,bary0,bary1,bary2,denominator tuple; no new disk section or material kind. 3import "nx_nxa_garment_bind_lib.nx" 4const NSA_E_INPUT:i64=-50 5const NSA_E_RANGE:i64=-51 6const NSA_E_MISS:i64=-52 7const NSA_E_AMBIGUOUS:i64=-53 8// Exact fraction comparison without cross-products. Inputs nonnegative, denominators positive. 9func nsa_unsigned_ratio_cmp(a:i64,b:i64,c:i64,d:i64)->i64{ 10 var x:i64=a;var y:i64=b;var z:i64=c;var w:i64=d;var sign:i64=1 11 while true{let p:i64=x/y;let q:i64=z/w;if p<q{return 0-sign};if p>q{return sign} 12 let r:i64=x%y;let s:i64=z%w;if r==0{if s==0{return 0};return 0-sign};if s==0{return sign} 13 x=y;y=r;z=w;w=s;sign=0-sign 14 };return 0 15} 16func nsa_ratio_cmp(a:i64,b:i64,c:i64,d:i64)->i64{ 17 if a<0{if c>=0{return -1};return 0-nsa_unsigned_ratio_cmp(0-a,b,0-c,d)} 18 if c<0{return 1};return nsa_unsigned_ratio_cmp(a,b,c,d) 19} 20// Coordinate bounds inherited from canonical source validation make 2D determinants exact i64. 21// y numerator is checked independently, since determinant times depth can exceed i64. 22func nsa_triangle_y_v1(p:*i64,idx:*i64,face:i64,qx:i64,qz:i64,result:*i64)->i64{ 23 let a:i64=idx[face*3]*3;let b:i64=idx[face*3+1]*3;let c:i64=idx[face*3+2]*3 24 let x:i64=p[b]-p[a];let z:i64=p[b+2]-p[a+2];let u:i64=p[c]-p[a];let v:i64=p[c+2]-p[a+2] 25 let dx:i64=qx-p[a];let dz:i64=qz-p[a+2];var den:i64=x*v-z*u 26 if den==0{return NSA_E_MISS};var w1:i64=dx*v-dz*u;var w2:i64=x*dz-z*dx 27 if den<0{den=0-den;w1=0-w1;w2=0-w2};if w1<0||w2<0||w1>den||w2>den-w1{return NSA_E_MISS} 28 var w0:i64=den-w1-w2;let gcd:i64=ngb_clip_gcd(ngb_clip_gcd(w0,w1),ngb_clip_gcd(w2,den));w0=w0/gcd;w1=w1/gcd;w2=w2/gcd;den=den/gcd 29 result[0]=w0;result[1]=w1;result[2]=w2;result[3]=den;var depth:i64=0;var k:i64=0 30 while k<3{let y:i64=p[idx[face*3+k]*3+1];let mag:i64=ngb_clip_product(ngb_abs(y),result[k]);if mag<0{return NSA_E_RANGE} 31 if y<0{if depth<0-NGB_CLIP_I64_MAX+mag{return NSA_E_RANGE};depth=depth-mag} 32 else{if depth>NGB_CLIP_I64_MAX-mag{return NSA_E_RANGE};depth=depth+mag};k=k+1 33 };result[4]=depth;return 0 34} 35// direction=-1 chooses smallest Y, +1 greatest Y. Integer query coordinates are caller data. 36// Caller output: five i64 words; scratch: twelve disjoint i64 words. Output unchanged on refusal. 37// Equal-depth overlapping hits refuse instead of guessing an attachment seam. 38func nsa_landmark_y_v1(b:*u8,n:i64,qx:i64,qz:i64,direction:i64,out:*i64,cap:i64,work:*i64,words:i64)->i64{ 39 if direction!=-1&&direction!=1{return NSA_E_INPUT} 40 if qx<0-NGB_ATTACH_COORD_MAX||qx>NGB_ATTACH_COORD_MAX||qz<0-NGB_ATTACH_COORD_MAX||qz>NGB_ATTACH_COORD_MAX{return NSA_E_RANGE} 41 if cap<5||words<12{return NSA_E_INPUT};let valid:i64=ngb_attach_source_v1(b,n);if valid<0{return valid} 42 let ow:i64=ngp_output(b,n,out as *u8,40,40);if ow<0{return ow} 43 let sw:i64=ngp_output(b,n,work as *u8,96,96);if sw<0{return sw} 44 let overlap:i64=ngp_output(work as *u8,96,out as *u8,40,40);if overlap<0{return overlap} 45 let va:i64=nxa_counted_section(b,n,nxa_tag4("VERT"),3);let ta:i64=nxa_counted_section(b,n,nxa_tag4("TRIS"),3);let h:*i64=b as *i64 46 let p:*i64=((b as i64)+(va+1)*8) as *i64;let idx:*i64=((b as i64)+(ta+1)*8) as *i64 47 return nsa_landmark_mesh_y_v1(p,idx,h[ta],qx,qz,direction,out,work) 48} 49// Internal mesh view: source and disjoint storage already validated by boundary. 50func nsa_landmark_mesh_y_v1(p:*i64,idx:*i64,nt:i64,qx:i64,qz:i64,direction:i64,out:*i64,work:*i64)->i64{ 51 let trial:*i64=work;let best:*i64=((work as i64)+48) as *i64;var face:i64=0;var selected:i64=-1;var ambiguous:i64=0 52 while face<nt{let rc:i64=nsa_triangle_y_v1(p,idx,face,qx,qz,trial);if rc==NSA_E_RANGE{return rc} 53 if rc==0{var comparison:i64=0;if selected>=0{comparison=nsa_ratio_cmp(trial[4],trial[3],best[4],best[3])} 54 if selected<0||comparison==direction{selected=face;ambiguous=0;var k:i64=0;while k<5{best[k]=trial[k];k=k+1}} 55 else{if comparison==0{ambiguous=1}} 56 };face=face+1 57 };if selected<0{return NSA_E_MISS};if ambiguous!=0{return NSA_E_AMBIGUOUS} 58 out[0]=selected;var k:i64=0;while k<4{out[k+1]=best[k];k=k+1};return 0 59} 60 61// Topological correspondence only: labels identify connected source vertices, never anatomy. 62// Stable label is the minimum source vertex index, independent of triangle order. 63func nsa_component_root_v1(labels:*i64,v:i64)->i64{ 64 var root:i64=v;while labels[root]!=root{root=labels[root]} 65 var q:i64=v;while labels[q]!=q{let next:i64=labels[q];labels[q]=root;q=next};return root 66} 67func nsa_component_join_v1(labels:*i64,a:i64,b:i64)->i64{ 68 let x:i64=nsa_component_root_v1(labels,a);let y:i64=nsa_component_root_v1(labels,b) 69 if x<y{labels[y]=x}else{if y<x{labels[x]=y}};return 0 70} 71// Boundary validation completes before output writes. Capacity is measured in i64 labels. 72// Required capacity derives from the validated VERT count; isolated vertices remain components. 73func nsa_component_labels_v1(b:*u8,n:i64,labels:*i64,capacity:i64)->i64{ 74 let valid:i64=ngb_attach_source_v1(b,n);if valid<0{return valid} 75 let va:i64=nxa_counted_section(b,n,nxa_tag4("VERT"),3);let ta:i64=nxa_counted_section(b,n,nxa_tag4("TRIS"),3);let h:*i64=b as *i64 76 let nv:i64=h[va];let nt:i64=h[ta];if capacity<nv{return NSA_E_INPUT} 77 if nv>NGB_CLIP_I64_MAX/8{return NSA_E_RANGE} 78 let space:i64=ngp_output(b,n,labels as *u8,nv*8,nv*8);if space<0{return space} 79 let idx:*i64=((b as i64)+(ta+1)*8) as *i64 80 var i:i64=0;while i<nv{labels[i]=i;i=i+1} 81 i=0;while i<nt{let a:i64=idx[i*3];nsa_component_join_v1(labels,a,idx[i*3+1]);nsa_component_join_v1(labels,a,idx[i*3+2]);i=i+1} 82 var count:i64=0;i=0;while i<nv{labels[i]=nsa_component_root_v1(labels,i);if labels[i]==i{count=count+1};i=i+1};return count 83} 84 85const NSA_E_MIXED_SUPPORT:i64=-54 86// Derive rigid attachment support from the actual connected component and skin weights. 87// A mixed-joint component requires shared deformation, never an inferred replacement joint. 88func nsa_rigid_component_joint_v1(b:*u8,n:i64,seed:i64,labels:*i64,capacity:i64)->i64{ 89 let rc:i64=nsa_component_labels_v1(b,n,labels,capacity);if rc<0{return rc} 90 let va:i64=nxa_counted_section(b,n,nxa_tag4("VERT"),3);let sk:i64=nxa_counted_section(b,n,nxa_tag4("SKIN"),8);let sj:i64=nxa_counted_section(b,n,nxa_tag4("SKEL"),8);if va<0||sk<0||sj<0{return NSA_E_INPUT} 91 let h:*i64=b as *i64;let nv:i64=h[va];let nj:i64=h[sj];if seed<0||seed>=nv||h[sk]!=nv||nj<1{return NSA_E_INPUT} 92 let component:i64=labels[seed];var selected:i64=-1;var v:i64=0 93 while v<nv{if labels[v]==component{var sum:i64=0;var k:i64=0;while k<4{let j:i64=h[sk+1+v*8+k];let w:i64=h[sk+1+v*8+4+k];if j<0||j>=nj||w<0||w>4096{return NSA_E_INPUT};sum=sum+w;if w>0{if selected<0{selected=j}else{if selected!=j{return NSA_E_MIXED_SUPPORT}}};k=k+1};if sum!=4096{return NSA_E_INPUT}};v=v+1};return selected 94}