use crate::geometry::{rotate, Box4, Size};
use std::collections::{HashMap, HashSet};
use std::hash::{BuildHasherDefault, Hasher};
use std::sync::Arc;
#[derive(Default)]
pub struct IdHasher(u64);
impl Hasher for IdHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for b in bytes {
self.0 = (self.0 ^ u64::from(*b)).wrapping_mul(0x100000001b3);
}
}
fn write_u64(&mut self, value: u64) {
let mut v = value.wrapping_add(0x9e3779b97f4a7c15);
v = (v ^ (v >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
v = (v ^ (v >> 27)).wrapping_mul(0x94d049bb133111eb);
self.0 = v ^ (v >> 31);
}
fn write_i64(&mut self, value: i64) {
self.write_u64(value as u64);
}
}
pub type Ids = HashSet<i64, BuildHasherDefault<IdHasher>>;
pub type Row = (f64, Vec<(i64, Box4)>);
pub fn valid(b: &Box4) -> bool {
b.iter().all(|v| v.is_finite() && v.abs() <= 1e150)
}
fn union(mut a: Box4, b: Box4) -> Box4 {
for i in 0..4 {
if (i < 2 && b[i] < a[i]) || (i >= 2 && b[i] > a[i]) {
a[i] = b[i];
}
}
a
}
fn area(b: Box4) -> f64 {
(b[2] - b[0]).max(0.0) * (b[3] - b[1]).max(0.0)
}
fn overlap(a: Box4, b: Box4) -> f64 {
let intersection =
(a[2].min(b[2]) - a[0].max(b[0])).max(0.0) * (a[3].min(b[3]) - a[1].max(b[1])).max(0.0);
let denominator = ((a[2] - a[0]) * (a[3] - a[1])).min((b[2] - b[0]) * (b[3] - b[1]));
if denominator > 0.0 {
intersection / denominator
} else {
0.0
}
}
fn inverse(b: Box4, size: Size, angle: i32) -> Box4 {
match angle {
270 => [b[1], size[1] - b[2], b[3], size[1] - b[0]],
90 => [size[0] - b[3], b[0], size[0] - b[1], b[2]],
180 => [
size[0] - b[2],
size[1] - b[3],
size[0] - b[0],
size[1] - b[1],
],
_ => b,
}
}
#[derive(Clone)]
pub struct Members {
base: Arc<Ids>,
added: Ids,
removed: Ids,
}
impl Members {
pub fn plain(added: Ids) -> Self {
Self {
base: Arc::new(Ids::default()),
added,
removed: Ids::default(),
}
}
pub fn new(base: Arc<Ids>, excluded: Ids) -> Self {
let removed = excluded.into_iter().filter(|v| base.contains(v)).collect();
Self {
base,
added: Ids::default(),
removed,
}
}
fn contains(&self, value: &i64) -> bool {
self.added.contains(value) || (self.base.contains(value) && !self.removed.contains(value))
}
pub fn values(&self) -> impl Iterator<Item = i64> + '_ {
self.base
.iter()
.filter(|v| !self.removed.contains(v))
.chain(&self.added)
.copied()
}
fn expand(&mut self, base: Arc<Ids>, excluded: &Ids) {
let added = self.values().filter(|v| !base.contains(v)).collect();
self.base = base;
self.added = added;
self.removed = Ids::default();
for id in excluded {
self.added.remove(id);
if self.base.contains(id) {
self.removed.insert(*id);
}
}
}
fn merge(&mut self, other: &Self) {
if !Arc::ptr_eq(&self.base, &other.base) && self.base.is_empty() && !other.base.is_empty() {
let visible: Ids = self.values().collect();
self.removed = other.base.difference(&visible).copied().collect();
self.added = visible.difference(&other.base).copied().collect();
self.base = other.base.clone();
}
if Arc::ptr_eq(&self.base, &other.base) {
self.removed.retain(|v| other.removed.contains(v));
for id in &other.added {
if self.base.contains(id) {
self.removed.remove(id);
} else {
self.added.insert(*id);
}
}
} else {
for id in other.values() {
if self.base.contains(&id) {
self.removed.remove(&id);
} else {
self.added.insert(id);
}
}
}
}
}
pub struct Annotation {
pub kind: String,
pub bbox: Box4,
pub members: Ids,
pub rows: Vec<(i64, Box4)>,
}
pub struct Candidate {
pub bbox: Box4,
pub local: Box4,
pub core: Option<Box4>,
pub angle: i32,
pub score: f64,
pub members: Members,
pub annotations: Vec<Annotation>,
}
pub struct GridContext {
grids: Vec<Box4>,
rows: Vec<Row>,
size: Size,
angle: i32,
tolerance: f64,
cache: HashMap<[u64; 4], Arc<Ids>>,
}
impl GridContext {
pub fn new(
grids: Vec<Box4>,
rows: Vec<Row>,
size: Size,
angle: i32,
height: f64,
) -> Option<Self> {
if grids.iter().any(|b| !valid(b))
|| rows
.iter()
.any(|r| !r.0.is_finite() || r.1.iter().any(|(_, b)| !valid(b)))
|| size.iter().any(|v| !v.is_finite() || v.abs() > 1e100)
|| !height.is_finite()
{
return None;
}
Some(Self {
grids,
rows,
size,
angle,
tolerance: 2.0_f64.max(height),
cache: HashMap::new(),
})
}
pub fn expand(&mut self, candidate: &mut Candidate) {
let Some(core) = candidate.core else {
return;
};
let core = rotate(core, self.size, self.angle);
let mut selected = None;
for grid in &self.grids {
let shorter = (core[2] - core[0]).min(grid[2] - grid[0]);
let share = if shorter > 0.0 {
(core[2].min(grid[2]) - core[0].max(grid[0])).max(0.0) / shorter
} else {
0.0
};
if share < 0.9
|| core[3] < grid[1] - self.tolerance
|| core[1] > grid[3] + self.tolerance
{
continue;
}
let score = (core[3].min(grid[3]) - core[1].max(grid[1]), area(*grid));
if selected
.as_ref()
.is_none_or(|(previous, _)| score > *previous)
{
selected = Some((score, *grid));
}
}
let Some((_, grid)) = selected else {
return;
};
let expanded = union(core, grid);
candidate.local = union(candidate.local, grid);
candidate.core = Some(inverse(expanded, self.size, self.angle));
candidate.bbox = inverse(candidate.local, self.size, self.angle);
let key = expanded.map(|v| if v == 0.0 { 0 } else { v.to_bits() });
let members = self.cache.get(&key).cloned().unwrap_or_else(|| {
let mut ids = Ids::default();
for (center, fragments) in &self.rows {
if *center < expanded[1] || *center > expanded[3] {
continue;
}
for (id, b) in fragments {
let x = (b[0] + b[2]) / 2.0;
let y = (b[1] + b[3]) / 2.0;
if expanded[0] <= x && x <= expanded[2] && expanded[1] <= y && y <= expanded[3]
{
ids.insert(*id);
}
}
}
let members = Arc::new(ids);
if self.cache.len() < 128 {
self.cache.insert(key, members.clone());
}
members
});
let excluded = candidate
.annotations
.iter()
.flat_map(|a| a.members.iter().copied())
.collect();
candidate.members.expand(members, &excluded);
}
}
fn merge_annotations(target: &mut Candidate, annotations: Vec<Annotation>) {
for annotation in annotations {
if let Some(existing) = target
.annotations
.iter_mut()
.find(|a| a.kind == annotation.kind)
{
existing.bbox = union(existing.bbox, annotation.bbox);
existing.members.extend(annotation.members);
for (id, bbox) in annotation.rows {
if let Some((_, previous)) = existing.rows.iter_mut().find(|(v, _)| *v == id) {
*previous = union(*previous, bbox);
} else {
existing.rows.push((id, bbox));
}
}
} else {
target.annotations.push(annotation);
}
}
for annotation in &mut target.annotations {
annotation.members.retain(|id| !target.members.contains(id));
annotation
.rows
.retain(|(id, _)| annotation.members.contains(id));
if let Some((_, first)) = annotation.rows.first() {
annotation.bbox = annotation
.rows
.iter()
.skip(1)
.fold(*first, |b, (_, next)| union(b, *next));
}
}
target.annotations.retain(|a| !a.members.is_empty());
}
#[derive(Default)]
pub struct Merger {
pub candidates: Vec<Candidate>,
}
impl Merger {
pub fn push(&mut self, candidate: Candidate) {
let Some(target) = self
.candidates
.iter_mut()
.find(|c| c.angle == candidate.angle && overlap(candidate.bbox, c.bbox) >= 0.2)
else {
self.candidates.push(candidate);
return;
};
target.bbox = union(target.bbox, candidate.bbox);
target.local = union(target.local, candidate.local);
target.core = match (target.core, candidate.core) {
(Some(a), Some(b)) => Some(union(a, b)),
(None, b) => b,
(a, None) => a,
};
target.members.merge(&candidate.members);
if candidate.score > target.score {
target.score = candidate.score;
}
merge_annotations(target, candidate.annotations);
}
pub fn finish(mut self) -> Vec<Candidate> {
self.candidates.sort_by(|a, b| {
(a.bbox[1], a.bbox[0])
.partial_cmp(&(b.bbox[1], b.bbox[0]))
.unwrap()
});
self.candidates
}
}