use super::tables::{alignment_centers, Version};
pub type Module = bool;
#[derive(Debug, Clone)]
pub struct Matrix {
pub version: Version,
pub size: usize,
modules: Vec<Module>,
reserved: Vec<bool>,
}
impl Matrix {
pub fn new(version: Version) -> Self {
let size = version.size();
let mut m = Matrix {
version,
size,
modules: vec![false; size * size],
reserved: vec![false; size * size],
};
m.place_finders_and_separators();
m.place_timing_patterns();
m.place_alignment_patterns();
m.place_dark_module();
m.reserve_format_info();
if version.0 >= 7 {
m.reserve_version_info();
}
m
}
#[inline]
fn idx(&self, row: usize, col: usize) -> usize {
row * self.size + col
}
#[inline]
pub fn get(&self, row: usize, col: usize) -> Module {
self.modules[self.idx(row, col)]
}
#[inline]
pub fn set_data(&mut self, row: usize, col: usize, value: Module) {
debug_assert!(
!self.reserved[self.idx(row, col)],
"set_data on reserved cell ({}, {})",
row,
col
);
let i = self.idx(row, col);
self.modules[i] = value;
}
fn set_function(&mut self, row: usize, col: usize, value: Module) {
let i = self.idx(row, col);
self.modules[i] = value;
self.reserved[i] = true;
}
pub fn set_reserved_bit(&mut self, row: usize, col: usize, value: Module) {
let i = self.idx(row, col);
debug_assert!(self.reserved[i], "set_reserved_bit on unreserved ({}, {})", row, col);
self.modules[i] = value;
}
fn reserve(&mut self, row: usize, col: usize) {
let i = self.idx(row, col);
self.reserved[i] = true;
}
#[inline]
pub fn is_reserved(&self, row: usize, col: usize) -> bool {
self.reserved[self.idx(row, col)]
}
pub fn modules_iter(&self) -> std::slice::Iter<'_, bool> {
self.modules.iter()
}
pub fn from_modules(version: Version, modules: Vec<bool>) -> Self {
let template = Matrix::new(version); debug_assert_eq!(modules.len(), template.size * template.size);
Self {
version,
size: template.size,
modules,
reserved: template.reserved,
}
}
fn place_finder_at(&mut self, top: usize, left: usize) {
for dr in 0..7 {
for dc in 0..7 {
let on_outer = dr == 0 || dr == 6 || dc == 0 || dc == 6;
let in_inner = (2..=4).contains(&dr) && (2..=4).contains(&dc);
self.set_function(top + dr, left + dc, on_outer || in_inner);
}
}
}
fn place_separators(&mut self) {
let n = self.size;
for k in 0..8 {
self.set_function(7, k, false);
self.set_function(k, 7, false);
}
for k in 0..8 {
self.set_function(7, n - 1 - k, false);
self.set_function(k, n - 8, false);
}
for k in 0..8 {
self.set_function(n - 8, k, false);
self.set_function(n - 1 - k, 7, false);
}
}
fn place_finders_and_separators(&mut self) {
let n = self.size;
self.place_finder_at(0, 0);
self.place_finder_at(0, n - 7);
self.place_finder_at(n - 7, 0);
self.place_separators();
}
fn place_timing_patterns(&mut self) {
let n = self.size;
for i in 8..(n - 8) {
let on = i % 2 == 0;
self.set_function(6, i, on); self.set_function(i, 6, on); }
}
fn place_alignment_at(&mut self, center_row: usize, center_col: usize) {
for dr in -2i32..=2 {
for dc in -2i32..=2 {
let r = (center_row as i32 + dr) as usize;
let c = (center_col as i32 + dc) as usize;
let on_outer = dr.abs() == 2 || dc.abs() == 2;
let is_center = dr == 0 && dc == 0;
self.set_function(r, c, on_outer || is_center);
}
}
}
fn place_alignment_patterns(&mut self) {
let centers = alignment_centers(self.version);
if centers.is_empty() {
return;
}
let n = self.size;
for &r in centers {
for &c in centers {
let r = r as usize;
let c = c as usize;
let overlaps_finder = (r < 8 && c < 8)
|| (r < 8 && c > n - 9)
|| (r > n - 9 && c < 8);
if overlaps_finder {
continue;
}
self.place_alignment_at(r, c);
}
}
}
fn place_dark_module(&mut self) {
let v = self.version.0 as usize;
self.set_function(4 * v + 9, 8, true);
}
fn reserve_format_info(&mut self) {
let n = self.size;
for c in 0..9 {
if c == 6 {
continue;
}
self.reserve(8, c);
}
for r in 0..9 {
if r == 6 {
continue;
}
self.reserve(r, 8);
}
for c in (n - 8)..n {
self.reserve(8, c);
}
for r in (n - 7)..n {
self.reserve(r, 8);
}
}
fn reserve_version_info(&mut self) {
let n = self.size;
for r in 0..6 {
for c in (n - 11)..(n - 8) {
self.reserve(r, c);
}
}
for r in (n - 11)..(n - 8) {
for c in 0..6 {
self.reserve(r, c);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn v1_size_and_finders() {
let m = Matrix::new(Version::new(1));
assert_eq!(m.size, 21);
assert!(m.get(3, 3));
assert!(m.get(3, 17));
assert!(m.get(17, 3));
assert!(!m.get(1, 3));
}
#[test]
fn v1_timing_alternates() {
let m = Matrix::new(Version::new(1));
for c in 8..13 {
assert_eq!(m.get(6, c), c % 2 == 0, "row 6 col {}", c);
assert_eq!(m.get(c, 6), c % 2 == 0, "col 6 row {}", c);
}
}
#[test]
fn dark_module_present() {
let m = Matrix::new(Version::new(1));
assert!(m.get(13, 8));
assert!(m.is_reserved(13, 8));
}
#[test]
fn v2_has_one_alignment_pattern() {
let m = Matrix::new(Version::new(2));
assert!(m.get(16, 16)); assert!(m.get(18, 18)); assert!(!m.get(17, 17)); }
#[test]
fn v7_has_version_info_reserved() {
let m = Matrix::new(Version::new(7));
let n = m.size;
assert!(m.is_reserved(0, n - 11));
assert!(m.is_reserved(5, n - 9));
assert!(m.is_reserved(n - 11, 0));
assert!(m.is_reserved(n - 9, 5));
}
#[test]
fn format_info_area_reserved() {
let m = Matrix::new(Version::new(1));
let n = m.size;
assert!(m.is_reserved(8, 0));
assert!(m.is_reserved(8, 8));
assert!(m.is_reserved(0, 8));
assert!(m.is_reserved(8, n - 1));
assert!(m.is_reserved(n - 1, 8));
assert!(m.is_reserved(6, 0));
}
#[test]
fn data_area_not_reserved() {
let m = Matrix::new(Version::new(1));
assert!(!m.is_reserved(10, 10));
assert!(!m.is_reserved(14, 12));
}
#[test]
fn data_module_count_v1() {
let m = Matrix::new(Version::new(1));
let n_data: usize = m.reserved.iter().filter(|&&r| !r).count();
assert_eq!(n_data, 208, "V1 应有 208 个数据模块");
}
#[test]
fn data_module_count_v2() {
let m = Matrix::new(Version::new(2));
let n_data: usize = m.reserved.iter().filter(|&&r| !r).count();
assert_eq!(n_data, 359, "V2 应有 359 个数据模块");
}
}