feat: QR 矩阵 + 功能图案绘制

This commit is contained in:
2026-06-16 23:44:21 +08:00
parent f6a349882d
commit 1adb7e41e4
2 changed files with 203 additions and 2 deletions
+35 -1
View File
@@ -1 +1,35 @@
// FIXME: 模块矩阵 — Task 7 /// QR 码模块矩阵
#[derive(Clone)]
pub struct Matrix {
pub size: u8,
pub modules: Vec<Vec<bool>>, // true = 暗(黑), false = 亮(白)
pub reserved: Vec<Vec<bool>>, // true = 功能图案占用,不可放数据
}
impl Matrix {
pub fn new(size: u8) -> Self {
let modules = vec![vec![false; size as usize]; size as usize];
let reserved = vec![vec![false; size as usize]; size as usize];
Matrix { size, modules, reserved }
}
pub fn get(&self, x: u8, y: u8) -> bool {
self.modules[y as usize][x as usize]
}
pub fn set(&mut self, x: u8, y: u8, value: bool) {
self.modules[y as usize][x as usize] = value;
}
/// 标记为功能图案保留区域
pub fn reserve(&mut self, x: u8, y: u8) {
if x < self.size && y < self.size {
self.reserved[y as usize][x as usize] = true;
}
}
/// 是否被功能图案占用(不可放置数据)
pub fn is_reserved(&self, x: u8, y: u8) -> bool {
x < self.size && y < self.size && self.reserved[y as usize][x as usize]
}
}
+168 -1
View File
@@ -1 +1,168 @@
// FIXME: 功能图案 — Task 7 use crate::matrix::grid::Matrix;
/// 放置定位图案(3 个角上的大回字形,7×7)
pub fn place_finder_patterns(matrix: &mut Matrix) {
let positions = [
(0u8, 0u8), // 左上
(matrix.size - 7, 0), // 右上
(0, matrix.size - 7), // 左下
];
for &(fx, fy) in &positions {
for dy in 0..7u8 {
for dx in 0..7u8 {
let is_dark = match (dx, dy) {
// 外边框
(0, _) | (6, _) | (_, 0) | (_, 6) => true,
// 中间 3×3 方块
(2..=4, 2..=4) => true,
_ => false,
};
let x = fx + dx;
let y = fy + dy;
matrix.set(x, y, is_dark);
matrix.reserve(x, y);
}
}
}
}
/// 放置时序图案(行 6 和列 6 的黑白交替线)
pub fn place_timing_patterns(matrix: &mut Matrix) {
let size = matrix.size;
// 水平时序线 (y = 6)
for x in 8..size - 8 {
matrix.set(x, 6, x % 2 == 0);
matrix.reserve(x, 6);
}
// 垂直时序线 (x = 6)
for y in 8..size - 8 {
matrix.set(6, y, y % 2 == 0);
matrix.reserve(6, y);
}
}
/// 放置所有对齐图案(5×5 回字形)
pub fn place_alignment_patterns(matrix: &mut Matrix, positions: &[u8]) {
for &cy in positions {
for &cx in positions {
if is_near_finder(cx, cy, matrix.size) {
continue;
}
place_single_alignment(matrix, cx, cy);
}
}
}
fn place_single_alignment(matrix: &mut Matrix, cx: u8, cy: u8) {
let x0 = cx - 2;
let y0 = cy - 2;
for dy in 0..5u8 {
for dx in 0..5u8 {
let is_dark = match (dx, dy) {
(0, _) | (4, _) | (_, 0) | (_, 4) => true,
(2, 2) => true,
_ => false,
};
let x = x0 + dx;
let y = y0 + dy;
matrix.set(x, y, is_dark);
matrix.reserve(x, y);
}
}
}
/// 检查坐标是否与定位图案重叠
fn is_near_finder(x: u8, y: u8, size: u8) -> bool {
let s = size as i16;
let x = x as i16;
let y = y as i16;
// 左上角
if x - 2 < 7 && y - 2 < 7 { return true; }
// 右上角
if x + 2 >= s - 7 && y - 2 < 7 { return true; }
// 左下角
if x - 2 < 7 && y + 2 >= s - 7 { return true; }
false
}
/// 预留格式信息区域(15 bit)
pub fn reserve_format_areas(matrix: &mut Matrix) {
let size = matrix.size;
// 定位图案旁的格式信息条
for i in 0..9u8 {
// 左上水平
if i != 6 { matrix.reserve(i, 8); }
// 左上垂直
if i != 6 { matrix.reserve(8, i); }
// 右上垂直
if i + size - 8 < size { matrix.reserve(size - 1 - i, 8); }
// 左下水平
if i + size - 8 < size { matrix.reserve(8, size - 1 - i); }
}
// 暗模块位置
if size > 8 {
matrix.set(8, size - 8, true);
matrix.reserve(8, size - 8);
}
}
/// 预留版本信息区域(版本 ≥ 7,18 bit)
pub fn reserve_version_areas(matrix: &mut Matrix) {
let size = matrix.size;
for i in 0..6u8 {
for j in 0..3u8 {
// 右上角旁边 (左下方向)
matrix.reserve(size - 11 + j, i);
// 左下角旁边 (右上方向)
matrix.reserve(i, size - 11 + j);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_finder_patterns_v1() {
let mut m = Matrix::new(21);
place_finder_patterns(&mut m);
// 三个角的定位图案外边框应有暗模块
assert!(m.get(0, 0));
assert!(m.get(20, 0));
assert!(m.get(0, 20));
// 定位图案中心 (3,3) 是暗的
assert!(m.get(3, 3));
}
#[test]
fn test_finder_patterns_reserved() {
let mut m = Matrix::new(21);
place_finder_patterns(&mut m);
// 定位图案区域应全被标记为保留
assert!(m.is_reserved(0, 0));
assert!(m.is_reserved(6, 6));
}
#[test]
fn test_timing_patterns_alternate() {
let mut m = Matrix::new(21);
place_timing_patterns(&mut m);
assert!(m.get(8, 6)); // 偶数 = 暗
assert!(!m.get(9, 6)); // 奇数 = 亮
}
#[test]
fn test_alignment_patterns_v2() {
let mut m = Matrix::new(25);
place_alignment_patterns(&mut m, &[6, 18]);
// v2 对齐位置 [6, 18],但 (6,6) (6,18) (18,6) 都与定位图案重叠被跳过,
// 只有 (18, 18) 实际绘制
assert!(m.get(18, 18), "对齐图案中心 (18,18) 应为暗模块");
// 被跳过的位置不应该有对齐图案轨迹
assert!(!m.is_reserved(6, 6), "(6,6) 与定位图案重叠,不应标记为保留");
assert!(!m.is_reserved(6, 18), "(6,18) 与左下定位图案重叠");
assert!(!m.is_reserved(18, 6), "(18,6) 与右上定位图案重叠");
}
}