diff --git a/Cargo.lock b/Cargo.lock index 6b553a728cd5f9247dde3213b097a4195c83f013..74ea1ae09b585b7acb7b93ebe274ff6a6341d784 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2233,6 +2233,7 @@ version = "0.14.1" dependencies = [ "bevy", "glam", + "log", "micro_autotile", "num-traits", "quadtree_rs", diff --git a/Cargo.toml b/Cargo.toml index 7ab6a1b629b576a4d14a7213b41c5ce20c891441..50b6872cf3bb26312459eb270808997aa9b5ccd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ no_panic = [] [dependencies] bevy = { optional = true, version = "0.15", default-features = false, features = ["bevy_render", "bevy_sprite", "bevy_asset", "serialize"] } +log = "0.4" thiserror = "2.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/src/assets/asset_events.rs b/src/assets/asset_events.rs index 578d71f5749c607e256bb5fe4e4946ae7b4dcb34..d3f5dd293a3cea394e030de9411bff6384a6134c 100644 --- a/src/assets/asset_events.rs +++ b/src/assets/asset_events.rs @@ -19,9 +19,10 @@ pub fn handle_ldtk_project_events( if let Some(project) = assets.get(*id) { for level in project.get_all_levels() { if level.external_rel_path.is_none() { - - level_index - .insert(level.identifier.clone(), LdtkLevel::from_project(&project, level.clone())); + level_index.insert( + level.identifier.clone(), + LdtkLevel::from_project(project, level.clone()), + ); update_events.send(LevelDataUpdated(level.identifier.clone())); } } diff --git a/src/camera.rs b/src/camera.rs index f60d30ada688e61356ce8fe96fd34c81edffd164..ef87736ad624195c74b3f283d344f0df458a32e7 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -34,7 +34,7 @@ pub struct CameraBounder<'w, 's, Filter: QueryFilter + 'static> { map_query: MapQuery<'w>, query: Query<'w, 's, &'static OrthographicProjection, Filter>, } -impl<'w, 's, Filter: QueryFilter + 'static> CameraBounder<'w, 's, Filter> { +impl<Filter: QueryFilter + 'static> CameraBounder<'_, '_, Filter> { pub fn get_extremeties(&self) -> Option<Rect> { if let Some(bounds) = self.map_query.get_camera_bounds() { if let Ok(proj) = self.query.get_single() { diff --git a/src/ldtk/mod.rs b/src/ldtk/mod.rs index 53aa94e01d45273d1ba04bccfe0aed340928c492..c303cb8ad3146349497c6a419efe61c78aed611d 100644 --- a/src/ldtk/mod.rs +++ b/src/ldtk/mod.rs @@ -19,7 +19,6 @@ mod data_1_4_0; #[cfg(any(feature = "ldtk_1_5_3"))] mod data_1_5_3; - use crate::ldtk; #[cfg(feature = "ldtk_1_0_0")] pub use data_1_0_0::*; @@ -79,8 +78,8 @@ macro_rules! impl_from_bytes { }; } -impl<'a> LdtkFromBytes<'a> for Level {} -impl<'a> LdtkFromBytes<'a> for Project {} +impl LdtkFromBytes<'_> for Level {} +impl LdtkFromBytes<'_> for Project {} impl_from_bytes!(Level); impl_from_bytes!(Project); @@ -89,11 +88,12 @@ impl_from_bytes!(Project); mod _bevy_impl { use super::*; use bevy::asset::io::Reader; - use bevy::asset::{AssetLoader, AsyncReadExt, LoadContext, UntypedAssetId, VisitAssetDependencies}; + use bevy::asset::{ + AssetLoader, AsyncReadExt, LoadContext, UntypedAssetId, VisitAssetDependencies, + }; use bevy::prelude::{Asset, Handle}; use bevy::reflect::TypePath; - impl TypePath for Project { fn type_path() -> &'static str { "micro_ldtk::ldtk::Project" @@ -124,7 +124,6 @@ mod _bevy_impl { impl Asset for Level {} - #[derive(Asset, TypePath)] pub struct LevelSet(pub Vec<Handle<Level>>); @@ -149,12 +148,6 @@ mod _bevy_impl { .levels .iter() .flat_map(|level| { - log::debug!( - "Checking if level is external: {} [{}]", - level.identifier, - level.external_rel_path.is_some() - ); - level .external_rel_path .as_ref() @@ -210,7 +203,7 @@ mod _bevy_impl { } #[cfg(feature = "bevy")] -pub use _bevy_impl::{LdtkLoader, LdtkLevelLoader, LevelSet}; +pub use _bevy_impl::{LdtkLevelLoader, LdtkLoader, LevelSet}; impl Project { pub fn get_all_levels(&self) -> Vec<&Level> { @@ -252,7 +245,6 @@ pub enum LdtkLoadError { pub type LdtkProject = Project; - #[cfg(feature = "autotile")] mod autotile_support { use micro_autotile::{AutoRuleSet, AutoTileRule, TileMatcher, TileOutput, TileStatus}; @@ -284,21 +276,15 @@ mod autotile_support { 1 => Some(AutoTileRule { chance: rule.chance as f32, output: create_output(rule), - matcher: TileMatcher::single(TileStatus::from( - rule.pattern[0], - )), + matcher: TileMatcher::single(TileStatus::from(rule.pattern[0])), }), - _ => { - TileMatcher::try_from(rule.pattern.as_slice()) - .ok().map(|matcher| { - AutoTileRule { - matcher, - chance: rule.chance as f32, - output: create_output(rule), - } - }) - - } + _ => TileMatcher::try_from(rule.pattern.as_slice()) + .ok() + .map(|matcher| AutoTileRule { + matcher, + chance: rule.chance as f32, + output: create_output(rule), + }), }) .collect(); diff --git a/src/lib.rs b/src/lib.rs index c33749b12ba94b6831165e8901a5b6f67431be36..82d7392a3428511aec24e57eacdab8cf79aecf5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,7 @@ mod __plugin { impl<CameraFilter: QueryFilter + Send + Sync + 'static> MicroLDTKCameraPlugin<CameraFilter> { pub fn new() -> Self { Self { - _p: PhantomData::default(), + _p: PhantomData, } } } @@ -93,19 +93,19 @@ mod __plugin { } } -use std::sync::atomic::{AtomicU32, Ordering}; #[cfg(feature = "bevy")] pub use __plugin::{MicroLDTKCameraPlugin, MicroLDTKPlugin}; #[cfg(feature = "bevy")] pub use assets::{LevelIndex, TileMetadata, TilesetIndex}; #[cfg(feature = "bevy")] pub use camera::CameraBounder; -pub use ldtk::{LdtkProject, Level, AutoLayerRuleGroup}; #[cfg(feature = "bevy")] pub use ldtk::LdtkLoader; +pub use ldtk::{AutoLayerRuleGroup, LdtkProject, Level}; pub use map_query::{CameraBounds, InstanceRef, MapQuery}; #[cfg(feature = "autotile")] pub use micro_autotile as autotile; #[cfg(feature = "bevy")] pub use pregen::{write_layer_to_texture, write_map_to_texture, Rasterise}; +use std::sync::atomic::{AtomicU32, Ordering}; pub use system::*; diff --git a/src/map_query.rs b/src/map_query.rs index a38040fe38edab04a1dfa75c1200077e1d2e0b18..b6723ce13e8d82af138fc8904deac9d2b1a8f9d7 100644 --- a/src/map_query.rs +++ b/src/map_query.rs @@ -114,7 +114,7 @@ impl<'a> InstanceRef<'a> { } } -impl<'a, T: ToString> Index<T> for InstanceRef<'a> { +impl<T: ToString> Index<T> for InstanceRef<'_> { type Output = serde_json::Value; fn index(&self, index: T) -> &Self::Output { @@ -152,7 +152,7 @@ impl CameraBounds { } } -impl<'w> MapQuery<'w> { +impl MapQuery<'_> { // --- We put our logic in static accessors because we might source a level other // --- than the currently active one. 'active' methods are a convenience to // --- call the static accessors on whatever the current level is diff --git a/src/map_query_neutral.rs b/src/map_query_neutral.rs index 441000a0eb3af47b8fbbf7ce1271678a8ce2a6d2..c0709f7a1fd965c991c908835cfc7a17250fdcb9 100644 --- a/src/map_query_neutral.rs +++ b/src/map_query_neutral.rs @@ -9,230 +9,230 @@ pub struct MapQuery {} #[derive(Clone)] pub struct InstanceRef<'a> { - pub entity: &'a EntityInstance, + pub entity: &'a EntityInstance, } impl<'a> InstanceRef<'a> { - /// Get the leftmost pixel of this entity's anchor point - pub fn x(&self) -> i64 { - self.entity.px[0] - } - /// Get the topmost pixel of this entity's anchor point - pub fn y(&self) -> i64 { - self.entity.px[1] - } - /// Get the pixel width of this entity - pub fn width(&self) -> i64 { - self.entity.width - } - /// Get the pixel width of this entity - pub fn height(&self) -> i64 { - self.entity.height - } - - /// Get the category that this instance belongs to. Exactly matches the string name - /// found in the LDTK entities list - pub fn get_type(&self) -> &'a String { - &self.entity.identifier - } - /// Try to get a type safe representation of this entity's type, as long as the target type - /// can be produced from a [str] representation - /// - /// ## Example - /// - /// ``` - /// # use std::str::FromStr; - /// # use micro_ldtk::InstanceRef; - /// # use micro_ldtk::ldtk::EntityInstance; - /// - /// #[derive(PartialEq, Debug)] - /// enum MyEntityType { - /// Player, - /// Monster, - /// } - /// - /// impl FromStr for MyEntityType { - /// # type Err = (); - /// fn from_str(s: &str) -> Result<Self, Self::Err> { - /// match s { - /// "player" => Ok(Self::Player), - /// "monster" => Ok(Self::Monster), - /// # _ => panic!("Oh no") - /// } - /// } - /// } - /// - /// let data_from_ldtk: EntityInstance = EntityInstance { - /// identifier: "player".to_string(), - /// // ...other properties - /// # smart_color: "".to_string(), - /// # grid: vec![], - /// # pivot: vec![], - /// # tags: vec![], - /// # tile: None, - /// # world_x: None, - /// # world_y: None, - /// # def_uid: 0, - /// # field_instances: vec![], - /// # height: 0, - /// # iid: "".to_string(), - /// # px: vec![], - /// # width: 0, - /// }; - /// # - /// # let process_ldtk_data = || -> InstanceRef<'_> { - /// # InstanceRef { - /// # entity: &data_from_ldtk, - /// # } - /// # }; - /// - /// let my_entity_type: InstanceRef<'_> = process_ldtk_data(); - /// assert_eq!(my_entity_type.try_get_typed_id(), Ok(MyEntityType::Player)); - /// ``` - pub fn try_get_typed_id<T: FromStr>(&self) -> Result<T, T::Err> { - T::from_str(self.get_type().as_str()) - } - - /// Retrieve an associated property from this instance. Will return [serde_json::Value::Null] - /// if there is no property with the given name - pub fn property(&self, name: impl ToString) -> serde_json::Value { - self[name].clone() - } - - /// Get a reference to the inner instance of this instance ref - pub fn instance_ref(&self) -> &EntityInstance { - self.entity - } + /// Get the leftmost pixel of this entity's anchor point + pub fn x(&self) -> i64 { + self.entity.px[0] + } + /// Get the topmost pixel of this entity's anchor point + pub fn y(&self) -> i64 { + self.entity.px[1] + } + /// Get the pixel width of this entity + pub fn width(&self) -> i64 { + self.entity.width + } + /// Get the pixel width of this entity + pub fn height(&self) -> i64 { + self.entity.height + } + + /// Get the category that this instance belongs to. Exactly matches the string name + /// found in the LDTK entities list + pub fn get_type(&self) -> &'a String { + &self.entity.identifier + } + /// Try to get a type safe representation of this entity's type, as long as the target type + /// can be produced from a [str] representation + /// + /// ## Example + /// + /// ``` + /// # use std::str::FromStr; + /// # use micro_ldtk::InstanceRef; + /// # use micro_ldtk::ldtk::EntityInstance; + /// + /// #[derive(PartialEq, Debug)] + /// enum MyEntityType { + /// Player, + /// Monster, + /// } + /// + /// impl FromStr for MyEntityType { + /// # type Err = (); + /// fn from_str(s: &str) -> Result<Self, Self::Err> { + /// match s { + /// "player" => Ok(Self::Player), + /// "monster" => Ok(Self::Monster), + /// # _ => panic!("Oh no") + /// } + /// } + /// } + /// + /// let data_from_ldtk: EntityInstance = EntityInstance { + /// identifier: "player".to_string(), + /// // ...other properties + /// # smart_color: "".to_string(), + /// # grid: vec![], + /// # pivot: vec![], + /// # tags: vec![], + /// # tile: None, + /// # world_x: None, + /// # world_y: None, + /// # def_uid: 0, + /// # field_instances: vec![], + /// # height: 0, + /// # iid: "".to_string(), + /// # px: vec![], + /// # width: 0, + /// }; + /// # + /// # let process_ldtk_data = || -> InstanceRef<'_> { + /// # InstanceRef { + /// # entity: &data_from_ldtk, + /// # } + /// # }; + /// + /// let my_entity_type: InstanceRef<'_> = process_ldtk_data(); + /// assert_eq!(my_entity_type.try_get_typed_id(), Ok(MyEntityType::Player)); + /// ``` + pub fn try_get_typed_id<T: FromStr>(&self) -> Result<T, T::Err> { + T::from_str(self.get_type().as_str()) + } + + /// Retrieve an associated property from this instance. Will return [serde_json::Value::Null] + /// if there is no property with the given name + pub fn property(&self, name: impl ToString) -> serde_json::Value { + self[name].clone() + } + + /// Get a reference to the inner instance of this instance ref + pub fn instance_ref(&self) -> &EntityInstance { + self.entity + } } -impl<'a, T: ToString> Index<T> for InstanceRef<'a> { - type Output = serde_json::Value; +impl<T: ToString> Index<T> for InstanceRef<'_> { + type Output = serde_json::Value; - fn index(&self, index: T) -> &Self::Output { - let name = index.to_string(); - for field in &self.entity.field_instances { - if field.identifier == name { - return field.value.as_ref().unwrap_or(&serde_json::Value::Null); - } - } + fn index(&self, index: T) -> &Self::Output { + let name = index.to_string(); + for field in &self.entity.field_instances { + if field.identifier == name { + return field.value.as_ref().unwrap_or(&serde_json::Value::Null); + } + } - &serde_json::Value::Null - } + &serde_json::Value::Null + } } #[derive(Copy, Clone, Debug)] pub struct CameraBounds { - pub left: f32, - pub top: f32, - pub bottom: f32, - pub right: f32, + pub left: f32, + pub top: f32, + pub bottom: f32, + pub right: f32, } impl CameraBounds { - pub fn get_min_x(&self, camera_width: f32) -> f32 { - self.left + (camera_width / 2.0) // - (get_ldtk_tile_scale() / 2.0) - } - pub fn get_max_x(&self, camera_width: f32) -> f32 { - self.right - (camera_width / 2.0) // - (get_ldtk_tile_scale() / 2.0) - } - pub fn get_min_y(&self, camera_height: f32) -> f32 { - self.bottom + (camera_height / 2.0) // - (get_ldtk_tile_scale() / 2.0) - } - pub fn get_max_y(&self, camera_height: f32) -> f32 { - self.top - (camera_height / 2.0) // - (get_ldtk_tile_scale() / 2.0) - } + pub fn get_min_x(&self, camera_width: f32) -> f32 { + self.left + (camera_width / 2.0) // - (get_ldtk_tile_scale() / 2.0) + } + pub fn get_max_x(&self, camera_width: f32) -> f32 { + self.right - (camera_width / 2.0) // - (get_ldtk_tile_scale() / 2.0) + } + pub fn get_min_y(&self, camera_height: f32) -> f32 { + self.bottom + (camera_height / 2.0) // - (get_ldtk_tile_scale() / 2.0) + } + pub fn get_max_y(&self, camera_height: f32) -> f32 { + self.top - (camera_height / 2.0) // - (get_ldtk_tile_scale() / 2.0) + } } impl MapQuery { - // --- We put our logic in static accessors because we might source a level other - // --- than the currently active one. 'active' methods are a convenience to - // --- call the static accessors on whatever the current level is - - /// Perform an action on each layer of the given LDTK level - pub fn for_each_layer_of(level: &LdtkLevel, mut cb: impl FnMut(&LdtkLayer)) { - for layer in level.layers() { - cb(layer); - } - } - - /// Retrieve an iterator over every layer in the given level, regardless of type - pub fn get_layers_of(level: &LdtkLevel) -> impl DoubleEndedIterator<Item = &LdtkLayer> { - level.layers() - } - - /// Retrieve a reference to every entity stored in the given level, regardless of which layer it is found on - pub fn get_entities_of(level: &LdtkLevel) -> Vec<&EntityInstance> { - level - .layers() - .flat_map(|layer| layer.as_ref().entity_instances.iter()) - .collect() - } - - /// Retrieve an enhanced wrapper to every entity stored in the given level, regardless of which layer it is found on - pub fn get_instance_refs_of(level: &LdtkLevel) -> Vec<InstanceRef> { - level - .layers() - .flat_map(|layer| { - layer - .as_ref() - .entity_instances - .iter() - .map(|inst| InstanceRef { entity: inst }) - }) - .collect() - } - - /// Retrieve a reference to every entity stored in the given level that matches the specified type name. - /// This must exactly match the name shown in the LDTK entity list - pub fn get_filtered_entities_of( - level: &LdtkLevel, - entity_type: impl ToString, - ) -> Vec<&EntityInstance> { - let e_type = entity_type.to_string(); - level - .layers() - .flat_map(|layer| layer.as_ref().entity_instances.iter()) - .filter(|inst| inst.identifier == e_type) - .collect() - } - - /// Retrieve an enhanced wrapper to every entity stored in the given level that matches the specified type name. - /// This must exactly match the name shown in the LDTK entity list - pub fn get_filtered_instance_refs_of( - level: &LdtkLevel, - entity_type: impl ToString, - ) -> Vec<InstanceRef> { - let e_type = entity_type.to_string(); - level - .layers() - .flat_map(|layer| { - layer - .as_ref() - .entity_instances - .iter() - .map(|inst| InstanceRef { entity: inst }) - }) - .filter(|inst| inst.entity.identifier == e_type) - .collect() - } - - /// Retrieve an owned copy of all entity data in the given level - pub fn get_owned_entities_of(level: &LdtkLevel) -> Vec<EntityInstance> { - level - .layers() - .flat_map(|layer| layer.as_ref().entity_instances.iter().cloned()) - .collect() - } - - /// Use the size of the level to create a zero-based rectangle indicating the boundaries that a camera should - ///stay within to avoid showing any out-of-level space - pub fn get_camera_bounds_of(level: &LdtkLevel) -> CameraBounds { - let level = level.level_ref(); - CameraBounds { - left: 0.0, - top: level.px_hei as f32, - bottom: 0.0, - right: level.px_wid as f32, - } - } + // --- We put our logic in static accessors because we might source a level other + // --- than the currently active one. 'active' methods are a convenience to + // --- call the static accessors on whatever the current level is + + /// Perform an action on each layer of the given LDTK level + pub fn for_each_layer_of(level: &LdtkLevel, mut cb: impl FnMut(&LdtkLayer)) { + for layer in level.layers() { + cb(layer); + } + } + + /// Retrieve an iterator over every layer in the given level, regardless of type + pub fn get_layers_of(level: &LdtkLevel) -> impl DoubleEndedIterator<Item = &LdtkLayer> { + level.layers() + } + + /// Retrieve a reference to every entity stored in the given level, regardless of which layer it is found on + pub fn get_entities_of(level: &LdtkLevel) -> Vec<&EntityInstance> { + level + .layers() + .flat_map(|layer| layer.as_ref().entity_instances.iter()) + .collect() + } + + /// Retrieve an enhanced wrapper to every entity stored in the given level, regardless of which layer it is found on + pub fn get_instance_refs_of(level: &LdtkLevel) -> Vec<InstanceRef> { + level + .layers() + .flat_map(|layer| { + layer + .as_ref() + .entity_instances + .iter() + .map(|inst| InstanceRef { entity: inst }) + }) + .collect() + } + + /// Retrieve a reference to every entity stored in the given level that matches the specified type name. + /// This must exactly match the name shown in the LDTK entity list + pub fn get_filtered_entities_of( + level: &LdtkLevel, + entity_type: impl ToString, + ) -> Vec<&EntityInstance> { + let e_type = entity_type.to_string(); + level + .layers() + .flat_map(|layer| layer.as_ref().entity_instances.iter()) + .filter(|inst| inst.identifier == e_type) + .collect() + } + + /// Retrieve an enhanced wrapper to every entity stored in the given level that matches the specified type name. + /// This must exactly match the name shown in the LDTK entity list + pub fn get_filtered_instance_refs_of( + level: &LdtkLevel, + entity_type: impl ToString, + ) -> Vec<InstanceRef> { + let e_type = entity_type.to_string(); + level + .layers() + .flat_map(|layer| { + layer + .as_ref() + .entity_instances + .iter() + .map(|inst| InstanceRef { entity: inst }) + }) + .filter(|inst| inst.entity.identifier == e_type) + .collect() + } + + /// Retrieve an owned copy of all entity data in the given level + pub fn get_owned_entities_of(level: &LdtkLevel) -> Vec<EntityInstance> { + level + .layers() + .flat_map(|layer| layer.as_ref().entity_instances.iter().cloned()) + .collect() + } + + /// Use the size of the level to create a zero-based rectangle indicating the boundaries that a camera should + ///stay within to avoid showing any out-of-level space + pub fn get_camera_bounds_of(level: &LdtkLevel) -> CameraBounds { + let level = level.level_ref(); + CameraBounds { + left: 0.0, + top: level.px_hei as f32, + bottom: 0.0, + right: level.px_wid as f32, + } + } } diff --git a/src/pregen.rs b/src/pregen.rs index 4078fa2c6d5f4a32d81a3f8d191d03f163dac5a9..d2e58989b487d742e19a040c986af65c89d64c4f 100644 --- a/src/pregen.rs +++ b/src/pregen.rs @@ -1,6 +1,6 @@ -use bevy::prelude::{Image, TextureAtlas}; -use bevy::render::render_resource::TextureFormat; use bevy::image::TextureFormatPixelInfo; +use bevy::prelude::Image; +use bevy::render::render_resource::TextureFormat; use bevy::sprite::TextureAtlasLayout; use crate::{ diff --git a/src/system/types.rs b/src/system/types.rs index b7497b8ed29bbc22584a28c225d57fc934fadc38..0cb96c3a21580d737e2597e29194dc34f55cb79d 100644 --- a/src/system/types.rs +++ b/src/system/types.rs @@ -18,8 +18,8 @@ use serde_json::{Map, Number, Value}; use crate::ldtk::{EntityInstance, FieldInstance, LayerInstance, Level, Project, TileInstance}; use crate::system::Indexer; -use crate::{get_ldtk_tile_scale, px_to_grid}; use crate::MapQuery; +use crate::{get_ldtk_tile_scale, px_to_grid}; mod compat; @@ -73,7 +73,7 @@ impl<'a> TileRef<'a> { } } -impl<'a> Debug for TileRef<'a> { +impl Debug for TileRef<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("TileRef") .field("gid", &self.gid()) @@ -122,9 +122,12 @@ impl LdtkLevel { #[cfg(feature = "_supports_ui_tags")] { level_data.layers_mut().for_each(|layer| { - if let Some(def) = project.defs.layers.iter().find(|inner| { - inner.uid == layer.layer.layer_def_uid - }) { + if let Some(def) = project + .defs + .layers + .iter() + .find(|inner| inner.uid == layer.layer.layer_def_uid) + { layer.tags = def.ui_filter_tags.clone(); } }); @@ -176,7 +179,6 @@ impl LdtkLevel { pub fn get_indexer(&self) -> Indexer { Indexer::new(px_to_grid(self.level.px_wid), px_to_grid(self.level.px_hei)) } - } #[derive(Debug, Clone, Default)] diff --git a/src/system/utils.rs b/src/system/utils.rs index 0b03580a841275a25c0489f9d02d34c3a43c5577..e3469ad24468173ca34cbe139d90a8851bf674ee 100644 --- a/src/system/utils.rs +++ b/src/system/utils.rs @@ -56,7 +56,7 @@ impl Indexer { pub fn index(&self, x: impl AsPrimitive<i64>, y: impl AsPrimitive<i64>) -> usize { match (self.width(), self.height()) { (0, _) | (_, 0) => 0, - (w, _) => ((y.as_() * w) + x.as_()).as_(), + (w, _) => ((y.as_() * w) + x.as_()).as_(), } } @@ -76,10 +76,10 @@ impl Indexer { let index = index.as_(); match index { 0 => (0, 0), - _ => ( + _ => ( index.checked_rem(self.width).unwrap_or(0) as usize, index.checked_div(self.width).unwrap_or(0) as usize, - ) + ), } } diff --git a/tests/indexer.rs b/tests/indexer.rs index c20979ef41500c10fe5346e5955e25b1e03e8d39..9a786d811a1b2f3aa8632ccecce315c2d7537b49 100644 --- a/tests/indexer.rs +++ b/tests/indexer.rs @@ -1,5 +1,5 @@ -use micro_ldtk::Indexer; use glam::IVec2; +use micro_ldtk::Indexer; use num_traits::AsPrimitive; use test_case::test_case; @@ -10,9 +10,12 @@ use test_case::test_case; #[test_case(2, 2 => 12)] #[test_case(1i32, 1i32 => 6)] #[test_case(2.5f32, 1.2f32 => 7)] // Floating point coordinates are truncated -fn test_indexer_calculates_correct_index(x: impl AsPrimitive<i64>, y: impl AsPrimitive<i64>) -> usize { - let indexer = Indexer::new(5, 4); - indexer.index(x, y) +fn test_indexer_calculates_correct_index( + x: impl AsPrimitive<i64>, + y: impl AsPrimitive<i64>, +) -> usize { + let indexer = Indexer::new(5, 4); + indexer.index(x, y) } #[test_case(0 => (0, 0))] @@ -22,8 +25,8 @@ fn test_indexer_calculates_correct_index(x: impl AsPrimitive<i64>, y: impl AsPri #[test_case(10 => (0, 2))] #[test_case(14 => (4, 2))] fn test_indexer_reverse(index: usize) -> (usize, usize) { - let indexer = Indexer::new(5, 3); - indexer.reverse(index) + let indexer = Indexer::new(5, 3); + indexer.reverse(index) } #[test_case(0, 0)] @@ -32,11 +35,11 @@ fn test_indexer_reverse(index: usize) -> (usize, usize) { #[test_case(2, 2)] #[test_case(4, 2)] fn test_index_is_reflexive(x: usize, y: usize) { - let indexer = Indexer::new(5, 3); - let idx = indexer.index(x, y); - let (x_rev, y_rev) = indexer.reverse(idx); + let indexer = Indexer::new(5, 3); + let idx = indexer.index(x, y); + let (x_rev, y_rev) = indexer.reverse(idx); - assert_eq!((x, y), (x_rev, y_rev)); + assert_eq!((x, y), (x_rev, y_rev)); } #[test_case(5, -1)] @@ -46,43 +49,40 @@ fn test_index_is_reflexive(x: usize, y: usize) { #[test_case(-12, 3)] #[test_case(-00, 123_000_000)] fn test_indexer_index_checked_returns_none_for_out_of_bounds(x: i64, y: i64) { - let indexer = Indexer::new(10, 10); - assert_eq!(indexer.index_checked(x, y), None); + let indexer = Indexer::new(10, 10); + assert_eq!(indexer.index_checked(x, y), None); } - #[test] fn test_indexer_with_zero_dimensions() { + // Create an indexer with zero width + let zero_width_indexer = Indexer::new(0, 10); - // Create an indexer with zero width - let zero_width_indexer = Indexer::new(0, 10); + assert_eq!(zero_width_indexer.width(), 0); + assert_eq!(zero_width_indexer.height(), 10); - assert_eq!(zero_width_indexer.width(), 0); - assert_eq!(zero_width_indexer.height(), 10); + // Zero width should make all x coordinates invalid + assert!(!zero_width_indexer.is_valid(0, 5)); + assert_eq!(zero_width_indexer.index_checked(0, 5), None); - // Zero width should make all x coordinates invalid - assert!(!zero_width_indexer.is_valid(0, 5)); - assert_eq!(zero_width_indexer.index_checked(0, 5), None); + // Index calculation with zero width + assert_eq!(zero_width_indexer.index(2, 3), 0); // 3 * 0 + 2 = 0 - // Index calculation with zero width - assert_eq!(zero_width_indexer.index(2, 3), 0); // 3 * 0 + 2 = 0 + // Reverse calculation with zero width + let (x, y) = zero_width_indexer.reverse(5); + assert_eq!(x, 0); // Any index % 0 is treated as 0 due to .max(0) + assert_eq!(y, 0); // Any index / 0 is treated as 0 due to .max(0) - // Reverse calculation with zero width - let (x, y) = zero_width_indexer.reverse(5); - assert_eq!(x, 0); // Any index % 0 is treated as 0 due to .max(0) - assert_eq!(y, 0); // Any index / 0 is treated as 0 due to .max(0) + // Create an indexer with zero height + let zero_height_indexer = Indexer::new(10, 0); + assert_eq!(zero_height_indexer.width(), 10); + assert_eq!(zero_height_indexer.height(), 0); - // Create an indexer with zero height - let zero_height_indexer = Indexer::new(10, 0); - assert_eq!(zero_height_indexer.width(), 10); - assert_eq!(zero_height_indexer.height(), 0); + // Zero height should make all y coordinates invalid + assert!(!zero_height_indexer.is_valid(5, 0)); + assert_eq!(zero_height_indexer.index_checked(5, 0), None); - // Zero height should make all y coordinates invalid - assert!(!zero_height_indexer.is_valid(5, 0)); - assert_eq!(zero_height_indexer.index_checked(5, 0), None); - - // Test flip_y with zero height - let flipped = zero_height_indexer.flip_y(IVec2::new(5, 3)); - assert_eq!(flipped, IVec2::new(5, -3)); // 0 - 3 = -3 + // Test flip_y with zero height + let flipped = zero_height_indexer.flip_y(IVec2::new(5, 3)); + assert_eq!(flipped, IVec2::new(5, -3)); // 0 - 3 = -3 } -