diff --git a/assets/tiled_group_layers.tmx b/assets/tiled_group_layers.tmx new file mode 100644 index 00000000..b027a4e8 --- /dev/null +++ b/assets/tiled_group_layers.tmx @@ -0,0 +1,64 @@ + + + + + + + + +6,7,8,0,0,0,0,0, +20,21,22,0,0,0,0,0, +34,35,36,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0 + + + + + + + + + + + +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,6,7,8,0,0, +0,0,0,20,21,22,0,0, +0,0,0,34,35,36,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0 + + + + + + + + + + + + + + + + +0,0,0,48,49,50,0,0, +0,0,0,62,63,64,0,0, +0,0,0,76,77,78,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0 + + + + + diff --git a/examples/main.rs b/examples/main.rs index 4269d8d5..5379f50d 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -30,11 +30,9 @@ fn main() { println!("Infinite tile layer with {} chunks", data.chunks.len()) } }, - tiled::LayerType::ObjectLayer(layer) => { println!("Object layer with {} objects", layer.data().objects.len()) - } - + }, tiled::LayerType::ImageLayer(layer) => { println!( "Image layer with {}", @@ -44,6 +42,12 @@ fn main() { None => "no image".to_owned(), } ) + }, + tiled::LayerType::GroupLayer(layer) => { + println!( + "Group layer with {} sublayers", + layer.layers().len() + ) } } } diff --git a/src/layers/group.rs b/src/layers/group.rs new file mode 100644 index 00000000..940de753 --- /dev/null +++ b/src/layers/group.rs @@ -0,0 +1,121 @@ +use std::path::Path; +use std::collections::HashMap; + +use crate:: { + layers::{LayerData, LayerTag}, + error::TiledError, + properties::{parse_properties, Properties}, + map::MapTilesetGid, + util::*, + MapWrapper, Layer, Map +}; + +#[derive(Debug, PartialEq, Clone)] +pub struct GroupLayerData { + layers: Vec, +} + +impl GroupLayerData { + pub(crate) fn new( + parser: &mut impl Iterator, + infinite: bool, + map_path: &Path, + tilesets: &[MapTilesetGid], + ) -> Result<(Self, Properties), TiledError> { + let mut properties = HashMap::new(); + let mut layers = Vec::new(); + parse_tag!(parser, "group", { + "layer" => |attrs| { + layers.push(LayerData::new( + parser, + attrs, + LayerTag::TileLayer, + infinite, + map_path, + &tilesets, + )?); + Ok(()) + }, + "imagelayer" => |attrs| { + layers.push(LayerData::new( + parser, + attrs, + LayerTag::ImageLayer, + infinite, + map_path, + &tilesets, + )?); + Ok(()) + }, + "objectgroup" => |attrs| { + layers.push(LayerData::new( + parser, + attrs, + LayerTag::ObjectLayer, + infinite, + map_path, + &tilesets, + )?); + Ok(()) + }, + "group" => |attrs| { + layers.push(LayerData::new( + parser, + attrs, + LayerTag::GroupLayer, + infinite, + map_path, + &tilesets, + )?); + Ok(()) + }, + "properties" => |_| { + properties = parse_properties(parser)?; + Ok(()) + }, + }); + Ok(( + Self { layers }, + properties, + )) + } +} + +pub type GroupLayer<'map> = MapWrapper<'map, GroupLayerData>; + +impl<'map> GroupLayer<'map> { + pub fn layers(&self) -> GroupLayerIter { + GroupLayerIter::new(self.map(), self.data()) + } + pub fn get_layer(&self, index: usize) -> Option { + self.data().layers.get(index).map(|data| Layer::new(self.map(), data)) + } +} + +/// An iterator that iterates over all the layers in a group layer, obtained via [`GroupLayer::layers`]. +pub struct GroupLayerIter<'map> { + map: &'map Map, + group: &'map GroupLayerData, + index: usize, +} + +impl<'map> GroupLayerIter<'map> { + fn new(map: &'map Map, group: &'map GroupLayerData) -> Self { + Self { map, group, index: 0 } + } +} + +impl<'map> Iterator for GroupLayerIter<'map> { + type Item = Layer<'map>; + fn next(&mut self) -> Option { + let layer_data = self.group.layers.get(self.index)?; + self.index += 1; + Some(Layer::new(self.map, layer_data)) + } +} + +impl<'map> ExactSizeIterator for GroupLayerIter<'map> { + fn len(&self) -> usize { + self.group.layers.len() - self.index + } +} diff --git a/src/layers/mod.rs b/src/layers/mod.rs index b05004a1..272b4967 100644 --- a/src/layers/mod.rs +++ b/src/layers/mod.rs @@ -10,13 +10,15 @@ mod object; pub use object::*; mod tile; pub use tile::*; +mod group; +pub use group::*; #[derive(Clone, PartialEq, Debug)] pub enum LayerDataType { TileLayer(TileLayerData), ObjectLayer(ObjectLayerData), ImageLayer(ImageLayerData), - // TODO: Support group layers + GroupLayer(GroupLayerData), } #[derive(Clone, Copy)] @@ -24,6 +26,7 @@ pub(crate) enum LayerTag { TileLayer, ObjectLayer, ImageLayer, + GroupLayer, } #[derive(Clone, PartialEq, Debug)] @@ -85,6 +88,10 @@ impl LayerData { let (ty, properties) = ImageLayerData::new(parser, map_path)?; (LayerDataType::ImageLayer(ty), properties) } + LayerTag::GroupLayer => { + let (ty, properties) = GroupLayerData::new(parser, infinite, map_path, tilesets)?; + (LayerDataType::GroupLayer(ty), properties) + } }; Ok(Self { @@ -116,7 +123,7 @@ pub enum LayerType<'map> { TileLayer(TileLayer<'map>), ObjectLayer(ObjectLayer<'map>), ImageLayer(ImageLayer<'map>), - // TODO: Support group layers + GroupLayer(GroupLayer<'map>), } impl<'map> LayerType<'map> { @@ -125,6 +132,7 @@ impl<'map> LayerType<'map> { LayerDataType::TileLayer(data) => Self::TileLayer(TileLayer::new(map, data)), LayerDataType::ObjectLayer(data) => Self::ObjectLayer(ObjectLayer::new(map, data)), LayerDataType::ImageLayer(data) => Self::ImageLayer(ImageLayer::new(map, data)), + LayerDataType::GroupLayer(data) => Self::GroupLayer(GroupLayer::new(map, data)), } } } diff --git a/src/map.rs b/src/map.rs index dddb7d2d..94974707 100644 --- a/src/map.rs +++ b/src/map.rs @@ -103,8 +103,8 @@ impl Map { } /// Get an iterator over all the layers in the map in ascending order of their layer index. - pub fn layers(&self) -> LayerIter { - LayerIter::new(self) + pub fn layers(&self) -> MapLayerIter { + MapLayerIter::new(self) } /// Returns the layer that has the specified index, if it exists. @@ -114,18 +114,18 @@ impl Map { } /// An iterator that iterates over all the layers in a map, obtained via [`Map::layers`]. -pub struct LayerIter<'map> { +pub struct MapLayerIter<'map> { map: &'map Map, index: usize, } -impl<'map> LayerIter<'map> { +impl<'map> MapLayerIter<'map> { fn new(map: &'map Map) -> Self { Self { map, index: 0 } } } -impl<'map> Iterator for LayerIter<'map> { +impl<'map> Iterator for MapLayerIter<'map> { type Item = Layer<'map>; fn next(&mut self) -> Option { @@ -135,7 +135,7 @@ impl<'map> Iterator for LayerIter<'map> { } } -impl<'map> ExactSizeIterator for LayerIter<'map> { +impl<'map> ExactSizeIterator for MapLayerIter<'map> { fn len(&self) -> usize { self.map.layers.len() - self.index } @@ -222,6 +222,17 @@ impl Map { )?); Ok(()) }, + "group" => |attrs| { + layers.push(LayerData::new( + parser, + attrs, + LayerTag::GroupLayer, + infinite, + map_path, + &tilesets, + )?); + Ok(()) + }, "properties" => |_| { properties = parse_properties(parser)?; Ok(()) diff --git a/tests/lib.rs b/tests/lib.rs index 396e01c5..c43bf8cf 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use tiled::{ Color, FilesystemResourceCache, FiniteTileLayerData, Layer, LayerDataType, LayerType, Map, - ObjectLayer, PropertyValue, ResourceCache, TileLayer, TileLayerData, + ObjectLayer, PropertyValue, ResourceCache, TileLayer, TileLayerData, GroupLayer, }; fn as_tile_layer<'map>(layer: Layer<'map>) -> TileLayer<'map> { @@ -25,6 +25,13 @@ fn as_object_layer<'map>(layer: Layer<'map>) -> ObjectLayer<'map> { } } +fn as_group_layer<'map>(layer: Layer<'map>) -> GroupLayer<'map> { + match layer.layer_type() { + LayerType::GroupLayer(x) => x, + _ => panic!("Not a group layer"), + } +} + fn compare_everything_but_tileset_sources(r: &Map, e: &Map) { assert_eq!(r.version, e.version); assert_eq!(r.orientation, e.orientation); @@ -196,10 +203,10 @@ fn test_object_group_property() { let mut cache = FilesystemResourceCache::new(); let r = Map::parse_file("assets/tiled_object_groups.tmx", &mut cache).unwrap(); - let prop_value: bool = if let Some(&PropertyValue::BoolValue(ref v)) = r - .layers() - .nth(1) - .unwrap() + let group_layer = r.get_layer(1).unwrap(); + let group_layer = as_group_layer(group_layer); + let sub_layer = group_layer.get_layer(0).unwrap(); + let prop_value: bool = if let Some(&PropertyValue::BoolValue(ref v)) = sub_layer .data() .properties .get("an object group property") @@ -338,3 +345,50 @@ fn test_tint_color() { }) ); } + +#[test] +fn test_group_layers() { + let mut cache = FilesystemResourceCache::new(); + + let r = Map::parse_file("assets/tiled_group_layers.tmx", &mut cache).unwrap(); + + // Depth = 0 + let layer_tile_1 = r.get_layer(0).unwrap(); + let layer_group_1 = r.get_layer(1).unwrap(); + let layer_group_2 = r.get_layer(2).unwrap(); + + assert_eq!( + Some(&PropertyValue::StringValue("value1".to_string())), + layer_tile_1.data().properties.get("key") + ); + assert_eq!( + Some(&PropertyValue::StringValue("value4".to_string())), + layer_group_1.data().properties.get("key") + ); + assert_eq!( + Some(&PropertyValue::StringValue("value5".to_string())), + layer_group_2.data().properties.get("key") + ); + + // Depth = 1 + let layer_group_1 = as_group_layer(layer_group_1); + let layer_tile_2 = layer_group_1.get_layer(0).unwrap(); + let layer_group_2 = as_group_layer(layer_group_2); + let layer_group_3 = layer_group_2.get_layer(0).unwrap(); + assert_eq!( + Some(&PropertyValue::StringValue("value2".to_string())), + layer_tile_2.data().properties.get("key") + ); + assert_eq!( + Some(&PropertyValue::StringValue("value6".to_string())), + layer_group_3.data().properties.get("key") + ); + + // Depth = 2 + let layer_group_3 = as_group_layer(layer_group_3); + let layer_tile_3 = layer_group_3.get_layer(0).unwrap(); + assert_eq!( + Some(&PropertyValue::StringValue("value3".to_string())), + layer_tile_3.data().properties.get("key") + ); +}