use std::cmp::Ordering;
use crate::{
DecodeHints, Exceptions, Point, PointCallback,
common::{BitMatrix, Result},
qrcode::detector::{FinderPattern, FinderPatternFinder, FinderPatternInfo},
result_point_utils,
};
const MAX_MODULE_COUNT_PER_EDGE: f32 = 180_f32;
const MIN_MODULE_COUNT_PER_EDGE: f32 = 9_f32;
const DIFF_MODSIZE_CUTOFF_PERCENT: f32 = 0.05_f32;
const DIFF_MODSIZE_CUTOFF: f32 = 0.5_f32;
pub struct MultiFinderPatternFinder<'a>(FinderPatternFinder<'a>);
impl<'a> MultiFinderPatternFinder<'_> {
pub fn new(
image: &'a BitMatrix,
resultPointCallback: Option<PointCallback>,
) -> MultiFinderPatternFinder<'a> {
MultiFinderPatternFinder(FinderPatternFinder::with_callback(
image,
resultPointCallback,
))
}
fn selectMultipleBestPatterns(&self) -> Result<Vec<[FinderPattern; 3]>> {
let mut possibleCenters = Vec::new();
for fp in self.0.getPossibleCenters() {
if fp.getCount() >= 2 {
possibleCenters.push(*fp);
}
}
let size = possibleCenters.len();
if size < 3 {
return Err(Exceptions::not_found_with(
"Couldn't find enough finder patterns",
));
}
if size == 3 {
return Ok(vec![[
possibleCenters[0],
possibleCenters[1],
possibleCenters[2],
]]);
}
possibleCenters.sort_by(compare_finder_patterns);
let mut results = Vec::new();
for i1 in 0..(size - 2) {
let Some(p1) = possibleCenters.get(i1) else {
continue;
};
for i2 in (i1 + 1)..(size - 1) {
let Some(p2) = possibleCenters.get(i2) else {
continue;
};
let vModSize12 = (p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize())
/ p1.getEstimatedModuleSize().min(p2.getEstimatedModuleSize());
let vModSize12A = (p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize()).abs();
if vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT {
break;
}
for i3 in (i2 + 1)..size {
let Some(p3) = possibleCenters.get(i3) else {
continue;
};
let vModSize23 = (p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize())
/ p2.getEstimatedModuleSize().min(p3.getEstimatedModuleSize());
let vModSize23A =
(p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize()).abs();
if vModSize23A > DIFF_MODSIZE_CUTOFF
&& vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT
{
break;
}
let mut test = [*p1, *p2, *p3];
result_point_utils::orderBestPatterns(&mut test);
let info = FinderPatternInfo::new(test);
let dA = Point::distance(info.getTopLeft().into(), info.getBottomLeft().into());
let dC =
Point::distance(info.getTopRight().into(), info.getBottomLeft().into());
let dB = Point::distance(info.getTopLeft().into(), info.getTopRight().into());
let estimatedModuleCount = (dA + dB) / (p1.getEstimatedModuleSize() * 2.0);
if !(MIN_MODULE_COUNT_PER_EDGE..=MAX_MODULE_COUNT_PER_EDGE)
.contains(&estimatedModuleCount)
{
continue;
}
let vABBC = ((dA - dB) / dA.min(dB)).abs();
if vABBC >= 0.1 {
continue;
}
let dCpy =
((dA as f64) * (dA as f64) + (dB as f64) * (dB as f64)).sqrt() as f32;
let vPyC = ((dC - dCpy) / dC.min(dCpy)).abs();
if vPyC >= 0.1 {
continue;
}
results.push(test);
}
}
}
if !results.is_empty() {
Ok(results)
} else {
Err(Exceptions::NOT_FOUND)
}
}
pub fn findMulti(&mut self, hints: &DecodeHints) -> Result<Vec<FinderPatternInfo>> {
let tryHarder = hints.TryHarder.unwrap_or(false);
let image = self.0.getImage().clone();
let maxI = image.getHeight();
let maxJ = image.getWidth();
let mut iSkip = (3 * maxI) / (4 * FinderPatternFinder::MAX_MODULES);
if iSkip < FinderPatternFinder::MIN_SKIP || tryHarder {
iSkip = FinderPatternFinder::MIN_SKIP;
}
let mut stateCount = [0_u32; 5]; let mut i = iSkip - 1;
while i < maxI {
FinderPatternFinder::doClearCounts(&mut stateCount);
let mut currentState = 0;
for j in 0..maxJ {
if image.get(j, i) {
if (currentState & 1) == 1 {
currentState += 1;
}
stateCount[currentState] += 1;
} else {
if (currentState & 1) == 0 {
if currentState == 4 {
if FinderPatternFinder::foundPatternCross(&stateCount)
&& self.0.handlePossibleCenter(&stateCount, i, j)
{
currentState = 0;
FinderPatternFinder::doClearCounts(&mut stateCount);
} else {
FinderPatternFinder::doShiftCounts2(&mut stateCount);
currentState = 3;
}
} else {
currentState += 1;
stateCount[currentState] += 1;
}
} else {
stateCount[currentState] += 1;
}
}
}
if FinderPatternFinder::foundPatternCross(&stateCount) {
self.0.handlePossibleCenter(&stateCount, i, maxJ);
}
i += iSkip;
} let mut patternInfo = self.selectMultipleBestPatterns()?;
let mut result = Vec::new(); for pattern in patternInfo.iter_mut() {
result_point_utils::orderBestPatterns(pattern);
result.push(FinderPatternInfo::new(*pattern));
}
Ok(result)
}
}
fn compare_finder_patterns(center1: &FinderPattern, center2: &FinderPattern) -> Ordering {
let value = center2.getEstimatedModuleSize() - center1.getEstimatedModuleSize();
if value < 0.0 {
Ordering::Less
} else if value > 0.0 {
Ordering::Greater
} else {
Ordering::Equal
}
}