code wiki / (root) / nx_voronoi_test.nx

nx_voronoi_test.nx source

↩ module page · 118 lines · 5444 B

1// nx_voronoi_test.nx -- smoke for 2D Voronoi. 2// 3// Validates: 4// 1. Per-pixel labelling assigns to nearest site 5// 2. Site at own coords gets labelled to itself 6// 3. Pixels exactly equidistant deterministically pick first-found 7// 4. Per-site counts sum to canvas size 8// 5. CV classifier produces a valid sealed-enum band 9// 6. Composition with nx_poisson_disk_sample (sites from poisson) 10// 7. Sealed-enum validity gate 11 12import "nx_syscalls.nx" 13import "nx_tier.nx" 14import "nx_poisson_disk.nx" 15import "nx_voronoi.nx" 16 17func main() -> nx_int { 18 // === Test 1: 3 hand-placed sites, label correctness ============ 19 let xs: *i64 = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *i64 20 let ys: *i64 = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *i64 21 xs[0] = 5; ys[0] = 5 // top-left-ish 22 xs[1] = 25; ys[1] = 5 // top-right-ish 23 xs[2] = 15; ys[2] = 25 // bottom-center-ish 24 let labels: *i64 = (sys_mmap(32 * 32 * NX_SIZEOF_NX_INT)) as *i64 25 let n_pixels: nx_int = nx_voronoi_label(32, 32, xs, ys, 3, labels) 26 if n_pixels != 32 * 32 { return 1 } 27 28 // Site coords always label to themselves. 29 if nx_voronoi_label_at(labels, 32, 32, 5, 5) != 0 { return 2 } 30 if nx_voronoi_label_at(labels, 32, 32, 25, 5) != 1 { return 3 } 31 if nx_voronoi_label_at(labels, 32, 32, 15, 25) != 2 { return 4 } 32 33 // Pixels near each site should be that site. 34 if nx_voronoi_label_at(labels, 32, 32, 6, 6) != 0 { return 5 } 35 if nx_voronoi_label_at(labels, 32, 32, 24, 6) != 1 { return 6 } 36 if nx_voronoi_label_at(labels, 32, 32, 14, 24) != 2 { return 7 } 37 38 // === Test 2: out-of-bounds label_at returns -1 ================= 39 if nx_voronoi_label_at(labels, 32, 32, 0 - 1, 0) != 0 - 1 { return 10 } 40 if nx_voronoi_label_at(labels, 32, 32, 32, 0) != 0 - 1 { return 11 } 41 if nx_voronoi_label_at(labels, 32, 32, 0, 0 - 1) != 0 - 1 { return 12 } 42 if nx_voronoi_label_at(labels, 32, 32, 0, 32) != 0 - 1 { return 13 } 43 44 // === Test 3: per-site counts sum to canvas size ================ 45 let counts: *i64 = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *i64 46 nx_voronoi_site_counts(32, 32, labels, 3, counts) 47 let total: nx_int = counts[0] + counts[1] + counts[2] 48 if total != 32 * 32 { return 20 } 49 // Each of the 3 sites should own a non-trivial slice. 50 if counts[0] < 100 { return 21 } 51 if counts[1] < 100 { return 22 } 52 if counts[2] < 100 { return 23 } 53 54 // === Test 4: CV classifier returns valid band ================== 55 let band: nx_int = nx_voronoi_classify(counts, 3) 56 if nx_voronoi_band_is_valid(band) != 1 { return 30 } 57 58 // === Test 5: uniform-grid sites should classify as UNIFORM ===== 59 // 60 // Place 4 sites at corners of a 32x32 canvas -- each owns a 61 // 16x16 quadrant; cell counts will be near-equal -> UNIFORM band. 62 xs[0] = 8; ys[0] = 8 63 xs[1] = 24; ys[1] = 8 64 xs[2] = 8; ys[2] = 24 65 xs[3] = 24; ys[3] = 24 66 nx_voronoi_label(32, 32, xs, ys, 4, labels) 67 nx_voronoi_site_counts(32, 32, labels, 4, counts) 68 let band_uniform: nx_int = nx_voronoi_classify(counts, 4) 69 if band_uniform != NX_VORONOI_BAND_UNIFORM { return 40 } 70 71 // === Test 6: degenerate placement classifies as DEGENERATE ===== 72 // 73 // 4 sites packed in one corner -- one of them owns nearly the 74 // whole canvas, others own tiny slivers. 75 xs[0] = 0; ys[0] = 0 76 xs[1] = 1; ys[1] = 0 77 xs[2] = 0; ys[2] = 1 78 xs[3] = 31; ys[3] = 31 79 nx_voronoi_label(32, 32, xs, ys, 4, labels) 80 nx_voronoi_site_counts(32, 32, labels, 4, counts) 81 let band_degen: nx_int = nx_voronoi_classify(counts, 4) 82 if nx_voronoi_band_is_valid(band_degen) != 1 { return 50 } 83 if band_degen == NX_VORONOI_BAND_UNIFORM { return 51 } 84 85 // === Test 7: composition with Poisson disk sites =============== 86 // 87 // Generate sites via Bridson Poisson, label, classify. Poisson's 88 // uniform-blue-noise property should produce UNIFORM or BALANCED. 89 let p_xs: *i64 = (sys_mmap(64 * NX_SIZEOF_NX_INT)) as *i64 90 let p_ys: *i64 = (sys_mmap(64 * NX_SIZEOF_NX_INT)) as *i64 91 let n_p: nx_int = nx_poisson_disk_sample(13, 32, 32, 6, p_xs, p_ys, 64) 92 if n_p < 4 { return 60 } 93 nx_voronoi_label(32, 32, p_xs, p_ys, n_p, labels) 94 let p_counts: *i64 = (sys_mmap(64 * NX_SIZEOF_NX_INT)) as *i64 95 nx_voronoi_site_counts(32, 32, labels, n_p, p_counts) 96 let p_total: nx_int = 0 97 var pi: nx_int = 0 98 var sum_check: nx_int = 0 99 while pi < n_p { 100 sum_check = sum_check + p_counts[pi] 101 pi = pi + 1 102 } 103 if sum_check != 32 * 32 { return 61 } 104 let p_band: nx_int = nx_voronoi_classify(p_counts, n_p) 105 if nx_voronoi_band_is_valid(p_band) != 1 { return 62 } 106 if p_band == NX_VORONOI_BAND_DEGENERATE { return 63 } 107 108 // === Test 8: sealed-enum validity ============================== 109 if nx_voronoi_band_is_valid(NX_VORONOI_BAND_UNIFORM) != 1 { return 70 } 110 if nx_voronoi_band_is_valid(NX_VORONOI_BAND_BALANCED) != 1 { return 71 } 111 if nx_voronoi_band_is_valid(NX_VORONOI_BAND_LOPSIDED) != 1 { return 72 } 112 if nx_voronoi_band_is_valid(NX_VORONOI_BAND_DEGENERATE) != 1 { return 73 } 113 if nx_voronoi_band_is_valid(NX_VORONOI_N_BANDS) != 0 { return 74 } 114 if nx_voronoi_band_is_valid(99) != 0 { return 75 } 115 if nx_voronoi_band_is_valid(0 - 1) != 0 { return 76 } 116 117 return 0 118}