Skip to main content

map_tile_toolkit/
polygon.rs

1//! Polygon slicing for [`Slicer`](crate::Slicer): [`Slicer::polygon`](crate::Slicer::polygon)
2//! yields every piece of a polygon in every tile it reaches, and
3//! [`Slicer::tile_polygon`](crate::Slicer::tile_polygon) its piece in one tile.
4//!
5//! A polygon is sliced lazily by a row-by-row scanline (see `docs/polygon-slicer.md` §6/§8):
6//!
7//! 1. **Prepare** — one pass over the polygon's rings, borrowing them in place: drop degenerate rings,
8//!    take the bounding box, and record the `y`-range of every run of [`CHUNK`] consecutive edges. A
9//!    polygon inside one tile's inner box is yielded whole, without the rest.
10//! 2. **Scan rows** top to bottom. A row looks only at the chunks whose `y`-range reaches it: their
11//!    edges that touch the row's buffered slab give the row's tile spans, and those crossing the row's
12//!    center line `y = cy` give its crossings. The center line carries the winding ray of every tile in
13//!    the row, so the crossings answer both remaining questions: how far an excursion outside a tile
14//!    winds around it (the detour's shape), and whether a ring with no edge in a tile contains it.
15//! 3. **Sweep** the row left to right over its edge tiles, closing every touching ring's arcs with the
16//!    shared [`close_ring`] core, and yielding the covered spans between them as [`FillRun`]s. A
17//!    Fenwick tree over the row's crossings (in edge order) answers "signed crossings right of this
18//!    tile among edges `i..j`" in `O(log n)`.
19//!
20//! Nothing outside the current row is kept, and a piece is built in one reused buffer, in the global
21//! frame: [`Local`] moves it into the tile's frame as it is read.
22
23use core::cmp::Ordering;
24use core::ops::Range;
25
26use geo_types::Coord;
27
28use crate::TileError;
29use crate::clip_polygon::{
30    ClipScratch, RingClip, clip_ring, close_ring, fill_box, keep_edge, push_distinct, push_fill_box,
31};
32use crate::clip_polyline::to_local;
33use crate::geom::{crossing_x_ceil, ring_orientation};
34use crate::grid::{Grid, Seg};
35use crate::slicer::Local;
36use crate::tile::TileId;
37use crate::vertex::{PolyVertex, Vertex};
38
39/// Edges per chunk of the row index: a chunk's `y`-range decides which rows look at its edges.
40const CHUNK: usize = 32;
41
42/// How many tiles of one row an edge must touch to be kept as a single [`Span`] for the row rather
43/// than one [`Hit`] per tile.
44const LONG_EDGE: i64 = 4;
45
46/// A horizontal run of tiles lying **entirely inside** the polygon: row `y`, columns `x.start..x.end`.
47///
48/// No ring edge touches these tiles' buffered boxes, so each is uniformly covered: in each of them,
49/// [`Slicer::tile_polygon`](crate::Slicer::tile_polygon) yields this polygon as a single
50/// all-synthetic box ring around the buffered box, which [`PolygonSlices::fill_ring`] (or
51/// [`Slicer::fill_ring`](crate::Slicer::fill_ring)) returns — the same tile-local ring for every tile
52/// of the run. Runs are maximal, and never overlap the polygon's edge tiles.
53#[derive(Debug, Clone, PartialEq, Eq, Hash)]
54#[non_exhaustive]
55pub struct FillRun {
56    /// The tile row.
57    pub y: i32,
58    /// The tile columns, end-exclusive.
59    pub x: Range<i32>,
60    /// The polygon's exterior winding, which its fill ring follows.
61    orient: Ordering,
62}
63
64/// One slice of a polygon.
65#[derive(Debug)]
66pub enum PolygonSlice<'a, V, R> {
67    /// One polygon's piece in one tile.
68    Piece(PolygonPiece<'a, V, R>),
69    /// Tiles a polygon covers entirely, whose content is [`PolygonSlices::fill_ring`] in each.
70    Run(FillRun),
71}
72
73/// One polygon's rings in one tile: exterior first, then its holes that reach the tile.
74#[derive(Debug)]
75pub struct PolygonPiece<'a, V, R> {
76    tile: TileId,
77    rings: PieceRings<'a, V, R>,
78}
79
80#[derive(Debug)]
81enum PieceRings<'a, V, R> {
82    /// Clipped rings in the piece buffer (global, each closed), ending at `ends`, and the tile
83    /// origin.
84    Built {
85        verts: &'a [V],
86        ends: &'a [u32],
87        origin: Coord<i32>,
88    },
89    /// A polygon lying wholly in the tile: its kept rings, lent from the input (or the copy of a ring
90    /// that had duplicate vertices).
91    Whole {
92        rings: &'a [RingInfo],
93        input: &'a [R],
94        copies: &'a [V],
95        origin: Coord<i32>,
96    },
97}
98
99// Borrowed views: `Clone`/`Copy` without requiring them of the vertex or ring types.
100impl<V, R> Clone for PieceRings<'_, V, R> {
101    fn clone(&self) -> Self {
102        *self
103    }
104}
105impl<V, R> Copy for PieceRings<'_, V, R> {}
106impl<V, R> Clone for PolygonPiece<'_, V, R> {
107    fn clone(&self) -> Self {
108        *self
109    }
110}
111impl<V, R> Copy for PolygonPiece<'_, V, R> {}
112
113impl<'a, V: Vertex, R: AsRef<[V]>> PolygonPiece<'a, V, R> {
114    /// The tile this piece belongs to.
115    #[must_use]
116    pub fn tile(&self) -> TileId {
117        self.tile
118    }
119
120    /// The piece's rings, exterior first, each closed and in the tile's local frame.
121    pub fn rings(&self) -> impl Iterator<Item = Local<'a, V>> + use<'a, V, R> {
122        let rings = self.rings;
123        let n = match rings {
124            PieceRings::Built { ends, .. } => ends.len(),
125            PieceRings::Whole { rings, .. } => rings.len(),
126        };
127        (0..n).map(move |i| match rings {
128            PieceRings::Built {
129                verts,
130                ends,
131                origin,
132            } => {
133                let start = i.checked_sub(1).map_or(0, |p| ends[p] as usize);
134                Local::ring(&verts[start..ends[i] as usize], origin, true)
135            }
136            PieceRings::Whole {
137                rings,
138                input,
139                copies,
140                origin,
141            } => Local::ring(rings[i].pts(input, copies), origin, false),
142        })
143    }
144}
145
146/// Where one kept ring's distinct vertices are.
147#[derive(Debug, Clone, Copy)]
148enum Src {
149    /// The first `len` vertices of the input ring.
150    Input,
151    /// `copies[start..start + len]`, the ring with its duplicate vertices dropped.
152    Copy(usize),
153}
154
155/// One kept ring (at least three distinct vertices) of the polygon being sliced: ring 0 is its
156/// exterior. Edge `i` runs from vertex `i` to vertex `(i + 1) % len`, and is edge `edge0 + i` of the
157/// polygon.
158#[derive(Debug, Clone, Copy)]
159struct RingInfo {
160    input: u32,
161    src: Src,
162    len: u32,
163    edge0: u32,
164}
165
166impl RingInfo {
167    fn pts<'a, V, R: AsRef<[V]>>(&self, input: &'a [R], copies: &'a [V]) -> &'a [V] {
168        let len = self.len as usize;
169        match self.src {
170            Src::Input => &input[self.input as usize].as_ref()[..len],
171            Src::Copy(start) => &copies[start..start + len],
172        }
173    }
174}
175
176/// [`CHUNK`] consecutive edges of one ring, and the tile rows their buffered `y`-range reaches.
177#[derive(Debug, Clone, Copy)]
178struct Chunk {
179    ring: u32,
180    first: u32,
181    count: u32,
182    rows: (i64, i64),
183}
184
185/// A ring edge touching tile `tx` of the current row. An edge touching fewer than [`LONG_EDGE`]
186/// tiles there has one hit per tile, so a tile's hits are one contiguous run, used as is.
187#[derive(Debug, Clone, Copy)]
188struct Hit {
189    tx: i32,
190    edge: u32,
191    ring: u32,
192}
193
194/// A long edge touching tiles `first..=last` of the current row: one entry for the row, so a row
195/// costs memory per edge, never per tile, however far an edge runs along it.
196#[derive(Debug, Clone, Copy)]
197struct Span {
198    first: i32,
199    last: i32,
200    edge: u32,
201    ring: u32,
202}
203
204/// A ring edge crossing the current row's center line, the line of every winding ray in that row
205/// (half-open in `y`, so each crossing is counted once). The crossing lies right of tile `tx`'s
206/// buffered box — on its winding ray — iff `tx <= right_of`.
207#[derive(Debug, Clone, Copy)]
208struct Crossing {
209    edge: u32,
210    ring: u32,
211    up: bool,
212    right_of: i64,
213}
214
215impl Crossing {
216    fn sign(self) -> i64 {
217        if self.up { 1 } else { -1 }
218    }
219}
220
221/// Row `ty`'s center line: the midpoint of its buffered box `[ty·e − b, ty·e + e − 1 + b]`, rounded
222/// toward zero, as the clip computes it with `midpoint` — in closed form: the buffer cancels, and the
223/// sum `2·ty·e + e − 1` is negative exactly when `ty < 0`, where rounding toward zero rounds up.
224fn row_center(ty: i64, extent: i64) -> i64 {
225    ty * extent + if ty < 0 { extent / 2 } else { (extent - 1) / 2 }
226}
227
228/// Rebuild `tree` as a Fenwick (binary indexed) tree over `values`, in `O(n)`.
229fn fenwick_build(tree: &mut Vec<i64>, values: impl Iterator<Item = i64>) {
230    tree.clear();
231    tree.extend(values);
232    for i in 0..tree.len() {
233        let parent = i | (i + 1);
234        if parent < tree.len() {
235            tree[parent] += tree[i];
236        }
237    }
238}
239
240/// Add `delta` at position `i`.
241fn fenwick_add(tree: &mut [i64], mut i: usize, delta: i64) {
242    while i < tree.len() {
243        tree[i] += delta;
244        i |= i + 1;
245    }
246}
247
248/// Sum of the first `end` positions.
249fn fenwick_prefix(tree: &[i64], mut end: usize) -> i64 {
250    let mut sum = 0;
251    while end > 0 {
252        sum += tree[end - 1];
253        end &= end - 1;
254    }
255    sum
256}
257
258/// Sort one row's `hits` by `(tx, edge)`. They arrive in ascending edge order, so a stable counting
259/// sort by column finishes the job in `O(n + columns)`; a column range much wider than the hits are
260/// many falls back to a comparison sort. `tmp` and `counts` are reused scratch.
261fn sort_hits(hits: &mut [Hit], tmp: &mut Vec<Hit>, counts: &mut Vec<usize>) {
262    let Some(head) = hits.first() else {
263        return;
264    };
265    let (lo, hi) = hits.iter().fold((head.tx, head.tx), |(lo, hi), h| {
266        (lo.min(h.tx), hi.max(h.tx))
267    });
268    let columns = hi.abs_diff(lo) as usize + 1;
269    if columns == 1 {
270        // One column: already in edge order.
271    } else if hits.len() < 16 || columns > hits.len().saturating_mul(4) {
272        hits.sort_unstable_by_key(|h| (h.tx, h.edge));
273    } else {
274        counts.clear();
275        counts.resize(columns, 0);
276        for h in hits.iter() {
277            counts[h.tx.abs_diff(lo) as usize] += 1;
278        }
279        // Exclusive prefix sums: where each column starts.
280        let mut start = 0;
281        for c in counts.iter_mut() {
282            (*c, start) = (start, start + *c);
283        }
284        tmp.clear();
285        tmp.extend_from_slice(hits);
286        for h in tmp.iter() {
287            let at = &mut counts[h.tx.abs_diff(lo) as usize];
288            hits[*at] = *h;
289            *at += 1;
290        }
291    }
292    debug_assert!(hits.is_sorted_by_key(|h| (h.tx, h.edge)));
293}
294
295/// Check that freshly written vertices fit the tile-local frame of `origin`, as [`Local`] lends them.
296fn check_local<V: Vertex>(verts: &[V], origin: Coord<i32>) -> Result<(), TileError> {
297    for &v in verts {
298        to_local(v, origin)?;
299    }
300    Ok(())
301}
302
303/// Restore edge order to `spans`, whose first `kept` are in edge order and the rest were appended (in
304/// first-column order, so in edge order too when they all start in the same column): sort the
305/// appended part only if needed, then merge the two in one pass through `tmp`.
306fn merge_by_edge<T: Copy>(
307    spans: &mut Vec<T>,
308    kept: usize,
309    tmp: &mut Vec<T>,
310    edge: impl Fn(&T) -> u32,
311) {
312    let added = &mut spans[kept..];
313    if !added.is_sorted_by_key(&edge) {
314        added.sort_unstable_by_key(&edge);
315    }
316    if kept == 0 || kept == spans.len() || edge(&spans[kept - 1]) < edge(&spans[kept]) {
317        return;
318    }
319    tmp.clear();
320    let (old, new) = spans.split_at(kept);
321    let (mut i, mut j) = (0, 0);
322    while i < old.len() && j < new.len() {
323        if edge(&old[i]) < edge(&new[j]) {
324            tmp.push(old[i]);
325            i += 1;
326        } else {
327            tmp.push(new[j]);
328            j += 1;
329        }
330    }
331    tmp.extend_from_slice(&old[i..]);
332    tmp.extend_from_slice(&new[j..]);
333    core::mem::swap(spans, tmp);
334}
335
336/// `hits` (the spans covering one tile, in edge order, so each ring's are contiguous) split into runs
337/// of the same ring.
338fn ring_groups(mut hits: &[Hit]) -> impl Iterator<Item = &[Hit]> {
339    core::iter::from_fn(move || {
340        let ring = hits.first()?.ring;
341        let (group, rest) = hits.split_at(hits.partition_point(|x| x.ring == ring));
342        hits = rest;
343        Some(group)
344    })
345}
346
347/// Signed crossings, right of the current tile, of `ring`'s `count` edges from local edge `first`
348/// (cyclically): the row's still-active crossings in that edge range, summed by the Fenwick tree.
349fn excursion_winding(
350    cs: &[Crossing],
351    fenwick: &[i64],
352    ring: RingInfo,
353    first: usize,
354    count: usize,
355) -> i64 {
356    let (start, len) = (ring.edge0 as usize, ring.len as usize);
357    let sum = |lo: usize, hi: usize| {
358        let i = cs.partition_point(|c| (c.edge as usize) < lo);
359        let j = cs.partition_point(|c| (c.edge as usize) < hi);
360        fenwick_prefix(fenwick, j) - fenwick_prefix(fenwick, i)
361    };
362    if first + count <= len {
363        sum(start + first, start + first + count)
364    } else {
365        sum(start + first, start + len) + sum(start, start + first + count - len)
366    }
367}
368
369/// Reusable working memory for polygon slicing, kept in the [`Slicer`](crate::Slicer) between
370/// features (see [`Scratch`]): only its capacity survives a call.
371pub(crate) struct PolyScratch<V> {
372    /// The current polygon's kept rings, copies of rings that had duplicate vertices, and chunks.
373    rings: Vec<RingInfo>,
374    copies: Vec<V>,
375    chunks: Vec<Chunk>,
376    /// Chunk ids by first row, and the chunks reaching the current row (ascending, so edge order).
377    order: Vec<u32>,
378    active: Vec<u32>,
379    /// The current row: its per-tile spans of short edges (by column, then edge), the spans of its
380    /// long edges (by first column, then edge), the long ones covering the sweep's current tile (by
381    /// edge), and a tile's merged spans when a long edge crosses it. Then its crossings (by edge), the
382    /// crossings by `right_of`, and the Fenwick tree of the signs of those still right of the sweep.
383    hits: Vec<Hit>,
384    long: Vec<Span>,
385    long_cover: Vec<Span>,
386    covering: Vec<Hit>,
387    crossings: Vec<Crossing>,
388    by_x: Vec<u32>,
389    /// Sorting scratch for the row's spans.
390    hits_tmp: Vec<Hit>,
391    spans_tmp: Vec<Span>,
392    counts: Vec<usize>,
393    fenwick: Vec<i64>,
394    /// Per ring: odd crossings right of the current tile.
395    inside: Vec<bool>,
396    /// The piece being yielded: tile-local ring vertices and where each ring ends.
397    verts: Vec<V>,
398    ring_ends: Vec<u32>,
399    arcs: Vec<(usize, usize)>,
400    clip: ClipScratch<V>,
401}
402
403impl<V> PolyScratch<V> {
404    pub(crate) const fn new() -> Self {
405        Self {
406            rings: Vec::new(),
407            copies: Vec::new(),
408            chunks: Vec::new(),
409            order: Vec::new(),
410            active: Vec::new(),
411            hits: Vec::new(),
412            long: Vec::new(),
413            long_cover: Vec::new(),
414            covering: Vec::new(),
415            crossings: Vec::new(),
416            by_x: Vec::new(),
417            hits_tmp: Vec::new(),
418            spans_tmp: Vec::new(),
419            counts: Vec::new(),
420            fenwick: Vec::new(),
421            inside: Vec::new(),
422            verts: Vec::new(),
423            ring_ends: Vec::new(),
424            arcs: Vec::new(),
425            clip: ClipScratch::new(),
426        }
427    }
428}
429
430/// The [`Slicer`](crate::Slicer)'s polygon working memory, allocated by its first polygon, so a
431/// slicer used only for lines and points never builds it. It holds nothing between calls that affects
432/// results: it compares equal, clones empty, and only its capacity survives a call.
433pub(crate) struct Scratch<V>(Option<Box<PolyState<V>>>);
434
435/// The working memory, and the state of the current polygon's sweep (kept out of the iterator, so the
436/// iterator stays small to move).
437pub(crate) struct PolyState<V> {
438    pub(crate) scratch: PolyScratch<V>,
439    sweep: Option<Sweep>,
440}
441
442impl<V> Scratch<V> {
443    pub(crate) const fn new() -> Self {
444        Self(None)
445    }
446
447    /// The working memory, allocated on first use.
448    #[inline]
449    pub(crate) fn get(&mut self) -> &mut PolyState<V> {
450        #[cold]
451        fn alloc<V>() -> Box<PolyState<V>> {
452            Box::new(PolyState {
453                scratch: PolyScratch::new(),
454                sweep: None,
455            })
456        }
457        self.0.get_or_insert_with(alloc)
458    }
459}
460
461impl<V> Clone for Scratch<V> {
462    fn clone(&self) -> Self {
463        Self::new()
464    }
465}
466
467impl<V> PartialEq for Scratch<V> {
468    fn eq(&self, _: &Self) -> bool {
469        true
470    }
471}
472
473impl<V> Eq for Scratch<V> {}
474
475impl<V> core::fmt::Debug for Scratch<V> {
476    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
477        f.debug_struct("PolyScratch").finish_non_exhaustive()
478    }
479}
480
481/// The fill ring of `run` on `grid`: tile (0, 0)'s buffered box `[-buffer, extent − 1 + buffer]`
482/// grown by one unit, wound like the run's polygon — the same in every tile's local frame. It fits
483/// `i32` because [`Grid::new`] keeps `extent + buffer` within it.
484pub(crate) fn fill_ring<V: PolyVertex>(grid: Grid, run: &FillRun) -> [V; 5] {
485    let (extent, buffer) = (grid.extent_i32(), grid.buffer_i32());
486    let sw = Coord {
487        x: -buffer - 1,
488        y: -buffer - 1,
489    };
490    let ne = Coord {
491        x: extent + buffer,
492        y: extent + buffer,
493    };
494    fill_box(run.orient, sw, ne).map(V::synthetic_at)
495}
496
497/// What the next slice is; the borrowed view is built from it.
498enum Event {
499    /// The current polygon lies wholly in `tile`'s inner box.
500    Whole {
501        tile: TileId,
502        origin: Coord<i32>,
503    },
504    /// A piece built into the piece buffer, for the tile at `origin`.
505    Built {
506        tile: TileId,
507        origin: Coord<i32>,
508    },
509    Run(FillRun),
510}
511
512/// The row sweep of the current polygon.
513struct Sweep {
514    /// The next row to load, and the last row.
515    row: i64,
516    last_row: i64,
517    /// Chunks not yet active, as an index into the by-first-row order.
518    next_chunk: usize,
519    /// Whether a row is loaded, and the sweep's place in it: the next crossing (by `right_of`), the
520    /// next short-edge span, the next long-edge span, and the first tile not yet yielded or filled.
521    in_row: bool,
522    ty: i32,
523    k: usize,
524    h: usize,
525    lh: usize,
526    next: i64,
527    /// The spans of the current tile: its short-edge spans as they are (`Some`, the usual case: no
528    /// copy), or merged with the long edges crossing it into the scratch's `covering` (`None`).
529    cover: Option<Range<usize>>,
530    /// The last column a long edge covering the sweep reaches (`i64::MIN` when none does).
531    long_last: i64,
532    /// How many holes have odd parity right of the sweep.
533    holes_in: u32,
534    /// A covered span of the current row not yet yielded (it may still grow), as `start..end`.
535    pending: Option<(i64, i64)>,
536    /// A run to yield before anything else, and an edge tile to build after it (from the spans
537    /// covering it).
538    ready: Option<FillRun>,
539    tile_ready: Option<i32>,
540    /// The exterior's winding, once a fill needs it.
541    orient: Option<Ordering>,
542}
543
544/// Lazy iterator over the slices of a polygon in every tile it reaches; see
545/// [`Slicer::polygon`](crate::Slicer::polygon).
546///
547/// A **lending** iterator: a [`PolygonPiece`] borrows the iterator's piece buffer, so it is valid
548/// until the next call to [`next`](Self::next). Loop with `while let Some(slice) = slices.next()`.
549pub struct PolygonSlices<'s, V, R> {
550    grid: Grid,
551    rings: &'s [R],
552    s: &'s mut PolyScratch<V>,
553    /// How the polygon starts, until the first step; then its sweep, if it has one.
554    start: Option<Start>,
555    sweep: &'s mut Option<Sweep>,
556    done: bool,
557}
558
559/// How a loaded polygon starts: small, so the iterator can be built in place, its sweep only on the
560/// first step.
561enum Start {
562    /// It lies wholly in `tile`'s inner box.
563    Whole { tile: TileId, origin: Coord<i32> },
564    /// It is swept over rows `row..=last_row`.
565    Sweep { row: i64, last_row: i64 },
566}
567
568impl<'s, V: PolyVertex, R: AsRef<[V]>> PolygonSlices<'s, V, R> {
569    /// Load the polygon `rings`, bounding and indexing it.
570    #[cfg_attr(feature = "hotpath", hotpath::measure)]
571    #[inline]
572    pub(crate) fn new(
573        grid: Grid,
574        rings: &'s [R],
575        state: &'s mut PolyState<V>,
576    ) -> Result<Self, TileError> {
577        let PolyState { scratch: s, sweep } = state;
578        *sweep = None;
579        let start = prepare(grid, rings, s)?;
580        Ok(Self {
581            grid,
582            rings,
583            s,
584            start,
585            sweep,
586            done: false,
587        })
588    }
589
590    /// The next slice, or `None` when the polygon is done. An error ends the iteration: only
591    /// [`TileError::GeometryTooLarge`], for a piece of more than `u32::MAX` vertices, since
592    /// [`Slicer::polygon`](crate::Slicer::polygon) settled every overflow up front.
593    #[expect(
594        clippy::should_implement_trait,
595        reason = "a lending iterator: each item borrows the iterator"
596    )]
597    pub fn next(&mut self) -> Option<Result<PolygonSlice<'_, V, R>, TileError>> {
598        if self.done {
599            return None;
600        }
601        match self.step() {
602            Ok(Some(event)) => Some(Ok(self.view(event))),
603            Ok(None) => {
604                self.done = true;
605                None
606            }
607            Err(e) => {
608                self.done = true;
609                Some(Err(e))
610            }
611        }
612    }
613
614    /// The ring [`Slicer::tile_polygon`](crate::Slicer::tile_polygon) yields for `run`'s polygon in
615    /// each tile of the run: the tile's buffered box grown by one unit, tile-local and closed (its
616    /// first vertex repeated), all synthetic vertices, wound like the polygon's exterior. It is the
617    /// polygon's only ring there. The same as [`Slicer::fill_ring`](crate::Slicer::fill_ring), for use
618    /// while the iterator borrows the slicer.
619    #[must_use]
620    pub fn fill_ring(&self, run: &FillRun) -> [V; 5] {
621        fill_ring(self.grid, run)
622    }
623
624    fn view(&self, event: Event) -> PolygonSlice<'_, V, R> {
625        let (tile, rings) = match event {
626            Event::Run(run) => return PolygonSlice::Run(run),
627            Event::Built { tile, origin } => (
628                tile,
629                PieceRings::Built {
630                    verts: &self.s.verts,
631                    ends: &self.s.ring_ends,
632                    origin,
633                },
634            ),
635            Event::Whole { tile, origin } => (
636                tile,
637                PieceRings::Whole {
638                    rings: &self.s.rings,
639                    input: self.rings,
640                    copies: &self.s.copies,
641                    origin,
642                },
643            ),
644        };
645        PolygonSlice::Piece(PolygonPiece { tile, rings })
646    }
647
648    fn step(&mut self) -> Result<Option<Event>, TileError> {
649        match self.start.take() {
650            Some(Start::Whole { tile, origin }) => return Ok(Some(Event::Whole { tile, origin })),
651            Some(Start::Sweep { row, last_row }) => *self.sweep = Some(Sweep::new(row, last_row)),
652            None => {}
653        }
654        let Some(sw) = self.sweep.as_mut() else {
655            return Ok(None);
656        };
657        let mut cx = Cx {
658            grid: self.grid,
659            s: &mut *self.s,
660            rings: self.rings,
661        };
662        sw.step(&mut cx)
663    }
664}
665
666/// Load the polygon `rings` into `s`: how it starts, or `None` if there is nothing to slice (a
667/// degenerate exterior). Every overflow the slicing could meet is settled here, from the polygon's
668/// bounds.
669fn prepare<V: PolyVertex, R: AsRef<[V]>>(
670    grid: Grid,
671    rings: &[R],
672    s: &mut PolyScratch<V>,
673) -> Result<Option<Start>, TileError> {
674    let (extent, buffer) = (i64::from(grid.extent()), i64::from(grid.buffer()));
675    s.rings.clear();
676    s.copies.clear();
677    u32::try_from(rings.len()).map_err(|_| TileError::GeometryTooLarge)?;
678    let mut bounds: Option<(Coord<i32>, Coord<i32>)> = None;
679    let mut edges = 0_u32;
680    for (index, ring) in (0_u32..).zip(rings) {
681        let Some((info, lo, hi)) = load_ring(s, index, ring.as_ref(), edges)? else {
682            if index == 0 {
683                return Ok(None); // a degenerate exterior drops the polygon
684            }
685            continue;
686        };
687        edges = edges
688            .checked_add(info.len)
689            .ok_or(TileError::GeometryTooLarge)?;
690        s.rings.push(info);
691        bounds = Some(bounds.map_or((lo, hi), |(l, h)| {
692            (
693                Coord {
694                    x: l.x.min(lo.x),
695                    y: l.y.min(lo.y),
696                },
697                Coord {
698                    x: h.x.max(hi.x),
699                    y: h.y.max(hi.y),
700                },
701            )
702        }));
703    }
704    let Some((lo, hi)) = bounds else {
705        return Ok(None);
706    };
707
708    // A polygon inside one tile's inner box touches only that tile, whose box `inner_tile` proved
709    // fits: it goes there whole, lent as it is, with nothing else to check.
710    if let Some((tile, origin)) = grid.inner_tile(lo, hi) {
711        return Ok(Some(Start::Whole { tile, origin }));
712    }
713    grid.check_polygon_bounds(lo, hi)?;
714
715    s.chunks.clear();
716    index_chunks(s, rings, extent, buffer);
717    s.order.clear();
718    #[expect(
719        clippy::cast_possible_truncation,
720        reason = "at most one chunk per edge"
721    )]
722    s.order.extend(0..s.chunks.len() as u32);
723    let chunks = &s.chunks;
724    s.order.sort_unstable_by_key(|&c| chunks[c as usize].rows.0);
725    s.active.clear();
726    s.inside.clear();
727    s.inside.resize(s.rings.len(), false);
728    Ok(Some(Start::Sweep {
729        row: (i64::from(lo.y) - buffer).div_euclid(extent),
730        last_row: (i64::from(hi.y) + buffer).div_euclid(extent),
731    }))
732}
733
734/// What the sweep reads besides its own state: the grid, the scratch, and the polygon being swept.
735struct Cx<'x, V, R> {
736    grid: Grid,
737    s: &'x mut PolyScratch<V>,
738    rings: &'x [R],
739}
740
741impl Sweep {
742    /// A sweep over rows `row..=last_row`, before its first row.
743    fn new(row: i64, last_row: i64) -> Self {
744        Self {
745            row,
746            last_row,
747            next_chunk: 0,
748            in_row: false,
749            ty: 0,
750            k: 0,
751            h: 0,
752            next: i64::MIN,
753            holes_in: 0,
754            pending: None,
755            ready: None,
756            tile_ready: None,
757            orient: None,
758            lh: 0,
759            cover: Some(0..0),
760            long_last: i64::MIN,
761        }
762    }
763
764    /// Advance the sweep to its next slice; `None` when the polygon is done.
765    fn step<V: PolyVertex, R: AsRef<[V]>>(
766        &mut self,
767        cx: &mut Cx<'_, V, R>,
768    ) -> Result<Option<Event>, TileError> {
769        loop {
770            if let Some(run) = self.ready.take() {
771                return Ok(Some(Event::Run(run)));
772            }
773            if let Some(tx) = self.tile_ready.take() {
774                let tile = TileId::new(tx, self.ty);
775                if let Some(origin) = self.build_tile(cx, tile)? {
776                    return Ok(Some(Event::Built { tile, origin }));
777                }
778                continue;
779            }
780            if !self.in_row {
781                if self.row > self.last_row {
782                    return Ok(None);
783                }
784                self.load_row(cx)?;
785                self.ty = i32::try_from(self.row).map_err(|_| TileError::Overflow)?;
786                (self.in_row, self.k, self.h, self.lh, self.next) = (true, 0, 0, 0, i64::MIN);
787                (self.cover, self.long_last) = (Some(0..0), i64::MIN);
788                continue;
789            }
790            // Tiles `next..=bound` all see the same crossings on their winding rays.
791            let s = &*cx.s;
792            let bound = s
793                .by_x
794                .get(self.k)
795                .map_or(i64::MAX, |&i| s.crossings[i as usize].right_of);
796            // The next edge tile, and its spans. Rows without long edges (most) take the short path.
797            let tile = if s.long.is_empty() {
798                s.hits.get(self.h).map(|c| i64::from(c.tx))
799            } else {
800                // The sweep's next tile if a long edge still covers it, else where the next short or
801                // long span starts.
802                let next = self.next;
803                // (`long_last` is `i64::MIN` when no long edge covers the sweep, as at a row start.)
804                let covered =
805                    (self.long_last != i64::MIN && self.long_last >= next).then_some(next);
806                let short = s.hits.get(self.h).map(|c| i64::from(c.tx));
807                let long = s.long.get(self.lh).map(|c| i64::from(c.first).max(next));
808                [covered, short, long].into_iter().flatten().min()
809            };
810            if let Some(tx) = tile
811                && tx <= bound
812            {
813                let tx = i32::try_from(tx).map_err(|_| TileError::Overflow)?;
814                let s = &mut *cx.s;
815                // The tile's short-edge spans: one contiguous run.
816                let start = self.h;
817                self.h += s.hits[start..].partition_point(|c| c.tx == tx);
818                self.cover = Some(start..self.h);
819                if !s.long.is_empty() {
820                    self.cover_long(s, tx, start);
821                }
822                self.fill(cx, self.next, i64::from(tx))?;
823                // The edge tile ends any covered span before it.
824                self.ready = self.take_pending();
825                self.tile_ready = Some(tx);
826                self.next = i64::from(tx) + 1;
827                continue;
828            }
829            if self.k == s.by_x.len() {
830                // Past every crossing each ring's parity is even again: nothing covers.
831                debug_assert!(cx.s.inside.iter().all(|&i| !i), "parity ends even");
832                self.ready = self.take_pending();
833                (self.in_row, self.row) = (false, self.row + 1);
834                continue;
835            }
836            // `bound` is a crossing's `right_of` (|·| ≤ 2^32), so `+ 1` cannot overflow.
837            self.fill(cx, self.next, bound + 1)?;
838            self.next = bound + 1;
839            // Tiles past `bound` have these crossings on their left.
840            let s = &mut *cx.s;
841            while let Some(&i) = s.by_x.get(self.k)
842                && s.crossings[i as usize].right_of == bound
843            {
844                let crossing = s.crossings[i as usize];
845                let r = crossing.ring as usize;
846                s.inside[r] = !s.inside[r];
847                if r != 0 {
848                    if s.inside[r] {
849                        self.holes_in += 1;
850                    } else {
851                        // Parity alternates from an empty row start, so a hole leaves only after
852                        // entering.
853                        self.holes_in = self.holes_in.saturating_sub(1);
854                    }
855                }
856                fenwick_add(&mut s.fenwick, i as usize, -crossing.sign());
857                self.k += 1;
858            }
859            // Coverage that ends here ends the covered span: it cannot grow past this crossing.
860            if !(s.inside[0] && self.holes_in == 0) {
861                self.ready = self.take_pending();
862            }
863        }
864    }
865
866    /// Add the long edges covering tile `tx` to its spans (the short ones are `s.hits[start..h]`):
867    /// drop those ending before it, add those starting by it, and merge both in edge order.
868    fn cover_long<V>(&mut self, s: &mut PolyScratch<V>, tx: i32, start: usize) {
869        if self.long_last < i64::from(tx) {
870            s.long_cover.clear();
871        } else {
872            s.long_cover.retain(|c| c.last >= tx);
873        }
874        let kept = s.long_cover.len();
875        while let Some(&span) = s.long.get(self.lh)
876            && span.first <= tx
877        {
878            s.long_cover.push(span);
879            self.lh += 1;
880        }
881        if s.long_cover.is_empty() {
882            self.long_last = i64::MIN;
883            return;
884        }
885        merge_by_edge(&mut s.long_cover, kept, &mut s.spans_tmp, |c| c.edge);
886        self.long_last = s
887            .long_cover
888            .iter()
889            .map(|c| i64::from(c.last))
890            .max()
891            .unwrap_or(i64::MIN);
892        s.covering.clear();
893        s.covering.extend(s.long_cover.iter().map(|c| Hit {
894            tx,
895            edge: c.edge,
896            ring: c.ring,
897        }));
898        let kept = s.covering.len();
899        s.covering.extend_from_slice(&s.hits[start..self.h]);
900        merge_by_edge(&mut s.covering, kept, &mut s.hits_tmp, |c| c.edge);
901        self.cover = None;
902    }
903
904    /// The covered span not yet yielded, as a run (ending it).
905    fn take_pending(&mut self) -> Option<FillRun> {
906        let (start, end) = self.pending.take()?;
907        #[expect(
908            clippy::cast_possible_truncation,
909            reason = "both ends were checked to fit i32 when the span was recorded"
910        )]
911        Some(FillRun {
912            y: self.ty,
913            x: start as i32..end as i32,
914            orient: self.orient.unwrap_or(Ordering::Equal),
915        })
916    }
917
918    /// Record tiles `start..end` of the current row as covered, if the polygon covers them,
919    /// extending the span not yet yielded when they abut.
920    fn fill<V: PolyVertex, R: AsRef<[V]>>(
921        &mut self,
922        cx: &mut Cx<'_, V, R>,
923        start: i64,
924        end: i64,
925    ) -> Result<(), TileError> {
926        let covered = cx.s.inside.first().copied().unwrap_or(false) && self.holes_in == 0;
927        if start >= end || !covered {
928            return Ok(());
929        }
930        i32::try_from(start).map_err(|_| TileError::Overflow)?;
931        i32::try_from(end).map_err(|_| TileError::Overflow)?;
932        if self.orient.is_none() {
933            let ext = cx.s.rings[0].pts(cx.rings, &cx.s.copies);
934            self.orient = Some(ring_orientation(ext));
935        }
936        match &mut self.pending {
937            Some((_, e)) if *e == start => *e = end,
938            pending => {
939                debug_assert!(
940                    pending.is_none(),
941                    "an edge tile or row end yields a span first"
942                );
943                *pending = Some((start, end));
944            }
945        }
946        Ok(())
947    }
948
949    /// Gather row `ty`'s hits and crossings from the chunks reaching it.
950    #[cfg_attr(feature = "hotpath", hotpath::measure)]
951    // (The `hotpath` wrapper hides the body from this lint.)
952    #[cfg_attr(
953        not(feature = "hotpath"),
954        expect(
955            clippy::many_single_char_names,
956            reason = "edge endpoints and the scratch"
957        )
958    )]
959    fn load_row<V: PolyVertex, R: AsRef<[V]>>(
960        &mut self,
961        cx: &mut Cx<'_, V, R>,
962    ) -> Result<(), TileError> {
963        let ty = self.row;
964        let (extent, buffer) = (i64::from(cx.grid.extent()), i64::from(cx.grid.buffer()));
965        let s = &mut *cx.s;
966        let rings = cx.rings;
967        while let Some(&c) = s.order.get(self.next_chunk)
968            && s.chunks[c as usize].rows.0 <= ty
969        {
970            let at = s.active.partition_point(|&a| a < c);
971            s.active.insert(at, c);
972            self.next_chunk += 1;
973        }
974        let chunks = &s.chunks;
975        s.active.retain(|&c| chunks[c as usize].rows.1 >= ty);
976
977        s.hits.clear();
978        s.long.clear();
979        s.long_cover.clear();
980        s.covering.clear();
981        s.crossings.clear();
982        let (slab_lo, slab_hi) = (ty * extent - buffer, ty * extent + extent - 1 + buffer);
983        let cy = row_center(ty, extent);
984        for &c in &s.active {
985            let chunk = s.chunks[c as usize];
986            let info = s.rings[chunk.ring as usize];
987            let pts = info.pts(rings, &s.copies);
988            let m = pts.len();
989            for i in chunk.first as usize..(chunk.first + chunk.count) as usize {
990                let (a, b) = (pts[i].position(), pts[(i + 1) % m].position());
991                let (lo, hi) = if a.y <= b.y { (a, b) } else { (b, a) };
992                if i64::from(hi.y) < slab_lo || i64::from(lo.y) > slab_hi {
993                    continue;
994                }
995                #[expect(clippy::cast_possible_truncation, reason = "edge ids fit u32")]
996                let edge = info.edge0 + i as u32;
997                let wide = |p: Coord<i32>| Coord {
998                    x: i64::from(p.x),
999                    y: i64::from(p.y),
1000                };
1001                let (first, last) = Seg::new(wide(lo), wide(hi)).row_span(ty, extent, buffer);
1002                let fits = |v: i64| i32::try_from(v).map_err(|_| TileError::Overflow);
1003                if last - first + 1 >= LONG_EDGE {
1004                    s.long.push(Span {
1005                        first: fits(first)?,
1006                        last: fits(last)?,
1007                        edge,
1008                        ring: chunk.ring,
1009                    });
1010                } else {
1011                    for tx in first..=last {
1012                        s.hits.push(Hit {
1013                            tx: fits(tx)?,
1014                            edge,
1015                            ring: chunk.ring,
1016                        });
1017                    }
1018                }
1019                // Crossings of the center line, half-open in `y`.
1020                if i64::from(lo.y) <= cy && cy < i64::from(hi.y) {
1021                    let x =
1022                        crossing_x_ceil(a, b, i32::try_from(cy).map_err(|_| TileError::Overflow)?);
1023                    s.crossings.push(Crossing {
1024                        edge,
1025                        ring: chunk.ring,
1026                        up: b.y > a.y,
1027                        // Right of tile `tx`'s box iff x > tx·extent + extent − 1 + buffer.
1028                        right_of: (x - extent - buffer).div_euclid(extent),
1029                    });
1030                }
1031            }
1032        }
1033        sort_hits(&mut s.hits, &mut s.hits_tmp, &mut s.counts);
1034        s.long.sort_unstable_by_key(|c| (c.first, c.edge));
1035        debug_assert!(s.crossings.is_sorted_by_key(|c| c.edge));
1036        s.by_x.clear();
1037        #[expect(clippy::cast_possible_truncation, reason = "one crossing per edge")]
1038        s.by_x.extend(0..s.crossings.len() as u32);
1039        let crossings = &s.crossings;
1040        s.by_x
1041            .sort_unstable_by_key(|&i| crossings[i as usize].right_of);
1042        fenwick_build(&mut s.fenwick, s.crossings.iter().map(|c| c.sign()));
1043        Ok(())
1044    }
1045
1046    /// Build the polygon's piece in edge tile `tile` from the spans covering it into the piece buffer,
1047    /// returning the tile origin; `None` if it leaves nothing there. Only the touching rings are
1048    /// visited: the untouched holes are settled at once by how many of them contain the tile. Every
1049    /// vertex fits the tile's local frame: the polygon's bounds were checked when it was loaded.
1050    #[cfg_attr(feature = "hotpath", hotpath::measure)]
1051    fn build_tile<V: PolyVertex, R: AsRef<[V]>>(
1052        &mut self,
1053        cx: &mut Cx<'_, V, R>,
1054        tile: TileId,
1055    ) -> Result<Option<Coord<i32>>, TileError> {
1056        let (min, max) = cx.grid.tile_buffered_bounds(tile)?;
1057        let origin = cx.grid.origin_of(min);
1058        let s = &mut *cx.s;
1059        let rings = cx.rings;
1060        let hits = match &self.cover {
1061            Some(range) => &s.hits[range.clone()],
1062            None => &s.covering[..],
1063        };
1064        s.verts.clear();
1065        s.ring_ends.clear();
1066        // Hits come in edge order, so the exterior's (if any) come first, then each hole's.
1067        let exterior_touched = hits.first().is_some_and(|x| x.ring == 0);
1068        if !exterior_touched && !s.inside[0] {
1069            return Ok(None); // the exterior misses the tile
1070        }
1071        // `holes_in` counts every hole whose ray parity is odd, but a touched hole's parity says
1072        // nothing about the tile: what remains are untouched holes containing it.
1073        let holes_in = self.holes_in as usize;
1074        if holes_in > 0
1075            && holes_in
1076                > ring_groups(hits)
1077                    .filter(|g| g[0].ring != 0 && s.inside[g[0].ring as usize])
1078                    .count()
1079        {
1080            return Ok(None);
1081        }
1082        if !exterior_touched {
1083            let orient = *self
1084                .orient
1085                .get_or_insert_with(|| ring_orientation(s.rings[0].pts(rings, &s.copies)));
1086            push_fill_box(&mut s.verts, orient, min, max)?;
1087            s.ring_ends.push(offset(s.verts.len())?);
1088        }
1089        for ring_hits in ring_groups(hits) {
1090            let info = s.rings[ring_hits[0].ring as usize];
1091            s.arcs.clear();
1092            for x in ring_hits {
1093                keep_edge(&mut s.arcs, (x.edge - info.edge0) as usize);
1094            }
1095            let (cs, fenwick) = (&s.crossings, &s.fenwick);
1096            let winding =
1097                |first: usize, count: usize| excursion_winding(cs, fenwick, info, first, count);
1098            close_ring(
1099                info.pts(rings, &s.copies),
1100                &mut s.arcs,
1101                min,
1102                max,
1103                winding,
1104                &mut s.verts,
1105            )?;
1106            s.ring_ends.push(offset(s.verts.len())?);
1107        }
1108        Ok(Some(origin))
1109    }
1110}
1111
1112/// A kept ring and its bounding box `(lo, hi)`.
1113type LoadedRing = (RingInfo, Coord<i32>, Coord<i32>);
1114
1115/// Load one ring of the polygon in `s` (its `index` among the input rings, its first edge `edge0`):
1116/// borrowed when it is distinct as given — no consecutive duplicates once its repeated closing
1117/// vertices are dropped — else its distinct vertices copied, with its bounding box. `None` for a
1118/// degenerate ring (fewer than 3 distinct vertices).
1119fn load_ring<V: Vertex>(
1120    s: &mut PolyScratch<V>,
1121    index: u32,
1122    ring: &[V],
1123    edge0: u32,
1124) -> Result<Option<LoadedRing>, TileError> {
1125    let Some(first) = ring.first().map(Vertex::position) else {
1126        return Ok(None);
1127    };
1128    let mut n = ring.len();
1129    while n >= 2 && ring[n - 1].position() == first {
1130        n -= 1;
1131    }
1132    // One pass: the bounding box, and whether the ring is distinct as given.
1133    let (mut lo, mut hi, mut prev, mut distinct) = (first, first, first, true);
1134    for v in &ring[1..n] {
1135        let p = v.position();
1136        (lo.x, lo.y, hi.x, hi.y) = (lo.x.min(p.x), lo.y.min(p.y), hi.x.max(p.x), hi.y.max(p.y));
1137        distinct &= p != prev;
1138        prev = p;
1139    }
1140    let (src, len) = if distinct {
1141        (Src::Input, n)
1142    } else {
1143        let start = s.copies.len();
1144        (Src::Copy(start), push_distinct(ring, &mut s.copies))
1145    };
1146    if len < 3 {
1147        if let Src::Copy(start) = src {
1148            s.copies.truncate(start);
1149        }
1150        return Ok(None);
1151    }
1152    let info = RingInfo {
1153        input: index,
1154        src,
1155        len: u32::try_from(len).map_err(|_| TileError::GeometryTooLarge)?,
1156        edge0,
1157    };
1158    Ok(Some((info, lo, hi)))
1159}
1160
1161/// The row index of the polygon in `s.rings`: every run of [`CHUNK`] edges, with the rows its buffered
1162/// `y`-range reaches.
1163fn index_chunks<V: Vertex, R: AsRef<[V]>>(
1164    s: &mut PolyScratch<V>,
1165    rings: &[R],
1166    extent: i64,
1167    buffer: i64,
1168) {
1169    for (ring_id, info) in (0_u32..).zip(&s.rings) {
1170        let pts = info.pts(rings, &s.copies);
1171        for first in (0..pts.len()).step_by(CHUNK) {
1172            let count = CHUNK.min(pts.len() - first);
1173            let (mut y0, mut y1) = (i32::MAX, i32::MIN);
1174            for v in pts[first..first + count]
1175                .iter()
1176                .chain(core::iter::once(&pts[(first + count) % pts.len()]))
1177            {
1178                let y = v.position().y;
1179                (y0, y1) = (y0.min(y), y1.max(y));
1180            }
1181            #[expect(
1182                clippy::cast_possible_truncation,
1183                reason = "within the ring's u32 length"
1184            )]
1185            s.chunks.push(Chunk {
1186                ring: ring_id,
1187                first: first as u32,
1188                count: count as u32,
1189                rows: (
1190                    (i64::from(y0) - buffer).div_euclid(extent),
1191                    (i64::from(y1) + buffer).div_euclid(extent),
1192                ),
1193            });
1194        }
1195    }
1196}
1197
1198/// `len` as a `u32` offset into the piece buffer.
1199fn offset(len: usize) -> Result<u32, TileError> {
1200    u32::try_from(len).map_err(|_| TileError::GeometryTooLarge)
1201}
1202
1203/// Clip a polygon's `rings` to one `tile` into the scratch's piece buffer, and lend the piece; `None`
1204/// if the polygon leaves nothing in the tile. See [`Slicer::tile_polygon`](crate::Slicer::tile_polygon).
1205#[cfg_attr(feature = "hotpath", hotpath::measure)]
1206pub(crate) fn clip_polygon_to_tile<'s, V: PolyVertex, R: AsRef<[V]>>(
1207    grid: Grid,
1208    tile: TileId,
1209    rings: &[R],
1210    s: &'s mut PolyScratch<V>,
1211) -> Result<Option<PolygonPiece<'s, V, R>>, TileError> {
1212    let (min, max) = grid.tile_buffered_bounds(tile)?;
1213    let origin = grid.origin_of(min);
1214    s.verts.clear();
1215    s.ring_ends.clear();
1216    let Some((exterior, holes)) = rings.split_first() else {
1217        return Ok(None);
1218    };
1219    // If the exterior misses the tile entirely, the whole polygon is absent here.
1220    if clip_ring(exterior.as_ref(), min, max, &mut s.clip, &mut s.verts)? == RingClip::Outside {
1221        return Ok(None);
1222    }
1223    check_local(&s.verts, origin)?;
1224    s.ring_ends.push(offset(s.verts.len())?);
1225    for hole in holes {
1226        let start = s.verts.len();
1227        match clip_ring(hole.as_ref(), min, max, &mut s.clip, &mut s.verts)? {
1228            // A hole covering the whole tile leaves nothing to draw here.
1229            RingClip::Covers => return Ok(None),
1230            RingClip::Clipped => {
1231                check_local(&s.verts[start..], origin)?;
1232                s.ring_ends.push(offset(s.verts.len())?);
1233            }
1234            RingClip::Outside => {}
1235        }
1236    }
1237    Ok(Some(PolygonPiece {
1238        tile,
1239        rings: PieceRings::Built {
1240            verts: &s.verts,
1241            ends: &s.ring_ends,
1242            origin,
1243        },
1244    }))
1245}