1use crate::basics::VertexSource;
7use crate::conv_adaptor_vcgen::ConvAdaptorVcgen;
8use crate::math_stroke::{InnerJoin, LineCap, LineJoin};
9use crate::vcgen_stroke::VcgenStroke;
10
11pub struct ConvStroke<VS: VertexSource> {
19 base: ConvAdaptorVcgen<VS, VcgenStroke>,
20}
21
22impl<VS: VertexSource> ConvStroke<VS> {
23 pub fn new(source: VS) -> Self {
24 Self {
25 base: ConvAdaptorVcgen::new(source, VcgenStroke::new()),
26 }
27 }
28
29 pub fn set_line_cap(&mut self, lc: LineCap) {
31 self.base.generator_mut().set_line_cap(lc);
32 }
33 pub fn line_cap(&self) -> LineCap {
34 self.base.generator().line_cap()
35 }
36
37 pub fn set_line_join(&mut self, lj: LineJoin) {
38 self.base.generator_mut().set_line_join(lj);
39 }
40 pub fn line_join(&self) -> LineJoin {
41 self.base.generator().line_join()
42 }
43
44 pub fn set_inner_join(&mut self, ij: InnerJoin) {
45 self.base.generator_mut().set_inner_join(ij);
46 }
47 pub fn inner_join(&self) -> InnerJoin {
48 self.base.generator().inner_join()
49 }
50
51 pub fn set_width(&mut self, w: f64) {
52 self.base.generator_mut().set_width(w);
53 }
54 pub fn width(&self) -> f64 {
55 self.base.generator().width()
56 }
57
58 pub fn set_miter_limit(&mut self, ml: f64) {
59 self.base.generator_mut().set_miter_limit(ml);
60 }
61 pub fn miter_limit(&self) -> f64 {
62 self.base.generator().miter_limit()
63 }
64
65 pub fn set_miter_limit_theta(&mut self, t: f64) {
66 self.base.generator_mut().set_miter_limit_theta(t);
67 }
68
69 pub fn set_inner_miter_limit(&mut self, ml: f64) {
70 self.base.generator_mut().set_inner_miter_limit(ml);
71 }
72 pub fn inner_miter_limit(&self) -> f64 {
73 self.base.generator().inner_miter_limit()
74 }
75
76 pub fn set_approximation_scale(&mut self, s: f64) {
77 self.base.generator_mut().set_approximation_scale(s);
78 }
79 pub fn approximation_scale(&self) -> f64 {
80 self.base.generator().approximation_scale()
81 }
82
83 pub fn set_shorten(&mut self, s: f64) {
84 self.base.generator_mut().set_shorten(s);
85 }
86 pub fn shorten(&self) -> f64 {
87 self.base.generator().shorten()
88 }
89
90 pub fn source(&self) -> &VS {
91 self.base.source()
92 }
93
94 pub fn source_mut(&mut self) -> &mut VS {
95 self.base.source_mut()
96 }
97}
98
99impl<VS: VertexSource> VertexSource for ConvStroke<VS> {
100 fn rewind(&mut self, path_id: u32) {
101 self.base.rewind(path_id);
102 }
103
104 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
105 self.base.vertex(x, y)
106 }
107}
108
109#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::basics::{is_stop, is_vertex, PATH_CMD_MOVE_TO};
117 use crate::path_storage::PathStorage;
118
119 fn collect_vertices<VS: VertexSource>(vs: &mut VS) -> Vec<(f64, f64, u32)> {
120 let mut result = Vec::new();
121 vs.rewind(0);
122 loop {
123 let (mut x, mut y) = (0.0, 0.0);
124 let cmd = vs.vertex(&mut x, &mut y);
125 if is_stop(cmd) {
126 break;
127 }
128 result.push((x, y, cmd));
129 }
130 result
131 }
132
133 #[test]
134 fn test_stroke_horizontal_line() {
135 let mut path = PathStorage::new();
136 path.move_to(0.0, 0.0);
137 path.line_to(100.0, 0.0);
138
139 let mut stroke = ConvStroke::new(path);
140 stroke.set_width(10.0);
141 let verts = collect_vertices(&mut stroke);
142
143 assert!(
144 verts.len() >= 4,
145 "Expected at least 4 stroke vertices, got {}",
146 verts.len()
147 );
148 assert_eq!(verts[0].2, PATH_CMD_MOVE_TO);
149 }
150
151 #[test]
152 fn test_stroke_triangle() {
153 let mut path = PathStorage::new();
154 path.move_to(10.0, 10.0);
155 path.line_to(90.0, 10.0);
156 path.line_to(50.0, 80.0);
157
158 let mut stroke = ConvStroke::new(path);
159 stroke.set_width(4.0);
160 let verts = collect_vertices(&mut stroke);
161
162 assert!(verts.len() >= 6, "Expected many stroke vertices");
163 }
164
165 #[test]
166 fn test_stroke_width() {
167 let path = PathStorage::new();
168 let mut stroke = ConvStroke::new(path);
169 stroke.set_width(5.0);
170 assert!((stroke.width() - 5.0).abs() < 1e-10);
171 }
172
173 #[test]
174 fn test_stroke_empty_path() {
175 let path = PathStorage::new();
176 let mut stroke = ConvStroke::new(path);
177 let verts = collect_vertices(&mut stroke);
178 assert!(verts.is_empty());
179 }
180
181 #[test]
182 fn test_stroke_round_cap() {
183 let mut path = PathStorage::new();
184 path.move_to(0.0, 0.0);
185 path.line_to(50.0, 0.0);
186
187 let mut stroke = ConvStroke::new(path);
188 stroke.set_width(20.0);
189 stroke.set_line_cap(LineCap::Round);
190 let verts = collect_vertices(&mut stroke);
191
192 assert!(
194 verts.len() > 10,
195 "Round cap stroke should have many vertices, got {}",
196 verts.len()
197 );
198 }
199
200 #[test]
201 fn test_stroke_y_extent() {
202 let mut path = PathStorage::new();
203 path.move_to(10.0, 50.0);
204 path.line_to(90.0, 50.0);
205
206 let mut stroke = ConvStroke::new(path);
207 stroke.set_width(20.0); let verts = collect_vertices(&mut stroke);
210 let max_y = verts
211 .iter()
212 .filter(|v| is_vertex(v.2))
213 .map(|v| v.1)
214 .fold(f64::MIN, f64::max);
215 let min_y = verts
216 .iter()
217 .filter(|v| is_vertex(v.2))
218 .map(|v| v.1)
219 .fold(f64::MAX, f64::min);
220
221 assert!(max_y >= 59.0, "Max y={} should be >= 59", max_y);
222 assert!(min_y <= 41.0, "Min y={} should be <= 41", min_y);
223 }
224
225 #[test]
226 fn test_stroke_rewind_replay() {
227 let mut path = PathStorage::new();
228 path.move_to(0.0, 0.0);
229 path.line_to(100.0, 0.0);
230
231 let mut stroke = ConvStroke::new(path);
232 stroke.set_width(4.0);
233 let v1 = collect_vertices(&mut stroke);
234 let v2 = collect_vertices(&mut stroke);
235 assert_eq!(v1.len(), v2.len());
236 }
237
238 #[test]
239 fn test_stroke_line_join_round() {
240 let mut path = PathStorage::new();
241 path.move_to(0.0, 0.0);
242 path.line_to(50.0, 0.0);
243 path.line_to(50.0, 50.0);
244
245 let mut miter = ConvStroke::new(&mut path);
246 miter.set_width(10.0);
247 miter.set_line_join(LineJoin::Miter);
248 let miter_verts = collect_vertices(&mut miter);
249
250 let mut round = ConvStroke::new(&mut path);
251 round.set_width(10.0);
252 round.set_line_join(LineJoin::Round);
253 let round_verts = collect_vertices(&mut round);
254
255 assert!(
257 round_verts.len() >= miter_verts.len(),
258 "Round ({}) should have >= vertices than miter ({})",
259 round_verts.len(),
260 miter_verts.len()
261 );
262 }
263}