Skip to content

Commit

Permalink
Merge current into next (#222)
Browse files Browse the repository at this point in the history
* Update zstd, add deps badge (#213)

* Fix some clippy warnings (#214)

* Add basic chunk utilities to infinite layers (#210)

* Add basic chunk utils to infinite layers

* Mark TODOs

* Apply suggestions from code review

Co-authored-by: Thorbjørn Lindeijer <[email protected]>

* Apply interface suggestions from code review

* Update changelog

Co-authored-by: Thorbjørn Lindeijer <[email protected]>

* Update version (#217)

* Update CI (#220)

* Fix test

Co-authored-by: Thorbjørn Lindeijer <[email protected]>
  • Loading branch information
aleokdev and bjorn authored May 2, 2022
1 parent 9cd6105 commit 896bcb9
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 21 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Rust

on:
push:
branches: [ master ]
branches: [ current, next ]
pull_request:
branches: [ master ]
branches: [ current, next ]

env:
CARGO_TERM_COLOR: always
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.10.2]
### Added
- Map-wrapped chunks: `ChunkWrapper`.
- Ability to access infinite tile layer chunks via `InfiniteTileLayer::chunks`,
`InfiniteTileLayerData::chunk_data`, `InfiniteTileLayerData::get_chunk_data` &
`InfiniteTileLayer::get_chunk`, as well as chunk data via `Chunk::get_tile_data` &
`ChunkWrapper::get_tile`.
- `TileLayer::width` & `TileLayer::height` for ergonomic access of width/height.
- `FiniteTileLayerData::get_tile_data`, `InfiniteTileLayerData::get_tile_data`.
- `Default` derived implementation for `Loader` & `FilesystemResourceCache`

### Changed
- Update `zstd` to `0.11.0`.

## [0.10.1]
### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ path = "examples/sfml/main.rs"
base64 = "0.13.0"
xml-rs = "0.8.4"
libflate = "1.1.2"
zstd = { version = "0.10.0", optional = true }
zstd = { version = "0.11.0", optional = true }

[dev-dependencies.sfml]
version = "0.16"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ tiled = "0.11.0"

[![Rust](https://github.com/mapeditor/rs-tiled/actions/workflows/rust.yml/badge.svg)](https://github.com/mapeditor/rs-tiled/actions/workflows/rust.yml)
[![Crates.io](https://img.shields.io/crates/v/tiled.svg)](https://crates.io/crates/tiled)
[![dependency status](https://deps.rs/crate/tiled/0.10.2/status.svg)](https://deps.rs/crate/tiled/0.10.2)

A crate for reading TMX (map) and TSX (tileset) files from the [Tiled Map Editor](http://www.mapeditor.org/) into Rust.
It provides a huge set of features as well as a strong wrapper over internal features such as GIDs.
Expand Down
147 changes: 132 additions & 15 deletions src/layers/tile/infinite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl InfiniteTileLayerData {
let chunk = InternalChunk::new(parser, attrs, e.clone(), c.clone(), tilesets)?;
for x in chunk.x..chunk.x + chunk.width as i32 {
for y in chunk.y..chunk.y + chunk.height as i32 {
let chunk_pos = tile_to_chunk_pos(x, y);
let chunk_pos = Chunk::tile_to_chunk_pos(x, y);
let relative_pos = (x - chunk_pos.0 * Chunk::WIDTH as i32, y - chunk_pos.1 * Chunk::HEIGHT as i32);
let chunk_index = (relative_pos.0 + relative_pos.1 * Chunk::WIDTH as i32) as usize;
let internal_pos = (x - chunk.x, y - chunk.y);
Expand All @@ -63,7 +63,7 @@ impl InfiniteTileLayerData {
///
/// If you want to get a [`Tile`](`crate::Tile`) instead, use [`InfiniteTileLayer::get_tile()`].
pub fn get_tile_data(&self, x: i32, y: i32) -> Option<&LayerTileData> {
let chunk_pos = tile_to_chunk_pos(x, y);
let chunk_pos = Chunk::tile_to_chunk_pos(x, y);
self.chunks
.get(&chunk_pos)
.and_then(|chunk| {
Expand All @@ -76,37 +76,97 @@ impl InfiniteTileLayerData {
})
.flatten()
}
}

fn tile_to_chunk_pos(x: i32, y: i32) -> (i32, i32) {
(
floor_div(x, Chunk::WIDTH as i32),
floor_div(y, Chunk::HEIGHT as i32),
)
/// Returns an iterator over only the data part of the chunks of this tile layer.
///
/// In 99.99% of cases you'll want to use [`InfiniteTileLayer::chunks()`] instead; Using this method is only
/// needed if you *only* require the tile data of the chunks (and no other utilities provided by
/// the map-wrapped [`LayerTile`]), and you are in dire need for that extra bit of performance.
///
/// This iterator doesn't have any particular order.
#[inline]
pub fn chunk_data(&self) -> impl ExactSizeIterator<Item = ((i32, i32), &Chunk)> {
self.chunks.iter().map(|(pos, chunk)| (*pos, chunk))
}

/// Obtains a chunk's data by its position. To obtain the position of the chunk that contains a
/// tile, use [Chunk::tile_to_chunk_pos()].
///
/// In 99.99% of cases you'll want to use [`InfiniteTileLayer::get_chunk()`] instead; Using this method is only
/// needed if you *only* require the tile data of the chunk (and no other utilities provided by
/// the map-wrapped [`LayerTile`]), and you are in dire need for that extra bit of performance.
#[inline]
pub fn get_chunk_data(&self, x: i32, y: i32) -> Option<&Chunk> {
self.chunks.get(&(x, y))
}
}

/// Part of an infinite tile layer.
/// Part of an infinite tile layer's data.
///
/// Has only the tile data contained within and not a reference to the map it is part of. It is for
/// this reason that this type should actually be called `ChunkData`, and so this will change in
/// the next breaking release. In 99.99% of cases you'll actually want to use [`ChunkWrapper`].
// TODO(0.11.0): Rename to ChunkData
#[derive(Debug, PartialEq, Clone)]
pub struct Chunk {
tiles: Box<[Option<LayerTileData>; Self::TILE_COUNT]>,
}

impl Chunk {
/// Internal infinite layer chunk width. Do not rely on this value as it might change between
/// versions.
/// Infinite layer chunk width. This constant might change between versions, not counting as a
/// breaking change.
pub const WIDTH: u32 = 16;
/// Internal infinite layer chunk height. Do not rely on this value as it might change between
/// versions.
/// Infinite layer chunk height. This constant might change between versions, not counting as a
/// breaking change.
pub const HEIGHT: u32 = 16;
/// Internal infinite layer chunk tile count. Do not rely on this value as it might change
/// between versions.
/// Infinite layer chunk tile count. This constant might change between versions, not counting
/// as a breaking change.
pub const TILE_COUNT: usize = Self::WIDTH as usize * Self::HEIGHT as usize;

pub(crate) fn new() -> Self {
Self {
tiles: Box::new([None; Self::TILE_COUNT]),
}
}

/// Obtains the tile data present at the position given relative to the chunk's top-left-most tile.
///
/// If the position given is invalid or the position is empty, this function will return [`None`].
///
/// If you want to get a [`LayerTile`](`crate::LayerTile`) instead, use [`ChunkWrapper::get_tile()`].
pub fn get_tile_data(&self, x: i32, y: i32) -> Option<&LayerTileData> {
if x < Self::WIDTH as i32 && y < Self::HEIGHT as i32 && x >= 0 && y >= 0 {
self.tiles[x as usize + y as usize * Self::WIDTH as usize].as_ref()
} else {
None
}
}

/// Returns the position of the chunk that contains the given tile position.
pub fn tile_to_chunk_pos(x: i32, y: i32) -> (i32, i32) {
(
floor_div(x, Chunk::WIDTH as i32),
floor_div(y, Chunk::HEIGHT as i32),
)
}
}

// TODO(0.11.0): Rename to Chunk
map_wrapper!(
#[doc = "Part of an [`InfiniteTileLayer`]."]
#[doc = "This is the only map-wrapped type that has a `Wrapper` suffix. This will change in a later version."]
ChunkWrapper => Chunk
);

impl<'map> ChunkWrapper<'map> {
/// Obtains the tile present at the position given relative to the chunk's top-left-most tile.
///
/// If the position given is invalid or the position is empty, this function will return [`None`].
pub fn get_tile(&self, x: i32, y: i32) -> Option<LayerTile<'map>> {
self.data
.get_tile_data(x, y)
.map(|data| LayerTile::new(self.map(), data))
}
}

#[derive(Debug, PartialEq, Clone)]
Expand Down Expand Up @@ -167,4 +227,61 @@ impl<'map> InfiniteTileLayer<'map> {
.get_tile_data(x, y)
.map(|data| LayerTile::new(self.map, data))
}

/// Returns an iterator over different parts of this map called [`Chunk`]s.
///
/// These **may not** correspond with the chunks in the TMX file, as the chunk size is
/// implementation defined (see [`Chunk::WIDTH`], [`Chunk::HEIGHT`]).
///
/// The iterator item contains the position of the chunk in chunk coordinates along with a
/// reference to the actual chunk at that position.
///
/// This iterator doesn't have any particular order.
///
/// ## Example
/// ```
/// # use tiled::{Loader, LayerType, TileLayer};
/// use tiled::Chunk;
///
/// # let map = Loader::new()
/// # .load_tmx_map("assets/tiled_base64_zlib_infinite.tmx")
/// # .unwrap();
/// # if let LayerType::Tiles(TileLayer::Infinite(infinite_layer)) =
/// # &map.get_layer(0).unwrap().layer_type()
/// # {
/// for (chunk_pos, chunk) in infinite_layer.chunks() {
/// for x in 0..Chunk::WIDTH as i32 {
/// for y in 0..Chunk::HEIGHT as i32 {
/// if let Some(tile) = chunk.get_tile(x, y) {
/// let tile_pos = (
/// chunk_pos.0 * Chunk::WIDTH as i32 + x,
/// chunk_pos.1 * Chunk::HEIGHT as i32 + y,
/// );
/// println!("At ({}, {}): {:?}", tile_pos.0, tile_pos.1, tile);
/// }
/// }
/// }
/// }
/// # } else {
/// # panic!("It is wrongly recognised as a finite map");
/// # }
/// ```
#[inline]
pub fn chunks(&self) -> impl ExactSizeIterator<Item = ((i32, i32), ChunkWrapper<'map>)> + 'map {
let map: &'map crate::Map = self.map;
self.data
.chunks
.iter()
.map(move |(pos, chunk)| (*pos, ChunkWrapper::new(map, chunk)))
}

/// Obtains a chunk by its position. To obtain the position of the chunk that contains a tile,
/// use [Chunk::tile_to_chunk_pos].
#[inline]
pub fn get_chunk(&self, x: i32, y: i32) -> Option<ChunkWrapper<'map>> {
let map: &'map crate::Map = self.map;
self.data
.get_chunk_data(x, y)
.map(move |data| ChunkWrapper::new(map, data))
}
}
4 changes: 2 additions & 2 deletions src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ impl ObjectData {
let height = h.unwrap_or(0f32);
let rotation = r.unwrap_or(0f32);
let id = id.unwrap_or(0u32);
let name = n.unwrap_or_else(String::new);
let obj_type = t.unwrap_or_else(String::new);
let name = n.unwrap_or_default();
let obj_type = t.unwrap_or_default();
let mut shape = None;
let mut properties = HashMap::new();

Expand Down
2 changes: 1 addition & 1 deletion tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ fn test_just_tileset() {
}

#[test]
fn test_infinite_tileset() {
fn test_infinite_map() {
let r = Loader::new()
.load_tmx_map("assets/tiled_base64_zlib_infinite.tmx")
.unwrap();
Expand Down

0 comments on commit 896bcb9

Please sign in to comment.