1use super::*;
41use crate::core::{algebra::Vector2, color::Color, log::Log, type_traits::prelude::*};
42use fxhash::FxHashMap;
43use fyrox_core::swap_hash_map_entry;
44use std::{
45 borrow::Cow,
46 collections::hash_map::Entry,
47 ops::{Deref, DerefMut},
48};
49
50struct BresenhamLineIter {
51 dx: i32,
52 dy: i32,
53 x: i32,
54 y: i32,
55 error: i32,
56 end_x: i32,
57 is_steep: bool,
58 y_step: i32,
59}
60
61impl BresenhamLineIter {
62 fn new(start: Vector2<i32>, end: Vector2<i32>) -> BresenhamLineIter {
63 let (mut x0, mut y0) = (start.x, start.y);
64 let (mut x1, mut y1) = (end.x, end.y);
65
66 let is_steep = (y1 - y0).abs() > (x1 - x0).abs();
67 if is_steep {
68 std::mem::swap(&mut x0, &mut y0);
69 std::mem::swap(&mut x1, &mut y1);
70 }
71
72 if x0 > x1 {
73 std::mem::swap(&mut x0, &mut x1);
74 std::mem::swap(&mut y0, &mut y1);
75 }
76
77 let dx = x1 - x0;
78
79 BresenhamLineIter {
80 dx,
81 dy: (y1 - y0).abs(),
82 x: x0,
83 y: y0,
84 error: dx / 2,
85 end_x: x1,
86 is_steep,
87 y_step: if y0 < y1 { 1 } else { -1 },
88 }
89 }
90}
91
92impl Iterator for BresenhamLineIter {
93 type Item = Vector2<i32>;
94
95 fn next(&mut self) -> Option<Vector2<i32>> {
96 if self.x > self.end_x {
97 None
98 } else {
99 let ret = if self.is_steep {
100 Vector2::new(self.y, self.x)
101 } else {
102 Vector2::new(self.x, self.y)
103 };
104
105 self.x += 1;
106 self.error -= self.dy;
107 if self.error < 0 {
108 self.y += self.y_step;
109 self.error += self.dx;
110 }
111
112 Some(ret)
113 }
114 }
115}
116
117#[derive(Clone, Debug, Default)]
119pub struct TileSetUpdate(FxHashMap<TileDefinitionHandle, TileDataUpdate>);
120
121impl Deref for TileSetUpdate {
122 type Target = FxHashMap<TileDefinitionHandle, TileDataUpdate>;
123 fn deref(&self) -> &Self::Target {
124 &self.0
125 }
126}
127impl DerefMut for TileSetUpdate {
128 fn deref_mut(&mut self) -> &mut Self::Target {
129 &mut self.0
130 }
131}
132
133#[derive(Debug, Clone)]
136pub enum MaterialUpdate {
137 Erase,
139 Replace(TileMaterialBounds),
141}
142
143#[derive(Clone, Debug, Default)]
145pub enum TileDataUpdate {
146 #[default]
148 Erase,
149 DoNothing,
151 MaterialTile(TileData),
153 FreeformTile(TileDefinition),
155 TransformSet(Option<TileDefinitionHandle>),
159 Color(Color),
161 Property(Uuid, Option<TileSetPropertyValue>),
163 PropertySlice(Uuid, [Option<i8>; 9]),
165 Collider(FxHashMap<Uuid, TileCollider>),
167 Material(TileMaterialBounds),
169}
170
171impl TileDataUpdate {
172 pub fn apply_to_property_value(
174 &self,
175 property_id: Uuid,
176 value: TileSetPropertyValue,
177 ) -> TileSetPropertyValue {
178 match self {
179 TileDataUpdate::Erase => value.make_default(),
180 TileDataUpdate::DoNothing => value,
181 TileDataUpdate::MaterialTile(tile_data) => tile_data
182 .properties
183 .get(&property_id)
184 .cloned()
185 .unwrap_or(value.make_default()),
186 TileDataUpdate::FreeformTile(tile_definition) => tile_definition
187 .data
188 .properties
189 .get(&property_id)
190 .cloned()
191 .unwrap_or(value.make_default()),
192 TileDataUpdate::TransformSet(_) => value,
193 TileDataUpdate::Color(_) => value,
194 TileDataUpdate::Property(uuid, new_value) => {
195 if *uuid == property_id {
196 new_value.as_ref().cloned().unwrap_or(value.make_default())
197 } else {
198 value
199 }
200 }
201 TileDataUpdate::PropertySlice(uuid, data) => match value {
202 TileSetPropertyValue::NineSlice(mut old_data) if property_id == *uuid => {
203 for (i, v) in data.iter().enumerate() {
204 old_data.0[i] = v.unwrap_or(old_data.0[i]);
205 }
206 TileSetPropertyValue::NineSlice(old_data)
207 }
208 _ if property_id == *uuid => {
209 TileSetPropertyValue::NineSlice(NineI8(data.map(|x| x.unwrap_or_default())))
210 }
211 _ => value,
212 },
213 TileDataUpdate::Collider(_) => value,
214 TileDataUpdate::Material(_) => value,
215 }
216 }
217 pub fn get_tile_collider(&self, uuid: &Uuid) -> Option<&TileCollider> {
220 match self {
221 TileDataUpdate::Erase => Some(&TileCollider::None),
222 TileDataUpdate::MaterialTile(data) => {
223 data.colliders.get(uuid).or(Some(&TileCollider::None))
224 }
225 TileDataUpdate::FreeformTile(def) => {
226 def.data.colliders.get(uuid).or(Some(&TileCollider::None))
227 }
228 TileDataUpdate::Collider(map) => map.get(uuid),
229 _ => None,
230 }
231 }
232 pub fn substitute_transform_handle(
237 &self,
238 source: TileDefinitionHandle,
239 ) -> Option<TileDefinitionHandle> {
240 if let TileDataUpdate::TransformSet(new_source) = self {
241 *new_source
242 } else {
243 Some(source)
244 }
245 }
246 pub fn modify_render<'a>(&self, source: &'a TileRenderData) -> Option<Cow<'a, TileRenderData>> {
249 match self {
250 TileDataUpdate::Erase => None,
251 TileDataUpdate::MaterialTile(tile_data) => Some(Cow::Owned(TileRenderData {
252 material_bounds: source.material_bounds.clone(),
253 color: tile_data.color,
254 })),
255 TileDataUpdate::FreeformTile(def) => Some(Cow::Owned(TileRenderData {
256 material_bounds: Some(def.material_bounds.clone()),
257 color: def.data.color,
258 })),
259 TileDataUpdate::Color(color) => Some(Cow::Owned(TileRenderData {
260 material_bounds: source.material_bounds.clone(),
261 color: *color,
262 })),
263 TileDataUpdate::Material(material_bounds) => Some(Cow::Owned(TileRenderData {
264 material_bounds: Some(material_bounds.clone()),
265 color: source.color,
266 })),
267 _ => Some(Cow::Borrowed(source)),
268 }
269 }
270 pub fn take_data(&mut self) -> TileData {
272 match std::mem::take(self) {
273 TileDataUpdate::MaterialTile(d) => d,
274 _ => panic!(),
275 }
276 }
277 pub fn take_definition(&mut self) -> TileDefinition {
279 match std::mem::take(self) {
280 TileDataUpdate::FreeformTile(d) => d,
281 _ => panic!(),
282 }
283 }
284 pub fn swap_with_data(&mut self, data: &mut TileData) {
287 match self {
288 TileDataUpdate::DoNothing => (),
289 TileDataUpdate::Erase => {
290 Log::err("Tile data swap error");
291 *self = Self::DoNothing;
292 }
293 TileDataUpdate::MaterialTile(tile_data) => std::mem::swap(tile_data, data),
294 TileDataUpdate::FreeformTile(tile_definition) => {
295 std::mem::swap(&mut tile_definition.data, data)
296 }
297 TileDataUpdate::Color(color) => std::mem::swap(color, &mut data.color),
298 TileDataUpdate::Collider(colliders) => {
299 for (uuid, value) in colliders.iter_mut() {
300 match data.colliders.entry(*uuid) {
301 Entry::Occupied(mut e) => {
302 if let TileCollider::None = value {
303 *value = e.remove();
304 } else {
305 std::mem::swap(e.get_mut(), value)
306 }
307 }
308 Entry::Vacant(e) => {
309 e.insert(value.clone());
310 *value = TileCollider::None;
311 }
312 }
313 }
314 }
315 TileDataUpdate::Property(uuid, value) => {
316 swap_hash_map_entry(data.properties.entry(*uuid), value)
317 }
318 TileDataUpdate::PropertySlice(uuid, value) => match data.properties.entry(*uuid) {
319 Entry::Occupied(mut e) => {
320 if let TileSetPropertyValue::NineSlice(v0) = e.get_mut() {
321 for (v0, v1) in v0.0.iter_mut().zip(value.iter_mut()) {
322 if let Some(v1) = v1 {
323 std::mem::swap(v0, v1);
324 }
325 }
326 }
327 }
328 Entry::Vacant(e) => {
329 let _ = e.insert(TileSetPropertyValue::NineSlice(NineI8(
330 value.map(|v| v.unwrap_or_default()),
331 )));
332 *self = TileDataUpdate::Property(*uuid, None);
333 }
334 },
335 TileDataUpdate::TransformSet(_) => {
336 Log::err("Tile data swap error");
337 *self = Self::DoNothing;
338 }
339 TileDataUpdate::Material(_) => {
340 Log::err("Tile data swap error");
341 *self = Self::DoNothing;
342 }
343 }
344 }
345}
346
347impl TileSetUpdate {
348 pub fn convert(
355 &mut self,
356 tiles: &TransTilesUpdate,
357 tile_set: &TileSetResource,
358 page: Vector2<i32>,
359 source_set: &TileSetResource,
360 ) {
361 let tile_set = tile_set.data_ref();
362 let Some(page_object) = tile_set.get_page(page) else {
363 return;
364 };
365 match &page_object.source {
366 TileSetPageSource::Atlas(_) => self.convert_material(tiles, page),
367 TileSetPageSource::Freeform(_) => {
368 drop(tile_set);
369 self.convert_freeform(tiles, &TileSetRef::new(source_set).as_loaded(), page);
370 }
371 TileSetPageSource::Transform(_) | TileSetPageSource::Animation(_) => {
372 drop(tile_set);
373 self.convert_transform(tiles, &TileSetRef::new(source_set).as_loaded(), page);
374 }
375 }
376 }
377 fn convert_material(&mut self, tiles: &TransTilesUpdate, page: Vector2<i32>) {
378 for (pos, value) in tiles.iter() {
379 let Some(handle) = TileDefinitionHandle::try_new(page, *pos) else {
380 continue;
381 };
382 if value.is_some() {
383 self.insert(handle, TileDataUpdate::MaterialTile(TileData::default()));
384 } else {
385 self.insert(handle, TileDataUpdate::Erase);
386 }
387 }
388 }
389 fn convert_freeform(
390 &mut self,
391 tiles: &TransTilesUpdate,
392 tile_set: &OptionTileSet,
393 page: Vector2<i32>,
394 ) {
395 for (pos, value) in tiles.iter() {
396 let Some(handle) = TileDefinitionHandle::try_new(page, *pos) else {
397 continue;
398 };
399 if let Some(def) = value.and_then(|(t, h)| tile_set.get_transformed_definition(t, h)) {
400 self.insert(handle, TileDataUpdate::FreeformTile(def));
401 } else {
402 self.insert(handle, TileDataUpdate::Erase);
403 }
404 }
405 }
406 fn convert_transform(
407 &mut self,
408 tiles: &TransTilesUpdate,
409 tile_set: &OptionTileSet,
410 page: Vector2<i32>,
411 ) {
412 for (pos, value) in tiles.iter() {
413 let Some(target_handle) = TileDefinitionHandle::try_new(page, *pos) else {
414 continue;
415 };
416 if let Some((trans, handle)) = value {
417 let handle = tile_set
418 .get_transformed_version(*trans, *handle)
419 .unwrap_or(*handle);
420 self.insert(target_handle, TileDataUpdate::TransformSet(Some(handle)));
421 } else {
422 self.insert(target_handle, TileDataUpdate::TransformSet(None));
423 }
424 }
425 }
426 pub fn get_color(&self, page: Vector2<i32>, position: Vector2<i32>) -> Option<Color> {
428 let handle = TileDefinitionHandle::try_new(page, position)?;
429 match self.get(&handle)? {
430 TileDataUpdate::Erase => Some(Color::default()),
431 TileDataUpdate::MaterialTile(data) => Some(data.color),
432 TileDataUpdate::FreeformTile(def) => Some(def.data.color),
433 TileDataUpdate::Color(color) => Some(*color),
434 _ => None,
435 }
436 }
437 pub fn get_material(
439 &self,
440 page: Vector2<i32>,
441 position: Vector2<i32>,
442 ) -> Option<MaterialUpdate> {
443 let handle = TileDefinitionHandle::try_new(page, position)?;
444 match self.get(&handle)? {
445 TileDataUpdate::Erase => Some(MaterialUpdate::Erase),
446 TileDataUpdate::FreeformTile(def) => {
447 Some(MaterialUpdate::Replace(def.material_bounds.clone()))
448 }
449 TileDataUpdate::Material(mat) => Some(MaterialUpdate::Replace(mat.clone())),
450 _ => None,
451 }
452 }
453 pub fn get_tile_bounds(
455 &self,
456 page: Vector2<i32>,
457 position: Vector2<i32>,
458 ) -> Option<TileBounds> {
459 let handle = TileDefinitionHandle::try_new(page, position)?;
460 match self.get(&handle)? {
461 TileDataUpdate::Erase => Some(TileBounds::default()),
462 TileDataUpdate::FreeformTile(def) => Some(def.material_bounds.bounds.clone()),
463 TileDataUpdate::Material(mat) => Some(mat.bounds.clone()),
464 _ => None,
465 }
466 }
467 pub fn get_property(
469 &self,
470 page: Vector2<i32>,
471 position: Vector2<i32>,
472 property_id: Uuid,
473 ) -> Option<Option<TileSetPropertyValue>> {
474 let handle = TileDefinitionHandle::try_new(page, position)?;
475 match self.get(&handle)? {
476 TileDataUpdate::Erase => Some(None),
477 TileDataUpdate::MaterialTile(data) => Some(data.properties.get(&property_id).cloned()),
478 TileDataUpdate::FreeformTile(def) => {
479 Some(def.data.properties.get(&property_id).cloned())
480 }
481 TileDataUpdate::Property(id, value) if *id == property_id => Some(value.clone()),
482 _ => None,
483 }
484 }
485 pub fn get_collider(
487 &self,
488 page: Vector2<i32>,
489 position: Vector2<i32>,
490 collider_id: &Uuid,
491 ) -> Option<&TileCollider> {
492 let handle = TileDefinitionHandle::try_new(page, position)?;
493 self.get(&handle)?.get_tile_collider(collider_id)
494 }
495 pub fn set_color(&mut self, page: Vector2<i32>, position: Vector2<i32>, color: Color) {
497 if let Some(handle) = TileDefinitionHandle::try_new(page, position) {
498 self.insert(handle, TileDataUpdate::Color(color));
499 }
500 }
501 pub fn set_property(
503 &mut self,
504 page: Vector2<i32>,
505 position: Vector2<i32>,
506 property_id: Uuid,
507 value: Option<TileSetPropertyValue>,
508 ) {
509 if let Some(handle) = TileDefinitionHandle::try_new(page, position) {
510 self.insert(handle, TileDataUpdate::Property(property_id, value));
511 }
512 }
513 pub fn set_property_slice(
515 &mut self,
516 page: Vector2<i32>,
517 position: Vector2<i32>,
518 subposition: Vector2<usize>,
519 property_id: Uuid,
520 value: i8,
521 ) {
522 use TileSetPropertyValue as PropValue;
523 let index = TileSetPropertyValue::nine_position_to_index(subposition);
524 if let Some(handle) = TileDefinitionHandle::try_new(page, position) {
525 match self.entry(handle) {
526 Entry::Occupied(mut e) => match e.get_mut() {
527 TileDataUpdate::PropertySlice(uuid, d0) if *uuid == property_id => {
528 d0[index] = Some(value);
529 }
530 TileDataUpdate::Property(uuid, Some(PropValue::NineSlice(d0)))
531 if *uuid == property_id =>
532 {
533 d0.0[index] = value;
534 }
535 d0 => {
536 let mut data = [0; 9];
537 data[index] = value;
538 *d0 = TileDataUpdate::Property(
539 property_id,
540 Some(PropValue::NineSlice(NineI8(data))),
541 );
542 }
543 },
544 Entry::Vacant(e) => {
545 let mut data = [None; 9];
546 data[index] = Some(value);
547 let _ = e.insert(TileDataUpdate::PropertySlice(property_id, data));
548 }
549 }
550 }
551 }
552 pub fn set_collider<I: Iterator<Item = Uuid>>(
554 &mut self,
555 page: Vector2<i32>,
556 position: Vector2<i32>,
557 property_ids: I,
558 value: &TileCollider,
559 ) {
560 let Some(handle) = TileDefinitionHandle::try_new(page, position) else {
561 return;
562 };
563 let mut colliders = FxHashMap::default();
564 colliders.extend(property_ids.map(|uuid| (uuid, value.clone())));
565 self.insert(handle, TileDataUpdate::Collider(colliders));
566 }
567 pub fn set_material(
569 &mut self,
570 page: Vector2<i32>,
571 position: Vector2<i32>,
572 value: TileMaterialBounds,
573 ) {
574 if let Some(handle) = TileDefinitionHandle::try_new(page, position) {
575 self.insert(handle, TileDataUpdate::Material(value));
576 }
577 }
578}
579
580type RotTileHandle = (OrthoTransformation, TileDefinitionHandle);
581
582#[derive(Clone, Debug, Default)]
586pub struct TransTilesUpdate(TileGridMap<Option<RotTileHandle>>);
587
588#[derive(Clone, Debug, Default, PartialEq)]
591pub struct TilesUpdate(TileGridMap<Option<TileDefinitionHandle>>);
592
593impl Deref for TilesUpdate {
594 type Target = TileGridMap<Option<TileDefinitionHandle>>;
595
596 fn deref(&self) -> &Self::Target {
597 &self.0
598 }
599}
600
601impl DerefMut for TilesUpdate {
602 fn deref_mut(&mut self) -> &mut Self::Target {
603 &mut self.0
604 }
605}
606
607impl Deref for TransTilesUpdate {
608 type Target = TileGridMap<Option<RotTileHandle>>;
609
610 fn deref(&self) -> &Self::Target {
611 &self.0
612 }
613}
614
615impl DerefMut for TransTilesUpdate {
616 fn deref_mut(&mut self) -> &mut Self::Target {
617 &mut self.0
618 }
619}
620
621impl TransTilesUpdate {
622 pub fn build_tiles_update(&self, tile_set: &OptionTileSet) -> TilesUpdate {
625 let mut result = TilesUpdate::default();
626 for (pos, value) in self.iter() {
627 if let Some((trans, handle)) = value {
628 let handle = tile_set
629 .get_transformed_version(*trans, *handle)
630 .unwrap_or(*handle);
631 result.insert(*pos, Some(handle));
632 } else {
633 result.insert(*pos, None);
634 }
635 }
636 result
637 }
638 pub fn flood_fill<T: BoundedTileSource, S: TileSource>(
644 &mut self,
645 tiles: &T,
646 start_point: Vector2<i32>,
647 brush: &S,
648 ) {
649 let mut bounds = tiles.bounding_rect();
650 bounds.push(start_point);
651
652 let allowed_definition = tiles.get_at(start_point);
653 let mut stack = vec![start_point];
654 while let Some(position) = stack.pop() {
655 let definition = tiles.get_at(position);
656 if definition == allowed_definition && !self.contains_key(&position) {
657 let value = brush.get_at(position).map(|h| (brush.transformation(), h));
658 self.insert(position, value);
659
660 for neighbour_position in [
662 Vector2::new(position.x - 1, position.y),
663 Vector2::new(position.x + 1, position.y),
664 Vector2::new(position.x, position.y - 1),
665 Vector2::new(position.x, position.y + 1),
666 ] {
667 if bounds.contains(neighbour_position) {
668 stack.push(neighbour_position);
669 }
670 }
671 }
672 }
673 }
674 #[inline]
676 pub fn draw_tiles(&mut self, origin: Vector2<i32>, brush: &Stamp) {
677 let trans = brush.transformation();
678 for (local_position, handle) in brush.iter() {
679 self.insert(origin + local_position, Some((trans, *handle)));
680 }
681 }
682 #[inline]
684 pub fn erase_stamp(&mut self, origin: Vector2<i32>, brush: &Stamp) {
685 for local_position in brush.keys() {
686 self.insert(origin + local_position, None);
687 }
688 }
689 pub fn erase(&mut self, position: Vector2<i32>) {
691 self.insert(position, None);
692 }
693 pub fn rect_fill(&mut self, start: Vector2<i32>, end: Vector2<i32>, stamp: &Stamp) {
695 let region = TileRegion::from_points(start, end);
696 let stamp_source = stamp.repeat(start, end);
697 self.rect_fill_inner(region, &stamp_source);
698 }
699 pub fn rect_fill_random(&mut self, start: Vector2<i32>, end: Vector2<i32>, stamp: &Stamp) {
701 let region = TileRegion::from_points(start, end);
702 self.rect_fill_inner(region, &RandomTileSource(stamp));
703 }
704 fn rect_fill_inner<S: TileSource>(&mut self, region: TileRegion, brush: &S) {
706 let trans = brush.transformation();
707 for (target, source) in region.iter() {
708 if let Some(definition_handle) = brush.get_at(source) {
709 self.insert(target, Some((trans, definition_handle)));
710 }
711 }
712 }
713 pub fn draw_line<S: TileSource>(&mut self, from: Vector2<i32>, to: Vector2<i32>, brush: &S) {
715 let trans = brush.transformation();
716 for position in BresenhamLineIter::new(from, to) {
717 if let Some(random_tile) = brush.get_at(position - from) {
718 self.insert(position, Some((trans, random_tile)));
719 }
720 }
721 }
722
723 pub fn nine_slice(&mut self, start: Vector2<i32>, end: Vector2<i32>, brush: &Stamp) {
727 self.nine_slice_inner(
728 start,
729 end,
730 brush,
731 |update, target_region, source, source_region| {
732 update.rect_fill_inner(
733 target_region,
734 &RepeatTileSource {
735 source,
736 region: source_region,
737 },
738 )
739 },
740 );
741 }
742 pub fn nine_slice_random(&mut self, start: Vector2<i32>, end: Vector2<i32>, brush: &Stamp) {
746 self.nine_slice_inner(
747 start,
748 end,
749 brush,
750 |update, target_region, source, source_region| {
751 update.rect_fill_inner(
752 target_region,
753 &PartialRandomTileSource(source, source_region.bounds),
754 )
755 },
756 );
757 }
758
759 #[inline]
763 fn nine_slice_inner<F>(
764 &mut self,
765 start: Vector2<i32>,
766 end: Vector2<i32>,
767 stamp: &Stamp,
768 fill: F,
769 ) where
770 F: Fn(&mut TransTilesUpdate, TileRegion, &Stamp, TileRegion),
771 {
772 let Some(stamp_rect) = *stamp.bounding_rect() else {
773 return;
774 };
775 let rect = TileRect::from_points(start, end);
776 let region = TileRegion {
777 origin: start,
778 bounds: rect.into(),
779 };
780 let inner_region = region.clone().deflate(1, 1);
781
782 let stamp_region = TileRegion::from_bounds_and_direction(stamp_rect.into(), start - end);
783 let inner_stamp_region = stamp_region.clone().deflate(1, 1);
784
785 let trans = stamp.transformation();
787 for (corner_position, actual_corner_position) in [
788 (stamp_rect.left_top_corner(), rect.left_top_corner()),
789 (stamp_rect.right_top_corner(), rect.right_top_corner()),
790 (stamp_rect.right_bottom_corner(), rect.right_bottom_corner()),
791 (stamp_rect.left_bottom_corner(), rect.left_bottom_corner()),
792 ] {
793 if let Some(tile) = stamp.get(corner_position) {
794 self.insert(actual_corner_position, Some((trans, *tile)));
795 }
796 }
797
798 let top = region.clone().with_bounds(
799 TileRect::from_points(
800 rect.left_top_corner() + Vector2::new(1, 0),
801 rect.right_top_corner() + Vector2::new(-1, 0),
802 )
803 .into(),
804 );
805 let bottom = region.clone().with_bounds(
806 TileRect::from_points(
807 rect.left_bottom_corner() + Vector2::new(1, 0),
808 rect.right_bottom_corner() + Vector2::new(-1, 0),
809 )
810 .into(),
811 );
812 let left = region.clone().with_bounds(
813 TileRect::from_points(
814 rect.left_bottom_corner() + Vector2::new(0, 1),
815 rect.left_top_corner() + Vector2::new(0, -1),
816 )
817 .into(),
818 );
819 let right = region.clone().with_bounds(
820 TileRect::from_points(
821 rect.right_bottom_corner() + Vector2::new(0, 1),
822 rect.right_top_corner() + Vector2::new(0, -1),
823 )
824 .into(),
825 );
826 let stamp_top = stamp_region.clone().with_bounds(
827 TileRect::from_points(
828 stamp_rect.left_top_corner() + Vector2::new(1, 0),
829 stamp_rect.right_top_corner() + Vector2::new(-1, 0),
830 )
831 .into(),
832 );
833 let stamp_bottom = stamp_region.clone().with_bounds(
834 TileRect::from_points(
835 stamp_rect.left_bottom_corner() + Vector2::new(1, 0),
836 stamp_rect.right_bottom_corner() + Vector2::new(-1, 0),
837 )
838 .into(),
839 );
840 let stamp_left = stamp_region.clone().with_bounds(
841 TileRect::from_points(
842 stamp_rect.left_bottom_corner() + Vector2::new(0, 1),
843 stamp_rect.left_top_corner() + Vector2::new(0, -1),
844 )
845 .into(),
846 );
847 let stamp_right = stamp_region.clone().with_bounds(
848 TileRect::from_points(
849 stamp_rect.right_bottom_corner() + Vector2::new(0, 1),
850 stamp_rect.right_top_corner() + Vector2::new(0, -1),
851 )
852 .into(),
853 );
854
855 if rect.size.x > 2 && stamp_rect.size.x > 2 {
856 fill(self, top, stamp, stamp_top);
857 fill(self, bottom, stamp, stamp_bottom);
858 }
859 if rect.size.y > 2 && stamp_rect.size.y > 2 {
860 fill(self, left, stamp, stamp_left);
861 fill(self, right, stamp, stamp_right);
862 }
863 fill(self, inner_region, stamp, inner_stamp_region);
864 }
865}