code wiki / (root) / nx_wallstat.nx

nx_wallstat.nx source

↩ module page · 141 lines · 5142 B

1// nx_wallstat.nx -- Analyzes mesh face thicknesses to detect dominant wall structures while avoiding false positives from surface roughness. 2// nx_wallstat.nx -- a WALL-THICKNESS STATISTIC THAT IS SAFE ON SCANNED / RECONSTRUCTED MESHES. 3// 4// THE HAZARD THIS EXISTS TO REMOVE (measured 2026-07-31): taking the MINIMUM of nx_meshthick over all faces 5// is correct on a clean closed CAD solid -- which is where it was gated -- but on a reconstructed surface it 6// reports the mesh's OWN ROUGHNESS, because a ray from one triangle hits another triangle of the SAME sheet. 7// On the flip-analysis question this arc exists to answer, that is a FALSE UNDERBUILT FLAG: a confident 8// number that looks like a thin wall and is really scanner noise. Measured min-wall was 64 on an open sheet 9// with no wall at all, and stayed 64 on shells of 120 AND 240 -- it tracked nothing. 10// 11// THE FIX IS THE STATISTIC, NOT THE RAY-CAST: a real wall shows up as a DOMINANT POPULATION of faces at one 12// thickness. Noise does not. So histogram the per-face thicknesses, find the dominant mode, and ★REFUSE to 13// emit a wall at all when no mode dominates -- fail loudly rather than return a plausible wrong number. 14// license_tier: ORIGINAL 15import "nx_meshthick.nx" 16 17const WS_BINS: i64 = 48 18const WS_DOMINANT: i64 = 18 // the mode cluster must hold >=18% of measured faces to count as a wall 19const WS_MINFACES: i64 = 12 // below this there is not enough evidence to call anything a wall 20 21// out4: [0]=mode thickness [1]=faces in the mode cluster [2]=faces measured [3]=cluster percent 22// returns 1 if a dominant wall was found, 0 if REFUSED (caller must not invent a number). 23func ws_wall(mesh: i64, out4: *i64, hist: *i64, vals: *i64) -> i64 { 24 let hd: *i64 = m3_hdr(mesh) 25 out4[0] = 0 - 1 26 out4[1] = 0 27 out4[2] = 0 28 out4[3] = 0 29 var n: i64 = 0 30 var lo: i64 = 0 31 var hi: i64 = 0 32 var f: i64 = 0 33 while f < hd[1] { 34 let th: i64 = mtk_face_thickness(mesh, f) 35 if th > 0 { 36 vals[n] = th 37 if n == 0 { 38 lo = th 39 hi = th 40 } else { 41 if th < lo { lo = th } 42 if th > hi { hi = th } 43 } 44 n = n + 1 45 } 46 f = f + 1 47 } 48 out4[2] = n 49 if n < WS_MINFACES { return 0 } 50 // âš THE HISTOGRAM RANGE MUST BE OUTLIER-ROBUST. Spanning raw min..max lets a few LONG rays -- shots that 51 // cross the whole part rather than measure a wall -- stretch the range so far that the real wall 52 // population is crushed into one or two bins and merges with the roughness. Measured: a 120 shell 53 // reported 84, and a 240 shell reported nothing at all. Sorting and binning the 5th..75th percentile 54 // puts the resolution where walls actually live. 55 var a: i64 = 1 56 while a < n { 57 let key: i64 = vals[a] 58 var b2: i64 = a - 1 59 var go: i64 = 1 60 while go == 1 { 61 if b2 >= 0 { 62 if vals[b2] > key { 63 vals[b2 + 1] = vals[b2] 64 b2 = b2 - 1 65 } else { go = 0 } 66 } else { go = 0 } 67 } 68 vals[b2 + 1] = key 69 a = a + 1 70 } 71 lo = vals[n / 20] 72 hi = vals[(n * 3) / 4] 73 if hi <= lo { 74 out4[0] = lo 75 out4[1] = n 76 out4[3] = 100 77 return 1 78 } 79 var b: i64 = 0 80 while b < WS_BINS { 81 hist[b] = 0 82 b = b + 1 83 } 84 var i: i64 = 0 85 while i < n { 86 if vals[i] >= lo { 87 if vals[i] <= hi { 88 var k: i64 = ((vals[i] - lo) * (WS_BINS - 1)) / (hi - lo) 89 if k < 0 { k = 0 } 90 if k >= WS_BINS { k = WS_BINS - 1 } 91 hist[k] = hist[k] + 1 92 } 93 } 94 i = i + 1 95 } 96 // dominant bin, then absorb its immediate neighbours (a real wall straddles a bin boundary) 97 var best: i64 = 0 98 var bi: i64 = 0 99 b = 0 100 while b < WS_BINS { 101 if hist[b] > best { 102 best = hist[b] 103 bi = b 104 } 105 b = b + 1 106 } 107 var lob: i64 = bi - 1 108 var hib: i64 = bi + 1 109 if lob < 0 { lob = 0 } 110 if hib >= WS_BINS { hib = WS_BINS - 1 } 111 var cluster: i64 = 0 112 b = lob 113 while b <= hib { 114 cluster = cluster + hist[b] 115 b = b + 1 116 } 117 let pct: i64 = (cluster * 100) / n 118 out4[1] = cluster 119 out4[3] = pct 120 // ★REFUSE when nothing dominates: a scattered distribution means there is no wall to report, and a 121 // number emitted here would be indistinguishable from a real measurement downstream. 122 if pct < WS_DOMINANT { return 0 } 123 // mode value = mean of the thicknesses inside the cluster 124 let clo: i64 = lo + ((hi - lo) * lob) / (WS_BINS - 1) 125 let chi: i64 = lo + ((hi - lo) * (hib + 1)) / (WS_BINS - 1) 126 var sum: i64 = 0 127 var cnt: i64 = 0 128 i = 0 129 while i < n { 130 if vals[i] >= clo { 131 if vals[i] <= chi { 132 sum = sum + vals[i] 133 cnt = cnt + 1 134 } 135 } 136 i = i + 1 137 } 138 if cnt == 0 { return 0 } 139 out4[0] = sum / cnt 140 return 1 141}