poly2tri_rs/
points.rs

1use std::cmp::Ordering;
2
3use crate::shape::Point;
4
5/// Type alias to the underlying type for PointId.
6/// Despite of maximum number supported, type size also affect performance
7/// PointId compare is in hot path, e.g, triangle neighbor check, find edge index etc
8type NumType = u32;
9
10/// new type for point id, currently is the index in context
11#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
12pub struct PointId(pub(crate) NumType);
13
14impl PointId {
15    /// Get the inner value as usize
16    pub fn as_usize(&self) -> usize {
17        self.0 as usize
18    }
19
20    /// helper method used in the crate when I know the `PointId` is valid in `Points`
21    pub(crate) fn get(&self, points: &Points) -> Point {
22        unsafe { points.get_point_uncheck(*self) }
23    }
24}
25
26#[derive(Clone, Default)]
27pub struct PointsBuilder {
28    points: Vec<PointWithEdge>,
29}
30
31impl PointsBuilder {
32    pub fn with_capacity(cap: usize) -> Self {
33        Self {
34            points: Vec::with_capacity(cap),
35        }
36    }
37
38    /// Add a point
39    pub fn add_steiner_point(&mut self, point: Point) -> PointId {
40        let point_id = PointId(self.points.len() as NumType);
41        self.points.push(PointWithEdge {
42            point,
43            edges: PointEdges::None,
44        });
45        point_id
46    }
47
48    /// Add all `points`
49    pub fn add_steiner_points(&mut self, points: impl IntoIterator<Item = Point>) {
50        self.points
51            .extend(points.into_iter().map(|p| PointWithEdge {
52                point: p,
53                edges: PointEdges::None,
54            }));
55    }
56
57    pub(crate) fn get_point_mut(&mut self, point_id: PointId) -> Option<&mut PointWithEdge> {
58        self.points.get_mut(point_id.as_usize())
59    }
60
61    pub fn build(self) -> Points {
62        Points::new(self.points)
63    }
64}
65
66#[derive(Clone, Copy)]
67pub enum PointEdges {
68    None,
69    One(PointId),
70    Two(PointId, PointId),
71}
72
73impl PointEdges {
74    pub fn push(&mut self, point_id: PointId) {
75        *self = match self {
76            PointEdges::None => PointEdges::One(point_id),
77            PointEdges::One(p0) => PointEdges::Two(*p0, point_id),
78            PointEdges::Two(_, _) => panic!("one point only has two edges"),
79        };
80    }
81}
82
83#[derive(Clone, Copy)]
84pub struct PointWithEdge {
85    pub point: Point,
86    pub edges: PointEdges,
87}
88
89impl Iterator for PointEdges {
90    type Item = PointId;
91
92    fn next(&mut self) -> Option<Self::Item> {
93        let mut result = None;
94        let new_self = match std::mem::replace(self, PointEdges::None) {
95            PointEdges::None => PointEdges::None,
96            PointEdges::One(p) => {
97                result = Some(p);
98                PointEdges::None
99            }
100            PointEdges::Two(p0, p1) => {
101                result = Some(p0);
102                PointEdges::One(p1)
103            }
104        };
105        *self = new_self;
106        result
107    }
108}
109
110/// Point store
111#[derive(Clone)]
112pub struct Points {
113    points: Vec<PointWithEdge>,
114    y_sorted: Vec<PointId>,
115    pub head: PointId,
116    pub tail: PointId,
117}
118
119impl Points {
120    pub fn new(mut points: Vec<PointWithEdge>) -> Self {
121        let mut xmax = f64::MIN;
122        let mut xmin = f64::MAX;
123        let mut ymax = f64::MIN;
124        let mut ymin = f64::MAX;
125
126        let mut unsorted_points = points
127            .iter()
128            .enumerate()
129            .map(|(idx, p)| {
130                xmax = xmax.max(p.point.x);
131                xmin = xmin.min(p.point.x);
132                ymax = ymax.max(p.point.y);
133                ymin = ymin.min(p.point.y);
134                (PointId(idx as NumType), p.point)
135            })
136            .collect::<Vec<_>>();
137
138        // sort by y
139        unsorted_points.sort_by(|p1, p2| {
140            let p1 = p1.1;
141            let p2 = p2.1;
142
143            if p1.y < p2.y {
144                Ordering::Less
145            } else if p1.y == p2.y {
146                if p1.x < p2.x {
147                    Ordering::Less
148                } else {
149                    Ordering::Greater
150                }
151            } else {
152                Ordering::Greater
153            }
154        });
155        let sorted_ids = unsorted_points
156            .into_iter()
157            .map(|(idx, _)| idx)
158            .collect::<Vec<_>>();
159
160        let (head, tail) = {
161            let dx = (xmax - xmin) * 0.3;
162            let dy = (ymax - ymin) * 0.3;
163
164            let head = Point::new(xmin - dx, ymin - dy);
165            let head_id = PointId(points.len() as NumType);
166            points.push(PointWithEdge {
167                point: head,
168                edges: PointEdges::None,
169            });
170
171            let tail = Point::new(xmax + dx, ymin - dy);
172            let tail_id = PointId(points.len() as NumType);
173            points.push(PointWithEdge {
174                point: tail,
175                edges: PointEdges::None,
176            });
177            (head_id, tail_id)
178        };
179
180        Self {
181            points,
182            y_sorted: sorted_ids,
183            head,
184            tail,
185        }
186    }
187
188    pub fn len(&self) -> usize {
189        self.points.len()
190    }
191
192    /// get point for id
193    pub fn get_point(&self, point_id: PointId) -> Option<Point> {
194        self.points
195            .get(point_id.as_usize())
196            .map(|p| &p.point)
197            .cloned()
198    }
199
200    /// get point for id
201    pub unsafe fn get_point_uncheck(&self, point_id: PointId) -> Point {
202        unsafe { self.points.get_unchecked(point_id.as_usize()).point }
203    }
204
205    pub fn iter_point_by_y<'a>(
206        &'a self,
207        order: usize,
208    ) -> impl Iterator<Item = (PointId, Point, PointEdges)> + 'a {
209        self.y_sorted.iter().skip(order).map(|id| {
210            let point = unsafe { self.points.get_unchecked(id.as_usize()).clone() };
211            (*id, point.point, point.edges)
212        })
213    }
214
215    /// get point by y order
216    /// Not including head, tail
217    pub fn get_id_by_y(&self, order: usize) -> Option<PointId> {
218        self.y_sorted.get(order).cloned()
219    }
220
221    /// iter all points
222    pub fn iter(&self) -> impl Iterator<Item = (PointId, &Point, PointEdges)> {
223        self.points
224            .iter()
225            .enumerate()
226            .map(|(idx, p)| (PointId(idx as NumType), &p.point, p.edges))
227    }
228}