1 | //! Approximate path length. |
2 | |
3 | use crate::geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment}; |
4 | use crate::path::PathEvent; |
5 | |
6 | use core::iter::IntoIterator; |
7 | |
8 | pub fn approximate_length<Iter>(path: Iter, tolerance: f32) -> f32 |
9 | where |
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 ] |
55 | fn 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 | |