Find every `points="..."` attribute in the SVG and, when the polyline has enough vertices, apply Ramer-Douglas-Peucker to remove redundant ones.
(svg: &mut String)
| 212 | /// Find every `points="..."` attribute in the SVG and, when the polyline has |
| 213 | /// enough vertices, apply Ramer-Douglas-Peucker to remove redundant ones. |
| 214 | fn simplify_svg_polylines(svg: &mut String) { |
| 215 | // MIN_POINTS: skip simplification for short polylines (axis ticks, error-bar |
| 216 | // caps, scatter markers); RDP overhead outweighs savings below this size. |
| 217 | // EPSILON: 0.5 px matches plotters' integer-pixel rounding granularity, so |
| 218 | // collinear-after-rounding vertices collapse without altering visible shape. |
| 219 | const MIN_POINTS: usize = 20; |
| 220 | const EPSILON: f64 = 0.5; |
| 221 | |
| 222 | let mut result = String::with_capacity(svg.len()); |
| 223 | let mut remaining = svg.as_str(); |
| 224 | let mut modified = false; |
| 225 | |
| 226 | while let Some(idx) = remaining.find("points=\"") { |
| 227 | let prefix_end = idx + 8; // length of `points="` |
| 228 | result.push_str(&remaining[..prefix_end]); |
| 229 | remaining = &remaining[prefix_end..]; |
| 230 | |
| 231 | if let Some(end) = remaining.find('"') { |
| 232 | let raw = &remaining[..end]; |
| 233 | // Cheap pre-check: count separators before parsing. A polyline with |
| 234 | // <MIN_POINTS vertices has <MIN_POINTS spaces between coord pairs. |
| 235 | let space_count = raw.bytes().filter(|&b| b == b' ').count(); |
| 236 | if space_count >= MIN_POINTS { |
| 237 | let points = parse_svg_points(raw); |
| 238 | if points.len() > MIN_POINTS { |
| 239 | let simplified = rdp_simplify(&points, EPSILON); |
| 240 | result.push_str(&format_svg_points(&simplified)); |
| 241 | modified = true; |
| 242 | } else { |
| 243 | result.push_str(raw); |
| 244 | } |
| 245 | } else { |
| 246 | result.push_str(raw); |
| 247 | } |
| 248 | result.push('"'); |
| 249 | remaining = &remaining[end + 1..]; |
| 250 | } |
| 251 | } |
| 252 | if modified { |
| 253 | result.push_str(remaining); |
| 254 | *svg = result; |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | fn parse_svg_points(s: &str) -> Vec<(f64, f64)> { |
| 259 | s.split_whitespace() |
no test coverage detected