1use crate::geometry::{RingIntersection, RingIntersectionLookup};
2use alloc::{
3 collections::{BTreeMap, BTreeSet},
4 rc::Rc,
5 vec,
6 vec::Vec,
7};
8use core::{
9 cell::RefCell,
10 cmp::Ordering,
11 f64::consts::{PI, TAU},
12 mem::take,
13};
14use libm::atan2;
15use s2json::{BBox, FullXY, Point};
16
17#[derive(Debug)]
19pub struct PolyPath<P: FullXY> {
20 pub id: usize,
22 pub outer: Option<Vec<P>>,
24 pub old_outers: Vec<BBox>,
26 pub holes: Vec<Vec<P>>,
28 pub polys_consumed: BTreeSet<usize>,
30 pub bbox: BBox,
32}
33pub type PolyPathRef<P> = Rc<RefCell<PolyPath<P>>>;
35impl<P: FullXY> PolyPath<P> {
36 pub fn new_ref(
38 ring: Vec<P>,
39 polys_consumed: BTreeSet<usize>,
40 is_outer: bool,
41 bbox: Option<BBox>,
42 ) -> PolyPathRef<P> {
43 Rc::new(RefCell::new(PolyPath::new(ring, polys_consumed, is_outer, bbox)))
44 }
45
46 pub fn new(
48 ring: Vec<P>,
49 polys_consumed: BTreeSet<usize>,
50 is_outer: bool,
51 bbox: Option<BBox>,
52 ) -> Self {
53 let bbox = bbox.unwrap_or(BBox::from_linestring(&ring));
54 let mut outer = None;
55 let mut holes = vec![];
56 if is_outer {
57 outer = Some(ring);
58 } else {
59 holes.push(ring);
60 }
61 PolyPath { id: 0, outer, old_outers: vec![], holes, polys_consumed, bbox }
62 }
63
64 pub fn get_path(&mut self) -> Option<Vec<Vec<P>>> {
69 if self.outer.is_none() {
70 return None;
71 }
72 let outer = self.outer.as_mut().unwrap();
73 if outer.len() < 4 {
74 return None;
75 }
76 let mut res = vec![take(outer)];
77 for hole in self.holes.iter_mut() {
78 if hole.len() < 4 {
79 continue;
80 }
81 res.push(take(hole));
82 }
83 Some(res)
84 }
85}
86
87#[derive(Debug, Clone, PartialEq)]
89pub struct NextRingChunk<P: FullXY> {
90 pub int_point: Point,
92 pub chunk: RingChunkRef<P>,
94}
95
96#[derive(Debug, Clone, PartialEq)]
98pub struct RingChunk<P: FullXY> {
99 pub visted: bool,
101 pub poly_index: usize,
103 pub ring_index: usize,
105 pub bbox: BBox,
107 pub mid: Vec<P>,
109 pub from: Point,
111 pub to: Point,
113 pub next: Option<NextRingChunk<P>>,
116 pub from_angle: f64,
118 pub to_angle: f64,
120}
121pub type RingChunkRef<P> = Rc<RefCell<RingChunk<P>>>;
123impl<P: FullXY> RingChunk<P> {
124 pub fn new(
126 poly_index: usize,
127 ring_index: usize,
128 bbox: BBox,
129 mid: Vec<P>,
130 from: Point,
131 to: Point,
132 from_angle: f64,
133 to_angle: f64,
134 ) -> Rc<RefCell<Self>> {
135 Rc::new(RefCell::new(RingChunk {
136 visted: false,
137 poly_index,
138 ring_index,
139 bbox,
140 mid,
141 from,
142 to,
143 next: None,
144 from_angle,
145 to_angle,
146 }))
147 }
148
149 pub fn equal_chunk(&self, other: &RingChunkRef<P>) -> bool {
151 let other = &other.borrow();
152 (self.ring_index > 0) == (other.ring_index > 0)
153 && self.from == other.from
154 && self.to == other.to
155 && self.mid == other.mid
156 }
157}
158
159#[derive(Debug, Clone, PartialEq)]
161pub struct IntersectionPoint<P: FullXY> {
162 pub point: Point,
164 pub from: Vec<RingChunkRef<P>>,
166 pub to: Vec<RingChunkRef<P>>,
168}
169pub type IntersectionPointRef<P> = Rc<RefCell<IntersectionPoint<P>>>;
171impl<P: FullXY> IntersectionPoint<P> {
172 pub fn new(point: Point) -> IntersectionPointRef<P> {
174 Rc::new(RefCell::new(IntersectionPoint { point, from: vec![], to: vec![] }))
175 }
176}
177
178#[derive(Debug, Clone, PartialEq)]
180pub struct InterPointLookup<P: FullXY> {
181 pub lookup: BTreeMap<Point, IntersectionPointRef<P>>,
183}
184impl<P: FullXY> InterPointLookup<P> {
185 pub fn new() -> InterPointLookup<P> {
187 InterPointLookup { lookup: BTreeMap::new() }
188 }
189
190 pub fn get(&mut self, point: Point) -> IntersectionPointRef<P> {
192 self.lookup.entry(point).or_insert_with(|| IntersectionPoint::new(point)).clone()
193 }
194
195 pub fn link_ints(
197 &mut self,
198 poly_index: usize,
199 ring_index: usize,
200 from: Point,
201 to: Point,
202 mid: Vec<P>,
203 from_angle: Option<f64>,
204 to_angle: Option<f64>,
205 ) -> RingChunkRef<P> {
206 let bbox = BBox::from_linestring(&mid).merge(&BBox::from_linestring(&vec![from, to]));
208 let from_angle =
209 from_angle.unwrap_or(angle(&mid.last().map(|p| Point::from(p)).unwrap_or(from), &to));
210 let to_angle =
211 to_angle.unwrap_or(angle(&mid.first().map(|p| Point::from(p)).unwrap_or(to), &from));
212 let chunk =
213 RingChunk::new(poly_index, ring_index, bbox, mid, from, to, from_angle, to_angle);
214 self.get(from).borrow_mut().to.push(chunk.clone());
215 self.get(to).borrow_mut().from.push(chunk.clone());
216 chunk
217 }
218}
219
220pub fn build_paths_and_chunks<P: FullXY>(
229 vector_polygons: &[Vec<Vec<P>>],
230 ring_intersect_lookup: &mut RingIntersectionLookup,
231) -> (
232 Vec<PolyPathRef<P>>,
233 BTreeMap<usize, PolyPathRef<P>>,
234 Vec<RingChunkRef<P>>,
235 InterPointLookup<P>,
236 Vec<BBox>,
237) {
238 let mut paths: Vec<PolyPathRef<P>> = vec![];
240 let mut path_lookup: BTreeMap<usize, PolyPathRef<P>> = BTreeMap::new();
242 let mut outer_ring_bboxes: Vec<BBox> = vec![Default::default(); vector_polygons.len()];
244
245 let mut chunks: Vec<RingChunkRef<P>> = vec![];
248 let mut int_lookup = InterPointLookup::<P>::new();
249 for (p_i, poly) in vector_polygons.iter().enumerate() {
250 for (r_i, ring) in poly.iter().enumerate() {
251 let intersections = ring_intersect_lookup.get_mut(&p_i).and_then(|r| r.get_mut(&r_i));
252 if intersections.is_none() || intersections.as_ref().unwrap().len() == 0 {
254 if let Some(existing_path) = path_lookup.get(&p_i) {
255 let existing_path = &mut existing_path.borrow_mut();
256 if r_i == 0 {
257 existing_path.bbox.merge_in_place(&BBox::from_linestring(ring));
258 existing_path.outer = Some(ring.clone());
259 outer_ring_bboxes[p_i] = existing_path.bbox;
260 } else {
261 existing_path.holes.push(ring.clone());
262 }
263 } else {
264 let path =
265 PolyPath::new_ref(ring.clone(), BTreeSet::from([p_i]), r_i == 0, None);
266 if r_i == 0 {
267 outer_ring_bboxes[p_i] = path.borrow().bbox;
268 }
269 path_lookup.insert(p_i, path.clone());
270 paths.push(path);
271 }
272 continue;
273 }
274 if r_i == 0 {
276 outer_ring_bboxes[p_i] = BBox::from_linestring(ring);
277 }
278 let intersections: Vec<&RingIntersection> =
279 intersections.as_ref().unwrap().iter().filter(|i| i.t != 0.).collect();
280 let mut curr_index = 0;
281 let mut int_index = 0;
282 while curr_index < ring.len() - 1 {
283 if let Some(cur_inter) = intersections.get(int_index) {
285 if curr_index != cur_inter.from {
287 let start = curr_index;
288 while curr_index != cur_inter.from {
289 curr_index += 1;
290 }
291 let mid = (&ring[start + 1..curr_index]).to_vec();
292 let from = &ring[start];
293 let to = &ring[curr_index];
294 let chunk =
295 int_lookup.link_ints(p_i, r_i, from.into(), to.into(), mid, None, None);
296 chunks.push(chunk);
297 }
298 let mut from: Point = (&ring[curr_index]).into();
300 let mut cur_int = Some(cur_inter);
301 while let Some(c_i) = cur_int
302 && c_i.from == curr_index
303 {
304 if from != *c_i.point.borrow() {
309 chunks.push(int_lookup.link_ints(
310 p_i,
311 r_i,
312 from,
313 *c_i.point.borrow(),
314 vec![],
315 Some(invert_angle(c_i.t_angle)),
316 Some(c_i.t_angle),
317 ));
318 }
319 int_index += 1;
320 from = *c_i.point.borrow();
321 cur_int = intersections.get(int_index);
322 }
323 let next: Point = (&ring[curr_index + 1]).into();
325 if from != next {
326 let ang = intersections[int_index - 1].t_angle;
327 chunks.push(int_lookup.link_ints(
328 p_i,
329 r_i,
330 from,
331 next,
332 vec![],
333 Some(invert_angle(ang)),
334 Some(ang),
335 ));
336 }
337 } else {
338 chunks.push(int_lookup.link_ints(
340 p_i,
341 r_i,
342 (&ring[curr_index]).into(),
343 (&ring[curr_index + 1]).into(),
344 vec![],
345 None,
346 None,
347 ));
348 }
349 curr_index += 1;
350 }
351 }
352 }
353
354 chunks.sort_by(|a, b| {
356 let BBox { left: a_left, bottom: a_bottom, .. } = a.borrow().bbox;
357 let BBox { left: b_left, bottom: b_bottom, .. } = b.borrow().bbox;
358 a_left
359 .partial_cmp(&b_left)
360 .unwrap_or(Ordering::Equal)
361 .then(a_bottom.partial_cmp(&b_bottom).unwrap_or(Ordering::Equal))
362 });
363
364 (paths, path_lookup, chunks, int_lookup, outer_ring_bboxes)
365}
366
367#[derive(Debug)]
368struct IntPair<P: FullXY> {
369 from: RingChunkRef<P>,
370 to: RingChunkRef<P>,
371 angle: f64,
372}
373
374pub fn merge_intersection_pairs<P: FullXY>(intersection: &IntersectionPointRef<P>) {
379 let IntersectionPoint { from, to, point, .. } = &mut *intersection.borrow_mut();
380 let int_point = *point;
381 if from.len() == 0 || to.len() == 0 {
382 return;
383 }
384 if from.len() == 1 && to.len() == 1 {
385 from[0].borrow_mut().next = Some(NextRingChunk { chunk: to[0].clone(), int_point });
387 return;
388 }
389
390 let mut froms: Vec<RingChunkRef<P>> = vec![];
392 for c in from {
393 if c.borrow().visted {
394 continue;
395 }
396 if !froms.iter().any(|r| r.borrow().equal_chunk(c)) {
397 froms.push(c.clone());
398 } else {
399 c.borrow_mut().visted = true;
400 }
401 }
402 let mut tos: Vec<RingChunkRef<P>> = vec![];
403 for c in to {
404 if c.borrow().visted {
405 continue;
406 }
407 if !tos.iter().any(|r| r.borrow().equal_chunk(c)) {
408 tos.push(c.clone());
409 } else {
410 c.borrow_mut().visted = true;
411 }
412 }
413
414 let mut pairs: Vec<IntPair<P>> = vec![];
416 for f in froms.iter() {
417 for t in tos.iter() {
418 let mut angle = t.borrow().to_angle - f.borrow().from_angle;
419 angle = if angle < 0. { angle + TAU } else { angle };
420 pairs.push(IntPair { from: f.clone(), to: t.clone(), angle });
421 }
422 }
423 pairs.sort_by(|a, b| a.angle.partial_cmp(&b.angle).unwrap_or(Ordering::Equal));
424
425 for IntPair { from, to, .. } in &pairs {
426 let from = &mut from.borrow_mut();
427 if from.visted || to.borrow().visted {
428 continue;
429 }
430 from.next = Some(NextRingChunk { chunk: to.clone(), int_point });
431 from.visted = true;
432 to.borrow_mut().visted = true;
433 }
434
435 for f in froms {
437 f.borrow_mut().visted = false;
438 }
439 for t in tos {
440 t.borrow_mut().visted = false;
441 }
442}
443
444fn angle<P: FullXY, Q: FullXY>(a: &P, b: &Q) -> f64 {
453 atan2(a.y() - b.y(), a.x() - b.x())
454}
455
456fn invert_angle(angle: f64) -> f64 {
464 if angle >= 0. { angle - PI } else { angle + PI }
465}