Skip to main content

docvortex_core/
geometry_runs.rs

1//! 连续生成全文字体 run 统计,保留样本成员、相邻对和同族传播顺序。
2use 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
32/// 校验样本并保留成员和 bearing,供样式判断及后续 run 计算连续复用。
33pub 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
57/// 在最终 source 确定后计算相邻对与同族传播,避免样式恢复前后重复计算。
58pub 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}