Skip to main content

agg_rust/
trans_warp_magnifier.rs

1//! Warp magnifier transformation.
2//!
3//! Port of `agg_trans_warp_magnifier.h` + `agg_trans_warp_magnifier.cpp`.
4//! Creates a magnified circular zone (magnifying glass effect).
5
6use crate::span_interpolator_linear::Transformer;
7
8/// Warp magnifier transformation — magnifies a circular zone.
9///
10/// Inside the radius, coordinates are scaled by the magnification factor.
11/// Outside, coordinates transition smoothly to unmagnified space.
12pub struct TransWarpMagnifier {
13    pub xc: f64,
14    pub yc: f64,
15    pub magn: f64,
16    pub radius: f64,
17}
18
19impl TransWarpMagnifier {
20    pub fn new() -> Self {
21        Self {
22            xc: 0.0,
23            yc: 0.0,
24            magn: 1.0,
25            radius: 1.0,
26        }
27    }
28
29    pub fn center(&mut self, x: f64, y: f64) {
30        self.xc = x;
31        self.yc = y;
32    }
33
34    pub fn magnification(&mut self, m: f64) {
35        self.magn = m;
36    }
37
38    pub fn set_radius(&mut self, r: f64) {
39        self.radius = r;
40    }
41
42    pub fn inverse_transform(&self, x: &mut f64, y: &mut f64) {
43        let dx = *x - self.xc;
44        let dy = *y - self.yc;
45        let r = (dx * dx + dy * dy).sqrt();
46
47        if r < self.radius * self.magn {
48            *x = self.xc + dx / self.magn;
49            *y = self.yc + dy / self.magn;
50        } else {
51            let rnew = r - self.radius * (self.magn - 1.0);
52            *x = self.xc + rnew * dx / r;
53            *y = self.yc + rnew * dy / r;
54        }
55    }
56}
57
58impl Transformer for TransWarpMagnifier {
59    fn transform(&self, x: &mut f64, y: &mut f64) {
60        let dx = *x - self.xc;
61        let dy = *y - self.yc;
62        let r = (dx * dx + dy * dy).sqrt();
63        if r < self.radius {
64            *x = self.xc + dx * self.magn;
65            *y = self.yc + dy * self.magn;
66            return;
67        }
68        let m = (r + self.radius * (self.magn - 1.0)) / r;
69        *x = self.xc + dx * m;
70        *y = self.yc + dy * m;
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_identity_magnification() {
80        let t = TransWarpMagnifier::new();
81        let (mut x, mut y) = (5.0, 5.0);
82        t.transform(&mut x, &mut y);
83        assert!((x - 5.0).abs() < 1e-10);
84        assert!((y - 5.0).abs() < 1e-10);
85    }
86
87    #[test]
88    fn test_center_magnification() {
89        let mut t = TransWarpMagnifier::new();
90        t.center(100.0, 100.0);
91        t.magnification(2.0);
92        t.set_radius(50.0);
93
94        // Point at center stays at center
95        let (mut x, mut y) = (100.0, 100.0);
96        t.transform(&mut x, &mut y);
97        assert!((x - 100.0).abs() < 1e-10);
98        assert!((y - 100.0).abs() < 1e-10);
99
100        // Point inside radius is magnified
101        let (mut x, mut y) = (120.0, 100.0);
102        t.transform(&mut x, &mut y);
103        assert!((x - 140.0).abs() < 1e-10); // 100 + 20*2 = 140
104        assert!((y - 100.0).abs() < 1e-10);
105    }
106
107    #[test]
108    fn test_inverse_roundtrip() {
109        let mut t = TransWarpMagnifier::new();
110        t.center(50.0, 50.0);
111        t.magnification(3.0);
112        t.set_radius(30.0);
113
114        for &(ox, oy) in &[(50.0, 50.0), (60.0, 50.0), (90.0, 90.0), (20.0, 30.0)] {
115            let (mut x, mut y) = (ox, oy);
116            t.transform(&mut x, &mut y);
117            t.inverse_transform(&mut x, &mut y);
118            assert!((x - ox).abs() < 1e-8, "x: {x} != {ox}");
119            assert!((y - oy).abs() < 1e-8, "y: {y} != {oy}");
120        }
121    }
122}