nx_poisson_disk_test.nx source
↩ module page · 93 lines · 3921 B
1// nx_poisson_disk_test.nx -- smoke for Bridson 2007 Poisson disk.
2
3import "nx_syscalls.nx"
4import "nx_tier.nx"
5import "nx_poisson_disk.nx"
6
7func main() -> nx_int {
8 // === Test 1: same seed -> identical point sets (determinism) ===
9 let max_pts: nx_int = 256
10 let xs_a: *i64 = (sys_mmap(max_pts * NX_SIZEOF_NX_INT)) as *i64
11 let ys_a: *i64 = (sys_mmap(max_pts * NX_SIZEOF_NX_INT)) as *i64
12 let n_a: nx_int = nx_poisson_disk_sample(42, 64, 64, 8, xs_a, ys_a, max_pts)
13 let xs_b: *i64 = (sys_mmap(max_pts * NX_SIZEOF_NX_INT)) as *i64
14 let ys_b: *i64 = (sys_mmap(max_pts * NX_SIZEOF_NX_INT)) as *i64
15 let n_b: nx_int = nx_poisson_disk_sample(42, 64, 64, 8, xs_b, ys_b, max_pts)
16 if n_a != n_b { return 1 }
17 var i: nx_int = 0
18 while i < n_a {
19 if xs_a[i] != xs_b[i] { return 2 }
20 if ys_a[i] != ys_b[i] { return 3 }
21 i = i + 1
22 }
23
24 // === Test 2: minimum-distance invariant -- no two points within r ===
25 // Bridson's defining property: any pair of returned points must be
26 // at least r apart.
27 let r: nx_int = 8
28 var i2: nx_int = 0
29 while i2 < n_a {
30 var j2: nx_int = i2 + 1
31 while j2 < n_a {
32 let dx: nx_int = xs_a[i2] - xs_a[j2]
33 let dy: nx_int = ys_a[i2] - ys_a[j2]
34 let d_sq: nx_int = dx * dx + dy * dy
35 // Bridson allows points at exactly distance r; allow tiny tolerance.
36 if d_sq < (r * r - r) { return 10 }
37 j2 = j2 + 1
38 }
39 i2 = i2 + 1
40 }
41
42 // === Test 3: all points in bounds ===
43 var i3: nx_int = 0
44 while i3 < n_a {
45 if xs_a[i3] < 0 { return 20 }
46 if xs_a[i3] >= 64 { return 21 }
47 if ys_a[i3] < 0 { return 22 }
48 if ys_a[i3] >= 64 { return 23 }
49 i3 = i3 + 1
50 }
51
52 // === Test 4: different seeds -> different sets (at least different counts OR positions) ===
53 let xs_c: *i64 = (sys_mmap(max_pts * NX_SIZEOF_NX_INT)) as *i64
54 let ys_c: *i64 = (sys_mmap(max_pts * NX_SIZEOF_NX_INT)) as *i64
55 let n_c: nx_int = nx_poisson_disk_sample(99, 64, 64, 8, xs_c, ys_c, max_pts)
56 // Different seed: even if counts match (likely), positions should differ.
57 var any_diff: nx_int = 0
58 if n_a != n_c { any_diff = 1 }
59 if n_a == n_c {
60 var i4: nx_int = 0
61 while i4 < n_a {
62 if xs_a[i4] != xs_c[i4] { any_diff = 1 }
63 if ys_a[i4] != ys_c[i4] { any_diff = 1 }
64 i4 = i4 + 1
65 }
66 }
67 if any_diff != 1 { return 30 }
68
69 // === Test 5: edge case -- r = 0 or w = 0 returns 0 points ===
70 let xs_e: *i64 = (sys_mmap(max_pts * NX_SIZEOF_NX_INT)) as *i64
71 let ys_e: *i64 = (sys_mmap(max_pts * NX_SIZEOF_NX_INT)) as *i64
72 if nx_poisson_disk_sample(1, 64, 64, 0, xs_e, ys_e, max_pts) != 0 { return 40 }
73 if nx_poisson_disk_sample(1, 0, 64, 8, xs_e, ys_e, max_pts) != 0 { return 41 }
74 if nx_poisson_disk_sample(1, 64, 64, 8, xs_e, ys_e, 0) != 0 { return 42 }
75
76 // === Test 6: qualitative density classification ===
77 if nx_poisson_disk_band_is_valid(NX_PD_BAND_SPARSE) != 1 { return 50 }
78 if nx_poisson_disk_band_is_valid(NX_PD_BAND_SATURATED) != 1 { return 51 }
79 if nx_poisson_disk_band_is_valid(99) != 0 { return 52 }
80 // Actual n_a achieved should classify into one of the bands.
81 let band: nx_int = nx_poisson_disk_classify(n_a, 64, 64, 8)
82 if nx_poisson_disk_band_is_valid(band) != 1 { return 53 }
83 // Zero points always SPARSE.
84 if nx_poisson_disk_classify(0, 64, 64, 8) != NX_PD_BAND_SPARSE { return 54 }
85
86 // === Test 7: at least the seed point lands ===
87 if n_a < 1 { return 60 }
88 // And typically Bridson fills ~50-70% of theoretical max for r=8 in 64x64;
89 // so n_a should not be just 1. Loose lower bound: at least 5.
90 if n_a < 5 { return 61 }
91
92 return 0
93}