maplibre/render/tile_view_pattern/
mod.rs

1//! Utility for generating a tile pattern which can be used for masking.
2
3mod pattern;
4
5use std::{marker::PhantomData, mem::size_of, ops::Range};
6
7use cgmath::Matrix4;
8pub use pattern::{TileViewPattern, DEFAULT_TILE_VIEW_PATTERN_SIZE};
9
10use crate::{
11    coords::{WorldTileCoords, Zoom},
12    render::shaders::ShaderTileMetadata,
13    tcs::{resources::ResourceQuery, world::World},
14};
15
16pub type WgpuTileViewPattern = TileViewPattern<wgpu::Queue, wgpu::Buffer>;
17
18/// If not otherwise specified, raster tiles usually are 512.0 by 512.0 pixel.
19/// In order to support 256.0 x 256.0 raster tiles 256.0 must be used.
20///
21/// Vector tiles always have a size of 512.0.
22pub const DEFAULT_TILE_SIZE: f64 = 512.0;
23
24/// This defines the source tile shaped from which the content for the `target` is taken.
25/// For example if the target is `(0, 0, 1)` (of [`ViewTile`]) , we might use
26/// `SourceShapes::Parent((0, 0, 0))` as source.
27/// Similarly if we have the target `(0, 0, 0)` we might use
28/// `SourceShapes::Children((0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1))` as sources.
29#[derive(Debug, Clone)]
30pub enum SourceShapes {
31    /// Parent tile is the source. We construct the `target` from parts of a parent.
32    Parent(TileShape),
33    /// Children are the source. We construct the `target` from multiple children.
34    Children(Vec<TileShape>),
35    /// Source and target are equal, so no need to differentiate. We render the `source` shape
36    /// exactly at the `target`.
37    SourceEqTarget(TileShape),
38    /// No data available so nothing to render
39    None,
40}
41
42/// Defines the `target` tile and its `source` from which data tile data comes.
43#[derive(Debug, Clone)]
44pub struct ViewTile {
45    target: WorldTileCoords,
46    source: SourceShapes,
47}
48
49impl ViewTile {
50    pub fn coords(&self) -> WorldTileCoords {
51        self.target
52    }
53
54    pub fn render<F>(&self, mut callback: F)
55    where
56        F: FnMut(&TileShape),
57    {
58        match &self.source {
59            SourceShapes::Parent(source_shape) => callback(source_shape),
60            SourceShapes::Children(source_shapes) => {
61                for shape in source_shapes {
62                    callback(shape)
63                }
64            }
65            SourceShapes::SourceEqTarget(source_shape) => callback(source_shape),
66            SourceShapes::None => {}
67        }
68    }
69}
70
71/// Defines the exact location where a specific tile on the map is rendered. It defines the shape
72/// of the tile with its location for the current zoom factor.
73#[derive(Debug, Clone)]
74pub struct TileShape {
75    coords: WorldTileCoords,
76
77    // TODO: optimization, `zoom_factor` and `transform` are no longer required if `buffer_range` is Some()
78    zoom_factor: f64,
79    transform: Matrix4<f64>,
80
81    buffer_range: Option<Range<wgpu::BufferAddress>>,
82}
83
84impl TileShape {
85    fn new(coords: WorldTileCoords, zoom: Zoom) -> Self {
86        Self {
87            coords,
88            zoom_factor: zoom.scale_to_tile(&coords),
89            transform: coords.transform_for_zoom(zoom),
90            buffer_range: None,
91        }
92    }
93
94    fn set_buffer_range(&mut self, index: u64) {
95        const STRIDE: u64 = size_of::<ShaderTileMetadata>() as u64;
96        self.buffer_range = Some(index * STRIDE..(index + 1) * STRIDE);
97    }
98
99    pub fn buffer_range(&self) -> Option<Range<wgpu::BufferAddress>> {
100        self.buffer_range.clone()
101    }
102
103    pub fn coords(&self) -> WorldTileCoords {
104        self.coords
105    }
106}
107
108impl Default for TileShape {
109    fn default() -> Self {
110        Self::new(
111            crate::coords::WorldTileCoords::default(),
112            crate::coords::Zoom::default(),
113        )
114    }
115}
116
117pub trait HasTile {
118    fn has_tile(&self, coords: WorldTileCoords, world: &World) -> bool;
119
120    fn get_available_parent(
121        &self,
122        coords: WorldTileCoords,
123        world: &World,
124    ) -> Option<WorldTileCoords> {
125        let mut current = coords;
126        loop {
127            if self.has_tile(current, world) {
128                return Some(current);
129            } else if let Some(parent) = current.get_parent() {
130                current = parent
131            } else {
132                return None;
133            }
134        }
135    }
136
137    fn get_available_children(
138        &self,
139        coords: WorldTileCoords,
140        world: &World,
141        search_depth: usize,
142    ) -> Option<Vec<WorldTileCoords>> {
143        let mut children = coords.get_children().to_vec();
144
145        let mut output = Vec::new();
146
147        for _ in 0..search_depth {
148            let mut new_children = Vec::with_capacity(children.len() * 4);
149
150            for child in children {
151                if self.has_tile(child, world) {
152                    output.push(child);
153                } else {
154                    new_children.extend(child.get_children())
155                }
156            }
157
158            children = new_children;
159        }
160
161        Some(output)
162    }
163}
164
165impl<A: HasTile> HasTile for &A {
166    fn has_tile(&self, coords: WorldTileCoords, world: &World) -> bool {
167        A::has_tile(*self, coords, world)
168    }
169}
170
171impl<A: HasTile> HasTile for (A,) {
172    fn has_tile(&self, coords: WorldTileCoords, world: &World) -> bool {
173        self.0.has_tile(coords, world)
174    }
175}
176
177impl<A: HasTile, B: HasTile> HasTile for (A, B) {
178    fn has_tile(&self, coords: WorldTileCoords, world: &World) -> bool {
179        self.0.has_tile(coords, world) && self.1.has_tile(coords, world)
180    }
181}
182
183impl<A: HasTile, B: HasTile, C: HasTile> HasTile for (A, B, C) {
184    fn has_tile(&self, coords: WorldTileCoords, world: &World) -> bool {
185        self.0.has_tile(coords, world)
186            && self.1.has_tile(coords, world)
187            && self.2.has_tile(coords, world)
188    }
189}
190
191pub struct QueryHasTile<Q> {
192    phantom_q: PhantomData<Q>,
193}
194
195impl<Q: ResourceQuery> Default for QueryHasTile<Q> {
196    fn default() -> Self {
197        Self {
198            phantom_q: Default::default(),
199        }
200    }
201}
202
203impl<Q: ResourceQuery> HasTile for QueryHasTile<Q>
204where
205    for<'a> Q::Item<'a>: HasTile,
206{
207    fn has_tile(&self, coords: WorldTileCoords, world: &World) -> bool {
208        let resources = world
209            .resources
210            .query::<Q>()
211            .expect("resource not found for has_tile check");
212
213        resources.has_tile(coords, world)
214    }
215}
216
217#[derive(Default)]
218pub struct ViewTileSources {
219    items: Vec<Box<dyn HasTile>>,
220}
221
222impl ViewTileSources {
223    pub fn add<H: HasTile + 'static + Default>(&mut self) -> &mut Self {
224        self.items.push(Box::<H>::default());
225        self
226    }
227
228    pub fn add_resource_query<Q: ResourceQuery + 'static>(&mut self) -> &mut Self
229    where
230        for<'a> Q::Item<'a>: HasTile,
231    {
232        self.items.push(Box::new(QueryHasTile::<Q>::default()));
233        self
234    }
235
236    pub fn clear(&mut self) {
237        self.items.clear()
238    }
239}
240
241impl HasTile for ViewTileSources {
242    fn has_tile(&self, coords: WorldTileCoords, world: &World) -> bool {
243        self.items.iter().all(|item| item.has_tile(coords, world))
244    }
245}