code wiki / _hdl_build / nx_geo_simplify.nx

nx_geo_simplify.nx source

↩ module page · 90 lines · 3290 B

1// nx_geo_simplify.nx -- LIB: GEO-014 LINE SIMPLIFY (Douglas-Peucker), DETERMINISTIC INTEGER ARITH. 2// 3// THE EXCEED ANGLE (measured, not asserted): the perpendicular distance of a point to a segment is 4// computed as |cross| / isqrt(|AB|^2) using INTEGER cross products + nx_isqrt -- ZERO floating point. 5// So the keep/drop decision against the tolerance is DETERMINISTIC and reproducible to the bit, where 6// Turf.js / Mapbox simplify (float) can keep or drop a near-tolerance vertex differently across 7// platforms. (We divide rather than compare cross^2 vs eps^2*ab^2 because cross^2 overflows i64 at 8// Earth scale; |cross| <= ~2.6e17 and ab2 <= ~2.6e17 both fit, so the integer divide is overflow-safe.) 9// 10// Convention (matches nx_geo): pts = flat [lat0,lon0, ...], n points, lat=Y, lon=X. Writes the kept 11// vertex INDICES (in order, endpoints always kept) to out_idx; returns the simplified count. Iterative 12// (explicit stack, no recursion). license_tier: ORIGINAL 13import "nx_isqrt.nx" 14import "nx_syscalls.nx" 15 16func geo_iabs(x: i64) -> i64 { if x < 0 { return 0 - x } return x } 17 18// integer perpendicular distance (microdeg) of point p to the segment a->b (all indices into pts). 19func geo_perp_dist_idx(pts: *i64, a: i64, b: i64, p: i64) -> i64 { 20 let ax: i64 = pts[a * 2 + 1] 21 let ay: i64 = pts[a * 2] 22 let bx: i64 = pts[b * 2 + 1] 23 let by: i64 = pts[b * 2] 24 let px: i64 = pts[p * 2 + 1] 25 let py: i64 = pts[p * 2] 26 let dx: i64 = bx - ax 27 let dy: i64 = by - ay 28 if dx == 0 { 29 if dy == 0 { 30 // degenerate segment: distance is point-to-point. 31 let ex: i64 = px - ax 32 let ey: i64 = py - ay 33 return nx_isqrt(ex * ex + ey * ey) 34 } 35 } 36 var cross: i64 = dx * (py - ay) - dy * (px - ax) 37 cross = geo_iabs(cross) 38 let ab: i64 = nx_isqrt(dx * dx + dy * dy) 39 if ab == 0 { return 0 } 40 return cross / ab 41} 42 43// Douglas-Peucker: keep endpoints + every vertex whose perpendicular distance exceeds eps (microdeg). 44func geo_simplify(pts: *i64, n: i64, eps: i64, out_idx: *i64) -> i64 { 45 if n <= 2 { 46 var t: i64 = 0 47 while t < n { out_idx[t] = t; t = t + 1 } 48 return n 49 } 50 let keep: *i64 = sys_mmap(8 * n) as *i64 51 var i: i64 = 0 52 while i < n { keep[i] = 0; i = i + 1 } 53 keep[0] = 1 54 keep[n - 1] = 1 55 56 // explicit segment stack (lo,hi); max depth <= n. 57 let slo: *i64 = sys_mmap(8 * (n + 2)) as *i64 58 let shi: *i64 = sys_mmap(8 * (n + 2)) as *i64 59 var top: i64 = 0 60 slo[top] = 0; shi[top] = n - 1; top = top + 1 61 62 while top > 0 { 63 top = top - 1 64 let lo: i64 = slo[top] 65 let hi: i64 = shi[top] 66 var idx: i64 = 0 - 1 67 var maxd: i64 = 0 - 1 68 var k: i64 = lo + 1 69 while k < hi { 70 let d: i64 = geo_perp_dist_idx(pts, lo, hi, k) 71 if d > maxd { maxd = d; idx = k } 72 k = k + 1 73 } 74 if idx >= 0 { 75 if maxd > eps { 76 keep[idx] = 1 77 slo[top] = lo; shi[top] = idx; top = top + 1 78 slo[top] = idx; shi[top] = hi; top = top + 1 79 } 80 } 81 } 82 83 var c: i64 = 0 84 i = 0 85 while i < n { 86 if keep[i] == 1 { out_idx[c] = i; c = c + 1 } 87 i = i + 1 88 } 89 return c 90}