use std::mem;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use super::Code;
use crate::bit_vec::BitVec;
use crate::linear_algebra::{Line, Point, Rect};
use crate::pixels::{Pixel, Pixels, Spanner};
use crate::version_db::{MAX_VERSION, VERSION_DB};
#[derive(Debug, Clone, Default)]
pub struct Region {
pub seed: Point,
count: usize,
capstone: bool,
}
pub type RcRegion = Rc<RefCell<Region>>;
#[derive(Debug, Clone, Default)]
pub struct Capstone {
pub ring: Rc<RefCell<Region>>,
pub stone: Rc<RefCell<Region>>,
pub corners: Rect,
pub center: Point,
pub c: PerspectiveParams,
pub qr_grid: bool,
}
pub type RcCapstone = Rc<RefCell<Capstone>>;
impl Capstone {
fn rotate(&mut self, h0: &Point, hd: &Point) {
let (best, _) = self
.corners
.0
.iter()
.enumerate()
.map(|(i, p)| (i, (p.x - h0.x) * -hd.y + (p.y - h0.y) * hd.x))
.min_by_key(|(_i, score)| *score)
.unwrap();
self.corners.0.rotate_left(best % 4);
self.c = PerspectiveParams::setup(&self.corners, 7.0, 7.0);
}
}
#[derive(Debug, Default, Clone)]
pub struct Grid {
pub caps: [RcCapstone; 3],
pub align_region: Option<Rc<RefCell<Region>>>,
pub align: Point,
pub grid_size: usize,
pub c: PerspectiveParams,
}
#[derive(Debug, Copy, Clone, Default)]
pub struct PerspectiveParams([f64; 8]);
impl PerspectiveParams {
pub fn setup(rect: &Rect, w: f64, h: f64) -> PerspectiveParams {
let x0 = rect.0[0].x as f64;
let y0 = rect.0[0].y as f64;
let x1 = rect.0[1].x as f64;
let y1 = rect.0[1].y as f64;
let x2 = rect.0[2].x as f64;
let y2 = rect.0[2].y as f64;
let x3 = rect.0[3].x as f64;
let y3 = rect.0[3].y as f64;
let wden = w * (x2 * y3 - x3 * y2 + (x3 - x2) * y1 + x1 * (y2 - y3));
let hden = h * (x2 * y3 + x1 * (y2 - y3) - x3 * y2 + (x3 - x2) * y1);
PerspectiveParams([
(x1 * (x2 * y3 - x3 * y2)
+ x0 * (-x2 * y3 + x3 * y2 + (x2 - x3) * y1)
+ x1 * (x3 - x2) * y0)
/ wden,
-(x0 * (x2 * y3 + x1 * (y2 - y3) - x2 * y1) - x1 * x3 * y2
+ x2 * x3 * y1
+ (x1 * x3 - x2 * x3) * y0)
/ hden,
x0,
(y0 * (x1 * (y3 - y2) - x2 * y3 + x3 * y2)
+ y1 * (x2 * y3 - x3 * y2)
+ x0 * y1 * (y2 - y3))
/ wden,
(x0 * (y1 * y3 - y2 * y3) + x1 * y2 * y3 - x2 * y1 * y3
+ y0 * (x3 * y2 - x1 * y2 + (x2 - x3) * y1))
/ hden,
y0,
(x1 * (y3 - y2) + x0 * (y2 - y3) + (x2 - x3) * y1 + (x3 - x2) * y0) / wden,
(-x2 * y3 + x1 * y3 + x3 * y2 + x0 * (y1 - y2) - x3 * y1 + (x2 - x1) * y0) / hden,
])
}
pub fn map(&self, u: f64, v: f64) -> Point {
let c = &self.0;
let den = c[6] * u + c[7] * v + 1.0;
let x = (c[0] * u + c[1] * v + c[2]) / den;
let y = (c[3] * u + c[4] * v + c[5]) / den;
let x = (x + 0.5).floor() as isize;
let y = (y + 0.5).floor() as isize;
Point { x, y }
}
pub fn unmap(&self, i: &Point) -> (f64, f64) {
let c = &self.0;
let x = i.x as f64;
let y = i.y as f64;
let den =
-c[0] * c[7] * y + c[1] * c[6] * y + (c[3] * c[7] - c[4] * c[6]) * x + c[0] * c[4]
- c[1] * c[3];
(
-(c[1] * (y - c[5]) - c[2] * c[7] * y + (c[5] * c[7] - c[4]) * x + c[2] * c[4]) / den,
(c[0] * (y - c[5]) - c[2] * c[6] * y + (c[5] * c[6] - c[3]) * x + c[2] * c[3]) / den,
)
}
}
fn region_code(q: &Scanner, x: usize, y: usize) -> Result<Option<RcRegion>, ()> {
let mut img = q.image.borrow_mut();
let pixel = match img.get_pixel(x, y) {
Some(Pixel::Region(reg)) => return Ok(Some(reg.clone())),
Some(Pixel::White) | None => return Ok(None),
Some(val) => val.clone(),
};
let region = {
let region_count = q.region_count.get();
if region_count >= q.max_regions {
return Err(());
}
q.region_count.set(region_count + 1);
Rc::new(RefCell::new(Region {
seed: Point::new(x as isize, y as isize),
..Region::default()
}))
};
let count = img.flood_fill_seed(x, y, pixel, Pixel::Region(region.clone()), AreaCount::new());
region.borrow_mut().count = count;
Ok(Some(region))
}
fn find_region_corners(q: &Scanner, region_rc: RcRegion, ref_: &Point) -> Rect {
let region = region_rc.borrow();
let ref_point = {
let corner = q.image.borrow_mut().flood_fill_seed(
region.seed.x as usize,
region.seed.y as usize,
Pixel::Region(region_rc.clone()),
Pixel::Black,
FindOneCorner::new(*ref_),
);
Point {
x: corner.x - ref_.x,
y: corner.y - ref_.y,
}
};
let i = region.seed.x * ref_point.x + region.seed.y * ref_point.y;
let j = region.seed.x * -ref_point.y + region.seed.y * ref_point.x;
let mut img = q.image.borrow_mut();
let spanner = FindOtherCorners {
ref_: ref_point,
scores: [i, j, -i, -j],
corners: Rect([region.seed; 4]),
};
img.flood_fill_seed(
region.seed.x as usize,
region.seed.y as usize,
Pixel::Black,
Pixel::Region(region_rc.clone()),
spanner,
)
}
fn test_capstone(
q: &Scanner,
x: usize,
y: usize,
pb: &[usize; 5],
) -> Result<Option<(RcRegion, RcRegion)>, ()> {
let ring_right = match region_code(q, x - pb[4], y)? {
Some(r) => r,
None => return Ok(None),
};
let stone = match region_code(q, (x - pb[4] - pb[3] - pb[2]) as usize, y as usize)? {
Some(r) => r,
None => return Ok(None),
};
let ring_left = match region_code(
q,
(x - pb[4] - pb[3] - pb[2] - pb[1] - pb[0]) as usize,
y as usize,
)? {
Some(r) => r,
None => return Ok(None),
};
if !Rc::ptr_eq(&ring_left, &ring_right) {
return Ok(None);
}
if Rc::ptr_eq(&ring_left, &stone) {
return Ok(None);
}
{
let stone = stone.borrow();
let ring_left = ring_left.borrow();
if stone.capstone || ring_left.capstone {
return Ok(None);
}
let ratio = stone.count * 100 / ring_left.count;
if ratio < 10 || ratio > 70 {
return Ok(None);
}
}
Ok(Some((ring_left, stone)))
}
fn finder_scan(q: &Scanner, y: usize) -> Result<(), ()> {
const CHECK_PATTERN_LEN: usize = 5;
const CHECK_PATTERN: [usize; CHECK_PATTERN_LEN] = [1, 1, 3, 1, 1];
let mut last_color = 0;
let mut run_length = 0;
let mut run_count = 0;
let mut pb = [0usize; CHECK_PATTERN_LEN];
let width = q.image.borrow().width();
for x in 0..width {
let color = if Pixel::White == *q.image.borrow().get_pixel(x, y).unwrap() {
0
} else {
1
};
if x > 0 && color != last_color {
pb = [pb[1], pb[2], pb[3], pb[4], run_length];
run_length = 0;
run_count += 1;
if color == 0 && run_count >= CHECK_PATTERN_LEN {
let avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4;
let err = avg * 3 / 4;
let ok = CHECK_PATTERN
.iter()
.zip(pb.iter())
.all(|(check, pb)| (*check * avg - err < *pb) && (*pb < *check * avg + err));
if ok {
if let Some((ring, stone)) = test_capstone(q, x, y, &pb)? {
q.add_capstone(ring, stone);
if q.capstones.borrow().len() >= q.max_capstones {
return Err(());
}
}
}
}
}
run_length += 1;
last_color = color;
}
Ok(())
}
fn find_alignment_pattern(q: &Scanner, qr: &mut Grid) -> Result<(), ()> {
let mut b = qr.align;
let a = {
let c0 = &qr.caps[0].borrow();
let (u, v) = c0.c.unmap(&b);
c0.c.map(u, v + 1.0)
};
let c = {
let c2 = &qr.caps[2].borrow();
let (u, v) = c2.c.unmap(&b);
c2.c.map(u, v + 1.0)
};
let size_estimate = ((a.x - b.x) * -(c.y - b.y) + (a.y - b.y) * (c.x - b.x)).abs() as usize;
let mut step_size = 1;
let mut dir = 0;
while step_size * step_size < size_estimate * 100 {
const DX_MAP: [isize; 4] = [1, 0, -1, 0];
const DY_MAP: [isize; 4] = [0, -1, 0, 1];
for _ in 0..step_size {
if let Some(reg) = region_code(q, b.x as usize, b.y as usize)? {
let reg_count = reg.borrow().count;
if (reg_count >= size_estimate / 2) && (reg_count <= size_estimate * 2) {
qr.align_region = Some(reg.clone());
return Ok(());
}
};
b.x += DX_MAP[dir];
b.y += DY_MAP[dir];
}
dir = (dir + 1) % 4;
if dir & 1 == 0 {
step_size += 1;
}
}
Ok(())
}
fn timing_scan(q: &Scanner, p0: &Point, p1: &Point) -> Option<usize> {
let (w, h) = q.image.borrow().dimensions();
if p0.x < 0 || p0.y < 0 || p0.x >= w as isize || p0.y >= h as isize {
return None;
}
if p1.x < 0 || p1.y < 0 || p1.x >= w as isize || p1.y >= h as isize {
return None;
}
let (mut x, mut y) = (p0.x, p0.y);
let mut n = p1.x - p0.x;
let mut d = p1.y - p0.y;
let (dom, nondom) = if n.abs() > d.abs() {
mem::swap(&mut n, &mut d);
(0, 1)
} else {
(1, 0)
};
let nondom_step = if n < 0 {
n = -n;
-1
} else {
1
};
let dom_step = if d < 0 {
d = -d;
-1
} else {
1
};
let mut count = 0;
let mut run_length = 0;
let mut a = 0;
for _ in 0..=d {
if y < 0 || y >= h as isize || x < 0 || x >= w as isize {
break;
}
let pixel = q
.image
.borrow()
.get_pixel(x as usize, y as usize)
.unwrap()
.clone();
if pixel != Pixel::White {
if run_length >= 2 {
count += 1;
}
run_length = 0;
} else {
run_length += 1;
}
a += n;
{
let dom_r = if dom == 0 { &mut x } else { &mut y };
*dom_r += dom_step;
}
if a >= d {
let nondom_r = if nondom == 0 { &mut x } else { &mut y };
*nondom_r += nondom_step;
a -= d;
}
}
Some(count)
}
fn measure_timing_pattern(q: &Scanner, qr: &mut Grid) -> Result<(), ()> {
use std::cmp;
const US: [f64; 3] = [6.5, 6.5, 0.5];
const VS: [f64; 3] = [0.5, 6.5, 6.5];
let mut tpep: [Point; 3] = [Point::default(); 3];
for i in 0..3 {
tpep[i] = qr.caps[i].borrow().c.map(US[i], VS[i])
}
let h_scan = timing_scan(q, &tpep[1], &tpep[2]).ok_or(())?;
let v_scan = timing_scan(q, &tpep[1], &tpep[0]).ok_or(())?;
let scan = cmp::max(h_scan, v_scan);
let size = scan * 2 + 13;
let ver = (size - 15) / 4;
qr.grid_size = ver * 4 + 17;
Ok(())
}
fn fitness_cell(q: &Scanner, qr: &Grid, x: isize, y: isize) -> isize {
const OFFSETS: [f64; 3] = [0.3, 0.5, 0.7];
let mut score = 0;
let img = q.image.borrow();
for v in OFFSETS.iter() {
for u in OFFSETS.iter() {
let p = qr.c.map(x as f64 + u, y as f64 + v);
if p.y < 0 || p.x < 0 {
continue;
}
if let Some(pixel) = img.get_pixel(p.x as usize, p.y as usize) {
if pixel.is_white() {
score -= 1;
} else {
score += 1;
}
}
}
}
score
}
fn fitness_ring(q: &Scanner, qr: &Grid, cx: isize, cy: isize, radius: isize) -> isize {
let mut score = 0;
for i in 0..(radius * 2) {
score += fitness_cell(q, qr, cx - radius + i, cy - radius);
score += fitness_cell(q, qr, cx - radius, cy + radius - i);
score += fitness_cell(q, qr, cx + radius, cy - radius + i);
score += fitness_cell(q, qr, cx + radius - i, cy + radius);
}
score
}
fn fitness_apat(q: &Scanner, qr: &Grid, cx: isize, cy: isize) -> isize {
fitness_cell(q, qr, cx, cy) - fitness_ring(q, qr, cx, cy, 1) + fitness_ring(q, qr, cx, cy, 2)
}
fn fitness_capstone(q: &Scanner, qr: &Grid, x: isize, y: isize) -> isize {
let x = x + 3;
let y = y + 3;
fitness_cell(q, qr, x, y) + fitness_ring(q, qr, x, y, 1) - fitness_ring(q, qr, x, y, 2)
+ fitness_ring(q, qr, x, y, 3)
}
fn fitness_all(q: &Scanner, qr: &Grid) -> isize {
let mut score = 0;
let grid_size = qr.grid_size as isize;
for i in 0..(grid_size - 14) {
let expect = if (i & 1) != 0 { 1 } else { -1 };
score += fitness_cell(q, qr, i + 7, 6) * expect;
score += fitness_cell(q, qr, 6, i + 7) * expect;
}
score += fitness_capstone(q, qr, 0, 0);
score += fitness_capstone(q, qr, grid_size - 7, 0);
score += fitness_capstone(q, qr, 0, grid_size - 7);
let version = (grid_size - 17) / 4;
if version < 0 || version > MAX_VERSION as isize {
return score;
}
let info = VERSION_DB[version as usize];
let ap_count = info.apat.len();
if ap_count > 1 {
for i in 1..(ap_count - 2) {
score += fitness_apat(q, qr, 6, info.apat[i] as isize);
score += fitness_apat(q, qr, info.apat[i] as isize, 6);
}
}
if ap_count > 0 {
for i in 1..(ap_count - 1) {
for j in 1..(ap_count - 1) {
score += fitness_apat(q, qr, info.apat[i] as isize, info.apat[j] as isize);
}
}
}
score
}
fn jiggle_perspective(q: &Scanner, qr: &mut Grid) {
const MIN_STEPS: usize = 2;
const MAX_STEPS: usize = 5;
let mut best = fitness_all(q, qr);
let mut adjustments: [f64; 8] = qr.c.0;
for adj in adjustments.iter_mut() {
*adj *= 0.02;
}
for c in 0..MAX_STEPS {
let old_best = best;
for (j, adj) in adjustments.iter().cloned().enumerate() {
for step in [-adj, adj].iter() {
let old = qr.c.0[j];
let new = old + step;
qr.c.0[j] = new;
let test = fitness_all(q, &qr);
if test > best {
best = test;
} else {
qr.c.0[j] = old
}
}
}
if c >= (MIN_STEPS - 1) && old_best != 0 && ((best - old_best) * 1000 / old_best) < 1 {
break;
}
for i in adjustments.iter_mut() {
*i *= 0.5;
}
}
}
fn setup_qr_perspective(q: &Scanner, qr: &mut Grid) {
let rect = Rect([
qr.caps[1].borrow().corners.0[0],
qr.caps[2].borrow().corners.0[0],
qr.align,
qr.caps[0].borrow().corners.0[0],
]);
let uv = (qr.grid_size - 7) as f64;
qr.c = PerspectiveParams::setup(&rect, uv, uv);
jiggle_perspective(q, qr);
}
fn record_qr_grid(
q: &Scanner,
mut a: RcCapstone,
b: RcCapstone,
mut c: RcCapstone,
) -> Result<(), ()> {
let (h0, mut hd) = {
let a = a.borrow();
let c = c.borrow();
(
a.center,
Point {
x: c.center.x - a.center.x,
y: c.center.y - a.center.y,
},
)
};
{
let b = b.borrow();
if (b.center.x - h0.x) * -hd.y + (b.center.y - h0.y) * hd.x > 0 {
mem::swap(&mut a, &mut c);
hd.x = -hd.x;
hd.y = -hd.y;
}
}
let mut qr = Grid {
caps: [a.clone(), b.clone(), c.clone()],
..Grid::default()
};
for cap in qr.caps.iter() {
cap.borrow_mut().rotate(&h0, &hd);
}
if measure_timing_pattern(q, &mut qr).is_err() {
return Ok(());
}
let align = {
let (a, c) = (a.borrow(), c.borrow());
Line {
a: a.corners.0[0],
b: a.corners.0[1],
}
.intersect(&Line {
a: c.corners.0[0],
b: c.corners.0[3],
})
};
match align {
Some(align) => qr.align = align,
None => {
return Ok(());
}
};
if qr.grid_size > 21 {
find_alignment_pattern(q, &mut qr)?;
let mut img = q.image.borrow_mut();
if let Some(ref reg_r) = &qr.align_region {
let (x, y) = reg_r.borrow().seed.split();
img.flood_fill_seed(
x as usize,
y as usize,
Pixel::Region(reg_r.clone()),
Pixel::Black,
NoSpan::new(),
);
{
let score = -hd.y * qr.align.x + hd.x * qr.align.y;
let align = img
.flood_fill_seed(
x as usize,
y as usize,
Pixel::Black,
Pixel::Region(reg_r.clone()),
FindLeftmostToLine::new(hd, score),
)
.unwrap_or_default();
qr.align = align;
}
}
}
setup_qr_perspective(q, &mut qr);
for cap in qr.caps.iter() {
cap.borrow_mut().qr_grid = true;
}
q.grids.borrow_mut().push(qr);
Ok(())
}
struct Neighbour {
capstone: RcCapstone,
distance: f64,
}
fn test_neighbours(
q: &Scanner,
b: RcCapstone,
hlist: &[Neighbour],
vlist: &[Neighbour],
) -> Result<(), ()> {
if q.grids.borrow().len() >= q.max_grids {
return Err(());
}
let mut best_score = 0.0;
let mut best_vh = None;
for hn in hlist.iter() {
for vn in vlist.iter() {
let score = (1.0 - hn.distance / vn.distance).abs();
if score > 2.5 {
continue;
}
if best_vh.is_none() || score < best_score {
best_vh = Some((vn.capstone.clone(), hn.capstone.clone()));
best_score = score;
}
}
}
if let Some((best_v, best_h)) = best_vh {
record_qr_grid(q, best_h, b, best_v)?;
}
Ok(())
}
fn test_grouping(q: &Scanner, c1_rc: RcCapstone) -> Result<(), ()> {
let c1 = c1_rc.borrow();
let capstones = q.capstones.borrow();
let mut hlist = Vec::new();
let mut vlist = Vec::new();
for c2_rc in capstones.iter() {
let c2 = c2_rc.borrow();
if Rc::ptr_eq(&c1_rc, &c2_rc) || c2.qr_grid {
continue;
}
let (mut u, mut v) = c1.c.unmap(&c2.center);
u = (u - 3.5).abs();
v = (v - 3.5).abs();
if u < 0.2 * v {
hlist.push(Neighbour {
capstone: c2_rc.clone(),
distance: v,
});
}
if v < 0.2 * u {
vlist.push(Neighbour {
capstone: c2_rc.clone(),
distance: u,
})
}
}
drop(c1);
drop(capstones);
if !vlist.is_empty() && !hlist.is_empty() {
test_neighbours(q, c1_rc.clone(), hlist.as_mut(), vlist.as_mut())?;
}
Ok(())
}
pub struct Scanner {
pub max_regions: usize,
pub max_capstones: usize,
pub max_grids: usize,
region_count: Cell<usize>,
capstones: RefCell<Vec<RcCapstone>>,
grids: RefCell<Vec<Grid>>,
image: RefCell<Pixels>,
}
impl Scanner {
pub fn new(img: &[u8], w: usize, h: usize) -> Scanner {
Scanner::from_iterator(img.iter().cloned(), w, h)
}
pub fn from_iterator<I: Iterator<Item = u8>>(iter: I, w: usize, h: usize) -> Scanner {
let iter = iter.map(Pixel::Origin);
let img = Pixels::from_iter(iter, w, h).unwrap();
Scanner::from_image(img)
}
pub fn scan(self) -> ScanResults {
self.image.borrow_mut().threshold();
let h = self.image.borrow().height();
for i in 0..h {
if finder_scan(&self, i).is_err() {
break;
}
}
let capstones = self.capstones.borrow();
for c in capstones.iter().cloned() {
if test_grouping(&self, c).is_err() {
break;
}
}
ScanResults {
image: self.image.into_inner(),
grids: self.grids.into_inner(),
}
}
fn from_image(img: Pixels) -> Scanner {
const DEFAULT_MAX_GRIDS: usize = 8;
const DEFAULT_MAX_CAPSTONES: usize = 3 * DEFAULT_MAX_GRIDS;
const DEFAULT_MAX_REGIONS: usize = 4096;
Scanner {
image: RefCell::new(img),
region_count: Cell::new(0),
capstones: RefCell::new(Vec::new()),
grids: RefCell::new(Vec::new()),
max_capstones: DEFAULT_MAX_CAPSTONES,
max_grids: DEFAULT_MAX_GRIDS,
max_regions: DEFAULT_MAX_REGIONS,
}
}
fn add_capstone(&self, ring: RcRegion, stone: RcRegion) -> RcCapstone {
let corners = find_region_corners(self, ring.clone(), &stone.borrow().seed);
let c = PerspectiveParams::setup(&corners, 7.0, 7.0);
let center = c.map(3.5, 3.5);
let capstone = Rc::new(RefCell::new(Capstone {
ring: ring.clone(),
stone: stone.clone(),
qr_grid: false,
corners,
center,
c,
}));
stone.borrow_mut().capstone = true;
ring.borrow_mut().capstone = true;
self.capstones.borrow_mut().push(capstone.clone());
capstone
}
}
struct NoSpan {}
impl NoSpan {
pub fn new() -> NoSpan {
NoSpan {}
}
}
impl Spanner for NoSpan {
type Result = ();
fn span(&mut self, _y: usize, _left: usize, _right: usize) {}
fn result(self) {}
}
struct FindLeftmostToLine {
ref_: Point,
score: isize,
corner: Option<Point>,
}
impl FindLeftmostToLine {
fn new(ref_: Point, score: isize) -> FindLeftmostToLine {
FindLeftmostToLine {
ref_,
score,
corner: None,
}
}
}
impl Spanner for FindLeftmostToLine {
type Result = Option<Point>;
fn span(&mut self, y: usize, left: usize, right: usize) {
let xs = [left, right];
for x in xs.iter() {
let d = self.ref_.x * (*x as isize) + self.ref_.x * (y as isize);
if d < self.score {
self.score = d;
self.corner = Some(Point::new(*x as isize, y as isize));
}
}
}
fn result(self) -> Self::Result {
self.corner
}
}
struct FindOtherCorners {
pub ref_: Point,
pub scores: [isize; 4],
pub corners: Rect,
}
impl Spanner for FindOtherCorners {
type Result = Rect;
fn span(&mut self, y: usize, left: usize, right: usize) {
for x in [left, right].iter() {
let up = (*x as isize) * self.ref_.x + (y as isize) * self.ref_.y;
let right = (*x as isize) * -self.ref_.y + (y as isize) * self.ref_.x;
let scores = [up, right, -up, -right];
for (j, score) in scores.iter().enumerate() {
if *score > self.scores[j] {
self.scores[j] = *score;
self.corners.0[j] = Point {
x: *x as isize,
y: y as isize,
}
}
}
}
}
fn result(self) -> Self::Result {
self.corners
}
}
struct FindOneCorner {
pub ref_: Point,
pub score: isize,
pub corner: Point,
}
impl FindOneCorner {
fn new(ref_: Point) -> FindOneCorner {
FindOneCorner {
ref_,
corner: Point::default(),
score: -1,
}
}
}
impl Spanner for FindOneCorner {
type Result = Point;
fn span(&mut self, y: usize, left: usize, right: usize) {
let dy = y as isize - self.ref_.y;
for x in [left, right].iter() {
let dx = *x as isize - self.ref_.x;
let d = dx * dx + dy * dy;
if d > self.score {
self.score = d;
self.corner = Point {
x: *x as isize,
y: y as isize,
}
}
}
}
fn result(self) -> Point {
self.corner
}
}
struct AreaCount {
count: usize,
}
impl AreaCount {
fn new() -> AreaCount {
AreaCount { count: 0 }
}
}
impl Spanner for AreaCount {
type Result = usize;
fn span(&mut self, _x: usize, left: usize, right: usize) {
self.count += right + 1 - left;
}
fn result(self) -> usize {
self.count
}
}
pub struct ScanResults {
image: Pixels,
grids: Vec<Grid>,
}
impl ScanResults {
pub fn extract(&self, index: usize) -> Option<Code> {
let qr = self.grids.get(index)?;
let corners = [
qr.c.map(0.0, 0.0),
qr.c.map(qr.grid_size as f64, 0.0),
qr.c.map(qr.grid_size as f64, qr.grid_size as f64),
qr.c.map(0.0, qr.grid_size as f64),
];
let mut cell_bitmap = BitVec::with_capacity(qr.grid_size * qr.grid_size);
for y in 0..qr.grid_size {
for x in 0..qr.grid_size {
let v = self.read_cell(qr, x as isize, y as isize);
cell_bitmap.push(v);
}
}
Some(Code {
corners,
cell_bitmap,
size: qr.grid_size,
})
}
pub fn len(&self) -> usize {
self.grids.len()
}
pub fn is_empty(&self) -> bool {
self.grids.is_empty()
}
fn read_cell(&self, qr: &Grid, x: isize, y: isize) -> bool {
let p = qr.c.map(x as f64 + 0.5, y as f64 + 0.5);
if p.y < 0 || p.x < 0 {
false
} else {
self.image
.get_pixel(p.x as usize, p.y as usize)
.map(|i| !i.is_white())
.unwrap_or(false)
}
}
}