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,
32 rand::seq::IteratorRandom,
33};
34use std::{
35 cmp::Ordering,
36 fmt::{Debug, Display, Formatter},
37 ops::{Deref, DerefMut},
38 str::FromStr,
39};
40
41use super::*;
42
43pub type PalettePosition = Vector2<i16>;
45
46#[inline]
47fn try_position(source: Vector2<i32>) -> Option<PalettePosition> {
48 Some(PalettePosition::new(
49 source.x.try_into().ok()?,
50 source.y.try_into().ok()?,
51 ))
52}
53
54#[inline]
55fn position_to_vector(source: PalettePosition) -> Vector2<i32> {
56 source.map(|x| x as i32)
57}
58
59#[derive(Default, Debug, Clone, PartialEq, Reflect)]
61pub struct TileGridMap<V: Debug + Clone + Reflect>(FxHashMap<Vector2<i32>, V>);
62
63impl<V: Visit + Default + Debug + Clone + Reflect> Visit for TileGridMap<V> {
64 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
65 self.0.visit(name, visitor)
66 }
67}
68
69impl<V: Debug + Clone + Reflect> Deref for TileGridMap<V> {
70 type Target = FxHashMap<Vector2<i32>, V>;
71 fn deref(&self) -> &Self::Target {
72 &self.0
73 }
74}
75
76impl<V: Debug + Clone + Reflect> DerefMut for TileGridMap<V> {
77 fn deref_mut(&mut self) -> &mut Self::Target {
78 &mut self.0
79 }
80}
81
82#[derive(Eq, PartialEq, Clone, Copy, Default, Hash, Reflect, Visit, TypeUuidProvider)]
84#[type_uuid(id = "3eb69303-d361-482d-8094-44b9f9c323ca")]
85#[repr(C)]
86pub struct TileDefinitionHandle {
87 pub page: PalettePosition,
89 pub tile: PalettePosition,
91}
92
93unsafe impl bytemuck::Zeroable for TileDefinitionHandle {}
94
95unsafe impl bytemuck::Pod for TileDefinitionHandle {}
96
97impl PartialOrd for TileDefinitionHandle {
98 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
99 Some(self.cmp(other))
100 }
101}
102
103impl Ord for TileDefinitionHandle {
104 fn cmp(&self, other: &Self) -> Ordering {
105 self.page
106 .y
107 .cmp(&other.page.y)
108 .reverse()
109 .then(self.page.x.cmp(&other.page.x))
110 .then(self.tile.y.cmp(&other.tile.y).reverse())
111 .then(self.tile.x.cmp(&other.tile.x))
112 }
113}
114
115impl Display for TileDefinitionHandle {
116 #[inline]
117 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
118 if self.is_empty() {
119 f.write_str("Empty")
120 } else {
121 write!(
122 f,
123 "({},{}):({},{})",
124 self.page.x, self.page.y, self.tile.x, self.tile.y
125 )
126 }
127 }
128}
129
130impl Debug for TileDefinitionHandle {
131 #[inline]
132 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
133 write!(
134 f,
135 "TileDefinitionHandle({},{};{},{})",
136 self.page.x, self.page.y, self.tile.x, self.tile.y
137 )
138 }
139}
140
141impl FromStr for TileDefinitionHandle {
142 type Err = TileDefinitionHandleParseError;
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 Self::parse(s).ok_or(TileDefinitionHandleParseError)
146 }
147}
148
149#[derive(Debug)]
151pub struct TileDefinitionHandleParseError;
152
153impl Display for TileDefinitionHandleParseError {
154 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155 write!(f, "Tile definition handle parse failure")
156 }
157}
158
159impl Error for TileDefinitionHandleParseError {}
160
161impl TileDefinitionHandle {
162 pub const EMPTY: Self = Self::new(i16::MIN, i16::MIN, i16::MIN, i16::MIN);
164 pub fn is_empty(&self) -> bool {
166 self == &Self::EMPTY
167 }
168 pub fn try_new(page: Vector2<i32>, tile: Vector2<i32>) -> Option<Self> {
173 Some(Self {
174 page: try_position(page)?,
175 tile: try_position(tile)?,
176 })
177 }
178 pub const fn new(page_x: i16, page_y: i16, tile_x: i16, tile_y: i16) -> Self {
181 Self {
182 page: PalettePosition::new(page_x, page_y),
183 tile: PalettePosition::new(tile_x, tile_y),
184 }
185 }
186 pub fn page(&self) -> Vector2<i32> {
188 position_to_vector(self.page)
189 }
190 pub fn tile(&self) -> Vector2<i32> {
192 position_to_vector(self.tile)
193 }
194 pub fn parse(s: &str) -> Option<Self> {
198 if s.eq_ignore_ascii_case("Empty") {
199 return Some(Self::EMPTY);
200 }
201 let mut iter = s
202 .split(|c: char| c != '-' && !c.is_ascii_digit())
203 .filter(|w| !w.is_empty());
204 let a: i16 = iter.next()?.parse().ok()?;
205 let b: i16 = iter.next()?.parse().ok()?;
206 let c: i16 = iter.next()?.parse().ok()?;
207 let d: i16 = iter.next()?.parse().ok()?;
208 if iter.next().is_some() {
209 None
210 } else {
211 Some(Self::new(a, b, c, d))
212 }
213 }
214}
215
216#[derive(Debug, Default, Clone)]
218pub struct TileRegion {
219 pub origin: Vector2<i32>,
222 pub bounds: OptionTileRect,
224}
225
226impl TileRegion {
227 pub fn from_bounds_and_direction(bounds: OptionTileRect, direction: Vector2<i32>) -> Self {
230 let Some(bounds) = *bounds else {
231 return Self::default();
232 };
233 let x0 = if direction.x <= 0 {
234 bounds.left_bottom_corner().x
235 } else {
236 bounds.right_top_corner().x
237 };
238 let y0 = if direction.y <= 0 {
239 bounds.left_bottom_corner().y
240 } else {
241 bounds.right_top_corner().y
242 };
243 Self {
244 origin: Vector2::new(x0, y0),
245 bounds: bounds.into(),
246 }
247 }
248 pub fn from_points(origin: Vector2<i32>, end: Vector2<i32>) -> Self {
250 Self {
251 origin,
252 bounds: OptionTileRect::from_points(origin, end),
253 }
254 }
255 pub fn with_bounds(mut self, bounds: OptionTileRect) -> Self {
257 self.bounds = bounds;
258 self
259 }
260 pub fn deflate(mut self, dw: i32, dh: i32) -> Self {
262 self.bounds = self.bounds.deflate(dw, dh);
263 self
264 }
265 pub fn iter(&self) -> impl Iterator<Item = (Vector2<i32>, Vector2<i32>)> + '_ {
270 self.bounds.iter().map(|p| (p, p - self.origin))
271 }
272}
273
274pub trait TileSource {
277 fn brush(&self) -> Option<&TileMapBrushResource>;
279 fn transformation(&self) -> OrthoTransformation;
281 fn get_at(&self, position: Vector2<i32>) -> Option<StampElement>;
285}
286
287pub trait BoundedTileSource: TileSource {
290 fn bounding_rect(&self) -> OptionTileRect;
292}
293
294#[derive(Clone, Debug)]
296pub struct SingleTileSource(pub OrthoTransformation, pub StampElement);
297
298impl TileSource for SingleTileSource {
299 fn brush(&self) -> Option<&TileMapBrushResource> {
300 None
301 }
302 fn transformation(&self) -> OrthoTransformation {
303 self.0
304 }
305 fn get_at(&self, _position: Vector2<i32>) -> Option<StampElement> {
306 Some(self.1.clone())
307 }
308}
309
310pub struct RandomTileSource<'a>(pub &'a Stamp);
312
313impl TileSource for RandomTileSource<'_> {
314 fn brush(&self) -> Option<&TileMapBrushResource> {
315 self.0.brush()
316 }
317 fn transformation(&self) -> OrthoTransformation {
318 self.0.transformation()
319 }
320 fn get_at(&self, _position: Vector2<i32>) -> Option<StampElement> {
321 self.0.values().choose(&mut rand::thread_rng()).cloned()
322 }
323}
324
325pub struct PartialRandomTileSource<'a>(pub &'a Stamp, pub OptionTileRect);
327
328impl TileSource for PartialRandomTileSource<'_> {
329 fn brush(&self) -> Option<&TileMapBrushResource> {
330 self.0.brush()
331 }
332 fn transformation(&self) -> OrthoTransformation {
333 self.0.transformation()
334 }
335 fn get_at(&self, _position: Vector2<i32>) -> Option<StampElement> {
336 let pos = self.1.iter().choose(&mut rand::thread_rng())?;
337 self.0.get_at(pos)
338 }
339}
340
341pub struct RepeatTileSource<'a, S> {
344 pub source: &'a S,
346 pub region: TileRegion,
348}
349
350impl<S: TileSource> TileSource for RepeatTileSource<'_, S> {
351 fn brush(&self) -> Option<&TileMapBrushResource> {
352 self.source.brush()
353 }
354 fn transformation(&self) -> OrthoTransformation {
355 self.source.transformation()
356 }
357 fn get_at(&self, position: Vector2<i32>) -> Option<StampElement> {
358 let rect = (*self.region.bounds)?;
359 let rect_pos = rect.position;
360 let size = rect.size;
361 let pos = position + self.region.origin - rect_pos;
362 let x = pos.x.rem_euclid(size.x);
363 let y = pos.y.rem_euclid(size.y);
364 self.source.get_at(Vector2::new(x, y) + rect_pos)
365 }
366}
367
368#[derive(Clone, Debug, Default, PartialEq)]
370pub struct Tiles(TileGridMap<TileDefinitionHandle>);
371
372#[derive(Clone, Debug, Default, Visit)]
375pub struct Stamp {
376 transform: OrthoTransformation,
377 #[visit(skip)]
378 elements: OrthoTransformMap<StampElement>,
379 #[visit(skip)]
380 source: Option<TileBook>,
381}
382
383#[derive(Clone, Debug, Reflect, Visit, Default)]
386pub struct StampElement {
387 pub handle: TileDefinitionHandle,
389 pub source: Option<ResourceTilePosition>,
391}
392
393impl From<TileDefinitionHandle> for StampElement {
394 fn from(handle: TileDefinitionHandle) -> Self {
395 Self {
396 handle,
397 source: None,
398 }
399 }
400}
401
402impl TileSource for Tiles {
403 fn brush(&self) -> Option<&TileMapBrushResource> {
404 None
405 }
406 fn transformation(&self) -> OrthoTransformation {
407 OrthoTransformation::default()
408 }
409 fn get_at(&self, position: Vector2<i32>) -> Option<StampElement> {
410 self.get(&position).copied().map(|h| h.into())
411 }
412}
413
414impl Visit for Tiles {
415 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
416 self.0.visit(name, visitor)
417 }
418}
419
420impl Deref for Tiles {
421 type Target = TileGridMap<TileDefinitionHandle>;
422
423 fn deref(&self) -> &Self::Target {
424 &self.0
425 }
426}
427
428impl DerefMut for Tiles {
429 fn deref_mut(&mut self) -> &mut Self::Target {
430 &mut self.0
431 }
432}
433
434impl TileSource for Stamp {
435 fn brush(&self) -> Option<&TileMapBrushResource> {
436 self.source.as_ref().and_then(|s| s.brush_ref())
437 }
438 fn transformation(&self) -> OrthoTransformation {
439 self.transform
440 }
441 fn get_at(&self, position: Vector2<i32>) -> Option<StampElement> {
442 self.elements.get(position).cloned()
443 }
444}
445
446impl Stamp {
447 pub fn source(&self) -> Option<&TileBook> {
449 self.source.as_ref()
450 }
451 pub fn tile_iter(&self) -> impl Iterator<Item = TileDefinitionHandle> + '_ {
453 self.elements.values().map(|s| s.handle)
454 }
455 pub fn repeat(&self, start: Vector2<i32>, end: Vector2<i32>) -> RepeatTileSource<Stamp> {
457 let bounds = self.bounding_rect();
458 RepeatTileSource {
459 source: self,
460 region: TileRegion::from_bounds_and_direction(bounds, start - end),
461 }
462 }
463
464 pub fn repeat_anywhere(&self) -> RepeatTileSource<Stamp> {
466 let bounds = self.bounding_rect();
467 RepeatTileSource {
468 source: self,
469 region: TileRegion::from_bounds_and_direction(bounds, Vector2::new(0, 0)),
470 }
471 }
472
473 pub fn is_empty(&self) -> bool {
475 self.elements.is_empty()
476 }
477 pub fn clear(&mut self) {
479 self.transform = OrthoTransformation::identity();
480 self.elements.clear();
481 }
482 pub fn build<I: Iterator<Item = (Vector2<i32>, StampElement)> + Clone>(
486 &mut self,
487 book: Option<TileBook>,
488 source: I,
489 ) {
490 self.source = book;
491 self.clear();
492 let mut rect = OptionTileRect::default();
493 for (p, _) in source.clone() {
494 rect.push(p);
495 }
496 let Some(rect) = *rect else {
497 return;
498 };
499 let center = rect.center();
500 for (p, e) in source {
501 _ = self.insert(p - center, e);
502 }
503 }
504 pub fn rotate(&mut self, amount: i8) {
506 self.transform = self.transform.rotated(amount);
507 self.elements = std::mem::take(&mut self.elements).rotated(amount);
508 }
509 pub fn x_flip(&mut self) {
511 self.transform = self.transform.x_flipped();
512 self.elements = std::mem::take(&mut self.elements).x_flipped();
513 }
514 pub fn y_flip(&mut self) {
516 self.transform = self.transform.y_flipped();
517 self.elements = std::mem::take(&mut self.elements).y_flipped();
518 }
519 pub fn transform(&mut self, amount: OrthoTransformation) {
521 self.transform = self.transform.transformed(amount);
522 self.elements = std::mem::take(&mut self.elements).transformed(amount);
523 }
524}
525
526impl Deref for Stamp {
527 type Target = OrthoTransformMap<StampElement>;
528 fn deref(&self) -> &Self::Target {
529 &self.elements
530 }
531}
532
533impl DerefMut for Stamp {
534 fn deref_mut(&mut self) -> &mut Self::Target {
535 &mut self.elements
536 }
537}
538
539impl Tiles {
540 pub fn new(source: TileGridMap<TileDefinitionHandle>) -> Self {
542 Self(source)
543 }
544 pub fn find_continuous_horizontal_span(&self, position: Vector2<i32>) -> (i32, i32) {
547 let y = position.y;
548 let mut min = position.x;
549 while self.contains_key(&Vector2::new(min, y)) {
550 min -= 1;
551 }
552 let mut max = position.x;
553 while self.contains_key(&Vector2::new(max, y)) {
554 max += 1;
555 }
556 (min, max)
557 }
558 pub fn swap_tiles(&mut self, updates: &mut TilesUpdate) {
562 for (k, v) in updates.iter_mut() {
563 swap_hash_map_entry(self.entry(*k), v);
564 }
565 }
566 #[inline]
568 pub fn bounding_rect(&self) -> OptionTileRect {
569 let mut result = OptionTileRect::default();
570 for position in self.keys() {
571 result.push(*position);
572 }
573 result
574 }
575
576 #[inline]
578 pub fn clear(&mut self) {
579 self.0.clear();
580 }
581}
582
583#[cfg(test)]
584mod tests {
585 use super::*;
586
587 #[test]
590 fn size_of_handle() {
591 assert_eq!(std::mem::size_of::<TileDefinitionHandle>(), 8);
592 }
593
594 #[test]
595 fn zero_handle() {
596 assert_eq!(
597 TileDefinitionHandle::zeroed(),
598 TileDefinitionHandle::default()
599 );
600 }
601}