Skip to main content

agg_rust/
span_interpolator_adaptor.rs

1//! Span interpolator adaptor with distortion.
2//!
3//! Port of `agg_span_interpolator_adaptor.h`.
4//! Wraps any `SpanInterpolator`, applying a distortion function
5//! after `coordinates()`.
6
7use crate::span_interpolator_linear::SpanInterpolator;
8
9/// Trait for coordinate distortion applied after interpolation.
10///
11/// Port of the C++ `Distortion` template concept.
12pub trait Distortion {
13    fn calculate(&self, x: &mut i32, y: &mut i32);
14}
15
16/// Adaptor that wraps a span interpolator and applies distortion.
17///
18/// Port of C++ `span_interpolator_adaptor<Interpolator, Distortion>`.
19/// After the base interpolator computes coordinates, the distortion
20/// function modifies them (e.g., for wave, lens, or other effects).
21pub struct SpanInterpolatorAdaptor<Interp, Dist> {
22    interp: Interp,
23    distortion: Dist,
24}
25
26impl<Interp: SpanInterpolator, Dist: Distortion> SpanInterpolatorAdaptor<Interp, Dist> {
27    pub fn new(interp: Interp, distortion: Dist) -> Self {
28        Self { interp, distortion }
29    }
30
31    pub fn interpolator(&self) -> &Interp {
32        &self.interp
33    }
34
35    pub fn interpolator_mut(&mut self) -> &mut Interp {
36        &mut self.interp
37    }
38
39    pub fn distortion(&self) -> &Dist {
40        &self.distortion
41    }
42
43    pub fn distortion_mut(&mut self) -> &mut Dist {
44        &mut self.distortion
45    }
46}
47
48impl<Interp: SpanInterpolator, Dist: Distortion> SpanInterpolator
49    for SpanInterpolatorAdaptor<Interp, Dist>
50{
51    fn begin(&mut self, x: f64, y: f64, len: u32) {
52        self.interp.begin(x, y, len);
53    }
54
55    fn next(&mut self) {
56        self.interp.next();
57    }
58
59    fn coordinates(&self, x: &mut i32, y: &mut i32) {
60        self.interp.coordinates(x, y);
61        self.distortion.calculate(x, y);
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use crate::span_interpolator_linear::{SpanInterpolatorLinear, SUBPIXEL_SCALE};
69    use crate::trans_affine::TransAffine;
70
71    /// A simple distortion that offsets coordinates by fixed amounts.
72    struct OffsetDistortion {
73        dx: i32,
74        dy: i32,
75    }
76
77    impl Distortion for OffsetDistortion {
78        fn calculate(&self, x: &mut i32, y: &mut i32) {
79            *x += self.dx;
80            *y += self.dy;
81        }
82    }
83
84    #[test]
85    fn test_identity_with_offset_distortion() {
86        let trans = TransAffine::new();
87        let interp = SpanInterpolatorLinear::new(trans);
88        let dist = OffsetDistortion {
89            dx: 10 * SUBPIXEL_SCALE,
90            dy: 20 * SUBPIXEL_SCALE,
91        };
92        let mut adaptor = SpanInterpolatorAdaptor::new(interp, dist);
93
94        adaptor.begin(5.0, 5.0, 1);
95        let (mut x, mut y) = (0, 0);
96        adaptor.coordinates(&mut x, &mut y);
97
98        // Identity transform: (5,5) * 256 = (1280,1280), plus offset (2560, 5120)
99        assert_eq!(x, 5 * SUBPIXEL_SCALE + 10 * SUBPIXEL_SCALE);
100        assert_eq!(y, 5 * SUBPIXEL_SCALE + 20 * SUBPIXEL_SCALE);
101    }
102
103    #[test]
104    fn test_zero_distortion_passthrough() {
105        let trans = TransAffine::new();
106        let interp = SpanInterpolatorLinear::new(trans);
107        let dist = OffsetDistortion { dx: 0, dy: 0 };
108        let mut adaptor = SpanInterpolatorAdaptor::new(interp, dist);
109
110        adaptor.begin(10.0, 20.0, 1);
111        let (mut x, mut y) = (0, 0);
112        adaptor.coordinates(&mut x, &mut y);
113
114        // Should match plain interpolator output
115        let trans2 = TransAffine::new();
116        let mut interp2 = SpanInterpolatorLinear::new(trans2);
117        interp2.begin(10.0, 20.0, 1);
118        let (mut x2, mut y2) = (0, 0);
119        interp2.coordinates(&mut x2, &mut y2);
120
121        assert_eq!(x, x2);
122        assert_eq!(y, y2);
123    }
124
125    /// Sine-wave distortion for testing.
126    struct WaveDistortion {
127        amplitude: i32,
128    }
129
130    impl Distortion for WaveDistortion {
131        fn calculate(&self, _x: &mut i32, y: &mut i32) {
132            *y += self.amplitude;
133        }
134    }
135
136    #[test]
137    fn test_wave_distortion() {
138        let trans = TransAffine::new();
139        let interp = SpanInterpolatorLinear::new(trans);
140        let dist = WaveDistortion { amplitude: 100 };
141        let mut adaptor = SpanInterpolatorAdaptor::new(interp, dist);
142
143        adaptor.begin(0.0, 0.0, 5);
144        let (mut x, mut y) = (0, 0);
145        adaptor.coordinates(&mut x, &mut y);
146        assert_eq!(y, 100); // 0 + amplitude
147    }
148}