code wiki / (root) / nx_step_nurbs.nx

nx_step_nurbs.nx source

↩ module page · 279 lines · 10726 B

1// nx_step_nurbs.nx -- RATIONAL B-SPLINE SURFACE evaluation from STEP (cadtwin P3a: the ROUND parts, exact). 2// OpenCascade exports cylinders/holes as complex-instance rational B-spline surfaces, degree (1,3): linear along 3// the axis x rational-CUBIC around -- weights (1,1/3,1/3,1) = an EXACT circle arc (NURBS circle). This organ 4// parses the complex instance (control net 2x4 + weights) and evaluates the rational Bezier in fixed point: 5// Bernstein Q12, weights Q20 (fx256 would corrupt 1/3 -> Q20 keeps radius error ~ppm), coords fx256 (1 unit = 6// 3.90625 um). ENGINEERING-TWIN doctrine: dimensional truth first -- the gate measures roundness/radius/positions 7// against the file's own declared values, in integer-exact teeth where the math is exact (t=0, 1/2, 1) and 8// measured-um bounds where it rounds. Units: as1 declares SI_UNIT(.MILLI.,.METRE.) -> mm confirmed at runtime, 9// never assumed. license_tier: ORIGINAL 10import "nx_step_tess.nx" 11const SN_MAGIC_1000000000000: i64 = 1000000000000 12 13const SN_Q12: i64 = 4096 14const SN_QW: i64 = 1048576 15 16// naive substring find of nul-terminated pat in buf[off..off+len); returns ABSOLUTE index or -1 17func sgn_find(buf: *u8, off: i64, len: i64, pat: *u8) -> i64 { 18 var pl: i64 = 0 19 while pat[pl] != (0 as u8) { pl = pl + 1 } 20 if pl == 0 { return 0 - 1 } 21 var i: i64 = off 22 let e: i64 = off + len - pl 23 while i <= e { 24 var j: i64 = 0 25 var ok: i64 = 1 26 while j < pl { 27 if buf[i + j] != pat[j] { ok = 0; j = pl } else { j = j + 1 } 28 } 29 if ok == 1 { return i } 30 i = i + 1 31 } 32 return 0 - 1 33} 34// parse a non-negative int at p; out[0]=val; returns next pos 35func sgn_int(buf: *u8, p: i64, e: i64, out: *i64) -> i64 { 36 var v: i64 = p 37 var val: i64 = 0 38 var going: i64 = 1 39 while going == 1 { 40 if v < e { let d: i64 = buf[v] as i64; if sg_isdigit(d) == 1 { val = val * 10 + (d - 48); v = v + 1 } else { going = 0 } } else { going = 0 } 41 } 42 out[0] = val 43 return v 44} 45// parse a STEP real at p scaled by q (like sg_parse_real but arbitrary Q); out[0]=val; returns next pos. 46// fraction digits stop accumulating once the denominator passes 1e12 (guard vs overflow; precision far beyond Q20). 47func sgn_realq(buf: *u8, p: i64, e: i64, q: i64, out: *i64) -> i64 { 48 var v: i64 = p 49 var sign: i64 = 1 50 if v < e { if buf[v] == (45 as u8) { sign = 0 - 1; v = v + 1 } else { if buf[v] == (43 as u8) { v = v + 1 } } } 51 var intval: i64 = 0 52 var g1: i64 = 1 53 while g1 == 1 { 54 if v < e { let d: i64 = buf[v] as i64; if sg_isdigit(d) == 1 { intval = intval * 10 + (d - 48); v = v + 1 } else { g1 = 0 } } else { g1 = 0 } 55 } 56 var val: i64 = intval * q 57 if v < e { if buf[v] == (46 as u8) { 58 v = v + 1 59 var fnum: i64 = 0 60 var fden: i64 = 1 61 var g2: i64 = 1 62 while g2 == 1 { 63 if v < e { let d: i64 = buf[v] as i64; if sg_isdigit(d) == 1 { 64 if fden <= SN_MAGIC_1000000000000 { fnum = fnum * 10 + (d - 48); fden = fden * 10 } 65 v = v + 1 66 } else { g2 = 0 } } else { g2 = 0 } 67 } 68 if fden > 1 { val = val + (fnum * q) / fden } 69 } } 70 var hase: i64 = 0 71 if v < e { if buf[v] == (69 as u8) { hase = 1; v = v + 1 } } 72 if hase == 0 { if v < e { if buf[v] == (101 as u8) { hase = 1; v = v + 1 } } } 73 if hase == 1 { 74 var esign: i64 = 1 75 if v < e { if buf[v] == (45 as u8) { esign = 0 - 1; v = v + 1 } else { if buf[v] == (43 as u8) { v = v + 1 } } } 76 var exp: i64 = 0 77 var g3: i64 = 1 78 while g3 == 1 { 79 if v < e { let d: i64 = buf[v] as i64; if sg_isdigit(d) == 1 { exp = exp * 10 + (d - 48); v = v + 1 } else { g3 = 0 } } else { g3 = 0 } 80 } 81 var k: i64 = 0 82 while k < exp { if esign > 0 { val = val * 10 } else { val = val / 10 } k = k + 1 } 83 } 84 out[0] = val * sign 85 return v 86} 87// rounded signed division 88func sgn_rdiv(num: i64, den: i64) -> i64 { 89 if num >= 0 { return (num + den / 2) / den } 90 return 0 - ((0 - num + den / 2) / den) 91} 92// cubic Bernstein basis at t (Q12) -> b[4] (Q12; exact at t=0, Q12/2, Q12) 93func sgn_bern(t: i64, b: *i64) -> i64 { 94 let u: i64 = SN_Q12 - t 95 b[0] = ((u * u) / SN_Q12) * u / SN_Q12 96 b[1] = 3 * (((u * u) / SN_Q12) * t / SN_Q12) 97 b[2] = 3 * (((t * t) / SN_Q12) * u / SN_Q12) 98 b[3] = ((t * t) / SN_Q12) * t / SN_Q12 99 return 0 100} 101 102// ---- nt ctx: [0]=st [1]=mesh [2]=counters(*i64 c2[16]) [3]=ctrl(*i64 24: 2x4 pts xyz fx256) [4]=wts(*i64 8, Q20) 103// [5]=out2 [6]=bern(*i64 4) [7]=refs(*i64 16) 104// c2: 0=spline-faces 1=parsed 2=tessellated 3=skip-degree 4=skip-refs 5=skip-weights 6=tris 7=BOUNDARY-EXACT-FAILS 105 106// parse the complex-instance rational surface (entity index sidx) into nt ctrl/wts. 1 ok. 107func sgn_parse_surface(nt: *i64, sidx: i64) -> i64 { 108 let st: *i64 = nt[0] as *i64 109 let c2: *i64 = nt[2] as *i64 110 let buf: *u8 = st[0] as *u8 111 let aao: *i64 = st[5] as *i64 112 let aal: *i64 = st[6] as *i64 113 let off: i64 = aao[sidx] 114 let len: i64 = aal[sidx] 115 let out2: *i64 = nt[5] as *i64 116 var p: i64 = sgn_find(buf, off, len, "B_SPLINE_SURFACE(" as *u8) 117 if p < 0 { c2[3] = c2[3] + 1; return 0 } 118 p = p + 17 119 let e: i64 = off + len 120 p = sgn_int(buf, p, e, out2) 121 let udeg: i64 = out2[0] 122 p = p + 1 // ',' 123 p = sgn_int(buf, p, e, out2) 124 let vdeg: i64 = out2[0] 125 if udeg != 1 { c2[3] = c2[3] + 1; return 0 } 126 if vdeg != 3 { c2[3] = c2[3] + 1; return 0 } 127 // control net refs: exactly 8 for a single Bezier span (2 rows x 4) 128 let refs: *i64 = nt[7] as *i64 129 let wk: i64 = sgn_find(buf, p, e - p, "B_SPLINE_SURFACE_WITH_KNOTS" as *u8) 130 var refend: i64 = e 131 if wk >= 0 { refend = wk } 132 let nr: i64 = sgt_refs(buf, p, refend - p, refs, 9) 133 if nr != 8 { c2[4] = c2[4] + 1; return 0 } 134 let ctrl: *i64 = nt[3] as *i64 135 var i: i64 = 0 136 while i < 8 { 137 if sgt_tuple_of(st, refs[i], ((ctrl as i64) + i * 24) as *i64) == 0 { c2[4] = c2[4] + 1; return 0 } 138 i = i + 1 139 } 140 // weights: 8 reals at Q20 after RATIONAL_B_SPLINE_SURFACE( 141 var q: i64 = sgn_find(buf, off, len, "RATIONAL_B_SPLINE_SURFACE(" as *u8) 142 if q < 0 { c2[5] = c2[5] + 1; return 0 } 143 q = q + 26 144 let wts: *i64 = nt[4] as *i64 145 i = 0 146 while i < 8 { 147 q = sg_skip_to_num(buf, q, e) 148 if q >= e { c2[5] = c2[5] + 1; return 0 } 149 q = sgn_realq(buf, q, e, SN_QW, out2) 150 if out2[0] <= 0 { c2[5] = c2[5] + 1; return 0 } 151 wts[i] = out2[0] 152 i = i + 1 153 } 154 return 1 155} 156 157// evaluate rational cubic row (0/1) at t (Q12) -> out3 fx256 158func sgn_eval(nt: *i64, row: i64, t: i64, out3: *i64) -> i64 { 159 let ctrl: *i64 = nt[3] as *i64 160 let wts: *i64 = nt[4] as *i64 161 let b: *i64 = nt[6] as *i64 162 sgn_bern(t, b) 163 let base: i64 = row * 4 164 var den: i64 = 0 165 var nx: i64 = 0 166 var ny: i64 = 0 167 var nz: i64 = 0 168 var j: i64 = 0 169 while j < 4 { 170 let bw: i64 = b[j] * wts[base + j] 171 den = den + bw 172 nx = nx + bw * ctrl[(base + j) * 3] 173 ny = ny + bw * ctrl[(base + j) * 3 + 1] 174 nz = nz + bw * ctrl[(base + j) * 3 + 2] 175 j = j + 1 176 } 177 if den <= 0 { out3[0] = 0; out3[1] = 0; out3[2] = 0; return 0 } 178 out3[0] = sgn_rdiv(nx, den) 179 out3[1] = sgn_rdiv(ny, den) 180 out3[2] = sgn_rdiv(nz, den) 181 return 1 182} 183 184// tessellate one spline face (surface entity sidx) into the mesh: N=16 segments x 2 rims. BOUNDARY-EXACTNESS 185// invariant: eval(row,0)==ctrl[row][0] and eval(row,1)==ctrl[row][3] integer-exact, else counted + face skipped. 186func sgn_tess_face(nt: *i64, sidx: i64) -> i64 { 187 let c2: *i64 = nt[2] as *i64 188 if sgn_parse_surface(nt, sidx) == 0 { return 0 } 189 c2[1] = c2[1] + 1 190 let ctrl: *i64 = nt[3] as *i64 191 let mesh: i64 = nt[1] 192 let ev: *i64 = ((nt[6] as i64) + 64) as *i64 // eval scratch after bern block 193 // boundary invariant both rows 194 var row: i64 = 0 195 while row < 2 { 196 sgn_eval(nt, row, 0, ev) 197 let c0: i64 = row * 4 * 3 198 if ev[0] != ctrl[c0] { c2[7] = c2[7] + 1; return 0 } 199 if ev[1] != ctrl[c0 + 1] { c2[7] = c2[7] + 1; return 0 } 200 if ev[2] != ctrl[c0 + 2] { c2[7] = c2[7] + 1; return 0 } 201 sgn_eval(nt, row, SN_Q12, ev) 202 let c3: i64 = (row * 4 + 3) * 3 203 if ev[0] != ctrl[c3] { c2[7] = c2[7] + 1; return 0 } 204 if ev[1] != ctrl[c3 + 1] { c2[7] = c2[7] + 1; return 0 } 205 if ev[2] != ctrl[c3 + 2] { c2[7] = c2[7] + 1; return 0 } 206 row = row + 1 207 } 208 // emit 2 x 17 verts + 32 tris 209 let h: *i64 = m3_hdr(mesh) 210 let vbase: i64 = h[0] 211 row = 0 212 while row < 2 { 213 var i: i64 = 0 214 while i <= 16 { 215 sgn_eval(nt, row, (i * SN_Q12) / 16, ev) 216 m3_add_vert(mesh, ev[0], ev[1], ev[2]) 217 i = i + 1 218 } 219 row = row + 1 220 } 221 var i2: i64 = 0 222 while i2 < 16 { 223 let a: i64 = vbase + i2 224 let bb: i64 = vbase + i2 + 1 225 let cc: i64 = vbase + 17 + i2 226 let dd: i64 = vbase + 17 + i2 + 1 227 m3_add_tri(mesh, a, bb, cc) 228 m3_add_tri(mesh, bb, dd, cc) 229 c2[6] = c2[6] + 2 230 i2 = i2 + 1 231 } 232 c2[2] = c2[2] + 1 233 return 1 234} 235 236// sweep every ADVANCED_FACE whose surface is a complex-instance spline; tessellate into nt mesh 237func sgn_tessellate_all(nt: *i64) -> i64 { 238 let st: *i64 = nt[0] as *i64 239 let c2: *i64 = nt[2] as *i64 240 let anl: *i64 = st[4] as *i64 241 let out2: *i64 = nt[5] as *i64 242 let cnt: i64 = st[7] 243 var idx: i64 = 0 244 while idx < cnt { 245 if sp_name_is(st, idx, "ADVANCED_FACE" as *u8) == 1 { 246 let sid: i64 = sgt_arg_ref(st, idx, 2, out2) 247 if sid >= 0 { 248 let sidx: i64 = sp_find(st, sid) 249 if sidx >= 0 { if anl[sidx] == 0 { 250 c2[0] = c2[0] + 1 251 sgn_tess_face(nt, sidx) 252 } } 253 } 254 } 255 idx = idx + 1 256 } 257 return c2[2] 258} 259 260// runtime unit confirmation: some complex instance declares LENGTH_UNIT + SI_UNIT(.MILLI.,.METRE.) -> mm 261func sgn_unit_mm(st: *i64) -> i64 { 262 let buf: *u8 = st[0] as *u8 263 let anl: *i64 = st[4] as *i64 264 let aao: *i64 = st[5] as *i64 265 let aal: *i64 = st[6] as *i64 266 let cnt: i64 = st[7] 267 var idx: i64 = 0 268 while idx < cnt { 269 if anl[idx] == 0 { 270 let f1: i64 = sgn_find(buf, aao[idx], aal[idx], "LENGTH_UNIT()" as *u8) 271 if f1 >= 0 { 272 let f2: i64 = sgn_find(buf, aao[idx], aal[idx], "SI_UNIT(.MILLI.,.METRE.)" as *u8) 273 if f2 >= 0 { return 1 } 274 } 275 } 276 idx = idx + 1 277 } 278 return 0 279}