1use crate::median;
4pub type Box4 = [f64; 4];
5pub type Size = [f64; 2];
6
7pub fn anchor_pairs(
9 records: Vec<(usize, Box4, Box4, Size, f64)>,
10 positive_source: bool,
11) -> Option<Vec<(usize, f64, f64, bool)>> {
12 if records.iter().any(|r| {
13 r.1.iter()
14 .chain(r.2.iter())
15 .chain(r.3.iter())
16 .any(|v| !v.is_finite())
17 || !r.4.is_finite()
18 }) {
19 return None;
20 }
21 let mut result = Vec::new();
22 for (index, pair) in records.windows(2).enumerate() {
23 let (a, b) = (&pair[0], &pair[1]);
24 if a.0 != b.0 {
25 continue;
26 }
27 let height = (a.2[3] - a.2[1]).max(b.2[3] - b.2[1]);
28 let width = (a.2[2] - a.2[0]).max(b.2[2] - b.2[0]);
29 let shift = (a.3[1] - b.3[1]).abs();
30 let advance = b.3[0] - a.3[0];
31 let limit = (5.0 * a.4.max(1.0)).max(8.0 * width);
32 if [height, width, shift, advance, limit]
33 .iter()
34 .any(|v| !v.is_finite())
35 {
36 return None;
37 }
38 if shift > 0.5_f64.max(0.25 * height) || !(0.1 < advance && advance <= limit) {
39 continue;
40 }
41 let source_width = a.1[2] - a.1[0];
42 if positive_source && source_width <= 0.0 {
43 continue;
44 }
45 let ratio = source_width / advance;
46 let overlap = a.1[2] - b.2[0];
47 let following_width = b.2[2] - b.2[0];
48 if [ratio, overlap, following_width]
49 .iter()
50 .any(|v| !v.is_finite())
51 {
52 return None;
53 }
54 result.push((
55 index,
56 advance,
57 ratio,
58 overlap >= 0.05 * following_width.max(0.1),
59 ));
60 }
61 Some(result)
62}
63
64pub fn normalize(raw: Option<Box4>, strict: bool) -> Option<Box4> {
66 let mut b = raw?;
67 if b.iter().any(|v| !v.is_finite()) {
68 return None;
69 }
70 if !strict {
71 if b[2] < b[0] {
72 b.swap(0, 2);
73 }
74 if b[3] < b[1] {
75 b.swap(1, 3);
76 }
77 }
78 if b[2] <= b[0] || b[3] <= b[1] {
79 None
80 } else {
81 Some(b)
82 }
83}
84
85fn minimum(a: f64, b: f64) -> f64 {
87 if b < a {
88 b
89 } else {
90 a
91 }
92}
93fn maximum(a: f64, b: f64) -> f64 {
95 if b > a {
96 b
97 } else {
98 a
99 }
100}
101
102pub fn clip(raw: Option<Box4>, size: Size) -> Option<Box4> {
104 let b = raw?;
105 let b = [
106 maximum(0.0, minimum(size[0], b[0])),
107 maximum(0.0, minimum(size[1], b[1])),
108 maximum(0.0, minimum(size[0], b[2])),
109 maximum(0.0, minimum(size[1], b[3])),
110 ];
111 if b[2] <= b[0] || b[3] <= b[1] {
112 None
113 } else {
114 Some(b)
115 }
116}
117
118pub fn rotate(b: Box4, size: Size, angle: i32) -> Box4 {
120 match angle {
121 270 => [size[1] - b[3], b[0], size[1] - b[1], b[2]],
122 90 => [b[1], size[0] - b[2], b[3], size[0] - b[0]],
123 180 => [
124 size[0] - b[2],
125 size[1] - b[3],
126 size[0] - b[0],
127 size[1] - b[1],
128 ],
129 _ => b,
130 }
131}
132
133pub fn rotate_point(p: Size, size: Size, angle: i32) -> Size {
135 match angle {
136 270 => [size[1] - p[1], p[0]],
137 90 => [p[1], size[0] - p[0]],
138 180 => [size[0] - p[0], size[1] - p[1]],
139 _ => p,
140 }
141}
142
143pub fn union(boxes: &[Box4]) -> Option<Box4> {
145 let mut b = *boxes.first()?;
146 for a in &boxes[1..] {
147 b = [
148 minimum(b[0], a[0]),
149 minimum(b[1], a[1]),
150 maximum(b[2], a[2]),
151 maximum(b[3], a[3]),
152 ];
153 }
154 Some(b)
155}
156
157pub fn visual_runs(
159 raw: Vec<Option<Box4>>,
160 overrides: Vec<Option<Box4>>,
161 flags: Vec<u8>,
162 size: Size,
163 angle: i32,
164) -> Vec<(usize, usize, Option<Box4>)> {
165 let boxes: Vec<_> = raw
166 .into_iter()
167 .zip(overrides)
168 .map(|(b, o)| clip(normalize(o, false).or_else(|| normalize(b, false)), size))
169 .collect();
170 let local: Vec<_> = boxes
171 .iter()
172 .map(|b| b.map(|v| rotate(v, size, angle)))
173 .collect();
174 let visible: Vec<_> = (0..flags.len())
175 .filter(|i| flags[*i] & 1 != 0 && local[*i].is_some())
176 .collect();
177 if visible.is_empty() {
178 return Vec::new();
179 }
180 let width = median(
181 visible
182 .iter()
183 .map(|i| 0.1_f64.max(local[*i].unwrap()[2] - local[*i].unwrap()[0]))
184 .collect(),
185 );
186 let page_width = if angle == 90 || angle == 270 {
187 size[1]
188 } else {
189 size[0]
190 };
191 let hard = 15.0_f64.max(3.0 * width).max(0.02 * page_width);
192 let gaps: Vec<_> = visible
193 .windows(2)
194 .map(|w| {
195 let a = local[w[0]].unwrap();
196 let b = local[w[1]].unwrap();
197 (a[0] - b[2]).max(b[0] - a[2]).max(0.0)
198 })
199 .collect();
200 let regular: Vec<_> = gaps.iter().copied().filter(|v| *v < hard).collect();
201 let regular = if regular.len() >= 3 {
202 median(regular)
203 } else {
204 0.0
205 };
206 let soft = 8.0_f64.max(2.2 * width).max(3.0 * regular);
207 let mut boundaries = vec![0];
208 for (w, gap) in visible.windows(2).zip(gaps) {
209 if gap >= hard || ((w[0] + 1..w[1]).any(|i| flags[i] & 2 != 0) && gap >= soft) {
210 boundaries.push(w[1]);
211 }
212 }
213 boundaries.push(flags.len());
214 boundaries
215 .windows(2)
216 .map(|w| {
217 let b: Vec<_> = (w[0]..w[1])
218 .filter(|i| flags[*i] & 1 != 0)
219 .filter_map(|i| boxes[i])
220 .collect();
221 (w[0], w[1], union(&b))
222 })
223 .collect()
224}
225
226pub type SourceRow = (Option<Box4>, Option<Box4>, Option<Box4>, Option<Size>, f64);
227pub type PreparedRow = (Box4, Box4, Size, Box4, Box4, Size);
228
229pub fn source_rows(rows: Vec<SourceRow>, size: Size, angle: i32) -> Vec<Option<PreparedRow>> {
231 rows.into_iter()
232 .map(|(raw, side, tight, origin, rotation)| {
233 let raw = normalize(raw, false);
234 let tight = normalize(tight, false);
235 let source = if rotation.is_finite() && rotation.abs() <= 1e-9 {
236 raw
237 } else {
238 let side = normalize(side, false);
239 match (raw, side) {
240 (Some(r), Some(s)) => {
241 let stable = (r[2] - r[0])
242 .max(tight.map_or(0.0, |b| b[2] - b[0]))
243 .max(0.1);
244 Some(if s[2] - s[0] > 1.6 * stable { r } else { s })
245 }
246 _ => side.or(raw),
247 }
248 };
249 let source = clip(source, size)?;
250 let tight = clip(tight, size)?;
251 let origin = origin.filter(|p| p.iter().all(|v| v.is_finite()))?;
252 Some((
253 source,
254 tight,
255 origin,
256 rotate(source, size, angle),
257 rotate(tight, size, angle),
258 rotate_point(origin, size, angle),
259 ))
260 })
261 .collect()
262}