1use crate::geometry::{Box4, Size};
4use std::collections::HashMap;
5
6pub type PaintRecord = (Option<Box4>, Option<Size>, usize, usize, bool);
7pub type Pair = (usize, usize);
8pub type Offset = (usize, usize, f64, f64);
9pub type MappingRecord = (Option<i64>, i64, i64, usize, f64, Option<Size>, Box4, bool);
10
11pub fn mapping_runs(records: Vec<MappingRecord>) -> Option<Vec<(usize, usize)>> {
13 if records.iter().any(|r| {
14 !r.4.is_finite()
15 || r.6.iter().any(|v| !v.is_finite())
16 || r.5.is_some_and(|p| p.iter().any(|v| !v.is_finite()))
17 }) {
18 return None;
19 }
20 let mut groups = Vec::new();
21 let mut start = 0;
22 for i in 1..records.len() {
23 let a = &records[i - 1];
24 let b = &records[i];
25 let same = a.0.is_some()
26 && a.0 == b.0
27 && a.2.checked_add(1) == Some(b.1)
28 && a.3 == b.3
29 && a.4 == b.4
30 && a.7
31 && b.7
32 && matches!((a.5, b.5), (Some(x), Some(y)) if close(x, y, 0.001))
33 && close(a.6, b.6, 0.001);
34 if !same {
35 groups.push((start, i));
36 start = i;
37 }
38 }
39 if !records.is_empty() {
40 groups.push((start, records.len()));
41 }
42 Some(groups)
43}
44
45fn close<const N: usize>(a: [f64; N], b: [f64; N], epsilon: f64) -> bool {
47 a.iter()
48 .zip(b)
49 .all(|(x, y)| x.is_finite() && y.is_finite() && (*x - y).abs() <= epsilon)
50}
51
52fn overlap(a: Box4, b: Box4) -> f64 {
54 let area = ((a[2] - a[0]) * (a[3] - a[1])).min((b[2] - b[0]) * (b[3] - b[1]));
55 if area > 0.0 {
56 0.0_f64.max(a[2].min(b[2]) - a[0].max(b[0])) * 0.0_f64.max(a[3].min(b[3]) - a[1].max(b[1]))
57 / area
58 } else {
59 0.0
60 }
61}
62
63pub fn paint_pairs(records: Vec<PaintRecord>) -> Option<(Vec<Pair>, Vec<Offset>)> {
65 if records
66 .iter()
67 .any(|r| r.0.is_some_and(|b| b.iter().any(|v| v.abs() > 1e15)))
68 {
69 return None;
70 }
71 let mut buckets: HashMap<(usize, i64, i64), Vec<usize>> = HashMap::new();
72 let mut exact = Vec::new();
73 let mut offsets = Vec::new();
74 for (i, (box_, origin, signature, object, eligible)) in records.iter().enumerate() {
75 if !eligible {
76 continue;
77 }
78 let (Some(b), Some(co)) = (box_, origin) else {
79 continue;
80 };
81 let bx = (b[0] / 2.5).floor() as i64;
82 let by = (b[1] / 2.5).floor() as i64;
83 for x in bx - 1..=bx + 1 {
84 for y in by - 1..=by + 1 {
85 if let Some(candidates) = buckets.get(&(*signature, x, y)) {
86 if candidates.len() >= 64 {
87 continue;
88 }
89 for &j in candidates {
90 let (a, po, _, previous_object, _) = records[j];
91 if previous_object == *object {
92 continue;
93 }
94 let a = a.unwrap();
95 let po = po.unwrap();
96 if close(a, *b, 0.001) && close(po, *co, 0.001) {
97 exact.push((j, i));
98 continue;
99 }
100 let dx = co[0] - po[0];
101 let dy = co[1] - po[1];
102 if !dx.is_finite() || !dy.is_finite() || dx.abs().max(dy.abs()) > 2.5 {
103 continue;
104 }
105 if [b[0] - a[0], b[1] - a[1], b[2] - a[2], b[3] - a[3]]
106 .iter()
107 .zip([dx, dy, dx, dy])
108 .all(|(d, t)| (*d - t).abs() <= 0.1)
109 && overlap(a, *b) >= 0.45
110 {
111 offsets.push((j, i, dx, dy));
112 }
113 }
114 }
115 }
116 }
117 let bucket = buckets.entry((*signature, bx, by)).or_default();
118 if bucket.len() < 64 {
119 bucket.push(i);
120 }
121 }
122 Some((exact, offsets))
123}
124
125pub fn components(count: usize, pairs: &[Pair]) -> Vec<usize> {
127 let mut parents: Vec<_> = (0..count).collect();
128 for &(mut a, mut b) in pairs {
129 while parents[a] != a {
130 parents[a] = parents[parents[a]];
131 a = parents[a];
132 }
133 while parents[b] != b {
134 parents[b] = parents[parents[b]];
135 b = parents[b];
136 }
137 parents[a.max(b)] = a.min(b);
138 }
139 for i in 0..count {
140 parents[i] = parents[parents[i]];
141 }
142 parents
143}
144
145pub type EvidenceRecord = (Option<Box4>, f64, usize, usize, bool);
146fn endpoints(records: &[EvidenceRecord], roots: &[usize], start: usize, end: usize) -> bool {
148 end - start <= 128
149 && (start + 1..end)
150 .all(|i| records[i].4 || roots[i] == roots[start] || roots[i] == roots[end])
151}
152
153pub fn confirmed_offsets(
155 records: Vec<EvidenceRecord>,
156 pairs: Vec<Offset>,
157 exact: Vec<Pair>,
158) -> Vec<Pair> {
159 if pairs.is_empty() {
160 return Vec::new();
161 }
162 let all: Vec<_> = exact
163 .into_iter()
164 .chain(pairs.iter().map(|p| (p.0, p.1)))
165 .collect();
166 let roots = components(records.len(), &all);
167 let mut bins: HashMap<(usize, i64, i64, i64), Vec<usize>> = HashMap::new();
168 let mut key_order = Vec::new();
169 let mut clusters: Vec<Vec<Offset>> = Vec::new();
170 for p in pairs {
171 let r = &records[p.0];
172 let normal = r.1;
173 let key = (
174 r.2,
175 (p.2 / 0.1).floor() as i64,
176 (p.3 / 0.1).floor() as i64,
177 (normal / 0.1).floor() as i64,
178 );
179 let mut found = None;
180 'search: for x in key.1 - 1..=key.1 + 1 {
181 for y in key.2 - 1..=key.2 + 1 {
182 for n in key.3 - 1..=key.3 + 1 {
183 if let Some(ids) = bins.get(&(key.0, x, y, n)) {
184 for &id in ids {
185 let first = clusters[id][0];
186 if (p.2 - first.2).abs() <= 0.1
187 && (p.3 - first.3).abs() <= 0.1
188 && (normal - records[first.0].1).abs() <= 0.1
189 {
190 found = Some(id);
191 break 'search;
192 }
193 }
194 }
195 }
196 }
197 }
198 if let Some(id) = found {
199 clusters[id].push(p);
200 } else {
201 if !bins.contains_key(&key) {
202 key_order.push(key);
203 }
204 bins.entry(key).or_default().push(clusters.len());
205 clusters.push(vec![p]);
206 }
207 }
208 let mut confirmed = Vec::new();
209 for key in key_order {
210 for &id in &bins[&key] {
211 let ordered = &mut clusters[id];
212 ordered.sort_by_key(|p| (p.0, p.1));
213 let mut runs: Vec<Vec<Offset>> = Vec::new();
214 for &p in ordered.iter() {
215 let continuous = if let Some(prev) = runs.last().and_then(|r| r.last()) {
216 let pb = records[prev.0].0.unwrap();
217 let cb = records[p.0].0.unwrap();
218 p.0 > prev.0
219 && p.1 > prev.1
220 && cb[0] >= pb[0]
221 && cb[0] - pb[2] <= 1.0_f64.max(0.6 * (pb[3] - pb[1]).min(cb[3] - cb[1]))
222 && endpoints(&records, &roots, prev.0, p.0)
223 && endpoints(&records, &roots, prev.1, p.1)
224 } else {
225 false
226 };
227 if !continuous {
228 runs.push(Vec::new());
229 }
230 runs.last_mut().unwrap().push(p);
231 }
232 for run in runs {
233 let unique_roots: std::collections::HashSet<_> =
234 run.iter().map(|p| roots[p.0]).collect();
235 let unique_text: std::collections::HashSet<_> =
236 run.iter().map(|p| records[p.0].3).collect();
237 if unique_roots.len() >= 3 && unique_text.len() >= 2 {
238 confirmed.extend(run.into_iter().map(|p| (p.0, p.1)));
239 }
240 }
241 }
242 }
243 confirmed
244}
245
246pub fn project(b: Box4, co: f64, si: f64) -> Box4 {
248 let points = [(b[0], b[1]), (b[0], b[3]), (b[2], b[1]), (b[2], b[3])]
249 .map(|(x, y)| (co * x + si * y, -si * x + co * y));
250 let mut out = [points[0].0, points[0].1, points[0].0, points[0].1];
251 for (x, y) in &points[1..] {
252 out = [
253 out[0].min(*x),
254 out[1].min(*y),
255 out[2].max(*x),
256 out[3].max(*y),
257 ];
258 }
259 out
260}
261
262pub type HiddenRecord = (
263 Option<Box4>,
264 Option<Size>,
265 usize,
266 i8,
267 bool,
268 bool,
269 f64,
270 f64,
271 f64,
272 usize,
273);
274
275pub fn hidden_candidates(records: Vec<HiddenRecord>) -> Option<Vec<(Vec<usize>, Vec<usize>)>> {
277 if records.iter().any(|r| {
278 r.0.is_some_and(|b| b.iter().any(|v| !v.is_finite() || v.abs() > 1e15))
279 }) {
280 return None;
281 }
282 let mut grid: HashMap<(i64, i64), Vec<usize>> = HashMap::new();
283 let mut key_order = Vec::new();
284 let mut runs: Vec<Vec<usize>> = Vec::new();
285 for (i, r) in records.iter().enumerate() {
286 let Some(b) = r.0 else {
287 continue;
288 };
289 if !r.5 {
290 continue;
291 }
292 if [0, 1, 2, 4, 5, 6].contains(&r.3) && r.4 {
293 let key = (
294 ((b[0] + b[2]) / 2.0 / 32.0).floor() as i64,
295 ((b[1] + b[3]) / 2.0 / 32.0).floor() as i64,
296 );
297 if !grid.contains_key(&key) {
298 key_order.push(key);
299 }
300 grid.entry(key).or_default().push(i);
301 } else if r.3 == 3 {
302 let mut same = false;
303 if let Some(previous) = runs.last().and_then(|v| v.last()).map(|j| &records[*j]) {
304 if previous.2 == r.2 && (previous.6 - r.6).abs() <= 0.001 {
305 let pb = project(previous.0.unwrap(), r.7, r.8);
306 let cb = project(b, r.7, r.8);
307 if let (Some(po), Some(co)) = (previous.1, r.1) {
308 same = (-r.8 * (po[0] - co[0]) + r.7 * (po[1] - co[1])).abs() <= 0.1
309 && cb[0] >= pb[0]
310 && cb[0] - pb[2] <= 1.0_f64.max(0.6 * (cb[3] - cb[1]));
311 }
312 }
313 }
314 if !same {
315 runs.push(Vec::new());
316 }
317 runs.last_mut().unwrap().push(i);
318 }
319 }
320 let mut result = Vec::new();
321 for run in runs {
322 let first = &records[run[0]];
323 let boxes: Vec<_> = run.iter().map(|i| records[*i].0.unwrap()).collect();
324 let b = crate::geometry::union(&boxes).unwrap();
325 let p = project(b, first.7, first.8);
326 let margin = 0.1 * (p[3] - p[1]);
327 let x0 = ((b[0] - margin) / 32.0).floor() as i64;
328 let x1 = ((b[2] + margin) / 32.0).floor() as i64;
329 let y0 = ((b[1] - margin) / 32.0).floor() as i64;
330 let y1 = ((b[3] + margin) / 32.0).floor() as i64;
331 let keys: Vec<_> = if (x1 - x0 + 1) as i128 * (y1 - y0 + 1) as i128 > 4 * grid.len() as i128
332 {
333 key_order
334 .iter()
335 .copied()
336 .filter(|(x, y)| x0 <= *x && *x <= x1 && y0 <= *y && *y <= y1)
337 .collect()
338 } else {
339 (x0..=x1)
340 .flat_map(|x| (y0..=y1).map(move |y| (x, y)))
341 .collect()
342 };
343 let mut candidates = Vec::new();
344 for key in keys {
345 if let Some(items) = grid.get(&key) {
346 for &i in items {
347 let r = &records[i];
348 if (r.6 - first.6).abs() > 0.001 {
349 continue;
350 }
351 let q = project(r.0.unwrap(), first.7, first.8);
352 if p[0] - margin <= (q[0] + q[2]) / 2.0
353 && (q[0] + q[2]) / 2.0 <= p[2] + margin
354 && p[1] <= (q[1] + q[3]) / 2.0
355 && (q[1] + q[3]) / 2.0 <= p[3]
356 {
357 candidates.push(i);
358 }
359 }
360 }
361 }
362 if candidates.is_empty() {
363 continue;
364 }
365 candidates.sort_by(|a, b| {
366 let x = project(records[*a].0.unwrap(), first.7, first.8)[0];
367 let y = project(records[*b].0.unwrap(), first.7, first.8)[0];
368 x.partial_cmp(&y)
369 .unwrap()
370 .then(records[*a].9.cmp(&records[*b].9))
371 });
372 let projected: Vec<_> = candidates
373 .iter()
374 .map(|i| project(records[*i].0.unwrap(), first.7, first.8))
375 .collect();
376 let v = crate::geometry::union(&projected).unwrap();
377 let axis = 0.0_f64.max(p[2].min(v[2]) - p[0].max(v[0]));
378 let normal = 0.0_f64.max(p[3].min(v[3]) - p[1].max(v[1]));
379 if axis < 0.8 * (p[2] - p[0]) || normal < 0.8 * (p[3] - p[1]).min(v[3] - v[1]) {
380 continue;
381 }
382 result.push((run, candidates));
383 }
384 Some(result)
385}