code wiki / (root) / nx_gm_cloth_lib_t138.nx

nx_gm_cloth_lib_t138.nx source

↩ module page · 402 lines · 18505 B

1// nx_gm_cloth_lib.nx -- LIB: the PG29 cloth XPBD solver (gm_xpbd_step and its edge/area 2// rulers). Split out of nx_nxa_garment so the GATE can import it without colliding with the 3// generator's main(); the garment organ imports this lib and carries a REAL call (the 4// xpbdcheck verb), so its PG29 watch-row flip is backed by wiring, not a name in a comment. 5// license_tier: ORIGINAL No hw writes (Rule 26). 6import "nx_softtissue.nx" 7// ---- PG29: THE CLOTH XPBD SOLVER (gm_xpbd_step) -- appended 2026-08-26 ---------------------- 8// nx_nxa_garment GENERATED a fitted dress; NOTHING simulated it (the page's own comment: 9// "moves with the skin, zero physics", and GPIN is emitted but unconsumed). This is the solver 10// half of PG29, the organ symbol its watch row names. It is a LIB surface inside this organ: 11// main() above is untouched, so the generator's behaviour is bit-identical by construction. 12// 13// SHAPE: standard XPBD (Mueller et al., Position-Based Simulation of Compliant Constrained 14// Dynamics) -- predict under gravity, then iterate distance-constraint projections whose 15// stiffness enters ONLY through a compliance alpha, converted to alpha-tilde = alpha/dt^2 by 16// st_alpha_tilde -- COMPOSED from nx_softtissue, the estate's one XPBD lane, never re-derived. 17// All positions/velocities are i64 in the caller's mesh units; no float anywhere. 18// 19// NO MAGIC NUMBERS: gravity, dt, iteration count and both compliances are CALLER ARGUMENTS. 20// The solver owns only structure. Absolute fabric parameters (a cited bending stiffness) are a 21// NAMED follow-on -- gm_stiffness_mask, the second half of PG29 -- and until they are banked 22// the gate proves RELATIVE physics: a fold happens, area is conserved, stiffness changes the 23// fold. That is exactly the plan row's pre-declared done-rule. 24 25// ---- edge list of the GU x GV ring-grid, DERIVED from the same topology main() emits ---- 26// classes: 0 = structural (ring + vertical), 1 = shear (quad diagonals), 2 = bending (2-hop 27// vertical). Rest lengths are measured from the rest verts handed in -- never assumed. 28// Row format per edge: [a, b, rest, class]. Returns the edge count written. 29const GM_EROW: i64 = 4 30const GM_C_STRUCT: i64 = 0 31const GM_C_SHEAR: i64 = 1 32const GM_C_BEND: i64 = 2 33// contact skin = the integer-truncation bound of the radial push (see gm_capsule_project) 34const GM_SKIN: i64 = 2 35 36func gm_isqrt(n: i64) -> i64 { 37 if n <= 0 { return 0 } 38 var x: i64 = n 39 if x > 4096 { x = n/2 } 40 var it: i64 = 0 41 while it < 40 { 42 if x > 0 { let y: i64 = (x + n/x)/2; if y > 0 { x = y } } 43 it = it + 1 44 } 45 return x 46} 47 48func gm_dist(v: *i64, a: i64, b: i64) -> i64 { 49 let dx: i64 = v[a*3] - v[b*3] 50 let dy: i64 = v[a*3+1] - v[b*3+1] 51 let dz: i64 = v[a*3+2] - v[b*3+2] 52 return gm_isqrt(dx*dx + dy*dy + dz*dz) 53} 54 55// build the edge list over an nu x nv2 ring grid (column-major ring index u + row r, vertex 56// id = r*nu + u, ring wraps u -> (u+1)%nu). Caller supplies rest verts; rests are MEASURED. 57func gm_edges_build(verts: *i64, nu: i64, nv2: i64, edges: *i64) -> i64 { 58 var ne: i64 = 0 59 var r: i64 = 0 60 while r < nv2 { 61 var u: i64 = 0 62 while u < nu { 63 let a: i64 = r*nu + u 64 let u1: i64 = (u + 1) % nu 65 // ring structural 66 let br: i64 = r*nu + u1 67 edges[ne*GM_EROW] = a; edges[ne*GM_EROW+1] = br 68 edges[ne*GM_EROW+2] = gm_dist(verts, a, br); edges[ne*GM_EROW+3] = GM_C_STRUCT 69 ne = ne + 1 70 if r + 1 < nv2 { 71 // vertical structural 72 let bv: i64 = (r+1)*nu + u 73 edges[ne*GM_EROW] = a; edges[ne*GM_EROW+1] = bv 74 edges[ne*GM_EROW+2] = gm_dist(verts, a, bv); edges[ne*GM_EROW+3] = GM_C_STRUCT 75 ne = ne + 1 76 // shear diagonals of the quad (a, ring-next, below, below-ring-next) 77 let bd: i64 = (r+1)*nu + u1 78 edges[ne*GM_EROW] = a; edges[ne*GM_EROW+1] = bd 79 edges[ne*GM_EROW+2] = gm_dist(verts, a, bd); edges[ne*GM_EROW+3] = GM_C_SHEAR 80 ne = ne + 1 81 edges[ne*GM_EROW] = br; edges[ne*GM_EROW+1] = bv 82 edges[ne*GM_EROW+2] = gm_dist(verts, br, bv); edges[ne*GM_EROW+3] = GM_C_SHEAR 83 ne = ne + 1 84 } 85 if r + 2 < nv2 { 86 // bending: 2-hop vertical resists the fold; its compliance is the stiffness knob 87 let bb: i64 = (r+2)*nu + u 88 edges[ne*GM_EROW] = a; edges[ne*GM_EROW+1] = bb 89 edges[ne*GM_EROW+2] = gm_dist(verts, a, bb); edges[ne*GM_EROW+3] = GM_C_BEND 90 ne = ne + 1 91 } 92 u = u + 1 93 } 94 r = r + 1 95 } 96 return ne 97} 98 99// ---- ONE XPBD STEP --------------------------------------------------------------------------- 100// verts/vel: i64 xyz triples, caller's mesh units. pins: vertex ids whose position is HELD 101// (infinite mass -- the GPIN contract). gy_per_tick2: gravity as velocity-delta per tick in mesh 102// units (sign carries direction; the garment's up-axis is +z, so a falling sheet passes negative). 103// alpha_q12_struct / alpha_q12_bend: XPBD compliance per class in the SAME Q12 the tissue lane 104// banks (st_alpha_tilde's own contract); shear uses the structural value. dt_us: microseconds per 105// tick, handed to st_alpha_tilde so stiffness is TIMESTEP-INDEPENDENT -- the whole point of XPBD. 106// Checked XPBD lane. Arithmetic failure -2, boundary -1, allocation -3, degenerate edge -4. 107// Positions and velocities retain caller mesh-units/per-tick convention. dt only scales compliance. 108const GMC_MAX:i64=9223372036854775807 109func gmc_add(a:i64,b:i64,s:*i64)->i64 { 110 if a<0-GMC_MAX || b<0-GMC_MAX {s[0]=0-2;return 0} 111 if b>0 {if a>GMC_MAX-b {s[0]=0-2;return 0}} 112 if b<0 {if a<(0-GMC_MAX)-b {s[0]=0-2;return 0}} 113 return a+b 114} 115func gmc_mul(a:i64,b:i64,s:*i64)->i64 { 116 if a<0-GMC_MAX || b<0-GMC_MAX {s[0]=0-2;return 0} 117 let aa:i64=st_abs(a);let bb:i64=st_abs(b) 118 if bb>0 {if aa>GMC_MAX/bb {s[0]=0-2;return 0}} 119 return a*b 120} 121func gmc_round(a:i64,b:i64,s:*i64)->i64 { 122 if b<=0 || a<0-GMC_MAX {s[0]=0-2;return 0} 123 if st_abs(a)>GMC_MAX-b/2 {s[0]=0-2;return 0} 124 return st_div_r(a,b) 125} 126func gmc_dot(ax:i64,ay:i64,az:i64,bx:i64,by:i64,bz:i64,s:*i64)->i64 { 127 return gmc_add(gmc_add(gmc_mul(ax,bx,s),gmc_mul(ay,by,s),s),gmc_mul(az,bz,s),s) 128} 129func gmc_alpha(a:i64,ds:i64,dt:i64,s:*i64)->i64 { 130 let v0:i64=gmc_round(gmc_mul(a,ds,s),ST_Q12,s) 131 let v1:i64=gmc_round(gmc_mul(v0,ST_DT_REF_US,s),dt,s) 132 gmc_round(gmc_mul(v1,ST_DT_REF_US,s),dt,s) 133 if s[0]!=0 {return 0} 134 return st_alpha_tilde(a,ds,dt) 135} 136func gmc_overlap(a:i64,na:i64,b:i64,nb:i64)->i64 { 137 if na==0 || nb==0 {return 0} 138 if a<=0 || b<=0 || na<0 || nb<0 {return 1} 139 if a>GMC_MAX-na || b>GMC_MAX-nb {return 1} 140 if a<b+nb && b<a+na {return 1} 141 return 0 142} 143func gm_xpbd_workspace_words(nvv:i64,ne:i64)->i64 { 144 if nvv<0 || ne<0 {return 0-1} 145 if ne>GMC_MAX-1 {return 0-1} 146 if nvv>(GMC_MAX-1-ne)/7 {return 0-1} 147 let words:i64=1+nvv*7+ne 148 if words>GMC_MAX/8 {return 0-1} 149 return words 150} 151// Caller supplies disjoint workspace; optional inverse masses use Q12, pinned vertices override to0. 152// Buffer extents are the declared nvv/ne/npins counts, as in the existing pointer API. 153// Commit occurs only after every constraint and velocity succeeds; workspace is scratch on failure. 154func gm_xpbd_step_checked(verts:*i64,vel:*i64,nvv:i64,edges:*i64,ne:i64, 155 pins:*i64,npins:i64,gz_per_tick:i64,dt_us:i64,iters:i64, 156 alpha_q12_struct:i64,alpha_q12_bend:i64,mass:*i64,mass_count:i64, 157 work:*i64,work_words:i64)->i64 { 158 let need:i64=gm_xpbd_workspace_words(nvv,ne) 159 if need<0 || work_words<need || (work as i64)<=0 {return 0-1} 160 if nvv<1 || (verts as i64)<=0 || (vel as i64)<=0 {return 0-1} 161 if ne>GMC_MAX/32 || npins<0 || npins>nvv {return 0-1} 162 if ne>0 {if (edges as i64)<=0 {return 0-1}} 163 if npins>0 {if (pins as i64)<=0 {return 0-1}} 164 if dt_us<=0 || iters<0 || alpha_q12_struct<0 || alpha_q12_bend<0 {return 0-1} 165 if (mass as i64)!=0 {if (mass as i64)<0 || mass_count<nvv {return 0-1}} 166 if gmc_overlap(work as i64,need*8,verts as i64,nvv*24)==1 || gmc_overlap(work as i64,need*8,vel as i64,nvv*24)==1 {return 0-1} 167 if gmc_overlap(work as i64,need*8,edges as i64,ne*32)==1 || gmc_overlap(work as i64,need*8,pins as i64,npins*8)==1 {return 0-1} 168 if (mass as i64)!=0 {if gmc_overlap(work as i64,need*8,mass as i64,nvv*8)==1 {return 0-1}} 169 if gmc_overlap(verts as i64,nvv*24,vel as i64,nvv*24)==1 {return 0-1} 170 work[0]=0 171 let q:*i64=((work as i64)+8) as *i64 172 let w:*i64=((q as i64)+nvv*24) as *i64 173 let iw:*i64=((w as i64)+nvv*24) as *i64 174 let lm:*i64=((iw as i64)+nvv*8) as *i64 175 var i:i64=0 176 while i<nvv { 177 iw[i]=ST_Q12 178 if (mass as i64)!=0 {iw[i]=mass[i];if iw[i]<0 {return 0-1}} 179 var k:i64=0 180 while k<3 {if verts[i*3+k]<0-GMC_MAX || vel[i*3+k]<0-GMC_MAX {return 0-2};q[i*3+k]=verts[i*3+k];w[i*3+k]=vel[i*3+k];k=k+1} 181 i=i+1 182 } 183 i=0 184 while i<npins {if pins[i]<0 || pins[i]>=nvv {return 0-1};iw[pins[i]]=0;i=i+1} 185 i=0 186 while i<ne { 187 if edges[i*4]<0 || edges[i*4]>=nvv || edges[i*4+1]<0 || edges[i*4+1]>=nvv {return 0-1} 188 if edges[i*4+2]<0 || edges[i*4+3]<GM_C_STRUCT || edges[i*4+3]>GM_C_BEND {return 0-1} 189 lm[i]=0;i=i+1 190 } 191 i=0 192 while i<nvv { 193 if iw[i]>0 { 194 w[i*3+2]=gmc_add(w[i*3+2],gz_per_tick,work) 195 var k:i64=0 196 while k<3 {q[i*3+k]=gmc_add(q[i*3+k],w[i*3+k],work);k=k+1} 197 } 198 i=i+1 199 } 200 if work[0]!=0 {return work[0]} 201 var it:i64=0 202 while it<iters { 203 var e:i64=0 204 while e<ne { 205 let a:i64=edges[e*4];let b:i64=edges[e*4+1] 206 let ds:i64=gmc_add(iw[a],iw[b],work) 207 if ds>0 { 208 let dx:i64=gmc_add(q[a*3],0-q[b*3],work) 209 let dy:i64=gmc_add(q[a*3+1],0-q[b*3+1],work) 210 let dz:i64=gmc_add(q[a*3+2],0-q[b*3+2],work) 211 let ll:i64=gmc_dot(dx,dy,dz,dx,dy,dz,work) 212 if work[0]!=0 {return work[0]} 213 let ln:i64=st_isqrt(ll) 214 if ln==0 {if edges[e*4+2]>0 {return 0-4}} 215 if ln>0 { 216 var alpha:i64=alpha_q12_struct 217 if edges[e*4+3]==GM_C_BEND {alpha=alpha_q12_bend} 218 let at:i64=gmc_alpha(alpha,ds,dt_us,work) 219 let den:i64=gmc_add(ds,at,work) 220 let c:i64=gmc_add(ln,0-edges[e*4+2],work) 221 let num:i64=gmc_add(gmc_mul(0-c,ST_Q12,work),0-gmc_round(gmc_mul(at,lm[e],work),ST_Q12,work),work) 222 let dl:i64=gmc_round(gmc_mul(num,ST_Q12,work),den,work) 223 lm[e]=gmc_add(lm[e],dl,work) 224 let sa:i64=gmc_round(gmc_mul(iw[a],dl,work),ST_Q12,work) 225 let sb:i64=gmc_round(gmc_mul(iw[b],dl,work),ST_Q12,work) 226 var k:i64=0 227 while k<3 { 228 let diff:i64=gmc_add(q[a*3+k],0-q[b*3+k],work) 229 let un:i64=gmc_round(gmc_mul(diff,ST_Q12,work),ln,work) 230 let da:i64=gmc_round(gmc_mul(sa,un,work),ST_Q12*ST_Q12,work) 231 let db:i64=gmc_round(gmc_mul(sb,un,work),ST_Q12*ST_Q12,work) 232 q[a*3+k]=gmc_add(q[a*3+k],da,work) 233 q[b*3+k]=gmc_add(q[b*3+k],0-db,work) 234 k=k+1 235 } 236 } 237 } 238 if work[0]!=0 {return work[0]} 239 e=e+1 240 } 241 it=it+1 242 } 243 i=0 244 while i<nvv*3 {w[i]=gmc_add(q[i],0-verts[i],work);i=i+1} 245 if work[0]!=0 {return work[0]} 246 i=0 247 while i<nvv*3 {verts[i]=q[i];vel[i]=w[i];i=i+1} 248 return 0 249} 250func gm_xpbd_step(verts:*i64,vel:*i64,nvv:i64,edges:*i64,ne:i64,pins:*i64,npins:i64, 251 gz_per_tick:i64,dt_us:i64,iters:i64,alpha_q12_struct:i64,alpha_q12_bend:i64)->i64 { 252 let words:i64=gm_xpbd_workspace_words(nvv,ne) 253 if words<0 {return 0-1} 254 let mem:*i64=sys_mmap_shared(words*8) as *i64 255 if (mem as i64)<=0 {return 0-3} 256 let rc:i64=gm_xpbd_step_checked(verts,vel,nvv,edges,ne,pins,npins,gz_per_tick,dt_us,iters, 257 alpha_q12_struct,alpha_q12_bend,0 as *i64,0,mem,words) 258 let closed:i64=sys_munmap(mem as *u8,words*8) 259 if rc!=0 {return rc} 260 if closed!=0 {return 0-5} 261 return 0 262} 263func gm_xpbd_step_banked(verts:*i64,vel:*i64,nvv:i64,edges:*i64,ne:i64,pins:*i64,npins:i64, 264 gz_per_tick:i64,dt_us:i64,iters:i64,alpha_q12_struct:i64,alpha_q12_bend:i64,bank:*i64)->i64 { 265 if nvv<1 || nvv>GMC_MAX/24 || (bank as i64)<=0 || (verts as i64)<=0 {return 0-1} 266 if gmc_overlap(bank as i64,nvv*24,verts as i64,nvv*24)==1 || gmc_overlap(bank as i64,nvv*24,vel as i64,nvv*24)==1 {return 0-1} 267 var i:i64=0 268 while i<nvv*3 {bank[i]=verts[i];i=i+1} 269 return gm_xpbd_step(verts,vel,nvv,edges,ne,pins,npins,gz_per_tick,dt_us,iters,alpha_q12_struct,alpha_q12_bend) 270} 271 272// total triangle area x2 of the ring grid (cross-product magnitudes), the conservation ruler 273// the gate's area tooth reads. Integer, exact up to isqrt rounding, and the SAME function 274// measures before and after so the comparison cannot drift between two rulers. 275func gm_area2(verts: *i64, nu: i64, nv2: i64) -> i64 { 276 var total: i64 = 0 277 var r: i64 = 0 278 while r < nv2 - 1 { 279 var u: i64 = 0 280 while u < nu { 281 let u1: i64 = (u + 1) % nu 282 let a: i64 = r*nu + u 283 let b: i64 = r*nu + u1 284 let c: i64 = (r+1)*nu + u 285 let d: i64 = (r+1)*nu + u1 286 // quad = two triangles (a,b,c) and (b,d,c) 287 var t9: i64 = 0 288 while t9 < 2 { 289 var p1: i64 = a; var p2: i64 = b; var p3: i64 = c 290 if t9 == 1 { p1 = b; p2 = d; p3 = c } 291 let e1x: i64 = verts[p2*3] - verts[p1*3] 292 let e1y: i64 = verts[p2*3+1] - verts[p1*3+1] 293 let e1z: i64 = verts[p2*3+2] - verts[p1*3+2] 294 let e2x: i64 = verts[p3*3] - verts[p1*3] 295 let e2y: i64 = verts[p3*3+1] - verts[p1*3+1] 296 let e2z: i64 = verts[p3*3+2] - verts[p1*3+2] 297 let cx: i64 = e1y*e2z - e1z*e2y 298 let cy: i64 = e1z*e2x - e1x*e2z 299 let cz: i64 = e1x*e2y - e1y*e2x 300 total = total + gm_isqrt(cx*cx + cy*cy + cz*cz) 301 t9 = t9 + 1 302 } 303 u = u + 1 304 } 305 r = r + 1 306 } 307 return total 308} 309 310// ---- CLOTH-OVER-BODY CONTACT: capsule projection -------------------------------------------- 311// The world page's body is a skinned figure whose limbs and torso a CAPSULE approximates -- the 312// same primitive st_set_touch's sphere probe already uses one rung down. Projection, not force: 313// after the constraint iterations, any cloth vert inside the capsule is pushed to its surface 314// along the radial, exactly the PBD contact convention. Called once per tick per capsule by the 315// stepper's caller (capsule count is the CALLER's anatomy, not this lib's constant). 316// Capsule: segment (ax,ay,az)-(bx,by,bz) with radius rad, all in the caller's mesh units. 317// Returns the number of verts projected -- 0 on a clean pass, so a gate can assert BOTH ways. 318// Frictionless static/moving capsule contact. No two-way body response or continuous collision. 319// Pinned penetration is an explicit conflict(-6), not a silently moved attachment. 320func gmc_closest(px:i64,py:i64,pz:i64,ax:i64,ay:i64,az:i64,ux:i64,uy:i64,uz:i64,uu:i64,c:*i64,s:*i64)->i64 { 321 let dot:i64=gmc_dot(gmc_add(px,0-ax,s),gmc_add(py,0-ay,s),gmc_add(pz,0-az,s),ux,uy,uz,s) 322 c[0]=ax;c[1]=ay;c[2]=az 323 if uu>0 && dot>0 { 324 if dot>=uu {c[0]=gmc_add(ax,ux,s);c[1]=gmc_add(ay,uy,s);c[2]=gmc_add(az,uz,s)} 325 else {c[0]=gmc_add(ax,gmc_round(gmc_mul(ux,dot,s),uu,s),s);c[1]=gmc_add(ay,gmc_round(gmc_mul(uy,dot,s),uu,s),s);c[2]=gmc_add(az,gmc_round(gmc_mul(uz,dot,s),uu,s),s)} 326 } 327 return s[0] 328} 329func gm_capsule_project_checked(verts:*i64,vel:*i64,nvv:i64, 330 ax:i64,ay:i64,az:i64,bx:i64,by:i64,bz:i64,rad:i64, 331 pins:*i64,npins:i64,cvx:i64,cvy:i64,cvz:i64,work:*i64,work_words:i64)->i64 { 332 if nvv<1 || nvv>(GMC_MAX/8-4)/6 || npins<0 || npins>nvv || rad<0 {return 0-1} 333 let need:i64=4+nvv*6 334 if work_words<need || (work as i64)<=0 || (verts as i64)<=0 || (vel as i64)<=0 {return 0-1} 335 if npins>0 {if (pins as i64)<=0 {return 0-1}} 336 if ax<0-GMC_MAX || ay<0-GMC_MAX || az<0-GMC_MAX || bx<0-GMC_MAX || by<0-GMC_MAX || bz<0-GMC_MAX {return 0-2} 337 if cvx<0-GMC_MAX || cvy<0-GMC_MAX || cvz<0-GMC_MAX {return 0-2} 338 if gmc_overlap(work as i64,need*8,verts as i64,nvv*24)==1 || gmc_overlap(work as i64,need*8,vel as i64,nvv*24)==1 || gmc_overlap(work as i64,need*8,pins as i64,npins*8)==1 {return 0-1} 339 if gmc_overlap(verts as i64,nvv*24,vel as i64,nvv*24)==1 {return 0-1} 340 work[0]=0 341 let q:*i64=((work as i64)+8) as *i64 342 let w:*i64=((q as i64)+nvv*24) as *i64 343 let cp:*i64=((w as i64)+nvv*24) as *i64 344 var i:i64=0 345 while i<npins {if pins[i]<0 || pins[i]>=nvv {return 0-1};i=i+1} 346 i=0 347 while i<nvv*3 {if verts[i]<0-GMC_MAX || vel[i]<0-GMC_MAX {return 0-2};q[i]=verts[i];w[i]=vel[i];i=i+1} 348 let ux:i64=gmc_add(bx,0-ax,work);let uy:i64=gmc_add(by,0-ay,work);let uz:i64=gmc_add(bz,0-az,work) 349 let uu:i64=gmc_dot(ux,uy,uz,ux,uy,uz,work) 350 let rr:i64=gmc_mul(rad,rad,work) 351 let rs:i64=gmc_add(rad,GM_SKIN,work) 352 if work[0]!=0 {return work[0]} 353 var hits:i64=0 354 i=0 355 while i<nvv { 356 gmc_closest(q[i*3],q[i*3+1],q[i*3+2],ax,ay,az,ux,uy,uz,uu,cp,work) 357 var dx:i64=gmc_add(q[i*3],0-cp[0],work);var dy:i64=gmc_add(q[i*3+1],0-cp[1],work);var dz:i64=gmc_add(q[i*3+2],0-cp[2],work) 358 var dd:i64=gmc_dot(dx,dy,dz,dx,dy,dz,work) 359 if work[0]!=0 {return work[0]} 360 if dd<rr { 361 var p:i64=0 362 while p<npins {if pins[p]==i {return 0-6};p=p+1} 363 if dd==0 { 364 if uu==0 {dx=1;dy=0;dz=0} 365 else { 366 if st_abs(ux)<=st_abs(uy) && st_abs(ux)<=st_abs(uz) {dx=0;dy=uz;dz=0-uy} 367 else {if st_abs(uy)<=st_abs(uz) {dx=0-uz;dy=0;dz=ux} else {dx=uy;dy=0-ux;dz=0}} 368 } 369 dd=gmc_dot(dx,dy,dz,dx,dy,dz,work) 370 } 371 let d:i64=st_isqrt(dd) 372 if d<=0 {return 0-4} 373 q[i*3]=gmc_add(cp[0],gmc_mul(dx,rs,work)/d,work) 374 q[i*3+1]=gmc_add(cp[1],gmc_mul(dy,rs,work)/d,work) 375 q[i*3+2]=gmc_add(cp[2],gmc_mul(dz,rs,work)/d,work) 376 let rx:i64=gmc_add(w[i*3],0-cvx,work);let ry:i64=gmc_add(w[i*3+1],0-cvy,work);let rz:i64=gmc_add(w[i*3+2],0-cvz,work) 377 let vn:i64=gmc_dot(rx,ry,rz,dx,dy,dz,work) 378 if vn<0 { 379 w[i*3]=gmc_add(w[i*3],0-gmc_round(gmc_mul(dx,vn,work),dd,work),work) 380 w[i*3+1]=gmc_add(w[i*3+1],0-gmc_round(gmc_mul(dy,vn,work),dd,work),work) 381 w[i*3+2]=gmc_add(w[i*3+2],0-gmc_round(gmc_mul(dz,vn,work),dd,work),work) 382 } 383 hits=hits+1 384 } 385 if work[0]!=0 {return work[0]} 386 i=i+1 387 } 388 i=0 389 while i<nvv*3 {verts[i]=q[i];vel[i]=w[i];i=i+1} 390 return hits 391} 392func gm_capsule_project(verts:*i64,vel:*i64,nvv:i64,ax:i64,ay:i64,az:i64,bx:i64,by:i64,bz:i64,rad:i64)->i64 { 393 if nvv<1 || nvv>(GMC_MAX/8-4)/6 {return 0-1} 394 let bytes:i64=(4+nvv*6)*8 395 let work:*i64=sys_mmap_shared(bytes) as *i64 396 if (work as i64)<=0 {return 0-3} 397 let rc:i64=gm_capsule_project_checked(verts,vel,nvv,ax,ay,az,bx,by,bz,rad,0 as *i64,0,0,0,0,work,bytes/8) 398 let closed:i64=sys_munmap(work as *u8,bytes) 399 if rc<0 {return rc} 400 if closed!=0 {return 0-5} 401 return rc 402}