From 24a2956c2f166942073e08343daa434b344e2c5f Mon Sep 17 00:00:00 2001
From: Louis Capitanchik <contact@louiscap.co>
Date: Tue, 17 Jan 2023 19:33:58 +0000
Subject: [PATCH] Wrap levels and layers, preprocess layers to extract tile
 info

---
 src/assets.rs    |  14 ++--
 src/lib.rs       |  16 +++-
 src/map_query.rs | 207 ++++++++--------------------------------------
 src/types.rs     | 210 +++++++++++++++++++++++++++++++++++++++++++++++
 src/utils.rs     |  32 +++-----
 5 files changed, 274 insertions(+), 205 deletions(-)
 create mode 100644 src/types.rs

diff --git a/src/assets.rs b/src/assets.rs
index e1d62ea..b9101ef 100644
--- a/src/assets.rs
+++ b/src/assets.rs
@@ -5,10 +5,11 @@ use anyhow::Error;
 use bevy::asset::{AssetEvent, AssetLoader, Assets, BoxedFuture, LoadContext, LoadedAsset};
 use bevy::prelude::{EventReader, Res, ResMut, Resource};
 use bevy::reflect::TypeUuid;
-use ldtk_rust::{Level, Project, TilesetDefinition};
+use ldtk_rust::Project;
 use serde_json::Value;
 
 use crate::utils::SerdeClone;
+use crate::LdtkLevel;
 
 #[derive(TypeUuid)]
 #[uuid = "292a8918-9487-11ed-8dd2-43b6f36cb076"]
@@ -51,9 +52,9 @@ impl AssetLoader for LdtkLoader {
 }
 
 #[derive(Resource, Default)]
-pub struct LevelIndex(pub HashMap<String, Level>);
+pub struct LevelIndex(pub HashMap<String, LdtkLevel>);
 impl Deref for LevelIndex {
-	type Target = HashMap<String, Level>;
+	type Target = HashMap<String, LdtkLevel>;
 	fn deref(&self) -> &Self::Target {
 		&self.0
 	}
@@ -92,7 +93,10 @@ pub fn handle_ldtk_project_events(
 			AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
 				if let Some(LdtkProject(project)) = assets.get(handle) {
 					for level in &project.levels {
-						level_index.insert(level.identifier.clone(), level.serde_clone());
+						level_index.insert(
+							level.identifier.clone(),
+							LdtkLevel::from(level.serde_clone()),
+						);
 					}
 
 					for tileset in &project.defs.tilesets {
@@ -100,7 +104,7 @@ pub fn handle_ldtk_project_events(
 						for custom in &tileset.custom_data {
 							tile_meta.insert(
 								custom.tile_id,
-								serde_json::from_str(&*custom.data).unwrap(),
+								serde_json::from_str(&custom.data).unwrap(),
 							);
 						}
 						tilset_index.insert(tileset.identifier.clone(), TileMetadata(tile_meta));
diff --git a/src/lib.rs b/src/lib.rs
index fbadc38..53bb4f2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,7 @@ mod assets;
 mod camera;
 mod locator;
 mod map_query;
+mod types;
 pub(crate) mod utils;
 // mod spawning;
 
@@ -45,6 +46,13 @@ mod __plugin {
 			}
 		}
 	}
+	impl<CameraFilter: ReadOnlyWorldQuery + Send + Sync + 'static> Default
+		for MicroLDTKCameraPlugin<CameraFilter>
+	{
+		fn default() -> Self {
+			Self::new()
+		}
+	}
 
 	pub struct MicroLDTKCameraPlugin<CameraFilter: ReadOnlyWorldQuery> {
 		_p: PhantomData<CameraFilter>,
@@ -65,7 +73,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
 
 pub use __plugin::{MicroLDTKCameraPlugin, MicroLDTKPlugin};
 pub use assets::{LdtkLoader, LdtkProject, LevelIndex, TileMetadata, TilesetIndex};
-pub use map_query::{CameraBounds, LayerRef, MapQuery, TileRef};
-pub use utils::{
-	entity_centre, entity_to_worldspace, grid_to_px, px_to_grid, ActiveLevel, WorldLinked,
-};
+pub use camera::CameraBounder;
+pub use map_query::{CameraBounds, MapQuery};
+pub use types::{LdtkLayer, LdtkLevel, SpatialIndex, TileFlip, TileRef};
+pub use utils::{entity_centre, grid_to_px, px_to_grid, ActiveLevel, WorldLinked};
diff --git a/src/map_query.rs b/src/map_query.rs
index 2238a41..6ad34d7 100644
--- a/src/map_query.rs
+++ b/src/map_query.rs
@@ -1,85 +1,23 @@
-use std::fmt::{Debug, Formatter};
+use std::fmt::Debug;
 use std::marker::PhantomData;
-use std::path::Path;
 use std::str::FromStr;
 
 use bevy::ecs::system::SystemParam;
 use bevy::prelude::*;
-use ldtk_rust::{EntityInstance, LayerInstance, Level, TileInstance};
-use num_traits::AsPrimitive;
+use ldtk_rust::EntityInstance;
 
-// use crate::assets::level_index::LevelIndex;
-use crate::assets::{LdtkProject, LevelIndex};
-use crate::utils::{ActiveLevel, Indexer, SerdeClone};
-use crate::{get_ldtk_tile_scale, px_to_grid};
+use crate::assets::LevelIndex;
+use crate::utils::{ActiveLevel, SerdeClone};
+use crate::{get_ldtk_tile_scale, LdtkLayer, LdtkLevel};
 
 #[derive(SystemParam)]
 pub struct MapQuery<'w, 's> {
-	assets: Res<'w, Assets<LdtkProject>>,
 	active: Option<Res<'w, ActiveLevel>>,
 	index: Res<'w, LevelIndex>,
 	#[system_param(ignore)]
 	_e: PhantomData<&'s ()>,
 }
 
-pub struct TileRef<'a> {
-	pub tile: &'a TileInstance,
-}
-
-#[repr(C)]
-#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default, Debug)]
-pub enum TileFlip {
-	#[default]
-	None = 0,
-	Horizontal = 1,
-	Vertical = 2,
-	Both = 3,
-}
-
-impl TileFlip {
-	pub fn is_horizontal(&self) -> bool {
-		match self {
-			Self::None | Self::Vertical => false,
-			Self::Horizontal | Self::Both => true,
-		}
-	}
-	pub fn is_vertical(&self) -> bool {
-		match self {
-			Self::None | Self::Horizontal => false,
-			Self::Vertical | Self::Both => true,
-		}
-	}
-}
-
-impl<'a> TileRef<'a> {
-	pub fn new(tile: &'a TileInstance) -> Self {
-		TileRef { tile }
-	}
-	pub fn gid(&self) -> usize {
-		(self.tile.src[0] * self.tile.src[1]).unsigned_abs() as usize
-	}
-	pub fn get_flip(&self) -> TileFlip {
-		match self.tile.f {
-			1 => TileFlip::Horizontal,
-			2 => TileFlip::Vertical,
-			3 => TileFlip::Both,
-			_ => TileFlip::None,
-		}
-	}
-}
-
-impl<'a> Debug for TileRef<'a> {
-	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-		f.debug_struct("TileRef")
-			.field("gid", &self.gid())
-			.field("tile.t", &self.tile.t)
-			.field("tile.d", &self.tile.d)
-			.field("tile.px", &self.tile.px)
-			.field("tile.src", &self.tile.src)
-			.finish()
-	}
-}
-
 pub struct InstanceRef<'a> {
 	pub entity: &'a EntityInstance,
 }
@@ -98,7 +36,7 @@ impl<'a> InstanceRef<'a> {
 				return field
 					.value
 					.as_ref()
-					.map(|v| v.clone())
+					.cloned()
 					.unwrap_or(serde_json::Value::Null);
 			}
 		}
@@ -107,77 +45,6 @@ impl<'a> InstanceRef<'a> {
 	}
 }
 
-pub struct LayerRef<'a> {
-	pub idx: usize,
-	pub indexer: Indexer,
-	pub layer: &'a LayerInstance,
-}
-
-impl<'a> LayerRef<'a> {
-	pub fn new(idx: usize, indexer: Indexer, layer: &'a LayerInstance) -> Self {
-		LayerRef {
-			layer,
-			indexer,
-			idx,
-		}
-	}
-	pub fn has_tiles(&self) -> bool {
-		!(self.layer.auto_layer_tiles.is_empty() && self.layer.grid_tiles.is_empty())
-	}
-	pub fn for_each_tile(&self, mut cb: impl FnMut(i64, i64, TileRef)) {
-		self.layer
-			.grid_tiles
-			.iter()
-			.chain(self.layer.auto_layer_tiles.iter())
-			.for_each(|tile: &TileInstance| {
-				let (x, y) = match tile.px.as_slice() {
-					&[x, y] => (px_to_grid(x), px_to_grid(y)),
-					_ => {
-						return;
-					}
-				};
-
-				cb(x, y, TileRef::new(tile));
-			});
-	}
-	pub fn get_z_delta(&self) -> f32 {
-		(self.idx as f32) / 100.0
-	}
-
-	pub fn get_tile_at(
-		&self,
-		x: impl AsPrimitive<isize>,
-		y: impl AsPrimitive<isize>,
-	) -> Option<TileRef> {
-		let idx = self.indexer.index(x, y);
-
-		match self.layer.grid_tiles.is_empty() {
-			true => self
-				.layer
-				.auto_layer_tiles
-				.get(idx)
-				.map(|tile| TileRef::new(tile)),
-			false => self
-				.layer
-				.grid_tiles
-				.get(idx)
-				.map(|tile| TileRef::new(tile)),
-		}
-	}
-
-	/// Returns the inferred name of the tileset used for this layer. This is assumed to be the
-	/// name of the tileset file, without the preceding path segments or the file extension. Case
-	/// remains unchanged
-	pub fn infer_tileset_name(&self) -> Option<String> {
-		self.layer.tileset_rel_path.as_ref().and_then(|path| {
-			Path::new(path)
-				.file_stem()
-				.and_then(|stem| stem.to_str())
-				.map(String::from)
-		})
-	}
-}
-
 #[derive(Copy, Clone, Debug)]
 pub struct CameraBounds {
 	pub left: f32,
@@ -206,24 +73,15 @@ impl<'w, 's> MapQuery<'w, 's> {
 	// --- than the currently active one. 'active' methods are a convenience to
 	// --- call the static accessors on whatever the current level is
 
-	pub fn get_indexer_for(level: &Level) -> Indexer {
-		Indexer::new(px_to_grid(level.px_wid), px_to_grid(level.px_hei))
-	}
-
-	pub fn for_each_layer_of(level: &Level, mut cb: impl FnMut(LayerRef)) {
-		if let Some(layers) = level.layer_instances.as_ref() {
-			for layer in layers.iter().rev().enumerate() {
-				cb(LayerRef::new(
-					layer.0,
-					MapQuery::get_indexer_for(level),
-					layer.1,
-				));
-			}
+	pub fn for_each_layer_of(level: &LdtkLevel, mut cb: impl FnMut(&LdtkLayer)) {
+		for layer in level.layers().rev() {
+			cb(layer);
 		}
 	}
 
-	pub fn get_entities_of(level: &Level) -> Vec<&EntityInstance> {
+	pub fn get_entities_of(level: &LdtkLevel) -> Vec<&EntityInstance> {
 		level
+			.level_ref()
 			.layer_instances
 			.as_ref()
 			.map(|layers| {
@@ -235,8 +93,9 @@ impl<'w, 's> MapQuery<'w, 's> {
 			.unwrap_or_default()
 	}
 
-	pub fn get_instance_refs_of(level: &Level) -> Vec<InstanceRef> {
+	pub fn get_instance_refs_of(level: &LdtkLevel) -> Vec<InstanceRef> {
 		level
+			.level_ref()
 			.layer_instances
 			.as_ref()
 			.map(|layers| {
@@ -250,34 +109,32 @@ impl<'w, 's> MapQuery<'w, 's> {
 	}
 
 	pub fn get_filtered_entities_of(
-		level: &Level,
+		level: &LdtkLevel,
 		entity_type: impl ToString,
 	) -> Vec<&EntityInstance> {
 		let e_type = entity_type.to_string();
-		match level.layer_instances.as_ref() {
-			Some(inst) => inst
-				.iter()
-				.flat_map(|layer| layer.entity_instances.iter())
-				.filter(|inst| inst.identifier == e_type)
-				.collect(),
-			None => Vec::new(),
-		}
+		level
+			.layers()
+			.flat_map(|layer| layer.as_ref().entity_instances.iter())
+			.filter(|inst| inst.identifier == e_type)
+			.collect()
 	}
 
-	pub fn get_owned_entities_of(level: &Level) -> Vec<EntityInstance> {
+	pub fn get_owned_entities_of(level: &LdtkLevel) -> Vec<EntityInstance> {
 		level
-			.layer_instances
-			.as_ref()
-			.map(|layers| {
-				layers
+			.layers()
+			.flat_map(|layer| {
+				layer
+					.as_ref()
+					.entity_instances
 					.iter()
-					.flat_map(|layer| layer.entity_instances.iter().map(|inst| inst.serde_clone()))
-					.collect()
+					.map(|inst| inst.serde_clone())
 			})
-			.unwrap_or_default()
+			.collect()
 	}
 
-	pub fn get_camera_bounds_of(level: &Level) -> CameraBounds {
+	pub fn get_camera_bounds_of(level: &LdtkLevel) -> CameraBounds {
+		let level = level.level_ref();
 		CameraBounds {
 			left: 0.0,
 			top: level.px_hei as f32,
@@ -286,7 +143,7 @@ impl<'w, 's> MapQuery<'w, 's> {
 		}
 	}
 
-	pub fn get_active_level(&self) -> Option<&Level> {
+	pub fn get_active_level(&self) -> Option<&LdtkLevel> {
 		self.active
 			.as_ref()
 			.and_then(|index| self.index.get(&index.map))
@@ -294,7 +151,7 @@ impl<'w, 's> MapQuery<'w, 's> {
 
 	pub fn get_entities(&self) -> Vec<&EntityInstance> {
 		self.get_active_level()
-			.map(|level| MapQuery::get_entities_of(level))
+			.map(MapQuery::get_entities_of)
 			.unwrap_or_default()
 	}
 
@@ -302,7 +159,7 @@ impl<'w, 's> MapQuery<'w, 's> {
 		self.get_active_level().map(MapQuery::get_camera_bounds_of)
 	}
 
-	pub fn for_each_layer(&self, mut cb: impl FnMut(LayerRef)) {
+	pub fn for_each_layer(&self, cb: impl FnMut(&LdtkLayer)) {
 		if let Some(level) = self.get_active_level() {
 			Self::for_each_layer_of(level, cb);
 		}
diff --git a/src/types.rs b/src/types.rs
new file mode 100644
index 0000000..7fa9a4b
--- /dev/null
+++ b/src/types.rs
@@ -0,0 +1,210 @@
+use std::fmt::{Debug, Formatter};
+use std::path::Path;
+
+use bevy::math::{IVec2, UVec2};
+use bevy::utils::HashMap;
+use ldtk_rust::{LayerInstance, Level, TileInstance};
+use num_traits::AsPrimitive;
+
+use crate::utils::{Indexer, SerdeClone};
+use crate::{get_ldtk_tile_scale, px_to_grid};
+
+pub struct TileRef<'a> {
+	pub tile: &'a TileInstance,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default, Debug)]
+pub enum TileFlip {
+	#[default]
+	None = 0,
+	Horizontal = 1,
+	Vertical = 2,
+	Both = 3,
+}
+
+impl TileFlip {
+	pub fn is_horizontal(&self) -> bool {
+		match self {
+			Self::None | Self::Vertical => false,
+			Self::Horizontal | Self::Both => true,
+		}
+	}
+	pub fn is_vertical(&self) -> bool {
+		match self {
+			Self::None | Self::Horizontal => false,
+			Self::Vertical | Self::Both => true,
+		}
+	}
+}
+
+impl<'a> TileRef<'a> {
+	pub fn new(tile: &'a TileInstance) -> Self {
+		TileRef { tile }
+	}
+	pub fn gid(&self) -> usize {
+		(self.tile.src[0] * self.tile.src[1]).unsigned_abs() as usize
+	}
+	pub fn get_flip(&self) -> TileFlip {
+		match self.tile.f {
+			1 => TileFlip::Horizontal,
+			2 => TileFlip::Vertical,
+			3 => TileFlip::Both,
+			_ => TileFlip::None,
+		}
+	}
+}
+
+impl<'a> Debug for TileRef<'a> {
+	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+		f.debug_struct("TileRef")
+			.field("gid", &self.gid())
+			.field("tile.t", &self.tile.t)
+			.field("tile.d", &self.tile.d)
+			.field("tile.px", &self.tile.px)
+			.field("tile.src", &self.tile.src)
+			.finish()
+	}
+}
+
+#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Copy, Hash, Debug)]
+pub struct SpatialIndex(i64, i64);
+impl<A, B> From<(A, B)> for SpatialIndex
+where
+	A: AsPrimitive<i64>,
+	B: AsPrimitive<i64>,
+{
+	fn from(value: (A, B)) -> Self {
+		Self(value.0.as_(), value.1.as_())
+	}
+}
+impl From<UVec2> for SpatialIndex {
+	fn from(value: UVec2) -> Self {
+		Self(value.x as i64, value.y as i64)
+	}
+}
+impl From<IVec2> for SpatialIndex {
+	fn from(value: IVec2) -> Self {
+		Self(value.x as i64, value.y as i64)
+	}
+}
+
+pub struct LdtkLevel {
+	level: Level,
+	processed_layers: Vec<LdtkLayer>,
+}
+
+impl LdtkLevel {
+	pub fn level_ref(&self) -> &Level {
+		&self.level
+	}
+	pub fn level_ref_mut(&mut self) -> &mut Level {
+		&mut self.level
+	}
+	pub fn layers(&self) -> impl DoubleEndedIterator<Item = &LdtkLayer> {
+		self.processed_layers.iter()
+	}
+	pub fn layers_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut LdtkLayer> {
+		self.processed_layers.iter_mut()
+	}
+	pub fn get_indexer(&self) -> Indexer {
+		Indexer::new(px_to_grid(self.level.px_wid), px_to_grid(self.level.px_hei))
+	}
+}
+
+impl From<Level> for LdtkLevel {
+	fn from(mut value: Level) -> Self {
+		let layers = value.layer_instances.take();
+
+		Self {
+			processed_layers: layers
+				.unwrap_or_default()
+				.into_iter()
+				.enumerate()
+				.map(|(idx, inst)| LdtkLayer::new(idx, inst))
+				.collect(),
+			level: value,
+		}
+	}
+}
+
+pub struct LdtkLayer {
+	layer: LayerInstance,
+	position_lookup: HashMap<SpatialIndex, TileInstance>,
+	level: usize,
+	indexer: Indexer,
+}
+
+impl LdtkLayer {
+	pub fn new(index: usize, layer: LayerInstance) -> Self {
+		let tile_list = layer.grid_tiles.iter().chain(&layer.auto_layer_tiles);
+		let mut position_lookup =
+			HashMap::with_capacity(layer.grid_tiles.len() + layer.auto_layer_tiles.len());
+
+		let scale = get_ldtk_tile_scale() as i64;
+		let indexer = Indexer::new(layer.c_wid, layer.c_hei);
+
+		for tile in tile_list {
+			let x = tile.px[0] / scale;
+			let y = tile.px[1] / scale;
+
+			position_lookup.insert((x, y).into(), tile.serde_clone());
+		}
+
+		Self {
+			level: index,
+			indexer,
+			layer,
+			position_lookup,
+		}
+	}
+
+	pub fn indexer(&self) -> Indexer {
+		self.indexer
+	}
+
+	pub fn has_tiles(&self) -> bool {
+		!(self.layer.auto_layer_tiles.is_empty() && self.layer.grid_tiles.is_empty())
+	}
+
+	pub fn for_each_tile(&self, mut cb: impl FnMut(i64, i64, TileRef)) {
+		self.position_lookup.iter().for_each(|(pos, tile)| {
+			cb(pos.0, pos.1, TileRef::new(tile));
+		});
+	}
+
+	pub fn get_z_delta(&self) -> f32 {
+		(self.level as f32) / 100.0
+	}
+
+	pub fn get_tile(&self, pos: impl Into<SpatialIndex>) -> Option<TileRef> {
+		self.position_lookup.get(&pos.into()).map(TileRef::new)
+	}
+	pub fn get_tile_at(
+		&self,
+		a: impl AsPrimitive<i64>,
+		b: impl AsPrimitive<i64>,
+	) -> Option<TileRef> {
+		self.position_lookup
+			.get(&SpatialIndex(a.as_(), b.as_()))
+			.map(TileRef::new)
+	}
+
+	/// Returns the inferred name of the tileset used for this layer. This is assumed to be the
+	/// name of the tileset file, without the preceding path segments or the file extension. Case
+	/// remains unchanged
+	pub fn infer_tileset_name(&self) -> Option<String> {
+		self.layer.tileset_rel_path.as_ref().and_then(|path| {
+			Path::new(path)
+				.file_stem()
+				.and_then(|stem| stem.to_str())
+				.map(String::from)
+		})
+	}
+}
+
+impl AsRef<LayerInstance> for LdtkLayer {
+	fn as_ref(&self) -> &LayerInstance {
+		&self.layer
+	}
+}
diff --git a/src/utils.rs b/src/utils.rs
index 3608636..76e3674 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -15,7 +15,7 @@ where
 	T: Serialize + DeserializeOwned,
 {
 	fn serde_clone(&self) -> Self {
-		serde_json::from_value(serde_json::to_value(&self).unwrap()).unwrap()
+		serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap()
 	}
 }
 
@@ -27,19 +27,9 @@ pub fn grid_to_px<T: AsPrimitive<f32>>(t: T) -> f32 {
 	t.as_() * get_ldtk_tile_scale()
 }
 
-pub fn entity_to_worldspace(level_height: i64, entity: &EntityInstance) -> (f32, f32) {
-	let centre_align_pixel_x = grid_to_px(entity.grid[0]) - (get_ldtk_tile_scale() / 2.0);
-	let centre_align_pixel_y = grid_to_px(entity.grid[1]) - (get_ldtk_tile_scale() / 2.0);
-	let inverted_pixel_y = level_height as f32 - centre_align_pixel_y - get_ldtk_tile_scale();
-	let box_aligned_x = centre_align_pixel_x + (entity.width / 2) as f32;
-	let box_aligned_y = inverted_pixel_y - (entity.height / 2) as f32;
-
-	(box_aligned_x, box_aligned_y)
-}
-
 pub fn entity_centre(level_height: i64, entity: &EntityInstance) -> (f32, f32) {
 	let x = entity.px[0] - (entity.width / 2);
-	let y = (level_height - entity.px[1] - entity.height / 2);
+	let y = level_height - entity.px[1] - entity.height / 2;
 	(x as f32, y as f32)
 }
 
@@ -63,33 +53,33 @@ impl ActiveLevel {
 
 #[derive(Debug, Copy, Clone)]
 pub struct Indexer {
-	width: isize,
-	height: isize,
+	width: i64,
+	height: i64,
 }
 
 impl Indexer {
-	pub fn new(width: impl AsPrimitive<isize>, height: impl AsPrimitive<isize>) -> Self {
+	pub fn new(width: impl AsPrimitive<i64>, height: impl AsPrimitive<i64>) -> Self {
 		Self {
 			width: width.as_(),
 			height: height.as_(),
 		}
 	}
 
-	pub fn index(&self, x: impl AsPrimitive<isize>, y: impl AsPrimitive<isize>) -> usize {
+	pub fn index(&self, x: impl AsPrimitive<i64>, y: impl AsPrimitive<i64>) -> usize {
 		((y.as_() * self.width) + x.as_()).as_()
 	}
 
-	pub fn reverse(&self, index: impl AsPrimitive<isize>) -> (usize, usize) {
+	pub fn reverse(&self, index: impl AsPrimitive<i64>) -> (usize, usize) {
 		(
-			(index.as_() % self.width) as usize,
-			(index.as_() / self.width) as usize,
+			(index.as_() % self.width).max(0) as usize,
+			(index.as_() / self.width).max(0) as usize,
 		)
 	}
 
-	pub fn width(&self) -> isize {
+	pub fn width(&self) -> i64 {
 		self.width
 	}
-	pub fn height(&self) -> isize {
+	pub fn height(&self) -> i64 {
 		self.height
 	}
 }
-- 
GitLab