1use crate::basics::VertexSource;
7use crate::conv_adaptor_vcgen::ConvAdaptorVcgen;
8use crate::math_stroke::{InnerJoin, LineJoin};
9use crate::vcgen_contour::VcgenContour;
10
11pub struct ConvContour<VS: VertexSource> {
19 base: ConvAdaptorVcgen<VS, VcgenContour>,
20}
21
22impl<VS: VertexSource> ConvContour<VS> {
23 pub fn new(source: VS) -> Self {
24 Self {
25 base: ConvAdaptorVcgen::new(source, VcgenContour::new()),
26 }
27 }
28
29 pub fn set_line_join(&mut self, lj: LineJoin) {
31 self.base.generator_mut().set_line_join(lj);
32 }
33 pub fn line_join(&self) -> LineJoin {
34 self.base.generator().line_join()
35 }
36
37 pub fn set_inner_join(&mut self, ij: InnerJoin) {
38 self.base.generator_mut().set_inner_join(ij);
39 }
40 pub fn inner_join(&self) -> InnerJoin {
41 self.base.generator().inner_join()
42 }
43
44 pub fn set_width(&mut self, w: f64) {
45 self.base.generator_mut().set_width(w);
46 }
47 pub fn width(&self) -> f64 {
48 self.base.generator().width()
49 }
50
51 pub fn set_miter_limit(&mut self, ml: f64) {
52 self.base.generator_mut().set_miter_limit(ml);
53 }
54 pub fn miter_limit(&self) -> f64 {
55 self.base.generator().miter_limit()
56 }
57
58 pub fn set_miter_limit_theta(&mut self, t: f64) {
59 self.base.generator_mut().set_miter_limit_theta(t);
60 }
61
62 pub fn set_inner_miter_limit(&mut self, ml: f64) {
63 self.base.generator_mut().set_inner_miter_limit(ml);
64 }
65 pub fn inner_miter_limit(&self) -> f64 {
66 self.base.generator().inner_miter_limit()
67 }
68
69 pub fn set_approximation_scale(&mut self, s: f64) {
70 self.base.generator_mut().set_approximation_scale(s);
71 }
72 pub fn approximation_scale(&self) -> f64 {
73 self.base.generator().approximation_scale()
74 }
75
76 pub fn set_auto_detect_orientation(&mut self, v: bool) {
77 self.base.generator_mut().set_auto_detect_orientation(v);
78 }
79 pub fn auto_detect_orientation(&self) -> bool {
80 self.base.generator().auto_detect_orientation()
81 }
82
83 pub fn source(&self) -> &VS {
84 self.base.source()
85 }
86
87 pub fn source_mut(&mut self) -> &mut VS {
88 self.base.source_mut()
89 }
90}
91
92impl<VS: VertexSource> VertexSource for ConvContour<VS> {
93 fn rewind(&mut self, path_id: u32) {
94 self.base.rewind(path_id);
95 }
96
97 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
98 self.base.vertex(x, y)
99 }
100}
101
102#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::basics::{is_stop, is_vertex, PATH_CMD_MOVE_TO};
110 use crate::path_storage::PathStorage;
111
112 fn collect_vertices<VS: VertexSource>(vs: &mut VS) -> Vec<(f64, f64, u32)> {
113 let mut result = Vec::new();
114 vs.rewind(0);
115 loop {
116 let (mut x, mut y) = (0.0, 0.0);
117 let cmd = vs.vertex(&mut x, &mut y);
118 if is_stop(cmd) {
119 break;
120 }
121 result.push((x, y, cmd));
122 }
123 result
124 }
125
126 #[test]
127 fn test_contour_empty_path() {
128 let path = PathStorage::new();
129 let mut contour = ConvContour::new(path);
130 let verts = collect_vertices(&mut contour);
131 assert!(verts.is_empty());
132 }
133
134 #[test]
135 fn test_contour_width() {
136 let path = PathStorage::new();
137 let mut contour = ConvContour::new(path);
138 contour.set_width(5.0);
139 assert!((contour.width() - 5.0).abs() < 1e-10);
140 }
141
142 #[test]
143 fn test_contour_closed_triangle() {
144 let mut path = PathStorage::new();
145 path.move_to(50.0, 10.0);
146 path.line_to(90.0, 90.0);
147 path.line_to(10.0, 90.0);
148 path.close_polygon(0);
149
150 let mut contour = ConvContour::new(path);
151 contour.set_width(5.0);
152 contour.set_auto_detect_orientation(true);
153 let verts = collect_vertices(&mut contour);
154
155 assert!(
156 verts.len() >= 3,
157 "Expected contour vertices, got {}",
158 verts.len()
159 );
160 assert_eq!(verts[0].2, PATH_CMD_MOVE_TO);
161 }
162
163 #[test]
164 fn test_contour_expands_polygon() {
165 let mut path = PathStorage::new();
166 path.move_to(20.0, 20.0);
167 path.line_to(80.0, 20.0);
168 path.line_to(80.0, 80.0);
169 path.line_to(20.0, 80.0);
170 path.close_polygon(0);
171
172 let mut contour = ConvContour::new(path);
173 contour.set_width(10.0);
174 contour.set_auto_detect_orientation(true);
175 let verts = collect_vertices(&mut contour);
176
177 let max_x = verts
178 .iter()
179 .filter(|v| is_vertex(v.2))
180 .map(|v| v.0)
181 .fold(f64::MIN, f64::max);
182 let min_x = verts
183 .iter()
184 .filter(|v| is_vertex(v.2))
185 .map(|v| v.0)
186 .fold(f64::MAX, f64::min);
187
188 assert!(max_x > 80.0, "Max x={} should exceed original 80", max_x);
189 assert!(
190 min_x < 20.0,
191 "Min x={} should be less than original 20",
192 min_x
193 );
194 }
195
196 #[test]
197 fn test_contour_auto_detect() {
198 let path = PathStorage::new();
199 let mut contour = ConvContour::new(path);
200 contour.set_auto_detect_orientation(true);
201 assert!(contour.auto_detect_orientation());
202 }
203}