nx_meshthick.nx source
↩ module page · 160 lines · 6811 B
1// nx_meshthick.nx -- R5 of the CAD car-twin ladder (cadtwin P6a): MESH WALL-THICKNESS by ray-casting (the
2// GOM/ZEISS-INSPECT "ray method"), the GEOMETRIC half of "identify underbuilt parts when flipping cars"
3// (R6 nx_underbuilt_board is the DATA half). For each triangle face: from its centroid, cast a ray along the
4// INWARD normal and find the nearest OTHER face -> that hit distance is the local wall thickness. The MIN over
5// all faces = the thinnest wall = the underbuilt candidate. A data-driven THRESHOLD (passed in, never
6// hardcoded) classifies a part OK vs UNDERBUILT. All fixed-point fx256 (256 = 1.0), deterministic. Composes
7// nx_mesh3 (real tri-mesh + STL interchange). Ray-triangle = Moller-Trumbore, KAT-gated. license_tier: ORIGINAL
8import "nx_mesh3.nx"
9
10const MTK_ONE: i64 = 256 // 1.0 in fx256
11const MTK_EPS: i64 = 64 // 0.25 real: skip self/coplanar hits (t must exceed this)
12const MTK_BIG: i64 = 4611686018427387904 // "no hit" sentinel thickness
13
14func mtk_fxmul(a: i64, b: i64) -> i64 { return (a * b) / MTK_ONE }
15func mtk_fxdiv(a: i64, b: i64) -> i64 { if b == 0 { return 0 } return (a * MTK_ONE) / b }
16func mtk_abs(a: i64) -> i64 { if a < 0 { return 0 - a } return a }
17
18// integer sqrt (Newton) on a plain (non-fx) magnitude
19func mtk_isqrt(n: i64) -> i64 {
20 if n <= 0 { return 0 }
21 var x: i64 = n
22 var y: i64 = (x + 1) / 2
23 while y < x { x = y; y = (x + n / x) / 2 }
24 return x
25}
26
27// Moller-Trumbore ray/triangle. orig/dir + tri verts all fx256; dir is a UNIT fx256 vector (|dir|=256).
28// Returns hit distance t in fx256 (>0), or -1 on miss/parallel/behind. u,v barycentric checked in [0,256].
29func mtk_ray_tri(ox: i64, oy: i64, oz: i64, dx: i64, dy: i64, dz: i64,
30 ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64, cx: i64, cy: i64, cz: i64) -> i64 {
31 let e1x: i64 = bx - ax
32 let e1y: i64 = by - ay
33 let e1z: i64 = bz - az
34 let e2x: i64 = cx - ax
35 let e2y: i64 = cy - ay
36 let e2z: i64 = cz - az
37 // h = dir x e2
38 let hx: i64 = mtk_fxmul(dy, e2z) - mtk_fxmul(dz, e2y)
39 let hy: i64 = mtk_fxmul(dz, e2x) - mtk_fxmul(dx, e2z)
40 let hz: i64 = mtk_fxmul(dx, e2y) - mtk_fxmul(dy, e2x)
41 // a = e1 . h (determinant)
42 let det: i64 = mtk_fxmul(e1x, hx) + mtk_fxmul(e1y, hy) + mtk_fxmul(e1z, hz)
43 if mtk_abs(det) < 1 { return 0 - 1 } // ray parallel to triangle
44 // s = orig - a
45 let sx: i64 = ox - ax
46 let sy: i64 = oy - ay
47 let sz: i64 = oz - az
48 // u = (s . h) / det -> barycentric in fx256 [0,256]
49 let su: i64 = mtk_fxmul(sx, hx) + mtk_fxmul(sy, hy) + mtk_fxmul(sz, hz)
50 let u: i64 = mtk_fxdiv(su, det)
51 if u < 0 { return 0 - 1 }
52 if u > MTK_ONE { return 0 - 1 }
53 // q = s x e1
54 let qx: i64 = mtk_fxmul(sy, e1z) - mtk_fxmul(sz, e1y)
55 let qy: i64 = mtk_fxmul(sz, e1x) - mtk_fxmul(sx, e1z)
56 let qz: i64 = mtk_fxmul(sx, e1y) - mtk_fxmul(sy, e1x)
57 // v = (dir . q) / det
58 let sv: i64 = mtk_fxmul(dx, qx) + mtk_fxmul(dy, qy) + mtk_fxmul(dz, qz)
59 let v: i64 = mtk_fxdiv(sv, det)
60 if v < 0 { return 0 - 1 }
61 if u + v > MTK_ONE { return 0 - 1 }
62 // t = (e2 . q) / det
63 let st: i64 = mtk_fxmul(e2x, qx) + mtk_fxmul(e2y, qy) + mtk_fxmul(e2z, qz)
64 let t: i64 = mtk_fxdiv(st, det)
65 if t < MTK_EPS { return 0 - 1 }
66 return t
67}
68
69// nearest hit of ray (orig,dir) against every mesh triangle EXCEPT skip_tri; -1 if none. dir is UNIT fx256.
70func mtk_cast(base: i64, ox: i64, oy: i64, oz: i64, dx: i64, dy: i64, dz: i64, skip_tri: i64) -> i64 {
71 let h: *i64 = m3_hdr(base)
72 var best: i64 = 0 - 1
73 var ti: i64 = 0
74 while ti < h[1] {
75 if ti != skip_tri {
76 let t: *i64 = m3_tri(base, ti)
77 let va: *i64 = m3_vert(base, t[0])
78 let vb: *i64 = m3_vert(base, t[1])
79 let vc: *i64 = m3_vert(base, t[2])
80 let hit: i64 = mtk_ray_tri(ox, oy, oz, dx, dy, dz,
81 va[0], va[1], va[2], vb[0], vb[1], vb[2], vc[0], vc[1], vc[2])
82 if hit > 0 {
83 if best < 0 { best = hit } else { if hit < best { best = hit } }
84 }
85 }
86 ti = ti + 1
87 }
88 return best
89}
90
91// unit fx256 outward normal of triangle ti into out3 (from winding v0->v1->v2, right-handed)
92func mtk_face_normal(base: i64, ti: i64, out3: *i64) -> i64 {
93 let t: *i64 = m3_tri(base, ti)
94 let va: *i64 = m3_vert(base, t[0])
95 let vb: *i64 = m3_vert(base, t[1])
96 let vc: *i64 = m3_vert(base, t[2])
97 let e1x: i64 = vb[0] - va[0]
98 let e1y: i64 = vb[1] - va[1]
99 let e1z: i64 = vb[2] - va[2]
100 let e2x: i64 = vc[0] - va[0]
101 let e2y: i64 = vc[1] - va[1]
102 let e2z: i64 = vc[2] - va[2]
103 let nx: i64 = e1y * e2z - e1z * e2y
104 let ny: i64 = e1z * e2x - e1x * e2z
105 let nz: i64 = e1x * e2y - e1y * e2x
106 let mag: i64 = mtk_isqrt(nx * nx + ny * ny + nz * nz)
107 if mag == 0 { out3[0] = 0; out3[1] = 0; out3[2] = 0; return 0 - 1 }
108 out3[0] = (nx * MTK_ONE) / mag
109 out3[1] = (ny * MTK_ONE) / mag
110 out3[2] = (nz * MTK_ONE) / mag
111 return 0
112}
113
114// centroid (fx256) of triangle ti into out3
115func mtk_centroid(base: i64, ti: i64, out3: *i64) -> i64 {
116 let t: *i64 = m3_tri(base, ti)
117 let va: *i64 = m3_vert(base, t[0])
118 let vb: *i64 = m3_vert(base, t[1])
119 let vc: *i64 = m3_vert(base, t[2])
120 out3[0] = (va[0] + vb[0] + vc[0]) / 3
121 out3[1] = (va[1] + vb[1] + vc[1]) / 3
122 out3[2] = (va[2] + vb[2] + vc[2]) / 3
123 return 0
124}
125
126// local wall thickness at face ti: cast INWARD (-normal); if no hit, try +normal (winding-robust). fx256, -1 none.
127func mtk_face_thickness(base: i64, ti: i64) -> i64 {
128 let n3: *i64 = sys_mmap(32) as *i64
129 let c3: *i64 = sys_mmap(32) as *i64
130 if mtk_face_normal(base, ti, n3) < 0 { return 0 - 1 }
131 mtk_centroid(base, ti, c3)
132 // inward = -normal
133 let t1: i64 = mtk_cast(base, c3[0], c3[1], c3[2], 0 - n3[0], 0 - n3[1], 0 - n3[2], ti)
134 if t1 > 0 { return t1 }
135 let t2: i64 = mtk_cast(base, c3[0], c3[1], c3[2], n3[0], n3[1], n3[2], ti)
136 return t2
137}
138
139// MIN wall thickness over all faces (fx256); argmin face index into out_face[0]; -1 if unmeasurable
140func mtk_min_thickness(base: i64, out_face: *i64) -> i64 {
141 let h: *i64 = m3_hdr(base)
142 var best: i64 = MTK_BIG
143 var bestf: i64 = 0 - 1
144 var ti: i64 = 0
145 while ti < h[1] {
146 let th: i64 = mtk_face_thickness(base, ti)
147 if th > 0 { if th < best { best = th; bestf = ti } }
148 ti = ti + 1
149 }
150 out_face[0] = bestf
151 if bestf < 0 { return 0 - 1 }
152 return best
153}
154
155// data-driven underbuilt classification: 1 = UNDERBUILT (thinner than threshold), 0 = OK. threshold fx256.
156func mtk_is_underbuilt(min_thick: i64, threshold_fx: i64) -> i64 {
157 if min_thick < 0 { return 0 - 1 } // unmeasurable
158 if min_thick < threshold_fx { return 1 }
159 return 0
160}