fyrox_impl/scene/tilemap/
tile_source.rs1use fyrox_core::swap_hash_map_entry;
27
28use crate::{
29 core::{algebra::Vector2, reflect::prelude::*, visitor::prelude::*},
30 fxhash::FxHashMap,
31 rand::{seq::IteratorRandom, thread_rng},
32};
33use std::{
34 cmp::Ordering,
35 fmt::{Debug, Display, Formatter},
36 ops::{Deref, DerefMut},
37 str::FromStr,
38};
39
40use super::*;
41
42pub type PalettePosition = Vector2<i16>;
44
45#[inline]
46fn try_position(source: Vector2<i32>) -> Option<PalettePosition> {
47 Some(PalettePosition::new(
48 source.x.try_into().ok()?,
49 source.y.try_into().ok()?,
50 ))
51}
52
53#[inline]
54fn position_to_vector(source: PalettePosition) -> Vector2<i32> {
55 source.map(|x| x as i32)
56}
57
58#[derive(Default, Debug, Clone, PartialEq, Reflect)]
60pub struct TileGridMap<V: Debug>(FxHashMap<Vector2<i32>, V>);
61
62impl<V: Visit + Default + Debug> Visit for TileGridMap<V> {
63 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
64 self.0.visit(name, visitor)
65 }
66}
67
68impl<V: Debug> Deref for TileGridMap<V> {
69 type Target = FxHashMap<Vector2<i32>, V>;
70 fn deref(&self) -> &Self::Target {
71 &self.0
72 }
73}
74
75impl<V: Debug> DerefMut for TileGridMap<V> {
76 fn deref_mut(&mut self) -> &mut Self::Target {
77 &mut self.0
78 }
79}
80
81#[derive(Eq, PartialEq, Clone, Copy, Default, Hash, Reflect, Visit, TypeUuidProvider)]
83#[type_uuid(id = "3eb69303-d361-482d-8094-44b9f9c323ca")]
84#[repr(C)]
85pub struct TileDefinitionHandle {
86 pub page: PalettePosition,
88 pub tile: PalettePosition,
90}
91
92unsafe impl bytemuck::Zeroable for TileDefinitionHandle {}
93
94unsafe impl bytemuck::Pod for TileDefinitionHandle {}
95
96impl PartialOrd for TileDefinitionHandle {
97 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
98 Some(self.cmp(other))
99 }
100}
101
102impl Ord for TileDefinitionHandle {
103 fn cmp(&self, other: &Self) -> Ordering {
104 self.page
105 .y
106 .cmp(&other.page.y)
107 .reverse()
108 .then(self.page.x.cmp(&other.page.x))
109 .then(self.tile.y.cmp(&other.tile.y).reverse())
110 .then(self.tile.x.cmp(&other.tile.x))
111 }
112}
113
114impl Display for TileDefinitionHandle {
115 #[inline]
116 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117 write!(
118 f,
119 "({},{}):({},{})",
120 self.page.x, self.page.y, self.tile.x, self.tile.y
121 )
122 }
123}
124
125impl Debug for TileDefinitionHandle {
126 #[inline]
127 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
128 write!(
129 f,
130 "TileDefinitionHandle({},{};{},{})",
131 self.page.x, self.page.y, self.tile.x, self.tile.y
132 )
133 }
134}
135
136impl FromStr for TileDefinitionHandle {
137 type Err = TileDefinitionHandleParseError;
138
139 fn from_str(s: &str) -> Result<Self, Self::Err> {
140 Self::parse(s).ok_or(TileDefinitionHandleParseError)
141 }
142}
143
144#[derive(Debug)]
146pub struct TileDefinitionHandleParseError;
147
148impl Display for TileDefinitionHandleParseError {
149 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
150 write!(f, "Tile definition handle parse failure")
151 }
152}
153
154impl Error for TileDefinitionHandleParseError {}
155
156impl TileDefinitionHandle {
157 pub const EMPTY: Self = Self::new(i16::MIN, i16::MIN, i16::MIN, i16::MIN);
159 pub fn is_empty(&self) -> bool {
161 self == &Self::EMPTY
162 }
163 pub fn try_new(page: Vector2<i32>, tile: Vector2<i32>) -> Option<Self> {
168 Some(Self {
169 page: try_position(page)?,
170 tile: try_position(tile)?,
171 })
172 }
173 pub const fn new(page_x: i16, page_y: i16, tile_x: i16, tile_y: i16) -> Self {
176 Self {
177 page: PalettePosition::new(page_x, page_y),
178 tile: PalettePosition::new(tile_x, tile_y),
179 }
180 }
181 pub fn page(&self) -> Vector2<i32> {
183 position_to_vector(self.page)
184 }
185 pub fn tile(&self) -> Vector2<i32> {
187 position_to_vector(self.tile)
188 }
189 pub fn parse(s: &str) -> Option<Self> {
193 let mut iter = s
194 .split(|c: char| c != '-' && !c.is_ascii_digit())
195 .filter(|w| !w.is_empty());
196 let a: i16 = iter.next()?.parse().ok()?;
197 let b: i16 = iter.next()?.parse().ok()?;
198 let c: i16 = iter.next()?.parse().ok()?;
199 let d: i16 = iter.next()?.parse().ok()?;
200 if iter.next().is_some() {
201 None
202 } else {
203 Some(Self::new(a, b, c, d))
204 }
205 }
206}
207
208#[derive(Debug, Default, Clone)]
210pub struct TileRegion {
211 pub origin: Vector2<i32>,
214 pub bounds: OptionTileRect,
216}
217
218impl TileRegion {
219 pub fn from_bounds_and_direction(bounds: OptionTileRect, direction: Vector2<i32>) -> Self {
222 let Some(bounds) = *bounds else {
223 return Self::default();
224 };
225 let x0 = if direction.x <= 0 {
226 bounds.left_bottom_corner().x
227 } else {
228 bounds.right_top_corner().x
229 };
230 let y0 = if direction.y <= 0 {
231 bounds.left_bottom_corner().y
232 } else {
233 bounds.right_top_corner().y
234 };
235 Self {
236 origin: Vector2::new(x0, y0),
237 bounds: bounds.into(),
238 }
239 }
240 pub fn from_points(origin: Vector2<i32>, end: Vector2<i32>) -> Self {
242 Self {
243 origin,
244 bounds: OptionTileRect::from_points(origin, end),
245 }
246 }
247 pub fn with_bounds(mut self, bounds: OptionTileRect) -> Self {
249 self.bounds = bounds;
250 self
251 }
252 pub fn deflate(mut self, dw: i32, dh: i32) -> Self {
254 self.bounds = self.bounds.deflate(dw, dh);
255 self
256 }
257 pub fn iter(&self) -> impl Iterator<Item = (Vector2<i32>, Vector2<i32>)> + '_ {
262 self.bounds.iter().map(|p| (p, p - self.origin))
263 }
264}
265
266pub trait TileSource {
269 fn transformation(&self) -> OrthoTransformation;
271 fn get_at(&self, position: Vector2<i32>) -> Option<TileDefinitionHandle>;
275}
276
277pub trait BoundedTileSource: TileSource {
280 fn bounding_rect(&self) -> OptionTileRect;
282}
283
284#[derive(Clone, Debug)]
286pub struct SingleTileSource(pub OrthoTransformation, pub TileDefinitionHandle);
287
288impl TileSource for SingleTileSource {
289 fn transformation(&self) -> OrthoTransformation {
290 self.0
291 }
292 fn get_at(&self, _position: Vector2<i32>) -> Option<TileDefinitionHandle> {
293 Some(self.1)
294 }
295}
296
297pub struct RandomTileSource<'a>(pub &'a Stamp);
299
300impl TileSource for RandomTileSource<'_> {
301 fn transformation(&self) -> OrthoTransformation {
302 self.0.transformation()
303 }
304 fn get_at(&self, _position: Vector2<i32>) -> Option<TileDefinitionHandle> {
305 self.0.values().choose(&mut thread_rng()).copied()
306 }
307}
308
309pub struct PartialRandomTileSource<'a>(pub &'a Stamp, pub OptionTileRect);
311
312impl TileSource for PartialRandomTileSource<'_> {
313 fn transformation(&self) -> OrthoTransformation {
314 self.0.transformation()
315 }
316 fn get_at(&self, _position: Vector2<i32>) -> Option<TileDefinitionHandle> {
317 let pos = self.1.iter().choose(&mut thread_rng())?;
318 self.0.get_at(pos)
319 }
320}
321
322pub struct RepeatTileSource<'a, S> {
325 pub source: &'a S,
327 pub region: TileRegion,
329}
330
331impl<S: TileSource> TileSource for RepeatTileSource<'_, S> {
332 fn transformation(&self) -> OrthoTransformation {
333 self.source.transformation()
334 }
335 fn get_at(&self, position: Vector2<i32>) -> Option<TileDefinitionHandle> {
336 let rect = (*self.region.bounds)?;
337 let rect_pos = rect.position;
338 let size = rect.size;
339 let pos = position + self.region.origin - rect_pos;
340 let x = pos.x.rem_euclid(size.x);
341 let y = pos.y.rem_euclid(size.y);
342 self.source.get_at(Vector2::new(x, y) + rect_pos)
343 }
344}
345
346#[derive(Clone, Debug, Default, PartialEq)]
348pub struct Tiles(TileGridMap<TileDefinitionHandle>);
349
350#[derive(Clone, Debug, Default, Visit)]
353pub struct Stamp(OrthoTransformation, OrthoTransformMap<TileDefinitionHandle>);
354
355impl TileSource for Tiles {
356 fn transformation(&self) -> OrthoTransformation {
357 OrthoTransformation::default()
358 }
359 fn get_at(&self, position: Vector2<i32>) -> Option<TileDefinitionHandle> {
360 self.get(&position).copied()
361 }
362}
363
364impl Visit for Tiles {
365 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
366 self.0.visit(name, visitor)
367 }
368}
369
370impl Deref for Tiles {
371 type Target = TileGridMap<TileDefinitionHandle>;
372
373 fn deref(&self) -> &Self::Target {
374 &self.0
375 }
376}
377
378impl DerefMut for Tiles {
379 fn deref_mut(&mut self) -> &mut Self::Target {
380 &mut self.0
381 }
382}
383
384impl TileSource for Stamp {
385 fn transformation(&self) -> OrthoTransformation {
386 self.0
387 }
388 fn get_at(&self, position: Vector2<i32>) -> Option<TileDefinitionHandle> {
389 self.1.get(position).copied()
390 }
391}
392
393impl Stamp {
394 pub fn tile_iter(&self) -> impl Iterator<Item = TileDefinitionHandle> + '_ {
396 self.1.values().copied()
397 }
398 pub fn repeat(&self, start: Vector2<i32>, end: Vector2<i32>) -> RepeatTileSource<Stamp> {
400 let bounds = self.bounding_rect();
401 RepeatTileSource {
402 source: self,
403 region: TileRegion::from_bounds_and_direction(bounds, start - end),
404 }
405 }
406
407 pub fn repeat_anywhere(&self) -> RepeatTileSource<Stamp> {
409 let bounds = self.bounding_rect();
410 RepeatTileSource {
411 source: self,
412 region: TileRegion::from_bounds_and_direction(bounds, Vector2::new(0, 0)),
413 }
414 }
415
416 pub fn is_empty(&self) -> bool {
418 self.1.is_empty()
419 }
420 pub fn clear(&mut self) {
422 self.1.clear();
423 self.0 = OrthoTransformation::identity();
424 }
425 pub fn build<I: Iterator<Item = (Vector2<i32>, TileDefinitionHandle)> + Clone>(
429 &mut self,
430 source: I,
431 ) {
432 self.clear();
433 let mut rect = OptionTileRect::default();
434 for (p, _) in source.clone() {
435 rect.push(p);
436 }
437 let Some(rect) = *rect else {
438 return;
439 };
440 let center = rect.center();
441 for (p, h) in source {
442 self.insert(p - center, h);
443 }
444 }
445 pub fn rotate(&mut self, amount: i8) {
447 self.0 = self.0.rotated(amount);
448 self.1 = std::mem::take(&mut self.1).rotated(amount);
449 }
450 pub fn x_flip(&mut self) {
452 self.0 = self.0.x_flipped();
453 self.1 = std::mem::take(&mut self.1).x_flipped();
454 }
455 pub fn y_flip(&mut self) {
457 self.0 = self.0.y_flipped();
458 self.1 = std::mem::take(&mut self.1).y_flipped();
459 }
460 pub fn transform(&mut self, amount: OrthoTransformation) {
462 self.0 = self.0.transformed(amount);
463 self.1 = std::mem::take(&mut self.1).transformed(amount);
464 }
465}
466
467impl Deref for Stamp {
468 type Target = OrthoTransformMap<TileDefinitionHandle>;
469 fn deref(&self) -> &Self::Target {
470 &self.1
471 }
472}
473
474impl DerefMut for Stamp {
475 fn deref_mut(&mut self) -> &mut Self::Target {
476 &mut self.1
477 }
478}
479
480impl Tiles {
481 pub fn new(source: TileGridMap<TileDefinitionHandle>) -> Self {
483 Self(source)
484 }
485 pub fn find_continuous_horizontal_span(&self, position: Vector2<i32>) -> (i32, i32) {
488 let y = position.y;
489 let mut min = position.x;
490 while self.contains_key(&Vector2::new(min, y)) {
491 min -= 1;
492 }
493 let mut max = position.x;
494 while self.contains_key(&Vector2::new(max, y)) {
495 max += 1;
496 }
497 (min, max)
498 }
499 pub fn swap_tiles(&mut self, updates: &mut TilesUpdate) {
503 for (k, v) in updates.iter_mut() {
504 swap_hash_map_entry(self.entry(*k), v);
505 }
506 }
507 #[inline]
509 pub fn bounding_rect(&self) -> OptionTileRect {
510 let mut result = OptionTileRect::default();
511 for position in self.keys() {
512 result.push(*position);
513 }
514 result
515 }
516
517 #[inline]
519 pub fn clear(&mut self) {
520 self.0.clear();
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use super::*;
527
528 #[test]
531 fn size_of_handle() {
532 assert_eq!(std::mem::size_of::<TileDefinitionHandle>(), 8);
533 }
534
535 #[test]
536 fn zero_handle() {
537 assert_eq!(
538 TileDefinitionHandle::zeroed(),
539 TileDefinitionHandle::default()
540 );
541 }
542}