code wiki / _hdl_build / nx_geo_clip.nx
nx_geo_clip.nx source
↩ module page · 96 lines · 4577 B
1// nx_geo_clip.nx -- LIB: GEO-015 POLYGON INTERSECTION (Sutherland-Hodgman convex clip).
2//
3// THE EXCEED ANGLE (measured, not asserted): the inside/outside classification of every vertex against
4// every clip edge is the SIGN of an INTEGER cross product -- ZERO floating point, so the topology of
5// the result (which vertices survive, where edges cross) is EXACT and DETERMINISTIC. That is the part
6// float clippers (Turf/PostGIS) get wrong on touching/collinear edges. HONEST SCOPE: the intersection
7// VERTEX itself is S + (E-S)*dS/(dS-dE) computed in integer arithmetic and ROUNDED to microdegrees --
8// deterministic-integer, NOT exact-rational geometry. To stay overflow-safe at any scale the fraction
9// (num,den) is normalized (shifted down in lockstep) before the multiply, since (E-S)*dS would
10// otherwise exceed i64 at continental scale; the ratio is preserved to sub-microdegree.
11//
12// Convention (matches nx_geo): polygons = flat [lat0,lon0, ...], lat=Y, lon=X. The CLIP polygon must be
13// CONVEX and CCW. Writes the clipped polygon to out (caller sizes it >= (ns+nc+8) vertices); returns
14// the output vertex count (0 = no overlap). license_tier: ORIGINAL
15import "nx_syscalls.nx"
16const K_MAGIC_17179869184: i64 = 17179869184
17
18func geo_cabs(x: i64) -> i64 { if x < 0 { return 0 - x } return x }
19
20// is P left-of-or-on the directed clip edge A->B? (inside, for a CCW convex clip polygon). Exact.
21func geo_clip_inside(ax: i64, ay: i64, bx: i64, by: i64, px: i64, py: i64) -> i64 {
22 let cross: i64 = (bx - ax) * (py - ay) - (by - ay) * (px - ax)
23 if cross >= 0 { return 1 }
24 return 0
25}
26
27// intersection of segment S->E with the infinite line A->B, written as [lat,lon] to buf[pos]. Integer,
28// microdeg-rounded; fraction normalized to avoid i64 overflow at any coordinate scale.
29func geo_intersect_into(buf: *i64, pos: i64, sx: i64, sy: i64, ex: i64, ey: i64, ax: i64, ay: i64, bx: i64, by: i64) -> i64 {
30 let ds: i64 = (bx - ax) * (sy - ay) - (by - ay) * (sx - ax)
31 let de: i64 = (bx - ax) * (ey - ay) - (by - ay) * (ex - ax)
32 var num: i64 = ds
33 var den: i64 = ds - de
34 // keep |num|,|den| below 2^34 so (E-S)*num (|E-S| <= 3.6e8) stays under i64's 9.2e18.
35 var go: i64 = 1
36 while go == 1 {
37 if geo_cabs(num) > K_MAGIC_17179869184 { num = num / 2; den = den / 2 } else {
38 if geo_cabs(den) > K_MAGIC_17179869184 { num = num / 2; den = den / 2 } else { go = 0 }
39 }
40 }
41 if den == 0 { buf[pos * 2] = sy; buf[pos * 2 + 1] = sx; return 0 }
42 let ix: i64 = sx + (ex - sx) * num / den
43 let iy: i64 = sy + (ey - sy) * num / den
44 buf[pos * 2] = iy
45 buf[pos * 2 + 1] = ix
46 return 0
47}
48
49// clip the subject polygon by the convex CCW clip polygon (Sutherland-Hodgman). Returns vertex count.
50func geo_poly_clip(subj: *i64, ns: i64, clip: *i64, nc: i64, out: *i64) -> i64 {
51 let cap: i64 = ns + nc + 8
52 let bufA: *i64 = sys_mmap(8 * cap * 2) as *i64
53 let bufB: *i64 = sys_mmap(8 * cap * 2) as *i64
54 var cur: *i64 = bufA
55 var nxt: *i64 = bufB
56 var i: i64 = 0
57 while i < ns { cur[i * 2] = subj[i * 2]; cur[i * 2 + 1] = subj[i * 2 + 1]; i = i + 1 }
58 var curn: i64 = ns
59
60 var ci: i64 = 0
61 while ci < nc {
62 if curn == 0 { ci = nc } else {
63 let ax: i64 = clip[ci * 2 + 1]
64 let ay: i64 = clip[ci * 2]
65 let cj: i64 = (ci + 1) % nc
66 let bx: i64 = clip[cj * 2 + 1]
67 let by: i64 = clip[cj * 2]
68 var nxtn: i64 = 0
69 var k: i64 = 0
70 while k < curn {
71 let sx: i64 = cur[k * 2 + 1]
72 let sy: i64 = cur[k * 2]
73 let ek: i64 = (k + 1) % curn
74 let ex: i64 = cur[ek * 2 + 1]
75 let ey: i64 = cur[ek * 2]
76 let sIn: i64 = geo_clip_inside(ax, ay, bx, by, sx, sy)
77 let eIn: i64 = geo_clip_inside(ax, ay, bx, by, ex, ey)
78 if eIn == 1 {
79 if sIn == 0 { geo_intersect_into(nxt, nxtn, sx, sy, ex, ey, ax, ay, bx, by); nxtn = nxtn + 1 }
80 nxt[nxtn * 2] = ey; nxt[nxtn * 2 + 1] = ex; nxtn = nxtn + 1
81 } else {
82 if sIn == 1 { geo_intersect_into(nxt, nxtn, sx, sy, ex, ey, ax, ay, bx, by); nxtn = nxtn + 1 }
83 }
84 k = k + 1
85 }
86 let tmp: *i64 = cur
87 cur = nxt
88 nxt = tmp
89 curn = nxtn
90 ci = ci + 1
91 }
92 }
93 i = 0
94 while i < curn { out[i * 2] = cur[i * 2]; out[i * 2 + 1] = cur[i * 2 + 1]; i = i + 1 }
95 return curn
96}