Skip to main content

agg_rust/
conv_dash.rs

1//! Dash converter for vertex sources.
2//!
3//! Port of `agg_conv_dash.h` — convenience wrapper that combines
4//! `ConvAdaptorVcgen` with `VcgenDash` to produce dashed lines.
5
6use crate::basics::VertexSource;
7use crate::conv_adaptor_vcgen::ConvAdaptorVcgen;
8use crate::vcgen_dash::VcgenDash;
9
10// ============================================================================
11// ConvDash
12// ============================================================================
13
14/// Dash converter: generates a dashed line from a continuous center-line path.
15///
16/// Port of C++ `conv_dash<VertexSource>`.
17pub struct ConvDash<VS: VertexSource> {
18    base: ConvAdaptorVcgen<VS, VcgenDash>,
19}
20
21impl<VS: VertexSource> ConvDash<VS> {
22    pub fn new(source: VS) -> Self {
23        Self {
24            base: ConvAdaptorVcgen::new(source, VcgenDash::new()),
25        }
26    }
27
28    pub fn remove_all_dashes(&mut self) {
29        self.base.generator_mut().remove_all_dashes();
30    }
31
32    pub fn add_dash(&mut self, dash_len: f64, gap_len: f64) {
33        self.base.generator_mut().add_dash(dash_len, gap_len);
34    }
35
36    pub fn dash_start(&mut self, ds: f64) {
37        self.base.generator_mut().dash_start(ds);
38    }
39
40    pub fn set_shorten(&mut self, s: f64) {
41        self.base.generator_mut().set_shorten(s);
42    }
43    pub fn shorten(&self) -> f64 {
44        self.base.generator().shorten()
45    }
46
47    pub fn source(&self) -> &VS {
48        self.base.source()
49    }
50
51    pub fn source_mut(&mut self) -> &mut VS {
52        self.base.source_mut()
53    }
54}
55
56impl<VS: VertexSource> VertexSource for ConvDash<VS> {
57    fn rewind(&mut self, path_id: u32) {
58        self.base.rewind(path_id);
59    }
60
61    fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
62        self.base.vertex(x, y)
63    }
64}
65
66// ============================================================================
67// Tests
68// ============================================================================
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate::basics::{is_stop, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO};
74    use crate::path_storage::PathStorage;
75
76    fn collect_vertices<VS: VertexSource>(vs: &mut VS) -> Vec<(f64, f64, u32)> {
77        let mut result = Vec::new();
78        vs.rewind(0);
79        loop {
80            let (mut x, mut y) = (0.0, 0.0);
81            let cmd = vs.vertex(&mut x, &mut y);
82            if is_stop(cmd) {
83                break;
84            }
85            result.push((x, y, cmd));
86        }
87        result
88    }
89
90    #[test]
91    fn test_dash_empty_path() {
92        let path = PathStorage::new();
93        let mut dash = ConvDash::new(path);
94        dash.add_dash(10.0, 5.0);
95        let verts = collect_vertices(&mut dash);
96        assert!(verts.is_empty());
97    }
98
99    #[test]
100    fn test_dash_basic_pattern() {
101        let mut path = PathStorage::new();
102        path.move_to(0.0, 0.0);
103        path.line_to(100.0, 0.0);
104
105        let mut dash = ConvDash::new(path);
106        dash.add_dash(20.0, 10.0);
107        let verts = collect_vertices(&mut dash);
108
109        assert!(!verts.is_empty(), "Should produce dash vertices");
110        assert_eq!(verts[0].2, PATH_CMD_MOVE_TO);
111    }
112
113    #[test]
114    fn test_dash_has_gaps() {
115        let mut path = PathStorage::new();
116        path.move_to(0.0, 0.0);
117        path.line_to(100.0, 0.0);
118
119        let mut dash = ConvDash::new(path);
120        dash.add_dash(20.0, 10.0);
121        let verts = collect_vertices(&mut dash);
122
123        let move_count = verts.iter().filter(|v| v.2 == PATH_CMD_MOVE_TO).count();
124        assert!(
125            move_count >= 2,
126            "Expected multiple dash segments, got {} move_to",
127            move_count
128        );
129    }
130
131    #[test]
132    fn test_dash_no_pattern_no_output() {
133        let mut path = PathStorage::new();
134        path.move_to(0.0, 0.0);
135        path.line_to(100.0, 0.0);
136
137        let mut dash = ConvDash::new(path);
138        // No add_dash called
139        let verts = collect_vertices(&mut dash);
140        assert!(verts.is_empty());
141    }
142
143    #[test]
144    fn test_dash_rewind_replay() {
145        let mut path = PathStorage::new();
146        path.move_to(0.0, 0.0);
147        path.line_to(100.0, 0.0);
148
149        let mut dash = ConvDash::new(path);
150        dash.add_dash(15.0, 5.0);
151        let v1 = collect_vertices(&mut dash);
152        let v2 = collect_vertices(&mut dash);
153        assert_eq!(v1.len(), v2.len());
154    }
155
156    #[test]
157    fn test_dash_line_count() {
158        let mut path = PathStorage::new();
159        path.move_to(0.0, 0.0);
160        path.line_to(100.0, 0.0);
161
162        let mut dash = ConvDash::new(path);
163        dash.add_dash(20.0, 10.0);
164        let verts = collect_vertices(&mut dash);
165
166        let line_count = verts.iter().filter(|v| v.2 == PATH_CMD_LINE_TO).count();
167        assert!(
168            line_count >= 3,
169            "Expected multiple line segments, got {}",
170            line_count
171        );
172    }
173}