use crate::fixture_definition_editor::shape::Shape;
pub(crate) fn path_from_svg(svg: &str) -> Result<(Shape, usize), String> {
let outlines = gled_svg::ParsedSvg::parse(svg)
.map_err(|error| error.to_string())?
.outlines();
let longest = outlines
.iter()
.max_by(|a, b| a.length_mm().total_cmp(&b.length_mm()))
.ok_or("the drawing has no path")?;
let (mut low, mut high) = ([f32::MAX; 2], [f32::MIN; 2]);
for point in &longest.points_mm {
for axis in 0..2 {
low[axis] = low[axis].min(point[axis]);
high[axis] = high[axis].max(point[axis]);
}
}
let centre = [(low[0] + high[0]) / 2.0, (low[1] + high[1]) / 2.0];
let points = longest
.points_mm
.iter()
.map(|point| {
[
(point[0] - centre[0]) / 1000.0,
(point[1] - centre[1]) / 1000.0,
]
})
.collect();
let shape = Shape::Path {
points,
closed: longest.closed,
finished: true,
};
Ok((shape, outlines.len()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_svg_circle_becomes_a_closed_path_round_the_origin() {
let svg = r#"<svg xmlns="http://www.w3.org/2000/svg" width="200mm" height="200mm" viewBox="0 0 200 200"><path d="M 0 0 L 10 0" stroke="black"/><circle cx="120" cy="80" r="40" fill="none" stroke="black"/></svg>"#;
let (shape, paths) = path_from_svg(svg).expect("an SVG with paths");
assert_eq!(paths, 2);
let Shape::Path {
points,
closed,
finished,
} = shape
else {
panic!("a path");
};
assert!(closed && finished);
let reach = points
.iter()
.fold(0.0f32, |reach, p| reach.max(p[0].hypot(p[1])));
assert!((reach - 0.04).abs() < 0.001, "{reach}");
}
}