Skip to main content

agg_rust/
conv_segmentator.rs

1//! Convenience line-segment subdivider.
2//!
3//! Port of `agg_conv_segmentator.h`.
4//! Wraps `ConvAdaptorVpgen` with `VpgenSegmentator` for subdividing
5//! long line segments into shorter ones.
6
7use crate::basics::VertexSource;
8use crate::conv_adaptor_vpgen::ConvAdaptorVpgen;
9use crate::vpgen_segmentator::VpgenSegmentator;
10
11/// Subdivides line segments of a vertex source for better curve approximation.
12///
13/// Port of C++ `conv_segmentator<VertexSource>`.
14/// Thin wrapper around `ConvAdaptorVpgen<VS, VpgenSegmentator>`.
15pub struct ConvSegmentator<VS> {
16    inner: ConvAdaptorVpgen<VS, VpgenSegmentator>,
17}
18
19impl<VS: VertexSource> ConvSegmentator<VS> {
20    pub fn new(source: VS) -> Self {
21        Self {
22            inner: ConvAdaptorVpgen::new(source, VpgenSegmentator::new()),
23        }
24    }
25
26    pub fn approximation_scale(&self) -> f64 {
27        self.inner.vpgen().approximation_scale()
28    }
29
30    pub fn set_approximation_scale(&mut self, s: f64) {
31        self.inner.vpgen_mut().set_approximation_scale(s);
32    }
33
34    pub fn source(&self) -> &VS {
35        self.inner.source()
36    }
37
38    pub fn source_mut(&mut self) -> &mut VS {
39        self.inner.source_mut()
40    }
41}
42
43impl<VS: VertexSource> VertexSource for ConvSegmentator<VS> {
44    fn rewind(&mut self, path_id: u32) {
45        self.inner.rewind(path_id);
46    }
47
48    fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
49        self.inner.vertex(x, y)
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::basics::{is_stop, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO, PATH_CMD_STOP};
57
58    /// Square path: (0,0)→(100,0)→(100,100)→(0,100)→close
59    struct SquareSource {
60        idx: usize,
61    }
62
63    impl SquareSource {
64        fn new() -> Self {
65            Self { idx: 0 }
66        }
67    }
68
69    impl VertexSource for SquareSource {
70        fn rewind(&mut self, _path_id: u32) {
71            self.idx = 0;
72        }
73
74        fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
75            self.idx += 1;
76            match self.idx {
77                1 => {
78                    *x = 0.0;
79                    *y = 0.0;
80                    PATH_CMD_MOVE_TO
81                }
82                2 => {
83                    *x = 100.0;
84                    *y = 0.0;
85                    PATH_CMD_LINE_TO
86                }
87                3 => {
88                    *x = 100.0;
89                    *y = 100.0;
90                    PATH_CMD_LINE_TO
91                }
92                4 => {
93                    *x = 0.0;
94                    *y = 100.0;
95                    PATH_CMD_LINE_TO
96                }
97                _ => PATH_CMD_STOP,
98            }
99        }
100    }
101
102    #[test]
103    fn test_conv_segmentator_passthrough() {
104        let mut seg = ConvSegmentator::new(SquareSource::new());
105        seg.set_approximation_scale(1.0);
106        seg.rewind(0);
107
108        let (mut x, mut y) = (0.0, 0.0);
109        let mut count = 0;
110        loop {
111            let cmd = seg.vertex(&mut x, &mut y);
112            if is_stop(cmd) {
113                break;
114            }
115            count += 1;
116        }
117        // With scale=1.0, short segments might not subdivide much
118        assert!(count >= 4, "Expected at least 4 vertices, got {count}");
119    }
120
121    #[test]
122    fn test_conv_segmentator_subdivides() {
123        let mut seg = ConvSegmentator::new(SquareSource::new());
124        seg.set_approximation_scale(10.0); // force fine subdivision
125        seg.rewind(0);
126
127        let (mut x, mut y) = (0.0, 0.0);
128        let mut count = 0;
129        loop {
130            let cmd = seg.vertex(&mut x, &mut y);
131            if is_stop(cmd) {
132                break;
133            }
134            count += 1;
135        }
136        assert!(
137            count > 10,
138            "With scale=10, square should have many vertices: got {count}"
139        );
140    }
141
142    #[test]
143    fn test_approximation_scale_accessors() {
144        let seg = ConvSegmentator::new(SquareSource::new());
145        assert!((seg.approximation_scale() - 1.0).abs() < 1e-10);
146
147        let mut seg = seg;
148        seg.set_approximation_scale(5.0);
149        assert!((seg.approximation_scale() - 5.0).abs() < 1e-10);
150    }
151}