Skip to content
Snippets Groups Projects
Verified Commit 24a2956c authored by Louis's avatar Louis :fire:
Browse files

Wrap levels and layers, preprocess layers to extract tile info

parent ec233e24
No related branches found
No related tags found
No related merge requests found
...@@ -5,10 +5,11 @@ use anyhow::Error; ...@@ -5,10 +5,11 @@ use anyhow::Error;
use bevy::asset::{AssetEvent, AssetLoader, Assets, BoxedFuture, LoadContext, LoadedAsset}; use bevy::asset::{AssetEvent, AssetLoader, Assets, BoxedFuture, LoadContext, LoadedAsset};
use bevy::prelude::{EventReader, Res, ResMut, Resource}; use bevy::prelude::{EventReader, Res, ResMut, Resource};
use bevy::reflect::TypeUuid; use bevy::reflect::TypeUuid;
use ldtk_rust::{Level, Project, TilesetDefinition}; use ldtk_rust::Project;
use serde_json::Value; use serde_json::Value;
use crate::utils::SerdeClone; use crate::utils::SerdeClone;
use crate::LdtkLevel;
#[derive(TypeUuid)] #[derive(TypeUuid)]
#[uuid = "292a8918-9487-11ed-8dd2-43b6f36cb076"] #[uuid = "292a8918-9487-11ed-8dd2-43b6f36cb076"]
...@@ -51,9 +52,9 @@ impl AssetLoader for LdtkLoader { ...@@ -51,9 +52,9 @@ impl AssetLoader for LdtkLoader {
} }
#[derive(Resource, Default)] #[derive(Resource, Default)]
pub struct LevelIndex(pub HashMap<String, Level>); pub struct LevelIndex(pub HashMap<String, LdtkLevel>);
impl Deref for LevelIndex { impl Deref for LevelIndex {
type Target = HashMap<String, Level>; type Target = HashMap<String, LdtkLevel>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
...@@ -92,7 +93,10 @@ pub fn handle_ldtk_project_events( ...@@ -92,7 +93,10 @@ pub fn handle_ldtk_project_events(
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
if let Some(LdtkProject(project)) = assets.get(handle) { if let Some(LdtkProject(project)) = assets.get(handle) {
for level in &project.levels { 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 { for tileset in &project.defs.tilesets {
...@@ -100,7 +104,7 @@ pub fn handle_ldtk_project_events( ...@@ -100,7 +104,7 @@ pub fn handle_ldtk_project_events(
for custom in &tileset.custom_data { for custom in &tileset.custom_data {
tile_meta.insert( tile_meta.insert(
custom.tile_id, 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)); tilset_index.insert(tileset.identifier.clone(), TileMetadata(tile_meta));
......
...@@ -2,6 +2,7 @@ mod assets; ...@@ -2,6 +2,7 @@ mod assets;
mod camera; mod camera;
mod locator; mod locator;
mod map_query; mod map_query;
mod types;
pub(crate) mod utils; pub(crate) mod utils;
// mod spawning; // mod spawning;
...@@ -45,6 +46,13 @@ mod __plugin { ...@@ -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> { pub struct MicroLDTKCameraPlugin<CameraFilter: ReadOnlyWorldQuery> {
_p: PhantomData<CameraFilter>, _p: PhantomData<CameraFilter>,
...@@ -65,7 +73,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; ...@@ -65,7 +73,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
pub use __plugin::{MicroLDTKCameraPlugin, MicroLDTKPlugin}; pub use __plugin::{MicroLDTKCameraPlugin, MicroLDTKPlugin};
pub use assets::{LdtkLoader, LdtkProject, LevelIndex, TileMetadata, TilesetIndex}; pub use assets::{LdtkLoader, LdtkProject, LevelIndex, TileMetadata, TilesetIndex};
pub use map_query::{CameraBounds, LayerRef, MapQuery, TileRef}; pub use camera::CameraBounder;
pub use utils::{ pub use map_query::{CameraBounds, MapQuery};
entity_centre, entity_to_worldspace, grid_to_px, px_to_grid, ActiveLevel, WorldLinked, pub use types::{LdtkLayer, LdtkLevel, SpatialIndex, TileFlip, TileRef};
}; pub use utils::{entity_centre, grid_to_px, px_to_grid, ActiveLevel, WorldLinked};
use std::fmt::{Debug, Formatter}; use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use bevy::ecs::system::SystemParam; use bevy::ecs::system::SystemParam;
use bevy::prelude::*; use bevy::prelude::*;
use ldtk_rust::{EntityInstance, LayerInstance, Level, TileInstance}; use ldtk_rust::EntityInstance;
use num_traits::AsPrimitive;
// use crate::assets::level_index::LevelIndex; use crate::assets::LevelIndex;
use crate::assets::{LdtkProject, LevelIndex}; use crate::utils::{ActiveLevel, SerdeClone};
use crate::utils::{ActiveLevel, Indexer, SerdeClone}; use crate::{get_ldtk_tile_scale, LdtkLayer, LdtkLevel};
use crate::{get_ldtk_tile_scale, px_to_grid};
#[derive(SystemParam)] #[derive(SystemParam)]
pub struct MapQuery<'w, 's> { pub struct MapQuery<'w, 's> {
assets: Res<'w, Assets<LdtkProject>>,
active: Option<Res<'w, ActiveLevel>>, active: Option<Res<'w, ActiveLevel>>,
index: Res<'w, LevelIndex>, index: Res<'w, LevelIndex>,
#[system_param(ignore)] #[system_param(ignore)]
_e: PhantomData<&'s ()>, _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 struct InstanceRef<'a> {
pub entity: &'a EntityInstance, pub entity: &'a EntityInstance,
} }
...@@ -98,7 +36,7 @@ impl<'a> InstanceRef<'a> { ...@@ -98,7 +36,7 @@ impl<'a> InstanceRef<'a> {
return field return field
.value .value
.as_ref() .as_ref()
.map(|v| v.clone()) .cloned()
.unwrap_or(serde_json::Value::Null); .unwrap_or(serde_json::Value::Null);
} }
} }
...@@ -107,77 +45,6 @@ impl<'a> InstanceRef<'a> { ...@@ -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)] #[derive(Copy, Clone, Debug)]
pub struct CameraBounds { pub struct CameraBounds {
pub left: f32, pub left: f32,
...@@ -206,24 +73,15 @@ impl<'w, 's> MapQuery<'w, 's> { ...@@ -206,24 +73,15 @@ impl<'w, 's> MapQuery<'w, 's> {
// --- than the currently active one. 'active' methods are a convenience to // --- than the currently active one. 'active' methods are a convenience to
// --- call the static accessors on whatever the current level is // --- call the static accessors on whatever the current level is
pub fn get_indexer_for(level: &Level) -> Indexer { pub fn for_each_layer_of(level: &LdtkLevel, mut cb: impl FnMut(&LdtkLayer)) {
Indexer::new(px_to_grid(level.px_wid), px_to_grid(level.px_hei)) for layer in level.layers().rev() {
} cb(layer);
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 get_entities_of(level: &Level) -> Vec<&EntityInstance> { pub fn get_entities_of(level: &LdtkLevel) -> Vec<&EntityInstance> {
level level
.level_ref()
.layer_instances .layer_instances
.as_ref() .as_ref()
.map(|layers| { .map(|layers| {
...@@ -235,8 +93,9 @@ impl<'w, 's> MapQuery<'w, 's> { ...@@ -235,8 +93,9 @@ impl<'w, 's> MapQuery<'w, 's> {
.unwrap_or_default() .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
.level_ref()
.layer_instances .layer_instances
.as_ref() .as_ref()
.map(|layers| { .map(|layers| {
...@@ -250,34 +109,32 @@ impl<'w, 's> MapQuery<'w, 's> { ...@@ -250,34 +109,32 @@ impl<'w, 's> MapQuery<'w, 's> {
} }
pub fn get_filtered_entities_of( pub fn get_filtered_entities_of(
level: &Level, level: &LdtkLevel,
entity_type: impl ToString, entity_type: impl ToString,
) -> Vec<&EntityInstance> { ) -> Vec<&EntityInstance> {
let e_type = entity_type.to_string(); let e_type = entity_type.to_string();
match level.layer_instances.as_ref() { level
Some(inst) => inst .layers()
.iter() .flat_map(|layer| layer.as_ref().entity_instances.iter())
.flat_map(|layer| layer.entity_instances.iter()) .filter(|inst| inst.identifier == e_type)
.filter(|inst| inst.identifier == e_type) .collect()
.collect(),
None => Vec::new(),
}
} }
pub fn get_owned_entities_of(level: &Level) -> Vec<EntityInstance> { pub fn get_owned_entities_of(level: &LdtkLevel) -> Vec<EntityInstance> {
level level
.layer_instances .layers()
.as_ref() .flat_map(|layer| {
.map(|layers| { layer
layers .as_ref()
.entity_instances
.iter() .iter()
.flat_map(|layer| layer.entity_instances.iter().map(|inst| inst.serde_clone())) .map(|inst| inst.serde_clone())
.collect()
}) })
.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 { CameraBounds {
left: 0.0, left: 0.0,
top: level.px_hei as f32, top: level.px_hei as f32,
...@@ -286,7 +143,7 @@ impl<'w, 's> MapQuery<'w, 's> { ...@@ -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 self.active
.as_ref() .as_ref()
.and_then(|index| self.index.get(&index.map)) .and_then(|index| self.index.get(&index.map))
...@@ -294,7 +151,7 @@ impl<'w, 's> MapQuery<'w, 's> { ...@@ -294,7 +151,7 @@ impl<'w, 's> MapQuery<'w, 's> {
pub fn get_entities(&self) -> Vec<&EntityInstance> { pub fn get_entities(&self) -> Vec<&EntityInstance> {
self.get_active_level() self.get_active_level()
.map(|level| MapQuery::get_entities_of(level)) .map(MapQuery::get_entities_of)
.unwrap_or_default() .unwrap_or_default()
} }
...@@ -302,7 +159,7 @@ impl<'w, 's> MapQuery<'w, 's> { ...@@ -302,7 +159,7 @@ impl<'w, 's> MapQuery<'w, 's> {
self.get_active_level().map(MapQuery::get_camera_bounds_of) 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() { if let Some(level) = self.get_active_level() {
Self::for_each_layer_of(level, cb); Self::for_each_layer_of(level, cb);
} }
......
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
}
}
...@@ -15,7 +15,7 @@ where ...@@ -15,7 +15,7 @@ where
T: Serialize + DeserializeOwned, T: Serialize + DeserializeOwned,
{ {
fn serde_clone(&self) -> Self { 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 { ...@@ -27,19 +27,9 @@ pub fn grid_to_px<T: AsPrimitive<f32>>(t: T) -> f32 {
t.as_() * get_ldtk_tile_scale() 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) { pub fn entity_centre(level_height: i64, entity: &EntityInstance) -> (f32, f32) {
let x = entity.px[0] - (entity.width / 2); 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) (x as f32, y as f32)
} }
...@@ -63,33 +53,33 @@ impl ActiveLevel { ...@@ -63,33 +53,33 @@ impl ActiveLevel {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Indexer { pub struct Indexer {
width: isize, width: i64,
height: isize, height: i64,
} }
impl Indexer { 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 { Self {
width: width.as_(), width: width.as_(),
height: height.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_() ((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).max(0) as usize,
(index.as_() / self.width) as usize, (index.as_() / self.width).max(0) as usize,
) )
} }
pub fn width(&self) -> isize { pub fn width(&self) -> i64 {
self.width self.width
} }
pub fn height(&self) -> isize { pub fn height(&self) -> i64 {
self.height self.height
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment