use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
use geo_types::Coord;
use crate::TileError;
use crate::clip_polyline::segment_intersects;
use crate::grid::Grid;
use crate::tile::{TileId, tile_of};
use crate::vertex::Vertex;
#[derive(Debug, Clone)]
struct Edge<V> {
a: V,
b: V,
tiles: BTreeSet<TileId>,
}
type EdgeKey = (Coord<i32>, Coord<i32>);
#[derive(Debug, Clone)]
struct EdgeIndex<V: Vertex> {
edges: HashMap<EdgeKey, Edge<V>>,
tile_edges: BTreeMap<TileId, Vec<EdgeKey>>,
}
impl<V: Vertex> EdgeIndex<V> {
fn new() -> Self {
Self {
edges: HashMap::new(),
tile_edges: BTreeMap::new(),
}
}
fn commit(
&mut self,
tile: TileId,
new_edges: HashMap<EdgeKey, (V, V)>,
ext: i32,
) -> Result<(), TileError> {
let mut conflicts = BTreeSet::new();
for (key, (a, b)) in &new_edges {
if let Some(existing) = self.edges.get(key)
&& !(existing.a == *a && existing.b == *b)
{
conflicts.extend(existing.tiles.iter().copied().filter(|&t| t != tile));
}
}
for (key, existing) in &self.edges {
if (tile_of(key.0, ext) == tile || tile_of(key.1, ext) == tile)
&& !new_edges.contains_key(key)
{
conflicts.extend(existing.tiles.iter().copied().filter(|&t| t != tile));
}
}
for key in new_edges.keys() {
for endpoint in [tile_of(key.0, ext), tile_of(key.1, ext)] {
if endpoint != tile
&& self.tile_edges.contains_key(&endpoint)
&& !self
.edges
.get(key)
.is_some_and(|e| e.tiles.contains(&endpoint))
{
conflicts.insert(endpoint);
}
}
}
if !conflicts.is_empty() {
return Err(TileError::Conflict(conflicts.into_iter().collect()));
}
self.purge(tile);
let mut keys = Vec::with_capacity(new_edges.len());
for (key, (a, b)) in new_edges {
self.edges
.entry(key)
.or_insert_with(|| Edge {
a,
b,
tiles: BTreeSet::new(),
})
.tiles
.insert(tile);
keys.push(key);
}
if !keys.is_empty() {
self.tile_edges.insert(tile, keys);
}
Ok(())
}
fn purge(&mut self, tile: TileId) -> bool {
let Some(keys) = self.tile_edges.remove(&tile) else {
return false;
};
for key in keys {
if let Some(edge) = self.edges.get_mut(&key) {
edge.tiles.remove(&tile);
if edge.tiles.is_empty() {
self.edges.remove(&key);
}
}
}
true
}
fn iter_features(&self) -> impl Iterator<Item = Vec<V>> {
let mut ordered: Vec<&Edge<V>> = self.edges.values().collect();
ordered.sort_unstable_by_key(|edge| {
let (a, b) = (edge.a.position(), edge.b.position());
(a.x, a.y, b.x, b.y)
});
let runs: Vec<Vec<V>> = ordered.iter().map(|edge| vec![edge.a, edge.b]).collect();
stitch(&runs).into_iter()
}
fn contains(&self, tile: TileId) -> bool {
self.tile_edges.contains_key(&tile)
}
fn len(&self) -> usize {
self.tile_edges.len()
}
fn is_empty(&self) -> bool {
self.tile_edges.is_empty()
}
fn clear(&mut self) {
self.edges.clear();
self.tile_edges.clear();
}
}
#[derive(Debug, Clone)]
pub struct Mosaic<V: Vertex = Coord<i32>> {
extent: u32,
index: EdgeIndex<V>,
}
impl<V: Vertex> Mosaic<V> {
pub fn new(extent: u32) -> Result<Self, TileError> {
if extent == 0 || extent > i32::MAX.cast_unsigned() {
return Err(TileError::InvalidExtent);
}
Ok(Self {
extent,
index: EdgeIndex::new(),
})
}
#[must_use]
pub fn extent(&self) -> u32 {
self.extent
}
#[cfg_attr(feature = "hotpath", hotpath::measure)]
pub fn add<L: AsRef<[V]>>(&mut self, tile: TileId, runs: &[L]) -> Result<(), TileError> {
let (off_x, off_y) = tile_offset(tile, self.extent);
let mut new_edges: HashMap<EdgeKey, (V, V)> = HashMap::new();
for run in runs {
let g = rebase(run.as_ref(), off_x, off_y)?;
for w in g.windows(2) {
let key = (w[0].position(), w[1].position());
if key.0 != key.1 {
new_edges.entry(key).or_insert((w[0], w[1]));
}
}
}
self.index
.commit(tile, new_edges, self.extent.cast_signed())
}
pub fn purge(&mut self, tile: TileId) -> bool {
self.index.purge(tile)
}
pub fn iter_features(&self) -> impl Iterator<Item = Vec<V>> {
self.index.iter_features()
}
#[must_use]
pub fn contains(&self, tile: TileId) -> bool {
self.index.contains(tile)
}
#[must_use]
pub fn len(&self) -> usize {
self.index.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.index.is_empty()
}
pub fn clear(&mut self) {
self.index.clear();
}
}
#[derive(Debug, Clone)]
pub struct PolygonMosaic<V: Vertex = Coord<i32>> {
grid: Grid,
index: EdgeIndex<V>,
}
impl<V: Vertex> PolygonMosaic<V> {
pub fn new(extent: u32, buffer: u16) -> Result<Self, TileError> {
Ok(Self {
grid: Grid::new(extent, buffer)?,
index: EdgeIndex::new(),
})
}
#[must_use]
pub fn extent(&self) -> u32 {
self.grid.extent()
}
#[must_use]
pub fn buffer(&self) -> u16 {
self.grid.buffer()
}
#[cfg_attr(feature = "hotpath", hotpath::measure)]
pub fn add<L: AsRef<[V]>>(&mut self, tile: TileId, rings: &[L]) -> Result<(), TileError> {
let (min, max) = self.grid.tile_buffered_bounds(tile)?;
let ext = self.grid.extent();
let (off_x, off_y) = tile_offset(tile, ext);
let mut new_edges: HashMap<EdgeKey, (V, V)> = HashMap::new();
for ring in rings {
ring_edges(ring.as_ref(), off_x, off_y, min, max, &mut new_edges)?;
}
self.index.commit(tile, new_edges, ext.cast_signed())
}
pub fn purge(&mut self, tile: TileId) -> bool {
self.index.purge(tile)
}
pub fn iter_features(&self) -> impl Iterator<Item = Vec<V>> {
self.index.iter_features()
}
#[must_use]
pub fn contains(&self, tile: TileId) -> bool {
self.index.contains(tile)
}
#[must_use]
pub fn len(&self) -> usize {
self.index.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.index.is_empty()
}
pub fn clear(&mut self) {
self.index.clear();
}
}
fn tile_offset(tile: TileId, extent: u32) -> (i64, i64) {
let e = i64::from(extent);
(i64::from(tile.x) * e, i64::from(tile.y) * e)
}
fn rebase_vertex<V: Vertex>(v: V, off_x: i64, off_y: i64) -> Result<V, TileError> {
let p = v.position();
Ok(v.with_position(Coord {
x: i32::try_from(i64::from(p.x) + off_x).map_err(|_| TileError::Overflow)?,
y: i32::try_from(i64::from(p.y) + off_y).map_err(|_| TileError::Overflow)?,
}))
}
fn rebase<V: Vertex>(run: &[V], off_x: i64, off_y: i64) -> Result<Vec<V>, TileError> {
run.iter()
.map(|&v| rebase_vertex(v, off_x, off_y))
.collect()
}
fn ring_edges<V: Vertex>(
ring: &[V],
off_x: i64,
off_y: i64,
min: Coord<i32>,
max: Coord<i32>,
new_edges: &mut HashMap<EdgeKey, (V, V)>,
) -> Result<(), TileError> {
let mut pts: Vec<V> = Vec::with_capacity(ring.len());
for &v in ring {
let g = rebase_vertex(v, off_x, off_y)?;
if pts.last().map(Vertex::position) != Some(g.position()) {
pts.push(g);
}
}
while pts.len() >= 2 && pts[0].position() == pts[pts.len() - 1].position() {
pts.pop();
}
let m = pts.len();
if m < 3 {
return Ok(());
}
for i in 0..m {
let (p, q) = (pts[i], pts[(i + 1) % m]);
if p.position() != q.position() && segment_intersects(p.position(), q.position(), min, max)
{
new_edges
.entry((p.position(), q.position()))
.or_insert((p, q));
}
}
Ok(())
}
fn stitch<V: Vertex>(runs: &[Vec<V>]) -> Vec<Vec<V>> {
let mut seen: HashSet<(Coord<i32>, Coord<i32>)> = HashSet::new();
let mut edges: Vec<(V, V)> = Vec::new();
for run in runs {
for w in run.windows(2) {
if seen.insert((w[0].position(), w[1].position())) {
edges.push((w[0], w[1]));
}
}
}
if edges.is_empty() {
return Vec::new();
}
let mut out: HashMap<Coord<i32>, VecDeque<usize>> = HashMap::new();
let mut indeg: HashMap<Coord<i32>, usize> = HashMap::new();
let mut points: Vec<Coord<i32>> = Vec::new();
let mut known: HashSet<Coord<i32>> = HashSet::new();
for (i, (p, q)) in edges.iter().enumerate() {
let (pp, qp) = (p.position(), q.position());
out.entry(pp).or_default().push_back(i);
*indeg.entry(qp).or_insert(0) += 1;
indeg.entry(pp).or_insert(0);
for pt in [pp, qp] {
if known.insert(pt) {
points.push(pt);
}
}
}
let mut used = vec![false; edges.len()];
let mut chains: Vec<Vec<V>> = Vec::new();
for &p in &points {
let outdeg = out.get(&p).map_or(0, VecDeque::len);
let ind = indeg.get(&p).copied().unwrap_or(0);
for _ in ind..outdeg {
if let Some(first) = next_unused(&mut out, p, &used) {
chains.push(build_chain(first, &edges, &mut used, &mut out));
}
}
}
for i in 0..edges.len() {
if !used[i] {
chains.push(build_chain(i, &edges, &mut used, &mut out));
}
}
chains
}
fn next_unused(
out: &mut HashMap<Coord<i32>, VecDeque<usize>>,
p: Coord<i32>,
used: &[bool],
) -> Option<usize> {
let dq = out.get_mut(&p)?;
while let Some(&i) = dq.front() {
if used[i] {
dq.pop_front();
} else {
return dq.pop_front();
}
}
None
}
fn build_chain<V: Vertex>(
first: usize,
edges: &[(V, V)],
used: &mut [bool],
out: &mut HashMap<Coord<i32>, VecDeque<usize>>,
) -> Vec<V> {
let mut chain = vec![edges[first].0];
let mut cur = first;
loop {
used[cur] = true;
let q = edges[cur].1;
chain.push(q);
match next_unused(out, q.position(), used) {
Some(next) => cur = next,
None => break,
}
}
chain
}