poly2tri_rs/
loader.rs

1use crate::{Point, SweeperBuilder};
2
3#[derive(thiserror::Error, Debug)]
4pub enum LoaderError {
5    #[error("IO error")]
6    Io(#[from] std::io::Error),
7    #[error("Inner error")]
8    Inner(#[from] Box<dyn std::error::Error>),
9}
10
11/// Loader loads source to a [`Sweeper`].
12/// e.g: PlainFileLoader load from file path with same format defined by original 'poly2tri' project
13pub trait Loader {
14    fn load(&mut self, source: &str) -> Result<SweeperBuilder, LoaderError>;
15}
16
17/// Loaders can load data from file
18#[derive(Default)]
19pub struct PlainFileLoader {}
20
21#[derive(Default)]
22enum ParseState {
23    #[default]
24    Polygon,
25    Hole,
26    Steiner,
27}
28
29impl Loader for PlainFileLoader {
30    fn load(&mut self, path: &str) -> Result<SweeperBuilder, LoaderError> {
31        let mut f = std::fs::File::options().read(true).open(path)?;
32        let mut value = "".to_string();
33        std::io::Read::read_to_string(&mut f, &mut value).unwrap();
34
35        let mut state = ParseState::default();
36        let mut polygon = vec![];
37        let mut holes = Vec::<Vec<Point>>::new();
38        let mut steiner_points = Vec::<Point>::new();
39
40        for line in value.lines() {
41            if line.eq("HOLE") {
42                state = ParseState::Hole;
43                holes.push(vec![]);
44                continue;
45            } else if line.eq("STEINER") {
46                state = ParseState::Steiner;
47                continue;
48            }
49            let Some(point) = parse_point(line)? else {
50                continue
51            };
52
53            match state {
54                ParseState::Polygon => {
55                    polygon.push(point);
56                }
57                ParseState::Hole => {
58                    let current_hole = holes.last_mut().unwrap();
59                    current_hole.push(point);
60                }
61                ParseState::Steiner => {
62                    steiner_points.push(point);
63                }
64            }
65        }
66
67        Ok(SweeperBuilder::new(polygon)
68            .add_holes(holes)
69            .add_steiner_points(steiner_points))
70    }
71}
72
73fn parse_point(line: &str) -> Result<Option<Point>, LoaderError> {
74    if line.is_empty() {
75        return Ok(None);
76    }
77    let mut iter = line.split_whitespace();
78    let x = iter.next().unwrap();
79    let y = iter.next().unwrap();
80    let x = x.parse::<f64>().unwrap();
81    let y = y.parse::<f64>().unwrap();
82
83    Ok(Some(Point::new(x, y)))
84}