docvortex_core/
table_candidates.rs1use crate::row_geometry::RowGeometry;
3use std::collections::HashMap;
4
5pub struct RuleCandidates {
6 centers: Vec<f64>,
7 bands: Vec<usize>,
8 on_rule: Vec<bool>,
9 fragment_counts: Vec<usize>,
10 boxes: Vec<[f64; 4]>,
11 row_centers: Vec<f64>,
12 members: Vec<usize>,
13 member_offsets: Vec<usize>,
14 source_ids: Vec<i64>,
15 geometry: RowGeometry,
16}
17
18impl RuleCandidates {
19 pub fn new(
21 centers: Vec<f64>,
22 rows: Vec<(f64, usize, [f64; 4], Vec<i64>)>,
23 ) -> Result<Self, &'static str> {
24 if centers.iter().any(|v| !v.is_finite())
25 || centers.windows(2).any(|pair| pair[0] >= pair[1])
26 || rows
27 .iter()
28 .any(|row| !row.0.is_finite() || row.2.iter().any(|v| !v.is_finite()))
29 {
30 return Err("nonfinite or unordered rule candidate geometry");
31 }
32 let mut bands = Vec::with_capacity(rows.len());
33 let mut on_rule = Vec::with_capacity(rows.len());
34 let mut fragment_counts = Vec::with_capacity(rows.len());
35 let mut boxes = Vec::with_capacity(rows.len());
36 let mut row_centers = Vec::with_capacity(rows.len());
37 let mut members = Vec::new();
38 let mut member_offsets = Vec::with_capacity(rows.len() + 1);
39 let mut source_ids = Vec::new();
40 let mut interned = HashMap::new();
41 member_offsets.push(0);
42 for (center, count, bbox, sources) in rows {
43 row_centers.push(center);
44 let band = centers.partition_point(|value| *value < center);
45 bands.push(band);
46 on_rule.push(centers.get(band) == Some(¢er));
47 fragment_counts.push(count);
48 boxes.push(bbox);
49 for source in sources {
51 let id = *interned.entry(source).or_insert_with(|| {
52 let id = source_ids.len();
53 source_ids.push(source);
54 id
55 });
56 members.push(id);
57 }
58 member_offsets.push(members.len());
59 }
60 let geometry = RowGeometry::new(boxes.clone()).ok_or("invalid row geometry")?;
61 Ok(Self {
62 centers,
63 bands,
64 on_rule,
65 fragment_counts,
66 boxes,
67 row_centers,
68 members,
69 member_offsets,
70 source_ids,
71 geometry,
72 })
73 }
74
75 pub fn short_intervals(
77 &self,
78 first: usize,
79 count: usize,
80 height: f64,
81 ) -> Result<bool, &'static str> {
82 if first
83 .checked_add(count)
84 .is_none_or(|end| end > self.centers.len())
85 || !height.is_finite()
86 {
87 return Err("invalid interval height range");
88 }
89 Ok(count >= 2
90 && self.centers[first..first + count]
91 .windows(2)
92 .all(|pair| pair[1] - pair[0] <= 6.0 * height))
93 }
94
95 pub fn segments(
97 &self,
98 start: usize,
99 end: usize,
100 height: f64,
101 ) -> Result<Vec<Vec<usize>>, &'static str> {
102 if start > end || end > self.boxes.len() || !height.is_finite() {
103 return Err("invalid row segment range");
104 }
105 let mut ordered: Vec<_> = (start..end).collect();
106 ordered.sort_by(|a, b| {
107 self.row_centers[*a]
108 .partial_cmp(&self.row_centers[*b])
109 .unwrap()
110 });
111 let mut output: Vec<Vec<usize>> = Vec::new();
112 for index in ordered {
113 if output.last().is_none_or(|group| {
114 (self.boxes[index][1] - self.boxes[*group.last().unwrap()][3]).max(0.0)
115 > 3.0 * height
116 }) {
117 output.push(vec![index]);
118 } else {
119 output.last_mut().unwrap().push(index);
120 }
121 }
122 Ok(output)
123 }
124
125 pub fn partition(
127 &self,
128 start: usize,
129 end: usize,
130 first: usize,
131 rule_count: usize,
132 height: f64,
133 ) -> Result<(Vec<Vec<usize>>, bool), &'static str> {
134 if start > end
135 || end > self.bands.len()
136 || first
137 .checked_add(rule_count)
138 .is_none_or(|last| last > self.centers.len())
139 || !height.is_finite()
140 {
141 return Err("invalid rule candidate interval");
142 }
143 let count = rule_count.saturating_sub(1);
144 let mut groups = vec![Vec::new(); count];
145 let mut dense = vec![false; count];
146 for row in start..end {
147 let band = self.bands[row];
148 if band > first && band - first - 1 < count {
149 let local = band - first - 1;
150 groups[local].push(row);
151 dense[local] |= self.fragment_counts[row] >= 2;
152 }
153 if self.on_rule[row] && band >= first && band - first < count {
154 let local = band - first;
155 groups[local].push(row);
156 dense[local] |= self.fragment_counts[row] >= 2;
157 }
158 }
159 let accepted = rule_count >= 2
160 && (0..count).all(|i| {
161 dense[i]
162 || (i == 0
163 && !groups[i].is_empty()
164 && self.centers[first + 1] - self.centers[first] <= 2.5 * height)
165 });
166 Ok((groups, accepted))
167 }
168
169 pub fn core(
171 &self,
172 start: usize,
173 end: usize,
174 rules: &[[f64; 4]],
175 ) -> Result<(Vec<i64>, [usize; 4]), &'static str> {
176 if start >= end
177 || end > self.boxes.len()
178 || rules.is_empty()
179 || rules.iter().flatten().any(|v| !v.is_finite())
180 {
181 return Err("invalid rule candidate core");
182 }
183 let selected = &self.members[self.member_offsets[start]..self.member_offsets[end]];
184 let mut seen = vec![0_u64; self.source_ids.len().div_ceil(64)];
186 let mut members = Vec::with_capacity(selected.len().min(self.source_ids.len()));
187 for &id in selected {
188 let word = id / 64;
189 let bit = 1_u64 << (id % 64);
190 if seen[word] & bit == 0 {
191 seen[word] |= bit;
192 members.push(self.source_ids[id]);
193 }
194 }
195 let mut sources = [0; 4];
196 for index in 1..rules.len() {
197 for axis in 0..4 {
198 if better(rules[index][axis], rules[sources[axis]][axis], axis) {
199 sources[axis] = index;
200 }
201 }
202 }
203 let rows = self
204 .geometry
205 .union_indices(start, end)
206 .ok_or("empty candidate core")?;
207 for axis in 0..4 {
208 if better(
209 self.boxes[rows[axis]][axis],
210 rules[sources[axis]][axis],
211 axis,
212 ) {
213 sources[axis] = rules.len() + rows[axis];
214 }
215 }
216 Ok((members, sources))
217 }
218}
219
220fn better(candidate: f64, current: f64, axis: usize) -> bool {
222 if axis < 2 {
223 candidate < current
224 } else {
225 candidate > current
226 }
227}
228
229pub fn repeated_fill_bands(boxes: &[[f64; 4]], rule: [f64; 4], em: f64) -> usize {
231 let minimum_width = (8.0 * em).max(0.3 * (rule[2] - rule[0]));
232 let mut candidates: Vec<[f64; 4]> = Vec::new();
233 for &b in boxes {
234 let width = b[2] - b[0];
235 let height = b[3] - b[1];
236 let center = (b[1] + b[3]) / 2.0;
237 let overlap = 0.0_f64.max(b[2].min(rule[2]) - b[0].max(rule[0]));
238 let shorter = width.min(rule[2] - rule[0]);
239 let ratio = if shorter > 0.0 {
240 overlap / shorter
241 } else {
242 0.0
243 };
244 if width < minimum_width
245 || !(0.25 * em <= height && height <= 3.0 * em)
246 || center < rule[1]
247 || center > rule[3]
248 || ratio < 0.8
249 {
250 continue;
251 }
252 if candidates.iter().any(|a| {
253 let width = 0.0_f64.max(b[2].min(a[2]) - b[0].max(a[0]));
254 let height = 0.0_f64.max(b[3].min(a[3]) - b[1].max(a[1]));
255 let smaller = ((b[2] - b[0]) * (b[3] - b[1])).min((a[2] - a[0]) * (a[3] - a[1]));
256 smaller > 0.0 && width * height / smaller >= 0.9
257 }) {
258 continue;
259 }
260 candidates.push(b);
261 }
262 let tolerance = 3.0_f64.max(em);
263 let mut groups: Vec<([f64; 4], usize)> = Vec::new();
264 for b in candidates {
265 let target = groups.iter_mut().find(|(a, _)| {
266 (b[0] - a[0]).abs() <= tolerance
267 && (b[2] - a[2]).abs() <= tolerance
268 && ((b[3] - b[1]) - (a[3] - a[1])).abs() <= tolerance
269 });
270 if let Some((_, count)) = target {
271 *count += 1;
272 } else {
273 groups.push((b, 1));
274 }
275 }
276 groups.iter().map(|(_, count)| *count).max().unwrap_or(0)
277}