docvortex_core/
geometry_runs.rs1use crate::{
3 geometry::{anchor_pairs, Box4, Size},
4 geometry_risk::quantile,
5 median,
6};
7use std::collections::HashSet;
8
9pub struct Sample {
10 pub run: usize,
11 pub position: i64,
12 pub source: Box4,
13 pub tight: Box4,
14 pub origin: Size,
15 pub size: f64,
16 pub anchor: bool,
17}
18
19#[derive(Default)]
20pub struct Run {
21 pub members: Vec<usize>,
22 pub ratios: Vec<f64>,
23 pub overlaps: Vec<bool>,
24 pub advances: Vec<f64>,
25 pub bearings: Vec<f64>,
26 pub median_advance: Option<f64>,
27 pub median_bearing: f64,
28 pub strong: bool,
29 pub sibling: bool,
30}
31
32pub fn group(samples: &[Sample], families: &[usize]) -> Option<Vec<Run>> {
34 if samples.iter().any(|s| {
35 s.run >= families.len()
36 || !s.size.is_finite()
37 || s.source
38 .iter()
39 .chain(&s.tight)
40 .chain(&s.origin)
41 .any(|v| !v.is_finite())
42 }) {
43 return None;
44 }
45 let mut runs: Vec<Run> = (0..families.len()).map(|_| Run::default()).collect();
46 for (index, sample) in samples.iter().enumerate() {
47 let bearing = sample.tight[0] - sample.origin[0];
48 if !bearing.is_finite() {
49 return None;
50 }
51 runs[sample.run].members.push(index);
52 runs[sample.run].bearings.push(bearing);
53 }
54 Some(runs)
55}
56
57pub fn complete(
59 samples: &[Sample],
60 lines: &[Vec<usize>],
61 families: &[usize],
62 runs: &mut [Run],
63) -> Option<()> {
64 if runs.len() != families.len()
65 || lines.iter().flatten().any(|i| *i >= samples.len())
66 || samples
67 .iter()
68 .any(|s| s.run >= runs.len() || s.source.iter().any(|v| !v.is_finite()))
69 {
70 return None;
71 }
72 for line in lines {
73 let mut anchors: Vec<_> = line
74 .iter()
75 .copied()
76 .filter(|i| samples[*i].anchor)
77 .collect();
78 anchors.sort_by_key(|i| samples[*i].position);
79 let rows = anchors
80 .iter()
81 .map(|i| {
82 let s = &samples[*i];
83 (s.run, s.source, s.tight, s.origin, s.size)
84 })
85 .collect();
86 for (index, advance, ratio, overlap) in anchor_pairs(rows, true)? {
87 let run = &mut runs[samples[anchors[index]].run];
88 run.ratios.push(ratio);
89 run.advances.push(advance);
90 run.overlaps.push(overlap);
91 }
92 }
93 for run in runs.iter_mut() {
94 run.median_advance = (!run.advances.is_empty()).then(|| median(run.advances.clone()));
95 run.median_bearing = median(run.bearings.clone());
96 let count = run.ratios.len();
97 if count < 30 {
98 continue;
99 }
100 run.strong = median(run.ratios.clone()) >= 1.30
101 && run.ratios.iter().filter(|v| **v > 1.35).count() as f64 / count as f64 >= 0.30
102 && run.overlaps.iter().filter(|v| **v).count() as f64 / count as f64 >= 0.30;
103 }
104 let bad: HashSet<_> = runs
105 .iter()
106 .enumerate()
107 .filter_map(|(i, r)| r.strong.then_some(families[i]))
108 .collect();
109 for (i, run) in runs.iter_mut().enumerate() {
110 if run.strong || !bad.contains(&families[i]) || run.ratios.len() < 10 {
111 continue;
112 }
113 run.sibling = (median(run.ratios.clone()) >= 1.15
114 && run.overlaps.iter().filter(|v| **v).count() as f64 / run.overlaps.len() as f64
115 >= 0.50)
116 || quantile(run.ratios.clone(), 0.9) >= 1.50;
117 }
118 Some(())
119}