dioxuscut_paths/
length.rs1use crate::parser::parse_path;
4use crate::types::Instruction;
5
6pub fn get_length(path: &str) -> f64 {
8 let instructions = match parse_path(path) {
9 Ok(insts) => insts,
10 Err(_) => return 0.0,
11 };
12
13 get_instructions_length(&instructions)
14}
15
16pub fn get_instructions_length(instructions: &[Instruction]) -> f64 {
18 let mut total_length = 0.0;
19 let mut current_x = 0.0;
20 let mut current_y = 0.0;
21 let mut start_x = 0.0;
22 let mut start_y = 0.0;
23
24 for inst in instructions {
25 match inst {
26 Instruction::MoveTo { x, y } => {
27 current_x = *x;
28 current_y = *y;
29 start_x = *x;
30 start_y = *y;
31 }
32 Instruction::LineTo { x, y } => {
33 let dx = x - current_x;
34 let dy = y - current_y;
35 total_length += (dx * dx + dy * dy).sqrt();
36 current_x = *x;
37 current_y = *y;
38 }
39 Instruction::ClosePath => {
40 let dx = start_x - current_x;
41 let dy = start_y - current_y;
42 total_length += (dx * dx + dy * dy).sqrt();
43 current_x = start_x;
44 current_y = start_y;
45 }
46 Instruction::CubicCurveTo {
47 x1,
48 y1,
49 x2,
50 y2,
51 x,
52 y,
53 } => {
54 total_length +=
55 cubic_bezier_length((current_x, current_y), (*x1, *y1), (*x2, *y2), (*x, *y));
56 current_x = *x;
57 current_y = *y;
58 }
59 Instruction::QuadCurveTo { x1, y1, x, y } => {
60 total_length += quad_bezier_length(current_x, current_y, *x1, *y1, *x, *y);
61 current_x = *x;
62 current_y = *y;
63 }
64 }
65 }
66
67 total_length
68}
69
70fn cubic_bezier_length(
71 (x0, y0): (f64, f64),
72 (x1, y1): (f64, f64),
73 (x2, y2): (f64, f64),
74 (x3, y3): (f64, f64),
75) -> f64 {
76 let steps = 16;
77 let mut length = 0.0;
78 let mut prev_x = x0;
79 let mut prev_y = y0;
80
81 for i in 1..=steps {
82 let t = i as f64 / steps as f64;
83 let mt = 1.0 - t;
84
85 let px =
86 mt * mt * mt * x0 + 3.0 * mt * mt * t * x1 + 3.0 * mt * t * t * x2 + t * t * t * x3;
87
88 let py =
89 mt * mt * mt * y0 + 3.0 * mt * mt * t * y1 + 3.0 * mt * t * t * y2 + t * t * t * y3;
90
91 let dx = px - prev_x;
92 let dy = py - prev_y;
93 length += (dx * dx + dy * dy).sqrt();
94
95 prev_x = px;
96 prev_y = py;
97 }
98
99 length
100}
101
102fn quad_bezier_length(x0: f64, y0: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
103 let steps = 16;
104 let mut length = 0.0;
105 let mut prev_x = x0;
106 let mut prev_y = y0;
107
108 for i in 1..=steps {
109 let t = i as f64 / steps as f64;
110 let mt = 1.0 - t;
111
112 let px = mt * mt * x0 + 2.0 * mt * t * x1 + t * t * x2;
113 let py = mt * mt * y0 + 2.0 * mt * t * y1 + t * t * y2;
114
115 let dx = px - prev_x;
116 let dy = py - prev_y;
117 length += (dx * dx + dy * dy).sqrt();
118
119 prev_x = px;
120 prev_y = py;
121 }
122
123 length
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_rect_path_length() {
132 let d = "M 0 0 L 100 0 L 100 100 L 0 100 Z";
133 let len = get_length(d);
134 assert_eq!(len, 400.0);
135 }
136}