connectivity_core/
coverage.rs1use crate::affine::Affine;
2use ndarray::parallel::prelude::*;
3use ndarray::{s, Array2, Axis};
4
5pub type Polygon = (Vec<(f64, f64)>, Vec<Vec<(f64, f64)>>);
8
9pub fn pixel_coverage(
20 polygons: &[Polygon],
21 transform: &Affine,
22 nrows: usize,
23 ncols: usize,
24) -> Array2<f32> {
25 let mut out = Array2::<f32>::zeros((nrows, ncols));
26
27 for (exterior, holes) in polygons {
28 let ext_px = to_pixel(exterior, transform);
29 accumulate_ring(&mut out, &ext_px, 1.0, nrows, ncols);
30 for hole in holes {
31 let hole_px = to_pixel(hole, transform);
32 accumulate_ring(&mut out, &hole_px, -1.0, nrows, ncols);
33 }
34 }
35
36 out.mapv_inplace(|v| v.clamp(0.0, 1.0));
37 out
38}
39
40fn to_pixel(ring: &[(f64, f64)], tr: &Affine) -> Vec<(f64, f64)> {
42 ring.iter().map(|&(x, y)| tr.col_row(x, y)).collect()
43}
44
45fn accumulate_ring(
48 out: &mut Array2<f32>,
49 ring: &[(f64, f64)],
50 sign: f32,
51 nrows: usize,
52 ncols: usize,
53) {
54 if ring.len() < 3 {
55 return;
56 }
57
58 let (mut xmin, mut ymin) = (f64::INFINITY, f64::INFINITY);
60 let (mut xmax, mut ymax) = (f64::NEG_INFINITY, f64::NEG_INFINITY);
61 for &(x, y) in ring {
62 if x < xmin { xmin = x; }
63 if x > xmax { xmax = x; }
64 if y < ymin { ymin = y; }
65 if y > ymax { ymax = y; }
66 }
67
68 let c0 = clamp_to_usize(xmin.floor(), ncols);
70 let c1 = clamp_to_usize(xmax.ceil(), ncols);
71 let r0 = clamp_to_usize(ymin.floor(), nrows);
72 let r1 = clamp_to_usize(ymax.ceil(), nrows);
73 if c0 >= c1 || r0 >= r1 {
74 return;
75 }
76
77 out.slice_mut(s![r0..r1, c0..c1])
79 .axis_iter_mut(Axis(0))
80 .into_par_iter()
81 .enumerate()
82 .for_each(|(local_r, mut row)| {
83 let r = r0 + local_r;
84 let r_top = r as f64;
85 let r_bot = r_top + 1.0;
86
87 let strip = clip_half(ring, 1, r_top, true);
89 let strip = clip_half(&strip, 1, r_bot, false);
90 if strip.len() < 3 {
91 return;
92 }
93
94 let mut sxmin = f64::INFINITY;
96 let mut sxmax = f64::NEG_INFINITY;
97 for &(x, _) in &strip {
98 if x < sxmin { sxmin = x; }
99 if x > sxmax { sxmax = x; }
100 }
101 let row_c0 = clamp_to_usize(sxmin.floor(), ncols).max(c0);
102 let row_c1 = clamp_to_usize(sxmax.ceil(), ncols).min(c1);
103
104 for c in row_c0..row_c1 {
105 let x_left = c as f64;
106 let x_right = x_left + 1.0;
107 let cell = clip_half(&strip, 0, x_left, true);
108 let cell = clip_half(&cell, 0, x_right, false);
109 let area = shoelace_abs(&cell) as f32;
110 if area > 0.0 {
111 row[c - c0] += sign * area;
112 }
113 }
114 });
115}
116
117fn clip_half(poly: &[(f64, f64)], axis: usize, threshold: f64, keep_geq: bool) -> Vec<(f64, f64)> {
121 let n = poly.len();
122 if n == 0 {
123 return Vec::new();
124 }
125
126 let coord = |p: (f64, f64)| if axis == 0 { p.0 } else { p.1 };
127 let inside = |p: (f64, f64)| {
128 let v = coord(p);
129 if keep_geq { v >= threshold } else { v <= threshold }
130 };
131 let intersect = |s: (f64, f64), p: (f64, f64)| -> (f64, f64) {
132 let cs = coord(s);
133 let cp = coord(p);
134 let t = (threshold - cs) / (cp - cs);
135 (s.0 + t * (p.0 - s.0), s.1 + t * (p.1 - s.1))
136 };
137
138 let mut out = Vec::with_capacity(n + 2);
139 let mut s = poly[n - 1];
140 let mut s_in = inside(s);
141 for &p in poly {
142 let p_in = inside(p);
143 if p_in {
144 if !s_in {
145 out.push(intersect(s, p));
146 }
147 out.push(p);
148 } else if s_in {
149 out.push(intersect(s, p));
150 }
151 s = p;
152 s_in = p_in;
153 }
154 out
155}
156
157fn shoelace_abs(poly: &[(f64, f64)]) -> f64 {
159 if poly.len() < 3 {
160 return 0.0;
161 }
162 let n = poly.len();
163 let mut sum = 0.0;
164 for i in 0..n {
165 let (x1, y1) = poly[i];
166 let (x2, y2) = poly[(i + 1) % n];
167 sum += x1 * y2 - x2 * y1;
168 }
169 (sum * 0.5).abs()
170}
171
172fn clamp_to_usize(v: f64, max: usize) -> usize {
174 if !v.is_finite() || v <= 0.0 {
175 0
176 } else if v >= max as f64 {
177 max
178 } else {
179 v as usize
180 }
181}