Skip to main content

qr_code_styling/figures/corner_square/
drawer.rs

1//! QR corner square drawer implementation.
2
3use crate::figures::traits::{rotate_transform, svg_path};
4use crate::types::CornerSquareType;
5
6/// QR code corner square drawer.
7pub struct QRCornerSquare {
8    square_type: CornerSquareType,
9}
10
11impl QRCornerSquare {
12    /// Create a new corner square drawer with the specified type.
13    pub fn new(square_type: CornerSquareType) -> Self {
14        Self { square_type }
15    }
16
17    /// Draw the corner square.
18    pub fn draw(&self, x: f64, y: f64, size: f64, rotation: f64) -> String {
19        match self.square_type {
20            CornerSquareType::Square => self.draw_square(x, y, size, rotation),
21            CornerSquareType::Dot => self.draw_dot(x, y, size, rotation),
22            CornerSquareType::ExtraRounded => self.draw_extra_rounded(x, y, size, rotation),
23        }
24    }
25
26    /// Draw basic dot (ring) shape.
27    fn basic_dot(&self, x: f64, y: f64, size: f64, rotation: f64) -> String {
28        let transform = rotate_transform(x, y, size, rotation);
29        let dot_size = size / 7.0;
30        let half_size = size / 2.0;
31        let inner_radius = half_size - dot_size;
32
33        // Ring shape using clip-rule evenodd
34        let d = format!(
35            "M {} {} a {} {} 0 1 0 0.1 0 z m 0 {} a {} {} 0 1 1 -0.1 0 Z",
36            x + half_size,
37            y,
38            half_size,
39            half_size,
40            dot_size,
41            inner_radius,
42            inner_radius
43        );
44
45        svg_path(&d, Some("evenodd"), transform.as_deref())
46    }
47
48    /// Draw basic square shape with hollow center.
49    fn basic_square(&self, x: f64, y: f64, size: f64, rotation: f64) -> String {
50        let transform = rotate_transform(x, y, size, rotation);
51        let dot_size = size / 7.0;
52
53        // Outer square + inner square (hollow)
54        let d = format!(
55            "M {} {} v {} h {} v {} z M {} {} h {} v {} h {} z",
56            x, y,
57            size, size, -size,
58            x + dot_size, y + dot_size,
59            size - 2.0 * dot_size,
60            size - 2.0 * dot_size,
61            -(size - 2.0 * dot_size)
62        );
63
64        svg_path(&d, Some("evenodd"), transform.as_deref())
65    }
66
67    /// Draw extra-rounded shape.
68    fn basic_extra_rounded(&self, x: f64, y: f64, size: f64, rotation: f64) -> String {
69        let transform = rotate_transform(x, y, size, rotation);
70        let dot_size = size / 7.0;
71
72        // Outer rounded path
73        let outer = format!(
74            "M {} {} v {} a {} {} 0 0 0 {} {} h {} a {} {} 0 0 0 {} {} v {} a {} {} 0 0 0 {} {} h {} a {} {} 0 0 0 {} {}",
75            x, y + 2.5 * dot_size,
76            2.0 * dot_size,
77            2.5 * dot_size, 2.5 * dot_size, 2.5 * dot_size, 2.5 * dot_size,
78            2.0 * dot_size,
79            2.5 * dot_size, 2.5 * dot_size, 2.5 * dot_size, -2.5 * dot_size,
80            -2.0 * dot_size,
81            2.5 * dot_size, 2.5 * dot_size, -2.5 * dot_size, -2.5 * dot_size,
82            -2.0 * dot_size,
83            2.5 * dot_size, 2.5 * dot_size, -2.5 * dot_size, 2.5 * dot_size
84        );
85
86        // Inner rounded path
87        let inner = format!(
88            "M {} {} h {} a {} {} 0 0 1 {} {} v {} a {} {} 0 0 1 {} {} h {} a {} {} 0 0 1 {} {} v {} a {} {} 0 0 1 {} {}",
89            x + 2.5 * dot_size, y + dot_size,
90            2.0 * dot_size,
91            1.5 * dot_size, 1.5 * dot_size, 1.5 * dot_size, 1.5 * dot_size,
92            2.0 * dot_size,
93            1.5 * dot_size, 1.5 * dot_size, -1.5 * dot_size, 1.5 * dot_size,
94            -2.0 * dot_size,
95            1.5 * dot_size, 1.5 * dot_size, -1.5 * dot_size, -1.5 * dot_size,
96            -2.0 * dot_size,
97            1.5 * dot_size, 1.5 * dot_size, 1.5 * dot_size, -1.5 * dot_size
98        );
99
100        let d = format!("{} {}", outer, inner);
101        svg_path(&d, Some("evenodd"), transform.as_deref())
102    }
103
104    fn draw_dot(&self, x: f64, y: f64, size: f64, rotation: f64) -> String {
105        self.basic_dot(x, y, size, rotation)
106    }
107
108    fn draw_square(&self, x: f64, y: f64, size: f64, rotation: f64) -> String {
109        self.basic_square(x, y, size, rotation)
110    }
111
112    fn draw_extra_rounded(&self, x: f64, y: f64, size: f64, rotation: f64) -> String {
113        self.basic_extra_rounded(x, y, size, rotation)
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_draw_square() {
123        let drawer = QRCornerSquare::new(CornerSquareType::Square);
124        let svg = drawer.draw(0.0, 0.0, 70.0, 0.0);
125        assert!(svg.contains("path"));
126        assert!(svg.contains("evenodd"));
127    }
128
129    #[test]
130    fn test_draw_dot() {
131        let drawer = QRCornerSquare::new(CornerSquareType::Dot);
132        let svg = drawer.draw(0.0, 0.0, 70.0, 0.0);
133        assert!(svg.contains("path"));
134        assert!(svg.contains("evenodd"));
135    }
136
137    #[test]
138    fn test_draw_extra_rounded() {
139        let drawer = QRCornerSquare::new(CornerSquareType::ExtraRounded);
140        let svg = drawer.draw(0.0, 0.0, 70.0, 0.0);
141        assert!(svg.contains("path"));
142    }
143}