Skip to main content

docvortex_core/
spatial.rs

1//! 有界同行区间查询;每次只返回一小批行,不保存平方规模的配对集合。
2
3use crate::geometry::Box4;
4
5/// 复现有限框的水平交叠比例,零宽和反向框保持原来的零支持量。
6fn x_overlap(a: Box4, b: Box4) -> f64 {
7    let length = a[2].min(b[2]) - a[0].max(b[0]);
8    let smaller = (a[2] - a[0]).min(b[2] - b[0]);
9    if smaller > 0.0 {
10        (if length > 0.0 { length } else { 0.0 }) / smaller
11    } else {
12        0.0
13    }
14}
15
16/// 批量计算标题净空,按源顺序处理相同对象和视觉行,溢出交给 Python。
17pub fn title_gaps(
18    records: Vec<(usize, Option<usize>, Box4, f64, bool)>,
19) -> Option<Vec<(Option<f64>, Option<f64>)>> {
20    if records.iter().any(|r| {
21        r.2.iter().any(|v| !v.is_finite())
22            || !r.3.is_finite()
23            || !(r.2[2] - r.2[0]).is_finite()
24            || !(r.2[1] + r.2[3]).is_finite()
25    }) {
26        return None;
27    }
28    let centers: Vec<_> = records.iter().map(|r| (r.2[1] + r.2[3]) / 2.0).collect();
29    let mut output = Vec::with_capacity(records.len());
30    for (i, a) in records.iter().enumerate() {
31        let (mut above, mut below): (Option<f64>, Option<f64>) = (None, None);
32        for (j, b) in records.iter().enumerate() {
33            if a.0 == b.0 || x_overlap(a.2, b.2) < 0.1 || (a.1.is_some() && a.1 == b.1) {
34                continue;
35            }
36            let (first, second, target) = if centers[j] < centers[i] {
37                (b, a, &mut above)
38            } else if centers[j] > centers[i] {
39                (a, b, &mut below)
40            } else {
41                continue;
42            };
43            let gap = if first.4 {
44                (second.2[1] - first.2[3]).max(-0.25 * first.3)
45            } else {
46                second.2[1] - (first.2[1] + first.3)
47            };
48            if !gap.is_finite() {
49                return None;
50            }
51            let gap = if gap > 0.0 { gap } else { 0.0 };
52            if target.is_none_or(|previous| gap < previous) {
53                *target = Some(gap);
54            }
55        }
56        output.push((above, below));
57    }
58    Some(output)
59}
60
61/// 为一页几何分析选择最近上下邻行;距离相等保留第一个来源,返回索引而非复制对象。
62pub fn line_neighbors(
63    records: Vec<(usize, Box4, f64, f64)>,
64) -> Option<Vec<(Option<usize>, Option<usize>)>> {
65    if records.iter().any(|r| {
66        r.1.iter().any(|v| !v.is_finite())
67            || !r.2.is_finite()
68            || !r.3.is_finite()
69            || !(r.1[2] - r.1[0]).is_finite()
70    }) {
71        return None;
72    }
73    let mut output = Vec::with_capacity(records.len());
74    for a in &records {
75        let (mut above, mut below): (Option<usize>, Option<usize>) = (None, None);
76        for (j, b) in records.iter().enumerate() {
77            if a.0 == b.0 {
78                continue;
79            }
80            let height = a.2.max(b.2);
81            let shift = (b.3 - a.3).abs();
82            if !shift.is_finite() || !(2.0 * height.max(1.0)).is_finite() {
83                return None;
84            }
85            if !(x_overlap(a.1, b.1) >= 0.35 || (a.1[0] - b.1[0]).abs() <= 2.0 * height.max(1.0))
86                || shift < 2.0_f64.max(0.6 * a.2)
87            {
88                continue;
89            }
90            if b.3 < a.3 && above.is_none_or(|k| b.3 > records[k].3) {
91                above = Some(j);
92            }
93            if b.3 > a.3 && below.is_none_or(|k| b.3 < records[k].3) {
94                below = Some(j);
95            }
96        }
97        output.push((above, below));
98    }
99    Some(output)
100}
101
102#[derive(Debug)]
103struct Group {
104    order: Vec<usize>,
105    lows: Vec<f64>,
106    maxima: Vec<f64>,
107    width: usize,
108}
109
110#[derive(Debug)]
111pub struct IntervalIndex {
112    bounds: Vec<(f64, f64)>,
113    group_ids: Vec<usize>,
114    groups: Vec<Group>,
115}
116
117impl IntervalIndex {
118    /// 建立按左端点排序的最大右端点树,输入必须是已经验证的有限区间。
119    pub fn new(bounds: Vec<(f64, f64)>, group_ids: Vec<usize>) -> Option<Self> {
120        if bounds.len() != group_ids.len()
121            || bounds
122                .iter()
123                .any(|(a, b)| !a.is_finite() || !b.is_finite() || a > b)
124            || group_ids.iter().any(|g| *g >= bounds.len())
125        {
126            return None;
127        }
128        let count = group_ids.iter().max().map_or(0, |g| g + 1);
129        let mut members = vec![Vec::new(); count];
130        for (i, &group) in group_ids.iter().enumerate() {
131            members[group].push(i);
132        }
133        let mut groups = Vec::with_capacity(count);
134        for mut order in members {
135            order.sort_by(|a, b| {
136                bounds[*a]
137                    .0
138                    .partial_cmp(&bounds[*b].0)
139                    .unwrap()
140                    .then(a.cmp(b))
141            });
142            let width = order.len().max(1).next_power_of_two();
143            let mut maxima = vec![f64::NEG_INFINITY; 2 * width];
144            for (position, &index) in order.iter().enumerate() {
145                maxima[width + position] = bounds[index].1;
146            }
147            for node in (1..width).rev() {
148                maxima[node] = maxima[2 * node].max(maxima[2 * node + 1]);
149            }
150            let lows = order.iter().map(|i| bounds[*i].0).collect();
151            groups.push(Group {
152                order,
153                lows,
154                maxima,
155                width,
156            });
157        }
158        Some(Self {
159            bounds,
160            group_ids,
161            groups,
162        })
163    }
164
165    /// 返回当前行右侧的相交行,按原始索引排序以保持并查集合并顺序。
166    pub fn query(&self, index: usize) -> Vec<usize> {
167        let (low, high) = self.bounds[index];
168        let group = &self.groups[self.group_ids[index]];
169        let end = group.lows.partition_point(|value| *value <= high);
170        let mut stack = vec![(1, 0, group.width)];
171        let mut result = Vec::new();
172        while let Some((node, left, right)) = stack.pop() {
173            if left >= end || group.maxima[node] < low {
174                continue;
175            }
176            if right - left == 1 {
177                let other = group.order[left];
178                if other > index {
179                    result.push(other);
180                }
181            } else {
182                let middle = (left + right) / 2;
183                stack.push((2 * node + 1, middle, right));
184                stack.push((2 * node, left, middle));
185            }
186        }
187        result.sort_unstable();
188        result
189    }
190
191    /// 批量查询有限行数;单个极密行可以超过预算,但不会缓存整个页面的行对。
192    pub fn rows(&self, start: usize, count: usize, budget: usize) -> Vec<Vec<usize>> {
193        let mut result = Vec::new();
194        let mut total = 0;
195        for index in start..start.saturating_add(count).min(self.bounds.len()) {
196            let row = self.query(index);
197            total += row.len();
198            result.push(row);
199            if total >= budget {
200                break;
201            }
202        }
203        result
204    }
205}
206
207/// 同行安全超集过滤,保留普通框和原始连续字符框两条独立路径。
208pub struct BaselineGeometry {
209    index: IntervalIndex,
210    boxes: Vec<Box4>,
211    heights: Vec<f64>,
212    sources: Vec<Option<Box4>>,
213}
214
215/// 有限正向框的参考同行几何,计算顺序与 Python 一致。
216fn same_baseline(a: Box4, ah: f64, b: Box4, bh: f64) -> bool {
217    let h = ah.max(bh);
218    if ah.min(bh) <= 0.0 || h / ah.min(bh) > 1.35 {
219        return false;
220    }
221    let overlap = 0.0_f64.max(a[3].min(b[3]) - a[1].max(b[1]));
222    let shorter = 0.1_f64.max((a[3] - a[1]).min(b[3] - b[1]));
223    if overlap / shorter < 0.7 && (a[3] - b[3]).abs() > 0.25 * h {
224        return false;
225    }
226    let (left, right) = if a[0] <= b[0] { (a, b) } else { (b, a) };
227    let gap = right[0] - left[2];
228    gap >= -0.25 * h && gap <= 3.0_f64.max(0.75 * h)
229}
230
231/// 验证打包记录,限制幅度避免中间运算溢出改变必要条件。
232fn safe_box(b: &Box4) -> bool {
233    b.iter().all(|v| v.is_finite() && v.abs() <= 1e100) && b[2] > b[0] && b[3] > b[1]
234}
235
236impl BaselineGeometry {
237    /// 每次闭包阶段构建独立只读索引,不复用已经合并的旧几何。
238    pub fn new(
239        bounds: Vec<(f64, f64)>,
240        groups: Vec<usize>,
241        boxes: Vec<Box4>,
242        heights: Vec<f64>,
243        sources: Vec<Option<Box4>>,
244    ) -> Option<Self> {
245        let n = bounds.len();
246        if boxes.len() != n
247            || heights.len() != n
248            || sources.len() != n
249            || boxes.iter().any(|b| !safe_box(b))
250            || heights.iter().any(|h| !h.is_finite() || h.abs() > 1e100)
251            || sources.iter().flatten().any(|b| !safe_box(b))
252        {
253            return None;
254        }
255        Some(Self {
256            index: IntervalIndex::new(bounds, groups)?,
257            boxes,
258            heights,
259            sources,
260        })
261    }
262
263    /// 只剔除所有参考分支均不可能接受的行对,语义与表格判断仍由 Python 执行。
264    fn accepts(&self, i: usize, j: usize) -> bool {
265        let (a, b, ah, bh) = (
266            self.boxes[i],
267            self.boxes[j],
268            self.heights[i],
269            self.heights[j],
270        );
271        if same_baseline(a, ah, b, bh) {
272            return true;
273        }
274        if let (Some(sa), Some(sb)) = (self.sources[i], self.sources[j]) {
275            if same_baseline(sa, sa[3] - sa[1], sb, sb[3] - sb[1]) {
276                return true;
277            }
278        }
279        let h = ah.max(bh);
280        let overlap = 0.0_f64.max(a[3].min(b[3]) - a[1].max(b[1]));
281        let smaller = 0.1_f64.max((a[3] - a[1]).min(b[3] - b[1]));
282        if overlap / smaller < 0.7 || ((a[1] + a[3]) / 2.0 - (b[1] + b[3]) / 2.0).abs() > 0.5 * h {
283            return false;
284        }
285        let (left, right) = if a[0] <= b[0] { (a, b) } else { (b, a) };
286        let gap = right[0] - left[2];
287        gap >= -0.15 * h && gap <= 0.75
288    }
289
290    /// 保序返回有界行批次,单行超预算时仍返回全部成员。
291    pub fn rows(&self, start: usize, count: usize, budget: usize) -> Vec<Vec<usize>> {
292        let mut output = Vec::new();
293        let mut total = 0;
294        for i in start..start.saturating_add(count).min(self.boxes.len()) {
295            let row: Vec<_> = self
296                .index
297                .query(i)
298                .into_iter()
299                .filter(|j| self.accepts(i, *j))
300                .collect();
301            total += row.len();
302            output.push(row);
303            if total >= budget {
304                break;
305            }
306        }
307        output
308    }
309}