galileo 0.2.1

Cross-platform general purpose map rendering engine
Documentation
use std::sync::Arc;
use std::time::Duration;

use parking_lot::Mutex;

use crate::render::PackedBundle;
use crate::tile_schema::TileIndex;
use crate::TileSchema;

#[derive(Clone)]
pub(crate) struct DisplayedTile<StyleId: Copy> {
    index: TileIndex,
    pub(crate) bundle: Arc<dyn PackedBundle>,
    style_id: StyleId,
    pub(crate) opacity: f32,
    displayed_at: web_time::Instant,
}

impl<StyleId: Copy> DisplayedTile<StyleId> {
    pub(crate) fn is_opaque(&self) -> bool {
        self.opacity >= 0.999
    }
}

pub(crate) trait TileProvider<StyleId> {
    fn get_tile(&self, index: TileIndex, style_id: StyleId) -> Option<Arc<dyn PackedBundle>>;
}

pub(crate) struct TilesContainer<StyleId, Provider>
where
    StyleId: Copy,
    Provider: TileProvider<StyleId>,
{
    pub(crate) tiles: Mutex<Vec<DisplayedTile<StyleId>>>,
    tile_schema: TileSchema,
    pub(crate) tile_provider: Provider,
}

impl<StyleId, Provider> TilesContainer<StyleId, Provider>
where
    StyleId: Copy + PartialEq,
    Provider: TileProvider<StyleId>,
{
    pub(crate) fn new(tile_schema: TileSchema, tile_provider: Provider) -> Self {
        Self {
            tiles: Default::default(),
            tile_schema,
            tile_provider,
        }
    }

    pub(crate) fn update_displayed_tiles(
        &self,
        needed_indices: impl IntoIterator<Item = TileIndex>,
        style_id: StyleId,
    ) -> bool {
        let mut displayed_tiles = self.tiles.lock();

        let mut needed_tiles = vec![];
        let mut to_substitute = vec![];

        let now = web_time::Instant::now();
        let fade_in_time = self.fade_in_time();
        let mut requires_redraw = false;

        for index in needed_indices {
            if let Some(displayed) = displayed_tiles
                .iter_mut()
                .find(|displayed| displayed.index == index && displayed.style_id == style_id)
            {
                if !displayed.is_opaque() {
                    to_substitute.push(index);
                    displayed.opacity = ((now.duration_since(displayed.displayed_at)).as_secs_f64()
                        / fade_in_time.as_secs_f64())
                    .min(1.0) as f32;
                    requires_redraw = true;
                }

                needed_tiles.push(displayed.clone());
            } else {
                match self.tile_provider.get_tile(index, style_id) {
                    None => to_substitute.push(index),
                    Some(bundle) => {
                        needed_tiles.push(DisplayedTile {
                            index,
                            bundle,
                            style_id,
                            opacity: 0.0,
                            displayed_at: now,
                        });
                        to_substitute.push(index);
                        requires_redraw = true;
                    }
                }
            }
        }

        let mut new_displayed = vec![];
        for displayed in displayed_tiles.iter() {
            if needed_tiles
                .iter()
                .any(|new| new.index == displayed.index && new.style_id == displayed.style_id)
            {
                continue;
            }

            let Some(displayed_bbox) = self.tile_schema.tile_bbox(displayed.index) else {
                continue;
            };

            for subst in &to_substitute {
                let Some(subst_bbox) = self.tile_schema.tile_bbox(*subst) else {
                    continue;
                };

                if displayed_bbox.intersects(subst_bbox) {
                    new_displayed.push(displayed.clone());
                    break;
                }
            }
        }

        new_displayed.append(&mut needed_tiles);
        *displayed_tiles = new_displayed;

        requires_redraw
    }

    fn fade_in_time(&self) -> Duration {
        Duration::from_millis(300)
    }
}