nx_body_skin_junction.nx source
↩ module page · 58 lines · 4924 B
1// Existing body loft's versioned skin-port composition. Coordinates and indices remain caller-owned.
2// Ports follow the oriented boundary edges of the retained source surface. No implicit cap removal,
3// nearest-neighbour welding, topology repair, anatomical placement or material reassignment occurs here.
4import "nx_buf_dyn.nx"
5const BJ_INDEX_BYTES:i64=8
6const BJ_TRI_WORDS:i64=3
7const BJ_TRI_BYTES:i64=24
8const BJ_INVALID:i64=-301
9const BJ_INDEX:i64=-302
10const BJ_BOUNDARY:i64=-303
11const BJ_OVERLAP:i64=-304
12const BJ_DEGENERATE:i64=-305
13const BJ_DOMAIN:i64=-306
14// For |coordinate| <= L, cross-product terms are bounded by 8*L*L <= INT64_MAX.
15const BJ_COORD_LIMIT:i64=1073741823
16struct BgSkinPort { indices:*i64, count:i64, region:i64, frame:i64 }
17func bj_input_alias(p:i64,n:i64,out:*NxBufOwned)->i64{
18 if p<=0||n<0||p>NX_BO_I64_MAX-n{return BJ_DOMAIN}
19 let d:i64=out as i64;if p<d+NX_BO_BYTES&&d<p+n{return NX_BO_ALIAS}
20 if out.cap>0{let b:i64=out.buf as i64;if p<b+out.cap&&b<p+n{return NX_BO_ALIAS}};return 0
21}
22func bj_edge_owner(tris:*i64,nt:i64,a:i64,b:i64)->i64{
23 var seen:i64=0;var dir:i64=0;var t:i64=0
24 while t<nt{var k:i64=0;while k<3{let x:i64=tris[t*3+k];let y:i64=tris[t*3+(k+1)%3];if x==a&&y==b{seen=seen+1;dir=dir+1};if x==b&&y==a{seen=seen+1;dir=dir-1};k=k+1};t=t+1}
25 if seen!=1||dir!=1{return BJ_BOUNDARY};return 0
26}
27func bj_port_validate(p:*BgSkinPort,nv:i64,tris:*i64,nt:i64)->i64{
28 if (p as i64)<=0{return BJ_INVALID};if (p.indices as i64)<=0||p.count<3||p.count>nv||p.region<0||p.frame<0{return BJ_INVALID}
29 var i:i64=0;while i<p.count{let v:i64=p.indices[i];if v<0||v>=nv{return BJ_INDEX};var j:i64=0;while j<i{if p.indices[j]==v{return BJ_OVERLAP};j=j+1};i=i+1}
30 i=0;while i<p.count{let rc:i64=bj_edge_owner(tris,nt,p.indices[i],p.indices[(i+1)%p.count]);if rc<0{return rc};i=i+1};return 0
31}
32func bj_triangle(vertices:*i64,a:i64,b:i64,c:i64)->i64{
33 let ux:i64=vertices[b*3]-vertices[a*3];let uy:i64=vertices[b*3+1]-vertices[a*3+1];let uz:i64=vertices[b*3+2]-vertices[a*3+2]
34 let vx:i64=vertices[c*3]-vertices[a*3];let vy:i64=vertices[c*3+1]-vertices[a*3+1];let vz:i64=vertices[c*3+2]-vertices[a*3+2]
35 if uy*vz-uz*vy==0&&uz*vx-ux*vz==0&&ux*vy-uy*vx==0{return BJ_DEGENERATE};return 0
36}
37// Builds ONLY the replacement strip. Caller retains source faces except explicitly declared patches.
38// Result is transactional: a refused port/capacity/triangle leaves prior complete output untouched.
39// Relative cyclic alignment is an explicit recipe choice: rotate either port list before this call.
40func bj_bridge(vertices:*i64,nv:i64,tris:*i64,nt:i64,a:*BgSkinPort,b:*BgSkinPort,max_bytes:i64,out:*NxBufOwned)->i64{
41 if (vertices as i64)<=0||(tris as i64)<=0||nv<3||nt<1||nv>NX_BO_I64_MAX/BJ_TRI_BYTES||nt>NX_BO_I64_MAX/BJ_TRI_BYTES{return BJ_INVALID}
42 let state:i64=nx_bo_state(out);if state<0{return state};var rc:i64=bj_input_alias(vertices as i64,nv*BJ_TRI_BYTES,out);if rc<0{return rc};rc=bj_input_alias(tris as i64,nt*BJ_TRI_BYTES,out);if rc<0{return rc};rc=bj_input_alias(a as i64,__size_of(BgSkinPort),out);if rc<0{return rc};rc=bj_input_alias(b as i64,__size_of(BgSkinPort),out);if rc<0{return rc};rc=bj_port_validate(a,nv,tris,nt);if rc<0{return rc};rc=bj_port_validate(b,nv,tris,nt);if rc<0{return rc}
43 rc=bj_input_alias(a.indices as i64,a.count*BJ_INDEX_BYTES,out);if rc<0{return rc};rc=bj_input_alias(b.indices as i64,b.count*BJ_INDEX_BYTES,out);if rc<0{return rc}
44 if a.count>NX_BO_I64_MAX/b.count{return BJ_DOMAIN};if max_bytes<0{return NX_BO_INVALID};if a.count>NX_BO_I64_MAX/BJ_TRI_BYTES-b.count{return BJ_DOMAIN};if max_bytes>0{if a.count>max_bytes/BJ_TRI_BYTES-b.count{return NX_BO_CAPACITY}}
45 var i:i64=0;while i<nv*3{if vertices[i]<0-BJ_COORD_LIMIT||vertices[i]>BJ_COORD_LIMIT{return BJ_DOMAIN};i=i+1}
46 i=0;while i<a.count{var j:i64=0;while j<b.count{if a.indices[i]==b.indices[j]{return BJ_OVERLAP};j=j+1};i=i+1}
47 let scratch:*NxBufOwned=sys_mmap_try(NX_BO_BYTES) as *NxBufOwned;let tri:*i64=sys_mmap_try(BJ_TRI_BYTES) as *i64
48 if (scratch as i64)<=0||(tri as i64)<=0{if (scratch as i64)>0{sys_munmap_direct(scratch as *u8,NX_BO_BYTES)};if (tri as i64)>0{sys_munmap_direct(tri as *u8,BJ_TRI_BYTES)};return NX_BO_ALLOCATION}
49 i=0;var j:i64=0
50 while (i<a.count||j<b.count)&&rc==0{
51 tri[0]=a.indices[i%a.count];tri[1]=b.indices[(b.count-j%b.count)%b.count]
52 var advance_a:i64=0;if j==b.count{advance_a=1}else{if i<a.count{if (i+1)*b.count<=(j+1)*a.count{advance_a=1}}}
53 if advance_a==1{tri[2]=a.indices[(i+1)%a.count];i=i+1}else{tri[2]=b.indices[(b.count-(j+1)%b.count)%b.count];j=j+1}
54 rc=bj_triangle(vertices,tri[0],tri[1],tri[2]);if rc==0{rc=nx_bo_append(scratch,tri as *u8,BJ_TRI_BYTES,max_bytes)}
55 }
56 if rc==0{rc=nx_bo_release(out);if rc==0{out.buf=scratch.buf;out.len=scratch.len;out.cap=scratch.cap;scratch.buf=0 as *u8;scratch.len=0;scratch.cap=0}}
57 nx_bo_release(scratch);sys_munmap_direct(scratch as *u8,NX_BO_BYTES);sys_munmap_direct(tri as *u8,BJ_TRI_BYTES);return rc
58}