1//! Approximate path length.
2
3use crate::geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment};
4use crate::path::PathEvent;
5
6use core::iter::IntoIterator;
7
8pub fn approximate_length<Iter>(path: Iter, tolerance: f32) -> f32
9where
10 Iter: IntoIterator<Item = PathEvent>,
11{
12 let tolerance = tolerance.max(1e-4);
13
14 let mut length = 0.0;
15
16 for evt in path.into_iter() {
17 match evt {
18 PathEvent::Line { from, to } => length += LineSegment { from, to }.length(),
19 PathEvent::Quadratic { from, ctrl, to } => {
20 length += QuadraticBezierSegment { from, ctrl, to }.length()
21 }
22 PathEvent::Cubic {
23 from,
24 ctrl1,
25 ctrl2,
26 to,
27 } => {
28 length += CubicBezierSegment {
29 from,
30 ctrl1,
31 ctrl2,
32 to,
33 }
34 .approximate_length(tolerance)
35 }
36 PathEvent::End {
37 last,
38 first,
39 close: true,
40 } => {
41 length += LineSegment {
42 from: last,
43 to: first,
44 }
45 .length()
46 }
47 _ => {}
48 }
49 }
50
51 length
52}
53
54#[test]
55fn approx_length() {
56 use crate::geom::point;
57
58 let mut builder: NoAttributes = crate::path::Path::builder();
59 builder.begin(at:point(x:0.0, y:0.0));
60 builder.line_to(point(x:1.0, y:0.0));
61 builder.line_to(point(x:1.0, y:1.0));
62 builder.line_to(point(x:0.0, y:1.0));
63 builder.end(close:true);
64
65 let path: Path = builder.build();
66
67 assert!((approximate_length(&path, 0.01) - 4.0).abs() < 0.0001);
68}
69