#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Module {
Data(bool),
Finder(bool),
Separator,
Alignment(bool),
Timing(bool),
FormatInfo(bool),
VersionInfo(bool),
}
impl Module {
pub fn is_dark(&self) -> bool {
match self {
Module::Data(v)
| Module::Finder(v)
| Module::Alignment(v)
| Module::Timing(v)
| Module::FormatInfo(v)
| Module::VersionInfo(v) => *v,
Module::Separator => false,
}
}
pub fn is_data(&self) -> bool {
matches!(self, Module::Data(_))
}
}
#[derive(Debug, Clone)]
pub struct QRMatrix {
pub version: u8,
pub size: usize,
pub modules: Vec<Vec<Module>>,
}
impl QRMatrix {
pub fn new(version: u8) -> Self {
let size = (version as usize) * 4 + 17;
let modules = vec![vec![Module::Data(false); size]; size];
Self { version, size, modules }
}
pub fn set(&mut self, i: usize, j: usize, module: Module) {
if i >= self.size || j >= self.size {
return;
}
self.modules[j][i] = module;
}
pub fn get(&self, i: usize, j: usize) -> Module {
if i >= self.size || j >= self.size {
return Module::Data(false);
}
self.modules[j][i]
}
pub fn data_at(&self, i: usize, j: usize) -> Option<bool> {
match self.get(i, j) {
Module::Data(v) => Some(v),
_ => None,
}
}
pub fn size(&self) -> usize {
self.size
}
pub fn place_function_patterns(&mut self) {
self.place_finder_marker(0, 0);
self.place_finder_marker(self.size - 7, 0);
self.place_finder_marker(0, self.size - 7);
self.place_separators();
self.place_timing_patterns();
if self.version >= 2 {
self.place_alignment_patterns();
}
self.reserve_format_info_area();
if self.version >= 7 {
self.reserve_version_info_area();
}
}
fn reserve_format_info_area(&mut self) {
for (c, r) in crate::matrix::format_info::all_format_info_positions(self.size) {
self.set(c, r, Module::FormatInfo(false));
}
let s = self.size;
self.set(8, s - 8, Module::FormatInfo(true));
}
fn reserve_version_info_area(&mut self) {
let s = self.size;
for row in 0..6 {
for col in 0..3 {
self.set(s - 11 + col, row, Module::VersionInfo(false));
self.set(row, s - 11 + col, Module::VersionInfo(false));
}
}
}
fn place_finder_marker(&mut self, ox: usize, oy: usize) {
for dy in 0..7 {
for dx in 0..7 {
self.set(ox + dx, oy + dy, Module::Finder(FINDER_PATTERN[dy][dx]));
}
}
}
fn place_separators(&mut self) {
let s = self.size;
for i in 0..8 {
self.set(i, 7, Module::Separator);
self.set(7, i, Module::Separator);
self.set(s - 8 + i, 7, Module::Separator);
self.set(s - 8, i, Module::Separator);
self.set(i, s - 8, Module::Separator);
self.set(7, s - 8 + i, Module::Separator);
}
}
fn place_timing_patterns(&mut self) {
let s = self.size;
for i in 8..=s - 9 {
let dark = i % 2 == 0;
self.set(i, 6, Module::Timing(dark));
self.set(6, i, Module::Timing(dark));
}
}
fn place_alignment_patterns(&mut self) {
let positions = alignment_positions(self.version);
for &(cx, cy) in &positions {
if matches!(self.get(cx, cy), Module::Finder(_)) {
continue;
}
let ox = cx - 2;
let oy = cy - 2;
for dy in 0..5 {
for dx in 0..5 {
self.set(ox + dx, oy + dy, Module::Alignment(ALIGNMENT_PATTERN[dy][dx]));
}
}
}
}
}
const FINDER_PATTERN: [[bool; 7]; 7] = [
[true, true, true, true, true, true, true ],
[true, false, false, false, false, false, true ],
[true, false, true, true, true, false, true ],
[true, false, true, true, true, false, true ],
[true, false, true, true, true, false, true ],
[true, false, false, false, false, false, true ],
[true, true, true, true, true, true, true ],
];
const ALIGNMENT_PATTERN: [[bool; 5]; 5] = [
[true, true, true, true, true ],
[true, false, false, false, true ],
[true, false, true, false, true ],
[true, false, false, false, true ],
[true, true, true, true, true ],
];
const ALIGNMENT_CENTRES: [&[usize]; 40] = [
&[],
&[6, 18],
&[6, 22],
&[6, 26],
&[6, 30],
&[6, 34],
&[6, 22, 38],
&[6, 24, 42],
&[6, 26, 46],
&[6, 28, 50],
&[6, 30, 54],
&[6, 32, 58],
&[6, 34, 62],
&[6, 26, 46, 66],
&[6, 26, 48, 70],
&[6, 26, 50, 74],
&[6, 30, 54, 78],
&[6, 30, 56, 82],
&[6, 30, 58, 86],
&[6, 34, 62, 90],
&[6, 28, 50, 72, 94],
&[6, 26, 50, 74, 98],
&[6, 30, 54, 78, 102],
&[6, 28, 54, 80, 106],
&[6, 32, 58, 84, 110],
&[6, 30, 58, 86, 114],
&[6, 34, 62, 90, 118],
&[6, 26, 50, 74, 98, 122],
&[6, 30, 54, 78, 102, 126],
&[6, 26, 52, 78, 104, 130],
&[6, 30, 56, 82, 108, 134],
&[6, 34, 60, 86, 112, 138],
&[6, 30, 58, 86, 114, 142],
&[6, 34, 62, 90, 118, 146],
&[6, 30, 54, 78, 102, 126, 150],
&[6, 24, 50, 76, 102, 128, 154],
&[6, 28, 54, 80, 106, 132, 158],
&[6, 32, 58, 84, 110, 136, 162],
&[6, 26, 54, 82, 110, 138, 166],
&[6, 30, 58, 86, 114, 142, 170],
];
fn alignment_positions(version: u8) -> Vec<(usize, usize)> {
let centres = ALIGNMENT_CENTRES[(version as usize) - 1];
if centres.is_empty() {
return Vec::new();
}
let last = *centres.last().unwrap();
let mut out = Vec::new();
for &cy in centres {
for &cx in centres {
let on_finder = (cx == 6 && cy == 6)
|| (cx == 6 && cy == last)
|| (cx == last && cy == 6);
if !on_finder {
out.push((cx, cy));
}
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_matrix_size_version_1() {
let m = QRMatrix::new(1);
assert_eq!(m.size, 21);
}
#[test]
fn test_matrix_size_version_10() {
let m = QRMatrix::new(10);
assert_eq!(m.size, 57);
}
#[test]
fn test_finder_marker_at_topleft() {
let mut mat = QRMatrix::new(1);
mat.place_function_patterns();
assert_eq!(mat.get(3, 3), Module::Finder(true));
assert_eq!(mat.get(1, 1), Module::Finder(false));
}
#[test]
fn test_separator_placed() {
let mut mat = QRMatrix::new(1);
mat.place_function_patterns();
assert_eq!(mat.get(0, 7), Module::Separator);
assert_eq!(mat.get(7, 0), Module::Separator);
}
#[test]
fn test_timing_alternates() {
let mut mat = QRMatrix::new(2);
mat.place_function_patterns();
assert_eq!(mat.get(8, 6), Module::Timing(true));
assert_eq!(mat.get(9, 6), Module::Timing(false));
assert_eq!(mat.get(10, 6), Module::Timing(true));
}
#[test]
fn format_reserve_matches_placement_count() {
let s = 25;
let cells = crate::matrix::format_info::all_format_info_positions(s);
assert_eq!(cells.len(), 30);
}
}