1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum Module {
34 Data(bool),
36 Finder(bool),
38 Separator,
40 Alignment(bool),
42 Timing(bool),
44 FormatInfo(bool),
46 VersionInfo(bool),
48}
49
50impl Module {
51 pub fn is_dark(&self) -> bool {
53 match self {
54 Module::Data(v)
55 | Module::Finder(v)
56 | Module::Alignment(v)
57 | Module::Timing(v)
58 | Module::FormatInfo(v)
59 | Module::VersionInfo(v) => *v,
60 Module::Separator => false,
61 }
62 }
63
64 pub fn is_data(&self) -> bool {
66 matches!(self, Module::Data(_))
67 }
68}
69
70#[derive(Debug, Clone)]
72pub struct QRMatrix {
73 pub version: u8,
74 pub size: usize,
75 pub modules: Vec<Vec<Module>>,
76}
77
78impl QRMatrix {
79 pub fn new(version: u8) -> Self {
82 let size = (version as usize) * 4 + 17;
83 let modules = vec![vec![Module::Data(false); size]; size];
84 Self { version, size, modules }
85 }
86
87 pub fn is_micro(&self) -> bool {
89 self.size <= 17 && self.size == self.version as usize * 2 + 9
90 }
91
92 pub fn set(&mut self, i: usize, j: usize, module: Module) {
94 if i >= self.size || j >= self.size {
95 return;
96 }
97 self.modules[j][i] = module;
98 }
99
100 pub fn get(&self, i: usize, j: usize) -> Module {
102 if i >= self.size || j >= self.size {
103 return Module::Data(false);
104 }
105 self.modules[j][i]
106 }
107
108 pub fn data_at(&self, i: usize, j: usize) -> Option<bool> {
110 match self.get(i, j) {
111 Module::Data(v) => Some(v),
112 _ => None,
113 }
114 }
115
116 pub fn size(&self) -> usize {
118 self.size
119 }
120
121 pub fn place_function_patterns(&mut self) {
125 self.place_finder_marker(0, 0);
126 self.place_finder_marker(self.size - 7, 0);
127 self.place_finder_marker(0, self.size - 7);
128
129 self.place_separators();
130 self.place_timing_patterns();
131
132 if self.version >= 2 {
133 self.place_alignment_patterns();
134 }
135
136 self.reserve_format_info_area();
137
138 if self.version >= 7 {
139 self.reserve_version_info_area();
140 }
141 }
142
143 fn reserve_format_info_area(&mut self) {
146 for (c, r) in crate::matrix::format_info::all_format_info_positions(self.size) {
147 self.set(c, r, Module::FormatInfo(false));
148 }
149 let s = self.size;
150 self.set(8, s - 8, Module::FormatInfo(true));
152 }
153
154 fn reserve_version_info_area(&mut self) {
156 let s = self.size;
157 for row in 0..6 {
158 for col in 0..3 {
159 self.set(s - 11 + col, row, Module::VersionInfo(false));
160 self.set(row, s - 11 + col, Module::VersionInfo(false));
161 }
162 }
163 }
164
165 fn place_finder_marker(&mut self, ox: usize, oy: usize) {
166 for dy in 0..7 {
167 for dx in 0..7 {
168 self.set(ox + dx, oy + dy, Module::Finder(FINDER_PATTERN[dy][dx]));
169 }
170 }
171 }
172
173 fn place_separators(&mut self) {
174 let s = self.size;
175 for i in 0..8 {
176 self.set(i, 7, Module::Separator);
177 self.set(7, i, Module::Separator);
178
179 self.set(s - 8 + i, 7, Module::Separator);
180 self.set(s - 8, i, Module::Separator);
181
182 self.set(i, s - 8, Module::Separator);
183 self.set(7, s - 8 + i, Module::Separator);
184 }
185 }
186
187 fn place_timing_patterns(&mut self) {
190 let s = self.size;
191 for i in 8..=s - 9 {
192 let dark = i % 2 == 0;
193 self.set(i, 6, Module::Timing(dark));
194 self.set(6, i, Module::Timing(dark));
195 }
196 }
197
198 fn place_alignment_patterns(&mut self) {
199 let positions = alignment_positions(self.version);
200 for &(cx, cy) in &positions {
201 if matches!(self.get(cx, cy), Module::Finder(_)) {
203 continue;
204 }
205 let ox = cx - 2;
206 let oy = cy - 2;
207 for dy in 0..5 {
208 for dx in 0..5 {
209 self.set(ox + dx, oy + dy, Module::Alignment(ALIGNMENT_PATTERN[dy][dx]));
210 }
211 }
212 }
213 }
214}
215
216const FINDER_PATTERN: [[bool; 7]; 7] = [
218 [true, true, true, true, true, true, true ],
219 [true, false, false, false, false, false, true ],
220 [true, false, true, true, true, false, true ],
221 [true, false, true, true, true, false, true ],
222 [true, false, true, true, true, false, true ],
223 [true, false, false, false, false, false, true ],
224 [true, true, true, true, true, true, true ],
225];
226
227const ALIGNMENT_PATTERN: [[bool; 5]; 5] = [
229 [true, true, true, true, true ],
230 [true, false, false, false, true ],
231 [true, false, true, false, true ],
232 [true, false, false, false, true ],
233 [true, true, true, true, true ],
234];
235
236const ALIGNMENT_CENTRES: [&[usize]; 40] = [
239 &[],
240 &[6, 18],
241 &[6, 22],
242 &[6, 26],
243 &[6, 30],
244 &[6, 34],
245 &[6, 22, 38],
246 &[6, 24, 42],
247 &[6, 26, 46],
248 &[6, 28, 50],
249 &[6, 30, 54],
250 &[6, 32, 58],
251 &[6, 34, 62],
252 &[6, 26, 46, 66],
253 &[6, 26, 48, 70],
254 &[6, 26, 50, 74],
255 &[6, 30, 54, 78],
256 &[6, 30, 56, 82],
257 &[6, 30, 58, 86],
258 &[6, 34, 62, 90],
259 &[6, 28, 50, 72, 94],
260 &[6, 26, 50, 74, 98],
261 &[6, 30, 54, 78, 102],
262 &[6, 28, 54, 80, 106],
263 &[6, 32, 58, 84, 110],
264 &[6, 30, 58, 86, 114],
265 &[6, 34, 62, 90, 118],
266 &[6, 26, 50, 74, 98, 122],
267 &[6, 30, 54, 78, 102, 126],
268 &[6, 26, 52, 78, 104, 130],
269 &[6, 30, 56, 82, 108, 134],
270 &[6, 34, 60, 86, 112, 138],
271 &[6, 30, 58, 86, 114, 142],
272 &[6, 34, 62, 90, 118, 146],
273 &[6, 30, 54, 78, 102, 126, 150],
274 &[6, 24, 50, 76, 102, 128, 154],
275 &[6, 28, 54, 80, 106, 132, 158],
276 &[6, 32, 58, 84, 110, 136, 162],
277 &[6, 26, 54, 82, 110, 138, 166],
278 &[6, 30, 58, 86, 114, 142, 170],
279];
280
281fn alignment_positions(version: u8) -> Vec<(usize, usize)> {
284 let centres = ALIGNMENT_CENTRES[(version as usize) - 1];
285 if centres.is_empty() {
286 return Vec::new();
287 }
288 let last = *centres.last().unwrap();
289 let mut out = Vec::new();
290 for &cy in centres {
291 for &cx in centres {
292 let on_finder = (cx == 6 && cy == 6)
294 || (cx == 6 && cy == last)
295 || (cx == last && cy == 6);
296 if !on_finder {
297 out.push((cx, cy));
298 }
299 }
300 }
301 out
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn test_matrix_size_version_1() {
310 let m = QRMatrix::new(1);
311 assert_eq!(m.size, 21);
312 }
313
314 #[test]
315 fn test_matrix_size_version_10() {
316 let m = QRMatrix::new(10);
317 assert_eq!(m.size, 57);
318 }
319
320 #[test]
321 fn test_finder_marker_at_topleft() {
322 let mut mat = QRMatrix::new(1);
323 mat.place_function_patterns();
324 assert_eq!(mat.get(3, 3), Module::Finder(true));
326 assert_eq!(mat.get(1, 1), Module::Finder(false));
328 }
329
330 #[test]
331 fn test_separator_placed() {
332 let mut mat = QRMatrix::new(1);
333 mat.place_function_patterns();
334 assert_eq!(mat.get(0, 7), Module::Separator);
335 assert_eq!(mat.get(7, 0), Module::Separator);
336 }
337
338 #[test]
339 fn test_timing_alternates() {
340 let mut mat = QRMatrix::new(2);
341 mat.place_function_patterns();
342 assert_eq!(mat.get(8, 6), Module::Timing(true));
344 assert_eq!(mat.get(9, 6), Module::Timing(false));
345 assert_eq!(mat.get(10, 6), Module::Timing(true));
346 }
347
348 #[test]
349 fn format_reserve_matches_placement_count() {
350 let s = 25;
351 let cells = crate::matrix::format_info::all_format_info_positions(s);
352 assert_eq!(cells.len(), 30);
353 }
354
355 #[test]
356 fn is_micro_detects_micro_vs_normal_sizing() {
357 let normal = QRMatrix::new(1);
358 assert!(!normal.is_micro());
359 assert_eq!(normal.size, 21);
360
361 let micro = QRMatrix {
362 version: 1,
363 size: 11,
364 modules: vec![vec![Module::Data(false); 11]; 11],
365 };
366 assert!(micro.is_micro());
367 }
368}