-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathmerge_qr_squares.rs
More file actions
124 lines (105 loc) · 3.05 KB
/
merge_qr_squares.rs
File metadata and controls
124 lines (105 loc) · 3.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use glam::DVec2;
use graphic_types::Vector;
use std::collections::VecDeque;
use vector_types::subpath;
pub fn merge_qr_squares(qr_code: &qrcodegen::QrCode) -> Vector {
let mut vector = Vector::default();
let size = qr_code.size() as usize;
// 0 = empty
// 1 = filled, unvisited
// 2 = filled, current island
let mut remaining = vec![vec![0u8; size]; size];
#[allow(clippy::needless_range_loop)]
for y in 0..size {
#[allow(clippy::needless_range_loop)]
for x in 0..size {
if qr_code.get_module(x as i32, y as i32) {
remaining[y][x] = 1;
}
}
}
for y in 0..size {
for x in 0..size {
if remaining[y][x] != 1 {
continue;
}
// fill island
let mut island = Vec::new();
let mut queue = VecDeque::new();
queue.push_back((x, y));
remaining[y][x] = 2;
while let Some((ix, iy)) = queue.pop_front() {
island.push((ix, iy));
for (dx, dy) in [(0, 1), (0, -1), (1, 0), (-1, 0)] {
let nx = ix as i32 + dx;
let ny = iy as i32 + dy;
if nx >= 0 && nx < size as i32 && ny >= 0 && ny < size as i32 && remaining[ny as usize][nx as usize] == 1 {
remaining[ny as usize][nx as usize] = 2;
queue.push_back((nx as usize, ny as usize));
}
}
}
// boundary detection
let mut outbound = vec![vec![0u8; size + 1]; size + 1];
for &(ix, iy) in &island {
if iy == 0 || remaining[iy - 1][ix] != 2 {
outbound[iy][ix] |= 1 << 0;
}
if ix == size - 1 || remaining[iy][ix + 1] != 2 {
outbound[iy][ix + 1] |= 1 << 1;
}
if iy == size - 1 || remaining[iy + 1][ix] != 2 {
outbound[iy + 1][ix + 1] |= 1 << 2;
}
if ix == 0 || remaining[iy][ix - 1] != 2 {
outbound[iy + 1][ix] |= 1 << 3;
}
}
// tracing loops
for vy in 0..=size {
for vx in 0..=size {
while outbound[vy][vx] != 0 {
let mut dir = outbound[vy][vx].trailing_zeros() as usize;
let start = (vx, vy);
let mut current = start;
let mut points = Vec::new();
loop {
points.push(DVec2::new(current.0 as f64, current.1 as f64));
outbound[current.1][current.0] &= !(1 << dir);
current = match dir {
0 => (current.0 + 1, current.1),
1 => (current.0, current.1 + 1),
2 => (current.0 - 1, current.1),
3 => (current.0, current.1 - 1),
_ => unreachable!(),
};
if current == start {
break;
}
dir = outbound[current.1][current.0].trailing_zeros() as usize;
}
if points.len() > 2 {
let mut simplified = Vec::new();
for i in 0..points.len() {
let prev = points[(i + points.len() - 1) % points.len()];
let curr = points[i];
let next = points[(i + 1) % points.len()];
if (curr - prev).perp_dot(next - curr).abs() > 1e-6 {
simplified.push(curr);
}
}
if !simplified.is_empty() {
vector.append_subpath(subpath::Subpath::from_anchors(simplified, true), false);
}
}
}
}
}
// marking island as processed
for &(ix, iy) in &island {
remaining[iy][ix] = 0;
}
}
}
vector
}