nx_meshseg3d.nx source
↩ module page · 81 lines · 3283 B
1// nx_meshseg3d.nx -- 3D PART SEGMENTATION of a triangle mesh (cadtwin P2): decompose a scanned mesh into its
2// component PARTS so each can be analyzed (per-part wall-thickness R5 + underbuilt R6). Classical region-growing:
3// (a) CONNECTIVITY -- disconnected mesh pieces = separate parts; (b) CREASE -- within a connected piece, split
4// at sharp dihedral edges (adjacent face normals differ by more than a DATA-DRIVEN threshold). ONE function
5// with a cos-threshold param does both (threshold = -Q -> pure connectivity; higher -> crease-split). The
6// neural SOTA (SAMPart3D/PartField, PartObjaverse-Tiny mIoU ~84) is trained-model-bound; this is the sovereign
7// classical first rung. Composes nx_mesh3 + nx_meshthick (face normals). Deterministic. license_tier: ORIGINAL
8import "nx_meshthick.nx"
9
10// two triangles adjacent iff they share exactly 2 vertex indices (a common edge)
11func ms_adjacent(base: i64, i: i64, j: i64) -> i64 {
12 let ti: *i64 = m3_tri(base, i)
13 let tj: *i64 = m3_tri(base, j)
14 var common: i64 = 0
15 var a: i64 = 0
16 while a < 3 {
17 var b: i64 = 0
18 while b < 3 {
19 if ti[a] == tj[b] { common = common + 1 }
20 b = b + 1
21 }
22 a = a + 1
23 }
24 if common >= 2 { return 1 }
25 return 0
26}
27
28// segment faces by region-growing across edges whose adjacent-normal dot >= cos_thresh (Q14). labels[F] filled
29// with segment ids 0..k-1. Returns k = number of segments. scratch: queue[F], n1[3], n2[3] via mmap once.
30func ms_segment(base: i64, cos_thresh: i64, labels: *i64) -> i64 {
31 let h: *i64 = m3_hdr(base)
32 let nf: i64 = h[1]
33 var i: i64 = 0
34 while i < nf { labels[i] = 0 - 1; i = i + 1 }
35 let queue: *i64 = sys_mmap((nf + 8) * 8) as *i64
36 let n1: *i64 = sys_mmap(32) as *i64
37 let n2: *i64 = sys_mmap(32) as *i64
38 var seg: i64 = 0
39 var f: i64 = 0
40 while f < nf {
41 if labels[f] < 0 {
42 // new segment via BFS
43 labels[f] = seg
44 queue[0] = f
45 var qh: i64 = 0
46 var qt: i64 = 1
47 while qh < qt {
48 let cur: i64 = queue[qh]
49 qh = qh + 1
50 mtk_face_normal(base, cur, n1)
51 var g: i64 = 0
52 while g < nf {
53 if labels[g] < 0 {
54 if ms_adjacent(base, cur, g) == 1 {
55 mtk_face_normal(base, g, n2)
56 let d: i64 = (n1[0] * n2[0] + n1[1] * n2[1] + n1[2] * n2[2]) / 256 // fx256 unit normals -> cos*256... see note
57 if d >= cos_thresh {
58 labels[g] = seg
59 queue[qt] = g
60 qt = qt + 1
61 }
62 }
63 }
64 g = g + 1
65 }
66 }
67 seg = seg + 1
68 }
69 f = f + 1
70 }
71 return seg
72}
73
74// count faces in each segment into counts[k]; returns nothing meaningful
75func ms_seg_sizes(labels: *i64, nf: i64, counts: *i64, k: i64) -> i64 {
76 var i: i64 = 0
77 while i < k { counts[i] = 0; i = i + 1 }
78 i = 0
79 while i < nf { let s: i64 = labels[i]; if s >= 0 { counts[s] = counts[s] + 1 } i = i + 1 }
80 return 0
81}