1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//! Approximate path length.

use crate::geom::{LineSegment, CubicBezierSegment, QuadraticBezierSegment};
use crate::path::PathEvent;

use std::iter::IntoIterator;

pub fn approximate_length<Iter>(path: Iter, tolerance: f32) -> f32
where
    Iter: IntoIterator<Item = PathEvent>
{
    let tolerance = tolerance.max(1e-4);

    let mut length = 0.0;

    for evt in path.into_iter() {
        match evt {
            PathEvent::Line { from, to } => {
                length += LineSegment { from, to }.length()
            }
            PathEvent::Quadratic { from, ctrl, to } => {
                length += QuadraticBezierSegment { from, ctrl, to }.approximate_length(tolerance)
            }
            PathEvent::Cubic { from, ctrl1, ctrl2, to } => {
                length += CubicBezierSegment { from, ctrl1, ctrl2, to }.approximate_length(tolerance)
            }
            PathEvent::End { last, first, close: true } => {
                length += LineSegment { from: last, to: first }.length()
            }
            _ => {}
        }
    }

    length
}

#[test]
fn approx_length() {
    use crate::geom::point;

    let mut builder = crate::path::Path::builder();
    builder.begin(point(0.0, 0.0));
    builder.line_to(point(1.0, 0.0));
    builder.line_to(point(1.0, 1.0));
    builder.line_to(point(0.0, 1.0));
    builder.end(true);

    let path = builder.build();

    assert!((approximate_length(&path, 0.01) - 4.0).abs() < 0.0001);
}