bevy_tilemap 0.4.0

Tilemaps with chunks for the Bevy game engine, a simple-driven game engine and app framework
Documentation
//! # Constructing a basic tilemap, setting tiles, and spawning.
//!
//! Bevy Tilemap makes it easy to quickly implement a tilemap if you are in a
//! rush or want to build a conceptual game.
//!
//! ```
//! use bevy_asset::{prelude::*, HandleId};
//! use bevy_sprite::prelude::*;
//! use bevy_tilemap::prelude::*;
//!
//! // This must be set in Asset<TextureAtlas>.
//! let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
//!
//! let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
//!
//! // Coordinate point with Z order.
//! let point = (16, 16);
//! let sprite_index = 0;
//! let tile = Tile { point: point.clone(), sprite_index, ..Default::default() };
//! tilemap.insert_tile(tile);
//!
//! tilemap.spawn_chunk_containing_point(point);
//! ```
//!
//! # Constructing a more advanced tilemap.
//!
//! For most cases, it is preferable to construct a tilemap with explicit
//! parameters. For that you would use a [`TilemapBuilder`].
//!
//! [`TilemapBuilder`]: crate::tilemap::TilemapBuilder
//!
//! ```
//! use bevy_asset::{prelude::*, HandleId};
//! use bevy_sprite::prelude::*;
//! use bevy_tilemap::prelude::*;
//!
//! // This must be set in Asset<TextureAtlas>.
//! let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
//!
//! let mut tilemap = TilemapBuilder::new()
//!     .texture_atlas(texture_atlas_handle)
//!     .chunk_dimensions(64, 64, 1)
//!     .texture_dimensions(8, 8)
//!     .dimensions(32, 32)
//!     .add_layer(TilemapLayer { kind: LayerKind::Dense, ..Default::default() }, 0)
//!     .add_layer(TilemapLayer { kind: LayerKind::Sparse, ..Default::default() }, 1)
//!     .add_layer(TilemapLayer { kind: LayerKind::Sparse, ..Default::default() }, 2)
//!     .z_layers(3)
//!     .finish();
//! ```
//!
//! The above example outlines all the current possible builder methods. What is
//! neat is that if more layers are accidentally set than z_layer set, it will
//! use the layer length instead. Much more features are planned including
//! automated systems that will enhance the tilemap further.
//!
//! # Setting tiles
//!
//! There are two methods to set tiles in the tilemap. The first is single tiles
//! at a time which is acceptable for tiny updates such as moving around
//! characters. The second being bulk setting many tiles at once.
//!
//! If you expect to move multiple tiles a frame, **always** use [`insert_tiles`].
//! A single event is created with all tiles if set this way.
//!
//! [`insert_tiles`]: crate::tilemap::Tilemap::insert_tiles
//!
//! ```
//! use bevy_asset::{prelude::*, HandleId};
//! use bevy_sprite::prelude::*;
//! use bevy_tilemap::prelude::*;
//!
//! // This must be set in Asset<TextureAtlas>.
//! let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
//!
//! let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
//!
//! // Prefer this
//! let mut tiles = Vec::new();
//! for y in 0..31 {
//!     for x in 0..31 {
//!         tiles.push(Tile { point: (x, y), ..Default::default() });
//!     }
//! }
//!
//! tilemap.insert_tiles(tiles);
//!
//! // Over this...
//! for y in 0..31 {
//!     for x in 0..31 {
//!         tilemap.insert_tile(Tile { point: (x, y), ..Default::default() });
//!     }
//! }
//! ```

use crate::{
    chunk::{mesh::ChunkMesh, Chunk, LayerKind, RawTile},
    event::TilemapChunkEvent,
    lib::*,
    prelude::GridTopology,
    tile::Tile,
};

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
/// The kinds of errors that can occur.
pub enum ErrorKind {
    /// If the coordinate or index is out of bounds.
    DimensionError(DimensionError),
    /// If a layer already exists this error is returned.
    LayerExists(usize),
    /// If a layer does not already exist this error is returned.
    LayerDoesNotExist(usize),
    /// Texture atlas was not set
    MissingTextureAtlas,
    /// The tile dimensions were not set.
    MissingTextureDimensions,
    /// The chunk does not exist.
    MissingChunk,
    /// The chunk already exists.
    ChunkAlreadyExists(Point2),
}

impl Display for ErrorKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        use ErrorKind::*;
        match self {
            DimensionError(err) => ::std::fmt::Debug::fmt(&err, f),
            LayerExists(n) => write!(
                f,
                "layer {} already exists, try `remove_layer` or `move_layer` first",
                n
            ),
            LayerDoesNotExist(n) => write!(f, "layer {} does not exist, try `add_layer` first", n),
            MissingTextureAtlas => write!(
                f,
                "texture atlas is missing, must use `TilemapBuilder::texture_atlas`"
            ),
            MissingTextureDimensions => {
                write!(f, "tile dimensions are missing, it is required to set it")
            }
            MissingChunk => write!(f, "the chunk does not exist, try `add_chunk` first"),
            ChunkAlreadyExists(p) => write!(
                f,
                "the chunk {} already exists, if this was intentional run `remove_chunk` first",
                p
            ),
        }
    }
}

impl Error for ErrorKind {}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
/// The error type for operations when interacting with the tilemap.
pub struct TilemapError(pub Box<ErrorKind>);

impl Display for TilemapError {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        Display::fmt(&self.0, f)
    }
}

impl Error for TilemapError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        Some(&self.0)
    }
}

impl From<ErrorKind> for TilemapError {
    fn from(kind: ErrorKind) -> TilemapError {
        TilemapError(Box::new(kind))
    }
}

impl From<DimensionError> for TilemapError {
    fn from(err: DimensionError) -> TilemapError {
        TilemapError(Box::new(ErrorKind::DimensionError(err)))
    }
}

/// A map result.
pub type TilemapResult<T> = Result<T, TilemapError>;

bitflags! {
    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
    struct AutoFlags: u16 {
        const NONE = 0b0;
        const AUTO_CONFIGURE = 0b0000_0000_0000_0001;
        const AUTO_CHUNK = 0b0000_0000_0000_0010;
        const AUTO_SPAWN = 0b0000_0000_0000_0100;
    }
}

/// The default texture dimensions in chunks.
const DEFAULT_TEXTURE_DIMENSIONS: Dimension2 = Dimension2::new(32, 32);
/// The default chunk dimensions in tiles.
const DEFAULT_CHUNK_DIMENSIONS: Dimension3 = Dimension3::new(32, 32, 1);
/// The default tile scale.
const DEFAULT_TILE_SCALE: (f32, f32, f32) = (1.0, 1.0, 1.0);
/// The default z layers.
const DEFAULT_Z_LAYERS: usize = 5;

impl Default for AutoFlags {
    fn default() -> Self {
        AutoFlags::AUTO_CONFIGURE & AutoFlags::AUTO_CHUNK
    }
}

/// A layer configuration for a tilemap.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct TilemapLayer {
    /// The kind of layer to create.
    pub kind: LayerKind,
}

impl Default for TilemapLayer {
    fn default() -> TilemapLayer {
        TilemapLayer {
            kind: LayerKind::Dense,
        }
    }
}

/// A Tilemap which maintains chunks and its tiles within.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug)]
pub struct Tilemap {
    /// The type of grid to use.
    topology: GridTopology,
    /// An optional field which can contain the tilemaps dimensions in chunks.
    dimensions: Option<Dimension2>,
    /// A chunks dimensions in tiles.
    chunk_dimensions: Dimension3,
    /// The layer in the chunks offset value as X, Y. Each layer will be offset
    /// by this.
    layer_offset: Vec2,
    /// A mesh for a chunk which is stored here and copied when needed.
    chunk_mesh: ChunkMesh,
    /// A tiles dimensions in pixels.
    texture_dimensions: Dimension2,
    /// The layers that are currently set in the tilemap in order from lowest
    /// to highest.
    layers: Vec<Option<TilemapLayer>>,
    /// Auto flags used for different automated features.
    auto_flags: AutoFlags,
    /// Dimensions of chunks to spawn from camera transform.
    auto_spawn: Option<Dimension2>,
    /// Custom flags.
    custom_flags: Vec<u32>,
    #[cfg_attr(feature = "serde", serde(skip))]
    /// The handle of the texture atlas.
    texture_atlas: Handle<TextureAtlas>,
    /// A map of all the chunks at points.
    chunks: HashMap<Point2, Chunk>,
    #[cfg_attr(feature = "serde", serde(skip))]
    /// A map of all currently spawned entities.
    entities: HashMap<usize, Vec<Entity>>,
    #[cfg_attr(feature = "serde", serde(skip))]
    /// The events of the tilemap.
    chunk_events: Events<TilemapChunkEvent>,
    /// A set of all spawned chunks.
    spawned: HashSet<(i32, i32)>,
}

/// Tilemap factory, which can be used to construct and configure new tilemaps.
///
/// Methods can be chained in order to configure it. The [`texture_atlas`]
/// method is **required** in order to have a successful factory creation.
///
/// The configuration options available are:
///
/// - [`topology`]: sets the topology of the tilemap.
/// - [`dimensions`]: specifies the dimensions of the tilemap. If this
/// is not set, then the tilemap will have no dimensions.
/// - [`chunk_dimensions`]: specifies the chunk's dimensions in tiles.
/// Default is 32x, 32y.
/// - [`texture_dimensions`]: specifies the tile's dimensions in pixels.
/// Default is 32px, 32px.
/// - [`layer_offset`]: Sets the layer offset as X, Y.
/// - [`z_layers`]: specifies the maximum number of layers that sprites
/// can exist on. Default is 20.
/// - [`texture_atlas`]: specifies the texture atlas handle
/// to use for the tilemap.
/// - [`tile_scale`]: sets the tile scale in pixels.
/// - [`add_layer`]: adds a layer to the tilemap.
/// - [`auto_chunk`]: set if you want the tilemap to automatically spawn new
/// chunks.
/// - [`auto_spawn`]: set if you want the tilemap to automatically spawn and
/// despawn chunks.
///
/// The [`finish`] method will take ownership and consume the builder returning
/// a [`TilemapResult`] with either an [`TilemapError`] or the [tilemap].
///
/// # Examples
/// ```
/// use bevy_asset::{prelude::*, HandleId};
/// use bevy_sprite::prelude::*;
/// use bevy_tilemap::prelude::*;
///
/// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
///
/// let builder = TilemapBuilder::new().texture_dimensions(32, 32).texture_atlas(texture_atlas_handle);
///
/// let tilemap = builder.finish().unwrap();
/// ```
///
/// Can also get a builder like this:
/// ```
/// use bevy_asset::{prelude::*, HandleId};
/// use bevy_sprite::prelude::*;
/// use bevy_tilemap::prelude::*;
///
/// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
///
/// let builder = Tilemap::builder().texture_dimensions(32, 32).texture_atlas(texture_atlas_handle);
///
/// let tilemap = builder.finish().unwrap();
/// ```
///
/// [`finish`]: TilemapBuilder::finish
/// [`chunk_dimensions`]: TilemapBuilder::chunk_dimensions
/// [`dimensions`]: TilemapBuilder::dimensions
/// [`texture_atlas`]: TilemapBuilder::texture_atlas
/// [`texture_dimensions`]: TilemapBuilder::texture_dimensions
/// [`z_layers`]: TilemapBuilder::z_layers
/// [`topology`]: TilemapBuilder::topology
/// [`layer_offset`]: TilemapBuilder::layer_offset
/// [`tile_scale`]: TilemapBuilder::tile_scale
/// [`add_layer`]: TilemapBuilder::add_layer
/// [`auto_chunk`]: TilemapBuilder::auto_chunk
/// [`auto_spawn`]: TilemapBuilder::auto_spawn
/// [tilemap]: Tilemap
/// [`TilemapError`]: TilemapError
/// [`TilemapResult`]: TilemapResult
#[derive(Clone, PartialEq, Debug)]
pub struct TilemapBuilder {
    /// The type of grid to use.
    topology: GridTopology,
    /// An optional field which can contain the tilemaps dimensions in chunks.
    dimensions: Option<Dimension2>,
    /// The chunks dimensions in tiles.
    chunk_dimensions: Dimension3,
    /// The layer in the chunks offset value as X, Y. Each layer will be offset
    /// by this.
    layer_offset: Vec2,
    /// The tiles dimensions in pixels.
    texture_dimensions: Option<Dimension2>,
    /// The scale of a tile.
    tile_scale: Vec3,
    /// The amount of z layers.
    z_layers: usize,
    /// The layers to be set. If there are more, it will override `z_layers`.
    layers: Option<HashMap<usize, TilemapLayer>>,
    /// If the tilemap currently has a sprite sheet handle on it or not.
    texture_atlas: Option<Handle<TextureAtlas>>,
    /// Sets how many Z layers to render.
    render_depth: usize,
    /// True if this tilemap will automatically configure.
    auto_flags: AutoFlags,
    /// The radius of chunks to spawn from a camera's transform.
    auto_spawn: Option<Dimension2>,
}

impl Default for TilemapBuilder {
    fn default() -> Self {
        let layers = {
            let mut map = HashMap::default();
            map.insert(
                0,
                TilemapLayer {
                    kind: LayerKind::Dense,
                },
            );
            Some(map)
        };
        TilemapBuilder {
            topology: GridTopology::Square,
            dimensions: None,
            chunk_dimensions: DEFAULT_CHUNK_DIMENSIONS,
            layer_offset: Vec2::new(0., 0.),
            texture_dimensions: None,
            tile_scale: DEFAULT_TILE_SCALE.into(),
            z_layers: DEFAULT_Z_LAYERS,
            layers,
            texture_atlas: None,
            render_depth: 0,
            auto_flags: AutoFlags::NONE,
            auto_spawn: None,
        }
    }
}

impl TilemapBuilder {
    /// Configures the builder with the default settings.
    ///
    /// Is equivalent to [`default`] and [`builder`] method in the
    /// [tilemap]. Start with this then you are able to method chain.
    ///
    /// [`default`]: TilemapBuilder::default
    /// [`builder`]: TilemapBuilder
    /// [tilemap]: Tilemap
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    /// use bevy_tilemap::tilemap;
    ///
    /// let builder = TilemapBuilder::new();
    ///
    /// // Equivalent to...
    ///
    /// let builder = TilemapBuilder::default();
    ///
    /// // Or...
    ///
    /// let builder = Tilemap::builder();
    /// ```
    pub fn new() -> TilemapBuilder {
        TilemapBuilder::default()
    }

    /// Sets the topology of the tilemap.
    ///
    /// The default is a square grid. Use this if you want a hexagonal grid instead.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new().topology(GridTopology::HexY);
    /// ```
    pub fn topology(mut self, topology: GridTopology) -> TilemapBuilder {
        self.topology = topology;
        self
    }

    /// Sets the dimensions of the tilemap.
    ///
    /// If this is not set then the tilemap will be boundless entirely.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new().dimensions(5, 5);
    /// ```
    pub fn dimensions(mut self, width: u32, height: u32) -> TilemapBuilder {
        self.dimensions = Some(Dimension2::new(width, height));
        self
    }

    /// Sets the chunk dimensions.
    ///
    /// Chunk dimensions are in tiles. If this is not set then the default of
    /// 32x, 32y is used.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new().chunk_dimensions(32, 32, 1);
    /// ```
    pub fn chunk_dimensions(mut self, width: u32, height: u32, depth: u32) -> TilemapBuilder {
        self.chunk_dimensions = Dimension3::new(width, height, depth);
        self
    }

    /// Sets the layer offset as X, Y.
    ///
    /// Per layer, this is the offset that will take place. This is generally
    /// useful for 3D style tilemaps that need to give the illusion of depth.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    /// use bevy_math::Vec2;
    ///
    /// let builder = TilemapBuilder::new().layer_offset(Vec2::new(0.5, 0.5));
    /// ```
    pub fn layer_offset(mut self, offset: Vec2) -> TilemapBuilder {
        self.layer_offset = offset;
        self
    }

    /// Sets the tile dimensions.
    ///
    /// Tile dimensions are in pixels. If this is not set then the default of
    /// 32px, 32px is used.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new().texture_dimensions(32, 32);
    /// ```
    pub fn texture_dimensions(mut self, width: u32, height: u32) -> TilemapBuilder {
        self.texture_dimensions = Some(Dimension2::new(width, height));
        self
    }

    /// Sets the tile scale.
    ///
    /// By default this is the size of the texture for width and height which
    /// means that a tile width of 1 is the width in texture pixels for example.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new().tile_scale(32.0, 32.0, 32.0);
    /// ```
    pub fn tile_scale(mut self, width: f32, height: f32, depth: f32) -> TilemapBuilder {
        self.tile_scale = Vec3::new(width, height, depth);
        self
    }

    /// Sets the amount of render layers that sprites can exist on.
    ///
    /// By default there are 20 if this is not set.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new().z_layers(5);
    /// ```
    pub fn z_layers(mut self, layers: usize) -> TilemapBuilder {
        self.z_layers = layers;
        self
    }

    /// Adds a sprite layer that sprites can exist on.
    ///
    /// Takes in a [`LayerKind`] and a Z layer and adds it to the builder.
    ///
    /// If there are more layers than Z layers is set, builder will construct
    /// a tilemap with that many layers instead. In the case that a layer is
    /// added twice to the same Z layer, the first layer will be overwritten by
    /// the later.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new()
    ///     .add_layer(TilemapLayer { kind: LayerKind::Dense, ..Default::default() }, 0)
    ///     .add_layer(TilemapLayer { kind: LayerKind::Sparse, ..Default::default() }, 1)
    ///     .add_layer(TilemapLayer { kind: LayerKind::Sparse, ..Default::default() }, 2);
    /// ```
    ///
    /// [`LayerKind`]: crate::chunk::LayerKind
    pub fn add_layer(mut self, layer: TilemapLayer, sprite_order: usize) -> TilemapBuilder {
        if let Some(layers) = &mut self.layers {
            layers.insert(sprite_order, layer);
        } else {
            let mut layers = HashMap::default();
            layers.insert(sprite_order, layer);
            self.layers = Some(layers);
        }
        self
    }

    /// Sets the texture atlas, this is **required** to be set.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let builder = TilemapBuilder::new().texture_atlas(texture_atlas_handle);
    /// ```
    pub fn texture_atlas(mut self, handle: Handle<TextureAtlas>) -> TilemapBuilder {
        self.texture_atlas = Some(handle);
        self
    }

    /// Sets if you want the tilemap to automatically spawn new chunks.
    ///
    /// This is useful if the tilemap map is meant to be endless or nearly
    /// endless with a defined size. Otherwise, it probably is better to spawn
    /// chunks directly or creating a system that can automatically spawn and
    /// despawn them given context.
    ///
    /// By default this is not enabled.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new().auto_chunk();
    /// ```
    pub fn auto_chunk(mut self) -> Self {
        self.auto_flags.toggle(AutoFlags::AUTO_CHUNK);
        self
    }

    /// Sets the tilemap to automatically spawn new chunks within given
    /// dimensions.
    ///
    /// This enables a feature which spawns just the right amount of chunks to
    /// fit the screen. It is possible that it may not be able to catch all
    /// dimensions but typical uses should be completely fine.
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new().auto_spawn(2, 3);
    /// ```
    pub fn auto_spawn(mut self, width: u32, height: u32) -> Self {
        self.auto_spawn = Some(Dimension2::new(width, height));
        self
    }

    /// Consumes the builder and returns a result.
    ///
    /// If successful a [`TilemapResult`] is return with [tilemap] on
    /// succes or a [`TilemapError`] if there is an issue.
    ///
    /// # Errors
    /// If a texture atlas is not set this is the only way that an error can
    /// occur. If this happens, be sure to use [`texture_atlas`].
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let builder = TilemapBuilder::new().texture_dimensions(32, 32).texture_atlas(texture_atlas_handle);
    ///
    /// assert!(builder.finish().is_ok());
    /// assert!(TilemapBuilder::new().finish().is_err());
    /// ```
    ///
    /// [`texture_atlas`]: TilemapBuilder::texture_atlas
    /// [tilemap]: Tilemap
    /// [`TilemapError`]: TilemapError
    /// [`TilemapResult`]: TilemapResult
    pub fn finish(self) -> TilemapResult<Tilemap> {
        let texture_atlas = if let Some(atlas) = self.texture_atlas {
            atlas
        } else {
            return Err(ErrorKind::MissingTextureAtlas.into());
        };
        let texture_dimensions = if let Some(dimensions) = self.texture_dimensions {
            dimensions
        } else {
            return Err(ErrorKind::MissingTextureDimensions.into());
        };

        let z_layers = if let Some(layers) = &self.layers {
            if self.z_layers > layers.len() {
                self.z_layers
            } else {
                layers.len()
            }
        } else {
            self.z_layers
        };

        let layer_count = if let Some(layers) = &self.layers {
            layers.iter().count()
        } else {
            0
        };
        let chunk_mesh =
            ChunkMesh::new(self.chunk_dimensions, layer_count as u32, self.layer_offset);

        let layers = {
            let mut layers = vec![None; z_layers];
            if let Some(map_layers) = self.layers {
                for (index, layer) in map_layers {
                    if let Some(l) = layers.get_mut(index) {
                        *l = Some(layer)
                    }
                }
            }
            layers
        };

        Ok(Tilemap {
            topology: self.topology,
            dimensions: self.dimensions,
            chunk_dimensions: self.chunk_dimensions,
            layer_offset: self.layer_offset,
            chunk_mesh,
            texture_dimensions,
            layers,
            auto_flags: self.auto_flags,
            auto_spawn: self.auto_spawn,
            custom_flags: Vec::new(),
            texture_atlas,
            chunks: Default::default(),
            entities: Default::default(),
            chunk_events: Default::default(),
            spawned: Default::default(),
        })
    }
}

impl TypeUuid for Tilemap {
    const TYPE_UUID: Uuid = Uuid::from_u128(109481186966523254410691740507722642628);
}

impl Default for Tilemap {
    fn default() -> Self {
        Tilemap {
            topology: GridTopology::Square,
            dimensions: None,
            chunk_dimensions: DEFAULT_CHUNK_DIMENSIONS,
            layer_offset: Vec2::default(),
            chunk_mesh: ChunkMesh::default(),
            texture_dimensions: DEFAULT_TEXTURE_DIMENSIONS,
            layers: vec![
                Some(TilemapLayer {
                    kind: LayerKind::Sparse,
                }),
                None,
                None,
                None,
                None,
            ],
            auto_flags: AutoFlags::NONE,
            auto_spawn: None,
            custom_flags: Vec::new(),
            texture_atlas: Handle::default(),
            chunks: Default::default(),
            entities: Default::default(),
            chunk_events: Default::default(),
            spawned: Default::default(),
        }
    }
}

impl Tilemap {
    /// Constructs a new Tilemap with the required texture atlas and default
    /// configuration.
    ///
    /// This differs from [`default`] in that it requires the texture atlas
    /// handle.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    /// ```
    ///
    /// [`default`]: Tilemap::default
    pub fn new(
        texture_atlas: Handle<TextureAtlas>,
        texture_width: u32,
        texture_height: u32,
    ) -> Tilemap {
        Tilemap {
            texture_atlas,
            texture_dimensions: Dimension2::new(texture_width, texture_height),
            ..Default::default()
        }
    }

    /// Configures the builder with the default settings.
    ///
    /// Is equivalent to [`default`] and [`builder`] method in the
    /// [tilemap]. Start with this then you are able to method chain.
    ///
    /// [`default`]: TilemapBuilder::default
    /// [`builder`]: Tilemap::builder
    /// [tilemap]: Tilemap
    ///
    /// # Examples
    /// ```
    /// use bevy_tilemap::prelude::*;
    ///
    /// let builder = TilemapBuilder::new();
    ///
    /// // Equivalent to...
    ///
    /// let builder = TilemapBuilder::default();
    ///
    /// // Or...
    ///
    /// let builder = Tilemap::builder();
    /// ```
    pub fn builder() -> TilemapBuilder {
        TilemapBuilder::default()
    }

    /// Sets the sprite sheet for use in the tilemap.
    ///
    /// This can be used if the need to swap the sprite sheet for another is
    /// wanted.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// let mut tilemap = Tilemap::default();
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// tilemap.set_texture_atlas(texture_atlas_handle);
    /// ```
    pub fn set_texture_atlas(&mut self, handle: Handle<TextureAtlas>) {
        self.texture_atlas = handle;
    }

    /// Returns a reference of the handle of the texture atlas.
    ///
    /// The Handle is used to get the correct sprite sheet that is used for this
    /// tilemap with the renderer.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    /// let texture_atlas: &Handle<TextureAtlas> = tilemap.texture_atlas();
    /// ```
    pub fn texture_atlas(&self) -> &Handle<TextureAtlas> {
        &self.texture_atlas
    }

    /// Constructs a new chunk and stores it at a coordinate position.
    ///
    /// It requires that you give it either a point. It then automatically sets
    /// both a sized mesh and chunk for use based on the parameters set in the
    /// parent tilemap.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .dimensions(3, 3)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// // Add some chunks.
    /// assert!(tilemap.insert_chunk((0, 0)).is_ok());
    /// assert!(tilemap.insert_chunk((1, 1)).is_ok());
    /// assert!(tilemap.insert_chunk((-2, -2)).is_err());
    ///
    /// assert!(tilemap.contains_chunk((0, 0)));
    /// assert!(tilemap.contains_chunk((1, 1)));
    /// assert!(!tilemap.contains_chunk((-2, -2)));
    /// ```
    /// # Errors
    ///
    /// If the point does not exist in the tilemap, an error is returned. This
    /// can only be returned if you had set the dimensions on the tilemap.
    ///
    /// Also will return an error if the chunk already exists. If this happens
    /// and was intentional, it is best to remove the chunk first. This is
    /// simply a fail safe without actually returning the chunk as it is meant
    /// to be kept internal.
    pub fn insert_chunk<P: Into<Point2>>(&mut self, point: P) -> TilemapResult<()> {
        let point: Point2 = point.into();
        if let Some(dimensions) = &self.dimensions {
            dimensions.check_point(point)?;
        }
        let layer_kinds = self
            .layers
            .iter()
            .map(|x| x.and_then(|y| Some(y.kind)))
            .collect::<Vec<Option<LayerKind>>>();
        let chunk = Chunk::new(point, &layer_kinds, self.chunk_dimensions);
        match self.chunks.insert(point, chunk) {
            Some(_) => Err(ErrorKind::ChunkAlreadyExists(point).into()),
            None => Ok(()),
        }
    }

    /// Returns `true` if the chunk is included in the tilemap.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// assert!(tilemap.insert_chunk((0, 0)).is_ok());
    /// assert!(tilemap.contains_chunk((0, 0)));
    /// assert!(!tilemap.contains_chunk((1, 1)));
    /// ```
    pub fn contains_chunk<P: Into<Point2>>(&mut self, point: P) -> bool {
        let point: Point2 = point.into();
        self.chunks.contains_key(&point)
    }

    #[deprecated(
        since = "0.4.0",
        note = "Please use `add_layer` method instead with the `TilemapLayer` struct"
    )]
    #[doc(hidden)]
    pub fn add_layer_with_kind(
        &mut self,
        kind: LayerKind,
        sprite_order: usize,
    ) -> TilemapResult<()> {
        let layer = TilemapLayer { kind };
        if let Some(some_kind) = self.layers.get_mut(sprite_order) {
            if some_kind.is_some() {
                return Err(ErrorKind::LayerExists(sprite_order).into());
            }
            *some_kind = Some(layer);
        }

        for chunk in self.chunks.values_mut() {
            chunk.add_sprite_layer(&kind, sprite_order, self.chunk_dimensions);
        }

        Ok(())
    }

    /// Adds a layer to the tilemap.
    ///
    /// ***Warning:*** This is very unwise and costly if there are many chunks
    /// in the tilemap. You should only add layers when creating the tilemap.
    /// The meshes for every single chunk has to be recalculated!
    ///
    /// This method creates a layer across all chunks at the specified Z layer.
    /// For ease of use, it by default makes a layer with a dense
    /// [`LayerKind`] which is ideal for layers full of sprites.
    ///
    /// If you want to use a layer that is more performant and less data heavy,
    /// use [`add_layer_with_kind`] with [`LayerKind::Sparse`].
    ///
    /// If the layer is already the specified layer's kind, then nothing
    /// happens.
    ///
    /// # Errors
    ///
    /// If a layer is set and a different layer already exists at that Z layer
    /// then an error is returned regarding that. This is done to prevent
    /// accidental overwrites of a layer.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let layer = TilemapLayer {
    ///    kind: LayerKind::Sparse,
    ///    ..Default::default()
    /// };
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// assert!(tilemap.add_layer(layer, 2).is_ok());
    /// assert!(tilemap.add_layer(layer, 2).is_err());
    /// ```
    ///
    /// [`add_layer_with_kind`]: Tilemap::add_layer_with_kind
    /// [`LayerKind`]: crate::chunk::LayerKind
    /// [`LayerKind::Sparse`]: crate::chunk::LayerKind::Sparse
    pub fn add_layer(&mut self, layer: TilemapLayer, sprite_layer: usize) -> TilemapResult<()> {
        if let Some(inner_layer) = self.layers.get_mut(sprite_layer) {
            if inner_layer.is_some() {
                return Err(ErrorKind::LayerExists(sprite_layer).into());
            }
            *inner_layer = Some(layer);
        }

        let mut layers = 0;
        for layer in self.layers().iter() {
            if layer.is_some() {
                layers += 1;
            }
        }
        let chunk_mesh = ChunkMesh::new(self.chunk_dimensions, layers, self.layer_offset);
        self.chunk_mesh = chunk_mesh;

        self.chunk_events.send(TilemapChunkEvent::AddLayer {
            layer_kind: layer.kind,
            sprite_layer,
        });

        Ok(())
    }

    /// Moves a layer from one Z level to another.
    ///
    /// # Errors
    ///
    /// If the destination exists, it will throw an error. Likewise, if the
    /// origin does not exist, it also will throw an error.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .z_layers(3)
    ///     .texture_dimensions(32, 32)
    ///     .add_layer(TilemapLayer { kind: LayerKind::Dense, ..Default::default() }, 0)
    ///     .add_layer(TilemapLayer { kind: LayerKind::Sparse, ..Default::default() }, 3)
    ///     .finish()
    ///     .unwrap();
    ///
    /// // If we moved this to layer 3, it would instead fail.
    /// assert!(tilemap.move_layer(0, 2).is_ok());
    /// assert!(tilemap.move_layer(3, 2).is_err());
    /// ```
    pub fn move_layer(
        &mut self,
        from_sprite_order: usize,
        to_sprite_order: usize,
    ) -> TilemapResult<()> {
        if let Some(layer) = self.layers.get(to_sprite_order) {
            if layer.is_some() {
                return Err(ErrorKind::LayerExists(to_sprite_order).into());
            }
        };
        if let Some(layer) = self.layers.get(from_sprite_order) {
            if Some(layer).is_none() {
                return Err(ErrorKind::LayerDoesNotExist(from_sprite_order).into());
            }
        }

        self.layers.swap(from_sprite_order, to_sprite_order);
        for chunk in self.chunks.values_mut() {
            chunk.move_sprite_layer(from_sprite_order, to_sprite_order);
        }

        Ok(())
    }

    /// Removes a layer from the tilemap and inner chunks.
    ///
    /// **Warning**: This is destructive if you have tiles that exist on that
    /// layer. If you want to add them back in, better to use the [`move_layer`]
    /// method instead.
    ///
    /// This method takes in a Z layer which is then flagged for deletion. If
    /// the layer already does not exist, it does nothing.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// tilemap.add_layer(TilemapLayer { kind: LayerKind::Sparse, ..Default::default() }, 1);
    ///
    /// tilemap.remove_layer(1);
    /// ```
    ///
    /// [`move_layer`]: Tilemap::move_layer
    pub fn remove_layer(&mut self, z: usize) {
        if let Some(layer) = self.layers.get_mut(z) {
            *layer = None;
        } else {
            return;
        }

        for chunk in self.chunks.values_mut() {
            chunk.remove_sprite_layer(z);
        }
    }

    /// Spawns a chunk at a given index or coordinate.
    ///
    /// Does nothing if the chunk does not exist.
    ///
    /// # Errors
    ///
    /// If the coordinate or index is out of bounds.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .dimensions(1, 1)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// tilemap.insert_chunk((0, 0));
    ///
    /// // Ideally you should want to set some tiles here else nothing will
    /// // display in the render...
    ///
    /// assert!(tilemap.spawn_chunk((0, 0)).is_ok());
    /// assert!(tilemap.spawn_chunk((1, 1)).is_err());
    /// assert!(tilemap.spawn_chunk((-1, -1)).is_err());
    /// ```
    pub fn spawn_chunk<P: Into<Point2>>(&mut self, point: P) -> TilemapResult<()> {
        let point: Point2 = point.into();
        if let Some(dimensions) = &self.dimensions {
            dimensions.check_point(point)?;
        }

        if self.spawned.contains(&(point.x, point.y)) {
            return Ok(());
        } else {
            self.chunk_events.send(TilemapChunkEvent::Spawned { point });
        }

        Ok(())
    }

    /// Spawns a chunk at a given tile point.
    ///
    /// # Errors
    ///
    /// If the coordinate or index is out of bounds or if the chunk does not
    /// exist, an error will be returned.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .chunk_dimensions(32, 32, 1)
    ///     .texture_dimensions(32, 32)
    ///     .dimensions(1, 1)
    ///     .finish()
    ///     .unwrap();
    ///
    /// let point = (15, 15);
    /// let sprite_index = 0;
    /// let tile = Tile { point, sprite_index, ..Default::default() };
    ///
    /// tilemap.insert_tile(tile);
    ///
    /// assert!(tilemap.spawn_chunk_containing_point(point).is_ok());
    /// assert!(tilemap.spawn_chunk_containing_point((16, 16)).is_err());
    /// assert!(tilemap.spawn_chunk_containing_point((-18, -18)).is_err());
    /// ```
    pub fn spawn_chunk_containing_point<P: Into<Point2>>(&mut self, point: P) -> TilemapResult<()> {
        let point = self.point_to_chunk_point(point);
        self.spawn_chunk(point)
    }

    /// De-spawns a spawned chunk at a given index or coordinate.
    ///
    /// If the chunk is not spawned this will result in nothing.
    ///
    /// # Errors
    ///
    /// If the coordinate or index is out of bounds, an error will be returned.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .dimensions(1, 1)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// assert!(tilemap.insert_chunk((0, 0)).is_ok());
    ///
    /// // Ideally you should want to set some tiles here else nothing will
    /// // display in the render...
    ///
    /// assert!(tilemap.spawn_chunk((0, 0)).is_ok());
    ///
    /// // Later a frame or more on...
    ///
    /// assert!(tilemap.despawn_chunk((0, 0)).is_ok());
    /// assert!(tilemap.despawn_chunk((-1, -1)).is_err());
    /// ```
    pub fn despawn_chunk<P: Into<Point2>>(&mut self, point: P) -> TilemapResult<()> {
        let point: Point2 = point.into();
        if let Some(dimensions) = &self.dimensions {
            dimensions.check_point(point)?;
        }

        self.spawned.remove(&(point.x, point.y));

        if self.chunks.get_mut(&point).is_some() {
            self.chunk_events
                .send(TilemapChunkEvent::Despawned { point });
            Ok(())
        } else {
            Err(ErrorKind::MissingChunk.into())
        }
    }

    /// Destructively removes a chunk at a coordinate position and despawns them
    /// if needed.
    ///
    /// Internally, this sends an event to the tilemap's system flagging which
    /// chunks must be removed by index and entity. A chunk is not recoverable
    /// if this action is done.
    ///
    /// Does nothing if the chunk does not exist.
    ///
    /// # Errors
    ///
    /// If the coordinate or index is out of bounds, an error will be returned.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .dimensions(3, 3)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// // Add some chunks.
    /// assert!(tilemap.insert_chunk((0, 0)).is_ok());
    /// assert!(tilemap.insert_chunk((1, 1)).is_ok());
    ///
    /// assert!(tilemap.remove_chunk((0, 0)).is_ok());
    /// assert!(tilemap.remove_chunk((1, 1)).is_ok());
    /// assert!(tilemap.remove_chunk((-2, -2)).is_err());
    /// ```
    pub fn remove_chunk<P: Into<Point2>>(&mut self, point: P) -> TilemapResult<()> {
        let point = point.into();
        self.despawn_chunk(point)?;

        self.chunks.remove(&point);

        Ok(())
    }

    /// Takes a tile point and changes it into a chunk point.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// let tile_point = (15, 15);
    /// let chunk_point = tilemap.point_to_chunk_point(tile_point);
    /// assert_eq!((0, 0), chunk_point);
    ///
    /// let tile_point = (16, 16);
    /// let chunk_point = tilemap.point_to_chunk_point(tile_point);
    /// assert_eq!((1, 1), chunk_point);
    ///
    /// let tile_point = (-16, -16);
    /// let chunk_point = tilemap.point_to_chunk_point(tile_point);
    /// assert_eq!((-0, -0), chunk_point);
    ///
    /// let tile_point = (-17, -17);
    /// let chunk_point = tilemap.point_to_chunk_point(tile_point);
    /// assert_eq!((-1, -1), chunk_point);
    /// ```
    pub fn point_to_chunk_point<P: Into<Point2>>(&self, point: P) -> (i32, i32) {
        let point: Point2 = point.into();
        let width = self.chunk_dimensions.width as f32;
        let height = self.chunk_dimensions.height as f32;
        let x = ((point.x as f32 + width / 2.0) / width).floor() as i32;
        let y = ((point.y as f32 + height / 2.0) / height).floor() as i32;
        (x, y)
    }

    /// Sorts tiles into the chunks they belong to.
    fn sort_tiles_to_chunks<P, I>(
        &mut self,
        tiles: I,
    ) -> TilemapResult<HashMap<Point2, Vec<Tile<Point3>>>>
    where
        P: Into<Point3>,
        I: IntoIterator<Item = Tile<P>>,
    {
        let width = self.chunk_dimensions.width as i32;
        let height = self.chunk_dimensions.height as i32;

        let mut chunk_map: HashMap<Point2, Vec<Tile<Point3>>> = HashMap::default();
        for tile in tiles.into_iter() {
            let global_tile_point: Point3 = tile.point.into();
            let chunk_point: Point2 = self.point_to_chunk_point(global_tile_point).into();

            if let Some(layer) = self.layers.get(tile.sprite_order as usize) {
                if layer.as_ref().is_none() {
                    self.add_layer(TilemapLayer::default(), tile.sprite_order as usize)?;
                }
            } else {
                return Err(ErrorKind::LayerDoesNotExist(tile.sprite_order).into());
            }

            let tile_point = Point3::new(
                global_tile_point.x - (width * chunk_point.x) + (width / 2),
                global_tile_point.y - (height * chunk_point.y) + (height / 2),
                global_tile_point.z,
            );

            let chunk_tile: Tile<Point3> = Tile {
                point: tile_point,
                sprite_order: tile.sprite_order,
                sprite_index: tile.sprite_index,
                tint: tile.tint,
            };
            if let Some(tiles) = chunk_map.get_mut(&chunk_point) {
                tiles.push(chunk_tile);
            } else {
                let tiles = vec![chunk_tile];
                chunk_map.insert(chunk_point, tiles);
            }
        }
        Ok(chunk_map)
    }

    /// Sets many tiles, creating new chunks if needed.
    ///
    /// If setting a single tile is more preferable, then use the [`insert_tile`]
    /// method instead.
    ///
    /// If the chunk does not yet exist, it will create a new one automatically.
    ///
    /// # Errors
    ///
    /// Returns an error if the given coordinate or index is out of bounds, the
    /// layer or chunk does not exist. If either the layer or chunk error occurs
    /// then creating what is missing will resolve it.
    ///
    /// # Examples
    ///
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_render::prelude::*;
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::{prelude::*, chunk::RawTile};
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .dimensions(1, 1)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// tilemap.insert_chunk((0, 0)).unwrap();
    ///
    /// let mut tiles = vec![
    ///     Tile { point: (1, 1), sprite_index: 0, ..Default::default() },
    ///     Tile { point: (2, 2), sprite_index: 1, ..Default::default() },
    ///     Tile { point: (3, 3), sprite_index: 2, ..Default::default() },
    /// ];
    ///
    /// // Set multiple tiles and unwrap the result
    /// tilemap.insert_tiles(tiles).unwrap();
    ///
    /// assert_eq!(tilemap.get_tile((1, 1), 0), Some(&RawTile { index: 0, color: Color::WHITE }));
    /// assert_eq!(tilemap.get_tile((2, 2), 0), Some(&RawTile { index: 1, color: Color::WHITE }));
    /// assert_eq!(tilemap.get_tile((3, 3), 0), Some(&RawTile { index: 2, color: Color::WHITE }));
    /// assert_eq!(tilemap.get_tile((4, 4), 0), None);
    /// ```
    ///
    /// [`insert_tile`]: Tilemap::insert_tile
    pub fn insert_tiles<P, I>(&mut self, tiles: I) -> TilemapResult<()>
    where
        P: Into<Point3>,
        I: IntoIterator<Item = Tile<P>>,
    {
        let chunk_map = self.sort_tiles_to_chunks(tiles)?;
        for (chunk_point, tiles) in chunk_map.into_iter() {
            // Is there a better way to do this? Clippy hates if I don't do it
            // like this talking about constructing regardless yet, here it is,
            // copying stuff regardless because it doesn't like self in the
            // `FnOnce`.
            let layers = self.layers.clone();
            let chunk_dimensions = self.chunk_dimensions;
            let chunk = if self.auto_flags.contains(AutoFlags::AUTO_CHUNK) {
                self.chunks.entry(chunk_point).or_insert_with(|| {
                    let layer_kinds = layers
                        .iter()
                        .map(|x| x.and_then(|y| Some(y.kind)))
                        .collect::<Vec<Option<LayerKind>>>();
                    Chunk::new(chunk_point, &layer_kinds, chunk_dimensions)
                })
            } else {
                match self.chunks.get_mut(&chunk_point) {
                    Some(c) => c,
                    None => return Err(ErrorKind::MissingChunk.into()),
                }
            };

            for tile in tiles.iter() {
                let index = self.chunk_dimensions.encode_point_unchecked(tile.point);
                chunk.set_tile(index, *tile);
            }

            if chunk.mesh().is_some() {
                self.chunk_events.send(TilemapChunkEvent::Modified {
                    point: chunk.point(),
                });
            }
        }

        Ok(())
    }

    /// Sets a single tile at a coordinate position, creating a chunk if necessary.
    ///
    /// If you are setting more than one tile at a time, it is highly
    /// recommended not to run this method! If that is preferred, do use
    /// [`insert_tiles`] instead. Every single tile that is created creates a new
    /// event. With bulk tiles, it creates 1 event for all.
    ///
    /// If the chunk does not yet exist, it will create a new one automatically.
    ///
    /// [`insert_tiles`]: Tilemap::insert_tiles
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_render::prelude::*;
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::{prelude::*, chunk::RawTile};
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// tilemap.insert_chunk((0, 0)).unwrap();
    ///
    /// let point = (9, 3);
    /// let sprite_index = 3;
    /// let tile = Tile { point, sprite_index, ..Default::default() };
    ///
    /// assert!(tilemap.insert_tile(tile).is_ok());
    /// assert_eq!(tilemap.get_tile((9, 3), 0), Some(&RawTile { index: 3, color: Color::WHITE }))
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an error if the given coordinate or index is out of bounds.
    pub fn insert_tile<P: Into<Point3>>(&mut self, tile: Tile<P>) -> TilemapResult<()> {
        let tiles = vec![tile];
        self.insert_tiles(tiles)
    }

    /// Clears the tiles at the specified points from the tilemap.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_render::prelude::*;
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::{prelude::*, chunk::RawTile};
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// assert!(tilemap.insert_chunk((0, 0)).is_ok());
    ///
    /// let mut tiles = vec![
    ///     Tile { point: (1, 1, 0), ..Default::default() },
    ///     Tile { point: (2, 2, 0), ..Default::default() },
    ///     Tile { point: (3, 3, 0), ..Default::default() },
    /// ];
    ///
    /// // Set multiple tiles and unwrap the result
    /// assert!(tilemap.insert_tiles(tiles.clone()).is_ok());
    ///
    /// // Then later on... Do note that if this done in the same frame, the
    /// // tiles will not even exist at all.
    /// let mut to_remove = vec![
    ///     ((1, 1), 0),
    ///     ((2, 2), 0),
    /// ];
    ///
    /// tilemap.clear_tiles(to_remove).unwrap();
    /// assert_eq!(tilemap.get_tile((1, 1, 0), 0), None);
    /// assert_eq!(tilemap.get_tile((2, 2, 0), 0), None);
    /// assert_eq!(tilemap.get_tile((3, 3, 0), 0), Some(&RawTile { index: 0, color: Color::WHITE} ));
    /// ```
    ///
    /// # Errors
    ///
    /// An error can occure if the point is outside of the tilemap. This can
    /// only happen if the tilemap has dimensions.
    pub fn clear_tiles<P, I>(&mut self, points: I) -> TilemapResult<()>
    where
        P: Into<Point3>,
        I: IntoIterator<Item = (P, usize)>,
    {
        let mut tiles = Vec::new();
        for (point, sprite_order) in points {
            tiles.push(Tile {
                point: point.into(),
                sprite_index: 0,
                sprite_order,
                tint: Color::rgba(0.0, 0.0, 0.0, 0.0),
            });
        }
        let chunk_map = self.sort_tiles_to_chunks(tiles)?;
        for (chunk_point, tiles) in chunk_map.into_iter() {
            let chunk = match self.chunks.get_mut(&chunk_point) {
                Some(c) => c,
                None => return Err(ErrorKind::MissingChunk.into()),
            };
            for tile in tiles.iter() {
                let index = self.chunk_dimensions.encode_point_unchecked(tile.point);
                chunk.remove_tile(index, tile.sprite_order, tile.point.z as usize);
            }

            self.chunk_events.send(TilemapChunkEvent::Modified {
                point: chunk.point(),
            });
        }

        Ok(())
    }

    /// Takes a global tile point and returns a tile point in a chunk.
    fn point_to_tile_point(&self, point: Point3) -> Point3 {
        let chunk_point: Point2 = self.point_to_chunk_point(point).into();
        let width = self.chunk_dimensions.width as i32;
        let height = self.chunk_dimensions.height as i32;
        Point3::new(
            point.x - (width * chunk_point.x) + (width / 2),
            point.y - (height * chunk_point.y) + (height / 2),
            point.z,
        )
    }

    /// Clear a single tile at the specified point from the tilemap.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::{prelude::*, chunk::RawTile};
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// assert!(tilemap.insert_chunk((0, 0)).is_ok());
    ///
    /// let point = (3, 1);
    /// let sprite_index = 1;
    /// let tile = Tile { point, sprite_index, ..Default::default() };
    ///
    /// // Set a single tile and unwrap the result
    /// assert!(tilemap.insert_tile(tile).is_ok());
    ///
    /// // Later on...
    /// assert!(tilemap.clear_tile(point, 0).is_ok());
    /// assert_eq!(tilemap.get_tile((3, 1), 0), None);
    /// ```
    ///
    /// # Errors
    ///
    /// An error can occure if the point is outside of the tilemap. This can
    /// only happen if the tilemap has dimensions.
    pub fn clear_tile<P>(&mut self, point: P, sprite_order: usize) -> TilemapResult<()>
    where
        P: Into<Point3>,
    {
        let points = vec![(point, sprite_order)];
        self.clear_tiles(points)
    }

    /// Gets a raw tile from a given point and z order.
    ///
    /// This is different thant he usual [`Tile`] struct in that it only
    /// contains the sprite index and the tint.
    ///
    /// [`Tile`]: crate::tile::Tile
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_render::prelude::*;
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::{prelude::*, chunk::RawTile};
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// tilemap.insert_chunk((0, 0)).unwrap();
    ///
    /// let point = (9, 3);
    /// let sprite_index = 3;
    /// let tile = Tile { point, sprite_index, ..Default::default() };
    ///
    /// assert!(tilemap.insert_tile(tile).is_ok());
    /// assert_eq!(tilemap.get_tile((9, 3), 0), Some(&RawTile { index: 3, color: Color::WHITE }));
    /// assert_eq!(tilemap.get_tile((10, 4), 0), None);
    /// ```
    pub fn get_tile<P>(&mut self, point: P, sprite_order: usize) -> Option<&RawTile>
    where
        P: Into<Point3>,
    {
        let point: Point3 = point.into();
        let chunk_point: Point2 = self.point_to_chunk_point(point).into();
        let tile_point = self.point_to_tile_point(point);
        let chunk = self.chunks.get(&chunk_point)?;
        let index = self.chunk_dimensions.encode_point_unchecked(tile_point);
        chunk.get_tile(index, sprite_order, point.z as usize)
    }

    /// Gets a mutable raw tile from a given point and z order.
    ///
    /// This is different thant he usual [`Tile`] struct in that it only
    /// contains the sprite index and the tint.
    ///
    /// [`Tile`]: crate::tile::Tile
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_render::prelude::*;
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::{prelude::*, chunk::RawTile};
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// tilemap.insert_chunk((0, 0)).unwrap();
    ///
    /// let point = (2, 5);
    /// let sprite_index = 2;
    /// let tile = Tile { point, sprite_index, ..Default::default() };
    ///
    /// assert!(tilemap.insert_tile(tile).is_ok());
    /// assert_eq!(tilemap.get_tile_mut((2, 5), 0), Some(&mut RawTile { index: 2, color: Color::WHITE }));
    /// assert_eq!(tilemap.get_tile_mut((1, 4), 0), None);
    /// ```
    pub fn get_tile_mut<P>(&mut self, point: P, sprite_order: usize) -> Option<&mut RawTile>
    where
        P: Into<Point3>,
    {
        let point: Point3 = point.into();
        let chunk_point: Point2 = self.point_to_chunk_point(point).into();
        let tile_point = self.point_to_tile_point(point);
        let chunk = self.chunks.get_mut(&chunk_point)?;
        let index = self.chunk_dimensions.encode_point_unchecked(tile_point);
        let mut layers = HashMap::default();
        layers.insert(sprite_order, chunk_point);
        self.chunk_events.send(TilemapChunkEvent::Modified {
            point: chunk.point(),
        });
        chunk.get_tile_mut(index, sprite_order, point.z as usize)
    }

    /// Clears a layer of all the tiles.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_render::prelude::*;
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::{prelude::*, chunk::RawTile};
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .dimensions(1, 1)
    ///     .texture_dimensions(32, 32)
    ///     .add_layer( TilemapLayer { kind: LayerKind::Dense}, 0)
    ///     .add_layer( TilemapLayer { kind: LayerKind::Sparse}, 1)
    ///     .finish()
    ///     .unwrap();
    ///
    /// assert!(tilemap.clear_layer(0).is_ok());
    /// assert!(tilemap.clear_layer(1).is_ok());
    /// assert!(tilemap.clear_layer(2).is_err());
    /// ```
    ///
    /// # Errors
    /// Fails if the layer does not exist.
    pub fn clear_layer(&mut self, layer: usize) -> Result<(), TilemapError> {
        if let Some(l) = self.layers.get(layer) {
            if l.is_none() {
                return Err(ErrorKind::LayerDoesNotExist(layer).into());
            }
        } else {
            return Err(ErrorKind::LayerDoesNotExist(layer).into());
        }

        for chunk in self.chunks.values_mut() {
            chunk.clear_layer(layer);
        }

        Ok(())
    }

    /// Returns the center tile, if the tilemap has dimensions.
    ///
    /// Returns `None` if the tilemap has no constrainted dimensions.
    ///
    /// # Examples
    ///
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let mut tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle.clone_weak())
    ///     .dimensions(32, 32)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// let center = tilemap.center_tile_coord();
    ///
    /// // 32 * 32 / 2 = 512
    /// assert_eq!(center, Some((512, 512)));
    ///
    /// let mut tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// let center = tilemap.center_tile_coord();
    ///
    /// assert_eq!(center, None);
    /// ```
    pub fn center_tile_coord(&self) -> Option<(i32, i32)> {
        self.dimensions.map(|dimensions| {
            (
                (dimensions.width / 2 * self.chunk_dimensions.width) as i32,
                (dimensions.height / 2 * self.chunk_dimensions.height) as i32,
            )
        })
    }

    /// The width of the tilemap in chunks, if it has dimensions.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle.clone_weak())
    ///     .dimensions(32, 64)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// let width = tilemap.width();
    ///
    /// assert_eq!(width, Some(32));
    ///
    /// let tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// let width = tilemap.width();
    ///
    /// assert_eq!(width, None);
    /// ```
    pub fn width(&self) -> Option<u32> {
        self.dimensions.map(|dimensions| dimensions.width)
    }

    /// The height of the tilemap in chunks, if it has dimensions.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle.clone_weak())
    ///     .dimensions(32, 64)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// let height = tilemap.height();
    ///
    /// assert_eq!(height, Some(64));
    ///
    /// let tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// let height = tilemap.height();
    ///
    /// assert_eq!(height, None);
    /// ```
    pub fn height(&self) -> Option<u32> {
        self.dimensions.map(|dimensions| dimensions.height)
    }

    /// The width of all the chunks in tiles.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .chunk_dimensions(32, 64, 1)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// let chunk_width: u32 = tilemap.chunk_width();
    ///
    /// assert_eq!(chunk_width, 32);
    /// ```
    pub fn chunk_width(&self) -> u32 {
        self.chunk_dimensions.width
    }

    /// The height of all the chunks in tiles.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .chunk_dimensions(32, 64, 1)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// let chunk_height: u32 = tilemap.chunk_height();
    ///
    /// assert_eq!(chunk_height, 64);
    /// ```
    pub fn chunk_height(&self) -> u32 {
        self.chunk_dimensions.height
    }

    /// The width of a tile in pixels.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .texture_dimensions(32, 64)
    ///     .finish()
    ///     .unwrap();
    ///
    /// let tile_width: u32 = tilemap.tile_width();
    ///
    /// assert_eq!(tile_width, 32);
    /// ```
    pub fn tile_width(&self) -> u32 {
        self.texture_dimensions.width
    }

    /// The height of a tile in pixels.
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .texture_dimensions(32, 64)
    ///     .finish()
    ///     .unwrap();
    ///
    /// let tile_height: u32 = tilemap.tile_height();
    ///
    /// assert_eq!(tile_height, 64);
    /// ```
    pub fn tile_height(&self) -> u32 {
        self.texture_dimensions.height
    }

    /// Gets a reference to a chunk.
    pub(crate) fn get_chunk(&self, point: &Point2) -> Option<&Chunk> {
        self.chunks.get(point)
    }

    /// The topology of the tilemap grid.
    ///
    /// Currently there are 7 topologies which are set with [`GridTopology`]. By
    /// default this is square as it is the most common topology.
    ///
    /// Typically, for most situations squares are used for local maps and hex
    /// is used for war games or world maps. It is easier to define structures
    /// with walls and floors with square but not impossible with hex.
    ///
    /// [`GridTopology`]: crate::chunk::render::GridTopology
    ///
    /// # Examples
    /// ```
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::prelude::*;
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = TilemapBuilder::new()
    ///     .texture_atlas(texture_atlas_handle)
    ///     .topology(GridTopology::HexX)
    ///     .texture_dimensions(32, 32)
    ///     .finish()
    ///     .unwrap();
    ///
    /// assert_eq!(tilemap.topology(), GridTopology::HexX);
    /// ```
    pub fn topology(&self) -> GridTopology {
        self.topology
    }

    /// Returns a reference to the tilemap chunk events.
    ///
    /// This is handy if it is needed to know when new chunks are created which
    /// can then be used to trigger events with other systems. For example,
    /// if you have a system that adds tiles procedurally to the chunks, upon
    /// a chunk event this can be used to trigger the creation of those tiles.
    ///
    /// # Examples
    /// ```
    /// use bevy_app::{prelude::*, Events};
    /// use bevy_asset::{prelude::*, HandleId};
    /// use bevy_sprite::prelude::*;
    /// use bevy_tilemap::{prelude::*, event::TilemapChunkEvent};
    ///
    /// // In production use a strong handle from an actual source.
    /// let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    ///
    /// let tilemap = Tilemap::new(texture_atlas_handle, 32, 32);
    ///
    /// let events: &Events<TilemapChunkEvent> = tilemap.chunk_events();
    /// ```
    pub fn chunk_events(&self) -> &Events<TilemapChunkEvent> {
        &self.chunk_events
    }

    /// Updates the chunk events. This should only be done once per frame.
    pub(crate) fn chunk_events_update(&mut self) {
        self.chunk_events.update()
    }

    /// Returns an option containing a Dimension2.
    pub(crate) fn auto_spawn(&self) -> Option<Dimension2> {
        self.auto_spawn
    }

    /// Sets the auto spawn radius.
    pub(crate) fn set_auto_spawn(&mut self, dimension: Dimension2) {
        self.auto_spawn = Some(dimension);
    }

    /// Returns a copy of the chunk's dimensions.
    pub(crate) fn chunk_dimensions(&self) -> Dimension3 {
        self.chunk_dimensions
    }

    /// Returns a copy of the chunk's texture dimensions.
    pub(crate) fn texture_dimensions(&self) -> Dimension2 {
        self.texture_dimensions
    }

    /// Returns a reference to the hash set of spawned chunks.
    pub(crate) fn spawned_chunks(&self) -> &HashSet<(i32, i32)> {
        &self.spawned
    }

    /// Returns a mutable reference to the spawned chunk points.
    pub(crate) fn spawned_chunks_mut(&mut self) -> &mut HashSet<(i32, i32)> {
        &mut self.spawned
    }

    /// Returns a reference to the layers in the tilemap.
    pub(crate) fn layers(&self) -> Vec<Option<TilemapLayer>> {
        self.layers.clone()
    }

    /// Returns a reference to the chunks in the tilemap.
    pub(crate) fn chunks(&self) -> &HashMap<Point2, Chunk> {
        &self.chunks
    }

    /// Returns a mutable reference to the inner chunks.
    pub(crate) fn chunks_mut(&mut self) -> &mut HashMap<Point2, Chunk> {
        &mut self.chunks
    }

    /// A reference of a chunk's mesh.
    pub(crate) fn chunk_mesh(&self) -> &ChunkMesh {
        &self.chunk_mesh
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    impl Tilemap {
        /// Flags a tilemap chunk that it has been modified. Intended for testing
        /// purposes only.
        pub(crate) fn modify_chunk(&mut self, point: Point2) {
            self.chunk_events
                .send(TilemapChunkEvent::Modified { point });
        }
    }

    // fn new_tilemap_no_auto() -> Tilemap {
    //     let texture_atlas_handle = Handle::weak(Handllet modified_layer = layer_query.get()eId::random::<TextureAtlas>());

    //     let mut tilemap = Tilemap::builder()
    //         .chunk_dimensions(5, 5)
    //         .texture_atlas(texture_atlas_handle)
    //         .finish()
    //         .unwrap();

    //     tilemap
    // }

    // #[test]
    // fn insert_chunks() {
    //     let texture_atlas_handle = Handle::weak(HandleId::random::<TextureAtlas>());
    //     let mut tilemap = Tilemap::new(texture_atlas_handle);

    //     tilemap.insert_chunk(Point2::new(0, 0)).unwrap();
    //     tilemap.insert_chunk(Point2::new(1, -1)).unwrap();
    //     tilemap.insert_chunk(Point2::new(1, 1)).unwrap();
    //     tilemap.insert_chunk(Point2::new(-1, -1)).unwrap();
    // }
}