code wiki / (root) / nx_garment_boundary_t138.nx

nx_garment_boundary_t138.nx source

↩ module page · 1539 lines · 71905 B

1// nx_garment_gen_lib.nx -- CONFORMING GARMENT MESHES GENERATED FROM THE BODY SURFACE (2026-08-30, 2// gameengine gpe_garment_draw paint->mesh; operator: "the clothes are paint not objects"). 3// 4// WHAT IT BUILDS. For every garment id of the engine's table (1 bikini top . 2 bikini bottom . 3 one-piece 5// . 4 sarong knot . 5 sundress bodice . 6 tee . 7 shorts . 8 skirt waistband . 9 evening bodice) a SHELL: 6// the body triangles the garment covers, offset along the body's own smooth vertex normals by fabric 7// thickness + wearing ease, its open boundary CLOSED by a hem strip (the fabric's edge face, so the cloth 8// has a visible thickness), a raised binding band one ring inside every opening (the elastic / folded 9// hem), a PUSH-OUT pass that walks any vertex the offset dropped into a fold back out until no body vertex 10// sits inside the fabric, and RIBBON STRAPS between anatomical anchors for the strapped ids. Every garment 11// vertex records the BODY VERTEX it was grown from, so the page skins it with that vertex's own joint 12// weights and it rides pose, gait and the soft-tissue fields exactly as the skin under it does 13// (layering by construction: a layer that rides its underlayer's field cannot be pierced by it). 14// 15// UNITS. Mesh units: 171530 per 1.78 m (the page's own pairing) => 96.37 units per mm. Heights are in 16// DECIMIL of the mesh's own measured stature ((z - zmin) * 10000 / H). Normals and blend weights are Q12. 17// Thickness and ease are DERIVED from micrometres in gmg_mat, with the source of each number beside it. 18// 19// SECTION EMITTED: "GARM" (one tag, one checksum) = 20// header [ver=20260830, nkinds, vstride=5, tstride=3, units_per_mm_x1000, dirstride=8, total_words, 0] 21// dir nkinds x [id, nverts, ntris, voff(words from section start), toff, thickness_units, ease_units, class] 22// verts [x, y, z, src_body_vertex, flag] flag 0 shell . 1 hem . 2 strap 23// tris [a, b, c] local to the kind 24// Normals are NOT shipped: the page already accumulates area-weighted smooth normals for the skirt mesh 25// and does the same here (hem strips and straps carry their own copies of the shared vertices, so their 26// faces get their own normals). license_tier: ORIGINAL. No hw writes (Rule 26). 27import "nx_syscalls.nx" 28import "nx_nxa.nx" 29import "nx_nxa_garment_bind_lib.nx" 30 31const GMG_Q: i64 = 4096 32const GMG_DM: i64 = 10000 33const GMG_UNITS_178: i64 = 171530 34const GMG_MM_178: i64 = 1780 35const GMG_UM_PER_MM: i64 = 1000 36const GMG_TWO_PI_MILLI: i64 = 6283 37const GMG_KINDS: i64 = 9 38const GMG_VSTRIDE: i64 = 5 39const GMG_TSTRIDE: i64 = 3 40const GMG_DIRSTRIDE: i64 = 8 41const GMG_HDR: i64 = 8 42const GMG_GARM_VER: i64 = 20260830 43const GMG_NBR_CAP: i64 = 32 44const GMG_BADJ_CAP: i64 = 4 45const GMG_OV_CAP: i64 = 16384 46const GMG_OT_CAP: i64 = 32768 47const GMG_SEC_CAP: i64 = 262144 48const GMG_STAT_N: i64 = 32 49const GMG_PUSH_PASSES: i64 = 8 50const GMG_SKEL_STRIDE: i64 = 8 51const GMG_SKIN_STRIDE: i64 = 8 52const GMG_DYN_FIELDS: i64 = 8 53const GMG_STRAP_K: i64 = 9 54const GMG_STRAP_CAP: i64 = 24 55const GMG_BIG: i64 = 4611686018427387903 56const GMG_ISQRT_HI: i64 = 3037000499 57const GMG_CROSS_SHIFT: i64 = 64 58const GMG_NRM_CAP: i64 = 1000000 59 60// flags 61const GMG_F_SHELL: i64 = 0 62const GMG_F_HEM: i64 = 1 63const GMG_F_STRAP: i64 = 2 64// material classes (the page's fabric BRDF keys on these) 65const GMG_M_SWIM: i64 = 1 66const GMG_M_WOVEN: i64 = 2 67const GMG_M_JERSEY: i64 = 3 68const GMG_M_SATIN: i64 = 4 69// refusals (negative returns; the program maps them to exit codes and NAMES them) 70const GMG_ERR_BADNXA: i64 = 0 - 5 71const GMG_ERR_REFUSED_THICK: i64 = 0 - 4 72const GMG_ERR_EMPTY: i64 = 0 - 6 73const GMG_ERR_CAP: i64 = 0 - 7 74 75// ---- anatomy (decimil of stature unless noted) -- every number names its source ---- 76const GMG_TORSO_HALFW: i64 = 15000 // the page shader's own torso gate (|bind x| < 15000) 77const GMG_ARM_BIND_X: i64 = 12000 // a dominant joint this far off the midline is an arm joint 78const GMG_HEAD_DM: i64 = 8600 // above the chin line (.885 H) a dominant joint is a head joint 79const GMG_FRONT_Y: i64 = 0 - 1200 // the page's bust-side convention: front is y < -1200 80const GMG_YBLEND: i64 = 1200 // front/back blend half-width (units) for the leg/neck lines 81const GMG_HIP_DM: i64 = 5450 // hip band top (.545 H), the incumbent paint band's own edge 82const GMG_CROTCH_DM: i64 = 4750 // crotch .475 H (Drillis-Contini 1966) 83const GMG_KNEE_DM: i64 = 2850 // knee .285 H (Drillis-Contini 1966, the page's own gdJn(.285)) 84const GMG_HIPJ_DM: i64 = 5200 // hip joint .52 H (Drillis-Contini) -- the anchor the page maps NIP through 85const GMG_EYE_DM: i64 = 9300 // eye line .93 H, the second anchor 86const GMG_NIP_RATIO_DM: i64 = 7200 // published nipple ratio .72 H before the per-mesh anchor mapping 87const GMG_HEADJ_PROBE_DM: i64 = 9500 88const GMG_BUST_HALF_DM: i64 = 550 // the nipple detail's own half-width (.055 H) 89const GMG_EASE_FADE_LO: i64 = 7400 // ease fades to zero over the bust->shoulder band so necklines hug 90const GMG_EASE_FADE_HI: i64 = 8000 91const GMG_MIDTHIGH_DM: i64 = 4025 // (hip .52 + knee .285) / 2 -- Drillis-Contini mid-thigh, the shorts hem 92const GMG_SHORTS_TOP_DM: i64 = 5600 93const GMG_TEE_NECK_DM: i64 = 8350 // crew neck front: .835 H (acromion .818 + a finger) 94const GMG_TEE_NECK_RISE: i64 = 300 95const GMG_SLEEVE_TOP_DM: i64 = 7400 96const GMG_SLEEVE_HALFW: i64 = 19000 // short-sleeve cap reach off the midline (units) 97const GMG_SLEEVE_BAND_LO: i64 = 7800 98const GMG_SLEEVE_BAND_HI: i64 = 8700 99const GMG_SLEEVE_X_LO: i64 = 12000 100const GMG_ONEPIECE_NECK_F: i64 = 7700 // one-piece scoop front .77 H 101const GMG_ONEPIECE_NECK_B: i64 = 7000 // one-piece back .70 H 102const GMG_STRAP_SHOULDER_DM: i64 = 8580 103const GMG_STRAP_BAND_TOL_DM: i64 = 120 // bikini back band half-height 104const GMG_SARONG_X: i64 = 13500 // the knot sits on the hip side 105const GMG_SARONG_DM: i64 = 6150 106const GMG_SARONG_R: i64 = 5000 107const GMG_WAIST_BAND_DM: i64 = 150 // skirt waistband half-height around the skirt's own top ring 108const GMG_SKIRT_TOP_TOL_DM: i64 = 300 109const GMG_STRAP_SEARCH_DM: i64 = 700 110const GMG_STRAP_AX_MAX: i64 = 14500 111const GMG_STRAP_AX_FRONT: i64 = 9000 112const GMG_STRAP_Y_FRONT: i64 = 0 - 9000 113const GMG_STRAP_AX_SHOULDER: i64 = 11400 114const GMG_STRAP_Y_SHOULDER: i64 = 2000 115const GMG_STRAP_AX_BACK: i64 = 8000 116const GMG_STRAP_Y_BACK: i64 = 9000 117const GMG_STRAP_HALFW_BIKINI_UM: i64 = 6000 // 12 mm bikini strap 118const GMG_STRAP_HALFW_DRESS_UM: i64 = 10000 // 20 mm dress / one-piece strap 119const GMG_SWIM_INNER_UM: i64 = 200 // a swim hem's inner edge is pressed to the skin (0.2 mm) 120const GMG_PUSH_MARGIN: i64 = 2 121const GMG_CLEAR_PERMIL: i64 = 900 // the clearance tooth's bar: 0.9 t (vertex-to-vertex is an 122 // UPPER bound on vertex-to-surface -- declared imprecision) 123const GMG_CLEAR_REPORT_MULT: i64 = 4 124 125// ---- fabric table sources (all DECLARED until a bench swatch measures the real blanks; garment_twin.conf 126// carries the same honesty). Thickness: ASTM D1777 handbook classes -- nylon/elastane swim tricot ~0.55 mm, 127// cotton single jersey (160-180 g/m2) ~0.7 mm, cotton poplin ~0.35 mm, polyester charmeuse ~0.25 mm. 128// Ease: Aldrich, Metric Pattern Cutting for Women's Wear; Armstrong, Patternmaking for Fashion Design -- 129// a basic tee carries ~7.5 cm of bust ease, a trouser/short block ~7.6 cm (3 in) of hip ease, an evening 130// (fitted) block ~6.4 cm (2.5 in); two-way-stretch swimwear is cut with NEGATIVE ease and sits at skin + 131// fabric, so its radial ease is zero. Hem: a swim opening carries a ~1.0 mm elastic under two plies; a 132// woven/jersey hem is a two-ply fold. Circumferential ease becomes radial ease by / 2 pi. 133const GMG_T_SWIM_UM: i64 = 550 134const GMG_T_JERSEY_UM: i64 = 700 135const GMG_T_WOVEN_UM: i64 = 350 136const GMG_T_SARONG_UM: i64 = 300 137const GMG_T_SATIN_UM: i64 = 250 138const GMG_ELASTIC_UM: i64 = 1000 139const GMG_EASE_TEE_UM: i64 = 75000 140const GMG_EASE_SHORTS_UM: i64 = 76200 141const GMG_EASE_DRESS_UM: i64 = 75000 142const GMG_EASE_EVENING_UM: i64 = 63500 143const GMG_EASE_KNOT_RADIAL_UM: i64 = 30000 144 145// ---- context slots (one *i64 block; pointers stored as i64) ---- 146const GMG_C_B: i64 = 0 147const GMG_C_FLEN: i64 = 1 148const GMG_C_POS: i64 = 2 149const GMG_C_NV: i64 = 3 150const GMG_C_IDX: i64 = 4 151const GMG_C_NT: i64 = 5 152const GMG_C_NRM: i64 = 6 153const GMG_C_ZMN: i64 = 7 154const GMG_C_H: i64 = 8 155const GMG_C_HH: i64 = 9 156const GMG_C_AX: i64 = 10 157const GMG_C_VF: i64 = 11 158const GMG_C_NIP: i64 = 12 159const GMG_C_DYN: i64 = 13 160const GMG_C_ND: i64 = 14 161const GMG_C_GTH: i64 = 15 162const GMG_C_GAP: i64 = 16 163const GMG_C_YMID: i64 = 17 164const GMG_C_BIND: i64 = 18 165const GMG_C_NJ: i64 = 19 166const GMG_C_VMAP: i64 = 20 167const GMG_C_INNER: i64 = 21 168const GMG_C_SEL: i64 = 22 169const GMG_C_NBR: i64 = 23 170const GMG_C_NBC: i64 = 24 171const GMG_C_DEG: i64 = 25 172const GMG_C_OV: i64 = 26 173const GMG_C_ONV: i64 = 27 174const GMG_C_OT: i64 = 28 175const GMG_C_ONT: i64 = 29 176const GMG_C_CAND: i64 = 30 177const GMG_C_NCAND: i64 = 31 178const GMG_C_TRIS: i64 = 32 179const GMG_C_NTRIS: i64 = 33 180const GMG_C_THICK_PERMIL: i64 = 34 181const GMG_C_UPMK: i64 = 35 182const GMG_C_BSET: i64 = 36 183const GMG_C_RING: i64 = 37 184const GMG_C_BADJ: i64 = 38 185const GMG_C_BDEG: i64 = 39 186const GMG_C_STACK: i64 = 40 187const GMG_C_SEEN: i64 = 41 188const GMG_C_NOPUSH: i64 = 42 189const GMG_C_NBROVER: i64 = 43 190const GMG_C_AJ: i64 = 44 191const GMG_C_AW: i64 = 45 192const GMG_C_DST: i64 = 46 // the asset's own DYNA row stride (ref9d ships 12 words, not the 8 the 193 // first cut demanded -- an exact-stride check read a present section as absent) 194const GMG_C_DIL: i64 = 47 // per-vertex pinch-dilation flags (see gmg_build steps 1-3) 195const GMG_C_SLOTS: i64 = 48 196const GMG_PINCH_PASSES: i64 = 4 197// vertex flag bits (GMG_C_VF) 198const GMG_VF_TOR: i64 = 1 199const GMG_VF_ARM: i64 = 2 200const GMG_VF_HEAD: i64 = 4 201// stat slots 202const GMG_S_NV: i64 = 0 203const GMG_S_NT: i64 = 1 204const GMG_S_SHELL: i64 = 2 205const GMG_S_HEM: i64 = 3 206const GMG_S_BEDGES: i64 = 4 207const GMG_S_PUSHED: i64 = 5 208const GMG_S_STRAPS: i64 = 6 209const GMG_S_MINCLEAR: i64 = 7 210const GMG_S_LOOPS: i64 = 8 211const GMG_S_OPEN: i64 = 9 212const GMG_S_SEL: i64 = 10 213const GMG_S_T: i64 = 11 214const GMG_S_TH: i64 = 12 215const GMG_S_EASE: i64 = 13 216const GMG_S_CLASS: i64 = 14 217const GMG_S_WITHIN: i64 = 15 218const GMG_S_SLF: i64 = 16 219const GMG_S_SLB: i64 = 17 220const GMG_S_SRF: i64 = 18 221const GMG_S_SRB: i64 = 19 222const GMG_S_NBROVER: i64 = 20 223const GMG_S_VOFF: i64 = 21 224const GMG_S_TOFF: i64 = 22 225const GMG_S_ID: i64 = 23 226const GMG_S_PINCHPASS: i64 = 24 // selection passes taken by the pinch dilation (1 = no pinch found) 227 228func gmg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 229func gmg_pn(v: i64) -> i64 { 230 let t: *u8 = sys_mmap(32) as *u8 231 var m: i64 = v 232 var w: i64 = 0 233 if m < 0 { t[w] = 45 as u8; w = w + 1; m = 0 - m } 234 if m == 0 { t[w] = 48 as u8; sys_write(1, t, w + 1); return 0 } 235 let d: *u8 = sys_mmap(32) as *u8 236 var k: i64 = 0 237 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 238 var j: i64 = 0 239 while j < k { t[w] = d[k - 1 - j]; w = w + 1; j = j + 1 } 240 sys_write(1, t, w) 241 return 0 242} 243func gmg_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 244func gmg_min(a: i64, b: i64) -> i64 { if a < b { return a } return b } 245func gmg_max(a: i64, b: i64) -> i64 { if a > b { return a } return b } 246// floor(sqrt(v)) by Newton from a power-of-two upper bound; v < 0 reads as 0 247func gmg_isqrt(v: i64) -> i64 { 248 if v <= 0 { return 0 } 249 var g: i64 = 1 250 var grow: i64 = 1 251 while grow == 1 { 252 if g >= GMG_ISQRT_HI { grow = 0 } else { 253 if g * g >= v { grow = 0 } else { g = g * 2 } 254 } 255 } 256 var y: i64 = g 257 var it: i64 = 0 258 while it < 80 { 259 let z: i64 = (y + v / y) / 2 260 if z >= y { it = 80 } else { y = z } 261 it = it + 1 262 } 263 return y 264} 265// smoothstep(a, b, x) in Q12: 0 below a, Q above b 266func gmg_sst(a: i64, b: i64, x: i64) -> i64 { 267 if b <= a { if x >= b { return GMG_Q } return 0 } 268 var t: i64 = (x - a) * GMG_Q / (b - a) 269 if t < 0 { t = 0 } 270 if t > GMG_Q { t = GMG_Q } 271 return (t * t / GMG_Q) * (3 * GMG_Q - 2 * t) / GMG_Q 272} 273// micrometres -> mesh units through the page's own 1.78 m = 171530 pairing 274func gmg_um2u(um: i64) -> i64 { return um * GMG_UNITS_178 / (GMG_MM_178 * GMG_UM_PER_MM) } 275func gmg_circ2radial_um(circ_um: i64) -> i64 { return circ_um * 1000 / GMG_TWO_PI_MILLI } 276 277// gmg_mat: the fabric table per garment id. out[0] thickness units . out[1] hem (edge face) units . 278// out[2] radial ease units . out[3] material class. Thickness and hem scale with the ctx thick permil 279// (1000 = derived; 0 = the neg-control that must be REFUSED); ease never does. 280func gmg_mat(G: *i64, kind: i64, out: *i64) -> i64 { 281 var t: i64 = 0 282 var th: i64 = 0 283 var e: i64 = 0 284 var cl: i64 = 0 285 if kind == 1 { t = GMG_T_SWIM_UM; th = 2 * GMG_T_SWIM_UM + GMG_ELASTIC_UM; e = 0; cl = GMG_M_SWIM } 286 if kind == 2 { t = GMG_T_SWIM_UM; th = 2 * GMG_T_SWIM_UM + GMG_ELASTIC_UM; e = 0; cl = GMG_M_SWIM } 287 if kind == 3 { t = GMG_T_SWIM_UM; th = 2 * GMG_T_SWIM_UM + GMG_ELASTIC_UM; e = 0; cl = GMG_M_SWIM } 288 if kind == 4 { t = GMG_T_SARONG_UM; th = 2 * GMG_T_SARONG_UM; e = GMG_EASE_KNOT_RADIAL_UM; cl = GMG_M_WOVEN } 289 if kind == 5 { t = GMG_T_WOVEN_UM; th = 2 * GMG_T_WOVEN_UM; e = gmg_circ2radial_um(GMG_EASE_DRESS_UM); cl = GMG_M_WOVEN } 290 if kind == 6 { t = GMG_T_JERSEY_UM; th = 2 * GMG_T_JERSEY_UM; e = gmg_circ2radial_um(GMG_EASE_TEE_UM); cl = GMG_M_JERSEY } 291 if kind == 7 { t = GMG_T_WOVEN_UM; th = 2 * GMG_T_WOVEN_UM; e = gmg_circ2radial_um(GMG_EASE_SHORTS_UM); cl = GMG_M_WOVEN } 292 if kind == 8 { t = GMG_T_WOVEN_UM; th = 2 * GMG_T_WOVEN_UM; e = 0; cl = GMG_M_WOVEN } 293 if kind == 9 { t = GMG_T_SATIN_UM; th = 2 * GMG_T_SATIN_UM; e = gmg_circ2radial_um(GMG_EASE_EVENING_UM); cl = GMG_M_SATIN } 294 if cl == 0 { return 0 - 1 } 295 let pm: i64 = G[GMG_C_THICK_PERMIL] 296 out[0] = gmg_um2u(t) * pm / 1000 297 out[1] = gmg_um2u(th) * pm / 1000 298 out[2] = gmg_um2u(e) 299 // the waistband's ease is MEASURED: the gap between the skirt's own top ring and the body 300 if kind == 8 { out[2] = G[GMG_C_GAP] } 301 out[3] = cl 302 return 1 303} 304 305func gmg_pos(G: *i64) -> *i64 { return G[GMG_C_POS] as *i64 } 306func gmg_nrm(G: *i64) -> *i64 { return G[GMG_C_NRM] as *i64 } 307func gmg_hh(G: *i64) -> *i64 { return G[GMG_C_HH] as *i64 } 308func gmg_ax(G: *i64) -> *i64 { return G[GMG_C_AX] as *i64 } 309func gmg_vf(G: *i64) -> *u8 { return G[GMG_C_VF] as *u8 } 310func gmg_has(G: *i64, v: i64, bit: i64) -> i64 { 311 let f: *u8 = gmg_vf(G) 312 if ((f[v] as i64) & bit) != 0 { return 1 } 313 return 0 314} 315 316// nearest joint to height f (decimil) with a midline preference (the page's near()) 317func gmg_near_joint(G: *i64, fdm: i64) -> i64 { 318 let bind: *i64 = G[GMG_C_BIND] as *i64 319 let nj: i64 = G[GMG_C_NJ] 320 let tz: i64 = G[GMG_C_ZMN] + fdm * G[GMG_C_H] / GMG_DM 321 var bj: i64 = 0 322 var bd: i64 = GMG_BIG 323 var j: i64 = 0 324 while j < nj { 325 let d: i64 = gmg_abs(bind[j * 3 + 2] - tz) + 2 * gmg_abs(bind[j * 3]) 326 if d < bd { bd = d; bj = j } 327 j = j + 1 328 } 329 return bj 330} 331 332// gmg_load: parse VERT/TRIS/SKEL/SKIN (+DYNA, +GVRT), measure the body, derive the landmarks, allocate the 333// scratch. Returns the context, or 0 with the refusal NAMED on stdout. 334func gmg_load(b: *u8, flen: i64) -> *i64 { 335 let vwo: i64 = nxa_find(b, flen, nxa_tag4("VERT" as *u8)) 336 let two: i64 = nxa_find(b, flen, nxa_tag4("TRIS" as *u8)) 337 let swo: i64 = nxa_find(b, flen, nxa_tag4("SKEL" as *u8)) 338 let kwo: i64 = nxa_find(b, flen, nxa_tag4("SKIN" as *u8)) 339 if vwo < 0 { gmg_puts("GMG-REFUSED bad nxa: VERT\n" as *u8); return 0 as *i64 } 340 if two < 0 { gmg_puts("GMG-REFUSED bad nxa: TRIS\n" as *u8); return 0 as *i64 } 341 if swo < 0 { gmg_puts("GMG-REFUSED bad nxa: SKEL\n" as *u8); return 0 as *i64 } 342 if kwo < 0 { gmg_puts("GMG-REFUSED bad nxa: SKIN\n" as *u8); return 0 as *i64 } 343 let h: *i64 = b as *i64 344 let nv: i64 = h[vwo] 345 let nt: i64 = h[two] 346 let nj: i64 = h[swo] 347 let nsv: i64 = h[kwo] 348 if nsv != nv { gmg_puts("GMG-REFUSED skin/vert desync\n" as *u8); return 0 as *i64 } 349 if nv < 3 { gmg_puts("GMG-REFUSED empty mesh\n" as *u8); return 0 as *i64 } 350 let G: *i64 = sys_mmap(GMG_C_SLOTS * 8) as *i64 351 var s0: i64 = 0 352 while s0 < GMG_C_SLOTS { G[s0] = 0; s0 = s0 + 1 } 353 G[GMG_C_B] = b as i64 354 G[GMG_C_FLEN] = flen 355 let pos: *i64 = ((b as i64) + (vwo + 1) * 8) as *i64 356 let idx: *i64 = ((b as i64) + (two + 1) * 8) as *i64 357 G[GMG_C_POS] = pos as i64 358 G[GMG_C_NV] = nv 359 G[GMG_C_IDX] = idx as i64 360 G[GMG_C_NT] = nt 361 G[GMG_C_THICK_PERMIL] = 1000 362 G[GMG_C_UPMK] = GMG_UNITS_178 * 1000 / GMG_MM_178 363 // bind positions 364 let bind: *i64 = sys_mmap(nj * 3 * 8 + 64) as *i64 365 var j: i64 = 0 366 while j < nj { 367 let bw: i64 = swo + 1 + j * GMG_SKEL_STRIDE 368 bind[j * 3] = h[bw + 1] 369 bind[j * 3 + 1] = h[bw + 2] 370 bind[j * 3 + 2] = h[bw + 3] 371 j = j + 1 372 } 373 G[GMG_C_BIND] = bind as i64 374 G[GMG_C_NJ] = nj 375 // skin rows (joint ids + Q12 weights) -- the page copies these per garment vertex 376 G[GMG_C_AJ] = ((b as i64) + (kwo + 1) * 8) 377 G[GMG_C_AW] = 0 378 // extents 379 var zmn: i64 = GMG_BIG 380 var zmx: i64 = 0 - GMG_BIG 381 var v: i64 = 0 382 while v < nv { 383 let z: i64 = pos[v * 3 + 2] 384 if z < zmn { zmn = z } 385 if z > zmx { zmx = z } 386 v = v + 1 387 } 388 var H: i64 = zmx - zmn 389 if H < 1 { H = 1 } 390 G[GMG_C_ZMN] = zmn 391 G[GMG_C_H] = H 392 // per-vertex height (decimil) and |x| 393 let hh: *i64 = sys_mmap(nv * 8 + 64) as *i64 394 let ax: *i64 = sys_mmap(nv * 8 + 64) as *i64 395 var ys: i64 = 0 396 var yc: i64 = 0 397 v = 0 398 while v < nv { 399 hh[v] = (pos[v * 3 + 2] - zmn) * GMG_DM / H 400 ax[v] = gmg_abs(pos[v * 3]) 401 if ax[v] < GMG_TORSO_HALFW { if hh[v] > 5000 { if hh[v] < 8000 { ys = ys + pos[v * 3 + 1]; yc = yc + 1 } } } 402 v = v + 1 403 } 404 G[GMG_C_HH] = hh as i64 405 G[GMG_C_AX] = ax as i64 406 var ymid: i64 = 0 407 if yc > 0 { ymid = ys / yc } 408 G[GMG_C_YMID] = ymid 409 // smooth vertex normals: area-weighted accumulation of face cross products (scaled down to keep 410 // the sums small), normalised to Q12, then the torso-column outwardness guard: a normal pointing 411 // INTO the column (against the radial from the axis) is flipped -- the mesh's winding is measured 412 // 96 percent consistent on the reference body, and the 4 percent would drop a shell inside the skin. 413 let acc: *i64 = sys_mmap(nv * 3 * 8 + 64) as *i64 414 var z0: i64 = 0 415 while z0 < nv * 3 { acc[z0] = 0; z0 = z0 + 1 } 416 var e: i64 = 0 417 while e < nt { 418 let a: i64 = idx[e * 3] 419 let bb: i64 = idx[e * 3 + 1] 420 let c: i64 = idx[e * 3 + 2] 421 let e1x: i64 = pos[bb * 3] - pos[a * 3] 422 let e1y: i64 = pos[bb * 3 + 1] - pos[a * 3 + 1] 423 let e1z: i64 = pos[bb * 3 + 2] - pos[a * 3 + 2] 424 let e2x: i64 = pos[c * 3] - pos[a * 3] 425 let e2y: i64 = pos[c * 3 + 1] - pos[a * 3 + 1] 426 let e2z: i64 = pos[c * 3 + 2] - pos[a * 3 + 2] 427 let fx: i64 = (e1y * e2z - e1z * e2y) / GMG_CROSS_SHIFT 428 let fy: i64 = (e1z * e2x - e1x * e2z) / GMG_CROSS_SHIFT 429 let fz: i64 = (e1x * e2y - e1y * e2x) / GMG_CROSS_SHIFT 430 acc[a * 3] = acc[a * 3] + fx; acc[a * 3 + 1] = acc[a * 3 + 1] + fy; acc[a * 3 + 2] = acc[a * 3 + 2] + fz 431 acc[bb * 3] = acc[bb * 3] + fx; acc[bb * 3 + 1] = acc[bb * 3 + 1] + fy; acc[bb * 3 + 2] = acc[bb * 3 + 2] + fz 432 acc[c * 3] = acc[c * 3] + fx; acc[c * 3 + 1] = acc[c * 3 + 1] + fy; acc[c * 3 + 2] = acc[c * 3 + 2] + fz 433 e = e + 1 434 } 435 let nrm: *i64 = sys_mmap(nv * 3 * 8 + 64) as *i64 436 v = 0 437 while v < nv { 438 var nx: i64 = acc[v * 3] 439 var ny: i64 = acc[v * 3 + 1] 440 var nz: i64 = acc[v * 3 + 2] 441 var m: i64 = gmg_max(gmg_abs(nx), gmg_max(gmg_abs(ny), gmg_abs(nz))) 442 while m > GMG_NRM_CAP { nx = nx / 16; ny = ny / 16; nz = nz / 16; m = m / 16 } 443 let ln: i64 = gmg_isqrt(nx * nx + ny * ny + nz * nz) 444 if ln < 1 { nx = 0; ny = 0; nz = GMG_Q } else { nx = nx * GMG_Q / ln; ny = ny * GMG_Q / ln; nz = nz * GMG_Q / ln } 445 if ax[v] < GMG_TORSO_HALFW { if hh[v] > 4000 { if hh[v] < 8000 { 446 let rx: i64 = pos[v * 3] 447 let ry: i64 = pos[v * 3 + 1] - ymid 448 if nx * rx + ny * ry < 0 { nx = 0 - nx; ny = 0 - ny; nz = 0 - nz } 449 } } } 450 nrm[v * 3] = nx; nrm[v * 3 + 1] = ny; nrm[v * 3 + 2] = nz 451 v = v + 1 452 } 453 G[GMG_C_NRM] = nrm as i64 454 // dominant joint per vertex -> arm / head flags; torso-column flag 455 let vf: *u8 = sys_mmap(nv + 64) as *u8 456 let skin: *i64 = G[GMG_C_AJ] as *i64 457 v = 0 458 while v < nv { 459 let row: i64 = v * GMG_SKIN_STRIDE 460 var k: i64 = 0 461 var q: i64 = 1 462 while q < 4 { if skin[row + 4 + q] > skin[row + 4 + k] { k = q } q = q + 1 } 463 var dj: i64 = skin[row + k] 464 if dj < 0 { dj = 0 } 465 if dj >= nj { dj = 0 } 466 var f: i64 = 0 467 if gmg_abs(bind[dj * 3]) > GMG_ARM_BIND_X { f = f | GMG_VF_ARM } 468 if (bind[dj * 3 + 2] - zmn) * GMG_DM / H > GMG_HEAD_DM { f = f | GMG_VF_HEAD } 469 if ax[v] < GMG_TORSO_HALFW { if (f & (GMG_VF_ARM | GMG_VF_HEAD)) == 0 { f = f | GMG_VF_TOR } } 470 vf[v] = f as u8 471 v = v + 1 472 } 473 G[GMG_C_VF] = vf as i64 474 // NIP: the published .72 ratio mapped through THIS mesh's pelvis (.52) and head (.95) anchors -- the 475 // page's own derivation, in permil-then-decimil so the two agree to the rounding 476 let pj: i64 = gmg_near_joint(G, GMG_HIPJ_DM) 477 let hj: i64 = gmg_near_joint(G, GMG_HEADJ_PROBE_DM) 478 let pv: i64 = ((bind[pj * 3 + 2] - zmn) * 1000 + H / 2) / H * 10 479 let hd: i64 = ((bind[hj * 3 + 2] - zmn) * 1000 + H / 2) / H * 10 480 var nip: i64 = GMG_NIP_RATIO_DM 481 let sl: i64 = (hd - pv) * GMG_Q / (GMG_EYE_DM - GMG_HIPJ_DM) 482 if sl > GMG_Q * 4 / 10 { if sl < GMG_Q * 16 / 10 { nip = pv + sl * (GMG_NIP_RATIO_DM - GMG_HIPJ_DM) / GMG_Q } } 483 G[GMG_C_NIP] = nip 484 // DYNA anchors (optional): rows of 8 words [side,x,y,z,k,c,max,infl] 485 let dwo: i64 = nxa_find(b, flen, nxa_tag4("DYNA" as *u8)) 486 if dwo >= 0 { 487 let ndb: i64 = h[dwo] 488 let dst: i64 = h[dwo + 1] 489 if dst >= GMG_DYN_FIELDS { G[GMG_C_DYN] = ((b as i64) + (dwo + 2) * 8); G[GMG_C_ND] = ndb; G[GMG_C_DST] = dst } 490 } 491 // the skirt (GVRT) top ring: its height and its mean gap to the body -- the waistband's ease 492 G[GMG_C_GTH] = 0 - 1 493 G[GMG_C_GAP] = 0 494 let gwo: i64 = nxa_find(b, flen, nxa_tag4("GVRT" as *u8)) 495 if gwo >= 0 { 496 let gnv: i64 = h[gwo] 497 let gp: *i64 = ((b as i64) + (gwo + 1) * 8) as *i64 498 var gtop: i64 = 0 - GMG_BIG 499 var i: i64 = 0 500 while i < gnv { if gp[i * 3 + 2] > gtop { gtop = gp[i * 3 + 2] } i = i + 1 } 501 let gth: i64 = (gtop - zmn) * GMG_DM / H 502 var gsum: i64 = 0 503 var gcnt: i64 = 0 504 i = 0 505 while i < gnv { 506 if gmg_abs(gp[i * 3 + 2] - gtop) < 2 { 507 var bd: i64 = GMG_BIG 508 var u: i64 = 0 509 while u < nv { 510 if gmg_abs(hh[u] - gth) <= GMG_SKIRT_TOP_TOL_DM { 511 let dx: i64 = pos[u * 3] - gp[i * 3] 512 let dy: i64 = pos[u * 3 + 1] - gp[i * 3 + 1] 513 let dz: i64 = pos[u * 3 + 2] - gp[i * 3 + 2] 514 let dd: i64 = dx * dx + dy * dy + dz * dz 515 if dd < bd { bd = dd } 516 } 517 u = u + 1 518 } 519 if bd < GMG_BIG { gsum = gsum + gmg_isqrt(bd); gcnt = gcnt + 1 } 520 } 521 i = i + 1 522 } 523 G[GMG_C_GTH] = gth 524 if gcnt > 0 { G[GMG_C_GAP] = gsum / gcnt } 525 } 526 // scratch 527 G[GMG_C_VMAP] = sys_mmap(nv * 8 + 64) as i64 528 G[GMG_C_INNER] = sys_mmap(nv * 8 + 64) as i64 529 G[GMG_C_SEL] = sys_mmap(nv + 64) as i64 530 G[GMG_C_BSET] = sys_mmap(nv + 64) as i64 531 G[GMG_C_RING] = sys_mmap(nv + 64) as i64 532 G[GMG_C_SEEN] = sys_mmap(nv + 64) as i64 533 G[GMG_C_DIL] = sys_mmap(nv + 64) as i64 534 G[GMG_C_NBR] = sys_mmap(nv * GMG_NBR_CAP * 8 + 64) as i64 535 G[GMG_C_NBC] = sys_mmap(nv * GMG_NBR_CAP * 8 + 64) as i64 536 G[GMG_C_DEG] = sys_mmap(nv * 8 + 64) as i64 537 G[GMG_C_BADJ] = sys_mmap(nv * GMG_BADJ_CAP * 8 + 64) as i64 538 G[GMG_C_BDEG] = sys_mmap(nv * 8 + 64) as i64 539 G[GMG_C_STACK] = sys_mmap(nv * 8 + 64) as i64 540 G[GMG_C_CAND] = sys_mmap(nv * 8 + 64) as i64 541 G[GMG_C_TRIS] = sys_mmap(nt * 8 + 64) as i64 542 G[GMG_C_OV] = sys_mmap(GMG_OV_CAP * GMG_VSTRIDE * 8 + 64) as i64 543 G[GMG_C_OT] = sys_mmap(GMG_OT_CAP * GMG_TSTRIDE * 8 + 64) as i64 544 return G 545} 546 547// nearest body vertex to (x,y,z) inside a height band, torso/limb only (no arm, no head), |x| bounded 548func gmg_near_body(G: *i64, x: i64, y: i64, z: i64, hlo: i64, hhi: i64, axmax: i64) -> i64 { 549 let pos: *i64 = gmg_pos(G) 550 let hh: *i64 = gmg_hh(G) 551 let ax: *i64 = gmg_ax(G) 552 let vf: *u8 = gmg_vf(G) 553 let nv: i64 = G[GMG_C_NV] 554 var bi: i64 = 0 - 1 555 var bd: i64 = GMG_BIG 556 var v: i64 = 0 557 while v < nv { 558 var ok: i64 = 1 559 if hh[v] < hlo { ok = 0 } 560 if hh[v] > hhi { ok = 0 } 561 if ax[v] > axmax { ok = 0 } 562 if ((vf[v] as i64) & (GMG_VF_ARM | GMG_VF_HEAD)) != 0 { ok = 0 } 563 if ok == 1 { 564 let dx: i64 = pos[v * 3] - x 565 let dy: i64 = pos[v * 3 + 1] - y 566 let dz: i64 = pos[v * 3 + 2] - z 567 let dd: i64 = dx * dx + dy * dy + dz * dz 568 if dd < bd { bd = dd; bi = v } 569 } 570 v = v + 1 571 } 572 return bi 573} 574 575// the leg opening line (decimil) at vertex v: crotch .475 H front rising with |x| toward the hip, higher 576// cut at the back (.445 -> .50), blended front-to-back over the y midline 577func gmg_hleg(G: *i64, v: i64) -> i64 { 578 let ax: *i64 = gmg_ax(G) 579 let pos: *i64 = gmg_pos(G) 580 var u: i64 = ax[v] * GMG_Q / GMG_TORSO_HALFW 581 if u > GMG_Q { u = GMG_Q } 582 let uu: i64 = u * u / GMG_Q 583 let f: i64 = GMG_CROTCH_DM + 250 * uu / GMG_Q 584 let bk: i64 = 4450 + 550 * uu / GMG_Q 585 return f + (bk - f) * gmg_sst(0 - GMG_YBLEND, GMG_YBLEND, pos[v * 3 + 1]) / GMG_Q 586} 587// the one-piece / sundress neckline (decimil): scoop front .77 H rising to .82 at the shoulder, back .70 588func gmg_hneck(G: *i64, v: i64) -> i64 { 589 let ax: *i64 = gmg_ax(G) 590 let pos: *i64 = gmg_pos(G) 591 var u: i64 = ax[v] * GMG_Q / GMG_TORSO_HALFW 592 if u > GMG_Q { u = GMG_Q } 593 let uu: i64 = u * u / GMG_Q 594 let f: i64 = GMG_ONEPIECE_NECK_F + 500 * uu / GMG_Q 595 let bk: i64 = GMG_ONEPIECE_NECK_B + 600 * uu / GMG_Q 596 return f + (bk - f) * gmg_sst(0 - GMG_YBLEND, GMG_YBLEND, pos[v * 3 + 1]) / GMG_Q 597} 598// the tee crew neck (decimil): .835 H, rising .03 H toward the shoulder 599func gmg_hneck_tee(G: *i64, v: i64) -> i64 { 600 let ax: *i64 = gmg_ax(G) 601 var t: i64 = (ax[v] - 3000) * GMG_Q / 9000 602 if t < 0 { t = 0 } 603 if t > GMG_Q { t = GMG_Q } 604 return GMG_TEE_NECK_DM + GMG_TEE_NECK_RISE * t / GMG_Q 605} 606func gmg_dyn_side(G: *i64, side: i64) -> i64 { 607 let nd: i64 = G[GMG_C_ND] 608 if nd == 0 { return 0 - 1 } 609 let dyn: *i64 = G[GMG_C_DYN] as *i64 610 let dst: i64 = G[GMG_C_DST] 611 var d: i64 = 0 612 while d < nd { 613 if side < 0 { if dyn[d * dst] < 0 { return d } } 614 if side > 0 { if dyn[d * dst] > 0 { return d } } 615 d = d + 1 616 } 617 return 0 - 1 618} 619func gmg_in_dyn(G: *i64, v: i64, d: i64) -> i64 { 620 if d < 0 { return 0 } 621 let dyn: *i64 = G[GMG_C_DYN] as *i64 622 let dst: i64 = G[GMG_C_DST] 623 let pos: *i64 = gmg_pos(G) 624 let dx: i64 = pos[v * 3] - dyn[d * dst + 1] 625 let dy: i64 = pos[v * 3 + 1] - dyn[d * dst + 2] 626 let dz: i64 = pos[v * 3 + 2] - dyn[d * dst + 3] 627 let r: i64 = dyn[d * dst + 7] 628 if dx * dx + dy * dy + dz * dz < r * r { return 1 } 629 return 0 630} 631 632// gmg_region: does garment `kind` cover body vertex v? 633func gmg_region(G: *i64, kind: i64, v: i64) -> i64 { 634 let hh: *i64 = gmg_hh(G) 635 let ax: *i64 = gmg_ax(G) 636 let pos: *i64 = gmg_pos(G) 637 let h: i64 = hh[v] 638 let tor: i64 = gmg_has(G, v, GMG_VF_TOR) 639 let arm: i64 = gmg_has(G, v, GMG_VF_ARM) 640 let head: i64 = gmg_has(G, v, GMG_VF_HEAD) 641 let nip: i64 = G[GMG_C_NIP] 642 if kind == 1 { 643 if tor == 0 { return 0 } 644 // the cups: the front bust regions inside the asset's OWN DYNA anchor radii; the back band: a 645 // narrow ring at the underbust line (NIP - .055 H) all the way round 646 if pos[v * 3 + 1] < GMG_FRONT_Y { 647 if gmg_in_dyn(G, v, gmg_dyn_side(G, 0 - 1)) == 1 { return 1 } 648 if gmg_in_dyn(G, v, gmg_dyn_side(G, 1)) == 1 { return 1 } 649 // DYNA-less asset: the bust band itself stands in for the cups (declared fallback) 650 if G[GMG_C_ND] == 0 { if gmg_abs(h - nip) < GMG_BUST_HALF_DM { return 1 } } 651 } 652 if gmg_abs(h - (nip - GMG_BUST_HALF_DM)) < GMG_STRAP_BAND_TOL_DM { return 1 } 653 return 0 654 } 655 if kind == 2 { if tor == 1 { if h < 5500 { if h > gmg_hleg(G, v) { return 1 } } } return 0 } 656 if kind == 3 { if tor == 1 { if h > gmg_hleg(G, v) { if h < gmg_hneck(G, v) { return 1 } } } return 0 } 657 if kind == 9 { if tor == 1 { if h > gmg_hleg(G, v) { if h < gmg_hneck(G, v) { return 1 } } } return 0 } 658 if kind == 4 { 659 let dx: i64 = pos[v * 3] - GMG_SARONG_X 660 let dy: i64 = pos[v * 3 + 1] 661 let dz: i64 = pos[v * 3 + 2] - (G[GMG_C_ZMN] + GMG_SARONG_DM * G[GMG_C_H] / GMG_DM) 662 if dx * dx + dy * dy + dz * dz < GMG_SARONG_R * GMG_SARONG_R { return 1 } 663 return 0 664 } 665 if kind == 5 { if tor == 1 { if h > GMG_HIP_DM { if h < gmg_hneck(G, v) { return 1 } } } return 0 } 666 if kind == 6 { 667 if tor == 1 { if h > GMG_HIP_DM { if h < gmg_hneck_tee(G, v) { return 1 } } } 668 if arm == 1 { if ax[v] < GMG_SLEEVE_HALFW { if h > GMG_SLEEVE_TOP_DM { return 1 } } } 669 if ax[v] > GMG_SLEEVE_X_LO { if ax[v] < GMG_SLEEVE_HALFW { if h > GMG_SLEEVE_BAND_LO { if h < GMG_SLEEVE_BAND_HI { if head == 0 { return 1 } } } } } 670 return 0 671 } 672 if kind == 7 { if ax[v] < GMG_TORSO_HALFW { if arm == 0 { if h > GMG_MIDTHIGH_DM { if h < GMG_SHORTS_TOP_DM { return 1 } } } } return 0 } 673 if kind == 8 { 674 if G[GMG_C_GTH] < 0 { return 0 } 675 if tor == 1 { if gmg_abs(h - G[GMG_C_GTH]) < GMG_WAIST_BAND_DM { return 1 } } 676 return 0 677 } 678 return 0 679} 680 681// radial ease at vertex v: the full ease below the bust, fading to zero across the bust->shoulder band so 682// necklines and straps sit on the body; the sarong knot and the waistband keep theirs everywhere 683func gmg_ease_at(G: *i64, kind: i64, mat: *i64, v: i64) -> i64 { 684 let e: i64 = mat[2] 685 if e == 0 { return 0 } 686 if kind == 4 { return e } 687 if kind == 8 { return e } 688 let hh: *i64 = gmg_hh(G) 689 return e * (GMG_Q - gmg_sst(GMG_EASE_FADE_LO, GMG_EASE_FADE_HI, hh[v])) / GMG_Q 690} 691 692// append one garment vertex grown from body vertex src at offset d along its normal; returns its index 693func gmg_addv(G: *i64, src: i64, d: i64, flag: i64) -> i64 { 694 let onv: i64 = G[GMG_C_ONV] 695 if onv >= GMG_OV_CAP { return 0 - 1 } 696 let ov: *i64 = G[GMG_C_OV] as *i64 697 let pos: *i64 = gmg_pos(G) 698 let nrm: *i64 = gmg_nrm(G) 699 let o: i64 = onv * GMG_VSTRIDE 700 ov[o] = pos[src * 3] + nrm[src * 3] * d / GMG_Q 701 ov[o + 1] = pos[src * 3 + 1] + nrm[src * 3 + 1] * d / GMG_Q 702 ov[o + 2] = pos[src * 3 + 2] + nrm[src * 3 + 2] * d / GMG_Q 703 ov[o + 3] = src 704 ov[o + 4] = flag 705 G[GMG_C_ONV] = onv + 1 706 return onv 707} 708func gmg_addv_at(G: *i64, x: i64, y: i64, z: i64, src: i64, flag: i64) -> i64 { 709 let onv: i64 = G[GMG_C_ONV] 710 if onv >= GMG_OV_CAP { return 0 - 1 } 711 let ov: *i64 = G[GMG_C_OV] as *i64 712 let o: i64 = onv * GMG_VSTRIDE 713 ov[o] = x; ov[o + 1] = y; ov[o + 2] = z; ov[o + 3] = src; ov[o + 4] = flag 714 G[GMG_C_ONV] = onv + 1 715 return onv 716} 717func gmg_addt(G: *i64, a: i64, b: i64, c: i64) -> i64 { 718 let ont: i64 = G[GMG_C_ONT] 719 if ont >= GMG_OT_CAP { return 0 - 1 } 720 let ot: *i64 = G[GMG_C_OT] as *i64 721 ot[ont * 3] = a; ot[ont * 3 + 1] = b; ot[ont * 3 + 2] = c 722 G[GMG_C_ONT] = ont + 1 723 return ont 724} 725 726// undirected edge table keyed under the LOWER vertex: count occurrences across the selected triangles 727func gmg_nbr_add(G: *i64, u0: i64, v0: i64) -> i64 { 728 var u: i64 = u0 729 var v: i64 = v0 730 if v < u { u = v0; v = u0 } 731 let nbr: *i64 = G[GMG_C_NBR] as *i64 732 let nbc: *i64 = G[GMG_C_NBC] as *i64 733 let deg: *i64 = G[GMG_C_DEG] as *i64 734 var k: i64 = 0 735 while k < deg[u] { 736 if nbr[u * GMG_NBR_CAP + k] == v { nbc[u * GMG_NBR_CAP + k] = nbc[u * GMG_NBR_CAP + k] + 1; return 1 } 737 k = k + 1 738 } 739 if deg[u] >= GMG_NBR_CAP { G[GMG_C_NBROVER] = G[GMG_C_NBROVER] + 1; return 0 } 740 nbr[u * GMG_NBR_CAP + deg[u]] = v 741 nbc[u * GMG_NBR_CAP + deg[u]] = 1 742 deg[u] = deg[u] + 1 743 return 1 744} 745func gmg_nbr_count(G: *i64, u0: i64, v0: i64) -> i64 { 746 var u: i64 = u0 747 var v: i64 = v0 748 if v < u { u = v0; v = u0 } 749 let nbr: *i64 = G[GMG_C_NBR] as *i64 750 let nbc: *i64 = G[GMG_C_NBC] as *i64 751 let deg: *i64 = G[GMG_C_DEG] as *i64 752 var k: i64 = 0 753 while k < deg[u] { 754 if nbr[u * GMG_NBR_CAP + k] == v { return nbc[u * GMG_NBR_CAP + k] } 755 k = k + 1 756 } 757 return 0 758} 759 760// one ribbon strap: front anchor -> shoulder top -> back anchor, sampled, each sample snapped to the 761// nearest body vertex in its height band and lifted by the hem thickness; two verts per sample across 762// the local tangent x normal, quads between samples. Returns the sample count; end heights into ends[0..1]. 763// A sampled surface path can revisit its previous vertex at a shoulder crest. 764// Preserve a nonzero local frame and its handedness instead of pinching both ribbon edges together. 765func gmg_strap_frame_v1(ring:*i64,nrm:*i64,n:i64,i:i64,halfw:i64,previous:*i64,out:*i64)->i64 { 766 if (ring as i64)==0||(nrm as i64)==0||(previous as i64)==0||(out as i64)==0||n<2||i<0||i>=n||halfw<=0{return GMG_ERR_EMPTY} 767 var jp:i64=i-1;if jp<0{jp=0};var jn:i64=i+1;if jn>=n{jn=n-1} 768 var tx:i64=ring[jn*4+1]-ring[jp*4+1];var ty:i64=ring[jn*4+2]-ring[jp*4+2];var tz:i64=ring[jn*4+3]-ring[jp*4+3] 769 if tx==0&&ty==0&&tz==0{tx=ring[i*4+1]-ring[jp*4+1];ty=ring[i*4+2]-ring[jp*4+2];tz=ring[i*4+3]-ring[jp*4+3]} 770 if tx==0&&ty==0&&tz==0{tx=ring[jn*4+1]-ring[i*4+1];ty=ring[jn*4+2]-ring[i*4+2];tz=ring[jn*4+3]-ring[i*4+3]} 771 let tl:i64=gmg_isqrt(tx*tx+ty*ty+tz*tz);if tl<1{return GMG_ERR_EMPTY} 772 tx=tx*GMG_Q/tl;ty=ty*GMG_Q/tl;tz=tz*GMG_Q/tl 773 let v:i64=ring[i*4];let nx:i64=nrm[v*3];let ny:i64=nrm[v*3+1];let nz:i64=nrm[v*3+2] 774 var sxv:i64=(ty*nz-tz*ny)/GMG_Q;var syv:i64=(tz*nx-tx*nz)/GMG_Q;var szv:i64=(tx*ny-ty*nx)/GMG_Q 775 let sl:i64=gmg_isqrt(sxv*sxv+syv*syv+szv*szv);if sl<1{return GMG_ERR_EMPTY} 776 if sxv*previous[0]+syv*previous[1]+szv*previous[2]<0{sxv=0-sxv;syv=0-syv;szv=0-szv} 777 out[0]=sxv*halfw/sl;out[1]=syv*halfw/sl;out[2]=szv*halfw/sl 778 if out[0]==0&&out[1]==0&&out[2]==0{return GMG_ERR_EMPTY} 779 previous[0]=out[0];previous[1]=out[1];previous[2]=out[2];return 0 780} 781func gmg_strap(G: *i64, mat: *i64, sx: i64, hF: i64, hB: i64, halfw: i64, ends: *i64) -> i64 { 782 let pos: *i64 = gmg_pos(G) 783 let nrm: *i64 = gmg_nrm(G) 784 let hh: *i64 = gmg_hh(G) 785 let zmn: i64 = G[GMG_C_ZMN] 786 let H: i64 = G[GMG_C_H] 787 let th: i64 = mat[1] 788 // anchors (bind space) 789 let ax0: i64 = sx * GMG_STRAP_AX_FRONT 790 let ay0: i64 = GMG_STRAP_Y_FRONT 791 let az0: i64 = zmn + hF * H / GMG_DM 792 let bx0: i64 = sx * GMG_STRAP_AX_SHOULDER 793 let by0: i64 = GMG_STRAP_Y_SHOULDER 794 let bz0: i64 = zmn + GMG_STRAP_SHOULDER_DM * H / GMG_DM 795 let cx0: i64 = sx * GMG_STRAP_AX_BACK 796 let cy0: i64 = GMG_STRAP_Y_BACK 797 let cz0: i64 = zmn + hB * H / GMG_DM 798 let ring: *i64 = sys_mmap(GMG_STRAP_CAP * 4 * 8 + 64) as *i64 799 if (ring as i64)<=0{return GMG_ERR_CAP} 800 let previous:*i64=((ring as i64)+GMG_STRAP_CAP*4*8) as *i64 801 let offset:*i64=((previous as i64)+24) as *i64 802 previous[0]=0;previous[1]=0;previous[2]=0 803 var n: i64 = 0 804 var seg: i64 = 0 805 while seg < 2 { 806 var i: i64 = 0 807 if seg == 1 { i = 1 } 808 while i <= GMG_STRAP_K { 809 var px: i64 = 0 810 var py: i64 = 0 811 var pz: i64 = 0 812 if seg == 0 { 813 px = ax0 + (bx0 - ax0) * i / GMG_STRAP_K; py = ay0 + (by0 - ay0) * i / GMG_STRAP_K; pz = az0 + (bz0 - az0) * i / GMG_STRAP_K 814 } else { 815 px = bx0 + (cx0 - bx0) * i / GMG_STRAP_K; py = by0 + (cy0 - by0) * i / GMG_STRAP_K; pz = bz0 + (cz0 - bz0) * i / GMG_STRAP_K 816 } 817 let hq: i64 = (pz - zmn) * GMG_DM / H 818 let v: i64 = gmg_near_body(G, px, py, pz, hq - GMG_STRAP_SEARCH_DM, hq + GMG_STRAP_SEARCH_DM, GMG_STRAP_AX_MAX) 819 if v >= 0 { if n < GMG_STRAP_CAP { 820 ring[n * 4] = v 821 ring[n * 4 + 1] = pos[v * 3] + nrm[v * 3] * th / GMG_Q 822 ring[n * 4 + 2] = pos[v * 3 + 1] + nrm[v * 3 + 1] * th / GMG_Q 823 ring[n * 4 + 3] = pos[v * 3 + 2] + nrm[v * 3 + 2] * th / GMG_Q 824 n = n + 1 825 } } 826 i = i + 1 827 } 828 seg = seg + 1 829 } 830 if n < 2 { sys_munmap(ring as *u8,GMG_STRAP_CAP*4*8+64);return 0 } 831 var i2: i64 = 0 832 var prev: i64 = 0 - 1 833 while i2 < n { 834 let v: i64 = ring[i2 * 4] 835 let frame:i64=gmg_strap_frame_v1(ring,nrm,n,i2,halfw,previous,offset) 836 if frame<0{sys_munmap(ring as *u8,GMG_STRAP_CAP*4*8+64);return frame} 837 let sxv:i64=offset[0];let syv:i64=offset[1];let szv:i64=offset[2] 838 let a: i64 = gmg_addv_at(G, ring[i2 * 4 + 1] + sxv, ring[i2 * 4 + 2] + syv, ring[i2 * 4 + 3] + szv, v, GMG_F_STRAP) 839 let c: i64 = gmg_addv_at(G, ring[i2 * 4 + 1] - sxv, ring[i2 * 4 + 2] - syv, ring[i2 * 4 + 3] - szv, v, GMG_F_STRAP) 840 if a < 0 { sys_munmap(ring as *u8,GMG_STRAP_CAP*4*8+64);return 0 - 1 } 841 if c < 0 { sys_munmap(ring as *u8,GMG_STRAP_CAP*4*8+64);return 0 - 1 } 842 if prev >= 0 { 843 gmg_addt(G, prev, prev + 1, a) 844 gmg_addt(G, prev + 1, c, a) 845 } 846 prev = a 847 i2 = i2 + 1 848 } 849 ends[0] = hh[ring[0]] 850 ends[1] = hh[ring[(n - 1) * 4]] 851 sys_munmap(ring as *u8,GMG_STRAP_CAP*4*8+64) 852 return n 853} 854 855// gmg_build: one garment kind into the OV/OT scratch (reset here). Returns nverts, 0 when the region 856// selects no triangle, negative on refusal. stat[] carries the counts the gate reads. 857func gmg_build(G: *i64, kind: i64, stat: *i64) -> i64 { 858 var s: i64 = 0 859 while s < GMG_STAT_N { stat[s] = 0; s = s + 1 } 860 stat[GMG_S_ID] = kind 861 let mat: *i64 = sys_mmap(4 * 8) as *i64 862 if gmg_mat(G, kind, mat) < 0 { return GMG_ERR_EMPTY } 863 let t: i64 = mat[0] 864 let th: i64 = mat[1] 865 stat[GMG_S_T] = t; stat[GMG_S_TH] = th; stat[GMG_S_EASE] = mat[2]; stat[GMG_S_CLASS] = mat[3] 866 // A ZERO-THICKNESS GARMENT IS NOT A GARMENT: refused by name, never emitted as a coincident shell 867 if t <= 0 { gmg_puts("GMG-REFUSED kind=" as *u8); gmg_pn(kind); gmg_puts(" zero-thickness: a shell coincident with the skin is paint, not an object\n" as *u8); return GMG_ERR_REFUSED_THICK } 868 let pos: *i64 = gmg_pos(G) 869 let nrm: *i64 = gmg_nrm(G) 870 let idx: *i64 = G[GMG_C_IDX] as *i64 871 let nv: i64 = G[GMG_C_NV] 872 let nt: i64 = G[GMG_C_NT] 873 let sel: *u8 = G[GMG_C_SEL] as *u8 874 let bset: *u8 = G[GMG_C_BSET] as *u8 875 let ring: *u8 = G[GMG_C_RING] as *u8 876 let vmap: *i64 = G[GMG_C_VMAP] as *i64 877 let inner: *i64 = G[GMG_C_INNER] as *i64 878 let deg: *i64 = G[GMG_C_DEG] as *i64 879 let cand: *i64 = G[GMG_C_CAND] as *i64 880 let tris: *i64 = G[GMG_C_TRIS] as *i64 881 G[GMG_C_ONV] = 0 882 G[GMG_C_ONT] = 0 883 G[GMG_C_NBROVER] = 0 884 // 1-3. region -> covered triangles + edge table -> boundary, with PINCH DILATION: where two parts of a 885 // region touch at a single vertex (the bikini cups meeting the under-bust band, measured on the 886 // served body: cup bottom 703 dm, band top 701 dm) the union's boundary has a bow-tie vertex with 887 // four boundary edges and the hem cannot close there. Real fabric BRIDGES a pinch, so every vertex 888 // of every body triangle touching a pinch joins the region and the boundary is re-derived; bounded 889 // passes, and a pinch that survives them still FAILS the closed-hems tooth (never hidden). 890 let nbr: *i64 = G[GMG_C_NBR] as *i64 891 let nbc: *i64 = G[GMG_C_NBC] as *i64 892 let bdeg0: *i64 = G[GMG_C_BDEG] as *i64 893 let dil: *u8 = G[GMG_C_DIL] as *u8 894 let mark: *u8 = G[GMG_C_SEEN] as *u8 895 var v: i64 = 0 896 while v < nv { dil[v] = 0 as u8; v = v + 1 } 897 var ncand: i64 = 0 898 var ntris: i64 = 0 899 var nb: i64 = 0 900 var pinch: i64 = 1 901 var iter: i64 = 0 902 var passes: i64 = 0 903 while pinch > 0 { 904 if iter >= GMG_PINCH_PASSES { pinch = 0 } else { 905 ncand = 0 906 v = 0 907 while v < nv { 908 sel[v] = 0 as u8; bset[v] = 0 as u8; ring[v] = 0 as u8; vmap[v] = 0 - 1; inner[v] = 0 - 1; deg[v] = 0; bdeg0[v] = 0; mark[v] = 0 as u8 909 var inr: i64 = gmg_region(G, kind, v) 910 if dil[v] == (1 as u8) { inr = 1 } 911 if inr == 1 { sel[v] = 1 as u8; cand[ncand] = v; ncand = ncand + 1 } 912 v = v + 1 913 } 914 G[GMG_C_NBROVER] = 0 915 ntris = 0 916 var e: i64 = 0 917 while e < nt { 918 let a: i64 = idx[e * 3] 919 let b: i64 = idx[e * 3 + 1] 920 let c: i64 = idx[e * 3 + 2] 921 if sel[a] == (1 as u8) { if sel[b] == (1 as u8) { if sel[c] == (1 as u8) { 922 tris[ntris] = e; ntris = ntris + 1 923 gmg_nbr_add(G, a, b); gmg_nbr_add(G, b, c); gmg_nbr_add(G, c, a) 924 } } } 925 e = e + 1 926 } 927 nb = 0 928 v = 0 929 while v < nv { 930 var k: i64 = 0 931 while k < deg[v] { 932 if nbc[v * GMG_NBR_CAP + k] == 1 { 933 let w: i64 = nbr[v * GMG_NBR_CAP + k] 934 bset[v] = 1 as u8; bset[w] = 1 as u8; nb = nb + 1 935 bdeg0[v] = bdeg0[v] + 1; bdeg0[w] = bdeg0[w] + 1 936 } 937 k = k + 1 938 } 939 v = v + 1 940 } 941 pinch = 0 942 v = 0 943 while v < nv { if bdeg0[v] > 2 { pinch = pinch + 1; mark[v] = 1 as u8 } v = v + 1 } 944 if pinch > 0 { 945 e = 0 946 while e < nt { 947 let a: i64 = idx[e * 3] 948 let b: i64 = idx[e * 3 + 1] 949 let c: i64 = idx[e * 3 + 2] 950 var hit: i64 = 0 951 if mark[a] == (1 as u8) { hit = 1 } 952 if mark[b] == (1 as u8) { hit = 1 } 953 if mark[c] == (1 as u8) { hit = 1 } 954 if hit == 1 { dil[a] = 1 as u8; dil[b] = 1 as u8; dil[c] = 1 as u8 } 955 e = e + 1 956 } 957 } 958 iter = iter + 1 959 passes = passes + 1 960 } 961 } 962 G[GMG_C_NCAND] = ncand 963 stat[GMG_S_SEL] = ncand 964 stat[GMG_S_PINCHPASS] = passes 965 G[GMG_C_NTRIS] = ntris 966 stat[GMG_S_NBROVER] = G[GMG_C_NBROVER] 967 if ntris == 0 { return 0 } 968 stat[GMG_S_BEDGES] = nb 969 v = 0 970 while v < nv { 971 var k: i64 = 0 972 while k < deg[v] { 973 let w: i64 = nbr[v * GMG_NBR_CAP + k] 974 if bset[v] == (1 as u8) { ring[w] = 1 as u8 } 975 if bset[w] == (1 as u8) { ring[v] = 1 as u8 } 976 k = k + 1 977 } 978 v = v + 1 979 } 980 // 4. the shell: every covered triangle's vertices, offset by thickness (hem thickness at the opening, 981 // half-way one ring in = the raised binding band) plus the wearing ease 982 var ti: i64 = 0 983 while ti < ntris { 984 let e2: i64 = tris[ti] 985 var q: i64 = 0 986 while q < 3 { 987 let vv: i64 = idx[e2 * 3 + q] 988 if vmap[vv] < 0 { 989 var d: i64 = t 990 if ring[vv] == (1 as u8) { d = (t + th) / 2 } 991 if bset[vv] == (1 as u8) { d = th } 992 d = d + gmg_ease_at(G, kind, mat, vv) 993 let ix: i64 = gmg_addv(G, vv, d, GMG_F_SHELL) 994 if ix < 0 { return GMG_ERR_CAP } 995 vmap[vv] = ix 996 } 997 q = q + 1 998 } 999 if gmg_addt(G, vmap[idx[e2 * 3]], vmap[idx[e2 * 3 + 1]], vmap[idx[e2 * 3 + 2]]) < 0 { return GMG_ERR_CAP } 1000 ti = ti + 1 1001 } 1002 stat[GMG_S_SHELL] = G[GMG_C_ONV] 1003 let nshell: i64 = G[GMG_C_ONV] 1004 let ov: *i64 = G[GMG_C_OV] as *i64 1005 // 5. PUSH-OUT: in a concave fold the normal offset lands nearer a NEIGHBOURING skin vertex than its own; 1006 // walk such vertices away from the offending vertex until no covered body vertex is inside the fabric 1007 var pushed: i64 = 0 1008 if G[GMG_C_NOPUSH] == 0 { 1009 var pass: i64 = 0 1010 var again: i64 = 1 1011 while pass < GMG_PUSH_PASSES { 1012 if again == 1 { 1013 again = 0 1014 var i: i64 = 0 1015 while i < nshell { 1016 let o: i64 = i * GMG_VSTRIDE 1017 let x: i64 = ov[o] 1018 let y: i64 = ov[o + 1] 1019 let z: i64 = ov[o + 2] 1020 let src: i64 = ov[o + 3] 1021 var bd: i64 = t * t 1022 var bq: i64 = 0 - 1 1023 var ci: i64 = 0 1024 while ci < ncand { 1025 let qv: i64 = cand[ci] 1026 let dz: i64 = pos[qv * 3 + 2] - z 1027 if gmg_abs(dz) < t { 1028 let dx: i64 = pos[qv * 3] - x 1029 if gmg_abs(dx) < t { 1030 let dy: i64 = pos[qv * 3 + 1] - y 1031 if gmg_abs(dy) < t { 1032 let dd: i64 = dx * dx + dy * dy + dz * dz 1033 if dd < bd { bd = dd; bq = qv } 1034 } 1035 } 1036 } 1037 ci = ci + 1 1038 } 1039 if bq >= 0 { 1040 let dcur: i64 = gmg_isqrt(bd) 1041 let k2: i64 = t - dcur + GMG_PUSH_MARGIN 1042 var mx: i64 = nrm[src * 3] 1043 var my: i64 = nrm[src * 3 + 1] 1044 var mz: i64 = nrm[src * 3 + 2] 1045 if bq != src { if dcur >= 1 { 1046 mx = (x - pos[bq * 3]) * GMG_Q / dcur 1047 my = (y - pos[bq * 3 + 1]) * GMG_Q / dcur 1048 mz = (z - pos[bq * 3 + 2]) * GMG_Q / dcur 1049 } } 1050 ov[o] = x + mx * k2 / GMG_Q 1051 ov[o + 1] = y + my * k2 / GMG_Q 1052 ov[o + 2] = z + mz * k2 / GMG_Q 1053 pushed = pushed + 1 1054 again = 1 1055 } 1056 i = i + 1 1057 } 1058 } 1059 pass = pass + 1 1060 } 1061 } 1062 stat[GMG_S_PUSHED] = pushed 1063 // 6. hems: every boundary edge grows a strip from the shell edge down to the fabric's inner face 1064 var nhem: i64 = 0 1065 var e_in_swim: i64 = gmg_um2u(GMG_SWIM_INNER_UM) 1066 ti = 0 1067 while ti < ntris { 1068 let e2: i64 = tris[ti] 1069 var q: i64 = 0 1070 while q < 3 { 1071 let u: i64 = idx[e2 * 3 + q] 1072 var q1: i64 = q + 1 1073 if q1 > 2 { q1 = 0 } 1074 let w: i64 = idx[e2 * 3 + q1] 1075 if gmg_nbr_count(G, u, w) == 1 { 1076 if inner[u] < 0 { 1077 var ein: i64 = e_in_swim 1078 if mat[2] > 0 { ein = gmg_ease_at(G, kind, mat, u) } 1079 inner[u] = gmg_addv(G, u, ein, GMG_F_HEM) 1080 if inner[u] < 0 { return GMG_ERR_CAP } 1081 } 1082 if inner[w] < 0 { 1083 var ein2: i64 = e_in_swim 1084 if mat[2] > 0 { ein2 = gmg_ease_at(G, kind, mat, w) } 1085 inner[w] = gmg_addv(G, w, ein2, GMG_F_HEM) 1086 if inner[w] < 0 { return GMG_ERR_CAP } 1087 } 1088 // the strip carries COPIES so its faces get their own normals page-side 1089 let A: i64 = vmap[u] 1090 let B: i64 = vmap[w] 1091 let Bi: i64 = inner[w] 1092 let Ai: i64 = inner[u] 1093 let base: i64 = G[GMG_C_ONV] 1094 var cpy: i64 = 0 1095 while cpy < 4 { 1096 var srcix: i64 = A 1097 if cpy == 1 { srcix = B } 1098 if cpy == 2 { srcix = Bi } 1099 if cpy == 3 { srcix = Ai } 1100 let so: i64 = srcix * GMG_VSTRIDE 1101 if gmg_addv_at(G, ov[so], ov[so + 1], ov[so + 2], ov[so + 3], GMG_F_HEM) < 0 { return GMG_ERR_CAP } 1102 cpy = cpy + 1 1103 } 1104 gmg_addt(G, base, base + 1, base + 2) 1105 gmg_addt(G, base, base + 2, base + 3) 1106 nhem = nhem + 1 1107 } 1108 q = q + 1 1109 } 1110 ti = ti + 1 1111 } 1112 stat[GMG_S_HEM] = nhem 1113 // 7. straps for the strapped ids 1114 var nstrap: i64 = 0 1115 if kind == 1 { nstrap = 1 } 1116 if kind == 3 { nstrap = 1 } 1117 if kind == 5 { nstrap = 1 } 1118 if kind == 9 { nstrap = 1 } 1119 if nstrap == 1 { 1120 nstrap = 0 1121 var hF: i64 = GMG_ONEPIECE_NECK_F 1122 var hB: i64 = GMG_ONEPIECE_NECK_B 1123 var hw: i64 = gmg_um2u(GMG_STRAP_HALFW_DRESS_UM) 1124 if kind == 1 { hF = G[GMG_C_NIP] + 500; hB = G[GMG_C_NIP] - GMG_BUST_HALF_DM; hw = gmg_um2u(GMG_STRAP_HALFW_BIKINI_UM) } 1125 let ends: *i64 = sys_mmap(4 * 8) as *i64 1126 let nL: i64 = gmg_strap(G, mat, 0 - 1, hF, hB, hw, ends) 1127 if nL < 0 { return GMG_ERR_CAP } 1128 if nL > 0 { nstrap = nstrap + 1; stat[GMG_S_SLF] = ends[0]; stat[GMG_S_SLB] = ends[1] } 1129 let nR: i64 = gmg_strap(G, mat, 1, hF, hB, hw, ends) 1130 if nR < 0 { return GMG_ERR_CAP } 1131 if nR > 0 { nstrap = nstrap + 1; stat[GMG_S_SRF] = ends[0]; stat[GMG_S_SRB] = ends[1] } 1132 } 1133 stat[GMG_S_STRAPS] = nstrap 1134 // 8. clearance: shell vertices vs every covered body vertex (the gate's tooth: none inside 0.9 t); 1135 // the reported minimum is a FLOOR capped at 4 t (the search stops there) 1136 let bar: i64 = t * GMG_CLEAR_PERMIL / 1000 1137 let rep: i64 = t * GMG_CLEAR_REPORT_MULT 1138 var within: i64 = 0 1139 var minc: i64 = rep * rep 1140 var i3: i64 = 0 1141 while i3 < nshell { 1142 let o: i64 = i3 * GMG_VSTRIDE 1143 let x: i64 = ov[o] 1144 let y: i64 = ov[o + 1] 1145 let z: i64 = ov[o + 2] 1146 var best: i64 = rep * rep 1147 var ci: i64 = 0 1148 while ci < ncand { 1149 let qv: i64 = cand[ci] 1150 let dz: i64 = pos[qv * 3 + 2] - z 1151 if gmg_abs(dz) < rep { 1152 let dx: i64 = pos[qv * 3] - x 1153 if gmg_abs(dx) < rep { 1154 let dy: i64 = pos[qv * 3 + 1] - y 1155 if gmg_abs(dy) < rep { 1156 let dd: i64 = dx * dx + dy * dy + dz * dz 1157 if dd < best { best = dd } 1158 } 1159 } 1160 } 1161 ci = ci + 1 1162 } 1163 if best < bar * bar { within = within + 1 } 1164 if best < minc { minc = best } 1165 i3 = i3 + 1 1166 } 1167 stat[GMG_S_WITHIN] = within 1168 stat[GMG_S_MINCLEAR] = gmg_isqrt(minc) 1169 // 9. boundary topology: closed hems = every boundary vertex has exactly two boundary edges and the 1170 // boundary graph's components are all cycles 1171 let badj: *i64 = G[GMG_C_BADJ] as *i64 1172 let bdeg: *i64 = G[GMG_C_BDEG] as *i64 1173 let stack: *i64 = G[GMG_C_STACK] as *i64 1174 let seen: *u8 = G[GMG_C_SEEN] as *u8 1175 v = 0 1176 while v < nv { bdeg[v] = 0; seen[v] = 0 as u8; v = v + 1 } 1177 var over: i64 = 0 1178 v = 0 1179 while v < nv { 1180 var k: i64 = 0 1181 while k < deg[v] { 1182 if nbc[v * GMG_NBR_CAP + k] == 1 { 1183 let w: i64 = nbr[v * GMG_NBR_CAP + k] 1184 if bdeg[v] < GMG_BADJ_CAP { badj[v * GMG_BADJ_CAP + bdeg[v]] = w } else { over = over + 1 } 1185 bdeg[v] = bdeg[v] + 1 1186 if bdeg[w] < GMG_BADJ_CAP { badj[w * GMG_BADJ_CAP + bdeg[w]] = v } else { over = over + 1 } 1187 bdeg[w] = bdeg[w] + 1 1188 } 1189 k = k + 1 1190 } 1191 v = v + 1 1192 } 1193 var loops: i64 = 0 1194 var openc: i64 = 0 1195 v = 0 1196 while v < nv { 1197 if bdeg[v] > 0 { if seen[v] == (0 as u8) { 1198 loops = loops + 1 1199 var odd: i64 = 0 1200 var sp: i64 = 0 1201 stack[sp] = v; sp = sp + 1; seen[v] = 1 as u8 1202 while sp > 0 { 1203 sp = sp - 1 1204 let u: i64 = stack[sp] 1205 if bdeg[u] != 2 { odd = odd + 1 } 1206 var k: i64 = 0 1207 var kl: i64 = bdeg[u] 1208 if kl > GMG_BADJ_CAP { kl = GMG_BADJ_CAP } 1209 while k < kl { 1210 let w: i64 = badj[u * GMG_BADJ_CAP + k] 1211 if seen[w] == (0 as u8) { seen[w] = 1 as u8; stack[sp] = w; sp = sp + 1 } 1212 k = k + 1 1213 } 1214 } 1215 if odd > 0 { openc = openc + 1 } 1216 } } 1217 v = v + 1 1218 } 1219 if over > 0 { openc = openc + 1 } 1220 stat[GMG_S_LOOPS] = loops 1221 stat[GMG_S_OPEN] = openc 1222 stat[GMG_S_NV] = G[GMG_C_ONV] 1223 stat[GMG_S_NT] = G[GMG_C_ONT] 1224 return G[GMG_C_ONV] 1225} 1226 1227// gmg_gen_all: every kind into one GARM section buffer (sec, GMG_SEC_CAP words). stats = 9 x GMG_STAT_N. 1228// Returns the section's word count, or a negative refusal (kind named on stdout). 1229func gmg_gen_all(G: *i64, sec: *i64, stats: *i64) -> i64 { 1230 let nk: i64 = GMG_KINDS 1231 var p: i64 = GMG_HDR + nk * GMG_DIRSTRIDE 1232 sec[0] = GMG_GARM_VER 1233 sec[1] = nk 1234 sec[2] = GMG_VSTRIDE 1235 sec[3] = GMG_TSTRIDE 1236 sec[4] = G[GMG_C_UPMK] 1237 sec[5] = GMG_DIRSTRIDE 1238 sec[6] = 0 1239 sec[7] = 0 1240 var k: i64 = 1 1241 while k <= nk { 1242 let st: *i64 = ((stats as i64) + (k - 1) * GMG_STAT_N * 8) as *i64 1243 let r: i64 = gmg_build(G, k, st) 1244 if r < 0 { gmg_puts("GMG-REFUSED kind=" as *u8); gmg_pn(k); gmg_puts(" build refused rc=" as *u8); gmg_pn(r); gmg_puts("\n" as *u8); return r } 1245 if r == 0 { gmg_puts("GMG-REFUSED kind=" as *u8); gmg_pn(k); gmg_puts(" selects no triangle (an id with no mesh would silently stay paint)\n" as *u8); return GMG_ERR_EMPTY } 1246 let onv: i64 = G[GMG_C_ONV] 1247 let ont: i64 = G[GMG_C_ONT] 1248 if p + onv * GMG_VSTRIDE + ont * GMG_TSTRIDE > GMG_SEC_CAP { gmg_puts("GMG-REFUSED section cap\n" as *u8); return GMG_ERR_CAP } 1249 let d: i64 = GMG_HDR + (k - 1) * GMG_DIRSTRIDE 1250 sec[d] = k 1251 sec[d + 1] = onv 1252 sec[d + 2] = ont 1253 sec[d + 3] = p 1254 let ov: *i64 = G[GMG_C_OV] as *i64 1255 var i: i64 = 0 1256 while i < onv * GMG_VSTRIDE { sec[p + i] = ov[i]; i = i + 1 } 1257 p = p + onv * GMG_VSTRIDE 1258 sec[d + 4] = p 1259 let ot: *i64 = G[GMG_C_OT] as *i64 1260 i = 0 1261 while i < ont * GMG_TSTRIDE { sec[p + i] = ot[i]; i = i + 1 } 1262 p = p + ont * GMG_TSTRIDE 1263 sec[d + 5] = st[GMG_S_T] 1264 sec[d + 6] = st[GMG_S_EASE] 1265 sec[d + 7] = st[GMG_S_CLASS] 1266 st[GMG_S_VOFF] = sec[d + 3] 1267 st[GMG_S_TOFF] = sec[d + 4] 1268 k = k + 1 1269 } 1270 sec[6] = p 1271 return p 1272} 1273 1274// one stats line per kind -- the receipt the program prints and the gate re-derives in-process 1275func gmg_print_stat(st: *i64) -> i64 { 1276 gmg_puts("GARM kind=" as *u8); gmg_pn(st[GMG_S_ID]) 1277 gmg_puts(" class=" as *u8); gmg_pn(st[GMG_S_CLASS]) 1278 gmg_puts(" verts=" as *u8); gmg_pn(st[GMG_S_NV]) 1279 gmg_puts(" tris=" as *u8); gmg_pn(st[GMG_S_NT]) 1280 gmg_puts(" shell=" as *u8); gmg_pn(st[GMG_S_SHELL]) 1281 gmg_puts(" sel=" as *u8); gmg_pn(st[GMG_S_SEL]) 1282 gmg_puts(" hem_quads=" as *u8); gmg_pn(st[GMG_S_HEM]) 1283 gmg_puts(" boundary_edges=" as *u8); gmg_pn(st[GMG_S_BEDGES]) 1284 gmg_puts(" loops=" as *u8); gmg_pn(st[GMG_S_LOOPS]) 1285 gmg_puts(" open_chains=" as *u8); gmg_pn(st[GMG_S_OPEN]) 1286 gmg_puts(" pushed=" as *u8); gmg_pn(st[GMG_S_PUSHED]) 1287 gmg_puts(" straps=" as *u8); gmg_pn(st[GMG_S_STRAPS]) 1288 gmg_puts(" t_units=" as *u8); gmg_pn(st[GMG_S_T]) 1289 gmg_puts(" hem_units=" as *u8); gmg_pn(st[GMG_S_TH]) 1290 gmg_puts(" ease_units=" as *u8); gmg_pn(st[GMG_S_EASE]) 1291 gmg_puts(" within_09t=" as *u8); gmg_pn(st[GMG_S_WITHIN]) 1292 gmg_puts(" minclear_floor=" as *u8); gmg_pn(st[GMG_S_MINCLEAR]) 1293 gmg_puts(" strap_ends_dm=" as *u8); gmg_pn(st[GMG_S_SLF]); gmg_puts("/" as *u8); gmg_pn(st[GMG_S_SLB]); gmg_puts("/" as *u8); gmg_pn(st[GMG_S_SRF]); gmg_puts("/" as *u8); gmg_pn(st[GMG_S_SRB]) 1294 gmg_puts(" nbr_overflow=" as *u8); gmg_pn(st[GMG_S_NBROVER]) 1295 gmg_puts(" pinch_passes=" as *u8); gmg_pn(st[GMG_S_PINCHPASS]) 1296 gmg_puts("\n" as *u8) 1297 return 0 1298} 1299 1300// gmg_write_nxa: copy every section of the input and append GARM (TOC rebuilt, checksums re-folded) 1301func gmg_write_nxa(b: *u8, flen: i64, sec: *i64, nwords: i64, outpath: *u8) -> i64 { 1302 let h: *i64 = b as *i64 1303 let ons: i64 = h[2] 1304 let otoc: *i64 = ((b as i64) + 32) as *i64 1305 // an input already carrying GARM is REPLACED, never doubled (idempotent re-runs) 1306 let gtag: i64 = nxa_tag4("GARM" as *u8) 1307 var keep: i64 = 0 1308 var ti: i64 = 0 1309 while ti < ons { if otoc[ti * 4] != gtag { keep = keep + 1 } ti = ti + 1 } 1310 let ns2: i64 = keep + 1 1311 let hdr: *i64 = sys_mmap(64) as *i64 1312 let toc: *i64 = sys_mmap(ns2 * 32 + 64) as *i64 1313 var o: i64 = 32 + ns2 * 32 1314 var w: i64 = 0 1315 ti = 0 1316 while ti < ons { 1317 if otoc[ti * 4] != gtag { 1318 toc[w * 4] = otoc[ti * 4] 1319 toc[w * 4 + 1] = o 1320 toc[w * 4 + 2] = otoc[ti * 4 + 2] 1321 toc[w * 4 + 3] = otoc[ti * 4 + 3] 1322 o = o + otoc[ti * 4 + 2] * 8 1323 w = w + 1 1324 } 1325 ti = ti + 1 1326 } 1327 toc[w * 4] = gtag 1328 toc[w * 4 + 1] = o 1329 toc[w * 4 + 2] = nwords 1330 toc[w * 4 + 3] = nxa_check2(1, sec, nwords) 1331 hdr[0] = nxa_magic() 1332 hdr[1] = NXA_VER 1333 hdr[2] = ns2 1334 hdr[3] = nxa_check2(1, toc, ns2 * 4) 1335 let fd: i64 = sys_openat_wr(outpath, MODE_0644) 1336 if fd < 0 { return 0 - 9 } 1337 sys_write(fd, hdr as *u8, 32) 1338 sys_write(fd, toc as *u8, ns2 * 32) 1339 ti = 0 1340 while ti < ons { 1341 if otoc[ti * 4] != gtag { sys_write(fd, ((b as i64) + otoc[ti * 4 + 1]) as *u8, otoc[ti * 4 + 2] * 8) } 1342 ti = ti + 1 1343 } 1344 sys_write(fd, sec as *u8, nwords * 8) 1345 sys_close(fd) 1346 return 0 1347} 1348 1349// Additive boundary contract v1: clip one source triangle by its vertex scalar samples. 1350// Output is at most four [bary0 numerator,bary1 numerator,bary2 numerator,denominator] 1351// records in original winding. Ratios are exact integers: no weight truncation or coordinate rounding. 1352// A consumer may interpolate already-deformed source vertices, preserving the current triangle surface; 1353// interpolating joint weights and then dual-quaternion skinning is NOT equivalent and is not implied. 1354// Material, source triangle identity and garment offset remain explicit caller-owned attributes. 1355const GMG_CLIP_RECORD_WORDS:i64=4 1356const GMG_CLIP_CAP_WORDS:i64=16 1357const GMG_CLIP_DISTANCE_MAX:i64=2147483647 1358const GMG_CLIP_E_INPUT:i64=0-30 1359const GMG_CLIP_E_CAP:i64=0-31 1360func gmg_clip_distance(a:i64,b:i64,c:i64,k:i64)->i64 { 1361 if k==0{return a};if k==1{return b};return c 1362} 1363func gmg_clip_vertex(out:*i64,n:i64,k:i64)->i64 { 1364 var i:i64=0;while i<3{out[n*4+i]=0;i=i+1};out[n*4+k]=1;out[n*4+3]=1;return n+1 1365} 1366func gmg_clip_crossing(out:*i64,n:i64,s:i64,e:i64,ds:i64,de:i64)->i64 { 1367 let a:i64=gmg_abs(ds);let b:i64=gmg_abs(de) 1368 var i:i64=0;while i<3{out[n*4+i]=0;i=i+1} 1369 out[n*4+s]=b;out[n*4+e]=a;out[n*4+3]=a+b;return n+1 1370} 1371func gmg_clip_triangle_bary_v1(distance:*i64,out:*i64,cap_words:i64)->i64 { 1372 if (distance as i64)==0||(out as i64)==0{return GMG_CLIP_E_INPUT} 1373 if cap_words<GMG_CLIP_CAP_WORDS{return GMG_CLIP_E_CAP} 1374 let a:i64=distance[0];let b:i64=distance[1];let c:i64=distance[2] 1375 if a<0-GMG_CLIP_DISTANCE_MAX||a>GMG_CLIP_DISTANCE_MAX{return GMG_CLIP_E_INPUT} 1376 if b<0-GMG_CLIP_DISTANCE_MAX||b>GMG_CLIP_DISTANCE_MAX{return GMG_CLIP_E_INPUT} 1377 if c<0-GMG_CLIP_DISTANCE_MAX||c>GMG_CLIP_DISTANCE_MAX{return GMG_CLIP_E_INPUT} 1378 var n:i64=0;var e:i64=0 1379 while e<3 { 1380 let s:i64=(e+2)%3;let ds:i64=gmg_clip_distance(a,b,c,s);let de:i64=gmg_clip_distance(a,b,c,e) 1381 if ds<0&&de>0{n=gmg_clip_crossing(out,n,s,e,ds,de)} 1382 if de>=0{n=gmg_clip_vertex(out,n,e)} 1383 if ds>0&&de<0{n=gmg_clip_crossing(out,n,s,e,ds,de)} 1384 e=e+1 1385 } 1386 if n<3{return 0} 1387 return n 1388} 1389 1390// Repeated halfspace clipping composes ORIGINAL source-triangle barycentrics. 1391// Caller supplies disjoint input/output/scratch buffers; refusals leave output unchanged. 1392// Checked i64 arithmetic refuses unrepresentable exact ratios instead of rounding provenance. 1393const GMG_CLIP_E_RANGE:i64=0-32 1394const GMG_CLIP_I64_MAX:i64=9223372036854775807 1395func gmg_clip_product(a:i64,b:i64)->i64 { 1396 if a<0||b<0{return -1};if b>0{if a>GMG_CLIP_I64_MAX/b{return -1}};return a*b 1397} 1398func gmg_clip_gcd(a:i64,b:i64)->i64 {var x:i64=a;var y:i64=b;while y>0{let z:i64=x%y;x=y;y=z};return x} 1399func gmg_clip_scalar(d:*i64,r:*i64,result:*i64)->i64 { 1400 let den:i64=r[3];if den<=0{return GMG_CLIP_E_INPUT} 1401 var sum:i64=0;var v:i64=0;var k:i64=0 1402 while k<3{ 1403 let n:i64=r[k];if n<0||n>den-sum{return GMG_CLIP_E_INPUT};sum=sum+n 1404 if d[k]<0-GMG_CLIP_DISTANCE_MAX||d[k]>GMG_CLIP_DISTANCE_MAX{return GMG_CLIP_E_INPUT} 1405 let p:i64=gmg_clip_product(n,gmg_abs(d[k]));if p<0{return GMG_CLIP_E_RANGE} 1406 if d[k]>=0 {if v>GMG_CLIP_I64_MAX-p{return GMG_CLIP_E_RANGE};v=v+p} 1407 else{if v<0-GMG_CLIP_I64_MAX+p{return GMG_CLIP_E_RANGE};v=v-p} 1408 k=k+1 1409 } 1410 if sum!=den{return GMG_CLIP_E_INPUT};result[0]=v;return 0 1411} 1412func gmg_clip_polygon_bary_v1(d:*i64,input:*i64,n:i64,out:*i64,scratch:*i64,cap_words:i64)->i64 { 1413 if n<3||n>7{return GMG_CLIP_E_INPUT} 1414 if (d as i64)==0||(input as i64)==0||(out as i64)==0||(scratch as i64)==0{return GMG_CLIP_E_INPUT} 1415 if input==out||input==scratch||out==scratch{return GMG_CLIP_E_INPUT} 1416 if cap_words<(n+1)*4+2{return GMG_CLIP_E_CAP} 1417 var count:i64=0;var e:i64=0 1418 // Scratch capacity includes two named scalar workspace words after polygon capacity. 1419 let scalar:*i64=((scratch as i64)+(cap_words-2)*8) as *i64 1420 while e<n{ 1421 let s:i64=(e+n-1)%n;let a:*i64=((input as i64)+s*32) as *i64;let b:*i64=((input as i64)+e*32) as *i64 1422 let ra:i64=gmg_clip_scalar(d,a,scalar);if ra<0{return ra} 1423 let rb:i64=gmg_clip_scalar(d,b,((scalar as i64)+8) as *i64);if rb<0{return rb} 1424 let ds:i64=scalar[0];let de:i64=scalar[1] 1425 if (ds<0&&de>0)||(ds>0&&de<0){ 1426 if count>=n+1{return GMG_CLIP_E_CAP} 1427 let ad:i64=gmg_abs(ds);let bd:i64=gmg_abs(de);var div:i64=0;var k:i64=0 1428 while k<4{ 1429 let x:i64=gmg_clip_product(a[k],bd);let y:i64=gmg_clip_product(b[k],ad) 1430 if x<0||y<0{return GMG_CLIP_E_RANGE};if x>GMG_CLIP_I64_MAX-y{return GMG_CLIP_E_RANGE} 1431 let v:i64=x+y;scratch[count*4+k]=v;div=gmg_clip_gcd(div,v);k=k+1 1432 } 1433 if div<=0{return GMG_CLIP_E_INPUT};k=0;while k<4{scratch[count*4+k]=scratch[count*4+k]/div;k=k+1};count=count+1 1434 } 1435 if de>=0{if count>=n+1{return GMG_CLIP_E_CAP};var k:i64=0;while k<4{scratch[count*4+k]=b[k];k=k+1};count=count+1} 1436 e=e+1 1437 } 1438 if count<3{return 0};var k:i64=0;while k<count*4{out[k]=scratch[k];k=k+1};return count 1439} 1440 1441// Twice polygon area in the canonical source triangle's (bary1,bary2) plane. 1442// Exact numerator/denominator; positive winding and <=1 means no inverted/expanded source coverage. 1443// Input records are already validated by the clipping boundary. Large common denominators refuse. 1444func gmg_clip_area_ratio_v1(poly:*i64,n:i64,result:*i64)->i64{return ngb_clip_area_ratio_v1(poly,n,result)} 1445 1446// GAT1 v1: additive source-surface attachments; legacy GARM is unchanged. 1447// Header 12 words: version,record_count,triangle_count,record_stride,VERT_check,TRIS_check, 1448// source_nv,source_nt,kind,total_words,triangle_stride,reserved. 1449// Record 8 words: source_face,bary0,bary1,bary2,denominator,normal_offset_units,material,flags. 1450// Triangle 3 words: attachment record indices. Coefficients remain exact i64 on disk. 1451const GMG_ATTACH_HDR:i64=12 1452const GMG_ATTACH_REC:i64=8 1453const GMG_ATTACH_COORD_MAX:i64=16777216 1454const GMG_ATTACH_E_SOURCE:i64=-40 1455const GMG_ATTACH_E_SHAPE:i64=-41 1456const GMG_ATTACH_E_GEOMETRY:i64=-42 1457// Exact integer coordinates and products of differences fit i64 under this boundary. 1458func gmg_attach_source_v1(b:*u8,n:i64)->i64{return ngb_attach_source_v1(b,n)} 1459func gmg_attach_face_v1(pos:*i64,idx:*i64,face:i64)->i64{return ngb_attach_face_v1(pos,idx,face)} 1460// Validate before consumer upload. Native NXA checks are integrity checks, not cryptographic authentication. 1461// Caller workspace: 14 i64 words, disjoint from section/source; no output mesh or device mutation here. 1462func gmg_attach_validate_v1(b:*u8,n:i64,s:*i64,nw:i64,work:*i64,work_words:i64)->i64{return ngb_attach_validate_v1(b,n,s,nw,work,work_words)} 1463 1464// Existing one-piece anatomical boundaries, now clipped exactly instead of whole-face selection. 1465// Caller-owned scratch: three 34-word polygons, two distance triples, four material words (112 total). 1466// out==0 measures required words. A nonzero output needs that capacity; only success is consumable. 1467func gmg_attach_onepiece_into_v1(G:*i64,out:*i64,cap:i64,scratch:*i64,sw:i64)->i64{ 1468 if (G as i64)==0||(scratch as i64)==0||sw<112{return GMG_ATTACH_E_SHAPE} 1469 let b:*u8=G[GMG_C_B] as *u8;let flen:i64=G[GMG_C_FLEN];let valid:i64=gmg_attach_source_v1(b,flen);if valid<0{return valid} 1470 let a:*i64=scratch;let p:*i64=((scratch as i64)+272) as *i64;let work:*i64=((scratch as i64)+544) as *i64 1471 let d:*i64=((scratch as i64)+816) as *i64;let e:*i64=((scratch as i64)+840) as *i64;let mat:*i64=((scratch as i64)+864) as *i64 1472 gmg_mat(G,3,mat);let off:i64=mat[0];let idx:*i64=G[GMG_C_IDX] as *i64;let hh:*i64=gmg_hh(G) 1473 var totalv:i64=0;var totalt:i64=0;var pass:i64=0;var need:i64=0 1474 while pass<2{var nv:i64=0;var nt:i64=0;var face:i64=0 1475 while face<G[GMG_C_NT]{let v0:i64=idx[face*3];let v1:i64=idx[face*3+1];let v2:i64=idx[face*3+2] 1476 if gmg_has(G,v0,GMG_VF_TOR)==1&&gmg_has(G,v1,GMG_VF_TOR)==1&&gmg_has(G,v2,GMG_VF_TOR)==1{ 1477 var k:i64=0;while k<3{let v:i64=idx[face*3+k];d[k]=hh[v]-gmg_hleg(G,v);e[k]=gmg_hneck(G,v)-hh[v];k=k+1} 1478 let n:i64=gmg_clip_triangle_bary_v1(d,a,16);if n<0{return n};var m:i64=0 1479 if n>0{m=gmg_clip_polygon_bary_v1(e,a,n,p,work,34);if m<0{return m}} 1480 if m>0{if gmg_attach_face_v1(G[GMG_C_POS] as *i64,idx,face)<0{return GMG_ATTACH_E_GEOMETRY} 1481 if pass==1{k=0;while k<m{let r:i64=GMG_ATTACH_HDR+(nv+k)*GMG_ATTACH_REC;out[r]=face;var j:i64=0;while j<4{out[r+1+j]=p[k*4+j];j=j+1};out[r+5]=off;out[r+6]=3;out[r+7]=0;k=k+1} 1482 k=1;while k<m-1{let t:i64=GMG_ATTACH_HDR+totalv*GMG_ATTACH_REC+nt*3;out[t]=nv;out[t+1]=nv+k;out[t+2]=nv+k+1;nt=nt+1;k=k+1} 1483 }else{nt=nt+m-2};nv=nv+m 1484 } 1485 };face=face+1 1486 } 1487 if pass==0{totalv=nv;totalt=nt;if totalv<3||totalt<1{return GMG_ATTACH_E_GEOMETRY} 1488 if totalv>(GMG_CLIP_I64_MAX-GMG_ATTACH_HDR)/GMG_ATTACH_REC{return GMG_CLIP_E_RANGE};need=GMG_ATTACH_HDR+totalv*GMG_ATTACH_REC 1489 if totalt>(GMG_CLIP_I64_MAX-need)/3{return GMG_CLIP_E_RANGE};need=need+totalt*3 1490 if (out as i64)==0{return need};if cap<need{return GMG_CLIP_E_CAP} 1491 let h:*i64=b as *i64;let ve:i64=nxa_section_entry(b,flen,nxa_tag4("VERT"));let te:i64=nxa_section_entry(b,flen,nxa_tag4("TRIS")) 1492 out[0]=1;out[1]=totalv;out[2]=totalt;out[3]=GMG_ATTACH_REC;out[4]=h[ve+3];out[5]=h[te+3] 1493 out[6]=G[GMG_C_NV];out[7]=G[GMG_C_NT];out[8]=3;out[9]=need;out[10]=3;out[11]=0 1494 }else{if nv!=totalv||nt!=totalt{return GMG_ATTACH_E_SHAPE}};pass=pass+1 1495 };return need 1496} 1497// Compose an additive section in the existing NXA container; preserve all other payload words. 1498// Disjoint buffers required. No filesystem side effects; caller publishes only validated output. 1499func gmg_attach_compose_into_v1(b:*u8,n:i64,s:*i64,nw:i64,out:*u8,cap:i64,work:*i64,ww:i64)->i64{ 1500 let valid:i64=gmg_attach_validate_v1(b,n,s,nw,work,ww);if valid<0{return valid} 1501 if (out as i64)==0||out==b||(out as i64)==(s as i64){return GMG_ATTACH_E_SHAPE} 1502 let h:*i64=b as *i64;let tag:i64=nxa_tag4("GAT1");let ns:i64=h[2];var keep:i64=0;var old:i64=0;var bytes:i64=0;var i:i64=0 1503 while i<ns{let t:i64=4+i*4;let found:i64=nxa_section_entry(b,n,h[t]);if found!=t{return GMG_ATTACH_E_SOURCE} 1504 if h[t]==tag{old=old+1}else{keep=keep+1;if h[t+2]>(GMG_CLIP_I64_MAX-bytes)/8{return GMG_CLIP_E_RANGE};bytes=bytes+h[t+2]*8};i=i+1 1505 };if old>1{return GMG_ATTACH_E_SOURCE} 1506 if nw>GMG_CLIP_I64_MAX/8{return GMG_CLIP_E_RANGE};let head:i64=32+(keep+1)*32 1507 if bytes>GMG_CLIP_I64_MAX-head{return GMG_CLIP_E_RANGE};let base:i64=head+bytes 1508 if nw>(GMG_CLIP_I64_MAX-base)/8{return GMG_CLIP_E_RANGE};let need:i64=base+nw*8;if cap<need{return GMG_CLIP_E_CAP} 1509 let dst:*i64=out as *i64;dst[0]=nxa_magic();dst[1]=NXA_VER;dst[2]=keep+1 1510 var at:i64=head/8;var w:i64=0;i=0;while i<ns{let t:i64=4+i*4;if h[t]!=tag{let dt:i64=4+w*4;dst[dt]=h[t];dst[dt+1]=at*8;dst[dt+2]=h[t+2];dst[dt+3]=h[t+3] 1511 var j:i64=0;while j<h[t+2]{dst[at+j]=h[h[t+1]/8+j];j=j+1};at=at+h[t+2];w=w+1};i=i+1 1512 } 1513 let dt:i64=4+w*4;dst[dt]=tag;dst[dt+1]=at*8;dst[dt+2]=nw;dst[dt+3]=nxa_check2(1,s,nw) 1514 i=0;while i<nw{dst[at+i]=s[i];i=i+1};dst[3]=nxa_check2(1,((out as i64)+32) as *i64,(keep+1)*4);return need 1515} 1516 1517 1518// Private qualification output: exclusive creation preserves an existing artifact; failed partial files remain evidence. 1519func gmg_write_all_v1(fd:i64,p:*u8,n:i64)->i64{var at:i64=0;while at<n{let wr:i64=sys_write(fd,((p as i64)+at) as *u8,n-at);if wr<=0||wr>n-at{return -9};at=at+wr};return 0} 1520func gmg_write_nxa_checked_v1(b:*u8,flen:i64,sec:*i64,nwords:i64,outpath:*u8)->i64{ 1521 if (b as i64)<=0||(sec as i64)<=0||(outpath as i64)<=0||nwords<GMG_HDR{return GMG_ATTACH_E_SHAPE} 1522 let h:*i64=b as *i64;let ons:i64=h[2];if ons<1||ons>(flen-32)/32{return GMG_ATTACH_E_SHAPE} 1523 let old:*i64=((b as i64)+32) as *i64;let tag:i64=nxa_tag4("GARM");var keep:i64=0;var i:i64=0 1524 while i<ons{if nxa_section_entry(b,flen,old[i*4])!=4+i*4{return GMG_ATTACH_E_SOURCE};if old[i*4]!=tag{keep=keep+1};i=i+1} 1525 let ns:i64=keep+1;let tocbytes:i64=ns*32;let header:*i64=sys_mmap(32+tocbytes) as *i64;if (header as i64)<=0{return GMG_ERR_CAP} 1526 let toc:*i64=((header as i64)+32) as *i64;var offset:i64=32+tocbytes;var k:i64=0;i=0 1527 while i<ons{if old[i*4]!=tag{toc[k*4]=old[i*4];toc[k*4+1]=offset;toc[k*4+2]=old[i*4+2];toc[k*4+3]=old[i*4+3];offset=offset+old[i*4+2]*8;k=k+1};i=i+1} 1528 toc[k*4]=tag;toc[k*4+1]=offset;toc[k*4+2]=nwords;toc[k*4+3]=nxa_check2(1,sec,nwords) 1529 header[0]=nxa_magic();header[1]=NXA_VER;header[2]=ns;header[3]=nxa_check2(1,toc,ns*4) 1530 let fd:i64=sys_openat_exclusive(outpath,MODE_0644);if fd<0{sys_munmap(header as *u8,32+tocbytes);return -9} 1531 var rc:i64=gmg_write_all_v1(fd,header as *u8,32+tocbytes);i=0 1532 while i<ons&&rc==0{if old[i*4]!=tag{rc=gmg_write_all_v1(fd,((b as i64)+old[i*4+1]) as *u8,old[i*4+2]*8)};i=i+1} 1533 if rc==0{rc=gmg_write_all_v1(fd,sec as *u8,nwords*8)};if rc==0{if sys_fsync(fd)<0{rc=-9}};if sys_close(fd)<0{rc=-9};sys_munmap(header as *u8,32+tocbytes) 1534 if rc<0{return rc} 1535 var len:i64=0;var slash:i64=-1;while outpath[len]!=(0 as u8){if outpath[len]==(47 as u8){slash=len};len=len+1} 1536 let directory:*u8=sys_mmap(len+2) as *u8;if (directory as i64)<=0{return GMG_ERR_CAP} 1537 if slash<0{directory[0]=46 as u8;directory[1]=0 as u8}else{var end:i64=slash;if end==0{end=1};i=0;while i<end{directory[i]=outpath[i];i=i+1};directory[end]=0 as u8} 1538 let dfd:i64=sys_openat_directory(directory);sys_munmap(directory,len+2);if dfd<0{return -9};rc=sys_fsync(dfd);if sys_close(dfd)<0{return -9};if rc<0{return -9};return 0 1539}